diff --git a/README.md b/README.md index 5bad3e7..8aa1b3d 100644 --- a/README.md +++ b/README.md @@ -58,8 +58,8 @@ Options: --max-orders Maximum number of orders to place in single drip call (default: 250) --buy-amount-slippage-bps Tolerance to add to the quoted buyAmount (default: 100) --module COWFeeModule address - --token-list-strategy Strategy to use to get the list of tokens to swap on (choices: "explorer", "chain", default: "explorer") --lookback-range Last number of blocks to check the `Trade` events for (default: 1000) + --multicall-size max number of calls in a multicall (default: 100) -h, --help display help for command ``` @@ -73,8 +73,8 @@ yarn ts-node index.ts \ --rpc-url https://eth.llamarpc.com \ --buy-amount-slippage-bps 100 \ --module \ - --token-list-strategy explorer \ - --lookback-range 1000 + --lookback-range 1000 \ + --multicall-size 100 ``` #### Docker @@ -85,6 +85,7 @@ docker build -t cow-fee . # run the container docker run --rm \ -e PRIVATE_KEY=$PRIVATE_KEY \ + -e LOG_LEVEL=debug \ cow-fee \ --network mainnet \ --max-orders 250 \ @@ -92,8 +93,8 @@ docker run --rm \ --rpc-url https://eth.llamarpc.com \ --buy-amount-slippage-bps 100 \ --module \ - --token-list-strategy explorer \ - --lookback-range 1000 + --lookback-range 1000 \ + --multicall-size 100 ``` ### Module operations with cast diff --git a/index.ts b/index.ts index 3915bd9..e13da57 100644 --- a/index.ts +++ b/index.ts @@ -1,7 +1,11 @@ import { ethers } from 'ethers'; import { formatUnits } from 'ethers/lib/utils'; import { getTokensToSwap, swapTokens } from './ts/cowfee'; -import { IConfig, networkSpecificConfigs } from './ts/common'; +import { + IConfig, + getLogger, + networkSpecificConfigs, +} from './ts/common'; import { Command, Option } from '@commander-js/extra-typings'; import { erc20Abi, moduleAbi, settlementAbi } from './ts/abi'; @@ -43,18 +47,23 @@ const readConfig = async (): Promise< .addOption(new Option('--module ', 'COWFeeModule address')) .addOption( new Option( - '--token-list-strategy ', - 'Strategy to use to get the list of tokens to swap on' + '--lookback-range ', + 'Last number of blocks to check the `Trade` events for' ) - .choices(['explorer', 'chain'] as const) - .default('explorer' as 'explorer' | 'chain') + .default(1000) + .argParser((x) => +x) + ) + .addOption( + new Option('--multicall-size ', 'max number of calls in a multicall') + .default(100) + .argParser((x) => +x) ) .addOption( new Option( - '--lookback-range ', - 'Last number of blocks to check the `Trade` events for' + '--query-logs-size ', + 'max block range to use for eth_getLogs' ) - .default(1000) + .default(50000) .argParser((x) => +x) ); program.parse(); @@ -67,7 +76,8 @@ const readConfig = async (): Promise< buyAmountSlippageBps, module: selectedModule, lookbackRange, - tokenListStrategy, + multicallSize, + queryLogsSize, } = options; const network = selectedNetwork || 'mainnet'; @@ -120,32 +130,35 @@ const readConfig = async (): Promise< buyAmountSlippageBps, keeper, appData, - tokenListStrategy, lookbackRange, targetSafe, + multicallSize, + queryLogsSize, }, provider, ]; }; +const logger = getLogger('index'); + export const dripItAll = async () => { // await getAppData().then(console.log); const [config, provider] = await readConfig(); - console.log(config.options); - // return; + logger.info(config.options, 'config options'); const signer = new ethers.Wallet(config.privateKey, provider); + process.on('warning', (e) => console.warn(e.stack)); const tokensToSwap = await getTokensToSwap(config, provider); - console.log( - 'tokensToSwap', + logger.info( tokensToSwap.map((token) => [ token.symbol, token.address, formatUnits(token.balance, token.decimals), token.buyAmount, token.needsApproval, - ]) + ]), + 'tokensToSwap' ); for (let i = 0; i < tokensToSwap.length; i += config.maxOrders) { diff --git a/package.json b/package.json index 31be66e..a194249 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,9 @@ "@cowprotocol/cow-sdk": "^5.2.0", "axios": "^1.6.8", "commander": "^12.0.0", - "ethers": "v5" + "ethers": "v5", + "pino": "^9.0.0", + "pino-pretty": "^11.0.0" }, "devDependencies": { "@types/minimist": "^1.2.5", diff --git a/ts/common.ts b/ts/common.ts index 84011d5..cfd97f6 100644 --- a/ts/common.ts +++ b/ts/common.ts @@ -1,6 +1,7 @@ import { OrderBookApi, SupportedChainId } from '@cowprotocol/cow-sdk'; import { ethers } from 'ethers'; import { multicall3Abi } from './abi'; +import pino from 'pino'; export const networkSpecificConfigs = { mainnet: { @@ -31,8 +32,9 @@ export interface IConfig { buyAmountSlippageBps: number; keeper: string; appData: string; - tokenListStrategy: 'explorer' | 'chain'; lookbackRange: number; + multicallSize: number; + queryLogsSize: number; } const toChainId = (network: keyof typeof networkSpecificConfigs) => { @@ -71,3 +73,43 @@ export const getMulticall3 = (provider: ethers.providers.JsonRpcProvider) => { provider ); }; + +export const chunkedMulticall = async ( + provider: ethers.providers.JsonRpcProvider, + calls: { target: string; callData: string }[], + chunkSize: number +) => { + const mcall = getMulticall3(provider); + const nChunks = Math.ceil(calls.length / chunkSize); + const ret = []; + for (let i = 0; i < nChunks; i++) { + const chunk = calls.slice(i * chunkSize, (i + 1) * chunkSize); + ret.push(...(await mcall.tryAggregate(false, chunk))); + } + return ret; +}; + +export const chunkedQueryFilter = async ( + contract: ethers.Contract, + filter: ethers.EventFilter, + fromBlock: number, + toBlock: number, + chunkSize: number +) => { + const ret = []; + for (let i = fromBlock; i <= toBlock; i += chunkSize) { + ret.push( + ...(await contract.queryFilter( + filter, + i, + Math.min(i + chunkSize - 1, toBlock) + )) + ); + } + return ret; +}; + +const logger = pino(); +export const getLogger = (name: string) => { + return logger.child({ name, level: process.env.LOG_LEVEL || 'info' }); +}; diff --git a/ts/cowfee.ts b/ts/cowfee.ts index 5331485..930267e 100644 --- a/ts/cowfee.ts +++ b/ts/cowfee.ts @@ -1,11 +1,13 @@ import { BigNumber, ContractTransaction, ethers } from 'ethers'; import { IConfig, + chunkedMulticall, + getLogger, getMulticall3, getOrderbookApi, networkSpecificConfigs, } from './common'; -import { getTokenBalances } from './explorer-apis'; +import { getTokenBalances } from './token-fetcher'; import { multicall3Abi, erc20Abi, moduleAbi } from './abi'; import { formatUnits, parseEther, parseUnits } from 'ethers/lib/utils'; import { @@ -20,12 +22,14 @@ import { } from '@cowprotocol/cow-sdk'; import { MetadataApi } from '@cowprotocol/app-data'; +const logger = getLogger('cowfee'); const ABI_CODER = new ethers.utils.AbiCoder(); const getBalances = async ( provider: ethers.providers.JsonRpcProvider, address: string, - tokens: string[] + tokens: string[], + multicallSize: number ): Promise => { const Multicall3 = getMulticall3(provider); const balanceOfCalldata = new ethers.utils.Interface( @@ -35,7 +39,7 @@ const getBalances = async ( target: token, callData: balanceOfCalldata, })); - const balancesRet = await Multicall3.tryAggregate(false, cds); + const balancesRet = await chunkedMulticall(provider, cds, multicallSize); const balances = balancesRet.map((r: any) => r.success ? ABI_CODER.decode(['uint'], r.returnData)[0] : BigNumber.from(0) ) as BigNumber[]; @@ -46,18 +50,20 @@ const getAllowances = async ( provider: ethers.providers.JsonRpcProvider, owner: string, spender: string, - tokens: string[] + tokens: string[], + multicallSize: number ): Promise => { const Multicall3 = getMulticall3(provider); const allowanceCalldata = new ethers.utils.Interface( erc20Abi ).encodeFunctionData('allowance', [owner, spender]); - const allowancesRet = await Multicall3.tryAggregate( - false, + const allowancesRet = await chunkedMulticall( + provider, tokens.map((token) => ({ target: token, callData: allowanceCalldata, - })) + })), + multicallSize ); const allowances = allowancesRet.map((r: any) => r.success ? ABI_CODER.decode(['uint'], r.returnData)[0] : BigNumber.from(0) @@ -69,24 +75,30 @@ export const getTokensToSwap = async ( config: IConfig, provider: ethers.providers.JsonRpcProvider ) => { - const unfiltered = await getTokenBalances( - config.gpv2Settlement, - config.network, - config.tokenListStrategy, - config - ); + const unfiltered = await getTokenBalances(config); // populate the balances and allowances const tokenAddresses = unfiltered.map((token) => token.address); + logger.info( + `Getting token balances and allowances for ${tokenAddresses.length} tokens` + ); const [balances, allowances] = await Promise.all([ - getBalances(provider, config.gpv2Settlement, tokenAddresses), + getBalances( + provider, + config.gpv2Settlement, + tokenAddresses, + config.multicallSize + ), getAllowances( provider, config.gpv2Settlement, config.vaultRelayer, - tokenAddresses + tokenAddresses, + config.multicallSize ), ]); + logger.info('got balances and allowances'); + // minValue filter again with _real_ balance const unfilteredWithBalanceAndAllowance = unfiltered.map((token, idx) => ({ ...token, @@ -95,6 +107,10 @@ export const getTokensToSwap = async ( needsApproval: allowances[idx].lt(balances[idx]), })); + + logger.info( + `Getting quotes for ${unfilteredWithBalanceAndAllowance.length} tokens` + ); // filter shitcoins with no liquidity by using the quotes api const orderBookApi = getOrderbookApi(config); const quotes = await Promise.allSettled( @@ -119,7 +135,7 @@ export const getTokensToSwap = async ( (quotes[i].status === 'fulfilled' && (quotes[i] as PromiseFulfilledResult).value.quote .buyAmount) || - 0 + 0 ) .mul(10000 - config.buyAmountSlippageBps) .div(10000), @@ -196,17 +212,17 @@ export const swapTokens = async ( }) ) ); - console.log( - 'failed', + logger.info( orders .filter((x) => x.status === 'rejected') - .map((x) => (x as PromiseRejectedResult).reason) + .map((x) => (x as PromiseRejectedResult).reason), + 'failed orders' ); - console.log( - 'orderIds', + logger.info( orders .filter((x) => x.status === 'fulfilled') - .map((x) => (x as PromiseFulfilledResult).value) + .map((x) => (x as PromiseFulfilledResult).value), + 'orderIds' ); // only execute drip for successfully created orders const toActuallySwap = toSwap.filter( @@ -232,7 +248,7 @@ export const swapTokens = async ( toApprove, toDrip ); - console.log('dripTx', dripTx.hash); + logger.info(dripTx.hash, 'dripTx'); const dripTxReceipt = await dripTx.wait(); if (dripTxReceipt.status === 0) throw new Error(`drip failed: ${dripTxReceipt.transactionHash}`); diff --git a/ts/explorer-apis.ts b/ts/explorer-apis.ts deleted file mode 100644 index 6c70360..0000000 --- a/ts/explorer-apis.ts +++ /dev/null @@ -1,170 +0,0 @@ -import axios from 'axios'; -import { IConfig, getMulticall3, getOrderbookApi } from './common'; -import { BigNumber, ethers } from 'ethers'; -import { erc20Abi, settlementAbi } from './abi'; - -interface ITokenInfo { - address: string; - symbol: string; - decimals: number; - balance?: string; -} - -interface IEthplorerResponse { - tokens: { - tokenInfo: { - address: string; - name: string; - decimals: string; - symbol: string; - price: { rate: number }; - }; - rawBalance: string; - }[]; -} - -const getTokenInfosFromEthPlorer = async ( - address: string -): Promise => { - const { data } = await axios.get( - `https://api.ethplorer.io/getAddressInfo/${address}?apiKey=freekey` - ); - - return data.tokens.map((token) => ({ - address: token.tokenInfo.address, - decimals: +token.tokenInfo.decimals, - symbol: token.tokenInfo.symbol, - balance: token.rawBalance, - })); -}; - -interface IBlockscoutResponse { - token: { - address: string; - decimals: string; - exchange_rate: string; - symbol: string; - type: string; - }; - value: string; -} - -const getTokenInfosFromBlockscout = async ( - address: string -): Promise => { - const { data } = await axios.get( - `https://gnosis.blockscout.com/api/v2/addresses/${address}/token-balances` - ); - - return data - .filter((token) => token.token.type === 'ERC-20') - .map((token) => ({ - address: token.token.address, - decimals: +token.token.decimals, - symbol: token.token.symbol, - balance: token.value, - })); -}; - -interface IDefillamaPriceResponse { - coins: { - 'coingecko:ethereum': { - price: number; - }; - }; -} - -const getEthPrice = async () => { - const { data } = await axios.get( - 'https://coins.llama.fi/prices/current/coingecko:ethereum' - ); - return data.coins['coingecko:ethereum'].price; -}; - -const ABI_CODER = new ethers.utils.AbiCoder(); - -const getDecimals = async ( - provider: ethers.providers.JsonRpcProvider, - tokens: string[] -): Promise => { - const Multicall3 = getMulticall3(provider); - const decimalsCalldata = new ethers.utils.Interface( - erc20Abi - ).encodeFunctionData('decimals'); - const cds = tokens.map((token) => ({ - target: token, - callData: decimalsCalldata, - })); - const decimalsRet = await Multicall3.tryAggregate(false, cds); - const decimals = decimalsRet.map((r: any, idx: number) => { - if (!r.success) return 0; - - try { - return ABI_CODER.decode(['uint8'], r.returnData)[0]; - } catch (err) { - console.log('errror decoding', r.returnData, tokens[idx]); - } - }) as number[]; - return decimals; -}; - -export const getTokenInfosFromChain = async ( - config: IConfig -): Promise => { - const provider = new ethers.providers.JsonRpcProvider(config.rpcUrl); - const settlement = new ethers.Contract( - config.gpv2Settlement, - settlementAbi, - provider - ); - const tradeFilter = settlement.filters.Trade(); - const currentBlock = await provider.getBlockNumber(); - const logs = await settlement.queryFilter( - tradeFilter, - currentBlock - config.lookbackRange, - currentBlock - ); - const allTokens = Array.from( - new Set( - logs.map((log) => [log.args!.sellToken, log.args!.buyToken]).flat() - ).values() - ).filter( - (x) => - x.toLowerCase() !== - '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'.toLowerCase() - ); - - const allDecimals = await getDecimals(provider, allTokens); - const allTokenInfo: ITokenInfo[] = allTokens.map((token, idx) => { - const decimals = allDecimals[idx]; - return { - address: token, - decimals, - symbol: '', - }; - }); - return allTokenInfo; -}; - -export const getTokenBalances = async ( - address: string, - network: IConfig['network'], - strategy: 'explorer' | 'chain', - config: IConfig -): Promise => { - switch (strategy) { - case 'explorer': { - switch (network) { - case 'mainnet': { - return getTokenInfosFromEthPlorer(address); - } - case 'gnosis': { - return getTokenInfosFromBlockscout(address); - } - } - } - case 'chain': { - return getTokenInfosFromChain(config); - } - } -}; diff --git a/ts/token-fetcher.ts b/ts/token-fetcher.ts new file mode 100644 index 0000000..53942ab --- /dev/null +++ b/ts/token-fetcher.ts @@ -0,0 +1,104 @@ +import axios from 'axios'; +import { + IConfig, + chunkedMulticall, + chunkedQueryFilter, + getLogger, + getMulticall3, + getOrderbookApi, +} from './common'; +import { BigNumber, ethers } from 'ethers'; +import { erc20Abi, settlementAbi } from './abi'; + +const logger = getLogger('token-fetcher'); + +interface ITokenInfo { + address: string; + symbol: string; + decimals: number; + balance?: string; +} + +const ABI_CODER = new ethers.utils.AbiCoder(); + +const getDecimals = async ( + provider: ethers.providers.JsonRpcProvider, + tokens: string[], + multicallSize: number +): Promise => { + const Multicall3 = getMulticall3(provider); + const decimalsCalldata = new ethers.utils.Interface( + erc20Abi + ).encodeFunctionData('decimals'); + const cds = tokens.map((token) => ({ + target: token, + callData: decimalsCalldata, + })); + const decimalsRet = await chunkedMulticall(provider, cds, multicallSize); + const decimals = decimalsRet.map((r: any, idx: number) => { + if (!r.success) return 0; + + try { + return ABI_CODER.decode(['uint8'], r.returnData)[0]; + } catch (err) { + logger.info('errror decoding', r.returnData, tokens[idx]); + } + }) as number[]; + return decimals; +}; + +export const getTokenInfosFromChain = async ( + config: IConfig +): Promise => { + logger.info('getting token info from chain'); + const provider = new ethers.providers.JsonRpcProvider(config.rpcUrl); + const settlement = new ethers.Contract( + config.gpv2Settlement, + settlementAbi, + provider + ); + const tradeFilter = settlement.filters.Trade(); + const currentBlock = await provider.getBlockNumber(); + const fromBlock = currentBlock - config.lookbackRange; + logger.info(`querying trades from ${fromBlock} to ${currentBlock}`); + const logs = await chunkedQueryFilter( + settlement, + tradeFilter, + currentBlock - config.lookbackRange, + currentBlock, + config.queryLogsSize + ); + logger.info(`Found ${logs.length} trades`); + const allTokens = Array.from( + new Set( + logs.map((log) => [log.args!.sellToken, log.args!.buyToken]).flat() + ).values() + ).filter( + (x) => + x.toLowerCase() !== + '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'.toLowerCase() + ); + logger.info(`Found ${allTokens.length} unique tokens`); + + logger.info(`Getting decimals for ${allTokens.length} tokens`); + const allDecimals = await getDecimals( + provider, + allTokens, + config.multicallSize + ); + const allTokenInfo: ITokenInfo[] = allTokens.map((token, idx) => { + const decimals = allDecimals[idx]; + return { + address: token, + decimals, + symbol: '', + }; + }); + return allTokenInfo; +}; + +export const getTokenBalances = async ( + config: IConfig +): Promise => { + return getTokenInfosFromChain(config); +}; diff --git a/yarn.lock b/yarn.lock index 6f86ea7..51c8a79 100644 --- a/yarn.lock +++ b/yarn.lock @@ -572,6 +572,13 @@ resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz#56e2cc26c397c038fab0e3a917a12d5c5909e901" integrity sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA== +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + acorn-walk@^8.1.1: version "8.3.2" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.2.tgz#7703af9415f1b6db9315d6895503862e231d34aa" @@ -619,6 +626,11 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== +atomic-sleep@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" + integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== + axios@^1.6.8: version "1.6.8" resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.8.tgz#66d294951f5d988a00e87a0ffb955316a619ea66" @@ -720,6 +732,11 @@ color-name@1.1.3: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== +colorette@^2.0.7: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + combined-stream@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -744,6 +761,11 @@ cross-fetch@^3.1.5: dependencies: node-fetch "^2.6.12" +dateformat@^4.6.3: + version "4.6.3" + resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-4.6.3.tgz#556fa6497e5217fedb78821424f8a1c22fa3f4b5" + integrity sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA== + debug@^4.3.1: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" @@ -787,6 +809,13 @@ elliptic@6.5.4: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" +end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + err-code@^3.0.0, err-code@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/err-code/-/err-code-3.0.1.tgz#a444c7b992705f2b120ee320b09972eef331c920" @@ -850,6 +879,16 @@ ethers@v5: "@ethersproject/web" "5.7.1" "@ethersproject/wordlists" "5.7.0" +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + +events@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + exponential-backoff@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6" @@ -860,11 +899,26 @@ extract-files@^9.0.0: resolved "https://registry.yarnpkg.com/extract-files/-/extract-files-9.0.0.tgz#8a7744f2437f81f5ed3250ed9f1550de902fe54a" integrity sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ== +fast-copy@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/fast-copy/-/fast-copy-3.0.2.tgz#59c68f59ccbcac82050ba992e0d5c389097c9d35" + integrity sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ== + fast-deep-equal@^3.1.1: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-redact@^3.1.1: + version "3.5.0" + resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-3.5.0.tgz#e9ea02f7e57d0cd8438180083e93077e496285e4" + integrity sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A== + +fast-safe-stringify@^2.1.1: + version "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== + find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" @@ -948,6 +1002,11 @@ hasown@^2.0.0: dependencies: function-bind "^1.1.2" +help-me@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/help-me/-/help-me-5.0.0.tgz#b1ebe63b967b74060027c2ac61f9be12d354a6f6" + integrity sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg== + hmac-drbg@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" @@ -1086,6 +1145,11 @@ it-parallel-batch@^1.0.9: dependencies: it-batch "^1.0.9" +joycon@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03" + integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw== + js-sha3@0.8.0, js-sha3@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" @@ -1228,7 +1292,7 @@ minimist-options@4.1.0: is-plain-obj "^1.1.0" kind-of "^6.0.3" -minimist@^1.2.5: +minimist@^1.2.5, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -1311,6 +1375,18 @@ normalize-package-data@^3.0.0: semver "^7.3.4" validate-npm-package-license "^3.0.1" +on-exit-leak-free@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz#fed195c9ebddb7d9e4c3842f93f281ac8dadd3b8" + integrity sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA== + +once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -1355,6 +1431,66 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== +pino-abstract-transport@^1.0.0, pino-abstract-transport@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz#97f9f2631931e242da531b5c66d3079c12c9d1b5" + integrity sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q== + dependencies: + readable-stream "^4.0.0" + split2 "^4.0.0" + +pino-pretty@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/pino-pretty/-/pino-pretty-11.0.0.tgz#9b883f7b933f58fa94caa44225aab302409461ef" + integrity sha512-YFJZqw59mHIY72wBnBs7XhLGG6qpJMa4pEQTRgEPEbjIYbng2LXEZZF1DoyDg9CfejEy8uZCyzpcBXXG0oOCwQ== + dependencies: + colorette "^2.0.7" + dateformat "^4.6.3" + fast-copy "^3.0.0" + fast-safe-stringify "^2.1.1" + help-me "^5.0.0" + joycon "^3.1.1" + minimist "^1.2.6" + on-exit-leak-free "^2.1.0" + pino-abstract-transport "^1.0.0" + pump "^3.0.0" + readable-stream "^4.0.0" + secure-json-parse "^2.4.0" + sonic-boom "^3.0.0" + strip-json-comments "^3.1.1" + +pino-std-serializers@^6.0.0: + version "6.2.2" + resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz#d9a9b5f2b9a402486a5fc4db0a737570a860aab3" + integrity sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA== + +pino@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/pino/-/pino-9.0.0.tgz#25fddb6f449032afcc8c939c2234d4a24b9129a6" + integrity sha512-uI1ThkzTShNSwvsUM6b4ND8ANzWURk9zTELMztFkmnCQeR/4wkomJ+echHee5GMWGovoSfjwdeu80DsFIt7mbA== + dependencies: + atomic-sleep "^1.0.0" + fast-redact "^3.1.1" + on-exit-leak-free "^2.1.0" + pino-abstract-transport "^1.2.0" + pino-std-serializers "^6.0.0" + process-warning "^3.0.0" + quick-format-unescaped "^4.0.3" + real-require "^0.2.0" + safe-stable-stringify "^2.3.1" + sonic-boom "^3.7.0" + thread-stream "^2.6.0" + +process-warning@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-3.0.0.tgz#96e5b88884187a1dce6f5c3166d611132058710b" + integrity sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ== + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== + protobufjs@^6.10.2: version "6.11.4" resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.4.tgz#29a412c38bf70d89e537b6d02d904a6f448173aa" @@ -1379,11 +1515,24 @@ proxy-from-env@^1.1.0: resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + punycode@^2.1.0: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== +quick-format-unescaped@^4.0.3: + version "4.0.4" + resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz#93ef6dd8d3453cbc7970dd614fad4c5954d6b5a7" + integrity sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg== + quick-lru@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" @@ -1429,6 +1578,22 @@ readable-stream@^3.4.0, readable-stream@^3.6.0: string_decoder "^1.1.1" util-deprecate "^1.0.1" +readable-stream@^4.0.0: + version "4.5.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.5.2.tgz#9e7fc4c45099baeed934bff6eb97ba6cf2729e09" + integrity sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g== + dependencies: + abort-controller "^3.0.0" + buffer "^6.0.3" + events "^3.3.0" + process "^0.11.10" + string_decoder "^1.3.0" + +real-require@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/real-require/-/real-require-0.2.0.tgz#209632dea1810be2ae063a6ac084fee7e33fba78" + integrity sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg== + redent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" @@ -1456,11 +1621,21 @@ safe-buffer@~5.2.0: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +safe-stable-stringify@^2.3.1: + version "2.4.3" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" + integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== + scrypt-js@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== +secure-json-parse@^2.4.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/secure-json-parse/-/secure-json-parse-2.7.0.tgz#5a5f9cd6ae47df23dba3151edd06855d47e09862" + integrity sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw== + "semver@2 || 3 || 4 || 5": version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" @@ -1473,6 +1648,13 @@ semver@^7.3.4: dependencies: lru-cache "^6.0.0" +sonic-boom@^3.0.0, sonic-boom@^3.7.0: + version "3.8.1" + resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-3.8.1.tgz#d5ba8c4e26d6176c9a1d14d549d9ff579a163422" + integrity sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg== + dependencies: + atomic-sleep "^1.0.0" + sparse-array@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/sparse-array/-/sparse-array-1.3.2.tgz#0e1a8b71706d356bc916fe754ff496d450ec20b0" @@ -1504,12 +1686,17 @@ spdx-license-ids@^3.0.0: resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz#887da8aa73218e51a1d917502d79863161a93f9c" integrity sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg== +split2@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4" + integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg== + stable@^0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== -string_decoder@^1.1.1: +string_decoder@^1.1.1, string_decoder@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== @@ -1523,6 +1710,11 @@ strip-indent@^3.0.0: dependencies: min-indent "^1.0.0" +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -1535,6 +1727,13 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +thread-stream@^2.6.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/thread-stream/-/thread-stream-2.7.0.tgz#d8a8e1b3fd538a6cca8ce69dbe5d3d097b601e11" + integrity sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw== + dependencies: + real-require "^0.2.0" + tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" @@ -1651,6 +1850,11 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + ws@7.4.6: version "7.4.6" resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c"