diff --git a/.eslintrc.js b/.eslintrc.js index daec0f421c9..710873e1b56 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,4 +1,4 @@ -module.exports = { // E: 'module' is not defined. +module.exports = { extends: ['turbo', '@solana/eslint-config-solana', '@solana/eslint-config-solana/jest'], root: true, }; diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000000..7bfa0e8650f --- /dev/null +++ b/.prettierignore @@ -0,0 +1,11 @@ +docs +lib +test-ledger +node_modules +dist +generated +.mypy_cache +*.yml +*.yaml +*.md +*.json diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000000..173a61f766c --- /dev/null +++ b/.prettierrc @@ -0,0 +1,9 @@ +{ + "semi": true, + "singleQuote": true, + "trailingComma": "es5", + "useTabs": false, + "tabWidth": 4, + "arrowParens": "always", + "printWidth": 80 +} diff --git a/Cargo.lock b/Cargo.lock index 48af5913c93..16fda2face9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -430,13 +430,14 @@ dependencies = [ [[package]] name = "assert_cmd" -version = "2.0.15" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc65048dd435533bb1baf2ed9956b9a278fbfdcf90301b39ee117f06c0199d37" +checksum = "dc1835b7f27878de8525dc71410b5a31cdcc5f230aed5ba5df968e09c201b23d" dependencies = [ "anstyle", "bstr 1.6.0", "doc-comment", + "libc", "predicates 3.0.3", "predicates-core", "predicates-tree", @@ -922,18 +923,18 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "bytemuck" -version = "1.16.1" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e" +checksum = "6fd4c6dcc3b0aea2f5c0b4b82c2b15fe39ddbc76041a310848f4706edf76bb31" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.7.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ee891b04274a59bd38b412188e24b849617b2e45a0fd8d057deb63e7403761b" +checksum = "0cc8b54b395f2fcfbb3d90c47b01c7f444d94d05bdeb775811dec868ac3bbc26" dependencies = [ "proc-macro2", "quote", @@ -1064,7 +1065,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.52.0", + "windows-targets 0.52.6", ] [[package]] @@ -1721,7 +1722,7 @@ dependencies = [ "ed25519", "rand 0.7.3", "serde", - "sha2 0.9.8", + "sha2 0.9.9", "zeroize", ] @@ -2908,7 +2909,7 @@ dependencies = [ "libsecp256k1-gen-genmult", "rand 0.7.3", "serde", - "sha2 0.9.8", + "sha2 0.9.9", "typenum", ] @@ -3385,18 +3386,18 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", @@ -4604,9 +4605,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.204" +version = "1.0.208" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +checksum = "cff085d2cb684faa248efb494c39b68e522822ac0de72ccf08109abde717cfb2" dependencies = [ "serde_derive", ] @@ -4622,9 +4623,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.204" +version = "1.0.208" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +checksum = "24008e81ff7613ed8e5ba0cfaf24e2c2f1e5b8a0495711e44fcd4882fca62bcf" dependencies = [ "proc-macro2", "quote", @@ -4633,9 +4634,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.121" +version = "1.0.125" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ab380d7d9f22ef3f21ad3e6c1ebe8e4fc7a2000ccba2e4d71fc96f15b2cb609" +checksum = "83c8e735a073ccf5be70aa8066aa984eaf2fa000db6c8d0100ae605b366d31ed" dependencies = [ "itoa", "memchr", @@ -4794,9 +4795,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ "block-buffer 0.9.0", "cfg-if 1.0.0", @@ -7252,30 +7253,30 @@ dependencies = [ [[package]] name = "spl-pod" version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6166a591d93af33afd75bbd8573c5fd95fb1213f1bf254f0508c89fdb5ee156" dependencies = [ - "base64 0.22.1", "borsh 1.5.1", "bytemuck", "bytemuck_derive", - "serde", - "serde_json", "solana-program", "solana-zk-token-sdk", - "spl-program-error 0.5.0", + "spl-program-error 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "spl-pod" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6166a591d93af33afd75bbd8573c5fd95fb1213f1bf254f0508c89fdb5ee156" +version = "0.3.1" dependencies = [ + "base64 0.22.1", "borsh 1.5.1", "bytemuck", "bytemuck_derive", + "serde", + "serde_json", "solana-program", - "solana-zk-token-sdk", - "spl-program-error 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "solana-zk-sdk", + "spl-program-error 0.5.0", ] [[package]] @@ -7337,7 +7338,7 @@ dependencies = [ "solana-program", "solana-program-test", "solana-sdk", - "spl-pod 0.3.0", + "spl-pod 0.3.1", "thiserror", ] @@ -7428,7 +7429,7 @@ dependencies = [ "solana-security-txt", "solana-vote-program", "spl-math", - "spl-pod 0.3.0", + "spl-pod 0.3.1", "spl-token 6.0.0", "spl-token-2022 4.0.1", "test-case", @@ -7473,7 +7474,7 @@ dependencies = [ "solana-program-test", "solana-sdk", "spl-discriminator 0.3.0", - "spl-pod 0.3.0", + "spl-pod 0.3.1", "spl-program-error 0.5.0", "spl-type-length-value 0.5.0", ] @@ -7487,7 +7488,7 @@ dependencies = [ "bytemuck", "solana-program", "spl-discriminator 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "spl-pod 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-pod 0.3.0", "spl-program-error 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "spl-type-length-value 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -7540,7 +7541,7 @@ dependencies = [ "solana-security-txt", "solana-zk-token-sdk", "spl-memo 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "spl-pod 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-pod 0.3.0", "spl-token 6.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "spl-token-group-interface 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "spl-token-metadata-interface 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -7569,11 +7570,14 @@ dependencies = [ "solana-program-test", "solana-sdk", "solana-security-txt", - "solana-zk-token-sdk", + "solana-zk-sdk", "spl-memo 5.0.0", - "spl-pod 0.3.0", + "spl-pod 0.3.1", "spl-tlv-account-resolution 0.7.0", "spl-token 6.0.0", + "spl-token-confidential-transfer-ciphertext-arithmetic", + "spl-token-confidential-transfer-proof-extraction", + "spl-token-confidential-transfer-proof-generation", "spl-token-group-interface 0.3.0", "spl-token-metadata-interface 0.4.0", "spl-transfer-hook-interface 0.7.0", @@ -7587,6 +7591,7 @@ version = "0.0.1" dependencies = [ "async-trait", "borsh 1.5.1", + "bytemuck", "futures-util", "solana-program", "solana-program-test", @@ -7594,10 +7599,12 @@ dependencies = [ "spl-associated-token-account 4.0.0", "spl-instruction-padding", "spl-memo 5.0.0", - "spl-pod 0.3.0", + "spl-pod 0.3.1", + "spl-record", "spl-tlv-account-resolution 0.7.0", "spl-token-2022 4.0.1", "spl-token-client", + "spl-token-confidential-transfer-proof-generation", "spl-token-group-interface 0.3.0", "spl-token-metadata-interface 0.4.0", "spl-transfer-hook-example", @@ -7608,7 +7615,7 @@ dependencies = [ [[package]] name = "spl-token-cli" -version = "4.0.0" +version = "4.0.1" dependencies = [ "assert_cmd", "base64 0.22.1", @@ -7635,6 +7642,7 @@ dependencies = [ "spl-token 6.0.0", "spl-token-2022 4.0.1", "spl-token-client", + "spl-token-confidential-transfer-proof-generation", "spl-token-group-interface 0.3.0", "spl-token-metadata-interface 0.4.0", "strum 0.26.3", @@ -7649,7 +7657,8 @@ name = "spl-token-client" version = "0.11.0" dependencies = [ "async-trait", - "curve25519-dalek", + "bincode", + "bytemuck", "futures 0.3.30", "futures-util", "solana-banks-interface", @@ -7660,8 +7669,10 @@ dependencies = [ "solana-sdk", "spl-associated-token-account 4.0.0", "spl-memo 5.0.0", + "spl-record", "spl-token 6.0.0", "spl-token-2022 4.0.1", + "spl-token-confidential-transfer-proof-generation", "spl-token-group-interface 0.3.0", "spl-token-metadata-interface 0.4.0", "spl-transfer-hook-interface 0.7.0", @@ -7676,7 +7687,7 @@ dependencies = [ "solana-program-test", "solana-sdk", "spl-discriminator 0.3.0", - "spl-pod 0.3.0", + "spl-pod 0.3.1", "spl-program-error 0.5.0", "spl-token-2022 4.0.1", "spl-token-client", @@ -7736,7 +7747,7 @@ dependencies = [ "solana-program-test", "solana-sdk", "spl-discriminator 0.3.0", - "spl-pod 0.3.0", + "spl-pod 0.3.1", "spl-token-2022 4.0.1", "spl-token-client", "spl-token-group-interface 0.3.0", @@ -7751,7 +7762,7 @@ dependencies = [ "bytemuck", "solana-program", "spl-discriminator 0.3.0", - "spl-pod 0.3.0", + "spl-pod 0.3.1", "spl-program-error 0.5.0", "spl-type-length-value 0.5.0", ] @@ -7765,7 +7776,7 @@ dependencies = [ "bytemuck", "solana-program", "spl-discriminator 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "spl-pod 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-pod 0.3.0", "spl-program-error 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -7809,7 +7820,7 @@ dependencies = [ "solana-program", "solana-program-test", "solana-sdk", - "spl-pod 0.3.0", + "spl-pod 0.3.1", "spl-token-2022 4.0.1", "spl-token-client", "spl-token-metadata-interface 0.4.0", @@ -7826,7 +7837,7 @@ dependencies = [ "serde_json", "solana-program", "spl-discriminator 0.3.0", - "spl-pod 0.3.0", + "spl-pod 0.3.1", "spl-program-error 0.5.0", "spl-type-length-value 0.5.0", ] @@ -7840,7 +7851,7 @@ dependencies = [ "borsh 1.5.1", "solana-program", "spl-discriminator 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "spl-pod 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-pod 0.3.0", "spl-program-error 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "spl-type-length-value 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -7977,7 +7988,7 @@ dependencies = [ "bytemuck", "solana-program", "spl-discriminator 0.3.0", - "spl-pod 0.3.0", + "spl-pod 0.3.1", "spl-program-error 0.5.0", "spl-tlv-account-resolution 0.7.0", "spl-type-length-value 0.5.0", @@ -7994,7 +8005,7 @@ dependencies = [ "bytemuck", "solana-program", "spl-discriminator 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "spl-pod 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-pod 0.3.0", "spl-program-error 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "spl-tlv-account-resolution 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "spl-type-length-value 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -8007,7 +8018,7 @@ dependencies = [ "bytemuck", "solana-program", "spl-discriminator 0.3.0", - "spl-pod 0.3.0", + "spl-pod 0.3.1", "spl-program-error 0.5.0", "spl-type-length-value-derive", ] @@ -8021,7 +8032,7 @@ dependencies = [ "bytemuck", "solana-program", "spl-discriminator 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "spl-pod 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-pod 0.3.0", "spl-program-error 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -8292,14 +8303,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.10.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" dependencies = [ "cfg-if 1.0.0", "fastrand", + "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -8458,7 +8470,7 @@ dependencies = [ "pbkdf2 0.4.0", "rand 0.7.3", "rustc-hash", - "sha2 0.9.8", + "sha2 0.9.9", "thiserror", "unicode-normalization", "wasm-bindgen", @@ -8482,9 +8494,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.39.2" +version = "1.39.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" +checksum = "9babc99b9923bfa4804bd74722ff02c0381021eafa4db9949217e3be8e84fff5" dependencies = [ "backtrace", "bytes", @@ -9199,7 +9211,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -9219,17 +9240,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -9240,9 +9262,9 @@ checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -9252,9 +9274,9 @@ checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -9264,9 +9286,15 @@ checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -9276,9 +9304,9 @@ checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -9288,9 +9316,9 @@ checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -9300,9 +9328,9 @@ checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -9312,9 +9340,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" diff --git a/account-compression/programs/account-compression/Cargo.toml b/account-compression/programs/account-compression/Cargo.toml index 095ad9d5e7b..a57df385abe 100644 --- a/account-compression/programs/account-compression/Cargo.toml +++ b/account-compression/programs/account-compression/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spl-account-compression" -version = "0.3.0" +version = "0.3.1" description = "Solana Program Library Account Compression Program" authors = ["Solana Labs Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" @@ -21,9 +21,7 @@ default = [] anchor-lang = "0.29.0" bytemuck = "1.13" solana-program = ">=1.18.11,<=2" -spl-concurrent-merkle-tree = { version = "0.3.0", path = "../../../libraries/concurrent-merkle-tree", features = [ - "sol-log", -] } +spl-concurrent-merkle-tree = { version = "0.3.0", path = "../../../libraries/concurrent-merkle-tree" } spl-noop = { version = "0.2.0", path = "../noop", features = ["no-entrypoint"] } [profile.release] diff --git a/account-compression/programs/account-compression/README.md b/account-compression/programs/account-compression/README.md index e57de7a10c6..e720cc57b9d 100644 --- a/account-compression/programs/account-compression/README.md +++ b/account-compression/programs/account-compression/README.md @@ -4,10 +4,8 @@

-# SPL Account Compression Rust SDK (Beta) +# SPL Account Compression Rust SDK More information about account compression can be found in [the solana-program-library repo](https://github.com/solana-labs/solana-program-library/tree/master/account-compression). -The [Solana Program Examples repo](https://github.com/solana-developers/program-examples) will eventually include examples of how to use this program. - -`spl-account-compression` and this crate's implementation are targeted towards supporting [Metaplex Compressed NFTs](https://github.com/metaplex-foundation/metaplex-program-library/tree/master/bubblegum) and may be subject to change. \ No newline at end of file +`spl-account-compression` and this crate's implementation are targeted towards supporting [Metaplex Compressed NFTs](https://github.com/metaplex-foundation/mpl-bubblegum) and may be subject to change. \ No newline at end of file diff --git a/account-compression/sdk/README.md b/account-compression/sdk/README.md index b4f81da6d62..d4c76dbfad0 100644 --- a/account-compression/sdk/README.md +++ b/account-compression/sdk/README.md @@ -167,7 +167,7 @@ Here are some examples using account compression in the wild: * Solana Program Library [tests](https://github.com/solana-labs/solana-program-library/tree/master/account-compression/sdk/tests) -* Metaplex Program Library Compressed NFT [tests](https://github.com/metaplex-foundation/metaplex-program-library/tree/master/bubblegum/js/tests) +* Metaplex Program Library Compressed NFT [tests](https://github.com/metaplex-foundation/mpl-bubblegum/tree/main/clients/js/test) ## Build from Source diff --git a/account-compression/sdk/package.json b/account-compression/sdk/package.json index 58d0334cba7..bd7af81a7d8 100644 --- a/account-compression/sdk/package.json +++ b/account-compression/sdk/package.json @@ -35,11 +35,8 @@ "scripts": { "build": "rm -rf dist/ && tsc -p tsconfig.json", "build:program": "cargo build-sbf --manifest-path=../programs/account-compression/Cargo.toml && cargo build-sbf --manifest-path=../programs/noop/Cargo.toml", - "fmt": "prettier --write '{*,**/*}.{ts,tsx,js,jsx,json}'", - "pretty": "prettier --check '{,{src,test}/**/}*.{j,t}s'", - "pretty:fix": "prettier --write '{,{src,test}/**/}*.{j,t}s'", - "lint": "set -ex; npm run pretty; eslint . --ext .js,.ts", - "lint:fix": "npm run pretty:fix && eslint . --fix --ext .js,.ts", + "lint": "set -ex; eslint . --ext .js,.ts", + "lint:fix": "eslint . --fix --ext .js,.ts", "docs": "rm -rf docs/ && typedoc --out docs", "deploy:docs": "npm run docs && gh-pages --dest account-compression/sdk --dist docs --dotfiles", "start-validator": "solana-test-validator --reset --quiet --bpf-program cmtDvXumGCrqC1Age74AVPhSRVXJMd8PJS91L8KbNCK ../target/deploy/spl_account_compression.so --bpf-program noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV ../target/deploy/spl_noop.so --account 27QMkDMpBoAhmWj6xxQNYdqXZL5nnC8tkZcEtkNxCqeX pre-batch-init-tree-account.json", @@ -61,39 +58,34 @@ "typescript-collections": "^1.3.3" }, "peerDependencies": { - "@solana/web3.js": "^1.95.1" + "@solana/web3.js": "^1.95.2" }, "devDependencies": { "@metaplex-foundation/rustbin": "^0.3.5", "@metaplex-foundation/solita": "0.20.1", - "@coral-xyz/anchor": "^0.27.0", + "@coral-xyz/anchor": "^0.29.0", "@solana/eslint-config-solana": "^3.0.3", - "@solana/prettier-config-solana": "^0.0.5", "@types/bn.js": "^5.1.0", "@types/jest": "^29.5.12", - "@types/node": "^22.0.0", + "@types/node": "^22.4.2", "@types/node-fetch": "^2.6.11", - "@typescript-eslint/eslint-plugin": "^7.17.0", - "@typescript-eslint/parser": "^7.17.0", + "@typescript-eslint/eslint-plugin": "^8.2.0", + "@typescript-eslint/parser": "^8.2.0", "eslint": "^8.57.0", - "eslint-config-prettier": "^9.1.0", - "eslint-config-turbo": "^2.0.9", + "eslint-config-turbo": "^2.0.14", "eslint-plugin-import": "^2.29.1", - "eslint-plugin-jest": "^28.6.0", + "eslint-plugin-jest": "^28.8.0", "eslint-plugin-mocha": "^10.5.0", - "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-simple-import-sort": "^12.1.1", "eslint-plugin-sort-keys-fix": "^1.1.2", "gh-pages": "^6.1.1", "jest": "^29.0.1", "jest-config": "^29.0.1", - "prettier": "^3.3.3", "start-server-and-test": "^2.0.5", - "ts-jest": "^29.2.3", + "ts-jest": "^29.2.4", "ts-jest-resolver": "^2.0.1", "ts-node": "^10.9.2", - "typedoc": "^0.26.5", + "typedoc": "^0.26.6", "typescript": "5.5.4" - }, - "prettier": "@solana/prettier-config-solana" + } } diff --git a/account-compression/sdk/src/.prettierignore b/account-compression/sdk/src/.prettierignore deleted file mode 100644 index 3380d72b2f8..00000000000 --- a/account-compression/sdk/src/.prettierignore +++ /dev/null @@ -1,2 +0,0 @@ -test/dist -docs/ \ No newline at end of file diff --git a/account-compression/sdk/tests/accountCompression.test.ts b/account-compression/sdk/tests/accountCompression.test.ts index 6ee7fc22b90..02e77cfdab6 100644 --- a/account-compression/sdk/tests/accountCompression.test.ts +++ b/account-compression/sdk/tests/accountCompression.test.ts @@ -618,7 +618,7 @@ describe('Account Compression', () => { assert( Buffer.from(onChainRoot).equals(offChainTree.root), - 'Updated on chain root matches root of updated off chain tree' + 'Updated on chain root matches root of updated off chain tree', ); }); it('Verify proof works for that leaf', async () => { @@ -637,7 +637,7 @@ describe('Account Compression', () => { assert( Buffer.from(onChainRoot).equals(offChainTree.root), - 'Updated on chain root matches root of updated off chain tree' + 'Updated on chain root matches root of updated off chain tree', ); }); it('Verify leaf fails when proof fails', async () => { @@ -668,7 +668,7 @@ describe('Account Compression', () => { assert( Buffer.from(onChainRoot).equals(offChainTree.root), - 'Updated on chain root matches root of updated off chain tree' + 'Updated on chain root matches root of updated off chain tree', ); }); it('Replace that leaf', async () => { @@ -687,7 +687,7 @@ describe('Account Compression', () => { assert( Buffer.from(onChainRoot).equals(offChainTree.root), - 'Updated on chain root matches root of updated off chain tree' + 'Updated on chain root matches root of updated off chain tree', ); }); @@ -706,7 +706,7 @@ describe('Account Compression', () => { assert( Buffer.from(onChainRoot).equals(offChainTree.root), - 'Updated on chain root matches root of updated off chain tree' + 'Updated on chain root matches root of updated off chain tree', ); }); @@ -743,7 +743,7 @@ describe('Account Compression', () => { beforeEach(async () => { await provider.connection.confirmTransaction( - await (connection as Connection).requestAirdrop(authority, 1e10) + await (connection as Connection).requestAirdrop(authority, 1e10), ); [cmtKeypair, offChainTree] = await createTreeOnChain(provider, authorityKeypair, 1, DEPTH_SIZE_PAIR); cmt = cmtKeypair.publicKey; @@ -767,7 +767,7 @@ describe('Account Compression', () => { assert( splCMT.getAuthority().equals(randomSigner), - `Upon transferring authority, authority should be ${randomSigner.toString()}, but was instead updated to ${splCMT.getAuthority()}` + `Upon transferring authority, authority should be ${randomSigner.toString()}, but was instead updated to ${splCMT.getAuthority()}`, ); // Attempting to replace with new authority now works @@ -816,7 +816,7 @@ describe('Account Compression', () => { assert( Buffer.from(onChainRoot).equals(offChainTree.root), - 'Updated on chain root does not match root of updated off chain tree' + 'Updated on chain root does not match root of updated off chain tree', ); }); it('Empty all of the leaves and close the tree', async () => { @@ -854,7 +854,7 @@ describe('Account Compression', () => { const finalLamports = payerInfo!.lamports; assert( finalLamports === payerLamports + treeLamports - 5000, - 'Expected payer to have received the lamports from the closed tree account' + 'Expected payer to have received the lamports from the closed tree account', ); treeInfo = await provider.connection.getAccountInfo(cmt, 'confirmed'); @@ -869,7 +869,7 @@ describe('Account Compression', () => { try { await execute(provider, [ix], [payerKeypair]); assert(false, 'Closing a tree account before it is empty should ALWAYS error'); - } catch (e) {} + } catch {} }); }); @@ -914,13 +914,13 @@ describe('Account Compression', () => { try { await execute(provider, [replaceIx], [payerKeypair]); assert(false, 'Attacker was able to successfully write fake existence of a leaf'); - } catch (e) {} + } catch {} const splCMT = await ConcurrentMerkleTreeAccount.fromAccountAddress(connection, cmt); assert( splCMT.getCurrentBufferIndex() === 0, - "CMT updated its active index after attacker's transaction, when it shouldn't have done anything" + "CMT updated its active index after attacker's transaction, when it shouldn't have done anything", ); }); }); @@ -932,7 +932,7 @@ describe('Account Compression', () => { payerKeypair, 2 ** DEPTH, { maxBufferSize: 8, maxDepth: DEPTH }, - DEPTH // Store full tree on chain + DEPTH, // Store full tree on chain ); cmt = cmtKeypair.publicKey; @@ -962,7 +962,7 @@ describe('Account Compression', () => { payerKeypair, 0, { maxBufferSize: 8, maxDepth: DEPTH }, - DEPTH // Store full tree on chain + DEPTH, // Store full tree on chain ); cmt = cmtKeypair.publicKey; @@ -1090,7 +1090,7 @@ describe('Account Compression', () => { try { await execute(provider, [replaceIx], [payerKeypair]); throw Error('This replace instruction should have failed because the leaf index is OOB'); - } catch (_e) {} + } catch {} }); }); }); diff --git a/account-compression/sdk/tests/accounts/concurrentMerkleTreeAccount.test.ts b/account-compression/sdk/tests/accounts/concurrentMerkleTreeAccount.test.ts index 3449590506f..c3dcbcb85c9 100644 --- a/account-compression/sdk/tests/accounts/concurrentMerkleTreeAccount.test.ts +++ b/account-compression/sdk/tests/accounts/concurrentMerkleTreeAccount.test.ts @@ -19,18 +19,18 @@ export function assertCMTProperties( ) { assert( onChainCMT.getMaxDepth() === expectedMaxDepth, - `Max depth does not match ${onChainCMT.getMaxDepth()}, expected ${expectedMaxDepth}` + `Max depth does not match ${onChainCMT.getMaxDepth()}, expected ${expectedMaxDepth}`, ); assert( onChainCMT.getMaxBufferSize() === expectedMaxBufferSize, - `Max buffer size does not match ${onChainCMT.getMaxBufferSize()}, expected ${expectedMaxBufferSize}` + `Max buffer size does not match ${onChainCMT.getMaxBufferSize()}, expected ${expectedMaxBufferSize}`, ); assert(onChainCMT.getAuthority().equals(expectedAuthority), 'Failed to write auth pubkey'); assert(onChainCMT.getCurrentRoot().equals(expectedRoot), 'On chain root does not match root passed in instruction'); if (expectedCanopyDepth) { assert( onChainCMT.getCanopyDepth() === expectedCanopyDepth, - 'On chain canopy depth does not match expected canopy depth' + 'On chain canopy depth does not match expected canopy depth', ); } assert( @@ -62,7 +62,7 @@ describe('ConcurrentMerkleTreeAccount tests', () => { await provider.connection.confirmTransaction( await provider.connection.requestAirdrop(payer, 1e10), - 'confirmed' + 'confirmed', ); }); @@ -81,7 +81,7 @@ describe('ConcurrentMerkleTreeAccount tests', () => { const cmt = await ConcurrentMerkleTreeAccount.fromAccountAddress( connection, cmtKeypair.publicKey, - 'confirmed' + 'confirmed', ); await assertCMTProperties(cmt, MAX_DEPTH, MAX_SIZE, payer, offChainTree.root); @@ -102,7 +102,7 @@ describe('ConcurrentMerkleTreeAccount tests', () => { const cmt = await ConcurrentMerkleTreeAccount.fromAccountAddress( connection, cmtKeypair.publicKey, - 'confirmed' + 'confirmed', ); // Verify it was initialized correctly @@ -111,7 +111,7 @@ describe('ConcurrentMerkleTreeAccount tests', () => { depthSizePair.maxDepth, depthSizePair.maxBufferSize, payer, - emptyNode(depthSizePair.maxDepth) + emptyNode(depthSizePair.maxDepth), ); } }); @@ -134,12 +134,12 @@ describe('ConcurrentMerkleTreeAccount tests', () => { provider, payerKeypair, { maxBufferSize, maxDepth }, - canopyDepth + canopyDepth, ); const cmt = await ConcurrentMerkleTreeAccount.fromAccountAddress( connection, cmtKeypair.publicKey, - 'confirmed' + 'confirmed', ); // Verify it was initialized correctly diff --git a/account-compression/sdk/tests/events/applicationData.test.ts b/account-compression/sdk/tests/events/applicationData.test.ts index 14a1f5a60ce..8f82490348d 100644 --- a/account-compression/sdk/tests/events/applicationData.test.ts +++ b/account-compression/sdk/tests/events/applicationData.test.ts @@ -1,27 +1,24 @@ -import { strict as assert } from "node:assert"; +import { strict as assert } from 'node:assert'; -import { BN } from "bn.js"; +import { BN } from 'bn.js'; -import { deserializeApplicationDataEvent } from "../../src"; +import { deserializeApplicationDataEvent } from '../../src'; -describe("Serde tests", () => { - describe("ApplicationDataEvent tests", () => { - it("Can serialize and deserialize ApplicationDataEvent", () => { - const data = Buffer.from("Hello world"); - const applicationDataEvent = Buffer.concat([ - Buffer.from([0x1]), // ApplicationData Event tag - Buffer.from([0x0]), // version 0 tag - Buffer.from(new BN.BN(data.length).toArray("le", 4)), // Size of application data (for Vec) - data, // serialized application data (for Vec) - ]); +describe('Serde tests', () => { + describe('ApplicationDataEvent tests', () => { + it('Can serialize and deserialize ApplicationDataEvent', () => { + const data = Buffer.from('Hello world'); + const applicationDataEvent = Buffer.concat([ + Buffer.from([0x1]), // ApplicationData Event tag + Buffer.from([0x0]), // version 0 tag + Buffer.from(new BN.BN(data.length).toArray('le', 4)), // Size of application data (for Vec) + data, // serialized application data (for Vec) + ]); - const deserialized = - deserializeApplicationDataEvent(applicationDataEvent); - const decoder = new TextDecoder(); - const deserializedData = decoder.decode( - deserialized.fields[0].applicationData - ); - assert("Hello world" === deserializedData); + const deserialized = deserializeApplicationDataEvent(applicationDataEvent); + const decoder = new TextDecoder(); + const deserializedData = decoder.decode(deserialized.fields[0].applicationData); + assert('Hello world' === deserializedData); + }); }); - }); }); diff --git a/account-compression/sdk/tests/events/changelog.test.ts b/account-compression/sdk/tests/events/changelog.test.ts index a90a99c8d8b..114e8a9d5f7 100644 --- a/account-compression/sdk/tests/events/changelog.test.ts +++ b/account-compression/sdk/tests/events/changelog.test.ts @@ -1,116 +1,100 @@ -import { strict as assert } from "node:assert"; +import { strict as assert } from 'node:assert'; -import { AnchorProvider } from "@coral-xyz/anchor"; -import NodeWallet from "@coral-xyz/anchor/dist/cjs/nodewallet"; -import { bs58 } from "@coral-xyz/anchor/dist/cjs/utils/bytes"; -import { Connection, Keypair, PublicKey } from "@solana/web3.js"; -import { BN } from "bn.js"; -import * as crypto from "crypto"; +import { AnchorProvider } from '@coral-xyz/anchor'; +import NodeWallet from '@coral-xyz/anchor/dist/cjs/nodewallet'; +import { bs58 } from '@coral-xyz/anchor/dist/cjs/utils/bytes'; +import { Connection, Keypair, PublicKey } from '@solana/web3.js'; +import { BN } from 'bn.js'; +import * as crypto from 'crypto'; -import { - createAppendIx, - deserializeChangeLogEventV1, - SPL_NOOP_PROGRAM_ID, -} from "../../src"; -import { MerkleTree } from "../../src/merkle-tree"; -import { createTreeOnChain,execute } from "../utils"; +import { createAppendIx, deserializeChangeLogEventV1, SPL_NOOP_PROGRAM_ID } from '../../src'; +import { MerkleTree } from '../../src/merkle-tree'; +import { createTreeOnChain, execute } from '../utils'; -describe("Serde tests", () => { - let offChainTree: MerkleTree; - let cmtKeypair: Keypair; - let payerKeypair: Keypair; - let payer: PublicKey; - let connection: Connection; - let provider: AnchorProvider; +describe('Serde tests', () => { + let offChainTree: MerkleTree; + let cmtKeypair: Keypair; + let payerKeypair: Keypair; + let payer: PublicKey; + let connection: Connection; + let provider: AnchorProvider; - const MAX_SIZE = 64; - const MAX_DEPTH = 14; + const MAX_SIZE = 64; + const MAX_DEPTH = 14; - beforeEach(async () => { - payerKeypair = Keypair.generate(); - payer = payerKeypair.publicKey; + beforeEach(async () => { + payerKeypair = Keypair.generate(); + payer = payerKeypair.publicKey; - connection = new Connection("http://127.0.0.1:8899", { - commitment: "confirmed", - }); - const wallet = new NodeWallet(payerKeypair); - provider = new AnchorProvider(connection, wallet, { - commitment: connection.commitment, - skipPreflight: true, - }); + connection = new Connection('http://127.0.0.1:8899', { + commitment: 'confirmed', + }); + const wallet = new NodeWallet(payerKeypair); + provider = new AnchorProvider(connection, wallet, { + commitment: connection.commitment, + skipPreflight: true, + }); - await provider.connection.confirmTransaction( - await provider.connection.requestAirdrop(payer, 1e10), - "confirmed" - ); - }); - describe("ChangeLogEvent tests", () => { - let cmt: PublicKey; - beforeEach(async () => { - [cmtKeypair, offChainTree] = await createTreeOnChain( - provider, - payerKeypair, - 0, - { maxBufferSize: MAX_SIZE, maxDepth: MAX_DEPTH } - ); - cmt = cmtKeypair.publicKey; + await provider.connection.confirmTransaction( + await provider.connection.requestAirdrop(payer, 1e10), + 'confirmed', + ); }); - it("Can deserialize a ChangeLogEvent", async () => { - const newLeaf = crypto.randomBytes(32); - const txId = await execute( - provider, - [createAppendIx(cmt, payer, newLeaf)], - [payerKeypair] - ); - offChainTree.updateLeaf(0, newLeaf); + describe('ChangeLogEvent tests', () => { + let cmt: PublicKey; + beforeEach(async () => { + [cmtKeypair, offChainTree] = await createTreeOnChain(provider, payerKeypair, 0, { + maxBufferSize: MAX_SIZE, + maxDepth: MAX_DEPTH, + }); + cmt = cmtKeypair.publicKey; + }); + it('Can deserialize a ChangeLogEvent', async () => { + const newLeaf = crypto.randomBytes(32); + const txId = await execute(provider, [createAppendIx(cmt, payer, newLeaf)], [payerKeypair]); + offChainTree.updateLeaf(0, newLeaf); - const transaction = await connection.getTransaction(txId, { - commitment: "confirmed", - maxSupportedTransactionVersion: 2, - }); + const transaction = await connection.getTransaction(txId, { + commitment: 'confirmed', + maxSupportedTransactionVersion: 2, + }); - // Get noop program instruction - const accountKeys = transaction!.transaction.message.getAccountKeys(); - const noopInstruction = - transaction!.meta!.innerInstructions![0].instructions[0]; - const programId = accountKeys.get(noopInstruction.programIdIndex)!; - if (!programId.equals(SPL_NOOP_PROGRAM_ID)) { - throw Error( - `Only inner ix should be a noop, but instead is a ${programId.toBase58()}` - ); - } - const cpiData = Buffer.from(bs58.decode(noopInstruction.data)); - const changeLogEvent = deserializeChangeLogEventV1(cpiData); + // Get noop program instruction + const accountKeys = transaction!.transaction.message.getAccountKeys(); + const noopInstruction = transaction!.meta!.innerInstructions![0].instructions[0]; + const programId = accountKeys.get(noopInstruction.programIdIndex)!; + if (!programId.equals(SPL_NOOP_PROGRAM_ID)) { + throw Error(`Only inner ix should be a noop, but instead is a ${programId.toBase58()}`); + } + const cpiData = Buffer.from(bs58.decode(noopInstruction.data)); + const changeLogEvent = deserializeChangeLogEventV1(cpiData); - assert( - changeLogEvent.treeId.equals(cmt), - `Tree id in changeLog differs from expected tree ${cmt.toBase58()}` - ); - assert( - changeLogEvent.index === 0, - `ChangeLog should have index 0, but has index ${changeLogEvent.index}` - ); - assert( - new BN.BN(changeLogEvent.seq).toNumber() === 1, - `ChangeLog should have sequence number of 1, but has seq number of ${changeLogEvent.seq.toString()}` - ); + assert( + changeLogEvent.treeId.equals(cmt), + `Tree id in changeLog differs from expected tree ${cmt.toBase58()}`, + ); + assert(changeLogEvent.index === 0, `ChangeLog should have index 0, but has index ${changeLogEvent.index}`); + assert( + new BN.BN(changeLogEvent.seq).toNumber() === 1, + `ChangeLog should have sequence number of 1, but has seq number of ${changeLogEvent.seq.toString()}`, + ); - // Check that the emitted ChangeLog path matches up with the updated Tree - let nodeIndex = 0 + (1 << MAX_DEPTH); - let realTreeNode = offChainTree.leaves[0]; - while (nodeIndex > 0) { - const clNode = changeLogEvent.path.shift()!; - assert( - nodeIndex === clNode.index, - `Expected changeLog index to be ${nodeIndex} but is ${clNode.index}` - ); - assert( - realTreeNode!.node.equals(Buffer.from(clNode.node)), - `ChangeLog node differs at node index: ${clNode.index}` - ); - realTreeNode = realTreeNode.parent!; - nodeIndex = nodeIndex >> 1; - } + // Check that the emitted ChangeLog path matches up with the updated Tree + let nodeIndex = 0 + (1 << MAX_DEPTH); + let realTreeNode = offChainTree.leaves[0]; + while (nodeIndex > 0) { + const clNode = changeLogEvent.path.shift()!; + assert( + nodeIndex === clNode.index, + `Expected changeLog index to be ${nodeIndex} but is ${clNode.index}`, + ); + assert( + realTreeNode!.node.equals(Buffer.from(clNode.node)), + `ChangeLog node differs at node index: ${clNode.index}`, + ); + realTreeNode = realTreeNode.parent!; + nodeIndex = nodeIndex >> 1; + } + }); }); - }); }); diff --git a/account-compression/sdk/tests/merkleTree.test.ts b/account-compression/sdk/tests/merkleTree.test.ts index f2e4814f363..1ecc6763b2d 100644 --- a/account-compression/sdk/tests/merkleTree.test.ts +++ b/account-compression/sdk/tests/merkleTree.test.ts @@ -1,35 +1,31 @@ -import { strict as assert } from "node:assert"; +import { strict as assert } from 'node:assert'; -import * as crypto from "crypto"; +import * as crypto from 'crypto'; -import { emptyNode, MerkleTree } from "../src"; +import { emptyNode, MerkleTree } from '../src'; -describe("MerkleTree tests", () => { - it("Check constructor equivalence for depth 2 tree", () => { - const leaves = [ - crypto.randomBytes(32), - crypto.randomBytes(32), - crypto.randomBytes(32), - ]; - const rawLeaves = leaves.concat(emptyNode(0)); - const merkleTreeRaw = new MerkleTree(rawLeaves); - const merkleTreeSparse = MerkleTree.sparseMerkleTreeFromLeaves(leaves, 2); +describe('MerkleTree tests', () => { + it('Check constructor equivalence for depth 2 tree', () => { + const leaves = [crypto.randomBytes(32), crypto.randomBytes(32), crypto.randomBytes(32)]; + const rawLeaves = leaves.concat(emptyNode(0)); + const merkleTreeRaw = new MerkleTree(rawLeaves); + const merkleTreeSparse = MerkleTree.sparseMerkleTreeFromLeaves(leaves, 2); - assert(merkleTreeRaw.root.equals(merkleTreeSparse.root)); - }); + assert(merkleTreeRaw.root.equals(merkleTreeSparse.root)); + }); - const TEST_DEPTH = 14; - it(`Check proofs for 2^${TEST_DEPTH} tree`, () => { - const leaves: Buffer[] = []; - for (let i = 0; i < 2 ** TEST_DEPTH; i++) { - leaves.push(crypto.randomBytes(32)); - } - const merkleTree = new MerkleTree(leaves); + const TEST_DEPTH = 14; + it(`Check proofs for 2^${TEST_DEPTH} tree`, () => { + const leaves: Buffer[] = []; + for (let i = 0; i < 2 ** TEST_DEPTH; i++) { + leaves.push(crypto.randomBytes(32)); + } + const merkleTree = new MerkleTree(leaves); - // Check proofs - for (let i = 0; i < leaves.length; i++) { - const proof = merkleTree.getProof(i); - assert(MerkleTree.verify(merkleTree.getRoot(), proof)); - } - }); + // Check proofs + for (let i = 0; i < leaves.length; i++) { + const proof = merkleTree.getProof(i); + assert(MerkleTree.verify(merkleTree.getRoot(), proof)); + } + }); }); diff --git a/account-compression/sdk/tests/utils.ts b/account-compression/sdk/tests/utils.ts index 6016053a07c..4e49c305c71 100644 --- a/account-compression/sdk/tests/utils.ts +++ b/account-compression/sdk/tests/utils.ts @@ -29,7 +29,7 @@ export async function execute( instructions: TransactionInstruction[], signers: Signer[], skipPreflight = false, - verbose = false + verbose = false, ): Promise { let tx = new Transaction(); instructions.map(ix => { @@ -60,7 +60,7 @@ export async function createTreeOnChain( payer: Keypair, numLeaves: number, depthSizePair: ValidDepthSizePair, - canopyDepth = 0 + canopyDepth = 0, ): Promise<[Keypair, MerkleTree]> { const cmtKeypair = Keypair.generate(); @@ -75,7 +75,7 @@ export async function createTreeOnChain( cmtKeypair.publicKey, payer.publicKey, depthSizePair, - canopyDepth + canopyDepth, ); const ixs = [allocAccountIx, createInitEmptyMerkleTreeIx(cmtKeypair.publicKey, payer.publicKey, depthSizePair)]; @@ -103,7 +103,7 @@ export async function createEmptyTreeOnChain( provider: AnchorProvider, payer: Keypair, depthSizePair: ValidDepthSizePair, - canopyDepth = 0 + canopyDepth = 0, ): Promise { const cmtKeypair = Keypair.generate(); const allocAccountIx = await createAllocTreeIx( @@ -111,7 +111,7 @@ export async function createEmptyTreeOnChain( cmtKeypair.publicKey, payer.publicKey, depthSizePair, - canopyDepth + canopyDepth, ); const ixs = [allocAccountIx, createInitEmptyMerkleTreeIx(cmtKeypair.publicKey, payer.publicKey, depthSizePair)]; diff --git a/ci/do-audit.sh b/ci/do-audit.sh index 74631550c63..94da4303d23 100755 --- a/ci/do-audit.sh +++ b/ci/do-audit.sh @@ -5,32 +5,11 @@ cd "$(dirname "$0")/.." source ./ci/rust-version.sh stable cargo_audit_ignores=( - # Potential segfault in the time crate - # - # Blocked on chrono updating `time` to >= 0.2.23 - --ignore RUSTSEC-2020-0071 - - # tokio: vulnerability affecting named pipes on Windows - # - # Exception is a stopgap to unblock CI - # https://github.com/solana-labs/solana/issues/29586 - --ignore RUSTSEC-2023-0001 - # ed25519-dalek: Double Public Key Signing Function Oracle Attack # - # Remove once SPL upgrades to Solana v1.17 or greater + # Remove once SPL upgrades to ed25519-dalek v2 --ignore RUSTSEC-2022-0093 - # webpki: CPU denial of service in certificate path building - # - # No fixed upgrade is available! Only fix is switching to rustls-webpki - --ignore RUSTSEC-2023-0052 - - # tungstenite - # - # Remove once SPL upgrades to Solana v1.17 or greater - --ignore RUSTSEC-2023-0065 - # curve25519-dalek # # Remove once SPL upgrades to curve25519-dalek v4 diff --git a/ci/js-test-account-compression.sh b/ci/js-test-account-compression.sh index 38b6314eca2..2c9659b6ff1 100755 --- a/ci/js-test-account-compression.sh +++ b/ci/js-test-account-compression.sh @@ -5,8 +5,9 @@ cd "$(dirname "$0")/.." source ./ci/solana-version.sh install set -x -cd account-compression/sdk pnpm install +pnpm format +cd account-compression/sdk pnpm build pnpm build:program pnpm lint diff --git a/ci/js-test-libraries.sh b/ci/js-test-libraries.sh index fab495f0bba..9d8ef554e54 100755 --- a/ci/js-test-libraries.sh +++ b/ci/js-test-libraries.sh @@ -4,9 +4,10 @@ set -e cd "$(dirname "$0")/.." set -x -cd libraries/type-length-value/js - pnpm install +pnpm format + +cd libraries/type-length-value/js pnpm lint pnpm build pnpm test diff --git a/ci/js-test-memo.sh b/ci/js-test-memo.sh index fe6b243384f..3496f8d3f65 100755 --- a/ci/js-test-memo.sh +++ b/ci/js-test-memo.sh @@ -5,9 +5,10 @@ cd "$(dirname "$0")/.." source ./ci/solana-version.sh install set -x -cd memo/js - pnpm install +pnpm format + +cd memo/js pnpm lint pnpm build pnpm test diff --git a/ci/js-test-name-service.sh b/ci/js-test-name-service.sh index f854b5fae67..e78cb1501a2 100755 --- a/ci/js-test-name-service.sh +++ b/ci/js-test-name-service.sh @@ -5,9 +5,10 @@ cd "$(dirname "$0")/.." source ./ci/solana-version.sh install set -x -cd name-service/js - pnpm install +pnpm format + +cd name-service/js pnpm lint pnpm build:program pnpm build diff --git a/ci/js-test-single-pool.sh b/ci/js-test-single-pool.sh index 3842116df91..63ae054e328 100755 --- a/ci/js-test-single-pool.sh +++ b/ci/js-test-single-pool.sh @@ -4,10 +4,10 @@ set -ex cd "$(dirname "$0")/.." source ./ci/solana-version.sh install -cd single-pool/js pnpm install +pnpm format -cd packages/modern +cd single-pool/js/packages/modern pnpm lint pnpm build diff --git a/ci/js-test-stake-pool.sh b/ci/js-test-stake-pool.sh index 4650f6597bc..500e96acfd5 100755 --- a/ci/js-test-stake-pool.sh +++ b/ci/js-test-stake-pool.sh @@ -4,6 +4,7 @@ set -ex cd "$(dirname "$0")/.." pnpm install +pnpm format pnpm build cd stake-pool/js diff --git a/ci/js-test-token-group.sh b/ci/js-test-token-group.sh index 7c84fd16218..daead80d8bf 100755 --- a/ci/js-test-token-group.sh +++ b/ci/js-test-token-group.sh @@ -5,6 +5,7 @@ cd "$(dirname "$0")/.." set -x pnpm install +pnpm format pnpm build cd token-group/js diff --git a/ci/js-test-token-lending.sh b/ci/js-test-token-lending.sh index 8342a2ab47f..4753d04e2fc 100755 --- a/ci/js-test-token-lending.sh +++ b/ci/js-test-token-lending.sh @@ -5,6 +5,7 @@ cd "$(dirname "$0")/.." set -x pnpm install +pnpm format pnpm build cd token-lending/js diff --git a/ci/js-test-token-metadata.sh b/ci/js-test-token-metadata.sh index 14c0fc472ac..c12a3ebcb40 100755 --- a/ci/js-test-token-metadata.sh +++ b/ci/js-test-token-metadata.sh @@ -5,6 +5,7 @@ cd "$(dirname "$0")/.." set -x pnpm install +pnpm format pnpm build cd token-metadata/js diff --git a/ci/js-test-token-swap.sh b/ci/js-test-token-swap.sh index f08a7ea722a..923a72c7cf1 100755 --- a/ci/js-test-token-swap.sh +++ b/ci/js-test-token-swap.sh @@ -5,6 +5,7 @@ cd "$(dirname "$0")/.." source ./ci/solana-version.sh install pnpm install +pnpm format pnpm build cd token-swap/js diff --git a/ci/js-test-token.sh b/ci/js-test-token.sh index f7d0db98f69..089ee200c66 100755 --- a/ci/js-test-token.sh +++ b/ci/js-test-token.sh @@ -6,6 +6,7 @@ source ./ci/solana-version.sh install set -x pnpm install +pnpm format pnpm build cd token/js diff --git a/docs/package-lock.json b/docs/package-lock.json index fdc79968146..7a07067ad07 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -5537,11 +5537,11 @@ } }, "node_modules/axios": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", - "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", + "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==", "dependencies": { - "follow-redirects": "^1.15.0", + "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } @@ -23253,11 +23253,11 @@ } }, "axios": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", - "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", + "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==", "requires": { - "follow-redirects": "^1.15.0", + "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } diff --git a/governance/addin-mock/program/Cargo.toml b/governance/addin-mock/program/Cargo.toml index 4c500b8f6b4..10c772e04eb 100644 --- a/governance/addin-mock/program/Cargo.toml +++ b/governance/addin-mock/program/Cargo.toml @@ -17,7 +17,7 @@ bincode = "1.3.2" borsh = "1.5.1" num-derive = "0.4" num-traits = "0.2" -serde = "1.0.204" +serde = "1.0.208" serde_derive = "1.0.103" solana-program = "2.0.3" spl-token = { version = "6.0", path = "../../../token/program", features = [ diff --git a/governance/chat/program/Cargo.toml b/governance/chat/program/Cargo.toml index 743a8186e14..caccacbefa8 100644 --- a/governance/chat/program/Cargo.toml +++ b/governance/chat/program/Cargo.toml @@ -17,7 +17,7 @@ bincode = "1.3.2" borsh = "1.5.1" num-derive = "0.4" num-traits = "0.2" -serde = "1.0.204" +serde = "1.0.208" serde_derive = "1.0.103" solana-program = "2.0.3" spl-token = { version = "6.0", path = "../../../token/program", features = [ diff --git a/governance/program/Cargo.toml b/governance/program/Cargo.toml index 36ecb4b4944..495cbbd79ae 100644 --- a/governance/program/Cargo.toml +++ b/governance/program/Cargo.toml @@ -17,7 +17,7 @@ bincode = "1.3.2" borsh = "1.5.1" num-derive = "0.4" num-traits = "0.2" -serde = "1.0.204" +serde = "1.0.208" serde_derive = "1.0.103" solana-program = "2.0.3" spl-token = { version = "6.0", path = "../../token/program", features = [ diff --git a/governance/test-sdk/Cargo.toml b/governance/test-sdk/Cargo.toml index 536d5806839..95d2930b289 100644 --- a/governance/test-sdk/Cargo.toml +++ b/governance/test-sdk/Cargo.toml @@ -14,7 +14,7 @@ borsh = "1.5.1" lazy_static = "1.5.0" num-derive = "0.4" num-traits = "0.2" -serde = "1.0.204" +serde = "1.0.208" serde_derive = "1.0.103" solana-program = "2.0.3" solana-program-test = "2.0.3" diff --git a/governance/tools/Cargo.toml b/governance/tools/Cargo.toml index e2375d8bd27..150f5ac5467 100644 --- a/governance/tools/Cargo.toml +++ b/governance/tools/Cargo.toml @@ -13,7 +13,7 @@ bincode = "1.3.2" borsh = "1.5.1" num-derive = "0.4" num-traits = "0.2" -serde = "1.0.204" +serde = "1.0.208" serde_derive = "1.0.103" solana-program = "2.0.3" spl-token = { version = "6.0", path = "../../token/program", features = [ diff --git a/instruction-padding/program/Cargo.toml b/instruction-padding/program/Cargo.toml index f6e07034cea..b863e8be46d 100644 --- a/instruction-padding/program/Cargo.toml +++ b/instruction-padding/program/Cargo.toml @@ -13,7 +13,7 @@ no-entrypoint = [] test-sbf = [] [dependencies] -num_enum = "0.7.2" +num_enum = "0.7.3" solana-program = "2.0.3" [dev-dependencies] diff --git a/libraries/concurrent-merkle-tree/Cargo.toml b/libraries/concurrent-merkle-tree/Cargo.toml index 62b05ba7d34..2504dfc27a8 100644 --- a/libraries/concurrent-merkle-tree/Cargo.toml +++ b/libraries/concurrent-merkle-tree/Cargo.toml @@ -13,7 +13,7 @@ sol-log = ["log"] [dependencies] solana-program = ">=1.18.11,<=2" -bytemuck = "1.16" +bytemuck = "1.17" thiserror = "1.0.63" [dev-dependencies] diff --git a/libraries/discriminator/Cargo.toml b/libraries/discriminator/Cargo.toml index 9f0d92a0e2d..3a93666fff9 100644 --- a/libraries/discriminator/Cargo.toml +++ b/libraries/discriminator/Cargo.toml @@ -12,7 +12,7 @@ borsh = ["dep:borsh"] [dependencies] borsh = { version = "1", optional = true } -bytemuck = { version = "1.16.1", features = ["derive"] } +bytemuck = { version = "1.17.0", features = ["derive"] } solana-program = "2.0.3" spl-discriminator-derive = { version = "0.2.0", path = "./derive" } diff --git a/libraries/pod/Cargo.toml b/libraries/pod/Cargo.toml index 15ad72d1019..82a40b3d971 100644 --- a/libraries/pod/Cargo.toml +++ b/libraries/pod/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spl-pod" -version = "0.3.0" +version = "0.3.1" description = "Solana Program Library Plain Old Data (Pod)" authors = ["Solana Labs Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" @@ -8,21 +8,21 @@ license = "Apache-2.0" edition = "2021" [features] -serde-traits = ["dep:serde", "dep:base64"] +serde-traits = ["dep:serde"] borsh = ["dep:borsh"] [dependencies] -base64 = { version = "0.22.1", optional = true } borsh = { version = "1.5.1", optional = true } -bytemuck = { version = "1.16.1" } -bytemuck_derive = { version = "1.7.0" } -serde = { version = "1.0.204", optional = true } +bytemuck = { version = "1.17.0" } +bytemuck_derive = { version = "1.7.1" } +serde = { version = "1.0.208", optional = true } solana-program = "2.0.3" -solana-zk-token-sdk = "2.0.3" +solana-zk-sdk = "2.0.3" spl-program-error = { version = "0.5.0", path = "../program-error" } [dev-dependencies] -serde_json = "1.0.121" +serde_json = "1.0.125" +base64 = { version = "0.22.1" } [lib] crate-type = ["cdylib", "lib"] diff --git a/libraries/pod/src/option.rs b/libraries/pod/src/option.rs index 3b15bdd1bc4..0ebad18608a 100644 --- a/libraries/pod/src/option.rs +++ b/libraries/pod/src/option.rs @@ -8,16 +8,25 @@ use { bytemuck::{Pod, Zeroable}, - solana_program::{program_option::COption, pubkey::Pubkey}, + solana_program::{ + program_error::ProgramError, + program_option::COption, + pubkey::{Pubkey, PUBKEY_BYTES}, + }, }; /// Trait for types that can be `None`. /// /// This trait is used to indicate that a type can be `None` according to a /// specific value. -pub trait Nullable: Default + Pod { +pub trait Nullable: PartialEq + Pod + Sized { + /// Value that represents `None` for the type. + const NONE: Self; + /// Indicates whether the value is `None` or not. - fn is_none(&self) -> bool; + fn is_none(&self) -> bool { + self == &Self::NONE + } /// Indicates whether the value is `Some`` value of type `T`` or not. fn is_some(&self) -> bool { @@ -34,6 +43,12 @@ pub trait Nullable: Default + Pod { #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct PodOption(T); +impl Default for PodOption { + fn default() -> Self { + Self(T::NONE) + } +} + impl PodOption { /// Returns the contained value as an `Option`. #[inline] @@ -66,8 +81,16 @@ impl PodOption { } } +/// ## Safety +/// +/// `PodOption` is a transparent wrapper around a `Pod` type `T` with identical +/// data representation. unsafe impl Pod for PodOption {} +/// ## Safety +/// +/// `PodOption` is a transparent wrapper around a `Pod` type `T` with identical +/// data representation. unsafe impl Zeroable for PodOption {} impl From for PodOption { @@ -76,32 +99,33 @@ impl From for PodOption { } } -impl From> for PodOption { - fn from(from: Option) -> Self { - match from { - Some(value) => PodOption(value), - None => PodOption(T::default()), +impl TryFrom> for PodOption { + type Error = ProgramError; + + fn try_from(value: Option) -> Result { + match value { + Some(value) if value.is_none() => Err(ProgramError::InvalidArgument), + Some(value) => Ok(PodOption(value)), + None => Ok(PodOption(T::NONE)), } } } -impl From> for PodOption { - fn from(from: COption) -> Self { - match from { - COption::Some(value) => PodOption(value), - COption::None => PodOption(T::default()), +impl TryFrom> for PodOption { + type Error = ProgramError; + + fn try_from(value: COption) -> Result { + match value { + COption::Some(value) if value.is_none() => Err(ProgramError::InvalidArgument), + COption::Some(value) => Ok(PodOption(value)), + COption::None => Ok(PodOption(T::NONE)), } } } /// Implementation of `Nullable` for `Pubkey`. -/// -/// The implementation assumes that the default value of `Pubkey` represents -/// the `None` value. impl Nullable for Pubkey { - fn is_none(&self) -> bool { - self == &Pubkey::default() - } + const NONE: Self = Pubkey::new_from_array([0u8; PUBKEY_BYTES]); } #[cfg(test)] @@ -126,13 +150,44 @@ mod tests { assert_eq!(values[1], PodOption::from(Pubkey::default())); let option_pubkey = Some(sysvar::ID); - let pod_option_pubkey: PodOption = option_pubkey.into(); + let pod_option_pubkey: PodOption = option_pubkey.try_into().unwrap(); assert_eq!(pod_option_pubkey, PodOption::from(sysvar::ID)); - assert_eq!(pod_option_pubkey, PodOption::from(option_pubkey)); + assert_eq!( + pod_option_pubkey, + PodOption::try_from(option_pubkey).unwrap() + ); let coption_pubkey = COption::Some(sysvar::ID); - let pod_option_pubkey: PodOption = coption_pubkey.into(); + let pod_option_pubkey: PodOption = coption_pubkey.try_into().unwrap(); assert_eq!(pod_option_pubkey, PodOption::from(sysvar::ID)); - assert_eq!(pod_option_pubkey, PodOption::from(coption_pubkey)); + assert_eq!( + pod_option_pubkey, + PodOption::try_from(coption_pubkey).unwrap() + ); + } + + #[test] + fn test_try_from_option() { + let some_pubkey = Some(sysvar::ID); + assert_eq!( + PodOption::try_from(some_pubkey).unwrap(), + PodOption(sysvar::ID) + ); + + let none_pubkey = None; + assert_eq!( + PodOption::try_from(none_pubkey).unwrap(), + PodOption::from(Pubkey::NONE) + ); + + let invalid_option = Some(Pubkey::NONE); + let err = PodOption::try_from(invalid_option).unwrap_err(); + assert_eq!(err, ProgramError::InvalidArgument); + } + + #[test] + fn test_default() { + let def = PodOption::::default(); + assert_eq!(def, None.try_into().unwrap()); } } diff --git a/libraries/pod/src/optional_keys.rs b/libraries/pod/src/optional_keys.rs index ad71b1bcc4d..b31494d4ed7 100644 --- a/libraries/pod/src/optional_keys.rs +++ b/libraries/pod/src/optional_keys.rs @@ -1,18 +1,17 @@ //! Optional pubkeys that can be used a `Pod`s #[cfg(feature = "borsh")] use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; +use { + bytemuck_derive::{Pod, Zeroable}, + solana_program::{program_error::ProgramError, program_option::COption, pubkey::Pubkey}, + solana_zk_sdk::encryption::pod::elgamal::PodElGamalPubkey, +}; #[cfg(feature = "serde-traits")] use { - base64::{prelude::BASE64_STANDARD, Engine}, serde::de::{Error, Unexpected, Visitor}, serde::{Deserialize, Deserializer, Serialize, Serializer}, std::{convert::TryFrom, fmt, str::FromStr}, }; -use { - bytemuck_derive::{Pod, Zeroable}, - solana_program::{program_error::ProgramError, program_option::COption, pubkey::Pubkey}, - solana_zk_token_sdk::zk_token_elgamal::pod::ElGamalPubkey, -}; /// A Pubkey that encodes `None` as all `0`, meant to be usable as a Pod type, /// similar to all NonZero* number types from the bytemuck library. @@ -131,21 +130,21 @@ impl<'de> Deserialize<'de> for OptionalNonZeroPubkey { /// type. #[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] #[repr(transparent)] -pub struct OptionalNonZeroElGamalPubkey(ElGamalPubkey); +pub struct OptionalNonZeroElGamalPubkey(PodElGamalPubkey); impl OptionalNonZeroElGamalPubkey { /// Checks equality between an OptionalNonZeroElGamalPubkey and an /// ElGamalPubkey when interpreted as bytes. - pub fn equals(&self, other: &ElGamalPubkey) -> bool { + pub fn equals(&self, other: &PodElGamalPubkey) -> bool { &self.0 == other } } -impl TryFrom> for OptionalNonZeroElGamalPubkey { +impl TryFrom> for OptionalNonZeroElGamalPubkey { type Error = ProgramError; - fn try_from(p: Option) -> Result { + fn try_from(p: Option) -> Result { match p { - None => Ok(Self(ElGamalPubkey::default())), + None => Ok(Self(PodElGamalPubkey::default())), Some(elgamal_pubkey) => { - if elgamal_pubkey == ElGamalPubkey::default() { + if elgamal_pubkey == PodElGamalPubkey::default() { Err(ProgramError::InvalidArgument) } else { Ok(Self(elgamal_pubkey)) @@ -154,9 +153,9 @@ impl TryFrom> for OptionalNonZeroElGamalPubkey { } } } -impl From for Option { +impl From for Option { fn from(p: OptionalNonZeroElGamalPubkey) -> Self { - if p.0 == ElGamalPubkey::default() { + if p.0 == PodElGamalPubkey::default() { None } else { Some(p.0) @@ -164,16 +163,13 @@ impl From for Option { } } -#[cfg(any(feature = "serde-traits", test))] -const OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN: usize = 32; - #[cfg(feature = "serde-traits")] impl Serialize for OptionalNonZeroElGamalPubkey { fn serialize(&self, s: S) -> Result where S: Serializer, { - if self.0 == ElGamalPubkey::default() { + if self.0 == PodElGamalPubkey::default() { s.serialize_none() } else { s.serialize_some(&self.0.to_string()) @@ -196,18 +192,7 @@ impl<'de> Visitor<'de> for OptionalNonZeroElGamalPubkeyVisitor { where E: Error, { - let bytes = BASE64_STANDARD.decode(v).map_err(Error::custom)?; - - if bytes.len() != OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN { - return Err(Error::custom(format!( - "Length of base64 decoded bytes is not {}", - OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN - ))); - } - - let mut array = [0; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN]; - array.copy_from_slice(&bytes[0..OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN]); - let elgamal_pubkey = ElGamalPubkey(array); + let elgamal_pubkey: PodElGamalPubkey = FromStr::from_str(v).map_err(Error::custom)?; OptionalNonZeroElGamalPubkey::try_from(Some(elgamal_pubkey)).map_err(Error::custom) } @@ -231,7 +216,12 @@ impl<'de> Deserialize<'de> for OptionalNonZeroElGamalPubkey { #[cfg(test)] mod tests { - use {super::*, crate::bytemuck::pod_from_bytes, solana_program::pubkey::PUBKEY_BYTES}; + use { + super::*, + crate::bytemuck::pod_from_bytes, + base64::{prelude::BASE64_STANDARD, Engine}, + solana_program::pubkey::PUBKEY_BYTES, + }; #[test] fn test_pod_non_zero_option() { @@ -290,23 +280,41 @@ mod tests { assert_eq!(optional_non_zero_pubkey_none, deserialized_none); } + const OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN: usize = 32; + + // Unfortunately, the `solana-zk-sdk` does not expose a constructor interface + // to construct `PodRistrettoPoint` from bytes. As a work-around, encode the + // bytes as base64 string and then convert the string to a + // `PodElGamalCiphertext`. + // + // The constructor will be added (and this function removed) with + // `solana-zk-sdk` 2.1. + fn elgamal_pubkey_from_bytes(bytes: &[u8]) -> PodElGamalPubkey { + let string = BASE64_STANDARD.encode(bytes); + std::str::FromStr::from_str(&string).unwrap() + } + #[test] fn test_pod_non_zero_elgamal_option() { assert_eq!( - Some(ElGamalPubkey([1; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN])), - Option::::from(OptionalNonZeroElGamalPubkey(ElGamalPubkey( - [1; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN] - ))) + Some(elgamal_pubkey_from_bytes( + &[1; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN] + )), + Option::::from(OptionalNonZeroElGamalPubkey( + elgamal_pubkey_from_bytes(&[1; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN]) + )) ); assert_eq!( None, - Option::::from(OptionalNonZeroElGamalPubkey(ElGamalPubkey( - [0; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN] - ))) + Option::::from(OptionalNonZeroElGamalPubkey( + elgamal_pubkey_from_bytes(&[0; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN]) + )) ); assert_eq!( - OptionalNonZeroElGamalPubkey(ElGamalPubkey([1; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN])), + OptionalNonZeroElGamalPubkey(elgamal_pubkey_from_bytes( + &[1; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN] + )), *pod_from_bytes::( &[1; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN] ) @@ -318,8 +326,9 @@ mod tests { #[cfg(feature = "serde-traits")] #[test] fn test_pod_non_zero_elgamal_option_serde_some() { - let optional_non_zero_elgamal_pubkey_some = - OptionalNonZeroElGamalPubkey(ElGamalPubkey([1; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN])); + let optional_non_zero_elgamal_pubkey_some = OptionalNonZeroElGamalPubkey( + elgamal_pubkey_from_bytes(&[1; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN]), + ); let serialized_some = serde_json::to_string(&optional_non_zero_elgamal_pubkey_some).unwrap(); assert_eq!( @@ -335,8 +344,9 @@ mod tests { #[cfg(feature = "serde-traits")] #[test] fn test_pod_non_zero_elgamal_option_serde_none() { - let optional_non_zero_elgamal_pubkey_none = - OptionalNonZeroElGamalPubkey(ElGamalPubkey([0; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN])); + let optional_non_zero_elgamal_pubkey_none = OptionalNonZeroElGamalPubkey( + elgamal_pubkey_from_bytes(&[0; OPTIONAL_NONZERO_ELGAMAL_PUBKEY_LEN]), + ); let serialized_none = serde_json::to_string(&optional_non_zero_elgamal_pubkey_none).unwrap(); assert_eq!(&serialized_none, "null"); diff --git a/libraries/program-error/derive/src/macro_impl.rs b/libraries/program-error/derive/src/macro_impl.rs index 21e89ee3441..1f90cd55613 100644 --- a/libraries/program-error/derive/src/macro_impl.rs +++ b/libraries/program-error/derive/src/macro_impl.rs @@ -1,7 +1,7 @@ //! The actual token generator for the macro use { - crate::parser::SplProgramErrorArgs, + crate::parser::{SolanaProgram, SplProgramErrorArgs}, proc_macro2::Span, quote::quote, sha2::{Digest, Sha256}, @@ -36,10 +36,13 @@ pub enum MacroType { impl MacroType { /// Generates the corresponding tokens based on variant selection pub fn generate_tokens(&mut self) -> proc_macro2::TokenStream { + let default_solana_program = SolanaProgram::default(); match self { - Self::IntoProgramError { ident } => into_program_error(ident), - Self::DecodeError { ident } => decode_error(ident), - Self::PrintProgramError { ident, variants } => print_program_error(ident, variants), + Self::IntoProgramError { ident } => into_program_error(ident, &default_solana_program), + Self::DecodeError { ident } => decode_error(ident, &default_solana_program), + Self::PrintProgramError { ident, variants } => { + print_program_error(ident, variants, &default_solana_program) + } Self::SplProgramError { args, item_enum } => spl_program_error(args, item_enum), } } @@ -48,25 +51,27 @@ impl MacroType { /// Builds the implementation of /// `Into` More specifically, /// implements `From for solana_program::program_error::ProgramError` -pub fn into_program_error(ident: &Ident) -> proc_macro2::TokenStream { - quote! { - impl From<#ident> for ::solana_program::program_error::ProgramError { +pub fn into_program_error(ident: &Ident, import: &SolanaProgram) -> proc_macro2::TokenStream { + let this_impl = quote! { + impl From<#ident> for #import::program_error::ProgramError { fn from(e: #ident) -> Self { - ::solana_program::program_error::ProgramError::Custom(e as u32) + #import::program_error::ProgramError::Custom(e as u32) } } - } + }; + import.wrap(this_impl) } /// Builds the implementation of `solana_program::decode_error::DecodeError` -pub fn decode_error(ident: &Ident) -> proc_macro2::TokenStream { - quote! { - impl ::solana_program::decode_error::DecodeError for #ident { +pub fn decode_error(ident: &Ident, import: &SolanaProgram) -> proc_macro2::TokenStream { + let this_impl = quote! { + impl #import::decode_error::DecodeError for #ident { fn type_of() -> &'static str { stringify!(#ident) } } - } + }; + import.wrap(this_impl) } /// Builds the implementation of @@ -74,6 +79,7 @@ pub fn decode_error(ident: &Ident) -> proc_macro2::TokenStream { pub fn print_program_error( ident: &Ident, variants: &Punctuated, + import: &SolanaProgram, ) -> proc_macro2::TokenStream { let ppe_match_arms = variants.iter().map(|variant| { let variant_ident = &variant.ident; @@ -81,18 +87,18 @@ pub fn print_program_error( .unwrap_or_else(|| String::from("Unknown custom program error")); quote! { #ident::#variant_ident => { - ::solana_program::msg!(#error_msg) + #import::msg!(#error_msg) } } }); - quote! { - impl ::solana_program::program_error::PrintProgramError for #ident { + let this_impl = quote! { + impl #import::program_error::PrintProgramError for #ident { fn print(&self) where E: 'static + std::error::Error - + ::solana_program::decode_error::DecodeError - + ::solana_program::program_error::PrintProgramError + + #import::decode_error::DecodeError + + #import::program_error::PrintProgramError + num_traits::FromPrimitive, { match self { @@ -100,7 +106,8 @@ pub fn print_program_error( } } } - } + }; + import.wrap(this_impl) } /// Helper to parse out the string literal from the `#[error(..)]` attribute @@ -128,9 +135,9 @@ pub fn spl_program_error( let ident = &item_enum.ident; let variants = &item_enum.variants; - let into_program_error = into_program_error(ident); - let decode_error = decode_error(ident); - let print_program_error = print_program_error(ident, variants); + let into_program_error = into_program_error(ident, &args.import); + let decode_error = decode_error(ident, &args.import); + let print_program_error = print_program_error(ident, variants, &args.import); quote! { #[repr(u32)] diff --git a/libraries/program-error/derive/src/parser.rs b/libraries/program-error/derive/src/parser.rs index a09ffc7ba7c..7e01cb728cc 100644 --- a/libraries/program-error/derive/src/parser.rs +++ b/libraries/program-error/derive/src/parser.rs @@ -1,11 +1,12 @@ //! Token parsing use { - proc_macro2::Ident, + proc_macro2::{Ident, Span, TokenStream}, + quote::quote, syn::{ parse::{Parse, ParseStream}, token::Comma, - LitInt, Token, + LitInt, LitStr, Token, }, }; @@ -14,20 +15,60 @@ pub struct SplProgramErrorArgs { /// Whether to hash the error codes using `solana_program::hash` /// or to use the default error code assigned by `num_traits`. pub hash_error_code_start: Option, + /// Crate to use for solana_program + pub import: SolanaProgram, +} + +/// Struct representing the path to a `solana_program` crate, which may be +/// renamed or otherwise. +pub struct SolanaProgram { + import: Ident, + explicit: bool, +} +impl quote::ToTokens for SolanaProgram { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.import.to_tokens(tokens); + } +} +impl SolanaProgram { + pub fn wrap(&self, output: TokenStream) -> TokenStream { + if self.explicit { + output + } else { + anon_const_trick(output) + } + } +} +impl Default for SolanaProgram { + fn default() -> Self { + Self { + import: Ident::new("_solana_program", Span::call_site()), + explicit: false, + } + } } impl Parse for SplProgramErrorArgs { fn parse(input: ParseStream) -> syn::Result { - if input.is_empty() { - return Ok(Self { - hash_error_code_start: None, - }); - } - match SplProgramErrorArgParser::parse(input)? { - SplProgramErrorArgParser::HashErrorCodes { value, .. } => Ok(Self { - hash_error_code_start: Some(value.base10_parse::()?), - }), + let mut hash_error_code_start = None; + let mut import = None; + while !input.is_empty() { + match SplProgramErrorArgParser::parse(input)? { + SplProgramErrorArgParser::HashErrorCodes { value, .. } => { + hash_error_code_start = Some(value.base10_parse::()?); + } + SplProgramErrorArgParser::SolanaProgramCrate { value, .. } => { + import = Some(SolanaProgram { + import: value.parse()?, + explicit: true, + }); + } + } } + Ok(Self { + hash_error_code_start, + import: import.unwrap_or(SolanaProgram::default()), + }) } } @@ -35,30 +76,70 @@ impl Parse for SplProgramErrorArgs { /// ie. `#[spl_program_error(hash_error_code_start = 1275525928)]` enum SplProgramErrorArgParser { HashErrorCodes { - _ident: Ident, _equals_sign: Token![=], value: LitInt, _comma: Option, }, + SolanaProgramCrate { + _equals_sign: Token![=], + value: LitStr, + _comma: Option, + }, } impl Parse for SplProgramErrorArgParser { fn parse(input: ParseStream) -> syn::Result { - let _ident = { - let ident = input.parse::()?; - if ident != "hash_error_code_start" { - return Err(input.error("Expected argument 'hash_error_code_start'")); + let ident = input.parse::()?; + match ident.to_string().as_str() { + "hash_error_code_start" => { + let _equals_sign = input.parse::()?; + let value = input.parse::()?; + let _comma: Option = input.parse().unwrap_or(None); + Ok(Self::HashErrorCodes { + _equals_sign, + value, + _comma, + }) + } + "solana_program" => { + let _equals_sign = input.parse::()?; + let value = input.parse::()?; + let _comma: Option = input.parse().unwrap_or(None); + Ok(Self::SolanaProgramCrate { + _equals_sign, + value, + _comma, + }) } - ident + _ => Err(input.error("Expected argument 'hash_error_code_start' or 'solana_program'")), + } + } +} + +// Within `exp`, you can bring things into scope with `extern crate`. +// +// We don't want to assume that `solana_program::` is in scope - the user may +// have imported it under a different name, or may have imported it in a +// non-toplevel module (common when putting impls behind a feature gate). +// +// Solution: let's just generate `extern crate solana_program as +// _solana_program` and then refer to `_solana_program` in the derived code. +// However, macros are not allowed to produce `extern crate` statements at the +// toplevel. +// +// Solution: let's generate `mod _impl_foo` and import solana_program within +// that. However, now we lose access to private members of the surrounding +// module. This is a problem if, for example, we're deriving for a newtype, +// where the inner type is defined in the same module, but not exported. +// +// Solution: use the anonymous const trick. For some reason, `extern crate` +// statements are allowed here, but everything from the surrounding module is in +// scope. This trick is taken from serde and num_traits. +fn anon_const_trick(exp: TokenStream) -> TokenStream { + quote! { + const _: () = { + extern crate solana_program as _solana_program; + #exp }; - let _equals_sign = input.parse::()?; - let value = input.parse::()?; - let _comma: Option = input.parse().unwrap_or(None); - Ok(Self::HashErrorCodes { - _ident, - _equals_sign, - value, - _comma, - }) } } diff --git a/libraries/program-error/tests/spl.rs b/libraries/program-error/tests/spl.rs index bbdb9af0aca..d772d24f6c5 100644 --- a/libraries/program-error/tests/spl.rs +++ b/libraries/program-error/tests/spl.rs @@ -72,3 +72,20 @@ fn test_library_error_codes() { first_error_as_u32 + 3, ); } + +/// Example error with solana_program crate set +#[spl_program_error(solana_program = "solana_program")] +enum ExampleSolanaProgramCrateError { + /// This is a very informative error + #[error("This is a very informative error")] + VeryInformativeError, + /// This is a super important error + #[error("This is a super important error")] + SuperImportantError, +} + +/// Tests that all macros compile +#[test] +fn test_macros_compile_with_solana_program_crate() { + let _ = ExampleSolanaProgramCrateError::VeryInformativeError; +} diff --git a/libraries/tlv-account-resolution/Cargo.toml b/libraries/tlv-account-resolution/Cargo.toml index 8cc6b6196e4..5938a8791cf 100644 --- a/libraries/tlv-account-resolution/Cargo.toml +++ b/libraries/tlv-account-resolution/Cargo.toml @@ -12,8 +12,8 @@ serde-traits = ["dep:serde"] test-sbf = [] [dependencies] -bytemuck = { version = "1.16.1", features = ["derive"] } -serde = { version = "1.0.204", optional = true } +bytemuck = { version = "1.17.0", features = ["derive"] } +serde = { version = "1.0.208", optional = true } solana-program = "2.0.3" spl-discriminator = { version = "0.3.0", path = "../discriminator" } spl-program-error = { version = "0.5.0", path = "../program-error" } diff --git a/libraries/type-length-value/Cargo.toml b/libraries/type-length-value/Cargo.toml index 01e84f76334..ce6c087a850 100644 --- a/libraries/type-length-value/Cargo.toml +++ b/libraries/type-length-value/Cargo.toml @@ -12,7 +12,7 @@ exclude = ["js/**"] derive = ["dep:spl-type-length-value-derive"] [dependencies] -bytemuck = { version = "1.16.1", features = ["derive"] } +bytemuck = { version = "1.17.0", features = ["derive"] } solana-program = "2.0.3" spl-discriminator = { version = "0.3.0", path = "../discriminator" } spl-program-error = { version = "0.5.0", path = "../program-error" } diff --git a/libraries/type-length-value/README.md b/libraries/type-length-value/README.md index 24ccf8aff95..b72442c926c 100644 --- a/libraries/type-length-value/README.md +++ b/libraries/type-length-value/README.md @@ -8,9 +8,8 @@ This simple examples defines a zero-copy type with its discriminator. ```rust use { - borsh::{BorshSerialize, BorshDeserialize}, bytemuck::{Pod, Zeroable}, - spl_discriminator::{ArrayDiscriminator, SplDiscriminate} + spl_discriminator::{ArrayDiscriminator, SplDiscriminate}, spl_type_length_value::{ state::{TlvState, TlvStateBorrowed, TlvStateMut} }, @@ -45,8 +44,12 @@ impl SplDiscriminate for MyOtherPodValue { // Account will have two sets of `get_base_len()` (8-byte discriminator and 4-byte length), // and enough room for a `MyPodValue` and a `MyOtherPodValue` -let account_size = TlvState::get_base_len() + std::mem::size_of::() + \ - TlvState::get_base_len() + std::mem::size_of::(); +let account_size = TlvStateMut::get_base_len() + + std::mem::size_of::() + + TlvStateMut::get_base_len() + + std::mem::size_of::() + + TlvStateMut::get_base_len() + + std::mem::size_of::(); // Buffer likely comes from a Solana `solana_program::account_info::AccountInfo`, // but this example just uses a vector. @@ -59,25 +62,29 @@ let mut state = TlvStateMut::unpack(&mut buffer).unwrap(); // Note: you'll need to provide a boolean whether or not to allow repeating // values with the same TLV discriminator. // If set to false, this function will error when an existing entry is detected. -let value = state.init_value::(false).unwrap(); +// Note the function also returns the repetition number, which can be used to +// fetch the value again. +let (value, _repetition_number) = state.init_value::(false).unwrap(); // Update it in-place value.data[0] = 1; // Init and write another default value // This time, we're going to allow repeating values. -let other_value1 = state.init_value::(true).unwrap(); +let (other_value1, other_value1_repetition_number) = + state.init_value::(true).unwrap(); assert_eq!(other_value1.data, 10); // Update it in-place other_value1.data = 2; // Let's do it again, since we can now have repeating values! -let other_value2 = state.init_value::(true).unwrap(); +let (other_value2, other_value2_repetition_number) = + state.init_value::(true).unwrap(); assert_eq!(other_value2.data, 10); // Update it in-place other_value2.data = 4; -// Later on, to work with it again, since we did _not_ allow repeating entries, -// we can just get the first value we encounter. +// Later on, to work with it again, we can just get the first value we +// encounter, because we did _not_ allow repeating entries for `MyPodValue`. let value = state.get_first_value_mut::().unwrap(); // Or fetch it from an immutable buffer @@ -85,9 +92,13 @@ let state = TlvStateBorrowed::unpack(&buffer).unwrap(); let value1 = state.get_first_value::().unwrap(); // Since we used repeating entries for `MyOtherPodValue`, we can grab either one by -// its entry number -let value1 = state.get_value_with_repetition::(1).unwrap(); -let value2 = state.get_value_with_repetition::(2).unwrap(); +// its repetition number +let value1 = state + .get_value_with_repetition::(other_value1_repetition_number) + .unwrap(); +let value2 = state + .get_value_with_repetition::(other_value2_repetition_number) + .unwrap(); ``` @@ -95,7 +106,7 @@ let value2 = state.get_value_with_repetition::(2).unwrap(); The Solana blockchain exposes slabs of bytes to on-chain programs, allowing program writers to interpret these bytes and change them however they wish. Currently, -programs interpret account bytes as being only of one type. For example, an token +programs interpret account bytes as being only of one type. For example, a token mint account is only ever a token mint, an AMM pool account is only ever an AMM pool, a token metadata account can only hold token metadata, etc. @@ -135,7 +146,11 @@ trait on your type. ```rust use { borsh::{BorshDeserialize, BorshSerialize}, - solana_program::borsh::{get_instance_packed_len, try_from_slice_unchecked}, + solana_program::{ + borsh1::{get_instance_packed_len, try_from_slice_unchecked}, + program_error::ProgramError, + }, + spl_discriminator::{ArrayDiscriminator, SplDiscriminate}, spl_type_length_value::{ state::{TlvState, TlvStateMut}, variable_len_pack::VariableLenPack @@ -164,7 +179,7 @@ impl VariableLenPack for MyVariableLenType { let initial_data = "This is a pretty cool test!"; // Allocate exactly the right size for the string, can go bigger if desired let tlv_size = 4 + initial_data.len(); -let account_size = TlvState::get_base_len() + tlv_size; +let account_size = TlvStateMut::get_base_len() + tlv_size; // Buffer likely comes from a Solana `solana_program::account_info::AccountInfo`, // but this example just uses a vector. @@ -177,7 +192,7 @@ let _ = state.alloc::(tlv_size, false).unwrap(); let my_variable_len = MyVariableLenType { data: initial_data.to_string() }; -state.pack_variable_len_value(&my_variable_len).unwrap(); +state.pack_first_variable_len_value(&my_variable_len).unwrap(); let deser = state.get_first_variable_len_value::().unwrap(); assert_eq!(deser, my_variable_len); ``` diff --git a/libraries/type-length-value/js/.prettierignore b/libraries/type-length-value/js/.prettierignore deleted file mode 100644 index 6da325effab..00000000000 --- a/libraries/type-length-value/js/.prettierignore +++ /dev/null @@ -1,5 +0,0 @@ -docs -lib -test-ledger - -package-lock.json diff --git a/libraries/type-length-value/js/.prettierrc b/libraries/type-length-value/js/.prettierrc deleted file mode 100644 index b9ce4c1923a..00000000000 --- a/libraries/type-length-value/js/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "printWidth": 120, - "trailingComma": "es5", - "tabWidth": 4, - "semi": true, - "singleQuote": true -} \ No newline at end of file diff --git a/libraries/type-length-value/js/package.json b/libraries/type-length-value/js/package.json index 3de3c2a293a..e21ee2189f5 100644 --- a/libraries/type-length-value/js/package.json +++ b/libraries/type-length-value/js/package.json @@ -33,9 +33,8 @@ "deploy": "npm run deploy:docs", "deploy:docs": "npm run docs && gh-pages --dest type-length-value/js --dist docs --dotfiles", "docs": "shx rm -rf docs && typedoc && shx cp .nojekyll docs/", - "fmt": "prettier --write '{*,**/*}.{ts,tsx,js,jsx,json}'", - "lint": "prettier --check '{*,**/*}.{ts,tsx,js,jsx,json}' && eslint --max-warnings 0 .", - "lint:fix": "npm run fmt && eslint --fix .", + "lint": "eslint --max-warnings 0 .", + "lint:fix": "eslint --fix .", "nuke": "shx rm -rf node_modules package-lock.json || true", "postbuild": "shx echo '{ \"type\": \"commonjs\" }' > lib/cjs/package.json", "reinstall": "npm run nuke && npm install", @@ -47,22 +46,19 @@ "buffer": "^6.0.3" }, "devDependencies": { - "@types/chai": "^4.3.16", + "@types/chai": "^4.3.17", "@types/mocha": "^10.0.7", - "@types/node": "^22.0.0", - "@typescript-eslint/eslint-plugin": "^7.17.0", - "@typescript-eslint/parser": "^7.17.0", + "@types/node": "^22.4.2", + "@typescript-eslint/eslint-plugin": "^8.2.0", + "@typescript-eslint/parser": "^8.2.0", "chai": "^5.1.1", "eslint": "^8.57.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-require-extensions": "^0.1.1", "gh-pages": "^6.1.1", - "mocha": "^10.7.0", - "prettier": "^3.3.3", + "mocha": "^10.7.3", "shx": "^0.3.4", "ts-node": "^10.9.2", - "typedoc": "^0.26.5", + "typedoc": "^0.26.6", "typescript": "^5.5.4" } } diff --git a/libraries/type-length-value/js/test/splDiscriminate.test.ts b/libraries/type-length-value/js/test/splDiscriminate.test.ts index 0dd8616ddff..789a26f15c0 100644 --- a/libraries/type-length-value/js/test/splDiscriminate.test.ts +++ b/libraries/type-length-value/js/test/splDiscriminate.test.ts @@ -10,7 +10,7 @@ describe('splDiscrimintor', () => { 'test-namespace:this-is-a-test:with-a-longer-name', ]; - const testExpectedBytes = testVectors.map((x) => { + const testExpectedBytes = testVectors.map(x => { return createHash('sha256').update(x).digest(); }); diff --git a/libraries/type-length-value/js/test/tlvData.test.ts b/libraries/type-length-value/js/test/tlvData.test.ts index 2ac06aff7e9..d43604ff37b 100644 --- a/libraries/type-length-value/js/test/tlvData.test.ts +++ b/libraries/type-length-value/js/test/tlvData.test.ts @@ -92,7 +92,7 @@ describe('tlvData', () => { type[0] = 2; expect(tlv.firstBytes(type)).to.be.deep.equal(Buffer.from([1, 2])); type[0] = 3; - expect(tlv.firstBytes(type)).to.be.null; + expect(tlv.firstBytes(type)).to.equal(null); }; it('should get the entries individually', () => { diff --git a/libraries/type-length-value/src/error.rs b/libraries/type-length-value/src/error.rs index 48e5d9efa34..583810e2b40 100644 --- a/libraries/type-length-value/src/error.rs +++ b/libraries/type-length-value/src/error.rs @@ -3,7 +3,10 @@ use spl_program_error::*; /// Errors that may be returned by the Token program. -#[spl_program_error(hash_error_code_start = 1_202_666_432)] +#[spl_program_error( + hash_error_code_start = 1_202_666_432, + solana_program = "solana_program" +)] pub enum TlvError { /// Type not found in TLV data #[error("Type not found in TLV data")] diff --git a/managed-token/sdk/.solitarc.js b/managed-token/sdk/.solitarc.js index 899a10ebde8..c759620a057 100644 --- a/managed-token/sdk/.solitarc.js +++ b/managed-token/sdk/.solitarc.js @@ -1,14 +1,14 @@ -const path = require("path"); -const programDir = path.join(__dirname, "..", "program"); -const idlDir = path.join(__dirname, "idl"); -const sdkDir = path.join(__dirname, "src", "generated"); -const binaryInstallDir = path.join(__dirname, "..", "..", "target", "solita"); +const path = require('path'); +const programDir = path.join(__dirname, '..', 'program'); +const idlDir = path.join(__dirname, 'idl'); +const sdkDir = path.join(__dirname, 'src', 'generated'); +const binaryInstallDir = path.join(__dirname, '..', '..', 'target', 'solita'); module.exports = { - idlGenerator: "shank", - programName: "spl_managed_token", - idlDir, - sdkDir, - binaryInstallDir, - programDir, + idlGenerator: 'shank', + programName: 'spl_managed_token', + idlDir, + sdkDir, + binaryInstallDir, + programDir, }; diff --git a/memo/js/.prettierignore b/memo/js/.prettierignore deleted file mode 100644 index 6da325effab..00000000000 --- a/memo/js/.prettierignore +++ /dev/null @@ -1,5 +0,0 @@ -docs -lib -test-ledger - -package-lock.json diff --git a/memo/js/.prettierrc b/memo/js/.prettierrc deleted file mode 100644 index b9ce4c1923a..00000000000 --- a/memo/js/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "printWidth": 120, - "trailingComma": "es5", - "tabWidth": 4, - "semi": true, - "singleQuote": true -} \ No newline at end of file diff --git a/memo/js/package.json b/memo/js/package.json index eccd11807e8..18e64c0bc70 100644 --- a/memo/js/package.json +++ b/memo/js/package.json @@ -35,9 +35,8 @@ "postbuild": "shx echo '{ \"type\": \"commonjs\" }' > lib/cjs/package.json", "watch": "tsc --build --verbose --watch tsconfig.all.json", "release": "npm run clean && npm run build", - "fmt": "prettier --write '{*,**/*}.{ts,tsx,js,jsx,json}'", - "lint": "prettier --check '{*,**/*}.{ts,tsx,js,jsx,json}' && eslint --max-warnings 0 .", - "lint:fix": "npm run fmt && eslint --fix .", + "lint": "eslint --max-warnings 0 .", + "lint:fix": "eslint --fix .", "test": "npm run test:unit && npm run test:e2e", "test:unit": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest test/unit", "test:e2e": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" start-server-and-test 'solana-test-validator -r -q' http://127.0.0.1:8899/health 'jest test/e2e'", @@ -46,33 +45,30 @@ "deploy:docs": "npm run docs && gh-pages --dest memo/js --dist docs --dotfiles" }, "peerDependencies": { - "@solana/web3.js": "^1.95.1" + "@solana/web3.js": "^1.95.3" }, "dependencies": { "buffer": "^6.0.3" }, "devDependencies": { - "@solana/web3.js": "^1.95.1", - "@types/chai": "^4.3.16", + "@solana/web3.js": "^1.95.3", + "@types/chai": "^4.3.17", "@types/jest": "^29.5.12", - "@types/node": "^22.0.0", + "@types/node": "^22.4.2", "@types/node-fetch": "^2.6.11", - "@typescript-eslint/eslint-plugin": "^7.17.0", - "@typescript-eslint/parser": "^7.17.0", + "@typescript-eslint/eslint-plugin": "^8.2.0", + "@typescript-eslint/parser": "^8.2.0", "chai": "^5.1.1", "eslint": "^8.57.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-require-extensions": "^0.1.1", "gh-pages": "^6.1.1", "jest": "^29.0.0", - "prettier": "^3.3.3", "process": "^0.11.10", "shx": "^0.3.4", "start-server-and-test": "^2.0.5", - "ts-jest": "^29.2.3", + "ts-jest": "^29.2.4", "ts-node": "^10.9.2", - "typedoc": "^0.26.5", + "typedoc": "^0.26.6", "typescript": "^5.5.4" } } diff --git a/name-service/js/.prettierignore b/name-service/js/.prettierignore deleted file mode 100644 index 6da325effab..00000000000 --- a/name-service/js/.prettierignore +++ /dev/null @@ -1,5 +0,0 @@ -docs -lib -test-ledger - -package-lock.json diff --git a/name-service/js/jest.config.ts b/name-service/js/jest.config.ts index 56a444ea2d5..586fb9fe3d1 100644 --- a/name-service/js/jest.config.ts +++ b/name-service/js/jest.config.ts @@ -1,7 +1,7 @@ import type { JestConfigWithTsJest } from 'ts-jest'; const jestConfig: JestConfigWithTsJest = { - preset: 'ts-jest/presets/default-esm', + preset: 'ts-jest/presets/default-esm', }; export default jestConfig; diff --git a/name-service/js/package.json b/name-service/js/package.json index 5f86bb3383b..b84c982138d 100644 --- a/name-service/js/package.json +++ b/name-service/js/package.json @@ -34,40 +34,33 @@ "build": "tsc --build --verbose tsconfig.all.json", "build:program": "cargo build-sbf --manifest-path=../program/Cargo.toml", "prepublish": "tsc", - "lint": "npm run pretty && eslint --max-warnings 0 'src/*.ts'", - "lint:fix": "npm run pretty:fix && eslint 'src/*.ts' --fix", - "pretty": "prettier --check '{*,**/*}.{ts,tsx,js,jsx,json}'", - "pretty:fix": "prettier --write '{*,**/*}.{ts,tsx,js,jsx,json}'", + "lint": "eslint --max-warnings 0 'src/*.ts'", + "lint:fix": "eslint 'src/*.ts' --fix", "doc": "npm run typedoc src/index.ts", "test": "npm run test:unit && npm run test:e2e", "test:unit": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest test/unit", "test:e2e": "NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" start-server-and-test 'solana-test-validator --bpf-program namesLPneVptA9Z5rqUDD9tMTWEJwofgaYwp8cawRkX ../../target/deploy/spl_name_service.so --reset --quiet' http://127.0.0.1:8899/health 'jest test/e2e'" }, - "prettier": { - "singleQuote": true - }, "devDependencies": { "@jest/globals": "^29.7.0", "@types/bn.js": "^5.1.1", "@types/jest": "^29.5.12", - "@types/node": "^22.0.0", - "@typescript-eslint/eslint-plugin": "^7.17.0", - "@typescript-eslint/parser": "^7.17.0", + "@types/node": "^22.4.2", + "@typescript-eslint/eslint-plugin": "^8.2.0", + "@typescript-eslint/parser": "^8.2.0", "eslint": "^8.57.0", - "eslint-config-prettier": "^9.1.0", "eslint-plugin-eslint-comments": "^3.2.0", - "eslint-plugin-functional": "^6.6.3", + "eslint-plugin-functional": "^7.0.1", "eslint-plugin-import": "^2.29.1", "jest": "^29.7.0", - "prettier": "^3.3.3", "start-server-and-test": "^2.0.5", - "ts-jest": "^29.2.3", + "ts-jest": "^29.2.4", "ts-node": "^10.9.2", - "typedoc": "^0.26.5", + "typedoc": "^0.26.6", "typescript": "^5.5.4" }, "dependencies": { - "@solana/web3.js": "^1.95.1", + "@solana/web3.js": "^1.95.3", "bn.js": "^5.1.3", "borsh": "^2.0.0" } diff --git a/name-service/js/src/bindings.ts b/name-service/js/src/bindings.ts index b0f9a095b84..b2377c51967 100644 --- a/name-service/js/src/bindings.ts +++ b/name-service/js/src/bindings.ts @@ -1,31 +1,19 @@ -import { - Connection, - PublicKey, - SystemProgram, - TransactionInstruction, -} from '@solana/web3.js'; +import { Connection, PublicKey, SystemProgram, TransactionInstruction } from '@solana/web3.js'; import { - createInstruction, - deleteInstruction, - reallocInstruction, - transferInstruction, - updateInstruction, + createInstruction, + deleteInstruction, + reallocInstruction, + transferInstruction, + updateInstruction, } from './instructions'; import { NameRegistryState } from './state'; import { Numberu64 } from './utils'; -import { - getHashedName, - getNameAccountKey, - getNameOwner, - Numberu32, -} from './utils'; +import { getHashedName, getNameAccountKey, getNameOwner, Numberu32 } from './utils'; //////////////////////////////////////////////////////////// -export const NAME_PROGRAM_ID = new PublicKey( - 'namesLPneVptA9Z5rqUDD9tMTWEJwofgaYwp8cawRkX', -); +export const NAME_PROGRAM_ID = new PublicKey('namesLPneVptA9Z5rqUDD9tMTWEJwofgaYwp8cawRkX'); export const HASH_PREFIX = 'SPL Name Service'; //////////////////////////////////////////////////////////// @@ -43,47 +31,41 @@ export const HASH_PREFIX = 'SPL Name Service'; * @returns */ export async function createNameRegistry( - connection: Connection, - name: string, - space: number, - payerKey: PublicKey, - nameOwner: PublicKey, - lamports?: number, - nameClass?: PublicKey, - parentName?: PublicKey, + connection: Connection, + name: string, + space: number, + payerKey: PublicKey, + nameOwner: PublicKey, + lamports?: number, + nameClass?: PublicKey, + parentName?: PublicKey, ): Promise { - const hashed_name = await getHashedName(name); - const nameAccountKey = await getNameAccountKey( - hashed_name, - nameClass, - parentName, - ); - - const balance = lamports - ? lamports - : await connection.getMinimumBalanceForRentExemption(space); - - let nameParentOwner: PublicKey | undefined; - if (parentName) { - const parentAccount = await getNameOwner(connection, parentName); - nameParentOwner = parentAccount.owner; - } - - const createNameInstr = createInstruction( - NAME_PROGRAM_ID, - SystemProgram.programId, - nameAccountKey, - nameOwner, - payerKey, - hashed_name, - new Numberu64(balance), - new Numberu32(space), - nameClass, - parentName, - nameParentOwner, - ); - - return createNameInstr; + const hashed_name = await getHashedName(name); + const nameAccountKey = await getNameAccountKey(hashed_name, nameClass, parentName); + + const balance = lamports ? lamports : await connection.getMinimumBalanceForRentExemption(space); + + let nameParentOwner: PublicKey | undefined; + if (parentName) { + const parentAccount = await getNameOwner(connection, parentName); + nameParentOwner = parentAccount.owner; + } + + const createNameInstr = createInstruction( + NAME_PROGRAM_ID, + SystemProgram.programId, + nameAccountKey, + nameOwner, + payerKey, + hashed_name, + new Numberu64(balance), + new Numberu32(space), + nameClass, + parentName, + nameParentOwner, + ); + + return createNameInstr; } /** @@ -97,38 +79,33 @@ export async function createNameRegistry( * @param nameParent The parent name of this name, if it exists */ export async function updateNameRegistryData( - connection: Connection, - name: string, - offset: number, - input_data: Buffer, - nameClass?: PublicKey, - nameParent?: PublicKey, + connection: Connection, + name: string, + offset: number, + input_data: Buffer, + nameClass?: PublicKey, + nameParent?: PublicKey, ): Promise { - const hashed_name = await getHashedName(name); - const nameAccountKey = await getNameAccountKey( - hashed_name, - nameClass, - nameParent, - ); - - let signer: PublicKey; - if (nameClass) { - signer = nameClass; - } else { - signer = (await NameRegistryState.retrieve(connection, nameAccountKey)) - .owner; - } - - const updateInstr = updateInstruction( - NAME_PROGRAM_ID, - nameAccountKey, - new Numberu32(offset), - input_data, - signer, - nameParent, - ); - - return updateInstr; + const hashed_name = await getHashedName(name); + const nameAccountKey = await getNameAccountKey(hashed_name, nameClass, nameParent); + + let signer: PublicKey; + if (nameClass) { + signer = nameClass; + } else { + signer = (await NameRegistryState.retrieve(connection, nameAccountKey)).owner; + } + + const updateInstr = updateInstruction( + NAME_PROGRAM_ID, + nameAccountKey, + new Numberu32(offset), + input_data, + signer, + nameParent, + ); + + return updateInstr; } /** @@ -143,38 +120,32 @@ export async function updateNameRegistryData( * @returns */ export async function transferNameOwnership( - connection: Connection, - name: string, - newOwner: PublicKey, - nameClass?: PublicKey, - nameParent?: PublicKey, + connection: Connection, + name: string, + newOwner: PublicKey, + nameClass?: PublicKey, + nameParent?: PublicKey, ): Promise { - const hashed_name = await getHashedName(name); - const nameAccountKey = await getNameAccountKey( - hashed_name, - nameClass, - nameParent, - ); - - let curentNameOwner: PublicKey; - if (nameClass) { - curentNameOwner = nameClass; - } else { - curentNameOwner = ( - await NameRegistryState.retrieve(connection, nameAccountKey) - ).owner; - } - - const transferInstr = transferInstruction( - NAME_PROGRAM_ID, - nameAccountKey, - newOwner, - curentNameOwner, - nameClass, - nameParent, - ); - - return transferInstr; + const hashed_name = await getHashedName(name); + const nameAccountKey = await getNameAccountKey(hashed_name, nameClass, nameParent); + + let curentNameOwner: PublicKey; + if (nameClass) { + curentNameOwner = nameClass; + } else { + curentNameOwner = (await NameRegistryState.retrieve(connection, nameAccountKey)).owner; + } + + const transferInstr = transferInstruction( + NAME_PROGRAM_ID, + nameAccountKey, + newOwner, + curentNameOwner, + nameClass, + nameParent, + ); + + return transferInstr; } /** @@ -188,35 +159,25 @@ export async function transferNameOwnership( * @returns */ export async function deleteNameRegistry( - connection: Connection, - name: string, - refundTargetKey: PublicKey, - nameClass?: PublicKey, - nameParent?: PublicKey, + connection: Connection, + name: string, + refundTargetKey: PublicKey, + nameClass?: PublicKey, + nameParent?: PublicKey, ): Promise { - const hashed_name = await getHashedName(name); - const nameAccountKey = await getNameAccountKey( - hashed_name, - nameClass, - nameParent, - ); + const hashed_name = await getHashedName(name); + const nameAccountKey = await getNameAccountKey(hashed_name, nameClass, nameParent); - let nameOwner: PublicKey; - if (nameClass) { - nameOwner = nameClass; - } else { - nameOwner = (await NameRegistryState.retrieve(connection, nameAccountKey)) - .owner; - } + let nameOwner: PublicKey; + if (nameClass) { + nameOwner = nameClass; + } else { + nameOwner = (await NameRegistryState.retrieve(connection, nameAccountKey)).owner; + } - const changeAuthoritiesInstr = deleteInstruction( - NAME_PROGRAM_ID, - nameAccountKey, - refundTargetKey, - nameOwner, - ); + const changeAuthoritiesInstr = deleteInstruction(NAME_PROGRAM_ID, nameAccountKey, refundTargetKey, nameOwner); - return changeAuthoritiesInstr; + return changeAuthoritiesInstr; } /** @@ -231,36 +192,31 @@ export async function deleteNameRegistry( * @returns */ export async function reallocNameAccount( - connection: Connection, - name: string, - space: number, - payerKey: PublicKey, - nameClass?: PublicKey, - nameParent?: PublicKey, + connection: Connection, + name: string, + space: number, + payerKey: PublicKey, + nameClass?: PublicKey, + nameParent?: PublicKey, ): Promise { - const hashedName = await getHashedName(name); - const nameAccountKey = await getNameAccountKey( - hashedName, - nameClass, - nameParent, - ); - - let nameOwner: PublicKey; - if (nameClass) { - nameOwner = nameClass; - } else { - nameOwner = (await NameRegistryState.retrieve(connection, nameAccountKey)) - .owner; - } - - const reallocInstr = reallocInstruction( - NAME_PROGRAM_ID, - SystemProgram.programId, - payerKey, - nameAccountKey, - nameOwner, - new Numberu32(space), - ); - - return reallocInstr; + const hashedName = await getHashedName(name); + const nameAccountKey = await getNameAccountKey(hashedName, nameClass, nameParent); + + let nameOwner: PublicKey; + if (nameClass) { + nameOwner = nameClass; + } else { + nameOwner = (await NameRegistryState.retrieve(connection, nameAccountKey)).owner; + } + + const reallocInstr = reallocInstruction( + NAME_PROGRAM_ID, + SystemProgram.programId, + payerKey, + nameAccountKey, + nameOwner, + new Numberu32(space), + ); + + return reallocInstr; } diff --git a/name-service/js/src/instructions.ts b/name-service/js/src/instructions.ts index 9d48d60bb0f..7e729cc32ed 100644 --- a/name-service/js/src/instructions.ts +++ b/name-service/js/src/instructions.ts @@ -3,255 +3,255 @@ import { PublicKey, TransactionInstruction } from '@solana/web3.js'; import { Numberu32, Numberu64 } from './utils'; export function createInstruction( - nameProgramId: PublicKey, - systemProgramId: PublicKey, - nameKey: PublicKey, - nameOwnerKey: PublicKey, - payerKey: PublicKey, - hashed_name: Buffer, - lamports: Numberu64, - space: Numberu32, - nameClassKey?: PublicKey, - nameParent?: PublicKey, - nameParentOwner?: PublicKey, + nameProgramId: PublicKey, + systemProgramId: PublicKey, + nameKey: PublicKey, + nameOwnerKey: PublicKey, + payerKey: PublicKey, + hashed_name: Buffer, + lamports: Numberu64, + space: Numberu32, + nameClassKey?: PublicKey, + nameParent?: PublicKey, + nameParentOwner?: PublicKey, ): TransactionInstruction { - const buffers = [ - Buffer.from(Int8Array.from([0])), - new Numberu32(hashed_name.length).toBuffer(), - hashed_name, - lamports.toBuffer(), - space.toBuffer(), - ]; + const buffers = [ + Buffer.from(Int8Array.from([0])), + new Numberu32(hashed_name.length).toBuffer(), + hashed_name, + lamports.toBuffer(), + space.toBuffer(), + ]; - const data = Buffer.concat(buffers); + const data = Buffer.concat(buffers); - const keys = [ - { - pubkey: systemProgramId, - isSigner: false, - isWritable: false, - }, - { - pubkey: payerKey, - isSigner: true, - isWritable: true, - }, - { - pubkey: nameKey, - isSigner: false, - isWritable: true, - }, - { - pubkey: nameOwnerKey, - isSigner: false, - isWritable: false, - }, - ]; + const keys = [ + { + pubkey: systemProgramId, + isSigner: false, + isWritable: false, + }, + { + pubkey: payerKey, + isSigner: true, + isWritable: true, + }, + { + pubkey: nameKey, + isSigner: false, + isWritable: true, + }, + { + pubkey: nameOwnerKey, + isSigner: false, + isWritable: false, + }, + ]; - if (nameClassKey) { - keys.push({ - pubkey: nameClassKey, - isSigner: true, - isWritable: false, - }); - } else { - keys.push({ - pubkey: new PublicKey(Buffer.alloc(32)), - isSigner: false, - isWritable: false, - }); - } - if (nameParent) { - keys.push({ - pubkey: nameParent, - isSigner: false, - isWritable: false, - }); - } else { - keys.push({ - pubkey: new PublicKey(Buffer.alloc(32)), - isSigner: false, - isWritable: false, - }); - } - if (nameParentOwner) { - keys.push({ - pubkey: nameParentOwner, - isSigner: true, - isWritable: false, - }); - } + if (nameClassKey) { + keys.push({ + pubkey: nameClassKey, + isSigner: true, + isWritable: false, + }); + } else { + keys.push({ + pubkey: new PublicKey(Buffer.alloc(32)), + isSigner: false, + isWritable: false, + }); + } + if (nameParent) { + keys.push({ + pubkey: nameParent, + isSigner: false, + isWritable: false, + }); + } else { + keys.push({ + pubkey: new PublicKey(Buffer.alloc(32)), + isSigner: false, + isWritable: false, + }); + } + if (nameParentOwner) { + keys.push({ + pubkey: nameParentOwner, + isSigner: true, + isWritable: false, + }); + } - return new TransactionInstruction({ - keys, - programId: nameProgramId, - data, - }); + return new TransactionInstruction({ + keys, + programId: nameProgramId, + data, + }); } export function updateInstruction( - nameProgramId: PublicKey, - nameAccountKey: PublicKey, - offset: Numberu32, - input_data: Buffer, - nameUpdateSigner: PublicKey, - parentNameKey: PublicKey | undefined, + nameProgramId: PublicKey, + nameAccountKey: PublicKey, + offset: Numberu32, + input_data: Buffer, + nameUpdateSigner: PublicKey, + parentNameKey: PublicKey | undefined, ): TransactionInstruction { - const buffers = [ - Buffer.from(Int8Array.from([1])), - offset.toBuffer(), - new Numberu32(input_data.length).toBuffer(), - input_data, - ]; + const buffers = [ + Buffer.from(Int8Array.from([1])), + offset.toBuffer(), + new Numberu32(input_data.length).toBuffer(), + input_data, + ]; - const data = Buffer.concat(buffers); - const keys = [ - { - pubkey: nameAccountKey, - isSigner: false, - isWritable: true, - }, - { - pubkey: nameUpdateSigner, - isSigner: true, - isWritable: false, - }, - ]; + const data = Buffer.concat(buffers); + const keys = [ + { + pubkey: nameAccountKey, + isSigner: false, + isWritable: true, + }, + { + pubkey: nameUpdateSigner, + isSigner: true, + isWritable: false, + }, + ]; - if (parentNameKey) { - keys.push({ - pubkey: parentNameKey, - isSigner: false, - isWritable: false, - }); - } + if (parentNameKey) { + keys.push({ + pubkey: parentNameKey, + isSigner: false, + isWritable: false, + }); + } - return new TransactionInstruction({ - keys, - programId: nameProgramId, - data, - }); + return new TransactionInstruction({ + keys, + programId: nameProgramId, + data, + }); } export function transferInstruction( - nameProgramId: PublicKey, - nameAccountKey: PublicKey, - newOwnerKey: PublicKey, - currentNameOwnerKey: PublicKey, - nameClassKey?: PublicKey, - nameParent?: PublicKey, + nameProgramId: PublicKey, + nameAccountKey: PublicKey, + newOwnerKey: PublicKey, + currentNameOwnerKey: PublicKey, + nameClassKey?: PublicKey, + nameParent?: PublicKey, ): TransactionInstruction { - const buffers = [Buffer.from(Int8Array.from([2])), newOwnerKey.toBuffer()]; + const buffers = [Buffer.from(Int8Array.from([2])), newOwnerKey.toBuffer()]; - const data = Buffer.concat(buffers); + const data = Buffer.concat(buffers); - const keys = [ - { - pubkey: nameAccountKey, - isSigner: false, - isWritable: true, - }, - { - pubkey: currentNameOwnerKey, - isSigner: true, - isWritable: false, - }, - ]; + const keys = [ + { + pubkey: nameAccountKey, + isSigner: false, + isWritable: true, + }, + { + pubkey: currentNameOwnerKey, + isSigner: true, + isWritable: false, + }, + ]; - if (nameClassKey) { - keys.push({ - pubkey: nameClassKey, - isSigner: true, - isWritable: false, - }); - } + if (nameClassKey) { + keys.push({ + pubkey: nameClassKey, + isSigner: true, + isWritable: false, + }); + } - if (nameParent) { - keys.push({ - pubkey: nameParent, - isSigner: false, - isWritable: false, - }); - } + if (nameParent) { + keys.push({ + pubkey: nameParent, + isSigner: false, + isWritable: false, + }); + } - return new TransactionInstruction({ - keys, - programId: nameProgramId, - data, - }); + return new TransactionInstruction({ + keys, + programId: nameProgramId, + data, + }); } export function deleteInstruction( - nameProgramId: PublicKey, - nameAccountKey: PublicKey, - refundTargetKey: PublicKey, - nameOwnerKey: PublicKey, + nameProgramId: PublicKey, + nameAccountKey: PublicKey, + refundTargetKey: PublicKey, + nameOwnerKey: PublicKey, ): TransactionInstruction { - const buffers = [Buffer.from(Int8Array.from([3]))]; + const buffers = [Buffer.from(Int8Array.from([3]))]; - const data = Buffer.concat(buffers); - const keys = [ - { - pubkey: nameAccountKey, - isSigner: false, - isWritable: true, - }, - { - pubkey: nameOwnerKey, - isSigner: true, - isWritable: false, - }, - { - pubkey: refundTargetKey, - isSigner: false, - isWritable: true, - }, - ]; + const data = Buffer.concat(buffers); + const keys = [ + { + pubkey: nameAccountKey, + isSigner: false, + isWritable: true, + }, + { + pubkey: nameOwnerKey, + isSigner: true, + isWritable: false, + }, + { + pubkey: refundTargetKey, + isSigner: false, + isWritable: true, + }, + ]; - return new TransactionInstruction({ - keys, - programId: nameProgramId, - data, - }); + return new TransactionInstruction({ + keys, + programId: nameProgramId, + data, + }); } export function reallocInstruction( - nameProgramId: PublicKey, - systemProgramId: PublicKey, - payerKey: PublicKey, - nameAccountKey: PublicKey, - nameOwnerKey: PublicKey, - space: Numberu32, + nameProgramId: PublicKey, + systemProgramId: PublicKey, + payerKey: PublicKey, + nameAccountKey: PublicKey, + nameOwnerKey: PublicKey, + space: Numberu32, ): TransactionInstruction { - const buffers = [Buffer.from(Int8Array.from([4])), space.toBuffer()]; + const buffers = [Buffer.from(Int8Array.from([4])), space.toBuffer()]; - const data = Buffer.concat(buffers); - const keys = [ - { - pubkey: systemProgramId, - isSigner: false, - isWritable: false, - }, - { - pubkey: payerKey, - isSigner: true, - isWritable: true, - }, - { - pubkey: nameAccountKey, - isSigner: false, - isWritable: true, - }, - { - pubkey: nameOwnerKey, - isSigner: true, - isWritable: false, - }, - ]; + const data = Buffer.concat(buffers); + const keys = [ + { + pubkey: systemProgramId, + isSigner: false, + isWritable: false, + }, + { + pubkey: payerKey, + isSigner: true, + isWritable: true, + }, + { + pubkey: nameAccountKey, + isSigner: false, + isWritable: true, + }, + { + pubkey: nameOwnerKey, + isSigner: true, + isWritable: false, + }, + ]; - return new TransactionInstruction({ - keys, - programId: nameProgramId, - data, - }); + return new TransactionInstruction({ + keys, + programId: nameProgramId, + data, + }); } diff --git a/name-service/js/src/state.ts b/name-service/js/src/state.ts index 88b72809018..e7b6c306508 100644 --- a/name-service/js/src/state.ts +++ b/name-service/js/src/state.ts @@ -2,48 +2,42 @@ import { Connection, PublicKey } from '@solana/web3.js'; import { deserialize, Schema } from 'borsh'; type InitArgs = { - parentName: Uint8Array; - owner: Uint8Array; - class: Uint8Array; + parentName: Uint8Array; + owner: Uint8Array; + class: Uint8Array; }; export class NameRegistryState { - static HEADER_LEN = 96; - parentName: PublicKey; - owner: PublicKey; - class: PublicKey; - data: Buffer | undefined; + static HEADER_LEN = 96; + parentName: PublicKey; + owner: PublicKey; + class: PublicKey; + data: Buffer | undefined; - static schema: Schema = { - struct: { - parentName: { array: { type: 'u8', len: 32 } }, - owner: { array: { type: 'u8', len: 32 } }, - class: { array: { type: 'u8', len: 32 } }, - }, - }; - constructor(obj: InitArgs) { - this.parentName = new PublicKey(obj.parentName); - this.owner = new PublicKey(obj.owner); - this.class = new PublicKey(obj.class); - } - - public static async retrieve( - connection: Connection, - nameAccountKey: PublicKey, - ): Promise { - const nameAccount = await connection.getAccountInfo( - nameAccountKey, - 'processed', - ); - if (!nameAccount) { - throw new Error('Invalid name account provided'); + static schema: Schema = { + struct: { + parentName: { array: { type: 'u8', len: 32 } }, + owner: { array: { type: 'u8', len: 32 } }, + class: { array: { type: 'u8', len: 32 } }, + }, + }; + constructor(obj: InitArgs) { + this.parentName = new PublicKey(obj.parentName); + this.owner = new PublicKey(obj.owner); + this.class = new PublicKey(obj.class); } - const deserialized = deserialize(this.schema, nameAccount.data) as InitArgs; - const res = new NameRegistryState(deserialized); + public static async retrieve(connection: Connection, nameAccountKey: PublicKey): Promise { + const nameAccount = await connection.getAccountInfo(nameAccountKey, 'processed'); + if (!nameAccount) { + throw new Error('Invalid name account provided'); + } + + const deserialized = deserialize(this.schema, nameAccount.data) as InitArgs; + const res = new NameRegistryState(deserialized); - res.data = nameAccount.data?.slice(this.HEADER_LEN); + res.data = nameAccount.data?.slice(this.HEADER_LEN); - return res; - } + return res; + } } diff --git a/name-service/js/src/twitter.ts b/name-service/js/src/twitter.ts index f0a420dbc3a..05f3a212a23 100644 --- a/name-service/js/src/twitter.ts +++ b/name-service/js/src/twitter.ts @@ -1,433 +1,362 @@ -import { - Connection, - PublicKey, - SystemProgram, - TransactionInstruction, -} from '@solana/web3.js'; +import { Connection, PublicKey, SystemProgram, TransactionInstruction } from '@solana/web3.js'; import { deserialize, Schema, serialize } from 'borsh'; import { deleteNameRegistry, NAME_PROGRAM_ID } from './bindings'; -import { - createInstruction, - deleteInstruction, - transferInstruction, - updateInstruction, -} from './instructions'; +import { createInstruction, deleteInstruction, transferInstruction, updateInstruction } from './instructions'; import { NameRegistryState } from './state'; -import { - getFilteredProgramAccounts, - getHashedName, - getNameAccountKey, - Numberu32, - Numberu64, -} from './utils'; +import { getFilteredProgramAccounts, getHashedName, getNameAccountKey, Numberu32, Numberu64 } from './utils'; //////////////////////////////////////////////////// // Global Variables -export const TWITTER_VERIFICATION_AUTHORITY = new PublicKey( - 'FvPH7PrVrLGKPfqaf3xJodFTjZriqrAXXLTVWEorTFBi', -); +export const TWITTER_VERIFICATION_AUTHORITY = new PublicKey('FvPH7PrVrLGKPfqaf3xJodFTjZriqrAXXLTVWEorTFBi'); // The address of the name registry that will be a parent to all twitter handle registries, // it should be owned by the TWITTER_VERIFICATION_AUTHORITY and its name is irrelevant -export const TWITTER_ROOT_PARENT_REGISTRY_KEY = new PublicKey( - '4YcexoW3r78zz16J2aqmukBLRwGq6rAvWzJpkYAXqebv', -); +export const TWITTER_ROOT_PARENT_REGISTRY_KEY = new PublicKey('4YcexoW3r78zz16J2aqmukBLRwGq6rAvWzJpkYAXqebv'); //////////////////////////////////////////////////// // Bindings // Signed by the authority, the payer and the verified pubkey export async function createVerifiedTwitterRegistry( - connection: Connection, - twitterHandle: string, - verifiedPubkey: PublicKey, - space: number, // The space that the user will have to write data into the verified registry - payerKey: PublicKey, + connection: Connection, + twitterHandle: string, + verifiedPubkey: PublicKey, + space: number, // The space that the user will have to write data into the verified registry + payerKey: PublicKey, ): Promise { - // Create user facing registry - const hashedTwitterHandle = await getHashedName(twitterHandle); - const twitterHandleRegistryKey = await getNameAccountKey( - hashedTwitterHandle, - undefined, - TWITTER_ROOT_PARENT_REGISTRY_KEY, - ); - - let instructions = [ - createInstruction( - NAME_PROGRAM_ID, - SystemProgram.programId, - twitterHandleRegistryKey, - verifiedPubkey, - payerKey, - hashedTwitterHandle, - new Numberu64(await connection.getMinimumBalanceForRentExemption(space)), - new Numberu32(space), - undefined, - TWITTER_ROOT_PARENT_REGISTRY_KEY, - TWITTER_VERIFICATION_AUTHORITY, // Twitter authority acts as owner of the parent for all user-facing registries - ), - ]; - - instructions = instructions.concat( - await createReverseTwitterRegistry( - connection, - twitterHandle, - twitterHandleRegistryKey, - verifiedPubkey, - payerKey, - ), - ); - - return instructions; + // Create user facing registry + const hashedTwitterHandle = await getHashedName(twitterHandle); + const twitterHandleRegistryKey = await getNameAccountKey( + hashedTwitterHandle, + undefined, + TWITTER_ROOT_PARENT_REGISTRY_KEY, + ); + + let instructions = [ + createInstruction( + NAME_PROGRAM_ID, + SystemProgram.programId, + twitterHandleRegistryKey, + verifiedPubkey, + payerKey, + hashedTwitterHandle, + new Numberu64(await connection.getMinimumBalanceForRentExemption(space)), + new Numberu32(space), + undefined, + TWITTER_ROOT_PARENT_REGISTRY_KEY, + TWITTER_VERIFICATION_AUTHORITY, // Twitter authority acts as owner of the parent for all user-facing registries + ), + ]; + + instructions = instructions.concat( + await createReverseTwitterRegistry( + connection, + twitterHandle, + twitterHandleRegistryKey, + verifiedPubkey, + payerKey, + ), + ); + + return instructions; } // Overwrite the data that is written in the user facing registry // Signed by the verified pubkey export async function changeTwitterRegistryData( - twitterHandle: string, - verifiedPubkey: PublicKey, - offset: number, // The offset at which to write the input data into the NameRegistryData - input_data: Buffer, + twitterHandle: string, + verifiedPubkey: PublicKey, + offset: number, // The offset at which to write the input data into the NameRegistryData + input_data: Buffer, ): Promise { - const hashedTwitterHandle = await getHashedName(twitterHandle); - const twitterHandleRegistryKey = await getNameAccountKey( - hashedTwitterHandle, - undefined, - TWITTER_ROOT_PARENT_REGISTRY_KEY, - ); - - const instructions = [ - updateInstruction( - NAME_PROGRAM_ID, - twitterHandleRegistryKey, - new Numberu32(offset), - input_data, - verifiedPubkey, - undefined, - ), - ]; - - return instructions; + const hashedTwitterHandle = await getHashedName(twitterHandle); + const twitterHandleRegistryKey = await getNameAccountKey( + hashedTwitterHandle, + undefined, + TWITTER_ROOT_PARENT_REGISTRY_KEY, + ); + + const instructions = [ + updateInstruction( + NAME_PROGRAM_ID, + twitterHandleRegistryKey, + new Numberu32(offset), + input_data, + verifiedPubkey, + undefined, + ), + ]; + + return instructions; } // Change the verified pubkey for a given twitter handle // Signed by the Authority, the verified pubkey and the payer export async function changeVerifiedPubkey( - connection: Connection, - twitterHandle: string, - currentVerifiedPubkey: PublicKey, - newVerifiedPubkey: PublicKey, - payerKey: PublicKey, + connection: Connection, + twitterHandle: string, + currentVerifiedPubkey: PublicKey, + newVerifiedPubkey: PublicKey, + payerKey: PublicKey, ): Promise { - const hashedTwitterHandle = await getHashedName(twitterHandle); - const twitterHandleRegistryKey = await getNameAccountKey( - hashedTwitterHandle, - undefined, - TWITTER_ROOT_PARENT_REGISTRY_KEY, - ); - - // Transfer the user-facing registry ownership - let instructions = [ - transferInstruction( - NAME_PROGRAM_ID, - twitterHandleRegistryKey, - newVerifiedPubkey, - currentVerifiedPubkey, - undefined, - ), - ]; - - // Delete the current reverse registry - instructions.push( - await deleteNameRegistry( - connection, - currentVerifiedPubkey.toString(), - payerKey, - TWITTER_VERIFICATION_AUTHORITY, - TWITTER_ROOT_PARENT_REGISTRY_KEY, - ), - ); - - // Create the new reverse registry - instructions = instructions.concat( - await createReverseTwitterRegistry( - connection, - twitterHandle, - twitterHandleRegistryKey, - newVerifiedPubkey, - payerKey, - ), - ); - - return instructions; + const hashedTwitterHandle = await getHashedName(twitterHandle); + const twitterHandleRegistryKey = await getNameAccountKey( + hashedTwitterHandle, + undefined, + TWITTER_ROOT_PARENT_REGISTRY_KEY, + ); + + // Transfer the user-facing registry ownership + let instructions = [ + transferInstruction( + NAME_PROGRAM_ID, + twitterHandleRegistryKey, + newVerifiedPubkey, + currentVerifiedPubkey, + undefined, + ), + ]; + + // Delete the current reverse registry + instructions.push( + await deleteNameRegistry( + connection, + currentVerifiedPubkey.toString(), + payerKey, + TWITTER_VERIFICATION_AUTHORITY, + TWITTER_ROOT_PARENT_REGISTRY_KEY, + ), + ); + + // Create the new reverse registry + instructions = instructions.concat( + await createReverseTwitterRegistry( + connection, + twitterHandle, + twitterHandleRegistryKey, + newVerifiedPubkey, + payerKey, + ), + ); + + return instructions; } // Delete the verified registry for a given twitter handle // Signed by the verified pubkey export async function deleteTwitterRegistry( - twitterHandle: string, - verifiedPubkey: PublicKey, + twitterHandle: string, + verifiedPubkey: PublicKey, ): Promise { - const hashedTwitterHandle = await getHashedName(twitterHandle); - const twitterHandleRegistryKey = await getNameAccountKey( - hashedTwitterHandle, - undefined, - TWITTER_ROOT_PARENT_REGISTRY_KEY, - ); - - const hashedVerifiedPubkey = await getHashedName(verifiedPubkey.toString()); - const reverseRegistryKey = await getNameAccountKey( - hashedVerifiedPubkey, - TWITTER_VERIFICATION_AUTHORITY, - TWITTER_ROOT_PARENT_REGISTRY_KEY, - ); - - const instructions = [ - // Delete the user facing registry - deleteInstruction( - NAME_PROGRAM_ID, - twitterHandleRegistryKey, - verifiedPubkey, - verifiedPubkey, - ), - // Delete the reverse registry - deleteInstruction( - NAME_PROGRAM_ID, - reverseRegistryKey, - verifiedPubkey, - verifiedPubkey, - ), - ]; - - return instructions; + const hashedTwitterHandle = await getHashedName(twitterHandle); + const twitterHandleRegistryKey = await getNameAccountKey( + hashedTwitterHandle, + undefined, + TWITTER_ROOT_PARENT_REGISTRY_KEY, + ); + + const hashedVerifiedPubkey = await getHashedName(verifiedPubkey.toString()); + const reverseRegistryKey = await getNameAccountKey( + hashedVerifiedPubkey, + TWITTER_VERIFICATION_AUTHORITY, + TWITTER_ROOT_PARENT_REGISTRY_KEY, + ); + + const instructions = [ + // Delete the user facing registry + deleteInstruction(NAME_PROGRAM_ID, twitterHandleRegistryKey, verifiedPubkey, verifiedPubkey), + // Delete the reverse registry + deleteInstruction(NAME_PROGRAM_ID, reverseRegistryKey, verifiedPubkey, verifiedPubkey), + ]; + + return instructions; } ////////////////////////////////////////// // Getter Functions // Returns the key of the user-facing registry -export async function getTwitterRegistryKey( - twitter_handle: string, -): Promise { - const hashedTwitterHandle = await getHashedName(twitter_handle); - return await getNameAccountKey( - hashedTwitterHandle, - undefined, - TWITTER_ROOT_PARENT_REGISTRY_KEY, - ); +export async function getTwitterRegistryKey(twitter_handle: string): Promise { + const hashedTwitterHandle = await getHashedName(twitter_handle); + return await getNameAccountKey(hashedTwitterHandle, undefined, TWITTER_ROOT_PARENT_REGISTRY_KEY); } -export async function getTwitterRegistry( - connection: Connection, - twitter_handle: string, -): Promise { - const hashedTwitterHandle = await getHashedName(twitter_handle); - const twitterHandleRegistryKey = await getNameAccountKey( - hashedTwitterHandle, - undefined, - TWITTER_ROOT_PARENT_REGISTRY_KEY, - ); - const registry = NameRegistryState.retrieve( - connection, - twitterHandleRegistryKey, - ); - return registry; +export async function getTwitterRegistry(connection: Connection, twitter_handle: string): Promise { + const hashedTwitterHandle = await getHashedName(twitter_handle); + const twitterHandleRegistryKey = await getNameAccountKey( + hashedTwitterHandle, + undefined, + TWITTER_ROOT_PARENT_REGISTRY_KEY, + ); + const registry = NameRegistryState.retrieve(connection, twitterHandleRegistryKey); + return registry; } export async function getHandleAndRegistryKey( - connection: Connection, - verifiedPubkey: PublicKey, + connection: Connection, + verifiedPubkey: PublicKey, ): Promise<[string, PublicKey]> { - const hashedVerifiedPubkey = await getHashedName(verifiedPubkey.toString()); - const reverseRegistryKey = await getNameAccountKey( - hashedVerifiedPubkey, - TWITTER_VERIFICATION_AUTHORITY, - TWITTER_ROOT_PARENT_REGISTRY_KEY, - ); - - const reverseRegistryState = await ReverseTwitterRegistryState.retrieve( - connection, - reverseRegistryKey, - ); - return [ - reverseRegistryState.twitterHandle, - new PublicKey(reverseRegistryState.twitterRegistryKey), - ]; + const hashedVerifiedPubkey = await getHashedName(verifiedPubkey.toString()); + const reverseRegistryKey = await getNameAccountKey( + hashedVerifiedPubkey, + TWITTER_VERIFICATION_AUTHORITY, + TWITTER_ROOT_PARENT_REGISTRY_KEY, + ); + + const reverseRegistryState = await ReverseTwitterRegistryState.retrieve(connection, reverseRegistryKey); + return [reverseRegistryState.twitterHandle, new PublicKey(reverseRegistryState.twitterRegistryKey)]; } // Uses the RPC node filtering feature, execution speed may vary export async function getTwitterHandleandRegistryKeyViaFilters( - connection: Connection, - verifiedPubkey: PublicKey, + connection: Connection, + verifiedPubkey: PublicKey, ): Promise<[string, PublicKey]> { - const filters = [ - { - memcmp: { - offset: 0, - bytes: TWITTER_ROOT_PARENT_REGISTRY_KEY.toBase58(), - }, - }, - { - memcmp: { - offset: 32, - bytes: verifiedPubkey.toBase58(), - }, - }, - { - memcmp: { - offset: 64, - bytes: TWITTER_VERIFICATION_AUTHORITY.toBase58(), - }, - }, - ]; - - const filteredAccounts = await getFilteredProgramAccounts( - connection, - NAME_PROGRAM_ID, - filters, - ); - - for (const f of filteredAccounts) { - if (f.accountInfo.data.length > NameRegistryState.HEADER_LEN + 32) { - const data = f.accountInfo.data.slice(NameRegistryState.HEADER_LEN); - const state = deserialize( - ReverseTwitterRegistryState.schema, - data, - ) as ReverseTwitterRegistryState; - return [state.twitterHandle, new PublicKey(state.twitterRegistryKey)]; + const filters = [ + { + memcmp: { + offset: 0, + bytes: TWITTER_ROOT_PARENT_REGISTRY_KEY.toBase58(), + }, + }, + { + memcmp: { + offset: 32, + bytes: verifiedPubkey.toBase58(), + }, + }, + { + memcmp: { + offset: 64, + bytes: TWITTER_VERIFICATION_AUTHORITY.toBase58(), + }, + }, + ]; + + const filteredAccounts = await getFilteredProgramAccounts(connection, NAME_PROGRAM_ID, filters); + + for (const f of filteredAccounts) { + if (f.accountInfo.data.length > NameRegistryState.HEADER_LEN + 32) { + const data = f.accountInfo.data.slice(NameRegistryState.HEADER_LEN); + const state = deserialize(ReverseTwitterRegistryState.schema, data) as ReverseTwitterRegistryState; + return [state.twitterHandle, new PublicKey(state.twitterRegistryKey)]; + } } - } - throw new Error('Registry not found.'); + throw new Error('Registry not found.'); } // Uses the RPC node filtering feature, execution speed may vary // Does not give you the handle, but is an alternative to getHandlesAndKeysFromVerifiedPubkey + getTwitterRegistry to get the data -export async function getTwitterRegistryData( - connection: Connection, - verifiedPubkey: PublicKey, -): Promise { - const filters = [ - { - memcmp: { - offset: 0, - bytes: TWITTER_ROOT_PARENT_REGISTRY_KEY.toBase58(), - }, - }, - { - memcmp: { - offset: 32, - bytes: verifiedPubkey.toBase58(), - }, - }, - { - memcmp: { - offset: 64, - bytes: new PublicKey(Buffer.alloc(32, 0)).toBase58(), - }, - }, - ]; - - const filteredAccounts = await getFilteredProgramAccounts( - connection, - NAME_PROGRAM_ID, - filters, - ); - - if (filteredAccounts.length > 1) { - throw new Error('Found more than one registry.'); - } - - return filteredAccounts[0].accountInfo.data.slice( - NameRegistryState.HEADER_LEN, - ); +export async function getTwitterRegistryData(connection: Connection, verifiedPubkey: PublicKey): Promise { + const filters = [ + { + memcmp: { + offset: 0, + bytes: TWITTER_ROOT_PARENT_REGISTRY_KEY.toBase58(), + }, + }, + { + memcmp: { + offset: 32, + bytes: verifiedPubkey.toBase58(), + }, + }, + { + memcmp: { + offset: 64, + bytes: new PublicKey(Buffer.alloc(32, 0)).toBase58(), + }, + }, + ]; + + const filteredAccounts = await getFilteredProgramAccounts(connection, NAME_PROGRAM_ID, filters); + + if (filteredAccounts.length > 1) { + throw new Error('Found more than one registry.'); + } + + return filteredAccounts[0].accountInfo.data.slice(NameRegistryState.HEADER_LEN); } ////////////////////////////////////////////// // Utils export class ReverseTwitterRegistryState { - twitterRegistryKey: Uint8Array; - twitterHandle: string; - - static schema: Schema = { - struct: { - twitterRegistryKey: { array: { type: 'u8', len: 32 } }, - twitterHandle: 'string', - }, - }; - constructor(obj: { twitterRegistryKey: Uint8Array; twitterHandle: string }) { - this.twitterRegistryKey = obj.twitterRegistryKey; - this.twitterHandle = obj.twitterHandle; - } - - public static async retrieve( - connection: Connection, - reverseTwitterAccountKey: PublicKey, - ): Promise { - const reverseTwitterAccount = await connection.getAccountInfo( - reverseTwitterAccountKey, - 'processed', - ); - if (!reverseTwitterAccount) { - throw new Error('Invalid reverse Twitter account provided'); + twitterRegistryKey: Uint8Array; + twitterHandle: string; + + static schema: Schema = { + struct: { + twitterRegistryKey: { array: { type: 'u8', len: 32 } }, + twitterHandle: 'string', + }, + }; + constructor(obj: { twitterRegistryKey: Uint8Array; twitterHandle: string }) { + this.twitterRegistryKey = obj.twitterRegistryKey; + this.twitterHandle = obj.twitterHandle; } - const res = deserialize( - this.schema, - reverseTwitterAccount.data.slice(NameRegistryState.HEADER_LEN), - ) as ReverseTwitterRegistryState; - - return res; - } + public static async retrieve( + connection: Connection, + reverseTwitterAccountKey: PublicKey, + ): Promise { + const reverseTwitterAccount = await connection.getAccountInfo(reverseTwitterAccountKey, 'processed'); + if (!reverseTwitterAccount) { + throw new Error('Invalid reverse Twitter account provided'); + } + + const res = deserialize( + this.schema, + reverseTwitterAccount.data.slice(NameRegistryState.HEADER_LEN), + ) as ReverseTwitterRegistryState; + + return res; + } } export async function createReverseTwitterRegistry( - connection: Connection, - twitterHandle: string, - twitterRegistryKey: PublicKey, - verifiedPubkey: PublicKey, - payerKey: PublicKey, + connection: Connection, + twitterHandle: string, + twitterRegistryKey: PublicKey, + verifiedPubkey: PublicKey, + payerKey: PublicKey, ): Promise { - // Create the reverse lookup registry - const hashedVerifiedPubkey = await getHashedName(verifiedPubkey.toString()); - const reverseRegistryKey = await getNameAccountKey( - hashedVerifiedPubkey, - TWITTER_VERIFICATION_AUTHORITY, - TWITTER_ROOT_PARENT_REGISTRY_KEY, - ); - const reverseTwitterRegistryStateBuff = serialize( - ReverseTwitterRegistryState.schema, - new ReverseTwitterRegistryState({ - twitterRegistryKey: twitterRegistryKey.toBytes(), - twitterHandle, - }), - ); - return [ - createInstruction( - NAME_PROGRAM_ID, - SystemProgram.programId, - reverseRegistryKey, - verifiedPubkey, - payerKey, - hashedVerifiedPubkey, - new Numberu64( - await connection.getMinimumBalanceForRentExemption( - reverseTwitterRegistryStateBuff.length, + // Create the reverse lookup registry + const hashedVerifiedPubkey = await getHashedName(verifiedPubkey.toString()); + const reverseRegistryKey = await getNameAccountKey( + hashedVerifiedPubkey, + TWITTER_VERIFICATION_AUTHORITY, + TWITTER_ROOT_PARENT_REGISTRY_KEY, + ); + const reverseTwitterRegistryStateBuff = serialize( + ReverseTwitterRegistryState.schema, + new ReverseTwitterRegistryState({ + twitterRegistryKey: twitterRegistryKey.toBytes(), + twitterHandle, + }), + ); + return [ + createInstruction( + NAME_PROGRAM_ID, + SystemProgram.programId, + reverseRegistryKey, + verifiedPubkey, + payerKey, + hashedVerifiedPubkey, + new Numberu64(await connection.getMinimumBalanceForRentExemption(reverseTwitterRegistryStateBuff.length)), + new Numberu32(reverseTwitterRegistryStateBuff.length), + TWITTER_VERIFICATION_AUTHORITY, // Twitter authority acts as class for all reverse-lookup registries + TWITTER_ROOT_PARENT_REGISTRY_KEY, // Reverse registries are also children of the root + TWITTER_VERIFICATION_AUTHORITY, + ), + updateInstruction( + NAME_PROGRAM_ID, + reverseRegistryKey, + new Numberu32(0), + Buffer.from(reverseTwitterRegistryStateBuff), + TWITTER_VERIFICATION_AUTHORITY, + undefined, ), - ), - new Numberu32(reverseTwitterRegistryStateBuff.length), - TWITTER_VERIFICATION_AUTHORITY, // Twitter authority acts as class for all reverse-lookup registries - TWITTER_ROOT_PARENT_REGISTRY_KEY, // Reverse registries are also children of the root - TWITTER_VERIFICATION_AUTHORITY, - ), - updateInstruction( - NAME_PROGRAM_ID, - reverseRegistryKey, - new Numberu32(0), - Buffer.from(reverseTwitterRegistryStateBuff), - TWITTER_VERIFICATION_AUTHORITY, - undefined, - ), - ]; + ]; } diff --git a/name-service/js/src/utils.ts b/name-service/js/src/utils.ts index 1210512b517..29f0779662e 100644 --- a/name-service/js/src/utils.ts +++ b/name-service/js/src/utils.ts @@ -2,13 +2,13 @@ import assert from 'assert'; import { createHash } from 'crypto'; import { - AccountInfo, - Connection, - GetProgramAccountsFilter, - Keypair, - PublicKey, - Transaction, - TransactionInstruction, + AccountInfo, + Connection, + GetProgramAccountsFilter, + Keypair, + PublicKey, + Transaction, + TransactionInstruction, } from '@solana/web3.js'; import BN from 'bn.js'; @@ -16,142 +16,134 @@ import { HASH_PREFIX, NAME_PROGRAM_ID } from './bindings'; import { NameRegistryState } from './state'; export class Numberu32 extends BN { - /** - * Convert to Buffer representation - */ - toBuffer(): Buffer { - const a = super.toArray().reverse(); - const b = Buffer.from(a); - if (b.length === 4) { - return b; - } - assert(b.length < 4, 'Numberu32 too large'); + /** + * Convert to Buffer representation + */ + toBuffer(): Buffer { + const a = super.toArray().reverse(); + const b = Buffer.from(a); + if (b.length === 4) { + return b; + } + assert(b.length < 4, 'Numberu32 too large'); - const zeroPad = Buffer.alloc(4); - b.copy(zeroPad); - return zeroPad; - } + const zeroPad = Buffer.alloc(4); + b.copy(zeroPad); + return zeroPad; + } - /** - * Construct a Numberu64 from Buffer representation - */ - static fromBuffer(buffer: Buffer): BN { - assert(buffer.length === 4, `Invalid buffer length: ${buffer.length}`); - return new BN( - [...buffer] - .reverse() - .map((i) => `00${i.toString(16)}`.slice(-2)) - .join(''), - 16, - ); - } + /** + * Construct a Numberu64 from Buffer representation + */ + static fromBuffer(buffer: Buffer): BN { + assert(buffer.length === 4, `Invalid buffer length: ${buffer.length}`); + return new BN( + [...buffer] + .reverse() + .map(i => `00${i.toString(16)}`.slice(-2)) + .join(''), + 16, + ); + } } export class Numberu64 extends BN { - /** - * Convert to Buffer representation - */ - toBuffer(): Buffer { - const a = super.toArray().reverse(); - const b = Buffer.from(a); - if (b.length === 8) { - return b; - } - assert(b.length < 8, 'Numberu64 too large'); + /** + * Convert to Buffer representation + */ + toBuffer(): Buffer { + const a = super.toArray().reverse(); + const b = Buffer.from(a); + if (b.length === 8) { + return b; + } + assert(b.length < 8, 'Numberu64 too large'); - const zeroPad = Buffer.alloc(8); - b.copy(zeroPad); - return zeroPad; - } + const zeroPad = Buffer.alloc(8); + b.copy(zeroPad); + return zeroPad; + } - /** - * Construct a Numberu64 from Buffer representation - */ - static fromBuffer(buffer: Buffer): BN { - assert(buffer.length === 8, `Invalid buffer length: ${buffer.length}`); - return new BN( - [...buffer] - .reverse() - .map((i) => `00${i.toString(16)}`.slice(-2)) - .join(''), - 16, - ); - } + /** + * Construct a Numberu64 from Buffer representation + */ + static fromBuffer(buffer: Buffer): BN { + assert(buffer.length === 8, `Invalid buffer length: ${buffer.length}`); + return new BN( + [...buffer] + .reverse() + .map(i => `00${i.toString(16)}`.slice(-2)) + .join(''), + 16, + ); + } } export const signAndSendTransactionInstructions = async ( - // sign and send transaction - connection: Connection, - signers: Array, - feePayer: Keypair, - txInstructions: Array, + // sign and send transaction + connection: Connection, + signers: Array, + feePayer: Keypair, + txInstructions: Array, ): Promise => { - const tx = new Transaction(); - tx.feePayer = feePayer.publicKey; - signers.push(feePayer); - tx.add(...txInstructions); - return await connection.sendTransaction(tx, signers); + const tx = new Transaction(); + tx.feePayer = feePayer.publicKey; + signers.push(feePayer); + tx.add(...txInstructions); + return await connection.sendTransaction(tx, signers); }; export async function getHashedName(name: string): Promise { - const input = HASH_PREFIX + name; - const buffer = createHash('sha256').update(input, 'utf8').digest(); - return buffer; + const input = HASH_PREFIX + name; + const buffer = createHash('sha256').update(input, 'utf8').digest(); + return buffer; } export async function getNameAccountKey( - hashed_name: Buffer, - nameClass?: PublicKey, - nameParent?: PublicKey, + hashed_name: Buffer, + nameClass?: PublicKey, + nameParent?: PublicKey, ): Promise { - const seeds = [hashed_name]; - if (nameClass) { - seeds.push(nameClass.toBuffer()); - } else { - seeds.push(Buffer.alloc(32)); - } - if (nameParent) { - seeds.push(nameParent.toBuffer()); - } else { - seeds.push(Buffer.alloc(32)); - } - const [nameAccountKey] = await PublicKey.findProgramAddress( - seeds, - NAME_PROGRAM_ID, - ); - return nameAccountKey; + const seeds = [hashed_name]; + if (nameClass) { + seeds.push(nameClass.toBuffer()); + } else { + seeds.push(Buffer.alloc(32)); + } + if (nameParent) { + seeds.push(nameParent.toBuffer()); + } else { + seeds.push(Buffer.alloc(32)); + } + const [nameAccountKey] = await PublicKey.findProgramAddress(seeds, NAME_PROGRAM_ID); + return nameAccountKey; } -export async function getNameOwner( - connection: Connection, - nameAccountKey: PublicKey, -): Promise { - const nameAccount = await connection.getAccountInfo(nameAccountKey); - if (!nameAccount) { - throw new Error('Unable to find the given account.'); - } - return NameRegistryState.retrieve(connection, nameAccountKey); +export async function getNameOwner(connection: Connection, nameAccountKey: PublicKey): Promise { + const nameAccount = await connection.getAccountInfo(nameAccountKey); + if (!nameAccount) { + throw new Error('Unable to find the given account.'); + } + return NameRegistryState.retrieve(connection, nameAccountKey); } export async function getFilteredProgramAccounts( - connection: Connection, - programId: PublicKey, - filters: GetProgramAccountsFilter[], + connection: Connection, + programId: PublicKey, + filters: GetProgramAccountsFilter[], ): Promise<{ publicKey: PublicKey; accountInfo: AccountInfo }[]> { - const resp = await connection.getProgramAccounts(programId, { - commitment: connection.commitment, - filters, - encoding: 'base64', - }); - return resp.map( - ({ pubkey, account: { data, executable, owner, lamports } }) => ({ - publicKey: pubkey, - accountInfo: { - data: data, - executable, - owner: owner, - lamports, - }, - }), - ); + const resp = await connection.getProgramAccounts(programId, { + commitment: connection.commitment, + filters, + encoding: 'base64', + }); + return resp.map(({ pubkey, account: { data, executable, owner, lamports } }) => ({ + publicKey: pubkey, + accountInfo: { + data: data, + executable, + owner: owner, + lamports, + }, + })); } diff --git a/name-service/js/test/e2e/index.test.ts b/name-service/js/test/e2e/index.test.ts index 570dc04eb6b..3f84ff9b109 100644 --- a/name-service/js/test/e2e/index.test.ts +++ b/name-service/js/test/e2e/index.test.ts @@ -1,22 +1,22 @@ import { - Connection, - Keypair, - LAMPORTS_PER_SOL, - PublicKey, - sendAndConfirmTransaction, - Transaction, + Connection, + Keypair, + LAMPORTS_PER_SOL, + PublicKey, + sendAndConfirmTransaction, + Transaction, } from '@solana/web3.js'; import { beforeAll, beforeEach, describe, expect, test } from '@jest/globals'; import { - createNameRegistry, - deleteNameRegistry, - getHashedName, - getNameAccountKey, - NameRegistryState, - reallocNameAccount, - transferNameOwnership, - updateNameRegistryData, + createNameRegistry, + deleteNameRegistry, + getHashedName, + getNameAccountKey, + NameRegistryState, + reallocNameAccount, + transferNameOwnership, + updateNameRegistryData, } from '../../src'; const url = 'http://127.0.0.1:8899'; @@ -27,101 +27,67 @@ const space = 20; let nameKey: PublicKey; let name: string; beforeAll(async () => { - const airdropSignature = await connection.requestAirdrop( - payer.publicKey, - LAMPORTS_PER_SOL, - ); - await connection.confirmTransaction({ - signature: airdropSignature, - ...(await connection.getLatestBlockhash()), - }); + const airdropSignature = await connection.requestAirdrop(payer.publicKey, LAMPORTS_PER_SOL); + await connection.confirmTransaction({ + signature: airdropSignature, + ...(await connection.getLatestBlockhash()), + }); }); beforeEach(async () => { - name = Math.random().toString() + '.sol'; - nameKey = await getNameKey(name); - const lamports = await connection.getMinimumBalanceForRentExemption( - space + NameRegistryState.HEADER_LEN, - ); - const inst = await createNameRegistry( - connection, - name, - space, - payer.publicKey, - owner.publicKey, - lamports, - ); - const tx = new Transaction().add(inst); - await sendAndConfirmTransaction(connection, tx, [payer]); + name = Math.random().toString() + '.sol'; + nameKey = await getNameKey(name); + const lamports = await connection.getMinimumBalanceForRentExemption(space + NameRegistryState.HEADER_LEN); + const inst = await createNameRegistry(connection, name, space, payer.publicKey, owner.publicKey, lamports); + const tx = new Transaction().add(inst); + await sendAndConfirmTransaction(connection, tx, [payer]); }); test('Create Name Registery', async () => { - const nameAccount = await NameRegistryState.retrieve(connection, nameKey); - nameAccount.owner.equals(owner.publicKey); - expect(nameAccount.data?.length).toEqual(space); + const nameAccount = await NameRegistryState.retrieve(connection, nameKey); + nameAccount.owner.equals(owner.publicKey); + expect(nameAccount.data?.length).toEqual(space); }); test('Update Name Registery', async () => { - const data = Buffer.from('@Dudl'); - const inst = await updateNameRegistryData(connection, name, 0, data); - const tx = new Transaction().add(inst); - await sendAndConfirmTransaction(connection, tx, [payer, owner]); - const nameAccount = await NameRegistryState.retrieve(connection, nameKey); - nameAccount.data?.equals(data); + const data = Buffer.from('@Dudl'); + const inst = await updateNameRegistryData(connection, name, 0, data); + const tx = new Transaction().add(inst); + await sendAndConfirmTransaction(connection, tx, [payer, owner]); + const nameAccount = await NameRegistryState.retrieve(connection, nameKey); + nameAccount.data?.equals(data); }); test('Transfer Name Ownership', async () => { - const newOwner = Keypair.generate(); - const inst = await transferNameOwnership( - connection, - name, - newOwner.publicKey, - ); - const tx = new Transaction().add(inst); - await sendAndConfirmTransaction(connection, tx, [payer, owner]); - const nameAccount = await NameRegistryState.retrieve(connection, nameKey); - nameAccount.owner.equals(newOwner.publicKey); + const newOwner = Keypair.generate(); + const inst = await transferNameOwnership(connection, name, newOwner.publicKey); + const tx = new Transaction().add(inst); + await sendAndConfirmTransaction(connection, tx, [payer, owner]); + const nameAccount = await NameRegistryState.retrieve(connection, nameKey); + nameAccount.owner.equals(newOwner.publicKey); }); test('Realloc Name Account to bigger space', async () => { - const inst = await reallocNameAccount( - connection, - name, - space + 10, - payer.publicKey, - ); - const tx = new Transaction().add(inst); - await sendAndConfirmTransaction(connection, tx, [payer, owner]); - const nameAccount = await NameRegistryState.retrieve(connection, nameKey); - expect(nameAccount.data?.length).toEqual(space + 10); + const inst = await reallocNameAccount(connection, name, space + 10, payer.publicKey); + const tx = new Transaction().add(inst); + await sendAndConfirmTransaction(connection, tx, [payer, owner]); + const nameAccount = await NameRegistryState.retrieve(connection, nameKey); + expect(nameAccount.data?.length).toEqual(space + 10); }); test('Realloc Name Account to smaller space', async () => { - const inst = await reallocNameAccount( - connection, - name, - space - 10, - payer.publicKey, - ); - const tx = new Transaction().add(inst); - await sendAndConfirmTransaction(connection, tx, [payer, owner]); - const nameAccount = await NameRegistryState.retrieve(connection, nameKey); - expect(nameAccount.data?.length).toEqual(space - 10); + const inst = await reallocNameAccount(connection, name, space - 10, payer.publicKey); + const tx = new Transaction().add(inst); + await sendAndConfirmTransaction(connection, tx, [payer, owner]); + const nameAccount = await NameRegistryState.retrieve(connection, nameKey); + expect(nameAccount.data?.length).toEqual(space - 10); }); test('Delete Name Registry', async () => { - const inst = await deleteNameRegistry(connection, name, payer.publicKey); - const tx = new Transaction().add(inst); - await sendAndConfirmTransaction(connection, tx, [payer, owner]); - const nameAccount = await connection.getAccountInfo(nameKey); - expect(nameAccount).toBeNull; + const inst = await deleteNameRegistry(connection, name, payer.publicKey); + const tx = new Transaction().add(inst); + await sendAndConfirmTransaction(connection, tx, [payer, owner]); + const nameAccount = await connection.getAccountInfo(nameKey); + expect(nameAccount).toBeNull; }); -const getNameKey = async ( - name: string, - nameClass?: PublicKey, - parentName?: PublicKey, -) => { - const hashedName = await getHashedName(name); - const nameAccountKey = await getNameAccountKey( - hashedName, - nameClass, - parentName, - ); - return nameAccountKey; +const getNameKey = async (name: string, nameClass?: PublicKey, parentName?: PublicKey) => { + const hashedName = await getHashedName(name); + const nameAccountKey = await getNameAccountKey(hashedName, nameClass, parentName); + return nameAccountKey; }; diff --git a/name-service/js/test/unit/index.test.ts b/name-service/js/test/unit/index.test.ts index 5ae8f81a721..87d3d67de03 100644 --- a/name-service/js/test/unit/index.test.ts +++ b/name-service/js/test/unit/index.test.ts @@ -1,137 +1,120 @@ -import { - Keypair, - LAMPORTS_PER_SOL, - PublicKey, - SystemProgram, -} from '@solana/web3.js'; +import { Keypair, LAMPORTS_PER_SOL, PublicKey, SystemProgram } from '@solana/web3.js'; import { - createInstruction, - deleteInstruction, - reallocInstruction, - transferInstruction, - updateInstruction, + createInstruction, + deleteInstruction, + reallocInstruction, + transferInstruction, + updateInstruction, } from '../../src'; import { Numberu32, Numberu64 } from '../../src/utils'; import { describe, expect, test } from '@jest/globals'; describe('SplNameService Instructions', () => { - const nameServiceAddress = new PublicKey( - 'namesLPneVptA9Z5rqUDD9tMTWEJwofgaYwp8cawRkX', - ); - const nameAccountKey = Keypair.generate().publicKey; - const nameOwnerKey = Keypair.generate().publicKey; - const payerKey = Keypair.generate().publicKey; - const nameClassKey = Keypair.generate().publicKey; - const nameParent = Keypair.generate().publicKey; - const nameParentOwner = Keypair.generate().publicKey; - const name = Buffer.from('hello'); + const nameServiceAddress = new PublicKey('namesLPneVptA9Z5rqUDD9tMTWEJwofgaYwp8cawRkX'); + const nameAccountKey = Keypair.generate().publicKey; + const nameOwnerKey = Keypair.generate().publicKey; + const payerKey = Keypair.generate().publicKey; + const nameClassKey = Keypair.generate().publicKey; + const nameParent = Keypair.generate().publicKey; + const nameParentOwner = Keypair.generate().publicKey; + const name = Buffer.from('hello'); - test('createInstruction without class and parent name key', () => { - const instruction = createInstruction( - nameServiceAddress, - SystemProgram.programId, - nameAccountKey, - nameOwnerKey, - payerKey, - name, - new Numberu64(LAMPORTS_PER_SOL), - new Numberu64(10), - ); + test('createInstruction without class and parent name key', () => { + const instruction = createInstruction( + nameServiceAddress, + SystemProgram.programId, + nameAccountKey, + nameOwnerKey, + payerKey, + name, + new Numberu64(LAMPORTS_PER_SOL), + new Numberu64(10), + ); - expect(instruction.keys).toHaveLength(6); - instruction.keys[0].pubkey.equals(SystemProgram.programId); - instruction.keys[1].pubkey.equals(payerKey); - instruction.keys[2].pubkey.equals(nameAccountKey); - instruction.keys[3].pubkey.equals(nameOwnerKey); - instruction.keys[4].pubkey.equals(new PublicKey(Buffer.alloc(32))); - instruction.keys[5].pubkey.equals(new PublicKey(Buffer.alloc(32))); - }); + expect(instruction.keys).toHaveLength(6); + instruction.keys[0].pubkey.equals(SystemProgram.programId); + instruction.keys[1].pubkey.equals(payerKey); + instruction.keys[2].pubkey.equals(nameAccountKey); + instruction.keys[3].pubkey.equals(nameOwnerKey); + instruction.keys[4].pubkey.equals(new PublicKey(Buffer.alloc(32))); + instruction.keys[5].pubkey.equals(new PublicKey(Buffer.alloc(32))); + }); - test('createInstruction with class and parent name key', () => { - const instruction = createInstruction( - nameServiceAddress, - SystemProgram.programId, - nameAccountKey, - nameOwnerKey, - payerKey, - name, - new Numberu64(LAMPORTS_PER_SOL), - new Numberu64(10), - nameClassKey, - nameParent, - nameParentOwner, - ); + test('createInstruction with class and parent name key', () => { + const instruction = createInstruction( + nameServiceAddress, + SystemProgram.programId, + nameAccountKey, + nameOwnerKey, + payerKey, + name, + new Numberu64(LAMPORTS_PER_SOL), + new Numberu64(10), + nameClassKey, + nameParent, + nameParentOwner, + ); - expect(instruction.keys).toHaveLength(7); - instruction.keys[0].pubkey.equals(SystemProgram.programId); - instruction.keys[1].pubkey.equals(payerKey); - instruction.keys[2].pubkey.equals(nameAccountKey); - instruction.keys[3].pubkey.equals(nameOwnerKey); - instruction.keys[4].pubkey.equals(nameClassKey); - instruction.keys[5].pubkey.equals(nameParent); - instruction.keys[6].pubkey.equals(nameParentOwner); - }); + expect(instruction.keys).toHaveLength(7); + instruction.keys[0].pubkey.equals(SystemProgram.programId); + instruction.keys[1].pubkey.equals(payerKey); + instruction.keys[2].pubkey.equals(nameAccountKey); + instruction.keys[3].pubkey.equals(nameOwnerKey); + instruction.keys[4].pubkey.equals(nameClassKey); + instruction.keys[5].pubkey.equals(nameParent); + instruction.keys[6].pubkey.equals(nameParentOwner); + }); - test('updateInstruction', () => { - const data = Buffer.from('@Dudl'); - const instruction = updateInstruction( - nameServiceAddress, - nameAccountKey, - new Numberu32(0), - data, - nameOwnerKey, - undefined, - ); + test('updateInstruction', () => { + const data = Buffer.from('@Dudl'); + const instruction = updateInstruction( + nameServiceAddress, + nameAccountKey, + new Numberu32(0), + data, + nameOwnerKey, + undefined, + ); - expect(instruction.keys).toHaveLength(2); - instruction.keys[0].pubkey.equals(nameAccountKey); - instruction.keys[1].pubkey.equals(nameOwnerKey); - }); + expect(instruction.keys).toHaveLength(2); + instruction.keys[0].pubkey.equals(nameAccountKey); + instruction.keys[1].pubkey.equals(nameOwnerKey); + }); - test('transferInstruction', () => { - const newOwner = Keypair.generate().publicKey; - const instruction = transferInstruction( - nameServiceAddress, - nameAccountKey, - newOwner, - nameOwnerKey, - ); + test('transferInstruction', () => { + const newOwner = Keypair.generate().publicKey; + const instruction = transferInstruction(nameServiceAddress, nameAccountKey, newOwner, nameOwnerKey); - expect(instruction.keys).toHaveLength(2); - instruction.keys[0].pubkey.equals(nameAccountKey); - instruction.keys[1].pubkey.equals(nameOwnerKey); - }); + expect(instruction.keys).toHaveLength(2); + instruction.keys[0].pubkey.equals(nameAccountKey); + instruction.keys[1].pubkey.equals(nameOwnerKey); + }); - test('deleteInstruction', () => { - const instruction = deleteInstruction( - nameServiceAddress, - nameAccountKey, - payerKey, - nameOwnerKey, - ); + test('deleteInstruction', () => { + const instruction = deleteInstruction(nameServiceAddress, nameAccountKey, payerKey, nameOwnerKey); - expect(instruction.keys).toHaveLength(3); - instruction.keys[0].pubkey.equals(nameAccountKey); - instruction.keys[1].pubkey.equals(nameOwnerKey); - instruction.keys[2].pubkey.equals(payerKey); - }); + expect(instruction.keys).toHaveLength(3); + instruction.keys[0].pubkey.equals(nameAccountKey); + instruction.keys[1].pubkey.equals(nameOwnerKey); + instruction.keys[2].pubkey.equals(payerKey); + }); - test('reallocInstruction', () => { - const instruction = reallocInstruction( - nameServiceAddress, - SystemProgram.programId, - payerKey, - nameAccountKey, - nameOwnerKey, - new Numberu32(30), - ); + test('reallocInstruction', () => { + const instruction = reallocInstruction( + nameServiceAddress, + SystemProgram.programId, + payerKey, + nameAccountKey, + nameOwnerKey, + new Numberu32(30), + ); - expect(instruction.keys).toHaveLength(4); - instruction.keys[0].pubkey.equals(SystemProgram.programId); - instruction.keys[1].pubkey.equals(payerKey); - instruction.keys[2].pubkey.equals(nameAccountKey); - instruction.keys[3].pubkey.equals(nameOwnerKey); - }); + expect(instruction.keys).toHaveLength(4); + instruction.keys[0].pubkey.equals(SystemProgram.programId); + instruction.keys[1].pubkey.equals(payerKey); + instruction.keys[2].pubkey.equals(nameAccountKey); + instruction.keys[3].pubkey.equals(nameOwnerKey); + }); }); diff --git a/package.json b/package.json index 770a87397cc..98ea88e0dc8 100644 --- a/package.json +++ b/package.json @@ -17,14 +17,18 @@ "build:program": "turbo run build:program", "clean": "turbo run clean", "lint": "turbo run lint", - "lint:fix": "turbo run lint:fix", + "format": "prettier --check .", + "format:fix": "prettier --write .", "test": "turbo run test" }, "devDependencies": { "@solana/eslint-config-solana": "^3.0.3", "@solana/prettier-config-solana": "^0.0.5", - "eslint-config-turbo": "^2.0.9", - "turbo": "^2.0.9" + "eslint-config-prettier": "^9.1.0", + "eslint-config-turbo": "^2.0.14", + "eslint-plugin-prettier": "^5.2.1", + "prettier": "^3.3.3", + "turbo": "^2.0.14" }, "engines": { "node": ">=14.0.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5f8a058212e..68a38835b37 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,12 +14,21 @@ importers: '@solana/prettier-config-solana': specifier: ^0.0.5 version: 0.0.5(prettier@3.3.3) + eslint-config-prettier: + specifier: ^9.1.0 + version: 9.1.0(eslint@8.57.0) eslint-config-turbo: - specifier: ^2.0.9 - version: 2.0.9(eslint@8.57.0) + specifier: ^2.0.14 + version: 2.0.14(eslint@8.57.0) + eslint-plugin-prettier: + specifier: ^5.2.1 + version: 5.2.1(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.3.3) + prettier: + specifier: ^3.3.3 + version: 3.3.3 turbo: - specifier: ^2.0.9 - version: 2.0.9 + specifier: ^2.0.14 + version: 2.0.14 account-compression/sdk: dependencies: @@ -30,8 +39,8 @@ importers: specifier: ^0.4.1 version: 0.4.1 '@solana/web3.js': - specifier: ^1.95.1 - version: 1.95.1 + specifier: ^1.95.2 + version: 1.95.2 bn.js: specifier: ^5.2.1 version: 5.2.1 @@ -43,8 +52,8 @@ importers: version: 1.3.3 devDependencies: '@coral-xyz/anchor': - specifier: ^0.27.0 - version: 0.27.0 + specifier: ^0.29.0 + version: 0.29.0 '@metaplex-foundation/rustbin': specifier: ^0.3.5 version: 0.3.5 @@ -53,10 +62,7 @@ importers: version: 0.20.1 '@solana/eslint-config-solana': specifier: ^3.0.3 - version: 3.0.3(@typescript-eslint/eslint-plugin@7.17.0)(@typescript-eslint/parser@7.17.0)(eslint-plugin-jest@28.6.0)(eslint-plugin-react-hooks@4.6.0)(eslint-plugin-simple-import-sort@12.1.1)(eslint-plugin-sort-keys-fix@1.1.2)(eslint-plugin-typescript-sort-keys@3.2.0)(eslint@8.57.0)(typescript@5.5.4) - '@solana/prettier-config-solana': - specifier: ^0.0.5 - version: 0.0.5(prettier@3.3.3) + version: 3.0.3(@typescript-eslint/eslint-plugin@8.2.0)(@typescript-eslint/parser@8.2.0)(eslint-plugin-jest@28.8.0)(eslint-plugin-react-hooks@4.6.0)(eslint-plugin-simple-import-sort@12.1.1)(eslint-plugin-sort-keys-fix@1.1.2)(eslint-plugin-typescript-sort-keys@3.2.0)(eslint@8.57.0)(typescript@5.5.4) '@types/bn.js': specifier: ^5.1.0 version: 5.1.5 @@ -64,38 +70,32 @@ importers: specifier: ^29.5.12 version: 29.5.12 '@types/node': - specifier: ^22.0.0 - version: 22.0.0 + specifier: ^22.4.2 + version: 22.4.2 '@types/node-fetch': specifier: ^2.6.11 version: 2.6.11 '@typescript-eslint/eslint-plugin': - specifier: ^7.17.0 - version: 7.17.0(@typescript-eslint/parser@7.17.0)(eslint@8.57.0)(typescript@5.5.4) + specifier: ^8.2.0 + version: 8.2.0(@typescript-eslint/parser@8.2.0)(eslint@8.57.0)(typescript@5.5.4) '@typescript-eslint/parser': - specifier: ^7.17.0 - version: 7.17.0(eslint@8.57.0)(typescript@5.5.4) + specifier: ^8.2.0 + version: 8.2.0(eslint@8.57.0)(typescript@5.5.4) eslint: specifier: ^8.57.0 version: 8.57.0 - eslint-config-prettier: - specifier: ^9.1.0 - version: 9.1.0(eslint@8.57.0) eslint-config-turbo: - specifier: ^2.0.9 - version: 2.0.9(eslint@8.57.0) + specifier: ^2.0.14 + version: 2.0.14(eslint@8.57.0) eslint-plugin-import: specifier: ^2.29.1 - version: 2.29.1(@typescript-eslint/parser@7.17.0)(eslint@8.57.0) + version: 2.29.1(@typescript-eslint/parser@8.2.0)(eslint@8.57.0) eslint-plugin-jest: - specifier: ^28.6.0 - version: 28.6.0(@typescript-eslint/eslint-plugin@7.17.0)(eslint@8.57.0)(jest@29.7.0)(typescript@5.5.4) + specifier: ^28.8.0 + version: 28.8.0(@typescript-eslint/eslint-plugin@8.2.0)(eslint@8.57.0)(jest@29.7.0)(typescript@5.5.4) eslint-plugin-mocha: specifier: ^10.5.0 version: 10.5.0(eslint@8.57.0) - eslint-plugin-prettier: - specifier: ^5.2.1 - version: 5.2.1(@types/eslint@8.56.7)(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.3.3) eslint-plugin-simple-import-sort: specifier: ^12.1.1 version: 12.1.1(eslint@8.57.0) @@ -107,28 +107,25 @@ importers: version: 6.1.1 jest: specifier: ^29.0.1 - version: 29.7.0(@types/node@22.0.0)(ts-node@10.9.2) + version: 29.7.0(@types/node@22.4.2)(ts-node@10.9.2) jest-config: specifier: ^29.0.1 - version: 29.7.0(@types/node@22.0.0)(ts-node@10.9.2) - prettier: - specifier: ^3.3.3 - version: 3.3.3 + version: 29.7.0(@types/node@22.4.2)(ts-node@10.9.2) start-server-and-test: specifier: ^2.0.5 version: 2.0.5 ts-jest: - specifier: ^29.2.3 - version: 29.2.3(@babel/core@7.23.2)(jest@29.7.0)(typescript@5.5.4) + specifier: ^29.2.4 + version: 29.2.4(@babel/core@7.23.2)(jest@29.7.0)(typescript@5.5.4) ts-jest-resolver: specifier: ^2.0.1 version: 2.0.1 ts-node: specifier: ^10.9.2 - version: 10.9.2(@types/node@22.0.0)(typescript@5.5.4) + version: 10.9.2(@types/node@22.4.2)(typescript@5.5.4) typedoc: - specifier: ^0.26.5 - version: 0.26.5(typescript@5.5.4) + specifier: ^0.26.6 + version: 0.26.6(typescript@5.5.4) typescript: specifier: 5.5.4 version: 5.5.4 @@ -140,32 +137,26 @@ importers: version: 6.0.3 devDependencies: '@types/chai': - specifier: ^4.3.16 - version: 4.3.16 + specifier: ^4.3.17 + version: 4.3.17 '@types/mocha': specifier: ^10.0.7 version: 10.0.7 '@types/node': - specifier: ^22.0.0 - version: 22.0.0 + specifier: ^22.4.2 + version: 22.4.2 '@typescript-eslint/eslint-plugin': - specifier: ^7.17.0 - version: 7.17.0(@typescript-eslint/parser@7.17.0)(eslint@8.57.0)(typescript@5.5.4) + specifier: ^8.2.0 + version: 8.2.0(@typescript-eslint/parser@8.2.0)(eslint@8.57.0)(typescript@5.5.4) '@typescript-eslint/parser': - specifier: ^7.17.0 - version: 7.17.0(eslint@8.57.0)(typescript@5.5.4) + specifier: ^8.2.0 + version: 8.2.0(eslint@8.57.0)(typescript@5.5.4) chai: specifier: ^5.1.1 version: 5.1.1 eslint: specifier: ^8.57.0 version: 8.57.0 - eslint-config-prettier: - specifier: ^9.1.0 - version: 9.1.0(eslint@8.57.0) - eslint-plugin-prettier: - specifier: ^5.2.1 - version: 5.2.1(@types/eslint@8.56.7)(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.3.3) eslint-plugin-require-extensions: specifier: ^0.1.1 version: 0.1.3(eslint@8.57.0) @@ -173,20 +164,17 @@ importers: specifier: ^6.1.1 version: 6.1.1 mocha: - specifier: ^10.7.0 - version: 10.7.0 - prettier: - specifier: ^3.3.3 - version: 3.3.3 + specifier: ^10.7.3 + version: 10.7.3 shx: specifier: ^0.3.4 version: 0.3.4 ts-node: specifier: ^10.9.2 - version: 10.9.2(@types/node@22.0.0)(typescript@5.5.4) + version: 10.9.2(@types/node@22.4.2)(typescript@5.5.4) typedoc: - specifier: ^0.26.5 - version: 0.26.5(typescript@5.5.4) + specifier: ^0.26.6 + version: 0.26.6(typescript@5.5.4) typescript: specifier: ^5.5.4 version: 5.5.4 @@ -198,38 +186,32 @@ importers: version: 6.0.3 devDependencies: '@solana/web3.js': - specifier: ^1.95.1 - version: 1.95.1 + specifier: ^1.95.3 + version: 1.95.3 '@types/chai': - specifier: ^4.3.16 - version: 4.3.16 + specifier: ^4.3.17 + version: 4.3.17 '@types/jest': specifier: ^29.5.12 version: 29.5.12 '@types/node': - specifier: ^22.0.0 - version: 22.0.0 + specifier: ^22.4.2 + version: 22.4.2 '@types/node-fetch': specifier: ^2.6.11 version: 2.6.11 '@typescript-eslint/eslint-plugin': - specifier: ^7.17.0 - version: 7.17.0(@typescript-eslint/parser@7.17.0)(eslint@8.57.0)(typescript@5.5.4) + specifier: ^8.2.0 + version: 8.2.0(@typescript-eslint/parser@8.2.0)(eslint@8.57.0)(typescript@5.5.4) '@typescript-eslint/parser': - specifier: ^7.17.0 - version: 7.17.0(eslint@8.57.0)(typescript@5.5.4) + specifier: ^8.2.0 + version: 8.2.0(eslint@8.57.0)(typescript@5.5.4) chai: specifier: ^5.1.1 version: 5.1.1 eslint: specifier: ^8.57.0 version: 8.57.0 - eslint-config-prettier: - specifier: ^9.1.0 - version: 9.1.0(eslint@8.57.0) - eslint-plugin-prettier: - specifier: ^5.2.1 - version: 5.2.1(@types/eslint@8.56.7)(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.3.3) eslint-plugin-require-extensions: specifier: ^0.1.1 version: 0.1.3(eslint@8.57.0) @@ -238,10 +220,7 @@ importers: version: 6.1.1 jest: specifier: ^29.0.0 - version: 29.7.0(@types/node@22.0.0)(ts-node@10.9.2) - prettier: - specifier: ^3.3.3 - version: 3.3.3 + version: 29.7.0(@types/node@22.4.2)(ts-node@10.9.2) process: specifier: ^0.11.10 version: 0.11.10 @@ -252,14 +231,14 @@ importers: specifier: ^2.0.5 version: 2.0.5 ts-jest: - specifier: ^29.2.3 - version: 29.2.3(@babel/core@7.23.2)(jest@29.7.0)(typescript@5.5.4) + specifier: ^29.2.4 + version: 29.2.4(@babel/core@7.23.2)(jest@29.7.0)(typescript@5.5.4) ts-node: specifier: ^10.9.2 - version: 10.9.2(@types/node@22.0.0)(typescript@5.5.4) + version: 10.9.2(@types/node@22.4.2)(typescript@5.5.4) typedoc: - specifier: ^0.26.5 - version: 0.26.5(typescript@5.5.4) + specifier: ^0.26.6 + version: 0.26.6(typescript@5.5.4) typescript: specifier: ^5.5.4 version: 5.5.4 @@ -267,8 +246,8 @@ importers: name-service/js: dependencies: '@solana/web3.js': - specifier: ^1.95.1 - version: 1.95.1 + specifier: ^1.95.3 + version: 1.95.3 bn.js: specifier: ^5.1.3 version: 5.2.1 @@ -286,47 +265,41 @@ importers: specifier: ^29.5.12 version: 29.5.12 '@types/node': - specifier: ^22.0.0 - version: 22.0.0 + specifier: ^22.4.2 + version: 22.4.2 '@typescript-eslint/eslint-plugin': - specifier: ^7.17.0 - version: 7.17.0(@typescript-eslint/parser@7.17.0)(eslint@8.57.0)(typescript@5.5.4) + specifier: ^8.2.0 + version: 8.2.0(@typescript-eslint/parser@8.2.0)(eslint@8.57.0)(typescript@5.5.4) '@typescript-eslint/parser': - specifier: ^7.17.0 - version: 7.17.0(eslint@8.57.0)(typescript@5.5.4) + specifier: ^8.2.0 + version: 8.2.0(eslint@8.57.0)(typescript@5.5.4) eslint: specifier: ^8.57.0 version: 8.57.0 - eslint-config-prettier: - specifier: ^9.1.0 - version: 9.1.0(eslint@8.57.0) eslint-plugin-eslint-comments: specifier: ^3.2.0 version: 3.2.0(eslint@8.57.0) eslint-plugin-functional: - specifier: ^6.6.3 - version: 6.6.3(eslint@8.57.0)(typescript@5.5.4) + specifier: ^7.0.1 + version: 7.0.1(eslint@8.57.0)(typescript@5.5.4) eslint-plugin-import: specifier: ^2.29.1 - version: 2.29.1(@typescript-eslint/parser@7.17.0)(eslint@8.57.0) + version: 2.29.1(@typescript-eslint/parser@8.2.0)(eslint@8.57.0) jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@22.0.0)(ts-node@10.9.2) - prettier: - specifier: ^3.3.3 - version: 3.3.3 + version: 29.7.0(@types/node@22.4.2)(ts-node@10.9.2) start-server-and-test: specifier: ^2.0.5 version: 2.0.5 ts-jest: - specifier: ^29.2.3 - version: 29.2.3(@babel/core@7.23.2)(jest@29.7.0)(typescript@5.5.4) + specifier: ^29.2.4 + version: 29.2.4(@babel/core@7.23.2)(jest@29.7.0)(typescript@5.5.4) ts-node: specifier: ^10.9.2 - version: 10.9.2(@types/node@22.0.0)(typescript@5.5.4) + version: 10.9.2(@types/node@22.4.2)(typescript@5.5.4) typedoc: - specifier: ^0.26.5 - version: 0.26.5(typescript@5.5.4) + specifier: ^0.26.6 + version: 0.26.6(typescript@5.5.4) typescript: specifier: ^5.5.4 version: 5.5.4 @@ -334,67 +307,61 @@ importers: single-pool/js/packages/classic: dependencies: '@solana/addresses': - specifier: '=2.0.0-experimental.21e994f' - version: 2.0.0-experimental.21e994f + specifier: 2.0.0-rc.1 + version: 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) '@solana/spl-single-pool': specifier: 1.0.0 version: link:../modern '@solana/web3.js': - specifier: ^1.95.1 - version: 1.95.1 + specifier: ^1.95.3 + version: 1.95.3 devDependencies: '@ava/typescript': specifier: ^5.0.0 version: 5.0.0 '@types/node': - specifier: ^22.0.0 - version: 22.0.0 + specifier: ^22.4.2 + version: 22.4.2 '@typescript-eslint/eslint-plugin': - specifier: ^7.17.0 - version: 7.17.0(@typescript-eslint/parser@7.17.0)(eslint@8.57.0)(typescript@5.5.4) + specifier: ^8.2.0 + version: 8.2.0(@typescript-eslint/parser@8.2.0)(eslint@8.57.0)(typescript@5.5.4) ava: specifier: ^6.1.3 version: 6.1.3(@ava/typescript@5.0.0) eslint: specifier: ^8.57.0 version: 8.57.0 - eslint-config-prettier: - specifier: ^9.1.0 - version: 9.1.0(eslint@8.57.0) - eslint-plugin-prettier: - specifier: ^5.2.1 - version: 5.2.1(@types/eslint@8.56.7)(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.3.3) solana-bankrun: specifier: ^0.2.0 version: 0.2.0 tsx: - specifier: ^4.16.2 - version: 4.16.2 + specifier: ^4.17.0 + version: 4.17.0 typescript: specifier: ^5.5.4 version: 5.5.4 single-pool/js/packages/modern: dependencies: - '@solana/web3.js': - specifier: '=2.0.0-experimental.21e994f' - version: 2.0.0-experimental.21e994f(node-fetch@2.7.0)(ws@8.14.2) + '@solana/addresses': + specifier: 2.0.0-rc.1 + version: 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/instructions': + specifier: 2.0.0-rc.1 + version: 2.0.0-rc.1(typescript@5.5.4) + '@solana/transaction-messages': + specifier: 2.0.0-rc.1 + version: 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) devDependencies: '@types/node': - specifier: ^22.0.0 - version: 22.0.0 + specifier: ^22.4.2 + version: 22.4.2 '@typescript-eslint/eslint-plugin': - specifier: ^7.17.0 - version: 7.17.0(@typescript-eslint/parser@7.17.0)(eslint@8.57.0)(typescript@5.5.4) + specifier: ^8.2.0 + version: 8.2.0(@typescript-eslint/parser@8.2.0)(eslint@8.57.0)(typescript@5.5.4) eslint: specifier: ^8.57.0 version: 8.57.0 - eslint-config-prettier: - specifier: ^9.1.0 - version: 9.1.0(eslint@8.57.0) - eslint-plugin-prettier: - specifier: ^5.2.1 - version: 5.2.1(@types/eslint@8.56.7)(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.3.3) typescript: specifier: ^5.5.4 version: 5.5.4 @@ -406,10 +373,10 @@ importers: version: 4.0.1 '@solana/spl-token': specifier: 0.4.6 - version: 0.4.6(@solana/web3.js@1.95.1)(fastestsmallesttextencoderdecoder@1.0.22) + version: 0.4.6(@solana/web3.js@1.95.3)(fastestsmallesttextencoderdecoder@1.0.22) '@solana/web3.js': - specifier: ^1.95.1 - version: 1.95.1 + specifier: ^1.95.3 + version: 1.95.3 bn.js: specifier: ^5.2.0 version: 5.2.1 @@ -425,25 +392,25 @@ importers: devDependencies: '@rollup/plugin-alias': specifier: ^5.1.0 - version: 5.1.0(rollup@4.19.1) + version: 5.1.0(rollup@4.21.0) '@rollup/plugin-commonjs': specifier: ^26.0.1 - version: 26.0.1(rollup@4.19.1) + version: 26.0.1(rollup@4.21.0) '@rollup/plugin-json': specifier: ^6.1.0 - version: 6.1.0(rollup@4.19.1) + version: 6.1.0(rollup@4.21.0) '@rollup/plugin-multi-entry': specifier: ^6.0.0 - version: 6.0.1(rollup@4.19.1) + version: 6.0.1(rollup@4.21.0) '@rollup/plugin-node-resolve': specifier: ^15.0.2 - version: 15.2.3(rollup@4.19.1) + version: 15.2.3(rollup@4.21.0) '@rollup/plugin-terser': specifier: ^0.4.4 - version: 0.4.4(rollup@4.19.1) + version: 0.4.4(rollup@4.21.0) '@rollup/plugin-typescript': specifier: ^11.1.6 - version: 11.1.6(rollup@4.19.1)(tslib@2.6.3)(typescript@5.5.4) + version: 11.1.6(rollup@4.21.0)(tslib@2.6.3)(typescript@5.5.4) '@types/bn.js': specifier: ^5.1.0 version: 5.1.5 @@ -451,47 +418,38 @@ importers: specifier: ^29.5.12 version: 29.5.12 '@types/node': - specifier: ^22.0.0 - version: 22.0.0 + specifier: ^22.4.2 + version: 22.4.2 '@types/node-fetch': specifier: ^2.6.11 version: 2.6.11 '@typescript-eslint/eslint-plugin': - specifier: ^7.17.0 - version: 7.17.0(@typescript-eslint/parser@7.17.0)(eslint@8.57.0)(typescript@5.5.4) + specifier: ^8.2.0 + version: 8.2.0(@typescript-eslint/parser@8.2.0)(eslint@8.57.0)(typescript@5.5.4) '@typescript-eslint/parser': - specifier: ^7.17.0 - version: 7.17.0(eslint@8.57.0)(typescript@5.5.4) + specifier: ^8.2.0 + version: 8.2.0(eslint@8.57.0)(typescript@5.5.4) cross-env: specifier: ^7.0.3 version: 7.0.3 eslint: specifier: ^8.57.0 version: 8.57.0 - eslint-config-prettier: - specifier: ^9.1.0 - version: 9.1.0(eslint@8.57.0) - eslint-plugin-prettier: - specifier: ^5.2.1 - version: 5.2.1(@types/eslint@8.56.7)(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.3.3) jest: specifier: ^29.0.0 - version: 29.7.0(@types/node@22.0.0)(ts-node@10.9.2) - prettier: - specifier: ^3.3.3 - version: 3.3.3 + version: 29.7.0(@types/node@22.4.2)(ts-node@10.9.2) rimraf: specifier: ^6.0.1 version: 6.0.1 rollup: - specifier: ^4.19.1 - version: 4.19.1 + specifier: ^4.21.0 + version: 4.21.0 rollup-plugin-dts: specifier: ^6.1.1 - version: 6.1.1(rollup@4.19.1)(typescript@5.5.4) + version: 6.1.1(rollup@4.21.0)(typescript@5.5.4) ts-jest: - specifier: ^29.2.3 - version: 29.2.3(@babel/core@7.23.2)(jest@29.7.0)(typescript@5.5.4) + specifier: ^29.2.4 + version: 29.2.4(@babel/core@7.23.2)(jest@29.7.0)(typescript@5.5.4) typescript: specifier: ^5.5.4 version: 5.5.4 @@ -499,42 +457,36 @@ importers: token-group/js: dependencies: '@solana/codecs': - specifier: 2.0.0-preview.4 - version: 2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + specifier: 2.0.0-rc.1 + version: 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) '@solana/spl-type-length-value': specifier: 0.1.0 version: link:../../libraries/type-length-value/js devDependencies: '@solana/web3.js': - specifier: ^1.95.1 - version: 1.95.1 + specifier: ^1.95.3 + version: 1.95.3 '@types/chai': - specifier: ^4.3.16 - version: 4.3.16 + specifier: ^4.3.17 + version: 4.3.17 '@types/mocha': specifier: ^10.0.7 version: 10.0.7 '@types/node': - specifier: ^22.0.0 - version: 22.0.0 + specifier: ^22.4.2 + version: 22.4.2 '@typescript-eslint/eslint-plugin': - specifier: ^7.17.0 - version: 7.17.0(@typescript-eslint/parser@7.17.0)(eslint@8.57.0)(typescript@5.5.4) + specifier: ^8.2.0 + version: 8.2.0(@typescript-eslint/parser@8.2.0)(eslint@8.57.0)(typescript@5.5.4) '@typescript-eslint/parser': - specifier: ^7.17.0 - version: 7.17.0(eslint@8.57.0)(typescript@5.5.4) + specifier: ^8.2.0 + version: 8.2.0(eslint@8.57.0)(typescript@5.5.4) chai: specifier: ^5.1.1 version: 5.1.1 eslint: specifier: ^8.57.0 version: 8.57.0 - eslint-config-prettier: - specifier: ^9.1.0 - version: 9.1.0(eslint@8.57.0) - eslint-plugin-prettier: - specifier: ^5.2.1 - version: 5.2.1(@types/eslint@8.56.7)(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.3.3) eslint-plugin-require-extensions: specifier: ^0.1.1 version: 0.1.3(eslint@8.57.0) @@ -542,23 +494,20 @@ importers: specifier: ^6.1.1 version: 6.1.1 mocha: - specifier: ^10.7.0 - version: 10.7.0 - prettier: - specifier: ^3.3.3 - version: 3.3.3 + specifier: ^10.7.3 + version: 10.7.3 shx: specifier: ^0.3.4 version: 0.3.4 ts-node: specifier: ^10.9.2 - version: 10.9.2(@types/node@22.0.0)(typescript@5.5.4) + version: 10.9.2(@types/node@22.4.2)(typescript@5.5.4) tslib: specifier: ^2.6.3 version: 2.6.3 typedoc: - specifier: ^0.26.5 - version: 0.26.5(typescript@5.5.4) + specifier: ^0.26.6 + version: 0.26.6(typescript@5.5.4) typescript: specifier: ^5.5.4 version: 5.5.4 @@ -580,61 +529,49 @@ importers: devDependencies: '@rollup/plugin-commonjs': specifier: ^26.0.1 - version: 26.0.1(rollup@4.19.1) + version: 26.0.1(rollup@4.21.0) '@rollup/plugin-node-resolve': specifier: ^15.0.2 - version: 15.2.3(rollup@4.19.1) + version: 15.2.3(rollup@4.21.0) '@rollup/plugin-typescript': specifier: ^11.1.6 - version: 11.1.6(rollup@4.19.1)(tslib@2.6.3)(typescript@5.5.4) + version: 11.1.6(rollup@4.21.0)(tslib@2.6.3)(typescript@5.5.4) '@solana/spl-token': specifier: 0.4.6 - version: 0.4.6(@solana/web3.js@1.95.1)(fastestsmallesttextencoderdecoder@1.0.22) + version: 0.4.6(@solana/web3.js@1.95.3)(fastestsmallesttextencoderdecoder@1.0.22) '@solana/web3.js': - specifier: ^1.95.1 - version: 1.95.1 + specifier: ^1.95.3 + version: 1.95.3 '@types/eslint': specifier: ^8.56.7 version: 8.56.7 - '@types/eslint-plugin-prettier': - specifier: ^3.1.0 - version: 3.1.3 '@types/node': - specifier: ^22.0.0 - version: 22.0.0 + specifier: ^22.4.2 + version: 22.4.2 '@typescript-eslint/eslint-plugin': - specifier: ^7.17.0 - version: 7.17.0(@typescript-eslint/parser@7.17.0)(eslint@8.57.0)(typescript@5.5.4) + specifier: ^8.2.0 + version: 8.2.0(@typescript-eslint/parser@8.2.0)(eslint@8.57.0)(typescript@5.5.4) '@typescript-eslint/parser': - specifier: ^7.17.0 - version: 7.17.0(eslint@8.57.0)(typescript@5.5.4) + specifier: ^8.2.0 + version: 8.2.0(eslint@8.57.0)(typescript@5.5.4) eslint: specifier: ^8.57.0 version: 8.57.0 - eslint-config-prettier: - specifier: ^9.1.0 - version: 9.1.0(eslint@8.57.0) - eslint-plugin-prettier: - specifier: ^5.2.1 - version: 5.2.1(@types/eslint@8.56.7)(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.3.3) gh-pages: specifier: ^6.1.1 version: 6.1.1 - prettier: - specifier: ^3.3.3 - version: 3.3.3 rollup: - specifier: ^4.19.1 - version: 4.19.1 + specifier: ^4.21.0 + version: 4.21.0 ts-node: specifier: ^10.9.2 - version: 10.9.2(@types/node@22.0.0)(typescript@5.5.4) + version: 10.9.2(@types/node@22.4.2)(typescript@5.5.4) tslib: specifier: ^2.6.3 version: 2.6.3 typedoc: - specifier: ^0.26.5 - version: 0.26.5(typescript@5.5.4) + specifier: ^0.26.6 + version: 0.26.6(typescript@5.5.4) typescript: specifier: ^5.5.4 version: 5.5.4 @@ -642,42 +579,36 @@ importers: token-metadata/js: dependencies: '@solana/codecs': - specifier: 2.0.0-preview.4 - version: 2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + specifier: 2.0.0-rc.1 + version: 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) '@solana/spl-type-length-value': specifier: 0.1.0 version: link:../../libraries/type-length-value/js devDependencies: '@solana/web3.js': - specifier: ^1.95.1 - version: 1.95.1 + specifier: ^1.95.3 + version: 1.95.3 '@types/chai': - specifier: ^4.3.16 - version: 4.3.16 + specifier: ^4.3.17 + version: 4.3.17 '@types/mocha': specifier: ^10.0.7 version: 10.0.7 '@types/node': - specifier: ^22.0.0 - version: 22.0.0 + specifier: ^22.4.2 + version: 22.4.2 '@typescript-eslint/eslint-plugin': - specifier: ^7.17.0 - version: 7.17.0(@typescript-eslint/parser@7.17.0)(eslint@8.57.0)(typescript@5.5.4) + specifier: ^8.2.0 + version: 8.2.0(@typescript-eslint/parser@8.2.0)(eslint@8.57.0)(typescript@5.5.4) '@typescript-eslint/parser': - specifier: ^7.17.0 - version: 7.17.0(eslint@8.57.0)(typescript@5.5.4) + specifier: ^8.2.0 + version: 8.2.0(eslint@8.57.0)(typescript@5.5.4) chai: specifier: ^5.1.1 version: 5.1.1 eslint: specifier: ^8.57.0 version: 8.57.0 - eslint-config-prettier: - specifier: ^9.1.0 - version: 9.1.0(eslint@8.57.0) - eslint-plugin-prettier: - specifier: ^5.2.1 - version: 5.2.1(@types/eslint@8.56.7)(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.3.3) eslint-plugin-require-extensions: specifier: ^0.1.1 version: 0.1.3(eslint@8.57.0) @@ -685,23 +616,20 @@ importers: specifier: ^6.1.1 version: 6.1.1 mocha: - specifier: ^10.7.0 - version: 10.7.0 - prettier: - specifier: ^3.3.3 - version: 3.3.3 + specifier: ^10.7.3 + version: 10.7.3 shx: specifier: ^0.3.4 version: 0.3.4 ts-node: specifier: ^10.9.2 - version: 10.9.2(@types/node@22.0.0)(typescript@5.5.4) + version: 10.9.2(@types/node@22.4.2)(typescript@5.5.4) tslib: specifier: ^2.6.3 version: 2.6.3 typedoc: - specifier: ^0.26.5 - version: 0.26.5(typescript@5.5.4) + specifier: ^0.26.6 + version: 0.26.6(typescript@5.5.4) typescript: specifier: ^5.5.4 version: 5.5.4 @@ -717,16 +645,16 @@ importers: devDependencies: '@solana/spl-token': specifier: 0.4.6 - version: 0.4.6(@solana/web3.js@1.95.1)(fastestsmallesttextencoderdecoder@1.0.22) + version: 0.4.6(@solana/web3.js@1.95.3)(fastestsmallesttextencoderdecoder@1.0.22) '@solana/web3.js': - specifier: ^1.95.1 - version: 1.95.1 + specifier: ^1.95.3 + version: 1.95.3 '@types/bn.js': specifier: ^5.1.0 version: 5.1.5 '@types/chai': - specifier: ^4.3.16 - version: 4.3.16 + specifier: ^4.3.17 + version: 4.3.17 '@types/chai-as-promised': specifier: ^7.1.4 version: 7.1.8 @@ -734,38 +662,29 @@ importers: specifier: ^10.0.7 version: 10.0.7 '@typescript-eslint/eslint-plugin': - specifier: ^7.17.0 - version: 7.17.0(@typescript-eslint/parser@7.17.0)(eslint@8.57.0)(typescript@5.5.4) + specifier: ^8.2.0 + version: 8.2.0(@typescript-eslint/parser@8.2.0)(eslint@8.57.0)(typescript@5.5.4) '@typescript-eslint/parser': - specifier: ^7.17.0 - version: 7.17.0(eslint@8.57.0)(typescript@5.5.4) + specifier: ^8.2.0 + version: 8.2.0(eslint@8.57.0)(typescript@5.5.4) eslint: specifier: ^8.57.0 version: 8.57.0 - eslint-config-prettier: - specifier: ^9.1.0 - version: 9.1.0(eslint@8.57.0) eslint-plugin-import: specifier: ^2.29.1 - version: 2.29.1(@typescript-eslint/parser@7.17.0)(eslint@8.57.0) - eslint-plugin-prettier: - specifier: ^5.2.1 - version: 5.2.1(@types/eslint@8.56.7)(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.3.3) + version: 2.29.1(@typescript-eslint/parser@8.2.0)(eslint@8.57.0) eslint-plugin-require-extensions: specifier: ^0.1.1 version: 0.1.3(eslint@8.57.0) mocha: - specifier: ^10.7.0 - version: 10.7.0 - prettier: - specifier: ^3.3.3 - version: 3.3.3 + specifier: ^10.7.3 + version: 10.7.3 start-server-and-test: specifier: ^2.0.5 version: 2.0.5 ts-node: specifier: ^10.9.2 - version: 10.9.2(@types/node@22.0.0)(typescript@5.5.4) + version: 10.9.2(@types/node@22.4.2)(typescript@5.5.4) typescript: specifier: ^5.5.4 version: 5.5.4 @@ -783,23 +702,23 @@ importers: version: link:../../token-group/js '@solana/spl-token-metadata': specifier: ^0.1.3 - version: 0.1.4(@solana/web3.js@1.95.1)(fastestsmallesttextencoderdecoder@1.0.22) + version: 0.1.4(@solana/web3.js@1.95.3)(fastestsmallesttextencoderdecoder@1.0.22) buffer: specifier: ^6.0.3 version: 6.0.3 devDependencies: '@solana/codecs-strings': - specifier: 2.0.0-preview.4 - version: 2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + specifier: 2.0.0-rc.1 + version: 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) '@solana/spl-memo': specifier: 0.2.4 version: link:../../memo/js '@solana/web3.js': - specifier: ^1.95.1 - version: 1.95.1 + specifier: ^1.95.3 + version: 1.95.3 '@types/chai': - specifier: ^4.3.16 - version: 4.3.16 + specifier: ^4.3.17 + version: 4.3.17 '@types/chai-as-promised': specifier: ^7.1.4 version: 7.1.8 @@ -807,17 +726,17 @@ importers: specifier: ^10.0.7 version: 10.0.7 '@types/node': - specifier: ^22.0.0 - version: 22.0.0 + specifier: ^22.4.2 + version: 22.4.2 '@types/node-fetch': specifier: ^2.6.11 version: 2.6.11 '@typescript-eslint/eslint-plugin': - specifier: ^7.17.0 - version: 7.17.0(@typescript-eslint/parser@7.17.0)(eslint@8.57.0)(typescript@5.5.4) + specifier: ^8.2.0 + version: 8.2.0(@typescript-eslint/parser@8.2.0)(eslint@8.57.0)(typescript@5.5.4) '@typescript-eslint/parser': - specifier: ^7.17.0 - version: 7.17.0(eslint@8.57.0)(typescript@5.5.4) + specifier: ^8.2.0 + version: 8.2.0(eslint@8.57.0)(typescript@5.5.4) chai: specifier: ^5.1.1 version: 5.1.1 @@ -827,12 +746,6 @@ importers: eslint: specifier: ^8.57.0 version: 8.57.0 - eslint-config-prettier: - specifier: ^9.1.0 - version: 9.1.0(eslint@8.57.0) - eslint-plugin-prettier: - specifier: ^5.2.1 - version: 5.2.1(@types/eslint@8.56.7)(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.3.3) eslint-plugin-require-extensions: specifier: ^0.1.1 version: 0.1.3(eslint@8.57.0) @@ -840,11 +753,8 @@ importers: specifier: ^6.1.1 version: 6.1.1 mocha: - specifier: ^10.7.0 - version: 10.7.0 - prettier: - specifier: ^3.3.3 - version: 3.3.3 + specifier: ^10.7.3 + version: 10.7.3 process: specifier: ^0.11.10 version: 0.11.10 @@ -856,10 +766,10 @@ importers: version: 2.0.5 ts-node: specifier: ^10.9.2 - version: 10.9.2(@types/node@22.0.0)(typescript@5.5.4) + version: 10.9.2(@types/node@22.4.2)(typescript@5.5.4) typedoc: - specifier: ^0.26.5 - version: 0.26.5(typescript@5.5.4) + specifier: ^0.26.6 + version: 0.26.6(typescript@5.5.4) typescript: specifier: ^5.5.4 version: 5.5.4 @@ -915,7 +825,7 @@ packages: '@babel/traverse': 7.23.2 '@babel/types': 7.23.0 convert-source-map: 2.0.0 - debug: 4.3.6 + debug: 4.3.6(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -1183,6 +1093,12 @@ packages: dependencies: regenerator-runtime: 0.14.0 + /@babel/runtime@7.25.0: + resolution: {integrity: sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==} + engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.14.0 + /@babel/template@7.22.15: resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} engines: {node: '>=6.9.0'} @@ -1204,7 +1120,7 @@ packages: '@babel/helper-split-export-declaration': 7.22.6 '@babel/parser': 7.23.0 '@babel/types': 7.23.0 - debug: 4.3.6 + debug: 4.3.6(supports-color@8.1.1) globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -1223,13 +1139,13 @@ packages: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} dev: true - /@coral-xyz/anchor@0.27.0: - resolution: {integrity: sha512-+P/vPdORawvg3A9Wj02iquxb4T0C5m4P6aZBVYysKl4Amk+r6aMPZkUhilBkD6E4Nuxnoajv3CFykUfkGE0n5g==} + /@coral-xyz/anchor@0.29.0: + resolution: {integrity: sha512-eny6QNG0WOwqV0zQ7cs/b1tIuzZGmP7U7EcH+ogt4Gdbl8HDmIYVMh/9aTmYZPaFWjtUaI8qSn73uYEXWfATdA==} engines: {node: '>=11'} dependencies: - '@coral-xyz/borsh': 0.27.0(@solana/web3.js@1.95.1) - '@solana/web3.js': 1.95.1 - base64-js: 1.5.1 + '@coral-xyz/borsh': 0.29.0(@solana/web3.js@1.95.2) + '@noble/hashes': 1.4.0 + '@solana/web3.js': 1.95.2 bn.js: 5.2.1 bs58: 4.0.1 buffer-layout: 1.2.2 @@ -1237,7 +1153,6 @@ packages: cross-fetch: 3.1.8 crypto-hash: 1.3.0 eventemitter3: 4.0.7 - js-sha256: 0.9.0 pako: 2.1.0 snake-case: 3.0.4 superstruct: 0.15.5 @@ -1248,13 +1163,13 @@ packages: - utf-8-validate dev: true - /@coral-xyz/borsh@0.27.0(@solana/web3.js@1.95.1): - resolution: {integrity: sha512-tJKzhLukghTWPLy+n8K8iJKgBq1yLT/AxaNd10yJrX8mI56ao5+OFAKAqW/h0i79KCvb4BK0VGO5ECmmolFz9A==} + /@coral-xyz/borsh@0.29.0(@solana/web3.js@1.95.2): + resolution: {integrity: sha512-s7VFVa3a0oqpkuRloWVPdCK7hMbAMY270geZOGfCnaqexrP5dTIpbEHL33req6IYPPJ0hYa71cdvJ1h6V55/oQ==} engines: {node: '>=10'} peerDependencies: '@solana/web3.js': ^1.68.0 dependencies: - '@solana/web3.js': 1.95.1 + '@solana/web3.js': 1.95.2 bn.js: 5.2.1 buffer-layout: 1.2.2 dev: true @@ -1266,207 +1181,216 @@ packages: '@jridgewell/trace-mapping': 0.3.9 dev: true - /@esbuild/aix-ppc64@0.21.5: - resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} - engines: {node: '>=12'} + /@esbuild/aix-ppc64@0.23.0: + resolution: {integrity: sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ==} + engines: {node: '>=18'} cpu: [ppc64] os: [aix] requiresBuild: true dev: true optional: true - /@esbuild/android-arm64@0.21.5: - resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} - engines: {node: '>=12'} + /@esbuild/android-arm64@0.23.0: + resolution: {integrity: sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ==} + engines: {node: '>=18'} cpu: [arm64] os: [android] requiresBuild: true dev: true optional: true - /@esbuild/android-arm@0.21.5: - resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} - engines: {node: '>=12'} + /@esbuild/android-arm@0.23.0: + resolution: {integrity: sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g==} + engines: {node: '>=18'} cpu: [arm] os: [android] requiresBuild: true dev: true optional: true - /@esbuild/android-x64@0.21.5: - resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} - engines: {node: '>=12'} + /@esbuild/android-x64@0.23.0: + resolution: {integrity: sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==} + engines: {node: '>=18'} cpu: [x64] os: [android] requiresBuild: true dev: true optional: true - /@esbuild/darwin-arm64@0.21.5: - resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} - engines: {node: '>=12'} + /@esbuild/darwin-arm64@0.23.0: + resolution: {integrity: sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==} + engines: {node: '>=18'} cpu: [arm64] os: [darwin] requiresBuild: true dev: true optional: true - /@esbuild/darwin-x64@0.21.5: - resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} - engines: {node: '>=12'} + /@esbuild/darwin-x64@0.23.0: + resolution: {integrity: sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ==} + engines: {node: '>=18'} cpu: [x64] os: [darwin] requiresBuild: true dev: true optional: true - /@esbuild/freebsd-arm64@0.21.5: - resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} - engines: {node: '>=12'} + /@esbuild/freebsd-arm64@0.23.0: + resolution: {integrity: sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==} + engines: {node: '>=18'} cpu: [arm64] os: [freebsd] requiresBuild: true dev: true optional: true - /@esbuild/freebsd-x64@0.21.5: - resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} - engines: {node: '>=12'} + /@esbuild/freebsd-x64@0.23.0: + resolution: {integrity: sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ==} + engines: {node: '>=18'} cpu: [x64] os: [freebsd] requiresBuild: true dev: true optional: true - /@esbuild/linux-arm64@0.21.5: - resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} - engines: {node: '>=12'} + /@esbuild/linux-arm64@0.23.0: + resolution: {integrity: sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw==} + engines: {node: '>=18'} cpu: [arm64] os: [linux] requiresBuild: true dev: true optional: true - /@esbuild/linux-arm@0.21.5: - resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} - engines: {node: '>=12'} + /@esbuild/linux-arm@0.23.0: + resolution: {integrity: sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw==} + engines: {node: '>=18'} cpu: [arm] os: [linux] requiresBuild: true dev: true optional: true - /@esbuild/linux-ia32@0.21.5: - resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} - engines: {node: '>=12'} + /@esbuild/linux-ia32@0.23.0: + resolution: {integrity: sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==} + engines: {node: '>=18'} cpu: [ia32] os: [linux] requiresBuild: true dev: true optional: true - /@esbuild/linux-loong64@0.21.5: - resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} - engines: {node: '>=12'} + /@esbuild/linux-loong64@0.23.0: + resolution: {integrity: sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A==} + engines: {node: '>=18'} cpu: [loong64] os: [linux] requiresBuild: true dev: true optional: true - /@esbuild/linux-mips64el@0.21.5: - resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} - engines: {node: '>=12'} + /@esbuild/linux-mips64el@0.23.0: + resolution: {integrity: sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w==} + engines: {node: '>=18'} cpu: [mips64el] os: [linux] requiresBuild: true dev: true optional: true - /@esbuild/linux-ppc64@0.21.5: - resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} - engines: {node: '>=12'} + /@esbuild/linux-ppc64@0.23.0: + resolution: {integrity: sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw==} + engines: {node: '>=18'} cpu: [ppc64] os: [linux] requiresBuild: true dev: true optional: true - /@esbuild/linux-riscv64@0.21.5: - resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} - engines: {node: '>=12'} + /@esbuild/linux-riscv64@0.23.0: + resolution: {integrity: sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw==} + engines: {node: '>=18'} cpu: [riscv64] os: [linux] requiresBuild: true dev: true optional: true - /@esbuild/linux-s390x@0.21.5: - resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} - engines: {node: '>=12'} + /@esbuild/linux-s390x@0.23.0: + resolution: {integrity: sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg==} + engines: {node: '>=18'} cpu: [s390x] os: [linux] requiresBuild: true dev: true optional: true - /@esbuild/linux-x64@0.21.5: - resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} - engines: {node: '>=12'} + /@esbuild/linux-x64@0.23.0: + resolution: {integrity: sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ==} + engines: {node: '>=18'} cpu: [x64] os: [linux] requiresBuild: true dev: true optional: true - /@esbuild/netbsd-x64@0.21.5: - resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} - engines: {node: '>=12'} + /@esbuild/netbsd-x64@0.23.0: + resolution: {integrity: sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw==} + engines: {node: '>=18'} cpu: [x64] os: [netbsd] requiresBuild: true dev: true optional: true - /@esbuild/openbsd-x64@0.21.5: - resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} - engines: {node: '>=12'} + /@esbuild/openbsd-arm64@0.23.0: + resolution: {integrity: sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.23.0: + resolution: {integrity: sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg==} + engines: {node: '>=18'} cpu: [x64] os: [openbsd] requiresBuild: true dev: true optional: true - /@esbuild/sunos-x64@0.21.5: - resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} - engines: {node: '>=12'} + /@esbuild/sunos-x64@0.23.0: + resolution: {integrity: sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==} + engines: {node: '>=18'} cpu: [x64] os: [sunos] requiresBuild: true dev: true optional: true - /@esbuild/win32-arm64@0.21.5: - resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} - engines: {node: '>=12'} + /@esbuild/win32-arm64@0.23.0: + resolution: {integrity: sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==} + engines: {node: '>=18'} cpu: [arm64] os: [win32] requiresBuild: true dev: true optional: true - /@esbuild/win32-ia32@0.21.5: - resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} - engines: {node: '>=12'} + /@esbuild/win32-ia32@0.23.0: + resolution: {integrity: sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==} + engines: {node: '>=18'} cpu: [ia32] os: [win32] requiresBuild: true dev: true optional: true - /@esbuild/win32-x64@0.21.5: - resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} - engines: {node: '>=12'} + /@esbuild/win32-x64@0.23.0: + resolution: {integrity: sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==} + engines: {node: '>=18'} cpu: [x64] os: [win32] requiresBuild: true @@ -1493,7 +1417,7 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: ajv: 6.12.6 - debug: 4.3.6 + debug: 4.3.6(supports-color@8.1.1) espree: 9.6.1 globals: 13.24.0 ignore: 5.3.1 @@ -1525,7 +1449,7 @@ packages: engines: {node: '>=10.10.0'} dependencies: '@humanwhocodes/object-schema': 2.0.2 - debug: 4.3.6 + debug: 4.3.6(supports-color@8.1.1) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -1573,7 +1497,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 22.0.0 + '@types/node': 22.4.2 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -1594,14 +1518,14 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.0.0 + '@types/node': 22.4.2 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@22.0.0)(ts-node@10.9.2) + jest-config: 29.7.0(@types/node@22.4.2)(ts-node@10.9.2) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -1629,7 +1553,7 @@ packages: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.0.0 + '@types/node': 22.4.2 jest-mock: 29.7.0 dev: true @@ -1656,7 +1580,7 @@ packages: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 22.0.0 + '@types/node': 22.4.2 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -1689,7 +1613,7 @@ packages: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.20 - '@types/node': 22.0.0 + '@types/node': 22.4.2 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit: 0.1.2 @@ -1777,7 +1701,7 @@ packages: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 22.0.0 + '@types/node': 22.4.2 '@types/yargs': 17.0.31 chalk: 4.1.2 dev: true @@ -1848,9 +1772,9 @@ packages: resolution: {integrity: sha512-tgyEl6dvtLln8XX81JyBvWjIiEcjTkUwZbrM5dIobTmoqMuGewSyk9CClno8qsMsFdB5T3jC91Rjeqmu/6xk2g==} dependencies: '@metaplex-foundation/beet': 0.7.2 - '@solana/web3.js': 1.95.1 + '@solana/web3.js': 1.95.3 bs58: 5.0.0 - debug: 4.3.6 + debug: 4.3.6(supports-color@8.1.1) transitivePeerDependencies: - bufferutil - encoding @@ -1862,7 +1786,7 @@ packages: resolution: {integrity: sha512-/6o32FNUtwK8tjhotrvU/vorP7umBuRFvBZrC6XCk51aKidBHe5LPVPA5AjGPbV3oftMfRuXPNd9yAGeEqeCDQ==} dependencies: '@metaplex-foundation/beet': 0.7.2 - '@solana/web3.js': 1.95.1 + '@solana/web3.js': 1.95.2 bs58: 5.0.0 debug: 4.3.4 transitivePeerDependencies: @@ -1900,7 +1824,7 @@ packages: '@metaplex-foundation/beet': 0.7.2 '@metaplex-foundation/beet-solana': 0.3.1 '@metaplex-foundation/rustbin': 0.3.5 - '@solana/web3.js': 1.95.1 + '@solana/web3.js': 1.95.2 ansi-colors: 4.1.3 camelcase: 6.3.0 debug: 4.3.4 @@ -1915,42 +1839,6 @@ packages: - utf-8-validate dev: true - /@metaplex-foundation/umi-options@0.8.9: - resolution: {integrity: sha512-jSQ61sZMPSAk/TXn8v8fPqtz3x8d0/blVZXLLbpVbo2/T5XobiI6/MfmlUosAjAUaQl6bHRF8aIIqZEFkJiy4A==} - dev: false - - /@metaplex-foundation/umi-public-keys@0.8.9: - resolution: {integrity: sha512-CxMzN7dgVGOq9OcNCJe2casKUpJ3RmTVoOvDFyeoTQuK+vkZ1YSSahbqC1iGuHEtKTLSjtWjKvUU6O7zWFTw3Q==} - dependencies: - '@metaplex-foundation/umi-serializers-encodings': 0.8.9 - dev: false - - /@metaplex-foundation/umi-serializers-core@0.8.9: - resolution: {integrity: sha512-WT82tkiYJ0Qmscp7uTj1Hz6aWQPETwaKLAENAUN5DeWghkuBKtuxyBKVvEOuoXerJSdhiAk0e8DWA4cxcTTQ/w==} - dev: false - - /@metaplex-foundation/umi-serializers-encodings@0.8.9: - resolution: {integrity: sha512-N3VWLDTJ0bzzMKcJDL08U3FaqRmwlN79FyE4BHj6bbAaJ9LEHjDQ9RJijZyWqTm0jE7I750fU7Ow5EZL38Xi6Q==} - dependencies: - '@metaplex-foundation/umi-serializers-core': 0.8.9 - dev: false - - /@metaplex-foundation/umi-serializers-numbers@0.8.9: - resolution: {integrity: sha512-NtBf1fnVNQJHFQjLFzRu2i9GGnigb9hOm/Gfrk628d0q0tRJB7BOM3bs5C61VAs7kJs4yd+pDNVAERJkknQ7Lg==} - dependencies: - '@metaplex-foundation/umi-serializers-core': 0.8.9 - dev: false - - /@metaplex-foundation/umi-serializers@0.8.9: - resolution: {integrity: sha512-Sve8Etm3zqvLSUfza+MYRkjTnCpiaAFT7VWdqeHzA3n58P0AfT3p74RrZwVt/UFkxI+ln8BslwBDJmwzcPkuHw==} - dependencies: - '@metaplex-foundation/umi-options': 0.8.9 - '@metaplex-foundation/umi-public-keys': 0.8.9 - '@metaplex-foundation/umi-serializers-core': 0.8.9 - '@metaplex-foundation/umi-serializers-encodings': 0.8.9 - '@metaplex-foundation/umi-serializers-numbers': 0.8.9 - dev: false - /@noble/curves@1.4.2: resolution: {integrity: sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==} dependencies: @@ -1993,7 +1881,7 @@ packages: engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} dev: true - /@rollup/plugin-alias@5.1.0(rollup@4.19.1): + /@rollup/plugin-alias@5.1.0(rollup@4.21.0): resolution: {integrity: sha512-lpA3RZ9PdIG7qqhEfv79tBffNaoDuukFDrmhLqg9ifv99u/ehn+lOg30x2zmhf8AQqQUZaMk/B9fZraQ6/acDQ==} engines: {node: '>=14.0.0'} peerDependencies: @@ -2002,11 +1890,11 @@ packages: rollup: optional: true dependencies: - rollup: 4.19.1 + rollup: 4.21.0 slash: 4.0.0 dev: true - /@rollup/plugin-commonjs@26.0.1(rollup@4.19.1): + /@rollup/plugin-commonjs@26.0.1(rollup@4.21.0): resolution: {integrity: sha512-UnsKoZK6/aGIH6AdkptXhNvhaqftcjq3zZdT+LY5Ftms6JR06nADcDsYp5hTU9E2lbJUEOhdlY5J4DNTneM+jQ==} engines: {node: '>=16.0.0 || 14 >= 14.17'} peerDependencies: @@ -2015,16 +1903,16 @@ packages: rollup: optional: true dependencies: - '@rollup/pluginutils': 5.1.0(rollup@4.19.1) + '@rollup/pluginutils': 5.1.0(rollup@4.21.0) commondir: 1.0.1 estree-walker: 2.0.2 glob: 10.4.1 is-reference: 1.2.1 magic-string: 0.30.10 - rollup: 4.19.1 + rollup: 4.21.0 dev: true - /@rollup/plugin-json@6.1.0(rollup@4.19.1): + /@rollup/plugin-json@6.1.0(rollup@4.21.0): resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==} engines: {node: '>=14.0.0'} peerDependencies: @@ -2033,11 +1921,11 @@ packages: rollup: optional: true dependencies: - '@rollup/pluginutils': 5.1.0(rollup@4.19.1) - rollup: 4.19.1 + '@rollup/pluginutils': 5.1.0(rollup@4.21.0) + rollup: 4.21.0 dev: true - /@rollup/plugin-multi-entry@6.0.1(rollup@4.19.1): + /@rollup/plugin-multi-entry@6.0.1(rollup@4.21.0): resolution: {integrity: sha512-AXm6toPyTSfbYZWghQGbom1Uh7dHXlrGa+HoiYNhQtDUE3Q7LqoUYdVQx9E1579QWS1uOiu+cZRSE4okO7ySgw==} engines: {node: '>=14.0.0'} peerDependencies: @@ -2046,12 +1934,12 @@ packages: rollup: optional: true dependencies: - '@rollup/plugin-virtual': 3.0.2(rollup@4.19.1) + '@rollup/plugin-virtual': 3.0.2(rollup@4.21.0) matched: 5.0.1 - rollup: 4.19.1 + rollup: 4.21.0 dev: true - /@rollup/plugin-node-resolve@15.2.3(rollup@4.19.1): + /@rollup/plugin-node-resolve@15.2.3(rollup@4.21.0): resolution: {integrity: sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==} engines: {node: '>=14.0.0'} peerDependencies: @@ -2060,16 +1948,16 @@ packages: rollup: optional: true dependencies: - '@rollup/pluginutils': 5.0.5(rollup@4.19.1) + '@rollup/pluginutils': 5.0.5(rollup@4.21.0) '@types/resolve': 1.20.2 deepmerge: 4.3.1 is-builtin-module: 3.2.1 is-module: 1.0.0 resolve: 1.22.8 - rollup: 4.19.1 + rollup: 4.21.0 dev: true - /@rollup/plugin-terser@0.4.4(rollup@4.19.1): + /@rollup/plugin-terser@0.4.4(rollup@4.21.0): resolution: {integrity: sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==} engines: {node: '>=14.0.0'} peerDependencies: @@ -2078,13 +1966,13 @@ packages: rollup: optional: true dependencies: - rollup: 4.19.1 + rollup: 4.21.0 serialize-javascript: 6.0.1 smob: 1.4.1 terser: 5.24.0 dev: true - /@rollup/plugin-typescript@11.1.6(rollup@4.19.1)(tslib@2.6.3)(typescript@5.5.4): + /@rollup/plugin-typescript@11.1.6(rollup@4.21.0)(tslib@2.6.3)(typescript@5.5.4): resolution: {integrity: sha512-R92yOmIACgYdJ7dJ97p4K69I8gg6IEHt8M7dUBxN3W6nrO8uUxX5ixl0yU/N3aZTi8WhPuICvOHXQvF6FaykAA==} engines: {node: '>=14.0.0'} peerDependencies: @@ -2097,14 +1985,14 @@ packages: tslib: optional: true dependencies: - '@rollup/pluginutils': 5.1.0(rollup@4.19.1) + '@rollup/pluginutils': 5.1.0(rollup@4.21.0) resolve: 1.22.8 - rollup: 4.19.1 + rollup: 4.21.0 tslib: 2.6.3 typescript: 5.5.4 dev: true - /@rollup/plugin-virtual@3.0.2(rollup@4.19.1): + /@rollup/plugin-virtual@3.0.2(rollup@4.21.0): resolution: {integrity: sha512-10monEYsBp3scM4/ND4LNH5Rxvh3e/cVeL3jWTgZ2SrQ+BmUoQcopVQvnaMcOnykb1VkxUFuDAN+0FnpTFRy2A==} engines: {node: '>=14.0.0'} peerDependencies: @@ -2113,7 +2001,7 @@ packages: rollup: optional: true dependencies: - rollup: 4.19.1 + rollup: 4.21.0 dev: true /@rollup/pluginutils@4.2.1: @@ -2124,7 +2012,7 @@ packages: picomatch: 2.3.1 dev: true - /@rollup/pluginutils@5.0.5(rollup@4.19.1): + /@rollup/pluginutils@5.0.5(rollup@4.21.0): resolution: {integrity: sha512-6aEYR910NyP73oHiJglti74iRyOwgFU4x3meH/H8OJx6Ry0j6cOVZ5X/wTvub7G7Ao6qaHBEaNsV3GLJkSsF+Q==} engines: {node: '>=14.0.0'} peerDependencies: @@ -2136,10 +2024,10 @@ packages: '@types/estree': 1.0.5 estree-walker: 2.0.2 picomatch: 2.3.1 - rollup: 4.19.1 + rollup: 4.21.0 dev: true - /@rollup/pluginutils@5.1.0(rollup@4.19.1): + /@rollup/pluginutils@5.1.0(rollup@4.21.0): resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==} engines: {node: '>=14.0.0'} peerDependencies: @@ -2151,131 +2039,131 @@ packages: '@types/estree': 1.0.5 estree-walker: 2.0.2 picomatch: 2.3.1 - rollup: 4.19.1 + rollup: 4.21.0 dev: true - /@rollup/rollup-android-arm-eabi@4.19.1: - resolution: {integrity: sha512-XzqSg714++M+FXhHfXpS1tDnNZNpgxxuGZWlRG/jSj+VEPmZ0yg6jV4E0AL3uyBKxO8mO3xtOsP5mQ+XLfrlww==} + /@rollup/rollup-android-arm-eabi@4.21.0: + resolution: {integrity: sha512-WTWD8PfoSAJ+qL87lE7votj3syLavxunWhzCnx3XFxFiI/BA/r3X7MUM8dVrH8rb2r4AiO8jJsr3ZjdaftmnfA==} cpu: [arm] os: [android] requiresBuild: true dev: true optional: true - /@rollup/rollup-android-arm64@4.19.1: - resolution: {integrity: sha512-thFUbkHteM20BGShD6P08aungq4irbIZKUNbG70LN8RkO7YztcGPiKTTGZS7Kw+x5h8hOXs0i4OaHwFxlpQN6A==} + /@rollup/rollup-android-arm64@4.21.0: + resolution: {integrity: sha512-a1sR2zSK1B4eYkiZu17ZUZhmUQcKjk2/j9Me2IDjk1GHW7LB5Z35LEzj9iJch6gtUfsnvZs1ZNyDW2oZSThrkA==} cpu: [arm64] os: [android] requiresBuild: true dev: true optional: true - /@rollup/rollup-darwin-arm64@4.19.1: - resolution: {integrity: sha512-8o6eqeFZzVLia2hKPUZk4jdE3zW7LCcZr+MD18tXkgBBid3lssGVAYuox8x6YHoEPDdDa9ixTaStcmx88lio5Q==} + /@rollup/rollup-darwin-arm64@4.21.0: + resolution: {integrity: sha512-zOnKWLgDld/svhKO5PD9ozmL6roy5OQ5T4ThvdYZLpiOhEGY+dp2NwUmxK0Ld91LrbjrvtNAE0ERBwjqhZTRAA==} cpu: [arm64] os: [darwin] requiresBuild: true dev: true optional: true - /@rollup/rollup-darwin-x64@4.19.1: - resolution: {integrity: sha512-4T42heKsnbjkn7ovYiAdDVRRWZLU9Kmhdt6HafZxFcUdpjlBlxj4wDrt1yFWLk7G4+E+8p2C9tcmSu0KA6auGA==} + /@rollup/rollup-darwin-x64@4.21.0: + resolution: {integrity: sha512-7doS8br0xAkg48SKE2QNtMSFPFUlRdw9+votl27MvT46vo44ATBmdZdGysOevNELmZlfd+NEa0UYOA8f01WSrg==} cpu: [x64] os: [darwin] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm-gnueabihf@4.19.1: - resolution: {integrity: sha512-MXg1xp+e5GhZ3Vit1gGEyoC+dyQUBy2JgVQ+3hUrD9wZMkUw/ywgkpK7oZgnB6kPpGrxJ41clkPPnsknuD6M2Q==} + /@rollup/rollup-linux-arm-gnueabihf@4.21.0: + resolution: {integrity: sha512-pWJsfQjNWNGsoCq53KjMtwdJDmh/6NubwQcz52aEwLEuvx08bzcy6tOUuawAOncPnxz/3siRtd8hiQ32G1y8VA==} cpu: [arm] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm-musleabihf@4.19.1: - resolution: {integrity: sha512-DZNLwIY4ftPSRVkJEaxYkq7u2zel7aah57HESuNkUnz+3bZHxwkCUkrfS2IWC1sxK6F2QNIR0Qr/YXw7nkF3Pw==} + /@rollup/rollup-linux-arm-musleabihf@4.21.0: + resolution: {integrity: sha512-efRIANsz3UHZrnZXuEvxS9LoCOWMGD1rweciD6uJQIx2myN3a8Im1FafZBzh7zk1RJ6oKcR16dU3UPldaKd83w==} cpu: [arm] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm64-gnu@4.19.1: - resolution: {integrity: sha512-C7evongnjyxdngSDRRSQv5GvyfISizgtk9RM+z2biV5kY6S/NF/wta7K+DanmktC5DkuaJQgoKGf7KUDmA7RUw==} + /@rollup/rollup-linux-arm64-gnu@4.21.0: + resolution: {integrity: sha512-ZrPhydkTVhyeGTW94WJ8pnl1uroqVHM3j3hjdquwAcWnmivjAwOYjTEAuEDeJvGX7xv3Z9GAvrBkEzCgHq9U1w==} cpu: [arm64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-arm64-musl@4.19.1: - resolution: {integrity: sha512-89tFWqxfxLLHkAthAcrTs9etAoBFRduNfWdl2xUs/yLV+7XDrJ5yuXMHptNqf1Zw0UCA3cAutkAiAokYCkaPtw==} + /@rollup/rollup-linux-arm64-musl@4.21.0: + resolution: {integrity: sha512-cfaupqd+UEFeURmqNP2eEvXqgbSox/LHOyN9/d2pSdV8xTrjdg3NgOFJCtc1vQ/jEke1qD0IejbBfxleBPHnPw==} cpu: [arm64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-powerpc64le-gnu@4.19.1: - resolution: {integrity: sha512-PromGeV50sq+YfaisG8W3fd+Cl6mnOOiNv2qKKqKCpiiEke2KiKVyDqG/Mb9GWKbYMHj5a01fq/qlUR28PFhCQ==} + /@rollup/rollup-linux-powerpc64le-gnu@4.21.0: + resolution: {integrity: sha512-ZKPan1/RvAhrUylwBXC9t7B2hXdpb/ufeu22pG2psV7RN8roOfGurEghw1ySmX/CmDDHNTDDjY3lo9hRlgtaHg==} cpu: [ppc64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-riscv64-gnu@4.19.1: - resolution: {integrity: sha512-/1BmHYh+iz0cNCP0oHCuF8CSiNj0JOGf0jRlSo3L/FAyZyG2rGBuKpkZVH9YF+x58r1jgWxvm1aRg3DHrLDt6A==} + /@rollup/rollup-linux-riscv64-gnu@4.21.0: + resolution: {integrity: sha512-H1eRaCwd5E8eS8leiS+o/NqMdljkcb1d6r2h4fKSsCXQilLKArq6WS7XBLDu80Yz+nMqHVFDquwcVrQmGr28rg==} cpu: [riscv64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-s390x-gnu@4.19.1: - resolution: {integrity: sha512-0cYP5rGkQWRZKy9/HtsWVStLXzCF3cCBTRI+qRL8Z+wkYlqN7zrSYm6FuY5Kd5ysS5aH0q5lVgb/WbG4jqXN1Q==} + /@rollup/rollup-linux-s390x-gnu@4.21.0: + resolution: {integrity: sha512-zJ4hA+3b5tu8u7L58CCSI0A9N1vkfwPhWd/puGXwtZlsB5bTkwDNW/+JCU84+3QYmKpLi+XvHdmrlwUwDA6kqw==} cpu: [s390x] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-x64-gnu@4.19.1: - resolution: {integrity: sha512-XUXeI9eM8rMP8aGvii/aOOiMvTs7xlCosq9xCjcqI9+5hBxtjDpD+7Abm1ZhVIFE1J2h2VIg0t2DX/gjespC2Q==} + /@rollup/rollup-linux-x64-gnu@4.21.0: + resolution: {integrity: sha512-e2hrvElFIh6kW/UNBQK/kzqMNY5mO+67YtEh9OA65RM5IJXYTWiXjX6fjIiPaqOkBthYF1EqgiZ6OXKcQsM0hg==} cpu: [x64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-linux-x64-musl@4.19.1: - resolution: {integrity: sha512-V7cBw/cKXMfEVhpSvVZhC+iGifD6U1zJ4tbibjjN+Xi3blSXaj/rJynAkCFFQfoG6VZrAiP7uGVzL440Q6Me2Q==} + /@rollup/rollup-linux-x64-musl@4.21.0: + resolution: {integrity: sha512-1vvmgDdUSebVGXWX2lIcgRebqfQSff0hMEkLJyakQ9JQUbLDkEaMsPTLOmyccyC6IJ/l3FZuJbmrBw/u0A0uCQ==} cpu: [x64] os: [linux] requiresBuild: true dev: true optional: true - /@rollup/rollup-win32-arm64-msvc@4.19.1: - resolution: {integrity: sha512-88brja2vldW/76jWATlBqHEoGjJLRnP0WOEKAUbMcXaAZnemNhlAHSyj4jIwMoP2T750LE9lblvD4e2jXleZsA==} + /@rollup/rollup-win32-arm64-msvc@4.21.0: + resolution: {integrity: sha512-s5oFkZ/hFcrlAyBTONFY1TWndfyre1wOMwU+6KCpm/iatybvrRgmZVM+vCFwxmC5ZhdlgfE0N4XorsDpi7/4XQ==} cpu: [arm64] os: [win32] requiresBuild: true dev: true optional: true - /@rollup/rollup-win32-ia32-msvc@4.19.1: - resolution: {integrity: sha512-LdxxcqRVSXi6k6JUrTah1rHuaupoeuiv38du8Mt4r4IPer3kwlTo+RuvfE8KzZ/tL6BhaPlzJ3835i6CxrFIRQ==} + /@rollup/rollup-win32-ia32-msvc@4.21.0: + resolution: {integrity: sha512-G9+TEqRnAA6nbpqyUqgTiopmnfgnMkR3kMukFBDsiyy23LZvUCpiUwjTRx6ezYCjJODXrh52rBR9oXvm+Fp5wg==} cpu: [ia32] os: [win32] requiresBuild: true dev: true optional: true - /@rollup/rollup-win32-x64-msvc@4.19.1: - resolution: {integrity: sha512-2bIrL28PcK3YCqD9anGxDxamxdiJAxA+l7fWIwM5o8UqNy1t3d1NdAweO2XhA0KTDJ5aH1FsuiT5+7VhtHliXg==} + /@rollup/rollup-win32-x64-msvc@4.21.0: + resolution: {integrity: sha512-2jsCDZwtQvRhejHLfZ1JY6w6kEuEtfF9nzYsZxzSlNVKDX+DpsDJ+Rbjkm74nvg2rdx0gwBS+IMdvwJuq3S9pQ==} cpu: [x64] os: [win32] requiresBuild: true @@ -2321,15 +2209,27 @@ packages: '@sinonjs/commons': 3.0.0 dev: true - /@solana/addresses@2.0.0-experimental.21e994f: - resolution: {integrity: sha512-zmg+ALhjxZApKJKSjeGK7EgMT9NywdvGKlAjyNL2fieiFWp0lRTBmWyjPBCQQGdJjBkayCscq3GQkDF2MhC6fg==} + /@solana/addresses@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4): + resolution: {integrity: sha512-g31KrLZdECjAKceShlGoYxnWDmEVklpjPs8xOtnj/HWupEk+Mds4vtmTACTAeJkWZW/3x+z0aexMtO86MKA47g==} + peerDependencies: + typescript: '>=5' dependencies: - '@metaplex-foundation/umi-serializers': 0.8.9 - '@solana/assertions': 2.0.0-experimental.21e994f + '@solana/assertions': 2.0.0-rc.1(typescript@5.5.4) + '@solana/codecs-core': 2.0.0-rc.1(typescript@5.5.4) + '@solana/codecs-strings': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/errors': 2.0.0-rc.1(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder dev: false - /@solana/assertions@2.0.0-experimental.21e994f: - resolution: {integrity: sha512-iGOUpOqkqxzQ/xi4Q3YLiBQPASiQ43NYTalmQm99hmOhySRA4+yyQTmMW1PJ8FAm7Zf86cCiYTf19Exa7+DxoQ==} + /@solana/assertions@2.0.0-rc.1(typescript@5.5.4): + resolution: {integrity: sha512-dvxYCUB7ftZa5lWcsyMYLsGm204H6yVN8Q3ngluMG0rhTtScMBRklVg7Vs39ISwJOkJWJPGToaZ7DjNJ83bm1Q==} + peerDependencies: + typescript: '>=5' + dependencies: + '@solana/errors': 2.0.0-rc.1(typescript@5.5.4) + typescript: 5.5.4 dev: false /@solana/buffer-layout-utils@0.2.0: @@ -2337,7 +2237,7 @@ packages: engines: {node: '>= 10'} dependencies: '@solana/buffer-layout': 4.0.1 - '@solana/web3.js': 1.95.1 + '@solana/web3.js': 1.95.3 bigint-buffer: 1.1.5 bignumber.js: 9.1.2 transitivePeerDependencies: @@ -2356,12 +2256,12 @@ packages: dependencies: '@solana/errors': 2.0.0-preview.2 - /@solana/codecs-core@2.0.0-preview.4(typescript@5.5.4): - resolution: {integrity: sha512-A0VVuDDA5kNKZUinOqHxJQK32aKTucaVbvn31YenGzHX1gPqq+SOnFwgaEY6pq4XEopSmaK16w938ZQS8IvCnw==} + /@solana/codecs-core@2.0.0-rc.1(typescript@5.5.4): + resolution: {integrity: sha512-bauxqMfSs8EHD0JKESaNmNuNvkvHSuN3bbWAF5RjOfDu2PugxHrvRebmYauvSumZ3cTfQ4HJJX6PG5rN852qyQ==} peerDependencies: typescript: '>=5' dependencies: - '@solana/errors': 2.0.0-preview.4(typescript@5.5.4) + '@solana/errors': 2.0.0-rc.1(typescript@5.5.4) typescript: 5.5.4 /@solana/codecs-data-structures@2.0.0-preview.2: @@ -2371,14 +2271,14 @@ packages: '@solana/codecs-numbers': 2.0.0-preview.2 '@solana/errors': 2.0.0-preview.2 - /@solana/codecs-data-structures@2.0.0-preview.4(typescript@5.5.4): - resolution: {integrity: sha512-nt2k2eTeyzlI/ccutPcG36M/J8NAYfxBPI9h/nQjgJ+M+IgOKi31JV8StDDlG/1XvY0zyqugV3I0r3KAbZRJpA==} + /@solana/codecs-data-structures@2.0.0-rc.1(typescript@5.5.4): + resolution: {integrity: sha512-rinCv0RrAVJ9rE/rmaibWJQxMwC5lSaORSZuwjopSUE6T0nb/MVg6Z1siNCXhh/HFTOg0l8bNvZHgBcN/yvXog==} peerDependencies: typescript: '>=5' dependencies: - '@solana/codecs-core': 2.0.0-preview.4(typescript@5.5.4) - '@solana/codecs-numbers': 2.0.0-preview.4(typescript@5.5.4) - '@solana/errors': 2.0.0-preview.4(typescript@5.5.4) + '@solana/codecs-core': 2.0.0-rc.1(typescript@5.5.4) + '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.5.4) + '@solana/errors': 2.0.0-rc.1(typescript@5.5.4) typescript: 5.5.4 dev: false @@ -2388,13 +2288,13 @@ packages: '@solana/codecs-core': 2.0.0-preview.2 '@solana/errors': 2.0.0-preview.2 - /@solana/codecs-numbers@2.0.0-preview.4(typescript@5.5.4): - resolution: {integrity: sha512-Q061rLtMadsO7uxpguT+Z7G4UHnjQ6moVIxAQxR58nLxDPCC7MB1Pk106/Z7NDhDLHTcd18uO6DZ7ajHZEn2XQ==} + /@solana/codecs-numbers@2.0.0-rc.1(typescript@5.5.4): + resolution: {integrity: sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ==} peerDependencies: typescript: '>=5' dependencies: - '@solana/codecs-core': 2.0.0-preview.4(typescript@5.5.4) - '@solana/errors': 2.0.0-preview.4(typescript@5.5.4) + '@solana/codecs-core': 2.0.0-rc.1(typescript@5.5.4) + '@solana/errors': 2.0.0-rc.1(typescript@5.5.4) typescript: 5.5.4 /@solana/codecs-strings@2.0.0-preview.2(fastestsmallesttextencoderdecoder@1.0.22): @@ -2407,15 +2307,15 @@ packages: '@solana/errors': 2.0.0-preview.2 fastestsmallesttextencoderdecoder: 1.0.22 - /@solana/codecs-strings@2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4): - resolution: {integrity: sha512-YDbsQePRWm+xnrfS64losSGRg8Wb76cjK1K6qfR8LPmdwIC3787x9uW5/E4icl/k+9nwgbIRXZ65lpF+ucZUnw==} + /@solana/codecs-strings@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4): + resolution: {integrity: sha512-9/wPhw8TbGRTt6mHC4Zz1RqOnuPTqq1Nb4EyuvpZ39GW6O2t2Q7Q0XxiB3+BdoEjwA2XgPw6e2iRfvYgqty44g==} peerDependencies: fastestsmallesttextencoderdecoder: ^1.0.22 typescript: '>=5' dependencies: - '@solana/codecs-core': 2.0.0-preview.4(typescript@5.5.4) - '@solana/codecs-numbers': 2.0.0-preview.4(typescript@5.5.4) - '@solana/errors': 2.0.0-preview.4(typescript@5.5.4) + '@solana/codecs-core': 2.0.0-rc.1(typescript@5.5.4) + '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.5.4) + '@solana/errors': 2.0.0-rc.1(typescript@5.5.4) fastestsmallesttextencoderdecoder: 1.0.22 typescript: 5.5.4 @@ -2430,16 +2330,16 @@ packages: transitivePeerDependencies: - fastestsmallesttextencoderdecoder - /@solana/codecs@2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4): - resolution: {integrity: sha512-gLMupqI4i+G4uPi2SGF/Tc1aXcviZF2ybC81x7Q/fARamNSgNOCUUoSCg9nWu1Gid6+UhA7LH80sWI8XjKaRog==} + /@solana/codecs@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4): + resolution: {integrity: sha512-qxoR7VybNJixV51L0G1RD2boZTcxmwUWnKCaJJExQ5qNKwbpSyDdWfFJfM5JhGyKe9DnPVOZB+JHWXnpbZBqrQ==} peerDependencies: typescript: '>=5' dependencies: - '@solana/codecs-core': 2.0.0-preview.4(typescript@5.5.4) - '@solana/codecs-data-structures': 2.0.0-preview.4(typescript@5.5.4) - '@solana/codecs-numbers': 2.0.0-preview.4(typescript@5.5.4) - '@solana/codecs-strings': 2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) - '@solana/options': 2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/codecs-core': 2.0.0-rc.1(typescript@5.5.4) + '@solana/codecs-data-structures': 2.0.0-rc.1(typescript@5.5.4) + '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.5.4) + '@solana/codecs-strings': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/options': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) typescript: 5.5.4 transitivePeerDependencies: - fastestsmallesttextencoderdecoder @@ -2452,8 +2352,8 @@ packages: chalk: 5.3.0 commander: 12.1.0 - /@solana/errors@2.0.0-preview.4(typescript@5.5.4): - resolution: {integrity: sha512-kadtlbRv2LCWr8A9V22On15Us7Nn8BvqNaOB4hXsTB3O0fU40D1ru2l+cReqLcRPij4znqlRzW9Xi0m6J5DIhA==} + /@solana/errors@2.0.0-rc.1(typescript@5.5.4): + resolution: {integrity: sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ==} hasBin: true peerDependencies: typescript: '>=5' @@ -2486,7 +2386,7 @@ packages: typescript: 5.5.4 dev: true - /@solana/eslint-config-solana@3.0.3(@typescript-eslint/eslint-plugin@7.17.0)(@typescript-eslint/parser@7.17.0)(eslint-plugin-jest@28.6.0)(eslint-plugin-react-hooks@4.6.0)(eslint-plugin-simple-import-sort@12.1.1)(eslint-plugin-sort-keys-fix@1.1.2)(eslint-plugin-typescript-sort-keys@3.2.0)(eslint@8.57.0)(typescript@5.5.4): + /@solana/eslint-config-solana@3.0.3(@typescript-eslint/eslint-plugin@8.2.0)(@typescript-eslint/parser@8.2.0)(eslint-plugin-jest@28.8.0)(eslint-plugin-react-hooks@4.6.0)(eslint-plugin-simple-import-sort@12.1.1)(eslint-plugin-sort-keys-fix@1.1.2)(eslint-plugin-typescript-sort-keys@3.2.0)(eslint@8.57.0)(typescript@5.5.4): resolution: {integrity: sha512-yTaeCbOBwjmK4oUkknixOpwOzzAK8+4YWvJEJFNHuueESetieDnAeEHV7rzJllFgHEWa9nXps9Q3aD4/XJp71A==} peerDependencies: '@typescript-eslint/eslint-plugin': ^6.0.0 @@ -2499,29 +2399,32 @@ packages: eslint-plugin-typescript-sort-keys: ^3.2.0 typescript: ^5.1.6 dependencies: - '@typescript-eslint/eslint-plugin': 7.17.0(@typescript-eslint/parser@7.17.0)(eslint@8.57.0)(typescript@5.5.4) - '@typescript-eslint/parser': 7.17.0(eslint@8.57.0)(typescript@5.5.4) + '@typescript-eslint/eslint-plugin': 8.2.0(@typescript-eslint/parser@8.2.0)(eslint@8.57.0)(typescript@5.5.4) + '@typescript-eslint/parser': 8.2.0(eslint@8.57.0)(typescript@5.5.4) eslint: 8.57.0 - eslint-plugin-jest: 28.6.0(@typescript-eslint/eslint-plugin@7.17.0)(eslint@8.57.0)(jest@29.7.0)(typescript@5.5.4) + eslint-plugin-jest: 28.8.0(@typescript-eslint/eslint-plugin@8.2.0)(eslint@8.57.0)(jest@29.7.0)(typescript@5.5.4) eslint-plugin-react-hooks: 4.6.0(eslint@8.57.0) eslint-plugin-simple-import-sort: 12.1.1(eslint@8.57.0) eslint-plugin-sort-keys-fix: 1.1.2 - eslint-plugin-typescript-sort-keys: 3.2.0(@typescript-eslint/parser@7.17.0)(eslint@8.57.0)(typescript@5.5.4) + eslint-plugin-typescript-sort-keys: 3.2.0(@typescript-eslint/parser@8.2.0)(eslint@8.57.0)(typescript@5.5.4) typescript: 5.5.4 dev: true - /@solana/functional@2.0.0-experimental.21e994f: - resolution: {integrity: sha512-FMXFiTA+hsc9FCv0r47oF7njq/K9x7zh0H+To7tpeqwN65LtJPu5BMG7xZY3rn5TrudgKw6XPuIr3ARbI8+IWA==} - dev: false - - /@solana/instructions@2.0.0-experimental.21e994f: - resolution: {integrity: sha512-PuJJzvT7wtwE5UcGavUppnfVWnoxL8CPhZBb96HpOaQhQ2JuyhN445bfav5KkaUMCE6ubrVzOEqzrbtygD3aBg==} + /@solana/functional@2.0.0-rc.1(typescript@5.5.4): + resolution: {integrity: sha512-BmedS5o8HTlU8/NA22I6urJqat9QYIw0oH6rKdMMBisDwX7MtgJhe38W8iLP7QCcxoJeS4526qaD8uD62+Pheg==} + peerDependencies: + typescript: '>=5' + dependencies: + typescript: 5.5.4 dev: false - /@solana/keys@2.0.0-experimental.21e994f: - resolution: {integrity: sha512-Qsm7ARy69PdIuis7TZy8ELyhq0pcRFPXtaZ8vLFUvsukrcWRowiJ8JJs6Q3tA+gQK5vUn9ABp7a7Qs0FHzgbyw==} + /@solana/instructions@2.0.0-rc.1(typescript@5.5.4): + resolution: {integrity: sha512-zkfL4WBHPbkMrYsuGZc/sekPa/oALIVvVGUw/gwAervMeLZ34cWCUE6WC2uUUh+bq3OFq0/FSFhAg2YbHHDyUw==} + peerDependencies: + typescript: '>=5' dependencies: - '@solana/assertions': 2.0.0-experimental.21e994f + '@solana/errors': 2.0.0-rc.1(typescript@5.5.4) + typescript: 5.5.4 dev: false /@solana/options@2.0.0-preview.2: @@ -2530,16 +2433,16 @@ packages: '@solana/codecs-core': 2.0.0-preview.2 '@solana/codecs-numbers': 2.0.0-preview.2 - /@solana/options@2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4): - resolution: {integrity: sha512-tv2O/Frxql/wSe3jbzi5nVicIWIus/BftH+5ZR+r9r3FO0/htEllZS5Q9XdbmSboHu+St87584JXeDx3xm4jaA==} + /@solana/options@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4): + resolution: {integrity: sha512-mLUcR9mZ3qfHlmMnREdIFPf9dpMc/Bl66tLSOOWxw4ml5xMT2ohFn7WGqoKcu/UHkT9CrC6+amEdqCNvUqI7AA==} peerDependencies: typescript: '>=5' dependencies: - '@solana/codecs-core': 2.0.0-preview.4(typescript@5.5.4) - '@solana/codecs-data-structures': 2.0.0-preview.4(typescript@5.5.4) - '@solana/codecs-numbers': 2.0.0-preview.4(typescript@5.5.4) - '@solana/codecs-strings': 2.0.0-preview.4(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) - '@solana/errors': 2.0.0-preview.4(typescript@5.5.4) + '@solana/codecs-core': 2.0.0-rc.1(typescript@5.5.4) + '@solana/codecs-data-structures': 2.0.0-rc.1(typescript@5.5.4) + '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.5.4) + '@solana/codecs-strings': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/errors': 2.0.0-rc.1(typescript@5.5.4) typescript: 5.5.4 transitivePeerDependencies: - fastestsmallesttextencoderdecoder @@ -2553,23 +2456,22 @@ packages: prettier: 3.3.3 dev: true - /@solana/rpc-core@2.0.0-experimental.21e994f: - resolution: {integrity: sha512-T7VcTLRi4dsqmpFYdnvcHZFS8Vcgdi6funMUrXcM7ofQqb8vWGJnlX6AX0eIZiVsmoYk5Ki8wW4D6Ul6bXZyZg==} - dependencies: - '@metaplex-foundation/umi-serializers': 0.8.9 - dev: false - - /@solana/rpc-transport@2.0.0-experimental.21e994f(node-fetch@2.7.0)(ws@8.14.2): - resolution: {integrity: sha512-PfGPzRuEodhfLyOD8ZneYQ389SWYgmj1Q/HWQZo8yZMsiAaW/lqCygoW88lecxXKlZF5gJYrBX154kgvGqEM7g==} + /@solana/rpc-types@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4): + resolution: {integrity: sha512-EcGx9VXqA0+uYEdaa1lKTaGBVxLyNL8nkecE4GkqQ+ntRyYlNBPecd4b8siQGSleUQa+Tk/VSPUawSkHqNTLug==} peerDependencies: - node-fetch: ^2.6.7 - ws: ^8.14.0 + typescript: '>=5' dependencies: - node-fetch: 2.7.0 - ws: 8.14.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@solana/addresses': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/codecs-core': 2.0.0-rc.1(typescript@5.5.4) + '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.5.4) + '@solana/codecs-strings': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/errors': 2.0.0-rc.1(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder dev: false - /@solana/spl-token-group@0.0.4(@solana/web3.js@1.95.1)(fastestsmallesttextencoderdecoder@1.0.22): + /@solana/spl-token-group@0.0.4(@solana/web3.js@1.95.3)(fastestsmallesttextencoderdecoder@1.0.22): resolution: {integrity: sha512-7+80nrEMdUKlK37V6kOe024+T7J4nNss0F8LQ9OOPYdWCCfJmsGUzVx2W3oeizZR4IHM6N4yC9v1Xqwc3BTPWw==} engines: {node: '>=16'} peerDependencies: @@ -2577,11 +2479,11 @@ packages: dependencies: '@solana/codecs': 2.0.0-preview.2(fastestsmallesttextencoderdecoder@1.0.22) '@solana/spl-type-length-value': 0.1.0 - '@solana/web3.js': 1.95.1 + '@solana/web3.js': 1.95.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - /@solana/spl-token-metadata@0.1.4(@solana/web3.js@1.95.1)(fastestsmallesttextencoderdecoder@1.0.22): + /@solana/spl-token-metadata@0.1.4(@solana/web3.js@1.95.3)(fastestsmallesttextencoderdecoder@1.0.22): resolution: {integrity: sha512-N3gZ8DlW6NWDV28+vCCDJoTqaCZiF/jDUnk3o8GRkAFzHObiR60Bs1gXHBa8zCPdvOwiG6Z3dg5pg7+RW6XNsQ==} engines: {node: '>=16'} peerDependencies: @@ -2589,11 +2491,11 @@ packages: dependencies: '@solana/codecs': 2.0.0-preview.2(fastestsmallesttextencoderdecoder@1.0.22) '@solana/spl-type-length-value': 0.1.0 - '@solana/web3.js': 1.95.1 + '@solana/web3.js': 1.95.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - /@solana/spl-token@0.4.6(@solana/web3.js@1.95.1)(fastestsmallesttextencoderdecoder@1.0.22): + /@solana/spl-token@0.4.6(@solana/web3.js@1.95.3)(fastestsmallesttextencoderdecoder@1.0.22): resolution: {integrity: sha512-1nCnUqfHVtdguFciVWaY/RKcQz1IF4b31jnKgAmjU9QVN1q7dRUkTEWJZgTYIEtsULjVnC9jRqlhgGN39WbKKA==} engines: {node: '>=16'} peerDependencies: @@ -2601,9 +2503,9 @@ packages: dependencies: '@solana/buffer-layout': 4.0.1 '@solana/buffer-layout-utils': 0.2.0 - '@solana/spl-token-group': 0.0.4(@solana/web3.js@1.95.1)(fastestsmallesttextencoderdecoder@1.0.22) - '@solana/spl-token-metadata': 0.1.4(@solana/web3.js@1.95.1)(fastestsmallesttextencoderdecoder@1.0.22) - '@solana/web3.js': 1.95.1 + '@solana/spl-token-group': 0.0.4(@solana/web3.js@1.95.3)(fastestsmallesttextencoderdecoder@1.0.22) + '@solana/spl-token-metadata': 0.1.4(@solana/web3.js@1.95.3)(fastestsmallesttextencoderdecoder@1.0.22) + '@solana/web3.js': 1.95.3 buffer: 6.0.3 transitivePeerDependencies: - bufferutil @@ -2617,16 +2519,26 @@ packages: dependencies: buffer: 6.0.3 - /@solana/transactions@2.0.0-experimental.21e994f: - resolution: {integrity: sha512-DunbTMBzlC7jmTzkFsRm5DhGe+MjaZ8m+SJ7V520mQq+kxrbPrRmI3ikfUVdejg0WaEV4Dy+RwQ5xllsrJ47kA==} + /@solana/transaction-messages@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4): + resolution: {integrity: sha512-pZTetOtRDwfuK/fyE8FKbtRsLQOTgEIQld3tskB85npUHaEgrnCYzp3nJtMhKOLel3w3f/27VtWLNSrRyyAiew==} + peerDependencies: + typescript: '>=5' dependencies: - '@metaplex-foundation/umi-serializers': 0.8.9 - '@solana/addresses': 2.0.0-experimental.21e994f - '@solana/keys': 2.0.0-experimental.21e994f + '@solana/addresses': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + '@solana/codecs-core': 2.0.0-rc.1(typescript@5.5.4) + '@solana/codecs-data-structures': 2.0.0-rc.1(typescript@5.5.4) + '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.5.4) + '@solana/errors': 2.0.0-rc.1(typescript@5.5.4) + '@solana/functional': 2.0.0-rc.1(typescript@5.5.4) + '@solana/instructions': 2.0.0-rc.1(typescript@5.5.4) + '@solana/rpc-types': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder dev: false - /@solana/web3.js@1.95.1: - resolution: {integrity: sha512-mRX/AjV6QbiOXpWcy5Rz1ZWEH2lVkwO7T0pcv9t97ACpv3/i3tPiqXwk0JIZgSR3wOSTiT26JfygnJH2ulS6dQ==} + /@solana/web3.js@1.95.2: + resolution: {integrity: sha512-SjlHp0G4qhuhkQQc+YXdGkI8EerCqwxvgytMgBpzMUQTafrkNant3e7pgilBGgjy/iM40ICvWBLgASTPMrQU7w==} dependencies: '@babel/runtime': 7.24.8 '@noble/curves': 1.4.2 @@ -2648,22 +2560,28 @@ packages: - encoding - utf-8-validate - /@solana/web3.js@2.0.0-experimental.21e994f(node-fetch@2.7.0)(ws@8.14.2): - resolution: {integrity: sha512-Yy0D57nlNTDm0BhBRIM85Sn52T6vjxpBRRdwE/FOJJmN92n0Qpc4mTAwOPfEqoVpiTcluUBZ4l8FAWxjGCFMgQ==} - dependencies: - '@metaplex-foundation/umi-serializers': 0.8.9 - '@solana/addresses': 2.0.0-experimental.21e994f - '@solana/functional': 2.0.0-experimental.21e994f - '@solana/instructions': 2.0.0-experimental.21e994f - '@solana/keys': 2.0.0-experimental.21e994f - '@solana/rpc-core': 2.0.0-experimental.21e994f - '@solana/rpc-transport': 2.0.0-experimental.21e994f(node-fetch@2.7.0)(ws@8.14.2) - '@solana/transactions': 2.0.0-experimental.21e994f + /@solana/web3.js@1.95.3: + resolution: {integrity: sha512-O6rPUN0w2fkNqx/Z3QJMB9L225Ex10PRDH8bTaIUPZXMPV0QP8ZpPvjQnXK+upUczlRgzHzd6SjKIha1p+I6og==} + dependencies: + '@babel/runtime': 7.25.0 + '@noble/curves': 1.4.2 + '@noble/hashes': 1.4.0 + '@solana/buffer-layout': 4.0.1 + agentkeepalive: 4.5.0 + bigint-buffer: 1.1.5 + 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 transitivePeerDependencies: - - node-fetch - - ws - dev: false + - bufferutil + - encoding + - utf-8-validate /@swc/helpers@0.5.11: resolution: {integrity: sha512-YNlnKRWF2sVojTpIyzwou9XoTNbzbzONwRhOoniEioF1AtaitTvVZblaQRrAzChWQ1bLYyYSWzM18y4WwgzJ+A==} @@ -2718,29 +2636,23 @@ packages: /@types/bn.js@5.1.5: resolution: {integrity: sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==} dependencies: - '@types/node': 22.0.0 + '@types/node': 22.4.2 dev: true /@types/chai-as-promised@7.1.8: resolution: {integrity: sha512-ThlRVIJhr69FLlh6IctTXFkmhtP3NpMZ2QGq69StYLyKZFp/HOp1VdKZj7RvfNWYYcJ1xlbLGLLWj1UvP5u/Gw==} dependencies: - '@types/chai': 4.3.16 + '@types/chai': 4.3.17 dev: true - /@types/chai@4.3.16: - resolution: {integrity: sha512-PatH4iOdyh3MyWtmHVFXLWCCIhUbopaltqddG9BzB+gMIzee2MJrvd+jouii9Z3wzQJruGWAm7WOMjgfG8hQlQ==} + /@types/chai@4.3.17: + resolution: {integrity: sha512-zmZ21EWzR71B4Sscphjief5djsLre50M6lI622OSySTmn9DB3j+C3kWroHfBQWXbOBwbgg/M8CG/hUxDLIloow==} dev: true /@types/connect@3.4.38: resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} dependencies: - '@types/node': 22.0.0 - - /@types/eslint-plugin-prettier@3.1.3: - resolution: {integrity: sha512-Jk+lgBZM0u9ETUyqe2HvykL1j6uE7L1oEqbInWtsVHT6sP+AdwLb4ncmQBqASTS+YzYOPF6hKX0zgEhblRMjqA==} - dependencies: - '@types/eslint': 8.56.7 - dev: true + '@types/node': 22.4.2 /@types/eslint@8.56.7: resolution: {integrity: sha512-SjDvI/x3zsZnOkYZ3lCt9lOZWZLB2jIlNKz+LBgCtDurK0JZcwucxYHn1w2BJkD34dgX9Tjnak0txtq4WTggEA==} @@ -2756,7 +2668,7 @@ packages: /@types/graceful-fs@4.1.9: resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} dependencies: - '@types/node': 22.0.0 + '@types/node': 22.4.2 dev: true /@types/istanbul-lib-coverage@2.0.6: @@ -2797,17 +2709,17 @@ packages: /@types/node-fetch@2.6.11: resolution: {integrity: sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==} dependencies: - '@types/node': 22.0.0 + '@types/node': 22.4.2 form-data: 4.0.0 dev: true /@types/node@12.20.55: resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - /@types/node@22.0.0: - resolution: {integrity: sha512-VT7KSYudcPOzP5Q0wfbowyNLaVR8QWUdw+088uFWwfvpY6uCWaXpqV6ieLAu9WBcnTa7H4Z5RLK8I5t2FuOcqw==} + /@types/node@22.4.2: + resolution: {integrity: sha512-nAvM3Ey230/XzxtyDcJ+VjvlzpzoHwLsF7JaDRfoI0ytO0mVheerNmM45CtA0yOILXwXXxOrcUWH3wltX+7PSw==} dependencies: - undici-types: 6.11.1 + undici-types: 6.19.6 /@types/resolve@1.20.2: resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} @@ -2827,12 +2739,12 @@ packages: /@types/ws@7.4.7: resolution: {integrity: sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==} dependencies: - '@types/node': 22.0.0 + '@types/node': 22.4.2 /@types/ws@8.5.10: resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==} dependencies: - '@types/node': 22.0.0 + '@types/node': 22.4.2 /@types/yargs-parser@21.0.3: resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} @@ -2861,7 +2773,7 @@ packages: '@typescript-eslint/type-utils': 6.21.0(eslint@8.57.0)(typescript@5.5.4) '@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.5.4) '@typescript-eslint/visitor-keys': 6.21.0 - debug: 4.3.6 + debug: 4.3.6(supports-color@8.1.1) eslint: 8.57.0 graphemer: 1.4.0 ignore: 5.3.1 @@ -2873,23 +2785,23 @@ packages: - supports-color dev: true - /@typescript-eslint/eslint-plugin@7.17.0(@typescript-eslint/parser@7.17.0)(eslint@8.57.0)(typescript@5.5.4): - resolution: {integrity: sha512-pyiDhEuLM3PuANxH7uNYan1AaFs5XE0zw1hq69JBvGvE7gSuEoQl1ydtEe/XQeoC3GQxLXyOVa5kNOATgM638A==} - engines: {node: ^18.18.0 || >=20.0.0} + /@typescript-eslint/eslint-plugin@8.2.0(@typescript-eslint/parser@8.2.0)(eslint@8.57.0)(typescript@5.5.4): + resolution: {integrity: sha512-02tJIs655em7fvt9gps/+4k4OsKULYGtLBPJfOsmOq1+3cdClYiF0+d6mHu6qDnTcg88wJBkcPLpQhq7FyDz0A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^7.0.0 - eslint: ^8.56.0 + '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 + eslint: ^8.57.0 || ^9.0.0 typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: '@eslint-community/regexpp': 4.10.0 - '@typescript-eslint/parser': 7.17.0(eslint@8.57.0)(typescript@5.5.4) - '@typescript-eslint/scope-manager': 7.17.0 - '@typescript-eslint/type-utils': 7.17.0(eslint@8.57.0)(typescript@5.5.4) - '@typescript-eslint/utils': 7.17.0(eslint@8.57.0)(typescript@5.5.4) - '@typescript-eslint/visitor-keys': 7.17.0 + '@typescript-eslint/parser': 8.2.0(eslint@8.57.0)(typescript@5.5.4) + '@typescript-eslint/scope-manager': 8.2.0 + '@typescript-eslint/type-utils': 8.2.0(eslint@8.57.0)(typescript@5.5.4) + '@typescript-eslint/utils': 8.2.0(eslint@8.57.0)(typescript@5.5.4) + '@typescript-eslint/visitor-keys': 8.2.0 eslint: 8.57.0 graphemer: 1.4.0 ignore: 5.3.1 @@ -2927,28 +2839,28 @@ packages: '@typescript-eslint/types': 6.21.0 '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.5.4) '@typescript-eslint/visitor-keys': 6.21.0 - debug: 4.3.6 + debug: 4.3.6(supports-color@8.1.1) eslint: 8.57.0 typescript: 5.5.4 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/parser@7.17.0(eslint@8.57.0)(typescript@5.5.4): - resolution: {integrity: sha512-puiYfGeg5Ydop8eusb/Hy1k7QmOU6X3nvsqCgzrB2K4qMavK//21+PzNE8qeECgNOIoertJPUC1SpegHDI515A==} - engines: {node: ^18.18.0 || >=20.0.0} + /@typescript-eslint/parser@8.2.0(eslint@8.57.0)(typescript@5.5.4): + resolution: {integrity: sha512-j3Di+o0lHgPrb7FxL3fdEy6LJ/j2NE8u+AP/5cQ9SKb+JLH6V6UHDqJ+e0hXBkHP1wn1YDFjYCS9LBQsZDlDEg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.56.0 + eslint: ^8.57.0 || ^9.0.0 typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: - '@typescript-eslint/scope-manager': 7.17.0 - '@typescript-eslint/types': 7.17.0 - '@typescript-eslint/typescript-estree': 7.17.0(typescript@5.5.4) - '@typescript-eslint/visitor-keys': 7.17.0 - debug: 4.3.5(supports-color@8.1.1) + '@typescript-eslint/scope-manager': 8.2.0 + '@typescript-eslint/types': 8.2.0 + '@typescript-eslint/typescript-estree': 8.2.0(typescript@5.5.4) + '@typescript-eslint/visitor-keys': 8.2.0 + debug: 4.3.6(supports-color@8.1.1) eslint: 8.57.0 typescript: 5.5.4 transitivePeerDependencies: @@ -2971,28 +2883,20 @@ packages: '@typescript-eslint/visitor-keys': 6.21.0 dev: true - /@typescript-eslint/scope-manager@7.12.0: - resolution: {integrity: sha512-itF1pTnN6F3unPak+kutH9raIkL3lhH1YRPGgt7QQOh43DQKVJXmWkpb+vpc/TiDHs6RSd9CTbDsc/Y+Ygq7kg==} - engines: {node: ^18.18.0 || >=20.0.0} - dependencies: - '@typescript-eslint/types': 7.12.0 - '@typescript-eslint/visitor-keys': 7.12.0 - dev: true - - /@typescript-eslint/scope-manager@7.16.0: - resolution: {integrity: sha512-8gVv3kW6n01Q6TrI1cmTZ9YMFi3ucDT7i7aI5lEikk2ebk1AEjrwX8MDTdaX5D7fPXMBLvnsaa0IFTAu+jcfOw==} - engines: {node: ^18.18.0 || >=20.0.0} + /@typescript-eslint/scope-manager@8.0.0: + resolution: {integrity: sha512-V0aa9Csx/ZWWv2IPgTfY7T4agYwJyILESu/PVqFtTFz9RIS823mAze+NbnBI8xiwdX3iqeQbcTYlvB04G9wyQw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} dependencies: - '@typescript-eslint/types': 7.16.0 - '@typescript-eslint/visitor-keys': 7.16.0 + '@typescript-eslint/types': 8.0.0 + '@typescript-eslint/visitor-keys': 8.0.0 dev: true - /@typescript-eslint/scope-manager@7.17.0: - resolution: {integrity: sha512-0P2jTTqyxWp9HiKLu/Vemr2Rg1Xb5B7uHItdVZ6iAenXmPo4SZ86yOPCJwMqpCyaMiEHTNqizHfsbmCFT1x9SA==} - engines: {node: ^18.18.0 || >=20.0.0} + /@typescript-eslint/scope-manager@8.2.0: + resolution: {integrity: sha512-OFn80B38yD6WwpoHU2Tz/fTz7CgFqInllBoC3WP+/jLbTb4gGPTy9HBSTsbDWkMdN55XlVU0mMDYAtgvlUspGw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} dependencies: - '@typescript-eslint/types': 7.17.0 - '@typescript-eslint/visitor-keys': 7.17.0 + '@typescript-eslint/types': 8.2.0 + '@typescript-eslint/visitor-keys': 8.2.0 dev: true /@typescript-eslint/type-utils@6.21.0(eslint@8.57.0)(typescript@5.5.4): @@ -3007,7 +2911,7 @@ packages: dependencies: '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.5.4) '@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.5.4) - debug: 4.3.6 + debug: 4.3.6(supports-color@8.1.1) eslint: 8.57.0 ts-api-utils: 1.3.0(typescript@5.5.4) typescript: 5.5.4 @@ -3015,23 +2919,22 @@ packages: - supports-color dev: true - /@typescript-eslint/type-utils@7.17.0(eslint@8.57.0)(typescript@5.5.4): - resolution: {integrity: sha512-XD3aaBt+orgkM/7Cei0XNEm1vwUxQ958AOLALzPlbPqb8C1G8PZK85tND7Jpe69Wualri81PLU+Zc48GVKIMMA==} - engines: {node: ^18.18.0 || >=20.0.0} + /@typescript-eslint/type-utils@8.2.0(eslint@8.57.0)(typescript@5.5.4): + resolution: {integrity: sha512-g1CfXGFMQdT5S+0PSO0fvGXUaiSkl73U1n9LTK5aRAFnPlJ8dLKkXr4AaLFvPedW8lVDoMgLLE3JN98ZZfsj0w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.56.0 typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: - '@typescript-eslint/typescript-estree': 7.17.0(typescript@5.5.4) - '@typescript-eslint/utils': 7.17.0(eslint@8.57.0)(typescript@5.5.4) - debug: 4.3.6 - eslint: 8.57.0 + '@typescript-eslint/typescript-estree': 8.2.0(typescript@5.5.4) + '@typescript-eslint/utils': 8.2.0(eslint@8.57.0)(typescript@5.5.4) + debug: 4.3.6(supports-color@8.1.1) ts-api-utils: 1.3.0(typescript@5.5.4) typescript: 5.5.4 transitivePeerDependencies: + - eslint - supports-color dev: true @@ -3045,19 +2948,14 @@ packages: engines: {node: ^16.0.0 || >=18.0.0} dev: true - /@typescript-eslint/types@7.12.0: - resolution: {integrity: sha512-o+0Te6eWp2ppKY3mLCU+YA9pVJxhUJE15FV7kxuD9jgwIAa+w/ycGJBMrYDTpVGUM/tgpa9SeMOugSabWFq7bg==} - engines: {node: ^18.18.0 || >=20.0.0} + /@typescript-eslint/types@8.0.0: + resolution: {integrity: sha512-wgdSGs9BTMWQ7ooeHtu5quddKKs5Z5dS+fHLbrQI+ID0XWJLODGMHRfhwImiHoeO2S5Wir2yXuadJN6/l4JRxw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} dev: true - /@typescript-eslint/types@7.16.0: - resolution: {integrity: sha512-fecuH15Y+TzlUutvUl9Cc2XJxqdLr7+93SQIbcZfd4XRGGKoxyljK27b+kxKamjRkU7FYC6RrbSCg0ALcZn/xw==} - engines: {node: ^18.18.0 || >=20.0.0} - dev: true - - /@typescript-eslint/types@7.17.0: - resolution: {integrity: sha512-a29Ir0EbyKTKHnZWbNsrc/gqfIBqYPwj3F2M+jWE/9bqfEHg0AMtXzkbUkOG6QgEScxh2+Pz9OXe11jHDnHR7A==} - engines: {node: ^18.18.0 || >=20.0.0} + /@typescript-eslint/types@8.2.0: + resolution: {integrity: sha512-6a9QSK396YqmiBKPkJtxsgZZZVjYQ6wQ/TlI0C65z7vInaETuC6HAHD98AGLC8DyIPqHytvNuS8bBVvNLKyqvQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} dev: true /@typescript-eslint/typescript-estree@5.62.0(typescript@5.5.4): @@ -3071,7 +2969,7 @@ packages: dependencies: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/visitor-keys': 5.62.0 - debug: 4.3.6 + debug: 4.3.6(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 semver: 7.6.0 @@ -3092,7 +2990,7 @@ packages: dependencies: '@typescript-eslint/types': 6.21.0 '@typescript-eslint/visitor-keys': 6.21.0 - debug: 4.3.6 + debug: 4.3.6(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.3 @@ -3103,18 +3001,18 @@ packages: - supports-color dev: true - /@typescript-eslint/typescript-estree@7.12.0(typescript@5.5.4): - resolution: {integrity: sha512-5bwqLsWBULv1h6pn7cMW5dXX/Y2amRqLaKqsASVwbBHMZSnHqE/HN4vT4fE0aFsiwxYvr98kqOWh1a8ZKXalCQ==} - engines: {node: ^18.18.0 || >=20.0.0} + /@typescript-eslint/typescript-estree@8.0.0(typescript@5.5.4): + resolution: {integrity: sha512-5b97WpKMX+Y43YKi4zVcCVLtK5F98dFls3Oxui8LbnmRsseKenbbDinmvxrWegKDMmlkIq/XHuyy0UGLtpCDKg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: - '@typescript-eslint/types': 7.12.0 - '@typescript-eslint/visitor-keys': 7.12.0 - debug: 4.3.6 + '@typescript-eslint/types': 8.0.0 + '@typescript-eslint/visitor-keys': 8.0.0 + debug: 4.3.6(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.5 @@ -3125,40 +3023,18 @@ packages: - supports-color dev: true - /@typescript-eslint/typescript-estree@7.16.0(typescript@5.5.4): - resolution: {integrity: sha512-a5NTvk51ZndFuOLCh5OaJBELYc2O3Zqxfl3Js78VFE1zE46J2AaVuW+rEbVkQznjkmlzWsUI15BG5tQMixzZLw==} - engines: {node: ^18.18.0 || >=20.0.0} + /@typescript-eslint/typescript-estree@8.2.0(typescript@5.5.4): + resolution: {integrity: sha512-kiG4EDUT4dImplOsbh47B1QnNmXSoUqOjWDvCJw/o8LgfD0yr7k2uy54D5Wm0j4t71Ge1NkynGhpWdS0dEIAUA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '*' peerDependenciesMeta: typescript: optional: true dependencies: - '@typescript-eslint/types': 7.16.0 - '@typescript-eslint/visitor-keys': 7.16.0 - debug: 4.3.6 - globby: 11.1.0 - is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.6.0 - ts-api-utils: 1.3.0(typescript@5.5.4) - typescript: 5.5.4 - transitivePeerDependencies: - - supports-color - dev: true - - /@typescript-eslint/typescript-estree@7.17.0(typescript@5.5.4): - resolution: {integrity: sha512-72I3TGq93t2GoSBWI093wmKo0n6/b7O4j9o8U+f65TVD0FS6bI2180X5eGEr8MA8PhKMvYe9myZJquUT2JkCZw==} - engines: {node: ^18.18.0 || >=20.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/types': 7.17.0 - '@typescript-eslint/visitor-keys': 7.17.0 - debug: 4.3.6 + '@typescript-eslint/types': 8.2.0 + '@typescript-eslint/visitor-keys': 8.2.0 + debug: 4.3.6(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.5 @@ -3208,48 +3084,32 @@ packages: - typescript dev: true - /@typescript-eslint/utils@7.12.0(eslint@8.57.0)(typescript@5.5.4): - resolution: {integrity: sha512-Y6hhwxwDx41HNpjuYswYp6gDbkiZ8Hin9Bf5aJQn1bpTs3afYY4GX+MPYxma8jtoIV2GRwTM/UJm/2uGCVv+DQ==} - engines: {node: ^18.18.0 || >=20.0.0} - peerDependencies: - eslint: ^8.56.0 - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@typescript-eslint/scope-manager': 7.12.0 - '@typescript-eslint/types': 7.12.0 - '@typescript-eslint/typescript-estree': 7.12.0(typescript@5.5.4) - eslint: 8.57.0 - transitivePeerDependencies: - - supports-color - - typescript - dev: true - - /@typescript-eslint/utils@7.16.0(eslint@8.57.0)(typescript@5.5.4): - resolution: {integrity: sha512-PqP4kP3hb4r7Jav+NiRCntlVzhxBNWq6ZQ+zQwII1y/G/1gdIPeYDCKr2+dH6049yJQsWZiHU6RlwvIFBXXGNA==} - engines: {node: ^18.18.0 || >=20.0.0} + /@typescript-eslint/utils@8.0.0(eslint@8.57.0)(typescript@5.5.4): + resolution: {integrity: sha512-k/oS/A/3QeGLRvOWCg6/9rATJL5rec7/5s1YmdS0ZU6LHveJyGFwBvLhSRBv6i9xaj7etmosp+l+ViN1I9Aj/Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.56.0 + eslint: ^8.57.0 || ^9.0.0 dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@typescript-eslint/scope-manager': 7.16.0 - '@typescript-eslint/types': 7.16.0 - '@typescript-eslint/typescript-estree': 7.16.0(typescript@5.5.4) + '@typescript-eslint/scope-manager': 8.0.0 + '@typescript-eslint/types': 8.0.0 + '@typescript-eslint/typescript-estree': 8.0.0(typescript@5.5.4) eslint: 8.57.0 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/utils@7.17.0(eslint@8.57.0)(typescript@5.5.4): - resolution: {integrity: sha512-r+JFlm5NdB+JXc7aWWZ3fKSm1gn0pkswEwIYsrGPdsT2GjsRATAKXiNtp3vgAAO1xZhX8alIOEQnNMl3kbTgJw==} - engines: {node: ^18.18.0 || >=20.0.0} + /@typescript-eslint/utils@8.2.0(eslint@8.57.0)(typescript@5.5.4): + resolution: {integrity: sha512-O46eaYKDlV3TvAVDNcoDzd5N550ckSe8G4phko++OCSC1dYIb9LTc3HDGYdWqWIAT5qDUKphO6sd9RrpIJJPfg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.56.0 + eslint: ^8.57.0 || ^9.0.0 dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@typescript-eslint/scope-manager': 7.17.0 - '@typescript-eslint/types': 7.17.0 - '@typescript-eslint/typescript-estree': 7.17.0(typescript@5.5.4) + '@typescript-eslint/scope-manager': 8.2.0 + '@typescript-eslint/types': 8.2.0 + '@typescript-eslint/typescript-estree': 8.2.0(typescript@5.5.4) eslint: 8.57.0 transitivePeerDependencies: - supports-color @@ -3272,27 +3132,19 @@ packages: eslint-visitor-keys: 3.4.3 dev: true - /@typescript-eslint/visitor-keys@7.12.0: - resolution: {integrity: sha512-uZk7DevrQLL3vSnfFl5bj4sL75qC9D6EdjemIdbtkuUmIheWpuiiylSY01JxJE7+zGrOWDZrp1WxOuDntvKrHQ==} - engines: {node: ^18.18.0 || >=20.0.0} + /@typescript-eslint/visitor-keys@8.0.0: + resolution: {integrity: sha512-oN0K4nkHuOyF3PVMyETbpP5zp6wfyOvm7tWhTMfoqxSSsPmJIh6JNASuZDlODE8eE+0EB9uar+6+vxr9DBTYOA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} dependencies: - '@typescript-eslint/types': 7.12.0 + '@typescript-eslint/types': 8.0.0 eslint-visitor-keys: 3.4.3 dev: true - /@typescript-eslint/visitor-keys@7.16.0: - resolution: {integrity: sha512-rMo01uPy9C7XxG7AFsxa8zLnWXTF8N3PYclekWSrurvhwiw1eW88mrKiAYe6s53AUY57nTRz8dJsuuXdkAhzCg==} - engines: {node: ^18.18.0 || >=20.0.0} + /@typescript-eslint/visitor-keys@8.2.0: + resolution: {integrity: sha512-sbgsPMW9yLvS7IhCi8IpuK1oBmtbWUNP+hBdwl/I9nzqVsszGnNGti5r9dUtF5RLivHUFFIdRvLiTsPhzSyJ3Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} dependencies: - '@typescript-eslint/types': 7.16.0 - eslint-visitor-keys: 3.4.3 - dev: true - - /@typescript-eslint/visitor-keys@7.17.0: - resolution: {integrity: sha512-RVGC9UhPOCsfCdI9pU++K4nD7to+jTcMIbXTSOcrLqUEW6gF2pU1UUbYJKc9cvcRSK1UDeMJ7pdMxf4bhMpV/A==} - engines: {node: ^18.18.0 || >=20.0.0} - dependencies: - '@typescript-eslint/types': 7.17.0 + '@typescript-eslint/types': 8.2.0 eslint-visitor-keys: 3.4.3 dev: true @@ -3389,7 +3241,7 @@ packages: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} dependencies: - debug: 4.3.6 + debug: 4.3.6(supports-color@8.1.1) transitivePeerDependencies: - supports-color dev: true @@ -4166,7 +4018,7 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dev: true - /create-jest@29.7.0(@types/node@22.0.0)(ts-node@10.9.2): + /create-jest@29.7.0(@types/node@22.4.2)(ts-node@10.9.2): resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -4175,7 +4027,7 @@ packages: chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@22.0.0)(ts-node@10.9.2) + jest-config: 29.7.0(@types/node@22.4.2)(ts-node@10.9.2) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -4255,20 +4107,7 @@ packages: dependencies: ms: 2.1.2 - /debug@4.3.5(supports-color@8.1.1): - resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - dependencies: - ms: 2.1.2 - supports-color: 8.1.1 - dev: true - - /debug@4.3.6: + /debug@4.3.6(supports-color@8.1.1): resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==} engines: {node: '>=6.0'} peerDependencies: @@ -4278,6 +4117,7 @@ packages: optional: true dependencies: ms: 2.1.2 + supports-color: 8.1.1 dev: true /decamelize@4.0.0: @@ -4303,8 +4143,8 @@ packages: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} dev: true - /deepmerge-ts@5.1.0: - resolution: {integrity: sha512-eS8dRJOckyo9maw9Tu5O5RUi/4inFLrnoLkBe3cPfDMx3WZioXtmOew4TXQaxq7Rhl4xjDtR7c6x8nNTxOvbFw==} + /deepmerge-ts@7.1.0: + resolution: {integrity: sha512-q6bNsfNBtgr8ZOQqmZbl94MmYWm+QcDNIkqCxVWiw1vKvf+y/N2dZQKdnDXn4c5Ygt/y63tDof6OCN+2YwWVEg==} engines: {node: '>=16.0.0'} dev: true @@ -4534,35 +4374,36 @@ packages: dependencies: es6-promise: 4.2.8 - /esbuild@0.21.5: - resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} - engines: {node: '>=12'} + /esbuild@0.23.0: + resolution: {integrity: sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==} + engines: {node: '>=18'} hasBin: true requiresBuild: true optionalDependencies: - '@esbuild/aix-ppc64': 0.21.5 - '@esbuild/android-arm': 0.21.5 - '@esbuild/android-arm64': 0.21.5 - '@esbuild/android-x64': 0.21.5 - '@esbuild/darwin-arm64': 0.21.5 - '@esbuild/darwin-x64': 0.21.5 - '@esbuild/freebsd-arm64': 0.21.5 - '@esbuild/freebsd-x64': 0.21.5 - '@esbuild/linux-arm': 0.21.5 - '@esbuild/linux-arm64': 0.21.5 - '@esbuild/linux-ia32': 0.21.5 - '@esbuild/linux-loong64': 0.21.5 - '@esbuild/linux-mips64el': 0.21.5 - '@esbuild/linux-ppc64': 0.21.5 - '@esbuild/linux-riscv64': 0.21.5 - '@esbuild/linux-s390x': 0.21.5 - '@esbuild/linux-x64': 0.21.5 - '@esbuild/netbsd-x64': 0.21.5 - '@esbuild/openbsd-x64': 0.21.5 - '@esbuild/sunos-x64': 0.21.5 - '@esbuild/win32-arm64': 0.21.5 - '@esbuild/win32-ia32': 0.21.5 - '@esbuild/win32-x64': 0.21.5 + '@esbuild/aix-ppc64': 0.23.0 + '@esbuild/android-arm': 0.23.0 + '@esbuild/android-arm64': 0.23.0 + '@esbuild/android-x64': 0.23.0 + '@esbuild/darwin-arm64': 0.23.0 + '@esbuild/darwin-x64': 0.23.0 + '@esbuild/freebsd-arm64': 0.23.0 + '@esbuild/freebsd-x64': 0.23.0 + '@esbuild/linux-arm': 0.23.0 + '@esbuild/linux-arm64': 0.23.0 + '@esbuild/linux-ia32': 0.23.0 + '@esbuild/linux-loong64': 0.23.0 + '@esbuild/linux-mips64el': 0.23.0 + '@esbuild/linux-ppc64': 0.23.0 + '@esbuild/linux-riscv64': 0.23.0 + '@esbuild/linux-s390x': 0.23.0 + '@esbuild/linux-x64': 0.23.0 + '@esbuild/netbsd-x64': 0.23.0 + '@esbuild/openbsd-arm64': 0.23.0 + '@esbuild/openbsd-x64': 0.23.0 + '@esbuild/sunos-x64': 0.23.0 + '@esbuild/win32-arm64': 0.23.0 + '@esbuild/win32-ia32': 0.23.0 + '@esbuild/win32-x64': 0.23.0 dev: true /escalade@3.1.1: @@ -4599,13 +4440,13 @@ packages: eslint: 8.57.0 dev: true - /eslint-config-turbo@2.0.9(eslint@8.57.0): - resolution: {integrity: sha512-FoIMElI8md/dR5DxjB5Om52KJfi7Qf7RInXeE+PGU6lN388rumppwyqEJsZ7vnR5GhGa9cLPt0vNZwEK9iXtKg==} + /eslint-config-turbo@2.0.14(eslint@8.57.0): + resolution: {integrity: sha512-VkzAH/AR1/fjMsqzuurfWkEgyGVTTzfZQB1umDB8dMWzFhqo8p/2KEWbvRQLEvSFxjVfeJl5ErQAJ7h7DYglxg==} peerDependencies: eslint: '>6.6.0' dependencies: eslint: 8.57.0 - eslint-plugin-turbo: 2.0.9(eslint@8.57.0) + eslint-plugin-turbo: 2.0.14(eslint@8.57.0) dev: true /eslint-import-resolver-node@0.3.9: @@ -4618,7 +4459,7 @@ packages: - supports-color dev: true - /eslint-module-utils@2.8.0(@typescript-eslint/parser@7.17.0)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0): + /eslint-module-utils@2.8.0(@typescript-eslint/parser@8.2.0)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0): resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} engines: {node: '>=4'} peerDependencies: @@ -4639,7 +4480,7 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 7.17.0(eslint@8.57.0)(typescript@5.5.4) + '@typescript-eslint/parser': 8.2.0(eslint@8.57.0)(typescript@5.5.4) debug: 3.2.7 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 @@ -4658,29 +4499,29 @@ packages: ignore: 5.2.4 dev: true - /eslint-plugin-functional@6.6.3(eslint@8.57.0)(typescript@5.5.4): - resolution: {integrity: sha512-sVbbvNvwX3HVkXAykKyoNLv57r4DPF7f1sy+/8j4YtzLYVQPGljMUWv3T6Kd4lwnnjmcKuj0EkIbS+knL6P5jw==} - engines: {node: '>=16.10.0'} + /eslint-plugin-functional@7.0.1(eslint@8.57.0)(typescript@5.5.4): + resolution: {integrity: sha512-naT4A+NiYb5TIweR4ibK3SYN+r1Iqd6rWv3VAnLiyWl0zfYxz9hzNsRAPdREOe1aqwMXasdOZQ1nPqvZKC50ZQ==} + engines: {node: '>=v18.18.0'} peerDependencies: - eslint: ^8.0.0 || ^9.0.0 - typescript: '>=4.3.5' + eslint: ^9.0.0 + typescript: '>=4.7.4' peerDependenciesMeta: typescript: optional: true dependencies: - '@typescript-eslint/utils': 7.16.0(eslint@8.57.0)(typescript@5.5.4) - deepmerge-ts: 5.1.0 - escape-string-regexp: 4.0.0 + '@typescript-eslint/utils': 8.0.0(eslint@8.57.0)(typescript@5.5.4) + deepmerge-ts: 7.1.0 + escape-string-regexp: 5.0.0 eslint: 8.57.0 - is-immutable-type: 4.0.0(eslint@8.57.0)(typescript@5.5.4) - semver: 7.6.0 + is-immutable-type: 5.0.0(eslint@8.57.0)(typescript@5.5.4) ts-api-utils: 1.3.0(typescript@5.5.4) + ts-declaration-location: 1.0.4(typescript@5.5.4) typescript: 5.5.4 transitivePeerDependencies: - supports-color dev: true - /eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.17.0)(eslint@8.57.0): + /eslint-plugin-import@2.29.1(@typescript-eslint/parser@8.2.0)(eslint@8.57.0): resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} engines: {node: '>=4'} peerDependencies: @@ -4690,7 +4531,7 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 7.17.0(eslint@8.57.0)(typescript@5.5.4) + '@typescript-eslint/parser': 8.2.0(eslint@8.57.0)(typescript@5.5.4) array-includes: 3.1.7 array.prototype.findlastindex: 1.2.3 array.prototype.flat: 1.3.2 @@ -4699,7 +4540,7 @@ packages: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.17.0)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@8.2.0)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0) hasown: 2.0.0 is-core-module: 2.13.1 is-glob: 4.0.3 @@ -4736,11 +4577,11 @@ packages: - typescript dev: true - /eslint-plugin-jest@28.6.0(@typescript-eslint/eslint-plugin@7.17.0)(eslint@8.57.0)(jest@29.7.0)(typescript@5.5.4): - resolution: {integrity: sha512-YG28E1/MIKwnz+e2H7VwYPzHUYU4aMa19w0yGcwXnnmJH6EfgHahTJ2un3IyraUxNfnz/KUhJAFXNNwWPo12tg==} + /eslint-plugin-jest@28.8.0(@typescript-eslint/eslint-plugin@8.2.0)(eslint@8.57.0)(jest@29.7.0)(typescript@5.5.4): + resolution: {integrity: sha512-Tubj1hooFxCl52G4qQu0edzV/+EZzPUeN8p2NnW5uu4fbDs+Yo7+qDVDc4/oG3FbCqEBmu/OC3LSsyiU22oghw==} engines: {node: ^16.10.0 || ^18.12.0 || >=20.0.0} peerDependencies: - '@typescript-eslint/eslint-plugin': ^6.0.0 || ^7.0.0 + '@typescript-eslint/eslint-plugin': ^6.0.0 || ^7.0.0 || ^8.0.0 eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 jest: '*' peerDependenciesMeta: @@ -4749,10 +4590,10 @@ packages: jest: optional: true dependencies: - '@typescript-eslint/eslint-plugin': 7.17.0(@typescript-eslint/parser@7.17.0)(eslint@8.57.0)(typescript@5.5.4) - '@typescript-eslint/utils': 7.12.0(eslint@8.57.0)(typescript@5.5.4) + '@typescript-eslint/eslint-plugin': 8.2.0(@typescript-eslint/parser@8.2.0)(eslint@8.57.0)(typescript@5.5.4) + '@typescript-eslint/utils': 8.0.0(eslint@8.57.0)(typescript@5.5.4) eslint: 8.57.0 - jest: 29.7.0(@types/node@22.0.0)(ts-node@10.9.2) + jest: 29.7.0(@types/node@22.4.2)(ts-node@10.9.2) transitivePeerDependencies: - supports-color - typescript @@ -4770,7 +4611,7 @@ packages: rambda: 7.5.0 dev: true - /eslint-plugin-prettier@5.2.1(@types/eslint@8.56.7)(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.3.3): + /eslint-plugin-prettier@5.2.1(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.3.3): resolution: {integrity: sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: @@ -4784,7 +4625,6 @@ packages: eslint-config-prettier: optional: true dependencies: - '@types/eslint': 8.56.7 eslint: 8.57.0 eslint-config-prettier: 9.1.0(eslint@8.57.0) prettier: 3.3.3 @@ -4836,8 +4676,8 @@ packages: requireindex: 1.2.0 dev: true - /eslint-plugin-turbo@2.0.9(eslint@8.57.0): - resolution: {integrity: sha512-q4s4mg6JcXzz5zK4LC3c6FcWehGAWjGj7kIM76ZvG0KiR9Ks0znzjnAKW0NoiDP4s/gt3r4YPOpI357qWt167Q==} + /eslint-plugin-turbo@2.0.14(eslint@8.57.0): + resolution: {integrity: sha512-E++MSAEeWZTU0FYARrfakMPq+7XeltqeY4JBDQTzbGEWG3kgYJPeYBMWsypcvBujVihQLlMu0S6ImnfV692mHg==} peerDependencies: eslint: '>6.6.0' dependencies: @@ -4863,7 +4703,7 @@ packages: - supports-color dev: true - /eslint-plugin-typescript-sort-keys@3.2.0(@typescript-eslint/parser@7.17.0)(eslint@8.57.0)(typescript@5.5.4): + /eslint-plugin-typescript-sort-keys@3.2.0(@typescript-eslint/parser@8.2.0)(eslint@8.57.0)(typescript@5.5.4): resolution: {integrity: sha512-GutszvriaVtwmn7pQjuj9/9o0iXhD7XZs0/424+zsozdRr/fdg5e8206t478Vnqnqi1GjuxcAolj1kf74KnhPA==} engines: {node: '>= 16'} peerDependencies: @@ -4872,7 +4712,7 @@ packages: typescript: ^3 || ^4 || ^5 dependencies: '@typescript-eslint/experimental-utils': 5.62.0(eslint@8.57.0)(typescript@5.5.4) - '@typescript-eslint/parser': 7.17.0(eslint@8.57.0)(typescript@5.5.4) + '@typescript-eslint/parser': 8.2.0(eslint@8.57.0)(typescript@5.5.4) eslint: 8.57.0 json-schema: 0.4.0 natural-compare-lite: 1.4.0 @@ -5199,7 +5039,7 @@ packages: dependencies: chalk: 4.1.2 commander: 5.1.0 - debug: 4.3.6 + debug: 4.3.6(supports-color@8.1.1) transitivePeerDependencies: - supports-color dev: true @@ -5252,7 +5092,7 @@ packages: debug: optional: true dependencies: - debug: 4.3.6 + debug: 4.3.6(supports-color@8.1.1) dev: true /for-each@0.3.3: @@ -5605,7 +5445,7 @@ packages: engines: {node: '>= 6'} dependencies: agent-base: 6.0.2 - debug: 4.3.6 + debug: 4.3.6(supports-color@8.1.1) transitivePeerDependencies: - supports-color dev: true @@ -5797,16 +5637,16 @@ packages: is-extglob: 2.1.1 dev: true - /is-immutable-type@4.0.0(eslint@8.57.0)(typescript@5.5.4): - resolution: {integrity: sha512-gyFBCXv+NikTs8/PGZhgjbMmFZQ5jvHGZIsVu6+/9Bk4K7imlWBIDN7hTr9fNioGzFg71I4YM3z8f0aKXarTAw==} + /is-immutable-type@5.0.0(eslint@8.57.0)(typescript@5.5.4): + resolution: {integrity: sha512-mcvHasqbRBWJznuPqqHRKiJgYAz60sZ0mvO3bN70JbkuK7ksfmgc489aKZYxMEjIbRvyOseaTjaRZLRF/xFeRA==} peerDependencies: eslint: '*' typescript: '>=4.7.4' dependencies: - '@typescript-eslint/type-utils': 7.17.0(eslint@8.57.0)(typescript@5.5.4) + '@typescript-eslint/type-utils': 8.2.0(eslint@8.57.0)(typescript@5.5.4) eslint: 8.57.0 ts-api-utils: 1.3.0(typescript@5.5.4) - ts-declaration-location: 1.0.0(typescript@5.5.4) + ts-declaration-location: 1.0.4(typescript@5.5.4) typescript: 5.5.4 transitivePeerDependencies: - supports-color @@ -5984,7 +5824,7 @@ packages: resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} engines: {node: '>=10'} dependencies: - debug: 4.3.6 + debug: 4.3.6(supports-color@8.1.1) istanbul-lib-coverage: 3.2.1 source-map: 0.6.1 transitivePeerDependencies: @@ -6066,7 +5906,7 @@ packages: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.0.0 + '@types/node': 22.4.2 chalk: 4.1.2 co: 4.6.0 dedent: 1.5.1 @@ -6087,7 +5927,7 @@ packages: - supports-color dev: true - /jest-cli@29.7.0(@types/node@22.0.0)(ts-node@10.9.2): + /jest-cli@29.7.0(@types/node@22.4.2)(ts-node@10.9.2): resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -6101,10 +5941,10 @@ packages: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@22.0.0)(ts-node@10.9.2) + create-jest: 29.7.0(@types/node@22.4.2)(ts-node@10.9.2) exit: 0.1.2 import-local: 3.1.0 - jest-config: 29.7.0(@types/node@22.0.0)(ts-node@10.9.2) + jest-config: 29.7.0(@types/node@22.4.2)(ts-node@10.9.2) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -6115,7 +5955,7 @@ packages: - ts-node dev: true - /jest-config@29.7.0(@types/node@22.0.0)(ts-node@10.9.2): + /jest-config@29.7.0(@types/node@22.4.2)(ts-node@10.9.2): resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -6130,7 +5970,7 @@ packages: '@babel/core': 7.23.2 '@jest/test-sequencer': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.0.0 + '@types/node': 22.4.2 babel-jest: 29.7.0(@babel/core@7.23.2) chalk: 4.1.2 ci-info: 3.9.0 @@ -6150,7 +5990,7 @@ packages: pretty-format: 29.7.0 slash: 3.0.0 strip-json-comments: 3.1.1 - ts-node: 10.9.2(@types/node@22.0.0)(typescript@5.5.4) + ts-node: 10.9.2(@types/node@22.4.2)(typescript@5.5.4) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -6191,7 +6031,7 @@ packages: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.0.0 + '@types/node': 22.4.2 jest-mock: 29.7.0 jest-util: 29.7.0 dev: true @@ -6207,7 +6047,7 @@ packages: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.9 - '@types/node': 22.0.0 + '@types/node': 22.4.2 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -6258,7 +6098,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 22.0.0 + '@types/node': 22.4.2 jest-util: 29.7.0 dev: true @@ -6313,7 +6153,7 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.0.0 + '@types/node': 22.4.2 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -6344,7 +6184,7 @@ packages: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.0.0 + '@types/node': 22.4.2 chalk: 4.1.2 cjs-module-lexer: 1.2.3 collect-v8-coverage: 1.0.2 @@ -6396,7 +6236,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/types': 29.6.3 - '@types/node': 22.0.0 + '@types/node': 22.4.2 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -6421,7 +6261,7 @@ packages: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.0.0 + '@types/node': 22.4.2 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -6433,13 +6273,13 @@ packages: resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@types/node': 22.0.0 + '@types/node': 22.4.2 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true - /jest@29.7.0(@types/node@22.0.0)(ts-node@10.9.2): + /jest@29.7.0(@types/node@22.4.2)(ts-node@10.9.2): resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -6452,7 +6292,7 @@ packages: '@jest/core': 29.7.0(ts-node@10.9.2) '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.7.0(@types/node@22.0.0)(ts-node@10.9.2) + jest-cli: 29.7.0(@types/node@22.4.2)(ts-node@10.9.2) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -6865,15 +6705,15 @@ packages: hasBin: true dev: true - /mocha@10.7.0: - resolution: {integrity: sha512-v8/rBWr2VO5YkspYINnvu81inSz2y3ODJrhO175/Exzor1RcEZZkizgE2A+w/CAXXoESS8Kys5E62dOHGHzULA==} + /mocha@10.7.3: + resolution: {integrity: sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A==} engines: {node: '>= 14.0.0'} hasBin: true dependencies: ansi-colors: 4.1.3 browser-stdout: 1.3.1 chokidar: 3.5.3 - debug: 4.3.5(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) diff: 5.2.0 escape-string-regexp: 4.0.0 find-up: 5.0.0 @@ -7452,7 +7292,7 @@ packages: package-json-from-dist: 1.0.0 dev: true - /rollup-plugin-dts@6.1.1(rollup@4.19.1)(typescript@5.5.4): + /rollup-plugin-dts@6.1.1(rollup@4.21.0)(typescript@5.5.4): resolution: {integrity: sha512-aSHRcJ6KG2IHIioYlvAOcEq6U99sVtqDDKVhnwt70rW6tsz3tv5OSjEiWcgzfsHdLyGXZ/3b/7b/+Za3Y6r1XA==} engines: {node: '>=16'} peerDependencies: @@ -7460,35 +7300,35 @@ packages: typescript: ^4.5 || ^5.0 dependencies: magic-string: 0.30.10 - rollup: 4.19.1 + rollup: 4.21.0 typescript: 5.5.4 optionalDependencies: '@babel/code-frame': 7.24.2 dev: true - /rollup@4.19.1: - resolution: {integrity: sha512-K5vziVlg7hTpYfFBI+91zHBEMo6jafYXpkMlqZjg7/zhIG9iHqazBf4xz9AVdjS9BruRn280ROqLI7G3OFRIlw==} + /rollup@4.21.0: + resolution: {integrity: sha512-vo+S/lfA2lMS7rZ2Qoubi6I5hwZwzXeUIctILZLbHI+laNtvhhOIon2S1JksA5UEDQ7l3vberd0fxK44lTYjbQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true dependencies: '@types/estree': 1.0.5 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.19.1 - '@rollup/rollup-android-arm64': 4.19.1 - '@rollup/rollup-darwin-arm64': 4.19.1 - '@rollup/rollup-darwin-x64': 4.19.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.19.1 - '@rollup/rollup-linux-arm-musleabihf': 4.19.1 - '@rollup/rollup-linux-arm64-gnu': 4.19.1 - '@rollup/rollup-linux-arm64-musl': 4.19.1 - '@rollup/rollup-linux-powerpc64le-gnu': 4.19.1 - '@rollup/rollup-linux-riscv64-gnu': 4.19.1 - '@rollup/rollup-linux-s390x-gnu': 4.19.1 - '@rollup/rollup-linux-x64-gnu': 4.19.1 - '@rollup/rollup-linux-x64-musl': 4.19.1 - '@rollup/rollup-win32-arm64-msvc': 4.19.1 - '@rollup/rollup-win32-ia32-msvc': 4.19.1 - '@rollup/rollup-win32-x64-msvc': 4.19.1 + '@rollup/rollup-android-arm-eabi': 4.21.0 + '@rollup/rollup-android-arm64': 4.21.0 + '@rollup/rollup-darwin-arm64': 4.21.0 + '@rollup/rollup-darwin-x64': 4.21.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.21.0 + '@rollup/rollup-linux-arm-musleabihf': 4.21.0 + '@rollup/rollup-linux-arm64-gnu': 4.21.0 + '@rollup/rollup-linux-arm64-musl': 4.21.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.21.0 + '@rollup/rollup-linux-riscv64-gnu': 4.21.0 + '@rollup/rollup-linux-s390x-gnu': 4.21.0 + '@rollup/rollup-linux-x64-gnu': 4.21.0 + '@rollup/rollup-linux-x64-musl': 4.21.0 + '@rollup/rollup-win32-arm64-msvc': 4.21.0 + '@rollup/rollup-win32-ia32-msvc': 4.21.0 + '@rollup/rollup-win32-x64-msvc': 4.21.0 fsevents: 2.3.3 dev: true @@ -7741,7 +7581,7 @@ packages: resolution: {integrity: sha512-TS6vYoO/9YJZng7oiLOVyuz8V7yLow5Hp4SLYWW71XM3702v+z9f1fvUBKudRfa4dfpta4tRNufApSiBIALxJQ==} engines: {node: '>= 10'} dependencies: - '@solana/web3.js': 1.95.1 + '@solana/web3.js': 1.95.3 bs58: 4.0.1 optionalDependencies: solana-bankrun-darwin-arm64: 0.2.0 @@ -7808,7 +7648,7 @@ packages: arg: 5.0.2 bluebird: 3.7.2 check-more-types: 2.24.0 - debug: 4.3.6 + debug: 4.3.6(supports-color@8.1.1) execa: 5.1.1 lazy-ass: 1.6.0 ps-tree: 1.2.0 @@ -8078,12 +7918,12 @@ packages: typescript: 5.5.4 dev: true - /ts-declaration-location@1.0.0(typescript@5.5.4): - resolution: {integrity: sha512-/C+R0e1CLF1KtwbE59IFhCg4jLwfU7Puob+uVIl4iUVQhepN/bHmWXy3Gt3mAgvdLQybEZ4yb4qhnJQbphnEgA==} + /ts-declaration-location@1.0.4(typescript@5.5.4): + resolution: {integrity: sha512-r4JoxYhKULbZuH81Pjrp9OEG5St7XWk7zXwGkLKhmVcjiBVHTJXV5wK6dEa9JKW5QGSTW6b1lOjxAKp8R1SQhg==} peerDependencies: typescript: '>=4.0.0' dependencies: - fast-glob: 3.3.2 + minimatch: 10.0.1 typescript: 5.5.4 dev: true @@ -8093,8 +7933,8 @@ packages: jest-resolve: 29.7.0 dev: true - /ts-jest@29.2.3(@babel/core@7.23.2)(jest@29.7.0)(typescript@5.5.4): - resolution: {integrity: sha512-yCcfVdiBFngVz9/keHin9EnsrQtQtEu3nRykNy9RVp+FiPFFbPJ3Sg6Qg4+TkmH0vMP5qsTKgXSsk80HRwvdgQ==} + /ts-jest@29.2.4(@babel/core@7.23.2)(jest@29.7.0)(typescript@5.5.4): + resolution: {integrity: sha512-3d6tgDyhCI29HlpwIq87sNuI+3Q6GLTTCeYRHCs7vDz+/3GCMwEtV9jezLyl4ZtnBgx00I7hm8PCP8cTksMGrw==} engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -8121,7 +7961,7 @@ packages: bs-logger: 0.2.6 ejs: 3.1.10 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@22.0.0)(ts-node@10.9.2) + jest: 29.7.0(@types/node@22.4.2)(ts-node@10.9.2) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 @@ -8131,7 +7971,7 @@ packages: yargs-parser: 21.1.1 dev: true - /ts-node@10.9.2(@types/node@22.0.0)(typescript@5.5.4): + /ts-node@10.9.2(@types/node@22.4.2)(typescript@5.5.4): resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} hasBin: true peerDependencies: @@ -8150,7 +7990,7 @@ packages: '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 22.0.0 + '@types/node': 22.4.2 acorn: 8.11.2 acorn-walk: 8.3.0 arg: 4.1.3 @@ -8188,75 +8028,75 @@ packages: typescript: 5.5.4 dev: true - /tsx@4.16.2: - resolution: {integrity: sha512-C1uWweJDgdtX2x600HjaFaucXTilT7tgUZHbOE4+ypskZ1OP8CRCSDkCxG6Vya9EwaFIVagWwpaVAn5wzypaqQ==} + /tsx@4.17.0: + resolution: {integrity: sha512-eN4mnDA5UMKDt4YZixo9tBioibaMBpoxBkD+rIPAjVmYERSG0/dWEY1CEFuV89CgASlKL499q8AhmkMnnjtOJg==} engines: {node: '>=18.0.0'} hasBin: true dependencies: - esbuild: 0.21.5 + esbuild: 0.23.0 get-tsconfig: 4.7.5 optionalDependencies: fsevents: 2.3.3 dev: true - /turbo-darwin-64@2.0.9: - resolution: {integrity: sha512-owlGsOaExuVGBUfrnJwjkL1BWlvefjSKczEAcpLx4BI7Oh6ttakOi+JyomkPkFlYElRpjbvlR2gP8WIn6M/+xQ==} + /turbo-darwin-64@2.0.14: + resolution: {integrity: sha512-kwfDmjNwlNfvtrvT29+ZBg5n1Wvxl891bFHchMJyzMoR0HOE9N1NSNdSZb9wG3e7sYNIu4uDkNk+VBEqJW0HzQ==} cpu: [x64] os: [darwin] requiresBuild: true dev: true optional: true - /turbo-darwin-arm64@2.0.9: - resolution: {integrity: sha512-XAXkKkePth5ZPPE/9G9tTnPQx0C8UTkGWmNGYkpmGgRr8NedW+HrPsi9N0HcjzzIH9A4TpNYvtiV+WcwdaEjKA==} + /turbo-darwin-arm64@2.0.14: + resolution: {integrity: sha512-m3LXYEshCx3wc4ZClM6gb01KYpFmtjQ9IBF3A7ofjb6ahux3xlYZJZ3uFCLAGHuvGLuJ3htfiPbwlDPTdknqqw==} cpu: [arm64] os: [darwin] requiresBuild: true dev: true optional: true - /turbo-linux-64@2.0.9: - resolution: {integrity: sha512-l9wSgEjrCFM1aG16zItBsZ206ZlhSSx1owB8Cgskfv0XyIXRGHRkluihiaxkp+UeU5WoEfz4EN5toc+ICA0q0w==} + /turbo-linux-64@2.0.14: + resolution: {integrity: sha512-7vBzCPdoTtR92SNn2JMgj1FlMmyonGmpMaQdgAB1OVYtuQ6NVGoh7/lODfaILqXjpvmFSVbpBIDrKOT6EvcprQ==} cpu: [x64] os: [linux] requiresBuild: true dev: true optional: true - /turbo-linux-arm64@2.0.9: - resolution: {integrity: sha512-gRnjxXRne18B27SwxXMqL3fJu7jw/8kBrOBTBNRSmZZiG1Uu3nbnP7b4lgrA/bCku6C0Wligwqurvtpq6+nFHA==} + /turbo-linux-arm64@2.0.14: + resolution: {integrity: sha512-jwH+c0bfjpBf26K/tdEFatmnYyXwGROjbr6bZmNcL8R+IkGAc/cglL+OToqJnQZTgZvH7uDGbeSyUo7IsHyjuA==} cpu: [arm64] os: [linux] requiresBuild: true dev: true optional: true - /turbo-windows-64@2.0.9: - resolution: {integrity: sha512-ZVo0apxUvaRq4Vm1qhsfqKKhtRgReYlBVf9MQvVU1O9AoyydEQvLDO1ryqpXDZWpcHoFxHAQc9msjAMtE5K2lA==} + /turbo-windows-64@2.0.14: + resolution: {integrity: sha512-w9/XwkHSzvLjmioo6cl3S1yRfI6swxsV1j1eJwtl66JM4/pn0H2rBa855R0n7hZnmI6H5ywLt/nLt6Ae8RTDmw==} cpu: [x64] os: [win32] requiresBuild: true dev: true optional: true - /turbo-windows-arm64@2.0.9: - resolution: {integrity: sha512-sGRz7c5Pey6y7y9OKi8ypbWNuIRPF9y8xcMqL56OZifSUSo+X2EOsOleR9MKxQXVaqHPGOUKWsE6y8hxBi9pag==} + /turbo-windows-arm64@2.0.14: + resolution: {integrity: sha512-XaQlyYk+Rf4xS5XWCo8XCMIpssgGGy8blzLfolN6YBp4baElIWMlkLZHDbGyiFmCbNf9I9gJI64XGRG+LVyyjA==} cpu: [arm64] os: [win32] requiresBuild: true dev: true optional: true - /turbo@2.0.9: - resolution: {integrity: sha512-QaLaUL1CqblSKKPgLrFW3lZWkWG4pGBQNW+q1ScJB5v1D/nFWtsrD/yZljW/bdawg90ihi4/ftQJ3h6fz1FamA==} + /turbo@2.0.14: + resolution: {integrity: sha512-00JjdCMD/cpsjP0Izkjcm8Oaor5yUCfDwODtaLb+WyblyadkaDEisGhy3Dbd5az9n+5iLSPiUgf+WjPbns6MRg==} hasBin: true optionalDependencies: - turbo-darwin-64: 2.0.9 - turbo-darwin-arm64: 2.0.9 - turbo-linux-64: 2.0.9 - turbo-linux-arm64: 2.0.9 - turbo-windows-64: 2.0.9 - turbo-windows-arm64: 2.0.9 + turbo-darwin-64: 2.0.14 + turbo-darwin-arm64: 2.0.14 + turbo-linux-64: 2.0.14 + turbo-linux-arm64: 2.0.14 + turbo-windows-64: 2.0.14 + turbo-windows-arm64: 2.0.14 dev: true /type-check@0.4.0: @@ -8324,8 +8164,8 @@ packages: is-typed-array: 1.1.12 dev: true - /typedoc@0.26.5(typescript@5.5.4): - resolution: {integrity: sha512-Vn9YKdjKtDZqSk+by7beZ+xzkkr8T8CYoiasqyt4TTRFy5+UHzL/mF/o4wGBjRF+rlWQHDb0t6xCpA3JNL5phg==} + /typedoc@0.26.6(typescript@5.5.4): + resolution: {integrity: sha512-SfEU3SH3wHNaxhFPjaZE2kNl/NFtLNW5c1oHsg7mti7GjmUj1Roq6osBQeMd+F4kL0BoRBBr8gQAuqBlfFu8LA==} engines: {node: '>= 18'} hasBin: true peerDependencies: @@ -8361,8 +8201,8 @@ packages: which-boxed-primitive: 1.0.2 dev: true - /undici-types@6.11.1: - resolution: {integrity: sha512-mIDEX2ek50x0OlRgxryxsenE5XaQD4on5U2inY7RApK3SOJpofyw7uW2AyfMKkhAxXIceo2DeWGVGwyvng1GNQ==} + /undici-types@6.19.6: + resolution: {integrity: sha512-e/vggGopEfTKSvj4ihnOLTsqhrKRN3LeO6qSN/GxohhuRv8qH9bNQ4B8W7e/vFL+0XTnmHPB4/kegunZGA4Org==} /unicorn-magic@0.1.0: resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} diff --git a/record/program/Cargo.toml b/record/program/Cargo.toml index 97a0f25b526..4d324713ab4 100644 --- a/record/program/Cargo.toml +++ b/record/program/Cargo.toml @@ -12,7 +12,7 @@ no-entrypoint = [] test-sbf = [] [dependencies] -bytemuck = { version = "1.16.1", features = ["derive"] } +bytemuck = { version = "1.17.0", features = ["derive"] } num-derive = "0.4" num-traits = "0.2" solana-program = "2.0.3" diff --git a/single-pool/cli/Cargo.toml b/single-pool/cli/Cargo.toml index 50dfa8a254e..632bfa185d8 100644 --- a/single-pool/cli/Cargo.toml +++ b/single-pool/cli/Cargo.toml @@ -13,9 +13,9 @@ clap = { version = "3.2.23", features = ["derive"] } console = "0.15.8" borsh = "1.5.1" bincode = "1.3.1" -serde = "1.0.204" +serde = "1.0.208" serde_derive = "1.0.103" -serde_json = "1.0.121" +serde_json = "1.0.125" serde_with = "3.9.0" solana-account-decoder = "2.0.3" solana-clap-v3-utils = "2.0.3" @@ -42,7 +42,7 @@ spl-single-pool = { version = "1.0.0", path = "../program", features = [ solana-test-validator = "2.0.3" serial_test = "3.1.1" test-case = "3.3" -tempfile = "3.10.1" +tempfile = "3.12.0" [[bin]] name = "spl-single-pool" diff --git a/single-pool/js/packages/classic/package.json b/single-pool/js/packages/classic/package.json index 173b35f4247..3ca7b2c75d4 100644 --- a/single-pool/js/packages/classic/package.json +++ b/single-pool/js/packages/classic/package.json @@ -18,20 +18,18 @@ "test": "sed -i '1s/.*/{ \"type\": \"module\",/' package.json && NODE_OPTIONS='--loader=tsx' ava ; ret=$?; sed -i '1s/.*/{/' package.json && exit $ret" }, "devDependencies": { - "@types/node": "^22.0.0", + "@types/node": "^22.4.2", "@ava/typescript": "^5.0.0", - "@typescript-eslint/eslint-plugin": "^7.17.0", + "@typescript-eslint/eslint-plugin": "^8.2.0", "ava": "^6.1.3", "eslint": "^8.57.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-prettier": "^5.2.1", "solana-bankrun": "^0.2.0", - "tsx": "^4.16.2", + "tsx": "^4.17.0", "typescript": "^5.5.4" }, "dependencies": { - "@solana/web3.js": "^1.95.1", - "@solana/addresses": "=2.0.0-experimental.21e994f", + "@solana/web3.js": "^1.95.3", + "@solana/addresses": "2.0.0-rc.1", "@solana/spl-single-pool": "1.0.0" }, "ava": { diff --git a/single-pool/js/packages/classic/src/addresses.ts b/single-pool/js/packages/classic/src/addresses.ts index 2f5a483f04b..e9b6a22b2ff 100644 --- a/single-pool/js/packages/classic/src/addresses.ts +++ b/single-pool/js/packages/classic/src/addresses.ts @@ -1,4 +1,4 @@ -import type { Base58EncodedAddress } from '@solana/addresses'; +import type { Address } from '@solana/addresses'; import { PublicKey } from '@solana/web3.js'; import type { PoolAddress, VoteAccountAddress } from '@solana/spl-single-pool'; import { @@ -14,7 +14,7 @@ import { export async function findPoolAddress(programId: PublicKey, voteAccountAddress: PublicKey) { return new PublicKey( await findPoolModern( - programId.toBase58() as Base58EncodedAddress, + programId.toBase58() as Address, voteAccountAddress.toBase58() as VoteAccountAddress, ), ); @@ -22,26 +22,20 @@ export async function findPoolAddress(programId: PublicKey, voteAccountAddress: export async function findPoolStakeAddress(programId: PublicKey, poolAddress: PublicKey) { return new PublicKey( - await findStakeModern( - programId.toBase58() as Base58EncodedAddress, - poolAddress.toBase58() as PoolAddress, - ), + await findStakeModern(programId.toBase58() as Address, poolAddress.toBase58() as PoolAddress), ); } export async function findPoolMintAddress(programId: PublicKey, poolAddress: PublicKey) { return new PublicKey( - await findMintModern( - programId.toBase58() as Base58EncodedAddress, - poolAddress.toBase58() as PoolAddress, - ), + await findMintModern(programId.toBase58() as Address, poolAddress.toBase58() as PoolAddress), ); } export async function findPoolStakeAuthorityAddress(programId: PublicKey, poolAddress: PublicKey) { return new PublicKey( await findStakeAuthorityModern( - programId.toBase58() as Base58EncodedAddress, + programId.toBase58() as Address, poolAddress.toBase58() as PoolAddress, ), ); @@ -50,7 +44,7 @@ export async function findPoolStakeAuthorityAddress(programId: PublicKey, poolAd export async function findPoolMintAuthorityAddress(programId: PublicKey, poolAddress: PublicKey) { return new PublicKey( await findMintAuthorityModern( - programId.toBase58() as Base58EncodedAddress, + programId.toBase58() as Address, poolAddress.toBase58() as PoolAddress, ), ); @@ -59,7 +53,7 @@ export async function findPoolMintAuthorityAddress(programId: PublicKey, poolAdd export async function findPoolMplAuthorityAddress(programId: PublicKey, poolAddress: PublicKey) { return new PublicKey( await findMplAuthorityModern( - programId.toBase58() as Base58EncodedAddress, + programId.toBase58() as Address, poolAddress.toBase58() as PoolAddress, ), ); @@ -72,7 +66,7 @@ export async function findDefaultDepositAccountAddress( return new PublicKey( await findDefaultDepositModern( poolAddress.toBase58() as PoolAddress, - userWallet.toBase58() as Base58EncodedAddress, + userWallet.toBase58() as Address, ), ); } diff --git a/single-pool/js/packages/classic/src/instructions.ts b/single-pool/js/packages/classic/src/instructions.ts index a1593a27346..ae24d33829d 100644 --- a/single-pool/js/packages/classic/src/instructions.ts +++ b/single-pool/js/packages/classic/src/instructions.ts @@ -1,4 +1,4 @@ -import type { Base58EncodedAddress } from '@solana/addresses'; +import type { Address } from '@solana/addresses'; import { PublicKey, TransactionInstruction } from '@solana/web3.js'; import type { PoolAddress, VoteAccountAddress } from '@solana/spl-single-pool'; import { SinglePoolInstruction as PoolInstructionModern } from '@solana/spl-single-pool'; @@ -28,9 +28,9 @@ export class SinglePoolInstruction { ): Promise { const instruction = await PoolInstructionModern.depositStake( pool.toBase58() as PoolAddress, - userStakeAccount.toBase58() as Base58EncodedAddress, - userTokenAccount.toBase58() as Base58EncodedAddress, - userLamportAccount.toBase58() as Base58EncodedAddress, + userStakeAccount.toBase58() as Address, + userTokenAccount.toBase58() as Address, + userLamportAccount.toBase58() as Address, ); return modernInstructionToLegacy(instruction); } @@ -44,9 +44,9 @@ export class SinglePoolInstruction { ): Promise { const instruction = await PoolInstructionModern.withdrawStake( pool.toBase58() as PoolAddress, - userStakeAccount.toBase58() as Base58EncodedAddress, - userStakeAuthority.toBase58() as Base58EncodedAddress, - userTokenAccount.toBase58() as Base58EncodedAddress, + userStakeAccount.toBase58() as Address, + userStakeAuthority.toBase58() as Address, + userTokenAccount.toBase58() as Address, BigInt(tokenAmount), ); return modernInstructionToLegacy(instruction); @@ -58,7 +58,7 @@ export class SinglePoolInstruction { ): Promise { const instruction = await PoolInstructionModern.createTokenMetadata( pool.toBase58() as PoolAddress, - payer.toBase58() as Base58EncodedAddress, + payer.toBase58() as Address, ); return modernInstructionToLegacy(instruction); } @@ -72,7 +72,7 @@ export class SinglePoolInstruction { ): Promise { const instruction = await PoolInstructionModern.updateTokenMetadata( voteAccount.toBase58() as VoteAccountAddress, - authorizedWithdrawer.toBase58() as Base58EncodedAddress, + authorizedWithdrawer.toBase58() as Address, tokenName, tokenSymbol, tokenUri, diff --git a/single-pool/js/packages/classic/src/transactions.ts b/single-pool/js/packages/classic/src/transactions.ts index 2241deaf317..aac785ab48c 100644 --- a/single-pool/js/packages/classic/src/transactions.ts +++ b/single-pool/js/packages/classic/src/transactions.ts @@ -1,4 +1,4 @@ -import type { Base58EncodedAddress } from '@solana/addresses'; +import type { Address } from '@solana/addresses'; import { PublicKey, Connection } from '@solana/web3.js'; import type { PoolAddress, VoteAccountAddress } from '@solana/spl-single-pool'; import { SinglePoolProgram as PoolProgramModern } from '@solana/spl-single-pool'; @@ -41,7 +41,7 @@ export class SinglePoolProgram { const modernTransaction = await PoolProgramModern.initialize( rpc(connection), voteAccount.toBase58() as VoteAccountAddress, - payer.toBase58() as Base58EncodedAddress, + payer.toBase58() as Address, skipMetadata, ); @@ -73,7 +73,7 @@ export class SinglePoolProgram { static async createTokenMetadata(pool: PublicKey, payer: PublicKey) { const modernTransaction = await PoolProgramModern.createTokenMetadata( pool.toBase58() as PoolAddress, - payer.toBase58() as Base58EncodedAddress, + payer.toBase58() as Address, ); return modernTransactionToLegacy(modernTransaction); @@ -88,7 +88,7 @@ export class SinglePoolProgram { ) { const modernTransaction = await PoolProgramModern.updateTokenMetadata( voteAccount.toBase58() as VoteAccountAddress, - authorizedWithdrawer.toBase58() as Base58EncodedAddress, + authorizedWithdrawer.toBase58() as Address, name, symbol, uri, @@ -106,7 +106,7 @@ export class SinglePoolProgram { const modernTransaction = await PoolProgramModern.createAndDelegateUserStake( rpc(connection), voteAccount.toBase58() as VoteAccountAddress, - userWallet.toBase58() as Base58EncodedAddress, + userWallet.toBase58() as Address, BigInt(stakeAmount), ); diff --git a/single-pool/js/packages/classic/tests/transactions.test.ts b/single-pool/js/packages/classic/tests/transactions.test.ts index c6e1cb9cc5d..38dcc41d867 100644 --- a/single-pool/js/packages/classic/tests/transactions.test.ts +++ b/single-pool/js/packages/classic/tests/transactions.test.ts @@ -14,7 +14,6 @@ import { getVoteAccountAddressForPool, findDefaultDepositAccountAddress, MPL_METADATA_PROGRAM_ID, - SinglePoolProgram, findPoolAddress, findPoolStakeAddress, findPoolMintAddress, diff --git a/single-pool/js/packages/modern/package.json b/single-pool/js/packages/modern/package.json index 06c0e187d4a..b363efbd23e 100644 --- a/single-pool/js/packages/modern/package.json +++ b/single-pool/js/packages/modern/package.json @@ -16,14 +16,14 @@ "lint:fix": "eslint . --fix" }, "devDependencies": { - "@types/node": "^22.0.0", - "@typescript-eslint/eslint-plugin": "^7.17.0", + "@types/node": "^22.4.2", + "@typescript-eslint/eslint-plugin": "^8.2.0", "eslint": "^8.57.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-prettier": "^5.2.1", "typescript": "^5.5.4" }, "dependencies": { - "@solana/web3.js": "=2.0.0-experimental.21e994f" + "@solana/addresses": "2.0.0-rc.1", + "@solana/instructions": "2.0.0-rc.1", + "@solana/transaction-messages": "2.0.0-rc.1" } } diff --git a/single-pool/js/packages/modern/src/addresses.ts b/single-pool/js/packages/modern/src/addresses.ts index 7e8464e487f..473b7237e3e 100644 --- a/single-pool/js/packages/modern/src/addresses.ts +++ b/single-pool/js/packages/modern/src/addresses.ts @@ -1,99 +1,91 @@ import { address, getAddressCodec, - Base58EncodedAddress, getProgramDerivedAddress, createAddressWithSeed, -} from '@solana/web3.js'; + Address, +} from '@solana/addresses'; import { MPL_METADATA_PROGRAM_ID } from './internal.js'; import { STAKE_PROGRAM_ID } from './quarantine.js'; export const SINGLE_POOL_PROGRAM_ID = address('SVSPxpvHdN29nkVg9rPapPNDddN5DipNLRUFhyjFThE'); -export type VoteAccountAddress = - Base58EncodedAddress & { - readonly __voteAccountAddress: unique symbol; - }; +export type VoteAccountAddress = Address & { + readonly __voteAccountAddress: unique symbol; +}; -export type PoolAddress = Base58EncodedAddress & { +export type PoolAddress = Address & { readonly __poolAddress: unique symbol; }; -export type PoolStakeAddress = Base58EncodedAddress & { +export type PoolStakeAddress = Address & { readonly __poolStakeAddress: unique symbol; }; -export type PoolMintAddress = Base58EncodedAddress & { +export type PoolMintAddress = Address & { readonly __poolMintAddress: unique symbol; }; -export type PoolStakeAuthorityAddress = - Base58EncodedAddress & { - readonly __poolStakeAuthorityAddress: unique symbol; - }; +export type PoolStakeAuthorityAddress = Address & { + readonly __poolStakeAuthorityAddress: unique symbol; +}; -export type PoolMintAuthorityAddress = - Base58EncodedAddress & { - readonly __poolMintAuthorityAddress: unique symbol; - }; +export type PoolMintAuthorityAddress = Address & { + readonly __poolMintAuthorityAddress: unique symbol; +}; -export type PoolMplAuthorityAddress = - Base58EncodedAddress & { - readonly __poolMplAuthorityAddress: unique symbol; - }; +export type PoolMplAuthorityAddress = Address & { + readonly __poolMplAuthorityAddress: unique symbol; +}; export async function findPoolAddress( - programId: Base58EncodedAddress, + programId: Address, voteAccountAddress: VoteAccountAddress, ): Promise { return (await findPda(programId, voteAccountAddress, 'pool')) as PoolAddress; } export async function findPoolStakeAddress( - programId: Base58EncodedAddress, + programId: Address, poolAddress: PoolAddress, ): Promise { return (await findPda(programId, poolAddress, 'stake')) as PoolStakeAddress; } export async function findPoolMintAddress( - programId: Base58EncodedAddress, + programId: Address, poolAddress: PoolAddress, ): Promise { return (await findPda(programId, poolAddress, 'mint')) as PoolMintAddress; } export async function findPoolStakeAuthorityAddress( - programId: Base58EncodedAddress, + programId: Address, poolAddress: PoolAddress, ): Promise { return (await findPda(programId, poolAddress, 'stake_authority')) as PoolStakeAuthorityAddress; } export async function findPoolMintAuthorityAddress( - programId: Base58EncodedAddress, + programId: Address, poolAddress: PoolAddress, ): Promise { return (await findPda(programId, poolAddress, 'mint_authority')) as PoolMintAuthorityAddress; } export async function findPoolMplAuthorityAddress( - programId: Base58EncodedAddress, + programId: Address, poolAddress: PoolAddress, ): Promise { return (await findPda(programId, poolAddress, 'mpl_authority')) as PoolMplAuthorityAddress; } -async function findPda( - programId: Base58EncodedAddress, - baseAddress: Base58EncodedAddress, - prefix: string, -) { - const { serialize } = getAddressCodec(); +async function findPda(programId: Address, baseAddress: Address, prefix: string) { + const { encode } = getAddressCodec(); const [pda] = await getProgramDerivedAddress({ programAddress: programId, - seeds: [prefix, serialize(baseAddress)], + seeds: [prefix, encode(baseAddress)], }); return pda; @@ -101,7 +93,7 @@ async function findPda( export async function findDefaultDepositAccountAddress( poolAddress: PoolAddress, - userWallet: Base58EncodedAddress, + userWallet: Address, ) { return createAddressWithSeed({ baseAddress: userWallet, @@ -115,10 +107,10 @@ export function defaultDepositAccountSeed(poolAddress: PoolAddress): string { } export async function findMplMetadataAddress(poolMintAddress: PoolMintAddress) { - const { serialize } = getAddressCodec(); + const { encode } = getAddressCodec(); const [pda] = await getProgramDerivedAddress({ programAddress: MPL_METADATA_PROGRAM_ID, - seeds: ['metadata', serialize(MPL_METADATA_PROGRAM_ID), serialize(poolMintAddress)], + seeds: ['metadata', encode(MPL_METADATA_PROGRAM_ID), encode(poolMintAddress)], }); return pda; diff --git a/single-pool/js/packages/modern/src/index.ts b/single-pool/js/packages/modern/src/index.ts index b26524a4f1b..f739b8ae9a0 100644 --- a/single-pool/js/packages/modern/src/index.ts +++ b/single-pool/js/packages/modern/src/index.ts @@ -1,4 +1,4 @@ -import { getAddressCodec } from '@solana/web3.js'; +import { getAddressCodec } from '@solana/addresses'; import { PoolAddress, VoteAccountAddress } from './addresses.js'; @@ -15,5 +15,5 @@ export async function getVoteAccountAddressForPool( if (!(poolAccount && poolAccount.data[0] === 1)) { throw 'invalid pool address'; } - return getAddressCodec().deserialize(poolAccount.data.slice(1))[0] as VoteAccountAddress; + return getAddressCodec().decode(poolAccount.data.slice(1)) as VoteAccountAddress; } diff --git a/single-pool/js/packages/modern/src/instructions.ts b/single-pool/js/packages/modern/src/instructions.ts index a170f47764e..5fcaf618faa 100644 --- a/single-pool/js/packages/modern/src/instructions.ts +++ b/single-pool/js/packages/modern/src/instructions.ts @@ -1,6 +1,5 @@ +import { getAddressCodec, Address } from '@solana/addresses'; import { - getAddressCodec, - Base58EncodedAddress, ReadonlySignerAccount, ReadonlyAccount, IInstructionWithAccounts, @@ -9,7 +8,7 @@ import { WritableSignerAccount, IInstruction, AccountRole, -} from '@solana/web3.js'; +} from '@solana/instructions'; import { PoolMintAuthorityAddress, @@ -84,9 +83,9 @@ type DepositStakeInstruction = IInstruction & WritableAccount, ReadonlyAccount, ReadonlyAccount, - WritableAccount, // user stake - WritableAccount, // user token - WritableAccount, // user lamport + WritableAccount
, // user stake + WritableAccount
, // user token + WritableAccount
, // user lamport ReadonlyAccount, ReadonlyAccount, ReadonlyAccount, @@ -103,8 +102,8 @@ type WithdrawStakeInstruction = IInstruction & WritableAccount, ReadonlyAccount, ReadonlyAccount, - WritableAccount, // user stake - WritableAccount, // user token + WritableAccount
, // user stake + WritableAccount
, // user token ReadonlyAccount, ReadonlyAccount, ReadonlyAccount, @@ -119,8 +118,8 @@ type CreateTokenMetadataInstruction = IInstruction, ReadonlyAccount, ReadonlyAccount, - WritableSignerAccount, // mpl payer - WritableAccount, // mpl account + WritableSignerAccount
, // mpl payer + WritableAccount
, // mpl account ReadonlyAccount, ReadonlyAccount, ] @@ -133,8 +132,8 @@ type UpdateTokenMetadataInstruction = IInstruction, ReadonlyAccount, ReadonlyAccount, - ReadonlySignerAccount, // authorized withdrawer - WritableAccount, // mpl account + ReadonlySignerAccount
, // authorized withdrawer + WritableAccount
, // mpl account ReadonlyAccount, ] > & @@ -223,9 +222,9 @@ export async function reactivatePoolStakeInstruction( export async function depositStakeInstruction( pool: PoolAddress, - userStakeAccount: Base58EncodedAddress, - userTokenAccount: Base58EncodedAddress, - userLamportAccount: Base58EncodedAddress, + userStakeAccount: Address, + userTokenAccount: Address, + userLamportAccount: Address, ): Promise { const programAddress = SINGLE_POOL_PROGRAM_ID; const [stake, mint, stakeAuthority, mintAuthority] = await Promise.all([ @@ -259,9 +258,9 @@ export async function depositStakeInstruction( export async function withdrawStakeInstruction( pool: PoolAddress, - userStakeAccount: Base58EncodedAddress, - userStakeAuthority: Base58EncodedAddress, - userTokenAccount: Base58EncodedAddress, + userStakeAccount: Address, + userStakeAuthority: Address, + userTokenAccount: Address, tokenAmount: bigint, ): Promise { const programAddress = SINGLE_POOL_PROGRAM_ID; @@ -272,10 +271,10 @@ export async function withdrawStakeInstruction( findPoolMintAuthorityAddress(programAddress, pool), ]); - const { serialize } = getAddressCodec(); + const { encode } = getAddressCodec(); const data = new Uint8Array([ SinglePoolInstructionType.WithdrawStake, - ...serialize(userStakeAuthority), + ...encode(userStakeAuthority), ...u64(tokenAmount), ]); @@ -299,7 +298,7 @@ export async function withdrawStakeInstruction( export async function createTokenMetadataInstruction( pool: PoolAddress, - payer: Base58EncodedAddress, + payer: Address, ): Promise { const programAddress = SINGLE_POOL_PROGRAM_ID; const mint = await findPoolMintAddress(programAddress, pool); @@ -329,7 +328,7 @@ export async function createTokenMetadataInstruction( export async function updateTokenMetadataInstruction( voteAccount: VoteAccountAddress, - authorizedWithdrawer: Base58EncodedAddress, + authorizedWithdrawer: Address, tokenName: string, tokenSymbol: string, tokenUri?: string, diff --git a/single-pool/js/packages/modern/src/internal.ts b/single-pool/js/packages/modern/src/internal.ts index cdea7d11bae..6ade8ed221b 100644 --- a/single-pool/js/packages/modern/src/internal.ts +++ b/single-pool/js/packages/modern/src/internal.ts @@ -1,3 +1,3 @@ -import { address } from '@solana/web3.js'; +import { address } from '@solana/addresses'; export const MPL_METADATA_PROGRAM_ID = address('metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s'); diff --git a/single-pool/js/packages/modern/src/quarantine.ts b/single-pool/js/packages/modern/src/quarantine.ts index 3784589ff91..b3f93a74818 100644 --- a/single-pool/js/packages/modern/src/quarantine.ts +++ b/single-pool/js/packages/modern/src/quarantine.ts @@ -1,10 +1,5 @@ -import { - address, - getAddressCodec, - Base58EncodedAddress, - AccountRole, - getProgramDerivedAddress, -} from '@solana/web3.js'; +import { address, getAddressCodec, getProgramDerivedAddress, Address } from '@solana/addresses'; +import { AccountRole } from '@solana/instructions'; // HERE BE DRAGONS // this is all the stuff that shouldn't be in our library once we can import from elsewhere @@ -33,18 +28,18 @@ export function u64(n: bigint): Uint8Array { export class SystemInstruction { static createAccount(params: { - from: Base58EncodedAddress; - newAccount: Base58EncodedAddress; + from: Address; + newAccount: Address; lamports: bigint; space: bigint; - programAddress: Base58EncodedAddress; + programAddress: Address; }) { - const { serialize } = getAddressCodec(); + const { encode } = getAddressCodec(); const data = new Uint8Array([ ...u32(0), ...u64(params.lamports), ...u64(params.space), - ...serialize(params.programAddress), + ...encode(params.programAddress), ]); const accounts = [ @@ -59,11 +54,7 @@ export class SystemInstruction { }; } - static transfer(params: { - from: Base58EncodedAddress; - to: Base58EncodedAddress; - lamports: bigint; - }) { + static transfer(params: { from: Address; to: Address; lamports: bigint }) { const data = new Uint8Array([...u32(2), ...u64(params.lamports)]); const accounts = [ @@ -79,23 +70,23 @@ export class SystemInstruction { } static createAccountWithSeed(params: { - from: Base58EncodedAddress; - newAccount: Base58EncodedAddress; - base: Base58EncodedAddress; + from: Address; + newAccount: Address; + base: Address; seed: string; lamports: bigint; space: bigint; - programAddress: Base58EncodedAddress; + programAddress: Address; }) { - const { serialize } = getAddressCodec(); + const { encode } = getAddressCodec(); const data = new Uint8Array([ ...u32(3), - ...serialize(params.base), + ...encode(params.base), ...u64(BigInt(params.seed.length)), ...new TextEncoder().encode(params.seed), ...u64(params.lamports), ...u64(params.space), - ...serialize(params.programAddress), + ...encode(params.programAddress), ]); const accounts = [ @@ -115,12 +106,7 @@ export class SystemInstruction { } export class TokenInstruction { - static approve(params: { - account: Base58EncodedAddress; - delegate: Base58EncodedAddress; - owner: Base58EncodedAddress; - amount: bigint; - }) { + static approve(params: { account: Address; delegate: Address; owner: Address; amount: bigint }) { const data = new Uint8Array([...u32(4), ...u64(params.amount)]); const accounts = [ @@ -137,10 +123,10 @@ export class TokenInstruction { } static createAssociatedTokenAccount(params: { - payer: Base58EncodedAddress; - associatedAccount: Base58EncodedAddress; - owner: Base58EncodedAddress; - mint: Base58EncodedAddress; + payer: Address; + associatedAccount: Address; + owner: Address; + mint: Address; }) { const data = new Uint8Array([0]); @@ -168,16 +154,12 @@ export enum StakeAuthorizationType { export class StakeInstruction { // idc about doing it right unless this goes in a lib - static initialize(params: { - stakeAccount: Base58EncodedAddress; - staker: Base58EncodedAddress; - withdrawer: Base58EncodedAddress; - }) { - const { serialize } = getAddressCodec(); + static initialize(params: { stakeAccount: Address; staker: Address; withdrawer: Address }) { + const { encode } = getAddressCodec(); const data = new Uint8Array([ ...u32(0), - ...serialize(params.staker), - ...serialize(params.withdrawer), + ...encode(params.staker), + ...encode(params.withdrawer), ...Array(48).fill(0), ]); @@ -194,16 +176,16 @@ export class StakeInstruction { } static authorize(params: { - stakeAccount: Base58EncodedAddress; - authorized: Base58EncodedAddress; - newAuthorized: Base58EncodedAddress; + stakeAccount: Address; + authorized: Address; + newAuthorized: Address; authorizationType: StakeAuthorizationType; - custodian?: Base58EncodedAddress; + custodian?: Address; }) { - const { serialize } = getAddressCodec(); + const { encode } = getAddressCodec(); const data = new Uint8Array([ ...u32(1), - ...serialize(params.newAuthorized), + ...encode(params.newAuthorized), ...u32(params.authorizationType), ]); @@ -223,11 +205,7 @@ export class StakeInstruction { }; } - static delegate(params: { - stakeAccount: Base58EncodedAddress; - authorized: Base58EncodedAddress; - voteAccount: Base58EncodedAddress; - }) { + static delegate(params: { stakeAccount: Address; authorized: Address; voteAccount: Address }) { const data = new Uint8Array(u32(2)); const accounts = [ @@ -247,14 +225,11 @@ export class StakeInstruction { } } -export async function getAssociatedTokenAddress( - mint: Base58EncodedAddress, - owner: Base58EncodedAddress, -) { - const { serialize } = getAddressCodec(); +export async function getAssociatedTokenAddress(mint: Address, owner: Address) { + const { encode } = getAddressCodec(); const [pda] = await getProgramDerivedAddress({ programAddress: ATOKEN_PROGRAM_ID, - seeds: [serialize(owner), serialize(TOKEN_PROGRAM_ID), serialize(mint)], + seeds: [encode(owner), encode(TOKEN_PROGRAM_ID), encode(mint)], }); return pda; diff --git a/single-pool/js/packages/modern/src/transactions.ts b/single-pool/js/packages/modern/src/transactions.ts index 03b2842b7c8..7ad7b2579f6 100644 --- a/single-pool/js/packages/modern/src/transactions.ts +++ b/single-pool/js/packages/modern/src/transactions.ts @@ -1,9 +1,10 @@ +import { Address } from '@solana/addresses'; import { - appendTransactionInstruction, - Transaction, + appendTransactionMessageInstruction, + createTransactionMessage, TransactionVersion, - Base58EncodedAddress, -} from '@solana/web3.js'; + TransactionMessage, +} from '@solana/transaction-messages'; import { findPoolAddress, @@ -39,24 +40,24 @@ import { interface DepositParams { rpc: any; // XXX Rpc pool: PoolAddress; - userWallet: Base58EncodedAddress; - userStakeAccount?: Base58EncodedAddress; + userWallet: Address; + userStakeAccount?: Address; depositFromDefaultAccount?: boolean; - userTokenAccount?: Base58EncodedAddress; - userLamportAccount?: Base58EncodedAddress; - userWithdrawAuthority?: Base58EncodedAddress; + userTokenAccount?: Address; + userLamportAccount?: Address; + userWithdrawAuthority?: Address; } interface WithdrawParams { rpc: any; // XXX Rpc pool: PoolAddress; - userWallet: Base58EncodedAddress; - userStakeAccount: Base58EncodedAddress; + userWallet: Address; + userStakeAccount: Address; tokenAmount: bigint; createStakeAccount?: boolean; - userStakeAuthority?: Base58EncodedAddress; - userTokenAccount?: Base58EncodedAddress; - userTokenAuthority?: Base58EncodedAddress; + userStakeAuthority?: Address; + userTokenAccount?: Address; + userTokenAuthority?: Address; } export const SINGLE_POOL_ACCOUNT_SIZE = 33n; @@ -76,10 +77,10 @@ export const SinglePoolProgram = { export async function initializeTransaction( rpc: any, // XXX not exported: Rpc, voteAccount: VoteAccountAddress, - payer: Base58EncodedAddress, + payer: Address, skipMetadata = false, -): Promise { - let transaction = { instructions: [] as any, version: 'legacy' as TransactionVersion }; +): Promise { + let transaction = createTransactionMessage({ version: 0 }); const pool = await findPoolAddress(SINGLE_POOL_PROGRAM_ID, voteAccount); const [stake, mint, poolRent, stakeRent, mintRent, minimumDelegationObj] = await Promise.all([ @@ -92,7 +93,7 @@ export async function initializeTransaction( ]); const minimumDelegation = minimumDelegationObj.value; - transaction = appendTransactionInstruction( + transaction = appendTransactionMessageInstruction( SystemInstruction.transfer({ from: payer, to: pool, @@ -101,7 +102,7 @@ export async function initializeTransaction( transaction, ); - transaction = appendTransactionInstruction( + transaction = appendTransactionMessageInstruction( SystemInstruction.transfer({ from: payer, to: stake, @@ -110,7 +111,7 @@ export async function initializeTransaction( transaction, ); - transaction = appendTransactionInstruction( + transaction = appendTransactionMessageInstruction( SystemInstruction.transfer({ from: payer, to: mint, @@ -119,13 +120,13 @@ export async function initializeTransaction( transaction, ); - transaction = appendTransactionInstruction( + transaction = appendTransactionMessageInstruction( await initializePoolInstruction(voteAccount), transaction, ); if (!skipMetadata) { - transaction = appendTransactionInstruction( + transaction = appendTransactionMessageInstruction( await createTokenMetadataInstruction(pool, payer), transaction, ); @@ -136,9 +137,9 @@ export async function initializeTransaction( export async function reactivatePoolStakeTransaction( voteAccount: VoteAccountAddress, -): Promise { +): Promise { let transaction = { instructions: [] as any, version: 'legacy' as TransactionVersion }; - transaction = appendTransactionInstruction( + transaction = appendTransactionMessageInstruction( await reactivatePoolStakeInstruction(voteAccount), transaction, ); @@ -158,7 +159,7 @@ export async function depositTransaction(params: DepositParams) { params.depositFromDefaultAccount ? await findDefaultDepositAccountAddress(pool, userWallet) : params.userStakeAccount - ) as Base58EncodedAddress; + ) as Address; let transaction = { instructions: [] as any, version: 'legacy' as TransactionVersion }; @@ -176,7 +177,7 @@ export async function depositTransaction(params: DepositParams) { userTokenAccount == userAssociatedTokenAccount && (await rpc.getAccountInfo(userAssociatedTokenAccount).send()) == null ) { - transaction = appendTransactionInstruction( + transaction = appendTransactionMessageInstruction( TokenInstruction.createAssociatedTokenAccount({ payer: userWallet, associatedAccount: userAssociatedTokenAccount, @@ -187,7 +188,7 @@ export async function depositTransaction(params: DepositParams) { ); } - transaction = appendTransactionInstruction( + transaction = appendTransactionMessageInstruction( StakeInstruction.authorize({ stakeAccount: userStakeAccount, authorized: userWithdrawAuthority, @@ -197,7 +198,7 @@ export async function depositTransaction(params: DepositParams) { transaction, ); - transaction = appendTransactionInstruction( + transaction = appendTransactionMessageInstruction( StakeInstruction.authorize({ stakeAccount: userStakeAccount, authorized: userWithdrawAuthority, @@ -207,7 +208,7 @@ export async function depositTransaction(params: DepositParams) { transaction, ); - transaction = appendTransactionInstruction( + transaction = appendTransactionMessageInstruction( await depositStakeInstruction(pool, userStakeAccount, userTokenAccount, userLamportAccount), transaction, ); @@ -232,7 +233,7 @@ export async function withdrawTransaction(params: WithdrawParams) { const userTokenAuthority = params.userTokenAuthority || userWallet; if (createStakeAccount) { - transaction = appendTransactionInstruction( + transaction = appendTransactionMessageInstruction( SystemInstruction.createAccount({ from: userWallet, lamports: await rpc.getMinimumBalanceForRentExemption(STAKE_ACCOUNT_SIZE).send(), @@ -244,7 +245,7 @@ export async function withdrawTransaction(params: WithdrawParams) { ); } - transaction = appendTransactionInstruction( + transaction = appendTransactionMessageInstruction( TokenInstruction.approve({ account: userTokenAccount, delegate: poolMintAuthority, @@ -254,7 +255,7 @@ export async function withdrawTransaction(params: WithdrawParams) { transaction, ); - transaction = appendTransactionInstruction( + transaction = appendTransactionMessageInstruction( await withdrawStakeInstruction( pool, userStakeAccount, @@ -270,10 +271,10 @@ export async function withdrawTransaction(params: WithdrawParams) { export async function createTokenMetadataTransaction( pool: PoolAddress, - payer: Base58EncodedAddress, -): Promise { + payer: Address, +): Promise { let transaction = { instructions: [] as any, version: 'legacy' as TransactionVersion }; - transaction = appendTransactionInstruction( + transaction = appendTransactionMessageInstruction( await createTokenMetadataInstruction(pool, payer), transaction, ); @@ -283,13 +284,13 @@ export async function createTokenMetadataTransaction( export async function updateTokenMetadataTransaction( voteAccount: VoteAccountAddress, - authorizedWithdrawer: Base58EncodedAddress, + authorizedWithdrawer: Address, name: string, symbol: string, uri?: string, -): Promise { +): Promise { let transaction = { instructions: [] as any, version: 'legacy' as TransactionVersion }; - transaction = appendTransactionInstruction( + transaction = appendTransactionMessageInstruction( await updateTokenMetadataInstruction(voteAccount, authorizedWithdrawer, name, symbol, uri), transaction, ); @@ -300,9 +301,9 @@ export async function updateTokenMetadataTransaction( export async function createAndDelegateUserStakeTransaction( rpc: any, // XXX not exported: Rpc, voteAccount: VoteAccountAddress, - userWallet: Base58EncodedAddress, + userWallet: Address, stakeAmount: bigint, -): Promise { +): Promise { let transaction = { instructions: [] as any, version: 'legacy' as TransactionVersion }; const pool = await findPoolAddress(SINGLE_POOL_PROGRAM_ID, voteAccount); @@ -311,7 +312,7 @@ export async function createAndDelegateUserStakeTransaction( await rpc.getMinimumBalanceForRentExemption(STAKE_ACCOUNT_SIZE).send(), ]); - transaction = appendTransactionInstruction( + transaction = appendTransactionMessageInstruction( SystemInstruction.createAccountWithSeed({ base: userWallet, from: userWallet, @@ -324,7 +325,7 @@ export async function createAndDelegateUserStakeTransaction( transaction, ); - transaction = appendTransactionInstruction( + transaction = appendTransactionMessageInstruction( StakeInstruction.initialize({ stakeAccount, staker: userWallet, @@ -333,7 +334,7 @@ export async function createAndDelegateUserStakeTransaction( transaction, ); - transaction = appendTransactionInstruction( + transaction = appendTransactionMessageInstruction( StakeInstruction.delegate({ stakeAccount, authorized: userWallet, diff --git a/single-pool/program/Cargo.toml b/single-pool/program/Cargo.toml index c4e4c96b130..86f22f4a71d 100644 --- a/single-pool/program/Cargo.toml +++ b/single-pool/program/Cargo.toml @@ -16,7 +16,7 @@ arrayref = "0.3.8" borsh = "1.5.1" num-derive = "0.4" num-traits = "0.2" -num_enum = "0.7.2" +num_enum = "0.7.3" solana-program = "2.0.3" solana-security-txt = "1.1.1" spl-token = { version = "6.0", path = "../../token/program", features = [ diff --git a/stake-pool/cli/Cargo.toml b/stake-pool/cli/Cargo.toml index 3c6dd8e7c1e..1b2f2134782 100644 --- a/stake-pool/cli/Cargo.toml +++ b/stake-pool/cli/Cargo.toml @@ -11,9 +11,9 @@ version = "2.0.0" [dependencies] borsh = "1.5.1" clap = "2.33.3" -serde = "1.0.204" +serde = "1.0.208" serde_derive = "1.0.130" -serde_json = "1.0.121" +serde_json = "1.0.125" solana-account-decoder = "2.0.3" solana-clap-utils = "2.0.3" solana-cli-config = "2.0.3" diff --git a/stake-pool/cli/src/main.rs b/stake-pool/cli/src/main.rs index 3c8e4af58bb..a777d8f6ee0 100644 --- a/stake-pool/cli/src/main.rs +++ b/stake-pool/cli/src/main.rs @@ -2,9 +2,6 @@ mod client; mod output; -// use instruction::create_associated_token_account once ATA 1.0.5 is released -#[allow(deprecated)] -use spl_associated_token_account::create_associated_token_account; use { crate::{ client::*, @@ -46,7 +43,9 @@ use { system_instruction, transaction::Transaction, }, - spl_associated_token_account::get_associated_token_address, + spl_associated_token_account::{ + get_associated_token_address, instruction::create_associated_token_account, + }, spl_stake_pool::{ self, find_stake_program_address, find_transient_stake_program_address, find_withdraw_authority_program_address, @@ -298,6 +297,290 @@ fn new_stake_account( stake_receiver_keypair } +fn setup_reserve_stake_account( + config: &Config, + reserve_keypair: &Keypair, + reserve_stake_balance: u64, + withdraw_authority: &Pubkey, +) -> CommandResult { + let reserve_account_info = config.rpc_client.get_account(&reserve_keypair.pubkey()); + if let Ok(account) = reserve_account_info { + if account.owner == stake::program::id() { + if account.data.iter().any(|&x| x != 0) { + println!( + "Reserve stake account {} already exists and is initialized", + reserve_keypair.pubkey() + ); + return Ok(()); + } else { + let instructions = vec![stake::instruction::initialize( + &reserve_keypair.pubkey(), + &stake::state::Authorized { + staker: *withdraw_authority, + withdrawer: *withdraw_authority, + }, + &stake::state::Lockup::default(), + )]; + let signers = vec![config.fee_payer.as_ref()]; + let transaction = + checked_transaction_with_signers(config, &instructions, &signers)?; + println!( + "Initializing existing reserve stake account {}", + reserve_keypair.pubkey() + ); + send_transaction(config, transaction)?; + return Ok(()); + } + } + } + + let instructions = vec![ + system_instruction::create_account( + &config.fee_payer.pubkey(), + &reserve_keypair.pubkey(), + reserve_stake_balance, + STAKE_STATE_LEN as u64, + &stake::program::id(), + ), + stake::instruction::initialize( + &reserve_keypair.pubkey(), + &stake::state::Authorized { + staker: *withdraw_authority, + withdrawer: *withdraw_authority, + }, + &stake::state::Lockup::default(), + ), + ]; + + let signers = vec![config.fee_payer.as_ref(), reserve_keypair]; + let transaction = checked_transaction_with_signers(config, &instructions, &signers)?; + + println!( + "Creating and initializing reserve stake account {}", + reserve_keypair.pubkey() + ); + send_transaction(config, transaction)?; + Ok(()) +} + +fn setup_mint_account( + config: &Config, + mint_keypair: &Keypair, + mint_account_balance: u64, + withdraw_authority: &Pubkey, + default_decimals: u8, +) -> CommandResult { + let mint_account_info = config.rpc_client.get_account(&mint_keypair.pubkey()); + if let Ok(account) = mint_account_info { + if account.owner == spl_token::id() { + if account.data.iter().any(|&x| x != 0) { + println!( + "Mint account {} already exists and is initialized", + mint_keypair.pubkey() + ); + return Ok(()); + } else { + let instructions = vec![spl_token::instruction::initialize_mint( + &spl_token::id(), + &mint_keypair.pubkey(), + withdraw_authority, + None, + default_decimals, + )?]; + let signers = vec![config.fee_payer.as_ref()]; + let transaction = + checked_transaction_with_signers(config, &instructions, &signers)?; + println!( + "Initializing existing mint account {}", + mint_keypair.pubkey() + ); + send_transaction(config, transaction)?; + return Ok(()); + } + } + } + + let instructions = vec![ + system_instruction::create_account( + &config.fee_payer.pubkey(), + &mint_keypair.pubkey(), + mint_account_balance, + spl_token::state::Mint::LEN as u64, + &spl_token::id(), + ), + spl_token::instruction::initialize_mint( + &spl_token::id(), + &mint_keypair.pubkey(), + withdraw_authority, + None, + default_decimals, + )?, + ]; + + let signers = vec![config.fee_payer.as_ref(), mint_keypair]; + let transaction = checked_transaction_with_signers(config, &instructions, &signers)?; + + println!( + "Creating and initializing mint account {}", + mint_keypair.pubkey() + ); + send_transaction(config, transaction)?; + Ok(()) +} + +fn setup_pool_fee_account( + config: &Config, + mint_pubkey: &Pubkey, + total_rent_free_balances: &mut u64, +) -> CommandResult { + let pool_fee_account = get_associated_token_address(&config.manager.pubkey(), mint_pubkey); + let pool_fee_account_info = config.rpc_client.get_account(&pool_fee_account); + if let Ok(account) = pool_fee_account_info { + if account.owner == spl_token::id() { + println!("Pool fee account {} already exists", pool_fee_account); + return Ok(()); + } + } + // Create pool fee account + let mut instructions = vec![]; + add_associated_token_account( + config, + mint_pubkey, + &config.manager.pubkey(), + &mut instructions, + total_rent_free_balances, + ); + + println!("Creating pool fee collection account {}", pool_fee_account); + + let signers = vec![config.fee_payer.as_ref()]; + let transaction = checked_transaction_with_signers(config, &instructions, &signers)?; + + send_transaction(config, transaction)?; + Ok(()) +} + +#[allow(clippy::too_many_arguments)] +fn setup_and_initialize_validator_list_with_stake_pool( + config: &Config, + stake_pool_keypair: &Keypair, + validator_list_keypair: &Keypair, + reserve_keypair: &Keypair, + mint_keypair: &Keypair, + pool_fee_account: &Pubkey, + deposit_authority: Option, + epoch_fee: Fee, + withdrawal_fee: Fee, + deposit_fee: Fee, + referral_fee: u8, + max_validators: u32, + withdraw_authority: &Pubkey, + validator_list_balance: u64, + validator_list_size: usize, +) -> CommandResult { + let stake_pool_account_info = config.rpc_client.get_account(&stake_pool_keypair.pubkey()); + let validator_list_account_info = config + .rpc_client + .get_account(&validator_list_keypair.pubkey()); + + let stake_pool_account_lamports = config + .rpc_client + .get_minimum_balance_for_rent_exemption(get_packed_len::())?; + + let mut instructions = vec![]; + let mut signers = vec![config.fee_payer.as_ref(), config.manager.as_ref()]; + + if let Ok(account) = validator_list_account_info { + if account.owner == spl_stake_pool::id() { + if account.data.iter().all(|&x| x == 0) { + println!( + "Validator list account {} already exists and is ready to be initialized", + validator_list_keypair.pubkey() + ); + } else { + println!( + "Validator list account {} already exists and is initialized", + validator_list_keypair.pubkey() + ); + return Ok(()); + } + } + } else { + instructions.push(system_instruction::create_account( + &config.fee_payer.pubkey(), + &validator_list_keypair.pubkey(), + validator_list_balance, + validator_list_size as u64, + &spl_stake_pool::id(), + )); + signers.push(validator_list_keypair); + } + + if let Ok(account) = stake_pool_account_info { + if account.owner == spl_stake_pool::id() { + if account.data.iter().all(|&x| x == 0) { + println!( + "Stake pool account {} already exists but is not initialized", + stake_pool_keypair.pubkey() + ); + } else { + println!( + "Stake pool account {} already exists and is initialized", + stake_pool_keypair.pubkey() + ); + return Ok(()); + } + } + } else { + instructions.push(system_instruction::create_account( + &config.fee_payer.pubkey(), + &stake_pool_keypair.pubkey(), + stake_pool_account_lamports, + get_packed_len::() as u64, + &spl_stake_pool::id(), + )); + } + instructions.push(spl_stake_pool::instruction::initialize( + &spl_stake_pool::id(), + &stake_pool_keypair.pubkey(), + &config.manager.pubkey(), + &config.staker.pubkey(), + withdraw_authority, + &validator_list_keypair.pubkey(), + &reserve_keypair.pubkey(), + &mint_keypair.pubkey(), + pool_fee_account, + &spl_token::id(), + deposit_authority.as_ref().map(|x| x.pubkey()), + epoch_fee, + withdrawal_fee, + deposit_fee, + referral_fee, + max_validators, + )); + signers.push(stake_pool_keypair); + + if let Some(ref deposit_auth) = deposit_authority { + signers.push(deposit_auth); + println!( + "Deposits will be restricted to {} only, this can be changed using the set-funding-authority command.", + deposit_auth.pubkey() + ); + } + + unique_signers!(signers); + let transaction = checked_transaction_with_signers(config, &instructions, &signers)?; + + println!( + "Setting up and initializing stake pool account {} with validator list {}", + stake_pool_keypair.pubkey(), + validator_list_keypair.pubkey() + ); + send_transaction(config, transaction)?; + + Ok(()) +} + #[allow(clippy::too_many_arguments)] fn command_create_pool( config: &Config, @@ -316,14 +599,10 @@ fn command_create_pool( if !unsafe_fees { check_stake_pool_fees(&epoch_fee, &withdrawal_fee, &deposit_fee)?; } - let reserve_keypair = reserve_keypair.unwrap_or_else(Keypair::new); - println!("Creating reserve stake {}", reserve_keypair.pubkey()); + let reserve_keypair = reserve_keypair.unwrap_or_else(Keypair::new); let mint_keypair = mint_keypair.unwrap_or_else(Keypair::new); - println!("Creating mint {}", mint_keypair.pubkey()); - let stake_pool_keypair = stake_pool_keypair.unwrap_or_else(Keypair::new); - let validator_list_keypair = validator_list_keypair.unwrap_or_else(Keypair::new); let reserve_stake_balance = config @@ -362,131 +641,45 @@ fn command_create_pool( println!("Stake pool withdraw authority {}", withdraw_authority); } - let mut setup_instructions = vec![ - // Account for the stake pool reserve - system_instruction::create_account( - &config.fee_payer.pubkey(), - &reserve_keypair.pubkey(), - reserve_stake_balance, - STAKE_STATE_LEN as u64, - &stake::program::id(), - ), - stake::instruction::initialize( - &reserve_keypair.pubkey(), - &stake::state::Authorized { - staker: withdraw_authority, - withdrawer: withdraw_authority, - }, - &stake::state::Lockup::default(), - ), - // Account for the stake pool mint - system_instruction::create_account( - &config.fee_payer.pubkey(), - &mint_keypair.pubkey(), - mint_account_balance, - spl_token::state::Mint::LEN as u64, - &spl_token::id(), - ), - // Initialize pool token mint account - spl_token::instruction::initialize_mint( - &spl_token::id(), - &mint_keypair.pubkey(), - &withdraw_authority, - None, - default_decimals, - )?, - ]; - - let pool_fee_account = add_associated_token_account( + setup_reserve_stake_account( + config, + &reserve_keypair, + reserve_stake_balance, + &withdraw_authority, + )?; + setup_mint_account( + config, + &mint_keypair, + mint_account_balance, + &withdraw_authority, + default_decimals, + )?; + setup_pool_fee_account( config, &mint_keypair.pubkey(), - &config.manager.pubkey(), - &mut setup_instructions, &mut total_rent_free_balances, - ); - println!("Creating pool fee collection account {}", pool_fee_account); - - let initialize_instructions = &[ - // Validator stake account list storage - system_instruction::create_account( - &config.fee_payer.pubkey(), - &validator_list_keypair.pubkey(), - validator_list_balance, - validator_list_size as u64, - &spl_stake_pool::id(), - ), - // Account for the stake pool - system_instruction::create_account( - &config.fee_payer.pubkey(), - &stake_pool_keypair.pubkey(), - stake_pool_account_lamports, - get_packed_len::() as u64, - &spl_stake_pool::id(), - ), - // Initialize stake pool - spl_stake_pool::instruction::initialize( - &spl_stake_pool::id(), - &stake_pool_keypair.pubkey(), - &config.manager.pubkey(), - &config.staker.pubkey(), - &withdraw_authority, - &validator_list_keypair.pubkey(), - &reserve_keypair.pubkey(), - &mint_keypair.pubkey(), - &pool_fee_account, - &spl_token::id(), - deposit_authority.as_ref().map(|x| x.pubkey()), - epoch_fee, - withdrawal_fee, - deposit_fee, - referral_fee, - max_validators, - ), - ]; - - { - let mut setup_signers = vec![config.fee_payer.as_ref(), &mint_keypair, &reserve_keypair]; - unique_signers!(setup_signers); - - let setup_transaction = - checked_transaction_with_signers(config, &setup_instructions, &setup_signers)?; - - println!( - "Setting up required accounts for stake pool: reserve stake {} and mint {}", - reserve_keypair.pubkey(), - mint_keypair.pubkey() - ); - send_transaction(config, setup_transaction)?; - } + )?; - { - let mut initialize_signers = vec![ - config.fee_payer.as_ref(), - &stake_pool_keypair, - &validator_list_keypair, - config.manager.as_ref(), - ]; - let initialize_transaction = if let Some(deposit_authority) = deposit_authority { - println!( - "Deposits will be restricted to {} only, this can be changed using the set-funding-authority command.", - deposit_authority.pubkey() - ); - let mut initialize_signers = initialize_signers.clone(); - initialize_signers.push(&deposit_authority); - unique_signers!(initialize_signers); - checked_transaction_with_signers(config, initialize_instructions, &initialize_signers)? - } else { - unique_signers!(initialize_signers); - checked_transaction_with_signers(config, initialize_instructions, &initialize_signers)? - }; + let pool_fee_account = + get_associated_token_address(&config.manager.pubkey(), &mint_keypair.pubkey()); - println!( - "Creating stake pool {} with validator list {}", - stake_pool_keypair.pubkey(), - validator_list_keypair.pubkey() - ); - send_transaction(config, initialize_transaction)?; - } + setup_and_initialize_validator_list_with_stake_pool( + config, + &stake_pool_keypair, + &validator_list_keypair, + &reserve_keypair, + &mint_keypair, + &pool_fee_account, + deposit_authority, + epoch_fee, + withdrawal_fee, + deposit_fee, + referral_fee, + max_validators, + &withdraw_authority, + validator_list_balance, + validator_list_size, + )?; Ok(()) } @@ -775,11 +968,11 @@ fn add_associated_token_account( .get_minimum_balance_for_rent_exemption(spl_token::state::Account::LEN) .unwrap(); - #[allow(deprecated)] instructions.push(create_associated_token_account( &config.fee_payer.pubkey(), owner, mint, + &spl_token::id(), )); *rent_free_balances += min_account_balance; diff --git a/stake-pool/js/package.json b/stake-pool/js/package.json index 064afa8cfbb..98bcea1a23f 100644 --- a/stake-pool/js/package.json +++ b/stake-pool/js/package.json @@ -45,7 +45,7 @@ "dependencies": { "@solana/buffer-layout": "^4.0.1", "@solana/spl-token": "0.4.6", - "@solana/web3.js": "^1.95.1", + "@solana/web3.js": "^1.95.3", "bn.js": "^5.2.0", "buffer": "^6.0.3", "buffer-layout": "^1.2.2", @@ -61,20 +61,17 @@ "@rollup/plugin-typescript": "^11.1.6", "@types/bn.js": "^5.1.0", "@types/jest": "^29.5.12", - "@types/node": "^22.0.0", + "@types/node": "^22.4.2", "@types/node-fetch": "^2.6.11", - "@typescript-eslint/eslint-plugin": "^7.17.0", - "@typescript-eslint/parser": "^7.17.0", + "@typescript-eslint/eslint-plugin": "^8.2.0", + "@typescript-eslint/parser": "^8.2.0", "cross-env": "^7.0.3", "eslint": "^8.57.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-prettier": "^5.2.1", "jest": "^29.0.0", - "prettier": "^3.3.3", "rimraf": "^6.0.1", - "rollup": "^4.19.1", + "rollup": "^4.21.0", "rollup-plugin-dts": "^6.1.1", - "ts-jest": "^29.2.3", + "ts-jest": "^29.2.4", "typescript": "^5.5.4" }, "jest": { diff --git a/stake-pool/js/src/instructions.ts b/stake-pool/js/src/instructions.ts index e792dd12687..eba2f149538 100644 --- a/stake-pool/js/src/instructions.ts +++ b/stake-pool/js/src/instructions.ts @@ -90,6 +90,7 @@ export function tokenMetadataLayout( * @internal */ export const STAKE_POOL_INSTRUCTION_LAYOUTS: { + /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ [type in StakePoolInstructionType]: InstructionType; } = Object.freeze({ AddValidatorToPool: { diff --git a/stake-pool/program/Cargo.toml b/stake-pool/program/Cargo.toml index 94b1be949ef..2a1ff42ea2e 100644 --- a/stake-pool/program/Cargo.toml +++ b/stake-pool/program/Cargo.toml @@ -14,11 +14,11 @@ test-sbf = [] [dependencies] arrayref = "0.3.8" borsh = "1.5.1" -bytemuck = "1.16" +bytemuck = "1.17" num-derive = "0.4" num-traits = "0.2" -num_enum = "0.7.2" -serde = "1.0.204" +num_enum = "0.7.3" +serde = "1.0.208" serde_derive = "1.0.103" solana-program = "2.0.3" solana-security-txt = "1.1.1" diff --git a/stake-pool/program/src/instruction.rs b/stake-pool/program/src/instruction.rs index 5b370a93808..9709bef0fe3 100644 --- a/stake-pool/program/src/instruction.rs +++ b/stake-pool/program/src/instruction.rs @@ -1323,6 +1323,62 @@ pub fn decrease_validator_stake_with_vote( ) } +/// Create a `IncreaseAdditionalValidatorStake` instruction given an existing +/// stake pool, valiator list and vote account +pub fn increase_additional_validator_stake_with_list( + program_id: &Pubkey, + stake_pool: &StakePool, + validator_list: &ValidatorList, + stake_pool_address: &Pubkey, + vote_account_address: &Pubkey, + lamports: u64, + ephemeral_stake_seed: u64, +) -> Result { + let validator_info = validator_list + .find(vote_account_address) + .ok_or(ProgramError::InvalidInstructionData)?; + let transient_stake_seed = u64::from(validator_info.transient_seed_suffix); + let validator_stake_seed = NonZeroU32::new(validator_info.validator_seed_suffix.into()); + Ok(increase_additional_validator_stake_with_vote( + program_id, + stake_pool, + stake_pool_address, + vote_account_address, + lamports, + validator_stake_seed, + transient_stake_seed, + ephemeral_stake_seed, + )) +} + +/// Create a `DecreaseAdditionalValidatorStake` instruction given an existing +/// stake pool, valiator list and vote account +pub fn decrease_additional_validator_stake_with_list( + program_id: &Pubkey, + stake_pool: &StakePool, + validator_list: &ValidatorList, + stake_pool_address: &Pubkey, + vote_account_address: &Pubkey, + lamports: u64, + ephemeral_stake_seed: u64, +) -> Result { + let validator_info = validator_list + .find(vote_account_address) + .ok_or(ProgramError::InvalidInstructionData)?; + let transient_stake_seed = u64::from(validator_info.transient_seed_suffix); + let validator_stake_seed = NonZeroU32::new(validator_info.validator_seed_suffix.into()); + Ok(decrease_additional_validator_stake_with_vote( + program_id, + stake_pool, + stake_pool_address, + vote_account_address, + lamports, + validator_stake_seed, + transient_stake_seed, + ephemeral_stake_seed, + )) +} + /// Create a `DecreaseAdditionalValidatorStake` instruction given an existing /// stake pool and vote account pub fn decrease_additional_validator_stake_with_vote( diff --git a/token-collection/program/tests/setup.rs b/token-collection/program/tests/setup.rs index 709f6f92943..363fc5c1b5a 100644 --- a/token-collection/program/tests/setup.rs +++ b/token-collection/program/tests/setup.rs @@ -89,7 +89,7 @@ pub async fn setup_group( mint: &Keypair, mint_authority: &Keypair, update_authority: Option, - max_size: u32, + max_size: u64, rent_lamports: u64, space: usize, ) { diff --git a/token-collection/program/tests/token_collection.rs b/token-collection/program/tests/token_collection.rs index aeff8994ef8..79505163a43 100644 --- a/token-collection/program/tests/token_collection.rs +++ b/token-collection/program/tests/token_collection.rs @@ -325,7 +325,7 @@ async fn test_token_collection() { .data; let state = TlvStateBorrowed::unpack(&buffer).unwrap(); let collection = state.get_first_value::().unwrap(); - assert_eq!(u32::from(collection.size), 3); + assert_eq!(u64::from(collection.size), 3); // The "Snakes" collection should have 2 members let buffer = context @@ -337,7 +337,7 @@ async fn test_token_collection() { .data; let state = TlvStateBorrowed::unpack(&buffer).unwrap(); let collection = state.get_first_value::().unwrap(); - assert_eq!(u32::from(collection.size), 2); + assert_eq!(u64::from(collection.size), 2); // The "Python" should be a member of 2 collections let buffer = context diff --git a/token-group/example/tests/initialize_member.rs b/token-group/example/tests/initialize_member.rs index 6a0589e81e7..c5f6ba97869 100644 --- a/token-group/example/tests/initialize_member.rs +++ b/token-group/example/tests/initialize_member.rs @@ -256,5 +256,5 @@ async fn test_initialize_group_member() { let fetched_meta = TlvStateBorrowed::unpack(&member_account.data).unwrap(); let fetched_group_member_state = fetched_meta.get_first_value::().unwrap(); assert_eq!(fetched_group_member_state.group, group.pubkey()); - assert_eq!(u32::from(fetched_group_member_state.member_number), 1); + assert_eq!(u64::from(fetched_group_member_state.member_number), 1); } diff --git a/token-group/interface/Cargo.toml b/token-group/interface/Cargo.toml index 198934a64aa..81c0e3c26ce 100644 --- a/token-group/interface/Cargo.toml +++ b/token-group/interface/Cargo.toml @@ -8,7 +8,7 @@ license = "Apache-2.0" edition = "2021" [dependencies] -bytemuck = "1.16.1" +bytemuck = "1.17.0" solana-program = "2.0.3" spl-discriminator = { version = "0.3.0" , path = "../../libraries/discriminator" } spl-pod = { version = "0.3.0" , path = "../../libraries/pod", features = ["borsh"] } diff --git a/token-group/interface/README.md b/token-group/interface/README.md index e221d7f7d63..04925742cbc 100644 --- a/token-group/interface/README.md +++ b/token-group/interface/README.md @@ -95,7 +95,7 @@ For a group: ```rust type OptionalNonZeroPubkey = Pubkey; // if all zeroes, interpreted as `None` -type PodU32 = [u8; 4]; +type PodU64 = [u8; 8]; type Pubkey = [u8; 32]; /// Type discriminant: [214, 15, 63, 132, 49, 119, 209, 40] @@ -107,9 +107,9 @@ pub struct TokenGroup { /// belongs to a particular mint pub mint: Pubkey, /// The current number of group members - pub size: PodU32, + pub size: PodU64, /// The maximum number of group members - pub max_size: PodU32, + pub max_size: PodU64, } ``` @@ -125,7 +125,7 @@ pub struct TokenGroupMember { /// The pubkey of the `TokenGroup` pub group: Pubkey, /// The member number - pub member_number: PodU32, + pub member_number: PodU64, } ``` diff --git a/token-group/interface/src/instruction.rs b/token-group/interface/src/instruction.rs index 67536d896bd..2ba4306683b 100644 --- a/token-group/interface/src/instruction.rs +++ b/token-group/interface/src/instruction.rs @@ -11,7 +11,7 @@ use { spl_pod::{ bytemuck::{pod_bytes_of, pod_from_bytes}, optional_keys::OptionalNonZeroPubkey, - primitives::PodU32, + primitives::PodU64, }, }; @@ -23,7 +23,7 @@ pub struct InitializeGroup { /// Update authority for the group pub update_authority: OptionalNonZeroPubkey, /// The maximum number of group members - pub max_size: PodU32, + pub max_size: PodU64, } /// Instruction data for updating the max size of a `Group` @@ -32,7 +32,7 @@ pub struct InitializeGroup { #[discriminator_hash_input("spl_token_group_interface:update_group_max_size")] pub struct UpdateGroupMaxSize { /// New max size for the group - pub max_size: PodU32, + pub max_size: PodU64, } /// Instruction data for updating the authority of a `Group` @@ -155,7 +155,7 @@ pub fn initialize_group( mint: &Pubkey, mint_authority: &Pubkey, update_authority: Option, - max_size: u32, + max_size: u64, ) -> Instruction { let update_authority = OptionalNonZeroPubkey::try_from(update_authority) .expect("Failed to deserialize `Option`"); @@ -180,7 +180,7 @@ pub fn update_group_max_size( program_id: &Pubkey, group: &Pubkey, update_authority: &Pubkey, - max_size: u32, + max_size: u64, ) -> Instruction { let data = TokenGroupInstruction::UpdateGroupMaxSize(UpdateGroupMaxSize { max_size: max_size.into(), diff --git a/token-group/interface/src/state.rs b/token-group/interface/src/state.rs index 9f73ecc442b..0766f6bad6e 100644 --- a/token-group/interface/src/state.rs +++ b/token-group/interface/src/state.rs @@ -5,7 +5,7 @@ use { bytemuck::{Pod, Zeroable}, solana_program::{program_error::ProgramError, pubkey::Pubkey}, spl_discriminator::SplDiscriminate, - spl_pod::{error::PodSliceError, optional_keys::OptionalNonZeroPubkey, primitives::PodU32}, + spl_pod::{error::PodSliceError, optional_keys::OptionalNonZeroPubkey, primitives::PodU64}, }; /// Data struct for a `TokenGroup` @@ -19,26 +19,26 @@ pub struct TokenGroup { /// belongs to a particular mint pub mint: Pubkey, /// The current number of group members - pub size: PodU32, + pub size: PodU64, /// The maximum number of group members - pub max_size: PodU32, + pub max_size: PodU64, } impl TokenGroup { /// Creates a new `TokenGroup` state - pub fn new(mint: &Pubkey, update_authority: OptionalNonZeroPubkey, max_size: u32) -> Self { + pub fn new(mint: &Pubkey, update_authority: OptionalNonZeroPubkey, max_size: u64) -> Self { Self { mint: *mint, update_authority, - size: PodU32::default(), // [0, 0, 0, 0] + size: PodU64::default(), // [0, 0, 0, 0, 0, 0, 0, 0] max_size: max_size.into(), } } /// Updates the max size for a group - pub fn update_max_size(&mut self, new_max_size: u32) -> Result<(), ProgramError> { + pub fn update_max_size(&mut self, new_max_size: u64) -> Result<(), ProgramError> { // The new max size cannot be less than the current size - if new_max_size < u32::from(self.size) { + if new_max_size < u64::from(self.size) { return Err(TokenGroupError::SizeExceedsNewMaxSize.into()); } self.max_size = new_max_size.into(); @@ -46,12 +46,12 @@ impl TokenGroup { } /// Increment the size for a group, returning the new size - pub fn increment_size(&mut self) -> Result { + pub fn increment_size(&mut self) -> Result { // The new size cannot be greater than the max size - let new_size = u32::from(self.size) + let new_size = u64::from(self.size) .checked_add(1) .ok_or::(PodSliceError::CalculationFailure.into())?; - if new_size > u32::from(self.max_size) { + if new_size > u64::from(self.max_size) { return Err(TokenGroupError::SizeExceedsMaxSize.into()); } self.size = new_size.into(); @@ -70,11 +70,11 @@ pub struct TokenGroupMember { /// The pubkey of the `TokenGroup` pub group: Pubkey, /// The member number - pub member_number: PodU32, + pub member_number: PodU64, } impl TokenGroupMember { /// Creates a new `TokenGroupMember` state - pub fn new(mint: &Pubkey, group: &Pubkey, member_number: u32) -> Self { + pub fn new(mint: &Pubkey, group: &Pubkey, member_number: u64) -> Self { Self { mint: *mint, group: *group, @@ -156,7 +156,7 @@ mod tests { let new_max_size = 30; group.update_max_size(new_max_size).unwrap(); - assert_eq!(u32::from(group.max_size), new_max_size); + assert_eq!(u64::from(group.max_size), new_max_size); // Change the current size to 30 group.size = 30.into(); @@ -170,7 +170,7 @@ mod tests { let new_max_size = 30; group.update_max_size(new_max_size).unwrap(); - assert_eq!(u32::from(group.max_size), new_max_size); + assert_eq!(u64::from(group.max_size), new_max_size); } #[test] @@ -183,7 +183,7 @@ mod tests { }; group.increment_size().unwrap(); - assert_eq!(u32::from(group.size), 1); + assert_eq!(u64::from(group.size), 1); // Try to increase the current size to 2, which is greater than the max size assert_eq!( diff --git a/token-group/js/.prettierignore b/token-group/js/.prettierignore deleted file mode 100644 index 6da325effab..00000000000 --- a/token-group/js/.prettierignore +++ /dev/null @@ -1,5 +0,0 @@ -docs -lib -test-ledger - -package-lock.json diff --git a/token-group/js/.prettierrc b/token-group/js/.prettierrc deleted file mode 100644 index b9ce4c1923a..00000000000 --- a/token-group/js/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "printWidth": 120, - "trailingComma": "es5", - "tabWidth": 4, - "semi": true, - "singleQuote": true -} \ No newline at end of file diff --git a/token-group/js/package.json b/token-group/js/package.json index 3789602aff9..fb386361ce3 100644 --- a/token-group/js/package.json +++ b/token-group/js/package.json @@ -33,9 +33,8 @@ "deploy": "npm run deploy:docs", "deploy:docs": "npm run docs && gh-pages --dest token-group/js --dist docs --dotfiles", "docs": "shx rm -rf docs && typedoc && shx cp .nojekyll docs/", - "fmt": "prettier --write '{*,**/*}.{ts,tsx,js,jsx,json}'", - "lint": "prettier --check '{*,**/*}.{ts,tsx,js,jsx,json}' && eslint --max-warnings 0 .", - "lint:fix": "npm run fmt && eslint --fix .", + "lint": "eslint --max-warnings 0 .", + "lint:fix": "eslint --fix .", "nuke": "shx rm -rf node_modules package-lock.json || true", "postbuild": "shx echo '{ \"type\": \"commonjs\" }' > lib/cjs/package.json", "reinstall": "npm run nuke && npm install", @@ -44,31 +43,28 @@ "watch": "tsc --build --verbose --watch tsconfig.all.json" }, "peerDependencies": { - "@solana/web3.js": "^1.95.1" + "@solana/web3.js": "^1.95.3" }, "dependencies": { - "@solana/codecs": "2.0.0-preview.4", + "@solana/codecs": "2.0.0-rc.1", "@solana/spl-type-length-value": "0.1.0" }, "devDependencies": { - "@solana/web3.js": "^1.95.1", - "@types/chai": "^4.3.16", + "@solana/web3.js": "^1.95.3", + "@types/chai": "^4.3.17", "@types/mocha": "^10.0.7", - "@types/node": "^22.0.0", - "@typescript-eslint/eslint-plugin": "^7.17.0", - "@typescript-eslint/parser": "^7.17.0", + "@types/node": "^22.4.2", + "@typescript-eslint/eslint-plugin": "^8.2.0", + "@typescript-eslint/parser": "^8.2.0", "chai": "^5.1.1", "eslint": "^8.57.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-require-extensions": "^0.1.1", "gh-pages": "^6.1.1", - "mocha": "^10.7.0", - "prettier": "^3.3.3", + "mocha": "^10.7.3", "shx": "^0.3.4", "ts-node": "^10.9.2", "tslib": "^2.6.3", - "typedoc": "^0.26.5", + "typedoc": "^0.26.6", "typescript": "^5.5.4" } } diff --git a/token-group/js/src/instruction.ts b/token-group/js/src/instruction.ts index ab08a770da0..6d14cbb4b95 100644 --- a/token-group/js/src/instruction.ts +++ b/token-group/js/src/instruction.ts @@ -5,7 +5,7 @@ import { getBytesEncoder, getStructEncoder, getTupleEncoder, - getU32Encoder, + getU64Encoder, transformEncoder, } from '@solana/codecs'; import { splDiscriminate } from '@solana/spl-type-length-value'; @@ -28,7 +28,7 @@ export interface InitializeGroupInstruction { mint: PublicKey; mintAuthority: PublicKey; updateAuthority: PublicKey | null; - maxSize: number; + maxSize: bigint; } export function createInitializeGroupInstruction(args: InitializeGroupInstruction): TransactionInstruction { @@ -46,9 +46,9 @@ export function createInitializeGroupInstruction(args: InitializeGroupInstructio splDiscriminate('spl_token_group_interface:initialize_token_group'), getStructEncoder([ ['updateAuthority', getPublicKeyEncoder()], - ['maxSize', getU32Encoder()], - ]) - ).encode({ updateAuthority: updateAuthority ?? SystemProgram.programId, maxSize }) + ['maxSize', getU64Encoder()], + ]), + ).encode({ updateAuthority: updateAuthority ?? SystemProgram.programId, maxSize }), ), }); } @@ -57,7 +57,7 @@ export interface UpdateGroupMaxSize { programId: PublicKey; group: PublicKey; updateAuthority: PublicKey; - maxSize: number; + maxSize: bigint; } export function createUpdateGroupMaxSizeInstruction(args: UpdateGroupMaxSize): TransactionInstruction { @@ -71,8 +71,8 @@ export function createUpdateGroupMaxSizeInstruction(args: UpdateGroupMaxSize): T data: Buffer.from( getInstructionEncoder( splDiscriminate('spl_token_group_interface:update_group_max_size'), - getStructEncoder([['maxSize', getU32Encoder()]]) - ).encode({ maxSize }) + getStructEncoder([['maxSize', getU64Encoder()]]), + ).encode({ maxSize }), ), }); } @@ -96,8 +96,8 @@ export function createUpdateGroupAuthorityInstruction(args: UpdateGroupAuthority data: Buffer.from( getInstructionEncoder( splDiscriminate('spl_token_group_interface:update_authority'), - getStructEncoder([['newAuthority', getPublicKeyEncoder()]]) - ).encode({ newAuthority: newAuthority ?? SystemProgram.programId }) + getStructEncoder([['newAuthority', getPublicKeyEncoder()]]), + ).encode({ newAuthority: newAuthority ?? SystemProgram.programId }), ), }); } @@ -126,8 +126,8 @@ export function createInitializeMemberInstruction(args: InitializeMember): Trans data: Buffer.from( getInstructionEncoder( splDiscriminate('spl_token_group_interface:initialize_member'), - getStructEncoder([]) - ).encode({}) + getStructEncoder([]), + ).encode({}), ), }); } diff --git a/token-group/js/src/state/tokenGroup.ts b/token-group/js/src/state/tokenGroup.ts index ecc51254268..c0895e28507 100644 --- a/token-group/js/src/state/tokenGroup.ts +++ b/token-group/js/src/state/tokenGroup.ts @@ -1,12 +1,12 @@ import { PublicKey } from '@solana/web3.js'; import type { ReadonlyUint8Array } from '@solana/codecs'; -import { fixCodecSize, getBytesCodec, getStructCodec, getU32Codec } from '@solana/codecs'; +import { fixCodecSize, getBytesCodec, getStructCodec, getU64Codec } from '@solana/codecs'; const tokenGroupCodec = getStructCodec([ ['updateAuthority', fixCodecSize(getBytesCodec(), 32)], ['mint', fixCodecSize(getBytesCodec(), 32)], - ['size', getU32Codec()], - ['maxSize', getU32Codec()], + ['size', getU64Codec()], + ['maxSize', getU64Codec()], ]); export const TOKEN_GROUP_SIZE = tokenGroupCodec.fixedSize; @@ -17,9 +17,9 @@ export interface TokenGroup { /** The associated mint, used to counter spoofing to be sure that group belongs to a particular mint */ mint: PublicKey; /** The current number of group members */ - size: number; + size: bigint; /** The maximum number of group members */ - maxSize: number; + maxSize: bigint; } // Checks if all elements in the array are 0 diff --git a/token-group/js/src/state/tokenGroupMember.ts b/token-group/js/src/state/tokenGroupMember.ts index 8d49a0316a7..c952c4faaa1 100644 --- a/token-group/js/src/state/tokenGroupMember.ts +++ b/token-group/js/src/state/tokenGroupMember.ts @@ -1,11 +1,11 @@ import { PublicKey } from '@solana/web3.js'; import type { ReadonlyUint8Array } from '@solana/codecs'; -import { fixCodecSize, getBytesCodec, getStructCodec, getU32Codec } from '@solana/codecs'; +import { fixCodecSize, getBytesCodec, getStructCodec, getU64Codec } from '@solana/codecs'; const tokenGroupMemberCodec = getStructCodec([ ['mint', fixCodecSize(getBytesCodec(), 32)], ['group', fixCodecSize(getBytesCodec(), 32)], - ['memberNumber', getU32Codec()], + ['memberNumber', getU64Codec()], ]); export const TOKEN_GROUP_MEMBER_SIZE = tokenGroupMemberCodec.fixedSize; @@ -16,7 +16,7 @@ export interface TokenGroupMember { /** The pubkey of the `TokenGroup` */ group: PublicKey; /** The member number */ - memberNumber: number; + memberNumber: bigint; } // Pack TokenGroupMember into byte slab diff --git a/token-group/js/test/instruction.test.ts b/token-group/js/test/instruction.test.ts index a0454cf4a91..ad8f405d406 100644 --- a/token-group/js/test/instruction.test.ts +++ b/token-group/js/test/instruction.test.ts @@ -1,6 +1,6 @@ import { expect } from 'chai'; import type { Decoder } from '@solana/codecs'; -import { fixDecoderSize, getBytesDecoder, getStructDecoder, getU32Decoder } from '@solana/codecs'; +import { fixDecoderSize, getBytesDecoder, getStructDecoder, getU64Decoder } from '@solana/codecs'; import { splDiscriminate } from '@solana/spl-type-length-value'; import { PublicKey, type TransactionInstruction } from '@solana/web3.js'; @@ -15,7 +15,7 @@ function checkPackUnpack( instruction: TransactionInstruction, discriminator: Uint8Array, decoder: Decoder, - values: T + values: T, ) { expect(instruction.data.subarray(0, 8)).to.deep.equal(discriminator); const unpacked = decoder.decode(instruction.data.subarray(8)); @@ -28,7 +28,7 @@ describe('Token Group Instructions', () => { const updateAuthority = new PublicKey('44444444444444444444444444444444444444444444'); const mint = new PublicKey('55555555555555555555555555555555555555555555'); const mintAuthority = new PublicKey('66666666666666666666666666666666666666666666'); - const maxSize = 100; + const maxSize = BigInt(100); it('Can create InitializeGroup Instruction', () => { checkPackUnpack( @@ -43,9 +43,9 @@ describe('Token Group Instructions', () => { splDiscriminate('spl_token_group_interface:initialize_token_group'), getStructDecoder([ ['updateAuthority', fixDecoderSize(getBytesDecoder(), 32)], - ['maxSize', getU32Decoder()], + ['maxSize', getU64Decoder()], ]), - { updateAuthority: Uint8Array.from(updateAuthority.toBuffer()), maxSize } + { updateAuthority: Uint8Array.from(updateAuthority.toBuffer()), maxSize }, ); }); @@ -58,8 +58,8 @@ describe('Token Group Instructions', () => { maxSize, }), splDiscriminate('spl_token_group_interface:update_group_max_size'), - getStructDecoder([['maxSize', getU32Decoder()]]), - { maxSize } + getStructDecoder([['maxSize', getU64Decoder()]]), + { maxSize }, ); }); @@ -73,7 +73,7 @@ describe('Token Group Instructions', () => { }), splDiscriminate('spl_token_group_interface:update_authority'), getStructDecoder([['newAuthority', fixDecoderSize(getBytesDecoder(), 32)]]), - { newAuthority: Uint8Array.from(PublicKey.default.toBuffer()) } + { newAuthority: Uint8Array.from(PublicKey.default.toBuffer()) }, ); }); @@ -95,7 +95,7 @@ describe('Token Group Instructions', () => { }), splDiscriminate('spl_token_group_interface:initialize_member'), getStructDecoder([]), - {} + {}, ); }); }); diff --git a/token-group/js/test/state.test.ts b/token-group/js/test/state.test.ts index 36f874b5209..f071de87dc1 100644 --- a/token-group/js/test/state.test.ts +++ b/token-group/js/test/state.test.ts @@ -16,16 +16,16 @@ describe('Token Group State', () => { checkPackUnpack({ mint: new PublicKey('44444444444444444444444444444444444444444444'), updateAuthority: new PublicKey('55555555555555555555555555555555555555555555'), - size: 10, - maxSize: 20, + size: BigInt(10), + maxSize: BigInt(20), }); }); it('Can pack and unpack TokenGroup without updateAuthoritygroup', () => { checkPackUnpack({ mint: new PublicKey('44444444444444444444444444444444444444444444'), - size: 10, - maxSize: 20, + size: BigInt(10), + maxSize: BigInt(20), }); }); }); @@ -40,7 +40,7 @@ describe('Token Group State', () => { checkPackUnpack({ mint: new PublicKey('55555555555555555555555555555555555555555555'), group: new PublicKey('66666666666666666666666666666666666666666666'), - memberNumber: 8, + memberNumber: BigInt(8), }); }); }); diff --git a/token-lending/js/.prettierignore b/token-lending/js/.prettierignore deleted file mode 100644 index 741a987a650..00000000000 --- a/token-lending/js/.prettierignore +++ /dev/null @@ -1,2 +0,0 @@ -docs -lib diff --git a/token-lending/js/.prettierrc b/token-lending/js/.prettierrc deleted file mode 100644 index f19e7945f9a..00000000000 --- a/token-lending/js/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "printWidth": 120, - "trailingComma": "es5", - "tabWidth": 4, - "semi": true, - "singleQuote": true -} \ No newline at end of file diff --git a/token-lending/js/package.json b/token-lending/js/package.json index 08c5beedac4..fda450b0720 100644 --- a/token-lending/js/package.json +++ b/token-lending/js/package.json @@ -27,8 +27,8 @@ "build": "tsc && rollup --configPlugin @rollup/plugin-typescript -c && rm lib/rollup.config.d.ts", "deploy": "npm docs && gh-pages -d docs -e token-lending", "docs": "typedoc", - "lint": "eslint . --ext .ts --max-warnings 0 && prettier --check '**/*.{ts,js,json}'", - "lint:fix": "eslint . --ext .ts --fix && prettier --write '**/*.{ts,js,json}'" + "lint": "eslint . --ext .ts --max-warnings 0", + "lint:fix": "eslint . --ext .ts --fix" }, "dependencies": { "@solana/buffer-layout": "^4.0.0", @@ -45,21 +45,17 @@ "@rollup/plugin-node-resolve": "^15.0.2", "@rollup/plugin-typescript": "^11.1.6", "@solana/spl-token": "0.4.6", - "@solana/web3.js": "^1.95.1", + "@solana/web3.js": "^1.95.3", "@types/eslint": "^8.56.7", - "@types/eslint-plugin-prettier": "^3.1.0", - "@types/node": "^22.0.0", - "@typescript-eslint/eslint-plugin": "^7.17.0", - "@typescript-eslint/parser": "^7.17.0", + "@types/node": "^22.4.2", + "@typescript-eslint/eslint-plugin": "^8.2.0", + "@typescript-eslint/parser": "^8.2.0", "eslint": "^8.57.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-prettier": "^5.2.1", "gh-pages": "^6.1.1", - "prettier": "^3.3.3", - "rollup": "^4.19.1", + "rollup": "^4.21.0", "ts-node": "^10.9.2", "tslib": "^2.6.3", - "typedoc": "^0.26.5", + "typedoc": "^0.26.6", "typescript": "^5.5.4" } } diff --git a/token-lending/js/src/instructions/borrowObligationLiquidity.ts b/token-lending/js/src/instructions/borrowObligationLiquidity.ts index 0c7da66efd1..9b30eabc090 100644 --- a/token-lending/js/src/instructions/borrowObligationLiquidity.ts +++ b/token-lending/js/src/instructions/borrowObligationLiquidity.ts @@ -22,7 +22,7 @@ export const borrowObligationLiquidityInstruction = ( lendingMarket: PublicKey, lendingMarketAuthority: PublicKey, obligationOwner: PublicKey, - hostFeeReceiver?: PublicKey + hostFeeReceiver?: PublicKey, ): TransactionInstruction => { const data = Buffer.alloc(DataLayout.span); DataLayout.encode( @@ -30,7 +30,7 @@ export const borrowObligationLiquidityInstruction = ( instruction: LendingInstruction.BorrowObligationLiquidity, liquidityAmount: BigInt(liquidityAmount), }, - data + data, ); const keys = [ diff --git a/token-lending/js/src/instructions/depositObligationCollateral.ts b/token-lending/js/src/instructions/depositObligationCollateral.ts index 3dca3dc6cff..ab960c70058 100644 --- a/token-lending/js/src/instructions/depositObligationCollateral.ts +++ b/token-lending/js/src/instructions/depositObligationCollateral.ts @@ -20,7 +20,7 @@ export const depositObligationCollateralInstruction = ( obligation: PublicKey, lendingMarket: PublicKey, obligationOwner: PublicKey, - transferAuthority: PublicKey + transferAuthority: PublicKey, ): TransactionInstruction => { const data = Buffer.alloc(DataLayout.span); DataLayout.encode( @@ -28,7 +28,7 @@ export const depositObligationCollateralInstruction = ( instruction: LendingInstruction.DepositObligationCollateral, collateralAmount: BigInt(collateralAmount), }, - data + data, ); const keys = [ diff --git a/token-lending/js/src/instructions/depositReserveLiquidity.ts b/token-lending/js/src/instructions/depositReserveLiquidity.ts index 588e63eb3bb..e61561c4761 100644 --- a/token-lending/js/src/instructions/depositReserveLiquidity.ts +++ b/token-lending/js/src/instructions/depositReserveLiquidity.ts @@ -21,7 +21,7 @@ export const depositReserveLiquidityInstruction = ( reserveCollateralMint: PublicKey, lendingMarket: PublicKey, lendingMarketAuthority: PublicKey, - transferAuthority: PublicKey + transferAuthority: PublicKey, ): TransactionInstruction => { const data = Buffer.alloc(DataLayout.span); DataLayout.encode( @@ -29,7 +29,7 @@ export const depositReserveLiquidityInstruction = ( instruction: LendingInstruction.DepositReserveLiquidity, liquidityAmount: BigInt(liquidityAmount), }, - data + data, ); const keys = [ diff --git a/token-lending/js/src/instructions/flashLoan.ts b/token-lending/js/src/instructions/flashLoan.ts index 35d5bf01425..96d7cfc68d8 100644 --- a/token-lending/js/src/instructions/flashLoan.ts +++ b/token-lending/js/src/instructions/flashLoan.ts @@ -22,7 +22,7 @@ export const flashLoanInstruction = ( lendingMarket: PublicKey, lendingMarketAuthority: PublicKey, flashLoanProgram: PublicKey, - transferAuthority: PublicKey + transferAuthority: PublicKey, ): TransactionInstruction => { const data = Buffer.alloc(DataLayout.span); DataLayout.encode( @@ -30,7 +30,7 @@ export const flashLoanInstruction = ( instruction: LendingInstruction.FlashLoan, liquidityAmount: BigInt(liquidityAmount), }, - data + data, ); const keys = [ diff --git a/token-lending/js/src/instructions/initLendingMarket.ts b/token-lending/js/src/instructions/initLendingMarket.ts index 9c05c08bf47..f9f83f9f177 100644 --- a/token-lending/js/src/instructions/initLendingMarket.ts +++ b/token-lending/js/src/instructions/initLendingMarket.ts @@ -16,7 +16,7 @@ const DataLayout = struct([u8('instruction'), publicKey('owner'), blob(32, export const initLendingMarketInstruction = ( owner: PublicKey, quoteCurrency: Uint8Array, - lendingMarket: PublicKey + lendingMarket: PublicKey, ): TransactionInstruction => { const data = Buffer.alloc(DataLayout.span); DataLayout.encode( @@ -25,7 +25,7 @@ export const initLendingMarketInstruction = ( owner, quoteCurrency, }, - data + data, ); const keys = [ diff --git a/token-lending/js/src/instructions/initObligation.ts b/token-lending/js/src/instructions/initObligation.ts index 60911f43326..b59ef1d5357 100644 --- a/token-lending/js/src/instructions/initObligation.ts +++ b/token-lending/js/src/instructions/initObligation.ts @@ -13,7 +13,7 @@ const DataLayout = struct([u8('instruction')]); export const initObligationInstruction = ( obligation: PublicKey, lendingMarket: PublicKey, - obligationOwner: PublicKey + obligationOwner: PublicKey, ): TransactionInstruction => { const data = Buffer.alloc(DataLayout.span); DataLayout.encode({ instruction: LendingInstruction.InitObligation }, data); diff --git a/token-lending/js/src/instructions/initReserve.ts b/token-lending/js/src/instructions/initReserve.ts index 936459d619f..4d5c6ee567e 100644 --- a/token-lending/js/src/instructions/initReserve.ts +++ b/token-lending/js/src/instructions/initReserve.ts @@ -30,7 +30,7 @@ export const initReserveInstruction = ( lendingMarket: PublicKey, lendingMarketAuthority: PublicKey, lendingMarketOwner: PublicKey, - transferAuthority: PublicKey + transferAuthority: PublicKey, ): TransactionInstruction => { const data = Buffer.alloc(DataLayout.span); DataLayout.encode( @@ -39,7 +39,7 @@ export const initReserveInstruction = ( liquidityAmount: BigInt(liquidityAmount), config, }, - data + data, ); const keys = [ diff --git a/token-lending/js/src/instructions/liquidateObligation.ts b/token-lending/js/src/instructions/liquidateObligation.ts index df0f338a90e..77264289299 100644 --- a/token-lending/js/src/instructions/liquidateObligation.ts +++ b/token-lending/js/src/instructions/liquidateObligation.ts @@ -23,7 +23,7 @@ export const liquidateObligationInstruction = ( obligation: PublicKey, lendingMarket: PublicKey, lendingMarketAuthority: PublicKey, - transferAuthority: PublicKey + transferAuthority: PublicKey, ): TransactionInstruction => { const data = Buffer.alloc(DataLayout.span); DataLayout.encode( @@ -31,7 +31,7 @@ export const liquidateObligationInstruction = ( instruction: LendingInstruction.LiquidateObligation, liquidityAmount: BigInt(liquidityAmount), }, - data + data, ); const keys = [ diff --git a/token-lending/js/src/instructions/redeemReserveCollateral.ts b/token-lending/js/src/instructions/redeemReserveCollateral.ts index 40c66a1350c..c4bbf09566e 100644 --- a/token-lending/js/src/instructions/redeemReserveCollateral.ts +++ b/token-lending/js/src/instructions/redeemReserveCollateral.ts @@ -21,7 +21,7 @@ export const redeemReserveCollateralInstruction = ( reserveLiquiditySupply: PublicKey, lendingMarket: PublicKey, lendingMarketAuthority: PublicKey, - transferAuthority: PublicKey + transferAuthority: PublicKey, ): TransactionInstruction => { const data = Buffer.alloc(DataLayout.span); DataLayout.encode( @@ -29,7 +29,7 @@ export const redeemReserveCollateralInstruction = ( instruction: LendingInstruction.RedeemReserveCollateral, collateralAmount: BigInt(collateralAmount), }, - data + data, ); const keys = [ diff --git a/token-lending/js/src/instructions/refreshObligation.ts b/token-lending/js/src/instructions/refreshObligation.ts index e673dd5192b..f2eb43ee2e2 100644 --- a/token-lending/js/src/instructions/refreshObligation.ts +++ b/token-lending/js/src/instructions/refreshObligation.ts @@ -12,7 +12,7 @@ const DataLayout = struct([u8('instruction')]); export const refreshObligationInstruction = ( obligation: PublicKey, depositReserves: PublicKey[], - borrowReserves: PublicKey[] + borrowReserves: PublicKey[], ): TransactionInstruction => { const data = Buffer.alloc(DataLayout.span); DataLayout.encode({ instruction: LendingInstruction.RefreshObligation }, data); diff --git a/token-lending/js/src/instructions/repayObligationLiquidity.ts b/token-lending/js/src/instructions/repayObligationLiquidity.ts index 0a63173ff25..7af02463fb8 100644 --- a/token-lending/js/src/instructions/repayObligationLiquidity.ts +++ b/token-lending/js/src/instructions/repayObligationLiquidity.ts @@ -19,7 +19,7 @@ export const repayObligationLiquidityInstruction = ( repayReserve: PublicKey, obligation: PublicKey, lendingMarket: PublicKey, - transferAuthority: PublicKey + transferAuthority: PublicKey, ): TransactionInstruction => { const data = Buffer.alloc(DataLayout.span); DataLayout.encode( @@ -27,7 +27,7 @@ export const repayObligationLiquidityInstruction = ( instruction: LendingInstruction.RepayObligationLiquidity, liquidityAmount: BigInt(liquidityAmount), }, - data + data, ); const keys = [ diff --git a/token-lending/js/src/instructions/setLendingMarketOwner.ts b/token-lending/js/src/instructions/setLendingMarketOwner.ts index b1b98a95299..9007d72e5a1 100644 --- a/token-lending/js/src/instructions/setLendingMarketOwner.ts +++ b/token-lending/js/src/instructions/setLendingMarketOwner.ts @@ -14,7 +14,7 @@ const DataLayout = struct([u8('instruction'), publicKey('newOwner')]); export const setLendingMarketOwnerInstruction = ( newOwner: PublicKey, lendingMarket: PublicKey, - currentOwner: PublicKey + currentOwner: PublicKey, ): TransactionInstruction => { const data = Buffer.alloc(DataLayout.span); DataLayout.encode( @@ -22,7 +22,7 @@ export const setLendingMarketOwnerInstruction = ( instruction: LendingInstruction.SetLendingMarketOwner, newOwner, }, - data + data, ); const keys = [ diff --git a/token-lending/js/src/instructions/withdrawObligationCollateral.ts b/token-lending/js/src/instructions/withdrawObligationCollateral.ts index 1b8178c6cf5..9087fc316b3 100644 --- a/token-lending/js/src/instructions/withdrawObligationCollateral.ts +++ b/token-lending/js/src/instructions/withdrawObligationCollateral.ts @@ -20,7 +20,7 @@ export const withdrawObligationCollateralInstruction = ( obligation: PublicKey, lendingMarket: PublicKey, lendingMarketAuthority: PublicKey, - obligationOwner: PublicKey + obligationOwner: PublicKey, ): TransactionInstruction => { const data = Buffer.alloc(DataLayout.span); DataLayout.encode( @@ -28,7 +28,7 @@ export const withdrawObligationCollateralInstruction = ( instruction: LendingInstruction.WithdrawObligationCollateral, collateralAmount: BigInt(collateralAmount), }, - data + data, ); const keys = [ diff --git a/token-lending/js/src/state/lendingMarket.ts b/token-lending/js/src/state/lendingMarket.ts index 8768a3138f7..3a6530535b4 100644 --- a/token-lending/js/src/state/lendingMarket.ts +++ b/token-lending/js/src/state/lendingMarket.ts @@ -23,7 +23,7 @@ export const LendingMarketLayout = struct( publicKey('oracleProgramId'), blob(128, 'padding'), ], - 'lendingMarket' + 'lendingMarket', ); export const LENDING_MARKET_SIZE = LendingMarketLayout.span; diff --git a/token-lending/js/src/state/obligation.ts b/token-lending/js/src/state/obligation.ts index ba455df1ab0..61949959e4e 100644 --- a/token-lending/js/src/state/obligation.ts +++ b/token-lending/js/src/state/obligation.ts @@ -49,7 +49,7 @@ export interface ObligationDataFlat { /** @internal */ export const ObligationCollateralLayout = struct( [publicKey('depositReserve'), u64('depositedAmount'), decimal('marketValue')], - 'collateral' + 'collateral', ); /** @internal */ @@ -60,7 +60,7 @@ export const ObligationLiquidityLayout = struct( decimal('borrowedAmountWads'), decimal('marketValue'), ], - 'liquidity' + 'liquidity', ); /** @internal */ @@ -78,7 +78,7 @@ export const ObligationLayout = struct( u8('borrowsLen'), blob(ObligationCollateralLayout.span + 9 * ObligationLiquidityLayout.span, 'dataFlat'), ], - 'obligation' + 'obligation', ); export const OBLIGATION_SIZE = ObligationLayout.span; diff --git a/token-lending/js/src/state/reserve.ts b/token-lending/js/src/state/reserve.ts index 1c242a67006..0c9ac718adb 100644 --- a/token-lending/js/src/state/reserve.ts +++ b/token-lending/js/src/state/reserve.ts @@ -63,19 +63,19 @@ export const ReserveLiquidityLayout = struct( decimal('cumulativeBorrowRateWads'), decimal('marketPrice'), ], - 'liquidity' + 'liquidity', ); /** @internal */ export const ReserveCollateralLayout = struct( [publicKey('mintPubkey'), u64('mintTotalSupply'), publicKey('supplyPubkey')], - 'collateral' + 'collateral', ); /** @internal */ export const ReserveFeesLayout = struct( [u64('borrowFeeWad'), u64('flashLoanFeeWad'), u8('hostFeePercentage')], - 'fees' + 'fees', ); /** @internal */ @@ -90,7 +90,7 @@ export const ReserveConfigLayout = struct( u8('maxBorrowRate'), ReserveFeesLayout, ], - 'config' + 'config', ); /** @internal */ diff --git a/token-lending/js/src/util/layout.ts b/token-lending/js/src/util/layout.ts index dc55b09b82b..6d1a3cb8dec 100644 --- a/token-lending/js/src/util/layout.ts +++ b/token-lending/js/src/util/layout.ts @@ -2,7 +2,7 @@ import { AccountInfo, PublicKey } from '@solana/web3.js'; export type Parser = ( pubkey: PublicKey, - info: AccountInfo + info: AccountInfo, ) => | { pubkey: PublicKey; diff --git a/token-lending/program/Cargo.toml b/token-lending/program/Cargo.toml index 4932f36391a..eff6aa9f4cf 100644 --- a/token-lending/program/Cargo.toml +++ b/token-lending/program/Cargo.toml @@ -13,7 +13,7 @@ test-sbf = [] [dependencies] arrayref = "0.3.8" -bytemuck = "1.16.1" +bytemuck = "1.17.0" num-derive = "0.4" num-traits = "0.2" solana-program = "2.0.3" diff --git a/token-metadata/interface/Cargo.toml b/token-metadata/interface/Cargo.toml index c752adb419b..57e93778794 100644 --- a/token-metadata/interface/Cargo.toml +++ b/token-metadata/interface/Cargo.toml @@ -12,7 +12,7 @@ serde-traits = ["dep:serde", "spl-pod/serde-traits"] [dependencies] borsh = "1.5.1" -serde = { version = "1.0.204", optional = true } +serde = { version = "1.0.208", optional = true } solana-program = "2.0.3" spl-discriminator = { version = "0.3.0", path = "../../libraries/discriminator" } spl-program-error = { version = "0.5.0", path = "../../libraries/program-error" } @@ -22,7 +22,7 @@ spl-pod = { version = "0.3.0", path = "../../libraries/pod", features = [ ] } [dev-dependencies] -serde_json = "1.0.121" +serde_json = "1.0.125" [lib] crate-type = ["cdylib", "lib"] diff --git a/token-metadata/js/.prettierignore b/token-metadata/js/.prettierignore deleted file mode 100644 index 6da325effab..00000000000 --- a/token-metadata/js/.prettierignore +++ /dev/null @@ -1,5 +0,0 @@ -docs -lib -test-ledger - -package-lock.json diff --git a/token-metadata/js/.prettierrc b/token-metadata/js/.prettierrc deleted file mode 100644 index b9ce4c1923a..00000000000 --- a/token-metadata/js/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "printWidth": 120, - "trailingComma": "es5", - "tabWidth": 4, - "semi": true, - "singleQuote": true -} \ No newline at end of file diff --git a/token-metadata/js/package.json b/token-metadata/js/package.json index 12f68aec4e9..f9d44900450 100644 --- a/token-metadata/js/package.json +++ b/token-metadata/js/package.json @@ -33,9 +33,8 @@ "deploy": "npm run deploy:docs", "deploy:docs": "npm run docs && gh-pages --dest token-metadata/js --dist docs --dotfiles", "docs": "shx rm -rf docs && typedoc && shx cp .nojekyll docs/", - "fmt": "prettier --write '{*,**/*}.{ts,tsx,js,jsx,json}'", - "lint": "prettier --check '{*,**/*}.{ts,tsx,js,jsx,json}' && eslint --max-warnings 0 .", - "lint:fix": "npm run fmt && eslint --fix .", + "lint": "eslint --max-warnings 0 .", + "lint:fix": "eslint --fix .", "nuke": "shx rm -rf node_modules package-lock.json || true", "postbuild": "shx echo '{ \"type\": \"commonjs\" }' > lib/cjs/package.json", "reinstall": "npm run nuke && npm install", @@ -44,31 +43,28 @@ "watch": "tsc --build --verbose --watch tsconfig.all.json" }, "peerDependencies": { - "@solana/web3.js": "^1.95.1" + "@solana/web3.js": "^1.95.3" }, "dependencies": { - "@solana/codecs": "2.0.0-preview.4", + "@solana/codecs": "2.0.0-rc.1", "@solana/spl-type-length-value": "0.1.0" }, "devDependencies": { - "@solana/web3.js": "^1.95.1", - "@types/chai": "^4.3.16", + "@solana/web3.js": "^1.95.3", + "@types/chai": "^4.3.17", "@types/mocha": "^10.0.7", - "@types/node": "^22.0.0", - "@typescript-eslint/eslint-plugin": "^7.17.0", - "@typescript-eslint/parser": "^7.17.0", + "@types/node": "^22.4.2", + "@typescript-eslint/eslint-plugin": "^8.2.0", + "@typescript-eslint/parser": "^8.2.0", "chai": "^5.1.1", "eslint": "^8.57.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-require-extensions": "^0.1.1", "gh-pages": "^6.1.1", - "mocha": "^10.7.0", - "prettier": "^3.3.3", + "mocha": "^10.7.3", "shx": "^0.3.4", "ts-node": "^10.9.2", "tslib": "^2.6.3", - "typedoc": "^0.26.5", + "typedoc": "^0.26.6", "typescript": "^5.5.4" } } diff --git a/token-metadata/js/src/instruction.ts b/token-metadata/js/src/instruction.ts index b428d4fef8e..2afdb1121e0 100644 --- a/token-metadata/js/src/instruction.ts +++ b/token-metadata/js/src/instruction.ts @@ -71,8 +71,8 @@ export function createInitializeInstruction(args: InitializeInstructionArgs): Tr ['name', getStringEncoder()], ['symbol', getStringEncoder()], ['uri', getStringEncoder()], - ]) - ).encode({ name, symbol, uri }) + ]), + ).encode({ name, symbol, uri }), ), }); } @@ -103,8 +103,8 @@ export function createUpdateFieldInstruction(args: UpdateFieldInstruction): Tran getStructEncoder([ ['field', getDataEnumCodec(getFieldCodec())], ['value', getStringEncoder()], - ]) - ).encode({ field: getFieldConfig(field), value }) + ]), + ).encode({ field: getFieldConfig(field), value }), ), }); } @@ -131,8 +131,8 @@ export function createRemoveKeyInstruction(args: RemoveKeyInstructionArgs) { getStructEncoder([ ['idempotent', getBooleanEncoder()], ['key', getStringEncoder()], - ]) - ).encode({ idempotent, key }) + ]), + ).encode({ idempotent, key }), ), }); } @@ -156,8 +156,8 @@ export function createUpdateAuthorityInstruction(args: UpdateAuthorityInstructio data: Buffer.from( getInstructionEncoder( splDiscriminate('spl_token_metadata_interface:update_the_authority'), - getStructEncoder([['newAuthority', getPublicKeyEncoder()]]) - ).encode({ newAuthority: newAuthority ?? SystemProgram.programId }) + getStructEncoder([['newAuthority', getPublicKeyEncoder()]]), + ).encode({ newAuthority: newAuthority ?? SystemProgram.programId }), ), }); } @@ -180,8 +180,8 @@ export function createEmitInstruction(args: EmitInstructionArgs): TransactionIns getStructEncoder([ ['start', getOptionEncoder(getU64Encoder())], ['end', getOptionEncoder(getU64Encoder())], - ]) - ).encode({ start: start ?? null, end: end ?? null }) + ]), + ).encode({ start: start ?? null, end: end ?? null }), ), }); } diff --git a/token-metadata/js/test/instruction.test.ts b/token-metadata/js/test/instruction.test.ts index e206393c7e1..a9dded10b30 100644 --- a/token-metadata/js/test/instruction.test.ts +++ b/token-metadata/js/test/instruction.test.ts @@ -30,7 +30,7 @@ function checkPackUnpack( instruction: TransactionInstruction, discriminator: Uint8Array, decoder: Decoder, - values: T + values: T, ) { expect(instruction.data.subarray(0, 8)).to.deep.equal(discriminator); const unpacked = decoder.decode(instruction.data.subarray(8)); @@ -69,7 +69,7 @@ describe('Token Metadata Instructions', () => { ['symbol', getStringDecoder()], ['uri', getStringDecoder()], ]), - { name, symbol, uri } + { name, symbol, uri }, ); }); @@ -89,7 +89,7 @@ describe('Token Metadata Instructions', () => { ['key', getDataEnumCodec(getFieldCodec())], ['value', getStringDecoder()], ]), - { key: getFieldConfig(field), value } + { key: getFieldConfig(field), value }, ); }); @@ -109,7 +109,7 @@ describe('Token Metadata Instructions', () => { ['key', getDataEnumCodec(getFieldCodec())], ['value', getStringDecoder()], ]), - { key: getFieldConfig(field), value } + { key: getFieldConfig(field), value }, ); }); @@ -127,7 +127,7 @@ describe('Token Metadata Instructions', () => { ['idempotent', getBooleanDecoder()], ['key', getStringDecoder()], ]), - { idempotent: true, key: 'MyTestField' } + { idempotent: true, key: 'MyTestField' }, ); }); @@ -142,7 +142,7 @@ describe('Token Metadata Instructions', () => { }), splDiscriminate('spl_token_metadata_interface:update_the_authority'), getStructDecoder([['newAuthority', fixDecoderSize(getBytesDecoder(), 32)]]), - { newAuthority: Uint8Array.from(newAuthority.toBuffer()) } + { newAuthority: Uint8Array.from(newAuthority.toBuffer()) }, ); }); @@ -161,7 +161,7 @@ describe('Token Metadata Instructions', () => { ['start', getOptionDecoder(getU64Decoder())], ['end', getOptionDecoder(getU64Decoder())], ]), - { start, end } + { start, end }, ); }); }); diff --git a/token-swap/js/package.json b/token-swap/js/package.json index 27666f268f4..bcb9f2daf08 100644 --- a/token-swap/js/package.json +++ b/token-swap/js/package.json @@ -37,10 +37,8 @@ "build:program": "cargo build-sbf --manifest-path=../program/Cargo.toml", "test:js": "mocha test", "test": "start-server-and-test 'solana-test-validator --bpf-program SwapsVeCiPHMUAtzQWZw7RjsKjgCjhwU55QGu4U1Szw ../../target/deploy/spl_token_swap.so --reset --quiet' http://127.0.0.1:8899/health test:js", - "lint": "npm run pretty && eslint --max-warnings 0 .", - "lint:fix": "npm run pretty:fix && eslint . --fix", - "pretty": "prettier --check '{*,**/*}.{ts,tsx,js,jsx,json}'", - "pretty:fix": "prettier --write '{*,**/*}.{ts,tsx,js,jsx,json}'" + "lint": "eslint --max-warnings 0 .", + "lint:fix": "eslint . --fix" }, "keywords": [], "dependencies": { @@ -48,24 +46,21 @@ "@solana/buffer-layout-utils": "^0.2.0" }, "peerDependencies": { - "@solana/web3.js": "^1.95.1" + "@solana/web3.js": "^1.95.3" }, "devDependencies": { "@solana/spl-token": "0.4.6", - "@solana/web3.js": "^1.95.1", + "@solana/web3.js": "^1.95.3", "@types/bn.js": "^5.1.0", "@types/chai-as-promised": "^7.1.4", - "@types/chai": "^4.3.16", + "@types/chai": "^4.3.17", "@types/mocha": "^10.0.7", - "@typescript-eslint/eslint-plugin": "^7.17.0", - "@typescript-eslint/parser": "^7.17.0", + "@typescript-eslint/eslint-plugin": "^8.2.0", + "@typescript-eslint/parser": "^8.2.0", "eslint": "^8.57.0", "eslint-plugin-import": "^2.29.1", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-require-extensions": "^0.1.1", - "mocha": "^10.7.0", - "prettier": "^3.3.3", + "mocha": "^10.7.3", "start-server-and-test": "^2.0.5", "ts-node": "^10.9.2", "typescript": "^5.5.4" diff --git a/token-upgrade/program/Cargo.toml b/token-upgrade/program/Cargo.toml index 2ad05f11c72..ca6cc72c03f 100644 --- a/token-upgrade/program/Cargo.toml +++ b/token-upgrade/program/Cargo.toml @@ -14,7 +14,7 @@ test-sbf = [] [dependencies] num-derive = "0.4" num-traits = "0.2" -num_enum = "0.7.2" +num_enum = "0.7.3" solana-program = "2.0.3" spl-token-2022 = { version = "4.0.0", path = "../../token/program-2022", features = ["no-entrypoint"] } thiserror = "1.0" diff --git a/token-wrap/program/Cargo.toml b/token-wrap/program/Cargo.toml index 66057b8c0d0..ef4d8d76790 100644 --- a/token-wrap/program/Cargo.toml +++ b/token-wrap/program/Cargo.toml @@ -12,7 +12,7 @@ no-entrypoint = [] test-sbf = [] [dependencies] -bytemuck = { version = "1.16.1", features = ["derive"] } +bytemuck = { version = "1.17.0", features = ["derive"] } num_enum = "0.7" solana-program = "2.0.3" spl-associated-token-account = { version = "4.0.0", path = "../../associated-token-account/program", features = ["no-entrypoint"] } diff --git a/token/cli/Cargo.toml b/token/cli/Cargo.toml index ba4c3d58456..79a6be1b06e 100644 --- a/token/cli/Cargo.toml +++ b/token/cli/Cargo.toml @@ -6,7 +6,7 @@ homepage = "https://spl.solana.com/token" license = "Apache-2.0" name = "spl-token-cli" repository = "https://github.com/solana-labs/solana-program-library" -version = "4.0.0" +version = "4.0.1" [build-dependencies] walkdir = "2" @@ -16,9 +16,9 @@ base64 = "0.22.1" clap = "2.33.3" console = "0.15.8" futures = "0.3" -serde = "1.0.204" +serde = "1.0.208" serde_derive = "1.0.103" -serde_json = "1.0.121" +serde_json = "1.0.125" solana-account-decoder = "2.0.3" solana-clap-utils = "2.0.3" solana-cli-config = "2.0.3" @@ -35,6 +35,7 @@ spl-token-2022 = { version = "4.0.0", path = "../program-2022", features = [ "no-entrypoint", ] } spl-token-client = { version = "0.11.0", path = "../client" } +spl-token-confidential-transfer-proof-generation = { version = "0.1.0", path = "../confidential-transfer/proof-generation" } spl-token-metadata-interface = { version = "0.4.0", path = "../../token-metadata/interface" } spl-token-group-interface = { version = "0.3.0", path = "../../token-group/interface" } spl-associated-token-account = { version = "4.0.0", path = "../../associated-token-account/program", features = [ @@ -49,10 +50,10 @@ tokio = "1.39" [dev-dependencies] solana-test-validator = "2.0.3" -assert_cmd = "2.0.15" +assert_cmd = "2.0.16" libtest-mimic = "0.7" serial_test = "3.1.1" -tempfile = "3.10.1" +tempfile = "3.12.0" [[bin]] name = "spl-token" diff --git a/token/cli/README.md b/token/cli/README.md index 1d706b5609a..3583effaca4 100644 --- a/token/cli/README.md +++ b/token/cli/README.md @@ -33,3 +33,10 @@ After that, you can run the tests as any other Rust project: ```sh cargo test ``` + +To run it locally you can do it like this: + +```sh +cargo build --manifest-path token/cli/Cargo.toml +target/debug/spl-token +``` diff --git a/token/cli/src/clap_app.rs b/token/cli/src/clap_app.rs index 94d67ef87c7..74c5f4bf186 100644 --- a/token/cli/src/clap_app.rs +++ b/token/cli/src/clap_app.rs @@ -591,6 +591,14 @@ pub fn app<'a, 'b>( .possible_values(&["json", "json-compact"]) .help("Return information in specified output format"), ) + .arg( + Arg::with_name("program_2022") + .long("program-2022") + .takes_value(false) + .global(true) + .conflicts_with("program_id") + .help("Use token extension program token 2022 with program id: TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"), + ) .arg( Arg::with_name("program_id") .short("p") @@ -599,6 +607,7 @@ pub fn app<'a, 'b>( .takes_value(true) .global(true) .validator(is_valid_token_program_id) + .conflicts_with("program_2022") .help("SPL Token program id"), ) .arg( @@ -763,11 +772,42 @@ pub fn app<'a, 'b>( .value_names(&["FEE_IN_BASIS_POINTS", "MAXIMUM_FEE"]) .takes_value(true) .number_of_values(2) + .hidden(true) + .conflicts_with("transfer_fee_basis_points") + .conflicts_with("transfer_fee_maximum_fee") .help( "Add a transfer fee to the mint. \ The mint authority can set the fee and withdraw collected fees.", ), ) + .arg( + Arg::with_name("transfer_fee_basis_points") + .long("transfer-fee-basis-points") + .value_names(&["FEE_IN_BASIS_POINTS"]) + .takes_value(true) + .number_of_values(1) + .conflicts_with("transfer_fee") + .requires("transfer_fee_maximum_fee") + .validator(is_parsable::) + .help( + "Add transfer fee to the mint. \ + The mint authority can set the fee.", + ), + ) + .arg( + Arg::with_name("transfer_fee_maximum_fee") + .long("transfer-fee-maximum-fee") + .value_names(&["MAXIMUM_FEE"]) + .takes_value(true) + .number_of_values(1) + .conflicts_with("transfer_fee") + .requires("transfer_fee_basis_points") + .validator(is_amount) + .help( + "Add a UI amount maximum transfer fee to the mint. \ + The mint authority can set and collect fees" + ) + ) .arg( Arg::with_name("enable_permanent_delegate") .long("enable-permanent-delegate") @@ -2231,6 +2271,13 @@ pub fn app<'a, 'b>( ) .arg(owner_address_arg()) .arg(multisig_signer_arg()) + .group( + ArgGroup::with_name("source_or_mint") + .arg("source") + .arg("include_mint") + .multiple(true) + .required(true) + ) ) .subcommand( SubCommand::with_name(CommandName::SetTransferFee.into()) diff --git a/token/cli/src/command.rs b/token/cli/src/command.rs index 82abe3910f7..567b5792853 100644 --- a/token/cli/src/command.rs +++ b/token/cli/src/command.rs @@ -41,7 +41,6 @@ use { account_info::{ ApplyPendingBalanceAccountInfo, TransferAccountInfo, WithdrawAccountInfo, }, - instruction::TransferSplitContextStateAccounts, ConfidentialTransferAccount, ConfidentialTransferMint, }, confidential_transfer_fee::ConfidentialTransferFeeConfig, @@ -58,18 +57,19 @@ use { transfer_hook::TransferHook, BaseStateWithExtensions, ExtensionType, StateWithExtensionsOwned, }, - solana_zk_token_sdk::{ - encryption::{ - auth_encryption::AeKey, - elgamal::{self, ElGamalKeypair}, - }, - zk_token_elgamal::pod::ElGamalPubkey, + solana_zk_sdk::encryption::{ + auth_encryption::AeKey, + elgamal::{self, ElGamalKeypair}, + pod::elgamal::PodElGamalPubkey, }, state::{Account, AccountState, Mint}, }, spl_token_client::{ client::{ProgramRpcClientSendTransaction, RpcClientResponse}, - token::{ComputeUnitLimit, ExtensionInitializationParams, Token}, + token::{ComputeUnitLimit, ExtensionInitializationParams, ProofAccount, Token}, + }, + spl_token_confidential_transfer_proof_generation::{ + transfer::TransferProofData, withdraw::WithdrawProofData, }, spl_token_group_interface::state::TokenGroup, spl_token_metadata_interface::state::{Field, TokenMetadata}, @@ -194,6 +194,14 @@ fn native_token_client_from_config( config.fee_payer()?.clone(), ); + let token = token.with_compute_unit_limit(config.compute_unit_limit.clone()); + + let token = if let Some(compute_unit_price) = config.compute_unit_price { + token.with_compute_unit_price(compute_unit_price) + } else { + token + }; + if let (Some(nonce_account), Some(nonce_authority), Some(nonce_blockhash)) = ( config.nonce_account, &config.nonce_authority, @@ -640,7 +648,7 @@ async fn command_initialize_group( token_pubkey: Pubkey, mint_authority: Pubkey, update_authority: Pubkey, - max_size: u32, + max_size: u64, bulk_signers: Vec>, ) -> CommandResult { let token = token_client_from_config(config, &token_pubkey, None)?; @@ -671,7 +679,7 @@ async fn command_update_group_max_size( config: &Config<'_>, token_pubkey: Pubkey, update_authority: Pubkey, - new_max_size: u32, + new_max_size: u64, bulk_signers: Vec>, ) -> CommandResult { let token = token_client_from_config(config, &token_pubkey, None)?; @@ -1471,7 +1479,7 @@ async fn command_transfer( let auditor_elgamal_pubkey = if let Ok(confidential_transfer_mint) = mint_state.get_extension::() { - let expected_auditor_elgamal_pubkey = Option::::from( + let expected_auditor_elgamal_pubkey = Option::::from( confidential_transfer_mint.auditor_elgamal_pubkey, ); @@ -1566,6 +1574,7 @@ async fn command_transfer( }); let context_state_authority = config.fee_payer()?; + let context_state_authority_pubkey = context_state_authority.pubkey(); let equality_proof_context_state_account = Keypair::new(); let equality_proof_pubkey = equality_proof_context_state_account.pubkey(); let ciphertext_validity_proof_context_state_account = Keypair::new(); @@ -1574,27 +1583,17 @@ async fn command_transfer( let range_proof_context_state_account = Keypair::new(); let range_proof_pubkey = range_proof_context_state_account.pubkey(); - let transfer_context_state_accounts = TransferSplitContextStateAccounts { - equality_proof: &equality_proof_pubkey, - ciphertext_validity_proof: &ciphertext_validity_proof_pubkey, - range_proof: &range_proof_pubkey, - authority: &context_state_authority.pubkey(), - no_op_on_uninitialized_split_context_state: false, - close_split_context_state_accounts: None, - }; - let state = token.get_account_info(&sender).await.unwrap(); let extension = state .get_extension::() .unwrap(); let transfer_account_info = TransferAccountInfo::new(extension); - let ( + let TransferProofData { equality_proof_data, ciphertext_validity_proof_data, range_proof_data, - source_decrypt_handles, - ) = transfer_account_info + } = transfer_account_info .generate_split_transfer_proof_data( transfer_balance, &args.sender_elgamal_keypair, @@ -1605,60 +1604,81 @@ async fn command_transfer( .unwrap(); // setup proofs + let create_range_proof_context_signer = &[&range_proof_context_state_account]; + let create_equality_proof_context_signer = &[&equality_proof_context_state_account]; + let create_ciphertext_validity_proof_context_signer = + &[&ciphertext_validity_proof_context_state_account]; + let _ = try_join!( - token.create_range_proof_context_state_for_transfer( - transfer_context_state_accounts, + token.confidential_transfer_create_context_state_account( + &range_proof_pubkey, + &context_state_authority_pubkey, &range_proof_data, - &range_proof_context_state_account, + true, + create_range_proof_context_signer ), - token.create_equality_proof_context_state_for_transfer( - transfer_context_state_accounts, + token.confidential_transfer_create_context_state_account( + &equality_proof_pubkey, + &context_state_authority_pubkey, &equality_proof_data, - &equality_proof_context_state_account, + false, + create_equality_proof_context_signer ), - token.create_ciphertext_validity_proof_context_state_for_transfer( - transfer_context_state_accounts, + token.confidential_transfer_create_context_state_account( + &ciphertext_validity_proof_pubkey, + &context_state_authority_pubkey, &ciphertext_validity_proof_data, - &ciphertext_validity_proof_context_state_account, + false, + create_ciphertext_validity_proof_context_signer ) )?; // do the transfer + let equality_proof_context_proof_account = + ProofAccount::ContextAccount(equality_proof_pubkey); + let ciphertext_validity_proof_context_proof_account = + ProofAccount::ContextAccount(ciphertext_validity_proof_pubkey); + let range_proof_context_proof_account = + ProofAccount::ContextAccount(range_proof_pubkey); + let transfer_result = token - .confidential_transfer_transfer_with_split_proofs( + .confidential_transfer_transfer( &sender, &recipient_token_account, &sender_owner, - transfer_context_state_accounts, + Some(&equality_proof_context_proof_account), + Some(&ciphertext_validity_proof_context_proof_account), + Some(&range_proof_context_proof_account), transfer_balance, Some(transfer_account_info), + &args.sender_elgamal_keypair, &args.sender_aes_key, - &source_decrypt_handles, + &recipient_elgamal_pubkey, + auditor_elgamal_pubkey.as_ref(), &bulk_signers, ) .await?; // close context state accounts - let context_state_authority_pubkey = context_state_authority.pubkey(); - let close_context_state_signers = &[context_state_authority]; + let close_context_state_signer = &[&context_state_authority]; let _ = try_join!( - token.confidential_transfer_close_context_state( + token.confidential_transfer_close_context_state_account( &equality_proof_pubkey, &sender, &context_state_authority_pubkey, - close_context_state_signers, + close_context_state_signer ), - token.confidential_transfer_close_context_state( + token.confidential_transfer_close_context_state_account( &ciphertext_validity_proof_pubkey, &sender, &context_state_authority_pubkey, - close_context_state_signers, + close_context_state_signer ), - token.confidential_transfer_close_context_state( + token.confidential_transfer_close_context_state_account( &range_proof_pubkey, &sender, &context_state_authority_pubkey, - close_context_state_signers, + close_context_state_signer ), )?; @@ -2936,7 +2956,7 @@ async fn command_update_confidential_transfer_settings( let new_auditor_pubkey = if let Some(auditor_pubkey) = auditor_pubkey { auditor_pubkey.into() } else { - Option::::from(confidential_transfer_mint.auditor_elgamal_pubkey) + Option::::from(confidential_transfer_mint.auditor_elgamal_pubkey) }; (new_auto_approve, new_auditor_pubkey) @@ -3327,28 +3347,49 @@ async fn command_deposit_withdraw_confidential_tokens( let withdraw_account_info = WithdrawAccountInfo::new(extension_state); let context_state_authority = config.fee_payer()?; - let context_state_keypair = Keypair::new(); - let context_state_pubkey = context_state_keypair.pubkey(); + let equality_proof_context_state_keypair = Keypair::new(); + let equality_proof_context_state_pubkey = equality_proof_context_state_keypair.pubkey(); + let range_proof_context_state_keypair = Keypair::new(); + let range_proof_context_state_pubkey = range_proof_context_state_keypair.pubkey(); - let withdraw_proof_data = - withdraw_account_info.generate_proof_data(amount, elgamal_keypair, aes_key)?; + let WithdrawProofData { + equality_proof_data, + range_proof_data, + } = withdraw_account_info.generate_proof_data(amount, elgamal_keypair, aes_key)?; - // setup proof - token - .create_withdraw_proof_context_state( - &context_state_pubkey, - &context_state_authority.pubkey(), - &withdraw_proof_data, - &context_state_keypair, + // set up context state accounts + let context_state_authority_pubkey = context_state_authority.pubkey(); + let create_equality_proof_signer = &[&equality_proof_context_state_keypair]; + let create_range_proof_signer = &[&range_proof_context_state_keypair]; + + let _ = try_join!( + token.confidential_transfer_create_context_state_account( + &equality_proof_context_state_pubkey, + &context_state_authority_pubkey, + &equality_proof_data, + false, + create_equality_proof_signer + ), + token.confidential_transfer_create_context_state_account( + &range_proof_context_state_pubkey, + &context_state_authority_pubkey, + &range_proof_data, + true, + create_range_proof_signer, ) - .await?; + )?; // do the withdrawal - token + let withdraw_result = token .confidential_transfer_withdraw( &token_account_address, &owner, - Some(&context_state_pubkey), + Some(&ProofAccount::ContextAccount( + equality_proof_context_state_pubkey, + )), + Some(&ProofAccount::ContextAccount( + range_proof_context_state_pubkey, + )), amount, decimals, Some(withdraw_account_info), @@ -3359,16 +3400,23 @@ async fn command_deposit_withdraw_confidential_tokens( .await?; // close context state account - let context_state_authority_pubkey = context_state_authority.pubkey(); - let close_context_state_signers = &[context_state_authority]; - token - .confidential_transfer_close_context_state( - &context_state_pubkey, + let close_context_state_signer = &[&context_state_authority]; + let _ = try_join!( + token.confidential_transfer_close_context_state_account( + &equality_proof_context_state_pubkey, + &token_account_address, + &context_state_authority_pubkey, + close_context_state_signer + ), + token.confidential_transfer_close_context_state_account( + &range_proof_context_state_pubkey, &token_account_address, &context_state_authority_pubkey, - close_context_state_signers, + close_context_state_signer ) - .await? + )?; + + withdraw_result } }; @@ -3440,8 +3488,8 @@ async fn command_apply_pending_balance( struct ConfidentialTransferArgs { sender_elgamal_keypair: ElGamalKeypair, sender_aes_key: AeKey, - recipient_elgamal_pubkey: Option, - auditor_elgamal_pubkey: Option, + recipient_elgamal_pubkey: Option, + auditor_elgamal_pubkey: Option, } pub async fn process_command<'a>( @@ -3472,6 +3520,7 @@ pub async fn process_command<'a>( let member_address = value_t!(arg_matches, "member_address", Pubkey).ok(); let transfer_fee = arg_matches.values_of("transfer_fee").map(|mut v| { + println_display(config,"transfer-fee has been deprecated and will be removed in a future release. Please specify --transfer-fee-basis-points and --transfer-fee-maximum-fee with a UI amount".to_string()); ( v.next() .unwrap() @@ -3484,6 +3533,13 @@ pub async fn process_command<'a>( ) }); + let tranfer_fee_basis_point = value_of::(arg_matches, "transfer_fee_basis_points"); + let transfer_fee_maximum_fee = value_of::(arg_matches, "transfer_fee_maximum_fee") + .map(|v| spl_token::ui_amount_to_amount(v, decimals)); + let transfer_fee = tranfer_fee_basis_point + .map(|v| (v, transfer_fee_maximum_fee.unwrap())) + .or(transfer_fee); + let (token_signer, token) = get_signer(arg_matches, "token_keypair", &mut wallet_manager) .unwrap_or_else(new_throwaway_signer); @@ -3622,7 +3678,7 @@ pub async fn process_command<'a>( let token_pubkey = pubkey_of_signer(arg_matches, "token", &mut wallet_manager) .unwrap() .unwrap(); - let max_size = value_t_or_exit!(arg_matches, "max_size", u32); + let max_size = value_t_or_exit!(arg_matches, "max_size", u64); let (mint_authority_signer, mint_authority) = config.signer_or_default(arg_matches, "mint_authority", &mut wallet_manager); let update_authority = @@ -3643,7 +3699,7 @@ pub async fn process_command<'a>( let token_pubkey = pubkey_of_signer(arg_matches, "token", &mut wallet_manager) .unwrap() .unwrap(); - let new_max_size = value_t_or_exit!(arg_matches, "new_max_size", u32); + let new_max_size = value_t_or_exit!(arg_matches, "new_max_size", u64); let (update_authority_signer, update_authority) = config.signer_or_default(arg_matches, "update_authority", &mut wallet_manager); let bulk_signers = vec![update_authority_signer]; diff --git a/token/cli/src/config.rs b/token/cli/src/config.rs index 89137a51b31..c1aa6c53c08 100644 --- a/token/cli/src/config.rs +++ b/token/cli/src/config.rs @@ -260,28 +260,29 @@ impl<'a> Config<'a> { let dump_transaction_message = matches.is_present(DUMP_TRANSACTION_MESSAGE.name); let default_program_id = spl_token::id(); - let (program_id, restrict_to_program_id) = - if let Some(program_id) = value_of(matches, "program_id") { - (program_id, true) - } else if !sign_only { - if let Some(address) = value_of(matches, "token") - .or_else(|| value_of(matches, "account")) - .or_else(|| value_of(matches, "address")) - { - ( - rpc_client - .get_account(&address) - .await - .map(|account| account.owner) - .unwrap_or(default_program_id), - false, - ) - } else { - (default_program_id, false) - } + let (program_id, restrict_to_program_id) = if matches.is_present("program_2022") { + (spl_token_2022::id(), true) + } else if let Some(program_id) = value_of(matches, "program_id") { + (program_id, true) + } else if !sign_only { + if let Some(address) = value_of(matches, "token") + .or_else(|| value_of(matches, "account")) + .or_else(|| value_of(matches, "address")) + { + ( + rpc_client + .get_account(&address) + .await + .map(|account| account.owner) + .unwrap_or(default_program_id), + false, + ) } else { (default_program_id, false) - }; + } + } else { + (default_program_id, false) + }; // need to specify a compute limit if compute price and blockhash are specified if matches.is_present(BLOCKHASH_ARG.name) diff --git a/token/cli/src/encryption_keypair.rs b/token/cli/src/encryption_keypair.rs index f2b2d733237..3a5aecbcf76 100644 --- a/token/cli/src/encryption_keypair.rs +++ b/token/cli/src/encryption_keypair.rs @@ -5,9 +5,9 @@ use { base64::{prelude::BASE64_STANDARD, Engine}, clap::ArgMatches, - spl_token_2022::solana_zk_token_sdk::{ - encryption::elgamal::{ElGamalKeypair, ElGamalPubkey}, - zk_token_elgamal::pod::ElGamalPubkey as PodElGamalPubkey, + spl_token_2022::solana_zk_sdk::encryption::{ + elgamal::{ElGamalKeypair, ElGamalPubkey}, + pod::elgamal::PodElGamalPubkey, }, }; diff --git a/token/cli/tests/command.rs b/token/cli/tests/command.rs index 1b66cfaa0e7..02add5913c2 100644 --- a/token/cli/tests/command.rs +++ b/token/cli/tests/command.rs @@ -32,7 +32,7 @@ use { BaseStateWithExtensions, StateWithExtensionsOwned, }, instruction::create_native_mint, - solana_zk_token_sdk::zk_token_elgamal::pod::ElGamalPubkey, + solana_zk_sdk::encryption::pod::elgamal::PodElGamalPubkey, state::{Account, AccountState, Mint, Multisig}, }, spl_token_cli::{ @@ -92,6 +92,7 @@ async fn main() { // maybe come up with a way to do this through a some macro tag on the function? let tests = vec![ async_trial!(create_token_default, test_validator, payer), + async_trial!(create_token_2022, test_validator, payer), async_trial!(create_token_interest_bearing, test_validator, payer), async_trial!(set_interest_rate, test_validator, payer), async_trial!(supply, test_validator, payer), @@ -123,6 +124,7 @@ async fn main() { async_trial!(non_transferable, test_validator, payer), async_trial!(default_account_state, test_validator, payer), async_trial!(transfer_fee, test_validator, payer), + async_trial!(transfer_fee_basis_point, test_validator, payer), async_trial!(confidential_transfer, test_validator, payer), async_trial!(multisig_transfer, test_validator, payer), async_trial!(offline_multisig_transfer_with_nonce, test_validator, payer), @@ -495,6 +497,43 @@ async fn create_token_default(test_validator: &TestValidator, payer: &Keypair) { } } +async fn create_token_2022(test_validator: &TestValidator, payer: &Keypair) { + let config = test_config_with_default_signer(test_validator, payer, &spl_token_2022::id()); + let mut wallet_manager = None; + let mut bulk_signers: Vec> = Vec::new(); + let mut multisigner_ids = Vec::new(); + + let args = &[ + "spl-token", + CommandName::CreateToken.into(), + "--program-2022", + ]; + + let default_decimals = format!("{}", spl_token_2022::native_mint::DECIMALS); + let minimum_signers_help = minimum_signers_help_string(); + let multisig_member_help = multisig_member_help_string(); + + let app_matches = app( + &default_decimals, + &minimum_signers_help, + &multisig_member_help, + ) + .get_matches_from(args); + + let config = Config::new_with_clients_and_ws_url( + &app_matches, + &mut wallet_manager, + &mut bulk_signers, + &mut multisigner_ids, + config.rpc_client.clone(), + config.program_client.clone(), + config.websocket_url.clone(), + ) + .await; + + assert_eq!(config.program_id, spl_token_2022::ID); +} + async fn create_token_interest_bearing(test_validator: &TestValidator, payer: &Keypair) { let config = test_config_with_default_signer(test_validator, payer, &spl_token_2022::id()); let rate_bps: i16 = 100; @@ -2521,8 +2560,56 @@ async fn transfer_fee(test_validator: &TestValidator, payer: &Keypair) { ); } +async fn transfer_fee_basis_point(test_validator: &TestValidator, payer: &Keypair) { + let config = test_config_with_default_signer(test_validator, payer, &spl_token_2022::id()); + + let transfer_fee_basis_points = 100; + let maximum_fee = 1.2; + let decimal = 9; + + let token = Keypair::new(); + let token_keypair_file = NamedTempFile::new().unwrap(); + write_keypair_file(&token, &token_keypair_file).unwrap(); + let token_pubkey = token.pubkey(); + process_test_command( + &config, + payer, + &[ + "spl-token", + CommandName::CreateToken.into(), + token_keypair_file.path().to_str().unwrap(), + "--transfer-fee-basis-points", + &transfer_fee_basis_points.to_string(), + "--transfer-fee-maximum-fee", + &maximum_fee.to_string(), + ], + ) + .await + .unwrap(); + + let account = config.rpc_client.get_account(&token_pubkey).await.unwrap(); + let test_mint = StateWithExtensionsOwned::::unpack(account.data).unwrap(); + let extension = test_mint.get_extension::().unwrap(); + assert_eq!( + u16::from(extension.older_transfer_fee.transfer_fee_basis_points), + transfer_fee_basis_points + ); + assert_eq!( + u64::from(extension.older_transfer_fee.maximum_fee), + (maximum_fee * i32::pow(10, decimal) as f64) as u64 + ); + assert_eq!( + u16::from(extension.newer_transfer_fee.transfer_fee_basis_points), + transfer_fee_basis_points + ); + assert_eq!( + u64::from(extension.newer_transfer_fee.maximum_fee), + (maximum_fee * i32::pow(10, decimal) as f64) as u64 + ); +} + async fn confidential_transfer(test_validator: &TestValidator, payer: &Keypair) { - use spl_token_2022::solana_zk_token_sdk::encryption::elgamal::ElGamalKeypair; + use spl_token_2022::solana_zk_sdk::encryption::elgamal::ElGamalKeypair; let config = test_config_with_default_signer(test_validator, payer, &spl_token_2022::id()); @@ -2563,13 +2650,13 @@ async fn confidential_transfer(test_validator: &TestValidator, payer: &Keypair) auto_approve, ); assert_eq!( - Option::::from(extension.auditor_elgamal_pubkey), + Option::::from(extension.auditor_elgamal_pubkey), None, ); // update confidential transfer mint settings let auditor_keypair = ElGamalKeypair::new_rand(); - let auditor_pubkey: ElGamalPubkey = (*auditor_keypair.pubkey()).into(); + let auditor_pubkey: PodElGamalPubkey = (*auditor_keypair.pubkey()).into(); let new_auto_approve = true; process_test_command( @@ -2599,7 +2686,7 @@ async fn confidential_transfer(test_validator: &TestValidator, payer: &Keypair) new_auto_approve, ); assert_eq!( - Option::::from(extension.auditor_elgamal_pubkey), + Option::::from(extension.auditor_elgamal_pubkey), Some(auditor_pubkey), ); @@ -2859,7 +2946,7 @@ async fn confidential_transfer_with_fee(test_validator: &TestValidator, payer: & auto_approve, ); assert_eq!( - Option::::from(extension.auditor_elgamal_pubkey), + Option::::from(extension.auditor_elgamal_pubkey), None, ); @@ -4064,7 +4151,7 @@ async fn group(test_validator: &TestValidator, payer: &Keypair) { let account = config.rpc_client.get_account(&mint).await.unwrap(); let group_mint_state = StateWithExtensionsOwned::::unpack(account.data).unwrap(); let extension = group_mint_state.get_extension::().unwrap(); - assert_eq!(u32::from(extension.size), 1); + assert_eq!(u64::from(extension.size), 1); let account = config.rpc_client.get_account(&member_mint).await.unwrap(); let member_mint_state = StateWithExtensionsOwned::::unpack(account.data).unwrap(); @@ -4073,7 +4160,7 @@ async fn group(test_validator: &TestValidator, payer: &Keypair) { .unwrap(); assert_eq!(extension.group, mint); assert_eq!(extension.mint, member_mint); - assert_eq!(u32::from(extension.member_number), 1); + assert_eq!(u64::from(extension.member_number), 1); // update authority process_test_command( diff --git a/token/client/Cargo.toml b/token/client/Cargo.toml index 762b2f3e4cb..882a9340f00 100644 --- a/token/client/Cargo.toml +++ b/token/client/Cargo.toml @@ -9,7 +9,8 @@ version = "0.11.0" [dependencies] async-trait = "0.1" -curve25519-dalek = "3.2.1" +bincode = "1.3.2" +bytemuck = "1.17.0" futures = "0.3.30" futures-util = "0.3" solana-banks-interface = "2.0.3" @@ -26,9 +27,11 @@ spl-associated-token-account = { version = "4.0.0", path = "../../associated-tok spl-memo = { version = "5.0", path = "../../memo/program", features = [ "no-entrypoint", ] } +spl-record = { version = "0.2.0", path = "../../record/program", features = ["no-entrypoint"] } spl-token = { version = "6.0", path = "../program", features = [ "no-entrypoint", ] } +spl-token-confidential-transfer-proof-generation = { version = "0.1.0", path = "../confidential-transfer/proof-generation" } spl-token-2022 = { version = "4.0.0", path = "../program-2022" } spl-token-group-interface = { version = "0.3.0", path = "../../token-group/interface" } spl-token-metadata-interface = { version = "0.4.0", path = "../../token-metadata/interface" } diff --git a/token/client/src/lib.rs b/token/client/src/lib.rs index b0d017870bc..e2ea2307cb4 100644 --- a/token/client/src/lib.rs +++ b/token/client/src/lib.rs @@ -3,11 +3,4 @@ pub mod client; pub mod output; pub mod token; -/// Helper functions to generate split zero-knowledge proofs for confidential -/// transfers. -/// -/// The logic in this submodule should belong to the `solana-zk-token-sdk` and -/// will be removed with an upgrade to the Solana program in the future. -pub mod proof_generation; - pub use spl_token_2022; diff --git a/token/client/src/proof_generation.rs b/token/client/src/proof_generation.rs deleted file mode 100644 index 3c41d5094eb..00000000000 --- a/token/client/src/proof_generation.rs +++ /dev/null @@ -1,385 +0,0 @@ -//! Helper functions to generate split zero-knowledge proofs for confidential -//! transfers in the Confidential Transfer Extension. -//! -//! The logic in this submodule should belong to the `solana-zk-token-sdk` and -//! will be removed with an upgrade to the Solana program. - -use { - curve25519_dalek::scalar::Scalar, - spl_token_2022::{ - error::TokenError, - extension::confidential_transfer::{ - ciphertext_extraction::{transfer_amount_source_ciphertext, SourceDecryptHandles}, - processor::verify_and_split_deposit_amount, - }, - solana_zk_token_sdk::{ - encryption::{ - auth_encryption::{AeCiphertext, AeKey}, - elgamal::{DecryptHandle, ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey}, - grouped_elgamal::GroupedElGamal, - pedersen::{Pedersen, PedersenCommitment, PedersenOpening}, - }, - instruction::{ - transfer::{ - try_combine_lo_hi_commitments, try_combine_lo_hi_openings, FeeEncryption, - FeeParameters, TransferAmountCiphertext, - }, - BatchedGroupedCiphertext2HandlesValidityProofData, BatchedRangeProofU256Data, - CiphertextCommitmentEqualityProofData, FeeSigmaProofData, - }, - zk_token_elgamal::ops::subtract_with_lo_hi, - }, - }, -}; - -/// The main logic to create the five split proof data for a transfer with fee. -#[allow(clippy::too_many_arguments)] -pub fn transfer_with_fee_split_proof_data( - current_available_balance: &ElGamalCiphertext, - current_decryptable_available_balance: &AeCiphertext, - transfer_amount: u64, - source_elgamal_keypair: &ElGamalKeypair, - aes_key: &AeKey, - destination_elgamal_pubkey: &ElGamalPubkey, - auditor_elgamal_pubkey: Option<&ElGamalPubkey>, - withdraw_withheld_authority_elgamal_pubkey: &ElGamalPubkey, - transfer_fee_parameters: &FeeParameters, -) -> Result< - ( - CiphertextCommitmentEqualityProofData, - BatchedGroupedCiphertext2HandlesValidityProofData, - FeeSigmaProofData, - BatchedGroupedCiphertext2HandlesValidityProofData, - BatchedRangeProofU256Data, - SourceDecryptHandles, - ), - TokenError, -> { - let default_auditor_pubkey = ElGamalPubkey::default(); - let auditor_elgamal_pubkey = auditor_elgamal_pubkey.unwrap_or(&default_auditor_pubkey); - - // Split the transfer amount into the low and high bit components. - let (transfer_amount_lo, transfer_amount_hi) = - verify_and_split_deposit_amount(transfer_amount)?; - - // Encrypt the `lo` and `hi` transfer amounts. - let (transfer_amount_grouped_ciphertext_lo, transfer_amount_opening_lo) = - TransferAmountCiphertext::new( - transfer_amount_lo, - source_elgamal_keypair.pubkey(), - destination_elgamal_pubkey, - auditor_elgamal_pubkey, - ); - - let (transfer_amount_grouped_ciphertext_hi, transfer_amount_opening_hi) = - TransferAmountCiphertext::new( - transfer_amount_hi, - source_elgamal_keypair.pubkey(), - destination_elgamal_pubkey, - auditor_elgamal_pubkey, - ); - - // Decrypt the current available balance at the source - let current_decrypted_available_balance = current_decryptable_available_balance - .decrypt(aes_key) - .ok_or(TokenError::AccountDecryption)?; - - // Compute the remaining balance at the source - let new_decrypted_available_balance = current_decrypted_available_balance - .checked_sub(transfer_amount) - .ok_or(TokenError::InsufficientFunds)?; - - // Create a new Pedersen commitment for the remaining balance at the source - let (new_available_balance_commitment, new_source_opening) = - Pedersen::new(new_decrypted_available_balance); - - // Compute the remaining balance at the source as ElGamal ciphertexts - let transfer_amount_source_ciphertext_lo = - transfer_amount_source_ciphertext(&transfer_amount_grouped_ciphertext_lo.into()); - let transfer_amount_source_ciphertext_hi = - transfer_amount_source_ciphertext(&transfer_amount_grouped_ciphertext_hi.into()); - - let current_available_balance = (*current_available_balance).into(); - let new_available_balance_ciphertext = subtract_with_lo_hi( - ¤t_available_balance, - &transfer_amount_source_ciphertext_lo, - &transfer_amount_source_ciphertext_hi, - ) - .ok_or(TokenError::CiphertextArithmeticFailed)?; - let new_available_balance_ciphertext: ElGamalCiphertext = new_available_balance_ciphertext - .try_into() - .map_err(|_| TokenError::MalformedCiphertext)?; - - // generate equality proof data - let equality_proof_data = CiphertextCommitmentEqualityProofData::new( - source_elgamal_keypair, - &new_available_balance_ciphertext, - &new_available_balance_commitment, - &new_source_opening, - new_decrypted_available_balance, - ) - .map_err(|_| TokenError::ProofGeneration)?; - - // create source decrypt handle - let source_decrypt_handle_lo = - DecryptHandle::new(source_elgamal_keypair.pubkey(), &transfer_amount_opening_lo); - let source_decrypt_handle_hi = - DecryptHandle::new(source_elgamal_keypair.pubkey(), &transfer_amount_opening_hi); - - let source_decrypt_handles = SourceDecryptHandles { - lo: source_decrypt_handle_lo.into(), - hi: source_decrypt_handle_hi.into(), - }; - - // encrypt the transfer amount under the destination and auditor ElGamal public - // key - let transfer_amount_destination_auditor_ciphertext_lo = GroupedElGamal::encrypt_with( - [destination_elgamal_pubkey, auditor_elgamal_pubkey], - transfer_amount_lo, - &transfer_amount_opening_lo, - ); - let transfer_amount_destination_auditor_ciphertext_hi = GroupedElGamal::encrypt_with( - [destination_elgamal_pubkey, auditor_elgamal_pubkey], - transfer_amount_hi, - &transfer_amount_opening_hi, - ); - - // generate transfer amount ciphertext validity data - let transfer_amount_ciphertext_validity_proof_data = - BatchedGroupedCiphertext2HandlesValidityProofData::new( - destination_elgamal_pubkey, - auditor_elgamal_pubkey, - &transfer_amount_destination_auditor_ciphertext_lo, - &transfer_amount_destination_auditor_ciphertext_hi, - transfer_amount_lo, - transfer_amount_hi, - &transfer_amount_opening_lo, - &transfer_amount_opening_hi, - ) - .map_err(|_| TokenError::ProofGeneration)?; - - // calculate fee - let transfer_fee_basis_points = transfer_fee_parameters.fee_rate_basis_points; - let transfer_fee_maximum_fee = transfer_fee_parameters.maximum_fee; - let (raw_fee_amount, delta_fee) = - calculate_raw_fee_and_delta(transfer_amount, transfer_fee_basis_points) - .ok_or(TokenError::Overflow)?; - - // if raw fee is greater than the maximum fee, then use the maximum fee for the - // fee amount - let fee_amount = std::cmp::min(transfer_fee_maximum_fee, raw_fee_amount); - - // split and encrypt fee - let (fee_amount_lo, fee_amount_hi) = verify_and_split_deposit_amount(fee_amount)?; - let (fee_ciphertext_lo, fee_opening_lo) = FeeEncryption::new( - fee_amount_lo, - destination_elgamal_pubkey, - withdraw_withheld_authority_elgamal_pubkey, - ); - let (fee_ciphertext_hi, fee_opening_hi) = FeeEncryption::new( - fee_amount_hi, - destination_elgamal_pubkey, - withdraw_withheld_authority_elgamal_pubkey, - ); - - // create combined commitments and openings to be used to generate proofs - const TRANSFER_AMOUNT_LO_BIT_LENGTH: usize = 16; - let combined_transfer_amount_commitment = try_combine_lo_hi_commitments( - transfer_amount_grouped_ciphertext_lo.get_commitment(), - transfer_amount_grouped_ciphertext_hi.get_commitment(), - TRANSFER_AMOUNT_LO_BIT_LENGTH, - ) - .map_err(|_| TokenError::ProofGeneration)?; - let combined_transfer_amount_opening = try_combine_lo_hi_openings( - &transfer_amount_opening_lo, - &transfer_amount_opening_hi, - TRANSFER_AMOUNT_LO_BIT_LENGTH, - ) - .map_err(|_| TokenError::ProofGeneration)?; - - const FEE_AMOUNT_LO_BIT_LENGTH: usize = 16; - let combined_fee_commitment = try_combine_lo_hi_commitments( - fee_ciphertext_lo.get_commitment(), - fee_ciphertext_hi.get_commitment(), - FEE_AMOUNT_LO_BIT_LENGTH, - ) - .map_err(|_| TokenError::ProofGeneration)?; - let combined_fee_opening = - try_combine_lo_hi_openings(&fee_opening_lo, &fee_opening_hi, FEE_AMOUNT_LO_BIT_LENGTH) - .map_err(|_| TokenError::ProofGeneration)?; - - // compute claimed and real delta commitment - let (claimed_commitment, claimed_opening) = Pedersen::new(delta_fee); - let (delta_commitment, delta_opening) = compute_delta_commitment_and_opening( - ( - &combined_transfer_amount_commitment, - &combined_transfer_amount_opening, - ), - (&combined_fee_commitment, &combined_fee_opening), - transfer_fee_basis_points, - ); - - // generate fee sigma proof - let fee_sigma_proof_data = FeeSigmaProofData::new( - &combined_fee_commitment, - &delta_commitment, - &claimed_commitment, - &combined_fee_opening, - &delta_opening, - &claimed_opening, - fee_amount, - delta_fee, - transfer_fee_maximum_fee, - ) - .map_err(|_| TokenError::ProofGeneration)?; - - // encrypt the fee amount under the destination and withdraw withheld authority - // ElGamal public key - let fee_destination_withdraw_withheld_authority_ciphertext_lo = GroupedElGamal::encrypt_with( - [ - destination_elgamal_pubkey, - withdraw_withheld_authority_elgamal_pubkey, - ], - fee_amount_lo, - &fee_opening_lo, - ); - let fee_destination_withdraw_withheld_authority_ciphertext_hi = GroupedElGamal::encrypt_with( - [ - destination_elgamal_pubkey, - withdraw_withheld_authority_elgamal_pubkey, - ], - fee_amount_hi, - &fee_opening_hi, - ); - - // generate fee ciphertext validity data - let fee_ciphertext_validity_proof_data = - BatchedGroupedCiphertext2HandlesValidityProofData::new( - destination_elgamal_pubkey, - withdraw_withheld_authority_elgamal_pubkey, - &fee_destination_withdraw_withheld_authority_ciphertext_lo, - &fee_destination_withdraw_withheld_authority_ciphertext_hi, - fee_amount_lo, - fee_amount_hi, - &fee_opening_lo, - &fee_opening_hi, - ) - .map_err(|_| TokenError::ProofGeneration)?; - - // generate range proof data - const REMAINING_BALANCE_BIT_LENGTH: usize = 64; - const TRANSFER_AMOUNT_HI_BIT_LENGTH: usize = 32; - const DELTA_BIT_LENGTH: usize = 48; - const FEE_AMOUNT_HI_BIT_LENGTH: usize = 32; - const MAX_FEE_BASIS_POINTS: u64 = 10_000; - - let delta_fee_complement = MAX_FEE_BASIS_POINTS - delta_fee; - - let max_fee_basis_points_commitment = - Pedersen::with(MAX_FEE_BASIS_POINTS, &PedersenOpening::default()); - let claimed_complement_commitment = max_fee_basis_points_commitment - claimed_commitment; - let claimed_complement_opening = PedersenOpening::default() - &claimed_opening; - - let range_proof_data = BatchedRangeProofU256Data::new( - vec![ - &new_available_balance_commitment, - transfer_amount_grouped_ciphertext_lo.get_commitment(), - transfer_amount_grouped_ciphertext_hi.get_commitment(), - &claimed_commitment, - &claimed_complement_commitment, - fee_ciphertext_lo.get_commitment(), - fee_ciphertext_hi.get_commitment(), - ], - vec![ - new_decrypted_available_balance, - transfer_amount_lo, - transfer_amount_hi, - delta_fee, - delta_fee_complement, - fee_amount_lo, - fee_amount_hi, - ], - vec![ - REMAINING_BALANCE_BIT_LENGTH, - TRANSFER_AMOUNT_LO_BIT_LENGTH, - TRANSFER_AMOUNT_HI_BIT_LENGTH, - DELTA_BIT_LENGTH, - DELTA_BIT_LENGTH, - FEE_AMOUNT_LO_BIT_LENGTH, - FEE_AMOUNT_HI_BIT_LENGTH, - ], - vec![ - &new_source_opening, - &transfer_amount_opening_lo, - &transfer_amount_opening_hi, - &claimed_opening, - &claimed_complement_opening, - &fee_opening_lo, - &fee_opening_hi, - ], - ) - .map_err(|_| TokenError::ProofGeneration)?; - - Ok(( - equality_proof_data, - transfer_amount_ciphertext_validity_proof_data, - fee_sigma_proof_data, - fee_ciphertext_validity_proof_data, - range_proof_data, - source_decrypt_handles, - )) -} - -/// Calculate transfer fee and the "delta" value. The function returns the raw -/// fee, which could be greater than the maximum fee amount of a fee parameter. -/// -/// The "delta" value is a number that captures the round-off value when the fee -/// is computed. The fee is computed according to the formula `fee = -/// transfer_amount * fee_rate_basis_points / 10_000`. If no rounding occurred, -/// then we must have `fee * 10_000 - transfer_amount * fee_rate_basis_points = -/// 0`. If there is rounding involved (`10_000` does not divide cleanly), -/// then the difference `fee * 10_000 - transfer_amount * fee_rate_basis_points` -/// can be a non-zero number between `0` and `9_999` inclusively. We call this -/// number the "delta" value. -fn calculate_raw_fee_and_delta( - transfer_amount: u64, - fee_rate_basis_points: u16, -) -> Option<(u64, u64)> { - const ONE_IN_BASIS_POINTS: u128 = 10_000_u128; - - // compute `transfer_amount * fee_rate_basis_points` - let numerator = (transfer_amount as u128).checked_mul(fee_rate_basis_points as u128)?; - - // compute fee as `transfer_amount * fee_rate_basis_points / 10_000 ` - let fee = numerator - .checked_add(ONE_IN_BASIS_POINTS)? - .checked_sub(1)? - .checked_div(ONE_IN_BASIS_POINTS)?; - - // compute the delta fee as `fee * 10_000 - fee_rate_basis_points` - let delta_fee = fee - .checked_mul(ONE_IN_BASIS_POINTS)? - .checked_sub(numerator)?; - - Some((fee as u64, delta_fee as u64)) -} - -/// Calculate the "delta" commitment-opening pair from a transfer amount and fee -/// commitment-opening pairs. -fn compute_delta_commitment_and_opening( - (transfer_amount_commitment, transfer_amount_opening): (&PedersenCommitment, &PedersenOpening), - (fee_commitment, fee_opening): (&PedersenCommitment, &PedersenOpening), - fee_rate_basis_points: u16, -) -> (PedersenCommitment, PedersenOpening) { - const ONE_IN_BASIS_POINTS: u128 = 10_000_u128; - - let one_in_basis_points_scalar = Scalar::from(ONE_IN_BASIS_POINTS); - let fee_rate_scalar = Scalar::from(fee_rate_basis_points); - - let delta_commitment = - fee_commitment * one_in_basis_points_scalar - transfer_amount_commitment * fee_rate_scalar; - let delta_opening = - fee_opening * one_in_basis_points_scalar - transfer_amount_opening * fee_rate_scalar; - - (delta_commitment, delta_opening) -} diff --git a/token/client/src/token.rs b/token/client/src/token.rs index a9fefc21f70..d23bf5b8ded 100644 --- a/token/client/src/token.rs +++ b/token/client/src/token.rs @@ -1,12 +1,9 @@ use { - crate::{ - client::{ - ProgramClient, ProgramClientError, SendTransaction, SimulateTransaction, - SimulationResult, - }, - proof_generation::transfer_with_fee_split_proof_data, + crate::client::{ + ProgramClient, ProgramClientError, SendTransaction, SimulateTransaction, SimulationResult, }, - futures::{future::join_all, try_join}, + bytemuck::{bytes_of, Pod}, + futures::future::join_all, futures_util::TryFutureExt, solana_program_test::tokio::time, solana_sdk::{ @@ -15,9 +12,11 @@ use { hash::Hash, instruction::{AccountMeta, Instruction}, message::Message, + packet::PACKET_DATA_SIZE, program_error::ProgramError, program_pack::Pack, pubkey::Pubkey, + signature::Signature, signer::{signers::Signers, Signer, SignerError}, system_instruction, transaction::Transaction, @@ -28,6 +27,7 @@ use { create_associated_token_account, create_associated_token_account_idempotent, }, }, + spl_record::state::RecordData, spl_token_2022::{ extension::{ confidential_transfer::{ @@ -36,10 +36,6 @@ use { ApplyPendingBalanceAccountInfo, EmptyAccountAccountInfo, TransferAccountInfo, WithdrawAccountInfo, }, - ciphertext_extraction::SourceDecryptHandles, - instruction::{ - TransferSplitContextStateAccounts, TransferWithFeeSplitContextStateAccounts, - }, ConfidentialTransferAccount, DecryptableBalance, }, confidential_transfer_fee::{ @@ -51,20 +47,26 @@ use { BaseStateWithExtensions, Extension, ExtensionType, StateWithExtensionsOwned, }, instruction, offchain, - proof::ProofLocation, - solana_zk_token_sdk::{ + proof::{zk_proof_type_to_instruction, ProofData, ProofLocation}, + solana_zk_sdk::{ encryption::{ auth_encryption::AeKey, elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey, ElGamalSecretKey}, + pod::elgamal::PodElGamalPubkey, + }, + zk_elgamal_proof_program::{ + self, + instruction::{close_context_state, ContextStateInfo}, + proof_data::*, + state::ProofContextState, }, - instruction::*, - zk_token_elgamal::pod::ElGamalPubkey as PodElGamalPubkey, - zk_token_proof_instruction::{self, ContextStateInfo, ProofInstruction}, - zk_token_proof_program, - zk_token_proof_state::ProofContextState, }, state::{Account, AccountState, Mint, Multisig}, }, + spl_token_confidential_transfer_proof_generation::{ + transfer::TransferProofData, transfer_with_fee::TransferWithFeeProofData, + withdraw::WithdrawProofData, + }, spl_token_group_interface::state::{TokenGroup, TokenGroupMember}, spl_token_metadata_interface::state::{Field, TokenMetadata}, std::{ @@ -339,6 +341,11 @@ pub enum ComputeUnitLimit { Static(u32), } +pub enum ProofAccount { + ContextAccount(Pubkey), + RecordAccount(Pubkey, u32), +} + pub struct Token { client: Arc>, pubkey: Pubkey, /* token mint */ @@ -1892,7 +1899,7 @@ where &self, account: &Pubkey, authority: &Pubkey, - context_state_account: Option<&Pubkey>, + proof_account: Option<&ProofAccount>, maximum_pending_balance_credit_counter: Option, elgamal_keypair: &ElGamalKeypair, aes_key: &AeKey, @@ -1906,21 +1913,23 @@ where let maximum_pending_balance_credit_counter = maximum_pending_balance_credit_counter .unwrap_or(DEFAULT_MAXIMUM_PENDING_BALANCE_CREDIT_COUNTER); - let proof_data = if context_state_account.is_some() { + let proof_data = if proof_account.is_some() { None } else { Some( - confidential_transfer::instruction::PubkeyValidityData::new(elgamal_keypair) + confidential_transfer::instruction::PubkeyValidityProofData::new(elgamal_keypair) .map_err(|_| TokenError::ProofGeneration)?, ) }; - let proof_location = if let Some(proof_data_temp) = proof_data.as_ref() { - ProofLocation::InstructionOffset(1.try_into().unwrap(), proof_data_temp) - } else { - let context_state_account = context_state_account.unwrap(); - ProofLocation::ContextStateAccount(context_state_account) - }; + // cannot panic as long as either `proof_data` or `proof_account` is `Some(..)`, + // which is guaranteed by the previous check + let proof_location = Self::confidential_transfer_create_proof_location( + proof_data.as_ref(), + proof_account, + 1, + ) + .unwrap(); let decryptable_balance = aes_key.encrypt(0); @@ -1929,7 +1938,7 @@ where &self.program_id, account, &self.pubkey, - decryptable_balance, + decryptable_balance.into(), maximum_pending_balance_credit_counter, authority, &multisig_signers, @@ -1969,7 +1978,7 @@ where &self, account: &Pubkey, authority: &Pubkey, - context_state_account: Option<&Pubkey>, + proof_account: Option<&ProofAccount>, account_info: Option, elgamal_keypair: &ElGamalKeypair, signing_keypairs: &S, @@ -1986,7 +1995,7 @@ where EmptyAccountAccountInfo::new(confidential_transfer_account) }; - let proof_data = if context_state_account.is_some() { + let proof_data = if proof_account.is_some() { None } else { Some( @@ -1996,12 +2005,14 @@ where ) }; - let proof_location = if let Some(proof_data_temp) = proof_data.as_ref() { - ProofLocation::InstructionOffset(1.try_into().unwrap(), proof_data_temp) - } else { - let context_state_account = context_state_account.unwrap(); - ProofLocation::ContextStateAccount(context_state_account) - }; + // cannot panic as long as either `proof_data` or `proof_account` is `Some(..)`, + // which is guaranteed by the previous check + let proof_location = Self::confidential_transfer_create_proof_location( + proof_data.as_ref(), + proof_account, + 1, + ) + .unwrap(); self.process_ixs( &confidential_transfer::instruction::empty_account( @@ -2051,7 +2062,8 @@ where &self, account: &Pubkey, authority: &Pubkey, - context_state_account: Option<&Pubkey>, + equality_proof_account: Option<&ProofAccount>, + range_proof_account: Option<&ProofAccount>, withdraw_amount: u64, decimals: u8, account_info: Option, @@ -2071,22 +2083,42 @@ where WithdrawAccountInfo::new(confidential_transfer_account) }; - let proof_data = if context_state_account.is_some() { - None - } else { - Some( - account_info + let (equality_proof_data, range_proof_data) = + if equality_proof_account.is_some() && range_proof_account.is_some() { + (None, None) + } else { + let WithdrawProofData { + equality_proof_data, + range_proof_data, + } = account_info .generate_proof_data(withdraw_amount, elgamal_keypair, aes_key) - .map_err(|_| TokenError::ProofGeneration)?, - ) - }; + .map_err(|_| TokenError::ProofGeneration)?; - let proof_location = if let Some(proof_data_temp) = proof_data.as_ref() { - ProofLocation::InstructionOffset(1.try_into().unwrap(), proof_data_temp) - } else { - let context_state_account = context_state_account.unwrap(); - ProofLocation::ContextStateAccount(context_state_account) - }; + // if proof accounts are none, then proof data must be included as instruction + // data + let equality_proof_data = equality_proof_account + .is_none() + .then_some(equality_proof_data); + let range_proof_data = range_proof_account.is_none().then_some(range_proof_data); + + (equality_proof_data, range_proof_data) + }; + + // cannot panic as long as either `proof_data` or `proof_account` is `Some(..)`, + // which is guaranteed by the previous check + let equality_proof_location = Self::confidential_transfer_create_proof_location( + equality_proof_data.as_ref(), + equality_proof_account, + 1, + ) + .unwrap(); + + let range_proof_location = Self::confidential_transfer_create_proof_location( + range_proof_data.as_ref(), + range_proof_account, + 2, + ) + .unwrap(); let new_decryptable_available_balance = account_info .new_decryptable_available_balance(withdraw_amount, aes_key) @@ -2099,73 +2131,17 @@ where &self.pubkey, withdraw_amount, decimals, - new_decryptable_available_balance, + new_decryptable_available_balance.into(), authority, &multisig_signers, - proof_location, + equality_proof_location, + range_proof_location, )?, signing_keypairs, ) .await } - /// Create withdraw proof context state account for a confidential transfer - /// withdraw instruction. - pub async fn create_withdraw_proof_context_state( - &self, - context_state_account: &Pubkey, - context_state_authority: &Pubkey, - withdraw_proof_data: &WithdrawData, - withdraw_proof_signer: &S, - ) -> TokenResult { - // create withdraw proof context state - let instruction_type = ProofInstruction::VerifyWithdraw; - let space = size_of::>(); - let rent = self - .client - .get_minimum_balance_for_rent_exemption(space) - .await - .map_err(TokenError::Client)?; - - let withdraw_proof_context_state_info = ContextStateInfo { - context_state_account, - context_state_authority, - }; - - self.process_ixs( - &[system_instruction::create_account( - &self.payer.pubkey(), - context_state_account, - rent, - space as u64, - &zk_token_proof_program::id(), - )], - &[withdraw_proof_signer], - ) - .await?; - - // This instruction is right at the transaction size limit, so we cannot - // add any other instructions to it - let blockhash = self - .client - .get_latest_blockhash() - .await - .map_err(TokenError::Client)?; - - let transaction = Transaction::new_signed_with_payer( - &[instruction_type - .encode_verify_proof(Some(withdraw_proof_context_state_info), withdraw_proof_data)], - Some(&self.payer.pubkey()), - &[self.payer.as_ref()], - blockhash, - ); - - self.client - .send_transaction(&transaction) - .await - .map_err(TokenError::Client) - } - /// Transfer tokens confidentially #[allow(clippy::too_many_arguments)] pub async fn confidential_transfer_transfer( @@ -2173,7 +2149,9 @@ where source_account: &Pubkey, destination_account: &Pubkey, source_authority: &Pubkey, - context_state_account: Option<&Pubkey>, + equality_proof_account: Option<&ProofAccount>, + ciphertext_validity_proof_account: Option<&ProofAccount>, + range_proof_account: Option<&ProofAccount>, transfer_amount: u64, account_info: Option, source_elgamal_keypair: &ElGamalKeypair, @@ -2194,28 +2172,67 @@ where TransferAccountInfo::new(confidential_transfer_account) }; - let proof_data = if context_state_account.is_some() { - None + let (equality_proof_data, ciphertext_validity_proof_data, range_proof_data) = if [ + equality_proof_account, + ciphertext_validity_proof_account, + range_proof_account, + ] + .iter() + .all(|proof_account| proof_account.is_some()) + { + (None, None, None) } else { - Some( - account_info - .generate_transfer_proof_data( - transfer_amount, - source_elgamal_keypair, - source_aes_key, - destination_elgamal_pubkey, - auditor_elgamal_pubkey, - ) - .map_err(|_| TokenError::ProofGeneration)?, + let TransferProofData { + equality_proof_data, + ciphertext_validity_proof_data, + range_proof_data, + } = account_info + .generate_split_transfer_proof_data( + transfer_amount, + source_elgamal_keypair, + source_aes_key, + destination_elgamal_pubkey, + auditor_elgamal_pubkey, + ) + .map_err(|_| TokenError::ProofGeneration)?; + + // if proof accounts are none, then proof data must be included as instruction + // data + let equality_proof_data = equality_proof_account + .is_none() + .then_some(equality_proof_data); + let ciphertext_validity_proof_data = ciphertext_validity_proof_account + .is_none() + .then_some(ciphertext_validity_proof_data); + let range_proof_data = range_proof_account.is_none().then_some(range_proof_data); + + ( + equality_proof_data, + ciphertext_validity_proof_data, + range_proof_data, ) }; - let proof_location = if let Some(proof_data_temp) = proof_data.as_ref() { - ProofLocation::InstructionOffset(1.try_into().unwrap(), proof_data_temp) - } else { - let context_state_account = context_state_account.unwrap(); - ProofLocation::ContextStateAccount(context_state_account) - }; + // cannot panic as long as either `proof_data` or `proof_account` is `Some(..)`, + // which is guaranteed by the previous check + let equality_proof_location = Self::confidential_transfer_create_proof_location( + equality_proof_data.as_ref(), + equality_proof_account, + 1, + ) + .unwrap(); + let ciphertext_validity_proof_location = Self::confidential_transfer_create_proof_location( + ciphertext_validity_proof_data.as_ref(), + ciphertext_validity_proof_account, + 2, + ) + .unwrap(); + let range_proof_location = Self::confidential_transfer_create_proof_location( + range_proof_data.as_ref(), + range_proof_account, + 3, + ) + .unwrap(); let new_decryptable_available_balance = account_info .new_decryptable_available_balance(transfer_amount, source_aes_key) @@ -2226,10 +2243,12 @@ where source_account, self.get_address(), destination_account, - new_decryptable_available_balance, + new_decryptable_available_balance.into(), source_authority, &multisig_signers, - proof_location, + equality_proof_location, + ciphertext_validity_proof_location, + range_proof_location, )?; offchain::add_extra_account_metas( &mut instructions[0], @@ -2249,23 +2268,234 @@ where self.process_ixs(&instructions, signing_keypairs).await } - /// Transfer tokens confidentially using split proofs. - /// - /// This function assumes that proof context states have already been - /// created. + /// Create a record account containing zero-knowledge proof needed for a + /// confidential transfer. + pub async fn confidential_transfer_create_record_account< + S1: Signer, + S2: Signer, + ZK: Pod + ZkProofData, + U: Pod, + >( + &self, + record_account: &Pubkey, + record_authority: &Pubkey, + proof_data: &ZK, + record_account_signer: &S1, + record_authority_signer: &S2, + ) -> TokenResult> { + let proof_data = bytes_of(proof_data); + let space = proof_data + .len() + .saturating_add(RecordData::WRITABLE_START_INDEX); + let rent = self + .client + .get_minimum_balance_for_rent_exemption(space) + .await + .map_err(TokenError::Client)?; + + // A closure that constructs a vector of instructions needed to create and write + // to record accounts. The closure is defined as a convenience function + // to be fed into the function `calculate_record_max_chunk_size`. + let create_record_instructions = |first_instruction: bool, bytes: &[u8], offset: u64| { + let mut ixs = vec![]; + if first_instruction { + ixs.push(system_instruction::create_account( + &self.payer.pubkey(), + record_account, + rent, + space as u64, + &spl_record::id(), + )); + ixs.push(spl_record::instruction::initialize( + record_account, + record_authority, + )); + } + ixs.push(spl_record::instruction::write( + record_account, + record_authority, + offset, + bytes, + )); + ixs + }; + let first_chunk_size = calculate_record_max_chunk_size(create_record_instructions, true); + let (first_chunk, rest) = if space <= first_chunk_size { + (proof_data, &[] as &[u8]) + } else { + proof_data.split_at(first_chunk_size) + }; + + let first_ixs = create_record_instructions(true, first_chunk, 0); + let first_ixs_signers: [&dyn Signer; 2] = [record_account_signer, record_authority_signer]; + self.process_ixs(&first_ixs, &first_ixs_signers).await?; + + let subsequent_chunk_size = + calculate_record_max_chunk_size(create_record_instructions, false); + let mut record_offset = first_chunk_size; + let mut ixs_batch = vec![]; + for chunk in rest.chunks(subsequent_chunk_size) { + ixs_batch.push(create_record_instructions( + false, + chunk, + record_offset as u64, + )); + record_offset = record_offset.saturating_add(chunk.len()); + } + + let futures = ixs_batch + .into_iter() + .map(|ixs| async move { self.process_ixs(&ixs, &[record_authority_signer]).await }) + .collect::>(); + + join_all(futures).await.into_iter().collect() + } + + /// Close a record account. + pub async fn confidential_transfer_close_record_account( + &self, + record_account: &Pubkey, + lamport_destination_account: &Pubkey, + record_account_authority: &Pubkey, + signing_keypairs: &S, + ) -> TokenResult { + self.process_ixs( + &[spl_record::instruction::close_account( + record_account, + record_account_authority, + lamport_destination_account, + )], + signing_keypairs, + ) + .await + } + + /// Create a context state account containing zero-knowledge proof needed + /// for a confidential transfer instruction. + pub async fn confidential_transfer_create_context_state_account< + S: Signers, + ZK: Pod + ZkProofData, + U: Pod, + >( + &self, + context_state_account: &Pubkey, + context_state_authority: &Pubkey, + proof_data: &ZK, + split_account_creation_and_proof_verification: bool, + signing_keypairs: &S, + ) -> TokenResult { + let instruction_type = zk_proof_type_to_instruction(ZK::PROOF_TYPE)?; + let space = size_of::>(); + let rent = self + .client + .get_minimum_balance_for_rent_exemption(space) + .await + .map_err(TokenError::Client)?; + + let context_state_info = ContextStateInfo { + context_state_account, + context_state_authority, + }; + + // Some proof instructions are right at the transaction size limit, but in the + // future it might be able to support the transfer too + if split_account_creation_and_proof_verification { + self.process_ixs( + &[system_instruction::create_account( + &self.payer.pubkey(), + context_state_account, + rent, + space as u64, + &zk_elgamal_proof_program::id(), + )], + signing_keypairs, + ) + .await?; + + let blockhash = self + .client + .get_latest_blockhash() + .await + .map_err(TokenError::Client)?; + + let transaction = Transaction::new_signed_with_payer( + &[instruction_type.encode_verify_proof(Some(context_state_info), proof_data)], + Some(&self.payer.pubkey()), + &[self.payer.as_ref()], + blockhash, + ); + + self.client + .send_transaction(&transaction) + .await + .map_err(TokenError::Client) + } else { + self.process_ixs( + &[ + system_instruction::create_account( + &self.payer.pubkey(), + context_state_account, + rent, + space as u64, + &zk_elgamal_proof_program::id(), + ), + instruction_type.encode_verify_proof(Some(context_state_info), proof_data), + ], + signing_keypairs, + ) + .await + } + } + + /// Close a ZK Token proof program context state + pub async fn confidential_transfer_close_context_state_account( + &self, + context_state_account: &Pubkey, + lamport_destination_account: &Pubkey, + context_state_authority: &Pubkey, + signing_keypairs: &S, + ) -> TokenResult { + let context_state_info = ContextStateInfo { + context_state_account, + context_state_authority, + }; + + self.process_ixs( + &[close_context_state( + context_state_info, + lamport_destination_account, + )], + signing_keypairs, + ) + .await + } + + /// Transfer tokens confidentially with fee #[allow(clippy::too_many_arguments)] - pub async fn confidential_transfer_transfer_with_split_proofs( + pub async fn confidential_transfer_transfer_with_fee( &self, source_account: &Pubkey, destination_account: &Pubkey, source_authority: &Pubkey, - context_state_accounts: TransferSplitContextStateAccounts<'_>, + equality_proof_account: Option<&ProofAccount>, + transfer_amount_ciphertext_validity_proof_account: Option<&ProofAccount>, + percentage_with_cap_proof_account: Option<&ProofAccount>, + fee_ciphertext_validity_proof_account: Option<&ProofAccount>, + range_proof_account: Option<&ProofAccount>, transfer_amount: u64, account_info: Option, + source_elgamal_keypair: &ElGamalKeypair, source_aes_key: &AeKey, - source_decrypt_handles: &SourceDecryptHandles, + destination_elgamal_pubkey: &ElGamalPubkey, + auditor_elgamal_pubkey: Option<&ElGamalPubkey>, + withdraw_withheld_authority_elgamal_pubkey: &ElGamalPubkey, + fee_rate_basis_points: u16, + maximum_fee: u64, signing_keypairs: &S, ) -> TokenResult { + let signing_pubkeys = signing_keypairs.pubkeys(); + let multisig_signers = self.get_multisig_signers(source_authority, &signing_pubkeys); + let account_info = if let Some(account_info) = account_info { account_info } else { @@ -2275,22 +2505,123 @@ where TransferAccountInfo::new(confidential_transfer_account) }; + let ( + equality_proof_data, + transfer_amount_ciphertext_validity_proof_data, + percentage_with_cap_proof_data, + fee_ciphertext_validity_proof_data, + range_proof_data, + ) = if [ + equality_proof_account, + transfer_amount_ciphertext_validity_proof_account, + percentage_with_cap_proof_account, + fee_ciphertext_validity_proof_account, + range_proof_account, + ] + .iter() + .all(|proof_account| proof_account.is_some()) + { + // is all proofs come from accounts, then skip proof generation + (None, None, None, None, None) + } else { + let TransferWithFeeProofData { + equality_proof_data, + transfer_amount_ciphertext_validity_proof_data, + percentage_with_cap_proof_data, + fee_ciphertext_validity_proof_data, + range_proof_data, + } = account_info + .generate_split_transfer_with_fee_proof_data( + transfer_amount, + source_elgamal_keypair, + source_aes_key, + destination_elgamal_pubkey, + auditor_elgamal_pubkey, + withdraw_withheld_authority_elgamal_pubkey, + fee_rate_basis_points, + maximum_fee, + ) + .map_err(|_| TokenError::ProofGeneration)?; + + let equality_proof_data = equality_proof_account + .is_none() + .then_some(equality_proof_data); + let transfer_amount_ciphertext_validity_proof_data = + transfer_amount_ciphertext_validity_proof_account + .is_none() + .then_some(transfer_amount_ciphertext_validity_proof_data); + let percentage_with_cap_proof_data = percentage_with_cap_proof_account + .is_none() + .then_some(percentage_with_cap_proof_data); + let fee_ciphertext_validity_proof_data = fee_ciphertext_validity_proof_account + .is_none() + .then_some(fee_ciphertext_validity_proof_data); + let range_proof_data = range_proof_account.is_none().then_some(range_proof_data); + + ( + equality_proof_data, + transfer_amount_ciphertext_validity_proof_data, + percentage_with_cap_proof_data, + fee_ciphertext_validity_proof_data, + range_proof_data, + ) + }; + + // cannot panic as long as either `proof_data` or `proof_account` is `Some(..)`, + // which is guaranteed by the previous check + let equality_proof_location = Self::confidential_transfer_create_proof_location( + equality_proof_data.as_ref(), + equality_proof_account, + 1, + ) + .unwrap(); + let transfer_amount_ciphertext_validity_proof_location = + Self::confidential_transfer_create_proof_location( + transfer_amount_ciphertext_validity_proof_data.as_ref(), + transfer_amount_ciphertext_validity_proof_account, + 2, + ) + .unwrap(); + let fee_sigma_proof_location = Self::confidential_transfer_create_proof_location( + percentage_with_cap_proof_data.as_ref(), + percentage_with_cap_proof_account, + 3, + ) + .unwrap(); + let fee_ciphertext_validity_proof_location = + Self::confidential_transfer_create_proof_location( + fee_ciphertext_validity_proof_data.as_ref(), + fee_ciphertext_validity_proof_account, + 4, + ) + .unwrap(); + let range_proof_location = Self::confidential_transfer_create_proof_location( + range_proof_data.as_ref(), + range_proof_account, + 5, + ) + .unwrap(); + let new_decryptable_available_balance = account_info .new_decryptable_available_balance(transfer_amount, source_aes_key) .map_err(|_| TokenError::AccountDecryption)?; - let mut instruction = confidential_transfer::instruction::transfer_with_split_proofs( + let mut instructions = confidential_transfer::instruction::transfer_with_fee( &self.program_id, source_account, self.get_address(), destination_account, new_decryptable_available_balance.into(), source_authority, - context_state_accounts, - source_decrypt_handles, + &multisig_signers, + equality_proof_location, + transfer_amount_ciphertext_validity_proof_location, + fee_sigma_proof_location, + fee_ciphertext_validity_proof_location, + range_proof_location, )?; offchain::add_extra_account_metas( - &mut instruction, + &mut instructions[0], source_account, self.get_address(), destination_account, @@ -2304,1081 +2635,43 @@ where ) .await .map_err(|_| TokenError::AccountNotFound)?; - self.process_ixs(&[instruction], signing_keypairs).await + self.process_ixs(&instructions, signing_keypairs).await } - /// Transfer tokens confidentially using split proofs in parallel - /// - /// This function internally generates the ZK Token proof instructions to - /// create the necessary proof context states. - #[allow(clippy::too_many_arguments)] - pub async fn confidential_transfer_transfer_with_split_proofs_in_parallel( + /// Applies the confidential transfer pending balance to the available + /// balance + pub async fn confidential_transfer_apply_pending_balance( &self, - source_account: &Pubkey, - destination_account: &Pubkey, - source_authority: &Pubkey, - context_state_accounts: TransferSplitContextStateAccounts<'_>, - transfer_amount: u64, - account_info: Option, - source_elgamal_keypair: &ElGamalKeypair, - source_aes_key: &AeKey, - destination_elgamal_pubkey: &ElGamalPubkey, - auditor_elgamal_pubkey: Option<&ElGamalPubkey>, - equality_and_ciphertext_validity_proof_signers: &S, - range_proof_signers: &S, - ) -> TokenResult<(T::Output, T::Output)> { + account: &Pubkey, + authority: &Pubkey, + account_info: Option, + elgamal_secret_key: &ElGamalSecretKey, + aes_key: &AeKey, + signing_keypairs: &S, + ) -> TokenResult { + let signing_pubkeys = signing_keypairs.pubkeys(); + let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys); + let account_info = if let Some(account_info) = account_info { account_info } else { - let account = self.get_account_info(source_account).await?; + let account = self.get_account_info(account).await?; let confidential_transfer_account = account.get_extension::()?; - TransferAccountInfo::new(confidential_transfer_account) + ApplyPendingBalanceAccountInfo::new(confidential_transfer_account) }; - let ( - equality_proof_data, - ciphertext_validity_proof_data, - range_proof_data, - source_decrypt_handles, - ) = account_info - .generate_split_transfer_proof_data( - transfer_amount, - source_elgamal_keypair, - source_aes_key, - destination_elgamal_pubkey, - auditor_elgamal_pubkey, - ) - .map_err(|_| TokenError::ProofGeneration)?; - + let expected_pending_balance_credit_counter = account_info.pending_balance_credit_counter(); let new_decryptable_available_balance = account_info - .new_decryptable_available_balance(transfer_amount, source_aes_key) + .new_decryptable_available_balance(elgamal_secret_key, aes_key) .map_err(|_| TokenError::AccountDecryption)?; - let mut transfer_instruction = - confidential_transfer::instruction::transfer_with_split_proofs( - &self.program_id, - source_account, - self.get_address(), - destination_account, - new_decryptable_available_balance.into(), - source_authority, - context_state_accounts, - &source_decrypt_handles, - )?; - offchain::add_extra_account_metas( - &mut transfer_instruction, - source_account, - self.get_address(), - destination_account, - source_authority, - u64::MAX, - |address| { - self.client - .get_account(address) - .map_ok(|opt| opt.map(|acc| acc.data)) - }, - ) - .await - .map_err(|_| TokenError::AccountNotFound)?; - - let transfer_with_equality_and_ciphertext_validity = self - .create_equality_and_ciphertext_validity_proof_context_states_for_transfer_parallel( - context_state_accounts, - &equality_proof_data, - &ciphertext_validity_proof_data, - &transfer_instruction, - equality_and_ciphertext_validity_proof_signers, - ); - - let transfer_with_range_proof = self - .create_range_proof_context_state_for_transfer_parallel( - context_state_accounts, - &range_proof_data, - &transfer_instruction, - range_proof_signers, - ); - - try_join!( - transfer_with_equality_and_ciphertext_validity, - transfer_with_range_proof - ) - } - - /// Create equality proof context state account for a confidential transfer. - #[allow(clippy::too_many_arguments)] - pub async fn create_equality_proof_context_state_for_transfer( - &self, - context_state_accounts: TransferSplitContextStateAccounts<'_>, - equality_proof_data: &CiphertextCommitmentEqualityProofData, - equality_proof_signer: &S, - ) -> TokenResult { - // create equality proof context state - let instruction_type = ProofInstruction::VerifyCiphertextCommitmentEquality; - let space = size_of::>(); - let rent = self - .client - .get_minimum_balance_for_rent_exemption(space) - .await - .map_err(TokenError::Client)?; - - let equality_proof_context_state_info = ContextStateInfo { - context_state_account: context_state_accounts.equality_proof, - context_state_authority: context_state_accounts.authority, - }; - - self.process_ixs( - &[ - system_instruction::create_account( - &self.payer.pubkey(), - context_state_accounts.equality_proof, - rent, - space as u64, - &zk_token_proof_program::id(), - ), - instruction_type.encode_verify_proof( - Some(equality_proof_context_state_info), - equality_proof_data, - ), - ], - &[equality_proof_signer], - ) - .await - } - - /// Create ciphertext validity proof context state account for a - /// confidential transfer. - pub async fn create_ciphertext_validity_proof_context_state_for_transfer( - &self, - context_state_accounts: TransferSplitContextStateAccounts<'_>, - ciphertext_validity_proof_data: &BatchedGroupedCiphertext2HandlesValidityProofData, - ciphertext_validity_proof_signer: &S, - ) -> TokenResult { - // create ciphertext validity proof context state - let instruction_type = ProofInstruction::VerifyBatchedGroupedCiphertext2HandlesValidity; - let space = - size_of::>(); - let rent = self - .client - .get_minimum_balance_for_rent_exemption(space) - .await - .map_err(TokenError::Client)?; - - let ciphertext_validity_proof_context_state_info = ContextStateInfo { - context_state_account: context_state_accounts.ciphertext_validity_proof, - context_state_authority: context_state_accounts.authority, - }; - - self.process_ixs( - &[ - system_instruction::create_account( - &self.payer.pubkey(), - context_state_accounts.ciphertext_validity_proof, - rent, - space as u64, - &zk_token_proof_program::id(), - ), - instruction_type.encode_verify_proof( - Some(ciphertext_validity_proof_context_state_info), - ciphertext_validity_proof_data, - ), - ], - &[ciphertext_validity_proof_signer], - ) - .await - } - - /// Create equality and ciphertext validity proof context state accounts for - /// a confidential transfer. - #[allow(clippy::too_many_arguments)] - pub async fn create_equality_and_ciphertext_validity_proof_context_states_for_transfer< - S: Signers, - >( - &self, - context_state_accounts: TransferSplitContextStateAccounts<'_>, - equality_proof_data: &CiphertextCommitmentEqualityProofData, - ciphertext_validity_proof_data: &BatchedGroupedCiphertext2HandlesValidityProofData, - signing_keypairs: &S, - ) -> TokenResult { - self.create_equality_and_ciphertext_validity_proof_context_state_with_optional_transfer( - context_state_accounts, - equality_proof_data, - ciphertext_validity_proof_data, - None, - signing_keypairs, - ) - .await - } - - /// Create equality and ciphertext validity proof context state accounts - /// with a confidential transfer instruction. - #[allow(clippy::too_many_arguments)] - pub async fn create_equality_and_ciphertext_validity_proof_context_states_for_transfer_parallel< - S: Signers, - >( - &self, - context_state_accounts: TransferSplitContextStateAccounts<'_>, - equality_proof_data: &CiphertextCommitmentEqualityProofData, - ciphertext_validity_proof_data: &BatchedGroupedCiphertext2HandlesValidityProofData, - transfer_instruction: &Instruction, - signing_keypairs: &S, - ) -> TokenResult { - self.create_equality_and_ciphertext_validity_proof_context_state_with_optional_transfer( - context_state_accounts, - equality_proof_data, - ciphertext_validity_proof_data, - Some(transfer_instruction), - signing_keypairs, - ) - .await - } - - /// Create equality and ciphertext validity proof context states for a - /// confidential transfer. - /// - /// If an optional transfer instruction is provided, then the transfer - /// instruction is attached to the same transaction. - #[allow(clippy::too_many_arguments)] - async fn create_equality_and_ciphertext_validity_proof_context_state_with_optional_transfer< - S: Signers, - >( - &self, - context_state_accounts: TransferSplitContextStateAccounts<'_>, - equality_proof_data: &CiphertextCommitmentEqualityProofData, - ciphertext_validity_proof_data: &BatchedGroupedCiphertext2HandlesValidityProofData, - transfer_instruction: Option<&Instruction>, - signing_keypairs: &S, - ) -> TokenResult { - let mut instructions = vec![]; - - // create equality proof context state - let instruction_type = ProofInstruction::VerifyCiphertextCommitmentEquality; - let space = size_of::>(); - let rent = self - .client - .get_minimum_balance_for_rent_exemption(space) - .await - .map_err(TokenError::Client)?; - instructions.push(system_instruction::create_account( - &self.payer.pubkey(), - context_state_accounts.equality_proof, - rent, - space as u64, - &zk_token_proof_program::id(), - )); - - let equality_proof_context_state_info = ContextStateInfo { - context_state_account: context_state_accounts.equality_proof, - context_state_authority: context_state_accounts.authority, - }; - instructions.push( - instruction_type - .encode_verify_proof(Some(equality_proof_context_state_info), equality_proof_data), - ); - - // create ciphertext validity proof context state - let instruction_type = ProofInstruction::VerifyBatchedGroupedCiphertext2HandlesValidity; - let space = - size_of::>(); - let rent = self - .client - .get_minimum_balance_for_rent_exemption(space) - .await - .map_err(TokenError::Client)?; - instructions.push(system_instruction::create_account( - &self.payer.pubkey(), - context_state_accounts.ciphertext_validity_proof, - rent, - space as u64, - &zk_token_proof_program::id(), - )); - - let ciphertext_validity_proof_context_state_info = ContextStateInfo { - context_state_account: context_state_accounts.ciphertext_validity_proof, - context_state_authority: context_state_accounts.authority, - }; - instructions.push(instruction_type.encode_verify_proof( - Some(ciphertext_validity_proof_context_state_info), - ciphertext_validity_proof_data, - )); - - // add transfer instruction - if let Some(transfer_instruction) = transfer_instruction { - instructions.push(transfer_instruction.clone()); - } - - self.process_ixs(&instructions, signing_keypairs).await - } - - /// Create a range proof context state account for a confidential transfer. - pub async fn create_range_proof_context_state_for_transfer( - &self, - context_state_accounts: TransferSplitContextStateAccounts<'_>, - range_proof_data: &BatchedRangeProofU128Data, - range_proof_keypair: &S, - ) -> TokenResult { - let instruction_type = ProofInstruction::VerifyBatchedRangeProofU128; - let space = size_of::>(); - let rent = self - .client - .get_minimum_balance_for_rent_exemption(space) - .await - .map_err(TokenError::Client)?; - let range_proof_context_state_info = ContextStateInfo { - context_state_account: context_state_accounts.range_proof, - context_state_authority: context_state_accounts.authority, - }; - self.process_ixs( - &[system_instruction::create_account( - &self.payer.pubkey(), - context_state_accounts.range_proof, - rent, - space as u64, - &zk_token_proof_program::id(), - )], - &[range_proof_keypair], - ) - .await?; - - // This instruction is right at the transaction size limit, but in the - // future it might be able to support the transfer too - let blockhash = self - .client - .get_latest_blockhash() - .await - .map_err(TokenError::Client)?; - - let transaction = Transaction::new_signed_with_payer( - &[instruction_type - .encode_verify_proof(Some(range_proof_context_state_info), range_proof_data)], - Some(&self.payer.pubkey()), - &[self.payer.as_ref()], - blockhash, - ); - - self.client - .send_transaction(&transaction) - .await - .map_err(TokenError::Client) - } - - /// Create a range proof context state account with a confidential transfer - /// instruction. - pub async fn create_range_proof_context_state_for_transfer_parallel( - &self, - context_state_accounts: TransferSplitContextStateAccounts<'_>, - range_proof_data: &BatchedRangeProofU128Data, - transfer_instruction: &Instruction, - signing_keypairs: &S, - ) -> TokenResult { - self.create_range_proof_context_state_with_optional_transfer( - context_state_accounts, - range_proof_data, - Some(transfer_instruction), - signing_keypairs, - ) - .await - } - - /// Create a range proof context state account and an optional confidential - /// transfer instruction. - async fn create_range_proof_context_state_with_optional_transfer( - &self, - context_state_accounts: TransferSplitContextStateAccounts<'_>, - range_proof_data: &BatchedRangeProofU128Data, - transfer_instruction: Option<&Instruction>, - signing_keypairs: &S, - ) -> TokenResult { - let instruction_type = ProofInstruction::VerifyBatchedRangeProofU128; - let space = size_of::>(); - let rent = self - .client - .get_minimum_balance_for_rent_exemption(space) - .await - .map_err(TokenError::Client)?; - let range_proof_context_state_info = ContextStateInfo { - context_state_account: context_state_accounts.range_proof, - context_state_authority: context_state_accounts.authority, - }; - - let mut instructions = vec![ - system_instruction::create_account( - &self.payer.pubkey(), - context_state_accounts.range_proof, - rent, - space as u64, - &zk_token_proof_program::id(), - ), - instruction_type - .encode_verify_proof(Some(range_proof_context_state_info), range_proof_data), - ]; - - if let Some(transfer_instruction) = transfer_instruction { - instructions.push(transfer_instruction.clone()); - } - - self.process_ixs(&instructions, signing_keypairs).await - } - - /// Close a ZK Token proof program context state - pub async fn confidential_transfer_close_context_state( - &self, - context_state_account: &Pubkey, - lamport_destination_account: &Pubkey, - context_state_authority: &Pubkey, - signing_keypairs: &S, - ) -> TokenResult { - let context_state_info = ContextStateInfo { - context_state_account, - context_state_authority, - }; - - self.process_ixs( - &[zk_token_proof_instruction::close_context_state( - context_state_info, - lamport_destination_account, - )], - signing_keypairs, - ) - .await - } - - /// Transfer tokens confidentially with fee - #[allow(clippy::too_many_arguments)] - pub async fn confidential_transfer_transfer_with_fee( - &self, - source_account: &Pubkey, - destination_account: &Pubkey, - source_authority: &Pubkey, - context_state_account: Option<&Pubkey>, - transfer_amount: u64, - account_info: Option, - source_elgamal_keypair: &ElGamalKeypair, - source_aes_key: &AeKey, - destination_elgamal_pubkey: &ElGamalPubkey, - auditor_elgamal_pubkey: Option<&ElGamalPubkey>, - withdraw_withheld_authority_elgamal_pubkey: &ElGamalPubkey, - fee_rate_basis_points: u16, - maximum_fee: u64, - signing_keypairs: &S, - ) -> TokenResult { - let signing_pubkeys = signing_keypairs.pubkeys(); - let multisig_signers = self.get_multisig_signers(source_authority, &signing_pubkeys); - - let account_info = if let Some(account_info) = account_info { - account_info - } else { - let account = self.get_account_info(source_account).await?; - let confidential_transfer_account = - account.get_extension::()?; - TransferAccountInfo::new(confidential_transfer_account) - }; - - let proof_data = if context_state_account.is_some() { - None - } else { - Some( - account_info - .generate_transfer_with_fee_proof_data( - transfer_amount, - source_elgamal_keypair, - source_aes_key, - destination_elgamal_pubkey, - auditor_elgamal_pubkey, - withdraw_withheld_authority_elgamal_pubkey, - fee_rate_basis_points, - maximum_fee, - ) - .map_err(|_| TokenError::ProofGeneration)?, - ) - }; - - let proof_location = if let Some(proof_data_temp) = proof_data.as_ref() { - ProofLocation::InstructionOffset(1.try_into().unwrap(), proof_data_temp) - } else { - let context_state_account = context_state_account.unwrap(); - ProofLocation::ContextStateAccount(context_state_account) - }; - - let new_decryptable_available_balance = account_info - .new_decryptable_available_balance(transfer_amount, source_aes_key) - .map_err(|_| TokenError::AccountDecryption)?; - - let mut instructions = confidential_transfer::instruction::transfer_with_fee( - &self.program_id, - source_account, - destination_account, - self.get_address(), - new_decryptable_available_balance, - source_authority, - &multisig_signers, - proof_location, - )?; - offchain::add_extra_account_metas( - &mut instructions[0], - source_account, - self.get_address(), - destination_account, - source_authority, - u64::MAX, - |address| { - self.client - .get_account(address) - .map_ok(|opt| opt.map(|acc| acc.data)) - }, - ) - .await - .map_err(|_| TokenError::AccountNotFound)?; - self.process_ixs(&instructions, signing_keypairs).await - } - - /// Transfer tokens confidentially with fee using split proofs. - /// - /// This function assumes that proof context states have already been - /// created. - #[allow(clippy::too_many_arguments)] - pub async fn confidential_transfer_transfer_with_fee_and_split_proofs( - &self, - source_account: &Pubkey, - destination_account: &Pubkey, - source_authority: &Pubkey, - context_state_accounts: TransferWithFeeSplitContextStateAccounts<'_>, - transfer_amount: u64, - account_info: Option, - source_aes_key: &AeKey, - source_decrypt_handles: &SourceDecryptHandles, - signing_keypairs: &S, - ) -> TokenResult { - let account_info = if let Some(account_info) = account_info { - account_info - } else { - let account = self.get_account_info(source_account).await?; - let confidential_transfer_account = - account.get_extension::()?; - TransferAccountInfo::new(confidential_transfer_account) - }; - - let new_decryptable_available_balance = account_info - .new_decryptable_available_balance(transfer_amount, source_aes_key) - .map_err(|_| TokenError::AccountDecryption)?; - - let mut instruction = - confidential_transfer::instruction::transfer_with_fee_and_split_proofs( - &self.program_id, - source_account, - self.get_address(), - destination_account, - new_decryptable_available_balance.into(), - source_authority, - context_state_accounts, - source_decrypt_handles, - )?; - offchain::add_extra_account_metas( - &mut instruction, - source_account, - self.get_address(), - destination_account, - source_authority, - u64::MAX, - |address| { - self.client - .get_account(address) - .map_ok(|opt| opt.map(|acc| acc.data)) - }, - ) - .await - .map_err(|_| TokenError::AccountNotFound)?; - self.process_ixs(&[instruction], signing_keypairs).await - } - - /// Transfer tokens confidentially using split proofs in parallel - /// - /// This function internally generates the ZK Token proof instructions to - /// create the necessary proof context states. - #[allow(clippy::too_many_arguments)] - pub async fn confidential_transfer_transfer_with_fee_and_split_proofs_in_parallel< - S: Signers, - >( - &self, - source_account: &Pubkey, - destination_account: &Pubkey, - source_authority: &Pubkey, - context_state_accounts: TransferWithFeeSplitContextStateAccounts<'_>, - transfer_amount: u64, - account_info: Option, - source_elgamal_keypair: &ElGamalKeypair, - source_aes_key: &AeKey, - destination_elgamal_pubkey: &ElGamalPubkey, - auditor_elgamal_pubkey: Option<&ElGamalPubkey>, - withdraw_withheld_authority_elgamal_pubkey: &ElGamalPubkey, - fee_rate_basis_points: u16, - maximum_fee: u64, - equality_and_ciphertext_validity_proof_signers: &S, - fee_sigma_proof_signers: &S, - range_proof_signers: &S, - ) -> TokenResult<(T::Output, T::Output, T::Output)> { - let account_info = if let Some(account_info) = account_info { - account_info - } else { - let account = self.get_account_info(source_account).await?; - let confidential_transfer_account = - account.get_extension::()?; - TransferAccountInfo::new(confidential_transfer_account) - }; - - let current_source_available_balance = account_info - .available_balance - .try_into() - .map_err(|_| TokenError::AccountDecryption)?; - let current_decryptable_available_balance = account_info - .decryptable_available_balance - .try_into() - .map_err(|_| TokenError::AccountDecryption)?; - - let fee_parameters = FeeParameters { - fee_rate_basis_points, - maximum_fee, - }; - - let ( - equality_proof_data, - transfer_amount_ciphertext_validity_proof_data, - fee_sigma_proof_data, - fee_ciphertext_validity_proof_data, - range_proof_data, - source_decrypt_handles, - ) = transfer_with_fee_split_proof_data( - ¤t_source_available_balance, - ¤t_decryptable_available_balance, - transfer_amount, - source_elgamal_keypair, - source_aes_key, - destination_elgamal_pubkey, - auditor_elgamal_pubkey, - withdraw_withheld_authority_elgamal_pubkey, - &fee_parameters, - ) - .map_err(|_| TokenError::ProofGeneration)?; - - let new_decryptable_available_balance = account_info - .new_decryptable_available_balance(transfer_amount, source_aes_key) - .map_err(|_| TokenError::AccountDecryption)?; - - let mut transfer_instruction = - confidential_transfer::instruction::transfer_with_fee_and_split_proofs( - &self.program_id, - source_account, - self.get_address(), - destination_account, - new_decryptable_available_balance.into(), - source_authority, - context_state_accounts, - &source_decrypt_handles, - )?; - offchain::add_extra_account_metas( - &mut transfer_instruction, - source_account, - self.get_address(), - destination_account, - source_authority, - u64::MAX, - |address| { - self.client - .get_account(address) - .map_ok(|opt| opt.map(|acc| acc.data)) - }, - ) - .await - .map_err(|_| TokenError::AccountNotFound)?; - - let transfer_with_equality_and_ciphertext_valdity = self - .create_equality_and_ciphertext_validity_proof_context_states_for_transfer_with_fee_parallel( - context_state_accounts, - &equality_proof_data, - &transfer_amount_ciphertext_validity_proof_data, - &transfer_instruction, - equality_and_ciphertext_validity_proof_signers - ); - - let transfer_with_fee_sigma_and_ciphertext_validity = self - .create_fee_sigma_and_ciphertext_validity_proof_context_states_for_transfer_with_fee_parallel( - context_state_accounts, - &fee_sigma_proof_data, - &fee_ciphertext_validity_proof_data, - &transfer_instruction, - fee_sigma_proof_signers, - ); - - let transfer_with_range_proof = self - .create_range_proof_context_state_for_transfer_with_fee_parallel( - context_state_accounts, - &range_proof_data, - &transfer_instruction, - range_proof_signers, - ); - - try_join!( - transfer_with_equality_and_ciphertext_valdity, - transfer_with_fee_sigma_and_ciphertext_validity, - transfer_with_range_proof, - ) - } - - /// Create equality and transfer amount ciphertext validity proof context - /// state accounts for a confidential transfer with fee. - #[allow(clippy::too_many_arguments)] - pub async fn create_equality_and_ciphertext_validity_proof_context_states_for_transfer_with_fee< - S: Signers, - >( - &self, - context_state_accounts: TransferWithFeeSplitContextStateAccounts<'_>, - equality_proof_data: &CiphertextCommitmentEqualityProofData, - ciphertext_validity_proof_data: &BatchedGroupedCiphertext2HandlesValidityProofData, - signing_keypairs: &S, - ) -> TokenResult { - self.create_equality_and_ciphertext_validity_proof_context_states_with_optional_transfer_with_fee( - context_state_accounts, - equality_proof_data, - ciphertext_validity_proof_data, - None, - signing_keypairs, - ) - .await - } - - /// Create equality and transfer amount ciphertext validity proof context - /// state accounts with a confidential transfer instruction. - #[allow(clippy::too_many_arguments)] - pub async fn create_equality_and_ciphertext_validity_proof_context_states_for_transfer_with_fee_parallel< - S: Signers, - >( - &self, - context_state_accounts: TransferWithFeeSplitContextStateAccounts<'_>, - equality_proof_data: &CiphertextCommitmentEqualityProofData, - ciphertext_validity_proof_data: &BatchedGroupedCiphertext2HandlesValidityProofData, - transfer_instruction: &Instruction, - signing_keypairs: &S, - ) -> TokenResult { - self.create_equality_and_ciphertext_validity_proof_context_states_with_optional_transfer_with_fee( - context_state_accounts, - equality_proof_data, - ciphertext_validity_proof_data, - Some(transfer_instruction), - signing_keypairs, - ) - .await - } - - /// Create equality and ciphertext validity proof context states for a - /// confidential transfer with fee. - /// - /// If an optional transfer instruction is provided, then the transfer - /// instruction is attached to the same transaction. - #[allow(clippy::too_many_arguments)] - async fn create_equality_and_ciphertext_validity_proof_context_states_with_optional_transfer_with_fee< - S: Signers, - >( - &self, - context_state_accounts: TransferWithFeeSplitContextStateAccounts<'_>, - equality_proof_data: &CiphertextCommitmentEqualityProofData, - transfer_amount_ciphertext_validity_proof_data: &BatchedGroupedCiphertext2HandlesValidityProofData, - transfer_instruction: Option<&Instruction>, - signing_keypairs: &S, - ) -> TokenResult { - let mut instructions = vec![]; - - // create equality proof context state - let instruction_type = ProofInstruction::VerifyCiphertextCommitmentEquality; - let space = size_of::>(); - let rent = self - .client - .get_minimum_balance_for_rent_exemption(space) - .await - .map_err(TokenError::Client)?; - instructions.push(system_instruction::create_account( - &self.payer.pubkey(), - context_state_accounts.equality_proof, - rent, - space as u64, - &zk_token_proof_program::id(), - )); - - let equality_proof_context_state_info = ContextStateInfo { - context_state_account: context_state_accounts.equality_proof, - context_state_authority: context_state_accounts.authority, - }; - instructions.push( - instruction_type - .encode_verify_proof(Some(equality_proof_context_state_info), equality_proof_data), - ); - - // create transfer amount ciphertext validity proof context state - let instruction_type = ProofInstruction::VerifyBatchedGroupedCiphertext2HandlesValidity; - let space = - size_of::>(); - let rent = self - .client - .get_minimum_balance_for_rent_exemption(space) - .await - .map_err(TokenError::Client)?; - instructions.push(system_instruction::create_account( - &self.payer.pubkey(), - context_state_accounts.transfer_amount_ciphertext_validity_proof, - rent, - space as u64, - &zk_token_proof_program::id(), - )); - - let transfer_amount_ciphertext_validity_proof_context_state_info = ContextStateInfo { - context_state_account: context_state_accounts.transfer_amount_ciphertext_validity_proof, - context_state_authority: context_state_accounts.authority, - }; - instructions.push(instruction_type.encode_verify_proof( - Some(transfer_amount_ciphertext_validity_proof_context_state_info), - transfer_amount_ciphertext_validity_proof_data, - )); - - // add transfer instruction - if let Some(transfer_instruction) = transfer_instruction { - instructions.push(transfer_instruction.clone()); - } - - self.process_ixs(&instructions, signing_keypairs).await - } - - /// Create fee sigma and fee ciphertext validity proof context state - /// accounts for a confidential transfer with fee. - #[allow(clippy::too_many_arguments)] - pub async fn create_fee_sigma_and_ciphertext_validity_proof_context_states_for_transfer_with_fee< - S: Signers, - >( - &self, - context_state_accounts: TransferWithFeeSplitContextStateAccounts<'_>, - fee_sigma_proof_data: &FeeSigmaProofData, - fee_ciphertext_validity_proof_data: &BatchedGroupedCiphertext2HandlesValidityProofData, - signing_keypairs: &S, - ) -> TokenResult { - self.create_fee_sigma_and_ciphertext_validity_proof_context_states_with_optional_transfer_with_fee( - context_state_accounts, - fee_sigma_proof_data, - fee_ciphertext_validity_proof_data, - None, - signing_keypairs, - ) - .await - } - - /// Create fee sigma and fee ciphertext validity proof context state - /// accounts with a confidential transfer with fee. - #[allow(clippy::too_many_arguments)] - pub async fn create_fee_sigma_and_ciphertext_validity_proof_context_states_for_transfer_with_fee_parallel< - S: Signers, - >( - &self, - context_state_accounts: TransferWithFeeSplitContextStateAccounts<'_>, - fee_sigma_proof_data: &FeeSigmaProofData, - fee_ciphertext_validity_proof_data: &BatchedGroupedCiphertext2HandlesValidityProofData, - transfer_instruction: &Instruction, - signing_keypairs: &S, - ) -> TokenResult { - self.create_fee_sigma_and_ciphertext_validity_proof_context_states_with_optional_transfer_with_fee( - context_state_accounts, - fee_sigma_proof_data, - fee_ciphertext_validity_proof_data, - Some(transfer_instruction), - signing_keypairs, - ) - .await - } - - /// Create fee sigma and fee ciphertext validity proof context states for a - /// confidential transfer with fee. - /// - /// If an optional transfer instruction is provided, then the transfer - /// instruction is attached to the same transaction. - #[allow(clippy::too_many_arguments)] - async fn create_fee_sigma_and_ciphertext_validity_proof_context_states_with_optional_transfer_with_fee< - S: Signers, - >( - &self, - context_state_accounts: TransferWithFeeSplitContextStateAccounts<'_>, - fee_sigma_proof_data: &FeeSigmaProofData, - fee_ciphertext_validity_proof_data: &BatchedGroupedCiphertext2HandlesValidityProofData, - transfer_instruction: Option<&Instruction>, - signing_keypairs: &S, - ) -> TokenResult { - let mut instructions = vec![]; - - // create fee sigma proof context state - let instruction_type = ProofInstruction::VerifyFeeSigma; - let space = size_of::>(); - let rent = self - .client - .get_minimum_balance_for_rent_exemption(space) - .await - .map_err(TokenError::Client)?; - instructions.push(system_instruction::create_account( - &self.payer.pubkey(), - context_state_accounts.fee_sigma_proof, - rent, - space as u64, - &zk_token_proof_program::id(), - )); - - let fee_sigma_proof_context_state_info = ContextStateInfo { - context_state_account: context_state_accounts.fee_sigma_proof, - context_state_authority: context_state_accounts.authority, - }; - instructions.push(instruction_type.encode_verify_proof( - Some(fee_sigma_proof_context_state_info), - fee_sigma_proof_data, - )); - - // create fee ciphertext validity proof context state - let instruction_type = ProofInstruction::VerifyBatchedGroupedCiphertext2HandlesValidity; - let space = - size_of::>(); - let rent = self - .client - .get_minimum_balance_for_rent_exemption(space) - .await - .map_err(TokenError::Client)?; - instructions.push(system_instruction::create_account( - &self.payer.pubkey(), - context_state_accounts.fee_ciphertext_validity_proof, - rent, - space as u64, - &zk_token_proof_program::id(), - )); - - let fee_ciphertext_validity_proof_context_state_info = ContextStateInfo { - context_state_account: context_state_accounts.fee_ciphertext_validity_proof, - context_state_authority: context_state_accounts.authority, - }; - instructions.push(instruction_type.encode_verify_proof( - Some(fee_ciphertext_validity_proof_context_state_info), - fee_ciphertext_validity_proof_data, - )); - - // add transfer instruction - if let Some(transfer_instruction) = transfer_instruction { - instructions.push(transfer_instruction.clone()); - } - - self.process_ixs(&instructions, signing_keypairs).await - } - - /// Create range proof context state account for a confidential transfer - /// with fee. - #[allow(clippy::too_many_arguments)] - pub async fn create_range_proof_context_state_for_transfer_with_fee( - &self, - context_state_accounts: TransferWithFeeSplitContextStateAccounts<'_>, - range_proof_data: &BatchedRangeProofU256Data, - signing_keypairs: &S, - ) -> TokenResult { - self.create_range_proof_context_state_with_optional_transfer_with_fee( - context_state_accounts, - range_proof_data, - None, - signing_keypairs, - ) - .await - } - - /// Create range proof context state account for a confidential transfer - /// with fee. - #[allow(clippy::too_many_arguments)] - pub async fn create_range_proof_context_state_for_transfer_with_fee_parallel( - &self, - context_state_accounts: TransferWithFeeSplitContextStateAccounts<'_>, - range_proof_data: &BatchedRangeProofU256Data, - transfer_instruction: &Instruction, - signing_keypairs: &S, - ) -> TokenResult { - self.create_range_proof_context_state_with_optional_transfer_with_fee( - context_state_accounts, - range_proof_data, - Some(transfer_instruction), - signing_keypairs, - ) - .await - } - - /// Create a range proof context state account and an optional confidential - /// transfer instruction. - async fn create_range_proof_context_state_with_optional_transfer_with_fee( - &self, - context_state_accounts: TransferWithFeeSplitContextStateAccounts<'_>, - range_proof_data: &BatchedRangeProofU256Data, - transfer_instruction: Option<&Instruction>, - signing_keypairs: &S, - ) -> TokenResult { - let instruction_type = ProofInstruction::VerifyBatchedRangeProofU256; - let space = size_of::>(); - let rent = self - .client - .get_minimum_balance_for_rent_exemption(space) - .await - .map_err(TokenError::Client)?; - let range_proof_context_state_info = ContextStateInfo { - context_state_account: context_state_accounts.range_proof, - context_state_authority: context_state_accounts.authority, - }; - - let mut instructions = vec![ - system_instruction::create_account( - &self.payer.pubkey(), - context_state_accounts.range_proof, - rent, - space as u64, - &zk_token_proof_program::id(), - ), - instruction_type - .encode_verify_proof(Some(range_proof_context_state_info), range_proof_data), - ]; - - if let Some(transfer_instruction) = transfer_instruction { - instructions.push(transfer_instruction.clone()); - } - - self.process_ixs(&instructions, signing_keypairs).await - } - - /// Applies the confidential transfer pending balance to the available - /// balance - pub async fn confidential_transfer_apply_pending_balance( - &self, - account: &Pubkey, - authority: &Pubkey, - account_info: Option, - elgamal_secret_key: &ElGamalSecretKey, - aes_key: &AeKey, - signing_keypairs: &S, - ) -> TokenResult { - let signing_pubkeys = signing_keypairs.pubkeys(); - let multisig_signers = self.get_multisig_signers(authority, &signing_pubkeys); - - let account_info = if let Some(account_info) = account_info { - account_info - } else { - let account = self.get_account_info(account).await?; - let confidential_transfer_account = - account.get_extension::()?; - ApplyPendingBalanceAccountInfo::new(confidential_transfer_account) - }; - - let expected_pending_balance_credit_counter = account_info.pending_balance_credit_counter(); - let new_decryptable_available_balance = account_info - .new_decryptable_available_balance(elgamal_secret_key, aes_key) - .map_err(|_| TokenError::AccountDecryption)?; - - self.process_ixs( - &[confidential_transfer::instruction::apply_pending_balance( + self.process_ixs( + &[confidential_transfer::instruction::apply_pending_balance( &self.program_id, account, expected_pending_balance_credit_counter, - new_decryptable_available_balance, + new_decryptable_available_balance.into(), authority, &multisig_signers, )?], @@ -3493,7 +2786,7 @@ where &self, destination_account: &Pubkey, withdraw_withheld_authority: &Pubkey, - context_state_account: Option<&Pubkey>, + proof_account: Option<&ProofAccount>, withheld_tokens_info: Option, withdraw_withheld_authority_elgamal_keypair: &ElGamalKeypair, destination_elgamal_pubkey: &ElGamalPubkey, @@ -3513,7 +2806,7 @@ where WithheldTokensInfo::new(&confidential_transfer_fee_config.withheld_amount) }; - let proof_data = if context_state_account.is_some() { + let proof_data = if proof_account.is_some() { None } else { Some( @@ -3526,12 +2819,14 @@ where ) }; - let proof_location = if let Some(proof_data_temp) = proof_data.as_ref() { - ProofLocation::InstructionOffset(1.try_into().unwrap(), proof_data_temp) - } else { - let context_state_account = context_state_account.unwrap(); - ProofLocation::ContextStateAccount(context_state_account) - }; + // cannot panic as long as either `proof_data` or `proof_account` is `Some(..)`, + // which is guaranteed by the previous check + let proof_location = Self::confidential_transfer_create_proof_location( + proof_data.as_ref(), + proof_account, + 1, + ) + .unwrap(); self.process_ixs( &confidential_transfer_fee::instruction::withdraw_withheld_tokens_from_mint( @@ -3554,7 +2849,7 @@ where &self, destination_account: &Pubkey, withdraw_withheld_authority: &Pubkey, - context_state_account: Option<&Pubkey>, + proof_account: Option<&ProofAccount>, withheld_tokens_info: Option, withdraw_withheld_authority_elgamal_keypair: &ElGamalKeypair, destination_elgamal_pubkey: &ElGamalPubkey, @@ -3585,7 +2880,7 @@ where WithheldTokensInfo::new(&aggregate_withheld_amount.into()) }; - let proof_data = if context_state_account.is_some() { + let proof_data = if proof_account.is_some() { None } else { Some( @@ -3598,12 +2893,14 @@ where ) }; - let proof_location = if let Some(proof_data_temp) = proof_data.as_ref() { - ProofLocation::InstructionOffset(1.try_into().unwrap(), proof_data_temp) - } else { - let context_state_account = context_state_account.unwrap(); - ProofLocation::ContextStateAccount(context_state_account) - }; + // cannot panic as long as either `proof_data` or `proof_account` is `Some(..)`, + // which is guaranteed by the previous check + let proof_location = Self::confidential_transfer_create_proof_location( + proof_data.as_ref(), + proof_account, + 1, + ) + .unwrap(); self.process_ixs( &confidential_transfer_fee::instruction::withdraw_withheld_tokens_from_accounts( @@ -3687,6 +2984,35 @@ where .await } + // Creates `ProofLocation` from proof data and `ProofAccount`. If both + // `proof_data` and `proof_account` are `None`, then the result is `None`. + fn confidential_transfer_create_proof_location<'a, ZK: ZkProofData, U: Pod>( + proof_data: Option<&'a ZK>, + proof_account: Option<&'a ProofAccount>, + instruction_offset: i8, + ) -> Option> { + if let Some(proof_data) = proof_data { + Some(ProofLocation::InstructionOffset( + instruction_offset.try_into().unwrap(), + ProofData::InstructionData(proof_data), + )) + } else if let Some(proof_account) = proof_account { + match proof_account { + ProofAccount::ContextAccount(context_state_account) => { + Some(ProofLocation::ContextStateAccount(context_state_account)) + } + ProofAccount::RecordAccount(record_account, data_offset) => { + Some(ProofLocation::InstructionOffset( + instruction_offset.try_into().unwrap(), + ProofData::RecordAccount(record_account, *data_offset), + )) + } + } + } else { + None + } + } + pub async fn withdraw_excess_lamports( &self, source: &Pubkey, @@ -3917,7 +3243,7 @@ where &self, mint_authority: &Pubkey, update_authority: &Pubkey, - max_size: u32, + max_size: u64, signing_keypairs: &S, ) -> TokenResult { self.process_ixs( @@ -3959,7 +3285,7 @@ where payer: &Pubkey, mint_authority: &Pubkey, update_authority: &Pubkey, - max_size: u32, + max_size: u64, signing_keypairs: &S, ) -> TokenResult { let additional_lamports = self @@ -3988,7 +3314,7 @@ where pub async fn token_group_update_max_size( &self, update_authority: &Pubkey, - new_max_size: u32, + new_max_size: u64, signing_keypairs: &S, ) -> TokenResult { self.process_ixs( @@ -4080,3 +3406,22 @@ where self.process_ixs(&instructions, signing_keypairs).await } } + +/// Calculates the maximum chunk size for a zero-knowledge proof record +/// instruction to fit inside a single transaction. +fn calculate_record_max_chunk_size( + create_record_instructions: F, + first_instruction: bool, +) -> usize +where + F: Fn(bool, &[u8], u64) -> Vec, +{ + let ixs = create_record_instructions(first_instruction, &[], 0); + let message = Message::new_with_blockhash(&ixs, Some(&Pubkey::default()), &Hash::default()); + let tx_size = bincode::serialized_size(&Transaction { + signatures: vec![Signature::default(); message.header.num_required_signatures as usize], + message, + }) + .unwrap() as usize; + PACKET_DATA_SIZE.saturating_sub(tx_size).saturating_sub(1) +} diff --git a/token/confidential-transfer/ciphertext-arithmetic/Cargo.toml b/token/confidential-transfer/ciphertext-arithmetic/Cargo.toml index 3f848de384a..41e8475d670 100644 --- a/token/confidential-transfer/ciphertext-arithmetic/Cargo.toml +++ b/token/confidential-transfer/ciphertext-arithmetic/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" [dependencies] base64 = "0.22.1" -bytemuck = "1.16.1" +bytemuck = "1.17.0" solana-curve25519 = "2.0.0" solana-zk-sdk = "2.0.3" diff --git a/token/confidential-transfer/proof-extraction/Cargo.toml b/token/confidential-transfer/proof-extraction/Cargo.toml index 8949627a946..d70856c11b7 100644 --- a/token/confidential-transfer/proof-extraction/Cargo.toml +++ b/token/confidential-transfer/proof-extraction/Cargo.toml @@ -8,7 +8,7 @@ license = "Apache-2.0" edition = "2021" [dependencies] -bytemuck = "1.16.1" +bytemuck = "1.17.0" solana-curve25519 = "2.0.0" solana-zk-sdk = "2.0.3" thiserror = "1.0.63" diff --git a/token/confidential-transfer/proof-extraction/src/encryption.rs b/token/confidential-transfer/proof-extraction/src/encryption.rs index 49db2b61cec..34ca2fff156 100644 --- a/token/confidential-transfer/proof-extraction/src/encryption.rs +++ b/token/confidential-transfer/proof-extraction/src/encryption.rs @@ -1,11 +1,48 @@ -use solana_zk_sdk::encryption::pod::grouped_elgamal::{ - PodGroupedElGamalCiphertext2Handles, PodGroupedElGamalCiphertext3Handles, +use { + crate::errors::TokenProofExtractionError, + solana_zk_sdk::encryption::pod::{ + elgamal::PodElGamalCiphertext, + grouped_elgamal::{ + PodGroupedElGamalCiphertext2Handles, PodGroupedElGamalCiphertext3Handles, + }, + pedersen::PodPedersenCommitment, + }, }; #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[repr(C)] pub struct PodTransferAmountCiphertext(pub(crate) PodGroupedElGamalCiphertext3Handles); +impl PodTransferAmountCiphertext { + pub fn extract_commitment(&self) -> PodPedersenCommitment { + self.0.extract_commitment() + } + + pub fn try_extract_ciphertext( + &self, + index: usize, + ) -> Result { + self.0 + .try_extract_ciphertext(index) + .map_err(|_| TokenProofExtractionError::CiphertextExtraction) + } +} + #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[repr(C)] pub struct PodFeeCiphertext(pub(crate) PodGroupedElGamalCiphertext2Handles); + +impl PodFeeCiphertext { + pub fn extract_commitment(&self) -> PodPedersenCommitment { + self.0.extract_commitment() + } + + pub fn try_extract_ciphertext( + &self, + index: usize, + ) -> Result { + self.0 + .try_extract_ciphertext(index) + .map_err(|_| TokenProofExtractionError::CiphertextExtraction) + } +} diff --git a/token/confidential-transfer/proof-extraction/src/errors.rs b/token/confidential-transfer/proof-extraction/src/errors.rs index 4e05a11a29b..e710dec7159 100644 --- a/token/confidential-transfer/proof-extraction/src/errors.rs +++ b/token/confidential-transfer/proof-extraction/src/errors.rs @@ -12,4 +12,6 @@ pub enum TokenProofExtractionError { FeeParametersMismatch, #[error("Curve arithmetic failed")] CurveArithmetic, + #[error("Ciphertext extraction failed")] + CiphertextExtraction, } diff --git a/token/js/.prettierignore b/token/js/.prettierignore deleted file mode 100644 index 6da325effab..00000000000 --- a/token/js/.prettierignore +++ /dev/null @@ -1,5 +0,0 @@ -docs -lib -test-ledger - -package-lock.json diff --git a/token/js/.prettierrc b/token/js/.prettierrc deleted file mode 100644 index b9ce4c1923a..00000000000 --- a/token/js/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "printWidth": 120, - "trailingComma": "es5", - "tabWidth": 4, - "semi": true, - "singleQuote": true -} \ No newline at end of file diff --git a/token/js/examples/cpiGuard.ts b/token/js/examples/cpiGuard.ts index 9158d32107f..b7182910e1d 100644 --- a/token/js/examples/cpiGuard.ts +++ b/token/js/examples/cpiGuard.ts @@ -35,7 +35,7 @@ import { decimals, undefined, undefined, - TOKEN_2022_PROGRAM_ID + TOKEN_2022_PROGRAM_ID, ); const accountLen = getAccountLen([ExtensionType.CpiGuard]); @@ -53,7 +53,7 @@ import { programId: TOKEN_2022_PROGRAM_ID, }), createInitializeAccountInstruction(destination, mint, owner.publicKey, TOKEN_2022_PROGRAM_ID), - createEnableCpiGuardInstruction(destination, owner.publicKey, [], TOKEN_2022_PROGRAM_ID) + createEnableCpiGuardInstruction(destination, owner.publicKey, [], TOKEN_2022_PROGRAM_ID), ); await sendAndConfirmTransaction(connection, transaction, [payer, owner, destinationKeypair], undefined); diff --git a/token/js/examples/createMintAndTransferTokens.ts b/token/js/examples/createMintAndTransferTokens.ts index 30a619b980d..da6437d73f2 100644 --- a/token/js/examples/createMintAndTransferTokens.ts +++ b/token/js/examples/createMintAndTransferTokens.ts @@ -26,7 +26,7 @@ import { createMint, getOrCreateAssociatedTokenAccount, mintTo, transfer } from connection, fromWallet, mint, - fromWallet.publicKey + fromWallet.publicKey, ); // Get the token account of the toWallet address, and if it does not exist, create it @@ -40,7 +40,7 @@ import { createMint, getOrCreateAssociatedTokenAccount, mintTo, transfer } from fromTokenAccount.address, fromWallet.publicKey, 1000000000, - [] + [], ); console.log('mint tx:', signature); @@ -52,7 +52,7 @@ import { createMint, getOrCreateAssociatedTokenAccount, mintTo, transfer } from toTokenAccount.address, fromWallet.publicKey, 1000000000, - [] + [], ); console.log('transfer tx:', signature); })(); diff --git a/token/js/examples/createMintCloseAuthority.ts b/token/js/examples/createMintCloseAuthority.ts index 9850d782445..330a35946ea 100644 --- a/token/js/examples/createMintCloseAuthority.ts +++ b/token/js/examples/createMintCloseAuthority.ts @@ -48,8 +48,8 @@ import { 9, mintAuthority.publicKey, freezeAuthority.publicKey, - TOKEN_2022_PROGRAM_ID - ) + TOKEN_2022_PROGRAM_ID, + ), ); await sendAndConfirmTransaction(connection, transaction, [payer, mintKeypair], undefined); diff --git a/token/js/examples/defaultAccountState.ts b/token/js/examples/defaultAccountState.ts index 8529cf442c5..e160abe88bb 100644 --- a/token/js/examples/defaultAccountState.ts +++ b/token/js/examples/defaultAccountState.ts @@ -51,8 +51,8 @@ import { decimals, mintAuthority.publicKey, freezeAuthority.publicKey, - TOKEN_2022_PROGRAM_ID - ) + TOKEN_2022_PROGRAM_ID, + ), ); await sendAndConfirmTransaction(connection, transaction, [payer, mintKeypair], undefined); @@ -65,6 +65,6 @@ import { freezeAuthority, [], undefined, - TOKEN_2022_PROGRAM_ID + TOKEN_2022_PROGRAM_ID, ); })(); diff --git a/token/js/examples/immutableOwner.ts b/token/js/examples/immutableOwner.ts index 4010a35d074..53e4c774233 100644 --- a/token/js/examples/immutableOwner.ts +++ b/token/js/examples/immutableOwner.ts @@ -34,7 +34,7 @@ import { decimals, undefined, undefined, - TOKEN_2022_PROGRAM_ID + TOKEN_2022_PROGRAM_ID, ); const accountLen = getAccountLen([ExtensionType.ImmutableOwner]); @@ -52,7 +52,7 @@ import { programId: TOKEN_2022_PROGRAM_ID, }), createInitializeImmutableOwnerInstruction(account, TOKEN_2022_PROGRAM_ID), - createInitializeAccountInstruction(account, mint, owner.publicKey, TOKEN_2022_PROGRAM_ID) + createInitializeAccountInstruction(account, mint, owner.publicKey, TOKEN_2022_PROGRAM_ID), ); await sendAndConfirmTransaction(connection, transaction, [payer, accountKeypair], undefined); diff --git a/token/js/examples/interestBearing.ts b/token/js/examples/interestBearing.ts index 8d688e83ea2..280c706b36c 100644 --- a/token/js/examples/interestBearing.ts +++ b/token/js/examples/interestBearing.ts @@ -24,7 +24,7 @@ import { createInterestBearingMint, updateRateInterestBearingMint, TOKEN_2022_PR decimals, mintKeypair, undefined, - TOKEN_2022_PROGRAM_ID + TOKEN_2022_PROGRAM_ID, ); const updateRate = 50; @@ -36,6 +36,6 @@ import { createInterestBearingMint, updateRateInterestBearingMint, TOKEN_2022_PR updateRate, [], undefined, - TOKEN_2022_PROGRAM_ID + TOKEN_2022_PROGRAM_ID, ); })(); diff --git a/token/js/examples/memoTransfer.ts b/token/js/examples/memoTransfer.ts index 598f17dc650..708ea7424d4 100644 --- a/token/js/examples/memoTransfer.ts +++ b/token/js/examples/memoTransfer.ts @@ -39,7 +39,7 @@ import { decimals, undefined, undefined, - TOKEN_2022_PROGRAM_ID + TOKEN_2022_PROGRAM_ID, ); const accountLen = getAccountLen([ExtensionType.MemoTransfer]); @@ -57,7 +57,7 @@ import { programId: TOKEN_2022_PROGRAM_ID, }), createInitializeAccountInstruction(destination, mint, owner.publicKey, TOKEN_2022_PROGRAM_ID), - createEnableRequiredMemoTransfersInstruction(destination, owner.publicKey, [], TOKEN_2022_PROGRAM_ID) + createEnableRequiredMemoTransfersInstruction(destination, owner.publicKey, [], TOKEN_2022_PROGRAM_ID), ); await sendAndConfirmTransaction(connection, transaction, [payer, owner, destinationKeypair], undefined); @@ -72,13 +72,13 @@ import { mint, payer.publicKey, undefined, - TOKEN_2022_PROGRAM_ID + TOKEN_2022_PROGRAM_ID, ); await mintTo(connection, payer, mint, sourceTokenAccount, mintAuthority, 100, [], undefined, TOKEN_2022_PROGRAM_ID); const transferTransaction = new Transaction().add( createMemoInstruction('Hello, memo-transfer!', [payer.publicKey]), - createTransferInstruction(sourceTokenAccount, destination, payer.publicKey, 100, [], TOKEN_2022_PROGRAM_ID) + createTransferInstruction(sourceTokenAccount, destination, payer.publicKey, 100, [], TOKEN_2022_PROGRAM_ID), ); await sendAndConfirmTransaction(connection, transferTransaction, [payer], undefined); })(); diff --git a/token/js/examples/metadata.ts b/token/js/examples/metadata.ts index 66b47cb3291..92f09aaf61c 100644 --- a/token/js/examples/metadata.ts +++ b/token/js/examples/metadata.ts @@ -63,7 +63,7 @@ import { mint.publicKey, payer.publicKey, mint.publicKey, - TOKEN_2022_PROGRAM_ID + TOKEN_2022_PROGRAM_ID, ), createInitializeMintInstruction(mint.publicKey, decimals, payer.publicKey, null, TOKEN_2022_PROGRAM_ID), createInitializeInstruction({ @@ -102,7 +102,7 @@ import { updateAuthority: payer.publicKey, key: 'new-field', idempotent: true, // If false the operation will fail if the field does not exist in the metadata - }) + }), ); const sig = await sendAndConfirmTransaction(connection, mintTransaction, [payer, mint]); console.log('Signature:', sig); diff --git a/token/js/examples/nonTransferable.ts b/token/js/examples/nonTransferable.ts index 0e2bb7d4e41..13df064fc1a 100644 --- a/token/js/examples/nonTransferable.ts +++ b/token/js/examples/nonTransferable.ts @@ -39,7 +39,7 @@ import { programId: TOKEN_2022_PROGRAM_ID, }), createInitializeNonTransferableMintInstruction(mint, TOKEN_2022_PROGRAM_ID), - createInitializeMintInstruction(mint, decimals, mintAuthority.publicKey, null, TOKEN_2022_PROGRAM_ID) + createInitializeMintInstruction(mint, decimals, mintAuthority.publicKey, null, TOKEN_2022_PROGRAM_ID), ); await sendAndConfirmTransaction(connection, transaction, [payer, mintKeypair], undefined); })(); diff --git a/token/js/examples/permanentDelegate.ts b/token/js/examples/permanentDelegate.ts index 2b87570c602..a1c43e63a21 100644 --- a/token/js/examples/permanentDelegate.ts +++ b/token/js/examples/permanentDelegate.ts @@ -46,7 +46,7 @@ import { programId: TOKEN_2022_PROGRAM_ID, }), createInitializePermanentDelegateInstruction(mint, permanentDelegate.publicKey, TOKEN_2022_PROGRAM_ID), - createInitializeMintInstruction(mint, decimals, mintAuthority.publicKey, null, TOKEN_2022_PROGRAM_ID) + createInitializeMintInstruction(mint, decimals, mintAuthority.publicKey, null, TOKEN_2022_PROGRAM_ID), ); await sendAndConfirmTransaction(connection, mintTransaction, [payer, mintKeypair], undefined); @@ -59,7 +59,7 @@ import { owner.publicKey, undefined, undefined, - TOKEN_2022_PROGRAM_ID + TOKEN_2022_PROGRAM_ID, ); await mintTo( connection, @@ -70,7 +70,7 @@ import { mintAmount, [], undefined, - TOKEN_2022_PROGRAM_ID + TOKEN_2022_PROGRAM_ID, ); const accountKeypair = Keypair.generate(); @@ -81,7 +81,7 @@ import { owner.publicKey, accountKeypair, undefined, - TOKEN_2022_PROGRAM_ID + TOKEN_2022_PROGRAM_ID, ); // Transfer by signing with the permanent delegate @@ -96,6 +96,6 @@ import { decimals, [], undefined, - TOKEN_2022_PROGRAM_ID + TOKEN_2022_PROGRAM_ID, ); })(); diff --git a/token/js/examples/reallocate.ts b/token/js/examples/reallocate.ts index aece8c1ec23..3ee07efd3f5 100644 --- a/token/js/examples/reallocate.ts +++ b/token/js/examples/reallocate.ts @@ -32,7 +32,7 @@ import { decimals, undefined, undefined, - TOKEN_2022_PROGRAM_ID + TOKEN_2022_PROGRAM_ID, ); const owner = Keypair.generate(); @@ -43,7 +43,7 @@ import { owner.publicKey, undefined, undefined, - TOKEN_2022_PROGRAM_ID + TOKEN_2022_PROGRAM_ID, ); const extensions = [ExtensionType.MemoTransfer]; @@ -54,9 +54,9 @@ import { extensions, owner.publicKey, undefined, - TOKEN_2022_PROGRAM_ID + TOKEN_2022_PROGRAM_ID, ), - createEnableRequiredMemoTransfersInstruction(account, owner.publicKey, [], TOKEN_2022_PROGRAM_ID) + createEnableRequiredMemoTransfersInstruction(account, owner.publicKey, [], TOKEN_2022_PROGRAM_ID), ); await sendAndConfirmTransaction(connection, transaction, [payer, owner], undefined); })(); diff --git a/token/js/examples/transferFee.ts b/token/js/examples/transferFee.ts index 953a780895b..2cc31fddedf 100644 --- a/token/js/examples/transferFee.ts +++ b/token/js/examples/transferFee.ts @@ -63,9 +63,9 @@ import { withdrawWithheldAuthority.publicKey, feeBasisPoints, maxFee, - TOKEN_2022_PROGRAM_ID + TOKEN_2022_PROGRAM_ID, ), - createInitializeMintInstruction(mint, decimals, mintAuthority.publicKey, null, TOKEN_2022_PROGRAM_ID) + createInitializeMintInstruction(mint, decimals, mintAuthority.publicKey, null, TOKEN_2022_PROGRAM_ID), ); await sendAndConfirmTransaction(connection, mintTransaction, [payer, mintKeypair], undefined); @@ -78,7 +78,7 @@ import { owner.publicKey, undefined, undefined, - TOKEN_2022_PROGRAM_ID + TOKEN_2022_PROGRAM_ID, ); await mintTo( connection, @@ -89,7 +89,7 @@ import { mintAmount, [], undefined, - TOKEN_2022_PROGRAM_ID + TOKEN_2022_PROGRAM_ID, ); const accountKeypair = Keypair.generate(); @@ -100,7 +100,7 @@ import { owner.publicKey, accountKeypair, undefined, - TOKEN_2022_PROGRAM_ID + TOKEN_2022_PROGRAM_ID, ); const transferAmount = BigInt(1_000_000); @@ -117,7 +117,7 @@ import { fee, [], undefined, - TOKEN_2022_PROGRAM_ID + TOKEN_2022_PROGRAM_ID, ); const allAccounts = await connection.getProgramAccounts(TOKEN_2022_PROGRAM_ID, { @@ -149,7 +149,7 @@ import { [], accountsToWithdrawFrom, undefined, - TOKEN_2022_PROGRAM_ID + TOKEN_2022_PROGRAM_ID, ); await harvestWithheldTokensToMint(connection, payer, mint, [destinationAccount], undefined, TOKEN_2022_PROGRAM_ID); @@ -162,6 +162,6 @@ import { withdrawWithheldAuthority, [], undefined, - TOKEN_2022_PROGRAM_ID + TOKEN_2022_PROGRAM_ID, ); })(); diff --git a/token/js/examples/transferHook.ts b/token/js/examples/transferHook.ts index bd73583b797..1850b757d12 100644 --- a/token/js/examples/transferHook.ts +++ b/token/js/examples/transferHook.ts @@ -52,7 +52,7 @@ import { programId: TOKEN_2022_PROGRAM_ID, }), createInitializeTransferHookInstruction(mint, payer.publicKey, transferHookPogramId, TOKEN_2022_PROGRAM_ID), - createInitializeMintInstruction(mint, decimals, mintAuthority.publicKey, null, TOKEN_2022_PROGRAM_ID) + createInitializeMintInstruction(mint, decimals, mintAuthority.publicKey, null, TOKEN_2022_PROGRAM_ID), ); await sendAndConfirmTransaction(connection, mintTransaction, [payer, mintKeypair], undefined); @@ -64,7 +64,7 @@ import { payer.publicKey, [], undefined, - TOKEN_2022_PROGRAM_ID + TOKEN_2022_PROGRAM_ID, ); const senderAta = getAssociatedTokenAddressSync( @@ -72,14 +72,14 @@ import { sender.publicKey, false, TOKEN_2022_PROGRAM_ID, - ASSOCIATED_TOKEN_PROGRAM_ID + ASSOCIATED_TOKEN_PROGRAM_ID, ); const recipientAta = getAssociatedTokenAddressSync( mint, recipient.publicKey, false, TOKEN_2022_PROGRAM_ID, - ASSOCIATED_TOKEN_PROGRAM_ID + ASSOCIATED_TOKEN_PROGRAM_ID, ); await transferCheckedWithTransferHook( @@ -93,6 +93,6 @@ import { 9, [], undefined, - TOKEN_2022_PROGRAM_ID + TOKEN_2022_PROGRAM_ID, ); })(); diff --git a/token/js/package.json b/token/js/package.json index 5626f2b69ba..3544cc82961 100644 --- a/token/js/package.json +++ b/token/js/package.json @@ -36,9 +36,8 @@ "build:program": "cargo build-sbf --manifest-path=../program/Cargo.toml && cargo build-sbf --manifest-path=../program-2022/Cargo.toml && cargo build-sbf --manifest-path=../../associated-token-account/program/Cargo.toml && cargo build-sbf --no-default-features --manifest-path=../transfer-hook/example/Cargo.toml", "watch": "tsc --build --verbose --watch tsconfig.all.json", "release": "npm run clean && npm run build", - "fmt": "prettier --write '{*,**/*}.{ts,tsx,js,jsx,json}'", - "lint": "prettier --check '{*,**/*}.{ts,tsx,js,jsx,json}' && eslint --max-warnings 0 .", - "lint:fix": "npm run fmt && eslint --fix .", + "lint": "eslint --max-warnings 0 .", + "lint:fix": "eslint --fix .", "example": "node --experimental-specifier-resolution=node --loader ts-node/esm examples/createMintAndTransferTokens.ts", "test": "npm run test:unit && npm run test:e2e-built && npm run test:e2e-native && npm run test:e2e-2022", "test:unit": "mocha test/unit", @@ -51,7 +50,7 @@ "deploy:docs": "npm run docs && gh-pages --dest token/js --dist docs --dotfiles" }, "peerDependencies": { - "@solana/web3.js": "^1.95.1" + "@solana/web3.js": "^1.95.3" }, "dependencies": { "@solana/buffer-layout": "^4.0.0", @@ -61,30 +60,27 @@ "buffer": "^6.0.3" }, "devDependencies": { - "@solana/codecs-strings": "2.0.0-preview.4", + "@solana/codecs-strings": "2.0.0-rc.1", "@solana/spl-memo": "0.2.4", - "@solana/web3.js": "^1.95.1", + "@solana/web3.js": "^1.95.3", "@types/chai-as-promised": "^7.1.4", - "@types/chai": "^4.3.16", + "@types/chai": "^4.3.17", "@types/mocha": "^10.0.7", - "@types/node": "^22.0.0", + "@types/node": "^22.4.2", "@types/node-fetch": "^2.6.11", - "@typescript-eslint/eslint-plugin": "^7.17.0", - "@typescript-eslint/parser": "^7.17.0", + "@typescript-eslint/eslint-plugin": "^8.2.0", + "@typescript-eslint/parser": "^8.2.0", "chai": "^5.1.1", "chai-as-promised": "^8.0.0", "eslint": "^8.57.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-require-extensions": "^0.1.1", "gh-pages": "^6.1.1", - "mocha": "^10.7.0", - "prettier": "^3.3.3", + "mocha": "^10.7.3", "process": "^0.11.10", "shx": "^0.3.4", "start-server-and-test": "^2.0.5", "ts-node": "^10.9.2", - "typedoc": "^0.26.5", + "typedoc": "^0.26.6", "typescript": "^5.5.4" } } diff --git a/token/js/src/actions/amountToUiAmount.ts b/token/js/src/actions/amountToUiAmount.ts index e39d13ae260..667454ab8a8 100644 --- a/token/js/src/actions/amountToUiAmount.ts +++ b/token/js/src/actions/amountToUiAmount.ts @@ -19,7 +19,7 @@ export async function amountToUiAmount( payer: Signer, mint: PublicKey, amount: number | bigint, - programId = TOKEN_PROGRAM_ID + programId = TOKEN_PROGRAM_ID, ): Promise { const transaction = new Transaction().add(createAmountToUiAmountInstruction(mint, amount, programId)); const { returnData, err } = (await connection.simulateTransaction(transaction, [payer], false)).value; diff --git a/token/js/src/actions/approve.ts b/token/js/src/actions/approve.ts index 3c16310ee91..1e8f091e8d3 100644 --- a/token/js/src/actions/approve.ts +++ b/token/js/src/actions/approve.ts @@ -28,12 +28,12 @@ export async function approve( amount: number | bigint, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_PROGRAM_ID + programId = TOKEN_PROGRAM_ID, ): Promise { const [ownerPublicKey, signers] = getSigners(owner, multiSigners); const transaction = new Transaction().add( - createApproveInstruction(account, delegate, ownerPublicKey, amount, multiSigners, programId) + createApproveInstruction(account, delegate, ownerPublicKey, amount, multiSigners, programId), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); diff --git a/token/js/src/actions/approveChecked.ts b/token/js/src/actions/approveChecked.ts index b32c0b796ff..68d0d651db7 100644 --- a/token/js/src/actions/approveChecked.ts +++ b/token/js/src/actions/approveChecked.ts @@ -33,7 +33,7 @@ export async function approveChecked( decimals: number, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_PROGRAM_ID + programId = TOKEN_PROGRAM_ID, ): Promise { const [ownerPublicKey, signers] = getSigners(owner, multiSigners); @@ -46,8 +46,8 @@ export async function approveChecked( amount, decimals, multiSigners, - programId - ) + programId, + ), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); diff --git a/token/js/src/actions/burn.ts b/token/js/src/actions/burn.ts index ea54f591151..8bc23303c03 100644 --- a/token/js/src/actions/burn.ts +++ b/token/js/src/actions/burn.ts @@ -28,12 +28,12 @@ export async function burn( amount: number | bigint, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_PROGRAM_ID + programId = TOKEN_PROGRAM_ID, ): Promise { const [ownerPublicKey, signers] = getSigners(owner, multiSigners); const transaction = new Transaction().add( - createBurnInstruction(account, mint, ownerPublicKey, amount, multiSigners, programId) + createBurnInstruction(account, mint, ownerPublicKey, amount, multiSigners, programId), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); diff --git a/token/js/src/actions/burnChecked.ts b/token/js/src/actions/burnChecked.ts index 5ca9b7285bb..b29c8d635c2 100644 --- a/token/js/src/actions/burnChecked.ts +++ b/token/js/src/actions/burnChecked.ts @@ -30,12 +30,12 @@ export async function burnChecked( decimals: number, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_PROGRAM_ID + programId = TOKEN_PROGRAM_ID, ): Promise { const [ownerPublicKey, signers] = getSigners(owner, multiSigners); const transaction = new Transaction().add( - createBurnCheckedInstruction(account, mint, ownerPublicKey, amount, decimals, multiSigners, programId) + createBurnCheckedInstruction(account, mint, ownerPublicKey, amount, decimals, multiSigners, programId), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); diff --git a/token/js/src/actions/closeAccount.ts b/token/js/src/actions/closeAccount.ts index fe99dc4ec72..c6b84654f3f 100644 --- a/token/js/src/actions/closeAccount.ts +++ b/token/js/src/actions/closeAccount.ts @@ -26,12 +26,12 @@ export async function closeAccount( authority: Signer | PublicKey, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_PROGRAM_ID + programId = TOKEN_PROGRAM_ID, ): Promise { const [authorityPublicKey, signers] = getSigners(authority, multiSigners); const transaction = new Transaction().add( - createCloseAccountInstruction(account, destination, authorityPublicKey, multiSigners, programId) + createCloseAccountInstruction(account, destination, authorityPublicKey, multiSigners, programId), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); diff --git a/token/js/src/actions/createAccount.ts b/token/js/src/actions/createAccount.ts index 9a680a73006..8b2641cf059 100644 --- a/token/js/src/actions/createAccount.ts +++ b/token/js/src/actions/createAccount.ts @@ -26,7 +26,7 @@ export async function createAccount( owner: PublicKey, keypair?: Keypair, confirmOptions?: ConfirmOptions, - programId = TOKEN_PROGRAM_ID + programId = TOKEN_PROGRAM_ID, ): Promise { // If a keypair isn't provided, create the associated token account and return its address if (!keypair) return await createAssociatedTokenAccount(connection, payer, mint, owner, confirmOptions, programId); @@ -44,7 +44,7 @@ export async function createAccount( lamports, programId, }), - createInitializeAccountInstruction(keypair.publicKey, mint, owner, programId) + createInitializeAccountInstruction(keypair.publicKey, mint, owner, programId), ); await sendAndConfirmTransaction(connection, transaction, [payer, keypair], confirmOptions); diff --git a/token/js/src/actions/createAssociatedTokenAccount.ts b/token/js/src/actions/createAssociatedTokenAccount.ts index 42199a0cd24..2d84acaed62 100644 --- a/token/js/src/actions/createAssociatedTokenAccount.ts +++ b/token/js/src/actions/createAssociatedTokenAccount.ts @@ -14,6 +14,7 @@ import { getAssociatedTokenAddressSync } from '../state/mint.js'; * @param confirmOptions Options for confirming the transaction * @param programId SPL Token program account * @param associatedTokenProgramId SPL Associated Token program account + * @param allowOwnerOffCurve Allow the owner account to be a PDA (Program Derived Address) * * @return Address of the new associated token account */ @@ -24,9 +25,16 @@ export async function createAssociatedTokenAccount( owner: PublicKey, confirmOptions?: ConfirmOptions, programId = TOKEN_PROGRAM_ID, - associatedTokenProgramId = ASSOCIATED_TOKEN_PROGRAM_ID + associatedTokenProgramId = ASSOCIATED_TOKEN_PROGRAM_ID, + allowOwnerOffCurve = false, ): Promise { - const associatedToken = getAssociatedTokenAddressSync(mint, owner, false, programId, associatedTokenProgramId); + const associatedToken = getAssociatedTokenAddressSync( + mint, + owner, + allowOwnerOffCurve, + programId, + associatedTokenProgramId, + ); const transaction = new Transaction().add( createAssociatedTokenAccountInstruction( @@ -35,8 +43,8 @@ export async function createAssociatedTokenAccount( owner, mint, programId, - associatedTokenProgramId - ) + associatedTokenProgramId, + ), ); await sendAndConfirmTransaction(connection, transaction, [payer], confirmOptions); diff --git a/token/js/src/actions/createAssociatedTokenAccountIdempotent.ts b/token/js/src/actions/createAssociatedTokenAccountIdempotent.ts index 25329bef677..b0913de3db5 100644 --- a/token/js/src/actions/createAssociatedTokenAccountIdempotent.ts +++ b/token/js/src/actions/createAssociatedTokenAccountIdempotent.ts @@ -25,7 +25,7 @@ export async function createAssociatedTokenAccountIdempotent( owner: PublicKey, confirmOptions?: ConfirmOptions, programId = TOKEN_PROGRAM_ID, - associatedTokenProgramId = ASSOCIATED_TOKEN_PROGRAM_ID + associatedTokenProgramId = ASSOCIATED_TOKEN_PROGRAM_ID, ): Promise { const associatedToken = getAssociatedTokenAddressSync(mint, owner, false, programId, associatedTokenProgramId); @@ -36,8 +36,8 @@ export async function createAssociatedTokenAccountIdempotent( owner, mint, programId, - associatedTokenProgramId - ) + associatedTokenProgramId, + ), ); await sendAndConfirmTransaction(connection, transaction, [payer], confirmOptions); diff --git a/token/js/src/actions/createMint.ts b/token/js/src/actions/createMint.ts index 39818e17cf4..4a35e9171de 100644 --- a/token/js/src/actions/createMint.ts +++ b/token/js/src/actions/createMint.ts @@ -26,7 +26,7 @@ export async function createMint( decimals: number, keypair = Keypair.generate(), confirmOptions?: ConfirmOptions, - programId = TOKEN_PROGRAM_ID + programId = TOKEN_PROGRAM_ID, ): Promise { const lamports = await getMinimumBalanceForRentExemptMint(connection); @@ -38,7 +38,7 @@ export async function createMint( lamports, programId, }), - createInitializeMint2Instruction(keypair.publicKey, decimals, mintAuthority, freezeAuthority, programId) + createInitializeMint2Instruction(keypair.publicKey, decimals, mintAuthority, freezeAuthority, programId), ); await sendAndConfirmTransaction(connection, transaction, [payer, keypair], confirmOptions); diff --git a/token/js/src/actions/createMultisig.ts b/token/js/src/actions/createMultisig.ts index f7e0ac45d32..3717ee51d4b 100644 --- a/token/js/src/actions/createMultisig.ts +++ b/token/js/src/actions/createMultisig.ts @@ -24,7 +24,7 @@ export async function createMultisig( m: number, keypair = Keypair.generate(), confirmOptions?: ConfirmOptions, - programId = TOKEN_PROGRAM_ID + programId = TOKEN_PROGRAM_ID, ): Promise { const lamports = await getMinimumBalanceForRentExemptMultisig(connection); @@ -36,7 +36,7 @@ export async function createMultisig( lamports, programId, }), - createInitializeMultisigInstruction(keypair.publicKey, signers, m, programId) + createInitializeMultisigInstruction(keypair.publicKey, signers, m, programId), ); await sendAndConfirmTransaction(connection, transaction, [payer, keypair], confirmOptions); diff --git a/token/js/src/actions/createNativeMint.ts b/token/js/src/actions/createNativeMint.ts index 95262693229..ec4119cd952 100644 --- a/token/js/src/actions/createNativeMint.ts +++ b/token/js/src/actions/createNativeMint.ts @@ -17,10 +17,10 @@ export async function createNativeMint( payer: Signer, confirmOptions?: ConfirmOptions, nativeMint = NATIVE_MINT_2022, - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): Promise { const transaction = new Transaction().add( - createCreateNativeMintInstruction(payer.publicKey, nativeMint, programId) + createCreateNativeMintInstruction(payer.publicKey, nativeMint, programId), ); await sendAndConfirmTransaction(connection, transaction, [payer], confirmOptions); } diff --git a/token/js/src/actions/createWrappedNativeAccount.ts b/token/js/src/actions/createWrappedNativeAccount.ts index cc4188d0656..9064fb9ad48 100644 --- a/token/js/src/actions/createWrappedNativeAccount.ts +++ b/token/js/src/actions/createWrappedNativeAccount.ts @@ -29,7 +29,7 @@ export async function createWrappedNativeAccount( keypair?: Keypair, confirmOptions?: ConfirmOptions, programId = TOKEN_PROGRAM_ID, - nativeMint = NATIVE_MINT + nativeMint = NATIVE_MINT, ): Promise { // If the amount provided is explicitly 0 or NaN, just create the account without funding it if (!amount) return await createAccount(connection, payer, nativeMint, owner, keypair, confirmOptions, programId); @@ -41,7 +41,7 @@ export async function createWrappedNativeAccount( owner, false, programId, - ASSOCIATED_TOKEN_PROGRAM_ID + ASSOCIATED_TOKEN_PROGRAM_ID, ); const transaction = new Transaction().add( @@ -51,14 +51,14 @@ export async function createWrappedNativeAccount( owner, nativeMint, programId, - ASSOCIATED_TOKEN_PROGRAM_ID + ASSOCIATED_TOKEN_PROGRAM_ID, ), SystemProgram.transfer({ fromPubkey: payer.publicKey, toPubkey: associatedToken, lamports: amount, }), - createSyncNativeInstruction(associatedToken, programId) + createSyncNativeInstruction(associatedToken, programId), ); await sendAndConfirmTransaction(connection, transaction, [payer], confirmOptions); @@ -82,7 +82,7 @@ export async function createWrappedNativeAccount( toPubkey: keypair.publicKey, lamports: amount, }), - createInitializeAccountInstruction(keypair.publicKey, nativeMint, owner, programId) + createInitializeAccountInstruction(keypair.publicKey, nativeMint, owner, programId), ); await sendAndConfirmTransaction(connection, transaction, [payer, keypair], confirmOptions); diff --git a/token/js/src/actions/freezeAccount.ts b/token/js/src/actions/freezeAccount.ts index 1ef51fec49e..9ec7dae424d 100644 --- a/token/js/src/actions/freezeAccount.ts +++ b/token/js/src/actions/freezeAccount.ts @@ -26,12 +26,12 @@ export async function freezeAccount( authority: Signer | PublicKey, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_PROGRAM_ID + programId = TOKEN_PROGRAM_ID, ): Promise { const [authorityPublicKey, signers] = getSigners(authority, multiSigners); const transaction = new Transaction().add( - createFreezeAccountInstruction(account, mint, authorityPublicKey, multiSigners, programId) + createFreezeAccountInstruction(account, mint, authorityPublicKey, multiSigners, programId), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); diff --git a/token/js/src/actions/getOrCreateAssociatedTokenAccount.ts b/token/js/src/actions/getOrCreateAssociatedTokenAccount.ts index caf21388f0d..0bdaaf17cd0 100644 --- a/token/js/src/actions/getOrCreateAssociatedTokenAccount.ts +++ b/token/js/src/actions/getOrCreateAssociatedTokenAccount.ts @@ -36,14 +36,14 @@ export async function getOrCreateAssociatedTokenAccount( commitment?: Commitment, confirmOptions?: ConfirmOptions, programId = TOKEN_PROGRAM_ID, - associatedTokenProgramId = ASSOCIATED_TOKEN_PROGRAM_ID + associatedTokenProgramId = ASSOCIATED_TOKEN_PROGRAM_ID, ): Promise { const associatedToken = getAssociatedTokenAddressSync( mint, owner, allowOwnerOffCurve, programId, - associatedTokenProgramId + associatedTokenProgramId, ); // This is the optimal logic, considering TX fee, client-side computation, RPC roundtrips and guaranteed idempotent. @@ -65,8 +65,8 @@ export async function getOrCreateAssociatedTokenAccount( owner, mint, programId, - associatedTokenProgramId - ) + associatedTokenProgramId, + ), ); await sendAndConfirmTransaction(connection, transaction, [payer], confirmOptions); diff --git a/token/js/src/actions/mintTo.ts b/token/js/src/actions/mintTo.ts index 06dc5aff049..0804d63a0fb 100644 --- a/token/js/src/actions/mintTo.ts +++ b/token/js/src/actions/mintTo.ts @@ -28,12 +28,12 @@ export async function mintTo( amount: number | bigint, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_PROGRAM_ID + programId = TOKEN_PROGRAM_ID, ): Promise { const [authorityPublicKey, signers] = getSigners(authority, multiSigners); const transaction = new Transaction().add( - createMintToInstruction(mint, destination, authorityPublicKey, amount, multiSigners, programId) + createMintToInstruction(mint, destination, authorityPublicKey, amount, multiSigners, programId), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); diff --git a/token/js/src/actions/mintToChecked.ts b/token/js/src/actions/mintToChecked.ts index eef7f45cb05..3b988fe4476 100644 --- a/token/js/src/actions/mintToChecked.ts +++ b/token/js/src/actions/mintToChecked.ts @@ -30,12 +30,20 @@ export async function mintToChecked( decimals: number, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_PROGRAM_ID + programId = TOKEN_PROGRAM_ID, ): Promise { const [authorityPublicKey, signers] = getSigners(authority, multiSigners); const transaction = new Transaction().add( - createMintToCheckedInstruction(mint, destination, authorityPublicKey, amount, decimals, multiSigners, programId) + createMintToCheckedInstruction( + mint, + destination, + authorityPublicKey, + amount, + decimals, + multiSigners, + programId, + ), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); diff --git a/token/js/src/actions/recoverNested.ts b/token/js/src/actions/recoverNested.ts index ee1e73b401b..ac8e50f7f8b 100644 --- a/token/js/src/actions/recoverNested.ts +++ b/token/js/src/actions/recoverNested.ts @@ -26,14 +26,14 @@ export async function recoverNested( nestedMint: PublicKey, confirmOptions?: ConfirmOptions, programId = TOKEN_PROGRAM_ID, - associatedTokenProgramId = ASSOCIATED_TOKEN_PROGRAM_ID + associatedTokenProgramId = ASSOCIATED_TOKEN_PROGRAM_ID, ): Promise { const ownerAssociatedToken = getAssociatedTokenAddressSync( mint, owner.publicKey, false, programId, - associatedTokenProgramId + associatedTokenProgramId, ); const destinationAssociatedToken = getAssociatedTokenAddressSync( @@ -41,7 +41,7 @@ export async function recoverNested( owner.publicKey, false, programId, - associatedTokenProgramId + associatedTokenProgramId, ); const nestedAssociatedToken = getAssociatedTokenAddressSync( @@ -49,7 +49,7 @@ export async function recoverNested( ownerAssociatedToken, true, programId, - associatedTokenProgramId + associatedTokenProgramId, ); const transaction = new Transaction().add( @@ -61,8 +61,8 @@ export async function recoverNested( mint, owner.publicKey, programId, - associatedTokenProgramId - ) + associatedTokenProgramId, + ), ); return await sendAndConfirmTransaction(connection, transaction, [payer, owner], confirmOptions); diff --git a/token/js/src/actions/revoke.ts b/token/js/src/actions/revoke.ts index 1bc3e6dfeb1..e89a222bd33 100644 --- a/token/js/src/actions/revoke.ts +++ b/token/js/src/actions/revoke.ts @@ -24,12 +24,12 @@ export async function revoke( owner: Signer | PublicKey, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_PROGRAM_ID + programId = TOKEN_PROGRAM_ID, ): Promise { const [ownerPublicKey, signers] = getSigners(owner, multiSigners); const transaction = new Transaction().add( - createRevokeInstruction(account, ownerPublicKey, multiSigners, programId) + createRevokeInstruction(account, ownerPublicKey, multiSigners, programId), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); diff --git a/token/js/src/actions/setAuthority.ts b/token/js/src/actions/setAuthority.ts index f233fb35c37..340b36dc427 100644 --- a/token/js/src/actions/setAuthority.ts +++ b/token/js/src/actions/setAuthority.ts @@ -29,7 +29,7 @@ export async function setAuthority( newAuthority: PublicKey | null, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_PROGRAM_ID + programId = TOKEN_PROGRAM_ID, ): Promise { const [currentAuthorityPublicKey, signers] = getSigners(currentAuthority, multiSigners); @@ -40,8 +40,8 @@ export async function setAuthority( authorityType, newAuthority, multiSigners, - programId - ) + programId, + ), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); diff --git a/token/js/src/actions/syncNative.ts b/token/js/src/actions/syncNative.ts index 32bcc886932..7e6a8909324 100644 --- a/token/js/src/actions/syncNative.ts +++ b/token/js/src/actions/syncNative.ts @@ -19,7 +19,7 @@ export async function syncNative( payer: Signer, account: PublicKey, confirmOptions?: ConfirmOptions, - programId = TOKEN_PROGRAM_ID + programId = TOKEN_PROGRAM_ID, ): Promise { const transaction = new Transaction().add(createSyncNativeInstruction(account, programId)); diff --git a/token/js/src/actions/thawAccount.ts b/token/js/src/actions/thawAccount.ts index 6491b159b16..0c2d1c27586 100644 --- a/token/js/src/actions/thawAccount.ts +++ b/token/js/src/actions/thawAccount.ts @@ -26,12 +26,12 @@ export async function thawAccount( authority: Signer | PublicKey, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_PROGRAM_ID + programId = TOKEN_PROGRAM_ID, ): Promise { const [authorityPublicKey, signers] = getSigners(authority, multiSigners); const transaction = new Transaction().add( - createThawAccountInstruction(account, mint, authorityPublicKey, multiSigners, programId) + createThawAccountInstruction(account, mint, authorityPublicKey, multiSigners, programId), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); diff --git a/token/js/src/actions/transfer.ts b/token/js/src/actions/transfer.ts index c69229838ec..adb1f94b842 100644 --- a/token/js/src/actions/transfer.ts +++ b/token/js/src/actions/transfer.ts @@ -28,12 +28,12 @@ export async function transfer( amount: number | bigint, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_PROGRAM_ID + programId = TOKEN_PROGRAM_ID, ): Promise { const [ownerPublicKey, signers] = getSigners(owner, multiSigners); const transaction = new Transaction().add( - createTransferInstruction(source, destination, ownerPublicKey, amount, multiSigners, programId) + createTransferInstruction(source, destination, ownerPublicKey, amount, multiSigners, programId), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); diff --git a/token/js/src/actions/transferChecked.ts b/token/js/src/actions/transferChecked.ts index cd4c6d6c982..356cbdbca32 100644 --- a/token/js/src/actions/transferChecked.ts +++ b/token/js/src/actions/transferChecked.ts @@ -32,7 +32,7 @@ export async function transferChecked( decimals: number, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_PROGRAM_ID + programId = TOKEN_PROGRAM_ID, ): Promise { const [ownerPublicKey, signers] = getSigners(owner, multiSigners); @@ -45,8 +45,8 @@ export async function transferChecked( amount, decimals, multiSigners, - programId - ) + programId, + ), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); diff --git a/token/js/src/actions/uiAmountToAmount.ts b/token/js/src/actions/uiAmountToAmount.ts index 7987b69b64c..e6bdaf61f6a 100644 --- a/token/js/src/actions/uiAmountToAmount.ts +++ b/token/js/src/actions/uiAmountToAmount.ts @@ -20,7 +20,7 @@ export async function uiAmountToAmount( payer: Signer, mint: PublicKey, amount: string, - programId = TOKEN_PROGRAM_ID + programId = TOKEN_PROGRAM_ID, ): Promise { const transaction = new Transaction().add(createUiAmountToAmountInstruction(mint, amount, programId)); const { returnData, err } = (await connection.simulateTransaction(transaction, [payer], false)).value; diff --git a/token/js/src/extensions/cpiGuard/actions.ts b/token/js/src/extensions/cpiGuard/actions.ts index 05393b30ab6..905d4b8a794 100644 --- a/token/js/src/extensions/cpiGuard/actions.ts +++ b/token/js/src/extensions/cpiGuard/actions.ts @@ -24,12 +24,12 @@ export async function enableCpiGuard( owner: Signer | PublicKey, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): Promise { const [ownerPublicKey, signers] = getSigners(owner, multiSigners); const transaction = new Transaction().add( - createEnableCpiGuardInstruction(account, ownerPublicKey, signers, programId) + createEnableCpiGuardInstruction(account, ownerPublicKey, signers, programId), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); @@ -55,12 +55,12 @@ export async function disableCpiGuard( owner: Signer | PublicKey, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): Promise { const [ownerPublicKey, signers] = getSigners(owner, multiSigners); const transaction = new Transaction().add( - createDisableCpiGuardInstruction(account, ownerPublicKey, signers, programId) + createDisableCpiGuardInstruction(account, ownerPublicKey, signers, programId), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); diff --git a/token/js/src/extensions/cpiGuard/instructions.ts b/token/js/src/extensions/cpiGuard/instructions.ts index 57754ef4f04..04e5d65f9db 100644 --- a/token/js/src/extensions/cpiGuard/instructions.ts +++ b/token/js/src/extensions/cpiGuard/instructions.ts @@ -34,7 +34,7 @@ export function createEnableCpiGuardInstruction( account: PublicKey, authority: PublicKey, multiSigners: (Signer | PublicKey)[] = [], - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): TransactionInstruction { return createCpiGuardInstruction(CpiGuardInstruction.Enable, account, authority, multiSigners, programId); } @@ -53,7 +53,7 @@ export function createDisableCpiGuardInstruction( account: PublicKey, authority: PublicKey, multiSigners: (Signer | PublicKey)[] = [], - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): TransactionInstruction { return createCpiGuardInstruction(CpiGuardInstruction.Disable, account, authority, multiSigners, programId); } @@ -63,7 +63,7 @@ function createCpiGuardInstruction( account: PublicKey, authority: PublicKey, multiSigners: (Signer | PublicKey)[], - programId: PublicKey + programId: PublicKey, ): TransactionInstruction { if (!programSupportsExtensions(programId)) { throw new TokenUnsupportedInstructionError(); @@ -76,7 +76,7 @@ function createCpiGuardInstruction( instruction: TokenInstruction.CpiGuardExtension, cpiGuardInstruction, }, - data + data, ); return new TransactionInstruction({ keys, programId, data }); diff --git a/token/js/src/extensions/defaultAccountState/actions.ts b/token/js/src/extensions/defaultAccountState/actions.ts index 03727a635d7..09a3ac16f2b 100644 --- a/token/js/src/extensions/defaultAccountState/actions.ts +++ b/token/js/src/extensions/defaultAccountState/actions.ts @@ -26,7 +26,7 @@ export async function initializeDefaultAccountState( mint: PublicKey, state: AccountState, confirmOptions?: ConfirmOptions, - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): Promise { const transaction = new Transaction().add(createInitializeDefaultAccountStateInstruction(mint, state, programId)); @@ -55,12 +55,12 @@ export async function updateDefaultAccountState( freezeAuthority: Signer | PublicKey, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): Promise { const [freezeAuthorityPublicKey, signers] = getSigners(freezeAuthority, multiSigners); const transaction = new Transaction().add( - createUpdateDefaultAccountStateInstruction(mint, state, freezeAuthorityPublicKey, signers, programId) + createUpdateDefaultAccountStateInstruction(mint, state, freezeAuthorityPublicKey, signers, programId), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); diff --git a/token/js/src/extensions/defaultAccountState/instructions.ts b/token/js/src/extensions/defaultAccountState/instructions.ts index 518507ec79d..02b16d06a59 100644 --- a/token/js/src/extensions/defaultAccountState/instructions.ts +++ b/token/js/src/extensions/defaultAccountState/instructions.ts @@ -38,7 +38,7 @@ export const defaultAccountStateInstructionData = struct i === extensionTypes.indexOf(element)) - .map((element) => addTypeAndLengthToLen(getTypeLen(element))) + .map(element => addTypeAndLengthToLen(getTypeLen(element))) .reduce((a, b) => a + b, 0) + Object.entries(variableLengthExtensions) .map(([extension, len]) => { @@ -245,7 +245,7 @@ function getLen( export function getMintLen( extensionTypes: ExtensionType[], - variableLengthExtensions: { [E in ExtensionType]?: number } = {} + variableLengthExtensions: { [E in ExtensionType]?: number } = {}, ): number { return getLen(extensionTypes, MINT_SIZE, variableLengthExtensions); } @@ -292,7 +292,7 @@ export function getNewAccountLenForExtensionLen( address: PublicKey, extensionType: ExtensionType, extensionLen: number, - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): number { const mint = unpackMint(address, info, programId); const extensionData = getExtensionData(extensionType, mint.tlvData); diff --git a/token/js/src/extensions/groupMemberPointer/instructions.ts b/token/js/src/extensions/groupMemberPointer/instructions.ts index fa389cff0b6..21e13a62f06 100644 --- a/token/js/src/extensions/groupMemberPointer/instructions.ts +++ b/token/js/src/extensions/groupMemberPointer/instructions.ts @@ -39,7 +39,7 @@ export function createInitializeGroupMemberPointerInstruction( mint: PublicKey, authority: PublicKey | null, memberAddress: PublicKey | null, - programId: PublicKey = TOKEN_2022_PROGRAM_ID + programId: PublicKey = TOKEN_2022_PROGRAM_ID, ): TransactionInstruction { if (!programSupportsExtensions(programId)) { throw new TokenUnsupportedInstructionError(); @@ -54,7 +54,7 @@ export function createInitializeGroupMemberPointerInstruction( authority: authority ?? PublicKey.default, memberAddress: memberAddress ?? PublicKey.default, }, - data + data, ); return new TransactionInstruction({ keys, programId, data: data }); @@ -76,7 +76,7 @@ export function createUpdateGroupMemberPointerInstruction( authority: PublicKey, memberAddress: PublicKey | null, multiSigners: (Signer | PublicKey)[] = [], - programId: PublicKey = TOKEN_2022_PROGRAM_ID + programId: PublicKey = TOKEN_2022_PROGRAM_ID, ): TransactionInstruction { if (!programSupportsExtensions(programId)) { throw new TokenUnsupportedInstructionError(); @@ -91,7 +91,7 @@ export function createUpdateGroupMemberPointerInstruction( groupMemberPointerInstruction: GroupMemberPointerInstruction.Update, memberAddress: memberAddress ?? PublicKey.default, }, - data + data, ); return new TransactionInstruction({ keys, programId, data: data }); diff --git a/token/js/src/extensions/groupPointer/instructions.ts b/token/js/src/extensions/groupPointer/instructions.ts index e48b7d8ae6e..7ebbf9ae448 100644 --- a/token/js/src/extensions/groupPointer/instructions.ts +++ b/token/js/src/extensions/groupPointer/instructions.ts @@ -39,7 +39,7 @@ export function createInitializeGroupPointerInstruction( mint: PublicKey, authority: PublicKey | null, groupAddress: PublicKey | null, - programId: PublicKey = TOKEN_2022_PROGRAM_ID + programId: PublicKey = TOKEN_2022_PROGRAM_ID, ): TransactionInstruction { if (!programSupportsExtensions(programId)) { throw new TokenUnsupportedInstructionError(); @@ -54,7 +54,7 @@ export function createInitializeGroupPointerInstruction( authority: authority ?? PublicKey.default, groupAddress: groupAddress ?? PublicKey.default, }, - data + data, ); return new TransactionInstruction({ keys, programId, data: data }); @@ -76,7 +76,7 @@ export function createUpdateGroupPointerInstruction( authority: PublicKey, groupAddress: PublicKey | null, multiSigners: (Signer | PublicKey)[] = [], - programId: PublicKey = TOKEN_2022_PROGRAM_ID + programId: PublicKey = TOKEN_2022_PROGRAM_ID, ): TransactionInstruction { if (!programSupportsExtensions(programId)) { throw new TokenUnsupportedInstructionError(); @@ -91,7 +91,7 @@ export function createUpdateGroupPointerInstruction( groupPointerInstruction: GroupPointerInstruction.Update, groupAddress: groupAddress ?? PublicKey.default, }, - data + data, ); return new TransactionInstruction({ keys, programId, data: data }); diff --git a/token/js/src/extensions/interestBearingMint/actions.ts b/token/js/src/extensions/interestBearingMint/actions.ts index a60b2f9677b..02ad8917454 100644 --- a/token/js/src/extensions/interestBearingMint/actions.ts +++ b/token/js/src/extensions/interestBearingMint/actions.ts @@ -35,7 +35,7 @@ export async function createInterestBearingMint( decimals: number, keypair = Keypair.generate(), confirmOptions?: ConfirmOptions, - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): Promise { const mintLen = getMintLen([ExtensionType.InterestBearingConfig]); const lamports = await connection.getMinimumBalanceForRentExemption(mintLen); @@ -48,7 +48,7 @@ export async function createInterestBearingMint( programId, }), createInitializeInterestBearingMintInstruction(keypair.publicKey, rateAuthority, rate, programId), - createInitializeMintInstruction(keypair.publicKey, decimals, mintAuthority, freezeAuthority, programId) + createInitializeMintInstruction(keypair.publicKey, decimals, mintAuthority, freezeAuthority, programId), ); await sendAndConfirmTransaction(connection, transaction, [payer, keypair], confirmOptions); return keypair.publicKey; @@ -76,11 +76,11 @@ export async function updateRateInterestBearingMint( rate: number, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): Promise { const [rateAuthorityPublicKey, signers] = getSigners(rateAuthority, multiSigners); const transaction = new Transaction().add( - createUpdateRateInterestBearingMintInstruction(mint, rateAuthorityPublicKey, rate, signers, programId) + createUpdateRateInterestBearingMintInstruction(mint, rateAuthorityPublicKey, rate, signers, programId), ); return await sendAndConfirmTransaction(connection, transaction, [payer, rateAuthority, ...signers], confirmOptions); diff --git a/token/js/src/extensions/interestBearingMint/instructions.ts b/token/js/src/extensions/interestBearingMint/instructions.ts index 34b3eef52db..e5de48eb79b 100644 --- a/token/js/src/extensions/interestBearingMint/instructions.ts +++ b/token/js/src/extensions/interestBearingMint/instructions.ts @@ -52,7 +52,7 @@ export function createInitializeInterestBearingMintInstruction( mint: PublicKey, rateAuthority: PublicKey, rate: number, - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ) { const keys = [{ pubkey: mint, isSigner: false, isWritable: true }]; const data = Buffer.alloc(interestBearingMintInitializeInstructionData.span); @@ -63,7 +63,7 @@ export function createInitializeInterestBearingMintInstruction( rateAuthority, rate, }, - data + data, ); return new TransactionInstruction({ keys, programId, data }); } @@ -84,7 +84,7 @@ export function createUpdateRateInterestBearingMintInstruction( rateAuthority: PublicKey, rate: number, multiSigners: (Signer | PublicKey)[] = [], - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ) { const keys = addSigners( [ @@ -92,7 +92,7 @@ export function createUpdateRateInterestBearingMintInstruction( { pubkey: rateAuthority, isSigner: !multiSigners.length, isWritable: false }, ], rateAuthority, - multiSigners + multiSigners, ); const data = Buffer.alloc(interestBearingMintUpdateRateInstructionData.span); interestBearingMintUpdateRateInstructionData.encode( @@ -101,7 +101,7 @@ export function createUpdateRateInterestBearingMintInstruction( interestBearingMintInstruction: InterestBearingMintInstruction.UpdateRate, rate, }, - data + data, ); return new TransactionInstruction({ keys, programId, data }); } diff --git a/token/js/src/extensions/memoTransfer/actions.ts b/token/js/src/extensions/memoTransfer/actions.ts index 7a2dceff98b..77288b95561 100644 --- a/token/js/src/extensions/memoTransfer/actions.ts +++ b/token/js/src/extensions/memoTransfer/actions.ts @@ -27,12 +27,12 @@ export async function enableRequiredMemoTransfers( owner: Signer | PublicKey, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): Promise { const [ownerPublicKey, signers] = getSigners(owner, multiSigners); const transaction = new Transaction().add( - createEnableRequiredMemoTransfersInstruction(account, ownerPublicKey, signers, programId) + createEnableRequiredMemoTransfersInstruction(account, ownerPublicKey, signers, programId), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); @@ -58,12 +58,12 @@ export async function disableRequiredMemoTransfers( owner: Signer | PublicKey, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): Promise { const [ownerPublicKey, signers] = getSigners(owner, multiSigners); const transaction = new Transaction().add( - createDisableRequiredMemoTransfersInstruction(account, ownerPublicKey, signers, programId) + createDisableRequiredMemoTransfersInstruction(account, ownerPublicKey, signers, programId), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); diff --git a/token/js/src/extensions/memoTransfer/instructions.ts b/token/js/src/extensions/memoTransfer/instructions.ts index 689b5761540..65ff26528ad 100644 --- a/token/js/src/extensions/memoTransfer/instructions.ts +++ b/token/js/src/extensions/memoTransfer/instructions.ts @@ -37,7 +37,7 @@ export function createEnableRequiredMemoTransfersInstruction( account: PublicKey, authority: PublicKey, multiSigners: (Signer | PublicKey)[] = [], - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): TransactionInstruction { return createMemoTransferInstruction(MemoTransferInstruction.Enable, account, authority, multiSigners, programId); } @@ -56,7 +56,7 @@ export function createDisableRequiredMemoTransfersInstruction( account: PublicKey, authority: PublicKey, multiSigners: (Signer | PublicKey)[] = [], - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): TransactionInstruction { return createMemoTransferInstruction(MemoTransferInstruction.Disable, account, authority, multiSigners, programId); } @@ -66,7 +66,7 @@ function createMemoTransferInstruction( account: PublicKey, authority: PublicKey, multiSigners: (Signer | PublicKey)[], - programId: PublicKey + programId: PublicKey, ): TransactionInstruction { if (!programSupportsExtensions(programId)) { throw new TokenUnsupportedInstructionError(); @@ -79,7 +79,7 @@ function createMemoTransferInstruction( instruction: TokenInstruction.MemoTransferExtension, memoTransferInstruction, }, - data + data, ); return new TransactionInstruction({ keys, programId, data }); diff --git a/token/js/src/extensions/metadataPointer/instructions.ts b/token/js/src/extensions/metadataPointer/instructions.ts index 2d4b83541b4..fe26b32b8f3 100644 --- a/token/js/src/extensions/metadataPointer/instructions.ts +++ b/token/js/src/extensions/metadataPointer/instructions.ts @@ -39,7 +39,7 @@ export function createInitializeMetadataPointerInstruction( mint: PublicKey, authority: PublicKey | null, metadataAddress: PublicKey | null, - programId: PublicKey + programId: PublicKey, ): TransactionInstruction { if (!programSupportsExtensions(programId)) { throw new TokenUnsupportedInstructionError(); @@ -54,7 +54,7 @@ export function createInitializeMetadataPointerInstruction( authority: authority ?? PublicKey.default, metadataAddress: metadataAddress ?? PublicKey.default, }, - data + data, ); return new TransactionInstruction({ keys, programId, data: data }); @@ -76,7 +76,7 @@ export function createUpdateMetadataPointerInstruction( authority: PublicKey, metadataAddress: PublicKey | null, multiSigners: (Signer | PublicKey)[] = [], - programId: PublicKey = TOKEN_2022_PROGRAM_ID + programId: PublicKey = TOKEN_2022_PROGRAM_ID, ): TransactionInstruction { if (!programSupportsExtensions(programId)) { throw new TokenUnsupportedInstructionError(); @@ -91,7 +91,7 @@ export function createUpdateMetadataPointerInstruction( metadataPointerInstruction: MetadataPointerInstruction.Update, metadataAddress: metadataAddress ?? PublicKey.default, }, - data + data, ); return new TransactionInstruction({ keys, programId, data: data }); diff --git a/token/js/src/extensions/tokenGroup/actions.ts b/token/js/src/extensions/tokenGroup/actions.ts index 553a7aa4404..318c49fe8fe 100644 --- a/token/js/src/extensions/tokenGroup/actions.ts +++ b/token/js/src/extensions/tokenGroup/actions.ts @@ -35,10 +35,10 @@ export async function tokenGroupInitializeGroup( mint: PublicKey, mintAuthority: PublicKey | Signer, updateAuthority: PublicKey | null, - maxSize: number, + maxSize: bigint, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): Promise { const [mintAuthorityPublicKey, signers] = getSigners(mintAuthority, multiSigners); @@ -50,7 +50,7 @@ export async function tokenGroupInitializeGroup( mintAuthority: mintAuthorityPublicKey, updateAuthority, maxSize, - }) + }), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); @@ -79,10 +79,10 @@ export async function tokenGroupInitializeGroupWithRentTransfer( mint: PublicKey, mintAuthority: PublicKey | Signer, updateAuthority: PublicKey | null, - maxSize: number, + maxSize: bigint, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): Promise { const [mintAuthorityPublicKey, signers] = getSigners(mintAuthority, multiSigners); @@ -101,7 +101,7 @@ export async function tokenGroupInitializeGroupWithRentTransfer( mintAuthority: mintAuthorityPublicKey, updateAuthority, maxSize, - }) + }), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); @@ -126,10 +126,10 @@ export async function tokenGroupUpdateGroupMaxSize( payer: Signer, mint: PublicKey, updateAuthority: PublicKey | Signer, - maxSize: number, + maxSize: bigint, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): Promise { const [updateAuthorityPublicKey, signers] = getSigners(updateAuthority, multiSigners); @@ -139,7 +139,7 @@ export async function tokenGroupUpdateGroupMaxSize( group: mint, updateAuthority: updateAuthorityPublicKey, maxSize, - }) + }), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); @@ -167,7 +167,7 @@ export async function tokenGroupUpdateGroupAuthority( newAuthority: PublicKey | null, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): Promise { const [updateAuthorityPublicKey, signers] = getSigners(updateAuthority, multiSigners); @@ -177,7 +177,7 @@ export async function tokenGroupUpdateGroupAuthority( group: mint, currentAuthority: updateAuthorityPublicKey, newAuthority, - }) + }), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); @@ -210,7 +210,7 @@ export async function tokenGroupMemberInitialize( groupUpdateAuthority: PublicKey, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): Promise { const [mintAuthorityPublicKey, signers] = getSigners(mintAuthority, multiSigners); @@ -222,7 +222,7 @@ export async function tokenGroupMemberInitialize( memberMintAuthority: mintAuthorityPublicKey, group, groupUpdateAuthority, - }) + }), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); @@ -255,7 +255,7 @@ export async function tokenGroupMemberInitializeWithRentTransfer( groupUpdateAuthority: PublicKey, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): Promise { const [mintAuthorityPublicKey, signers] = getSigners(mintAuthority, multiSigners); @@ -274,7 +274,7 @@ export async function tokenGroupMemberInitializeWithRentTransfer( memberMintAuthority: mintAuthorityPublicKey, group, groupUpdateAuthority, - }) + }), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); diff --git a/token/js/src/extensions/tokenMetadata/actions.ts b/token/js/src/extensions/tokenMetadata/actions.ts index f37868638e6..c8838877998 100644 --- a/token/js/src/extensions/tokenMetadata/actions.ts +++ b/token/js/src/extensions/tokenMetadata/actions.ts @@ -21,7 +21,7 @@ async function getAdditionalRentForNewMetadata( connection: Connection, address: PublicKey, tokenMetadata: TokenMetadata, - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): Promise { const info = await connection.getAccountInfo(address); if (!info) { @@ -34,7 +34,7 @@ async function getAdditionalRentForNewMetadata( address, ExtensionType.TokenMetadata, extensionLen, - programId + programId, ); if (newAccountLen <= info.data.length) { @@ -51,7 +51,7 @@ async function getAdditionalRentForUpdatedMetadata( address: PublicKey, field: string | Field, value: string, - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): Promise { const info = await connection.getAccountInfo(address); if (!info) { @@ -72,7 +72,7 @@ async function getAdditionalRentForUpdatedMetadata( address, ExtensionType.TokenMetadata, extensionLen, - programId + programId, ); if (newAccountLen <= info.data.length) { @@ -112,7 +112,7 @@ export async function tokenMetadataInitialize( uri: string, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): Promise { const [mintAuthorityPublicKey, signers] = getSigners(mintAuthority, multiSigners); @@ -126,7 +126,7 @@ export async function tokenMetadataInitialize( name, symbol, uri, - }) + }), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); @@ -161,7 +161,7 @@ export async function tokenMetadataInitializeWithRentTransfer( uri: string, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): Promise { const [mintAuthorityPublicKey, signers] = getSigners(mintAuthority, multiSigners); @@ -178,7 +178,7 @@ export async function tokenMetadataInitializeWithRentTransfer( uri, additionalMetadata: [], }, - programId + programId, ); if (lamports > 0) { @@ -195,7 +195,7 @@ export async function tokenMetadataInitializeWithRentTransfer( name, symbol, uri, - }) + }), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); @@ -229,7 +229,7 @@ export async function tokenMetadataUpdateField( value: string, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): Promise { const [updateAuthorityPublicKey, signers] = getSigners(updateAuthority, multiSigners); @@ -240,7 +240,7 @@ export async function tokenMetadataUpdateField( updateAuthority: updateAuthorityPublicKey, field, value, - }) + }), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); @@ -275,7 +275,7 @@ export async function tokenMetadataUpdateFieldWithRentTransfer( value: string, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): Promise { const [updateAuthorityPublicKey, signers] = getSigners(updateAuthority, multiSigners); @@ -294,7 +294,7 @@ export async function tokenMetadataUpdateFieldWithRentTransfer( updateAuthority: updateAuthorityPublicKey, field, value, - }) + }), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); @@ -326,7 +326,7 @@ export async function tokenMetadataRemoveKey( idempotent: boolean, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): Promise { const [updateAuthorityPublicKey, signers] = getSigners(updateAuthority, multiSigners); @@ -337,7 +337,7 @@ export async function tokenMetadataRemoveKey( updateAuthority: updateAuthorityPublicKey, key, idempotent, - }) + }), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); @@ -365,7 +365,7 @@ export async function tokenMetadataUpdateAuthority( newAuthority: PublicKey | null, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): Promise { const [updateAuthorityPublicKey, signers] = getSigners(updateAuthority, multiSigners); @@ -375,7 +375,7 @@ export async function tokenMetadataUpdateAuthority( metadata: mint, oldAuthority: updateAuthorityPublicKey, newAuthority, - }) + }), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); diff --git a/token/js/src/extensions/tokenMetadata/state.ts b/token/js/src/extensions/tokenMetadata/state.ts index d124dcc7509..95480ca04a2 100644 --- a/token/js/src/extensions/tokenMetadata/state.ts +++ b/token/js/src/extensions/tokenMetadata/state.ts @@ -41,7 +41,7 @@ export function updateTokenMetadata(current: TokenMetadata, key: Field | string, // Avoid mutating input, make a shallow copy const additionalMetadata = [...current.additionalMetadata]; - const i = current.additionalMetadata.findIndex((x) => x[0] === field); + const i = current.additionalMetadata.findIndex(x => x[0] === field); if (i === -1) { // Key was not found, add it @@ -71,7 +71,7 @@ export async function getTokenMetadata( connection: Connection, address: PublicKey, commitment?: Commitment, - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): Promise { const mintInfo = await getMint(connection, address, commitment, programId); const data = getExtensionData(ExtensionType.TokenMetadata, mintInfo.tlvData); diff --git a/token/js/src/extensions/transferFee/actions.ts b/token/js/src/extensions/transferFee/actions.ts index aaa67577c90..fe0ea10042c 100644 --- a/token/js/src/extensions/transferFee/actions.ts +++ b/token/js/src/extensions/transferFee/actions.ts @@ -39,7 +39,7 @@ export async function transferCheckedWithFee( fee: bigint, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): Promise { const [ownerPublicKey, signers] = getSigners(owner, multiSigners); @@ -53,8 +53,8 @@ export async function transferCheckedWithFee( decimals, fee, multiSigners, - programId - ) + programId, + ), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); @@ -82,12 +82,12 @@ export async function withdrawWithheldTokensFromMint( authority: Signer | PublicKey, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): Promise { const [authorityPublicKey, signers] = getSigners(authority, multiSigners); const transaction = new Transaction().add( - createWithdrawWithheldTokensFromMintInstruction(mint, destination, authorityPublicKey, signers, programId) + createWithdrawWithheldTokensFromMintInstruction(mint, destination, authorityPublicKey, signers, programId), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); @@ -117,7 +117,7 @@ export async function withdrawWithheldTokensFromAccounts( multiSigners: Signer[], sources: PublicKey[], confirmOptions?: ConfirmOptions, - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): Promise { const [authorityPublicKey, signers] = getSigners(authority, multiSigners); @@ -128,8 +128,8 @@ export async function withdrawWithheldTokensFromAccounts( authorityPublicKey, signers, sources, - programId - ) + programId, + ), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); @@ -153,7 +153,7 @@ export async function harvestWithheldTokensToMint( mint: PublicKey, sources: PublicKey[], confirmOptions?: ConfirmOptions, - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): Promise { const transaction = new Transaction().add(createHarvestWithheldTokensToMintInstruction(mint, sources, programId)); @@ -184,7 +184,7 @@ export async function setTransferFee( transferFeeBasisPoints: number, maximumFee: bigint, confirmOptions?: ConfirmOptions, - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): Promise { const [authorityPublicKey, signers] = getSigners(authority, multiSigners); @@ -195,8 +195,8 @@ export async function setTransferFee( signers, transferFeeBasisPoints, maximumFee, - programId - ) + programId, + ), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); diff --git a/token/js/src/extensions/transferFee/instructions.ts b/token/js/src/extensions/transferFee/instructions.ts index f2a74d85a7c..2db3f791798 100644 --- a/token/js/src/extensions/transferFee/instructions.ts +++ b/token/js/src/extensions/transferFee/instructions.ts @@ -63,7 +63,7 @@ export function createInitializeTransferFeeConfigInstruction( withdrawWithheldAuthority: PublicKey | null, transferFeeBasisPoints: number, maximumFee: bigint, - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): TransactionInstruction { if (!programSupportsExtensions(programId)) { throw new TokenUnsupportedInstructionError(); @@ -80,7 +80,7 @@ export function createInitializeTransferFeeConfigInstruction( transferFeeBasisPoints: transferFeeBasisPoints, maximumFee: maximumFee, }, - data + data, ); return new TransactionInstruction({ keys, programId, data }); @@ -112,7 +112,7 @@ export interface DecodedInitializeTransferFeeConfigInstruction { */ export function decodeInitializeTransferFeeConfigInstruction( instruction: TransactionInstruction, - programId: PublicKey + programId: PublicKey, ): DecodedInitializeTransferFeeConfigInstruction { if (!instruction.programId.equals(programId)) throw new TokenInvalidInstructionProgramError(); if (instruction.data.length !== initializeTransferFeeConfigInstructionData.span) @@ -232,7 +232,7 @@ export function createTransferCheckedWithFeeInstruction( decimals: number, fee: bigint, multiSigners: (Signer | PublicKey)[] = [], - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): TransactionInstruction { if (!programSupportsExtensions(programId)) { throw new TokenUnsupportedInstructionError(); @@ -246,7 +246,7 @@ export function createTransferCheckedWithFeeInstruction( decimals, fee, }, - data + data, ); const keys = addSigners( [ @@ -255,7 +255,7 @@ export function createTransferCheckedWithFeeInstruction( { pubkey: destination, isSigner: false, isWritable: true }, ], authority, - multiSigners + multiSigners, ); return new TransactionInstruction({ keys, programId, data }); } @@ -289,7 +289,7 @@ export interface DecodedTransferCheckedWithFeeInstruction { */ export function decodeTransferCheckedWithFeeInstruction( instruction: TransactionInstruction, - programId: PublicKey + programId: PublicKey, ): DecodedTransferCheckedWithFeeInstruction { if (!instruction.programId.equals(programId)) throw new TokenInvalidInstructionProgramError(); if (instruction.data.length !== transferCheckedWithFeeInstructionData.span) @@ -399,7 +399,7 @@ export function createWithdrawWithheldTokensFromMintInstruction( destination: PublicKey, authority: PublicKey, signers: (Signer | PublicKey)[] = [], - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): TransactionInstruction { if (!programSupportsExtensions(programId)) { throw new TokenUnsupportedInstructionError(); @@ -410,7 +410,7 @@ export function createWithdrawWithheldTokensFromMintInstruction( instruction: TokenInstruction.TransferFeeExtension, transferFeeInstruction: TransferFeeInstruction.WithdrawWithheldTokensFromMint, }, - data + data, ); const keys = addSigners( [ @@ -418,7 +418,7 @@ export function createWithdrawWithheldTokensFromMintInstruction( { pubkey: destination, isSigner: false, isWritable: true }, ], authority, - signers + signers, ); return new TransactionInstruction({ keys, programId, data }); } @@ -448,7 +448,7 @@ export interface DecodedWithdrawWithheldTokensFromMintInstruction { */ export function decodeWithdrawWithheldTokensFromMintInstruction( instruction: TransactionInstruction, - programId: PublicKey + programId: PublicKey, ): DecodedWithdrawWithheldTokensFromMintInstruction { if (!instruction.programId.equals(programId)) throw new TokenInvalidInstructionProgramError(); if (instruction.data.length !== withdrawWithheldTokensFromMintInstructionData.span) @@ -553,7 +553,7 @@ export function createWithdrawWithheldTokensFromAccountsInstruction( authority: PublicKey, signers: (Signer | PublicKey)[], sources: PublicKey[], - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): TransactionInstruction { if (!programSupportsExtensions(programId)) { throw new TokenUnsupportedInstructionError(); @@ -565,7 +565,7 @@ export function createWithdrawWithheldTokensFromAccountsInstruction( transferFeeInstruction: TransferFeeInstruction.WithdrawWithheldTokensFromAccounts, numTokenAccounts: sources.length, }, - data + data, ); const keys = addSigners( [ @@ -573,7 +573,7 @@ export function createWithdrawWithheldTokensFromAccountsInstruction( { pubkey: destination, isSigner: false, isWritable: true }, ], authority, - signers + signers, ); for (const source of sources) { keys.push({ pubkey: source, isSigner: false, isWritable: true }); @@ -608,7 +608,7 @@ export interface DecodedWithdrawWithheldTokensFromAccountsInstruction { */ export function decodeWithdrawWithheldTokensFromAccountsInstruction( instruction: TransactionInstruction, - programId: PublicKey + programId: PublicKey, ): DecodedWithdrawWithheldTokensFromAccountsInstruction { if (!instruction.programId.equals(programId)) throw new TokenInvalidInstructionProgramError(); if (instruction.data.length !== withdrawWithheldTokensFromAccountsInstructionData.span) @@ -717,7 +717,7 @@ export const harvestWithheldTokensToMintInstructionData = struct { const transaction = new Transaction().add( - createInitializeTransferHookInstruction(mint, authority, transferHookProgramId, programId) + createInitializeTransferHookInstruction(mint, authority, transferHookProgramId, programId), ); return await sendAndConfirmTransaction(connection, transaction, [payer], confirmOptions); @@ -61,12 +61,12 @@ export async function updateTransferHook( authority: Signer | PublicKey, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): Promise { const [authorityPublicKey, signers] = getSigners(authority, multiSigners); const transaction = new Transaction().add( - createUpdateTransferHookInstruction(mint, authorityPublicKey, transferHookProgramId, signers, programId) + createUpdateTransferHookInstruction(mint, authorityPublicKey, transferHookProgramId, signers, programId), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); @@ -100,7 +100,7 @@ export async function transferCheckedWithTransferHook( decimals: number, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_PROGRAM_ID + programId = TOKEN_PROGRAM_ID, ): Promise { const [authorityPublicKey, signers] = getSigners(authority, multiSigners); @@ -115,8 +115,8 @@ export async function transferCheckedWithTransferHook( decimals, signers, confirmOptions?.commitment, - programId - ) + programId, + ), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); @@ -152,7 +152,7 @@ export async function transferCheckedWithFeeAndTransferHook( fee: bigint, multiSigners: Signer[] = [], confirmOptions?: ConfirmOptions, - programId = TOKEN_PROGRAM_ID + programId = TOKEN_PROGRAM_ID, ): Promise { const [authorityPublicKey, signers] = getSigners(authority, multiSigners); @@ -168,8 +168,8 @@ export async function transferCheckedWithFeeAndTransferHook( fee, signers, confirmOptions?.commitment, - programId - ) + programId, + ), ); return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions); diff --git a/token/js/src/extensions/transferHook/instructions.ts b/token/js/src/extensions/transferHook/instructions.ts index ffdf2296b57..ab113386e26 100644 --- a/token/js/src/extensions/transferHook/instructions.ts +++ b/token/js/src/extensions/transferHook/instructions.ts @@ -46,7 +46,7 @@ export function createInitializeTransferHookInstruction( mint: PublicKey, authority: PublicKey, transferHookProgramId: PublicKey, - programId: PublicKey + programId: PublicKey, ): TransactionInstruction { if (!programSupportsExtensions(programId)) { throw new TokenUnsupportedInstructionError(); @@ -61,7 +61,7 @@ export function createInitializeTransferHookInstruction( authority, transferHookProgramId, }, - data + data, ); return new TransactionInstruction({ keys, programId, data }); @@ -97,7 +97,7 @@ export function createUpdateTransferHookInstruction( authority: PublicKey, transferHookProgramId: PublicKey, multiSigners: (Signer | PublicKey)[] = [], - programId = TOKEN_2022_PROGRAM_ID + programId = TOKEN_2022_PROGRAM_ID, ): TransactionInstruction { if (!programSupportsExtensions(programId)) { throw new TokenUnsupportedInstructionError(); @@ -111,7 +111,7 @@ export function createUpdateTransferHookInstruction( transferHookInstruction: TransferHookInstruction.Update, transferHookProgramId, }, - data + data, ); return new TransactionInstruction({ keys, programId, data }); @@ -119,7 +119,7 @@ export function createUpdateTransferHookInstruction( function deEscalateAccountMeta(accountMeta: AccountMeta, accountMetas: AccountMeta[]): AccountMeta { const maybeHighestPrivileges = accountMetas - .filter((x) => x.pubkey.equals(accountMeta.pubkey)) + .filter(x => x.pubkey.equals(accountMeta.pubkey)) .reduce<{ isSigner: boolean; isWritable: boolean } | undefined>((acc, x) => { if (!acc) return { isSigner: x.isSigner, isWritable: x.isWritable }; return { isSigner: acc.isSigner || x.isSigner, isWritable: acc.isWritable || x.isWritable }; @@ -156,9 +156,9 @@ export function createExecuteInstruction( destination: PublicKey, owner: PublicKey, validateStatePubkey: PublicKey, - amount: bigint + amount: bigint, ): TransactionInstruction { - const keys = [source, mint, destination, owner, validateStatePubkey].map((pubkey) => ({ + const keys = [source, mint, destination, owner, validateStatePubkey].map(pubkey => ({ pubkey, isSigner: false, isWritable: false, @@ -195,7 +195,7 @@ export async function addExtraAccountMetasForExecute( destination: PublicKey, owner: PublicKey, amount: number | bigint, - commitment?: Commitment + commitment?: Commitment, ) { const validateStatePubkey = getExtraAccountMetaAddress(mint, programId); const validateStateAccount = await connection.getAccountInfo(validateStatePubkey, commitment); @@ -205,7 +205,7 @@ export async function addExtraAccountMetasForExecute( const validateStateData = getExtraAccountMetas(validateStateAccount); // Check to make sure the provided keys are in the instruction - if (![source, mint, destination, owner].every((key) => instruction.keys.some((meta) => meta.pubkey.equals(key)))) { + if (![source, mint, destination, owner].every(key => instruction.keys.some(meta => meta.pubkey.equals(key)))) { throw new Error('Missing required account in instruction'); } @@ -216,7 +216,7 @@ export async function addExtraAccountMetasForExecute( destination, owner, validateStatePubkey, - BigInt(amount) + BigInt(amount), ); for (const extraAccountMeta of validateStateData) { @@ -227,10 +227,10 @@ export async function addExtraAccountMetasForExecute( extraAccountMeta, executeInstruction.keys, executeInstruction.data, - executeInstruction.programId + executeInstruction.programId, ), - executeInstruction.keys - ) + executeInstruction.keys, + ), ); } @@ -268,7 +268,7 @@ export async function createTransferCheckedWithTransferHookInstruction( decimals: number, multiSigners: (Signer | PublicKey)[] = [], commitment?: Commitment, - programId = TOKEN_PROGRAM_ID + programId = TOKEN_PROGRAM_ID, ) { const instruction = createTransferCheckedInstruction( source, @@ -278,7 +278,7 @@ export async function createTransferCheckedWithTransferHookInstruction( amount, decimals, multiSigners, - programId + programId, ); const mintInfo = await getMint(connection, mint, commitment, programId); @@ -294,7 +294,7 @@ export async function createTransferCheckedWithTransferHookInstruction( destination, owner, amount, - commitment + commitment, ); } @@ -329,7 +329,7 @@ export async function createTransferCheckedWithFeeAndTransferHookInstruction( fee: bigint, multiSigners: (Signer | PublicKey)[] = [], commitment?: Commitment, - programId = TOKEN_PROGRAM_ID + programId = TOKEN_PROGRAM_ID, ) { const instruction = createTransferCheckedWithFeeInstruction( source, @@ -340,7 +340,7 @@ export async function createTransferCheckedWithFeeAndTransferHookInstruction( decimals, fee, multiSigners, - programId + programId, ); const mintInfo = await getMint(connection, mint, commitment, programId); @@ -356,7 +356,7 @@ export async function createTransferCheckedWithFeeAndTransferHookInstruction( destination, owner, amount, - commitment + commitment, ); } diff --git a/token/js/src/extensions/transferHook/seeds.ts b/token/js/src/extensions/transferHook/seeds.ts index 5530b0a4826..230af7208a5 100644 --- a/token/js/src/extensions/transferHook/seeds.ts +++ b/token/js/src/extensions/transferHook/seeds.ts @@ -60,7 +60,7 @@ function unpackSeedAccountKey(seeds: Uint8Array, previousMetas: AccountMeta[]): async function unpackSeedAccountData( seeds: Uint8Array, previousMetas: AccountMeta[], - connection: Connection + connection: Connection, ): Promise { if (seeds.length < 3) { throw new TokenTransferHookInvalidSeed(); @@ -87,7 +87,7 @@ async function unpackFirstSeed( seeds: Uint8Array, previousMetas: AccountMeta[], instructionData: Buffer, - connection: Connection + connection: Connection, ): Promise { const [discriminator, ...rest] = seeds; const remaining = new Uint8Array(rest); @@ -111,7 +111,7 @@ export async function unpackSeeds( seeds: Uint8Array, previousMetas: AccountMeta[], instructionData: Buffer, - connection: Connection + connection: Connection, ): Promise { const unpackedSeeds: Buffer[] = []; let i = 0; diff --git a/token/js/src/extensions/transferHook/state.ts b/token/js/src/extensions/transferHook/state.ts index f436a4213b1..d79604a9b13 100644 --- a/token/js/src/extensions/transferHook/state.ts +++ b/token/js/src/extensions/transferHook/state.ts @@ -111,7 +111,7 @@ export async function resolveExtraAccountMeta( extraMeta: ExtraAccountMeta, previousMetas: AccountMeta[], instructionData: Buffer, - transferHookProgramId: PublicKey + transferHookProgramId: PublicKey, ): Promise { if (extraMeta.discriminator === 0) { return { diff --git a/token/js/src/instructions/amountToUiAmount.ts b/token/js/src/instructions/amountToUiAmount.ts index 6a180ee7fb0..57230e99d43 100644 --- a/token/js/src/instructions/amountToUiAmount.ts +++ b/token/js/src/instructions/amountToUiAmount.ts @@ -35,7 +35,7 @@ export const amountToUiAmountInstructionData = struct([ diff --git a/token/js/src/state/account.ts b/token/js/src/state/account.ts index 361548201d3..b068f472759 100644 --- a/token/js/src/state/account.ts +++ b/token/js/src/state/account.ts @@ -97,7 +97,7 @@ export async function getAccount( connection: Connection, address: PublicKey, commitment?: Commitment, - programId = TOKEN_PROGRAM_ID + programId = TOKEN_PROGRAM_ID, ): Promise { const info = await connection.getAccountInfo(address, commitment); return unpackAccount(address, info, programId); @@ -117,7 +117,7 @@ export async function getMultipleAccounts( connection: Connection, addresses: PublicKey[], commitment?: Commitment, - programId = TOKEN_PROGRAM_ID + programId = TOKEN_PROGRAM_ID, ): Promise { const infos = await connection.getMultipleAccountsInfo(addresses, commitment); return addresses.map((address, i) => unpackAccount(address, infos[i], programId)); @@ -132,7 +132,7 @@ export async function getMultipleAccounts( */ export async function getMinimumBalanceForRentExemptAccount( connection: Connection, - commitment?: Commitment + commitment?: Commitment, ): Promise { return await getMinimumBalanceForRentExemptAccountWithExtensions(connection, [], commitment); } @@ -147,7 +147,7 @@ export async function getMinimumBalanceForRentExemptAccount( export async function getMinimumBalanceForRentExemptAccountWithExtensions( connection: Connection, extensions: ExtensionType[], - commitment?: Commitment + commitment?: Commitment, ): Promise { const accountLen = getAccountLen(extensions); return await connection.getMinimumBalanceForRentExemption(accountLen, commitment); @@ -165,7 +165,7 @@ export async function getMinimumBalanceForRentExemptAccountWithExtensions( export function unpackAccount( address: PublicKey, info: AccountInfo | null, - programId = TOKEN_PROGRAM_ID + programId = TOKEN_PROGRAM_ID, ): Account { if (!info) throw new TokenAccountNotFoundError(); if (!info.owner.equals(programId)) throw new TokenInvalidAccountOwnerError(); diff --git a/token/js/src/state/mint.ts b/token/js/src/state/mint.ts index 69bd98cc3cf..cb09ec317d9 100644 --- a/token/js/src/state/mint.ts +++ b/token/js/src/state/mint.ts @@ -76,7 +76,7 @@ export async function getMint( connection: Connection, address: PublicKey, commitment?: Commitment, - programId = TOKEN_PROGRAM_ID + programId = TOKEN_PROGRAM_ID, ): Promise { const info = await connection.getAccountInfo(address, commitment); return unpackMint(address, info, programId); @@ -125,7 +125,7 @@ export function unpackMint(address: PublicKey, info: AccountInfo | null, */ export async function getMinimumBalanceForRentExemptMint( connection: Connection, - commitment?: Commitment + commitment?: Commitment, ): Promise { return await getMinimumBalanceForRentExemptMintWithExtensions(connection, [], commitment); } @@ -141,7 +141,7 @@ export async function getMinimumBalanceForRentExemptMint( export async function getMinimumBalanceForRentExemptMintWithExtensions( connection: Connection, extensions: ExtensionType[], - commitment?: Commitment + commitment?: Commitment, ): Promise { const mintLen = getMintLen(extensions); return await connection.getMinimumBalanceForRentExemption(mintLen, commitment); @@ -164,13 +164,13 @@ export async function getAssociatedTokenAddress( owner: PublicKey, allowOwnerOffCurve = false, programId = TOKEN_PROGRAM_ID, - associatedTokenProgramId = ASSOCIATED_TOKEN_PROGRAM_ID + associatedTokenProgramId = ASSOCIATED_TOKEN_PROGRAM_ID, ): Promise { if (!allowOwnerOffCurve && !PublicKey.isOnCurve(owner.toBuffer())) throw new TokenOwnerOffCurveError(); const [address] = await PublicKey.findProgramAddress( [owner.toBuffer(), programId.toBuffer(), mint.toBuffer()], - associatedTokenProgramId + associatedTokenProgramId, ); return address; @@ -192,13 +192,13 @@ export function getAssociatedTokenAddressSync( owner: PublicKey, allowOwnerOffCurve = false, programId = TOKEN_PROGRAM_ID, - associatedTokenProgramId = ASSOCIATED_TOKEN_PROGRAM_ID + associatedTokenProgramId = ASSOCIATED_TOKEN_PROGRAM_ID, ): PublicKey { if (!allowOwnerOffCurve && !PublicKey.isOnCurve(owner.toBuffer())) throw new TokenOwnerOffCurveError(); const [address] = PublicKey.findProgramAddressSync( [owner.toBuffer(), programId.toBuffer(), mint.toBuffer()], - associatedTokenProgramId + associatedTokenProgramId, ); return address; diff --git a/token/js/src/state/multisig.ts b/token/js/src/state/multisig.ts index f821d7f9c4c..04734837550 100644 --- a/token/js/src/state/multisig.ts +++ b/token/js/src/state/multisig.ts @@ -66,7 +66,7 @@ export async function getMultisig( connection: Connection, address: PublicKey, commitment?: Commitment, - programId = TOKEN_PROGRAM_ID + programId = TOKEN_PROGRAM_ID, ): Promise { const info = await connection.getAccountInfo(address, commitment); return unpackMultisig(address, info, programId); @@ -84,7 +84,7 @@ export async function getMultisig( export function unpackMultisig( address: PublicKey, info: AccountInfo | null, - programId = TOKEN_PROGRAM_ID + programId = TOKEN_PROGRAM_ID, ): Multisig { if (!info) throw new TokenAccountNotFoundError(); if (!info.owner.equals(programId)) throw new TokenInvalidAccountOwnerError(); @@ -104,7 +104,7 @@ export function unpackMultisig( */ export async function getMinimumBalanceForRentExemptMultisig( connection: Connection, - commitment?: Commitment + commitment?: Commitment, ): Promise { return await connection.getMinimumBalanceForRentExemption(MULTISIG_SIZE, commitment); } diff --git a/token/js/test/e2e-2022/closeMint.test.ts b/token/js/test/e2e-2022/closeMint.test.ts index 0fabd6506fd..0f144dc6aeb 100644 --- a/token/js/test/e2e-2022/closeMint.test.ts +++ b/token/js/test/e2e-2022/closeMint.test.ts @@ -51,7 +51,7 @@ describe('closeMint', () => { programId: TEST_PROGRAM_ID, }), createInitializeMintCloseAuthorityInstruction(mint, closeAuthority.publicKey, TEST_PROGRAM_ID), - createInitializeMintInstruction(mint, TEST_TOKEN_DECIMALS, mintAuthority.publicKey, null, TEST_PROGRAM_ID) + createInitializeMintInstruction(mint, TEST_TOKEN_DECIMALS, mintAuthority.publicKey, null, TEST_PROGRAM_ID), ); await sendAndConfirmTransaction(connection, transaction, [payer, mintKeypair], undefined); @@ -62,14 +62,15 @@ describe('closeMint', () => { account = await createAccount(connection, payer, mint, owner.publicKey, undefined, undefined, TEST_PROGRAM_ID); const amount = BigInt(1000); await mintTo(connection, payer, mint, account, mintAuthority, amount, [], undefined, TEST_PROGRAM_ID); - expect(closeAccount(connection, payer, mint, destination, closeAuthority, [], undefined, TEST_PROGRAM_ID)).to.be - .rejected; + expect( + closeAccount(connection, payer, mint, destination, closeAuthority, [], undefined, TEST_PROGRAM_ID), + ).to.be.rejectedWith(Error); }); it('works', async () => { destination = Keypair.generate().publicKey; const accountInfo = await connection.getAccountInfo(mint); let rentExemptAmount; - expect(accountInfo).to.not.be.null; + expect(accountInfo).to.not.equal(null); if (accountInfo !== null) { rentExemptAmount = accountInfo.lamports; } @@ -77,10 +78,10 @@ describe('closeMint', () => { await closeAccount(connection, payer, mint, destination, closeAuthority, [], undefined, TEST_PROGRAM_ID); const closedInfo = await connection.getAccountInfo(mint); - expect(closedInfo).to.be.null; + expect(closedInfo).to.equal(null); const destinationInfo = await connection.getAccountInfo(destination); - expect(destinationInfo).to.not.be.null; + expect(destinationInfo).to.not.equal(null); if (destinationInfo !== null) { expect(destinationInfo.lamports).to.eql(rentExemptAmount); } @@ -95,11 +96,11 @@ describe('closeMint', () => { null, [], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const mintInfo = await getMint(connection, mint, undefined, TEST_PROGRAM_ID); const mintCloseAuthority = getMintCloseAuthority(mintInfo); - expect(mintCloseAuthority).to.not.be.null; + expect(mintCloseAuthority).to.not.equal(null); if (mintCloseAuthority !== null) { expect(mintCloseAuthority.closeAuthority).to.eql(PublicKey.default); } diff --git a/token/js/test/e2e-2022/cpiGuard.test.ts b/token/js/test/e2e-2022/cpiGuard.test.ts index 3a0e8be3d80..aee7aa0f24d 100644 --- a/token/js/test/e2e-2022/cpiGuard.test.ts +++ b/token/js/test/e2e-2022/cpiGuard.test.ts @@ -47,7 +47,7 @@ describe('cpiGuard', () => { TEST_TOKEN_DECIMALS, mintKeypair, undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const transaction = new Transaction().add( @@ -58,7 +58,7 @@ describe('cpiGuard', () => { lamports, programId: TEST_PROGRAM_ID, }), - createInitializeAccountInstruction(account, mint, owner.publicKey, TEST_PROGRAM_ID) + createInitializeAccountInstruction(account, mint, owner.publicKey, TEST_PROGRAM_ID), ); await sendAndConfirmTransaction(connection, transaction, [payer, accountKeypair], undefined); @@ -68,32 +68,32 @@ describe('cpiGuard', () => { let accountInfo = await getAccount(connection, account, undefined, TEST_PROGRAM_ID); let cpiGuard = getCpiGuard(accountInfo); - expect(cpiGuard).to.be.null; + expect(cpiGuard).to.equal(null); let transaction = new Transaction().add( - createEnableCpiGuardInstruction(account, owner.publicKey, [], TEST_PROGRAM_ID) + createEnableCpiGuardInstruction(account, owner.publicKey, [], TEST_PROGRAM_ID), ); await sendAndConfirmTransaction(connection, transaction, [payer, owner], undefined); accountInfo = await getAccount(connection, account, undefined, TEST_PROGRAM_ID); cpiGuard = getCpiGuard(accountInfo); - expect(cpiGuard).to.not.be.null; + expect(cpiGuard).to.not.equal(null); if (cpiGuard !== null) { - expect(cpiGuard.lockCpi).to.be.true; + expect(cpiGuard.lockCpi).to.equal(true); } transaction = new Transaction().add( - createDisableCpiGuardInstruction(account, owner.publicKey, [], TEST_PROGRAM_ID) + createDisableCpiGuardInstruction(account, owner.publicKey, [], TEST_PROGRAM_ID), ); await sendAndConfirmTransaction(connection, transaction, [payer, owner], undefined); accountInfo = await getAccount(connection, account, undefined, TEST_PROGRAM_ID); cpiGuard = getCpiGuard(accountInfo); - expect(cpiGuard).to.not.be.null; + expect(cpiGuard).to.not.equal(null); if (cpiGuard !== null) { - expect(cpiGuard.lockCpi).to.be.false; + expect(cpiGuard.lockCpi).to.equal(false); } }); @@ -101,16 +101,16 @@ describe('cpiGuard', () => { let accountInfo = await getAccount(connection, account, undefined, TEST_PROGRAM_ID); let cpiGuard = getCpiGuard(accountInfo); - expect(cpiGuard).to.be.null; + expect(cpiGuard).to.equal(null); await enableCpiGuard(connection, payer, account, owner, [], undefined, TEST_PROGRAM_ID); accountInfo = await getAccount(connection, account, undefined, TEST_PROGRAM_ID); cpiGuard = getCpiGuard(accountInfo); - expect(cpiGuard).to.not.be.null; + expect(cpiGuard).to.not.equal(null); if (cpiGuard !== null) { - expect(cpiGuard.lockCpi).to.be.true; + expect(cpiGuard.lockCpi).to.equal(true); } await disableCpiGuard(connection, payer, account, owner, [], undefined, TEST_PROGRAM_ID); @@ -118,9 +118,9 @@ describe('cpiGuard', () => { accountInfo = await getAccount(connection, account, undefined, TEST_PROGRAM_ID); cpiGuard = getCpiGuard(accountInfo); - expect(cpiGuard).to.not.be.null; + expect(cpiGuard).to.not.equal(null); if (cpiGuard !== null) { - expect(cpiGuard.lockCpi).to.be.false; + expect(cpiGuard.lockCpi).to.equal(false); } }); }); diff --git a/token/js/test/e2e-2022/defaultAccountState.test.ts b/token/js/test/e2e-2022/defaultAccountState.test.ts index f0118adbc8c..898bb2e6a7c 100644 --- a/token/js/test/e2e-2022/defaultAccountState.test.ts +++ b/token/js/test/e2e-2022/defaultAccountState.test.ts @@ -50,8 +50,8 @@ describe('defaultAccountState', () => { TEST_TOKEN_DECIMALS, mintAuthority.publicKey, freezeAuthority.publicKey, - TEST_PROGRAM_ID - ) + TEST_PROGRAM_ID, + ), ); await sendAndConfirmTransaction(connection, transaction, [payer, mintKeypair], undefined); @@ -59,7 +59,7 @@ describe('defaultAccountState', () => { it('defaults to frozen', async () => { const mintInfo = await getMint(connection, mint, undefined, TEST_PROGRAM_ID); const defaultAccountState = getDefaultAccountState(mintInfo); - expect(defaultAccountState).to.not.be.null; + expect(defaultAccountState).to.not.equal(null); if (defaultAccountState !== null) { expect(defaultAccountState.state).to.eql(TEST_STATE); } @@ -71,11 +71,11 @@ describe('defaultAccountState', () => { owner.publicKey, undefined, undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const accountInfo = await getAccount(connection, account, undefined, TEST_PROGRAM_ID); - expect(accountInfo.isFrozen).to.be.true; - expect(accountInfo.isInitialized).to.be.true; + expect(accountInfo.isFrozen).to.equal(true); + expect(accountInfo.isInitialized).to.equal(true); }); it('defaults to initialized after update', async () => { await updateDefaultAccountState( @@ -86,7 +86,7 @@ describe('defaultAccountState', () => { freezeAuthority, [], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const owner = Keypair.generate(); const account = await createAccount( @@ -96,10 +96,10 @@ describe('defaultAccountState', () => { owner.publicKey, undefined, undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const accountInfo = await getAccount(connection, account, undefined, TEST_PROGRAM_ID); - expect(accountInfo.isFrozen).to.be.false; - expect(accountInfo.isInitialized).to.be.true; + expect(accountInfo.isFrozen).to.equal(false); + expect(accountInfo.isInitialized).to.equal(true); }); }); diff --git a/token/js/test/e2e-2022/groupMemberPointer.test.ts b/token/js/test/e2e-2022/groupMemberPointer.test.ts index 7111787e115..8546a4869f7 100644 --- a/token/js/test/e2e-2022/groupMemberPointer.test.ts +++ b/token/js/test/e2e-2022/groupMemberPointer.test.ts @@ -51,15 +51,15 @@ describe('GroupMember pointer', () => { mint.publicKey, mintAuthority.publicKey, memberAddress, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ), createInitializeMintInstruction( mint.publicKey, TEST_TOKEN_DECIMALS, mintAuthority.publicKey, null, - TEST_PROGRAM_ID - ) + TEST_PROGRAM_ID, + ), ); await sendAndConfirmTransaction(connection, transaction, [payer, mint], undefined); @@ -83,8 +83,8 @@ describe('GroupMember pointer', () => { mintAuthority.publicKey, newGroupMemberAddress, undefined, - TEST_PROGRAM_ID - ) + TEST_PROGRAM_ID, + ), ); await sendAndConfirmTransaction(connection, transaction, [payer, mintAuthority], undefined); @@ -106,8 +106,8 @@ describe('GroupMember pointer', () => { AuthorityType.GroupMemberPointer, newAuthority, [], - TEST_PROGRAM_ID - ) + TEST_PROGRAM_ID, + ), ); await sendAndConfirmTransaction(connection, transaction, [payer, mintAuthority], undefined); @@ -128,8 +128,8 @@ describe('GroupMember pointer', () => { AuthorityType.GroupMemberPointer, null, [], - TEST_PROGRAM_ID - ) + TEST_PROGRAM_ID, + ), ); await sendAndConfirmTransaction(connection, transaction, [payer, mintAuthority], undefined); diff --git a/token/js/test/e2e-2022/groupPointer.test.ts b/token/js/test/e2e-2022/groupPointer.test.ts index 5375031d4fa..1855c032ef8 100644 --- a/token/js/test/e2e-2022/groupPointer.test.ts +++ b/token/js/test/e2e-2022/groupPointer.test.ts @@ -51,15 +51,15 @@ describe('Group pointer', () => { mint.publicKey, mintAuthority.publicKey, groupAddress, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ), createInitializeMintInstruction( mint.publicKey, TEST_TOKEN_DECIMALS, mintAuthority.publicKey, null, - TEST_PROGRAM_ID - ) + TEST_PROGRAM_ID, + ), ); await sendAndConfirmTransaction(connection, transaction, [payer, mint], undefined); @@ -83,8 +83,8 @@ describe('Group pointer', () => { mintAuthority.publicKey, newGroupAddress, undefined, - TEST_PROGRAM_ID - ) + TEST_PROGRAM_ID, + ), ); await sendAndConfirmTransaction(connection, transaction, [payer, mintAuthority], undefined); @@ -106,8 +106,8 @@ describe('Group pointer', () => { AuthorityType.GroupPointer, newAuthority, [], - TEST_PROGRAM_ID - ) + TEST_PROGRAM_ID, + ), ); await sendAndConfirmTransaction(connection, transaction, [payer, mintAuthority], undefined); @@ -128,8 +128,8 @@ describe('Group pointer', () => { AuthorityType.GroupPointer, null, [], - TEST_PROGRAM_ID - ) + TEST_PROGRAM_ID, + ), ); await sendAndConfirmTransaction(connection, transaction, [payer, mintAuthority], undefined); diff --git a/token/js/test/e2e-2022/immutableOwner.test.ts b/token/js/test/e2e-2022/immutableOwner.test.ts index f45c924444c..43088a2e144 100644 --- a/token/js/test/e2e-2022/immutableOwner.test.ts +++ b/token/js/test/e2e-2022/immutableOwner.test.ts @@ -39,7 +39,7 @@ describe('immutableOwner', () => { TEST_TOKEN_DECIMALS, mintKeypair, undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); owner = Keypair.generate(); const accountLen = getAccountLen(EXTENSIONS); @@ -55,7 +55,7 @@ describe('immutableOwner', () => { programId: TEST_PROGRAM_ID, }), createInitializeImmutableOwnerInstruction(account, TEST_PROGRAM_ID), - createInitializeAccountInstruction(account, mint, owner.publicKey, TEST_PROGRAM_ID) + createInitializeAccountInstruction(account, mint, owner.publicKey, TEST_PROGRAM_ID), ); await sendAndConfirmTransaction(connection, transaction, [payer, accountKeypair], undefined); }); @@ -71,8 +71,8 @@ describe('immutableOwner', () => { owner.publicKey, [], undefined, - TEST_PROGRAM_ID - ) - ).to.be.rejected; + TEST_PROGRAM_ID, + ), + ).to.be.rejectedWith(Error); }); }); diff --git a/token/js/test/e2e-2022/interestBearingMint.test.ts b/token/js/test/e2e-2022/interestBearingMint.test.ts index 94e9349dceb..3561abe42d4 100644 --- a/token/js/test/e2e-2022/interestBearingMint.test.ts +++ b/token/js/test/e2e-2022/interestBearingMint.test.ts @@ -46,11 +46,11 @@ describe('interestBearingMint', () => { TEST_TOKEN_DECIMALS, mintKeypair, undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const mintInfo = await getMint(connection, mint, undefined, TEST_PROGRAM_ID); const interestBearingMintConfigState = getInterestBearingMintConfigState(mintInfo); - expect(interestBearingMintConfigState).to.not.be.null; + expect(interestBearingMintConfigState).to.not.equal(null); if (interestBearingMintConfigState !== null) { expect(interestBearingMintConfigState.rateAuthority).to.eql(rateAuthority.publicKey); expect(interestBearingMintConfigState.preUpdateAverageRate).to.eql(TEST_RATE); @@ -67,12 +67,12 @@ describe('interestBearingMint', () => { TEST_UPDATE_RATE, [], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const mintInfoUpdatedRate = await getMint(connection, mint, undefined, TEST_PROGRAM_ID); const updatedRateConfigState = getInterestBearingMintConfigState(mintInfoUpdatedRate); - expect(updatedRateConfigState).to.not.be.null; + expect(updatedRateConfigState).to.not.equal(null); if (updatedRateConfigState !== null) { expect(updatedRateConfigState.rateAuthority).to.eql(rateAuthority.publicKey); expect(updatedRateConfigState.currentRate).to.eql(TEST_UPDATE_RATE); @@ -91,11 +91,11 @@ describe('interestBearingMint', () => { null, [], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const mintInfo = await getMint(connection, mint, undefined, TEST_PROGRAM_ID); const rateConfigState = getInterestBearingMintConfigState(mintInfo); - expect(rateConfigState).to.not.be.null; + expect(rateConfigState).to.not.equal(null); if (rateConfigState !== null) { expect(rateConfigState.rateAuthority).to.eql(PublicKey.default); } diff --git a/token/js/test/e2e-2022/memoTransfer.test.ts b/token/js/test/e2e-2022/memoTransfer.test.ts index 2c78c2d08a1..e7200ebe36a 100644 --- a/token/js/test/e2e-2022/memoTransfer.test.ts +++ b/token/js/test/e2e-2022/memoTransfer.test.ts @@ -49,7 +49,7 @@ describe('memoTransfer', () => { TEST_TOKEN_DECIMALS, mintKeypair, undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); source = await createAccount( @@ -59,7 +59,7 @@ describe('memoTransfer', () => { owner.publicKey, undefined, // uses ATA by default undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const destinationKeypair = Keypair.generate(); @@ -76,7 +76,7 @@ describe('memoTransfer', () => { programId: TEST_PROGRAM_ID, }), createInitializeAccountInstruction(destination, mint, owner.publicKey, TEST_PROGRAM_ID), - createEnableRequiredMemoTransfersInstruction(destination, owner.publicKey, [], TEST_PROGRAM_ID) + createEnableRequiredMemoTransfersInstruction(destination, owner.publicKey, [], TEST_PROGRAM_ID), ); await sendAndConfirmTransaction(connection, transaction, [payer, owner, destinationKeypair], undefined); @@ -89,30 +89,32 @@ describe('memoTransfer', () => { TRANSFER_AMOUNT * 10, [], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); }); it('fails without memo when enabled', async () => { const accountInfo = await getAccount(connection, destination, undefined, TEST_PROGRAM_ID); const memoTransfer = getMemoTransfer(accountInfo); - expect(memoTransfer).to.not.be.null; + expect(memoTransfer).to.not.equal(null); if (memoTransfer !== null) { - expect(memoTransfer.requireIncomingTransferMemos).to.be.true; + expect(memoTransfer.requireIncomingTransferMemos).to.equal(true); } - expect(transfer(connection, payer, source, destination, owner, TRANSFER_AMOUNT, [], undefined, TEST_PROGRAM_ID)) - .to.be.rejected; + expect( + transfer(connection, payer, source, destination, owner, TRANSFER_AMOUNT, [], undefined, TEST_PROGRAM_ID), + ).to.be.rejectedWith(Error); }); it('works without memo when disabled', async () => { await disableRequiredMemoTransfers(connection, payer, destination, owner, [], undefined, TEST_PROGRAM_ID); await transfer(connection, payer, source, destination, owner, TRANSFER_AMOUNT, [], undefined, TEST_PROGRAM_ID); await enableRequiredMemoTransfers(connection, payer, destination, owner, [], undefined, TEST_PROGRAM_ID); - expect(transfer(connection, payer, source, destination, owner, TRANSFER_AMOUNT, [], undefined, TEST_PROGRAM_ID)) - .to.be.rejected; + expect( + transfer(connection, payer, source, destination, owner, TRANSFER_AMOUNT, [], undefined, TEST_PROGRAM_ID), + ).to.be.rejectedWith(Error); }); it('works with memo when enabled', async () => { const transaction = new Transaction().add( createMemoInstruction('transfer with a memo', [payer.publicKey, owner.publicKey]), - createTransferInstruction(source, destination, owner.publicKey, TRANSFER_AMOUNT, [], TEST_PROGRAM_ID) + createTransferInstruction(source, destination, owner.publicKey, TRANSFER_AMOUNT, [], TEST_PROGRAM_ID), ); await sendAndConfirmTransaction(connection, transaction, [payer, owner], { preflightCommitment: 'confirmed', diff --git a/token/js/test/e2e-2022/metadataPointer.test.ts b/token/js/test/e2e-2022/metadataPointer.test.ts index 18eeeefa97c..4fbdb6de3cb 100644 --- a/token/js/test/e2e-2022/metadataPointer.test.ts +++ b/token/js/test/e2e-2022/metadataPointer.test.ts @@ -49,15 +49,15 @@ describe('Metadata pointer', () => { mint.publicKey, mintAuthority.publicKey, metadataAddress, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ), createInitializeMintInstruction( mint.publicKey, TEST_TOKEN_DECIMALS, mintAuthority.publicKey, null, - TEST_PROGRAM_ID - ) + TEST_PROGRAM_ID, + ), ); await sendAndConfirmTransaction(connection, transaction, [payer, mint], undefined); @@ -81,8 +81,8 @@ describe('Metadata pointer', () => { mintAuthority.publicKey, newMetadataAddress, undefined, - TEST_PROGRAM_ID - ) + TEST_PROGRAM_ID, + ), ); await sendAndConfirmTransaction(connection, transaction, [payer, mintAuthority], undefined); diff --git a/token/js/test/e2e-2022/nonTransferableMint.test.ts b/token/js/test/e2e-2022/nonTransferableMint.test.ts index 2d0698e28d3..1261b6cef29 100644 --- a/token/js/test/e2e-2022/nonTransferableMint.test.ts +++ b/token/js/test/e2e-2022/nonTransferableMint.test.ts @@ -46,7 +46,7 @@ describe('nonTransferable', () => { programId: TEST_PROGRAM_ID, }), createInitializeNonTransferableMintInstruction(mint, TEST_PROGRAM_ID), - createInitializeMintInstruction(mint, TEST_TOKEN_DECIMALS, mintAuthority.publicKey, null, TEST_PROGRAM_ID) + createInitializeMintInstruction(mint, TEST_TOKEN_DECIMALS, mintAuthority.publicKey, null, TEST_PROGRAM_ID), ); await sendAndConfirmTransaction(connection, transaction, [payer, mintKeypair], undefined); @@ -54,7 +54,7 @@ describe('nonTransferable', () => { it('fails transfer', async () => { const mintInfo = await getMint(connection, mint, undefined, TEST_PROGRAM_ID); const nonTransferable = getNonTransferable(mintInfo); - expect(nonTransferable).to.not.be.null; + expect(nonTransferable).to.not.equal(null); const owner = Keypair.generate(); const accountLen = getAccountLen([ExtensionType.ImmutableOwner, ExtensionType.NonTransferableAccount]); @@ -71,7 +71,7 @@ describe('nonTransferable', () => { programId: TEST_PROGRAM_ID, }), createInitializeImmutableOwnerInstruction(source, TEST_PROGRAM_ID), - createInitializeAccountInstruction(source, mint, owner.publicKey, TEST_PROGRAM_ID) + createInitializeAccountInstruction(source, mint, owner.publicKey, TEST_PROGRAM_ID), ); await sendAndConfirmTransaction(connection, transaction, [payer, sourceKeypair], undefined); @@ -86,14 +86,15 @@ describe('nonTransferable', () => { programId: TEST_PROGRAM_ID, }), createInitializeImmutableOwnerInstruction(destination, TEST_PROGRAM_ID), - createInitializeAccountInstruction(destination, mint, owner.publicKey, TEST_PROGRAM_ID) + createInitializeAccountInstruction(destination, mint, owner.publicKey, TEST_PROGRAM_ID), ); await sendAndConfirmTransaction(connection, transaction, [payer, destinationKeypair], undefined); const amount = BigInt(1000); await mintTo(connection, payer, mint, source, mintAuthority, amount, [], undefined, TEST_PROGRAM_ID); - expect(transfer(connection, payer, source, destination, owner, amount, [], undefined, TEST_PROGRAM_ID)).to.be - .rejected; + expect( + transfer(connection, payer, source, destination, owner, amount, [], undefined, TEST_PROGRAM_ID), + ).to.be.rejectedWith(Error); }); }); diff --git a/token/js/test/e2e-2022/permanentDelegate.test.ts b/token/js/test/e2e-2022/permanentDelegate.test.ts index b6ac28eb6e1..29e69cd3a55 100644 --- a/token/js/test/e2e-2022/permanentDelegate.test.ts +++ b/token/js/test/e2e-2022/permanentDelegate.test.ts @@ -48,7 +48,7 @@ describe('permanentDelegate', () => { programId: TEST_PROGRAM_ID, }), createInitializePermanentDelegateInstruction(mint, permanentDelegate.publicKey, TEST_PROGRAM_ID), - createInitializeMintInstruction(mint, TEST_TOKEN_DECIMALS, mintAuthority.publicKey, null, TEST_PROGRAM_ID) + createInitializeMintInstruction(mint, TEST_TOKEN_DECIMALS, mintAuthority.publicKey, null, TEST_PROGRAM_ID), ); await sendAndConfirmTransaction(connection, transaction, [payer, mintKeypair], undefined); @@ -59,7 +59,7 @@ describe('permanentDelegate', () => { await mintTo(connection, payer, mint, account, mintAuthority, 5, [], undefined, TEST_PROGRAM_ID); await burn(connection, payer, account, mint, permanentDelegate, 2, undefined, undefined, TEST_PROGRAM_ID); const info = await connection.getTokenAccountBalance(account); - expect(info).to.not.be.null; + expect(info).to.not.equal(null); if (info !== null) { expect(info.value.uiAmount).to.eql(3); } @@ -74,7 +74,7 @@ describe('permanentDelegate', () => { owner2.publicKey, undefined, undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); account = await createAccount(connection, payer, mint, owner1.publicKey, undefined, undefined, TEST_PROGRAM_ID); await mintTo(connection, payer, mint, account, mintAuthority, 5, [], undefined, TEST_PROGRAM_ID); @@ -89,12 +89,12 @@ describe('permanentDelegate', () => { 0, undefined, undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const source_info = await connection.getTokenAccountBalance(account); const destination_info = await connection.getTokenAccountBalance(destination); - expect(source_info).to.not.be.null; - expect(destination_info).to.not.be.null; + expect(source_info).to.not.equal(null); + expect(destination_info).to.not.equal(null); if (source_info !== null) { expect(source_info.value.uiAmount).to.eql(3); } @@ -112,11 +112,11 @@ describe('permanentDelegate', () => { null, [], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const mintInfo = await getMint(connection, mint, undefined, TEST_PROGRAM_ID); const permanentDelegateConfig = getPermanentDelegate(mintInfo); - expect(permanentDelegateConfig).to.not.be.null; + expect(permanentDelegateConfig).to.not.equal(null); if (permanentDelegateConfig !== null) { expect(permanentDelegateConfig.delegate).to.eql(PublicKey.default); } diff --git a/token/js/test/e2e-2022/reallocate.test.ts b/token/js/test/e2e-2022/reallocate.test.ts index d36563954c6..399b917f2ea 100644 --- a/token/js/test/e2e-2022/reallocate.test.ts +++ b/token/js/test/e2e-2022/reallocate.test.ts @@ -28,7 +28,7 @@ describe('reallocate', () => { TEST_TOKEN_DECIMALS, mintKeypair, undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); owner = Keypair.generate(); account = await createAccount(connection, payer, mint, owner.publicKey, undefined, undefined, TEST_PROGRAM_ID); @@ -41,12 +41,12 @@ describe('reallocate', () => { EXTENSIONS, owner.publicKey, undefined, - TEST_PROGRAM_ID - ) + TEST_PROGRAM_ID, + ), ); await sendAndConfirmTransaction(connection, transaction, [payer, owner], undefined); const info = await connection.getAccountInfo(account); - expect(info).to.not.be.null; + expect(info).to.not.equal(null); if (info !== null) { const expectedAccountLen = getAccountLen(EXTENSIONS); expect(info.data.length).to.eql(expectedAccountLen); diff --git a/token/js/test/e2e-2022/tlv.test.ts b/token/js/test/e2e-2022/tlv.test.ts index a4bcc5e44eb..b75c30ed821 100644 --- a/token/js/test/e2e-2022/tlv.test.ts +++ b/token/js/test/e2e-2022/tlv.test.ts @@ -31,7 +31,7 @@ describe('tlv test', () => { }); // test that the parser gracefully handles accounts with arbitrary extra bytes - it('parse account with extra bytes', async () => { + it('parses account with extra bytes', async () => { const initTestAccount = async (extraBytes: number) => { const mintKeypair = Keypair.generate(); const accountKeypair = Keypair.generate(); @@ -47,7 +47,7 @@ describe('tlv test', () => { TEST_TOKEN_DECIMALS, mintKeypair, undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const transaction = new Transaction().add( @@ -58,7 +58,7 @@ describe('tlv test', () => { lamports, programId: TEST_PROGRAM_ID, }), - createInitializeAccountInstruction(account, mint, owner.publicKey, TEST_PROGRAM_ID) + createInitializeAccountInstruction(account, mint, owner.publicKey, TEST_PROGRAM_ID), ); await sendAndConfirmTransaction(connection, transaction, [payer, accountKeypair], undefined); @@ -80,11 +80,11 @@ describe('tlv test', () => { expect( getExtensionData(extension, accountInfo.tlvData), `account parse test failed: found ${ExtensionType[extension]}, but should not have. \ - test case: no extensions, ${i} extra bytes` - ).to.be.null; + test case: no extensions, ${i} extra bytes`, + ).to.equal(null); } return Promise.resolve(undefined); - }) + }), ); } diff --git a/token/js/test/e2e-2022/tokenGroup.test.ts b/token/js/test/e2e-2022/tokenGroup.test.ts index 0b302b67095..b076f8ac55d 100644 --- a/token/js/test/e2e-2022/tokenGroup.test.ts +++ b/token/js/test/e2e-2022/tokenGroup.test.ts @@ -53,15 +53,15 @@ describe('tokenGroup', async () => { mint.publicKey, mintAuthority.publicKey, mint.publicKey, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ), createInitializeMintInstruction( mint.publicKey, TEST_TOKEN_DECIMALS, mintAuthority.publicKey, null, - TEST_PROGRAM_ID - ) + TEST_PROGRAM_ID, + ), ); await sendAndConfirmTransaction(connection, transaction, [payer, mint], undefined); @@ -76,8 +76,8 @@ describe('tokenGroup', async () => { const tokenGroup = { updateAuthority: updateAuthority.publicKey, mint: mint.publicKey, - size: 0, - maxSize: 10, + size: BigInt(0), + maxSize: BigInt(10), }; // Transfer the required amount for rent exemption @@ -87,7 +87,7 @@ describe('tokenGroup', async () => { fromPubkey: payer.publicKey, toPubkey: mint.publicKey, lamports, - }) + }), ); await sendAndConfirmTransaction(connection, transaction, [payer], undefined); @@ -100,7 +100,7 @@ describe('tokenGroup', async () => { tokenGroup.maxSize, [mintAuthority], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const mintInfo = await getMint(connection, mint.publicKey, undefined, TEST_PROGRAM_ID); @@ -112,8 +112,8 @@ describe('tokenGroup', async () => { const tokenGroup = { updateAuthority: updateAuthority.publicKey, mint: mint.publicKey, - size: 0, - maxSize: 10, + size: BigInt(0), + maxSize: BigInt(10), }; await tokenGroupInitializeGroupWithRentTransfer( @@ -125,7 +125,7 @@ describe('tokenGroup', async () => { tokenGroup.maxSize, [mintAuthority], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const mintInfo = await getMint(connection, mint.publicKey, undefined, TEST_PROGRAM_ID); @@ -137,8 +137,8 @@ describe('tokenGroup', async () => { const tokenGroup = { updateAuthority: updateAuthority.publicKey, mint: mint.publicKey, - size: 0, - maxSize: 10, + size: BigInt(0), + maxSize: BigInt(10), }; // Transfer the required amount for rent exemption @@ -148,7 +148,7 @@ describe('tokenGroup', async () => { fromPubkey: payer.publicKey, toPubkey: mint.publicKey, lamports, - }) + }), ); await sendAndConfirmTransaction(connection, transaction, [payer], undefined); @@ -161,7 +161,7 @@ describe('tokenGroup', async () => { tokenGroup.maxSize, [mintAuthority], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); await tokenGroupUpdateGroupMaxSize( @@ -169,10 +169,10 @@ describe('tokenGroup', async () => { payer, mint.publicKey, updateAuthority.publicKey, - 20, + BigInt(20), [updateAuthority], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const mintInfo = await getMint(connection, mint.publicKey, undefined, TEST_PROGRAM_ID); @@ -180,8 +180,8 @@ describe('tokenGroup', async () => { expect(group).to.deep.equal({ updateAuthority: updateAuthority.publicKey, mint: mint.publicKey, - size: 0, - maxSize: 20, + size: BigInt(0), + maxSize: BigInt(20), }); }); @@ -189,8 +189,8 @@ describe('tokenGroup', async () => { const tokenGroup = { updateAuthority: updateAuthority.publicKey, mint: mint.publicKey, - size: 0, - maxSize: 10, + size: BigInt(0), + maxSize: BigInt(10), }; // Transfer the required amount for rent exemption @@ -200,7 +200,7 @@ describe('tokenGroup', async () => { fromPubkey: payer.publicKey, toPubkey: mint.publicKey, lamports, - }) + }), ); await sendAndConfirmTransaction(connection, transaction, [payer], undefined); @@ -213,7 +213,7 @@ describe('tokenGroup', async () => { tokenGroup.maxSize, [mintAuthority], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const newUpdateAuthority = Keypair.generate(); @@ -225,7 +225,7 @@ describe('tokenGroup', async () => { newUpdateAuthority.publicKey, [updateAuthority], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const mintInfo = await getMint(connection, mint.publicKey, undefined, TEST_PROGRAM_ID); @@ -233,8 +233,8 @@ describe('tokenGroup', async () => { expect(group).to.deep.equal({ updateAuthority: newUpdateAuthority.publicKey, mint: mint.publicKey, - size: 0, - maxSize: 10, + size: BigInt(0), + maxSize: BigInt(10), }); }); }); diff --git a/token/js/test/e2e-2022/tokenGroupMember.test.ts b/token/js/test/e2e-2022/tokenGroupMember.test.ts index 744cc312888..07cee386132 100644 --- a/token/js/test/e2e-2022/tokenGroupMember.test.ts +++ b/token/js/test/e2e-2022/tokenGroupMember.test.ts @@ -64,18 +64,18 @@ describe('tokenGroupMember', async () => { groupMint.publicKey, groupUpdateAuthority.publicKey, groupMint.publicKey, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ), createInitializeMintInstruction( groupMint.publicKey, TEST_TOKEN_DECIMALS, groupMintAuthority.publicKey, null, - TEST_PROGRAM_ID - ) + TEST_PROGRAM_ID, + ), ), [payer, groupMint], - undefined + undefined, ); await tokenGroupInitializeGroupWithRentTransfer( connection, @@ -83,10 +83,10 @@ describe('tokenGroupMember', async () => { groupMint.publicKey, groupMintAuthority.publicKey, groupUpdateAuthority.publicKey, - 3, + BigInt(3), [payer, groupMintAuthority], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); // Create the member mint. @@ -104,18 +104,18 @@ describe('tokenGroupMember', async () => { memberMint.publicKey, memberUpdateAuthority.publicKey, memberMint.publicKey, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ), createInitializeMintInstruction( memberMint.publicKey, TEST_TOKEN_DECIMALS, memberMintAuthority.publicKey, null, - TEST_PROGRAM_ID - ) + TEST_PROGRAM_ID, + ), ), [payer, memberMint], - undefined + undefined, ); }); @@ -123,7 +123,7 @@ describe('tokenGroupMember', async () => { const tokenGroupMember = { mint: memberMint.publicKey, group: groupMint.publicKey, - memberNumber: 1, + memberNumber: BigInt(1), }; await tokenGroupMemberInitializeWithRentTransfer( @@ -135,7 +135,7 @@ describe('tokenGroupMember', async () => { groupUpdateAuthority.publicKey, [memberMintAuthority, groupUpdateAuthority], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const mintInfo = await getMint(connection, memberMint.publicKey, undefined, TEST_PROGRAM_ID); diff --git a/token/js/test/e2e-2022/tokenMetadata.test.ts b/token/js/test/e2e-2022/tokenMetadata.test.ts index b98bce9a233..22ecfdd1783 100644 --- a/token/js/test/e2e-2022/tokenMetadata.test.ts +++ b/token/js/test/e2e-2022/tokenMetadata.test.ts @@ -62,15 +62,15 @@ describe('tokenMetadata', async () => { mint.publicKey, updateAuthority.publicKey, mint.publicKey, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ), createInitializeMintInstruction( mint.publicKey, TEST_TOKEN_DECIMALS, mintAuthority.publicKey, null, - TEST_PROGRAM_ID - ) + TEST_PROGRAM_ID, + ), ); await sendAndConfirmTransaction(connection, transaction, [payer, mint], undefined); @@ -97,7 +97,7 @@ describe('tokenMetadata', async () => { fromPubkey: payer.publicKey, toPubkey: mint.publicKey, lamports, - }) + }), ); await sendAndConfirmTransaction(connection, transaction, [payer], undefined); @@ -112,7 +112,7 @@ describe('tokenMetadata', async () => { tokenMetadata.uri, [mintAuthority], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const meta = await getTokenMetadata(connection, mint.publicKey, undefined, TEST_PROGRAM_ID); @@ -138,7 +138,7 @@ describe('tokenMetadata', async () => { 'uri', [mintAuthority], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const meta = await getTokenMetadata(connection, mint.publicKey, undefined, TEST_PROGRAM_ID); expect(meta).to.deep.equal({ @@ -163,7 +163,7 @@ describe('tokenMetadata', async () => { 'uri', [mintAuthority], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); await tokenMetadataUpdateField( connection, @@ -174,7 +174,7 @@ describe('tokenMetadata', async () => { 'TEST', [updateAuthority], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const meta = await getTokenMetadata(connection, mint.publicKey, undefined, TEST_PROGRAM_ID); expect(meta).to.deep.equal({ @@ -199,7 +199,7 @@ describe('tokenMetadata', async () => { 'uri', [mintAuthority], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); await tokenMetadataUpdateFieldWithRentTransfer( connection, @@ -210,7 +210,7 @@ describe('tokenMetadata', async () => { 'My Shiny New Token Metadata', [updateAuthority], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const meta = await getTokenMetadata(connection, mint.publicKey, undefined, TEST_PROGRAM_ID); expect(meta).to.deep.equal({ @@ -235,7 +235,7 @@ describe('tokenMetadata', async () => { 'uri', [mintAuthority], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); await tokenMetadataUpdateFieldWithRentTransfer( connection, @@ -246,7 +246,7 @@ describe('tokenMetadata', async () => { 'CUSTOM', [updateAuthority], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const meta = await getTokenMetadata(connection, mint.publicKey, undefined, TEST_PROGRAM_ID); expect(meta).to.deep.equal({ @@ -271,7 +271,7 @@ describe('tokenMetadata', async () => { 'uri', [mintAuthority], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); await tokenMetadataUpdateFieldWithRentTransfer( connection, @@ -282,7 +282,7 @@ describe('tokenMetadata', async () => { 'CUSTOM', [updateAuthority], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); await tokenMetadataUpdateField( connection, @@ -293,7 +293,7 @@ describe('tokenMetadata', async () => { 'test', [updateAuthority], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const meta = await getTokenMetadata(connection, mint.publicKey, undefined, TEST_PROGRAM_ID); expect(meta).to.deep.equal({ @@ -318,7 +318,7 @@ describe('tokenMetadata', async () => { 'uri', [mintAuthority], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); await tokenMetadataUpdateFieldWithRentTransfer( connection, @@ -329,7 +329,7 @@ describe('tokenMetadata', async () => { 'CUSTOM', [updateAuthority], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); await tokenMetadataUpdateFieldWithRentTransfer( connection, @@ -340,7 +340,7 @@ describe('tokenMetadata', async () => { 'My Shiny Custom Field', [updateAuthority], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const meta = await getTokenMetadata(connection, mint.publicKey, undefined, TEST_PROGRAM_ID); expect(meta).to.deep.equal({ @@ -365,7 +365,7 @@ describe('tokenMetadata', async () => { 'uri', [mintAuthority], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); await tokenMetadataUpdateFieldWithRentTransfer( connection, @@ -376,7 +376,7 @@ describe('tokenMetadata', async () => { 'CUSTOM', [updateAuthority], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); await tokenMetadataRemoveKey( connection, @@ -387,7 +387,7 @@ describe('tokenMetadata', async () => { true, [updateAuthority], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const meta = await getTokenMetadata(connection, mint.publicKey, undefined, TEST_PROGRAM_ID); expect(meta).to.deep.equal({ @@ -412,7 +412,7 @@ describe('tokenMetadata', async () => { 'uri', [mintAuthority], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); await tokenMetadataRemoveKey( connection, @@ -423,7 +423,7 @@ describe('tokenMetadata', async () => { true, [updateAuthority], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const meta = await getTokenMetadata(connection, mint.publicKey, undefined, TEST_PROGRAM_ID); expect(meta).to.deep.equal({ @@ -448,7 +448,7 @@ describe('tokenMetadata', async () => { 'uri', [mintAuthority], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const newAuthority = Keypair.generate().publicKey; await tokenMetadataUpdateAuthority( @@ -459,7 +459,7 @@ describe('tokenMetadata', async () => { newAuthority, [updateAuthority], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const meta = await getTokenMetadata(connection, mint.publicKey, undefined, TEST_PROGRAM_ID); expect(meta).to.deep.equal({ @@ -493,11 +493,11 @@ describe('tokenMetadata', async () => { tokenMetadata.uri, undefined, undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const payerKey = payer.publicKey; - const recentBlockhash = await connection.getLatestBlockhash().then((res) => res.blockhash); + const recentBlockhash = await connection.getLatestBlockhash().then(res => res.blockhash); const instructions = [ createEmitInstruction({ programId: TEST_PROGRAM_ID, @@ -516,7 +516,7 @@ describe('tokenMetadata', async () => { const returnDataBase64 = (await connection .simulateTransaction(tx) - .then((res) => res.value.returnData?.data[0])) as string; + .then(res => res.value.returnData?.data[0])) as string; const returnData = getBase64Encoder().encode(returnDataBase64); expect(returnData).to.deep.equal(tokenMetadata.updateAuthority.toBuffer()); diff --git a/token/js/test/e2e-2022/transferFee.test.ts b/token/js/test/e2e-2022/transferFee.test.ts index e7315f6538f..edea4158385 100644 --- a/token/js/test/e2e-2022/transferFee.test.ts +++ b/token/js/test/e2e-2022/transferFee.test.ts @@ -50,7 +50,7 @@ describe('transferFee', () => { async function setupTransferFeeMint( transferFeeConfigAuthority: PublicKey | null, - withdrawWithheldAuthority: PublicKey | null + withdrawWithheldAuthority: PublicKey | null, ) { const mintKeypair = Keypair.generate(); mint = mintKeypair.publicKey; @@ -71,9 +71,9 @@ describe('transferFee', () => { withdrawWithheldAuthority, FEE_BASIS_POINTS, MAX_FEE, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ), - createInitializeMintInstruction(mint, TEST_TOKEN_DECIMALS, mintAuthority.publicKey, null, TEST_PROGRAM_ID) + createInitializeMintInstruction(mint, TEST_TOKEN_DECIMALS, mintAuthority.publicKey, null, TEST_PROGRAM_ID), ); await sendAndConfirmTransaction(connection, mintTransaction, [payer, mintKeypair], undefined); } @@ -95,7 +95,7 @@ describe('transferFee', () => { owner.publicKey, undefined, undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); await mintTo( connection, @@ -106,7 +106,7 @@ describe('transferFee', () => { MINT_AMOUNT, [], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const accountKeypair = Keypair.generate(); @@ -117,7 +117,7 @@ describe('transferFee', () => { owner.publicKey, accountKeypair, undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); await transferChecked( @@ -131,13 +131,13 @@ describe('transferFee', () => { TEST_TOKEN_DECIMALS, [], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); }); it('initializes', async () => { const mintInfo = await getMint(connection, mint, undefined, TEST_PROGRAM_ID); const transferFeeConfig = getTransferFeeConfig(mintInfo); - expect(transferFeeConfig).to.not.be.null; + expect(transferFeeConfig).to.not.equal(null); if (transferFeeConfig !== null) { expect(transferFeeConfig.transferFeeConfigAuthority).to.eql(transferFeeConfigAuthority.publicKey); expect(transferFeeConfig.withdrawWithheldAuthority).to.eql(withdrawWithheldAuthority.publicKey); @@ -150,7 +150,7 @@ describe('transferFee', () => { const accountInfo = await getAccount(connection, destinationAccount, undefined, TEST_PROGRAM_ID); const transferFeeAmount = getTransferFeeAmount(accountInfo); - expect(transferFeeAmount).to.not.be.null; + expect(transferFeeAmount).to.not.equal(null); if (transferFeeAmount !== null) { expect(transferFeeAmount.withheldAmount).to.eql(FEE); } @@ -168,11 +168,11 @@ describe('transferFee', () => { FEE, [], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const accountInfo = await getAccount(connection, destinationAccount, undefined, TEST_PROGRAM_ID); const transferFeeAmount = getTransferFeeAmount(accountInfo); - expect(transferFeeAmount).to.not.be.null; + expect(transferFeeAmount).to.not.equal(null); if (transferFeeAmount !== null) { expect(transferFeeAmount.withheldAmount).to.eql(FEE * BigInt(2)); } @@ -187,12 +187,12 @@ describe('transferFee', () => { [], [destinationAccount], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const accountInfo = await getAccount(connection, destinationAccount, undefined, TEST_PROGRAM_ID); expect(accountInfo.amount).to.eql(TRANSFER_AMOUNT); const transferFeeAmount = getTransferFeeAmount(accountInfo); - expect(transferFeeAmount).to.not.be.null; + expect(transferFeeAmount).to.not.equal(null); if (transferFeeAmount !== null) { expect(transferFeeAmount.withheldAmount).to.eql(BigInt(0)); } @@ -204,17 +204,17 @@ describe('transferFee', () => { mint, [destinationAccount], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const accountInfo = await getAccount(connection, destinationAccount, undefined, TEST_PROGRAM_ID); const transferFeeAmount = getTransferFeeAmount(accountInfo); - expect(transferFeeAmount).to.not.be.null; + expect(transferFeeAmount).to.not.equal(null); if (transferFeeAmount !== null) { expect(transferFeeAmount.withheldAmount).to.eql(BigInt(0)); } const mintInfo = await getMint(connection, mint, undefined, TEST_PROGRAM_ID); const transferFeeConfig = getTransferFeeConfig(mintInfo); - expect(transferFeeConfig).to.not.be.null; + expect(transferFeeConfig).to.not.equal(null); if (transferFeeConfig !== null) { expect(transferFeeConfig.withheldAmount).to.eql(FEE); } @@ -226,7 +226,7 @@ describe('transferFee', () => { mint, [destinationAccount], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); await withdrawWithheldTokensFromMint( connection, @@ -236,18 +236,18 @@ describe('transferFee', () => { withdrawWithheldAuthority, [], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const accountInfo = await getAccount(connection, destinationAccount, undefined, TEST_PROGRAM_ID); expect(accountInfo.amount).to.eql(TRANSFER_AMOUNT); const transferFeeAmount = getTransferFeeAmount(accountInfo); - expect(transferFeeAmount).to.not.be.null; + expect(transferFeeAmount).to.not.equal(null); if (transferFeeAmount !== null) { expect(transferFeeAmount.withheldAmount).to.eql(BigInt(0)); } const mintInfo = await getMint(connection, mint, undefined, TEST_PROGRAM_ID); const transferFeeConfig = getTransferFeeConfig(mintInfo); - expect(transferFeeConfig).to.not.be.null; + expect(transferFeeConfig).to.not.equal(null); if (transferFeeConfig !== null) { expect(transferFeeConfig.withheldAmount).to.eql(BigInt(0)); } @@ -262,11 +262,11 @@ describe('transferFee', () => { null, [], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const mintInfo = await getMint(connection, mint, undefined, TEST_PROGRAM_ID); const transferFeeConfig = getTransferFeeConfig(mintInfo); - expect(transferFeeConfig).to.not.be.null; + expect(transferFeeConfig).to.not.equal(null); if (transferFeeConfig !== null) { expect(transferFeeConfig.transferFeeConfigAuthority).to.eql(PublicKey.default); } @@ -281,11 +281,11 @@ describe('transferFee', () => { null, [], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const mintInfo = await getMint(connection, mint, undefined, TEST_PROGRAM_ID); const transferFeeConfig = getTransferFeeConfig(mintInfo); - expect(transferFeeConfig).to.not.be.null; + expect(transferFeeConfig).to.not.equal(null); if (transferFeeConfig !== null) { expect(transferFeeConfig.withdrawWithheldAuthority).to.eql(PublicKey.default); } @@ -303,11 +303,11 @@ describe('transferFee', () => { UPDATED_FEE_BASIS_POINTS, UPDATED_MAX_FEE, undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const mintInfo = await getMint(connection, mint, undefined, TEST_PROGRAM_ID); const transferFeeConfig = getTransferFeeConfig(mintInfo); - expect(transferFeeConfig).to.not.be.null; + expect(transferFeeConfig).to.not.equal(null); if (transferFeeConfig !== null) { expect(transferFeeConfig.transferFeeConfigAuthority).to.eql(transferFeeConfigAuthority.publicKey); expect(transferFeeConfig.olderTransferFee.transferFeeBasisPoints).to.eql(FEE_BASIS_POINTS); @@ -325,7 +325,7 @@ describe('transferFee', () => { const mintInfo = await getMint(connection, mint, undefined, TEST_PROGRAM_ID); const transferFeeConfig = getTransferFeeConfig(mintInfo); - expect(transferFeeConfig).to.not.be.null; + expect(transferFeeConfig).to.not.equal(null); if (transferFeeConfig !== null) { expect(transferFeeConfig.transferFeeConfigAuthority).to.eql(PublicKey.default); expect(transferFeeConfig.withdrawWithheldAuthority).to.eql(withdrawWithheldAuthority.publicKey); @@ -342,7 +342,7 @@ describe('transferFee', () => { const mintInfo = await getMint(connection, mint, undefined, TEST_PROGRAM_ID); const transferFeeConfig = getTransferFeeConfig(mintInfo); - expect(transferFeeConfig).to.not.be.null; + expect(transferFeeConfig).to.not.equal(null); if (transferFeeConfig !== null) { expect(transferFeeConfig.transferFeeConfigAuthority).to.eql(transferFeeConfigAuthority.publicKey); expect(transferFeeConfig.withdrawWithheldAuthority).to.eql(PublicKey.default); @@ -358,7 +358,7 @@ describe('transferFee', () => { const mintInfo = await getMint(connection, mint, undefined, TEST_PROGRAM_ID); const transferFeeConfig = getTransferFeeConfig(mintInfo); - expect(transferFeeConfig).to.not.be.null; + expect(transferFeeConfig).to.not.equal(null); if (transferFeeConfig !== null) { expect(transferFeeConfig.transferFeeConfigAuthority).to.eql(PublicKey.default); expect(transferFeeConfig.withdrawWithheldAuthority).to.eql(PublicKey.default); diff --git a/token/js/test/e2e-2022/transferHook.test.ts b/token/js/test/e2e-2022/transferHook.test.ts index 717c940335e..12256286163 100644 --- a/token/js/test/e2e-2022/transferHook.test.ts +++ b/token/js/test/e2e-2022/transferHook.test.ts @@ -51,14 +51,14 @@ describe('transferHook', () => { payer.publicKey, false, TEST_PROGRAM_ID, - ASSOCIATED_TOKEN_PROGRAM_ID + ASSOCIATED_TOKEN_PROGRAM_ID, ); destinationAta = getAssociatedTokenAddressSync( mint, destinationAuthority, false, TEST_PROGRAM_ID, - ASSOCIATED_TOKEN_PROGRAM_ID + ASSOCIATED_TOKEN_PROGRAM_ID, ); const mintLen = getMintLen(EXTENSIONS); const lamports = await connection.getMinimumBalanceForRentExemption(mintLen); @@ -74,9 +74,9 @@ describe('transferHook', () => { mint, transferHookAuthority.publicKey, TRANSFER_HOOK_TEST_PROGRAM_ID, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ), - createInitializeMintInstruction(mint, TEST_TOKEN_DECIMALS, payer.publicKey, null, TEST_PROGRAM_ID) + createInitializeMintInstruction(mint, TEST_TOKEN_DECIMALS, payer.publicKey, null, TEST_PROGRAM_ID), ); await sendAndConfirmTransaction(connection, transaction, [payer, mintKeypair], undefined); @@ -84,7 +84,7 @@ describe('transferHook', () => { it('is initialized', async () => { const mintInfo = await getMint(connection, mint, undefined, TEST_PROGRAM_ID); const transferHook = getTransferHook(mintInfo); - expect(transferHook).to.not.be.null; + expect(transferHook).to.not.equal(null); if (transferHook !== null) { expect(transferHook.authority).to.eql(transferHookAuthority.publicKey); expect(transferHook.programId).to.eql(TRANSFER_HOOK_TEST_PROGRAM_ID); @@ -100,11 +100,11 @@ describe('transferHook', () => { transferHookAuthority, [], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const mintInfo = await getMint(connection, mint, undefined, TEST_PROGRAM_ID); const transferHook = getTransferHook(mintInfo); - expect(transferHook).to.not.be.null; + expect(transferHook).to.not.equal(null); if (transferHook !== null) { expect(transferHook.authority).to.eql(transferHookAuthority.publicKey); expect(transferHook.programId).to.eql(newTransferHookProgramId); @@ -120,11 +120,11 @@ describe('transferHook', () => { null, [], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const mintInfo = await getMint(connection, mint, undefined, TEST_PROGRAM_ID); const transferHook = getTransferHook(mintInfo); - expect(transferHook).to.not.be.null; + expect(transferHook).to.not.equal(null); if (transferHook !== null) { expect(transferHook.authority).to.eql(PublicKey.default); } @@ -157,7 +157,7 @@ describe('transferHook', () => { ], }, data, - 8 + 8, ); const initExtraAccountMetaInstruction = new TransactionInstruction({ @@ -179,7 +179,7 @@ describe('transferHook', () => { payer.publicKey, mint, TEST_PROGRAM_ID, - ASSOCIATED_TOKEN_PROGRAM_ID + ASSOCIATED_TOKEN_PROGRAM_ID, ), createMintToCheckedInstruction( mint, @@ -188,8 +188,8 @@ describe('transferHook', () => { 5 * 10 ** TEST_TOKEN_DECIMALS, TEST_TOKEN_DECIMALS, [], - TEST_PROGRAM_ID - ) + TEST_PROGRAM_ID, + ), ); await sendAndConfirmTransaction(connection, setupTransaction, [payer]); @@ -201,7 +201,7 @@ describe('transferHook', () => { destinationAuthority, undefined, TEST_PROGRAM_ID, - ASSOCIATED_TOKEN_PROGRAM_ID + ASSOCIATED_TOKEN_PROGRAM_ID, ); await transferCheckedWithTransferHook( @@ -215,7 +215,7 @@ describe('transferHook', () => { TEST_TOKEN_DECIMALS, [], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); }); }); diff --git a/token/js/test/e2e/amount.test.ts b/token/js/test/e2e/amount.test.ts index a8e0c0d8c09..0db64133f37 100644 --- a/token/js/test/e2e/amount.test.ts +++ b/token/js/test/e2e/amount.test.ts @@ -23,7 +23,7 @@ describe('Amount', () => { TEST_TOKEN_DECIMALS, mintKeypair, undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); }); it('amountToUiAmount', async () => { diff --git a/token/js/test/e2e/burn.test.ts b/token/js/test/e2e/burn.test.ts index 4b0c4e23ff5..fd04c3a1bf5 100644 --- a/token/js/test/e2e/burn.test.ts +++ b/token/js/test/e2e/burn.test.ts @@ -26,7 +26,7 @@ describe('burn', () => { TEST_TOKEN_DECIMALS, mintKeypair, undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); }); beforeEach(async () => { @@ -53,7 +53,7 @@ describe('burn', () => { TEST_TOKEN_DECIMALS, [], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const accountInfo = await getAccount(connection, account, undefined, TEST_PROGRAM_ID); expect(accountInfo.amount).to.eql(amount - burnAmount); diff --git a/token/js/test/e2e/close.test.ts b/token/js/test/e2e/close.test.ts index 6d03a6ccf33..deb2dde7667 100644 --- a/token/js/test/e2e/close.test.ts +++ b/token/js/test/e2e/close.test.ts @@ -30,7 +30,7 @@ describe('close', () => { TEST_TOKEN_DECIMALS, mintKeypair, undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); }); beforeEach(async () => { @@ -41,13 +41,14 @@ describe('close', () => { it('failsWithNonZeroAmount', async () => { const amount = BigInt(1000); await mintTo(connection, payer, mint, account, mintAuthority, amount, [], undefined, TEST_PROGRAM_ID); - expect(closeAccount(connection, payer, account, destination, owner, [], undefined, TEST_PROGRAM_ID)).to.be - .rejected; + expect( + closeAccount(connection, payer, account, destination, owner, [], undefined, TEST_PROGRAM_ID), + ).to.be.rejectedWith(Error); }); it('works', async () => { const accountInfo = await connection.getAccountInfo(account); let tokenRentExemptAmount; - expect(accountInfo).to.not.be.null; + expect(accountInfo).to.not.equal(null); if (accountInfo !== null) { tokenRentExemptAmount = accountInfo.lamports; } @@ -55,10 +56,10 @@ describe('close', () => { await closeAccount(connection, payer, account, destination, owner, [], undefined, TEST_PROGRAM_ID); const closedInfo = await connection.getAccountInfo(account); - expect(closedInfo).to.be.null; + expect(closedInfo).to.equal(null); const destinationInfo = await connection.getAccountInfo(destination); - expect(destinationInfo).to.not.be.null; + expect(destinationInfo).to.not.equal(null); if (destinationInfo !== null) { expect(destinationInfo.lamports).to.eql(tokenRentExemptAmount); } diff --git a/token/js/test/e2e/create.test.ts b/token/js/test/e2e/create.test.ts index 5d16638e6be..570f38a51f3 100644 --- a/token/js/test/e2e/create.test.ts +++ b/token/js/test/e2e/create.test.ts @@ -32,7 +32,7 @@ describe('createMint', () => { TEST_TOKEN_DECIMALS, mintKeypair, undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const mintInfo = await getMint(connection, mint, undefined, TEST_PROGRAM_ID); @@ -40,7 +40,7 @@ describe('createMint', () => { expect(mintInfo.mintAuthority).to.eql(testMintAuthority.publicKey); expect(mintInfo.supply).to.eql(BigInt(0)); expect(mintInfo.decimals).to.eql(TEST_TOKEN_DECIMALS); - expect(mintInfo.isInitialized).to.be.true; + expect(mintInfo.isInitialized).to.equal(true); expect(mintInfo.freezeAuthority).to.eql(testMintAuthority.publicKey); }); }); @@ -62,149 +62,149 @@ describe('createAccount', () => { TEST_TOKEN_DECIMALS, mintKeypair, undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, + ); + }); + it('auxiliary token account', async () => { + const owner = Keypair.generate(); + const account = await createAccount( + connection, + payer, + mint, + owner.publicKey, + Keypair.generate(), + undefined, + TEST_PROGRAM_ID, + ); + const accountInfo = await getAccount(connection, account, undefined, TEST_PROGRAM_ID); + expect(accountInfo.mint).to.eql(mint); + expect(accountInfo.owner).to.eql(owner.publicKey); + expect(accountInfo.amount).to.eql(BigInt(0)); + expect(accountInfo.delegate).to.equal(null); + expect(accountInfo.delegatedAmount).to.eql(BigInt(0)); + expect(accountInfo.isInitialized).to.equal(true); + expect(accountInfo.isFrozen).to.equal(false); + expect(accountInfo.isNative).to.equal(false); + expect(accountInfo.rentExemptReserve).to.equal(null); + expect(accountInfo.closeAuthority).to.equal(null); + + // you can create as many accounts as with same owner + const account2 = await createAccount( + connection, + payer, + mint, + owner.publicKey, + Keypair.generate(), + undefined, + TEST_PROGRAM_ID, + ); + expect(account2).to.not.eql(account); + }); + it('creates associated token account if it does not exist', async () => { + const owner = Keypair.generate(); + const associatedAddress = await getAssociatedTokenAddress( + mint, + owner.publicKey, + false, + TEST_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID, ); - }), - it('auxiliary token account', async () => { - const owner = Keypair.generate(); - const account = await createAccount( - connection, - payer, - mint, - owner.publicKey, - Keypair.generate(), - undefined, - TEST_PROGRAM_ID - ); - const accountInfo = await getAccount(connection, account, undefined, TEST_PROGRAM_ID); - expect(accountInfo.mint).to.eql(mint); - expect(accountInfo.owner).to.eql(owner.publicKey); - expect(accountInfo.amount).to.eql(BigInt(0)); - expect(accountInfo.delegate).to.be.null; - expect(accountInfo.delegatedAmount).to.eql(BigInt(0)); - expect(accountInfo.isInitialized).to.be.true; - expect(accountInfo.isFrozen).to.be.false; - expect(accountInfo.isNative).to.be.false; - expect(accountInfo.rentExemptReserve).to.be.null; - expect(accountInfo.closeAuthority).to.be.null; - - // you can create as many accounts as with same owner - const account2 = await createAccount( - connection, - payer, - mint, - owner.publicKey, - Keypair.generate(), - undefined, - TEST_PROGRAM_ID - ); - expect(account2).to.not.eql(account); - }), - it('creates associated token account if it does not exist', async () => { - const owner = Keypair.generate(); - const associatedAddress = await getAssociatedTokenAddress( - mint, - owner.publicKey, - false, - TEST_PROGRAM_ID, - ASSOCIATED_TOKEN_PROGRAM_ID - ); - // associated account shouldn't exist - const info = await connection.getAccountInfo(associatedAddress); - expect(info).to.be.null; + // associated account shouldn't exist + const info = await connection.getAccountInfo(associatedAddress); + expect(info).to.equal(null); - const createdAccountInfo = await getOrCreateAssociatedTokenAccount( - connection, - payer, - mint, - owner.publicKey, - false, - undefined, - undefined, - TEST_PROGRAM_ID, - ASSOCIATED_TOKEN_PROGRAM_ID - ); - expect(createdAccountInfo.mint).to.eql(mint); - expect(createdAccountInfo.owner).to.eql(owner.publicKey); - expect(createdAccountInfo.amount).to.eql(BigInt(0)); - expect(createdAccountInfo.delegate).to.be.null; - expect(createdAccountInfo.delegatedAmount).to.eql(BigInt(0)); - expect(createdAccountInfo.isInitialized).to.be.true; - expect(createdAccountInfo.isFrozen).to.be.false; - expect(createdAccountInfo.isNative).to.be.false; - expect(createdAccountInfo.rentExemptReserve).to.be.null; - expect(createdAccountInfo.closeAuthority).to.be.null; - - // do it again, just gives the account info - const accountInfo = await getOrCreateAssociatedTokenAccount( + const createdAccountInfo = await getOrCreateAssociatedTokenAccount( + connection, + payer, + mint, + owner.publicKey, + false, + undefined, + undefined, + TEST_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID, + ); + expect(createdAccountInfo.mint).to.eql(mint); + expect(createdAccountInfo.owner).to.eql(owner.publicKey); + expect(createdAccountInfo.amount).to.eql(BigInt(0)); + expect(createdAccountInfo.delegate).to.equal(null); + expect(createdAccountInfo.delegatedAmount).to.eql(BigInt(0)); + expect(createdAccountInfo.isInitialized).to.equal(true); + expect(createdAccountInfo.isFrozen).to.equal(false); + expect(createdAccountInfo.isNative).to.equal(false); + expect(createdAccountInfo.rentExemptReserve).to.equal(null); + expect(createdAccountInfo.closeAuthority).to.equal(null); + + // do it again, just gives the account info + const accountInfo = await getOrCreateAssociatedTokenAccount( + connection, + payer, + mint, + owner.publicKey, + false, + undefined, + undefined, + TEST_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID, + ); + + expect(createdAccountInfo).to.eql(accountInfo); + }); + it('associated token account', async () => { + const owner = Keypair.generate(); + const associatedAddress = await getAssociatedTokenAddress( + mint, + owner.publicKey, + false, + TEST_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID, + ); + + // associated account shouldn't exist + const info = await connection.getAccountInfo(associatedAddress); + expect(info).to.equal(null); + + const createdAddress = await createAccount( + connection, + payer, + mint, + owner.publicKey, + undefined, // uses ATA by default + undefined, + TEST_PROGRAM_ID, + ); + expect(createdAddress).to.eql(associatedAddress); + + const accountInfo = await getAccount(connection, associatedAddress, undefined, TEST_PROGRAM_ID); + expect(accountInfo).to.not.equal(null); + expect(accountInfo.mint).to.eql(mint); + expect(accountInfo.owner).to.eql(owner.publicKey); + expect(accountInfo.amount).to.eql(BigInt(0)); + + // creating again should cause TX error for the associated token account + expect( + createAccount( connection, payer, mint, owner.publicKey, - false, - undefined, + undefined, // uses ATA by default undefined, TEST_PROGRAM_ID, - ASSOCIATED_TOKEN_PROGRAM_ID - ); - - expect(createdAccountInfo).to.eql(accountInfo); - }), - it('associated token account', async () => { - const owner = Keypair.generate(); - const associatedAddress = await getAssociatedTokenAddress( - mint, - owner.publicKey, - false, - TEST_PROGRAM_ID, - ASSOCIATED_TOKEN_PROGRAM_ID - ); - - // associated account shouldn't exist - const info = await connection.getAccountInfo(associatedAddress); - expect(info).to.be.null; + ), + ).to.be.rejectedWith(Error); - const createdAddress = await createAccount( + // when creating again but with idempotent mode, TX should not throw error + return expect( + createAssociatedTokenAccountIdempotent( connection, payer, mint, owner.publicKey, - undefined, // uses ATA by default undefined, - TEST_PROGRAM_ID - ); - expect(createdAddress).to.eql(associatedAddress); - - const accountInfo = await getAccount(connection, associatedAddress, undefined, TEST_PROGRAM_ID); - expect(accountInfo).to.not.be.null; - expect(accountInfo.mint).to.eql(mint); - expect(accountInfo.owner).to.eql(owner.publicKey); - expect(accountInfo.amount).to.eql(BigInt(0)); - - // creating again should cause TX error for the associated token account - expect( - createAccount( - connection, - payer, - mint, - owner.publicKey, - undefined, // uses ATA by default - undefined, - TEST_PROGRAM_ID - ) - ).to.be.rejected; - - // when creating again but with idempotent mode, TX should not throw error - return expect( - createAssociatedTokenAccountIdempotent( - connection, - payer, - mint, - owner.publicKey, - undefined, - TEST_PROGRAM_ID - ) - ).to.be.fulfilled; - }); + TEST_PROGRAM_ID, + ), + ).to.be.fulfilled; + }); }); diff --git a/token/js/test/e2e/freeze.test.ts b/token/js/test/e2e/freeze.test.ts index 1b5a4ea82b2..b013edf1642 100644 --- a/token/js/test/e2e/freeze.test.ts +++ b/token/js/test/e2e/freeze.test.ts @@ -32,7 +32,7 @@ describe('freezeThaw', () => { TEST_TOKEN_DECIMALS, mintKeypair, undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); }); beforeEach(async () => { @@ -44,8 +44,9 @@ describe('freezeThaw', () => { it('freezes', async () => { await freezeAccount(connection, payer, account, mint, freezeAuthority, [], undefined, TEST_PROGRAM_ID); - expect(burn(connection, payer, account, mint, owner, burnAmount, [], undefined, TEST_PROGRAM_ID)).to.be - .rejected; + expect( + burn(connection, payer, account, mint, owner, burnAmount, [], undefined, TEST_PROGRAM_ID), + ).to.be.rejectedWith(Error); }); it('thaws', async () => { await freezeAccount(connection, payer, account, mint, freezeAuthority, [], undefined, TEST_PROGRAM_ID); diff --git a/token/js/test/e2e/initialize.test.ts b/token/js/test/e2e/initialize.test.ts index 973f0fca20b..ae635120adf 100644 --- a/token/js/test/e2e/initialize.test.ts +++ b/token/js/test/e2e/initialize.test.ts @@ -39,15 +39,15 @@ describe('initialize mint', () => { TEST_TOKEN_DECIMALS, mintAuthority, freezeAuthority, - TEST_PROGRAM_ID - ) + TEST_PROGRAM_ID, + ), ); await sendAndConfirmTransaction(connection, transaction, [payer, mintKeypair]); const mintInfo = await getMint(connection, mintKeypair.publicKey, undefined, TEST_PROGRAM_ID); expect(mintInfo.mintAuthority).to.eql(mintAuthority); expect(mintInfo.supply).to.eql(BigInt(0)); expect(mintInfo.decimals).to.eql(TEST_TOKEN_DECIMALS); - expect(mintInfo.isInitialized).to.be.true; + expect(mintInfo.isInitialized).to.equal(true); expect(mintInfo.freezeAuthority).to.eql(freezeAuthority); }); it('works with null freeze authority', async () => { @@ -65,16 +65,16 @@ describe('initialize mint', () => { TEST_TOKEN_DECIMALS, mintAuthority, null, - TEST_PROGRAM_ID - ) + TEST_PROGRAM_ID, + ), ); await sendAndConfirmTransaction(connection, transaction, [payer, mintKeypair]); const mintInfo = await getMint(connection, mintKeypair.publicKey, undefined, TEST_PROGRAM_ID); expect(mintInfo.mintAuthority).to.eql(mintAuthority); expect(mintInfo.supply).to.eql(BigInt(0)); expect(mintInfo.decimals).to.eql(TEST_TOKEN_DECIMALS); - expect(mintInfo.isInitialized).to.be.true; - expect(mintInfo.freezeAuthority).to.be.null; + expect(mintInfo.isInitialized).to.equal(true); + expect(mintInfo.freezeAuthority).to.equal(null); }); }); describe('initialize mint 2', () => { @@ -104,15 +104,15 @@ describe('initialize mint 2', () => { TEST_TOKEN_DECIMALS, mintAuthority, freezeAuthority, - TEST_PROGRAM_ID - ) + TEST_PROGRAM_ID, + ), ); await sendAndConfirmTransaction(connection, transaction, [payer, mintKeypair]); const mintInfo = await getMint(connection, mintKeypair.publicKey, undefined, TEST_PROGRAM_ID); expect(mintInfo.mintAuthority).to.eql(mintAuthority); expect(mintInfo.supply).to.eql(BigInt(0)); expect(mintInfo.decimals).to.eql(TEST_TOKEN_DECIMALS); - expect(mintInfo.isInitialized).to.be.true; + expect(mintInfo.isInitialized).to.equal(true); expect(mintInfo.freezeAuthority).to.eql(freezeAuthority); }); it('works with null freeze authority', async () => { @@ -130,15 +130,15 @@ describe('initialize mint 2', () => { TEST_TOKEN_DECIMALS, mintAuthority, null, - TEST_PROGRAM_ID - ) + TEST_PROGRAM_ID, + ), ); await sendAndConfirmTransaction(connection, transaction, [payer, mintKeypair]); const mintInfo = await getMint(connection, mintKeypair.publicKey, undefined, TEST_PROGRAM_ID); expect(mintInfo.mintAuthority).to.eql(mintAuthority); expect(mintInfo.supply).to.eql(BigInt(0)); expect(mintInfo.decimals).to.eql(TEST_TOKEN_DECIMALS); - expect(mintInfo.isInitialized).to.be.true; - expect(mintInfo.freezeAuthority).to.be.null; + expect(mintInfo.isInitialized).to.equal(true); + expect(mintInfo.freezeAuthority).to.equal(null); }); }); diff --git a/token/js/test/e2e/mint.test.ts b/token/js/test/e2e/mint.test.ts index 750e63bed88..2dfe0cb0623 100644 --- a/token/js/test/e2e/mint.test.ts +++ b/token/js/test/e2e/mint.test.ts @@ -29,7 +29,7 @@ describe('mint', () => { TEST_TOKEN_DECIMALS, mintKeypair, undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); owner = Keypair.generate(); account = await createAccount(connection, payer, mint, owner.publicKey, undefined, undefined, TEST_PROGRAM_ID); @@ -56,11 +56,11 @@ describe('mint', () => { TEST_TOKEN_DECIMALS, [], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); expect( - mintToChecked(connection, payer, mint, account, mintAuthority, amount, 1, [], undefined, TEST_PROGRAM_ID) - ).to.be.rejected; + mintToChecked(connection, payer, mint, account, mintAuthority, amount, 1, [], undefined, TEST_PROGRAM_ID), + ).to.be.rejectedWith(Error); }); }); diff --git a/token/js/test/e2e/multisig.test.ts b/token/js/test/e2e/multisig.test.ts index e66dd7fcffa..c5941901344 100644 --- a/token/js/test/e2e/multisig.test.ts +++ b/token/js/test/e2e/multisig.test.ts @@ -51,7 +51,7 @@ describe('multisig', () => { TEST_TOKEN_DECIMALS, mintKeypair, undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); }); beforeEach(async () => { @@ -63,7 +63,7 @@ describe('multisig', () => { multisig, Keypair.generate(), undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); account2 = await createAccount( connection, @@ -72,7 +72,7 @@ describe('multisig', () => { multisig, Keypair.generate(), undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); amount = BigInt(1000); await mintTo(connection, payer, mint, account1, mintAuthority, amount, [], undefined, TEST_PROGRAM_ID); @@ -110,7 +110,7 @@ describe('multisig', () => { newOwner, signers, undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const accountInfo = await getAccount(connection, account1, undefined, TEST_PROGRAM_ID); expect(accountInfo.owner).to.eql(newOwner); diff --git a/token/js/test/e2e/native.test.ts b/token/js/test/e2e/native.test.ts index 95805e27d62..6eb32350974 100644 --- a/token/js/test/e2e/native.test.ts +++ b/token/js/test/e2e/native.test.ts @@ -41,18 +41,18 @@ describe('native', () => { undefined, undefined, TEST_PROGRAM_ID, - nativeMint + nativeMint, ); }); it('works', async () => { const accountInfo = await getAccount(connection, account, undefined, TEST_PROGRAM_ID); - expect(accountInfo.isNative).to.be.true; + expect(accountInfo.isNative).to.equal(true); expect(accountInfo.amount).to.eql(BigInt(amount)); }); it('syncNative', async () => { let balance = 0; const preInfo = await connection.getAccountInfo(account); - expect(preInfo).to.not.be.null; + expect(preInfo).to.not.equal(null); if (preInfo != null) { balance = preInfo.lamports; } @@ -66,19 +66,19 @@ describe('native', () => { fromPubkey: payer.publicKey, toPubkey: account, lamports: additionalLamports, - }) + }), ), - [payer] + [payer], ); // no change in the amount const preAccountInfo = await getAccount(connection, account, undefined, TEST_PROGRAM_ID); - expect(preAccountInfo.isNative).to.be.true; + expect(preAccountInfo.isNative).to.equal(true); expect(preAccountInfo.amount).to.eql(BigInt(amount)); // but change in lamports const postInfo = await connection.getAccountInfo(account); - expect(postInfo).to.not.be.null; + expect(postInfo).to.not.equal(null); if (postInfo !== null) { expect(postInfo.lamports).to.eql(balance + additionalLamports); } @@ -86,22 +86,22 @@ describe('native', () => { // sync, amount changes await syncNative(connection, payer, account, undefined, TEST_PROGRAM_ID); const postAccountInfo = await getAccount(connection, account, undefined, TEST_PROGRAM_ID); - expect(postAccountInfo.isNative).to.be.true; + expect(postAccountInfo.isNative).to.equal(true); expect(postAccountInfo.amount).to.eql(BigInt(amount + additionalLamports)); }); it('closeAccount', async () => { let balance = 0; const preInfo = await connection.getAccountInfo(account); - expect(preInfo).to.not.be.null; + expect(preInfo).to.not.equal(null); if (preInfo != null) { balance = preInfo.lamports; } const destination = Keypair.generate().publicKey; await closeAccount(connection, payer, account, destination, owner, [], undefined, TEST_PROGRAM_ID); const nullInfo = await connection.getAccountInfo(account); - expect(nullInfo).to.be.null; + expect(nullInfo).to.equal(null); const destinationInfo = await connection.getAccountInfo(destination); - expect(destinationInfo).to.not.be.null; + expect(destinationInfo).to.not.equal(null); if (destinationInfo != null) { expect(destinationInfo.lamports).to.eql(balance); } diff --git a/token/js/test/e2e/recoverNested.test.ts b/token/js/test/e2e/recoverNested.test.ts index af563b7a181..fc11e8dcc99 100644 --- a/token/js/test/e2e/recoverNested.test.ts +++ b/token/js/test/e2e/recoverNested.test.ts @@ -40,7 +40,7 @@ describe('recoverNested', () => { 0, mintKeypair, undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); associatedToken = await createAssociatedTokenAccount( @@ -49,7 +49,7 @@ describe('recoverNested', () => { mint, owner.publicKey, undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); // nested mint @@ -63,7 +63,7 @@ describe('recoverNested', () => { 0, nestedMintKeypair, undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); nestedAssociatedToken = getAssociatedTokenAddressSync( @@ -71,7 +71,7 @@ describe('recoverNested', () => { associatedToken, true, TEST_PROGRAM_ID, - ASSOCIATED_TOKEN_PROGRAM_ID + ASSOCIATED_TOKEN_PROGRAM_ID, ); const transaction = new Transaction().add( createAssociatedTokenAccountInstruction( @@ -80,8 +80,8 @@ describe('recoverNested', () => { associatedToken, nestedMint, TEST_PROGRAM_ID, - ASSOCIATED_TOKEN_PROGRAM_ID - ) + ASSOCIATED_TOKEN_PROGRAM_ID, + ), ); await sendAndConfirmTransaction(connection, transaction, [payer], undefined); @@ -95,28 +95,28 @@ describe('recoverNested', () => { nestedMintAmount, undefined, undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, + ); + }); + it('success', async () => { + // create destination associated token + const destinationAssociatedToken = await createAssociatedTokenAccount( + connection, + payer, + nestedMint, + owner.publicKey, + undefined, + TEST_PROGRAM_ID, ); - }), - it('success', async () => { - // create destination associated token - const destinationAssociatedToken = await createAssociatedTokenAccount( - connection, - payer, - nestedMint, - owner.publicKey, - undefined, - TEST_PROGRAM_ID - ); - await recoverNested(connection, payer, owner, mint, nestedMint, undefined, TEST_PROGRAM_ID); + await recoverNested(connection, payer, owner, mint, nestedMint, undefined, TEST_PROGRAM_ID); - expect(await connection.getAccountInfo(nestedAssociatedToken)).to.be.null; + expect(await connection.getAccountInfo(nestedAssociatedToken)).to.equal(null); - const accountInfo = await getAccount(connection, destinationAssociatedToken, undefined, TEST_PROGRAM_ID); - expect(accountInfo).to.not.be.null; - expect(accountInfo.mint).to.eql(nestedMint); - expect(accountInfo.owner).to.eql(owner.publicKey); - expect(accountInfo.amount).to.eql(BigInt(nestedMintAmount)); - }); + const accountInfo = await getAccount(connection, destinationAssociatedToken, undefined, TEST_PROGRAM_ID); + expect(accountInfo).to.not.equal(null); + expect(accountInfo.mint).to.eql(nestedMint); + expect(accountInfo.owner).to.eql(owner.publicKey); + expect(accountInfo.amount).to.eql(BigInt(nestedMintAmount)); + }); }); diff --git a/token/js/test/e2e/setAuthority.test.ts b/token/js/test/e2e/setAuthority.test.ts index 5dac03f4e69..cc17bcfd9e7 100644 --- a/token/js/test/e2e/setAuthority.test.ts +++ b/token/js/test/e2e/setAuthority.test.ts @@ -31,7 +31,7 @@ describe('setAuthority', () => { TEST_TOKEN_DECIMALS, mintKeypair, undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); }); beforeEach(async () => { @@ -43,7 +43,7 @@ describe('setAuthority', () => { owner.publicKey, Keypair.generate(), undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); }); it('AccountOwner', async () => { @@ -57,7 +57,7 @@ describe('setAuthority', () => { newOwner.publicKey, [], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const accountInfo = await getAccount(connection, account, undefined, TEST_PROGRAM_ID); expect(accountInfo.owner).to.eql(newOwner.publicKey); @@ -70,7 +70,7 @@ describe('setAuthority', () => { owner.publicKey, [], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); expect( setAuthority( @@ -82,9 +82,9 @@ describe('setAuthority', () => { owner.publicKey, [], undefined, - TEST_PROGRAM_ID - ) - ).to.be.rejected; + TEST_PROGRAM_ID, + ), + ).to.be.rejectedWith(Error); }); it('MintAuthority', async () => { await setAuthority( @@ -96,10 +96,10 @@ describe('setAuthority', () => { null, [], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const mintInfo = await getMint(connection, mint, undefined, TEST_PROGRAM_ID); - expect(mintInfo.mintAuthority).to.be.null; + expect(mintInfo.mintAuthority).to.equal(null); }); it('CloseAuthority', async () => { const closeAuthority = Keypair.generate(); @@ -112,7 +112,7 @@ describe('setAuthority', () => { closeAuthority.publicKey, [], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const accountInfo = await getAccount(connection, account, undefined, TEST_PROGRAM_ID); expect(accountInfo.closeAuthority).to.eql(closeAuthority.publicKey); @@ -127,9 +127,9 @@ describe('setAuthority', () => { null, [], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const mintInfo = await getMint(connection, mint, undefined, TEST_PROGRAM_ID); - expect(mintInfo.freezeAuthority).to.be.null; + expect(mintInfo.freezeAuthority).to.equal(null); }); }); diff --git a/token/js/test/e2e/transfer.test.ts b/token/js/test/e2e/transfer.test.ts index 2796118c474..019b0ff9fd8 100644 --- a/token/js/test/e2e/transfer.test.ts +++ b/token/js/test/e2e/transfer.test.ts @@ -42,7 +42,7 @@ describe('transfer', () => { TEST_TOKEN_DECIMALS, mintKeypair, undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); }); beforeEach(async () => { @@ -54,7 +54,7 @@ describe('transfer', () => { owner1.publicKey, undefined, undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); owner2 = Keypair.generate(); account2 = await createAccount( @@ -64,7 +64,7 @@ describe('transfer', () => { owner2.publicKey, undefined, undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); amount = BigInt(1000); await mintTo(connection, payer, mint, account1, mintAuthority, amount, [], undefined, TEST_PROGRAM_ID); @@ -91,7 +91,7 @@ describe('transfer', () => { TEST_TOKEN_DECIMALS, [], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const destAccountInfo = await getAccount(connection, account2, undefined, TEST_PROGRAM_ID); @@ -111,9 +111,9 @@ describe('transfer', () => { TEST_TOKEN_DECIMALS - 1, [], undefined, - TEST_PROGRAM_ID - ) - ).to.be.rejected; + TEST_PROGRAM_ID, + ), + ).to.be.rejectedWith(Error); }); it('approveRevoke', async () => { const delegate = Keypair.generate(); @@ -127,7 +127,7 @@ describe('transfer', () => { delegatedAmount, [], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const approvedAccountInfo = await getAccount(connection, account1, undefined, TEST_PROGRAM_ID); expect(approvedAccountInfo.delegatedAmount).to.eql(delegatedAmount); @@ -135,7 +135,7 @@ describe('transfer', () => { await revoke(connection, payer, account1, owner1, [], undefined, TEST_PROGRAM_ID); const revokedAccountInfo = await getAccount(connection, account1, undefined, TEST_PROGRAM_ID); expect(revokedAccountInfo.delegatedAmount).to.eql(BigInt(0)); - expect(revokedAccountInfo.delegate).to.be.null; + expect(revokedAccountInfo.delegate).to.equal(null); }); it('delegateTransfer', async () => { const delegate = Keypair.generate(); @@ -151,14 +151,15 @@ describe('transfer', () => { TEST_TOKEN_DECIMALS, [], undefined, - TEST_PROGRAM_ID + TEST_PROGRAM_ID, ); const transferAmount = delegatedAmount - BigInt(1); await transfer(connection, payer, account1, account2, delegate, transferAmount, [], undefined, TEST_PROGRAM_ID); const accountInfo = await getAccount(connection, account1, undefined, TEST_PROGRAM_ID); expect(accountInfo.delegatedAmount).to.eql(delegatedAmount - transferAmount); expect(accountInfo.delegate).to.eql(delegate.publicKey); - expect(transfer(connection, payer, account1, account2, delegate, BigInt(2), [], undefined, TEST_PROGRAM_ID)).to - .be.rejected; + expect( + transfer(connection, payer, account1, account2, delegate, BigInt(2), [], undefined, TEST_PROGRAM_ID), + ).to.be.rejectedWith(Error); }); }); diff --git a/token/js/test/unit/decode.test.ts b/token/js/test/unit/decode.test.ts index 97602083da2..1dfe99ab925 100644 --- a/token/js/test/unit/decode.test.ts +++ b/token/js/test/unit/decode.test.ts @@ -11,7 +11,7 @@ describe('spl-token-2022 instructions', () => { const ix = createInitializeMintCloseAuthorityInstruction( Keypair.generate().publicKey, Keypair.generate().publicKey, - TOKEN_2022_PROGRAM_ID + TOKEN_2022_PROGRAM_ID, ); expect(ix.programId).to.eql(TOKEN_2022_PROGRAM_ID); expect(ix.keys).to.have.length(1); @@ -20,7 +20,7 @@ describe('spl-token-2022 instructions', () => { const ix = createInitializePermanentDelegateInstruction( Keypair.generate().publicKey, Keypair.generate().publicKey, - TOKEN_2022_PROGRAM_ID + TOKEN_2022_PROGRAM_ID, ); expect(ix.programId).to.eql(TOKEN_2022_PROGRAM_ID); expect(ix.keys).to.have.length(1); diff --git a/token/js/test/unit/groupMemberPointer.test.ts b/token/js/test/unit/groupMemberPointer.test.ts index c6ceefb2ef2..eebfec22841 100644 --- a/token/js/test/unit/groupMemberPointer.test.ts +++ b/token/js/test/unit/groupMemberPointer.test.ts @@ -21,7 +21,7 @@ describe('SPL Token 2022 GroupMemberPointer Extension', () => { mint, authority, memberAddress, - TOKEN_2022_PROGRAM_ID + TOKEN_2022_PROGRAM_ID, ); expect(instruction).to.deep.equal( new TransactionInstruction({ @@ -35,7 +35,7 @@ describe('SPL Token 2022 GroupMemberPointer Extension', () => { AUTHORITY_ADDRESS_BYTES, GROUP_MEMBER_ADDRESS_BYTES, ]), - }) + }), ); }); it('can create UpdateGroupMemberPointerInstruction', () => { @@ -57,7 +57,7 @@ describe('SPL Token 2022 GroupMemberPointer Extension', () => { ]), GROUP_MEMBER_ADDRESS_BYTES, ]), - }) + }), ); }); it('can create UpdateGroupMemberPointerInstruction to none', () => { @@ -79,7 +79,7 @@ describe('SPL Token 2022 GroupMemberPointer Extension', () => { ]), NULL_OPTIONAL_NONZERO_PUBKEY_BYTES, ]), - }) + }), ); }); it('can get state with authority and group address', async () => { diff --git a/token/js/test/unit/groupPointer.test.ts b/token/js/test/unit/groupPointer.test.ts index 93a967c405b..ddd93e3fea0 100644 --- a/token/js/test/unit/groupPointer.test.ts +++ b/token/js/test/unit/groupPointer.test.ts @@ -21,7 +21,7 @@ describe('SPL Token 2022 GroupPointer Extension', () => { mint, authority, groupAddress, - TOKEN_2022_PROGRAM_ID + TOKEN_2022_PROGRAM_ID, ); expect(instruction).to.deep.equal( new TransactionInstruction({ @@ -35,7 +35,7 @@ describe('SPL Token 2022 GroupPointer Extension', () => { AUTHORITY_ADDRESS_BYTES, GROUP_ADDRESS_BYTES, ]), - }) + }), ); }); it('can create UpdateGroupPointerInstruction', () => { @@ -57,7 +57,7 @@ describe('SPL Token 2022 GroupPointer Extension', () => { ]), GROUP_ADDRESS_BYTES, ]), - }) + }), ); }); it('can create UpdateGroupPointerInstruction to none', () => { @@ -79,7 +79,7 @@ describe('SPL Token 2022 GroupPointer Extension', () => { ]), NULL_OPTIONAL_NONZERO_PUBKEY_BYTES, ]), - }) + }), ); }); it('can get state with authority and group address', async () => { diff --git a/token/js/test/unit/index.test.ts b/token/js/test/unit/index.test.ts index f1a5c7b4f5f..60b155f7d07 100644 --- a/token/js/test/unit/index.test.ts +++ b/token/js/test/unit/index.test.ts @@ -35,7 +35,7 @@ describe('spl-token instructions', () => { Keypair.generate().publicKey, Keypair.generate().publicKey, 1, - 9 + 9, ); expect(ix.programId).to.eql(TOKEN_PROGRAM_ID); expect(ix.keys).to.have.length(4); @@ -52,7 +52,7 @@ describe('spl-token instructions', () => { Keypair.generate().publicKey, 9, Keypair.generate().publicKey, - null + null, ); expect(ix.programId).to.eql(TOKEN_PROGRAM_ID); expect(ix.keys).to.have.length(1); @@ -68,7 +68,7 @@ describe('spl-token instructions', () => { const ix = createInitializeAccount2Instruction( Keypair.generate().publicKey, Keypair.generate().publicKey, - Keypair.generate().publicKey + Keypair.generate().publicKey, ); expect(ix.programId).to.eql(TOKEN_PROGRAM_ID); expect(ix.keys).to.have.length(3); @@ -78,7 +78,7 @@ describe('spl-token instructions', () => { const ix = createInitializeAccount3Instruction( Keypair.generate().publicKey, Keypair.generate().publicKey, - Keypair.generate().publicKey + Keypair.generate().publicKey, ); expect(ix.programId).to.eql(TOKEN_PROGRAM_ID); expect(ix.keys).to.have.length(2); @@ -95,7 +95,7 @@ describe('spl-token-2022 instructions', () => { 1, 9, [], - TOKEN_2022_PROGRAM_ID + TOKEN_2022_PROGRAM_ID, ); expect(ix.programId).to.eql(TOKEN_2022_PROGRAM_ID); expect(ix.keys).to.have.length(4); @@ -107,7 +107,7 @@ describe('spl-token-2022 instructions', () => { 9, Keypair.generate().publicKey, null, - TOKEN_2022_PROGRAM_ID + TOKEN_2022_PROGRAM_ID, ); expect(ix.programId).to.eql(TOKEN_2022_PROGRAM_ID); expect(ix.keys).to.have.length(2); @@ -119,7 +119,7 @@ describe('spl-token-2022 instructions', () => { 9, Keypair.generate().publicKey, null, - TOKEN_2022_PROGRAM_ID + TOKEN_2022_PROGRAM_ID, ); expect(ix.programId).to.eql(TOKEN_2022_PROGRAM_ID); expect(ix.keys).to.have.length(1); @@ -162,7 +162,7 @@ describe('spl-associated-token-account instructions', () => { Keypair.generate().publicKey, Keypair.generate().publicKey, Keypair.generate().publicKey, - Keypair.generate().publicKey + Keypair.generate().publicKey, ); expect(ix.programId).to.eql(ASSOCIATED_TOKEN_PROGRAM_ID); expect(ix.keys).to.have.length(6); @@ -173,61 +173,61 @@ describe('state', () => { it('getAssociatedTokenAddress', async () => { const associatedPublicKey = await getAssociatedTokenAddress( new PublicKey('7o36UsWR1JQLpZ9PE2gn9L4SQ69CNNiWAXd4Jt7rqz9Z'), - new PublicKey('B8UwBUUnKwCyKuGMbFKWaG7exYdDk2ozZrPg72NyVbfj') + new PublicKey('B8UwBUUnKwCyKuGMbFKWaG7exYdDk2ozZrPg72NyVbfj'), ); expect(associatedPublicKey.toString()).to.eql( - new PublicKey('DShWnroshVbeUp28oopA3Pu7oFPDBtC1DBmPECXXAQ9n').toString() + new PublicKey('DShWnroshVbeUp28oopA3Pu7oFPDBtC1DBmPECXXAQ9n').toString(), ); await expect( getAssociatedTokenAddress( new PublicKey('7o36UsWR1JQLpZ9PE2gn9L4SQ69CNNiWAXd4Jt7rqz9Z'), - associatedPublicKey - ) + associatedPublicKey, + ), ).to.be.rejectedWith(TokenOwnerOffCurveError); const associatedPublicKey2 = await getAssociatedTokenAddress( new PublicKey('7o36UsWR1JQLpZ9PE2gn9L4SQ69CNNiWAXd4Jt7rqz9Z'), associatedPublicKey, - true + true, ); expect(associatedPublicKey2.toString()).to.eql( - new PublicKey('F3DmXZFqkfEWFA7MN2vDPs813GeEWPaT6nLk4PSGuWJd').toString() + new PublicKey('F3DmXZFqkfEWFA7MN2vDPs813GeEWPaT6nLk4PSGuWJd').toString(), ); }); it('getAssociatedTokenAddressSync matches getAssociatedTokenAddress', async () => { const asyncAssociatedPublicKey = await getAssociatedTokenAddress( new PublicKey('7o36UsWR1JQLpZ9PE2gn9L4SQ69CNNiWAXd4Jt7rqz9Z'), - new PublicKey('B8UwBUUnKwCyKuGMbFKWaG7exYdDk2ozZrPg72NyVbfj') + new PublicKey('B8UwBUUnKwCyKuGMbFKWaG7exYdDk2ozZrPg72NyVbfj'), ); const associatedPublicKey = getAssociatedTokenAddressSync( new PublicKey('7o36UsWR1JQLpZ9PE2gn9L4SQ69CNNiWAXd4Jt7rqz9Z'), - new PublicKey('B8UwBUUnKwCyKuGMbFKWaG7exYdDk2ozZrPg72NyVbfj') + new PublicKey('B8UwBUUnKwCyKuGMbFKWaG7exYdDk2ozZrPg72NyVbfj'), ); expect(associatedPublicKey.toString()).to.eql( - new PublicKey('DShWnroshVbeUp28oopA3Pu7oFPDBtC1DBmPECXXAQ9n').toString() + new PublicKey('DShWnroshVbeUp28oopA3Pu7oFPDBtC1DBmPECXXAQ9n').toString(), ); expect(asyncAssociatedPublicKey.toString()).to.eql(associatedPublicKey.toString()); expect(function () { getAssociatedTokenAddressSync( new PublicKey('7o36UsWR1JQLpZ9PE2gn9L4SQ69CNNiWAXd4Jt7rqz9Z'), - associatedPublicKey + associatedPublicKey, ); }).to.throw(TokenOwnerOffCurveError); const asyncAssociatedPublicKey2 = await getAssociatedTokenAddress( new PublicKey('7o36UsWR1JQLpZ9PE2gn9L4SQ69CNNiWAXd4Jt7rqz9Z'), asyncAssociatedPublicKey, - true + true, ); const associatedPublicKey2 = getAssociatedTokenAddressSync( new PublicKey('7o36UsWR1JQLpZ9PE2gn9L4SQ69CNNiWAXd4Jt7rqz9Z'), associatedPublicKey, - true + true, ); expect(associatedPublicKey2.toString()).to.eql( - new PublicKey('F3DmXZFqkfEWFA7MN2vDPs813GeEWPaT6nLk4PSGuWJd').toString() + new PublicKey('F3DmXZFqkfEWFA7MN2vDPs813GeEWPaT6nLk4PSGuWJd').toString(), ); expect(asyncAssociatedPublicKey2.toString()).to.eql(associatedPublicKey2.toString()); }); @@ -249,18 +249,18 @@ describe('extensionType', () => { expect( getMintLen([ExtensionType.TransferFeeConfig, ExtensionType.NonTransferable], { [ExtensionType.TokenMetadata]: 200, - }) + }), ).to.eql(486); expect( getMintLen([], { [ExtensionType.TokenMetadata]: 200, - }) + }), ).to.eql(370); // Should error on an extension that isn't variable-length expect(() => getMintLen([ExtensionType.TransferFeeConfig, ExtensionType.NonTransferable], { [ExtensionType.TransferHook]: 200, - }) + }), ).to.throw('Extension 14 is not variable length'); }); diff --git a/token/js/test/unit/metadataPointer.test.ts b/token/js/test/unit/metadataPointer.test.ts index 5b7580767b0..1b614cfccdb 100644 --- a/token/js/test/unit/metadataPointer.test.ts +++ b/token/js/test/unit/metadataPointer.test.ts @@ -21,7 +21,7 @@ describe('SPL Token 2022 MetadataPointer Extension', () => { mint, authority, metadataAddress, - TOKEN_2022_PROGRAM_ID + TOKEN_2022_PROGRAM_ID, ); expect(instruction).to.deep.equal( new TransactionInstruction({ @@ -35,7 +35,7 @@ describe('SPL Token 2022 MetadataPointer Extension', () => { AUTHORITY_ADDRESS_BYTES, METADATA_ADDRESS_BYTES, ]), - }) + }), ); }); it('can create UpdateMetadataPointerInstruction', () => { @@ -57,7 +57,7 @@ describe('SPL Token 2022 MetadataPointer Extension', () => { ]), METADATA_ADDRESS_BYTES, ]), - }) + }), ); }); it('can create UpdateMetadataPointerInstruction to none', () => { @@ -79,7 +79,7 @@ describe('SPL Token 2022 MetadataPointer Extension', () => { ]), NULL_OPTIONAL_NONZERO_PUBKEY_BYTES, ]), - }) + }), ); }); it('can get state with authority and metadata address', async () => { diff --git a/token/js/test/unit/tokenMetadata.test.ts b/token/js/test/unit/tokenMetadata.test.ts index 33bbb1a8155..9d1f14d9f99 100644 --- a/token/js/test/unit/tokenMetadata.test.ts +++ b/token/js/test/unit/tokenMetadata.test.ts @@ -20,10 +20,10 @@ describe('SPL Token 2022 Metadata Extension', () => { } as TokenMetadata); expect(() => updateTokenMetadata(input, 'mint', 'string')).to.throw( - 'Cannot update mint via this instruction' + 'Cannot update mint via this instruction', ); expect(() => updateTokenMetadata(input, 'updateAuthority', 'string')).to.throw( - 'Cannot update updateAuthority via this instruction' + 'Cannot update updateAuthority via this instruction', ); }); it('can update name', async () => { diff --git a/token/js/test/unit/transferHook.test.ts b/token/js/test/unit/transferHook.test.ts index a943f752fc2..56ed5e96821 100644 --- a/token/js/test/unit/transferHook.test.ts +++ b/token/js/test/unit/transferHook.test.ts @@ -117,7 +117,7 @@ describe('transferHook', () => { connection = await getConnection(); connection.getAccountInfo = async ( _publicKey: PublicKey, - _commitmentOrConfig?: Parameters<(typeof connection)['getAccountInfo']>[1] + _commitmentOrConfig?: Parameters<(typeof connection)['getAccountInfo']>[1], ): ReturnType<(typeof connection)['getAccountInfo']> => ({ data: Buffer.from([0, 0, 2, 2, 2, 2]), owner: PublicKey.default, @@ -134,7 +134,7 @@ describe('transferHook', () => { lamports: 0, }; const parsedExtraAccounts = getExtraAccountMetas(accountInfo); - expect(parsedExtraAccounts).to.not.be.null; + expect(parsedExtraAccounts).to.not.equal(null); if (parsedExtraAccounts == null) { return; } @@ -146,18 +146,18 @@ describe('transferHook', () => { expect(parsedExtraAccounts[0].discriminator).to.eql(0); expect(parsedExtraAccounts[0].addressConfig).to.eql(plainAccount.toBuffer()); - expect(parsedExtraAccounts[0].isSigner).to.be.false; - expect(parsedExtraAccounts[0].isWritable).to.be.false; + expect(parsedExtraAccounts[0].isSigner).to.equal(false); + expect(parsedExtraAccounts[0].isWritable).to.equal(false); expect(parsedExtraAccounts[1].discriminator).to.eql(1); expect(parsedExtraAccounts[1].addressConfig).to.eql(addressConfig); - expect(parsedExtraAccounts[1].isSigner).to.be.true; - expect(parsedExtraAccounts[1].isWritable).to.be.false; + expect(parsedExtraAccounts[1].isSigner).to.equal(true); + expect(parsedExtraAccounts[1].isWritable).to.equal(false); expect(parsedExtraAccounts[2].discriminator).to.eql(128); expect(parsedExtraAccounts[2].addressConfig).to.eql(addressConfig); - expect(parsedExtraAccounts[2].isSigner).to.be.false; - expect(parsedExtraAccounts[2].isWritable).to.be.true; + expect(parsedExtraAccounts[2].isSigner).to.equal(false); + expect(parsedExtraAccounts[2].isWritable).to.equal(true); }); it('can resolve extra metas', async () => { @@ -166,36 +166,36 @@ describe('transferHook', () => { plainExtraAccountMeta, [], instructionData, - testProgramId + testProgramId, ); expect(resolvedPlainAccount.pubkey).to.eql(plainAccount); - expect(resolvedPlainAccount.isSigner).to.be.false; - expect(resolvedPlainAccount.isWritable).to.be.false; + expect(resolvedPlainAccount.isSigner).to.equal(false); + expect(resolvedPlainAccount.isWritable).to.equal(false); const resolvedPdaAccount = await resolveExtraAccountMeta( connection, pdaExtraAccountMeta, [resolvedPlainAccount], instructionData, - testProgramId + testProgramId, ); expect(resolvedPdaAccount.pubkey).to.eql(pdaPublicKey); - expect(resolvedPdaAccount.isSigner).to.be.true; - expect(resolvedPdaAccount.isWritable).to.be.false; + expect(resolvedPdaAccount.isSigner).to.equal(true); + expect(resolvedPdaAccount.isWritable).to.equal(false); const resolvedPdaAccountWithProgramId = await resolveExtraAccountMeta( connection, pdaExtraAccountMetaWithProgramId, [resolvedPlainAccount], instructionData, - testProgramId + testProgramId, ); expect(resolvedPdaAccountWithProgramId.pubkey).to.eql(pdaPublicKeyWithProgramId); - expect(resolvedPdaAccountWithProgramId.isSigner).to.be.false; - expect(resolvedPdaAccountWithProgramId.isWritable).to.be.true; + expect(resolvedPdaAccountWithProgramId.isSigner).to.equal(false); + expect(resolvedPdaAccountWithProgramId.isWritable).to.equal(true); }); }); @@ -235,12 +235,12 @@ describe('transferHook', () => { function createMockFetchAccountDataFn(extraAccounts: ExtraAccountMeta[]) { return async function mockFetchAccountDataFn( publicKey: PublicKey, - _commitmentOrConfig?: Parameters[1] + _commitmentOrConfig?: Parameters[1], ): ReturnType { // Mocked mint state if (publicKey.equals(mintPubkey)) { const data = Buffer.alloc( - ACCOUNT_SIZE + ACCOUNT_TYPE_SIZE + TYPE_SIZE + LENGTH_SIZE + TRANSFER_HOOK_SIZE + ACCOUNT_SIZE + ACCOUNT_TYPE_SIZE + TYPE_SIZE + LENGTH_SIZE + TRANSFER_HOOK_SIZE, ); MintLayout.encode( { @@ -253,7 +253,7 @@ describe('transferHook', () => { freezeAuthority: PublicKey.default, }, data, - 0 + 0, ); data.writeUint8(1, ACCOUNT_SIZE); // Account type (1): Mint = 1 data.writeUint16LE(ExtensionType.TransferHook, ACCOUNT_SIZE + ACCOUNT_TYPE_SIZE); @@ -264,7 +264,7 @@ describe('transferHook', () => { programId: transferHookProgramId, }, data, - ACCOUNT_SIZE + ACCOUNT_TYPE_SIZE + TYPE_SIZE + LENGTH_SIZE + ACCOUNT_SIZE + ACCOUNT_TYPE_SIZE + TYPE_SIZE + LENGTH_SIZE, ); return { data, @@ -290,7 +290,7 @@ describe('transferHook', () => { length: 4 + ExtraAccountMetaLayout.span * extraAccounts.length, extraAccountsList, }, - data + data, ); return { data, @@ -362,18 +362,18 @@ describe('transferHook', () => { const extraMeta4Pubkey = PublicKey.findProgramAddressSync( [sourcePubkey.toBuffer(), validateStatePubkey.toBuffer()], - transferHookProgramId + transferHookProgramId, )[0]; const extraMeta5Pubkey = PublicKey.findProgramAddressSync( [extraMeta1Pubkey.toBuffer(), extraMeta2Pubkey.toBuffer()], - transferHookProgramId + transferHookProgramId, )[0]; const extraMeta6Pubkey = PublicKey.findProgramAddressSync( [ Buffer.from('prefix'), amountInLeBytes, // Instruction data 8..16 ], - transferHookProgramId + transferHookProgramId, )[0]; // Fail missing key @@ -395,8 +395,8 @@ describe('transferHook', () => { mintPubkey, destinationPubkey, authorityPubkey, - amount - ) + amount, + ), ).to.be.rejectedWith('Missing required account in instruction'); const instruction = new TransactionInstruction({ @@ -417,7 +417,7 @@ describe('transferHook', () => { mintPubkey, destinationPubkey, authorityPubkey, - amount + amount, ); const checkMetas = [ @@ -472,20 +472,20 @@ describe('transferHook', () => { sourcePubkey.toBuffer(), // Account key at index 0 mintPubkey.toBuffer(), // Account key at index 1 ], - transferHookProgramId + transferHookProgramId, )[0]; const extraMeta2Pubkey = PublicKey.findProgramAddressSync( [ validateStatePubkey.toBuffer(), // Account key at index 4 ], - transferHookProgramId + transferHookProgramId, )[0]; const extraMeta3Pubkey = PublicKey.findProgramAddressSync( [ Buffer.from('prefix'), amountInLeBytes, // Instruction data 8..16 ], - transferHookProgramId + transferHookProgramId, )[0]; const extraMeta4Pubkey = arbitraryProgramId; const extraMeta5Pubkey = PublicKey.findProgramAddressSync( @@ -494,7 +494,7 @@ describe('transferHook', () => { amountInLeBytes, // Instruction data 8..16 extraMeta2Pubkey.toBuffer(), ], - extraMeta4Pubkey // PDA off of the arbitrary program ID + extraMeta4Pubkey, // PDA off of the arbitrary program ID )[0]; const extraMeta6Pubkey = PublicKey.findProgramAddressSync( [ @@ -503,7 +503,7 @@ describe('transferHook', () => { extraMeta2Pubkey.toBuffer(), extraMeta5Pubkey.toBuffer(), ], - extraMeta4Pubkey // PDA off of the arbitrary program ID + extraMeta4Pubkey, // PDA off of the arbitrary program ID )[0]; const instruction = await createTransferCheckedWithTransferHookInstruction( @@ -516,7 +516,7 @@ describe('transferHook', () => { decimals, [], undefined, - TOKEN_2022_PROGRAM_ID + TOKEN_2022_PROGRAM_ID, ); const checkMetas = [ diff --git a/token/js/test/unit/transferfee.test.ts b/token/js/test/unit/transferfee.test.ts index bfde797d778..0e9c9111ff8 100644 --- a/token/js/test/unit/transferfee.test.ts +++ b/token/js/test/unit/transferfee.test.ts @@ -19,7 +19,7 @@ describe('transferFee', () => { transferFeeConfigAuthority, withdrawWithheldAuthority, 100, - 100n + 100n, ); const decoded = decodeInitializeTransferFeeConfigInstructionUnchecked(instruction); expect(decoded.data.transferFeeConfigAuthority).to.eql(transferFeeConfigAuthority); @@ -35,7 +35,7 @@ describe('transferFee', () => { null, withdrawWithheldAuthority, 100, - 100n + 100n, ); const decoded = decodeInitializeTransferFeeConfigInstructionUnchecked(instruction); expect(decoded.data.transferFeeConfigAuthority).to.eql(null); @@ -51,7 +51,7 @@ describe('transferFee', () => { transferFeeConfigAuthority, null, 100, - 100n + 100n, ); const decoded = decodeInitializeTransferFeeConfigInstructionUnchecked(instruction); expect(decoded.data.transferFeeConfigAuthority).to.eql(transferFeeConfigAuthority); diff --git a/token/program-2022-test/Cargo.toml b/token/program-2022-test/Cargo.toml index 6350ebd285a..9524831e0d5 100644 --- a/token/program-2022-test/Cargo.toml +++ b/token/program-2022-test/Cargo.toml @@ -18,6 +18,7 @@ walkdir = "2" [dev-dependencies] async-trait = "0.1" borsh = "1.5.1" +bytemuck = "1.17.0" futures-util = "0.3" solana-program = "2.0.3" solana-program-test = "2.0.3" @@ -27,9 +28,13 @@ spl-memo = { version = "5.0.0", path = "../../memo/program", features = [ "no-entrypoint", ] } spl-pod = { version = "0.3.0", path = "../../libraries/pod" } +spl-record = { version = "0.2.0", path = "../../record/program", features = [ + "no-entrypoint", +]} spl-token-2022 = { version = "4.0.0", path = "../program-2022", features = [ "no-entrypoint", ] } +spl-token-confidential-transfer-proof-generation = { version = "0.1.0", path = "../confidential-transfer/proof-generation" } spl-instruction-padding = { version = "0.2.0", path = "../../instruction-padding/program", features = [ "no-entrypoint", ] } diff --git a/token/program-2022-test/tests/confidential_transfer.rs b/token/program-2022-test/tests/confidential_transfer.rs index bacc44c2848..64ae39c72c9 100644 --- a/token/program-2022-test/tests/confidential_transfer.rs +++ b/token/program-2022-test/tests/confidential_transfer.rs @@ -2,46 +2,47 @@ mod program_test; use { + bytemuck::Zeroable, program_test::{ - ConfidentialTokenAccountBalances, ConfidentialTokenAccountMeta, TestContext, TokenContext, + ConfidentialTokenAccountBalances, ConfidentialTokenAccountMeta, ConfidentialTransferOption, + TestContext, TokenContext, }, solana_program_test::tokio, solana_sdk::{ instruction::InstructionError, pubkey::Pubkey, signature::Signer, - signer::keypair::Keypair, - system_instruction, - transaction::{Transaction, TransactionError}, + signer::{keypair::Keypair, signers::Signers}, + transaction::TransactionError, transport::TransportError, }, + spl_record::state::RecordData, spl_token_2022::{ error::TokenError, extension::{ confidential_transfer::{ - self, - account_info::TransferAccountInfo, - instruction::{ - CloseSplitContextStateAccounts, TransferSplitContextStateAccounts, - TransferWithFeeSplitContextStateAccounts, - }, + account_info::{EmptyAccountAccountInfo, TransferAccountInfo, WithdrawAccountInfo}, ConfidentialTransferAccount, MAXIMUM_DEPOSIT_TRANSFER_AMOUNT, }, BaseStateWithExtensions, ExtensionType, }, - solana_zk_token_sdk::{ - encryption::{auth_encryption::*, elgamal::*}, - zk_token_elgamal::pod::{self, Zeroable}, - zk_token_proof_instruction::*, - zk_token_proof_program, - zk_token_proof_state::ProofContextState, + solana_zk_sdk::{ + encryption::{auth_encryption::*, elgamal::*, pod::elgamal::PodElGamalCiphertext}, + zk_elgamal_proof_program::proof_data::*, }, }, spl_token_client::{ - proof_generation::transfer_with_fee_split_proof_data, - token::{ComputeUnitLimit, ExtensionInitializationParams, TokenError as TokenClientError}, + client::ProgramBanksClientProcessTransaction, + token::{ + ExtensionInitializationParams, ProofAccount, Token, TokenError as TokenClientError, + TokenResult, + }, + }, + spl_token_confidential_transfer_proof_generation::{ + transfer::TransferProofData, transfer_with_fee::TransferWithFeeProofData, + withdraw::WithdrawProofData, }, - std::{convert::TryInto, mem::size_of}, + std::convert::TryInto, }; #[cfg(feature = "zk-ops")] @@ -49,8 +50,141 @@ const TEST_MAXIMUM_FEE: u64 = 100; #[cfg(feature = "zk-ops")] const TEST_FEE_BASIS_POINTS: u16 = 250; +async fn configure_account_with_option( + token: &Token, + account: &Pubkey, + authority: &Pubkey, + elgamal_keypair: &ElGamalKeypair, + aes_key: &AeKey, + signing_keypairs: &S, + option: ConfidentialTransferOption, +) -> TokenResult<()> { + match option { + ConfidentialTransferOption::InstructionData => { + token + .confidential_transfer_configure_token_account( + account, + authority, + None, + None, + elgamal_keypair, + aes_key, + signing_keypairs, + ) + .await + } + ConfidentialTransferOption::RecordAccount => { + let pubkey_validity_proof_data = PubkeyValidityProofData::new(elgamal_keypair).unwrap(); + + let pubkey_validity_proof_record_account = Keypair::new(); + let record_account_authority = Keypair::new(); + + token + .confidential_transfer_create_record_account( + &pubkey_validity_proof_record_account.pubkey(), + &record_account_authority.pubkey(), + &pubkey_validity_proof_data, + &pubkey_validity_proof_record_account, + &record_account_authority, + ) + .await + .unwrap(); + + let pubkey_validity_proof_account = ProofAccount::RecordAccount( + pubkey_validity_proof_record_account.pubkey(), + RecordData::WRITABLE_START_INDEX as u32, + ); + + let result = token + .confidential_transfer_configure_token_account( + account, + authority, + Some(&pubkey_validity_proof_account), + None, + elgamal_keypair, + aes_key, + signing_keypairs, + ) + .await; + + token + .confidential_transfer_close_record_account( + &pubkey_validity_proof_record_account.pubkey(), + account, + &record_account_authority.pubkey(), + &[&record_account_authority], + ) + .await + .unwrap(); + + result + } + ConfidentialTransferOption::ContextStateAccount => { + let pubkey_validity_proof_data = PubkeyValidityProofData::new(elgamal_keypair).unwrap(); + + let pubkey_validity_proof_context_account = Keypair::new(); + let context_account_authority = Keypair::new(); + + token + .confidential_transfer_create_context_state_account( + &pubkey_validity_proof_context_account.pubkey(), + &context_account_authority.pubkey(), + &pubkey_validity_proof_data, + false, + &[&pubkey_validity_proof_context_account], + ) + .await + .unwrap(); + + let pubkey_validity_proof_account = + ProofAccount::ContextAccount(pubkey_validity_proof_context_account.pubkey()); + + let result = token + .confidential_transfer_configure_token_account( + account, + authority, + Some(&pubkey_validity_proof_account), + None, + elgamal_keypair, + aes_key, + signing_keypairs, + ) + .await; + + token + .confidential_transfer_close_context_state_account( + &pubkey_validity_proof_context_account.pubkey(), + account, + &context_account_authority.pubkey(), + &[&context_account_authority], + ) + .await + .unwrap(); + + result + } + } +} + #[tokio::test] async fn confidential_transfer_configure_token_account() { + confidential_transfer_configure_token_account_with_option( + ConfidentialTransferOption::InstructionData, + ) + .await; + confidential_transfer_configure_token_account_with_option( + ConfidentialTransferOption::RecordAccount, + ) + .await; + confidential_transfer_configure_token_account_with_option( + ConfidentialTransferOption::ContextStateAccount, + ) + .await; +} + +async fn confidential_transfer_configure_token_account_with_option( + option: ConfidentialTransferOption, +) { let authority = Keypair::new(); let auto_approve_new_accounts = false; let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); @@ -69,7 +203,39 @@ async fn confidential_transfer_configure_token_account() { .unwrap(); let TokenContext { token, alice, .. } = context.token_context.unwrap(); - let alice_meta = ConfidentialTokenAccountMeta::new(&token, &alice, None, false, false).await; + let alice_account_keypair = Keypair::new(); + token + .create_auxiliary_token_account_with_extension_space( + &alice_account_keypair, + &alice.pubkey(), + vec![ExtensionType::ConfidentialTransferAccount], + ) + .await + .unwrap(); + let elgamal_keypair = + ElGamalKeypair::new_from_signer(&alice, &alice_account_keypair.pubkey().to_bytes()) + .unwrap(); + let aes_key = + AeKey::new_from_signer(&alice, &alice_account_keypair.pubkey().to_bytes()).unwrap(); + + let alice_meta = ConfidentialTokenAccountMeta { + token_account: alice_account_keypair.pubkey(), + elgamal_keypair, + aes_key, + }; + + configure_account_with_option( + &token, + &alice_meta.token_account, + &alice.pubkey(), + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + &[&alice], + option, + ) + .await + .unwrap(); + let alice_elgamal_pubkey = (*alice_meta.elgamal_keypair.pubkey()).into(); let state = token @@ -109,18 +275,17 @@ async fn confidential_transfer_configure_token_account() { assert!(bool::from(&extension.approved)); // Configuring an already initialized account should produce an error - let err = token - .confidential_transfer_configure_token_account( - &alice_meta.token_account, - &alice.pubkey(), - None, - None, - &alice_meta.elgamal_keypair, - &alice_meta.aes_key, - &[&alice], - ) - .await - .unwrap_err(); + let err = configure_account_with_option( + &token, + &alice_meta.token_account, + &alice.pubkey(), + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + &[&alice], + option, + ) + .await + .unwrap_err(); assert_eq!( err, @@ -407,8 +572,148 @@ async fn confidential_transfer_enable_disable_non_confidential_credits() { .unwrap(); } +#[cfg(feature = "zk-ops")] +async fn empty_account_with_option( + token: &Token, + account: &Pubkey, + authority: &Pubkey, + elgamal_keypair: &ElGamalKeypair, + signing_keypairs: &S, + option: ConfidentialTransferOption, +) -> TokenResult<()> { + match option { + ConfidentialTransferOption::InstructionData => { + token + .confidential_transfer_empty_account( + account, + authority, + None, + None, + elgamal_keypair, + signing_keypairs, + ) + .await + } + ConfidentialTransferOption::RecordAccount => { + let state = token.get_account_info(account).await.unwrap(); + let extension = state + .get_extension::() + .unwrap(); + let account_info = EmptyAccountAccountInfo::new(extension); + + let zero_ciphertext_proof_data = + account_info.generate_proof_data(elgamal_keypair).unwrap(); + + let zero_ciphertext_proof_record_account = Keypair::new(); + let record_account_authority = Keypair::new(); + + token + .confidential_transfer_create_record_account( + &zero_ciphertext_proof_record_account.pubkey(), + &record_account_authority.pubkey(), + &zero_ciphertext_proof_data, + &zero_ciphertext_proof_record_account, + &record_account_authority, + ) + .await + .unwrap(); + + let zero_ciphertext_account = ProofAccount::RecordAccount( + zero_ciphertext_proof_record_account.pubkey(), + RecordData::WRITABLE_START_INDEX as u32, + ); + + let result = token + .confidential_transfer_empty_account( + account, + authority, + Some(&zero_ciphertext_account), + None, + elgamal_keypair, + signing_keypairs, + ) + .await; + + token + .confidential_transfer_close_record_account( + &zero_ciphertext_proof_record_account.pubkey(), + account, + &record_account_authority.pubkey(), + &[&record_account_authority], + ) + .await + .unwrap(); + + result + } + ConfidentialTransferOption::ContextStateAccount => { + let state = token.get_account_info(account).await.unwrap(); + let extension = state + .get_extension::() + .unwrap(); + let account_info = EmptyAccountAccountInfo::new(extension); + + let zero_ciphertext_proof_data = + account_info.generate_proof_data(elgamal_keypair).unwrap(); + + let zero_ciphertext_proof_context_account = Keypair::new(); + let context_account_authority = Keypair::new(); + + token + .confidential_transfer_create_context_state_account( + &zero_ciphertext_proof_context_account.pubkey(), + &context_account_authority.pubkey(), + &zero_ciphertext_proof_data, + false, + &[&zero_ciphertext_proof_context_account], + ) + .await + .unwrap(); + + let zero_ciphertext_account = + ProofAccount::ContextAccount(zero_ciphertext_proof_context_account.pubkey()); + + let result = token + .confidential_transfer_empty_account( + account, + authority, + Some(&zero_ciphertext_account), + None, + elgamal_keypair, + signing_keypairs, + ) + .await; + + token + .confidential_transfer_close_context_state_account( + &zero_ciphertext_proof_context_account.pubkey(), + account, + &context_account_authority.pubkey(), + &[&context_account_authority], + ) + .await + .unwrap(); + + result + } + } +} + +#[cfg(feature = "zk-ops")] #[tokio::test] async fn confidential_transfer_empty_account() { + confidential_transfer_empty_account_with_option(ConfidentialTransferOption::InstructionData) + .await; + confidential_transfer_empty_account_with_option(ConfidentialTransferOption::RecordAccount) + .await; + confidential_transfer_empty_account_with_option( + ConfidentialTransferOption::ContextStateAccount, + ) + .await; +} + +#[cfg(feature = "zk-ops")] +async fn confidential_transfer_empty_account_with_option(option: ConfidentialTransferOption) { let authority = Keypair::new(); let auto_approve_new_accounts = true; let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); @@ -432,17 +737,16 @@ async fn confidential_transfer_empty_account() { let TokenContext { token, alice, .. } = context.token_context.unwrap(); let alice_meta = ConfidentialTokenAccountMeta::new(&token, &alice, None, false, false).await; - token - .confidential_transfer_empty_account( - &alice_meta.token_account, - &alice.pubkey(), - None, - None, - &alice_meta.elgamal_keypair, - &[&alice], - ) - .await - .unwrap(); + empty_account_with_option( + &token, + &alice_meta.token_account, + &alice.pubkey(), + &alice_meta.elgamal_keypair, + &[&alice], + option, + ) + .await + .unwrap(); } #[cfg(feature = "zk-ops")] @@ -495,18 +799,9 @@ async fn confidential_transfer_deposit() { assert_eq!(extension.pending_balance_credit_counter, 0.into()); assert_eq!(extension.expected_pending_balance_credit_counter, 0.into()); assert_eq!(extension.actual_pending_balance_credit_counter, 0.into()); - assert_eq!( - extension.pending_balance_lo, - pod::ElGamalCiphertext::zeroed() - ); - assert_eq!( - extension.pending_balance_hi, - pod::ElGamalCiphertext::zeroed() - ); - assert_eq!( - extension.available_balance, - pod::ElGamalCiphertext::zeroed() - ); + assert_eq!(extension.pending_balance_lo, PodElGamalCiphertext::zeroed()); + assert_eq!(extension.pending_balance_hi, PodElGamalCiphertext::zeroed()); + assert_eq!(extension.available_balance, PodElGamalCiphertext::zeroed()); token .confidential_transfer_deposit( @@ -661,9 +956,220 @@ async fn confidential_transfer_deposit() { assert_eq!(extension.actual_pending_balance_credit_counter, 2.into()); } +#[allow(clippy::too_many_arguments)] +#[cfg(feature = "zk-ops")] +async fn withdraw_with_option( + token: &Token, + source_account: &Pubkey, + source_authority: &Pubkey, + withdraw_amount: u64, + decimals: u8, + source_elgamal_keypair: &ElGamalKeypair, + source_aes_key: &AeKey, + signing_keypairs: &S, + option: ConfidentialTransferOption, +) -> TokenResult<()> { + match option { + ConfidentialTransferOption::InstructionData => { + token + .confidential_transfer_withdraw( + source_account, + source_authority, + None, + None, + withdraw_amount, + decimals, + None, + source_elgamal_keypair, + source_aes_key, + signing_keypairs, + ) + .await + } + ConfidentialTransferOption::RecordAccount => { + let state = token.get_account_info(source_account).await.unwrap(); + let extension = state + .get_extension::() + .unwrap(); + let withdraw_account_info = WithdrawAccountInfo::new(extension); + + let WithdrawProofData { + equality_proof_data, + range_proof_data, + } = withdraw_account_info + .generate_proof_data(withdraw_amount, source_elgamal_keypair, source_aes_key) + .unwrap(); + + let equality_proof_record_account = Keypair::new(); + let range_proof_record_account = Keypair::new(); + let record_account_authority = Keypair::new(); + + token + .confidential_transfer_create_record_account( + &equality_proof_record_account.pubkey(), + &record_account_authority.pubkey(), + &equality_proof_data, + &equality_proof_record_account, + &record_account_authority, + ) + .await + .unwrap(); + + let equality_proof_account = ProofAccount::RecordAccount( + equality_proof_record_account.pubkey(), + RecordData::WRITABLE_START_INDEX as u32, + ); + + token + .confidential_transfer_create_record_account( + &range_proof_record_account.pubkey(), + &record_account_authority.pubkey(), + &range_proof_data, + &range_proof_record_account, + &record_account_authority, + ) + .await + .unwrap(); + + let range_proof_account = ProofAccount::RecordAccount( + range_proof_record_account.pubkey(), + RecordData::WRITABLE_START_INDEX as u32, + ); + + let result = token + .confidential_transfer_withdraw( + source_account, + source_authority, + Some(&equality_proof_account), + Some(&range_proof_account), + withdraw_amount, + decimals, + None, + source_elgamal_keypair, + source_aes_key, + signing_keypairs, + ) + .await; + + token + .confidential_transfer_close_record_account( + &equality_proof_record_account.pubkey(), + source_account, + &record_account_authority.pubkey(), + &[&record_account_authority], + ) + .await + .unwrap(); + + token + .confidential_transfer_close_record_account( + &range_proof_record_account.pubkey(), + source_account, + &record_account_authority.pubkey(), + &[&record_account_authority], + ) + .await + .unwrap(); + + result + } + ConfidentialTransferOption::ContextStateAccount => { + let state = token.get_account_info(source_account).await.unwrap(); + let extension = state + .get_extension::() + .unwrap(); + let withdraw_account_info = WithdrawAccountInfo::new(extension); + + let WithdrawProofData { + equality_proof_data, + range_proof_data, + } = withdraw_account_info + .generate_proof_data(withdraw_amount, source_elgamal_keypair, source_aes_key) + .unwrap(); + + let equality_proof_context_account = Keypair::new(); + let range_proof_context_account = Keypair::new(); + let context_account_authority = Keypair::new(); + + token + .confidential_transfer_create_context_state_account( + &equality_proof_context_account.pubkey(), + &context_account_authority.pubkey(), + &equality_proof_data, + false, + &[&equality_proof_context_account], + ) + .await + .unwrap(); + + let equality_proof_account = + ProofAccount::ContextAccount(equality_proof_context_account.pubkey()); + + token + .confidential_transfer_create_context_state_account( + &range_proof_context_account.pubkey(), + &context_account_authority.pubkey(), + &range_proof_data, + false, + &[&range_proof_context_account], + ) + .await + .unwrap(); + + let range_proof_account = + ProofAccount::ContextAccount(range_proof_context_account.pubkey()); + + let result = token + .confidential_transfer_withdraw( + source_account, + source_authority, + Some(&equality_proof_account), + Some(&range_proof_account), + withdraw_amount, + decimals, + None, + source_elgamal_keypair, + source_aes_key, + signing_keypairs, + ) + .await; + + token + .confidential_transfer_close_context_state_account( + &equality_proof_context_account.pubkey(), + source_account, + &context_account_authority.pubkey(), + &[&context_account_authority], + ) + .await + .unwrap(); + + token + .confidential_transfer_close_context_state_account( + &range_proof_context_account.pubkey(), + source_account, + &context_account_authority.pubkey(), + &[&context_account_authority], + ) + .await + .unwrap(); + + result + } + } +} + #[cfg(feature = "zk-ops")] #[tokio::test] async fn confidential_transfer_withdraw() { + confidential_transfer_withdraw_with_option(ConfidentialTransferOption::InstructionData).await; + confidential_transfer_withdraw_with_option(ConfidentialTransferOption::RecordAccount).await; + confidential_transfer_withdraw_with_option(ConfidentialTransferOption::ContextStateAccount) + .await; +} + +#[cfg(feature = "zk-ops")] +async fn confidential_transfer_withdraw_with_option(option: ConfidentialTransferOption) { let authority = Keypair::new(); let auto_approve_new_accounts = true; let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); @@ -718,20 +1224,19 @@ async fn confidential_transfer_withdraw() { .await; // withdraw zero amount - token - .confidential_transfer_withdraw( - &alice_meta.token_account, - &alice.pubkey(), - None, - 0, - decimals, - None, - &alice_meta.elgamal_keypair, - &alice_meta.aes_key, - &[&alice], - ) - .await - .unwrap(); + withdraw_with_option( + &token, + &alice_meta.token_account, + &alice.pubkey(), + 0, + decimals, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + &[&alice], + option, + ) + .await + .unwrap(); alice_meta .check_balances( @@ -746,20 +1251,19 @@ async fn confidential_transfer_withdraw() { .await; // withdraw entire balance - token - .confidential_transfer_withdraw( - &alice_meta.token_account, - &alice.pubkey(), - None, - 42, - decimals, - None, - &alice_meta.elgamal_keypair, - &alice_meta.aes_key, - &[&alice], - ) - .await - .unwrap(); + withdraw_with_option( + &token, + &alice_meta.token_account, + &alice.pubkey(), + 42, + decimals, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + &[&alice], + option, + ) + .await + .unwrap(); let state = token .get_account_info(&alice_meta.token_account) @@ -777,63 +1281,340 @@ async fn confidential_transfer_withdraw() { }, ) .await; - - // attempt to withdraw without enough funds - let err = token - .confidential_transfer_withdraw( - &alice_meta.token_account, - &alice.pubkey(), - None, - 1, - decimals, - None, - &alice_meta.elgamal_keypair, - &alice_meta.aes_key, - &[&alice], - ) - .await - .unwrap_err(); - - assert_eq!(err, TokenClientError::ProofGeneration); - - token - .confidential_transfer_empty_account( - &alice_meta.token_account, - &alice.pubkey(), - None, - None, - &alice_meta.elgamal_keypair, - &[&alice], - ) - .await - .unwrap(); } +#[allow(clippy::too_many_arguments)] #[cfg(feature = "zk-ops")] -#[tokio::test] -async fn confidential_transfer_transfer() { - let authority = Keypair::new(); - let auto_approve_new_accounts = true; - let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); - let auditor_elgamal_pubkey = (*auditor_elgamal_keypair.pubkey()).into(); +async fn confidential_transfer_with_option( + token: &Token, + source_account: &Pubkey, + destination_account: &Pubkey, + source_authority: &Pubkey, + transfer_amount: u64, + source_elgamal_keypair: &ElGamalKeypair, + source_aes_key: &AeKey, + destination_elgamal_pubkey: &ElGamalPubkey, + auditor_elgamal_pubkey: Option<&ElGamalPubkey>, + memo: Option<(&str, Vec)>, + signing_keypairs: &S, + option: ConfidentialTransferOption, +) -> TokenResult<()> { + match option { + ConfidentialTransferOption::InstructionData => { + let transfer_token = if let Some((memo, signing_pubkey)) = memo { + token.with_memo(memo, signing_pubkey) + } else { + token + }; + + transfer_token + .confidential_transfer_transfer( + source_account, + destination_account, + source_authority, + None, + None, + None, + transfer_amount, + None, + source_elgamal_keypair, + source_aes_key, + destination_elgamal_pubkey, + auditor_elgamal_pubkey, + signing_keypairs, + ) + .await + } + ConfidentialTransferOption::RecordAccount => { + let state = token.get_account_info(source_account).await.unwrap(); + let extension = state + .get_extension::() + .unwrap(); + let transfer_account_info = TransferAccountInfo::new(extension); + + let TransferProofData { + equality_proof_data, + ciphertext_validity_proof_data, + range_proof_data, + } = transfer_account_info + .generate_split_transfer_proof_data( + transfer_amount, + source_elgamal_keypair, + source_aes_key, + destination_elgamal_pubkey, + auditor_elgamal_pubkey, + ) + .unwrap(); - let mut context = TestContext::new().await; - context - .init_token_with_mint(vec![ - ExtensionInitializationParams::ConfidentialTransferMint { - authority: Some(authority.pubkey()), - auto_approve_new_accounts, - auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), - }, - ]) - .await - .unwrap(); + let equality_proof_record_account = Keypair::new(); + let ciphertext_validity_proof_record_account = Keypair::new(); + let range_proof_record_account = Keypair::new(); + let record_account_authority = Keypair::new(); + + token + .confidential_transfer_create_record_account( + &equality_proof_record_account.pubkey(), + &record_account_authority.pubkey(), + &equality_proof_data, + &equality_proof_record_account, + &record_account_authority, + ) + .await + .unwrap(); - let TokenContext { - token, - alice, - bob, - mint_authority, + let equality_proof_account = ProofAccount::RecordAccount( + equality_proof_record_account.pubkey(), + RecordData::WRITABLE_START_INDEX as u32, + ); + + token + .confidential_transfer_create_record_account( + &ciphertext_validity_proof_record_account.pubkey(), + &record_account_authority.pubkey(), + &ciphertext_validity_proof_data, + &ciphertext_validity_proof_record_account, + &record_account_authority, + ) + .await + .unwrap(); + + let ciphertext_validity_proof_account = ProofAccount::RecordAccount( + ciphertext_validity_proof_record_account.pubkey(), + RecordData::WRITABLE_START_INDEX as u32, + ); + + token + .confidential_transfer_create_record_account( + &range_proof_record_account.pubkey(), + &record_account_authority.pubkey(), + &range_proof_data, + &range_proof_record_account, + &record_account_authority, + ) + .await + .unwrap(); + + let range_proof_account = ProofAccount::RecordAccount( + range_proof_record_account.pubkey(), + RecordData::WRITABLE_START_INDEX as u32, + ); + + let transfer_token = if let Some((memo, signing_pubkey)) = memo { + token.with_memo(memo, signing_pubkey) + } else { + token + }; + + let result = transfer_token + .confidential_transfer_transfer( + source_account, + destination_account, + source_authority, + Some(&equality_proof_account), + Some(&ciphertext_validity_proof_account), + Some(&range_proof_account), + transfer_amount, + None, + source_elgamal_keypair, + source_aes_key, + destination_elgamal_pubkey, + auditor_elgamal_pubkey, + signing_keypairs, + ) + .await; + + token + .confidential_transfer_close_record_account( + &equality_proof_record_account.pubkey(), + source_account, + &record_account_authority.pubkey(), + &[&record_account_authority], + ) + .await + .unwrap(); + + token + .confidential_transfer_close_record_account( + &ciphertext_validity_proof_record_account.pubkey(), + source_account, + &record_account_authority.pubkey(), + &[&record_account_authority], + ) + .await + .unwrap(); + + token + .confidential_transfer_close_record_account( + &range_proof_record_account.pubkey(), + source_account, + &record_account_authority.pubkey(), + &[&record_account_authority], + ) + .await + .unwrap(); + + result + } + ConfidentialTransferOption::ContextStateAccount => { + let state = token.get_account_info(source_account).await.unwrap(); + let extension = state + .get_extension::() + .unwrap(); + let transfer_account_info = TransferAccountInfo::new(extension); + + let TransferProofData { + equality_proof_data, + ciphertext_validity_proof_data, + range_proof_data, + } = transfer_account_info + .generate_split_transfer_proof_data( + transfer_amount, + source_elgamal_keypair, + source_aes_key, + destination_elgamal_pubkey, + auditor_elgamal_pubkey, + ) + .unwrap(); + + let equality_proof_context_account = Keypair::new(); + let ciphertext_validity_proof_context_account = Keypair::new(); + let range_proof_context_account = Keypair::new(); + let context_account_authority = Keypair::new(); + + token + .confidential_transfer_create_context_state_account( + &equality_proof_context_account.pubkey(), + &context_account_authority.pubkey(), + &equality_proof_data, + false, + &[&equality_proof_context_account], + ) + .await + .unwrap(); + + let equality_proof_context_proof_account = + ProofAccount::ContextAccount(equality_proof_context_account.pubkey()); + + token + .confidential_transfer_create_context_state_account( + &ciphertext_validity_proof_context_account.pubkey(), + &context_account_authority.pubkey(), + &ciphertext_validity_proof_data, + false, + &[&ciphertext_validity_proof_context_account], + ) + .await + .unwrap(); + + let ciphertext_validity_proof_context_proof_account = + ProofAccount::ContextAccount(ciphertext_validity_proof_context_account.pubkey()); + + token + .confidential_transfer_create_context_state_account( + &range_proof_context_account.pubkey(), + &context_account_authority.pubkey(), + &range_proof_data, + false, + &[&range_proof_context_account], + ) + .await + .unwrap(); + + let range_proof_context_proof_account = + ProofAccount::ContextAccount(range_proof_context_account.pubkey()); + + let transfer_token = if let Some((memo, signing_pubkey)) = memo { + token.with_memo(memo, signing_pubkey) + } else { + token + }; + + let result = transfer_token + .confidential_transfer_transfer( + source_account, + destination_account, + source_authority, + Some(&equality_proof_context_proof_account), + Some(&ciphertext_validity_proof_context_proof_account), + Some(&range_proof_context_proof_account), + transfer_amount, + None, + source_elgamal_keypair, + source_aes_key, + destination_elgamal_pubkey, + auditor_elgamal_pubkey, + signing_keypairs, + ) + .await; + + token + .confidential_transfer_close_context_state_account( + &equality_proof_context_account.pubkey(), + source_account, + &context_account_authority.pubkey(), + &[&context_account_authority], + ) + .await + .unwrap(); + + token + .confidential_transfer_close_context_state_account( + &ciphertext_validity_proof_context_account.pubkey(), + source_account, + &context_account_authority.pubkey(), + &[&context_account_authority], + ) + .await + .unwrap(); + + token + .confidential_transfer_close_context_state_account( + &range_proof_context_account.pubkey(), + source_account, + &context_account_authority.pubkey(), + &[&context_account_authority], + ) + .await + .unwrap(); + + result + } + } +} + +#[cfg(feature = "zk-ops")] +#[tokio::test] +async fn confidential_transfer_transfer() { + confidential_transfer_transfer_with_option(ConfidentialTransferOption::InstructionData).await; + confidential_transfer_transfer_with_option(ConfidentialTransferOption::RecordAccount).await; + confidential_transfer_transfer_with_option(ConfidentialTransferOption::ContextStateAccount) + .await; +} + +#[cfg(feature = "zk-ops")] +async fn confidential_transfer_transfer_with_option(option: ConfidentialTransferOption) { + let authority = Keypair::new(); + let auto_approve_new_accounts = true; + let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); + let auditor_elgamal_pubkey = (*auditor_elgamal_keypair.pubkey()).into(); + + let mut context = TestContext::new().await; + context + .init_token_with_mint(vec![ + ExtensionInitializationParams::ConfidentialTransferMint { + authority: Some(authority.pubkey()), + auto_approve_new_accounts, + auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), + }, + ]) + .await + .unwrap(); + + let TokenContext { + token, + alice, + bob, + mint_authority, decimals, .. } = context.token_context.unwrap(); @@ -853,22 +1634,22 @@ async fn confidential_transfer_transfer() { let bob_meta = ConfidentialTokenAccountMeta::new(&token, &bob, Some(2), false, false).await; // Self-transfer of 0 tokens - token - .confidential_transfer_transfer( - &alice_meta.token_account, - &alice_meta.token_account, - &alice.pubkey(), - None, - 0, - None, - &alice_meta.elgamal_keypair, - &alice_meta.aes_key, - alice_meta.elgamal_keypair.pubkey(), - Some(auditor_elgamal_keypair.pubkey()), - &[&alice], - ) - .await - .unwrap(); + confidential_transfer_with_option( + &token, + &alice_meta.token_account, + &alice_meta.token_account, + &alice.pubkey(), + 0, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + alice_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + None, + &[&alice], + option, + ) + .await + .unwrap(); alice_meta .check_balances( @@ -883,22 +1664,22 @@ async fn confidential_transfer_transfer() { .await; // Self-transfer of N tokens - token - .confidential_transfer_transfer( - &alice_meta.token_account, - &alice_meta.token_account, - &alice.pubkey(), - None, - 42, - None, - &alice_meta.elgamal_keypair, - &alice_meta.aes_key, - alice_meta.elgamal_keypair.pubkey(), - Some(auditor_elgamal_keypair.pubkey()), - &[&alice], - ) - .await - .unwrap(); + confidential_transfer_with_option( + &token, + &alice_meta.token_account, + &alice_meta.token_account, + &alice.pubkey(), + 42, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + alice_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + None, + &[&alice], + option, + ) + .await + .unwrap(); alice_meta .check_balances( @@ -936,22 +1717,22 @@ async fn confidential_transfer_transfer() { ) .await; - token - .confidential_transfer_transfer( - &alice_meta.token_account, - &bob_meta.token_account, - &alice.pubkey(), - None, - 42, - None, - &alice_meta.elgamal_keypair, - &alice_meta.aes_key, - bob_meta.elgamal_keypair.pubkey(), - Some(auditor_elgamal_keypair.pubkey()), - &[&alice], - ) - .await - .unwrap(); + confidential_transfer_with_option( + &token, + &alice_meta.token_account, + &bob_meta.token_account, + &alice.pubkey(), + 42, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + bob_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + None, + &[&alice], + option, + ) + .await + .unwrap(); alice_meta .check_balances( @@ -977,39 +1758,39 @@ async fn confidential_transfer_transfer() { ) .await; - token - .confidential_transfer_transfer( - &bob_meta.token_account, - &bob_meta.token_account, - &bob.pubkey(), - None, - 0, - None, - &bob_meta.elgamal_keypair, - &bob_meta.aes_key, - bob_meta.elgamal_keypair.pubkey(), - Some(auditor_elgamal_keypair.pubkey()), - &[&bob], - ) - .await - .unwrap(); + confidential_transfer_with_option( + &token, + &bob_meta.token_account, + &bob_meta.token_account, + &bob.pubkey(), + 0, + &bob_meta.elgamal_keypair, + &bob_meta.aes_key, + bob_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + None, + &[&bob], + option, + ) + .await + .unwrap(); - let err = token - .confidential_transfer_transfer( - &bob_meta.token_account, - &bob_meta.token_account, - &bob.pubkey(), - None, - 0, - None, - &bob_meta.elgamal_keypair, - &bob_meta.aes_key, - bob_meta.elgamal_keypair.pubkey(), - Some(auditor_elgamal_keypair.pubkey()), - &[&bob], - ) - .await - .unwrap_err(); + let err = confidential_transfer_with_option( + &token, + &bob_meta.token_account, + &bob_meta.token_account, + &bob.pubkey(), + 0, + &bob_meta.elgamal_keypair, + &bob_meta.aes_key, + bob_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + None, + &[&bob], + option, + ) + .await + .unwrap_err(); assert_eq!( err, @@ -1048,1234 +1829,488 @@ async fn confidential_transfer_transfer() { .await; } +#[allow(clippy::too_many_arguments)] #[cfg(feature = "zk-ops")] -#[tokio::test] -async fn confidential_transfer_transfer_with_fee() { - let transfer_fee_authority = Keypair::new(); - let withdraw_withheld_authority = Keypair::new(); +async fn confidential_transfer_with_fee_with_option( + token: &Token, + source_account: &Pubkey, + destination_account: &Pubkey, + source_authority: &Pubkey, + transfer_amount: u64, + source_elgamal_keypair: &ElGamalKeypair, + source_aes_key: &AeKey, + destination_elgamal_pubkey: &ElGamalPubkey, + auditor_elgamal_pubkey: Option<&ElGamalPubkey>, + withdraw_withheld_authority_elgamal_pubkey: &ElGamalPubkey, + fee_rate_basis_points: u16, + maximum_fee: u64, + memo: Option<(&str, Vec)>, + signing_keypairs: &S, + option: ConfidentialTransferOption, +) -> TokenResult<()> { + match option { + ConfidentialTransferOption::InstructionData => { + let transfer_token = if let Some((memo, signing_pubkey)) = memo { + token.with_memo(memo, signing_pubkey) + } else { + token + }; + + transfer_token + .confidential_transfer_transfer_with_fee( + source_account, + destination_account, + source_authority, + None, + None, + None, + None, + None, + transfer_amount, + None, + source_elgamal_keypair, + source_aes_key, + destination_elgamal_pubkey, + auditor_elgamal_pubkey, + withdraw_withheld_authority_elgamal_pubkey, + fee_rate_basis_points, + maximum_fee, + signing_keypairs, + ) + .await + } + ConfidentialTransferOption::RecordAccount => { + let state = token.get_account_info(source_account).await.unwrap(); + let extension = state + .get_extension::() + .unwrap(); + let transfer_account_info = TransferAccountInfo::new(extension); + + let TransferWithFeeProofData { + equality_proof_data, + transfer_amount_ciphertext_validity_proof_data, + percentage_with_cap_proof_data, + fee_ciphertext_validity_proof_data, + range_proof_data, + } = transfer_account_info + .generate_split_transfer_with_fee_proof_data( + transfer_amount, + source_elgamal_keypair, + source_aes_key, + destination_elgamal_pubkey, + auditor_elgamal_pubkey, + withdraw_withheld_authority_elgamal_pubkey, + fee_rate_basis_points, + maximum_fee, + ) + .unwrap(); - let confidential_transfer_authority = Keypair::new(); - let auto_approve_new_accounts = true; - let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); - let auditor_elgamal_pubkey = (*auditor_elgamal_keypair.pubkey()).into(); + let equality_proof_record_account = Keypair::new(); + let transfer_amount_ciphertext_validity_proof_record_account = Keypair::new(); + let fee_sigma_proof_record_account = Keypair::new(); + let fee_ciphertext_validity_proof_record_account = Keypair::new(); + let range_proof_record_account = Keypair::new(); + let record_account_authority = Keypair::new(); + + token + .confidential_transfer_create_record_account( + &equality_proof_record_account.pubkey(), + &record_account_authority.pubkey(), + &equality_proof_data, + &equality_proof_record_account, + &record_account_authority, + ) + .await + .unwrap(); - let confidential_transfer_fee_authority = Keypair::new(); - let withdraw_withheld_authority_elgamal_keypair = ElGamalKeypair::new_rand(); - let withdraw_withheld_authority_elgamal_pubkey = - (*withdraw_withheld_authority_elgamal_keypair.pubkey()).into(); + let equality_proof_account = ProofAccount::RecordAccount( + equality_proof_record_account.pubkey(), + RecordData::WRITABLE_START_INDEX as u32, + ); + + token + .confidential_transfer_create_record_account( + &transfer_amount_ciphertext_validity_proof_record_account.pubkey(), + &record_account_authority.pubkey(), + &transfer_amount_ciphertext_validity_proof_data, + &transfer_amount_ciphertext_validity_proof_record_account, + &record_account_authority, + ) + .await + .unwrap(); - let mut context = TestContext::new().await; - context - .init_token_with_mint(vec![ - ExtensionInitializationParams::TransferFeeConfig { - transfer_fee_config_authority: Some(transfer_fee_authority.pubkey()), - withdraw_withheld_authority: Some(withdraw_withheld_authority.pubkey()), - transfer_fee_basis_points: TEST_FEE_BASIS_POINTS, - maximum_fee: TEST_MAXIMUM_FEE, - }, - ExtensionInitializationParams::ConfidentialTransferMint { - authority: Some(confidential_transfer_authority.pubkey()), - auto_approve_new_accounts, - auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), - }, - ExtensionInitializationParams::ConfidentialTransferFeeConfig { - authority: Some(confidential_transfer_fee_authority.pubkey()), - withdraw_withheld_authority_elgamal_pubkey, - }, - ]) - .await - .unwrap(); + let transfer_amount_ciphertext_validity_proof_account = ProofAccount::RecordAccount( + transfer_amount_ciphertext_validity_proof_record_account.pubkey(), + RecordData::WRITABLE_START_INDEX as u32, + ); + + token + .confidential_transfer_create_record_account( + &fee_sigma_proof_record_account.pubkey(), + &record_account_authority.pubkey(), + &percentage_with_cap_proof_data, + &fee_sigma_proof_record_account, + &record_account_authority, + ) + .await + .unwrap(); - let TokenContext { - token, - alice, - bob, - mint_authority, - decimals, - .. - } = context.token_context.unwrap(); + let fee_sigma_proof_account = ProofAccount::RecordAccount( + fee_sigma_proof_record_account.pubkey(), + RecordData::WRITABLE_START_INDEX as u32, + ); + + token + .confidential_transfer_create_record_account( + &fee_ciphertext_validity_proof_record_account.pubkey(), + &record_account_authority.pubkey(), + &fee_ciphertext_validity_proof_data, + &fee_ciphertext_validity_proof_record_account, + &record_account_authority, + ) + .await + .unwrap(); - let alice_meta = ConfidentialTokenAccountMeta::new_with_tokens( - &token, - &alice, - None, - false, - true, - &mint_authority, - 100, - decimals, - ) - .await; + let fee_ciphertext_validity_proof_account = ProofAccount::RecordAccount( + fee_ciphertext_validity_proof_record_account.pubkey(), + RecordData::WRITABLE_START_INDEX as u32, + ); + + token + .confidential_transfer_create_record_account( + &range_proof_record_account.pubkey(), + &record_account_authority.pubkey(), + &range_proof_data, + &range_proof_record_account, + &record_account_authority, + ) + .await + .unwrap(); - let bob_meta = ConfidentialTokenAccountMeta::new(&token, &bob, None, false, true).await; + let range_proof_account = ProofAccount::RecordAccount( + range_proof_record_account.pubkey(), + RecordData::WRITABLE_START_INDEX as u32, + ); + + let transfer_token = if let Some((memo, signing_pubkey)) = memo { + token.with_memo(memo, signing_pubkey) + } else { + token + }; + + let result = transfer_token + .confidential_transfer_transfer_with_fee( + source_account, + destination_account, + source_authority, + Some(&equality_proof_account), + Some(&transfer_amount_ciphertext_validity_proof_account), + Some(&fee_sigma_proof_account), + Some(&fee_ciphertext_validity_proof_account), + Some(&range_proof_account), + transfer_amount, + None, + source_elgamal_keypair, + source_aes_key, + destination_elgamal_pubkey, + auditor_elgamal_pubkey, + withdraw_withheld_authority_elgamal_pubkey, + fee_rate_basis_points, + maximum_fee, + signing_keypairs, + ) + .await; + + token + .confidential_transfer_close_record_account( + &equality_proof_record_account.pubkey(), + source_account, + &record_account_authority.pubkey(), + &[&record_account_authority], + ) + .await + .unwrap(); - // Self-transfer of 0 tokens - token - .confidential_transfer_transfer_with_fee( - &alice_meta.token_account, - &alice_meta.token_account, - &alice.pubkey(), - None, - 0, - None, - &alice_meta.elgamal_keypair, - &alice_meta.aes_key, - alice_meta.elgamal_keypair.pubkey(), - Some(auditor_elgamal_keypair.pubkey()), - withdraw_withheld_authority_elgamal_keypair.pubkey(), - TEST_FEE_BASIS_POINTS, - TEST_MAXIMUM_FEE, - &[&alice], - ) - .await - .unwrap(); - - alice_meta - .check_balances( - &token, - ConfidentialTokenAccountBalances { - pending_balance_lo: 0, - pending_balance_hi: 0, - available_balance: 100, - decryptable_available_balance: 100, - }, - ) - .await; - - // Self-transfers does not incur a fee - token - .confidential_transfer_transfer_with_fee( - &alice_meta.token_account, - &alice_meta.token_account, - &alice.pubkey(), - None, - 100, - None, - &alice_meta.elgamal_keypair, - &alice_meta.aes_key, - alice_meta.elgamal_keypair.pubkey(), - Some(auditor_elgamal_keypair.pubkey()), - withdraw_withheld_authority_elgamal_keypair.pubkey(), - TEST_FEE_BASIS_POINTS, - TEST_MAXIMUM_FEE, - &[&alice], - ) - .await - .unwrap(); - - alice_meta - .check_balances( - &token, - ConfidentialTokenAccountBalances { - pending_balance_lo: 100, - pending_balance_hi: 0, - available_balance: 0, - decryptable_available_balance: 0, - }, - ) - .await; - - token - .confidential_transfer_apply_pending_balance( - &alice_meta.token_account, - &alice.pubkey(), - None, - alice_meta.elgamal_keypair.secret(), - &alice_meta.aes_key, - &[&alice], - ) - .await - .unwrap(); - - alice_meta - .check_balances( - &token, - ConfidentialTokenAccountBalances { - pending_balance_lo: 0, - pending_balance_hi: 0, - available_balance: 100, - decryptable_available_balance: 100, - }, - ) - .await; - - token - .confidential_transfer_transfer_with_fee( - &alice_meta.token_account, - &bob_meta.token_account, - &alice.pubkey(), - None, - 100, - None, - &alice_meta.elgamal_keypair, - &alice_meta.aes_key, - bob_meta.elgamal_keypair.pubkey(), - Some(auditor_elgamal_keypair.pubkey()), - withdraw_withheld_authority_elgamal_keypair.pubkey(), - TEST_FEE_BASIS_POINTS, - TEST_MAXIMUM_FEE, - &[&alice], - ) - .await - .unwrap(); - - alice_meta - .check_balances( - &token, - ConfidentialTokenAccountBalances { - pending_balance_lo: 0, - pending_balance_hi: 0, - available_balance: 0, - decryptable_available_balance: 0, - }, - ) - .await; - - token - .confidential_transfer_empty_account( - &alice_meta.token_account, - &alice.pubkey(), - None, - None, - &alice_meta.elgamal_keypair, - &[&alice], - ) - .await - .unwrap(); - - let err = token - .confidential_transfer_empty_account( - &bob_meta.token_account, - &bob.pubkey(), - None, - None, - &bob_meta.elgamal_keypair, - &[&bob], - ) - .await - .unwrap_err(); - - assert_eq!( - err, - TokenClientError::Client(Box::new(TransportError::TransactionError( - TransactionError::InstructionError( - 0, - InstructionError::Custom(TokenError::ConfidentialTransferAccountHasBalance as u32) - ) - ))) - ); - - bob_meta - .check_balances( - &token, - ConfidentialTokenAccountBalances { - pending_balance_lo: 97, - pending_balance_hi: 0, - available_balance: 0, - decryptable_available_balance: 0, - }, - ) - .await; - - token - .confidential_transfer_apply_pending_balance( - &bob_meta.token_account, - &bob.pubkey(), - None, - bob_meta.elgamal_keypair.secret(), - &bob_meta.aes_key, - &[&bob], - ) - .await - .unwrap(); - - bob_meta - .check_balances( - &token, - ConfidentialTokenAccountBalances { - pending_balance_lo: 0, - pending_balance_hi: 0, - available_balance: 97, - decryptable_available_balance: 97, - }, - ) - .await; -} - -#[cfg(feature = "zk-ops")] -#[tokio::test] -async fn confidential_transfer_transfer_memo() { - let authority = Keypair::new(); - let auto_approve_new_accounts = true; - let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); - let auditor_elgamal_pubkey = (*auditor_elgamal_keypair.pubkey()).into(); - - let mut context = TestContext::new().await; - context - .init_token_with_mint(vec![ - ExtensionInitializationParams::ConfidentialTransferMint { - authority: Some(authority.pubkey()), - auto_approve_new_accounts, - auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), - }, - ]) - .await - .unwrap(); - - let TokenContext { - token, - alice, - bob, - mint_authority, - decimals, - .. - } = context.token_context.unwrap(); - - let alice_meta = ConfidentialTokenAccountMeta::new_with_tokens( - &token, - &alice, - None, - false, - false, - &mint_authority, - 42, - decimals, - ) - .await; - - let bob_meta = ConfidentialTokenAccountMeta::new(&token, &bob, None, true, false).await; - - // transfer without memo - let err = token - .confidential_transfer_transfer( - &alice_meta.token_account, - &bob_meta.token_account, - &alice.pubkey(), - None, - 42, - None, - &alice_meta.elgamal_keypair, - &alice_meta.aes_key, - bob_meta.elgamal_keypair.pubkey(), - Some(auditor_elgamal_keypair.pubkey()), - &[&alice], - ) - .await - .unwrap_err(); - - assert_eq!( - err, - TokenClientError::Client(Box::new(TransportError::TransactionError( - TransactionError::InstructionError( - 0, - InstructionError::Custom(TokenError::NoMemo as u32) - ) - ))) - ); - - // transfer with memo - token - .with_memo("🦖", vec![alice.pubkey()]) - .confidential_transfer_transfer( - &alice_meta.token_account, - &bob_meta.token_account, - &alice.pubkey(), - None, - 42, - None, - &alice_meta.elgamal_keypair, - &alice_meta.aes_key, - bob_meta.elgamal_keypair.pubkey(), - Some(auditor_elgamal_keypair.pubkey()), - &[&alice], - ) - .await - .unwrap(); - - alice_meta - .check_balances( - &token, - ConfidentialTokenAccountBalances { - pending_balance_lo: 0, - pending_balance_hi: 0, - available_balance: 0, - decryptable_available_balance: 0, - }, - ) - .await; - - bob_meta - .check_balances( - &token, - ConfidentialTokenAccountBalances { - pending_balance_lo: 42, - pending_balance_hi: 0, - available_balance: 0, - decryptable_available_balance: 0, - }, - ) - .await; -} - -#[cfg(feature = "zk-ops")] -#[tokio::test] -async fn confidential_transfer_transfer_with_fee_and_memo() { - let transfer_fee_authority = Keypair::new(); - let withdraw_withheld_authority = Keypair::new(); - - let confidential_transfer_authority = Keypair::new(); - let auto_approve_new_accounts = true; - let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); - let auditor_elgamal_pubkey = (*auditor_elgamal_keypair.pubkey()).into(); - - let confidential_transfer_fee_authority = Keypair::new(); - let withdraw_withheld_authority_elgamal_keypair = ElGamalKeypair::new_rand(); - let withdraw_withheld_authority_elgamal_pubkey = - (*withdraw_withheld_authority_elgamal_keypair.pubkey()).into(); - - let mut context = TestContext::new().await; - context - .init_token_with_mint(vec![ - ExtensionInitializationParams::TransferFeeConfig { - transfer_fee_config_authority: Some(transfer_fee_authority.pubkey()), - withdraw_withheld_authority: Some(withdraw_withheld_authority.pubkey()), - transfer_fee_basis_points: TEST_FEE_BASIS_POINTS, - maximum_fee: TEST_MAXIMUM_FEE, - }, - ExtensionInitializationParams::ConfidentialTransferMint { - authority: Some(confidential_transfer_authority.pubkey()), - auto_approve_new_accounts, - auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), - }, - ExtensionInitializationParams::ConfidentialTransferFeeConfig { - authority: Some(confidential_transfer_fee_authority.pubkey()), - withdraw_withheld_authority_elgamal_pubkey, - }, - ]) - .await - .unwrap(); - - let TokenContext { - token, - alice, - bob, - mint_authority, - decimals, - .. - } = context.token_context.unwrap(); - - let alice_meta = ConfidentialTokenAccountMeta::new_with_tokens( - &token, - &alice, - None, - false, - true, - &mint_authority, - 100, - decimals, - ) - .await; - - let bob_meta = ConfidentialTokenAccountMeta::new(&token, &bob, None, true, true).await; - - let err = token - .confidential_transfer_transfer_with_fee( - &alice_meta.token_account, - &bob_meta.token_account, - &alice.pubkey(), - None, - 100, - None, - &alice_meta.elgamal_keypair, - &alice_meta.aes_key, - bob_meta.elgamal_keypair.pubkey(), - Some(auditor_elgamal_keypair.pubkey()), - withdraw_withheld_authority_elgamal_keypair.pubkey(), - TEST_FEE_BASIS_POINTS, - TEST_MAXIMUM_FEE, - &[&alice], - ) - .await - .unwrap_err(); - - assert_eq!( - err, - TokenClientError::Client(Box::new(TransportError::TransactionError( - TransactionError::InstructionError( - 0, - InstructionError::Custom(TokenError::NoMemo as u32) - ) - ))) - ); - - token - .with_memo("🦖", vec![alice.pubkey()]) - .confidential_transfer_transfer_with_fee( - &alice_meta.token_account, - &bob_meta.token_account, - &alice.pubkey(), - None, - 100, - None, - &alice_meta.elgamal_keypair, - &alice_meta.aes_key, - bob_meta.elgamal_keypair.pubkey(), - Some(auditor_elgamal_keypair.pubkey()), - withdraw_withheld_authority_elgamal_keypair.pubkey(), - TEST_FEE_BASIS_POINTS, - TEST_MAXIMUM_FEE, - &[&alice], - ) - .await - .unwrap(); - - alice_meta - .check_balances( - &token, - ConfidentialTokenAccountBalances { - pending_balance_lo: 0, - pending_balance_hi: 0, - available_balance: 0, - decryptable_available_balance: 0, - }, - ) - .await; - - bob_meta - .check_balances( - &token, - ConfidentialTokenAccountBalances { - pending_balance_lo: 97, - pending_balance_hi: 0, - available_balance: 0, - decryptable_available_balance: 0, - }, - ) - .await; -} - -#[tokio::test] -async fn confidential_transfer_configure_token_account_with_proof_context() { - let authority = Keypair::new(); - let auto_approve_new_accounts = false; - - let mut context = TestContext::new().await; - context - .init_token_with_mint(vec![ - ExtensionInitializationParams::ConfidentialTransferMint { - authority: Some(authority.pubkey()), - auto_approve_new_accounts, - auditor_elgamal_pubkey: None, - }, - ]) - .await - .unwrap(); - - let TokenContext { - token, alice, bob, .. - } = context.token_context.unwrap(); - - let token_account_keypair = Keypair::new(); - token - .create_auxiliary_token_account_with_extension_space( - &token_account_keypair, - &alice.pubkey(), - vec![ExtensionType::ConfidentialTransferAccount], - ) - .await - .unwrap(); - let token_account = token_account_keypair.pubkey(); - - let elgamal_keypair = - ElGamalKeypair::new_from_signer(&alice, &token_account.to_bytes()).unwrap(); - let aes_key = AeKey::new_from_signer(&alice, &token_account.to_bytes()).unwrap(); - - let context_state_account = Keypair::new(); - - // create context state - { - let context_state_authority = Keypair::new(); - let space = size_of::>(); - - let instruction_type = ProofInstruction::VerifyPubkeyValidity; - - let context_state_info = ContextStateInfo { - context_state_account: &context_state_account.pubkey(), - context_state_authority: &context_state_authority.pubkey(), - }; - - let proof_data = - confidential_transfer::instruction::PubkeyValidityData::new(&elgamal_keypair).unwrap(); - - let mut ctx = context.context.lock().await; - let rent = ctx.banks_client.get_rent().await.unwrap(); - - let instructions = vec![ - system_instruction::create_account( - &ctx.payer.pubkey(), - &context_state_account.pubkey(), - rent.minimum_balance(space), - space as u64, - &zk_token_proof_program::id(), - ), - instruction_type.encode_verify_proof(Some(context_state_info), &proof_data), - ]; - - let last_blockhash = ctx.get_new_latest_blockhash().await.unwrap(); - let tx = Transaction::new_signed_with_payer( - &instructions, - Some(&ctx.payer.pubkey()), - &[&ctx.payer, &context_state_account], - last_blockhash, - ); - ctx.banks_client.process_transaction(tx).await.unwrap(); - } - - token - .confidential_transfer_configure_token_account( - &token_account, - &alice.pubkey(), - Some(&context_state_account.pubkey()), - None, - &elgamal_keypair, - &aes_key, - &[&alice], - ) - .await - .unwrap(); - - let elgamal_pubkey = (*elgamal_keypair.pubkey()).into(); - - let state = token.get_account_info(&token_account).await.unwrap(); - let extension = state - .get_extension::() - .unwrap(); - assert!(!bool::from(&extension.approved)); - assert!(bool::from(&extension.allow_confidential_credits)); - assert_eq!(extension.elgamal_pubkey, elgamal_pubkey); - assert_eq!( - aes_key - .decrypt(&(extension.decryptable_available_balance.try_into().unwrap())) - .unwrap(), - 0 - ); - - // attempt to create an account with a wrong proof type context state - let token_account_keypair = Keypair::new(); - token - .create_auxiliary_token_account_with_extension_space( - &token_account_keypair, - &bob.pubkey(), - vec![ExtensionType::ConfidentialTransferAccount], - ) - .await - .unwrap(); - let token_account = token_account_keypair.pubkey(); - - let elgamal_keypair = ElGamalKeypair::new_from_signer(&bob, &token_account.to_bytes()).unwrap(); - let aes_key = AeKey::new_from_signer(&bob, &token_account.to_bytes()).unwrap(); - - let context_state_account = Keypair::new(); - - { - let context_state_authority = Keypair::new(); - let space = size_of::>(); - - let instruction_type = ProofInstruction::VerifyZeroBalance; - let context_state_info = ContextStateInfo { - context_state_account: &context_state_account.pubkey(), - context_state_authority: &context_state_authority.pubkey(), - }; - - let ciphertext = elgamal_keypair.pubkey().encrypt(0_u64); - let proof_data = confidential_transfer::instruction::ZeroBalanceProofData::new( - &elgamal_keypair, - &ciphertext, - ) - .unwrap(); - - let mut ctx = context.context.lock().await; - let rent = ctx.banks_client.get_rent().await.unwrap(); - - let instructions = vec![ - system_instruction::create_account( - &ctx.payer.pubkey(), - &context_state_account.pubkey(), - rent.minimum_balance(space), - space as u64, - &zk_token_proof_program::id(), - ), - instruction_type.encode_verify_proof(Some(context_state_info), &proof_data), - ]; - - let last_blockhash = ctx.get_new_latest_blockhash().await.unwrap(); - let tx = Transaction::new_signed_with_payer( - &instructions, - Some(&ctx.payer.pubkey()), - &[&ctx.payer, &context_state_account], - last_blockhash, - ); - ctx.banks_client.process_transaction(tx).await.unwrap(); - } - - let err = token - .confidential_transfer_configure_token_account( - &token_account, - &bob.pubkey(), - Some(&context_state_account.pubkey()), - None, - &elgamal_keypair, - &aes_key, - &[&bob], - ) - .await - .unwrap_err(); - - assert_eq!( - err, - TokenClientError::Client(Box::new(TransportError::TransactionError( - TransactionError::InstructionError(0, InstructionError::InvalidArgument,) - ))) - ); -} - -#[tokio::test] -async fn confidential_transfer_empty_account_with_proof_context() { - let authority = Keypair::new(); - let auto_approve_new_accounts = false; - - let mut context = TestContext::new().await; - context - .init_token_with_mint(vec![ - ExtensionInitializationParams::ConfidentialTransferMint { - authority: Some(authority.pubkey()), - auto_approve_new_accounts, - auditor_elgamal_pubkey: None, - }, - ]) - .await - .unwrap(); - - let TokenContext { - token, alice, bob, .. - } = context.token_context.unwrap(); - - let alice_meta = ConfidentialTokenAccountMeta::new(&token, &alice, None, false, false).await; - let context_state_account = Keypair::new(); - - // create context state - { - let context_state_authority = Keypair::new(); - let space = size_of::>(); - - let instruction_type = ProofInstruction::VerifyZeroBalance; - - let context_state_info = ContextStateInfo { - context_state_account: &context_state_account.pubkey(), - context_state_authority: &context_state_authority.pubkey(), - }; - - let proof_data = confidential_transfer::instruction::ZeroBalanceProofData::new( - &alice_meta.elgamal_keypair, - &ElGamalCiphertext::default(), - ) - .unwrap(); - - let mut ctx = context.context.lock().await; - let rent = ctx.banks_client.get_rent().await.unwrap(); - - let instructions = vec![ - system_instruction::create_account( - &ctx.payer.pubkey(), - &context_state_account.pubkey(), - rent.minimum_balance(space), - space as u64, - &zk_token_proof_program::id(), - ), - instruction_type.encode_verify_proof(Some(context_state_info), &proof_data), - ]; - - let last_blockhash = ctx.get_new_latest_blockhash().await.unwrap(); - let tx = Transaction::new_signed_with_payer( - &instructions, - Some(&ctx.payer.pubkey()), - &[&ctx.payer, &context_state_account], - last_blockhash, - ); - ctx.banks_client.process_transaction(tx).await.unwrap(); - } - - token - .confidential_transfer_empty_account( - &alice_meta.token_account, - &alice.pubkey(), - Some(&context_state_account.pubkey()), - None, - &alice_meta.elgamal_keypair, - &[&alice], - ) - .await - .unwrap(); - - // attempt to create an account with a wrong proof type context state - let bob_meta = ConfidentialTokenAccountMeta::new(&token, &bob, None, false, false).await; - let context_state_account = Keypair::new(); - - { - let context_state_authority = Keypair::new(); - let space = size_of::>(); - - let instruction_type = ProofInstruction::VerifyPubkeyValidity; - - let context_state_info = ContextStateInfo { - context_state_account: &context_state_account.pubkey(), - context_state_authority: &context_state_authority.pubkey(), - }; - - let proof_data = - confidential_transfer::instruction::PubkeyValidityData::new(&bob_meta.elgamal_keypair) + token + .confidential_transfer_close_record_account( + &transfer_amount_ciphertext_validity_proof_record_account.pubkey(), + source_account, + &record_account_authority.pubkey(), + &[&record_account_authority], + ) + .await .unwrap(); - let mut ctx = context.context.lock().await; - let rent = ctx.banks_client.get_rent().await.unwrap(); - - let instructions = vec![ - system_instruction::create_account( - &ctx.payer.pubkey(), - &context_state_account.pubkey(), - rent.minimum_balance(space), - space as u64, - &zk_token_proof_program::id(), - ), - instruction_type.encode_verify_proof(Some(context_state_info), &proof_data), - ]; - - let last_blockhash = ctx.get_new_latest_blockhash().await.unwrap(); - let tx = Transaction::new_signed_with_payer( - &instructions, - Some(&ctx.payer.pubkey()), - &[&ctx.payer, &context_state_account], - last_blockhash, - ); - ctx.banks_client.process_transaction(tx).await.unwrap(); - } - - let err = token - .confidential_transfer_empty_account( - &bob_meta.token_account, - &bob.pubkey(), - Some(&context_state_account.pubkey()), - None, - &bob_meta.elgamal_keypair, - &[&bob], - ) - .await - .unwrap_err(); - - assert_eq!( - err, - TokenClientError::Client(Box::new(TransportError::TransactionError( - TransactionError::InstructionError(0, InstructionError::InvalidArgument,) - ))) - ); -} - -#[tokio::test] -async fn confidential_transfer_withdraw_with_proof_context() { - let authority = Keypair::new(); - let auto_approve_new_accounts = true; - - let mut context = TestContext::new().await; - context - .init_token_with_mint(vec![ - ExtensionInitializationParams::ConfidentialTransferMint { - authority: Some(authority.pubkey()), - auto_approve_new_accounts, - auditor_elgamal_pubkey: None, - }, - ]) - .await - .unwrap(); - - let TokenContext { - token, - alice, - bob, - mint_authority, - decimals, - .. - } = context.token_context.unwrap(); - - let alice_meta = ConfidentialTokenAccountMeta::new_with_tokens( - &token, - &alice, - None, - false, - false, - &mint_authority, - 42, - decimals, - ) - .await; - - let context_state_account = Keypair::new(); - - // create context state - { - let context_state_authority = Keypair::new(); - let space = size_of::>(); - - let instruction_type = ProofInstruction::VerifyWithdraw; - - let context_state_info = ContextStateInfo { - context_state_account: &context_state_account.pubkey(), - context_state_authority: &context_state_authority.pubkey(), - }; - - let state = token - .get_account_info(&alice_meta.token_account) - .await - .unwrap(); - let extension = state - .get_extension::() - .unwrap(); - let current_ciphertext = extension.available_balance.try_into().unwrap(); - - let proof_data = confidential_transfer::instruction::WithdrawData::new( - 0, - &alice_meta.elgamal_keypair, - 42, - ¤t_ciphertext, - ) - .unwrap(); - - let mut ctx = context.context.lock().await; - let rent = ctx.banks_client.get_rent().await.unwrap(); - - let instructions = vec![ - system_instruction::create_account( - &ctx.payer.pubkey(), - &context_state_account.pubkey(), - rent.minimum_balance(space), - space as u64, - &zk_token_proof_program::id(), - ), - instruction_type.encode_verify_proof(Some(context_state_info), &proof_data), - ]; - - let last_blockhash = ctx.get_new_latest_blockhash().await.unwrap(); - let tx = Transaction::new_signed_with_payer( - &instructions, - Some(&ctx.payer.pubkey()), - &[&ctx.payer, &context_state_account], - last_blockhash, - ); - ctx.banks_client.process_transaction(tx).await.unwrap(); - } - - token - .confidential_transfer_withdraw( - &alice_meta.token_account, - &alice.pubkey(), - Some(&context_state_account.pubkey()), - 0, - decimals, - None, - &alice_meta.elgamal_keypair, - &alice_meta.aes_key, - &[&alice], - ) - .await - .unwrap(); - - // attempt to create an account with a wrong proof type context state - let bob_meta = ConfidentialTokenAccountMeta::new(&token, &bob, None, false, false).await; - let context_state_account = Keypair::new(); - - { - let context_state_authority = Keypair::new(); - let space = size_of::>(); - - let instruction_type = ProofInstruction::VerifyPubkeyValidity; - - let context_state_info = ContextStateInfo { - context_state_account: &context_state_account.pubkey(), - context_state_authority: &context_state_authority.pubkey(), - }; - - let proof_data = - confidential_transfer::instruction::PubkeyValidityData::new(&bob_meta.elgamal_keypair) + token + .confidential_transfer_close_record_account( + &fee_sigma_proof_record_account.pubkey(), + source_account, + &record_account_authority.pubkey(), + &[&record_account_authority], + ) + .await .unwrap(); - let mut ctx = context.context.lock().await; - let rent = ctx.banks_client.get_rent().await.unwrap(); - - let instructions = vec![ - system_instruction::create_account( - &ctx.payer.pubkey(), - &context_state_account.pubkey(), - rent.minimum_balance(space), - space as u64, - &zk_token_proof_program::id(), - ), - instruction_type.encode_verify_proof(Some(context_state_info), &proof_data), - ]; - - let last_blockhash = ctx.get_new_latest_blockhash().await.unwrap(); - let tx = Transaction::new_signed_with_payer( - &instructions, - Some(&ctx.payer.pubkey()), - &[&ctx.payer, &context_state_account], - last_blockhash, - ); - ctx.banks_client.process_transaction(tx).await.unwrap(); - } - - let err = token - .confidential_transfer_withdraw( - &bob_meta.token_account, - &bob.pubkey(), - Some(&context_state_account.pubkey()), - 0, - decimals, - None, - &bob_meta.elgamal_keypair, - &bob_meta.aes_key, - &[&bob], - ) - .await - .unwrap_err(); - - assert_eq!( - err, - TokenClientError::Client(Box::new(TransportError::TransactionError( - TransactionError::InstructionError(0, InstructionError::InvalidArgument,) - ))) - ); -} - -#[tokio::test] -async fn confidential_transfer_transfer_with_proof_context() { - let authority = Keypair::new(); - let auto_approve_new_accounts = true; - let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); - let auditor_elgamal_pubkey = (*auditor_elgamal_keypair.pubkey()).into(); - - let mut context = TestContext::new().await; - context - .init_token_with_mint(vec![ - ExtensionInitializationParams::ConfidentialTransferMint { - authority: Some(authority.pubkey()), - auto_approve_new_accounts, - auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), - }, - ]) - .await - .unwrap(); - - let TokenContext { - token, - alice, - bob, - mint_authority, - decimals, - .. - } = context.token_context.unwrap(); - - let alice_meta = ConfidentialTokenAccountMeta::new_with_tokens( - &token, - &alice, - None, - false, - false, - &mint_authority, - 42, - decimals, - ) - .await; - - let bob_meta = ConfidentialTokenAccountMeta::new_with_tokens( - &token, - &bob, - None, - false, - false, - &mint_authority, - 0, - decimals, - ) - .await; + token + .confidential_transfer_close_record_account( + &fee_ciphertext_validity_proof_record_account.pubkey(), + source_account, + &record_account_authority.pubkey(), + &[&record_account_authority], + ) + .await + .unwrap(); - let context_state_account = Keypair::new(); + token + .confidential_transfer_close_record_account( + &range_proof_record_account.pubkey(), + source_account, + &record_account_authority.pubkey(), + &[&record_account_authority], + ) + .await + .unwrap(); - // create context state - { - let context_state_authority = Keypair::new(); - let space = size_of::>(); + result + } + ConfidentialTransferOption::ContextStateAccount => { + let state = token.get_account_info(source_account).await.unwrap(); + let extension = state + .get_extension::() + .unwrap(); + let transfer_account_info = TransferAccountInfo::new(extension); + + let TransferWithFeeProofData { + equality_proof_data, + transfer_amount_ciphertext_validity_proof_data, + percentage_with_cap_proof_data, + fee_ciphertext_validity_proof_data, + range_proof_data, + } = transfer_account_info + .generate_split_transfer_with_fee_proof_data( + transfer_amount, + source_elgamal_keypair, + source_aes_key, + destination_elgamal_pubkey, + auditor_elgamal_pubkey, + withdraw_withheld_authority_elgamal_pubkey, + fee_rate_basis_points, + maximum_fee, + ) + .unwrap(); - let instruction_type = ProofInstruction::VerifyTransfer; + let equality_proof_context_account = Keypair::new(); + let transfer_amount_ciphertext_validity_proof_context_account = Keypair::new(); + let percentage_with_cap_proof_context_account = Keypair::new(); + let fee_ciphertext_validity_proof_context_account = Keypair::new(); + let range_proof_context_account = Keypair::new(); + let context_account_authority = Keypair::new(); + + token + .confidential_transfer_create_context_state_account( + &equality_proof_context_account.pubkey(), + &context_account_authority.pubkey(), + &equality_proof_data, + false, + &[&equality_proof_context_account], + ) + .await + .unwrap(); - let context_state_info = ContextStateInfo { - context_state_account: &context_state_account.pubkey(), - context_state_authority: &context_state_authority.pubkey(), - }; + let equality_proof_context_proof_account = + ProofAccount::ContextAccount(equality_proof_context_account.pubkey()); - let state = token - .get_account_info(&alice_meta.token_account) - .await - .unwrap(); - let extension = state - .get_extension::() - .unwrap(); - let current_available_balance = extension.available_balance.try_into().unwrap(); + token + .confidential_transfer_create_context_state_account( + &transfer_amount_ciphertext_validity_proof_context_account.pubkey(), + &context_account_authority.pubkey(), + &transfer_amount_ciphertext_validity_proof_data, + false, + &[&transfer_amount_ciphertext_validity_proof_context_account], + ) + .await + .unwrap(); - let proof_data = confidential_transfer::instruction::TransferData::new( - 42, - (42, ¤t_available_balance), - &alice_meta.elgamal_keypair, - ( - bob_meta.elgamal_keypair.pubkey(), - auditor_elgamal_keypair.pubkey(), - ), - ) - .unwrap(); + let transfer_amount_ciphertext_validity_proof_context_proof_account = + ProofAccount::ContextAccount( + transfer_amount_ciphertext_validity_proof_context_account.pubkey(), + ); + + token + .confidential_transfer_create_context_state_account( + &percentage_with_cap_proof_context_account.pubkey(), + &context_account_authority.pubkey(), + &percentage_with_cap_proof_data, + false, + &[&percentage_with_cap_proof_context_account], + ) + .await + .unwrap(); - let mut ctx = context.context.lock().await; - let rent = ctx.banks_client.get_rent().await.unwrap(); - - let instructions = vec![ - system_instruction::create_account( - &ctx.payer.pubkey(), - &context_state_account.pubkey(), - rent.minimum_balance(space), - space as u64, - &zk_token_proof_program::id(), - ), - instruction_type.encode_verify_proof(Some(context_state_info), &proof_data), - ]; - - let last_blockhash = ctx.get_new_latest_blockhash().await.unwrap(); - let tx = Transaction::new_signed_with_payer( - &instructions, - Some(&ctx.payer.pubkey()), - &[&ctx.payer, &context_state_account], - last_blockhash, - ); - ctx.banks_client.process_transaction(tx).await.unwrap(); - } + let fee_sigma_proof_context_proof_account = + ProofAccount::ContextAccount(percentage_with_cap_proof_context_account.pubkey()); - token - .confidential_transfer_transfer( - &alice_meta.token_account, - &bob_meta.token_account, - &alice.pubkey(), - Some(&context_state_account.pubkey()), - 42, - None, - &alice_meta.elgamal_keypair, - &alice_meta.aes_key, - bob_meta.elgamal_keypair.pubkey(), - Some(auditor_elgamal_keypair.pubkey()), - &[&alice], - ) - .await - .unwrap(); + token + .confidential_transfer_create_context_state_account( + &fee_ciphertext_validity_proof_context_account.pubkey(), + &context_account_authority.pubkey(), + &fee_ciphertext_validity_proof_data, + false, + &[&fee_ciphertext_validity_proof_context_account], + ) + .await + .unwrap(); - // attempt to create an account with a wrong proof type context state - let context_state_account = Keypair::new(); + let fee_ciphertext_validity_proof_context_proof_account = ProofAccount::ContextAccount( + fee_ciphertext_validity_proof_context_account.pubkey(), + ); + + token + .confidential_transfer_create_context_state_account( + &range_proof_context_account.pubkey(), + &context_account_authority.pubkey(), + &range_proof_data, + false, + &[&range_proof_context_account], + ) + .await + .unwrap(); - { - let context_state_authority = Keypair::new(); - let space = size_of::>(); + let range_proof_context_proof_account = + ProofAccount::ContextAccount(range_proof_context_account.pubkey()); + + let transfer_token = if let Some((memo, signing_pubkey)) = memo { + token.with_memo(memo, signing_pubkey) + } else { + token + }; + + let result = transfer_token + .confidential_transfer_transfer_with_fee( + source_account, + destination_account, + source_authority, + Some(&equality_proof_context_proof_account), + Some(&transfer_amount_ciphertext_validity_proof_context_proof_account), + Some(&fee_sigma_proof_context_proof_account), + Some(&fee_ciphertext_validity_proof_context_proof_account), + Some(&range_proof_context_proof_account), + transfer_amount, + None, + source_elgamal_keypair, + source_aes_key, + destination_elgamal_pubkey, + auditor_elgamal_pubkey, + withdraw_withheld_authority_elgamal_pubkey, + fee_rate_basis_points, + maximum_fee, + signing_keypairs, + ) + .await; + + token + .confidential_transfer_close_context_state_account( + &equality_proof_context_account.pubkey(), + source_account, + &context_account_authority.pubkey(), + &[&context_account_authority], + ) + .await + .unwrap(); - let instruction_type = ProofInstruction::VerifyWithdraw; + token + .confidential_transfer_close_context_state_account( + &transfer_amount_ciphertext_validity_proof_context_account.pubkey(), + source_account, + &context_account_authority.pubkey(), + &[&context_account_authority], + ) + .await + .unwrap(); - let context_state_info = ContextStateInfo { - context_state_account: &context_state_account.pubkey(), - context_state_authority: &context_state_authority.pubkey(), - }; + token + .confidential_transfer_close_context_state_account( + &percentage_with_cap_proof_context_account.pubkey(), + source_account, + &context_account_authority.pubkey(), + &[&context_account_authority], + ) + .await + .unwrap(); - let state = token - .get_account_info(&alice_meta.token_account) - .await - .unwrap(); - let extension = state - .get_extension::() - .unwrap(); - let current_ciphertext = extension.available_balance.try_into().unwrap(); + token + .confidential_transfer_close_context_state_account( + &fee_ciphertext_validity_proof_context_account.pubkey(), + source_account, + &context_account_authority.pubkey(), + &[&context_account_authority], + ) + .await + .unwrap(); - let proof_data = confidential_transfer::instruction::WithdrawData::new( - 0, - &alice_meta.elgamal_keypair, - 0, - ¤t_ciphertext, - ) - .unwrap(); + token + .confidential_transfer_close_context_state_account( + &range_proof_context_account.pubkey(), + source_account, + &context_account_authority.pubkey(), + &[&context_account_authority], + ) + .await + .unwrap(); - let mut ctx = context.context.lock().await; - let rent = ctx.banks_client.get_rent().await.unwrap(); - - let instructions = vec![ - system_instruction::create_account( - &ctx.payer.pubkey(), - &context_state_account.pubkey(), - rent.minimum_balance(space), - space as u64, - &zk_token_proof_program::id(), - ), - instruction_type.encode_verify_proof(Some(context_state_info), &proof_data), - ]; - - let last_blockhash = ctx.get_new_latest_blockhash().await.unwrap(); - let tx = Transaction::new_signed_with_payer( - &instructions, - Some(&ctx.payer.pubkey()), - &[&ctx.payer, &context_state_account], - last_blockhash, - ); - ctx.banks_client.process_transaction(tx).await.unwrap(); + result + } } +} - let err = token - .confidential_transfer_transfer( - &alice_meta.token_account, - &bob_meta.token_account, - &alice.pubkey(), - Some(&context_state_account.pubkey()), - 0, - None, - &alice_meta.elgamal_keypair, - &alice_meta.aes_key, - bob_meta.elgamal_keypair.pubkey(), - Some(auditor_elgamal_keypair.pubkey()), - &[&alice], - ) - .await - .unwrap_err(); - - assert_eq!( - err, - TokenClientError::Client(Box::new(TransportError::TransactionError( - TransactionError::InstructionError(0, InstructionError::InvalidArgument,) - ))) +#[cfg(feature = "zk-ops")] +#[tokio::test] +async fn confidential_transfer_transfer_with_fee() { + confidential_transfer_transfer_with_fee_with_option( + ConfidentialTransferOption::InstructionData, + ) + .await; + confidential_transfer_transfer_with_fee_with_option(ConfidentialTransferOption::RecordAccount) + .await; + confidential_transfer_transfer_with_fee_with_option( + ConfidentialTransferOption::ContextStateAccount, ) + .await; } -#[tokio::test] -async fn confidential_transfer_transfer_with_split_proof_context() { - let authority = Keypair::new(); +#[cfg(feature = "zk-ops")] +async fn confidential_transfer_transfer_with_fee_with_option(option: ConfidentialTransferOption) { + let transfer_fee_authority = Keypair::new(); + let withdraw_withheld_authority = Keypair::new(); + + let confidential_transfer_authority = Keypair::new(); let auto_approve_new_accounts = true; let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); let auditor_elgamal_pubkey = (*auditor_elgamal_keypair.pubkey()).into(); + let confidential_transfer_fee_authority = Keypair::new(); + let withdraw_withheld_authority_elgamal_keypair = ElGamalKeypair::new_rand(); + let withdraw_withheld_authority_elgamal_pubkey = + (*withdraw_withheld_authority_elgamal_keypair.pubkey()).into(); + let mut context = TestContext::new().await; context .init_token_with_mint(vec![ + ExtensionInitializationParams::TransferFeeConfig { + transfer_fee_config_authority: Some(transfer_fee_authority.pubkey()), + withdraw_withheld_authority: Some(withdraw_withheld_authority.pubkey()), + transfer_fee_basis_points: TEST_FEE_BASIS_POINTS, + maximum_fee: TEST_MAXIMUM_FEE, + }, ExtensionInitializationParams::ConfidentialTransferMint { - authority: Some(authority.pubkey()), + authority: Some(confidential_transfer_authority.pubkey()), auto_approve_new_accounts, auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), }, + ExtensionInitializationParams::ConfidentialTransferFeeConfig { + authority: Some(confidential_transfer_fee_authority.pubkey()), + withdraw_withheld_authority_elgamal_pubkey, + }, ]) .await .unwrap(); @@ -2294,270 +2329,176 @@ async fn confidential_transfer_transfer_with_split_proof_context() { &alice, None, false, - false, + true, &mint_authority, - 42, + 100, decimals, ) .await; - let bob_meta = ConfidentialTokenAccountMeta::new_with_tokens( + let bob_meta = ConfidentialTokenAccountMeta::new(&token, &bob, None, false, true).await; + + // Self-transfer of 0 tokens + confidential_transfer_with_fee_with_option( &token, - &bob, - None, - false, - false, - &mint_authority, + &alice_meta.token_account, + &alice_meta.token_account, + &alice.pubkey(), 0, - decimals, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + alice_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + withdraw_withheld_authority_elgamal_keypair.pubkey(), + TEST_FEE_BASIS_POINTS, + TEST_MAXIMUM_FEE, + None, + &[&alice], + option, ) - .await; + .await + .unwrap(); - let state = token - .get_account_info(&alice_meta.token_account) - .await - .unwrap(); - let extension = state - .get_extension::() - .unwrap(); - let transfer_account_info = TransferAccountInfo::new(extension); - - let ( - equality_proof_data, - ciphertext_validity_proof_data, - range_proof_data, - source_decrypt_handles, - ) = transfer_account_info - .generate_split_transfer_proof_data( - 42, - &alice_meta.elgamal_keypair, - &alice_meta.aes_key, - bob_meta.elgamal_keypair.pubkey(), - Some(auditor_elgamal_keypair.pubkey()), + alice_meta + .check_balances( + &token, + ConfidentialTokenAccountBalances { + pending_balance_lo: 0, + pending_balance_hi: 0, + available_balance: 100, + decryptable_available_balance: 100, + }, ) - .unwrap(); - - let context_state_authority = Keypair::new(); - let equality_proof_context_state_account = Keypair::new(); - let ciphertext_validity_proof_context_state_account = Keypair::new(); - let range_proof_context_state_account = Keypair::new(); - - let transfer_context_state_accounts = TransferSplitContextStateAccounts { - equality_proof: &equality_proof_context_state_account.pubkey(), - ciphertext_validity_proof: &ciphertext_validity_proof_context_state_account.pubkey(), - range_proof: &range_proof_context_state_account.pubkey(), - authority: &context_state_authority.pubkey(), - no_op_on_uninitialized_split_context_state: false, - close_split_context_state_accounts: None, - }; + .await; - // create context state accounts - token - .create_equality_and_ciphertext_validity_proof_context_states_for_transfer( - transfer_context_state_accounts, - &equality_proof_data, - &ciphertext_validity_proof_data, - &[ - &equality_proof_context_state_account, - &ciphertext_validity_proof_context_state_account, - ], - ) - .await - .unwrap(); + // Self-transfers does not incur a fee + confidential_transfer_with_fee_with_option( + &token, + &alice_meta.token_account, + &alice_meta.token_account, + &alice.pubkey(), + 100, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + alice_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + withdraw_withheld_authority_elgamal_keypair.pubkey(), + TEST_FEE_BASIS_POINTS, + TEST_MAXIMUM_FEE, + None, + &[&alice], + option, + ) + .await + .unwrap(); - token - .create_range_proof_context_state_for_transfer( - transfer_context_state_accounts, - &range_proof_data, - &range_proof_context_state_account, + alice_meta + .check_balances( + &token, + ConfidentialTokenAccountBalances { + pending_balance_lo: 100, + pending_balance_hi: 0, + available_balance: 0, + decryptable_available_balance: 0, + }, ) - .await - .unwrap(); + .await; - // create token22 confidential transfer instruction token - .confidential_transfer_transfer_with_split_proofs( + .confidential_transfer_apply_pending_balance( &alice_meta.token_account, - &bob_meta.token_account, &alice.pubkey(), - transfer_context_state_accounts, - 42, None, + alice_meta.elgamal_keypair.secret(), &alice_meta.aes_key, - &source_decrypt_handles, &[&alice], ) .await .unwrap(); - // close context state accounts - token - .confidential_transfer_close_context_state( - &equality_proof_context_state_account.pubkey(), - &alice_meta.token_account, - &context_state_authority.pubkey(), - &[&context_state_authority], - ) - .await - .unwrap(); - - token - .confidential_transfer_close_context_state( - &ciphertext_validity_proof_context_state_account.pubkey(), - &alice_meta.token_account, - &context_state_authority.pubkey(), - &[&context_state_authority], - ) - .await - .unwrap(); - - token - .confidential_transfer_close_context_state( - &range_proof_context_state_account.pubkey(), - &alice_meta.token_account, - &context_state_authority.pubkey(), - &[&context_state_authority], - ) - .await - .unwrap(); - - // check balances alice_meta .check_balances( &token, ConfidentialTokenAccountBalances { pending_balance_lo: 0, pending_balance_hi: 0, - available_balance: 0, - decryptable_available_balance: 0, + available_balance: 100, + decryptable_available_balance: 100, }, ) .await; - bob_meta + confidential_transfer_with_fee_with_option( + &token, + &alice_meta.token_account, + &bob_meta.token_account, + &alice.pubkey(), + 100, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + bob_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + withdraw_withheld_authority_elgamal_keypair.pubkey(), + TEST_FEE_BASIS_POINTS, + TEST_MAXIMUM_FEE, + None, + &[&alice], + option, + ) + .await + .unwrap(); + + alice_meta .check_balances( &token, ConfidentialTokenAccountBalances { - pending_balance_lo: 42, + pending_balance_lo: 0, pending_balance_hi: 0, available_balance: 0, decryptable_available_balance: 0, }, ) .await; -} - -#[tokio::test] -async fn confidential_transfer_transfer_with_split_proof_contexts_in_parallel() { - let authority = Keypair::new(); - let auto_approve_new_accounts = true; - let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); - let auditor_elgamal_pubkey = (*auditor_elgamal_keypair.pubkey()).into(); - - let mut context = TestContext::new().await; - context - .init_token_with_mint(vec![ - ExtensionInitializationParams::ConfidentialTransferMint { - authority: Some(authority.pubkey()), - auto_approve_new_accounts, - auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), - }, - ]) - .await - .unwrap(); - - let TokenContext { - token, - alice, - bob, - mint_authority, - decimals, - .. - } = context.token_context.unwrap(); - - let alice_meta = ConfidentialTokenAccountMeta::new_with_tokens( - &token, - &alice, - None, - false, - false, - &mint_authority, - 42, - decimals, - ) - .await; - - let bob_meta = ConfidentialTokenAccountMeta::new_with_tokens( - &token, - &bob, - None, - false, - false, - &mint_authority, - 0, - decimals, - ) - .await; - let context_state_authority = Keypair::new(); - let equality_proof_context_state_account = Keypair::new(); - let ciphertext_validity_proof_context_state_account = Keypair::new(); - let range_proof_context_state_account = Keypair::new(); - - let lamport_destination = Pubkey::new_unique(); - - let close_split_context_state_accounts = CloseSplitContextStateAccounts { - lamport_destination: &lamport_destination, - zk_token_proof_program: &zk_token_proof_program::id(), - }; - - let transfer_context_state_accounts = TransferSplitContextStateAccounts { - equality_proof: &equality_proof_context_state_account.pubkey(), - ciphertext_validity_proof: &ciphertext_validity_proof_context_state_account.pubkey(), - range_proof: &range_proof_context_state_account.pubkey(), - authority: &context_state_authority.pubkey(), - no_op_on_uninitialized_split_context_state: true, - close_split_context_state_accounts: Some(close_split_context_state_accounts), - }; - - let equality_and_ciphertext_proof_signers = vec![ - &alice, - &equality_proof_context_state_account, - &ciphertext_validity_proof_context_state_account, - &context_state_authority, - ]; - let range_proof_signers = vec![ - &alice, - &range_proof_context_state_account, - &context_state_authority, - ]; - // With split proofs in parallel, one of the transactions does more work - // than the other, which isn't caught during the simulation to discover the - // compute unit limit. - let token = token.with_compute_unit_limit(ComputeUnitLimit::Static(500_000)); token - .confidential_transfer_transfer_with_split_proofs_in_parallel( + .confidential_transfer_empty_account( &alice_meta.token_account, + &alice.pubkey(), + None, + None, + &alice_meta.elgamal_keypair, + &[&alice], + ) + .await + .unwrap(); + + let err = token + .confidential_transfer_empty_account( &bob_meta.token_account, - &alice.pubkey(), - transfer_context_state_accounts, - 42, + &bob.pubkey(), None, - &alice_meta.elgamal_keypair, - &alice_meta.aes_key, - bob_meta.elgamal_keypair.pubkey(), - Some(auditor_elgamal_keypair.pubkey()), - &equality_and_ciphertext_proof_signers, - &range_proof_signers, + None, + &bob_meta.elgamal_keypair, + &[&bob], ) .await - .unwrap(); + .unwrap_err(); - alice_meta + assert_eq!( + err, + TokenClientError::Client(Box::new(TransportError::TransactionError( + TransactionError::InstructionError( + 0, + InstructionError::Custom(TokenError::ConfidentialTransferAccountHasBalance as u32) + ) + ))) + ); + + bob_meta .check_balances( &token, ConfidentialTokenAccountBalances { - pending_balance_lo: 0, + pending_balance_lo: 97, pending_balance_hi: 0, available_balance: 0, decryptable_available_balance: 0, @@ -2565,73 +2506,59 @@ async fn confidential_transfer_transfer_with_split_proof_contexts_in_parallel() ) .await; + token + .confidential_transfer_apply_pending_balance( + &bob_meta.token_account, + &bob.pubkey(), + None, + bob_meta.elgamal_keypair.secret(), + &bob_meta.aes_key, + &[&bob], + ) + .await + .unwrap(); + bob_meta .check_balances( &token, ConfidentialTokenAccountBalances { - pending_balance_lo: 42, + pending_balance_lo: 0, pending_balance_hi: 0, - available_balance: 0, - decryptable_available_balance: 0, + available_balance: 97, + decryptable_available_balance: 97, }, ) .await; - - let error = token - .get_account(equality_proof_context_state_account.pubkey()) - .await - .unwrap_err(); - assert_eq!(error, TokenClientError::AccountNotFound); - - let error = token - .get_account(ciphertext_validity_proof_context_state_account.pubkey()) - .await - .unwrap_err(); - assert_eq!(error, TokenClientError::AccountNotFound); - - let error = token - .get_account(range_proof_context_state_account.pubkey()) - .await - .unwrap_err(); - assert_eq!(error, TokenClientError::AccountNotFound); - - let lamport_destination = token.get_account(lamport_destination).await.unwrap(); - assert!(lamport_destination.lamports > 0); } +#[cfg(feature = "zk-ops")] #[tokio::test] -async fn confidential_transfer_transfer_with_fee_and_split_proof_context() { - let transfer_fee_authority = Keypair::new(); - let withdraw_withheld_authority = Keypair::new(); +async fn confidential_transfer_transfer_memo() { + confidential_transfer_transfer_memo_with_option(ConfidentialTransferOption::InstructionData) + .await; + confidential_transfer_transfer_memo_with_option(ConfidentialTransferOption::RecordAccount) + .await; + confidential_transfer_transfer_memo_with_option( + ConfidentialTransferOption::ContextStateAccount, + ) + .await; +} - let confidential_transfer_authority = Keypair::new(); +#[cfg(feature = "zk-ops")] +async fn confidential_transfer_transfer_memo_with_option(option: ConfidentialTransferOption) { + let authority = Keypair::new(); let auto_approve_new_accounts = true; let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); let auditor_elgamal_pubkey = (*auditor_elgamal_keypair.pubkey()).into(); - let confidential_transfer_fee_authority = Keypair::new(); - let withdraw_withheld_authority_elgamal_keypair = ElGamalKeypair::new_rand(); - let withdraw_withheld_authority_elgamal_pubkey = - (*withdraw_withheld_authority_elgamal_keypair.pubkey()).into(); - let mut context = TestContext::new().await; context .init_token_with_mint(vec![ - ExtensionInitializationParams::TransferFeeConfig { - transfer_fee_config_authority: Some(transfer_fee_authority.pubkey()), - withdraw_withheld_authority: Some(withdraw_withheld_authority.pubkey()), - transfer_fee_basis_points: TEST_FEE_BASIS_POINTS, - maximum_fee: TEST_MAXIMUM_FEE, - }, ExtensionInitializationParams::ConfidentialTransferMint { - authority: Some(confidential_transfer_authority.pubkey()), + authority: Some(authority.pubkey()), auto_approve_new_accounts, auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), }, - ExtensionInitializationParams::ConfidentialTransferFeeConfig { - authority: Some(confidential_transfer_fee_authority.pubkey()), - withdraw_withheld_authority_elgamal_pubkey, - }, ]) .await .unwrap(); @@ -2650,180 +2577,61 @@ async fn confidential_transfer_transfer_with_fee_and_split_proof_context() { &alice, None, false, - true, + false, &mint_authority, - 100, + 42, decimals, ) .await; - let bob_meta = ConfidentialTokenAccountMeta::new(&token, &bob, None, false, true).await; - - let state = token - .get_account_info(&alice_meta.token_account) - .await - .unwrap(); - let extension = state - .get_extension::() - .unwrap(); - let transfer_account_info = TransferAccountInfo::new(extension); + let bob_meta = ConfidentialTokenAccountMeta::new(&token, &bob, None, true, false).await; - let current_source_available_balance = - transfer_account_info.available_balance.try_into().unwrap(); - let current_decryptable_available_balance = transfer_account_info - .decryptable_available_balance - .try_into() - .unwrap(); + // transfer without memo + let err = confidential_transfer_with_option( + &token, + &alice_meta.token_account, + &bob_meta.token_account, + &alice.pubkey(), + 42, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + bob_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + None, + &[&alice], + option, + ) + .await + .unwrap_err(); - let fee_parameters = FeeParameters { - fee_rate_basis_points: TEST_FEE_BASIS_POINTS, - maximum_fee: TEST_MAXIMUM_FEE, - }; + assert_eq!( + err, + TokenClientError::Client(Box::new(TransportError::TransactionError( + TransactionError::InstructionError( + 0, + InstructionError::Custom(TokenError::NoMemo as u32) + ) + ))) + ); - let ( - equality_proof_data, - transfer_amount_ciphertext_validity_proof_data, - fee_sigma_proof_data, - fee_ciphertext_validity_proof_data, - range_proof_data, - source_decrypt_handles, - ) = transfer_with_fee_split_proof_data( - ¤t_source_available_balance, - ¤t_decryptable_available_balance, - 100, + // transfer with memo + confidential_transfer_with_option( + &token, + &alice_meta.token_account, + &bob_meta.token_account, + &alice.pubkey(), + 42, &alice_meta.elgamal_keypair, &alice_meta.aes_key, bob_meta.elgamal_keypair.pubkey(), Some(auditor_elgamal_keypair.pubkey()), - withdraw_withheld_authority_elgamal_keypair.pubkey(), - &fee_parameters, + Some(("🦖", vec![alice.pubkey()])), + &[&alice], + option, ) + .await .unwrap(); - let context_state_authority = Keypair::new(); - let equality_proof_context_state_account = Keypair::new(); - let transfer_amount_ciphertext_validity_proof_context_state_account = Keypair::new(); - let fee_sigma_proof_context_state_account = Keypair::new(); - let fee_ciphertext_validity_proof_context_state_account = Keypair::new(); - let range_proof_context_state_account = Keypair::new(); - - let transfer_context_state_accounts = TransferWithFeeSplitContextStateAccounts { - equality_proof: &equality_proof_context_state_account.pubkey(), - transfer_amount_ciphertext_validity_proof: - &transfer_amount_ciphertext_validity_proof_context_state_account.pubkey(), - fee_sigma_proof: &fee_sigma_proof_context_state_account.pubkey(), - fee_ciphertext_validity_proof: &fee_ciphertext_validity_proof_context_state_account - .pubkey(), - range_proof: &range_proof_context_state_account.pubkey(), - authority: &context_state_authority.pubkey(), - no_op_on_uninitialized_split_context_state: false, - close_split_context_state_accounts: None, - }; - - // create context state accounts - token - .create_equality_and_ciphertext_validity_proof_context_states_for_transfer_with_fee( - transfer_context_state_accounts, - &equality_proof_data, - &transfer_amount_ciphertext_validity_proof_data, - &[ - &equality_proof_context_state_account, - &transfer_amount_ciphertext_validity_proof_context_state_account, - ], - ) - .await - .unwrap(); - - token - .create_fee_sigma_and_ciphertext_validity_proof_context_states_for_transfer_with_fee( - transfer_context_state_accounts, - &fee_sigma_proof_data, - &fee_ciphertext_validity_proof_data, - &[ - &fee_sigma_proof_context_state_account, - &fee_ciphertext_validity_proof_context_state_account, - ], - ) - .await - .unwrap(); - - token - .create_range_proof_context_state_for_transfer_with_fee( - transfer_context_state_accounts, - &range_proof_data, - &[&range_proof_context_state_account], - ) - .await - .unwrap(); - - // create token22 confidential transfer instruction - token - .confidential_transfer_transfer_with_fee_and_split_proofs( - &alice_meta.token_account, - &bob_meta.token_account, - &alice.pubkey(), - transfer_context_state_accounts, - 100, - None, - &alice_meta.aes_key, - &source_decrypt_handles, - &[&alice], - ) - .await - .unwrap(); - - // close context state accounts - token - .confidential_transfer_close_context_state( - &equality_proof_context_state_account.pubkey(), - &alice_meta.token_account, - &context_state_authority.pubkey(), - &[&context_state_authority], - ) - .await - .unwrap(); - - token - .confidential_transfer_close_context_state( - &transfer_amount_ciphertext_validity_proof_context_state_account.pubkey(), - &alice_meta.token_account, - &context_state_authority.pubkey(), - &[&context_state_authority], - ) - .await - .unwrap(); - - token - .confidential_transfer_close_context_state( - &fee_sigma_proof_context_state_account.pubkey(), - &alice_meta.token_account, - &context_state_authority.pubkey(), - &[&context_state_authority], - ) - .await - .unwrap(); - - token - .confidential_transfer_close_context_state( - &fee_ciphertext_validity_proof_context_state_account.pubkey(), - &alice_meta.token_account, - &context_state_authority.pubkey(), - &[&context_state_authority], - ) - .await - .unwrap(); - - token - .confidential_transfer_close_context_state( - &range_proof_context_state_account.pubkey(), - &alice_meta.token_account, - &context_state_authority.pubkey(), - &[&context_state_authority], - ) - .await - .unwrap(); - - // check balances alice_meta .check_balances( &token, @@ -2840,7 +2648,7 @@ async fn confidential_transfer_transfer_with_fee_and_split_proof_context() { .check_balances( &token, ConfidentialTokenAccountBalances { - pending_balance_lo: 97, + pending_balance_lo: 42, pending_balance_hi: 0, available_balance: 0, decryptable_available_balance: 0, @@ -2849,8 +2657,27 @@ async fn confidential_transfer_transfer_with_fee_and_split_proof_context() { .await; } +#[cfg(feature = "zk-ops")] #[tokio::test] -async fn confidential_transfer_transfer_with_fee_and_split_proof_context_in_parallel() { +async fn confidential_transfer_transfer_with_fee_and_memo() { + confidential_transfer_transfer_with_fee_and_memo_option( + ConfidentialTransferOption::InstructionData, + ) + .await; + confidential_transfer_transfer_with_fee_and_memo_option( + ConfidentialTransferOption::RecordAccount, + ) + .await; + confidential_transfer_transfer_with_fee_and_memo_option( + ConfidentialTransferOption::ContextStateAccount, + ) + .await; +} + +#[cfg(feature = "zk-ops")] +async fn confidential_transfer_transfer_with_fee_and_memo_option( + option: ConfidentialTransferOption, +) { let transfer_fee_authority = Keypair::new(); let withdraw_withheld_authority = Keypair::new(); @@ -2907,79 +2734,58 @@ async fn confidential_transfer_transfer_with_fee_and_split_proof_context_in_para ) .await; - let bob_meta = ConfidentialTokenAccountMeta::new(&token, &bob, None, false, true).await; - - let context_state_authority = Keypair::new(); - let equality_proof_context_state_account = Keypair::new(); - let transfer_amount_ciphertext_validity_proof_context_state_account = Keypair::new(); - let fee_sigma_proof_context_state_account = Keypair::new(); - let fee_ciphertext_validity_proof_context_state_account = Keypair::new(); - let range_proof_context_state_account = Keypair::new(); - - let lamport_destination = Pubkey::new_unique(); + let bob_meta = ConfidentialTokenAccountMeta::new(&token, &bob, None, true, true).await; - let close_split_context_state_accounts = CloseSplitContextStateAccounts { - lamport_destination: &lamport_destination, - zk_token_proof_program: &zk_token_proof_program::id(), - }; + let err = confidential_transfer_with_fee_with_option( + &token, + &alice_meta.token_account, + &bob_meta.token_account, + &alice.pubkey(), + 100, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + bob_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + withdraw_withheld_authority_elgamal_keypair.pubkey(), + TEST_FEE_BASIS_POINTS, + TEST_MAXIMUM_FEE, + None, + &[&alice], + option, + ) + .await + .unwrap_err(); - let transfer_context_state_accounts = TransferWithFeeSplitContextStateAccounts { - equality_proof: &equality_proof_context_state_account.pubkey(), - transfer_amount_ciphertext_validity_proof: - &transfer_amount_ciphertext_validity_proof_context_state_account.pubkey(), - fee_sigma_proof: &fee_sigma_proof_context_state_account.pubkey(), - fee_ciphertext_validity_proof: &fee_ciphertext_validity_proof_context_state_account - .pubkey(), - range_proof: &range_proof_context_state_account.pubkey(), - authority: &context_state_authority.pubkey(), - no_op_on_uninitialized_split_context_state: true, - close_split_context_state_accounts: Some(close_split_context_state_accounts), - }; + assert_eq!( + err, + TokenClientError::Client(Box::new(TransportError::TransactionError( + TransactionError::InstructionError( + 0, + InstructionError::Custom(TokenError::NoMemo as u32) + ) + ))) + ); - let equality_and_ciphertext_proof_signers = vec![ - &alice, - &equality_proof_context_state_account, - &transfer_amount_ciphertext_validity_proof_context_state_account, - &context_state_authority, - ]; - let fee_sigma_proof_signers = vec![ - &alice, - &fee_sigma_proof_context_state_account, - &fee_ciphertext_validity_proof_context_state_account, - &context_state_authority, - ]; - let range_proof_signers = vec![ - &alice, - &range_proof_context_state_account, - &context_state_authority, - ]; - // With split proofs in parallel, one of the transactions does more work - // than the other, which isn't caught during the simulation to discover the - // compute unit limit. - let token = token.with_compute_unit_limit(ComputeUnitLimit::Static(500_000)); - token - .confidential_transfer_transfer_with_fee_and_split_proofs_in_parallel( - &alice_meta.token_account, - &bob_meta.token_account, - &alice.pubkey(), - transfer_context_state_accounts, - 100, - None, - &alice_meta.elgamal_keypair, - &alice_meta.aes_key, - bob_meta.elgamal_keypair.pubkey(), - Some(auditor_elgamal_keypair.pubkey()), - withdraw_withheld_authority_elgamal_keypair.pubkey(), - TEST_FEE_BASIS_POINTS, - TEST_MAXIMUM_FEE, - &equality_and_ciphertext_proof_signers, - &fee_sigma_proof_signers, - &range_proof_signers, - ) - .await - .unwrap(); + confidential_transfer_with_fee_with_option( + &token, + &alice_meta.token_account, + &bob_meta.token_account, + &alice.pubkey(), + 100, + &alice_meta.elgamal_keypair, + &alice_meta.aes_key, + bob_meta.elgamal_keypair.pubkey(), + Some(auditor_elgamal_keypair.pubkey()), + withdraw_withheld_authority_elgamal_keypair.pubkey(), + TEST_FEE_BASIS_POINTS, + TEST_MAXIMUM_FEE, + Some(("🦖", vec![alice.pubkey()])), + &[&alice], + option, + ) + .await + .unwrap(); - // check balances alice_meta .check_balances( &token, @@ -3003,37 +2809,4 @@ async fn confidential_transfer_transfer_with_fee_and_split_proof_context_in_para }, ) .await; - - let error = token - .get_account(equality_proof_context_state_account.pubkey()) - .await - .unwrap_err(); - assert_eq!(error, TokenClientError::AccountNotFound); - - let error = token - .get_account(transfer_amount_ciphertext_validity_proof_context_state_account.pubkey()) - .await - .unwrap_err(); - assert_eq!(error, TokenClientError::AccountNotFound); - - let error = token - .get_account(fee_sigma_proof_context_state_account.pubkey()) - .await - .unwrap_err(); - assert_eq!(error, TokenClientError::AccountNotFound); - - let error = token - .get_account(fee_ciphertext_validity_proof_context_state_account.pubkey()) - .await - .unwrap_err(); - assert_eq!(error, TokenClientError::AccountNotFound); - - let error = token - .get_account(range_proof_context_state_account.pubkey()) - .await - .unwrap_err(); - assert_eq!(error, TokenClientError::AccountNotFound); - - let lamport_destination = token.get_account(lamport_destination).await.unwrap(); - assert!(lamport_destination.lamports > 0); } diff --git a/token/program-2022-test/tests/confidential_transfer_fee.rs b/token/program-2022-test/tests/confidential_transfer_fee.rs index 161ee2c2a81..c2664525c5f 100644 --- a/token/program-2022-test/tests/confidential_transfer_fee.rs +++ b/token/program-2022-test/tests/confidential_transfer_fee.rs @@ -2,21 +2,24 @@ mod program_test; use { - program_test::{TestContext, TokenContext}, + bytemuck::Zeroable, + program_test::{ConfidentialTransferOption, TestContext, TokenContext}, solana_program_test::tokio, solana_sdk::{ instruction::InstructionError, pubkey::Pubkey, signature::Signer, - signer::keypair::Keypair, - system_instruction, - transaction::{Transaction, TransactionError}, + signer::{keypair::Keypair, signers::Signers}, + transaction::TransactionError, transport::TransportError, }, + spl_record::state::RecordData, spl_token_2022::{ error::TokenError, extension::{ - confidential_transfer::{ConfidentialTransferAccount, ConfidentialTransferMint}, + confidential_transfer::{ + ConfidentialTransferAccount, ConfidentialTransferMint, DecryptableBalance, + }, confidential_transfer_fee::{ account_info::WithheldTokensInfo, ConfidentialTransferFeeAmount, ConfidentialTransferFeeConfig, @@ -25,19 +28,18 @@ use { BaseStateWithExtensions, ExtensionType, }, instruction, - solana_zk_token_sdk::{ - encryption::{auth_encryption::*, elgamal::*}, - zk_token_elgamal::pod::{self, Zeroable}, - zk_token_proof_instruction::*, - zk_token_proof_program, - zk_token_proof_state::ProofContextState, + solana_zk_sdk::encryption::{ + auth_encryption::*, elgamal::*, pod::elgamal::PodElGamalCiphertext, }, }, spl_token_client::{ - client::{SendTransaction, SimulateTransaction}, - token::{ExtensionInitializationParams, Token, TokenError as TokenClientError}, + client::{ProgramBanksClientProcessTransaction, SendTransaction, SimulateTransaction}, + token::{ + ExtensionInitializationParams, ProofAccount, Token, TokenError as TokenClientError, + TokenResult, + }, }, - std::{convert::TryInto, mem::size_of}, + std::convert::TryInto, }; #[cfg(feature = "zk-ops")] @@ -146,23 +148,23 @@ impl ConfidentialTokenAccountMeta { .unwrap(); assert_eq!( - extension - .pending_balance_lo - .decrypt(self.elgamal_keypair.secret()) + self.elgamal_keypair + .secret() + .decrypt_u32(&extension.pending_balance_lo.try_into().unwrap()) .unwrap(), expected.pending_balance_lo, ); assert_eq!( - extension - .pending_balance_hi - .decrypt(self.elgamal_keypair.secret()) + self.elgamal_keypair + .secret() + .decrypt_u32(&extension.pending_balance_hi.try_into().unwrap()) .unwrap(), expected.pending_balance_hi, ); assert_eq!( - extension - .available_balance - .decrypt(self.elgamal_keypair.secret()) + self.elgamal_keypair + .secret() + .decrypt_u32(&extension.available_balance.try_into().unwrap()) .unwrap(), expected.available_balance, ); @@ -195,10 +197,12 @@ async fn check_withheld_amount_in_mint( let extension = state .get_extension::() .unwrap(); - let decrypted_amount = extension - .withheld_amount - .decrypt(withdraw_withheld_authority_elgamal_keypair.secret()) + + let decrypted_amount = withdraw_withheld_authority_elgamal_keypair + .secret() + .decrypt_u32(&extension.withheld_amount.try_into().unwrap()) .unwrap(); + assert_eq!(decrypted_amount, expected); } @@ -454,9 +458,170 @@ async fn confidential_transfer_initialize_and_update_mint() { assert_eq!(extension.authority, None.try_into().unwrap()); } +#[allow(clippy::too_many_arguments)] +#[cfg(feature = "zk-ops")] +async fn withdraw_withheld_tokens_from_mint_with_option( + token: &Token, + destination_account: &Pubkey, + withdraw_withheld_authority: &Pubkey, + withdraw_withheld_authority_elgamal_keypair: &ElGamalKeypair, + destination_elgamal_pubkey: &ElGamalPubkey, + new_decryptable_available_balance: &DecryptableBalance, + signing_keypairs: &S, + option: ConfidentialTransferOption, +) -> TokenResult<()> { + match option { + ConfidentialTransferOption::InstructionData => { + token + .confidential_transfer_withdraw_withheld_tokens_from_mint( + destination_account, + withdraw_withheld_authority, + None, + None, + withdraw_withheld_authority_elgamal_keypair, + destination_elgamal_pubkey, + new_decryptable_available_balance, + signing_keypairs, + ) + .await + } + ConfidentialTransferOption::RecordAccount => { + let state = token.get_mint_info().await.unwrap(); + let extension = state + .get_extension::() + .unwrap(); + let account_info = WithheldTokensInfo::new(&extension.withheld_amount); + + let equality_proof = account_info + .generate_proof_data( + withdraw_withheld_authority_elgamal_keypair, + destination_elgamal_pubkey, + ) + .unwrap(); + + let record_account = Keypair::new(); + let record_account_authority = Keypair::new(); + + token + .confidential_transfer_create_record_account( + &record_account.pubkey(), + &record_account_authority.pubkey(), + &equality_proof, + &record_account, + &record_account_authority, + ) + .await + .unwrap(); + + let proof_account = ProofAccount::RecordAccount( + record_account.pubkey(), + RecordData::WRITABLE_START_INDEX as u32, + ); + + let result = token + .confidential_transfer_withdraw_withheld_tokens_from_mint( + destination_account, + withdraw_withheld_authority, + Some(&proof_account), + None, + withdraw_withheld_authority_elgamal_keypair, + destination_elgamal_pubkey, + new_decryptable_available_balance, + signing_keypairs, + ) + .await; + + token + .confidential_transfer_close_record_account( + &record_account.pubkey(), + destination_account, + &record_account_authority.pubkey(), + &[&record_account_authority], + ) + .await + .unwrap(); + + result + } + ConfidentialTransferOption::ContextStateAccount => { + let state = token.get_mint_info().await.unwrap(); + let extension = state + .get_extension::() + .unwrap(); + let account_info = WithheldTokensInfo::new(&extension.withheld_amount); + + let equality_proof = account_info + .generate_proof_data( + withdraw_withheld_authority_elgamal_keypair, + destination_elgamal_pubkey, + ) + .unwrap(); + + let context_account = Keypair::new(); + let context_account_authority = Keypair::new(); + + token + .confidential_transfer_create_context_state_account( + &context_account.pubkey(), + &context_account_authority.pubkey(), + &equality_proof, + false, + &[&context_account], + ) + .await + .unwrap(); + + let proof_account = ProofAccount::ContextAccount(context_account.pubkey()); + + let result = token + .confidential_transfer_withdraw_withheld_tokens_from_mint( + destination_account, + withdraw_withheld_authority, + Some(&proof_account), + None, + withdraw_withheld_authority_elgamal_keypair, + destination_elgamal_pubkey, + new_decryptable_available_balance, + signing_keypairs, + ) + .await; + + token + .confidential_transfer_close_context_state_account( + &context_account.pubkey(), + destination_account, + &context_account_authority.pubkey(), + &[&context_account_authority], + ) + .await + .unwrap(); + + result + } + } +} + #[cfg(feature = "zk-ops")] #[tokio::test] async fn confidential_transfer_withdraw_withheld_tokens_from_mint() { + confidential_transfer_withdraw_withheld_tokens_from_mint_with_option( + ConfidentialTransferOption::InstructionData, + ) + .await; + confidential_transfer_withdraw_withheld_tokens_from_mint_with_option( + ConfidentialTransferOption::RecordAccount, + ) + .await; + confidential_transfer_withdraw_withheld_tokens_from_mint_with_option( + ConfidentialTransferOption::ContextStateAccount, + ) + .await; +} + +#[cfg(feature = "zk-ops")] +async fn confidential_transfer_withdraw_withheld_tokens_from_mint_with_option( + option: ConfidentialTransferOption, +) { let transfer_fee_authority = Keypair::new(); let withdraw_withheld_authority = Keypair::new(); @@ -519,6 +684,10 @@ async fn confidential_transfer_withdraw_withheld_tokens_from_mint() { &bob_meta.token_account, &alice.pubkey(), None, + None, + None, + None, + None, 100, None, &alice_meta.elgamal_keypair, @@ -573,7 +742,7 @@ async fn confidential_transfer_withdraw_withheld_tokens_from_mint() { let extension = state .get_extension::() .unwrap(); - assert_eq!(extension.withheld_amount, pod::ElGamalCiphertext::zeroed()); + assert_eq!(extension.withheld_amount, PodElGamalCiphertext::zeroed()); // calculate and encrypt fee to attach to the `WithdrawWithheldTokensFromMint` // instruction data @@ -582,19 +751,18 @@ async fn confidential_transfer_withdraw_withheld_tokens_from_mint() { check_withheld_amount_in_mint(&token, &withdraw_withheld_authority_elgamal_keypair, fee).await; - token - .confidential_transfer_withdraw_withheld_tokens_from_mint( - &alice_meta.token_account, - &withdraw_withheld_authority.pubkey(), - None, - None, - &withdraw_withheld_authority_elgamal_keypair, - alice_meta.elgamal_keypair.pubkey(), - &new_decryptable_available_balance.into(), - &[&withdraw_withheld_authority], - ) - .await - .unwrap(); + withdraw_withheld_tokens_from_mint_with_option( + &token, + &alice_meta.token_account, + &withdraw_withheld_authority.pubkey(), + &withdraw_withheld_authority_elgamal_keypair, + alice_meta.elgamal_keypair.pubkey(), + &new_decryptable_available_balance.into(), + &[&withdraw_withheld_authority], + option, + ) + .await + .unwrap(); // withheld fees are withdrawn back to alice's account alice_meta @@ -612,139 +780,174 @@ async fn confidential_transfer_withdraw_withheld_tokens_from_mint() { check_withheld_amount_in_mint(&token, &withdraw_withheld_authority_elgamal_keypair, 0).await; } +#[allow(clippy::too_many_arguments)] +#[cfg(feature = "zk-ops")] +async fn withdraw_withheld_tokens_from_accounts_with_option( + token: &Token, + destination_account: &Pubkey, + withdraw_withheld_authority: &Pubkey, + withdraw_withheld_authority_elgamal_keypair: &ElGamalKeypair, + destination_elgamal_pubkey: &ElGamalPubkey, + new_decryptable_available_balance: &DecryptableBalance, + signing_keypairs: &S, + source: &Pubkey, + option: ConfidentialTransferOption, +) -> TokenResult<()> { + match option { + ConfidentialTransferOption::InstructionData => { + token + .confidential_transfer_withdraw_withheld_tokens_from_accounts( + destination_account, + withdraw_withheld_authority, + None, + None, + withdraw_withheld_authority_elgamal_keypair, + destination_elgamal_pubkey, + new_decryptable_available_balance, + &[source], + signing_keypairs, + ) + .await + } + ConfidentialTransferOption::RecordAccount => { + let state = token.get_account_info(source).await.unwrap(); + let extension = state + .get_extension::() + .unwrap(); + let account_info = WithheldTokensInfo::new(&extension.withheld_amount); + + let equality_proof = account_info + .generate_proof_data( + withdraw_withheld_authority_elgamal_keypair, + destination_elgamal_pubkey, + ) + .unwrap(); + + let record_account = Keypair::new(); + let record_account_authority = Keypair::new(); + + token + .confidential_transfer_create_record_account( + &record_account.pubkey(), + &record_account_authority.pubkey(), + &equality_proof, + &record_account, + &record_account_authority, + ) + .await + .unwrap(); + + let proof_account = ProofAccount::RecordAccount( + record_account.pubkey(), + RecordData::WRITABLE_START_INDEX as u32, + ); + + let result = token + .confidential_transfer_withdraw_withheld_tokens_from_accounts( + destination_account, + withdraw_withheld_authority, + Some(&proof_account), + None, + withdraw_withheld_authority_elgamal_keypair, + destination_elgamal_pubkey, + new_decryptable_available_balance, + &[source], + signing_keypairs, + ) + .await; + + token + .confidential_transfer_close_record_account( + &record_account.pubkey(), + destination_account, + &record_account_authority.pubkey(), + &[&record_account_authority], + ) + .await + .unwrap(); + + result + } + ConfidentialTransferOption::ContextStateAccount => { + let state = token.get_account_info(source).await.unwrap(); + let extension = state + .get_extension::() + .unwrap(); + let account_info = WithheldTokensInfo::new(&extension.withheld_amount); + + let equality_proof = account_info + .generate_proof_data( + withdraw_withheld_authority_elgamal_keypair, + destination_elgamal_pubkey, + ) + .unwrap(); + + let context_account = Keypair::new(); + let context_account_authority = Keypair::new(); + + token + .confidential_transfer_create_context_state_account( + &context_account.pubkey(), + &context_account_authority.pubkey(), + &equality_proof, + false, + &[&context_account], + ) + .await + .unwrap(); + + let proof_account = ProofAccount::ContextAccount(context_account.pubkey()); + + let result = token + .confidential_transfer_withdraw_withheld_tokens_from_accounts( + destination_account, + withdraw_withheld_authority, + Some(&proof_account), + None, + withdraw_withheld_authority_elgamal_keypair, + destination_elgamal_pubkey, + new_decryptable_available_balance, + &[source], + signing_keypairs, + ) + .await; + + token + .confidential_transfer_close_context_state_account( + &context_account.pubkey(), + destination_account, + &context_account_authority.pubkey(), + &[&context_account_authority], + ) + .await + .unwrap(); + + result + } + } +} + #[cfg(feature = "zk-ops")] #[tokio::test] async fn confidential_transfer_withdraw_withheld_tokens_from_accounts() { - let transfer_fee_authority = Keypair::new(); - let withdraw_withheld_authority = Keypair::new(); - - let confidential_transfer_authority = Keypair::new(); - let auto_approve_new_accounts = true; - let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); - let auditor_elgamal_pubkey = (*auditor_elgamal_keypair.pubkey()).into(); - - let confidential_transfer_fee_authority = Keypair::new(); - let withdraw_withheld_authority_elgamal_keypair = ElGamalKeypair::new_rand(); - let withdraw_withheld_authority_elgamal_pubkey = - (*withdraw_withheld_authority_elgamal_keypair.pubkey()).into(); - - let mut context = TestContext::new().await; - context - .init_token_with_mint(vec![ - ExtensionInitializationParams::TransferFeeConfig { - transfer_fee_config_authority: Some(transfer_fee_authority.pubkey()), - withdraw_withheld_authority: Some(withdraw_withheld_authority.pubkey()), - transfer_fee_basis_points: TEST_FEE_BASIS_POINTS, - maximum_fee: TEST_MAXIMUM_FEE, - }, - ExtensionInitializationParams::ConfidentialTransferMint { - authority: Some(confidential_transfer_authority.pubkey()), - auto_approve_new_accounts, - auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), - }, - ExtensionInitializationParams::ConfidentialTransferFeeConfig { - authority: Some(confidential_transfer_fee_authority.pubkey()), - withdraw_withheld_authority_elgamal_pubkey, - }, - ]) - .await - .unwrap(); - - let TokenContext { - token, - alice, - bob, - mint_authority, - decimals, - .. - } = context.token_context.unwrap(); - - let alice_meta = - ConfidentialTokenAccountMeta::new(&token, &alice, &mint_authority, 100, decimals).await; - let bob_meta = - ConfidentialTokenAccountMeta::new(&token, &bob, &mint_authority, 0, decimals).await; - - let transfer_fee_parameters = TransferFee { - epoch: 0.into(), - maximum_fee: TEST_MAXIMUM_FEE.into(), - transfer_fee_basis_points: TEST_FEE_BASIS_POINTS.into(), - }; - - // Test fee is 2.5% so the withheld fees should be 3 - token - .confidential_transfer_transfer_with_fee( - &alice_meta.token_account, - &bob_meta.token_account, - &alice.pubkey(), - None, - 100, - None, - &alice_meta.elgamal_keypair, - &alice_meta.aes_key, - bob_meta.elgamal_keypair.pubkey(), - Some(auditor_elgamal_keypair.pubkey()), - withdraw_withheld_authority_elgamal_keypair.pubkey(), - transfer_fee_parameters.transfer_fee_basis_points.into(), - transfer_fee_parameters.maximum_fee.into(), - &[&alice], - ) - .await - .unwrap(); - - let fee = transfer_fee_parameters.calculate_fee(100).unwrap(); - let new_decryptable_available_balance = alice_meta.aes_key.encrypt(fee); - token - .confidential_transfer_withdraw_withheld_tokens_from_accounts( - &alice_meta.token_account, - &withdraw_withheld_authority.pubkey(), - None, - None, - &withdraw_withheld_authority_elgamal_keypair, - alice_meta.elgamal_keypair.pubkey(), - &new_decryptable_available_balance.into(), - &[&bob_meta.token_account], - &[&withdraw_withheld_authority], - ) - .await - .unwrap(); - - alice_meta - .check_balances( - &token, - ConfidentialTokenAccountBalances { - pending_balance_lo: 0, - pending_balance_hi: 0, - available_balance: fee, - decryptable_available_balance: fee, - }, - ) - .await; - - bob_meta - .check_balances( - &token, - ConfidentialTokenAccountBalances { - pending_balance_lo: 97, - pending_balance_hi: 0, - available_balance: 0, - decryptable_available_balance: 0, - }, - ) - .await; - - let state = token - .get_account_info(&bob_meta.token_account) - .await - .unwrap(); - let extension = state - .get_extension::() - .unwrap(); - assert_eq!(extension.withheld_amount, pod::ElGamalCiphertext::zeroed()); + confidential_transfer_withdraw_withheld_tokens_from_accounts_with_option( + ConfidentialTransferOption::InstructionData, + ) + .await; + confidential_transfer_withdraw_withheld_tokens_from_accounts_with_option( + ConfidentialTransferOption::RecordAccount, + ) + .await; + confidential_transfer_withdraw_withheld_tokens_from_accounts_with_option( + ConfidentialTransferOption::ContextStateAccount, + ) + .await; } #[cfg(feature = "zk-ops")] -#[tokio::test] -async fn confidential_transfer_withdraw_withheld_tokens_from_mint_with_proof_context() { +async fn confidential_transfer_withdraw_withheld_tokens_from_accounts_with_option( + option: ConfidentialTransferOption, +) { let transfer_fee_authority = Keypair::new(); let withdraw_withheld_authority = Keypair::new(); @@ -807,174 +1010,9 @@ async fn confidential_transfer_withdraw_withheld_tokens_from_mint_with_proof_con &bob_meta.token_account, &alice.pubkey(), None, - 100, None, - &alice_meta.elgamal_keypair, - &alice_meta.aes_key, - bob_meta.elgamal_keypair.pubkey(), - Some(auditor_elgamal_keypair.pubkey()), - withdraw_withheld_authority_elgamal_keypair.pubkey(), - transfer_fee_parameters.transfer_fee_basis_points.into(), - transfer_fee_parameters.maximum_fee.into(), - &[&alice], - ) - .await - .unwrap(); - - token - .confidential_transfer_harvest_withheld_tokens_to_mint(&[&bob_meta.token_account]) - .await - .unwrap(); - - let context_state_account = Keypair::new(); - - // create context state - { - let context_state_authority = Keypair::new(); - let space = size_of::>(); - - let instruction_type = ProofInstruction::VerifyCiphertextCiphertextEquality; - - let context_state_info = ContextStateInfo { - context_state_account: &context_state_account.pubkey(), - context_state_authority: &context_state_authority.pubkey(), - }; - - let state = token.get_mint_info().await.unwrap(); - let extension = state - .get_extension::() - .unwrap(); - let withheld_tokens_info = WithheldTokensInfo::new(&extension.withheld_amount); - - let proof_data = withheld_tokens_info - .generate_proof_data( - &withdraw_withheld_authority_elgamal_keypair, - alice_meta.elgamal_keypair.pubkey(), - ) - .unwrap(); - - let mut ctx = context.context.lock().await; - let rent = ctx.banks_client.get_rent().await.unwrap(); - - let instructions = vec![ - system_instruction::create_account( - &ctx.payer.pubkey(), - &context_state_account.pubkey(), - rent.minimum_balance(space), - space as u64, - &zk_token_proof_program::id(), - ), - instruction_type.encode_verify_proof(Some(context_state_info), &proof_data), - ]; - - let recent_blockhash = ctx.get_new_latest_blockhash().await.unwrap(); - let tx = Transaction::new_signed_with_payer( - &instructions, - Some(&ctx.payer.pubkey()), - &[&ctx.payer, &context_state_account], - recent_blockhash, - ); - ctx.banks_client.process_transaction(tx).await.unwrap(); - } - - // calculate and encrypt fee to attach to the `WithdrawWithheldTokensFromMint` - // instruction data - let fee = transfer_fee_parameters.calculate_fee(100).unwrap(); - let new_decryptable_available_balance = alice_meta.aes_key.encrypt(fee); - token - .confidential_transfer_withdraw_withheld_tokens_from_mint( - &alice_meta.token_account, - &withdraw_withheld_authority.pubkey(), - Some(&context_state_account.pubkey()), None, - &withdraw_withheld_authority_elgamal_keypair, - alice_meta.elgamal_keypair.pubkey(), - &new_decryptable_available_balance.into(), - &[&withdraw_withheld_authority], - ) - .await - .unwrap(); - - // withheld fees are withdrawn back to alice's account - alice_meta - .check_balances( - &token, - ConfidentialTokenAccountBalances { - pending_balance_lo: 0, - pending_balance_hi: 0, - available_balance: 3, - decryptable_available_balance: 3, - }, - ) - .await; - - check_withheld_amount_in_mint(&token, &withdraw_withheld_authority_elgamal_keypair, 0).await; -} - -#[cfg(feature = "zk-ops")] -#[tokio::test] -async fn confidential_transfer_withdraw_withheld_tokens_from_accounts_with_proof_context() { - let transfer_fee_authority = Keypair::new(); - let withdraw_withheld_authority = Keypair::new(); - - let confidential_transfer_authority = Keypair::new(); - let auto_approve_new_accounts = true; - let auditor_elgamal_keypair = ElGamalKeypair::new_rand(); - let auditor_elgamal_pubkey = (*auditor_elgamal_keypair.pubkey()).into(); - - let confidential_transfer_fee_authority = Keypair::new(); - let withdraw_withheld_authority_elgamal_keypair = ElGamalKeypair::new_rand(); - let withdraw_withheld_authority_elgamal_pubkey = - (*withdraw_withheld_authority_elgamal_keypair.pubkey()).into(); - - let mut context = TestContext::new().await; - context - .init_token_with_mint(vec![ - ExtensionInitializationParams::TransferFeeConfig { - transfer_fee_config_authority: Some(transfer_fee_authority.pubkey()), - withdraw_withheld_authority: Some(withdraw_withheld_authority.pubkey()), - transfer_fee_basis_points: TEST_FEE_BASIS_POINTS, - maximum_fee: TEST_MAXIMUM_FEE, - }, - ExtensionInitializationParams::ConfidentialTransferMint { - authority: Some(confidential_transfer_authority.pubkey()), - auto_approve_new_accounts, - auditor_elgamal_pubkey: Some(auditor_elgamal_pubkey), - }, - ExtensionInitializationParams::ConfidentialTransferFeeConfig { - authority: Some(confidential_transfer_fee_authority.pubkey()), - withdraw_withheld_authority_elgamal_pubkey, - }, - ]) - .await - .unwrap(); - - let TokenContext { - token, - alice, - bob, - mint_authority, - decimals, - .. - } = context.token_context.unwrap(); - - let alice_meta = - ConfidentialTokenAccountMeta::new(&token, &alice, &mint_authority, 100, decimals).await; - let bob_meta = - ConfidentialTokenAccountMeta::new(&token, &bob, &mint_authority, 0, decimals).await; - - let transfer_fee_parameters = TransferFee { - epoch: 0.into(), - maximum_fee: TEST_MAXIMUM_FEE.into(), - transfer_fee_basis_points: TEST_FEE_BASIS_POINTS.into(), - }; - - // Test fee is 2.5% so the withheld fees should be 3 - token - .confidential_transfer_transfer_with_fee( - &alice_meta.token_account, - &bob_meta.token_account, - &alice.pubkey(), + None, None, 100, None, @@ -990,77 +1028,21 @@ async fn confidential_transfer_withdraw_withheld_tokens_from_accounts_with_proof .await .unwrap(); - let context_state_account = Keypair::new(); - - // create context state - { - let context_state_authority = Keypair::new(); - let space = size_of::>(); - - let instruction_type = ProofInstruction::VerifyCiphertextCiphertextEquality; - - let context_state_info = ContextStateInfo { - context_state_account: &context_state_account.pubkey(), - context_state_authority: &context_state_authority.pubkey(), - }; - - let state = token - .get_account_info(&bob_meta.token_account) - .await - .unwrap(); - let withheld_amount = state - .get_extension::() - .unwrap() - .withheld_amount; - let withheld_tokens_info = WithheldTokensInfo::new(&withheld_amount); - - let proof_data = withheld_tokens_info - .generate_proof_data( - &withdraw_withheld_authority_elgamal_keypair, - alice_meta.elgamal_keypair.pubkey(), - ) - .unwrap(); - - let mut ctx = context.context.lock().await; - let rent = ctx.banks_client.get_rent().await.unwrap(); - - let instructions = vec![ - system_instruction::create_account( - &ctx.payer.pubkey(), - &context_state_account.pubkey(), - rent.minimum_balance(space), - space as u64, - &zk_token_proof_program::id(), - ), - instruction_type.encode_verify_proof(Some(context_state_info), &proof_data), - ]; - - let recent_blockhash = ctx.get_new_latest_blockhash().await.unwrap(); - let tx = Transaction::new_signed_with_payer( - &instructions, - Some(&ctx.payer.pubkey()), - &[&ctx.payer, &context_state_account], - recent_blockhash, - ); - ctx.banks_client.process_transaction(tx).await.unwrap(); - } - let fee = transfer_fee_parameters.calculate_fee(100).unwrap(); let new_decryptable_available_balance = alice_meta.aes_key.encrypt(fee); - token - .confidential_transfer_withdraw_withheld_tokens_from_accounts( - &alice_meta.token_account, - &withdraw_withheld_authority.pubkey(), - Some(&context_state_account.pubkey()), - None, - &withdraw_withheld_authority_elgamal_keypair, - alice_meta.elgamal_keypair.pubkey(), - &new_decryptable_available_balance.into(), - &[&bob_meta.token_account], - &[&withdraw_withheld_authority], - ) - .await - .unwrap(); + withdraw_withheld_tokens_from_accounts_with_option( + &token, + &alice_meta.token_account, + &withdraw_withheld_authority.pubkey(), + &withdraw_withheld_authority_elgamal_keypair, + alice_meta.elgamal_keypair.pubkey(), + &new_decryptable_available_balance.into(), + &[&withdraw_withheld_authority], + &bob_meta.token_account, + option, + ) + .await + .unwrap(); alice_meta .check_balances( @@ -1093,7 +1075,7 @@ async fn confidential_transfer_withdraw_withheld_tokens_from_accounts_with_proof let extension = state .get_extension::() .unwrap(); - assert_eq!(extension.withheld_amount, pod::ElGamalCiphertext::zeroed()); + assert_eq!(extension.withheld_amount, PodElGamalCiphertext::zeroed()); } #[cfg(feature = "zk-ops")] @@ -1167,6 +1149,10 @@ async fn confidential_transfer_harvest_withheld_tokens_to_mint() { &bob_meta.token_account, &alice.pubkey(), None, + None, + None, + None, + None, 100, None, &alice_meta.elgamal_keypair, @@ -1228,7 +1214,7 @@ async fn confidential_transfer_harvest_withheld_tokens_to_mint() { let extension = state .get_extension::() .unwrap(); - assert_eq!(extension.withheld_amount, pod::ElGamalCiphertext::zeroed()); + assert_eq!(extension.withheld_amount, PodElGamalCiphertext::zeroed()); // calculate and encrypt fee to attach to the `WithdrawWithheldTokensFromMint` // instruction data diff --git a/token/program-2022-test/tests/initialize_mint.rs b/token/program-2022-test/tests/initialize_mint.rs index 898ecd4ea19..70cd4649e3a 100644 --- a/token/program-2022-test/tests/initialize_mint.rs +++ b/token/program-2022-test/tests/initialize_mint.rs @@ -22,7 +22,7 @@ use { ExtensionType, }, instruction, native_mint, - solana_zk_token_sdk::zk_token_elgamal::pod::ElGamalPubkey, + solana_zk_sdk::encryption::pod::elgamal::PodElGamalPubkey, state::Mint, }, spl_token_client::token::ExtensionInitializationParams, @@ -523,7 +523,7 @@ async fn fail_invalid_extensions_combination() { &spl_token_2022::id(), &mint_account.pubkey(), Some(Pubkey::new_unique()), - ElGamalPubkey::default(), + PodElGamalPubkey::default(), ) .unwrap(); diff --git a/token/program-2022-test/tests/program_test.rs b/token/program-2022-test/tests/program_test.rs index 0d4979383b4..84846c5ab04 100644 --- a/token/program-2022-test/tests/program_test.rs +++ b/token/program-2022-test/tests/program_test.rs @@ -13,7 +13,7 @@ use { }, id, native_mint, processor::Processor, - solana_zk_token_sdk::encryption::{auth_encryption::*, elgamal::*}, + solana_zk_sdk::encryption::{auth_encryption::*, elgamal::*}, }, spl_token_client::{ client::{ @@ -42,7 +42,14 @@ pub struct TestContext { impl TestContext { pub async fn new() -> Self { - let program_test = ProgramTest::new("spl_token_2022", id(), processor!(Processor::process)); + let mut program_test = + ProgramTest::new("spl_token_2022", id(), processor!(Processor::process)); + program_test.prefer_bpf(false); + program_test.add_program( + "spl_record", + spl_record::id(), + processor!(spl_record::processor::process_instruction), + ); let context = program_test.start_with_context().await; let context = Arc::new(Mutex::new(context)); @@ -319,23 +326,23 @@ impl ConfidentialTokenAccountMeta { .unwrap(); assert_eq!( - extension - .pending_balance_lo - .decrypt(self.elgamal_keypair.secret()) + self.elgamal_keypair + .secret() + .decrypt_u32(&extension.pending_balance_lo.try_into().unwrap()) .unwrap(), expected.pending_balance_lo, ); assert_eq!( - extension - .pending_balance_hi - .decrypt(self.elgamal_keypair.secret()) + self.elgamal_keypair + .secret() + .decrypt_u32(&extension.pending_balance_hi.try_into().unwrap()) .unwrap(), expected.pending_balance_hi, ); assert_eq!( - extension - .available_balance - .decrypt(self.elgamal_keypair.secret()) + self.elgamal_keypair + .secret() + .decrypt_u32(&extension.available_balance.try_into().unwrap()) .unwrap(), expected.available_balance, ); @@ -355,3 +362,10 @@ pub(crate) struct ConfidentialTokenAccountBalances { pub(crate) available_balance: u64, pub(crate) decryptable_available_balance: u64, } + +#[derive(Clone, Copy)] +pub enum ConfidentialTransferOption { + InstructionData, + RecordAccount, + ContextStateAccount, +} diff --git a/token/program-2022-test/tests/token_group_update_max_size.rs b/token/program-2022-test/tests/token_group_update_max_size.rs index 9e8382fae20..fef4db7e35e 100644 --- a/token/program-2022-test/tests/token_group_update_max_size.rs +++ b/token/program-2022-test/tests/token_group_update_max_size.rs @@ -61,17 +61,17 @@ async fn setup(mint: Keypair, authority: &Pubkey) -> TestContext { #[test_case(50, 0, 300_000_000)] #[test_case(100_000, 100_000, 300_000_000)] #[test_case(100_000_000, 100_000_000, 300_000_000)] -#[test_case(0, 0, u32::MAX)] -#[test_case(200_000, 200_000, u32::MAX)] -#[test_case(300_000_000, 300_000_000, u32::MAX)] +#[test_case(0, 0, u64::MAX)] +#[test_case(200_000, 200_000, u64::MAX)] +#[test_case(300_000_000, 300_000_000, u64::MAX)] // Attempts to set lower than size #[test_case(5, 5, 4)] #[test_case(200_000, 200_000, 50)] #[test_case(200_000, 200_000, 100_000)] #[test_case(300_000_000, 300_000_000, 50)] -#[test_case(u32::MAX, u32::MAX, 0)] +#[test_case(u64::MAX, u64::MAX, 0)] #[tokio::test] -async fn test_update_group_max_size(max_size: u32, size: u32, new_max_size: u32) { +async fn test_update_group_max_size(max_size: u64, size: u64, new_max_size: u64) { let authority = Keypair::new(); let mint_keypair = Keypair::new(); let mut test_context = setup(mint_keypair.insecure_clone(), &authority.pubkey()).await; diff --git a/token/program-2022-test/tests/transfer_hook.rs b/token/program-2022-test/tests/transfer_hook.rs index 5d9f301591e..d26511bd9fd 100644 --- a/token/program-2022-test/tests/transfer_hook.rs +++ b/token/program-2022-test/tests/transfer_hook.rs @@ -45,6 +45,15 @@ pub fn process_instruction_fail( Err(ProgramError::InvalidInstructionData) } +/// Test program to succeed transfer hook, conforms to transfer-hook-interface +pub fn process_instruction_success( + _program_id: &Pubkey, + _accounts: &[AccountInfo], + _input: &[u8], +) -> ProgramResult { + Ok(()) +} + /// Test program to check signer / write downgrade for repeated accounts, /// conforms to transfer-hook-interface pub fn process_instruction_downgrade( @@ -854,6 +863,8 @@ async fn success_confidential_transfer() { &bob_meta.token_account, &alice.pubkey(), None, + None, + None, amount, None, &alice_meta.elgamal_keypair, @@ -913,3 +924,61 @@ async fn success_confidential_transfer() { false.into() ); } + +#[tokio::test] +async fn success_without_validation_account() { + let authority = Pubkey::new_unique(); + let program_id = Pubkey::new_unique(); + let mint = Keypair::new(); + let mut program_test = ProgramTest::default(); + program_test.prefer_bpf(false); + program_test.add_program( + "spl_token_2022", + spl_token_2022::id(), + processor!(Processor::process), + ); + program_test.add_program( + "my_transfer_hook", + program_id, + processor!(process_instruction_success), + ); + let context = program_test.start_with_context().await; + let context = Arc::new(tokio::sync::Mutex::new(context)); + let mut context = TestContext { + context, + token_context: None, + }; + context + .init_token_with_mint_keypair_and_freeze_authority( + mint, + vec![ExtensionInitializationParams::TransferHook { + authority: Some(authority), + program_id: Some(program_id), + }], + None, + ) + .await + .unwrap(); + let token_context = context.token_context.take().unwrap(); + + let amount = 10; + let (alice_account, bob_account) = + setup_accounts(&token_context, Keypair::new(), Keypair::new(), amount).await; + + // only add the transfer hook program id, nothing else + let token = token_context + .token + .with_transfer_hook_accounts(vec![AccountMeta::new_readonly(program_id, false)]); + token + .transfer( + &alice_account, + &bob_account, + &token_context.alice.pubkey(), + amount, + &[&token_context.alice], + ) + .await + .unwrap(); + let destination = token.get_account_info(&bob_account).await.unwrap(); + assert_eq!(destination.base.amount, amount); +} diff --git a/token/program-2022/Cargo.toml b/token/program-2022/Cargo.toml index 1733d87f3c3..8cbe0434a78 100644 --- a/token/program-2022/Cargo.toml +++ b/token/program-2022/Cargo.toml @@ -18,25 +18,30 @@ zk-ops = [] [dependencies] arrayref = "0.3.8" -bytemuck = { version = "1.16.1", features = ["derive"] } +bytemuck = { version = "1.17.0", features = ["derive"] } num-derive = "0.4" num-traits = "0.2" -num_enum = "0.7.2" +num_enum = "0.7.3" solana-program = "2.0.3" solana-security-txt = "1.1.1" -solana-zk-token-sdk = "2.0.3" +solana-zk-sdk = "2.0.3" spl-memo = { version = "5.0", path = "../../memo/program", features = [ "no-entrypoint" ] } spl-token = { version = "6.0", path = "../program", features = ["no-entrypoint"] } +spl-token-confidential-transfer-ciphertext-arithmetic = { version = "0.1.0", path = "../confidential-transfer/ciphertext-arithmetic" } +spl-token-confidential-transfer-proof-extraction = { version = "0.1.0", path = "../confidential-transfer/proof-extraction" } spl-token-group-interface = { version = "0.3.0", path = "../../token-group/interface" } spl-token-metadata-interface = { version = "0.4.0", path = "../../token-metadata/interface" } spl-transfer-hook-interface = { version = "0.7.0", path = "../transfer-hook/interface" } spl-type-length-value = { version = "0.5.0", path = "../../libraries/type-length-value" } spl-pod = { version = "0.3.0", path = "../../libraries/pod" } thiserror = "1.0" -serde = { version = "1.0.204", optional = true } +serde = { version = "1.0.208", optional = true } serde_with = { version = "3.9.0", optional = true } base64 = { version = "0.22.1", optional = true } +[target.'cfg(not(target_os = "solana"))'.dependencies] +spl-token-confidential-transfer-proof-generation = { version = "0.1.0", path = "../confidential-transfer/proof-generation"} + [dev-dependencies] lazy_static = "1.5.0" proptest = "1.5" @@ -44,7 +49,7 @@ serial_test = "3.1.1" solana-program-test = "2.0.3" solana-sdk = "2.0.3" spl-tlv-account-resolution = { version = "0.7.0", path = "../../libraries/tlv-account-resolution" } -serde_json = "1.0.121" +serde_json = "1.0.125" [lib] crate-type = ["cdylib", "lib"] diff --git a/token/program-2022/src/error.rs b/token/program-2022/src/error.rs index ff004f0ffb7..a886eb29e4e 100644 --- a/token/program-2022/src/error.rs +++ b/token/program-2022/src/error.rs @@ -1,5 +1,7 @@ //! Error types +#[cfg(not(target_os = "solana"))] +use spl_token_confidential_transfer_proof_generation::errors::TokenProofGenerationError; use { num_derive::FromPrimitive, solana_program::{ @@ -7,6 +9,7 @@ use { msg, program_error::{PrintProgramError, ProgramError}, }, + spl_token_confidential_transfer_proof_extraction::errors::TokenProofExtractionError, thiserror::Error, }; @@ -243,6 +246,18 @@ pub enum TokenError { /// Ciphertext arithmetic failed #[error("Ciphertext arithmetic failed")] CiphertextArithmeticFailed, + /// Pedersen commitments did not match + #[error("Pedersen commitment mismatch")] + PedersenCommitmentMismatch, + /// Range proof length did not match + #[error("Range proof length mismatch")] + RangeProofLengthMismatch, + /// Illegal transfer amount bit length + #[error("Illegal transfer amount bit length")] + IllegalBitLength, + /// Fee calculation failed + #[error("Fee calculation failed")] + FeeCalculation, } impl From for ProgramError { fn from(e: TokenError) -> Self { @@ -418,6 +433,49 @@ impl PrintProgramError for TokenError { TokenError::CiphertextArithmeticFailed => { msg!("Ciphertext arithmetic failed") } + TokenError::PedersenCommitmentMismatch => { + msg!("Pedersen commitments did not match") + } + TokenError::RangeProofLengthMismatch => { + msg!("Range proof lengths did not match") + } + TokenError::IllegalBitLength => { + msg!("Illegal transfer amount bit length") + } + TokenError::FeeCalculation => { + msg!("Transfer fee calculation failed") + } + } + } +} + +#[cfg(not(target_os = "solana"))] +impl From for TokenError { + fn from(e: TokenProofGenerationError) -> Self { + match e { + TokenProofGenerationError::ProofGeneration(_) => TokenError::ProofGeneration, + TokenProofGenerationError::NotEnoughFunds => TokenError::InsufficientFunds, + TokenProofGenerationError::IllegalAmountBitLength => TokenError::IllegalBitLength, + TokenProofGenerationError::FeeCalculation => TokenError::FeeCalculation, + } + } +} + +impl From for TokenError { + fn from(e: TokenProofExtractionError) -> Self { + match e { + TokenProofExtractionError::ElGamalPubkeyMismatch => { + TokenError::ConfidentialTransferElGamalPubkeyMismatch + } + TokenProofExtractionError::PedersenCommitmentMismatch => { + TokenError::PedersenCommitmentMismatch + } + TokenProofExtractionError::RangeProofLengthMismatch => { + TokenError::RangeProofLengthMismatch + } + TokenProofExtractionError::FeeParametersMismatch => TokenError::FeeParametersMismatch, + TokenProofExtractionError::CurveArithmetic => TokenError::CiphertextArithmeticFailed, + TokenProofExtractionError::CiphertextExtraction => TokenError::MalformedCiphertext, } } } diff --git a/token/program-2022/src/extension/confidential_transfer/account_info.rs b/token/program-2022/src/extension/confidential_transfer/account_info.rs index bfa88b73b3e..ed9a94e68dd 100644 --- a/token/program-2022/src/extension/confidential_transfer/account_info.rs +++ b/token/program-2022/src/extension/confidential_transfer/account_info.rs @@ -2,26 +2,24 @@ use { crate::{ error::TokenError, extension::confidential_transfer::{ - ciphertext_extraction::SourceDecryptHandles, - split_proof_generation::transfer_split_proof_data, ConfidentialTransferAccount, - DecryptableBalance, EncryptedBalance, PENDING_BALANCE_LO_BIT_LENGTH, + ConfidentialTransferAccount, DecryptableBalance, EncryptedBalance, + PENDING_BALANCE_LO_BIT_LENGTH, }, }, bytemuck::{Pod, Zeroable}, - solana_zk_token_sdk::{ + solana_zk_sdk::{ encryption::{ auth_encryption::{AeCiphertext, AeKey}, elgamal::{ElGamalKeypair, ElGamalPubkey, ElGamalSecretKey}, }, - instruction::{ - transfer::{FeeParameters, TransferData, TransferWithFeeData}, - withdraw::WithdrawData, - zero_balance::ZeroBalanceProofData, - BatchedGroupedCiphertext2HandlesValidityProofData, BatchedRangeProofU128Data, - CiphertextCommitmentEqualityProofData, - }, + zk_elgamal_proof_program::proof_data::ZeroCiphertextProofData, }, spl_pod::primitives::PodU64, + spl_token_confidential_transfer_proof_generation::{ + transfer::{transfer_split_proof_data, TransferProofData}, + transfer_with_fee::{transfer_with_fee_split_proof_data, TransferWithFeeProofData}, + withdraw::{withdraw_proof_data, WithdrawProofData}, + }, }; /// Confidential transfer extension information needed to construct an @@ -45,13 +43,13 @@ impl EmptyAccountAccountInfo { pub fn generate_proof_data( &self, elgamal_keypair: &ElGamalKeypair, - ) -> Result { + ) -> Result { let available_balance = self .available_balance .try_into() .map_err(|_| TokenError::MalformedCiphertext)?; - ZeroBalanceProofData::new(elgamal_keypair, &available_balance) + ZeroCiphertextProofData::new(elgamal_keypair, &available_balance) .map_err(|_| TokenError::ProofGeneration) } } @@ -180,20 +178,20 @@ impl WithdrawAccountInfo { withdraw_amount: u64, elgamal_keypair: &ElGamalKeypair, aes_key: &AeKey, - ) -> Result { + ) -> Result { let current_available_balance = self .available_balance .try_into() .map_err(|_| TokenError::MalformedCiphertext)?; let current_decrypted_available_balance = self.decrypted_available_balance(aes_key)?; - WithdrawData::new( + withdraw_proof_data( + ¤t_available_balance, + current_decrypted_available_balance, withdraw_amount, elgamal_keypair, - current_decrypted_available_balance, - ¤t_available_balance, ) - .map_err(|_| TokenError::ProofGeneration) + .map_err(|e| -> TokenError { e.into() }) } /// Update the decryptable available balance. @@ -241,37 +239,6 @@ impl TransferAccountInfo { .ok_or(TokenError::AccountDecryption) } - /// Create a transfer proof data. - pub fn generate_transfer_proof_data( - &self, - transfer_amount: u64, - elgamal_keypair: &ElGamalKeypair, - aes_key: &AeKey, - destination_elgamal_pubkey: &ElGamalPubkey, - auditor_elgamal_pubkey: Option<&ElGamalPubkey>, - ) -> Result { - let current_source_available_balance = self - .available_balance - .try_into() - .map_err(|_| TokenError::MalformedCiphertext)?; - let current_source_decrypted_available_balance = - self.decrypted_available_balance(aes_key)?; - - let default_auditor_pubkey = ElGamalPubkey::default(); - let auditor_elgamal_pubkey = auditor_elgamal_pubkey.unwrap_or(&default_auditor_pubkey); - - TransferData::new( - transfer_amount, - ( - current_source_decrypted_available_balance, - ¤t_source_available_balance, - ), - elgamal_keypair, - (destination_elgamal_pubkey, auditor_elgamal_pubkey), - ) - .map_err(|_| TokenError::ProofGeneration) - } - /// Create a transfer proof data that is split into equality, ciphertext /// validity, and range proofs. pub fn generate_split_transfer_proof_data( @@ -281,15 +248,7 @@ impl TransferAccountInfo { aes_key: &AeKey, destination_elgamal_pubkey: &ElGamalPubkey, auditor_elgamal_pubkey: Option<&ElGamalPubkey>, - ) -> Result< - ( - CiphertextCommitmentEqualityProofData, - BatchedGroupedCiphertext2HandlesValidityProofData, - BatchedRangeProofU128Data, - SourceDecryptHandles, - ), - TokenError, - > { + ) -> Result { let current_available_balance = self .available_balance .try_into() @@ -308,48 +267,46 @@ impl TransferAccountInfo { destination_elgamal_pubkey, auditor_elgamal_pubkey, ) + .map_err(|e| -> TokenError { e.into() }) } - /// Create a transfer with fee proof data + /// Create a transfer proof data that is split into equality, ciphertext + /// validity (transfer amount), percentage-with-cap, ciphertext validity + /// (fee), and range proofs. #[allow(clippy::too_many_arguments)] - pub fn generate_transfer_with_fee_proof_data( + pub fn generate_split_transfer_with_fee_proof_data( &self, transfer_amount: u64, - elgamal_keypair: &ElGamalKeypair, + source_elgamal_keypair: &ElGamalKeypair, aes_key: &AeKey, destination_elgamal_pubkey: &ElGamalPubkey, auditor_elgamal_pubkey: Option<&ElGamalPubkey>, withdraw_withheld_authority_elgamal_pubkey: &ElGamalPubkey, fee_rate_basis_points: u16, maximum_fee: u64, - ) -> Result { - let current_source_available_balance = self + ) -> Result { + let current_available_balance = self .available_balance .try_into() .map_err(|_| TokenError::MalformedCiphertext)?; - let current_source_decrypted_available_balance = - self.decrypted_available_balance(aes_key)?; - - let default_auditor_pubkey = ElGamalPubkey::default(); - let auditor_elgamal_pubkey = auditor_elgamal_pubkey.unwrap_or(&default_auditor_pubkey); - - let fee_parameters = FeeParameters { - fee_rate_basis_points, - maximum_fee, - }; + let current_decryptable_available_balance = self + .decryptable_available_balance + .try_into() + .map_err(|_| TokenError::MalformedCiphertext)?; - TransferWithFeeData::new( + transfer_with_fee_split_proof_data( + ¤t_available_balance, + ¤t_decryptable_available_balance, transfer_amount, - ( - current_source_decrypted_available_balance, - ¤t_source_available_balance, - ), - elgamal_keypair, - (destination_elgamal_pubkey, auditor_elgamal_pubkey), - fee_parameters, + source_elgamal_keypair, + aes_key, + destination_elgamal_pubkey, + auditor_elgamal_pubkey, withdraw_withheld_authority_elgamal_pubkey, + fee_rate_basis_points, + maximum_fee, ) - .map_err(|_| TokenError::ProofGeneration) + .map_err(|e| -> TokenError { e.into() }) } /// Update the decryptable available balance. diff --git a/token/program-2022/src/extension/confidential_transfer/ciphertext_extraction.rs b/token/program-2022/src/extension/confidential_transfer/ciphertext_extraction.rs deleted file mode 100644 index 85f9fd0b031..00000000000 --- a/token/program-2022/src/extension/confidential_transfer/ciphertext_extraction.rs +++ /dev/null @@ -1,685 +0,0 @@ -//! Ciphertext extraction and proof related helper logic -//! -//! This submodule should be removed with the next upgrade to the Solana program - -use crate::{ - extension::{ - confidential_transfer::*, confidential_transfer_fee::EncryptedFee, - transfer_fee::TransferFee, - }, - solana_program::program_error::ProgramError, - solana_zk_token_sdk::{ - curve25519::{ - ristretto::{self, PodRistrettoPoint}, - scalar::PodScalar, - }, - instruction::{ - transfer::{TransferProofContext, TransferWithFeeProofContext}, - BatchedGroupedCiphertext2HandlesValidityProofContext, BatchedRangeProofContext, - CiphertextCommitmentEqualityProofContext, FeeSigmaProofContext, - }, - zk_token_elgamal::pod::{ - DecryptHandle, FeeEncryption, GroupedElGamalCiphertext2Handles, - GroupedElGamalCiphertext3Handles, PedersenCommitment, TransferAmountCiphertext, - }, - }, -}; -#[cfg(feature = "serde-traits")] -use { - crate::serialization::decrypthandle_fromstr, - serde::{Deserialize, Serialize}, -}; - -/// Extract the commitment component from a grouped ciphertext with 2 handles. -/// -/// A grouped ciphertext with 2 handles consists of the following 32-bytes -/// components that are serialized in order: -/// 1. The `commitment` component that encodes the fee amount. -/// 3. The `decryption handle` component with respect to the destination -/// public key. -/// 4. The `decryption handle` component with respect to the withdraw withheld -/// authority public key. -/// -/// The fee commitment component consists of the first 32-byte. -pub(crate) fn extract_commitment_from_grouped_ciphertext( - transfer_amount_ciphertext: &GroupedElGamalCiphertext2Handles, -) -> PedersenCommitment { - let transfer_amount_ciphertext_bytes = bytemuck::bytes_of(transfer_amount_ciphertext); - let transfer_amount_commitment_bytes = - transfer_amount_ciphertext_bytes[..32].try_into().unwrap(); - PedersenCommitment(transfer_amount_commitment_bytes) -} - -/// Extract the transfer amount ciphertext encrypted under the source ElGamal -/// public key. -/// -/// A transfer amount ciphertext consists of the following 32-byte components -/// that are serialized in order: -/// 1. The `commitment` component that encodes the transfer amount. -/// 2. The `decryption handle` component with respect to the source public -/// key. -/// 3. The `decryption handle` component with respect to the destination -/// public key. -/// 4. The `decryption handle` component with respect to the auditor public -/// key. -/// -/// An ElGamal ciphertext for the source consists of the `commitment` component -/// and the `decryption handle` component with respect to the source. -pub fn transfer_amount_source_ciphertext( - transfer_amount_ciphertext: &TransferAmountCiphertext, -) -> ElGamalCiphertext { - let transfer_amount_ciphertext_bytes = bytemuck::bytes_of(transfer_amount_ciphertext); - - let mut source_ciphertext_bytes = [0u8; 64]; - source_ciphertext_bytes[..32].copy_from_slice(&transfer_amount_ciphertext_bytes[..32]); - source_ciphertext_bytes[32..].copy_from_slice(&transfer_amount_ciphertext_bytes[32..64]); - - ElGamalCiphertext(source_ciphertext_bytes) -} - -/// Extract the transfer amount ciphertext encrypted under the destination -/// ElGamal public key. -/// -/// A transfer amount ciphertext consists of the following 32-byte components -/// that are serialized in order: -/// 1. The `commitment` component that encodes the transfer amount. -/// 2. The `decryption handle` component with respect to the source public -/// key. -/// 3. The `decryption handle` component with respect to the destination -/// public key. -/// 4. The `decryption handle` component with respect to the auditor public -/// key. -/// -/// An ElGamal ciphertext for the destination consists of the `commitment` -/// component and the `decryption handle` component with respect to the -/// destination public key. -#[cfg(feature = "zk-ops")] -pub(crate) fn transfer_amount_destination_ciphertext( - transfer_amount_ciphertext: &TransferAmountCiphertext, -) -> ElGamalCiphertext { - let transfer_amount_ciphertext_bytes = bytemuck::bytes_of(transfer_amount_ciphertext); - - let mut destination_ciphertext_bytes = [0u8; 64]; - destination_ciphertext_bytes[..32].copy_from_slice(&transfer_amount_ciphertext_bytes[..32]); - destination_ciphertext_bytes[32..].copy_from_slice(&transfer_amount_ciphertext_bytes[64..96]); - - ElGamalCiphertext(destination_ciphertext_bytes) -} - -/// Extract the fee amount ciphertext encrypted under the destination ElGamal -/// public key. -/// -/// A fee encryption amount consists of the following 32-byte components that -/// are serialized in order: -/// 1. The `commitment` component that encodes the fee amount. -/// 2. The `decryption handle` component with respect to the destination -/// public key. -/// 3. The `decryption handle` component with respect to the withdraw withheld -/// authority public key. -/// -/// An ElGamal ciphertext for the destination consists of the `commitment` -/// component and the `decryption handle` component with respect to the -/// destination public key. -#[cfg(feature = "zk-ops")] -pub(crate) fn fee_amount_destination_ciphertext( - transfer_amount_ciphertext: &EncryptedFee, -) -> ElGamalCiphertext { - let transfer_amount_ciphertext_bytes = bytemuck::bytes_of(transfer_amount_ciphertext); - - let mut source_ciphertext_bytes = [0u8; 64]; - source_ciphertext_bytes[..32].copy_from_slice(&transfer_amount_ciphertext_bytes[..32]); - source_ciphertext_bytes[32..].copy_from_slice(&transfer_amount_ciphertext_bytes[32..64]); - - ElGamalCiphertext(source_ciphertext_bytes) -} - -/// Extract the transfer amount ciphertext encrypted under the withdraw withheld -/// authority ElGamal public key. -/// -/// A fee encryption amount consists of the following 32-byte components that -/// are serialized in order: -/// 1. The `commitment` component that encodes the fee amount. -/// 2. The `decryption handle` component with respect to the destination -/// public key. -/// 3. The `decryption handle` component with respect to the withdraw withheld -/// authority public key. -/// -/// An ElGamal ciphertext for the destination consists of the `commitment` -/// component and the `decryption handle` component with respect to the withdraw -/// withheld authority public key. -#[cfg(feature = "zk-ops")] -pub(crate) fn fee_amount_withdraw_withheld_authority_ciphertext( - transfer_amount_ciphertext: &EncryptedFee, -) -> ElGamalCiphertext { - let transfer_amount_ciphertext_bytes = bytemuck::bytes_of(transfer_amount_ciphertext); - - let mut destination_ciphertext_bytes = [0u8; 64]; - destination_ciphertext_bytes[..32].copy_from_slice(&transfer_amount_ciphertext_bytes[..32]); - destination_ciphertext_bytes[32..].copy_from_slice(&transfer_amount_ciphertext_bytes[64..96]); - - ElGamalCiphertext(destination_ciphertext_bytes) -} - -#[cfg(feature = "zk-ops")] -pub(crate) fn transfer_amount_encryption_from_decrypt_handle( - source_decrypt_handle: &DecryptHandle, - grouped_ciphertext: &GroupedElGamalCiphertext2Handles, -) -> TransferAmountCiphertext { - let source_decrypt_handle_bytes = bytemuck::bytes_of(source_decrypt_handle); - let grouped_ciphertext_bytes = bytemuck::bytes_of(grouped_ciphertext); - - let mut transfer_amount_ciphertext_bytes = [0u8; 128]; - transfer_amount_ciphertext_bytes[..32].copy_from_slice(&grouped_ciphertext_bytes[..32]); - transfer_amount_ciphertext_bytes[32..64].copy_from_slice(source_decrypt_handle_bytes); - transfer_amount_ciphertext_bytes[64..128].copy_from_slice(&grouped_ciphertext_bytes[32..96]); - - TransferAmountCiphertext(GroupedElGamalCiphertext3Handles( - transfer_amount_ciphertext_bytes, - )) -} - -/// The transfer public keys associated with a transfer. -#[cfg(feature = "zk-ops")] -pub struct TransferPubkeysInfo { - /// Source ElGamal public key - pub source: ElGamalPubkey, - /// Destination ElGamal public key - pub destination: ElGamalPubkey, - /// Auditor ElGamal public key - pub auditor: ElGamalPubkey, -} - -/// The proof context information needed to process a [Transfer] instruction. -#[cfg(feature = "zk-ops")] -pub struct TransferProofContextInfo { - /// Ciphertext containing the low 16 bits of the transafer amount - pub ciphertext_lo: TransferAmountCiphertext, - /// Ciphertext containing the high 32 bits of the transafer amount - pub ciphertext_hi: TransferAmountCiphertext, - /// The transfer public keys associated with a transfer - pub transfer_pubkeys: TransferPubkeysInfo, - /// The new source available balance ciphertext - pub new_source_ciphertext: ElGamalCiphertext, -} - -#[cfg(feature = "zk-ops")] -impl From for TransferProofContextInfo { - fn from(context: TransferProofContext) -> Self { - let transfer_pubkeys = TransferPubkeysInfo { - source: context.transfer_pubkeys.source, - destination: context.transfer_pubkeys.destination, - auditor: context.transfer_pubkeys.auditor, - }; - - TransferProofContextInfo { - ciphertext_lo: context.ciphertext_lo, - ciphertext_hi: context.ciphertext_hi, - transfer_pubkeys, - new_source_ciphertext: context.new_source_ciphertext, - } - } -} - -#[cfg(feature = "zk-ops")] -impl TransferProofContextInfo { - /// Create a transfer proof context information needed to process a - /// [Transfer] instruction from split proof contexts after verifying - /// their consistency. - pub fn verify_and_extract( - equality_proof_context: &CiphertextCommitmentEqualityProofContext, - ciphertext_validity_proof_context: &BatchedGroupedCiphertext2HandlesValidityProofContext, - range_proof_context: &BatchedRangeProofContext, - source_decrypt_handles: &SourceDecryptHandles, - ) -> Result { - // The equality proof context consists of the source ElGamal public key, the new - // source available balance ciphertext, and the new source available - // commitment. The public key and ciphertext should be returned as parts - // of `TransferProofContextInfo` and the commitment should be checked - // with range proof for consistency. - let CiphertextCommitmentEqualityProofContext { - pubkey: source_pubkey, - ciphertext: new_source_ciphertext, - commitment: new_source_commitment, - } = equality_proof_context; - - // The ciphertext validity proof context consists of the destination ElGamal - // public key, auditor ElGamal public key, and the transfer amount - // ciphertexts. All of these fields should be returned as part of - // `TransferProofContextInfo`. In addition, the commitments pertaining - // to the transfer amount ciphertexts should be checked with range proof for - // consistency. - let BatchedGroupedCiphertext2HandlesValidityProofContext { - destination_pubkey, - auditor_pubkey, - grouped_ciphertext_lo: transfer_amount_ciphertext_lo, - grouped_ciphertext_hi: transfer_amount_ciphertext_hi, - } = ciphertext_validity_proof_context; - - // The range proof context consists of the Pedersen commitments and bit-lengths - // for which the range proof is proved. The commitments must consist of - // three commitments pertaining to the new source available balance, the - // low bits of the transfer amount, and high bits of the transfer - // amount. These commitments must be checked for bit lengths `64`, `16`, - // and `32`. - let BatchedRangeProofContext { - commitments: range_proof_commitments, - bit_lengths: range_proof_bit_lengths, - } = range_proof_context; - - // check that the range proof was created for the correct set of Pedersen - // commitments - let transfer_amount_commitment_lo = - extract_commitment_from_grouped_ciphertext(transfer_amount_ciphertext_lo); - let transfer_amount_commitment_hi = - extract_commitment_from_grouped_ciphertext(transfer_amount_ciphertext_hi); - - let expected_commitments = [ - *new_source_commitment, - transfer_amount_commitment_lo, - transfer_amount_commitment_hi, - // the fourth dummy commitment can be any commitment - ]; - - if !range_proof_commitments - .iter() - .zip(expected_commitments.iter()) - .all(|(proof_commitment, expected_commitment)| proof_commitment == expected_commitment) - { - return Err(ProgramError::InvalidInstructionData); - } - - // check that the range proof was created for the correct number of bits - const REMAINING_BALANCE_BIT_LENGTH: u8 = 64; - const TRANSFER_AMOUNT_LO_BIT_LENGTH: u8 = 16; - const TRANSFER_AMOUNT_HI_BIT_LENGTH: u8 = 32; - const PADDING_BIT_LENGTH: u8 = 16; - let expected_bit_lengths = [ - REMAINING_BALANCE_BIT_LENGTH, - TRANSFER_AMOUNT_LO_BIT_LENGTH, - TRANSFER_AMOUNT_HI_BIT_LENGTH, - PADDING_BIT_LENGTH, - ] - .iter(); - - if !range_proof_bit_lengths - .iter() - .zip(expected_bit_lengths) - .all(|(proof_len, expected_len)| proof_len == expected_len) - { - return Err(ProgramError::InvalidInstructionData); - } - - let transfer_pubkeys = TransferPubkeysInfo { - source: *source_pubkey, - destination: *destination_pubkey, - auditor: *auditor_pubkey, - }; - - let transfer_amount_ciphertext_lo = transfer_amount_encryption_from_decrypt_handle( - &source_decrypt_handles.lo, - transfer_amount_ciphertext_lo, - ); - - let transfer_amount_ciphertext_hi = transfer_amount_encryption_from_decrypt_handle( - &source_decrypt_handles.hi, - transfer_amount_ciphertext_hi, - ); - - Ok(Self { - ciphertext_lo: transfer_amount_ciphertext_lo, - ciphertext_hi: transfer_amount_ciphertext_hi, - transfer_pubkeys, - new_source_ciphertext: *new_source_ciphertext, - }) - } -} - -/// The transfer public keys associated with a transfer with fee. -#[cfg(feature = "zk-ops")] -pub struct TransferWithFeePubkeysInfo { - /// Source ElGamal public key - pub source: ElGamalPubkey, - /// Destination ElGamal public key - pub destination: ElGamalPubkey, - /// Auditor ElGamal public key - pub auditor: ElGamalPubkey, - /// Withdraw withheld authority public key - pub withdraw_withheld_authority: ElGamalPubkey, -} - -/// The proof context information needed to process a [Transfer] instruction -/// with fee. -#[cfg(feature = "zk-ops")] -pub struct TransferWithFeeProofContextInfo { - /// Group encryption of the low 16 bits of the transfer amount - pub ciphertext_lo: TransferAmountCiphertext, - /// Group encryption of the high 48 bits of the transfer amount - pub ciphertext_hi: TransferAmountCiphertext, - /// The public encryption keys associated with the transfer: source, dest, - /// auditor, and withdraw withheld authority - pub transfer_with_fee_pubkeys: TransferWithFeePubkeysInfo, - /// The final spendable ciphertext after the transfer, - pub new_source_ciphertext: ElGamalCiphertext, - /// The transfer fee encryption of the low 16 bits of the transfer fee - /// amount - pub fee_ciphertext_lo: EncryptedFee, - /// The transfer fee encryption of the hi 32 bits of the transfer fee amount - pub fee_ciphertext_hi: EncryptedFee, -} - -#[cfg(feature = "zk-ops")] -impl From for TransferWithFeeProofContextInfo { - fn from(context: TransferWithFeeProofContext) -> Self { - let transfer_with_fee_pubkeys = TransferWithFeePubkeysInfo { - source: context.transfer_with_fee_pubkeys.source, - destination: context.transfer_with_fee_pubkeys.destination, - auditor: context.transfer_with_fee_pubkeys.auditor, - withdraw_withheld_authority: context - .transfer_with_fee_pubkeys - .withdraw_withheld_authority, - }; - - TransferWithFeeProofContextInfo { - ciphertext_lo: context.ciphertext_lo, - ciphertext_hi: context.ciphertext_hi, - transfer_with_fee_pubkeys, - new_source_ciphertext: context.new_source_ciphertext, - fee_ciphertext_lo: context.fee_ciphertext_lo, - fee_ciphertext_hi: context.fee_ciphertext_hi, - } - } -} - -#[cfg(feature = "zk-ops")] -impl TransferWithFeeProofContextInfo { - /// Create a transfer proof context information needed to process a - /// [Transfer] instruction from split proof contexts after verifying - /// their consistency. - pub fn verify_and_extract( - equality_proof_context: &CiphertextCommitmentEqualityProofContext, - transfer_amount_ciphertext_validity_proof_context: &BatchedGroupedCiphertext2HandlesValidityProofContext, - fee_sigma_proof_context: &FeeSigmaProofContext, - fee_ciphertext_validity_proof_context: &BatchedGroupedCiphertext2HandlesValidityProofContext, - range_proof_context: &BatchedRangeProofContext, - source_decrypt_handles: &SourceDecryptHandles, - fee_parameters: &TransferFee, - ) -> Result { - // The equality proof context consists of the source ElGamal public key, the new - // source available balance ciphertext, and the new source available - // commitment. The public key and ciphertext should be returned as part - // of `TransferWithFeeProofContextInfo` and the commitment should be - // checked with range proof for consistency. - let CiphertextCommitmentEqualityProofContext { - pubkey: source_pubkey, - ciphertext: new_source_ciphertext, - commitment: new_source_commitment, - } = equality_proof_context; - - // The transfer amount ciphertext validity proof context consists of the - // destination ElGamal public key, auditor ElGamal public key, and the - // transfer amount ciphertexts. All of these fields should be returned - // as part of `TransferWithFeeProofContextInfo`. In addition, the - // commitments pertaining to the transfer amount ciphertexts should be - // checked with range proof for consistency. - let BatchedGroupedCiphertext2HandlesValidityProofContext { - destination_pubkey, - auditor_pubkey, - grouped_ciphertext_lo: transfer_amount_ciphertext_lo, - grouped_ciphertext_hi: transfer_amount_ciphertext_hi, - } = transfer_amount_ciphertext_validity_proof_context; - - // The fee sigma proof context consists of the fee commitment, delta commitment, - // claimed commitment, and max fee. The fee and claimed commitment - // should be checked with range proof for consistency. The delta - // commitment should be checked whether it is properly generated with - // respect to the fee parameters. The max fee should be checked for - // consistency with the fee parameters. - let FeeSigmaProofContext { - fee_commitment, - delta_commitment, - claimed_commitment, - max_fee, - } = fee_sigma_proof_context; - - let expected_maximum_fee: u64 = fee_parameters.maximum_fee.into(); - let proof_maximum_fee: u64 = (*max_fee).into(); - if expected_maximum_fee != proof_maximum_fee { - return Err(ProgramError::InvalidInstructionData); - } - - // The transfer fee ciphertext validity proof context consists of the - // destination ElGamal public key, withdraw withheld authority ElGamal - // public key, and the transfer fee ciphertexts. The rest of the fields - // should be return as part of `TransferWithFeeProofContextInfo`. In - // addition, the destination public key should be checked for - // consistency with the destination public key contained in the transfer amount - // ciphertext validity proof, and the commitments pertaining to the transfer fee - // amount ciphertexts should be checked with range proof for - // consistency. - let BatchedGroupedCiphertext2HandlesValidityProofContext { - destination_pubkey: destination_pubkey_from_transfer_fee_validity_proof, - auditor_pubkey: withdraw_withheld_authority_pubkey, - grouped_ciphertext_lo: fee_ciphertext_lo, - grouped_ciphertext_hi: fee_ciphertext_hi, - } = fee_ciphertext_validity_proof_context; - - if destination_pubkey != destination_pubkey_from_transfer_fee_validity_proof { - return Err(ProgramError::InvalidInstructionData); - } - - // The range proof context consists of the Pedersen commitments and bit-lengths - // for which the range proof is proved. The commitments must consist of - // seven commitments pertaining to - // - the new source available balance (64 bits) - // - the low bits of the transfer amount (16 bits) - // - the high bits of the transfer amount (32 bits) - // - the delta amount for the fee (48 bits) - // - the complement of the delta amount for the fee (48 bits) - // - the low bits of the fee amount (16 bits) - // - the high bits of the fee amount (32 bits) - let BatchedRangeProofContext { - commitments: range_proof_commitments, - bit_lengths: range_proof_bit_lengths, - } = range_proof_context; - - // check that the range proof was created for the correct set of Pedersen - // commitments - let transfer_amount_commitment_lo = - extract_commitment_from_grouped_ciphertext(transfer_amount_ciphertext_lo); - let transfer_amount_commitment_hi = - extract_commitment_from_grouped_ciphertext(transfer_amount_ciphertext_hi); - - let fee_commitment_lo = extract_commitment_from_grouped_ciphertext(fee_ciphertext_lo); - let fee_commitment_hi = extract_commitment_from_grouped_ciphertext(fee_ciphertext_hi); - - const MAX_FEE_BASIS_POINTS: u64 = 10_000; - let max_fee_basis_points_scalar = u64_to_scalar(MAX_FEE_BASIS_POINTS); - let max_fee_basis_points_commitment = - ristretto::multiply_ristretto(&max_fee_basis_points_scalar, &G) - .ok_or(TokenError::CiphertextArithmeticFailed)?; - let claimed_complement_commitment = ristretto::subtract_ristretto( - &max_fee_basis_points_commitment, - &(*claimed_commitment).into(), - ) - .ok_or(TokenError::CiphertextArithmeticFailed)?; - - let expected_commitments = [ - *new_source_commitment, - transfer_amount_commitment_lo, - transfer_amount_commitment_hi, - *claimed_commitment, - claimed_complement_commitment.into(), - fee_commitment_lo, - fee_commitment_hi, - ]; - - if !range_proof_commitments - .iter() - .zip(expected_commitments.iter()) - .all(|(proof_commitment, expected_commitment)| proof_commitment == expected_commitment) - { - return Err(ProgramError::InvalidInstructionData); - } - - // check that the range proof was created for the correct number of bits - const REMAINING_BALANCE_BIT_LENGTH: u8 = 64; - const TRANSFER_AMOUNT_LO_BIT_LENGTH: u8 = 16; - const TRANSFER_AMOUNT_HI_BIT_LENGTH: u8 = 32; - const DELTA_BIT_LENGTH: u8 = 48; - const FEE_AMOUNT_LO_BIT_LENGTH: u8 = 16; - const FEE_AMOUNT_HI_BIT_LENGTH: u8 = 32; - - let expected_bit_lengths = [ - REMAINING_BALANCE_BIT_LENGTH, - TRANSFER_AMOUNT_LO_BIT_LENGTH, - TRANSFER_AMOUNT_HI_BIT_LENGTH, - DELTA_BIT_LENGTH, - DELTA_BIT_LENGTH, - FEE_AMOUNT_LO_BIT_LENGTH, - FEE_AMOUNT_HI_BIT_LENGTH, - ] - .iter(); - - if !range_proof_bit_lengths - .iter() - .zip(expected_bit_lengths) - .all(|(proof_len, expected_len)| proof_len == expected_len) - { - return Err(ProgramError::InvalidInstructionData); - } - - // check consistency between fee sigma and fee ciphertext validity proofs - let sigma_proof_fee_commitment_point: PodRistrettoPoint = (*fee_commitment).into(); - let validity_proof_fee_point = - combine_lo_hi_pedersen_points(&fee_commitment_lo.into(), &fee_commitment_hi.into()) - .ok_or(TokenError::CiphertextArithmeticFailed)?; - if validity_proof_fee_point != sigma_proof_fee_commitment_point { - return Err(ProgramError::InvalidInstructionData); - } - - verify_delta_commitment( - &transfer_amount_commitment_lo, - &transfer_amount_commitment_hi, - fee_commitment, - delta_commitment, - fee_parameters.transfer_fee_basis_points.into(), - )?; - - // create transfer with fee proof context info and return - let transfer_with_fee_pubkeys = TransferWithFeePubkeysInfo { - source: *source_pubkey, - destination: *destination_pubkey, - auditor: *auditor_pubkey, - withdraw_withheld_authority: *withdraw_withheld_authority_pubkey, - }; - - let transfer_amount_ciphertext_lo = transfer_amount_encryption_from_decrypt_handle( - &source_decrypt_handles.lo, - transfer_amount_ciphertext_lo, - ); - - let transfer_amount_ciphertext_hi = transfer_amount_encryption_from_decrypt_handle( - &source_decrypt_handles.hi, - transfer_amount_ciphertext_hi, - ); - - Ok(Self { - ciphertext_lo: transfer_amount_ciphertext_lo, - ciphertext_hi: transfer_amount_ciphertext_hi, - transfer_with_fee_pubkeys, - new_source_ciphertext: *new_source_ciphertext, - fee_ciphertext_lo: FeeEncryption(*fee_ciphertext_lo), - fee_ciphertext_hi: FeeEncryption(*fee_ciphertext_hi), - }) - } -} - -/// The ElGamal ciphertext decryption handle pertaining to the low and high bits -/// of the transfer amount under the source public key of the transfer. -/// -/// The `TransferProofContext` contains decryption handles for the low and high -/// bits of the transfer amount. However, these decryption handles were -/// (mistakenly) removed from the split proof contexts as a form of -/// optimization. These components should be added back into these split proofs -/// in `zk-token-sdk`. Until this modifications is made, include -/// `SourceDecryptHandle` in the transfer instruction data. -#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "serde-traits", serde(rename_all = "camelCase"))] -#[repr(C)] -#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] -pub struct SourceDecryptHandles { - /// The ElGamal decryption handle pertaining to the low 16 bits of the - /// transfer amount. - #[cfg_attr(feature = "serde-traits", serde(with = "decrypthandle_fromstr"))] - pub lo: DecryptHandle, - /// The ElGamal decryption handle pertaining to the low 32 bits of the - /// transfer amount. - #[cfg_attr(feature = "serde-traits", serde(with = "decrypthandle_fromstr"))] - pub hi: DecryptHandle, -} - -/// Ristretto generator point for curve25519 -const G: PodRistrettoPoint = PodRistrettoPoint([ - 226, 242, 174, 10, 106, 188, 78, 113, 168, 132, 169, 97, 197, 0, 81, 95, 88, 227, 11, 106, 165, - 130, 221, 141, 182, 166, 89, 69, 224, 141, 45, 118, -]); - -/// Convert a `u16` amount into a curve25519 scalar -fn u16_to_scalar(amount: u16) -> PodScalar { - let mut bytes = [0u8; 32]; - bytes[..2].copy_from_slice(&amount.to_le_bytes()); - PodScalar(bytes) -} - -/// Convert a `u64` amount into a curve25519 scalar -fn u64_to_scalar(amount: u64) -> PodScalar { - let mut bytes = [0u8; 32]; - bytes[..8].copy_from_slice(&amount.to_le_bytes()); - PodScalar(bytes) -} - -/// Combine lo and hi Pedersen commitment points -fn combine_lo_hi_pedersen_points( - point_lo: &PodRistrettoPoint, - point_hi: &PodRistrettoPoint, -) -> Option { - const SCALING_CONSTANT: u64 = 65536; - let scaling_constant_scalar = u64_to_scalar(SCALING_CONSTANT); - let scaled_point_hi = ristretto::multiply_ristretto(&scaling_constant_scalar, point_hi)?; - ristretto::add_ristretto(point_lo, &scaled_point_hi) -} - -/// Compute fee delta commitment -fn verify_delta_commitment( - transfer_amount_commitment_lo: &PedersenCommitment, - transfer_amount_commitment_hi: &PedersenCommitment, - fee_commitment: &PedersenCommitment, - proof_delta_commitment: &PedersenCommitment, - transfer_fee_basis_points: u16, -) -> Result<(), ProgramError> { - let transfer_amount_point = combine_lo_hi_pedersen_points( - &(*transfer_amount_commitment_lo).into(), - &(*transfer_amount_commitment_hi).into(), - ) - .ok_or(TokenError::CiphertextArithmeticFailed)?; - let transfer_fee_basis_points_scalar = u16_to_scalar(transfer_fee_basis_points); - let scaled_transfer_amount_point = - ristretto::multiply_ristretto(&transfer_fee_basis_points_scalar, &transfer_amount_point) - .ok_or(TokenError::CiphertextArithmeticFailed)?; - - const MAX_FEE_BASIS_POINTS: u64 = 10_000; - let max_fee_basis_points_scalar = u64_to_scalar(MAX_FEE_BASIS_POINTS); - let fee_point: PodRistrettoPoint = (*fee_commitment).into(); - let scaled_fee_point = ristretto::multiply_ristretto(&max_fee_basis_points_scalar, &fee_point) - .ok_or(TokenError::CiphertextArithmeticFailed)?; - - let expected_delta_commitment_point = - ristretto::subtract_ristretto(&scaled_fee_point, &scaled_transfer_amount_point) - .ok_or(TokenError::CiphertextArithmeticFailed)?; - - let proof_delta_commitment_point = (*proof_delta_commitment).into(); - if expected_delta_commitment_point != proof_delta_commitment_point { - return Err(ProgramError::InvalidInstructionData); - } - Ok(()) -} diff --git a/token/program-2022/src/extension/confidential_transfer/instruction.rs b/token/program-2022/src/extension/confidential_transfer/instruction.rs index ac73574f5a9..ddbfb242635 100644 --- a/token/program-2022/src/extension/confidential_transfer/instruction.rs +++ b/token/program-2022/src/extension/confidential_transfer/instruction.rs @@ -1,7 +1,5 @@ -#[cfg(not(target_os = "solana"))] -use solana_zk_token_sdk::encryption::auth_encryption::AeCiphertext; -pub use solana_zk_token_sdk::{ - zk_token_proof_instruction::*, zk_token_proof_state::ProofContextState, +pub use solana_zk_sdk::zk_elgamal_proof_program::{ + instruction::ProofInstruction, proof_data::*, state::ProofContextState, }; #[cfg(feature = "serde-traits")] use { @@ -11,11 +9,11 @@ use { use { crate::{ check_program_account, - extension::confidential_transfer::{ciphertext_extraction::SourceDecryptHandles, *}, + extension::confidential_transfer::*, instruction::{encode_instruction, TokenInstruction}, - proof::ProofLocation, + proof::{ProofData, ProofLocation}, }, - bytemuck::Zeroable, // `Pod` comes from zk_token_proof_instruction + bytemuck::Zeroable, num_enum::{IntoPrimitive, TryFromPrimitive}, solana_program::{ instruction::{AccountMeta, Instruction}, @@ -77,8 +75,8 @@ pub enum ConfidentialTransferInstruction { /// `DisableNonConfidentialCredits` instructions to disable. /// /// In order for this instruction to be successfully processed, it must be - /// accompanied by the `VerifyPubkeyValidityProof` instruction of the - /// `zk_token_proof` program in the same transaction or the address of a + /// accompanied by the `VerifyPubkeyValidity` instruction of the + /// `zk_elgamal_proof` program in the same transaction or the address of a /// context state account for the proof must be provided. /// /// Accounts expected by this instruction: @@ -86,21 +84,25 @@ pub enum ConfidentialTransferInstruction { /// * Single owner/delegate /// 0. `[writeable]` The SPL Token account. /// 1. `[]` The corresponding SPL Token mint. - /// 2. `[]` Instructions sysvar if `VerifyPubkeyValidityProof` is included - /// in the same transaction or context state account if - /// `VerifyPubkeyValidityProof` is pre-verified into a context state + /// 2. `[]` Instructions sysvar if `VerifyPubkeyValidity` is included in + /// the same transaction or context state account if + /// `VerifyPubkeyValidity` is pre-verified into a context state /// account. - /// 3. `[signer]` The single source account owner. + /// 3. `[]` (Optional) Record account if the accompanying proof is to be + /// read from a record account. + /// 4. `[signer]` The single source account owner. /// /// * Multisignature owner/delegate /// 0. `[writeable]` The SPL Token account. /// 1. `[]` The corresponding SPL Token mint. - /// 2. `[]` Instructions sysvar if `VerifyPubkeyValidityProof` is included - /// in the same transaction or context state account if - /// `VerifyPubkeyValidityProof` is pre-verified into a context state + /// 2. `[]` Instructions sysvar if `VerifyPubkeyValidity` is included in + /// the same transaction or context state account if + /// `VerifyPubkeyValidity` is pre-verified into a context state /// account. - /// 3. `[]` The multisig source account owner. - /// 4.. `[signer]` Required M signer accounts for the SPL Token Multisig + /// 3. `[]` (Optional) Record account if the accompanying proof is to be + /// read from a record account. + /// 4. `[]` The multisig source account owner. + /// 5.. `[signer]` Required M signer accounts for the SPL Token Multisig /// account. /// /// Data expected by this instruction: @@ -141,26 +143,30 @@ pub enum ConfidentialTransferInstruction { /// token account. /// /// In order for this instruction to be successfully processed, it must be - /// accompanied by the `VerifyZeroBalanceProof` instruction of the - /// `zk_token_proof` program in the same transaction or the address of a + /// accompanied by the `VerifyZeroCiphertext` instruction of the + /// `zk_elgamal_proof` program in the same transaction or the address of a /// context state account for the proof must be provided. /// /// * Single owner/delegate /// 0. `[writable]` The SPL Token account. - /// 1. `[]` Instructions sysvar if `VerifyZeroBalanceProof` is included in + /// 1. `[]` Instructions sysvar if `VerifyZeroCiphertext` is included in /// the same transaction or context state account if - /// `VerifyZeroBalanceProof` is pre-verified into a context state + /// `VerifyZeroCiphertext` is pre-verified into a context state /// account. - /// 2. `[signer]` The single account owner. + /// 2. `[]` (Optional) Record account if the accompanying proof is to be + /// read from a record account. + /// 3. `[signer]` The single account owner. /// /// * Multisignature owner/delegate /// 0. `[writable]` The SPL Token account. - /// 1. `[]` Instructions sysvar if `VerifyZeroBalanceProof` is included in + /// 1. `[]` Instructions sysvar if `VerifyZeroCiphertext` is included in /// the same transaction or context state account if - /// `VerifyZeroBalanceProof` is pre-verified into a context state + /// `VerifyZeroCiphertext` is pre-verified into a context state /// account. - /// 2. `[]` The multisig account owner. - /// 3.. `[signer]` Required M signer accounts for the SPL Token Multisig + /// 2. `[]` (Optional) Record account if the accompanying proof is to be + /// read from a record account. + /// 3. `[]` The multisig account owner. + /// 4.. `[signer]` Required M signer accounts for the SPL Token Multisig /// account. /// /// Data expected by this instruction: @@ -198,32 +204,44 @@ pub enum ConfidentialTransferInstruction { /// Withdraw SPL Tokens from the available balance of a confidential token /// account. /// + /// In order for this instruction to be successfully processed, it must be + /// accompanied by the following list of `zk_elgamal_proof` program + /// instructions: + /// - `VerifyCiphertextCommitmentEquality` + /// - `VerifyBatchedRangeProofU64` + /// These instructions can be accompanied in the same transaction or can be + /// pre-verified into a context state account, in which case, only their + /// context state account address need to be provided. + /// /// Fails if the source or destination accounts are frozen. /// Fails if the associated mint is extended as `NonTransferable`. /// - /// In order for this instruction to be successfully processed, it must be - /// accompanied by the `VerifyWithdraw` instruction of the - /// `zk_token_proof` program in the same transaction or the address of a - /// context state account for the proof must be provided. - /// /// Accounts expected by this instruction: /// /// * Single owner/delegate /// 0. `[writable]` The SPL Token account. /// 1. `[]` The token mint. - /// 2. `[]` Instructions sysvar if `VerifyWithdraw` is included in the - /// same transaction or context state account if `VerifyWithdraw` is - /// pre-verified into a context state account. - /// 3. `[signer]` The single source account owner. + /// 2. `[]` (Optional) Instructions sysvar if at least one of the + /// `zk_elgamal_proof` instructions are included in the same + /// transaction. + /// 3. `[]` (Optional) Equality proof record account or context state + /// account. + /// 4. `[]` (Optional) Range proof record account or context state + /// account. + /// 5. `[signer]` The single source account owner. /// /// * Multisignature owner/delegate /// 0. `[writable]` The SPL Token account. /// 1. `[]` The token mint. - /// 2. `[]` Instructions sysvar if `VerifyWithdraw` is included in the - /// same transaction or context state account if `VerifyWithdraw` is - /// pre-verified into a context state account. - /// 3. `[]` The multisig source account owner. - /// 4.. `[signer]` Required M signer accounts for the SPL Token Multisig + /// 2. `[]` (Optional) Instructions sysvar if at least one of the + /// `zk_elgamal_proof` instructions are included in the same + /// transaction. + /// 3. `[]` (Optional) Equality proof record account or context state + /// account. + /// 4. `[]` (Optional) Range proof record account or context state + /// account. + /// 5. `[]` The multisig source account owner. + /// 6.. `[signer]` Required M signer accounts for the SPL Token Multisig /// account. /// /// Data expected by this instruction: @@ -233,10 +251,14 @@ pub enum ConfidentialTransferInstruction { /// Transfer tokens confidentially. /// /// In order for this instruction to be successfully processed, it must be - /// accompanied by either the `VerifyTransfer` or - /// `VerifyTransferWithFee` instruction of the `zk_token_proof` - /// program in the same transaction or the address of a context state - /// account for the proof must be provided. + /// accompanied by the following list of `zk_elgamal_proof` program + /// instructions: + /// - `VerifyCiphertextCommitmentEquality` + /// - `VerifyBatchedGroupedCiphertext3HandlesValidity` + /// - `VerifyBatchedRangeProofU128` + /// These instructions can be accompanied in the same transaction or can be + /// pre-verified into a context state account, in which case, only their + /// context state account addresses need to be provided. /// /// Fails if the associated mint is extended as `NonTransferable`. /// @@ -244,22 +266,32 @@ pub enum ConfidentialTransferInstruction { /// 1. `[writable]` The source SPL Token account. /// 2. `[]` The token mint. /// 3. `[writable]` The destination SPL Token account. - /// 4. `[]` Instructions sysvar if `VerifyTransfer` or - /// `VerifyTransferWithFee` is included in the same transaction or - /// context state account if these proofs are pre-verified into a - /// context state account. - /// 5. `[signer]` The single source account owner. + /// 4. `[]` (Optional) Instructions sysvar if at least one of the + /// `zk_elgamal_proof` instructions are included in the same + /// transaction. + /// 5. `[]` (Optional) Equality proof record account or context state + /// account. + /// 6. `[]` (Optional) Ciphertext validity proof record account or context + /// state account. + /// 7. `[]` (Optional) Range proof record account or context state + /// account. + /// 8. `[signer]` The single source account owner. /// /// * Multisignature owner/delegate /// 1. `[writable]` The source SPL Token account. /// 2. `[]` The token mint. /// 3. `[writable]` The destination SPL Token account. - /// 4. `[]` Instructions sysvar if `VerifyTransfer` or - /// `VerifyTransferWithFee` is included in the same transaction or - /// context state account if these proofs are pre-verified into a - /// context state account. - /// 5. `[]` The multisig source account owner. - /// 6.. `[signer]` Required M signer accounts for the SPL Token Multisig + /// 4. `[]` (Optional) Instructions sysvar if at least one of the + /// `zk_elgamal_proof` instructions are included in the same + /// transaction. + /// 5. `[]` (Optional) Equality proof record account or context state + /// account. + /// 6. `[]` (Optional) Ciphertext validity proof record account or context + /// state account. + /// 7. `[]` (Optional) Range proof record account or context state + /// account. + /// 8. `[]` The multisig source account owner. + /// 9.. `[signer]` Required M signer accounts for the SPL Token Multisig /// account. /// /// Data expected by this instruction: @@ -379,59 +411,67 @@ pub enum ConfidentialTransferInstruction { /// None DisableNonConfidentialCredits, - /// Transfer tokens confidentially with zero-knowledge proofs that are split - /// into smaller components. + /// Transfer tokens confidentially with fee. /// /// In order for this instruction to be successfully processed, it must be - /// accompanied by suitable zero-knowledge proof context accounts listed - /// below. + /// accompanied by the following list of `zk_elgamal_proof` program + /// instructions: + /// - `VerifyCiphertextCommitmentEquality` + /// - `VerifyBatchedGroupedCiphertext3HandlesValidity` (transfer amount + /// ciphertext) + /// - `VerifyPercentageWithFee` + /// - `VerifyBatchedGroupedCiphertext2HandlesValidity` (fee ciphertext) + /// - `VerifyBatchedRangeProofU256` + /// These instructions can be accompanied in the same transaction or can be + /// pre-verified into a context state account, in which case, only their + /// context state account addresses need to be provided. /// /// The same restrictions for the `Transfer` applies to - /// `TransferWithSplitProofs`. Namely, the instruction fails if the + /// `TransferWithFee`. Namely, the instruction fails if the /// associated mint is extended as `NonTransferable`. /// /// * Transfer without fee /// 1. `[writable]` The source SPL Token account. /// 2. `[]` The token mint. /// 3. `[writable]` The destination SPL Token account. - /// 4. `[]` Context state account for - /// `VerifyCiphertextCommitmentEqualityProof`. - /// 5. `[]` Context state account for - /// `VerifyBatchedGroupedCiphertext2HandlesValidityProof`. - /// 6. `[]` Context state account for `VerifyBatchedRangeProofU128`. - /// If `close_split_context_state_on_execution` is set, all context state - /// accounts must be `writable` and the following sequence - /// of accounts that are marked with asterisk are needed: - /// 7*. `[]` The destination account for lamports from the context state - /// accounts. - /// 8*. `[signer]` The context state account owner. - /// 9*. `[]` The zk token proof program. + /// 4. `[]` (Optional) Instructions sysvar if at least one of the + /// `zk_elgamal_proof` instructions are included in the same + /// transaction. + /// 5. `[]` (Optional) Equality proof record account or context state + /// account. + /// 6. `[]` (Optional) Transfer amount ciphertext validity proof record + /// account or context state account. + /// 7. `[]` (Optional) Fee sigma proof record account or context state + /// account. + /// 8. `[]` (Optional) Fee ciphertext validity proof record account or + /// context state account. + /// 9. `[]` (Optional) Range proof record account or context state + /// account. /// 10. `[signer]` The source account owner. /// /// * Transfer with fee /// 1. `[writable]` The source SPL Token account. /// 2. `[]` The token mint. /// 3. `[writable]` The destination SPL Token account. - /// 4. `[]` Context state account for - /// `VerifyCiphertextCommitmentEqualityProof`. - /// 5. `[]` Context state account for - /// `VerifyBatchedGroupedCiphertext2HandlesValidityProof`. - /// 6. `[]` Context state account for `VerifyFeeSigmaProof`. - /// 7. `[]` Context state account for - /// `VerifyBatchedGroupedCiphertext2HandlesValidityProof`. - /// 8. `[]` Context state account for `VerifyBatchedRangeProofU256`. - /// If `close_split_context_state_on_execution` is set, all context state - /// accounts must be `writable` and the following sequence - /// of accounts that are marked with asterisk are needed: - /// 9*. `[]` The destination account for lamports from the context state - /// accounts. - /// 10*. `[signer]` The context state account owner. - /// 11*. `[]` The zk token proof program. - /// 12. `[signer]` The source account owner. + /// 4. `[]` (Optional) Instructions sysvar if at least one of the + /// `zk_elgamal_proof` instructions are included in the same + /// transaction. + /// 5. `[]` (Optional) Equality proof record account or context state + /// account. + /// 6. `[]` (Optional) Transfer amount ciphertext validity proof record + /// account or context state account. + /// 7. `[]` (Optional) Fee sigma proof record account or context state + /// account. + /// 8. `[]` (Optional) Fee ciphertext validity proof record account or + /// context state account. + /// 9. `[]` (Optional) Range proof record account or context state + /// account. + /// 10. `[]` The multisig source account owner. + /// 11.. `[signer]` Required M signer accounts for the SPL Token Multisig /// /// Data expected by this instruction: - /// `TransferWithSplitProofsInstructionData` - TransferWithSplitProofs, + /// `TransferWithFeeInstructionData` + TransferWithFee, } /// Data expected by `ConfidentialTransferInstruction::InitializeMint` @@ -475,7 +515,7 @@ pub struct ConfigureAccountInstructionData { /// The maximum number of despots and transfers that an account can receiver /// before the `ApplyPendingBalance` is executed pub maximum_pending_balance_credit_counter: PodU64, - /// Relative location of the `ProofInstruction::ZeroBalanceProof` + /// Relative location of the `ProofInstruction::ZeroCiphertextProof` /// instruction to the `ConfigureAccount` instruction in the /// transaction. If the offset is `0`, then use a context state account /// for the proof. @@ -519,10 +559,15 @@ pub struct WithdrawInstructionData { /// The new decryptable balance if the withdrawal succeeds #[cfg_attr(feature = "serde-traits", serde(with = "aeciphertext_fromstr"))] pub new_decryptable_available_balance: DecryptableBalance, - /// Relative location of the `ProofInstruction::VerifyWithdraw` instruction + /// Relative location of the + /// `ProofInstruction::VerifyCiphertextCommitmentEquality` instruction /// to the `Withdraw` instruction in the transaction. If the offset is /// `0`, then use a context state account for the proof. - pub proof_instruction_offset: i8, + pub equality_proof_instruction_offset: i8, + /// Relative location of the `ProofInstruction::BatchedRangeProofU64` + /// instruction to the `Withdraw` instruction in the transaction. If the + /// offset is `0`, then use a context state account for the proof. + pub range_proof_instruction_offset: i8, } /// Data expected by `ConfidentialTransferInstruction::Transfer` @@ -534,10 +579,20 @@ pub struct TransferInstructionData { /// The new source decryptable balance if the transfer succeeds #[cfg_attr(feature = "serde-traits", serde(with = "aeciphertext_fromstr"))] pub new_source_decryptable_available_balance: DecryptableBalance, - /// Relative location of the `ProofInstruction::VerifyTransfer` instruction + /// Relative location of the + /// `ProofInstruction::VerifyCiphertextCommitmentEquality` instruction /// to the `Transfer` instruction in the transaction. If the offset is /// `0`, then use a context state account for the proof. - pub proof_instruction_offset: i8, + pub equality_proof_instruction_offset: i8, + /// Relative location of the + /// `ProofInstruction::VerifyBatchedGroupedCiphertext3HandlesValidity` + /// instruction to the `Transfer` instruction in the transaction. If the + /// offset is `0`, then use a context state account for the proof. + pub ciphertext_validity_proof_instruction_offset: i8, + /// Relative location of the `ProofInstruction::BatchedRangeProofU128Data` + /// instruction to the `Transfer` instruction in the transaction. If the + /// offset is `0`, then use a context state account for the proof. + pub range_proof_instruction_offset: i8, } /// Data expected by `ConfidentialTransferInstruction::ApplyPendingBalance` @@ -555,89 +610,41 @@ pub struct ApplyPendingBalanceData { pub new_decryptable_available_balance: DecryptableBalance, } -/// Data expected by `ConfidentialTransferInstruction::TransferWithSplitProofs` +/// Data expected by `ConfidentialTransferInstruction::TransferWithFee` #[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] #[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)] #[repr(C)] -pub struct TransferWithSplitProofsInstructionData { +pub struct TransferWithFeeInstructionData { /// The new source decryptable balance if the transfer succeeds #[cfg_attr(feature = "serde-traits", serde(with = "aeciphertext_fromstr"))] pub new_source_decryptable_available_balance: DecryptableBalance, - /// If true, execute no op when an associated context state account is not - /// initialized. Otherwise, fail on an uninitialized context state - /// account. - pub no_op_on_uninitialized_split_context_state: PodBool, - /// Close associated context states after a complete execution of the - /// transfer instruction. - pub close_split_context_state_on_execution: PodBool, - /// The ElGamal decryption handle pertaining to the low and high bits of the - /// transfer amount. This field is used when the transfer proofs are - /// split and verified as smaller components. - /// - /// NOTE: This field is to be removed in the next Solana upgrade. - pub source_decrypt_handles: SourceDecryptHandles, -} - -/// Type for split transfer (without fee) instruction proof context state -/// account addresses intended to be used as parameters to functions. -#[derive(Clone, Copy)] -pub struct TransferSplitContextStateAccounts<'a> { - /// The context state account address for an equality proof needed for a - /// transfer. - pub equality_proof: &'a Pubkey, - /// The context state account address for a ciphertext validity proof needed - /// for a transfer. - pub ciphertext_validity_proof: &'a Pubkey, - /// The context state account address for a range proof needed for a - /// transfer. - pub range_proof: &'a Pubkey, - /// The context state accounts authority - pub authority: &'a Pubkey, - /// No op if an associated split proof context state account is not - /// initialized. - pub no_op_on_uninitialized_split_context_state: bool, - /// Accounts needed if `close_split_context_state_on_execution` flag is - /// enabled. - pub close_split_context_state_accounts: Option>, -} - -/// Type for split transfer (with fee) instruction proof context state account -/// addresses intended to be used as parameters to functions. -#[derive(Clone, Copy)] -pub struct TransferWithFeeSplitContextStateAccounts<'a> { - /// The context state account address for an equality proof needed for a - /// transfer with fee. - pub equality_proof: &'a Pubkey, - /// The context state account address for a transfer amount ciphertext - /// validity proof needed for a transfer with fee. - pub transfer_amount_ciphertext_validity_proof: &'a Pubkey, - /// The context state account address for a fee sigma proof needed for a - /// transfer with fee. - pub fee_sigma_proof: &'a Pubkey, - /// The context state account address for a fee ciphertext validity proof - /// needed for a transfer with fee. - pub fee_ciphertext_validity_proof: &'a Pubkey, - /// The context state account address for a range proof needed for a - /// transfer with fee. - pub range_proof: &'a Pubkey, - /// The context state accounts authority - pub authority: &'a Pubkey, - /// No op if an associated split proof context state account is not - /// initialized. - pub no_op_on_uninitialized_split_context_state: bool, - /// Accounts needed if `close_split_context_state_on_execution` flag is - /// enabled. - pub close_split_context_state_accounts: Option>, -} - -/// Accounts needed if `close_split_context_state_on_execution` flag is enabled -/// on a transfer. -#[derive(Clone, Copy)] -pub struct CloseSplitContextStateAccounts<'a> { - /// The lamport destination account. - pub lamport_destination: &'a Pubkey, - /// The ZK Token proof program. - pub zk_token_proof_program: &'a Pubkey, + /// Relative location of the + /// `ProofInstruction::VerifyCiphertextCommitmentEquality` instruction + /// to the `TransferWithFee` instruction in the transaction. If the offset + /// is `0`, then use a context state account for the proof. + pub equality_proof_instruction_offset: i8, + /// Relative location of the + /// `ProofInstruction::VerifyBatchedGroupedCiphertext3HandlesValidity` + /// instruction to the `TransferWithFee` instruction in the transaction. + /// If the offset is `0`, then use a context state account for the + /// proof. + pub transfer_amount_ciphertext_validity_proof_instruction_offset: i8, + /// Relative location of the `ProofInstruction::VerifyPercentageWithFee` + /// instruction to the `TransferWithFee` instruction in the transaction. + /// If the offset is `0`, then use a context state account for the + /// proof. + pub fee_sigma_proof_instruction_offset: i8, + /// Relative location of the + /// `ProofInstruction::VerifyBatchedGroupedCiphertext2HandlesValidity` + /// instruction to the `TransferWithFee` instruction in the transaction. + /// If the offset is `0`, then use a context state account for the + /// proof. + pub fee_ciphertext_validity_proof_instruction_offset: i8, + /// Relative location of the `ProofInstruction::BatchedRangeProofU256Data` + /// instruction to the `TransferWithFee` instruction in the transaction. + /// If the offset is `0`, then use a context state account for the + /// proof. + pub range_proof_instruction_offset: i8, } /// Create a `InitializeMint` instruction @@ -646,7 +653,7 @@ pub fn initialize_mint( mint: &Pubkey, authority: Option, auto_approve_new_accounts: bool, - auditor_elgamal_pubkey: Option, + auditor_elgamal_pubkey: Option, ) -> Result { check_program_account(token_program_id)?; let accounts = vec![AccountMeta::new(*mint, false)]; @@ -671,7 +678,7 @@ pub fn update_mint( authority: &Pubkey, multisig_signers: &[&Pubkey], auto_approve_new_accounts: bool, - auditor_elgamal_pubkey: Option, + auditor_elgamal_pubkey: Option, ) -> Result { check_program_account(token_program_id)?; let mut accounts = vec![ @@ -701,11 +708,11 @@ pub fn inner_configure_account( token_program_id: &Pubkey, token_account: &Pubkey, mint: &Pubkey, - decryptable_zero_balance: AeCiphertext, + decryptable_zero_balance: PodAeCiphertext, maximum_pending_balance_credit_counter: u64, authority: &Pubkey, multisig_signers: &[&Pubkey], - proof_data_location: ProofLocation, + proof_data_location: ProofLocation, ) -> Result { check_program_account(token_program_id)?; @@ -715,8 +722,11 @@ pub fn inner_configure_account( ]; let proof_instruction_offset = match proof_data_location { - ProofLocation::InstructionOffset(proof_instruction_offset, _) => { + ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) => { accounts.push(AccountMeta::new_readonly(sysvar::instructions::id(), false)); + if let ProofData::RecordAccount(record_address, _) = proof_data { + accounts.push(AccountMeta::new_readonly(*record_address, false)); + } proof_instruction_offset.into() } ProofLocation::ContextStateAccount(context_state_account) => { @@ -740,7 +750,7 @@ pub fn inner_configure_account( TokenInstruction::ConfidentialTransferExtension, ConfidentialTransferInstruction::ConfigureAccount, &ConfigureAccountInstructionData { - decryptable_zero_balance: decryptable_zero_balance.into(), + decryptable_zero_balance, maximum_pending_balance_credit_counter: maximum_pending_balance_credit_counter.into(), proof_instruction_offset, }, @@ -753,11 +763,11 @@ pub fn configure_account( token_program_id: &Pubkey, token_account: &Pubkey, mint: &Pubkey, - decryptable_zero_balance: AeCiphertext, + decryptable_zero_balance: PodAeCiphertext, maximum_pending_balance_credit_counter: u64, authority: &Pubkey, multisig_signers: &[&Pubkey], - proof_data_location: ProofLocation, + proof_data_location: ProofLocation, ) -> Result, ProgramError> { let mut instructions = vec![inner_configure_account( token_program_id, @@ -781,8 +791,15 @@ pub fn configure_account( if proof_instruction_offset != 1 { return Err(TokenError::InvalidProofInstructionOffset.into()); } - instructions.push(verify_pubkey_validity(None, proof_data)); - }; + match proof_data { + ProofData::InstructionData(data) => instructions + .push(ProofInstruction::VerifyPubkeyValidity.encode_verify_proof(None, data)), + ProofData::RecordAccount(address, offset) => instructions.push( + ProofInstruction::VerifyPubkeyValidity + .encode_verify_proof_from_account(None, address, offset), + ), + }; + } Ok(instructions) } @@ -821,14 +838,17 @@ pub fn inner_empty_account( token_account: &Pubkey, authority: &Pubkey, multisig_signers: &[&Pubkey], - proof_data_location: ProofLocation, + proof_data_location: ProofLocation, ) -> Result { check_program_account(token_program_id)?; let mut accounts = vec![AccountMeta::new(*token_account, false)]; let proof_instruction_offset = match proof_data_location { - ProofLocation::InstructionOffset(proof_instruction_offset, _) => { + ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) => { accounts.push(AccountMeta::new_readonly(sysvar::instructions::id(), false)); + if let ProofData::RecordAccount(record_address, _) = proof_data { + accounts.push(AccountMeta::new_readonly(*record_address, false)); + } proof_instruction_offset.into() } ProofLocation::ContextStateAccount(context_state_account) => { @@ -863,7 +883,7 @@ pub fn empty_account( token_account: &Pubkey, authority: &Pubkey, multisig_signers: &[&Pubkey], - proof_data_location: ProofLocation, + proof_data_location: ProofLocation, ) -> Result, ProgramError> { let mut instructions = vec![inner_empty_account( token_program_id, @@ -884,7 +904,14 @@ pub fn empty_account( if proof_instruction_offset != 1 { return Err(TokenError::InvalidProofInstructionOffset.into()); } - instructions.push(verify_zero_balance(None, proof_data)); + match proof_data { + ProofData::InstructionData(data) => instructions + .push(ProofInstruction::VerifyZeroCiphertext.encode_verify_proof(None, data)), + ProofData::RecordAccount(address, offset) => instructions.push( + ProofInstruction::VerifyZeroCiphertext + .encode_verify_proof_from_account(None, address, offset), + ), + }; }; Ok(instructions) @@ -937,7 +964,8 @@ pub fn inner_withdraw( new_decryptable_available_balance: DecryptableBalance, authority: &Pubkey, multisig_signers: &[&Pubkey], - proof_data_location: ProofLocation, + equality_proof_data_location: ProofLocation, + range_proof_data_location: ProofLocation, ) -> Result { check_program_account(token_program_id)?; let mut accounts = vec![ @@ -945,9 +973,32 @@ pub fn inner_withdraw( AccountMeta::new_readonly(*mint, false), ]; - let proof_instruction_offset = match proof_data_location { - ProofLocation::InstructionOffset(proof_instruction_offset, _) => { - accounts.push(AccountMeta::new_readonly(sysvar::instructions::id(), false)); + // if at least one of the proof locations is an instruction offset, sysvar + // account is needed + if equality_proof_data_location.is_instruction_offset() + || range_proof_data_location.is_instruction_offset() + { + accounts.push(AccountMeta::new_readonly(sysvar::instructions::id(), false)); + } + + let equality_proof_instruction_offset = match equality_proof_data_location { + ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) => { + if let ProofData::RecordAccount(record_address, _) = proof_data { + accounts.push(AccountMeta::new_readonly(*record_address, false)); + } + proof_instruction_offset.into() + } + ProofLocation::ContextStateAccount(context_state_account) => { + accounts.push(AccountMeta::new_readonly(*context_state_account, false)); + 0 + } + }; + + let range_proof_instruction_offset = match range_proof_data_location { + ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) => { + if let ProofData::RecordAccount(record_address, _) = proof_data { + accounts.push(AccountMeta::new_readonly(*record_address, false)); + } proof_instruction_offset.into() } ProofLocation::ContextStateAccount(context_state_account) => { @@ -974,7 +1025,8 @@ pub fn inner_withdraw( amount: amount.into(), decimals, new_decryptable_available_balance, - proof_instruction_offset, + equality_proof_instruction_offset, + range_proof_instruction_offset, }, )) } @@ -987,10 +1039,11 @@ pub fn withdraw( mint: &Pubkey, amount: u64, decimals: u8, - new_decryptable_available_balance: AeCiphertext, + new_decryptable_available_balance: PodAeCiphertext, authority: &Pubkey, multisig_signers: &[&Pubkey], - proof_data_location: ProofLocation, + equality_proof_data_location: ProofLocation, + range_proof_data_location: ProofLocation, ) -> Result, ProgramError> { let mut instructions = vec![inner_withdraw( token_program_id, @@ -998,30 +1051,57 @@ pub fn withdraw( mint, amount, decimals, - new_decryptable_available_balance.into(), + new_decryptable_available_balance, authority, multisig_signers, - proof_data_location, + equality_proof_data_location, + range_proof_data_location, )?]; + let mut expected_instruction_offset = 1; + if let ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) = - proof_data_location + equality_proof_data_location { - // This constructor appends the proof instruction right after the `Withdraw` - // instruction. This means that the proof instruction offset must be - // always be 1. To use an arbitrary proof instruction offset, use the - // `inner_withdraw` constructor. let proof_instruction_offset: i8 = proof_instruction_offset.into(); - if proof_instruction_offset != 1 { + if proof_instruction_offset != expected_instruction_offset { + return Err(TokenError::InvalidProofInstructionOffset.into()); + } + match proof_data { + ProofData::InstructionData(data) => instructions.push( + ProofInstruction::VerifyCiphertextCommitmentEquality + .encode_verify_proof(None, data), + ), + ProofData::RecordAccount(address, offset) => instructions.push( + ProofInstruction::VerifyCiphertextCommitmentEquality + .encode_verify_proof_from_account(None, address, offset), + ), + }; + + expected_instruction_offset += 1; + }; + + if let ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) = + range_proof_data_location + { + let proof_instruction_offset: i8 = proof_instruction_offset.into(); + if proof_instruction_offset != expected_instruction_offset { return Err(TokenError::InvalidProofInstructionOffset.into()); } - instructions.push(verify_withdraw(None, proof_data)); + match proof_data { + ProofData::InstructionData(data) => instructions + .push(ProofInstruction::VerifyBatchedRangeProofU64.encode_verify_proof(None, data)), + ProofData::RecordAccount(address, offset) => instructions.push( + ProofInstruction::VerifyBatchedRangeProofU64 + .encode_verify_proof_from_account(None, address, offset), + ), + }; }; Ok(instructions) } -/// Create a inner `Transfer` instruction +/// Create an inner `Transfer` instruction /// /// This instruction is suitable for use with a cross-program `invoke` #[allow(clippy::too_many_arguments)] @@ -1033,7 +1113,11 @@ pub fn inner_transfer( new_source_decryptable_available_balance: DecryptableBalance, authority: &Pubkey, multisig_signers: &[&Pubkey], - proof_data_location: ProofLocation, + equality_proof_data_location: ProofLocation, + ciphertext_validity_proof_data_location: ProofLocation< + BatchedGroupedCiphertext3HandlesValidityProofData, + >, + range_proof_data_location: ProofLocation, ) -> Result { check_program_account(token_program_id)?; let mut accounts = vec![ @@ -1042,9 +1126,47 @@ pub fn inner_transfer( AccountMeta::new(*destination_token_account, false), ]; - let proof_instruction_offset = match proof_data_location { - ProofLocation::InstructionOffset(proof_instruction_offset, _) => { - accounts.push(AccountMeta::new_readonly(sysvar::instructions::id(), false)); + // if at least one of the proof locations is an instruction offset, sysvar + // account is needed + if equality_proof_data_location.is_instruction_offset() + || ciphertext_validity_proof_data_location.is_instruction_offset() + || range_proof_data_location.is_instruction_offset() + { + accounts.push(AccountMeta::new_readonly(sysvar::instructions::id(), false)); + } + + let equality_proof_instruction_offset = match equality_proof_data_location { + ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) => { + if let ProofData::RecordAccount(record_address, _) = proof_data { + accounts.push(AccountMeta::new_readonly(*record_address, false)); + } + proof_instruction_offset.into() + } + ProofLocation::ContextStateAccount(context_state_account) => { + accounts.push(AccountMeta::new_readonly(*context_state_account, false)); + 0 + } + }; + + let ciphertext_validity_proof_instruction_offset = match ciphertext_validity_proof_data_location + { + ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) => { + if let ProofData::RecordAccount(record_address, _) = proof_data { + accounts.push(AccountMeta::new_readonly(*record_address, false)); + } + proof_instruction_offset.into() + } + ProofLocation::ContextStateAccount(context_state_account) => { + accounts.push(AccountMeta::new_readonly(*context_state_account, false)); + 0 + } + }; + + let range_proof_instruction_offset = match range_proof_data_location { + ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) => { + if let ProofData::RecordAccount(record_address, _) = proof_data { + accounts.push(AccountMeta::new_readonly(*record_address, false)); + } proof_instruction_offset.into() } ProofLocation::ContextStateAccount(context_state_account) => { @@ -1069,140 +1191,103 @@ pub fn inner_transfer( ConfidentialTransferInstruction::Transfer, &TransferInstructionData { new_source_decryptable_available_balance, - proof_instruction_offset, + equality_proof_instruction_offset, + ciphertext_validity_proof_instruction_offset, + range_proof_instruction_offset, }, )) } -/// Create a `Transfer` instruction with regular (no-fee) proof +/// Create a `Transfer` instruction #[allow(clippy::too_many_arguments)] pub fn transfer( token_program_id: &Pubkey, source_token_account: &Pubkey, mint: &Pubkey, destination_token_account: &Pubkey, - new_source_decryptable_available_balance: AeCiphertext, + new_source_decryptable_available_balance: DecryptableBalance, authority: &Pubkey, multisig_signers: &[&Pubkey], - proof_data_location: ProofLocation, + equality_proof_data_location: ProofLocation, + ciphertext_validity_proof_data_location: ProofLocation< + BatchedGroupedCiphertext3HandlesValidityProofData, + >, + range_proof_data_location: ProofLocation, ) -> Result, ProgramError> { let mut instructions = vec![inner_transfer( token_program_id, source_token_account, mint, destination_token_account, - new_source_decryptable_available_balance.into(), + new_source_decryptable_available_balance, authority, multisig_signers, - proof_data_location, + equality_proof_data_location, + ciphertext_validity_proof_data_location, + range_proof_data_location, )?]; + let mut expected_instruction_offset = 1; + if let ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) = - proof_data_location + equality_proof_data_location { - // This constructor appends the proof instruction right after the `Transfer` - // instruction. This means that the proof instruction offset must be - // always be 1. To use an arbitrary proof instruction offset, use the - // `inner_transfer` constructor. let proof_instruction_offset: i8 = proof_instruction_offset.into(); - if proof_instruction_offset != 1 { + if proof_instruction_offset != expected_instruction_offset { return Err(TokenError::InvalidProofInstructionOffset.into()); } - instructions.push(verify_transfer(None, proof_data)); - }; - - Ok(instructions) -} + match proof_data { + ProofData::InstructionData(data) => instructions.push( + ProofInstruction::VerifyCiphertextCommitmentEquality + .encode_verify_proof(None, data), + ), + ProofData::RecordAccount(address, offset) => instructions.push( + ProofInstruction::VerifyCiphertextCommitmentEquality + .encode_verify_proof_from_account(None, address, offset), + ), + }; -/// Create a inner `Transfer` instruction with fee -/// -/// This instruction is suitable for use with a cross-program `invoke` -#[allow(clippy::too_many_arguments)] -pub fn inner_transfer_with_fee( - token_program_id: &Pubkey, - source_token_account: &Pubkey, - mint: &Pubkey, - destination_token_account: &Pubkey, - new_source_decryptable_available_balance: DecryptableBalance, - authority: &Pubkey, - multisig_signers: &[&Pubkey], - proof_data_location: ProofLocation, -) -> Result { - check_program_account(token_program_id)?; - let mut accounts = vec![ - AccountMeta::new(*source_token_account, false), - AccountMeta::new_readonly(*mint, false), - AccountMeta::new(*destination_token_account, false), - ]; + expected_instruction_offset += 1; + } - let proof_instruction_offset = match proof_data_location { - ProofLocation::InstructionOffset(proof_instruction_offset, _) => { - accounts.push(AccountMeta::new_readonly(sysvar::instructions::id(), false)); - proof_instruction_offset.into() - } - ProofLocation::ContextStateAccount(context_state_account) => { - accounts.push(AccountMeta::new_readonly(*context_state_account, false)); - 0 + if let ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) = + ciphertext_validity_proof_data_location + { + let proof_instruction_offset: i8 = proof_instruction_offset.into(); + if proof_instruction_offset != expected_instruction_offset { + return Err(TokenError::InvalidProofInstructionOffset.into()); } - }; - - accounts.push(AccountMeta::new_readonly( - *authority, - multisig_signers.is_empty(), - )); + match proof_data { + ProofData::InstructionData(data) => instructions.push( + ProofInstruction::VerifyBatchedGroupedCiphertext3HandlesValidity + .encode_verify_proof(None, data), + ), + ProofData::RecordAccount(address, offset) => instructions.push( + ProofInstruction::VerifyBatchedGroupedCiphertext3HandlesValidity + .encode_verify_proof_from_account(None, address, offset), + ), + }; - for multisig_signer in multisig_signers.iter() { - accounts.push(AccountMeta::new_readonly(**multisig_signer, true)); + expected_instruction_offset += 1; } - Ok(encode_instruction( - token_program_id, - accounts, - TokenInstruction::ConfidentialTransferExtension, - ConfidentialTransferInstruction::Transfer, - &TransferInstructionData { - new_source_decryptable_available_balance, - proof_instruction_offset, - }, - )) -} - -/// Create a `Transfer` instruction with fee proof -#[allow(clippy::too_many_arguments)] -pub fn transfer_with_fee( - token_program_id: &Pubkey, - source_token_account: &Pubkey, - mint: &Pubkey, - destination_token_account: &Pubkey, - new_source_decryptable_available_balance: AeCiphertext, - authority: &Pubkey, - multisig_signers: &[&Pubkey], - proof_data_location: ProofLocation, -) -> Result, ProgramError> { - let mut instructions = vec![inner_transfer_with_fee( - token_program_id, - source_token_account, - destination_token_account, - mint, - new_source_decryptable_available_balance.into(), - authority, - multisig_signers, - proof_data_location, - )?]; - if let ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) = - proof_data_location + range_proof_data_location { - // This constructor appends the proof instruction right after the - // `TransferWithFee` instruction. This means that the proof instruction - // offset must be always be 1. To use an arbitrary proof instruction - // offset, use the `inner_transfer_with_fee` constructor. let proof_instruction_offset: i8 = proof_instruction_offset.into(); - if proof_instruction_offset != 1 { + if proof_instruction_offset != expected_instruction_offset { return Err(TokenError::InvalidProofInstructionOffset.into()); } - instructions.push(verify_transfer_with_fee(None, proof_data)); - }; + match proof_data { + ProofData::InstructionData(data) => instructions.push( + ProofInstruction::VerifyBatchedRangeProofU128.encode_verify_proof(None, data), + ), + ProofData::RecordAccount(address, offset) => instructions.push( + ProofInstruction::VerifyBatchedRangeProofU128 + .encode_verify_proof_from_account(None, address, offset), + ), + }; + } Ok(instructions) } @@ -1245,7 +1330,7 @@ pub fn apply_pending_balance( token_program_id: &Pubkey, token_account: &Pubkey, pending_balance_instructions: u64, - new_decryptable_available_balance: AeCiphertext, + new_decryptable_available_balance: PodAeCiphertext, authority: &Pubkey, multisig_signers: &[&Pubkey], ) -> Result { @@ -1253,7 +1338,7 @@ pub fn apply_pending_balance( token_program_id, token_account, pending_balance_instructions, - new_decryptable_available_balance.into(), + new_decryptable_available_balance, authority, multisig_signers, ) // calls check_program_account @@ -1349,17 +1434,27 @@ pub fn disable_non_confidential_credits( ) } -/// Create a `TransferWithSplitProof` instruction without fee +/// Create an inner `TransferWithFee` instruction +/// +/// This instruction is suitable for use with a cross-program `invoke` #[allow(clippy::too_many_arguments)] -pub fn transfer_with_split_proofs( +pub fn inner_transfer_with_fee( token_program_id: &Pubkey, source_token_account: &Pubkey, mint: &Pubkey, destination_token_account: &Pubkey, new_source_decryptable_available_balance: DecryptableBalance, - source_account_authority: &Pubkey, - context_accounts: TransferSplitContextStateAccounts, - source_decrypt_handles: &SourceDecryptHandles, + authority: &Pubkey, + multisig_signers: &[&Pubkey], + equality_proof_data_location: ProofLocation, + transfer_amount_ciphertext_validity_proof_data_location: ProofLocation< + BatchedGroupedCiphertext3HandlesValidityProofData, + >, + fee_sigma_proof_data_location: ProofLocation, + fee_ciphertext_validity_proof_data_location: ProofLocation< + BatchedGroupedCiphertext2HandlesValidityProofData, + >, + range_proof_data_location: ProofLocation, ) -> Result { check_program_account(token_program_id)?; let mut accounts = vec![ @@ -1368,151 +1463,241 @@ pub fn transfer_with_split_proofs( AccountMeta::new(*destination_token_account, false), ]; - let close_split_context_state_on_execution = - if let Some(close_split_context_state_on_execution_accounts) = - context_accounts.close_split_context_state_accounts - { - // If `close_split_context_state_accounts` is set, then all context state - // accounts must be `writable`. - accounts.push(AccountMeta::new(*context_accounts.equality_proof, false)); - accounts.push(AccountMeta::new( - *context_accounts.ciphertext_validity_proof, - false, - )); - accounts.push(AccountMeta::new(*context_accounts.range_proof, false)); - accounts.push(AccountMeta::new( - *close_split_context_state_on_execution_accounts.lamport_destination, - false, - )); - accounts.push(AccountMeta::new_readonly(*context_accounts.authority, true)); - accounts.push(AccountMeta::new_readonly( - *close_split_context_state_on_execution_accounts.zk_token_proof_program, - false, - )); - accounts.push(AccountMeta::new_readonly(*source_account_authority, true)); - true - } else { - // If `close_split_context_state_accounts` is not set, then context state - // accounts can be read-only. - accounts.push(AccountMeta::new_readonly( - *context_accounts.equality_proof, - false, - )); - accounts.push(AccountMeta::new_readonly( - *context_accounts.ciphertext_validity_proof, - false, - )); - accounts.push(AccountMeta::new_readonly( - *context_accounts.range_proof, - false, - )); - accounts.push(AccountMeta::new_readonly(*source_account_authority, true)); - - false + // if at least one of the proof locations is an instruction offset, sysvar + // account is needed + if equality_proof_data_location.is_instruction_offset() + || transfer_amount_ciphertext_validity_proof_data_location.is_instruction_offset() + || fee_sigma_proof_data_location.is_instruction_offset() + || fee_ciphertext_validity_proof_data_location.is_instruction_offset() + || range_proof_data_location.is_instruction_offset() + { + accounts.push(AccountMeta::new_readonly(sysvar::instructions::id(), false)); + } + + let equality_proof_instruction_offset = match equality_proof_data_location { + ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) => { + if let ProofData::RecordAccount(record_address, _) = proof_data { + accounts.push(AccountMeta::new_readonly(*record_address, false)); + } + proof_instruction_offset.into() + } + ProofLocation::ContextStateAccount(context_state_account) => { + accounts.push(AccountMeta::new_readonly(*context_state_account, false)); + 0 + } + }; + + let transfer_amount_ciphertext_validity_proof_instruction_offset = + match transfer_amount_ciphertext_validity_proof_data_location { + ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) => { + if let ProofData::RecordAccount(record_address, _) = proof_data { + accounts.push(AccountMeta::new_readonly(*record_address, false)); + } + proof_instruction_offset.into() + } + ProofLocation::ContextStateAccount(context_state_account) => { + accounts.push(AccountMeta::new_readonly(*context_state_account, false)); + 0 + } }; + let fee_sigma_proof_instruction_offset = match fee_sigma_proof_data_location { + ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) => { + if let ProofData::RecordAccount(record_address, _) = proof_data { + accounts.push(AccountMeta::new_readonly(*record_address, false)); + } + proof_instruction_offset.into() + } + ProofLocation::ContextStateAccount(context_state_account) => { + accounts.push(AccountMeta::new_readonly(*context_state_account, false)); + 0 + } + }; + + let fee_ciphertext_validity_proof_instruction_offset = + match fee_ciphertext_validity_proof_data_location { + ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) => { + if let ProofData::RecordAccount(record_address, _) = proof_data { + accounts.push(AccountMeta::new_readonly(*record_address, false)); + } + proof_instruction_offset.into() + } + ProofLocation::ContextStateAccount(context_state_account) => { + accounts.push(AccountMeta::new_readonly(*context_state_account, false)); + 0 + } + }; + + let range_proof_instruction_offset = match range_proof_data_location { + ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) => { + if let ProofData::RecordAccount(record_address, _) = proof_data { + accounts.push(AccountMeta::new_readonly(*record_address, false)); + } + proof_instruction_offset.into() + } + ProofLocation::ContextStateAccount(context_state_account) => { + accounts.push(AccountMeta::new_readonly(*context_state_account, false)); + 0 + } + }; + + accounts.push(AccountMeta::new_readonly( + *authority, + multisig_signers.is_empty(), + )); + + for multisig_signer in multisig_signers.iter() { + accounts.push(AccountMeta::new_readonly(**multisig_signer, true)); + } + Ok(encode_instruction( token_program_id, accounts, TokenInstruction::ConfidentialTransferExtension, - ConfidentialTransferInstruction::TransferWithSplitProofs, - &TransferWithSplitProofsInstructionData { + ConfidentialTransferInstruction::TransferWithFee, + &TransferWithFeeInstructionData { new_source_decryptable_available_balance, - no_op_on_uninitialized_split_context_state: context_accounts - .no_op_on_uninitialized_split_context_state - .into(), - close_split_context_state_on_execution: close_split_context_state_on_execution.into(), - source_decrypt_handles: *source_decrypt_handles, + equality_proof_instruction_offset, + transfer_amount_ciphertext_validity_proof_instruction_offset, + fee_sigma_proof_instruction_offset, + fee_ciphertext_validity_proof_instruction_offset, + range_proof_instruction_offset, }, )) } -/// Create a `TransferWithSplitProof` instruction with fee +/// Create a `TransferWithFee` instruction #[allow(clippy::too_many_arguments)] -pub fn transfer_with_fee_and_split_proofs( +pub fn transfer_with_fee( token_program_id: &Pubkey, source_token_account: &Pubkey, mint: &Pubkey, destination_token_account: &Pubkey, new_source_decryptable_available_balance: DecryptableBalance, - source_account_authority: &Pubkey, - context_accounts: TransferWithFeeSplitContextStateAccounts, - source_decrypt_handles: &SourceDecryptHandles, -) -> Result { - check_program_account(token_program_id)?; - let mut accounts = vec![ - AccountMeta::new(*source_token_account, false), - AccountMeta::new_readonly(*mint, false), - AccountMeta::new(*destination_token_account, false), - ]; + authority: &Pubkey, + multisig_signers: &[&Pubkey], + equality_proof_data_location: ProofLocation, + transfer_amount_ciphertext_validity_proof_data_location: ProofLocation< + BatchedGroupedCiphertext3HandlesValidityProofData, + >, + fee_sigma_proof_data_location: ProofLocation, + fee_ciphertext_validity_proof_data_location: ProofLocation< + BatchedGroupedCiphertext2HandlesValidityProofData, + >, + range_proof_data_location: ProofLocation, +) -> Result, ProgramError> { + let mut instructions = vec![inner_transfer_with_fee( + token_program_id, + source_token_account, + mint, + destination_token_account, + new_source_decryptable_available_balance, + authority, + multisig_signers, + equality_proof_data_location, + transfer_amount_ciphertext_validity_proof_data_location, + fee_sigma_proof_data_location, + fee_ciphertext_validity_proof_data_location, + range_proof_data_location, + )?]; - let close_split_context_state_on_execution = - if let Some(close_split_context_state_on_execution_accounts) = - context_accounts.close_split_context_state_accounts - { - // If `close_split_context_state_accounts` is set, then all context state - // accounts must be `writable`. - accounts.push(AccountMeta::new(*context_accounts.equality_proof, false)); - accounts.push(AccountMeta::new( - *context_accounts.transfer_amount_ciphertext_validity_proof, - false, - )); - accounts.push(AccountMeta::new(*context_accounts.fee_sigma_proof, false)); - accounts.push(AccountMeta::new( - *context_accounts.fee_ciphertext_validity_proof, - false, - )); - accounts.push(AccountMeta::new(*context_accounts.range_proof, false)); - accounts.push(AccountMeta::new( - *close_split_context_state_on_execution_accounts.lamport_destination, - false, - )); - accounts.push(AccountMeta::new_readonly(*context_accounts.authority, true)); - accounts.push(AccountMeta::new_readonly( - *close_split_context_state_on_execution_accounts.zk_token_proof_program, - false, - )); - accounts.push(AccountMeta::new_readonly(*source_account_authority, true)); - true - } else { - // If `close_split_context_state_accounts` is not set, then context state - // accounts can be read-only. - accounts.push(AccountMeta::new_readonly( - *context_accounts.equality_proof, - false, - )); - accounts.push(AccountMeta::new_readonly( - *context_accounts.transfer_amount_ciphertext_validity_proof, - false, - )); - accounts.push(AccountMeta::new_readonly( - *context_accounts.fee_sigma_proof, - false, - )); - accounts.push(AccountMeta::new_readonly( - *context_accounts.fee_ciphertext_validity_proof, - false, - )); - accounts.push(AccountMeta::new_readonly( - *context_accounts.range_proof, - false, - )); - accounts.push(AccountMeta::new_readonly(*source_account_authority, true)); - false + let mut expected_instruction_offset = 1; + + if let ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) = + equality_proof_data_location + { + let proof_instruction_offset: i8 = proof_instruction_offset.into(); + if proof_instruction_offset != expected_instruction_offset { + return Err(TokenError::InvalidProofInstructionOffset.into()); + } + match proof_data { + ProofData::InstructionData(data) => instructions.push( + ProofInstruction::VerifyCiphertextCommitmentEquality + .encode_verify_proof(None, data), + ), + ProofData::RecordAccount(address, offset) => instructions.push( + ProofInstruction::VerifyCiphertextCommitmentEquality + .encode_verify_proof_from_account(None, address, offset), + ), }; + expected_instruction_offset += 1; + } - Ok(encode_instruction( - token_program_id, - accounts, - TokenInstruction::ConfidentialTransferExtension, - ConfidentialTransferInstruction::TransferWithSplitProofs, - &TransferWithSplitProofsInstructionData { - new_source_decryptable_available_balance, - no_op_on_uninitialized_split_context_state: context_accounts - .no_op_on_uninitialized_split_context_state - .into(), - close_split_context_state_on_execution: close_split_context_state_on_execution.into(), - source_decrypt_handles: *source_decrypt_handles, - }, - )) + if let ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) = + transfer_amount_ciphertext_validity_proof_data_location + { + let proof_instruction_offset: i8 = proof_instruction_offset.into(); + if proof_instruction_offset != expected_instruction_offset { + return Err(TokenError::InvalidProofInstructionOffset.into()); + } + match proof_data { + ProofData::InstructionData(data) => instructions.push( + ProofInstruction::VerifyBatchedGroupedCiphertext3HandlesValidity + .encode_verify_proof(None, data), + ), + ProofData::RecordAccount(address, offset) => instructions.push( + ProofInstruction::VerifyBatchedGroupedCiphertext3HandlesValidity + .encode_verify_proof_from_account(None, address, offset), + ), + }; + expected_instruction_offset += 1; + } + + if let ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) = + fee_sigma_proof_data_location + { + let proof_instruction_offset: i8 = proof_instruction_offset.into(); + if proof_instruction_offset != expected_instruction_offset { + return Err(TokenError::InvalidProofInstructionOffset.into()); + } + match proof_data { + ProofData::InstructionData(data) => instructions + .push(ProofInstruction::VerifyPercentageWithCap.encode_verify_proof(None, data)), + ProofData::RecordAccount(address, offset) => instructions.push( + ProofInstruction::VerifyPercentageWithCap + .encode_verify_proof_from_account(None, address, offset), + ), + }; + expected_instruction_offset += 1; + } + + if let ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) = + fee_ciphertext_validity_proof_data_location + { + let proof_instruction_offset: i8 = proof_instruction_offset.into(); + if proof_instruction_offset != expected_instruction_offset { + return Err(TokenError::InvalidProofInstructionOffset.into()); + } + match proof_data { + ProofData::InstructionData(data) => instructions.push( + ProofInstruction::VerifyBatchedGroupedCiphertext2HandlesValidity + .encode_verify_proof(None, data), + ), + ProofData::RecordAccount(address, offset) => instructions.push( + ProofInstruction::VerifyBatchedGroupedCiphertext2HandlesValidity + .encode_verify_proof_from_account(None, address, offset), + ), + }; + expected_instruction_offset += 1; + } + + if let ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) = + range_proof_data_location + { + let proof_instruction_offset: i8 = proof_instruction_offset.into(); + if proof_instruction_offset != expected_instruction_offset { + return Err(TokenError::InvalidProofInstructionOffset.into()); + } + match proof_data { + ProofData::InstructionData(data) => instructions.push( + ProofInstruction::VerifyBatchedRangeProofU256.encode_verify_proof(None, data), + ), + ProofData::RecordAccount(address, offset) => instructions.push( + ProofInstruction::VerifyBatchedRangeProofU256 + .encode_verify_proof_from_account(None, address, offset), + ), + }; + } + + Ok(instructions) } diff --git a/token/program-2022/src/extension/confidential_transfer/mod.rs b/token/program-2022/src/extension/confidential_transfer/mod.rs index c2f177d1acf..f779db36938 100644 --- a/token/program-2022/src/extension/confidential_transfer/mod.rs +++ b/token/program-2022/src/extension/confidential_transfer/mod.rs @@ -5,9 +5,11 @@ use { }, bytemuck::{Pod, Zeroable}, solana_program::entrypoint::ProgramResult, - solana_zk_token_sdk::zk_token_elgamal::pod::{AeCiphertext, ElGamalCiphertext, ElGamalPubkey}, + solana_zk_sdk::encryption::pod::{ + auth_encryption::PodAeCiphertext, + elgamal::{PodElGamalCiphertext, PodElGamalPubkey}, + }, spl_pod::{ - bytemuck::pod_from_bytes, optional_keys::{OptionalNonZeroElGamalPubkey, OptionalNonZeroPubkey}, primitives::{PodBool, PodU64}, }, @@ -31,27 +33,14 @@ pub mod processor; /// Transfer Extension pub mod verify_proof; -/// Helper functions to generate split zero-knowledge proofs for confidential -/// transfers in the Confidential Transfer Extension. -/// -/// The logic in this submodule should belong to the `solana-zk-token-sdk` and -/// will be removed with the next upgrade to the Solana program. -#[cfg(not(target_os = "solana"))] -pub mod split_proof_generation; - /// Confidential Transfer Extension account information needed for instructions #[cfg(not(target_os = "solana"))] pub mod account_info; -/// Ciphertext extraction and proof related helper logic -/// -/// This submodule should be removed with the next upgrade to the Solana program -pub mod ciphertext_extraction; - /// ElGamal ciphertext containing an account balance -pub type EncryptedBalance = ElGamalCiphertext; +pub type EncryptedBalance = PodElGamalCiphertext; /// Authenticated encryption containing an account balance -pub type DecryptableBalance = AeCiphertext; +pub type DecryptableBalance = PodAeCiphertext; /// Confidential transfer mint configuration #[repr(C)] @@ -90,7 +79,7 @@ pub struct ConfidentialTransferAccount { pub approved: PodBool, /// The public key associated with ElGamal encryption - pub elgamal_pubkey: ElGamalPubkey, + pub elgamal_pubkey: PodElGamalPubkey, /// The low 16 bits of the pending balance (encrypted by `elgamal_pubkey`) pub pending_balance_lo: EncryptedBalance, diff --git a/token/program-2022/src/extension/confidential_transfer/processor.rs b/token/program-2022/src/extension/confidential_transfer/processor.rs index e73ed6bfec4..49f6ba2d8f7 100644 --- a/token/program-2022/src/extension/confidential_transfer/processor.rs +++ b/token/program-2022/src/extension/confidential_transfer/processor.rs @@ -2,14 +2,14 @@ #[cfg(feature = "zk-ops")] use { crate::extension::non_transferable::NonTransferableAccount, - solana_zk_token_sdk::zk_token_elgamal::ops as syscall, + spl_token_confidential_transfer_ciphertext_arithmetic as ciphertext_arithmetic, }; use { crate::{ check_program_account, error::TokenError, extension::{ - confidential_transfer::{ciphertext_extraction::*, instruction::*, verify_proof::*, *}, + confidential_transfer::{instruction::*, verify_proof::*, *}, confidential_transfer_fee::{ ConfidentialTransferFeeAmount, ConfidentialTransferFeeConfig, EncryptedWithheldAmount, @@ -22,6 +22,7 @@ use { instruction::{decode_instruction_data, decode_instruction_type}, pod::{PodAccount, PodMint}, processor::Processor, + proof::verify_and_extract_context, }, solana_program::{ account_info::{next_account_info, AccountInfo}, @@ -32,6 +33,9 @@ use { pubkey::Pubkey, sysvar::Sysvar, }, + spl_token_confidential_transfer_proof_extraction::{ + transfer::TransferProofContext, transfer_with_fee::TransferWithFeeProofContext, + }, }; /// Processes an [InitializeMint] instruction. @@ -101,8 +105,10 @@ fn process_configure_account( let mint_info = next_account_info(account_info_iter)?; // zero-knowledge proof certifies that the supplied ElGamal public key is valid - let proof_context = - verify_configure_account_proof(account_info_iter, proof_instruction_offset)?; + let proof_context = verify_and_extract_context::< + PubkeyValidityProofData, + PubkeyValidityProofContext, + >(account_info_iter, proof_instruction_offset, None)?; let authority_info = next_account_info(account_info_iter)?; let authority_info_data_len = authority_info.data_len(); @@ -206,7 +212,10 @@ fn process_empty_account( // zero-knowledge proof certifies that the available balance ciphertext holds // the balance of 0. - let proof_context = verify_empty_account_proof(account_info_iter, proof_instruction_offset)?; + let proof_context = verify_and_extract_context::< + ZeroCiphertextProofData, + ZeroCiphertextProofContext, + >(account_info_iter, proof_instruction_offset, None)?; let authority_info = next_account_info(account_info_iter)?; let authority_info_data_len = authority_info.data_len(); @@ -311,14 +320,18 @@ fn process_deposit( // Prevent unnecessary ciphertext arithmetic syscalls if `amount_lo` or // `amount_hi` is zero if amount_lo > 0 { - confidential_transfer_account.pending_balance_lo = - syscall::add_to(&confidential_transfer_account.pending_balance_lo, amount_lo) - .ok_or(TokenError::CiphertextArithmeticFailed)?; + confidential_transfer_account.pending_balance_lo = ciphertext_arithmetic::add_to( + &confidential_transfer_account.pending_balance_lo, + amount_lo, + ) + .ok_or(TokenError::CiphertextArithmeticFailed)?; } if amount_hi > 0 { - confidential_transfer_account.pending_balance_hi = - syscall::add_to(&confidential_transfer_account.pending_balance_hi, amount_hi) - .ok_or(TokenError::CiphertextArithmeticFailed)?; + confidential_transfer_account.pending_balance_hi = ciphertext_arithmetic::add_to( + &confidential_transfer_account.pending_balance_hi, + amount_hi, + ) + .ok_or(TokenError::CiphertextArithmeticFailed)?; } confidential_transfer_account.increment_pending_balance_credit_counter()?; @@ -346,7 +359,8 @@ fn process_withdraw( amount: u64, expected_decimals: u8, new_decryptable_available_balance: DecryptableBalance, - proof_instruction_offset: i64, + equality_proof_instruction_offset: i64, + range_proof_instruction_offset: i64, ) -> ProgramResult { let account_info_iter = &mut accounts.iter(); let token_account_info = next_account_info(account_info_iter)?; @@ -354,7 +368,11 @@ fn process_withdraw( // zero-knowledge proof certifies that the account has enough available balance // to withdraw the amount. - let proof_context = verify_withdraw_proof(account_info_iter, proof_instruction_offset)?; + let proof_context = verify_withdraw_proof( + account_info_iter, + equality_proof_instruction_offset, + range_proof_instruction_offset, + )?; let authority_info = next_account_info(account_info_iter)?; let authority_info_data_len = authority_info.data_len(); @@ -404,20 +422,23 @@ fn process_withdraw( // Check that the encryption public key associated with the confidential // extension is consistent with the public key that was actually used to // generate the zkp. - if confidential_transfer_account.elgamal_pubkey != proof_context.pubkey { + if confidential_transfer_account.elgamal_pubkey != proof_context.source_pubkey { return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); } // Prevent unnecessary ciphertext arithmetic syscalls if the withdraw amount is // zero if amount > 0 { - confidential_transfer_account.available_balance = - syscall::subtract_from(&confidential_transfer_account.available_balance, amount) - .ok_or(TokenError::CiphertextArithmeticFailed)?; + confidential_transfer_account.available_balance = ciphertext_arithmetic::subtract_from( + &confidential_transfer_account.available_balance, + amount, + ) + .ok_or(TokenError::CiphertextArithmeticFailed)?; } // Check that the final available balance ciphertext is consistent with the // actual ciphertext for which the zero-knowledge proof was generated for. - if confidential_transfer_account.available_balance != proof_context.final_ciphertext { + if confidential_transfer_account.available_balance != proof_context.remaining_balance_ciphertext + { return Err(TokenError::ConfidentialTransferBalanceMismatch.into()); } @@ -430,18 +451,18 @@ fn process_withdraw( Ok(()) } -/// Processes a [Transfer] or [TransferWithSplitProofs] instruction. +/// Processes a [Transfer] or [TransferWithFee] instruction. #[allow(clippy::too_many_arguments)] #[cfg(feature = "zk-ops")] fn process_transfer( program_id: &Pubkey, accounts: &[AccountInfo], new_source_decryptable_available_balance: DecryptableBalance, - proof_instruction_offset: i64, - split_proof_context_state_accounts: bool, - no_op_on_uninitialized_split_context_state: bool, - close_split_context_state_on_execution: bool, - source_decrypt_handles: &SourceDecryptHandles, + equality_proof_instruction_offset: i64, + transfer_amount_ciphertext_validity_proof_instruction_offset: i64, + fee_sigma_proof_instruction_offset: Option, + fee_ciphertext_validity_proof_instruction_offset: Option, + range_proof_instruction_offset: i64, ) -> ProgramResult { let account_info_iter = &mut accounts.iter(); let source_account_info = next_account_info(account_info_iter)?; @@ -466,46 +487,27 @@ fn process_transfer( // transfer fee is required. let authority_info = if mint.get_extension::().is_err() { // Transfer fee is not required. Decode the zero-knowledge proof as - // `TransferData`. + // `TransferContext`. // // The zero-knowledge proof certifies that: // 1. the transfer amount is encrypted in the correct form // 2. the source account has enough balance to send the transfer amount - let maybe_proof_context = verify_transfer_proof( + let proof_context = verify_transfer_proof( account_info_iter, - proof_instruction_offset, - split_proof_context_state_accounts, - no_op_on_uninitialized_split_context_state, - close_split_context_state_on_execution, - source_decrypt_handles, + equality_proof_instruction_offset, + transfer_amount_ciphertext_validity_proof_instruction_offset, + range_proof_instruction_offset, )?; - // If `maybe_proof_context` is `None`, then this means that - // `no_op_on_uninitialized_split_context_state` is true and a required context - // state account is not yet initialized. Even if this is the case, we - // follow through with the rest of the transfer logic to perform all the - // necessary checks for a transfer to be safe. - - // If `close_split_context_state_on_execution` is `true`, then the source - // account authority info is located after the lamport destination, - // context state authority, and zk token proof program account infos. - // Flush out these account infos. - if close_split_context_state_on_execution && maybe_proof_context.is_none() { - let _lamport_destination_account_info = next_account_info(account_info_iter)?; - let _context_state_authority_info = next_account_info(account_info_iter)?; - let _zk_token_proof_program_info = next_account_info(account_info_iter)?; - } let authority_info = next_account_info(account_info_iter)?; // Check that the auditor encryption public key associated wth the confidential // mint is consistent with what was actually used to generate the zkp. - if let Some(ref proof_context) = maybe_proof_context { - if !confidential_transfer_mint - .auditor_elgamal_pubkey - .equals(&proof_context.transfer_pubkeys.auditor) - { - return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); - } + if !confidential_transfer_mint + .auditor_elgamal_pubkey + .equals(&proof_context.transfer_pubkeys.auditor) + { + return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); } process_source_for_transfer( @@ -514,72 +516,50 @@ fn process_transfer( mint_info, authority_info, account_info_iter.as_slice(), - maybe_proof_context.as_ref(), + &proof_context, new_source_decryptable_available_balance, )?; - process_destination_for_transfer( - destination_account_info, - mint_info, - maybe_proof_context.as_ref(), - )?; + process_destination_for_transfer(destination_account_info, mint_info, &proof_context)?; - if maybe_proof_context.is_none() { - msg!( - "Context states not fully initialized: returning with no op; transfer is NOT yet - executed" - ); - } authority_info } else { // Transfer fee is required. let transfer_fee_config = mint.get_extension::()?; let fee_parameters = transfer_fee_config.get_epoch_fee(Clock::get()?.epoch); - // Decode the zero-knowledge proof as `TransferWithFeeData`. + let fee_sigma_proof_insruction_offset = + fee_sigma_proof_instruction_offset.ok_or(ProgramError::InvalidInstructionData)?; + let fee_ciphertext_validity_proof_insruction_offset = + fee_ciphertext_validity_proof_instruction_offset + .ok_or(ProgramError::InvalidInstructionData)?; + + // Decode the zero-knowledge proof as `TransferWithFeeContext`. // // The zero-knowledge proof certifies that: // 1. the transfer amount is encrypted in the correct form // 2. the source account has enough balance to send the transfer amount // 3. the transfer fee is computed correctly and encrypted in the correct form - let maybe_proof_context = verify_transfer_with_fee_proof( + let proof_context = verify_transfer_with_fee_proof( account_info_iter, - proof_instruction_offset, - split_proof_context_state_accounts, - no_op_on_uninitialized_split_context_state, - close_split_context_state_on_execution, - source_decrypt_handles, + equality_proof_instruction_offset, + transfer_amount_ciphertext_validity_proof_instruction_offset, + fee_sigma_proof_insruction_offset, + fee_ciphertext_validity_proof_insruction_offset, + range_proof_instruction_offset, fee_parameters, )?; - // If `maybe_proof_context` is `None`, then this means that - // `no_op_on_uninitialized_split_context_state` is true and a required context - // state account is not yet initialized. Even if this is the case, we - // follow through with the rest of the transfer with fee logic to - // perform all the necessary checks to be safe. - - // If `close_split_context_state_on_execution` is `true`, then the source - // account authority info is located after the lamport destination, - // context state authority, and zk token proof program account infos. - // Flush out these account infos. - if close_split_context_state_on_execution && maybe_proof_context.is_none() { - let _lamport_destination_account_info = next_account_info(account_info_iter)?; - let _context_state_authority_info = next_account_info(account_info_iter)?; - let _zk_token_proof_program_info = next_account_info(account_info_iter)?; - } - let authority_info = next_account_info(account_info_iter)?; // Check that the encryption public keys associated with the mint confidential // transfer and confidential transfer fee extensions are consistent with // the keys that were used to generate the zkp. - if let Some(ref proof_context) = maybe_proof_context { - if !confidential_transfer_mint - .auditor_elgamal_pubkey - .equals(&proof_context.transfer_with_fee_pubkeys.auditor) - { - return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); - } + if !confidential_transfer_mint + .auditor_elgamal_pubkey + .equals(&proof_context.transfer_with_fee_pubkeys.auditor) + { + return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); } let confidential_transfer_fee_config = @@ -587,14 +567,12 @@ fn process_transfer( // Check that the withdraw withheld authority ElGamal public key in the mint is // consistent with what was used to generate the zkp. - if let Some(ref proof_context) = maybe_proof_context { - if proof_context - .transfer_with_fee_pubkeys - .withdraw_withheld_authority - != confidential_transfer_fee_config.withdraw_withheld_authority_elgamal_pubkey - { - return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); - } + if proof_context + .transfer_with_fee_pubkeys + .withdraw_withheld_authority + != confidential_transfer_fee_config.withdraw_withheld_authority_elgamal_pubkey + { + return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); } process_source_for_transfer_with_fee( @@ -603,7 +581,7 @@ fn process_transfer( mint_info, authority_info, account_info_iter.as_slice(), - maybe_proof_context.as_ref(), + &proof_context, new_source_decryptable_available_balance, )?; @@ -611,15 +589,10 @@ fn process_transfer( process_destination_for_transfer_with_fee( destination_account_info, mint_info, - maybe_proof_context.as_ref(), + &proof_context, is_self_transfer, )?; - if maybe_proof_context.is_none() { - msg!( - "Context state not fully initialized: returning with no op; transfer is NOT yet executed" - ); - } authority_info }; @@ -668,7 +641,7 @@ fn process_source_for_transfer( mint_info: &AccountInfo, authority_info: &AccountInfo, signers: &[AccountInfo], - maybe_proof_context: Option<&TransferProofContextInfo>, + proof_context: &TransferProofContext, new_source_decryptable_available_balance: DecryptableBalance, ) -> ProgramResult { check_program_account(source_account_info.owner)?; @@ -702,36 +675,38 @@ fn process_source_for_transfer( token_account.get_extension_mut::()?; confidential_transfer_account.valid_as_source()?; - if let Some(proof_context) = maybe_proof_context { - // Check that the source encryption public key is consistent with what was - // actually used to generate the zkp. - if proof_context.transfer_pubkeys.source != confidential_transfer_account.elgamal_pubkey { - return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); - } - - let source_transfer_amount_lo = - transfer_amount_source_ciphertext(&proof_context.ciphertext_lo); - let source_transfer_amount_hi = - transfer_amount_source_ciphertext(&proof_context.ciphertext_hi); + // Check that the source encryption public key is consistent with what was + // actually used to generate the zkp. + if proof_context.transfer_pubkeys.source != confidential_transfer_account.elgamal_pubkey { + return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); + } - let new_source_available_balance = syscall::subtract_with_lo_hi( - &confidential_transfer_account.available_balance, - &source_transfer_amount_lo, - &source_transfer_amount_hi, - ) - .ok_or(TokenError::CiphertextArithmeticFailed)?; + let source_transfer_amount_lo = proof_context + .ciphertext_lo + .try_extract_ciphertext(0) + .map_err(|e| -> TokenError { e.into() })?; + let source_transfer_amount_hi = proof_context + .ciphertext_hi + .try_extract_ciphertext(0) + .map_err(|e| -> TokenError { e.into() })?; - // Check that the computed available balance is consistent with what was - // actually used to generate the zkp on the client side. - if new_source_available_balance != proof_context.new_source_ciphertext { - return Err(TokenError::ConfidentialTransferBalanceMismatch.into()); - } + let new_source_available_balance = ciphertext_arithmetic::subtract_with_lo_hi( + &confidential_transfer_account.available_balance, + &source_transfer_amount_lo, + &source_transfer_amount_hi, + ) + .ok_or(TokenError::CiphertextArithmeticFailed)?; - confidential_transfer_account.available_balance = new_source_available_balance; - confidential_transfer_account.decryptable_available_balance = - new_source_decryptable_available_balance; + // Check that the computed available balance is consistent with what was + // actually used to generate the zkp on the client side. + if new_source_available_balance != proof_context.new_source_ciphertext { + return Err(TokenError::ConfidentialTransferBalanceMismatch.into()); } + confidential_transfer_account.available_balance = new_source_available_balance; + confidential_transfer_account.decryptable_available_balance = + new_source_decryptable_available_balance; + Ok(()) } @@ -739,7 +714,7 @@ fn process_source_for_transfer( fn process_destination_for_transfer( destination_account_info: &AccountInfo, mint_info: &AccountInfo, - maybe_transfer_proof_context_info: Option<&TransferProofContextInfo>, + proof_context: &TransferProofContext, ) -> ProgramResult { check_program_account(destination_account_info.owner)?; let destination_token_account_data = &mut destination_account_info.data.borrow_mut(); @@ -762,32 +737,34 @@ fn process_destination_for_transfer( destination_token_account.get_extension_mut::()?; destination_confidential_transfer_account.valid_as_destination()?; - if let Some(proof_context) = maybe_transfer_proof_context_info { - if proof_context.transfer_pubkeys.destination - != destination_confidential_transfer_account.elgamal_pubkey - { - return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); - } - - let destination_ciphertext_lo = - transfer_amount_destination_ciphertext(&proof_context.ciphertext_lo); - let destination_ciphertext_hi = - transfer_amount_destination_ciphertext(&proof_context.ciphertext_hi); + if proof_context.transfer_pubkeys.destination + != destination_confidential_transfer_account.elgamal_pubkey + { + return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); + } - destination_confidential_transfer_account.pending_balance_lo = syscall::add( - &destination_confidential_transfer_account.pending_balance_lo, - &destination_ciphertext_lo, - ) - .ok_or(TokenError::CiphertextArithmeticFailed)?; + let destination_ciphertext_lo = proof_context + .ciphertext_lo + .try_extract_ciphertext(1) + .map_err(|e| -> TokenError { e.into() })?; + let destination_ciphertext_hi = proof_context + .ciphertext_hi + .try_extract_ciphertext(1) + .map_err(|e| -> TokenError { e.into() })?; + + destination_confidential_transfer_account.pending_balance_lo = ciphertext_arithmetic::add( + &destination_confidential_transfer_account.pending_balance_lo, + &destination_ciphertext_lo, + ) + .ok_or(TokenError::CiphertextArithmeticFailed)?; - destination_confidential_transfer_account.pending_balance_hi = syscall::add( - &destination_confidential_transfer_account.pending_balance_hi, - &destination_ciphertext_hi, - ) - .ok_or(TokenError::CiphertextArithmeticFailed)?; + destination_confidential_transfer_account.pending_balance_hi = ciphertext_arithmetic::add( + &destination_confidential_transfer_account.pending_balance_hi, + &destination_ciphertext_hi, + ) + .ok_or(TokenError::CiphertextArithmeticFailed)?; - destination_confidential_transfer_account.increment_pending_balance_credit_counter()?; - } + destination_confidential_transfer_account.increment_pending_balance_credit_counter()?; Ok(()) } @@ -800,7 +777,7 @@ fn process_source_for_transfer_with_fee( mint_info: &AccountInfo, authority_info: &AccountInfo, signers: &[AccountInfo], - maybe_proof_context: Option<&TransferWithFeeProofContextInfo>, + proof_context: &TransferWithFeeProofContext, new_source_decryptable_available_balance: DecryptableBalance, ) -> ProgramResult { check_program_account(source_account_info.owner)?; @@ -834,38 +811,40 @@ fn process_source_for_transfer_with_fee( token_account.get_extension_mut::()?; confidential_transfer_account.valid_as_source()?; - if let Some(proof_context) = maybe_proof_context { - // Check that the source encryption public key is consistent with what was - // actually used to generate the zkp. - if proof_context.transfer_with_fee_pubkeys.source - != confidential_transfer_account.elgamal_pubkey - { - return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); - } - - let source_transfer_amount_lo = - transfer_amount_source_ciphertext(&proof_context.ciphertext_lo); - let source_transfer_amount_hi = - transfer_amount_source_ciphertext(&proof_context.ciphertext_hi); + // Check that the source encryption public key is consistent with what was + // actually used to generate the zkp. + if proof_context.transfer_with_fee_pubkeys.source + != confidential_transfer_account.elgamal_pubkey + { + return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); + } - let new_source_available_balance = syscall::subtract_with_lo_hi( - &confidential_transfer_account.available_balance, - &source_transfer_amount_lo, - &source_transfer_amount_hi, - ) - .ok_or(TokenError::CiphertextArithmeticFailed)?; + let source_transfer_amount_lo = proof_context + .ciphertext_lo + .try_extract_ciphertext(0) + .map_err(|e| -> TokenError { e.into() })?; + let source_transfer_amount_hi = proof_context + .ciphertext_hi + .try_extract_ciphertext(0) + .map_err(|e| -> TokenError { e.into() })?; - // Check that the computed available balance is consistent with what was - // actually used to generate the zkp on the client side. - if new_source_available_balance != proof_context.new_source_ciphertext { - return Err(TokenError::ConfidentialTransferBalanceMismatch.into()); - } + let new_source_available_balance = ciphertext_arithmetic::subtract_with_lo_hi( + &confidential_transfer_account.available_balance, + &source_transfer_amount_lo, + &source_transfer_amount_hi, + ) + .ok_or(TokenError::CiphertextArithmeticFailed)?; - confidential_transfer_account.available_balance = new_source_available_balance; - confidential_transfer_account.decryptable_available_balance = - new_source_decryptable_available_balance; + // Check that the computed available balance is consistent with what was + // actually used to generate the zkp on the client side. + if new_source_available_balance != proof_context.new_source_ciphertext { + return Err(TokenError::ConfidentialTransferBalanceMismatch.into()); } + confidential_transfer_account.available_balance = new_source_available_balance; + confidential_transfer_account.decryptable_available_balance = + new_source_decryptable_available_balance; + Ok(()) } @@ -873,7 +852,7 @@ fn process_source_for_transfer_with_fee( fn process_destination_for_transfer_with_fee( destination_account_info: &AccountInfo, mint_info: &AccountInfo, - maybe_proof_context: Option<&TransferWithFeeProofContextInfo>, + proof_context: &TransferWithFeeProofContext, is_self_transfer: bool, ) -> ProgramResult { check_program_account(destination_account_info.owner)?; @@ -897,71 +876,84 @@ fn process_destination_for_transfer_with_fee( destination_token_account.get_extension_mut::()?; destination_confidential_transfer_account.valid_as_destination()?; - if let Some(proof_context) = maybe_proof_context { - if proof_context.transfer_with_fee_pubkeys.destination - != destination_confidential_transfer_account.elgamal_pubkey - { - return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); - } - - let destination_transfer_amount_lo = - transfer_amount_destination_ciphertext(&proof_context.ciphertext_lo); - let destination_transfer_amount_hi = - transfer_amount_destination_ciphertext(&proof_context.ciphertext_hi); - - destination_confidential_transfer_account.pending_balance_lo = syscall::add( - &destination_confidential_transfer_account.pending_balance_lo, - &destination_transfer_amount_lo, - ) - .ok_or(TokenError::CiphertextArithmeticFailed)?; - - destination_confidential_transfer_account.pending_balance_hi = syscall::add( - &destination_confidential_transfer_account.pending_balance_hi, - &destination_transfer_amount_hi, - ) - .ok_or(TokenError::CiphertextArithmeticFailed)?; + if proof_context.transfer_with_fee_pubkeys.destination + != destination_confidential_transfer_account.elgamal_pubkey + { + return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); + } - destination_confidential_transfer_account.increment_pending_balance_credit_counter()?; + let destination_transfer_amount_lo = proof_context + .ciphertext_lo + .try_extract_ciphertext(1) + .map_err(|e| -> TokenError { e.into() })?; + let destination_transfer_amount_hi = proof_context + .ciphertext_hi + .try_extract_ciphertext(1) + .map_err(|e| -> TokenError { e.into() })?; + + destination_confidential_transfer_account.pending_balance_lo = ciphertext_arithmetic::add( + &destination_confidential_transfer_account.pending_balance_lo, + &destination_transfer_amount_lo, + ) + .ok_or(TokenError::CiphertextArithmeticFailed)?; - // process transfer fee - if !is_self_transfer { - // Decode lo and hi fee amounts encrypted under the destination encryption - // public key - let destination_fee_lo = - fee_amount_destination_ciphertext(&proof_context.fee_ciphertext_lo); - let destination_fee_hi = - fee_amount_destination_ciphertext(&proof_context.fee_ciphertext_hi); + destination_confidential_transfer_account.pending_balance_hi = ciphertext_arithmetic::add( + &destination_confidential_transfer_account.pending_balance_hi, + &destination_transfer_amount_hi, + ) + .ok_or(TokenError::CiphertextArithmeticFailed)?; - // Subtract the fee amount from the destination pending balance - destination_confidential_transfer_account.pending_balance_lo = syscall::subtract( + destination_confidential_transfer_account.increment_pending_balance_credit_counter()?; + + // process transfer fee + if !is_self_transfer { + // Decode lo and hi fee amounts encrypted under the destination encryption + // public key + let destination_fee_lo = proof_context + .fee_ciphertext_lo + .try_extract_ciphertext(0) + .map_err(|e| -> TokenError { e.into() })?; + let destination_fee_hi = proof_context + .fee_ciphertext_hi + .try_extract_ciphertext(0) + .map_err(|e| -> TokenError { e.into() })?; + + // Subtract the fee amount from the destination pending balance + destination_confidential_transfer_account.pending_balance_lo = + ciphertext_arithmetic::subtract( &destination_confidential_transfer_account.pending_balance_lo, &destination_fee_lo, ) .ok_or(TokenError::CiphertextArithmeticFailed)?; - destination_confidential_transfer_account.pending_balance_hi = syscall::subtract( + destination_confidential_transfer_account.pending_balance_hi = + ciphertext_arithmetic::subtract( &destination_confidential_transfer_account.pending_balance_hi, &destination_fee_hi, ) .ok_or(TokenError::CiphertextArithmeticFailed)?; - // Decode lo and hi fee amounts encrypted under the withdraw authority - // encryption public key - let withdraw_withheld_authority_fee_lo = - fee_amount_withdraw_withheld_authority_ciphertext(&proof_context.fee_ciphertext_lo); - let withdraw_withheld_authority_fee_hi = - fee_amount_withdraw_withheld_authority_ciphertext(&proof_context.fee_ciphertext_hi); - - let destination_confidential_transfer_fee_amount = - destination_token_account.get_extension_mut::()?; - - // Add the fee amount to the destination withheld fee - destination_confidential_transfer_fee_amount.withheld_amount = syscall::add_with_lo_hi( + // Decode lo and hi fee amounts encrypted under the withdraw authority + // encryption public key + let withdraw_withheld_authority_fee_lo = proof_context + .fee_ciphertext_lo + .try_extract_ciphertext(1) + .map_err(|e| -> TokenError { e.into() })?; + let withdraw_withheld_authority_fee_hi = proof_context + .fee_ciphertext_hi + .try_extract_ciphertext(1) + .map_err(|e| -> TokenError { e.into() })?; + + let destination_confidential_transfer_fee_amount = + destination_token_account.get_extension_mut::()?; + + // Add the fee amount to the destination withheld fee + destination_confidential_transfer_fee_amount.withheld_amount = + ciphertext_arithmetic::add_with_lo_hi( &destination_confidential_transfer_fee_amount.withheld_amount, &withdraw_withheld_authority_fee_lo, &withdraw_withheld_authority_fee_hi, ) .ok_or(TokenError::CiphertextArithmeticFailed)?; - } } Ok(()) @@ -997,7 +989,7 @@ fn process_apply_pending_balance( let confidential_transfer_account = token_account.get_extension_mut::()?; - confidential_transfer_account.available_balance = syscall::add_with_lo_hi( + confidential_transfer_account.available_balance = ciphertext_arithmetic::add_with_lo_hi( &confidential_transfer_account.available_balance, &confidential_transfer_account.pending_balance_lo, &confidential_transfer_account.pending_balance_hi, @@ -1149,7 +1141,8 @@ pub(crate) fn process_instruction( data.amount.into(), data.decimals, data.new_decryptable_available_balance, - data.proof_instruction_offset as i64, + data.equality_proof_instruction_offset as i64, + data.range_proof_instruction_offset as i64, ) } #[cfg(not(feature = "zk-ops"))] @@ -1164,11 +1157,11 @@ pub(crate) fn process_instruction( program_id, accounts, data.new_source_decryptable_available_balance, - data.proof_instruction_offset as i64, - false, - false, - false, - &SourceDecryptHandles::zeroed(), + data.equality_proof_instruction_offset as i64, + data.ciphertext_validity_proof_instruction_offset as i64, + None, + None, + data.range_proof_instruction_offset as i64, ) } #[cfg(not(feature = "zk-ops"))] @@ -1205,21 +1198,20 @@ pub(crate) fn process_instruction( msg!("ConfidentialTransferInstruction::EnableNonConfidentialCredits"); process_allow_non_confidential_credits(program_id, accounts, true) } - ConfidentialTransferInstruction::TransferWithSplitProofs => { - msg!("ConfidentialTransferInstruction::TransferWithSplitProofs"); + ConfidentialTransferInstruction::TransferWithFee => { + msg!("ConfidentialTransferInstruction::TransferWithFee"); #[cfg(feature = "zk-ops")] { - let data = - decode_instruction_data::(input)?; + let data = decode_instruction_data::(input)?; process_transfer( program_id, accounts, data.new_source_decryptable_available_balance, - 0, - true, - data.no_op_on_uninitialized_split_context_state.into(), - data.close_split_context_state_on_execution.into(), - &data.source_decrypt_handles, + data.equality_proof_instruction_offset as i64, + data.transfer_amount_ciphertext_validity_proof_instruction_offset as i64, + Some(data.fee_sigma_proof_instruction_offset as i64), + Some(data.fee_ciphertext_validity_proof_instruction_offset as i64), + data.range_proof_instruction_offset as i64, ) } #[cfg(not(feature = "zk-ops"))] diff --git a/token/program-2022/src/extension/confidential_transfer/split_proof_generation.rs b/token/program-2022/src/extension/confidential_transfer/split_proof_generation.rs deleted file mode 100644 index 9d376bb0883..00000000000 --- a/token/program-2022/src/extension/confidential_transfer/split_proof_generation.rs +++ /dev/null @@ -1,190 +0,0 @@ -//! Helper functions to generate split zero-knowledge proofs for confidential -//! transfers in the Confidential Transfer Extension. -//! -//! The logic in this submodule should belong to the `solana-zk-token-sdk` and -//! will be removed with the next upgrade to the Solana program. - -use crate::{ - extension::confidential_transfer::{ - ciphertext_extraction::{transfer_amount_source_ciphertext, SourceDecryptHandles}, - processor::verify_and_split_deposit_amount, - *, - }, - solana_zk_token_sdk::{ - encryption::{ - auth_encryption::{AeCiphertext, AeKey}, - elgamal::{DecryptHandle, ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey}, - grouped_elgamal::GroupedElGamal, - pedersen::Pedersen, - }, - instruction::{ - transfer::TransferAmountCiphertext, BatchedGroupedCiphertext2HandlesValidityProofData, - BatchedRangeProofU128Data, CiphertextCommitmentEqualityProofData, - }, - zk_token_elgamal::ops::subtract_with_lo_hi, - }, -}; - -/// The main logic to create the three split proof data for a transfer. -pub fn transfer_split_proof_data( - current_available_balance: &ElGamalCiphertext, - current_decryptable_available_balance: &AeCiphertext, - transfer_amount: u64, - source_elgamal_keypair: &ElGamalKeypair, - aes_key: &AeKey, - destination_elgamal_pubkey: &ElGamalPubkey, - auditor_elgamal_pubkey: Option<&ElGamalPubkey>, -) -> Result< - ( - CiphertextCommitmentEqualityProofData, - BatchedGroupedCiphertext2HandlesValidityProofData, - BatchedRangeProofU128Data, - SourceDecryptHandles, - ), - TokenError, -> { - let default_auditor_pubkey = ElGamalPubkey::default(); - let auditor_elgamal_pubkey = auditor_elgamal_pubkey.unwrap_or(&default_auditor_pubkey); - - // Split the transfer amount into the low and high bit components. - let (transfer_amount_lo, transfer_amount_hi) = - verify_and_split_deposit_amount(transfer_amount)?; - - // Encrypt the `lo` and `hi` transfer amounts. - let (transfer_amount_grouped_ciphertext_lo, transfer_amount_opening_lo) = - TransferAmountCiphertext::new( - transfer_amount_lo, - source_elgamal_keypair.pubkey(), - destination_elgamal_pubkey, - auditor_elgamal_pubkey, - ); - - let (transfer_amount_grouped_ciphertext_hi, transfer_amount_opening_hi) = - TransferAmountCiphertext::new( - transfer_amount_hi, - source_elgamal_keypair.pubkey(), - destination_elgamal_pubkey, - auditor_elgamal_pubkey, - ); - - // Decrypt the current available balance at the source - let current_decrypted_available_balance = current_decryptable_available_balance - .decrypt(aes_key) - .ok_or(TokenError::AccountDecryption)?; - - // Compute the remaining balance at the source - let new_decrypted_available_balance = current_decrypted_available_balance - .checked_sub(transfer_amount) - .ok_or(TokenError::InsufficientFunds)?; - - // Create a new Pedersen commitment for the remaining balance at the source - let (new_available_balance_commitment, new_source_opening) = - Pedersen::new(new_decrypted_available_balance); - - // Compute the remaining balance at the source as ElGamal ciphertexts - let transfer_amount_source_ciphertext_lo = - transfer_amount_source_ciphertext(&transfer_amount_grouped_ciphertext_lo.into()); - let transfer_amount_source_ciphertext_hi = - transfer_amount_source_ciphertext(&transfer_amount_grouped_ciphertext_hi.into()); - - let current_available_balance = (*current_available_balance).into(); - let new_available_balance_ciphertext = subtract_with_lo_hi( - ¤t_available_balance, - &transfer_amount_source_ciphertext_lo, - &transfer_amount_source_ciphertext_hi, - ) - .ok_or(TokenError::CiphertextArithmeticFailed)?; - let new_available_balance_ciphertext: ElGamalCiphertext = new_available_balance_ciphertext - .try_into() - .map_err(|_| TokenError::MalformedCiphertext)?; - - // generate equality proof data - let equality_proof_data = CiphertextCommitmentEqualityProofData::new( - source_elgamal_keypair, - &new_available_balance_ciphertext, - &new_available_balance_commitment, - &new_source_opening, - new_decrypted_available_balance, - ) - .map_err(|_| TokenError::ProofGeneration)?; - - // create source decrypt handle - let source_decrypt_handle_lo = - DecryptHandle::new(source_elgamal_keypair.pubkey(), &transfer_amount_opening_lo); - let source_decrypt_handle_hi = - DecryptHandle::new(source_elgamal_keypair.pubkey(), &transfer_amount_opening_hi); - - let source_decrypt_handles = SourceDecryptHandles { - lo: source_decrypt_handle_lo.into(), - hi: source_decrypt_handle_hi.into(), - }; - - // encrypt the transfer amount under the destination and auditor ElGamal public - // key - let transfer_amount_destination_auditor_ciphertext_lo = GroupedElGamal::encrypt_with( - [destination_elgamal_pubkey, auditor_elgamal_pubkey], - transfer_amount_lo, - &transfer_amount_opening_lo, - ); - let transfer_amount_destination_auditor_ciphertext_hi = GroupedElGamal::encrypt_with( - [destination_elgamal_pubkey, auditor_elgamal_pubkey], - transfer_amount_hi, - &transfer_amount_opening_hi, - ); - - // generate ciphertext validity data - let ciphertext_validity_proof_data = BatchedGroupedCiphertext2HandlesValidityProofData::new( - destination_elgamal_pubkey, - auditor_elgamal_pubkey, - &transfer_amount_destination_auditor_ciphertext_lo, - &transfer_amount_destination_auditor_ciphertext_hi, - transfer_amount_lo, - transfer_amount_hi, - &transfer_amount_opening_lo, - &transfer_amount_opening_hi, - ) - .map_err(|_| TokenError::ProofGeneration)?; - - // generate range proof data - const REMAINING_BALANCE_BIT_LENGTH: usize = 64; - const TRANSFER_AMOUNT_LO_BIT_LENGTH: usize = 16; - const TRANSFER_AMOUNT_HI_BIT_LENGTH: usize = 32; - const PADDING_BIT_LENGTH: usize = 16; - - let (padding_commitment, padding_opening) = Pedersen::new(0_u64); - - let range_proof_data = BatchedRangeProofU128Data::new( - vec![ - &new_available_balance_commitment, - transfer_amount_grouped_ciphertext_lo.get_commitment(), - transfer_amount_grouped_ciphertext_hi.get_commitment(), - &padding_commitment, - ], - vec![ - new_decrypted_available_balance, - transfer_amount_lo, - transfer_amount_hi, - 0, - ], - vec![ - REMAINING_BALANCE_BIT_LENGTH, - TRANSFER_AMOUNT_LO_BIT_LENGTH, - TRANSFER_AMOUNT_HI_BIT_LENGTH, - PADDING_BIT_LENGTH, - ], - vec![ - &new_source_opening, - &transfer_amount_opening_lo, - &transfer_amount_opening_hi, - &padding_opening, - ], - ) - .map_err(|_| TokenError::ProofGeneration)?; - - Ok(( - equality_proof_data, - ciphertext_validity_proof_data, - range_proof_data, - source_decrypt_handles, - )) -} diff --git a/token/program-2022/src/extension/confidential_transfer/verify_proof.rs b/token/program-2022/src/extension/confidential_transfer/verify_proof.rs index 13c6001ba7b..912d3c66d3d 100644 --- a/token/program-2022/src/extension/confidential_transfer/verify_proof.rs +++ b/token/program-2022/src/extension/confidential_transfer/verify_proof.rs @@ -1,600 +1,198 @@ use { crate::{ - check_system_program_account, check_zk_token_proof_program_account, - extension::{ - confidential_transfer::{ciphertext_extraction::*, instruction::*, *}, - transfer_fee::TransferFee, - }, - proof::decode_proof_instruction_context, + error::TokenError, + extension::{confidential_transfer::instruction::*, transfer_fee::TransferFee}, + proof::verify_and_extract_context, }, solana_program::{ account_info::{next_account_info, AccountInfo}, - msg, - program::invoke, program_error::ProgramError, - sysvar::instructions::get_instruction_relative, }, - solana_zk_token_sdk::zk_token_proof_instruction::{self, ContextStateInfo}, + spl_token_confidential_transfer_proof_extraction::{ + transfer::TransferProofContext, transfer_with_fee::TransferWithFeeProofContext, + withdraw::WithdrawProofContext, + }, std::slice::Iter, }; -/// Verify zero-knowledge proof needed for a [ConfigureAccount] instruction and -/// return the corresponding proof context. -pub fn verify_configure_account_proof( - account_info_iter: &mut Iter<'_, AccountInfo<'_>>, - proof_instruction_offset: i64, -) -> Result { - if proof_instruction_offset == 0 { - // interpret `account_info` as a context state account - let context_state_account_info = next_account_info(account_info_iter)?; - check_zk_token_proof_program_account(context_state_account_info.owner)?; - let context_state_account_data = context_state_account_info.data.borrow(); - let context_state = pod_from_bytes::>( - &context_state_account_data, - )?; - - if context_state.proof_type != ProofType::PubkeyValidity.into() { - return Err(ProgramError::InvalidInstructionData); - } - - Ok(context_state.proof_context) - } else { - // interpret `account_info` as a sysvar - let sysvar_account_info = next_account_info(account_info_iter)?; - let zkp_instruction = - get_instruction_relative(proof_instruction_offset, sysvar_account_info)?; - Ok(*decode_proof_instruction_context::< - PubkeyValidityData, - PubkeyValidityProofContext, - >( - ProofInstruction::VerifyPubkeyValidity, &zkp_instruction - )?) - } -} - -/// Verify zero-knowledge proof needed for a [EmptyAccount] instruction and -/// return the corresponding proof context. -pub fn verify_empty_account_proof( - account_info_iter: &mut Iter<'_, AccountInfo<'_>>, - proof_instruction_offset: i64, -) -> Result { - if proof_instruction_offset == 0 { - // interpret `account_info` as a context state account - let context_state_account_info = next_account_info(account_info_iter)?; - check_zk_token_proof_program_account(context_state_account_info.owner)?; - let context_state_account_data = context_state_account_info.data.borrow(); - let context_state = pod_from_bytes::>( - &context_state_account_data, - )?; - - if context_state.proof_type != ProofType::ZeroBalance.into() { - return Err(ProgramError::InvalidInstructionData); - } - - Ok(context_state.proof_context) - } else { - // interpret `account_info` as a sysvar - let sysvar_account_info = next_account_info(account_info_iter)?; - let zkp_instruction = - get_instruction_relative(proof_instruction_offset, sysvar_account_info)?; - Ok(*decode_proof_instruction_context::< - ZeroBalanceProofData, - ZeroBalanceProofContext, - >( - ProofInstruction::VerifyZeroBalance, &zkp_instruction - )?) - } -} - -/// Verify zero-knowledge proof needed for a [Withdraw] instruction and return +/// Verify zero-knowledge proofs needed for a [Withdraw] instruction and return /// the corresponding proof context. +#[cfg(feature = "zk-ops")] pub fn verify_withdraw_proof( - account_info_iter: &mut Iter<'_, AccountInfo<'_>>, - proof_instruction_offset: i64, + account_info_iter: &mut Iter, + equality_proof_instruction_offset: i64, + range_proof_instruction_offset: i64, ) -> Result { - if proof_instruction_offset == 0 { - // interpret `account_info` as a context state account - let context_state_account_info = next_account_info(account_info_iter)?; - check_zk_token_proof_program_account(context_state_account_info.owner)?; - let context_state_account_data = context_state_account_info.data.borrow(); - let context_state = - pod_from_bytes::>(&context_state_account_data)?; + let sysvar_account_info = + if equality_proof_instruction_offset != 0 || range_proof_instruction_offset != 0 { + Some(next_account_info(account_info_iter)?) + } else { + None + }; + + let equality_proof_context = verify_and_extract_context::< + CiphertextCommitmentEqualityProofData, + CiphertextCommitmentEqualityProofContext, + >( + account_info_iter, + equality_proof_instruction_offset, + sysvar_account_info, + )?; + + let range_proof_context = + verify_and_extract_context::( + account_info_iter, + range_proof_instruction_offset, + sysvar_account_info, + )?; - if context_state.proof_type != ProofType::Withdraw.into() { - return Err(ProgramError::InvalidInstructionData); - } + // The `WithdrawProofContext` constructor verifies the consistency of the + // individual proof context and generates a `WithdrawProofContext` struct + // that is used to process the rest of the token-2022 logic. + let transfer_proof_context = + WithdrawProofContext::verify_and_extract(&equality_proof_context, &range_proof_context) + .map_err(|e| -> TokenError { e.into() })?; - Ok(context_state.proof_context) - } else { - // interpret `account_info` as a sysvar - let sysvar_account_info = next_account_info(account_info_iter)?; - let zkp_instruction = - get_instruction_relative(proof_instruction_offset, sysvar_account_info)?; - Ok(*decode_proof_instruction_context::< - WithdrawData, - WithdrawProofContext, - >( - ProofInstruction::VerifyWithdraw, &zkp_instruction - )?) - } + Ok(transfer_proof_context) } /// Verify zero-knowledge proof needed for a [Transfer] instruction without fee /// and return the corresponding proof context. -/// -/// This returns a `Result` type for an `Option` type. -/// If the proof verification fails, then the function returns a suitable error -/// variant. If the proof succeeds to verify, then the function returns a -/// `TransferProofContextInfo` that is wrapped inside -/// `Ok(Some(TransferProofContextInfo))`. If -/// `no_op_on_split_proof_context_state` is `true` and some a split context -/// state account is not initialized, then it returns `Ok(None)`. #[cfg(feature = "zk-ops")] pub fn verify_transfer_proof( - account_info_iter: &mut Iter<'_, AccountInfo<'_>>, - proof_instruction_offset: i64, - split_proof_context_state_accounts: bool, - no_op_on_split_proof_context_state: bool, - close_split_context_state_on_execution: bool, - source_decrypt_handles: &SourceDecryptHandles, -) -> Result, ProgramError> { - if proof_instruction_offset == 0 && split_proof_context_state_accounts { - let equality_proof_context_state_account_info = next_account_info(account_info_iter)?; - let ciphertext_validity_proof_context_state_account_info = - next_account_info(account_info_iter)?; - let range_proof_context_state_account_info = next_account_info(account_info_iter)?; - - if no_op_on_split_proof_context_state - && check_system_program_account(equality_proof_context_state_account_info.owner).is_ok() - { - msg!("Equality proof context state account not initialized"); - return Ok(None); - } - - if no_op_on_split_proof_context_state - && check_system_program_account( - ciphertext_validity_proof_context_state_account_info.owner, - ) - .is_ok() - { - msg!("Ciphertext validity proof context state account not initialized"); - return Ok(None); - } - - if no_op_on_split_proof_context_state - && check_system_program_account(range_proof_context_state_account_info.owner).is_ok() - { - msg!("Range proof context state account not initialized"); - return Ok(None); - } - - let equality_proof_context = - verify_equality_proof(equality_proof_context_state_account_info)?; - let ciphertext_validity_proof_context = - verify_ciphertext_validity_proof(ciphertext_validity_proof_context_state_account_info)?; - let range_proof_context = - verify_transfer_range_proof(range_proof_context_state_account_info)?; - - // The `TransferProofContextInfo` constructor verifies the consistency of the - // individual proof context and generates a `TransferWithFeeProofInfo` struct - // that is used to process the rest of the token-2022 logic. - let transfer_proof_context = TransferProofContextInfo::verify_and_extract( - &equality_proof_context, - &ciphertext_validity_proof_context, - &range_proof_context, - source_decrypt_handles, - )?; - - if close_split_context_state_on_execution { - let lamport_destination_account_info = next_account_info(account_info_iter)?; - let context_state_account_authority_info = next_account_info(account_info_iter)?; - let _zk_token_proof_program = next_account_info(account_info_iter)?; - - msg!("Closing equality proof context state account"); - invoke( - &zk_token_proof_instruction::close_context_state( - ContextStateInfo { - context_state_account: equality_proof_context_state_account_info.key, - context_state_authority: context_state_account_authority_info.key, - }, - lamport_destination_account_info.key, - ), - &[ - equality_proof_context_state_account_info.clone(), - lamport_destination_account_info.clone(), - context_state_account_authority_info.clone(), - ], - )?; - - msg!("Closing ciphertext validity proof context state account"); - invoke( - &zk_token_proof_instruction::close_context_state( - ContextStateInfo { - context_state_account: ciphertext_validity_proof_context_state_account_info - .key, - context_state_authority: context_state_account_authority_info.key, - }, - lamport_destination_account_info.key, - ), - &[ - ciphertext_validity_proof_context_state_account_info.clone(), - lamport_destination_account_info.clone(), - context_state_account_authority_info.clone(), - ], - )?; - - msg!("Closing range proof context state account"); - invoke( - &zk_token_proof_instruction::close_context_state( - ContextStateInfo { - context_state_account: range_proof_context_state_account_info.key, - context_state_authority: context_state_account_authority_info.key, - }, - lamport_destination_account_info.key, - ), - &[ - range_proof_context_state_account_info.clone(), - lamport_destination_account_info.clone(), - context_state_account_authority_info.clone(), - ], - )?; - } - - Ok(Some(transfer_proof_context)) - } else if proof_instruction_offset == 0 && !split_proof_context_state_accounts { - // interpret `account_info` as a context state account - let context_state_account_info = next_account_info(account_info_iter)?; - check_zk_token_proof_program_account(context_state_account_info.owner)?; - let context_state_account_data = context_state_account_info.data.borrow(); - let context_state = - pod_from_bytes::>(&context_state_account_data)?; - - if context_state.proof_type != ProofType::Transfer.into() { - return Err(ProgramError::InvalidInstructionData); - } - - Ok(Some(context_state.proof_context.into())) + account_info_iter: &mut Iter, + equality_proof_instruction_offset: i64, + ciphertext_validity_proof_instruction_offset: i64, + range_proof_instruction_offset: i64, +) -> Result { + let sysvar_account_info = if equality_proof_instruction_offset != 0 + || ciphertext_validity_proof_instruction_offset != 0 + || range_proof_instruction_offset != 0 + { + Some(next_account_info(account_info_iter)?) } else { - // interpret `account_info` as sysvar - let sysvar_account_info = next_account_info(account_info_iter)?; - let zkp_instruction = - get_instruction_relative(proof_instruction_offset, sysvar_account_info)?; - let proof_context = (*decode_proof_instruction_context::< - TransferData, - TransferProofContext, - >(ProofInstruction::VerifyTransfer, &zkp_instruction)?) - .into(); + None + }; + + let equality_proof_context = verify_and_extract_context::< + CiphertextCommitmentEqualityProofData, + CiphertextCommitmentEqualityProofContext, + >( + account_info_iter, + equality_proof_instruction_offset, + sysvar_account_info, + )?; + + let ciphertext_validity_proof_context = verify_and_extract_context::< + BatchedGroupedCiphertext3HandlesValidityProofData, + BatchedGroupedCiphertext3HandlesValidityProofContext, + >( + account_info_iter, + ciphertext_validity_proof_instruction_offset, + sysvar_account_info, + )?; + + let range_proof_context = + verify_and_extract_context::( + account_info_iter, + range_proof_instruction_offset, + sysvar_account_info, + )?; - Ok(Some(proof_context)) - } + // The `TransferProofContext` constructor verifies the consistency of the + // individual proof context and generates a `TransferWithFeeProofInfo` struct + // that is used to process the rest of the token-2022 logic. + let transfer_proof_context = TransferProofContext::verify_and_extract( + &equality_proof_context, + &ciphertext_validity_proof_context, + &range_proof_context, + ) + .map_err(|e| -> TokenError { e.into() })?; + + Ok(transfer_proof_context) } /// Verify zero-knowledge proof needed for a [Transfer] instruction with fee and /// return the corresponding proof context. #[cfg(feature = "zk-ops")] +#[allow(clippy::too_many_arguments)] pub fn verify_transfer_with_fee_proof( - account_info_iter: &mut Iter<'_, AccountInfo<'_>>, - proof_instruction_offset: i64, - split_proof_context_state_accounts: bool, - no_op_on_split_proof_context_state: bool, - close_split_context_state_on_execution: bool, - source_decrypt_handles: &SourceDecryptHandles, + account_info_iter: &mut Iter, + equality_proof_instruction_offset: i64, + transfer_amount_ciphertext_validity_proof_instruction_offset: i64, + fee_sigma_proof_instruction_offset: i64, + fee_ciphertext_validity_proof_instruction_offset: i64, + range_proof_instruction_offset: i64, fee_parameters: &TransferFee, -) -> Result, ProgramError> { - if proof_instruction_offset == 0 && split_proof_context_state_accounts { - let equality_proof_context_state_account_info = next_account_info(account_info_iter)?; - let transfer_amount_ciphertext_validity_proof_context_state_account_info = - next_account_info(account_info_iter)?; - let fee_sigma_proof_context_state_account_info = next_account_info(account_info_iter)?; - let fee_ciphertext_validity_proof_context_state_account_info = - next_account_info(account_info_iter)?; - let range_proof_context_state_account_info = next_account_info(account_info_iter)?; - - if no_op_on_split_proof_context_state - && check_system_program_account(equality_proof_context_state_account_info.owner).is_ok() - { - msg!("Equality proof context state account not initialized"); - return Ok(None); - } - - if no_op_on_split_proof_context_state - && check_system_program_account( - transfer_amount_ciphertext_validity_proof_context_state_account_info.owner, - ) - .is_ok() - { - msg!("Transfer amount ciphertext validity proof context state account not initialized"); - return Ok(None); - } - - if no_op_on_split_proof_context_state - && check_system_program_account(fee_sigma_proof_context_state_account_info.owner) - .is_ok() - { - msg!("Fee sigma proof context state account not initialized"); - return Ok(None); - } - - if no_op_on_split_proof_context_state - && check_system_program_account( - fee_ciphertext_validity_proof_context_state_account_info.owner, - ) - .is_ok() - { - msg!("Fee ciphertext validity proof context state account not initialized"); - return Ok(None); - } - - if no_op_on_split_proof_context_state - && check_system_program_account(range_proof_context_state_account_info.owner).is_ok() - { - msg!("Range proof context state account not initialized"); - return Ok(None); - } - - let equality_proof_context = - verify_equality_proof(equality_proof_context_state_account_info)?; - let transfer_amount_ciphertext_validity_proof_context = verify_ciphertext_validity_proof( - transfer_amount_ciphertext_validity_proof_context_state_account_info, - )?; - let fee_sigma_proof_context = - verify_fee_sigma_proof(fee_sigma_proof_context_state_account_info)?; - let fee_ciphertext_validity_proof_context = verify_ciphertext_validity_proof( - fee_ciphertext_validity_proof_context_state_account_info, - )?; - let range_proof_context = - verify_transfer_with_fee_range_proof(range_proof_context_state_account_info)?; - - // The `TransferWithFeeProofContextInfo` constructor verifies the consistency of - // the individual proof context and generates a - // `TransferWithFeeProofInfo` struct that is used to process the rest of - // the token-2022 logic. The consistency check includes verifying - // whether the fee-related zkps were generated with respect to the correct fee - // parameter that is stored in the mint extension. - let transfer_with_fee_proof_context = TransferWithFeeProofContextInfo::verify_and_extract( - &equality_proof_context, - &transfer_amount_ciphertext_validity_proof_context, - &fee_sigma_proof_context, - &fee_ciphertext_validity_proof_context, - &range_proof_context, - source_decrypt_handles, - fee_parameters, +) -> Result { + let sysvar_account_info = if equality_proof_instruction_offset != 0 + || transfer_amount_ciphertext_validity_proof_instruction_offset != 0 + || fee_sigma_proof_instruction_offset != 0 + || fee_ciphertext_validity_proof_instruction_offset != 0 + || range_proof_instruction_offset != 0 + { + Some(next_account_info(account_info_iter)?) + } else { + None + }; + + let equality_proof_context = verify_and_extract_context::< + CiphertextCommitmentEqualityProofData, + CiphertextCommitmentEqualityProofContext, + >( + account_info_iter, + equality_proof_instruction_offset, + sysvar_account_info, + )?; + + let transfer_amount_ciphertext_validity_proof_context = verify_and_extract_context::< + BatchedGroupedCiphertext3HandlesValidityProofData, + BatchedGroupedCiphertext3HandlesValidityProofContext, + >( + account_info_iter, + transfer_amount_ciphertext_validity_proof_instruction_offset, + sysvar_account_info, + )?; + + let fee_sigma_proof_context = + verify_and_extract_context::( + account_info_iter, + fee_sigma_proof_instruction_offset, + sysvar_account_info, )?; - if close_split_context_state_on_execution { - let lamport_destination_account_info = next_account_info(account_info_iter)?; - let context_state_account_authority_info = next_account_info(account_info_iter)?; - let _zk_token_proof_program = next_account_info(account_info_iter)?; - - msg!("Closing equality proof context state account"); - invoke( - &zk_token_proof_instruction::close_context_state( - ContextStateInfo { - context_state_account: equality_proof_context_state_account_info.key, - context_state_authority: context_state_account_authority_info.key, - }, - lamport_destination_account_info.key, - ), - &[ - equality_proof_context_state_account_info.clone(), - lamport_destination_account_info.clone(), - context_state_account_authority_info.clone(), - ], - )?; - - msg!("Closing transfer amount ciphertext validity proof context state account"); - invoke( - &zk_token_proof_instruction::close_context_state( - ContextStateInfo { - context_state_account: - transfer_amount_ciphertext_validity_proof_context_state_account_info.key, - context_state_authority: context_state_account_authority_info.key, - }, - lamport_destination_account_info.key, - ), - &[ - transfer_amount_ciphertext_validity_proof_context_state_account_info.clone(), - lamport_destination_account_info.clone(), - context_state_account_authority_info.clone(), - ], - )?; - - msg!("Closing fee sigma proof context state account"); - invoke( - &zk_token_proof_instruction::close_context_state( - ContextStateInfo { - context_state_account: fee_sigma_proof_context_state_account_info.key, - context_state_authority: context_state_account_authority_info.key, - }, - lamport_destination_account_info.key, - ), - &[ - fee_sigma_proof_context_state_account_info.clone(), - lamport_destination_account_info.clone(), - context_state_account_authority_info.clone(), - ], - )?; - - msg!("Closing fee ciphertext validity proof context state account"); - invoke( - &zk_token_proof_instruction::close_context_state( - ContextStateInfo { - context_state_account: - fee_ciphertext_validity_proof_context_state_account_info.key, - context_state_authority: context_state_account_authority_info.key, - }, - lamport_destination_account_info.key, - ), - &[ - fee_ciphertext_validity_proof_context_state_account_info.clone(), - lamport_destination_account_info.clone(), - context_state_account_authority_info.clone(), - ], - )?; - - msg!("Closing range proof context state account"); - invoke( - &zk_token_proof_instruction::close_context_state( - ContextStateInfo { - context_state_account: range_proof_context_state_account_info.key, - context_state_authority: context_state_account_authority_info.key, - }, - lamport_destination_account_info.key, - ), - &[ - range_proof_context_state_account_info.clone(), - lamport_destination_account_info.clone(), - context_state_account_authority_info.clone(), - ], - )?; - } - - Ok(Some(transfer_with_fee_proof_context)) - } else if proof_instruction_offset == 0 && !split_proof_context_state_accounts { - // interpret `account_info` as a context state account - let context_state_account_info = next_account_info(account_info_iter)?; - check_zk_token_proof_program_account(context_state_account_info.owner)?; - let context_state_account_data = context_state_account_info.data.borrow(); - let context_state = pod_from_bytes::>( - &context_state_account_data, + let fee_ciphertext_validity_proof_context = verify_and_extract_context::< + BatchedGroupedCiphertext2HandlesValidityProofData, + BatchedGroupedCiphertext2HandlesValidityProofContext, + >( + account_info_iter, + fee_ciphertext_validity_proof_instruction_offset, + sysvar_account_info, + )?; + + let range_proof_context = + verify_and_extract_context::( + account_info_iter, + range_proof_instruction_offset, + sysvar_account_info, )?; - if context_state.proof_type != ProofType::TransferWithFee.into() { - return Err(ProgramError::InvalidInstructionData); - } - - let proof_tranfer_fee_basis_points: u16 = context_state - .proof_context - .fee_parameters - .fee_rate_basis_points - .into(); - let proof_maximum_fee: u64 = context_state - .proof_context - .fee_parameters - .maximum_fee - .into(); - - // check consistency of the transfer fee parameters in the mint extension with - // what were used to generate the zkp, which is not checked in the - // `From` implementation for - // `TransferWithFeeProofContextInfo`. - if u16::from(fee_parameters.transfer_fee_basis_points) != proof_tranfer_fee_basis_points - || u64::from(fee_parameters.maximum_fee) != proof_maximum_fee - { - return Err(TokenError::FeeParametersMismatch.into()); - } - - Ok(Some(context_state.proof_context.into())) - } else { - // interpret `account_info` as sysvar - let sysvar_account_info = next_account_info(account_info_iter)?; - let zkp_instruction = - get_instruction_relative(proof_instruction_offset, sysvar_account_info)?; - let proof_context = decode_proof_instruction_context::< - TransferWithFeeData, - TransferWithFeeProofContext, - >(ProofInstruction::VerifyTransferWithFee, &zkp_instruction)?; - - let proof_tranfer_fee_basis_points: u16 = - proof_context.fee_parameters.fee_rate_basis_points.into(); - let proof_maximum_fee: u64 = proof_context.fee_parameters.maximum_fee.into(); - - // check consistency of the transfer fee parameters in the mint extension with - // what were used to generate the zkp, which is not checked in the - // `From` implementation for - // `TransferWithFeeProofContextInfo`. - if u16::from(fee_parameters.transfer_fee_basis_points) != proof_tranfer_fee_basis_points - || u64::from(fee_parameters.maximum_fee) != proof_maximum_fee - { - return Err(TokenError::FeeParametersMismatch.into()); - } - - Ok(Some((*proof_context).into())) - } -} - -/// Verify and process equality proof for [Transfer] and [TransferWithFee] -/// instructions. -fn verify_equality_proof( - account_info: &AccountInfo<'_>, -) -> Result { - check_zk_token_proof_program_account(account_info.owner)?; - let context_state_account_data = account_info.data.borrow(); - let equality_proof_context_state = pod_from_bytes::< - ProofContextState, - >(&context_state_account_data)?; - - if equality_proof_context_state.proof_type != ProofType::CiphertextCommitmentEquality.into() { - return Err(ProgramError::InvalidInstructionData); - } - - Ok(equality_proof_context_state.proof_context) -} - -/// Verify and process ciphertext validity proof for [Transfer] and -/// [TransferWithFee] instructions. -fn verify_ciphertext_validity_proof( - account_info: &AccountInfo<'_>, -) -> Result { - check_zk_token_proof_program_account(account_info.owner)?; - let context_state_account_data = account_info.data.borrow(); - let ciphertext_validity_proof_context_state = pod_from_bytes::< - ProofContextState, - >(&context_state_account_data)?; - - if ciphertext_validity_proof_context_state.proof_type - != ProofType::BatchedGroupedCiphertext2HandlesValidity.into() - { - return Err(ProgramError::InvalidInstructionData); - } - - Ok(ciphertext_validity_proof_context_state.proof_context) -} - -/// Verify and process range proof for [Transfer] instruction. -fn verify_transfer_range_proof( - account_info: &AccountInfo<'_>, -) -> Result { - check_zk_token_proof_program_account(account_info.owner)?; - let context_state_account_data = account_info.data.borrow(); - let range_proof_context_state = - pod_from_bytes::>(&context_state_account_data)?; - - if range_proof_context_state.proof_type != ProofType::BatchedRangeProofU128.into() { - return Err(ProgramError::InvalidInstructionData); - } - - Ok(range_proof_context_state.proof_context) -} - -/// Verify and process range proof for [Transfer] instruction with fee. -fn verify_transfer_with_fee_range_proof( - account_info: &AccountInfo<'_>, -) -> Result { - check_zk_token_proof_program_account(account_info.owner)?; - let context_state_account_data = account_info.data.borrow(); - let range_proof_context_state = - pod_from_bytes::>(&context_state_account_data)?; - - if range_proof_context_state.proof_type != ProofType::BatchedRangeProofU256.into() { - return Err(ProgramError::InvalidInstructionData); - } - - Ok(range_proof_context_state.proof_context) -} - -/// Verify and process fee sigma proof for [TransferWithFee] instruction. -fn verify_fee_sigma_proof( - account_info: &AccountInfo<'_>, -) -> Result { - check_zk_token_proof_program_account(account_info.owner)?; - let context_state_account_data = account_info.data.borrow(); - let fee_sigma_proof_context_state = - pod_from_bytes::>(&context_state_account_data)?; - - if fee_sigma_proof_context_state.proof_type != ProofType::FeeSigma.into() { - return Err(ProgramError::InvalidInstructionData); - } - - Ok(fee_sigma_proof_context_state.proof_context) + // The `TransferWithFeeProofContext` constructor verifies the consistency of + // the individual proof context and generates a + // `TransferWithFeeProofInfo` struct that is used to process the rest of + // the token-2022 logic. The consistency check includes verifying + // whether the fee-related zkps were generated with respect to the correct fee + // parameter that is stored in the mint extension. + let transfer_with_fee_proof_context = TransferWithFeeProofContext::verify_and_extract( + &equality_proof_context, + &transfer_amount_ciphertext_validity_proof_context, + &fee_sigma_proof_context, + &fee_ciphertext_validity_proof_context, + &range_proof_context, + fee_parameters.transfer_fee_basis_points.into(), + fee_parameters.maximum_fee.into(), + ) + .map_err(|e| -> TokenError { e.into() })?; + + Ok(transfer_with_fee_proof_context) } diff --git a/token/program-2022/src/extension/confidential_transfer_fee/account_info.rs b/token/program-2022/src/extension/confidential_transfer_fee/account_info.rs index 40a8919ae9c..ee87dd2f5a8 100644 --- a/token/program-2022/src/extension/confidential_transfer_fee/account_info.rs +++ b/token/program-2022/src/extension/confidential_transfer_fee/account_info.rs @@ -1,12 +1,12 @@ use { crate::{error::TokenError, extension::confidential_transfer_fee::EncryptedWithheldAmount}, bytemuck::{Pod, Zeroable}, - solana_zk_token_sdk::{ + solana_zk_sdk::{ encryption::{ elgamal::{ElGamalCiphertext, ElGamalKeypair, ElGamalPubkey}, pedersen::PedersenOpening, }, - instruction::ciphertext_ciphertext_equality::CiphertextCiphertextEqualityProofData, + zk_elgamal_proof_program::proof_data::ciphertext_ciphertext_equality::CiphertextCiphertextEqualityProofData, }, }; diff --git a/token/program-2022/src/extension/confidential_transfer_fee/instruction.rs b/token/program-2022/src/extension/confidential_transfer_fee/instruction.rs index 12a708624cb..aeb243303bf 100644 --- a/token/program-2022/src/extension/confidential_transfer_fee/instruction.rs +++ b/token/program-2022/src/extension/confidential_transfer_fee/instruction.rs @@ -8,14 +8,14 @@ use { check_program_account, error::TokenError, extension::confidential_transfer::{ - instruction::{ - verify_ciphertext_ciphertext_equality, CiphertextCiphertextEqualityProofData, - }, - DecryptableBalance, + instruction::CiphertextCiphertextEqualityProofData, DecryptableBalance, }, instruction::{encode_instruction, TokenInstruction}, - proof::ProofLocation, - solana_zk_token_sdk::zk_token_elgamal::pod::ElGamalPubkey, + proof::{ProofData, ProofLocation}, + solana_zk_sdk::{ + encryption::pod::elgamal::PodElGamalPubkey, + zk_elgamal_proof_program::instruction::ProofInstruction, + }, }, bytemuck::{Pod, Zeroable}, num_enum::{IntoPrimitive, TryFromPrimitive}, @@ -61,7 +61,7 @@ pub enum ConfidentialTransferFeeInstruction { /// /// In order for this instruction to be successfully processed, it must be /// accompanied by the `VerifyCiphertextCiphertextEquality` instruction - /// of the `zk_token_proof` program in the same transaction or the + /// of the `zk_elgamal_proof` program in the same transaction or the /// address of a context state account for the proof must be provided. /// /// Accounts expected by this instruction: @@ -75,7 +75,9 @@ pub enum ConfidentialTransferFeeInstruction { /// included in the same transaction or context state account if /// `VerifyCiphertextCiphertextEquality` is pre-verified into a context /// state account. - /// 3. `[signer]` The mint's `withdraw_withheld_authority`. + /// 3. `[]` (Optional) Record account if the accompanying proof is to be + /// read from a record account. + /// 4. `[signer]` The mint's `withdraw_withheld_authority`. /// /// * Multisignature owner/delegate /// 0. `[writable]` The token mint. Must include the `TransferFeeConfig` @@ -86,8 +88,10 @@ pub enum ConfidentialTransferFeeInstruction { /// included in the same transaction or context state account if /// `VerifyCiphertextCiphertextEquality` is pre-verified into a context /// state account. - /// 3. `[]` The mint's multisig `withdraw_withheld_authority`. - /// 4. ..3+M `[signer]` M signer accounts. + /// 3. `[]` (Optional) Record account if the accompanying proof is to be + /// read from a record account. + /// 4. `[]` The mint's multisig `withdraw_withheld_authority`. + /// 5. ..3+M `[signer]` M signer accounts. /// /// Data expected by this instruction: /// WithdrawWithheldTokensFromMintData @@ -122,7 +126,7 @@ pub enum ConfidentialTransferFeeInstruction { /// /// In order for this instruction to be successfully processed, it must be /// accompanied by the `VerifyWithdrawWithheldTokens` instruction of the - /// `zk_token_proof` program in the same transaction or the address of a + /// `zk_elgamal_proof` program in the same transaction or the address of a /// context state account for the proof must be provided. /// /// Accounts expected by this instruction: @@ -136,8 +140,10 @@ pub enum ConfidentialTransferFeeInstruction { /// included in the same transaction or context state account if /// `VerifyCiphertextCiphertextEquality` is pre-verified into a context /// state account. - /// 3. `[signer]` The mint's `withdraw_withheld_authority`. - /// 4. ..3+N `[writable]` The source accounts to withdraw from. + /// 3. `[]` (Optional) Record account if the accompanying proof is to be + /// read from a record account. + /// 4. `[signer]` The mint's `withdraw_withheld_authority`. + /// 5. ..3+N `[writable]` The source accounts to withdraw from. /// /// * Multisignature owner/delegate /// 0. `[]` The token mint. Must include the `TransferFeeConfig` @@ -148,9 +154,11 @@ pub enum ConfidentialTransferFeeInstruction { /// included in the same transaction or context state account if /// `VerifyCiphertextCiphertextEquality` is pre-verified into a context /// state account. - /// 3. `[]` The mint's multisig `withdraw_withheld_authority`. - /// 4. ..4+M `[signer]` M signer accounts. - /// 4+M+1. ..4+M+N `[writable]` The source accounts to withdraw from. + /// 3. `[]` (Optional) Record account if the accompanying proof is to be + /// read from a record account. + /// 4. `[]` The mint's multisig `withdraw_withheld_authority`. + /// 5. ..5+M `[signer]` M signer accounts. + /// 5+M+1. ..5+M+N `[writable]` The source accounts to withdraw from. /// /// Data expected by this instruction: /// WithdrawWithheldTokensFromAccountsData @@ -223,7 +231,7 @@ pub struct InitializeConfidentialTransferFeeConfigData { /// ElGamal public key used to encrypt withheld fees. #[cfg_attr(feature = "serde-traits", serde(with = "elgamalpubkey_fromstr"))] - pub withdraw_withheld_authority_elgamal_pubkey: ElGamalPubkey, + pub withdraw_withheld_authority_elgamal_pubkey: PodElGamalPubkey, } /// Data expected by @@ -267,7 +275,7 @@ pub fn initialize_confidential_transfer_fee_config( token_program_id: &Pubkey, mint: &Pubkey, authority: Option, - withdraw_withheld_authority_elgamal_pubkey: ElGamalPubkey, + withdraw_withheld_authority_elgamal_pubkey: PodElGamalPubkey, ) -> Result { check_program_account(token_program_id)?; let accounts = vec![AccountMeta::new(*mint, false)]; @@ -303,8 +311,11 @@ pub fn inner_withdraw_withheld_tokens_from_mint( ]; let proof_instruction_offset = match proof_data_location { - ProofLocation::InstructionOffset(proof_instruction_offset, _) => { + ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) => { accounts.push(AccountMeta::new_readonly(sysvar::instructions::id(), false)); + if let ProofData::RecordAccount(record_address, _) = proof_data { + accounts.push(AccountMeta::new_readonly(*record_address, false)); + } proof_instruction_offset.into() } ProofLocation::ContextStateAccount(context_state_account) => { @@ -366,7 +377,16 @@ pub fn withdraw_withheld_tokens_from_mint( if proof_instruction_offset != 1 { return Err(TokenError::InvalidProofInstructionOffset.into()); } - instructions.push(verify_ciphertext_ciphertext_equality(None, proof_data)); + match proof_data { + ProofData::InstructionData(data) => instructions.push( + ProofInstruction::VerifyCiphertextCiphertextEquality + .encode_verify_proof(None, data), + ), + ProofData::RecordAccount(address, offset) => instructions.push( + ProofInstruction::VerifyCiphertextCiphertextEquality + .encode_verify_proof_from_account(None, address, offset), + ), + }; }; Ok(instructions) @@ -395,8 +415,11 @@ pub fn inner_withdraw_withheld_tokens_from_accounts( ]; let proof_instruction_offset = match proof_data_location { - ProofLocation::InstructionOffset(proof_instruction_offset, _) => { + ProofLocation::InstructionOffset(proof_instruction_offset, proof_data) => { accounts.push(AccountMeta::new_readonly(sysvar::instructions::id(), false)); + if let ProofData::RecordAccount(record_address, _) = proof_data { + accounts.push(AccountMeta::new_readonly(*record_address, false)); + } proof_instruction_offset.into() } ProofLocation::ContextStateAccount(context_state_account) => { @@ -466,7 +489,16 @@ pub fn withdraw_withheld_tokens_from_accounts( if proof_instruction_offset != 1 { return Err(TokenError::InvalidProofInstructionOffset.into()); } - instructions.push(verify_ciphertext_ciphertext_equality(None, proof_data)); + match proof_data { + ProofData::InstructionData(data) => instructions.push( + ProofInstruction::VerifyCiphertextCiphertextEquality + .encode_verify_proof(None, data), + ), + ProofData::RecordAccount(address, offset) => instructions.push( + ProofInstruction::VerifyCiphertextCiphertextEquality + .encode_verify_proof_from_account(None, address, offset), + ), + }; }; Ok(instructions) diff --git a/token/program-2022/src/extension/confidential_transfer_fee/mod.rs b/token/program-2022/src/extension/confidential_transfer_fee/mod.rs index 64576b9a720..f48eb2dceb1 100644 --- a/token/program-2022/src/extension/confidential_transfer_fee/mod.rs +++ b/token/program-2022/src/extension/confidential_transfer_fee/mod.rs @@ -5,8 +5,9 @@ use { }, bytemuck::{Pod, Zeroable}, solana_program::entrypoint::ProgramResult, - solana_zk_token_sdk::zk_token_elgamal::pod::{ElGamalCiphertext, ElGamalPubkey, FeeEncryption}, + solana_zk_sdk::encryption::pod::elgamal::{PodElGamalCiphertext, PodElGamalPubkey}, spl_pod::{optional_keys::OptionalNonZeroPubkey, primitives::PodBool}, + spl_token_confidential_transfer_proof_extraction::encryption::PodFeeCiphertext, }; /// Confidential transfer fee extension instructions @@ -21,9 +22,9 @@ pub mod processor; pub mod account_info; /// ElGamal ciphertext containing a transfer fee -pub type EncryptedFee = FeeEncryption; +pub type EncryptedFee = PodFeeCiphertext; /// ElGamal ciphertext containing a withheld fee in an account -pub type EncryptedWithheldAmount = ElGamalCiphertext; +pub type EncryptedWithheldAmount = PodElGamalCiphertext; /// Confidential transfer fee extension data for mints #[repr(C)] @@ -38,7 +39,7 @@ pub struct ConfidentialTransferFeeConfig { /// key has the ability to decode any withheld fee amount that are /// associated with accounts. When combined with the fee parameters, the /// withheld fee amounts can reveal information about transfer amounts. - pub withdraw_withheld_authority_elgamal_pubkey: ElGamalPubkey, + pub withdraw_withheld_authority_elgamal_pubkey: PodElGamalPubkey, /// If `false`, the harvest of withheld tokens to mint is rejected. pub harvest_to_mint_enabled: PodBool, diff --git a/token/program-2022/src/extension/confidential_transfer_fee/processor.rs b/token/program-2022/src/extension/confidential_transfer_fee/processor.rs index 4fa079ec0b4..82549130f0e 100644 --- a/token/program-2022/src/extension/confidential_transfer_fee/processor.rs +++ b/token/program-2022/src/extension/confidential_transfer_fee/processor.rs @@ -1,16 +1,14 @@ // Remove feature once zk ops syscalls are enabled on all networks #[cfg(feature = "zk-ops")] -use solana_zk_token_sdk::zk_token_elgamal::ops as syscall; +use spl_token_confidential_transfer_ciphertext_arithmetic as ciphertext_arithmetic; use { crate::{ - check_program_account, check_zk_token_proof_program_account, + check_program_account, error::TokenError, extension::{ confidential_transfer::{ instruction::{ - CiphertextCiphertextEqualityProofContext, - CiphertextCiphertextEqualityProofData, ProofContextState, ProofInstruction, - ProofType, + CiphertextCiphertextEqualityProofContext, CiphertextCiphertextEqualityProofData, }, ConfidentialTransferAccount, DecryptableBalance, }, @@ -29,8 +27,8 @@ use { instruction::{decode_instruction_data, decode_instruction_type}, pod::{PodAccount, PodMint}, processor::Processor, - proof::decode_proof_instruction_context, - solana_zk_token_sdk::zk_token_elgamal::pod::ElGamalPubkey, + proof::verify_and_extract_context, + solana_zk_sdk::encryption::pod::elgamal::PodElGamalPubkey, }, bytemuck::Zeroable, solana_program::{ @@ -39,16 +37,15 @@ use { msg, program_error::ProgramError, pubkey::Pubkey, - sysvar::instructions::get_instruction_relative, }, - spl_pod::{bytemuck::pod_from_bytes, optional_keys::OptionalNonZeroPubkey}, + spl_pod::optional_keys::OptionalNonZeroPubkey, }; /// Processes an [InitializeConfidentialTransferFeeConfig] instruction. fn process_initialize_confidential_transfer_fee_config( accounts: &[AccountInfo], authority: &OptionalNonZeroPubkey, - withdraw_withheld_authority_elgamal_pubkey: &ElGamalPubkey, + withdraw_withheld_authority_elgamal_pubkey: &PodElGamalPubkey, ) -> ProgramResult { let account_info_iter = &mut accounts.iter(); let mint_account_info = next_account_info(account_info_iter)?; @@ -79,10 +76,10 @@ fn process_withdraw_withheld_tokens_from_mint( // zero-knowledge proof certifies that the exact withheld amount is credited to // the destination account. - let proof_context = verify_ciphertext_ciphertext_equality_proof( - next_account_info(account_info_iter)?, - proof_instruction_offset, - )?; + let proof_context = verify_and_extract_context::< + CiphertextCiphertextEqualityProofData, + CiphertextCiphertextEqualityProofContext, + >(account_info_iter, proof_instruction_offset, None)?; let authority_info = next_account_info(account_info_iter)?; let authority_info_data_len = authority_info.data_len(); @@ -137,28 +134,27 @@ fn process_withdraw_withheld_tokens_from_mint( // Check that the withdraw authority ElGamal public key associated with the mint // is consistent with what was actually used to generate the zkp. - if proof_context.source_pubkey + if proof_context.first_pubkey != confidential_transfer_fee_config.withdraw_withheld_authority_elgamal_pubkey { return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); } // Check that the ElGamal public key associated with the destination account is // consistent with what was actually used to generate the zkp. - if proof_context.destination_pubkey != destination_confidential_transfer_account.elgamal_pubkey - { + if proof_context.second_pubkey != destination_confidential_transfer_account.elgamal_pubkey { return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); } // Check that the withheld amount ciphertext is consistent with the ciphertext // data that was actually used to generate the zkp. - if proof_context.source_ciphertext != confidential_transfer_fee_config.withheld_amount { + if proof_context.first_ciphertext != confidential_transfer_fee_config.withheld_amount { return Err(TokenError::ConfidentialTransferBalanceMismatch.into()); } // The proof data contains the mint withheld amount encrypted under the // destination ElGamal pubkey. Add this amount to the available balance. - destination_confidential_transfer_account.available_balance = syscall::add( + destination_confidential_transfer_account.available_balance = ciphertext_arithmetic::add( &destination_confidential_transfer_account.available_balance, - &proof_context.destination_ciphertext, + &proof_context.second_ciphertext, ) .ok_or(ProgramError::InvalidInstructionData)?; @@ -171,39 +167,6 @@ fn process_withdraw_withheld_tokens_from_mint( Ok(()) } -/// Verify zero-knowledge proof needed for a [WithdrawWithheldTokensFromMint] -/// instruction or a `[WithdrawWithheldTokensFromAccounts]` and return the -/// corresponding proof context. -fn verify_ciphertext_ciphertext_equality_proof( - account_info: &AccountInfo<'_>, - proof_instruction_offset: i64, -) -> Result { - if proof_instruction_offset == 0 { - // interpret `account_info` as a context state account - check_zk_token_proof_program_account(account_info.owner)?; - let context_state_account_data = account_info.data.borrow(); - let context_state = pod_from_bytes::< - ProofContextState, - >(&context_state_account_data)?; - - if context_state.proof_type != ProofType::CiphertextCiphertextEquality.into() { - return Err(ProgramError::InvalidInstructionData); - } - - Ok(context_state.proof_context) - } else { - // interpret `account_info` as a sysvar - let zkp_instruction = get_instruction_relative(proof_instruction_offset, account_info)?; - Ok(*decode_proof_instruction_context::< - CiphertextCiphertextEqualityProofData, - CiphertextCiphertextEqualityProofContext, - >( - ProofInstruction::VerifyCiphertextCiphertextEquality, - &zkp_instruction, - )?) - } -} - /// Processes a [WithdrawWithheldTokensFromAccounts] instruction. #[cfg(feature = "zk-ops")] fn process_withdraw_withheld_tokens_from_accounts( @@ -219,10 +182,10 @@ fn process_withdraw_withheld_tokens_from_accounts( // zero-knowledge proof certifies that the exact aggregate withheld amount is // credited to the destination account. - let proof_context = verify_ciphertext_ciphertext_equality_proof( - next_account_info(account_info_iter)?, - proof_instruction_offset, - )?; + let proof_context = verify_and_extract_context::< + CiphertextCiphertextEqualityProofData, + CiphertextCiphertextEqualityProofContext, + >(account_info_iter, proof_instruction_offset, None)?; let authority_info = next_account_info(account_info_iter)?; let authority_info_data_len = authority_info.data_len(); @@ -268,7 +231,7 @@ fn process_withdraw_withheld_tokens_from_accounts( .get_extension_mut::() .map_err(|_| TokenError::InvalidState)?; - aggregate_withheld_amount = syscall::add( + aggregate_withheld_amount = ciphertext_arithmetic::add( &aggregate_withheld_amount, &destination_confidential_transfer_fee_amount.withheld_amount, ) @@ -279,9 +242,11 @@ fn process_withdraw_withheld_tokens_from_accounts( } else { match harvest_from_account(mint_account_info.key, account_info) { Ok(encrypted_withheld_amount) => { - aggregate_withheld_amount = - syscall::add(&aggregate_withheld_amount, &encrypted_withheld_amount) - .ok_or(ProgramError::InvalidInstructionData)?; + aggregate_withheld_amount = ciphertext_arithmetic::add( + &aggregate_withheld_amount, + &encrypted_withheld_amount, + ) + .ok_or(ProgramError::InvalidInstructionData)?; } Err(e) => { msg!("Error harvesting from {}: {}", account_info.key, e); @@ -302,29 +267,28 @@ fn process_withdraw_withheld_tokens_from_accounts( // mint is consistent with what was actually used to generate the zkp. let confidential_transfer_fee_config = mint.get_extension_mut::()?; - if proof_context.source_pubkey + if proof_context.first_pubkey != confidential_transfer_fee_config.withdraw_withheld_authority_elgamal_pubkey { return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); } // Checks that the ElGamal public key associated with the destination account is // consistent with what was actually used to generate the zkp. - if proof_context.destination_pubkey != destination_confidential_transfer_account.elgamal_pubkey - { + if proof_context.second_pubkey != destination_confidential_transfer_account.elgamal_pubkey { return Err(TokenError::ConfidentialTransferElGamalPubkeyMismatch.into()); } // Checks that the withheld amount ciphertext is consistent with the ciphertext // data that was actually used to generate the zkp. - if proof_context.source_ciphertext != aggregate_withheld_amount { + if proof_context.first_ciphertext != aggregate_withheld_amount { return Err(TokenError::ConfidentialTransferBalanceMismatch.into()); } // The proof data contains the mint withheld amount encrypted under the // destination ElGamal pubkey. This amount is added to the destination // available balance. - destination_confidential_transfer_account.available_balance = syscall::add( + destination_confidential_transfer_account.available_balance = ciphertext_arithmetic::add( &destination_confidential_transfer_account.available_balance, - &proof_context.destination_ciphertext, + &proof_context.second_ciphertext, ) .ok_or(ProgramError::InvalidInstructionData)?; @@ -381,7 +345,7 @@ fn process_harvest_withheld_tokens_to_mint(accounts: &[AccountInfo]) -> ProgramR for token_account_info in token_account_infos { match harvest_from_account(mint_account_info.key, token_account_info) { Ok(withheld_amount) => { - let new_mint_withheld_amount = syscall::add( + let new_mint_withheld_amount = ciphertext_arithmetic::add( &confidential_transfer_fee_mint.withheld_amount, &withheld_amount, ) diff --git a/token/program-2022/src/lib.rs b/token/program-2022/src/lib.rs index 7561c6b5d8e..573fd0f18d7 100644 --- a/token/program-2022/src/lib.rs +++ b/token/program-2022/src/lib.rs @@ -27,7 +27,7 @@ mod entrypoint; use solana_program::{ entrypoint::ProgramResult, program_error::ProgramError, pubkey::Pubkey, system_program, }; -pub use {solana_program, solana_zk_token_sdk}; +pub use {solana_program, solana_zk_sdk}; /// Convert the UI representation of a token amount (using the decimals field /// defined in its mint) to the raw amount @@ -112,10 +112,12 @@ pub fn check_spl_token_program_account(spl_token_program_id: &Pubkey) -> Program Ok(()) } -/// Checks that the supplied program ID is correct for the ZK Token proof +/// Checks that the supplied program ID is correct for the ZK ElGamal proof /// program -pub fn check_zk_token_proof_program_account(zk_token_proof_program_id: &Pubkey) -> ProgramResult { - if zk_token_proof_program_id != &solana_zk_token_sdk::zk_token_proof_program::id() { +pub fn check_zk_elgamal_proof_program_account( + zk_elgamal_proof_program_id: &Pubkey, +) -> ProgramResult { + if zk_elgamal_proof_program_id != &solana_zk_sdk::zk_elgamal_proof_program::id() { return Err(ProgramError::IncorrectProgramId); } Ok(()) diff --git a/token/program-2022/src/proof.rs b/token/program-2022/src/proof.rs index fbb9b525a10..1bebfabe827 100644 --- a/token/program-2022/src/proof.rs +++ b/token/program-2022/src/proof.rs @@ -1,31 +1,69 @@ -//! Helper for processing instruction data from ZK Token proof program +//! Helper for processing instruction data from ZK ElGamal proof program use { + crate::check_zk_elgamal_proof_program_account, bytemuck::Pod, - solana_program::{instruction::Instruction, msg, program_error::ProgramError, pubkey::Pubkey}, - solana_zk_token_sdk::{ - instruction::ZkProofData, zk_token_proof_instruction::ProofInstruction, - zk_token_proof_program, + solana_program::{ + account_info::{next_account_info, AccountInfo}, + instruction::Instruction, + msg, + program_error::ProgramError, + pubkey::Pubkey, + sysvar::instructions::get_instruction_relative, }, - std::num::NonZeroI8, + solana_zk_sdk::zk_elgamal_proof_program::{ + self, + instruction::ProofInstruction, + proof_data::{ProofType, ZkProofData}, + state::ProofContextState, + }, + spl_pod::bytemuck::pod_from_bytes, + std::{num::NonZeroI8, slice::Iter}, }; +/// If a proof is to be read from a record account, the proof instruction data +/// must be 5 bytes: 1 byte for the proof type and 4 bytes for the u32 offset +const INSTRUCTION_DATA_LENGTH_WITH_RECORD_ACCOUNT: usize = 5; + /// Decodes the proof context data associated with a zero-knowledge proof /// instruction. pub fn decode_proof_instruction_context, U: Pod>( + account_info_iter: &mut Iter<'_, AccountInfo<'_>>, expected: ProofInstruction, instruction: &Instruction, -) -> Result<&U, ProgramError> { - if instruction.program_id != zk_token_proof_program::id() +) -> Result { + if instruction.program_id != zk_elgamal_proof_program::id() || ProofInstruction::instruction_type(&instruction.data) != Some(expected) { msg!("Unexpected proof instruction"); return Err(ProgramError::InvalidInstructionData); } - ProofInstruction::proof_data::(&instruction.data) - .map(ZkProofData::context_data) - .ok_or(ProgramError::InvalidInstructionData) + // If the instruction data size is exactly 5 bytes, then interpret it as an + // offset byte for a record account. This behavior is identical to that of + // the ZK ElGamal proof program. + if instruction.data.len() == INSTRUCTION_DATA_LENGTH_WITH_RECORD_ACCOUNT { + let record_account = next_account_info(account_info_iter)?; + + // first byte is the proof type + let start_offset = u32::from_le_bytes(instruction.data[1..].try_into().unwrap()) as usize; + let end_offset = start_offset + .checked_add(std::mem::size_of::()) + .ok_or(ProgramError::InvalidAccountData)?; + + let record_account_data = record_account.data.borrow(); + let raw_proof_data = record_account_data + .get(start_offset..end_offset) + .ok_or(ProgramError::AccountDataTooSmall)?; + + bytemuck::try_from_bytes::(raw_proof_data) + .map(|proof_data| *ZkProofData::context_data(proof_data)) + .map_err(|_| ProgramError::InvalidAccountData) + } else { + ProofInstruction::proof_data::(&instruction.data) + .map(|proof_data| *ZkProofData::context_data(proof_data)) + .ok_or(ProgramError::InvalidInstructionData) + } } /// A proof location type meant to be used for arguments to instruction @@ -34,11 +72,102 @@ pub fn decode_proof_instruction_context, U: Pod>( pub enum ProofLocation<'a, T> { /// The proof is included in the same transaction of a corresponding /// token-2022 instruction. - InstructionOffset(NonZeroI8, &'a T), + InstructionOffset(NonZeroI8, ProofData<'a, T>), /// The proof is pre-verified into a context state account. ContextStateAccount(&'a Pubkey), } +impl<'a, T> ProofLocation<'a, T> { + /// Returns true if the proof location is an instruction offset + pub fn is_instruction_offset(&self) -> bool { + match self { + Self::InstructionOffset(_, _) => true, + Self::ContextStateAccount(_) => false, + } + } +} + +/// A proof data type to distinguish between proof data included as part of +/// zk-token proof instruction data and proof data stored in a record account. +#[derive(Clone, Copy)] +pub enum ProofData<'a, T> { + /// The proof data + InstructionData(&'a T), + /// The address of a record account containing the proof data and its byte + /// offset + RecordAccount(&'a Pubkey, u32), +} + +/// Verify zero-knowledge proof and return the corresponding proof context. +pub fn verify_and_extract_context<'a, T: Pod + ZkProofData, U: Pod>( + account_info_iter: &mut Iter<'_, AccountInfo<'a>>, + proof_instruction_offset: i64, + sysvar_account_info: Option<&'_ AccountInfo<'a>>, +) -> Result { + if proof_instruction_offset == 0 { + // interpret `account_info` as a context state account + let context_state_account_info = next_account_info(account_info_iter)?; + check_zk_elgamal_proof_program_account(context_state_account_info.owner)?; + let context_state_account_data = context_state_account_info.data.borrow(); + let context_state = pod_from_bytes::>(&context_state_account_data)?; + + if context_state.proof_type != T::PROOF_TYPE.into() { + return Err(ProgramError::InvalidInstructionData); + } + + Ok(context_state.proof_context) + } else { + // if sysvar account is not provided, then get the sysvar account + let sysvar_account_info = if let Some(sysvar_account_info) = sysvar_account_info { + sysvar_account_info + } else { + next_account_info(account_info_iter)? + }; + let zkp_instruction = + get_instruction_relative(proof_instruction_offset, sysvar_account_info)?; + let expected_proof_type = zk_proof_type_to_instruction(T::PROOF_TYPE)?; + Ok(decode_proof_instruction_context::( + account_info_iter, + expected_proof_type, + &zkp_instruction, + )?) + } +} + +/// Converts a zk proof type to a corresponding ZK ElGamal proof program +/// instruction that verifies the proof. +pub fn zk_proof_type_to_instruction( + proof_type: ProofType, +) -> Result { + match proof_type { + ProofType::ZeroCiphertext => Ok(ProofInstruction::VerifyZeroCiphertext), + ProofType::CiphertextCiphertextEquality => { + Ok(ProofInstruction::VerifyCiphertextCiphertextEquality) + } + ProofType::PubkeyValidity => Ok(ProofInstruction::VerifyPubkeyValidity), + ProofType::BatchedRangeProofU64 => Ok(ProofInstruction::VerifyBatchedRangeProofU64), + ProofType::BatchedRangeProofU128 => Ok(ProofInstruction::VerifyBatchedRangeProofU128), + ProofType::BatchedRangeProofU256 => Ok(ProofInstruction::VerifyBatchedRangeProofU256), + ProofType::CiphertextCommitmentEquality => { + Ok(ProofInstruction::VerifyCiphertextCommitmentEquality) + } + ProofType::GroupedCiphertext2HandlesValidity => { + Ok(ProofInstruction::VerifyGroupedCiphertext2HandlesValidity) + } + ProofType::BatchedGroupedCiphertext2HandlesValidity => { + Ok(ProofInstruction::VerifyBatchedGroupedCiphertext2HandlesValidity) + } + ProofType::PercentageWithCap => Ok(ProofInstruction::VerifyPercentageWithCap), + ProofType::GroupedCiphertext3HandlesValidity => { + Ok(ProofInstruction::VerifyGroupedCiphertext3HandlesValidity) + } + ProofType::BatchedGroupedCiphertext3HandlesValidity => { + Ok(ProofInstruction::VerifyBatchedGroupedCiphertext3HandlesValidity) + } + ProofType::Uninitialized => Err(ProgramError::InvalidInstructionData), + } +} + /// Instruction options for when using split context state accounts #[derive(Clone, Copy)] pub struct SplitContextStateAccountsConfig { diff --git a/token/program-2022/src/serialization.rs b/token/program-2022/src/serialization.rs index 9287caacab8..b246f1abc80 100644 --- a/token/program-2022/src/serialization.rs +++ b/token/program-2022/src/serialization.rs @@ -6,22 +6,6 @@ use { serde::de::Error, }; -/// helper function to convert base64 encoded string into a bytes array -fn base64_to_bytes(v: &str) -> Result<[u8; N], E> { - let bytes = BASE64_STANDARD.decode(v).map_err(Error::custom)?; - - if bytes.len() != N { - return Err(Error::custom(format!( - "Length of base64 decoded bytes is not {}", - N - ))); - } - - let mut array = [0; N]; - array.copy_from_slice(&bytes[0..N]); - Ok(array) -} - /// helper function to ser/deser COption wrapped values pub mod coption_fromstr { use { @@ -106,14 +90,12 @@ pub mod aeciphertext_fromstr { de::{Error, Visitor}, Deserializer, Serializer, }, - solana_zk_token_sdk::zk_token_elgamal::pod::AeCiphertext, - std::fmt, + solana_zk_sdk::encryption::pod::auth_encryption::PodAeCiphertext, + std::{fmt, str::FromStr}, }; - const AE_CIPHERTEXT_LEN: usize = 36; - /// serialize AeCiphertext values supporting Display trait - pub fn serialize(x: &AeCiphertext, s: S) -> Result + pub fn serialize(x: &PodAeCiphertext, s: S) -> Result where S: Serializer, { @@ -123,7 +105,7 @@ pub mod aeciphertext_fromstr { struct AeCiphertextVisitor; impl<'de> Visitor<'de> for AeCiphertextVisitor { - type Value = AeCiphertext; + type Value = PodAeCiphertext; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a FromStr type") @@ -133,13 +115,12 @@ pub mod aeciphertext_fromstr { where E: Error, { - let array = super::base64_to_bytes::(v)?; - Ok(AeCiphertext(array)) + FromStr::from_str(v).map_err(Error::custom) } } /// deserialize AeCiphertext values from str - pub fn deserialize<'de, D>(d: D) -> Result + pub fn deserialize<'de, D>(d: D) -> Result where D: Deserializer<'de>, { @@ -154,14 +135,12 @@ pub mod elgamalpubkey_fromstr { de::{Error, Visitor}, Deserializer, Serializer, }, - solana_zk_token_sdk::zk_token_elgamal::pod::ElGamalPubkey, - std::fmt, + solana_zk_sdk::encryption::pod::elgamal::PodElGamalPubkey, + std::{fmt, str::FromStr}, }; - const ELGAMAL_PUBKEY_LEN: usize = 32; - /// serialize ElGamalPubkey values supporting Display trait - pub fn serialize(x: &ElGamalPubkey, s: S) -> Result + pub fn serialize(x: &PodElGamalPubkey, s: S) -> Result where S: Serializer, { @@ -171,7 +150,7 @@ pub mod elgamalpubkey_fromstr { struct ElGamalPubkeyVisitor; impl<'de> Visitor<'de> for ElGamalPubkeyVisitor { - type Value = ElGamalPubkey; + type Value = PodElGamalPubkey; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a FromStr type") @@ -181,65 +160,15 @@ pub mod elgamalpubkey_fromstr { where E: Error, { - let array = super::base64_to_bytes::(v)?; - Ok(ElGamalPubkey(array)) + FromStr::from_str(v).map_err(Error::custom) } } /// deserialize ElGamalPubkey values from str - pub fn deserialize<'de, D>(d: D) -> Result + pub fn deserialize<'de, D>(d: D) -> Result where D: Deserializer<'de>, { d.deserialize_str(ElGamalPubkeyVisitor) } } - -/// helper to ser/deser pod::DecryptHandle values -pub mod decrypthandle_fromstr { - use { - base64::{prelude::BASE64_STANDARD, Engine}, - serde::{ - de::{Error, Visitor}, - Deserializer, Serializer, - }, - solana_zk_token_sdk::zk_token_elgamal::pod::DecryptHandle, - std::fmt, - }; - - const DECRYPT_HANDLE_LEN: usize = 32; - - /// Serialize a decrypt handle as a base64 string - pub fn serialize(x: &DecryptHandle, s: S) -> Result - where - S: Serializer, - { - s.serialize_str(&BASE64_STANDARD.encode(x.0)) - } - - struct DecryptHandleVisitor; - - impl<'de> Visitor<'de> for DecryptHandleVisitor { - type Value = DecryptHandle; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("a FromStr type") - } - - fn visit_str(self, v: &str) -> Result - where - E: Error, - { - let array = super::base64_to_bytes::(v)?; - Ok(DecryptHandle(array)) - } - } - - /// Deserialize a DecryptHandle from a base64 string - pub fn deserialize<'de, D>(d: D) -> Result - where - D: Deserializer<'de>, - { - d.deserialize_str(DecryptHandleVisitor) - } -} diff --git a/token/program-2022/tests/serialization.rs b/token/program-2022/tests/serialization.rs index 9645daadb83..1d8d3b5021f 100644 --- a/token/program-2022/tests/serialization.rs +++ b/token/program-2022/tests/serialization.rs @@ -1,13 +1,16 @@ #![cfg(feature = "serde-traits")] use { + base64::{engine::general_purpose::STANDARD, Engine}, solana_program::program_option::COption, solana_sdk::pubkey::Pubkey, spl_pod::optional_keys::{OptionalNonZeroElGamalPubkey, OptionalNonZeroPubkey}, spl_token_2022::{ extension::confidential_transfer, instruction, - solana_zk_token_sdk::zk_token_elgamal::pod::{AeCiphertext, ElGamalPubkey}, + solana_zk_sdk::encryption::pod::{ + auth_encryption::PodAeCiphertext, elgamal::PodElGamalPubkey, + }, }, std::str::FromStr, }; @@ -51,10 +54,12 @@ fn serde_instruction_optional_nonzero_pubkeys_podbool() { Some(Pubkey::from_str("4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM").unwrap()); let authority: OptionalNonZeroPubkey = authority_option.try_into().unwrap(); - let elgamal_pubkey_pod_option: Option = Some(ElGamalPubkey([ + let pubkey_string = STANDARD.encode([ 162, 23, 108, 36, 130, 143, 18, 219, 196, 134, 242, 145, 179, 49, 229, 193, 74, 64, 3, 158, 68, 235, 124, 88, 247, 144, 164, 254, 228, 12, 173, 85, - ])); + ]); + let elgamal_pubkey_pod_option = Some(FromStr::from_str(&pubkey_string).unwrap()); + let auditor_elgamal_pubkey: OptionalNonZeroElGamalPubkey = elgamal_pubkey_pod_option.try_into().unwrap(); @@ -105,10 +110,11 @@ fn serde_instruction_optional_nonzero_pubkeys_podbool_with_none() { #[test] fn serde_instruction_decryptable_balance_podu64() { - let decryptable_zero_balance = AeCiphertext([ + let ciphertext_string = STANDARD.encode([ 56, 22, 102, 48, 112, 106, 58, 25, 25, 244, 194, 217, 73, 137, 73, 38, 24, 26, 36, 25, 235, 234, 68, 181, 11, 82, 170, 163, 89, 205, 113, 160, 55, 16, 35, 151, ]); + let decryptable_zero_balance = FromStr::from_str(&ciphertext_string).unwrap(); let inst = confidential_transfer::instruction::ConfigureAccountInstructionData { decryptable_zero_balance, @@ -131,10 +137,11 @@ fn serde_instruction_decryptable_balance_podu64() { fn serde_instruction_elgamal_pubkey() { use spl_token_2022::extension::confidential_transfer_fee::instruction::InitializeConfidentialTransferFeeConfigData; - let withdraw_withheld_authority_elgamal_pubkey = ElGamalPubkey([ + let pubkey_string = STANDARD.encode([ 162, 23, 108, 36, 130, 143, 18, 219, 196, 134, 242, 145, 179, 49, 229, 193, 74, 64, 3, 158, 68, 235, 124, 88, 247, 144, 164, 254, 228, 12, 173, 85, ]); + let withdraw_withheld_authority_elgamal_pubkey = FromStr::from_str(&pubkey_string).unwrap(); let inst = InitializeConfidentialTransferFeeConfigData { authority: OptionalNonZeroPubkey::default(), diff --git a/token/program/Cargo.toml b/token/program/Cargo.toml index fbc257511c4..50d5de2320f 100644 --- a/token/program/Cargo.toml +++ b/token/program/Cargo.toml @@ -14,10 +14,10 @@ test-sbf = [] [dependencies] arrayref = "0.3.8" -bytemuck = "1.16.1" +bytemuck = "1.17.0" num-derive = "0.4" num-traits = "0.2" -num_enum = "0.7.2" +num_enum = "0.7.3" solana-program = "2.0.3" thiserror = "1.0" diff --git a/token/transfer-hook/cli/Cargo.toml b/token/transfer-hook/cli/Cargo.toml index 2a27ea99b3f..29871e7e1be 100644 --- a/token/transfer-hook/cli/Cargo.toml +++ b/token/transfer-hook/cli/Cargo.toml @@ -22,8 +22,8 @@ spl-transfer-hook-interface = { version = "0.7.0", path = "../interface" } strum = "0.26" strum_macros = "0.26" tokio = { version = "1", features = ["full"] } -serde = { version = "1.0.204", features = ["derive"] } -serde_json = "1.0.121" +serde = { version = "1.0.208", features = ["derive"] } +serde_json = "1.0.125" serde_yaml = "0.9.34" [dev-dependencies] diff --git a/token/transfer-hook/interface/Cargo.toml b/token/transfer-hook/interface/Cargo.toml index c120069d916..80f4e5c3ae8 100644 --- a/token/transfer-hook/interface/Cargo.toml +++ b/token/transfer-hook/interface/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" [dependencies] arrayref = "0.3.8" -bytemuck = { version = "1.16.1", features = ["derive"] } +bytemuck = { version = "1.17.0", features = ["derive"] } solana-program = "2.0.3" spl-discriminator = { version = "0.3.0" , path = "../../../libraries/discriminator" } spl-program-error = { version = "0.5.0" , path = "../../../libraries/program-error" } @@ -21,7 +21,7 @@ spl-pod = { version = "0.3.0", path = "../../../libraries/pod" } crate-type = ["cdylib", "lib"] [dev-dependencies] -tokio = { version = "1.39.2", features = ["full"] } +tokio = { version = "1.39.3", features = ["full"] } [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/token/transfer-hook/interface/src/instruction.rs b/token/transfer-hook/interface/src/instruction.rs index 39639d5d646..88880546450 100644 --- a/token/transfer-hook/interface/src/instruction.rs +++ b/token/transfer-hook/interface/src/instruction.rs @@ -25,9 +25,9 @@ pub enum TransferHookInstruction { /// 1. `[]` Token mint /// 2. `[]` Destination account /// 3. `[]` Source account's owner/delegate - /// 4. `[]` Validation account - /// 5..5+M `[]` `M` additional accounts, written in validation account - /// data + /// 4. `[]` (Optional) Validation account + /// 5..5+M `[]` `M` optional additional accounts, written in validation + /// account data Execute { /// Amount of tokens to transfer amount: u64, @@ -165,9 +165,11 @@ pub fn execute_with_extra_account_metas( mint_pubkey, destination_pubkey, authority_pubkey, - validate_state_pubkey, amount, ); + instruction + .accounts + .push(AccountMeta::new_readonly(*validate_state_pubkey, false)); instruction.accounts.extend_from_slice(additional_accounts); instruction } @@ -180,7 +182,6 @@ pub fn execute( mint_pubkey: &Pubkey, destination_pubkey: &Pubkey, authority_pubkey: &Pubkey, - validate_state_pubkey: &Pubkey, amount: u64, ) -> Instruction { let data = TransferHookInstruction::Execute { amount }.pack(); @@ -189,7 +190,6 @@ pub fn execute( AccountMeta::new_readonly(*mint_pubkey, false), AccountMeta::new_readonly(*destination_pubkey, false), AccountMeta::new_readonly(*authority_pubkey, false), - AccountMeta::new_readonly(*validate_state_pubkey, false), ]; Instruction { program_id: *program_id, diff --git a/token/transfer-hook/interface/src/offchain.rs b/token/transfer-hook/interface/src/offchain.rs index 2d3b7bbdd39..50c8c57e4b7 100644 --- a/token/transfer-hook/interface/src/offchain.rs +++ b/token/transfer-hook/interface/src/offchain.rs @@ -85,9 +85,11 @@ where mint_pubkey, destination_pubkey, authority_pubkey, - &validate_state_pubkey, amount, ); + execute_instruction + .accounts + .push(AccountMeta::new_readonly(validate_state_pubkey, false)); ExtraAccountMetaList::add_to_instruction::( &mut execute_instruction, diff --git a/token/transfer-hook/interface/src/onchain.rs b/token/transfer-hook/interface/src/onchain.rs index 41c2ec1b27a..1e332579e80 100644 --- a/token/transfer-hook/interface/src/onchain.rs +++ b/token/transfer-hook/interface/src/onchain.rs @@ -23,34 +23,36 @@ pub fn invoke_execute<'a>( additional_accounts: &[AccountInfo<'a>], amount: u64, ) -> ProgramResult { - let validation_pubkey = get_extra_account_metas_address(mint_info.key, program_id); - let validation_info = additional_accounts - .iter() - .find(|&x| *x.key == validation_pubkey) - .ok_or(TransferHookError::IncorrectAccount)?; let mut cpi_instruction = instruction::execute( program_id, source_info.key, mint_info.key, destination_info.key, authority_info.key, - &validation_pubkey, amount, ); - let mut cpi_account_infos = vec![ - source_info, - mint_info, - destination_info, - authority_info, - validation_info.clone(), - ]; - ExtraAccountMetaList::add_to_cpi_instruction::( - &mut cpi_instruction, - &mut cpi_account_infos, - &validation_info.try_borrow_data()?, - additional_accounts, - )?; + let validation_pubkey = get_extra_account_metas_address(mint_info.key, program_id); + + let mut cpi_account_infos = vec![source_info, mint_info, destination_info, authority_info]; + + if let Some(validation_info) = additional_accounts + .iter() + .find(|&x| *x.key == validation_pubkey) + { + cpi_instruction + .accounts + .push(AccountMeta::new_readonly(validation_pubkey, false)); + cpi_account_infos.push(validation_info.clone()); + + ExtraAccountMetaList::add_to_cpi_instruction::( + &mut cpi_instruction, + &mut cpi_account_infos, + &validation_info.try_borrow_data()?, + additional_accounts, + )?; + } + invoke(&cpi_instruction, &cpi_account_infos) } @@ -76,55 +78,60 @@ pub fn add_extra_accounts_for_execute_cpi<'a>( additional_accounts: &[AccountInfo<'a>], ) -> ProgramResult { let validate_state_pubkey = get_extra_account_metas_address(mint_info.key, program_id); - let validate_state_info = additional_accounts - .iter() - .find(|&x| *x.key == validate_state_pubkey) - .ok_or(TransferHookError::IncorrectAccount)?; let program_info = additional_accounts .iter() .find(|&x| x.key == program_id) .ok_or(TransferHookError::IncorrectAccount)?; - let mut execute_instruction = instruction::execute( - program_id, - source_info.key, - mint_info.key, - destination_info.key, - authority_info.key, - &validate_state_pubkey, - amount, - ); - let mut execute_account_infos = vec![ - source_info, - mint_info, - destination_info, - authority_info, - validate_state_info.clone(), - ]; - - ExtraAccountMetaList::add_to_cpi_instruction::( - &mut execute_instruction, - &mut execute_account_infos, - &validate_state_info.try_borrow_data()?, - additional_accounts, - )?; - - // Add only the extra accounts resolved from the validation state - cpi_instruction - .accounts - .extend_from_slice(&execute_instruction.accounts[5..]); - cpi_account_infos.extend_from_slice(&execute_account_infos[5..]); + if let Some(validate_state_info) = additional_accounts + .iter() + .find(|&x| *x.key == validate_state_pubkey) + { + let mut execute_instruction = instruction::execute( + program_id, + source_info.key, + mint_info.key, + destination_info.key, + authority_info.key, + amount, + ); + execute_instruction + .accounts + .push(AccountMeta::new_readonly(validate_state_pubkey, false)); + let mut execute_account_infos = vec![ + source_info, + mint_info, + destination_info, + authority_info, + validate_state_info.clone(), + ]; - // Add the program id and validation state account + ExtraAccountMetaList::add_to_cpi_instruction::( + &mut execute_instruction, + &mut execute_account_infos, + &validate_state_info.try_borrow_data()?, + additional_accounts, + )?; + + // Add only the extra accounts resolved from the validation state + cpi_instruction + .accounts + .extend_from_slice(&execute_instruction.accounts[5..]); + cpi_account_infos.extend_from_slice(&execute_account_infos[5..]); + + // Add the validation state account + cpi_instruction + .accounts + .push(AccountMeta::new_readonly(validate_state_pubkey, false)); + cpi_account_infos.push(validate_state_info.clone()); + } + + // Add the program id cpi_instruction .accounts .push(AccountMeta::new_readonly(*program_id, false)); - cpi_instruction - .accounts - .push(AccountMeta::new_readonly(validate_state_pubkey, false)); cpi_account_infos.push(program_info.clone()); - cpi_account_infos.push(validate_state_info.clone()); Ok(()) } @@ -368,16 +375,18 @@ mod tests { validate_state_account_info.clone(), ]; - // Fail missing validation info from additional account infos - let additional_account_infos_missing_infos = vec![ - extra_meta_1_account_info.clone(), - extra_meta_2_account_info.clone(), - extra_meta_3_account_info.clone(), - extra_meta_4_account_info.clone(), - // validate state missing - transfer_hook_program_account_info.clone(), - ]; - assert_eq!( + // Allow missing validation info from additional account infos + { + let additional_account_infos_missing_infos = vec![ + extra_meta_1_account_info.clone(), + extra_meta_2_account_info.clone(), + extra_meta_3_account_info.clone(), + extra_meta_4_account_info.clone(), + // validate state missing + transfer_hook_program_account_info.clone(), + ]; + let mut cpi_instruction = cpi_instruction.clone(); + let mut cpi_account_infos = cpi_account_infos.clone(); add_extra_accounts_for_execute_cpi( &mut cpi_instruction, &mut cpi_account_infos, @@ -387,11 +396,32 @@ mod tests { destination_account_info.clone(), authority_account_info.clone(), amount, - &additional_account_infos_missing_infos, // Missing account info + &additional_account_infos_missing_infos, ) - .unwrap_err(), - TransferHookError::IncorrectAccount.into() - ); + .unwrap(); + let check_metas = [ + AccountMeta::new(source_pubkey, false), + AccountMeta::new_readonly(mint_pubkey, false), + AccountMeta::new(destination_pubkey, false), + AccountMeta::new_readonly(authority_pubkey, true), + AccountMeta::new_readonly(transfer_hook_program_id, false), + ]; + + let check_account_infos = vec![ + source_account_info.clone(), + mint_account_info.clone(), + destination_account_info.clone(), + authority_account_info.clone(), + transfer_hook_program_account_info.clone(), + ]; + + assert_eq!(cpi_instruction.accounts, check_metas); + for (a, b) in std::iter::zip(cpi_account_infos, check_account_infos) { + assert_eq!(a.key, b.key); + assert_eq!(a.is_signer, b.is_signer); + assert_eq!(a.is_writable, b.is_writable); + } + } // Fail missing program info from additional account infos let additional_account_infos_missing_infos = vec![ @@ -466,8 +496,8 @@ mod tests { AccountMeta::new_readonly(EXTRA_META_2, true), AccountMeta::new(extra_meta_3_pubkey, false), AccountMeta::new(extra_meta_4_pubkey, false), - AccountMeta::new_readonly(transfer_hook_program_id, false), AccountMeta::new_readonly(validate_state_pubkey, false), + AccountMeta::new_readonly(transfer_hook_program_id, false), ]; let check_account_infos = vec![ @@ -479,8 +509,8 @@ mod tests { extra_meta_2_account_info, extra_meta_3_account_info, extra_meta_4_account_info, - transfer_hook_program_account_info, validate_state_account_info, + transfer_hook_program_account_info, ]; assert_eq!(cpi_instruction.accounts, check_metas);