diff --git a/.env.copy b/.env.copy index 0c298e3..c4be1c4 100644 --- a/.env.copy +++ b/.env.copy @@ -1,16 +1,40 @@ +# Wallet PRIVATE_KEY= + +# Connection RPC_ENDPOINT=https://api.mainnet-beta.solana.com RPC_WEBSOCKET_ENDPOINT=wss://api.mainnet-beta.solana.com +COMMITMENT_LEVEL=confirmed + +# Bot +LOG_LEVEL=debug +ONE_TOKEN_AT_A_TIME=true +COMPUTE_UNIT_LIMIT=421197 +COMPUTE_UNIT_PRICE=101337 +PRE_LOAD_EXISTING_MARKETS=false +CACHE_NEW_MARKETS=false + +# Buy QUOTE_MINT=WSOL -QUOTE_AMOUNT=0.01 -COMMITMENT_LEVEL=finalized +QUOTE_AMOUNT=0.001 +AUTO_BUY_DELAY=0 +MAX_BUY_RETRIES=10 +BUY_SLIPPAGE=5 + +# Sell +AUTO_SELL=true +MAX_SELL_RETRIES=10 +AUTO_SELL_DELAY=0 +PRICE_CHECK_INTERVAL=2000 +PRICE_CHECK_DURATION=60000 +TAKE_PROFIT=25 +STOP_LOSS=15 +SELL_SLIPPAGE=5 + +# Filters USE_SNIPE_LIST=false SNIPE_LIST_REFRESH_INTERVAL=30000 -CHECK_IF_MINT_IS_RENOUNCED=false -AUTO_SELL=true -MAX_SELL_RETRIES=5 -AUTO_SELL_DELAY=1000 -LOG_LEVEL=info -MIN_POOL_SIZE=10 -MAX_POOL_SIZE=50 -ONE_TOKEN_AT_A_TIME=true +CHECK_IF_MINT_IS_RENOUNCED=true +CHECK_IF_BURNED=false +MIN_POOL_SIZE=5 +MAX_POOL_SIZE=50 \ No newline at end of file diff --git a/bot.ts b/bot.ts new file mode 100644 index 0000000..fa2c5d8 --- /dev/null +++ b/bot.ts @@ -0,0 +1,393 @@ +import { + ComputeBudgetProgram, + Connection, + Keypair, + PublicKey, + TransactionMessage, + VersionedTransaction, +} from '@solana/web3.js'; +import { + createAssociatedTokenAccountIdempotentInstruction, + createCloseAccountInstruction, + getAccount, + getAssociatedTokenAddress, + RawAccount, + TOKEN_PROGRAM_ID, +} from '@solana/spl-token'; +import { + Liquidity, + LiquidityPoolKeysV4, + LiquidityStateV4, + Percent, + Token, + TokenAmount, +} from '@raydium-io/raydium-sdk'; +import { MarketCache, PoolCache, SnipeListCache } from './cache'; +import { PoolFilters } from './filters'; +import { TransactionExecutor } from './transactions'; +import { createPoolKeys, logger, NETWORK, sleep } from './helpers'; +import { Mutex } from 'async-mutex'; +import BN from 'bn.js'; + +export interface BotConfig { + wallet: Keypair; + checkRenounced: boolean; + checkBurned: boolean; + minPoolSize: TokenAmount; + maxPoolSize: TokenAmount; + quoteToken: Token; + quoteAmount: TokenAmount; + quoteAta: PublicKey; + oneTokenAtATime: boolean; + useSnipeList: boolean; + autoSell: boolean; + autoBuyDelay: number; + autoSellDelay: number; + maxBuyRetries: number; + maxSellRetries: number; + unitLimit: number; + unitPrice: number; + takeProfit: number; + stopLoss: number; + buySlippage: number; + sellSlippage: number; + priceCheckInterval: number; + priceCheckDuration: number; +} + +export class Bot { + private readonly poolFilters: PoolFilters; + + // snipe list + private readonly snipeListCache?: SnipeListCache; + + // one token at the time + private readonly mutex: Mutex; + private sellExecutionCount = 0; + + constructor( + private readonly connection: Connection, + private readonly marketStorage: MarketCache, + private readonly poolStorage: PoolCache, + private readonly txExecutor: TransactionExecutor, + private readonly config: BotConfig, + ) { + this.mutex = new Mutex(); + this.poolFilters = new PoolFilters(connection, { + quoteToken: this.config.quoteToken, + minPoolSize: this.config.minPoolSize, + maxPoolSize: this.config.maxPoolSize, + }); + + if (this.config.useSnipeList) { + this.snipeListCache = new SnipeListCache(); + this.snipeListCache.init(); + } + } + + async validate() { + try { + await getAccount(this.connection, this.config.quoteAta, this.connection.commitment); + } catch (error) { + logger.error( + `${this.config.quoteToken.symbol} token account not found in wallet: ${this.config.wallet.publicKey.toString()}`, + ); + return false; + } + + return true; + } + + public async buy(accountId: PublicKey, poolState: LiquidityStateV4) { + logger.trace({ mint: poolState.baseMint }, `Processing buy...`); + + if (this.config.useSnipeList && !this.snipeListCache?.isInList(poolState.baseMint.toString())) { + logger.debug({ mint: poolState.baseMint.toString() }, `Skipping buy because token is not in a snipe list`); + return; + } + + if (this.config.autoBuyDelay > 0) { + logger.debug({ mint: poolState.baseMint }, `Waiting for ${this.config.autoBuyDelay} ms before buy`); + await sleep(this.config.autoBuyDelay); + } + + if (this.config.oneTokenAtATime) { + if (this.mutex.isLocked() || this.sellExecutionCount > 0) { + logger.debug( + { mint: poolState.baseMint.toString() }, + `Skipping buy because one token at a time is turned on and token is already being processed`, + ); + return; + } + + await this.mutex.acquire(); + } + + try { + const shouldBuy = await this.poolFilters.execute(poolState); + + if (!shouldBuy) { + logger.debug({ mint: poolState.baseMint.toString() }, `Skipping buy because pool doesn't match filters`); + return; + } + + for (let i = 0; i < this.config.maxBuyRetries; i++) { + try { + const [market, mintAta] = await Promise.all([ + this.marketStorage.get(poolState.marketId.toString()), + getAssociatedTokenAddress(poolState.baseMint, this.config.wallet.publicKey), + ]); + const poolKeys: LiquidityPoolKeysV4 = createPoolKeys(accountId, poolState, market); + + logger.info( + { mint: poolState.baseMint.toString() }, + `Send buy transaction attempt: ${i + 1}/${this.config.maxBuyRetries}`, + ); + const tokenOut = new Token(TOKEN_PROGRAM_ID, poolKeys.baseMint, poolKeys.baseDecimals); + const result = await this.swap( + poolKeys, + this.config.quoteAta, + mintAta, + this.config.quoteToken, + tokenOut, + this.config.quoteAmount, + this.config.buySlippage, + this.config.wallet, + 'buy', + ); + + if (result.confirmed) { + logger.info( + { + mint: poolState.baseMint.toString(), + signature: result.signature, + url: `https://solscan.io/tx/${result.signature}?cluster=${NETWORK}`, + }, + `Confirmed buy tx`, + ); + + break; + } + + logger.debug( + { + mint: poolState.baseMint.toString(), + signature: result.signature, + }, + `Error confirming buy tx`, + ); + } catch (error) { + logger.debug({ mint: poolState.baseMint.toString(), error }, `Error confirming buy transaction`); + } + } + } catch (error) { + logger.error({ mint: poolState.baseMint.toString(), error }, `Failed to buy token`); + } finally { + if (this.config.oneTokenAtATime) { + this.mutex.release(); + } + } + } + + public async sell(accountId: PublicKey, rawAccount: RawAccount) { + if (this.config.oneTokenAtATime) { + this.sellExecutionCount++; + } + + try { + logger.trace({ mint: rawAccount.mint }, `Processing sell...`); + + const poolData = await this.poolStorage.get(rawAccount.mint.toString()); + + if (!poolData) { + logger.trace({ mint: rawAccount.mint.toString() }, `Token pool data is not found, can't sell`); + return; + } + + const tokenIn = new Token(TOKEN_PROGRAM_ID, poolData.state.baseMint, poolData.state.baseDecimal.toNumber()); + const tokenAmountIn = new TokenAmount(tokenIn, rawAccount.amount, true); + + if (tokenAmountIn.isZero()) { + logger.info({ mint: rawAccount.mint.toString() }, `Empty balance, can't sell`); + return; + } + + if (this.config.autoSellDelay > 0) { + logger.debug({ mint: rawAccount.mint }, `Waiting for ${this.config.autoSellDelay} ms before sell`); + await sleep(this.config.autoSellDelay); + } + + for (let i = 0; i < this.config.maxSellRetries; i++) { + try { + const market = await this.marketStorage.get(poolData.state.marketId.toString()); + const poolKeys: LiquidityPoolKeysV4 = createPoolKeys(new PublicKey(poolData.id), poolData.state, market); + + await this.priceMatch(tokenAmountIn, poolKeys); + + logger.info( + { mint: rawAccount.mint }, + `Send sell transaction attempt: ${i + 1}/${this.config.maxSellRetries}`, + ); + + const result = await this.swap( + poolKeys, + accountId, + this.config.quoteAta, + tokenIn, + this.config.quoteToken, + tokenAmountIn, + this.config.sellSlippage, + this.config.wallet, + 'sell', + ); + + if (result.confirmed) { + logger.info( + { + dex: `https://dexscreener.com/solana/${rawAccount.mint.toString()}?maker=${this.config.wallet.publicKey}`, + mint: rawAccount.mint.toString(), + signature: result.signature, + url: `https://solscan.io/tx/${result.signature}?cluster=${NETWORK}`, + }, + `Confirmed sell tx`, + ); + break; + } + + logger.info( + { + mint: rawAccount.mint.toString(), + signature: result.signature, + }, + `Error confirming sell tx`, + ); + } catch (error) { + logger.debug({ mint: rawAccount.mint.toString(), error }, `Error confirming sell transaction`); + } + } + } catch (error) { + logger.debug({ mint: rawAccount.mint.toString(), error }, `Failed to sell token`); + } finally { + if (this.config.oneTokenAtATime) { + this.sellExecutionCount--; + } + } + } + + private async swap( + poolKeys: LiquidityPoolKeysV4, + ataIn: PublicKey, + ataOut: PublicKey, + tokenIn: Token, + tokenOut: Token, + amountIn: TokenAmount, + slippage: number, + wallet: Keypair, + direction: 'buy' | 'sell', + ) { + const slippagePercent = new Percent(slippage, 100); + const poolInfo = await Liquidity.fetchInfo({ + connection: this.connection, + poolKeys, + }); + + const computedAmountOut = Liquidity.computeAmountOut({ + poolKeys, + poolInfo, + amountIn, + currencyOut: tokenOut, + slippage: slippagePercent, + }); + + const latestBlockhash = await this.connection.getLatestBlockhash(); + const { innerTransaction } = Liquidity.makeSwapFixedInInstruction( + { + poolKeys: poolKeys, + userKeys: { + tokenAccountIn: ataIn, + tokenAccountOut: ataOut, + owner: wallet.publicKey, + }, + amountIn: amountIn.raw, + minAmountOut: computedAmountOut.minAmountOut.raw, + }, + poolKeys.version, + ); + + const messageV0 = new TransactionMessage({ + payerKey: wallet.publicKey, + recentBlockhash: latestBlockhash.blockhash, + instructions: [ + ComputeBudgetProgram.setComputeUnitPrice({ microLamports: this.config.unitPrice }), + ComputeBudgetProgram.setComputeUnitLimit({ units: this.config.unitLimit }), + ...(direction === 'buy' + ? [ + createAssociatedTokenAccountIdempotentInstruction( + wallet.publicKey, + ataOut, + wallet.publicKey, + tokenOut.mint, + ), + ] + : []), + ...innerTransaction.instructions, + ...(direction === 'sell' ? [createCloseAccountInstruction(ataIn, wallet.publicKey, wallet.publicKey)] : []), + ], + }).compileToV0Message(); + + const transaction = new VersionedTransaction(messageV0); + transaction.sign([wallet, ...innerTransaction.signers]); + + return this.txExecutor.executeAndConfirm(transaction, latestBlockhash); + } + + private async priceMatch(amountIn: TokenAmount, poolKeys: LiquidityPoolKeysV4) { + const profitFraction = this.config.quoteAmount.mul(this.config.takeProfit).numerator.div(new BN(100)); + const profitAmount = new TokenAmount(this.config.quoteToken, profitFraction, true); + const takeProfit = this.config.quoteAmount.add(profitAmount); + + const lossFraction = this.config.quoteAmount.mul(this.config.stopLoss).numerator.div(new BN(100)); + const lossAmount = new TokenAmount(this.config.quoteToken, lossFraction, true); + const stopLoss = this.config.quoteAmount.subtract(lossAmount); + const slippage = new Percent(this.config.sellSlippage, 100); + + const timesToCheck = this.config.priceCheckDuration / this.config.priceCheckInterval; + let timesChecked = 0; + + do { + try { + const poolInfo = await Liquidity.fetchInfo({ + connection: this.connection, + poolKeys, + }); + + const amountOut = Liquidity.computeAmountOut({ + poolKeys, + poolInfo, + amountIn: amountIn, + currencyOut: this.config.quoteToken, + slippage, + }).amountOut; + + logger.debug( + { mint: poolKeys.baseMint.toString() }, + `Take profit: ${takeProfit.toFixed()} | Stop loss: ${stopLoss.toFixed()} | Current: ${amountOut.toFixed()}`, + ); + + if (amountOut.lt(stopLoss)){ + break; + } + + if (amountOut.gt(takeProfit)){ + break; + } + + await sleep(this.config.priceCheckInterval); + } catch (e) { + logger.trace({ mint: poolKeys.baseMint.toString(), e }, `Failed to check token price`); + } finally { + timesChecked++; + } + } while (timesChecked < timesToCheck); + } +} diff --git a/buy.ts b/buy.ts deleted file mode 100644 index 071701f..0000000 --- a/buy.ts +++ /dev/null @@ -1,555 +0,0 @@ -import { - BigNumberish, - Liquidity, - LIQUIDITY_STATE_LAYOUT_V4, - LiquidityPoolKeys, - LiquidityStateV4, - MARKET_STATE_LAYOUT_V3, - MarketStateV3, - Token, - TokenAmount, -} from '@raydium-io/raydium-sdk'; -import { - AccountLayout, - createAssociatedTokenAccountIdempotentInstruction, - createCloseAccountInstruction, - getAssociatedTokenAddressSync, - TOKEN_PROGRAM_ID, -} from '@solana/spl-token'; -import { - Keypair, - Connection, - PublicKey, - ComputeBudgetProgram, - KeyedAccountInfo, - TransactionMessage, - VersionedTransaction, -} from '@solana/web3.js'; -import { getTokenAccounts, RAYDIUM_LIQUIDITY_PROGRAM_ID_V4, OPENBOOK_PROGRAM_ID, createPoolKeys } from './liquidity'; -import { logger } from './utils'; -import { getMinimalMarketV3, MinimalMarketLayoutV3 } from './market'; -import { MintLayout } from './types'; -import bs58 from 'bs58'; -import * as fs from 'fs'; -import * as path from 'path'; -import { - AUTO_SELL, - AUTO_SELL_DELAY, - CHECK_IF_MINT_IS_RENOUNCED, - COMMITMENT_LEVEL, - LOG_LEVEL, - MAX_SELL_RETRIES, - NETWORK, - PRIVATE_KEY, - QUOTE_AMOUNT, - QUOTE_MINT, - RPC_ENDPOINT, - RPC_WEBSOCKET_ENDPOINT, - SNIPE_LIST_REFRESH_INTERVAL, - USE_SNIPE_LIST, - MIN_POOL_SIZE, - MAX_POOL_SIZE, - ONE_TOKEN_AT_A_TIME, -} from './constants'; - -const solanaConnection = new Connection(RPC_ENDPOINT, { - wsEndpoint: RPC_WEBSOCKET_ENDPOINT, -}); - -export interface MinimalTokenAccountData { - mint: PublicKey; - address: PublicKey; - poolKeys?: LiquidityPoolKeys; - market?: MinimalMarketLayoutV3; -} - -const existingLiquidityPools: Set = new Set(); -const existingOpenBookMarkets: Set = new Set(); -const existingTokenAccounts: Map = new Map(); - -let wallet: Keypair; -let quoteToken: Token; -let quoteTokenAssociatedAddress: PublicKey; -let quoteAmount: TokenAmount; -let quoteMinPoolSizeAmount: TokenAmount; -let quoteMaxPoolSizeAmount: TokenAmount; -let processingToken: Boolean = false; - - - -let snipeList: string[] = []; - -async function init(): Promise { - logger.level = LOG_LEVEL; - - // get wallet - wallet = Keypair.fromSecretKey(bs58.decode(PRIVATE_KEY)); - logger.info(`Wallet Address: ${wallet.publicKey}`); - - // get quote mint and amount - switch (QUOTE_MINT) { - case 'WSOL': { - quoteToken = Token.WSOL; - quoteAmount = new TokenAmount(Token.WSOL, QUOTE_AMOUNT, false); - quoteMinPoolSizeAmount = new TokenAmount(quoteToken, MIN_POOL_SIZE, false); - quoteMaxPoolSizeAmount = new TokenAmount(quoteToken, MAX_POOL_SIZE, false); - break; - } - case 'USDC': { - quoteToken = new Token( - TOKEN_PROGRAM_ID, - new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'), - 6, - 'USDC', - 'USDC', - ); - quoteAmount = new TokenAmount(quoteToken, QUOTE_AMOUNT, false); - quoteMinPoolSizeAmount = new TokenAmount(quoteToken, MIN_POOL_SIZE, false); - quoteMaxPoolSizeAmount = new TokenAmount(quoteToken, MAX_POOL_SIZE, false); - break; - } - default: { - throw new Error(`Unsupported quote mint "${QUOTE_MINT}". Supported values are USDC and WSOL`); - } - } - - logger.info(`Snipe list: ${USE_SNIPE_LIST}`); - logger.info(`Check mint renounced: ${CHECK_IF_MINT_IS_RENOUNCED}`); - logger.info( - `Min pool size: ${quoteMinPoolSizeAmount.isZero() ? 'false' : quoteMinPoolSizeAmount.toFixed()} ${quoteToken.symbol}`, - ); - logger.info( - `Max pool size: ${quoteMaxPoolSizeAmount.isZero() ? 'false' : quoteMaxPoolSizeAmount.toFixed()} ${quoteToken.symbol}`, - ); - logger.info(`One token at a time: ${ONE_TOKEN_AT_A_TIME}`); - logger.info(`Buy amount: ${quoteAmount.toFixed()} ${quoteToken.symbol}`); - logger.info(`Auto sell: ${AUTO_SELL}`); - logger.info(`Sell delay: ${AUTO_SELL_DELAY === 0 ? 'false' : AUTO_SELL_DELAY}`); - - // check existing wallet for associated token account of quote mint - const tokenAccounts = await getTokenAccounts(solanaConnection, wallet.publicKey, COMMITMENT_LEVEL); - - for (const ta of tokenAccounts) { - existingTokenAccounts.set(ta.accountInfo.mint.toString(), { - mint: ta.accountInfo.mint, - address: ta.pubkey, - }); - } - - const tokenAccount = tokenAccounts.find((acc) => acc.accountInfo.mint.toString() === quoteToken.mint.toString())!; - - if (!tokenAccount) { - throw new Error(`No ${quoteToken.symbol} token account found in wallet: ${wallet.publicKey}`); - } - - quoteTokenAssociatedAddress = tokenAccount.pubkey; - - // load tokens to snipe - loadSnipeList(); -} - -function saveTokenAccount(mint: PublicKey, accountData: MinimalMarketLayoutV3) { - const ata = getAssociatedTokenAddressSync(mint, wallet.publicKey); - const tokenAccount = { - address: ata, - mint: mint, - market: { - bids: accountData.bids, - asks: accountData.asks, - eventQueue: accountData.eventQueue, - }, - }; - existingTokenAccounts.set(mint.toString(), tokenAccount); - return tokenAccount; -} - -export async function processRaydiumPool(id: PublicKey, poolState: LiquidityStateV4) { - if (!shouldBuy(poolState.baseMint.toString())) { - return; - } - - if (!quoteMinPoolSizeAmount.isZero()) { - const poolSize = new TokenAmount(quoteToken, poolState.swapQuoteInAmount, true); - logger.info(`Processing pool: ${id.toString()} with ${poolSize.toFixed()} ${quoteToken.symbol} in liquidity`); - - if (poolSize.lt(quoteMinPoolSizeAmount)) { - logger.warn( - { - mint: poolState.baseMint, - pooled: `${poolSize.toFixed()} ${quoteToken.symbol}`, - }, - `Skipping pool, smaller than ${quoteMinPoolSizeAmount.toFixed()} ${quoteToken.symbol}`, - `Swap quote in amount: ${poolSize.toFixed()}`, - ); - logger.info(`-------------------🤖🔧------------------- \n`); - return; - } - } - - if (!quoteMaxPoolSizeAmount.isZero()) { - const poolSize = new TokenAmount(quoteToken, poolState.swapQuoteInAmount, true); - - if (poolSize.gt(quoteMaxPoolSizeAmount)) { - logger.warn( - { - mint: poolState.baseMint, - pooled: `${poolSize.toFixed()} ${quoteToken.symbol}`, - }, - `Skipping pool, bigger than ${quoteMaxPoolSizeAmount.toFixed()} ${quoteToken.symbol}`, - `Swap quote in amount: ${poolSize.toFixed()}`, - ); - logger.info(`-------------------🤖🔧------------------- \n`); - return; - } - } - - if (CHECK_IF_MINT_IS_RENOUNCED) { - const mintOption = await checkMintable(poolState.baseMint); - - if (mintOption !== true) { - logger.warn({ mint: poolState.baseMint }, 'Skipping, owner can mint tokens!'); - return; - } - } - - await buy(id, poolState); -} - -export async function checkMintable(vault: PublicKey): Promise { - try { - let { data } = (await solanaConnection.getAccountInfo(vault)) || {}; - if (!data) { - return; - } - const deserialize = MintLayout.decode(data); - return deserialize.mintAuthorityOption === 0; - } catch (e) { - logger.debug(e); - logger.error({ mint: vault }, `Failed to check if mint is renounced`); - } -} - -export async function processOpenBookMarket(updatedAccountInfo: KeyedAccountInfo) { - let accountData: MarketStateV3 | undefined; - try { - accountData = MARKET_STATE_LAYOUT_V3.decode(updatedAccountInfo.accountInfo.data); - - // to be competitive, we collect market data before buying the token... - if (existingTokenAccounts.has(accountData.baseMint.toString())) { - return; - } - - saveTokenAccount(accountData.baseMint, accountData); - } catch (e) { - logger.debug(e); - logger.error({ mint: accountData?.baseMint }, `Failed to process market`); - } -} - -async function buy(accountId: PublicKey, accountData: LiquidityStateV4): Promise { - try { - let tokenAccount = existingTokenAccounts.get(accountData.baseMint.toString()); - - if (!tokenAccount) { - // it's possible that we didn't have time to fetch open book data - const market = await getMinimalMarketV3(solanaConnection, accountData.marketId, COMMITMENT_LEVEL); - tokenAccount = saveTokenAccount(accountData.baseMint, market); - } - - tokenAccount.poolKeys = createPoolKeys(accountId, accountData, tokenAccount.market!); - const { innerTransaction } = Liquidity.makeSwapFixedInInstruction( - { - poolKeys: tokenAccount.poolKeys, - userKeys: { - tokenAccountIn: quoteTokenAssociatedAddress, - tokenAccountOut: tokenAccount.address, - owner: wallet.publicKey, - }, - amountIn: quoteAmount.raw, - minAmountOut: 0, - }, - tokenAccount.poolKeys.version, - ); - - const latestBlockhash = await solanaConnection.getLatestBlockhash({ - commitment: COMMITMENT_LEVEL, - }); - const messageV0 = new TransactionMessage({ - payerKey: wallet.publicKey, - recentBlockhash: latestBlockhash.blockhash, - instructions: [ - ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 421197 }), - ComputeBudgetProgram.setComputeUnitLimit({ units: 101337 }), - createAssociatedTokenAccountIdempotentInstruction( - wallet.publicKey, - tokenAccount.address, - wallet.publicKey, - accountData.baseMint, - ), - ...innerTransaction.instructions, - ], - }).compileToV0Message(); - const transaction = new VersionedTransaction(messageV0); - transaction.sign([wallet, ...innerTransaction.signers]); - const signature = await solanaConnection.sendRawTransaction(transaction.serialize(), { - preflightCommitment: COMMITMENT_LEVEL, - }); - logger.info({ mint: accountData.baseMint, signature }, `Sent buy tx`); - processingToken = true; - - const confirmation = await solanaConnection.confirmTransaction( - { - signature, - lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, - blockhash: latestBlockhash.blockhash, - }, - COMMITMENT_LEVEL, - ); - if (!confirmation.value.err) { - logger.info(`-------------------🟢------------------- `); - logger.info( - { - mint: accountData.baseMint, - signature, - url: `https://solscan.io/tx/${signature}?cluster=${NETWORK}`, - }, - `Confirmed buy tx`, - ); - } else { - logger.debug(confirmation.value.err); - logger.info({ mint: accountData.baseMint, signature }, `Error confirming buy tx`); - } - } catch (e) { - logger.debug(e); - processingToken = false; - logger.error({ mint: accountData.baseMint }, `Failed to buy token`); - } -} - -async function sell(accountId: PublicKey, mint: PublicKey, amount: BigNumberish): Promise { - let sold = false; - let retries = 0; - - if (AUTO_SELL_DELAY > 0) { - await new Promise((resolve) => setTimeout(resolve, AUTO_SELL_DELAY)); - } - - do { - try { - const tokenAccount = existingTokenAccounts.get(mint.toString()); - - if (!tokenAccount) { - return; - } - - if (!tokenAccount.poolKeys) { - logger.warn({ mint }, 'No pool keys found'); - return; - } - - if (amount === 0) { - logger.info( - { - mint: tokenAccount.mint, - }, - `Empty balance, can't sell`, - ); - return; - } - - const { innerTransaction } = Liquidity.makeSwapFixedInInstruction( - { - poolKeys: tokenAccount.poolKeys!, - userKeys: { - tokenAccountOut: quoteTokenAssociatedAddress, - tokenAccountIn: tokenAccount.address, - owner: wallet.publicKey, - }, - amountIn: amount, - minAmountOut: 0, - }, - tokenAccount.poolKeys!.version, - ); - - const latestBlockhash = await solanaConnection.getLatestBlockhash({ - commitment: COMMITMENT_LEVEL, - }); - const messageV0 = new TransactionMessage({ - payerKey: wallet.publicKey, - recentBlockhash: latestBlockhash.blockhash, - instructions: [ - ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 421197 }), - ComputeBudgetProgram.setComputeUnitLimit({ units: 101337 }), - ...innerTransaction.instructions, - createCloseAccountInstruction(tokenAccount.address, wallet.publicKey, wallet.publicKey), - ], - }).compileToV0Message(); - const transaction = new VersionedTransaction(messageV0); - transaction.sign([wallet, ...innerTransaction.signers]); - const signature = await solanaConnection.sendRawTransaction(transaction.serialize(), { - preflightCommitment: COMMITMENT_LEVEL, - }); - logger.info({ mint, signature }, `Sent sell tx`); - const confirmation = await solanaConnection.confirmTransaction( - { - signature, - lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, - blockhash: latestBlockhash.blockhash, - }, - COMMITMENT_LEVEL, - ); - if (confirmation.value.err) { - logger.debug(confirmation.value.err); - logger.info({ mint, signature }, `Error confirming sell tx`); - continue; - } - logger.info(`-------------------🔴------------------- `); - logger.info( - { - dex: `https://dexscreener.com/solana/${mint}?maker=${wallet.publicKey}`, - mint, - signature, - url: `https://solscan.io/tx/${signature}?cluster=${NETWORK}`, - }, - `Confirmed sell tx`, - ); - sold = true; - processingToken = false; - } catch (e: any) { - // wait for a bit before retrying - await new Promise((resolve) => setTimeout(resolve, 100)); - retries++; - logger.debug(e); - logger.error({ mint }, `Failed to sell token, retry: ${retries}/${MAX_SELL_RETRIES}`); - } - } while (!sold && retries < MAX_SELL_RETRIES); - processingToken = false; -} - -function loadSnipeList() { - if (!USE_SNIPE_LIST) { - return; - } - - const count = snipeList.length; - const data = fs.readFileSync(path.join(__dirname, 'snipe-list.txt'), 'utf-8'); - snipeList = data - .split('\n') - .map((a) => a.trim()) - .filter((a) => a); - - if (snipeList.length != count) { - logger.info(`Loaded snipe list: ${snipeList.length}`); - } -} - -function shouldBuy(key: string): boolean { - logger.info(`-------------------🤖🔧------------------- `); - logger.info(`Processing token: ${processingToken}`) - return USE_SNIPE_LIST ? snipeList.includes(key) : ONE_TOKEN_AT_A_TIME ? !processingToken : true -} - -const runListener = async () => { - await init(); - const runTimestamp = Math.floor(new Date().getTime() / 1000); - const raydiumSubscriptionId = solanaConnection.onProgramAccountChange( - RAYDIUM_LIQUIDITY_PROGRAM_ID_V4, - async (updatedAccountInfo) => { - const key = updatedAccountInfo.accountId.toString(); - const poolState = LIQUIDITY_STATE_LAYOUT_V4.decode(updatedAccountInfo.accountInfo.data); - const poolOpenTime = parseInt(poolState.poolOpenTime.toString()); - const existing = existingLiquidityPools.has(key); - - if (poolOpenTime > runTimestamp && !existing) { - existingLiquidityPools.add(key); - const _ = processRaydiumPool(updatedAccountInfo.accountId, poolState); - } - }, - COMMITMENT_LEVEL, - [ - { dataSize: LIQUIDITY_STATE_LAYOUT_V4.span }, - { - memcmp: { - offset: LIQUIDITY_STATE_LAYOUT_V4.offsetOf('quoteMint'), - bytes: quoteToken.mint.toBase58(), - }, - }, - { - memcmp: { - offset: LIQUIDITY_STATE_LAYOUT_V4.offsetOf('marketProgramId'), - bytes: OPENBOOK_PROGRAM_ID.toBase58(), - }, - }, - { - memcmp: { - offset: LIQUIDITY_STATE_LAYOUT_V4.offsetOf('status'), - bytes: bs58.encode([6, 0, 0, 0, 0, 0, 0, 0]), - }, - }, - ], - ); - - const openBookSubscriptionId = solanaConnection.onProgramAccountChange( - OPENBOOK_PROGRAM_ID, - async (updatedAccountInfo) => { - const key = updatedAccountInfo.accountId.toString(); - const existing = existingOpenBookMarkets.has(key); - if (!existing) { - existingOpenBookMarkets.add(key); - const _ = processOpenBookMarket(updatedAccountInfo); - } - }, - COMMITMENT_LEVEL, - [ - { dataSize: MARKET_STATE_LAYOUT_V3.span }, - { - memcmp: { - offset: MARKET_STATE_LAYOUT_V3.offsetOf('quoteMint'), - bytes: quoteToken.mint.toBase58(), - }, - }, - ], - ); - - if (AUTO_SELL) { - const walletSubscriptionId = solanaConnection.onProgramAccountChange( - TOKEN_PROGRAM_ID, - async (updatedAccountInfo) => { - const accountData = AccountLayout.decode(updatedAccountInfo.accountInfo!.data); - - if (updatedAccountInfo.accountId.equals(quoteTokenAssociatedAddress)) { - return; - } - - const _ = sell(updatedAccountInfo.accountId, accountData.mint, accountData.amount); - }, - COMMITMENT_LEVEL, - [ - { - dataSize: 165, - }, - { - memcmp: { - offset: 32, - bytes: wallet.publicKey.toBase58(), - }, - }, - ], - ); - - logger.info(`Listening for wallet changes: ${walletSubscriptionId}`); - } - - logger.info(`Listening for raydium changes: ${raydiumSubscriptionId}`); - logger.info(`Listening for open book changes: ${openBookSubscriptionId}`); - - logger.info('------------------- 🚀 ---------------------'); - logger.info('Bot is running! Press CTRL + C to stop it.'); - logger.info('------------------- 🚀 ---------------------'); - - if (USE_SNIPE_LIST) { - setInterval(loadSnipeList, SNIPE_LIST_REFRESH_INTERVAL); - } -}; - -runListener(); diff --git a/cache/index.ts b/cache/index.ts new file mode 100644 index 0000000..4bd079e --- /dev/null +++ b/cache/index.ts @@ -0,0 +1,3 @@ +export * from './market.cache'; +export * from './pool.cache'; +export * from './snipe-list.cache'; diff --git a/cache/market.cache.ts b/cache/market.cache.ts new file mode 100644 index 0000000..b360c46 --- /dev/null +++ b/cache/market.cache.ts @@ -0,0 +1,58 @@ +import { Connection, PublicKey } from '@solana/web3.js'; +import { getMinimalMarketV3, logger, MINIMAL_MARKET_STATE_LAYOUT_V3, MinimalMarketLayoutV3 } from '../helpers'; +import { MAINNET_PROGRAM_ID, MARKET_STATE_LAYOUT_V3, Token } from '@raydium-io/raydium-sdk'; + +export class MarketCache { + private readonly keys: Map = new Map(); + constructor(private readonly connection: Connection) {} + + async init(config: { quoteToken: Token }) { + logger.debug({}, `Fetching all existing ${config.quoteToken.symbol} markets...`); + + const accounts = await this.connection.getProgramAccounts(MAINNET_PROGRAM_ID.OPENBOOK_MARKET, { + commitment: this.connection.commitment, + dataSlice: { + offset: MARKET_STATE_LAYOUT_V3.offsetOf('eventQueue'), + length: MINIMAL_MARKET_STATE_LAYOUT_V3.span, + }, + filters: [ + { dataSize: MARKET_STATE_LAYOUT_V3.span }, + { + memcmp: { + offset: MARKET_STATE_LAYOUT_V3.offsetOf('quoteMint'), + bytes: config.quoteToken.mint.toBase58(), + }, + }, + ], + }); + + for (const account of accounts) { + const market = MINIMAL_MARKET_STATE_LAYOUT_V3.decode(account.account.data); + this.keys.set(account.pubkey.toString(), market); + } + + logger.debug({}, `Cached ${this.keys.size} markets`); + } + + public save(marketId: string, keys: MinimalMarketLayoutV3) { + if (!this.keys.has(marketId)) { + logger.trace({}, `Caching new market: ${marketId}`); + this.keys.set(marketId, keys); + } + } + + public async get(marketId: string): Promise { + if (this.keys.has(marketId)) { + return this.keys.get(marketId)!; + } + + logger.trace({}, `Fetching new market keys for ${marketId}`); + const market = await this.fetch(marketId); + this.keys.set(marketId, market); + return market; + } + + private fetch(marketId: string): Promise { + return getMinimalMarketV3(this.connection, new PublicKey(marketId), this.connection.commitment); + } +} diff --git a/cache/pool.cache.ts b/cache/pool.cache.ts new file mode 100644 index 0000000..8139427 --- /dev/null +++ b/cache/pool.cache.ts @@ -0,0 +1,20 @@ +import { LiquidityStateV4 } from '@raydium-io/raydium-sdk'; +import { logger } from '../helpers'; + +export class PoolCache { + private readonly keys: Map = new Map< + string, + { id: string; state: LiquidityStateV4 } + >(); + + public save(id: string, state: LiquidityStateV4) { + if (!this.keys.has(state.baseMint.toString())) { + logger.trace(`Caching new pool for mint: ${state.baseMint.toString()}`); + this.keys.set(state.baseMint.toString(), { id, state }); + } + } + + public async get(mint: string): Promise<{ id: string; state: LiquidityStateV4 }> { + return this.keys.get(mint)!; + } +} diff --git a/cache/snipe-list.cache.ts b/cache/snipe-list.cache.ts new file mode 100644 index 0000000..ea3a2b3 --- /dev/null +++ b/cache/snipe-list.cache.ts @@ -0,0 +1,34 @@ +import fs from 'fs'; +import path from 'path'; +import { logger, SNIPE_LIST_REFRESH_INTERVAL } from '../helpers'; + +export class SnipeListCache { + private snipeList: string[] = []; + + constructor() { + setInterval(this.loadSnipeList, SNIPE_LIST_REFRESH_INTERVAL); + } + + public init() { + this.loadSnipeList(); + } + + public isInList(mint: string) { + return this.snipeList.includes(mint); + } + + private loadSnipeList() { + logger.trace('Refreshing snipe list...'); + + const count = this.snipeList.length; + const data = fs.readFileSync(path.join(__dirname, 'snipe-list.txt'), 'utf-8'); + this.snipeList = data + .split('\n') + .map((a) => a.trim()) + .filter((a) => a); + + if (this.snipeList.length != count) { + logger.info(`Loaded snipe list: ${this.snipeList.length}`); + } + } +} diff --git a/constants/constants.ts b/constants/constants.ts deleted file mode 100644 index eb97806..0000000 --- a/constants/constants.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Commitment } from "@solana/web3.js"; -import { logger, retrieveEnvVariable } from "../utils"; - -export const NETWORK = 'mainnet-beta'; -export const COMMITMENT_LEVEL: Commitment = retrieveEnvVariable('COMMITMENT_LEVEL', logger) as Commitment; -export const RPC_ENDPOINT = retrieveEnvVariable('RPC_ENDPOINT', logger); -export const RPC_WEBSOCKET_ENDPOINT = retrieveEnvVariable('RPC_WEBSOCKET_ENDPOINT', logger); -export const LOG_LEVEL = retrieveEnvVariable('LOG_LEVEL', logger); -export const CHECK_IF_MINT_IS_RENOUNCED = retrieveEnvVariable('CHECK_IF_MINT_IS_RENOUNCED', logger) === 'true'; -export const USE_SNIPE_LIST = retrieveEnvVariable('USE_SNIPE_LIST', logger) === 'true'; -export const SNIPE_LIST_REFRESH_INTERVAL = Number(retrieveEnvVariable('SNIPE_LIST_REFRESH_INTERVAL', logger)); -export const AUTO_SELL = retrieveEnvVariable('AUTO_SELL', logger) === 'true'; -export const MAX_SELL_RETRIES = Number(retrieveEnvVariable('MAX_SELL_RETRIES', logger)); -export const AUTO_SELL_DELAY = Number(retrieveEnvVariable('AUTO_SELL_DELAY', logger)); -export const PRIVATE_KEY = retrieveEnvVariable('PRIVATE_KEY', logger); -export const QUOTE_MINT = retrieveEnvVariable('QUOTE_MINT', logger); -export const QUOTE_AMOUNT = retrieveEnvVariable('QUOTE_AMOUNT', logger); -export const MIN_POOL_SIZE = retrieveEnvVariable('MIN_POOL_SIZE', logger); -export const MAX_POOL_SIZE = retrieveEnvVariable('MAX_POOL_SIZE', logger); -export const ONE_TOKEN_AT_A_TIME = retrieveEnvVariable('ONE_TOKEN_AT_A_TIME', logger) === 'true'; diff --git a/constants/index.ts b/constants/index.ts deleted file mode 100644 index e94e4b1..0000000 --- a/constants/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './constants'; \ No newline at end of file diff --git a/filters/burn.filter.ts b/filters/burn.filter.ts new file mode 100644 index 0000000..18acc5c --- /dev/null +++ b/filters/burn.filter.ts @@ -0,0 +1,24 @@ +import { Filter, FilterResult } from './pool-filters'; +import { Connection } from '@solana/web3.js'; +import { LiquidityStateV4 } from '@raydium-io/raydium-sdk'; +import { logger } from '../helpers'; + +export class BurnFilter implements Filter { + constructor(private readonly connection: Connection) {} + + async execute(poolState: LiquidityStateV4): Promise { + try { + const amount = await this.connection.getTokenSupply(poolState.lpMint, this.connection.commitment); + const burned = amount.value.uiAmount === 0; + return { ok: burned, message: burned ? undefined : "Burned -> Creator didn't burn LP" }; + } catch (e: any) { + if (e.code == -32602) { + return { ok: true }; + } + + logger.error({ mint: poolState.baseMint }, `Failed to check if LP is burned`); + } + + return { ok: false, message: 'Failed to check if LP is burned' }; + } +} diff --git a/filters/index.ts b/filters/index.ts new file mode 100644 index 0000000..d2ba963 --- /dev/null +++ b/filters/index.ts @@ -0,0 +1,4 @@ +export * from './burn.filter'; +export * from './pool-filters'; +export * from './pool-size.filter'; +export * from './renounced.filter'; diff --git a/filters/pool-filters.ts b/filters/pool-filters.ts new file mode 100644 index 0000000..3a6e91d --- /dev/null +++ b/filters/pool-filters.ts @@ -0,0 +1,61 @@ +import { Connection } from '@solana/web3.js'; +import { LiquidityStateV4, Token, TokenAmount } from '@raydium-io/raydium-sdk'; +import { BurnFilter } from './burn.filter'; +import { RenouncedFilter } from './renounced.filter'; +import { PoolSizeFilter } from './pool-size.filter'; +import { CHECK_IF_BURNED, CHECK_IF_MINT_IS_RENOUNCED, logger } from '../helpers'; + +export interface Filter { + execute(poolState: LiquidityStateV4): Promise; +} + +export interface FilterResult { + ok: boolean; + message?: string; +} + +export interface PoolFilterArgs { + minPoolSize: TokenAmount; + maxPoolSize: TokenAmount; + quoteToken: Token; +} + +export class PoolFilters { + private readonly filters: Filter[] = []; + + constructor( + readonly connection: Connection, + readonly args: PoolFilterArgs, + ) { + if (CHECK_IF_BURNED) { + this.filters.push(new BurnFilter(connection)); + } + + if (CHECK_IF_MINT_IS_RENOUNCED) { + this.filters.push(new RenouncedFilter(connection)); + } + + if (!args.minPoolSize.isZero() || !args.maxPoolSize.isZero()) { + this.filters.push(new PoolSizeFilter(connection, args.quoteToken, args.minPoolSize, args.maxPoolSize)); + } + } + + public async execute(poolState: LiquidityStateV4): Promise { + if (this.filters.length === 0) { + return true; + } + + const result = await Promise.all(this.filters.map((f) => f.execute(poolState))); + const pass = result.every((r) => r.ok); + + if (pass) { + return true; + } + + for (const filterResult of result.filter((r) => !r.ok)) { + logger.info(filterResult.message); + } + + return false; + } +} diff --git a/filters/pool-size.filter.ts b/filters/pool-size.filter.ts new file mode 100644 index 0000000..b1377c4 --- /dev/null +++ b/filters/pool-size.filter.ts @@ -0,0 +1,36 @@ +import { Filter, FilterResult } from './pool-filters'; +import { LiquidityStateV4, Token, TokenAmount } from '@raydium-io/raydium-sdk'; +import { Connection } from '@solana/web3.js'; + +export class PoolSizeFilter implements Filter { + constructor( + private readonly connection: Connection, + private readonly quoteToken: Token, + private readonly minPoolSize: TokenAmount, + private readonly maxPoolSize: TokenAmount, + ) {} + + async execute(poolState: LiquidityStateV4): Promise { + const response = await this.connection.getTokenAccountBalance(poolState.quoteVault, this.connection.commitment); + const poolSize = new TokenAmount(this.quoteToken, response.value.amount, true); + let inRange = true; + + if (!this.maxPoolSize?.isZero()) { + inRange = poolSize.lt(this.maxPoolSize); + + if (!inRange) { + return { ok: false, message: `PoolSize -> Pool size ${poolSize.toFixed()} > ${this.maxPoolSize.toFixed()}` }; + } + } + + if (!this.minPoolSize?.isZero()) { + inRange = poolSize.gt(this.minPoolSize); + + if (!inRange) { + return { ok: false, message: `PoolSize -> Pool size ${poolSize.toFixed()} < ${this.minPoolSize.toFixed()}` }; + } + } + + return { ok: inRange }; + } +} diff --git a/filters/renounced.filter.ts b/filters/renounced.filter.ts new file mode 100644 index 0000000..56ece08 --- /dev/null +++ b/filters/renounced.filter.ts @@ -0,0 +1,26 @@ +import { Filter, FilterResult } from './pool-filters'; +import { MintLayout } from '@solana/spl-token'; +import { Connection } from '@solana/web3.js'; +import { LiquidityStateV4 } from '@raydium-io/raydium-sdk'; +import { logger } from '../helpers'; + +export class RenouncedFilter implements Filter { + constructor(private readonly connection: Connection) {} + + async execute(poolState: LiquidityStateV4): Promise { + try { + const accountInfo = await this.connection.getAccountInfo(poolState.baseMint, this.connection.commitment); + if (!accountInfo?.data) { + return { ok: false, message: 'Renounced -> Failed to fetch account data' }; + } + + const deserialize = MintLayout.decode(accountInfo.data); + const renounced = deserialize.mintAuthorityOption === 0; + return { ok: renounced, message: renounced ? undefined : 'Renounced -> Creator can mint more tokens' }; + } catch (e) { + logger.error({ mint: poolState.baseMint }, `Failed to check if mint is renounced`); + } + + return { ok: false, message: 'Renounced -> Failed to check if mint is renounced' }; + } +} diff --git a/helpers/constants.ts b/helpers/constants.ts new file mode 100644 index 0000000..c860a72 --- /dev/null +++ b/helpers/constants.ts @@ -0,0 +1,57 @@ +import { Logger } from 'pino'; +import dotenv from 'dotenv'; +import { Commitment } from '@solana/web3.js'; +import { logger } from './logger'; + +dotenv.config(); + +const retrieveEnvVariable = (variableName: string, logger: Logger) => { + const variable = process.env[variableName] || ''; + if (!variable) { + logger.error(`${variableName} is not set`); + process.exit(1); + } + return variable; +}; + +// Wallet +export const PRIVATE_KEY = retrieveEnvVariable('PRIVATE_KEY', logger); + +// Connection +export const NETWORK = 'mainnet-beta'; +export const COMMITMENT_LEVEL: Commitment = retrieveEnvVariable('COMMITMENT_LEVEL', logger) as Commitment; +export const RPC_ENDPOINT = retrieveEnvVariable('RPC_ENDPOINT', logger); +export const RPC_WEBSOCKET_ENDPOINT = retrieveEnvVariable('RPC_WEBSOCKET_ENDPOINT', logger); + +// Bot +export const LOG_LEVEL = retrieveEnvVariable('LOG_LEVEL', logger); +export const ONE_TOKEN_AT_A_TIME = retrieveEnvVariable('ONE_TOKEN_AT_A_TIME', logger) === 'true'; +export const COMPUTE_UNIT_LIMIT = Number(retrieveEnvVariable('COMPUTE_UNIT_LIMIT', logger)); +export const COMPUTE_UNIT_PRICE = Number(retrieveEnvVariable('COMPUTE_UNIT_PRICE', logger)); +export const PRE_LOAD_EXISTING_MARKETS = retrieveEnvVariable('PRE_LOAD_EXISTING_MARKETS', logger) === 'true'; +export const CACHE_NEW_MARKETS = retrieveEnvVariable('CACHE_NEW_MARKETS', logger) === 'true'; + +// Buy +export const AUTO_BUY_DELAY = Number(retrieveEnvVariable('AUTO_BUY_DELAY', logger)); +export const QUOTE_MINT = retrieveEnvVariable('QUOTE_MINT', logger); +export const QUOTE_AMOUNT = retrieveEnvVariable('QUOTE_AMOUNT', logger); +export const MAX_BUY_RETRIES = Number(retrieveEnvVariable('MAX_BUY_RETRIES', logger)); +export const BUY_SLIPPAGE = Number(retrieveEnvVariable('BUY_SLIPPAGE', logger)); + +// Sell +export const AUTO_SELL = retrieveEnvVariable('AUTO_SELL', logger) === 'true'; +export const AUTO_SELL_DELAY = Number(retrieveEnvVariable('AUTO_SELL_DELAY', logger)); +export const MAX_SELL_RETRIES = Number(retrieveEnvVariable('MAX_SELL_RETRIES', logger)); +export const TAKE_PROFIT = Number(retrieveEnvVariable('TAKE_PROFIT', logger)); +export const STOP_LOSS = Number(retrieveEnvVariable('STOP_LOSS', logger)); +export const PRICE_CHECK_INTERVAL = Number(retrieveEnvVariable('PRICE_CHECK_INTERVAL', logger)); +export const PRICE_CHECK_DURATION = Number(retrieveEnvVariable('PRICE_CHECK_DURATION', logger)); +export const SELL_SLIPPAGE = Number(retrieveEnvVariable('SELL_SLIPPAGE', logger)); + +// Filters +export const CHECK_IF_MINT_IS_RENOUNCED = retrieveEnvVariable('CHECK_IF_MINT_IS_RENOUNCED', logger) === 'true'; +export const CHECK_IF_BURNED = retrieveEnvVariable('CHECK_IF_BURNED', logger) === 'true'; +export const MIN_POOL_SIZE = retrieveEnvVariable('MIN_POOL_SIZE', logger); +export const MAX_POOL_SIZE = retrieveEnvVariable('MAX_POOL_SIZE', logger); +export const USE_SNIPE_LIST = retrieveEnvVariable('USE_SNIPE_LIST', logger) === 'true'; +export const SNIPE_LIST_REFRESH_INTERVAL = Number(retrieveEnvVariable('SNIPE_LIST_REFRESH_INTERVAL', logger)); diff --git a/helpers/index.ts b/helpers/index.ts new file mode 100644 index 0000000..3ff8cee --- /dev/null +++ b/helpers/index.ts @@ -0,0 +1,7 @@ +export * from './market'; +export * from './liquidity'; +export * from './logger'; +export * from './constants'; +export * from './token'; +export * from './wallet'; +export * from './promises' diff --git a/liquidity/liquidity.ts b/helpers/liquidity.ts similarity index 50% rename from liquidity/liquidity.ts rename to helpers/liquidity.ts index 38667a3..971586d 100644 --- a/liquidity/liquidity.ts +++ b/helpers/liquidity.ts @@ -1,26 +1,6 @@ -import { Commitment, Connection, PublicKey } from '@solana/web3.js'; -import { - Liquidity, - LiquidityPoolKeys, - Market, - TokenAccount, - SPL_ACCOUNT_LAYOUT, - publicKey, - struct, - MAINNET_PROGRAM_ID, - LiquidityStateV4, -} from '@raydium-io/raydium-sdk'; -import { TOKEN_PROGRAM_ID } from '@solana/spl-token'; -import { MinimalMarketLayoutV3 } from '../market'; - -export const RAYDIUM_LIQUIDITY_PROGRAM_ID_V4 = MAINNET_PROGRAM_ID.AmmV4; -export const OPENBOOK_PROGRAM_ID = MAINNET_PROGRAM_ID.OPENBOOK_MARKET; - -export const MINIMAL_MARKET_STATE_LAYOUT_V3 = struct([ - publicKey('eventQueue'), - publicKey('bids'), - publicKey('asks'), -]); +import { PublicKey } from '@solana/web3.js'; +import { Liquidity, LiquidityPoolKeys, LiquidityStateV4, MAINNET_PROGRAM_ID, Market } from '@raydium-io/raydium-sdk'; +import { MinimalMarketLayoutV3 } from './market'; export function createPoolKeys( id: PublicKey, @@ -36,9 +16,9 @@ export function createPoolKeys( quoteDecimals: accountData.quoteDecimal.toNumber(), lpDecimals: 5, version: 4, - programId: RAYDIUM_LIQUIDITY_PROGRAM_ID_V4, + programId: MAINNET_PROGRAM_ID.AmmV4, authority: Liquidity.getAssociatedAuthority({ - programId: RAYDIUM_LIQUIDITY_PROGRAM_ID_V4, + programId: MAINNET_PROGRAM_ID.AmmV4, }).publicKey, openOrders: accountData.openOrders, targetOrders: accountData.targetOrders, @@ -61,28 +41,3 @@ export function createPoolKeys( lookupTableAccount: PublicKey.default, }; } - -export async function getTokenAccounts( - connection: Connection, - owner: PublicKey, - commitment?: Commitment, -) { - const tokenResp = await connection.getTokenAccountsByOwner( - owner, - { - programId: TOKEN_PROGRAM_ID, - }, - commitment, - ); - - const accounts: TokenAccount[] = []; - for (const { pubkey, account } of tokenResp.value) { - accounts.push({ - pubkey, - programId: account.owner, - accountInfo: SPL_ACCOUNT_LAYOUT.decode(account.data), - }); - } - - return accounts; -} diff --git a/utils/logger.ts b/helpers/logger.ts similarity index 90% rename from utils/logger.ts rename to helpers/logger.ts index 393068e..53c6bf9 100644 --- a/utils/logger.ts +++ b/helpers/logger.ts @@ -1,4 +1,4 @@ -import pino from "pino"; +import pino from 'pino'; const transport = pino.transport({ target: 'pino-pretty', diff --git a/market/market.ts b/helpers/market.ts similarity index 64% rename from market/market.ts rename to helpers/market.ts index 0593e35..54734f9 100644 --- a/market/market.ts +++ b/helpers/market.ts @@ -1,10 +1,9 @@ import { Commitment, Connection, PublicKey } from '@solana/web3.js'; -import { GetStructureSchema, MARKET_STATE_LAYOUT_V3 } from '@raydium-io/raydium-sdk'; -import { MINIMAL_MARKET_STATE_LAYOUT_V3 } from '../liquidity'; +import { GetStructureSchema, MARKET_STATE_LAYOUT_V3, publicKey, struct } from '@raydium-io/raydium-sdk'; +export const MINIMAL_MARKET_STATE_LAYOUT_V3 = struct([publicKey('eventQueue'), publicKey('bids'), publicKey('asks')]); export type MinimalMarketStateLayoutV3 = typeof MINIMAL_MARKET_STATE_LAYOUT_V3; -export type MinimalMarketLayoutV3 = - GetStructureSchema; +export type MinimalMarketLayoutV3 = GetStructureSchema; export async function getMinimalMarketV3( connection: Connection, diff --git a/helpers/promises.ts b/helpers/promises.ts new file mode 100644 index 0000000..dfdc54a --- /dev/null +++ b/helpers/promises.ts @@ -0,0 +1 @@ +export const sleep = (ms = 0) => new Promise((resolve) => setTimeout(resolve, ms)); diff --git a/helpers/token.ts b/helpers/token.ts new file mode 100644 index 0000000..0731245 --- /dev/null +++ b/helpers/token.ts @@ -0,0 +1,23 @@ +import { Token } from '@raydium-io/raydium-sdk'; +import { TOKEN_PROGRAM_ID } from '@solana/spl-token'; +import { PublicKey } from '@solana/web3.js'; + +export function getToken(token: string) { + switch (token) { + case 'WSOL': { + return Token.WSOL; + } + case 'USDC': { + return new Token( + TOKEN_PROGRAM_ID, + new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'), + 6, + 'USDC', + 'USDC', + ); + } + default: { + throw new Error(`Unsupported quote mint "${token}". Supported values are USDC and WSOL`); + } + } +} diff --git a/helpers/wallet.ts b/helpers/wallet.ts new file mode 100644 index 0000000..3f6f7df --- /dev/null +++ b/helpers/wallet.ts @@ -0,0 +1,21 @@ +import { Keypair } from '@solana/web3.js'; +import bs58 from 'bs58'; +import { mnemonicToSeedSync } from 'bip39'; +import { derivePath } from 'ed25519-hd-key'; + +export function getWallet(wallet: string): Keypair { + // most likely someone pasted the private key in binary format + if (wallet.startsWith('[')) { + return Keypair.fromSecretKey(JSON.parse(wallet)); + } + + // most likely someone pasted mnemonic + if (wallet.split(' ').length > 1) { + const seed = mnemonicToSeedSync(wallet, ''); + const path = `m/44'/501'/0'/0'`; // we assume it's first path + return Keypair.fromSeed(derivePath(path, seed.toString('hex')).key); + } + + // most likely someone pasted base58 encoded private key + return Keypair.fromSecretKey(bs58.decode(wallet)); +} diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..d4f25aa --- /dev/null +++ b/index.ts @@ -0,0 +1,192 @@ +import { MarketCache, PoolCache } from './cache'; +import { Listeners } from './listeners'; +import { Connection, KeyedAccountInfo, Keypair } from '@solana/web3.js'; +import { LIQUIDITY_STATE_LAYOUT_V4, MARKET_STATE_LAYOUT_V3, Token, TokenAmount } from '@raydium-io/raydium-sdk'; +import { AccountLayout, getAssociatedTokenAddressSync } from '@solana/spl-token'; +import { Bot, BotConfig } from './bot'; +import { DefaultTransactionExecutor } from './transactions'; +import { + getToken, + getWallet, + logger, + COMMITMENT_LEVEL, + RPC_ENDPOINT, + RPC_WEBSOCKET_ENDPOINT, + PRE_LOAD_EXISTING_MARKETS, + LOG_LEVEL, + CHECK_IF_MINT_IS_RENOUNCED, + CHECK_IF_BURNED, + QUOTE_MINT, + MAX_POOL_SIZE, + MIN_POOL_SIZE, + QUOTE_AMOUNT, + PRIVATE_KEY, + USE_SNIPE_LIST, + ONE_TOKEN_AT_A_TIME, + AUTO_SELL_DELAY, + MAX_SELL_RETRIES, + AUTO_SELL, + MAX_BUY_RETRIES, + AUTO_BUY_DELAY, + COMPUTE_UNIT_LIMIT, + COMPUTE_UNIT_PRICE, + CACHE_NEW_MARKETS, + TAKE_PROFIT, + STOP_LOSS, + BUY_SLIPPAGE, + SELL_SLIPPAGE, + PRICE_CHECK_DURATION, + PRICE_CHECK_INTERVAL, SNIPE_LIST_REFRESH_INTERVAL, +} from './helpers'; +import { version } from './package.json'; + +const connection = new Connection(RPC_ENDPOINT, { + wsEndpoint: RPC_WEBSOCKET_ENDPOINT, + commitment: COMMITMENT_LEVEL, +}); + +function printDetails(wallet: Keypair, quoteToken: Token, botConfig: BotConfig) { + logger.info(` + .. :-===++++- + .-==+++++++- =+++++++++- + ..:::--===+=.=: .+++++++++++:=+++++++++: + .==+++++++++++++++=:+++: .+++++++++++.=++++++++-. + .-+++++++++++++++=:=++++- .+++++++++=:.=+++++-::-. + -:+++++++++++++=:+++++++- .++++++++-:- =+++++=-: + -:++++++=++++=:++++=++++= .++++++++++- =+++++: + -:++++-:=++=:++++=:-+++++:+++++====--:::::::. + ::=+-:::==:=+++=::-:--::::::::::---------::. + ::-: .::::::::. --------:::.. + :- .:.-:::. + + WARP DRIVE ACTIVATED 🚀🐟 + Made with ❤️ by humans. + Version: ${version} + `); + + logger.info('------- CONFIGURATION START -------'); + logger.info(`Wallet: ${wallet.publicKey.toString()}`); + + logger.info('- Bot -'); + logger.info(`Compute Unit limit: ${botConfig.unitLimit}`); + logger.info(`Compute Unit price (micro lamports): ${botConfig.unitPrice}`); + logger.info(`Single token at the time: ${botConfig.oneTokenAtATime}`); + logger.info(`Pre load existing markets: ${PRE_LOAD_EXISTING_MARKETS}`); + logger.info(`Cache new markets: ${CACHE_NEW_MARKETS}`); + logger.info(`Log level: ${LOG_LEVEL}`); + + logger.info('- Buy -'); + logger.info(`Buy amount: ${botConfig.quoteAmount.toFixed()} ${botConfig.quoteToken.name}`); + logger.info(`Auto buy delay: ${botConfig.autoBuyDelay} ms`); + logger.info(`Max buy retries: ${botConfig.maxBuyRetries}`); + logger.info(`Buy amount (${quoteToken.symbol}): ${botConfig.quoteAmount.toFixed()}`); + logger.info(`Buy slippage: ${botConfig.buySlippage}%`); + + logger.info('- Sell -'); + logger.info(`Auto sell: ${AUTO_SELL}`); + logger.info(`Auto sell delay: ${botConfig.autoSellDelay} ms`); + logger.info(`Max sell retries: ${botConfig.maxSellRetries}`); + logger.info(`Sell slippage: ${botConfig.sellSlippage}%`); + logger.info(`Price check interval: ${botConfig.priceCheckInterval} ms`); + logger.info(`Price check duration: ${botConfig.priceCheckDuration} ms`); + logger.info(`Take profit: ${botConfig.takeProfit}%`); + logger.info(`Stop loss: ${botConfig.stopLoss}%`); + + logger.info('- Filters -'); + logger.info(`Snipe list: ${botConfig.useSnipeList}`); + logger.info(`Snipe list refresh interval: ${SNIPE_LIST_REFRESH_INTERVAL} ms`); + logger.info(`Check renounced: ${botConfig.checkRenounced}`); + logger.info(`Check burned: ${botConfig.checkBurned}`); + logger.info(`Min pool size: ${botConfig.minPoolSize.toFixed()}`); + logger.info(`Max pool size: ${botConfig.maxPoolSize.toFixed()}`); + + logger.info('------- CONFIGURATION END -------'); + + logger.info('Bot is running! Press CTRL + C to stop it.'); +} + +const runListener = async () => { + logger.level = LOG_LEVEL; + logger.info('Bot is starting...'); + + const marketCache = new MarketCache(connection); + const poolCache = new PoolCache(); + const txExecutor = new DefaultTransactionExecutor(connection); + const wallet = getWallet(PRIVATE_KEY.trim()); + const quoteToken = getToken(QUOTE_MINT); + const botConfig = { + wallet, + quoteAta: getAssociatedTokenAddressSync(quoteToken.mint, wallet.publicKey), + checkRenounced: CHECK_IF_MINT_IS_RENOUNCED, + checkBurned: CHECK_IF_BURNED, + minPoolSize: new TokenAmount(quoteToken, MIN_POOL_SIZE, false), + maxPoolSize: new TokenAmount(quoteToken, MAX_POOL_SIZE, false), + quoteToken, + quoteAmount: new TokenAmount(quoteToken, QUOTE_AMOUNT, false), + oneTokenAtATime: ONE_TOKEN_AT_A_TIME, + useSnipeList: USE_SNIPE_LIST, + autoSellDelay: AUTO_SELL_DELAY, + maxSellRetries: MAX_SELL_RETRIES, + autoBuyDelay: AUTO_BUY_DELAY, + maxBuyRetries: MAX_BUY_RETRIES, + unitLimit: COMPUTE_UNIT_LIMIT, + unitPrice: COMPUTE_UNIT_PRICE, + takeProfit: TAKE_PROFIT, + stopLoss: STOP_LOSS, + buySlippage: BUY_SLIPPAGE, + sellSlippage: SELL_SLIPPAGE, + priceCheckInterval: PRICE_CHECK_INTERVAL, + priceCheckDuration: PRICE_CHECK_DURATION, + }; + + const bot = new Bot(connection, marketCache, poolCache, txExecutor, botConfig); + const valid = await bot.validate(); + + if (!valid) { + logger.info('Bot is exiting...'); + process.exit(1); + } + + if (PRE_LOAD_EXISTING_MARKETS) { + await marketCache.init({ quoteToken }); + } + + const runTimestamp = Math.floor(new Date().getTime() / 1000); + const listeners = new Listeners(connection); + await listeners.start({ + walletPublicKey: wallet.publicKey, + quoteToken, + autoSell: AUTO_SELL, + cacheNewMarkets: CACHE_NEW_MARKETS, + }); + + listeners.on('market', (updatedAccountInfo: KeyedAccountInfo) => { + const marketState = MARKET_STATE_LAYOUT_V3.decode(updatedAccountInfo.accountInfo.data); + marketCache.save(updatedAccountInfo.accountId.toString(), marketState); + }); + + listeners.on('pool', async (updatedAccountInfo: KeyedAccountInfo) => { + const poolState = LIQUIDITY_STATE_LAYOUT_V4.decode(updatedAccountInfo.accountInfo.data); + const poolOpenTime = parseInt(poolState.poolOpenTime.toString()); + const exists = await poolCache.get(poolState.baseMint.toString()); + + if (!exists && poolOpenTime > runTimestamp) { + poolCache.save(updatedAccountInfo.accountId.toString(), poolState); + await bot.buy(updatedAccountInfo.accountId, poolState); + } + }); + + listeners.on('wallet', async (updatedAccountInfo: KeyedAccountInfo) => { + const accountData = AccountLayout.decode(updatedAccountInfo.accountInfo.data); + + if (accountData.mint.equals(quoteToken.mint)) { + return; + } + + await bot.sell(updatedAccountInfo.accountId, accountData); + }); + + printDetails(wallet, quoteToken, botConfig); +}; + +runListener(); diff --git a/liquidity/index.ts b/liquidity/index.ts deleted file mode 100644 index c6c4dbe..0000000 --- a/liquidity/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './liquidity'; diff --git a/listeners/index.ts b/listeners/index.ts new file mode 100644 index 0000000..52f405a --- /dev/null +++ b/listeners/index.ts @@ -0,0 +1 @@ +export * from './listeners'; diff --git a/listeners/listeners.ts b/listeners/listeners.ts new file mode 100644 index 0000000..0c191ec --- /dev/null +++ b/listeners/listeners.ts @@ -0,0 +1,112 @@ +import { LIQUIDITY_STATE_LAYOUT_V4, MAINNET_PROGRAM_ID, MARKET_STATE_LAYOUT_V3, Token } from '@raydium-io/raydium-sdk'; +import bs58 from 'bs58'; +import { Connection, PublicKey } from '@solana/web3.js'; +import { TOKEN_PROGRAM_ID } from '@solana/spl-token'; +import { EventEmitter } from 'events'; + +export class Listeners extends EventEmitter { + private subscriptions: number[] = []; + + constructor(private readonly connection: Connection) { + super(); + } + + public async start(config: { + walletPublicKey: PublicKey; + quoteToken: Token; + autoSell: boolean; + cacheNewMarkets: boolean; + }) { + if (config.cacheNewMarkets) { + const openBookSubscription = await this.subscribeToOpenBookMarkets(config); + this.subscriptions.push(openBookSubscription); + } + + const raydiumSubscription = await this.subscribeToRaydiumPools(config); + this.subscriptions.push(raydiumSubscription); + + if (config.autoSell) { + const walletSubscription = await this.subscribeToWalletChanges(config); + this.subscriptions.push(walletSubscription); + } + } + + private async subscribeToOpenBookMarkets(config: { quoteToken: Token }) { + return this.connection.onProgramAccountChange( + MAINNET_PROGRAM_ID.OPENBOOK_MARKET, + async (updatedAccountInfo) => { + this.emit('market', updatedAccountInfo); + }, + this.connection.commitment, + [ + { dataSize: MARKET_STATE_LAYOUT_V3.span }, + { + memcmp: { + offset: MARKET_STATE_LAYOUT_V3.offsetOf('quoteMint'), + bytes: config.quoteToken.mint.toBase58(), + }, + }, + ], + ); + } + + private async subscribeToRaydiumPools(config: { quoteToken: Token }) { + return this.connection.onProgramAccountChange( + MAINNET_PROGRAM_ID.AmmV4, + async (updatedAccountInfo) => { + this.emit('pool', updatedAccountInfo); + }, + this.connection.commitment, + [ + { dataSize: LIQUIDITY_STATE_LAYOUT_V4.span }, + { + memcmp: { + offset: LIQUIDITY_STATE_LAYOUT_V4.offsetOf('quoteMint'), + bytes: config.quoteToken.mint.toBase58(), + }, + }, + { + memcmp: { + offset: LIQUIDITY_STATE_LAYOUT_V4.offsetOf('marketProgramId'), + bytes: MAINNET_PROGRAM_ID.OPENBOOK_MARKET.toBase58(), + }, + }, + { + memcmp: { + offset: LIQUIDITY_STATE_LAYOUT_V4.offsetOf('status'), + bytes: bs58.encode([6, 0, 0, 0, 0, 0, 0, 0]), + }, + }, + ], + ); + } + + private async subscribeToWalletChanges(config: { walletPublicKey: PublicKey }) { + return this.connection.onProgramAccountChange( + TOKEN_PROGRAM_ID, + async (updatedAccountInfo) => { + this.emit('wallet', updatedAccountInfo); + }, + this.connection.commitment, + [ + { + dataSize: 165, + }, + { + memcmp: { + offset: 32, + bytes: config.walletPublicKey.toBase58(), + }, + }, + ], + ); + } + + public async stop() { + for (let i = this.subscriptions.length; i >= 0; --i) { + const subscription = this.subscriptions[i]; + await this.connection.removeAccountChangeListener(subscription); + this.subscriptions.splice(i, 1); + } + } +} diff --git a/market/index.ts b/market/index.ts deleted file mode 100644 index 9fc9e36..0000000 --- a/market/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './market'; diff --git a/package-lock.json b/package-lock.json index df907b2..a5dc331 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,10 +9,15 @@ "@raydium-io/raydium-sdk": "^1.3.1-beta.47", "@solana/spl-token": "^0.4.0", "@solana/web3.js": "^1.89.1", + "async-mutex": "^0.5.0", "bigint-buffer": "^1.1.5", + "bip39": "^3.1.0", "bn.js": "^5.2.1", "bs58": "^5.0.0", "dotenv": "^16.4.1", + "ed25519-hd-key": "^1.3.0", + "i": "^0.3.7", + "npm": "^10.5.2", "pino": "^8.18.0", "pino-pretty": "^10.3.1", "pino-std-serializers": "^6.2.2" @@ -406,6 +411,14 @@ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", "dev": true }, + "node_modules/async-mutex": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", + "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -493,6 +506,14 @@ "file-uri-to-path": "1.0.0" } }, + "node_modules/bip39": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.1.0.tgz", + "integrity": "sha512-c9kiwdk45Do5GL0vJMe7tS95VjCii65mYAH7DfWl3uW8AVzXKQVUm64i3hzVybBDMp9r7j9iNxR85+ul8MdN/A==", + "dependencies": { + "@noble/hashes": "^1.2.0" + } + }, "node_modules/bn.js": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", @@ -568,6 +589,15 @@ "node": ">=6.14.2" } }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/colorette": { "version": "2.0.20", "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", @@ -589,6 +619,31 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -652,6 +707,15 @@ "url": "https://github.com/motdotla/dotenv?sponsor=1" } }, + "node_modules/ed25519-hd-key": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ed25519-hd-key/-/ed25519-hd-key-1.3.0.tgz", + "integrity": "sha512-IWwAyiiuJQhgu3L8NaHb68eJxTu2pgCwxIBdgpLJdKpYZM46+AXePSVTr7fkNKaUOfOL4IrjEUaQvyVRIDP7fg==", + "dependencies": { + "create-hmac": "1.1.7", + "tweetnacl": "1.0.3" + } + }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -773,6 +837,32 @@ "node": ">= 6" } }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash-base/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/help-me": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", @@ -786,6 +876,14 @@ "ms": "^2.0.0" } }, + "node_modules/i": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/i/-/i-0.3.7.tgz", + "integrity": "sha512-FYz4wlXgkQwIPqhzC5TdNMLSE5+GS1IIDJZY/1ZiEPCT2S3COUVZeT5OW4BmW4r5LHLQuOosSwsvnroG9GR59Q==", + "engines": { + "node": ">=0.4" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -805,6 +903,11 @@ } ] }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, "node_modules/isomorphic-ws": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", @@ -885,6 +988,16 @@ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -928,6 +1041,2584 @@ "node-gyp-build-test": "build-test.js" } }, + "node_modules/npm": { + "version": "10.5.2", + "resolved": "https://registry.npmjs.org/npm/-/npm-10.5.2.tgz", + "integrity": "sha512-cHVG7QEJwJdZyOrK0dKX5uf3R5Fd0E8AcmSES1jLtO52UT1enUKZ96Onw/xwq4CbrTZEnDuu2Vf9kCQh/Sd12w==", + "bundleDependencies": [ + "@isaacs/string-locale-compare", + "@npmcli/arborist", + "@npmcli/config", + "@npmcli/fs", + "@npmcli/map-workspaces", + "@npmcli/package-json", + "@npmcli/promise-spawn", + "@npmcli/redact", + "@npmcli/run-script", + "@sigstore/tuf", + "abbrev", + "archy", + "cacache", + "chalk", + "ci-info", + "cli-columns", + "cli-table3", + "columnify", + "fastest-levenshtein", + "fs-minipass", + "glob", + "graceful-fs", + "hosted-git-info", + "ini", + "init-package-json", + "is-cidr", + "json-parse-even-better-errors", + "libnpmaccess", + "libnpmdiff", + "libnpmexec", + "libnpmfund", + "libnpmhook", + "libnpmorg", + "libnpmpack", + "libnpmpublish", + "libnpmsearch", + "libnpmteam", + "libnpmversion", + "make-fetch-happen", + "minimatch", + "minipass", + "minipass-pipeline", + "ms", + "node-gyp", + "nopt", + "normalize-package-data", + "npm-audit-report", + "npm-install-checks", + "npm-package-arg", + "npm-pick-manifest", + "npm-profile", + "npm-registry-fetch", + "npm-user-validate", + "npmlog", + "p-map", + "pacote", + "parse-conflict-json", + "proc-log", + "qrcode-terminal", + "read", + "semver", + "spdx-expression-parse", + "ssri", + "supports-color", + "tar", + "text-table", + "tiny-relative-date", + "treeverse", + "validate-npm-package-name", + "which", + "write-file-atomic" + ], + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/arborist": "^7.2.1", + "@npmcli/config": "^8.0.2", + "@npmcli/fs": "^3.1.0", + "@npmcli/map-workspaces": "^3.0.6", + "@npmcli/package-json": "^5.0.2", + "@npmcli/promise-spawn": "^7.0.1", + "@npmcli/redact": "^1.1.0", + "@npmcli/run-script": "^7.0.4", + "@sigstore/tuf": "^2.3.2", + "abbrev": "^2.0.0", + "archy": "~1.0.0", + "cacache": "^18.0.2", + "chalk": "^5.3.0", + "ci-info": "^4.0.0", + "cli-columns": "^4.0.0", + "cli-table3": "^0.6.4", + "columnify": "^1.6.0", + "fastest-levenshtein": "^1.0.16", + "fs-minipass": "^3.0.3", + "glob": "^10.3.12", + "graceful-fs": "^4.2.11", + "hosted-git-info": "^7.0.1", + "ini": "^4.1.2", + "init-package-json": "^6.0.2", + "is-cidr": "^5.0.5", + "json-parse-even-better-errors": "^3.0.1", + "libnpmaccess": "^8.0.1", + "libnpmdiff": "^6.0.3", + "libnpmexec": "^7.0.4", + "libnpmfund": "^5.0.1", + "libnpmhook": "^10.0.0", + "libnpmorg": "^6.0.1", + "libnpmpack": "^6.0.3", + "libnpmpublish": "^9.0.2", + "libnpmsearch": "^7.0.0", + "libnpmteam": "^6.0.0", + "libnpmversion": "^5.0.1", + "make-fetch-happen": "^13.0.0", + "minimatch": "^9.0.4", + "minipass": "^7.0.4", + "minipass-pipeline": "^1.2.4", + "ms": "^2.1.2", + "node-gyp": "^10.1.0", + "nopt": "^7.2.0", + "normalize-package-data": "^6.0.0", + "npm-audit-report": "^5.0.0", + "npm-install-checks": "^6.3.0", + "npm-package-arg": "^11.0.1", + "npm-pick-manifest": "^9.0.0", + "npm-profile": "^9.0.0", + "npm-registry-fetch": "^16.2.0", + "npm-user-validate": "^2.0.0", + "npmlog": "^7.0.1", + "p-map": "^4.0.0", + "pacote": "^17.0.6", + "parse-conflict-json": "^3.0.1", + "proc-log": "^3.0.0", + "qrcode-terminal": "^0.12.0", + "read": "^3.0.1", + "semver": "^7.6.0", + "spdx-expression-parse": "^4.0.0", + "ssri": "^10.0.5", + "supports-color": "^9.4.0", + "tar": "^6.2.1", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "treeverse": "^3.0.0", + "validate-npm-package-name": "^5.0.0", + "which": "^4.0.0", + "write-file-atomic": "^5.0.1" + }, + "bin": { + "npm": "bin/npm-cli.js", + "npx": "bin/npx-cli.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm/node_modules/@colors/colors": { + "version": "1.5.0", + "inBundle": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui": { + "version": "8.0.2", + "inBundle": true, + "license": "ISC", + "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" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/@isaacs/string-locale-compare": { + "version": "1.1.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/@npmcli/agent": { + "version": "2.2.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/arborist": { + "version": "7.4.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/fs": "^3.1.0", + "@npmcli/installed-package-contents": "^2.0.2", + "@npmcli/map-workspaces": "^3.0.2", + "@npmcli/metavuln-calculator": "^7.0.0", + "@npmcli/name-from-folder": "^2.0.0", + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/package-json": "^5.0.0", + "@npmcli/query": "^3.1.0", + "@npmcli/redact": "^1.1.0", + "@npmcli/run-script": "^7.0.2", + "bin-links": "^4.0.1", + "cacache": "^18.0.0", + "common-ancestor-path": "^1.0.1", + "hosted-git-info": "^7.0.1", + "json-parse-even-better-errors": "^3.0.0", + "json-stringify-nice": "^1.1.4", + "minimatch": "^9.0.4", + "nopt": "^7.0.0", + "npm-install-checks": "^6.2.0", + "npm-package-arg": "^11.0.1", + "npm-pick-manifest": "^9.0.0", + "npm-registry-fetch": "^16.2.0", + "npmlog": "^7.0.1", + "pacote": "^17.0.4", + "parse-conflict-json": "^3.0.0", + "proc-log": "^3.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^3.0.1", + "read-package-json-fast": "^3.0.2", + "semver": "^7.3.7", + "ssri": "^10.0.5", + "treeverse": "^3.0.0", + "walk-up-path": "^3.0.1" + }, + "bin": { + "arborist": "bin/index.js" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/config": { + "version": "8.2.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/map-workspaces": "^3.0.2", + "ci-info": "^4.0.0", + "ini": "^4.1.2", + "nopt": "^7.0.0", + "proc-log": "^3.0.0", + "read-package-json-fast": "^3.0.2", + "semver": "^7.3.5", + "walk-up-path": "^3.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/disparity-colors": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "ansi-styles": "^4.3.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/disparity-colors/node_modules/ansi-styles": { + "version": "4.3.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/@npmcli/fs": { + "version": "3.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/git": { + "version": "5.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^7.0.0", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^9.0.0", + "proc-log": "^3.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/installed-package-contents": { + "version": "2.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "bin": { + "installed-package-contents": "lib/index.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/map-workspaces": { + "version": "3.0.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/name-from-folder": "^2.0.0", + "glob": "^10.2.2", + "minimatch": "^9.0.0", + "read-package-json-fast": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { + "version": "7.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "cacache": "^18.0.0", + "json-parse-even-better-errors": "^3.0.0", + "pacote": "^17.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/name-from-folder": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/node-gyp": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/package-json": { + "version": "5.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^5.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^7.0.0", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^6.0.0", + "proc-log": "^3.0.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/promise-spawn": { + "version": "7.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/query": { + "version": "3.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/redact": { + "version": "1.1.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/run-script": { + "version": "7.0.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/package-json": "^5.0.0", + "@npmcli/promise-spawn": "^7.0.0", + "node-gyp": "^10.0.0", + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "inBundle": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/@sigstore/bundle": { + "version": "2.3.1", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/core": { + "version": "1.1.0", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/protobuf-specs": { + "version": "0.3.1", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/sign": { + "version": "2.3.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^2.3.0", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.1", + "make-fetch-happen": "^13.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/tuf": { + "version": "2.3.2", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.0", + "tuf-js": "^2.2.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@sigstore/verify": { + "version": "1.2.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^2.3.1", + "@sigstore/core": "^1.1.0", + "@sigstore/protobuf-specs": "^0.3.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@tufjs/models": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.3" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/abbrev": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/agent-base": { + "version": "7.1.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/aggregate-error": { + "version": "3.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ansi-regex": { + "version": "5.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ansi-styles": { + "version": "6.2.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/aproba": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/archy": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/are-we-there-yet": { + "version": "4.0.2", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/balanced-match": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/bin-links": { + "version": "4.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "cmd-shim": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "read-cmd-shim": "^4.0.0", + "write-file-atomic": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/binary-extensions": { + "version": "2.3.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/brace-expansion": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/npm/node_modules/builtins": { + "version": "5.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "semver": "^7.0.0" + } + }, + "node_modules/npm/node_modules/cacache": { + "version": "18.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/chalk": { + "version": "5.3.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/npm/node_modules/chownr": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ci-info": { + "version": "4.0.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/cidr-regex": { + "version": "4.0.5", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "ip-regex": "^5.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/clean-stack": { + "version": "2.2.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/cli-columns": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/cli-table3": { + "version": "0.6.4", + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/npm/node_modules/clone": { + "version": "1.0.4", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/npm/node_modules/cmd-shim": { + "version": "6.0.2", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/color-convert": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/npm/node_modules/color-name": { + "version": "1.1.4", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/color-support": { + "version": "1.1.3", + "inBundle": true, + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/npm/node_modules/columnify": { + "version": "1.6.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "strip-ansi": "^6.0.1", + "wcwidth": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/npm/node_modules/common-ancestor-path": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/console-control-strings": { + "version": "1.1.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/cross-spawn": { + "version": "7.0.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cssesc": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/debug": { + "version": "4.3.4", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/npm/node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/defaults": { + "version": "1.0.4", + "inBundle": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/diff": { + "version": "5.2.0", + "inBundle": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/npm/node_modules/eastasianwidth": { + "version": "0.2.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/emoji-regex": { + "version": "8.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/encoding": { + "version": "0.1.13", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/npm/node_modules/env-paths": { + "version": "2.2.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/err-code": { + "version": "2.0.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/exponential-backoff": { + "version": "3.1.1", + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/npm/node_modules/fastest-levenshtein": { + "version": "1.0.16", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/npm/node_modules/foreground-child": { + "version": "3.1.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/fs-minipass": { + "version": "3.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/function-bind": { + "version": "1.1.2", + "inBundle": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/npm/node_modules/gauge": { + "version": "5.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^4.0.1", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/glob": { + "version": "10.3.12", + "inBundle": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.6", + "minimatch": "^9.0.1", + "minipass": "^7.0.4", + "path-scurry": "^1.10.2" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/graceful-fs": { + "version": "4.2.11", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/has-unicode": { + "version": "2.0.1", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/hasown": { + "version": "2.0.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/npm/node_modules/hosted-git-info": { + "version": "7.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/http-cache-semantics": { + "version": "4.1.1", + "inBundle": true, + "license": "BSD-2-Clause" + }, + "node_modules/npm/node_modules/http-proxy-agent": { + "version": "7.0.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/https-proxy-agent": { + "version": "7.0.4", + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/iconv-lite": { + "version": "0.6.3", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/ignore-walk": { + "version": "6.0.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/imurmurhash": { + "version": "0.1.4", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/npm/node_modules/indent-string": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ini": { + "version": "4.1.2", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/init-package-json": { + "version": "6.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/package-json": "^5.0.0", + "npm-package-arg": "^11.0.0", + "promzard": "^1.0.0", + "read": "^3.0.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/ip-address": { + "version": "9.0.5", + "inBundle": true, + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/npm/node_modules/ip-address/node_modules/sprintf-js": { + "version": "1.1.3", + "inBundle": true, + "license": "BSD-3-Clause" + }, + "node_modules/npm/node_modules/ip-regex": { + "version": "5.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/is-cidr": { + "version": "5.0.5", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "cidr-regex": "^4.0.4" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/is-core-module": { + "version": "2.13.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/npm/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/is-lambda": { + "version": "1.0.1", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/isexe": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/jackspeak": { + "version": "2.3.6", + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/npm/node_modules/jsbn": { + "version": "1.1.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/json-parse-even-better-errors": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/json-stringify-nice": { + "version": "1.1.4", + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/jsonparse": { + "version": "1.3.1", + "engines": [ + "node >= 0.2.0" + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff": { + "version": "6.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff-apply": { + "version": "5.5.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/libnpmaccess": { + "version": "8.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-package-arg": "^11.0.1", + "npm-registry-fetch": "^16.2.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmdiff": { + "version": "6.0.9", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^7.2.1", + "@npmcli/disparity-colors": "^3.0.0", + "@npmcli/installed-package-contents": "^2.0.2", + "binary-extensions": "^2.3.0", + "diff": "^5.1.0", + "minimatch": "^9.0.4", + "npm-package-arg": "^11.0.1", + "pacote": "^17.0.4", + "tar": "^6.2.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmexec": { + "version": "7.0.10", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^7.2.1", + "@npmcli/run-script": "^7.0.2", + "ci-info": "^4.0.0", + "npm-package-arg": "^11.0.1", + "npmlog": "^7.0.1", + "pacote": "^17.0.4", + "proc-log": "^3.0.0", + "read": "^3.0.1", + "read-package-json-fast": "^3.0.2", + "semver": "^7.3.7", + "walk-up-path": "^3.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmfund": { + "version": "5.0.7", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^7.2.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmhook": { + "version": "10.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^16.2.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmorg": { + "version": "6.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^16.2.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmpack": { + "version": "6.0.9", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^7.2.1", + "@npmcli/run-script": "^7.0.2", + "npm-package-arg": "^11.0.1", + "pacote": "^17.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmpublish": { + "version": "9.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "ci-info": "^4.0.0", + "normalize-package-data": "^6.0.0", + "npm-package-arg": "^11.0.1", + "npm-registry-fetch": "^16.2.0", + "proc-log": "^3.0.0", + "semver": "^7.3.7", + "sigstore": "^2.2.0", + "ssri": "^10.0.5" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmsearch": { + "version": "7.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^16.2.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmteam": { + "version": "6.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^16.2.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmversion": { + "version": "5.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^5.0.3", + "@npmcli/run-script": "^7.0.2", + "json-parse-even-better-errors": "^3.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/lru-cache": { + "version": "10.2.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/npm/node_modules/make-fetch-happen": { + "version": "13.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^2.0.0", + "cacache": "^18.0.0", + "http-cache-semantics": "^4.1.1", + "is-lambda": "^1.0.1", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/minimatch": { + "version": "9.0.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/minipass": { + "version": "7.0.4", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-collect": { + "version": "2.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-fetch": { + "version": "3.0.4", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/npm/node_modules/minipass-flush": { + "version": "1.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-json-stream": { + "version": "1.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + } + }, + "node_modules/npm/node_modules/minipass-json-stream/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline": { + "version": "1.2.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized": { + "version": "1.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minizlib": { + "version": "2.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/mkdirp": { + "version": "1.0.4", + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ms": { + "version": "2.1.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/mute-stream": { + "version": "1.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/negotiator": { + "version": "0.6.3", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm/node_modules/node-gyp": { + "version": "10.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^10.3.10", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^13.0.0", + "nopt": "^7.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^4.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/nopt": { + "version": "7.2.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/normalize-package-data": { + "version": "6.0.0", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^7.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-audit-report": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-bundled": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-install-checks": { + "version": "6.3.0", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-normalize-package-bin": { + "version": "3.0.1", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-package-arg": { + "version": "11.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^7.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-packlist": { + "version": "8.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "ignore-walk": "^6.0.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-pick-manifest": { + "version": "9.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "npm-package-arg": "^11.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-profile": { + "version": "9.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^16.0.0", + "proc-log": "^3.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-registry-fetch": { + "version": "16.2.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/redact": "^1.1.0", + "make-fetch-happen": "^13.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^11.0.0", + "proc-log": "^3.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-user-validate": { + "version": "2.0.0", + "inBundle": true, + "license": "BSD-2-Clause", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npmlog": { + "version": "7.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "are-we-there-yet": "^4.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^5.0.0", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/p-map": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/pacote": { + "version": "17.0.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^5.0.0", + "@npmcli/installed-package-contents": "^2.0.1", + "@npmcli/promise-spawn": "^7.0.0", + "@npmcli/run-script": "^7.0.0", + "cacache": "^18.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^11.0.0", + "npm-packlist": "^8.0.0", + "npm-pick-manifest": "^9.0.0", + "npm-registry-fetch": "^16.0.0", + "proc-log": "^3.0.0", + "promise-retry": "^2.0.1", + "read-package-json": "^7.0.0", + "read-package-json-fast": "^3.0.0", + "sigstore": "^2.2.0", + "ssri": "^10.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "lib/bin.js" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/parse-conflict-json": { + "version": "3.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^3.0.0", + "just-diff": "^6.0.0", + "just-diff-apply": "^5.2.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/path-key": { + "version": "3.1.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/path-scurry": { + "version": "1.10.2", + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/postcss-selector-parser": { + "version": "6.0.16", + "inBundle": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/proc-log": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/promise-all-reject-late": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-call-limit": { + "version": "3.0.1", + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-inflight": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/promise-retry": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/promzard": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "read": "^3.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/qrcode-terminal": { + "version": "0.12.0", + "inBundle": true, + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" + } + }, + "node_modules/npm/node_modules/read": { + "version": "3.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "mute-stream": "^1.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/read-cmd-shim": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/read-package-json": { + "version": "7.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "glob": "^10.2.2", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/read-package-json-fast": { + "version": "3.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/retry": { + "version": "0.12.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm/node_modules/safer-buffer": { + "version": "2.1.2", + "inBundle": true, + "license": "MIT", + "optional": true + }, + "node_modules/npm/node_modules/semver": { + "version": "7.6.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/set-blocking": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/shebang-command": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/shebang-regex": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/signal-exit": { + "version": "4.1.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/sigstore": { + "version": "2.3.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^2.3.1", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.1", + "@sigstore/sign": "^2.3.0", + "@sigstore/tuf": "^2.3.1", + "@sigstore/verify": "^1.2.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/smart-buffer": { + "version": "4.2.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks": { + "version": "2.8.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks-proxy-agent": { + "version": "8.0.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.1", + "debug": "^4.3.4", + "socks": "^2.7.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/spdx-correct": { + "version": "3.2.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-correct/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-exceptions": { + "version": "2.5.0", + "inBundle": true, + "license": "CC-BY-3.0" + }, + "node_modules/npm/node_modules/spdx-expression-parse": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-license-ids": { + "version": "3.0.17", + "inBundle": true, + "license": "CC0-1.0" + }, + "node_modules/npm/node_modules/ssri": { + "version": "10.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/string-width": { + "version": "4.2.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi": { + "version": "6.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/supports-color": { + "version": "9.4.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/npm/node_modules/tar": { + "version": "6.2.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass": { + "version": "2.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/text-table": { + "version": "0.2.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/tiny-relative-date": { + "version": "1.3.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/treeverse": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/tuf-js": { + "version": "2.2.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/models": "2.0.0", + "debug": "^4.3.4", + "make-fetch-happen": "^13.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/unique-filename": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/unique-slug": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/util-deprecate": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/validate-npm-package-license": { + "version": "3.0.4", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-name": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "builtins": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/walk-up-path": { + "version": "3.0.1", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/wcwidth": { + "version": "1.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/npm/node_modules/which": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/which/node_modules/isexe": { + "version": "3.1.1", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/npm/node_modules/wide-align": { + "version": "1.1.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/npm/node_modules/wrap-ansi": { + "version": "8.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/write-file-atomic": { + "version": "5.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/yallist": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC" + }, "node_modules/on-exit-leak-free": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.0.tgz", @@ -1075,6 +3766,15 @@ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, "node_modules/rpc-websockets": { "version": "7.9.0", "resolved": "https://registry.npmjs.org/rpc-websockets/-/rpc-websockets-7.9.0.tgz", @@ -1146,6 +3846,18 @@ "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==" }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, "node_modules/sonic-boom": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.8.0.tgz", @@ -1257,6 +3969,16 @@ } } }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" + }, "node_modules/typescript": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", @@ -1283,6 +4005,11 @@ "node": ">=6.14.2" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, "node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", diff --git a/package.json b/package.json index 75ea9c1..159ef17 100644 --- a/package.json +++ b/package.json @@ -1,18 +1,24 @@ { "name": "solana-sniper-bot", "author": "Filip Dundjer", + "version": "2.0.0", "scripts": { - "buy": "ts-node buy.ts", + "start": "ts-node index.ts", "tsc": "tsc --noEmit" }, "dependencies": { "@raydium-io/raydium-sdk": "^1.3.1-beta.47", "@solana/spl-token": "^0.4.0", "@solana/web3.js": "^1.89.1", + "async-mutex": "^0.5.0", "bigint-buffer": "^1.1.5", + "bip39": "^3.1.0", "bn.js": "^5.2.1", "bs58": "^5.0.0", "dotenv": "^16.4.1", + "ed25519-hd-key": "^1.3.0", + "i": "^0.3.7", + "npm": "^10.5.2", "pino": "^8.18.0", "pino-pretty": "^10.3.1", "pino-std-serializers": "^6.2.2" @@ -23,4 +29,4 @@ "ts-node": "^10.9.2", "typescript": "^5.3.3" } -} \ No newline at end of file +} diff --git a/transactions/default-transaction-executor.ts b/transactions/default-transaction-executor.ts new file mode 100644 index 0000000..fdb4311 --- /dev/null +++ b/transactions/default-transaction-executor.ts @@ -0,0 +1,37 @@ +import { BlockhashWithExpiryBlockHeight, Connection, Transaction, VersionedTransaction } from '@solana/web3.js'; +import { TransactionExecutor } from './transaction-executor.interface'; +import { logger } from '../helpers'; + +export class DefaultTransactionExecutor implements TransactionExecutor { + constructor(private readonly connection: Connection) {} + + public async executeAndConfirm( + transaction: Transaction | VersionedTransaction, + latestBlockhash: BlockhashWithExpiryBlockHeight, + ): Promise<{ confirmed: boolean; signature: string }> { + logger.debug('Executing transaction...'); + const signature = await this.execute(transaction); + + logger.debug({ signature }, 'Confirming transaction...'); + return this.confirm(signature, latestBlockhash); + } + + private async execute(transaction: Transaction | VersionedTransaction) { + return this.connection.sendRawTransaction(transaction.serialize(), { + preflightCommitment: this.connection.commitment, + }); + } + + private async confirm(signature: string, latestBlockhash: BlockhashWithExpiryBlockHeight) { + const confirmation = await this.connection.confirmTransaction( + { + signature, + lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, + blockhash: latestBlockhash.blockhash, + }, + this.connection.commitment, + ); + + return { confirmed: !confirmation.value.err, signature }; + } +} diff --git a/transactions/index.ts b/transactions/index.ts new file mode 100644 index 0000000..b376e26 --- /dev/null +++ b/transactions/index.ts @@ -0,0 +1,2 @@ +export * from './default-transaction-executor'; +export * from './transaction-executor.interface'; diff --git a/transactions/transaction-executor.interface.ts b/transactions/transaction-executor.interface.ts new file mode 100644 index 0000000..1871ae1 --- /dev/null +++ b/transactions/transaction-executor.interface.ts @@ -0,0 +1,8 @@ +import { BlockhashWithExpiryBlockHeight, Transaction, VersionedTransaction } from '@solana/web3.js'; + +export interface TransactionExecutor { + executeAndConfirm( + transaction: Transaction | VersionedTransaction, + latestBlockhash: BlockhashWithExpiryBlockHeight, + ): Promise<{ confirmed: boolean; signature: string }>; +} diff --git a/types/index.ts b/types/index.ts deleted file mode 100644 index e3ef92b..0000000 --- a/types/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './mint'; \ No newline at end of file diff --git a/types/mint.ts b/types/mint.ts deleted file mode 100644 index 270e3c1..0000000 --- a/types/mint.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { struct, u32, u8 } from '@solana/buffer-layout'; -import { bool, publicKey, u64 } from '@solana/buffer-layout-utils'; -import { Commitment, Connection, PublicKey } from '@solana/web3.js'; - -/** Information about a mint */ -export interface Mint { - /** Address of the mint */ - address: PublicKey; - /** - * Optional authority used to mint new tokens. The mint authority may only be provided during mint creation. - * If no mint authority is present then the mint has a fixed supply and no further tokens may be minted. - */ - mintAuthority: PublicKey | null; - /** Total supply of tokens */ - supply: bigint; - /** Number of base 10 digits to the right of the decimal place */ - decimals: number; - /** Is this mint initialized */ - isInitialized: boolean; - /** Optional authority to freeze token accounts */ - freezeAuthority: PublicKey | null; -} - -/** Mint as stored by the program */ -export interface RawMint { - mintAuthorityOption: 1 | 0; - mintAuthority: PublicKey; - supply: bigint; - decimals: number; - isInitialized: boolean; - freezeAuthorityOption: 1 | 0; - freezeAuthority: PublicKey; -} - -/** Buffer layout for de/serializing a mint */ -export const MintLayout = struct([ - u32('mintAuthorityOption'), - publicKey('mintAuthority'), - u64('supply'), - u8('decimals'), - bool('isInitialized'), - u32('freezeAuthorityOption'), - publicKey('freezeAuthority'), -]); \ No newline at end of file diff --git a/utils/index.ts b/utils/index.ts deleted file mode 100644 index d90cdf8..0000000 --- a/utils/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './utils'; -export * from './logger'; \ No newline at end of file diff --git a/utils/utils.ts b/utils/utils.ts deleted file mode 100644 index 09128d2..0000000 --- a/utils/utils.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Logger } from 'pino'; -import dotenv from 'dotenv'; - -dotenv.config(); - -export const retrieveEnvVariable = (variableName: string, logger: Logger) => { - const variable = process.env[variableName] || ''; - if (!variable) { - logger.error(`${variableName} is not set`); - process.exit(1); - } - return variable; -};