diff --git a/tokens/escrow/steel/.gitignore b/tokens/escrow/steel/.gitignore new file mode 100644 index 000000000..1fae5e12b --- /dev/null +++ b/tokens/escrow/steel/.gitignore @@ -0,0 +1,3 @@ +target +test-ledger +node_modules diff --git a/tokens/escrow/steel/Cargo.toml b/tokens/escrow/steel/Cargo.toml new file mode 100644 index 000000000..fe33541ea --- /dev/null +++ b/tokens/escrow/steel/Cargo.toml @@ -0,0 +1,25 @@ +[workspace] +resolver = "2" +members = ["api", "program"] + +[workspace.package] +version = "0.1.0" +edition = "2021" +license = "Apache-2.0" +homepage = "" +documentation = "" +respository = "https://www.github.com/Ovodo" +readme = "./README.md" +keywords = ["solana","steel","escrow"] + +[workspace.dependencies] +escrow-api = { path = "./api", version = "0.1.0" } +bytemuck = "1.14" +num_enum = "0.7" +solana-program = "1.18" +steel = { version = "2.1.1", features = ["spl"] } +thiserror = "1.0" +spl-token = { features = ["no-entrypoint"], version = "^4" } +spl-associated-token-account = { features = [ "no-entrypoint" ], version = "^2.3" } + + diff --git a/tokens/escrow/steel/README.md b/tokens/escrow/steel/README.md new file mode 100644 index 000000000..f5b651b49 --- /dev/null +++ b/tokens/escrow/steel/README.md @@ -0,0 +1,51 @@ +# Escrow + +**Escrow** is a an example of an escrow holding tokens on behalf of a user. + +## API + +- [`Consts`](api/src/consts.rs) – Program constants. +- [`Error`](api/src/error.rs) – Custom program errors. +- [`Instruction`](api/src/instruction.rs) – Declared instructions. + +## Instructions + +- [`MakeOffer`](program/src/make_offer.rs) Makes an offer ... +- [`TakeOfferr`](program/src/take_offer.rs) Takes an offer ... +- [`Refund`](program/src/refund.rs) Refunds an offer to the ochestrator ... + +## State + +- [`Offer`](api/src/state/offer.rs) – Offer ... + +## Get started + +Compile your program: + +```sh +steel build +``` + +Install dependencies: + +```sh +pnpm install +``` + +Run unit and integration tests (native): + +```sh +steel test +``` + +Run unit and integration tests (bankrun): + +```sh +pnpm build-and-test +``` + +Run unit and integration tests without logs for a cleaner output (bankrun): + +```sh +pnpm test-no-log +``` diff --git a/tokens/escrow/steel/api/Cargo.toml b/tokens/escrow/steel/api/Cargo.toml new file mode 100644 index 000000000..1191b4a21 --- /dev/null +++ b/tokens/escrow/steel/api/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "escrow-api" +version = "0.1.0" +edition = "2021" + +[dependencies] +bytemuck.workspace = true +num_enum.workspace = true +spl-associated-token-account.workspace = true +solana-program.workspace = true +steel.workspace = true +spl-token.workspace = true +thiserror.workspace = true diff --git a/tokens/escrow/steel/api/src/consts.rs b/tokens/escrow/steel/api/src/consts.rs new file mode 100644 index 000000000..d91efc1f5 --- /dev/null +++ b/tokens/escrow/steel/api/src/consts.rs @@ -0,0 +1,2 @@ +/// Seed of the offer account PDA. +pub const OFFER: &[u8] = b"offer"; diff --git a/tokens/escrow/steel/api/src/error.rs b/tokens/escrow/steel/api/src/error.rs new file mode 100644 index 000000000..b723fa153 --- /dev/null +++ b/tokens/escrow/steel/api/src/error.rs @@ -0,0 +1,10 @@ +use steel::*; + +#[derive(Debug, Error, Clone, Copy, PartialEq, Eq, IntoPrimitive)] +#[repr(u32)] +pub enum AccountError { + #[error("This is a dummy error")] + Dummy = 0, +} + +error!(AccountError); diff --git a/tokens/escrow/steel/api/src/instruction.rs b/tokens/escrow/steel/api/src/instruction.rs new file mode 100644 index 000000000..d29ddf7bd --- /dev/null +++ b/tokens/escrow/steel/api/src/instruction.rs @@ -0,0 +1,30 @@ +use steel::*; + +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, TryFromPrimitive)] +pub enum AccountInstruction { + MakeOffer = 0, + TakeOffer = 1, + Refund = 2, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Pod, Zeroable)] +pub struct MakeOffer { + pub id: [u8; 8], + pub token_a_offered_amount: [u8; 8], + pub token_b_wanted_amount: [u8; 8], + pub bump: [u8; 1], +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Pod, Zeroable)] +pub struct TakeOffer {} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Pod, Zeroable)] +pub struct Refund {} + +instruction!(AccountInstruction, MakeOffer); +instruction!(AccountInstruction, TakeOffer); +instruction!(AccountInstruction, Refund); diff --git a/tokens/escrow/steel/api/src/lib.rs b/tokens/escrow/steel/api/src/lib.rs new file mode 100644 index 000000000..710d1da15 --- /dev/null +++ b/tokens/escrow/steel/api/src/lib.rs @@ -0,0 +1,18 @@ +pub mod consts; +pub mod error; +pub mod instruction; +pub mod sdk; +pub mod state; + +pub mod prelude { + pub use crate::consts::*; + pub use crate::error::*; + pub use crate::instruction::*; + pub use crate::sdk::*; + pub use crate::state::*; +} + +use steel::*; + +// TODO Set program id +declare_id!("z7msBPQHDJjTvdQRoEcKyENgXDhSRYeHieN1ZMTqo35"); diff --git a/tokens/escrow/steel/api/src/sdk.rs b/tokens/escrow/steel/api/src/sdk.rs new file mode 100644 index 000000000..792f0b0d0 --- /dev/null +++ b/tokens/escrow/steel/api/src/sdk.rs @@ -0,0 +1,91 @@ +use steel::*; + +use crate::prelude::*; + +pub fn make_offer( + signer: Pubkey, + token_mint_a: Pubkey, + token_mint_b: Pubkey, + maker_token_account_a: Pubkey, + vault: Pubkey, + token_a_offered_amount: u64, + token_b_wanted_amount: u64, + id: u64, + bump: u8, +) -> Instruction { + Instruction { + program_id: crate::ID, + accounts: vec![ + AccountMeta::new(signer, true), + AccountMeta::new(token_mint_a, false), + AccountMeta::new(token_mint_b, false), + AccountMeta::new(maker_token_account_a, false), + AccountMeta::new(offer_pda(signer, id).0, false), + AccountMeta::new(vault, false), + AccountMeta::new_readonly(spl_token::ID, false), + AccountMeta::new_readonly(spl_associated_token_account::ID, false), + AccountMeta::new_readonly(system_program::ID, false), + ], + data: MakeOffer { + token_a_offered_amount: token_a_offered_amount.to_le_bytes(), + token_b_wanted_amount: token_b_wanted_amount.to_le_bytes(), + id: id.to_le_bytes(), + bump: bump.to_le_bytes(), + } + .to_bytes(), + } +} + +pub fn take_offer( + signer: Pubkey, + maker: Pubkey, + token_mint_a: Pubkey, + token_mint_b: Pubkey, + taker_token_account_a: Pubkey, + taker_token_account_b: Pubkey, + maker_token_account_b: Pubkey, + vault: Pubkey, + id: u64, +) -> Instruction { + Instruction { + program_id: crate::ID, + accounts: vec![ + AccountMeta::new(signer, true), + AccountMeta::new(maker, false), + AccountMeta::new(token_mint_a, false), + AccountMeta::new(token_mint_b, false), + AccountMeta::new(taker_token_account_a, false), + AccountMeta::new(taker_token_account_b, false), + AccountMeta::new(maker_token_account_b, false), + AccountMeta::new(offer_pda(maker, id).0, false), + AccountMeta::new(vault, false), + AccountMeta::new_readonly(spl_token::ID, false), + AccountMeta::new_readonly(spl_associated_token_account::ID, false), + AccountMeta::new_readonly(system_program::ID, false), + ], + data: TakeOffer {}.to_bytes(), + } +} + +pub fn refund( + maker: Pubkey, + token_mint_a: Pubkey, + maker_token_account_a: Pubkey, + vault: Pubkey, + id: u64, +) -> Instruction { + Instruction { + program_id: crate::ID, + accounts: vec![ + AccountMeta::new(maker, true), + AccountMeta::new(token_mint_a, false), + AccountMeta::new(maker_token_account_a, false), + AccountMeta::new(offer_pda(maker, id).0, false), + AccountMeta::new(vault, false), + AccountMeta::new_readonly(spl_token::ID, false), + AccountMeta::new_readonly(spl_associated_token_account::ID, false), + AccountMeta::new_readonly(system_program::ID, false), + ], + data: Refund {}.to_bytes(), + } +} diff --git a/tokens/escrow/steel/api/src/state/mod.rs b/tokens/escrow/steel/api/src/state/mod.rs new file mode 100644 index 000000000..7a2eebbab --- /dev/null +++ b/tokens/escrow/steel/api/src/state/mod.rs @@ -0,0 +1,19 @@ +mod offer; + +use crate::consts::*; +pub use offer::*; +use steel::*; + +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)] +pub enum SteelAccount { + Offer = 0, +} + +/// Fetch PDA of the offer account. +pub fn offer_pda(maker: Pubkey, id: u64) -> (Pubkey, u8) { + Pubkey::find_program_address( + &[OFFER, maker.as_ref(), id.to_le_bytes().as_ref()], + &crate::id(), + ) +} diff --git a/tokens/escrow/steel/api/src/state/offer.rs b/tokens/escrow/steel/api/src/state/offer.rs new file mode 100644 index 000000000..de36182a3 --- /dev/null +++ b/tokens/escrow/steel/api/src/state/offer.rs @@ -0,0 +1,16 @@ +use steel::*; + +use super::SteelAccount; + +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Pod, Zeroable)] +pub struct Offer { + pub id: u64, + pub maker: Pubkey, + pub token_mint_a: Pubkey, + pub token_mint_b: Pubkey, + pub token_b_wanted_amount: u64, + pub bump: u64, +} + +account!(SteelAccount, Offer); diff --git a/tokens/escrow/steel/package.json b/tokens/escrow/steel/package.json new file mode 100644 index 000000000..8a2e82c02 --- /dev/null +++ b/tokens/escrow/steel/package.json @@ -0,0 +1,38 @@ +{ + "name": "escrow", + "version": "1.0.0", + "description": "**Account** is a ...", + "main": "index.js", + "scripts": { + "test-no-log": "RUST_LOG=error pnpm ts-mocha -p ./tsconfig.json -t 1000000 ./tests/*.test.ts", + "test": "pnpm ts-mocha -p ./tsconfig.json -t 1000000 ./tests/*.test.ts", + "build-and-test": "cargo build-sbf --manifest-path=./program/Cargo.toml --sbf-out-dir=./tests/fixtures && pnpm test", + "build": "cargo build-sbf --manifest-path=./program/Cargo.toml --sbf-out-dir=./program/target/so", + "deploy": "solana program deploy ./program/target/so/program.so" + }, + "keywords": [], + "author": "", + "license": "ISC", + "devDependencies": { + "@types/bn.js": "^5.1.6", + "@types/chai": "^5.0.0", + "@types/mocha": "^10.0.9", + "@types/node": "^22.7.9", + "anchor-bankrun": "^0.5.0", + "chai": "^4.5.0", + "mocha": "^10.7.3", + "solana-bankrun": "^0.4.0", + "ts-mocha": "^10.0.0", + "ts-node": "^10.9.2", + "typescript": "^5.6.3" + }, + "dependencies": { + "@coral-xyz/anchor": "^0.30.1", + "@solana-developers/helpers": "^2.5.6", + "@solana/buffer-layout": "^4.0.1", + "@solana/buffer-layout-utils": "^0.2.0", + "@solana/spl-token": "^0.4.9", + "@solana/web3.js": "^1.95.4", + "borsh": "^2.0.0" + } +} diff --git a/tokens/escrow/steel/pnpm-lock.yaml b/tokens/escrow/steel/pnpm-lock.yaml new file mode 100644 index 000000000..6e3b46b40 --- /dev/null +++ b/tokens/escrow/steel/pnpm-lock.yaml @@ -0,0 +1,1762 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@coral-xyz/anchor': + specifier: ^0.30.1 + version: 0.30.1(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@solana-developers/helpers': + specifier: ^2.5.6 + version: 2.5.6(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10) + '@solana/buffer-layout': + specifier: ^4.0.1 + version: 4.0.1 + '@solana/buffer-layout-utils': + specifier: ^0.2.0 + version: 0.2.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@solana/spl-token': + specifier: ^0.4.9 + version: 0.4.9(@solana/web3.js@1.95.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10) + '@solana/web3.js': + specifier: ^1.95.4 + version: 1.95.4(bufferutil@4.0.8)(utf-8-validate@5.0.10) + borsh: + specifier: ^2.0.0 + version: 2.0.0 + devDependencies: + '@types/bn.js': + specifier: ^5.1.6 + version: 5.1.6 + '@types/chai': + specifier: ^5.0.0 + version: 5.0.0 + '@types/mocha': + specifier: ^10.0.9 + version: 10.0.9 + '@types/node': + specifier: ^22.7.9 + version: 22.7.9 + anchor-bankrun: + specifier: ^0.5.0 + version: 0.5.0(@coral-xyz/anchor@0.30.1(bufferutil@4.0.8)(utf-8-validate@5.0.10))(@solana/web3.js@1.95.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solana-bankrun@0.4.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)) + chai: + specifier: ^4.5.0 + version: 4.5.0 + mocha: + specifier: ^10.7.3 + version: 10.7.3 + solana-bankrun: + specifier: ^0.4.0 + version: 0.4.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + ts-mocha: + specifier: ^10.0.0 + version: 10.0.0(mocha@10.7.3) + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@types/node@22.7.9)(typescript@5.6.3) + typescript: + specifier: ^5.6.3 + version: 5.6.3 + +packages: + + '@babel/runtime@7.25.9': + resolution: {integrity: sha512-4zpTHZ9Cm6L9L+uIqghQX8ZXg8HKFcjYO3qHoO8zTmRm6HQUJ8SSJ+KRvbMBZn0EGVlT4DRYeQ/6hjlyXBh+Kg==} + engines: {node: '>=6.9.0'} + + '@coral-xyz/anchor-errors@0.30.1': + resolution: {integrity: sha512-9Mkradf5yS5xiLWrl9WrpjqOrAV+/W2RQHDlbnAZBivoGpOs1ECjoDCkVk4aRG8ZdiFiB8zQEVlxf+8fKkmSfQ==} + engines: {node: '>=10'} + + '@coral-xyz/anchor@0.30.1': + resolution: {integrity: sha512-gDXFoF5oHgpriXAaLpxyWBHdCs8Awgf/gLHIo6crv7Aqm937CNdY+x+6hoj7QR5vaJV7MxWSQ0NGFzL3kPbWEQ==} + engines: {node: '>=11'} + + '@coral-xyz/borsh@0.30.1': + resolution: {integrity: sha512-aaxswpPrCFKl8vZTbxLssA2RvwX2zmKLlRCIktJOwW+VpVwYtXRtlWiIP+c2pPRKneiTiWCN2GEMSH9j1zTlWQ==} + engines: {node: '>=10'} + peerDependencies: + '@solana/web3.js': ^1.68.0 + + '@cspotcode/source-map-support@0.8.1': + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/trace-mapping@0.3.9': + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + + '@noble/curves@1.6.0': + resolution: {integrity: sha512-TlaHRXDehJuRNR9TfZDNQ45mMEd5dwUwmicsafcIX4SsNiqnCHKjE/1alYPd/lDRVhxdhUAlv8uEhMCI5zjIJQ==} + engines: {node: ^14.21.3 || >=16} + + '@noble/hashes@1.5.0': + resolution: {integrity: sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==} + engines: {node: ^14.21.3 || >=16} + + '@solana-developers/helpers@2.5.6': + resolution: {integrity: sha512-NPWZblVMl4LuVVSJOZG0ZF0VYnrMUjCyMNTiGwNUXPK2WWYJCqpuDyzs/PMqwvM4gMTjk4pEToBX8N2UxDvZkQ==} + + '@solana/buffer-layout-utils@0.2.0': + resolution: {integrity: sha512-szG4sxgJGktbuZYDg2FfNmkMi0DYQoVjN2h7ta1W1hPrwzarcFLBq9UpX1UjNXsNpT9dn+chgprtWGioUAr4/g==} + engines: {node: '>= 10'} + + '@solana/buffer-layout@4.0.1': + resolution: {integrity: sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==} + engines: {node: '>=5.10'} + + '@solana/codecs-core@2.0.0-rc.1': + resolution: {integrity: sha512-bauxqMfSs8EHD0JKESaNmNuNvkvHSuN3bbWAF5RjOfDu2PugxHrvRebmYauvSumZ3cTfQ4HJJX6PG5rN852qyQ==} + peerDependencies: + typescript: '>=5' + + '@solana/codecs-data-structures@2.0.0-rc.1': + resolution: {integrity: sha512-rinCv0RrAVJ9rE/rmaibWJQxMwC5lSaORSZuwjopSUE6T0nb/MVg6Z1siNCXhh/HFTOg0l8bNvZHgBcN/yvXog==} + peerDependencies: + typescript: '>=5' + + '@solana/codecs-numbers@2.0.0-rc.1': + resolution: {integrity: sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ==} + peerDependencies: + typescript: '>=5' + + '@solana/codecs-strings@2.0.0-rc.1': + resolution: {integrity: sha512-9/wPhw8TbGRTt6mHC4Zz1RqOnuPTqq1Nb4EyuvpZ39GW6O2t2Q7Q0XxiB3+BdoEjwA2XgPw6e2iRfvYgqty44g==} + peerDependencies: + fastestsmallesttextencoderdecoder: ^1.0.22 + typescript: '>=5' + + '@solana/codecs@2.0.0-rc.1': + resolution: {integrity: sha512-qxoR7VybNJixV51L0G1RD2boZTcxmwUWnKCaJJExQ5qNKwbpSyDdWfFJfM5JhGyKe9DnPVOZB+JHWXnpbZBqrQ==} + peerDependencies: + typescript: '>=5' + + '@solana/errors@2.0.0-rc.1': + resolution: {integrity: sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ==} + hasBin: true + peerDependencies: + typescript: '>=5' + + '@solana/options@2.0.0-rc.1': + resolution: {integrity: sha512-mLUcR9mZ3qfHlmMnREdIFPf9dpMc/Bl66tLSOOWxw4ml5xMT2ohFn7WGqoKcu/UHkT9CrC6+amEdqCNvUqI7AA==} + peerDependencies: + typescript: '>=5' + + '@solana/spl-token-group@0.0.7': + resolution: {integrity: sha512-V1N/iX7Cr7H0uazWUT2uk27TMqlqedpXHRqqAbVO2gvmJyT0E0ummMEAVQeXZ05ZhQ/xF39DLSdBp90XebWEug==} + engines: {node: '>=16'} + peerDependencies: + '@solana/web3.js': ^1.95.3 + + '@solana/spl-token-metadata@0.1.6': + resolution: {integrity: sha512-7sMt1rsm/zQOQcUWllQX9mD2O6KhSAtY1hFR2hfFwgqfFWzSY9E9GDvFVNYUI1F0iQKcm6HmePU9QbKRXTEBiA==} + engines: {node: '>=16'} + peerDependencies: + '@solana/web3.js': ^1.95.3 + + '@solana/spl-token@0.4.9': + resolution: {integrity: sha512-g3wbj4F4gq82YQlwqhPB0gHFXfgsC6UmyGMxtSLf/BozT/oKd59465DbnlUK8L8EcimKMavxsVAMoLcEdeCicg==} + engines: {node: '>=16'} + peerDependencies: + '@solana/web3.js': ^1.95.3 + + '@solana/web3.js@1.95.4': + resolution: {integrity: sha512-sdewnNEA42ZSMxqkzdwEWi6fDgzwtJHaQa5ndUGEJYtoOnM6X5cvPmjoTUp7/k7bRrVAxfBgDnvQQHD6yhlLYw==} + + '@swc/helpers@0.5.13': + resolution: {integrity: sha512-UoKGxQ3r5kYI9dALKJapMmuK+1zWM/H17Z1+iwnNmzcJRnfFuevZs375TA5rW31pu4BS4NoSy1fRsexDXfWn5w==} + + '@tsconfig/node10@1.0.11': + resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} + + '@tsconfig/node12@1.0.11': + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + + '@tsconfig/node14@1.0.3': + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + + '@tsconfig/node16@1.0.4': + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + + '@types/bn.js@5.1.6': + resolution: {integrity: sha512-Xh8vSwUeMKeYYrj3cX4lGQgFSF/N03r+tv4AiLl1SucqV+uTQpxRcnM8AkXKHwYP9ZPXOYXRr2KPXpVlIvqh9w==} + + '@types/chai@5.0.0': + resolution: {integrity: sha512-+DwhEHAaFPPdJ2ral3kNHFQXnTfscEEFsUxzD+d7nlcLrFK23JtNjH71RGasTcHb88b4vVi4mTyfpf8u2L8bdA==} + + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + + '@types/mocha@10.0.9': + resolution: {integrity: sha512-sicdRoWtYevwxjOHNMPTl3vSfJM6oyW8o1wXeI7uww6b6xHg8eBznQDNSGBCDJmsE8UMxP05JgZRtsKbTqt//Q==} + + '@types/node@12.20.55': + resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} + + '@types/node@22.7.9': + resolution: {integrity: sha512-jrTfRC7FM6nChvU7X2KqcrgquofrWLFDeYC1hKfwNWomVvrn7JIksqf344WN2X/y8xrgqBd2dJATZV4GbatBfg==} + + '@types/uuid@8.3.4': + resolution: {integrity: sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==} + + '@types/ws@7.4.7': + resolution: {integrity: sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==} + + '@types/ws@8.5.12': + resolution: {integrity: sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==} + + JSONStream@1.3.5: + resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} + hasBin: true + + acorn-walk@8.3.4: + resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} + engines: {node: '>=0.4.0'} + + acorn@8.13.0: + resolution: {integrity: sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w==} + engines: {node: '>=0.4.0'} + hasBin: true + + agentkeepalive@4.5.0: + resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==} + engines: {node: '>= 8.0.0'} + + anchor-bankrun@0.5.0: + resolution: {integrity: sha512-cNTRv7pN9dy+kiyJ3UlNVTg9hAXhY2HtNVNXJbP/2BkS9nOdLV0qKWhgW8UR9Go0gYuEOLKuPzrGL4HFAZPsVw==} + engines: {node: '>= 10'} + peerDependencies: + '@coral-xyz/anchor': ^0.30.0 + '@solana/web3.js': '>1.92.0' + solana-bankrun: '>=0.2.0 <0.5.0' + + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + arrify@1.0.1: + resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} + engines: {node: '>=0.10.0'} + + assertion-error@1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + base-x@3.0.10: + resolution: {integrity: sha512-7d0s06rR9rYaIWHkpfLIFICM/tkSVdoPC9qYAQRpxn9DdKNWNsKC0uk++akckyLq16Tx2WIinnZ6WRriAt6njQ==} + + base-x@5.0.0: + resolution: {integrity: sha512-sMW3VGSX1QWVFA6l8U62MLKz29rRfpTlYdCqLdpLo1/Yd4zZwSbnUaDfciIAowAqvq7YFnWq9hrhdg1KYgc1lQ==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + bigint-buffer@1.1.5: + resolution: {integrity: sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA==} + engines: {node: '>= 10.0.0'} + + bignumber.js@9.1.2: + resolution: {integrity: sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + bindings@1.5.0: + resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + + bn.js@5.2.1: + resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==} + + borsh@0.7.0: + resolution: {integrity: sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==} + + borsh@2.0.0: + resolution: {integrity: sha512-kc9+BgR3zz9+cjbwM8ODoUB4fs3X3I5A/HtX7LZKxCLaMrEeDFoBpnhZY//DTS1VZBSs6S5v46RZRbZjRFspEg==} + + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browser-stdout@1.3.1: + resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} + + bs58@4.0.1: + resolution: {integrity: sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==} + + bs58@6.0.0: + resolution: {integrity: sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw==} + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + buffer-layout@1.2.2: + resolution: {integrity: sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA==} + engines: {node: '>=4.5'} + + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + + bufferutil@4.0.8: + resolution: {integrity: sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==} + engines: {node: '>=6.14.2'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + chai@4.5.0: + resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==} + engines: {node: '>=4'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.3.0: + resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + check-error@1.0.3: + resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + + cross-fetch@3.1.8: + resolution: {integrity: sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==} + + crypto-hash@1.3.0: + resolution: {integrity: sha512-lyAZ0EMyjDkVvz8WOeVnuCPvKVBXcMv1l5SVqO1yC7PzTwrD/pPje/BIRbWhMoPe436U+Y2nD7f5bFx0kt+Sbg==} + engines: {node: '>=8'} + + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decamelize@4.0.0: + resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} + engines: {node: '>=10'} + + deep-eql@4.1.4: + resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==} + engines: {node: '>=6'} + + delay@5.0.0: + resolution: {integrity: sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==} + engines: {node: '>=10'} + + diff@3.5.0: + resolution: {integrity: sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==} + engines: {node: '>=0.3.1'} + + diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + + diff@5.2.0: + resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} + engines: {node: '>=0.3.1'} + + dot-case@3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + + dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + engines: {node: '>=12'} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + es6-promise@4.2.8: + resolution: {integrity: sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==} + + es6-promisify@5.0.0: + resolution: {integrity: sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + eyes@0.1.8: + resolution: {integrity: sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==} + engines: {node: '> 0.1.90'} + + fast-stable-stringify@1.0.0: + resolution: {integrity: sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==} + + fastestsmallesttextencoderdecoder@1.0.22: + resolution: {integrity: sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==} + + file-uri-to-path@1.0.0: + resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat@5.0.2: + resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} + hasBin: true + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-func-name@2.0.2: + resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob@8.1.0: + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + engines: {node: '>=12'} + deprecated: Glob versions prior to v9 are no longer supported + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + + humanize-ms@1.2.1: + resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-plain-obj@2.1.0: + resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} + engines: {node: '>=8'} + + is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + + isomorphic-ws@4.0.1: + resolution: {integrity: sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==} + peerDependencies: + ws: '*' + + jayson@4.1.2: + resolution: {integrity: sha512-5nzMWDHy6f+koZOuYsArh2AXs73NfWYVlFyJJuCedr93GpY+Ku8qq10ropSXVfHK+H0T6paA88ww+/dV+1fBNA==} + engines: {node: '>=8'} + hasBin: true + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + json-stringify-safe@5.0.1: + resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + jsonparse@1.3.1: + resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} + engines: {'0': node >= 0.2.0} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + + loupe@2.3.7: + resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + + lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + + make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + + minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + mkdirp@0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + + mocha@10.7.3: + resolution: {integrity: sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A==} + engines: {node: '>= 14.0.0'} + hasBin: true + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-gyp-build@4.8.2: + resolution: {integrity: sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==} + hasBin: true + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + pako@2.1.0: + resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + pathval@1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + rpc-websockets@9.0.4: + resolution: {integrity: sha512-yWZWN0M+bivtoNLnaDbtny4XchdAIF5Q4g/ZsC5UC61Ckbp0QczwO8fg44rV3uYmY4WHd+EZQbn90W1d8ojzqQ==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + serialize-javascript@6.0.2: + resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + + snake-case@3.0.4: + resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} + + solana-bankrun-darwin-arm64@0.4.0: + resolution: {integrity: sha512-6dz78Teoz7ez/3lpRLDjktYLJb79FcmJk2me4/YaB8WiO6W43OdExU4h+d2FyuAryO2DgBPXaBoBNY/8J1HJmw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + solana-bankrun-darwin-universal@0.4.0: + resolution: {integrity: sha512-zSSw/Jx3KNU42pPMmrEWABd0nOwGJfsj7nm9chVZ3ae7WQg3Uty0hHAkn5NSDCj3OOiN0py9Dr1l9vmRJpOOxg==} + engines: {node: '>= 10'} + os: [darwin] + + solana-bankrun-darwin-x64@0.4.0: + resolution: {integrity: sha512-LWjs5fsgHFtyr7YdJR6r0Ho5zrtzI6CY4wvwPXr8H2m3b4pZe6RLIZjQtabCav4cguc14G0K8yQB2PTMuGub8w==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + solana-bankrun-linux-x64-gnu@0.4.0: + resolution: {integrity: sha512-SrlVrb82UIxt21Zr/XZFHVV/h9zd2/nP25PMpLJVLD7Pgl2yhkhfi82xj3OjxoQqWe+zkBJ+uszA0EEKr67yNw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + solana-bankrun-linux-x64-musl@0.4.0: + resolution: {integrity: sha512-Nv328ZanmURdYfcLL+jwB1oMzX4ZzK57NwIcuJjGlf0XSNLq96EoaO5buEiUTo4Ls7MqqMyLbClHcrPE7/aKyA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + solana-bankrun@0.4.0: + resolution: {integrity: sha512-NMmXUipPBkt8NgnyNO3SCnPERP6xT/AMNMBooljGA3+rG6NN8lmXJsKeLqQTiFsDeWD74U++QM/DgcueSWvrIg==} + engines: {node: '>= 10'} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + superstruct@0.15.5: + resolution: {integrity: sha512-4AOeU+P5UuE/4nOUkmcQdW5y7i9ndt1cQd/3iUe+LTz3RxESf/W/5lg4B74HbDMMv8PHnPnGCQFH45kBcrQYoQ==} + + superstruct@2.0.2: + resolution: {integrity: sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==} + engines: {node: '>=14.0.0'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + text-encoding-utf-8@1.0.2: + resolution: {integrity: sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==} + + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + toml@3.0.0: + resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + ts-mocha@10.0.0: + resolution: {integrity: sha512-VRfgDO+iiuJFlNB18tzOfypJ21xn2xbuZyDvJvqpTbWgkAgD17ONGr8t+Tl8rcBtOBdjXp5e/Rk+d39f7XBHRw==} + engines: {node: '>= 6.X.X'} + hasBin: true + peerDependencies: + 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 + + ts-node@10.9.2: + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + + ts-node@7.0.1: + resolution: {integrity: sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==} + engines: {node: '>=4.2.0'} + hasBin: true + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + + tslib@2.8.0: + resolution: {integrity: sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==} + + type-detect@4.1.0: + resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==} + engines: {node: '>=4'} + + typescript@5.6.3: + resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + + utf-8-validate@5.0.10: + resolution: {integrity: sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==} + engines: {node: '>=6.14.2'} + + uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + + v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + workerpool@6.5.1: + resolution: {integrity: sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + ws@7.5.10: + resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} + engines: {node: '>=8.3.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.18.0: + resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + + yargs-unparser@2.0.0: + resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==} + engines: {node: '>=10'} + + yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + + yn@2.0.0: + resolution: {integrity: sha512-uTv8J/wiWTgUTg+9vLTi//leUl5vDQS6uii/emeTb2ssY7vl6QWf2fFbIIGjnhjvbdKlU0ed7QPgY1htTC86jQ==} + engines: {node: '>=4'} + + yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + +snapshots: + + '@babel/runtime@7.25.9': + dependencies: + regenerator-runtime: 0.14.1 + + '@coral-xyz/anchor-errors@0.30.1': {} + + '@coral-xyz/anchor@0.30.1(bufferutil@4.0.8)(utf-8-validate@5.0.10)': + dependencies: + '@coral-xyz/anchor-errors': 0.30.1 + '@coral-xyz/borsh': 0.30.1(@solana/web3.js@1.95.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)) + '@noble/hashes': 1.5.0 + '@solana/web3.js': 1.95.4(bufferutil@4.0.8)(utf-8-validate@5.0.10) + bn.js: 5.2.1 + bs58: 4.0.1 + buffer-layout: 1.2.2 + camelcase: 6.3.0 + cross-fetch: 3.1.8 + crypto-hash: 1.3.0 + eventemitter3: 4.0.7 + pako: 2.1.0 + snake-case: 3.0.4 + superstruct: 0.15.5 + toml: 3.0.0 + transitivePeerDependencies: + - bufferutil + - encoding + - utf-8-validate + + '@coral-xyz/borsh@0.30.1(@solana/web3.js@1.95.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))': + dependencies: + '@solana/web3.js': 1.95.4(bufferutil@4.0.8)(utf-8-validate@5.0.10) + bn.js: 5.2.1 + buffer-layout: 1.2.2 + + '@cspotcode/source-map-support@0.8.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.9': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@noble/curves@1.6.0': + dependencies: + '@noble/hashes': 1.5.0 + + '@noble/hashes@1.5.0': {} + + '@solana-developers/helpers@2.5.6(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10)': + dependencies: + '@solana/spl-token': 0.4.9(@solana/web3.js@1.95.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10) + '@solana/spl-token-metadata': 0.1.6(@solana/web3.js@1.95.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3) + '@solana/web3.js': 1.95.4(bufferutil@4.0.8)(utf-8-validate@5.0.10) + bs58: 6.0.0 + dotenv: 16.4.5 + transitivePeerDependencies: + - bufferutil + - encoding + - fastestsmallesttextencoderdecoder + - typescript + - utf-8-validate + + '@solana/buffer-layout-utils@0.2.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)': + dependencies: + '@solana/buffer-layout': 4.0.1 + '@solana/web3.js': 1.95.4(bufferutil@4.0.8)(utf-8-validate@5.0.10) + bigint-buffer: 1.1.5 + bignumber.js: 9.1.2 + transitivePeerDependencies: + - bufferutil + - encoding + - utf-8-validate + + '@solana/buffer-layout@4.0.1': + dependencies: + buffer: 6.0.3 + + '@solana/codecs-core@2.0.0-rc.1(typescript@5.6.3)': + dependencies: + '@solana/errors': 2.0.0-rc.1(typescript@5.6.3) + typescript: 5.6.3 + + '@solana/codecs-data-structures@2.0.0-rc.1(typescript@5.6.3)': + dependencies: + '@solana/codecs-core': 2.0.0-rc.1(typescript@5.6.3) + '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.6.3) + '@solana/errors': 2.0.0-rc.1(typescript@5.6.3) + typescript: 5.6.3 + + '@solana/codecs-numbers@2.0.0-rc.1(typescript@5.6.3)': + dependencies: + '@solana/codecs-core': 2.0.0-rc.1(typescript@5.6.3) + '@solana/errors': 2.0.0-rc.1(typescript@5.6.3) + typescript: 5.6.3 + + '@solana/codecs-strings@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)': + dependencies: + '@solana/codecs-core': 2.0.0-rc.1(typescript@5.6.3) + '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.6.3) + '@solana/errors': 2.0.0-rc.1(typescript@5.6.3) + fastestsmallesttextencoderdecoder: 1.0.22 + typescript: 5.6.3 + + '@solana/codecs@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)': + dependencies: + '@solana/codecs-core': 2.0.0-rc.1(typescript@5.6.3) + '@solana/codecs-data-structures': 2.0.0-rc.1(typescript@5.6.3) + '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.6.3) + '@solana/codecs-strings': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3) + '@solana/options': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3) + typescript: 5.6.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/errors@2.0.0-rc.1(typescript@5.6.3)': + dependencies: + chalk: 5.3.0 + commander: 12.1.0 + typescript: 5.6.3 + + '@solana/options@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)': + dependencies: + '@solana/codecs-core': 2.0.0-rc.1(typescript@5.6.3) + '@solana/codecs-data-structures': 2.0.0-rc.1(typescript@5.6.3) + '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.6.3) + '@solana/codecs-strings': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3) + '@solana/errors': 2.0.0-rc.1(typescript@5.6.3) + typescript: 5.6.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/spl-token-group@0.0.7(@solana/web3.js@1.95.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)': + dependencies: + '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3) + '@solana/web3.js': 1.95.4(bufferutil@4.0.8)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + - typescript + + '@solana/spl-token-metadata@0.1.6(@solana/web3.js@1.95.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)': + dependencies: + '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3) + '@solana/web3.js': 1.95.4(bufferutil@4.0.8)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + - typescript + + '@solana/spl-token@0.4.9(@solana/web3.js@1.95.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3)(utf-8-validate@5.0.10)': + dependencies: + '@solana/buffer-layout': 4.0.1 + '@solana/buffer-layout-utils': 0.2.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@solana/spl-token-group': 0.0.7(@solana/web3.js@1.95.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3) + '@solana/spl-token-metadata': 0.1.6(@solana/web3.js@1.95.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.6.3) + '@solana/web3.js': 1.95.4(bufferutil@4.0.8)(utf-8-validate@5.0.10) + buffer: 6.0.3 + transitivePeerDependencies: + - bufferutil + - encoding + - fastestsmallesttextencoderdecoder + - typescript + - utf-8-validate + + '@solana/web3.js@1.95.4(bufferutil@4.0.8)(utf-8-validate@5.0.10)': + dependencies: + '@babel/runtime': 7.25.9 + '@noble/curves': 1.6.0 + '@noble/hashes': 1.5.0 + '@solana/buffer-layout': 4.0.1 + agentkeepalive: 4.5.0 + bigint-buffer: 1.1.5 + bn.js: 5.2.1 + borsh: 0.7.0 + bs58: 4.0.1 + buffer: 6.0.3 + fast-stable-stringify: 1.0.0 + jayson: 4.1.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) + node-fetch: 2.7.0 + rpc-websockets: 9.0.4 + superstruct: 2.0.2 + transitivePeerDependencies: + - bufferutil + - encoding + - utf-8-validate + + '@swc/helpers@0.5.13': + dependencies: + tslib: 2.8.0 + + '@tsconfig/node10@1.0.11': {} + + '@tsconfig/node12@1.0.11': {} + + '@tsconfig/node14@1.0.3': {} + + '@tsconfig/node16@1.0.4': {} + + '@types/bn.js@5.1.6': + dependencies: + '@types/node': 22.7.9 + + '@types/chai@5.0.0': {} + + '@types/connect@3.4.38': + dependencies: + '@types/node': 22.7.9 + + '@types/json5@0.0.29': + optional: true + + '@types/mocha@10.0.9': {} + + '@types/node@12.20.55': {} + + '@types/node@22.7.9': + dependencies: + undici-types: 6.19.8 + + '@types/uuid@8.3.4': {} + + '@types/ws@7.4.7': + dependencies: + '@types/node': 22.7.9 + + '@types/ws@8.5.12': + dependencies: + '@types/node': 22.7.9 + + JSONStream@1.3.5: + dependencies: + jsonparse: 1.3.1 + through: 2.3.8 + + acorn-walk@8.3.4: + dependencies: + acorn: 8.13.0 + + acorn@8.13.0: {} + + agentkeepalive@4.5.0: + dependencies: + humanize-ms: 1.2.1 + + anchor-bankrun@0.5.0(@coral-xyz/anchor@0.30.1(bufferutil@4.0.8)(utf-8-validate@5.0.10))(@solana/web3.js@1.95.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(solana-bankrun@0.4.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)): + dependencies: + '@coral-xyz/anchor': 0.30.1(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.95.4(bufferutil@4.0.8)(utf-8-validate@5.0.10) + solana-bankrun: 0.4.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + + ansi-colors@4.1.3: {} + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + arg@4.1.3: {} + + argparse@2.0.1: {} + + arrify@1.0.1: {} + + assertion-error@1.1.0: {} + + balanced-match@1.0.2: {} + + base-x@3.0.10: + dependencies: + safe-buffer: 5.2.1 + + base-x@5.0.0: {} + + base64-js@1.5.1: {} + + bigint-buffer@1.1.5: + dependencies: + bindings: 1.5.0 + + bignumber.js@9.1.2: {} + + binary-extensions@2.3.0: {} + + bindings@1.5.0: + dependencies: + file-uri-to-path: 1.0.0 + + bn.js@5.2.1: {} + + borsh@0.7.0: + dependencies: + bn.js: 5.2.1 + bs58: 4.0.1 + text-encoding-utf-8: 1.0.2 + + borsh@2.0.0: {} + + brace-expansion@2.0.1: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browser-stdout@1.3.1: {} + + bs58@4.0.1: + dependencies: + base-x: 3.0.10 + + bs58@6.0.0: + dependencies: + base-x: 5.0.0 + + buffer-from@1.1.2: {} + + buffer-layout@1.2.2: {} + + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + bufferutil@4.0.8: + dependencies: + node-gyp-build: 4.8.2 + optional: true + + camelcase@6.3.0: {} + + chai@4.5.0: + dependencies: + assertion-error: 1.1.0 + check-error: 1.0.3 + deep-eql: 4.1.4 + get-func-name: 2.0.2 + loupe: 2.3.7 + pathval: 1.1.1 + type-detect: 4.1.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@5.3.0: {} + + check-error@1.0.3: + dependencies: + get-func-name: 2.0.2 + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + cliui@7.0.4: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + commander@12.1.0: {} + + commander@2.20.3: {} + + create-require@1.1.1: {} + + cross-fetch@3.1.8: + dependencies: + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + crypto-hash@1.3.0: {} + + debug@4.3.7(supports-color@8.1.1): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 8.1.1 + + decamelize@4.0.0: {} + + deep-eql@4.1.4: + dependencies: + type-detect: 4.1.0 + + delay@5.0.0: {} + + diff@3.5.0: {} + + diff@4.0.2: {} + + diff@5.2.0: {} + + dot-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.8.0 + + dotenv@16.4.5: {} + + emoji-regex@8.0.0: {} + + es6-promise@4.2.8: {} + + es6-promisify@5.0.0: + dependencies: + es6-promise: 4.2.8 + + escalade@3.2.0: {} + + escape-string-regexp@4.0.0: {} + + eventemitter3@4.0.7: {} + + eventemitter3@5.0.1: {} + + eyes@0.1.8: {} + + fast-stable-stringify@1.0.0: {} + + fastestsmallesttextencoderdecoder@1.0.22: {} + + file-uri-to-path@1.0.0: {} + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat@5.0.2: {} + + fs.realpath@1.0.0: {} + + fsevents@2.3.3: + optional: true + + get-caller-file@2.0.5: {} + + get-func-name@2.0.2: {} + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob@8.1.0: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 5.1.6 + once: 1.4.0 + + has-flag@4.0.0: {} + + he@1.2.0: {} + + humanize-ms@1.2.1: + dependencies: + ms: 2.1.3 + + ieee754@1.2.1: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + is-plain-obj@2.1.0: {} + + is-unicode-supported@0.1.0: {} + + isomorphic-ws@4.0.1(ws@7.5.10(bufferutil@4.0.8)(utf-8-validate@5.0.10)): + dependencies: + ws: 7.5.10(bufferutil@4.0.8)(utf-8-validate@5.0.10) + + jayson@4.1.2(bufferutil@4.0.8)(utf-8-validate@5.0.10): + dependencies: + '@types/connect': 3.4.38 + '@types/node': 12.20.55 + '@types/ws': 7.4.7 + 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(ws@7.5.10(bufferutil@4.0.8)(utf-8-validate@5.0.10)) + json-stringify-safe: 5.0.1 + uuid: 8.3.2 + ws: 7.5.10(bufferutil@4.0.8)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + json-stringify-safe@5.0.1: {} + + json5@1.0.2: + dependencies: + minimist: 1.2.8 + optional: true + + jsonparse@1.3.1: {} + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + log-symbols@4.1.0: + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + + loupe@2.3.7: + dependencies: + get-func-name: 2.0.2 + + lower-case@2.0.2: + dependencies: + tslib: 2.8.0 + + make-error@1.3.6: {} + + minimatch@5.1.6: + dependencies: + brace-expansion: 2.0.1 + + minimist@1.2.8: {} + + mkdirp@0.5.6: + dependencies: + minimist: 1.2.8 + + mocha@10.7.3: + dependencies: + ansi-colors: 4.1.3 + browser-stdout: 1.3.1 + chokidar: 3.6.0 + debug: 4.3.7(supports-color@8.1.1) + diff: 5.2.0 + escape-string-regexp: 4.0.0 + find-up: 5.0.0 + glob: 8.1.0 + he: 1.2.0 + js-yaml: 4.1.0 + log-symbols: 4.1.0 + minimatch: 5.1.6 + ms: 2.1.3 + serialize-javascript: 6.0.2 + strip-json-comments: 3.1.1 + supports-color: 8.1.1 + workerpool: 6.5.1 + yargs: 16.2.0 + yargs-parser: 20.2.9 + yargs-unparser: 2.0.0 + + ms@2.1.3: {} + + no-case@3.0.4: + dependencies: + lower-case: 2.0.2 + tslib: 2.8.0 + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + node-gyp-build@4.8.2: + optional: true + + normalize-path@3.0.0: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + pako@2.1.0: {} + + path-exists@4.0.0: {} + + pathval@1.1.1: {} + + picomatch@2.3.1: {} + + randombytes@2.1.0: + dependencies: + safe-buffer: 5.2.1 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + regenerator-runtime@0.14.1: {} + + require-directory@2.1.1: {} + + rpc-websockets@9.0.4: + dependencies: + '@swc/helpers': 0.5.13 + '@types/uuid': 8.3.4 + '@types/ws': 8.5.12 + buffer: 6.0.3 + eventemitter3: 5.0.1 + uuid: 8.3.2 + ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + optionalDependencies: + bufferutil: 4.0.8 + utf-8-validate: 5.0.10 + + safe-buffer@5.2.1: {} + + serialize-javascript@6.0.2: + dependencies: + randombytes: 2.1.0 + + snake-case@3.0.4: + dependencies: + dot-case: 3.0.4 + tslib: 2.8.0 + + solana-bankrun-darwin-arm64@0.4.0: + optional: true + + solana-bankrun-darwin-universal@0.4.0: + optional: true + + solana-bankrun-darwin-x64@0.4.0: + optional: true + + solana-bankrun-linux-x64-gnu@0.4.0: + optional: true + + solana-bankrun-linux-x64-musl@0.4.0: + optional: true + + solana-bankrun@0.4.0(bufferutil@4.0.8)(utf-8-validate@5.0.10): + dependencies: + '@solana/web3.js': 1.95.4(bufferutil@4.0.8)(utf-8-validate@5.0.10) + bs58: 4.0.1 + optionalDependencies: + solana-bankrun-darwin-arm64: 0.4.0 + solana-bankrun-darwin-universal: 0.4.0 + solana-bankrun-darwin-x64: 0.4.0 + solana-bankrun-linux-x64-gnu: 0.4.0 + solana-bankrun-linux-x64-musl: 0.4.0 + transitivePeerDependencies: + - bufferutil + - encoding + - utf-8-validate + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-bom@3.0.0: + optional: true + + strip-json-comments@3.1.1: {} + + superstruct@0.15.5: {} + + superstruct@2.0.2: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + text-encoding-utf-8@1.0.2: {} + + through@2.3.8: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + toml@3.0.0: {} + + tr46@0.0.3: {} + + ts-mocha@10.0.0(mocha@10.7.3): + dependencies: + mocha: 10.7.3 + ts-node: 7.0.1 + optionalDependencies: + tsconfig-paths: 3.15.0 + + ts-node@10.9.2(@types/node@22.7.9)(typescript@5.6.3): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.11 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 22.7.9 + acorn: 8.13.0 + acorn-walk: 8.3.4 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.6.3 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + + ts-node@7.0.1: + dependencies: + arrify: 1.0.1 + buffer-from: 1.1.2 + diff: 3.5.0 + make-error: 1.3.6 + minimist: 1.2.8 + mkdirp: 0.5.6 + source-map-support: 0.5.21 + yn: 2.0.0 + + tsconfig-paths@3.15.0: + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + optional: true + + tslib@2.8.0: {} + + type-detect@4.1.0: {} + + typescript@5.6.3: {} + + undici-types@6.19.8: {} + + utf-8-validate@5.0.10: + dependencies: + node-gyp-build: 4.8.2 + optional: true + + uuid@8.3.2: {} + + v8-compile-cache-lib@3.0.1: {} + + webidl-conversions@3.0.1: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + workerpool@6.5.1: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrappy@1.0.2: {} + + ws@7.5.10(bufferutil@4.0.8)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.0.8 + utf-8-validate: 5.0.10 + + ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.0.8 + utf-8-validate: 5.0.10 + + y18n@5.0.8: {} + + yargs-parser@20.2.9: {} + + yargs-unparser@2.0.0: + dependencies: + camelcase: 6.3.0 + decamelize: 4.0.0 + flat: 5.0.2 + is-plain-obj: 2.1.0 + + yargs@16.2.0: + dependencies: + cliui: 7.0.4 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.9 + + yn@2.0.0: {} + + yn@3.1.1: {} + + yocto-queue@0.1.0: {} diff --git a/tokens/escrow/steel/program/Cargo.toml b/tokens/escrow/steel/program/Cargo.toml new file mode 100644 index 000000000..3e9acb261 --- /dev/null +++ b/tokens/escrow/steel/program/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "escrow-program" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib", "lib"] + +[dependencies] +escrow-api.workspace = true +num-bigint = {version = "0.4.6", features = ["rand"]} +solana-program.workspace = true +spl-associated-token-account.workspace = true +spl-token.workspace = true +steel.workspace = true + +[dev-dependencies] +bs64 = "0.1.2" +rand = "0.8.5" +solana-program-test = "1.18" +solana-sdk = "1.18" +tokio = { version = "1.35", features = ["full"] } diff --git a/tokens/escrow/steel/program/src/lib.rs b/tokens/escrow/steel/program/src/lib.rs new file mode 100644 index 000000000..a35844d0f --- /dev/null +++ b/tokens/escrow/steel/program/src/lib.rs @@ -0,0 +1,27 @@ +mod make_offer; +mod refund; +mod take_offer; + +use escrow_api::prelude::*; +use make_offer::*; +use refund::*; +use steel::*; +use take_offer::*; + +pub fn process_instruction( + program_id: &Pubkey, + accounts: &[AccountInfo], + data: &[u8], +) -> ProgramResult { + let (ix, data) = parse_instruction(&escrow_api::ID, program_id, data)?; + + match ix { + AccountInstruction::MakeOffer => process_make_offer(accounts, data)?, + AccountInstruction::TakeOffer => process_take_offer(accounts, data)?, + AccountInstruction::Refund => process_refund(accounts)?, + } + + Ok(()) +} + +entrypoint!(process_instruction); diff --git a/tokens/escrow/steel/program/src/make_offer.rs b/tokens/escrow/steel/program/src/make_offer.rs new file mode 100644 index 000000000..efce406b4 --- /dev/null +++ b/tokens/escrow/steel/program/src/make_offer.rs @@ -0,0 +1,93 @@ +use escrow_api::prelude::*; +use solana_program::msg; +use steel::*; + +pub fn process_make_offer(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult { + // Check number of accounts provided + if accounts.len() < 9 { + return Err(ProgramError::NotEnoughAccountKeys); + } + + // Parse args. + let args = MakeOffer::try_from_bytes(data)?; + let token_a_offered = u64::from_le_bytes(args.token_a_offered_amount); + let token_b_wanted_amount = u64::from_le_bytes(args.token_b_wanted_amount); + let id = u64::from_le_bytes(args.id); + let offer_bump = u8::from_le_bytes(args.bump); + + // Load accounts. + let [signer_maker_info, token_mint_a, token_mint_b, maker_token_account_a, offer, vault, token_program, associated_token_program, system_program] = + accounts + else { + return Err(ProgramError::NotEnoughAccountKeys); + }; + + //Validating Accounts + signer_maker_info.is_signer()?; + token_mint_a.as_mint()?; + token_mint_b.as_mint()?; + maker_token_account_a.as_associated_token_account(signer_maker_info.key, token_mint_a.key)?; + vault.is_empty()?.is_writable()?; + offer.is_empty()?.is_writable()?.has_seeds( + &[ + OFFER, + signer_maker_info.key.as_ref(), + id.to_le_bytes().as_ref(), + ], + &escrow_api::ID, + )?; + system_program.is_program(&system_program::ID)?; + token_program.is_program(&spl_token::ID)?; + associated_token_program.is_program(&spl_associated_token_account::ID)?; + + //Create offer account + create_account::( + offer, + system_program, + signer_maker_info, + &escrow_api::ID, + &[ + OFFER, + signer_maker_info.key.as_ref(), + id.to_le_bytes().as_ref(), + ], + )?; + + create_associated_token_account( + signer_maker_info, + offer, + vault, + token_mint_a, + system_program, + token_program, + associated_token_program, + )?; + + msg!( + "ATA's amount balance:{:?}, token offered:{:?}", + maker_token_account_a + .as_associated_token_account(signer_maker_info.key, token_mint_a.key)? + .amount, + token_a_offered + ); + // Move the tokens from the maker's ATA to the vault + transfer( + signer_maker_info, + maker_token_account_a, + vault, + token_program, + token_a_offered, + )?; + + // Save the details of the offer to the offer account + let offer_data: &mut Offer = offer.as_account_mut::(&escrow_api::ID)?; + + offer_data.id = id; + offer_data.maker = *signer_maker_info.key; + offer_data.token_mint_a = *token_mint_a.key; + offer_data.token_mint_b = *token_mint_b.key; + offer_data.token_b_wanted_amount = token_b_wanted_amount; + offer_data.bump = offer_bump as u64; + + Ok(()) +} diff --git a/tokens/escrow/steel/program/src/refund.rs b/tokens/escrow/steel/program/src/refund.rs new file mode 100644 index 000000000..b97cf8afb --- /dev/null +++ b/tokens/escrow/steel/program/src/refund.rs @@ -0,0 +1,61 @@ +use escrow_api::prelude::*; +use steel::*; + +pub fn process_refund(accounts: &[AccountInfo<'_>]) -> ProgramResult { + // Load accounts. + let [signer_maker_info, token_mint_a, maker_token_account_a, offer, vault, token_program, associated_token_program, system_program] = + accounts + else { + return Err(ProgramError::NotEnoughAccountKeys); + }; + + signer_maker_info.is_signer()?; + let offer_data: &Offer = offer.as_account::(&escrow_api::ID)?; + offer.is_writable()?.has_seeds( + &[ + OFFER, + signer_maker_info.key.as_ref(), + offer_data.id.to_le_bytes().as_ref(), + ], + &escrow_api::ID, + )?; + system_program.is_program(&system_program::ID)?; + token_program.is_program(&spl_token::ID)?; + associated_token_program.is_program(&spl_associated_token_account::ID)?; + + let id = offer_data.id.to_le_bytes(); + + let seeds = &[OFFER, signer_maker_info.key.as_ref(), &id]; + let signer_seeds = &seeds[..]; + + // refund / transfer from vault to maker + transfer_signed( + offer, + vault, + maker_token_account_a, + token_program, + vault + .as_associated_token_account(offer.key, token_mint_a.key)? + .amount, + signer_seeds, + )?; + + // close the vault and offer accounts + let close_instruction = &spl_token::instruction::close_account( + token_program.key, + vault.key, + signer_maker_info.key, + offer.key, + &[offer.key, signer_maker_info.key], + )?; + let account_infos = &[vault.clone(), signer_maker_info.clone(), offer.clone()]; + + invoke_signed_with_bump( + close_instruction, + account_infos, + signer_seeds, + offer_data.bump as u8, + )?; + offer.close(signer_maker_info)?; + Ok(()) +} diff --git a/tokens/escrow/steel/program/src/take_offer.rs b/tokens/escrow/steel/program/src/take_offer.rs new file mode 100644 index 000000000..201965c43 --- /dev/null +++ b/tokens/escrow/steel/program/src/take_offer.rs @@ -0,0 +1,95 @@ +use escrow_api::prelude::*; +use solana_program::msg; +use steel::*; + +pub fn process_take_offer(accounts: &[AccountInfo<'_>], _data: &[u8]) -> ProgramResult { + // Load accounts. + let [taker_signer, maker, token_mint_a, token_mint_b, taker_token_account_a, taker_token_account_b, maker_token_account_b, offer, vault, token_program, associated_token_program, system_program] = + accounts + else { + return Err(ProgramError::NotEnoughAccountKeys); + }; + + taker_signer.is_signer()?; + token_mint_a.as_mint()?; + token_mint_b.as_mint()?; + taker_token_account_b.as_associated_token_account(taker_signer.key, token_mint_b.key)?; + system_program.is_program(&system_program::ID)?; + token_program.is_program(&spl_token::ID)?; + associated_token_program.is_program(&spl_associated_token_account::ID)?; + + // create ATA's for maker_token_b and taker_token_a if needed + + match maker_token_account_b.as_associated_token_account(maker.key, token_mint_b.key) { + Ok(_) => msg!("maker token account already exists"), + Err(_) => create_associated_token_account( + taker_signer, + maker, + maker_token_account_b, + token_mint_b, + system_program, + token_program, + associated_token_program, + )?, + }; + match taker_token_account_a.as_associated_token_account(taker_signer.key, token_mint_a.key) { + Ok(_) => msg!("maker token account already exists"), + Err(_) => create_associated_token_account( + taker_signer, + taker_signer, + taker_token_account_a, + token_mint_a, + system_program, + token_program, + associated_token_program, + )?, + }; + + let offer_data: &Offer = offer.as_account::(&escrow_api::ID)?; + + // Move the tokens from the taker's ATA to the maker's ATA + transfer( + taker_signer, + taker_token_account_b, + maker_token_account_b, + token_program, + offer_data.token_b_wanted_amount, + )?; + + let id = offer_data.id.to_le_bytes(); + + let seeds = &[OFFER, maker.key.as_ref(), &id]; + let signer_seeds = &seeds[..]; + + // transfer from vault to taker + transfer_signed( + offer, + vault, + taker_token_account_a, + token_program, + vault + .as_associated_token_account(offer.key, token_mint_a.key)? + .amount, + signer_seeds, + )?; + + //Close the vault and offer accounts. + let close_instruction = &spl_token::instruction::close_account( + token_program.key, + vault.key, + taker_signer.key, + offer.key, + &[offer.key, taker_signer.key], + )?; + let account_infos = &[vault.clone(), taker_signer.clone(), offer.clone()]; + + invoke_signed_with_bump( + close_instruction, + account_infos, + signer_seeds, + offer_data.bump as u8, + )?; + offer.close(maker)?; + + Ok(()) +} diff --git a/tokens/escrow/steel/program/tests/test.rs b/tokens/escrow/steel/program/tests/test.rs new file mode 100644 index 000000000..533bae5e7 --- /dev/null +++ b/tokens/escrow/steel/program/tests/test.rs @@ -0,0 +1,260 @@ +use rand::Rng; +use std::collections::HashMap; + +use escrow_api::prelude::*; +use solana_program::hash::Hash; +use solana_program_test::{processor, BanksClient, ProgramTest}; +use solana_sdk::{ + native_token::LAMPORTS_PER_SOL, program_pack::Pack, signature::Keypair, signer::Signer, + system_instruction, transaction::Transaction, +}; +use spl_associated_token_account::{ + get_associated_token_address, instruction::create_associated_token_account, +}; +use spl_token::{ + id, + instruction::{initialize_mint, mint_to}, + state::Account, + state::Mint, +}; +use steel::*; + +fn get_random_big_number(_size: usize) -> u64 { + let mut rng = rand::thread_rng(); + // let signed: BigInt = rng.sample(RandomBits::new(256)); + rng.gen::() +} + +async fn setup() -> (BanksClient, Keypair, Hash) { + let mut program_test = ProgramTest::new( + "escrow_program", + escrow_api::ID, + processor!(escrow_program::process_instruction), + ); + program_test.prefer_bpf(true); + program_test.start().await +} + +#[tokio::test] +async fn make_offer_and_take_offer_successful() { + // Setup test + let (mut banks, payer, blockhash) = setup().await; + + // Set up Keypairs for alice, bob, and the token mints + let alice = Keypair::new(); + let bob = Keypair::new(); + let token_mint_a = Keypair::new(); + let token_mint_b = Keypair::new(); + + // Token amounts + let token_a_offered_amount = 5_000_000; + let token_b_wanted_amount = 1_000_000; + + //Variables for offer_id, pubkey and bump + let offer_id = get_random_big_number(8); + let offer_pubkey = offer_pda(alice.pubkey(), offer_id).0; + let offer_bump = offer_pda(alice.pubkey(), offer_id).1; + + // Calculate Associated Token Accounts for alice (maker), bob (taker) and the vault + let alice_token_account_a = + get_associated_token_address(&alice.pubkey(), &token_mint_a.pubkey()); + let alice_token_account_b = + get_associated_token_address(&alice.pubkey(), &token_mint_b.pubkey()); + let bob_token_account_a = get_associated_token_address(&bob.pubkey(), &token_mint_a.pubkey()); + let bob_token_account_b = get_associated_token_address(&bob.pubkey(), &token_mint_b.pubkey()); + let vault = get_associated_token_address(&offer_pubkey, &token_mint_a.pubkey()); + + let _accounts = HashMap::from([ + ("maker", alice.pubkey()), + ("taker", bob.pubkey()), + ("tokenMintA", token_mint_a.pubkey()), + ("makerTokenAccountA", alice_token_account_a), + ("takerTokenAccountA", bob_token_account_a), + ("tokenMintB", token_mint_b.pubkey()), + ("makerTokenAccountB", alice_token_account_b), + ("takerTokenAccountB", bob_token_account_b), + ("offer", offer_pubkey), + ("vault", vault), + ]); + + // Airdrop SOL to alice and bob + let lamports = LAMPORTS_PER_SOL * 10; + let airdrop_tx = Transaction::new_signed_with_payer( + &[ + system_instruction::transfer(&payer.pubkey(), &alice.pubkey(), lamports), + system_instruction::transfer(&payer.pubkey(), &bob.pubkey(), lamports), + ], + Some(&payer.pubkey()), + &[&payer], + blockhash, + ); + let res = banks.process_transaction(airdrop_tx).await; + assert!(res.is_ok()); + + // Calculate minimum lamports required for rent exemption on mint accounts + let rent = banks.get_rent().await.unwrap(); + let mint_min_balance = rent.minimum_balance(Mint::LEN); + + // Create mint accounts for token A and token B + let create_mint_tx = Transaction::new_signed_with_payer( + &[ + system_instruction::create_account( + &payer.pubkey(), + &token_mint_a.pubkey(), + mint_min_balance, + Mint::LEN as u64, + &id(), + ), + system_instruction::create_account( + &payer.pubkey(), + &token_mint_b.pubkey(), + mint_min_balance, + Mint::LEN as u64, + &id(), + ), + ], + Some(&payer.pubkey()), + &[&payer, &token_mint_a, &token_mint_b], + blockhash, + ); + let res = banks.process_transaction(create_mint_tx).await; + assert!(res.is_ok()); + + // Transaction to initialize mints + let init_mint_tx = Transaction::new_signed_with_payer( + &[ + initialize_mint(&id(), &token_mint_a.pubkey(), &alice.pubkey(), None, 6).unwrap(), + initialize_mint(&id(), &token_mint_b.pubkey(), &bob.pubkey(), None, 6).unwrap(), + ], + Some(&payer.pubkey()), + &[&payer], + blockhash, + ); + let res = banks.process_transaction(init_mint_tx).await; + assert!(res.is_ok()); + + // Transaction to create associated token accounts and mint tokens + let mint_tokens_tx = Transaction::new_signed_with_payer( + &[ + create_associated_token_account( + &payer.pubkey(), + &alice.pubkey(), + &token_mint_a.pubkey(), + &id(), + ), + create_associated_token_account( + &payer.pubkey(), + &bob.pubkey(), + &token_mint_b.pubkey(), + &id(), + ), + mint_to( + &id(), + &token_mint_a.pubkey(), + &alice_token_account_a, + &alice.pubkey(), + &[], + 1_000_000_000, + ) + .unwrap(), + mint_to( + &id(), + &token_mint_b.pubkey(), + &bob_token_account_b, + &bob.pubkey(), + &[], + 1_000_000_000, + ) + .unwrap(), + ], + Some(&payer.pubkey()), + &[&payer, &bob, &alice], + blockhash, + ); + let res = banks.process_transaction(mint_tokens_tx).await; + assert!(res.is_ok()); + + // Create and submit make_offer transaction. + let ix = make_offer( + alice.pubkey(), + token_mint_a.pubkey(), + token_mint_b.pubkey(), + alice_token_account_a, + vault, + token_a_offered_amount, + token_b_wanted_amount, + offer_id, + offer_bump, + ); + + let tx = Transaction::new_signed_with_payer( + &[ix], + Some(&payer.pubkey()), + &[&alice, &payer], + blockhash, + ); + let res = banks.process_transaction(tx).await; + assert!(res.is_ok()); + + // Verify vault balance has increased. + let vault_account = banks + .get_account(vault) + .await + .unwrap() + .expect("could not fetch account"); + let account_inifo = Account::unpack(&vault_account.data).unwrap(); + assert_eq!(account_inifo.amount, token_a_offered_amount); + + //Verify the offer account contains the necessar details + let offer_account = banks + .get_account(offer_pubkey) + .await + .unwrap() + .expect("could not fetch account"); + let offer_data = Offer::try_from_bytes(&offer_account.data).unwrap(); + assert_eq!(offer_data.maker, alice.pubkey()); + assert_eq!(offer_data.id, offer_id); + assert_eq!(offer_data.token_mint_a, token_mint_a.pubkey()); + assert_eq!(offer_data.token_mint_b, token_mint_b.pubkey()); + assert_eq!(offer_data.token_b_wanted_amount, token_b_wanted_amount); + + // Create and submit take_offer transaction. + let ix = take_offer( + bob.pubkey(), + alice.pubkey(), + token_mint_a.pubkey(), + token_mint_b.pubkey(), + bob_token_account_a, + bob_token_account_b, + alice_token_account_b, + vault, + offer_id, + ); + + let tx = Transaction::new_signed_with_payer( + &[ix], + Some(&payer.pubkey()), + &[&bob, &payer], + blockhash, + ); + let res = banks.process_transaction(tx).await; + assert!(res.is_ok()); + + //Verify alice balance contains tokens wanted amount. + let alice_account = banks + .get_account(alice_token_account_b) + .await + .unwrap() + .expect("could not fetch account"); + let account_inifo = Account::unpack(&alice_account.data).unwrap(); + assert_eq!(account_inifo.amount, token_b_wanted_amount); + + //Verify bob balance contains tokens offered amount. + let bob_account = banks + .get_account(bob_token_account_a) + .await + .unwrap() + .expect("could not fetch account"); + let account_inifo = Account::unpack(&bob_account.data).unwrap(); + assert_eq!(account_inifo.amount, token_a_offered_amount); +} diff --git a/tokens/escrow/steel/tests/escrow.test.ts b/tokens/escrow/steel/tests/escrow.test.ts new file mode 100644 index 000000000..aa10e2158 --- /dev/null +++ b/tokens/escrow/steel/tests/escrow.test.ts @@ -0,0 +1,397 @@ +import { randomBytes } from 'node:crypto'; +import { before, describe, it } from 'node:test'; +import { BN } from '@coral-xyz/anchor'; +import { makeKeypairs } from '@solana-developers/helpers'; +import { + ASSOCIATED_TOKEN_PROGRAM_ID, + MINT_SIZE, + TOKEN_PROGRAM_ID as TOKEN_PROGRAM, + createAssociatedTokenAccountIdempotentInstruction, + createInitializeMint2Instruction, + createMintToInstruction, + getAssociatedTokenAddressSync, + getMinimumBalanceForRentExemptMint, +} from '@solana/spl-token'; +import { LAMPORTS_PER_SOL, PublicKey, SystemProgram, Transaction, TransactionInstruction } from '@solana/web3.js'; +import { BankrunProvider } from 'anchor-bankrun'; +import { serialize } from 'borsh'; +import { assert, expect } from 'chai'; +import { start } from 'solana-bankrun'; +import * as T from './types'; + +const getRandomBigNumber = (size = 8) => { + return new BN(randomBytes(size)); +}; + +describe('escrow-example', async () => { + // load program in solana-bankrun + const PROGRAM_ID = new PublicKey('z7msBPQHDJjTvdQRoEcKyENgXDhSRYeHieN1ZMTqo35'); + const context = await start([{ name: 'escrow_program', programId: PROGRAM_ID }], []); + const provider = new BankrunProvider(context); + const connection = provider.connection; + const client = context.banksClient; + + // We're going to reuse these accounts across multiple tests + const accounts: Record = { + tokenProgram: TOKEN_PROGRAM, + }; + + const [alice, bob, tokenMintA, tokenMintB] = makeKeypairs(4); + + const tokenAOfferedAmount = new BN(5_000_000); + const tokenBWantedAmount = new BN(1_000_000); + + before(async () => { + const [aliceTokenAccountA, aliceTokenAccountB, bobTokenAccountA, bobTokenAccountB] = [alice, bob].flatMap((keypair) => + [tokenMintA, tokenMintB].map((mint) => getAssociatedTokenAddressSync(mint.publicKey, keypair.publicKey, false, TOKEN_PROGRAM)), + ); + + // Airdrops to users, and creates two tokens mints 'A' and 'B'" + const minimumLamports = await getMinimumBalanceForRentExemptMint(connection); + + const sendSolInstructions: Array = [alice, bob].map((account) => + SystemProgram.transfer({ + fromPubkey: provider.publicKey, + toPubkey: account.publicKey, + lamports: 10 * LAMPORTS_PER_SOL, + }), + ); + + const createMintInstructions: Array = [tokenMintA, tokenMintB].map((mint) => + SystemProgram.createAccount({ + fromPubkey: provider.publicKey, + newAccountPubkey: mint.publicKey, + lamports: minimumLamports, + space: MINT_SIZE, + programId: TOKEN_PROGRAM, + }), + ); + + // Make tokenA and tokenB mints, mint tokens and create ATAs + const mintTokensInstructions: Array = [ + { + mint: tokenMintA.publicKey, + authority: alice.publicKey, + ata: aliceTokenAccountA, + }, + { + mint: tokenMintB.publicKey, + authority: bob.publicKey, + ata: bobTokenAccountB, + }, + ].flatMap((mintDetails) => [ + createInitializeMint2Instruction(mintDetails.mint, 6, mintDetails.authority, null, TOKEN_PROGRAM), + createAssociatedTokenAccountIdempotentInstruction(provider.publicKey, mintDetails.ata, mintDetails.authority, mintDetails.mint, TOKEN_PROGRAM), + createMintToInstruction(mintDetails.mint, mintDetails.ata, mintDetails.authority, 1_000_000_000, [], TOKEN_PROGRAM), + ]); + + // Add all these instructions to our transaction + const tx = new Transaction(); + tx.instructions = [...sendSolInstructions, ...createMintInstructions, ...mintTokensInstructions]; + const blockhash = context.lastBlockhash; + + tx.recentBlockhash = blockhash; + tx.sign(tokenMintA, tokenMintB, alice, bob); + await provider.sendAndConfirm(tx, [tokenMintA, tokenMintB, alice, bob]); + + // Save the accounts for later use + accounts.maker = alice.publicKey; + accounts.taker = bob.publicKey; + accounts.tokenMintA = tokenMintA.publicKey; + accounts.makerTokenAccountA = aliceTokenAccountA; + accounts.takerTokenAccountA = bobTokenAccountA; + accounts.tokenMintB = tokenMintB.publicKey; + accounts.makerTokenAccountB = aliceTokenAccountB; + accounts.takerTokenAccountB = bobTokenAccountB; + }); + + const make = async () => { + // Pick a random ID for the offer we'll make + const offerId = getRandomBigNumber(); + + // Then determine the account addresses we'll use for the offer and the vault + const [offer, bump] = PublicKey.findProgramAddressSync( + [Buffer.from('offer'), accounts.maker.toBuffer(), offerId.toArrayLike(Buffer, 'le', 8)], + PROGRAM_ID, + ); + + const vault = getAssociatedTokenAddressSync(accounts.tokenMintA, offer, true, TOKEN_PROGRAM, ASSOCIATED_TOKEN_PROGRAM_ID); + + accounts.offer = offer; + accounts.vault = vault; + + const data = serialize( + { + struct: { + discriminator: 'u8', + id: 'u64', + token_a_offered_amount: 'u64', + token_b_wanted_amount: 'u64', + bump: 'u8', + }, + }, + { + discriminator: 0, + id: offerId, + token_a_offered_amount: tokenAOfferedAmount, + token_b_wanted_amount: tokenBWantedAmount, + bump, + }, + ); + + const ix = new TransactionInstruction({ + data: Buffer.from(data), + keys: [ + { pubkey: alice.publicKey, isSigner: true, isWritable: true }, + { pubkey: accounts.tokenMintA, isSigner: false, isWritable: true }, + { pubkey: accounts.tokenMintB, isSigner: false, isWritable: true }, + { + pubkey: accounts.makerTokenAccountA, + isSigner: false, + isWritable: true, + }, + { + pubkey: accounts.offer, + isSigner: false, + isWritable: true, + }, + { + pubkey: accounts.vault, + isSigner: false, + isWritable: true, + }, + { + pubkey: TOKEN_PROGRAM, + isSigner: false, + isWritable: false, + }, + { + pubkey: ASSOCIATED_TOKEN_PROGRAM_ID, + isSigner: false, + isWritable: false, + }, + { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }, + ], + programId: PROGRAM_ID, + }); + + const blockhash = context.lastBlockhash; + const tx = new Transaction(); + tx.recentBlockhash = blockhash; + tx.add(ix); + tx.sign(alice); + await client.processTransaction(tx); + + // Check our vault contains the tokens offered + const vaultBalanceResponse = await connection.getAccountInfo(vault); + const datas = T.decodeAccount(vaultBalanceResponse.data); + const vaultBalance = new BN(datas.amount.toString()); + assert(vaultBalance.eq(tokenAOfferedAmount)); + + // Check our Offer account contains the correct data + const offerAccount = await connection.getAccountInfo(offer); + const offerData = T.decodeOffer(offerAccount.data); + + assert(offerData.maker.equals(alice.publicKey)); + assert(offerData.tokenMintA.equals(accounts.tokenMintA)); + assert(offerData.tokenMintB.equals(accounts.tokenMintB)); + const tokenBWanted = new BN(offerData.tokenBWantedAmount.toString()); + assert(tokenBWanted.eq(tokenBWantedAmount)); + }; + + const take = async () => { + const ix = new TransactionInstruction({ + data: Buffer.alloc(1, 1), + keys: [ + { pubkey: bob.publicKey, isSigner: true, isWritable: true }, + { pubkey: alice.publicKey, isSigner: false, isWritable: true }, + { pubkey: accounts.tokenMintA, isSigner: false, isWritable: true }, + { pubkey: accounts.tokenMintB, isSigner: false, isWritable: true }, + { + pubkey: accounts.takerTokenAccountA, + isSigner: false, + isWritable: true, + }, + { + pubkey: accounts.takerTokenAccountB, + isSigner: false, + isWritable: true, + }, + { + pubkey: accounts.makerTokenAccountB, + isSigner: false, + isWritable: true, + }, + { + pubkey: accounts.offer, + isSigner: false, + isWritable: true, + }, + { + pubkey: accounts.vault, + isSigner: false, + isWritable: true, + }, + { + pubkey: TOKEN_PROGRAM, + isSigner: false, + isWritable: false, + }, + { + pubkey: ASSOCIATED_TOKEN_PROGRAM_ID, + isSigner: false, + isWritable: false, + }, + { pubkey: SystemProgram.programId, isSigner: false, isWritable: false }, + ], + programId: PROGRAM_ID, + }); + + const blockhash = context.lastBlockhash; + const tx = new Transaction(); + tx.recentBlockhash = blockhash; + tx.add(ix); + tx.sign(bob); + await client.processTransaction(tx); + + /**Check alice's account for the tokens wanted*/ + const AliceAccountData = T.decodeAccount((await connection.getAccountInfo(accounts.makerTokenAccountB)).data); + const aliceBalance = new BN(AliceAccountData.amount.toString()); + assert(aliceBalance.eq(tokenBWantedAmount)); + + /**Check bobs's account for the tokens offered*/ + const bobsData = T.decodeAccount((await connection.getAccountInfo(accounts.takerTokenAccountA)).data); + const bobsBalance = new BN(bobsData.amount.toString()); + assert.strictEqual(bobsBalance.toString(), tokenAOfferedAmount.toString()); + }; + + it('Puts the tokens Alice offers into the vault when Alice makes an offer', async () => { + await make(); + }); + + it("Puts the tokens from the vault into Bob's account, and gives Alice Bob's tokens, when Bob takes an offer", async () => { + await take(); + /**Check that the Vault account does not exist */ + + try { + await connection.getAccountInfo(accounts.vault); + } catch (error: any) { + expect(error.toString()).to.include(`Could not find ${accounts.vault.toString()}`); + } + }); + + it('Refunds the token amount from the vault if the maker chooses to', async () => { + await make(); + const ix = new TransactionInstruction({ + data: Buffer.alloc(1, 2), + keys: [ + { pubkey: alice.publicKey, isSigner: true, isWritable: true }, + { pubkey: accounts.tokenMintA, isSigner: false, isWritable: true }, + { + pubkey: accounts.makerTokenAccountA, + isSigner: false, + isWritable: true, + }, + { + pubkey: accounts.offer, + isSigner: false, + isWritable: true, + }, + { + pubkey: accounts.vault, + isSigner: false, + isWritable: true, + }, + { + pubkey: TOKEN_PROGRAM, + isSigner: false, + isWritable: false, + }, + { + pubkey: ASSOCIATED_TOKEN_PROGRAM_ID, + isSigner: false, + isWritable: false, + }, + { + pubkey: SystemProgram.programId, + isSigner: false, + isWritable: false, + }, + ], + programId: PROGRAM_ID, + }); + + const blockhash = context.lastBlockhash; + const tx = new Transaction(); + tx.recentBlockhash = blockhash; + tx.add(ix); + tx.sign(alice); + await client.processTransaction(tx); + + /**Check alice's account for the tokens wanted*/ + const AliceAccountData = T.decodeAccount((await connection.getAccountInfo(accounts.makerTokenAccountA)).data); + const aliceBalance = new BN(AliceAccountData.amount.toString()); + expect(aliceBalance.toString()).to.equal(new BN(1_000_000_000).sub(tokenAOfferedAmount).toString()); + + /**Check that the Vault account does not exist */ + try { + await connection.getAccountInfo(accounts.vault); + } catch (error: any) { + expect(error.toString()).to.include(`Could not find ${accounts.vault.toString()}`); + } + }); + + it('should fail when Bob tries to withdraw Alices funds without depositing', async () => { + await make(); + const ix = new TransactionInstruction({ + data: Buffer.alloc(1, 2), + keys: [ + { pubkey: bob.publicKey, isSigner: true, isWritable: true }, + { pubkey: accounts.tokenMintA, isSigner: false, isWritable: true }, + { + pubkey: accounts.takerTokenAccountA, + isSigner: false, + isWritable: true, + }, + { + pubkey: accounts.offer, + isSigner: false, + isWritable: true, + }, + { + pubkey: accounts.vault, + isSigner: false, + isWritable: true, + }, + { + pubkey: TOKEN_PROGRAM, + isSigner: false, + isWritable: false, + }, + { + pubkey: ASSOCIATED_TOKEN_PROGRAM_ID, + isSigner: false, + isWritable: false, + }, + { + pubkey: SystemProgram.programId, + isSigner: false, + isWritable: false, + }, + ], + programId: PROGRAM_ID, + }); + + const blockhash = context.lastBlockhash; + const tx = new Transaction(); + tx.recentBlockhash = blockhash; + tx.add(ix); + tx.sign(bob); + + try { + await client.processTransaction(tx); + } catch (error) { + expect(error.message).to.include('Error'); + } + }); +}); diff --git a/tokens/escrow/steel/tests/types.ts b/tokens/escrow/steel/tests/types.ts new file mode 100644 index 000000000..eec0458fa --- /dev/null +++ b/tokens/escrow/steel/tests/types.ts @@ -0,0 +1,57 @@ +import { type BN, type Program } from '@coral-xyz/anchor'; +import { struct, u8, u32 } from '@solana/buffer-layout'; +import { bool, publicKey, u64 } from '@solana/buffer-layout-utils'; +import { PublicKey } from '@solana/web3.js'; +import { type Schema, deserialize } from 'borsh'; + +interface TokenAccount { + mint: PublicKey; + owner: PublicKey; + amount: bigint; + delegateOption: 1 | 0; + delegate: PublicKey; + isNativeOption: 1 | 0; + isNative: bigint; + delegateAmount: bigint; + closeAuthorityOption: 1 | 0; + closeAuthority: PublicKey; +} + +interface Offer { + disc: bigint; + id: bigint; + maker: PublicKey; + tokenMintA: PublicKey; + tokenMintB: PublicKey; + tokenBWantedAmount: bigint; +} + +export const TokenLayout = struct([ + publicKey('mint'), + publicKey('owner'), + u64('amount'), + u64('delegateOption'), + publicKey('delegate'), + u64('isNativeOption'), + u64('isNative'), + u64('delegateAmount'), + u64('closeAuthorityOption'), + publicKey('closeAuthority'), +]); + +export const OfferLayout = struct([ + u64('disc'), + u64('id'), + publicKey('maker'), + publicKey('tokenMintA'), + publicKey('tokenMintB'), + u64('tokenBWantedAmount'), +]); + +// Example of decoding +export function decodeAccount(data) { + return TokenLayout.decode(data); +} +export function decodeOffer(data) { + return OfferLayout.decode(data); +} diff --git a/tokens/escrow/steel/tsconfig.json b/tokens/escrow/steel/tsconfig.json new file mode 100644 index 000000000..8a2658454 --- /dev/null +++ b/tokens/escrow/steel/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "types": ["mocha", "chai", "node"], + "typeRoots": ["./node_modules/@types"], + "lib": ["es2015"], + "module": "commonjs", + "target": "es6", + "moduleResolution": "node", + "esModuleInterop": true + } +}