From 6655afee8f03292cbb2d89d3ab5e8316641b964f Mon Sep 17 00:00:00 2001 From: "Ricardo J. Mendez" Date: Wed, 16 Feb 2022 09:32:09 +0100 Subject: [PATCH 1/2] Improvements on settling perp pnl This is an optimization on f97370f following Dafy's suggestion here: https://github.com/blockworks-foundation/liquidator-v3/pull/17#issuecomment-1020808416 It uses the client's settlePosPnl function instead of doing all the work itself. It required making the mangoAccounts array global so we can have access to it when liquidating from the RPC endpoint, but I'm not getting any movement from the feed anyway. --- src/liquidator.ts | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/liquidator.ts b/src/liquidator.ts index 588e05d..df3d2cd 100644 --- a/src/liquidator.ts +++ b/src/liquidator.ts @@ -91,6 +91,7 @@ let liqorMangoAccount: MangoAccount; let spotMarkets: Market[]; let perpMarkets: PerpMarket[]; let rootBanks: (RootBank | undefined)[]; +let mangoAccounts: MangoAccount[] = []; async function main() { console.log(`Starting liquidator for ${groupName}...`); @@ -164,7 +165,6 @@ async function main() { // never returns async function liquidatableFromSolanaRpc() { - let mangoAccounts: MangoAccount[] = []; await refreshAccounts(mangoGroup, mangoAccounts); watchAccounts(groupIds.mangoProgramId, mangoGroup, mangoAccounts); @@ -357,6 +357,7 @@ async function liquidatableFromLiquidatableFeed() { if (!candidatesSet.has(account)) { candidatesSet.add(account); candidates.enqueue(account); + console.log(`Enqueued ${account.publicKey.toBase58()}`) } }); @@ -551,7 +552,7 @@ async function liquidateAccount( rootBanks: (RootBank | undefined)[], perpMarkets: PerpMarket[], liqee: MangoAccount, - liqor: MangoAccount, + liqor: MangoAccount ) { const hasPerpOpenOrders = liqee.perpAccounts.some( (pa) => pa.bidsQuantity.gt(ZERO_BN) || pa.asksQuantity.gt(ZERO_BN), @@ -814,7 +815,7 @@ async function liquidatePerps( perpMarkets: PerpMarket[], rootBanks: (RootBank | undefined)[], liqee: MangoAccount, - liqor: MangoAccount, + liqor: MangoAccount ) { console.log('liquidatePerps'); const lowestHealthMarket = perpMarkets @@ -852,8 +853,8 @@ async function liquidatePerps( const liqorInitHealth = liqor.getHealth(mangoGroup, cache, 'Init'); let maxLiabTransfer = liqorInitHealth.mul(liabLimit); + const quoteRootBank = rootBanks[QUOTE_INDEX]; if (liqee.isBankrupt) { - const quoteRootBank = rootBanks[QUOTE_INDEX]; if (quoteRootBank) { // don't do anything it if quote position is zero console.log('resolvePerpBankruptcy', maxLiabTransfer.toString()); @@ -873,6 +874,16 @@ async function liquidatePerps( let maxNet = ZERO_I80F48; let maxNetIndex = mangoGroup.tokens.length - 1; + // We need to settle the positive PnLs to make sure that the account has funds + // to actually compensate for a liquidation. Otherwise we may end up with a + // token liquidation of 0. + // + // https://discord.com/channels/791995070613159966/826034521261604874/934629112734167060 + + if (quoteRootBank) { + await client.settlePosPnl(mangoGroup, cache, liqee, perpMarkets, quoteRootBank, payer, mangoAccounts) + } + for (let i = 0; i < mangoGroup.tokens.length; i++) { const price = cache.priceCache[i] ? cache.priceCache[i].price From 410bfc5691f2604e2ee14dc00b2299c1d6938599 Mon Sep 17 00:00:00 2001 From: Riordan Panayides Date: Tue, 12 Apr 2022 12:13:19 +0100 Subject: [PATCH 2/2] Upgrade mango-client --- package.json | 2 +- src/liquidator.ts | 184 ++++++++++++++++++++++++++-------------------- yarn.lock | 94 +++++++++++++++++++---- 3 files changed, 188 insertions(+), 92 deletions(-) diff --git a/package.json b/package.json index 444e82e..c30fd68 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "trailingComma": "all" }, "dependencies": { - "@blockworks-foundation/mango-client": "latest", + "@blockworks-foundation/mango-client": "^3.4.3", "@project-serum/anchor": "^0.16.2", "@project-serum/serum": "0.13.55", "@project-serum/sol-wallet-adapter": "^0.2.0", diff --git a/src/liquidator.ts b/src/liquidator.ts index dfc46f7..c110b3e 100644 --- a/src/liquidator.ts +++ b/src/liquidator.ts @@ -22,14 +22,19 @@ import { sleep, ZERO_I80F48, } from '@blockworks-foundation/mango-client'; -import { Account, Commitment, Connection, PublicKey } from '@solana/web3.js'; +import { + Commitment, + Connection, + Keypair, + PublicKey, +} from '@solana/web3.js'; import { Market, OpenOrders } from '@project-serum/serum'; import BN from 'bn.js'; import { Orderbook } from '@project-serum/serum/lib/market'; import axios from 'axios'; import * as Env from 'dotenv'; import envExpand from 'dotenv-expand'; -import {Client as RpcWebSocketClient} from 'rpc-websockets'; +import { Client as RpcWebSocketClient } from 'rpc-websockets'; import { AsyncBlockingQueue } from './AsyncBlockingQueue'; envExpand(Env.config()); @@ -41,7 +46,8 @@ const refreshAccountsInterval = parseInt( const refreshWebsocketInterval = parseInt( process.env.INTERVAL_WEBSOCKET || '300000', ); -const liquidatableFeedWebsocketAddress = process.env.LIQUIDATABLE_FEED_WEBSOCKET_ADDRESS; +const liquidatableFeedWebsocketAddress = + process.env.LIQUIDATABLE_FEED_WEBSOCKET_ADDRESS; const rebalanceInterval = parseInt(process.env.INTERVAL_REBALANCE || '10000'); const checkTriggers = process.env.CHECK_TRIGGERS ? process.env.CHECK_TRIGGERS === 'true' @@ -55,36 +61,43 @@ const config = new Config(IDS); const cluster = (process.env.CLUSTER || 'mainnet') as Cluster; const groupName = process.env.GROUP || 'mainnet.1'; -const groupIds = config.getGroup(cluster, groupName) ?? (() => { throw new Error(`Group ${groupName} not found`); })(); +const groupIds = + config.getGroup(cluster, groupName) ?? + (() => { + throw new Error(`Group ${groupName} not found`); + })(); // Target values to keep in spot, ordered the same as in mango client's ids.json // Example: // // MNGO BTC ETH SOL USDT SRM RAY COPE FTT MSOL // TARGETS=0 0 0 1 0 0 0 0 0 0 -const TARGETS = process.env.TARGETS?.replace(/\s+/g,' ').trim().split(' ').map((s) => parseFloat(s)) - ?? [0, 0, 0, 0, 0, 0, 0, 0, 0]; +const TARGETS = process.env.TARGETS?.replace(/\s+/g, ' ') + .trim() + .split(' ') + .map((s) => parseFloat(s)) ?? [0, 0, 0, 0, 0, 0, 0, 0, 0]; // Do not liquidate accounts that have less than this much in value -const minEquity = parseInt( - process.env.MIN_EQUITY || '0', -); -if(minEquity > 0) { +const minEquity = parseInt(process.env.MIN_EQUITY || '0'); +if (minEquity > 0) { console.log(`Minimum equity required to liquidate: ${minEquity}`); } const mangoProgramId = groupIds.mangoProgramId; const mangoGroupKey = groupIds.publicKey; -const payer = new Account( - JSON.parse( - process.env.PRIVATE_KEY || - fs.readFileSync( - process.env.KEYPAIR || os.homedir() + '/.config/solana/id.json', - 'utf-8', - ), +const payer = Keypair.fromSecretKey( + Uint8Array.from( + JSON.parse( + process.env.PRIVATE_KEY || + fs.readFileSync( + process.env.KEYPAIR || os.homedir() + '/.config/solana/id.json', + 'utf-8', + ), + ), ), ); + console.log(`Payer: ${payer.publicKey.toBase58()}`); const rpcEndpoint = process.env.ENDPOINT_URL || config.cluster_urls[cluster]; const connection = new Connection(rpcEndpoint, 'processed' as Commitment); @@ -274,56 +287,53 @@ async function liquidatableFromSolanaRpc() { } } -async function maybeLiquidateAccount(mangoAccount: MangoAccount): Promise { - const mangoAccountKeyString = mangoAccount.publicKey.toBase58(); +async function maybeLiquidateAccount( + mangoAccount: MangoAccount, +): Promise { + const mangoAccountKeyString = mangoAccount.publicKey.toBase58(); - if (!mangoAccount.isLiquidatable(mangoGroup, cache)) { - console.log( - `Account ${mangoAccountKeyString} no longer liquidatable`, - ); - return false; - } + if (!mangoAccount.isLiquidatable(mangoGroup, cache)) { + console.log(`Account ${mangoAccountKeyString} no longer liquidatable`); + return false; + } - const equity = mangoAccount.computeValue(mangoGroup, cache).toNumber() - if (equity < minEquity && minEquity > 0) { - // console.log(`Account ${mangoAccountKeyString} only has ${equity}, PASS`); - return false; - } + const equity = mangoAccount.computeValue(mangoGroup, cache).toNumber(); + if (equity < minEquity && minEquity > 0) { + // console.log(`Account ${mangoAccountKeyString} only has ${equity}, PASS`); + return false; + } - - const health = mangoAccount.getHealthRatio(mangoGroup, cache, 'Maint'); - const accountInfoString = mangoAccount.toPrettyString( - groupIds, + const health = mangoAccount.getHealthRatio(mangoGroup, cache, 'Maint'); + const accountInfoString = mangoAccount.toPrettyString( + groupIds, + mangoGroup, + cache, + ); + console.log( + `Sick account ${mangoAccountKeyString} health ratio: ${health.toString()}\n${accountInfoString}`, + ); + notify(`Sick account\n${accountInfoString}`); + try { + await liquidateAccount( mangoGroup, cache, + spotMarkets, + rootBanks, + perpMarkets, + mangoAccount, + liqorMangoAccount, ); - console.log( - `Sick account ${mangoAccountKeyString} health ratio: ${health.toString()}\n${accountInfoString}`, + + console.log('Liquidated account', mangoAccountKeyString); + notify(`Liquidated account ${mangoAccountKeyString}`); + } catch (err: any) { + console.error( + `Failed to liquidate account ${mangoAccountKeyString}: ${err}`, ); - notify(`Sick account\n${accountInfoString}`); - try { - await liquidateAccount( - mangoGroup, - cache, - spotMarkets, - rootBanks, - perpMarkets, - mangoAccount, - liqorMangoAccount, - ); + notify(`Failed to liquidate account ${mangoAccountKeyString}: ${err}`); + } - console.log('Liquidated account', mangoAccountKeyString); - notify(`Liquidated account ${mangoAccountKeyString}`); - } catch (err: any) { - console.error( - `Failed to liquidate account ${mangoAccountKeyString}: ${err}`, - ); - notify( - `Failed to liquidate account ${mangoAccountKeyString}: ${err}`, - ); - } - - return true; + return true; } async function newAccountOnLiquidatableFeed(account) { @@ -332,7 +342,7 @@ async function newAccountOnLiquidatableFeed(account) { const mangoAccountKey = new PublicKey(account); const mangoAccount = new MangoAccount(mangoAccountKey, null); - [cache, liqorMangoAccount, ] = await Promise.all([ + [cache, liqorMangoAccount] = await Promise.all([ mangoGroup.loadCache(connection), liqorMangoAccount.reload(connection, mangoGroup.dexProgramId), mangoAccount.reload(connection, mangoGroup.dexProgramId), @@ -364,16 +374,16 @@ async function liquidatableFromLiquidatableFeed() { const ws = new RpcWebSocketClient(liquidatableFeedWebsocketAddress, { max_reconnects: Infinity, }); - ws.on('open', (x) => console.log("opened liquidatable feed")); - ws.on('error', (status) => console.log("error on liquidatable feed", status)); - ws.on('close', (err) => console.log("closed liquidatable feed", err)); + ws.on('open', (x) => console.log('opened liquidatable feed')); + ws.on('error', (status) => console.log('error on liquidatable feed', status)); + ws.on('close', (err) => console.log('closed liquidatable feed', err)); ws.on('candidate', (params) => { - const account = params.account; - if (!candidatesSet.has(account)) { - candidatesSet.add(account); - candidates.enqueue(account); - console.log(`Enqueued ${account.publicKey.toBase58()}`) - } + const account = params.account; + if (!candidatesSet.has(account)) { + candidatesSet.add(account); + candidates.enqueue(account); + console.log(`Enqueued ${account.publicKey.toBase58()}`); + } }); while (true) { @@ -567,7 +577,7 @@ async function liquidateAccount( rootBanks: (RootBank | undefined)[], perpMarkets: PerpMarket[], liqee: MangoAccount, - liqor: MangoAccount + liqor: MangoAccount, ) { const hasPerpOpenOrders = liqee.perpAccounts.some( (pa) => pa.bidsQuantity.gt(ZERO_BN) || pa.asksQuantity.gt(ZERO_BN), @@ -830,7 +840,7 @@ async function liquidatePerps( perpMarkets: PerpMarket[], rootBanks: (RootBank | undefined)[], liqee: MangoAccount, - liqor: MangoAccount + liqor: MangoAccount, ) { console.log('liquidatePerps'); const lowestHealthMarket = perpMarkets @@ -894,9 +904,17 @@ async function liquidatePerps( // token liquidation of 0. // // https://discord.com/channels/791995070613159966/826034521261604874/934629112734167060 - + if (quoteRootBank) { - await client.settlePosPnl(mangoGroup, cache, liqee, perpMarkets, quoteRootBank, payer, mangoAccounts) + await client.settlePosPnl( + mangoGroup, + cache, + liqee, + perpMarkets, + quoteRootBank, + payer, + mangoAccounts, + ); } for (let i = 0; i < mangoGroup.tokens.length; i++) { @@ -1017,7 +1035,13 @@ function getDiffsAndNet( const marketIndex = groupIds!.spotMarkets[i].marketIndex; const diff = mangoAccount .getUiDeposit(cache.rootBankCache[marketIndex], mangoGroup, marketIndex) - .sub(mangoAccount.getUiBorrow(cache.rootBankCache[marketIndex], mangoGroup, marketIndex)) + .sub( + mangoAccount.getUiBorrow( + cache.rootBankCache[marketIndex], + mangoGroup, + marketIndex, + ), + ) .sub(I80F48.fromNumber(target)); diffs.push(diff); netValues.push([i, diff.mul(cache.priceCache[i].price), marketIndex]); @@ -1075,7 +1099,7 @@ async function balanceTokens( console.log('balanceTokens'); await mangoAccount.reload(connection, mangoGroup.dexProgramId); const cache = await mangoGroup.loadCache(connection); - const cancelOrdersPromises: Promise[] = []; + const cancelOrdersPromises: Promise[] = []; const bidsInfo = await getMultipleAccounts( connection, markets.map((m) => m.bidsAddress), @@ -1116,7 +1140,7 @@ async function balanceTokens( connection, mangoGroup.dexProgramId, ); - const settlePromises: Promise[] = []; + const settlePromises: Promise[] = []; for (let i = 0; i < markets.length; i++) { const marketIndex = mangoGroup.getSpotMarketIndex(markets[i].publicKey); const oo = openOrders[marketIndex]; @@ -1143,8 +1167,12 @@ async function balanceTokens( for (let i = 0; i < groupIds!.spotMarkets.length; i++) { const marketIndex = netValues[i][2]; const netIndex = netValues[i][0]; - const marketConfig = groupIds!.spotMarkets.find((m) => m.marketIndex == marketIndex)! - const market = markets.find((m) => m.publicKey.equals(mangoGroup.spotMarkets[marketIndex].spotMarket))!; + const marketConfig = groupIds!.spotMarkets.find( + (m) => m.marketIndex == marketIndex, + )!; + const market = markets.find((m) => + m.publicKey.equals(mangoGroup.spotMarkets[marketIndex].spotMarket), + )!; const liquidationFee = mangoGroup.spotMarkets[marketIndex].liquidationFee; if (Math.abs(diffs[netIndex].toNumber()) > market!.minOrderSize) { const side = netValues[i][1].gt(ZERO_I80F48) ? 'sell' : 'buy'; diff --git a/yarn.lock b/yarn.lock index 2214f11..2c5bf7d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -37,20 +37,22 @@ dependencies: regenerator-runtime "^0.13.4" -"@blockworks-foundation/mango-client@latest": - version "3.2.24" - resolved "https://registry.yarnpkg.com/@blockworks-foundation/mango-client/-/mango-client-3.2.24.tgz#c46f2cff3dca06d88e304610643f40fddbf0ee76" - integrity sha512-1rWvmMBK1wGVLG7UfTpHI2FPnXfRNTLFWifGa9q/FkRLp07GzGDY3fGhquJW2NxGv2KPwzX/YJEAHX+2x485OA== +"@blockworks-foundation/mango-client@^3.4.3": + version "3.4.3" + resolved "https://registry.yarnpkg.com/@blockworks-foundation/mango-client/-/mango-client-3.4.3.tgz#a5d72cc4520736edf843caaf8aff5376d1c056a4" + integrity sha512-8JFyE3nlwolfcqbhAYIenv25VnNvvYcEIUUUczHc31WaCtrcwC96Epj7wprHQO4OP6uLc9TBF4zuuGlZY3Fd6Q== dependencies: - "@project-serum/anchor" "^0.16.2" + "@project-serum/anchor" "^0.21.0" "@project-serum/serum" "0.13.55" "@project-serum/sol-wallet-adapter" "^0.2.0" "@solana/spl-token" "^0.1.6" "@solana/web3.js" "^1.31.0" big.js "^6.1.1" - bn.js "^5.2.0" + bn.js "^5.1.0" buffer-layout "^1.2.1" + cross-fetch "^3.1.5" dotenv "^10.0.0" + toformat "^2.0.0" yargs "^17.0.1" "@eslint/eslintrc@^0.4.3": @@ -164,6 +166,27 @@ snake-case "^3.0.4" toml "^3.0.0" +"@project-serum/anchor@^0.21.0": + version "0.21.0" + resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.21.0.tgz#ad5fb33744991ec1900cdb2fd22707c908b12b5f" + integrity sha512-flRuW/F+iC8mitNokx82LOXyND7Dyk6n5UUPJpQv/+NfySFrNFlzuQZaBZJ4CG5g9s8HS/uaaIz1nVkDR8V/QA== + dependencies: + "@project-serum/borsh" "^0.2.4" + "@solana/web3.js" "^1.17.0" + base64-js "^1.5.1" + bn.js "^5.1.2" + bs58 "^4.0.1" + buffer-layout "^1.2.2" + camelcase "^5.3.1" + cross-fetch "^3.1.5" + crypto-hash "^1.3.0" + eventemitter3 "^4.0.7" + find "^0.3.0" + js-sha256 "^0.9.0" + pako "^2.0.3" + snake-case "^3.0.4" + toml "^3.0.0" + "@project-serum/borsh@^0.2.2": version "0.2.2" resolved "https://registry.yarnpkg.com/@project-serum/borsh/-/borsh-0.2.2.tgz#63e558f2d6eb6ab79086bf499dea94da3182498f" @@ -172,6 +195,14 @@ bn.js "^5.1.2" buffer-layout "^1.2.0" +"@project-serum/borsh@^0.2.4": + version "0.2.5" + resolved "https://registry.yarnpkg.com/@project-serum/borsh/-/borsh-0.2.5.tgz#6059287aa624ecebbfc0edd35e4c28ff987d8663" + integrity sha512-UmeUkUoKdQ7rhx6Leve1SssMR/Ghv8qrEiyywyxSWg7ooV7StdpPBhciiy5eB3T0qU1BXvdRNC8TdrkxK7WC5Q== + dependencies: + bn.js "^5.1.2" + buffer-layout "^1.2.0" + "@project-serum/serum@0.13.55": version "0.13.55" resolved "https://registry.yarnpkg.com/@project-serum/serum/-/serum-0.13.55.tgz#2ac44fe7b07651274eb57ac54ea9325789df5dd7" @@ -654,7 +685,7 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -buffer-layout@^1.2.0, buffer-layout@^1.2.1: +buffer-layout@^1.2.0, buffer-layout@^1.2.1, buffer-layout@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/buffer-layout/-/buffer-layout-1.2.2.tgz#b9814e7c7235783085f9ca4966a0cfff112259d5" integrity sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA== @@ -813,6 +844,13 @@ cross-fetch@^3.1.4: dependencies: node-fetch "2.6.1" +cross-fetch@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" + integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== + dependencies: + node-fetch "2.6.7" + cross-spawn@^7.0.1, cross-spawn@^7.0.2: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -1651,6 +1689,13 @@ node-fetch@2.6.1: resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== +node-fetch@2.6.7: + version "2.6.7" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" + integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== + dependencies: + whatwg-url "^5.0.0" + node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3" @@ -2023,11 +2068,21 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +toformat@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/toformat/-/toformat-2.0.0.tgz#7a043fd2dfbe9021a4e36e508835ba32056739d8" + integrity sha512-03SWBVop6nU8bpyZCx7SodpYznbZF5R4ljwNLBcTQzKOD9xuihRo/psX58llS1BMFhhAI08H3luot5GoXJz2pQ== + toml@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/toml/-/toml-3.0.0.tgz#342160f1af1904ec9d204d03a5d61222d762c5ee" integrity sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w== +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= + traverse-chain@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/traverse-chain/-/traverse-chain-0.1.0.tgz#61dbc2d53b69ff6091a12a168fd7d433107e40f1" @@ -2129,6 +2184,19 @@ vscode-textmate@5.2.0: resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-5.2.0.tgz#01f01760a391e8222fe4f33fbccbd1ad71aed74e" integrity sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ== +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + which@2.0.2, which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" @@ -2191,9 +2259,9 @@ yargs-parser@^20.2.2: integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== yargs-parser@^21.0.0: - version "21.0.0" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.0.0.tgz#a485d3966be4317426dd56bdb6a30131b281dc55" - integrity sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA== + version "21.0.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.0.1.tgz#0267f286c877a4f0f728fceb6f8a3e4cb95c6e35" + integrity sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg== yargs-unparser@2.0.0: version "2.0.0" @@ -2219,9 +2287,9 @@ yargs@16.2.0: yargs-parser "^20.2.2" yargs@^17.0.1: - version "17.3.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.3.1.tgz#da56b28f32e2fd45aefb402ed9c26f42be4c07b9" - integrity sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA== + version "17.4.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.4.1.tgz#ebe23284207bb75cee7c408c33e722bfb27b5284" + integrity sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g== dependencies: cliui "^7.0.2" escalade "^3.1.1"