From 4fc709d30ec4674d29e58ed8ecfd495fee00e4f9 Mon Sep 17 00:00:00 2001 From: zer0fi <192312799+zer0fi@users.noreply.github.com> Date: Tue, 10 Dec 2024 16:03:47 +0100 Subject: [PATCH 1/3] zerofi integration --- package.json | 5 + programs/drift/src/error.rs | 6 + programs/drift/src/instructions/admin.rs | 108 ++++ programs/drift/src/instructions/keeper.rs | 13 + programs/drift/src/instructions/user.rs | 23 + programs/drift/src/lib.rs | 15 + programs/drift/src/state/events.rs | 1 + .../drift/src/state/fulfillment_params/mod.rs | 1 + .../src/state/fulfillment_params/zerofi.rs | 457 ++++++++++++++ sdk/src/addresses/pda.ts | 13 + sdk/src/adminClient.ts | 51 ++ sdk/src/driftClient.ts | 103 +++- sdk/src/idl/drift.json | 171 ++++++ sdk/src/types.ts | 16 + test-scripts/run-anchor-tests.sh | 3 +- tests/fixtures/zerofi.so | Bin 0 -> 273800 bytes ...iUbySimawqXEveD3RdTVsf7FzSgAJK3EABAjZ.json | 1 + ...1111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM.json | 1 + ...1111ogCyDbaRMvkdsHB3qfdyFYaG1WtRUAfdh.json | 1 + ...xHqfY7D4JHU3fbmvWmTV26F2DQzsoCySpE1Ya.json | 1 + ...1112D1oxKts8YPdTJRG5FzxTNpMtWmq8hkVx3.json | 1 + ...1112cMQwSC9qirWGjZM6gLGwW69X22mqwLLGP.json | 1 + ...WP1CUvLMuwVSg1KKcjp9YJxifbQBnQt74SFKM.json | 1 + ...Jnwo1G9VSCfYPZDsmnnsLpPwoqpTvwuoV3TTZ.json | 1 + ...vBNn91Q8TWHfGhwqX5VdbhMmfujPRmkoRXPVT.json | 1 + ...12UQCQ4RPt1NyziyCqjuMLkn9JmtQrReZTzAN.json | 1 + tests/zerofiTest.ts | 340 +++++++++++ yarn.lock | 574 +++++++++++++++++- 28 files changed, 1897 insertions(+), 13 deletions(-) create mode 100644 programs/drift/src/state/fulfillment_params/zerofi.rs create mode 100755 tests/fixtures/zerofi.so create mode 100644 tests/fixtures/zerofi/market-G6Yn5VpiUbySimawqXEveD3RdTVsf7FzSgAJK3EABAjZ.json create mode 100644 tests/fixtures/zerofi/mint_base-1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM.json create mode 100644 tests/fixtures/zerofi/mint_quote-1111111ogCyDbaRMvkdsHB3qfdyFYaG1WtRUAfdh.json create mode 100644 tests/fixtures/zerofi/user_authority-DiCEXQfxHqfY7D4JHU3fbmvWmTV26F2DQzsoCySpE1Ya.json create mode 100644 tests/fixtures/zerofi/user_base-11111112D1oxKts8YPdTJRG5FzxTNpMtWmq8hkVx3.json create mode 100644 tests/fixtures/zerofi/user_quote-11111112cMQwSC9qirWGjZM6gLGwW69X22mqwLLGP.json create mode 100644 tests/fixtures/zerofi/vault_base-EiR75kaWP1CUvLMuwVSg1KKcjp9YJxifbQBnQt74SFKM.json create mode 100644 tests/fixtures/zerofi/vault_base_info-8aYjmYBJnwo1G9VSCfYPZDsmnnsLpPwoqpTvwuoV3TTZ.json create mode 100644 tests/fixtures/zerofi/vault_quote-ES2YchtvBNn91Q8TWHfGhwqX5VdbhMmfujPRmkoRXPVT.json create mode 100644 tests/fixtures/zerofi/vault_quote_info-GKGrCkr12UQCQ4RPt1NyziyCqjuMLkn9JmtQrReZTzAN.json create mode 100644 tests/zerofiTest.ts diff --git a/package.json b/package.json index 590af7ccfe..9fd0d8c331 100644 --- a/package.json +++ b/package.json @@ -14,15 +14,20 @@ "@solana/web3.js": "1.73.2", "@types/bn.js": "5.1.6", "@types/chai": "5.0.0", + "@types/glob": "^8.1.0", "@types/mocha": "8.2.3", "@typescript-eslint/eslint-plugin": "4.33.0", "@typescript-eslint/parser": "4.33.0", + "bs58": "^6.0.0", "chai": "4.4.1", "eslint": "7.32.0", "eslint-config-prettier": "8.3.0", "eslint-plugin-prettier": "3.4.0", + "glob": "^11.0.0", "husky": "7.0.4", + "mocha": "^11.0.1", "prettier": "3.0.1", + "ts-mocha": "^10.0.0", "typedoc": "0.23.23", "typescript": "4.9.5", "@pythnetwork/price-service-client": "1.9.0" diff --git a/programs/drift/src/error.rs b/programs/drift/src/error.rs index 5c2d229ab3..0cc98b1657 100644 --- a/programs/drift/src/error.rs +++ b/programs/drift/src/error.rs @@ -621,6 +621,12 @@ pub enum ErrorCode { InvalidPythLazerMessage, #[msg("Pyth lazer message does not correspond to correct fed id")] PythLazerMessagePriceFeedMismatch, + #[msg("FailedZerofiCPI")] + FailedZerofiCPI, + #[msg("InvalidZerofiProgram")] + InvalidZerofiProgram, + #[msg("InvalidZerofiMarket")] + InvalidZerofiMarket, } #[macro_export] diff --git a/programs/drift/src/instructions/admin.rs b/programs/drift/src/instructions/admin.rs index 7fad00f109..e15db3a50f 100644 --- a/programs/drift/src/instructions/admin.rs +++ b/programs/drift/src/instructions/admin.rs @@ -41,6 +41,7 @@ use crate::state::fulfillment_params::phoenix::PhoenixMarketContext; use crate::state::fulfillment_params::phoenix::PhoenixV1FulfillmentConfig; use crate::state::fulfillment_params::serum::SerumContext; use crate::state::fulfillment_params::serum::SerumV3FulfillmentConfig; +use crate::state::fulfillment_params::zerofi::{ZerofiContext, ZerofiFulfillmentConfig}; use crate::state::high_leverage_mode_config::HighLeverageModeConfig; use crate::state::insurance_fund_stake::ProtocolIfSharesTransferConfig; use crate::state::oracle::get_sb_on_demand_price; @@ -631,6 +632,60 @@ pub fn handle_update_phoenix_fulfillment_config_status( Ok(()) } +pub fn handle_initialize_zerofi_fulfillment_config( + ctx: Context, + market_index: u16, +) -> Result<()> { + validate!( + market_index != QUOTE_SPOT_MARKET_INDEX, + ErrorCode::InvalidSpotMarketAccount, + "Cannot add zerofi market to quote asset" + )?; + + let base_spot_market = load!(&ctx.accounts.base_spot_market)?; + let quote_spot_market = load!(&ctx.accounts.quote_spot_market)?; + + let zerofi_program_id = crate::state::fulfillment_params::zerofi::zerofi_program_id::id(); + + validate!( + ctx.accounts.zerofi_program.key() == zerofi_program_id, + ErrorCode::InvalidZerofiProgram + )?; + + let zerofi_market_context = ZerofiContext { + zerofi_program: &ctx.accounts.zerofi_program, + zerofi_market: &ctx.accounts.zerofi_market, + }; + let market = zerofi_market_context.load_zerofi_market()?; + validate!( + market.mint_base == base_spot_market.mint, + ErrorCode::InvalidZerofiMarket, + "Invalid base mint" + )?; + + validate!( + market.mint_quote == quote_spot_market.mint, + ErrorCode::InvalidZerofiMarket, + "Invalid quote mint" + )?; + + let zerofi_fulfillment_config_key = ctx.accounts.zerofi_fulfillment_config.key(); + let mut zerofi_fulfillment_config = ctx.accounts.zerofi_fulfillment_config.load_init()?; + *zerofi_fulfillment_config = zerofi_market_context + .to_zerofi_fulfillment_config(&zerofi_fulfillment_config_key, market_index)?; + Ok(()) +} + +pub fn handle_update_zerofi_fulfillment_config_status( + ctx: Context, + status: SpotFulfillmentConfigStatus, +) -> Result<()> { + let mut config = load_mut!(ctx.accounts.zerofi_fulfillment_config)?; + msg!("config.status {:?} -> {:?}", config.status, status); + config.status = status; + Ok(()) +} + pub fn handle_initialize_perp_market( ctx: Context, market_index: u16, @@ -4457,6 +4512,59 @@ pub struct UpdatePhoenixFulfillmentConfig<'info> { pub admin: Signer<'info>, } +#[derive(Accounts)] +#[instruction(market_index: u16)] +pub struct InitializeZerofiFulfillmentConfig<'info> { + #[account( + seeds = [b"spot_market", market_index.to_le_bytes().as_ref()], + bump, + )] + pub base_spot_market: AccountLoader<'info, SpotMarket>, + #[account( + seeds = [b"spot_market", 0_u16.to_le_bytes().as_ref()], + bump, + )] + pub quote_spot_market: AccountLoader<'info, SpotMarket>, + #[account( + mut, + has_one = admin + )] + pub state: Box>, + /// CHECK: checked in ix + pub zerofi_program: AccountInfo<'info>, + /// CHECK: checked in ix + pub zerofi_market: AccountInfo<'info>, + #[account( + constraint = state.signer.eq(&drift_signer.key()) + )] + /// CHECK: program signer + pub drift_signer: AccountInfo<'info>, + #[account( + init, + seeds = [b"zerofi_fulfillment_config".as_ref(), zerofi_market.key.as_ref()], + space = ZerofiFulfillmentConfig::SIZE, + bump, + payer = admin, + )] + pub zerofi_fulfillment_config: AccountLoader<'info, ZerofiFulfillmentConfig>, + #[account(mut)] + pub admin: Signer<'info>, + pub rent: Sysvar<'info, Rent>, + pub system_program: Program<'info, System>, +} + +#[derive(Accounts)] +pub struct UpdateZerofiFulfillmentConfig<'info> { + #[account( + has_one = admin + )] + pub state: Box>, + #[account(mut)] + pub zerofi_fulfillment_config: AccountLoader<'info, ZerofiFulfillmentConfig>, + #[account(mut)] + pub admin: Signer<'info>, +} + #[derive(Accounts)] pub struct UpdateSerumVault<'info> { #[account( diff --git a/programs/drift/src/instructions/keeper.rs b/programs/drift/src/instructions/keeper.rs index 8685c6380a..06a8693a99 100644 --- a/programs/drift/src/instructions/keeper.rs +++ b/programs/drift/src/instructions/keeper.rs @@ -37,6 +37,7 @@ use crate::state::fulfillment_params::drift::MatchFulfillmentParams; use crate::state::fulfillment_params::openbook_v2::OpenbookV2FulfillmentParams; use crate::state::fulfillment_params::phoenix::PhoenixFulfillmentParams; use crate::state::fulfillment_params::serum::SerumFulfillmentParams; +use crate::state::fulfillment_params::zerofi::ZerofiFulfillmentParams; use crate::state::high_leverage_mode_config::HighLeverageModeConfig; use crate::state::insurance_fund_stake::InsuranceFundStake; use crate::state::oracle_map::OracleMap; @@ -186,6 +187,7 @@ pub enum SpotFulfillmentType { Match, PhoenixV1, OpenbookV2, + Zerofi, } #[access_control( @@ -283,6 +285,17 @@ fn fill_spot_order<'c: 'info, 'info>( clock.unix_timestamp, )?) } + SpotFulfillmentType::Zerofi => { + let base_market = spot_market_map.get_ref(&market_index)?; + let quote_market = spot_market_map.get_quote_spot_market()?; + Box::new(ZerofiFulfillmentParams::new( + remaining_accounts_iter, + &ctx.accounts.state, + &base_market, + "e_market, + clock.unix_timestamp, + )?) + } SpotFulfillmentType::Match => { let base_market = spot_market_map.get_ref(&market_index)?; let quote_market = spot_market_map.get_quote_spot_market()?; diff --git a/programs/drift/src/instructions/user.rs b/programs/drift/src/instructions/user.rs index 90e579fb3a..c0178f3a37 100644 --- a/programs/drift/src/instructions/user.rs +++ b/programs/drift/src/instructions/user.rs @@ -52,6 +52,7 @@ use crate::state::fulfillment_params::drift::MatchFulfillmentParams; use crate::state::fulfillment_params::openbook_v2::OpenbookV2FulfillmentParams; use crate::state::fulfillment_params::phoenix::PhoenixFulfillmentParams; use crate::state::fulfillment_params::serum::SerumFulfillmentParams; +use crate::state::fulfillment_params::zerofi::ZerofiFulfillmentParams; use crate::state::high_leverage_mode_config::HighLeverageModeConfig; use crate::state::oracle::StrictOraclePrice; use crate::state::order_params::RFQMatch; @@ -1747,6 +1748,17 @@ pub fn handle_place_and_take_spot_order<'c: 'info, 'info>( clock.unix_timestamp, )?) } + SpotFulfillmentType::Zerofi => { + let base_market = spot_market_map.get_ref(&market_index)?; + let quote_market = spot_market_map.get_quote_spot_market()?; + Box::new(ZerofiFulfillmentParams::new( + remaining_accounts_iter, + &ctx.accounts.state, + &base_market, + "e_market, + clock.unix_timestamp, + )?) + } SpotFulfillmentType::Match => { let base_market = spot_market_map.get_ref(&market_index)?; let quote_market = spot_market_map.get_quote_spot_market()?; @@ -1895,6 +1907,17 @@ pub fn handle_place_and_make_spot_order<'c: 'info, 'info>( clock.unix_timestamp, )?) } + SpotFulfillmentType::Zerofi => { + let base_market = spot_market_map.get_ref(&market_index)?; + let quote_market = spot_market_map.get_quote_spot_market()?; + Box::new(ZerofiFulfillmentParams::new( + remaining_accounts_iter, + &ctx.accounts.state, + &base_market, + "e_market, + clock.unix_timestamp, + )?) + } SpotFulfillmentType::Match => { let base_market = spot_market_map.get_ref(&market_index)?; let quote_market = spot_market_map.get_quote_spot_market()?; diff --git a/programs/drift/src/lib.rs b/programs/drift/src/lib.rs index cde40ba8d0..44510d94ab 100644 --- a/programs/drift/src/lib.rs +++ b/programs/drift/src/lib.rs @@ -813,6 +813,7 @@ pub mod drift { ) -> Result<()> { handle_update_openbook_v2_fulfillment_config_status(ctx, status) } + pub fn initialize_phoenix_fulfillment_config( ctx: Context, market_index: u16, @@ -827,6 +828,20 @@ pub mod drift { handle_update_phoenix_fulfillment_config_status(ctx, status) } + pub fn initialize_zerofi_fulfillment_config( + ctx: Context, + market_index: u16, + ) -> Result<()> { + handle_initialize_zerofi_fulfillment_config(ctx, market_index) + } + + pub fn zerofi_fulfillment_config_status( + ctx: Context, + status: SpotFulfillmentConfigStatus, + ) -> Result<()> { + handle_update_zerofi_fulfillment_config_status(ctx, status) + } + pub fn update_serum_vault(ctx: Context) -> Result<()> { handle_update_serum_vault(ctx) } diff --git a/programs/drift/src/state/events.rs b/programs/drift/src/state/events.rs index c94150bf92..ba0ea7fbd9 100644 --- a/programs/drift/src/state/events.rs +++ b/programs/drift/src/state/events.rs @@ -345,6 +345,7 @@ pub enum OrderActionExplanation { OrderFilledWithLPJit, DeriskLp, OrderFilledWithOpenbookV2, + OrderFilledWithZerofi, } #[event] diff --git a/programs/drift/src/state/fulfillment_params/mod.rs b/programs/drift/src/state/fulfillment_params/mod.rs index dc1fae2416..13960d64a2 100644 --- a/programs/drift/src/state/fulfillment_params/mod.rs +++ b/programs/drift/src/state/fulfillment_params/mod.rs @@ -2,3 +2,4 @@ pub mod drift; pub mod openbook_v2; pub mod phoenix; pub mod serum; +pub mod zerofi; diff --git a/programs/drift/src/state/fulfillment_params/zerofi.rs b/programs/drift/src/state/fulfillment_params/zerofi.rs new file mode 100644 index 0000000000..9aa5a77e09 --- /dev/null +++ b/programs/drift/src/state/fulfillment_params/zerofi.rs @@ -0,0 +1,457 @@ +#![allow(unused)] // unused when target_os is not solana +use crate::controller::position::PositionDirection; +use crate::error::{DriftResult, ErrorCode}; +use crate::instructions::SpotFulfillmentType; +use crate::math::casting::Cast; +use crate::math::constants::PRICE_TO_QUOTE_PRECISION_RATIO; +use crate::math::safe_math::SafeMath; +use crate::math::serum::{ + calculate_price_from_serum_limit_price, calculate_serum_limit_price, + calculate_serum_max_coin_qty, +}; +use crate::math::spot_withdraw::validate_spot_market_vault_amount; +use crate::signer::get_signer_seeds; +use crate::state::events::OrderActionExplanation; +use crate::state::load_ref::load_ref; +use crate::state::spot_fulfillment_params::{ExternalSpotFill, SpotFulfillmentParams}; +use crate::state::spot_market::{SpotBalanceType, SpotFulfillmentConfigStatus, SpotMarket}; +use crate::state::state::State; +use crate::state::traits::Size; +use crate::{load, load_mut, validate}; +use anchor_lang::prelude::*; +use anchor_lang::prelude::{Account, Program, System}; +use anchor_lang::{account, Discriminator, InstructionData, Key}; +use anchor_spl::token::{Token, TokenAccount}; +use arrayref::array_ref; +use solana_program::account_info::AccountInfo; +use solana_program::instruction::{AccountMeta, Instruction}; +use solana_program::program::invoke_signed_unchecked; +use solana_program::pubkey::Pubkey; +use std::cell::Ref; +use std::convert::TryFrom; + +pub mod zerofi_program_id { + anchor_lang::declare_id!("ZERor4xhbUycZ6gb9ntrhqscUcZmAbQDjEAtCf4hbZY"); +} + +#[account(zero_copy(unsafe))] +#[derive(Default, PartialEq, Eq, Debug)] +#[repr(C)] +pub struct ZerofiFulfillmentConfig { + pub pubkey: Pubkey, // 32 + pub zerofi_program_id: Pubkey, // 64 + pub zerofi_market: Pubkey, // 96 + pub zerofi_vault_base: Pubkey, // 128 + pub zerofi_vault_base_info: Pubkey, // 160 + pub zerofi_vault_quote: Pubkey, // 192 + pub zerofi_vault_quote_info: Pubkey, // 224 + pub market_index: u16, // 256 + pub fulfillment_type: SpotFulfillmentType, // 258 + pub status: SpotFulfillmentConfigStatus, // 259 + pub padding: [u8; 4], // 260 +} + +impl Size for ZerofiFulfillmentConfig { + const SIZE: usize = 264; +} + +pub struct ZerofiContext<'a, 'b> { + pub zerofi_program: &'a AccountInfo<'b>, + pub zerofi_market: &'a AccountInfo<'b>, +} + +#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +#[repr(C)] +pub struct Market { + pub discriminator: u64, + pub _config_authority: Pubkey, + pub _update_authority: Pubkey, + pub mint_base: Pubkey, + pub mint_quote: Pubkey, + pub vault_base: Pubkey, + pub vault_base_info: Pubkey, + pub vault_quote: Pubkey, + pub vault_quote_info: Pubkey, +} + +impl Market { + pub fn load_ref<'a>(account_info: &'a AccountInfo) -> Result> { + use anchor_lang::error::ErrorCode; + let data = account_info.try_borrow_data()?; + let market: Ref = Ref::map(data, |data| { + bytemuck::from_bytes(&data[..std::mem::size_of::()]) + }); + if market.discriminator != 4 { + return Err(ErrorCode::AccountDiscriminatorMismatch.into()); + } + Ok(market) + } +} + +impl<'a, 'b> ZerofiContext<'a, 'b> { + pub fn load_zerofi_market(&self) -> DriftResult> { + let market = + Market::load_ref(self.zerofi_market).map_err(|_| ErrorCode::FailedZerofiCPI)?; + Ok(market) + } + + pub fn to_zerofi_fulfillment_config( + &self, + zerofi_fulfillment_config_key: &Pubkey, + market_index: u16, + ) -> DriftResult { + let market = self + .load_zerofi_market() + .map_err(|_| ErrorCode::FailedZerofiCPI)?; + Ok(ZerofiFulfillmentConfig { + pubkey: *zerofi_fulfillment_config_key, + zerofi_program_id: *self.zerofi_program.key, + zerofi_market: *self.zerofi_market.key, + zerofi_vault_base: market.vault_base, + zerofi_vault_base_info: market.vault_base_info, + zerofi_vault_quote: market.vault_quote, + zerofi_vault_quote_info: market.vault_quote_info, + market_index, + fulfillment_type: SpotFulfillmentType::Zerofi, + status: SpotFulfillmentConfigStatus::Enabled, + padding: [0; 4], + }) + } +} + +pub struct ZerofiFulfillmentParams<'a, 'b> { + pub drift_signer: &'a AccountInfo<'b>, // same as penalty payer + pub zerofi_context: ZerofiContext<'a, 'b>, + pub zerofi_vault_base: &'a AccountInfo<'b>, + pub zerofi_vault_base_info: &'a AccountInfo<'b>, + pub zerofi_vault_quote: &'a AccountInfo<'b>, + pub zerofi_vault_quote_info: &'a AccountInfo<'b>, + pub base_market_vault: Box>, + pub quote_market_vault: Box>, + pub token_program: Program<'b, Token>, + pub instructions_sysvar: &'a AccountInfo<'b>, + pub signer_nonce: u8, + pub now: i64, + pub base_precision: u64, +} + +impl<'a, 'b> ZerofiFulfillmentParams<'a, 'b> { + #[allow(clippy::type_complexity)] + pub fn new<'c: 'b>( + account_info_iter: &'a mut std::iter::Peekable>>, + state: &State, + base_market: &SpotMarket, + quote_market: &SpotMarket, + now: i64, + ) -> DriftResult { + let account_info_vec = account_info_iter.collect::>(); + let account_infos = array_ref![account_info_vec, 0, 12]; + let [zerofi_fulfillment_config, drift_signer, zerofi_program, zerofi_market, zerofi_vault_base_info, zerofi_vault_quote_info, zerofi_vault_base, zerofi_vault_quote, base_market_vault, quote_market_vault, token_program, instructions_sysvar] = + account_infos; + + validate!( + &state.signer == drift_signer.key, + ErrorCode::InvalidFulfillmentConfig + )?; + validate!( + &base_market.vault == base_market_vault.key, + ErrorCode::InvalidFulfillmentConfig + )?; + validate!( + "e_market.vault == quote_market_vault.key, + ErrorCode::InvalidFulfillmentConfig + )?; + + let zerofi_fulfillment_config_loader: AccountLoader = + AccountLoader::try_from(zerofi_fulfillment_config).map_err(|e| { + msg!("{:?}", e); + ErrorCode::InvalidFulfillmentConfig + })?; + let zerofi_fulfillment_config = load!(zerofi_fulfillment_config_loader)?; + + validate!( + zerofi_fulfillment_config.market_index == base_market.market_index, + ErrorCode::InvalidFulfillmentConfig, + "config market index {} does not equal base asset index {}", + zerofi_fulfillment_config.market_index, + base_market.market_index + )?; + + validate!( + zerofi_market.key == &zerofi_fulfillment_config.zerofi_market, + ErrorCode::InvalidFulfillmentConfig, + "Zerofi market key does not match" + )?; + + // loading market data, validating discriminator + let market = Market::load_ref(zerofi_market).map_err(|_| ErrorCode::FailedZerofiCPI)?; + + validate!( + zerofi_fulfillment_config.status == SpotFulfillmentConfigStatus::Enabled, + ErrorCode::SpotFulfillmentConfigDisabled + )?; + + validate!( + &zerofi_fulfillment_config.zerofi_program_id == zerofi_market.owner, + ErrorCode::FailedZerofiCPI, + "market owner {} needs to be equal to {}!", + zerofi_market.owner, + zerofi_fulfillment_config.zerofi_program_id + ); + validate!( + &zerofi_fulfillment_config.zerofi_program_id == zerofi_program.key, + ErrorCode::InvalidFulfillmentConfig + )?; + + validate!( + &market.vault_base_info == zerofi_vault_base_info.key, + ErrorCode::InvalidFulfillmentConfig, + "Zerofi vault info base key does not match" + )?; + + validate!( + &market.vault_quote_info == zerofi_vault_quote_info.key, + ErrorCode::InvalidFulfillmentConfig, + "Zerofi vault info quote key does not match" + )?; + + validate!( + &market.vault_base == zerofi_vault_base.key, + ErrorCode::InvalidFulfillmentConfig, + "Zerofi quote vault key does not match" + )?; + + validate!( + &market.vault_quote == zerofi_vault_quote.key, + ErrorCode::InvalidFulfillmentConfig, + "Zerofi quote vault key does not match" + )?; + + let base_market_vault: Box> = + Box::new(Account::try_from(base_market_vault).map_err(|e| { + msg!("{:?}", e); + ErrorCode::InvalidFulfillmentConfig + })?); + let quote_market_vault: Box> = + Box::new(Account::try_from(quote_market_vault).map_err(|e| { + msg!("{:?}", e); + ErrorCode::InvalidFulfillmentConfig + })?); + + validate!( + market.mint_quote == quote_market_vault.mint, + ErrorCode::InvalidFulfillmentConfig + )?; + validate!( + market.mint_base == base_market_vault.mint, + ErrorCode::InvalidFulfillmentConfig + )?; + + let token_program: Program = Program::try_from(*token_program).map_err(|e| { + msg!("{:?}", e); + ErrorCode::InvalidFulfillmentConfig + })?; + validate!( + instructions_sysvar.key == &solana_program::sysvar::instructions::ID, + ErrorCode::InvalidFulfillmentConfig + )?; + Ok(ZerofiFulfillmentParams { + drift_signer, + zerofi_context: ZerofiContext { + zerofi_program, + zerofi_market, + }, + zerofi_vault_base_info, + zerofi_vault_quote_info, + zerofi_vault_base, + zerofi_vault_quote, + base_market_vault, + quote_market_vault, + token_program, + instructions_sysvar, + signer_nonce: state.signer_nonce, + now, + base_precision: base_market.get_precision(), + }) + } +} + +impl<'a, 'b> ZerofiFulfillmentParams<'a, 'b> { + pub fn invoke_swap(&self, is_base_to_quote: bool, data: Vec) -> DriftResult { + let ctx = &self.zerofi_context; + let accounts = if is_base_to_quote { + vec![ + AccountMeta::new(*ctx.zerofi_market.key, false), + AccountMeta::new(*self.zerofi_vault_base_info.key, false), + AccountMeta::new(*self.zerofi_vault_base.key, false), + AccountMeta::new(*self.zerofi_vault_quote_info.key, false), + AccountMeta::new(*self.zerofi_vault_quote.key, false), + AccountMeta::new(self.base_market_vault.key(), false), + AccountMeta::new(self.quote_market_vault.key(), false), + AccountMeta::new(*self.drift_signer.key, true), + AccountMeta::new_readonly(*self.token_program.key, false), + AccountMeta::new_readonly(*self.instructions_sysvar.key, false), + ] + } else { + vec![ + AccountMeta::new(*ctx.zerofi_market.key, false), + AccountMeta::new(*self.zerofi_vault_quote_info.key, false), + AccountMeta::new(*self.zerofi_vault_quote.key, false), + AccountMeta::new(*self.zerofi_vault_base_info.key, false), + AccountMeta::new(*self.zerofi_vault_base.key, false), + AccountMeta::new(self.quote_market_vault.key(), false), + AccountMeta::new(self.base_market_vault.key(), false), + AccountMeta::new(*self.drift_signer.key, true), + AccountMeta::new_readonly(*self.token_program.key, false), + AccountMeta::new_readonly(*self.instructions_sysvar.key, false), + ] + }; + let account_infos = vec![ + ctx.zerofi_program.clone(), + ctx.zerofi_market.clone(), + self.zerofi_vault_base_info.clone(), + self.zerofi_vault_base.clone(), + self.zerofi_vault_quote_info.clone(), + self.zerofi_vault_quote.clone(), + self.base_market_vault.to_account_info(), + self.quote_market_vault.to_account_info(), + self.drift_signer.clone(), + self.token_program.to_account_info(), + self.instructions_sysvar.to_account_info(), + ]; + let swap_instruction = Instruction { + program_id: *ctx.zerofi_program.key, + accounts, + data, + }; + let signer_seeds = get_signer_seeds(&self.signer_nonce); + let signers_seeds = &[&signer_seeds[..]]; + + invoke_signed_unchecked(&swap_instruction, &account_infos, signers_seeds).map_err(|e| { + msg!("{:?}", e); + ErrorCode::FailedZerofiCPI + })?; + + Ok(()) + } +} + +impl<'a, 'b> SpotFulfillmentParams for ZerofiFulfillmentParams<'a, 'b> { + fn is_external(&self) -> bool { + true + } + fn fulfill_order( + &mut self, + taker_direction: PositionDirection, + taker_price: u64, + taker_base_asset_amount: u64, + taker_max_quote_asset_amount: u64, + ) -> DriftResult { + let market = self.zerofi_context.load_zerofi_market()?; + + // According to calculate_fill_price(), this is how taker_price works + let taker_quote_asset_amount: u64 = taker_price + .cast::()? + .safe_mul(taker_base_asset_amount.cast()?)? + .safe_div(self.base_precision.cast()?)? + .cast::()?; + + let is_base_to_quote = taker_direction == PositionDirection::Short; + let (in_amount, out_amount) = if !is_base_to_quote { + let max_quote_in = taker_quote_asset_amount.min(taker_max_quote_asset_amount); + (max_quote_in, taker_base_asset_amount) + } else { + (taker_base_asset_amount, taker_quote_asset_amount) + }; + + let mut args = vec![0u8; 17]; + args[0] = 6; + args[1..9].copy_from_slice(&in_amount.to_le_bytes()); + args[9..17].copy_from_slice(&out_amount.to_le_bytes()); + + let base_before = self.base_market_vault.amount; + let quote_before = self.quote_market_vault.amount; + + self.invoke_swap(is_base_to_quote, args)?; + + self.base_market_vault.reload().map_err(|_e| { + msg!("Failed to reload base_market_vault"); + ErrorCode::FailedZerofiCPI + })?; + self.quote_market_vault.reload().map_err(|_e| { + msg!("Failed to reload quote_market_vault"); + ErrorCode::FailedZerofiCPI + })?; + + let base_after = self.base_market_vault.amount; + let quote_after = self.quote_market_vault.amount; + + let (base_update_direction, base_asset_amount_filled) = if base_after > base_before { + (SpotBalanceType::Deposit, base_after.safe_sub(base_before)?) + } else { + (SpotBalanceType::Borrow, base_before.safe_sub(base_after)?) + }; + + if base_asset_amount_filled == 0 { + msg!("No base filled on zerofi"); + return Ok(ExternalSpotFill::empty()); + } + + let (quote_update_direction, quote_asset_amount_filled) = + if base_update_direction == SpotBalanceType::Borrow { + let quote_asset_amount_delta = quote_after.safe_sub(quote_before)?; + (SpotBalanceType::Deposit, quote_asset_amount_delta) + } else { + let quote_asset_amount_delta = quote_before.safe_sub(quote_after)?; + (SpotBalanceType::Borrow, quote_asset_amount_delta) + }; + + Ok(ExternalSpotFill { + base_asset_amount_filled, + quote_asset_amount_filled, + base_update_direction, + quote_update_direction, + fee: 0, + unsettled_referrer_rebate: 0, + settled_referrer_rebate: 0, + }) + } + + fn get_best_bid_and_ask(&self) -> DriftResult<(Option, Option)> { + Ok((None, None)) + } + + fn get_order_action_explanation(&self) -> DriftResult { + Ok(OrderActionExplanation::OrderFilledWithZerofi) + } + + fn validate_vault_amounts( + &self, + base_market: &Ref, + quote_market: &Ref, + ) -> DriftResult { + validate_spot_market_vault_amount(base_market, self.base_market_vault.amount)?; + validate_spot_market_vault_amount(quote_market, self.quote_market_vault.amount)?; + Ok(()) + } + + fn validate_markets( + &self, + base_market: &SpotMarket, + quote_market: &SpotMarket, + ) -> DriftResult<()> { + validate!( + self.base_market_vault.mint == base_market.mint, + ErrorCode::DefaultError, + "base mints dont match" + )?; + + validate!( + self.quote_market_vault.mint == quote_market.mint, + ErrorCode::DefaultError, + "base mints dont match" + )?; + + Ok(()) + } +} diff --git a/sdk/src/addresses/pda.ts b/sdk/src/addresses/pda.ts index 793ade9c77..6c8a0dacd3 100644 --- a/sdk/src/addresses/pda.ts +++ b/sdk/src/addresses/pda.ts @@ -271,6 +271,19 @@ export function getOpenbookV2FulfillmentConfigPublicKey( )[0]; } +export function getZerofiFulfillmentConfigPublicKey( + programId: PublicKey, + market: PublicKey +): PublicKey { + return PublicKey.findProgramAddressSync( + [ + Buffer.from(anchor.utils.bytes.utf8.encode('zerofi_fulfillment_config')), + market.toBuffer(), + ], + programId + )[0]; +} + export function getReferrerNamePublicKeySync( programId: PublicKey, nameBuffer: number[] diff --git a/sdk/src/adminClient.ts b/sdk/src/adminClient.ts index dde5aea08f..ce71650d25 100644 --- a/sdk/src/adminClient.ts +++ b/sdk/src/adminClient.ts @@ -35,6 +35,7 @@ import { getHighLeverageModeConfigPublicKey, getPythLazerOraclePublicKey, getProtectedMakerModeConfigPublicKey, + getZerofiFulfillmentConfigPublicKey, } from './addresses/pda'; import { squareRootBN } from './math/utils'; import { TOKEN_PROGRAM_ID } from '@solana/spl-token'; @@ -57,6 +58,10 @@ const OPENBOOK_PROGRAM_ID = new PublicKey( 'opnb2LAfJYbRMAHHvqjCwQxanZn7ReEHp1k81EohpZb' ); +const ZEROFI_PROGRAM_ID = new PublicKey( + 'ZERor4xhbUycZ6gb9ntrhqscUcZmAbQDjEAtCf4hbZY' +); + export class AdminClient extends DriftClient { public async initialize( usdcMint: PublicKey, @@ -442,6 +447,52 @@ export class AdminClient extends DriftClient { ); } + public async initializeZerofiFulfillmentConfig( + marketIndex: number, + zerofiMarket: PublicKey + ): Promise { + const initializeIx = await this.getInitializeZerofiFulfillmentConfigIx( + marketIndex, + zerofiMarket + ); + + const tx = await this.buildTransaction(initializeIx); + + const { txSig } = await this.sendTransaction(tx, [], this.opts); + + return txSig; + } + + public async getInitializeZerofiFulfillmentConfigIx( + marketIndex: number, + zerofiMarket: PublicKey + ): Promise { + const zerofiFulfillmentConfig = getZerofiFulfillmentConfigPublicKey( + this.program.programId, + zerofiMarket + ); + + return this.program.instruction.initializeZerofiFulfillmentConfig( + marketIndex, + { + accounts: { + baseSpotMarket: this.getSpotMarketAccount(marketIndex).pubkey, + quoteSpotMarket: this.getQuoteSpotMarketAccount().pubkey, + state: await this.getStatePublicKey(), + zerofiProgram: ZEROFI_PROGRAM_ID, + zerofiMarket: zerofiMarket, + driftSigner: this.getSignerPublicKey(), + zerofiFulfillmentConfig: zerofiFulfillmentConfig, + admin: this.isSubscribed + ? this.getStateAccount().admin + : this.wallet.publicKey, + rent: SYSVAR_RENT_PUBKEY, + systemProgram: anchor.web3.SystemProgram.programId, + }, + } + ); + } + public async initializePerpMarket( marketIndex: number, priceOracle: PublicKey, diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index fa19604d90..e6def3e194 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -58,6 +58,7 @@ import { TxParams, UserAccount, UserStatsAccount, + ZerofiFulfillmentConfigAccount, } from './types'; import driftIDL from './idl/drift.json'; @@ -104,6 +105,7 @@ import { getUserAccountPublicKey, getUserAccountPublicKeySync, getUserStatsAccountPublicKey, + getZerofiFulfillmentConfigPublicKey, } from './addresses/pda'; import { DataAndSlot, @@ -681,6 +683,27 @@ export class DriftClient { ) as OpenbookV2FulfillmentConfigAccount[]; } + public async getZerofiFulfillmentConfig( + zerofiMarket: PublicKey + ): Promise { + const address = getZerofiFulfillmentConfigPublicKey( + this.program.programId, + zerofiMarket + ); + return (await this.program.account.zerofiFulfillmentConfig.fetch( + address + )) as ZerofiFulfillmentConfigAccount; + } + + public async getZerofiFulfillmentConfigs(): Promise< + ZerofiFulfillmentConfigAccount[] + > { + const accounts = await this.program.account.zerofiFulfillmentConfig.all(); + return accounts.map( + (account) => account.account + ) as ZerofiFulfillmentConfigAccount[]; + } + public async fetchMarketLookupTableAccount(): Promise { if (this.lookupTableAccount) return this.lookupTableAccount; @@ -4218,7 +4241,8 @@ export class DriftClient { fulfillmentConfig?: | SerumV3FulfillmentConfigAccount | PhoenixV1FulfillmentConfigAccount - | OpenbookV2FulfillmentConfigAccount, + | OpenbookV2FulfillmentConfigAccount + | ZerofiFulfillmentConfigAccount, makerInfo?: MakerInfo | MakerInfo[], referrerInfo?: ReferrerInfo, txParams?: TxParams @@ -4248,7 +4272,8 @@ export class DriftClient { fulfillmentConfig?: | SerumV3FulfillmentConfigAccount | PhoenixV1FulfillmentConfigAccount - | OpenbookV2FulfillmentConfigAccount, + | OpenbookV2FulfillmentConfigAccount + | ZerofiFulfillmentConfigAccount, makerInfo?: MakerInfo | MakerInfo[], referrerInfo?: ReferrerInfo, fillerPublicKey?: PublicKey @@ -4328,6 +4353,7 @@ export class DriftClient { | SerumV3FulfillmentConfigAccount | PhoenixV1FulfillmentConfigAccount | OpenbookV2FulfillmentConfigAccount + | ZerofiFulfillmentConfigAccount ): void { if (fulfillmentConfig) { if ('serumProgramId' in fulfillmentConfig) { @@ -4348,6 +4374,12 @@ export class DriftClient { remainingAccounts, fulfillmentConfig ); + } else if ('zerofiProgramId' in fulfillmentConfig) { + this.addZerofiRemainingAccounts( + marketIndex, + remainingAccounts, + fulfillmentConfig + ); } else { throw Error('Invalid fulfillment config type'); } @@ -4610,6 +4642,73 @@ export class DriftClient { } } + addZerofiRemainingAccounts( + marketIndex: number, + remainingAccounts: AccountMeta[], + fulfillmentConfig: ZerofiFulfillmentConfigAccount + ): void { + remainingAccounts.push({ + pubkey: fulfillmentConfig.pubkey, + isWritable: false, + isSigner: false, + }); + remainingAccounts.push({ + pubkey: this.getSignerPublicKey(), + isWritable: true, + isSigner: false, + }); + remainingAccounts.push({ + pubkey: fulfillmentConfig.zerofiProgramId, + isWritable: false, + isSigner: false, + }); + remainingAccounts.push({ + pubkey: fulfillmentConfig.zerofiMarket, + isWritable: true, + isSigner: false, + }); + remainingAccounts.push({ + pubkey: fulfillmentConfig.zerofiVaultBaseInfo, + isWritable: true, + isSigner: false, + }); + remainingAccounts.push({ + pubkey: fulfillmentConfig.zerofiVaultQuoteInfo, + isWritable: true, + isSigner: false, + }); + remainingAccounts.push({ + pubkey: fulfillmentConfig.zerofiVaultBase, + isWritable: true, + isSigner: false, + }); + remainingAccounts.push({ + pubkey: fulfillmentConfig.zerofiVaultQuote, + isWritable: true, + isSigner: false, + }); + remainingAccounts.push({ + pubkey: this.getSpotMarketAccount(marketIndex).vault, + isWritable: true, + isSigner: false, + }); + remainingAccounts.push({ + pubkey: this.getQuoteSpotMarketAccount().vault, + isWritable: true, + isSigner: false, + }); + remainingAccounts.push({ + pubkey: TOKEN_PROGRAM_ID, + isWritable: false, + isSigner: false, + }); + remainingAccounts.push({ + pubkey: SYSVAR_INSTRUCTIONS_PUBKEY, + isWritable: false, + isSigner: false, + }); + } + /** * Swap tokens in drift account using jupiter * @param jupiterClient jupiter client to find routes and jupiter instructions diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index 2478883afb..50a0bf6782 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -3786,6 +3786,95 @@ } ] }, + { + "name": "initializeZerofiFulfillmentConfig", + "accounts": [ + { + "name": "baseSpotMarket", + "isMut": false, + "isSigner": false + }, + { + "name": "quoteSpotMarket", + "isMut": false, + "isSigner": false + }, + { + "name": "state", + "isMut": true, + "isSigner": false + }, + { + "name": "zerofiProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "zerofiMarket", + "isMut": false, + "isSigner": false + }, + { + "name": "driftSigner", + "isMut": false, + "isSigner": false + }, + { + "name": "zerofiFulfillmentConfig", + "isMut": true, + "isSigner": false + }, + { + "name": "admin", + "isMut": true, + "isSigner": true + }, + { + "name": "rent", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [ + { + "name": "marketIndex", + "type": "u16" + } + ] + }, + { + "name": "zerofiFulfillmentConfigStatus", + "accounts": [ + { + "name": "state", + "isMut": false, + "isSigner": false + }, + { + "name": "zerofiFulfillmentConfig", + "isMut": true, + "isSigner": false + }, + { + "name": "admin", + "isMut": true, + "isSigner": true + } + ], + "args": [ + { + "name": "status", + "type": { + "defined": "SpotFulfillmentConfigStatus" + } + } + ] + }, { "name": "updateSerumVault", "accounts": [ @@ -6852,6 +6941,67 @@ ] } }, + { + "name": "ZerofiFulfillmentConfig", + "type": { + "kind": "struct", + "fields": [ + { + "name": "pubkey", + "type": "publicKey" + }, + { + "name": "zerofiProgramId", + "type": "publicKey" + }, + { + "name": "zerofiMarket", + "type": "publicKey" + }, + { + "name": "zerofiVaultBase", + "type": "publicKey" + }, + { + "name": "zerofiVaultBaseInfo", + "type": "publicKey" + }, + { + "name": "zerofiVaultQuote", + "type": "publicKey" + }, + { + "name": "zerofiVaultQuoteInfo", + "type": "publicKey" + }, + { + "name": "marketIndex", + "type": "u16" + }, + { + "name": "fulfillmentType", + "type": { + "defined": "SpotFulfillmentType" + } + }, + { + "name": "status", + "type": { + "defined": "SpotFulfillmentConfigStatus" + } + }, + { + "name": "padding", + "type": { + "array": [ + "u8", + 4 + ] + } + } + ] + } + }, { "name": "HighLeverageModeConfig", "type": { @@ -10770,6 +10920,9 @@ }, { "name": "OpenbookV2" + }, + { + "name": "Zerofi" } ] } @@ -11044,6 +11197,9 @@ }, { "name": "OrderFilledWithOpenbookV2" + }, + { + "name": "OrderFilledWithZerofi" } ] } @@ -14411,6 +14567,21 @@ "code": 6307, "name": "PythLazerMessagePriceFeedMismatch", "msg": "Pyth lazer message does not correspond to correct fed id" + }, + { + "code": 6308, + "name": "FailedZerofiCPI", + "msg": "FailedZerofiCPI" + }, + { + "code": 6309, + "name": "InvalidZerofiProgram", + "msg": "InvalidZerofiProgram" + }, + { + "code": 6310, + "name": "InvalidZerofiMarket", + "msg": "InvalidZerofiMarket" } ], "metadata": { diff --git a/sdk/src/types.ts b/sdk/src/types.ts index 055478e3f6..d5db116dd7 100644 --- a/sdk/src/types.ts +++ b/sdk/src/types.ts @@ -233,6 +233,9 @@ export class OrderActionExplanation { static readonly DERISK_LP = { deriskLp: {}, }; + static readonly ORDER_FILLED_WITH_ZEROFI = { + orderFilledWithZerofi: {}, + }; } export class OrderTriggerCondition { @@ -1319,6 +1322,19 @@ export type OpenbookV2FulfillmentConfigAccount = { remainingAccounts?: PublicKey[]; }; +export type ZerofiFulfillmentConfigAccount = { + pubkey: PublicKey; + zerofiProgramId: PublicKey; + zerofiMarket: PublicKey; + zerofiVaultBase: PublicKey; + zerofiVaultBaseInfo: PublicKey; + zerofiVaultQuote: PublicKey; + zerofiVaultQuoteInfo: PublicKey; + marketIndex: number; + fulfillmentType: SpotFulfillmentType; + status: SpotFulfillmentStatus; +}; + export type ReferrerNameAccount = { name: number[]; user: PublicKey; diff --git a/test-scripts/run-anchor-tests.sh b/test-scripts/run-anchor-tests.sh index ebf7fdadda..44fe68395d 100644 --- a/test-scripts/run-anchor-tests.sh +++ b/test-scripts/run-anchor-tests.sh @@ -82,9 +82,10 @@ test_files=( userDelegate.ts userOrderId.ts whitelist.ts + zerofiTest.ts ) for test_file in ${test_files[@]}; do ts-mocha -t 300000 ./tests/${test_file} || exit 1 -done \ No newline at end of file +done diff --git a/tests/fixtures/zerofi.so b/tests/fixtures/zerofi.so new file mode 100755 index 0000000000000000000000000000000000000000..fde211ef99f57fee25888cf4dd15c8d566663bce GIT binary patch literal 273800 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 WZgknZ74n~jVlSunagu6;)Bpd4fDcgs literal 0 HcmV?d00001 diff --git a/tests/fixtures/zerofi/market-G6Yn5VpiUbySimawqXEveD3RdTVsf7FzSgAJK3EABAjZ.json b/tests/fixtures/zerofi/market-G6Yn5VpiUbySimawqXEveD3RdTVsf7FzSgAJK3EABAjZ.json new file mode 100644 index 0000000000..1a15ef1788 --- /dev/null +++ b/tests/fixtures/zerofi/market-G6Yn5VpiUbySimawqXEveD3RdTVsf7FzSgAJK3EABAjZ.json @@ -0,0 +1 @@ +{"lamports":52784640,"data":[4,0,0,0,0,0,0,0,25,59,145,91,242,87,44,194,216,238,152,135,16,60,226,109,208,37,226,45,24,48,231,73,106,206,5,175,133,51,185,229,25,59,145,91,242,87,44,194,216,238,152,135,16,60,226,109,208,37,226,45,24,48,231,73,106,206,5,175,133,51,185,229,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,203,195,255,168,172,196,137,173,7,198,187,63,9,243,210,107,139,5,134,164,241,208,82,86,177,185,92,151,68,79,192,56,112,153,220,90,44,221,79,179,41,212,227,248,77,134,81,94,140,111,76,109,187,219,136,211,45,90,129,182,64,54,145,124,199,145,50,131,73,180,100,81,19,213,231,97,170,85,153,134,74,188,170,89,105,252,191,135,119,38,193,154,174,37,139,58,227,141,173,159,105,183,217,250,249,177,76,244,141,92,67,44,213,42,121,48,163,172,206,98,215,147,255,179,170,161,110,211,255,255,6,6,0,0,0,0,0,228,11,84,2,0,0,0,255,255,255,255,255,255,255,255,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,232,3,0,0,0,0,0,0,232,3,0,0,0,0,0,0,232,3,0,0,0,0,0,0,232,3,0,0,0,0,0,0,232,3,0,0,0,0,0,0,232,3,0,0,0,0,0,0,232,3,0,0,0,0,0,0,232,3,0,0,0,0,0,0,232,3,0,0,0,0,0,0,232,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,89,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,89,64,123,20,174,71,225,122,132,63,0,0,0,0,0,0,240,63,123,20,174,71,225,122,132,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,232,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,228,11,84,2,0,0,0,0,228,11,84,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,228,11,84,2,0,0,0,0,228,11,84,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"owner":[8,65,196,198,238,55,111,23,202,75,3,26,148,248,184,42,106,178,174,145,174,25,224,140,116,54,106,121,117,145,8,151],"executable":false,"rentEpoch":18446744073709551615} \ No newline at end of file diff --git a/tests/fixtures/zerofi/mint_base-1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM.json b/tests/fixtures/zerofi/mint_base-1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM.json new file mode 100644 index 0000000000..3498481f48 --- /dev/null +++ b/tests/fixtures/zerofi/mint_base-1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM.json @@ -0,0 +1 @@ +{"lamports":1,"data":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"owner":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169],"executable":false,"rentEpoch":0} \ No newline at end of file diff --git a/tests/fixtures/zerofi/mint_quote-1111111ogCyDbaRMvkdsHB3qfdyFYaG1WtRUAfdh.json b/tests/fixtures/zerofi/mint_quote-1111111ogCyDbaRMvkdsHB3qfdyFYaG1WtRUAfdh.json new file mode 100644 index 0000000000..3498481f48 --- /dev/null +++ b/tests/fixtures/zerofi/mint_quote-1111111ogCyDbaRMvkdsHB3qfdyFYaG1WtRUAfdh.json @@ -0,0 +1 @@ +{"lamports":1,"data":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"owner":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169],"executable":false,"rentEpoch":0} \ No newline at end of file diff --git a/tests/fixtures/zerofi/user_authority-DiCEXQfxHqfY7D4JHU3fbmvWmTV26F2DQzsoCySpE1Ya.json b/tests/fixtures/zerofi/user_authority-DiCEXQfxHqfY7D4JHU3fbmvWmTV26F2DQzsoCySpE1Ya.json new file mode 100644 index 0000000000..bbece8f5ed --- /dev/null +++ b/tests/fixtures/zerofi/user_authority-DiCEXQfxHqfY7D4JHU3fbmvWmTV26F2DQzsoCySpE1Ya.json @@ -0,0 +1 @@ +{"lamports":1000000000,"data":[],"owner":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"executable":false,"rentEpoch":0} \ No newline at end of file diff --git a/tests/fixtures/zerofi/user_base-11111112D1oxKts8YPdTJRG5FzxTNpMtWmq8hkVx3.json b/tests/fixtures/zerofi/user_base-11111112D1oxKts8YPdTJRG5FzxTNpMtWmq8hkVx3.json new file mode 100644 index 0000000000..5b6a75ad01 --- /dev/null +++ b/tests/fixtures/zerofi/user_base-11111112D1oxKts8YPdTJRG5FzxTNpMtWmq8hkVx3.json @@ -0,0 +1 @@ +{"lamports":1,"data":[0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,188,217,193,247,119,18,116,71,248,24,163,105,206,248,36,245,96,188,54,17,31,160,41,119,236,211,196,51,200,174,173,63,0,202,154,59,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"owner":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169],"executable":false,"rentEpoch":0} \ No newline at end of file diff --git a/tests/fixtures/zerofi/user_quote-11111112cMQwSC9qirWGjZM6gLGwW69X22mqwLLGP.json b/tests/fixtures/zerofi/user_quote-11111112cMQwSC9qirWGjZM6gLGwW69X22mqwLLGP.json new file mode 100644 index 0000000000..de90f030d2 --- /dev/null +++ b/tests/fixtures/zerofi/user_quote-11111112cMQwSC9qirWGjZM6gLGwW69X22mqwLLGP.json @@ -0,0 +1 @@ +{"lamports":1,"data":[0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,188,217,193,247,119,18,116,71,248,24,163,105,206,248,36,245,96,188,54,17,31,160,41,119,236,211,196,51,200,174,173,63,0,202,154,59,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"owner":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169],"executable":false,"rentEpoch":0} \ No newline at end of file diff --git a/tests/fixtures/zerofi/vault_base-EiR75kaWP1CUvLMuwVSg1KKcjp9YJxifbQBnQt74SFKM.json b/tests/fixtures/zerofi/vault_base-EiR75kaWP1CUvLMuwVSg1KKcjp9YJxifbQBnQt74SFKM.json new file mode 100644 index 0000000000..70de9910ec --- /dev/null +++ b/tests/fixtures/zerofi/vault_base-EiR75kaWP1CUvLMuwVSg1KKcjp9YJxifbQBnQt74SFKM.json @@ -0,0 +1 @@ +{"lamports":2039280,"data":[0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,203,195,255,168,172,196,137,173,7,198,187,63,9,243,210,107,139,5,134,164,241,208,82,86,177,185,92,151,68,79,192,56,0,202,154,59,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"owner":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169],"executable":false,"rentEpoch":18446744073709551615} \ No newline at end of file diff --git a/tests/fixtures/zerofi/vault_base_info-8aYjmYBJnwo1G9VSCfYPZDsmnnsLpPwoqpTvwuoV3TTZ.json b/tests/fixtures/zerofi/vault_base_info-8aYjmYBJnwo1G9VSCfYPZDsmnnsLpPwoqpTvwuoV3TTZ.json new file mode 100644 index 0000000000..965dc2c27e --- /dev/null +++ b/tests/fixtures/zerofi/vault_base_info-8aYjmYBJnwo1G9VSCfYPZDsmnnsLpPwoqpTvwuoV3TTZ.json @@ -0,0 +1 @@ +{"lamports":8352000,"data":[1,0,0,0,0,0,0,0,25,59,145,91,242,87,44,194,216,238,152,135,16,60,226,109,208,37,226,45,24,48,231,73,106,206,5,175,133,51,185,229,203,195,255,168,172,196,137,173,7,198,187,63,9,243,210,107,139,5,134,164,241,208,82,86,177,185,92,151,68,79,192,56,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,112,153,220,90,44,221,79,179,41,212,227,248,77,134,81,94,140,111,76,109,187,219,136,211,45,90,129,182,64,54,145,124,0,148,53,119,0,0,0,0,255,255,255,255,255,255,255,255,0,202,154,59,0,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,148,53,119,0,0,0,0,0,202,154,59,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,202,154,59,0,0,0,0,0,0,0,0,255,1,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"owner":[8,65,196,198,238,55,111,23,202,75,3,26,148,248,184,42,106,178,174,145,174,25,224,140,116,54,106,121,117,145,8,151],"executable":false,"rentEpoch":18446744073709551615} \ No newline at end of file diff --git a/tests/fixtures/zerofi/vault_quote-ES2YchtvBNn91Q8TWHfGhwqX5VdbhMmfujPRmkoRXPVT.json b/tests/fixtures/zerofi/vault_quote-ES2YchtvBNn91Q8TWHfGhwqX5VdbhMmfujPRmkoRXPVT.json new file mode 100644 index 0000000000..7d28fd52c2 --- /dev/null +++ b/tests/fixtures/zerofi/vault_quote-ES2YchtvBNn91Q8TWHfGhwqX5VdbhMmfujPRmkoRXPVT.json @@ -0,0 +1 @@ +{"lamports":2039280,"data":[0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,199,145,50,131,73,180,100,81,19,213,231,97,170,85,153,134,74,188,170,89,105,252,191,135,119,38,193,154,174,37,139,58,0,202,154,59,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"owner":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169],"executable":false,"rentEpoch":18446744073709551615} \ No newline at end of file diff --git a/tests/fixtures/zerofi/vault_quote_info-GKGrCkr12UQCQ4RPt1NyziyCqjuMLkn9JmtQrReZTzAN.json b/tests/fixtures/zerofi/vault_quote_info-GKGrCkr12UQCQ4RPt1NyziyCqjuMLkn9JmtQrReZTzAN.json new file mode 100644 index 0000000000..3a5f9c2b96 --- /dev/null +++ b/tests/fixtures/zerofi/vault_quote_info-GKGrCkr12UQCQ4RPt1NyziyCqjuMLkn9JmtQrReZTzAN.json @@ -0,0 +1 @@ +{"lamports":8352000,"data":[1,0,0,0,0,0,0,0,25,59,145,91,242,87,44,194,216,238,152,135,16,60,226,109,208,37,226,45,24,48,231,73,106,206,5,175,133,51,185,229,199,145,50,131,73,180,100,81,19,213,231,97,170,85,153,134,74,188,170,89,105,252,191,135,119,38,193,154,174,37,139,58,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,227,141,173,159,105,183,217,250,249,177,76,244,141,92,67,44,213,42,121,48,163,172,206,98,215,147,255,179,170,161,110,211,0,148,53,119,0,0,0,0,255,255,255,255,255,255,255,255,0,202,154,59,0,0,0,0,255,255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,148,53,119,0,0,0,0,0,202,154,59,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,202,154,59,0,0,0,0,0,0,0,0,255,1,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"owner":[8,65,196,198,238,55,111,23,202,75,3,26,148,248,184,42,106,178,174,145,174,25,224,140,116,54,106,121,117,145,8,151],"executable":false,"rentEpoch":18446744073709551615} \ No newline at end of file diff --git a/tests/zerofiTest.ts b/tests/zerofiTest.ts new file mode 100644 index 0000000000..73b7a153ce --- /dev/null +++ b/tests/zerofiTest.ts @@ -0,0 +1,340 @@ +import { assert } from 'chai'; +import * as anchor from '@coral-xyz/anchor'; + +import { Program, BN } from '@coral-xyz/anchor'; + +import { + OracleSource, + OrderType, + PositionDirection, + PublicKey, + TestClient, +} from '../sdk/src'; +import { startAnchor, AddedAccount } from 'solana-bankrun'; +import { TestBulkAccountLoader } from '../sdk/src/accounts/testBulkAccountLoader'; +import { BankrunContextWrapper } from '../sdk/src/bankrun/bankrunConnection'; +import { + initializeQuoteSpotMarket, + initializeSolSpotMarket, + mockOracleNoProgram, +} from './testHelpers'; +import { Keypair } from '@solana/web3.js'; +import { PRICE_PRECISION } from '../sdk/src'; +import { ZERO } from '../sdk'; +import fs from 'fs'; +import path from 'path'; +import { globSync } from 'glob'; +import bs58 from 'bs58'; + +export const ZEROFI_PROGRAM = new PublicKey( + 'ZERor4xhbUycZ6gb9ntrhqscUcZmAbQDjEAtCf4hbZY' +); + +interface AccountFile { + lamports: number; + data: number[]; + owner: number[]; + executable: boolean; + rentEpoch: number; +} + +interface ParsedAccounts { + accounts: AddedAccount[]; + nameToKey: Map; +} + +function loadFixtureAccounts(fixturesPath: string): ParsedAccounts { + const files = globSync(path.join(fixturesPath, '*.json')); + + const accounts: AddedAccount[] = []; + const nameToKey = new Map(); + + const filenamePattern = /^([^-]+)-([A-Za-z0-9]{32,44})\.json$/; + + for (const file of files) { + const basename = path.basename(file); + const match = basename.match(filenamePattern); + if (!match) { + continue; + } + + const [, name, pubkeyStr] = match; + + let pubkey: PublicKey; + try { + pubkey = new PublicKey(pubkeyStr); + } catch (e) { + console.warn(`Skipping ${file}: invalid pubkey`); + continue; + } + + try { + const content = fs.readFileSync(file, 'utf8'); + const accountData = JSON.parse(content) as AccountFile; + + const account = { + address: pubkey, + info: { + lamports: accountData.lamports, + data: Uint8Array.from(accountData.data), + owner: new PublicKey(Buffer.from(accountData.owner)), + executable: accountData.executable, + rentEpoch: accountData.rentEpoch, + }, + }; + + accounts.push(account); + nameToKey.set(name, pubkey); + } catch (e) { + console.warn(`Error processing ${file}: ${e}`); + continue; + } + } + + return { accounts, nameToKey }; +} + +describe('zerofi', () => { + const chProgram = anchor.workspace.Drift as Program; + + let driftClient: TestClient; + + let fillerDriftClient: TestClient; + const fillerKeypair = Keypair.generate(); + + let bulkAccountLoader: TestBulkAccountLoader; + + let bankrunContextWrapper: BankrunContextWrapper; + + const solSpotMarketIndex = 1; + + let usdcMint: PublicKey; + let solMint: PublicKey; + + const usdcAmount = new anchor.BN(200 * 10 ** 6); + + let userUsdcAccount: PublicKey; + + let market: PublicKey; + + before(async () => { + const { accounts: fixtureAccountList, nameToKey: fixtureAccountsByName } = + loadFixtureAccounts('tests/fixtures/zerofi'); + console.log(fixtureAccountsByName); + + const context = await startAnchor( + '', + [ + { + name: 'zerofi', + programId: ZEROFI_PROGRAM, + }, + ], + fixtureAccountList + ); + + bankrunContextWrapper = new BankrunContextWrapper(context); + + // override the wallet to be the one that's shared + const keypair = Keypair.fromSecretKey( + bs58.decode( + '4ruNgnB26rEiy9G1nBevajsjkeoUNzjVWkdrcFnQ8BKyrF2ZYQ21gDfFsfrZmXU4FkbCjQVAktkMCnoAQeyBYrYW' + ) + ); + const wallet = new anchor.Wallet(keypair); + bankrunContextWrapper.provider.wallet = wallet; + + bulkAccountLoader = new TestBulkAccountLoader( + bankrunContextWrapper.connection, + 'processed', + 1 + ); + + const solOracle = await mockOracleNoProgram(bankrunContextWrapper, 100); + + usdcMint = fixtureAccountsByName.get('mint_quote'); + solMint = fixtureAccountsByName.get('mint_base'); + userUsdcAccount = fixtureAccountsByName.get('user_quote'); + market = fixtureAccountsByName.get('market'); + + driftClient = new TestClient({ + connection: bankrunContextWrapper.connection.toConnection(), + wallet: bankrunContextWrapper.provider.wallet, + programID: chProgram.programId, + opts: { + commitment: 'confirmed', + }, + activeSubAccountId: 0, + perpMarketIndexes: [0], + spotMarketIndexes: [0, 1], + subAccountIds: [], + oracleInfos: [ + { + publicKey: solOracle, + source: OracleSource.PYTH, + }, + ], + userStats: true, + accountSubscription: { + type: 'polling', + accountLoader: bulkAccountLoader, + }, + }); + + await driftClient.initialize(usdcMint, true); + await driftClient.subscribe(); + + await initializeQuoteSpotMarket(driftClient, usdcMint); + await initializeSolSpotMarket(driftClient, solOracle, solMint); + + const quoteSizeLot = new BN(1); + const baseSizeLot = new BN(100); + await driftClient.updateSpotMarketStepSizeAndTickSize( + 1, + baseSizeLot, + quoteSizeLot + ); + + await driftClient.updateSpotMarketOrdersEnabled(1, true); + + await driftClient.initializeUserAccountAndDepositCollateral( + // @ts-ignore + usdcAmount, + userUsdcAccount + ); + + await driftClient.addUser(0); + // @ts-ignore + // await driftClient.deposit(solAmount, 1, userWSolAccount.publicKey); + + fillerDriftClient = new TestClient({ + connection: bankrunContextWrapper.connection.toConnection(), + wallet: new anchor.Wallet(fillerKeypair), + programID: chProgram.programId, + opts: { + commitment: 'confirmed', + }, + activeSubAccountId: 0, + perpMarketIndexes: [0], + spotMarketIndexes: [0, 1], + subAccountIds: [], + oracleInfos: [ + { + publicKey: solOracle, + source: OracleSource.PYTH, + }, + ], + userStats: true, + accountSubscription: { + type: 'polling', + accountLoader: bulkAccountLoader, + }, + }); + + await fillerDriftClient.subscribe(); + + await bankrunContextWrapper.fundKeypair(fillerKeypair, 10 * 10 ** 9); + + await fillerDriftClient.initializeUserAccount(); + + await fillerDriftClient.addUser(0); + }); + + after(async () => { + await driftClient.unsubscribe(); + await fillerDriftClient.unsubscribe(); + }); + + it('add market', async () => { + await driftClient.initializeZerofiFulfillmentConfig( + solSpotMarketIndex, + market + ); + }); + + it('fill long', async () => { + const quoteTokenAmountBefore = driftClient.getTokenAmount(0); + const baseTokenAmountBefore = driftClient.getTokenAmount(1); + + console.log(`quoteTokenAmountBefore ${quoteTokenAmountBefore.toString()}`); + console.log(`baseTokenAmountBefore ${baseTokenAmountBefore.toString()}`); + + await driftClient.placeSpotOrder({ + orderType: OrderType.LIMIT, + marketIndex: 1, + // @ts-ignore + baseAssetAmount: driftClient.convertToSpotPrecision(1, 1), + direction: PositionDirection.LONG, + price: PRICE_PRECISION.muln(101), + }); + + const fulfillmentConfig = await driftClient.getZerofiFulfillmentConfig( + market + ); + + const userAccount = driftClient.getUserAccount(); + const order = userAccount.orders.filter( + (order) => order.marketIndex == 1 + )[0]; + await fillerDriftClient.fillSpotOrder( + await driftClient.getUserAccountPublicKey(), + driftClient.getUserAccount(), + order, + fulfillmentConfig + ); + + await driftClient.fetchAccounts(); + + const quoteTokenAmountAfter = driftClient.getTokenAmount(0); + const baseTokenAmountAfter = driftClient.getTokenAmount(1); + + console.log(`quoteTokenAmountAfter ${quoteTokenAmountAfter.toString()}`); + console.log(`baseTokenAmountAfter ${baseTokenAmountAfter.toString()}`); + + assert(baseTokenAmountAfter.eq(new BN(1e6))); + // cost is 101 + 0.1% drift fee + assert(quoteTokenAmountAfter.eq(new BN(200e6 - 101.101001e6))); + }); + + it('fill short', async () => { + const quoteTokenAmountBefore = driftClient.getTokenAmount(0); + + await driftClient.placeSpotOrder({ + orderType: OrderType.LIMIT, + marketIndex: 1, + // @ts-ignore + baseAssetAmount: driftClient.convertToSpotPrecision(1, 1), + direction: PositionDirection.SHORT, + price: PRICE_PRECISION.muln(99), + }); + + const fulfillmentConfig = await driftClient.getZerofiFulfillmentConfig( + market + ); + + const userAccount = driftClient.getUserAccount(); + const order = userAccount.orders.filter( + (order) => order.marketIndex == 1 + )[0]; + await fillerDriftClient.fillSpotOrder( + await driftClient.getUserAccountPublicKey(), + driftClient.getUserAccount(), + order, + fulfillmentConfig + ); + + await driftClient.fetchAccounts(); + + const quoteTokenAmountAfter = driftClient.getTokenAmount(0); + const baseTokenAmountAfter = driftClient.getTokenAmount(1); + + console.log(`quoteTokenAmountAfter ${quoteTokenAmountAfter.toString()}`); + console.log(`baseTokenAmountAfter ${baseTokenAmountAfter.toString()}`); + + assert(baseTokenAmountAfter.eq(ZERO)); + // 99 - 0.1% drift fee + assert( + quoteTokenAmountAfter.eq(quoteTokenAmountBefore.add(new BN(98.901e6))) + ); + }); +}); diff --git a/yarn.lock b/yarn.lock index 3238694fb3..ca886d47ec 100644 --- a/yarn.lock +++ b/yarn.lock @@ -176,6 +176,18 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + "@js-sdsl/ordered-map@^4.4.2": version "4.4.2" resolved "https://registry.yarnpkg.com/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz#9299f82874bab9e4c7f9c48d865becbfe8d6907c" @@ -286,6 +298,11 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + "@project-serum/anchor@^0.11.1": version "0.11.1" resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.11.1.tgz#155bff2c70652eafdcfd5559c81a83bb19cec9ff" @@ -724,11 +741,29 @@ dependencies: "@types/node" "*" +"@types/glob@^8.1.0": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-8.1.0.tgz#b63e70155391b0584dce44e7ea25190bbc38f2fc" + integrity sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w== + dependencies: + "@types/minimatch" "^5.1.2" + "@types/node" "*" + "@types/json-schema@^7.0.7": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== +"@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/minimatch@^5.1.2": + version "5.1.2" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" + integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== + "@types/mocha@8.2.3": version "8.2.3" resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-8.2.3.tgz#bbeb55fbc73f28ea6de601fbfa4613f58d785323" @@ -921,7 +956,7 @@ anchor-bankrun@0.3.0: resolved "https://registry.yarnpkg.com/anchor-bankrun/-/anchor-bankrun-0.3.0.tgz#3789fcecbc201a2334cff228b99cc0da8ef0167e" integrity sha512-PYBW5fWX+iGicIS5MIM/omhk1tQPUc0ELAnI/IkLKQJ6d75De/CQRh8MF2bU/TgGyFi6zEel80wUe3uRol9RrQ== -ansi-colors@^4.1.1: +ansi-colors@^4.1.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== @@ -931,6 +966,11 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +ansi-regex@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" + integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== + ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -945,11 +985,24 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + 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" + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -967,6 +1020,11 @@ array-union@^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" @@ -1043,6 +1101,11 @@ base-x@^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, base64-js@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" @@ -1065,6 +1128,11 @@ bignumber.js@^9.0.1: 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" @@ -1106,13 +1174,18 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -braces@^3.0.3: +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== + bs58@^4.0.0, bs58@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" @@ -1127,6 +1200,18 @@ bs58@^5.0.0: 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-layout@^1.2.0, buffer-layout@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/buffer-layout/-/buffer-layout-1.2.2.tgz#b9814e7c7235783085f9ca4966a0cfff112259d5" @@ -1176,7 +1261,7 @@ camelcase@^5.3.1: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -camelcase@^6.2.1, camelcase@^6.3.0: +camelcase@^6.0.0, camelcase@^6.2.1, camelcase@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== @@ -1208,7 +1293,7 @@ chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.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== @@ -1228,6 +1313,30 @@ check-error@^1.0.3: 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" + +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" + cliui@^8.0.1: version "8.0.1" resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" @@ -1310,6 +1419,15 @@ cross-fetch@^3.1.5: dependencies: node-fetch "^2.6.12" +cross-spawn@^7.0.0: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + cross-spawn@^7.0.2: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -1340,6 +1458,18 @@ debug@^4.0.1, debug@^4.1.1, debug@^4.3.1, debug@^4.3.3, debug@^4.3.4: dependencies: ms "2.1.2" +debug@^4.3.5: + version "4.4.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" + integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== + dependencies: + ms "^2.1.3" + +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== + decimal.js@^10.4.0, decimal.js@^10.4.3: version "10.4.3" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" @@ -1385,6 +1515,16 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== +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@^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" @@ -1417,11 +1557,21 @@ dotenv@16.4.5, dotenv@^16.0.3: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + 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== +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + enquirer@^2.3.5: version "2.4.1" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.4.1.tgz#93334b3fbd74fc7097b224ab4a8fb7e40bf4ae56" @@ -1688,6 +1838,14 @@ find-process@^1.4.7: commander "^5.1.0" debug "^4.1.1" +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" + find@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/find/-/find-0.3.0.tgz#4082e8fc8d8320f1a382b5e4f521b9bc50775cb8" @@ -1704,6 +1862,11 @@ flat-cache@^3.0.4: 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" @@ -1721,6 +1884,14 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" +foreground-child@^3.1.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.0.tgz#0ac8644c06e431439f8561db8ecf29a7b5519c77" + integrity sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + form-data@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" @@ -1735,6 +1906,11 @@ fs.realpath@^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" @@ -1766,13 +1942,37 @@ get-intrinsic@^1.1.3, get-intrinsic@^1.2.4: has-symbols "^1.0.3" hasown "^2.0.0" -glob-parent@^5.1.2: +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@^10.4.5: + version "10.4.5" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" + integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" + +glob@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-11.0.0.tgz#6031df0d7b65eaa1ccb9b29b5ced16cea658e77e" + integrity sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g== + dependencies: + foreground-child "^3.1.0" + jackspeak "^4.0.1" + minimatch "^10.0.0" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^2.0.0" + glob@^7.1.3: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" @@ -1852,6 +2052,11 @@ hasown@^2.0.0: 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== + humanize-ms@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" @@ -1913,6 +2118,13 @@ is-arguments@^1.0.4: 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" @@ -1935,7 +2147,7 @@ is-generator-function@^1.0.7: dependencies: has-tostringtag "^1.0.0" -is-glob@^4.0.0, is-glob@^4.0.1: +is-glob@^4.0.0, is-glob@^4.0.1, 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== @@ -1955,6 +2167,11 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +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-retry-allowed@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-2.2.0.tgz#88f34cbd236e043e71b6932d09b0c65fb7b4d71d" @@ -1967,6 +2184,11 @@ is-typed-array@^1.1.3: dependencies: which-typed-array "^1.1.14" +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-utf8@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" @@ -1982,6 +2204,22 @@ isomorphic-ws@^4.0.1: resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc" integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== +jackspeak@^3.1.2: + version "3.4.3" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" + integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + +jackspeak@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-4.0.2.tgz#11f9468a3730c6ff6f56823a820d7e3be9bef015" + integrity sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw== + dependencies: + "@isaacs/cliui" "^8.0.2" + jayson@^3.4.4: version "3.7.0" resolved "https://registry.yarnpkg.com/jayson/-/jayson-3.7.0.tgz#b735b12d06d348639ae8230d7a1e2916cb078f25" @@ -2110,6 +2348,13 @@ json2csv@5.0.7: jsonparse "^1.3.1" lodash.get "^4.4.2" +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" + jsonc-parser@^3.0.0: version "3.2.1" resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.1.tgz#031904571ccf929d7670ee8c547545081cb37f1a" @@ -2140,6 +2385,13 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.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.camelcase@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" @@ -2165,6 +2417,14 @@ lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.3: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== +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" + long@^5.0.0: version "5.2.3" resolved "https://registry.yarnpkg.com/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1" @@ -2184,11 +2444,26 @@ lower-case@^2.0.2: dependencies: tslib "^2.0.3" +lru-cache@^10.2.0: + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + +lru-cache@^11.0.0: + version "11.0.2" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.0.2.tgz#fbd8e7cf8211f5e7e5d91905c415a3f55755ca39" + integrity sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA== + 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-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== + marked@^4.2.4: version "4.3.0" resolved "https://registry.yarnpkg.com/marked/-/marked-4.3.0.tgz#796362821b019f734054582038b116481b456cf3" @@ -2219,6 +2494,13 @@ mime-types@^2.1.12: dependencies: mime-db "1.52.0" +minimatch@^10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.0.1.tgz#ce0521856b453c86e25f2c4c0d03e6ff7ddc440b" + integrity sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ== + dependencies: + brace-expansion "^2.0.1" + minimatch@^3.0.4, minimatch@^3.1.1: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -2226,19 +2508,69 @@ minimatch@^3.0.4, minimatch@^3.1.1: dependencies: brace-expansion "^1.1.7" -minimatch@^5.1.1: +minimatch@^5.1.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: + 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== + +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + +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@^11.0.1: + version "11.0.1" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-11.0.1.tgz#85c1c0e806275fe2479245be4ac4a0d81f533aa8" + integrity sha512-+3GkODfsDG71KSCQhc4IekSW+ItCK/kiez1Z28ksWvYhKXV/syxMlerR/sC7whDp7IyreZ4YxceMLdTs5hQE8A== + 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 "^10.4.5" + 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.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== @@ -2280,6 +2612,11 @@ node-gyp-build@^4.3.0: resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.1.tgz#976d3ad905e71b76086f4f0b0d3637fe79b6cda5" integrity sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw== +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== + object-is@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.6.tgz#1a6a53aed2dd8f7e6775ff870bea58545956ab07" @@ -2322,6 +2659,25 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.5" +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@^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" + +package-json-from-dist@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== + pako@^2.0.3: version "2.1.0" resolved "https://registry.yarnpkg.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86" @@ -2334,6 +2690,11 @@ parent-module@^1.0.0: 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" @@ -2344,6 +2705,22 @@ path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== +path-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + +path-scurry@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-2.0.0.tgz#9f052289f23ad8bf9397a2a0425e7b8615c58580" + integrity sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg== + dependencies: + lru-cache "^11.0.0" + minipass "^7.1.2" + path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" @@ -2359,7 +2736,7 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== -picomatch@^2.3.1: +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== @@ -2429,6 +2806,20 @@ queue-microtask@^1.2.2: 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" + +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" @@ -2514,7 +2905,7 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -safe-buffer@^5.0.1: +safe-buffer@^5.0.1, safe-buffer@^5.1.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== @@ -2524,6 +2915,13 @@ semver@^7.2.1, semver@^7.3.5, semver@^7.3.7: resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== +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-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" @@ -2557,6 +2955,11 @@ shiki@^0.11.1: vscode-oniguruma "^1.6.1" vscode-textmate "^6.0.0" +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -2618,6 +3021,19 @@ solana-bankrun@0.3.0: solana-bankrun-linux-x64-gnu "0.3.0" solana-bankrun-linux-x64-musl "0.3.0" +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: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + spok@^1.4.3: version "1.5.5" resolved "https://registry.yarnpkg.com/spok/-/spok-1.5.5.tgz#a51f7f290a53131d7b7a922dfedc461dda0aed72" @@ -2631,6 +3047,15 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== +"string-width-cjs@npm: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-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" @@ -2640,6 +3065,22 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +"strip-ansi-cjs@npm: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-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" @@ -2647,6 +3088,13 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + strip-bom@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" @@ -2654,6 +3102,11 @@ strip-bom@^2.0.0: dependencies: is-utf8 "^0.2.0" +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-json-comments@^3.1.0, 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" @@ -2701,6 +3154,13 @@ supports-color@^7.1.0: 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" + table@^6.0.9: version "6.8.2" resolved "https://registry.yarnpkg.com/table/-/table-6.8.2.tgz#c5504ccf201213fa227248bdc8c5569716ac6c58" @@ -2759,6 +3219,39 @@ ts-log@^2.2.4: resolved "https://registry.yarnpkg.com/ts-log/-/ts-log-2.2.7.tgz#4f4512144898b77c9984e91587076fcb8518688e" integrity sha512-320x5Ggei84AxzlXp91QkIGSw5wgaLT6GeAH0KsqDmRZdVWW2OiSeVvElVoatk3f7nicwXlElXsoFkARiGE2yg== +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" + +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@^1.8.1: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" @@ -2894,6 +3387,20 @@ word-wrap@^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-cjs@npm: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" + wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -2903,6 +3410,15 @@ wrap-ansi@^7.0.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -2928,11 +3444,39 @@ y18n@^5.0.5: resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== +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-parser@^21.1.1: version "21.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== +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@^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" + yargs@^17.7.2: version "17.7.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" @@ -2946,6 +3490,16 @@ yargs@^17.7.2: y18n "^5.0.5" yargs-parser "^21.1.1" +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.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/zstddec/-/zstddec-0.1.0.tgz#7050f3f0e0c3978562d0c566b3e5a427d2bad7ec" From cddf2f6769c075df62e5bc97a19189b07eaf6fe3 Mon Sep 17 00:00:00 2001 From: zer0fi <192312799+zer0fi@users.noreply.github.com> Date: Fri, 10 Jan 2025 18:04:28 +0100 Subject: [PATCH 2/3] review fixes --- programs/drift/src/instructions/admin.rs | 8 +++-- .../src/state/fulfillment_params/zerofi.rs | 36 +++++++++++++------ 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/programs/drift/src/instructions/admin.rs b/programs/drift/src/instructions/admin.rs index e15db3a50f..e57af7c00d 100644 --- a/programs/drift/src/instructions/admin.rs +++ b/programs/drift/src/instructions/admin.rs @@ -656,13 +656,15 @@ pub fn handle_initialize_zerofi_fulfillment_config( zerofi_program: &ctx.accounts.zerofi_program, zerofi_market: &ctx.accounts.zerofi_market, }; + + // Validates owner and discriminator let market = zerofi_market_context.load_zerofi_market()?; + validate!( market.mint_base == base_spot_market.mint, ErrorCode::InvalidZerofiMarket, "Invalid base mint" )?; - validate!( market.mint_quote == quote_spot_market.mint, ErrorCode::InvalidZerofiMarket, @@ -4530,9 +4532,9 @@ pub struct InitializeZerofiFulfillmentConfig<'info> { has_one = admin )] pub state: Box>, - /// CHECK: checked in ix + /// CHECK: single valid pubkey checked in ix pub zerofi_program: AccountInfo<'info>, - /// CHECK: checked in ix + /// CHECK: owner and discriminator checked in ix pub zerofi_market: AccountInfo<'info>, #[account( constraint = state.signer.eq(&drift_signer.key()) diff --git a/programs/drift/src/state/fulfillment_params/zerofi.rs b/programs/drift/src/state/fulfillment_params/zerofi.rs index 9aa5a77e09..7a85d9b7c5 100644 --- a/programs/drift/src/state/fulfillment_params/zerofi.rs +++ b/programs/drift/src/state/fulfillment_params/zerofi.rs @@ -62,7 +62,7 @@ pub struct ZerofiContext<'a, 'b> { #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] #[repr(C)] -pub struct Market { +pub struct ZerofiMarket { pub discriminator: u64, pub _config_authority: Pubkey, pub _update_authority: Pubkey, @@ -74,24 +74,29 @@ pub struct Market { pub vault_quote_info: Pubkey, } -impl Market { +impl ZerofiMarket { pub fn load_ref<'a>(account_info: &'a AccountInfo) -> Result> { use anchor_lang::error::ErrorCode; + validate!( + account_info.owner == &zerofi_program_id::ID, + ErrorCode::AccountOwnedByWrongProgram + )?; let data = account_info.try_borrow_data()?; - let market: Ref = Ref::map(data, |data| { - bytemuck::from_bytes(&data[..std::mem::size_of::()]) + let market: Ref = Ref::map(data, |data| { + bytemuck::from_bytes(&data[..std::mem::size_of::()]) }); - if market.discriminator != 4 { - return Err(ErrorCode::AccountDiscriminatorMismatch.into()); - } + validate!( + market.discriminator == 4, + ErrorCode::AccountDiscriminatorMismatch + )?; Ok(market) } } impl<'a, 'b> ZerofiContext<'a, 'b> { - pub fn load_zerofi_market(&self) -> DriftResult> { + pub fn load_zerofi_market(&self) -> DriftResult> { let market = - Market::load_ref(self.zerofi_market).map_err(|_| ErrorCode::FailedZerofiCPI)?; + ZerofiMarket::load_ref(self.zerofi_market).map_err(|_| ErrorCode::FailedZerofiCPI)?; Ok(market) } @@ -184,7 +189,8 @@ impl<'a, 'b> ZerofiFulfillmentParams<'a, 'b> { )?; // loading market data, validating discriminator - let market = Market::load_ref(zerofi_market).map_err(|_| ErrorCode::FailedZerofiCPI)?; + let market = + ZerofiMarket::load_ref(zerofi_market).map_err(|_| ErrorCode::FailedZerofiCPI)?; validate!( zerofi_fulfillment_config.status == SpotFulfillmentConfigStatus::Enabled, @@ -356,12 +362,20 @@ impl<'a, 'b> SpotFulfillmentParams for ZerofiFulfillmentParams<'a, 'b> { .safe_div(self.base_precision.cast()?)? .cast::()?; + // Convert the max_quote limitation to max_base using taker_price + let taker_max_base_asset_amount: u64 = taker_price + .cast::()? + .safe_mul(self.base_precision.cast()?)? + .safe_div(taker_max_quote_asset_amount.cast()?)? + .cast::()?; + let is_base_to_quote = taker_direction == PositionDirection::Short; let (in_amount, out_amount) = if !is_base_to_quote { let max_quote_in = taker_quote_asset_amount.min(taker_max_quote_asset_amount); (max_quote_in, taker_base_asset_amount) } else { - (taker_base_asset_amount, taker_quote_asset_amount) + let max_base_in = taker_base_asset_amount.min(taker_max_base_asset_amount); + (max_base_in, taker_quote_asset_amount) }; let mut args = vec![0u8; 17]; From 1bae652dc01a0cf7f4fb45507287c97b04e98e3d Mon Sep 17 00:00:00 2001 From: zer0fi <192312799+zer0fi@users.noreply.github.com> Date: Fri, 10 Jan 2025 21:46:18 +0100 Subject: [PATCH 3/3] review: honor base market step size --- .../src/state/fulfillment_params/zerofi.rs | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/programs/drift/src/state/fulfillment_params/zerofi.rs b/programs/drift/src/state/fulfillment_params/zerofi.rs index 7a85d9b7c5..486d940776 100644 --- a/programs/drift/src/state/fulfillment_params/zerofi.rs +++ b/programs/drift/src/state/fulfillment_params/zerofi.rs @@ -4,6 +4,7 @@ use crate::error::{DriftResult, ErrorCode}; use crate::instructions::SpotFulfillmentType; use crate::math::casting::Cast; use crate::math::constants::PRICE_TO_QUOTE_PRECISION_RATIO; +use crate::math::orders::{standardize_base_asset_amount, standardize_base_asset_amount_ceil}; use crate::math::safe_math::SafeMath; use crate::math::serum::{ calculate_price_from_serum_limit_price, calculate_serum_limit_price, @@ -138,6 +139,7 @@ pub struct ZerofiFulfillmentParams<'a, 'b> { pub signer_nonce: u8, pub now: i64, pub base_precision: u64, + pub base_step_size: u64, } impl<'a, 'b> ZerofiFulfillmentParams<'a, 'b> { @@ -278,6 +280,7 @@ impl<'a, 'b> ZerofiFulfillmentParams<'a, 'b> { signer_nonce: state.signer_nonce, now, base_precision: base_market.get_precision(), + base_step_size: base_market.order_step_size, }) } } @@ -356,17 +359,17 @@ impl<'a, 'b> SpotFulfillmentParams for ZerofiFulfillmentParams<'a, 'b> { let market = self.zerofi_context.load_zerofi_market()?; // According to calculate_fill_price(), this is how taker_price works - let taker_quote_asset_amount: u64 = taker_price + let taker_quote_asset_amount: u64 = taker_base_asset_amount .cast::()? - .safe_mul(taker_base_asset_amount.cast()?)? + .safe_mul(taker_price.cast()?)? .safe_div(self.base_precision.cast()?)? .cast::()?; // Convert the max_quote limitation to max_base using taker_price - let taker_max_base_asset_amount: u64 = taker_price + let taker_max_base_asset_amount: u64 = taker_max_quote_asset_amount .cast::()? .safe_mul(self.base_precision.cast()?)? - .safe_div(taker_max_quote_asset_amount.cast()?)? + .safe_div(taker_price.cast()?)? .cast::()?; let is_base_to_quote = taker_direction == PositionDirection::Short; @@ -400,10 +403,24 @@ impl<'a, 'b> SpotFulfillmentParams for ZerofiFulfillmentParams<'a, 'b> { let base_after = self.base_market_vault.amount; let quote_after = self.quote_market_vault.amount; + // Forcing the step size will make the fill have a slightly worse price, + // with the extra tokens being left on the base market vault. let (base_update_direction, base_asset_amount_filled) = if base_after > base_before { - (SpotBalanceType::Deposit, base_after.safe_sub(base_before)?) + ( + SpotBalanceType::Deposit, + standardize_base_asset_amount( + base_after.safe_sub(base_before)?, + self.base_step_size, + )?, + ) } else { - (SpotBalanceType::Borrow, base_before.safe_sub(base_after)?) + ( + SpotBalanceType::Borrow, + standardize_base_asset_amount_ceil( + base_before.safe_sub(base_after)?, + self.base_step_size, + )?, + ) }; if base_asset_amount_filled == 0 {