diff --git a/package.json b/package.json index 09e1dd0..2c05fc8 100644 --- a/package.json +++ b/package.json @@ -22,17 +22,17 @@ "pino-pretty": "^11.2.2" }, "devDependencies": { - "@aws-sdk/client-s3": "^3.624.0", + "@aws-sdk/client-s3": "^3.629.0", "@commitlint/cli": "^19.4.0", "@commitlint/config-conventional": "^19.2.2", "@flashbots/ethers-provider-bundle": "^1.0.0", "@gearbox-protocol/eslint-config": "2.0.0-next.2", - "@gearbox-protocol/liquidator-v2-contracts": "^2.1.0-next.17", + "@gearbox-protocol/liquidator-v2-contracts": "^2.1.0", "@gearbox-protocol/prettier-config": "2.0.0-next.0", - "@gearbox-protocol/sdk-gov": "^2.14.1", + "@gearbox-protocol/sdk-gov": "^2.15.0", "@gearbox-protocol/types": "^1.12.1", "@redstone-finance/evm-connector": "^0.6.1", - "@types/node": "^22.1.0", + "@types/node": "^22.2.0", "@uniswap/sdk-core": "^5.3.1", "@uniswap/v3-sdk": "^3.13.1", "@vlad-yakovlev/telegram-md": "^2.0.0", @@ -46,15 +46,15 @@ "eslint": "^8.57.0", "ethers": "^6.13.2", "husky": "^9.1.4", - "lint-staged": "^15.2.8", + "lint-staged": "^15.2.9", "nanoid": "^5.0.7", "node-pty": "^1.0.0", "pino": "^9.3.2", "prettier": "^3.3.3", "redstone-protocol": "^1.0.5", - "tsx": "^4.16.5", + "tsx": "^4.17.0", "typescript": "^5.5.4", - "viem": "^2.19.1", + "viem": "^2.19.4", "vitest": "^2.0.5" }, "prettier": "@gearbox-protocol/prettier-config", diff --git a/src/app.ts b/src/app.ts index fd0d26e..09433bb 100644 --- a/src/app.ts +++ b/src/app.ts @@ -7,7 +7,7 @@ import type Client from "./services/Client.js"; import type HealthCheckerService from "./services/HealthCheckerService.js"; import type { IOptimisticOutputWriter } from "./services/output/index.js"; import type { RedstoneServiceV3 } from "./services/RedstoneServiceV3.js"; -import type { ScanServiceV3 } from "./services/scan/index.js"; +import type { Scanner } from "./services/scanner/index.js"; import type { ISwapper } from "./services/swap/index.js"; import version from "./version.js"; @@ -22,7 +22,7 @@ class App { addressProvider!: AddressProviderService; @DI.Inject(DI.Scanner) - scanServiceV3!: ScanServiceV3; + scanner!: Scanner; @DI.Inject(DI.HealthChecker) healthChecker!: HealthCheckerService; @@ -56,7 +56,7 @@ class App { this.healthChecker.launch(); await this.swapper.launch(this.config.network); - await this.scanServiceV3.launch(); + await this.scanner.launch(); if (this.config.optimistic) { this.log.debug("optimistic liquidation finished, writing output"); diff --git a/src/config/env.ts b/src/config/env.ts index 5219ccf..1c84e14 100644 --- a/src/config/env.ts +++ b/src/config/env.ts @@ -3,10 +3,13 @@ import type { ConfigSchema } from "./schema.js"; const envConfigMapping: Record = { addressProviderOverride: "ADDRESS_PROVIDER", appName: "APP_NAME", + batchLiquidatorAddress: "BATCH_LIQUIDATOR_ADDRESS", debugAccounts: "DEBUG_ACCOUNTS", debugManagers: "DEBUG_MANAGERS", + batchSize: "BATCH_SIZE", castBin: "CAST_BIN", deployPartialLiquidatorContracts: "DEPLOY_PARTIAL_LIQUIDATOR", + deployBatchLiquidatorContracts: "DEPLOY_BATCH_LIQUIDATOR", ethProviderRpcs: ["JSON_RPC_PROVIDERS", "JSON_RPC_PROVIDER"], hfThreshold: "HF_TRESHOLD", restakingWorkaround: "RESTAKING_WORKAROUND", diff --git a/src/config/schema.ts b/src/config/schema.ts index fed99af..dbc6d77 100644 --- a/src/config/schema.ts +++ b/src/config/schema.ts @@ -50,6 +50,9 @@ export const ConfigSchema = z.object({ optimistic: booleanLike.pipe(z.boolean().optional()), deployPartialLiquidatorContracts: booleanLike.pipe(z.boolean().optional()), partialLiquidatorAddress: Address.optional(), + deployBatchLiquidatorContracts: booleanLike.pipe(z.boolean().optional()), + batchSize: z.coerce.number().nonnegative().default(10), + batchLiquidatorAddress: Address.optional(), slippage: z.coerce.number().min(0).max(10000).int().default(50), restakingWorkaround: booleanLike.pipe(z.boolean().optional()), diff --git a/src/errors/ErrorHandler.ts b/src/errors/ErrorHandler.ts index 5b5ab3c..37c5535 100644 --- a/src/errors/ErrorHandler.ts +++ b/src/errors/ErrorHandler.ts @@ -66,6 +66,15 @@ export class ErrorHandler { }; } + public async saveTransactionTrace(hash: string): Promise { + return this.#runCast([ + "run", + "--rpc-url", + this.config.ethProviderRpcs[0], + hash, + ]); + } + /** * Safely tries to save trace of failed transaction to configured output * @param error @@ -75,52 +84,65 @@ export class ErrorHandler { e: BaseError, context?: CreditAccountData, ): Promise { - const logger = this.#caLogger(context); - if (!this.config.castBin || !this.config.outDir) { - return undefined; - } - try { - let cast: string[] = []; - if (e instanceof TransactionRevertedError) { + let cast: string[] = []; + if (e instanceof TransactionRevertedError) { + cast = [ + "run", + "--rpc-url", + this.config.ethProviderRpcs[0], + e.receipt.transactionHash, + ]; + } else { + const exErr = e.walk( + err => err instanceof ContractFunctionExecutionError, + ); + if ( + exErr instanceof ContractFunctionExecutionError && + exErr.contractAddress + ) { + const data = encodeFunctionData({ + abi: exErr.abi, + args: exErr.args, + functionName: exErr.functionName, + }); cast = [ - "run", + "call", + "--trace", "--rpc-url", this.config.ethProviderRpcs[0], - e.receipt.transactionHash, + exErr.contractAddress, + data, ]; - } else { - const exErr = e.walk( - err => err instanceof ContractFunctionExecutionError, - ); - if ( - exErr instanceof ContractFunctionExecutionError && - exErr.contractAddress - ) { - const data = encodeFunctionData({ - abi: exErr.abi, - args: exErr.args, - functionName: exErr.functionName, - }); - cast = [ - "call", - "--trace", - "--rpc-url", - this.config.ethProviderRpcs[0], - exErr.contractAddress, - data, - ]; - } - } - if (!cast.length) { - return undefined; } + } + if (!cast.length) { + return undefined; + } + return this.#runCast(cast, context); + } + /** + * Runs cast cli command and saves output to a unique file + * @param args + * @param context + * @returns + */ + async #runCast( + args: string[], + context?: CreditAccountData, + ): Promise { + if (!this.config.castBin || !this.config.outDir) { + return undefined; + } + + const logger = this.#caLogger(context); + try { const traceId = `${nanoid()}.trace`; const traceFile = path.resolve(this.config.outDir, traceId); const out = createWriteStream(traceFile, "utf-8"); await events.once(out, "open"); // use node-pty instead of node:child_process to have colored output - const pty = spawn(this.config.castBin, cast, { cols: 1024 }); + const pty = spawn(this.config.castBin, args, { cols: 1024 }); pty.onData(data => out.write(data)); await new Promise(resolve => { pty.onExit(() => resolve(undefined)); diff --git a/src/index.ts b/src/index.ts index b587989..c4c22f8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,7 +4,7 @@ import "./services/Client.js"; import "./services/HealthCheckerService.js"; import "./services/OracleServiceV3.js"; import "./services/RedstoneServiceV3.js"; -import "./services/scan/index.js"; +import "./services/scanner/index.js"; import "./services/liquidate/index.js"; import "./services/output/index.js"; import "./services/notifier/index.js"; diff --git a/src/services/Client.ts b/src/services/Client.ts index 2099f93..cd74e09 100644 --- a/src/services/Client.ts +++ b/src/services/Client.ts @@ -36,7 +36,6 @@ import { privateKeyToAccount } from "viem/accounts"; import { arbitrum, base, mainnet, optimism } from "viem/chains"; import type { Config } from "../config/index.js"; -import type { CreditAccountData } from "../data/index.js"; import { exceptionsAbis } from "../data/index.js"; import { DI } from "../di.js"; import { TransactionRevertedError } from "../errors/TransactionRevertedError.js"; @@ -167,14 +166,9 @@ export default class Client { } public async liquidate( - ca: CreditAccountData, request: SimulateContractReturnType["request"], + logger: ILogger, ): Promise { - const logger = this.logger.child({ - account: ca.addr, - borrower: ca.borrower, - manager: ca.managerName, - }); logger.debug("sending liquidation tx"); const { abi, address, args, dataSuffix, functionName, ...rest } = request; const data = encodeFunctionData({ diff --git a/src/services/HealthCheckerService.ts b/src/services/HealthCheckerService.ts index d0f622b..774de06 100644 --- a/src/services/HealthCheckerService.ts +++ b/src/services/HealthCheckerService.ts @@ -7,7 +7,7 @@ import { DI } from "../di.js"; import type { ILogger } from "../log/index.js"; import { Logger } from "../log/index.js"; import version from "../version.js"; -import type { ScanServiceV3 } from "./scan/index.js"; +import type { Scanner } from "./scanner/index.js"; const nanoid = customAlphabet("1234567890abcdef", 8); @@ -17,7 +17,7 @@ export default class HealthCheckerService { log!: ILogger; @DI.Inject(DI.Scanner) - scanServiceV3!: ScanServiceV3; + scanner!: Scanner; @DI.Inject(DI.Config) config!: Config; @@ -40,7 +40,7 @@ export default class HealthCheckerService { res.end( JSON.stringify({ start_time: this.#start, - block_number: this.scanServiceV3.lastUpdated, + block_number: this.scanner.lastUpdated, version, }), ); @@ -97,7 +97,7 @@ start_time{${labels}} ${this.#start} # HELP block_number Latest processed block # TYPE block_number gauge -block_number{${labels}} ${this.scanServiceV3.lastUpdated} +block_number{${labels}} ${this.scanner.lastUpdated} `; } diff --git a/src/services/RedstoneServiceV3.ts b/src/services/RedstoneServiceV3.ts index e442038..53b064b 100644 --- a/src/services/RedstoneServiceV3.ts +++ b/src/services/RedstoneServiceV3.ts @@ -34,6 +34,14 @@ import type { PriceOnDemandExtras, PriceUpdate } from "./liquidate/index.js"; import type { RedstoneFeed } from "./OracleServiceV3.js"; import type OracleServiceV3 from "./OracleServiceV3.js"; +interface RedstoneUpdate extends RedstoneFeed { + /** + * In case when Redstone feed is using ticker to updates, this will be the original token + * Otherwise they are the same + */ + originalToken: Address; +} + export type RedstonePriceFeed = Extract< PriceFeedData, { type: PriceFeedType.REDSTONE_ORACLE } @@ -113,12 +121,17 @@ export class RedstoneServiceV3 { const redstoneFeeds = this.oracle.getRedstoneFeeds(activeOnly); const tickers = tickerInfoTokensByNetwork[this.config.network]; - const redstoneUpdates: RedstoneFeed[] = []; + const redstoneUpdates: RedstoneUpdate[] = []; for (const t of tokens) { - const token = t.toLowerCase(); + const token = t.toLowerCase() as Address; const feeds = redstoneFeeds[token]; if (feeds?.length) { - redstoneUpdates.push(...feeds); + redstoneUpdates.push( + ...feeds.map(f => ({ + ...f, + originalToken: token, + })), + ); continue; } const symb = tokenSymbolByAddress[token]; @@ -136,8 +149,9 @@ export class RedstoneServiceV3 { ticker.dataId; redstoneUpdates.push({ - dataFeedId: tickerFeedId, + originalToken: token, token: ticker.address, + dataFeedId: tickerFeedId, reserve: false, // tickers are always added as main feed }); } else { @@ -155,8 +169,9 @@ export class RedstoneServiceV3 { `need to update ${redstoneUpdates.length} redstone feeds: ${printFeeds(redstoneUpdates)}`, ); const result = await Promise.all( - redstoneUpdates.map(({ token, dataFeedId, reserve }) => + redstoneUpdates.map(({ originalToken, token, dataFeedId, reserve }) => this.#getRedstonePayloadForManualUsage( + originalToken, token, reserve, "redstone-primary-prod", @@ -200,16 +215,20 @@ export class RedstoneServiceV3 { return result; } - public async multicallUpdates(ca: CreditAccountData): Promise { - const priceUpdates = await this.liquidationPreviewUpdates(ca, true); - return priceUpdates.map(({ token, data, reserve }) => ({ - target: ca.creditFacade, - callData: encodeFunctionData({ - abi: iCreditFacadeV3MulticallAbi, - functionName: "onDemandPriceUpdate", - args: [token, reserve, data], - }), - })); + public toMulticallUpdates( + ca: CreditAccountData, + priceUpdates?: PriceUpdate[], + ): MultiCall[] { + return ( + priceUpdates?.map(({ token, data, reserve }) => ({ + target: ca.creditFacade, + callData: encodeFunctionData({ + abi: iCreditFacadeV3MulticallAbi, + functionName: "onDemandPriceUpdate", + args: [token, reserve, data], + }), + })) ?? [] + ); } public async dataCompressorUpdates( @@ -244,8 +263,58 @@ export class RedstoneServiceV3 { })); } + /** + * Gets updates from redstone for multiple accounts at once + * Reduces duplication, so that we don't make redstone request twice if two accounts share a token + * + * @param accounts + * @param activeOnly + * @returns + */ + public async batchLiquidationPreviewUpdates( + accounts: CreditAccountData[], + activeOnly = false, + ): Promise> { + const tokensByAccount: Record> = {}; + const allTokens = new Set
(); + for (const ca of accounts) { + const accTokens = tokensByAccount[ca.addr] ?? new Set
(); + for (const { token, balance, isEnabled } of ca.allBalances) { + if (isEnabled && balance > 10n) { + accTokens.add(token); + allTokens.add(token); + } + } + tokensByAccount[ca.addr] = accTokens; + } + + const priceUpdates = await this.updatesForTokens( + Array.from(allTokens), + activeOnly, + ); + + const result: Record = {}; + for (const [accAddr, accTokens] of Object.entries(tokensByAccount)) { + const accUpdates: PriceUpdate[] = []; + // There can be 2 price feeds (main and reserve) per originalToken + for (const u of priceUpdates) { + if (accTokens.has(u.originalToken)) { + accUpdates.push({ + token: u.token, + reserve: u.reserve, + data: u.callData, + }); + } + } + result[accAddr as Address] = accUpdates; + } + + return result; + } + async #getRedstonePayloadForManualUsage( - token: Address, + originalToken: Address, + tokenOrTicker: Address, reserve: boolean, dataServiceId: string, dataFeedId: string, @@ -255,7 +324,7 @@ export class RedstoneServiceV3 { const logger = this.logger.child(logContext); const cacheAllowed = this.config.optimistic; const key = redstoneCacheKey( - token, + tokenOrTicker, reserve, dataServiceId, dataFeedId, @@ -311,8 +380,9 @@ export class RedstoneServiceV3 { ] as const; }); - const response = { - token, + const response: PriceOnDemandExtras = { + originalToken, + token: tokenOrTicker, reserve, callData: result[0][0], ts: result[0][1], diff --git a/src/services/liquidate/AbstractLiquidationStrategyV3.ts b/src/services/liquidate/AbstractLiquidationStrategyV3.ts deleted file mode 100644 index 47ad426..0000000 --- a/src/services/liquidate/AbstractLiquidationStrategyV3.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { iDataCompressorV3Abi } from "@gearbox-protocol/types/abi"; -import type { Address } from "viem"; -import { getContract } from "viem"; - -import type { Config } from "../../config/index.js"; -import { CreditAccountData, CreditManagerData } from "../../data/index.js"; -import { DI } from "../../di.js"; -import type { ILogger } from "../../log/index.js"; -import { PathFinder } from "../../utils/ethers-6-temp/pathfinder/index.js"; -import { TxParserHelper } from "../../utils/ethers-6-temp/txparser/index.js"; -import type { IDataCompressorContract } from "../../utils/index.js"; -import type { AddressProviderService } from "../AddressProviderService.js"; -import type Client from "../Client.js"; -import type OracleServiceV3 from "../OracleServiceV3.js"; -import type { RedstoneServiceV3 } from "../RedstoneServiceV3.js"; - -export default abstract class AbstractLiquidationStrategyV3 { - abstract logger: ILogger; - - @DI.Inject(DI.AddressProvider) - addressProvider!: AddressProviderService; - - @DI.Inject(DI.Config) - config!: Config; - - @DI.Inject(DI.Redstone) - redstone!: RedstoneServiceV3; - - @DI.Inject(DI.Oracle) - oracle!: OracleServiceV3; - - @DI.Inject(DI.Client) - client!: Client; - - #compressor?: IDataCompressorContract; - #pathFinder?: PathFinder; - #cmCache: Record = {}; - - public async launch(): Promise { - const [pfAddr, dcAddr] = await Promise.all([ - this.addressProvider.findService("ROUTER", 300), - this.addressProvider.findService("DATA_COMPRESSOR", 300), - ]); - this.#compressor = getContract({ - abi: iDataCompressorV3Abi, - address: dcAddr, - client: this.client.pub, - }); - this.#pathFinder = new PathFinder( - pfAddr, - this.client.pub, - this.config.network, - ); - } - - public async updateCreditAccountData( - ca: CreditAccountData, - ): Promise { - if (!this.config.optimistic) { - throw new Error( - "updateCreditAccountData should only be used in optimistic mode", - ); - } - const priceUpdates = await this.redstone.dataCompressorUpdates(ca); - const { result } = await this.compressor.simulate.getCreditAccountData([ - ca.addr, - priceUpdates, - ]); - return new CreditAccountData(result); - } - - protected async getCreditManagerData( - addr: Address, - ): Promise { - let cm: CreditManagerData | undefined; - if (this.config.optimistic) { - cm = this.#cmCache[addr.toLowerCase()]; - } - if (!cm) { - cm = new CreditManagerData( - await this.compressor.read.getCreditManagerData([addr]), - ); - if (this.config.optimistic) { - this.#cmCache[addr.toLowerCase()] = cm; - } - } - // TODO: TxParser is really old and weird class, until we refactor it it's the best place to have this - TxParserHelper.addCreditManager(cm); - return cm; - } - - protected async getCreditManagersV3List(): Promise { - const raw = await this.compressor.read.getCreditManagersV3List(); - const result = raw.map(d => new CreditManagerData(d)); - - if (this.config.optimistic) { - for (const cm of result) { - this.#cmCache[cm.address.toLowerCase()] = cm; - } - } - - return result; - } - - protected get compressor(): IDataCompressorContract { - if (!this.#compressor) { - throw new Error("strategy not launched"); - } - return this.#compressor; - } - - protected get pathFinder(): PathFinder { - if (!this.#pathFinder) { - throw new Error("strategy not launched"); - } - return this.#pathFinder; - } -} diff --git a/src/services/liquidate/AbstractLiquidator.ts b/src/services/liquidate/AbstractLiquidator.ts new file mode 100644 index 0000000..b8845ec --- /dev/null +++ b/src/services/liquidate/AbstractLiquidator.ts @@ -0,0 +1,239 @@ +import { tokenSymbolByAddress } from "@gearbox-protocol/sdk-gov"; +import { iDataCompressorV3Abi, ierc20Abi } from "@gearbox-protocol/types/abi"; +import type { OptimisticResult } from "@gearbox-protocol/types/optimist"; +import type { Address, TransactionReceipt } from "viem"; +import { getContract } from "viem"; + +import type { Config } from "../../config/index.js"; +import { CreditAccountData, CreditManagerData } from "../../data/index.js"; +import { DI } from "../../di.js"; +import { ErrorHandler } from "../../errors/index.js"; +import type { ILogger } from "../../log/index.js"; +import { Logger } from "../../log/index.js"; +import { PathFinder } from "../../utils/ethers-6-temp/pathfinder/index.js"; +import { TxParserHelper } from "../../utils/ethers-6-temp/txparser/index.js"; +import type { IDataCompressorContract } from "../../utils/index.js"; +import type { AddressProviderService } from "../AddressProviderService.js"; +import type Client from "../Client.js"; +import { type INotifier, StartedMessage } from "../notifier/index.js"; +import type OracleServiceV3 from "../OracleServiceV3.js"; +import type { IOptimisticOutputWriter } from "../output/index.js"; +import type { RedstoneServiceV3 } from "../RedstoneServiceV3.js"; +import type { ISwapper } from "../swap/index.js"; +import type { OptimisticResults } from "./OptimisiticResults.js"; +import type { StrategyPreview } from "./types.js"; + +export default abstract class AbstractLiquidator { + @Logger("Liquidator") + logger!: ILogger; + + @DI.Inject(DI.Redstone) + redstone!: RedstoneServiceV3; + + @DI.Inject(DI.Notifier) + notifier!: INotifier; + + @DI.Inject(DI.Config) + config!: Config; + + @DI.Inject(DI.AddressProvider) + addressProvider!: AddressProviderService; + + @DI.Inject(DI.Oracle) + oracle!: OracleServiceV3; + + @DI.Inject(DI.Output) + outputWriter!: IOptimisticOutputWriter; + + @DI.Inject(DI.Swapper) + swapper!: ISwapper; + + @DI.Inject(DI.OptimisticResults) + optimistic!: OptimisticResults; + + @DI.Inject(DI.Client) + client!: Client; + + skipList = new Set
(); + + #errorHandler?: ErrorHandler; + #compressor?: IDataCompressorContract; + #pathFinder?: PathFinder; + #router?: Address; + #cmCache: Record = {}; + + public async launch(): Promise { + this.#errorHandler = new ErrorHandler(this.config, this.logger); + const [pfAddr, dcAddr] = await Promise.all([ + this.addressProvider.findService("ROUTER", 300), + this.addressProvider.findService("DATA_COMPRESSOR", 300), + ]); + this.#router = pfAddr; + this.#compressor = getContract({ + abi: iDataCompressorV3Abi, + address: dcAddr, + client: this.client.pub, + }); + this.#pathFinder = new PathFinder( + pfAddr, + this.client.pub, + this.config.network, + ); + this.notifier.notify(new StartedMessage()); + } + + protected newOptimisticResult(acc: CreditAccountData): OptimisticResult { + return { + creditManager: acc.creditManager, + borrower: acc.borrower, + account: acc.addr, + balancesBefore: acc.filterDust(), + hfBefore: acc.healthFactor, + balancesAfter: {}, + hfAfter: 0, + gasUsed: 0, + calls: [], + callsHuman: [], + isError: true, + pathAmount: "0", + liquidatorPremium: "0", + liquidatorProfit: "0", + }; + } + + protected updateAfterPreview( + result: OptimisticResult, + preview: StrategyPreview, + ): OptimisticResult { + return { + ...result, + assetOut: preview.assetOut, + amountOut: preview.amountOut, + flashLoanAmount: preview.flashLoanAmount, + calls: preview.calls, + pathAmount: preview.underlyingBalance.toString(), + priceUpdates: preview.priceUpdates, + callsHuman: TxParserHelper.parseMultiCall(preview), + }; + } + + protected async updateAfterLiquidation( + result: OptimisticResult, + acc: CreditAccountData, + underlyingBalanceBefore: bigint, + receipt: TransactionReceipt, + ): Promise { + const ca = await this.updateCreditAccountData(acc); + result.balancesAfter = ca.filterDust(); + result.hfAfter = ca.healthFactor; + + const balanceAfter = await this.getExecutorBalance(ca.underlyingToken); + result.gasUsed = Number(receipt.gasUsed); + result.liquidatorPremium = ( + balanceAfter.underlying - underlyingBalanceBefore + ).toString(10); + return result; + } + + protected async getCreditManagerData( + addr: Address, + ): Promise { + let cm: CreditManagerData | undefined; + if (this.config.optimistic) { + cm = this.#cmCache[addr.toLowerCase()]; + } + if (!cm) { + cm = new CreditManagerData( + await this.compressor.read.getCreditManagerData([addr]), + ); + if (this.config.optimistic) { + this.#cmCache[addr.toLowerCase()] = cm; + } + } + // TODO: TxParser is really old and weird class, until we refactor it it's the best place to have this + TxParserHelper.addCreditManager(cm); + return cm; + } + + protected async getCreditManagersV3List(): Promise { + const raw = await this.compressor.read.getCreditManagersV3List(); + const result = raw.map(d => new CreditManagerData(d)); + + for (const cm of result) { + TxParserHelper.addCreditManager(cm); + if (this.config.optimistic) { + this.#cmCache[cm.address.toLowerCase()] = cm; + } + } + + return result; + } + + /** + * Fetches credit account data again for optimistic report + * @param ca + * @returns + */ + protected async updateCreditAccountData( + ca: CreditAccountData, + ): Promise { + if (!this.config.optimistic) { + throw new Error( + "updateCreditAccountData should only be used in optimistic mode", + ); + } + const priceUpdates = await this.redstone.dataCompressorUpdates(ca); + const { result } = await this.compressor.simulate.getCreditAccountData([ + ca.addr, + priceUpdates, + ]); + return new CreditAccountData(result); + } + + protected async getExecutorBalance( + underlyingToken: Address, + ): Promise<{ eth: bigint; underlying: bigint }> { + // using promise.all here sometimes results in anvil being stuck + const isWeth = tokenSymbolByAddress[underlyingToken] === "WETH"; + const eth = await this.client.pub.getBalance({ + address: this.client.address, + }); + const underlying = isWeth + ? eth + : await this.client.pub.readContract({ + address: underlyingToken, + abi: ierc20Abi, + functionName: "balanceOf", + args: [this.client.address], + }); + return { eth, underlying }; + } + + protected get errorHandler(): ErrorHandler { + if (!this.#errorHandler) { + throw new Error("liquidator not launched"); + } + return this.#errorHandler; + } + + protected get compressor(): IDataCompressorContract { + if (!this.#compressor) { + throw new Error("liquidator not launched"); + } + return this.#compressor; + } + + protected get pathFinder(): PathFinder { + if (!this.#pathFinder) { + throw new Error("liquidator not launched"); + } + return this.#pathFinder; + } + + protected get router(): Address { + if (!this.#router) { + throw new Error("liquidator not launched"); + } + return this.#router; + } +} diff --git a/src/services/liquidate/BatchLiquidator.ts b/src/services/liquidate/BatchLiquidator.ts new file mode 100644 index 0000000..95236c1 --- /dev/null +++ b/src/services/liquidate/BatchLiquidator.ts @@ -0,0 +1,328 @@ +import { + batchLiquidatorAbi, + iBatchLiquidatorAbi, +} from "@gearbox-protocol/liquidator-v2-contracts/abi"; +import { BatchLiquidator_bytecode } from "@gearbox-protocol/liquidator-v2-contracts/bytecode"; +import { iCreditFacadeV3Abi } from "@gearbox-protocol/types/abi"; +import type { OptimisticResult } from "@gearbox-protocol/types/optimist"; +import type { Address, TransactionReceipt } from "viem"; +import { parseEventLogs } from "viem"; + +import type { CreditAccountData, CreditManagerData } from "../../data/index.js"; +import type { EstimateBatchInput } from "../../utils/ethers-6-temp/pathfinder/viem-types.js"; +import { TxParserHelper } from "../../utils/ethers-6-temp/txparser/index.js"; +import { + BatchLiquidationErrorMessage, + BatchLiquidationFinishedMessage, +} from "../notifier/messages.js"; +import AbstractLiquidator from "./AbstractLiquidator.js"; +import type { ILiquidatorService } from "./types.js"; +import type { + BatchLiquidationResult, + LiquidateBatchInput, +} from "./viem-types.js"; + +interface BatchLiquidationOutput { + readonly receipt: TransactionReceipt; + readonly results: OptimisticResult[]; +} + +export default class BatchLiquidator + extends AbstractLiquidator + implements ILiquidatorService +{ + #batchLiquidator?: Address; + + public override async launch(): Promise { + await super.launch(); + await this.#deployContract(); + } + + public async liquidate(accounts: CreditAccountData[]): Promise { + if (!accounts.length) { + return; + } + this.logger.warn(`need to liquidate ${accounts.length} accounts`); + const cms = await this.getCreditManagersV3List(); + const batches = this.#sliceBatches(accounts); + + for (let i = 0; i < batches.length; i++) { + const batch = batches[i]; + this.logger.debug( + `processing batch of ${batch.length} for ${batch[0]?.cmName}: ${batch.map(ca => ca.addr)}`, + ); + try { + const { receipt, results } = await this.#liquidateBatch( + batch, + cms, + i, + batches.length, + ); + this.notifier.notify( + new BatchLiquidationFinishedMessage(receipt, results), + ); + } catch (e) { + const decoded = await this.errorHandler.explain(e); + this.logger.error(decoded, "cant liquidate"); + this.notifier.notify( + new BatchLiquidationErrorMessage(batch, decoded.shortMessage), + ); + } + } + } + + public async liquidateOptimistic( + accounts: CreditAccountData[], + ): Promise { + const cms = await this.getCreditManagersV3List(); + const total = accounts.length; + this.logger.info(`optimistic batch-liquidation for ${total} accounts`); + const batches = this.#sliceBatches(accounts); + for (let i = 0; i < batches.length; i++) { + const batch = batches[i]; + this.logger.debug( + `processing batch of ${batch.length} for ${batch[0]?.cmName}: ${batch.map(ca => ca.addr)}`, + ); + const snapshotId = await this.client.anvil.snapshot(); + const { results, receipt } = await this.#liquidateBatch( + batch, + cms, + i, + batches.length, + ); + const hasErrors = results.some(r => !!r.isError); + let traceId: string | undefined; + if (hasErrors && !!receipt) { + traceId = await this.errorHandler.saveTransactionTrace( + receipt.transactionHash, + ); + } + for (const r of results) { + this.optimistic.push({ ...r, traceFile: traceId }); + } + await this.client.anvil.revert({ id: snapshotId }); + } + const success = this.optimistic.get().filter(r => !r.isError).length; + this.logger.info( + `optimistic batch-liquidation finished: ${success}/${total} accounts liquidated`, + ); + } + + async #liquidateBatch( + accounts: CreditAccountData[], + cms: CreditManagerData[], + index: number, + total: number, + ): Promise { + const priceUpdatesByAccount = + await this.redstone.batchLiquidationPreviewUpdates(accounts); + const inputs: EstimateBatchInput[] = []; + for (const ca of accounts) { + const cm = cms.find(m => ca.creditManager === m.address); + if (!cm) { + throw new Error( + `cannot find credit manager data for ${ca.creditManager}`, + ); + } + // pathfinder returns input without price updates + const input = this.pathFinder.getEstimateBatchInput( + ca, + cm, + this.config.slippage, + ); + input.priceUpdates = priceUpdatesByAccount[ca.addr]; + inputs.push(input); + } + const { result } = await this.client.pub.simulateContract({ + account: this.client.account, + address: this.batchLiquidator, + abi: iBatchLiquidatorAbi, + functionName: "estimateBatch", + args: [inputs], + }); + // BatchLiquidator contract does not return onDemandPriceUpdate calls, need to prepend them manually: + for (let i = 0; i < accounts.length; i++) { + const account = accounts[i]; + result[i].calls = [ + ...this.redstone.toMulticallUpdates( + account, + priceUpdatesByAccount[account.addr], + ), + ...result[i].calls, + ]; + } + + const batch: Record = Object.fromEntries( + result.map(r => [r.creditAccount.toLowerCase(), r]), + ); + const liquidateBatchInput: LiquidateBatchInput[] = []; + for (const r of result) { + const input = inputs.find( + i => i.creditAccount === r.creditAccount.toLowerCase(), + ); + this.logger.debug( + { + account: r.creditAccount, + executed: r.executed, + pathFound: r.pathFound, + calls: r.calls, + input, + }, + `estimation for account ${r.creditAccount}`, + ); + if (r.executed) { + const acc = accounts.find( + a => a.addr === r.creditAccount.toLowerCase(), + ); + if (acc) { + liquidateBatchInput.push({ + calls: r.calls, + creditAccount: r.creditAccount, + creditFacade: acc.creditFacade, + }); + } + } + } + this.logger.debug( + { + accounts: accounts.length, + outputSize: result.length, + executed: liquidateBatchInput.length, + }, + "estimated batch", + ); + + const { request } = await this.client.pub.simulateContract({ + account: this.client.account, + address: this.batchLiquidator, + abi: iBatchLiquidatorAbi, + functionName: "liquidateBatch", + args: [liquidateBatchInput, this.client.address], + }); + const receipt = await this.client.liquidate(request as any, this.logger); // TODO: types + this.logger.debug( + { tx: receipt.transactionHash, gasUsed: receipt.gasUsed }, + "liquidated batch", + ); + + const logs = parseEventLogs({ + abi: iCreditFacadeV3Abi, + eventName: "LiquidateCreditAccount", + logs: receipt.logs, + }); + const liquidated = new Set( + logs.map(l => l.args.creditAccount.toLowerCase() as Address), + ); + this.logger.debug(`emitted ${liquidated.size} liquidation events`); + const getError = (a: CreditAccountData): string | undefined => { + if (liquidated.has(a.addr)) { + return undefined; + } + const item = batch[a.addr]; + if (!item) { + return "not found in estimateBatch output"; + } + if (!item.pathFound) { + return "batch path not found"; + } + if (!item.executed) { + return "cannot execute in estimateBatch"; + } + return "cannot liquidate in batch"; + }; + const results = accounts.map( + (a): OptimisticResult => ({ + callsHuman: TxParserHelper.parseMultiCall({ + calls: [...(batch[a.addr]?.calls ?? [])], + }), + balancesBefore: a.filterDust(), + balancesAfter: {}, + hfBefore: a.healthFactor, + hfAfter: 0, + creditManager: a.creditManager, + borrower: a.borrower, + account: a.addr, + gasUsed: 0, // cannot know for single account + calls: [...(batch[a.addr]?.calls ?? [])], + pathAmount: "0", // TODO: ?? + liquidatorPremium: (batch[a.addr]?.profit ?? 0n).toString(10), + liquidatorProfit: "0", // cannot compute for single account + priceUpdates: priceUpdatesByAccount[a.addr], + isError: !liquidated.has(a.addr), + error: getError(a), + batchId: `${index + 1}/${total}`, + }), + ); + return { + receipt, + results, + }; + } + + async #deployContract(): Promise { + this.#batchLiquidator = this.config.batchLiquidatorAddress; + if (!this.#batchLiquidator) { + this.logger.debug("deploying batch liquidator"); + + let hash = await this.client.wallet.deployContract({ + abi: batchLiquidatorAbi, + bytecode: BatchLiquidator_bytecode, + args: [this.router], + }); + this.logger.debug( + `waiting for BatchLiquidator to deploy, tx hash: ${hash}`, + ); + const { contractAddress } = + await this.client.pub.waitForTransactionReceipt({ + hash, + timeout: 120_000, + }); + if (!contractAddress) { + throw new Error(`BatchLiquidator was not deployed, tx hash: ${hash}`); + } + this.#batchLiquidator = contractAddress; + } + this.logger.debug( + `using batch liquidator contract ${this.#batchLiquidator}`, + ); + } + + private get batchLiquidator(): Address { + if (!this.#batchLiquidator) { + throw new Error("batch liquidator not deployed"); + } + return this.#batchLiquidator; + } + + #sliceBatches(accounts: CreditAccountData[]): CreditAccountData[][] { + // sort by healthFactor bin ASC, debt DESC + const sortedAccounts = accounts.sort((a, b) => { + if (a.healthFactor !== b.healthFactor) { + return healthFactorBin(a) - healthFactorBin(b); + } + if (b.totalDebtUSD > a.totalDebtUSD) { + return 1; + } else if (b.totalDebtUSD === a.totalDebtUSD) { + return 0; + } else { + return -1; + } + }); + + const batches: CreditAccountData[][] = []; + for (let i = 0; i < sortedAccounts.length; i += this.config.batchSize) { + batches.push(sortedAccounts.slice(i, i + this.config.batchSize)); + } + return batches; + } +} + +function healthFactorBin({ healthFactor }: CreditAccountData): number { + if (healthFactor < 9300) { + return 0; + } else if (healthFactor < 9600) { + return 1; + } else { + return 2; + } +} diff --git a/src/services/liquidate/LiquidationStrategyV3Full.ts b/src/services/liquidate/LiquidationStrategyV3Full.ts deleted file mode 100644 index ec6232f..0000000 --- a/src/services/liquidate/LiquidationStrategyV3Full.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { getDecimals } from "@gearbox-protocol/sdk-gov"; -import { iCreditFacadeV3Abi } from "@gearbox-protocol/types/abi"; -import type { SimulateContractReturnType } from "viem"; - -import { - type Balance, - type CreditAccountData, - exceptionsAbis, -} from "../../data/index.js"; -import { type ILogger, Logger } from "../../log/index.js"; -import type { PathFinderCloseResult } from "../../utils/ethers-6-temp/pathfinder/index.js"; -import AbstractLiquidationStrategyV3 from "./AbstractLiquidationStrategyV3.js"; -import type { ILiquidationStrategy, MakeLiquidatableResult } from "./types.js"; - -export default class LiquidationStrategyV3Full - extends AbstractLiquidationStrategyV3 - implements ILiquidationStrategy -{ - public readonly name = "full"; - public readonly adverb = "fully"; - - @Logger("LiquidationStrategyV3Full") - logger!: ILogger; - - public async makeLiquidatable( - ca: CreditAccountData, - ): Promise { - // not supported - return Promise.resolve({}); - } - - public async preview(ca: CreditAccountData): Promise { - try { - const cm = await this.getCreditManagerData(ca.creditManager); - const expectedBalances: Record = {}; - const leftoverBalances: Record = {}; - for (const { token, balance, isEnabled } of ca.allBalances) { - expectedBalances[token] = { token, balance }; - // filter out dust, we don't want to swap it - const minBalance = 10n ** BigInt(Math.max(8, getDecimals(token)) - 8); - // also: gearbox liquidator does not need to swap disabled tokens. third-party liquidators might want to do it - if (balance < minBalance || !isEnabled) { - leftoverBalances[token] = { token, balance }; - } - } - const result = await this.pathFinder.findBestClosePath({ - creditAccount: ca, - creditManager: cm, - expectedBalances, - leftoverBalances, - slippage: this.config.slippage, - }); - if (!result) { - throw new Error("pathfinder result is empty"); - } - // we want fresh redstone price in actual liquidation transactions - const priceUpdateCalls = await this.redstone.multicallUpdates(ca); - return { - amount: result.amount, - minAmount: result.minAmount, - underlyingBalance: result.underlyingBalance, - calls: [...priceUpdateCalls, ...result.calls], - }; - } catch (e) { - throw new Error(`cant find close path: ${e}`); - } - } - - public async simulate( - account: CreditAccountData, - preview: PathFinderCloseResult, - ): Promise { - return this.client.pub.simulateContract({ - account: this.client.account, - abi: [...iCreditFacadeV3Abi, ...exceptionsAbis], - address: account.creditFacade, - functionName: "liquidateCreditAccount", - args: [account.addr, this.client.address, preview.calls], - }); - } -} diff --git a/src/services/liquidate/LiquidatorService.ts b/src/services/liquidate/LiquidatorService.ts deleted file mode 100644 index b50cb43..0000000 --- a/src/services/liquidate/LiquidatorService.ts +++ /dev/null @@ -1,248 +0,0 @@ -import { tokenSymbolByAddress } from "@gearbox-protocol/sdk-gov"; -import { ierc20Abi } from "@gearbox-protocol/types/abi"; -import type { OptimisticResult } from "@gearbox-protocol/types/optimist"; -import type { Address, Hex } from "viem"; - -import type { Config } from "../../config/index.js"; -import type { CreditAccountData } from "../../data/index.js"; -import { DI } from "../../di.js"; -import { ErrorHandler } from "../../errors/index.js"; -import { type ILogger, Logger } from "../../log/index.js"; -import { TxParserHelper } from "../../utils/ethers-6-temp/txparser/index.js"; -import type { AddressProviderService } from "../AddressProviderService.js"; -import type Client from "../Client.js"; -import type { INotifier } from "../notifier/index.js"; -import { - LiquidationErrorMessage, - LiquidationStartMessage, - LiquidationSuccessMessage, - StartedMessage, -} from "../notifier/index.js"; -import type { IOptimisticOutputWriter } from "../output/index.js"; -import type { RedstoneServiceV3 } from "../RedstoneServiceV3.js"; -import type { ISwapper } from "../swap/index.js"; -import LiquidationStrategyV3Full from "./LiquidationStrategyV3Full.js"; -import LiquidationStrategyV3Partial from "./LiquidationStrategyV3Partial.js"; -import type { OptimisticResults } from "./OptimisiticResults.js"; -import type { - ILiquidationStrategy, - ILiquidatorService, - StrategyPreview, -} from "./types.js"; - -export interface Balance { - underlying: bigint; - eth: bigint; -} - -@DI.Injectable(DI.Liquidator) -export class LiquidatorService implements ILiquidatorService { - @Logger("Liquidator") - log!: ILogger; - - @DI.Inject(DI.Redstone) - redstone!: RedstoneServiceV3; - - @DI.Inject(DI.Notifier) - notifier!: INotifier; - - @DI.Inject(DI.Config) - config!: Config; - - @DI.Inject(DI.AddressProvider) - addressProvider!: AddressProviderService; - - @DI.Inject(DI.Output) - outputWriter!: IOptimisticOutputWriter; - - @DI.Inject(DI.Swapper) - swapper!: ISwapper; - - @DI.Inject(DI.OptimisticResults) - optimistic!: OptimisticResults; - - @DI.Inject(DI.Client) - client!: Client; - - #errorHandler!: ErrorHandler; - #skipList = new Set
(); - - protected strategy!: ILiquidationStrategy; - - /** - * Launch LiquidatorService - */ - public async launch(): Promise { - this.#errorHandler = new ErrorHandler(this.config, this.log); - const { partialLiquidatorAddress, deployPartialLiquidatorContracts } = - this.config; - this.strategy = - partialLiquidatorAddress || deployPartialLiquidatorContracts - ? (new LiquidationStrategyV3Partial() as any) - : (new LiquidationStrategyV3Full() as any); - await this.strategy.launch(); - this.notifier.notify(new StartedMessage()); - } - - public async liquidate(ca: CreditAccountData): Promise { - const logger = this.log.child({ - account: ca.addr, - borrower: ca.borrower, - manager: ca.managerName, - }); - if (this.#skipList.has(ca.addr)) { - this.log.warn("skipping this account"); - return; - } - logger.info( - `begin ${this.strategy.name} liquidation: HF = ${ca.healthFactor}`, - ); - this.notifier.notify(new LiquidationStartMessage(ca, this.strategy.name)); - let pathHuman: string[] | undefined; - let preview: StrategyPreview | undefined; - try { - preview = await this.strategy.preview(ca); - pathHuman = TxParserHelper.parseMultiCall(preview); - logger.debug({ pathHuman }, "path found"); - - const { request } = await this.strategy.simulate(ca, preview); - const receipt = await this.client.liquidate(ca, request); - - this.notifier.alert( - new LiquidationSuccessMessage( - ca, - this.strategy.adverb, - receipt, - pathHuman, - ), - ); - } catch (e) { - const decoded = await this.#errorHandler.explain(e, ca); - logger.error(decoded, "cant liquidate"); - if (preview?.skipOnFailure) { - this.#skipList.add(ca.addr); - this.log.warn("adding to skip list"); - } - this.notifier.alert( - new LiquidationErrorMessage( - ca, - this.strategy.adverb, - decoded.shortMessage, - pathHuman, - preview?.skipOnFailure, - ), - ); - } - } - - public async liquidateOptimistic( - ca: CreditAccountData, - ): Promise { - let acc = ca; - const logger = this.log.child({ - account: acc.addr, - borrower: acc.borrower, - manager: acc.managerName, - }); - let snapshotId: Hex | undefined; - const optimisticResult: OptimisticResult = { - creditManager: acc.creditManager, - borrower: acc.borrower, - account: acc.addr, - balancesBefore: ca.filterDust(), - hfBefore: acc.healthFactor, - balancesAfter: {}, - hfAfter: 0, - gasUsed: 0, - calls: [], - callsHuman: [], - isError: true, - pathAmount: "0", - liquidatorPremium: "0", - liquidatorProfit: "0", - }; - const start = Date.now(); - try { - const balanceBefore = await this.getExecutorBalance(acc.underlyingToken); - const mlRes = await this.strategy.makeLiquidatable(acc); - snapshotId = mlRes.snapshotId; - optimisticResult.partialLiquidationCondition = - mlRes.partialLiquidationCondition; - logger.debug({ snapshotId }, "previewing..."); - const preview = await this.strategy.preview(acc); - optimisticResult.assetOut = preview.assetOut; - optimisticResult.amountOut = preview.amountOut; - optimisticResult.flashLoanAmount = preview.flashLoanAmount; - optimisticResult.calls = preview.calls; - optimisticResult.pathAmount = preview.underlyingBalance.toString(); - optimisticResult.priceUpdates = preview.priceUpdates; - optimisticResult.callsHuman = TxParserHelper.parseMultiCall(preview); - logger.debug({ pathHuman: optimisticResult.callsHuman }, "path found"); - - const { request } = await this.strategy.simulate(acc, preview); - - // snapshotId might be present if we had to setup liquidation conditions for single account - // otherwise, not write requests has been made up to this point, and it's safe to take snapshot now - if (!snapshotId) { - snapshotId = await this.client.anvil.snapshot(); - } - // ------ Actual liquidation (write request start here) ----- - const receipt = await this.client.liquidate(acc, request); - logger.debug(`Liquidation tx hash: ${receipt.transactionHash}`); - optimisticResult.isError = receipt.status === "reverted"; - logger.debug( - `Liquidation tx receipt: status=${receipt.status}, gas=${receipt.cumulativeGasUsed.toString()}`, - ); - // ------ End of actual liquidation - acc = await this.strategy.updateCreditAccountData(acc); - optimisticResult.balancesAfter = ca.filterDust(); - optimisticResult.hfAfter = acc.healthFactor; - - let balanceAfter = await this.getExecutorBalance(acc.underlyingToken); - optimisticResult.gasUsed = Number(receipt.gasUsed); - optimisticResult.liquidatorPremium = ( - balanceAfter.underlying - balanceBefore.underlying - ).toString(10); - // swap underlying back to ETH - await this.swapper.swap(acc.underlyingToken, balanceAfter.underlying); - balanceAfter = await this.getExecutorBalance(acc.underlyingToken); - optimisticResult.liquidatorProfit = ( - balanceAfter.eth - balanceBefore.eth - ).toString(10); - } catch (e: any) { - const decoded = await this.#errorHandler.explain(e, acc, true); - optimisticResult.traceFile = decoded.traceFile; - optimisticResult.error = - `cannot liquidate: ${decoded.longMessage}`.replaceAll("\n", "\\n"); - logger.error({ decoded }, "cannot liquidate"); - } - - optimisticResult.duration = Date.now() - start; - this.optimistic.push(optimisticResult); - - if (snapshotId) { - await this.client.anvil.revert({ id: snapshotId }); - } - - return optimisticResult; - } - - protected async getExecutorBalance( - underlyingToken: Address, - ): Promise { - // using promise.all here sometimes results in anvil being stuck - const isWeth = tokenSymbolByAddress[underlyingToken] === "WETH"; - const eth = await this.client.pub.getBalance({ - address: this.client.address, - }); - const underlying = isWeth - ? eth - : await this.client.pub.readContract({ - address: underlyingToken, - abi: ierc20Abi, - functionName: "balanceOf", - args: [this.client.address], - }); - return { eth, underlying }; - } -} diff --git a/src/services/liquidate/SingularFullLiquidator.ts b/src/services/liquidate/SingularFullLiquidator.ts new file mode 100644 index 0000000..80cf8ff --- /dev/null +++ b/src/services/liquidate/SingularFullLiquidator.ts @@ -0,0 +1,68 @@ +import { iCreditFacadeV3Abi } from "@gearbox-protocol/types/abi"; +import type { SimulateContractReturnType } from "viem"; + +import { type CreditAccountData, exceptionsAbis } from "../../data/index.js"; +import type { PathFinderCloseResult } from "../../utils/ethers-6-temp/pathfinder/index.js"; +import SingularLiquidator from "./SingularLiquidator.js"; +import type { MakeLiquidatableResult, PriceUpdate } from "./types.js"; + +interface SinglularFullPreview extends PathFinderCloseResult { + priceUpdates: PriceUpdate[]; +} + +export default class SingularFullLiquidator extends SingularLiquidator { + protected readonly name = "full"; + protected readonly adverb = "fully"; + + public async makeLiquidatable( + ca: CreditAccountData, + ): Promise { + // not supported + return Promise.resolve({}); + } + + public async preview(ca: CreditAccountData): Promise { + try { + const cm = await this.getCreditManagerData(ca.creditManager); + + const result = await this.pathFinder.findBestClosePath( + ca, + cm, + this.config.slippage, + ); + if (!result) { + throw new Error("pathfinder result is empty"); + } + // we want fresh redstone price in actual liquidation transactions + const priceUpdates = await this.redstone.liquidationPreviewUpdates( + ca, + true, + ); + return { + amount: result.amount, + minAmount: result.minAmount, + underlyingBalance: result.underlyingBalance, + calls: [ + ...this.redstone.toMulticallUpdates(ca, priceUpdates), + ...result.calls, + ], + priceUpdates, + }; + } catch (e) { + throw new Error(`cant find close path: ${e}`); + } + } + + public async simulate( + account: CreditAccountData, + preview: SinglularFullPreview, + ): Promise { + return this.client.pub.simulateContract({ + account: this.client.account, + abi: [...iCreditFacadeV3Abi, ...exceptionsAbis], + address: account.creditFacade, + functionName: "liquidateCreditAccount", + args: [account.addr, this.client.address, preview.calls], + }); + } +} diff --git a/src/services/liquidate/SingularLiquidator.ts b/src/services/liquidate/SingularLiquidator.ts new file mode 100644 index 0000000..f1e1551 --- /dev/null +++ b/src/services/liquidate/SingularLiquidator.ts @@ -0,0 +1,195 @@ +import type { OptimisticResult } from "@gearbox-protocol/types/optimist"; +import type { Hex, SimulateContractReturnType } from "viem"; + +import type { CreditAccountData } from "../../data/index.js"; +import { TxParserHelper } from "../../utils/ethers-6-temp/txparser/index.js"; +import { + LiquidationErrorMessage, + LiquidationStartMessage, + LiquidationSuccessMessage, +} from "../notifier/index.js"; +import AbstractLiquidator from "./AbstractLiquidator.js"; +import type { + ILiquidatorService, + MakeLiquidatableResult, + StrategyPreview, +} from "./types.js"; + +export default abstract class SingularLiquidator + extends AbstractLiquidator + implements ILiquidatorService +{ + protected abstract readonly name: string; + protected abstract readonly adverb: string; + + public async liquidate(accounts: CreditAccountData[]): Promise { + if (!accounts.length) { + return; + } + this.logger.warn(`Need to liquidate ${accounts.length} accounts`); + for (const ca of accounts) { + await this.#liquidateOne(ca); + } + } + + public async liquidateOptimistic( + accounts: CreditAccountData[], + ): Promise { + const total = accounts.length; + const debugS = this.config.debugAccounts ? "selective " : " "; + this.logger.info(`${debugS}optimistic liquidation for ${total} accounts`); + + for (let i = 0; i < total; i++) { + const acc = accounts[i]; + const result = await this.#liquidateOneOptimistic(acc); + const status = result.isError ? "FAIL" : "OK"; + const msg = `[${i + 1}/${total}] ${acc.addr} in ${acc.creditManager} ${status}`; + if (result.isError) { + this.logger.warn(msg); + } else { + this.logger.info(msg); + } + } + const success = this.optimistic.get().filter(r => !r.isError).length; + this.logger.info( + `optimistic liquidation finished: ${success}/${total} accounts liquidated`, + ); + } + + async #liquidateOne(ca: CreditAccountData): Promise { + const logger = this.logger.child({ + account: ca.addr, + borrower: ca.borrower, + manager: ca.managerName, + }); + if (this.skipList.has(ca.addr)) { + this.logger.warn("skipping this account"); + return; + } + logger.info(`begin ${this.name} liquidation: HF = ${ca.healthFactor}`); + this.notifier.notify(new LiquidationStartMessage(ca, this.name)); + let pathHuman: string[] | undefined; + let preview: T | undefined; + try { + preview = await this.preview(ca); + pathHuman = TxParserHelper.parseMultiCall(preview); + logger.debug({ pathHuman }, "path found"); + + const { request } = await this.simulate(ca, preview); + const receipt = await this.client.liquidate(request, logger); + + this.notifier.alert( + new LiquidationSuccessMessage(ca, this.adverb, receipt, pathHuman), + ); + } catch (e) { + const decoded = await this.errorHandler.explain(e, ca); + logger.error(decoded, "cant liquidate"); + if (preview?.skipOnFailure) { + this.skipList.add(ca.addr); + this.logger.warn("adding to skip list"); + } + this.notifier.alert( + new LiquidationErrorMessage( + ca, + this.adverb, + decoded.shortMessage, + pathHuman, + preview?.skipOnFailure, + ), + ); + } + } + + async #liquidateOneOptimistic( + acc: CreditAccountData, + ): Promise { + const logger = this.logger.child({ + account: acc.addr, + borrower: acc.borrower, + manager: acc.managerName, + }); + let snapshotId: Hex | undefined; + let result = this.newOptimisticResult(acc); + const start = Date.now(); + try { + const balanceBefore = await this.getExecutorBalance(acc.underlyingToken); + const mlRes = await this.makeLiquidatable(acc); + snapshotId = mlRes.snapshotId; + result.partialLiquidationCondition = mlRes.partialLiquidationCondition; + logger.debug({ snapshotId }, "previewing..."); + const preview = await this.preview(acc); + logger.debug({ pathHuman: result.callsHuman }, "path found"); + result = this.updateAfterPreview(result, preview); + + const { request } = await this.simulate(acc, preview); + + // snapshotId might be present if we had to setup liquidation conditions for single account + // otherwise, not write requests has been made up to this point, and it's safe to take snapshot now + if (!snapshotId) { + snapshotId = await this.client.anvil.snapshot(); + } + // ------ Actual liquidation (write request start here) ----- + const receipt = await this.client.liquidate(request, logger); + logger.debug(`Liquidation tx hash: ${receipt.transactionHash}`); + result.isError = receipt.status === "reverted"; + logger.debug( + `Liquidation tx receipt: status=${receipt.status}, gas=${receipt.cumulativeGasUsed.toString()}`, + ); + // ------ End of actual liquidation + result = await this.updateAfterLiquidation( + result, + acc, + balanceBefore.underlying, + receipt, + ); + // swap underlying back to ETH + await this.swapper.swap( + acc.underlyingToken, + balanceBefore.underlying + BigInt(result.liquidatorPremium), + ); + const balanceAfter = await this.getExecutorBalance(acc.underlyingToken); + result.liquidatorProfit = (balanceAfter.eth - balanceBefore.eth).toString( + 10, + ); + } catch (e: any) { + const decoded = await this.errorHandler.explain(e, acc, true); + result.traceFile = decoded.traceFile; + result.error = `cannot liquidate: ${decoded.longMessage}`.replaceAll( + "\n", + "\\n", + ); + logger.error({ decoded }, "cannot liquidate"); + } + + result.duration = Date.now() - start; + this.optimistic.push(result); + + if (snapshotId) { + await this.client.anvil.revert({ id: snapshotId }); + } + + return result; + } + + /** + * For optimistic liquidations only: create conditions that make this account liquidatable + * If strategy implements this scenario, it must make evm_snapshot beforehand and return it as a result + * Id strategy does not support this, return undefined + * @param ca + * @returns evm snapshotId or underfined + */ + abstract makeLiquidatable( + ca: CreditAccountData, + ): Promise; + abstract preview(ca: CreditAccountData): Promise; + /** + * Simulates liquidation + * @param account + * @param preview + * @returns + */ + abstract simulate( + account: CreditAccountData, + preview: T, + ): Promise; +} diff --git a/src/services/liquidate/LiquidationStrategyV3Partial.ts b/src/services/liquidate/SingularPartialLiquidator.ts similarity index 98% rename from src/services/liquidate/LiquidationStrategyV3Partial.ts rename to src/services/liquidate/SingularPartialLiquidator.ts index 7a9d677..bddb8a9 100644 --- a/src/services/liquidate/LiquidationStrategyV3Partial.ts +++ b/src/services/liquidate/SingularPartialLiquidator.ts @@ -34,10 +34,9 @@ import { type CreditManagerData, exceptionsAbis, } from "../../data/index.js"; -import { type ILogger, Logger } from "../../log/index.js"; -import AbstractLiquidationStrategyV3 from "./AbstractLiquidationStrategyV3.js"; +import type { ILogger } from "../../log/index.js"; +import SingularLiquidator from "./SingularLiquidator.js"; import type { - ILiquidationStrategy, MakeLiquidatableResult, MerkleDistributorInfo, PartialLiquidationPreview, @@ -51,15 +50,9 @@ interface TokenBalance extends ExcludeArrayProps { weightedBalance: bigint; } -export default class LiquidationStrategyV3Partial - extends AbstractLiquidationStrategyV3 - implements ILiquidationStrategy -{ - public readonly name = "partial"; - public readonly adverb = "partially"; - - @Logger("LiquidationStrategyV3Partial") - logger!: ILogger; +export default class SingularPartialLiquidator extends SingularLiquidator { + protected readonly name = "partial"; + protected readonly adverb = "partially"; #partialLiquidator?: Address; #priceHelper?: Address; diff --git a/src/services/liquidate/factory.ts b/src/services/liquidate/factory.ts new file mode 100644 index 0000000..56a5f5a --- /dev/null +++ b/src/services/liquidate/factory.ts @@ -0,0 +1,30 @@ +import type { IFactory } from "di-at-home"; + +import type { Config } from "../../config/index.js"; +import { DI } from "../../di.js"; +import BatchLiquidator from "./BatchLiquidator.js"; +import SingularFullLiquidator from "./SingularFullLiquidator.js"; +import SingularPartialLiquidator from "./SingularPartialLiquidator.js"; +import type { ILiquidatorService } from "./types.js"; + +@DI.Factory(DI.Liquidator) +export class LiquidatorFactory implements IFactory { + @DI.Inject(DI.Config) + config!: Config; + + produce(): ILiquidatorService { + const { + deployBatchLiquidatorContracts, + deployPartialLiquidatorContracts, + partialLiquidatorAddress, + batchLiquidatorAddress, + } = this.config; + if (deployPartialLiquidatorContracts || partialLiquidatorAddress) { + return new SingularPartialLiquidator(); + } + if (deployBatchLiquidatorContracts || batchLiquidatorAddress) { + return new BatchLiquidator(); + } + return new SingularFullLiquidator(); + } +} diff --git a/src/services/liquidate/index.ts b/src/services/liquidate/index.ts index c2f49b1..a0a7e6b 100644 --- a/src/services/liquidate/index.ts +++ b/src/services/liquidate/index.ts @@ -1,3 +1,4 @@ -export * from "./LiquidatorService.js"; +export * from "./factory.js"; export * from "./OptimisiticResults.js"; export type * from "./types.js"; +export type * from "./viem-types.js"; diff --git a/src/services/liquidate/types.ts b/src/services/liquidate/types.ts index aa589e5..b3b024d 100644 --- a/src/services/liquidate/types.ts +++ b/src/services/liquidate/types.ts @@ -1,8 +1,5 @@ -import type { - OptimisticResult, - PartialLiquidationCondition, -} from "@gearbox-protocol/types/optimist"; -import type { Address, Hash, Hex, SimulateContractReturnType } from "viem"; +import type { PartialLiquidationCondition } from "@gearbox-protocol/types/optimist"; +import type { Address, Hash, Hex } from "viem"; import type { CreditAccountData, @@ -11,6 +8,11 @@ import type { } from "../../data/index.js"; export interface PriceOnDemandExtras extends PriceOnDemand { + /** + * In case when token in PriceOnDemand is ticker, this will be the original token + * Otherwise they are the same + */ + originalToken: Address; ts: number; reserve: boolean; } @@ -33,14 +35,14 @@ export interface PartialLiquidationPreview { export interface ILiquidatorService { launch: () => Promise; - liquidate: (ca: CreditAccountData) => Promise; + liquidate: (accounts: CreditAccountData[]) => Promise; /** * * @param ca * @param redstoneTokens * @returns true is account was successfully liquidated */ - liquidateOptimistic: (ca: CreditAccountData) => Promise; + liquidateOptimistic: (accounts: CreditAccountData[]) => Promise; } export interface StrategyPreview { @@ -65,39 +67,6 @@ export interface StrategyPreview { skipOnFailure?: boolean; } -export interface ILiquidationStrategy { - name: string; - adverb: string; - launch: () => Promise; - /** - * Fetches credit account data again for optimistic report - * @param ca - * @returns - */ - updateCreditAccountData: ( - ca: CreditAccountData, - ) => Promise; - /** - * For optimistic liquidations only: create conditions that make this account liquidatable - * If strategy implements this scenario, it must make evm_snapshot beforehand and return it as a result - * Id strategy does not support this, return undefined - * @param ca - * @returns evm snapshotId or underfined - */ - makeLiquidatable: (ca: CreditAccountData) => Promise; - preview: (ca: CreditAccountData) => Promise; - /** - * Simulates liquidation - * @param account - * @param preview - * @returns - */ - simulate: ( - account: CreditAccountData, - preview: T, - ) => Promise; -} - export interface MakeLiquidatableResult { snapshotId?: Hex; partialLiquidationCondition?: PartialLiquidationCondition; diff --git a/src/services/liquidate/viem-types.ts b/src/services/liquidate/viem-types.ts index 0a788a3..15cc490 100644 --- a/src/services/liquidate/viem-types.ts +++ b/src/services/liquidate/viem-types.ts @@ -1,4 +1,5 @@ import type { + iBatchLiquidatorAbi, iLiquidatorAbi, iPriceHelperAbi, } from "@gearbox-protocol/liquidator-v2-contracts/abi"; @@ -22,3 +23,21 @@ export type TokenPriceInfo = ArrayElementType< ExtractAbiFunction["outputs"]["0"] > >; + +export type BatchLiquidationResult = ArrayElementType< + AbiParameterToPrimitiveType< + ExtractAbiFunction< + typeof iBatchLiquidatorAbi, + "estimateBatch" + >["outputs"]["0"] + > +>; + +export type LiquidateBatchInput = ArrayElementType< + AbiParameterToPrimitiveType< + ExtractAbiFunction< + typeof iBatchLiquidatorAbi, + "liquidateBatch" + >["inputs"]["0"] + > +>; diff --git a/src/services/notifier/messages.ts b/src/services/notifier/messages.ts index ecf101d..0a6fa6a 100644 --- a/src/services/notifier/messages.ts +++ b/src/services/notifier/messages.ts @@ -1,5 +1,6 @@ import type { NetworkType } from "@gearbox-protocol/sdk-gov"; import { formatBN } from "@gearbox-protocol/sdk-gov"; +import type { OptimisticResult } from "@gearbox-protocol/types/optimist"; import type { Markdown } from "@vlad-yakovlev/telegram-md"; import { md } from "@vlad-yakovlev/telegram-md"; import type { Address, TransactionReceipt } from "viem"; @@ -196,6 +197,59 @@ ${callsMd(this.#callsHuman)}`, } } +export class BatchLiquidationFinishedMessage + extends BaseMessage + implements INotifierMessage +{ + #liquidated: number; + #notLiquidated: number; + + constructor(receipt: TransactionReceipt, results: OptimisticResult[]) { + super({ receipt }); + this.#liquidated = results.filter(r => !r.isError).length; + this.#notLiquidated = results.filter(r => !!r.isError).length; + } + + public get plain(): string { + if (this.receipt?.status === "success") { + if (this.#notLiquidated === 0) { + return `✅ [${this.network}] batch-liquidated ${this.#liquidated} accounts: +Tx receipt: ${this.receiptPlain} +Gas used: ${this.receipt?.gasUsed?.toLocaleString("en")}`; + } else { + return `❌ [${this.network}] batch-liquidated ${this.#liquidated} accounts, but failed to liquidate ${this.#notLiquidated} more +Tx receipt: ${this.receiptPlain} +Gas used: ${this.receipt?.gasUsed?.toLocaleString("en")}`; + } + } + + return `❌ [${this.network}] batch-liquidate tx reverted +Tx: ${this.receiptPlain}`; + } + + public get markdown(): string { + if (this.receipt?.status === "success") { + if (this.#notLiquidated === 0) { + return md.build( + md`✅ [${this.network}] batch-liquidated ${this.#liquidated} accounts +Tx receipt: ${this.receiptMd} +Gas used: ${md.bold(this.receipt?.gasUsed?.toLocaleString("en"))}`, + ); + } else { + return md.build( + md`❌ [${this.network}] batch-liquidated ${this.#liquidated} accounts, but failed to liquidate ${this.#notLiquidated} more +Tx receipt: ${this.receiptMd} +Gas used: ${md.bold(this.receipt?.gasUsed?.toLocaleString("en"))}`, + ); + } + } + return md.build( + md`❌ [${this.network}] batch-liquidate tx reverted +Tx: ${this.receiptMd}`, + ); + } +} + export class LiquidationErrorMessage extends BaseMessage implements INotifierMessage @@ -239,6 +293,31 @@ ${this.#skipOnFailure}`, ); } } +export class BatchLiquidationErrorMessage + extends BaseMessage + implements INotifierMessage +{ + #error: string; + #accounts: CreditAccountData[]; + + constructor(accounts: CreditAccountData[], error: string) { + super({}); + this.#accounts = accounts; + this.#error = error.length > 128 ? error.slice(0, 128) + "..." : error; + } + + public get plain(): string { + return `❌ [${this.network}] failed to batch-liquidate ${this.#accounts.length} accounts +Error: ${this.#error}`; + } + + public get markdown(): string { + return md.build( + md`❌ [${this.network}] failed to batch-liquidate ${this.#accounts.length} accounts +Error: ${md.inlineCode(this.#error)}`, + ); + } +} function callsPlain(calls?: string[]): string { return calls ? calls.map(c => " ➤ " + c).join("\n") : "-"; diff --git a/src/services/scan/AbstractScanService.ts b/src/services/scan/AbstractScanService.ts deleted file mode 100644 index e8dc575..0000000 --- a/src/services/scan/AbstractScanService.ts +++ /dev/null @@ -1,101 +0,0 @@ -import type { Config } from "../../config/index.js"; -import type { CreditAccountData } from "../../data/index.js"; -import { DI } from "../../di.js"; -import type { ILogger } from "../../log/index.js"; -import type { AddressProviderService } from "../AddressProviderService.js"; -import type Client from "../Client.js"; -import type { - ILiquidatorService, - OptimisticResults, -} from "../liquidate/index.js"; - -export default abstract class AbstractScanService { - abstract log: ILogger; - - @DI.Inject(DI.Config) - config!: Config; - - @DI.Inject(DI.AddressProvider) - addressProvider!: AddressProviderService; - - @DI.Inject(DI.OptimisticResults) - optimistic!: OptimisticResults; - - @DI.Inject(DI.Client) - client!: Client; - - protected _lastUpdated = 0n; - - public get lastUpdated(): bigint { - return this._lastUpdated; - } - - protected abstract get liquidatorService(): ILiquidatorService; - - /** - * Launches ScanService - * @param dataCompressor Address of DataCompressor - * @param liquidatorService Liquidation service - */ - public async launch(): Promise { - await this.liquidatorService.launch(); - await this._launch(); - this.subscribeToUpdates(); - } - - protected subscribeToUpdates(): void { - if (this.config.optimistic) { - return; - } - this.client.pub.watchBlockNumber({ - onBlockNumber: n => this.onBlock(n), - }); - } - - protected abstract _launch(): Promise; - protected abstract onBlock(block: bigint): Promise; - - /** - * Liquidate accounts using NORMAL flow - * @param accountsToLiquidate - */ - protected async liquidateNormal( - accountsToLiquidate: CreditAccountData[], - ): Promise { - if (!accountsToLiquidate.length) { - return; - } - this.log.warn(`Need to liquidate ${accountsToLiquidate.length} accounts`); - for (const ca of accountsToLiquidate) { - await this.liquidatorService.liquidate(ca); - } - } - - /** - * Liquidate accounts using OPTIMISTIC flow - * @param accountsToLiquidate - */ - protected async liquidateOptimistically( - accounts: CreditAccountData[], - ): Promise { - const total = accounts.length; - const debugS = this.config.debugAccounts ? "selective " : " "; - this.log.info(`${debugS}optimistic liquidation for ${total} accounts`); - - for (let i = 0; i < total; i++) { - const acc = accounts[i]; - const result = await this.liquidatorService.liquidateOptimistic(acc); - const status = result.isError ? "FAIL" : "OK"; - const msg = `[${i + 1}/${total}] ${acc.addr} in ${acc.creditManager} ${status}`; - if (result.isError) { - this.log.warn(msg); - } else { - this.log.info(msg); - } - } - const success = this.optimistic.get().filter(r => !r.isError).length; - this.log.info( - `optimistic liquidation finished: ${success}/${total} accounts liquidated`, - ); - } -} diff --git a/src/services/scan/index.ts b/src/services/scan/index.ts deleted file mode 100644 index 8d19a87..0000000 --- a/src/services/scan/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./ScanServiceV3.js"; diff --git a/src/services/scan/ScanServiceV3.ts b/src/services/scanner/Scanner.ts similarity index 91% rename from src/services/scan/ScanServiceV3.ts rename to src/services/scanner/Scanner.ts index 4dea782..f6c3f35 100644 --- a/src/services/scan/ScanServiceV3.ts +++ b/src/services/scanner/Scanner.ts @@ -10,18 +10,17 @@ import { } from "@gearbox-protocol/types/abi"; import { getContract } from "viem"; +import type { Config } from "../../config/index.js"; import type { CreditAccountDataRaw, PriceOnDemand } from "../../data/index.js"; import { CreditAccountData } from "../../data/index.js"; import { DI } from "../../di.js"; import { type ILogger, Logger } from "../../log/index.js"; import type { IDataCompressorContract } from "../../utils/index.js"; -import type { - ILiquidatorService, - LiquidatorService, -} from "../liquidate/index.js"; +import type { AddressProviderService } from "../AddressProviderService.js"; +import type Client from "../Client.js"; +import type { ILiquidatorService } from "../liquidate/index.js"; import type OracleServiceV3 from "../OracleServiceV3.js"; import type { RedstoneServiceV3 } from "../RedstoneServiceV3.js"; -import AbstractScanService from "./AbstractScanService.js"; const RESTAKING_CMS: Partial> = { Mainnet: @@ -37,10 +36,19 @@ interface AccountSelection { } @DI.Injectable(DI.Scanner) -export class ScanServiceV3 extends AbstractScanService { +export class Scanner { @Logger("Scanner") log!: ILogger; + @DI.Inject(DI.Config) + config!: Config; + + @DI.Inject(DI.AddressProvider) + addressProvider!: AddressProviderService; + + @DI.Inject(DI.Client) + client!: Client; + @DI.Inject(DI.Oracle) oracle!: OracleServiceV3; @@ -48,14 +56,16 @@ export class ScanServiceV3 extends AbstractScanService { redstone!: RedstoneServiceV3; @DI.Inject(DI.Liquidator) - _liquidatorService!: LiquidatorService; + liquidatorService!: ILiquidatorService; #dataCompressor?: IDataCompressorContract; #processing: bigint | null = null; #restakingCMAddr?: Address; #restakingMinHF?: bigint; + #lastUpdated = 0n; - protected override async _launch(): Promise { + public async launch(): Promise { + await this.liquidatorService.launch(); if (this.config.restakingWorkaround) { await this.#setupRestakingWorkaround(); } @@ -73,10 +83,15 @@ export class ScanServiceV3 extends AbstractScanService { await this.oracle.launch(block); // we should not pin block during optimistic liquidations // because during optimistic liquidations we need to call evm_mine to make redstone work - await this.updateAccounts(this.config.optimistic ? undefined : block); + await this.#updateAccounts(this.config.optimistic ? undefined : block); + if (!this.config.optimistic) { + this.client.pub.watchBlockNumber({ + onBlockNumber: n => this.#onBlock(n), + }); + } } - protected override async onBlock(blockNumber: bigint): Promise { + async #onBlock(blockNumber: bigint): Promise { if (this.#processing) { this.log.debug( `skipping block ${blockNumber}, still processing block ${this.#processing}`, @@ -85,16 +100,16 @@ export class ScanServiceV3 extends AbstractScanService { } this.#processing = blockNumber; await this.oracle.update(blockNumber); - await this.updateAccounts(blockNumber); + await this.#updateAccounts(blockNumber); this.#processing = null; - this._lastUpdated = blockNumber; + this.#lastUpdated = blockNumber; } /** * Loads new data and recompute all health factors * @param atBlock Fiex block for archive node which is needed to get data */ - protected async updateAccounts(atBlock?: bigint): Promise { + async #updateAccounts(atBlock?: bigint): Promise { const start = new Date().getTime(); const blockS = atBlock ? ` in ${atBlock}` : ""; let [accounts, failedTokens] = await this.#potentialLiquidations( @@ -138,9 +153,9 @@ export class ScanServiceV3 extends AbstractScanService { } if (this.config.optimistic) { - await this.liquidateOptimistically(accounts); + await this.liquidatorService.liquidateOptimistic(accounts); } else { - await this.liquidateNormal(accounts); + await this.liquidatorService.liquidate(accounts); } } @@ -383,16 +398,16 @@ export class ScanServiceV3 extends AbstractScanService { }); } - protected override get liquidatorService(): ILiquidatorService { - return this._liquidatorService; - } - private get dataCompressor(): IDataCompressorContract { if (!this.#dataCompressor) { throw new Error("data compressor not initialized"); } return this.#dataCompressor; } + + public get lastUpdated(): bigint { + return this.#lastUpdated; + } } function printTokens(tokens: Address[]): string { diff --git a/src/services/scanner/index.ts b/src/services/scanner/index.ts new file mode 100644 index 0000000..34f043b --- /dev/null +++ b/src/services/scanner/index.ts @@ -0,0 +1 @@ +export * from "./Scanner.js"; diff --git a/src/utils/ethers-6-temp/pathfinder/pathfinder.ts b/src/utils/ethers-6-temp/pathfinder/pathfinder.ts index 637b6ad..3acf345 100644 --- a/src/utils/ethers-6-temp/pathfinder/pathfinder.ts +++ b/src/utils/ethers-6-temp/pathfinder/pathfinder.ts @@ -1,5 +1,5 @@ import type { NetworkType } from "@gearbox-protocol/sdk-gov"; -import { getConnectors } from "@gearbox-protocol/sdk-gov"; +import { getConnectors, getDecimals } from "@gearbox-protocol/sdk-gov"; import { iRouterV3Abi } from "@gearbox-protocol/types/abi"; import { type Address, getContract, type PublicClient } from "viem"; @@ -9,18 +9,23 @@ import type { CreditManagerData, } from "../../../data/index.js"; import type { PathFinderCloseResult } from "./core.js"; +import type { PathOptionSerie } from "./pathOptions.js"; import { PathOptionFactory } from "./pathOptions.js"; -import type { IRouterV3Contract, RouterResult } from "./viem-types.js"; +import type { + EstimateBatchInput, + IRouterV3Contract, + RouterResult, +} from "./viem-types.js"; const MAX_GAS_PER_ROUTE = 200_000_000n; const GAS_PER_BLOCK = 400_000_000n; +const LOOPS_PER_TX = Number(GAS_PER_BLOCK / MAX_GAS_PER_ROUTE); -interface FindBestClosePathProps { - creditAccount: CreditAccountData; - creditManager: CreditManagerData; - expectedBalances: Record; - leftoverBalances: Record; - slippage: number; +interface FindBestClosePathInterm { + pathOptions: PathOptionSerie[]; + expected: Balance[]; + leftover: Balance[]; + connectors: Address[]; } export class PathFinder { @@ -41,54 +46,31 @@ export class PathFinder { /** * @dev Finds the path to swap / withdraw all assets from CreditAccount into underlying asset * Can bu used for closing Credit Account and for liquidations as well. - * @param creditAccount CreditAccountData object used for close path computation + * @param ca CreditAccountData object used for close path computation + * @param cm CreditManagerData for corresponging credit manager * @param slippage Slippage in PERCENTAGE_FORMAT (100% = 10_000) per operation * @return The best option in PathFinderCloseResult format, which * - underlyingBalance - total balance of underlying token * - calls - list of calls which should be done to swap & unwrap everything to underlying token */ - async findBestClosePath({ - creditAccount, - creditManager: cm, - expectedBalances, - leftoverBalances, - slippage, - }: FindBestClosePathProps): Promise { - const loopsPerTx = Number(GAS_PER_BLOCK / MAX_GAS_PER_ROUTE); - const pathOptions = PathOptionFactory.generatePathOptions( - creditAccount.allBalances, - loopsPerTx, - this.#network, - ); - - const expected: Balance[] = cm.collateralTokens.map(token => { - // When we pass expected balances explicitly, we need to mimic router behaviour by filtering out leftover tokens - // for example, we can have stETH balance of 2, because 1 transforms to 2 because of rebasing - // https://github.com/Gearbox-protocol/router-v3/blob/c230a3aa568bb432e50463cfddc877fec8940cf5/contracts/RouterV3.sol#L222 - const actual = expectedBalances[token]?.balance || 0n; - return { - token, - balance: actual > 10n ? actual : 0n, - }; - }); - - const leftover: Balance[] = cm.collateralTokens.map(token => ({ - token, - balance: leftoverBalances[token]?.balance || 1n, - })); - - const connectors = this.getAvailableConnectors(creditAccount.allBalances); + async findBestClosePath( + ca: CreditAccountData, + cm: CreditManagerData, + slippage: bigint | number, + ): Promise { + const { pathOptions, expected, leftover, connectors } = + this.#getBestClosePathInput(ca, cm); let results: RouterResult[] = []; for (const po of pathOptions) { const { result } = await this.#pathFinder.simulate.findBestClosePath( [ - creditAccount.addr, + ca.addr, expected, leftover, connectors, BigInt(slippage), po, - BigInt(loopsPerTx), + BigInt(LOOPS_PER_TX), false, ], { @@ -114,14 +96,73 @@ export class PathFinder { callData: c.callData, target: c.target, })), - underlyingBalance: - bestResult.minAmount + - creditAccount.balances[ - creditAccount.underlyingToken.toLowerCase() as Address - ], + underlyingBalance: bestResult.minAmount + ca.balances[ca.underlyingToken], + }; + } + + // TODO: readme + getEstimateBatchInput( + ca: CreditAccountData, + cm: CreditManagerData, + slippage: number, + ): EstimateBatchInput { + const { pathOptions, connectors, expected, leftover } = + this.#getBestClosePathInput(ca, cm); + return { + creditAccount: ca.addr, + expectedBalances: expected, + leftoverBalances: leftover, + connectors, + slippage: BigInt(slippage), + pathOptions: pathOptions[0] ?? [], // TODO: what to put here? + iterations: BigInt(LOOPS_PER_TX), + force: false, + priceUpdates: [], }; } + #getBestClosePathInput( + ca: CreditAccountData, + cm: CreditManagerData, + ): FindBestClosePathInterm { + const expectedBalances: Record = {}; + const leftoverBalances: Record = {}; + for (const { token, balance, isEnabled } of ca.allBalances) { + expectedBalances[token] = { token, balance }; + // filter out dust, we don't want to swap it + const minBalance = 10n ** BigInt(Math.max(8, getDecimals(token)) - 8); + // also: gearbox liquidator does not need to swap disabled tokens. third-party liquidators might want to do it + if (balance < minBalance || !isEnabled) { + leftoverBalances[token] = { token, balance }; + } + } + + const pathOptions = PathOptionFactory.generatePathOptions( + ca.allBalances, + LOOPS_PER_TX, + this.#network, + ); + + const expected: Balance[] = cm.collateralTokens.map(token => { + // When we pass expected balances explicitly, we need to mimic router behaviour by filtering out leftover tokens + // for example, we can have stETH balance of 2, because 1 transforms to 2 because of rebasing + // https://github.com/Gearbox-protocol/router-v3/blob/c230a3aa568bb432e50463cfddc877fec8940cf5/contracts/RouterV3.sol#L222 + const actual = expectedBalances[token]?.balance || 0n; + return { + token, + balance: actual > 10n ? actual : 0n, + }; + }); + + const leftover: Balance[] = cm.collateralTokens.map(token => ({ + token, + balance: leftoverBalances[token]?.balance || 1n, + })); + + const connectors = this.getAvailableConnectors(ca.allBalances); + return { expected, leftover, connectors, pathOptions }; + } + static compare(r1: T, r2: T): T { return r1.amount > r2.amount ? r1 : r2; } diff --git a/src/utils/ethers-6-temp/pathfinder/viem-types.ts b/src/utils/ethers-6-temp/pathfinder/viem-types.ts index 623b13d..8e8956f 100644 --- a/src/utils/ethers-6-temp/pathfinder/viem-types.ts +++ b/src/utils/ethers-6-temp/pathfinder/viem-types.ts @@ -1,7 +1,10 @@ +import type { iBatchLiquidatorAbi } from "@gearbox-protocol/liquidator-v2-contracts/abi"; import type { iRouterV3Abi } from "@gearbox-protocol/types/abi"; import type { AbiParameterToPrimitiveType, ExtractAbiFunction } from "abitype"; import type { GetContractReturnType, PublicClient } from "viem"; +import type { ArrayElementType } from "../../index.js"; + export type IRouterV3Contract = GetContractReturnType< typeof iRouterV3Abi, PublicClient @@ -10,3 +13,12 @@ export type IRouterV3Contract = GetContractReturnType< export type RouterResult = AbiParameterToPrimitiveType< ExtractAbiFunction["outputs"]["0"] >; + +export type EstimateBatchInput = ArrayElementType< + AbiParameterToPrimitiveType< + ExtractAbiFunction< + typeof iBatchLiquidatorAbi, + "estimateBatch" + >["inputs"]["0"] + > +>; diff --git a/yarn.lock b/yarn.lock index 35c7f07..4812c52 100644 --- a/yarn.lock +++ b/yarn.lock @@ -88,18 +88,18 @@ "@smithy/util-utf8" "^2.0.0" tslib "^2.6.2" -"@aws-sdk/client-s3@^3.624.0": - version "3.624.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-s3/-/client-s3-3.624.0.tgz#4114c3cb4bf820e0aa480d3783c3cd2ae2575737" - integrity sha512-A18tgTKC4ZTAwV8i3pkyAL1XDLgH7WGS5hZA/0FOntI5l+icztGZFF8CdeYWEAFnZA7SfHK6vmtEbIQDOzTTAA== +"@aws-sdk/client-s3@^3.629.0": + version "3.629.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-s3/-/client-s3-3.629.0.tgz#6c22639c0cdb73b05409b5633a87010bfec7b107" + integrity sha512-Q0YXKdUA7NboPl94JOKD4clHHuERG1Kwy0JPbU+3Hvmz/UuwUGBmlfaRAqd9y4LXsTv/2xKtFPW9R+nBfy9mwA== dependencies: "@aws-crypto/sha1-browser" "5.2.0" "@aws-crypto/sha256-browser" "5.2.0" "@aws-crypto/sha256-js" "5.2.0" - "@aws-sdk/client-sso-oidc" "3.624.0" - "@aws-sdk/client-sts" "3.624.0" - "@aws-sdk/core" "3.624.0" - "@aws-sdk/credential-provider-node" "3.624.0" + "@aws-sdk/client-sso-oidc" "3.629.0" + "@aws-sdk/client-sts" "3.629.0" + "@aws-sdk/core" "3.629.0" + "@aws-sdk/credential-provider-node" "3.629.0" "@aws-sdk/middleware-bucket-endpoint" "3.620.0" "@aws-sdk/middleware-expect-continue" "3.620.0" "@aws-sdk/middleware-flexible-checksums" "3.620.0" @@ -107,11 +107,11 @@ "@aws-sdk/middleware-location-constraint" "3.609.0" "@aws-sdk/middleware-logger" "3.609.0" "@aws-sdk/middleware-recursion-detection" "3.620.0" - "@aws-sdk/middleware-sdk-s3" "3.624.0" + "@aws-sdk/middleware-sdk-s3" "3.629.0" "@aws-sdk/middleware-ssec" "3.609.0" "@aws-sdk/middleware-user-agent" "3.620.0" "@aws-sdk/region-config-resolver" "3.614.0" - "@aws-sdk/signature-v4-multi-region" "3.624.0" + "@aws-sdk/signature-v4-multi-region" "3.629.0" "@aws-sdk/types" "3.609.0" "@aws-sdk/util-endpoints" "3.614.0" "@aws-sdk/util-user-agent-browser" "3.609.0" @@ -119,9 +119,9 @@ "@aws-sdk/xml-builder" "3.609.0" "@smithy/config-resolver" "^3.0.5" "@smithy/core" "^2.3.2" - "@smithy/eventstream-serde-browser" "^3.0.5" + "@smithy/eventstream-serde-browser" "^3.0.6" "@smithy/eventstream-serde-config-resolver" "^3.0.3" - "@smithy/eventstream-serde-node" "^3.0.4" + "@smithy/eventstream-serde-node" "^3.0.5" "@smithy/fetch-http-handler" "^3.2.4" "@smithy/hash-blob-browser" "^3.1.2" "@smithy/hash-node" "^3.0.3" @@ -152,15 +152,15 @@ "@smithy/util-waiter" "^3.1.2" tslib "^2.6.2" -"@aws-sdk/client-sso-oidc@3.624.0": - version "3.624.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.624.0.tgz#33d0927519de333387ee07cb7f6483b0bd4db2f2" - integrity sha512-Ki2uKYJKKtfHxxZsiMTOvJoVRP6b2pZ1u3rcUb2m/nVgBPUfLdl8ZkGpqE29I+t5/QaS/sEdbn6cgMUZwl+3Dg== +"@aws-sdk/client-sso-oidc@3.629.0": + version "3.629.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.629.0.tgz#8bd4138c4ff24962e0f2753cfa9722a18330ad1f" + integrity sha512-3if0LauNJPqubGYf8vnlkp+B3yAeKRuRNxfNbHlE6l510xWGcKK/ZsEmiFmfePzKKSRrDh/cxMFMScgOrXptNg== dependencies: "@aws-crypto/sha256-browser" "5.2.0" "@aws-crypto/sha256-js" "5.2.0" - "@aws-sdk/core" "3.624.0" - "@aws-sdk/credential-provider-node" "3.624.0" + "@aws-sdk/core" "3.629.0" + "@aws-sdk/credential-provider-node" "3.629.0" "@aws-sdk/middleware-host-header" "3.620.0" "@aws-sdk/middleware-logger" "3.609.0" "@aws-sdk/middleware-recursion-detection" "3.620.0" @@ -197,14 +197,14 @@ "@smithy/util-utf8" "^3.0.0" tslib "^2.6.2" -"@aws-sdk/client-sso@3.624.0": - version "3.624.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.624.0.tgz#5d6bd308c1a6bb876c0bffc369c31a4916271219" - integrity sha512-EX6EF+rJzMPC5dcdsu40xSi2To7GSvdGQNIpe97pD9WvZwM9tRNQnNM4T6HA4gjV1L6Jwk8rBlG/CnveXtLEMw== +"@aws-sdk/client-sso@3.629.0": + version "3.629.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.629.0.tgz#19ad0236cf3985da68552dc597ed14736450630e" + integrity sha512-2w8xU4O0Grca5HmT2dXZ5fF0g39RxODtmoqHJDsK5DSt750LqDG4w3ktmBvQs3+SrpkkJOjlX5v/hb2PCxVbww== dependencies: "@aws-crypto/sha256-browser" "5.2.0" "@aws-crypto/sha256-js" "5.2.0" - "@aws-sdk/core" "3.624.0" + "@aws-sdk/core" "3.629.0" "@aws-sdk/middleware-host-header" "3.620.0" "@aws-sdk/middleware-logger" "3.609.0" "@aws-sdk/middleware-recursion-detection" "3.620.0" @@ -241,16 +241,16 @@ "@smithy/util-utf8" "^3.0.0" tslib "^2.6.2" -"@aws-sdk/client-sts@3.624.0": - version "3.624.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-sts/-/client-sts-3.624.0.tgz#ee19e08835a04d173f4997285e26558ac431d938" - integrity sha512-k36fLZCb2nfoV/DKK3jbRgO/Yf7/R80pgYfMiotkGjnZwDmRvNN08z4l06L9C+CieazzkgRxNUzyppsYcYsQaw== +"@aws-sdk/client-sts@3.629.0": + version "3.629.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sts/-/client-sts-3.629.0.tgz#a6ee546ebda64be90d310bb0a7316d98feabf1bd" + integrity sha512-RjOs371YwnSVGxhPjuluJKaxl4gcPYTAky0nPjwBime0i9/iS9nI8R8l5j7k7ec9tpFWjBPvNnThCU07pvjdzw== dependencies: "@aws-crypto/sha256-browser" "5.2.0" "@aws-crypto/sha256-js" "5.2.0" - "@aws-sdk/client-sso-oidc" "3.624.0" - "@aws-sdk/core" "3.624.0" - "@aws-sdk/credential-provider-node" "3.624.0" + "@aws-sdk/client-sso-oidc" "3.629.0" + "@aws-sdk/core" "3.629.0" + "@aws-sdk/credential-provider-node" "3.629.0" "@aws-sdk/middleware-host-header" "3.620.0" "@aws-sdk/middleware-logger" "3.609.0" "@aws-sdk/middleware-recursion-detection" "3.620.0" @@ -287,13 +287,14 @@ "@smithy/util-utf8" "^3.0.0" tslib "^2.6.2" -"@aws-sdk/core@3.624.0": - version "3.624.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.624.0.tgz#ff0d7745bd662f53d99dbb7293416a45ddde5aa6" - integrity sha512-WyFmPbhRIvtWi7hBp8uSFy+iPpj8ccNV/eX86hwF4irMjfc/FtsGVIAeBXxXM/vGCjkdfEzOnl+tJ2XACD4OXg== +"@aws-sdk/core@3.629.0": + version "3.629.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.629.0.tgz#1ed02c657edcd22ffdce9b3b5bdbd2a36fe899aa" + integrity sha512-+/ShPU/tyIBM3oY1cnjgNA/tFyHtlWq+wXF9xEKRv19NOpYbWQ+xzNwVjGq8vR07cCRqy/sDQLWPhxjtuV/FiQ== dependencies: "@smithy/core" "^2.3.2" "@smithy/node-config-provider" "^3.1.4" + "@smithy/property-provider" "^3.1.3" "@smithy/protocol-http" "^4.1.0" "@smithy/signature-v4" "^4.1.0" "@smithy/smithy-client" "^3.1.12" @@ -327,15 +328,15 @@ "@smithy/util-stream" "^3.1.3" tslib "^2.6.2" -"@aws-sdk/credential-provider-ini@3.624.0": - version "3.624.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.624.0.tgz#af4b485e9ceeca97e1681b2402c50dc192c1dc06" - integrity sha512-mMoNIy7MO2WTBbdqMyLpbt6SZpthE6e0GkRYpsd0yozPt0RZopcBhEh+HG1U9Y1PVODo+jcMk353vAi61CfnhQ== +"@aws-sdk/credential-provider-ini@3.629.0": + version "3.629.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.629.0.tgz#88a88ec752d8db388300143a37e70d96d6ea2cef" + integrity sha512-r9fI7BABARvVDp77DBUImQzYdvarAIdhbvpCEZib0rlpvfWu3zxE9KZcapCAAi0MPjxeDfb7RMehFQIkAP7mYw== dependencies: "@aws-sdk/credential-provider-env" "3.620.1" "@aws-sdk/credential-provider-http" "3.622.0" "@aws-sdk/credential-provider-process" "3.620.1" - "@aws-sdk/credential-provider-sso" "3.624.0" + "@aws-sdk/credential-provider-sso" "3.629.0" "@aws-sdk/credential-provider-web-identity" "3.621.0" "@aws-sdk/types" "3.609.0" "@smithy/credential-provider-imds" "^3.2.0" @@ -344,16 +345,16 @@ "@smithy/types" "^3.3.0" tslib "^2.6.2" -"@aws-sdk/credential-provider-node@3.624.0": - version "3.624.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.624.0.tgz#d7ebe191194eb09764b313c1f4e259cb42795079" - integrity sha512-vYyGK7oNpd81BdbH5IlmQ6zfaQqU+rPwsKTDDBeLRjshtrGXOEpfoahVpG9PX0ibu32IOWp4ZyXBNyVrnvcMOw== +"@aws-sdk/credential-provider-node@3.629.0": + version "3.629.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.629.0.tgz#4004ada7d3edbf0d28c710a5a5d42027dc34bfb2" + integrity sha512-868hnVOLlXOBHk91Rl0jZIRgr/M4WJCa0nOrW9A9yidsQxuZp9P0vshDmm4hMvNZadmPIfo0Rra2MpA4RELoCw== dependencies: "@aws-sdk/credential-provider-env" "3.620.1" "@aws-sdk/credential-provider-http" "3.622.0" - "@aws-sdk/credential-provider-ini" "3.624.0" + "@aws-sdk/credential-provider-ini" "3.629.0" "@aws-sdk/credential-provider-process" "3.620.1" - "@aws-sdk/credential-provider-sso" "3.624.0" + "@aws-sdk/credential-provider-sso" "3.629.0" "@aws-sdk/credential-provider-web-identity" "3.621.0" "@aws-sdk/types" "3.609.0" "@smithy/credential-provider-imds" "^3.2.0" @@ -373,12 +374,12 @@ "@smithy/types" "^3.3.0" tslib "^2.6.2" -"@aws-sdk/credential-provider-sso@3.624.0": - version "3.624.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.624.0.tgz#4a617577d04b23f80e86e1f76cc15dc3e9895c21" - integrity sha512-A02bayIjU9APEPKr3HudrFHEx0WfghoSPsPopckDkW7VBqO4wizzcxr75Q9A3vNX+cwg0wCN6UitTNe6pVlRaQ== +"@aws-sdk/credential-provider-sso@3.629.0": + version "3.629.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.629.0.tgz#f6c550d74007d1262149ae736df5868d4ea5aad7" + integrity sha512-Lf4XOuj6jamxgGZGrVojERh5S+NS2t2S4CUOnAu6tJ5U0GPlpjhINUKlcVxJBpsIXudMGW1nkumAd3+kazCPig== dependencies: - "@aws-sdk/client-sso" "3.624.0" + "@aws-sdk/client-sso" "3.629.0" "@aws-sdk/token-providers" "3.614.0" "@aws-sdk/types" "3.609.0" "@smithy/property-provider" "^3.1.3" @@ -471,12 +472,12 @@ "@smithy/types" "^3.3.0" tslib "^2.6.2" -"@aws-sdk/middleware-sdk-s3@3.624.0": - version "3.624.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.624.0.tgz#770cbc3f91b99ed70de6f7b9552917de99ee7e78" - integrity sha512-HUiaZ6+JXcG0qQda10ZxDGJvbT71YUp1zX+oikIsfTUeq0N75O82OY3Noqd7cyjEVtsGSo/y0e6U3aV1hO+wPw== +"@aws-sdk/middleware-sdk-s3@3.629.0": + version "3.629.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.629.0.tgz#10ad7b8af945f915d31f00cec0198248be95291c" + integrity sha512-FRXLcnPWXBoq/T9mnGnrpqhrSKNSm22rqJ0L7P14KESmbGuwhF/7ELYYxXIpgnIpb/CIUVmIU5EE8lsW1VTe8A== dependencies: - "@aws-sdk/core" "3.624.0" + "@aws-sdk/core" "3.629.0" "@aws-sdk/types" "3.609.0" "@aws-sdk/util-arn-parser" "3.568.0" "@smithy/core" "^2.3.2" @@ -523,12 +524,12 @@ "@smithy/util-middleware" "^3.0.3" tslib "^2.6.2" -"@aws-sdk/signature-v4-multi-region@3.624.0": - version "3.624.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.624.0.tgz#86829acc07871758a592dd50812ca4d370f641ed" - integrity sha512-gu1SfCyUPnq4s0AI1xdAl0whHwhkTyltg4QZWc4vnZvEVudCpJVVxEcroUHYQIO51YyVUT9jSMS1SVRe5VqPEw== +"@aws-sdk/signature-v4-multi-region@3.629.0": + version "3.629.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.629.0.tgz#ca75443f3324fd398d228c3cba0f4275e7bb4a3a" + integrity sha512-GPX6dnmuLGDFp7CsGqGCzleEoNyr9ekgOzSBtcL5nKX++NruxO7f1QzJAbcYvz0gdKvz958UO0EKsGM6hnkTSg== dependencies: - "@aws-sdk/middleware-sdk-s3" "3.624.0" + "@aws-sdk/middleware-sdk-s3" "3.629.0" "@aws-sdk/types" "3.609.0" "@smithy/protocol-http" "^4.1.0" "@smithy/signature-v4" "^4.1.0" @@ -546,7 +547,7 @@ "@smithy/types" "^3.3.0" tslib "^2.6.2" -"@aws-sdk/types@3.609.0": +"@aws-sdk/types@3.609.0", "@aws-sdk/types@^3.222.0": version "3.609.0" resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.609.0.tgz#06b39d799c9f197a7b43670243e8e78a3bf7d6a5" integrity sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q== @@ -554,14 +555,6 @@ "@smithy/types" "^3.3.0" tslib "^2.6.2" -"@aws-sdk/types@^3.222.0": - version "3.598.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.598.0.tgz#b840d2446dee19a2a4731e6166f2327915d846db" - integrity sha512-742uRl6z7u0LFmZwDrFP6r1wlZcgVPw+/TilluDJmCAR8BgRw3IR+743kUXKBGd8QZDRW2n6v/PYsi/AWCDDMQ== - dependencies: - "@smithy/types" "^3.1.0" - tslib "^2.6.2" - "@aws-sdk/util-arn-parser@3.568.0": version "3.568.0" resolved "https://registry.yarnpkg.com/@aws-sdk/util-arn-parser/-/util-arn-parser-3.568.0.tgz#6a19a8c6bbaa520b6be1c278b2b8c17875b91527" @@ -1046,9 +1039,9 @@ eslint-visitor-keys "^3.3.0" "@eslint-community/regexpp@^4.5.1", "@eslint-community/regexpp@^4.6.1": - version "4.10.1" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.1.tgz#361461e5cb3845d874e61731c11cfedd664d83a0" - integrity sha512-Zm2NGpWELsQAD1xsJzGQpYfvICSsFkEpU0jxBjfdC6uNEWXcHnfs9hScFWtXVDVl+rBQJGrl4g1vcKIejpH9dA== + version "4.11.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.0.tgz#b0ffd0312b4a3fd2d6f77237e7248a5ad3a680ae" + integrity sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A== "@eslint/eslintrc@^2.1.4": version "2.1.4" @@ -1463,20 +1456,20 @@ eslint-plugin-simple-import-sort "^10.0.0" eslint-plugin-unused-imports "^3.0.0" -"@gearbox-protocol/liquidator-v2-contracts@^2.1.0-next.17": - version "2.1.0-next.17" - resolved "https://registry.yarnpkg.com/@gearbox-protocol/liquidator-v2-contracts/-/liquidator-v2-contracts-2.1.0-next.17.tgz#6b6664c2bfacec11f05608fbe008aac64aff283f" - integrity sha512-SX/0uuMgFKVfahwel9hNwpnQ/1qmBhmxzodeTRqfApLdlrS4a81BFgl1er96UT7SKTZlBXBap7vQz+EPBi6lEQ== +"@gearbox-protocol/liquidator-v2-contracts@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@gearbox-protocol/liquidator-v2-contracts/-/liquidator-v2-contracts-2.1.0.tgz#64a7458eb0df1caf5688d243d5f678f43db09760" + integrity sha512-Xn8LvC2ddn1p1QsTNt1n+fpFfCvZuX84CK1+lKorgimNmnMMgLCWZXuX+dZRNjxA74lvhmco9Xcr1aab4wg/Aw== "@gearbox-protocol/prettier-config@2.0.0-next.0": version "2.0.0-next.0" resolved "https://registry.yarnpkg.com/@gearbox-protocol/prettier-config/-/prettier-config-2.0.0-next.0.tgz#8183cfa8c6ee538543089961ecb4d03fe77045de" integrity sha512-hDokre6TjEkpNdf+tTk/Gh2dTJpkJFgMPTpE7KS4KFddUqGLqDKMaE4/ZzBA8kvYNm5gSXytCwWrxPXO8kFKYA== -"@gearbox-protocol/sdk-gov@^2.14.1": - version "2.14.1" - resolved "https://registry.yarnpkg.com/@gearbox-protocol/sdk-gov/-/sdk-gov-2.14.1.tgz#82a9ca01bd7167c9774231f6af80efe92c6984a1" - integrity sha512-3dyN8uWqb6YZNYmbHbQ+8acH3HD6kEurjnSd8g07Bgfk9kRzHkzlzTuAP3fY7An3kiZA9t27jpqgxgbc0nejag== +"@gearbox-protocol/sdk-gov@^2.15.0": + version "2.15.0" + resolved "https://registry.yarnpkg.com/@gearbox-protocol/sdk-gov/-/sdk-gov-2.15.0.tgz#3ed7da0154b170fa5360f4dbb24fd8c98a67443b" + integrity sha512-AfSgKf+tSkpNT2a8f+yWX14pfXQXg82BU9aJcW2mTEEAT7b1CJg3jl8DQLneO2y3YtqnyGlQpv2m4pYtrUqAaQ== dependencies: ethers "6.12.1" humanize-duration-ts "^2.1.1" @@ -1525,16 +1518,11 @@ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== -"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== -"@jridgewell/sourcemap-codec@^1.4.15": - version "1.4.15" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" - integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== - "@jridgewell/trace-mapping@^0.3.24": version "0.3.25" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" @@ -1674,85 +1662,85 @@ ethers "^5.7.2" zod "^3.22.4" -"@rollup/rollup-android-arm-eabi@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz#bbd0e616b2078cd2d68afc9824d1fadb2f2ffd27" - integrity sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ== - -"@rollup/rollup-android-arm64@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz#97255ef6384c5f73f4800c0de91f5f6518e21203" - integrity sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA== - -"@rollup/rollup-darwin-arm64@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz#b6dd74e117510dfe94541646067b0545b42ff096" - integrity sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w== - -"@rollup/rollup-darwin-x64@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz#e07d76de1cec987673e7f3d48ccb8e106d42c05c" - integrity sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA== - -"@rollup/rollup-linux-arm-gnueabihf@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz#9f1a6d218b560c9d75185af4b8bb42f9f24736b8" - integrity sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA== - -"@rollup/rollup-linux-arm-musleabihf@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz#53618b92e6ffb642c7b620e6e528446511330549" - integrity sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A== - -"@rollup/rollup-linux-arm64-gnu@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz#99a7ba5e719d4f053761a698f7b52291cefba577" - integrity sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw== - -"@rollup/rollup-linux-arm64-musl@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz#f53db99a45d9bc00ce94db8a35efa7c3c144a58c" - integrity sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ== - -"@rollup/rollup-linux-powerpc64le-gnu@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz#cbb0837408fe081ce3435cf3730e090febafc9bf" - integrity sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA== - -"@rollup/rollup-linux-riscv64-gnu@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz#8ed09c1d1262ada4c38d791a28ae0fea28b80cc9" - integrity sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg== - -"@rollup/rollup-linux-s390x-gnu@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz#938138d3c8e0c96f022252a28441dcfb17afd7ec" - integrity sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg== - -"@rollup/rollup-linux-x64-gnu@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz#1a7481137a54740bee1ded4ae5752450f155d942" - integrity sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w== - -"@rollup/rollup-linux-x64-musl@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz#f1186afc601ac4f4fc25fac4ca15ecbee3a1874d" - integrity sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg== - -"@rollup/rollup-win32-arm64-msvc@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz#ed6603e93636a96203c6915be4117245c1bd2daf" - integrity sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA== - -"@rollup/rollup-win32-ia32-msvc@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz#14e0b404b1c25ebe6157a15edb9c46959ba74c54" - integrity sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg== - -"@rollup/rollup-win32-x64-msvc@4.18.0": - version "4.18.0" - resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz#5d694d345ce36b6ecf657349e03eb87297e68da4" - integrity sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g== +"@rollup/rollup-android-arm-eabi@4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.19.1.tgz#7746deb85e4a8fb54fbfda8ac5c102692f102476" + integrity sha512-XzqSg714++M+FXhHfXpS1tDnNZNpgxxuGZWlRG/jSj+VEPmZ0yg6jV4E0AL3uyBKxO8mO3xtOsP5mQ+XLfrlww== + +"@rollup/rollup-android-arm64@4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.19.1.tgz#93de4d867709d3313794723b5afd91e1e174f906" + integrity sha512-thFUbkHteM20BGShD6P08aungq4irbIZKUNbG70LN8RkO7YztcGPiKTTGZS7Kw+x5h8hOXs0i4OaHwFxlpQN6A== + +"@rollup/rollup-darwin-arm64@4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.19.1.tgz#e41e6a81673260ab196e0f59462b9940a6ac03cd" + integrity sha512-8o6eqeFZzVLia2hKPUZk4jdE3zW7LCcZr+MD18tXkgBBid3lssGVAYuox8x6YHoEPDdDa9ixTaStcmx88lio5Q== + +"@rollup/rollup-darwin-x64@4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.19.1.tgz#2b0a0aef6e8c5317d494cfc9076d7a16b099bdcb" + integrity sha512-4T42heKsnbjkn7ovYiAdDVRRWZLU9Kmhdt6HafZxFcUdpjlBlxj4wDrt1yFWLk7G4+E+8p2C9tcmSu0KA6auGA== + +"@rollup/rollup-linux-arm-gnueabihf@4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.19.1.tgz#e22319deb5367384ef315e66bc6de80d2bf2b3ae" + integrity sha512-MXg1xp+e5GhZ3Vit1gGEyoC+dyQUBy2JgVQ+3hUrD9wZMkUw/ywgkpK7oZgnB6kPpGrxJ41clkPPnsknuD6M2Q== + +"@rollup/rollup-linux-arm-musleabihf@4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.19.1.tgz#d5dd68f5d7ae21b345a5c87208c94e5c813f54b8" + integrity sha512-DZNLwIY4ftPSRVkJEaxYkq7u2zel7aah57HESuNkUnz+3bZHxwkCUkrfS2IWC1sxK6F2QNIR0Qr/YXw7nkF3Pw== + +"@rollup/rollup-linux-arm64-gnu@4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.19.1.tgz#1703d3a418d33f8f025acaf93f39ca1efcd5b645" + integrity sha512-C7evongnjyxdngSDRRSQv5GvyfISizgtk9RM+z2biV5kY6S/NF/wta7K+DanmktC5DkuaJQgoKGf7KUDmA7RUw== + +"@rollup/rollup-linux-arm64-musl@4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.19.1.tgz#3f59c2c6e60f75ce8b1090bd841c555e3bb01f0e" + integrity sha512-89tFWqxfxLLHkAthAcrTs9etAoBFRduNfWdl2xUs/yLV+7XDrJ5yuXMHptNqf1Zw0UCA3cAutkAiAokYCkaPtw== + +"@rollup/rollup-linux-powerpc64le-gnu@4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.19.1.tgz#3f99a0921596a6f539121a312df29af52a205f15" + integrity sha512-PromGeV50sq+YfaisG8W3fd+Cl6mnOOiNv2qKKqKCpiiEke2KiKVyDqG/Mb9GWKbYMHj5a01fq/qlUR28PFhCQ== + +"@rollup/rollup-linux-riscv64-gnu@4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.19.1.tgz#c08fb3e629d50d2eac31329347cfc559a1cf81d1" + integrity sha512-/1BmHYh+iz0cNCP0oHCuF8CSiNj0JOGf0jRlSo3L/FAyZyG2rGBuKpkZVH9YF+x58r1jgWxvm1aRg3DHrLDt6A== + +"@rollup/rollup-linux-s390x-gnu@4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.19.1.tgz#173722cd745779d730d4b24d21386185e0e12de8" + integrity sha512-0cYP5rGkQWRZKy9/HtsWVStLXzCF3cCBTRI+qRL8Z+wkYlqN7zrSYm6FuY5Kd5ysS5aH0q5lVgb/WbG4jqXN1Q== + +"@rollup/rollup-linux-x64-gnu@4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.19.1.tgz#0af2b6541ab0f4954d2c4f96bcdc7947420dd28c" + integrity sha512-XUXeI9eM8rMP8aGvii/aOOiMvTs7xlCosq9xCjcqI9+5hBxtjDpD+7Abm1ZhVIFE1J2h2VIg0t2DX/gjespC2Q== + +"@rollup/rollup-linux-x64-musl@4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.19.1.tgz#f973f9552744764b221128f7c3629222216ace69" + integrity sha512-V7cBw/cKXMfEVhpSvVZhC+iGifD6U1zJ4tbibjjN+Xi3blSXaj/rJynAkCFFQfoG6VZrAiP7uGVzL440Q6Me2Q== + +"@rollup/rollup-win32-arm64-msvc@4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.19.1.tgz#21ac5ed84d914bc31821fec3dd909f7257cfb17b" + integrity sha512-88brja2vldW/76jWATlBqHEoGjJLRnP0WOEKAUbMcXaAZnemNhlAHSyj4jIwMoP2T750LE9lblvD4e2jXleZsA== + +"@rollup/rollup-win32-ia32-msvc@4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.19.1.tgz#0cfe740063b35dcd5a62c4e243226631a846ce11" + integrity sha512-LdxxcqRVSXi6k6JUrTah1rHuaupoeuiv38du8Mt4r4IPer3kwlTo+RuvfE8KzZ/tL6BhaPlzJ3835i6CxrFIRQ== + +"@rollup/rollup-win32-x64-msvc@4.19.1": + version "4.19.1" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.19.1.tgz#5f2c40d3f1b53ede80fb4e6964f840c0f8936832" + integrity sha512-2bIrL28PcK3YCqD9anGxDxamxdiJAxA+l7fWIwM5o8UqNy1t3d1NdAweO2XhA0KTDJ5aH1FsuiT5+7VhtHliXg== "@scure/base@~1.1.6": version "1.1.7" @@ -1845,12 +1833,12 @@ "@smithy/util-hex-encoding" "^3.0.0" tslib "^2.6.2" -"@smithy/eventstream-serde-browser@^3.0.5": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.5.tgz#3e971afd2b8a02a098af8decc4b9e3f35296d6a2" - integrity sha512-dEyiUYL/ekDfk+2Ra4GxV+xNnFoCmk1nuIXg+fMChFTrM2uI/1r9AdiTYzPqgb72yIv/NtAj6C3dG//1wwgakQ== +"@smithy/eventstream-serde-browser@^3.0.6": + version "3.0.6" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.6.tgz#a4ab4f7cfbd137bcaa54c375276f9214e568fd8f" + integrity sha512-2hM54UWQUOrki4BtsUI1WzmD13/SeaqT/AB3EUJKbcver/WgKNaiJ5y5F5XXuVe6UekffVzuUDrBZVAA3AWRpQ== dependencies: - "@smithy/eventstream-serde-universal" "^3.0.4" + "@smithy/eventstream-serde-universal" "^3.0.5" "@smithy/types" "^3.3.0" tslib "^2.6.2" @@ -1862,19 +1850,19 @@ "@smithy/types" "^3.3.0" tslib "^2.6.2" -"@smithy/eventstream-serde-node@^3.0.4": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.4.tgz#6301752ca51b3ebabcd2dec112f1dacd990de4c1" - integrity sha512-mjlG0OzGAYuUpdUpflfb9zyLrBGgmQmrobNT8b42ZTsGv/J03+t24uhhtVEKG/b2jFtPIHF74Bq+VUtbzEKOKg== +"@smithy/eventstream-serde-node@^3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.5.tgz#2bbf5c9312a28f23bc55ae284efa9499f8b8f982" + integrity sha512-+upXvnHNyZP095s11jF5dhGw/Ihzqwl5G+/KtMnoQOpdfC3B5HYCcDVG9EmgkhJMXJlM64PyN5gjJl0uXFQehQ== dependencies: - "@smithy/eventstream-serde-universal" "^3.0.4" + "@smithy/eventstream-serde-universal" "^3.0.5" "@smithy/types" "^3.3.0" tslib "^2.6.2" -"@smithy/eventstream-serde-universal@^3.0.4": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.4.tgz#6754de5b94bdc286d8ef1d6bcf22d80f6ab68f30" - integrity sha512-Od9dv8zh3PgOD7Vj4T3HSuox16n0VG8jJIM2gvKASL6aCtcS8CfHZDWe1Ik3ZXW6xBouU+45Q5wgoliWDZiJ0A== +"@smithy/eventstream-serde-universal@^3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.5.tgz#e1cc2f71f4d174a03e00ce4b563395a81dd17bec" + integrity sha512-5u/nXbyoh1s4QxrvNre9V6vfyoLWuiVvvd5TlZjGThIikc3G+uNiG9uOTCWweSRjv1asdDIWK7nOmN7le4RYHQ== dependencies: "@smithy/eventstream-codec" "^3.1.2" "@smithy/types" "^3.3.0" @@ -2099,13 +2087,6 @@ "@smithy/util-stream" "^3.1.3" tslib "^2.6.2" -"@smithy/types@^3.1.0": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@smithy/types/-/types-3.2.0.tgz#1350fe8a50d5e35e12ffb34be46d946860b2b5ab" - integrity sha512-cKyeKAPazZRVqm7QPvcPD2jEIt2wqDPAL1KJKb0f/5I7uhollvsWZuZKLclmyP6a+Jwmr3OV3t+X0pZUUHS9BA== - dependencies: - tslib "^2.6.2" - "@smithy/types@^3.3.0": version "3.3.0" resolved "https://registry.yarnpkg.com/@smithy/types/-/types-3.3.0.tgz#fae037c733d09bc758946a01a3de0ef6e210b16b" @@ -2294,26 +2275,26 @@ integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== "@types/lodash@^4.14.195": - version "4.17.5" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.5.tgz#e6c29b58e66995d57cd170ce3e2a61926d55ee04" - integrity sha512-MBIOHVZqVqgfro1euRDWX7OO0fBVUUMrN6Pwm8LQsz8cWhEpihlvR70ENj3f40j58TNxZaWv2ndSkInykNBBJw== + version "4.17.7" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.7.tgz#2f776bcb53adc9e13b2c0dfd493dfcbd7de43612" + integrity sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA== "@types/node@*": - version "20.14.8" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.8.tgz#45c26a2a5de26c3534a9504530ddb3b27ce031ac" - integrity sha512-DO+2/jZinXfROG7j7WKFn/3C6nFwxy2lLpgLjEXJz+0XKphZlTLJ14mo8Vfg8X5BWN6XjyESXq+LcYdT7tR3bA== + version "22.0.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.0.0.tgz#04862a2a71e62264426083abe1e27e87cac05a30" + integrity sha512-VT7KSYudcPOzP5Q0wfbowyNLaVR8QWUdw+088uFWwfvpY6uCWaXpqV6ieLAu9WBcnTa7H4Z5RLK8I5t2FuOcqw== dependencies: - undici-types "~5.26.4" + undici-types "~6.11.1" "@types/node@18.15.13": version "18.15.13" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== -"@types/node@^22.1.0": - version "22.1.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.1.0.tgz#6d6adc648b5e03f0e83c78dc788c2b037d0ad94b" - integrity sha512-AOmuRF0R2/5j1knA3c6G3HOk523Ga+l+ZXltX8SF1+5oqcXijjfTd8fY3XRZqSihEu9XhtQnKYLmkFaoxgsJHw== +"@types/node@^22.2.0": + version "22.2.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.2.0.tgz#7cf046a99f0ba4d628ad3088cb21f790df9b0c5b" + integrity sha512-bm6EG6/pCpkxDf/0gDNDdtDILMOHgaQBVOJGdwsqClnxA3xL6jtMv76rLBc006RVMWbmaf0xbmom4Z/5o2nRkQ== dependencies: undici-types "~6.13.0" @@ -2581,9 +2562,9 @@ acorn-jsx@^5.3.2: integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== acorn@^8.9.0: - version "8.12.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.0.tgz#1627bfa2e058148036133b8d9b51a700663c294c" - integrity sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw== + version "8.12.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" + integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== aes-js@3.0.0: version "3.0.0" @@ -2606,14 +2587,14 @@ ajv@^6.12.4: uri-js "^4.2.2" ajv@^8.11.0: - version "8.16.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.16.0.tgz#22e2a92b94f005f7e0f9c9d39652ef0b8f6f0cb4" - integrity sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw== + version "8.17.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" + integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== dependencies: fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" json-schema-traverse "^1.0.0" require-from-string "^2.0.2" - uri-js "^4.4.1" ansi-escapes@^7.0.0: version "7.0.0" @@ -2738,16 +2719,6 @@ array.prototype.flatmap@^1.3.2: es-abstract "^1.22.1" es-shim-unscopables "^1.0.0" -array.prototype.toreversed@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz#b989a6bf35c4c5051e1dc0325151bf8088954eba" - integrity sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.22.1" - es-shim-unscopables "^1.0.0" - array.prototype.tosorted@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz#fe954678ff53034e717ea3352a03f0b0b86f7ffc" @@ -2932,9 +2903,9 @@ callsites@^3.0.0: integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== chai@^4.3.4: - version "4.4.1" - resolved "https://registry.yarnpkg.com/chai/-/chai-4.4.1.tgz#3603fa6eba35425b0f2ac91a009fe924106e50d1" - integrity sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g== + version "4.5.0" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.5.0.tgz#707e49923afdd9b13a8b0b47d33d732d13812fd8" + integrity sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw== dependencies: assertion-error "^1.1.0" check-error "^1.0.3" @@ -2942,7 +2913,18 @@ chai@^4.3.4: get-func-name "^2.0.2" loupe "^2.3.6" pathval "^1.1.1" - type-detect "^4.0.8" + type-detect "^4.1.0" + +chai@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/chai/-/chai-5.1.1.tgz#f035d9792a22b481ead1c65908d14bb62ec1c82c" + integrity sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA== + dependencies: + assertion-error "^2.0.1" + check-error "^2.1.1" + deep-eql "^5.0.1" + loupe "^3.1.0" + pathval "^2.0.0" chai@^5.1.1: version "5.1.1" @@ -3186,10 +3168,10 @@ debug@^3.2.7: dependencies: ms "^2.1.1" -debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5: - version "4.3.5" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" - integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== +debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5, debug@~4.3.6: + version "4.3.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b" + integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg== dependencies: ms "2.1.2" @@ -3236,7 +3218,7 @@ define-data-property@^1.0.1, define-data-property@^1.1.4: es-errors "^1.3.0" gopd "^1.0.1" -define-properties@^1.2.0, define-properties@^1.2.1: +define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== @@ -3324,9 +3306,9 @@ end-of-stream@^1.1.0: once "^1.4.0" enhanced-resolve@^5.12.0: - version "5.17.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz#d037603789dd9555b89aaec7eb78845c49089bc5" - integrity sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA== + version "5.17.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15" + integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -3348,7 +3330,7 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.1, es-abstract@^1.23.2, es-abstract@^1.23.3: +es-abstract@^1.17.5, es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.1, es-abstract@^1.23.2, es-abstract@^1.23.3: version "1.23.3" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.3.tgz#8f0c5a35cd215312573c5a27c87dfd6c881a0aa0" integrity sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A== @@ -3464,7 +3446,7 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -esbuild@^0.21.3, esbuild@~0.21.5: +esbuild@^0.21.3: version "0.21.5" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d" integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw== @@ -3493,7 +3475,7 @@ esbuild@^0.21.3, esbuild@~0.21.5: "@esbuild/win32-ia32" "0.21.5" "@esbuild/win32-x64" "0.21.5" -esbuild@^0.23.0: +esbuild@^0.23.0, esbuild@~0.23.0: version "0.23.0" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.23.0.tgz#de06002d48424d9fdb7eb52dbe8e95927f852599" integrity sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA== @@ -3601,28 +3583,28 @@ eslint-plugin-react-hooks@^4.6.0: integrity sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ== eslint-plugin-react@^7.33.2: - version "7.34.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.34.3.tgz#9965f27bd1250a787b5d4cfcc765e5a5d58dcb7b" - integrity sha512-aoW4MV891jkUulwDApQbPYTVZmeuSyFrudpbTAQuj5Fv8VL+o6df2xIGpw8B0hPjAaih1/Fb0om9grCdyFYemA== + version "7.35.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.35.0.tgz#00b1e4559896710e58af6358898f2ff917ea4c41" + integrity sha512-v501SSMOWv8gerHkk+IIQBkcGRGrO2nfybfj5pLxuJNFTPxxA3PSryhXTK+9pNbtkggheDdsC0E9Q8CuPk6JKA== dependencies: array-includes "^3.1.8" array.prototype.findlast "^1.2.5" array.prototype.flatmap "^1.3.2" - array.prototype.toreversed "^1.1.2" array.prototype.tosorted "^1.1.4" doctrine "^2.1.0" es-iterator-helpers "^1.0.19" estraverse "^5.3.0" + hasown "^2.0.2" jsx-ast-utils "^2.4.1 || ^3.0.0" minimatch "^3.1.2" object.entries "^1.1.8" object.fromentries "^2.0.8" - object.hasown "^1.1.4" object.values "^1.2.0" prop-types "^15.8.1" resolve "^2.0.0-next.5" semver "^6.3.1" string.prototype.matchall "^4.0.11" + string.prototype.repeat "^1.0.0" eslint-plugin-simple-import-sort@^10.0.0: version "10.0.0" @@ -3708,9 +3690,9 @@ espree@^9.6.0, espree@^9.6.1: eslint-visitor-keys "^3.4.1" esquery@^1.4.2: - version "1.5.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" - integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== + version "1.6.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" + integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== dependencies: estraverse "^5.1.0" @@ -3871,6 +3853,11 @@ fast-safe-stringify@^2.1.1: resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== +fast-uri@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.1.tgz#cddd2eecfc83a71c1be2cc2ef2061331be8a7134" + integrity sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw== + fast-xml-parser@4.4.1: version "4.4.1" resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz#86dbf3f18edf8739326447bcaac31b4ae7f6514f" @@ -3966,7 +3953,7 @@ function-bind@^1.1.2: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== -function.prototype.name@^1.1.5, function.prototype.name@^1.1.6: +function.prototype.name@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== @@ -4022,9 +4009,9 @@ get-symbol-description@^1.0.2: get-intrinsic "^1.2.4" get-tsconfig@^4.5.0, get-tsconfig@^4.7.5: - version "4.7.5" - resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.7.5.tgz#5e012498579e9a6947511ed0cd403272c7acbbaf" - integrity sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw== + version "4.7.6" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.7.6.tgz#118fd5b7b9bae234cc7705a00cd771d7eb65d62a" + integrity sha512-ZAqrLlu18NbDdRaHq+AKXzAmqIUPswPWKUchfytdAjiRFnCe5ojG2bstg6mRiZabkKfCoL/e98pbBELIV/YCeA== dependencies: resolve-pkg-maps "^1.0.0" @@ -4307,9 +4294,9 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== is-core-module@^2.11.0, is-core-module@^2.13.0, is-core-module@^2.13.1: - version "2.14.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.14.0.tgz#43b8ef9f46a6a08888db67b1ffd4ec9e3dfd59d1" - integrity sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A== + version "2.15.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.0.tgz#71c72ec5442ace7e76b306e9d48db361f22699ea" + integrity sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA== dependencies: hasown "^2.0.2" @@ -4610,10 +4597,10 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== -lint-staged@^15.2.8: - version "15.2.8" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-15.2.8.tgz#5e19eb7b4dbb922f56fafb4635b44ee3c92f7322" - integrity sha512-PUWFf2zQzsd9EFU+kM1d7UP+AZDbKFKuj+9JNVTBkhUFhbg4MAt6WfyMMwBfM4lYqd4D2Jwac5iuTu9rVj4zCQ== +lint-staged@^15.2.9: + version "15.2.9" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-15.2.9.tgz#bf70d40b6b192df6ad756fb89822211615e0f4da" + integrity sha512-BZAt8Lk3sEnxw7tfxM7jeZlPRuT4M68O0/CwZhhaw6eeWu0Lz5eERE3m386InivXB64fp/mDID452h48tvKlRQ== dependencies: chalk "~5.3.0" commander "~12.1.0" @@ -4735,11 +4722,11 @@ loupe@^3.1.0, loupe@^3.1.1: get-func-name "^2.0.1" magic-string@^0.30.10: - version "0.30.10" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.10.tgz#123d9c41a0cb5640c892b041d4cfb3bd0aa4b39e" - integrity sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ== + version "0.30.11" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.11.tgz#301a6f93b3e8c2cb13ac1a7a673492c0dfd12954" + integrity sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A== dependencies: - "@jridgewell/sourcemap-codec" "^1.4.15" + "@jridgewell/sourcemap-codec" "^1.5.0" meow@^12.0.1: version "12.1.1" @@ -4917,15 +4904,6 @@ object.groupby@^1.0.1: define-properties "^1.2.1" es-abstract "^1.23.2" -object.hasown@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.4.tgz#e270ae377e4c120cdcb7656ce66884a6218283dc" - integrity sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg== - dependencies: - define-properties "^1.2.1" - es-abstract "^1.23.2" - es-object-atoms "^1.0.0" - object.values@^1.1.6, object.values@^1.1.7, object.values@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.0.tgz#65405a9d92cee68ac2d303002e0b8470a4d9ab1b" @@ -5068,7 +5046,7 @@ pathval@^2.0.0: resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.0.tgz#7e2550b422601d4f6b8e26f1301bc8f15a741a25" integrity sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA== -picocolors@^1.0.0: +picocolors@^1.0.0, picocolors@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.1.tgz#a8ad579b571952f0e5d25892de5445bcfe25aaa1" integrity sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew== @@ -5138,13 +5116,13 @@ possible-typed-array-names@^1.0.0: resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== -postcss@^8.4.38: - version "8.4.38" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.38.tgz#b387d533baf2054288e337066d81c6bee9db9e0e" - integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A== +postcss@^8.4.39: + version "8.4.40" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.40.tgz#eb81f2a4dd7668ed869a6db25999e02e9ad909d8" + integrity sha512-YF2kKIUzAofPMpfH6hOi2cGnv/HrUlfucspc7pDyvv7kGdqXrfj8SCl/t8owkEgKEuu8ZcRjSOxFxVLqwChZ2Q== dependencies: nanoid "^3.3.7" - picocolors "^1.0.0" + picocolors "^1.0.1" source-map-js "^1.2.0" prelude-ls@^1.2.1: @@ -5331,28 +5309,28 @@ rimraf@^3.0.2: glob "^7.1.3" rollup@^4.13.0: - version "4.18.0" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.18.0.tgz#497f60f0c5308e4602cf41136339fbf87d5f5dda" - integrity sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg== + version "4.19.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.19.1.tgz#21d865cd60d4a325172ce8b082e60caccd97b309" + integrity sha512-K5vziVlg7hTpYfFBI+91zHBEMo6jafYXpkMlqZjg7/zhIG9iHqazBf4xz9AVdjS9BruRn280ROqLI7G3OFRIlw== dependencies: "@types/estree" "1.0.5" optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.18.0" - "@rollup/rollup-android-arm64" "4.18.0" - "@rollup/rollup-darwin-arm64" "4.18.0" - "@rollup/rollup-darwin-x64" "4.18.0" - "@rollup/rollup-linux-arm-gnueabihf" "4.18.0" - "@rollup/rollup-linux-arm-musleabihf" "4.18.0" - "@rollup/rollup-linux-arm64-gnu" "4.18.0" - "@rollup/rollup-linux-arm64-musl" "4.18.0" - "@rollup/rollup-linux-powerpc64le-gnu" "4.18.0" - "@rollup/rollup-linux-riscv64-gnu" "4.18.0" - "@rollup/rollup-linux-s390x-gnu" "4.18.0" - "@rollup/rollup-linux-x64-gnu" "4.18.0" - "@rollup/rollup-linux-x64-musl" "4.18.0" - "@rollup/rollup-win32-arm64-msvc" "4.18.0" - "@rollup/rollup-win32-ia32-msvc" "4.18.0" - "@rollup/rollup-win32-x64-msvc" "4.18.0" + "@rollup/rollup-android-arm-eabi" "4.19.1" + "@rollup/rollup-android-arm64" "4.19.1" + "@rollup/rollup-darwin-arm64" "4.19.1" + "@rollup/rollup-darwin-x64" "4.19.1" + "@rollup/rollup-linux-arm-gnueabihf" "4.19.1" + "@rollup/rollup-linux-arm-musleabihf" "4.19.1" + "@rollup/rollup-linux-arm64-gnu" "4.19.1" + "@rollup/rollup-linux-arm64-musl" "4.19.1" + "@rollup/rollup-linux-powerpc64le-gnu" "4.19.1" + "@rollup/rollup-linux-riscv64-gnu" "4.19.1" + "@rollup/rollup-linux-s390x-gnu" "4.19.1" + "@rollup/rollup-linux-x64-gnu" "4.19.1" + "@rollup/rollup-linux-x64-musl" "4.19.1" + "@rollup/rollup-win32-arm64-msvc" "4.19.1" + "@rollup/rollup-win32-ia32-msvc" "4.19.1" + "@rollup/rollup-win32-x64-msvc" "4.19.1" fsevents "~2.3.2" run-parallel@^1.1.9: @@ -5407,9 +5385,9 @@ semver@^6.3.1: integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== semver@^7.5.4, semver@^7.6.0: - version "7.6.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" - integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== set-function-length@^1.2.1: version "1.2.2" @@ -5528,9 +5506,9 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: strip-ansi "^6.0.1" string-width@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-7.1.0.tgz#d994252935224729ea3719c49f7206dc9c46550a" - integrity sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw== + version "7.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-7.2.0.tgz#b5bb8e2165ce275d4d43476dd2700ad9091db6dc" + integrity sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ== dependencies: emoji-regex "^10.3.0" get-east-asian-width "^1.0.0" @@ -5554,6 +5532,14 @@ string.prototype.matchall@^4.0.11: set-function-name "^2.0.2" side-channel "^1.0.6" +string.prototype.repeat@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz#e90872ee0308b29435aa26275f6e1b762daee01a" + integrity sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + string.prototype.trim@^1.2.9: version "1.2.9" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz#b6fa326d72d2c78b6df02f7759c73f8f6274faa4" @@ -5736,12 +5722,12 @@ tslib@^2.6.2: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== -tsx@^4.16.5: - version "4.16.5" - resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.16.5.tgz#49c2a8f4d4d66bd7cf538e23e7368a1919a9a1ca" - integrity sha512-ArsiAQHEW2iGaqZ8fTA1nX0a+lN5mNTyuGRRO6OW3H/Yno1y9/t1f9YOI1Cfoqz63VAthn++ZYcbDP7jPflc+A== +tsx@^4.17.0: + version "4.17.0" + resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.17.0.tgz#6ffd9851a0c7aa4ecacf4dc19f28d82112af25c5" + integrity sha512-eN4mnDA5UMKDt4YZixo9tBioibaMBpoxBkD+rIPAjVmYERSG0/dWEY1CEFuV89CgASlKL499q8AhmkMnnjtOJg== dependencies: - esbuild "~0.21.5" + esbuild "~0.23.0" get-tsconfig "^4.7.5" optionalDependencies: fsevents "~2.3.3" @@ -5753,10 +5739,10 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" -type-detect@^4.0.0, type-detect@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" - integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== +type-detect@^4.0.0, type-detect@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.1.0.tgz#deb2453e8f08dcae7ae98c626b13dddb0155906c" + integrity sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw== type-fest@^0.20.2: version "0.20.2" @@ -5822,10 +5808,15 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" -undici-types@~5.26.4: - version "5.26.5" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" - integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== +undici-types@~6.11.1: + version "6.11.1" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.11.1.tgz#432ea6e8efd54a48569705a699e62d8f4981b197" + integrity sha512-mIDEX2ek50x0OlRgxryxsenE5XaQD4on5U2inY7RApK3SOJpofyw7uW2AyfMKkhAxXIceo2DeWGVGwyvng1GNQ== + +undici-types@~6.13.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.13.0.tgz#e3e79220ab8c81ed1496b5812471afd7cf075ea5" + integrity sha512-xtFJHudx8S2DSoujjMd1WeWvn7KKWFRESZTMeL1RptAYERu29D6jphMjjY+vn96jvN3kVPDNxU/E13VTaXj6jg== undici-types@~6.13.0: version "6.13.0" @@ -5837,7 +5828,7 @@ unicorn-magic@^0.1.0: resolved "https://registry.yarnpkg.com/unicorn-magic/-/unicorn-magic-0.1.0.tgz#1bb9a51c823aaf9d73a8bfcd3d1a23dde94b0ce4" integrity sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ== -uri-js@^4.2.2, uri-js@^4.4.1: +uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== @@ -5849,10 +5840,10 @@ uuid@^9.0.1: resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== -viem@^2.19.1: - version "2.19.1" - resolved "https://registry.yarnpkg.com/viem/-/viem-2.19.1.tgz#38ccffbacf69d8a2f940ff35be8ea3ac42ea5d61" - integrity sha512-a0ca/ACEz3FRZB3OmiSfRUogWZGQh700wu7Pg3GmAWiGD+0PS9bVaWG67JQ+9azFZLq0BU/m0t2CeWd3xi8IzQ== +viem@^2.19.4: + version "2.19.4" + resolved "https://registry.yarnpkg.com/viem/-/viem-2.19.4.tgz#129a6dfbaf81bfc5664fde62c6a77cdbdebeeff9" + integrity sha512-JdhK3ui3uPD2tnpqGNkJaDQV4zTfOeKXcF+VrU8RG88Dn2e0lFjv6l7m0YNmYLsHm+n5vFFfCLcUrTk6xcYv5w== dependencies: "@adraffy/ens-normalize" "1.10.0" "@noble/curves" "1.4.0" @@ -5876,12 +5867,12 @@ vite-node@2.0.5: vite "^5.0.0" vite@^5.0.0: - version "5.3.1" - resolved "https://registry.yarnpkg.com/vite/-/vite-5.3.1.tgz#bb2ca6b5fd7483249d3e86b25026e27ba8a663e6" - integrity sha512-XBmSKRLXLxiaPYamLv3/hnP/KXDai1NDexN0FpkTaZXTfycHvkRHoenpgl/fvuK/kPbB6xAgoyiryAhQNxYmAQ== + version "5.3.5" + resolved "https://registry.yarnpkg.com/vite/-/vite-5.3.5.tgz#b847f846fb2b6cb6f6f4ed50a830186138cb83d8" + integrity sha512-MdjglKR6AQXQb9JGiS7Rc2wC6uMjcm7Go/NHNO63EwiJXfuk9PgqiP/n5IDJCziMkfw9n4Ubp7lttNwz+8ZVKA== dependencies: esbuild "^0.21.3" - postcss "^8.4.38" + postcss "^8.4.39" rollup "^4.13.0" optionalDependencies: fsevents "~2.3.3" @@ -5931,12 +5922,12 @@ which-boxed-primitive@^1.0.2: is-symbol "^1.0.3" which-builtin-type@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.1.3.tgz#b1b8443707cc58b6e9bf98d32110ff0c2cbd029b" - integrity sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw== + version "1.1.4" + resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.1.4.tgz#592796260602fc3514a1b5ee7fa29319b72380c3" + integrity sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w== dependencies: - function.prototype.name "^1.1.5" - has-tostringtag "^1.0.0" + function.prototype.name "^1.1.6" + has-tostringtag "^1.0.2" is-async-function "^2.0.0" is-date-object "^1.0.5" is-finalizationregistry "^1.0.2" @@ -5945,10 +5936,10 @@ which-builtin-type@^1.1.3: is-weakref "^1.0.2" isarray "^2.0.5" which-boxed-primitive "^1.0.2" - which-collection "^1.0.1" - which-typed-array "^1.1.9" + which-collection "^1.0.2" + which-typed-array "^1.1.15" -which-collection@^1.0.1: +which-collection@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.2.tgz#627ef76243920a107e7ce8e96191debe4b16c2a0" integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw== @@ -5958,7 +5949,7 @@ which-collection@^1.0.1: is-weakmap "^2.0.2" is-weakset "^2.0.3" -which-typed-array@^1.1.14, which-typed-array@^1.1.15, which-typed-array@^1.1.9: +which-typed-array@^1.1.14, which-typed-array@^1.1.15: version "1.1.15" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== @@ -6061,9 +6052,9 @@ yocto-queue@^0.1.0: integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== yocto-queue@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251" - integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g== + version "1.1.1" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.1.1.tgz#fef65ce3ac9f8a32ceac5a634f74e17e5b232110" + integrity sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g== zod@^3.22.2, zod@^3.22.4: version "3.23.8"