diff --git a/.github/workflows/ci-pr-main-program.yml b/.github/workflows/ci-pr-main-program.yml index 87ed092..bb337b0 100644 --- a/.github/workflows/ci-pr-main-program.yml +++ b/.github/workflows/ci-pr-main-program.yml @@ -4,10 +4,12 @@ on: pull_request: branches: - main + - staging + env: - SOLANA_CLI_VERSION: 1.16.25 + SOLANA_CLI_VERSION: 1.18.21 NODE_VERSION: 18.14.2 - ANCHOR_CLI_VERSION: 0.28.0 + ANCHOR_CLI_VERSION: 0.30.1 jobs: program_changed_files: @@ -32,12 +34,11 @@ jobs: steps: - uses: actions/checkout@v2 # Install rust + toolchain - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - components: clippy + - run: rustup toolchain install stable --component clippy # Cache rust, cargo - - uses: Swatinem/rust-cache@v1 + - uses: Swatinem/rust-cache@v2 + with: + prefix-key: "v1" - run: cargo test --package locker shell: bash @@ -51,12 +52,11 @@ jobs: - uses: ./.github/actions/setup-dep - uses: ./.github/actions/setup-anchor # Install rust + toolchain - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - components: clippy + - run: rustup toolchain install stable --component clippy # Cache rust, cargo - - uses: Swatinem/rust-cache@v1 + - uses: Swatinem/rust-cache@v2 + with: + prefix-key: "v1" # Cache node_modules - uses: actions/cache@v2 id: cache-node-modules diff --git a/.gitignore b/.gitignore index 9cba99f..aa56c4b 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,6 @@ target node_modules test-ledger .yarn -proptest-regressions \ No newline at end of file +proptest-regressions + +.idea \ No newline at end of file diff --git a/Anchor.toml b/Anchor.toml index 1483284..cf2cc07 100644 --- a/Anchor.toml +++ b/Anchor.toml @@ -15,4 +15,8 @@ cluster = "Localnet" wallet = "keys/localnet/admin-bossj3JvwiNK7pvjr149DqdtJxf2gdygbcmEPTkb2F1.json" [scripts] -test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts" \ No newline at end of file +test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts" + +[[test.genesis]] +address = "EBZDYx7599krFc4m2govwBdZcicr4GgepqC78m71nsHS" +program = "artifacts/transfer_hook_counter.so" \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 368db28..c105f59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,29 +21,55 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Breaking Changes +## Program [0.3.0] [PR #5](https://github.com/jup-ag/jup-lock/pull/5) [PR #15](https://github.com/jup-ag/jup-lock/pull/15) [PR #18](https://github.com/jup-ag/jup-lock/pull/18) + +### Breaking Changes + +- Endpoint `create_vesting_escrow` add `cancel_mode` to indicates who can cancel the escrow. + +### Changed + +- Bump `anchor` version to 0.30.1 + +### Added + +- escrow state add `token_program_flag` to indicates the token program used within the escrow. +- escrow state add `cancelled_at` to indicates the timestamp of the cancellation. +- Add new instruction `cancel_vesting_escrow`, which will cancel the escrow and close the `escrow_token` token account. + The claimable amount will be transferred to recipient and the remaining amount will be transferred to creator. The + instruction supports both `splToken` and `token2022`. +- Add new v2 instructions to support `token2022` extensions, + including: `TransferFeeConfig`, `TokenMetadata`, `MetadataPointer`, `ConfidentialTransferMint`, `ConfidentialTransferFeeConfig`, `PermanentDelegate`, `TransferHook`, `MintCloseAuthority`, `DefaultAccountState`, `GroupPointer`, `GroupMemberPointer` + for Token Mint and `MemoTransfer` for Token Account extensions + - `create_vesting_escrow_v2` to create the escrow relevant accounts. + - `claim_v2` to claim from the escrow. + ## Program [0.2.2] [PR #8](https://github.com/jup-ag/jup-lock/pull/8) ### Breaking Changes -- Rename `cliff_amount` to `initial_unlock_amount` +- Rename `cliff_amount` to `initial_unlock_amount` ## Program [0.2.1] [PR #4](https://github.com/jup-ag/jup-lock/pull/4) ### Fixed + - Add check `recipient_token` in claim instruction - Update `emit_cpi` in `claim` and `create_vesting_escrow` instruction ### Breaking Changes -- Endpoint `update vesting_escrow_recipient` allow signer to update `recipient_email` in `escrow_metadata` +- Endpoint `update vesting_escrow_recipient` allow signer to update `recipient_email` in `escrow_metadata` ## Program [0.2.0] [PR #3](https://github.com/jup-ag/jup-lock/pull/3) ### Changed + - Rename account `escrow` to `vesting_escrow`, `escrow_metadata` to `vesting_escrow_metadata` - Rename endpoint `create_vesting_plan` to `create_vesting_escrow` - Rename endpoint `create_escrow_metadata` to `create_vesting_escrow_metadata` - Rename endpoint `update_recipient` to `update_vesting_escrow_recipient` ### Breaking Changes + - escrow state remove field `escrow_token` and add field `token_mint` diff --git a/Cargo.lock b/Cargo.lock index 214fa07..e0e376b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,19 +14,13 @@ dependencies = [ [[package]] name = "addr2line" -version = "0.22.0" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - [[package]] name = "adler2" version = "2.0.0" @@ -82,9 +76,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.6" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "getrandom 0.2.15", @@ -119,87 +113,85 @@ dependencies = [ [[package]] name = "anchor-attribute-access-control" -version = "0.28.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faa5be5b72abea167f87c868379ba3c2be356bfca9e6f474fd055fa0f7eeb4f2" +checksum = "47fe28365b33e8334dd70ae2f34a43892363012fe239cf37d2ee91693575b1f8" dependencies = [ "anchor-syn", - "anyhow", - "proc-macro2 1.0.82", - "quote 1.0.36", - "regex", + "proc-macro2", + "quote", "syn 1.0.109", ] [[package]] name = "anchor-attribute-account" -version = "0.28.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f468970344c7c9f9d03b4da854fd7c54f21305059f53789d0045c1dd803f0018" +checksum = "3c288d496168268d198d9b53ee9f4f9d260a55ba4df9877ea1d4486ad6109e0f" dependencies = [ "anchor-syn", - "anyhow", "bs58 0.5.1", - "proc-macro2 1.0.82", - "quote 1.0.36", - "rustversion", + "proc-macro2", + "quote", "syn 1.0.109", ] [[package]] name = "anchor-attribute-constant" -version = "0.28.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59948e7f9ef8144c2aefb3f32a40c5fce2798baeec765ba038389e82301017ef" +checksum = "49b77b6948d0eeaaa129ce79eea5bbbb9937375a9241d909ca8fb9e006bb6e90" dependencies = [ "anchor-syn", - "proc-macro2 1.0.82", + "quote", "syn 1.0.109", ] [[package]] name = "anchor-attribute-error" -version = "0.28.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc753c9d1c7981cb8948cf7e162fb0f64558999c0413058e2d43df1df5448086" +checksum = "4d20bb569c5a557c86101b944721d865e1fd0a4c67c381d31a44a84f07f84828" dependencies = [ "anchor-syn", - "proc-macro2 1.0.82", - "quote 1.0.36", + "quote", "syn 1.0.109", ] [[package]] name = "anchor-attribute-event" -version = "0.28.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38b4e172ba1b52078f53fdc9f11e3dc0668ad27997838a0aad2d148afac8c97" +checksum = "4cebd8d0671a3a9dc3160c48598d652c34c77de6be4d44345b8b514323284d57" dependencies = [ "anchor-syn", - "anyhow", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 1.0.109", ] [[package]] name = "anchor-attribute-program" -version = "0.28.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eebd21543606ab61e2d83d9da37d24d3886a49f390f9c43a1964735e8c0f0d5" +checksum = "efb2a5eb0860e661ab31aff7bb5e0288357b176380e985bade4ccb395981b42d" dependencies = [ + "anchor-lang-idl", "anchor-syn", "anyhow", - "proc-macro2 1.0.82", - "quote 1.0.36", + "bs58 0.5.1", + "heck 0.3.3", + "proc-macro2", + "quote", + "serde_json", "syn 1.0.109", ] [[package]] name = "anchor-client" -version = "0.28.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8434a6bf33efba0c93157f7fa2fafac658cb26ab75396886dcedd87c2a8ad445" +checksum = "95b4397af9b7d6919df3342210d897c0ffda1a31d052abc8eee3e6035ee71567" dependencies = [ "anchor-lang", "anyhow", @@ -216,33 +208,44 @@ dependencies = [ [[package]] name = "anchor-derive-accounts" -version = "0.28.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec4720d899b3686396cced9508f23dab420f1308344456ec78ef76f98fda42af" +checksum = "04368b5abef4266250ca8d1d12f4dff860242681e4ec22b885dcfe354fd35aa1" dependencies = [ "anchor-syn", - "anyhow", - "proc-macro2 1.0.82", - "quote 1.0.36", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "anchor-derive-serde" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0bb0e0911ad4a70cab880cdd6287fe1e880a1a9d8e4e6defa8e9044b9796a6c" +dependencies = [ + "anchor-syn", + "borsh-derive-internal 0.10.4", + "proc-macro2", + "quote", "syn 1.0.109", ] [[package]] name = "anchor-derive-space" -version = "0.28.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f495e85480bd96ddeb77b71d499247c7d4e8b501e75ecb234e9ef7ae7bd6552a" +checksum = "5ef415ff156dc82e9ecb943189b0cb241b3a6bfc26a180234dc21bd3ef3ce0cb" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 1.0.109", ] [[package]] name = "anchor-lang" -version = "0.28.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d2d4b20100f1310a774aba3471ef268e5c4ba4d5c28c0bbe663c2658acbc414" +checksum = "6620c9486d9d36a4389cab5e37dc34a42ed0bfaa62e6a75a2999ce98f8f2e373" dependencies = [ "anchor-attribute-access-control", "anchor-attribute-account", @@ -251,41 +254,72 @@ dependencies = [ "anchor-attribute-event", "anchor-attribute-program", "anchor-derive-accounts", + "anchor-derive-serde", "anchor-derive-space", + "anchor-lang-idl", "arrayref", - "base64 0.13.1", + "base64 0.21.7", "bincode", - "borsh 0.10.3", + "borsh 0.10.4", "bytemuck", "getrandom 0.2.15", "solana-program", "thiserror", ] +[[package]] +name = "anchor-lang-idl" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31cf97b4e6f7d6144a05e435660fcf757dbc3446d38d0e2b851d11ed13625bba" +dependencies = [ + "anchor-lang-idl-spec", + "anyhow", + "heck 0.3.3", + "regex", + "serde", + "serde_json", + "sha2 0.10.8", +] + +[[package]] +name = "anchor-lang-idl-spec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bdf143115440fe621bdac3a29a1f7472e09f6cd82b2aa569429a0c13f103838" +dependencies = [ + "anyhow", + "serde", +] + [[package]] name = "anchor-spl" -version = "0.28.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78f860599da1c2354e7234c768783049eb42e2f54509ecfc942d2e0076a2da7b" +checksum = "04bd077c34449319a1e4e0bc21cea572960c9ae0d0fefda0dd7c52fcc3c647a3" dependencies = [ "anchor-lang", - "solana-program", - "spl-associated-token-account 1.1.3", - "spl-token 3.5.0", - "spl-token-2022 0.6.1", + "spl-associated-token-account 3.0.4", + "spl-memo", + "spl-pod 0.2.5", + "spl-token", + "spl-token-2022 3.0.4", + "spl-token-group-interface 0.2.5", + "spl-token-metadata-interface 0.3.5", ] [[package]] name = "anchor-syn" -version = "0.28.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a125e4b0cc046cfec58f5aa25038e34cf440151d58f0db3afc55308251fe936d" +checksum = "f99daacb53b55cfd37ce14d6c9905929721137fd4c67bbab44a19802aecb622f" dependencies = [ "anyhow", "bs58 0.5.1", + "cargo_toml", "heck 0.3.3", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "serde", "serde_json", "sha2 0.10.8", @@ -319,9 +353,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.83" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" +checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8" [[package]] name = "ark-bn254" @@ -364,7 +398,7 @@ dependencies = [ "derivative", "digest 0.10.7", "itertools", - "num-bigint 0.4.5", + "num-bigint 0.4.6", "num-traits", "paste", "rustc_version", @@ -377,7 +411,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" dependencies = [ - "quote 1.0.36", + "quote", "syn 1.0.109", ] @@ -387,10 +421,10 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ - "num-bigint 0.4.5", + "num-bigint 0.4.6", "num-traits", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -416,7 +450,7 @@ dependencies = [ "ark-serialize-derive", "ark-std", "digest 0.10.7", - "num-bigint 0.4.5", + "num-bigint 0.4.6", ] [[package]] @@ -425,8 +459,8 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae3281bc6d0fd7e549af32b52511e1302185bd688fd3359fa36423346ff682ea" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -440,23 +474,17 @@ dependencies = [ "rand 0.8.5", ] -[[package]] -name = "array-bytes" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ad284aeb45c13f2fb4f084de4a420ebf447423bdf9386c0540ce33cb3ef4b8c" - [[package]] name = "arrayref" -version = "0.3.7" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "ascii" @@ -486,8 +514,8 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 1.0.109", "synstructure", ] @@ -498,8 +526,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -522,9 +550,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.15" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e26a9844c659a2a293d239c7910b752f8487fe122c6c8bd1659bf85a6507c302" +checksum = "0cb8f1d480b0ea3783ab015936d2a55c87e219676f0c0b7dec61494043f21857" dependencies = [ "brotli", "flate2", @@ -549,9 +577,9 @@ version = "0.1.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.63", + "proc-macro2", + "quote", + "syn 2.0.85", ] [[package]] @@ -567,23 +595,23 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "backtrace" -version = "0.3.73" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", - "miniz_oxide 0.7.4", + "miniz_oxide", "object", "rustc-demangle", + "windows-targets 0.52.6", ] [[package]] @@ -642,9 +670,12 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +dependencies = [ + "serde", +] [[package]] name = "bitmaps" @@ -706,14 +737,24 @@ dependencies = [ [[package]] name = "borsh" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b" +checksum = "115e54d64eb62cdebad391c19efc9dce4981c690c85a33a12199d99bb9546fee" dependencies = [ - "borsh-derive 0.10.3", + "borsh-derive 0.10.4", "hashbrown 0.13.2", ] +[[package]] +name = "borsh" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6362ed55def622cddc70a4746a68554d7b687713770de539e59a739b249f8ed" +dependencies = [ + "borsh-derive 1.5.1", + "cfg_aliases", +] + [[package]] name = "borsh-derive" version = "0.9.3" @@ -723,42 +764,56 @@ dependencies = [ "borsh-derive-internal 0.9.3", "borsh-schema-derive-internal 0.9.3", "proc-macro-crate 0.1.5", - "proc-macro2 1.0.82", + "proc-macro2", "syn 1.0.109", ] [[package]] name = "borsh-derive" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0754613691538d51f329cce9af41d7b7ca150bc973056f1156611489475f54f7" +checksum = "831213f80d9423998dd696e2c5345aba6be7a0bd8cd19e31c5243e13df1cef89" dependencies = [ - "borsh-derive-internal 0.10.3", - "borsh-schema-derive-internal 0.10.3", + "borsh-derive-internal 0.10.4", + "borsh-schema-derive-internal 0.10.4", "proc-macro-crate 0.1.5", - "proc-macro2 1.0.82", + "proc-macro2", "syn 1.0.109", ] +[[package]] +name = "borsh-derive" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ef8005764f53cd4dca619f5bf64cafd4664dada50ece25e4d81de54c80cc0b" +dependencies = [ + "once_cell", + "proc-macro-crate 3.2.0", + "proc-macro2", + "quote", + "syn 2.0.85", + "syn_derive", +] + [[package]] name = "borsh-derive-internal" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 1.0.109", ] [[package]] name = "borsh-derive-internal" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afb438156919598d2c7bad7e1c0adf3d26ed3840dbc010db1a882a65583ca2fb" +checksum = "65d6ba50644c98714aa2a70d13d7df3cd75cd2b523a2b452bf010443800976b3" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -768,19 +823,19 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 1.0.109", ] [[package]] name = "borsh-schema-derive-internal" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634205cc43f74a1b9046ef87c4540ebda95696ec0f315024860cad7c5b0f5ccd" +checksum = "276691d96f063427be83e6692b86148e488ebba9f48f77788724ca027ba3b6d4" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -838,22 +893,22 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.15.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" +checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4da9a32f3fed317401fa3c862968128267c3106685286e15d5aaa3d7389c2f60" +checksum = "bcfcc3cd946cb52f0bbfdbbcfa2f4e24f75ebb6c0e1002f7c25904fada18b9ec" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.63", + "proc-macro2", + "quote", + "syn 2.0.85", ] [[package]] @@ -864,9 +919,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.2" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" [[package]] name = "caps" @@ -878,15 +933,25 @@ dependencies = [ "thiserror", ] +[[package]] +name = "cargo_toml" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a98356df42a2eb1bd8f1793ae4ee4de48e384dd974ce5eac8eee802edb7492be" +dependencies = [ + "serde", + "toml 0.8.19", +] + [[package]] name = "cc" -version = "1.0.97" +version = "1.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" +checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" dependencies = [ "jobserver", "libc", - "once_cell", + "shlex", ] [[package]] @@ -895,6 +960,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.38" @@ -907,7 +978,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -959,8 +1030,8 @@ checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" dependencies = [ "heck 0.4.1", "proc-macro-error", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -988,7 +1059,7 @@ dependencies = [ "solana-program", "solana-rpc-client", "solana-sdk", - "spl-associated-token-account 2.2.0", + "spl-associated-token-account 2.3.0", ] [[package]] @@ -1054,9 +1125,9 @@ checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" [[package]] name = "constant_time_eq" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" [[package]] name = "core-foundation" @@ -1076,9 +1147,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" dependencies = [ "libc", ] @@ -1122,9 +1193,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crunchy" @@ -1198,9 +1269,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.8" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ "darling_core", "darling_macro", @@ -1208,27 +1279,40 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.8" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.82", - "quote 1.0.36", - "strsim 0.10.0", - "syn 2.0.63", + "proc-macro2", + "quote", + "strsim 0.11.1", + "syn 2.0.85", ] [[package]] name = "darling_macro" -version = "0.20.8" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", - "quote 1.0.36", - "syn 2.0.63", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", ] [[package]] @@ -1255,7 +1339,7 @@ dependencies = [ "asn1-rs", "displaydoc", "nom", - "num-bigint 0.4.5", + "num-bigint 0.4.6", "num-traits", "rusticata-macros", ] @@ -1281,8 +1365,8 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -1324,32 +1408,32 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.63", + "proc-macro2", + "quote", + "syn 2.0.85", ] [[package]] -name = "dlopen" -version = "0.1.8" +name = "dlopen2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e80ad39f814a9abe68583cd50a2d45c8a67561c3361ab8da240587dda80937" +checksum = "09b4f5f101177ff01b8ec4ecc81eead416a8aa42819a2869311b3420fa114ffa" dependencies = [ - "dlopen_derive", - "lazy_static", + "dlopen2_derive", "libc", + "once_cell", "winapi", ] [[package]] -name = "dlopen_derive" -version = "0.1.4" +name = "dlopen2_derive" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f236d9e1b1fbd81cea0f9cbdc8dcc7e8ebcd80e6659cd7cb2ad5f6c05946c581" +checksum = "a6cbae11b3de8fce2a456e8ea3dada226b35fe791f0dc1d360c0941f0bb681f3" dependencies = [ - "libc", - "quote 0.6.13", - "syn 0.15.44", + "proc-macro2", + "quote", + "syn 2.0.85", ] [[package]] @@ -1395,9 +1479,9 @@ dependencies = [ [[package]] name = "either" -version = "1.11.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "encode_unicode" @@ -1429,9 +1513,9 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1ab991c1362ac86c61ab6f556cff143daa22e5a15e4e189df818b2fd19fe65b" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.63", + "proc-macro2", + "quote", + "syn 2.0.85", ] [[package]] @@ -1471,9 +1555,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "fastrand" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] name = "feature-probe" @@ -1488,7 +1572,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" dependencies = [ "crc32fast", - "miniz_oxide 0.8.0", + "miniz_oxide", ] [[package]] @@ -1560,9 +1644,9 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.63", + "proc-macro2", + "quote", + "syn 2.0.85", ] [[package]] @@ -1644,9 +1728,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.29.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "goblin" @@ -1671,7 +1755,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 2.2.6", + "indexmap 2.6.0", "slab", "tokio", "tokio-util", @@ -1708,7 +1792,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "ahash 0.8.6", + "ahash 0.8.11", ] [[package]] @@ -1717,6 +1801,12 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + [[package]] name = "heck" version = "0.3.3" @@ -1825,9 +1915,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.30" +version = "0.14.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85" dependencies = [ "bytes", "futures-channel", @@ -1840,7 +1930,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.5.7", + "socket2", "tokio", "tower-service", "tracing", @@ -1856,9 +1946,9 @@ dependencies = [ "futures-util", "http", "hyper", - "rustls 0.21.12", + "rustls", "tokio", - "tokio-rustls 0.24.1", + "tokio-rustls", ] [[package]] @@ -1928,12 +2018,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown 0.15.0", ] [[package]] @@ -1981,18 +2071,18 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jobserver" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" dependencies = [ "wasm-bindgen", ] @@ -2023,15 +2113,15 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.154" +version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" [[package]] name = "libm" @@ -2087,6 +2177,18 @@ dependencies = [ "libsecp256k1-core", ] +[[package]] +name = "light-poseidon" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c9a85a9752c549ceb7578064b4ed891179d20acd85f27318573b64d2d7ee7ee" +dependencies = [ + "ark-bn254", + "ark-ff", + "num-bigint 0.4.6", + "thiserror", +] + [[package]] name = "linux-raw-sys" version = "0.4.14" @@ -2105,27 +2207,29 @@ dependencies = [ [[package]] name = "locker" -version = "0.2.2" +version = "0.3.0" dependencies = [ "anchor-lang", "anchor-spl", "bytemuck", - "num_enum 0.7.2", + "num_enum 0.7.3", "proptest", + "solana-program", + "spl-transfer-hook-interface 0.5.0", "static_assertions", ] [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" @@ -2178,15 +2282,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" -[[package]] -name = "miniz_oxide" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" -dependencies = [ - "adler", -] - [[package]] name = "miniz_oxide" version = "0.8.0" @@ -2258,9 +2353,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", @@ -2288,8 +2383,8 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 1.0.109", ] @@ -2299,9 +2394,9 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.63", + "proc-macro2", + "quote", + "syn 2.0.85", ] [[package]] @@ -2356,15 +2451,6 @@ dependencies = [ "libc", ] -[[package]] -name = "num_enum" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" -dependencies = [ - "num_enum_derive 0.5.11", -] - [[package]] name = "num_enum" version = "0.6.1" @@ -2376,23 +2462,11 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" -dependencies = [ - "num_enum_derive 0.7.2", -] - -[[package]] -name = "num_enum_derive" -version = "0.5.11" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 1.0.109", + "num_enum_derive 0.7.3", ] [[package]] @@ -2402,21 +2476,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" dependencies = [ "proc-macro-crate 1.3.1", - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.63", + "proc-macro2", + "quote", + "syn 2.0.85", ] [[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 1.3.1", - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.63", + "proc-macro-crate 3.2.0", + "proc-macro2", + "quote", + "syn 2.0.85", ] [[package]] @@ -2445,9 +2519,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "opaque-debug" @@ -2469,9 +2543,9 @@ checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" [[package]] name = "parking_lot" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -2487,7 +2561,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -2599,9 +2673,12 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "proc-macro-crate" @@ -2609,7 +2686,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" dependencies = [ - "toml", + "toml 0.5.11", ] [[package]] @@ -2619,7 +2696,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "toml_edit", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +dependencies = [ + "toml_edit 0.22.22", ] [[package]] @@ -2629,8 +2715,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 1.0.109", "version_check", ] @@ -2641,25 +2727,16 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "version_check", ] [[package]] name = "proc-macro2" -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -dependencies = [ - "unicode-xid 0.1.0", -] - -[[package]] -name = "proc-macro2" -version = "1.0.82" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] @@ -2672,7 +2749,7 @@ checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.5.0", + "bitflags 2.6.0", "lazy_static", "num-traits", "rand 0.8.5", @@ -2693,6 +2770,17 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "qualifier_attr" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e2e25ee72f5b24d773cae88422baddefff7714f97aab68d96fe2b6fc4a28fb2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -2701,70 +2789,59 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quinn" -version = "0.9.4" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e8b432585672228923edbbf64b8b12c14e1112f62e88737655b4a083dbcd78e" +checksum = "8cc2c5017e4b43d5995dcea317bc46c1e09404c0a9664d2908f7f02dfe943d75" dependencies = [ "bytes", "pin-project-lite", "quinn-proto", "quinn-udp", "rustc-hash", - "rustls 0.20.9", + "rustls", "thiserror", "tokio", "tracing", - "webpki", ] [[package]] name = "quinn-proto" -version = "0.9.6" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b0b33c13a79f669c85defaf4c275dc86a0c0372807d0ca3d78e0bb87274863" +checksum = "141bf7dfde2fbc246bfd3fe12f2455aa24b0fbd9af535d8c86c7bd1381ff2b1a" dependencies = [ "bytes", "rand 0.8.5", "ring 0.16.20", "rustc-hash", - "rustls 0.20.9", + "rustls", "rustls-native-certs", "slab", "thiserror", "tinyvec", "tracing", - "webpki", ] [[package]] name = "quinn-udp" -version = "0.3.2" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "641538578b21f5e5c8ea733b736895576d0fe329bb883b937db6f4d163dbaaf4" +checksum = "055b4e778e8feb9f93c4e439f71dc2156ef13360b432b799e179a8c4cdf0b1d7" dependencies = [ + "bytes", "libc", - "quinn-proto", - "socket2 0.4.10", + "socket2", "tracing", - "windows-sys 0.42.0", -] - -[[package]] -name = "quote" -version = "0.6.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" -dependencies = [ - "proc-macro2 0.4.30", + "windows-sys 0.48.0", ] [[package]] name = "quote" -version = "1.0.36" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ - "proc-macro2 1.0.82", + "proc-macro2", ] [[package]] @@ -2890,18 +2967,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.1" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", ] [[package]] name = "regex" -version = "1.10.4" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" dependencies = [ "aho-corasick", "memchr", @@ -2911,9 +2988,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", @@ -2922,9 +2999,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" @@ -2950,7 +3027,7 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.21.12", + "rustls", "rustls-pemfile", "serde", "serde_json", @@ -2958,7 +3035,7 @@ dependencies = [ "sync_wrapper", "system-configuration", "tokio", - "tokio-rustls 0.24.1", + "tokio-rustls", "tokio-util", "tower-service", "url", @@ -3034,9 +3111,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver", ] @@ -3052,29 +3129,17 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.34" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", "windows-sys 0.52.0", ] -[[package]] -name = "rustls" -version = "0.20.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" -dependencies = [ - "log", - "ring 0.16.20", - "sct", - "webpki", -] - [[package]] name = "rustls" version = "0.21.12" @@ -3120,9 +3185,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.16" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "092474d1a01ea8278f69e6a358998405fae5b8b963ddaeb2b0b04a128bf1dfb0" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" [[package]] name = "rusty-fork" @@ -3144,11 +3209,11 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "schannel" -version = "0.1.23" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3172,9 +3237,9 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.63", + "proc-macro2", + "quote", + "syn 2.0.85", ] [[package]] @@ -3189,11 +3254,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "core-foundation", "core-foundation-sys", "libc", @@ -3218,44 +3283,54 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.201" +version = "1.0.213" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" +checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1" dependencies = [ "serde_derive", ] [[package]] name = "serde_bytes" -version = "0.11.14" +version = "0.11.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" +checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.201" +version = "1.0.213" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" +checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.63", + "proc-macro2", + "quote", + "syn 2.0.85", ] [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -3285,16 +3360,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" dependencies = [ "darling", - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.63", + "proc-macro2", + "quote", + "syn 2.0.85", ] [[package]] -name = "sha-1" -version = "0.10.1" +name = "sha1" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", "cpufeatures", @@ -3353,6 +3428,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -3368,6 +3449,12 @@ version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + [[package]] name = "sized-chunks" version = "0.6.5" @@ -3393,16 +3480,6 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" -[[package]] -name = "socket2" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "socket2" version = "0.5.7" @@ -3415,9 +3492,9 @@ dependencies = [ [[package]] name = "solana-account-decoder" -version = "1.16.16" +version = "1.18.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52aec62a85932e26d1085864b0f7b99b31934aec8dd132429bfef6d7fb1d3a6" +checksum = "b109fd3a106e079005167e5b0e6f6d2c88bbedec32530837b584791a8b5abf36" dependencies = [ "Inflector", "base64 0.21.7", @@ -3428,47 +3505,25 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "solana-address-lookup-table-program", "solana-config-program", "solana-sdk", - "spl-token 4.0.0", - "spl-token-2022 0.9.0", - "spl-token-metadata-interface", + "spl-token", + "spl-token-2022 1.0.0", + "spl-token-group-interface 0.1.0", + "spl-token-metadata-interface 0.2.0", "thiserror", "zstd", ] [[package]] -name = "solana-address-lookup-table-program" -version = "1.16.16" +name = "solana-clap-utils" +version = "1.18.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee0bd25f4ba0a15fc16c57b41b1e1b14f5271b83214fda158fdedb58758d394e" -dependencies = [ - "bincode", - "bytemuck", - "log", - "num-derive 0.3.3", - "num-traits", - "rustc_version", - "serde", - "solana-frozen-abi", - "solana-frozen-abi-macro", - "solana-program", - "solana-program-runtime", - "solana-sdk", - "thiserror", -] - -[[package]] -name = "solana-clap-utils" -version = "1.16.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e963043668c640183d48472b281ebb9f713e0c36df0271961d23e6a394e09070" +checksum = "074ef478856a45d5627270fbc6b331f91de9aae7128242d9e423931013fb8a2a" dependencies = [ "chrono", "clap 2.34.0", "rpassword", - "solana-perf", "solana-remote-wallet", "solana-sdk", "thiserror", @@ -3479,19 +3534,19 @@ dependencies = [ [[package]] name = "solana-client" -version = "1.16.16" +version = "1.18.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ccf7bb34fb81c74582a9011babaa2e0220da56c71186e77f45a6f352017fdb" +checksum = "24a9f32c42402c4b9484d5868ac74b7e0a746e3905d8bfd756e1203e50cbb87e" dependencies = [ "async-trait", "bincode", + "dashmap", "futures", "futures-util", - "indexmap 1.9.3", + "indexmap 2.6.0", "indicatif", "log", "quinn", - "rand 0.7.3", "rayon", "solana-connection-cache", "solana-measure", @@ -3512,9 +3567,9 @@ dependencies = [ [[package]] name = "solana-config-program" -version = "1.16.16" +version = "1.18.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fd0fc1efb91a1661aeb1ff6a691156c3b1bffdaed0aa096589499dd83f9e50b" +checksum = "9d75b803860c0098e021a26f0624129007c15badd5b0bc2fbd9f0e1a73060d3b" dependencies = [ "bincode", "chrono", @@ -3526,16 +3581,17 @@ dependencies = [ [[package]] name = "solana-connection-cache" -version = "1.16.16" +version = "1.18.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8759e9cb9b1e92a94c31812169ecb5e65b5e215fb70fb68691e03655de5b7b6c" +checksum = "b9306ede13e8ceeab8a096bcf5fa7126731e44c201ca1721ea3c38d89bcd4111" dependencies = [ "async-trait", "bincode", + "crossbeam-channel", "futures-util", - "indexmap 1.9.3", + "indexmap 2.6.0", "log", - "rand 0.7.3", + "rand 0.8.5", "rayon", "rcgen", "solana-measure", @@ -3547,31 +3603,23 @@ dependencies = [ [[package]] name = "solana-frozen-abi" -version = "1.16.16" +version = "1.18.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02eb4f0ed3eade20f4abdcc0031167344237cd6e16808bd0f33945f9db7861fe" +checksum = "03ab2c30c15311b511c0d1151e4ab6bc9a3e080a37e7c6e7c2d96f5784cf9434" dependencies = [ - "ahash 0.8.6", - "blake3", "block-buffer 0.10.4", "bs58 0.4.0", "bv", - "byteorder", - "cc", "either", "generic-array", - "getrandom 0.1.16", "im", "lazy_static", "log", "memmap2", - "once_cell", - "rand_core 0.6.4", "rustc_version", "serde", "serde_bytes", "serde_derive", - "serde_json", "sha2 0.10.8", "solana-frozen-abi-macro", "subtle", @@ -3580,21 +3628,21 @@ dependencies = [ [[package]] name = "solana-frozen-abi-macro" -version = "1.16.16" +version = "1.18.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f28514761a285944cbad5b3d7930546369b80a713ba37d84bcf6ed2753611765" +checksum = "c142f779c3633ac83c84d04ff06c70e1f558c876f13358bed77ba629c7417932" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "rustc_version", - "syn 2.0.63", + "syn 2.0.85", ] [[package]] name = "solana-logger" -version = "1.16.16" +version = "1.18.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c310c6749435ce1ea25a9ae3edfb2fd2c2aed2aa4d4f7e0487a8077a0b1ee30" +checksum = "121d36ffb3c6b958763312cbc697fbccba46ee837d3a0aa4fc0e90fcb3b884f3" dependencies = [ "env_logger", "lazy_static", @@ -3603,9 +3651,9 @@ dependencies = [ [[package]] name = "solana-measure" -version = "1.16.16" +version = "1.18.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d171357580e62aa4ca19c780e25f4e74de064e2780cb8b9f6b6901d986fcd23" +checksum = "5c01a7f9cdc9d9d37a3d5651b2fe7ec9d433c2a3470b9f35897e373b421f0737" dependencies = [ "log", "solana-sdk", @@ -3613,9 +3661,9 @@ dependencies = [ [[package]] name = "solana-metrics" -version = "1.16.16" +version = "1.18.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "013cbb3c82588278d2be18d3317ece5286cb54a3a06d5d38fc31e2a76a6d5e2d" +checksum = "71e36052aff6be1536bdf6f737c6e69aca9dbb6a2f3f582e14ecb0ddc0cd66ce" dependencies = [ "crossbeam-channel", "gethostname", @@ -3623,23 +3671,24 @@ dependencies = [ "log", "reqwest", "solana-sdk", + "thiserror", ] [[package]] name = "solana-net-utils" -version = "1.16.16" +version = "1.18.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50d7cac0694b1fe07499de25404a0c7d6836457e359aba3b08c3983c3dc5eb6" +checksum = "2a1f5c6be9c5b272866673741e1ebc64b2ea2118e5c6301babbce526fdfb15f4" dependencies = [ "bincode", "clap 3.2.25", "crossbeam-channel", "log", "nix", - "rand 0.7.3", + "rand 0.8.5", "serde", "serde_derive", - "socket2 0.4.10", + "socket2", "solana-logger", "solana-sdk", "solana-version", @@ -3649,25 +3698,27 @@ dependencies = [ [[package]] name = "solana-perf" -version = "1.16.16" +version = "1.18.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395d559e5be2c439551298e9fa95561807f24921fd9a1e08bb82a3dc05c02dea" +checksum = "28acaf22477566a0fbddd67249ea5d859b39bacdb624aff3fadd3c5745e2643c" dependencies = [ - "ahash 0.8.6", + "ahash 0.8.11", "bincode", "bv", "caps", "curve25519-dalek", - "dlopen", - "dlopen_derive", + "dlopen2", "fnv", "lazy_static", "libc", "log", "nix", - "rand 0.7.3", + "rand 0.8.5", "rayon", + "rustc_version", "serde", + "solana-frozen-abi", + "solana-frozen-abi-macro", "solana-metrics", "solana-rayon-threadlimit", "solana-sdk", @@ -3676,21 +3727,21 @@ dependencies = [ [[package]] name = "solana-program" -version = "1.16.16" +version = "1.18.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff2aa5434a77413e9d43e971ceb47bdb003f2e8bbc0365a25b684aca2605c25" +checksum = "c10f4588cefd716b24a1a40dd32c278e43a560ab8ce4de6b5805c9d113afdfa1" dependencies = [ "ark-bn254", "ark-ec", "ark-ff", "ark-serialize", - "array-bytes", "base64 0.21.7", "bincode", - "bitflags 1.3.2", + "bitflags 2.6.0", "blake3", - "borsh 0.10.3", + "borsh 0.10.4", "borsh 0.9.3", + "borsh 1.5.1", "bs58 0.4.0", "bv", "bytemuck", @@ -3704,14 +3755,14 @@ dependencies = [ "lazy_static", "libc", "libsecp256k1", + "light-poseidon", "log", "memoffset 0.9.1", - "num-bigint 0.4.5", - "num-derive 0.3.3", + "num-bigint 0.4.6", + "num-derive 0.4.2", "num-traits", "parking_lot", - "rand 0.7.3", - "rand_chacha 0.2.2", + "rand 0.8.5", "rustc_version", "rustversion", "serde", @@ -3731,9 +3782,9 @@ dependencies = [ [[package]] name = "solana-program-runtime" -version = "1.16.16" +version = "1.18.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1832fefc2187142dac169812518ec20da68b09abad86e4a78f8ae1787e4f56" +checksum = "fbf0c3eab2a80f514289af1f422c121defb030937643c43b117959d6f1932fb5" dependencies = [ "base64 0.21.7", "bincode", @@ -3742,10 +3793,10 @@ dependencies = [ "itertools", "libc", "log", - "num-derive 0.3.3", + "num-derive 0.4.2", "num-traits", "percentage", - "rand 0.7.3", + "rand 0.8.5", "rustc_version", "serde", "solana-frozen-abi", @@ -3759,9 +3810,9 @@ dependencies = [ [[package]] name = "solana-pubsub-client" -version = "1.16.16" +version = "1.18.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89388addbc3192407d948634f82c95c4dbe1efbe578582abfd136720e059556e" +checksum = "b064e76909d33821b80fdd826e6757251934a52958220c92639f634bea90366d" dependencies = [ "crossbeam-channel", "futures-util", @@ -3784,9 +3835,9 @@ dependencies = [ [[package]] name = "solana-quic-client" -version = "1.16.16" +version = "1.18.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7897b876a6df2d97b3a5ddfd764155c0591e3497e863fd7fdf32b54de4e2644" +checksum = "5a90e40ee593f6e9ddd722d296df56743514ae804975a76d47e7afed4e3da244" dependencies = [ "async-mutex", "async-trait", @@ -3796,9 +3847,8 @@ dependencies = [ "log", "quinn", "quinn-proto", - "quinn-udp", "rcgen", - "rustls 0.20.9", + "rustls", "solana-connection-cache", "solana-measure", "solana-metrics", @@ -3812,9 +3862,9 @@ dependencies = [ [[package]] name = "solana-rayon-threadlimit" -version = "1.16.16" +version = "1.18.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba17a930f9974a17a9a6c6e82e7f89b40127e9cc0f9c17cfc29fc5b149d2c38" +checksum = "66468f9c014992167de10cc68aad6ac8919a8c8ff428dc88c0d2b4da8c02b8b7" dependencies = [ "lazy_static", "num_cpus", @@ -3822,14 +3872,14 @@ dependencies = [ [[package]] name = "solana-remote-wallet" -version = "1.16.16" +version = "1.18.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80fb35e3fa78ef1d08a6a1d852e2c357e6ae388cb307d24fad359f57c34429f0" +checksum = "c191019f4d4f84281a6d0dd9a43181146b33019627fc394e42e08ade8976b431" dependencies = [ "console", "dialoguer", "log", - "num-derive 0.3.3", + "num-derive 0.4.2", "num-traits", "parking_lot", "qstring", @@ -3841,9 +3891,9 @@ dependencies = [ [[package]] name = "solana-rpc-client" -version = "1.16.16" +version = "1.18.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c512944689d747a87f76936c89bb59f5be6c9a3189631857f49746cfa47d5bd1" +checksum = "36ed4628e338077c195ddbf790693d410123d17dec0a319b5accb4aaee3fb15c" dependencies = [ "async-trait", "base64 0.21.7", @@ -3867,9 +3917,9 @@ dependencies = [ [[package]] name = "solana-rpc-client-api" -version = "1.16.16" +version = "1.18.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e918b75f8ac4c358a6b512bf8b7dafc8166ddcb52ded5164c6235e0693ccb09" +checksum = "83c913551faa4a1ae4bbfef6af19f3a5cf847285c05b4409e37c8993b3444229" dependencies = [ "base64 0.21.7", "bs58 0.4.0", @@ -3883,15 +3933,15 @@ dependencies = [ "solana-sdk", "solana-transaction-status", "solana-version", - "spl-token-2022 0.9.0", + "spl-token-2022 1.0.0", "thiserror", ] [[package]] name = "solana-rpc-client-nonce-utils" -version = "1.16.16" +version = "1.18.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6e360ea2f3a756bdf6256c1f6ef13f8b01b5d2a7855b4f16cafb4a4017f0557" +checksum = "1a47b6bb1834e6141a799db62bbdcf80d17a7d58d7bc1684c614e01a7293d7cf" dependencies = [ "clap 2.34.0", "solana-clap-utils", @@ -3902,15 +3952,15 @@ dependencies = [ [[package]] name = "solana-sdk" -version = "1.16.16" +version = "1.18.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b1002048941cedbd7dd6a96fdaa3dc5238b998aaa70b81946b1e3ec108cc2be" +checksum = "580ad66c2f7a4c3cb3244fe21440546bd500f5ecb955ad9826e92a78dded8009" dependencies = [ "assert_matches", "base64 0.21.7", "bincode", - "bitflags 1.3.2", - "borsh 0.10.3", + "bitflags 2.6.0", + "borsh 1.5.1", "bs58 0.4.0", "bytemuck", "byteorder", @@ -3927,13 +3977,14 @@ dependencies = [ "libsecp256k1", "log", "memmap2", - "num-derive 0.3.3", + "num-derive 0.4.2", "num-traits", - "num_enum 0.6.1", + "num_enum 0.7.3", "pbkdf2 0.11.0", "qstring", + "qualifier_attr", "rand 0.7.3", - "rand_chacha 0.2.2", + "rand 0.8.5", "rustc_version", "rustversion", "serde", @@ -3943,6 +3994,7 @@ dependencies = [ "serde_with", "sha2 0.10.8", "sha3 0.10.8", + "siphasher", "solana-frozen-abi", "solana-frozen-abi-macro", "solana-logger", @@ -3955,29 +4007,35 @@ dependencies = [ [[package]] name = "solana-sdk-macro" -version = "1.16.16" +version = "1.18.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b41b63b2da4a37ce323aba108db21f4c7bfa638dd1bf58fdc870f83bdce48ba" +checksum = "1b75d0f193a27719257af19144fdaebec0415d1c9e9226ae4bd29b791be5e9bd" dependencies = [ "bs58 0.4.0", - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "rustversion", - "syn 2.0.63", + "syn 2.0.85", ] +[[package]] +name = "solana-security-txt" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "468aa43b7edb1f9b7b7b686d5c3aeb6630dc1708e86e31343499dd5c4d775183" + [[package]] name = "solana-streamer" -version = "1.16.16" +version = "1.18.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f00e575f2bd5ae2776870fbd1d7379d4ad392c015e2a4e2a328953b821a9d36d" +checksum = "f8476e41ad94fe492e8c06697ee35912cf3080aae0c9e9ac6430835256ccf056" dependencies = [ "async-channel", "bytes", "crossbeam-channel", "futures-util", "histogram", - "indexmap 1.9.3", + "indexmap 2.6.0", "itertools", "libc", "log", @@ -3987,10 +4045,10 @@ dependencies = [ "pkcs8", "quinn", "quinn-proto", - "quinn-udp", - "rand 0.7.3", + "rand 0.8.5", "rcgen", - "rustls 0.20.9", + "rustls", + "smallvec", "solana-metrics", "solana-perf", "solana-sdk", @@ -4001,9 +4059,9 @@ dependencies = [ [[package]] name = "solana-thin-client" -version = "1.16.16" +version = "1.18.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df328e2624cce68c9949a53eac317a264eceb997131cb9bd22175698a5f5dc74" +checksum = "d8c02245d0d232430e79dc0d624aa42d50006097c3aec99ac82ac299eaa3a73f" dependencies = [ "bincode", "log", @@ -4016,17 +4074,16 @@ dependencies = [ [[package]] name = "solana-tpu-client" -version = "1.16.16" +version = "1.18.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8ce6fe221c0e1fd8aa3078b8fcb0cc3f4c27942f1256b57a60608d81ae5348" +checksum = "67251506ed03de15f1347b46636b45c47da6be75015b4a13f0620b21beb00566" dependencies = [ "async-trait", "bincode", "futures-util", - "indexmap 1.9.3", + "indexmap 2.6.0", "indicatif", "log", - "rand 0.7.3", "rayon", "solana-connection-cache", "solana-measure", @@ -4041,14 +4098,14 @@ dependencies = [ [[package]] name = "solana-transaction-status" -version = "1.16.16" +version = "1.18.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db0b811793e78a908119cc02edca3ff8b54d5660104ebd06cc0e2e7e2f66900b" +checksum = "2d3d36db1b2ab2801afd5482aad9fb15ed7959f774c81a77299fdd0ddcf839d4" dependencies = [ "Inflector", "base64 0.21.7", "bincode", - "borsh 0.10.3", + "borsh 0.10.4", "bs58 0.4.0", "lazy_static", "log", @@ -4056,20 +4113,19 @@ dependencies = [ "serde_derive", "serde_json", "solana-account-decoder", - "solana-address-lookup-table-program", "solana-sdk", - "spl-associated-token-account 2.2.0", - "spl-memo 4.0.0", - "spl-token 4.0.0", - "spl-token-2022 0.9.0", + "spl-associated-token-account 2.3.0", + "spl-memo", + "spl-token", + "spl-token-2022 1.0.0", "thiserror", ] [[package]] name = "solana-udp-client" -version = "1.16.16" +version = "1.18.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897ff303a15ba956e80573dee4cf96d94d41a69adc5362898b98851e347505ad" +checksum = "3a754a3c2265eb02e0c35aeaca96643951f03cee6b376afe12e0cf8860ffccd1" dependencies = [ "async-trait", "solana-connection-cache", @@ -4082,9 +4138,9 @@ dependencies = [ [[package]] name = "solana-version" -version = "1.16.16" +version = "1.18.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9513754d3b2203a0e1045a211c5d68e72e4ed135e477344c21d096897fd2bf70" +checksum = "f44776bd685cc02e67ba264384acc12ef2931d01d1a9f851cb8cdbd3ce455b9e" dependencies = [ "log", "rustc_version", @@ -4098,13 +4154,13 @@ dependencies = [ [[package]] name = "solana-vote-program" -version = "1.16.16" +version = "1.18.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b6bfc5302ce0383eb129aa3a916705a20d22c4ad448144ef684b7b028d4053f" +checksum = "25810970c91feb579bd3f67dca215fce971522e42bfd59696af89c5dfebd997c" dependencies = [ "bincode", "log", - "num-derive 0.3.3", + "num-derive 0.4.2", "num-traits", "rustc_version", "serde", @@ -4120,9 +4176,9 @@ dependencies = [ [[package]] name = "solana-zk-token-sdk" -version = "1.16.16" +version = "1.18.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d1fe77918563768a65fd5d6cd2fa06cf0aeb11e529a1ef8c230b0fe018600e3" +checksum = "7cbdf4249b6dfcbba7d84e2b53313698043f60f8e22ce48286e6fbe8a17c8d16" dependencies = [ "aes-gcm-siv", "base64 0.21.7", @@ -4134,7 +4190,7 @@ dependencies = [ "itertools", "lazy_static", "merlin", - "num-derive 0.3.3", + "num-derive 0.4.2", "num-traits", "rand 0.7.3", "serde", @@ -4149,9 +4205,9 @@ dependencies = [ [[package]] name = "solana_rbpf" -version = "0.6.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17d4ba1e58947346e360fabde0697029d36ba83c42f669199b16a8931313cf29" +checksum = "da5d083187e3b3f453e140f292c09186881da8a02a7b5e27f645ee26de3d9cc5" dependencies = [ "byteorder", "combine", @@ -4190,33 +4246,33 @@ dependencies = [ [[package]] name = "spl-associated-token-account" -version = "1.1.3" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978dba3bcbe88d0c2c58366c254d9ea41c5f73357e72fc0bdee4d6b5fc99c8f4" +checksum = "992d9c64c2564cc8f63a4b508bf3ebcdf2254b0429b13cd1d31adb6162432a5f" dependencies = [ "assert_matches", - "borsh 0.9.3", - "num-derive 0.3.3", + "borsh 0.10.4", + "num-derive 0.4.2", "num-traits", "solana-program", - "spl-token 3.5.0", - "spl-token-2022 0.6.1", + "spl-token", + "spl-token-2022 1.0.0", "thiserror", ] [[package]] name = "spl-associated-token-account" -version = "2.2.0" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385e31c29981488f2820b2022d8e731aae3b02e6e18e2fd854e4c9a94dc44fc3" +checksum = "143109d789171379e6143ef23191786dfaac54289ad6e7917cfb26b36c432b10" dependencies = [ "assert_matches", - "borsh 0.10.3", + "borsh 1.5.1", "num-derive 0.4.2", "num-traits", "solana-program", - "spl-token 4.0.0", - "spl-token-2022 0.9.0", + "spl-token", + "spl-token-2022 3.0.4", "thiserror", ] @@ -4228,7 +4284,18 @@ checksum = "cce5d563b58ef1bb2cdbbfe0dfb9ffdc24903b10ae6a4df2d8f425ece375033f" dependencies = [ "bytemuck", "solana-program", - "spl-discriminator-derive", + "spl-discriminator-derive 0.1.2", +] + +[[package]] +name = "spl-discriminator" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "210101376962bb22bb13be6daea34656ea1cbc248fce2164b146e39203b55e03" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator-derive 0.2.0", ] [[package]] @@ -4237,9 +4304,20 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07fd7858fc4ff8fb0e34090e41d7eb06a823e1057945c26d480bfc21d2338a93" dependencies = [ - "quote 1.0.36", - "spl-discriminator-syn", - "syn 2.0.63", + "quote", + "spl-discriminator-syn 0.1.2", + "syn 2.0.85", +] + +[[package]] +name = "spl-discriminator-derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9e8418ea6269dcfb01c712f0444d2c75542c04448b480e87de59d2865edc750" +dependencies = [ + "quote", + "spl-discriminator-syn 0.2.0", + "syn 2.0.85", ] [[package]] @@ -4248,20 +4326,24 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18fea7be851bd98d10721782ea958097c03a0c2a07d8d4997041d0ece6319a63" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "sha2 0.10.8", - "syn 2.0.63", + "syn 2.0.85", "thiserror", ] [[package]] -name = "spl-memo" -version = "3.0.1" +name = "spl-discriminator-syn" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd0dc6f70db6bacea7ff25870b016a65ba1d1b6013536f08e4fd79a8f9005325" +checksum = "8c1f05593b7ca9eac7caca309720f2eafb96355e037e6d373b909a80fe7b69b9" dependencies = [ - "solana-program", + "proc-macro2", + "quote", + "sha2 0.10.8", + "syn 2.0.85", + "thiserror", ] [[package]] @@ -4279,11 +4361,24 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2881dddfca792737c0706fa0175345ab282b1b0879c7d877bad129645737c079" dependencies = [ - "borsh 0.10.3", + "borsh 0.10.4", + "bytemuck", + "solana-program", + "solana-zk-token-sdk", + "spl-program-error 0.3.0", +] + +[[package]] +name = "spl-pod" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c52d84c55efeef8edcc226743dc089d7e3888b8e3474569aa3eff152b37b9996" +dependencies = [ + "borsh 1.5.1", "bytemuck", "solana-program", "solana-zk-token-sdk", - "spl-program-error", + "spl-program-error 0.4.4", ] [[package]] @@ -4295,7 +4390,20 @@ dependencies = [ "num-derive 0.4.2", "num-traits", "solana-program", - "spl-program-error-derive", + "spl-program-error-derive 0.3.2", + "thiserror", +] + +[[package]] +name = "spl-program-error" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e45a49acb925db68aa501b926096b2164adbdcade7a0c24152af9f0742d0a602" +dependencies = [ + "num-derive 0.4.2", + "num-traits", + "solana-program", + "spl-program-error-derive 0.4.1", "thiserror", ] @@ -4305,39 +4413,50 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1845dfe71fd68f70382232742e758557afe973ae19e6c06807b2c30f5d5cb474" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "sha2 0.10.8", - "syn 2.0.63", + "syn 2.0.85", +] + +[[package]] +name = "spl-program-error-derive" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d375dd76c517836353e093c2dbb490938ff72821ab568b545fd30ab3256b3e" +dependencies = [ + "proc-macro2", + "quote", + "sha2 0.10.8", + "syn 2.0.85", ] [[package]] name = "spl-tlv-account-resolution" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "062e148d3eab7b165582757453632ffeef490c02c86a48bfdb4988f63eefb3b9" +checksum = "615d381f48ddd2bb3c57c7f7fb207591a2a05054639b18a62e785117dd7a8683" dependencies = [ "bytemuck", "solana-program", - "spl-discriminator", - "spl-pod", - "spl-program-error", - "spl-type-length-value", + "spl-discriminator 0.1.0", + "spl-pod 0.1.0", + "spl-program-error 0.3.0", + "spl-type-length-value 0.3.0", ] [[package]] -name = "spl-token" -version = "3.5.0" +name = "spl-tlv-account-resolution" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e85e168a785e82564160dcb87b2a8e04cee9bfd1f4d488c729d53d6a4bd300d" +checksum = "fab8edfd37be5fa17c9e42c1bff86abbbaf0494b031b37957f2728ad2ff842ba" dependencies = [ - "arrayref", "bytemuck", - "num-derive 0.3.3", - "num-traits", - "num_enum 0.5.11", "solana-program", - "thiserror", + "spl-discriminator 0.2.5", + "spl-pod 0.2.5", + "spl-program-error 0.4.4", + "spl-type-length-value 0.4.6", ] [[package]] @@ -4357,72 +4476,152 @@ dependencies = [ [[package]] name = "spl-token-2022" -version = "0.6.1" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0043b590232c400bad5ee9eb983ced003d15163c4c5d56b090ac6d9a57457b47" +checksum = "d697fac19fd74ff472dfcc13f0b442dd71403178ce1de7b5d16f83a33561c059" dependencies = [ "arrayref", "bytemuck", - "num-derive 0.3.3", + "num-derive 0.4.2", "num-traits", - "num_enum 0.5.11", + "num_enum 0.7.3", "solana-program", + "solana-security-txt", "solana-zk-token-sdk", - "spl-memo 3.0.1", - "spl-token 3.5.0", + "spl-memo", + "spl-pod 0.1.0", + "spl-token", + "spl-token-group-interface 0.1.0", + "spl-token-metadata-interface 0.2.0", + "spl-transfer-hook-interface 0.4.1", + "spl-type-length-value 0.3.0", "thiserror", ] [[package]] name = "spl-token-2022" -version = "0.9.0" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4abf34a65ba420584a0c35f3903f8d727d1f13ababbdc3f714c6b065a686e86" +checksum = "b01d1b2851964e257187c0bca43a0de38d0af59192479ca01ac3e2b58b1bd95a" dependencies = [ "arrayref", "bytemuck", "num-derive 0.4.2", "num-traits", - "num_enum 0.7.2", + "num_enum 0.7.3", "solana-program", + "solana-security-txt", "solana-zk-token-sdk", - "spl-memo 4.0.0", - "spl-pod", - "spl-token 4.0.0", - "spl-token-metadata-interface", - "spl-transfer-hook-interface", - "spl-type-length-value", + "spl-memo", + "spl-pod 0.2.5", + "spl-token", + "spl-token-group-interface 0.2.5", + "spl-token-metadata-interface 0.3.5", + "spl-transfer-hook-interface 0.6.5", + "spl-type-length-value 0.4.6", "thiserror", ] +[[package]] +name = "spl-token-group-interface" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b889509d49fa74a4a033ca5dae6c2307e9e918122d97e58562f5c4ffa795c75d" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator 0.1.0", + "spl-pod 0.1.0", + "spl-program-error 0.3.0", +] + +[[package]] +name = "spl-token-group-interface" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "014817d6324b1e20c4bbc883e8ee30a5faa13e59d91d1b2b95df98b920150c17" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator 0.2.5", + "spl-pod 0.2.5", + "spl-program-error 0.4.4", +] + [[package]] name = "spl-token-metadata-interface" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c16ce3ba6979645fb7627aa1e435576172dd63088dc7848cb09aa331fa1fe4f" dependencies = [ - "borsh 0.10.3", + "borsh 0.10.4", "solana-program", - "spl-discriminator", - "spl-pod", - "spl-program-error", - "spl-type-length-value", + "spl-discriminator 0.1.0", + "spl-pod 0.1.0", + "spl-program-error 0.3.0", + "spl-type-length-value 0.3.0", +] + +[[package]] +name = "spl-token-metadata-interface" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3da00495b602ebcf5d8ba8b3ecff1ee454ce4c125c9077747be49c2d62335ba" +dependencies = [ + "borsh 1.5.1", + "solana-program", + "spl-discriminator 0.2.5", + "spl-pod 0.2.5", + "spl-program-error 0.4.4", + "spl-type-length-value 0.4.6", ] [[package]] name = "spl-transfer-hook-interface" -version = "0.3.0" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aabdb7c471566f6ddcee724beb8618449ea24b399e58d464d6b5bc7db550259" +dependencies = [ + "arrayref", + "bytemuck", + "solana-program", + "spl-discriminator 0.1.0", + "spl-pod 0.1.0", + "spl-program-error 0.3.0", + "spl-tlv-account-resolution 0.5.1", + "spl-type-length-value 0.3.0", +] + +[[package]] +name = "spl-transfer-hook-interface" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "051d31803f873cabe71aec3c1b849f35248beae5d19a347d93a5c9cccc5d5a9b" +checksum = "825a69d531baa1ff261a29b98fcf08e134249e624052a6b60183bb2eab2fa8ae" dependencies = [ "arrayref", "bytemuck", "solana-program", - "spl-discriminator", - "spl-pod", - "spl-program-error", - "spl-tlv-account-resolution", - "spl-type-length-value", + "spl-discriminator 0.1.0", + "spl-pod 0.1.0", + "spl-program-error 0.3.0", + "spl-tlv-account-resolution 0.5.1", + "spl-type-length-value 0.3.0", +] + +[[package]] +name = "spl-transfer-hook-interface" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9b5c08a89838e5a2931f79b17f611857f281a14a2100968a3ccef352cb7414b" +dependencies = [ + "arrayref", + "bytemuck", + "solana-program", + "spl-discriminator 0.2.5", + "spl-pod 0.2.5", + "spl-program-error 0.4.4", + "spl-tlv-account-resolution 0.6.5", + "spl-type-length-value 0.4.6", ] [[package]] @@ -4433,9 +4632,22 @@ checksum = "a468e6f6371f9c69aae760186ea9f1a01c2908351b06a5e0026d21cfc4d7ecac" dependencies = [ "bytemuck", "solana-program", - "spl-discriminator", - "spl-pod", - "spl-program-error", + "spl-discriminator 0.1.0", + "spl-pod 0.1.0", + "spl-program-error 0.3.0", +] + +[[package]] +name = "spl-type-length-value" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c872f93d0600e743116501eba2d53460e73a12c9a496875a42a7d70e034fe06d" +dependencies = [ + "bytemuck", + "solana-program", + "spl-discriminator 0.2.5", + "spl-pod 0.2.5", + "spl-program-error 0.4.4", ] [[package]] @@ -4456,6 +4668,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "subtle" version = "2.4.1" @@ -4464,35 +4682,36 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "0.15.44" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "unicode-xid 0.1.0", + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] name = "syn" -version = "1.0.109" +version = "2.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "unicode-ident", ] [[package]] -name = "syn" -version = "2.0.63" +name = "syn_derive" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" +checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "unicode-ident", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.85", ] [[package]] @@ -4507,10 +4726,10 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", + "proc-macro2", + "quote", "syn 1.0.109", - "unicode-xid 0.2.6", + "unicode-xid", ] [[package]] @@ -4536,14 +4755,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.10.1" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" dependencies = [ "cfg-if", "fastrand", + "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -4572,22 +4792,22 @@ checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" [[package]] name = "thiserror" -version = "1.0.60" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" +checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.60" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" +checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.63", + "proc-macro2", + "quote", + "syn 2.0.85", ] [[package]] @@ -4642,9 +4862,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] @@ -4657,9 +4877,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.40.0" +version = "1.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb" dependencies = [ "backtrace", "bytes", @@ -4668,7 +4888,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.7", + "socket2", "tokio-macros", "windows-sys 0.52.0", ] @@ -4679,20 +4899,9 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.63", -] - -[[package]] -name = "tokio-rustls" -version = "0.23.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" -dependencies = [ - "rustls 0.20.9", - "tokio", - "webpki", + "proc-macro2", + "quote", + "syn 2.0.85", ] [[package]] @@ -4701,7 +4910,7 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls 0.21.12", + "rustls", "tokio", ] @@ -4718,18 +4927,17 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.17.2" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181" +checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" dependencies = [ "futures-util", "log", - "rustls 0.20.9", + "rustls", "tokio", - "tokio-rustls 0.23.4", + "tokio-rustls", "tungstenite", - "webpki", - "webpki-roots 0.22.6", + "webpki-roots 0.25.4", ] [[package]] @@ -4754,11 +4962,26 @@ dependencies = [ "serde", ] +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.22.22", +] + [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" @@ -4766,9 +4989,22 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.6.0", "toml_datetime", - "winnow", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap 2.6.0", + "serde", + "serde_spanned", + "toml_datetime", + "winnow 0.6.20", ] [[package]] @@ -4783,6 +5019,7 @@ version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -4794,9 +5031,9 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.63", + "proc-macro2", + "quote", + "syn 2.0.85", ] [[package]] @@ -4816,24 +5053,23 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tungstenite" -version = "0.17.3" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" +checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" dependencies = [ - "base64 0.13.1", "byteorder", "bytes", + "data-encoding", "http", "httparse", "log", "rand 0.8.5", - "rustls 0.20.9", - "sha-1", + "rustls", + "sha1", "thiserror", "url", "utf-8", - "webpki", - "webpki-roots 0.22.6", + "webpki-roots 0.24.0", ] [[package]] @@ -4856,24 +5092,24 @@ checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" @@ -4881,12 +5117,6 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" -[[package]] -name = "unicode-xid" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" - [[package]] name = "unicode-xid" version = "0.2.6" @@ -4959,9 +5189,9 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "void" @@ -5001,34 +5231,35 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.63", + "proc-macro2", + "quote", + "syn 2.0.85", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.42" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" dependencies = [ "cfg-if", "js-sys", @@ -5038,60 +5269,50 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" dependencies = [ - "quote 1.0.36", + "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.63", + "proc-macro2", + "quote", + "syn 2.0.85", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" [[package]] name = "web-sys" -version = "0.3.69" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" dependencies = [ "js-sys", "wasm-bindgen", ] -[[package]] -name = "webpki" -version = "0.22.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" -dependencies = [ - "ring 0.17.8", - "untrusted 0.9.0", -] - [[package]] name = "webpki-roots" -version = "0.22.6" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +checksum = "b291546d5d9d1eab74f069c77749f2cb8504a12caa20f0f2de93ddbf6f411888" dependencies = [ - "webpki", + "rustls-webpki", ] [[package]] @@ -5118,11 +5339,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -5137,40 +5358,34 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] name = "windows-sys" -version = "0.42.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets 0.48.5", ] [[package]] name = "windows-sys" -version = "0.48.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] name = "windows-sys" -version = "0.52.0" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -5190,26 +5405,20 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "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.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "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]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -5218,15 +5427,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -5236,15 +5439,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -5254,21 +5451,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -5278,15 +5469,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -5296,15 +5481,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -5314,15 +5493,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -5332,9 +5505,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" @@ -5345,6 +5518,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.50.0" @@ -5384,22 +5566,23 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.34" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ + "byteorder", "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.34" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.63", + "proc-macro2", + "quote", + "syn 2.0.85", ] [[package]] @@ -5417,9 +5600,9 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ - "proc-macro2 1.0.82", - "quote 1.0.36", - "syn 2.0.63", + "proc-macro2", + "quote", + "syn 2.0.85", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 7452e1a..b653d48 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,13 +17,13 @@ incremental = false codegen-units = 1 [workspace.dependencies] -anchor-lang = "0.28.0" -anchor-spl = "0.28.0" -anchor-client = "0.28.0" +anchor-lang = "0.30.1" +anchor-spl = "0.30.1" +anchor-client = "0.30.1" clap = { version = "3.2.25", features = ["derive", "env"] } csv = "1.3.0" serde = "1.0.137" -solana-program = { version = "=1.16.16" } -solana-rpc-client = { version = "=1.16.16" } -solana-sdk = { version = "=1.16.16" } +solana-program = { version = "1.18.21" } +solana-rpc-client = { version = "1.18.21" } +solana-sdk = { version = "1.18.21" } spl-associated-token-account = "2.2.0" \ No newline at end of file diff --git a/README.md b/README.md index bd94215..9c1e5a4 100644 --- a/README.md +++ b/README.md @@ -5,21 +5,23 @@ Open source program to allow user to lock token based on a vesting plan ## For developer To run all the test, please run the following commands: -``` + +```bash anchor test -- --features localnet ``` Build programs: -``` + +```bash anchor build ``` - ## Audit -Jupiter Locker has been audited by Sec3 (prev.Soteria) and OtterSec. View the audit report [here](./audits). +Jupiter Locker has been audited by Sec3 (prev.Soteria) and OtterSec. View the audit report [here](./audits). ## License + Anchor is licensed under [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0). Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in Jupiter Locker by you, as defined in the Apache-2.0 license, shall be licensed as above, without any additional terms or conditions. \ No newline at end of file diff --git a/artifacts/transfer_hook_counter.so b/artifacts/transfer_hook_counter.so new file mode 100755 index 0000000..2327dd7 Binary files /dev/null and b/artifacts/transfer_hook_counter.so differ diff --git a/cli/src/bin/cli.rs b/cli/src/bin/cli.rs index 9070213..8126ec5 100644 --- a/cli/src/bin/cli.rs +++ b/cli/src/bin/cli.rs @@ -57,6 +57,9 @@ pub struct InitializeLockEscrowFromFileArgs { /// update recipient mode #[clap(long, env)] pub update_recipient_mode: u8, + /// cancel mode + #[clap(long, env)] + pub cancel_mode: u8, } fn main() { diff --git a/cli/src/bin/instructions/initialize_lock_escrow_from_file.rs b/cli/src/bin/instructions/initialize_lock_escrow_from_file.rs index 34c2b3c..aa42a95 100644 --- a/cli/src/bin/instructions/initialize_lock_escrow_from_file.rs +++ b/cli/src/bin/instructions/initialize_lock_escrow_from_file.rs @@ -39,6 +39,7 @@ fn create_lock_escrow(args: &Args, sub_args: &InitializeLockEscrowFromFileArgs) frequency, number_of_period, update_recipient_mode, + cancel_mode, } = sub_args; let file = File::open(sub_args.wallet_path.clone())?; let mut rdr = csv::Reader::from_reader(file); @@ -63,6 +64,7 @@ fn create_lock_escrow(args: &Args, sub_args: &InitializeLockEscrowFromFileArgs) amount_per_period: entry.amount_per_period, number_of_period, update_recipient_mode, + cancel_mode, }, )?; println!( @@ -84,6 +86,7 @@ pub struct LockEscrowForAnUserParam { pub amount_per_period: u64, pub number_of_period: u64, pub update_recipient_mode: u8, + pub cancel_mode: u8, } fn create_lock_escrow_for_an_user( args: &Args, @@ -99,6 +102,7 @@ fn create_lock_escrow_for_an_user( amount_per_period, number_of_period, update_recipient_mode, + cancel_mode, } = sub_args; let client = RpcClient::new_with_commitment(args.rpc_url.clone(), CommitmentConfig::finalized()); @@ -157,6 +161,7 @@ fn create_lock_escrow_for_an_user( amount_per_period, number_of_period, update_recipient_mode, + cancel_mode, }, } .data(), diff --git a/package.json b/package.json index 3b0c252..46243ae 100644 --- a/package.json +++ b/package.json @@ -1,20 +1,21 @@ { - "scripts": { - "lint:fix": "prettier */*.js \"*/**/*{.js,.ts}\" -w", - "lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check" - }, - "dependencies": { - "@coral-xyz/anchor": "^0.28.0", - "@solana/spl-token": "^0.3.8" - }, - "devDependencies": { - "chai": "^4.3.4", - "mocha": "^9.0.3", - "ts-mocha": "^10.0.0", - "@types/bn.js": "^5.1.0", - "@types/chai": "^4.3.0", - "@types/mocha": "^9.0.0", - "typescript": "^4.3.5", - "prettier": "^2.6.2" - } -} \ No newline at end of file + "scripts": { + "lint:fix": "prettier */*.js \"*/**/*{.js,.ts}\" -w", + "lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check" + }, + "dependencies": { + "@coral-xyz/anchor": "^0.30.1", + "@solana/spl-token": "^0.4.8", + "tiny-invariant": "^1.3.3" + }, + "devDependencies": { + "@types/bn.js": "^5.1.0", + "@types/chai": "^4.3.0", + "@types/mocha": "^9.0.0", + "chai": "^4.3.4", + "mocha": "^9.0.3", + "prettier": "^2.6.2", + "ts-mocha": "^10.0.0", + "typescript": "^4.3.5" + } +} diff --git a/programs/locker/Cargo.toml b/programs/locker/Cargo.toml index 374b35e..65ca86b 100644 --- a/programs/locker/Cargo.toml +++ b/programs/locker/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "locker" -version = "0.2.2" +version = "0.3.0" description = "Created with Anchor" edition = "2021" authors = ["andrew "] @@ -17,10 +17,13 @@ cpi = ["no-entrypoint"] default = [] localnet = [] staging = [] +idl-build = ["anchor-lang/idl-build", "anchor-spl/idl-build"] [dependencies] -anchor-lang = { version = "0.28.0", features = ["event-cpi"] } -anchor-spl = "0.28.0" +anchor-lang = { workspace = true, features = ["event-cpi"] } +anchor-spl = { workspace = true, features = ["memo"] } +spl-transfer-hook-interface = "0.5.0" +solana-program = {workspace = true} bytemuck = { version = "1.13.1", features = ["derive", "min_const_generics"] } static_assertions = "1.1.0" num_enum = "0.7.1" diff --git a/programs/locker/src/errors.rs b/programs/locker/src/errors.rs index 3d19229..d563245 100644 --- a/programs/locker/src/errors.rs +++ b/programs/locker/src/errors.rs @@ -15,15 +15,48 @@ pub enum LockerError { #[msg("Invalid update recipient mode")] InvalidUpdateRecipientMode, + #[msg("Invalid cancel mode")] + InvalidCancelMode, + #[msg("Not permit to do this action")] NotPermitToDoThisAction, #[msg("Invalid recipient token account")] InvalidRecipientTokenAccount, + #[msg("Invalid creator token account")] + InvalidCreatorTokenAccount, + #[msg("Invalid escrow metadata")] InvalidEscrowMetadata, #[msg("Invalid vesting start time")] InvalidVestingStartTime, + + #[msg("Already cancelled")] + AlreadyCancelled, + + #[msg("Cancelled timestamp is zero")] + CancelledAtIsZero, + + #[msg("Invalid token program ID")] + IncorrectTokenProgramId, + + #[msg("Calculate transfer fee failure")] + TransferFeeCalculationFailure, + + #[msg("Unsupported mint")] + UnsupportedMint, + + #[msg("Invalid remaining accounts")] + InvalidRemainingAccountSlice, + + #[msg("Insufficient remaining accounts")] + InsufficientRemainingAccounts, + + #[msg("Same accounts type is provided more than once")] + DuplicatedRemainingAccountTypes, + + #[msg("Missing remaining accounts for transfer hook.")] + NoTransferHookProgram, } diff --git a/programs/locker/src/events.rs b/programs/locker/src/events.rs index ff6d1d4..ae52dd5 100644 --- a/programs/locker/src/events.rs +++ b/programs/locker/src/events.rs @@ -9,6 +9,7 @@ pub struct EventCreateVestingEscrow { pub amount_per_period: u64, pub number_of_period: u64, pub update_recipient_mode: u8, + pub cancel_mode: u8, pub recipient: Pubkey, pub escrow: Pubkey, } @@ -27,3 +28,13 @@ pub struct EventUpdateVestingEscrowRecipient { pub new_recipient: Pubkey, pub signer: Pubkey, } + + +#[event] +pub struct EventCancelVestingEscrow { + pub escrow: Pubkey, + pub signer: Pubkey, + pub claimable_amount: u64, + pub remaining_amount: u64, + pub cancelled_at: u64, +} \ No newline at end of file diff --git a/programs/locker/src/instructions/claim.rs b/programs/locker/src/instructions/claim.rs index 2f62867..0585c79 100644 --- a/programs/locker/src/instructions/claim.rs +++ b/programs/locker/src/instructions/claim.rs @@ -1,77 +1,57 @@ +use anchor_spl::token::{Token, TokenAccount}; + +use crate::util::token::transfer_to_user; use crate::*; -use anchor_spl::token::{Token, TokenAccount, Transfer}; #[event_cpi] #[derive(Accounts)] pub struct ClaimCtx<'info> { - #[account(mut, has_one = recipient)] + /// Escrow. + #[account( + mut, + has_one = recipient, + constraint = escrow.load()?.cancelled_at == 0 @ LockerError::AlreadyCancelled + )] pub escrow: AccountLoader<'info, VestingEscrow>, - #[account(mut)] + /// Escrow Token Account. + #[account( + mut, + associated_token::mint = escrow.load()?.token_mint, + associated_token::authority = escrow + )] pub escrow_token: Box>, + /// Recipient. #[account(mut)] pub recipient: Signer<'info>, - #[account(mut, constraint = recipient_token.key() != escrow_token.key() @ LockerError::InvalidRecipientTokenAccount)] + /// Recipient Token Account. + #[account( + mut, + constraint = recipient_token.key() != escrow_token.key() @ LockerError::InvalidRecipientTokenAccount + )] pub recipient_token: Box>, /// Token program. pub token_program: Program<'info, Token>, } -impl<'info> ClaimCtx<'info> { - fn transfer_to_recipient(&self, amount: u64) -> Result<()> { - let escrow = self.escrow.load()?; - let escrow_seeds = escrow_seeds!(escrow); - anchor_spl::token::transfer( - CpiContext::new_with_signer( - self.token_program.to_account_info(), - Transfer { - from: self.escrow_token.to_account_info(), - to: self.recipient_token.to_account_info(), - authority: self.escrow.to_account_info(), - }, - &[&escrow_seeds[..]], - ), - amount, - )?; - Ok(()) - } -} - pub fn handle_claim(ctx: Context, max_amount: u64) -> Result<()> { - let current_ts = Clock::get()?.unix_timestamp as u64; let mut escrow = ctx.accounts.escrow.load_mut()?; - let escrow_token = anchor_spl::associated_token::get_associated_token_address( - &ctx.accounts.escrow.key(), - &escrow.token_mint, - ); - - require!( - escrow_token == ctx.accounts.escrow_token.key(), - LockerError::InvalidEscrowTokenAddress - ); - - let claimable_amount = escrow.get_claimable_amount(current_ts)?; - - let amount = claimable_amount.min(max_amount); - escrow.accumulate_claimed_amount(amount)?; - - // localnet debug - #[cfg(feature = "localnet")] - msg!( - "claim amount {} {} {}", - amount, - current_ts, - escrow.cliff_time - ); - + let amount = escrow.claim(max_amount)?; drop(escrow); - ctx.accounts.transfer_to_recipient(amount)?; + transfer_to_user( + &ctx.accounts.escrow, + &ctx.accounts.escrow_token, + &ctx.accounts.recipient_token, + &ctx.accounts.token_program, + amount, + )?; + let current_ts = Clock::get()?.unix_timestamp as u64; emit_cpi!(EventClaim { amount, current_ts, diff --git a/programs/locker/src/instructions/create_vesting_escrow.rs b/programs/locker/src/instructions/create_vesting_escrow.rs index 879c326..a140e21 100644 --- a/programs/locker/src/instructions/create_vesting_escrow.rs +++ b/programs/locker/src/instructions/create_vesting_escrow.rs @@ -1,8 +1,11 @@ +use anchor_spl::token::{Token, TokenAccount}; + use crate::safe_math::SafeMath; +use crate::util::token::transfer_to_escrow; +use crate::TokenProgramFlag::UseSplToken; use crate::*; -use anchor_spl::token::{Token, TokenAccount, Transfer}; -#[derive(AnchorSerialize, AnchorDeserialize)] +#[derive(AnchorSerialize, AnchorDeserialize)] /// Accounts for [locker::create_vesting_escrow]. pub struct CreateVestingEscrowParameters { pub vesting_start_time: u64, @@ -12,6 +15,7 @@ pub struct CreateVestingEscrowParameters { pub amount_per_period: u64, pub number_of_period: u64, pub update_recipient_mode: u8, + pub cancel_mode: u8, } impl CreateVestingEscrowParameters { @@ -19,16 +23,73 @@ impl CreateVestingEscrowParameters { let total_amount = self .cliff_unlock_amount .safe_add(self.amount_per_period.safe_mul(self.number_of_period)?)?; + Ok(total_amount) } + + fn validate(&self) -> Result<()> { + require!( + self.cliff_time >= self.vesting_start_time, + LockerError::InvalidVestingStartTime + ); + + require!( + UpdateRecipientMode::try_from(self.update_recipient_mode).is_ok(), + LockerError::InvalidUpdateRecipientMode, + ); + + require!( + CancelMode::try_from(self.cancel_mode).is_ok(), + LockerError::InvalidCancelMode, + ); + + require!(self.frequency != 0, LockerError::FrequencyIsZero); + + Ok(()) + } + + pub fn init_escrow( + &self, + vesting_escrow: &AccountLoader, + recipient: Pubkey, + token_mint: Pubkey, + sender: Pubkey, + base: Pubkey, + escrow_bump: u8, + token_program_flag: TokenProgramFlag, + ) -> Result<()> { + self.validate()?; + + let mut escrow = vesting_escrow.load_init()?; + escrow.init( + self.vesting_start_time, + self.cliff_time, + self.frequency, + self.cliff_unlock_amount, + self.amount_per_period, + self.number_of_period, + recipient, + token_mint, + sender, + base, + escrow_bump, + self.update_recipient_mode, + self.cancel_mode, + token_program_flag.into(), + ); + + Ok(()) + } } #[event_cpi] #[derive(Accounts)] pub struct CreateVestingEscrowCtx<'info> { + /// Base. #[account(mut)] pub base: Signer<'info>, + /// Escrow. #[account( init, seeds = [ @@ -41,6 +102,7 @@ pub struct CreateVestingEscrowCtx<'info> { )] pub escrow: AccountLoader<'info, VestingEscrow>, + /// Escrow Token Account. #[account( mut, associated_token::mint = sender_token.mint, @@ -48,19 +110,21 @@ pub struct CreateVestingEscrowCtx<'info> { )] pub escrow_token: Box>, + /// Sender. #[account(mut)] pub sender: Signer<'info>, + /// Sender Token Account. #[account(mut)] pub sender_token: Box>, - /// CHECK: recipient account + /// CHECK: recipient account. pub recipient: UncheckedAccount<'info>, /// Token program. pub token_program: Program<'info, Token>, - // system program + /// system program. pub system_program: Program<'info, System>, } @@ -68,56 +132,34 @@ pub fn handle_create_vesting_escrow( ctx: Context, params: &CreateVestingEscrowParameters, ) -> Result<()> { - let &CreateVestingEscrowParameters { - vesting_start_time, - cliff_time, - frequency, - cliff_unlock_amount, - amount_per_period, - number_of_period, - update_recipient_mode, - } = params; - - require!( - cliff_time >= vesting_start_time, - LockerError::InvalidVestingStartTime - ); - - require!( - UpdateRecipientMode::try_from(update_recipient_mode).is_ok(), - LockerError::InvalidUpdateRecipientMode, - ); + params.init_escrow( + &ctx.accounts.escrow, + ctx.accounts.recipient.key(), + ctx.accounts.sender_token.mint, + ctx.accounts.sender.key(), + ctx.accounts.base.key(), + ctx.bumps.escrow, + UseSplToken, + )?; - require!(frequency != 0, LockerError::FrequencyIsZero); + transfer_to_escrow( + &ctx.accounts.sender, + &ctx.accounts.sender_token, + &ctx.accounts.escrow_token, + &ctx.accounts.token_program, + params.get_total_deposit_amount()?, + )?; - let mut escrow = ctx.accounts.escrow.load_init()?; - escrow.init( + let &CreateVestingEscrowParameters { vesting_start_time, cliff_time, frequency, cliff_unlock_amount, amount_per_period, number_of_period, - ctx.accounts.recipient.key(), - ctx.accounts.sender_token.mint, - ctx.accounts.sender.key(), - ctx.accounts.base.key(), - *ctx.bumps.get("escrow").unwrap(), update_recipient_mode, - ); - - anchor_spl::token::transfer( - CpiContext::new( - ctx.accounts.token_program.to_account_info(), - Transfer { - from: ctx.accounts.sender_token.to_account_info(), - to: ctx.accounts.escrow_token.to_account_info(), - authority: ctx.accounts.sender.to_account_info(), - }, - ), - params.get_total_deposit_amount()?, - )?; - + cancel_mode, + } = params; emit_cpi!(EventCreateVestingEscrow { cliff_time, frequency, @@ -128,6 +170,7 @@ pub fn handle_create_vesting_escrow( escrow: ctx.accounts.escrow.key(), update_recipient_mode, vesting_start_time, + cancel_mode, }); Ok(()) } diff --git a/programs/locker/src/instructions/create_vesting_escrow_metadata.rs b/programs/locker/src/instructions/create_vesting_escrow_metadata.rs index e36e387..eced150 100644 --- a/programs/locker/src/instructions/create_vesting_escrow_metadata.rs +++ b/programs/locker/src/instructions/create_vesting_escrow_metadata.rs @@ -32,7 +32,7 @@ pub struct CreateVestingEscrowMetadataCtx<'info> { /// Payer of the [ProposalMeta]. #[account(mut)] pub payer: Signer<'info>, - /// System program. + /// system program. pub system_program: Program<'info, System>, } diff --git a/programs/locker/src/instructions/mod.rs b/programs/locker/src/instructions/mod.rs index f0af244..333d0bc 100644 --- a/programs/locker/src/instructions/mod.rs +++ b/programs/locker/src/instructions/mod.rs @@ -1,11 +1,16 @@ -pub mod create_vesting_escrow; +pub use claim::*; pub use create_vesting_escrow::*; +pub use create_vesting_escrow_metadata::*; +pub use update_vesting_escrow_recipient::*; + +pub mod create_vesting_escrow; pub mod claim; -pub use claim::*; pub mod create_vesting_escrow_metadata; -pub use create_vesting_escrow_metadata::*; pub mod update_vesting_escrow_recipient; -pub use update_vesting_escrow_recipient::*; + +pub mod v2; + +pub use v2::*; diff --git a/programs/locker/src/instructions/update_vesting_escrow_recipient.rs b/programs/locker/src/instructions/update_vesting_escrow_recipient.rs index 873fb93..ae633b2 100644 --- a/programs/locker/src/instructions/update_vesting_escrow_recipient.rs +++ b/programs/locker/src/instructions/update_vesting_escrow_recipient.rs @@ -5,7 +5,10 @@ use crate::*; #[event_cpi] pub struct UpdateVestingEscrowRecipientCtx<'info> { /// Escrow. - #[account(mut)] + #[account( + mut, + constraint = escrow.load()?.cancelled_at == 0 @ LockerError::AlreadyCancelled + )] pub escrow: AccountLoader<'info, VestingEscrow>, /// Escrow metadata. @@ -15,7 +18,8 @@ pub struct UpdateVestingEscrowRecipientCtx<'info> { /// Signer. #[account(mut)] pub signer: Signer<'info>, - /// System program. + + /// system program. pub system_program: Program<'info, System>, } @@ -27,33 +31,7 @@ pub fn handle_update_vesting_escrow_recipient( let mut escrow = ctx.accounts.escrow.load_mut()?; let old_recipient = escrow.recipient; let signer = ctx.accounts.signer.key(); - let update_recipient_mode = - UpdateRecipientMode::try_from(escrow.update_recipient_mode).unwrap(); - - match update_recipient_mode { - UpdateRecipientMode::NeitherCreatorOrRecipient => { - return Err(LockerError::NotPermitToDoThisAction.into()); - } - UpdateRecipientMode::OnlyCreator => { - require!( - signer == escrow.creator, - LockerError::NotPermitToDoThisAction - ); - } - UpdateRecipientMode::OnlyRecipient => { - require!( - signer == escrow.recipient, - LockerError::NotPermitToDoThisAction - ); - } - UpdateRecipientMode::EitherCreatorAndRecipient => { - require!( - signer == escrow.creator || signer == escrow.recipient, - LockerError::NotPermitToDoThisAction - ); - } - } - + escrow.validate_update_actor(signer)?; escrow.update_recipient(new_recipient); if let Some(recipient_email) = new_recipient_email { diff --git a/programs/locker/src/instructions/v2/cancel_vesting_escrow.rs b/programs/locker/src/instructions/v2/cancel_vesting_escrow.rs new file mode 100644 index 0000000..8855057 --- /dev/null +++ b/programs/locker/src/instructions/v2/cancel_vesting_escrow.rs @@ -0,0 +1,168 @@ +use anchor_spl::memo::Memo; +use anchor_spl::token_interface::{ + close_account, CloseAccount, Mint, TokenAccount, TokenInterface, +}; +use util::{ + harvest_fees, parse_remaining_accounts, AccountsType, ParsedRemainingAccounts, + TRANSFER_MEMO_CANCEL_VESTING, +}; + +use crate::safe_math::SafeMath; +use crate::util::{transfer_to_user_v2, MemoTransferContext}; +use crate::*; + +/// Accounts for [locker::cancel_vesting_escrow]. +#[derive(Accounts)] +#[event_cpi] +pub struct CancelVestingEscrow<'info> { + /// Escrow. + #[account( + mut, + has_one = token_mint, + constraint = escrow.load()?.cancelled_at == 0 @ LockerError::AlreadyCancelled + )] + pub escrow: AccountLoader<'info, VestingEscrow>, + + /// Mint. + #[account(mut)] + pub token_mint: Box>, + + /// Escrow Token Account. + #[account( + mut, + associated_token::mint = token_mint, + associated_token::authority = escrow, + associated_token::token_program = token_program + )] + pub escrow_token: Box>, + + /// Creator Token Account. + #[account( + mut, + associated_token::mint = token_mint, + associated_token::authority = escrow.load()?.creator, + associated_token::token_program = token_program + )] + pub creator_token: Box>, + + /// Receipient Token Account. + #[account( + mut, + associated_token::mint = token_mint, + associated_token::authority = escrow.load()?.recipient, + associated_token::token_program = token_program + )] + pub recipient_token: Box>, + + /// CHECKED: The Token Account will receive the rent + #[account(mut)] + pub rent_receiver: UncheckedAccount<'info>, + + /// Signer. + #[account(mut)] + pub signer: Signer<'info>, + + /// Memo program. + pub memo_program: Program<'info, Memo>, + + /// Token program. + pub token_program: Interface<'info, TokenInterface>, +} + +impl<'info> CancelVestingEscrow<'info> { + fn close_escrow_token(&self) -> Result<()> { + let escrow = self.escrow.load()?; + let escrow_seeds = escrow_seeds!(escrow); + + close_account(CpiContext::new_with_signer( + self.token_program.to_account_info(), + CloseAccount { + account: self.escrow_token.to_account_info(), + destination: self.rent_receiver.to_account_info(), + authority: self.escrow.to_account_info(), + }, + &[&escrow_seeds[..]], + ))?; + + Ok(()) + } +} + +pub fn handle_cancel_vesting_escrow<'c: 'info, 'info>( + ctx: Context<'_, '_, 'c, 'info, CancelVestingEscrow<'info>>, + remaining_accounts_info: Option, +) -> Result<()> { + let mut escrow = ctx.accounts.escrow.load_mut()?; + let signer = ctx.accounts.signer.key(); + escrow.validate_cancel_actor(signer)?; + + let current_ts = Clock::get()?.unix_timestamp as u64; + let claimable_amount = escrow.get_claimable_amount(current_ts)?; + let remaining_amount = ctx + .accounts + .escrow_token + .amount + .safe_sub(claimable_amount)?; + escrow.cancelled_at = current_ts; + require!(escrow.cancelled_at > 0, LockerError::CancelledAtIsZero); + drop(escrow); + + // Process remaining accounts + let mut remaining_accounts = &ctx.remaining_accounts[..]; + let parsed_transfer_hook_accounts = match remaining_accounts_info { + Some(info) => parse_remaining_accounts( + &mut remaining_accounts, + &info.slices, + &[AccountsType::TransferHookEscrow], + )?, + None => ParsedRemainingAccounts::default(), + }; + + // Transfer the claimable amount to the recipient + transfer_to_user_v2( + &ctx.accounts.escrow, + &ctx.accounts.token_mint, + &ctx.accounts.escrow_token, + &ctx.accounts.recipient_token, + &ctx.accounts.token_program, + Some(MemoTransferContext { + memo_program: &ctx.accounts.memo_program, + memo: TRANSFER_MEMO_CANCEL_VESTING.as_bytes(), + }), + claimable_amount, + parsed_transfer_hook_accounts.transfer_hook_escrow, + )?; + + // Transfer the remaining amount to the creator + transfer_to_user_v2( + &ctx.accounts.escrow, + &ctx.accounts.token_mint, + &ctx.accounts.escrow_token, + &ctx.accounts.creator_token, + &ctx.accounts.token_program, + Some(MemoTransferContext { + memo_program: &ctx.accounts.memo_program, + memo: TRANSFER_MEMO_CANCEL_VESTING.as_bytes(), + }), + remaining_amount, + parsed_transfer_hook_accounts.transfer_hook_escrow, + )?; + + // Do fee harvesting + harvest_fees( + &ctx.accounts.token_program, + &ctx.accounts.escrow_token, + &ctx.accounts.token_mint, + )?; + + ctx.accounts.close_escrow_token()?; + + emit_cpi!(EventCancelVestingEscrow { + escrow: ctx.accounts.escrow.key(), + signer, + claimable_amount, + remaining_amount, + cancelled_at: current_ts, + }); + Ok(()) +} diff --git a/programs/locker/src/instructions/v2/claim.rs b/programs/locker/src/instructions/v2/claim.rs new file mode 100644 index 0000000..4c31ddb --- /dev/null +++ b/programs/locker/src/instructions/v2/claim.rs @@ -0,0 +1,96 @@ +use anchor_spl::memo::Memo; +use anchor_spl::token_interface::{ + Mint, TokenAccount, TokenInterface, +}; +use util::{parse_remaining_accounts, AccountsType, ParsedRemainingAccounts, TRANSFER_MEMO_CLAIM_VESTING}; + +use crate::*; +use crate::util::{MemoTransferContext, transfer_to_user_v2}; + +#[event_cpi] +#[derive(Accounts)] +pub struct ClaimV2<'info> { + /// Escrow. + #[account( + mut, + has_one = token_mint, + has_one = recipient, + constraint = escrow.load()?.cancelled_at == 0 @ LockerError::AlreadyCancelled + )] + pub escrow: AccountLoader<'info, VestingEscrow>, + + /// Mint. + pub token_mint: Box>, + + /// Escrow Token Account. + #[account( + mut, + associated_token::mint = token_mint, + associated_token::authority = escrow, + associated_token::token_program = token_program + )] + pub escrow_token: Box>, + + /// Recipient. + #[account(mut)] + pub recipient: Signer<'info>, + + /// Recipient Token Account. + #[account( + mut, + constraint = recipient_token.key() != escrow_token.key() @ LockerError::InvalidRecipientTokenAccount + )] + pub recipient_token: Box>, + + /// Memo program. + pub memo_program: Program<'info, Memo>, + + /// Token program. + pub token_program: Interface<'info, TokenInterface>, +} + +pub fn handle_claim_v2<'c: 'info, 'info>( + ctx: Context<'_, '_, 'c, 'info, ClaimV2<'info>>, + max_amount: u64, + remaining_accounts_info: Option, +) -> Result<()> { + let mut escrow = ctx.accounts.escrow.load_mut()?; + + let amount = escrow.claim(max_amount)?; + drop(escrow); + + // Process remaining accounts + let mut remaining_accounts = &ctx.remaining_accounts[..]; + let parsed_transfer_hook_accounts = match remaining_accounts_info { + Some(info) => parse_remaining_accounts( + &mut remaining_accounts, + &info.slices, + &[ + AccountsType::TransferHookEscrow, + ], + )?, + None => ParsedRemainingAccounts::default(), + }; + + transfer_to_user_v2( + &ctx.accounts.escrow, + &ctx.accounts.token_mint, + &ctx.accounts.escrow_token, + &ctx.accounts.recipient_token, + &ctx.accounts.token_program, + Some(MemoTransferContext { + memo_program: &ctx.accounts.memo_program, + memo: TRANSFER_MEMO_CLAIM_VESTING.as_bytes(), + }), + amount, + parsed_transfer_hook_accounts.transfer_hook_escrow, + )?; + + let current_ts = Clock::get()?.unix_timestamp as u64; + emit_cpi!(EventClaim { + amount, + current_ts, + escrow: ctx.accounts.escrow.key(), + }); + Ok(()) +} diff --git a/programs/locker/src/instructions/v2/create_vesting_escrow.rs b/programs/locker/src/instructions/v2/create_vesting_escrow.rs new file mode 100644 index 0000000..df7a6e7 --- /dev/null +++ b/programs/locker/src/instructions/v2/create_vesting_escrow.rs @@ -0,0 +1,134 @@ +use anchor_spl::token::spl_token; +use anchor_spl::token_2022::spl_token_2022; +use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface}; + +use crate::util::{ + calculate_transfer_fee_included_amount, parse_remaining_accounts, transfer_to_escrow_v2, + validate_mint, AccountsType, ParsedRemainingAccounts, +}; +use crate::TokenProgramFlag::{UseSplToken, UseToken2022}; +use crate::*; + +#[event_cpi] +#[derive(Accounts)] +pub struct CreateVestingEscrowV2<'info> { + /// Base. + #[account(mut)] + pub base: Signer<'info>, + + /// Escrow. + #[account( + init, + seeds = [ + b"escrow".as_ref(), + base.key().as_ref(), + ], + bump, + payer = sender, + space = 8 + VestingEscrow::INIT_SPACE + )] + pub escrow: AccountLoader<'info, VestingEscrow>, + + // Mint. + pub token_mint: Box>, + + /// Escrow Token Account. + #[account( + mut, + associated_token::mint = token_mint, + associated_token::authority = escrow, + associated_token::token_program = token_program + )] + pub escrow_token: Box>, + + /// Sender. + #[account(mut)] + pub sender: Signer<'info>, + + /// Sender Token Account. + #[account(mut)] + pub sender_token: Box>, + + /// CHECK: recipient account + pub recipient: UncheckedAccount<'info>, + + /// Token program. + pub token_program: Interface<'info, TokenInterface>, + + /// system program. + pub system_program: Program<'info, System>, +} + +pub fn handle_create_vesting_escrow_v2<'c: 'info, 'info>( + ctx: Context<'_, '_, 'c, 'info, CreateVestingEscrowV2<'info>>, + params: &CreateVestingEscrowParameters, + remaining_accounts_info: Option, +) -> Result<()> { + // Validate if token_mint is supported + validate_mint(&ctx.accounts.token_mint)?; + + let token_mint_info = ctx.accounts.token_mint.to_account_info(); + let token_program_flag = match *token_mint_info.owner { + spl_token::ID => Ok(UseSplToken), + spl_token_2022::ID => Ok(UseToken2022), + _ => Err(LockerError::IncorrectTokenProgramId), + }?; + + params.init_escrow( + &ctx.accounts.escrow, + ctx.accounts.recipient.key(), + ctx.accounts.sender_token.mint, + ctx.accounts.sender.key(), + ctx.accounts.base.key(), + ctx.bumps.escrow, + token_program_flag, + )?; + + // Process remaining accounts + let mut remaining_accounts = &ctx.remaining_accounts[..]; + let parsed_transfer_hook_accounts = match remaining_accounts_info { + Some(info) => parse_remaining_accounts( + &mut remaining_accounts, + &info.slices, + &[AccountsType::TransferHookEscrow], + )?, + None => ParsedRemainingAccounts::default(), + }; + + transfer_to_escrow_v2( + &ctx.accounts.sender, + &ctx.accounts.token_mint, + &ctx.accounts.sender_token, + &ctx.accounts.escrow_token, + &ctx.accounts.token_program, + calculate_transfer_fee_included_amount( + params.get_total_deposit_amount()?, + &ctx.accounts.token_mint, + )?, + parsed_transfer_hook_accounts.transfer_hook_escrow, + )?; + + let &CreateVestingEscrowParameters { + vesting_start_time, + cliff_time, + frequency, + cliff_unlock_amount, + amount_per_period, + number_of_period, + update_recipient_mode, + cancel_mode, + } = params; + emit_cpi!(EventCreateVestingEscrow { + vesting_start_time, + cliff_time, + frequency, + cliff_unlock_amount, + amount_per_period, + number_of_period, + recipient: ctx.accounts.recipient.key(), + escrow: ctx.accounts.escrow.key(), + update_recipient_mode, + cancel_mode, + }); + Ok(()) +} diff --git a/programs/locker/src/instructions/v2/mod.rs b/programs/locker/src/instructions/v2/mod.rs new file mode 100644 index 0000000..3077458 --- /dev/null +++ b/programs/locker/src/instructions/v2/mod.rs @@ -0,0 +1,7 @@ +pub use cancel_vesting_escrow::*; +pub use claim::*; +pub use create_vesting_escrow::*; + +pub mod cancel_vesting_escrow; +pub mod claim; +pub mod create_vesting_escrow; diff --git a/programs/locker/src/lib.rs b/programs/locker/src/lib.rs index 6219b64..ae4c224 100644 --- a/programs/locker/src/lib.rs +++ b/programs/locker/src/lib.rs @@ -1,21 +1,26 @@ use anchor_lang::prelude::*; +pub use errors::*; +pub use events::*; +pub use instructions::*; +pub use state::*; + +use crate::util::RemainingAccountsInfo; + #[macro_use] pub mod macros; pub mod instructions; -pub use instructions::*; pub mod state; -pub use state::*; pub mod errors; -pub use errors::*; pub mod safe_math; pub mod events; -pub use events::*; + +pub mod util; #[cfg(feature = "localnet")] declare_id!("2r5VekMNiWPzi1pWwvJczrdPaZnJG59u91unSrTunwJg"); @@ -30,6 +35,20 @@ declare_id!("LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn"); pub mod locker { use super::*; + /// Create a vesting escrow for the given params + /// # Arguments + /// + /// * ctx - The accounts needed by instruction. + /// * params - The params needed by instruction. + /// * vesting_start_time - The creation time of this escrow + /// * cliff_time - Trade cliff time of the escrow + /// * frequency - How frequent the claimable amount will be updated + /// * cliff_unlock_amount - The amount unlocked after cliff time + /// * amount_per_period - The amount unlocked per vesting period + /// * number_of_period - The total number of vesting period + /// * update_recipient_mode - Decide who can update the recipient of the escrow + /// * cancel_mode - Decide who can cancel the the escrow + /// pub fn create_vesting_escrow( ctx: Context, params: CreateVestingEscrowParameters, @@ -37,10 +56,26 @@ pub mod locker { handle_create_vesting_escrow(ctx, ¶ms) } + /// Claim maximum amount from the vesting escrow + /// # Arguments + /// + /// * ctx - The accounts needed by instruction. + /// * max_amount - The maximum amount claimed by the recipient + /// pub fn claim(ctx: Context, max_amount: u64) -> Result<()> { handle_claim(ctx, max_amount) } + /// Create vesting escrow metadata + /// # Arguments + /// + /// * ctx - The accounts needed by instruction. + /// * params - The params needed by instruction. + /// * name - The name of the vesting escrow + /// * description - The description of the vesting escrow + /// * creator_email - The email of the creator + /// * recipient_email - The email of the recipient + /// pub fn create_vesting_escrow_metadata( ctx: Context, params: CreateVestingEscrowMetadataParameters, @@ -48,6 +83,13 @@ pub mod locker { handle_create_vesting_escrow_metadata(ctx, ¶ms) } + /// Update vesting escrow metadata + /// # Arguments + /// + /// * ctx - The accounts needed by instruction. + /// * new_recipient - The address of the new recipient + /// * new_recipient_email - The email of the new recipient + /// pub fn update_vesting_escrow_recipient( ctx: Context, new_recipient: Pubkey, @@ -56,5 +98,61 @@ pub mod locker { handle_update_vesting_escrow_recipient(ctx, new_recipient, new_recipient_email) } - // TODO add function to close escrow after all token has been claimed + // V2 instructions + + /// Create a vesting escrow for the given params + /// This instruction supports both splToken and token2022 + /// # Arguments + /// + /// * ctx - The accounts needed by instruction. + /// * params - The params needed by instruction. + /// * vesting_start_time - The creation time of this escrow + /// * cliff_time - Trade cliff time of the escrow + /// * frequency - How frequent the claimable amount will be updated + /// * cliff_unlock_amount - The amount unlocked after cliff time + /// * amount_per_period - The amount unlocked per vesting period + /// * number_of_period - The total number of vesting period + /// * update_recipient_mode - Decide who can update the recipient of the escrow + /// * cancel_mode - Decide who can cancel the the escrow + /// * remaining_accounts_info: additional accounts needed by instruction + /// + pub fn create_vesting_escrow_v2<'c: 'info, 'info>( + ctx: Context<'_, '_, 'c, 'info, CreateVestingEscrowV2<'info>>, + params: CreateVestingEscrowParameters, + remaining_accounts_info: Option, + ) -> Result<()> { + handle_create_vesting_escrow_v2(ctx, ¶ms, remaining_accounts_info) + } + + /// Claim maximum amount from the vesting escrow + /// This instruction supports both splToken and token2022 + /// # Arguments + /// + /// * ctx - The accounts needed by instruction. + /// * max_amount - The maximum amount claimed by the recipient + /// * remaining_accounts_info: additional accounts needed by instruction + /// + pub fn claim_v2<'c: 'info, 'info>( + ctx: Context<'_, '_, 'c, 'info, ClaimV2<'info>>, + max_amount: u64, + remaining_accounts_info: Option, + ) -> Result<()> { + handle_claim_v2(ctx, max_amount, remaining_accounts_info) + } + + /// Cancel a vesting escrow. + /// - The claimable token will be transferred to recipient + /// - The remaining token will be transferred to the creator + /// This instruction supports both splToken and token2022 + /// # Arguments + /// + /// * ctx - The accounts needed by instruction. + /// * remaining_accounts_info: additional accounts needed by instruction + /// + pub fn cancel_vesting_escrow<'c: 'info, 'info>( + ctx: Context<'_, '_, 'c, 'info, CancelVestingEscrow<'info>>, + remaining_accounts_info: Option, + ) -> Result<()> { + handle_cancel_vesting_escrow(ctx, remaining_accounts_info) + } } diff --git a/programs/locker/src/state/mod.rs b/programs/locker/src/state/mod.rs index f510e5f..dd395bd 100644 --- a/programs/locker/src/state/mod.rs +++ b/programs/locker/src/state/mod.rs @@ -1,5 +1,7 @@ -pub mod vesting_escrow; pub use vesting_escrow::*; +pub use vesting_escrow_metadata::*; + +pub mod vesting_escrow; pub mod vesting_escrow_metadata; -pub use vesting_escrow_metadata::*; + diff --git a/programs/locker/src/state/vesting_escrow.rs b/programs/locker/src/state/vesting_escrow.rs index 1cd01cb..d6b71a7 100644 --- a/programs/locker/src/state/vesting_escrow.rs +++ b/programs/locker/src/state/vesting_escrow.rs @@ -1,8 +1,9 @@ +use num_enum::{IntoPrimitive, TryFromPrimitive}; +use static_assertions::const_assert_eq; + use crate::*; use self::safe_math::SafeMath; -use num_enum::{IntoPrimitive, TryFromPrimitive}; -use static_assertions::const_assert_eq; #[derive(Copy, Clone, Debug, PartialEq, Eq, IntoPrimitive, TryFromPrimitive)] #[repr(u8)] @@ -13,6 +14,22 @@ pub enum UpdateRecipientMode { EitherCreatorAndRecipient, //3 } +#[derive(Copy, Clone, Debug, PartialEq, Eq, IntoPrimitive, TryFromPrimitive)] +#[repr(u8)] +pub enum CancelMode { + NeitherCreatorOrRecipient, //0 + OnlyCreator, //1 + OnlyRecipient, //2 + EitherCreatorAndRecipient, //3 +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, IntoPrimitive, TryFromPrimitive)] +#[repr(u8)] +pub enum TokenProgramFlag { + UseSplToken, //0 + UseToken2022, //1 +} + #[account(zero_copy)] #[derive(Default, InitSpace, Debug)] pub struct VestingEscrow { @@ -28,8 +45,12 @@ pub struct VestingEscrow { pub escrow_bump: u8, /// update_recipient_mode pub update_recipient_mode: u8, + /// cancel_mode + pub cancel_mode: u8, + /// token program flag + pub token_program_flag: u8, /// padding - pub padding_0: [u8; 6], + pub padding_0: [u8; 4], /// cliff time pub cliff_time: u64, /// frequency @@ -44,8 +65,12 @@ pub struct VestingEscrow { pub total_claimed_amount: u64, /// vesting start time pub vesting_start_time: u64, + /// cancelled_at + pub cancelled_at: u64, + /// buffer + pub padding_1: u64, /// buffer - pub buffer: [u128; 6], + pub buffer: [u128; 5], } const_assert_eq!(VestingEscrow::INIT_SPACE, 288); // 32 * 4 + 8 * 8 + 16 * 6 @@ -65,6 +90,8 @@ impl VestingEscrow { base: Pubkey, escrow_bump: u8, update_recipient_mode: u8, + cancel_mode: u8, + token_program_flag: u8, ) { self.vesting_start_time = vesting_start_time; self.cliff_time = cliff_time; @@ -78,6 +105,8 @@ impl VestingEscrow { self.base = base; self.escrow_bump = escrow_bump; self.update_recipient_mode = update_recipient_mode; + self.cancel_mode = cancel_mode; + self.token_program_flag = token_program_flag; } pub fn get_max_unlocked_amount(&self, current_ts: u64) -> Result { @@ -107,16 +136,55 @@ impl VestingEscrow { Ok(()) } + pub fn claim(&mut self, max_amount: u64) -> Result { + let current_ts = Clock::get()?.unix_timestamp as u64; + let claimable_amount = self.get_claimable_amount(current_ts)?; + + let amount = claimable_amount.min(max_amount); + self.accumulate_claimed_amount(amount)?; + + Ok(amount) + } + pub fn update_recipient(&mut self, new_recipient: Pubkey) { self.recipient = new_recipient; } + + pub fn validate_cancel_actor(self, signer: Pubkey) -> Result<()> { + require!( + self.cancel_mode & self.signer_flag(signer) > 0, + LockerError::NotPermitToDoThisAction + ); + + Ok(()) + } + + pub fn validate_update_actor(self, signer: Pubkey) -> Result<()> { + require!( + self.update_recipient_mode & self.signer_flag(signer) > 0, + LockerError::NotPermitToDoThisAction + ); + + Ok(()) + } + + fn signer_flag(&self, signer: Pubkey) -> u8 { + if signer == self.creator { + 0x1 + } else if signer == self.recipient { + 0x2 + } else { + 0x0 + } + } } #[cfg(test)] mod escrow_test { - use super::*; use proptest::proptest; + use super::*; + proptest! { #[test] fn test_get_max_unlocked_amount( diff --git a/programs/locker/src/util/constants.rs b/programs/locker/src/util/constants.rs new file mode 100644 index 0000000..0358b7b --- /dev/null +++ b/programs/locker/src/util/constants.rs @@ -0,0 +1,2 @@ +pub const TRANSFER_MEMO_CLAIM_VESTING: &str = "Jup-Lock ClaimVesting"; +pub const TRANSFER_MEMO_CANCEL_VESTING: &str = "Jup-Lock CancelVesting"; diff --git a/programs/locker/src/util/mod.rs b/programs/locker/src/util/mod.rs new file mode 100644 index 0000000..4784db8 --- /dev/null +++ b/programs/locker/src/util/mod.rs @@ -0,0 +1,8 @@ +pub use constants::*; +pub use remaining_accounts::*; +pub use token2022::*; + +pub mod constants; +pub mod remaining_accounts; +pub mod token; +pub mod token2022; diff --git a/programs/locker/src/util/remaining_accounts.rs b/programs/locker/src/util/remaining_accounts.rs new file mode 100644 index 0000000..d4665f6 --- /dev/null +++ b/programs/locker/src/util/remaining_accounts.rs @@ -0,0 +1,65 @@ +use anchor_lang::prelude::*; + +use crate::LockerError; + +#[derive(AnchorSerialize, AnchorDeserialize, Clone, PartialEq, Eq)] +pub enum AccountsType { + TransferHookEscrow, +} + +#[derive(AnchorSerialize, AnchorDeserialize, Clone)] +pub struct RemainingAccountsSlice { + pub accounts_type: AccountsType, + pub length: u8, +} + +#[derive(AnchorSerialize, AnchorDeserialize, Clone)] +pub struct RemainingAccountsInfo { + pub slices: Vec, +} + +#[derive(Default)] +pub struct ParsedRemainingAccounts<'a, 'info> { + pub transfer_hook_escrow: Option<&'a [AccountInfo<'info>]>, +} + +pub fn parse_remaining_accounts<'a, 'info>( + remaining_accounts: &mut &'a [AccountInfo<'info>], + remaining_accounts_slice: &[RemainingAccountsSlice], + valid_accounts_type_list: &[AccountsType], +) -> Result> { + let mut parsed_remaining_accounts = ParsedRemainingAccounts::default(); + + if remaining_accounts_slice.is_empty() { + return Ok(ParsedRemainingAccounts::default()); + } + + for slice in remaining_accounts_slice.iter() { + if !valid_accounts_type_list.contains(&slice.accounts_type) { + return Err(LockerError::InvalidRemainingAccountSlice.into()); + } + + if slice.length == 0 { + continue; + } + + if remaining_accounts.len() < slice.length as usize { + return Err(LockerError::InsufficientRemainingAccounts.into()); + } + + let end_idx = slice.length as usize; + let accounts = &remaining_accounts[0..end_idx]; + *remaining_accounts = &remaining_accounts[end_idx..]; + + match slice.accounts_type { + AccountsType::TransferHookEscrow => { + if parsed_remaining_accounts.transfer_hook_escrow.is_some() { + return Err(LockerError::DuplicatedRemainingAccountTypes.into()); + } + parsed_remaining_accounts.transfer_hook_escrow = Some(accounts); + } + } + } + + Ok(parsed_remaining_accounts) +} diff --git a/programs/locker/src/util/token.rs b/programs/locker/src/util/token.rs new file mode 100644 index 0000000..766ddd3 --- /dev/null +++ b/programs/locker/src/util/token.rs @@ -0,0 +1,51 @@ +use anchor_lang::prelude::*; +use anchor_spl::token::{Token, TokenAccount, Transfer}; + +use crate::VestingEscrow; + +pub fn transfer_to_escrow<'info>( + sender: &Signer<'info>, + sender_token: &Account<'info, TokenAccount>, + escrow_token: &Account<'info, TokenAccount>, + token_program: &Program<'info, Token>, + amount: u64, +) -> Result<()> { + anchor_spl::token::transfer( + CpiContext::new( + token_program.to_account_info(), + Transfer { + from: sender_token.to_account_info(), + to: escrow_token.to_account_info(), + authority: sender.to_account_info(), + }, + ), + amount, + )?; + + Ok(()) +} + +pub fn transfer_to_user<'info>( + escrow: &AccountLoader<'info, VestingEscrow>, + escrow_token: &Account<'info, TokenAccount>, + recipient_token: &Account<'info, TokenAccount>, + token_program: &Program<'info, Token>, + amount: u64, +) -> Result<()> { + let escrow_state = escrow.load()?; + let escrow_seeds = escrow_seeds!(escrow_state); + + anchor_spl::token::transfer( + CpiContext::new_with_signer( + token_program.to_account_info(), + Transfer { + from: escrow_token.to_account_info(), + to: recipient_token.to_account_info(), + authority: escrow.to_account_info(), + }, + &[&escrow_seeds[..]], + ), + amount, + )?; + Ok(()) +} diff --git a/programs/locker/src/util/token2022.rs b/programs/locker/src/util/token2022.rs new file mode 100644 index 0000000..65f669a --- /dev/null +++ b/programs/locker/src/util/token2022.rs @@ -0,0 +1,355 @@ +use anchor_lang::prelude::*; +use anchor_spl::memo; +use anchor_spl::memo::{BuildMemo, Memo}; +use anchor_spl::token::Token; +use anchor_spl::token_2022::spl_token_2022::extension::transfer_fee::{ + TransferFee, TransferFeeConfig, MAX_FEE_BASIS_POINTS, +}; +use anchor_spl::token_2022::spl_token_2022::{ + self, + extension::{self, StateWithExtensions}, +}; +use anchor_spl::token_interface::spl_token_2022::extension::BaseStateWithExtensions; +use anchor_spl::token_interface::{ + harvest_withheld_tokens_to_mint, HarvestWithheldTokensToMint, Mint, TokenAccount, + TokenInterface, +}; + +use crate::{LockerError, VestingEscrow}; + +const ONE_IN_BASIS_POINTS: u128 = MAX_FEE_BASIS_POINTS as u128; + +#[derive(Clone, Copy)] +pub struct MemoTransferContext<'a, 'info> { + pub memo_program: &'a Program<'info, Memo>, + pub memo: &'static [u8], +} + +pub fn transfer_to_escrow_v2<'a, 'c: 'info, 'info>( + sender: &'a Signer<'info>, + token_mint: &InterfaceAccount<'info, Mint>, + sender_token: &InterfaceAccount<'info, TokenAccount>, + escrow_token: &InterfaceAccount<'info, TokenAccount>, + token_program: &Interface<'info, TokenInterface>, + amount: u64, + transfer_hook_accounts: Option<&'c [AccountInfo<'info>]>, +) -> Result<()> { + let mut instruction = spl_token_2022::instruction::transfer_checked( + token_program.key, + &sender_token.key(), + &token_mint.key(), // mint + &escrow_token.key(), // to + sender.key, // authority + &[], + // The transfer amount should include fee + amount, + token_mint.decimals, + )?; + + let mut account_infos = vec![ + sender_token.to_account_info(), + token_mint.to_account_info(), + escrow_token.to_account_info(), + sender.to_account_info(), + ]; + + // TransferHook extension + if let Some(hook_program_id) = get_transfer_hook_program_id(token_mint)? { + let Some(transfer_hook_accounts) = transfer_hook_accounts else { + return Err(LockerError::NoTransferHookProgram.into()); + }; + + spl_transfer_hook_interface::onchain::add_extra_accounts_for_execute_cpi( + &mut instruction, + &mut account_infos, + &hook_program_id, + sender_token.to_account_info(), + token_mint.to_account_info(), + escrow_token.to_account_info(), + sender.to_account_info(), + amount, + transfer_hook_accounts, + )?; + } else { + require!( + transfer_hook_accounts.is_none(), + LockerError::NoTransferHookProgram + ); + } + + solana_program::program::invoke(&instruction, &account_infos)?; + + Ok(()) +} + +pub fn transfer_to_user_v2<'c: 'info, 'info>( + escrow: &AccountLoader<'info, VestingEscrow>, + token_mint: &InterfaceAccount<'info, Mint>, + escrow_token: &InterfaceAccount<'info, TokenAccount>, + recipient_account: &InterfaceAccount<'info, TokenAccount>, + token_program: &Interface<'info, TokenInterface>, + memo_transfer_context: Option>, + amount: u64, + transfer_hook_accounts: Option<&'c [AccountInfo<'info>]>, +) -> Result<()> { + let escrow_state = escrow.load()?; + let escrow_seeds = escrow_seeds!(escrow_state); + + if let Some(memo_ctx) = memo_transfer_context { + if is_transfer_memo_required(&recipient_account)? { + memo::build_memo( + CpiContext::new(memo_ctx.memo_program.to_account_info(), BuildMemo {}), + memo_ctx.memo, + )?; + } + } + + let mut instruction = spl_token_2022::instruction::transfer_checked( + token_program.key, + &escrow_token.key(), + &token_mint.key(), // mint + &recipient_account.key(), // to + &escrow.key(), // authority + &[], + amount, + token_mint.decimals, + )?; + + let mut account_infos = vec![ + escrow_token.to_account_info(), + token_mint.to_account_info(), + recipient_account.to_account_info(), + escrow.to_account_info(), + ]; + + // TransferHook extension + if let Some(hook_program_id) = get_transfer_hook_program_id(token_mint)? { + let Some(transfer_hook_accounts) = transfer_hook_accounts else { + return Err(LockerError::NoTransferHookProgram.into()); + }; + + spl_transfer_hook_interface::onchain::add_extra_accounts_for_execute_cpi( + &mut instruction, + &mut account_infos, + &hook_program_id, + escrow_token.to_account_info(), + token_mint.to_account_info(), + recipient_account.to_account_info(), + escrow.to_account_info(), + amount, + transfer_hook_accounts, + )?; + } else { + require!( + transfer_hook_accounts.is_none(), + LockerError::NoTransferHookProgram + ); + } + + solana_program::program::invoke_signed(&instruction, &account_infos, &[&escrow_seeds[..]])?; + + Ok(()) +} + +pub fn validate_mint(token_mint: &InterfaceAccount) -> Result<()> { + let token_mint_info = token_mint.to_account_info(); + + // mint owned by Token Program is supported by default + if *token_mint_info.owner == Token::id() { + return Ok(()); + } + + let token_mint_data = token_mint_info.try_borrow_data()?; + let token_mint_unpacked = + StateWithExtensions::::unpack(&token_mint_data)?; + + let extensions = token_mint_unpacked.get_extension_types()?; + for extension in extensions { + match extension { + // supported + extension::ExtensionType::TransferFeeConfig => {} + extension::ExtensionType::TokenMetadata => {} + extension::ExtensionType::MetadataPointer => {} + // partially supported + extension::ExtensionType::ConfidentialTransferMint => { + // Supported, but non-confidential transfer only + } + extension::ExtensionType::ConfidentialTransferFeeConfig => { + // Supported, but non-confidential transfer only + } + // supported with risks that creator should be aware of + extension::ExtensionType::PermanentDelegate => {} + extension::ExtensionType::TransferHook => {} + extension::ExtensionType::MintCloseAuthority => {} + extension::ExtensionType::DefaultAccountState => {} + extension::ExtensionType::GroupMemberPointer => {} + extension::ExtensionType::GroupPointer => {} + // Not stable yet to support + // extension::ExtensionType::TokenGroup => {} + // extension::ExtensionType::TokenGroupMember => {} + // mint has unknown or unsupported extensions + _ => { + return Err(LockerError::UnsupportedMint.into()); + } + } + } + + Ok(()) +} + +// This function calculate the pre amount (with fee) require to transfer `amount` of token +pub fn calculate_transfer_fee_included_amount( + amount: u64, + token_mint: &InterfaceAccount, +) -> Result { + let mint_info = token_mint.to_account_info(); + if *mint_info.owner == Token::id() { + return Ok(amount); + } + + let token_mint_data = mint_info.try_borrow_data()?; + let token_mint_unpacked = + StateWithExtensions::::unpack(&token_mint_data)?; + let actual_amount: u64 = + if let Ok(transfer_fee_config) = token_mint_unpacked.get_extension::() { + let transfer_fee = transfer_fee_config.get_epoch_fee(Clock::get()?.epoch); + calculate_pre_fee_amount(transfer_fee, amount) + .ok_or(LockerError::TransferFeeCalculationFailure)? + } else { + amount + }; + + Ok(actual_amount) +} + +pub fn harvest_fees<'c: 'info, 'info>( + token_program_id: &Interface<'info, TokenInterface>, + token_account: &InterfaceAccount<'info, TokenAccount>, + mint: &InterfaceAccount<'info, Mint>, +) -> Result<()> { + let mint_info = mint.to_account_info(); + if mint_info.owner.key() == Token::id() { + return Result::Ok(()); + } + + let token_mint_data = mint_info.try_borrow_data()?; + let token_mint_unpacked = + StateWithExtensions::::unpack(&token_mint_data)?; + let mut is_harvestable = false; + if let Ok(_transfer_fee_config) = token_mint_unpacked.get_extension::() { + is_harvestable = true; + } + // need to do this because Rust says we are still borrowing the data + drop(token_mint_data); + + if is_harvestable { + harvest_withheld_tokens_to_mint( + CpiContext::new( + token_program_id.to_account_info(), + HarvestWithheldTokensToMint { + token_program_id: token_program_id.to_account_info(), + mint: mint.to_account_info(), + }, + ), + vec![token_account.to_account_info()], + )?; + } + + Ok(()) +} + +// Memo Extension support +pub fn is_transfer_memo_required(token_account: &InterfaceAccount) -> Result { + let token_account_info = token_account.to_account_info(); + if *token_account_info.owner == Token::id() { + return Ok(false); + } + + let token_account_data = token_account_info.try_borrow_data()?; + let token_account_unpacked = + StateWithExtensions::::unpack(&token_account_data)?; + let extension = + token_account_unpacked.get_extension::(); + + if let Ok(memo_transfer) = extension { + Ok(memo_transfer.require_incoming_transfer_memos.into()) + } else { + Ok(false) + } +} + +fn get_transfer_hook_program_id<'info>( + token_mint: &InterfaceAccount<'info, Mint>, +) -> Result> { + let token_mint_info = token_mint.to_account_info(); + if *token_mint_info.owner == Token::id() { + return Ok(None); + } + + let token_mint_data = token_mint_info.try_borrow_data()?; + let token_mint_unpacked = + StateWithExtensions::::unpack(&token_mint_data)?; + Ok(extension::transfer_hook::get_program_id( + &token_mint_unpacked, + )) +} + +pub fn calculate_pre_fee_amount(transfer_fee: &TransferFee, post_fee_amount: u64) -> Option { + if post_fee_amount == 0 { + return Some(0); + } + let maximum_fee = u64::from(transfer_fee.maximum_fee); + let transfer_fee_basis_points = u16::from(transfer_fee.transfer_fee_basis_points) as u128; + if transfer_fee_basis_points == 0 { + Some(post_fee_amount) + } else if transfer_fee_basis_points == ONE_IN_BASIS_POINTS { + Some(maximum_fee.checked_add(post_fee_amount)?) + } else { + let numerator = (post_fee_amount as u128).checked_mul(ONE_IN_BASIS_POINTS)?; + let denominator = ONE_IN_BASIS_POINTS.checked_sub(transfer_fee_basis_points)?; + // let raw_pre_fee_amount = ceil_div(numerator, denominator)?; + let raw_pre_fee_amount = numerator + .checked_add(ONE_IN_BASIS_POINTS)? + .checked_sub(1)? + .checked_div(denominator)?; + + if raw_pre_fee_amount.checked_sub(post_fee_amount as u128)? >= maximum_fee as u128 { + post_fee_amount.checked_add(maximum_fee) + } else { + // should return `None` if `pre_fee_amount` overflows + u64::try_from(raw_pre_fee_amount).ok() + } + } +} + +#[cfg(test)] +mod token2022_tests { + use anchor_spl::token_interface::spl_pod::primitives::{PodU16, PodU64}; + use proptest::prelude::*; + + use super::*; + + const MAX_FEE_BASIS_POINTS: u16 = 100; + proptest! { + #[test] + fn inverse_fee_relationship( + transfer_fee_basis_points in 0u16..MAX_FEE_BASIS_POINTS, + maximum_fee in u64::MIN..=u64::MAX, + amount_in in 0..=u64::MAX + ) { + let transfer_fee = TransferFee { + epoch: PodU64::from(0), + maximum_fee: PodU64::from(maximum_fee), + transfer_fee_basis_points: PodU16::from(transfer_fee_basis_points), + }; + let fee = transfer_fee.calculate_fee(amount_in).unwrap(); + let amount_out = amount_in.checked_sub(fee).unwrap(); + let fee_exact_out = calculate_pre_fee_amount(&transfer_fee, amount_out).unwrap().checked_sub(amount_out).unwrap(); + assert!(fee_exact_out >= fee); + if fee_exact_out - fee > 0 { + println!("dif {} {} {} {} {}",fee_exact_out - fee, fee, fee_exact_out, maximum_fee, amount_in); + } + + } + } +} diff --git a/sdk/artifacts/locker.json b/sdk/artifacts/locker.json new file mode 100644 index 0000000..1671f4d --- /dev/null +++ b/sdk/artifacts/locker.json @@ -0,0 +1,777 @@ +{ + "version": "0.3.0", + "name": "locker", + "instructions": [ + { + "name": "createVestingEscrow", + "accounts": [ + { + "name": "base", + "isMut": true, + "isSigner": true + }, + { + "name": "escrow", + "isMut": true, + "isSigner": false + }, + { + "name": "escrowToken", + "isMut": true, + "isSigner": false + }, + { + "name": "sender", + "isMut": true, + "isSigner": true + }, + { + "name": "senderToken", + "isMut": true, + "isSigner": false + }, + { + "name": "recipient", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "Token program." + ] + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "eventAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "program", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "CreateVestingEscrowParameters" + } + } + ] + }, + { + "name": "claim", + "accounts": [ + { + "name": "escrow", + "isMut": true, + "isSigner": false + }, + { + "name": "escrowToken", + "isMut": true, + "isSigner": false + }, + { + "name": "recipient", + "isMut": true, + "isSigner": true + }, + { + "name": "recipientToken", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "Token program." + ] + }, + { + "name": "eventAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "program", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "maxAmount", + "type": "u64" + } + ] + }, + { + "name": "createVestingEscrowMetadata", + "accounts": [ + { + "name": "escrow", + "isMut": true, + "isSigner": false, + "docs": [ + "The [Escrow]." + ] + }, + { + "name": "creator", + "isMut": false, + "isSigner": true, + "docs": [ + "Creator of the escrow." + ] + }, + { + "name": "escrowMetadata", + "isMut": true, + "isSigner": false, + "docs": [ + "The [ProposalMeta]." + ] + }, + { + "name": "payer", + "isMut": true, + "isSigner": true, + "docs": [ + "Payer of the [ProposalMeta]." + ] + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "System program." + ] + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "CreateVestingEscrowMetadataParameters" + } + } + ] + }, + { + "name": "updateVestingEscrowRecipient", + "accounts": [ + { + "name": "escrow", + "isMut": true, + "isSigner": false, + "docs": [ + "Escrow." + ] + }, + { + "name": "escrowMetadata", + "isMut": true, + "isSigner": false, + "isOptional": true, + "docs": [ + "Escrow metadata." + ] + }, + { + "name": "signer", + "isMut": true, + "isSigner": true, + "docs": [ + "Signer." + ] + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "System program." + ] + } + ], + "args": [ + { + "name": "newRecipient", + "type": "publicKey" + }, + { + "name": "newRecipientEmail", + "type": { + "option": "string" + } + } + ] + }, + { + "name": "createVestingEscrowV2", + "accounts": [ + { + "name": "base", + "isMut": true, + "isSigner": true + }, + { + "name": "escrow", + "isMut": true, + "isSigner": false + }, + { + "name": "escrowToken", + "isMut": true, + "isSigner": false + }, + { + "name": "sender", + "isMut": true, + "isSigner": true + }, + { + "name": "senderToken", + "isMut": true, + "isSigner": false + }, + { + "name": "mint", + "isMut": false, + "isSigner": false + }, + { + "name": "memoProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "recipient", + "isMut": false, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "Token program." + ] + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "eventAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "program", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "CreateVestingEscrowParameters" + } + }, + { + "name": "memo", + "type": "string" + } + ] + }, + { + "name": "claimV2", + "accounts": [ + { + "name": "escrow", + "isMut": true, + "isSigner": false + }, + { + "name": "mint", + "isMut": false, + "isSigner": false + }, + { + "name": "memoProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "escrowToken", + "isMut": true, + "isSigner": false + }, + { + "name": "recipient", + "isMut": true, + "isSigner": true + }, + { + "name": "recipientToken", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "Token program." + ] + }, + { + "name": "eventAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "program", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "maxAmount", + "type": "u64" + } + ] + } + ], + "accounts": [ + { + "name": "VestingEscrowMetadata", + "docs": [ + "Metadata about an escrow." + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "escrow", + "docs": [ + "The [Escrow]." + ], + "type": "publicKey" + }, + { + "name": "name", + "docs": [ + "Name of escrow." + ], + "type": "string" + }, + { + "name": "description", + "docs": [ + "Description of escrow." + ], + "type": "string" + }, + { + "name": "creatorEmail", + "docs": [ + "Email of creator" + ], + "type": "string" + }, + { + "name": "recipientEmail", + "docs": [ + "Email of recipient" + ], + "type": "string" + } + ] + } + }, + { + "name": "VestingEscrow", + "type": { + "kind": "struct", + "fields": [ + { + "name": "recipient", + "docs": [ + "recipient address" + ], + "type": "publicKey" + }, + { + "name": "tokenMint", + "docs": [ + "token mint" + ], + "type": "publicKey" + }, + { + "name": "creator", + "docs": [ + "creator of the escrow" + ], + "type": "publicKey" + }, + { + "name": "base", + "docs": [ + "escrow base key" + ], + "type": "publicKey" + }, + { + "name": "escrowBump", + "docs": [ + "escrow bump" + ], + "type": "u8" + }, + { + "name": "updateRecipientMode", + "docs": [ + "update_recipient_mode" + ], + "type": "u8" + }, + { + "name": "padding0", + "docs": [ + "padding" + ], + "type": { + "array": [ + "u8", + 6 + ] + } + }, + { + "name": "startTime", + "docs": [ + "start time" + ], + "type": "u64" + }, + { + "name": "frequency", + "docs": [ + "frequency" + ], + "type": "u64" + }, + { + "name": "initialUnlockAmount", + "docs": [ + "initial unlock amount" + ], + "type": "u64" + }, + { + "name": "amountPerPeriod", + "docs": [ + "amount per period" + ], + "type": "u64" + }, + { + "name": "numberOfPeriod", + "docs": [ + "number of period" + ], + "type": "u64" + }, + { + "name": "totalClaimedAmount", + "docs": [ + "total claimed amount" + ], + "type": "u64" + }, + { + "name": "padding1", + "docs": [ + "padding" + ], + "type": { + "array": [ + "u8", + 8 + ] + } + }, + { + "name": "buffer", + "docs": [ + "buffer" + ], + "type": { + "array": [ + "u128", + 6 + ] + } + } + ] + } + } + ], + "types": [ + { + "name": "CreateVestingEscrowMetadataParameters", + "type": { + "kind": "struct", + "fields": [ + { + "name": "name", + "type": "string" + }, + { + "name": "description", + "type": "string" + }, + { + "name": "creatorEmail", + "type": "string" + }, + { + "name": "recipientEmail", + "type": "string" + } + ] + } + }, + { + "name": "CreateVestingEscrowParameters", + "docs": [ + "Accounts for [locker::create_vesting_escrow]." + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "startTime", + "type": "u64" + }, + { + "name": "frequency", + "type": "u64" + }, + { + "name": "initialUnlockAmount", + "type": "u64" + }, + { + "name": "amountPerPeriod", + "type": "u64" + }, + { + "name": "numberOfPeriod", + "type": "u64" + }, + { + "name": "updateRecipientMode", + "type": "u8" + } + ] + } + }, + { + "name": "UpdateRecipientMode", + "type": { + "kind": "enum", + "variants": [ + { + "name": "NeitherCreatorOrRecipient" + }, + { + "name": "OnlyCreator" + }, + { + "name": "OnlyRecipient" + }, + { + "name": "EitherCreatorAndRecipient" + } + ] + } + } + ], + "events": [ + { + "name": "EventCreateVestingEscrow", + "fields": [ + { + "name": "startTime", + "type": "u64", + "index": false + }, + { + "name": "frequency", + "type": "u64", + "index": false + }, + { + "name": "initialUnlockAmount", + "type": "u64", + "index": false + }, + { + "name": "amountPerPeriod", + "type": "u64", + "index": false + }, + { + "name": "numberOfPeriod", + "type": "u64", + "index": false + }, + { + "name": "updateRecipientMode", + "type": "u8", + "index": false + }, + { + "name": "recipient", + "type": "publicKey", + "index": false + }, + { + "name": "escrow", + "type": "publicKey", + "index": false + } + ] + }, + { + "name": "EventClaim", + "fields": [ + { + "name": "amount", + "type": "u64", + "index": false + }, + { + "name": "currentTs", + "type": "u64", + "index": false + }, + { + "name": "escrow", + "type": "publicKey", + "index": false + } + ] + }, + { + "name": "EventUpdateVestingEscrowRecipient", + "fields": [ + { + "name": "escrow", + "type": "publicKey", + "index": false + }, + { + "name": "oldRecipient", + "type": "publicKey", + "index": false + }, + { + "name": "newRecipient", + "type": "publicKey", + "index": false + }, + { + "name": "signer", + "type": "publicKey", + "index": false + } + ] + } + ], + "errors": [ + { + "code": 6000, + "name": "MathOverflow", + "msg": "Math operation overflow" + }, + { + "code": 6001, + "name": "FrequencyIsZero", + "msg": "Frequency is zero" + }, + { + "code": 6002, + "name": "InvalidEscrowTokenAddress", + "msg": "Invalid escrow token address" + }, + { + "code": 6003, + "name": "InvalidUpdateRecipientMode", + "msg": "Invalid update recipient mode" + }, + { + "code": 6004, + "name": "NotPermitToDoThisAction", + "msg": "Not permit to do this action" + }, + { + "code": 6005, + "name": "InvalidRecipientTokenAccount", + "msg": "Invalid recipient token account" + }, + { + "code": 6006, + "name": "InvalidEscrowMetadata", + "msg": "Invalid escrow metadata" + }, + { + "code": 6007, + "name": "InvalidMintAccount", + "msg": "Invalid mint account" + }, + { + "code": 6008, + "name": "IncorrectTokenProgramId", + "msg": "Invalid token programId" + }, + { + "code": 6009, + "name": "ParseTokenExtensionsFailure", + "msg": "Parse token extensions failure" + }, + { + "code": 6010, + "name": "TransferFeeCalculationFailure", + "msg": "Calculate transfer fee failure" + }, + { + "code": 6011, + "name": "UnsupportedMint", + "msg": "Unsupported mint" + } + ], + "metadata": { + "address": "2r5VekMNiWPzi1pWwvJczrdPaZnJG59u91unSrTunwJg" + } +} \ No newline at end of file diff --git a/tests/common/index.ts b/tests/common/index.ts index 2e7a043..7ddba39 100644 --- a/tests/common/index.ts +++ b/tests/common/index.ts @@ -1,5 +1,11 @@ -import { web3, Wallet } from "@coral-xyz/anchor"; -import { ASSOCIATED_TOKEN_PROGRAM_ID, createMint, getOrCreateAssociatedTokenAccount, mintTo, TOKEN_PROGRAM_ID } from "@solana/spl-token"; +import { Wallet, web3 } from "@coral-xyz/anchor"; +import { + ASSOCIATED_TOKEN_PROGRAM_ID, + createMint, + getOrCreateAssociatedTokenAccount, + mintTo, + TOKEN_PROGRAM_ID, +} from "@solana/spl-token"; export * from "./asserter"; @@ -69,7 +75,13 @@ export const createMintIfNotExists = async ( } return mintKeypair.publicKey; }; -export const mintTokenTo = async (connection: web3.Connection, mintAuthority: web3.Keypair, tokenMint: web3.PublicKey, owner: web3.PublicKey, amount: number) => { +export const mintTokenTo = async ( + connection: web3.Connection, + mintAuthority: web3.Keypair, + tokenMint: web3.PublicKey, + owner: web3.PublicKey, + amount: number +) => { const userToken = await getOrCreateAssociatedTokenAccount( connection, mintAuthority, @@ -97,11 +109,15 @@ export const mintTokenTo = async (connection: web3.Connection, mintAuthority: we }, TOKEN_PROGRAM_ID ); -} +}; export const getCurrentBlockTime = async (connection: web3.Connection) => { // TODO fetch clock account can help to reduce rpc call const currentSlot = await connection.getSlot(); const currentBlockTime = await connection.getBlockTime(currentSlot); return currentBlockTime; -} \ No newline at end of file +}; + +export const getCurrentEpoch = async (connection: web3.Connection) => { + return (await connection.getEpochInfo()).epoch; +}; diff --git a/tests/escrow_metadata.ts b/tests/escrow_metadata.ts deleted file mode 100644 index c3d37ee..0000000 --- a/tests/escrow_metadata.ts +++ /dev/null @@ -1,108 +0,0 @@ -import * as anchor from "@coral-xyz/anchor"; -import { web3 } from "@coral-xyz/anchor"; -import { - ASSOCIATED_TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - createMint, - getOrCreateAssociatedTokenAccount, - mintTo, -} from "@solana/spl-token"; -import { BN } from "bn.js"; -import { - createAndFundWallet, - getCurrentBlockTime, - sleep, -} from "./common"; -import { claimToken, createEscrowMetadata, createLockerProgram, createVestingPlan } from "./locker_utils"; - - -let provider = anchor.AnchorProvider.env(); -provider.opts.commitment = 'confirmed'; - -describe("Escrow metadata", () => { - const tokenDecimal = 8; - let TOKEN: web3.PublicKey; - let UserKP: web3.Keypair; - let ReceipentKP: web3.Keypair; - - before(async () => { - { - const result = await createAndFundWallet(provider.connection); - UserKP = result.keypair; - } - { - const result = await createAndFundWallet(provider.connection); - ReceipentKP = result.keypair; - } - - TOKEN = await createMint( - provider.connection, - UserKP, - UserKP.publicKey, - null, - tokenDecimal, - web3.Keypair.generate(), - { - commitment: "confirmed", - }, - TOKEN_PROGRAM_ID - ); - - const userToken = await getOrCreateAssociatedTokenAccount( - provider.connection, - UserKP, - TOKEN, - UserKP.publicKey, - false, - "confirmed", - { - commitment: "confirmed", - }, - TOKEN_PROGRAM_ID, - ASSOCIATED_TOKEN_PROGRAM_ID - ); - // userBTC = userTokenX.address; - await mintTo( - provider.connection, - UserKP, - TOKEN, - userToken.address, - UserKP.publicKey, - 100 * 10 ** tokenDecimal, - [], - { - commitment: "confirmed", - }, - TOKEN_PROGRAM_ID - ); - }); - it("Full flow", async () => { - console.log("Create vesting plan"); - const program = createLockerProgram(new anchor.Wallet(UserKP)); - let currentBlockTime = await getCurrentBlockTime(program.provider.connection); - const cliffTime = new BN(currentBlockTime).add(new BN(5)); - let escrow = await createVestingPlan({ - ownerKeypair: UserKP, - tokenMint: TOKEN, - vestingStartTime: new BN(0), - isAssertion: true, - cliffTime, - frequency: new BN(1), - cliffUnlockAmount: new BN(100_000), - amountPerPeriod: new BN(50_000), - numberOfPeriod: new BN(2), - recipient: ReceipentKP.publicKey, - updateRecipientMode: 0, - }); - console.log("Create escrow metadata"); - await createEscrowMetadata({ - escrow, - name: "Jupiter lock", - description: "This is jupiter lock", - creatorEmail: "andrew@raccoons.dev", - recipientEmail: "max@raccoons.dev", - creator: UserKP, - isAssertion: true - }); - }); -}); diff --git a/tests/locker.ts b/tests/locker.ts deleted file mode 100644 index 6a4e53c..0000000 --- a/tests/locker.ts +++ /dev/null @@ -1,135 +0,0 @@ -import * as anchor from "@coral-xyz/anchor"; -import { web3 } from "@coral-xyz/anchor"; -import { - ASSOCIATED_TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - createMint, - getOrCreateAssociatedTokenAccount, - mintTo, -} from "@solana/spl-token"; -import { BN } from "bn.js"; -import { - createAndFundWallet, - getCurrentBlockTime, - sleep, -} from "./common"; -import { claimToken, createLockerProgram, createVestingPlan } from "./locker_utils"; - - -let provider = anchor.AnchorProvider.env(); -provider.opts.commitment = 'confirmed'; - -describe("Full flow", () => { - const tokenDecimal = 8; - let TOKEN: web3.PublicKey; - let UserKP: web3.Keypair; - let ReceipentKP: web3.Keypair; - let ReceipentToken: web3.PublicKey; - - before(async () => { - { - const result = await createAndFundWallet(provider.connection); - UserKP = result.keypair; - } - { - const result = await createAndFundWallet(provider.connection); - ReceipentKP = result.keypair; - } - - TOKEN = await createMint( - provider.connection, - UserKP, - UserKP.publicKey, - null, - tokenDecimal, - web3.Keypair.generate(), - { - commitment: "confirmed", - }, - TOKEN_PROGRAM_ID - ); - - const userToken = await getOrCreateAssociatedTokenAccount( - provider.connection, - UserKP, - TOKEN, - UserKP.publicKey, - false, - "confirmed", - { - commitment: "confirmed", - }, - TOKEN_PROGRAM_ID, - ASSOCIATED_TOKEN_PROGRAM_ID - ); - // userBTC = userTokenX.address; - - const receipentToken = await getOrCreateAssociatedTokenAccount( - provider.connection, - UserKP, - TOKEN, - ReceipentKP.publicKey, - false, - "confirmed", - { - commitment: "confirmed", - }, - TOKEN_PROGRAM_ID, - ASSOCIATED_TOKEN_PROGRAM_ID - ); - ReceipentToken = receipentToken.address; - await mintTo( - provider.connection, - UserKP, - TOKEN, - userToken.address, - UserKP.publicKey, - 100 * 10 ** tokenDecimal, - [], - { - commitment: "confirmed", - }, - TOKEN_PROGRAM_ID - ); - }); - - - it("Full flow", async () => { - console.log("Create vesting plan"); - const program = createLockerProgram(new anchor.Wallet(UserKP)); - let currentBlockTime = await getCurrentBlockTime(program.provider.connection); - const cliffTime = new BN(currentBlockTime).add(new BN(5)); - let escrow = await createVestingPlan({ - vestingStartTime: new BN(0), - ownerKeypair: UserKP, - tokenMint: TOKEN, - isAssertion: true, - cliffTime, - frequency: new BN(1), - cliffUnlockAmount: new BN(100_000), - amountPerPeriod: new BN(50_000), - numberOfPeriod: new BN(2), - recipient: ReceipentKP.publicKey, - updateRecipientMode: 0, - }); - - while (true) { - const currentBlockTime = await getCurrentBlockTime(program.provider.connection); - if (currentBlockTime > cliffTime.toNumber()) { - break; - } else { - await sleep(1000); - console.log("Wait until cliffTime"); - } - } - - console.log("Claim token"); - await claimToken({ - recipient: ReceipentKP, - recipientToken: ReceipentToken, - escrow, - maxAmount: new BN(1_000_000), - isAssertion: true, - }) - }); -}); diff --git a/tests/locker_utils/index.ts b/tests/locker_utils/index.ts index 7de3f15..16547de 100644 --- a/tests/locker_utils/index.ts +++ b/tests/locker_utils/index.ts @@ -1,235 +1,642 @@ -import { AnchorProvider, Program, Wallet, web3, BN } from "@coral-xyz/anchor"; -import { Locker, IDL as LockerIDL } from "../../target/types/locker"; import { - ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, getAssociatedTokenAddressSync, createAssociatedTokenAccountInstruction + AnchorProvider, + BN, + Program, + Wallet, + web3, + workspace, +} from "@coral-xyz/anchor"; +import { Locker } from "../../target/types/locker"; +import { + ASSOCIATED_TOKEN_PROGRAM_ID, + calculateEpochFee, + createAssociatedTokenAccountInstruction, + getAssociatedTokenAddressSync, + getEpochFee, + getMint, + getTransferFeeConfig, + TOKEN_2022_PROGRAM_ID, + TOKEN_PROGRAM_ID, } from "@solana/spl-token"; import { expect } from "chai"; +import { TokenExtensionUtil } from "./token_2022/token-extensions"; +import { + RemainingAccountsBuilder, + RemainingAccountsType, +} from "./token_2022/remaining-accounts"; +import { AccountMeta, ComputeBudgetProgram } from "@solana/web3.js"; +import { getCurrentEpoch } from "../common"; export const LOCKER_PROGRAM_ID = new web3.PublicKey( - "2r5VekMNiWPzi1pWwvJczrdPaZnJG59u91unSrTunwJg" + "2r5VekMNiWPzi1pWwvJczrdPaZnJG59u91unSrTunwJg" ); +const MEMO_PROGRAM = new web3.PublicKey( + "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" +); -export function createLockerProgram( - wallet: Wallet, -): Program { - const provider = new AnchorProvider(AnchorProvider.env().connection, wallet, { - maxRetries: 3, - }); - provider.opts.commitment = 'confirmed'; - const program = new Program(LockerIDL, LOCKER_PROGRAM_ID, provider); - return program; -} +const ESCROW_USE_SPL_TOKEN = 0; +const ESCROW_USE_TOKEN_2022 = 1; -export function deriveEscrow( - base: web3.PublicKey, - programId: web3.PublicKey -) { - return web3.PublicKey.findProgramAddressSync( - [ - Buffer.from("escrow"), - base.toBuffer(), - ], - programId - ); +export function createLockerProgram(wallet?: Wallet): Program { + const provider = new AnchorProvider(AnchorProvider.env().connection, wallet, { + maxRetries: 3, + }); + provider.opts.commitment = "confirmed"; + + return workspace.Locker as Program; } +export function deriveEscrow(base: web3.PublicKey, programId: web3.PublicKey) { + return web3.PublicKey.findProgramAddressSync( + [Buffer.from("escrow"), base.toBuffer()], + programId + ); +} export function deriveEscrowMetadata( - escrow: web3.PublicKey, - programId: web3.PublicKey + escrow: web3.PublicKey, + programId: web3.PublicKey ) { - return web3.PublicKey.findProgramAddressSync( - [ - Buffer.from("escrow_metadata"), - escrow.toBuffer(), - ], - programId - ); + return web3.PublicKey.findProgramAddressSync( + [Buffer.from("escrow_metadata"), escrow.toBuffer()], + programId + ); } - export interface CreateVestingPlanParams { - ownerKeypair: web3.Keypair, - tokenMint: web3.PublicKey, - isAssertion: boolean, - vestingStartTime: BN, - cliffTime: BN, - frequency: BN, - cliffUnlockAmount: BN, - amountPerPeriod: BN, - numberOfPeriod: BN, - recipient: web3.PublicKey, - updateRecipientMode: number, + ownerKeypair: web3.Keypair; + tokenMint: web3.PublicKey; + isAssertion: boolean; + vestingStartTime: BN; + cliffTime: BN; + frequency: BN; + cliffUnlockAmount: BN; + amountPerPeriod: BN; + numberOfPeriod: BN; + recipient: web3.PublicKey; + updateRecipientMode: number; + cancelMode: number; + tokenProgram?: web3.PublicKey; } export async function createVestingPlan(params: CreateVestingPlanParams) { - let { isAssertion, tokenMint, ownerKeypair, cliffTime, frequency, cliffUnlockAmount, amountPerPeriod, numberOfPeriod, recipient, updateRecipientMode, vestingStartTime } = params; - const program = createLockerProgram(new Wallet(ownerKeypair)); + let { + isAssertion, + tokenMint, + ownerKeypair, + vestingStartTime, + cliffTime, + frequency, + cliffUnlockAmount, + amountPerPeriod, + numberOfPeriod, + recipient, + updateRecipientMode, + cancelMode, + tokenProgram, + } = params; + if (tokenProgram && tokenProgram != TOKEN_PROGRAM_ID) { + throw Error("This instruction only supports TOKEN_PROGRAM_ID"); + } + const program = createLockerProgram(new Wallet(ownerKeypair)); - const baseKP = web3.Keypair.generate(); + const baseKP = web3.Keypair.generate(); - let [escrow] = deriveEscrow(baseKP.publicKey, program.programId); + let [escrow] = deriveEscrow(baseKP.publicKey, program.programId); - const senderToken = getAssociatedTokenAddressSync( - tokenMint, + const senderToken = getAssociatedTokenAddressSync( + tokenMint, + ownerKeypair.publicKey, + false, + TOKEN_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID + ); + + const escrowToken = getAssociatedTokenAddressSync( + tokenMint, + escrow, + true, + TOKEN_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID + ); + await program.methods + .createVestingEscrow({ + cliffTime, + frequency, + cliffUnlockAmount, + amountPerPeriod, + numberOfPeriod, + updateRecipientMode, + vestingStartTime, + cancelMode, + }) + .accounts({ + base: baseKP.publicKey, + senderToken, + escrowToken, + recipient, + sender: ownerKeypair.publicKey, + tokenProgram: TOKEN_PROGRAM_ID, + systemProgram: web3.SystemProgram.programId, + escrow, + }) + .preInstructions([ + createAssociatedTokenAccountInstruction( ownerKeypair.publicKey, - false, + escrowToken, + escrow, + tokenMint, TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID + ), + ]) + .signers([baseKP, ownerKeypair]) + .rpc(); + + if (isAssertion) { + const escrowState = await program.account.vestingEscrow.fetch(escrow); + expect(escrowState.cliffTime.toString()).eq(cliffTime.toString()); + expect(escrowState.frequency.toString()).eq(frequency.toString()); + expect(escrowState.cliffUnlockAmount.toString()).eq( + cliffUnlockAmount.toString() + ); + expect(escrowState.amountPerPeriod.toString()).eq( + amountPerPeriod.toString() + ); + expect(escrowState.numberOfPeriod.toString()).eq(numberOfPeriod.toString()); + expect(escrowState.recipient.toString()).eq(recipient.toString()); + expect(escrowState.tokenMint.toString()).eq(tokenMint.toString()); + expect(escrowState.creator.toString()).eq( + ownerKeypair.publicKey.toString() ); + expect(escrowState.base.toString()).eq(baseKP.publicKey.toString()); + expect(escrowState.updateRecipientMode).eq(updateRecipientMode); + expect(escrowState.tokenProgramFlag).eq(0); + expect(escrowState.cancelMode).eq(cancelMode); + } - const escrowToken = getAssociatedTokenAddressSync( - tokenMint, - escrow, - true, - TOKEN_PROGRAM_ID, - ASSOCIATED_TOKEN_PROGRAM_ID + return escrow; +} + +export interface ClaimTokenParams { + isAssertion: boolean; + escrow: web3.PublicKey; + recipient: web3.Keypair; + maxAmount: BN; + recipientToken: web3.PublicKey; +} + +export async function claimToken(params: ClaimTokenParams) { + let { isAssertion, escrow, recipient, maxAmount, recipientToken } = params; + const program = createLockerProgram(new Wallet(recipient)); + const escrowState = await program.account.vestingEscrow.fetch(escrow); + + const escrowToken = getAssociatedTokenAddressSync( + escrowState.tokenMint, + escrow, + true, + TOKEN_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID + ); + + await program.methods + .claim(maxAmount) + .accounts({ + tokenProgram: TOKEN_PROGRAM_ID, + escrow, + escrowToken, + recipient: recipient.publicKey, + recipientToken, + }) + .signers([recipient]) + .rpc(); +} + +export interface CreateEscrowMetadataParams { + isAssertion: boolean; + creator: web3.Keypair; + escrow: web3.PublicKey; + name: string; + description: string; + creatorEmail: string; + recipientEmail: string; +} + +export async function createEscrowMetadata(params: CreateEscrowMetadataParams) { + let { + isAssertion, + escrow, + name, + description, + creatorEmail, + recipientEmail, + creator, + } = params; + const program = createLockerProgram(new Wallet(creator)); + const [escrowMetadata] = deriveEscrowMetadata(escrow, program.programId); + await program.methods + .createVestingEscrowMetadata({ + name, + description, + creatorEmail, + recipientEmail, + }) + .accounts({ + escrow, + systemProgram: web3.SystemProgram.programId, + payer: creator.publicKey, + creator: creator.publicKey, + escrowMetadata, + }) + .signers([creator]) + .rpc(); + + if (isAssertion) { + const escrowMetadataState = + await program.account.vestingEscrowMetadata.fetch(escrowMetadata); + expect(escrowMetadataState.escrow.toString()).eq(escrow.toString()); + expect(escrowMetadataState.name.toString()).eq(name.toString()); + expect(escrowMetadataState.description.toString()).eq( + description.toString() ); - await program.methods.createVestingEscrow({ + expect(escrowMetadataState.creatorEmail.toString()).eq( + creatorEmail.toString() + ); + expect(escrowMetadataState.recipientEmail.toString()).eq( + recipientEmail.toString() + ); + } +} + +export interface UpdateRecipientParams { + isAssertion: boolean; + signer: web3.Keypair; + escrow: web3.PublicKey; + newRecipient: web3.PublicKey; + newRecipientEmail: null | string; +} + +export async function updateRecipient(params: UpdateRecipientParams) { + let { isAssertion, escrow, signer, newRecipient, newRecipientEmail } = params; + const program = createLockerProgram(new Wallet(signer)); + let escrowMetadata = null; + if (newRecipientEmail != null) { + [escrowMetadata] = deriveEscrowMetadata(escrow, program.programId); + } + await program.methods + .updateVestingEscrowRecipient(newRecipient, newRecipientEmail) + .accounts({ + escrow, + escrowMetadata, + signer: signer.publicKey, + systemProgram: web3.SystemProgram.programId, + }) + .signers([signer]) + .rpc(); + + if (isAssertion) { + const escrowState = await program.account.vestingEscrow.fetch(escrow); + expect(escrowState.recipient.toString()).eq(newRecipient.toString()); + if (newRecipientEmail != null) { + [escrowMetadata] = deriveEscrowMetadata(escrow, program.programId); + const escrowMetadataState = + await program.account.vestingEscrowMetadata.fetch(escrowMetadata); + expect(escrowMetadataState.recipientEmail.toString()).eq( + newRecipientEmail.toString() + ); + } + } +} + +// V2 instructions +export async function createVestingPlanV2(params: CreateVestingPlanParams) { + let { + isAssertion, + tokenMint, + ownerKeypair, + vestingStartTime, + cliffTime, + frequency, + cliffUnlockAmount, + amountPerPeriod, + numberOfPeriod, + recipient, + updateRecipientMode, + cancelMode, + tokenProgram, + } = params; + const program = createLockerProgram(new Wallet(ownerKeypair)); + + const baseKP = web3.Keypair.generate(); + + let [escrow] = deriveEscrow(baseKP.publicKey, program.programId); + + const senderToken = getAssociatedTokenAddressSync( + tokenMint, + ownerKeypair.publicKey, + false, + tokenProgram, + ASSOCIATED_TOKEN_PROGRAM_ID + ); + + const escrowToken = getAssociatedTokenAddressSync( + tokenMint, + escrow, + true, + tokenProgram, + ASSOCIATED_TOKEN_PROGRAM_ID + ); + + let remainingAccountsInfo = null; + let remainingAccounts: AccountMeta[] = []; + if (tokenProgram == TOKEN_2022_PROGRAM_ID) { + let inputTransferHookAccounts = + await TokenExtensionUtil.getExtraAccountMetasForTransferHook( + program.provider.connection, + tokenMint, + senderToken, + escrowToken, + ownerKeypair.publicKey, + TOKEN_2022_PROGRAM_ID + ); + + [remainingAccountsInfo, remainingAccounts] = new RemainingAccountsBuilder() + .addSlice( + RemainingAccountsType.TransferHookEscrow, + inputTransferHookAccounts + ) + .build(); + } + + await program.methods + .createVestingEscrowV2( + { + vestingStartTime, cliffTime, frequency, cliffUnlockAmount, amountPerPeriod, numberOfPeriod, updateRecipientMode, - vestingStartTime, - }).accounts({ - base: baseKP.publicKey, - senderToken, + cancelMode, + }, + remainingAccountsInfo + ) + .accounts({ + base: baseKP.publicKey, + senderToken, + escrowToken, + recipient, + tokenMint, + sender: ownerKeypair.publicKey, + tokenProgram, + systemProgram: web3.SystemProgram.programId, + escrow, + }) + .remainingAccounts(remainingAccounts ? remainingAccounts : []) + .preInstructions([ + createAssociatedTokenAccountInstruction( + ownerKeypair.publicKey, escrowToken, - recipient, - sender: ownerKeypair.publicKey, - tokenProgram: TOKEN_PROGRAM_ID, - systemProgram: web3.SystemProgram.programId, escrow, - }).preInstructions( - [ - createAssociatedTokenAccountInstruction( - ownerKeypair.publicKey, - escrowToken, - escrow, - tokenMint, - TOKEN_PROGRAM_ID, - ASSOCIATED_TOKEN_PROGRAM_ID - ) - ] - ).signers([baseKP]).rpc(); - - if (isAssertion) { - const escrowState = await program.account.vestingEscrow.fetch(escrow); - expect(escrowState.cliffTime.toString()).eq(cliffTime.toString()); - expect(escrowState.frequency.toString()).eq(frequency.toString()); - expect(escrowState.cliffUnlockAmount.toString()).eq(cliffUnlockAmount.toString()); - expect(escrowState.amountPerPeriod.toString()).eq(amountPerPeriod.toString()); - expect(escrowState.numberOfPeriod.toString()).eq(numberOfPeriod.toString()); - expect(escrowState.recipient.toString()).eq(recipient.toString()); - expect(escrowState.tokenMint.toString()).eq(tokenMint.toString()); - expect(escrowState.creator.toString()).eq(ownerKeypair.publicKey.toString()); - expect(escrowState.base.toString()).eq(baseKP.publicKey.toString()); - expect(escrowState.updateRecipientMode).eq(updateRecipientMode); - } + tokenMint, + tokenProgram, + ASSOCIATED_TOKEN_PROGRAM_ID + ), + ]) + .signers([baseKP, ownerKeypair]) + .rpc(); - return escrow; -} + if (isAssertion) { + const escrowState = await program.account.vestingEscrow.fetch(escrow); + expect(escrowState.cliffTime.toString()).eq(cliffTime.toString()); + expect(escrowState.frequency.toString()).eq(frequency.toString()); + expect(escrowState.cliffUnlockAmount.toString()).eq( + cliffUnlockAmount.toString() + ); + expect(escrowState.amountPerPeriod.toString()).eq( + amountPerPeriod.toString() + ); + expect(escrowState.numberOfPeriod.toString()).eq(numberOfPeriod.toString()); + expect(escrowState.recipient.toString()).eq(recipient.toString()); + expect(escrowState.tokenMint.toString()).eq(tokenMint.toString()); + expect(escrowState.creator.toString()).eq( + ownerKeypair.publicKey.toString() + ); + expect(escrowState.base.toString()).eq(baseKP.publicKey.toString()); + expect(escrowState.updateRecipientMode).eq(updateRecipientMode); + expect(escrowState.tokenProgramFlag).eq( + tokenProgram == TOKEN_PROGRAM_ID ? 0 : 1 + ); + } + return escrow; +} -export interface ClaimTokenParams { - isAssertion: boolean, - escrow: web3.PublicKey, - recipient: web3.Keypair, - maxAmount: BN, - recipientToken: web3.PublicKey, +export interface ClaimTokenParamsV2 { + isAssertion: boolean; + escrow: web3.PublicKey; + recipient: web3.Keypair; + maxAmount: BN; + recipientToken: web3.PublicKey; + tokenProgram: web3.PublicKey; } -export async function claimToken(params: ClaimTokenParams) { - let { isAssertion, escrow, recipient, maxAmount, recipientToken } = params; - const program = createLockerProgram(new Wallet(recipient)); - const escrowState = await program.account.vestingEscrow.fetch(escrow); - const escrowToken = getAssociatedTokenAddressSync( - escrowState.tokenMint, - escrow, - true, - TOKEN_PROGRAM_ID, - ASSOCIATED_TOKEN_PROGRAM_ID - ); +export async function claimTokenV2(params: ClaimTokenParamsV2) { + let { isAssertion, escrow, recipient, maxAmount, recipientToken } = params; + const program = createLockerProgram(new Wallet(recipient)); + const escrowState = await program.account.vestingEscrow.fetch(escrow); + const tokenProgram = + escrowState.tokenProgramFlag == ESCROW_USE_SPL_TOKEN + ? TOKEN_PROGRAM_ID + : TOKEN_2022_PROGRAM_ID; - await program.methods.claim(maxAmount).accounts({ - tokenProgram: TOKEN_PROGRAM_ID, - escrow, + const escrowToken = getAssociatedTokenAddressSync( + escrowState.tokenMint, + escrow, + true, + tokenProgram, + ASSOCIATED_TOKEN_PROGRAM_ID + ); + + let remainingAccountsInfo = null; + let remainingAccounts: AccountMeta[] = []; + if (tokenProgram == TOKEN_2022_PROGRAM_ID) { + let claimTransferHookAccounts = + await TokenExtensionUtil.getExtraAccountMetasForTransferHook( + program.provider.connection, + escrowState.tokenMint, escrowToken, - recipient: recipient.publicKey, recipientToken, - }).rpc(); -} + escrow, + TOKEN_2022_PROGRAM_ID + ); + [remainingAccountsInfo, remainingAccounts] = new RemainingAccountsBuilder() + .addSlice( + RemainingAccountsType.TransferHookEscrow, + claimTransferHookAccounts + ) + .build(); + } -export interface CreateEscrowMetadataParams { - isAssertion: boolean, - creator: web3.Keypair, - escrow: web3.PublicKey, - name: string, - description: string, - creatorEmail: string, - recipientEmail: string, + const tx = await program.methods + .claimV2(maxAmount, remainingAccountsInfo) + .accounts({ + tokenProgram, + tokenMint: escrowState.tokenMint, + memoProgram: MEMO_PROGRAM, + escrow, + escrowToken, + recipient: recipient.publicKey, + recipientToken, + }) + .remainingAccounts(remainingAccounts ? remainingAccounts : []) + .signers([recipient]) + .rpc(); + + console.log(" claim token signature", tx); } -export async function createEscrowMetadata(params: CreateEscrowMetadataParams) { - let { isAssertion, escrow, name, description, creatorEmail, recipientEmail, creator } = params; - const program = createLockerProgram(new Wallet(creator)); - const [escrowMetadata] = deriveEscrowMetadata(escrow, program.programId); - await program.methods.createVestingEscrowMetadata({ - name, - description, - creatorEmail, - recipientEmail, - }).accounts({ - escrow, - systemProgram: web3.SystemProgram.programId, - payer: creator.publicKey, - creator: creator.publicKey, - escrowMetadata - }).rpc(); - - if (isAssertion) { - const escrowMetadataState = await program.account.vestingEscrowMetadata.fetch(escrowMetadata); - expect(escrowMetadataState.escrow.toString()).eq(escrow.toString()); - expect(escrowMetadataState.name.toString()).eq(name.toString()); - expect(escrowMetadataState.description.toString()).eq(description.toString()); - expect(escrowMetadataState.creatorEmail.toString()).eq(creatorEmail.toString()); - expect(escrowMetadataState.recipientEmail.toString()).eq(recipientEmail.toString()); - } + +export interface CancelVestingPlanParams { + isAssertion: boolean; + escrow: web3.PublicKey; + rentReceiver: web3.PublicKey; + creatorToken: web3.PublicKey; + recipientToken: web3.PublicKey; + signer: web3.Keypair; } +export async function cancelVestingPlan( + params: CancelVestingPlanParams, + claimable_amount: number, + total_amount: number +) { + let { + isAssertion, + escrow, + rentReceiver, + creatorToken, + recipientToken, + signer, + } = params; + const program = createLockerProgram(new Wallet(signer)); + const escrowState = await program.account.vestingEscrow.fetch(escrow); + const tokenProgram = + escrowState.tokenProgramFlag == ESCROW_USE_SPL_TOKEN + ? TOKEN_PROGRAM_ID + : TOKEN_2022_PROGRAM_ID; -export interface UpdateRecipientParams { - isAssertion: boolean, - signer: web3.Keypair, - escrow: web3.PublicKey, - newRecipient: web3.PublicKey, - newRecipientEmail: null | string -} -export async function updateRecipient(params: UpdateRecipientParams) { - let { isAssertion, escrow, signer, newRecipient, newRecipientEmail } = params; - const program = createLockerProgram(new Wallet(signer)); - let escrowMetadata = null; - if (newRecipientEmail != null) { - [escrowMetadata] = deriveEscrowMetadata(escrow, program.programId); - } - await program.methods.updateVestingEscrowRecipient(newRecipient, newRecipientEmail).accounts({ + const escrowToken = getAssociatedTokenAddressSync( + escrowState.tokenMint, + escrow, + true, + tokenProgram, + ASSOCIATED_TOKEN_PROGRAM_ID + ); + + const creator_token_balance_before = ( + await program.provider.connection.getTokenAccountBalance(creatorToken) + ).value.amount; + + const recipient_token_balance_before = ( + await program.provider.connection.getTokenAccountBalance(recipientToken) + ).value.amount; + + let remainingAccountsInfo = null; + let remainingAccounts: AccountMeta[] = []; + if (tokenProgram == TOKEN_2022_PROGRAM_ID) { + let cancelTransferHookAccounts = + await TokenExtensionUtil.getExtraAccountMetasForTransferHook( + program.provider.connection, + escrowState.tokenMint, + escrowToken, + recipientToken, escrow, - escrowMetadata, - signer: signer.publicKey, - systemProgram: web3.SystemProgram.programId, - }).rpc(); - - if (isAssertion) { - const escrowState = await program.account.vestingEscrow.fetch(escrow); - expect(escrowState.recipient.toString()).eq(newRecipient.toString()); - if (newRecipientEmail != null) { - [escrowMetadata] = deriveEscrowMetadata(escrow, program.programId); - const escrowMetadataState = await program.account.vestingEscrowMetadata.fetch(escrowMetadata); - expect(escrowMetadataState.recipientEmail.toString()).eq(newRecipientEmail.toString()); - } + tokenProgram + ); - } -} \ No newline at end of file + [remainingAccountsInfo, remainingAccounts] = new RemainingAccountsBuilder() + .addSlice( + RemainingAccountsType.TransferHookEscrow, + cancelTransferHookAccounts + ) + .build(); + } + + await program.methods + .cancelVestingEscrow(remainingAccountsInfo) + .accounts({ + escrow, + tokenMint: escrowState.tokenMint, + escrowToken, + rentReceiver, + creatorToken: creatorToken, + recipientToken: recipientToken, + signer: signer.publicKey, + tokenProgram, + memoProgram: MEMO_PROGRAM, + }) + .preInstructions([ + ComputeBudgetProgram.setComputeUnitLimit({ + units: 400_000, + }), + ]) + .remainingAccounts(remainingAccounts ? remainingAccounts : []) + .signers([signer]) + .rpc(); + + let creator_fee = 0; + let claimer_fee = 0; + if (tokenProgram == TOKEN_2022_PROGRAM_ID) { + const feeConfig = getTransferFeeConfig( + await getMint( + program.provider.connection, + escrowState.tokenMint, + undefined, + TOKEN_2022_PROGRAM_ID + ) + ); + const epoch = BigInt(await getCurrentEpoch(program.provider.connection)); + creator_fee = feeConfig + ? Number( + calculateEpochFee( + feeConfig, + epoch, + BigInt(total_amount - claimable_amount) + ) + ) + : 0; + claimer_fee = feeConfig + ? Number(calculateEpochFee(feeConfig, epoch, BigInt(claimable_amount))) + : 0; + } + + if (isAssertion) { + const escrowState = await program.account.vestingEscrow.fetch(escrow); + expect(escrowState.cancelledAt.toNumber()).greaterThan(0); + + const escrowTokenAccount = await program.provider.connection.getAccountInfo( + escrowToken + ); + expect(escrowTokenAccount).eq(null); + + const creator_token_balance = ( + await program.provider.connection.getTokenAccountBalance(creatorToken) + ).value.amount; + expect( + parseInt(creator_token_balance_before) + + total_amount - + claimable_amount - + creator_fee + ).eq(parseInt(creator_token_balance)); + + const recipient_token_balance = ( + await program.provider.connection.getTokenAccountBalance(recipientToken) + ).value.amount; + expect( + parseInt(recipient_token_balance_before) + claimable_amount - claimer_fee + ).eq(parseInt(recipient_token_balance)); + } +} diff --git a/tests/locker_utils/token_2022/confidential_transfer.ts b/tests/locker_utils/token_2022/confidential_transfer.ts new file mode 100644 index 0000000..c4d4d39 --- /dev/null +++ b/tests/locker_utils/token_2022/confidential_transfer.ts @@ -0,0 +1,109 @@ +import { PublicKey, TransactionInstruction } from "@solana/web3.js"; +import { struct, u8 } from "@solana/buffer-layout"; +import { publicKey } from "@solana/buffer-layout-utils"; +import { + ExtensionType, + TOKEN_2022_PROGRAM_ID, + TokenInstruction, +} from "@solana/spl-token"; + +enum ConfidentialTransferInstruction { + // We are interested in initilization only + InitializeMint = 0, + // ... + // https://github.com/solana-labs/solana-program-library/blob/d4bbd51b5167d3f0c8a247b5f304a92e6482cd6f/token/program-2022/src/extension/confidential_transfer/instruction.rs#L33 +} + +interface InitializeConfidentialTransferMintInstructionData { + instruction: TokenInstruction.ConfidentialTransferExtension; + confidentialTransferInstruction: ConfidentialTransferInstruction.InitializeMint; + authority: PublicKey | null; + autoApproveNewAccounts: boolean; + auditorElgamalPubkey: PublicKey | null; +} + +const initializeConfidentialTransferMintInstructionData = + struct([ + u8("instruction"), + u8("confidentialTransferInstruction"), + publicKey("authority"), + u8("autoApproveNewAccounts"), + publicKey("auditorElgamalPubkey"), + ]); + +export function createInitializeConfidentialTransferMintInstruction( + mint: PublicKey, + authority: PublicKey, + autoApproveNewAccounts: boolean = true, + auditorElgamalPubkey: PublicKey = PublicKey.default, + programId: PublicKey = TOKEN_2022_PROGRAM_ID +) { + const keys = [{ pubkey: mint, isSigner: false, isWritable: true }]; + const data = Buffer.alloc( + initializeConfidentialTransferMintInstructionData.span + ); + initializeConfidentialTransferMintInstructionData.encode( + { + instruction: TokenInstruction.ConfidentialTransferExtension, + confidentialTransferInstruction: + ConfidentialTransferInstruction.InitializeMint, + authority, + auditorElgamalPubkey, + autoApproveNewAccounts, + }, + data + ); + + return new TransactionInstruction({ keys, programId, data }); +} + +enum ConfidentialTransferFeeInstruction { + // We are interested in initilization only + InitializeConfidentialTransferFeeConfig = 0, + // ... + // https://github.com/solana-labs/solana-program-library/blob/d4bbd51b5167d3f0c8a247b5f304a92e6482cd6f/token/program-2022/src/extension/confidential_transfer_fee/instruction.rs#L37 +} + +const TOKEN_INSTRUCTION_CONFIDENTIAL_TRANSFER_FEE_CONFIG_EXTENSION = 37; +const EXTENSION_TYPE_CONFIDENTIAL_TRANSFER_FEE_CONFIG = 16 as ExtensionType; + +interface InitializeConfidentialTransferFeeConfigInstructionData { + //TokenInstruction.ConfidentialTransferFeeExtension = 37 is commented out + //instruction: TokenInstruction.ConfidentialTransferFeeExtension; + instruction: 37; + confidentialTransferFeeInstruction: ConfidentialTransferFeeInstruction.InitializeConfidentialTransferFeeConfig; + authority: PublicKey | null; + withdrawWithheldAuthorityElgamalPubkey: PublicKey | null; +} + +const initializeConfidentialTransferFeeConfigInstructionData = + struct([ + u8("instruction"), + u8("confidentialTransferFeeInstruction"), + publicKey("authority"), + publicKey("withdrawWithheldAuthorityElgamalPubkey"), + ]); + +export function createInitializeConfidentialTransferFeeConfigInstruction( + mint: PublicKey, + authority: PublicKey, + withdrawWithheldAuthorityElgamalPubkey: PublicKey = PublicKey.default, + programId: PublicKey = TOKEN_2022_PROGRAM_ID +) { + const keys = [{ pubkey: mint, isSigner: false, isWritable: true }]; + const data = Buffer.alloc( + initializeConfidentialTransferFeeConfigInstructionData.span + ); + initializeConfidentialTransferFeeConfigInstructionData.encode( + { + instruction: TOKEN_INSTRUCTION_CONFIDENTIAL_TRANSFER_FEE_CONFIG_EXTENSION, + confidentialTransferFeeInstruction: + ConfidentialTransferFeeInstruction.InitializeConfidentialTransferFeeConfig, + authority, + withdrawWithheldAuthorityElgamalPubkey, + }, + data + ); + + return new TransactionInstruction({ keys, programId, data }); +} diff --git a/tests/locker_utils/token_2022/mint.ts b/tests/locker_utils/token_2022/mint.ts new file mode 100644 index 0000000..da14a5f --- /dev/null +++ b/tests/locker_utils/token_2022/mint.ts @@ -0,0 +1,370 @@ +import { AnchorProvider, web3 } from "@coral-xyz/anchor"; +import { + sendAndConfirmTransaction, + SystemProgram, + Transaction, +} from "@solana/web3.js"; +import { + AccountState, + ASSOCIATED_TOKEN_PROGRAM_ID, + createAssociatedTokenAccountIdempotent, + createInitializeDefaultAccountStateInstruction, + createInitializeGroupMemberPointerInstruction, + createInitializeGroupPointerInstruction, + createInitializeInterestBearingMintInstruction, + createInitializeMintCloseAuthorityInstruction, + createInitializeMintInstruction, + createInitializePermanentDelegateInstruction, + createInitializeTransferFeeConfigInstruction, + createInitializeTransferHookInstruction, + ExtensionType, + getMintLen, + getTypeLen, + mintTo, + thawAccount, + TOKEN_2022_PROGRAM_ID, +} from "@solana/spl-token"; +import { TEST_TRANSFER_HOOK_PROGRAM_ID } from "./token-extensions"; +import { createAndFundWallet } from "../../common"; +import { + createInitializeConfidentialTransferFeeConfigInstruction, + createInitializeConfidentialTransferMintInstruction, +} from "./confidential_transfer"; + +export const ADMIN = web3.Keypair.fromSecretKey( + Uint8Array.from([ + 158, 34, 15, 43, 215, 144, 99, 15, 49, 40, 202, 189, 244, 179, 70, 200, 156, + 140, 193, 247, 230, 82, 1, 103, 248, 52, 233, 244, 82, 52, 98, 196, 70, 116, + 166, 240, 58, 250, 204, 125, 228, 56, 121, 32, 22, 54, 214, 133, 148, 40, + 149, 8, 60, 74, 23, 212, 222, 54, 125, 78, 2, 203, 157, 229, + ]) +); + +let feeBasisPoints: number; +let maxFee: bigint; + +const tokenDecimal = 8; + +let mintAuthority: web3.Keypair; + +export async function createMintTransaction( + provider: AnchorProvider, + UserKP: web3.Keypair, + extensions: ExtensionType[], + shouldMint: boolean = true, + shouldHaveFreezeAuthority: boolean = false +) { + // Set the decimals, fee basis points, and maximum fee + feeBasisPoints = 100; // 1% + maxFee = BigInt(9 * Math.pow(10, tokenDecimal)); // 9 tokens + + // Define the amount to be minted and the amount to be transferred, accounting for decimals + let mintAmount = BigInt(1_000_000 * Math.pow(10, tokenDecimal)); // Mint 1,000,000 tokens + + mintAuthority = new web3.Keypair(); + let mintKeypair = new web3.Keypair(); + let TOKEN = mintKeypair.publicKey; + + { + await createAndFundWallet(provider.connection, mintAuthority); + } + + // Generate keys for transfer fee config authority and withdrawal authority + let transferFeeConfigAuthority = new web3.Keypair(); + let withdrawWithheldAuthority = new web3.Keypair(); + + let { instructions, postInstructions, additionalLength, rentReserveSpace } = + createExtensionMintIx( + extensions, + UserKP, + TOKEN, + transferFeeConfigAuthority, + withdrawWithheldAuthority + ); + + let mintLen = getMintLen(extensions) + additionalLength; + const mintLamports = + await provider.connection.getMinimumBalanceForRentExemption( + mintLen + rentReserveSpace + ); + + const mintTransaction = new Transaction().add( + SystemProgram.createAccount({ + fromPubkey: UserKP.publicKey, + newAccountPubkey: TOKEN, + space: mintLen, + lamports: mintLamports, + programId: TOKEN_2022_PROGRAM_ID, + }) + ); + + if (instructions.length > 0) mintTransaction.add(...instructions); + + mintTransaction.add( + createInitializeMintInstruction( + TOKEN, + tokenDecimal, + mintAuthority.publicKey, + shouldHaveFreezeAuthority ? mintAuthority.publicKey : null, + TOKEN_2022_PROGRAM_ID + ), + ...postInstructions + ); + + await sendAndConfirmTransaction( + provider.connection, + mintTransaction, + [UserKP, mintKeypair], + undefined + ); + + const userToken = await createAssociatedTokenAccountIdempotent( + provider.connection, + UserKP, + TOKEN, + UserKP.publicKey, + {}, + TOKEN_2022_PROGRAM_ID + ); + + if (shouldMint) { + if (extensions.indexOf(ExtensionType.DefaultAccountState) != -1) { + await thawAccount( + provider.connection, + mintAuthority, // Transaction fee payer + userToken, // Token Account to unfreeze + TOKEN, // Mint Account address + mintAuthority.publicKey, // Freeze Authority + undefined, // Additional signers + undefined, // Confirmation options + TOKEN_2022_PROGRAM_ID // Token Extension Program ID + ); + } + + await mintTo( + provider.connection, + UserKP, + TOKEN, + userToken, + mintAuthority, + mintAmount, + [], + undefined, + TOKEN_2022_PROGRAM_ID + ); + } + + return TOKEN; +} + +function createExtensionMintIx( + extensions: ExtensionType[], + UserKP: web3.Keypair, + TOKEN: web3.PublicKey, + transferFeeConfigAuthority: web3.Keypair, + withdrawWithheldAuthority: web3.Keypair +): { + instructions: web3.TransactionInstruction[]; + postInstructions: web3.TransactionInstruction[]; + additionalLength: number; + rentReserveSpace: number; +} { + const ix = []; + const postIx = []; + let confidentialTransferMintSizePatch = 0; + let confidentialTransferFeeConfigSizePatch = 0; + let groupPointerSize = 0; + + if (extensions.includes(ExtensionType.TransferFeeConfig)) { + ix.push( + createInitializeTransferFeeConfigInstruction( + TOKEN, + transferFeeConfigAuthority.publicKey, + withdrawWithheldAuthority.publicKey, + feeBasisPoints, + maxFee, + TOKEN_2022_PROGRAM_ID + ) + ); + } + + if (extensions.includes(ExtensionType.InterestBearingConfig)) { + ix.push( + createInitializeInterestBearingMintInstruction( + TOKEN, + UserKP.publicKey, + 10, + TOKEN_2022_PROGRAM_ID + ) + ); + } + + if (extensions.includes(ExtensionType.DefaultAccountState)) { + ix.push( + createInitializeDefaultAccountStateInstruction( + TOKEN, // Mint Account address + AccountState.Frozen, // Default AccountState + TOKEN_2022_PROGRAM_ID // Token Extension Program ID + ) + ); + } + + if (extensions.includes(ExtensionType.PermanentDelegate)) { + ix.push( + createInitializePermanentDelegateInstruction( + TOKEN, // Mint Account address + ADMIN.publicKey, // Designated Permanent Delegate + TOKEN_2022_PROGRAM_ID // Token Extension Program ID + ) + ); + } + + if (extensions.includes(ExtensionType.MintCloseAuthority)) { + ix.push( + createInitializeMintCloseAuthorityInstruction( + TOKEN, // Mint Account address + ADMIN.publicKey, // Designated Close Authority + TOKEN_2022_PROGRAM_ID // Token Extension Program ID + ) + ); + } + + if (extensions.includes(ExtensionType.TransferHook)) { + ix.push( + createInitializeTransferHookInstruction( + TOKEN, + ADMIN.publicKey, + TEST_TRANSFER_HOOK_PROGRAM_ID, // Transfer Hook Program ID + TOKEN_2022_PROGRAM_ID + ) + ); + + // create ExtraAccountMetaList account + postIx.push( + createInitializeExtraAccountMetaListInstruction(UserKP.publicKey, TOKEN) + ); + } + + if (extensions.includes(ExtensionType.ConfidentialTransferMint)) { + confidentialTransferMintSizePatch = + 65 - getTypeLen(ExtensionType.ConfidentialTransferMint); + ix.push( + createInitializeConfidentialTransferMintInstruction( + TOKEN, + mintAuthority.publicKey + ) + ); + } + + // ConfidentialTransferFeeConfig + // When both TransferFeeConfig and ConfidentialTransferMint are enabled, ConfidentialTransferFeeConfig is also required + + if ( + extensions.includes(ExtensionType.TransferFeeConfig) && + extensions.includes(ExtensionType.ConfidentialTransferMint) + ) { + // fixedLengthExtensions.push(ExtensionType.ConfidentialTransferFeeConfig); + // [May 25, 2024] ExtensionType.ConfidentialTransferFeeConfig is not yet supported in spl-token + // ConfidentialTransferFeeConfig struct fields: + // - authority: OptionalNonZeroPubkey (32 bytes) + // - withdraw_withheld_authority_elgamal_pubkey: ElGamalPubkey (32 bytes) + // - harvest_to_mint_enabled: bool (1 byte) + // - withheld_amount: EncryptedWithheldAmount (64 bytes) + confidentialTransferFeeConfigSizePatch = 2 + 2 + 129; // type + length + data + ix.push( + createInitializeConfidentialTransferFeeConfigInstruction( + TOKEN, + mintAuthority.publicKey, + mintAuthority.publicKey, + TOKEN_2022_PROGRAM_ID + ) + ); + } + + if (extensions.includes(ExtensionType.GroupPointer)) { + ix.push( + createInitializeGroupPointerInstruction( + TOKEN, + UserKP.publicKey, + TOKEN, + TOKEN_2022_PROGRAM_ID + ) + ); + + // This extension is not yet stable + // Trying this https://solana.com/developers/courses/token-extensions/group-member#lab + // However, the instruction always failed with error 0xc. + // groupPointerSize = TOKEN_GROUP_SIZE; + // postIx.push( + // createInitializeGroupInstruction({ + // group: TOKEN, + // maxSize: 10, + // mint: TOKEN, + // mintAuthority: UserKP.publicKey, + // programId: TOKEN_2022_PROGRAM_ID, + // updateAuthority: UserKP.publicKey, + // }) + // ); + } + + if (extensions.includes(ExtensionType.GroupMemberPointer)) { + ix.push( + createInitializeGroupMemberPointerInstruction( + TOKEN, + UserKP.publicKey, + TOKEN, + TOKEN_2022_PROGRAM_ID + ) + ); + } + + return { + instructions: ix, + postInstructions: postIx, + additionalLength: + confidentialTransferMintSizePatch + + confidentialTransferFeeConfigSizePatch, + rentReserveSpace: groupPointerSize, + }; +} + +export function createInitializeExtraAccountMetaListInstruction( + payer: web3.PublicKey, + tokenMint: web3.PublicKey +): web3.TransactionInstruction { + // create ExtraAccountMetaList account + const [extraAccountMetaListPDA] = web3.PublicKey.findProgramAddressSync( + [Buffer.from("extra-account-metas"), tokenMint.toBuffer()], + TEST_TRANSFER_HOOK_PROGRAM_ID + ); + const [counterAccountPDA] = web3.PublicKey.findProgramAddressSync( + [Buffer.from("counter"), tokenMint.toBuffer()], + TEST_TRANSFER_HOOK_PROGRAM_ID + ); + + return { + programId: TEST_TRANSFER_HOOK_PROGRAM_ID, + keys: [ + { pubkey: payer, isSigner: true, isWritable: true }, + { pubkey: extraAccountMetaListPDA, isSigner: false, isWritable: true }, + { pubkey: tokenMint, isSigner: false, isWritable: false }, + { pubkey: counterAccountPDA, isSigner: false, isWritable: true }, + { + pubkey: TOKEN_2022_PROGRAM_ID, + isSigner: false, + isWritable: false, + }, + { + pubkey: ASSOCIATED_TOKEN_PROGRAM_ID, + isSigner: false, + isWritable: false, + }, + { + pubkey: web3.SystemProgram.programId, + isSigner: false, + isWritable: false, + }, + ], + data: Buffer.from([0x5c, 0xc5, 0xae, 0xc5, 0x29, 0x7c, 0x13, 0x03]), // InitializeExtraAccountMetaList + }; +} diff --git a/tests/locker_utils/token_2022/remaining-accounts.ts b/tests/locker_utils/token_2022/remaining-accounts.ts new file mode 100644 index 0000000..8034e91 --- /dev/null +++ b/tests/locker_utils/token_2022/remaining-accounts.ts @@ -0,0 +1,48 @@ +import { AccountMeta } from "@solana/web3.js"; + +export enum RemainingAccountsType { + TransferHookEscrow = "transferHookEscrow", +} + +type RemainingAccountsAnchorType = { transferHookEscrow: {} }; + +export type RemainingAccountsSliceData = { + accountsType: RemainingAccountsAnchorType; + length: number; +}; + +export type RemainingAccountsInfoData = { + slices: RemainingAccountsSliceData[]; +}; + +// Option on Rust +// null is treated as None in Rust. undefined doesn't work. +export type OptionRemainingAccountsInfoData = RemainingAccountsInfoData | null; + +export class RemainingAccountsBuilder { + private remainingAccounts: AccountMeta[] = []; + private slices: RemainingAccountsSliceData[] = []; + + constructor() {} + + addSlice( + accountsType: RemainingAccountsType, + accounts?: AccountMeta[] + ): this { + if (!accounts || accounts.length === 0) return this; + + this.slices.push({ + accountsType: { [accountsType]: {} } as RemainingAccountsAnchorType, + length: accounts.length, + }); + this.remainingAccounts.push(...accounts); + + return this; + } + + build(): [OptionRemainingAccountsInfoData, AccountMeta[] | undefined] { + return this.slices.length === 0 + ? [null, undefined] + : [{ slices: this.slices }, this.remainingAccounts]; + } +} diff --git a/tests/locker_utils/token_2022/token-extensions.ts b/tests/locker_utils/token_2022/token-extensions.ts new file mode 100644 index 0000000..a46f730 --- /dev/null +++ b/tests/locker_utils/token_2022/token-extensions.ts @@ -0,0 +1,64 @@ +import { + AccountMeta, + Connection, + PublicKey, + TransactionInstruction, +} from "@solana/web3.js"; +import { + addExtraAccountMetasForExecute, + getMint, + getTransferHook, + TOKEN_2022_PROGRAM_ID, +} from "@solana/spl-token"; +import { web3 } from "@coral-xyz/anchor"; + +export const TEST_TRANSFER_HOOK_PROGRAM_ID = new web3.PublicKey( + "EBZDYx7599krFc4m2govwBdZcicr4GgepqC78m71nsHS" +); + +export class TokenExtensionUtil { + public static async getExtraAccountMetasForTransferHook( + connection: Connection, + tokenMint: PublicKey, + source: PublicKey, + destination: PublicKey, + owner: PublicKey, + tokenProgram: PublicKey + ): Promise { + let mint = await getMint(connection, tokenMint, "confirmed", tokenProgram); + const transferHook = getTransferHook(mint); + + if (!transferHook) return undefined; + + const instruction = new TransactionInstruction({ + programId: TOKEN_2022_PROGRAM_ID, + keys: [ + { pubkey: source, isSigner: false, isWritable: false }, + { + pubkey: tokenMint, + isSigner: false, + isWritable: false, + }, + { pubkey: destination, isSigner: false, isWritable: false }, + { pubkey: owner, isSigner: false, isWritable: false }, + { pubkey: owner, isSigner: false, isWritable: false }, + ], + }); + + // Note: + await addExtraAccountMetasForExecute( + connection, + instruction, + transferHook.programId, + source, + tokenMint, + destination, + owner, + 0, // extra account must not depend on the amount (the amount will be changed due to slippage) + "confirmed" + ); + + const extraAccountMetas = instruction.keys.slice(5); + return extraAccountMetas.length > 0 ? extraAccountMetas : undefined; + } +} diff --git a/tests/test_escrow_metadata.ts b/tests/test_escrow_metadata.ts new file mode 100644 index 0000000..b380e18 --- /dev/null +++ b/tests/test_escrow_metadata.ts @@ -0,0 +1,132 @@ +import * as anchor from "@coral-xyz/anchor"; +import { web3 } from "@coral-xyz/anchor"; +import { + createAssociatedTokenAccountIdempotent, + createInitializeMint2Instruction, + mintTo, + TOKEN_PROGRAM_ID, +} from "@solana/spl-token"; +import { BN } from "bn.js"; +import { createAndFundWallet, getCurrentBlockTime } from "./common"; +import { + createEscrowMetadata, + createLockerProgram, + createVestingPlan, +} from "./locker_utils"; +import { + sendAndConfirmTransaction, + SystemProgram, + Transaction, +} from "@solana/web3.js"; + +let provider = anchor.AnchorProvider.env(); +provider.opts.commitment = "confirmed"; + +describe("Escrow metadata", () => { + const tokenDecimal = 8; + let mintAuthority: web3.Keypair; + let mintKeypair: web3.Keypair; + let TOKEN: web3.PublicKey; + + let UserKP: web3.Keypair; + let RecipientKP: web3.Keypair; + let RecipientToken: web3.PublicKey; + + let mintAmount: bigint; + before(async () => { + { + const result = await createAndFundWallet(provider.connection); + UserKP = result.keypair; + } + { + const result = await createAndFundWallet(provider.connection); + RecipientKP = result.keypair; + } + + mintAuthority = new web3.Keypair(); + mintKeypair = new web3.Keypair(); + TOKEN = mintKeypair.publicKey; + + mintAmount = BigInt(1_000_000 * Math.pow(10, tokenDecimal)); // Mint 1,000,000 tokens + + // Step 2 - Create a New Token + const mintLamports = + await provider.connection.getMinimumBalanceForRentExemption(82); + const mintTransaction = new Transaction().add( + SystemProgram.createAccount({ + fromPubkey: UserKP.publicKey, + newAccountPubkey: TOKEN, + space: 82, + lamports: mintLamports, + programId: TOKEN_PROGRAM_ID, + }), + createInitializeMint2Instruction( + TOKEN, // Mint account + tokenDecimal, // Decimals + mintAuthority.publicKey, // Mint authority + null, // Freeze authority + TOKEN_PROGRAM_ID // Token program ID + ) + ); + await sendAndConfirmTransaction( + provider.connection, + mintTransaction, + [UserKP, mintKeypair], + undefined + ); + + const userToken = await createAssociatedTokenAccountIdempotent( + provider.connection, + UserKP, + TOKEN, + UserKP.publicKey, + {}, + TOKEN_PROGRAM_ID + ); + + await mintTo( + provider.connection, + UserKP, + TOKEN, + userToken, + mintAuthority, + mintAmount, + [], + undefined, + TOKEN_PROGRAM_ID + ); + }); + it("Full flow", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlan({ + ownerKeypair: UserKP, + tokenMint: TOKEN, + vestingStartTime: new BN(0), + isAssertion: true, + cliffTime, + frequency: new BN(1), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 0, + cancelMode: 0, + }); + + console.log("Create escrow metadata"); + await createEscrowMetadata({ + escrow, + name: "Jupiter lock", + description: "This is jupiter lock", + creatorEmail: "andrew@raccoons.dev", + recipientEmail: "max@raccoons.dev", + creator: UserKP, + isAssertion: true, + }); + }); +}); diff --git a/tests/test_locker.ts b/tests/test_locker.ts new file mode 100644 index 0000000..5974862 --- /dev/null +++ b/tests/test_locker.ts @@ -0,0 +1,200 @@ +import * as anchor from "@coral-xyz/anchor"; +import { web3 } from "@coral-xyz/anchor"; +import { + ASSOCIATED_TOKEN_PROGRAM_ID, + createAssociatedTokenAccountIdempotent, + createInitializeMint2Instruction, + mintTo, + TOKEN_PROGRAM_ID, +} from "@solana/spl-token"; +import { BN } from "bn.js"; +import { createAndFundWallet, getCurrentBlockTime, sleep } from "./common"; +import { + claimToken, + createLockerProgram, + createVestingPlan, +} from "./locker_utils"; +import { + sendAndConfirmTransaction, + SystemProgram, + Transaction, +} from "@solana/web3.js"; + +let provider = anchor.AnchorProvider.env(); +provider.opts.commitment = "confirmed"; + +describe("Full flow", () => { + const tokenDecimal = 8; + let mintAuthority: web3.Keypair; + let mintKeypair: web3.Keypair; + let TOKEN: web3.PublicKey; + + let UserKP: web3.Keypair; + let RecipientKP: web3.Keypair; + let RecipientToken: web3.PublicKey; + + let mintAmount: bigint; + + before(async () => { + { + const result = await createAndFundWallet(provider.connection); + UserKP = result.keypair; + } + { + const result = await createAndFundWallet(provider.connection); + RecipientKP = result.keypair; + } + + mintAuthority = new web3.Keypair(); + mintKeypair = new web3.Keypair(); + TOKEN = mintKeypair.publicKey; + + mintAmount = BigInt(1_000_000 * Math.pow(10, tokenDecimal)); // Mint 1,000,000 tokens + + // Step 2 - Create a New Token + const mintLamports = + await provider.connection.getMinimumBalanceForRentExemption(82); + const mintTransaction = new Transaction().add( + SystemProgram.createAccount({ + fromPubkey: UserKP.publicKey, + newAccountPubkey: TOKEN, + space: 82, + lamports: mintLamports, + programId: TOKEN_PROGRAM_ID, + }), + createInitializeMint2Instruction( + TOKEN, // Mint account + tokenDecimal, // Decimals + mintAuthority.publicKey, // Mint authority + null, // Freeze authority + TOKEN_PROGRAM_ID // Token program ID + ) + ); + await sendAndConfirmTransaction( + provider.connection, + mintTransaction, + [UserKP, mintKeypair], + undefined + ); + + const userToken = await createAssociatedTokenAccountIdempotent( + provider.connection, + UserKP, + TOKEN, + UserKP.publicKey, + {}, + TOKEN_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID + ); + + await mintTo( + provider.connection, + UserKP, + TOKEN, + userToken, + mintAuthority, + mintAmount, + [], + undefined, + TOKEN_PROGRAM_ID + ); + + RecipientToken = await createAssociatedTokenAccountIdempotent( + provider.connection, + UserKP, + TOKEN, + RecipientKP.publicKey, + {}, + TOKEN_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID + ); + }); + + it("Full flow", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlan({ + vestingStartTime: new BN(0), + ownerKeypair: UserKP, + tokenMint: TOKEN, + isAssertion: true, + cliffTime, + frequency: new BN(1), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 0, + cancelMode: 0, + }); + + while (true) { + const currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + if (currentBlockTime > cliffTime.toNumber()) { + break; + } else { + await sleep(1000); + console.log("Wait until cliffTime"); + } + } + + console.log("Claim token"); + await claimToken({ + recipient: RecipientKP, + recipientToken: RecipientToken, + escrow, + maxAmount: new BN(1_000_000), + isAssertion: true, + }); + }); + + it("Full flow with Cancel", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlan({ + vestingStartTime: new BN(0), + ownerKeypair: UserKP, + tokenMint: TOKEN, + isAssertion: true, + cliffTime, + frequency: new BN(1), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 0, + cancelMode: 0, + }); + + while (true) { + const currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + if (currentBlockTime > cliffTime.toNumber()) { + break; + } else { + await sleep(1000); + console.log("Wait until cliffTime"); + } + } + + console.log("Claim token"); + await claimToken({ + recipient: RecipientKP, + recipientToken: RecipientToken, + escrow, + maxAmount: new BN(1_000_000), + isAssertion: true, + }); + }); +}); diff --git a/tests/test_update_recipient.ts b/tests/test_update_recipient.ts new file mode 100644 index 0000000..9780605 --- /dev/null +++ b/tests/test_update_recipient.ts @@ -0,0 +1,373 @@ +import * as anchor from "@coral-xyz/anchor"; +import { web3 } from "@coral-xyz/anchor"; +import { + createAssociatedTokenAccountIdempotent, + createInitializeMint2Instruction, + mintTo, + TOKEN_PROGRAM_ID, +} from "@solana/spl-token"; +import { BN } from "bn.js"; +import { + createAndFundWallet, + getCurrentBlockTime, + invokeAndAssertError, +} from "./common"; +import { + createEscrowMetadata, + createLockerProgram, + createVestingPlan, + updateRecipient, +} from "./locker_utils"; +import { + sendAndConfirmTransaction, + SystemProgram, + Transaction, +} from "@solana/web3.js"; + +const provider = anchor.AnchorProvider.env(); +provider.opts.commitment = "confirmed"; + +describe("Update recipient", () => { + const tokenDecimal = 8; + let mintAuthority: web3.Keypair; + let mintKeypair: web3.Keypair; + let TOKEN: web3.PublicKey; + + let UserKP: web3.Keypair; + let RecipientKP: web3.Keypair; + + let mintAmount: bigint; + + before(async () => { + { + const result = await createAndFundWallet(provider.connection); + UserKP = result.keypair; + } + { + const result = await createAndFundWallet(provider.connection); + RecipientKP = result.keypair; + } + + mintAuthority = new web3.Keypair(); + mintKeypair = new web3.Keypair(); + TOKEN = mintKeypair.publicKey; + + mintAmount = BigInt(1_000_000 * Math.pow(10, tokenDecimal)); // Mint 1,000,000 tokens + + // Step 2 - Create a New Token + const mintLamports = + await provider.connection.getMinimumBalanceForRentExemption(82); + const mintTransaction = new Transaction().add( + SystemProgram.createAccount({ + fromPubkey: UserKP.publicKey, + newAccountPubkey: TOKEN, + space: 82, + lamports: mintLamports, + programId: TOKEN_PROGRAM_ID, + }), + createInitializeMint2Instruction( + TOKEN, // Mint account + tokenDecimal, // Decimals + mintAuthority.publicKey, // Mint authority + null, // Freeze authority + TOKEN_PROGRAM_ID // Token program ID + ) + ); + await sendAndConfirmTransaction( + provider.connection, + mintTransaction, + [UserKP, mintKeypair], + undefined + ); + + const userToken = await createAssociatedTokenAccountIdempotent( + provider.connection, + UserKP, + TOKEN, + UserKP.publicKey, + {}, + TOKEN_PROGRAM_ID + ); + + await mintTo( + provider.connection, + UserKP, + TOKEN, + userToken, + mintAuthority, + mintAmount, + [], + undefined, + TOKEN_PROGRAM_ID + ); + }); + it("No one is able to update", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlan({ + ownerKeypair: UserKP, + vestingStartTime: new BN(0), + tokenMint: TOKEN, + isAssertion: true, + cliffTime, + frequency: new BN(1), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 0, + cancelMode: 0, + }); + + console.log("Update recipient"); + const newRecipient = web3.Keypair.generate(); + invokeAndAssertError( + async () => { + await updateRecipient({ + escrow, + newRecipient: newRecipient.publicKey, + isAssertion: true, + signer: UserKP, + newRecipientEmail: null, + }); + }, + "Not permit to do this action", + true + ); + + invokeAndAssertError( + async () => { + await updateRecipient({ + escrow, + newRecipient: newRecipient.publicKey, + isAssertion: true, + signer: RecipientKP, + newRecipientEmail: null, + }); + }, + "Not permit to do this action", + true + ); + }); + + it("Creator is able to update recipient", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlan({ + ownerKeypair: UserKP, + tokenMint: TOKEN, + vestingStartTime: new BN(0), + isAssertion: true, + cliffTime, + frequency: new BN(1), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 1, + cancelMode: 0, + }); + console.log("Update recipient"); + const newRecipient = web3.Keypair.generate(); + invokeAndAssertError( + async () => { + await updateRecipient({ + escrow, + newRecipient: newRecipient.publicKey, + isAssertion: true, + signer: RecipientKP, + newRecipientEmail: null, + }); + }, + "Not permit to do this action", + true + ); + + await updateRecipient({ + escrow, + newRecipient: newRecipient.publicKey, + isAssertion: true, + signer: UserKP, + newRecipientEmail: null, + }); + }); + + it("Recipient is able to update recipient", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlan({ + ownerKeypair: UserKP, + vestingStartTime: new BN(0), + tokenMint: TOKEN, + isAssertion: true, + cliffTime, + frequency: new BN(1), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 2, + cancelMode: 0, + }); + console.log("Update recipient"); + const newRecipient = web3.Keypair.generate(); + invokeAndAssertError( + async () => { + await updateRecipient({ + escrow, + newRecipient: newRecipient.publicKey, + isAssertion: true, + signer: UserKP, + newRecipientEmail: null, + }); + }, + "Not permit to do this action", + true + ); + + await updateRecipient({ + escrow, + newRecipient: newRecipient.publicKey, + isAssertion: true, + signer: RecipientKP, + newRecipientEmail: null, + }); + }); + + it("Creator and Recipient is able to update recipient", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlan({ + ownerKeypair: UserKP, + vestingStartTime: new BN(0), + tokenMint: TOKEN, + isAssertion: true, + cliffTime, + frequency: new BN(1), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 3, + cancelMode: 0, + }); + console.log("Update recipient"); + await updateRecipient({ + escrow, + newRecipient: RecipientKP.publicKey, + isAssertion: true, + signer: UserKP, + newRecipientEmail: null, + }); + + await updateRecipient({ + escrow, + newRecipient: RecipientKP.publicKey, + isAssertion: true, + signer: RecipientKP, + newRecipientEmail: null, + }); + }); + + it("Update both recipient and recipient email", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlan({ + ownerKeypair: UserKP, + vestingStartTime: new BN(0), + tokenMint: TOKEN, + isAssertion: true, + cliffTime, + frequency: new BN(1), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 3, + cancelMode: 0, + }); + + console.log("Create escrow metadata"); + await createEscrowMetadata({ + escrow, + name: "Jupiter lock", + description: "This is jupiter lock", + creatorEmail: "andrew@raccoons.dev", + recipientEmail: "max@raccoons.dev", + creator: UserKP, + isAssertion: true, + }); + + it("Update both recipient and recipient email", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlan({ + ownerKeypair: UserKP, + tokenMint: TOKEN, + vestingStartTime: new BN(0), + isAssertion: true, + cliffTime, + frequency: new BN(1), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 3, + cancelMode: 0, + }); + + console.log("Create escrow metadata"); + await createEscrowMetadata({ + escrow, + name: "Jupiter lock", + description: "This is jupiter lock", + creatorEmail: "andrew@raccoons.dev", + recipientEmail: "max@raccoons.dev", + creator: UserKP, + isAssertion: true, + }); + + console.log("Update recipient with bigger email size"); + await updateRecipient({ + escrow, + newRecipient: RecipientKP.publicKey, + isAssertion: true, + signer: UserKP, + newRecipientEmail: "maximillian@raccoons.dev", + }); + + console.log("Update recipient with smaller email size"); + await updateRecipient({ + escrow, + newRecipient: RecipientKP.publicKey, + isAssertion: true, + signer: UserKP, + newRecipientEmail: "max@raccoons.dev", + }); + }); + }); +}); diff --git a/tests/update_recipient.ts b/tests/update_recipient.ts deleted file mode 100644 index 5136562..0000000 --- a/tests/update_recipient.ts +++ /dev/null @@ -1,285 +0,0 @@ -import * as anchor from "@coral-xyz/anchor"; -import { web3 } from "@coral-xyz/anchor"; -import { - ASSOCIATED_TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - createMint, - getOrCreateAssociatedTokenAccount, - mintTo, -} from "@solana/spl-token"; -import { BN } from "bn.js"; -import { - createAndFundWallet, - getCurrentBlockTime, - invokeAndAssertError, - sleep, -} from "./common"; -import { claimToken, createEscrowMetadata, createLockerProgram, createVestingPlan, updateRecipient } from "./locker_utils"; - - -const provider = anchor.AnchorProvider.env(); -provider.opts.commitment = 'confirmed'; - -describe("Update recipient", () => { - const tokenDecimal = 8; - let TOKEN: web3.PublicKey; - let UserKP: web3.Keypair; - let ReceipentKP: web3.Keypair; - - before(async () => { - { - const result = await createAndFundWallet(provider.connection); - UserKP = result.keypair; - } - { - const result = await createAndFundWallet(provider.connection); - ReceipentKP = result.keypair; - } - - TOKEN = await createMint( - provider.connection, - UserKP, - UserKP.publicKey, - null, - tokenDecimal, - web3.Keypair.generate(), - { - commitment: "confirmed", - }, - TOKEN_PROGRAM_ID - ); - - const userToken = await getOrCreateAssociatedTokenAccount( - provider.connection, - UserKP, - TOKEN, - UserKP.publicKey, - false, - "confirmed", - { - commitment: "confirmed", - }, - TOKEN_PROGRAM_ID, - ASSOCIATED_TOKEN_PROGRAM_ID - ); - // userBTC = userTokenX.address; - await mintTo( - provider.connection, - UserKP, - TOKEN, - userToken.address, - UserKP.publicKey, - 100 * 10 ** tokenDecimal, - [], - { - commitment: "confirmed", - }, - TOKEN_PROGRAM_ID - ); - }); - it("No one is able to update", async () => { - console.log("Create vesting plan"); - const program = createLockerProgram(new anchor.Wallet(UserKP)); - let currentBlockTime = await getCurrentBlockTime(program.provider.connection); - const cliffTime = new BN(currentBlockTime).add(new BN(5)); - let escrow = await createVestingPlan({ - ownerKeypair: UserKP, - vestingStartTime: new BN(0), - tokenMint: TOKEN, - isAssertion: true, - cliffTime, - frequency: new BN(1), - cliffUnlockAmount: new BN(100_000), - amountPerPeriod: new BN(50_000), - numberOfPeriod: new BN(2), - recipient: ReceipentKP.publicKey, - updateRecipientMode: 0, - }); - console.log("Update recipient"); - const newRecipient = web3.Keypair.generate(); - invokeAndAssertError(async () => { - await updateRecipient({ - escrow, - newRecipient: newRecipient.publicKey, - isAssertion: true, - signer: UserKP, - newRecipientEmail: null, - }); - }, "Not permit to do this action", true); - - invokeAndAssertError(async () => { - await updateRecipient({ - escrow, - newRecipient: newRecipient.publicKey, - isAssertion: true, - signer: ReceipentKP, - newRecipientEmail: null, - }); - }, "Not permit to do this action", true); - }); - - it("Creator is able to update recipient", async () => { - console.log("Create vesting plan"); - const program = createLockerProgram(new anchor.Wallet(UserKP)); - let currentBlockTime = await getCurrentBlockTime(program.provider.connection); - const cliffTime = new BN(currentBlockTime).add(new BN(5)); - let escrow = await createVestingPlan({ - ownerKeypair: UserKP, - vestingStartTime: new BN(0), - tokenMint: TOKEN, - isAssertion: true, - cliffTime, - frequency: new BN(1), - cliffUnlockAmount: new BN(100_000), - amountPerPeriod: new BN(50_000), - numberOfPeriod: new BN(2), - recipient: ReceipentKP.publicKey, - updateRecipientMode: 1, - }); - console.log("Update recipient"); - const newRecipient = web3.Keypair.generate(); - invokeAndAssertError(async () => { - await updateRecipient({ - escrow, - newRecipient: newRecipient.publicKey, - isAssertion: true, - signer: ReceipentKP, - newRecipientEmail: null, - }); - }, "Not permit to do this action", true); - - await updateRecipient({ - escrow, - newRecipient: newRecipient.publicKey, - isAssertion: true, - signer: UserKP, - newRecipientEmail: null, - }); - }); - - - it("Recipient is able to update recipient", async () => { - console.log("Create vesting plan"); - const program = createLockerProgram(new anchor.Wallet(UserKP)); - let currentBlockTime = await getCurrentBlockTime(program.provider.connection); - const cliffTime = new BN(currentBlockTime).add(new BN(5)); - let escrow = await createVestingPlan({ - ownerKeypair: UserKP, - vestingStartTime: new BN(0), - tokenMint: TOKEN, - isAssertion: true, - cliffTime, - frequency: new BN(1), - cliffUnlockAmount: new BN(100_000), - amountPerPeriod: new BN(50_000), - numberOfPeriod: new BN(2), - recipient: ReceipentKP.publicKey, - updateRecipientMode: 2, - }); - console.log("Update recipient"); - const newRecipient = web3.Keypair.generate(); - invokeAndAssertError(async () => { - await updateRecipient({ - escrow, - newRecipient: newRecipient.publicKey, - isAssertion: true, - signer: UserKP, - newRecipientEmail: null, - }); - }, "Not permit to do this action", true); - - await updateRecipient({ - escrow, - newRecipient: newRecipient.publicKey, - isAssertion: true, - signer: ReceipentKP, - newRecipientEmail: null, - }); - }); - - it("Creator and Recipient is able to update recipient", async () => { - console.log("Create vesting plan"); - const program = createLockerProgram(new anchor.Wallet(UserKP)); - let currentBlockTime = await getCurrentBlockTime(program.provider.connection); - const cliffTime = new BN(currentBlockTime).add(new BN(5)); - let escrow = await createVestingPlan({ - ownerKeypair: UserKP, - vestingStartTime: new BN(0), - tokenMint: TOKEN, - isAssertion: true, - cliffTime, - frequency: new BN(1), - cliffUnlockAmount: new BN(100_000), - amountPerPeriod: new BN(50_000), - numberOfPeriod: new BN(2), - recipient: ReceipentKP.publicKey, - updateRecipientMode: 3, - }); - console.log("Update recipient"); - await updateRecipient({ - escrow, - newRecipient: ReceipentKP.publicKey, - isAssertion: true, - signer: UserKP, - newRecipientEmail: null, - }); - - await updateRecipient({ - escrow, - newRecipient: ReceipentKP.publicKey, - isAssertion: true, - signer: ReceipentKP, - newRecipientEmail: null, - }); - }); - - - it("Update both recipient and recipient email", async () => { - console.log("Create vesting plan"); - const program = createLockerProgram(new anchor.Wallet(UserKP)); - let currentBlockTime = await getCurrentBlockTime(program.provider.connection); - const cliffTime = new BN(currentBlockTime).add(new BN(5)); - let escrow = await createVestingPlan({ - ownerKeypair: UserKP, - tokenMint: TOKEN, - vestingStartTime: new BN(0), - isAssertion: true, - cliffTime, - frequency: new BN(1), - cliffUnlockAmount: new BN(100_000), - amountPerPeriod: new BN(50_000), - numberOfPeriod: new BN(2), - recipient: ReceipentKP.publicKey, - updateRecipientMode: 3, - }); - - console.log("Create escrow metadata"); - await createEscrowMetadata({ - escrow, - name: "Jupiter lock", - description: "This is jupiter lock", - creatorEmail: "andrew@raccoons.dev", - recipientEmail: "max@raccoons.dev", - creator: UserKP, - isAssertion: true - }); - - console.log("Update recipient with bigger email size"); - await updateRecipient({ - escrow, - newRecipient: ReceipentKP.publicKey, - isAssertion: true, - signer: UserKP, - newRecipientEmail: "maximillian@raccoons.dev", - }); - - console.log("Update recipient with smaller email size"); - await updateRecipient({ - escrow, - newRecipient: ReceipentKP.publicKey, - isAssertion: true, - signer: UserKP, - newRecipientEmail: "max@raccoons.dev", - }); - }); -}); diff --git a/tests/v2/test_cancel.ts b/tests/v2/test_cancel.ts new file mode 100644 index 0000000..ce38b56 --- /dev/null +++ b/tests/v2/test_cancel.ts @@ -0,0 +1,377 @@ +import * as anchor from "@coral-xyz/anchor"; +import { web3 } from "@coral-xyz/anchor"; +import { + ASSOCIATED_TOKEN_PROGRAM_ID, + createAssociatedTokenAccountIdempotent, + ExtensionType, + getAssociatedTokenAddress, + TOKEN_2022_PROGRAM_ID, +} from "@solana/spl-token"; +import { BN } from "bn.js"; +import { + createAndFundWallet, + getCurrentBlockTime, + invokeAndAssertError, + sleep, +} from "../common"; +import { + createVestingPlanV2, + cancelVestingPlan, + createLockerProgram, +} from "../locker_utils"; +import { ADMIN, createMintTransaction } from "../locker_utils/token_2022/mint"; + +const provider = anchor.AnchorProvider.env(); +provider.opts.commitment = "confirmed"; + +describe("[V2] Cancel", () => { + let TOKEN: web3.PublicKey; + let UserKP: web3.Keypair; + let UserToken: web3.PublicKey; + let RecipientKP: web3.Keypair; + let RecipientToken: web3.PublicKey; + + let extensions: ExtensionType[]; + + before(async () => { + { + await createAndFundWallet(provider.connection, ADMIN); + } + { + const result = await createAndFundWallet(provider.connection); + UserKP = result.keypair; + } + { + const result = await createAndFundWallet(provider.connection); + RecipientKP = result.keypair; + } + + // Define the extensions to be used by the mint + extensions = [ExtensionType.TransferHook]; + + TOKEN = await createMintTransaction( + provider, + UserKP, + extensions, + true, + false + ); + + UserToken = await getAssociatedTokenAddress( + TOKEN, + UserKP.publicKey, + true, + TOKEN_2022_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID + ); + + RecipientToken = await createAssociatedTokenAccountIdempotent( + provider.connection, + UserKP, + TOKEN, + RecipientKP.publicKey, + {}, + TOKEN_2022_PROGRAM_ID + ); + }); + it("No one is able to cancel", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlanV2({ + ownerKeypair: UserKP, + vestingStartTime: new BN(0), + tokenMint: TOKEN, + isAssertion: true, + cliffTime, + frequency: new BN(10), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 0, + cancelMode: 0, + tokenProgram: TOKEN_2022_PROGRAM_ID, + }); + + console.log("Cancel vesting plan"); + invokeAndAssertError( + async () => { + await cancelVestingPlan( + { + escrow, + isAssertion: true, + rentReceiver: UserKP.publicKey, + creatorToken: UserToken, + recipientToken: RecipientToken, + signer: UserKP, + }, + 0, + 200_000 + ); + }, + "Not permit to do this action", + true + ); + + invokeAndAssertError( + async () => { + await cancelVestingPlan( + { + escrow, + isAssertion: true, + rentReceiver: UserKP.publicKey, + creatorToken: UserToken, + recipientToken: RecipientToken, + signer: RecipientKP, + }, + 0, + 200_000 + ); + }, + "Not permit to do this action", + true + ); + }); + + it("Creator is able to cancel", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlanV2({ + ownerKeypair: UserKP, + vestingStartTime: new BN(0), + tokenMint: TOKEN, + isAssertion: true, + cliffTime, + frequency: new BN(10), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 0, + cancelMode: 1, + tokenProgram: TOKEN_2022_PROGRAM_ID, + }); + + console.log("Cancel Vesting Plan"); + invokeAndAssertError( + async () => { + await cancelVestingPlan( + { + escrow, + isAssertion: true, + rentReceiver: UserKP.publicKey, + creatorToken: UserToken, + recipientToken: RecipientToken, + signer: RecipientKP, + }, + 0, + 200_000 + ); + }, + "Not permit to do this action", + true + ); + + await cancelVestingPlan( + { + escrow, + isAssertion: true, + rentReceiver: UserKP.publicKey, + creatorToken: UserToken, + recipientToken: RecipientToken, + signer: UserKP, + }, + 0, + 200_000 + ); + }); + + it("Recipient is able to cancel", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlanV2({ + ownerKeypair: UserKP, + vestingStartTime: new BN(0), + tokenMint: TOKEN, + isAssertion: true, + cliffTime, + frequency: new BN(10), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 0, + cancelMode: 2, + tokenProgram: TOKEN_2022_PROGRAM_ID, + }); + console.log("Cancel Vesting Plan"); + const newRecipient = web3.Keypair.generate(); + invokeAndAssertError( + async () => { + await cancelVestingPlan( + { + escrow, + isAssertion: true, + rentReceiver: UserKP.publicKey, + creatorToken: UserToken, + recipientToken: RecipientToken, + signer: UserKP, + }, + 0, + 200_000 + ); + }, + "Not permit to do this action", + true + ); + + await cancelVestingPlan( + { + escrow, + isAssertion: true, + rentReceiver: UserKP.publicKey, + creatorToken: UserToken, + recipientToken: RecipientToken, + signer: RecipientKP, + }, + 0, + 200_000 + ); + }); + + it("Creator and Recipient is able to cancel, called by creator", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlanV2({ + ownerKeypair: UserKP, + vestingStartTime: new BN(0), + tokenMint: TOKEN, + isAssertion: true, + cliffTime, + frequency: new BN(10), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 0, + cancelMode: 3, + tokenProgram: TOKEN_2022_PROGRAM_ID, + }); + console.log("Cancel Vesting Plan"); + await cancelVestingPlan( + { + escrow, + isAssertion: true, + rentReceiver: UserKP.publicKey, + creatorToken: UserToken, + recipientToken: RecipientToken, + signer: UserKP, + }, + 0, + 200_000 + ); + }); + + it("Creator and Recipient is able to cancel, called by recipient", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlanV2({ + ownerKeypair: UserKP, + vestingStartTime: new BN(0), + tokenMint: TOKEN, + isAssertion: true, + cliffTime, + frequency: new BN(10), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 0, + cancelMode: 3, + tokenProgram: TOKEN_2022_PROGRAM_ID, + }); + console.log("Cancel Vesting Plan"); + await cancelVestingPlan( + { + escrow, + isAssertion: true, + rentReceiver: UserKP.publicKey, + creatorToken: UserToken, + recipientToken: RecipientToken, + signer: RecipientKP, + }, + 0, + 200_000 + ); + }); + + it("Creator is able to cancel after cliff time", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlanV2({ + ownerKeypair: UserKP, + vestingStartTime: new BN(0), + tokenMint: TOKEN, + isAssertion: true, + cliffTime, + frequency: new BN(10), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 0, + cancelMode: 1, + tokenProgram: TOKEN_2022_PROGRAM_ID, + }); + + while (true) { + const currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + if (currentBlockTime > cliffTime.toNumber()) { + break; + } else { + await sleep(1000); + console.log("Wait until startTime"); + } + } + + console.log("Cancel Vesting Plan"); + await cancelVestingPlan( + { + escrow, + isAssertion: true, + rentReceiver: UserKP.publicKey, + creatorToken: UserToken, + recipientToken: RecipientToken, + signer: UserKP, + }, + 100_000, + 200_000 + ); + }); +}); diff --git a/tests/v2/test_cancel_spl_token.ts b/tests/v2/test_cancel_spl_token.ts new file mode 100644 index 0000000..87e67fc --- /dev/null +++ b/tests/v2/test_cancel_spl_token.ts @@ -0,0 +1,418 @@ +import * as anchor from "@coral-xyz/anchor"; +import { web3 } from "@coral-xyz/anchor"; +import { + ASSOCIATED_TOKEN_PROGRAM_ID, + createAssociatedTokenAccountIdempotent, + createInitializeMint2Instruction, + mintTo, + TOKEN_PROGRAM_ID, +} from "@solana/spl-token"; +import { BN } from "bn.js"; +import { + createAndFundWallet, + getCurrentBlockTime, + invokeAndAssertError, + sleep, +} from "../common"; +import { + cancelVestingPlan, + createVestingPlanV2, + createLockerProgram, +} from "../locker_utils"; +import { + sendAndConfirmTransaction, + SystemProgram, + Transaction, +} from "@solana/web3.js"; + +const provider = anchor.AnchorProvider.env(); +provider.opts.commitment = "confirmed"; + +describe("[V2] Cancel with SPL Token", () => { + const tokenDecimal = 8; + let mintAuthority: web3.Keypair; + let mintKeypair: web3.Keypair; + let TOKEN: web3.PublicKey; + + let UserKP: web3.Keypair; + let UserToken: web3.PublicKey; + let RecipientKP: web3.Keypair; + let RecipientToken: web3.PublicKey; + + let mintAmount: bigint; + + before(async () => { + { + const result = await createAndFundWallet(provider.connection); + UserKP = result.keypair; + } + { + const result = await createAndFundWallet(provider.connection); + RecipientKP = result.keypair; + } + + mintAuthority = new web3.Keypair(); + mintKeypair = new web3.Keypair(); + TOKEN = mintKeypair.publicKey; + + mintAmount = BigInt(1_000_000 * Math.pow(10, tokenDecimal)); // Mint 1,000,000 tokens + + // Step 2 - Create a New Token + const mintLamports = + await provider.connection.getMinimumBalanceForRentExemption(82); + const mintTransaction = new Transaction().add( + SystemProgram.createAccount({ + fromPubkey: UserKP.publicKey, + newAccountPubkey: TOKEN, + space: 82, + lamports: mintLamports, + programId: TOKEN_PROGRAM_ID, + }), + createInitializeMint2Instruction( + TOKEN, // Mint account + tokenDecimal, // Decimals + mintAuthority.publicKey, // Mint authority + null, // Freeze authority + TOKEN_PROGRAM_ID // Token program ID + ) + ); + await sendAndConfirmTransaction( + provider.connection, + mintTransaction, + [UserKP, mintKeypair], + undefined + ); + + UserToken = await createAssociatedTokenAccountIdempotent( + provider.connection, + UserKP, + TOKEN, + UserKP.publicKey, + {}, + TOKEN_PROGRAM_ID + ); + + await mintTo( + provider.connection, + UserKP, + TOKEN, + UserToken, + mintAuthority, + mintAmount, + [], + undefined, + TOKEN_PROGRAM_ID + ); + + RecipientToken = await createAssociatedTokenAccountIdempotent( + provider.connection, + UserKP, + TOKEN, + RecipientKP.publicKey, + {}, + TOKEN_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID + ); + }); + it("No one is able to cancel", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlanV2({ + ownerKeypair: UserKP, + vestingStartTime: new BN(0), + tokenMint: TOKEN, + isAssertion: true, + cliffTime, + frequency: new BN(1), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 0, + cancelMode: 0, + tokenProgram: TOKEN_PROGRAM_ID, + }); + + console.log("Cancel vesting plan"); + const newRecipient = web3.Keypair.generate(); + invokeAndAssertError( + async () => { + await cancelVestingPlan( + { + escrow, + isAssertion: true, + rentReceiver: UserKP.publicKey, + creatorToken: UserToken, + recipientToken: RecipientToken, + signer: UserKP, + }, + 0, + 200_000 + ); + }, + "Not permit to do this action", + true + ); + + invokeAndAssertError( + async () => { + await cancelVestingPlan( + { + escrow, + isAssertion: true, + rentReceiver: UserKP.publicKey, + creatorToken: UserToken, + recipientToken: RecipientToken, + signer: RecipientKP, + }, + 0, + 200_000 + ); + }, + "Not permit to do this action", + true + ); + }); + + it("Creator is able to cancel", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlanV2({ + ownerKeypair: UserKP, + vestingStartTime: new BN(0), + tokenMint: TOKEN, + isAssertion: true, + cliffTime, + frequency: new BN(1), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 0, + cancelMode: 1, + tokenProgram: TOKEN_PROGRAM_ID, + }); + console.log("Cancel Vesting Plan"); + const newRecipient = web3.Keypair.generate(); + invokeAndAssertError( + async () => { + await cancelVestingPlan( + { + escrow, + isAssertion: true, + rentReceiver: UserKP.publicKey, + creatorToken: UserToken, + recipientToken: RecipientToken, + signer: RecipientKP, + }, + 0, + 200_000 + ); + }, + "Not permit to do this action", + true + ); + + await cancelVestingPlan( + { + escrow, + isAssertion: true, + rentReceiver: UserKP.publicKey, + creatorToken: UserToken, + recipientToken: RecipientToken, + signer: UserKP, + }, + 0, + 200_000 + ); + }); + + it("Recipient is able to cancel", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlanV2({ + ownerKeypair: UserKP, + vestingStartTime: new BN(0), + tokenMint: TOKEN, + isAssertion: true, + cliffTime, + frequency: new BN(1), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 0, + cancelMode: 2, + tokenProgram: TOKEN_PROGRAM_ID, + }); + console.log("Cancel Vesting Plan"); + const newRecipient = web3.Keypair.generate(); + invokeAndAssertError( + async () => { + await cancelVestingPlan( + { + escrow, + isAssertion: true, + rentReceiver: UserKP.publicKey, + creatorToken: UserToken, + recipientToken: RecipientToken, + signer: UserKP, + }, + 0, + 200_000 + ); + }, + "Not permit to do this action", + true + ); + + await cancelVestingPlan( + { + escrow, + isAssertion: true, + rentReceiver: UserKP.publicKey, + creatorToken: UserToken, + recipientToken: RecipientToken, + signer: RecipientKP, + }, + 0, + 200_000 + ); + }); + + it("Creator and Recipient is able to cancel, called by creator", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlanV2({ + ownerKeypair: UserKP, + vestingStartTime: new BN(0), + tokenMint: TOKEN, + isAssertion: true, + cliffTime, + frequency: new BN(1), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 0, + cancelMode: 3, + tokenProgram: TOKEN_PROGRAM_ID, + }); + console.log("Cancel Vesting Plan"); + await cancelVestingPlan( + { + escrow, + isAssertion: true, + rentReceiver: UserKP.publicKey, + creatorToken: UserToken, + recipientToken: RecipientToken, + signer: UserKP, + }, + 0, + 200_000 + ); + }); + + it("Creator and Recipient is able to cancel, called by recipient", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlanV2({ + ownerKeypair: UserKP, + vestingStartTime: new BN(0), + tokenMint: TOKEN, + isAssertion: true, + cliffTime, + frequency: new BN(1), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 0, + cancelMode: 3, + tokenProgram: TOKEN_PROGRAM_ID, + }); + console.log("Cancel Vesting Plan"); + await cancelVestingPlan( + { + escrow, + isAssertion: true, + rentReceiver: UserKP.publicKey, + creatorToken: UserToken, + recipientToken: RecipientToken, + signer: RecipientKP, + }, + 0, + 200_000 + ); + }); + + it("Creator is able to cancel after cliff time", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlanV2({ + ownerKeypair: UserKP, + vestingStartTime: new BN(0), + tokenMint: TOKEN, + isAssertion: true, + cliffTime, + frequency: new BN(10), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 0, + cancelMode: 1, + tokenProgram: TOKEN_PROGRAM_ID, + }); + + while (true) { + const currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + if (currentBlockTime > cliffTime.toNumber()) { + break; + } else { + await sleep(1000); + console.log("Wait until startTime"); + } + } + + console.log("Cancel Vesting Plan"); + await cancelVestingPlan( + { + escrow, + isAssertion: true, + rentReceiver: UserKP.publicKey, + creatorToken: UserToken, + recipientToken: RecipientToken, + signer: UserKP, + }, + 100_000, + 200_000 + ); + }); +}); diff --git a/tests/v2/test_cancel_with_harvest_fees.ts b/tests/v2/test_cancel_with_harvest_fees.ts new file mode 100644 index 0000000..e3f46c2 --- /dev/null +++ b/tests/v2/test_cancel_with_harvest_fees.ts @@ -0,0 +1,377 @@ +import * as anchor from "@coral-xyz/anchor"; +import { web3 } from "@coral-xyz/anchor"; +import { + ASSOCIATED_TOKEN_PROGRAM_ID, + createAssociatedTokenAccountIdempotent, + ExtensionType, + getAssociatedTokenAddress, + TOKEN_2022_PROGRAM_ID, +} from "@solana/spl-token"; +import { BN } from "bn.js"; +import { + createAndFundWallet, + getCurrentBlockTime, + invokeAndAssertError, + sleep, +} from "../common"; +import { + createVestingPlanV2, + cancelVestingPlan, + createLockerProgram, +} from "../locker_utils"; +import { ADMIN, createMintTransaction } from "../locker_utils/token_2022/mint"; + +const provider = anchor.AnchorProvider.env(); +provider.opts.commitment = "confirmed"; + +describe("[V2] Cancel, with transfer fees and harvesting", () => { + let TOKEN: web3.PublicKey; + let UserKP: web3.Keypair; + let UserToken: web3.PublicKey; + let RecipientKP: web3.Keypair; + let RecipientToken: web3.PublicKey; + + let extensions: ExtensionType[]; + + before(async () => { + { + await createAndFundWallet(provider.connection, ADMIN); + } + { + const result = await createAndFundWallet(provider.connection); + UserKP = result.keypair; + } + { + const result = await createAndFundWallet(provider.connection); + RecipientKP = result.keypair; + } + + // Define the extensions to be used by the mint + extensions = [ExtensionType.TransferHook, ExtensionType.TransferFeeConfig]; + + TOKEN = await createMintTransaction( + provider, + UserKP, + extensions, + true, + false + ); + + UserToken = await getAssociatedTokenAddress( + TOKEN, + UserKP.publicKey, + true, + TOKEN_2022_PROGRAM_ID, + ASSOCIATED_TOKEN_PROGRAM_ID + ); + + RecipientToken = await createAssociatedTokenAccountIdempotent( + provider.connection, + UserKP, + TOKEN, + RecipientKP.publicKey, + {}, + TOKEN_2022_PROGRAM_ID + ); + }); + it("No one is able to cancel", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlanV2({ + ownerKeypair: UserKP, + vestingStartTime: new BN(0), + tokenMint: TOKEN, + isAssertion: true, + cliffTime, + frequency: new BN(10), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 0, + cancelMode: 0, + tokenProgram: TOKEN_2022_PROGRAM_ID, + }); + + console.log("Cancel vesting plan"); + invokeAndAssertError( + async () => { + await cancelVestingPlan( + { + escrow, + isAssertion: true, + rentReceiver: UserKP.publicKey, + creatorToken: UserToken, + recipientToken: RecipientToken, + signer: UserKP, + }, + 0, + 200_000 + ); + }, + "Not permit to do this action", + true + ); + + invokeAndAssertError( + async () => { + await cancelVestingPlan( + { + escrow, + isAssertion: true, + rentReceiver: UserKP.publicKey, + creatorToken: UserToken, + recipientToken: RecipientToken, + signer: RecipientKP, + }, + 0, + 200_000 + ); + }, + "Not permit to do this action", + true + ); + }); + + it("Creator is able to cancel", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlanV2({ + ownerKeypair: UserKP, + vestingStartTime: new BN(0), + tokenMint: TOKEN, + isAssertion: true, + cliffTime, + frequency: new BN(10), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 0, + cancelMode: 1, + tokenProgram: TOKEN_2022_PROGRAM_ID, + }); + + console.log("Cancel Vesting Plan"); + invokeAndAssertError( + async () => { + await cancelVestingPlan( + { + escrow, + isAssertion: true, + rentReceiver: UserKP.publicKey, + creatorToken: UserToken, + recipientToken: RecipientToken, + signer: RecipientKP, + }, + 0, + 200_000 + ); + }, + "Not permit to do this action", + true + ); + + await cancelVestingPlan( + { + escrow, + isAssertion: true, + rentReceiver: UserKP.publicKey, + creatorToken: UserToken, + recipientToken: RecipientToken, + signer: UserKP, + }, + 0, + 200_000 + ); + }); + + it("Recipient is able to cancel", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlanV2({ + ownerKeypair: UserKP, + vestingStartTime: new BN(0), + tokenMint: TOKEN, + isAssertion: true, + cliffTime, + frequency: new BN(10), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 0, + cancelMode: 2, + tokenProgram: TOKEN_2022_PROGRAM_ID, + }); + console.log("Cancel Vesting Plan"); + const newRecipient = web3.Keypair.generate(); + invokeAndAssertError( + async () => { + await cancelVestingPlan( + { + escrow, + isAssertion: true, + rentReceiver: UserKP.publicKey, + creatorToken: UserToken, + recipientToken: RecipientToken, + signer: UserKP, + }, + 0, + 200_000 + ); + }, + "Not permit to do this action", + true + ); + + await cancelVestingPlan( + { + escrow, + isAssertion: true, + rentReceiver: UserKP.publicKey, + creatorToken: UserToken, + recipientToken: RecipientToken, + signer: RecipientKP, + }, + 0, + 200_000 + ); + }); + + it("Creator and Recipient is able to cancel, called by creator", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlanV2({ + ownerKeypair: UserKP, + vestingStartTime: new BN(0), + tokenMint: TOKEN, + isAssertion: true, + cliffTime, + frequency: new BN(10), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 0, + cancelMode: 3, + tokenProgram: TOKEN_2022_PROGRAM_ID, + }); + console.log("Cancel Vesting Plan"); + await cancelVestingPlan( + { + escrow, + isAssertion: true, + rentReceiver: UserKP.publicKey, + creatorToken: UserToken, + recipientToken: RecipientToken, + signer: UserKP, + }, + 0, + 200_000 + ); + }); + + it("Creator and Recipient is able to cancel, called by recipient", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlanV2({ + ownerKeypair: UserKP, + vestingStartTime: new BN(0), + tokenMint: TOKEN, + isAssertion: true, + cliffTime, + frequency: new BN(10), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 0, + cancelMode: 3, + tokenProgram: TOKEN_2022_PROGRAM_ID, + }); + console.log("Cancel Vesting Plan"); + await cancelVestingPlan( + { + escrow, + isAssertion: true, + rentReceiver: UserKP.publicKey, + creatorToken: UserToken, + recipientToken: RecipientToken, + signer: RecipientKP, + }, + 0, + 200_000 + ); + }); + + it("Creator is able to cancel after cliff time", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlanV2({ + ownerKeypair: UserKP, + vestingStartTime: new BN(0), + tokenMint: TOKEN, + isAssertion: true, + cliffTime, + frequency: new BN(10), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 0, + cancelMode: 1, + tokenProgram: TOKEN_2022_PROGRAM_ID, + }); + + while (true) { + const currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + if (currentBlockTime > cliffTime.toNumber()) { + break; + } else { + await sleep(1000); + console.log("Wait until startTime"); + } + } + + console.log("Cancel Vesting Plan"); + await cancelVestingPlan( + { + escrow, + isAssertion: true, + rentReceiver: UserKP.publicKey, + creatorToken: UserToken, + recipientToken: RecipientToken, + signer: UserKP, + }, + 100_000, + 200_000 + ); + }); +}); diff --git a/tests/v2/test_extensions.ts b/tests/v2/test_extensions.ts new file mode 100644 index 0000000..3e4aaf1 --- /dev/null +++ b/tests/v2/test_extensions.ts @@ -0,0 +1,218 @@ +import * as anchor from "@coral-xyz/anchor"; +import { web3 } from "@coral-xyz/anchor"; +import { + createAssociatedTokenAccountIdempotent, + ExtensionType, + TOKEN_2022_PROGRAM_ID, +} from "@solana/spl-token"; +import { BN } from "bn.js"; +import { createAndFundWallet, getCurrentBlockTime, sleep } from "../common"; +import { + claimTokenV2, + createLockerProgram, + createVestingPlanV2, +} from "../locker_utils"; +import { assert } from "chai"; +import { ADMIN, createMintTransaction } from "../locker_utils/token_2022/mint"; + +const provider = anchor.AnchorProvider.env(); + +describe("[V2] Test supported/unsupported Token Mint", () => { + let TOKEN: web3.PublicKey; + let UserKP: web3.Keypair; + let RecipientKP: web3.Keypair; + + before(async () => { + { + await createAndFundWallet(provider.connection, ADMIN); + } + { + const result = await createAndFundWallet(provider.connection); + UserKP = result.keypair; + } + { + const result = await createAndFundWallet(provider.connection); + RecipientKP = result.keypair; + } + }); + + it("InterestBearingConfig", async () => { + // Define the extensions to be used by the mint + let extensions = [ExtensionType.InterestBearingConfig]; + + TOKEN = await createMintTransaction(provider, UserKP, extensions); + + await check(TOKEN); + }); + + it("DefaultAccountState", async () => { + // Define the extensions to be used by the mint + let extensions = [ExtensionType.DefaultAccountState]; + + TOKEN = await createMintTransaction( + provider, + UserKP, + extensions, + true, + true + ); + + await check(TOKEN, "Program log: Error: Account is frozen"); + }); + + it("FreezeAuthority", async () => { + let extensions = []; + + TOKEN = await createMintTransaction( + provider, + UserKP, + extensions, + true, + true + ); + + await check(TOKEN, "Program log: Error: Account is frozen"); + }); + + it("ConfidentialTransferMint", async () => { + let extensions = [ExtensionType.ConfidentialTransferMint]; + + TOKEN = await createMintTransaction( + provider, + UserKP, + extensions, + true, + true + ); + + await check(TOKEN); + }); + + it("ConfidentialTransferFeeConfig", async () => { + let extensions = [ + ExtensionType.TransferFeeConfig, + ExtensionType.ConfidentialTransferMint, + ]; + + TOKEN = await createMintTransaction( + provider, + UserKP, + extensions, + true, + true + ); + + await check(TOKEN); + }); + + it("PermanentDelegate", async () => { + let extensions = [ExtensionType.PermanentDelegate]; + + TOKEN = await createMintTransaction(provider, UserKP, extensions); + + await check(TOKEN); + }); + + it("MintCloseAuthority", async () => { + let extensions = [ExtensionType.MintCloseAuthority]; + + TOKEN = await createMintTransaction(provider, UserKP, extensions); + + await check(TOKEN); + }); + + it("TransferHook", async () => { + let extensions = [ExtensionType.TransferHook]; + + TOKEN = await createMintTransaction(provider, UserKP, extensions); + + await check(TOKEN); + }); + + it("GroupPointer", async () => { + let extensions = [ExtensionType.GroupPointer]; + + TOKEN = await createMintTransaction(provider, UserKP, extensions); + + await check(TOKEN); + }); + + it("GroupMemberPointer", async () => { + let extensions = [ExtensionType.GroupMemberPointer]; + + TOKEN = await createMintTransaction(provider, UserKP, extensions); + + await check(TOKEN); + }); + + async function check(TOKEN: web3.PublicKey, errorMsg = "Unsupported mint") { + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + try { + let escrow = await createVestingPlanV2({ + ownerKeypair: UserKP, + vestingStartTime: new BN(0), + tokenMint: TOKEN, + isAssertion: true, + cliffTime, + frequency: new BN(1), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 0, + cancelMode: 0, + tokenProgram: TOKEN_2022_PROGRAM_ID, + }); + + let RecipientToken = await createAssociatedTokenAccountIdempotent( + provider.connection, + UserKP, + TOKEN, + RecipientKP.publicKey, + {}, + TOKEN_2022_PROGRAM_ID + ); + + while (true) { + const currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + if (currentBlockTime > cliffTime.toNumber()) { + break; + } else { + await sleep(1000); + console.log("Wait until startTime"); + } + } + + console.log("Claim token"); + try { + await claimTokenV2({ + recipient: RecipientKP, + recipientToken: RecipientToken, + escrow, + maxAmount: new BN(1_000_000), + isAssertion: true, + tokenProgram: TOKEN_2022_PROGRAM_ID, + }); + } catch (error) { + console.log(error); + } + } catch (error) { + if (error.logs.includes(errorMsg)) { + assert(true); + } else { + const errMsg = error.error?.errorMessage + ? error.error?.errorMessage + : anchor.AnchorError.parse(error.logs)?.error.errorMessage; + + assert.equal(errMsg, errorMsg); + } + } + } +}); diff --git a/tests/v2/test_locker.ts b/tests/v2/test_locker.ts new file mode 100644 index 0000000..3eb9797 --- /dev/null +++ b/tests/v2/test_locker.ts @@ -0,0 +1,111 @@ +import * as anchor from "@coral-xyz/anchor"; +import { web3 } from "@coral-xyz/anchor"; +import { + createAssociatedTokenAccountIdempotent, + ExtensionType, + TOKEN_2022_PROGRAM_ID, +} from "@solana/spl-token"; +import { BN } from "bn.js"; +import { createAndFundWallet, getCurrentBlockTime, sleep } from "../common"; +import { + claimTokenV2, + createVestingPlanV2, + createLockerProgram, +} from "../locker_utils"; +import { ADMIN, createMintTransaction } from "../locker_utils/token_2022/mint"; + +const provider = anchor.AnchorProvider.env(); + +describe("[V2] Test full flow With token 2022", () => { + let TOKEN: web3.PublicKey; + let UserKP: web3.Keypair; + let RecipientKP: web3.Keypair; + let RecipientToken: web3.PublicKey; + + let extensions: ExtensionType[]; + + before(async () => { + { + await createAndFundWallet(provider.connection, ADMIN); + } + { + const result = await createAndFundWallet(provider.connection); + UserKP = result.keypair; + } + { + const result = await createAndFundWallet(provider.connection); + RecipientKP = result.keypair; + } + + // Define the extensions to be used by the mint + extensions = [ExtensionType.TransferFeeConfig, ExtensionType.TransferHook]; + + TOKEN = await createMintTransaction( + provider, + UserKP, + extensions, + true, + false + ); + + RecipientToken = await createAssociatedTokenAccountIdempotent( + provider.connection, + UserKP, + TOKEN, + RecipientKP.publicKey, + {}, + TOKEN_2022_PROGRAM_ID + ); + }); + + it("Full flow", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlanV2({ + ownerKeypair: UserKP, + vestingStartTime: new BN(0), + tokenMint: TOKEN, + isAssertion: true, + cliffTime, + frequency: new BN(1), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 0, + cancelMode: 0, + tokenProgram: TOKEN_2022_PROGRAM_ID, + }); + + while (true) { + const currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + if (currentBlockTime > cliffTime.toNumber()) { + break; + } else { + await sleep(1000); + console.log("Wait until startTime"); + } + } + + console.log("Claim token"); + try { + await claimTokenV2({ + recipient: RecipientKP, + recipientToken: RecipientToken, + escrow, + maxAmount: new BN(1_000_000), + isAssertion: true, + tokenProgram: TOKEN_2022_PROGRAM_ID, + }); + } catch (error) { + console.log(error); + } + }); +}); diff --git a/tests/v2/test_locker_spl_token.ts b/tests/v2/test_locker_spl_token.ts new file mode 100644 index 0000000..8e6fde6 --- /dev/null +++ b/tests/v2/test_locker_spl_token.ts @@ -0,0 +1,153 @@ +import * as anchor from "@coral-xyz/anchor"; +import { web3 } from "@coral-xyz/anchor"; +import { + createAssociatedTokenAccountIdempotent, + createInitializeMint2Instruction, + mintTo, + TOKEN_PROGRAM_ID, +} from "@solana/spl-token"; +import { BN } from "bn.js"; +import { createAndFundWallet, getCurrentBlockTime, sleep } from "../common"; +import { + claimTokenV2, + createVestingPlanV2, + createLockerProgram, +} from "../locker_utils"; +import { + sendAndConfirmTransaction, + SystemProgram, + Transaction, +} from "@solana/web3.js"; + +const provider = anchor.AnchorProvider.env(); + +describe("[V2] Full flow With SPL Token", () => { + const tokenDecimal = 8; + let mintAuthority: web3.Keypair; + let mintKeypair: web3.Keypair; + let TOKEN: web3.PublicKey; + + let UserKP: web3.Keypair; + let RecipientKP: web3.Keypair; + let RecipientToken: web3.PublicKey; + + let mintAmount: bigint; + + before(async () => { + { + const result = await createAndFundWallet(provider.connection); + UserKP = result.keypair; + } + { + const result = await createAndFundWallet(provider.connection); + RecipientKP = result.keypair; + } + + mintAuthority = new web3.Keypair(); + mintKeypair = new web3.Keypair(); + TOKEN = mintKeypair.publicKey; + + mintAmount = BigInt(1_000_000 * Math.pow(10, tokenDecimal)); // Mint 1,000,000 tokens + + // Step 2 - Create a New Token + const mintLamports = + await provider.connection.getMinimumBalanceForRentExemption(82); + const mintTransaction = new Transaction().add( + SystemProgram.createAccount({ + fromPubkey: UserKP.publicKey, + newAccountPubkey: TOKEN, + space: 82, + lamports: mintLamports, + programId: TOKEN_PROGRAM_ID, + }), + createInitializeMint2Instruction( + TOKEN, // Mint account + tokenDecimal, // Decimals + mintAuthority.publicKey, // Mint authority + null, // Freeze authority + TOKEN_PROGRAM_ID // Token program ID + ) + ); + await sendAndConfirmTransaction( + provider.connection, + mintTransaction, + [UserKP, mintKeypair], + undefined + ); + + const userToken = await createAssociatedTokenAccountIdempotent( + provider.connection, + UserKP, + TOKEN, + UserKP.publicKey, + {}, + TOKEN_PROGRAM_ID + ); + + await mintTo( + provider.connection, + UserKP, + TOKEN, + userToken, + mintAuthority, + mintAmount, + [], + undefined, + TOKEN_PROGRAM_ID + ); + + RecipientToken = await createAssociatedTokenAccountIdempotent( + provider.connection, + UserKP, + TOKEN, + RecipientKP.publicKey, + {}, + TOKEN_PROGRAM_ID + ); + }); + + it("Full flow", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlanV2({ + ownerKeypair: UserKP, + vestingStartTime: new BN(0), + tokenMint: TOKEN, + isAssertion: true, + cliffTime, + frequency: new BN(1), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 0, + cancelMode: 0, + tokenProgram: TOKEN_PROGRAM_ID, + }); + while (true) { + const currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + if (currentBlockTime > cliffTime.toNumber()) { + break; + } else { + await sleep(1000); + console.log("Wait until cliffTime"); + } + } + + console.log("Claim token"); + await claimTokenV2({ + recipient: RecipientKP, + recipientToken: RecipientToken, + escrow, + maxAmount: new BN(1_000_000), + isAssertion: true, + tokenProgram: TOKEN_PROGRAM_ID, + }); + }); +}); diff --git a/tests/v2/test_update_recipient.ts b/tests/v2/test_update_recipient.ts new file mode 100644 index 0000000..0e297b6 --- /dev/null +++ b/tests/v2/test_update_recipient.ts @@ -0,0 +1,384 @@ +import * as anchor from "@coral-xyz/anchor"; +import { web3 } from "@coral-xyz/anchor"; +import { + createAssociatedTokenAccountIdempotent, + createInitializeMintInstruction, + createInitializeTransferFeeConfigInstruction, + ExtensionType, + getMintLen, + mintTo, + TOKEN_2022_PROGRAM_ID, +} from "@solana/spl-token"; +import { BN } from "bn.js"; +import { + createAndFundWallet, + getCurrentBlockTime, + invokeAndAssertError, +} from "../common"; +import { + sendAndConfirmTransaction, + SystemProgram, + Transaction, +} from "@solana/web3.js"; +import { + createEscrowMetadata, + createLockerProgram, + updateRecipient, +} from "../locker_utils"; +import { createVestingPlanV2 } from "../locker_utils"; + +const provider = anchor.AnchorProvider.env(); + +describe("[V2] Update recipient", () => { + const tokenDecimal = 8; + let UserKP: web3.Keypair; + let RecipientKP: web3.Keypair; + let mintAuthority: web3.Keypair; + let mintKeypair: web3.Keypair; + let TOKEN: web3.PublicKey; + + let transferFeeConfigAuthority: web3.Keypair; + let withdrawWithheldAuthority: web3.Keypair; + + let extensions: ExtensionType[]; + let mintLen: number; + + let feeBasisPoints: number; + let maxFee: bigint; + + // Define the amount to be minted and the amount to be transferred, accounting for decimals + let mintAmount: bigint; + let transferAmount: bigint; + + before(async () => { + { + const result = await createAndFundWallet(provider.connection); + UserKP = result.keypair; + } + { + const result = await createAndFundWallet(provider.connection); + RecipientKP = result.keypair; + } + + mintAuthority = new web3.Keypair(); + mintKeypair = new web3.Keypair(); + TOKEN = mintKeypair.publicKey; + + // Generate keys for transfer fee config authority and withdrawal authority + transferFeeConfigAuthority = new web3.Keypair(); + withdrawWithheldAuthority = new web3.Keypair(); + + // Define the extensions to be used by the mint + extensions = [ExtensionType.TransferFeeConfig]; + + // Calculate the length of the mint + mintLen = getMintLen(extensions); + + // Set the decimals, fee basis points, and maximum fee + feeBasisPoints = 100; // 1% + maxFee = BigInt(9 * Math.pow(10, tokenDecimal)); // 9 tokens + + // Define the amount to be minted and the amount to be transferred, accounting for decimals + mintAmount = BigInt(1_000_000 * Math.pow(10, tokenDecimal)); // Mint 1,000,000 tokens + transferAmount = BigInt(1_000 * Math.pow(10, tokenDecimal)); // Transfer 1,000 tokens + + // Step 2 - Create a New Token + const mintLamports = + await provider.connection.getMinimumBalanceForRentExemption(mintLen); + const mintTransaction = new Transaction().add( + SystemProgram.createAccount({ + fromPubkey: UserKP.publicKey, + newAccountPubkey: TOKEN, + space: mintLen, + lamports: mintLamports, + programId: TOKEN_2022_PROGRAM_ID, + }), + createInitializeTransferFeeConfigInstruction( + TOKEN, + transferFeeConfigAuthority.publicKey, + withdrawWithheldAuthority.publicKey, + feeBasisPoints, + maxFee, + TOKEN_2022_PROGRAM_ID + ), + createInitializeMintInstruction( + TOKEN, + tokenDecimal, + mintAuthority.publicKey, + null, + TOKEN_2022_PROGRAM_ID + ) + ); + await sendAndConfirmTransaction( + provider.connection, + mintTransaction, + [UserKP, mintKeypair], + undefined + ); + + const userToken = await createAssociatedTokenAccountIdempotent( + provider.connection, + UserKP, + TOKEN, + UserKP.publicKey, + {}, + TOKEN_2022_PROGRAM_ID + ); + const mintSig = await mintTo( + provider.connection, + UserKP, + TOKEN, + userToken, + mintAuthority, + mintAmount, + [], + undefined, + TOKEN_2022_PROGRAM_ID + ); + }); + + it("No one is able to update", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlanV2({ + ownerKeypair: UserKP, + vestingStartTime: new BN(0), + tokenMint: TOKEN, + isAssertion: true, + cliffTime, + frequency: new BN(1), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 0, + cancelMode: 0, + tokenProgram: TOKEN_2022_PROGRAM_ID, + }); + console.log("Update recipient"); + const newRecipient = web3.Keypair.generate(); + invokeAndAssertError( + async () => { + await updateRecipient({ + escrow, + newRecipient: newRecipient.publicKey, + isAssertion: true, + signer: UserKP, + newRecipientEmail: null, + }); + }, + "Not permit to do this action", + true + ); + + invokeAndAssertError( + async () => { + await updateRecipient({ + escrow, + newRecipient: newRecipient.publicKey, + isAssertion: true, + signer: RecipientKP, + newRecipientEmail: null, + }); + }, + "Not permit to do this action", + true + ); + }); + + it("Creator is able to update recipient", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlanV2({ + ownerKeypair: UserKP, + vestingStartTime: new BN(0), + tokenMint: TOKEN, + isAssertion: true, + cliffTime, + frequency: new BN(1), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 1, + cancelMode: 0, + tokenProgram: TOKEN_2022_PROGRAM_ID, + }); + console.log("Update recipient"); + const newRecipient = web3.Keypair.generate(); + invokeAndAssertError( + async () => { + await updateRecipient({ + escrow, + newRecipient: newRecipient.publicKey, + isAssertion: true, + signer: RecipientKP, + newRecipientEmail: null, + }); + }, + "Not permit to do this action", + true + ); + + await updateRecipient({ + escrow, + newRecipient: newRecipient.publicKey, + isAssertion: true, + signer: UserKP, + newRecipientEmail: null, + }); + }); + + it("Recipient is able to update recipient", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlanV2({ + ownerKeypair: UserKP, + vestingStartTime: new BN(0), + tokenMint: TOKEN, + isAssertion: true, + cliffTime, + frequency: new BN(1), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 2, + cancelMode: 0, + tokenProgram: TOKEN_2022_PROGRAM_ID, + }); + console.log("Update recipient"); + const newRecipient = web3.Keypair.generate(); + invokeAndAssertError( + async () => { + await updateRecipient({ + escrow, + newRecipient: newRecipient.publicKey, + isAssertion: true, + signer: UserKP, + newRecipientEmail: null, + }); + }, + "Not permit to do this action", + true + ); + + await updateRecipient({ + escrow, + newRecipient: newRecipient.publicKey, + isAssertion: true, + signer: RecipientKP, + newRecipientEmail: null, + }); + }); + + it("Creator and Recipient is able to update recipient", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlanV2({ + ownerKeypair: UserKP, + vestingStartTime: new BN(0), + tokenMint: TOKEN, + isAssertion: true, + cliffTime, + frequency: new BN(1), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 3, + cancelMode: 0, + tokenProgram: TOKEN_2022_PROGRAM_ID, + }); + console.log("Update recipient"); + await updateRecipient({ + escrow, + newRecipient: RecipientKP.publicKey, + isAssertion: true, + signer: UserKP, + newRecipientEmail: null, + }); + + await updateRecipient({ + escrow, + newRecipient: RecipientKP.publicKey, + isAssertion: true, + signer: RecipientKP, + newRecipientEmail: null, + }); + }); + + it("Update both recipient and recipient email", async () => { + console.log("Create vesting plan"); + const program = createLockerProgram(new anchor.Wallet(UserKP)); + let currentBlockTime = await getCurrentBlockTime( + program.provider.connection + ); + + const cliffTime = new BN(currentBlockTime).add(new BN(5)); + let escrow = await createVestingPlanV2({ + ownerKeypair: UserKP, + vestingStartTime: new BN(0), + tokenMint: TOKEN, + isAssertion: true, + cliffTime, + frequency: new BN(1), + cliffUnlockAmount: new BN(100_000), + amountPerPeriod: new BN(50_000), + numberOfPeriod: new BN(2), + recipient: RecipientKP.publicKey, + updateRecipientMode: 3, + cancelMode: 0, + tokenProgram: TOKEN_2022_PROGRAM_ID, + }); + + console.log("Create escrow metadata"); + await createEscrowMetadata({ + escrow, + name: "Jupiter lock", + description: "This is jupiter lock", + creatorEmail: "andrew@raccoons.dev", + recipientEmail: "max@raccoons.dev", + creator: UserKP, + isAssertion: true, + }); + + console.log("Update recipient with bigger email size"); + await updateRecipient({ + escrow, + newRecipient: RecipientKP.publicKey, + isAssertion: true, + signer: UserKP, + newRecipientEmail: "maximillian@raccoons.dev", + }); + + console.log("Update recipient with smaller email size"); + await updateRecipient({ + escrow, + newRecipient: RecipientKP.publicKey, + isAssertion: true, + signer: UserKP, + newRecipientEmail: "max@raccoons.dev", + }); + }); +}); diff --git a/tsconfig.json b/tsconfig.json index 558b83e..a442941 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,11 +1,19 @@ { - "compilerOptions": { - "types": ["mocha", "chai"], - "typeRoots": ["./node_modules/@types"], - "lib": ["es2015"], - "module": "commonjs", - "target": "es6", - "esModuleInterop": true - } - } + "compilerOptions": { + "types": [ + "mocha", + "chai" + ], + "typeRoots": [ + "./node_modules/@types" + ], + "lib": [ + "es2015" + ], + "module": "commonjs", + "target": "es6", + "esModuleInterop": true, + "resolveJsonModule": true + } +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index b451168..5200941 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9,14 +9,20 @@ dependencies: regenerator-runtime "^0.14.0" -"@coral-xyz/anchor@^0.28.0": - version "0.28.0" - resolved "https://registry.npmjs.org/@coral-xyz/anchor/-/anchor-0.28.0.tgz" - integrity sha512-kQ02Hv2ZqxtWP30WN1d4xxT4QqlOXYDxmEd3k/bbneqhV3X5QMO4LAtoUFs7otxyivOgoqam5Il5qx81FuI4vw== - dependencies: - "@coral-xyz/borsh" "^0.28.0" +"@coral-xyz/anchor-errors@^0.30.1": + version "0.30.1" + resolved "https://registry.yarnpkg.com/@coral-xyz/anchor-errors/-/anchor-errors-0.30.1.tgz#bdfd3a353131345244546876eb4afc0e125bec30" + integrity sha512-9Mkradf5yS5xiLWrl9WrpjqOrAV+/W2RQHDlbnAZBivoGpOs1ECjoDCkVk4aRG8ZdiFiB8zQEVlxf+8fKkmSfQ== + +"@coral-xyz/anchor@^0.30.1": + version "0.30.1" + resolved "https://registry.yarnpkg.com/@coral-xyz/anchor/-/anchor-0.30.1.tgz#17f3e9134c28cd0ea83574c6bab4e410bcecec5d" + integrity sha512-gDXFoF5oHgpriXAaLpxyWBHdCs8Awgf/gLHIo6crv7Aqm937CNdY+x+6hoj7QR5vaJV7MxWSQ0NGFzL3kPbWEQ== + dependencies: + "@coral-xyz/anchor-errors" "^0.30.1" + "@coral-xyz/borsh" "^0.30.1" + "@noble/hashes" "^1.3.1" "@solana/web3.js" "^1.68.0" - base64-js "^1.5.1" bn.js "^5.1.2" bs58 "^4.0.1" buffer-layout "^1.2.2" @@ -24,16 +30,15 @@ cross-fetch "^3.1.5" crypto-hash "^1.3.0" eventemitter3 "^4.0.7" - js-sha256 "^0.9.0" pako "^2.0.3" snake-case "^3.0.4" superstruct "^0.15.4" toml "^3.0.0" -"@coral-xyz/borsh@^0.28.0": - version "0.28.0" - resolved "https://registry.npmjs.org/@coral-xyz/borsh/-/borsh-0.28.0.tgz" - integrity sha512-/u1VTzw7XooK7rqeD7JLUSwOyRSesPUk0U37BV9zK0axJc1q0nRbKFGFLYCQ16OtdOJTTwGfGp11Lx9B45bRCQ== +"@coral-xyz/borsh@^0.30.1": + version "0.30.1" + resolved "https://registry.yarnpkg.com/@coral-xyz/borsh/-/borsh-0.30.1.tgz#869d8833abe65685c72e9199b8688477a4f6b0e3" + integrity sha512-aaxswpPrCFKl8vZTbxLssA2RvwX2zmKLlRCIktJOwW+VpVwYtXRtlWiIP+c2pPRKneiTiWCN2GEMSH9j1zTlWQ== dependencies: bn.js "^5.1.2" buffer-layout "^1.2.0" @@ -45,7 +50,7 @@ dependencies: "@noble/hashes" "1.4.0" -"@noble/hashes@^1.4.0", "@noble/hashes@1.4.0": +"@noble/hashes@1.4.0", "@noble/hashes@^1.3.1", "@noble/hashes@^1.4.0": version "1.4.0" resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz" integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg== @@ -74,6 +79,13 @@ dependencies: "@solana/errors" "2.0.0-preview.2" +"@solana/codecs-core@2.0.0-preview.4": + version "2.0.0-preview.4" + resolved "https://registry.yarnpkg.com/@solana/codecs-core/-/codecs-core-2.0.0-preview.4.tgz#770826105f2f884110a21662573e7a2014654324" + integrity sha512-A0VVuDDA5kNKZUinOqHxJQK32aKTucaVbvn31YenGzHX1gPqq+SOnFwgaEY6pq4XEopSmaK16w938ZQS8IvCnw== + dependencies: + "@solana/errors" "2.0.0-preview.4" + "@solana/codecs-data-structures@2.0.0-preview.2": version "2.0.0-preview.2" resolved "https://registry.npmjs.org/@solana/codecs-data-structures/-/codecs-data-structures-2.0.0-preview.2.tgz" @@ -83,6 +95,15 @@ "@solana/codecs-numbers" "2.0.0-preview.2" "@solana/errors" "2.0.0-preview.2" +"@solana/codecs-data-structures@2.0.0-preview.4": + version "2.0.0-preview.4" + resolved "https://registry.yarnpkg.com/@solana/codecs-data-structures/-/codecs-data-structures-2.0.0-preview.4.tgz#f8a2470982a9792334737ea64000ccbdff287247" + integrity sha512-nt2k2eTeyzlI/ccutPcG36M/J8NAYfxBPI9h/nQjgJ+M+IgOKi31JV8StDDlG/1XvY0zyqugV3I0r3KAbZRJpA== + dependencies: + "@solana/codecs-core" "2.0.0-preview.4" + "@solana/codecs-numbers" "2.0.0-preview.4" + "@solana/errors" "2.0.0-preview.4" + "@solana/codecs-numbers@2.0.0-preview.2": version "2.0.0-preview.2" resolved "https://registry.npmjs.org/@solana/codecs-numbers/-/codecs-numbers-2.0.0-preview.2.tgz" @@ -91,6 +112,14 @@ "@solana/codecs-core" "2.0.0-preview.2" "@solana/errors" "2.0.0-preview.2" +"@solana/codecs-numbers@2.0.0-preview.4": + version "2.0.0-preview.4" + resolved "https://registry.yarnpkg.com/@solana/codecs-numbers/-/codecs-numbers-2.0.0-preview.4.tgz#6a53b456bb7866f252d8c032c81a92651e150f66" + integrity sha512-Q061rLtMadsO7uxpguT+Z7G4UHnjQ6moVIxAQxR58nLxDPCC7MB1Pk106/Z7NDhDLHTcd18uO6DZ7ajHZEn2XQ== + dependencies: + "@solana/codecs-core" "2.0.0-preview.4" + "@solana/errors" "2.0.0-preview.4" + "@solana/codecs-strings@2.0.0-preview.2": version "2.0.0-preview.2" resolved "https://registry.npmjs.org/@solana/codecs-strings/-/codecs-strings-2.0.0-preview.2.tgz" @@ -100,6 +129,15 @@ "@solana/codecs-numbers" "2.0.0-preview.2" "@solana/errors" "2.0.0-preview.2" +"@solana/codecs-strings@2.0.0-preview.4": + version "2.0.0-preview.4" + resolved "https://registry.yarnpkg.com/@solana/codecs-strings/-/codecs-strings-2.0.0-preview.4.tgz#4d06bb722a55a5d04598d362021bfab4bd446760" + integrity sha512-YDbsQePRWm+xnrfS64losSGRg8Wb76cjK1K6qfR8LPmdwIC3787x9uW5/E4icl/k+9nwgbIRXZ65lpF+ucZUnw== + dependencies: + "@solana/codecs-core" "2.0.0-preview.4" + "@solana/codecs-numbers" "2.0.0-preview.4" + "@solana/errors" "2.0.0-preview.4" + "@solana/codecs@2.0.0-preview.2": version "2.0.0-preview.2" resolved "https://registry.npmjs.org/@solana/codecs/-/codecs-2.0.0-preview.2.tgz" @@ -111,6 +149,17 @@ "@solana/codecs-strings" "2.0.0-preview.2" "@solana/options" "2.0.0-preview.2" +"@solana/codecs@2.0.0-preview.4": + version "2.0.0-preview.4" + resolved "https://registry.yarnpkg.com/@solana/codecs/-/codecs-2.0.0-preview.4.tgz#a1923cc78a6f64ebe656c7ec6335eb6b70405b22" + integrity sha512-gLMupqI4i+G4uPi2SGF/Tc1aXcviZF2ybC81x7Q/fARamNSgNOCUUoSCg9nWu1Gid6+UhA7LH80sWI8XjKaRog== + dependencies: + "@solana/codecs-core" "2.0.0-preview.4" + "@solana/codecs-data-structures" "2.0.0-preview.4" + "@solana/codecs-numbers" "2.0.0-preview.4" + "@solana/codecs-strings" "2.0.0-preview.4" + "@solana/options" "2.0.0-preview.4" + "@solana/errors@2.0.0-preview.2": version "2.0.0-preview.2" resolved "https://registry.npmjs.org/@solana/errors/-/errors-2.0.0-preview.2.tgz" @@ -119,6 +168,14 @@ chalk "^5.3.0" commander "^12.0.0" +"@solana/errors@2.0.0-preview.4": + version "2.0.0-preview.4" + resolved "https://registry.yarnpkg.com/@solana/errors/-/errors-2.0.0-preview.4.tgz#056ba76b6dd900dafa70117311bec3aef0f5250b" + integrity sha512-kadtlbRv2LCWr8A9V22On15Us7Nn8BvqNaOB4hXsTB3O0fU40D1ru2l+cReqLcRPij4znqlRzW9Xi0m6J5DIhA== + dependencies: + chalk "^5.3.0" + commander "^12.1.0" + "@solana/options@2.0.0-preview.2": version "2.0.0-preview.2" resolved "https://registry.npmjs.org/@solana/options/-/options-2.0.0-preview.2.tgz" @@ -127,22 +184,42 @@ "@solana/codecs-core" "2.0.0-preview.2" "@solana/codecs-numbers" "2.0.0-preview.2" -"@solana/spl-token-metadata@^0.1.2": +"@solana/options@2.0.0-preview.4": + version "2.0.0-preview.4" + resolved "https://registry.yarnpkg.com/@solana/options/-/options-2.0.0-preview.4.tgz#212d35d1da87c7efb13de4d3569ad9eb070f013d" + integrity sha512-tv2O/Frxql/wSe3jbzi5nVicIWIus/BftH+5ZR+r9r3FO0/htEllZS5Q9XdbmSboHu+St87584JXeDx3xm4jaA== + dependencies: + "@solana/codecs-core" "2.0.0-preview.4" + "@solana/codecs-data-structures" "2.0.0-preview.4" + "@solana/codecs-numbers" "2.0.0-preview.4" + "@solana/codecs-strings" "2.0.0-preview.4" + "@solana/errors" "2.0.0-preview.4" + +"@solana/spl-token-group@^0.0.5": + version "0.0.5" + resolved "https://registry.yarnpkg.com/@solana/spl-token-group/-/spl-token-group-0.0.5.tgz#f955dcca782031c85e862b2b46878d1bb02db6c2" + integrity sha512-CLJnWEcdoUBpQJfx9WEbX3h6nTdNiUzswfFdkABUik7HVwSNA98u5AYvBVK2H93d9PGMOHAak2lHW9xr+zAJGQ== + dependencies: + "@solana/codecs" "2.0.0-preview.4" + "@solana/spl-type-length-value" "0.1.0" + +"@solana/spl-token-metadata@^0.1.3": version "0.1.4" - resolved "https://registry.npmjs.org/@solana/spl-token-metadata/-/spl-token-metadata-0.1.4.tgz" + resolved "https://registry.yarnpkg.com/@solana/spl-token-metadata/-/spl-token-metadata-0.1.4.tgz#5cdc3b857a8c4a6877df24e24a8648c4132d22ba" integrity sha512-N3gZ8DlW6NWDV28+vCCDJoTqaCZiF/jDUnk3o8GRkAFzHObiR60Bs1gXHBa8zCPdvOwiG6Z3dg5pg7+RW6XNsQ== dependencies: "@solana/codecs" "2.0.0-preview.2" "@solana/spl-type-length-value" "0.1.0" -"@solana/spl-token@^0.3.8": - version "0.3.11" - resolved "https://registry.npmjs.org/@solana/spl-token/-/spl-token-0.3.11.tgz" - integrity sha512-bvohO3rIMSVL24Pb+I4EYTJ6cL82eFpInEXD/I8K8upOGjpqHsKUoAempR/RnUlI1qSFNyFlWJfu6MNUgfbCQQ== +"@solana/spl-token@^0.4.8": + version "0.4.8" + resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.4.8.tgz#a84e4131af957fa9fbd2727e5fc45dfbf9083586" + integrity sha512-RO0JD9vPRi4LsAbMUdNbDJ5/cv2z11MGhtAvFeRzT4+hAGE/FUzRi0tkkWtuCfSIU3twC6CtmAihRp/+XXjWsA== dependencies: "@solana/buffer-layout" "^4.0.0" "@solana/buffer-layout-utils" "^0.2.0" - "@solana/spl-token-metadata" "^0.1.2" + "@solana/spl-token-group" "^0.0.5" + "@solana/spl-token-metadata" "^0.1.3" buffer "^6.0.3" "@solana/spl-type-length-value@0.1.0": @@ -152,7 +229,7 @@ dependencies: buffer "^6.0.3" -"@solana/web3.js@^1.32.0", "@solana/web3.js@^1.68.0", "@solana/web3.js@^1.88.0", "@solana/web3.js@^1.91.6": +"@solana/web3.js@^1.32.0", "@solana/web3.js@^1.68.0": version "1.95.1" resolved "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.95.1.tgz" integrity sha512-mRX/AjV6QbiOXpWcy5Rz1ZWEH2lVkwO7T0pcv9t97ACpv3/i3tPiqXwk0JIZgSR3wOSTiT26JfygnJH2ulS6dQ== @@ -245,6 +322,14 @@ resolved "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz" integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== +JSONStream@^1.3.5: + version "1.3.5" + resolved "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz" + integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== + dependencies: + jsonparse "^1.2.0" + through ">=2.2.7 <3" + agentkeepalive@^4.5.0: version "4.5.0" resolved "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz" @@ -304,7 +389,7 @@ base-x@^3.0.2: dependencies: safe-buffer "^5.0.1" -base64-js@^1.3.1, base64-js@^1.5.1: +base64-js@^1.3.1: version "1.5.1" resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -384,7 +469,7 @@ buffer-layout@^1.2.0, buffer-layout@^1.2.2: resolved "https://registry.npmjs.org/buffer-layout/-/buffer-layout-1.2.2.tgz" integrity sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA== -buffer@^6.0.3, buffer@~6.0.3, buffer@6.0.3: +buffer@6.0.3, buffer@^6.0.3, buffer@~6.0.3: version "6.0.3" resolved "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz" integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== @@ -473,7 +558,7 @@ color-name@~1.1.4: resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -commander@^12.0.0: +commander@^12.0.0, commander@^12.1.0: version "12.1.0" resolved "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz" integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA== @@ -524,16 +609,16 @@ delay@^5.0.0: resolved "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz" integrity sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw== -diff@^3.1.0: - version "3.5.0" - resolved "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz" - integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== - diff@5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz" integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== +diff@^3.1.0: + version "3.5.0" + resolved "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz" + integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== + dot-case@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz" @@ -589,11 +674,6 @@ fast-stable-stringify@^1.0.0: resolved "https://registry.npmjs.org/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz" integrity sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag== -fastestsmallesttextencoderdecoder@^1.0.22: - version "1.0.22" - resolved "https://registry.npmjs.org/fastestsmallesttextencoderdecoder/-/fastestsmallesttextencoderdecoder-1.0.22.tgz" - integrity sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw== - file-uri-to-path@1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz" @@ -755,21 +835,16 @@ jayson@^4.1.1: "@types/connect" "^3.4.33" "@types/node" "^12.12.54" "@types/ws" "^7.4.4" + JSONStream "^1.3.5" commander "^2.20.3" delay "^5.0.0" es6-promisify "^5.0.0" eyes "^0.1.8" isomorphic-ws "^4.0.1" json-stringify-safe "^5.0.1" - JSONStream "^1.3.5" uuid "^8.3.2" ws "^7.5.10" -js-sha256@^0.9.0: - version "0.9.0" - resolved "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz" - integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA== - js-yaml@4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" @@ -794,14 +869,6 @@ jsonparse@^1.2.0: resolved "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz" integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== -JSONStream@^1.3.5: - version "1.3.5" - resolved "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz" - integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== - dependencies: - jsonparse "^1.2.0" - through ">=2.2.7 <3" - locate-path@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" @@ -836,13 +903,6 @@ make-error@^1.1.1: resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== -minimatch@^3.0.4: - version "3.1.2" - resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== - dependencies: - brace-expansion "^1.1.7" - minimatch@4.2.1: version "4.2.1" resolved "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz" @@ -850,6 +910,13 @@ minimatch@4.2.1: dependencies: brace-expansion "^1.1.7" +minimatch@^3.0.4: + version "3.1.2" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + minimist@^1.2.0, minimist@^1.2.6: version "1.2.8" resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" @@ -862,7 +929,7 @@ mkdirp@^0.5.1: dependencies: minimist "^1.2.6" -"mocha@^3.X.X || ^4.X.X || ^5.X.X || ^6.X.X || ^7.X.X || ^8.X.X || ^9.X.X || ^10.X.X", mocha@^9.0.3: +mocha@^9.0.3: version "9.2.2" resolved "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz" integrity sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g== @@ -892,16 +959,16 @@ mkdirp@^0.5.1: yargs-parser "20.2.4" yargs-unparser "2.0.0" -ms@^2.0.0, ms@2.1.3: - version "2.1.3" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - ms@2.1.2: version "2.1.2" resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +ms@2.1.3, ms@^2.0.0: + version "2.1.3" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + nanoid@3.3.1: version "3.3.1" resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz" @@ -1092,13 +1159,6 @@ superstruct@^2.0.2: resolved "https://registry.npmjs.org/superstruct/-/superstruct-2.0.2.tgz" integrity sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A== -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - supports-color@8.1.1: version "8.1.1" resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" @@ -1106,6 +1166,13 @@ supports-color@8.1.1: dependencies: has-flag "^4.0.0" +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + text-encoding-utf-8@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz" @@ -1116,6 +1183,11 @@ text-encoding-utf-8@^1.0.2: resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== +tiny-invariant@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz#46680b7a873a0d5d10005995eb90a70d74d60127" + integrity sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg== + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" @@ -1186,7 +1258,7 @@ undici-types@~5.26.4: resolved "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== -utf-8-validate@^5.0.2, utf-8-validate@>=5.0.2: +utf-8-validate@^5.0.2: version "5.0.10" resolved "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz" integrity sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ== @@ -1237,7 +1309,7 @@ wrappy@1: resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== -ws@*, ws@^7.5.10: +ws@^7.5.10: version "7.5.10" resolved "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz" integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== @@ -1252,16 +1324,16 @@ y18n@^5.0.5: resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== -yargs-parser@^20.2.2: - version "20.2.9" - resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz" - integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== - yargs-parser@20.2.4: version "20.2.4" resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz" integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== +yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + yargs-unparser@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz"