From 159ab3c92c1a57a4998684a1ca59931926659e45 Mon Sep 17 00:00:00 2001 From: Britt Cyr Date: Fri, 23 Aug 2024 16:07:04 +0200 Subject: [PATCH 01/16] Add wrapper reference impelementation --- Cargo.lock | 22 + .../src/program/processor/batch_update.rs | 25 +- programs/manifest/src/quantities.rs | 104 ++- programs/manifest/tests/cases/batch_update.rs | 12 +- programs/manifest/tests/cases/cancel_order.rs | 15 +- programs/manifest/tests/cases/global.rs | 18 +- programs/manifest/tests/cases/place_order.rs | 103 ++- programs/manifest/tests/cases/swap.rs | 39 +- programs/manifest/tests/cases/token22.rs | 12 +- .../manifest/tests/program_test/fixtures.rs | 12 +- programs/wrapper/Cargo.toml | 40 + programs/wrapper/src/instruction.rs | 66 ++ .../batch_update_instruction.rs | 42 ++ .../claim_seat_instruction.rs | 26 + .../create_wrapper_instruction.rs | 42 ++ .../deposit_instruction.rs | 38 + .../wrapper/src/instruction_builders/mod.rs | 11 + .../withdraw_instruction.rs | 38 + programs/wrapper/src/lib.rs | 74 ++ programs/wrapper/src/market_info.rs | 98 +++ programs/wrapper/src/open_order.rs | 193 +++++ .../wrapper/src/processors/batch_upate.rs | 463 ++++++++++++ programs/wrapper/src/processors/claim_seat.rs | 97 +++ .../wrapper/src/processors/create_wrapper.rs | 44 ++ programs/wrapper/src/processors/deposit.rs | 82 +++ programs/wrapper/src/processors/mod.rs | 6 + programs/wrapper/src/processors/shared.rs | 396 ++++++++++ programs/wrapper/src/processors/withdraw.rs | 84 +++ programs/wrapper/src/wrapper_state.rs | 51 ++ programs/wrapper/tests/cases/batch_update.rs | 533 ++++++++++++++ programs/wrapper/tests/cases/claim_seat.rs | 11 + programs/wrapper/tests/cases/deposit.rs | 12 + programs/wrapper/tests/cases/mod.rs | 4 + programs/wrapper/tests/cases/withdraw.rs | 12 + programs/wrapper/tests/mod.rs | 5 + .../wrapper/tests/program_test/fixtures.rs | 695 ++++++++++++++++++ programs/wrapper/tests/program_test/mod.rs | 3 + 37 files changed, 3436 insertions(+), 92 deletions(-) create mode 100644 programs/wrapper/Cargo.toml create mode 100644 programs/wrapper/src/instruction.rs create mode 100644 programs/wrapper/src/instruction_builders/batch_update_instruction.rs create mode 100644 programs/wrapper/src/instruction_builders/claim_seat_instruction.rs create mode 100644 programs/wrapper/src/instruction_builders/create_wrapper_instruction.rs create mode 100644 programs/wrapper/src/instruction_builders/deposit_instruction.rs create mode 100644 programs/wrapper/src/instruction_builders/mod.rs create mode 100644 programs/wrapper/src/instruction_builders/withdraw_instruction.rs create mode 100644 programs/wrapper/src/lib.rs create mode 100644 programs/wrapper/src/market_info.rs create mode 100644 programs/wrapper/src/open_order.rs create mode 100644 programs/wrapper/src/processors/batch_upate.rs create mode 100644 programs/wrapper/src/processors/claim_seat.rs create mode 100644 programs/wrapper/src/processors/create_wrapper.rs create mode 100644 programs/wrapper/src/processors/deposit.rs create mode 100644 programs/wrapper/src/processors/mod.rs create mode 100644 programs/wrapper/src/processors/shared.rs create mode 100644 programs/wrapper/src/processors/withdraw.rs create mode 100644 programs/wrapper/src/wrapper_state.rs create mode 100644 programs/wrapper/tests/cases/batch_update.rs create mode 100644 programs/wrapper/tests/cases/claim_seat.rs create mode 100644 programs/wrapper/tests/cases/deposit.rs create mode 100644 programs/wrapper/tests/cases/mod.rs create mode 100644 programs/wrapper/tests/cases/withdraw.rs create mode 100644 programs/wrapper/tests/mod.rs create mode 100644 programs/wrapper/tests/program_test/fixtures.rs create mode 100644 programs/wrapper/tests/program_test/mod.rs diff --git a/Cargo.lock b/Cargo.lock index cc862cc3b..e8226e45a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6167,6 +6167,28 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "wrapper" +version = "0.1.0" +dependencies = [ + "anyhow", + "borsh 0.9.3", + "bytemuck", + "hypertree", + "manifest", + "num_enum 0.5.9", + "shank", + "solana-logger", + "solana-program", + "solana-program-test", + "solana-sdk", + "solana-security-txt", + "spl-token 3.5.0", + "static_assertions", + "thiserror", + "tokio", +] + [[package]] name = "x509-parser" version = "0.14.0" diff --git a/programs/manifest/src/program/processor/batch_update.rs b/programs/manifest/src/program/processor/batch_update.rs index ef0b5e4d4..023fe208b 100644 --- a/programs/manifest/src/program/processor/batch_update.rs +++ b/programs/manifest/src/program/processor/batch_update.rs @@ -3,7 +3,7 @@ use std::cell::RefMut; use crate::{ logs::{emit_stack, CancelOrderLog, PlaceOrderLog}, program::{assert_with_msg, ManifestError}, - quantities::{BaseAtoms, QuoteAtomsPerBaseAtom, WrapperU64}, + quantities::{BaseAtoms, PriceConversionError, QuoteAtomsPerBaseAtom, WrapperU64}, state::{ AddOrderToMarketArgs, AddOrderToMarketResult, MarketRefMut, OrderType, RestingOrder, BLOCK_SIZE, @@ -11,7 +11,7 @@ use crate::{ validation::loaders::BatchUpdateContext, }; use borsh::{BorshDeserialize, BorshSerialize}; -use hypertree::{DataIndex, PodBool}; +use hypertree::{trace, DataIndex, PodBool}; use solana_program::{ account_info::AccountInfo, entrypoint::ProgramResult, program::set_return_data, pubkey::Pubkey, }; @@ -48,7 +48,8 @@ impl CancelOrderParams { #[derive(Debug, BorshDeserialize, BorshSerialize, Clone)] pub struct PlaceOrderParams { base_atoms: u64, - price: f64, // TODO: change type to u32 mantissa + i8 decimal exponent to save on bytes + price_mantissa: u32, + price_exponent: i8, is_bid: bool, last_valid_slot: u32, order_type: OrderType, @@ -57,14 +58,16 @@ pub struct PlaceOrderParams { impl PlaceOrderParams { pub fn new( base_atoms: u64, - price: f64, + price_mantissa: u32, + price_exponent: i8, is_bid: bool, order_type: OrderType, last_valid_slot: u32, ) -> Self { PlaceOrderParams { base_atoms, - price, + price_mantissa, + price_exponent, is_bid, order_type, last_valid_slot, @@ -73,8 +76,11 @@ impl PlaceOrderParams { pub fn base_atoms(&self) -> u64 { self.base_atoms } - pub fn price(&self) -> f64 { - self.price + pub fn try_price(&self) -> Result { + QuoteAtomsPerBaseAtom::try_from_mantissa_and_exponent( + self.price_mantissa, + self.price_exponent, + ) } pub fn is_bid(&self) -> bool { self.is_bid @@ -134,6 +140,8 @@ pub(crate) fn process_batch_update( orders, } = BatchUpdateParams::try_from_slice(data)?; + trace!("batch_update trader_index_hint:{trader_index_hint:?} cancels:{cancels:?} orders:{orders:?}"); + let trader_index: DataIndex = { let market_data: &mut RefMut<&mut [u8]> = &mut market.try_borrow_mut_data()?; let mut dynamic_account: MarketRefMut = get_mut_dynamic_account(market_data); @@ -201,8 +209,7 @@ pub(crate) fn process_batch_update( for place_order in orders { { let base_atoms: BaseAtoms = BaseAtoms::new(place_order.base_atoms()); - let price: QuoteAtomsPerBaseAtom = - QuoteAtomsPerBaseAtom::try_from(place_order.price()).unwrap(); + let price: QuoteAtomsPerBaseAtom = place_order.try_price()?; let order_type: OrderType = place_order.order_type(); let last_valid_slot: u32 = place_order.last_valid_slot(); diff --git a/programs/manifest/src/quantities.rs b/programs/manifest/src/quantities.rs index 411806329..89ac6f040 100644 --- a/programs/manifest/src/quantities.rs +++ b/programs/manifest/src/quantities.rs @@ -3,7 +3,7 @@ use borsh::{BorshDeserialize as Deserialize, BorshSerialize as Serialize}; use bytemuck::{Pod, Zeroable}; use hypertree::trace; use shank::ShankAccount; -use solana_program::program_error::ProgramError; +use solana_program::{msg, program_error::ProgramError}; use std::{ fmt::Display, ops::{Add, AddAssign, Div}, @@ -175,9 +175,43 @@ pub struct QuoteAtomsPerBaseAtom { } const ATOM_LIMIT: u128 = u64::MAX as u128; -const D20: u128 = 100_000_000_000_000_000_000; +const D20: u128 = 10u128.pow(20); const D20F: f64 = D20 as f64; +const DECIMAL_CONSTANTS: [u128; 31] = [ + 10u128.pow(30), + 10u128.pow(29), + 10u128.pow(28), + 10u128.pow(27), + 10u128.pow(26), + 10u128.pow(25), + 10u128.pow(24), + 10u128.pow(23), + 10u128.pow(22), + 10u128.pow(21), + 10u128.pow(20), + 10u128.pow(19), + 10u128.pow(18), + 10u128.pow(17), + 10u128.pow(16), + 10u128.pow(15), + 10u128.pow(14), + 10u128.pow(13), + 10u128.pow(12), + 10u128.pow(11), + 10u128.pow(10), + 10u128.pow(09), + 10u128.pow(08), + 10u128.pow(07), + 10u128.pow(06), + 10u128.pow(05), + 10u128.pow(04), + 10u128.pow(03), + 10u128.pow(02), + 10u128.pow(01), + 10u128.pow(00), +]; + // Prices impl QuoteAtomsPerBaseAtom { pub const ZERO: Self = QuoteAtomsPerBaseAtom { inner: 0 }; @@ -185,12 +219,42 @@ impl QuoteAtomsPerBaseAtom { pub const MIN: Self = QuoteAtomsPerBaseAtom::ZERO; pub const MAX: Self = QuoteAtomsPerBaseAtom { inner: u128::MAX }; + pub fn try_from_mantissa_and_exponent( + mantissa: u32, + exponent: i8, + ) -> Result { + if exponent > 10 { + msg!("invalid exponent {exponent} > 10 would truncate",); + return Err(PriceConversionError(0x0)); + } + if exponent < -20 { + msg!("invalid exponent {exponent} < -20 would truncate",); + return Err(PriceConversionError(0x1)); + } + /* map exponent to array range + 10 -> [0] -> D30 + 0 -> [10] -> D20 + -10 -> [20] -> D10 + -20 -> [30] -> D0 + */ + let offset = -(exponent - 10) as usize; + let inner = DECIMAL_CONSTANTS[offset] + .checked_mul(mantissa as u128) + .ok_or(PriceConversionError(0x2))?; + return Ok(QuoteAtomsPerBaseAtom { inner }); + } + pub fn checked_base_for_quote( self, quote_atoms: QuoteAtoms, round_up: bool, ) -> Result { - let dividend = quote_atoms.inner as u128 * D20; + if self == Self::ZERO { + return Ok(BaseAtoms::new(0)); + } + let dividend = D20 + .checked_mul(quote_atoms.inner as u128) + .ok_or(PriceConversionError(0x4))?; let base_atoms = if round_up { dividend.div_ceil(self.inner) } else { @@ -210,7 +274,10 @@ impl QuoteAtomsPerBaseAtom { base_atoms: BaseAtoms, round_up: bool, ) -> Result { - let product = self.inner * (base_atoms.inner as u128); + let product = self + .inner + .checked_mul(base_atoms.inner as u128) + .ok_or(PriceConversionError(0x8))?; let quote_atoms = if round_up { product.div_ceil(D20) } else { @@ -243,9 +310,12 @@ impl QuoteAtomsPerBaseAtom { if BaseAtoms::ZERO == num_base_atoms { return Ok(self); } - let quote_atoms_matched = self.checked_quote_for_base_(num_base_atoms, !is_bid)?; + let quote_matched_atoms = self.checked_quote_for_base_(num_base_atoms, !is_bid)?; + let quote_matched_d20 = quote_matched_atoms + .checked_mul(D20) + .ok_or(PriceConversionError(0x9))?; // no special case rounding needed because effective price is just a value used to compare for order - let inner = (quote_atoms_matched * D20).div(num_base_atoms.inner as u128); + let inner = quote_matched_d20.div(num_base_atoms.inner as u128); Ok(QuoteAtomsPerBaseAtom { inner }) } } @@ -264,19 +334,33 @@ impl std::fmt::Debug for QuoteAtomsPerBaseAtom { } } +#[derive(Debug)] +pub struct PriceConversionError(u32); + +const PRICE_CONVERSION_ERROR_BASE: u32 = 100; + +impl From for ProgramError { + fn from(value: PriceConversionError) -> Self { + ProgramError::Custom(value.0 + PRICE_CONVERSION_ERROR_BASE) + } +} + impl TryFrom for QuoteAtomsPerBaseAtom { - type Error = &'static str; + type Error = PriceConversionError; fn try_from(value: f64) -> Result { let mantissa = value * D20F; if mantissa.is_infinite() { - return Err("infinite can not be expressed as fixed point decimal"); + msg!("infinite can not be expressed as fixed point decimal"); + return Err(PriceConversionError(0xC)); } if mantissa.is_nan() { - return Err("nan can not be expressed as fixed point decimal"); + msg!("nan can not be expressed as fixed point decimal"); + return Err(PriceConversionError(0xD)); } if mantissa > u128::MAX as f64 { - return Err("price is too large"); + msg!("price is too large"); + return Err(PriceConversionError(0xE)); } Ok(QuoteAtomsPerBaseAtom { inner: mantissa as u128, diff --git a/programs/manifest/tests/cases/batch_update.rs b/programs/manifest/tests/cases/batch_update.rs index 57d2ddbb1..05b245b51 100644 --- a/programs/manifest/tests/cases/batch_update.rs +++ b/programs/manifest/tests/cases/batch_update.rs @@ -19,7 +19,8 @@ async fn batch_update_test() -> anyhow::Result<()> { vec![], vec![PlaceOrderParams::new( 1 * SOL_UNIT_SIZE, - 1.0, + 1, + 0, false, OrderType::Limit, NO_EXPIRATION_LAST_VALID_SLOT, @@ -34,7 +35,8 @@ async fn batch_update_test() -> anyhow::Result<()> { vec![CancelOrderParams::new(0)], vec![PlaceOrderParams::new( 1 * SOL_UNIT_SIZE, - 1.0, + 1, + 0, false, OrderType::Limit, NO_EXPIRATION_LAST_VALID_SLOT, @@ -76,7 +78,8 @@ async fn batch_update_fill_test() -> anyhow::Result<()> { vec![], vec![PlaceOrderParams::new( 1 * SOL_UNIT_SIZE, - 1.0, + 1, + 0, false, OrderType::Limit, NO_EXPIRATION_LAST_VALID_SLOT, @@ -91,7 +94,8 @@ async fn batch_update_fill_test() -> anyhow::Result<()> { vec![CancelOrderParams::new(0)], vec![PlaceOrderParams::new( 1 * SOL_UNIT_SIZE, - 1.0, + 1, + 0, true, OrderType::Limit, NO_EXPIRATION_LAST_VALID_SLOT, diff --git a/programs/manifest/tests/cases/cancel_order.rs b/programs/manifest/tests/cases/cancel_order.rs index c7040004e..c9cad7fa3 100644 --- a/programs/manifest/tests/cases/cancel_order.rs +++ b/programs/manifest/tests/cases/cancel_order.rs @@ -16,7 +16,7 @@ async fn cancel_order_test() -> anyhow::Result<()> { test_fixture.deposit(Token::SOL, SOL_UNIT_SIZE).await?; test_fixture - .place_order(Side::Ask, 1, 1.0, u32::MAX, OrderType::Limit) + .place_order(Side::Ask, 1, 1, 0, u32::MAX, OrderType::Limit) .await?; // First order always has order sequence number of zero. @@ -32,7 +32,7 @@ async fn cancel_order_bid_test() -> anyhow::Result<()> { test_fixture.deposit(Token::USDC, USDC_UNIT_SIZE).await?; test_fixture - .place_order(Side::Bid, 1, 1.0, u32::MAX, OrderType::Limit) + .place_order(Side::Bid, 1, 1, 0, u32::MAX, OrderType::Limit) .await?; // First order always has order sequence number of zero. @@ -57,7 +57,8 @@ async fn cancel_order_fail_other_trader_order_test() -> anyhow::Result<()> { .place_order_for_keypair( Side::Ask, 1, - 1.0, + 1, + 0, u32::MAX, OrderType::Limit, &second_keypair, @@ -76,7 +77,7 @@ async fn cancel_order_sequence_number_not_exist_test() -> anyhow::Result<()> { test_fixture.deposit(Token::SOL, SOL_UNIT_SIZE).await?; test_fixture - .place_order(Side::Ask, 1, 1.0, u32::MAX, OrderType::Limit) + .place_order(Side::Ask, 1, 1, 0, u32::MAX, OrderType::Limit) .await?; // Sequence number does not exist, but it fails open. @@ -94,10 +95,10 @@ async fn cancel_order_multiple_bid_on_orderbook_test() -> anyhow::Result<()> { .await?; test_fixture - .place_order(Side::Bid, 1, 1.0, u32::MAX, OrderType::Limit) + .place_order(Side::Bid, 1, 1, 0, u32::MAX, OrderType::Limit) .await?; test_fixture - .place_order(Side::Bid, 2, 2.0, u32::MAX, OrderType::Limit) + .place_order(Side::Bid, 2, 2, 0, u32::MAX, OrderType::Limit) .await?; // First order always has order sequence number of zero. @@ -113,7 +114,7 @@ async fn cancel_order_with_hint_test() -> anyhow::Result<()> { test_fixture.deposit(Token::SOL, SOL_UNIT_SIZE).await?; test_fixture - .place_order(Side::Ask, 1, 1.0, u32::MAX, OrderType::Limit) + .place_order(Side::Ask, 1, 1, 0, u32::MAX, OrderType::Limit) .await?; // First order always has order sequence number of zero. diff --git a/programs/manifest/tests/cases/global.rs b/programs/manifest/tests/cases/global.rs index f678340b5..ecb3f15b1 100644 --- a/programs/manifest/tests/cases/global.rs +++ b/programs/manifest/tests/cases/global.rs @@ -163,7 +163,8 @@ async fn global_place_order() -> anyhow::Result<()> { vec![], vec![PlaceOrderParams::new( 10, - 1.0, + 1, + 0, true, OrderType::Global, NO_EXPIRATION_LAST_VALID_SLOT, @@ -209,7 +210,8 @@ async fn global_place_order_only_global_quote() -> anyhow::Result<()> { vec![], vec![PlaceOrderParams::new( 10, - 1.0, + 1, + 0, true, OrderType::Global, NO_EXPIRATION_LAST_VALID_SLOT, @@ -265,7 +267,8 @@ async fn global_cancel_order() -> anyhow::Result<()> { vec![], vec![PlaceOrderParams::new( 10, - 1.0, + 1, + 0, true, OrderType::Global, NO_EXPIRATION_LAST_VALID_SLOT, @@ -318,7 +321,8 @@ async fn global_match_order() -> anyhow::Result<()> { vec![], vec![PlaceOrderParams::new( 100, - 1.0, + 1, + 0, true, OrderType::Global, NO_EXPIRATION_LAST_VALID_SLOT, @@ -335,7 +339,8 @@ async fn global_match_order() -> anyhow::Result<()> { vec![], vec![PlaceOrderParams::new( 100, - 0.9, + 9, + -1, false, OrderType::Limit, NO_EXPIRATION_LAST_VALID_SLOT, @@ -509,7 +514,8 @@ async fn global_match_22() -> anyhow::Result<()> { vec![], vec![PlaceOrderParams::new( 1_000_000, - 1.0, + 1, + 0, true, OrderType::Global, NO_EXPIRATION_LAST_VALID_SLOT, diff --git a/programs/manifest/tests/cases/place_order.rs b/programs/manifest/tests/cases/place_order.rs index be66d3504..b5226d4a6 100644 --- a/programs/manifest/tests/cases/place_order.rs +++ b/programs/manifest/tests/cases/place_order.rs @@ -24,7 +24,8 @@ async fn place_order_test() -> anyhow::Result<()> { .place_order( Side::Ask, 1, - 1.0, + 1, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, ) @@ -40,7 +41,8 @@ async fn place_order_fail_no_seat_test() -> anyhow::Result<()> { .place_order( Side::Ask, 1, - 1.0, + 1, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit ) @@ -58,7 +60,8 @@ async fn place_order_fail_no_deposit_yet_test() -> anyhow::Result<()> { .place_order( Side::Ask, 1, - 1.0, + 1, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit ) @@ -77,7 +80,8 @@ async fn place_order_fail_insufficient_funds_test() -> anyhow::Result<()> { .place_order( Side::Ask, 2 * SOL_UNIT_SIZE, - 1.0, + 1, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit ) @@ -97,7 +101,8 @@ async fn place_order_not_expand_if_not_needed_test() -> anyhow::Result<()> { .place_order( Side::Ask, 1, - 1.0, + 1, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, ) @@ -107,7 +112,8 @@ async fn place_order_not_expand_if_not_needed_test() -> anyhow::Result<()> { .place_order( Side::Ask, 1, - 1.0, + 1, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, ) @@ -135,7 +141,8 @@ async fn match_limit_orders_basic_test() -> anyhow::Result<()> { .place_order( Side::Ask, 2 * SOL_UNIT_SIZE, - 1.0, + 1, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, ) @@ -151,7 +158,8 @@ async fn match_limit_orders_basic_test() -> anyhow::Result<()> { .place_order_for_keypair( Side::Bid, 1 * SOL_UNIT_SIZE, - 1.0, + 1, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, &second_keypair, @@ -179,7 +187,8 @@ async fn match_limit_orders_basic_test() -> anyhow::Result<()> { .place_order_for_keypair( Side::Bid, 1 * SOL_UNIT_SIZE, - 1.0, + 1, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, &second_keypair, @@ -209,7 +218,8 @@ async fn match_limit_orders_basic_test_reverse() -> anyhow::Result<()> { .place_order( Side::Bid, 2 * SOL_UNIT_SIZE, - 1.0, + 1, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, ) @@ -224,7 +234,8 @@ async fn match_limit_orders_basic_test_reverse() -> anyhow::Result<()> { .place_order_for_keypair( Side::Ask, 1 * SOL_UNIT_SIZE, - 1.0, + 1, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, &second_keypair, @@ -249,7 +260,8 @@ async fn match_limit_orders_basic_test_reverse() -> anyhow::Result<()> { .place_order_for_keypair( Side::Ask, 1 * SOL_UNIT_SIZE, - 1.0, + 1, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, &second_keypair, @@ -275,7 +287,8 @@ async fn match_limit_orders_more_than_resting_test() -> anyhow::Result<()> { .place_order( Side::Ask, 1 * SOL_UNIT_SIZE, - 1.0, + 1, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, ) @@ -291,7 +304,8 @@ async fn match_limit_orders_more_than_resting_test() -> anyhow::Result<()> { .place_order_for_keypair( Side::Bid, 2 * SOL_UNIT_SIZE, - 1.0, + 1, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, &second_keypair, @@ -324,20 +338,22 @@ async fn match_limit_orders_fail_expired_test() -> anyhow::Result<()> { .place_order( Side::Ask, 1 * SOL_UNIT_SIZE, - 1.0, + 1, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, ) .await?; // Slots may advance during tests, so expiration is set pretty far out. test_fixture - .place_order(Side::Ask, 1 * SOL_UNIT_SIZE, 2.0, 1_000, OrderType::Limit) + .place_order(Side::Ask, 1 * SOL_UNIT_SIZE, 2, 0, 1_000, OrderType::Limit) .await?; test_fixture .place_order( Side::Ask, 1 * SOL_UNIT_SIZE, - 3.0, + 3, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, ) @@ -346,7 +362,8 @@ async fn match_limit_orders_fail_expired_test() -> anyhow::Result<()> { .place_order( Side::Ask, 1 * SOL_UNIT_SIZE, - 4.0, + 4, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, ) @@ -360,7 +377,8 @@ async fn match_limit_orders_fail_expired_test() -> anyhow::Result<()> { .place_order_for_keypair( Side::Bid, 4 * SOL_UNIT_SIZE, - 4.0, + 4, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, &second_keypair, @@ -397,7 +415,8 @@ async fn match_limit_orders_partial_match_price_test() -> anyhow::Result<()> { .place_order( Side::Ask, 1 * SOL_UNIT_SIZE, - 1.0, + 1, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, ) @@ -406,7 +425,8 @@ async fn match_limit_orders_partial_match_price_test() -> anyhow::Result<()> { .place_order( Side::Ask, 1 * SOL_UNIT_SIZE, - 2.0, + 2, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, ) @@ -415,7 +435,8 @@ async fn match_limit_orders_partial_match_price_test() -> anyhow::Result<()> { .place_order( Side::Ask, 1 * SOL_UNIT_SIZE, - 3.0, + 3, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, ) @@ -430,7 +451,8 @@ async fn match_limit_orders_partial_match_price_test() -> anyhow::Result<()> { .place_order_for_keypair( Side::Bid, 3 * SOL_UNIT_SIZE, - 2.0, + 2, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, &second_keypair, @@ -475,7 +497,8 @@ async fn match_limit_orders_with_large_deposits_test() -> anyhow::Result<()> { .place_order( Side::Bid, 1_000_000_000, - 1.0, + 1, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, ) @@ -493,7 +516,8 @@ async fn match_limit_orders_with_large_deposits_test() -> anyhow::Result<()> { .place_order_for_keypair( Side::Ask, 500_000_000, - 1.0, + 1, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, &second_keypair, @@ -594,7 +618,8 @@ async fn post_only_basic_test() -> anyhow::Result<()> { .place_order( Side::Ask, 2 * SOL_UNIT_SIZE, - 10.0, + 10, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, ) @@ -611,7 +636,8 @@ async fn post_only_basic_test() -> anyhow::Result<()> { .place_order_for_keypair( Side::Bid, 1 * SOL_UNIT_SIZE, - 1.0, + 1, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::PostOnly, &second_keypair, @@ -638,7 +664,8 @@ async fn post_only_fail_test() -> anyhow::Result<()> { .place_order( Side::Ask, 2 * SOL_UNIT_SIZE, - 10.0, + 10, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, ) @@ -655,7 +682,8 @@ async fn post_only_fail_test() -> anyhow::Result<()> { .place_order_for_keypair( Side::Bid, 1 * SOL_UNIT_SIZE, - 11.0, + 11, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::PostOnly, &second_keypair @@ -684,7 +712,8 @@ async fn post_only_slide_test() -> anyhow::Result<()> { .place_order( Side::Ask, 2 * SOL_UNIT_SIZE, - 10.0, + 10, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, ) @@ -701,7 +730,8 @@ async fn post_only_slide_test() -> anyhow::Result<()> { .place_order_for_keypair( Side::Bid, 1 * SOL_UNIT_SIZE, - 11.0, + 11, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::PostOnlySlide, &second_keypair, @@ -737,7 +767,8 @@ async fn post_only_slide_ask_test() -> anyhow::Result<()> { .place_order( Side::Bid, 1 * SOL_UNIT_SIZE, - 10.0, + 10, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, ) @@ -754,7 +785,8 @@ async fn post_only_slide_ask_test() -> anyhow::Result<()> { .place_order_for_keypair( Side::Ask, 1 * SOL_UNIT_SIZE, - 9.0, + 9, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::PostOnlySlide, &second_keypair, @@ -766,7 +798,8 @@ async fn post_only_slide_ask_test() -> anyhow::Result<()> { .place_order( Side::Bid, 1 * SOL_UNIT_SIZE, - 12.0, + 12, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, ) @@ -793,7 +826,7 @@ async fn place_order_already_expired_test() -> anyhow::Result<()> { test_fixture.advance_time_seconds(10).await; assert!(test_fixture - .place_order(Side::Ask, 1, 1.0, 1, OrderType::Limit,) + .place_order(Side::Ask, 1, 1, 0, 1, OrderType::Limit,) .await .is_err()); diff --git a/programs/manifest/tests/cases/swap.rs b/programs/manifest/tests/cases/swap.rs index 4c4b84703..314770423 100644 --- a/programs/manifest/tests/cases/swap.rs +++ b/programs/manifest/tests/cases/swap.rs @@ -46,7 +46,8 @@ async fn swap_full_match_test_sell_exact_in() -> anyhow::Result<()> { .place_order_for_keypair( Side::Bid, 1 * SOL_UNIT_SIZE, - 1.0, + 1, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, &second_keypair, @@ -57,7 +58,8 @@ async fn swap_full_match_test_sell_exact_in() -> anyhow::Result<()> { .place_order_for_keypair( Side::Bid, 1 * SOL_UNIT_SIZE, - 1.0, + 1, + 0, 10, OrderType::Limit, &second_keypair, @@ -68,7 +70,8 @@ async fn swap_full_match_test_sell_exact_in() -> anyhow::Result<()> { .place_order_for_keypair( Side::Bid, 2 * SOL_UNIT_SIZE, - 0.5, + 5, + -1, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, &second_keypair, @@ -121,7 +124,8 @@ async fn swap_full_match_test_sell_exact_out() -> anyhow::Result<()> { .place_order_for_keypair( Side::Bid, 1 * SOL_UNIT_SIZE, - 1.0, + 1, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, &second_keypair, @@ -132,7 +136,8 @@ async fn swap_full_match_test_sell_exact_out() -> anyhow::Result<()> { .place_order_for_keypair( Side::Bid, 1 * SOL_UNIT_SIZE, - 1.0, + 1, + 0, 10, OrderType::Limit, &second_keypair, @@ -143,7 +148,8 @@ async fn swap_full_match_test_sell_exact_out() -> anyhow::Result<()> { .place_order_for_keypair( Side::Bid, 2 * SOL_UNIT_SIZE, - 0.5, + 5, + -1, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, &second_keypair, @@ -196,7 +202,8 @@ async fn swap_full_match_test_buy_exact_in() -> anyhow::Result<()> { .place_order_for_keypair( Side::Ask, 1 * SOL_UNIT_SIZE, - 1.0, + 1, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, &second_keypair, @@ -207,7 +214,8 @@ async fn swap_full_match_test_buy_exact_in() -> anyhow::Result<()> { .place_order_for_keypair( Side::Ask, 1 * SOL_UNIT_SIZE, - 1.0, + 1, + 0, 10, OrderType::Limit, &second_keypair, @@ -218,7 +226,8 @@ async fn swap_full_match_test_buy_exact_in() -> anyhow::Result<()> { .place_order_for_keypair( Side::Ask, 1 * SOL_UNIT_SIZE, - 2.0, + 2, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, &second_keypair, @@ -275,7 +284,8 @@ async fn swap_already_has_deposits() -> anyhow::Result<()> { .place_order_for_keypair( Side::Ask, 1 * SOL_UNIT_SIZE, - 1.0, + 1, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, &second_keypair, @@ -595,7 +605,8 @@ async fn swap_fail_insufficient_funds_sell() -> anyhow::Result<()> { .place_order_for_keypair( Side::Bid, 2 * SOL_UNIT_SIZE, - 1.0, + 1, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, &second_keypair, @@ -650,7 +661,8 @@ async fn swap_fail_insufficient_funds_buy() -> anyhow::Result<()> { .place_order_for_keypair( Side::Ask, 2_000 * USDC_UNIT_SIZE, - 1.0, + 1, + 0, NO_EXPIRATION_LAST_VALID_SLOT, OrderType::Limit, &second_keypair, @@ -758,7 +770,8 @@ async fn swap_global() -> anyhow::Result<()> { vec![], vec![PlaceOrderParams::new( 1 * SOL_UNIT_SIZE, - 1.0, + 1, + 0, true, OrderType::Global, NO_EXPIRATION_LAST_VALID_SLOT, diff --git a/programs/manifest/tests/cases/token22.rs b/programs/manifest/tests/cases/token22.rs index 8fbd72072..282576cfb 100644 --- a/programs/manifest/tests/cases/token22.rs +++ b/programs/manifest/tests/cases/token22.rs @@ -209,14 +209,16 @@ async fn token22_base() -> anyhow::Result<()> { vec![ PlaceOrderParams::new( 1_000, - 0.9, + 9, + -1, true, OrderType::PostOnly, NO_EXPIRATION_LAST_VALID_SLOT, ), PlaceOrderParams::new( 1_000, - 1.1, + 11, + -1, false, OrderType::PostOnly, NO_EXPIRATION_LAST_VALID_SLOT, @@ -471,14 +473,16 @@ async fn token22_quote() -> anyhow::Result<()> { vec![ PlaceOrderParams::new( 1_000, - 0.9, + 9, + -1, true, OrderType::PostOnly, NO_EXPIRATION_LAST_VALID_SLOT, ), PlaceOrderParams::new( 1_000, - 1.1, + 11, + -1, false, OrderType::PostOnly, NO_EXPIRATION_LAST_VALID_SLOT, diff --git a/programs/manifest/tests/program_test/fixtures.rs b/programs/manifest/tests/program_test/fixtures.rs index ed288e352..fc3ba0376 100644 --- a/programs/manifest/tests/program_test/fixtures.rs +++ b/programs/manifest/tests/program_test/fixtures.rs @@ -423,14 +423,16 @@ impl TestFixture { &mut self, side: Side, base_atoms: u64, - price_atoms: f64, + price_mantissa: u32, + price_exponent: i8, last_valid_slot: u32, order_type: OrderType, ) -> anyhow::Result<(), BanksClientError> { self.place_order_for_keypair( side, base_atoms, - price_atoms, + price_mantissa, + price_exponent, last_valid_slot, order_type, &self.payer_keypair(), @@ -443,7 +445,8 @@ impl TestFixture { &mut self, side: Side, base_atoms: u64, - price_atoms: f64, + price_mantissa: u32, + price_exponent: i8, last_valid_slot: u32, order_type: OrderType, keypair: &Keypair, @@ -456,7 +459,8 @@ impl TestFixture { vec![], vec![PlaceOrderParams::new( base_atoms, - price_atoms, + price_mantissa, + price_exponent, is_bid, order_type, last_valid_slot, diff --git a/programs/wrapper/Cargo.toml b/programs/wrapper/Cargo.toml new file mode 100644 index 000000000..79848466d --- /dev/null +++ b/programs/wrapper/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "wrapper" +version = "0.1.0" +edition = "2021" +repository = "https://github.com/CKS-systems/manifest" +authors = ["Britt Cyr "] +description = "Wrapper for Manifest" +license-file = "LICENSE" + +[lib] +crate-type = ["cdylib", "lib"] +name = "wrapper" + +[features] +no-entrypoint = [] +cpi = ["no-entrypoint"] +no-log-ix-name = [] +default = [] +test = [] + +[dependencies] +manifest = { path = "../manifest", features = ["no-entrypoint"] } +hypertree = { path = "../../lib" } +# Cannot use workspace because the generator needs to see a version here +shank = "0.4.2" +spl-token = { workspace = true, features = ["no-entrypoint"] } +solana-program = { workspace = true } +borsh = { workspace = true } +bytemuck = { workspace = true } +num_enum = { workspace = true } +thiserror = { workspace = true } +solana-security-txt = { workspace = true } +static_assertions = { workspace = true } + +[dev-dependencies] +anyhow = { workspace = true } +solana-program-test = { workspace = true } +solana-logger = { workspace = true } +solana-sdk = { workspace = true } +tokio = { workspace = true } diff --git a/programs/wrapper/src/instruction.rs b/programs/wrapper/src/instruction.rs new file mode 100644 index 000000000..54f32202b --- /dev/null +++ b/programs/wrapper/src/instruction.rs @@ -0,0 +1,66 @@ +use num_enum::TryFromPrimitive; +use shank::ShankInstruction; + +/// Instructions available for the Manifest wrapper program +#[repr(u8)] +#[derive(TryFromPrimitive, Debug, Copy, Clone, ShankInstruction, PartialEq, Eq)] +#[rustfmt::skip] +pub enum ManifestWrapperInstruction { + // Create a market is not needed for the wrapper + + /// Create a wrapper for owner. Note that owner and payer are separate for + /// use as a PDA. + #[account(0, writable, signer, name = "owner", desc = "Owner of the Manifest account")] + #[account(1, name = "system_program", desc = "System program")] + #[account(2, writable, signer, name = "payer", desc = "Payer of rent and gas")] + #[account(3, writable, name = "wrapper_state", desc = "Wrapper state")] + CreateWrapper= 0, + + /// Allocate a seat. Also initializes this wrapper state + #[account(0, name = "manifest_program", desc = "Manifest program")] + #[account(1, writable, signer, name = "owner", desc = "Owner of the Manifest account")] + #[account(2, writable, name = "market", desc = "Account holding all market state")] + #[account(3, name = "system_program", desc = "System program")] + #[account(4, writable, signer, name = "payer", desc = "Payer of rent and gas")] + #[account(5, writable, name = "wrapper_state", desc = "Wrapper state")] + ClaimSeat = 1, + + /// Deposit + #[account(0, name = "manifest_program", desc = "Manifest program")] + #[account(1, writable, signer, name = "owner", desc = "Owner of the Manifest account")] + #[account(2, writable, name = "market", desc = "Account holding all market state")] + #[account(3, writable, name = "trader_token_account", desc = "Trader token account")] + #[account(4, writable, name = "vault", desc = "Vault PDA, seeds are [b'vault', market_address, mint_address]")] + #[account(5, name = "token_program", desc = "Token program")] + #[account(6, writable, signer, name = "payer", desc = "Payer of rent and gas")] + #[account(7, writable, name = "wrapper_state", desc = "Wrapper state")] + #[account(8, name = "mint", desc = "Mint, needed for token 22")] + Deposit = 2, + + /// Withdraw + #[account(0, name = "manifest_program", desc = "Manifest program")] + #[account(1, writable, signer, name = "owner", desc = "Owner of the Manifest account")] + #[account(2, writable, name = "market", desc = "Account holding all market state")] + #[account(3, writable, name = "trader_token_account", desc = "Trader token account")] + #[account(4, writable, name = "vault", desc = "Vault PDA, seeds are [b'vault', market_address, mint_address]")] + #[account(5, name = "token_program", desc = "Token program")] + #[account(6, writable, signer, name = "payer", desc = "Payer of rent and gas")] + #[account(7, writable, name = "wrapper_state", desc = "Wrapper state")] + #[account(8, name = "mint", desc = "Mint, needed for token 22")] + Withdraw = 3, + + /// Cancels, then places orders. + #[account(0, name = "manifest_program", desc = "Manifest program")] + #[account(1, writable, signer, name = "owner", desc = "Owner of the Manifest account")] + #[account(2, writable, name = "market", desc = "Account holding all market state")] + #[account(3, name = "system_program", desc = "System program")] + #[account(4, writable, signer, name = "payer", desc = "Payer of rent and gas")] + #[account(5, writable, name = "wrapper_state", desc = "Wrapper state")] + BatchUpdate = 4, +} + +impl ManifestWrapperInstruction { + pub fn to_vec(&self) -> Vec { + vec![*self as u8] + } +} diff --git a/programs/wrapper/src/instruction_builders/batch_update_instruction.rs b/programs/wrapper/src/instruction_builders/batch_update_instruction.rs new file mode 100644 index 000000000..e6d4d1772 --- /dev/null +++ b/programs/wrapper/src/instruction_builders/batch_update_instruction.rs @@ -0,0 +1,42 @@ +use crate::{ + instruction::ManifestWrapperInstruction, + processors::batch_upate::{ + WrapperBatchUpdateParams, WrapperCancelOrderParams, WrapperPlaceOrderParams, + }, +}; +use borsh::BorshSerialize; +use hypertree::DataIndex; +use solana_program::{ + instruction::{AccountMeta, Instruction}, + pubkey::Pubkey, + system_program, +}; + +pub fn batch_update_instruction( + market: &Pubkey, + payer: &Pubkey, + wrapper_state: &Pubkey, + cancels: Vec, + cancel_all: bool, + orders: Vec, + trader_index_hint: Option, +) -> Instruction { + Instruction { + program_id: crate::id(), + accounts: vec![ + AccountMeta::new_readonly(manifest::id(), false), + AccountMeta::new(*payer, true), + AccountMeta::new(*market, false), + AccountMeta::new_readonly(system_program::id(), false), + AccountMeta::new(*payer, true), + AccountMeta::new(*wrapper_state, false), + ], + data: [ + ManifestWrapperInstruction::BatchUpdate.to_vec(), + WrapperBatchUpdateParams::new(cancels, cancel_all, orders, trader_index_hint) + .try_to_vec() + .unwrap(), + ] + .concat(), + } +} diff --git a/programs/wrapper/src/instruction_builders/claim_seat_instruction.rs b/programs/wrapper/src/instruction_builders/claim_seat_instruction.rs new file mode 100644 index 000000000..7dc1883ed --- /dev/null +++ b/programs/wrapper/src/instruction_builders/claim_seat_instruction.rs @@ -0,0 +1,26 @@ +use crate::ManifestWrapperInstruction; +use solana_program::{ + instruction::{AccountMeta, Instruction}, + pubkey::Pubkey, + system_program, +}; + +pub fn claim_seat_instruction( + market: &Pubkey, + payer: &Pubkey, + owner: &Pubkey, + wrapper_state: &Pubkey, +) -> Instruction { + Instruction { + program_id: crate::id(), + accounts: vec![ + AccountMeta::new_readonly(manifest::id(), false), + AccountMeta::new(*owner, true), + AccountMeta::new(*market, false), + AccountMeta::new_readonly(system_program::id(), false), + AccountMeta::new(*payer, true), + AccountMeta::new(*wrapper_state, false), + ], + data: [ManifestWrapperInstruction::ClaimSeat.to_vec()].concat(), + } +} diff --git a/programs/wrapper/src/instruction_builders/create_wrapper_instruction.rs b/programs/wrapper/src/instruction_builders/create_wrapper_instruction.rs new file mode 100644 index 000000000..2f8d49d6b --- /dev/null +++ b/programs/wrapper/src/instruction_builders/create_wrapper_instruction.rs @@ -0,0 +1,42 @@ +use crate::{wrapper_state::ManifestWrapperStateFixed, ManifestWrapperInstruction}; +use solana_program::{ + instruction::{AccountMeta, Instruction}, + pubkey::Pubkey, + system_instruction, system_program, + sysvar::{rent::Rent, slot_history::ProgramError}, +}; + +pub fn create_wrapper_instructions( + payer: &Pubkey, + owner: &Pubkey, + wrapper_state: &Pubkey, +) -> Result, ProgramError> { + let space: usize = std::mem::size_of::(); + Ok(vec![ + system_instruction::create_account( + payer, + wrapper_state, + Rent::default().minimum_balance(space), + space as u64, + &crate::id(), + ), + create_wrapper_instruction(payer, owner, wrapper_state), + ]) +} + +fn create_wrapper_instruction( + payer: &Pubkey, + owner: &Pubkey, + wrapper_state: &Pubkey, +) -> Instruction { + Instruction { + program_id: crate::id(), + accounts: vec![ + AccountMeta::new(*owner, true), + AccountMeta::new_readonly(system_program::id(), false), + AccountMeta::new(*payer, true), + AccountMeta::new(*wrapper_state, true), + ], + data: [ManifestWrapperInstruction::CreateWrapper.to_vec()].concat(), + } +} diff --git a/programs/wrapper/src/instruction_builders/deposit_instruction.rs b/programs/wrapper/src/instruction_builders/deposit_instruction.rs new file mode 100644 index 000000000..093a48c76 --- /dev/null +++ b/programs/wrapper/src/instruction_builders/deposit_instruction.rs @@ -0,0 +1,38 @@ +use crate::ManifestWrapperInstruction; +use borsh::BorshSerialize; +use manifest::{program::deposit::DepositParams, validation::get_vault_address}; +use solana_program::{ + instruction::{AccountMeta, Instruction}, + pubkey::Pubkey, +}; + +pub fn deposit_instruction( + market: &Pubkey, + payer: &Pubkey, + mint: &Pubkey, + amount_atoms: u64, + trader_token_account: &Pubkey, + wrapper_state: &Pubkey, + token_program: Pubkey, +) -> Instruction { + let (vault_address, _) = get_vault_address(market, mint); + Instruction { + program_id: crate::id(), + accounts: vec![ + AccountMeta::new_readonly(manifest::id(), false), + AccountMeta::new(*payer, true), + AccountMeta::new(*market, false), + AccountMeta::new(*trader_token_account, false), + AccountMeta::new(vault_address, false), + AccountMeta::new(token_program, false), + AccountMeta::new(*payer, true), + AccountMeta::new(*wrapper_state, false), + AccountMeta::new(*mint, false), + ], + data: [ + ManifestWrapperInstruction::Deposit.to_vec(), + DepositParams::new(amount_atoms).try_to_vec().unwrap(), + ] + .concat(), + } +} diff --git a/programs/wrapper/src/instruction_builders/mod.rs b/programs/wrapper/src/instruction_builders/mod.rs new file mode 100644 index 000000000..6c60d8e9a --- /dev/null +++ b/programs/wrapper/src/instruction_builders/mod.rs @@ -0,0 +1,11 @@ +pub mod batch_update_instruction; +pub mod claim_seat_instruction; +pub mod create_wrapper_instruction; +pub mod deposit_instruction; +pub mod withdraw_instruction; + +pub use batch_update_instruction::*; +pub use claim_seat_instruction::*; +pub use create_wrapper_instruction::*; +pub use deposit_instruction::*; +pub use withdraw_instruction::*; diff --git a/programs/wrapper/src/instruction_builders/withdraw_instruction.rs b/programs/wrapper/src/instruction_builders/withdraw_instruction.rs new file mode 100644 index 000000000..7305e69ca --- /dev/null +++ b/programs/wrapper/src/instruction_builders/withdraw_instruction.rs @@ -0,0 +1,38 @@ +use crate::ManifestWrapperInstruction; +use borsh::BorshSerialize; +use manifest::{program::withdraw::WithdrawParams, validation::get_vault_address}; +use solana_program::{ + instruction::{AccountMeta, Instruction}, + pubkey::Pubkey, +}; + +pub fn withdraw_instruction( + market: &Pubkey, + payer: &Pubkey, + mint: &Pubkey, + amount_atoms: u64, + trader_token_account: &Pubkey, + wrapper_state: &Pubkey, + token_program: Pubkey, +) -> Instruction { + let (vault_address, _) = get_vault_address(market, mint); + Instruction { + program_id: crate::id(), + accounts: vec![ + AccountMeta::new_readonly(manifest::id(), false), + AccountMeta::new(*payer, true), + AccountMeta::new(*market, false), + AccountMeta::new(*trader_token_account, false), + AccountMeta::new(vault_address, false), + AccountMeta::new(token_program, false), + AccountMeta::new(*payer, true), + AccountMeta::new(*wrapper_state, false), + AccountMeta::new(*mint, false), + ], + data: [ + ManifestWrapperInstruction::Withdraw.to_vec(), + WithdrawParams::new(amount_atoms).try_to_vec().unwrap(), + ] + .concat(), + } +} diff --git a/programs/wrapper/src/lib.rs b/programs/wrapper/src/lib.rs new file mode 100644 index 000000000..82fa806b1 --- /dev/null +++ b/programs/wrapper/src/lib.rs @@ -0,0 +1,74 @@ +//! Wrapper program for Manifest +//! + +pub mod instruction; +pub mod instruction_builders; +pub mod market_info; +pub mod open_order; +pub mod processors; +pub mod wrapper_state; + +use instruction::ManifestWrapperInstruction; +use processors::{ + batch_upate::process_batch_update, claim_seat::process_claim_seat, + create_wrapper::process_create_wrapper, deposit::process_deposit, withdraw::process_withdraw, +}; +use solana_program::{ + account_info::AccountInfo, declare_id, entrypoint::ProgramResult, program_error::ProgramError, + pubkey::Pubkey, +}; + +#[cfg(not(feature = "no-entrypoint"))] +use solana_security_txt::security_txt; + +#[cfg(not(feature = "no-entrypoint"))] +security_txt! { + name: "manifest-wrapper", + project_url: "", + contacts: "email:britt@cks.systems", + policy: "", + preferred_languages: "en", + source_code: "https://github.com/CKS-Systems/manifest", + auditors: "" +} + +declare_id!("wMNFSTkir3HgyZTsB7uqu3i7FA73grFCptPXgrZjksL"); + +#[cfg(not(feature = "no-entrypoint"))] +solana_program::entrypoint!(process_instruction); + +pub fn process_instruction( + program_id: &Pubkey, + accounts: &[AccountInfo], + instruction_data: &[u8], +) -> ProgramResult { + let (tag, data) = instruction_data + .split_first() + .ok_or(ProgramError::InvalidInstructionData)?; + + let instruction: ManifestWrapperInstruction = + ManifestWrapperInstruction::try_from(*tag).or(Err(ProgramError::InvalidInstructionData))?; + + #[cfg(not(feature = "no-log-ix-name"))] + solana_program::msg!("Instruction: {:?}", instruction); + + match instruction { + ManifestWrapperInstruction::CreateWrapper => { + process_create_wrapper(program_id, accounts, data)?; + } + ManifestWrapperInstruction::ClaimSeat => { + process_claim_seat(program_id, accounts, data)?; + } + ManifestWrapperInstruction::Deposit => { + process_deposit(program_id, accounts, data)?; + } + ManifestWrapperInstruction::Withdraw => { + process_withdraw(program_id, accounts, data)?; + } + ManifestWrapperInstruction::BatchUpdate => { + process_batch_update(program_id, accounts, data)?; + } + } + + Ok(()) +} diff --git a/programs/wrapper/src/market_info.rs b/programs/wrapper/src/market_info.rs new file mode 100644 index 000000000..6698709f3 --- /dev/null +++ b/programs/wrapper/src/market_info.rs @@ -0,0 +1,98 @@ +use bytemuck::{Pod, Zeroable}; +use manifest::quantities::{BaseAtoms, QuoteAtoms}; +use hypertree::{DataIndex, NIL}; +use solana_program::pubkey::Pubkey; +use static_assertions::const_assert_eq; +use std::{cmp::Ordering, mem::size_of}; + +use crate::processors::shared::WRAPPER_BLOCK_PAYLOAD_SIZE; + +#[repr(C)] +#[derive(Default, Debug, Copy, Clone, Zeroable, Pod)] +pub struct MarketInfo { + /// Pubkey for the market + pub market: Pubkey, + + /// Root in the wrapper account. + pub orders_root_index: DataIndex, + + /// Trader index in the market. + pub trader_index: DataIndex, + + /// Withdrawable base balance on the market. + pub base_balance: BaseAtoms, + /// Withdrawable quote balance on the market. + pub quote_balance: QuoteAtoms, + + /// Last slot that a sync was called on. + pub last_updated_slot: u32, + pub _padding: [u32; 1], +} + +// Blocks on wrapper are bigger than blocks on the market because there is more +// data to store here. + +// 32 + // market +// 4 + // orders_root +// 4 + // trader_index +// 8 + // base_balance +// 8 + // quote_balance +// 4 + // last_updated_slot +// 4 // padding +// = 64 +const_assert_eq!(size_of::(), WRAPPER_BLOCK_PAYLOAD_SIZE); +const_assert_eq!(size_of::() % 16, 0); + +impl MarketInfo { + // Create a new empty market info. + pub fn new_empty(market: Pubkey, trader_index: DataIndex) -> Self { + MarketInfo { + market, + orders_root_index: NIL, + trader_index, + base_balance: BaseAtoms::ZERO, + quote_balance: QuoteAtoms::ZERO, + last_updated_slot: 0, + _padding: [0; 1], + } + } +} + +impl Ord for MarketInfo { + fn cmp(&self, other: &Self) -> Ordering { + (self.market).cmp(&(other.market)) + } +} + +impl PartialOrd for MarketInfo { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl PartialEq for MarketInfo { + fn eq(&self, other: &Self) -> bool { + (self.market) == (other.market) + } +} + +impl Eq for MarketInfo {} + +impl std::fmt::Display for MarketInfo { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", self.market) + } +} + +#[test] +fn test_display() { + let market_info: MarketInfo = MarketInfo::new_empty(Pubkey::default(), 0); + format!("{}", market_info); +} + +#[test] +fn test_cmp() { + let market_info: MarketInfo = MarketInfo::new_empty(Pubkey::new_unique(), 0); + let market_info2: MarketInfo = MarketInfo::new_empty(Pubkey::new_unique(), 1); + assert!(market_info > market_info2 || market_info < market_info2); +} diff --git a/programs/wrapper/src/open_order.rs b/programs/wrapper/src/open_order.rs new file mode 100644 index 000000000..88a79b8b0 --- /dev/null +++ b/programs/wrapper/src/open_order.rs @@ -0,0 +1,193 @@ +use bytemuck::{Pod, Zeroable}; +use manifest::{ + quantities::{BaseAtoms, QuoteAtomsPerBaseAtom, WrapperU64}, + state::{OrderType, NO_EXPIRATION_LAST_VALID_SLOT}, +}; +use hypertree::{DataIndex, PodBool, NIL}; +use static_assertions::const_assert_eq; +use std::{cmp::Ordering, mem::size_of}; + +use crate::processors::shared::WRAPPER_BLOCK_PAYLOAD_SIZE; + +#[repr(C)] +#[derive(Default, Debug, Copy, Clone, Zeroable, Pod)] +pub struct WrapperOpenOrder { + price: QuoteAtomsPerBaseAtom, + client_order_id: u64, + order_sequence_number: u64, + num_base_atoms: BaseAtoms, + market_data_index: DataIndex, + last_valid_slot: u32, + is_bid: PodBool, + order_type: OrderType, + + _padding: [u8; 14], +} + +// Blocks on wrapper are bigger than blocks on the market because there is more +// data to store here. + +// 16 + // price +// 8 + // client_order_id +// 8 + // order_sequence_number +// 8 + // num_base_atoms +// 4 + // market_data_index +// 4 + // last_valid_slot +// 1 + // is_bid +// 1 + // order_type +// 14 // padding +// = 64 +const_assert_eq!(size_of::(), WRAPPER_BLOCK_PAYLOAD_SIZE); +const_assert_eq!(size_of::() % 16, 0); + +impl WrapperOpenOrder { + /// Create a new WrapperOpenOrder. + pub fn new( + client_order_id: u64, + order_sequence_number: u64, + price: QuoteAtomsPerBaseAtom, + num_base_atoms: u64, + last_valid_slot: u32, + market_data_index: DataIndex, + is_bid: bool, + order_type: OrderType, + ) -> Self { + WrapperOpenOrder { + client_order_id, + order_sequence_number, + price, + num_base_atoms: BaseAtoms::new(num_base_atoms), + last_valid_slot, + order_type, + market_data_index, + is_bid: PodBool::from_bool(is_bid), + _padding: [0; 14], + } + } + + /// Create a new empty WrapperOpenOrder. Useful in tests. + pub fn new_empty(client_order_id: u64) -> Self { + WrapperOpenOrder { + client_order_id, + order_sequence_number: 0, + price: QuoteAtomsPerBaseAtom::ONE, + num_base_atoms: BaseAtoms::ZERO, + last_valid_slot: NO_EXPIRATION_LAST_VALID_SLOT, + order_type: OrderType::Limit, + market_data_index: NIL, + is_bid: PodBool::from_bool(true), + _padding: [0; 14], + } + } + + /// is_bid as a boolean. + pub fn get_is_bid(&self) -> bool { + self.is_bid.0 == 1 + } + + /// get the order sequence number from the market. + pub fn get_order_sequence_number(&self) -> u64 { + self.order_sequence_number + } + + /// Get the DataIndex for the order in the core program. + pub fn get_market_data_index(&self) -> DataIndex { + self.market_data_index + } + + /// Get the remaining number of base atoms in the order. + pub fn get_num_base_atoms(&self) -> BaseAtoms { + self.num_base_atoms + } + + /// Get client defined order id for the order. + pub fn get_client_order_id(&self) -> u64 { + self.client_order_id + } + + /// Get price on the order. + pub fn get_price(&self) -> QuoteAtomsPerBaseAtom { + self.price + } + + /// Set whether the order is a bid or not. + pub fn set_is_bid(&mut self, is_bid: bool) { + self.is_bid = PodBool::from_bool(is_bid); + } + + /// Set the price on an order. + pub fn set_price(&mut self, price: QuoteAtomsPerBaseAtom) { + self.price = price; + } + + /// Update the number of remaining base atoms. + pub fn update_remaining(&mut self, num_base_atoms: BaseAtoms) { + self.num_base_atoms = num_base_atoms; + } +} + +impl Ord for WrapperOpenOrder { + fn cmp(&self, other: &Self) -> Ordering { + (self.client_order_id).cmp(&(other.client_order_id)) + } +} + +impl PartialOrd for WrapperOpenOrder { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl PartialEq for WrapperOpenOrder { + fn eq(&self, other: &Self) -> bool { + (self.client_order_id) == (other.client_order_id) + } +} + +impl Eq for WrapperOpenOrder {} + +impl std::fmt::Display for WrapperOpenOrder { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", self.client_order_id) + } +} + +#[test] +fn test_display() { + let open_order: WrapperOpenOrder = WrapperOpenOrder::new( + 0, + 0, + 1.0.try_into().unwrap(), + 0, + 0, + 0, + false, + OrderType::Limit, + ); + format!("{}", open_order); +} + +#[test] +fn test_cmp() { + let open_order: WrapperOpenOrder = WrapperOpenOrder::new( + 0, + 0, + 1.0.try_into().unwrap(), + 0, + 0, + 0, + false, + OrderType::Limit, + ); + let open_order2: WrapperOpenOrder = WrapperOpenOrder::new( + 1, + 0, + 1.0.try_into().unwrap(), + 0, + 0, + 0, + false, + OrderType::Limit, + ); + assert!(open_order2 > open_order); +} diff --git a/programs/wrapper/src/processors/batch_upate.rs b/programs/wrapper/src/processors/batch_upate.rs new file mode 100644 index 000000000..75505d12b --- /dev/null +++ b/programs/wrapper/src/processors/batch_upate.rs @@ -0,0 +1,463 @@ +use std::{ + cell::{Ref, RefMut}, + mem::size_of, +}; + +use borsh::{BorshDeserialize, BorshSerialize}; +use manifest::{ + program::{ + batch_update::{BatchUpdateReturn, CancelOrderParams, PlaceOrderParams}, + batch_update_instruction, get_dynamic_account, + }, + quantities::{BaseAtoms, QuoteAtoms, QuoteAtomsPerBaseAtom, WrapperU64}, + state::{MarketFixed, MarketRef, OrderType}, + validation::{ManifestAccountInfo, Program, Signer}, +}; +use hypertree::{ + get_helper, get_mut_helper, DataIndex, FreeList, RBNode, TreeReadOperations, NIL, +}; +use solana_program::{ + account_info::{next_account_info, AccountInfo}, + entrypoint::ProgramResult, + program::{get_return_data, invoke}, + pubkey::Pubkey, + system_program, +}; + +use crate::{ + market_info::MarketInfo, open_order::WrapperOpenOrder, + processors::shared::OpenOrdersTreeReadOnly, wrapper_state::ManifestWrapperStateFixed, +}; + +use super::shared::{ + check_signer, expand_wrapper_if_needed, get_market_info_index_for_market, + get_wrapper_order_indexes_by_client_order_id, lookup_order_indexes_by_client_order_id, sync, + OpenOrdersTree, UnusedWrapperFreeListPadding, WrapperStateAccountInfo, +}; + +#[derive(BorshDeserialize, BorshSerialize, Clone)] +pub struct WrapperPlaceOrderParams { + client_order_id: u64, + base_atoms: u64, + price_mantissa: u32, + price_exponent: i8, + is_bid: bool, + last_valid_slot: u32, + order_type: OrderType, + min_out_atoms: u64, +} +impl WrapperPlaceOrderParams { + pub fn new( + client_order_id: u64, + base_atoms: u64, + price_mantissa: u32, + price_exponent: i8, + is_bid: bool, + last_valid_slot: u32, + order_type: OrderType, + min_out_atoms: u64, + ) -> Self { + WrapperPlaceOrderParams { + client_order_id, + base_atoms, + price_mantissa, + price_exponent, + is_bid, + last_valid_slot, + order_type, + min_out_atoms, + } + } +} + +#[derive(BorshDeserialize, BorshSerialize, Clone)] +pub struct WrapperCancelOrderParams { + client_order_id: u64, +} +impl WrapperCancelOrderParams { + pub fn new(client_order_id: u64) -> Self { + WrapperCancelOrderParams { client_order_id } + } +} + +#[derive(BorshDeserialize, BorshSerialize)] +pub struct WrapperBatchUpdateParams { + pub cancels: Vec, + pub cancel_all: bool, + pub orders: Vec, + pub trader_index_hint: Option, +} +impl WrapperBatchUpdateParams { + pub fn new( + cancels: Vec, + cancel_all: bool, + orders: Vec, + trader_index_hint: Option, + ) -> Self { + WrapperBatchUpdateParams { + cancels, + cancel_all, + orders, + trader_index_hint, + } + } +} + +pub(crate) fn process_batch_update( + _program_id: &Pubkey, + accounts: &[AccountInfo], + data: &[u8], +) -> ProgramResult { + let account_iter: &mut std::slice::Iter = &mut accounts.iter(); + let manifest_program: Program = + Program::new(next_account_info(account_iter)?, &manifest::id())?; + let owner: Signer = Signer::new(next_account_info(account_iter)?)?; + let market: ManifestAccountInfo = + ManifestAccountInfo::::new(next_account_info(account_iter)?)?; + let system_program: Program = + Program::new(next_account_info(account_iter)?, &system_program::id())?; + let payer: Signer = Signer::new(next_account_info(account_iter)?)?; + let wrapper_state: WrapperStateAccountInfo = + WrapperStateAccountInfo::new(next_account_info(account_iter)?)?; + check_signer(&wrapper_state, owner.key); + let market_info_index: DataIndex = get_market_info_index_for_market(&wrapper_state, market.key); + + // Possibly expand the wrapper. + expand_wrapper_if_needed(&wrapper_state, &payer, &system_program)?; + + // Do an initial sync to get all existing orders and balances fresh. This is + // needed for modifying user orders for insufficient funds. + sync( + &wrapper_state, + market.key, + get_dynamic_account(&market.try_borrow_data().unwrap()), + )?; + + // Cancels are mutable because the user may have mistakenly sent the same + // one multiple times and the wrapper will take the responsibility for + // deduping before forwarding to the core. + let WrapperBatchUpdateParams { + orders, + cancel_all, + mut cancels, + trader_index_hint, + } = WrapperBatchUpdateParams::try_from_slice(data)?; + + let wrapper_data: Ref<&mut [u8]> = wrapper_state.info.try_borrow_data()?; + let (_fixed_data, wrapper_dynamic_data) = + wrapper_data.split_at(size_of::()); + + let market_info: MarketInfo = + *get_helper::>(wrapper_dynamic_data, market_info_index).get_value(); + let mut remaining_base_atoms: BaseAtoms = market_info.base_balance; + let mut remaining_quote_atoms: QuoteAtoms = market_info.quote_balance; + drop(wrapper_data); + + // Delete duplicates in case the user gave multiple cancels for an individual order. + cancels.sort_by( + |a: &WrapperCancelOrderParams, b: &WrapperCancelOrderParams| { + a.client_order_id.cmp(&b.client_order_id) + }, + ); + cancels.dedup_by( + |a: &mut WrapperCancelOrderParams, b: &mut WrapperCancelOrderParams| { + a.client_order_id == b.client_order_id + }, + ); + let mut core_cancels: Vec = cancels + .clone() + .into_iter() + .map(|cancel: WrapperCancelOrderParams| { + let order_indexes_to_remove: Vec = + get_wrapper_order_indexes_by_client_order_id( + &wrapper_state, + market.key, + cancel.client_order_id, + ); + let wrapper_state_info: &AccountInfo = wrapper_state.info; + let mut wrapper_data = wrapper_state_info.try_borrow_mut_data().unwrap(); + let (_fixed_data, wrapper_dynamic_data) = + wrapper_data.split_at_mut(size_of::()); + + order_indexes_to_remove + .into_iter() + .map(|order_index_to_remove: DataIndex| { + if order_index_to_remove != NIL { + let order: &WrapperOpenOrder = get_helper::>( + wrapper_dynamic_data, + order_index_to_remove, + ) + .get_value(); + let core_cancel: CancelOrderParams = CancelOrderParams::new_with_hint( + order.get_order_sequence_number(), + Some(order.get_market_data_index()), + ); + if order.get_is_bid() { + // Note that this uses price instead of effective + // price, so might not be fully accurate. + remaining_quote_atoms += order + .get_price() + .checked_quote_for_base(order.get_num_base_atoms(), false) + .unwrap(); + } else { + remaining_base_atoms += order.get_num_base_atoms(); + }; + return core_cancel; + } else { + // Could not find the order. It has been removed, so skip it. + return CancelOrderParams::new_with_hint(0, Some(NIL)); + } + }) + .collect() + }) + .collect::>>() + .concat() + .into_iter() + .filter(|cancel: &CancelOrderParams| { + cancel.order_index_hint().is_some() && cancel.order_index_hint().unwrap() != NIL + }) + .collect(); + + // If the user wants to cancel all, then ignore the cancel request and just + // do a linear walk across the wrapper orders tree. + if cancel_all { + let wrapper_data: Ref<&mut [u8]> = wrapper_state.info.try_borrow_data()?; + let (_fixed_data, wrapper_dynamic_data) = + wrapper_data.split_at(size_of::()); + + let market_info: MarketInfo = + *get_helper::>(wrapper_dynamic_data, market_info_index).get_value(); + remaining_base_atoms = market_info.base_balance; + remaining_quote_atoms = market_info.quote_balance; + drop(wrapper_data); + + core_cancels = Vec::new(); + let wrapper_state_info: &AccountInfo = wrapper_state.info; + let wrapper_data: Ref<&mut [u8]> = wrapper_state_info.try_borrow_data().unwrap(); + let (_fixed_data, wrapper_dynamic_data) = + wrapper_data.split_at(size_of::()); + + let orders_root_index: DataIndex = + get_helper::>(wrapper_dynamic_data, market_info_index) + .get_value() + .orders_root_index; + let open_orders_tree: OpenOrdersTreeReadOnly = + OpenOrdersTreeReadOnly::new(wrapper_dynamic_data, orders_root_index, NIL); + + for open_order_node in open_orders_tree.iter() { + let open_order: &WrapperOpenOrder = open_order_node.1.get_value(); + core_cancels.push(CancelOrderParams::new_with_hint( + open_order.get_order_sequence_number(), + Some(open_order.get_market_data_index()), + )); + if open_order.get_is_bid() { + remaining_quote_atoms += open_order + .get_price() + .checked_quote_for_base(open_order.get_num_base_atoms(), true) + .unwrap(); + } else { + remaining_base_atoms += open_order.get_num_base_atoms(); + }; + } + } + + let core_orders: Vec = orders + .clone() + .into_iter() + .map(|order: WrapperPlaceOrderParams| { + // Possibly reduce the order due to insufficient funds. This is a + // request from a market maker so that the whole tx doesnt roll back + // if they do not have the funds on the exchange that the orders + // require. + let mut num_base_atoms: u64 = order.base_atoms; + if order.is_bid { + let price = QuoteAtomsPerBaseAtom::try_from_mantissa_and_exponent( + order.price_mantissa, + order.price_exponent, + ) + .unwrap(); + let desired: QuoteAtoms = BaseAtoms::new(order.base_atoms) + .checked_mul(price, false) + .unwrap(); + if desired > remaining_quote_atoms { + num_base_atoms = 0; + } else { + remaining_quote_atoms = remaining_quote_atoms.checked_sub(desired).unwrap(); + } + } else { + let desired: BaseAtoms = BaseAtoms::new(order.base_atoms); + if desired > remaining_base_atoms { + num_base_atoms = 0; + } else { + remaining_base_atoms = remaining_base_atoms.checked_sub(desired).unwrap(); + } + } + let core_place: PlaceOrderParams = PlaceOrderParams::new( + num_base_atoms, + order.price_mantissa, + order.price_exponent, + order.is_bid, + order.order_type, + order.last_valid_slot, + ); + core_place + }) + .filter(|wrapper_place_orders: &PlaceOrderParams| wrapper_place_orders.base_atoms() > 0) + .collect(); + + // Call the batch update CPI + invoke( + &batch_update_instruction( + market.key, + payer.key, + trader_index_hint, + core_cancels.clone(), + core_orders.clone(), + None, + None, + None, + None, + ), + // System program is not needed since already expanded. + &[ + manifest_program.info.clone(), + owner.info.clone(), + market.info.clone(), + system_program.info.clone(), + ], + )?; + + // Process the cancels + let mut wrapper_data: RefMut<&mut [u8]> = wrapper_state.info.try_borrow_mut_data().unwrap(); + let (fixed_data, wrapper_dynamic_data) = + wrapper_data.split_at_mut(size_of::()); + + let market_info: &mut MarketInfo = + get_mut_helper::>(wrapper_dynamic_data, market_info_index) + .get_mut_value(); + let mut orders_root_index: DataIndex = market_info.orders_root_index; + + cancels.into_iter().for_each(|cancel| { + let order_wrapper_indexes: Vec = lookup_order_indexes_by_client_order_id( + cancel.client_order_id, + wrapper_dynamic_data, + orders_root_index, + ); + + order_wrapper_indexes + .into_iter() + .for_each(|order_wrapper_index: DataIndex| { + let mut open_orders_tree: OpenOrdersTree = + OpenOrdersTree::new(wrapper_dynamic_data, orders_root_index, NIL); + open_orders_tree.remove_by_index(order_wrapper_index); + orders_root_index = open_orders_tree.get_root_index(); + if order_wrapper_index != NIL { + // Free the node in wrapper. + let wrapper_fixed: &mut ManifestWrapperStateFixed = + get_mut_helper(fixed_data, 0); + let mut free_list: FreeList = + FreeList::new(wrapper_dynamic_data, wrapper_fixed.free_list_head_index); + free_list.add(order_wrapper_index); + wrapper_fixed.free_list_head_index = free_list.get_head(); + } + }); + }); + if cancel_all { + let mut open_orders_tree: OpenOrdersTree = + OpenOrdersTree::new(wrapper_dynamic_data, orders_root_index, NIL); + let mut to_remove_indices: Vec = Vec::new(); + for open_order in open_orders_tree.iter() { + to_remove_indices.push(open_order.0); + } + for open_order_index in to_remove_indices.iter() { + open_orders_tree.remove_by_index(*open_order_index); + } + orders_root_index = open_orders_tree.get_root_index(); + + let wrapper_fixed: &mut ManifestWrapperStateFixed = get_mut_helper(fixed_data, 0); + let mut free_list: FreeList = + FreeList::new(wrapper_dynamic_data, wrapper_fixed.free_list_head_index); + for open_order_index in to_remove_indices.iter() { + // Free the node in wrapper. + free_list.add(*open_order_index); + } + wrapper_fixed.free_list_head_index = free_list.get_head(); + } + + let market_info: &mut MarketInfo = + get_mut_helper::>(wrapper_dynamic_data, market_info_index) + .get_mut_value(); + market_info.orders_root_index = orders_root_index; + drop(wrapper_data); + + let wrapper_state_info: &AccountInfo = wrapper_state.info; + + let cpi_return_data: Option<(Pubkey, Vec)> = get_return_data(); + let BatchUpdateReturn { + orders: batch_update_orders, + } = BatchUpdateReturn::try_from_slice(&cpi_return_data.unwrap().1[..])?; + for (index, &(order_sequence_number, order_index)) in batch_update_orders.iter().enumerate() { + // Order index is NIL when it did not rest. In that case, do not need to store in wrapper. + if order_index == NIL { + continue; + } + + // Add to client order id tree + let mut wrapper_data: RefMut<&mut [u8]> = wrapper_state_info.try_borrow_mut_data().unwrap(); + let (fixed_data, dynamic_data) = + wrapper_data.split_at_mut(size_of::()); + + let orders_root_index: DataIndex = { + let market_info: &mut MarketInfo = + get_mut_helper::>(dynamic_data, market_info_index) + .get_mut_value(); + market_info.orders_root_index + }; + + let wrapper_fixed: &mut ManifestWrapperStateFixed = get_mut_helper(fixed_data, 0); + let wrapper_new_order_index: DataIndex = { + let mut free_list: FreeList = + FreeList::new(dynamic_data, wrapper_fixed.free_list_head_index); + let new_index: DataIndex = free_list.remove(); + wrapper_fixed.free_list_head_index = free_list.get_head(); + new_index + }; + + let original_order: &WrapperPlaceOrderParams = &orders[index]; + let price = QuoteAtomsPerBaseAtom::try_from_mantissa_and_exponent( + original_order.price_mantissa, + original_order.price_exponent, + )?; + let order: WrapperOpenOrder = WrapperOpenOrder::new( + original_order.client_order_id, + order_sequence_number, + price, + // Base atoms can be wrong, will be fixed in the sync. + original_order.base_atoms, + original_order.last_valid_slot, + order_index, + original_order.is_bid, + original_order.order_type, + ); + + let mut open_orders_tree: OpenOrdersTree = + OpenOrdersTree::new(dynamic_data, orders_root_index, NIL); + open_orders_tree.insert(wrapper_new_order_index, order); + let new_root_index: DataIndex = open_orders_tree.get_root_index(); + let market_info: &mut MarketInfo = + get_mut_helper::>(dynamic_data, market_info_index).get_mut_value(); + market_info.orders_root_index = new_root_index; + + drop(wrapper_data); + expand_wrapper_if_needed(&wrapper_state, &payer, &system_program)?; + } + // TODO: Enforce min_out_atoms + + // Sync to get the balance correct and remove any expired orders. + let market_data: Ref<&mut [u8]> = market.try_borrow_data().unwrap(); + let market_ref: MarketRef = get_dynamic_account(&market_data); + + sync(&wrapper_state, market.key, market_ref)?; + + Ok(()) +} diff --git a/programs/wrapper/src/processors/claim_seat.rs b/programs/wrapper/src/processors/claim_seat.rs new file mode 100644 index 000000000..0ef93280e --- /dev/null +++ b/programs/wrapper/src/processors/claim_seat.rs @@ -0,0 +1,97 @@ +use std::{cell::RefMut, mem::size_of}; + +use manifest::{ + program::{claim_seat_instruction, expand_instruction, get_mut_dynamic_account}, + state::{MarketFixed, MarketRefMut}, + validation::ManifestAccountInfo, +}; +use hypertree::{get_mut_helper, DataIndex, FreeList, TreeReadOperations, NIL}; + +use crate::{market_info::MarketInfo, wrapper_state::ManifestWrapperStateFixed}; +use manifest::validation::{Program, Signer}; +use solana_program::{ + account_info::{next_account_info, AccountInfo}, + entrypoint::ProgramResult, + program::invoke, + pubkey::Pubkey, + system_program, +}; + +use super::shared::{ + check_signer, expand_wrapper_if_needed, MarketInfosTree, UnusedWrapperFreeListPadding, + WrapperStateAccountInfo, +}; + +pub(crate) fn process_claim_seat( + _program_id: &Pubkey, + accounts: &[AccountInfo], + _data: &[u8], +) -> ProgramResult { + let account_iter: &mut std::slice::Iter = &mut accounts.iter(); + let manifest_program: Program = + Program::new(next_account_info(account_iter)?, &manifest::id())?; + let owner: Signer = Signer::new(next_account_info(account_iter)?)?; + let market: ManifestAccountInfo = + ManifestAccountInfo::::new(next_account_info(account_iter)?)?; + let system_program: Program = + Program::new(next_account_info(account_iter)?, &system_program::id())?; + let payer: Signer = Signer::new(next_account_info(account_iter)?)?; + let wrapper_state: WrapperStateAccountInfo = + WrapperStateAccountInfo::new(next_account_info(account_iter)?)?; + check_signer(&wrapper_state, owner.key); + + // Call the Expand CPI + invoke( + &expand_instruction(market.key, payer.key), + &[ + manifest_program.info.clone(), + owner.info.clone(), + market.info.clone(), + system_program.info.clone(), + ], + )?; + + // Call the ClaimSeat CPI + invoke( + &claim_seat_instruction(market.key, owner.key), + &[ + manifest_program.info.clone(), + owner.info.clone(), + market.info.clone(), + system_program.info.clone(), + ], + )?; + + // Insert the seat into the wrapper state + expand_wrapper_if_needed(&wrapper_state, &payer, &system_program)?; + + // Load the market_infos tree and insert a new one + let wrapper_state_info: &AccountInfo = wrapper_state.info; + let mut wrapper_data: RefMut<&mut [u8]> = wrapper_state_info.try_borrow_mut_data().unwrap(); + let (fixed_data, wrapper_dynamic_data) = + wrapper_data.split_at_mut(size_of::()); + let wrapper_fixed: &mut ManifestWrapperStateFixed = get_mut_helper(fixed_data, 0); + + // Get the free block and setup the new MarketInfo there + let market_data: &mut RefMut<&mut [u8]> = &mut market.try_borrow_mut_data()?; + let mut dynamic_account: MarketRefMut = get_mut_dynamic_account(market_data); + let trader_index: DataIndex = dynamic_account.get_trader_index(payer.key); + let market_info: MarketInfo = MarketInfo::new_empty(*market.key, trader_index); + + // Put that market_info at the free list head + let mut free_list: FreeList = + FreeList::new(wrapper_dynamic_data, wrapper_fixed.free_list_head_index); + let free_address: DataIndex = free_list.remove(); + wrapper_fixed.free_list_head_index = free_list.get_head(); + + // Insert into the MarketInfosTree + let mut market_infos_tree: MarketInfosTree = MarketInfosTree::new( + wrapper_dynamic_data, + wrapper_fixed.market_infos_root_index, + NIL, + ); + market_infos_tree.insert(free_address, market_info); + wrapper_fixed.market_infos_root_index = market_infos_tree.get_root_index(); + + Ok(()) +} diff --git a/programs/wrapper/src/processors/create_wrapper.rs b/programs/wrapper/src/processors/create_wrapper.rs new file mode 100644 index 000000000..dbc11e7c4 --- /dev/null +++ b/programs/wrapper/src/processors/create_wrapper.rs @@ -0,0 +1,44 @@ +use manifest::validation::{Program, Signer}; +use hypertree::get_mut_helper; +use solana_program::{ + account_info::{next_account_info, AccountInfo}, + entrypoint::ProgramResult, + pubkey::Pubkey, + system_program, +}; + +use crate::wrapper_state::ManifestWrapperStateFixed; + +use super::shared::{expand_wrapper_if_needed, WrapperStateAccountInfo}; + +pub(crate) fn process_create_wrapper( + _program_id: &Pubkey, + accounts: &[AccountInfo], + _data: &[u8], +) -> ProgramResult { + // Load account infos + let account_iter: &mut std::slice::Iter = &mut accounts.iter(); + let owner: Signer = Signer::new(next_account_info(account_iter)?)?; + let system_program: Program = + Program::new(next_account_info(account_iter)?, &system_program::id())?; + // Payer only here for completeness. Is required just for the gas on this tx. + let payer: Signer = Signer::new(next_account_info(account_iter)?)?; + let wrapper_state: WrapperStateAccountInfo = + WrapperStateAccountInfo::new_init(next_account_info(account_iter)?)?; + + { + // Initialize wrapper state + let empty_market_fixed: ManifestWrapperStateFixed = + ManifestWrapperStateFixed::new_empty(owner.key); + let market_bytes: &mut [u8] = &mut wrapper_state.try_borrow_mut_data()?[..]; + *get_mut_helper::(market_bytes, 0_u32) = empty_market_fixed; + + // Drop the reference to wrapper_state so it can be borrowed in expand + // wrapper if needed. + } + + // Expand wrapper so there is an initial block available. + expand_wrapper_if_needed(&wrapper_state, &payer, &system_program)?; + + Ok(()) +} diff --git a/programs/wrapper/src/processors/deposit.rs b/programs/wrapper/src/processors/deposit.rs new file mode 100644 index 000000000..00a914d1b --- /dev/null +++ b/programs/wrapper/src/processors/deposit.rs @@ -0,0 +1,82 @@ +use std::cell::Ref; + +use borsh::BorshDeserialize; +use manifest::{ + program::{deposit::DepositParams, deposit_instruction, get_dynamic_account}, + state::MarketFixed, + validation::{ManifestAccountInfo, MintAccountInfo, TokenProgram}, +}; + +use manifest::validation::{Program, Signer}; +use solana_program::{ + account_info::{next_account_info, AccountInfo}, + entrypoint::ProgramResult, + program::invoke, + pubkey::Pubkey, +}; + +use super::shared::{check_signer, sync, WrapperStateAccountInfo}; + +pub(crate) fn process_deposit( + _program_id: &Pubkey, + accounts: &[AccountInfo], + data: &[u8], +) -> ProgramResult { + // Load account infos. + let account_iter: &mut std::slice::Iter = &mut accounts.iter(); + let manifest_program: Program = + Program::new(next_account_info(account_iter)?, &manifest::id())?; + let owner: Signer = Signer::new(next_account_info(account_iter)?)?; + let market: ManifestAccountInfo = + ManifestAccountInfo::::new(next_account_info(account_iter)?)?; + let trader_token_account: &AccountInfo = next_account_info(account_iter)?; + let vault: &AccountInfo = next_account_info(account_iter)?; + let token_program: TokenProgram = TokenProgram::new(next_account_info(account_iter)?)?; + let payer: Signer = Signer::new(next_account_info(account_iter)?)?; + let wrapper_state: WrapperStateAccountInfo = + WrapperStateAccountInfo::new(next_account_info(account_iter)?)?; + check_signer(&wrapper_state, owner.key); + let mint_account_info: MintAccountInfo = + MintAccountInfo::new(next_account_info(account_iter)?)?; + + let market_fixed: Ref = market.get_fixed()?; + let base_mint: Pubkey = *market_fixed.get_base_mint(); + let quote_mint: Pubkey = *market_fixed.get_quote_mint(); + let mint: &Pubkey = if &trader_token_account.try_borrow_data()?[0..32] == base_mint.as_ref() { + &base_mint + } else { + "e_mint + }; + drop(market_fixed); + + // Params are a direct pass through. + let DepositParams { amount_atoms } = DepositParams::try_from_slice(data)?; + // Call the deposit CPI + invoke( + &deposit_instruction( + market.key, + payer.key, + mint, + amount_atoms, + trader_token_account.key, + *token_program.key, + ), + &[ + manifest_program.info.clone(), + owner.info.clone(), + market.info.clone(), + trader_token_account.clone(), + vault.clone(), + token_program.info.clone(), + mint_account_info.info.clone(), + ], + )?; + + sync( + &wrapper_state, + market.key, + get_dynamic_account(&market.try_borrow_data().unwrap()), + )?; + + Ok(()) +} diff --git a/programs/wrapper/src/processors/mod.rs b/programs/wrapper/src/processors/mod.rs new file mode 100644 index 000000000..ea8c0bda4 --- /dev/null +++ b/programs/wrapper/src/processors/mod.rs @@ -0,0 +1,6 @@ +pub mod batch_upate; +pub mod claim_seat; +pub mod create_wrapper; +pub mod deposit; +pub mod shared; +pub mod withdraw; diff --git a/programs/wrapper/src/processors/shared.rs b/programs/wrapper/src/processors/shared.rs new file mode 100644 index 000000000..7a66de7fa --- /dev/null +++ b/programs/wrapper/src/processors/shared.rs @@ -0,0 +1,396 @@ +use std::{ + cell::{Ref, RefMut}, + mem::size_of, + ops::Deref, +}; + +use crate::{ + market_info::MarketInfo, open_order::WrapperOpenOrder, wrapper_state::ManifestWrapperStateFixed, +}; +use bytemuck::{Pod, Zeroable}; +use manifest::{ + program::assert_with_msg, + quantities::BaseAtoms, + state::{claimed_seat::ClaimedSeat, MarketRef, RestingOrder}, + validation::{Program, Signer}, +}; +use hypertree::{ + get_helper, get_mut_helper, trace, DataIndex, FreeList, RBNode, RedBlackTree, + RedBlackTreeReadOnly, TreeReadOperations, NIL, +}; +use solana_program::{ + account_info::AccountInfo, + clock::Clock, + entrypoint::ProgramResult, + program::invoke, + program_error::ProgramError, + pubkey::Pubkey, + system_instruction, + sysvar::{rent::Rent, Sysvar}, +}; +use static_assertions::const_assert_eq; + +pub const WRAPPER_BLOCK_PAYLOAD_SIZE: usize = 64; +pub const BLOCK_HEADER_SIZE: usize = 16; +pub const WRAPPER_BLOCK_SIZE: usize = WRAPPER_BLOCK_PAYLOAD_SIZE + BLOCK_HEADER_SIZE; + +#[repr(C, packed)] +#[derive(Default, Copy, Clone, Pod, Zeroable)] +pub struct UnusedWrapperFreeListPadding { + _padding: [u64; 7], + _padding2: [u32; 5], +} +pub const FREE_LIST_HEADER_SIZE: usize = 4; +// Assert that the free list blocks take up the same size as regular blocks. +const_assert_eq!( + size_of::(), + WRAPPER_BLOCK_SIZE - FREE_LIST_HEADER_SIZE +); +// Does not align to 8 bytes but not necessary +// const_assert_eq!(size_of::() % 8, 0); + +pub(crate) fn expand_wrapper_if_needed<'a, 'info>( + wrapper_state_account_info: &WrapperStateAccountInfo<'a, 'info>, + payer: &Signer<'a, 'info>, + system_program: &Program<'a, 'info>, +) -> ProgramResult { + let need_expand: bool = does_need_expand(wrapper_state_account_info); + if !need_expand { + return Ok(()); + } + + { + let wrapper_state: &AccountInfo = wrapper_state_account_info.info; + + let wrapper_data: Ref<&mut [u8]> = wrapper_state.try_borrow_data()?; + let new_size: usize = wrapper_data.len() + WRAPPER_BLOCK_SIZE; + drop(wrapper_data); + + let rent: Rent = Rent::get()?; + let new_minimum_balance: u64 = rent.minimum_balance(new_size); + let lamports_diff: u64 = new_minimum_balance.saturating_sub(wrapper_state.lamports()); + + invoke( + &system_instruction::transfer(payer.key, wrapper_state.key, lamports_diff), + &[ + payer.info.clone(), + wrapper_state.clone(), + system_program.info.clone(), + ], + )?; + + trace!( + "expand_if_needed -> realloc {} {:?}", + new_size, + wrapper_state.key + ); + #[cfg(feature = "fuzz")] + { + invoke( + &system_instruction::allocate(wrapper_state.key, new_size as u64), + &[wrapper_state.clone(), system_program.info.clone()], + )?; + } + #[cfg(not(feature = "fuzz"))] + { + wrapper_state.realloc(new_size, false)?; + } + } + + let wrapper_state_info: &AccountInfo = wrapper_state_account_info.info; + let wrapper_data: &mut [u8] = &mut wrapper_state_info.try_borrow_mut_data().unwrap(); + expand_wrapper(wrapper_data); + + Ok(()) +} + +pub fn expand_wrapper(wrapper_data: &mut [u8]) { + let (fixed_data, dynamic_data) = + wrapper_data.split_at_mut(size_of::()); + + let wrapper_fixed: &mut ManifestWrapperStateFixed = get_mut_helper(fixed_data, 0); + let mut free_list: FreeList = + FreeList::new(dynamic_data, wrapper_fixed.free_list_head_index); + + free_list.add(wrapper_fixed.num_bytes_allocated); + wrapper_fixed.num_bytes_allocated += WRAPPER_BLOCK_SIZE as u32; + wrapper_fixed.free_list_head_index = free_list.get_head(); +} + +fn does_need_expand(wrapper_state: &WrapperStateAccountInfo) -> bool { + let wrapper_data: Ref<&mut [u8]> = wrapper_state.info.try_borrow_data().unwrap(); + let (fixed_data, _dynamic_data) = wrapper_data.split_at(size_of::()); + + let wrapper_fixed: &ManifestWrapperStateFixed = get_helper(fixed_data, 0); + return wrapper_fixed.free_list_head_index == NIL; +} + +pub(crate) fn check_signer(wrapper_state: &WrapperStateAccountInfo, owner_key: &Pubkey) { + let mut wrapper_data: RefMut<&mut [u8]> = wrapper_state.info.try_borrow_mut_data().unwrap(); + let (header_bytes, _wrapper_dynamic_data) = + wrapper_data.split_at_mut(size_of::()); + let header: &ManifestWrapperStateFixed = + get_helper::(header_bytes, 0_u32); + assert_eq!(header.trader, *owner_key); +} + +pub(crate) fn sync( + wrapper_state: &WrapperStateAccountInfo, + market: &Pubkey, + market_ref: MarketRef, +) -> ProgramResult { + let market_info_index: DataIndex = get_market_info_index_for_market(&wrapper_state, market); + + let mut wrapper_data: RefMut<&mut [u8]> = wrapper_state.info.try_borrow_mut_data().unwrap(); + let (fixed_data, wrapper_dynamic_data) = + wrapper_data.split_at_mut(size_of::()); + + let market_info: &mut MarketInfo = + get_mut_helper::>(wrapper_dynamic_data, market_info_index) + .get_mut_value(); + let mut orders_root_index: DataIndex = market_info.orders_root_index; + + if orders_root_index != NIL { + let orders_tree: OpenOrdersTreeReadOnly = + OpenOrdersTreeReadOnly::new(wrapper_dynamic_data, orders_root_index, NIL); + + // Cannot do this in one pass because we need the data borrowed for the + // iterator so cannot also borrow it for updating the nodes. + let mut to_remove_indices: Vec = Vec::new(); + let mut to_update_and_core_indices: Vec<(DataIndex, DataIndex)> = Vec::new(); + for (order_index, order) in orders_tree.iter() { + let expected_sequence_number: u64 = order.get_value().get_order_sequence_number(); + let core_data_index: DataIndex = order.get_value().get_market_data_index(); + // Verifies that it is not just zeroed and happens to match seq num, + // also check that there are base atoms left. + let core_resting_order: &RestingOrder = + get_helper::>(market_ref.dynamic, core_data_index).get_value(); + if core_resting_order.get_sequence_number() != expected_sequence_number + || core_resting_order.get_num_base_atoms() == BaseAtoms::ZERO + { + to_remove_indices.push(order_index); + } else { + to_update_and_core_indices.push((order_index, core_data_index)); + } + } + // Update the amounts if there was partial fills. + for (to_update_index, core_data_index) in to_update_and_core_indices.iter() { + let node: &mut RBNode = + get_mut_helper::>(wrapper_dynamic_data, *to_update_index); + let core_resting_order: &RestingOrder = + get_helper::>(market_ref.dynamic, *core_data_index) + .get_value(); + node.get_mut_value() + .update_remaining(core_resting_order.get_num_base_atoms()); + + // Needed because the way things are added could be off by 1 when + // one of the orders fully matches as it is being placed. We only + // know that the indices are right, not the actual orders there. + node.get_mut_value() + .set_price(core_resting_order.get_price()); + node.get_mut_value() + .set_is_bid(core_resting_order.get_is_bid()); + } + let mut orders_tree: RedBlackTree = + RedBlackTree::::new(wrapper_dynamic_data, orders_root_index, NIL); + for to_remove_index in to_remove_indices.iter() { + orders_tree.remove_by_index(*to_remove_index); + } + orders_root_index = orders_tree.get_root_index(); + + let wrapper_fixed: &mut ManifestWrapperStateFixed = get_mut_helper(fixed_data, 0); + let mut free_list: FreeList = + FreeList::new(wrapper_dynamic_data, wrapper_fixed.free_list_head_index); + for open_order_index in to_remove_indices.iter() { + // Free the node in wrapper. + free_list.add(*open_order_index); + } + wrapper_fixed.free_list_head_index = free_list.get_head(); + } + let market_info: &mut MarketInfo = + get_mut_helper::>(wrapper_dynamic_data, market_info_index) + .get_mut_value(); + market_info.orders_root_index = orders_root_index; + + // Sync balances + let market_info: &mut MarketInfo = + get_mut_helper::>(wrapper_dynamic_data, market_info_index) + .get_mut_value(); + let claimed_seat: &ClaimedSeat = + get_helper::>(market_ref.dynamic, market_info.trader_index).get_value(); + market_info.base_balance = claimed_seat.base_withdrawable_balance; + market_info.quote_balance = claimed_seat.quote_withdrawable_balance; + market_info.last_updated_slot = Clock::get().unwrap().slot as u32; + + Ok(()) +} + +pub(crate) fn get_market_info_index_for_market( + wrapper_state: &WrapperStateAccountInfo, + market: &Pubkey, +) -> DataIndex { + let mut wrapper_data: RefMut<&mut [u8]> = wrapper_state.info.try_borrow_mut_data().unwrap(); + let (fixed_data, wrapper_dynamic_data) = + wrapper_data.split_at_mut(size_of::()); + + let wrapper_fixed: &ManifestWrapperStateFixed = get_helper(fixed_data, 0); + let market_infos_tree: MarketInfosTree = MarketInfosTree::new( + wrapper_dynamic_data, + wrapper_fixed.market_infos_root_index, + NIL, + ); + + // Just need to lookup by market key so the rest doesnt matter. + let market_info_index: DataIndex = + market_infos_tree.lookup_index(&MarketInfo::new_empty(*market, NIL)); + market_info_index +} + +pub(crate) fn get_wrapper_order_indexes_by_client_order_id( + wrapper_state: &WrapperStateAccountInfo, + market_key: &Pubkey, + client_order_id: u64, +) -> Vec { + // Lookup all orders with that client_order_id + let market_info_index: DataIndex = get_market_info_index_for_market(wrapper_state, market_key); + let wrapper_data: Ref<&mut [u8]> = wrapper_state.info.try_borrow_data().unwrap(); + let (_fixed_data, wrapper_dynamic_data) = + wrapper_data.split_at(size_of::()); + + let orders_root_index: DataIndex = + get_helper::>(wrapper_dynamic_data, market_info_index) + .get_value() + .orders_root_index; + + let matching_order_indexes: Vec = lookup_order_indexes_by_client_order_id( + client_order_id, + wrapper_dynamic_data, + orders_root_index, + ); + + matching_order_indexes +} + +pub(crate) fn lookup_order_indexes_by_client_order_id( + client_order_id: u64, + wrapper_dynamic_data: &[u8], + orders_root_index: DataIndex, +) -> Vec { + let open_orders_tree: OpenOrdersTreeReadOnly = + OpenOrdersTreeReadOnly::new(wrapper_dynamic_data, orders_root_index, NIL); + let matching_order_index: DataIndex = + open_orders_tree.lookup_index(&WrapperOpenOrder::new_empty(client_order_id)); + + let mut result: Vec = Vec::new(); + result.push(matching_order_index); + + let mut current_order_index: DataIndex = + open_orders_tree.get_predecessor_index::(matching_order_index); + while current_order_index != NIL { + if get_helper::>(wrapper_dynamic_data, current_order_index) + .get_value() + .get_client_order_id() + != client_order_id + { + break; + } + result.push(current_order_index); + current_order_index = + open_orders_tree.get_predecessor_index::(current_order_index); + } + + let mut current_order_index: DataIndex = + open_orders_tree.get_successor_index::(matching_order_index); + while current_order_index != NIL { + if get_helper::>(wrapper_dynamic_data, current_order_index) + .get_value() + .get_client_order_id() + != client_order_id + { + break; + } + result.push(current_order_index); + current_order_index = + open_orders_tree.get_successor_index::(current_order_index); + } + + // Not necessary but useful in practice. + result.sort_unstable(); + result.dedup(); + + result +} + +/// Validation for wrapper account +#[derive(Clone)] +pub struct WrapperStateAccountInfo<'a, 'info> { + pub(crate) info: &'a AccountInfo<'info>, +} +pub type MarketInfosTree<'a> = RedBlackTree<'a, MarketInfo>; +pub type MarketInfosTreeReadOnly<'a> = RedBlackTreeReadOnly<'a, MarketInfo>; +pub type OpenOrdersTree<'a> = RedBlackTree<'a, WrapperOpenOrder>; +pub type OpenOrdersTreeReadOnly<'a> = RedBlackTreeReadOnly<'a, WrapperOpenOrder>; + +pub const WRAPPER_STATE_DISCRIMINANT: u64 = 1; + +impl<'a, 'info> WrapperStateAccountInfo<'a, 'info> { + #[inline(always)] + fn _new_unchecked( + info: &'a AccountInfo<'info>, + ) -> Result, ProgramError> { + assert_with_msg( + info.owner == &crate::ID, + ProgramError::IllegalOwner, + "Wrapper must be owned by the program", + )?; + Ok(Self { info }) + } + + pub fn new( + info: &'a AccountInfo<'info>, + ) -> Result, ProgramError> { + let wrapper_state: WrapperStateAccountInfo<'a, 'info> = Self::_new_unchecked(info)?; + + let market_bytes: Ref<&mut [u8]> = info.try_borrow_data()?; + let (header_bytes, _) = market_bytes.split_at(size_of::()); + let header: &ManifestWrapperStateFixed = + get_helper::(header_bytes, 0_u32); + + assert_with_msg( + header.discriminant == WRAPPER_STATE_DISCRIMINANT, + ProgramError::InvalidAccountData, + "Invalid wrapper state discriminant", + )?; + + Ok(wrapper_state) + } + + pub fn new_init( + info: &'a AccountInfo<'info>, + ) -> Result, ProgramError> { + let market_bytes: Ref<&mut [u8]> = info.try_borrow_data()?; + let (header_bytes, _) = market_bytes.split_at(size_of::()); + let header: &ManifestWrapperStateFixed = + get_helper::(header_bytes, 0_u32); + assert_with_msg( + info.owner == &crate::ID, + ProgramError::IllegalOwner, + "Market must be owned by the Manifest program", + )?; + // On initialization, the discriminant is not set yet. + assert_with_msg( + header.discriminant == 0, + ProgramError::InvalidAccountData, + "Expected uninitialized market with discriminant 0", + )?; + Ok(Self { info }) + } +} + +impl<'a, 'info> Deref for WrapperStateAccountInfo<'a, 'info> { + type Target = AccountInfo<'info>; + + fn deref(&self) -> &Self::Target { + self.info + } +} diff --git a/programs/wrapper/src/processors/withdraw.rs b/programs/wrapper/src/processors/withdraw.rs new file mode 100644 index 000000000..4e501c4c8 --- /dev/null +++ b/programs/wrapper/src/processors/withdraw.rs @@ -0,0 +1,84 @@ +use std::cell::Ref; + +use borsh::BorshDeserialize; +use manifest::{ + program::{get_dynamic_account, withdraw::WithdrawParams, withdraw_instruction}, + state::MarketFixed, + validation::{ManifestAccountInfo, MintAccountInfo}, +}; + +use manifest::validation::{Program, Signer}; +use solana_program::{ + account_info::{next_account_info, AccountInfo}, + entrypoint::ProgramResult, + program::invoke, + pubkey::Pubkey, +}; + +use super::shared::{check_signer, sync, WrapperStateAccountInfo}; + +pub(crate) fn process_withdraw( + _program_id: &Pubkey, + accounts: &[AccountInfo], + data: &[u8], +) -> ProgramResult { + let account_iter: &mut std::slice::Iter = &mut accounts.iter(); + let manifest_program: Program = + Program::new(next_account_info(account_iter)?, &manifest::id())?; + let owner: Signer = Signer::new(next_account_info(account_iter)?)?; + let market: ManifestAccountInfo = + ManifestAccountInfo::::new(next_account_info(account_iter)?)?; + let trader_token_account: &AccountInfo = next_account_info(account_iter)?; + let vault: &AccountInfo = next_account_info(account_iter)?; + let token_program: Program = Program::new(next_account_info(account_iter)?, &spl_token::id())?; + let payer: Signer = Signer::new(next_account_info(account_iter)?)?; + let wrapper_state: WrapperStateAccountInfo = + WrapperStateAccountInfo::new(next_account_info(account_iter)?)?; + check_signer(&wrapper_state, owner.key); + let mint_account_info: MintAccountInfo = + MintAccountInfo::new(next_account_info(account_iter)?)?; + + let market_fixed: Ref = market.get_fixed()?; + let mint: Pubkey = { + let base_mint: &Pubkey = market_fixed.get_base_mint(); + let quote_mint: &Pubkey = market_fixed.get_quote_mint(); + if &trader_token_account.try_borrow_data()?[0..32] == base_mint.as_ref() { + *base_mint + } else { + *quote_mint + } + }; + drop(market_fixed); + + // Params are a direct pass through. + let WithdrawParams { amount_atoms } = WithdrawParams::try_from_slice(data)?; + // Call the withdraw CPI + invoke( + &withdraw_instruction( + market.key, + payer.key, + &mint, + amount_atoms, + trader_token_account.key, + spl_token::id(), + ), + &[ + manifest_program.info.clone(), + owner.info.clone(), + market.info.clone(), + trader_token_account.clone(), + vault.clone(), + token_program.info.clone(), + mint_account_info.info.clone(), + ], + )?; + + // Sync + sync( + &wrapper_state, + market.key, + get_dynamic_account(&market.try_borrow_data().unwrap()), + )?; + + Ok(()) +} diff --git a/programs/wrapper/src/wrapper_state.rs b/programs/wrapper/src/wrapper_state.rs new file mode 100644 index 000000000..2d2980bd7 --- /dev/null +++ b/programs/wrapper/src/wrapper_state.rs @@ -0,0 +1,51 @@ +use std::mem::size_of; + +use bytemuck::{Pod, Zeroable}; +use hypertree::{DataIndex, NIL}; +use solana_program::pubkey::Pubkey; +use static_assertions::const_assert_eq; + +use crate::processors::shared::WRAPPER_STATE_DISCRIMINANT; + +#[repr(C)] +#[derive(Default, Debug, Copy, Clone, Zeroable)] +pub struct ManifestWrapperStateFixed { + pub discriminant: u64, + + // This is the same signer for the core program. + pub trader: Pubkey, + + pub num_bytes_allocated: u32, + pub free_list_head_index: DataIndex, + + // Market infos is a tree that points to roots of trees. + pub market_infos_root_index: DataIndex, + + pub _padding: [u32; 3], +} +const_assert_eq!( + size_of::(), + 8 + // discriminant + 32 + // trader + 4 + // num_bytes_allocated + 4 + // free_list_head_index + 4 + // market_infos_root_index + 12 // padding +); +pub const WRAPPER_FIXED_SIZE: usize = 64; +const_assert_eq!(size_of::(), WRAPPER_FIXED_SIZE); +const_assert_eq!(size_of::() % 8, 0); +unsafe impl Pod for ManifestWrapperStateFixed {} + +impl ManifestWrapperStateFixed { + pub fn new_empty(trader: &Pubkey) -> ManifestWrapperStateFixed { + ManifestWrapperStateFixed { + discriminant: WRAPPER_STATE_DISCRIMINANT, + trader: *trader, + num_bytes_allocated: 0, + free_list_head_index: NIL, + market_infos_root_index: NIL, + _padding: [0; 3], + } + } +} diff --git a/programs/wrapper/tests/cases/batch_update.rs b/programs/wrapper/tests/cases/batch_update.rs new file mode 100644 index 000000000..84bef0d83 --- /dev/null +++ b/programs/wrapper/tests/cases/batch_update.rs @@ -0,0 +1,533 @@ +use std::{cell::RefMut, mem::size_of, rc::Rc}; + +use manifest::state::{constants::NO_EXPIRATION_LAST_VALID_SLOT, OrderType}; +use hypertree::{get_helper, DataIndex, RBNode, TreeReadOperations, NIL}; +use solana_program_test::{tokio, ProgramTestContext}; +use solana_sdk::{ + account::Account, instruction::Instruction, pubkey::Pubkey, signature::Keypair, signer::Signer, + transaction::Transaction, +}; +use wrapper::{ + instruction_builders::{batch_update_instruction, create_wrapper_instructions}, + market_info::MarketInfo, + processors::{ + batch_upate::{WrapperCancelOrderParams, WrapperPlaceOrderParams}, + shared::MarketInfosTree, + }, + wrapper_state::ManifestWrapperStateFixed, +}; + +use crate::{send_tx_with_retry, TestFixture, Token, SOL_UNIT_SIZE, USDC_UNIT_SIZE}; + +#[tokio::test] +async fn wrapper_batch_update_test() -> anyhow::Result<()> { + let mut test_fixture: TestFixture = TestFixture::new().await; + test_fixture.claim_seat().await?; + test_fixture.deposit(Token::SOL, SOL_UNIT_SIZE).await?; + + let payer: Pubkey = test_fixture.payer(); + let payer_keypair: Keypair = test_fixture.payer_keypair().insecure_clone(); + let mut program_test_context: RefMut = test_fixture.context.borrow_mut(); + + // There is no order 0 for the cancel to get, but it will fail silently and continue on. + let batch_update_ix: Instruction = batch_update_instruction( + &test_fixture.market.key, + &payer, + &test_fixture.wrapper.key, + vec![WrapperCancelOrderParams::new(0)], + false, + vec![WrapperPlaceOrderParams::new( + 0, + 1 * SOL_UNIT_SIZE, + 1, + 0, + false, + NO_EXPIRATION_LAST_VALID_SLOT, + OrderType::Limit, + 0, + )], + None, + ); + let batch_update_tx: Transaction = { + Transaction::new_signed_with_payer( + &[batch_update_ix], + Some(&payer), + &[&payer_keypair], + program_test_context.get_new_latest_blockhash().await?, + ) + }; + + program_test_context + .banks_client + .process_transaction(batch_update_tx) + .await?; + + // Cancel and place, so we have enough funds for the second one. + let batch_update_ix: Instruction = batch_update_instruction( + &test_fixture.market.key, + &payer, + &test_fixture.wrapper.key, + vec![WrapperCancelOrderParams::new(0)], + false, + vec![WrapperPlaceOrderParams::new( + 0, + 1 * SOL_UNIT_SIZE, + 1, + 0, + false, + NO_EXPIRATION_LAST_VALID_SLOT, + OrderType::Limit, + 0, + )], + None, + ); + let batch_update_tx: Transaction = { + Transaction::new_signed_with_payer( + &[batch_update_ix], + Some(&payer), + &[&payer_keypair], + program_test_context.get_new_latest_blockhash().await?, + ) + }; + + program_test_context + .banks_client + .process_transaction(batch_update_tx) + .await?; + + Ok(()) +} + +#[tokio::test] +async fn wrapper_batch_update_reuse_client_order_id_test() -> anyhow::Result<()> { + let mut test_fixture: TestFixture = TestFixture::new().await; + test_fixture.claim_seat().await?; + test_fixture.deposit(Token::SOL, 10 * SOL_UNIT_SIZE).await?; + + let payer: Pubkey = test_fixture.payer(); + let payer_keypair: Keypair = test_fixture.payer_keypair().insecure_clone(); + let mut program_test_context: RefMut = test_fixture.context.borrow_mut(); + + // All the orders have the same client order id. + let batch_update_ix: Instruction = batch_update_instruction( + &test_fixture.market.key, + &payer, + &test_fixture.wrapper.key, + vec![], + false, + vec![ + WrapperPlaceOrderParams::new( + 0, + 1 * SOL_UNIT_SIZE, + 1, + 0, + false, + NO_EXPIRATION_LAST_VALID_SLOT, + OrderType::Limit, + 0, + ), + WrapperPlaceOrderParams::new( + 0, + 1 * SOL_UNIT_SIZE, + 1, + 0, + false, + NO_EXPIRATION_LAST_VALID_SLOT, + OrderType::Limit, + 0, + ), + WrapperPlaceOrderParams::new( + 0, + 1 * SOL_UNIT_SIZE, + 1, + 0, + false, + NO_EXPIRATION_LAST_VALID_SLOT, + OrderType::Limit, + 0, + ), + WrapperPlaceOrderParams::new( + 0, + 1 * SOL_UNIT_SIZE, + 1, + 0, + false, + NO_EXPIRATION_LAST_VALID_SLOT, + OrderType::Limit, + 0, + ), + WrapperPlaceOrderParams::new( + 0, + 1 * SOL_UNIT_SIZE, + 1, + 0, + false, + NO_EXPIRATION_LAST_VALID_SLOT, + OrderType::Limit, + 0, + ), + ], + None, + ); + let batch_update_tx: Transaction = { + Transaction::new_signed_with_payer( + &[batch_update_ix], + Some(&payer), + &[&payer_keypair], + program_test_context.get_new_latest_blockhash().await?, + ) + }; + + program_test_context + .banks_client + .process_transaction(batch_update_tx) + .await?; + + // Cancel order 0 which is all of them + let batch_update_ix: Instruction = batch_update_instruction( + &test_fixture.market.key, + &payer, + &test_fixture.wrapper.key, + vec![WrapperCancelOrderParams::new(0)], + false, + vec![], + None, + ); + let batch_update_tx: Transaction = { + Transaction::new_signed_with_payer( + &[batch_update_ix], + Some(&payer), + &[&payer_keypair], + program_test_context.get_new_latest_blockhash().await?, + ) + }; + + program_test_context + .banks_client + .process_transaction(batch_update_tx) + .await?; + + // Assert that there are no more orders on the book. + let mut wrapper_account: Account = program_test_context + .banks_client + .get_account(test_fixture.wrapper.key) + .await + .expect("Fetch wrapper") + .expect("Wrapper is not none"); + let (fixed_data, wrapper_dynamic_data) = + wrapper_account.data[..].split_at_mut(size_of::()); + + let wrapper_fixed: &ManifestWrapperStateFixed = get_helper(fixed_data, 0); + let market_infos_tree: MarketInfosTree = MarketInfosTree::new( + wrapper_dynamic_data, + wrapper_fixed.market_infos_root_index, + NIL, + ); + + let market_info_index: DataIndex = + market_infos_tree.lookup_index(&MarketInfo::new_empty(test_fixture.market.key, NIL)); + let market_info: &MarketInfo = + get_helper::>(wrapper_dynamic_data, market_info_index).get_value(); + let orders_root_index: DataIndex = market_info.orders_root_index; + assert_eq!( + orders_root_index, NIL, + "Deleted all orders since they all had same client order id" + ); + + Ok(()) +} + +#[tokio::test] +async fn sync_remove_test() -> anyhow::Result<()> { + let mut test_fixture: TestFixture = TestFixture::new().await; + test_fixture.claim_seat().await?; + test_fixture.deposit(Token::SOL, 10 * SOL_UNIT_SIZE).await?; + + let payer: Pubkey = test_fixture.payer(); + let payer_keypair: Keypair = test_fixture.payer_keypair().insecure_clone(); + let second_payer: Pubkey = test_fixture.second_keypair.pubkey(); + let second_payer_keypair: Keypair = test_fixture.second_keypair.insecure_clone(); + let second_wrapper_keypair: Keypair = Keypair::new(); + + let create_wrapper_ixs: Vec = create_wrapper_instructions( + &second_payer, + &second_payer, + &second_wrapper_keypair.pubkey(), + ) + .unwrap(); + + send_tx_with_retry( + Rc::clone(&test_fixture.context), + &create_wrapper_ixs[..], + Some(&second_payer), + &[&second_payer_keypair, &second_wrapper_keypair], + ) + .await; + + test_fixture + .claim_seat_for_keypair_with_wrapper( + &test_fixture.second_keypair.insecure_clone(), + &second_wrapper_keypair.pubkey(), + ) + .await?; + test_fixture + .deposit_for_keypair_with_wrapper( + Token::USDC, + 1_000 * USDC_UNIT_SIZE, + &test_fixture.second_keypair.insecure_clone(), + &second_wrapper_keypair.pubkey(), + ) + .await?; + + let mut program_test_context: RefMut = test_fixture.context.borrow_mut(); + + let batch_update_ix: Instruction = batch_update_instruction( + &test_fixture.market.key, + &payer, + &test_fixture.wrapper.key, + vec![], + false, + vec![WrapperPlaceOrderParams::new( + 0, + 1 * SOL_UNIT_SIZE, + 1, + 0, + false, + NO_EXPIRATION_LAST_VALID_SLOT, + OrderType::Limit, + 0, + )], + None, + ); + let batch_update_tx: Transaction = { + Transaction::new_signed_with_payer( + &[batch_update_ix], + Some(&payer), + &[&payer_keypair], + program_test_context.get_new_latest_blockhash().await?, + ) + }; + + program_test_context + .banks_client + .process_transaction(batch_update_tx) + .await?; + + let batch_update_ix: Instruction = batch_update_instruction( + &test_fixture.market.key, + &second_payer, + &second_wrapper_keypair.pubkey(), + vec![], + false, + vec![WrapperPlaceOrderParams::new( + 0, + 1 * SOL_UNIT_SIZE, + 1, + 0, + true, + NO_EXPIRATION_LAST_VALID_SLOT, + OrderType::Limit, + 0, + )], + None, + ); + let batch_update_tx: Transaction = { + Transaction::new_signed_with_payer( + &[batch_update_ix], + Some(&second_payer), + &[&second_payer_keypair], + program_test_context.get_new_latest_blockhash().await?, + ) + }; + + program_test_context + .banks_client + .process_transaction(batch_update_tx) + .await?; + + let batch_update_ix: Instruction = batch_update_instruction( + &test_fixture.market.key, + &payer, + &test_fixture.wrapper.key, + vec![], + false, + vec![], + None, + ); + let batch_update_tx: Transaction = { + Transaction::new_signed_with_payer( + &[batch_update_ix], + Some(&payer), + &[&payer_keypair], + program_test_context.get_new_latest_blockhash().await?, + ) + }; + + program_test_context + .banks_client + .process_transaction(batch_update_tx) + .await?; + + // Assert that there are no more orders on the book. + let mut wrapper_account: Account = program_test_context + .banks_client + .get_account(test_fixture.wrapper.key) + .await + .expect("Fetch wrapper") + .expect("Wrapper is not none"); + let (fixed_data, wrapper_dynamic_data) = + wrapper_account.data[..].split_at_mut(size_of::()); + + let wrapper_fixed: &ManifestWrapperStateFixed = get_helper(fixed_data, 0); + let market_infos_tree: MarketInfosTree = MarketInfosTree::new( + wrapper_dynamic_data, + wrapper_fixed.market_infos_root_index, + NIL, + ); + + // Just need to lookup by market key so the rest doesnt matter. + let market_info_index: DataIndex = + market_infos_tree.lookup_index(&MarketInfo::new_empty(test_fixture.market.key, NIL)); + + let market_info: &MarketInfo = + get_helper::>(wrapper_dynamic_data, market_info_index).get_value(); + let orders_root_index: DataIndex = market_info.orders_root_index; + assert_eq!(orders_root_index, NIL, "Order matched"); + + Ok(()) +} + +#[tokio::test] +async fn wrapper_batch_update_cancel_all_test() -> anyhow::Result<()> { + let mut test_fixture: TestFixture = TestFixture::new().await; + test_fixture.claim_seat().await?; + test_fixture.deposit(Token::SOL, SOL_UNIT_SIZE).await?; + + let payer: Pubkey = test_fixture.payer(); + let payer_keypair: Keypair = test_fixture.payer_keypair().insecure_clone(); + let mut program_test_context: RefMut = test_fixture.context.borrow_mut(); + + let batch_update_ix: Instruction = batch_update_instruction( + &test_fixture.market.key, + &payer, + &test_fixture.wrapper.key, + vec![], + false, + vec![WrapperPlaceOrderParams::new( + 0, + 1 * SOL_UNIT_SIZE, + 1, + 0, + false, + NO_EXPIRATION_LAST_VALID_SLOT, + OrderType::Limit, + 0, + )], + None, + ); + let batch_update_tx: Transaction = { + Transaction::new_signed_with_payer( + &[batch_update_ix], + Some(&payer), + &[&payer_keypair], + program_test_context.get_new_latest_blockhash().await?, + ) + }; + + program_test_context + .banks_client + .process_transaction(batch_update_tx) + .await?; + + let batch_update_ix: Instruction = batch_update_instruction( + &test_fixture.market.key, + &payer, + &test_fixture.wrapper.key, + vec![], + true, + vec![], + None, + ); + let batch_update_tx: Transaction = { + Transaction::new_signed_with_payer( + &[batch_update_ix], + Some(&payer), + &[&payer_keypair], + program_test_context.get_new_latest_blockhash().await?, + ) + }; + + program_test_context + .banks_client + .process_transaction(batch_update_tx) + .await?; + + // Assert that there are no more orders on the book. + let mut wrapper_account: Account = program_test_context + .banks_client + .get_account(test_fixture.wrapper.key) + .await + .expect("Fetch wrapper") + .expect("Wrapper is not none"); + let (fixed_data, wrapper_dynamic_data) = + wrapper_account.data[..].split_at_mut(size_of::()); + + let wrapper_fixed: &ManifestWrapperStateFixed = get_helper(fixed_data, 0); + let market_infos_tree: MarketInfosTree = MarketInfosTree::new( + wrapper_dynamic_data, + wrapper_fixed.market_infos_root_index, + NIL, + ); + + let market_info_index: DataIndex = + market_infos_tree.lookup_index(&MarketInfo::new_empty(test_fixture.market.key, NIL)); + + let market_info: &MarketInfo = + get_helper::>(wrapper_dynamic_data, market_info_index).get_value(); + let orders_root_index: DataIndex = market_info.orders_root_index; + assert_eq!(orders_root_index, NIL, "Deleted all orders in cancel all"); + + Ok(()) +} + +#[tokio::test] +async fn wrapper_batch_update_trader_index_hint_test() -> anyhow::Result<()> { + let mut test_fixture: TestFixture = TestFixture::new().await; + test_fixture.claim_seat().await?; + test_fixture.deposit(Token::SOL, SOL_UNIT_SIZE).await?; + + let payer: Pubkey = test_fixture.payer(); + let payer_keypair: Keypair = test_fixture.payer_keypair().insecure_clone(); + let mut program_test_context: RefMut = test_fixture.context.borrow_mut(); + + let batch_update_ix: Instruction = batch_update_instruction( + &test_fixture.market.key, + &payer, + &test_fixture.wrapper.key, + vec![WrapperCancelOrderParams::new(0)], + false, + vec![WrapperPlaceOrderParams::new( + 0, + 1 * SOL_UNIT_SIZE, + 1, + 0, + false, + NO_EXPIRATION_LAST_VALID_SLOT, + OrderType::Limit, + 0, + )], + Some(0), + ); + let batch_update_tx: Transaction = Transaction::new_signed_with_payer( + &[batch_update_ix], + Some(&payer), + &[&payer_keypair], + program_test_context.get_new_latest_blockhash().await?, + ); + program_test_context + .banks_client + .process_transaction(batch_update_tx) + .await?; + + Ok(()) +} diff --git a/programs/wrapper/tests/cases/claim_seat.rs b/programs/wrapper/tests/cases/claim_seat.rs new file mode 100644 index 000000000..d4d7a7fed --- /dev/null +++ b/programs/wrapper/tests/cases/claim_seat.rs @@ -0,0 +1,11 @@ +use solana_program_test::tokio; + +use crate::TestFixture; + +#[tokio::test] +async fn claim_seat() -> anyhow::Result<()> { + let test_fixture: TestFixture = TestFixture::new().await; + test_fixture.claim_seat().await?; + + Ok(()) +} diff --git a/programs/wrapper/tests/cases/deposit.rs b/programs/wrapper/tests/cases/deposit.rs new file mode 100644 index 000000000..6f366d24c --- /dev/null +++ b/programs/wrapper/tests/cases/deposit.rs @@ -0,0 +1,12 @@ +use crate::{TestFixture, Token, SOL_UNIT_SIZE}; + +#[tokio::test] +async fn basic_deposit_test() -> anyhow::Result<()> { + let mut test_fixture: TestFixture = TestFixture::new().await; + test_fixture.claim_seat().await?; + + // Deposit also does a mint to user token account. + test_fixture.deposit(Token::SOL, SOL_UNIT_SIZE).await?; + + Ok(()) +} diff --git a/programs/wrapper/tests/cases/mod.rs b/programs/wrapper/tests/cases/mod.rs new file mode 100644 index 000000000..fb4a1a9a5 --- /dev/null +++ b/programs/wrapper/tests/cases/mod.rs @@ -0,0 +1,4 @@ +pub mod batch_update; +pub mod claim_seat; +pub mod deposit; +pub mod withdraw; diff --git a/programs/wrapper/tests/cases/withdraw.rs b/programs/wrapper/tests/cases/withdraw.rs new file mode 100644 index 000000000..5a6bc2760 --- /dev/null +++ b/programs/wrapper/tests/cases/withdraw.rs @@ -0,0 +1,12 @@ +use crate::{TestFixture, Token, SOL_UNIT_SIZE}; + +#[tokio::test] +async fn basic_withdraw_test() -> anyhow::Result<()> { + let mut test_fixture: TestFixture = TestFixture::new().await; + test_fixture.claim_seat().await?; + + test_fixture.deposit(Token::SOL, SOL_UNIT_SIZE).await?; + test_fixture.withdraw(Token::SOL, SOL_UNIT_SIZE).await?; + + Ok(()) +} diff --git a/programs/wrapper/tests/mod.rs b/programs/wrapper/tests/mod.rs new file mode 100644 index 000000000..a5f2d401f --- /dev/null +++ b/programs/wrapper/tests/mod.rs @@ -0,0 +1,5 @@ +pub mod cases; +pub mod program_test; + +pub use cases::*; +pub use program_test::*; diff --git a/programs/wrapper/tests/program_test/fixtures.rs b/programs/wrapper/tests/program_test/fixtures.rs new file mode 100644 index 000000000..888f2099d --- /dev/null +++ b/programs/wrapper/tests/program_test/fixtures.rs @@ -0,0 +1,695 @@ +use std::{ + cell::{Ref, RefCell, RefMut}, + io::Error, +}; + +use manifest::{ + program::{create_market_instructions, get_dynamic_value}, + quantities::WrapperU64, + state::{MarketFixed, MarketValue}, + validation::MintAccountInfo, +}; +use solana_program::{hash::Hash, pubkey::Pubkey, rent::Rent}; +use solana_program_test::{processor, BanksClientError, ProgramTest, ProgramTestContext}; +use solana_sdk::{ + account::Account, account_info::AccountInfo, instruction::Instruction, program_pack::Pack, + signature::Keypair, signer::Signer, system_instruction::create_account, + transaction::Transaction, +}; +use spl_token::state::Mint; +use std::rc::Rc; +use wrapper::instruction_builders::{ + claim_seat_instruction, create_wrapper_instructions, deposit_instruction, withdraw_instruction, +}; + +#[derive(PartialEq)] +pub enum Token { + USDC = 0, + SOL = 1, +} + +#[derive(PartialEq)] +pub enum Side { + Bid = 0, + Ask = 1, +} + +const RUST_LOG_DEFAULT: &str = "solana_rbpf::vm=info,\ + solana_program_runtime::stable_log=debug,\ + solana_runtime::message_processor=debug,\ + solana_runtime::system_instruction_processor=info,\ + solana_program_test=info,\ + solana_bpf_loader_program=debug"; + +// Not lots, just big enough numbers for tests to run. +pub const SOL_UNIT_SIZE: u64 = 1_000_000_000; +pub const USDC_UNIT_SIZE: u64 = 1_000_000; + +pub struct TestFixture { + pub context: Rc>, + pub sol_mint: MintFixture, + pub usdc_mint: MintFixture, + pub payer_sol: TokenAccountFixture, + pub payer_usdc: TokenAccountFixture, + pub market: MarketFixture, + pub wrapper: WrapperFixture, + pub second_keypair: Keypair, +} + +impl TestFixture { + pub async fn new() -> TestFixture { + let mut program: ProgramTest = ProgramTest::new( + "wrapper", + wrapper::ID, + processor!(wrapper::process_instruction), + ); + program.add_program( + "manifest", + manifest::ID, + processor!(manifest::process_instruction), + ); + + let second_keypair: Keypair = Keypair::new(); + program.add_account( + second_keypair.pubkey(), + solana_sdk::account::Account::new( + u32::MAX as u64, + 0, + &solana_sdk::system_program::id(), + ), + ); + + let usdc_keypair: Keypair = Keypair::new(); + let sol_keypair: Keypair = Keypair::new(); + let market_keypair: Keypair = Keypair::new(); + let wrapper_keypair: Keypair = Keypair::new(); + + let context: Rc> = + Rc::new(RefCell::new(program.start_with_context().await)); + solana_logger::setup_with_default(RUST_LOG_DEFAULT); + + let usdc_mint_f: MintFixture = + MintFixture::new(Rc::clone(&context), Some(usdc_keypair), Some(6)).await; + let sol_mint_f: MintFixture = + MintFixture::new(Rc::clone(&context), Some(sol_keypair), Some(9)).await; + + let mut context_cell: RefMut = context.borrow_mut(); + let payer_pubkey: &Pubkey = &context_cell.payer.pubkey(); + let create_market_ixs: Vec = create_market_instructions( + &market_keypair.pubkey(), + &sol_mint_f.key, + &usdc_mint_f.key, + payer_pubkey, + ) + .unwrap(); + + let create_market_tx: Transaction = { + Transaction::new_signed_with_payer( + &create_market_ixs[..], + Some(payer_pubkey), + &[&context_cell.payer.insecure_clone(), &market_keypair], + context_cell.get_new_latest_blockhash().await.unwrap(), + ) + }; + + context_cell + .banks_client + .process_transaction(create_market_tx) + .await + .unwrap(); + + // Now that market is created, we can make a market fixture. + let market_fixture: MarketFixture = + MarketFixture::new(Rc::clone(&context), market_keypair.pubkey()).await; + + let create_wrapper_ixs: Vec = + create_wrapper_instructions(payer_pubkey, payer_pubkey, &wrapper_keypair.pubkey()) + .unwrap(); + + let create_wrapper_tx: Transaction = { + Transaction::new_signed_with_payer( + &create_wrapper_ixs[..], + Some(payer_pubkey), + &[&context_cell.payer.insecure_clone(), &wrapper_keypair], + context_cell.get_new_latest_blockhash().await.unwrap(), + ) + }; + + context_cell + .banks_client + .process_transaction(create_wrapper_tx) + .await + .unwrap(); + let wrapper_fixture: WrapperFixture = + WrapperFixture::new(Rc::clone(&context), wrapper_keypair.pubkey()).await; + + drop(context_cell); + let payer_sol_fixture: TokenAccountFixture = + TokenAccountFixture::new(Rc::clone(&context), &sol_mint_f.key, payer_pubkey).await; + let payer_usdc_fixture = + TokenAccountFixture::new(Rc::clone(&context), &usdc_mint_f.key, payer_pubkey).await; + + TestFixture { + context: Rc::clone(&context), + usdc_mint: usdc_mint_f, + sol_mint: sol_mint_f, + market: market_fixture, + wrapper: wrapper_fixture, + payer_sol: payer_sol_fixture, + payer_usdc: payer_usdc_fixture, + second_keypair, + } + } + + pub async fn try_load( + &self, + address: &Pubkey, + ) -> anyhow::Result, BanksClientError> { + self.context + .borrow_mut() + .banks_client + .get_account(*address) + .await + } + + pub fn payer(&self) -> Pubkey { + self.context.borrow().payer.pubkey() + } + + pub fn payer_keypair(&self) -> Keypair { + self.context.borrow().payer.insecure_clone() + } + + pub async fn claim_seat(&self) -> anyhow::Result<(), BanksClientError> { + self.claim_seat_for_keypair(&self.payer_keypair()).await + } + + pub async fn claim_seat_for_keypair( + &self, + keypair: &Keypair, + ) -> anyhow::Result<(), BanksClientError> { + let wrapper_key: Pubkey = self.wrapper.key; + self.claim_seat_for_keypair_with_wrapper(keypair, &wrapper_key) + .await + } + + pub async fn claim_seat_for_keypair_with_wrapper( + &self, + keypair: &Keypair, + wrapper_state: &Pubkey, + ) -> anyhow::Result<(), BanksClientError> { + let mut program_test_context: RefMut = self.context.borrow_mut(); + let claim_seat_ix: Instruction = claim_seat_instruction( + &self.market.key, + &keypair.pubkey(), + &keypair.pubkey(), + wrapper_state, + ); + let claim_seat_tx: Transaction = { + Transaction::new_signed_with_payer( + &[claim_seat_ix], + Some(&keypair.pubkey()), + &[keypair], + program_test_context.get_new_latest_blockhash().await?, + ) + }; + + program_test_context + .banks_client + .process_transaction(claim_seat_tx) + .await?; + Ok(()) + } + + pub async fn deposit( + &mut self, + token: Token, + amount_atoms: u64, + ) -> anyhow::Result<(), BanksClientError> { + self.deposit_for_keypair(token, amount_atoms, &self.payer_keypair()) + .await?; + Ok(()) + } + + pub async fn deposit_for_keypair( + &mut self, + token: Token, + amount_atoms: u64, + keypair: &Keypair, + ) -> anyhow::Result<(), BanksClientError> { + let wrapper_key: Pubkey = self.wrapper.key; + self.deposit_for_keypair_with_wrapper(token, amount_atoms, keypair, &wrapper_key) + .await + } + + pub async fn deposit_for_keypair_with_wrapper( + &mut self, + token: Token, + amount_atoms: u64, + keypair: &Keypair, + wrapper_state: &Pubkey, + ) -> anyhow::Result<(), BanksClientError> { + let is_base: bool = token == Token::SOL; + let (mint, trader_token_account) = if is_base { + let trader_token_account: Pubkey = if keypair.pubkey() == self.payer() { + self.payer_sol.key + } else { + // Make a temporary token account + let token_account_keypair: Keypair = Keypair::new(); + let token_account_fixture: TokenAccountFixture = + TokenAccountFixture::new_with_keypair( + Rc::clone(&self.context), + &self.sol_mint.key, + &keypair.pubkey(), + &token_account_keypair, + ) + .await; + token_account_fixture.key + }; + self.sol_mint + .mint_to(&trader_token_account, amount_atoms) + .await; + (&self.sol_mint.key, trader_token_account) + } else { + let trader_token_account: Pubkey = if keypair.pubkey() == self.payer() { + self.payer_usdc.key + } else { + // Make a temporary token account + let token_account_keypair: Keypair = Keypair::new(); + let token_account_fixture: TokenAccountFixture = + TokenAccountFixture::new_with_keypair( + Rc::clone(&self.context), + &self.usdc_mint.key, + &keypair.pubkey(), + &token_account_keypair, + ) + .await; + token_account_fixture.key + }; + self.usdc_mint + .mint_to(&trader_token_account, amount_atoms) + .await; + (&self.usdc_mint.key, trader_token_account) + }; + + let mut program_test_context: RefMut = self.context.borrow_mut(); + let deposit_ix: Instruction = deposit_instruction( + &self.market.key, + &keypair.pubkey(), + mint, + amount_atoms, + &trader_token_account, + wrapper_state, + spl_token::id(), + ); + + let deposit_tx: Transaction = { + Transaction::new_signed_with_payer( + &[deposit_ix], + Some(&keypair.pubkey()), + &[keypair], + program_test_context.get_new_latest_blockhash().await?, + ) + }; + program_test_context + .banks_client + .process_transaction(deposit_tx) + .await?; + + Ok(()) + } + + pub async fn withdraw( + &mut self, + token: Token, + amount_atoms: u64, + ) -> anyhow::Result<(), BanksClientError> { + self.withdraw_for_keypair(token, amount_atoms, &self.payer_keypair()) + .await?; + Ok(()) + } + + pub async fn withdraw_for_keypair( + &mut self, + token: Token, + amount_atoms: u64, + keypair: &Keypair, + ) -> anyhow::Result<(), BanksClientError> { + let is_base: bool = token == Token::SOL; + let (mint, trader_token_account) = if is_base { + let trader_token_account: Pubkey = if keypair.pubkey() == self.payer() { + self.payer_sol.key + } else { + // Make a new token account + let token_account_keypair: Keypair = Keypair::new(); + let token_account_fixture: TokenAccountFixture = + TokenAccountFixture::new_with_keypair( + Rc::clone(&self.context), + &self.sol_mint.key, + &keypair.pubkey(), + &token_account_keypair, + ) + .await; + token_account_fixture.key + }; + (&self.sol_mint.key, trader_token_account) + } else { + let trader_token_account: Pubkey = if keypair.pubkey() == self.payer() { + self.payer_usdc.key + } else { + // Make a new token account + let token_account_keypair: Keypair = Keypair::new(); + let token_account_fixture: TokenAccountFixture = + TokenAccountFixture::new_with_keypair( + Rc::clone(&self.context), + &self.usdc_mint.key, + &keypair.pubkey(), + &token_account_keypair, + ) + .await; + token_account_fixture.key + }; + (&self.usdc_mint.key, trader_token_account) + }; + + let mut context: RefMut = self.context.borrow_mut(); + let withdraw_ix: Instruction = withdraw_instruction( + &self.market.key, + &keypair.pubkey(), + mint, + amount_atoms, + &trader_token_account, + &self.wrapper.key, + spl_token::id(), + ); + + let withdraw_tx: Transaction = { + Transaction::new_signed_with_payer( + &[withdraw_ix], + Some(&keypair.pubkey()), + &[keypair], + context.get_new_latest_blockhash().await?, + ) + }; + + context + .banks_client + .process_transaction(withdraw_tx) + .await?; + Ok(()) + } +} + +#[derive(Clone)] +pub struct MarketFixture { + pub context: Rc>, + pub key: Pubkey, + pub market: MarketValue, +} + +impl MarketFixture { + pub async fn new(context: Rc>, key: Pubkey) -> Self { + let context_ref: Rc> = Rc::clone(&context); + + // Just needed for storing the decimals. The rest can be blank. + let mut lamports: u64 = 0; + let base_mint: MintAccountInfo = MintAccountInfo { + mint: Mint { + mint_authority: None.into(), + supply: 0, + decimals: 6, + is_initialized: true, + freeze_authority: None.into(), + }, + info: &AccountInfo { + key: &Pubkey::new_unique(), + lamports: Rc::new(RefCell::new(&mut lamports)), + data: Rc::new(RefCell::new(&mut [])), + owner: &Pubkey::new_unique(), + rent_epoch: 0, + is_signer: false, + is_writable: false, + executable: false, + }, + }; + + let mut lamports: u64 = 0; + let quote_mint: MintAccountInfo = MintAccountInfo { + mint: Mint { + mint_authority: None.into(), + supply: 0, + decimals: 9, + is_initialized: true, + freeze_authority: None.into(), + }, + info: &AccountInfo { + key: &Pubkey::new_unique(), + lamports: Rc::new(RefCell::new(&mut lamports)), + data: Rc::new(RefCell::new(&mut [])), + owner: &Pubkey::new_unique(), + rent_epoch: 0, + is_signer: false, + is_writable: false, + executable: false, + }, + }; + // Dummy default value. Not valid until reload. + MarketFixture { + context: context_ref, + key, + market: MarketValue { + fixed: MarketFixed::new_empty(&base_mint, "e_mint, &key), + dynamic: Vec::new(), + }, + } + } + + pub async fn reload(&mut self) { + let market_account: Account = self + .context + .borrow_mut() + .banks_client + .get_account(self.key) + .await + .unwrap() + .unwrap(); + + let market: MarketValue = get_dynamic_value(market_account.data.as_slice()); + self.market = market; + } + + pub async fn get_base_balance_atoms(&mut self, trader: &Pubkey) -> u64 { + self.reload().await; + self.market.get_trader_balance(trader).0.as_u64() + } + + pub async fn get_quote_balance_atoms(&mut self, trader: &Pubkey) -> u64 { + self.reload().await; + self.market.get_trader_balance(trader).1.as_u64() + } +} + +#[derive(Clone)] +pub struct WrapperFixture { + pub context: Rc>, + pub key: Pubkey, +} + +impl WrapperFixture { + pub async fn new(context: Rc>, key: Pubkey) -> Self { + let context_ref: Rc> = Rc::clone(&context); + WrapperFixture { + context: context_ref, + key, + } + } +} + +#[derive(Clone)] +pub struct MintFixture { + pub context: Rc>, + pub key: Pubkey, +} + +impl MintFixture { + pub async fn new( + context: Rc>, + mint_keypair: Option, + mint_decimals: Option, + ) -> MintFixture { + let context_ref: Rc> = Rc::clone(&context); + let keypair: Keypair = mint_keypair.unwrap_or_else(Keypair::new); + let mut context: RefMut = context.borrow_mut(); + + let rent: Rent = context.banks_client.get_rent().await.unwrap(); + + let init_account_ix: Instruction = create_account( + &context.payer.pubkey(), + &keypair.pubkey(), + rent.minimum_balance(spl_token::state::Mint::LEN), + spl_token::state::Mint::LEN as u64, + &spl_token::id(), + ); + let init_mint_ix: Instruction = spl_token::instruction::initialize_mint( + &spl_token::id(), + &keypair.pubkey(), + &context.payer.pubkey(), + None, + mint_decimals.unwrap_or(6), + ) + .unwrap(); + + let initialize_mint_tx: Transaction = Transaction::new_signed_with_payer( + &[init_account_ix, init_mint_ix], + Some(&context.payer.pubkey()), + &[&context.payer.insecure_clone(), &keypair], + context.get_new_latest_blockhash().await.unwrap(), + ); + + context + .banks_client + .process_transaction(initialize_mint_tx) + .await + .unwrap(); + + MintFixture { + context: context_ref, + key: keypair.pubkey(), + } + } + + pub async fn mint_to(&mut self, dest: &Pubkey, native_amount: u64) { + let payer_keypair: Keypair = self.context.borrow().payer.insecure_clone(); + let mint_to_ix: Instruction = self.make_mint_to_ix(dest, native_amount); + send_tx_with_retry( + Rc::clone(&self.context), + &[mint_to_ix], + Some(&payer_keypair.pubkey()), + &[&payer_keypair], + ) + .await; + } + + fn make_mint_to_ix(&self, dest: &Pubkey, amount: u64) -> Instruction { + let context: Ref = self.context.borrow(); + let mint_to_instruction: Instruction = spl_token::instruction::mint_to( + &spl_token::ID, + &self.key, + dest, + &context.payer.pubkey(), + &[&context.payer.pubkey()], + amount, + ) + .unwrap(); + mint_to_instruction + } +} + +pub struct TokenAccountFixture { + pub key: Pubkey, +} + +impl TokenAccountFixture { + async fn create_ixs( + rent: Rent, + mint_pk: &Pubkey, + payer_pk: &Pubkey, + owner_pk: &Pubkey, + keypair: &Keypair, + ) -> [Instruction; 2] { + let init_account_ix: Instruction = create_account( + payer_pk, + &keypair.pubkey(), + rent.minimum_balance(spl_token::state::Account::LEN), + spl_token::state::Account::LEN as u64, + &spl_token::id(), + ); + + let init_token_ix: Instruction = spl_token::instruction::initialize_account( + &spl_token::id(), + &keypair.pubkey(), + mint_pk, + owner_pk, + ) + .unwrap(); + + [init_account_ix, init_token_ix] + } + + pub async fn new_with_keypair( + context: Rc>, + mint_pk: &Pubkey, + owner_pk: &Pubkey, + keypair: &Keypair, + ) -> Self { + let mut program_test_context: RefMut = context.borrow_mut(); + + let rent: Rent = program_test_context.banks_client.get_rent().await.unwrap(); + let instructions: [Instruction; 2] = Self::create_ixs( + rent, + mint_pk, + &program_test_context.payer.pubkey(), + owner_pk, + keypair, + ) + .await; + drop(program_test_context); + + let payer_keypair: Keypair = context.borrow().payer.insecure_clone(); + send_tx_with_retry( + Rc::clone(&context), + &instructions, + Some(&payer_keypair.pubkey()), + &[&payer_keypair, keypair], + ) + .await; + + Self { + key: keypair.pubkey(), + } + } + + pub async fn new( + context: Rc>, + mint_pk: &Pubkey, + owner_pk: &Pubkey, + ) -> TokenAccountFixture { + let keypair: Keypair = Keypair::new(); + TokenAccountFixture::new_with_keypair(context, mint_pk, owner_pk, &keypair).await + } +} + +// TODO: Make the TestFixture use this. +pub(crate) async fn send_tx_with_retry( + context: Rc>, + instructions: &[Instruction], + payer: Option<&Pubkey>, + signers: &[&Keypair], +) { + let mut context: std::cell::RefMut = context.borrow_mut(); + + loop { + let blockhash_or: Result = context.get_new_latest_blockhash().await; + if blockhash_or.is_err() { + continue; + } + let tx: Transaction = + Transaction::new_signed_with_payer(instructions, payer, signers, blockhash_or.unwrap()); + let result: Result<(), BanksClientError> = + context.banks_client.process_transaction(tx).await; + if result.is_ok() { + break; + } + let error: BanksClientError = result.err().unwrap(); + match error { + BanksClientError::RpcError(_rpc_err) => { + // Retry on rpc errors. + continue; + } + _ => { + println!("Unexpected error: {:?}", error); + assert!(false); + } + } + } +} diff --git a/programs/wrapper/tests/program_test/mod.rs b/programs/wrapper/tests/program_test/mod.rs new file mode 100644 index 000000000..4f380ccbd --- /dev/null +++ b/programs/wrapper/tests/program_test/mod.rs @@ -0,0 +1,3 @@ +pub mod fixtures; + +pub use fixtures::*; From 06c08d91c875a2a9e2bb67bfdeaecb340fe4aa20 Mon Sep 17 00:00:00 2001 From: Britt Cyr Date: Fri, 23 Aug 2024 16:08:18 +0200 Subject: [PATCH 02/16] fmt --- programs/wrapper/src/market_info.rs | 2 +- programs/wrapper/src/open_order.rs | 2 +- programs/wrapper/src/processors/batch_upate.rs | 4 +--- programs/wrapper/src/processors/claim_seat.rs | 2 +- programs/wrapper/src/processors/create_wrapper.rs | 2 +- programs/wrapper/src/processors/shared.rs | 8 ++++---- programs/wrapper/tests/cases/batch_update.rs | 2 +- 7 files changed, 10 insertions(+), 12 deletions(-) diff --git a/programs/wrapper/src/market_info.rs b/programs/wrapper/src/market_info.rs index 6698709f3..875862737 100644 --- a/programs/wrapper/src/market_info.rs +++ b/programs/wrapper/src/market_info.rs @@ -1,6 +1,6 @@ use bytemuck::{Pod, Zeroable}; -use manifest::quantities::{BaseAtoms, QuoteAtoms}; use hypertree::{DataIndex, NIL}; +use manifest::quantities::{BaseAtoms, QuoteAtoms}; use solana_program::pubkey::Pubkey; use static_assertions::const_assert_eq; use std::{cmp::Ordering, mem::size_of}; diff --git a/programs/wrapper/src/open_order.rs b/programs/wrapper/src/open_order.rs index 88a79b8b0..4c6b0a074 100644 --- a/programs/wrapper/src/open_order.rs +++ b/programs/wrapper/src/open_order.rs @@ -1,9 +1,9 @@ use bytemuck::{Pod, Zeroable}; +use hypertree::{DataIndex, PodBool, NIL}; use manifest::{ quantities::{BaseAtoms, QuoteAtomsPerBaseAtom, WrapperU64}, state::{OrderType, NO_EXPIRATION_LAST_VALID_SLOT}, }; -use hypertree::{DataIndex, PodBool, NIL}; use static_assertions::const_assert_eq; use std::{cmp::Ordering, mem::size_of}; diff --git a/programs/wrapper/src/processors/batch_upate.rs b/programs/wrapper/src/processors/batch_upate.rs index 75505d12b..05cd52ec4 100644 --- a/programs/wrapper/src/processors/batch_upate.rs +++ b/programs/wrapper/src/processors/batch_upate.rs @@ -4,6 +4,7 @@ use std::{ }; use borsh::{BorshDeserialize, BorshSerialize}; +use hypertree::{get_helper, get_mut_helper, DataIndex, FreeList, RBNode, TreeReadOperations, NIL}; use manifest::{ program::{ batch_update::{BatchUpdateReturn, CancelOrderParams, PlaceOrderParams}, @@ -13,9 +14,6 @@ use manifest::{ state::{MarketFixed, MarketRef, OrderType}, validation::{ManifestAccountInfo, Program, Signer}, }; -use hypertree::{ - get_helper, get_mut_helper, DataIndex, FreeList, RBNode, TreeReadOperations, NIL, -}; use solana_program::{ account_info::{next_account_info, AccountInfo}, entrypoint::ProgramResult, diff --git a/programs/wrapper/src/processors/claim_seat.rs b/programs/wrapper/src/processors/claim_seat.rs index 0ef93280e..5b5f2488f 100644 --- a/programs/wrapper/src/processors/claim_seat.rs +++ b/programs/wrapper/src/processors/claim_seat.rs @@ -1,11 +1,11 @@ use std::{cell::RefMut, mem::size_of}; +use hypertree::{get_mut_helper, DataIndex, FreeList, TreeReadOperations, NIL}; use manifest::{ program::{claim_seat_instruction, expand_instruction, get_mut_dynamic_account}, state::{MarketFixed, MarketRefMut}, validation::ManifestAccountInfo, }; -use hypertree::{get_mut_helper, DataIndex, FreeList, TreeReadOperations, NIL}; use crate::{market_info::MarketInfo, wrapper_state::ManifestWrapperStateFixed}; use manifest::validation::{Program, Signer}; diff --git a/programs/wrapper/src/processors/create_wrapper.rs b/programs/wrapper/src/processors/create_wrapper.rs index dbc11e7c4..10402b624 100644 --- a/programs/wrapper/src/processors/create_wrapper.rs +++ b/programs/wrapper/src/processors/create_wrapper.rs @@ -1,5 +1,5 @@ -use manifest::validation::{Program, Signer}; use hypertree::get_mut_helper; +use manifest::validation::{Program, Signer}; use solana_program::{ account_info::{next_account_info, AccountInfo}, entrypoint::ProgramResult, diff --git a/programs/wrapper/src/processors/shared.rs b/programs/wrapper/src/processors/shared.rs index 7a66de7fa..f5d78c1b9 100644 --- a/programs/wrapper/src/processors/shared.rs +++ b/programs/wrapper/src/processors/shared.rs @@ -8,16 +8,16 @@ use crate::{ market_info::MarketInfo, open_order::WrapperOpenOrder, wrapper_state::ManifestWrapperStateFixed, }; use bytemuck::{Pod, Zeroable}; +use hypertree::{ + get_helper, get_mut_helper, trace, DataIndex, FreeList, RBNode, RedBlackTree, + RedBlackTreeReadOnly, TreeReadOperations, NIL, +}; use manifest::{ program::assert_with_msg, quantities::BaseAtoms, state::{claimed_seat::ClaimedSeat, MarketRef, RestingOrder}, validation::{Program, Signer}, }; -use hypertree::{ - get_helper, get_mut_helper, trace, DataIndex, FreeList, RBNode, RedBlackTree, - RedBlackTreeReadOnly, TreeReadOperations, NIL, -}; use solana_program::{ account_info::AccountInfo, clock::Clock, diff --git a/programs/wrapper/tests/cases/batch_update.rs b/programs/wrapper/tests/cases/batch_update.rs index 84bef0d83..c591e96b9 100644 --- a/programs/wrapper/tests/cases/batch_update.rs +++ b/programs/wrapper/tests/cases/batch_update.rs @@ -1,7 +1,7 @@ use std::{cell::RefMut, mem::size_of, rc::Rc}; -use manifest::state::{constants::NO_EXPIRATION_LAST_VALID_SLOT, OrderType}; use hypertree::{get_helper, DataIndex, RBNode, TreeReadOperations, NIL}; +use manifest::state::{constants::NO_EXPIRATION_LAST_VALID_SLOT, OrderType}; use solana_program_test::{tokio, ProgramTestContext}; use solana_sdk::{ account::Account, instruction::Instruction, pubkey::Pubkey, signature::Keypair, signer::Signer, From fc1af167527380f5270d10784a1f84dcb9dd9085 Mon Sep 17 00:00:00 2001 From: Britt Cyr Date: Fri, 23 Aug 2024 16:16:08 +0200 Subject: [PATCH 03/16] add swap --- programs/manifest/src/program/processor/swap.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/programs/manifest/src/program/processor/swap.rs b/programs/manifest/src/program/processor/swap.rs index e72f88ed1..63cb55e4f 100644 --- a/programs/manifest/src/program/processor/swap.rs +++ b/programs/manifest/src/program/processor/swap.rs @@ -81,6 +81,8 @@ pub(crate) fn process_swap( is_exact_in, } = SwapParams::try_from_slice(data)?; + trace!("swap in_atoms:{in_atoms} out_atoms:{out_atoms} is_base_in:{is_base_in} is_exact_in:{is_exact_in}"); + // this is a virtual credit to ensure matching always proceeds // net token transfers will be handled later dynamic_account.deposit(payer.key, in_atoms, is_base_in)?; From 5b270c0794d9ea1b213080c985baf9e66a9ec863 Mon Sep 17 00:00:00 2001 From: Britt Cyr Date: Fri, 23 Aug 2024 21:33:38 +0200 Subject: [PATCH 04/16] Fixes for byte alignment --- lib/src/red_black_tree.rs | 1 + programs/manifest/src/quantities.rs | 69 +++++++++++++++--------- programs/manifest/src/state/constants.rs | 4 +- programs/wrapper/src/open_order.rs | 2 +- 4 files changed, 49 insertions(+), 27 deletions(-) diff --git a/lib/src/red_black_tree.rs b/lib/src/red_black_tree.rs index 7b569ffdd..278c52252 100644 --- a/lib/src/red_black_tree.rs +++ b/lib/src/red_black_tree.rs @@ -964,6 +964,7 @@ impl<'a, V: TreeValue> RedBlackTree<'a, V> { } } + // TODO: Go from max to min because that would be cheaper as we already store max /// Sorted iterator starting from the min. pub fn iter(&self) -> RedBlackTreeIterator { RedBlackTreeIterator { diff --git a/programs/manifest/src/quantities.rs b/programs/manifest/src/quantities.rs index 89ac6f040..5c62dadce 100644 --- a/programs/manifest/src/quantities.rs +++ b/programs/manifest/src/quantities.rs @@ -5,9 +5,7 @@ use hypertree::trace; use shank::ShankAccount; use solana_program::{msg, program_error::ProgramError}; use std::{ - fmt::Display, - ops::{Add, AddAssign, Div}, - u128, u64, + cmp::Ordering, fmt::Display, ops::{Add, AddAssign, Div}, u128, u64 }; /// New and as_u64 for creating and switching to u64 when needing to use base or @@ -156,10 +154,6 @@ basic_u64!(GlobalAtoms); // Manifest pricing #[derive( - PartialEq, - Eq, - PartialOrd, - Ord, Clone, Copy, Default, @@ -169,9 +163,16 @@ basic_u64!(GlobalAtoms); Serialize, ShankAccount, )] -#[repr(transparent)] +#[repr(C)] pub struct QuoteAtomsPerBaseAtom { - inner: u128, + inner: [u64; 2], +} + +fn u128_to_u64_slice(a: u128) -> [u64; 2] { + bytemuck::cast(a) +} +fn u64_slice_to_u128(a: [u64; 2]) -> u128 { + bytemuck::cast(a) } const ATOM_LIMIT: u128 = u64::MAX as u128; @@ -214,10 +215,9 @@ const DECIMAL_CONSTANTS: [u128; 31] = [ // Prices impl QuoteAtomsPerBaseAtom { - pub const ZERO: Self = QuoteAtomsPerBaseAtom { inner: 0 }; - pub const ONE: Self = QuoteAtomsPerBaseAtom { inner: D20 }; + pub const ZERO: Self = QuoteAtomsPerBaseAtom { inner: [0; 2] }; pub const MIN: Self = QuoteAtomsPerBaseAtom::ZERO; - pub const MAX: Self = QuoteAtomsPerBaseAtom { inner: u128::MAX }; + pub const MAX: Self = QuoteAtomsPerBaseAtom { inner: [u64::MAX; 2] }; pub fn try_from_mantissa_and_exponent( mantissa: u32, @@ -241,7 +241,7 @@ impl QuoteAtomsPerBaseAtom { let inner = DECIMAL_CONSTANTS[offset] .checked_mul(mantissa as u128) .ok_or(PriceConversionError(0x2))?; - return Ok(QuoteAtomsPerBaseAtom { inner }); + return Ok(QuoteAtomsPerBaseAtom { inner: u128_to_u64_slice(inner) }); } pub fn checked_base_for_quote( @@ -255,10 +255,11 @@ impl QuoteAtomsPerBaseAtom { let dividend = D20 .checked_mul(quote_atoms.inner as u128) .ok_or(PriceConversionError(0x4))?; + let inner: u128 = u64_slice_to_u128(self.inner); let base_atoms = if round_up { - dividend.div_ceil(self.inner) + dividend.div_ceil(inner) } else { - dividend.div(self.inner) + dividend.div(inner) }; assert_with_msg( base_atoms <= ATOM_LIMIT, @@ -274,8 +275,8 @@ impl QuoteAtomsPerBaseAtom { base_atoms: BaseAtoms, round_up: bool, ) -> Result { - let product = self - .inner + let inner: u128 = u64_slice_to_u128(self.inner); + let product: u128 = inner .checked_mul(base_atoms.inner as u128) .ok_or(PriceConversionError(0x8))?; let quote_atoms = if round_up { @@ -316,20 +317,40 @@ impl QuoteAtomsPerBaseAtom { .ok_or(PriceConversionError(0x9))?; // no special case rounding needed because effective price is just a value used to compare for order let inner = quote_matched_d20.div(num_base_atoms.inner as u128); - Ok(QuoteAtomsPerBaseAtom { inner }) + Ok(QuoteAtomsPerBaseAtom { inner: u128_to_u64_slice(inner) }) + } +} + +impl Ord for QuoteAtomsPerBaseAtom { + fn cmp(&self, other: &Self) -> Ordering { + (u64_slice_to_u128(self.inner)).cmp(&u64_slice_to_u128(other.inner)) } } +impl PartialOrd for QuoteAtomsPerBaseAtom { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl PartialEq for QuoteAtomsPerBaseAtom { + fn eq(&self, other: &Self) -> bool { + (self.inner) == (other.inner) + } +} + +impl Eq for QuoteAtomsPerBaseAtom {} + impl std::fmt::Display for QuoteAtomsPerBaseAtom { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_fmt(format_args!("{}", &(self.inner as f64 / D20F))) + f.write_fmt(format_args!("{}", &(u64_slice_to_u128(self.inner) as f64 / D20F))) } } impl std::fmt::Debug for QuoteAtomsPerBaseAtom { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("QuoteAtomsPerBaseAtom") - .field("value", &(self.inner as f64 / D20F)) + .field("value", &(u64_slice_to_u128(self.inner) as f64 / D20F)) .finish() } } @@ -363,7 +384,7 @@ impl TryFrom for QuoteAtomsPerBaseAtom { return Err(PriceConversionError(0xE)); } Ok(QuoteAtomsPerBaseAtom { - inner: mantissa as u128, + inner: u128_to_u64_slice(mantissa as u128), }) } } @@ -419,7 +440,7 @@ fn test_checked_sub() { fn test_multiply_macro() { let base_atoms: BaseAtoms = BaseAtoms::new(5); let quote_atoms_per_base_atom: QuoteAtomsPerBaseAtom = QuoteAtomsPerBaseAtom { - inner: 100 * D20 - 1, + inner: u128_to_u64_slice(100 * D20 - 1), }; assert_eq!( base_atoms @@ -436,7 +457,7 @@ fn test_print() { println!( "{}", QuoteAtomsPerBaseAtom { - inner: 123 * D20 / 100 + inner: u128_to_u64_slice(123 * D20 / 100), } ); } @@ -448,7 +469,7 @@ fn test_debug() { println!( "{:?}", QuoteAtomsPerBaseAtom { - inner: 123 * D20 / 100 + inner: u128_to_u64_slice(123 * D20 / 100), } ); } diff --git a/programs/manifest/src/state/constants.rs b/programs/manifest/src/state/constants.rs index 5990b3b32..c484d44c3 100644 --- a/programs/manifest/src/state/constants.rs +++ b/programs/manifest/src/state/constants.rs @@ -3,8 +3,8 @@ use hypertree::RBTREE_OVERHEAD_BYTES; pub const MARKET_FIXED_SIZE: usize = 512; pub const GLOBAL_FIXED_SIZE: usize = 88; -// Red black tree overhead is 16 bytes. If each block is 64 bytes, then we get -// 48 bytes for a RestingOrder or ClaimedSeat. +// Red black tree overhead is 16 bytes. If each block is 80 bytes, then we get +// 64 bytes for a RestingOrder or ClaimedSeat. pub const BLOCK_SIZE: usize = 80; const BLOCK_PAYLOAD_SIZE: usize = BLOCK_SIZE - RBTREE_OVERHEAD_BYTES; pub const RESTING_ORDER_SIZE: usize = BLOCK_PAYLOAD_SIZE; diff --git a/programs/wrapper/src/open_order.rs b/programs/wrapper/src/open_order.rs index 4c6b0a074..113c229d1 100644 --- a/programs/wrapper/src/open_order.rs +++ b/programs/wrapper/src/open_order.rs @@ -70,7 +70,7 @@ impl WrapperOpenOrder { WrapperOpenOrder { client_order_id, order_sequence_number: 0, - price: QuoteAtomsPerBaseAtom::ONE, + price: QuoteAtomsPerBaseAtom::ZERO, num_base_atoms: BaseAtoms::ZERO, last_valid_slot: NO_EXPIRATION_LAST_VALID_SLOT, order_type: OrderType::Limit, From 0e5a50019b249108553777598bdbb127b9af036e Mon Sep 17 00:00:00 2001 From: Britt Cyr Date: Fri, 23 Aug 2024 21:34:20 +0200 Subject: [PATCH 05/16] fmt --- programs/manifest/src/quantities.rs | 33 ++++++++++++++++------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/programs/manifest/src/quantities.rs b/programs/manifest/src/quantities.rs index 5c62dadce..3e5a13fa1 100644 --- a/programs/manifest/src/quantities.rs +++ b/programs/manifest/src/quantities.rs @@ -5,7 +5,10 @@ use hypertree::trace; use shank::ShankAccount; use solana_program::{msg, program_error::ProgramError}; use std::{ - cmp::Ordering, fmt::Display, ops::{Add, AddAssign, Div}, u128, u64 + cmp::Ordering, + fmt::Display, + ops::{Add, AddAssign, Div}, + u128, u64, }; /// New and as_u64 for creating and switching to u64 when needing to use base or @@ -153,16 +156,7 @@ pub struct GlobalAtoms { basic_u64!(GlobalAtoms); // Manifest pricing -#[derive( - Clone, - Copy, - Default, - Zeroable, - Pod, - Deserialize, - Serialize, - ShankAccount, -)] +#[derive(Clone, Copy, Default, Zeroable, Pod, Deserialize, Serialize, ShankAccount)] #[repr(C)] pub struct QuoteAtomsPerBaseAtom { inner: [u64; 2], @@ -217,7 +211,9 @@ const DECIMAL_CONSTANTS: [u128; 31] = [ impl QuoteAtomsPerBaseAtom { pub const ZERO: Self = QuoteAtomsPerBaseAtom { inner: [0; 2] }; pub const MIN: Self = QuoteAtomsPerBaseAtom::ZERO; - pub const MAX: Self = QuoteAtomsPerBaseAtom { inner: [u64::MAX; 2] }; + pub const MAX: Self = QuoteAtomsPerBaseAtom { + inner: [u64::MAX; 2], + }; pub fn try_from_mantissa_and_exponent( mantissa: u32, @@ -241,7 +237,9 @@ impl QuoteAtomsPerBaseAtom { let inner = DECIMAL_CONSTANTS[offset] .checked_mul(mantissa as u128) .ok_or(PriceConversionError(0x2))?; - return Ok(QuoteAtomsPerBaseAtom { inner: u128_to_u64_slice(inner) }); + return Ok(QuoteAtomsPerBaseAtom { + inner: u128_to_u64_slice(inner), + }); } pub fn checked_base_for_quote( @@ -317,7 +315,9 @@ impl QuoteAtomsPerBaseAtom { .ok_or(PriceConversionError(0x9))?; // no special case rounding needed because effective price is just a value used to compare for order let inner = quote_matched_d20.div(num_base_atoms.inner as u128); - Ok(QuoteAtomsPerBaseAtom { inner: u128_to_u64_slice(inner) }) + Ok(QuoteAtomsPerBaseAtom { + inner: u128_to_u64_slice(inner), + }) } } @@ -343,7 +343,10 @@ impl Eq for QuoteAtomsPerBaseAtom {} impl std::fmt::Display for QuoteAtomsPerBaseAtom { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_fmt(format_args!("{}", &(u64_slice_to_u128(self.inner) as f64 / D20F))) + f.write_fmt(format_args!( + "{}", + &(u64_slice_to_u128(self.inner) as f64 / D20F) + )) } } From c4ef560ca280738e6b4b2e15b5dd6f222fa5d3d8 Mon Sep 17 00:00:00 2001 From: Britt Cyr Date: Sat, 24 Aug 2024 20:14:11 +0200 Subject: [PATCH 06/16] test deflake --- programs/manifest/tests/program_test/fixtures.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/programs/manifest/tests/program_test/fixtures.rs b/programs/manifest/tests/program_test/fixtures.rs index fc3ba0376..efa1bb3aa 100644 --- a/programs/manifest/tests/program_test/fixtures.rs +++ b/programs/manifest/tests/program_test/fixtures.rs @@ -1193,6 +1193,10 @@ pub async fn send_tx_with_retry( // Retry on rpc errors. continue; } + BanksClientError::Io(_io_err) => { + // Retry on io errors. + continue; + } _ => { println!("Unexpected error: {:?}", error); return Err(error); From f9f57c384045fa5b6fc3ec3c4ac0a8f8ed786fa4 Mon Sep 17 00:00:00 2001 From: Britt Cyr Date: Sun, 25 Aug 2024 04:57:59 +0200 Subject: [PATCH 07/16] first commit for client --- .github/workflows/ci-code-review-ts.yml | 187 + Cargo.lock | 185 +- Cargo.toml | 1 + client/idl/generateClient.js | 68 + client/idl/generateIdl.js | 378 ++ client/idl/manifest.json | 1700 ++++++++ client/idl/package.json | 16 + client/idl/wrapper.json | 491 +++ client/idl/yarn.lock | 853 ++++ client/rust/Cargo.toml | 19 + client/rust/README.md | 9 + client/rust/src/lib.rs | 806 ++++ client/ts/src/client.ts | 495 +++ client/ts/src/constants.ts | 3 + client/ts/src/fillFeed.ts | 191 + client/ts/src/index.ts | 9 + client/ts/src/manifest/accounts/BaseAtoms.ts | 160 + .../src/manifest/accounts/CancelOrderLog.ts | 179 + .../ts/src/manifest/accounts/ClaimSeatLog.ts | 161 + .../src/manifest/accounts/CreateMarketLog.ts | 161 + client/ts/src/manifest/accounts/DepositLog.ts | 181 + client/ts/src/manifest/accounts/FillLog.ts | 197 + .../manifest/accounts/GlobalAddTraderLog.ts | 163 + .../ts/src/manifest/accounts/GlobalAtoms.ts | 159 + .../manifest/accounts/GlobalClaimSeatLog.ts | 167 + .../src/manifest/accounts/GlobalCreateLog.ts | 161 + .../src/manifest/accounts/GlobalDepositLog.ts | 166 + .../ts/src/manifest/accounts/PlaceOrderLog.ts | 220 ++ client/ts/src/manifest/accounts/QuoteAtoms.ts | 160 + .../accounts/QuoteAtomsPerBaseAtom.ts | 158 + .../ts/src/manifest/accounts/WithdrawLog.ts | 184 + client/ts/src/manifest/accounts/index.ts | 49 + client/ts/src/manifest/errors/index.ts | 414 ++ client/ts/src/manifest/index.ts | 21 + .../src/manifest/instructions/BatchUpdate.ts | 169 + .../ts/src/manifest/instructions/ClaimSeat.ts | 75 + .../src/manifest/instructions/CreateMarket.ts | 117 + .../ts/src/manifest/instructions/Deposit.ts | 118 + client/ts/src/manifest/instructions/Expand.ts | 75 + .../manifest/instructions/GlobalAddTrader.ts | 75 + .../manifest/instructions/GlobalClaimSeat.ts | 82 + .../src/manifest/instructions/GlobalCreate.ts | 96 + .../manifest/instructions/GlobalDeposit.ts | 121 + client/ts/src/manifest/instructions/Swap.ts | 175 + .../ts/src/manifest/instructions/Withdraw.ts | 118 + client/ts/src/manifest/instructions/index.ts | 11 + .../src/manifest/types/BatchUpdateParams.ts | 29 + .../src/manifest/types/BatchUpdateReturn.ts | 21 + .../src/manifest/types/CancelOrderParams.ts | 25 + client/ts/src/manifest/types/DepositParams.ts | 20 + .../src/manifest/types/GlobalDepositParams.ts | 21 + client/ts/src/manifest/types/OrderType.ts | 27 + .../ts/src/manifest/types/PlaceOrderParams.ts | 33 + client/ts/src/manifest/types/SwapParams.ts | 28 + .../ts/src/manifest/types/WithdrawParams.ts | 20 + client/ts/src/manifest/types/index.ts | 9 + client/ts/src/market.ts | 432 +++ client/ts/src/utils/beet.ts | 111 + client/ts/src/utils/market.ts | 11 + client/ts/src/utils/numbers.ts | 16 + client/ts/src/utils/redBlackTree.ts | 128 + client/ts/src/utils/solana.ts | 31 + client/ts/src/wrapper/index.ts | 19 + .../src/wrapper/instructions/BatchUpdate.ts | 120 + .../ts/src/wrapper/instructions/ClaimSeat.ts | 96 + .../src/wrapper/instructions/CreateWrapper.ts | 82 + client/ts/src/wrapper/instructions/Deposit.ts | 139 + .../ts/src/wrapper/instructions/Withdraw.ts | 139 + client/ts/src/wrapper/instructions/index.ts | 5 + client/ts/src/wrapper/types/DepositParams.ts | 20 + client/ts/src/wrapper/types/OrderType.ts | 27 + client/ts/src/wrapper/types/WithdrawParams.ts | 20 + .../wrapper/types/WrapperBatchUpdateParams.ts | 37 + .../wrapper/types/WrapperCancelOrderParams.ts | 21 + .../wrapper/types/WrapperPlaceOrderParams.ts | 38 + client/ts/src/wrapper/types/index.ts | 6 + client/ts/src/wrapperObj.ts | 301 ++ client/ts/tests/batchUpdate.ts | 105 + client/ts/tests/cancelOrder.ts | 148 + client/ts/tests/claimSeat.ts | 72 + client/ts/tests/createMarket.ts | 90 + client/ts/tests/deposit.ts | 87 + client/ts/tests/fillFeed.ts | 98 + client/ts/tests/market.ts | 110 + client/ts/tests/placeOrder.ts | 109 + client/ts/tests/swap.ts | 101 + client/ts/tests/utils.ts | 26 + client/ts/tests/withdraw.ts | 72 + client/ts/tests/wrapper.ts | 154 + local-validator-test.sh | 25 + package.json | 67 + programs/manifest/src/state/constants.rs | 1 + tsconfig.cjs.json | 13 + tsconfig.esm.json | 10 + tsconfig.json | 47 + tsconfig.types.json | 10 + yarn.lock | 3423 +++++++++++++++++ 97 files changed, 16503 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/ci-code-review-ts.yml create mode 100644 client/idl/generateClient.js create mode 100644 client/idl/generateIdl.js create mode 100644 client/idl/manifest.json create mode 100644 client/idl/package.json create mode 100644 client/idl/wrapper.json create mode 100644 client/idl/yarn.lock create mode 100644 client/rust/Cargo.toml create mode 100644 client/rust/README.md create mode 100644 client/rust/src/lib.rs create mode 100644 client/ts/src/client.ts create mode 100644 client/ts/src/constants.ts create mode 100644 client/ts/src/fillFeed.ts create mode 100644 client/ts/src/index.ts create mode 100644 client/ts/src/manifest/accounts/BaseAtoms.ts create mode 100644 client/ts/src/manifest/accounts/CancelOrderLog.ts create mode 100644 client/ts/src/manifest/accounts/ClaimSeatLog.ts create mode 100644 client/ts/src/manifest/accounts/CreateMarketLog.ts create mode 100644 client/ts/src/manifest/accounts/DepositLog.ts create mode 100644 client/ts/src/manifest/accounts/FillLog.ts create mode 100644 client/ts/src/manifest/accounts/GlobalAddTraderLog.ts create mode 100644 client/ts/src/manifest/accounts/GlobalAtoms.ts create mode 100644 client/ts/src/manifest/accounts/GlobalClaimSeatLog.ts create mode 100644 client/ts/src/manifest/accounts/GlobalCreateLog.ts create mode 100644 client/ts/src/manifest/accounts/GlobalDepositLog.ts create mode 100644 client/ts/src/manifest/accounts/PlaceOrderLog.ts create mode 100644 client/ts/src/manifest/accounts/QuoteAtoms.ts create mode 100644 client/ts/src/manifest/accounts/QuoteAtomsPerBaseAtom.ts create mode 100644 client/ts/src/manifest/accounts/WithdrawLog.ts create mode 100644 client/ts/src/manifest/accounts/index.ts create mode 100644 client/ts/src/manifest/errors/index.ts create mode 100644 client/ts/src/manifest/index.ts create mode 100644 client/ts/src/manifest/instructions/BatchUpdate.ts create mode 100644 client/ts/src/manifest/instructions/ClaimSeat.ts create mode 100644 client/ts/src/manifest/instructions/CreateMarket.ts create mode 100644 client/ts/src/manifest/instructions/Deposit.ts create mode 100644 client/ts/src/manifest/instructions/Expand.ts create mode 100644 client/ts/src/manifest/instructions/GlobalAddTrader.ts create mode 100644 client/ts/src/manifest/instructions/GlobalClaimSeat.ts create mode 100644 client/ts/src/manifest/instructions/GlobalCreate.ts create mode 100644 client/ts/src/manifest/instructions/GlobalDeposit.ts create mode 100644 client/ts/src/manifest/instructions/Swap.ts create mode 100644 client/ts/src/manifest/instructions/Withdraw.ts create mode 100644 client/ts/src/manifest/instructions/index.ts create mode 100644 client/ts/src/manifest/types/BatchUpdateParams.ts create mode 100644 client/ts/src/manifest/types/BatchUpdateReturn.ts create mode 100644 client/ts/src/manifest/types/CancelOrderParams.ts create mode 100644 client/ts/src/manifest/types/DepositParams.ts create mode 100644 client/ts/src/manifest/types/GlobalDepositParams.ts create mode 100644 client/ts/src/manifest/types/OrderType.ts create mode 100644 client/ts/src/manifest/types/PlaceOrderParams.ts create mode 100644 client/ts/src/manifest/types/SwapParams.ts create mode 100644 client/ts/src/manifest/types/WithdrawParams.ts create mode 100644 client/ts/src/manifest/types/index.ts create mode 100644 client/ts/src/market.ts create mode 100644 client/ts/src/utils/beet.ts create mode 100644 client/ts/src/utils/market.ts create mode 100644 client/ts/src/utils/numbers.ts create mode 100644 client/ts/src/utils/redBlackTree.ts create mode 100644 client/ts/src/utils/solana.ts create mode 100644 client/ts/src/wrapper/index.ts create mode 100644 client/ts/src/wrapper/instructions/BatchUpdate.ts create mode 100644 client/ts/src/wrapper/instructions/ClaimSeat.ts create mode 100644 client/ts/src/wrapper/instructions/CreateWrapper.ts create mode 100644 client/ts/src/wrapper/instructions/Deposit.ts create mode 100644 client/ts/src/wrapper/instructions/Withdraw.ts create mode 100644 client/ts/src/wrapper/instructions/index.ts create mode 100644 client/ts/src/wrapper/types/DepositParams.ts create mode 100644 client/ts/src/wrapper/types/OrderType.ts create mode 100644 client/ts/src/wrapper/types/WithdrawParams.ts create mode 100644 client/ts/src/wrapper/types/WrapperBatchUpdateParams.ts create mode 100644 client/ts/src/wrapper/types/WrapperCancelOrderParams.ts create mode 100644 client/ts/src/wrapper/types/WrapperPlaceOrderParams.ts create mode 100644 client/ts/src/wrapper/types/index.ts create mode 100644 client/ts/src/wrapperObj.ts create mode 100644 client/ts/tests/batchUpdate.ts create mode 100644 client/ts/tests/cancelOrder.ts create mode 100644 client/ts/tests/claimSeat.ts create mode 100644 client/ts/tests/createMarket.ts create mode 100644 client/ts/tests/deposit.ts create mode 100644 client/ts/tests/fillFeed.ts create mode 100644 client/ts/tests/market.ts create mode 100644 client/ts/tests/placeOrder.ts create mode 100644 client/ts/tests/swap.ts create mode 100644 client/ts/tests/utils.ts create mode 100644 client/ts/tests/withdraw.ts create mode 100644 client/ts/tests/wrapper.ts create mode 100644 local-validator-test.sh create mode 100644 package.json create mode 100644 tsconfig.cjs.json create mode 100644 tsconfig.esm.json create mode 100644 tsconfig.json create mode 100644 tsconfig.types.json create mode 100644 yarn.lock diff --git a/.github/workflows/ci-code-review-ts.yml b/.github/workflows/ci-code-review-ts.yml new file mode 100644 index 000000000..76a49ac38 --- /dev/null +++ b/.github/workflows/ci-code-review-ts.yml @@ -0,0 +1,187 @@ +name: Code Review - TypeScript + +on: + pull_request: + branches: ['main'] + paths: ['client/ts/**', 'yarn.lock'] + push: + branches: ['main'] + workflow_dispatch: + +env: + CARGO_TERM_COLOR: always + SOLANA_VERSION: '1.18.22' + RUST_TOOLCHAIN: '1.78.0' + +jobs: + format: + name: Format + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + token: ${{ secrets.MANIFEST_PAT }} + submodules: recursive + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: '18' + cache: 'yarn' + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Format + run: yarn format + + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + token: ${{ secrets.MANIFEST_PAT }} + submodules: recursive + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: '18' + cache: 'yarn' + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Lint + run: yarn lint + + depcheck: + name: Dependency check + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + token: ${{ secrets.MANIFEST_PAT }} + submodules: recursive + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: '18' + cache: 'yarn' + + - name: Duplicates check + run: npx yarn-deduplicate --list --fail + + test: + name: Test + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + token: ${{ secrets.MANIFEST_PAT }} + submodules: recursive + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: '18' + cache: 'yarn' + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Cache dependencies + uses: Swatinem/rust-cache@v2 + + - name: Set Rust version + run: rustup toolchain install ${{ env.RUST_TOOLCHAIN }} + + - name: Install Solana + run: | + sh -c "$(curl -sSfL https://release.solana.com/v${{ env.SOLANA_VERSION }}/install)" + echo "$HOME/.local/share/solana/install/active_release/bin" >> $GITHUB_PATH + export PATH="/home/runner/.local/share/solana/install/active_release/bin:$PATH" + solana --version + echo "Generating keypair..." + solana-keygen new -o "$HOME/.config/solana/id.json" --no-passphrase --silent + + - name: Start validator + run: | + solana-test-validator > /dev/null 2>&1 & + echo "Started test validator, sleeping for 15 seconds before starting" + sleep 15 + echo "Updating hardcoded discriminant constants for the hardcoded deploy key" + sed -i 's/MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms/bitpRsDktxXjwcQvx11p3JZ4A8ucwM1AYBgjNfhr3yf/' programs/manifest/src/lib.rs + sed -i 's/4859840929024028656/15533312255830019719/' programs/manifest/src/validation/market_checker.rs + sed -i 's/\[33, 31, 11, 6, 133, 143, 39, 71\]/[228, 66, 25, 184, 68, 250, 83, 68]/' programs/manifest/src/logs.rs + sed -i 's/\[129, 77, 152, 210, 218, 144, 163, 56\]/[83, 80, 13, 195, 157, 15, 144, 87]/' programs/manifest/src/logs.rs + sed -i 's/\[23, 214, 24, 34, 52, 104, 109, 188\]/[67, 61, 36, 241, 56, 245, 238, 146]/' programs/manifest/src/logs.rs + sed -i 's/\[112, 218, 111, 63, 18, 95, 136, 35\]/[216, 76, 133, 25, 77, 4, 208, 169]/' programs/manifest/src/logs.rs + sed -i 's/\[58, 230, 242, 3, 75, 113, 4, 169\]/[52, 81, 147, 82, 119, 191, 72, 172]/' programs/manifest/src/logs.rs + sed -i 's/\[157, 118, 247, 213, 47, 19, 164, 120\]/[192, 6, 190, 126, 253, 107, 243, 124]/' programs/manifest/src/logs.rs + sed -i 's/\[22, 65, 71, 33, 244, 235, 255, 215\]/[203, 247, 55, 164, 199, 233, 155, 33]/' programs/manifest/src/logs.rs + mkdir -p target/deploy + echo "[151,8,48,152,68,30,226,198,60,77,31,58,178,181,32,89,229,203,74,21,125,127,220,171,148,2,58,248,111,112,218,58,8,229,29,213,101,63,98,202,13,39,74,3,188,95,195,65,60,246,83,247,221,124,176,114,136,100,191,104,177,76,52,70]" > target/deploy/manifest-keypair.json + cargo build-sbf + echo "Rebuilt program" + rm target/deploy/manifest-keypair.json + touch target/deploy/manifest-keypair.json + echo "[151,8,48,152,68,30,226,198,60,77,31,58,178,181,32,89,229,203,74,21,125,127,220,171,148,2,58,248,111,112,218,58,8,229,29,213,101,63,98,202,13,39,74,3,188,95,195,65,60,246,83,247,221,124,176,114,136,100,191,104,177,76,52,70]" > target/deploy/manifest-keypair.json + solana config set --url l + solana program deploy target/deploy/manifest.so > deploy.log + cat deploy.log + + solana program deploy target/deploy/wrapper.so > deploy.log + cat deploy.log + sed -i 's/'$(awk -F\" '/declare_id!/ {print$2}' programs/wrapper/src/lib.rs)'/'$(awk '/Program Id:/ {print$3}' deploy.log)'/' client/ts/src/wrapper/index.ts + for f in $(ls client/ts/src/wrapper/instructions); do + sed -i 's/'$(awk -F\" '/declare_id!/ {print$2}' programs/wrapper/src/lib.rs)'/'$(awk '/Program Id:/ {print$3}' deploy.log)'/' client/ts/src/wrapper/instructions/$f + echo "Updated -> $f" + done + sed -i 's/'$(awk -F\" '/declare_id!/ {print$2}' programs/wrapper/src/lib.rs)'/'$(awk '/Program Id:/ {print$3}' deploy.log)'/' client/ts/src/wrapper/instructions/* + sed -i 's/'$(awk -F\" '/declare_id!/ {print$2}' programs/wrapper/src/lib.rs)'/'$(awk '/Program Id:/ {print$3}' deploy.log)'/' programs/wrapper/src/lib.rs + + for f in $(ls client/ts/src/manifest/instructions); do + sed -i 's/MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms/bitpRsDktxXjwcQvx11p3JZ4A8ucwM1AYBgjNfhr3yf/' client/ts/src/manifest/instructions/$f + echo "Updated -> $f" + done + sed -i 's/MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms/bitpRsDktxXjwcQvx11p3JZ4A8ucwM1AYBgjNfhr3yf/' client/ts/src/manifest/index.ts + + echo "Built and deployed" + + cargo build-sbf + echo "Rebuilt and redeploying because program id is hardcoded" + + solana program deploy target/deploy/manifest.so + solana program deploy target/deploy/wrapper.so + echo "Done redeploy" + + yarn clean + yarn build + + - name: Run test + run: | + yarn test + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: lcov.info + verbose: true + fail_ci_if_error: true + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + + ts-pass: + name: TS tests pass + needs: ['format', 'lint', 'test', 'depcheck'] + runs-on: ubuntu-latest + steps: + - run: echo ok \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index e8226e45a..c9da3375f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -481,6 +481,18 @@ dependencies = [ "typenum", ] +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "blake3" version = "1.5.1" @@ -677,6 +689,28 @@ dependencies = [ "serde", ] +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "bytemuck" version = "1.17.0" @@ -1427,6 +1461,12 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures" version = "0.3.30" @@ -1962,6 +2002,21 @@ dependencies = [ "serde_json", ] +[[package]] +name = "jupiter-amm-interface" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0a07d892e79ae4ad8f0947f30633188269287967ceb0b2c182f894745f49952" +dependencies = [ + "anyhow", + "borsh 0.9.3", + "rust_decimal", + "serde", + "serde_json", + "solana-account-decoder", + "solana-sdk", +] + [[package]] name = "keccak" version = "0.1.5" @@ -2127,6 +2182,21 @@ dependencies = [ "tokio", ] +[[package]] +name = "manifest-jupiter" +version = "0.1.0" +dependencies = [ + "anyhow", + "hypertree", + "jupiter-amm-interface", + "manifest", + "solana-program", + "solana-sdk", + "spl-token 3.5.0", + "spl-token-2022 3.0.4", + "tokio", +] + [[package]] name = "memchr" version = "2.7.4" @@ -2818,6 +2888,26 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "qstring" version = "0.7.2" @@ -2895,6 +2985,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.7.3" @@ -3045,6 +3141,15 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + [[package]] name = "reqwest" version = "0.11.27" @@ -3118,6 +3223,35 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rkyv" +version = "0.7.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "rpassword" version = "7.3.1" @@ -3139,6 +3273,22 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "rust_decimal" +version = "1.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b082d80e3e3cc52b2ed634388d436fe1f4de6af5786cc2de9ba9737527bdf555" +dependencies = [ + "arrayvec", + "borsh 1.5.1", + "bytes", + "num-traits", + "rand 0.8.5", + "rkyv", + "serde", + "serde_json", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -3291,6 +3441,12 @@ dependencies = [ "untrusted 0.9.0", ] +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + [[package]] name = "security-framework" version = "2.11.1" @@ -3543,6 +3699,12 @@ version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" +[[package]] +name = "simdutf8" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" + [[package]] name = "siphasher" version = "0.3.11" @@ -5263,6 +5425,12 @@ dependencies = [ "libc", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tar" version = "0.4.41" @@ -5813,6 +5981,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "uuid" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" + [[package]] name = "valuable" version = "0.1.0" @@ -5982,7 +6156,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -6189,6 +6363,15 @@ dependencies = [ "tokio", ] +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "x509-parser" version = "0.14.0" diff --git a/Cargo.toml b/Cargo.toml index a7bad8b32..26806cd2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ resolver = "2" members = [ "lib", + "client/rust", "programs/*", ] diff --git a/client/idl/generateClient.js b/client/idl/generateClient.js new file mode 100644 index 000000000..f651cb0bf --- /dev/null +++ b/client/idl/generateClient.js @@ -0,0 +1,68 @@ +const { Solita } = require('@metaplex-foundation/solita'); +const { spawnSync } = require('child_process'); +const path = require('path'); +const idlDir = __dirname; + +async function main() { + ['manifest', 'wrapper'].forEach((programName) => { + const sdkDir = path.join(__dirname, '..', 'ts', 'src', programName); + const accountsPath = path.join(sdkDir, 'accounts/*'); + const typesPath = path.join(sdkDir, 'types/*'); + + console.log('Generating TypeScript SDK to %s', sdkDir); + // Use a previously generated idl instead of all at once in this script + // https://github.com/metaplex-foundation/solita because we need to add args + // to instructions after shank runs. + const generatedIdlPath = path.join(idlDir, `${programName}.json`); + + console.log('Using IDL at %s', generatedIdlPath); + const idl = require(generatedIdlPath); + const gen = new Solita(idl, { formatCode: true }); + + gen.renderAndWriteTo(sdkDir).then(() => { + console.log('Running prettier on generated files...'); + spawnSync('prettier', ['--write', sdkDir, '--trailing-comma all'], { + stdio: 'inherit', + }); + // Fix the fact that floats are not supported by beet. + spawnSync( + 'sed', + ['-i', "'s/FixedSizeUint8Array/fixedSizeUint8Array(8)/g'", typesPath], + { stdio: 'inherit', shell: true, windowsVerbatimArguments: true }, + ); + if (programName == 'manifest') { + spawnSync( + 'sed', + [ + '-i', + "'s/FixedSizeUint8Array/fixedSizeUint8Array(8)/g'", + accountsPath, + ], + { stdio: 'inherit', shell: true, windowsVerbatimArguments: true }, + ); + } + + spawnSync( + 'cd ../../ && yarn format', + ['--write', ' --config package.json', '--trailing-comma'], + { stdio: 'inherit' }, + ); + + // TODO: Also do the wrapper constant. + // Make sure the client has the correct fixed header size. Copy any + // other constants over as needed. + spawnSync( + "ORIGINAL_LINE=$(awk '/export const FIXED_MANIFEST_HEADER_SIZE: number = [-.0-9]+;/' client/ts/src/constants.ts); " + + 'NEW_LINE=$(echo "export const FIXED_MANIFEST_HEADER_SIZE: number = ")$(awk \'/pub const MARKET_FIXED_SIZE: usize = [-.0-9]+;/\' programs/manifest/src/state/constants.rs | tr -d -c 0-9)$(echo ";"); ' + + 'sed --debug -i "s/${ORIGINAL_LINE}/${NEW_LINE}/" client/ts/src/constants.ts', + [], + { stdio: 'inherit' }, + ); + }); + }); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/client/idl/generateIdl.js b/client/idl/generateIdl.js new file mode 100644 index 000000000..3d3719bfb --- /dev/null +++ b/client/idl/generateIdl.js @@ -0,0 +1,378 @@ +const bs58 = require('bs58'); +const keccak256 = require('keccak256'); +const path = require('path'); +const fs = require('fs'); +const { + rustbinMatch, + confirmAutoMessageConsole, +} = require('@metaplex-foundation/rustbin'); +const { spawnSync } = require('child_process'); + +const idlDir = __dirname; +const rootDir = path.join(__dirname, '..', '..', '.crates'); + +async function main() { + console.log('Root dir address:', rootDir); + ['manifest', 'wrapper'].map(async (programName) => { + const programDir = path.join( + __dirname, + '..', + '..', + 'programs', + programName, + ); + const cargoToml = path.join(programDir, 'Cargo.toml'); + console.log('Cargo.Toml address:', cargoToml); + + const rustbinConfig = { + rootDir, + binaryName: 'shank', + binaryCrateName: 'shank-cli', + libName: 'shank', + dryRun: false, + cargoToml, + }; + // Uses rustbin from https://github.com/metaplex-foundation/rustbin + const { fullPathToBinary: shankExecutable } = await rustbinMatch( + rustbinConfig, + confirmAutoMessageConsole, + ); + spawnSync(shankExecutable, [ + 'idl', + '--out-dir', + idlDir, + '--crate-root', + programDir, + ]); + modifyIdlCore(programName); + }); +} + +function genLogDiscriminator(programIdString, accName) { + return keccak256( + Buffer.concat([ + Buffer.from(bs58.default.decode(programIdString)), + Buffer.from('manifest::logs::'), + Buffer.from(accName), + ]), + ).subarray(0, 8); +} + +function modifyIdlCore(programName) { + console.log('Adding arguments to IDL for', programName); + const generatedIdlPath = path.join(idlDir, `${programName}.json`); + let idl = require(generatedIdlPath); + + // Shank does not understand the type alias. + idl = findAndReplaceRecursively(idl, { defined: 'DataIndex' }, 'u32'); + + // Since we are not using anchor, we do not have the event macro, and that + // means we need to manually insert events into idl. + idl.events = []; + + if (programName == 'manifest') { + // generateClient does not handle events + // https://github.com/metaplex-foundation/shank/blob/34d3081208adca8b6b2be2b77db9b1ab4a70f577/shank-idl/src/file.rs#L185 + // so dont remove from accounts + for (const idlAccount of idl.accounts) { + if (idlAccount.name.includes('Log')) { + const event = { + name: idlAccount.name, + discriminator: [ + ...genLogDiscriminator(idl.metadata.address, idlAccount.name), + ], + fields: idlAccount.type.fields, + }; + idl.events.push(event); + } + } + + // Solita does not support f64 + // https://github.com/metaplex-foundation/beet/issues/48 + for (const idlType of idl.types) { + if (idlType.type && idlType.type.fields) { + idlType.type.fields = idlType.type.fields.map((field) => { + if (field.name == 'price') { + field.type = 'FixedSizeUint8Array'; + } + return field; + }); + } + } + + for (const idlAccount of idl.accounts) { + if (idlAccount.type && idlAccount.type.fields) { + idlAccount.type.fields = idlAccount.type.fields.map((field) => { + if (field.type.defined == 'PodBool') { + field.type = 'bool'; + } + if (field.type.defined == 'f64') { + field.type = 'FixedSizeUint8Array'; + } + return field; + }); + } + } + + for (const instruction of idl.instructions) { + switch (instruction.name) { + case 'CreateMarket': { + // Create market does not have params + break; + } + case 'ClaimSeat': { + // Claim seat does not have params + break; + } + case 'Deposit': { + instruction.args.push({ + name: 'params', + type: { + defined: 'DepositParams', + }, + }); + break; + } + case 'Withdraw': { + instruction.args.push({ + name: 'params', + type: { + defined: 'WithdrawParams', + }, + }); + break; + } + case 'PlaceOrder': { + instruction.args.push({ + name: 'params', + type: { + defined: 'PlaceOrderParams', + }, + }); + break; + } + case 'CancelOrder': { + instruction.args.push({ + name: 'params', + type: { + defined: 'CancelOrderParams', + }, + }); + break; + } + case 'Swap': { + instruction.args.push({ + name: 'params', + type: { + defined: 'SwapParams', + }, + }); + break; + } + case 'BatchUpdate': { + instruction.args.push({ + name: 'params', + type: { + defined: 'BatchUpdateParams', + }, + }); + break; + } + case 'Expand': { + break; + } + case 'GlobalCreate': { + break; + } + case 'GlobalAddTrader': { + break; + } + case 'GlobalClaimSeat': { + break; + } + case 'GlobalCleanOrder': { + break; + } + case 'GlobalDeposit': { + instruction.args.push({ + name: 'params', + type: { + defined: 'GlobalDepositParams', + }, + }); + break; + } + default: { + console.log(instruction); + throw new Error('Unexpected instruction'); + } + } + } + } else if (programName == 'wrapper') { + // Solita does not support f64 + // https://github.com/metaplex-foundation/beet/issues/48 + for (const idlType of idl.types) { + if (idlType.type && idlType.type.fields) { + idlType.type.fields = idlType.type.fields.map((field) => { + if (field.name == 'price') { + field.type = 'FixedSizeUint8Array'; + } + return field; + }); + } + } + idl.types.push({ + name: 'DepositParams', + type: { + kind: 'struct', + fields: [ + { + name: 'amountAtoms', + type: 'u64', + }, + ], + }, + }); + idl.types.push({ + name: 'WithdrawParams', + type: { + kind: 'struct', + fields: [ + { + name: 'amountAtoms', + type: 'u64', + }, + ], + }, + }); + idl.types.push({ + name: 'OrderType', + type: { + kind: 'enum', + variants: [ + { + name: 'Limit', + }, + { + name: 'ImmediateOrCancel', + }, + { + name: 'PostOnly', + }, + { + name: 'PostOnlySlide', + }, + { + name: 'FillOrKill', + }, + ], + }, + }); + + for (const idlType of idl.types) { + if (idlType.type && idlType.type.fields) { + idlType.type.fields = idlType.type.fields.map((field) => { + if (field.type.defined == 'PodBool') { + field.type = 'bool'; + } + if (field.type.defined == 'f64') { + field.type = 'FixedSizeUint8Array'; + } + return field; + }); + } + } + + for (const instruction of idl.instructions) { + switch (instruction.name) { + case 'CreateWrapper': { + break; + } + case 'ClaimSeat': { + // Claim seat does not have params + break; + } + case 'Deposit': { + instruction.args.push({ + name: 'params', + type: { + defined: 'DepositParams', + }, + }); + break; + } + case 'Withdraw': { + instruction.args.push({ + name: 'params', + type: { + defined: 'WithdrawParams', + }, + }); + break; + } + case 'BatchUpdate': { + instruction.args.push({ + name: 'params', + type: { + defined: 'WrapperBatchUpdateParams', + }, + }); + break; + } + case 'Expand': { + break; + } + default: { + console.log(instruction); + throw new Error('Unexpected instruction'); + } + } + } + } else { + throw new Error('Unexpected program name'); + } + fs.writeFileSync(generatedIdlPath, JSON.stringify(idl, null, 2)); +} + +function isObject(x) { + return x instanceof Object; +} + +function isArray(x) { + return x instanceof Array; +} + +/** + * @param {*} target Target can be anything + * @param {*} find val to find + * @param {*} replaceWith val to replace + * @returns the target with replaced values + */ +function findAndReplaceRecursively(target, find, replaceWith) { + if (!isObject(target)) { + if (target === find) { + return replaceWith; + } + return target; + } else if ( + isObject(find) && + JSON.stringify(target) === JSON.stringify(find) + ) { + return replaceWith; + } + if (isArray(target)) { + return target.map((child) => { + return findAndReplaceRecursively(child, find, replaceWith); + }); + } + return Object.keys(target).reduce((carry, key) => { + const val = target[key]; + carry[key] = findAndReplaceRecursively(val, find, replaceWith); + return carry; + }, {}); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/client/idl/manifest.json b/client/idl/manifest.json new file mode 100644 index 000000000..b82f4b68d --- /dev/null +++ b/client/idl/manifest.json @@ -0,0 +1,1700 @@ +{ + "version": "0.1.0", + "name": "manifest", + "instructions": [ + { + "name": "CreateMarket", + "accounts": [ + { + "name": "payer", + "isMut": true, + "isSigner": true, + "docs": [ + "Payer" + ] + }, + { + "name": "market", + "isMut": true, + "isSigner": false, + "docs": [ + "Account holding all market state" + ] + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "System program" + ] + }, + { + "name": "baseMint", + "isMut": false, + "isSigner": false, + "docs": [ + "Base mint" + ] + }, + { + "name": "quoteMint", + "isMut": false, + "isSigner": false, + "docs": [ + "Quote mint" + ] + }, + { + "name": "baseVault", + "isMut": true, + "isSigner": false, + "docs": [ + "Base vault PDA, seeds are [b'vault', market, base_mint]" + ] + }, + { + "name": "quoteVault", + "isMut": true, + "isSigner": false, + "docs": [ + "Quote vault PDA, seeds are [b'vault', market, quote_mint]" + ] + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "Token program" + ] + }, + { + "name": "tokenProgram22", + "isMut": false, + "isSigner": false, + "docs": [ + "Token program 22" + ] + } + ], + "args": [], + "discriminant": { + "type": "u8", + "value": 0 + } + }, + { + "name": "ClaimSeat", + "accounts": [ + { + "name": "payer", + "isMut": true, + "isSigner": true, + "docs": [ + "Payer" + ] + }, + { + "name": "market", + "isMut": true, + "isSigner": false, + "docs": [ + "Account holding all market state" + ] + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "System program" + ] + } + ], + "args": [], + "discriminant": { + "type": "u8", + "value": 1 + } + }, + { + "name": "Deposit", + "accounts": [ + { + "name": "payer", + "isMut": true, + "isSigner": true, + "docs": [ + "Payer" + ] + }, + { + "name": "market", + "isMut": true, + "isSigner": false, + "docs": [ + "Account holding all market state" + ] + }, + { + "name": "traderToken", + "isMut": true, + "isSigner": false, + "docs": [ + "Trader token account" + ] + }, + { + "name": "vault", + "isMut": true, + "isSigner": false, + "docs": [ + "Vault PDA, seeds are [b'vault', market, mint]" + ] + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "Token program(22), should be the version that aligns with the token being used" + ] + }, + { + "name": "mint", + "isMut": false, + "isSigner": false, + "docs": [ + "Required for token22 transfer_checked" + ] + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "DepositParams" + } + } + ], + "discriminant": { + "type": "u8", + "value": 2 + } + }, + { + "name": "Withdraw", + "accounts": [ + { + "name": "payer", + "isMut": true, + "isSigner": true, + "docs": [ + "Payer" + ] + }, + { + "name": "market", + "isMut": true, + "isSigner": false, + "docs": [ + "Account holding all market state" + ] + }, + { + "name": "traderToken", + "isMut": true, + "isSigner": false, + "docs": [ + "Trader token account" + ] + }, + { + "name": "vault", + "isMut": true, + "isSigner": false, + "docs": [ + "Vault PDA, seeds are [b'vault', market, mint]" + ] + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "Token program(22), should be the version that aligns with the token being used" + ] + }, + { + "name": "mint", + "isMut": false, + "isSigner": false, + "docs": [ + "Required for token22 transfer_checked" + ] + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "WithdrawParams" + } + } + ], + "discriminant": { + "type": "u8", + "value": 3 + } + }, + { + "name": "Swap", + "accounts": [ + { + "name": "payer", + "isMut": true, + "isSigner": true, + "docs": [ + "Payer" + ] + }, + { + "name": "market", + "isMut": true, + "isSigner": false, + "docs": [ + "Account holding all market state" + ] + }, + { + "name": "traderBase", + "isMut": true, + "isSigner": false, + "docs": [ + "Trader base token account" + ] + }, + { + "name": "traderQuote", + "isMut": true, + "isSigner": false, + "docs": [ + "Trader quote token account" + ] + }, + { + "name": "baseVault", + "isMut": true, + "isSigner": false, + "docs": [ + "Base vault PDA, seeds are [b'vault', market_address, base_mint]" + ] + }, + { + "name": "quoteVault", + "isMut": true, + "isSigner": false, + "docs": [ + "Quote vault PDA, seeds are [b'vault', market_address, quote_mint]" + ] + }, + { + "name": "tokenProgramBase", + "isMut": false, + "isSigner": false, + "docs": [ + "Token program(22) base" + ] + }, + { + "name": "baseMint", + "isMut": false, + "isSigner": false, + "docs": [ + "Base mint, only inlcuded if base is Token22, otherwise not required" + ] + }, + { + "name": "tokenProgramQuote", + "isMut": false, + "isSigner": false, + "docs": [ + "Token program(22) quote. Optional. Only include if different from base" + ] + }, + { + "name": "quoteMint", + "isMut": false, + "isSigner": false, + "docs": [ + "Quote mint, only inlcuded if base is Token22, otherwise not required" + ] + }, + { + "name": "global", + "isMut": true, + "isSigner": false, + "isOptional": true, + "docs": [ + "Global account" + ] + }, + { + "name": "globalVault", + "isMut": true, + "isSigner": false, + "isOptional": true, + "docs": [ + "Global vault" + ] + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "SwapParams" + } + } + ], + "discriminant": { + "type": "u8", + "value": 4 + } + }, + { + "name": "Expand", + "accounts": [ + { + "name": "payer", + "isMut": true, + "isSigner": true, + "docs": [ + "Payer" + ] + }, + { + "name": "market", + "isMut": true, + "isSigner": false, + "docs": [ + "Account holding all market state" + ] + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "System program" + ] + } + ], + "args": [], + "discriminant": { + "type": "u8", + "value": 5 + } + }, + { + "name": "BatchUpdate", + "accounts": [ + { + "name": "payer", + "isMut": true, + "isSigner": true, + "docs": [ + "Payer" + ] + }, + { + "name": "market", + "isMut": true, + "isSigner": false, + "docs": [ + "Account holding all market state" + ] + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "System program" + ] + }, + { + "name": "baseMint", + "isMut": false, + "isSigner": false, + "docs": [ + "Mint for the global global account" + ] + }, + { + "name": "baseGlobal", + "isMut": true, + "isSigner": false, + "docs": [ + "Base global account" + ] + }, + { + "name": "baseGlobalVault", + "isMut": false, + "isSigner": false, + "docs": [ + "Base global vault" + ] + }, + { + "name": "baseMarketVault", + "isMut": false, + "isSigner": false, + "docs": [ + "Base market vault" + ] + }, + { + "name": "baseTokenProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "Token program(22)" + ] + }, + { + "name": "quoteMint", + "isMut": false, + "isSigner": false, + "docs": [ + "Mint for this global account" + ] + }, + { + "name": "quoteGlobal", + "isMut": true, + "isSigner": false, + "docs": [ + "Quote global account" + ] + }, + { + "name": "quoteGlobalVault", + "isMut": false, + "isSigner": false, + "docs": [ + "Quote global vault" + ] + }, + { + "name": "quoteMarketVault", + "isMut": false, + "isSigner": false, + "docs": [ + "Quote market vault" + ] + }, + { + "name": "quoteTokenProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "Token program(22)" + ] + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "BatchUpdateParams" + } + } + ], + "discriminant": { + "type": "u8", + "value": 6 + } + }, + { + "name": "GlobalCreate", + "accounts": [ + { + "name": "payer", + "isMut": true, + "isSigner": true, + "docs": [ + "Payer" + ] + }, + { + "name": "global", + "isMut": true, + "isSigner": false, + "docs": [ + "Global account" + ] + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "System program" + ] + }, + { + "name": "mint", + "isMut": false, + "isSigner": false, + "docs": [ + "Mint for this global account" + ] + }, + { + "name": "globalVault", + "isMut": true, + "isSigner": false, + "docs": [ + "Global vault" + ] + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "Token program(22)" + ] + } + ], + "args": [], + "discriminant": { + "type": "u8", + "value": 7 + } + }, + { + "name": "GlobalAddTrader", + "accounts": [ + { + "name": "payer", + "isMut": true, + "isSigner": true, + "docs": [ + "Payer" + ] + }, + { + "name": "global", + "isMut": true, + "isSigner": false, + "docs": [ + "Global account" + ] + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "System program" + ] + } + ], + "args": [], + "discriminant": { + "type": "u8", + "value": 8 + } + }, + { + "name": "GlobalClaimSeat", + "accounts": [ + { + "name": "payer", + "isMut": true, + "isSigner": true, + "docs": [ + "Payer" + ] + }, + { + "name": "global", + "isMut": true, + "isSigner": false, + "docs": [ + "Global account" + ] + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "System program" + ] + }, + { + "name": "market", + "isMut": true, + "isSigner": false, + "docs": [ + "Account holding all market state" + ] + } + ], + "args": [], + "discriminant": { + "type": "u8", + "value": 9 + } + }, + { + "name": "GlobalDeposit", + "accounts": [ + { + "name": "payer", + "isMut": true, + "isSigner": true, + "docs": [ + "Payer" + ] + }, + { + "name": "global", + "isMut": true, + "isSigner": false, + "docs": [ + "Global account" + ] + }, + { + "name": "mint", + "isMut": false, + "isSigner": false, + "docs": [ + "Mint for this global account" + ] + }, + { + "name": "globalVault", + "isMut": false, + "isSigner": false, + "docs": [ + "Global vault" + ] + }, + { + "name": "traderToken", + "isMut": false, + "isSigner": false, + "docs": [ + "Trader token account" + ] + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "Token program(22)" + ] + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "GlobalDepositParams" + } + } + ], + "discriminant": { + "type": "u8", + "value": 10 + } + } + ], + "accounts": [ + { + "name": "CreateMarketLog", + "type": { + "kind": "struct", + "fields": [ + { + "name": "market", + "type": "publicKey" + }, + { + "name": "creator", + "type": "publicKey" + } + ] + } + }, + { + "name": "ClaimSeatLog", + "type": { + "kind": "struct", + "fields": [ + { + "name": "market", + "type": "publicKey" + }, + { + "name": "trader", + "type": "publicKey" + } + ] + } + }, + { + "name": "DepositLog", + "type": { + "kind": "struct", + "fields": [ + { + "name": "market", + "type": "publicKey" + }, + { + "name": "trader", + "type": "publicKey" + }, + { + "name": "mint", + "type": "publicKey" + }, + { + "name": "amountAtoms", + "type": "u64" + } + ] + } + }, + { + "name": "WithdrawLog", + "type": { + "kind": "struct", + "fields": [ + { + "name": "market", + "type": "publicKey" + }, + { + "name": "trader", + "type": "publicKey" + }, + { + "name": "mint", + "type": "publicKey" + }, + { + "name": "amountAtoms", + "type": "u64" + } + ] + } + }, + { + "name": "FillLog", + "type": { + "kind": "struct", + "fields": [ + { + "name": "market", + "type": "publicKey" + }, + { + "name": "maker", + "type": "publicKey" + }, + { + "name": "taker", + "type": "publicKey" + }, + { + "name": "price", + "type": { + "defined": "QuoteAtomsPerBaseAtom" + } + }, + { + "name": "baseAtoms", + "type": { + "defined": "BaseAtoms" + } + }, + { + "name": "quoteAtoms", + "type": { + "defined": "QuoteAtoms" + } + }, + { + "name": "takerIsBuy", + "type": "bool" + }, + { + "name": "padding", + "type": { + "array": [ + "u8", + 15 + ] + } + } + ] + } + }, + { + "name": "PlaceOrderLog", + "type": { + "kind": "struct", + "fields": [ + { + "name": "market", + "type": "publicKey" + }, + { + "name": "trader", + "type": "publicKey" + }, + { + "name": "price", + "type": { + "defined": "QuoteAtomsPerBaseAtom" + } + }, + { + "name": "baseAtoms", + "type": { + "defined": "BaseAtoms" + } + }, + { + "name": "orderSequenceNumber", + "type": "u64" + }, + { + "name": "orderIndex", + "type": "u32" + }, + { + "name": "lastValidSlot", + "type": "u32" + }, + { + "name": "orderType", + "type": { + "defined": "OrderType" + } + }, + { + "name": "isBid", + "type": "bool" + }, + { + "name": "padding", + "type": { + "array": [ + "u8", + 6 + ] + } + } + ] + } + }, + { + "name": "CancelOrderLog", + "type": { + "kind": "struct", + "fields": [ + { + "name": "market", + "type": "publicKey" + }, + { + "name": "trader", + "type": "publicKey" + }, + { + "name": "orderSequenceNumber", + "type": "u64" + } + ] + } + }, + { + "name": "GlobalCreateLog", + "type": { + "kind": "struct", + "fields": [ + { + "name": "global", + "type": "publicKey" + }, + { + "name": "creator", + "type": "publicKey" + } + ] + } + }, + { + "name": "GlobalAddTraderLog", + "type": { + "kind": "struct", + "fields": [ + { + "name": "global", + "type": "publicKey" + }, + { + "name": "trader", + "type": "publicKey" + } + ] + } + }, + { + "name": "GlobalClaimSeatLog", + "type": { + "kind": "struct", + "fields": [ + { + "name": "global", + "type": "publicKey" + }, + { + "name": "market", + "type": "publicKey" + }, + { + "name": "trader", + "type": "publicKey" + } + ] + } + }, + { + "name": "GlobalDepositLog", + "type": { + "kind": "struct", + "fields": [ + { + "name": "global", + "type": "publicKey" + }, + { + "name": "trader", + "type": "publicKey" + }, + { + "name": "globalAtoms", + "type": { + "defined": "GlobalAtoms" + } + } + ] + } + }, + { + "name": "QuoteAtoms", + "type": { + "kind": "struct", + "fields": [ + { + "name": "inner", + "type": "u64" + } + ] + } + }, + { + "name": "BaseAtoms", + "type": { + "kind": "struct", + "fields": [ + { + "name": "inner", + "type": "u64" + } + ] + } + }, + { + "name": "GlobalAtoms", + "type": { + "kind": "struct", + "fields": [ + { + "name": "inner", + "type": "u64" + } + ] + } + }, + { + "name": "QuoteAtomsPerBaseAtom", + "type": { + "kind": "struct", + "fields": [ + { + "name": "inner", + "type": { + "array": [ + "u64", + 2 + ] + } + } + ] + } + } + ], + "types": [ + { + "name": "CancelOrderParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "orderSequenceNumber", + "type": "u64" + }, + { + "name": "orderIndexHint", + "type": { + "option": "u32" + } + } + ] + } + }, + { + "name": "PlaceOrderParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "baseAtoms", + "type": "u64" + }, + { + "name": "priceMantissa", + "type": "u32" + }, + { + "name": "priceExponent", + "type": "i8" + }, + { + "name": "isBid", + "type": "bool" + }, + { + "name": "lastValidSlot", + "type": "u32" + }, + { + "name": "orderType", + "type": { + "defined": "OrderType" + } + } + ] + } + }, + { + "name": "BatchUpdateParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "traderIndexHint", + "type": { + "option": "u32" + } + }, + { + "name": "cancels", + "type": { + "vec": { + "defined": "CancelOrderParams" + } + } + }, + { + "name": "orders", + "type": { + "vec": { + "defined": "PlaceOrderParams" + } + } + } + ] + } + }, + { + "name": "BatchUpdateReturn", + "type": { + "kind": "struct", + "fields": [ + { + "name": "orders", + "type": { + "vec": { + "tuple": [ + "u64", + "u32" + ] + } + } + } + ] + } + }, + { + "name": "DepositParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "amountAtoms", + "type": "u64" + } + ] + } + }, + { + "name": "GlobalDepositParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "amountAtoms", + "type": "u64" + } + ] + } + }, + { + "name": "SwapParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "inAtoms", + "type": "u64" + }, + { + "name": "outAtoms", + "type": "u64" + }, + { + "name": "isBaseIn", + "type": "bool" + }, + { + "name": "isExactIn", + "type": "bool" + } + ] + } + }, + { + "name": "WithdrawParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "amountAtoms", + "type": "u64" + } + ] + } + }, + { + "name": "OrderType", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Limit" + }, + { + "name": "ImmediateOrCancel" + }, + { + "name": "PostOnly" + }, + { + "name": "PostOnlySlide" + }, + { + "name": "Global" + } + ] + } + } + ], + "errors": [ + { + "code": 0, + "name": "InvalidMarketParameters", + "msg": "Invalid market parameters error" + }, + { + "code": 1, + "name": "InvalidDepositAccounts", + "msg": "Invalid deposit accounts error" + }, + { + "code": 2, + "name": "InvalidWithdrawAccounts", + "msg": "Invalid withdraw accounts error" + }, + { + "code": 3, + "name": "InvalidCancel", + "msg": "Invalid cancel error" + }, + { + "code": 4, + "name": "InvalidFreeList", + "msg": "Internal free list corruption error" + }, + { + "code": 5, + "name": "AlreadyClaimedSeat", + "msg": "Cannot claim a second seat for the same trader" + }, + { + "code": 6, + "name": "PostOnlyCrosses", + "msg": "Matched on a post only order" + }, + { + "code": 7, + "name": "AlreadyExpired", + "msg": "New order is already expired" + }, + { + "code": 8, + "name": "InsufficientOut", + "msg": "Less than minimum out amount" + }, + { + "code": 9, + "name": "InvalidPlaceOrderFromWalletParams", + "msg": "Invalid place order from wallet params" + }, + { + "code": 10, + "name": "WrongIndexHintParams", + "msg": "Index hint did not match actual index" + }, + { + "code": 11, + "name": "PriceNotPositive", + "msg": "Price is not positive" + }, + { + "code": 12, + "name": "OrderWouldOverflow", + "msg": "Order settlement would overflow" + }, + { + "code": 13, + "name": "OrderTooSmall", + "msg": "Order is too small to settle any value" + }, + { + "code": 14, + "name": "Overflow", + "msg": "Overflow in token addition" + }, + { + "code": 15, + "name": "MissingGlobal", + "msg": "Missing Global account" + }, + { + "code": 16, + "name": "GlobalInsufficient", + "msg": "Insufficient funds on global account to rest an order" + } + ], + "metadata": { + "origin": "shank", + "address": "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms" + }, + "events": [ + { + "name": "CreateMarketLog", + "discriminator": [ + 33, + 31, + 11, + 6, + 133, + 143, + 39, + 71 + ], + "fields": [ + { + "name": "market", + "type": "publicKey" + }, + { + "name": "creator", + "type": "publicKey" + } + ] + }, + { + "name": "ClaimSeatLog", + "discriminator": [ + 129, + 77, + 152, + 210, + 218, + 144, + 163, + 56 + ], + "fields": [ + { + "name": "market", + "type": "publicKey" + }, + { + "name": "trader", + "type": "publicKey" + } + ] + }, + { + "name": "DepositLog", + "discriminator": [ + 23, + 214, + 24, + 34, + 52, + 104, + 109, + 188 + ], + "fields": [ + { + "name": "market", + "type": "publicKey" + }, + { + "name": "trader", + "type": "publicKey" + }, + { + "name": "mint", + "type": "publicKey" + }, + { + "name": "amountAtoms", + "type": "u64" + } + ] + }, + { + "name": "WithdrawLog", + "discriminator": [ + 112, + 218, + 111, + 63, + 18, + 95, + 136, + 35 + ], + "fields": [ + { + "name": "market", + "type": "publicKey" + }, + { + "name": "trader", + "type": "publicKey" + }, + { + "name": "mint", + "type": "publicKey" + }, + { + "name": "amountAtoms", + "type": "u64" + } + ] + }, + { + "name": "FillLog", + "discriminator": [ + 58, + 230, + 242, + 3, + 75, + 113, + 4, + 169 + ], + "fields": [ + { + "name": "market", + "type": "publicKey" + }, + { + "name": "maker", + "type": "publicKey" + }, + { + "name": "taker", + "type": "publicKey" + }, + { + "name": "price", + "type": { + "defined": "QuoteAtomsPerBaseAtom" + } + }, + { + "name": "baseAtoms", + "type": { + "defined": "BaseAtoms" + } + }, + { + "name": "quoteAtoms", + "type": { + "defined": "QuoteAtoms" + } + }, + { + "name": "takerIsBuy", + "type": "bool" + }, + { + "name": "padding", + "type": { + "array": [ + "u8", + 15 + ] + } + } + ] + }, + { + "name": "PlaceOrderLog", + "discriminator": [ + 157, + 118, + 247, + 213, + 47, + 19, + 164, + 120 + ], + "fields": [ + { + "name": "market", + "type": "publicKey" + }, + { + "name": "trader", + "type": "publicKey" + }, + { + "name": "price", + "type": { + "defined": "QuoteAtomsPerBaseAtom" + } + }, + { + "name": "baseAtoms", + "type": { + "defined": "BaseAtoms" + } + }, + { + "name": "orderSequenceNumber", + "type": "u64" + }, + { + "name": "orderIndex", + "type": "u32" + }, + { + "name": "lastValidSlot", + "type": "u32" + }, + { + "name": "orderType", + "type": { + "defined": "OrderType" + } + }, + { + "name": "isBid", + "type": "bool" + }, + { + "name": "padding", + "type": { + "array": [ + "u8", + 6 + ] + } + } + ] + }, + { + "name": "CancelOrderLog", + "discriminator": [ + 22, + 65, + 71, + 33, + 244, + 235, + 255, + 215 + ], + "fields": [ + { + "name": "market", + "type": "publicKey" + }, + { + "name": "trader", + "type": "publicKey" + }, + { + "name": "orderSequenceNumber", + "type": "u64" + } + ] + }, + { + "name": "GlobalCreateLog", + "discriminator": [ + 188, + 25, + 199, + 77, + 26, + 15, + 142, + 193 + ], + "fields": [ + { + "name": "global", + "type": "publicKey" + }, + { + "name": "creator", + "type": "publicKey" + } + ] + }, + { + "name": "GlobalAddTraderLog", + "discriminator": [ + 129, + 246, + 90, + 94, + 87, + 186, + 242, + 7 + ], + "fields": [ + { + "name": "global", + "type": "publicKey" + }, + { + "name": "trader", + "type": "publicKey" + } + ] + }, + { + "name": "GlobalClaimSeatLog", + "discriminator": [ + 164, + 46, + 227, + 175, + 3, + 143, + 73, + 86 + ], + "fields": [ + { + "name": "global", + "type": "publicKey" + }, + { + "name": "market", + "type": "publicKey" + }, + { + "name": "trader", + "type": "publicKey" + } + ] + }, + { + "name": "GlobalDepositLog", + "discriminator": [ + 16, + 26, + 72, + 1, + 145, + 232, + 182, + 71 + ], + "fields": [ + { + "name": "global", + "type": "publicKey" + }, + { + "name": "trader", + "type": "publicKey" + }, + { + "name": "globalAtoms", + "type": { + "defined": "GlobalAtoms" + } + } + ] + } + ] +} \ No newline at end of file diff --git a/client/idl/package.json b/client/idl/package.json new file mode 100644 index 000000000..e35d827d3 --- /dev/null +++ b/client/idl/package.json @@ -0,0 +1,16 @@ +{ + "dependencies": { + "@metaplex-foundation/rustbin": "^0.3.1", + "@metaplex-foundation/solita": "^0.19.4", + "bs58": "^6.0.0", + "keccak256": "^1.0.6" + }, + "prettier": { + "singleQuote": true, + "trailingComma": "all", + "semi": true + }, + "devDependencies": { + "prettier": "^3.3.2" + } +} diff --git a/client/idl/wrapper.json b/client/idl/wrapper.json new file mode 100644 index 000000000..b9b21673e --- /dev/null +++ b/client/idl/wrapper.json @@ -0,0 +1,491 @@ +{ + "version": "0.1.0", + "name": "wrapper", + "instructions": [ + { + "name": "CreateWrapper", + "accounts": [ + { + "name": "owner", + "isMut": true, + "isSigner": true, + "docs": [ + "Owner of the Manifest account" + ] + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "System program" + ] + }, + { + "name": "payer", + "isMut": true, + "isSigner": true, + "docs": [ + "Payer of rent and gas" + ] + }, + { + "name": "wrapperState", + "isMut": true, + "isSigner": false, + "docs": [ + "Wrapper state" + ] + } + ], + "args": [], + "discriminant": { + "type": "u8", + "value": 0 + } + }, + { + "name": "ClaimSeat", + "accounts": [ + { + "name": "manifestProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "Manifest program" + ] + }, + { + "name": "owner", + "isMut": true, + "isSigner": true, + "docs": [ + "Owner of the Manifest account" + ] + }, + { + "name": "market", + "isMut": true, + "isSigner": false, + "docs": [ + "Account holding all market state" + ] + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "System program" + ] + }, + { + "name": "payer", + "isMut": true, + "isSigner": true, + "docs": [ + "Payer of rent and gas" + ] + }, + { + "name": "wrapperState", + "isMut": true, + "isSigner": false, + "docs": [ + "Wrapper state" + ] + } + ], + "args": [], + "discriminant": { + "type": "u8", + "value": 1 + } + }, + { + "name": "Deposit", + "accounts": [ + { + "name": "manifestProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "Manifest program" + ] + }, + { + "name": "owner", + "isMut": true, + "isSigner": true, + "docs": [ + "Owner of the Manifest account" + ] + }, + { + "name": "market", + "isMut": true, + "isSigner": false, + "docs": [ + "Account holding all market state" + ] + }, + { + "name": "traderTokenAccount", + "isMut": true, + "isSigner": false, + "docs": [ + "Trader token account" + ] + }, + { + "name": "vault", + "isMut": true, + "isSigner": false, + "docs": [ + "Vault PDA, seeds are [b'vault', market_address, mint_address]" + ] + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "Token program" + ] + }, + { + "name": "payer", + "isMut": true, + "isSigner": true, + "docs": [ + "Payer of rent and gas" + ] + }, + { + "name": "wrapperState", + "isMut": true, + "isSigner": false, + "docs": [ + "Wrapper state" + ] + }, + { + "name": "mint", + "isMut": false, + "isSigner": false, + "docs": [ + "Mint, needed for token 22" + ] + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "DepositParams" + } + } + ], + "discriminant": { + "type": "u8", + "value": 2 + } + }, + { + "name": "Withdraw", + "accounts": [ + { + "name": "manifestProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "Manifest program" + ] + }, + { + "name": "owner", + "isMut": true, + "isSigner": true, + "docs": [ + "Owner of the Manifest account" + ] + }, + { + "name": "market", + "isMut": true, + "isSigner": false, + "docs": [ + "Account holding all market state" + ] + }, + { + "name": "traderTokenAccount", + "isMut": true, + "isSigner": false, + "docs": [ + "Trader token account" + ] + }, + { + "name": "vault", + "isMut": true, + "isSigner": false, + "docs": [ + "Vault PDA, seeds are [b'vault', market_address, mint_address]" + ] + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "Token program" + ] + }, + { + "name": "payer", + "isMut": true, + "isSigner": true, + "docs": [ + "Payer of rent and gas" + ] + }, + { + "name": "wrapperState", + "isMut": true, + "isSigner": false, + "docs": [ + "Wrapper state" + ] + }, + { + "name": "mint", + "isMut": false, + "isSigner": false, + "docs": [ + "Mint, needed for token 22" + ] + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "WithdrawParams" + } + } + ], + "discriminant": { + "type": "u8", + "value": 3 + } + }, + { + "name": "BatchUpdate", + "accounts": [ + { + "name": "manifestProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "Manifest program" + ] + }, + { + "name": "owner", + "isMut": true, + "isSigner": true, + "docs": [ + "Owner of the Manifest account" + ] + }, + { + "name": "market", + "isMut": true, + "isSigner": false, + "docs": [ + "Account holding all market state" + ] + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "docs": [ + "System program" + ] + }, + { + "name": "payer", + "isMut": true, + "isSigner": true, + "docs": [ + "Payer of rent and gas" + ] + }, + { + "name": "wrapperState", + "isMut": true, + "isSigner": false, + "docs": [ + "Wrapper state" + ] + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": "WrapperBatchUpdateParams" + } + } + ], + "discriminant": { + "type": "u8", + "value": 4 + } + } + ], + "types": [ + { + "name": "WrapperPlaceOrderParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "clientOrderId", + "type": "u64" + }, + { + "name": "baseAtoms", + "type": "u64" + }, + { + "name": "priceMantissa", + "type": "u32" + }, + { + "name": "priceExponent", + "type": "i8" + }, + { + "name": "isBid", + "type": "bool" + }, + { + "name": "lastValidSlot", + "type": "u32" + }, + { + "name": "orderType", + "type": { + "defined": "OrderType" + } + }, + { + "name": "minOutAtoms", + "type": "u64" + } + ] + } + }, + { + "name": "WrapperCancelOrderParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "clientOrderId", + "type": "u64" + } + ] + } + }, + { + "name": "WrapperBatchUpdateParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "cancels", + "type": { + "vec": { + "defined": "WrapperCancelOrderParams" + } + } + }, + { + "name": "cancelAll", + "type": "bool" + }, + { + "name": "orders", + "type": { + "vec": { + "defined": "WrapperPlaceOrderParams" + } + } + }, + { + "name": "traderIndexHint", + "type": { + "option": "u32" + } + } + ] + } + }, + { + "name": "DepositParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "amountAtoms", + "type": "u64" + } + ] + } + }, + { + "name": "WithdrawParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "amountAtoms", + "type": "u64" + } + ] + } + }, + { + "name": "OrderType", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Limit" + }, + { + "name": "ImmediateOrCancel" + }, + { + "name": "PostOnly" + }, + { + "name": "PostOnlySlide" + }, + { + "name": "FillOrKill" + } + ] + } + } + ], + "metadata": { + "origin": "shank", + "address": "wMNFSTkir3HgyZTsB7uqu3i7FA73grFCptPXgrZjksL" + }, + "events": [] +} \ No newline at end of file diff --git a/client/idl/yarn.lock b/client/idl/yarn.lock new file mode 100644 index 000000000..610e4765b --- /dev/null +++ b/client/idl/yarn.lock @@ -0,0 +1,853 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/runtime@^7.24.7": + version "7.24.8" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.8.tgz#5d958c3827b13cc6d05e038c07fb2e5e3420d82e" + integrity sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA== + dependencies: + regenerator-runtime "^0.14.0" + +"@metaplex-foundation/beet-solana@^0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@metaplex-foundation/beet-solana/-/beet-solana-0.3.1.tgz#4b37cda5c7f32ffd2bdd8b3164edc05c6463ab35" + integrity sha512-tgyEl6dvtLln8XX81JyBvWjIiEcjTkUwZbrM5dIobTmoqMuGewSyk9CClno8qsMsFdB5T3jC91Rjeqmu/6xk2g== + dependencies: + "@metaplex-foundation/beet" ">=0.1.0" + "@solana/web3.js" "^1.56.2" + bs58 "^5.0.0" + debug "^4.3.4" + +"@metaplex-foundation/beet@>=0.1.0", "@metaplex-foundation/beet@^0.7.1": + version "0.7.2" + resolved "https://registry.yarnpkg.com/@metaplex-foundation/beet/-/beet-0.7.2.tgz#fa4726e4cfd4fb6fed6cddc9b5213c1c2a2d0b77" + integrity sha512-K+g3WhyFxKPc0xIvcIjNyV1eaTVJTiuaHZpig7Xx0MuYRMoJLLvhLTnUXhFdR5Tu2l2QSyKwfyXDgZlzhULqFg== + dependencies: + ansicolors "^0.3.2" + assert "^2.1.0" + bn.js "^5.2.0" + debug "^4.3.3" + +"@metaplex-foundation/rustbin@^0.3.0", "@metaplex-foundation/rustbin@^0.3.1": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@metaplex-foundation/rustbin/-/rustbin-0.3.5.tgz#56d028afd96c2b56ad3bbea22ff454adde900e8c" + integrity sha512-m0wkRBEQB/8krwMwKBvFugufZtYwMXiGHud2cTDAv+aGXK4M90y0Hx67/wpu+AqqoQfdV8VM9YezUOHKD+Z5kA== + dependencies: + debug "^4.3.3" + semver "^7.3.7" + text-table "^0.2.0" + toml "^3.0.0" + +"@metaplex-foundation/solita@^0.19.4": + version "0.19.4" + resolved "https://registry.yarnpkg.com/@metaplex-foundation/solita/-/solita-0.19.4.tgz#d73fc3eea0424927c8cab2117177704bd8818681" + integrity sha512-5j7F2qKDc7Po6ye7fm/JsUcYMxPK5QkkGBJJfgY7dGhF5YQw8YPwqw95F7rSavuDrXOK9mhq/L8s+3ilvEk6CA== + dependencies: + "@metaplex-foundation/beet" "^0.7.1" + "@metaplex-foundation/beet-solana" "^0.3.1" + "@metaplex-foundation/rustbin" "^0.3.0" + "@solana/web3.js" "^1.56.2" + ansi-colors "^4.1.3" + camelcase "^6.2.1" + debug "^4.3.3" + js-sha256 "^0.9.0" + prettier "^2.5.1" + snake-case "^3.0.4" + spok "^1.4.3" + +"@noble/curves@^1.4.2": + version "1.4.2" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.4.2.tgz#40309198c76ed71bc6dbf7ba24e81ceb4d0d1fe9" + integrity sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw== + dependencies: + "@noble/hashes" "1.4.0" + +"@noble/hashes@1.4.0", "@noble/hashes@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426" + integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg== + +"@solana/buffer-layout@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz#b996235eaec15b1e0b5092a8ed6028df77fa6c15" + integrity sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA== + dependencies: + buffer "~6.0.3" + +"@solana/web3.js@^1.56.2": + version "1.95.0" + resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.95.0.tgz#9cf08383e7dcba212a73d78349cf9b25bc34764f" + integrity sha512-iHwJ/HcWrF9qbnI1ctwI1UXHJ0vZXRpnt+lI5UcQIk8WvJNuQ5gV06icxzM6B7ojUES85Q1/FM4jZ49UQ8yZZQ== + dependencies: + "@babel/runtime" "^7.24.7" + "@noble/curves" "^1.4.2" + "@noble/hashes" "^1.4.0" + "@solana/buffer-layout" "^4.0.1" + agentkeepalive "^4.5.0" + bigint-buffer "^1.1.5" + bn.js "^5.2.1" + borsh "^0.7.0" + bs58 "^4.0.1" + buffer "6.0.3" + fast-stable-stringify "^1.0.0" + jayson "^4.1.0" + node-fetch "^2.7.0" + rpc-websockets "^9.0.2" + superstruct "^2.0.2" + +"@swc/helpers@^0.5.11": + version "0.5.11" + resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.11.tgz#5bab8c660a6e23c13b2d23fcd1ee44a2db1b0cb7" + integrity sha512-YNlnKRWF2sVojTpIyzwou9XoTNbzbzONwRhOoniEioF1AtaitTvVZblaQRrAzChWQ1bLYyYSWzM18y4WwgzJ+A== + dependencies: + tslib "^2.4.0" + +"@types/connect@^3.4.33": + version "3.4.38" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" + integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== + dependencies: + "@types/node" "*" + +"@types/node@*": + version "20.14.10" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.10.tgz#a1a218290f1b6428682e3af044785e5874db469a" + integrity sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ== + dependencies: + undici-types "~5.26.4" + +"@types/node@^12.12.54": + version "12.20.55" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" + integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== + +"@types/uuid@^8.3.4": + version "8.3.4" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" + integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== + +"@types/ws@^7.4.4": + version "7.4.7" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702" + integrity sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww== + dependencies: + "@types/node" "*" + +"@types/ws@^8.2.2": + version "8.5.10" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.10.tgz#4acfb517970853fa6574a3a6886791d04a396787" + integrity sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A== + dependencies: + "@types/node" "*" + +JSONStream@^1.3.5: + version "1.3.5" + resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" + integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== + dependencies: + jsonparse "^1.2.0" + through ">=2.2.7 <3" + +agentkeepalive@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923" + integrity sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew== + dependencies: + humanize-ms "^1.2.1" + +ansi-colors@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" + integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== + +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansicolors@^0.3.2, ansicolors@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979" + integrity sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg== + +assert@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/assert/-/assert-2.1.0.tgz#6d92a238d05dc02e7427c881fb8be81c8448b2dd" + integrity sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw== + dependencies: + call-bind "^1.0.2" + is-nan "^1.3.2" + object-is "^1.1.5" + object.assign "^4.1.4" + util "^0.12.5" + +available-typed-arrays@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" + integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== + dependencies: + possible-typed-array-names "^1.0.0" + +base-x@^3.0.2: + version "3.0.10" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.10.tgz#62de58653f8762b5d6f8d9fe30fa75f7b2585a75" + integrity sha512-7d0s06rR9rYaIWHkpfLIFICM/tkSVdoPC9qYAQRpxn9DdKNWNsKC0uk++akckyLq16Tx2WIinnZ6WRriAt6njQ== + dependencies: + safe-buffer "^5.0.1" + +base-x@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-4.0.0.tgz#d0e3b7753450c73f8ad2389b5c018a4af7b2224a" + integrity sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw== + +base-x@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-5.0.0.tgz#6d835ceae379130e1a4cb846a70ac4746f28ea9b" + integrity sha512-sMW3VGSX1QWVFA6l8U62MLKz29rRfpTlYdCqLdpLo1/Yd4zZwSbnUaDfciIAowAqvq7YFnWq9hrhdg1KYgc1lQ== + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +bigint-buffer@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/bigint-buffer/-/bigint-buffer-1.1.5.tgz#d038f31c8e4534c1f8d0015209bf34b4fa6dd442" + integrity sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA== + dependencies: + bindings "^1.3.0" + +bindings@^1.3.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + +bn.js@^5.2.0, bn.js@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" + integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== + +borsh@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/borsh/-/borsh-0.7.0.tgz#6e9560d719d86d90dc589bca60ffc8a6c51fec2a" + integrity sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA== + dependencies: + bn.js "^5.2.0" + bs58 "^4.0.0" + text-encoding-utf-8 "^1.0.2" + +bs58@^4.0.0, bs58@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" + integrity sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw== + dependencies: + base-x "^3.0.2" + +bs58@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-5.0.0.tgz#865575b4d13c09ea2a84622df6c8cbeb54ffc279" + integrity sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ== + dependencies: + base-x "^4.0.0" + +bs58@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-6.0.0.tgz#a2cda0130558535dd281a2f8697df79caaf425d8" + integrity sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw== + dependencies: + base-x "^5.0.0" + +buffer@6.0.3, buffer@^6.0.3, buffer@~6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + +bufferutil@^4.0.1: + version "4.0.8" + resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.8.tgz#1de6a71092d65d7766c4d8a522b261a6e787e8ea" + integrity sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw== + dependencies: + node-gyp-build "^4.3.0" + +call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" + +camelcase@^6.2.1: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +chalk@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +commander@^2.20.3: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commander@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" + integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== + +debug@^4.1.1, debug@^4.3.3, debug@^4.3.4: + version "4.3.5" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" + integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== + dependencies: + ms "2.1.2" + +define-data-property@^1.0.1, define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + +define-properties@^1.1.3, define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +delay@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/delay/-/delay-5.0.0.tgz#137045ef1b96e5071060dd5be60bf9334436bd1d" + integrity sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw== + +dot-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" + integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es6-promise@^4.0.3: + version "4.2.8" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== + +es6-promisify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" + integrity sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ== + dependencies: + es6-promise "^4.0.3" + +eventemitter3@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" + integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== + +eyes@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0" + integrity sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ== + +fast-stable-stringify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz#5c5543462b22aeeefd36d05b34e51c78cb86d313" + integrity sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag== + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + +find-process@^1.4.7: + version "1.4.7" + resolved "https://registry.yarnpkg.com/find-process/-/find-process-1.4.7.tgz#8c76962259216c381ef1099371465b5b439ea121" + integrity sha512-/U4CYp1214Xrp3u3Fqr9yNynUrr5Le4y0SsJh2lMDDSbpwYSz3M2SMWQC+wqcx79cN8PQtHQIL8KnuY9M66fdg== + dependencies: + chalk "^4.0.0" + commander "^5.1.0" + debug "^4.1.1" + +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +get-intrinsic@^1.1.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-proto@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" + integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== + +has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +hasown@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +humanize-ms@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" + integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ== + dependencies: + ms "^2.0.0" + +ieee754@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +inherits@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +is-arguments@^1.0.4: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-callable@^1.1.3: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-generator-function@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" + +is-nan@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d" + integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + +is-typed-array@^1.1.3: + version "1.1.13" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" + integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== + dependencies: + which-typed-array "^1.1.14" + +isomorphic-ws@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc" + integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== + +jayson@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/jayson/-/jayson-4.1.1.tgz#282ff13d3cea09776db684b7eeca98c47b2fa99a" + integrity sha512-5ZWm4Q/0DHPyeMfAsrwViwUS2DMVsQgWh8bEEIVTkfb3DzHZ2L3G5WUnF+AKmGjjM9r1uAv73SaqC1/U4RL45w== + dependencies: + "@types/connect" "^3.4.33" + "@types/node" "^12.12.54" + "@types/ws" "^7.4.4" + JSONStream "^1.3.5" + commander "^2.20.3" + delay "^5.0.0" + es6-promisify "^5.0.0" + eyes "^0.1.8" + isomorphic-ws "^4.0.1" + json-stringify-safe "^5.0.1" + uuid "^8.3.2" + ws "^7.5.10" + +js-sha256@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966" + integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA== + +json-stringify-safe@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== + +jsonparse@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" + integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== + +keccak256@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/keccak256/-/keccak256-1.0.6.tgz#dd32fb771558fed51ce4e45a035ae7515573da58" + integrity sha512-8GLiM01PkdJVGUhR1e6M/AvWnSqYS0HaERI+K/QtStGDGlSTx2B1zTqZk4Zlqu5TxHJNTxWAdP9Y+WI50OApUw== + dependencies: + bn.js "^5.2.0" + buffer "^6.0.3" + keccak "^3.0.2" + +keccak@^3.0.2: + version "3.0.4" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.4.tgz#edc09b89e633c0549da444432ecf062ffadee86d" + integrity sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q== + dependencies: + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + readable-stream "^3.6.0" + +lower-case@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" + integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== + dependencies: + tslib "^2.0.3" + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@^2.0.0: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +no-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" + integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== + dependencies: + lower-case "^2.0.2" + tslib "^2.0.3" + +node-addon-api@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" + integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== + +node-fetch@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + +node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: + version "4.8.1" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.1.tgz#976d3ad905e71b76086f4f0b0d3637fe79b6cda5" + integrity sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw== + +object-is@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.6.tgz#1a6a53aed2dd8f7e6775ff870bea58545956ab07" + integrity sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.4: + version "4.1.5" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" + integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== + dependencies: + call-bind "^1.0.5" + define-properties "^1.2.1" + has-symbols "^1.0.3" + object-keys "^1.1.1" + +possible-typed-array-names@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" + integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== + +prettier@^2.5.1: + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== + +prettier@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.2.tgz#03ff86dc7c835f2d2559ee76876a3914cec4a90a" + integrity sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA== + +readable-stream@^3.6.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +regenerator-runtime@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== + +rpc-websockets@^9.0.2: + version "9.0.2" + resolved "https://registry.yarnpkg.com/rpc-websockets/-/rpc-websockets-9.0.2.tgz#4c1568d00b8100f997379a363478f41f8f4b242c" + integrity sha512-YzggvfItxMY3Lwuax5rC18inhbjJv9Py7JXRHxTIi94JOLrqBsSsUUc5bbl5W6c11tXhdfpDPK0KzBhoGe8jjw== + dependencies: + "@swc/helpers" "^0.5.11" + "@types/uuid" "^8.3.4" + "@types/ws" "^8.2.2" + buffer "^6.0.3" + eventemitter3 "^5.0.1" + uuid "^8.3.2" + ws "^8.5.0" + optionalDependencies: + bufferutil "^4.0.1" + utf-8-validate "^5.0.2" + +safe-buffer@^5.0.1, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +semver@^7.3.7: + version "7.6.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" + integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== + +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + +snake-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c" + integrity sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg== + dependencies: + dot-case "^3.0.4" + tslib "^2.0.3" + +spok@^1.4.3: + version "1.5.5" + resolved "https://registry.yarnpkg.com/spok/-/spok-1.5.5.tgz#a51f7f290a53131d7b7a922dfedc461dda0aed72" + integrity sha512-IrJIXY54sCNFASyHPOY+jEirkiJ26JDqsGiI0Dvhwcnkl0PEWi1PSsrkYql0rzDw8LFVTcA7rdUCAJdE2HE+2Q== + dependencies: + ansicolors "~0.3.2" + find-process "^1.4.7" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +superstruct@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-2.0.2.tgz#3f6d32fbdc11c357deff127d591a39b996300c54" + integrity sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A== + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +text-encoding-utf-8@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz#585b62197b0ae437e3c7b5d0af27ac1021e10d13" + integrity sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg== + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +"through@>=2.2.7 <3": + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + +toml@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/toml/-/toml-3.0.0.tgz#342160f1af1904ec9d204d03a5d61222d762c5ee" + integrity sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w== + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + +tslib@^2.0.3, tslib@^2.4.0: + version "2.6.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" + integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +utf-8-validate@^5.0.2: + version "5.0.10" + resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.10.tgz#d7d10ea39318171ca982718b6b96a8d2442571a2" + integrity sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ== + dependencies: + node-gyp-build "^4.3.0" + +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +util@^0.12.5: + version "0.12.5" + resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc" + integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA== + dependencies: + inherits "^2.0.3" + is-arguments "^1.0.4" + is-generator-function "^1.0.7" + is-typed-array "^1.1.3" + which-typed-array "^1.1.2" + +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +which-typed-array@^1.1.14, which-typed-array@^1.1.2: + version "1.1.15" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" + integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.2" + +ws@^7.5.10: + version "7.5.10" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" + integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== + +ws@^8.5.0: + version "8.18.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" + integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== diff --git a/client/rust/Cargo.toml b/client/rust/Cargo.toml new file mode 100644 index 000000000..42e73a698 --- /dev/null +++ b/client/rust/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "manifest-jupiter" +version = "0.1.0" +edition = "2021" +repository = "https://github.com/CKS-systems/manifest" +authors = ["Britt Cyr "] +description = "Jupiter AMM interface for Manifest exchange" +license-file = "LICENSE" + +[dependencies] +anyhow = { workspace = true } +manifest = { path = "../../programs/manifest" } +hypertree = { path = "../../lib" } +jupiter-amm-interface = "0.1.1" +solana-sdk = { workspace = true } +solana-program = { workspace = true } +spl-token = { workspace = true } +spl-token-2022 = { workspace = true } +tokio = { workspace = true } \ No newline at end of file diff --git a/client/rust/README.md b/client/rust/README.md new file mode 100644 index 000000000..afd839b1a --- /dev/null +++ b/client/rust/README.md @@ -0,0 +1,9 @@ +# Manifest Client + +This module implements the `Amm` trait defined [here](https://github.com/jup-ag/rust-amm-implementation). + +### Testing + +``` +cargo test -- --nocapture +``` \ No newline at end of file diff --git a/client/rust/src/lib.rs b/client/rust/src/lib.rs new file mode 100644 index 000000000..7e17acbfc --- /dev/null +++ b/client/rust/src/lib.rs @@ -0,0 +1,806 @@ +use anyhow::{Error, Result}; +use jupiter_amm_interface::{ + AccountMap, Amm, KeyedAccount, Quote, QuoteParams, Side, Swap, SwapAndAccountMetas, SwapParams, +}; + +use manifest::{ + quantities::{BaseAtoms, QuoteAtoms, WrapperU64}, + state::{DynamicAccount, MarketFixed, MarketValue}, + validation::{get_global_address, get_global_vault_address, get_vault_address}, +}; +use hypertree::get_helper; +use solana_sdk::{instruction::AccountMeta, pubkey::Pubkey}; +use std::mem::size_of; + +#[derive(Clone)] +pub struct ManifestMarket { + market: MarketValue, + key: Pubkey, + label: String, + base_token_program: Pubkey, + quote_token_program: Pubkey, +} + +impl ManifestMarket { + pub fn get_base_mint(&self) -> Pubkey { + *self.market.get_base_mint() + } + pub fn get_quote_mint(&self) -> Pubkey { + *self.market.get_quote_mint() + } +} + +impl Amm for ManifestMarket { + fn label(&self) -> String { + self.label.clone() + } + + fn key(&self) -> Pubkey { + self.key + } + + fn program_id(&self) -> Pubkey { + manifest::id() + } + + fn get_reserve_mints(&self) -> Vec { + vec![self.get_base_mint(), self.get_quote_mint()] + } + + fn get_accounts_to_update(&self) -> Vec { + vec![self.key, self.get_base_mint(), self.get_quote_mint()] + } + + fn from_keyed_account(keyed_account: &KeyedAccount) -> Result { + let mut_data: &mut &[u8] = &mut keyed_account.account.data.as_slice(); + + let (header_bytes, dynamic_data) = mut_data.split_at(size_of::()); + let market_fixed: &MarketFixed = get_helper::(header_bytes, 0_u32); + + Ok(ManifestMarket { + market: DynamicAccount::> { + fixed: *market_fixed, + dynamic: dynamic_data.to_vec(), + }, + key: keyed_account.key, + label: "Manifest".into(), + // Gets updated on the first iter + base_token_program: spl_token::id(), + quote_token_program: spl_token::id(), + }) + } + + fn update(&mut self, account_map: &AccountMap) -> Result<()> { + if let Some(mint) = account_map.get(&self.get_base_mint()) { + self.base_token_program = mint.owner; + }; + if let Some(mint) = account_map.get(&self.get_quote_mint()) { + self.quote_token_program = mint.owner; + }; + + let market_account: &solana_sdk::account::Account = account_map.get(&self.key).unwrap(); + + let (header_bytes, dynamic_data) = market_account.data.split_at(size_of::()); + let market_fixed: &MarketFixed = get_helper::(header_bytes, 0_u32); + self.market = DynamicAccount::> { + fixed: *market_fixed, + dynamic: dynamic_data.to_vec(), + }; + Ok(()) + } + + fn quote(&self, quote_params: &QuoteParams) -> Result { + let market: DynamicAccount> = self.market.clone(); + let out_amount: u64 = if quote_params.input_mint == self.get_base_mint() { + let in_atoms: BaseAtoms = BaseAtoms::new(quote_params.in_amount); + market.impact_quote_atoms(false, in_atoms)?.as_u64() + } else { + let in_atoms: QuoteAtoms = QuoteAtoms::new(quote_params.in_amount); + market.impact_base_atoms(true, true, in_atoms)?.as_u64() + }; + Ok(Quote { + out_amount, + ..Quote::default() + }) + } + + fn get_swap_and_account_metas(&self, swap_params: &SwapParams) -> Result { + let SwapParams { + destination_mint, + source_mint, + user_destination_token_account, + user_source_token_account, + user_transfer_authority, + .. + } = swap_params; + + let (side, base_account, quote_account) = if source_mint == &self.get_base_mint() { + if destination_mint != &self.get_quote_mint() { + return Err(Error::msg("Invalid quote mint")); + } + ( + Side::Ask, + user_source_token_account, + user_destination_token_account, + ) + } else { + if destination_mint != &self.get_base_mint() { + return Err(Error::msg("Invalid base mint")); + } + ( + Side::Bid, + user_destination_token_account, + user_source_token_account, + ) + }; + + let (base_vault, _base_bump) = get_vault_address(&self.key, &self.get_base_mint()); + let (quote_vault, _quote_bump) = get_vault_address(&self.key, &self.get_quote_mint()); + let (global, _global_bump) = get_global_address(destination_mint); + let (global_vault, _global_vault_bump) = get_global_vault_address(destination_mint); + + let account_metas: Vec = vec![ + AccountMeta::new_readonly(manifest::id(), false), + AccountMeta::new(*user_transfer_authority, true), + AccountMeta::new(self.key, false), + AccountMeta::new(*base_account, false), + AccountMeta::new(*quote_account, false), + AccountMeta::new(base_vault, false), + AccountMeta::new(quote_vault, false), + AccountMeta::new_readonly(self.base_token_program, false), + AccountMeta::new_readonly(self.get_base_mint(), false), + AccountMeta::new_readonly(self.quote_token_program, false), + AccountMeta::new_readonly(self.get_quote_mint(), false), + AccountMeta::new(global, false), + AccountMeta::new(global_vault, false), + ]; + + Ok(SwapAndAccountMetas { + swap: Swap::Openbook { side }, + account_metas, + }) + } + + fn clone_amm(&self) -> Box { + Box::new(self.clone()) + } + + fn has_dynamic_accounts(&self) -> bool { + false + } + + fn get_user_setup(&self) -> Option { + None + } + + fn unidirectional(&self) -> bool { + false + } + + fn program_dependencies(&self) -> Vec<(Pubkey, String)> { + std::vec![] + } + + fn get_accounts_len(&self) -> usize { + // 1 Program + // 2 Market + // 3 Signer + // 4 User Base + // 5 User Quote + // 6 Vault Base + // 7 Vault Quote + // 8 Base Token Program + // 9 Base Mint + // 10 Quote Token Program + // 11 Quote Mint + // 12 Global + // 13 Global Vault + 13 + } +} + +#[cfg(test)] +mod test { + use super::*; + use manifest::{ + quantities::{BaseAtoms, GlobalAtoms}, + state::{ + constants::NO_EXPIRATION_LAST_VALID_SLOT, AddOrderToMarketArgs, GlobalFixed, GlobalValue, OrderType, BLOCK_SIZE, GLOBAL_FIXED_SIZE, MARKET_FIXED_SIZE + }, + validation::{ + loaders::GlobalTradeAccounts, ManifestAccountInfo, MintAccountInfo, TokenAccountInfo, + TokenProgram, + }, + }; + use hypertree::{get_mut_helper, trace, DataIndex}; + use solana_sdk::{account::Account, account_info::AccountInfo}; + use spl_token::state::Mint; + use std::{cell::RefCell, collections::HashMap, rc::Rc, str::FromStr}; + + #[test] + fn test_jupiter_local() { + let base_mint_key: Pubkey = + Pubkey::from_str("So11111111111111111111111111111111111111112").unwrap(); + let quote_mint_key: Pubkey = + Pubkey::from_str("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v").unwrap(); + + let mut lamports: u64 = 0; + let base_mint: MintAccountInfo = MintAccountInfo { + mint: Mint { + mint_authority: None.into(), + supply: 0, + decimals: 6, + is_initialized: true, + freeze_authority: None.into(), + }, + info: &AccountInfo { + key: &base_mint_key, + lamports: Rc::new(RefCell::new(&mut lamports)), + data: Rc::new(RefCell::new(&mut [])), + owner: &Pubkey::new_unique(), + rent_epoch: 0, + is_signer: false, + is_writable: false, + executable: false, + }, + }; + + let mut lamports: u64 = 0; + let quote_mint: MintAccountInfo = MintAccountInfo { + mint: Mint { + mint_authority: None.into(), + supply: 0, + decimals: 9, + is_initialized: true, + freeze_authority: None.into(), + }, + info: &AccountInfo { + key: "e_mint_key, + lamports: Rc::new(RefCell::new(&mut lamports)), + data: Rc::new(RefCell::new(&mut [])), + owner: &Pubkey::new_unique(), + rent_epoch: 0, + is_signer: false, + is_writable: false, + executable: false, + }, + }; + + let market_key: Pubkey = + Pubkey::from_str("GPPda3ZQZannxp3AK8bSishVqvhHAxogiWdhw1mvmoZr").unwrap(); + + let mut market_value: DynamicAccount> = MarketValue { + fixed: MarketFixed::new_empty(&base_mint, "e_mint, &market_key), + // 4 because 1 extra, 1 seat, 2 orders. + dynamic: vec![0; BLOCK_SIZE * 4], + }; + let trader_key: Pubkey = + Pubkey::from_str("GCtjtH2ehL6BZTjismuZ8JhQnuM6U3bmtxVoFyiHMHGc").unwrap(); + market_value.market_expand().unwrap(); + market_value.claim_seat(&trader_key).unwrap(); + let trader_index: DataIndex = market_value.get_trader_index(&trader_key); + market_value + .deposit(&trader_key, 1_000_000_000_000, true) + .unwrap(); + market_value + .deposit(&trader_key, 1_000_000_000_000, false) + .unwrap(); + + // Bid for 10 SOL + market_value.market_expand().unwrap(); + market_value + .place_order(AddOrderToMarketArgs { + market: market_key, + trader_index, + num_base_atoms: BaseAtoms::new(10_000), + price: 0.150.try_into().unwrap(), + is_bid: true, + last_valid_slot: NO_EXPIRATION_LAST_VALID_SLOT, + order_type: OrderType::Limit, + global_trade_accounts_opts: &[None, None], + }) + .unwrap(); + + // Ask 10 SOL + market_value.market_expand().unwrap(); + market_value + .place_order(AddOrderToMarketArgs { + market: market_key, + trader_index, + num_base_atoms: BaseAtoms::new(10_000), + price: 0.180.try_into().unwrap(), + is_bid: false, + last_valid_slot: NO_EXPIRATION_LAST_VALID_SLOT, + order_type: OrderType::Limit, + global_trade_accounts_opts: &[None, None], + }) + .unwrap(); + + let mut header_bytes: [u8; MARKET_FIXED_SIZE] = [0; MARKET_FIXED_SIZE]; + *get_mut_helper::(&mut header_bytes, 0_u32) = market_value.fixed; + + let mut data_vec: Vec = Vec::new(); + data_vec.extend_from_slice(&header_bytes); + data_vec.append(&mut market_value.dynamic); + + let account: Account = Account { + lamports: 0, + data: data_vec, + owner: manifest::id(), + executable: false, + rent_epoch: 0, + }; + + let market_account: KeyedAccount = KeyedAccount { + key: market_key, + account: account.clone(), + params: None, + }; + + let mut manifest_market: ManifestMarket = + ManifestMarket::from_keyed_account(&market_account).unwrap(); + + let accounts_map: AccountMap = HashMap::from([(market_key, account)]); + + manifest_market.update(&accounts_map).unwrap(); + + let (base_mint, quote_mint) = { + let reserves: Vec = manifest_market.get_reserve_mints(); + (reserves[0], reserves[1]) + }; + + // Ask for 1 SOL, Bid for 180 USDC + for (side, in_amount) in [(Side::Ask, 1_000_000_000), (Side::Bid, 180_000_000)] { + let (input_mint, output_mint) = match side { + Side::Ask => (base_mint, quote_mint), + Side::Bid => (quote_mint, base_mint), + }; + + let quote_params: QuoteParams = QuoteParams { + in_amount, + input_mint, + output_mint, + }; + + let quote: Quote = manifest_market.quote("e_params).unwrap(); + + trace!("{:#?}", quote_params); + trace!("{:#?}", quote); + + match side { + Side::Ask => { + assert_eq!(quote.out_amount, 1_500); + } + Side::Bid => { + assert_eq!(quote.out_amount, 10_000); + } + }; + } + } + + #[test] + fn test_jupiter_other() { + let base_mint_key: Pubkey = + Pubkey::from_str("So11111111111111111111111111111111111111112").unwrap(); + let quote_mint_key: Pubkey = + Pubkey::from_str("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v").unwrap(); + + let mut lamports: u64 = 0; + let base_mint: MintAccountInfo = MintAccountInfo { + mint: Mint { + mint_authority: None.into(), + supply: 0, + decimals: 6, + is_initialized: true, + freeze_authority: None.into(), + }, + info: &AccountInfo { + key: &base_mint_key, + lamports: Rc::new(RefCell::new(&mut lamports)), + data: Rc::new(RefCell::new(&mut [])), + owner: &Pubkey::new_unique(), + rent_epoch: 0, + is_signer: false, + is_writable: false, + executable: false, + }, + }; + + let mut lamports: u64 = 0; + let quote_mint: MintAccountInfo = MintAccountInfo { + mint: Mint { + mint_authority: None.into(), + supply: 0, + decimals: 9, + is_initialized: true, + freeze_authority: None.into(), + }, + info: &AccountInfo { + key: "e_mint_key, + lamports: Rc::new(RefCell::new(&mut lamports)), + data: Rc::new(RefCell::new(&mut [])), + owner: &Pubkey::new_unique(), + rent_epoch: 0, + is_signer: false, + is_writable: false, + executable: false, + }, + }; + + let market_key: Pubkey = + Pubkey::from_str("GPPda3ZQZannxp3AK8bSishVqvhHAxogiWdhw1mvmoZr").unwrap(); + + let mut market_value: DynamicAccount> = MarketValue { + fixed: MarketFixed::new_empty(&base_mint, "e_mint, &market_key), + // 4 because 1 extra, 1 seat, 2 orders. + dynamic: vec![0; BLOCK_SIZE * 4], + }; + let trader_key: Pubkey = + Pubkey::from_str("GCtjtH2ehL6BZTjismuZ8JhQnuM6U3bmtxVoFyiHMHGc").unwrap(); + market_value.market_expand().unwrap(); + market_value.claim_seat(&trader_key).unwrap(); + let trader_index: DataIndex = market_value.get_trader_index(&trader_key); + market_value + .deposit(&trader_key, 1_000_000_000_000, true) + .unwrap(); + market_value + .deposit(&trader_key, 1_000_000_000_000, false) + .unwrap(); + + // Bid for 10 SOL + market_value.market_expand().unwrap(); + market_value + .place_order(AddOrderToMarketArgs { + market: market_key, + trader_index, + num_base_atoms: BaseAtoms::new(10_000), + price: 0.150.try_into().unwrap(), + is_bid: true, + last_valid_slot: NO_EXPIRATION_LAST_VALID_SLOT, + order_type: OrderType::Limit, + global_trade_accounts_opts: &[None, None], + }) + .unwrap(); + + // Ask 10 SOL + market_value.market_expand().unwrap(); + market_value + .place_order(AddOrderToMarketArgs { + market: market_key, + trader_index, + num_base_atoms: BaseAtoms::new(10_000), + price: 0.180.try_into().unwrap(), + is_bid: false, + last_valid_slot: NO_EXPIRATION_LAST_VALID_SLOT, + order_type: OrderType::Limit, + global_trade_accounts_opts: &[None, None], + }) + .unwrap(); + + let mut header_bytes: [u8; MARKET_FIXED_SIZE] = [0; MARKET_FIXED_SIZE]; + *get_mut_helper::(&mut header_bytes, 0_u32) = market_value.fixed; + + let mut data_vec: Vec = Vec::new(); + data_vec.extend_from_slice(&header_bytes); + data_vec.append(&mut market_value.dynamic); + + let account: Account = Account { + lamports: 0, + data: data_vec, + owner: manifest::id(), + executable: false, + rent_epoch: 0, + }; + + let market_account: KeyedAccount = KeyedAccount { + key: market_key, + account: account.clone(), + params: None, + }; + + let manifest_market: ManifestMarket = + ManifestMarket::from_keyed_account(&market_account).unwrap(); + + assert_eq!(manifest_market.get_accounts_len(), 13); + assert_eq!(manifest_market.label(), "Manifest"); + assert_eq!(manifest_market.key(), market_key); + assert_eq!(manifest_market.program_id(), manifest::id()); + assert_eq!(manifest_market.get_reserve_mints()[0], base_mint_key); + assert_eq!(manifest_market.get_accounts_to_update().len(), 3); + + let swap_params: SwapParams = SwapParams { + in_amount: 1, + source_mint: manifest_market.get_base_mint(), + destination_mint: manifest_market.get_quote_mint(), + user_source_token_account: manifest_market.get_base_mint(), + user_destination_token_account: manifest_market.get_quote_mint(), + user_transfer_authority: trader_key, + open_order_address: None, + quote_mint_to_referrer: None, + jupiter_program_id: &manifest::id(), + }; + + let _results_forward: SwapAndAccountMetas = manifest_market + .get_swap_and_account_metas(&swap_params) + .unwrap(); + + let swap_params: SwapParams = SwapParams { + in_amount: 1, + source_mint: manifest_market.get_quote_mint(), + destination_mint: manifest_market.get_base_mint(), + user_source_token_account: manifest_market.get_base_mint(), + user_destination_token_account: manifest_market.get_quote_mint(), + user_transfer_authority: trader_key, + open_order_address: None, + quote_mint_to_referrer: None, + jupiter_program_id: &manifest::id(), + }; + + let _results_backward: SwapAndAccountMetas = manifest_market + .get_swap_and_account_metas(&swap_params) + .unwrap(); + + manifest_market.clone_amm(); + assert!(!manifest_market.has_dynamic_accounts()); + assert!(manifest_market.get_user_setup().is_none()); + assert!(!manifest_market.unidirectional()); + assert_eq!(manifest_market.program_dependencies().len(), 0); + } + + #[test] + fn test_jupiter_global_22() { + let base_mint_key: Pubkey = + Pubkey::from_str("So11111111111111111111111111111111111111112").unwrap(); + // pyusd + let quote_mint_key: Pubkey = + Pubkey::from_str("2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo").unwrap(); + + let mut base_mint_lamports: u64 = 0; + let base_mint: MintAccountInfo = MintAccountInfo { + mint: Mint { + mint_authority: None.into(), + supply: 0, + decimals: 9, + is_initialized: true, + freeze_authority: None.into(), + }, + info: &AccountInfo { + key: &base_mint_key, + lamports: Rc::new(RefCell::new(&mut base_mint_lamports)), + data: Rc::new(RefCell::new(&mut [])), + owner: &Pubkey::new_unique(), + rent_epoch: 0, + is_signer: false, + is_writable: false, + executable: false, + }, + }; + + let mut quote_mint_lamports: u64 = 0; + let quote_mint_info: MintAccountInfo = MintAccountInfo { + mint: Mint { + mint_authority: None.into(), + supply: 0, + decimals: 6, + is_initialized: true, + freeze_authority: None.into(), + }, + info: &AccountInfo { + key: "e_mint_key, + lamports: Rc::new(RefCell::new(&mut quote_mint_lamports)), + data: Rc::new(RefCell::new(&mut [])), + owner: &spl_token_2022::id(), + rent_epoch: 0, + is_signer: false, + is_writable: false, + executable: false, + }, + }; + let quote_mint_account: Account = Account { + lamports: 0, + data: Vec::new(), + owner: spl_token_2022::id(), + executable: false, + rent_epoch: 0, + }; + + let market_key: Pubkey = + Pubkey::from_str("GPPda3ZQZannxp3AK8bSishVqvhHAxogiWdhw1mvmoZr").unwrap(); + + let mut market_value: DynamicAccount> = MarketValue { + fixed: MarketFixed::new_empty(&base_mint, "e_mint_info, &market_key), + // 4 because 1 extra, 1 seat, 2 orders. + dynamic: vec![0; BLOCK_SIZE * 4], + }; + let trader_key: Pubkey = + Pubkey::from_str("GCtjtH2ehL6BZTjismuZ8JhQnuM6U3bmtxVoFyiHMHGc").unwrap(); + market_value.market_expand().unwrap(); + market_value.claim_seat(&trader_key).unwrap(); + + let manifest_id: Pubkey = manifest::id(); + let global_key: Pubkey = get_global_address("e_mint_key).0; + let mut global_lamports: u64 = 0; + let mut global_data_vec: Vec = Vec::new(); + let global_account_info: AccountInfo = { + let mut global_value: DynamicAccount> = GlobalValue { + fixed: GlobalFixed::new_empty("e_mint_key), + dynamic: vec![0; BLOCK_SIZE * 2], + }; + global_value.global_expand().unwrap(); + global_value.add_trader(&trader_key).unwrap(); + global_value.global_expand().unwrap(); + global_value + .claim_seat_on_market(&trader_key, &market_key) + .unwrap(); + global_value + .deposit_global(&trader_key, GlobalAtoms::new(1_000_000_000_000)) + .unwrap(); + let mut header_bytes: [u8; GLOBAL_FIXED_SIZE] = [0; GLOBAL_FIXED_SIZE]; + *get_mut_helper::(&mut header_bytes, 0_u32) = global_value.fixed; + global_data_vec.extend_from_slice(&header_bytes); + global_data_vec.append(&mut global_value.dynamic); + let global_account_info: AccountInfo = AccountInfo { + key: &global_key, + lamports: Rc::new(RefCell::new(&mut global_lamports)), + data: Rc::new(RefCell::new(&mut global_data_vec)), + owner: &manifest_id, + rent_epoch: 0, + is_signer: false, + is_writable: false, + executable: false, + }; + global_account_info + }; + let mut global_vault_lamports: u64 = 0; + let mut quote_mint_key_bytes: Vec = Vec::from(quote_mint_key.as_ref()); + let global_vault_account_info: AccountInfo = AccountInfo { + key: &get_global_vault_address("e_mint_key).0, + lamports: Rc::new(RefCell::new(&mut global_vault_lamports)), + data: Rc::new(RefCell::new(&mut quote_mint_key_bytes)), + owner: &spl_token_2022::id(), + rent_epoch: 0, + is_signer: false, + is_writable: false, + executable: false, + }; + let mut quote_mint_key_bytes: Vec = Vec::from(quote_mint_key.as_ref()); + let mut market_vault_lamports: u64 = 0; + let market_vault_account_info: AccountInfo = AccountInfo { + key: &get_vault_address(&market_key, "e_mint_key).0, + lamports: Rc::new(RefCell::new(&mut market_vault_lamports)), + data: Rc::new(RefCell::new(&mut quote_mint_key_bytes)), + owner: &spl_token_2022::id(), + rent_epoch: 0, + is_signer: false, + is_writable: false, + executable: false, + }; + + let mut token_program_lamports: u64 = 0; + let token_program_account_info: AccountInfo = AccountInfo { + key: &spl_token_2022::id(), + lamports: Rc::new(RefCell::new(&mut token_program_lamports)), + data: Rc::new(RefCell::new(&mut [])), + owner: &spl_token_2022::id(), + rent_epoch: 0, + is_signer: false, + is_writable: false, + executable: false, + }; + + market_value + .deposit(&trader_key, 1_000_000_000_000, true) + .unwrap(); + market_value + .deposit(&trader_key, 1_000_000_000_000, false) + .unwrap(); + + // Bid for 10 SOL + market_value.market_expand().unwrap(); + + let quote_global_trade_account: GlobalTradeAccounts = GlobalTradeAccounts { + mint: Some(quote_mint_info.clone()), + global: ManifestAccountInfo::new(&global_account_info).unwrap(), + global_vault: TokenAccountInfo::new(&global_vault_account_info, "e_mint_key) + .unwrap(), + market_vault: TokenAccountInfo::new(&market_vault_account_info, "e_mint_key) + .unwrap(), + token_program: TokenProgram::new(&token_program_account_info).unwrap(), + trader: trader_key.clone(), + market: market_key.clone(), + }; + + let trader_index: DataIndex = market_value.get_trader_index(&trader_key); + market_value + .place_order(AddOrderToMarketArgs { + market: market_key, + trader_index, + num_base_atoms: BaseAtoms::new(10_000), + price: 0.150.try_into().unwrap(), + is_bid: true, + last_valid_slot: NO_EXPIRATION_LAST_VALID_SLOT, + order_type: OrderType::Global, + global_trade_accounts_opts: &[None, Some(quote_global_trade_account)], + }) + .unwrap(); + + // Ask 10 SOL + market_value.market_expand().unwrap(); + market_value + .place_order(AddOrderToMarketArgs { + market: market_key, + trader_index, + num_base_atoms: BaseAtoms::new(10_000), + price: 0.180.try_into().unwrap(), + last_valid_slot: NO_EXPIRATION_LAST_VALID_SLOT, + order_type: OrderType::Limit, + global_trade_accounts_opts: &[None, None], + is_bid: false, + }) + .unwrap(); + + let mut header_bytes: [u8; MARKET_FIXED_SIZE] = [0; MARKET_FIXED_SIZE]; + *get_mut_helper::(&mut header_bytes, 0_u32) = market_value.fixed; + + let mut data_vec: Vec = Vec::new(); + data_vec.extend_from_slice(&header_bytes); + data_vec.append(&mut market_value.dynamic); + + let market_account: Account = Account { + lamports: 0, + data: data_vec, + owner: manifest::id(), + executable: false, + rent_epoch: 0, + }; + + let market_keyed_account: KeyedAccount = KeyedAccount { + key: market_key, + account: market_account.clone(), + params: None, + }; + + let mut manifest_market: ManifestMarket = + ManifestMarket::from_keyed_account(&market_keyed_account).unwrap(); + + let accounts_map: AccountMap = HashMap::from([ + (market_key, market_account), + (quote_mint_key, quote_mint_account), + ]); + + manifest_market.update(&accounts_map).unwrap(); + + let (base_mint, quote_mint) = { + let reserves: Vec = manifest_market.get_reserve_mints(); + (reserves[0], reserves[1]) + }; + + // Ask for 1 SOL, Bid for 180 USDC + for (side, in_amount) in [(Side::Ask, 1_000_000_000), (Side::Bid, 180_000_000)] { + let (input_mint, output_mint) = match side { + Side::Ask => (base_mint, quote_mint), + Side::Bid => (quote_mint, base_mint), + }; + + let quote_params: QuoteParams = QuoteParams { + in_amount, + input_mint, + output_mint, + }; + + let quote: Quote = manifest_market.quote("e_params).unwrap(); + + trace!("{:#?}", quote_params); + trace!("{:#?}", quote); + + match side { + Side::Ask => { + assert_eq!(quote.out_amount, 1_500); + } + Side::Bid => { + assert_eq!(quote.out_amount, 10_000); + } + }; + } + } +} diff --git a/client/ts/src/client.ts b/client/ts/src/client.ts new file mode 100644 index 000000000..dcfacf4af --- /dev/null +++ b/client/ts/src/client.ts @@ -0,0 +1,495 @@ +import { bignum } from '@metaplex-foundation/beet'; +import { + PublicKey, + Connection, + Keypair, + TransactionInstruction, + SystemProgram, + Transaction, + sendAndConfirmTransaction, + GetProgramAccountsResponse, + AccountInfo, +} from '@solana/web3.js'; +import { + Mint, + TOKEN_2022_PROGRAM_ID, + TOKEN_PROGRAM_ID, + getAssociatedTokenAddressSync, + getMint, +} from '@solana/spl-token'; +import { + createCreateMarketInstruction, + createSwapInstruction, +} from './manifest/instructions'; +import { OrderType, SwapParams } from './manifest/types'; +import { Market } from './market'; +import { MarketInfoParsed, Wrapper, WrapperData } from './wrapperObj'; +import { PROGRAM_ID as MANIFEST_PROGRAM_ID } from './manifest'; +import { + PROGRAM_ID as WRAPPER_PROGRAM_ID, + WrapperCancelOrderParams, + WrapperPlaceOrderParams, + createBatchUpdateInstruction, + createClaimSeatInstruction, + createCreateWrapperInstruction, + createDepositInstruction, + createWithdrawInstruction, +} from './wrapper'; +import { FIXED_WRAPPER_HEADER_SIZE } from './constants'; +import { getVaultAddress } from './utils/market'; + +export class ManifestClient { + private constructor( + public connection: Connection, + public wrapper: Wrapper, + public market: Market, + private payer: PublicKey, + private baseMint: Mint, + private quoteMint: Mint, + ) {} + + /** + * Create a new client which creates a wrapper and claims seat if needed. + * + * @param connection Connection + * @param marketPk PublicKey of the market + * @param payerKeypair Keypair of the trader + * + * @returns ManifestClient + */ + public static async getClientForMarket( + connection: Connection, + marketPk: PublicKey, + payerKeypair: Keypair, + ): Promise { + const marketObject: Market = await Market.loadFromAddress({ + connection: connection, + address: marketPk, + }); + const baseMintPk: PublicKey = marketObject.baseMint(); + const quoteMintPk: PublicKey = marketObject.quoteMint(); + // TODO: Maybe update for token22. + const baseMint: Mint = await getMint(connection, baseMintPk); + const quoteMint: Mint = await getMint(connection, quoteMintPk); + + const existingWrappers: GetProgramAccountsResponse = + await connection.getProgramAccounts(WRAPPER_PROGRAM_ID, { + filters: [ + // Dont check discriminant since there is only one type of account. + { + memcmp: { + offset: 8, + encoding: 'base58', + bytes: payerKeypair.publicKey.toBase58(), + }, + }, + ], + }); + const transaction: Transaction = new Transaction(); + if (existingWrappers.length == 0) { + const wrapperKeypair: Keypair = Keypair.generate(); + const createAccountIx: TransactionInstruction = + SystemProgram.createAccount({ + fromPubkey: payerKeypair.publicKey, + newAccountPubkey: wrapperKeypair.publicKey, + space: FIXED_WRAPPER_HEADER_SIZE, + lamports: await connection.getMinimumBalanceForRentExemption( + FIXED_WRAPPER_HEADER_SIZE, + ), + programId: WRAPPER_PROGRAM_ID, + }); + const createWrapperIx: TransactionInstruction = + createCreateWrapperInstruction({ + owner: payerKeypair.publicKey, + payer: payerKeypair.publicKey, + wrapperState: wrapperKeypair.publicKey, + }); + const claimSeatIx: TransactionInstruction = createClaimSeatInstruction({ + manifestProgram: MANIFEST_PROGRAM_ID, + owner: payerKeypair.publicKey, + market: marketPk, + payer: payerKeypair.publicKey, + wrapperState: wrapperKeypair.publicKey, + }); + transaction.add(createAccountIx); + transaction.add(createWrapperIx); + transaction.add(claimSeatIx); + + await sendAndConfirmTransaction( + connection, + transaction, + [payerKeypair, wrapperKeypair], + { + commitment: 'finalized', + }, + ); + const wrapper = await Wrapper.loadFromAddress({ + connection, + address: wrapperKeypair.publicKey, + }); + + return new ManifestClient( + connection, + wrapper, + marketObject, + payerKeypair.publicKey, + baseMint, + quoteMint, + ); + } + + const wrapperResponse: Readonly<{ + account: AccountInfo; + pubkey: PublicKey; + }> = existingWrappers[0]; + // Otherwise there is an existing wrapper + const wrapperData: WrapperData = Wrapper.deserializeWrapperBuffer( + wrapperResponse.account.data, + ); + const existingMarketInfos: MarketInfoParsed[] = + wrapperData.marketInfos.filter((marketInfo: MarketInfoParsed) => { + return marketInfo.market.toBase58() == marketPk.toBase58(); + }); + if (existingMarketInfos.length > 0) { + const wrapper = await Wrapper.loadFromAddress({ + connection, + address: wrapperResponse.pubkey, + }); + return new ManifestClient( + connection, + wrapper, + marketObject, + payerKeypair.publicKey, + baseMint, + quoteMint, + ); + } + + // There is a wrapper, but need to claim a seat. + const claimSeatIx: TransactionInstruction = createClaimSeatInstruction({ + manifestProgram: MANIFEST_PROGRAM_ID, + owner: payerKeypair.publicKey, + market: marketPk, + payer: payerKeypair.publicKey, + wrapperState: wrapperResponse.pubkey, + }); + transaction.add(claimSeatIx); + await sendAndConfirmTransaction(connection, transaction, [payerKeypair]); + const wrapper = await Wrapper.loadFromAddress({ + connection, + address: wrapperResponse.pubkey, + }); + return new ManifestClient( + connection, + wrapper, + marketObject, + payerKeypair.publicKey, + baseMint, + quoteMint, + ); + } + + /** + * Reload the market and wrapper objects. + */ + public async reload(): Promise { + await Promise.all([ + this.wrapper.reload(this.connection), + this.market.reload(this.connection), + ]); + } + + /** + * CreateMarket instruction. Assumes the account is already funded onchain. + * + * @param payer PublicKey of the trader + * @param baseMint PublicKey of the baseMint + * @param quoteMint PublicKey of the quoteMint + * @param market PublicKey of the market that will be created. Private key + * will need to be a signer. + * + * @returns TransactionInstruction + */ + public static createMarketIx( + payer: PublicKey, + baseMint: PublicKey, + quoteMint: PublicKey, + market: PublicKey, + ): TransactionInstruction { + const baseVault: PublicKey = getVaultAddress(market, baseMint); + const quoteVault: PublicKey = getVaultAddress(market, quoteMint); + return createCreateMarketInstruction({ + payer, + market, + baseVault, + quoteVault, + baseMint, + quoteMint, + tokenProgram22: TOKEN_2022_PROGRAM_ID, + }); + } + + /** + * Deposit instruction + * + * @param payer PublicKey of the trader + * @param mint PublicKey for deposit mint. Must be either the base or quote + * @param amountAtoms Number of atoms to deposit. + * + * @returns TransactionInstruction + */ + public depositIx( + payer: PublicKey, + mint: PublicKey, + amountAtoms: number, + ): TransactionInstruction { + const vault: PublicKey = getVaultAddress(this.market.address, mint); + const traderTokenAccount: PublicKey = getAssociatedTokenAddressSync( + mint, + payer, + ); + return createDepositInstruction( + { + payer, + market: this.market.address, + traderTokenAccount, + vault, + manifestProgram: MANIFEST_PROGRAM_ID, + owner: this.payer, + wrapperState: this.wrapper.address, + mint, + }, + { + params: { + amountAtoms, + }, + }, + ); + } + + /** + * Withdraw instruction + * + * @param payer PublicKey of the trader + * @param market PublicKey of the market + * @param mint PublicKey for withdraw mint. Must be either the base or quote + * @param amountAtoms Number of atoms to withdraw. + * + * @returns TransactionInstruction + */ + public withdrawIx( + payer: PublicKey, + mint: PublicKey, + amountAtoms: number, + ): TransactionInstruction { + const vault: PublicKey = getVaultAddress(this.market.address, mint); + const traderTokenAccount: PublicKey = getAssociatedTokenAddressSync( + mint, + payer, + ); + return createWithdrawInstruction( + { + payer, + market: this.market.address, + traderTokenAccount, + vault, + manifestProgram: MANIFEST_PROGRAM_ID, + owner: this.payer, + wrapperState: this.wrapper.address, + mint, + }, + { + params: { + amountAtoms, + }, + }, + ); + } + + /** + * PlaceOrder instruction + * + * @param params PlaceOrderParamsExternal including all the information for + * placing an order like amount, price, ordertype, ... This is called external + * because to avoid conflicts with the autogenerated version which has + * problems with expressing some of the parameters. + * + * @returns TransactionInstruction + */ + public placeOrderIx( + params: WrapperPlaceOrderParamsExternal, + ): TransactionInstruction { + return createBatchUpdateInstruction( + { + payer: this.payer, + market: this.market.address, + manifestProgram: MANIFEST_PROGRAM_ID, + owner: this.payer, + wrapperState: this.wrapper.address, + }, + { + params: { + cancels: [], + cancelAll: false, + orders: [toWrapperPlaceOrderParams(params)], + traderIndexHint: null, + }, + }, + ); + } + + /** + * Swap instruction + * + * Optimized swap for routers and arb bots. Normal traders should compose + * depost/withdraw/placeOrder to get limit orders. Does not go through the + * wrapper. + * + * @param payer PublicKey of the trader + * @param params SwapParams + * + * @returns TransactionInstruction + */ + public swapIx(payer: PublicKey, params: SwapParams): TransactionInstruction { + const traderBase: PublicKey = getAssociatedTokenAddressSync( + this.baseMint.address, + payer, + ); + const traderQuote: PublicKey = getAssociatedTokenAddressSync( + this.quoteMint.address, + payer, + ); + const baseVault: PublicKey = getVaultAddress( + this.market.address, + this.baseMint.address, + ); + const quoteVault: PublicKey = getVaultAddress( + this.market.address, + this.quoteMint.address, + ); + // Assumes just normal token program for now. + // No Token22 support here in sdk yet. + return createSwapInstruction( + { + payer, + market: this.market.address, + traderBase, + traderQuote, + baseVault, + quoteVault, + tokenProgramBase: TOKEN_PROGRAM_ID, + baseMint: this.baseMint.address, + tokenProgramQuote: TOKEN_PROGRAM_ID, + quoteMint: this.quoteMint.address, + }, + { + params, + }, + ); + } + + /** + * CancelOrder instruction + * + * @param params CancelOrderParams includes the orderSequenceNumber of the + * order to cancel. + * + * @returns TransactionInstruction + */ + public cancelOrderIx( + params: WrapperCancelOrderParams, + ): TransactionInstruction { + return createBatchUpdateInstruction( + { + payer: this.payer, + market: this.market.address, + manifestProgram: MANIFEST_PROGRAM_ID, + owner: this.payer, + wrapperState: this.wrapper.address, + }, + { + params: { + cancels: [params], + cancelAll: false, + orders: [], + traderIndexHint: null, + }, + }, + ); + } + + /** + * BatchUpdate instruction + * + * @param params CancelOrderParams includes the orderSequenceNumber of the + * order to cancel. + * + * @returns TransactionInstruction + */ + public batchUpdateIx( + placeParams: WrapperPlaceOrderParamsExternal[], + cancelParams: WrapperCancelOrderParams[], + cancelAll: boolean, + ): TransactionInstruction { + return createBatchUpdateInstruction( + { + payer: this.payer, + market: this.market.address, + manifestProgram: MANIFEST_PROGRAM_ID, + owner: this.payer, + wrapperState: this.wrapper.address, + }, + { + params: { + cancels: cancelParams, + cancelAll, + orders: placeParams.map((params: WrapperPlaceOrderParamsExternal) => + toWrapperPlaceOrderParams(params), + ), + traderIndexHint: null, + }, + }, + ); + } +} + +/** + * Same as the autogenerated WrapperPlaceOrderParams except price here is a number. + */ +export type WrapperPlaceOrderParamsExternal = { + /** Number of base atoms in the order. */ + baseAtoms: bignum; + /** Price as float in atoms of quote per atoms of base. */ + price: number; + /** Boolean for whether this order is on the bid side. */ + isBid: boolean; + /** Last slot before this order is invalid and will be removed. */ + lastValidSlot: number; + /** Type of order (Limit, PostOnly, ...). */ + orderType: OrderType; + /** Used in fill or kill orders. Set to zero otherwise. */ + minOutAtoms: bignum; + /** Client order id used for cancelling orders. Does not need to be unique. */ + clientOrderId: bignum; +}; + +function toWrapperPlaceOrderParams( + wrapperPlaceOrderParamsExternal: WrapperPlaceOrderParamsExternal, +): WrapperPlaceOrderParams { + // TODO: Make a helper and test it for this logic. + let priceExponent = 0; + let priceMantissa = wrapperPlaceOrderParamsExternal.price; + while (priceExponent > -20 && priceMantissa < 4294967295 / 100) { + priceExponent -= 1; + priceMantissa /= 10; + } + priceMantissa = Math.floor(priceMantissa); + + // TODO: Fix this + // @ts-ignore + return { + ...wrapperPlaceOrderParamsExternal, + priceMantissa, + priceExponent, + }; +} diff --git a/client/ts/src/constants.ts b/client/ts/src/constants.ts new file mode 100644 index 000000000..e7d972d94 --- /dev/null +++ b/client/ts/src/constants.ts @@ -0,0 +1,3 @@ +export const FIXED_MANIFEST_HEADER_SIZE: number = 512; +export const FIXED_WRAPPER_HEADER_SIZE: number = 64; +export const NIL = 4_294_967_295; diff --git a/client/ts/src/fillFeed.ts b/client/ts/src/fillFeed.ts new file mode 100644 index 000000000..6797b0119 --- /dev/null +++ b/client/ts/src/fillFeed.ts @@ -0,0 +1,191 @@ +import WebSocket from 'ws'; +import { Connection, ConfirmedSignatureInfo } from '@solana/web3.js'; + +import { FillLog } from './manifest/accounts/FillLog'; +import { PROGRAM_ID } from './manifest'; +import { toNum } from './utils/numbers'; +import bs58 from 'bs58'; +import keccak256 from 'keccak256'; + +/** + * FillFeed example implementation. + */ +export class FillFeed { + private wss: WebSocket.Server; + constructor(private connection: Connection) { + this.wss = new WebSocket.Server({ port: 1234 }); + + this.wss.on('connection', (ws: WebSocket) => { + console.log('New client connected'); + + ws.on('message', (message: string) => { + console.log(`Received message: ${message}`); + }); + + ws.on('close', () => { + console.log('Client disconnected'); + }); + }); + } + + /** + * Parse logs in an endless loop. + */ + public async parseLogs(endEarly?: boolean) { + // Start with a hopefully recent signature. + let lastSignature: string | undefined = ( + await this.connection.getSignaturesForAddress(PROGRAM_ID) + )[0].signature; + + // End early is 30 seconds, used for testing. + const endTime: Date = endEarly + ? new Date(Date.now() + 30_000) + : new Date(Date.now() + 1_000_000_000_000); + + while (new Date(Date.now()) < endTime) { + await new Promise((f) => setTimeout(f, 10_000)); + const signatures: ConfirmedSignatureInfo[] = + await this.connection.getSignaturesForAddress(PROGRAM_ID, { + until: lastSignature, + }); + // Flip it so we do oldest first. + signatures.reverse(); + if (signatures.length == 0) { + continue; + } + lastSignature = signatures[signatures.length - 1].signature; + + for (const signature of signatures) { + await this.handleSignature(signature); + } + } + this.wss.close(); + } + + /** + * Handle a signature by fetching the tx onchain and possibly sending a fill + * notification. + */ + private async handleSignature(signature: ConfirmedSignatureInfo) { + console.log('Handling', signature.signature); + const tx = await this.connection.getTransaction(signature.signature)!; + if (!tx?.meta?.logMessages) { + console.log('No log messages'); + return; + } + if (tx.meta.err != null) { + console.log('Skipping failed tx'); + return; + } + + const messages: string[] = tx?.meta?.logMessages!; + if ( + !messages.includes('Program log: Instruction: PlaceOrder') && + !messages.includes('Program log: Instruction: BatchUpdate') + ) { + console.log('No possible matches'); + return; + } + + const programDatas: string[] = messages.filter((message) => { + return message.includes('Program data:'); + }); + + if (programDatas.length == 0) { + console.log('No program datas'); + return; + } + + for (const programDataEntry of programDatas) { + const programData = programDataEntry.split(' ')[2]; + const byteArray: Uint8Array = Uint8Array.from(atob(programData), (c) => + c.charCodeAt(0), + ); + const buffer = Buffer.from(byteArray); + // Hack to fix the difference in caching on the CI action. + if ( + !buffer.subarray(0, 8).equals(fillDiscriminant) && + !buffer + .subarray(0, 8) + .equals(Buffer.from([52, 81, 147, 82, 119, 191, 72, 172])) + ) { + continue; + } + const deserializedFillLog: FillLog = FillLog.deserialize( + buffer.subarray(8), + )[0]; + console.log( + 'Got a fill', + JSON.stringify(toFillLogResult(deserializedFillLog, signature.slot)), + ); + this.wss.clients.forEach((client) => { + client.send( + JSON.stringify(toFillLogResult(deserializedFillLog, signature.slot)), + ); + }); + } + } +} + +/** + * Run a fill feed as a websocket server that clients can connect to and get + * notifications of fills for all manifest markets. + */ +export async function runFillFeed() { + const connection: Connection = new Connection( + process.env.RPC_URL || 'http://127.0.0.1:8899', + ); + const fillFeed: FillFeed = new FillFeed(connection); + await fillFeed.parseLogs(); +} + +/** + * Helper function for getting account discriminator that matches how anchor + * generates discriminators. + */ +function genAccDiscriminator(accName: string) { + return keccak256( + Buffer.concat([ + Buffer.from(bs58.decode(PROGRAM_ID.toBase58())), + Buffer.from('manifest::logs::'), + Buffer.from(accName), + ]), + ).subarray(0, 8); +} +const fillDiscriminant = genAccDiscriminator('FillLog'); + +/** + * FillLogResult is the message sent to subscribers of the FillFeed + */ +export type FillLogResult = { + /** Public key for the market as base58. */ + market: string; + /** Public key for the maker as base58. */ + maker: string; + /** Public key for the taker as base58. */ + taker: string; + /** Number of base atoms traded. */ + baseAtoms: number; + /** Number of quote atoms traded. */ + quoteAtoms: number; + /** Price as float. Quote atoms per base atom. */ + price: number; + /** Boolean to indicate which side the trade was. */ + takerIsBuy: boolean; + /** Slot number of the fill. */ + slot: number; +}; +function toFillLogResult(fillLog: FillLog, slot: number): FillLogResult { + return { + market: fillLog.market.toBase58(), + maker: fillLog.maker.toBase58(), + taker: fillLog.taker.toBase58(), + baseAtoms: toNum(fillLog.baseAtoms.inner), + quoteAtoms: toNum(fillLog.quoteAtoms.inner), + price: toNum( + Buffer.from(fillLog.price.inner as Uint8Array).readDoubleLE(0), + ), + takerIsBuy: fillLog.takerIsBuy, + slot, + }; +} diff --git a/client/ts/src/index.ts b/client/ts/src/index.ts new file mode 100644 index 000000000..f864db48d --- /dev/null +++ b/client/ts/src/index.ts @@ -0,0 +1,9 @@ +export * from './client'; +export * from './fillFeed'; +export * from './market'; +// Do not export all of manifest because names collide with wrapper. Force users +// to use the client. +export * from './manifest/errors'; +export * from './manifest/accounts'; +export * from './wrapper'; +export * from './wrapperObj'; diff --git a/client/ts/src/manifest/accounts/BaseAtoms.ts b/client/ts/src/manifest/accounts/BaseAtoms.ts new file mode 100644 index 000000000..6e6f2e9f2 --- /dev/null +++ b/client/ts/src/manifest/accounts/BaseAtoms.ts @@ -0,0 +1,160 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from "@metaplex-foundation/beet"; +import * as web3 from "@solana/web3.js"; +import * as beetSolana from "@metaplex-foundation/beet-solana"; + +/** + * Arguments used to create {@link BaseAtoms} + * @category Accounts + * @category generated + */ +export type BaseAtomsArgs = { + inner: beet.bignum; +}; +/** + * Holds the data for the {@link BaseAtoms} Account and provides de/serialization + * functionality for that data + * + * @category Accounts + * @category generated + */ +export class BaseAtoms implements BaseAtomsArgs { + private constructor(readonly inner: beet.bignum) {} + + /** + * Creates a {@link BaseAtoms} instance from the provided args. + */ + static fromArgs(args: BaseAtomsArgs) { + return new BaseAtoms(args.inner); + } + + /** + * Deserializes the {@link BaseAtoms} from the data of the provided {@link web3.AccountInfo}. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static fromAccountInfo( + accountInfo: web3.AccountInfo, + offset = 0, + ): [BaseAtoms, number] { + return BaseAtoms.deserialize(accountInfo.data, offset); + } + + /** + * Retrieves the account info from the provided address and deserializes + * the {@link BaseAtoms} from its data. + * + * @throws Error if no account info is found at the address or if deserialization fails + */ + static async fromAccountAddress( + connection: web3.Connection, + address: web3.PublicKey, + commitmentOrConfig?: web3.Commitment | web3.GetAccountInfoConfig, + ): Promise { + const accountInfo = await connection.getAccountInfo( + address, + commitmentOrConfig, + ); + if (accountInfo == null) { + throw new Error(`Unable to find BaseAtoms account at ${address}`); + } + return BaseAtoms.fromAccountInfo(accountInfo, 0)[0]; + } + + /** + * Provides a {@link web3.Connection.getProgramAccounts} config builder, + * to fetch accounts matching filters that can be specified via that builder. + * + * @param programId - the program that owns the accounts we are filtering + */ + static gpaBuilder( + programId: web3.PublicKey = new web3.PublicKey( + "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms", + ), + ) { + return beetSolana.GpaBuilder.fromStruct(programId, baseAtomsBeet); + } + + /** + * Deserializes the {@link BaseAtoms} from the provided data Buffer. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static deserialize(buf: Buffer, offset = 0): [BaseAtoms, number] { + return baseAtomsBeet.deserialize(buf, offset); + } + + /** + * Serializes the {@link BaseAtoms} into a Buffer. + * @returns a tuple of the created Buffer and the offset up to which the buffer was written to store it. + */ + serialize(): [Buffer, number] { + return baseAtomsBeet.serialize(this); + } + + /** + * Returns the byteSize of a {@link Buffer} holding the serialized data of + * {@link BaseAtoms} + */ + static get byteSize() { + return baseAtomsBeet.byteSize; + } + + /** + * Fetches the minimum balance needed to exempt an account holding + * {@link BaseAtoms} data from rent + * + * @param connection used to retrieve the rent exemption information + */ + static async getMinimumBalanceForRentExemption( + connection: web3.Connection, + commitment?: web3.Commitment, + ): Promise { + return connection.getMinimumBalanceForRentExemption( + BaseAtoms.byteSize, + commitment, + ); + } + + /** + * Determines if the provided {@link Buffer} has the correct byte size to + * hold {@link BaseAtoms} data. + */ + static hasCorrectByteSize(buf: Buffer, offset = 0) { + return buf.byteLength - offset === BaseAtoms.byteSize; + } + + /** + * Returns a readable version of {@link BaseAtoms} properties + * and can be used to convert to JSON and/or logging + */ + pretty() { + return { + inner: (() => { + const x = <{ toNumber: () => number }>this.inner; + if (typeof x.toNumber === "function") { + try { + return x.toNumber(); + } catch (_) { + return x; + } + } + return x; + })(), + }; + } +} + +/** + * @category Accounts + * @category generated + */ +export const baseAtomsBeet = new beet.BeetStruct( + [["inner", beet.u64]], + BaseAtoms.fromArgs, + "BaseAtoms", +); diff --git a/client/ts/src/manifest/accounts/CancelOrderLog.ts b/client/ts/src/manifest/accounts/CancelOrderLog.ts new file mode 100644 index 000000000..d32024241 --- /dev/null +++ b/client/ts/src/manifest/accounts/CancelOrderLog.ts @@ -0,0 +1,179 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as web3 from "@solana/web3.js"; +import * as beet from "@metaplex-foundation/beet"; +import * as beetSolana from "@metaplex-foundation/beet-solana"; + +/** + * Arguments used to create {@link CancelOrderLog} + * @category Accounts + * @category generated + */ +export type CancelOrderLogArgs = { + market: web3.PublicKey; + trader: web3.PublicKey; + orderSequenceNumber: beet.bignum; +}; +/** + * Holds the data for the {@link CancelOrderLog} Account and provides de/serialization + * functionality for that data + * + * @category Accounts + * @category generated + */ +export class CancelOrderLog implements CancelOrderLogArgs { + private constructor( + readonly market: web3.PublicKey, + readonly trader: web3.PublicKey, + readonly orderSequenceNumber: beet.bignum, + ) {} + + /** + * Creates a {@link CancelOrderLog} instance from the provided args. + */ + static fromArgs(args: CancelOrderLogArgs) { + return new CancelOrderLog( + args.market, + args.trader, + args.orderSequenceNumber, + ); + } + + /** + * Deserializes the {@link CancelOrderLog} from the data of the provided {@link web3.AccountInfo}. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static fromAccountInfo( + accountInfo: web3.AccountInfo, + offset = 0, + ): [CancelOrderLog, number] { + return CancelOrderLog.deserialize(accountInfo.data, offset); + } + + /** + * Retrieves the account info from the provided address and deserializes + * the {@link CancelOrderLog} from its data. + * + * @throws Error if no account info is found at the address or if deserialization fails + */ + static async fromAccountAddress( + connection: web3.Connection, + address: web3.PublicKey, + commitmentOrConfig?: web3.Commitment | web3.GetAccountInfoConfig, + ): Promise { + const accountInfo = await connection.getAccountInfo( + address, + commitmentOrConfig, + ); + if (accountInfo == null) { + throw new Error(`Unable to find CancelOrderLog account at ${address}`); + } + return CancelOrderLog.fromAccountInfo(accountInfo, 0)[0]; + } + + /** + * Provides a {@link web3.Connection.getProgramAccounts} config builder, + * to fetch accounts matching filters that can be specified via that builder. + * + * @param programId - the program that owns the accounts we are filtering + */ + static gpaBuilder( + programId: web3.PublicKey = new web3.PublicKey( + "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms", + ), + ) { + return beetSolana.GpaBuilder.fromStruct(programId, cancelOrderLogBeet); + } + + /** + * Deserializes the {@link CancelOrderLog} from the provided data Buffer. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static deserialize(buf: Buffer, offset = 0): [CancelOrderLog, number] { + return cancelOrderLogBeet.deserialize(buf, offset); + } + + /** + * Serializes the {@link CancelOrderLog} into a Buffer. + * @returns a tuple of the created Buffer and the offset up to which the buffer was written to store it. + */ + serialize(): [Buffer, number] { + return cancelOrderLogBeet.serialize(this); + } + + /** + * Returns the byteSize of a {@link Buffer} holding the serialized data of + * {@link CancelOrderLog} + */ + static get byteSize() { + return cancelOrderLogBeet.byteSize; + } + + /** + * Fetches the minimum balance needed to exempt an account holding + * {@link CancelOrderLog} data from rent + * + * @param connection used to retrieve the rent exemption information + */ + static async getMinimumBalanceForRentExemption( + connection: web3.Connection, + commitment?: web3.Commitment, + ): Promise { + return connection.getMinimumBalanceForRentExemption( + CancelOrderLog.byteSize, + commitment, + ); + } + + /** + * Determines if the provided {@link Buffer} has the correct byte size to + * hold {@link CancelOrderLog} data. + */ + static hasCorrectByteSize(buf: Buffer, offset = 0) { + return buf.byteLength - offset === CancelOrderLog.byteSize; + } + + /** + * Returns a readable version of {@link CancelOrderLog} properties + * and can be used to convert to JSON and/or logging + */ + pretty() { + return { + market: this.market.toBase58(), + trader: this.trader.toBase58(), + orderSequenceNumber: (() => { + const x = <{ toNumber: () => number }>this.orderSequenceNumber; + if (typeof x.toNumber === "function") { + try { + return x.toNumber(); + } catch (_) { + return x; + } + } + return x; + })(), + }; + } +} + +/** + * @category Accounts + * @category generated + */ +export const cancelOrderLogBeet = new beet.BeetStruct< + CancelOrderLog, + CancelOrderLogArgs +>( + [ + ["market", beetSolana.publicKey], + ["trader", beetSolana.publicKey], + ["orderSequenceNumber", beet.u64], + ], + CancelOrderLog.fromArgs, + "CancelOrderLog", +); diff --git a/client/ts/src/manifest/accounts/ClaimSeatLog.ts b/client/ts/src/manifest/accounts/ClaimSeatLog.ts new file mode 100644 index 000000000..9af8e3c06 --- /dev/null +++ b/client/ts/src/manifest/accounts/ClaimSeatLog.ts @@ -0,0 +1,161 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as web3 from "@solana/web3.js"; +import * as beetSolana from "@metaplex-foundation/beet-solana"; +import * as beet from "@metaplex-foundation/beet"; + +/** + * Arguments used to create {@link ClaimSeatLog} + * @category Accounts + * @category generated + */ +export type ClaimSeatLogArgs = { + market: web3.PublicKey; + trader: web3.PublicKey; +}; +/** + * Holds the data for the {@link ClaimSeatLog} Account and provides de/serialization + * functionality for that data + * + * @category Accounts + * @category generated + */ +export class ClaimSeatLog implements ClaimSeatLogArgs { + private constructor( + readonly market: web3.PublicKey, + readonly trader: web3.PublicKey, + ) {} + + /** + * Creates a {@link ClaimSeatLog} instance from the provided args. + */ + static fromArgs(args: ClaimSeatLogArgs) { + return new ClaimSeatLog(args.market, args.trader); + } + + /** + * Deserializes the {@link ClaimSeatLog} from the data of the provided {@link web3.AccountInfo}. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static fromAccountInfo( + accountInfo: web3.AccountInfo, + offset = 0, + ): [ClaimSeatLog, number] { + return ClaimSeatLog.deserialize(accountInfo.data, offset); + } + + /** + * Retrieves the account info from the provided address and deserializes + * the {@link ClaimSeatLog} from its data. + * + * @throws Error if no account info is found at the address or if deserialization fails + */ + static async fromAccountAddress( + connection: web3.Connection, + address: web3.PublicKey, + commitmentOrConfig?: web3.Commitment | web3.GetAccountInfoConfig, + ): Promise { + const accountInfo = await connection.getAccountInfo( + address, + commitmentOrConfig, + ); + if (accountInfo == null) { + throw new Error(`Unable to find ClaimSeatLog account at ${address}`); + } + return ClaimSeatLog.fromAccountInfo(accountInfo, 0)[0]; + } + + /** + * Provides a {@link web3.Connection.getProgramAccounts} config builder, + * to fetch accounts matching filters that can be specified via that builder. + * + * @param programId - the program that owns the accounts we are filtering + */ + static gpaBuilder( + programId: web3.PublicKey = new web3.PublicKey( + "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms", + ), + ) { + return beetSolana.GpaBuilder.fromStruct(programId, claimSeatLogBeet); + } + + /** + * Deserializes the {@link ClaimSeatLog} from the provided data Buffer. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static deserialize(buf: Buffer, offset = 0): [ClaimSeatLog, number] { + return claimSeatLogBeet.deserialize(buf, offset); + } + + /** + * Serializes the {@link ClaimSeatLog} into a Buffer. + * @returns a tuple of the created Buffer and the offset up to which the buffer was written to store it. + */ + serialize(): [Buffer, number] { + return claimSeatLogBeet.serialize(this); + } + + /** + * Returns the byteSize of a {@link Buffer} holding the serialized data of + * {@link ClaimSeatLog} + */ + static get byteSize() { + return claimSeatLogBeet.byteSize; + } + + /** + * Fetches the minimum balance needed to exempt an account holding + * {@link ClaimSeatLog} data from rent + * + * @param connection used to retrieve the rent exemption information + */ + static async getMinimumBalanceForRentExemption( + connection: web3.Connection, + commitment?: web3.Commitment, + ): Promise { + return connection.getMinimumBalanceForRentExemption( + ClaimSeatLog.byteSize, + commitment, + ); + } + + /** + * Determines if the provided {@link Buffer} has the correct byte size to + * hold {@link ClaimSeatLog} data. + */ + static hasCorrectByteSize(buf: Buffer, offset = 0) { + return buf.byteLength - offset === ClaimSeatLog.byteSize; + } + + /** + * Returns a readable version of {@link ClaimSeatLog} properties + * and can be used to convert to JSON and/or logging + */ + pretty() { + return { + market: this.market.toBase58(), + trader: this.trader.toBase58(), + }; + } +} + +/** + * @category Accounts + * @category generated + */ +export const claimSeatLogBeet = new beet.BeetStruct< + ClaimSeatLog, + ClaimSeatLogArgs +>( + [ + ["market", beetSolana.publicKey], + ["trader", beetSolana.publicKey], + ], + ClaimSeatLog.fromArgs, + "ClaimSeatLog", +); diff --git a/client/ts/src/manifest/accounts/CreateMarketLog.ts b/client/ts/src/manifest/accounts/CreateMarketLog.ts new file mode 100644 index 000000000..e2ac0ba37 --- /dev/null +++ b/client/ts/src/manifest/accounts/CreateMarketLog.ts @@ -0,0 +1,161 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as web3 from "@solana/web3.js"; +import * as beetSolana from "@metaplex-foundation/beet-solana"; +import * as beet from "@metaplex-foundation/beet"; + +/** + * Arguments used to create {@link CreateMarketLog} + * @category Accounts + * @category generated + */ +export type CreateMarketLogArgs = { + market: web3.PublicKey; + creator: web3.PublicKey; +}; +/** + * Holds the data for the {@link CreateMarketLog} Account and provides de/serialization + * functionality for that data + * + * @category Accounts + * @category generated + */ +export class CreateMarketLog implements CreateMarketLogArgs { + private constructor( + readonly market: web3.PublicKey, + readonly creator: web3.PublicKey, + ) {} + + /** + * Creates a {@link CreateMarketLog} instance from the provided args. + */ + static fromArgs(args: CreateMarketLogArgs) { + return new CreateMarketLog(args.market, args.creator); + } + + /** + * Deserializes the {@link CreateMarketLog} from the data of the provided {@link web3.AccountInfo}. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static fromAccountInfo( + accountInfo: web3.AccountInfo, + offset = 0, + ): [CreateMarketLog, number] { + return CreateMarketLog.deserialize(accountInfo.data, offset); + } + + /** + * Retrieves the account info from the provided address and deserializes + * the {@link CreateMarketLog} from its data. + * + * @throws Error if no account info is found at the address or if deserialization fails + */ + static async fromAccountAddress( + connection: web3.Connection, + address: web3.PublicKey, + commitmentOrConfig?: web3.Commitment | web3.GetAccountInfoConfig, + ): Promise { + const accountInfo = await connection.getAccountInfo( + address, + commitmentOrConfig, + ); + if (accountInfo == null) { + throw new Error(`Unable to find CreateMarketLog account at ${address}`); + } + return CreateMarketLog.fromAccountInfo(accountInfo, 0)[0]; + } + + /** + * Provides a {@link web3.Connection.getProgramAccounts} config builder, + * to fetch accounts matching filters that can be specified via that builder. + * + * @param programId - the program that owns the accounts we are filtering + */ + static gpaBuilder( + programId: web3.PublicKey = new web3.PublicKey( + "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms", + ), + ) { + return beetSolana.GpaBuilder.fromStruct(programId, createMarketLogBeet); + } + + /** + * Deserializes the {@link CreateMarketLog} from the provided data Buffer. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static deserialize(buf: Buffer, offset = 0): [CreateMarketLog, number] { + return createMarketLogBeet.deserialize(buf, offset); + } + + /** + * Serializes the {@link CreateMarketLog} into a Buffer. + * @returns a tuple of the created Buffer and the offset up to which the buffer was written to store it. + */ + serialize(): [Buffer, number] { + return createMarketLogBeet.serialize(this); + } + + /** + * Returns the byteSize of a {@link Buffer} holding the serialized data of + * {@link CreateMarketLog} + */ + static get byteSize() { + return createMarketLogBeet.byteSize; + } + + /** + * Fetches the minimum balance needed to exempt an account holding + * {@link CreateMarketLog} data from rent + * + * @param connection used to retrieve the rent exemption information + */ + static async getMinimumBalanceForRentExemption( + connection: web3.Connection, + commitment?: web3.Commitment, + ): Promise { + return connection.getMinimumBalanceForRentExemption( + CreateMarketLog.byteSize, + commitment, + ); + } + + /** + * Determines if the provided {@link Buffer} has the correct byte size to + * hold {@link CreateMarketLog} data. + */ + static hasCorrectByteSize(buf: Buffer, offset = 0) { + return buf.byteLength - offset === CreateMarketLog.byteSize; + } + + /** + * Returns a readable version of {@link CreateMarketLog} properties + * and can be used to convert to JSON and/or logging + */ + pretty() { + return { + market: this.market.toBase58(), + creator: this.creator.toBase58(), + }; + } +} + +/** + * @category Accounts + * @category generated + */ +export const createMarketLogBeet = new beet.BeetStruct< + CreateMarketLog, + CreateMarketLogArgs +>( + [ + ["market", beetSolana.publicKey], + ["creator", beetSolana.publicKey], + ], + CreateMarketLog.fromArgs, + "CreateMarketLog", +); diff --git a/client/ts/src/manifest/accounts/DepositLog.ts b/client/ts/src/manifest/accounts/DepositLog.ts new file mode 100644 index 000000000..fbc53913e --- /dev/null +++ b/client/ts/src/manifest/accounts/DepositLog.ts @@ -0,0 +1,181 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as web3 from "@solana/web3.js"; +import * as beet from "@metaplex-foundation/beet"; +import * as beetSolana from "@metaplex-foundation/beet-solana"; + +/** + * Arguments used to create {@link DepositLog} + * @category Accounts + * @category generated + */ +export type DepositLogArgs = { + market: web3.PublicKey; + trader: web3.PublicKey; + mint: web3.PublicKey; + amountAtoms: beet.bignum; +}; +/** + * Holds the data for the {@link DepositLog} Account and provides de/serialization + * functionality for that data + * + * @category Accounts + * @category generated + */ +export class DepositLog implements DepositLogArgs { + private constructor( + readonly market: web3.PublicKey, + readonly trader: web3.PublicKey, + readonly mint: web3.PublicKey, + readonly amountAtoms: beet.bignum, + ) {} + + /** + * Creates a {@link DepositLog} instance from the provided args. + */ + static fromArgs(args: DepositLogArgs) { + return new DepositLog( + args.market, + args.trader, + args.mint, + args.amountAtoms, + ); + } + + /** + * Deserializes the {@link DepositLog} from the data of the provided {@link web3.AccountInfo}. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static fromAccountInfo( + accountInfo: web3.AccountInfo, + offset = 0, + ): [DepositLog, number] { + return DepositLog.deserialize(accountInfo.data, offset); + } + + /** + * Retrieves the account info from the provided address and deserializes + * the {@link DepositLog} from its data. + * + * @throws Error if no account info is found at the address or if deserialization fails + */ + static async fromAccountAddress( + connection: web3.Connection, + address: web3.PublicKey, + commitmentOrConfig?: web3.Commitment | web3.GetAccountInfoConfig, + ): Promise { + const accountInfo = await connection.getAccountInfo( + address, + commitmentOrConfig, + ); + if (accountInfo == null) { + throw new Error(`Unable to find DepositLog account at ${address}`); + } + return DepositLog.fromAccountInfo(accountInfo, 0)[0]; + } + + /** + * Provides a {@link web3.Connection.getProgramAccounts} config builder, + * to fetch accounts matching filters that can be specified via that builder. + * + * @param programId - the program that owns the accounts we are filtering + */ + static gpaBuilder( + programId: web3.PublicKey = new web3.PublicKey( + "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms", + ), + ) { + return beetSolana.GpaBuilder.fromStruct(programId, depositLogBeet); + } + + /** + * Deserializes the {@link DepositLog} from the provided data Buffer. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static deserialize(buf: Buffer, offset = 0): [DepositLog, number] { + return depositLogBeet.deserialize(buf, offset); + } + + /** + * Serializes the {@link DepositLog} into a Buffer. + * @returns a tuple of the created Buffer and the offset up to which the buffer was written to store it. + */ + serialize(): [Buffer, number] { + return depositLogBeet.serialize(this); + } + + /** + * Returns the byteSize of a {@link Buffer} holding the serialized data of + * {@link DepositLog} + */ + static get byteSize() { + return depositLogBeet.byteSize; + } + + /** + * Fetches the minimum balance needed to exempt an account holding + * {@link DepositLog} data from rent + * + * @param connection used to retrieve the rent exemption information + */ + static async getMinimumBalanceForRentExemption( + connection: web3.Connection, + commitment?: web3.Commitment, + ): Promise { + return connection.getMinimumBalanceForRentExemption( + DepositLog.byteSize, + commitment, + ); + } + + /** + * Determines if the provided {@link Buffer} has the correct byte size to + * hold {@link DepositLog} data. + */ + static hasCorrectByteSize(buf: Buffer, offset = 0) { + return buf.byteLength - offset === DepositLog.byteSize; + } + + /** + * Returns a readable version of {@link DepositLog} properties + * and can be used to convert to JSON and/or logging + */ + pretty() { + return { + market: this.market.toBase58(), + trader: this.trader.toBase58(), + mint: this.mint.toBase58(), + amountAtoms: (() => { + const x = <{ toNumber: () => number }>this.amountAtoms; + if (typeof x.toNumber === "function") { + try { + return x.toNumber(); + } catch (_) { + return x; + } + } + return x; + })(), + }; + } +} + +/** + * @category Accounts + * @category generated + */ +export const depositLogBeet = new beet.BeetStruct( + [ + ["market", beetSolana.publicKey], + ["trader", beetSolana.publicKey], + ["mint", beetSolana.publicKey], + ["amountAtoms", beet.u64], + ], + DepositLog.fromArgs, + "DepositLog", +); diff --git a/client/ts/src/manifest/accounts/FillLog.ts b/client/ts/src/manifest/accounts/FillLog.ts new file mode 100644 index 000000000..0c95e484b --- /dev/null +++ b/client/ts/src/manifest/accounts/FillLog.ts @@ -0,0 +1,197 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as web3 from "@solana/web3.js"; +import * as beetSolana from "@metaplex-foundation/beet-solana"; +import * as beet from "@metaplex-foundation/beet"; +import { + QuoteAtomsPerBaseAtom, + quoteAtomsPerBaseAtomBeet, +} from "./QuoteAtomsPerBaseAtom"; +import { BaseAtoms, baseAtomsBeet } from "./BaseAtoms"; +import { QuoteAtoms, quoteAtomsBeet } from "./QuoteAtoms"; + +/** + * Arguments used to create {@link FillLog} + * @category Accounts + * @category generated + */ +export type FillLogArgs = { + market: web3.PublicKey; + maker: web3.PublicKey; + taker: web3.PublicKey; + price: QuoteAtomsPerBaseAtom; + baseAtoms: BaseAtoms; + quoteAtoms: QuoteAtoms; + takerIsBuy: boolean; + padding: number[] /* size: 15 */; +}; +/** + * Holds the data for the {@link FillLog} Account and provides de/serialization + * functionality for that data + * + * @category Accounts + * @category generated + */ +export class FillLog implements FillLogArgs { + private constructor( + readonly market: web3.PublicKey, + readonly maker: web3.PublicKey, + readonly taker: web3.PublicKey, + readonly price: QuoteAtomsPerBaseAtom, + readonly baseAtoms: BaseAtoms, + readonly quoteAtoms: QuoteAtoms, + readonly takerIsBuy: boolean, + readonly padding: number[] /* size: 15 */, + ) {} + + /** + * Creates a {@link FillLog} instance from the provided args. + */ + static fromArgs(args: FillLogArgs) { + return new FillLog( + args.market, + args.maker, + args.taker, + args.price, + args.baseAtoms, + args.quoteAtoms, + args.takerIsBuy, + args.padding, + ); + } + + /** + * Deserializes the {@link FillLog} from the data of the provided {@link web3.AccountInfo}. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static fromAccountInfo( + accountInfo: web3.AccountInfo, + offset = 0, + ): [FillLog, number] { + return FillLog.deserialize(accountInfo.data, offset); + } + + /** + * Retrieves the account info from the provided address and deserializes + * the {@link FillLog} from its data. + * + * @throws Error if no account info is found at the address or if deserialization fails + */ + static async fromAccountAddress( + connection: web3.Connection, + address: web3.PublicKey, + commitmentOrConfig?: web3.Commitment | web3.GetAccountInfoConfig, + ): Promise { + const accountInfo = await connection.getAccountInfo( + address, + commitmentOrConfig, + ); + if (accountInfo == null) { + throw new Error(`Unable to find FillLog account at ${address}`); + } + return FillLog.fromAccountInfo(accountInfo, 0)[0]; + } + + /** + * Provides a {@link web3.Connection.getProgramAccounts} config builder, + * to fetch accounts matching filters that can be specified via that builder. + * + * @param programId - the program that owns the accounts we are filtering + */ + static gpaBuilder( + programId: web3.PublicKey = new web3.PublicKey( + "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms", + ), + ) { + return beetSolana.GpaBuilder.fromStruct(programId, fillLogBeet); + } + + /** + * Deserializes the {@link FillLog} from the provided data Buffer. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static deserialize(buf: Buffer, offset = 0): [FillLog, number] { + return fillLogBeet.deserialize(buf, offset); + } + + /** + * Serializes the {@link FillLog} into a Buffer. + * @returns a tuple of the created Buffer and the offset up to which the buffer was written to store it. + */ + serialize(): [Buffer, number] { + return fillLogBeet.serialize(this); + } + + /** + * Returns the byteSize of a {@link Buffer} holding the serialized data of + * {@link FillLog} + */ + static get byteSize() { + return fillLogBeet.byteSize; + } + + /** + * Fetches the minimum balance needed to exempt an account holding + * {@link FillLog} data from rent + * + * @param connection used to retrieve the rent exemption information + */ + static async getMinimumBalanceForRentExemption( + connection: web3.Connection, + commitment?: web3.Commitment, + ): Promise { + return connection.getMinimumBalanceForRentExemption( + FillLog.byteSize, + commitment, + ); + } + + /** + * Determines if the provided {@link Buffer} has the correct byte size to + * hold {@link FillLog} data. + */ + static hasCorrectByteSize(buf: Buffer, offset = 0) { + return buf.byteLength - offset === FillLog.byteSize; + } + + /** + * Returns a readable version of {@link FillLog} properties + * and can be used to convert to JSON and/or logging + */ + pretty() { + return { + market: this.market.toBase58(), + maker: this.maker.toBase58(), + taker: this.taker.toBase58(), + price: this.price, + baseAtoms: this.baseAtoms, + quoteAtoms: this.quoteAtoms, + takerIsBuy: this.takerIsBuy, + padding: this.padding, + }; + } +} + +/** + * @category Accounts + * @category generated + */ +export const fillLogBeet = new beet.BeetStruct( + [ + ["market", beetSolana.publicKey], + ["maker", beetSolana.publicKey], + ["taker", beetSolana.publicKey], + ["price", quoteAtomsPerBaseAtomBeet], + ["baseAtoms", baseAtomsBeet], + ["quoteAtoms", quoteAtomsBeet], + ["takerIsBuy", beet.bool], + ["padding", beet.uniformFixedSizeArray(beet.u8, 15)], + ], + FillLog.fromArgs, + "FillLog", +); diff --git a/client/ts/src/manifest/accounts/GlobalAddTraderLog.ts b/client/ts/src/manifest/accounts/GlobalAddTraderLog.ts new file mode 100644 index 000000000..bb877dbba --- /dev/null +++ b/client/ts/src/manifest/accounts/GlobalAddTraderLog.ts @@ -0,0 +1,163 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as web3 from "@solana/web3.js"; +import * as beetSolana from "@metaplex-foundation/beet-solana"; +import * as beet from "@metaplex-foundation/beet"; + +/** + * Arguments used to create {@link GlobalAddTraderLog} + * @category Accounts + * @category generated + */ +export type GlobalAddTraderLogArgs = { + global: web3.PublicKey; + trader: web3.PublicKey; +}; +/** + * Holds the data for the {@link GlobalAddTraderLog} Account and provides de/serialization + * functionality for that data + * + * @category Accounts + * @category generated + */ +export class GlobalAddTraderLog implements GlobalAddTraderLogArgs { + private constructor( + readonly global: web3.PublicKey, + readonly trader: web3.PublicKey, + ) {} + + /** + * Creates a {@link GlobalAddTraderLog} instance from the provided args. + */ + static fromArgs(args: GlobalAddTraderLogArgs) { + return new GlobalAddTraderLog(args.global, args.trader); + } + + /** + * Deserializes the {@link GlobalAddTraderLog} from the data of the provided {@link web3.AccountInfo}. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static fromAccountInfo( + accountInfo: web3.AccountInfo, + offset = 0, + ): [GlobalAddTraderLog, number] { + return GlobalAddTraderLog.deserialize(accountInfo.data, offset); + } + + /** + * Retrieves the account info from the provided address and deserializes + * the {@link GlobalAddTraderLog} from its data. + * + * @throws Error if no account info is found at the address or if deserialization fails + */ + static async fromAccountAddress( + connection: web3.Connection, + address: web3.PublicKey, + commitmentOrConfig?: web3.Commitment | web3.GetAccountInfoConfig, + ): Promise { + const accountInfo = await connection.getAccountInfo( + address, + commitmentOrConfig, + ); + if (accountInfo == null) { + throw new Error( + `Unable to find GlobalAddTraderLog account at ${address}`, + ); + } + return GlobalAddTraderLog.fromAccountInfo(accountInfo, 0)[0]; + } + + /** + * Provides a {@link web3.Connection.getProgramAccounts} config builder, + * to fetch accounts matching filters that can be specified via that builder. + * + * @param programId - the program that owns the accounts we are filtering + */ + static gpaBuilder( + programId: web3.PublicKey = new web3.PublicKey( + "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms", + ), + ) { + return beetSolana.GpaBuilder.fromStruct(programId, globalAddTraderLogBeet); + } + + /** + * Deserializes the {@link GlobalAddTraderLog} from the provided data Buffer. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static deserialize(buf: Buffer, offset = 0): [GlobalAddTraderLog, number] { + return globalAddTraderLogBeet.deserialize(buf, offset); + } + + /** + * Serializes the {@link GlobalAddTraderLog} into a Buffer. + * @returns a tuple of the created Buffer and the offset up to which the buffer was written to store it. + */ + serialize(): [Buffer, number] { + return globalAddTraderLogBeet.serialize(this); + } + + /** + * Returns the byteSize of a {@link Buffer} holding the serialized data of + * {@link GlobalAddTraderLog} + */ + static get byteSize() { + return globalAddTraderLogBeet.byteSize; + } + + /** + * Fetches the minimum balance needed to exempt an account holding + * {@link GlobalAddTraderLog} data from rent + * + * @param connection used to retrieve the rent exemption information + */ + static async getMinimumBalanceForRentExemption( + connection: web3.Connection, + commitment?: web3.Commitment, + ): Promise { + return connection.getMinimumBalanceForRentExemption( + GlobalAddTraderLog.byteSize, + commitment, + ); + } + + /** + * Determines if the provided {@link Buffer} has the correct byte size to + * hold {@link GlobalAddTraderLog} data. + */ + static hasCorrectByteSize(buf: Buffer, offset = 0) { + return buf.byteLength - offset === GlobalAddTraderLog.byteSize; + } + + /** + * Returns a readable version of {@link GlobalAddTraderLog} properties + * and can be used to convert to JSON and/or logging + */ + pretty() { + return { + global: this.global.toBase58(), + trader: this.trader.toBase58(), + }; + } +} + +/** + * @category Accounts + * @category generated + */ +export const globalAddTraderLogBeet = new beet.BeetStruct< + GlobalAddTraderLog, + GlobalAddTraderLogArgs +>( + [ + ["global", beetSolana.publicKey], + ["trader", beetSolana.publicKey], + ], + GlobalAddTraderLog.fromArgs, + "GlobalAddTraderLog", +); diff --git a/client/ts/src/manifest/accounts/GlobalAtoms.ts b/client/ts/src/manifest/accounts/GlobalAtoms.ts new file mode 100644 index 000000000..b3c05335d --- /dev/null +++ b/client/ts/src/manifest/accounts/GlobalAtoms.ts @@ -0,0 +1,159 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from "@metaplex-foundation/beet"; +import * as web3 from "@solana/web3.js"; +import * as beetSolana from "@metaplex-foundation/beet-solana"; + +/** + * Arguments used to create {@link GlobalAtoms} + * @category Accounts + * @category generated + */ +export type GlobalAtomsArgs = { + inner: beet.bignum; +}; +/** + * Holds the data for the {@link GlobalAtoms} Account and provides de/serialization + * functionality for that data + * + * @category Accounts + * @category generated + */ +export class GlobalAtoms implements GlobalAtomsArgs { + private constructor(readonly inner: beet.bignum) {} + + /** + * Creates a {@link GlobalAtoms} instance from the provided args. + */ + static fromArgs(args: GlobalAtomsArgs) { + return new GlobalAtoms(args.inner); + } + + /** + * Deserializes the {@link GlobalAtoms} from the data of the provided {@link web3.AccountInfo}. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static fromAccountInfo( + accountInfo: web3.AccountInfo, + offset = 0, + ): [GlobalAtoms, number] { + return GlobalAtoms.deserialize(accountInfo.data, offset); + } + + /** + * Retrieves the account info from the provided address and deserializes + * the {@link GlobalAtoms} from its data. + * + * @throws Error if no account info is found at the address or if deserialization fails + */ + static async fromAccountAddress( + connection: web3.Connection, + address: web3.PublicKey, + commitmentOrConfig?: web3.Commitment | web3.GetAccountInfoConfig, + ): Promise { + const accountInfo = await connection.getAccountInfo( + address, + commitmentOrConfig, + ); + if (accountInfo == null) { + throw new Error(`Unable to find GlobalAtoms account at ${address}`); + } + return GlobalAtoms.fromAccountInfo(accountInfo, 0)[0]; + } + + /** + * Provides a {@link web3.Connection.getProgramAccounts} config builder, + * to fetch accounts matching filters that can be specified via that builder. + * + * @param programId - the program that owns the accounts we are filtering + */ + static gpaBuilder( + programId: web3.PublicKey = new web3.PublicKey( + "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms", + ), + ) { + return beetSolana.GpaBuilder.fromStruct(programId, globalAtomsBeet); + } + + /** + * Deserializes the {@link GlobalAtoms} from the provided data Buffer. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static deserialize(buf: Buffer, offset = 0): [GlobalAtoms, number] { + return globalAtomsBeet.deserialize(buf, offset); + } + + /** + * Serializes the {@link GlobalAtoms} into a Buffer. + * @returns a tuple of the created Buffer and the offset up to which the buffer was written to store it. + */ + serialize(): [Buffer, number] { + return globalAtomsBeet.serialize(this); + } + + /** + * Returns the byteSize of a {@link Buffer} holding the serialized data of + * {@link GlobalAtoms} + */ + static get byteSize() { + return globalAtomsBeet.byteSize; + } + + /** + * Fetches the minimum balance needed to exempt an account holding + * {@link GlobalAtoms} data from rent + * + * @param connection used to retrieve the rent exemption information + */ + static async getMinimumBalanceForRentExemption( + connection: web3.Connection, + commitment?: web3.Commitment, + ): Promise { + return connection.getMinimumBalanceForRentExemption( + GlobalAtoms.byteSize, + commitment, + ); + } + + /** + * Determines if the provided {@link Buffer} has the correct byte size to + * hold {@link GlobalAtoms} data. + */ + static hasCorrectByteSize(buf: Buffer, offset = 0) { + return buf.byteLength - offset === GlobalAtoms.byteSize; + } + + /** + * Returns a readable version of {@link GlobalAtoms} properties + * and can be used to convert to JSON and/or logging + */ + pretty() { + return { + inner: (() => { + const x = <{ toNumber: () => number }>this.inner; + if (typeof x.toNumber === "function") { + try { + return x.toNumber(); + } catch (_) { + return x; + } + } + return x; + })(), + }; + } +} + +/** + * @category Accounts + * @category generated + */ +export const globalAtomsBeet = new beet.BeetStruct< + GlobalAtoms, + GlobalAtomsArgs +>([["inner", beet.u64]], GlobalAtoms.fromArgs, "GlobalAtoms"); diff --git a/client/ts/src/manifest/accounts/GlobalClaimSeatLog.ts b/client/ts/src/manifest/accounts/GlobalClaimSeatLog.ts new file mode 100644 index 000000000..c15e132f8 --- /dev/null +++ b/client/ts/src/manifest/accounts/GlobalClaimSeatLog.ts @@ -0,0 +1,167 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as web3 from "@solana/web3.js"; +import * as beetSolana from "@metaplex-foundation/beet-solana"; +import * as beet from "@metaplex-foundation/beet"; + +/** + * Arguments used to create {@link GlobalClaimSeatLog} + * @category Accounts + * @category generated + */ +export type GlobalClaimSeatLogArgs = { + global: web3.PublicKey; + market: web3.PublicKey; + trader: web3.PublicKey; +}; +/** + * Holds the data for the {@link GlobalClaimSeatLog} Account and provides de/serialization + * functionality for that data + * + * @category Accounts + * @category generated + */ +export class GlobalClaimSeatLog implements GlobalClaimSeatLogArgs { + private constructor( + readonly global: web3.PublicKey, + readonly market: web3.PublicKey, + readonly trader: web3.PublicKey, + ) {} + + /** + * Creates a {@link GlobalClaimSeatLog} instance from the provided args. + */ + static fromArgs(args: GlobalClaimSeatLogArgs) { + return new GlobalClaimSeatLog(args.global, args.market, args.trader); + } + + /** + * Deserializes the {@link GlobalClaimSeatLog} from the data of the provided {@link web3.AccountInfo}. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static fromAccountInfo( + accountInfo: web3.AccountInfo, + offset = 0, + ): [GlobalClaimSeatLog, number] { + return GlobalClaimSeatLog.deserialize(accountInfo.data, offset); + } + + /** + * Retrieves the account info from the provided address and deserializes + * the {@link GlobalClaimSeatLog} from its data. + * + * @throws Error if no account info is found at the address or if deserialization fails + */ + static async fromAccountAddress( + connection: web3.Connection, + address: web3.PublicKey, + commitmentOrConfig?: web3.Commitment | web3.GetAccountInfoConfig, + ): Promise { + const accountInfo = await connection.getAccountInfo( + address, + commitmentOrConfig, + ); + if (accountInfo == null) { + throw new Error( + `Unable to find GlobalClaimSeatLog account at ${address}`, + ); + } + return GlobalClaimSeatLog.fromAccountInfo(accountInfo, 0)[0]; + } + + /** + * Provides a {@link web3.Connection.getProgramAccounts} config builder, + * to fetch accounts matching filters that can be specified via that builder. + * + * @param programId - the program that owns the accounts we are filtering + */ + static gpaBuilder( + programId: web3.PublicKey = new web3.PublicKey( + "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms", + ), + ) { + return beetSolana.GpaBuilder.fromStruct(programId, globalClaimSeatLogBeet); + } + + /** + * Deserializes the {@link GlobalClaimSeatLog} from the provided data Buffer. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static deserialize(buf: Buffer, offset = 0): [GlobalClaimSeatLog, number] { + return globalClaimSeatLogBeet.deserialize(buf, offset); + } + + /** + * Serializes the {@link GlobalClaimSeatLog} into a Buffer. + * @returns a tuple of the created Buffer and the offset up to which the buffer was written to store it. + */ + serialize(): [Buffer, number] { + return globalClaimSeatLogBeet.serialize(this); + } + + /** + * Returns the byteSize of a {@link Buffer} holding the serialized data of + * {@link GlobalClaimSeatLog} + */ + static get byteSize() { + return globalClaimSeatLogBeet.byteSize; + } + + /** + * Fetches the minimum balance needed to exempt an account holding + * {@link GlobalClaimSeatLog} data from rent + * + * @param connection used to retrieve the rent exemption information + */ + static async getMinimumBalanceForRentExemption( + connection: web3.Connection, + commitment?: web3.Commitment, + ): Promise { + return connection.getMinimumBalanceForRentExemption( + GlobalClaimSeatLog.byteSize, + commitment, + ); + } + + /** + * Determines if the provided {@link Buffer} has the correct byte size to + * hold {@link GlobalClaimSeatLog} data. + */ + static hasCorrectByteSize(buf: Buffer, offset = 0) { + return buf.byteLength - offset === GlobalClaimSeatLog.byteSize; + } + + /** + * Returns a readable version of {@link GlobalClaimSeatLog} properties + * and can be used to convert to JSON and/or logging + */ + pretty() { + return { + global: this.global.toBase58(), + market: this.market.toBase58(), + trader: this.trader.toBase58(), + }; + } +} + +/** + * @category Accounts + * @category generated + */ +export const globalClaimSeatLogBeet = new beet.BeetStruct< + GlobalClaimSeatLog, + GlobalClaimSeatLogArgs +>( + [ + ["global", beetSolana.publicKey], + ["market", beetSolana.publicKey], + ["trader", beetSolana.publicKey], + ], + GlobalClaimSeatLog.fromArgs, + "GlobalClaimSeatLog", +); diff --git a/client/ts/src/manifest/accounts/GlobalCreateLog.ts b/client/ts/src/manifest/accounts/GlobalCreateLog.ts new file mode 100644 index 000000000..d6d3af7c1 --- /dev/null +++ b/client/ts/src/manifest/accounts/GlobalCreateLog.ts @@ -0,0 +1,161 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as web3 from "@solana/web3.js"; +import * as beetSolana from "@metaplex-foundation/beet-solana"; +import * as beet from "@metaplex-foundation/beet"; + +/** + * Arguments used to create {@link GlobalCreateLog} + * @category Accounts + * @category generated + */ +export type GlobalCreateLogArgs = { + global: web3.PublicKey; + creator: web3.PublicKey; +}; +/** + * Holds the data for the {@link GlobalCreateLog} Account and provides de/serialization + * functionality for that data + * + * @category Accounts + * @category generated + */ +export class GlobalCreateLog implements GlobalCreateLogArgs { + private constructor( + readonly global: web3.PublicKey, + readonly creator: web3.PublicKey, + ) {} + + /** + * Creates a {@link GlobalCreateLog} instance from the provided args. + */ + static fromArgs(args: GlobalCreateLogArgs) { + return new GlobalCreateLog(args.global, args.creator); + } + + /** + * Deserializes the {@link GlobalCreateLog} from the data of the provided {@link web3.AccountInfo}. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static fromAccountInfo( + accountInfo: web3.AccountInfo, + offset = 0, + ): [GlobalCreateLog, number] { + return GlobalCreateLog.deserialize(accountInfo.data, offset); + } + + /** + * Retrieves the account info from the provided address and deserializes + * the {@link GlobalCreateLog} from its data. + * + * @throws Error if no account info is found at the address or if deserialization fails + */ + static async fromAccountAddress( + connection: web3.Connection, + address: web3.PublicKey, + commitmentOrConfig?: web3.Commitment | web3.GetAccountInfoConfig, + ): Promise { + const accountInfo = await connection.getAccountInfo( + address, + commitmentOrConfig, + ); + if (accountInfo == null) { + throw new Error(`Unable to find GlobalCreateLog account at ${address}`); + } + return GlobalCreateLog.fromAccountInfo(accountInfo, 0)[0]; + } + + /** + * Provides a {@link web3.Connection.getProgramAccounts} config builder, + * to fetch accounts matching filters that can be specified via that builder. + * + * @param programId - the program that owns the accounts we are filtering + */ + static gpaBuilder( + programId: web3.PublicKey = new web3.PublicKey( + "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms", + ), + ) { + return beetSolana.GpaBuilder.fromStruct(programId, globalCreateLogBeet); + } + + /** + * Deserializes the {@link GlobalCreateLog} from the provided data Buffer. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static deserialize(buf: Buffer, offset = 0): [GlobalCreateLog, number] { + return globalCreateLogBeet.deserialize(buf, offset); + } + + /** + * Serializes the {@link GlobalCreateLog} into a Buffer. + * @returns a tuple of the created Buffer and the offset up to which the buffer was written to store it. + */ + serialize(): [Buffer, number] { + return globalCreateLogBeet.serialize(this); + } + + /** + * Returns the byteSize of a {@link Buffer} holding the serialized data of + * {@link GlobalCreateLog} + */ + static get byteSize() { + return globalCreateLogBeet.byteSize; + } + + /** + * Fetches the minimum balance needed to exempt an account holding + * {@link GlobalCreateLog} data from rent + * + * @param connection used to retrieve the rent exemption information + */ + static async getMinimumBalanceForRentExemption( + connection: web3.Connection, + commitment?: web3.Commitment, + ): Promise { + return connection.getMinimumBalanceForRentExemption( + GlobalCreateLog.byteSize, + commitment, + ); + } + + /** + * Determines if the provided {@link Buffer} has the correct byte size to + * hold {@link GlobalCreateLog} data. + */ + static hasCorrectByteSize(buf: Buffer, offset = 0) { + return buf.byteLength - offset === GlobalCreateLog.byteSize; + } + + /** + * Returns a readable version of {@link GlobalCreateLog} properties + * and can be used to convert to JSON and/or logging + */ + pretty() { + return { + global: this.global.toBase58(), + creator: this.creator.toBase58(), + }; + } +} + +/** + * @category Accounts + * @category generated + */ +export const globalCreateLogBeet = new beet.BeetStruct< + GlobalCreateLog, + GlobalCreateLogArgs +>( + [ + ["global", beetSolana.publicKey], + ["creator", beetSolana.publicKey], + ], + GlobalCreateLog.fromArgs, + "GlobalCreateLog", +); diff --git a/client/ts/src/manifest/accounts/GlobalDepositLog.ts b/client/ts/src/manifest/accounts/GlobalDepositLog.ts new file mode 100644 index 000000000..618db2c4c --- /dev/null +++ b/client/ts/src/manifest/accounts/GlobalDepositLog.ts @@ -0,0 +1,166 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as web3 from "@solana/web3.js"; +import * as beetSolana from "@metaplex-foundation/beet-solana"; +import * as beet from "@metaplex-foundation/beet"; +import { GlobalAtoms, globalAtomsBeet } from "./GlobalAtoms"; + +/** + * Arguments used to create {@link GlobalDepositLog} + * @category Accounts + * @category generated + */ +export type GlobalDepositLogArgs = { + global: web3.PublicKey; + trader: web3.PublicKey; + globalAtoms: GlobalAtoms; +}; +/** + * Holds the data for the {@link GlobalDepositLog} Account and provides de/serialization + * functionality for that data + * + * @category Accounts + * @category generated + */ +export class GlobalDepositLog implements GlobalDepositLogArgs { + private constructor( + readonly global: web3.PublicKey, + readonly trader: web3.PublicKey, + readonly globalAtoms: GlobalAtoms, + ) {} + + /** + * Creates a {@link GlobalDepositLog} instance from the provided args. + */ + static fromArgs(args: GlobalDepositLogArgs) { + return new GlobalDepositLog(args.global, args.trader, args.globalAtoms); + } + + /** + * Deserializes the {@link GlobalDepositLog} from the data of the provided {@link web3.AccountInfo}. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static fromAccountInfo( + accountInfo: web3.AccountInfo, + offset = 0, + ): [GlobalDepositLog, number] { + return GlobalDepositLog.deserialize(accountInfo.data, offset); + } + + /** + * Retrieves the account info from the provided address and deserializes + * the {@link GlobalDepositLog} from its data. + * + * @throws Error if no account info is found at the address or if deserialization fails + */ + static async fromAccountAddress( + connection: web3.Connection, + address: web3.PublicKey, + commitmentOrConfig?: web3.Commitment | web3.GetAccountInfoConfig, + ): Promise { + const accountInfo = await connection.getAccountInfo( + address, + commitmentOrConfig, + ); + if (accountInfo == null) { + throw new Error(`Unable to find GlobalDepositLog account at ${address}`); + } + return GlobalDepositLog.fromAccountInfo(accountInfo, 0)[0]; + } + + /** + * Provides a {@link web3.Connection.getProgramAccounts} config builder, + * to fetch accounts matching filters that can be specified via that builder. + * + * @param programId - the program that owns the accounts we are filtering + */ + static gpaBuilder( + programId: web3.PublicKey = new web3.PublicKey( + "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms", + ), + ) { + return beetSolana.GpaBuilder.fromStruct(programId, globalDepositLogBeet); + } + + /** + * Deserializes the {@link GlobalDepositLog} from the provided data Buffer. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static deserialize(buf: Buffer, offset = 0): [GlobalDepositLog, number] { + return globalDepositLogBeet.deserialize(buf, offset); + } + + /** + * Serializes the {@link GlobalDepositLog} into a Buffer. + * @returns a tuple of the created Buffer and the offset up to which the buffer was written to store it. + */ + serialize(): [Buffer, number] { + return globalDepositLogBeet.serialize(this); + } + + /** + * Returns the byteSize of a {@link Buffer} holding the serialized data of + * {@link GlobalDepositLog} + */ + static get byteSize() { + return globalDepositLogBeet.byteSize; + } + + /** + * Fetches the minimum balance needed to exempt an account holding + * {@link GlobalDepositLog} data from rent + * + * @param connection used to retrieve the rent exemption information + */ + static async getMinimumBalanceForRentExemption( + connection: web3.Connection, + commitment?: web3.Commitment, + ): Promise { + return connection.getMinimumBalanceForRentExemption( + GlobalDepositLog.byteSize, + commitment, + ); + } + + /** + * Determines if the provided {@link Buffer} has the correct byte size to + * hold {@link GlobalDepositLog} data. + */ + static hasCorrectByteSize(buf: Buffer, offset = 0) { + return buf.byteLength - offset === GlobalDepositLog.byteSize; + } + + /** + * Returns a readable version of {@link GlobalDepositLog} properties + * and can be used to convert to JSON and/or logging + */ + pretty() { + return { + global: this.global.toBase58(), + trader: this.trader.toBase58(), + globalAtoms: this.globalAtoms, + }; + } +} + +/** + * @category Accounts + * @category generated + */ +export const globalDepositLogBeet = new beet.BeetStruct< + GlobalDepositLog, + GlobalDepositLogArgs +>( + [ + ["global", beetSolana.publicKey], + ["trader", beetSolana.publicKey], + ["globalAtoms", globalAtomsBeet], + ], + GlobalDepositLog.fromArgs, + "GlobalDepositLog", +); diff --git a/client/ts/src/manifest/accounts/PlaceOrderLog.ts b/client/ts/src/manifest/accounts/PlaceOrderLog.ts new file mode 100644 index 000000000..0c7ffb4de --- /dev/null +++ b/client/ts/src/manifest/accounts/PlaceOrderLog.ts @@ -0,0 +1,220 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as web3 from "@solana/web3.js"; +import * as beet from "@metaplex-foundation/beet"; +import * as beetSolana from "@metaplex-foundation/beet-solana"; +import { + QuoteAtomsPerBaseAtom, + quoteAtomsPerBaseAtomBeet, +} from "./QuoteAtomsPerBaseAtom"; +import { BaseAtoms, baseAtomsBeet } from "./BaseAtoms"; +import { OrderType, orderTypeBeet } from "../types/OrderType"; + +/** + * Arguments used to create {@link PlaceOrderLog} + * @category Accounts + * @category generated + */ +export type PlaceOrderLogArgs = { + market: web3.PublicKey; + trader: web3.PublicKey; + price: QuoteAtomsPerBaseAtom; + baseAtoms: BaseAtoms; + orderSequenceNumber: beet.bignum; + orderIndex: number; + lastValidSlot: number; + orderType: OrderType; + isBid: boolean; + padding: number[] /* size: 6 */; +}; +/** + * Holds the data for the {@link PlaceOrderLog} Account and provides de/serialization + * functionality for that data + * + * @category Accounts + * @category generated + */ +export class PlaceOrderLog implements PlaceOrderLogArgs { + private constructor( + readonly market: web3.PublicKey, + readonly trader: web3.PublicKey, + readonly price: QuoteAtomsPerBaseAtom, + readonly baseAtoms: BaseAtoms, + readonly orderSequenceNumber: beet.bignum, + readonly orderIndex: number, + readonly lastValidSlot: number, + readonly orderType: OrderType, + readonly isBid: boolean, + readonly padding: number[] /* size: 6 */, + ) {} + + /** + * Creates a {@link PlaceOrderLog} instance from the provided args. + */ + static fromArgs(args: PlaceOrderLogArgs) { + return new PlaceOrderLog( + args.market, + args.trader, + args.price, + args.baseAtoms, + args.orderSequenceNumber, + args.orderIndex, + args.lastValidSlot, + args.orderType, + args.isBid, + args.padding, + ); + } + + /** + * Deserializes the {@link PlaceOrderLog} from the data of the provided {@link web3.AccountInfo}. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static fromAccountInfo( + accountInfo: web3.AccountInfo, + offset = 0, + ): [PlaceOrderLog, number] { + return PlaceOrderLog.deserialize(accountInfo.data, offset); + } + + /** + * Retrieves the account info from the provided address and deserializes + * the {@link PlaceOrderLog} from its data. + * + * @throws Error if no account info is found at the address or if deserialization fails + */ + static async fromAccountAddress( + connection: web3.Connection, + address: web3.PublicKey, + commitmentOrConfig?: web3.Commitment | web3.GetAccountInfoConfig, + ): Promise { + const accountInfo = await connection.getAccountInfo( + address, + commitmentOrConfig, + ); + if (accountInfo == null) { + throw new Error(`Unable to find PlaceOrderLog account at ${address}`); + } + return PlaceOrderLog.fromAccountInfo(accountInfo, 0)[0]; + } + + /** + * Provides a {@link web3.Connection.getProgramAccounts} config builder, + * to fetch accounts matching filters that can be specified via that builder. + * + * @param programId - the program that owns the accounts we are filtering + */ + static gpaBuilder( + programId: web3.PublicKey = new web3.PublicKey( + "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms", + ), + ) { + return beetSolana.GpaBuilder.fromStruct(programId, placeOrderLogBeet); + } + + /** + * Deserializes the {@link PlaceOrderLog} from the provided data Buffer. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static deserialize(buf: Buffer, offset = 0): [PlaceOrderLog, number] { + return placeOrderLogBeet.deserialize(buf, offset); + } + + /** + * Serializes the {@link PlaceOrderLog} into a Buffer. + * @returns a tuple of the created Buffer and the offset up to which the buffer was written to store it. + */ + serialize(): [Buffer, number] { + return placeOrderLogBeet.serialize(this); + } + + /** + * Returns the byteSize of a {@link Buffer} holding the serialized data of + * {@link PlaceOrderLog} + */ + static get byteSize() { + return placeOrderLogBeet.byteSize; + } + + /** + * Fetches the minimum balance needed to exempt an account holding + * {@link PlaceOrderLog} data from rent + * + * @param connection used to retrieve the rent exemption information + */ + static async getMinimumBalanceForRentExemption( + connection: web3.Connection, + commitment?: web3.Commitment, + ): Promise { + return connection.getMinimumBalanceForRentExemption( + PlaceOrderLog.byteSize, + commitment, + ); + } + + /** + * Determines if the provided {@link Buffer} has the correct byte size to + * hold {@link PlaceOrderLog} data. + */ + static hasCorrectByteSize(buf: Buffer, offset = 0) { + return buf.byteLength - offset === PlaceOrderLog.byteSize; + } + + /** + * Returns a readable version of {@link PlaceOrderLog} properties + * and can be used to convert to JSON and/or logging + */ + pretty() { + return { + market: this.market.toBase58(), + trader: this.trader.toBase58(), + price: this.price, + baseAtoms: this.baseAtoms, + orderSequenceNumber: (() => { + const x = <{ toNumber: () => number }>this.orderSequenceNumber; + if (typeof x.toNumber === "function") { + try { + return x.toNumber(); + } catch (_) { + return x; + } + } + return x; + })(), + orderIndex: this.orderIndex, + lastValidSlot: this.lastValidSlot, + orderType: "OrderType." + OrderType[this.orderType], + isBid: this.isBid, + padding: this.padding, + }; + } +} + +/** + * @category Accounts + * @category generated + */ +export const placeOrderLogBeet = new beet.BeetStruct< + PlaceOrderLog, + PlaceOrderLogArgs +>( + [ + ["market", beetSolana.publicKey], + ["trader", beetSolana.publicKey], + ["price", quoteAtomsPerBaseAtomBeet], + ["baseAtoms", baseAtomsBeet], + ["orderSequenceNumber", beet.u64], + ["orderIndex", beet.u32], + ["lastValidSlot", beet.u32], + ["orderType", orderTypeBeet], + ["isBid", beet.bool], + ["padding", beet.uniformFixedSizeArray(beet.u8, 6)], + ], + PlaceOrderLog.fromArgs, + "PlaceOrderLog", +); diff --git a/client/ts/src/manifest/accounts/QuoteAtoms.ts b/client/ts/src/manifest/accounts/QuoteAtoms.ts new file mode 100644 index 000000000..0e3b02842 --- /dev/null +++ b/client/ts/src/manifest/accounts/QuoteAtoms.ts @@ -0,0 +1,160 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from "@metaplex-foundation/beet"; +import * as web3 from "@solana/web3.js"; +import * as beetSolana from "@metaplex-foundation/beet-solana"; + +/** + * Arguments used to create {@link QuoteAtoms} + * @category Accounts + * @category generated + */ +export type QuoteAtomsArgs = { + inner: beet.bignum; +}; +/** + * Holds the data for the {@link QuoteAtoms} Account and provides de/serialization + * functionality for that data + * + * @category Accounts + * @category generated + */ +export class QuoteAtoms implements QuoteAtomsArgs { + private constructor(readonly inner: beet.bignum) {} + + /** + * Creates a {@link QuoteAtoms} instance from the provided args. + */ + static fromArgs(args: QuoteAtomsArgs) { + return new QuoteAtoms(args.inner); + } + + /** + * Deserializes the {@link QuoteAtoms} from the data of the provided {@link web3.AccountInfo}. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static fromAccountInfo( + accountInfo: web3.AccountInfo, + offset = 0, + ): [QuoteAtoms, number] { + return QuoteAtoms.deserialize(accountInfo.data, offset); + } + + /** + * Retrieves the account info from the provided address and deserializes + * the {@link QuoteAtoms} from its data. + * + * @throws Error if no account info is found at the address or if deserialization fails + */ + static async fromAccountAddress( + connection: web3.Connection, + address: web3.PublicKey, + commitmentOrConfig?: web3.Commitment | web3.GetAccountInfoConfig, + ): Promise { + const accountInfo = await connection.getAccountInfo( + address, + commitmentOrConfig, + ); + if (accountInfo == null) { + throw new Error(`Unable to find QuoteAtoms account at ${address}`); + } + return QuoteAtoms.fromAccountInfo(accountInfo, 0)[0]; + } + + /** + * Provides a {@link web3.Connection.getProgramAccounts} config builder, + * to fetch accounts matching filters that can be specified via that builder. + * + * @param programId - the program that owns the accounts we are filtering + */ + static gpaBuilder( + programId: web3.PublicKey = new web3.PublicKey( + "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms", + ), + ) { + return beetSolana.GpaBuilder.fromStruct(programId, quoteAtomsBeet); + } + + /** + * Deserializes the {@link QuoteAtoms} from the provided data Buffer. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static deserialize(buf: Buffer, offset = 0): [QuoteAtoms, number] { + return quoteAtomsBeet.deserialize(buf, offset); + } + + /** + * Serializes the {@link QuoteAtoms} into a Buffer. + * @returns a tuple of the created Buffer and the offset up to which the buffer was written to store it. + */ + serialize(): [Buffer, number] { + return quoteAtomsBeet.serialize(this); + } + + /** + * Returns the byteSize of a {@link Buffer} holding the serialized data of + * {@link QuoteAtoms} + */ + static get byteSize() { + return quoteAtomsBeet.byteSize; + } + + /** + * Fetches the minimum balance needed to exempt an account holding + * {@link QuoteAtoms} data from rent + * + * @param connection used to retrieve the rent exemption information + */ + static async getMinimumBalanceForRentExemption( + connection: web3.Connection, + commitment?: web3.Commitment, + ): Promise { + return connection.getMinimumBalanceForRentExemption( + QuoteAtoms.byteSize, + commitment, + ); + } + + /** + * Determines if the provided {@link Buffer} has the correct byte size to + * hold {@link QuoteAtoms} data. + */ + static hasCorrectByteSize(buf: Buffer, offset = 0) { + return buf.byteLength - offset === QuoteAtoms.byteSize; + } + + /** + * Returns a readable version of {@link QuoteAtoms} properties + * and can be used to convert to JSON and/or logging + */ + pretty() { + return { + inner: (() => { + const x = <{ toNumber: () => number }>this.inner; + if (typeof x.toNumber === "function") { + try { + return x.toNumber(); + } catch (_) { + return x; + } + } + return x; + })(), + }; + } +} + +/** + * @category Accounts + * @category generated + */ +export const quoteAtomsBeet = new beet.BeetStruct( + [["inner", beet.u64]], + QuoteAtoms.fromArgs, + "QuoteAtoms", +); diff --git a/client/ts/src/manifest/accounts/QuoteAtomsPerBaseAtom.ts b/client/ts/src/manifest/accounts/QuoteAtomsPerBaseAtom.ts new file mode 100644 index 000000000..b67eb48d8 --- /dev/null +++ b/client/ts/src/manifest/accounts/QuoteAtomsPerBaseAtom.ts @@ -0,0 +1,158 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from "@metaplex-foundation/beet"; +import * as web3 from "@solana/web3.js"; +import * as beetSolana from "@metaplex-foundation/beet-solana"; + +/** + * Arguments used to create {@link QuoteAtomsPerBaseAtom} + * @category Accounts + * @category generated + */ +export type QuoteAtomsPerBaseAtomArgs = { + inner: beet.bignum[] /* size: 2 */; +}; +/** + * Holds the data for the {@link QuoteAtomsPerBaseAtom} Account and provides de/serialization + * functionality for that data + * + * @category Accounts + * @category generated + */ +export class QuoteAtomsPerBaseAtom implements QuoteAtomsPerBaseAtomArgs { + private constructor(readonly inner: beet.bignum[] /* size: 2 */) {} + + /** + * Creates a {@link QuoteAtomsPerBaseAtom} instance from the provided args. + */ + static fromArgs(args: QuoteAtomsPerBaseAtomArgs) { + return new QuoteAtomsPerBaseAtom(args.inner); + } + + /** + * Deserializes the {@link QuoteAtomsPerBaseAtom} from the data of the provided {@link web3.AccountInfo}. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static fromAccountInfo( + accountInfo: web3.AccountInfo, + offset = 0, + ): [QuoteAtomsPerBaseAtom, number] { + return QuoteAtomsPerBaseAtom.deserialize(accountInfo.data, offset); + } + + /** + * Retrieves the account info from the provided address and deserializes + * the {@link QuoteAtomsPerBaseAtom} from its data. + * + * @throws Error if no account info is found at the address or if deserialization fails + */ + static async fromAccountAddress( + connection: web3.Connection, + address: web3.PublicKey, + commitmentOrConfig?: web3.Commitment | web3.GetAccountInfoConfig, + ): Promise { + const accountInfo = await connection.getAccountInfo( + address, + commitmentOrConfig, + ); + if (accountInfo == null) { + throw new Error( + `Unable to find QuoteAtomsPerBaseAtom account at ${address}`, + ); + } + return QuoteAtomsPerBaseAtom.fromAccountInfo(accountInfo, 0)[0]; + } + + /** + * Provides a {@link web3.Connection.getProgramAccounts} config builder, + * to fetch accounts matching filters that can be specified via that builder. + * + * @param programId - the program that owns the accounts we are filtering + */ + static gpaBuilder( + programId: web3.PublicKey = new web3.PublicKey( + "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms", + ), + ) { + return beetSolana.GpaBuilder.fromStruct( + programId, + quoteAtomsPerBaseAtomBeet, + ); + } + + /** + * Deserializes the {@link QuoteAtomsPerBaseAtom} from the provided data Buffer. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static deserialize(buf: Buffer, offset = 0): [QuoteAtomsPerBaseAtom, number] { + return quoteAtomsPerBaseAtomBeet.deserialize(buf, offset); + } + + /** + * Serializes the {@link QuoteAtomsPerBaseAtom} into a Buffer. + * @returns a tuple of the created Buffer and the offset up to which the buffer was written to store it. + */ + serialize(): [Buffer, number] { + return quoteAtomsPerBaseAtomBeet.serialize(this); + } + + /** + * Returns the byteSize of a {@link Buffer} holding the serialized data of + * {@link QuoteAtomsPerBaseAtom} + */ + static get byteSize() { + return quoteAtomsPerBaseAtomBeet.byteSize; + } + + /** + * Fetches the minimum balance needed to exempt an account holding + * {@link QuoteAtomsPerBaseAtom} data from rent + * + * @param connection used to retrieve the rent exemption information + */ + static async getMinimumBalanceForRentExemption( + connection: web3.Connection, + commitment?: web3.Commitment, + ): Promise { + return connection.getMinimumBalanceForRentExemption( + QuoteAtomsPerBaseAtom.byteSize, + commitment, + ); + } + + /** + * Determines if the provided {@link Buffer} has the correct byte size to + * hold {@link QuoteAtomsPerBaseAtom} data. + */ + static hasCorrectByteSize(buf: Buffer, offset = 0) { + return buf.byteLength - offset === QuoteAtomsPerBaseAtom.byteSize; + } + + /** + * Returns a readable version of {@link QuoteAtomsPerBaseAtom} properties + * and can be used to convert to JSON and/or logging + */ + pretty() { + return { + inner: this.inner, + }; + } +} + +/** + * @category Accounts + * @category generated + */ +export const quoteAtomsPerBaseAtomBeet = new beet.BeetStruct< + QuoteAtomsPerBaseAtom, + QuoteAtomsPerBaseAtomArgs +>( + [["inner", beet.uniformFixedSizeArray(beet.u64, 2)]], + QuoteAtomsPerBaseAtom.fromArgs, + "QuoteAtomsPerBaseAtom", +); diff --git a/client/ts/src/manifest/accounts/WithdrawLog.ts b/client/ts/src/manifest/accounts/WithdrawLog.ts new file mode 100644 index 000000000..f1f9038b2 --- /dev/null +++ b/client/ts/src/manifest/accounts/WithdrawLog.ts @@ -0,0 +1,184 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as web3 from "@solana/web3.js"; +import * as beet from "@metaplex-foundation/beet"; +import * as beetSolana from "@metaplex-foundation/beet-solana"; + +/** + * Arguments used to create {@link WithdrawLog} + * @category Accounts + * @category generated + */ +export type WithdrawLogArgs = { + market: web3.PublicKey; + trader: web3.PublicKey; + mint: web3.PublicKey; + amountAtoms: beet.bignum; +}; +/** + * Holds the data for the {@link WithdrawLog} Account and provides de/serialization + * functionality for that data + * + * @category Accounts + * @category generated + */ +export class WithdrawLog implements WithdrawLogArgs { + private constructor( + readonly market: web3.PublicKey, + readonly trader: web3.PublicKey, + readonly mint: web3.PublicKey, + readonly amountAtoms: beet.bignum, + ) {} + + /** + * Creates a {@link WithdrawLog} instance from the provided args. + */ + static fromArgs(args: WithdrawLogArgs) { + return new WithdrawLog( + args.market, + args.trader, + args.mint, + args.amountAtoms, + ); + } + + /** + * Deserializes the {@link WithdrawLog} from the data of the provided {@link web3.AccountInfo}. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static fromAccountInfo( + accountInfo: web3.AccountInfo, + offset = 0, + ): [WithdrawLog, number] { + return WithdrawLog.deserialize(accountInfo.data, offset); + } + + /** + * Retrieves the account info from the provided address and deserializes + * the {@link WithdrawLog} from its data. + * + * @throws Error if no account info is found at the address or if deserialization fails + */ + static async fromAccountAddress( + connection: web3.Connection, + address: web3.PublicKey, + commitmentOrConfig?: web3.Commitment | web3.GetAccountInfoConfig, + ): Promise { + const accountInfo = await connection.getAccountInfo( + address, + commitmentOrConfig, + ); + if (accountInfo == null) { + throw new Error(`Unable to find WithdrawLog account at ${address}`); + } + return WithdrawLog.fromAccountInfo(accountInfo, 0)[0]; + } + + /** + * Provides a {@link web3.Connection.getProgramAccounts} config builder, + * to fetch accounts matching filters that can be specified via that builder. + * + * @param programId - the program that owns the accounts we are filtering + */ + static gpaBuilder( + programId: web3.PublicKey = new web3.PublicKey( + "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms", + ), + ) { + return beetSolana.GpaBuilder.fromStruct(programId, withdrawLogBeet); + } + + /** + * Deserializes the {@link WithdrawLog} from the provided data Buffer. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static deserialize(buf: Buffer, offset = 0): [WithdrawLog, number] { + return withdrawLogBeet.deserialize(buf, offset); + } + + /** + * Serializes the {@link WithdrawLog} into a Buffer. + * @returns a tuple of the created Buffer and the offset up to which the buffer was written to store it. + */ + serialize(): [Buffer, number] { + return withdrawLogBeet.serialize(this); + } + + /** + * Returns the byteSize of a {@link Buffer} holding the serialized data of + * {@link WithdrawLog} + */ + static get byteSize() { + return withdrawLogBeet.byteSize; + } + + /** + * Fetches the minimum balance needed to exempt an account holding + * {@link WithdrawLog} data from rent + * + * @param connection used to retrieve the rent exemption information + */ + static async getMinimumBalanceForRentExemption( + connection: web3.Connection, + commitment?: web3.Commitment, + ): Promise { + return connection.getMinimumBalanceForRentExemption( + WithdrawLog.byteSize, + commitment, + ); + } + + /** + * Determines if the provided {@link Buffer} has the correct byte size to + * hold {@link WithdrawLog} data. + */ + static hasCorrectByteSize(buf: Buffer, offset = 0) { + return buf.byteLength - offset === WithdrawLog.byteSize; + } + + /** + * Returns a readable version of {@link WithdrawLog} properties + * and can be used to convert to JSON and/or logging + */ + pretty() { + return { + market: this.market.toBase58(), + trader: this.trader.toBase58(), + mint: this.mint.toBase58(), + amountAtoms: (() => { + const x = <{ toNumber: () => number }>this.amountAtoms; + if (typeof x.toNumber === "function") { + try { + return x.toNumber(); + } catch (_) { + return x; + } + } + return x; + })(), + }; + } +} + +/** + * @category Accounts + * @category generated + */ +export const withdrawLogBeet = new beet.BeetStruct< + WithdrawLog, + WithdrawLogArgs +>( + [ + ["market", beetSolana.publicKey], + ["trader", beetSolana.publicKey], + ["mint", beetSolana.publicKey], + ["amountAtoms", beet.u64], + ], + WithdrawLog.fromArgs, + "WithdrawLog", +); diff --git a/client/ts/src/manifest/accounts/index.ts b/client/ts/src/manifest/accounts/index.ts new file mode 100644 index 000000000..87fc3a6fa --- /dev/null +++ b/client/ts/src/manifest/accounts/index.ts @@ -0,0 +1,49 @@ +export * from "./BaseAtoms"; +export * from "./CancelOrderLog"; +export * from "./ClaimSeatLog"; +export * from "./CreateMarketLog"; +export * from "./DepositLog"; +export * from "./FillLog"; +export * from "./GlobalAddTraderLog"; +export * from "./GlobalAtoms"; +export * from "./GlobalClaimSeatLog"; +export * from "./GlobalCreateLog"; +export * from "./GlobalDepositLog"; +export * from "./PlaceOrderLog"; +export * from "./QuoteAtoms"; +export * from "./QuoteAtomsPerBaseAtom"; +export * from "./WithdrawLog"; + +import { CreateMarketLog } from "./CreateMarketLog"; +import { ClaimSeatLog } from "./ClaimSeatLog"; +import { DepositLog } from "./DepositLog"; +import { WithdrawLog } from "./WithdrawLog"; +import { FillLog } from "./FillLog"; +import { PlaceOrderLog } from "./PlaceOrderLog"; +import { CancelOrderLog } from "./CancelOrderLog"; +import { GlobalCreateLog } from "./GlobalCreateLog"; +import { GlobalAddTraderLog } from "./GlobalAddTraderLog"; +import { GlobalClaimSeatLog } from "./GlobalClaimSeatLog"; +import { GlobalDepositLog } from "./GlobalDepositLog"; +import { QuoteAtoms } from "./QuoteAtoms"; +import { BaseAtoms } from "./BaseAtoms"; +import { GlobalAtoms } from "./GlobalAtoms"; +import { QuoteAtomsPerBaseAtom } from "./QuoteAtomsPerBaseAtom"; + +export const accountProviders = { + CreateMarketLog, + ClaimSeatLog, + DepositLog, + WithdrawLog, + FillLog, + PlaceOrderLog, + CancelOrderLog, + GlobalCreateLog, + GlobalAddTraderLog, + GlobalClaimSeatLog, + GlobalDepositLog, + QuoteAtoms, + BaseAtoms, + GlobalAtoms, + QuoteAtomsPerBaseAtom, +}; diff --git a/client/ts/src/manifest/errors/index.ts b/client/ts/src/manifest/errors/index.ts new file mode 100644 index 000000000..a0312fc00 --- /dev/null +++ b/client/ts/src/manifest/errors/index.ts @@ -0,0 +1,414 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +type ErrorWithCode = Error & { code: number }; +type MaybeErrorWithCode = ErrorWithCode | null | undefined; + +const createErrorFromCodeLookup: Map ErrorWithCode> = new Map(); +const createErrorFromNameLookup: Map ErrorWithCode> = new Map(); + +/** + * InvalidMarketParameters: 'Invalid market parameters error' + * + * @category Errors + * @category generated + */ +export class InvalidMarketParametersError extends Error { + readonly code: number = 0x0; + readonly name: string = "InvalidMarketParameters"; + constructor() { + super("Invalid market parameters error"); + if (typeof Error.captureStackTrace === "function") { + Error.captureStackTrace(this, InvalidMarketParametersError); + } + } +} + +createErrorFromCodeLookup.set(0x0, () => new InvalidMarketParametersError()); +createErrorFromNameLookup.set( + "InvalidMarketParameters", + () => new InvalidMarketParametersError(), +); + +/** + * InvalidDepositAccounts: 'Invalid deposit accounts error' + * + * @category Errors + * @category generated + */ +export class InvalidDepositAccountsError extends Error { + readonly code: number = 0x1; + readonly name: string = "InvalidDepositAccounts"; + constructor() { + super("Invalid deposit accounts error"); + if (typeof Error.captureStackTrace === "function") { + Error.captureStackTrace(this, InvalidDepositAccountsError); + } + } +} + +createErrorFromCodeLookup.set(0x1, () => new InvalidDepositAccountsError()); +createErrorFromNameLookup.set( + "InvalidDepositAccounts", + () => new InvalidDepositAccountsError(), +); + +/** + * InvalidWithdrawAccounts: 'Invalid withdraw accounts error' + * + * @category Errors + * @category generated + */ +export class InvalidWithdrawAccountsError extends Error { + readonly code: number = 0x2; + readonly name: string = "InvalidWithdrawAccounts"; + constructor() { + super("Invalid withdraw accounts error"); + if (typeof Error.captureStackTrace === "function") { + Error.captureStackTrace(this, InvalidWithdrawAccountsError); + } + } +} + +createErrorFromCodeLookup.set(0x2, () => new InvalidWithdrawAccountsError()); +createErrorFromNameLookup.set( + "InvalidWithdrawAccounts", + () => new InvalidWithdrawAccountsError(), +); + +/** + * InvalidCancel: 'Invalid cancel error' + * + * @category Errors + * @category generated + */ +export class InvalidCancelError extends Error { + readonly code: number = 0x3; + readonly name: string = "InvalidCancel"; + constructor() { + super("Invalid cancel error"); + if (typeof Error.captureStackTrace === "function") { + Error.captureStackTrace(this, InvalidCancelError); + } + } +} + +createErrorFromCodeLookup.set(0x3, () => new InvalidCancelError()); +createErrorFromNameLookup.set("InvalidCancel", () => new InvalidCancelError()); + +/** + * InvalidFreeList: 'Internal free list corruption error' + * + * @category Errors + * @category generated + */ +export class InvalidFreeListError extends Error { + readonly code: number = 0x4; + readonly name: string = "InvalidFreeList"; + constructor() { + super("Internal free list corruption error"); + if (typeof Error.captureStackTrace === "function") { + Error.captureStackTrace(this, InvalidFreeListError); + } + } +} + +createErrorFromCodeLookup.set(0x4, () => new InvalidFreeListError()); +createErrorFromNameLookup.set( + "InvalidFreeList", + () => new InvalidFreeListError(), +); + +/** + * AlreadyClaimedSeat: 'Cannot claim a second seat for the same trader' + * + * @category Errors + * @category generated + */ +export class AlreadyClaimedSeatError extends Error { + readonly code: number = 0x5; + readonly name: string = "AlreadyClaimedSeat"; + constructor() { + super("Cannot claim a second seat for the same trader"); + if (typeof Error.captureStackTrace === "function") { + Error.captureStackTrace(this, AlreadyClaimedSeatError); + } + } +} + +createErrorFromCodeLookup.set(0x5, () => new AlreadyClaimedSeatError()); +createErrorFromNameLookup.set( + "AlreadyClaimedSeat", + () => new AlreadyClaimedSeatError(), +); + +/** + * PostOnlyCrosses: 'Matched on a post only order' + * + * @category Errors + * @category generated + */ +export class PostOnlyCrossesError extends Error { + readonly code: number = 0x6; + readonly name: string = "PostOnlyCrosses"; + constructor() { + super("Matched on a post only order"); + if (typeof Error.captureStackTrace === "function") { + Error.captureStackTrace(this, PostOnlyCrossesError); + } + } +} + +createErrorFromCodeLookup.set(0x6, () => new PostOnlyCrossesError()); +createErrorFromNameLookup.set( + "PostOnlyCrosses", + () => new PostOnlyCrossesError(), +); + +/** + * AlreadyExpired: 'New order is already expired' + * + * @category Errors + * @category generated + */ +export class AlreadyExpiredError extends Error { + readonly code: number = 0x7; + readonly name: string = "AlreadyExpired"; + constructor() { + super("New order is already expired"); + if (typeof Error.captureStackTrace === "function") { + Error.captureStackTrace(this, AlreadyExpiredError); + } + } +} + +createErrorFromCodeLookup.set(0x7, () => new AlreadyExpiredError()); +createErrorFromNameLookup.set( + "AlreadyExpired", + () => new AlreadyExpiredError(), +); + +/** + * InsufficientOut: 'Less than minimum out amount' + * + * @category Errors + * @category generated + */ +export class InsufficientOutError extends Error { + readonly code: number = 0x8; + readonly name: string = "InsufficientOut"; + constructor() { + super("Less than minimum out amount"); + if (typeof Error.captureStackTrace === "function") { + Error.captureStackTrace(this, InsufficientOutError); + } + } +} + +createErrorFromCodeLookup.set(0x8, () => new InsufficientOutError()); +createErrorFromNameLookup.set( + "InsufficientOut", + () => new InsufficientOutError(), +); + +/** + * InvalidPlaceOrderFromWalletParams: 'Invalid place order from wallet params' + * + * @category Errors + * @category generated + */ +export class InvalidPlaceOrderFromWalletParamsError extends Error { + readonly code: number = 0x9; + readonly name: string = "InvalidPlaceOrderFromWalletParams"; + constructor() { + super("Invalid place order from wallet params"); + if (typeof Error.captureStackTrace === "function") { + Error.captureStackTrace(this, InvalidPlaceOrderFromWalletParamsError); + } + } +} + +createErrorFromCodeLookup.set( + 0x9, + () => new InvalidPlaceOrderFromWalletParamsError(), +); +createErrorFromNameLookup.set( + "InvalidPlaceOrderFromWalletParams", + () => new InvalidPlaceOrderFromWalletParamsError(), +); + +/** + * WrongIndexHintParams: 'Index hint did not match actual index' + * + * @category Errors + * @category generated + */ +export class WrongIndexHintParamsError extends Error { + readonly code: number = 0xa; + readonly name: string = "WrongIndexHintParams"; + constructor() { + super("Index hint did not match actual index"); + if (typeof Error.captureStackTrace === "function") { + Error.captureStackTrace(this, WrongIndexHintParamsError); + } + } +} + +createErrorFromCodeLookup.set(0xa, () => new WrongIndexHintParamsError()); +createErrorFromNameLookup.set( + "WrongIndexHintParams", + () => new WrongIndexHintParamsError(), +); + +/** + * PriceNotPositive: 'Price is not positive' + * + * @category Errors + * @category generated + */ +export class PriceNotPositiveError extends Error { + readonly code: number = 0xb; + readonly name: string = "PriceNotPositive"; + constructor() { + super("Price is not positive"); + if (typeof Error.captureStackTrace === "function") { + Error.captureStackTrace(this, PriceNotPositiveError); + } + } +} + +createErrorFromCodeLookup.set(0xb, () => new PriceNotPositiveError()); +createErrorFromNameLookup.set( + "PriceNotPositive", + () => new PriceNotPositiveError(), +); + +/** + * OrderWouldOverflow: 'Order settlement would overflow' + * + * @category Errors + * @category generated + */ +export class OrderWouldOverflowError extends Error { + readonly code: number = 0xc; + readonly name: string = "OrderWouldOverflow"; + constructor() { + super("Order settlement would overflow"); + if (typeof Error.captureStackTrace === "function") { + Error.captureStackTrace(this, OrderWouldOverflowError); + } + } +} + +createErrorFromCodeLookup.set(0xc, () => new OrderWouldOverflowError()); +createErrorFromNameLookup.set( + "OrderWouldOverflow", + () => new OrderWouldOverflowError(), +); + +/** + * OrderTooSmall: 'Order is too small to settle any value' + * + * @category Errors + * @category generated + */ +export class OrderTooSmallError extends Error { + readonly code: number = 0xd; + readonly name: string = "OrderTooSmall"; + constructor() { + super("Order is too small to settle any value"); + if (typeof Error.captureStackTrace === "function") { + Error.captureStackTrace(this, OrderTooSmallError); + } + } +} + +createErrorFromCodeLookup.set(0xd, () => new OrderTooSmallError()); +createErrorFromNameLookup.set("OrderTooSmall", () => new OrderTooSmallError()); + +/** + * Overflow: 'Overflow in token addition' + * + * @category Errors + * @category generated + */ +export class OverflowError extends Error { + readonly code: number = 0xe; + readonly name: string = "Overflow"; + constructor() { + super("Overflow in token addition"); + if (typeof Error.captureStackTrace === "function") { + Error.captureStackTrace(this, OverflowError); + } + } +} + +createErrorFromCodeLookup.set(0xe, () => new OverflowError()); +createErrorFromNameLookup.set("Overflow", () => new OverflowError()); + +/** + * MissingGlobal: 'Missing Global account' + * + * @category Errors + * @category generated + */ +export class MissingGlobalError extends Error { + readonly code: number = 0xf; + readonly name: string = "MissingGlobal"; + constructor() { + super("Missing Global account"); + if (typeof Error.captureStackTrace === "function") { + Error.captureStackTrace(this, MissingGlobalError); + } + } +} + +createErrorFromCodeLookup.set(0xf, () => new MissingGlobalError()); +createErrorFromNameLookup.set("MissingGlobal", () => new MissingGlobalError()); + +/** + * GlobalInsufficient: 'Insufficient funds on global account to rest an order' + * + * @category Errors + * @category generated + */ +export class GlobalInsufficientError extends Error { + readonly code: number = 0x10; + readonly name: string = "GlobalInsufficient"; + constructor() { + super("Insufficient funds on global account to rest an order"); + if (typeof Error.captureStackTrace === "function") { + Error.captureStackTrace(this, GlobalInsufficientError); + } + } +} + +createErrorFromCodeLookup.set(0x10, () => new GlobalInsufficientError()); +createErrorFromNameLookup.set( + "GlobalInsufficient", + () => new GlobalInsufficientError(), +); + +/** + * Attempts to resolve a custom program error from the provided error code. + * @category Errors + * @category generated + */ +export function errorFromCode(code: number): MaybeErrorWithCode { + const createError = createErrorFromCodeLookup.get(code); + return createError != null ? createError() : null; +} + +/** + * Attempts to resolve a custom program error from the provided error name, i.e. 'Unauthorized'. + * @category Errors + * @category generated + */ +export function errorFromName(name: string): MaybeErrorWithCode { + const createError = createErrorFromNameLookup.get(name); + return createError != null ? createError() : null; +} diff --git a/client/ts/src/manifest/index.ts b/client/ts/src/manifest/index.ts new file mode 100644 index 000000000..c6f52337e --- /dev/null +++ b/client/ts/src/manifest/index.ts @@ -0,0 +1,21 @@ +import { PublicKey } from "@solana/web3.js"; +export * from "./accounts"; +export * from "./errors"; +export * from "./instructions"; +export * from "./types"; + +/** + * Program address + * + * @category constants + * @category generated + */ +export const PROGRAM_ADDRESS = "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms"; + +/** + * Program public key + * + * @category constants + * @category generated + */ +export const PROGRAM_ID = new PublicKey(PROGRAM_ADDRESS); diff --git a/client/ts/src/manifest/instructions/BatchUpdate.ts b/client/ts/src/manifest/instructions/BatchUpdate.ts new file mode 100644 index 000000000..c6a6295e0 --- /dev/null +++ b/client/ts/src/manifest/instructions/BatchUpdate.ts @@ -0,0 +1,169 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from "@metaplex-foundation/beet"; +import * as web3 from "@solana/web3.js"; +import { + BatchUpdateParams, + batchUpdateParamsBeet, +} from "../types/BatchUpdateParams"; + +/** + * @category Instructions + * @category BatchUpdate + * @category generated + */ +export type BatchUpdateInstructionArgs = { + params: BatchUpdateParams; +}; +/** + * @category Instructions + * @category BatchUpdate + * @category generated + */ +export const BatchUpdateStruct = new beet.FixableBeetArgsStruct< + BatchUpdateInstructionArgs & { + instructionDiscriminator: number; + } +>( + [ + ["instructionDiscriminator", beet.u8], + ["params", batchUpdateParamsBeet], + ], + "BatchUpdateInstructionArgs", +); +/** + * Accounts required by the _BatchUpdate_ instruction + * + * @property [_writable_, **signer**] payer + * @property [_writable_] market + * @property [] baseMint + * @property [_writable_] baseGlobal + * @property [] baseGlobalVault + * @property [] baseMarketVault + * @property [] baseTokenProgram + * @property [] quoteMint + * @property [_writable_] quoteGlobal + * @property [] quoteGlobalVault + * @property [] quoteMarketVault + * @property [] quoteTokenProgram + * @category Instructions + * @category BatchUpdate + * @category generated + */ +export type BatchUpdateInstructionAccounts = { + payer: web3.PublicKey; + market: web3.PublicKey; + systemProgram?: web3.PublicKey; + baseMint: web3.PublicKey; + baseGlobal: web3.PublicKey; + baseGlobalVault: web3.PublicKey; + baseMarketVault: web3.PublicKey; + baseTokenProgram: web3.PublicKey; + quoteMint: web3.PublicKey; + quoteGlobal: web3.PublicKey; + quoteGlobalVault: web3.PublicKey; + quoteMarketVault: web3.PublicKey; + quoteTokenProgram: web3.PublicKey; +}; + +export const batchUpdateInstructionDiscriminator = 6; + +/** + * Creates a _BatchUpdate_ instruction. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category BatchUpdate + * @category generated + */ +export function createBatchUpdateInstruction( + accounts: BatchUpdateInstructionAccounts, + args: BatchUpdateInstructionArgs, + programId = new web3.PublicKey("MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms"), +) { + const [data] = BatchUpdateStruct.serialize({ + instructionDiscriminator: batchUpdateInstructionDiscriminator, + ...args, + }); + const keys: web3.AccountMeta[] = [ + { + pubkey: accounts.payer, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.market, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.systemProgram ?? web3.SystemProgram.programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.baseMint, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.baseGlobal, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.baseGlobalVault, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.baseMarketVault, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.baseTokenProgram, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.quoteMint, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.quoteGlobal, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.quoteGlobalVault, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.quoteMarketVault, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.quoteTokenProgram, + isWritable: false, + isSigner: false, + }, + ]; + + const ix = new web3.TransactionInstruction({ + programId, + keys, + data, + }); + return ix; +} diff --git a/client/ts/src/manifest/instructions/ClaimSeat.ts b/client/ts/src/manifest/instructions/ClaimSeat.ts new file mode 100644 index 000000000..84eab5ae5 --- /dev/null +++ b/client/ts/src/manifest/instructions/ClaimSeat.ts @@ -0,0 +1,75 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from "@metaplex-foundation/beet"; +import * as web3 from "@solana/web3.js"; + +/** + * @category Instructions + * @category ClaimSeat + * @category generated + */ +export const ClaimSeatStruct = new beet.BeetArgsStruct<{ + instructionDiscriminator: number; +}>([["instructionDiscriminator", beet.u8]], "ClaimSeatInstructionArgs"); +/** + * Accounts required by the _ClaimSeat_ instruction + * + * @property [_writable_, **signer**] payer + * @property [_writable_] market + * @category Instructions + * @category ClaimSeat + * @category generated + */ +export type ClaimSeatInstructionAccounts = { + payer: web3.PublicKey; + market: web3.PublicKey; + systemProgram?: web3.PublicKey; +}; + +export const claimSeatInstructionDiscriminator = 1; + +/** + * Creates a _ClaimSeat_ instruction. + * + * @param accounts that will be accessed while the instruction is processed + * @category Instructions + * @category ClaimSeat + * @category generated + */ +export function createClaimSeatInstruction( + accounts: ClaimSeatInstructionAccounts, + programId = new web3.PublicKey("MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms"), +) { + const [data] = ClaimSeatStruct.serialize({ + instructionDiscriminator: claimSeatInstructionDiscriminator, + }); + const keys: web3.AccountMeta[] = [ + { + pubkey: accounts.payer, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.market, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.systemProgram ?? web3.SystemProgram.programId, + isWritable: false, + isSigner: false, + }, + ]; + + const ix = new web3.TransactionInstruction({ + programId, + keys, + data, + }); + return ix; +} diff --git a/client/ts/src/manifest/instructions/CreateMarket.ts b/client/ts/src/manifest/instructions/CreateMarket.ts new file mode 100644 index 000000000..7fbbb12c9 --- /dev/null +++ b/client/ts/src/manifest/instructions/CreateMarket.ts @@ -0,0 +1,117 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as splToken from "@solana/spl-token"; +import * as beet from "@metaplex-foundation/beet"; +import * as web3 from "@solana/web3.js"; + +/** + * @category Instructions + * @category CreateMarket + * @category generated + */ +export const CreateMarketStruct = new beet.BeetArgsStruct<{ + instructionDiscriminator: number; +}>([["instructionDiscriminator", beet.u8]], "CreateMarketInstructionArgs"); +/** + * Accounts required by the _CreateMarket_ instruction + * + * @property [_writable_, **signer**] payer + * @property [_writable_] market + * @property [] baseMint + * @property [] quoteMint + * @property [_writable_] baseVault + * @property [_writable_] quoteVault + * @property [] tokenProgram22 + * @category Instructions + * @category CreateMarket + * @category generated + */ +export type CreateMarketInstructionAccounts = { + payer: web3.PublicKey; + market: web3.PublicKey; + systemProgram?: web3.PublicKey; + baseMint: web3.PublicKey; + quoteMint: web3.PublicKey; + baseVault: web3.PublicKey; + quoteVault: web3.PublicKey; + tokenProgram?: web3.PublicKey; + tokenProgram22: web3.PublicKey; +}; + +export const createMarketInstructionDiscriminator = 0; + +/** + * Creates a _CreateMarket_ instruction. + * + * @param accounts that will be accessed while the instruction is processed + * @category Instructions + * @category CreateMarket + * @category generated + */ +export function createCreateMarketInstruction( + accounts: CreateMarketInstructionAccounts, + programId = new web3.PublicKey("MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms"), +) { + const [data] = CreateMarketStruct.serialize({ + instructionDiscriminator: createMarketInstructionDiscriminator, + }); + const keys: web3.AccountMeta[] = [ + { + pubkey: accounts.payer, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.market, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.systemProgram ?? web3.SystemProgram.programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.baseMint, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.quoteMint, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.baseVault, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.quoteVault, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.tokenProgram ?? splToken.TOKEN_PROGRAM_ID, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.tokenProgram22, + isWritable: false, + isSigner: false, + }, + ]; + + const ix = new web3.TransactionInstruction({ + programId, + keys, + data, + }); + return ix; +} diff --git a/client/ts/src/manifest/instructions/Deposit.ts b/client/ts/src/manifest/instructions/Deposit.ts new file mode 100644 index 000000000..a86581ad1 --- /dev/null +++ b/client/ts/src/manifest/instructions/Deposit.ts @@ -0,0 +1,118 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as splToken from "@solana/spl-token"; +import * as beet from "@metaplex-foundation/beet"; +import * as web3 from "@solana/web3.js"; +import { DepositParams, depositParamsBeet } from "../types/DepositParams"; + +/** + * @category Instructions + * @category Deposit + * @category generated + */ +export type DepositInstructionArgs = { + params: DepositParams; +}; +/** + * @category Instructions + * @category Deposit + * @category generated + */ +export const DepositStruct = new beet.BeetArgsStruct< + DepositInstructionArgs & { + instructionDiscriminator: number; + } +>( + [ + ["instructionDiscriminator", beet.u8], + ["params", depositParamsBeet], + ], + "DepositInstructionArgs", +); +/** + * Accounts required by the _Deposit_ instruction + * + * @property [_writable_, **signer**] payer + * @property [_writable_] market + * @property [_writable_] traderToken + * @property [_writable_] vault + * @property [] mint + * @category Instructions + * @category Deposit + * @category generated + */ +export type DepositInstructionAccounts = { + payer: web3.PublicKey; + market: web3.PublicKey; + traderToken: web3.PublicKey; + vault: web3.PublicKey; + tokenProgram?: web3.PublicKey; + mint: web3.PublicKey; +}; + +export const depositInstructionDiscriminator = 2; + +/** + * Creates a _Deposit_ instruction. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category Deposit + * @category generated + */ +export function createDepositInstruction( + accounts: DepositInstructionAccounts, + args: DepositInstructionArgs, + programId = new web3.PublicKey("MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms"), +) { + const [data] = DepositStruct.serialize({ + instructionDiscriminator: depositInstructionDiscriminator, + ...args, + }); + const keys: web3.AccountMeta[] = [ + { + pubkey: accounts.payer, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.market, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.traderToken, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.vault, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.tokenProgram ?? splToken.TOKEN_PROGRAM_ID, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.mint, + isWritable: false, + isSigner: false, + }, + ]; + + const ix = new web3.TransactionInstruction({ + programId, + keys, + data, + }); + return ix; +} diff --git a/client/ts/src/manifest/instructions/Expand.ts b/client/ts/src/manifest/instructions/Expand.ts new file mode 100644 index 000000000..646402a4c --- /dev/null +++ b/client/ts/src/manifest/instructions/Expand.ts @@ -0,0 +1,75 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from "@metaplex-foundation/beet"; +import * as web3 from "@solana/web3.js"; + +/** + * @category Instructions + * @category Expand + * @category generated + */ +export const ExpandStruct = new beet.BeetArgsStruct<{ + instructionDiscriminator: number; +}>([["instructionDiscriminator", beet.u8]], "ExpandInstructionArgs"); +/** + * Accounts required by the _Expand_ instruction + * + * @property [_writable_, **signer**] payer + * @property [_writable_] market + * @category Instructions + * @category Expand + * @category generated + */ +export type ExpandInstructionAccounts = { + payer: web3.PublicKey; + market: web3.PublicKey; + systemProgram?: web3.PublicKey; +}; + +export const expandInstructionDiscriminator = 5; + +/** + * Creates a _Expand_ instruction. + * + * @param accounts that will be accessed while the instruction is processed + * @category Instructions + * @category Expand + * @category generated + */ +export function createExpandInstruction( + accounts: ExpandInstructionAccounts, + programId = new web3.PublicKey("MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms"), +) { + const [data] = ExpandStruct.serialize({ + instructionDiscriminator: expandInstructionDiscriminator, + }); + const keys: web3.AccountMeta[] = [ + { + pubkey: accounts.payer, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.market, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.systemProgram ?? web3.SystemProgram.programId, + isWritable: false, + isSigner: false, + }, + ]; + + const ix = new web3.TransactionInstruction({ + programId, + keys, + data, + }); + return ix; +} diff --git a/client/ts/src/manifest/instructions/GlobalAddTrader.ts b/client/ts/src/manifest/instructions/GlobalAddTrader.ts new file mode 100644 index 000000000..69d946d33 --- /dev/null +++ b/client/ts/src/manifest/instructions/GlobalAddTrader.ts @@ -0,0 +1,75 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from "@metaplex-foundation/beet"; +import * as web3 from "@solana/web3.js"; + +/** + * @category Instructions + * @category GlobalAddTrader + * @category generated + */ +export const GlobalAddTraderStruct = new beet.BeetArgsStruct<{ + instructionDiscriminator: number; +}>([["instructionDiscriminator", beet.u8]], "GlobalAddTraderInstructionArgs"); +/** + * Accounts required by the _GlobalAddTrader_ instruction + * + * @property [_writable_, **signer**] payer + * @property [_writable_] global + * @category Instructions + * @category GlobalAddTrader + * @category generated + */ +export type GlobalAddTraderInstructionAccounts = { + payer: web3.PublicKey; + global: web3.PublicKey; + systemProgram?: web3.PublicKey; +}; + +export const globalAddTraderInstructionDiscriminator = 8; + +/** + * Creates a _GlobalAddTrader_ instruction. + * + * @param accounts that will be accessed while the instruction is processed + * @category Instructions + * @category GlobalAddTrader + * @category generated + */ +export function createGlobalAddTraderInstruction( + accounts: GlobalAddTraderInstructionAccounts, + programId = new web3.PublicKey("MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms"), +) { + const [data] = GlobalAddTraderStruct.serialize({ + instructionDiscriminator: globalAddTraderInstructionDiscriminator, + }); + const keys: web3.AccountMeta[] = [ + { + pubkey: accounts.payer, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.global, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.systemProgram ?? web3.SystemProgram.programId, + isWritable: false, + isSigner: false, + }, + ]; + + const ix = new web3.TransactionInstruction({ + programId, + keys, + data, + }); + return ix; +} diff --git a/client/ts/src/manifest/instructions/GlobalClaimSeat.ts b/client/ts/src/manifest/instructions/GlobalClaimSeat.ts new file mode 100644 index 000000000..4aaa29dd7 --- /dev/null +++ b/client/ts/src/manifest/instructions/GlobalClaimSeat.ts @@ -0,0 +1,82 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from "@metaplex-foundation/beet"; +import * as web3 from "@solana/web3.js"; + +/** + * @category Instructions + * @category GlobalClaimSeat + * @category generated + */ +export const GlobalClaimSeatStruct = new beet.BeetArgsStruct<{ + instructionDiscriminator: number; +}>([["instructionDiscriminator", beet.u8]], "GlobalClaimSeatInstructionArgs"); +/** + * Accounts required by the _GlobalClaimSeat_ instruction + * + * @property [_writable_, **signer**] payer + * @property [_writable_] global + * @property [_writable_] market + * @category Instructions + * @category GlobalClaimSeat + * @category generated + */ +export type GlobalClaimSeatInstructionAccounts = { + payer: web3.PublicKey; + global: web3.PublicKey; + systemProgram?: web3.PublicKey; + market: web3.PublicKey; +}; + +export const globalClaimSeatInstructionDiscriminator = 9; + +/** + * Creates a _GlobalClaimSeat_ instruction. + * + * @param accounts that will be accessed while the instruction is processed + * @category Instructions + * @category GlobalClaimSeat + * @category generated + */ +export function createGlobalClaimSeatInstruction( + accounts: GlobalClaimSeatInstructionAccounts, + programId = new web3.PublicKey("MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms"), +) { + const [data] = GlobalClaimSeatStruct.serialize({ + instructionDiscriminator: globalClaimSeatInstructionDiscriminator, + }); + const keys: web3.AccountMeta[] = [ + { + pubkey: accounts.payer, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.global, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.systemProgram ?? web3.SystemProgram.programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.market, + isWritable: true, + isSigner: false, + }, + ]; + + const ix = new web3.TransactionInstruction({ + programId, + keys, + data, + }); + return ix; +} diff --git a/client/ts/src/manifest/instructions/GlobalCreate.ts b/client/ts/src/manifest/instructions/GlobalCreate.ts new file mode 100644 index 000000000..5c9efb927 --- /dev/null +++ b/client/ts/src/manifest/instructions/GlobalCreate.ts @@ -0,0 +1,96 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as splToken from "@solana/spl-token"; +import * as beet from "@metaplex-foundation/beet"; +import * as web3 from "@solana/web3.js"; + +/** + * @category Instructions + * @category GlobalCreate + * @category generated + */ +export const GlobalCreateStruct = new beet.BeetArgsStruct<{ + instructionDiscriminator: number; +}>([["instructionDiscriminator", beet.u8]], "GlobalCreateInstructionArgs"); +/** + * Accounts required by the _GlobalCreate_ instruction + * + * @property [_writable_, **signer**] payer + * @property [_writable_] global + * @property [] mint + * @property [_writable_] globalVault + * @category Instructions + * @category GlobalCreate + * @category generated + */ +export type GlobalCreateInstructionAccounts = { + payer: web3.PublicKey; + global: web3.PublicKey; + systemProgram?: web3.PublicKey; + mint: web3.PublicKey; + globalVault: web3.PublicKey; + tokenProgram?: web3.PublicKey; +}; + +export const globalCreateInstructionDiscriminator = 7; + +/** + * Creates a _GlobalCreate_ instruction. + * + * @param accounts that will be accessed while the instruction is processed + * @category Instructions + * @category GlobalCreate + * @category generated + */ +export function createGlobalCreateInstruction( + accounts: GlobalCreateInstructionAccounts, + programId = new web3.PublicKey("MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms"), +) { + const [data] = GlobalCreateStruct.serialize({ + instructionDiscriminator: globalCreateInstructionDiscriminator, + }); + const keys: web3.AccountMeta[] = [ + { + pubkey: accounts.payer, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.global, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.systemProgram ?? web3.SystemProgram.programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.mint, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.globalVault, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.tokenProgram ?? splToken.TOKEN_PROGRAM_ID, + isWritable: false, + isSigner: false, + }, + ]; + + const ix = new web3.TransactionInstruction({ + programId, + keys, + data, + }); + return ix; +} diff --git a/client/ts/src/manifest/instructions/GlobalDeposit.ts b/client/ts/src/manifest/instructions/GlobalDeposit.ts new file mode 100644 index 000000000..d240100b0 --- /dev/null +++ b/client/ts/src/manifest/instructions/GlobalDeposit.ts @@ -0,0 +1,121 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as splToken from "@solana/spl-token"; +import * as beet from "@metaplex-foundation/beet"; +import * as web3 from "@solana/web3.js"; +import { + GlobalDepositParams, + globalDepositParamsBeet, +} from "../types/GlobalDepositParams"; + +/** + * @category Instructions + * @category GlobalDeposit + * @category generated + */ +export type GlobalDepositInstructionArgs = { + params: GlobalDepositParams; +}; +/** + * @category Instructions + * @category GlobalDeposit + * @category generated + */ +export const GlobalDepositStruct = new beet.BeetArgsStruct< + GlobalDepositInstructionArgs & { + instructionDiscriminator: number; + } +>( + [ + ["instructionDiscriminator", beet.u8], + ["params", globalDepositParamsBeet], + ], + "GlobalDepositInstructionArgs", +); +/** + * Accounts required by the _GlobalDeposit_ instruction + * + * @property [_writable_, **signer**] payer + * @property [_writable_] global + * @property [] mint + * @property [] globalVault + * @property [] traderToken + * @category Instructions + * @category GlobalDeposit + * @category generated + */ +export type GlobalDepositInstructionAccounts = { + payer: web3.PublicKey; + global: web3.PublicKey; + mint: web3.PublicKey; + globalVault: web3.PublicKey; + traderToken: web3.PublicKey; + tokenProgram?: web3.PublicKey; +}; + +export const globalDepositInstructionDiscriminator = 10; + +/** + * Creates a _GlobalDeposit_ instruction. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category GlobalDeposit + * @category generated + */ +export function createGlobalDepositInstruction( + accounts: GlobalDepositInstructionAccounts, + args: GlobalDepositInstructionArgs, + programId = new web3.PublicKey("MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms"), +) { + const [data] = GlobalDepositStruct.serialize({ + instructionDiscriminator: globalDepositInstructionDiscriminator, + ...args, + }); + const keys: web3.AccountMeta[] = [ + { + pubkey: accounts.payer, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.global, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.mint, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.globalVault, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.traderToken, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.tokenProgram ?? splToken.TOKEN_PROGRAM_ID, + isWritable: false, + isSigner: false, + }, + ]; + + const ix = new web3.TransactionInstruction({ + programId, + keys, + data, + }); + return ix; +} diff --git a/client/ts/src/manifest/instructions/Swap.ts b/client/ts/src/manifest/instructions/Swap.ts new file mode 100644 index 000000000..94c3e7173 --- /dev/null +++ b/client/ts/src/manifest/instructions/Swap.ts @@ -0,0 +1,175 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from "@metaplex-foundation/beet"; +import * as web3 from "@solana/web3.js"; +import { SwapParams, swapParamsBeet } from "../types/SwapParams"; + +/** + * @category Instructions + * @category Swap + * @category generated + */ +export type SwapInstructionArgs = { + params: SwapParams; +}; +/** + * @category Instructions + * @category Swap + * @category generated + */ +export const SwapStruct = new beet.BeetArgsStruct< + SwapInstructionArgs & { + instructionDiscriminator: number; + } +>( + [ + ["instructionDiscriminator", beet.u8], + ["params", swapParamsBeet], + ], + "SwapInstructionArgs", +); +/** + * Accounts required by the _Swap_ instruction + * + * @property [_writable_, **signer**] payer + * @property [_writable_] market + * @property [_writable_] traderBase + * @property [_writable_] traderQuote + * @property [_writable_] baseVault + * @property [_writable_] quoteVault + * @property [] tokenProgramBase + * @property [] baseMint + * @property [] tokenProgramQuote + * @property [] quoteMint + * @property [_writable_] global (optional) + * @property [_writable_] globalVault (optional) + * @category Instructions + * @category Swap + * @category generated + */ +export type SwapInstructionAccounts = { + payer: web3.PublicKey; + market: web3.PublicKey; + traderBase: web3.PublicKey; + traderQuote: web3.PublicKey; + baseVault: web3.PublicKey; + quoteVault: web3.PublicKey; + tokenProgramBase: web3.PublicKey; + baseMint: web3.PublicKey; + tokenProgramQuote: web3.PublicKey; + quoteMint: web3.PublicKey; + global?: web3.PublicKey; + globalVault?: web3.PublicKey; +}; + +export const swapInstructionDiscriminator = 4; + +/** + * Creates a _Swap_ instruction. + * + * Optional accounts that are not provided will be omitted from the accounts + * array passed with the instruction. + * An optional account that is set cannot follow an optional account that is unset. + * Otherwise an Error is raised. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category Swap + * @category generated + */ +export function createSwapInstruction( + accounts: SwapInstructionAccounts, + args: SwapInstructionArgs, + programId = new web3.PublicKey("MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms"), +) { + const [data] = SwapStruct.serialize({ + instructionDiscriminator: swapInstructionDiscriminator, + ...args, + }); + const keys: web3.AccountMeta[] = [ + { + pubkey: accounts.payer, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.market, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.traderBase, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.traderQuote, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.baseVault, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.quoteVault, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.tokenProgramBase, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.baseMint, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.tokenProgramQuote, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.quoteMint, + isWritable: false, + isSigner: false, + }, + ]; + + if (accounts.global != null) { + keys.push({ + pubkey: accounts.global, + isWritable: true, + isSigner: false, + }); + } + if (accounts.globalVault != null) { + if (accounts.global == null) { + throw new Error( + "When providing 'globalVault' then 'accounts.global' need(s) to be provided as well.", + ); + } + keys.push({ + pubkey: accounts.globalVault, + isWritable: true, + isSigner: false, + }); + } + + const ix = new web3.TransactionInstruction({ + programId, + keys, + data, + }); + return ix; +} diff --git a/client/ts/src/manifest/instructions/Withdraw.ts b/client/ts/src/manifest/instructions/Withdraw.ts new file mode 100644 index 000000000..3289dc136 --- /dev/null +++ b/client/ts/src/manifest/instructions/Withdraw.ts @@ -0,0 +1,118 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as splToken from "@solana/spl-token"; +import * as beet from "@metaplex-foundation/beet"; +import * as web3 from "@solana/web3.js"; +import { WithdrawParams, withdrawParamsBeet } from "../types/WithdrawParams"; + +/** + * @category Instructions + * @category Withdraw + * @category generated + */ +export type WithdrawInstructionArgs = { + params: WithdrawParams; +}; +/** + * @category Instructions + * @category Withdraw + * @category generated + */ +export const WithdrawStruct = new beet.BeetArgsStruct< + WithdrawInstructionArgs & { + instructionDiscriminator: number; + } +>( + [ + ["instructionDiscriminator", beet.u8], + ["params", withdrawParamsBeet], + ], + "WithdrawInstructionArgs", +); +/** + * Accounts required by the _Withdraw_ instruction + * + * @property [_writable_, **signer**] payer + * @property [_writable_] market + * @property [_writable_] traderToken + * @property [_writable_] vault + * @property [] mint + * @category Instructions + * @category Withdraw + * @category generated + */ +export type WithdrawInstructionAccounts = { + payer: web3.PublicKey; + market: web3.PublicKey; + traderToken: web3.PublicKey; + vault: web3.PublicKey; + tokenProgram?: web3.PublicKey; + mint: web3.PublicKey; +}; + +export const withdrawInstructionDiscriminator = 3; + +/** + * Creates a _Withdraw_ instruction. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category Withdraw + * @category generated + */ +export function createWithdrawInstruction( + accounts: WithdrawInstructionAccounts, + args: WithdrawInstructionArgs, + programId = new web3.PublicKey("MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms"), +) { + const [data] = WithdrawStruct.serialize({ + instructionDiscriminator: withdrawInstructionDiscriminator, + ...args, + }); + const keys: web3.AccountMeta[] = [ + { + pubkey: accounts.payer, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.market, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.traderToken, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.vault, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.tokenProgram ?? splToken.TOKEN_PROGRAM_ID, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.mint, + isWritable: false, + isSigner: false, + }, + ]; + + const ix = new web3.TransactionInstruction({ + programId, + keys, + data, + }); + return ix; +} diff --git a/client/ts/src/manifest/instructions/index.ts b/client/ts/src/manifest/instructions/index.ts new file mode 100644 index 000000000..4820ab400 --- /dev/null +++ b/client/ts/src/manifest/instructions/index.ts @@ -0,0 +1,11 @@ +export * from "./BatchUpdate"; +export * from "./ClaimSeat"; +export * from "./CreateMarket"; +export * from "./Deposit"; +export * from "./Expand"; +export * from "./GlobalAddTrader"; +export * from "./GlobalClaimSeat"; +export * from "./GlobalCreate"; +export * from "./GlobalDeposit"; +export * from "./Swap"; +export * from "./Withdraw"; diff --git a/client/ts/src/manifest/types/BatchUpdateParams.ts b/client/ts/src/manifest/types/BatchUpdateParams.ts new file mode 100644 index 000000000..4be039806 --- /dev/null +++ b/client/ts/src/manifest/types/BatchUpdateParams.ts @@ -0,0 +1,29 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from "@metaplex-foundation/beet"; +import { CancelOrderParams, cancelOrderParamsBeet } from "./CancelOrderParams"; +import { PlaceOrderParams, placeOrderParamsBeet } from "./PlaceOrderParams"; +export type BatchUpdateParams = { + traderIndexHint: beet.COption; + cancels: CancelOrderParams[]; + orders: PlaceOrderParams[]; +}; + +/** + * @category userTypes + * @category generated + */ +export const batchUpdateParamsBeet = + new beet.FixableBeetArgsStruct( + [ + ["traderIndexHint", beet.coption(beet.u32)], + ["cancels", beet.array(cancelOrderParamsBeet)], + ["orders", beet.array(placeOrderParamsBeet)], + ], + "BatchUpdateParams", + ); diff --git a/client/ts/src/manifest/types/BatchUpdateReturn.ts b/client/ts/src/manifest/types/BatchUpdateReturn.ts new file mode 100644 index 000000000..c519ec163 --- /dev/null +++ b/client/ts/src/manifest/types/BatchUpdateReturn.ts @@ -0,0 +1,21 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from "@metaplex-foundation/beet"; +export type BatchUpdateReturn = { + orders: [beet.bignum, number][]; +}; + +/** + * @category userTypes + * @category generated + */ +export const batchUpdateReturnBeet = + new beet.FixableBeetArgsStruct( + [["orders", beet.array(beet.fixedSizeTuple([beet.u64, beet.u32]))]], + "BatchUpdateReturn", + ); diff --git a/client/ts/src/manifest/types/CancelOrderParams.ts b/client/ts/src/manifest/types/CancelOrderParams.ts new file mode 100644 index 000000000..06bb2534d --- /dev/null +++ b/client/ts/src/manifest/types/CancelOrderParams.ts @@ -0,0 +1,25 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from "@metaplex-foundation/beet"; +export type CancelOrderParams = { + orderSequenceNumber: beet.bignum; + orderIndexHint: beet.COption; +}; + +/** + * @category userTypes + * @category generated + */ +export const cancelOrderParamsBeet = + new beet.FixableBeetArgsStruct( + [ + ["orderSequenceNumber", beet.u64], + ["orderIndexHint", beet.coption(beet.u32)], + ], + "CancelOrderParams", + ); diff --git a/client/ts/src/manifest/types/DepositParams.ts b/client/ts/src/manifest/types/DepositParams.ts new file mode 100644 index 000000000..4c5db097b --- /dev/null +++ b/client/ts/src/manifest/types/DepositParams.ts @@ -0,0 +1,20 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from "@metaplex-foundation/beet"; +export type DepositParams = { + amountAtoms: beet.bignum; +}; + +/** + * @category userTypes + * @category generated + */ +export const depositParamsBeet = new beet.BeetArgsStruct( + [["amountAtoms", beet.u64]], + "DepositParams", +); diff --git a/client/ts/src/manifest/types/GlobalDepositParams.ts b/client/ts/src/manifest/types/GlobalDepositParams.ts new file mode 100644 index 000000000..6f8cc6f37 --- /dev/null +++ b/client/ts/src/manifest/types/GlobalDepositParams.ts @@ -0,0 +1,21 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from "@metaplex-foundation/beet"; +export type GlobalDepositParams = { + amountAtoms: beet.bignum; +}; + +/** + * @category userTypes + * @category generated + */ +export const globalDepositParamsBeet = + new beet.BeetArgsStruct( + [["amountAtoms", beet.u64]], + "GlobalDepositParams", + ); diff --git a/client/ts/src/manifest/types/OrderType.ts b/client/ts/src/manifest/types/OrderType.ts new file mode 100644 index 000000000..70cb8fc41 --- /dev/null +++ b/client/ts/src/manifest/types/OrderType.ts @@ -0,0 +1,27 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from "@metaplex-foundation/beet"; +/** + * @category enums + * @category generated + */ +export enum OrderType { + Limit, + ImmediateOrCancel, + PostOnly, + PostOnlySlide, + Global, +} + +/** + * @category userTypes + * @category generated + */ +export const orderTypeBeet = beet.fixedScalarEnum( + OrderType, +) as beet.FixedSizeBeet; diff --git a/client/ts/src/manifest/types/PlaceOrderParams.ts b/client/ts/src/manifest/types/PlaceOrderParams.ts new file mode 100644 index 000000000..2ed21f2d8 --- /dev/null +++ b/client/ts/src/manifest/types/PlaceOrderParams.ts @@ -0,0 +1,33 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from "@metaplex-foundation/beet"; +import { OrderType, orderTypeBeet } from "./OrderType"; +export type PlaceOrderParams = { + baseAtoms: beet.bignum; + priceMantissa: number; + priceExponent: number; + isBid: boolean; + lastValidSlot: number; + orderType: OrderType; +}; + +/** + * @category userTypes + * @category generated + */ +export const placeOrderParamsBeet = new beet.BeetArgsStruct( + [ + ["baseAtoms", beet.u64], + ["priceMantissa", beet.u32], + ["priceExponent", beet.i8], + ["isBid", beet.bool], + ["lastValidSlot", beet.u32], + ["orderType", orderTypeBeet], + ], + "PlaceOrderParams", +); diff --git a/client/ts/src/manifest/types/SwapParams.ts b/client/ts/src/manifest/types/SwapParams.ts new file mode 100644 index 000000000..95521df1e --- /dev/null +++ b/client/ts/src/manifest/types/SwapParams.ts @@ -0,0 +1,28 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from "@metaplex-foundation/beet"; +export type SwapParams = { + inAtoms: beet.bignum; + outAtoms: beet.bignum; + isBaseIn: boolean; + isExactIn: boolean; +}; + +/** + * @category userTypes + * @category generated + */ +export const swapParamsBeet = new beet.BeetArgsStruct( + [ + ["inAtoms", beet.u64], + ["outAtoms", beet.u64], + ["isBaseIn", beet.bool], + ["isExactIn", beet.bool], + ], + "SwapParams", +); diff --git a/client/ts/src/manifest/types/WithdrawParams.ts b/client/ts/src/manifest/types/WithdrawParams.ts new file mode 100644 index 000000000..669cb0a66 --- /dev/null +++ b/client/ts/src/manifest/types/WithdrawParams.ts @@ -0,0 +1,20 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from "@metaplex-foundation/beet"; +export type WithdrawParams = { + amountAtoms: beet.bignum; +}; + +/** + * @category userTypes + * @category generated + */ +export const withdrawParamsBeet = new beet.BeetArgsStruct( + [["amountAtoms", beet.u64]], + "WithdrawParams", +); diff --git a/client/ts/src/manifest/types/index.ts b/client/ts/src/manifest/types/index.ts new file mode 100644 index 000000000..ae89e3653 --- /dev/null +++ b/client/ts/src/manifest/types/index.ts @@ -0,0 +1,9 @@ +export * from "./BatchUpdateParams"; +export * from "./BatchUpdateReturn"; +export * from "./CancelOrderParams"; +export * from "./DepositParams"; +export * from "./GlobalDepositParams"; +export * from "./OrderType"; +export * from "./PlaceOrderParams"; +export * from "./SwapParams"; +export * from "./WithdrawParams"; diff --git a/client/ts/src/market.ts b/client/ts/src/market.ts new file mode 100644 index 000000000..5b299bac7 --- /dev/null +++ b/client/ts/src/market.ts @@ -0,0 +1,432 @@ +import { PublicKey, Connection } from '@solana/web3.js'; +import { bignum } from '@metaplex-foundation/beet'; +import { claimedSeatBeet, publicKeyBeet, restingOrderBeet } from './utils/beet'; +import { publicKey as beetPublicKey } from '@metaplex-foundation/beet-solana'; +import { deserializeRedBlackTree } from './utils/redBlackTree'; +import { toNum } from './utils/numbers'; +import { FIXED_MANIFEST_HEADER_SIZE, NIL } from './constants'; + +/** + * Internal use only. Needed because shank doesnt handle f64 and because the + * client doesnt need to know about padding. + */ +export type RestingOrderInternal = { + traderIndex: bignum; + numBaseAtoms: bignum; + lastValidSlot: bignum; + sequenceNumber: bignum; + // Deserializes to UInt8Array, but we then convert it to number. + price: Uint8Array; + padding: bignum[]; // 16 bytes +}; + +/** + * RestingOrder on the market. + */ +export type RestingOrder = { + /** Trader public key. */ + trader: PublicKey; + /** Number of base atoms remaining in the order. */ + numBaseAtoms: bignum; + /** Last slot before this order is invalid and will be removed. */ + lastValidSlot: bignum; + /** Exchange defined sequenceNumber for this order, guaranteed to be unique. */ + sequenceNumber: bignum; + /** Price as float in atoms of quote per atoms of base. */ + price: number; +}; + +/** + * ClaimedSeat on the market. + */ +export type ClaimedSeat = { + /** Public key of the trader. */ + publicKey: PublicKey; + /** Balance of base atoms that are withdrawable (excluding in open orders). */ + baseBalance: bignum; + /** Balance of quote atoms that are withdrawable (excluding in open orders). */ + quoteBalance: bignum; +}; + +/** + * MarketData is all information stored on a market account. + */ +export interface MarketData { + /** Version of the struct, included in case features are added later which use the padding. */ + version: number; + /** Number of decimals for the baseMint (i.e. baseMintDecimals = 6 -> 1 baseAtom = .000001 baseToken). */ + baseMintDecimals: number; + /** Number of decimals for the quoteMint (i.e. quoteMintDecimals = 6 -> 1 quoteAtom = .000001 quoteToken). */ + quoteMintDecimals: number; + /** Public key for the base mint. */ + baseMint: PublicKey; + /** Public key for the quote mint. */ + quoteMint: PublicKey; + /** Current next order sequence number. */ + orderSequenceNumber: bigint; + /** Number of bytes used in the dynamic portion of the market account. */ + numBytesAllocated: number; + /** Sorted array of resting orders for bids currently on the orderbook. */ + bids: RestingOrder[]; + /** Sorted array of resting orders for asks currently on the orderbook. */ + asks: RestingOrder[]; + /** Array of all claimed seats. */ + claimedSeats: ClaimedSeat[]; +} + +/** + * Market object used for reading data from a manifest market. + */ +export class Market { + /** Public key for the market account. */ + address: PublicKey; + /** Deserialized data. */ + private data: MarketData; + + /** + * Constructs a Market object. + * + * @param address The `PublicKey` of the market account + * @param data Deserialized market data + */ + private constructor({ + address, + data, + }: { + address: PublicKey; + data: MarketData; + }) { + this.address = address; + this.data = data; + } + + /** + * Returns a `Market` for a given address, a data buffer + * + * @param marketAddress The `PublicKey` of the market account + * @param buffer The buffer holding the market account data + */ + static loadFromBuffer({ + address, + buffer, + }: { + address: PublicKey; + buffer: Buffer; + }): Market { + const marketData = Market.deserializeMarketBuffer(buffer); + return new Market({ address, data: marketData }); + } + + /** + * Returns a `Market` for a given address, a data buffer + * + * @param connection The Solana `Connection` object + * @param address The `PublicKey` of the market account + */ + static async loadFromAddress({ + connection, + address, + }: { + connection: Connection; + address: PublicKey; + }): Promise { + const buffer = await connection + .getAccountInfo(address, 'confirmed') + .then((accountInfo) => accountInfo?.data); + + if (buffer === undefined) { + throw new Error(`Failed to load ${address}`); + } + return Market.loadFromBuffer({ address, buffer }); + } + + /** + * Updates the data in a Market. + * + * @param connection The Solana `Connection` object + */ + public async reload(connection: Connection): Promise { + const buffer = await connection + .getAccountInfo(this.address, 'confirmed') + .then((accountInfo) => accountInfo?.data); + if (buffer === undefined) { + throw new Error(`Failed to load ${this.address}`); + } + this.data = Market.deserializeMarketBuffer(buffer); + } + + /** + * Get the amount in atoms of balance that is deposited on the exchange, does + * not include tokens currently in open orders. + * + * @param trader PublicKey of the trader to check balance of + * @param isBase boolean for whether this is checking base or quote + * + * @returns number in atoms + */ + public getWithdrawableBalanceAtoms( + trader: PublicKey, + isBase: boolean, + ): number { + const filteredSeats = this.data.claimedSeats.filter((claimedSeat) => { + return claimedSeat.publicKey.toBase58() == trader.toBase58(); + }); + // No seat claimed. + if (filteredSeats.length == 0) { + return 0; + } + const seat: ClaimedSeat = filteredSeats[0]; + return toNum(isBase ? seat.baseBalance : seat.quoteBalance); + } + + /** + * Gets the base mint of the market + * + * @returns PublicKey + */ + public baseMint(): PublicKey { + return this.data.baseMint; + } + + /** + * Gets the quote mint of the market + * + * @returns PublicKey + */ + public quoteMint(): PublicKey { + return this.data.quoteMint; + } + + /** + * Gets the base decimals of the market + * + * @returns number + */ + public baseDecimals(): number { + return this.data.baseMintDecimals; + } + + /** + * Gets the base decimals of the market + * + * @returns number + */ + public quoteDecimals(): number { + return this.data.quoteMintDecimals; + } + + /** + * Check whether a given public key has a claimed seat on the market + * + * @param trader PublicKey of the trader + * + * @returns boolean + */ + public hasSeat(trader: PublicKey): boolean { + const filteredSeats = this.data.claimedSeats.filter((claimedSeat) => { + return claimedSeat.publicKey.toBase58() == trader.toBase58(); + }); + return filteredSeats.length > 0; + } + + /** + * Get all open bids on the market. + * + * @returns RestingOrder[] + */ + public bids(): RestingOrder[] { + return this.data.bids; + } + + /** + * Get all open asks on the market. + * + * @returns RestingOrder[] + */ + public asks(): RestingOrder[] { + return this.data.asks; + } + + /** + * Get all open orders on the market. + * + * @returns RestingOrder[] + */ + public openOrders(): RestingOrder[] { + return [...this.data.bids, ...this.data.asks]; + } + + /** + * Print all information loaded about the market in a human readable format. + */ + public prettyPrint() { + console.log(''); + console.log(`Market: ${this.address}`); + console.log(`========================`); + console.log(`Version: ${this.data.version}`); + console.log(`BaseMint: ${this.data.baseMint.toBase58()}`); + console.log(`QuoteMint: ${this.data.quoteMint.toBase58()}`); + console.log(`OrderSequenceNumber: ${this.data.orderSequenceNumber}`); + console.log(`NumBytesAllocated: ${this.data.numBytesAllocated}`); + console.log('Bids:'); + this.data.bids.forEach((bid) => { + console.log( + `trader: ${bid.trader} numBaseAtoms: ${bid.numBaseAtoms} price: ${bid.price} lastValidSlot: ${bid.lastValidSlot} sequenceNumber: ${bid.sequenceNumber}`, + ); + }); + console.log('Asks:'); + this.data.asks.forEach((ask) => { + console.log( + `trader: ${ask.trader} numBaseAtoms: ${ask.numBaseAtoms} price: ${ask.price} lastValidSlot: ${ask.lastValidSlot} sequenceNumber: ${ask.sequenceNumber}`, + ); + }); + console.log('ClaimedSeats:'); + this.data.claimedSeats.forEach((claimedSeat) => { + console.log( + `publicKey: ${claimedSeat.publicKey.toBase58()} baseBalance: ${claimedSeat.baseBalance} quoteBalance: ${claimedSeat.quoteBalance}`, + ); + }); + console.log(`========================`); + } + + /** + * Deserializes market data from a given buffer and returns a `Market` object + * + * This includes both the fixed and dynamic parts of the market. + * https://github.com/CKS-Systems/manifest/blob/main/programs/manifest/src/state/market.rs#L56 + * + * @param data The data buffer to deserialize + */ + static deserializeMarketBuffer(data: Buffer): MarketData { + let offset = 0; + // Deserialize the market header + const _discriminant = data.readBigUInt64LE(0); + offset += 8; + + const version = data.readUInt8(offset); + offset += 1; + const baseMintDecimals = data.readUInt8(offset); + offset += 1; + const quoteMintDecimals = data.readUInt8(offset); + offset += 1; + const _baseVaultBump = data.readUInt8(offset); + offset += 1; + const _quoteVaultBump = data.readUInt8(offset); + offset += 1; + // 3 bytes of unused padding. + offset += 3; + + const baseMint = beetPublicKey.read(data, offset); + offset += beetPublicKey.byteSize; + const quoteMint = beetPublicKey.read(data, offset); + offset += beetPublicKey.byteSize; + const _baseVault = beetPublicKey.read(data, offset); + offset += beetPublicKey.byteSize; + const _quoteVault = beetPublicKey.read(data, offset); + offset += beetPublicKey.byteSize; + + const orderSequenceNumber = data.readBigUInt64LE(offset); + offset += 8; + + const numBytesAllocated = data.readUInt32LE(offset); + offset += 4; + + const bidsRootIndex = data.readUInt32LE(offset); + offset += 4; + const _bidsBestIndex = data.readUInt32LE(offset); + offset += 4; + + const asksRootIndex = data.readUInt32LE(offset); + offset += 4; + const _askBestIndex = data.readUInt32LE(offset); + offset += 4; + + const claimedSeatsRootIndex = data.readUInt32LE(offset); + offset += 4; + + const _freeListHeadIndex = data.readUInt32LE(offset); + offset += 4; + + // _padding2: [u32; 1], + // _padding3: [u64; 32], + // _padding4: [u64; 16], + + // Market fixed size len is 512. + // https://github.com/CKS-Systems/manifest/blob/main/programs/manifest/src/state/constants.rs + + // TODO: Fix this for the new pricing + const bids: RestingOrder[] = + bidsRootIndex != NIL + ? deserializeRedBlackTree( + data.subarray(FIXED_MANIFEST_HEADER_SIZE), + bidsRootIndex, + restingOrderBeet, + ).map((restingOrderInternal: RestingOrderInternal) => { + return { + ...restingOrderInternal, + trader: publicKeyBeet.deserialize( + data.subarray( + Number(restingOrderInternal.traderIndex) + + 16 + + FIXED_MANIFEST_HEADER_SIZE, + Number(restingOrderInternal.traderIndex) + + 48 + + FIXED_MANIFEST_HEADER_SIZE, + ), + )[0].publicKey, + price: Buffer.from( + restingOrderInternal.price as Uint8Array, + ).readDoubleLE(0), + }; + }) + : []; + + const asks: RestingOrder[] = + asksRootIndex != NIL + ? deserializeRedBlackTree( + data.subarray(FIXED_MANIFEST_HEADER_SIZE), + asksRootIndex, + restingOrderBeet, + ).map((restingOrderInternal: RestingOrderInternal) => { + return { + ...restingOrderInternal, + trader: publicKeyBeet.deserialize( + data.subarray( + Number(restingOrderInternal.traderIndex) + + 16 + + FIXED_MANIFEST_HEADER_SIZE, + Number(restingOrderInternal.traderIndex) + + 48 + + FIXED_MANIFEST_HEADER_SIZE, + ), + )[0].publicKey, + price: Buffer.from( + restingOrderInternal.price as Uint8Array, + ).readDoubleLE(0), + }; + }) + : []; + + const claimedSeats = + claimedSeatsRootIndex != NIL + ? deserializeRedBlackTree( + data.subarray(FIXED_MANIFEST_HEADER_SIZE), + claimedSeatsRootIndex, + claimedSeatBeet, + ) + : []; + + return { + version, + baseMintDecimals, + quoteMintDecimals, + baseMint, + quoteMint, + orderSequenceNumber, + numBytesAllocated, + bids, + asks, + claimedSeats, + }; + } +} diff --git a/client/ts/src/utils/beet.ts b/client/ts/src/utils/beet.ts new file mode 100644 index 000000000..abe3a4b5c --- /dev/null +++ b/client/ts/src/utils/beet.ts @@ -0,0 +1,111 @@ +import { PublicKey } from '@solana/web3.js'; + +import { ClaimedSeat, RestingOrderInternal } from '../market'; +import { + BeetArgsStruct, + fixedSizeUint8Array, + u32, + u64, + u8, + uniformFixedSizeArray, +} from '@metaplex-foundation/beet'; +import { publicKey as beetPublicKey } from '@metaplex-foundation/beet-solana'; +import { MarketInfoRaw, OpenOrderInternal } from '../wrapperObj'; +import { RedBlackTreeNodeHeader } from './redBlackTree'; + +type PubkeyWrapper = { + publicKey: PublicKey; +}; + +/** + * PublicKey deserializer. + */ +export const publicKeyBeet = new BeetArgsStruct( + [['publicKey', beetPublicKey]], + 'PubkeyWrapper', +); + +/** + * RestingOrder deserializer. + * + * https://github.com/CKS-Systems/manifest/blob/main/programs/manifest/src/state/resting_order.rs + */ +export const restingOrderBeet = new BeetArgsStruct( + [ + ['traderIndex', u32], + ['lastValidSlot', u32], + ['numBaseAtoms', u64], + ['sequenceNumber', u64], + ['price', fixedSizeUint8Array(8)], + ['padding', uniformFixedSizeArray(u64, 2)], + ], + 'restingOrder', +); + +/** + * ClaimedSeat deserializer. + * + * https://github.com/CKS-Systems/manifest/blob/main/programs/manifest/src/state/claimed_seat.rs + */ +export const claimedSeatBeet = new BeetArgsStruct( + [ + ['publicKey', beetPublicKey], + ['baseBalance', u64], + ['quoteBalance', u64], + ], + 'claimedSeat', +); + +/** + * RedBlackTreeHeader deserializer. + * + * https://github.com/CKS-Systems/manifest/blob/main/lib/src/red_black_tree.rs + */ +export const redBlackTreeHeaderBeet = + new BeetArgsStruct( + [ + ['left', u32], + ['right', u32], + ['parent', u32], + ['color', u32], + ], + 'redBlackTreeNodeHeader', + ); + +/** + * MarketInfo deserializer. + * + * https://github.com/CKS-Systems/manifest/blob/main/programs/wrapper/src/market_info.rs + */ +export const marketInfoBeet = new BeetArgsStruct( + [ + ['market', beetPublicKey], + ['openOrdersRootIndex', u32], + ['traderIndex', u32], + ['baseBalanceAtoms', u64], + ['quoteBalanceAtoms', u64], + ['lastUpdatedSlot', u32], + ['padding', u32], + ], + 'marketInfoRaw', +); + +/** + * OpenOrder (wrapper) deserializer. + * + * https://github.com/CKS-Systems/manifest/blob/main/programs/wrapper/src/open_order.rs + */ +export const openOrderBeet = new BeetArgsStruct( + [ + ['clientOrderId', u64], + ['orderSequenceNumber', u64], + ['price', fixedSizeUint8Array(8)], + ['numBaseAtoms', u64], + ['dataIndex', u32], + ['lastValidSlot', u32], + ['isBid', u8], + ['orderType', u8], + ['padding', uniformFixedSizeArray(u8, 26)], + ], + 'OpenOrder', +); diff --git a/client/ts/src/utils/market.ts b/client/ts/src/utils/market.ts new file mode 100644 index 000000000..cc19e5fa0 --- /dev/null +++ b/client/ts/src/utils/market.ts @@ -0,0 +1,11 @@ +import { PROGRAM_ID } from '../manifest/index'; + +import { PublicKey } from '@solana/web3.js'; + +export function getVaultAddress(market: PublicKey, mint: PublicKey) { + const [vaultAddress, _unusedBump] = PublicKey.findProgramAddressSync( + [Buffer.from('vault'), market.toBuffer(), mint.toBuffer()], + PROGRAM_ID, + ); + return vaultAddress; +} diff --git a/client/ts/src/utils/numbers.ts b/client/ts/src/utils/numbers.ts new file mode 100644 index 000000000..925a0c0e1 --- /dev/null +++ b/client/ts/src/utils/numbers.ts @@ -0,0 +1,16 @@ +import { bignum } from '@metaplex-foundation/beet'; + +/** + * Converts a beet.bignum to a number. + * + * @param n The number to convert + */ +export function toNum(n: bignum): number { + let target: number; + if (typeof n === 'number') { + target = n; + } else { + target = n.toNumber(); + } + return target; +} diff --git a/client/ts/src/utils/redBlackTree.ts b/client/ts/src/utils/redBlackTree.ts new file mode 100644 index 000000000..e08e75588 --- /dev/null +++ b/client/ts/src/utils/redBlackTree.ts @@ -0,0 +1,128 @@ +import { BeetArgsStruct, bignum } from '@metaplex-foundation/beet'; +import { redBlackTreeHeaderBeet } from './beet'; +import { toNum } from './numbers'; +import { NIL } from '../constants'; + +export type RedBlackTreeNodeHeader = { + left: bignum; + right: bignum; + parent: bignum; + color: bignum; +}; +const NUM_TREE_HEADER_BYTES = 16; + +/** + * Deserializes a RedBlackTree from a given buffer into a list + * @description This deserializes the RedBlackTree defined in https://github.com/CKS-Systems/manifest/blob/main/src/state/red_black_tree.rs + * + * @param data The data buffer to deserialize + * @param rootIndex Index in the buffer for the root + * @param valueDeserializer The deserializer for the tree value + */ +export function deserializeRedBlackTree( + data: Buffer, + rootIndex: number, + valueDeserializer: BeetArgsStruct, +): Value[] { + const result: Value[] = []; + const [rootHeaderValue] = redBlackTreeHeaderBeet.deserialize( + data.subarray(rootIndex, rootIndex + NUM_TREE_HEADER_BYTES), + ); + + // Find the min + let currentHeader = rootHeaderValue; + let currentIndex = rootIndex; + while (toNum(currentHeader.left) != NIL) { + currentIndex = toNum(currentHeader.left); + + const [currentHeaderTemp] = redBlackTreeHeaderBeet.deserialize( + data.subarray(currentIndex, currentIndex + NUM_TREE_HEADER_BYTES), + ); + currentHeader = currentHeaderTemp; + } + + // Keep going while there is a successor. + const [currentValue] = valueDeserializer.deserialize( + data.subarray( + currentIndex + NUM_TREE_HEADER_BYTES, + currentIndex + NUM_TREE_HEADER_BYTES + valueDeserializer.byteSize, + ), + ); + + result.push(currentValue); + while (getSuccessorIndex(data, currentIndex) != NIL) { + currentIndex = getSuccessorIndex(data, currentIndex); + const [currentValue] = valueDeserializer.deserialize( + data.subarray( + currentIndex + NUM_TREE_HEADER_BYTES, + currentIndex + NUM_TREE_HEADER_BYTES + valueDeserializer.byteSize, + ), + ); + result.push(currentValue); + } + + return result; +} + +function getSuccessorIndex(data: Buffer, index: number) { + if (index == NIL) { + return NIL; + } + let [currentHeader] = redBlackTreeHeaderBeet.deserialize( + data.subarray(index, index + NUM_TREE_HEADER_BYTES), + ); + let currentIndex = index; + + // Successor is below, go right then all the way to the left. + if (toNum(currentHeader.right) != NIL) { + currentIndex = toNum(currentHeader.right); + currentHeader = redBlackTreeHeaderBeet.deserialize( + data.subarray(currentIndex, currentIndex + NUM_TREE_HEADER_BYTES), + )[0]; + while (toNum(currentHeader.left) != NIL) { + currentIndex = toNum(currentHeader.left); + currentHeader = redBlackTreeHeaderBeet.deserialize( + data.subarray(currentIndex, currentIndex + NUM_TREE_HEADER_BYTES), + )[0]; + } + return currentIndex; + } + + if (currentHeader.parent == NIL) { + return NIL; + } + let [parentHeader] = redBlackTreeHeaderBeet.deserialize( + data.subarray( + toNum(currentHeader.parent), + toNum(currentHeader.parent) + NUM_TREE_HEADER_BYTES, + ), + ); + // Successor is above, keep going up while we are the right child + while (toNum(parentHeader.right) == currentIndex) { + currentIndex = toNum(currentHeader.parent); + if (currentIndex == NIL) { + return NIL; + } + currentHeader = redBlackTreeHeaderBeet.deserialize( + data.subarray(currentIndex, currentIndex + NUM_TREE_HEADER_BYTES), + )[0]; + if (currentHeader.parent == NIL) { + return NIL; + } + parentHeader = redBlackTreeHeaderBeet.deserialize( + data.subarray( + toNum(currentHeader.parent), + toNum(currentHeader.parent) + NUM_TREE_HEADER_BYTES, + ), + )[0]; + } + + // Go up once more. + currentHeader = redBlackTreeHeaderBeet.deserialize( + data.subarray(currentIndex, currentIndex + NUM_TREE_HEADER_BYTES), + )[0]; + if (currentHeader.parent == NIL) { + return NIL; + } + return toNum(currentHeader.parent); +} diff --git a/client/ts/src/utils/solana.ts b/client/ts/src/utils/solana.ts new file mode 100644 index 000000000..2797b3ec6 --- /dev/null +++ b/client/ts/src/utils/solana.ts @@ -0,0 +1,31 @@ +import { Connection, PublicKey } from '@solana/web3.js'; + +export type Cluster = 'mainnet-beta' | 'devnet' | 'localnet'; + +export async function getClusterFromConnection( + connection: Connection, +): Promise { + const hash = await connection.getGenesisHash(); + if (hash === '5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d') { + return 'mainnet-beta'; + } else if (hash === 'EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG') { + return 'devnet'; + } else { + return 'localnet'; + } +} + +export async function airdropSol(connection: Connection, recipient: PublicKey) { + console.log(`Requesting airdrop for ${recipient}`); + const signature = await connection.requestAirdrop(recipient, 2_000_000_000); + const { blockhash, lastValidBlockHeight } = + await connection.getLatestBlockhash(); + await connection.confirmTransaction( + { + blockhash, + lastValidBlockHeight, + signature, + }, + 'finalized', + ); +} diff --git a/client/ts/src/wrapper/index.ts b/client/ts/src/wrapper/index.ts new file mode 100644 index 000000000..ee12fa7b9 --- /dev/null +++ b/client/ts/src/wrapper/index.ts @@ -0,0 +1,19 @@ +import { PublicKey } from "@solana/web3.js"; +export * from "./instructions"; +export * from "./types"; + +/** + * Program address + * + * @category constants + * @category generated + */ +export const PROGRAM_ADDRESS = "wMNFSTkir3HgyZTsB7uqu3i7FA73grFCptPXgrZjksL"; + +/** + * Program public key + * + * @category constants + * @category generated + */ +export const PROGRAM_ID = new PublicKey(PROGRAM_ADDRESS); diff --git a/client/ts/src/wrapper/instructions/BatchUpdate.ts b/client/ts/src/wrapper/instructions/BatchUpdate.ts new file mode 100644 index 000000000..3cf19a5ce --- /dev/null +++ b/client/ts/src/wrapper/instructions/BatchUpdate.ts @@ -0,0 +1,120 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from "@metaplex-foundation/beet"; +import * as web3 from "@solana/web3.js"; +import { + WrapperBatchUpdateParams, + wrapperBatchUpdateParamsBeet, +} from "../types/WrapperBatchUpdateParams"; + +/** + * @category Instructions + * @category BatchUpdate + * @category generated + */ +export type BatchUpdateInstructionArgs = { + params: WrapperBatchUpdateParams; +}; +/** + * @category Instructions + * @category BatchUpdate + * @category generated + */ +export const BatchUpdateStruct = new beet.FixableBeetArgsStruct< + BatchUpdateInstructionArgs & { + instructionDiscriminator: number; + } +>( + [ + ["instructionDiscriminator", beet.u8], + ["params", wrapperBatchUpdateParamsBeet], + ], + "BatchUpdateInstructionArgs", +); +/** + * Accounts required by the _BatchUpdate_ instruction + * + * @property [] manifestProgram + * @property [_writable_, **signer**] owner + * @property [_writable_] market + * @property [_writable_, **signer**] payer + * @property [_writable_] wrapperState + * @category Instructions + * @category BatchUpdate + * @category generated + */ +export type BatchUpdateInstructionAccounts = { + manifestProgram: web3.PublicKey; + owner: web3.PublicKey; + market: web3.PublicKey; + systemProgram?: web3.PublicKey; + payer: web3.PublicKey; + wrapperState: web3.PublicKey; +}; + +export const batchUpdateInstructionDiscriminator = 4; + +/** + * Creates a _BatchUpdate_ instruction. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category BatchUpdate + * @category generated + */ +export function createBatchUpdateInstruction( + accounts: BatchUpdateInstructionAccounts, + args: BatchUpdateInstructionArgs, + programId = new web3.PublicKey("wMNFSTkir3HgyZTsB7uqu3i7FA73grFCptPXgrZjksL"), +) { + const [data] = BatchUpdateStruct.serialize({ + instructionDiscriminator: batchUpdateInstructionDiscriminator, + ...args, + }); + const keys: web3.AccountMeta[] = [ + { + pubkey: accounts.manifestProgram, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.owner, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.market, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.systemProgram ?? web3.SystemProgram.programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.payer, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.wrapperState, + isWritable: true, + isSigner: false, + }, + ]; + + const ix = new web3.TransactionInstruction({ + programId, + keys, + data, + }); + return ix; +} diff --git a/client/ts/src/wrapper/instructions/ClaimSeat.ts b/client/ts/src/wrapper/instructions/ClaimSeat.ts new file mode 100644 index 000000000..107c00556 --- /dev/null +++ b/client/ts/src/wrapper/instructions/ClaimSeat.ts @@ -0,0 +1,96 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from "@metaplex-foundation/beet"; +import * as web3 from "@solana/web3.js"; + +/** + * @category Instructions + * @category ClaimSeat + * @category generated + */ +export const ClaimSeatStruct = new beet.BeetArgsStruct<{ + instructionDiscriminator: number; +}>([["instructionDiscriminator", beet.u8]], "ClaimSeatInstructionArgs"); +/** + * Accounts required by the _ClaimSeat_ instruction + * + * @property [] manifestProgram + * @property [_writable_, **signer**] owner + * @property [_writable_] market + * @property [_writable_, **signer**] payer + * @property [_writable_] wrapperState + * @category Instructions + * @category ClaimSeat + * @category generated + */ +export type ClaimSeatInstructionAccounts = { + manifestProgram: web3.PublicKey; + owner: web3.PublicKey; + market: web3.PublicKey; + systemProgram?: web3.PublicKey; + payer: web3.PublicKey; + wrapperState: web3.PublicKey; +}; + +export const claimSeatInstructionDiscriminator = 1; + +/** + * Creates a _ClaimSeat_ instruction. + * + * @param accounts that will be accessed while the instruction is processed + * @category Instructions + * @category ClaimSeat + * @category generated + */ +export function createClaimSeatInstruction( + accounts: ClaimSeatInstructionAccounts, + programId = new web3.PublicKey("wMNFSTkir3HgyZTsB7uqu3i7FA73grFCptPXgrZjksL"), +) { + const [data] = ClaimSeatStruct.serialize({ + instructionDiscriminator: claimSeatInstructionDiscriminator, + }); + const keys: web3.AccountMeta[] = [ + { + pubkey: accounts.manifestProgram, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.owner, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.market, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.systemProgram ?? web3.SystemProgram.programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.payer, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.wrapperState, + isWritable: true, + isSigner: false, + }, + ]; + + const ix = new web3.TransactionInstruction({ + programId, + keys, + data, + }); + return ix; +} diff --git a/client/ts/src/wrapper/instructions/CreateWrapper.ts b/client/ts/src/wrapper/instructions/CreateWrapper.ts new file mode 100644 index 000000000..5f0787336 --- /dev/null +++ b/client/ts/src/wrapper/instructions/CreateWrapper.ts @@ -0,0 +1,82 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from "@metaplex-foundation/beet"; +import * as web3 from "@solana/web3.js"; + +/** + * @category Instructions + * @category CreateWrapper + * @category generated + */ +export const CreateWrapperStruct = new beet.BeetArgsStruct<{ + instructionDiscriminator: number; +}>([["instructionDiscriminator", beet.u8]], "CreateWrapperInstructionArgs"); +/** + * Accounts required by the _CreateWrapper_ instruction + * + * @property [_writable_, **signer**] owner + * @property [_writable_, **signer**] payer + * @property [_writable_] wrapperState + * @category Instructions + * @category CreateWrapper + * @category generated + */ +export type CreateWrapperInstructionAccounts = { + owner: web3.PublicKey; + systemProgram?: web3.PublicKey; + payer: web3.PublicKey; + wrapperState: web3.PublicKey; +}; + +export const createWrapperInstructionDiscriminator = 0; + +/** + * Creates a _CreateWrapper_ instruction. + * + * @param accounts that will be accessed while the instruction is processed + * @category Instructions + * @category CreateWrapper + * @category generated + */ +export function createCreateWrapperInstruction( + accounts: CreateWrapperInstructionAccounts, + programId = new web3.PublicKey("wMNFSTkir3HgyZTsB7uqu3i7FA73grFCptPXgrZjksL"), +) { + const [data] = CreateWrapperStruct.serialize({ + instructionDiscriminator: createWrapperInstructionDiscriminator, + }); + const keys: web3.AccountMeta[] = [ + { + pubkey: accounts.owner, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.systemProgram ?? web3.SystemProgram.programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.payer, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.wrapperState, + isWritable: true, + isSigner: false, + }, + ]; + + const ix = new web3.TransactionInstruction({ + programId, + keys, + data, + }); + return ix; +} diff --git a/client/ts/src/wrapper/instructions/Deposit.ts b/client/ts/src/wrapper/instructions/Deposit.ts new file mode 100644 index 000000000..2511344b5 --- /dev/null +++ b/client/ts/src/wrapper/instructions/Deposit.ts @@ -0,0 +1,139 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as splToken from "@solana/spl-token"; +import * as beet from "@metaplex-foundation/beet"; +import * as web3 from "@solana/web3.js"; +import { DepositParams, depositParamsBeet } from "../types/DepositParams"; + +/** + * @category Instructions + * @category Deposit + * @category generated + */ +export type DepositInstructionArgs = { + params: DepositParams; +}; +/** + * @category Instructions + * @category Deposit + * @category generated + */ +export const DepositStruct = new beet.BeetArgsStruct< + DepositInstructionArgs & { + instructionDiscriminator: number; + } +>( + [ + ["instructionDiscriminator", beet.u8], + ["params", depositParamsBeet], + ], + "DepositInstructionArgs", +); +/** + * Accounts required by the _Deposit_ instruction + * + * @property [] manifestProgram + * @property [_writable_, **signer**] owner + * @property [_writable_] market + * @property [_writable_] traderTokenAccount + * @property [_writable_] vault + * @property [_writable_, **signer**] payer + * @property [_writable_] wrapperState + * @property [] mint + * @category Instructions + * @category Deposit + * @category generated + */ +export type DepositInstructionAccounts = { + manifestProgram: web3.PublicKey; + owner: web3.PublicKey; + market: web3.PublicKey; + traderTokenAccount: web3.PublicKey; + vault: web3.PublicKey; + tokenProgram?: web3.PublicKey; + payer: web3.PublicKey; + wrapperState: web3.PublicKey; + mint: web3.PublicKey; +}; + +export const depositInstructionDiscriminator = 2; + +/** + * Creates a _Deposit_ instruction. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category Deposit + * @category generated + */ +export function createDepositInstruction( + accounts: DepositInstructionAccounts, + args: DepositInstructionArgs, + programId = new web3.PublicKey("wMNFSTkir3HgyZTsB7uqu3i7FA73grFCptPXgrZjksL"), +) { + const [data] = DepositStruct.serialize({ + instructionDiscriminator: depositInstructionDiscriminator, + ...args, + }); + const keys: web3.AccountMeta[] = [ + { + pubkey: accounts.manifestProgram, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.owner, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.market, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.traderTokenAccount, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.vault, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.tokenProgram ?? splToken.TOKEN_PROGRAM_ID, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.payer, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.wrapperState, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.mint, + isWritable: false, + isSigner: false, + }, + ]; + + const ix = new web3.TransactionInstruction({ + programId, + keys, + data, + }); + return ix; +} diff --git a/client/ts/src/wrapper/instructions/Withdraw.ts b/client/ts/src/wrapper/instructions/Withdraw.ts new file mode 100644 index 000000000..510ba9b20 --- /dev/null +++ b/client/ts/src/wrapper/instructions/Withdraw.ts @@ -0,0 +1,139 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as splToken from "@solana/spl-token"; +import * as beet from "@metaplex-foundation/beet"; +import * as web3 from "@solana/web3.js"; +import { WithdrawParams, withdrawParamsBeet } from "../types/WithdrawParams"; + +/** + * @category Instructions + * @category Withdraw + * @category generated + */ +export type WithdrawInstructionArgs = { + params: WithdrawParams; +}; +/** + * @category Instructions + * @category Withdraw + * @category generated + */ +export const WithdrawStruct = new beet.BeetArgsStruct< + WithdrawInstructionArgs & { + instructionDiscriminator: number; + } +>( + [ + ["instructionDiscriminator", beet.u8], + ["params", withdrawParamsBeet], + ], + "WithdrawInstructionArgs", +); +/** + * Accounts required by the _Withdraw_ instruction + * + * @property [] manifestProgram + * @property [_writable_, **signer**] owner + * @property [_writable_] market + * @property [_writable_] traderTokenAccount + * @property [_writable_] vault + * @property [_writable_, **signer**] payer + * @property [_writable_] wrapperState + * @property [] mint + * @category Instructions + * @category Withdraw + * @category generated + */ +export type WithdrawInstructionAccounts = { + manifestProgram: web3.PublicKey; + owner: web3.PublicKey; + market: web3.PublicKey; + traderTokenAccount: web3.PublicKey; + vault: web3.PublicKey; + tokenProgram?: web3.PublicKey; + payer: web3.PublicKey; + wrapperState: web3.PublicKey; + mint: web3.PublicKey; +}; + +export const withdrawInstructionDiscriminator = 3; + +/** + * Creates a _Withdraw_ instruction. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category Withdraw + * @category generated + */ +export function createWithdrawInstruction( + accounts: WithdrawInstructionAccounts, + args: WithdrawInstructionArgs, + programId = new web3.PublicKey("wMNFSTkir3HgyZTsB7uqu3i7FA73grFCptPXgrZjksL"), +) { + const [data] = WithdrawStruct.serialize({ + instructionDiscriminator: withdrawInstructionDiscriminator, + ...args, + }); + const keys: web3.AccountMeta[] = [ + { + pubkey: accounts.manifestProgram, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.owner, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.market, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.traderTokenAccount, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.vault, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.tokenProgram ?? splToken.TOKEN_PROGRAM_ID, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.payer, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.wrapperState, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.mint, + isWritable: false, + isSigner: false, + }, + ]; + + const ix = new web3.TransactionInstruction({ + programId, + keys, + data, + }); + return ix; +} diff --git a/client/ts/src/wrapper/instructions/index.ts b/client/ts/src/wrapper/instructions/index.ts new file mode 100644 index 000000000..dbe2be8d9 --- /dev/null +++ b/client/ts/src/wrapper/instructions/index.ts @@ -0,0 +1,5 @@ +export * from "./BatchUpdate"; +export * from "./ClaimSeat"; +export * from "./CreateWrapper"; +export * from "./Deposit"; +export * from "./Withdraw"; diff --git a/client/ts/src/wrapper/types/DepositParams.ts b/client/ts/src/wrapper/types/DepositParams.ts new file mode 100644 index 000000000..4c5db097b --- /dev/null +++ b/client/ts/src/wrapper/types/DepositParams.ts @@ -0,0 +1,20 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from "@metaplex-foundation/beet"; +export type DepositParams = { + amountAtoms: beet.bignum; +}; + +/** + * @category userTypes + * @category generated + */ +export const depositParamsBeet = new beet.BeetArgsStruct( + [["amountAtoms", beet.u64]], + "DepositParams", +); diff --git a/client/ts/src/wrapper/types/OrderType.ts b/client/ts/src/wrapper/types/OrderType.ts new file mode 100644 index 000000000..539c04d50 --- /dev/null +++ b/client/ts/src/wrapper/types/OrderType.ts @@ -0,0 +1,27 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from "@metaplex-foundation/beet"; +/** + * @category enums + * @category generated + */ +export enum OrderType { + Limit, + ImmediateOrCancel, + PostOnly, + PostOnlySlide, + FillOrKill, +} + +/** + * @category userTypes + * @category generated + */ +export const orderTypeBeet = beet.fixedScalarEnum( + OrderType, +) as beet.FixedSizeBeet; diff --git a/client/ts/src/wrapper/types/WithdrawParams.ts b/client/ts/src/wrapper/types/WithdrawParams.ts new file mode 100644 index 000000000..669cb0a66 --- /dev/null +++ b/client/ts/src/wrapper/types/WithdrawParams.ts @@ -0,0 +1,20 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from "@metaplex-foundation/beet"; +export type WithdrawParams = { + amountAtoms: beet.bignum; +}; + +/** + * @category userTypes + * @category generated + */ +export const withdrawParamsBeet = new beet.BeetArgsStruct( + [["amountAtoms", beet.u64]], + "WithdrawParams", +); diff --git a/client/ts/src/wrapper/types/WrapperBatchUpdateParams.ts b/client/ts/src/wrapper/types/WrapperBatchUpdateParams.ts new file mode 100644 index 000000000..0d8930af7 --- /dev/null +++ b/client/ts/src/wrapper/types/WrapperBatchUpdateParams.ts @@ -0,0 +1,37 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from "@metaplex-foundation/beet"; +import { + WrapperCancelOrderParams, + wrapperCancelOrderParamsBeet, +} from "./WrapperCancelOrderParams"; +import { + WrapperPlaceOrderParams, + wrapperPlaceOrderParamsBeet, +} from "./WrapperPlaceOrderParams"; +export type WrapperBatchUpdateParams = { + cancels: WrapperCancelOrderParams[]; + cancelAll: boolean; + orders: WrapperPlaceOrderParams[]; + traderIndexHint: beet.COption; +}; + +/** + * @category userTypes + * @category generated + */ +export const wrapperBatchUpdateParamsBeet = + new beet.FixableBeetArgsStruct( + [ + ["cancels", beet.array(wrapperCancelOrderParamsBeet)], + ["cancelAll", beet.bool], + ["orders", beet.array(wrapperPlaceOrderParamsBeet)], + ["traderIndexHint", beet.coption(beet.u32)], + ], + "WrapperBatchUpdateParams", + ); diff --git a/client/ts/src/wrapper/types/WrapperCancelOrderParams.ts b/client/ts/src/wrapper/types/WrapperCancelOrderParams.ts new file mode 100644 index 000000000..84494174a --- /dev/null +++ b/client/ts/src/wrapper/types/WrapperCancelOrderParams.ts @@ -0,0 +1,21 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from "@metaplex-foundation/beet"; +export type WrapperCancelOrderParams = { + clientOrderId: beet.bignum; +}; + +/** + * @category userTypes + * @category generated + */ +export const wrapperCancelOrderParamsBeet = + new beet.BeetArgsStruct( + [["clientOrderId", beet.u64]], + "WrapperCancelOrderParams", + ); diff --git a/client/ts/src/wrapper/types/WrapperPlaceOrderParams.ts b/client/ts/src/wrapper/types/WrapperPlaceOrderParams.ts new file mode 100644 index 000000000..ef75fbda9 --- /dev/null +++ b/client/ts/src/wrapper/types/WrapperPlaceOrderParams.ts @@ -0,0 +1,38 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from "@metaplex-foundation/beet"; +import { OrderType, orderTypeBeet } from "./OrderType"; +export type WrapperPlaceOrderParams = { + clientOrderId: beet.bignum; + baseAtoms: beet.bignum; + priceMantissa: number; + priceExponent: number; + isBid: boolean; + lastValidSlot: number; + orderType: OrderType; + minOutAtoms: beet.bignum; +}; + +/** + * @category userTypes + * @category generated + */ +export const wrapperPlaceOrderParamsBeet = + new beet.BeetArgsStruct( + [ + ["clientOrderId", beet.u64], + ["baseAtoms", beet.u64], + ["priceMantissa", beet.u32], + ["priceExponent", beet.i8], + ["isBid", beet.bool], + ["lastValidSlot", beet.u32], + ["orderType", orderTypeBeet], + ["minOutAtoms", beet.u64], + ], + "WrapperPlaceOrderParams", + ); diff --git a/client/ts/src/wrapper/types/index.ts b/client/ts/src/wrapper/types/index.ts new file mode 100644 index 000000000..2909a0fc0 --- /dev/null +++ b/client/ts/src/wrapper/types/index.ts @@ -0,0 +1,6 @@ +export * from "./DepositParams"; +export * from "./OrderType"; +export * from "./WithdrawParams"; +export * from "./WrapperBatchUpdateParams"; +export * from "./WrapperCancelOrderParams"; +export * from "./WrapperPlaceOrderParams"; diff --git a/client/ts/src/wrapperObj.ts b/client/ts/src/wrapperObj.ts new file mode 100644 index 000000000..0e7bb849f --- /dev/null +++ b/client/ts/src/wrapperObj.ts @@ -0,0 +1,301 @@ +import { Connection, PublicKey } from '@solana/web3.js'; +import { bignum } from '@metaplex-foundation/beet'; +import { publicKey as beetPublicKey } from '@metaplex-foundation/beet-solana'; +import { marketInfoBeet, openOrderBeet } from './utils/beet'; +import { FIXED_WRAPPER_HEADER_SIZE, NIL } from './constants'; +import { OrderType } from './manifest'; +import { deserializeRedBlackTree } from './utils/redBlackTree'; + +/** + * All data stored on a wrapper account. + */ +export interface WrapperData { + /** Public key for the trader that owns this wrapper. */ + trader: PublicKey; + /** Array of market infos that have been parsed. */ + marketInfos: MarketInfoParsed[]; +} + +/** + * Parsed market info on a wrapper. Accurate to the last sync. + */ +export interface MarketInfoParsed { + /** Public key for market. */ + market: PublicKey; + /** Base balance in atoms. */ + baseBalanceAtoms: bignum; + /** Quote balance in atoms. */ + quoteBalanceAtoms: bignum; + /** Open orders. */ + orders: OpenOrder[]; + /** Last update slot number. */ + lastUpdatedSlot: number; +} + +/** + * Raw market info on a wrapper. + */ +export interface MarketInfoRaw { + market: PublicKey; + openOrdersRootIndex: number; + traderIndex: number; + baseBalanceAtoms: bignum; + quoteBalanceAtoms: bignum; + lastUpdatedSlot: number; + padding: number; // 4 bytes +} + +/** + * OpenOrder on a wrapper. Accurate as of the latest sync. + */ +export interface OpenOrder { + /** Client order id used for cancelling orders. Does not need to be unique. */ + clientOrderId: bignum; + /** Exchange defined id for an order. */ + orderSequenceNumber: bignum; + /** Price as float in atoms of quote per atoms of base. */ + price: number; + /** Number of base atoms in the order. */ + numBaseAtoms: bignum; + /** Hint for the location of the order in the manifest dynamic data. */ + dataIndex: number; + /** Last slot before this order is invalid and will be removed. */ + lastValidSlot: number; + /** Boolean for whether this order is on the bid side. */ + isBid: boolean; + /** Type of order (Limit, PostOnly, ...). */ + orderType: OrderType; +} + +export interface OpenOrderInternal { + clientOrderId: bignum; + orderSequenceNumber: bignum; + price: Uint8Array; + numBaseAtoms: bignum; + dataIndex: number; + lastValidSlot: number; + isBid: boolean; + orderType: number; + padding: bignum[]; // 22 bytes +} + +/** + * Wrapper object used for reading data from a wrapper for manifest markets. + */ +export class Wrapper { + /** Public key for the market account. */ + address: PublicKey; + /** Deserialized data. */ + private data: WrapperData; + + /** + * Constructs a Wrapper object. + * + * @param address The `PublicKey` of the wrapper account + * @param data Deserialized wrapper data + */ + private constructor({ + address, + data, + }: { + address: PublicKey; + data: WrapperData; + }) { + this.address = address; + this.data = data; + } + + /** + * Returns a `Wrapper` for a given address, a data buffer + * + * @param marketAddress The `PublicKey` of the wrapper account + * @param buffer The buffer holding the wrapper account data + */ + static loadFromBuffer({ + address, + buffer, + }: { + address: PublicKey; + buffer: Buffer; + }): Wrapper { + const wrapperData = Wrapper.deserializeWrapperBuffer(buffer); + return new Wrapper({ address, data: wrapperData }); + } + + /** + * Returns a `Wrapper` for a given address, a data buffer + * + * @param connection The Solana `Connection` object + * @param address The `PublicKey` of the wrapper account + */ + static async loadFromAddress({ + connection, + address, + }: { + connection: Connection; + address: PublicKey; + }): Promise { + const buffer = await connection + .getAccountInfo(address, 'confirmed') + .then((accountInfo) => accountInfo?.data); + + if (buffer === undefined) { + throw new Error(`Failed to load ${address}`); + } + return Wrapper.loadFromBuffer({ address, buffer }); + } + + /** + * Updates the data in a Wrapper. + * + * @param connection The Solana `Connection` object + */ + public async reload(connection: Connection): Promise { + const buffer = await connection + .getAccountInfo(this.address, 'confirmed') + .then((accountInfo) => accountInfo?.data); + if (buffer === undefined) { + throw new Error(`Failed to load ${this.address}`); + } + this.data = Wrapper.deserializeWrapperBuffer(buffer); + } + + /** + * Get the parsed market info from the wrapper. + * + * @param marketPk PublicKey for the market + * + * @return MarketInfoParsed + */ + public marketInfoForMarket(marketPk: PublicKey): MarketInfoParsed | null { + const filtered: MarketInfoParsed[] = this.data.marketInfos.filter( + (marketInfo: MarketInfoParsed) => { + return marketInfo.market.toBase58() == marketPk.toBase58(); + }, + ); + if (filtered.length == 0) { + return null; + } + return filtered[0]; + } + + /** + * Get the open orders from the wrapper. + * + * @param marketPk PublicKey for the market + * + * @return OpenOrder[] + */ + public openOrdersForMarket(marketPk: PublicKey): OpenOrder[] | null { + const filtered: MarketInfoParsed[] = this.data.marketInfos.filter( + (marketInfo: MarketInfoParsed) => { + return marketInfo.market.toBase58() == marketPk.toBase58(); + }, + ); + if (filtered.length == 0) { + return null; + } + return filtered[0].orders; + } + + // Do not include getters for the balances because those can be retrieved from + // the market and that will be fresher data or the same always. + + /** + * Print all information loaded about the wrapper in a human readable format. + */ + public prettyPrint() { + console.log(''); + console.log(`Wrapper: ${this.address.toBase58()}`); + console.log(`========================`); + console.log(`Trader: ${this.data.trader.toBase58()}`); + this.data.marketInfos.forEach((marketInfo: MarketInfoParsed) => { + console.log(`------------------------`); + console.log(`Market: ${marketInfo.market}`); + console.log(`Last updated slot: ${marketInfo.lastUpdatedSlot}`); + console.log( + `BaseAtoms: ${marketInfo.baseBalanceAtoms} QuoteAtoms: ${marketInfo.quoteBalanceAtoms}`, + ); + marketInfo.orders.forEach((order: OpenOrder) => { + console.log( + `OpenOrder: ClientOrderId: ${order.clientOrderId} ${order.numBaseAtoms}@${order.price} SeqNum: ${order.orderSequenceNumber} LastValidSlot: ${order.lastValidSlot} IsBid: ${order.isBid}`, + ); + }); + }); + console.log(`------------------------`); + } + + /** + * Deserializes wrapper data from a given buffer and returns a `Wrapper` object + * + * This includes both the fixed and dynamic parts of the market. + * https://github.com/CKS-Systems/manifest/blob/main/programs/wrapper/src/wrapper_state.rs + * + * @param data The data buffer to deserialize + * + * @returns WrapperData + */ + public static deserializeWrapperBuffer(data: Buffer): WrapperData { + let offset = 0; + // Deserialize the market header + const _discriminant = data.readBigUInt64LE(0); + offset += 8; + + const trader = beetPublicKey.read(data, offset); + offset += beetPublicKey.byteSize; + + const _numBytesAllocated = data.readUInt32LE(offset); + offset += 4; + + const _freeListHeadIndex = data.readUInt32LE(offset); + offset += 4; + + const marketInfosRootIndex = data.readUInt32LE(offset); + offset += 4; + + const _padding = data.readUInt32LE(offset); + offset += 4; + + const marketInfos: MarketInfoRaw[] = + marketInfosRootIndex != NIL + ? deserializeRedBlackTree( + data.subarray(FIXED_WRAPPER_HEADER_SIZE), + marketInfosRootIndex, + marketInfoBeet, + ) + : []; + const parsedMarketInfos: MarketInfoParsed[] = marketInfos.map( + (marketInfoRaw: MarketInfoRaw) => { + const rootIndex: number = marketInfoRaw.openOrdersRootIndex; + const parsedOpenOrders: OpenOrderInternal[] = + rootIndex != NIL + ? deserializeRedBlackTree( + data.subarray(FIXED_WRAPPER_HEADER_SIZE), + rootIndex, + openOrderBeet, + ) + : []; + const parsedOpenOrdersWithPrice: OpenOrder[] = parsedOpenOrders.map( + (openOrder: OpenOrderInternal) => { + return { + ...openOrder, + price: Buffer.from(openOrder.price).readDoubleLE(0), + }; + }, + ); + return { + market: marketInfoRaw.market, + baseBalanceAtoms: marketInfoRaw.baseBalanceAtoms, + quoteBalanceAtoms: marketInfoRaw.quoteBalanceAtoms, + orders: parsedOpenOrdersWithPrice, + lastUpdatedSlot: marketInfoRaw.lastUpdatedSlot, + }; + }, + ); + + return { + trader, + marketInfos: parsedMarketInfos, + }; + } +} diff --git a/client/ts/tests/batchUpdate.ts b/client/ts/tests/batchUpdate.ts new file mode 100644 index 000000000..898c80f07 --- /dev/null +++ b/client/ts/tests/batchUpdate.ts @@ -0,0 +1,105 @@ +import { + Connection, + Keypair, + sendAndConfirmTransaction, + PublicKey, + Transaction, +} from '@solana/web3.js'; +import { ManifestClient } from '../src/client'; +import { OrderType } from '../src/manifest/types'; +import { createMarket } from './createMarket'; +import { deposit } from './deposit'; +import { Market } from '../src/market'; +import { assert } from 'chai'; + +async function testBatchUpdate(): Promise { + const connection: Connection = new Connection('http://127.0.0.1:8899'); + const payerKeypair: Keypair = Keypair.generate(); + + const marketAddress: PublicKey = await createMarket(connection, payerKeypair); + const market: Market = await Market.loadFromAddress({ + connection, + address: marketAddress, + }); + + await deposit( + connection, + payerKeypair, + marketAddress, + market.baseMint(), + 10_000_000_000, + ); + await batchUpdate( + connection, + payerKeypair, + marketAddress, + 5_000_000_000, + 5, + false, + OrderType.Limit, + 0, + ); + + await market.reload(connection); + market.prettyPrint(); + + // Asks are sorted worst to best. + assert(market.asks().length == 1, 'batch update did not work'); + assert( + Number(market.asks()[0].numBaseAtoms) == 5_000_000_000, + 'ask top of book wrong size', + ); + assert(market.asks()[0].price == 5, 'ask top of book wrong price'); + assert(market.bids().length == 0, 'place bids did not work'); +} + +async function batchUpdate( + connection: Connection, + payerKeypair: Keypair, + marketAddress: PublicKey, + baseAtoms: number, + price: number, + isBid: boolean, + orderType: OrderType, + clientOrderId: number, + minOutAtoms: number = 0, + lastValidSlot: number = 0, +): Promise { + const client: ManifestClient = await ManifestClient.getClientForMarket( + connection, + marketAddress, + payerKeypair, + ); + + const placeOrderIx = client.batchUpdateIx( + [ + { + baseAtoms, + price, + isBid, + lastValidSlot: lastValidSlot, + orderType: orderType, + minOutAtoms, + clientOrderId, + }, + ], + [], + false, + ); + + const signature = await sendAndConfirmTransaction( + connection, + new Transaction().add(placeOrderIx), + [payerKeypair], + { + commitment: 'confirmed', + }, + ); + console.log(`Placed order in ${signature}`); +} + +describe('Batch update test', () => { + it('BatchUpdate', async () => { + await testBatchUpdate(); + }); +}); diff --git a/client/ts/tests/cancelOrder.ts b/client/ts/tests/cancelOrder.ts new file mode 100644 index 000000000..c4135f8e0 --- /dev/null +++ b/client/ts/tests/cancelOrder.ts @@ -0,0 +1,148 @@ +import { + Connection, + Keypair, + sendAndConfirmTransaction, + PublicKey, + Transaction, +} from '@solana/web3.js'; +import { ManifestClient } from '../src/client'; +import { OrderType } from '../src/manifest/types'; +import { createMarket } from './createMarket'; +import { deposit } from './deposit'; +import { placeOrder } from './placeOrder'; +import { Market } from '../src/market'; +import { assert } from 'chai'; + +async function testCancelOrder(): Promise { + const connection: Connection = new Connection('http://127.0.0.1:8899'); + const payerKeypair: Keypair = Keypair.generate(); + + const marketAddress: PublicKey = await createMarket(connection, payerKeypair); + const market: Market = await Market.loadFromAddress({ + connection, + address: marketAddress, + }); + + await deposit(connection, payerKeypair, marketAddress, market.baseMint(), 10); + await placeOrder( + connection, + payerKeypair, + marketAddress, + 5, + 5, + false, + OrderType.Limit, + 0, + ); + await placeOrder( + connection, + payerKeypair, + marketAddress, + 5, + 3, + false, + OrderType.Limit, + 1, + ); + await cancelOrder(connection, payerKeypair, marketAddress, 0); + await placeOrder( + connection, + payerKeypair, + marketAddress, + 5, + 3, + false, + OrderType.Limit, + 2, + ); + await cancelOrder(connection, payerKeypair, marketAddress, 2); + await placeOrder( + connection, + payerKeypair, + marketAddress, + 5, + 3, + false, + OrderType.Limit, + 3, + ); + await cancelOrder(connection, payerKeypair, marketAddress, 1); + await placeOrder( + connection, + payerKeypair, + marketAddress, + 1, + 3, + false, + OrderType.Limit, + 4, + ); + await placeOrder( + connection, + payerKeypair, + marketAddress, + 1, + 3, + false, + OrderType.Limit, + 5, + ); + await placeOrder( + connection, + payerKeypair, + marketAddress, + 1, + 3, + false, + OrderType.Limit, + 6, + ); + await placeOrder( + connection, + payerKeypair, + marketAddress, + 1, + 3, + false, + OrderType.Limit, + 7, + ); + // 1 was already cancelled so wrapper fails silently. + await cancelOrder(connection, payerKeypair, marketAddress, 1); + + await market.reload(connection); + assert(market.openOrders().length == 5, 'cancel did not cancel all orders'); + market.prettyPrint(); +} + +export async function cancelOrder( + connection: Connection, + payerKeypair: Keypair, + marketAddress: PublicKey, + clientOrderId: number, +): Promise { + const client: ManifestClient = await ManifestClient.getClientForMarket( + connection, + marketAddress, + payerKeypair, + ); + const cancelOrderIx = client.cancelOrderIx({ + clientOrderId, + }); + + const signature = await sendAndConfirmTransaction( + connection, + new Transaction().add(cancelOrderIx), + [payerKeypair], + { + commitment: 'confirmed', + }, + ); + console.log(`Canceled order in ${signature}`); +} + +describe('Cancel test', () => { + it('Place and cancel orders', async () => { + await testCancelOrder(); + }); +}); diff --git a/client/ts/tests/claimSeat.ts b/client/ts/tests/claimSeat.ts new file mode 100644 index 000000000..dbe4cda65 --- /dev/null +++ b/client/ts/tests/claimSeat.ts @@ -0,0 +1,72 @@ +import { Connection, Keypair, PublicKey } from '@solana/web3.js'; +import { ManifestClient } from '../src/client'; +import { createMarket } from './createMarket'; +import { Market } from '../src/market'; +import { assert } from 'chai'; + +async function testClaimSeat(): Promise { + const connection: Connection = new Connection('http://127.0.0.1:8899'); + const payerKeypair: Keypair = Keypair.generate(); + + const marketAddress: PublicKey = await createMarket(connection, payerKeypair); + const market: Market = await Market.loadFromAddress({ + connection, + address: marketAddress, + }); + market.prettyPrint(); + + await claimSeat(connection, marketAddress, payerKeypair); + + const marketUpdated: Market = await Market.loadFromAddress({ + connection, + address: marketAddress, + }); + marketUpdated.prettyPrint(); + assert( + marketUpdated.hasSeat(payerKeypair.publicKey), + 'claim seat did not have the seat claimed', + ); + + // Claiming on a second market. There is a wrapper, but not a claimed seat. + const marketAddress2: PublicKey = await createMarket( + connection, + payerKeypair, + ); + const market2: Market = await Market.loadFromAddress({ + connection, + address: marketAddress2, + }); + market2.prettyPrint(); + + await claimSeat(connection, marketAddress2, payerKeypair); + + const marketUpdated2: Market = await Market.loadFromAddress({ + connection, + address: marketAddress2, + }); + marketUpdated2.prettyPrint(); + assert( + marketUpdated2.hasSeat(payerKeypair.publicKey), + 'claim seat did not have the seat claimed on second seat', + ); + + // Test loading without needing to initialize on chain. + await Market.loadFromAddress({ + connection, + address: marketAddress2, + }); +} + +export async function claimSeat( + connection: Connection, + market: PublicKey, + payerKeypair: Keypair, +): Promise { + await ManifestClient.getClientForMarket(connection, market, payerKeypair); +} + +describe('Claim Seat test', () => { + it('Claim seat', async () => { + await testClaimSeat(); + }); +}); diff --git a/client/ts/tests/createMarket.ts b/client/ts/tests/createMarket.ts new file mode 100644 index 000000000..73564fa2b --- /dev/null +++ b/client/ts/tests/createMarket.ts @@ -0,0 +1,90 @@ +import { + Connection, + Keypair, + sendAndConfirmTransaction, + PublicKey, + SystemProgram, + TransactionInstruction, + Transaction, +} from '@solana/web3.js'; +import { ManifestClient } from '../src/client'; +import { PROGRAM_ID } from '../src/manifest'; +import { Market } from '../src/market'; +import { airdropSol, getClusterFromConnection } from '../src/utils/solana'; +import { createMint } from '@solana/spl-token'; +import { FIXED_MANIFEST_HEADER_SIZE } from '../src/constants'; + +async function testCreateMarket(): Promise { + const connection: Connection = new Connection('http://127.0.0.1:8899'); + const payerKeypair: Keypair = Keypair.generate(); + const marketAddress: PublicKey = await createMarket(connection, payerKeypair); + + const market: Market = await Market.loadFromAddress({ + connection, + address: marketAddress, + }); + market.prettyPrint(); +} + +export async function createMarket( + connection: Connection, + payerKeypair: Keypair, +): Promise { + const marketKeypair: Keypair = Keypair.generate(); + console.log(`Cluster is ${await getClusterFromConnection(connection)}`); + + // Get SOL for rent and make airdrop states. + await airdropSol(connection, payerKeypair.publicKey); + const baseMint: PublicKey = await createMint( + connection, + payerKeypair, + payerKeypair.publicKey, + payerKeypair.publicKey, + 9, + ); + const quoteMint: PublicKey = await createMint( + connection, + payerKeypair, + payerKeypair.publicKey, + payerKeypair.publicKey, + 6, + ); + console.log(`Created baseMint ${baseMint} quoteMint ${quoteMint}`); + + const createAccountIx: TransactionInstruction = SystemProgram.createAccount({ + fromPubkey: payerKeypair.publicKey, + newAccountPubkey: marketKeypair.publicKey, + space: FIXED_MANIFEST_HEADER_SIZE, + lamports: await connection.getMinimumBalanceForRentExemption( + FIXED_MANIFEST_HEADER_SIZE, + ), + programId: PROGRAM_ID, + }); + + const createMarketIx = ManifestClient.createMarketIx( + payerKeypair.publicKey, + baseMint, + quoteMint, + marketKeypair.publicKey, + ); + + const tx: Transaction = new Transaction(); + tx.add(createAccountIx); + tx.add(createMarketIx); + const signature = await sendAndConfirmTransaction( + connection, + tx, + [payerKeypair, marketKeypair], + { + commitment: 'finalized', + }, + ); + console.log(`Created market at ${marketKeypair.publicKey} in ${signature}`); + return marketKeypair.publicKey; +} + +describe('Create Market test', () => { + it('Create Market', async () => { + await testCreateMarket(); + }); +}); diff --git a/client/ts/tests/deposit.ts b/client/ts/tests/deposit.ts new file mode 100644 index 000000000..3185d70b7 --- /dev/null +++ b/client/ts/tests/deposit.ts @@ -0,0 +1,87 @@ +import { + Connection, + Keypair, + sendAndConfirmTransaction, + PublicKey, + Transaction, +} from '@solana/web3.js'; +import { ManifestClient } from '../src/client'; +import { + mintTo, + createAssociatedTokenAccountIdempotent, +} from '@solana/spl-token'; +import { createMarket } from './createMarket'; +import { Market } from '../src/market'; +import { assert } from 'chai'; + +async function testDeposit(): Promise { + const connection: Connection = new Connection('http://127.0.0.1:8899'); + const payerKeypair: Keypair = Keypair.generate(); + + const marketAddress: PublicKey = await createMarket(connection, payerKeypair); + const market: Market = await Market.loadFromAddress({ + connection, + address: marketAddress, + }); + + await deposit(connection, payerKeypair, marketAddress, market.baseMint(), 10); + + await market.reload(connection); + assert( + market.getWithdrawableBalanceAtoms(payerKeypair.publicKey, true) == 10, + 'deposit withdrawable balance check base', + ); + assert( + market.getWithdrawableBalanceAtoms(payerKeypair.publicKey, false) == 0, + 'deposit withdrawable balance check quote', + ); + market.prettyPrint(); +} + +export async function deposit( + connection: Connection, + payerKeypair: Keypair, + marketAddress: PublicKey, + mint: PublicKey, + amountAtoms: number, +): Promise { + const client: ManifestClient = await ManifestClient.getClientForMarket( + connection, + marketAddress, + payerKeypair, + ); + const depositIx = client.depositIx(payerKeypair.publicKey, mint, amountAtoms); + + const traderTokenAccount = await createAssociatedTokenAccountIdempotent( + connection, + payerKeypair, + mint, + payerKeypair.publicKey, + ); + + const mintSig = await mintTo( + connection, + payerKeypair, + mint, + traderTokenAccount, + payerKeypair.publicKey, + amountAtoms, + ); + console.log(`Minted ${amountAtoms} to ${traderTokenAccount} in ${mintSig}`); + + const signature = await sendAndConfirmTransaction( + connection, + new Transaction().add(depositIx), + [payerKeypair], + { + commitment: 'confirmed', + }, + ); + console.log(`Deposited ${amountAtoms} atoms in ${signature}`); +} + +describe('Deposit test', () => { + it('Deposit', async () => { + await testDeposit(); + }); +}); diff --git a/client/ts/tests/fillFeed.ts b/client/ts/tests/fillFeed.ts new file mode 100644 index 000000000..00b25bdca --- /dev/null +++ b/client/ts/tests/fillFeed.ts @@ -0,0 +1,98 @@ +import { Connection, Keypair, PublicKey } from '@solana/web3.js'; +import { OrderType } from '../src/manifest/types'; +import { createMarket } from './createMarket'; +import { deposit } from './deposit'; +import { Market } from '../src/market'; +import { assert } from 'chai'; +import { FillFeed } from '../src'; +import { placeOrder } from './placeOrder'; +import WebSocket from 'ws'; + +async function testFillFeed(): Promise { + const connection: Connection = new Connection('http://127.0.0.1:8899'); + const payerKeypair: Keypair = Keypair.generate(); + + const marketAddress: PublicKey = await createMarket(connection, payerKeypair); + const market: Market = await Market.loadFromAddress({ + connection, + address: marketAddress, + }); + + // Deposit and place the first order. + await deposit( + connection, + payerKeypair, + marketAddress, + market.baseMint(), + 10_000_000_000, + ); + await deposit( + connection, + payerKeypair, + marketAddress, + market.quoteMint(), + 10_000_000_000, + ); + await placeOrder( + connection, + payerKeypair, + marketAddress, + 5_000_000_000, + 5, + false, + OrderType.Limit, + 0, + ); + + await market.reload(connection); + market.prettyPrint(); + + const fillFeed: FillFeed = new FillFeed(connection); + await Promise.all([ + fillFeed.parseLogs(true), + checkForFillMessage(connection, payerKeypair, marketAddress), + ]); +} + +async function checkForFillMessage( + connection: Connection, + payerKeypair: Keypair, + marketAddress: PublicKey, +) { + const ws = new WebSocket('ws://localhost:1234'); + + ws.on('open', () => { + console.log('Connected to server'); + }); + + let gotFillMessage: boolean = false; + ws.on('message', (message: string) => { + console.log(`Received message from server: ${message}`); + gotFillMessage = true; + }); + + // Wait for fill feed to connect. + await new Promise((f) => setTimeout(f, 1_000)); + + await placeOrder( + connection, + payerKeypair, + marketAddress, + 5_000, + 5, + true, + OrderType.Limit, + 0, + ); + + // Wait for the fill log + await new Promise((f) => setTimeout(f, 20_000)); + assert(gotFillMessage, 'Fill feed message'); + ws.close(); +} + +describe('FillListener test', () => { + it('FillListener', async () => { + await testFillFeed(); + }); +}); diff --git a/client/ts/tests/market.ts b/client/ts/tests/market.ts new file mode 100644 index 000000000..63fc9a01d --- /dev/null +++ b/client/ts/tests/market.ts @@ -0,0 +1,110 @@ +import { Connection, Keypair, PublicKey } from '@solana/web3.js'; +import { Market } from '../src/market'; +import { createMarket } from './createMarket'; +import { ManifestClient } from '../src'; +import { assert } from 'chai'; +import { placeOrder } from './placeOrder'; +import { OrderType } from '../src/manifest'; +import { deposit } from './deposit'; + +async function testMarket(): Promise { + const connection: Connection = new Connection('http://127.0.0.1:8899'); + const payerKeypair: Keypair = Keypair.generate(); + const marketAddress: PublicKey = await createMarket(connection, payerKeypair); + + // Test loading successfully. + const market: Market = await Market.loadFromAddress({ + connection, + address: marketAddress, + }); + market.prettyPrint(); + + // Test loading fails on bad address + try { + await Market.loadFromAddress({ + connection, + address: Keypair.generate().publicKey, + }); + assert(false, 'expected load from address fail'); + } catch (err) { + assert(true, 'expected load from address fail'); + } + + // Test reloading successful. + await market.reload(connection); + + // Test reloading fail. + try { + await market.reload(new Connection('https://api.devnet.solana.com')); + assert(false, 'expected reload fail'); + } catch (err) { + assert(true, 'expected reload fail'); + } + + // Market withdrawable balance not init + assert( + market.getWithdrawableBalanceAtoms(payerKeypair.publicKey, true) == 0, + 'Get withdrawable balance with no seat', + ); + + // Init seat. + await ManifestClient.getClientForMarket( + connection, + marketAddress, + payerKeypair, + ); + + // Place an order to get more coverage on the pretty print. + await deposit( + connection, + payerKeypair, + marketAddress, + market.quoteMint(), + 10_000_000_000, + ); + // Market withdrawable balance after deposit + assert( + market.getWithdrawableBalanceAtoms(payerKeypair.publicKey, false) == 0, + 'Get withdrawable balance after deposit', + ); + + assert(market.baseDecimals() == 9, 'base decimals'); + assert(market.quoteDecimals() == 6, 'quote decimals'); + + // Put orders on both sides to test pretty printing. + await deposit( + connection, + payerKeypair, + marketAddress, + market.baseMint(), + 10_000_000_000, + ); + await placeOrder( + connection, + payerKeypair, + marketAddress, + 5_000, + 5, + false, + OrderType.Limit, + 0, + ); + await placeOrder( + connection, + payerKeypair, + marketAddress, + 5_000, + 5, + true, + OrderType.Limit, + 0, + ); + + market.prettyPrint(); +} + +describe('Market test', () => { + it('Market', async () => { + await testMarket(); + }); +}); diff --git a/client/ts/tests/placeOrder.ts b/client/ts/tests/placeOrder.ts new file mode 100644 index 000000000..67f7dd9bf --- /dev/null +++ b/client/ts/tests/placeOrder.ts @@ -0,0 +1,109 @@ +import { + Connection, + Keypair, + sendAndConfirmTransaction, + PublicKey, + Transaction, +} from '@solana/web3.js'; +import { ManifestClient } from '../src/client'; +import { OrderType } from '../src/manifest/types'; +import { createMarket } from './createMarket'; +import { deposit } from './deposit'; +import { Market } from '../src/market'; +import { assert } from 'chai'; + +async function testPlaceOrder(): Promise { + const connection: Connection = new Connection('http://127.0.0.1:8899'); + const payerKeypair: Keypair = Keypair.generate(); + + const marketAddress: PublicKey = await createMarket(connection, payerKeypair); + const market: Market = await Market.loadFromAddress({ + connection, + address: marketAddress, + }); + + await deposit( + connection, + payerKeypair, + marketAddress, + market.baseMint(), + 10_000_000_000, + ); + await placeOrder( + connection, + payerKeypair, + marketAddress, + 5_000_000_000, + 5, + false, + OrderType.Limit, + 0, + ); + await placeOrder( + connection, + payerKeypair, + marketAddress, + 3_000_000_000, + 3, + false, + OrderType.Limit, + 1, + ); + + await market.reload(connection); + market.prettyPrint(); + + // Asks are sorted worst to best. + assert(market.asks().length == 2, 'place asks did not work'); + assert( + Number(market.asks()[0].numBaseAtoms) == 5_000_000_000, + 'ask top of book wrong size', + ); + assert(market.asks()[0].price == 5, 'ask top of book wrong price'); + assert(market.bids().length == 0, 'place bids did not work'); +} + +export async function placeOrder( + connection: Connection, + payerKeypair: Keypair, + marketAddress: PublicKey, + baseAtoms: number, + price: number, + isBid: boolean, + orderType: OrderType, + clientOrderId: number, + minOutAtoms: number = 0, + lastValidSlot: number = 0, +): Promise { + const client: ManifestClient = await ManifestClient.getClientForMarket( + connection, + marketAddress, + payerKeypair, + ); + + const placeOrderIx = client.placeOrderIx({ + baseAtoms, + price, + isBid, + lastValidSlot: lastValidSlot, + orderType: orderType, + minOutAtoms, + clientOrderId, + }); + + const signature = await sendAndConfirmTransaction( + connection, + new Transaction().add(placeOrderIx), + [payerKeypair], + { + commitment: 'confirmed', + }, + ); + console.log(`Placed order in ${signature}`); +} + +describe('Place Order test', () => { + it('Place Order', async () => { + await testPlaceOrder(); + }); +}); diff --git a/client/ts/tests/swap.ts b/client/ts/tests/swap.ts new file mode 100644 index 000000000..79723a996 --- /dev/null +++ b/client/ts/tests/swap.ts @@ -0,0 +1,101 @@ +import { + Connection, + Keypair, + sendAndConfirmTransaction, + PublicKey, + Transaction, + TransactionInstruction, +} from '@solana/web3.js'; +import { ManifestClient } from '../src/client'; +import { createMarket } from './createMarket'; +import { Market } from '../src/market'; +import { + createAssociatedTokenAccountIdempotent, + mintTo, +} from '@solana/spl-token'; +import { assert } from 'chai'; + +async function testSwap(): Promise { + const connection: Connection = new Connection('http://127.0.0.1:8899'); + const payerKeypair: Keypair = Keypair.generate(); + + const marketAddress: PublicKey = await createMarket(connection, payerKeypair); + const market: Market = await Market.loadFromAddress({ + connection, + address: marketAddress, + }); + + const traderTokenAccount = await createAssociatedTokenAccountIdempotent( + connection, + payerKeypair, + market.baseMint(), + payerKeypair.publicKey, + ); + // Initialize so trader can receive. + await createAssociatedTokenAccountIdempotent( + connection, + payerKeypair, + market.quoteMint(), + payerKeypair.publicKey, + ); + + const amountAtoms: number = 1_000_000_000; + const mintSig = await mintTo( + connection, + payerKeypair, + market.baseMint(), + traderTokenAccount, + payerKeypair.publicKey, + amountAtoms, + ); + console.log(`Minted ${amountAtoms} to ${traderTokenAccount} in ${mintSig}`); + + await swap(connection, payerKeypair, marketAddress, amountAtoms / 10, false); + + await market.reload(connection); + market.prettyPrint(); + + // Asks are sorted worst to best. + assert(market.openOrders().length == 0, 'Swap does not rest order'); +} + +export async function swap( + connection: Connection, + payerKeypair: Keypair, + marketAddress: PublicKey, + amountAtoms: number, + isBid: boolean, + minOutAtoms: number = 0, +): Promise { + const client: ManifestClient = await ManifestClient.getClientForMarket( + connection, + marketAddress, + payerKeypair, + ); + + const swapIx: TransactionInstruction = await client.swapIx( + payerKeypair.publicKey, + { + inAtoms: amountAtoms, + outAtoms: minOutAtoms, + isBaseIn: isBid, + isExactIn: true, + }, + ); + + const signature = await sendAndConfirmTransaction( + connection, + new Transaction().add(swapIx), + [payerKeypair], + { + commitment: 'confirmed', + }, + ); + console.log(`Placed order in ${signature}`); +} + +describe('Swap test', () => { + it('Swap', async () => { + await testSwap(); + }); +}); diff --git a/client/ts/tests/utils.ts b/client/ts/tests/utils.ts new file mode 100644 index 000000000..ad08a9683 --- /dev/null +++ b/client/ts/tests/utils.ts @@ -0,0 +1,26 @@ +import { Connection } from '@solana/web3.js'; +import { assert } from 'chai'; +import { getClusterFromConnection } from '../src/utils/solana'; + +async function testUtils(): Promise { + const localnetConnection: Connection = new Connection( + 'http://127.0.0.1:8899', + ); + assert((await getClusterFromConnection(localnetConnection)) == 'localnet'); + + const devnetConnection: Connection = new Connection( + 'https://api.devnet.solana.com', + ); + assert((await getClusterFromConnection(devnetConnection)) == 'devnet'); + + const mainnetConnection: Connection = new Connection( + 'https://api.mainnet-beta.solana.com', + ); + assert((await getClusterFromConnection(mainnetConnection)) == 'mainnet-beta'); +} + +describe('Utils test', () => { + it('Utils', async () => { + await testUtils(); + }); +}); diff --git a/client/ts/tests/withdraw.ts b/client/ts/tests/withdraw.ts new file mode 100644 index 000000000..20f5be7e2 --- /dev/null +++ b/client/ts/tests/withdraw.ts @@ -0,0 +1,72 @@ +import { + Connection, + Keypair, + sendAndConfirmTransaction, + PublicKey, + Transaction, +} from '@solana/web3.js'; +import { ManifestClient } from '../src/client'; +import { createMarket } from './createMarket'; +import { deposit } from './deposit'; +import { Market } from '../src/market'; +import { assert } from 'chai'; + +async function testWithdraw(): Promise { + const connection: Connection = new Connection('http://127.0.0.1:8899'); + const payerKeypair: Keypair = Keypair.generate(); + + const marketAddress: PublicKey = await createMarket(connection, payerKeypair); + const market: Market = await Market.loadFromAddress({ + connection, + address: marketAddress, + }); + + await deposit(connection, payerKeypair, marketAddress, market.baseMint(), 10); + await withdraw(connection, payerKeypair, marketAddress, market.baseMint(), 5); + + await market.reload(connection); + assert( + market.getWithdrawableBalanceAtoms(payerKeypair.publicKey, true) == 5, + 'withdraw withdrawable balance check base', + ); + assert( + market.getWithdrawableBalanceAtoms(payerKeypair.publicKey, false) == 0, + 'withdraw withdrawable balance check quote', + ); + market.prettyPrint(); +} + +export async function withdraw( + connection: Connection, + payerKeypair: Keypair, + marketAddress: PublicKey, + mint: PublicKey, + amountAtoms: number, +): Promise { + const client: ManifestClient = await ManifestClient.getClientForMarket( + connection, + marketAddress, + payerKeypair, + ); + const withdrawIx = client.withdrawIx( + payerKeypair.publicKey, + mint, + amountAtoms, + ); + + const signature = await sendAndConfirmTransaction( + connection, + new Transaction().add(withdrawIx), + [payerKeypair], + { + commitment: 'confirmed', + }, + ); + console.log(`Withdrew ${amountAtoms} atoms in ${signature}`); +} + +describe('Withdraw test', () => { + it('Withdraw', async () => { + await testWithdraw(); + }); +}); diff --git a/client/ts/tests/wrapper.ts b/client/ts/tests/wrapper.ts new file mode 100644 index 000000000..ffe97e2b8 --- /dev/null +++ b/client/ts/tests/wrapper.ts @@ -0,0 +1,154 @@ +import { + Connection, + Keypair, + PublicKey, + SystemProgram, + Transaction, + TransactionInstruction, + sendAndConfirmTransaction, +} from '@solana/web3.js'; +import { Market } from '../src/market'; +import { createMarket } from './createMarket'; +import { ManifestClient } from '../src'; +import { assert } from 'chai'; +import { placeOrder } from './placeOrder'; +import { OrderType } from '../src/manifest'; +import { deposit } from './deposit'; +import { + createCreateWrapperInstruction, + PROGRAM_ID as WRAPPER_PROGRAM_ID, +} from '../src/wrapper'; +import { Wrapper } from '../src/wrapperObj'; +import { FIXED_WRAPPER_HEADER_SIZE } from '../src/constants'; +import { airdropSol } from '../src/utils/solana'; + +async function testWrapper(): Promise { + const connection: Connection = new Connection('http://127.0.0.1:8899'); + const payerKeypair: Keypair = Keypair.generate(); + const marketAddress: PublicKey = await createMarket(connection, payerKeypair); + + const market: Market = await Market.loadFromAddress({ + connection, + address: marketAddress, + }); + market.prettyPrint(); + + const client: ManifestClient = await ManifestClient.getClientForMarket( + connection, + marketAddress, + payerKeypair, + ); + await client.reload(); + + // Test loading successfully. + const wrapper: Wrapper = await Wrapper.loadFromAddress({ + connection, + address: client.wrapper.address, + }); + + // Test loading fails on bad address + try { + await Wrapper.loadFromAddress({ + connection, + address: Keypair.generate().publicKey, + }); + assert(false, 'expected load from address fail'); + } catch (err) { + assert(true, 'expected load from address fail'); + } + + // Test reloading successful. + await wrapper.reload(connection); + + // Test reloading fail. + try { + await wrapper.reload(new Connection('https://api.devnet.solana.com')); + assert(false, 'expected reload fail'); + } catch (err) { + assert(true, 'expected reload fail'); + } + + // Wrapper successfully find market info + assert( + wrapper.marketInfoForMarket(marketAddress) != null, + 'expected non null market info for market', + ); + // Wrapper fail find market info + assert( + wrapper.marketInfoForMarket(Keypair.generate().publicKey) == null, + 'expected null market info for market', + ); + + // Place an order to get more coverage on the pretty print. + await deposit( + connection, + payerKeypair, + marketAddress, + market.baseMint(), + 10_000_000_000, + ); + await placeOrder( + connection, + payerKeypair, + marketAddress, + 5_000_000_000, + 5, + false, + OrderType.Limit, + 0, + ); + + await wrapper.reload(connection); + // Wrapper successfully find open orders + assert( + wrapper.openOrdersForMarket(marketAddress) != null, + 'expected non null open orders for market', + ); + // Wrapper fail find open orders + assert( + wrapper.openOrdersForMarket(Keypair.generate().publicKey) == null, + 'expected null open orders for market', + ); + + wrapper.prettyPrint(); + + // Wrapper without a market for coverage. + const payerKeypair2: Keypair = Keypair.generate(); + await airdropSol(connection, payerKeypair2.publicKey); + const wrapperKeypair2: Keypair = Keypair.generate(); + const createAccountIx: TransactionInstruction = SystemProgram.createAccount({ + fromPubkey: payerKeypair2.publicKey, + newAccountPubkey: wrapperKeypair2.publicKey, + space: FIXED_WRAPPER_HEADER_SIZE, + lamports: await connection.getMinimumBalanceForRentExemption( + FIXED_WRAPPER_HEADER_SIZE, + ), + programId: WRAPPER_PROGRAM_ID, + }); + const createWrapperIx: TransactionInstruction = + createCreateWrapperInstruction({ + owner: payerKeypair2.publicKey, + payer: payerKeypair2.publicKey, + wrapperState: wrapperKeypair2.publicKey, + }); + + await sendAndConfirmTransaction( + connection, + new Transaction().add(createAccountIx).add(createWrapperIx), + [payerKeypair2, wrapperKeypair2], + { + commitment: 'finalized', + }, + ); + const wrapper2 = await Wrapper.loadFromAddress({ + connection, + address: wrapperKeypair2.publicKey, + }); + wrapper2.prettyPrint(); +} + +describe('Wrapper test', () => { + it('Wrapper', async () => { + await testWrapper(); + }); +}); diff --git a/local-validator-test.sh b/local-validator-test.sh new file mode 100644 index 000000000..35a616c98 --- /dev/null +++ b/local-validator-test.sh @@ -0,0 +1,25 @@ +# Start a local validator +solana-test-validator > /dev/null 2>&1 & +echo "Started test validator, sleeping for 15 seconds before starting" +sleep 15 + +# Save the pid so we can kill it at the end +TEST_VALIDATOR_PID=$! + +cargo build-sbf +echo "Rebuilt program" + +echo "Setting solana config to localnet" +solana config set --url l + +solana program deploy target/deploy/manifest.so +solana program deploy target/deploy/wrapper.so +echo "Deployed manifest and wrapper" + +yarn test +echo "Done with client tests" + +kill -9 $TEST_VALIDATOR_PID + +echo "Cleaning up ledger" +rm -rf test-ledger \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 000000000..fe16f5684 --- /dev/null +++ b/package.json @@ -0,0 +1,67 @@ +{ + "name": "@cks-systems/manifest-sdk", + "version": "0.1.0", + "description": "TypeScript SDK for Manifest", + "author": "CKS Systems", + "repository": "https://github.com/CKS-Systems/manifest", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "license": "MIT", + "scripts": { + "build": "rimraf dist && yarn build:browser && yarn build:node && yarn build:types", + "build:node": "tsc -p tsconfig.cjs.json --noEmit false", + "build:browser": "tsc -p tsconfig.esm.json --noEmit false", + "build:types": "tsc -p tsconfig.types.json --noEmit false", + "format": "prettier --check client/ts", + "lint": "eslint client/ts/**/*.ts --ext ts --ext tsx --ext js --quiet", + "typecheck": "tsc --noEmit --pretty", + "validate": "yarn lint && yarn format", + "prepare": "yarn build", + "clean": "rm -rf dist", + "test": "TS_NODE_PROJECT='./tsconfig.cjs.json' nyc --reporter=lcov ts-mocha client/ts/tests/*.ts --timeout 3000000" + }, + "dependencies": { + "@metaplex-foundation/beet": "^0.7.1", + "@metaplex-foundation/rustbin": "^0.3.1", + "@metaplex-foundation/solita": "^0.12.2", + "@solana/spl-token": "^0.4.8", + "@solana/web3.js": "^1.95.1", + "bn.js": "^5.2.1", + "borsh": "^0.7.0", + "bs58": "^6.0.0", + "js-sha256": "^0.11.0", + "keccak256": "^1.0.6", + "percentile": "^1.6.0", + "typedoc": "^0.26.3", + "ws": "^8.18.0", + "zstddec": "^0.0.2" + }, + "prettier": { + "singleQuote": true, + "trailingComma": "all", + "useTabs": false, + "bracketSpacing": true, + "semi": true, + "tabWidth": 2 + }, + "devDependencies": { + "@types/bn.js": "^5.1.1", + "@types/chai": "^4.3.4", + "@types/mocha": "^10.0.1", + "@types/node": "^20.14.12", + "@typescript-eslint/eslint-plugin": "^7.16.0", + "@typescript-eslint/parser": "^7.16.0", + "chai": "^4.3.7", + "codecov": "^3.8.3", + "eslint": "^8.35.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.1.3", + "mocha": "^10.2.0", + "nyc": "^17.0.0", + "prettier": "^3.3.2", + "rpc-websockets": "^7.5.1", + "ts-mocha": "^10.0.0", + "ts-node": "^10.9.1", + "typescript": "^5.5.3" + } +} diff --git a/programs/manifest/src/state/constants.rs b/programs/manifest/src/state/constants.rs index c484d44c3..8cafc7b20 100644 --- a/programs/manifest/src/state/constants.rs +++ b/programs/manifest/src/state/constants.rs @@ -5,6 +5,7 @@ pub const GLOBAL_FIXED_SIZE: usize = 88; // Red black tree overhead is 16 bytes. If each block is 80 bytes, then we get // 64 bytes for a RestingOrder or ClaimedSeat. +// TODO: Separate constants for global block size and market block size. pub const BLOCK_SIZE: usize = 80; const BLOCK_PAYLOAD_SIZE: usize = BLOCK_SIZE - RBTREE_OVERHEAD_BYTES; pub const RESTING_ORDER_SIZE: usize = BLOCK_PAYLOAD_SIZE; diff --git a/tsconfig.cjs.json b/tsconfig.cjs.json new file mode 100644 index 000000000..872702721 --- /dev/null +++ b/tsconfig.cjs.json @@ -0,0 +1,13 @@ +{ + "extends": "./tsconfig", + "compilerOptions": { + "declaration": true, + "declarationMap": false, + "module": "commonjs", + "outDir": "dist/cjs", + "sourceMap": false + }, + "include": [ + "client/ts/src", + ] +} \ No newline at end of file diff --git a/tsconfig.esm.json b/tsconfig.esm.json new file mode 100644 index 000000000..b910cc3ff --- /dev/null +++ b/tsconfig.esm.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig", + "compilerOptions": { + "declaration": false, + "declarationMap": false, + "module": "esnext", + "outDir": "dist/esm", + "sourceMap": false, + } +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..1d78f02df --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,47 @@ +{ + "compilerOptions": { + "baseUrl": "src", + "module": "esnext", + "target": "esnext", + "esModuleInterop": true, + "moduleResolution": "node", + "allowSyntheticDefaultImports": true, + "resolveJsonModule": true, + "strict": true, + "noEmit": true, + "noImplicitAny": false, + "skipLibCheck": true, + "strictNullChecks": true, + "types": [ + "mocha", + "node" + ], + "paths": { + "@instructions/*": [ + "instructions/*", + "wrapper/instructions/*" + ], + "@types/*": [ + "types/*", + "wrapper/types/*" + ], + "@errors/*": [ + "errors/*" + ], + "@accounts/*": [ + "accounts/*" + ] + }, + }, + "ts-node": { + // these options are overrides used only by ts-node + // same as the --compilerOptions flag and the TS_NODE_COMPILER_OPTIONS environment variable + "compilerOptions": { + "module": "commonjs" + } + }, + "include": [ + "client/ts/src/**/*.ts", + "client/ts/tests/**/*.ts", + ] +} \ No newline at end of file diff --git a/tsconfig.types.json b/tsconfig.types.json new file mode 100644 index 000000000..9a7a605d6 --- /dev/null +++ b/tsconfig.types.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig", + "compilerOptions": { + "noEmit": false, + "outDir": "./dist/types", + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true + } +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 000000000..63e941002 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,3423 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@ampproject/remapping@^2.2.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" + integrity sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" + +"@babel/code-frame@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.7.tgz#882fd9e09e8ee324e496bd040401c6f046ef4465" + integrity sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA== + dependencies: + "@babel/highlight" "^7.24.7" + picocolors "^1.0.0" + +"@babel/compat-data@^7.25.2": + version "7.25.4" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.25.4.tgz#7d2a80ce229890edcf4cc259d4d696cb4dae2fcb" + integrity sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ== + +"@babel/core@^7.23.9": + version "7.25.2" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.25.2.tgz#ed8eec275118d7613e77a352894cd12ded8eba77" + integrity sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.24.7" + "@babel/generator" "^7.25.0" + "@babel/helper-compilation-targets" "^7.25.2" + "@babel/helper-module-transforms" "^7.25.2" + "@babel/helpers" "^7.25.0" + "@babel/parser" "^7.25.0" + "@babel/template" "^7.25.0" + "@babel/traverse" "^7.25.2" + "@babel/types" "^7.25.2" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + +"@babel/generator@^7.25.0", "@babel/generator@^7.25.4": + version "7.25.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.25.5.tgz#b31cf05b3fe8c32d206b6dad03bb0aacbde73450" + integrity sha512-abd43wyLfbWoxC6ahM8xTkqLpGB2iWBVyuKC9/srhFunCd1SDNrV1s72bBpK4hLj8KLzHBBcOblvLQZBNw9r3w== + dependencies: + "@babel/types" "^7.25.4" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^2.5.1" + +"@babel/helper-compilation-targets@^7.25.2": + version "7.25.2" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz#e1d9410a90974a3a5a66e84ff55ef62e3c02d06c" + integrity sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw== + dependencies: + "@babel/compat-data" "^7.25.2" + "@babel/helper-validator-option" "^7.24.8" + browserslist "^4.23.1" + lru-cache "^5.1.1" + semver "^6.3.1" + +"@babel/helper-module-imports@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz#f2f980392de5b84c3328fc71d38bd81bbb83042b" + integrity sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA== + dependencies: + "@babel/traverse" "^7.24.7" + "@babel/types" "^7.24.7" + +"@babel/helper-module-transforms@^7.25.2": + version "7.25.2" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz#ee713c29768100f2776edf04d4eb23b8d27a66e6" + integrity sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ== + dependencies: + "@babel/helper-module-imports" "^7.24.7" + "@babel/helper-simple-access" "^7.24.7" + "@babel/helper-validator-identifier" "^7.24.7" + "@babel/traverse" "^7.25.2" + +"@babel/helper-simple-access@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz#bcade8da3aec8ed16b9c4953b74e506b51b5edb3" + integrity sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg== + dependencies: + "@babel/traverse" "^7.24.7" + "@babel/types" "^7.24.7" + +"@babel/helper-string-parser@^7.24.8": + version "7.24.8" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz#5b3329c9a58803d5df425e5785865881a81ca48d" + integrity sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ== + +"@babel/helper-validator-identifier@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz#75b889cfaf9e35c2aaf42cf0d72c8e91719251db" + integrity sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w== + +"@babel/helper-validator-option@^7.24.8": + version "7.24.8" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz#3725cdeea8b480e86d34df15304806a06975e33d" + integrity sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q== + +"@babel/helpers@^7.25.0": + version "7.25.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.25.0.tgz#e69beb7841cb93a6505531ede34f34e6a073650a" + integrity sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw== + dependencies: + "@babel/template" "^7.25.0" + "@babel/types" "^7.25.0" + +"@babel/highlight@^7.24.7": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.7.tgz#a05ab1df134b286558aae0ed41e6c5f731bf409d" + integrity sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw== + dependencies: + "@babel/helper-validator-identifier" "^7.24.7" + chalk "^2.4.2" + js-tokens "^4.0.0" + picocolors "^1.0.0" + +"@babel/parser@^7.23.9", "@babel/parser@^7.25.0", "@babel/parser@^7.25.4": + version "7.25.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.25.4.tgz#af4f2df7d02440286b7de57b1c21acfb2a6f257a" + integrity sha512-nq+eWrOgdtu3jG5Os4TQP3x3cLA8hR8TvJNjD8vnPa20WGycimcparWnLK4jJhElTK6SDyuJo1weMKO/5LpmLA== + dependencies: + "@babel/types" "^7.25.4" + +"@babel/runtime@^7.25.0": + version "7.25.4" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.25.4.tgz#6ef37d678428306e7d75f054d5b1bdb8cf8aa8ee" + integrity sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w== + dependencies: + regenerator-runtime "^0.14.0" + +"@babel/template@^7.25.0": + version "7.25.0" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.25.0.tgz#e733dc3134b4fede528c15bc95e89cb98c52592a" + integrity sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q== + dependencies: + "@babel/code-frame" "^7.24.7" + "@babel/parser" "^7.25.0" + "@babel/types" "^7.25.0" + +"@babel/traverse@^7.24.7", "@babel/traverse@^7.25.2": + version "7.25.4" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.25.4.tgz#648678046990f2957407e3086e97044f13c3e18e" + integrity sha512-VJ4XsrD+nOvlXyLzmLzUs/0qjFS4sK30te5yEFlvbbUNEgKaVb2BHZUpAL+ttLPQAHNrsI3zZisbfha5Cvr8vg== + dependencies: + "@babel/code-frame" "^7.24.7" + "@babel/generator" "^7.25.4" + "@babel/parser" "^7.25.4" + "@babel/template" "^7.25.0" + "@babel/types" "^7.25.4" + debug "^4.3.1" + globals "^11.1.0" + +"@babel/types@^7.24.7", "@babel/types@^7.25.0", "@babel/types@^7.25.2", "@babel/types@^7.25.4": + version "7.25.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.25.4.tgz#6bcb46c72fdf1012a209d016c07f769e10adcb5f" + integrity sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ== + dependencies: + "@babel/helper-string-parser" "^7.24.8" + "@babel/helper-validator-identifier" "^7.24.7" + to-fast-properties "^2.0.0" + +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + +"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.6.1": + version "4.11.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.0.tgz#b0ffd0312b4a3fd2d6f77237e7248a5ad3a680ae" + integrity sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A== + +"@eslint/eslintrc@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" + integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.6.0" + globals "^13.19.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@8.57.0": + version "8.57.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f" + integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g== + +"@humanwhocodes/config-array@^0.11.14": + version "0.11.14" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" + integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== + dependencies: + "@humanwhocodes/object-schema" "^2.0.2" + debug "^4.3.1" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" + integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== + +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== + dependencies: + camelcase "^5.3.1" + find-up "^4.1.0" + get-package-type "^0.1.0" + js-yaml "^3.13.1" + resolve-from "^5.0.0" + +"@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" + integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@metaplex-foundation/beet-solana@^0.3.0": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@metaplex-foundation/beet-solana/-/beet-solana-0.3.1.tgz#4b37cda5c7f32ffd2bdd8b3164edc05c6463ab35" + integrity sha512-tgyEl6dvtLln8XX81JyBvWjIiEcjTkUwZbrM5dIobTmoqMuGewSyk9CClno8qsMsFdB5T3jC91Rjeqmu/6xk2g== + dependencies: + "@metaplex-foundation/beet" ">=0.1.0" + "@solana/web3.js" "^1.56.2" + bs58 "^5.0.0" + debug "^4.3.4" + +"@metaplex-foundation/beet@>=0.1.0", "@metaplex-foundation/beet@^0.7.1": + version "0.7.2" + resolved "https://registry.yarnpkg.com/@metaplex-foundation/beet/-/beet-0.7.2.tgz#fa4726e4cfd4fb6fed6cddc9b5213c1c2a2d0b77" + integrity sha512-K+g3WhyFxKPc0xIvcIjNyV1eaTVJTiuaHZpig7Xx0MuYRMoJLLvhLTnUXhFdR5Tu2l2QSyKwfyXDgZlzhULqFg== + dependencies: + ansicolors "^0.3.2" + assert "^2.1.0" + bn.js "^5.2.0" + debug "^4.3.3" + +"@metaplex-foundation/beet@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@metaplex-foundation/beet/-/beet-0.4.0.tgz#eb2a0a6eb084bb25d67dd9bed2f7387ee7e63a55" + integrity sha512-2OAKJnLatCc3mBXNL0QmWVQKAWK2C7XDfepgL0p/9+8oSx4bmRAFHFqptl1A/C0U5O3dxGwKfmKluW161OVGcA== + dependencies: + ansicolors "^0.3.2" + bn.js "^5.2.0" + debug "^4.3.3" + +"@metaplex-foundation/rustbin@^0.3.0", "@metaplex-foundation/rustbin@^0.3.1": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@metaplex-foundation/rustbin/-/rustbin-0.3.5.tgz#56d028afd96c2b56ad3bbea22ff454adde900e8c" + integrity sha512-m0wkRBEQB/8krwMwKBvFugufZtYwMXiGHud2cTDAv+aGXK4M90y0Hx67/wpu+AqqoQfdV8VM9YezUOHKD+Z5kA== + dependencies: + debug "^4.3.3" + semver "^7.3.7" + text-table "^0.2.0" + toml "^3.0.0" + +"@metaplex-foundation/solita@^0.12.2": + version "0.12.2" + resolved "https://registry.yarnpkg.com/@metaplex-foundation/solita/-/solita-0.12.2.tgz#13ef213ac183c986f6d01c5d981c44e59a900834" + integrity sha512-oczMfE43NNHWweSqhXPTkQBUbap/aAiwjDQw8zLKNnd/J8sXr/0+rKcN5yJIEgcHeKRkp90eTqkmt2WepQc8yw== + dependencies: + "@metaplex-foundation/beet" "^0.4.0" + "@metaplex-foundation/beet-solana" "^0.3.0" + "@metaplex-foundation/rustbin" "^0.3.0" + "@solana/web3.js" "^1.36.0" + camelcase "^6.2.1" + debug "^4.3.3" + js-sha256 "^0.9.0" + prettier "^2.5.1" + snake-case "^3.0.4" + spok "^1.4.3" + +"@noble/curves@^1.4.2": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.5.0.tgz#7a9b9b507065d516e6dce275a1e31db8d2a100dd" + integrity sha512-J5EKamIHnKPyClwVrzmaf5wSdQXgdHcPZIZLu3bwnbeCx8/7NPK5q2ZBWF+5FvYGByjiQQsJYX6jfgB2wDPn3A== + dependencies: + "@noble/hashes" "1.4.0" + +"@noble/hashes@1.4.0", "@noble/hashes@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426" + integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg== + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@pkgr/core@^0.1.0": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31" + integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA== + +"@shikijs/core@1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@shikijs/core/-/core-1.14.1.tgz#008f1c4a20ff83fd1672d9e31d76b687862f7511" + integrity sha512-KyHIIpKNaT20FtFPFjCQB5WVSTpLR/n+jQXhWHWVUMm9MaOaG9BGOG0MSyt7yA4+Lm+4c9rTc03tt3nYzeYSfw== + dependencies: + "@types/hast" "^3.0.4" + +"@solana/buffer-layout-utils@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@solana/buffer-layout-utils/-/buffer-layout-utils-0.2.0.tgz#b45a6cab3293a2eb7597cceb474f229889d875ca" + integrity sha512-szG4sxgJGktbuZYDg2FfNmkMi0DYQoVjN2h7ta1W1hPrwzarcFLBq9UpX1UjNXsNpT9dn+chgprtWGioUAr4/g== + dependencies: + "@solana/buffer-layout" "^4.0.0" + "@solana/web3.js" "^1.32.0" + bigint-buffer "^1.1.5" + bignumber.js "^9.0.1" + +"@solana/buffer-layout@^4.0.0", "@solana/buffer-layout@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz#b996235eaec15b1e0b5092a8ed6028df77fa6c15" + integrity sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA== + dependencies: + buffer "~6.0.3" + +"@solana/codecs-core@2.0.0-preview.2": + version "2.0.0-preview.2" + resolved "https://registry.yarnpkg.com/@solana/codecs-core/-/codecs-core-2.0.0-preview.2.tgz#689784d032fbc1fedbde40bb25d76cdcecf6553b" + integrity sha512-gLhCJXieSCrAU7acUJjbXl+IbGnqovvxQLlimztPoGgfLQ1wFYu+XJswrEVQqknZYK1pgxpxH3rZ+OKFs0ndQg== + dependencies: + "@solana/errors" "2.0.0-preview.2" + +"@solana/codecs-core@2.0.0-preview.4": + version "2.0.0-preview.4" + resolved "https://registry.yarnpkg.com/@solana/codecs-core/-/codecs-core-2.0.0-preview.4.tgz#770826105f2f884110a21662573e7a2014654324" + integrity sha512-A0VVuDDA5kNKZUinOqHxJQK32aKTucaVbvn31YenGzHX1gPqq+SOnFwgaEY6pq4XEopSmaK16w938ZQS8IvCnw== + dependencies: + "@solana/errors" "2.0.0-preview.4" + +"@solana/codecs-data-structures@2.0.0-preview.2": + version "2.0.0-preview.2" + resolved "https://registry.yarnpkg.com/@solana/codecs-data-structures/-/codecs-data-structures-2.0.0-preview.2.tgz#e82cb1b6d154fa636cd5c8953ff3f32959cc0370" + integrity sha512-Xf5vIfromOZo94Q8HbR04TbgTwzigqrKII0GjYr21K7rb3nba4hUW2ir8kguY7HWFBcjHGlU5x3MevKBOLp3Zg== + dependencies: + "@solana/codecs-core" "2.0.0-preview.2" + "@solana/codecs-numbers" "2.0.0-preview.2" + "@solana/errors" "2.0.0-preview.2" + +"@solana/codecs-data-structures@2.0.0-preview.4": + version "2.0.0-preview.4" + resolved "https://registry.yarnpkg.com/@solana/codecs-data-structures/-/codecs-data-structures-2.0.0-preview.4.tgz#f8a2470982a9792334737ea64000ccbdff287247" + integrity sha512-nt2k2eTeyzlI/ccutPcG36M/J8NAYfxBPI9h/nQjgJ+M+IgOKi31JV8StDDlG/1XvY0zyqugV3I0r3KAbZRJpA== + dependencies: + "@solana/codecs-core" "2.0.0-preview.4" + "@solana/codecs-numbers" "2.0.0-preview.4" + "@solana/errors" "2.0.0-preview.4" + +"@solana/codecs-numbers@2.0.0-preview.2": + version "2.0.0-preview.2" + resolved "https://registry.yarnpkg.com/@solana/codecs-numbers/-/codecs-numbers-2.0.0-preview.2.tgz#56995c27396cd8ee3bae8bd055363891b630bbd0" + integrity sha512-aLZnDTf43z4qOnpTcDsUVy1Ci9im1Md8thWipSWbE+WM9ojZAx528oAql+Cv8M8N+6ALKwgVRhPZkto6E59ARw== + dependencies: + "@solana/codecs-core" "2.0.0-preview.2" + "@solana/errors" "2.0.0-preview.2" + +"@solana/codecs-numbers@2.0.0-preview.4": + version "2.0.0-preview.4" + resolved "https://registry.yarnpkg.com/@solana/codecs-numbers/-/codecs-numbers-2.0.0-preview.4.tgz#6a53b456bb7866f252d8c032c81a92651e150f66" + integrity sha512-Q061rLtMadsO7uxpguT+Z7G4UHnjQ6moVIxAQxR58nLxDPCC7MB1Pk106/Z7NDhDLHTcd18uO6DZ7ajHZEn2XQ== + dependencies: + "@solana/codecs-core" "2.0.0-preview.4" + "@solana/errors" "2.0.0-preview.4" + +"@solana/codecs-strings@2.0.0-preview.2": + version "2.0.0-preview.2" + resolved "https://registry.yarnpkg.com/@solana/codecs-strings/-/codecs-strings-2.0.0-preview.2.tgz#8bd01a4e48614d5289d72d743c3e81305d445c46" + integrity sha512-EgBwY+lIaHHgMJIqVOGHfIfpdmmUDNoNO/GAUGeFPf+q0dF+DtwhJPEMShhzh64X2MeCZcmSO6Kinx0Bvmmz2g== + dependencies: + "@solana/codecs-core" "2.0.0-preview.2" + "@solana/codecs-numbers" "2.0.0-preview.2" + "@solana/errors" "2.0.0-preview.2" + +"@solana/codecs-strings@2.0.0-preview.4": + version "2.0.0-preview.4" + resolved "https://registry.yarnpkg.com/@solana/codecs-strings/-/codecs-strings-2.0.0-preview.4.tgz#4d06bb722a55a5d04598d362021bfab4bd446760" + integrity sha512-YDbsQePRWm+xnrfS64losSGRg8Wb76cjK1K6qfR8LPmdwIC3787x9uW5/E4icl/k+9nwgbIRXZ65lpF+ucZUnw== + dependencies: + "@solana/codecs-core" "2.0.0-preview.4" + "@solana/codecs-numbers" "2.0.0-preview.4" + "@solana/errors" "2.0.0-preview.4" + +"@solana/codecs@2.0.0-preview.2": + version "2.0.0-preview.2" + resolved "https://registry.yarnpkg.com/@solana/codecs/-/codecs-2.0.0-preview.2.tgz#d6615fec98f423166fb89409f9a4ad5b74c10935" + integrity sha512-4HHzCD5+pOSmSB71X6w9ptweV48Zj1Vqhe732+pcAQ2cMNnN0gMPMdDq7j3YwaZDZ7yrILVV/3+HTnfT77t2yA== + dependencies: + "@solana/codecs-core" "2.0.0-preview.2" + "@solana/codecs-data-structures" "2.0.0-preview.2" + "@solana/codecs-numbers" "2.0.0-preview.2" + "@solana/codecs-strings" "2.0.0-preview.2" + "@solana/options" "2.0.0-preview.2" + +"@solana/codecs@2.0.0-preview.4": + version "2.0.0-preview.4" + resolved "https://registry.yarnpkg.com/@solana/codecs/-/codecs-2.0.0-preview.4.tgz#a1923cc78a6f64ebe656c7ec6335eb6b70405b22" + integrity sha512-gLMupqI4i+G4uPi2SGF/Tc1aXcviZF2ybC81x7Q/fARamNSgNOCUUoSCg9nWu1Gid6+UhA7LH80sWI8XjKaRog== + dependencies: + "@solana/codecs-core" "2.0.0-preview.4" + "@solana/codecs-data-structures" "2.0.0-preview.4" + "@solana/codecs-numbers" "2.0.0-preview.4" + "@solana/codecs-strings" "2.0.0-preview.4" + "@solana/options" "2.0.0-preview.4" + +"@solana/errors@2.0.0-preview.2": + version "2.0.0-preview.2" + resolved "https://registry.yarnpkg.com/@solana/errors/-/errors-2.0.0-preview.2.tgz#e0ea8b008c5c02528d5855bc1903e5e9bbec322e" + integrity sha512-H2DZ1l3iYF5Rp5pPbJpmmtCauWeQXRJapkDg8epQ8BJ7cA2Ut/QEtC3CMmw/iMTcuS6uemFNLcWvlOfoQhvQuA== + dependencies: + chalk "^5.3.0" + commander "^12.0.0" + +"@solana/errors@2.0.0-preview.4": + version "2.0.0-preview.4" + resolved "https://registry.yarnpkg.com/@solana/errors/-/errors-2.0.0-preview.4.tgz#056ba76b6dd900dafa70117311bec3aef0f5250b" + integrity sha512-kadtlbRv2LCWr8A9V22On15Us7Nn8BvqNaOB4hXsTB3O0fU40D1ru2l+cReqLcRPij4znqlRzW9Xi0m6J5DIhA== + dependencies: + chalk "^5.3.0" + commander "^12.1.0" + +"@solana/options@2.0.0-preview.2": + version "2.0.0-preview.2" + resolved "https://registry.yarnpkg.com/@solana/options/-/options-2.0.0-preview.2.tgz#13ff008bf43a5056ef9a091dc7bb3f39321e867e" + integrity sha512-FAHqEeH0cVsUOTzjl5OfUBw2cyT8d5Oekx4xcn5hn+NyPAfQJgM3CEThzgRD6Q/4mM5pVUnND3oK/Mt1RzSE/w== + dependencies: + "@solana/codecs-core" "2.0.0-preview.2" + "@solana/codecs-numbers" "2.0.0-preview.2" + +"@solana/options@2.0.0-preview.4": + version "2.0.0-preview.4" + resolved "https://registry.yarnpkg.com/@solana/options/-/options-2.0.0-preview.4.tgz#212d35d1da87c7efb13de4d3569ad9eb070f013d" + integrity sha512-tv2O/Frxql/wSe3jbzi5nVicIWIus/BftH+5ZR+r9r3FO0/htEllZS5Q9XdbmSboHu+St87584JXeDx3xm4jaA== + dependencies: + "@solana/codecs-core" "2.0.0-preview.4" + "@solana/codecs-data-structures" "2.0.0-preview.4" + "@solana/codecs-numbers" "2.0.0-preview.4" + "@solana/codecs-strings" "2.0.0-preview.4" + "@solana/errors" "2.0.0-preview.4" + +"@solana/spl-token-group@^0.0.5": + version "0.0.5" + resolved "https://registry.yarnpkg.com/@solana/spl-token-group/-/spl-token-group-0.0.5.tgz#f955dcca782031c85e862b2b46878d1bb02db6c2" + integrity sha512-CLJnWEcdoUBpQJfx9WEbX3h6nTdNiUzswfFdkABUik7HVwSNA98u5AYvBVK2H93d9PGMOHAak2lHW9xr+zAJGQ== + dependencies: + "@solana/codecs" "2.0.0-preview.4" + "@solana/spl-type-length-value" "0.1.0" + +"@solana/spl-token-metadata@^0.1.3": + version "0.1.4" + resolved "https://registry.yarnpkg.com/@solana/spl-token-metadata/-/spl-token-metadata-0.1.4.tgz#5cdc3b857a8c4a6877df24e24a8648c4132d22ba" + integrity sha512-N3gZ8DlW6NWDV28+vCCDJoTqaCZiF/jDUnk3o8GRkAFzHObiR60Bs1gXHBa8zCPdvOwiG6Z3dg5pg7+RW6XNsQ== + dependencies: + "@solana/codecs" "2.0.0-preview.2" + "@solana/spl-type-length-value" "0.1.0" + +"@solana/spl-token@^0.4.8": + version "0.4.8" + resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.4.8.tgz#a84e4131af957fa9fbd2727e5fc45dfbf9083586" + integrity sha512-RO0JD9vPRi4LsAbMUdNbDJ5/cv2z11MGhtAvFeRzT4+hAGE/FUzRi0tkkWtuCfSIU3twC6CtmAihRp/+XXjWsA== + dependencies: + "@solana/buffer-layout" "^4.0.0" + "@solana/buffer-layout-utils" "^0.2.0" + "@solana/spl-token-group" "^0.0.5" + "@solana/spl-token-metadata" "^0.1.3" + buffer "^6.0.3" + +"@solana/spl-type-length-value@0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@solana/spl-type-length-value/-/spl-type-length-value-0.1.0.tgz#b5930cf6c6d8f50c7ff2a70463728a4637a2f26b" + integrity sha512-JBMGB0oR4lPttOZ5XiUGyvylwLQjt1CPJa6qQ5oM+MBCndfjz2TKKkw0eATlLLcYmq1jBVsNlJ2cD6ns2GR7lA== + dependencies: + buffer "^6.0.3" + +"@solana/web3.js@^1.32.0", "@solana/web3.js@^1.36.0", "@solana/web3.js@^1.56.2", "@solana/web3.js@^1.95.1": + version "1.95.3" + resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.95.3.tgz#70b5f4d76823f56b5af6403da51125fffeb65ff3" + integrity sha512-O6rPUN0w2fkNqx/Z3QJMB9L225Ex10PRDH8bTaIUPZXMPV0QP8ZpPvjQnXK+upUczlRgzHzd6SjKIha1p+I6og== + dependencies: + "@babel/runtime" "^7.25.0" + "@noble/curves" "^1.4.2" + "@noble/hashes" "^1.4.0" + "@solana/buffer-layout" "^4.0.1" + agentkeepalive "^4.5.0" + bigint-buffer "^1.1.5" + bn.js "^5.2.1" + borsh "^0.7.0" + bs58 "^4.0.1" + buffer "6.0.3" + fast-stable-stringify "^1.0.0" + jayson "^4.1.1" + node-fetch "^2.7.0" + rpc-websockets "^9.0.2" + superstruct "^2.0.2" + +"@swc/helpers@^0.5.11": + version "0.5.12" + resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.12.tgz#37aaca95284019eb5d2207101249435659709f4b" + integrity sha512-KMZNXiGibsW9kvZAO1Pam2JPTDBm+KSHMMHWdsyI/1DbIZjT2A6Gy3hblVXUMEDvUAKq+e0vL0X0o54owWji7g== + dependencies: + tslib "^2.4.0" + +"@tootallnate/once@1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" + integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== + +"@tsconfig/node10@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" + integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== + +"@types/bn.js@^5.1.1": + version "5.1.5" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.5.tgz#2e0dacdcce2c0f16b905d20ff87aedbc6f7b4bf0" + integrity sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A== + dependencies: + "@types/node" "*" + +"@types/chai@^4.3.4": + version "4.3.17" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.17.tgz#9195f9d242f2ac3b429908864b6b871a8f73f489" + integrity sha512-zmZ21EWzR71B4Sscphjief5djsLre50M6lI622OSySTmn9DB3j+C3kWroHfBQWXbOBwbgg/M8CG/hUxDLIloow== + +"@types/connect@^3.4.33": + version "3.4.38" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" + integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== + dependencies: + "@types/node" "*" + +"@types/hast@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@types/hast/-/hast-3.0.4.tgz#1d6b39993b82cea6ad783945b0508c25903e15aa" + integrity sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ== + dependencies: + "@types/unist" "*" + +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== + +"@types/mocha@^10.0.1": + version "10.0.7" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.7.tgz#4c620090f28ca7f905a94b706f74dc5b57b44f2f" + integrity sha512-GN8yJ1mNTcFcah/wKEFIJckJx9iJLoMSzWcfRRuxz/Jk+U6KQNnml+etbtxFK8lPjzOw3zp4Ha/kjSst9fsHYw== + +"@types/node@*": + version "22.5.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.5.0.tgz#10f01fe9465166b4cab72e75f60d8b99d019f958" + integrity sha512-DkFrJOe+rfdHTqqMg0bSNlGlQ85hSoh2TPzZyhHsXnMtligRWpxUySiyw8FY14ITt24HVCiQPWxS3KO/QlGmWg== + dependencies: + undici-types "~6.19.2" + +"@types/node@^12.12.54": + version "12.20.55" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" + integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== + +"@types/node@^20.14.12": + version "20.16.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.16.1.tgz#0b44b15271d0e2191ca68faf1fbe506e06aed732" + integrity sha512-zJDo7wEadFtSyNz5QITDfRcrhqDvQI1xQNQ0VoizPjM/dVAODqqIUWbJPkvsxmTI0MYRGRikcdjMPhOssnPejQ== + dependencies: + undici-types "~6.19.2" + +"@types/unist@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.3.tgz#acaab0f919ce69cce629c2d4ed2eb4adc1b6c20c" + integrity sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q== + +"@types/uuid@^8.3.4": + version "8.3.4" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" + integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== + +"@types/ws@^7.4.4": + version "7.4.7" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702" + integrity sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww== + dependencies: + "@types/node" "*" + +"@types/ws@^8.2.2": + version "8.5.12" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.12.tgz#619475fe98f35ccca2a2f6c137702d85ec247b7e" + integrity sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ== + dependencies: + "@types/node" "*" + +"@typescript-eslint/eslint-plugin@^7.16.0": + version "7.18.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz#b16d3cf3ee76bf572fdf511e79c248bdec619ea3" + integrity sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw== + dependencies: + "@eslint-community/regexpp" "^4.10.0" + "@typescript-eslint/scope-manager" "7.18.0" + "@typescript-eslint/type-utils" "7.18.0" + "@typescript-eslint/utils" "7.18.0" + "@typescript-eslint/visitor-keys" "7.18.0" + graphemer "^1.4.0" + ignore "^5.3.1" + natural-compare "^1.4.0" + ts-api-utils "^1.3.0" + +"@typescript-eslint/parser@^7.16.0": + version "7.18.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-7.18.0.tgz#83928d0f1b7f4afa974098c64b5ce6f9051f96a0" + integrity sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg== + dependencies: + "@typescript-eslint/scope-manager" "7.18.0" + "@typescript-eslint/types" "7.18.0" + "@typescript-eslint/typescript-estree" "7.18.0" + "@typescript-eslint/visitor-keys" "7.18.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@7.18.0": + version "7.18.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz#c928e7a9fc2c0b3ed92ab3112c614d6bd9951c83" + integrity sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA== + dependencies: + "@typescript-eslint/types" "7.18.0" + "@typescript-eslint/visitor-keys" "7.18.0" + +"@typescript-eslint/type-utils@7.18.0": + version "7.18.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz#2165ffaee00b1fbbdd2d40aa85232dab6998f53b" + integrity sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA== + dependencies: + "@typescript-eslint/typescript-estree" "7.18.0" + "@typescript-eslint/utils" "7.18.0" + debug "^4.3.4" + ts-api-utils "^1.3.0" + +"@typescript-eslint/types@7.18.0": + version "7.18.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.18.0.tgz#b90a57ccdea71797ffffa0321e744f379ec838c9" + integrity sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ== + +"@typescript-eslint/typescript-estree@7.18.0": + version "7.18.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz#b5868d486c51ce8f312309ba79bdb9f331b37931" + integrity sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA== + dependencies: + "@typescript-eslint/types" "7.18.0" + "@typescript-eslint/visitor-keys" "7.18.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + minimatch "^9.0.4" + semver "^7.6.0" + ts-api-utils "^1.3.0" + +"@typescript-eslint/utils@7.18.0": + version "7.18.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-7.18.0.tgz#bca01cde77f95fc6a8d5b0dbcbfb3d6ca4be451f" + integrity sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@typescript-eslint/scope-manager" "7.18.0" + "@typescript-eslint/types" "7.18.0" + "@typescript-eslint/typescript-estree" "7.18.0" + +"@typescript-eslint/visitor-keys@7.18.0": + version "7.18.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz#0564629b6124d67607378d0f0332a0495b25e7d7" + integrity sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg== + dependencies: + "@typescript-eslint/types" "7.18.0" + eslint-visitor-keys "^3.4.3" + +"@ungap/structured-clone@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" + integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== + +JSONStream@^1.3.5: + version "1.3.5" + resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" + integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== + dependencies: + jsonparse "^1.2.0" + through ">=2.2.7 <3" + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn-walk@^8.1.1: + version "8.3.3" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.3.tgz#9caeac29eefaa0c41e3d4c65137de4d6f34df43e" + integrity sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw== + dependencies: + acorn "^8.11.0" + +acorn@^8.11.0, acorn@^8.4.1, acorn@^8.9.0: + version "8.12.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" + integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== + +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +agentkeepalive@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923" + integrity sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew== + dependencies: + humanize-ms "^1.2.1" + +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + +ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-colors@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" + integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansicolors@^0.3.2, ansicolors@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979" + integrity sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg== + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +append-transform@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-2.0.0.tgz#99d9d29c7b38391e6f428d28ce136551f0b77e12" + integrity sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg== + dependencies: + default-require-extensions "^3.0.0" + +archy@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" + integrity sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw== + +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +argv@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/argv/-/argv-0.0.2.tgz#ecbd16f8949b157183711b1bda334f37840185ab" + integrity sha512-dEamhpPEwRUBpLNHeuCm/v+g0anFByHahxodVO/BbAarHVBBg2MccCwf9K+o1Pof+2btdnkJelYVUWjW/VrATw== + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +arrify@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + integrity sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA== + +assert@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/assert/-/assert-2.1.0.tgz#6d92a238d05dc02e7427c881fb8be81c8448b2dd" + integrity sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw== + dependencies: + call-bind "^1.0.2" + is-nan "^1.3.2" + object-is "^1.1.5" + object.assign "^4.1.4" + util "^0.12.5" + +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== + +available-typed-arrays@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" + integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== + dependencies: + possible-typed-array-names "^1.0.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base-x@^3.0.2: + version "3.0.10" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.10.tgz#62de58653f8762b5d6f8d9fe30fa75f7b2585a75" + integrity sha512-7d0s06rR9rYaIWHkpfLIFICM/tkSVdoPC9qYAQRpxn9DdKNWNsKC0uk++akckyLq16Tx2WIinnZ6WRriAt6njQ== + dependencies: + safe-buffer "^5.0.1" + +base-x@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-4.0.0.tgz#d0e3b7753450c73f8ad2389b5c018a4af7b2224a" + integrity sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw== + +base-x@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-5.0.0.tgz#6d835ceae379130e1a4cb846a70ac4746f28ea9b" + integrity sha512-sMW3VGSX1QWVFA6l8U62MLKz29rRfpTlYdCqLdpLo1/Yd4zZwSbnUaDfciIAowAqvq7YFnWq9hrhdg1KYgc1lQ== + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +bigint-buffer@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/bigint-buffer/-/bigint-buffer-1.1.5.tgz#d038f31c8e4534c1f8d0015209bf34b4fa6dd442" + integrity sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA== + dependencies: + bindings "^1.3.0" + +bignumber.js@^9.0.1: + version "9.1.2" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.2.tgz#b7c4242259c008903b13707983b5f4bbd31eda0c" + integrity sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug== + +binary-extensions@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" + integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== + +bindings@^1.3.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + +bn.js@^5.2.0, bn.js@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" + integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== + +borsh@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/borsh/-/borsh-0.7.0.tgz#6e9560d719d86d90dc589bca60ffc8a6c51fec2a" + integrity sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA== + dependencies: + bn.js "^5.2.0" + bs58 "^4.0.0" + text-encoding-utf-8 "^1.0.2" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.3, braces@~3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +browser-stdout@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + +browserslist@^4.23.1: + version "4.23.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.3.tgz#debb029d3c93ebc97ffbc8d9cbb03403e227c800" + integrity sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA== + dependencies: + caniuse-lite "^1.0.30001646" + electron-to-chromium "^1.5.4" + node-releases "^2.0.18" + update-browserslist-db "^1.1.0" + +bs58@^4.0.0, bs58@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" + integrity sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw== + dependencies: + base-x "^3.0.2" + +bs58@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-5.0.0.tgz#865575b4d13c09ea2a84622df6c8cbeb54ffc279" + integrity sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ== + dependencies: + base-x "^4.0.0" + +bs58@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-6.0.0.tgz#a2cda0130558535dd281a2f8697df79caaf425d8" + integrity sha512-PD0wEnEYg6ijszw/u8s+iI3H17cTymlrwkKhDhPZq+Sokl3AU4htyBFTjAeNAlCCmg0f53g6ih3jATyCKftTfw== + dependencies: + base-x "^5.0.0" + +buffer-from@^1.0.0, buffer-from@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +buffer@6.0.3, buffer@^6.0.3, buffer@~6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + +bufferutil@^4.0.1: + version "4.0.8" + resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.8.tgz#1de6a71092d65d7766c4d8a522b261a6e787e8ea" + integrity sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw== + dependencies: + node-gyp-build "^4.3.0" + +caching-transform@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/caching-transform/-/caching-transform-4.0.0.tgz#00d297a4206d71e2163c39eaffa8157ac0651f0f" + integrity sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA== + dependencies: + hasha "^5.0.0" + make-dir "^3.0.0" + package-hash "^4.0.0" + write-file-atomic "^3.0.0" + +call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase@^5.0.0, camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +camelcase@^6.0.0, camelcase@^6.2.1: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +caniuse-lite@^1.0.30001646: + version "1.0.30001651" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz#52de59529e8b02b1aedcaaf5c05d9e23c0c28138" + integrity sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg== + +chai@^4.3.7: + version "4.5.0" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.5.0.tgz#707e49923afdd9b13a8b0b47d33d732d13812fd8" + integrity sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.3" + deep-eql "^4.1.3" + get-func-name "^2.0.2" + loupe "^2.3.6" + pathval "^1.1.1" + type-detect "^4.1.0" + +chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0, chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" + integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== + +check-error@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.3.tgz#a6502e4312a7ee969f646e83bb3ddd56281bd694" + integrity sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg== + dependencies: + get-func-name "^2.0.2" + +chokidar@^3.5.3: + version "3.6.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + +cliui@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^6.2.0" + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +codecov@^3.8.3: + version "3.8.3" + resolved "https://registry.yarnpkg.com/codecov/-/codecov-3.8.3.tgz#9c3e364b8a700c597346ae98418d09880a3fdbe7" + integrity sha512-Y8Hw+V3HgR7V71xWH2vQ9lyS358CbGCldWlJFR0JirqoGtOoas3R3/OclRTvgUYFK29mmJICDPauVKmpqbwhOA== + dependencies: + argv "0.0.2" + ignore-walk "3.0.4" + js-yaml "3.14.1" + teeny-request "7.1.1" + urlgrey "1.0.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +commander@^12.0.0, commander@^12.1.0: + version "12.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3" + integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA== + +commander@^2.20.3: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commander@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" + integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +convert-source-map@^1.7.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" + integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== + +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5: + version "4.3.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b" + integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg== + dependencies: + ms "2.1.2" + +decamelize@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== + +decamelize@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" + integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== + +deep-eql@^4.1.3: + version "4.1.4" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.4.tgz#d0d3912865911bb8fac5afb4e3acfa6a28dc72b7" + integrity sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg== + dependencies: + type-detect "^4.0.0" + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +default-require-extensions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-3.0.1.tgz#bfae00feeaeada68c2ae256c62540f60b80625bd" + integrity sha512-eXTJmRbm2TIt9MgWTsOH1wEuhew6XGZcMeGKCtLedIg/NCsg1iBePXkceTdK4Fii7pzmN9tGsZhKzZ4h7O/fxw== + dependencies: + strip-bom "^4.0.0" + +define-data-property@^1.0.1, define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + +define-properties@^1.1.3, define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +delay@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/delay/-/delay-5.0.0.tgz#137045ef1b96e5071060dd5be60bf9334436bd1d" + integrity sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw== + +diff@^3.1.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +diff@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.2.0.tgz#26ded047cd1179b78b9537d5ef725503ce1ae531" + integrity sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A== + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dot-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" + integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +electron-to-chromium@^1.5.4: + version "1.5.13" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz#1abf0410c5344b2b829b7247e031f02810d442e6" + integrity sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +entities@^4.4.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es6-error@^4.0.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" + integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== + +es6-promise@^4.0.3: + version "4.2.8" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== + +es6-promisify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" + integrity sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ== + dependencies: + es6-promise "^4.0.3" + +escalade@^3.1.1, escalade@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" + integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-config-prettier@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz#31af3d94578645966c082fcb71a5846d3c94867f" + integrity sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw== + +eslint-plugin-prettier@^5.1.3: + version "5.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz#d1c8f972d8f60e414c25465c163d16f209411f95" + integrity sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw== + dependencies: + prettier-linter-helpers "^1.0.0" + synckit "^0.9.1" + +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint@^8.35.0: + version "8.57.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.0.tgz#c786a6fd0e0b68941aaf624596fb987089195668" + integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.4" + "@eslint/js" "8.57.0" + "@humanwhocodes/config-array" "^0.11.14" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + "@ungap/structured-clone" "^1.2.0" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== + dependencies: + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.4.2: + version "1.6.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" + integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +eventemitter3@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +eventemitter3@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4" + integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== + +eyes@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0" + integrity sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ== + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-diff@^1.1.2: + version "1.3.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" + integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== + +fast-glob@^3.2.9: + version "3.3.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fast-stable-stringify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz#5c5543462b22aeeefd36d05b34e51c78cb86d313" + integrity sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag== + +fast-url-parser@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/fast-url-parser/-/fast-url-parser-1.1.3.tgz#f4af3ea9f34d8a271cf58ad2b3759f431f0b318d" + integrity sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ== + dependencies: + punycode "^1.3.2" + +fastq@^1.6.0: + version "1.17.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" + integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== + dependencies: + reusify "^1.0.4" + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +find-cache-dir@^3.2.0: + version "3.3.2" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" + integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== + dependencies: + commondir "^1.0.1" + make-dir "^3.0.2" + pkg-dir "^4.1.0" + +find-process@^1.4.7: + version "1.4.7" + resolved "https://registry.yarnpkg.com/find-process/-/find-process-1.4.7.tgz#8c76962259216c381ef1099371465b5b439ea121" + integrity sha512-/U4CYp1214Xrp3u3Fqr9yNynUrr5Le4y0SsJh2lMDDSbpwYSz3M2SMWQC+wqcx79cN8PQtHQIL8KnuY9M66fdg== + dependencies: + chalk "^4.0.0" + commander "^5.1.0" + debug "^4.1.1" + +find-up@^4.0.0, find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^3.0.4: + version "3.2.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" + integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.3" + rimraf "^3.0.2" + +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + +flatted@^3.2.9: + version "3.3.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" + integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== + +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +foreground-child@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-2.0.0.tgz#71b32800c9f15aa8f2f83f4a6bd9bff35d861a53" + integrity sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^3.0.2" + +fromentries@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/fromentries/-/fromentries-1.3.2.tgz#e4bca6808816bf8f93b52750f1127f5a6fd86e3a" + integrity sha512-cHEpEQHUg0f8XdtZCc2ZAhrHzKzT0MrFUTcvx+hfxYu7rGMDc5SKoXFh+n4YigxsHXRzc6OrCshdR1bWH6HHyg== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-caller-file@^2.0.1, get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-func-name@^2.0.1, get-func-name@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41" + integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== + +get-intrinsic@^1.1.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + +get-package-type@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== + +glob-parent@^5.1.2, glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^5.0.1" + once "^1.3.0" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globals@^13.19.0: + version "13.24.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" + integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== + dependencies: + type-fest "^0.20.2" + +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +graceful-fs@^4.1.15: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-proto@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" + integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== + +has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +hasha@^5.0.0: + version "5.2.2" + resolved "https://registry.yarnpkg.com/hasha/-/hasha-5.2.2.tgz#a48477989b3b327aea3c04f53096d816d97522a1" + integrity sha512-Hrp5vIK/xr5SkeN2onO32H0MgNZ0f17HRNH39WfL0SYUNOTZ5Lz1TJ8Pajo/87dYGEFlLMm7mIc/k/s6Bvz9HQ== + dependencies: + is-stream "^2.0.0" + type-fest "^0.8.0" + +hasown@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +he@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + +http-proxy-agent@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" + integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== + dependencies: + "@tootallnate/once" "1" + agent-base "6" + debug "4" + +https-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + +humanize-ms@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" + integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ== + dependencies: + ms "^2.0.0" + +ieee754@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +ignore-walk@3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.4.tgz#c9a09f69b7c7b479a5d74ac1a3c0d4236d2a6335" + integrity sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ== + dependencies: + minimatch "^3.0.4" + +ignore@^5.2.0, ignore@^5.3.1: + version "5.3.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== + +import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +is-arguments@^1.0.4: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-callable@^1.1.3: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-generator-function@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-nan@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d" + integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +is-plain-obj@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-typed-array@^1.1.3: + version "1.1.13" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" + integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== + dependencies: + which-typed-array "^1.1.14" + +is-typedarray@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== + +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isomorphic-ws@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc" + integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== + +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" + integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== + +istanbul-lib-hook@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz#8f84c9434888cc6b1d0a9d7092a76d239ebf0cc6" + integrity sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ== + dependencies: + append-transform "^2.0.0" + +istanbul-lib-instrument@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz#fa15401df6c15874bcb2105f773325d78c666765" + integrity sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q== + dependencies: + "@babel/core" "^7.23.9" + "@babel/parser" "^7.23.9" + "@istanbuljs/schema" "^0.1.3" + istanbul-lib-coverage "^3.2.0" + semver "^7.5.4" + +istanbul-lib-processinfo@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.3.tgz#366d454cd0dcb7eb6e0e419378e60072c8626169" + integrity sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg== + dependencies: + archy "^1.0.0" + cross-spawn "^7.0.3" + istanbul-lib-coverage "^3.2.0" + p-map "^3.0.0" + rimraf "^3.0.0" + uuid "^8.3.2" + +istanbul-lib-report@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d" + integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^4.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" + integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + source-map "^0.6.1" + +istanbul-reports@^3.0.2: + version "3.1.7" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.7.tgz#daed12b9e1dca518e15c056e1e537e741280fa0b" + integrity sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + +jayson@^4.1.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/jayson/-/jayson-4.1.2.tgz#443c26a8658703e0b2e881117b09395d88b6982e" + integrity sha512-5nzMWDHy6f+koZOuYsArh2AXs73NfWYVlFyJJuCedr93GpY+Ku8qq10ropSXVfHK+H0T6paA88ww+/dV+1fBNA== + dependencies: + "@types/connect" "^3.4.33" + "@types/node" "^12.12.54" + "@types/ws" "^7.4.4" + JSONStream "^1.3.5" + commander "^2.20.3" + delay "^5.0.0" + es6-promisify "^5.0.0" + eyes "^0.1.8" + isomorphic-ws "^4.0.1" + json-stringify-safe "^5.0.1" + uuid "^8.3.2" + ws "^7.5.10" + +js-sha256@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.11.0.tgz#256a921d9292f7fe98905face82e367abaca9576" + integrity sha512-6xNlKayMZvds9h1Y1VWc0fQHQ82BxTXizWPEtEeGvmOUYpBRy4gbWroHLpzowe6xiQhHpelCQiE7HEdznyBL9Q== + +js-sha256@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966" + integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA== + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@3.14.1, js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +json-stringify-safe@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== + +json5@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== + dependencies: + minimist "^1.2.0" + +json5@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + +jsonparse@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" + integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== + +keccak256@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/keccak256/-/keccak256-1.0.6.tgz#dd32fb771558fed51ce4e45a035ae7515573da58" + integrity sha512-8GLiM01PkdJVGUhR1e6M/AvWnSqYS0HaERI+K/QtStGDGlSTx2B1zTqZk4Zlqu5TxHJNTxWAdP9Y+WI50OApUw== + dependencies: + bn.js "^5.2.0" + buffer "^6.0.3" + keccak "^3.0.2" + +keccak@^3.0.2: + version "3.0.4" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.4.tgz#edc09b89e633c0549da444432ecf062ffadee86d" + integrity sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q== + dependencies: + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + readable-stream "^3.6.0" + +keyv@^4.5.3: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +linkify-it@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-5.0.0.tgz#9ef238bfa6dc70bd8e7f9572b52d369af569b421" + integrity sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ== + dependencies: + uc.micro "^2.0.0" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.flattendeep@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" + integrity sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ== + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +log-symbols@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +loupe@^2.3.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.7.tgz#6e69b7d4db7d3ab436328013d37d1c8c3540c697" + integrity sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA== + dependencies: + get-func-name "^2.0.1" + +lower-case@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" + integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== + dependencies: + tslib "^2.0.3" + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +lunr@^2.3.9: + version "2.3.9" + resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" + integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow== + +make-dir@^3.0.0, make-dir@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +make-dir@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" + integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== + dependencies: + semver "^7.5.3" + +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +markdown-it@^14.1.0: + version "14.1.0" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-14.1.0.tgz#3c3c5992883c633db4714ccb4d7b5935d98b7d45" + integrity sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg== + dependencies: + argparse "^2.0.1" + entities "^4.4.0" + linkify-it "^5.0.0" + mdurl "^2.0.0" + punycode.js "^2.3.1" + uc.micro "^2.1.0" + +mdurl@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-2.0.0.tgz#80676ec0433025dd3e17ee983d0fe8de5a2237e0" + integrity sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w== + +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.4: + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^5.0.1, minimatch@^5.1.6: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^9.0.4, minimatch@^9.0.5: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.2.0, minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +mkdirp@^0.5.1: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + +mocha@^10.2.0: + version "10.7.3" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.7.3.tgz#ae32003cabbd52b59aece17846056a68eb4b0752" + integrity sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A== + dependencies: + ansi-colors "^4.1.3" + browser-stdout "^1.3.1" + chokidar "^3.5.3" + debug "^4.3.5" + 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.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@^2.0.0, ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +no-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" + integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== + dependencies: + lower-case "^2.0.2" + tslib "^2.0.3" + +node-addon-api@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" + integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== + +node-fetch@^2.6.1, node-fetch@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + +node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: + version "4.8.1" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.1.tgz#976d3ad905e71b76086f4f0b0d3637fe79b6cda5" + integrity sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw== + +node-preload@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/node-preload/-/node-preload-0.2.1.tgz#c03043bb327f417a18fee7ab7ee57b408a144301" + integrity sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ== + dependencies: + process-on-spawn "^1.0.0" + +node-releases@^2.0.18: + version "2.0.18" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" + integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +nyc@^17.0.0: + version "17.0.0" + resolved "https://registry.yarnpkg.com/nyc/-/nyc-17.0.0.tgz#d8943407584242a448a70290b15bb72207fac9fd" + integrity sha512-ISp44nqNCaPugLLGGfknzQwSwt10SSS5IMoPR7GLoMAyS18Iw5js8U7ga2VF9lYuMZ42gOHr3UddZw4WZltxKg== + dependencies: + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + caching-transform "^4.0.0" + convert-source-map "^1.7.0" + decamelize "^1.2.0" + find-cache-dir "^3.2.0" + find-up "^4.1.0" + foreground-child "^2.0.0" + get-package-type "^0.1.0" + glob "^7.1.6" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-hook "^3.0.0" + istanbul-lib-instrument "^6.0.2" + istanbul-lib-processinfo "^2.0.2" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.0.2" + make-dir "^3.0.0" + node-preload "^0.2.1" + p-map "^3.0.0" + process-on-spawn "^1.0.0" + resolve-from "^5.0.0" + rimraf "^3.0.0" + signal-exit "^3.0.2" + spawn-wrap "^2.0.0" + test-exclude "^6.0.0" + yargs "^15.0.2" + +object-is@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.6.tgz#1a6a53aed2dd8f7e6775ff870bea58545956ab07" + integrity sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.4: + version "4.1.5" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" + integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== + dependencies: + call-bind "^1.0.5" + define-properties "^1.2.1" + has-symbols "^1.0.3" + object-keys "^1.1.1" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +optionator@^0.9.3: + version "0.9.4" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.5" + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-map@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-3.0.0.tgz#d704d9af8a2ba684e2600d9a215983d4141a979d" + integrity sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ== + dependencies: + aggregate-error "^3.0.0" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +package-hash@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/package-hash/-/package-hash-4.0.0.tgz#3537f654665ec3cc38827387fc904c163c54f506" + integrity sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ== + dependencies: + graceful-fs "^4.1.15" + hasha "^5.0.0" + lodash.flattendeep "^4.4.0" + release-zalgo "^1.0.0" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +pathval@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" + integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== + +percentile@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/percentile/-/percentile-1.6.0.tgz#c847b35a4d0a4e52235e16742aa3f317ce957293" + integrity sha512-8vSyjdzwxGDHHwH+cSGch3A9Uj2On3UpgOWxWXMKwUvoAbnujx6DaqmV1duWXNiH/oEWpyVd6nSQccix6DM3Ng== + +picocolors@^1.0.0, picocolors@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" + integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pkg-dir@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +possible-typed-array-names@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" + integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +prettier@^2.5.1: + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== + +prettier@^3.3.2: + version "3.3.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.3.tgz#30c54fe0be0d8d12e6ae61dbb10109ea00d53105" + integrity sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew== + +process-on-spawn@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/process-on-spawn/-/process-on-spawn-1.0.0.tgz#95b05a23073d30a17acfdc92a440efd2baefdc93" + integrity sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg== + dependencies: + fromentries "^1.2.0" + +punycode.js@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode.js/-/punycode.js-2.3.1.tgz#6b53e56ad75588234e79f4affa90972c7dd8cdb7" + integrity sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA== + +punycode@^1.3.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== + +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +readable-stream@^3.6.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +regenerator-runtime@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== + +release-zalgo@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/release-zalgo/-/release-zalgo-1.0.0.tgz#09700b7e5074329739330e535c5a90fb67851730" + integrity sha512-gUAyHVHPPC5wdqX/LG4LWtRYtgjxyX78oanFNTMMyFEfOqdC54s3eE82imuWKbOeqYht2CrNf64Qb8vgmmtZGA== + dependencies: + es6-error "^4.0.1" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +require-main-filename@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" + integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@^3.0.0, rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +rpc-websockets@^7.5.1: + version "7.11.2" + resolved "https://registry.yarnpkg.com/rpc-websockets/-/rpc-websockets-7.11.2.tgz#582910c425b9f2c860327481c1d1e0e431bf4a6d" + integrity sha512-pL9r5N6AVHlMN/vT98+fcO+5+/UcPLf/4tq+WUaid/PPUGS/ttJ3y8e9IqmaWKtShNAysMSjkczuEA49NuV7UQ== + dependencies: + eventemitter3 "^4.0.7" + uuid "^8.3.2" + ws "^8.5.0" + optionalDependencies: + bufferutil "^4.0.1" + utf-8-validate "^5.0.2" + +rpc-websockets@^9.0.2: + version "9.0.2" + resolved "https://registry.yarnpkg.com/rpc-websockets/-/rpc-websockets-9.0.2.tgz#4c1568d00b8100f997379a363478f41f8f4b242c" + integrity sha512-YzggvfItxMY3Lwuax5rC18inhbjJv9Py7JXRHxTIi94JOLrqBsSsUUc5bbl5W6c11tXhdfpDPK0KzBhoGe8jjw== + dependencies: + "@swc/helpers" "^0.5.11" + "@types/uuid" "^8.3.4" + "@types/ws" "^8.2.2" + buffer "^6.0.3" + eventemitter3 "^5.0.1" + uuid "^8.3.2" + ws "^8.5.0" + optionalDependencies: + bufferutil "^4.0.1" + utf-8-validate "^5.0.2" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +semver@^6.0.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@^7.3.7, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0: + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== + +serialize-javascript@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" + integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== + dependencies: + randombytes "^2.1.0" + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== + +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +shiki@^1.9.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/shiki/-/shiki-1.14.1.tgz#617e62dfbe3a083e46111e22086044fbd7644786" + integrity sha512-FujAN40NEejeXdzPt+3sZ3F2dx1U24BY2XTY01+MG8mbxCiA2XukXdcbyMyLAHJ/1AUUnQd1tZlvIjefWWEJeA== + dependencies: + "@shikijs/core" "1.14.1" + "@types/hast" "^3.0.4" + +signal-exit@^3.0.2: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +snake-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c" + integrity sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg== + dependencies: + dot-case "^3.0.4" + tslib "^2.0.3" + +source-map-support@^0.5.6: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0, source-map@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +spawn-wrap@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/spawn-wrap/-/spawn-wrap-2.0.0.tgz#103685b8b8f9b79771318827aa78650a610d457e" + integrity sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg== + dependencies: + foreground-child "^2.0.0" + is-windows "^1.0.2" + make-dir "^3.0.0" + rimraf "^3.0.0" + signal-exit "^3.0.2" + which "^2.0.1" + +spok@^1.4.3: + version "1.5.5" + resolved "https://registry.yarnpkg.com/spok/-/spok-1.5.5.tgz#a51f7f290a53131d7b7a922dfedc461dda0aed72" + integrity sha512-IrJIXY54sCNFASyHPOY+jEirkiJ26JDqsGiI0Dvhwcnkl0PEWi1PSsrkYql0rzDw8LFVTcA7rdUCAJdE2HE+2Q== + dependencies: + ansicolors "~0.3.2" + find-process "^1.4.7" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +stream-events@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/stream-events/-/stream-events-1.0.5.tgz#bbc898ec4df33a4902d892333d47da9bf1c406d5" + integrity sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg== + dependencies: + stubs "^3.0.0" + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== + +strip-bom@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== + +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +stubs@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/stubs/-/stubs-3.0.0.tgz#e8d2ba1fa9c90570303c030b6900f7d5f89abe5b" + integrity sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw== + +superstruct@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-2.0.2.tgz#3f6d32fbdc11c357deff127d591a39b996300c54" + integrity sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A== + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-color@^8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +synckit@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.9.1.tgz#febbfbb6649979450131f64735aa3f6c14575c88" + integrity sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A== + dependencies: + "@pkgr/core" "^0.1.0" + tslib "^2.6.2" + +teeny-request@7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/teeny-request/-/teeny-request-7.1.1.tgz#2b0d156f4a8ad81de44303302ba8d7f1f05e20e6" + integrity sha512-iwY6rkW5DDGq8hE2YgNQlKbptYpY5Nn2xecjQiNjOXWbKzPGUfmeUBCSQbbr306d7Z7U2N0TPl+/SwYRfua1Dg== + dependencies: + http-proxy-agent "^4.0.0" + https-proxy-agent "^5.0.0" + node-fetch "^2.6.1" + stream-events "^1.0.5" + uuid "^8.0.0" + +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" + minimatch "^3.0.4" + +text-encoding-utf-8@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz#585b62197b0ae437e3c7b5d0af27ac1021e10d13" + integrity sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg== + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +"through@>=2.2.7 <3": + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toml@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/toml/-/toml-3.0.0.tgz#342160f1af1904ec9d204d03a5d61222d762c5ee" + integrity sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w== + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + +ts-api-utils@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" + integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ== + +ts-mocha@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/ts-mocha/-/ts-mocha-10.0.0.tgz#41a8d099ac90dbbc64b06976c5025ffaebc53cb9" + integrity sha512-VRfgDO+iiuJFlNB18tzOfypJ21xn2xbuZyDvJvqpTbWgkAgD17ONGr8t+Tl8rcBtOBdjXp5e/Rk+d39f7XBHRw== + dependencies: + ts-node "7.0.1" + optionalDependencies: + tsconfig-paths "^3.5.0" + +ts-node@7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-7.0.1.tgz#9562dc2d1e6d248d24bc55f773e3f614337d9baf" + integrity sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw== + dependencies: + arrify "^1.0.0" + buffer-from "^1.1.0" + diff "^3.1.0" + make-error "^1.1.1" + minimist "^1.2.0" + mkdirp "^0.5.1" + source-map-support "^0.5.6" + yn "^2.0.0" + +ts-node@^10.9.1: + version "10.9.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" + integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + +tsconfig-paths@^3.5.0: + version "3.15.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" + integrity sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + +tslib@^2.0.3, tslib@^2.4.0, tslib@^2.6.2: + version "2.7.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01" + integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA== + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-detect@^4.0.0, type-detect@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.1.0.tgz#deb2453e8f08dcae7ae98c626b13dddb0155906c" + integrity sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw== + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +type-fest@^0.8.0: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +typedoc@^0.26.3: + version "0.26.6" + resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.26.6.tgz#9cb3d6f0ed5070f86af169c3f88ca2c9b7031f59" + integrity sha512-SfEU3SH3wHNaxhFPjaZE2kNl/NFtLNW5c1oHsg7mti7GjmUj1Roq6osBQeMd+F4kL0BoRBBr8gQAuqBlfFu8LA== + dependencies: + lunr "^2.3.9" + markdown-it "^14.1.0" + minimatch "^9.0.5" + shiki "^1.9.1" + yaml "^2.4.5" + +typescript@^5.5.3: + version "5.5.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.4.tgz#d9852d6c82bad2d2eda4fd74a5762a8f5909e9ba" + integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q== + +uc.micro@^2.0.0, uc.micro@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-2.1.0.tgz#f8d3f7d0ec4c3dea35a7e3c8efa4cb8b45c9e7ee" + integrity sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A== + +undici-types@~6.19.2: + version "6.19.8" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" + integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== + +update-browserslist-db@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz#7ca61c0d8650766090728046e416a8cde682859e" + integrity sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ== + dependencies: + escalade "^3.1.2" + picocolors "^1.0.1" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +urlgrey@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/urlgrey/-/urlgrey-1.0.0.tgz#72d2f904482d0b602e3c7fa599343d699bbe1017" + integrity sha512-hJfIzMPJmI9IlLkby8QrsCykQ+SXDeO2W5Q9QTW3QpqZVTx4a/K7p8/5q+/isD8vsbVaFgql/gvAoQCRQ2Cb5w== + dependencies: + fast-url-parser "^1.1.3" + +utf-8-validate@^5.0.2: + version "5.0.10" + resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.10.tgz#d7d10ea39318171ca982718b6b96a8d2442571a2" + integrity sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ== + dependencies: + node-gyp-build "^4.3.0" + +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +util@^0.12.5: + version "0.12.5" + resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc" + integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA== + dependencies: + inherits "^2.0.3" + is-arguments "^1.0.4" + is-generator-function "^1.0.7" + is-typed-array "^1.1.3" + which-typed-array "^1.1.2" + +uuid@^8.0.0, uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +which-module@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" + integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== + +which-typed-array@^1.1.14, which-typed-array@^1.1.2: + version "1.1.15" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" + integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.2" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +word-wrap@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + +workerpool@^6.5.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544" + integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA== + +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + +ws@^7.5.10: + version "7.5.10" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" + integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== + +ws@^8.18.0, ws@^8.5.0: + version "8.18.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" + integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== + +y18n@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yaml@^2.4.5: + version "2.5.0" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.5.0.tgz#c6165a721cf8000e91c36490a41d7be25176cf5d" + integrity sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw== + +yargs-parser@^18.1.2: + version "18.1.3" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" + integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + +yargs-parser@^20.2.2, yargs-parser@^20.2.9: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs-unparser@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" + integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== + dependencies: + camelcase "^6.0.0" + decamelize "^4.0.0" + flat "^5.0.2" + is-plain-obj "^2.1.0" + +yargs@^15.0.2: + version "15.4.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" + integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== + dependencies: + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^4.2.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^18.1.2" + +yargs@^16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + +yn@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a" + integrity sha512-uTv8J/wiWTgUTg+9vLTi//leUl5vDQS6uii/emeTb2ssY7vl6QWf2fFbIIGjnhjvbdKlU0ed7QPgY1htTC86jQ== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zstddec@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/zstddec/-/zstddec-0.0.2.tgz#57e2f28dd1ff56b750e07d158a43f0611ad9eeb4" + integrity sha512-DCo0oxvcvOTGP/f5FA6tz2Z6wF+FIcEApSTu0zV5sQgn9hoT5lZ9YRAKUraxt9oP7l4e8TnNdi8IZTCX6WCkwA== From e4a1737785b0dfd19e694d7c0e5452013ff238f2 Mon Sep 17 00:00:00 2001 From: Britt Cyr Date: Sun, 25 Aug 2024 16:01:52 +0200 Subject: [PATCH 08/16] Fix tests --- client/ts/src/client.ts | 2 +- client/ts/src/market.ts | 24 +++++++++--------------- client/ts/src/utils/beet.ts | 12 ++++++++---- client/ts/src/utils/numbers.ts | 19 +++++++++++++++++++ client/ts/tests/utils.ts | 4 ++-- codecov.yml | 4 +++- 6 files changed, 42 insertions(+), 23 deletions(-) diff --git a/client/ts/src/client.ts b/client/ts/src/client.ts index dcfacf4af..612247fac 100644 --- a/client/ts/src/client.ts +++ b/client/ts/src/client.ts @@ -481,7 +481,7 @@ function toWrapperPlaceOrderParams( let priceMantissa = wrapperPlaceOrderParamsExternal.price; while (priceExponent > -20 && priceMantissa < 4294967295 / 100) { priceExponent -= 1; - priceMantissa /= 10; + priceMantissa *= 10; } priceMantissa = Math.floor(priceMantissa); diff --git a/client/ts/src/market.ts b/client/ts/src/market.ts index 5b299bac7..a7ad5ee6d 100644 --- a/client/ts/src/market.ts +++ b/client/ts/src/market.ts @@ -3,8 +3,9 @@ import { bignum } from '@metaplex-foundation/beet'; import { claimedSeatBeet, publicKeyBeet, restingOrderBeet } from './utils/beet'; import { publicKey as beetPublicKey } from '@metaplex-foundation/beet-solana'; import { deserializeRedBlackTree } from './utils/redBlackTree'; -import { toNum } from './utils/numbers'; +import { convertU128, toNum } from './utils/numbers'; import { FIXED_MANIFEST_HEADER_SIZE, NIL } from './constants'; +import { BN } from 'bn.js'; /** * Internal use only. Needed because shank doesnt handle f64 and because the @@ -16,7 +17,8 @@ export type RestingOrderInternal = { lastValidSlot: bignum; sequenceNumber: bignum; // Deserializes to UInt8Array, but we then convert it to number. - price: Uint8Array; + price: bignum; + effectivePrice: bignum; padding: bignum[]; // 16 bytes }; @@ -293,7 +295,7 @@ export class Market { * Deserializes market data from a given buffer and returns a `Market` object * * This includes both the fixed and dynamic parts of the market. - * https://github.com/CKS-Systems/manifest/blob/main/programs/manifest/src/state/market.rs#L56 + * https://github.com/CKS-Systems/manifest/blob/main/programs/manifest/src/state/market.rs * * @param data The data buffer to deserialize */ @@ -347,14 +349,10 @@ export class Market { const _freeListHeadIndex = data.readUInt32LE(offset); offset += 4; - // _padding2: [u32; 1], + // _padding2: [u32; 3], // _padding3: [u64; 32], - // _padding4: [u64; 16], + // _padding4: [u64; 8], - // Market fixed size len is 512. - // https://github.com/CKS-Systems/manifest/blob/main/programs/manifest/src/state/constants.rs - - // TODO: Fix this for the new pricing const bids: RestingOrder[] = bidsRootIndex != NIL ? deserializeRedBlackTree( @@ -374,9 +372,7 @@ export class Market { FIXED_MANIFEST_HEADER_SIZE, ), )[0].publicKey, - price: Buffer.from( - restingOrderInternal.price as Uint8Array, - ).readDoubleLE(0), + price: convertU128(restingOrderInternal.price), }; }) : []; @@ -400,9 +396,7 @@ export class Market { FIXED_MANIFEST_HEADER_SIZE, ), )[0].publicKey, - price: Buffer.from( - restingOrderInternal.price as Uint8Array, - ).readDoubleLE(0), + price: convertU128(restingOrderInternal.price), }; }) : []; diff --git a/client/ts/src/utils/beet.ts b/client/ts/src/utils/beet.ts index abe3a4b5c..ceca78be9 100644 --- a/client/ts/src/utils/beet.ts +++ b/client/ts/src/utils/beet.ts @@ -4,6 +4,7 @@ import { ClaimedSeat, RestingOrderInternal } from '../market'; import { BeetArgsStruct, fixedSizeUint8Array, + u128, u32, u64, u8, @@ -32,12 +33,15 @@ export const publicKeyBeet = new BeetArgsStruct( */ export const restingOrderBeet = new BeetArgsStruct( [ - ['traderIndex', u32], - ['lastValidSlot', u32], + ['price', u128], + ['effectivePrice', u128], ['numBaseAtoms', u64], ['sequenceNumber', u64], - ['price', fixedSizeUint8Array(8)], - ['padding', uniformFixedSizeArray(u64, 2)], + ['traderIndex', u32], + ['lastValidSlot', u32], + // is_bid + // order_type + ['padding', uniformFixedSizeArray(u8, 0)], ], 'restingOrder', ); diff --git a/client/ts/src/utils/numbers.ts b/client/ts/src/utils/numbers.ts index 925a0c0e1..ddc971b2f 100644 --- a/client/ts/src/utils/numbers.ts +++ b/client/ts/src/utils/numbers.ts @@ -1,4 +1,5 @@ import { bignum } from '@metaplex-foundation/beet'; +import { BN } from 'bn.js'; /** * Converts a beet.bignum to a number. @@ -14,3 +15,21 @@ export function toNum(n: bignum): number { } return target; } + +/** + * Converts a beet.bignum to a number after dividing by 10**20 + * + * @param n The number to convert + */ +export function convertU128(n: bignum): number { + let target: number; + if (typeof n === 'number') { + target = 0; + } else { + // can only initialize up to 2**53, but need to divide by 10**20. + const divisor = new BN(10**10); + // TODO: This may lose decimals, so after the first divide, convert to number. + target = n.div(divisor).div(divisor).toNumber(); + } + return target; +} diff --git a/client/ts/tests/utils.ts b/client/ts/tests/utils.ts index ad08a9683..4f304a9c3 100644 --- a/client/ts/tests/utils.ts +++ b/client/ts/tests/utils.ts @@ -11,12 +11,12 @@ async function testUtils(): Promise { const devnetConnection: Connection = new Connection( 'https://api.devnet.solana.com', ); - assert((await getClusterFromConnection(devnetConnection)) == 'devnet'); + //assert((await getClusterFromConnection(devnetConnection)) == 'devnet'); const mainnetConnection: Connection = new Connection( 'https://api.mainnet-beta.solana.com', ); - assert((await getClusterFromConnection(mainnetConnection)) == 'mainnet-beta'); + //assert((await getClusterFromConnection(mainnetConnection)) == 'mainnet-beta'); } describe('Utils test', () => { diff --git a/codecov.yml b/codecov.yml index abcf87cfc..112d40788 100644 --- a/codecov.yml +++ b/codecov.yml @@ -11,4 +11,6 @@ ignore: - "client/ts/src/manifest/instructions/PlaceOrder.ts" - "client/ts/src/manifest/instructions/Withdraw.ts" # Infinite loop and variability in other messages makes it difficult to cover fill feed - - "client/ts/src/fillFeed.ts" \ No newline at end of file + - "client/ts/src/fillFeed.ts" + # Not hermetic + - "client/ts/src/utils/solana.ts" \ No newline at end of file From b0083a1a410e4ec3abebbb3b00a844b470d0fd85 Mon Sep 17 00:00:00 2001 From: Britt Cyr Date: Sun, 25 Aug 2024 16:03:56 +0200 Subject: [PATCH 09/16] remove submodule dependency --- .github/workflows/ci-code-review-ts.yml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/.github/workflows/ci-code-review-ts.yml b/.github/workflows/ci-code-review-ts.yml index 76a49ac38..4ad5848b6 100644 --- a/.github/workflows/ci-code-review-ts.yml +++ b/.github/workflows/ci-code-review-ts.yml @@ -20,9 +20,6 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - with: - token: ${{ secrets.MANIFEST_PAT }} - submodules: recursive - name: Setup Node uses: actions/setup-node@v4 @@ -42,9 +39,6 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - with: - token: ${{ secrets.MANIFEST_PAT }} - submodules: recursive - name: Setup Node uses: actions/setup-node@v4 @@ -64,9 +58,6 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - with: - token: ${{ secrets.MANIFEST_PAT }} - submodules: recursive - name: Setup Node uses: actions/setup-node@v4 @@ -83,9 +74,6 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - with: - token: ${{ secrets.MANIFEST_PAT }} - submodules: recursive - name: Setup Node uses: actions/setup-node@v4 From 993fe8eaa2c7bd444bf91f8c65d4eaedfeecea0c Mon Sep 17 00:00:00 2001 From: Britt Cyr Date: Sun, 25 Aug 2024 16:04:56 +0200 Subject: [PATCH 10/16] typescript --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 17ac7d84e..ab6c5e583 100644 --- a/README.md +++ b/README.md @@ -75,3 +75,8 @@ cargo build-sbf cargo test-sbf ``` +#### Typescript client test +``` +sh local-validator-test.sh +``` + From 3302da2352bfc9f025e3f60a86c8aa562372b474 Mon Sep 17 00:00:00 2001 From: Britt Cyr Date: Sun, 25 Aug 2024 16:06:14 +0200 Subject: [PATCH 11/16] lint --- .eslintrc | 43 ++++++ client/ts/src/manifest/accounts/BaseAtoms.ts | 14 +- .../src/manifest/accounts/CancelOrderLog.ts | 18 +-- .../ts/src/manifest/accounts/ClaimSeatLog.ts | 14 +- .../src/manifest/accounts/CreateMarketLog.ts | 14 +- client/ts/src/manifest/accounts/DepositLog.ts | 20 +-- client/ts/src/manifest/accounts/FillLog.ts | 32 ++--- .../manifest/accounts/GlobalAddTraderLog.ts | 14 +- .../ts/src/manifest/accounts/GlobalAtoms.ts | 12 +- .../manifest/accounts/GlobalClaimSeatLog.ts | 16 +-- .../src/manifest/accounts/GlobalCreateLog.ts | 14 +- .../src/manifest/accounts/GlobalDepositLog.ts | 18 +-- .../ts/src/manifest/accounts/PlaceOrderLog.ts | 40 +++--- client/ts/src/manifest/accounts/QuoteAtoms.ts | 14 +- .../accounts/QuoteAtomsPerBaseAtom.ts | 12 +- .../ts/src/manifest/accounts/WithdrawLog.ts | 20 +-- client/ts/src/manifest/accounts/index.ts | 60 ++++---- client/ts/src/manifest/errors/index.ts | 136 +++++++++--------- client/ts/src/manifest/index.ts | 12 +- .../src/manifest/instructions/BatchUpdate.ts | 14 +- .../ts/src/manifest/instructions/ClaimSeat.ts | 8 +- .../src/manifest/instructions/CreateMarket.ts | 10 +- .../ts/src/manifest/instructions/Deposit.ts | 16 +-- client/ts/src/manifest/instructions/Expand.ts | 8 +- .../manifest/instructions/GlobalAddTrader.ts | 8 +- .../manifest/instructions/GlobalClaimSeat.ts | 8 +- .../src/manifest/instructions/GlobalCreate.ts | 10 +- .../manifest/instructions/GlobalDeposit.ts | 16 +-- client/ts/src/manifest/instructions/Swap.ts | 14 +- .../ts/src/manifest/instructions/Withdraw.ts | 16 +-- client/ts/src/manifest/instructions/index.ts | 22 +-- .../src/manifest/types/BatchUpdateParams.ts | 14 +- .../src/manifest/types/BatchUpdateReturn.ts | 6 +- .../src/manifest/types/CancelOrderParams.ts | 8 +- client/ts/src/manifest/types/DepositParams.ts | 6 +- .../src/manifest/types/GlobalDepositParams.ts | 6 +- client/ts/src/manifest/types/OrderType.ts | 2 +- .../ts/src/manifest/types/PlaceOrderParams.ts | 18 +-- client/ts/src/manifest/types/SwapParams.ts | 12 +- .../ts/src/manifest/types/WithdrawParams.ts | 6 +- client/ts/src/manifest/types/index.ts | 18 +-- client/ts/src/market.ts | 1 - client/ts/src/utils/numbers.ts | 2 +- client/ts/src/wrapper/index.ts | 8 +- .../src/wrapper/instructions/BatchUpdate.ts | 14 +- .../ts/src/wrapper/instructions/ClaimSeat.ts | 8 +- .../src/wrapper/instructions/CreateWrapper.ts | 8 +- client/ts/src/wrapper/instructions/Deposit.ts | 16 +-- .../ts/src/wrapper/instructions/Withdraw.ts | 16 +-- client/ts/src/wrapper/instructions/index.ts | 10 +- client/ts/src/wrapper/types/DepositParams.ts | 6 +- client/ts/src/wrapper/types/OrderType.ts | 2 +- client/ts/src/wrapper/types/WithdrawParams.ts | 6 +- .../wrapper/types/WrapperBatchUpdateParams.ts | 16 +-- .../wrapper/types/WrapperCancelOrderParams.ts | 6 +- .../wrapper/types/WrapperPlaceOrderParams.ts | 22 +-- client/ts/src/wrapper/types/index.ts | 12 +- client/ts/tests/utils.ts | 12 +- 58 files changed, 488 insertions(+), 446 deletions(-) create mode 100644 .eslintrc diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 000000000..e3fdce779 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,43 @@ +{ + "env": { + "browser": true, + "es2021": true, + "node": true + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "prettier" + ], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 12, + "sourceType": "module" + }, + "plugins": [ + "@typescript-eslint" + ], + "rules": { + "linebreak-style": [ + "error", + "unix" + ], + "semi": [ + "error", + "always" + ], + "@typescript-eslint/no-non-null-assertion": 0, + "@typescript-eslint/ban-ts-comment": 0, + "@typescript-eslint/no-explicit-any": 0, + "@typescript-eslint/no-non-null-asserted-optional-chain": 0, + "@typescript-eslint/explicit-function-return-type": "warn", + "@typescript-eslint/no-unused-vars": [ + "error", + { + "argsIgnorePattern": "^_", + "varsIgnorePattern": "^_", + "caughtErrorsIgnorePattern": "^_" + } + ] + } +} \ No newline at end of file diff --git a/client/ts/src/manifest/accounts/BaseAtoms.ts b/client/ts/src/manifest/accounts/BaseAtoms.ts index 6e6f2e9f2..7d5e59cd3 100644 --- a/client/ts/src/manifest/accounts/BaseAtoms.ts +++ b/client/ts/src/manifest/accounts/BaseAtoms.ts @@ -5,9 +5,9 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as beet from "@metaplex-foundation/beet"; -import * as web3 from "@solana/web3.js"; -import * as beetSolana from "@metaplex-foundation/beet-solana"; +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; +import * as beetSolana from '@metaplex-foundation/beet-solana'; /** * Arguments used to create {@link BaseAtoms} @@ -74,7 +74,7 @@ export class BaseAtoms implements BaseAtomsArgs { */ static gpaBuilder( programId: web3.PublicKey = new web3.PublicKey( - "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms", + 'MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms', ), ) { return beetSolana.GpaBuilder.fromStruct(programId, baseAtomsBeet); @@ -136,7 +136,7 @@ export class BaseAtoms implements BaseAtomsArgs { return { inner: (() => { const x = <{ toNumber: () => number }>this.inner; - if (typeof x.toNumber === "function") { + if (typeof x.toNumber === 'function') { try { return x.toNumber(); } catch (_) { @@ -154,7 +154,7 @@ export class BaseAtoms implements BaseAtomsArgs { * @category generated */ export const baseAtomsBeet = new beet.BeetStruct( - [["inner", beet.u64]], + [['inner', beet.u64]], BaseAtoms.fromArgs, - "BaseAtoms", + 'BaseAtoms', ); diff --git a/client/ts/src/manifest/accounts/CancelOrderLog.ts b/client/ts/src/manifest/accounts/CancelOrderLog.ts index d32024241..77b52108d 100644 --- a/client/ts/src/manifest/accounts/CancelOrderLog.ts +++ b/client/ts/src/manifest/accounts/CancelOrderLog.ts @@ -5,9 +5,9 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as web3 from "@solana/web3.js"; -import * as beet from "@metaplex-foundation/beet"; -import * as beetSolana from "@metaplex-foundation/beet-solana"; +import * as web3 from '@solana/web3.js'; +import * as beet from '@metaplex-foundation/beet'; +import * as beetSolana from '@metaplex-foundation/beet-solana'; /** * Arguments used to create {@link CancelOrderLog} @@ -84,7 +84,7 @@ export class CancelOrderLog implements CancelOrderLogArgs { */ static gpaBuilder( programId: web3.PublicKey = new web3.PublicKey( - "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms", + 'MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms', ), ) { return beetSolana.GpaBuilder.fromStruct(programId, cancelOrderLogBeet); @@ -148,7 +148,7 @@ export class CancelOrderLog implements CancelOrderLogArgs { trader: this.trader.toBase58(), orderSequenceNumber: (() => { const x = <{ toNumber: () => number }>this.orderSequenceNumber; - if (typeof x.toNumber === "function") { + if (typeof x.toNumber === 'function') { try { return x.toNumber(); } catch (_) { @@ -170,10 +170,10 @@ export const cancelOrderLogBeet = new beet.BeetStruct< CancelOrderLogArgs >( [ - ["market", beetSolana.publicKey], - ["trader", beetSolana.publicKey], - ["orderSequenceNumber", beet.u64], + ['market', beetSolana.publicKey], + ['trader', beetSolana.publicKey], + ['orderSequenceNumber', beet.u64], ], CancelOrderLog.fromArgs, - "CancelOrderLog", + 'CancelOrderLog', ); diff --git a/client/ts/src/manifest/accounts/ClaimSeatLog.ts b/client/ts/src/manifest/accounts/ClaimSeatLog.ts index 9af8e3c06..d9d31c468 100644 --- a/client/ts/src/manifest/accounts/ClaimSeatLog.ts +++ b/client/ts/src/manifest/accounts/ClaimSeatLog.ts @@ -5,9 +5,9 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as web3 from "@solana/web3.js"; -import * as beetSolana from "@metaplex-foundation/beet-solana"; -import * as beet from "@metaplex-foundation/beet"; +import * as web3 from '@solana/web3.js'; +import * as beetSolana from '@metaplex-foundation/beet-solana'; +import * as beet from '@metaplex-foundation/beet'; /** * Arguments used to create {@link ClaimSeatLog} @@ -78,7 +78,7 @@ export class ClaimSeatLog implements ClaimSeatLogArgs { */ static gpaBuilder( programId: web3.PublicKey = new web3.PublicKey( - "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms", + 'MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms', ), ) { return beetSolana.GpaBuilder.fromStruct(programId, claimSeatLogBeet); @@ -153,9 +153,9 @@ export const claimSeatLogBeet = new beet.BeetStruct< ClaimSeatLogArgs >( [ - ["market", beetSolana.publicKey], - ["trader", beetSolana.publicKey], + ['market', beetSolana.publicKey], + ['trader', beetSolana.publicKey], ], ClaimSeatLog.fromArgs, - "ClaimSeatLog", + 'ClaimSeatLog', ); diff --git a/client/ts/src/manifest/accounts/CreateMarketLog.ts b/client/ts/src/manifest/accounts/CreateMarketLog.ts index e2ac0ba37..377011643 100644 --- a/client/ts/src/manifest/accounts/CreateMarketLog.ts +++ b/client/ts/src/manifest/accounts/CreateMarketLog.ts @@ -5,9 +5,9 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as web3 from "@solana/web3.js"; -import * as beetSolana from "@metaplex-foundation/beet-solana"; -import * as beet from "@metaplex-foundation/beet"; +import * as web3 from '@solana/web3.js'; +import * as beetSolana from '@metaplex-foundation/beet-solana'; +import * as beet from '@metaplex-foundation/beet'; /** * Arguments used to create {@link CreateMarketLog} @@ -78,7 +78,7 @@ export class CreateMarketLog implements CreateMarketLogArgs { */ static gpaBuilder( programId: web3.PublicKey = new web3.PublicKey( - "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms", + 'MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms', ), ) { return beetSolana.GpaBuilder.fromStruct(programId, createMarketLogBeet); @@ -153,9 +153,9 @@ export const createMarketLogBeet = new beet.BeetStruct< CreateMarketLogArgs >( [ - ["market", beetSolana.publicKey], - ["creator", beetSolana.publicKey], + ['market', beetSolana.publicKey], + ['creator', beetSolana.publicKey], ], CreateMarketLog.fromArgs, - "CreateMarketLog", + 'CreateMarketLog', ); diff --git a/client/ts/src/manifest/accounts/DepositLog.ts b/client/ts/src/manifest/accounts/DepositLog.ts index fbc53913e..6236213e4 100644 --- a/client/ts/src/manifest/accounts/DepositLog.ts +++ b/client/ts/src/manifest/accounts/DepositLog.ts @@ -5,9 +5,9 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as web3 from "@solana/web3.js"; -import * as beet from "@metaplex-foundation/beet"; -import * as beetSolana from "@metaplex-foundation/beet-solana"; +import * as web3 from '@solana/web3.js'; +import * as beet from '@metaplex-foundation/beet'; +import * as beetSolana from '@metaplex-foundation/beet-solana'; /** * Arguments used to create {@link DepositLog} @@ -87,7 +87,7 @@ export class DepositLog implements DepositLogArgs { */ static gpaBuilder( programId: web3.PublicKey = new web3.PublicKey( - "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms", + 'MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms', ), ) { return beetSolana.GpaBuilder.fromStruct(programId, depositLogBeet); @@ -152,7 +152,7 @@ export class DepositLog implements DepositLogArgs { mint: this.mint.toBase58(), amountAtoms: (() => { const x = <{ toNumber: () => number }>this.amountAtoms; - if (typeof x.toNumber === "function") { + if (typeof x.toNumber === 'function') { try { return x.toNumber(); } catch (_) { @@ -171,11 +171,11 @@ export class DepositLog implements DepositLogArgs { */ export const depositLogBeet = new beet.BeetStruct( [ - ["market", beetSolana.publicKey], - ["trader", beetSolana.publicKey], - ["mint", beetSolana.publicKey], - ["amountAtoms", beet.u64], + ['market', beetSolana.publicKey], + ['trader', beetSolana.publicKey], + ['mint', beetSolana.publicKey], + ['amountAtoms', beet.u64], ], DepositLog.fromArgs, - "DepositLog", + 'DepositLog', ); diff --git a/client/ts/src/manifest/accounts/FillLog.ts b/client/ts/src/manifest/accounts/FillLog.ts index 0c95e484b..48d184edd 100644 --- a/client/ts/src/manifest/accounts/FillLog.ts +++ b/client/ts/src/manifest/accounts/FillLog.ts @@ -5,15 +5,15 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as web3 from "@solana/web3.js"; -import * as beetSolana from "@metaplex-foundation/beet-solana"; -import * as beet from "@metaplex-foundation/beet"; +import * as web3 from '@solana/web3.js'; +import * as beetSolana from '@metaplex-foundation/beet-solana'; +import * as beet from '@metaplex-foundation/beet'; import { QuoteAtomsPerBaseAtom, quoteAtomsPerBaseAtomBeet, -} from "./QuoteAtomsPerBaseAtom"; -import { BaseAtoms, baseAtomsBeet } from "./BaseAtoms"; -import { QuoteAtoms, quoteAtomsBeet } from "./QuoteAtoms"; +} from './QuoteAtomsPerBaseAtom'; +import { BaseAtoms, baseAtomsBeet } from './BaseAtoms'; +import { QuoteAtoms, quoteAtomsBeet } from './QuoteAtoms'; /** * Arguments used to create {@link FillLog} @@ -105,7 +105,7 @@ export class FillLog implements FillLogArgs { */ static gpaBuilder( programId: web3.PublicKey = new web3.PublicKey( - "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms", + 'MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms', ), ) { return beetSolana.GpaBuilder.fromStruct(programId, fillLogBeet); @@ -183,15 +183,15 @@ export class FillLog implements FillLogArgs { */ export const fillLogBeet = new beet.BeetStruct( [ - ["market", beetSolana.publicKey], - ["maker", beetSolana.publicKey], - ["taker", beetSolana.publicKey], - ["price", quoteAtomsPerBaseAtomBeet], - ["baseAtoms", baseAtomsBeet], - ["quoteAtoms", quoteAtomsBeet], - ["takerIsBuy", beet.bool], - ["padding", beet.uniformFixedSizeArray(beet.u8, 15)], + ['market', beetSolana.publicKey], + ['maker', beetSolana.publicKey], + ['taker', beetSolana.publicKey], + ['price', quoteAtomsPerBaseAtomBeet], + ['baseAtoms', baseAtomsBeet], + ['quoteAtoms', quoteAtomsBeet], + ['takerIsBuy', beet.bool], + ['padding', beet.uniformFixedSizeArray(beet.u8, 15)], ], FillLog.fromArgs, - "FillLog", + 'FillLog', ); diff --git a/client/ts/src/manifest/accounts/GlobalAddTraderLog.ts b/client/ts/src/manifest/accounts/GlobalAddTraderLog.ts index bb877dbba..6cfcf569a 100644 --- a/client/ts/src/manifest/accounts/GlobalAddTraderLog.ts +++ b/client/ts/src/manifest/accounts/GlobalAddTraderLog.ts @@ -5,9 +5,9 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as web3 from "@solana/web3.js"; -import * as beetSolana from "@metaplex-foundation/beet-solana"; -import * as beet from "@metaplex-foundation/beet"; +import * as web3 from '@solana/web3.js'; +import * as beetSolana from '@metaplex-foundation/beet-solana'; +import * as beet from '@metaplex-foundation/beet'; /** * Arguments used to create {@link GlobalAddTraderLog} @@ -80,7 +80,7 @@ export class GlobalAddTraderLog implements GlobalAddTraderLogArgs { */ static gpaBuilder( programId: web3.PublicKey = new web3.PublicKey( - "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms", + 'MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms', ), ) { return beetSolana.GpaBuilder.fromStruct(programId, globalAddTraderLogBeet); @@ -155,9 +155,9 @@ export const globalAddTraderLogBeet = new beet.BeetStruct< GlobalAddTraderLogArgs >( [ - ["global", beetSolana.publicKey], - ["trader", beetSolana.publicKey], + ['global', beetSolana.publicKey], + ['trader', beetSolana.publicKey], ], GlobalAddTraderLog.fromArgs, - "GlobalAddTraderLog", + 'GlobalAddTraderLog', ); diff --git a/client/ts/src/manifest/accounts/GlobalAtoms.ts b/client/ts/src/manifest/accounts/GlobalAtoms.ts index b3c05335d..ea0976c26 100644 --- a/client/ts/src/manifest/accounts/GlobalAtoms.ts +++ b/client/ts/src/manifest/accounts/GlobalAtoms.ts @@ -5,9 +5,9 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as beet from "@metaplex-foundation/beet"; -import * as web3 from "@solana/web3.js"; -import * as beetSolana from "@metaplex-foundation/beet-solana"; +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; +import * as beetSolana from '@metaplex-foundation/beet-solana'; /** * Arguments used to create {@link GlobalAtoms} @@ -74,7 +74,7 @@ export class GlobalAtoms implements GlobalAtomsArgs { */ static gpaBuilder( programId: web3.PublicKey = new web3.PublicKey( - "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms", + 'MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms', ), ) { return beetSolana.GpaBuilder.fromStruct(programId, globalAtomsBeet); @@ -136,7 +136,7 @@ export class GlobalAtoms implements GlobalAtomsArgs { return { inner: (() => { const x = <{ toNumber: () => number }>this.inner; - if (typeof x.toNumber === "function") { + if (typeof x.toNumber === 'function') { try { return x.toNumber(); } catch (_) { @@ -156,4 +156,4 @@ export class GlobalAtoms implements GlobalAtomsArgs { export const globalAtomsBeet = new beet.BeetStruct< GlobalAtoms, GlobalAtomsArgs ->([["inner", beet.u64]], GlobalAtoms.fromArgs, "GlobalAtoms"); +>([['inner', beet.u64]], GlobalAtoms.fromArgs, 'GlobalAtoms'); diff --git a/client/ts/src/manifest/accounts/GlobalClaimSeatLog.ts b/client/ts/src/manifest/accounts/GlobalClaimSeatLog.ts index c15e132f8..52f8e87bb 100644 --- a/client/ts/src/manifest/accounts/GlobalClaimSeatLog.ts +++ b/client/ts/src/manifest/accounts/GlobalClaimSeatLog.ts @@ -5,9 +5,9 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as web3 from "@solana/web3.js"; -import * as beetSolana from "@metaplex-foundation/beet-solana"; -import * as beet from "@metaplex-foundation/beet"; +import * as web3 from '@solana/web3.js'; +import * as beetSolana from '@metaplex-foundation/beet-solana'; +import * as beet from '@metaplex-foundation/beet'; /** * Arguments used to create {@link GlobalClaimSeatLog} @@ -82,7 +82,7 @@ export class GlobalClaimSeatLog implements GlobalClaimSeatLogArgs { */ static gpaBuilder( programId: web3.PublicKey = new web3.PublicKey( - "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms", + 'MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms', ), ) { return beetSolana.GpaBuilder.fromStruct(programId, globalClaimSeatLogBeet); @@ -158,10 +158,10 @@ export const globalClaimSeatLogBeet = new beet.BeetStruct< GlobalClaimSeatLogArgs >( [ - ["global", beetSolana.publicKey], - ["market", beetSolana.publicKey], - ["trader", beetSolana.publicKey], + ['global', beetSolana.publicKey], + ['market', beetSolana.publicKey], + ['trader', beetSolana.publicKey], ], GlobalClaimSeatLog.fromArgs, - "GlobalClaimSeatLog", + 'GlobalClaimSeatLog', ); diff --git a/client/ts/src/manifest/accounts/GlobalCreateLog.ts b/client/ts/src/manifest/accounts/GlobalCreateLog.ts index d6d3af7c1..ae2f39e7e 100644 --- a/client/ts/src/manifest/accounts/GlobalCreateLog.ts +++ b/client/ts/src/manifest/accounts/GlobalCreateLog.ts @@ -5,9 +5,9 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as web3 from "@solana/web3.js"; -import * as beetSolana from "@metaplex-foundation/beet-solana"; -import * as beet from "@metaplex-foundation/beet"; +import * as web3 from '@solana/web3.js'; +import * as beetSolana from '@metaplex-foundation/beet-solana'; +import * as beet from '@metaplex-foundation/beet'; /** * Arguments used to create {@link GlobalCreateLog} @@ -78,7 +78,7 @@ export class GlobalCreateLog implements GlobalCreateLogArgs { */ static gpaBuilder( programId: web3.PublicKey = new web3.PublicKey( - "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms", + 'MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms', ), ) { return beetSolana.GpaBuilder.fromStruct(programId, globalCreateLogBeet); @@ -153,9 +153,9 @@ export const globalCreateLogBeet = new beet.BeetStruct< GlobalCreateLogArgs >( [ - ["global", beetSolana.publicKey], - ["creator", beetSolana.publicKey], + ['global', beetSolana.publicKey], + ['creator', beetSolana.publicKey], ], GlobalCreateLog.fromArgs, - "GlobalCreateLog", + 'GlobalCreateLog', ); diff --git a/client/ts/src/manifest/accounts/GlobalDepositLog.ts b/client/ts/src/manifest/accounts/GlobalDepositLog.ts index 618db2c4c..55786b149 100644 --- a/client/ts/src/manifest/accounts/GlobalDepositLog.ts +++ b/client/ts/src/manifest/accounts/GlobalDepositLog.ts @@ -5,10 +5,10 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as web3 from "@solana/web3.js"; -import * as beetSolana from "@metaplex-foundation/beet-solana"; -import * as beet from "@metaplex-foundation/beet"; -import { GlobalAtoms, globalAtomsBeet } from "./GlobalAtoms"; +import * as web3 from '@solana/web3.js'; +import * as beetSolana from '@metaplex-foundation/beet-solana'; +import * as beet from '@metaplex-foundation/beet'; +import { GlobalAtoms, globalAtomsBeet } from './GlobalAtoms'; /** * Arguments used to create {@link GlobalDepositLog} @@ -81,7 +81,7 @@ export class GlobalDepositLog implements GlobalDepositLogArgs { */ static gpaBuilder( programId: web3.PublicKey = new web3.PublicKey( - "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms", + 'MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms', ), ) { return beetSolana.GpaBuilder.fromStruct(programId, globalDepositLogBeet); @@ -157,10 +157,10 @@ export const globalDepositLogBeet = new beet.BeetStruct< GlobalDepositLogArgs >( [ - ["global", beetSolana.publicKey], - ["trader", beetSolana.publicKey], - ["globalAtoms", globalAtomsBeet], + ['global', beetSolana.publicKey], + ['trader', beetSolana.publicKey], + ['globalAtoms', globalAtomsBeet], ], GlobalDepositLog.fromArgs, - "GlobalDepositLog", + 'GlobalDepositLog', ); diff --git a/client/ts/src/manifest/accounts/PlaceOrderLog.ts b/client/ts/src/manifest/accounts/PlaceOrderLog.ts index 0c7ffb4de..2ea9c6753 100644 --- a/client/ts/src/manifest/accounts/PlaceOrderLog.ts +++ b/client/ts/src/manifest/accounts/PlaceOrderLog.ts @@ -5,15 +5,15 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as web3 from "@solana/web3.js"; -import * as beet from "@metaplex-foundation/beet"; -import * as beetSolana from "@metaplex-foundation/beet-solana"; +import * as web3 from '@solana/web3.js'; +import * as beet from '@metaplex-foundation/beet'; +import * as beetSolana from '@metaplex-foundation/beet-solana'; import { QuoteAtomsPerBaseAtom, quoteAtomsPerBaseAtomBeet, -} from "./QuoteAtomsPerBaseAtom"; -import { BaseAtoms, baseAtomsBeet } from "./BaseAtoms"; -import { OrderType, orderTypeBeet } from "../types/OrderType"; +} from './QuoteAtomsPerBaseAtom'; +import { BaseAtoms, baseAtomsBeet } from './BaseAtoms'; +import { OrderType, orderTypeBeet } from '../types/OrderType'; /** * Arguments used to create {@link PlaceOrderLog} @@ -111,7 +111,7 @@ export class PlaceOrderLog implements PlaceOrderLogArgs { */ static gpaBuilder( programId: web3.PublicKey = new web3.PublicKey( - "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms", + 'MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms', ), ) { return beetSolana.GpaBuilder.fromStruct(programId, placeOrderLogBeet); @@ -177,7 +177,7 @@ export class PlaceOrderLog implements PlaceOrderLogArgs { baseAtoms: this.baseAtoms, orderSequenceNumber: (() => { const x = <{ toNumber: () => number }>this.orderSequenceNumber; - if (typeof x.toNumber === "function") { + if (typeof x.toNumber === 'function') { try { return x.toNumber(); } catch (_) { @@ -188,7 +188,7 @@ export class PlaceOrderLog implements PlaceOrderLogArgs { })(), orderIndex: this.orderIndex, lastValidSlot: this.lastValidSlot, - orderType: "OrderType." + OrderType[this.orderType], + orderType: 'OrderType.' + OrderType[this.orderType], isBid: this.isBid, padding: this.padding, }; @@ -204,17 +204,17 @@ export const placeOrderLogBeet = new beet.BeetStruct< PlaceOrderLogArgs >( [ - ["market", beetSolana.publicKey], - ["trader", beetSolana.publicKey], - ["price", quoteAtomsPerBaseAtomBeet], - ["baseAtoms", baseAtomsBeet], - ["orderSequenceNumber", beet.u64], - ["orderIndex", beet.u32], - ["lastValidSlot", beet.u32], - ["orderType", orderTypeBeet], - ["isBid", beet.bool], - ["padding", beet.uniformFixedSizeArray(beet.u8, 6)], + ['market', beetSolana.publicKey], + ['trader', beetSolana.publicKey], + ['price', quoteAtomsPerBaseAtomBeet], + ['baseAtoms', baseAtomsBeet], + ['orderSequenceNumber', beet.u64], + ['orderIndex', beet.u32], + ['lastValidSlot', beet.u32], + ['orderType', orderTypeBeet], + ['isBid', beet.bool], + ['padding', beet.uniformFixedSizeArray(beet.u8, 6)], ], PlaceOrderLog.fromArgs, - "PlaceOrderLog", + 'PlaceOrderLog', ); diff --git a/client/ts/src/manifest/accounts/QuoteAtoms.ts b/client/ts/src/manifest/accounts/QuoteAtoms.ts index 0e3b02842..13a0e0314 100644 --- a/client/ts/src/manifest/accounts/QuoteAtoms.ts +++ b/client/ts/src/manifest/accounts/QuoteAtoms.ts @@ -5,9 +5,9 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as beet from "@metaplex-foundation/beet"; -import * as web3 from "@solana/web3.js"; -import * as beetSolana from "@metaplex-foundation/beet-solana"; +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; +import * as beetSolana from '@metaplex-foundation/beet-solana'; /** * Arguments used to create {@link QuoteAtoms} @@ -74,7 +74,7 @@ export class QuoteAtoms implements QuoteAtomsArgs { */ static gpaBuilder( programId: web3.PublicKey = new web3.PublicKey( - "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms", + 'MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms', ), ) { return beetSolana.GpaBuilder.fromStruct(programId, quoteAtomsBeet); @@ -136,7 +136,7 @@ export class QuoteAtoms implements QuoteAtomsArgs { return { inner: (() => { const x = <{ toNumber: () => number }>this.inner; - if (typeof x.toNumber === "function") { + if (typeof x.toNumber === 'function') { try { return x.toNumber(); } catch (_) { @@ -154,7 +154,7 @@ export class QuoteAtoms implements QuoteAtomsArgs { * @category generated */ export const quoteAtomsBeet = new beet.BeetStruct( - [["inner", beet.u64]], + [['inner', beet.u64]], QuoteAtoms.fromArgs, - "QuoteAtoms", + 'QuoteAtoms', ); diff --git a/client/ts/src/manifest/accounts/QuoteAtomsPerBaseAtom.ts b/client/ts/src/manifest/accounts/QuoteAtomsPerBaseAtom.ts index b67eb48d8..cfcbcd021 100644 --- a/client/ts/src/manifest/accounts/QuoteAtomsPerBaseAtom.ts +++ b/client/ts/src/manifest/accounts/QuoteAtomsPerBaseAtom.ts @@ -5,9 +5,9 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as beet from "@metaplex-foundation/beet"; -import * as web3 from "@solana/web3.js"; -import * as beetSolana from "@metaplex-foundation/beet-solana"; +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; +import * as beetSolana from '@metaplex-foundation/beet-solana'; /** * Arguments used to create {@link QuoteAtomsPerBaseAtom} @@ -76,7 +76,7 @@ export class QuoteAtomsPerBaseAtom implements QuoteAtomsPerBaseAtomArgs { */ static gpaBuilder( programId: web3.PublicKey = new web3.PublicKey( - "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms", + 'MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms', ), ) { return beetSolana.GpaBuilder.fromStruct( @@ -152,7 +152,7 @@ export const quoteAtomsPerBaseAtomBeet = new beet.BeetStruct< QuoteAtomsPerBaseAtom, QuoteAtomsPerBaseAtomArgs >( - [["inner", beet.uniformFixedSizeArray(beet.u64, 2)]], + [['inner', beet.uniformFixedSizeArray(beet.u64, 2)]], QuoteAtomsPerBaseAtom.fromArgs, - "QuoteAtomsPerBaseAtom", + 'QuoteAtomsPerBaseAtom', ); diff --git a/client/ts/src/manifest/accounts/WithdrawLog.ts b/client/ts/src/manifest/accounts/WithdrawLog.ts index f1f9038b2..78fa5ee33 100644 --- a/client/ts/src/manifest/accounts/WithdrawLog.ts +++ b/client/ts/src/manifest/accounts/WithdrawLog.ts @@ -5,9 +5,9 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as web3 from "@solana/web3.js"; -import * as beet from "@metaplex-foundation/beet"; -import * as beetSolana from "@metaplex-foundation/beet-solana"; +import * as web3 from '@solana/web3.js'; +import * as beet from '@metaplex-foundation/beet'; +import * as beetSolana from '@metaplex-foundation/beet-solana'; /** * Arguments used to create {@link WithdrawLog} @@ -87,7 +87,7 @@ export class WithdrawLog implements WithdrawLogArgs { */ static gpaBuilder( programId: web3.PublicKey = new web3.PublicKey( - "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms", + 'MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms', ), ) { return beetSolana.GpaBuilder.fromStruct(programId, withdrawLogBeet); @@ -152,7 +152,7 @@ export class WithdrawLog implements WithdrawLogArgs { mint: this.mint.toBase58(), amountAtoms: (() => { const x = <{ toNumber: () => number }>this.amountAtoms; - if (typeof x.toNumber === "function") { + if (typeof x.toNumber === 'function') { try { return x.toNumber(); } catch (_) { @@ -174,11 +174,11 @@ export const withdrawLogBeet = new beet.BeetStruct< WithdrawLogArgs >( [ - ["market", beetSolana.publicKey], - ["trader", beetSolana.publicKey], - ["mint", beetSolana.publicKey], - ["amountAtoms", beet.u64], + ['market', beetSolana.publicKey], + ['trader', beetSolana.publicKey], + ['mint', beetSolana.publicKey], + ['amountAtoms', beet.u64], ], WithdrawLog.fromArgs, - "WithdrawLog", + 'WithdrawLog', ); diff --git a/client/ts/src/manifest/accounts/index.ts b/client/ts/src/manifest/accounts/index.ts index 87fc3a6fa..e553fcf31 100644 --- a/client/ts/src/manifest/accounts/index.ts +++ b/client/ts/src/manifest/accounts/index.ts @@ -1,34 +1,34 @@ -export * from "./BaseAtoms"; -export * from "./CancelOrderLog"; -export * from "./ClaimSeatLog"; -export * from "./CreateMarketLog"; -export * from "./DepositLog"; -export * from "./FillLog"; -export * from "./GlobalAddTraderLog"; -export * from "./GlobalAtoms"; -export * from "./GlobalClaimSeatLog"; -export * from "./GlobalCreateLog"; -export * from "./GlobalDepositLog"; -export * from "./PlaceOrderLog"; -export * from "./QuoteAtoms"; -export * from "./QuoteAtomsPerBaseAtom"; -export * from "./WithdrawLog"; +export * from './BaseAtoms'; +export * from './CancelOrderLog'; +export * from './ClaimSeatLog'; +export * from './CreateMarketLog'; +export * from './DepositLog'; +export * from './FillLog'; +export * from './GlobalAddTraderLog'; +export * from './GlobalAtoms'; +export * from './GlobalClaimSeatLog'; +export * from './GlobalCreateLog'; +export * from './GlobalDepositLog'; +export * from './PlaceOrderLog'; +export * from './QuoteAtoms'; +export * from './QuoteAtomsPerBaseAtom'; +export * from './WithdrawLog'; -import { CreateMarketLog } from "./CreateMarketLog"; -import { ClaimSeatLog } from "./ClaimSeatLog"; -import { DepositLog } from "./DepositLog"; -import { WithdrawLog } from "./WithdrawLog"; -import { FillLog } from "./FillLog"; -import { PlaceOrderLog } from "./PlaceOrderLog"; -import { CancelOrderLog } from "./CancelOrderLog"; -import { GlobalCreateLog } from "./GlobalCreateLog"; -import { GlobalAddTraderLog } from "./GlobalAddTraderLog"; -import { GlobalClaimSeatLog } from "./GlobalClaimSeatLog"; -import { GlobalDepositLog } from "./GlobalDepositLog"; -import { QuoteAtoms } from "./QuoteAtoms"; -import { BaseAtoms } from "./BaseAtoms"; -import { GlobalAtoms } from "./GlobalAtoms"; -import { QuoteAtomsPerBaseAtom } from "./QuoteAtomsPerBaseAtom"; +import { CreateMarketLog } from './CreateMarketLog'; +import { ClaimSeatLog } from './ClaimSeatLog'; +import { DepositLog } from './DepositLog'; +import { WithdrawLog } from './WithdrawLog'; +import { FillLog } from './FillLog'; +import { PlaceOrderLog } from './PlaceOrderLog'; +import { CancelOrderLog } from './CancelOrderLog'; +import { GlobalCreateLog } from './GlobalCreateLog'; +import { GlobalAddTraderLog } from './GlobalAddTraderLog'; +import { GlobalClaimSeatLog } from './GlobalClaimSeatLog'; +import { GlobalDepositLog } from './GlobalDepositLog'; +import { QuoteAtoms } from './QuoteAtoms'; +import { BaseAtoms } from './BaseAtoms'; +import { GlobalAtoms } from './GlobalAtoms'; +import { QuoteAtomsPerBaseAtom } from './QuoteAtomsPerBaseAtom'; export const accountProviders = { CreateMarketLog, diff --git a/client/ts/src/manifest/errors/index.ts b/client/ts/src/manifest/errors/index.ts index a0312fc00..fcb650f90 100644 --- a/client/ts/src/manifest/errors/index.ts +++ b/client/ts/src/manifest/errors/index.ts @@ -19,10 +19,10 @@ const createErrorFromNameLookup: Map ErrorWithCode> = new Map(); */ export class InvalidMarketParametersError extends Error { readonly code: number = 0x0; - readonly name: string = "InvalidMarketParameters"; + readonly name: string = 'InvalidMarketParameters'; constructor() { - super("Invalid market parameters error"); - if (typeof Error.captureStackTrace === "function") { + super('Invalid market parameters error'); + if (typeof Error.captureStackTrace === 'function') { Error.captureStackTrace(this, InvalidMarketParametersError); } } @@ -30,7 +30,7 @@ export class InvalidMarketParametersError extends Error { createErrorFromCodeLookup.set(0x0, () => new InvalidMarketParametersError()); createErrorFromNameLookup.set( - "InvalidMarketParameters", + 'InvalidMarketParameters', () => new InvalidMarketParametersError(), ); @@ -42,10 +42,10 @@ createErrorFromNameLookup.set( */ export class InvalidDepositAccountsError extends Error { readonly code: number = 0x1; - readonly name: string = "InvalidDepositAccounts"; + readonly name: string = 'InvalidDepositAccounts'; constructor() { - super("Invalid deposit accounts error"); - if (typeof Error.captureStackTrace === "function") { + super('Invalid deposit accounts error'); + if (typeof Error.captureStackTrace === 'function') { Error.captureStackTrace(this, InvalidDepositAccountsError); } } @@ -53,7 +53,7 @@ export class InvalidDepositAccountsError extends Error { createErrorFromCodeLookup.set(0x1, () => new InvalidDepositAccountsError()); createErrorFromNameLookup.set( - "InvalidDepositAccounts", + 'InvalidDepositAccounts', () => new InvalidDepositAccountsError(), ); @@ -65,10 +65,10 @@ createErrorFromNameLookup.set( */ export class InvalidWithdrawAccountsError extends Error { readonly code: number = 0x2; - readonly name: string = "InvalidWithdrawAccounts"; + readonly name: string = 'InvalidWithdrawAccounts'; constructor() { - super("Invalid withdraw accounts error"); - if (typeof Error.captureStackTrace === "function") { + super('Invalid withdraw accounts error'); + if (typeof Error.captureStackTrace === 'function') { Error.captureStackTrace(this, InvalidWithdrawAccountsError); } } @@ -76,7 +76,7 @@ export class InvalidWithdrawAccountsError extends Error { createErrorFromCodeLookup.set(0x2, () => new InvalidWithdrawAccountsError()); createErrorFromNameLookup.set( - "InvalidWithdrawAccounts", + 'InvalidWithdrawAccounts', () => new InvalidWithdrawAccountsError(), ); @@ -88,17 +88,17 @@ createErrorFromNameLookup.set( */ export class InvalidCancelError extends Error { readonly code: number = 0x3; - readonly name: string = "InvalidCancel"; + readonly name: string = 'InvalidCancel'; constructor() { - super("Invalid cancel error"); - if (typeof Error.captureStackTrace === "function") { + super('Invalid cancel error'); + if (typeof Error.captureStackTrace === 'function') { Error.captureStackTrace(this, InvalidCancelError); } } } createErrorFromCodeLookup.set(0x3, () => new InvalidCancelError()); -createErrorFromNameLookup.set("InvalidCancel", () => new InvalidCancelError()); +createErrorFromNameLookup.set('InvalidCancel', () => new InvalidCancelError()); /** * InvalidFreeList: 'Internal free list corruption error' @@ -108,10 +108,10 @@ createErrorFromNameLookup.set("InvalidCancel", () => new InvalidCancelError()); */ export class InvalidFreeListError extends Error { readonly code: number = 0x4; - readonly name: string = "InvalidFreeList"; + readonly name: string = 'InvalidFreeList'; constructor() { - super("Internal free list corruption error"); - if (typeof Error.captureStackTrace === "function") { + super('Internal free list corruption error'); + if (typeof Error.captureStackTrace === 'function') { Error.captureStackTrace(this, InvalidFreeListError); } } @@ -119,7 +119,7 @@ export class InvalidFreeListError extends Error { createErrorFromCodeLookup.set(0x4, () => new InvalidFreeListError()); createErrorFromNameLookup.set( - "InvalidFreeList", + 'InvalidFreeList', () => new InvalidFreeListError(), ); @@ -131,10 +131,10 @@ createErrorFromNameLookup.set( */ export class AlreadyClaimedSeatError extends Error { readonly code: number = 0x5; - readonly name: string = "AlreadyClaimedSeat"; + readonly name: string = 'AlreadyClaimedSeat'; constructor() { - super("Cannot claim a second seat for the same trader"); - if (typeof Error.captureStackTrace === "function") { + super('Cannot claim a second seat for the same trader'); + if (typeof Error.captureStackTrace === 'function') { Error.captureStackTrace(this, AlreadyClaimedSeatError); } } @@ -142,7 +142,7 @@ export class AlreadyClaimedSeatError extends Error { createErrorFromCodeLookup.set(0x5, () => new AlreadyClaimedSeatError()); createErrorFromNameLookup.set( - "AlreadyClaimedSeat", + 'AlreadyClaimedSeat', () => new AlreadyClaimedSeatError(), ); @@ -154,10 +154,10 @@ createErrorFromNameLookup.set( */ export class PostOnlyCrossesError extends Error { readonly code: number = 0x6; - readonly name: string = "PostOnlyCrosses"; + readonly name: string = 'PostOnlyCrosses'; constructor() { - super("Matched on a post only order"); - if (typeof Error.captureStackTrace === "function") { + super('Matched on a post only order'); + if (typeof Error.captureStackTrace === 'function') { Error.captureStackTrace(this, PostOnlyCrossesError); } } @@ -165,7 +165,7 @@ export class PostOnlyCrossesError extends Error { createErrorFromCodeLookup.set(0x6, () => new PostOnlyCrossesError()); createErrorFromNameLookup.set( - "PostOnlyCrosses", + 'PostOnlyCrosses', () => new PostOnlyCrossesError(), ); @@ -177,10 +177,10 @@ createErrorFromNameLookup.set( */ export class AlreadyExpiredError extends Error { readonly code: number = 0x7; - readonly name: string = "AlreadyExpired"; + readonly name: string = 'AlreadyExpired'; constructor() { - super("New order is already expired"); - if (typeof Error.captureStackTrace === "function") { + super('New order is already expired'); + if (typeof Error.captureStackTrace === 'function') { Error.captureStackTrace(this, AlreadyExpiredError); } } @@ -188,7 +188,7 @@ export class AlreadyExpiredError extends Error { createErrorFromCodeLookup.set(0x7, () => new AlreadyExpiredError()); createErrorFromNameLookup.set( - "AlreadyExpired", + 'AlreadyExpired', () => new AlreadyExpiredError(), ); @@ -200,10 +200,10 @@ createErrorFromNameLookup.set( */ export class InsufficientOutError extends Error { readonly code: number = 0x8; - readonly name: string = "InsufficientOut"; + readonly name: string = 'InsufficientOut'; constructor() { - super("Less than minimum out amount"); - if (typeof Error.captureStackTrace === "function") { + super('Less than minimum out amount'); + if (typeof Error.captureStackTrace === 'function') { Error.captureStackTrace(this, InsufficientOutError); } } @@ -211,7 +211,7 @@ export class InsufficientOutError extends Error { createErrorFromCodeLookup.set(0x8, () => new InsufficientOutError()); createErrorFromNameLookup.set( - "InsufficientOut", + 'InsufficientOut', () => new InsufficientOutError(), ); @@ -223,10 +223,10 @@ createErrorFromNameLookup.set( */ export class InvalidPlaceOrderFromWalletParamsError extends Error { readonly code: number = 0x9; - readonly name: string = "InvalidPlaceOrderFromWalletParams"; + readonly name: string = 'InvalidPlaceOrderFromWalletParams'; constructor() { - super("Invalid place order from wallet params"); - if (typeof Error.captureStackTrace === "function") { + super('Invalid place order from wallet params'); + if (typeof Error.captureStackTrace === 'function') { Error.captureStackTrace(this, InvalidPlaceOrderFromWalletParamsError); } } @@ -237,7 +237,7 @@ createErrorFromCodeLookup.set( () => new InvalidPlaceOrderFromWalletParamsError(), ); createErrorFromNameLookup.set( - "InvalidPlaceOrderFromWalletParams", + 'InvalidPlaceOrderFromWalletParams', () => new InvalidPlaceOrderFromWalletParamsError(), ); @@ -249,10 +249,10 @@ createErrorFromNameLookup.set( */ export class WrongIndexHintParamsError extends Error { readonly code: number = 0xa; - readonly name: string = "WrongIndexHintParams"; + readonly name: string = 'WrongIndexHintParams'; constructor() { - super("Index hint did not match actual index"); - if (typeof Error.captureStackTrace === "function") { + super('Index hint did not match actual index'); + if (typeof Error.captureStackTrace === 'function') { Error.captureStackTrace(this, WrongIndexHintParamsError); } } @@ -260,7 +260,7 @@ export class WrongIndexHintParamsError extends Error { createErrorFromCodeLookup.set(0xa, () => new WrongIndexHintParamsError()); createErrorFromNameLookup.set( - "WrongIndexHintParams", + 'WrongIndexHintParams', () => new WrongIndexHintParamsError(), ); @@ -272,10 +272,10 @@ createErrorFromNameLookup.set( */ export class PriceNotPositiveError extends Error { readonly code: number = 0xb; - readonly name: string = "PriceNotPositive"; + readonly name: string = 'PriceNotPositive'; constructor() { - super("Price is not positive"); - if (typeof Error.captureStackTrace === "function") { + super('Price is not positive'); + if (typeof Error.captureStackTrace === 'function') { Error.captureStackTrace(this, PriceNotPositiveError); } } @@ -283,7 +283,7 @@ export class PriceNotPositiveError extends Error { createErrorFromCodeLookup.set(0xb, () => new PriceNotPositiveError()); createErrorFromNameLookup.set( - "PriceNotPositive", + 'PriceNotPositive', () => new PriceNotPositiveError(), ); @@ -295,10 +295,10 @@ createErrorFromNameLookup.set( */ export class OrderWouldOverflowError extends Error { readonly code: number = 0xc; - readonly name: string = "OrderWouldOverflow"; + readonly name: string = 'OrderWouldOverflow'; constructor() { - super("Order settlement would overflow"); - if (typeof Error.captureStackTrace === "function") { + super('Order settlement would overflow'); + if (typeof Error.captureStackTrace === 'function') { Error.captureStackTrace(this, OrderWouldOverflowError); } } @@ -306,7 +306,7 @@ export class OrderWouldOverflowError extends Error { createErrorFromCodeLookup.set(0xc, () => new OrderWouldOverflowError()); createErrorFromNameLookup.set( - "OrderWouldOverflow", + 'OrderWouldOverflow', () => new OrderWouldOverflowError(), ); @@ -318,17 +318,17 @@ createErrorFromNameLookup.set( */ export class OrderTooSmallError extends Error { readonly code: number = 0xd; - readonly name: string = "OrderTooSmall"; + readonly name: string = 'OrderTooSmall'; constructor() { - super("Order is too small to settle any value"); - if (typeof Error.captureStackTrace === "function") { + super('Order is too small to settle any value'); + if (typeof Error.captureStackTrace === 'function') { Error.captureStackTrace(this, OrderTooSmallError); } } } createErrorFromCodeLookup.set(0xd, () => new OrderTooSmallError()); -createErrorFromNameLookup.set("OrderTooSmall", () => new OrderTooSmallError()); +createErrorFromNameLookup.set('OrderTooSmall', () => new OrderTooSmallError()); /** * Overflow: 'Overflow in token addition' @@ -338,17 +338,17 @@ createErrorFromNameLookup.set("OrderTooSmall", () => new OrderTooSmallError()); */ export class OverflowError extends Error { readonly code: number = 0xe; - readonly name: string = "Overflow"; + readonly name: string = 'Overflow'; constructor() { - super("Overflow in token addition"); - if (typeof Error.captureStackTrace === "function") { + super('Overflow in token addition'); + if (typeof Error.captureStackTrace === 'function') { Error.captureStackTrace(this, OverflowError); } } } createErrorFromCodeLookup.set(0xe, () => new OverflowError()); -createErrorFromNameLookup.set("Overflow", () => new OverflowError()); +createErrorFromNameLookup.set('Overflow', () => new OverflowError()); /** * MissingGlobal: 'Missing Global account' @@ -358,17 +358,17 @@ createErrorFromNameLookup.set("Overflow", () => new OverflowError()); */ export class MissingGlobalError extends Error { readonly code: number = 0xf; - readonly name: string = "MissingGlobal"; + readonly name: string = 'MissingGlobal'; constructor() { - super("Missing Global account"); - if (typeof Error.captureStackTrace === "function") { + super('Missing Global account'); + if (typeof Error.captureStackTrace === 'function') { Error.captureStackTrace(this, MissingGlobalError); } } } createErrorFromCodeLookup.set(0xf, () => new MissingGlobalError()); -createErrorFromNameLookup.set("MissingGlobal", () => new MissingGlobalError()); +createErrorFromNameLookup.set('MissingGlobal', () => new MissingGlobalError()); /** * GlobalInsufficient: 'Insufficient funds on global account to rest an order' @@ -378,10 +378,10 @@ createErrorFromNameLookup.set("MissingGlobal", () => new MissingGlobalError()); */ export class GlobalInsufficientError extends Error { readonly code: number = 0x10; - readonly name: string = "GlobalInsufficient"; + readonly name: string = 'GlobalInsufficient'; constructor() { - super("Insufficient funds on global account to rest an order"); - if (typeof Error.captureStackTrace === "function") { + super('Insufficient funds on global account to rest an order'); + if (typeof Error.captureStackTrace === 'function') { Error.captureStackTrace(this, GlobalInsufficientError); } } @@ -389,7 +389,7 @@ export class GlobalInsufficientError extends Error { createErrorFromCodeLookup.set(0x10, () => new GlobalInsufficientError()); createErrorFromNameLookup.set( - "GlobalInsufficient", + 'GlobalInsufficient', () => new GlobalInsufficientError(), ); diff --git a/client/ts/src/manifest/index.ts b/client/ts/src/manifest/index.ts index c6f52337e..d2e046978 100644 --- a/client/ts/src/manifest/index.ts +++ b/client/ts/src/manifest/index.ts @@ -1,8 +1,8 @@ -import { PublicKey } from "@solana/web3.js"; -export * from "./accounts"; -export * from "./errors"; -export * from "./instructions"; -export * from "./types"; +import { PublicKey } from '@solana/web3.js'; +export * from './accounts'; +export * from './errors'; +export * from './instructions'; +export * from './types'; /** * Program address @@ -10,7 +10,7 @@ export * from "./types"; * @category constants * @category generated */ -export const PROGRAM_ADDRESS = "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms"; +export const PROGRAM_ADDRESS = 'MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms'; /** * Program public key diff --git a/client/ts/src/manifest/instructions/BatchUpdate.ts b/client/ts/src/manifest/instructions/BatchUpdate.ts index c6a6295e0..3966ee05c 100644 --- a/client/ts/src/manifest/instructions/BatchUpdate.ts +++ b/client/ts/src/manifest/instructions/BatchUpdate.ts @@ -5,12 +5,12 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as beet from "@metaplex-foundation/beet"; -import * as web3 from "@solana/web3.js"; +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; import { BatchUpdateParams, batchUpdateParamsBeet, -} from "../types/BatchUpdateParams"; +} from '../types/BatchUpdateParams'; /** * @category Instructions @@ -31,10 +31,10 @@ export const BatchUpdateStruct = new beet.FixableBeetArgsStruct< } >( [ - ["instructionDiscriminator", beet.u8], - ["params", batchUpdateParamsBeet], + ['instructionDiscriminator', beet.u8], + ['params', batchUpdateParamsBeet], ], - "BatchUpdateInstructionArgs", + 'BatchUpdateInstructionArgs', ); /** * Accounts required by the _BatchUpdate_ instruction @@ -86,7 +86,7 @@ export const batchUpdateInstructionDiscriminator = 6; export function createBatchUpdateInstruction( accounts: BatchUpdateInstructionAccounts, args: BatchUpdateInstructionArgs, - programId = new web3.PublicKey("MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms"), + programId = new web3.PublicKey('MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms'), ) { const [data] = BatchUpdateStruct.serialize({ instructionDiscriminator: batchUpdateInstructionDiscriminator, diff --git a/client/ts/src/manifest/instructions/ClaimSeat.ts b/client/ts/src/manifest/instructions/ClaimSeat.ts index 84eab5ae5..9bc9e79ef 100644 --- a/client/ts/src/manifest/instructions/ClaimSeat.ts +++ b/client/ts/src/manifest/instructions/ClaimSeat.ts @@ -5,8 +5,8 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as beet from "@metaplex-foundation/beet"; -import * as web3 from "@solana/web3.js"; +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; /** * @category Instructions @@ -15,7 +15,7 @@ import * as web3 from "@solana/web3.js"; */ export const ClaimSeatStruct = new beet.BeetArgsStruct<{ instructionDiscriminator: number; -}>([["instructionDiscriminator", beet.u8]], "ClaimSeatInstructionArgs"); +}>([['instructionDiscriminator', beet.u8]], 'ClaimSeatInstructionArgs'); /** * Accounts required by the _ClaimSeat_ instruction * @@ -43,7 +43,7 @@ export const claimSeatInstructionDiscriminator = 1; */ export function createClaimSeatInstruction( accounts: ClaimSeatInstructionAccounts, - programId = new web3.PublicKey("MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms"), + programId = new web3.PublicKey('MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms'), ) { const [data] = ClaimSeatStruct.serialize({ instructionDiscriminator: claimSeatInstructionDiscriminator, diff --git a/client/ts/src/manifest/instructions/CreateMarket.ts b/client/ts/src/manifest/instructions/CreateMarket.ts index 7fbbb12c9..39ef7fb67 100644 --- a/client/ts/src/manifest/instructions/CreateMarket.ts +++ b/client/ts/src/manifest/instructions/CreateMarket.ts @@ -5,9 +5,9 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as splToken from "@solana/spl-token"; -import * as beet from "@metaplex-foundation/beet"; -import * as web3 from "@solana/web3.js"; +import * as splToken from '@solana/spl-token'; +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; /** * @category Instructions @@ -16,7 +16,7 @@ import * as web3 from "@solana/web3.js"; */ export const CreateMarketStruct = new beet.BeetArgsStruct<{ instructionDiscriminator: number; -}>([["instructionDiscriminator", beet.u8]], "CreateMarketInstructionArgs"); +}>([['instructionDiscriminator', beet.u8]], 'CreateMarketInstructionArgs'); /** * Accounts required by the _CreateMarket_ instruction * @@ -55,7 +55,7 @@ export const createMarketInstructionDiscriminator = 0; */ export function createCreateMarketInstruction( accounts: CreateMarketInstructionAccounts, - programId = new web3.PublicKey("MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms"), + programId = new web3.PublicKey('MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms'), ) { const [data] = CreateMarketStruct.serialize({ instructionDiscriminator: createMarketInstructionDiscriminator, diff --git a/client/ts/src/manifest/instructions/Deposit.ts b/client/ts/src/manifest/instructions/Deposit.ts index a86581ad1..c50dad795 100644 --- a/client/ts/src/manifest/instructions/Deposit.ts +++ b/client/ts/src/manifest/instructions/Deposit.ts @@ -5,10 +5,10 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as splToken from "@solana/spl-token"; -import * as beet from "@metaplex-foundation/beet"; -import * as web3 from "@solana/web3.js"; -import { DepositParams, depositParamsBeet } from "../types/DepositParams"; +import * as splToken from '@solana/spl-token'; +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; +import { DepositParams, depositParamsBeet } from '../types/DepositParams'; /** * @category Instructions @@ -29,10 +29,10 @@ export const DepositStruct = new beet.BeetArgsStruct< } >( [ - ["instructionDiscriminator", beet.u8], - ["params", depositParamsBeet], + ['instructionDiscriminator', beet.u8], + ['params', depositParamsBeet], ], - "DepositInstructionArgs", + 'DepositInstructionArgs', ); /** * Accounts required by the _Deposit_ instruction @@ -70,7 +70,7 @@ export const depositInstructionDiscriminator = 2; export function createDepositInstruction( accounts: DepositInstructionAccounts, args: DepositInstructionArgs, - programId = new web3.PublicKey("MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms"), + programId = new web3.PublicKey('MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms'), ) { const [data] = DepositStruct.serialize({ instructionDiscriminator: depositInstructionDiscriminator, diff --git a/client/ts/src/manifest/instructions/Expand.ts b/client/ts/src/manifest/instructions/Expand.ts index 646402a4c..c1d329204 100644 --- a/client/ts/src/manifest/instructions/Expand.ts +++ b/client/ts/src/manifest/instructions/Expand.ts @@ -5,8 +5,8 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as beet from "@metaplex-foundation/beet"; -import * as web3 from "@solana/web3.js"; +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; /** * @category Instructions @@ -15,7 +15,7 @@ import * as web3 from "@solana/web3.js"; */ export const ExpandStruct = new beet.BeetArgsStruct<{ instructionDiscriminator: number; -}>([["instructionDiscriminator", beet.u8]], "ExpandInstructionArgs"); +}>([['instructionDiscriminator', beet.u8]], 'ExpandInstructionArgs'); /** * Accounts required by the _Expand_ instruction * @@ -43,7 +43,7 @@ export const expandInstructionDiscriminator = 5; */ export function createExpandInstruction( accounts: ExpandInstructionAccounts, - programId = new web3.PublicKey("MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms"), + programId = new web3.PublicKey('MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms'), ) { const [data] = ExpandStruct.serialize({ instructionDiscriminator: expandInstructionDiscriminator, diff --git a/client/ts/src/manifest/instructions/GlobalAddTrader.ts b/client/ts/src/manifest/instructions/GlobalAddTrader.ts index 69d946d33..cefba9004 100644 --- a/client/ts/src/manifest/instructions/GlobalAddTrader.ts +++ b/client/ts/src/manifest/instructions/GlobalAddTrader.ts @@ -5,8 +5,8 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as beet from "@metaplex-foundation/beet"; -import * as web3 from "@solana/web3.js"; +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; /** * @category Instructions @@ -15,7 +15,7 @@ import * as web3 from "@solana/web3.js"; */ export const GlobalAddTraderStruct = new beet.BeetArgsStruct<{ instructionDiscriminator: number; -}>([["instructionDiscriminator", beet.u8]], "GlobalAddTraderInstructionArgs"); +}>([['instructionDiscriminator', beet.u8]], 'GlobalAddTraderInstructionArgs'); /** * Accounts required by the _GlobalAddTrader_ instruction * @@ -43,7 +43,7 @@ export const globalAddTraderInstructionDiscriminator = 8; */ export function createGlobalAddTraderInstruction( accounts: GlobalAddTraderInstructionAccounts, - programId = new web3.PublicKey("MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms"), + programId = new web3.PublicKey('MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms'), ) { const [data] = GlobalAddTraderStruct.serialize({ instructionDiscriminator: globalAddTraderInstructionDiscriminator, diff --git a/client/ts/src/manifest/instructions/GlobalClaimSeat.ts b/client/ts/src/manifest/instructions/GlobalClaimSeat.ts index 4aaa29dd7..e548226cb 100644 --- a/client/ts/src/manifest/instructions/GlobalClaimSeat.ts +++ b/client/ts/src/manifest/instructions/GlobalClaimSeat.ts @@ -5,8 +5,8 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as beet from "@metaplex-foundation/beet"; -import * as web3 from "@solana/web3.js"; +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; /** * @category Instructions @@ -15,7 +15,7 @@ import * as web3 from "@solana/web3.js"; */ export const GlobalClaimSeatStruct = new beet.BeetArgsStruct<{ instructionDiscriminator: number; -}>([["instructionDiscriminator", beet.u8]], "GlobalClaimSeatInstructionArgs"); +}>([['instructionDiscriminator', beet.u8]], 'GlobalClaimSeatInstructionArgs'); /** * Accounts required by the _GlobalClaimSeat_ instruction * @@ -45,7 +45,7 @@ export const globalClaimSeatInstructionDiscriminator = 9; */ export function createGlobalClaimSeatInstruction( accounts: GlobalClaimSeatInstructionAccounts, - programId = new web3.PublicKey("MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms"), + programId = new web3.PublicKey('MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms'), ) { const [data] = GlobalClaimSeatStruct.serialize({ instructionDiscriminator: globalClaimSeatInstructionDiscriminator, diff --git a/client/ts/src/manifest/instructions/GlobalCreate.ts b/client/ts/src/manifest/instructions/GlobalCreate.ts index 5c9efb927..d48391f94 100644 --- a/client/ts/src/manifest/instructions/GlobalCreate.ts +++ b/client/ts/src/manifest/instructions/GlobalCreate.ts @@ -5,9 +5,9 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as splToken from "@solana/spl-token"; -import * as beet from "@metaplex-foundation/beet"; -import * as web3 from "@solana/web3.js"; +import * as splToken from '@solana/spl-token'; +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; /** * @category Instructions @@ -16,7 +16,7 @@ import * as web3 from "@solana/web3.js"; */ export const GlobalCreateStruct = new beet.BeetArgsStruct<{ instructionDiscriminator: number; -}>([["instructionDiscriminator", beet.u8]], "GlobalCreateInstructionArgs"); +}>([['instructionDiscriminator', beet.u8]], 'GlobalCreateInstructionArgs'); /** * Accounts required by the _GlobalCreate_ instruction * @@ -49,7 +49,7 @@ export const globalCreateInstructionDiscriminator = 7; */ export function createGlobalCreateInstruction( accounts: GlobalCreateInstructionAccounts, - programId = new web3.PublicKey("MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms"), + programId = new web3.PublicKey('MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms'), ) { const [data] = GlobalCreateStruct.serialize({ instructionDiscriminator: globalCreateInstructionDiscriminator, diff --git a/client/ts/src/manifest/instructions/GlobalDeposit.ts b/client/ts/src/manifest/instructions/GlobalDeposit.ts index d240100b0..d54b0f18d 100644 --- a/client/ts/src/manifest/instructions/GlobalDeposit.ts +++ b/client/ts/src/manifest/instructions/GlobalDeposit.ts @@ -5,13 +5,13 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as splToken from "@solana/spl-token"; -import * as beet from "@metaplex-foundation/beet"; -import * as web3 from "@solana/web3.js"; +import * as splToken from '@solana/spl-token'; +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; import { GlobalDepositParams, globalDepositParamsBeet, -} from "../types/GlobalDepositParams"; +} from '../types/GlobalDepositParams'; /** * @category Instructions @@ -32,10 +32,10 @@ export const GlobalDepositStruct = new beet.BeetArgsStruct< } >( [ - ["instructionDiscriminator", beet.u8], - ["params", globalDepositParamsBeet], + ['instructionDiscriminator', beet.u8], + ['params', globalDepositParamsBeet], ], - "GlobalDepositInstructionArgs", + 'GlobalDepositInstructionArgs', ); /** * Accounts required by the _GlobalDeposit_ instruction @@ -73,7 +73,7 @@ export const globalDepositInstructionDiscriminator = 10; export function createGlobalDepositInstruction( accounts: GlobalDepositInstructionAccounts, args: GlobalDepositInstructionArgs, - programId = new web3.PublicKey("MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms"), + programId = new web3.PublicKey('MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms'), ) { const [data] = GlobalDepositStruct.serialize({ instructionDiscriminator: globalDepositInstructionDiscriminator, diff --git a/client/ts/src/manifest/instructions/Swap.ts b/client/ts/src/manifest/instructions/Swap.ts index 94c3e7173..367116349 100644 --- a/client/ts/src/manifest/instructions/Swap.ts +++ b/client/ts/src/manifest/instructions/Swap.ts @@ -5,9 +5,9 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as beet from "@metaplex-foundation/beet"; -import * as web3 from "@solana/web3.js"; -import { SwapParams, swapParamsBeet } from "../types/SwapParams"; +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; +import { SwapParams, swapParamsBeet } from '../types/SwapParams'; /** * @category Instructions @@ -28,10 +28,10 @@ export const SwapStruct = new beet.BeetArgsStruct< } >( [ - ["instructionDiscriminator", beet.u8], - ["params", swapParamsBeet], + ['instructionDiscriminator', beet.u8], + ['params', swapParamsBeet], ], - "SwapInstructionArgs", + 'SwapInstructionArgs', ); /** * Accounts required by the _Swap_ instruction @@ -87,7 +87,7 @@ export const swapInstructionDiscriminator = 4; export function createSwapInstruction( accounts: SwapInstructionAccounts, args: SwapInstructionArgs, - programId = new web3.PublicKey("MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms"), + programId = new web3.PublicKey('MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms'), ) { const [data] = SwapStruct.serialize({ instructionDiscriminator: swapInstructionDiscriminator, diff --git a/client/ts/src/manifest/instructions/Withdraw.ts b/client/ts/src/manifest/instructions/Withdraw.ts index 3289dc136..d0b6b5020 100644 --- a/client/ts/src/manifest/instructions/Withdraw.ts +++ b/client/ts/src/manifest/instructions/Withdraw.ts @@ -5,10 +5,10 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as splToken from "@solana/spl-token"; -import * as beet from "@metaplex-foundation/beet"; -import * as web3 from "@solana/web3.js"; -import { WithdrawParams, withdrawParamsBeet } from "../types/WithdrawParams"; +import * as splToken from '@solana/spl-token'; +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; +import { WithdrawParams, withdrawParamsBeet } from '../types/WithdrawParams'; /** * @category Instructions @@ -29,10 +29,10 @@ export const WithdrawStruct = new beet.BeetArgsStruct< } >( [ - ["instructionDiscriminator", beet.u8], - ["params", withdrawParamsBeet], + ['instructionDiscriminator', beet.u8], + ['params', withdrawParamsBeet], ], - "WithdrawInstructionArgs", + 'WithdrawInstructionArgs', ); /** * Accounts required by the _Withdraw_ instruction @@ -70,7 +70,7 @@ export const withdrawInstructionDiscriminator = 3; export function createWithdrawInstruction( accounts: WithdrawInstructionAccounts, args: WithdrawInstructionArgs, - programId = new web3.PublicKey("MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms"), + programId = new web3.PublicKey('MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms'), ) { const [data] = WithdrawStruct.serialize({ instructionDiscriminator: withdrawInstructionDiscriminator, diff --git a/client/ts/src/manifest/instructions/index.ts b/client/ts/src/manifest/instructions/index.ts index 4820ab400..449418de6 100644 --- a/client/ts/src/manifest/instructions/index.ts +++ b/client/ts/src/manifest/instructions/index.ts @@ -1,11 +1,11 @@ -export * from "./BatchUpdate"; -export * from "./ClaimSeat"; -export * from "./CreateMarket"; -export * from "./Deposit"; -export * from "./Expand"; -export * from "./GlobalAddTrader"; -export * from "./GlobalClaimSeat"; -export * from "./GlobalCreate"; -export * from "./GlobalDeposit"; -export * from "./Swap"; -export * from "./Withdraw"; +export * from './BatchUpdate'; +export * from './ClaimSeat'; +export * from './CreateMarket'; +export * from './Deposit'; +export * from './Expand'; +export * from './GlobalAddTrader'; +export * from './GlobalClaimSeat'; +export * from './GlobalCreate'; +export * from './GlobalDeposit'; +export * from './Swap'; +export * from './Withdraw'; diff --git a/client/ts/src/manifest/types/BatchUpdateParams.ts b/client/ts/src/manifest/types/BatchUpdateParams.ts index 4be039806..27f8718fb 100644 --- a/client/ts/src/manifest/types/BatchUpdateParams.ts +++ b/client/ts/src/manifest/types/BatchUpdateParams.ts @@ -5,9 +5,9 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as beet from "@metaplex-foundation/beet"; -import { CancelOrderParams, cancelOrderParamsBeet } from "./CancelOrderParams"; -import { PlaceOrderParams, placeOrderParamsBeet } from "./PlaceOrderParams"; +import * as beet from '@metaplex-foundation/beet'; +import { CancelOrderParams, cancelOrderParamsBeet } from './CancelOrderParams'; +import { PlaceOrderParams, placeOrderParamsBeet } from './PlaceOrderParams'; export type BatchUpdateParams = { traderIndexHint: beet.COption; cancels: CancelOrderParams[]; @@ -21,9 +21,9 @@ export type BatchUpdateParams = { export const batchUpdateParamsBeet = new beet.FixableBeetArgsStruct( [ - ["traderIndexHint", beet.coption(beet.u32)], - ["cancels", beet.array(cancelOrderParamsBeet)], - ["orders", beet.array(placeOrderParamsBeet)], + ['traderIndexHint', beet.coption(beet.u32)], + ['cancels', beet.array(cancelOrderParamsBeet)], + ['orders', beet.array(placeOrderParamsBeet)], ], - "BatchUpdateParams", + 'BatchUpdateParams', ); diff --git a/client/ts/src/manifest/types/BatchUpdateReturn.ts b/client/ts/src/manifest/types/BatchUpdateReturn.ts index c519ec163..d4c2e9269 100644 --- a/client/ts/src/manifest/types/BatchUpdateReturn.ts +++ b/client/ts/src/manifest/types/BatchUpdateReturn.ts @@ -5,7 +5,7 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as beet from "@metaplex-foundation/beet"; +import * as beet from '@metaplex-foundation/beet'; export type BatchUpdateReturn = { orders: [beet.bignum, number][]; }; @@ -16,6 +16,6 @@ export type BatchUpdateReturn = { */ export const batchUpdateReturnBeet = new beet.FixableBeetArgsStruct( - [["orders", beet.array(beet.fixedSizeTuple([beet.u64, beet.u32]))]], - "BatchUpdateReturn", + [['orders', beet.array(beet.fixedSizeTuple([beet.u64, beet.u32]))]], + 'BatchUpdateReturn', ); diff --git a/client/ts/src/manifest/types/CancelOrderParams.ts b/client/ts/src/manifest/types/CancelOrderParams.ts index 06bb2534d..e2361ee77 100644 --- a/client/ts/src/manifest/types/CancelOrderParams.ts +++ b/client/ts/src/manifest/types/CancelOrderParams.ts @@ -5,7 +5,7 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as beet from "@metaplex-foundation/beet"; +import * as beet from '@metaplex-foundation/beet'; export type CancelOrderParams = { orderSequenceNumber: beet.bignum; orderIndexHint: beet.COption; @@ -18,8 +18,8 @@ export type CancelOrderParams = { export const cancelOrderParamsBeet = new beet.FixableBeetArgsStruct( [ - ["orderSequenceNumber", beet.u64], - ["orderIndexHint", beet.coption(beet.u32)], + ['orderSequenceNumber', beet.u64], + ['orderIndexHint', beet.coption(beet.u32)], ], - "CancelOrderParams", + 'CancelOrderParams', ); diff --git a/client/ts/src/manifest/types/DepositParams.ts b/client/ts/src/manifest/types/DepositParams.ts index 4c5db097b..ad3714b1d 100644 --- a/client/ts/src/manifest/types/DepositParams.ts +++ b/client/ts/src/manifest/types/DepositParams.ts @@ -5,7 +5,7 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as beet from "@metaplex-foundation/beet"; +import * as beet from '@metaplex-foundation/beet'; export type DepositParams = { amountAtoms: beet.bignum; }; @@ -15,6 +15,6 @@ export type DepositParams = { * @category generated */ export const depositParamsBeet = new beet.BeetArgsStruct( - [["amountAtoms", beet.u64]], - "DepositParams", + [['amountAtoms', beet.u64]], + 'DepositParams', ); diff --git a/client/ts/src/manifest/types/GlobalDepositParams.ts b/client/ts/src/manifest/types/GlobalDepositParams.ts index 6f8cc6f37..5341ab4c1 100644 --- a/client/ts/src/manifest/types/GlobalDepositParams.ts +++ b/client/ts/src/manifest/types/GlobalDepositParams.ts @@ -5,7 +5,7 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as beet from "@metaplex-foundation/beet"; +import * as beet from '@metaplex-foundation/beet'; export type GlobalDepositParams = { amountAtoms: beet.bignum; }; @@ -16,6 +16,6 @@ export type GlobalDepositParams = { */ export const globalDepositParamsBeet = new beet.BeetArgsStruct( - [["amountAtoms", beet.u64]], - "GlobalDepositParams", + [['amountAtoms', beet.u64]], + 'GlobalDepositParams', ); diff --git a/client/ts/src/manifest/types/OrderType.ts b/client/ts/src/manifest/types/OrderType.ts index 70cb8fc41..da04d06a0 100644 --- a/client/ts/src/manifest/types/OrderType.ts +++ b/client/ts/src/manifest/types/OrderType.ts @@ -5,7 +5,7 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as beet from "@metaplex-foundation/beet"; +import * as beet from '@metaplex-foundation/beet'; /** * @category enums * @category generated diff --git a/client/ts/src/manifest/types/PlaceOrderParams.ts b/client/ts/src/manifest/types/PlaceOrderParams.ts index 2ed21f2d8..8d8edd7e1 100644 --- a/client/ts/src/manifest/types/PlaceOrderParams.ts +++ b/client/ts/src/manifest/types/PlaceOrderParams.ts @@ -5,8 +5,8 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as beet from "@metaplex-foundation/beet"; -import { OrderType, orderTypeBeet } from "./OrderType"; +import * as beet from '@metaplex-foundation/beet'; +import { OrderType, orderTypeBeet } from './OrderType'; export type PlaceOrderParams = { baseAtoms: beet.bignum; priceMantissa: number; @@ -22,12 +22,12 @@ export type PlaceOrderParams = { */ export const placeOrderParamsBeet = new beet.BeetArgsStruct( [ - ["baseAtoms", beet.u64], - ["priceMantissa", beet.u32], - ["priceExponent", beet.i8], - ["isBid", beet.bool], - ["lastValidSlot", beet.u32], - ["orderType", orderTypeBeet], + ['baseAtoms', beet.u64], + ['priceMantissa', beet.u32], + ['priceExponent', beet.i8], + ['isBid', beet.bool], + ['lastValidSlot', beet.u32], + ['orderType', orderTypeBeet], ], - "PlaceOrderParams", + 'PlaceOrderParams', ); diff --git a/client/ts/src/manifest/types/SwapParams.ts b/client/ts/src/manifest/types/SwapParams.ts index 95521df1e..b0b74306e 100644 --- a/client/ts/src/manifest/types/SwapParams.ts +++ b/client/ts/src/manifest/types/SwapParams.ts @@ -5,7 +5,7 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as beet from "@metaplex-foundation/beet"; +import * as beet from '@metaplex-foundation/beet'; export type SwapParams = { inAtoms: beet.bignum; outAtoms: beet.bignum; @@ -19,10 +19,10 @@ export type SwapParams = { */ export const swapParamsBeet = new beet.BeetArgsStruct( [ - ["inAtoms", beet.u64], - ["outAtoms", beet.u64], - ["isBaseIn", beet.bool], - ["isExactIn", beet.bool], + ['inAtoms', beet.u64], + ['outAtoms', beet.u64], + ['isBaseIn', beet.bool], + ['isExactIn', beet.bool], ], - "SwapParams", + 'SwapParams', ); diff --git a/client/ts/src/manifest/types/WithdrawParams.ts b/client/ts/src/manifest/types/WithdrawParams.ts index 669cb0a66..74d731667 100644 --- a/client/ts/src/manifest/types/WithdrawParams.ts +++ b/client/ts/src/manifest/types/WithdrawParams.ts @@ -5,7 +5,7 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as beet from "@metaplex-foundation/beet"; +import * as beet from '@metaplex-foundation/beet'; export type WithdrawParams = { amountAtoms: beet.bignum; }; @@ -15,6 +15,6 @@ export type WithdrawParams = { * @category generated */ export const withdrawParamsBeet = new beet.BeetArgsStruct( - [["amountAtoms", beet.u64]], - "WithdrawParams", + [['amountAtoms', beet.u64]], + 'WithdrawParams', ); diff --git a/client/ts/src/manifest/types/index.ts b/client/ts/src/manifest/types/index.ts index ae89e3653..cc732a053 100644 --- a/client/ts/src/manifest/types/index.ts +++ b/client/ts/src/manifest/types/index.ts @@ -1,9 +1,9 @@ -export * from "./BatchUpdateParams"; -export * from "./BatchUpdateReturn"; -export * from "./CancelOrderParams"; -export * from "./DepositParams"; -export * from "./GlobalDepositParams"; -export * from "./OrderType"; -export * from "./PlaceOrderParams"; -export * from "./SwapParams"; -export * from "./WithdrawParams"; +export * from './BatchUpdateParams'; +export * from './BatchUpdateReturn'; +export * from './CancelOrderParams'; +export * from './DepositParams'; +export * from './GlobalDepositParams'; +export * from './OrderType'; +export * from './PlaceOrderParams'; +export * from './SwapParams'; +export * from './WithdrawParams'; diff --git a/client/ts/src/market.ts b/client/ts/src/market.ts index a7ad5ee6d..0a35bedb5 100644 --- a/client/ts/src/market.ts +++ b/client/ts/src/market.ts @@ -5,7 +5,6 @@ import { publicKey as beetPublicKey } from '@metaplex-foundation/beet-solana'; import { deserializeRedBlackTree } from './utils/redBlackTree'; import { convertU128, toNum } from './utils/numbers'; import { FIXED_MANIFEST_HEADER_SIZE, NIL } from './constants'; -import { BN } from 'bn.js'; /** * Internal use only. Needed because shank doesnt handle f64 and because the diff --git a/client/ts/src/utils/numbers.ts b/client/ts/src/utils/numbers.ts index ddc971b2f..d0574caa3 100644 --- a/client/ts/src/utils/numbers.ts +++ b/client/ts/src/utils/numbers.ts @@ -27,7 +27,7 @@ export function convertU128(n: bignum): number { target = 0; } else { // can only initialize up to 2**53, but need to divide by 10**20. - const divisor = new BN(10**10); + const divisor = new BN(10 ** 10); // TODO: This may lose decimals, so after the first divide, convert to number. target = n.div(divisor).div(divisor).toNumber(); } diff --git a/client/ts/src/wrapper/index.ts b/client/ts/src/wrapper/index.ts index ee12fa7b9..56aa1472b 100644 --- a/client/ts/src/wrapper/index.ts +++ b/client/ts/src/wrapper/index.ts @@ -1,6 +1,6 @@ -import { PublicKey } from "@solana/web3.js"; -export * from "./instructions"; -export * from "./types"; +import { PublicKey } from '@solana/web3.js'; +export * from './instructions'; +export * from './types'; /** * Program address @@ -8,7 +8,7 @@ export * from "./types"; * @category constants * @category generated */ -export const PROGRAM_ADDRESS = "wMNFSTkir3HgyZTsB7uqu3i7FA73grFCptPXgrZjksL"; +export const PROGRAM_ADDRESS = 'wMNFSTkir3HgyZTsB7uqu3i7FA73grFCptPXgrZjksL'; /** * Program public key diff --git a/client/ts/src/wrapper/instructions/BatchUpdate.ts b/client/ts/src/wrapper/instructions/BatchUpdate.ts index 3cf19a5ce..fc7e823ff 100644 --- a/client/ts/src/wrapper/instructions/BatchUpdate.ts +++ b/client/ts/src/wrapper/instructions/BatchUpdate.ts @@ -5,12 +5,12 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as beet from "@metaplex-foundation/beet"; -import * as web3 from "@solana/web3.js"; +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; import { WrapperBatchUpdateParams, wrapperBatchUpdateParamsBeet, -} from "../types/WrapperBatchUpdateParams"; +} from '../types/WrapperBatchUpdateParams'; /** * @category Instructions @@ -31,10 +31,10 @@ export const BatchUpdateStruct = new beet.FixableBeetArgsStruct< } >( [ - ["instructionDiscriminator", beet.u8], - ["params", wrapperBatchUpdateParamsBeet], + ['instructionDiscriminator', beet.u8], + ['params', wrapperBatchUpdateParamsBeet], ], - "BatchUpdateInstructionArgs", + 'BatchUpdateInstructionArgs', ); /** * Accounts required by the _BatchUpdate_ instruction @@ -72,7 +72,7 @@ export const batchUpdateInstructionDiscriminator = 4; export function createBatchUpdateInstruction( accounts: BatchUpdateInstructionAccounts, args: BatchUpdateInstructionArgs, - programId = new web3.PublicKey("wMNFSTkir3HgyZTsB7uqu3i7FA73grFCptPXgrZjksL"), + programId = new web3.PublicKey('wMNFSTkir3HgyZTsB7uqu3i7FA73grFCptPXgrZjksL'), ) { const [data] = BatchUpdateStruct.serialize({ instructionDiscriminator: batchUpdateInstructionDiscriminator, diff --git a/client/ts/src/wrapper/instructions/ClaimSeat.ts b/client/ts/src/wrapper/instructions/ClaimSeat.ts index 107c00556..0ddcdfeeb 100644 --- a/client/ts/src/wrapper/instructions/ClaimSeat.ts +++ b/client/ts/src/wrapper/instructions/ClaimSeat.ts @@ -5,8 +5,8 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as beet from "@metaplex-foundation/beet"; -import * as web3 from "@solana/web3.js"; +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; /** * @category Instructions @@ -15,7 +15,7 @@ import * as web3 from "@solana/web3.js"; */ export const ClaimSeatStruct = new beet.BeetArgsStruct<{ instructionDiscriminator: number; -}>([["instructionDiscriminator", beet.u8]], "ClaimSeatInstructionArgs"); +}>([['instructionDiscriminator', beet.u8]], 'ClaimSeatInstructionArgs'); /** * Accounts required by the _ClaimSeat_ instruction * @@ -49,7 +49,7 @@ export const claimSeatInstructionDiscriminator = 1; */ export function createClaimSeatInstruction( accounts: ClaimSeatInstructionAccounts, - programId = new web3.PublicKey("wMNFSTkir3HgyZTsB7uqu3i7FA73grFCptPXgrZjksL"), + programId = new web3.PublicKey('wMNFSTkir3HgyZTsB7uqu3i7FA73grFCptPXgrZjksL'), ) { const [data] = ClaimSeatStruct.serialize({ instructionDiscriminator: claimSeatInstructionDiscriminator, diff --git a/client/ts/src/wrapper/instructions/CreateWrapper.ts b/client/ts/src/wrapper/instructions/CreateWrapper.ts index 5f0787336..bc067a2d9 100644 --- a/client/ts/src/wrapper/instructions/CreateWrapper.ts +++ b/client/ts/src/wrapper/instructions/CreateWrapper.ts @@ -5,8 +5,8 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as beet from "@metaplex-foundation/beet"; -import * as web3 from "@solana/web3.js"; +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; /** * @category Instructions @@ -15,7 +15,7 @@ import * as web3 from "@solana/web3.js"; */ export const CreateWrapperStruct = new beet.BeetArgsStruct<{ instructionDiscriminator: number; -}>([["instructionDiscriminator", beet.u8]], "CreateWrapperInstructionArgs"); +}>([['instructionDiscriminator', beet.u8]], 'CreateWrapperInstructionArgs'); /** * Accounts required by the _CreateWrapper_ instruction * @@ -45,7 +45,7 @@ export const createWrapperInstructionDiscriminator = 0; */ export function createCreateWrapperInstruction( accounts: CreateWrapperInstructionAccounts, - programId = new web3.PublicKey("wMNFSTkir3HgyZTsB7uqu3i7FA73grFCptPXgrZjksL"), + programId = new web3.PublicKey('wMNFSTkir3HgyZTsB7uqu3i7FA73grFCptPXgrZjksL'), ) { const [data] = CreateWrapperStruct.serialize({ instructionDiscriminator: createWrapperInstructionDiscriminator, diff --git a/client/ts/src/wrapper/instructions/Deposit.ts b/client/ts/src/wrapper/instructions/Deposit.ts index 2511344b5..6799f6acc 100644 --- a/client/ts/src/wrapper/instructions/Deposit.ts +++ b/client/ts/src/wrapper/instructions/Deposit.ts @@ -5,10 +5,10 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as splToken from "@solana/spl-token"; -import * as beet from "@metaplex-foundation/beet"; -import * as web3 from "@solana/web3.js"; -import { DepositParams, depositParamsBeet } from "../types/DepositParams"; +import * as splToken from '@solana/spl-token'; +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; +import { DepositParams, depositParamsBeet } from '../types/DepositParams'; /** * @category Instructions @@ -29,10 +29,10 @@ export const DepositStruct = new beet.BeetArgsStruct< } >( [ - ["instructionDiscriminator", beet.u8], - ["params", depositParamsBeet], + ['instructionDiscriminator', beet.u8], + ['params', depositParamsBeet], ], - "DepositInstructionArgs", + 'DepositInstructionArgs', ); /** * Accounts required by the _Deposit_ instruction @@ -76,7 +76,7 @@ export const depositInstructionDiscriminator = 2; export function createDepositInstruction( accounts: DepositInstructionAccounts, args: DepositInstructionArgs, - programId = new web3.PublicKey("wMNFSTkir3HgyZTsB7uqu3i7FA73grFCptPXgrZjksL"), + programId = new web3.PublicKey('wMNFSTkir3HgyZTsB7uqu3i7FA73grFCptPXgrZjksL'), ) { const [data] = DepositStruct.serialize({ instructionDiscriminator: depositInstructionDiscriminator, diff --git a/client/ts/src/wrapper/instructions/Withdraw.ts b/client/ts/src/wrapper/instructions/Withdraw.ts index 510ba9b20..03bbcd07c 100644 --- a/client/ts/src/wrapper/instructions/Withdraw.ts +++ b/client/ts/src/wrapper/instructions/Withdraw.ts @@ -5,10 +5,10 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as splToken from "@solana/spl-token"; -import * as beet from "@metaplex-foundation/beet"; -import * as web3 from "@solana/web3.js"; -import { WithdrawParams, withdrawParamsBeet } from "../types/WithdrawParams"; +import * as splToken from '@solana/spl-token'; +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; +import { WithdrawParams, withdrawParamsBeet } from '../types/WithdrawParams'; /** * @category Instructions @@ -29,10 +29,10 @@ export const WithdrawStruct = new beet.BeetArgsStruct< } >( [ - ["instructionDiscriminator", beet.u8], - ["params", withdrawParamsBeet], + ['instructionDiscriminator', beet.u8], + ['params', withdrawParamsBeet], ], - "WithdrawInstructionArgs", + 'WithdrawInstructionArgs', ); /** * Accounts required by the _Withdraw_ instruction @@ -76,7 +76,7 @@ export const withdrawInstructionDiscriminator = 3; export function createWithdrawInstruction( accounts: WithdrawInstructionAccounts, args: WithdrawInstructionArgs, - programId = new web3.PublicKey("wMNFSTkir3HgyZTsB7uqu3i7FA73grFCptPXgrZjksL"), + programId = new web3.PublicKey('wMNFSTkir3HgyZTsB7uqu3i7FA73grFCptPXgrZjksL'), ) { const [data] = WithdrawStruct.serialize({ instructionDiscriminator: withdrawInstructionDiscriminator, diff --git a/client/ts/src/wrapper/instructions/index.ts b/client/ts/src/wrapper/instructions/index.ts index dbe2be8d9..c093a6314 100644 --- a/client/ts/src/wrapper/instructions/index.ts +++ b/client/ts/src/wrapper/instructions/index.ts @@ -1,5 +1,5 @@ -export * from "./BatchUpdate"; -export * from "./ClaimSeat"; -export * from "./CreateWrapper"; -export * from "./Deposit"; -export * from "./Withdraw"; +export * from './BatchUpdate'; +export * from './ClaimSeat'; +export * from './CreateWrapper'; +export * from './Deposit'; +export * from './Withdraw'; diff --git a/client/ts/src/wrapper/types/DepositParams.ts b/client/ts/src/wrapper/types/DepositParams.ts index 4c5db097b..ad3714b1d 100644 --- a/client/ts/src/wrapper/types/DepositParams.ts +++ b/client/ts/src/wrapper/types/DepositParams.ts @@ -5,7 +5,7 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as beet from "@metaplex-foundation/beet"; +import * as beet from '@metaplex-foundation/beet'; export type DepositParams = { amountAtoms: beet.bignum; }; @@ -15,6 +15,6 @@ export type DepositParams = { * @category generated */ export const depositParamsBeet = new beet.BeetArgsStruct( - [["amountAtoms", beet.u64]], - "DepositParams", + [['amountAtoms', beet.u64]], + 'DepositParams', ); diff --git a/client/ts/src/wrapper/types/OrderType.ts b/client/ts/src/wrapper/types/OrderType.ts index 539c04d50..885a81294 100644 --- a/client/ts/src/wrapper/types/OrderType.ts +++ b/client/ts/src/wrapper/types/OrderType.ts @@ -5,7 +5,7 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as beet from "@metaplex-foundation/beet"; +import * as beet from '@metaplex-foundation/beet'; /** * @category enums * @category generated diff --git a/client/ts/src/wrapper/types/WithdrawParams.ts b/client/ts/src/wrapper/types/WithdrawParams.ts index 669cb0a66..74d731667 100644 --- a/client/ts/src/wrapper/types/WithdrawParams.ts +++ b/client/ts/src/wrapper/types/WithdrawParams.ts @@ -5,7 +5,7 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as beet from "@metaplex-foundation/beet"; +import * as beet from '@metaplex-foundation/beet'; export type WithdrawParams = { amountAtoms: beet.bignum; }; @@ -15,6 +15,6 @@ export type WithdrawParams = { * @category generated */ export const withdrawParamsBeet = new beet.BeetArgsStruct( - [["amountAtoms", beet.u64]], - "WithdrawParams", + [['amountAtoms', beet.u64]], + 'WithdrawParams', ); diff --git a/client/ts/src/wrapper/types/WrapperBatchUpdateParams.ts b/client/ts/src/wrapper/types/WrapperBatchUpdateParams.ts index 0d8930af7..b77aafaad 100644 --- a/client/ts/src/wrapper/types/WrapperBatchUpdateParams.ts +++ b/client/ts/src/wrapper/types/WrapperBatchUpdateParams.ts @@ -5,15 +5,15 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as beet from "@metaplex-foundation/beet"; +import * as beet from '@metaplex-foundation/beet'; import { WrapperCancelOrderParams, wrapperCancelOrderParamsBeet, -} from "./WrapperCancelOrderParams"; +} from './WrapperCancelOrderParams'; import { WrapperPlaceOrderParams, wrapperPlaceOrderParamsBeet, -} from "./WrapperPlaceOrderParams"; +} from './WrapperPlaceOrderParams'; export type WrapperBatchUpdateParams = { cancels: WrapperCancelOrderParams[]; cancelAll: boolean; @@ -28,10 +28,10 @@ export type WrapperBatchUpdateParams = { export const wrapperBatchUpdateParamsBeet = new beet.FixableBeetArgsStruct( [ - ["cancels", beet.array(wrapperCancelOrderParamsBeet)], - ["cancelAll", beet.bool], - ["orders", beet.array(wrapperPlaceOrderParamsBeet)], - ["traderIndexHint", beet.coption(beet.u32)], + ['cancels', beet.array(wrapperCancelOrderParamsBeet)], + ['cancelAll', beet.bool], + ['orders', beet.array(wrapperPlaceOrderParamsBeet)], + ['traderIndexHint', beet.coption(beet.u32)], ], - "WrapperBatchUpdateParams", + 'WrapperBatchUpdateParams', ); diff --git a/client/ts/src/wrapper/types/WrapperCancelOrderParams.ts b/client/ts/src/wrapper/types/WrapperCancelOrderParams.ts index 84494174a..f79d0c7c7 100644 --- a/client/ts/src/wrapper/types/WrapperCancelOrderParams.ts +++ b/client/ts/src/wrapper/types/WrapperCancelOrderParams.ts @@ -5,7 +5,7 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as beet from "@metaplex-foundation/beet"; +import * as beet from '@metaplex-foundation/beet'; export type WrapperCancelOrderParams = { clientOrderId: beet.bignum; }; @@ -16,6 +16,6 @@ export type WrapperCancelOrderParams = { */ export const wrapperCancelOrderParamsBeet = new beet.BeetArgsStruct( - [["clientOrderId", beet.u64]], - "WrapperCancelOrderParams", + [['clientOrderId', beet.u64]], + 'WrapperCancelOrderParams', ); diff --git a/client/ts/src/wrapper/types/WrapperPlaceOrderParams.ts b/client/ts/src/wrapper/types/WrapperPlaceOrderParams.ts index ef75fbda9..2fd0f3f39 100644 --- a/client/ts/src/wrapper/types/WrapperPlaceOrderParams.ts +++ b/client/ts/src/wrapper/types/WrapperPlaceOrderParams.ts @@ -5,8 +5,8 @@ * See: https://github.com/metaplex-foundation/solita */ -import * as beet from "@metaplex-foundation/beet"; -import { OrderType, orderTypeBeet } from "./OrderType"; +import * as beet from '@metaplex-foundation/beet'; +import { OrderType, orderTypeBeet } from './OrderType'; export type WrapperPlaceOrderParams = { clientOrderId: beet.bignum; baseAtoms: beet.bignum; @@ -25,14 +25,14 @@ export type WrapperPlaceOrderParams = { export const wrapperPlaceOrderParamsBeet = new beet.BeetArgsStruct( [ - ["clientOrderId", beet.u64], - ["baseAtoms", beet.u64], - ["priceMantissa", beet.u32], - ["priceExponent", beet.i8], - ["isBid", beet.bool], - ["lastValidSlot", beet.u32], - ["orderType", orderTypeBeet], - ["minOutAtoms", beet.u64], + ['clientOrderId', beet.u64], + ['baseAtoms', beet.u64], + ['priceMantissa', beet.u32], + ['priceExponent', beet.i8], + ['isBid', beet.bool], + ['lastValidSlot', beet.u32], + ['orderType', orderTypeBeet], + ['minOutAtoms', beet.u64], ], - "WrapperPlaceOrderParams", + 'WrapperPlaceOrderParams', ); diff --git a/client/ts/src/wrapper/types/index.ts b/client/ts/src/wrapper/types/index.ts index 2909a0fc0..f1075ca17 100644 --- a/client/ts/src/wrapper/types/index.ts +++ b/client/ts/src/wrapper/types/index.ts @@ -1,6 +1,6 @@ -export * from "./DepositParams"; -export * from "./OrderType"; -export * from "./WithdrawParams"; -export * from "./WrapperBatchUpdateParams"; -export * from "./WrapperCancelOrderParams"; -export * from "./WrapperPlaceOrderParams"; +export * from './DepositParams'; +export * from './OrderType'; +export * from './WithdrawParams'; +export * from './WrapperBatchUpdateParams'; +export * from './WrapperCancelOrderParams'; +export * from './WrapperPlaceOrderParams'; diff --git a/client/ts/tests/utils.ts b/client/ts/tests/utils.ts index 4f304a9c3..8b550703c 100644 --- a/client/ts/tests/utils.ts +++ b/client/ts/tests/utils.ts @@ -8,14 +8,14 @@ async function testUtils(): Promise { ); assert((await getClusterFromConnection(localnetConnection)) == 'localnet'); - const devnetConnection: Connection = new Connection( - 'https://api.devnet.solana.com', - ); + //const devnetConnection: Connection = new Connection( + // 'https://api.devnet.solana.com', + //); //assert((await getClusterFromConnection(devnetConnection)) == 'devnet'); - const mainnetConnection: Connection = new Connection( - 'https://api.mainnet-beta.solana.com', - ); + //const mainnetConnection: Connection = new Connection( + // 'https://api.mainnet-beta.solana.com', + //); //assert((await getClusterFromConnection(mainnetConnection)) == 'mainnet-beta'); } From b7b4e59192d7d311f2072237ab748a3ec3b806ca Mon Sep 17 00:00:00 2001 From: Britt Cyr Date: Sun, 25 Aug 2024 16:11:11 +0200 Subject: [PATCH 12/16] lint --- client/rust/src/lib.rs | 7 ++++--- client/ts/src/fillFeed.ts | 7 +++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/client/rust/src/lib.rs b/client/rust/src/lib.rs index 7e17acbfc..66bf94219 100644 --- a/client/rust/src/lib.rs +++ b/client/rust/src/lib.rs @@ -3,12 +3,12 @@ use jupiter_amm_interface::{ AccountMap, Amm, KeyedAccount, Quote, QuoteParams, Side, Swap, SwapAndAccountMetas, SwapParams, }; +use hypertree::get_helper; use manifest::{ quantities::{BaseAtoms, QuoteAtoms, WrapperU64}, state::{DynamicAccount, MarketFixed, MarketValue}, validation::{get_global_address, get_global_vault_address, get_vault_address}, }; -use hypertree::get_helper; use solana_sdk::{instruction::AccountMeta, pubkey::Pubkey}; use std::mem::size_of; @@ -202,17 +202,18 @@ impl Amm for ManifestMarket { #[cfg(test)] mod test { use super::*; + use hypertree::{get_mut_helper, trace, DataIndex}; use manifest::{ quantities::{BaseAtoms, GlobalAtoms}, state::{ - constants::NO_EXPIRATION_LAST_VALID_SLOT, AddOrderToMarketArgs, GlobalFixed, GlobalValue, OrderType, BLOCK_SIZE, GLOBAL_FIXED_SIZE, MARKET_FIXED_SIZE + constants::NO_EXPIRATION_LAST_VALID_SLOT, AddOrderToMarketArgs, GlobalFixed, + GlobalValue, OrderType, BLOCK_SIZE, GLOBAL_FIXED_SIZE, MARKET_FIXED_SIZE, }, validation::{ loaders::GlobalTradeAccounts, ManifestAccountInfo, MintAccountInfo, TokenAccountInfo, TokenProgram, }, }; - use hypertree::{get_mut_helper, trace, DataIndex}; use solana_sdk::{account::Account, account_info::AccountInfo}; use spl_token::state::Mint; use std::{cell::RefCell, collections::HashMap, rc::Rc, str::FromStr}; diff --git a/client/ts/src/fillFeed.ts b/client/ts/src/fillFeed.ts index 6797b0119..41dde2bff 100644 --- a/client/ts/src/fillFeed.ts +++ b/client/ts/src/fillFeed.ts @@ -3,7 +3,7 @@ import { Connection, ConfirmedSignatureInfo } from '@solana/web3.js'; import { FillLog } from './manifest/accounts/FillLog'; import { PROGRAM_ID } from './manifest'; -import { toNum } from './utils/numbers'; +import { convertU128, toNum } from './utils/numbers'; import bs58 from 'bs58'; import keccak256 from 'keccak256'; @@ -182,9 +182,8 @@ function toFillLogResult(fillLog: FillLog, slot: number): FillLogResult { taker: fillLog.taker.toBase58(), baseAtoms: toNum(fillLog.baseAtoms.inner), quoteAtoms: toNum(fillLog.quoteAtoms.inner), - price: toNum( - Buffer.from(fillLog.price.inner as Uint8Array).readDoubleLE(0), - ), + // TOOD: Fix this for the new price format + price: convertU128(fillLog.price.inner[1]), takerIsBuy: fillLog.takerIsBuy, slot, }; From a4ac84b02b752514402376578761a241f3b17607 Mon Sep 17 00:00:00 2001 From: Britt Cyr Date: Sun, 25 Aug 2024 16:15:25 +0200 Subject: [PATCH 13/16] u128 pricing --- client/idl/generateIdl.js | 13 +------------ client/idl/manifest.json | 7 +------ .../manifest/accounts/QuoteAtomsPerBaseAtom.ts | 18 ++++++++++++++---- 3 files changed, 16 insertions(+), 22 deletions(-) diff --git a/client/idl/generateIdl.js b/client/idl/generateIdl.js index 3d3719bfb..e57df067b 100644 --- a/client/idl/generateIdl.js +++ b/client/idl/generateIdl.js @@ -87,18 +87,7 @@ function modifyIdlCore(programName) { } } - // Solita does not support f64 - // https://github.com/metaplex-foundation/beet/issues/48 - for (const idlType of idl.types) { - if (idlType.type && idlType.type.fields) { - idlType.type.fields = idlType.type.fields.map((field) => { - if (field.name == 'price') { - field.type = 'FixedSizeUint8Array'; - } - return field; - }); - } - } + // TODO: Override u128 price in the output for QuoteAtomsPerBaseAtom for (const idlAccount of idl.accounts) { if (idlAccount.type && idlAccount.type.fields) { diff --git a/client/idl/manifest.json b/client/idl/manifest.json index b82f4b68d..d0e5f58af 100644 --- a/client/idl/manifest.json +++ b/client/idl/manifest.json @@ -1046,12 +1046,7 @@ "fields": [ { "name": "inner", - "type": { - "array": [ - "u64", - 2 - ] - } + "type": "u128" } ] } diff --git a/client/ts/src/manifest/accounts/QuoteAtomsPerBaseAtom.ts b/client/ts/src/manifest/accounts/QuoteAtomsPerBaseAtom.ts index cfcbcd021..daba82e72 100644 --- a/client/ts/src/manifest/accounts/QuoteAtomsPerBaseAtom.ts +++ b/client/ts/src/manifest/accounts/QuoteAtomsPerBaseAtom.ts @@ -15,7 +15,7 @@ import * as beetSolana from '@metaplex-foundation/beet-solana'; * @category generated */ export type QuoteAtomsPerBaseAtomArgs = { - inner: beet.bignum[] /* size: 2 */; + inner: beet.bignum; }; /** * Holds the data for the {@link QuoteAtomsPerBaseAtom} Account and provides de/serialization @@ -25,7 +25,7 @@ export type QuoteAtomsPerBaseAtomArgs = { * @category generated */ export class QuoteAtomsPerBaseAtom implements QuoteAtomsPerBaseAtomArgs { - private constructor(readonly inner: beet.bignum[] /* size: 2 */) {} + private constructor(readonly inner: beet.bignum) {} /** * Creates a {@link QuoteAtomsPerBaseAtom} instance from the provided args. @@ -139,7 +139,17 @@ export class QuoteAtomsPerBaseAtom implements QuoteAtomsPerBaseAtomArgs { */ pretty() { return { - inner: this.inner, + inner: (() => { + const x = <{ toNumber: () => number }>this.inner; + if (typeof x.toNumber === 'function') { + try { + return x.toNumber(); + } catch (_) { + return x; + } + } + return x; + })(), }; } } @@ -152,7 +162,7 @@ export const quoteAtomsPerBaseAtomBeet = new beet.BeetStruct< QuoteAtomsPerBaseAtom, QuoteAtomsPerBaseAtomArgs >( - [['inner', beet.uniformFixedSizeArray(beet.u64, 2)]], + [['inner', beet.u128]], QuoteAtomsPerBaseAtom.fromArgs, 'QuoteAtomsPerBaseAtom', ); From b4891cdca61419f09c8e065ba6368312580a159c Mon Sep 17 00:00:00 2001 From: Britt Cyr Date: Sun, 25 Aug 2024 16:32:30 +0200 Subject: [PATCH 14/16] fix script --- .github/workflows/ci-code-review-ts.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-code-review-ts.yml b/.github/workflows/ci-code-review-ts.yml index 4ad5848b6..9ed8848b3 100644 --- a/.github/workflows/ci-code-review-ts.yml +++ b/.github/workflows/ci-code-review-ts.yml @@ -106,7 +106,7 @@ jobs: sleep 15 echo "Updating hardcoded discriminant constants for the hardcoded deploy key" sed -i 's/MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms/bitpRsDktxXjwcQvx11p3JZ4A8ucwM1AYBgjNfhr3yf/' programs/manifest/src/lib.rs - sed -i 's/4859840929024028656/15533312255830019719/' programs/manifest/src/validation/market_checker.rs + sed -i 's/4859840929024028656/15533312255830019719/' programs/manifest/src/validation/manifest_checker.rs sed -i 's/\[33, 31, 11, 6, 133, 143, 39, 71\]/[228, 66, 25, 184, 68, 250, 83, 68]/' programs/manifest/src/logs.rs sed -i 's/\[129, 77, 152, 210, 218, 144, 163, 56\]/[83, 80, 13, 195, 157, 15, 144, 87]/' programs/manifest/src/logs.rs sed -i 's/\[23, 214, 24, 34, 52, 104, 109, 188\]/[67, 61, 36, 241, 56, 245, 238, 146]/' programs/manifest/src/logs.rs From 62c1bd2ca070b5c6a3abb006d53b922d2d262c79 Mon Sep 17 00:00:00 2001 From: Britt Cyr Date: Sun, 25 Aug 2024 19:33:05 +0200 Subject: [PATCH 15/16] fix fill feed --- client/ts/src/fillFeed.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ts/src/fillFeed.ts b/client/ts/src/fillFeed.ts index 41dde2bff..8f3a5ef71 100644 --- a/client/ts/src/fillFeed.ts +++ b/client/ts/src/fillFeed.ts @@ -183,7 +183,7 @@ function toFillLogResult(fillLog: FillLog, slot: number): FillLogResult { baseAtoms: toNum(fillLog.baseAtoms.inner), quoteAtoms: toNum(fillLog.quoteAtoms.inner), // TOOD: Fix this for the new price format - price: convertU128(fillLog.price.inner[1]), + price: convertU128(fillLog.price.inner), takerIsBuy: fillLog.takerIsBuy, slot, }; From 8bbbf8804b59321839218eba8a3224697017d58b Mon Sep 17 00:00:00 2001 From: Britt Cyr Date: Mon, 26 Aug 2024 17:27:04 +0200 Subject: [PATCH 16/16] fix lock --- Cargo.lock | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 479b5e1a3..1e40a750f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6372,23 +6372,6 @@ dependencies = [ "tap", ] -name = "x509-parser" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0ecbeb7b67ce215e40e3cc7f2ff902f94a223acf44995934763467e7b1febc8" -dependencies = [ - "asn1-rs", - "base64 0.13.1", - "data-encoding", - "der-parser", - "lazy_static", - "nom", - "oid-registry", - "rusticata-macros", - "thiserror", - "time", -] - [[package]] name = "xattr" version = "1.3.1"