From 9cc41f166f39f1b19fb7debc0a4dc5bfc851c473 Mon Sep 17 00:00:00 2001 From: Alexandr Kazachenko Date: Thu, 12 Dec 2024 12:28:00 +0500 Subject: [PATCH] feat: sdk extension for people to swap (#213) * feat: SDK extension for people to swap * feat: SDK extension for people to trade * refactor: move files to trading dir * feat: js example for trade-sdk * chore: trade sdk examples * docs: trading SDK docs * chore: encapsulate networkCostsAmount * chore: fix build * chore: fix build * chore: make networkCostsAmount optional * feat: calculate unique OrderId util * feat: support on-chain trades * chore: reduce getOrderToSign parameters * feat: support on-chain orders * test: app data utils * test: test calculateUniqueOrderId * test: test getOrderToSign * test: test getQuote * test: test postCoWProtocolTrade * test: test postLimitOrder * test: test postOnChainTrade * docs: docs for postOnChainTrade * chore: up docs * fix: update eth-flow addresses * chore: fix tests * chore: export helpers * chore: rc version * chore: fix readme * feat: avoid using signer in getQuote * chore: rc * chore: move QuoteResults to types * refactor: make QuoteResults serializable * feat: add json schemas for trading entities * chore: rc * chore: up node v * chore: remove schema gen call * chore: up app-data version * chore: bump version * feat: add order typed data to quote results * fix: fix order signing * fix: override sellToken with wrapped for eth-flow * feat(trading): function to generate eth-flow transaction * chore: export swapParamsToLimitOrderParams * fix: use hex numbers in transaction * fix(trading): fix order amounts calculation * chore: bump rc * chore: fix tests * chore: add mapQuoteAmountsAndCosts util * chore: fix types * chore: do not cache ethflow contract * test: test getEthFlowTransaction * feat(trading): add getPreSignTransaction function * chore: add more description to trading types * chore: fix test * chore: update trading sdk readme * chore: up rc * chore: fix base contract addresses * fix: make quoteId optional for limit orders * refactor: rename postOnChainTrade -> postSellNativeCurrencyTrade * docs: optional params * chore: fix tests * fix: add additional params for eth-flow quote req * feat: add getPreSignTransaction to TradingSdk * docs: get quest for smart-contract wallet * chore: rename postSellNativeCurrencyOrder * docs: create an order with smart-contract wallet * chore: fix tests --- .eslintrc.json | 2 +- .github/workflows/build.yml | 2 +- .github/workflows/publish.yml | 2 +- .github/workflows/test.yml | 2 +- abi/EthFlow.json | 152 ++++ abi/GPv2Settlement.json | 89 ++ examples/cra/tsconfig.json | 2 +- examples/nodejs/src/index.ts | 73 +- examples/vanilla/package.json | 6 +- examples/vanilla/src/formState.ts | 59 ++ examples/vanilla/src/index.ts | 90 +- examples/vanilla/src/pageActions.ts | 113 +++ examples/vanilla/src/pageHtml.ts | 84 ++ examples/vanilla/src/styles.css | 41 + examples/vanilla/src/tokens.ts | 60 ++ examples/vanilla/src/utils.ts | 3 + examples/vanilla/tsconfig.json | 4 +- examples/vanilla/webpack.config.js | 10 + examples/vanilla/yarn.lock | 497 ++-------- package.json | 13 +- scripts/generateTradingSchemas.ts | 30 + src/common/consts.ts | 34 +- src/index.ts | 1 + src/order-book/api.spec.ts | 8 +- src/order-book/quoteAmountsAndCostsUtils.ts | 2 +- src/order-book/transformOrder.ts | 4 +- src/order-signing/orderSigningUtils.ts | 31 +- src/order-signing/utils.ts | 30 +- src/trading/README.md | 421 +++++++++ src/trading/appDataUtils.test.ts | 51 ++ src/trading/appDataUtils.ts | 35 + src/trading/calculateUniqueOrderId.test.ts | 91 ++ src/trading/calculateUniqueOrderId.ts | 58 ++ src/trading/consts.ts | 13 + src/trading/getEthFlowTransaction.test.ts | 80 ++ src/trading/getEthFlowTransaction.ts | 68 ++ src/trading/getOrderToSign.test.ts | 81 ++ src/trading/getOrderToSign.ts | 68 ++ src/trading/getOrderTypedData.ts | 28 + src/trading/getPreSignTransaction.test.ts | 60 ++ src/trading/getPreSignTransaction.ts | 35 + src/trading/getQuote.test.ts | 169 ++++ src/trading/getQuote.ts | 161 ++++ src/trading/index.ts | 22 + src/trading/postCoWProtocolTrade.test.ts | 127 +++ src/trading/postCoWProtocolTrade.ts | 64 ++ src/trading/postLimitOrder.test.ts | 94 ++ src/trading/postLimitOrder.ts | 32 + .../postSellNativeCurrencyOrder.test.ts | 150 ++++ src/trading/postSellNativeCurrencyOrder.ts | 35 + src/trading/postSwapOrder.ts | 23 + src/trading/tradingSdk.ts | 62 ++ src/trading/types.ts | 165 ++++ src/trading/utils.ts | 78 ++ yarn.lock | 845 +++++++++++++++++- 55 files changed, 4097 insertions(+), 463 deletions(-) create mode 100644 abi/EthFlow.json create mode 100644 abi/GPv2Settlement.json create mode 100644 examples/vanilla/src/formState.ts create mode 100644 examples/vanilla/src/pageActions.ts create mode 100644 examples/vanilla/src/pageHtml.ts create mode 100644 examples/vanilla/src/styles.css create mode 100644 examples/vanilla/src/tokens.ts create mode 100644 examples/vanilla/src/utils.ts create mode 100644 scripts/generateTradingSchemas.ts create mode 100644 src/trading/README.md create mode 100644 src/trading/appDataUtils.test.ts create mode 100644 src/trading/appDataUtils.ts create mode 100644 src/trading/calculateUniqueOrderId.test.ts create mode 100644 src/trading/calculateUniqueOrderId.ts create mode 100644 src/trading/consts.ts create mode 100644 src/trading/getEthFlowTransaction.test.ts create mode 100644 src/trading/getEthFlowTransaction.ts create mode 100644 src/trading/getOrderToSign.test.ts create mode 100644 src/trading/getOrderToSign.ts create mode 100644 src/trading/getOrderTypedData.ts create mode 100644 src/trading/getPreSignTransaction.test.ts create mode 100644 src/trading/getPreSignTransaction.ts create mode 100644 src/trading/getQuote.test.ts create mode 100644 src/trading/getQuote.ts create mode 100644 src/trading/index.ts create mode 100644 src/trading/postCoWProtocolTrade.test.ts create mode 100644 src/trading/postCoWProtocolTrade.ts create mode 100644 src/trading/postLimitOrder.test.ts create mode 100644 src/trading/postLimitOrder.ts create mode 100644 src/trading/postSellNativeCurrencyOrder.test.ts create mode 100644 src/trading/postSellNativeCurrencyOrder.ts create mode 100644 src/trading/postSwapOrder.ts create mode 100644 src/trading/tradingSdk.ts create mode 100644 src/trading/types.ts create mode 100644 src/trading/utils.ts diff --git a/.eslintrc.json b/.eslintrc.json index b241b332..c227a5a1 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -22,5 +22,5 @@ ] }, - "ignorePatterns": ["dist", "node_modules", "src/subgraph/graphql.ts", "examples"] + "ignorePatterns": ["dist", "node_modules", "src/subgraph/graphql.ts", "examples", "schemas"] } diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 75e04ac3..ee4cd78b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ on: types: [published] env: - NODE_VERSION: lts/gallium + NODE_VERSION: lts/hydrogen jobs: build: diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 5f00a753..21963f31 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -5,7 +5,7 @@ on: types: [published] env: - NODE_VERSION: lts/gallium + NODE_VERSION: lts/hydrogen jobs: deploy: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2823405e..2b8758de 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,7 +2,7 @@ name: Unit tests & Coverage on: [push, pull_request] env: - NODE_VERSION: lts/gallium + NODE_VERSION: lts/hydrogen COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} jobs: diff --git a/abi/EthFlow.json b/abi/EthFlow.json new file mode 100644 index 00000000..2e76a24b --- /dev/null +++ b/abi/EthFlow.json @@ -0,0 +1,152 @@ +[ + { + "inputs": [ + { + "components": [ + { + "internalType": "contract IERC20", + "name": "buyToken", + "type": "address" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "sellAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "buyAmount", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "appData", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "feeAmount", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "validTo", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "partiallyFillable", + "type": "bool" + }, + { + "internalType": "int64", + "name": "quoteId", + "type": "int64" + } + ], + "internalType": "struct EthFlowOrder.Data", + "name": "order", + "type": "tuple" + } + ], + "name": "createOrder", + "outputs": [ + { + "internalType": "bytes32", + "name": "orderHash", + "type": "bytes32" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "contract IERC20", + "name": "buyToken", + "type": "address" + }, + { + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "sellAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "buyAmount", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "appData", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "feeAmount", + "type": "uint256" + }, + { + "internalType": "uint32", + "name": "validTo", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "partiallyFillable", + "type": "bool" + }, + { + "internalType": "int64", + "name": "quoteId", + "type": "int64" + } + ], + "internalType": "struct EthFlowOrder.Data", + "name": "order", + "type": "tuple" + } + ], + "name": "invalidateOrder", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "orders", + "outputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint32", + "name": "validTo", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/abi/GPv2Settlement.json b/abi/GPv2Settlement.json new file mode 100644 index 00000000..da5a9a26 --- /dev/null +++ b/abi/GPv2Settlement.json @@ -0,0 +1,89 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "contract IERC20", + "name": "sellToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "contract IERC20", + "name": "buyToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "sellAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "buyAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "feeAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bytes", + "name": "orderUid", + "type": "bytes" + } + ], + "name": "Trade", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "orderUid", + "type": "bytes" + }, + { + "internalType": "bool", + "name": "signed", + "type": "bool" + } + ], + "name": "setPreSignature", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "orderUid", + "type": "bytes" + } + ], + "name": "invalidateOrder", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "domainSeparator", + "outputs": [{ "name": "", "type": "bytes32" }], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/examples/cra/tsconfig.json b/examples/cra/tsconfig.json index 9c7d4a8a..98509a17 100644 --- a/examples/cra/tsconfig.json +++ b/examples/cra/tsconfig.json @@ -20,7 +20,7 @@ "isolatedModules": true, "noEmit": true, "jsx": "react-jsx" - }, + }, "include": [ "src" ] diff --git a/examples/nodejs/src/index.ts b/examples/nodejs/src/index.ts index 6e29fef8..e5165777 100644 --- a/examples/nodejs/src/index.ts +++ b/examples/nodejs/src/index.ts @@ -1,16 +1,69 @@ -import { OrderBookApi, SubgraphApi, SupportedChainId } from '@cowprotocol/cow-sdk' +import { SupportedChainId, OrderKind, postSwapOrder, postLimitOrder } from '../../../src' -// See more examples in /examples/cra +const privateKey = 'xxx' + +// Swap ;(async function () { - const orderBookApi = new OrderBookApi({ chainId: SupportedChainId.MAINNET }) - const subgraphApi = new SubgraphApi({ chainId: SupportedChainId.MAINNET }) + return - const order = await orderBookApi.getOrder( - '0xff2e2e54d178997f173266817c1e9ed6fee1a1aae4b43971c53b543cffcc2969845c6f5599fbb25dbdd1b9b013daf85c03f3c63763e4bc4a' - ) + postSwapOrder({ + appCode: 'cow-sdk-example', + signer: privateKey, + chainId: SupportedChainId.SEPOLIA, + + kind: OrderKind.SELL, + sellToken: '0xfff9976782d46cc05630d1f6ebab18b2324d6b14', + sellTokenDecimals: 18, + buyToken: '0x0625afb445c3b6b7b929342a04a22599fd5dbb59', + buyTokenDecimals: 18, + amount: '120000000000000000', + }) +})() + +// Limit order +;(async function () { + return - const lastDaysVolume = await subgraphApi.getTotals() + postLimitOrder({ + appCode: 'cow-sdk-example', + signer: privateKey, + chainId: SupportedChainId.SEPOLIA, - console.log('[orderBookApi] Order: ', order) - console.log('[subgraphApi] Last day volume: ', lastDaysVolume) + kind: OrderKind.BUY, + sellToken: '0xfff9976782d46cc05630d1f6ebab18b2324d6b14', + sellTokenDecimals: 18, + buyToken: '0x0625afb445c3b6b7b929342a04a22599fd5dbb59', + buyTokenDecimals: 18, + sellAmount: '120000000000000000', + buyAmount: '66600000000000000000', + networkCostsAmount: '0', + }) +})() + +// Swap with partner fee +;(async function () { + postSwapOrder( + { + appCode: 'cow-sdk-example', + signer: privateKey, + chainId: SupportedChainId.SEPOLIA, + + kind: OrderKind.SELL, + sellToken: '0xfff9976782d46cc05630d1f6ebab18b2324d6b14', + sellTokenDecimals: 18, + buyToken: '0x0625afb445c3b6b7b929342a04a22599fd5dbb59', + buyTokenDecimals: 18, + amount: '120000000000000000', + }, + { + appData: { + metadata: { + partnerFee: { + bps: 100, + recipient: '0xfb3c7eb936cAA12B5A884d612393969A557d4307', + }, + }, + }, + } + ) })() diff --git a/examples/vanilla/package.json b/examples/vanilla/package.json index 61e5b4a2..699c7253 100644 --- a/examples/vanilla/package.json +++ b/examples/vanilla/package.json @@ -7,13 +7,13 @@ "start": "webpack serve --mode=development", "test": "echo \"Error: no test specified\" && exit 1" }, - "dependencies": { - "@cowprotocol/cow-sdk": "^4.0.3" - }, + "dependencies": {}, "author": "", "license": "ISC", "devDependencies": { + "css-loader": "^7.1.2", "html-webpack-plugin": "^5.5.0", + "style-loader": "^4.0.0", "ts-loader": "^9.4.2", "webpack": "^5.76.3", "webpack-cli": "^5.0.1", diff --git a/examples/vanilla/src/formState.ts b/examples/vanilla/src/formState.ts new file mode 100644 index 00000000..4ee1f8b2 --- /dev/null +++ b/examples/vanilla/src/formState.ts @@ -0,0 +1,59 @@ +import { OrderKind, SupportedChainId, SwapParameters, TradeParameters } from '../../../src' +import { TOKENS } from './tokens' + +interface FormState { + privateKey: string + chainId: string + sellToken: string + buyToken: string + amount: string + slippageBps: string + kind: 'sell' | 'buy' +} + +const DECIMALS_SHIFT = 6 + +/** + * This function converts input amount to the correct number of decimals. + * For example, if the input amount is 1.23 and the token has 18 decimals, + * the result will be 1230000000000000000. + * Since this is a simplified example, we only allow input amounts with maximum 6 decimals. + */ +const adjustDecimals = (amount: number, decimals: number) => { + const multiplicator = decimals > DECIMALS_SHIFT ? DECIMALS_SHIFT : 0 + + return BigInt(amount * 10 ** multiplicator) * BigInt(10 ** (decimals - multiplicator)) +} + +export const getFormState = (): FormState => { + return Object.fromEntries(new FormData(document.getElementById('form') as HTMLFormElement)) as unknown as FormState +} + +export const getTradeParameters = (): TradeParameters => { + const { + slippageBps: _slippageBps, + chainId: _chainId, + sellToken: _sellToken, + buyToken: _buyToken, + amount: _amount, + kind, + } = getFormState() + + const chainId: SupportedChainId = +_chainId + const isSell = kind === 'sell' + const sellToken = TOKENS[chainId].find((t) => t.address === _sellToken) + const buyToken = TOKENS[chainId].find((t) => t.address === _buyToken) + const decimals = isSell ? sellToken.decimals : buyToken.decimals + const amount = adjustDecimals(+_amount, decimals) + const slippageBps = _slippageBps ? +_slippageBps : undefined + + return { + sellToken: sellToken.address, + sellTokenDecimals: sellToken.decimals, + buyToken: buyToken.address, + buyTokenDecimals: buyToken.decimals, + amount: amount.toString(), + slippageBps, + kind: isSell ? OrderKind.SELL : OrderKind.BUY, + } +} diff --git a/examples/vanilla/src/index.ts b/examples/vanilla/src/index.ts index aa9e69a7..e8fa0cc5 100644 --- a/examples/vanilla/src/index.ts +++ b/examples/vanilla/src/index.ts @@ -1,21 +1,85 @@ -import { OrderBookApi, SubgraphApi, SupportedChainId } from '@cowprotocol/cow-sdk' +import { SupportedChainId } from '../../../src' -// See more examples in /examples/cra +import { TradingSdk, QuoteAndPost } from '../../../src/trading' + +import { TOKENS } from './tokens' +import { atomsToAmount } from './utils' +import { pageHtml } from './pageHtml' +import { pageActions, printResult } from './pageActions' +import { getFormState, getTradeParameters } from './formState' +import './styles.css' + +const appCode = 'trade-sdk-example' + +// Run the example ;(async function () { - const orderBookApi = new OrderBookApi({ chainId: SupportedChainId.MAINNET }) - const subgraphApi = new SubgraphApi({ chainId: SupportedChainId.MAINNET }) + let quoteAndPost: QuoteAndPost | null = null + + // Render page + const page = pageHtml() + document.body.appendChild(page) + + // Bind actions to the page + pageActions({ + onFormReset() { + quoteAndPost = null + }, + async onGetQuote() { + const { + slippageBps: _slippageBps, + chainId: _chainId, + sellToken: _sellToken, + buyToken: _buyToken, + amount: _amount, + kind, + privateKey, + } = getFormState() + + const chainId: SupportedChainId = +_chainId + const isSell = kind === 'sell' + const sellToken = TOKENS[chainId].find((t) => t.address === _sellToken) + const buyToken = TOKENS[chainId].find((t) => t.address === _buyToken) + + const sdk = new TradingSdk({ + chainId, + signer: privateKey || (window as any).ethereum, + appCode, + }) + + quoteAndPost = await sdk.getQuote(getTradeParameters()) + + const { + amountsAndCosts: { beforeNetworkCosts, afterSlippage }, + } = quoteAndPost.quoteResults - const order = await orderBookApi.getOrder( - '0xff2e2e54d178997f173266817c1e9ed6fee1a1aae4b43971c53b543cffcc2969845c6f5599fbb25dbdd1b9b013daf85c03f3c63763e4bc4a' - ) + console.log('Quote results:', quoteAndPost.quoteResults) - const lastDaysVolume = await subgraphApi.getTotals() + const outputToken = isSell ? buyToken : sellToken - const orderElement = document.createElement('textarea') + printResult(` + Quote amount: ${atomsToAmount( + beforeNetworkCosts[isSell ? 'buyAmount' : 'sellAmount'], + outputToken.decimals + )} ${outputToken.symbol} + Amount to sign: ${atomsToAmount( + afterSlippage[isSell ? 'buyAmount' : 'sellAmount'], + outputToken.decimals + )} ${outputToken.symbol} + See more info in the console (Quote results) + `) + }, + async onConfirmOrder() { + const orderToSign = quoteAndPost.quoteResults.orderToSign - orderElement.value = JSON.stringify({ order, lastDaysVolume }, null, 4) - orderElement.style.minWidth = '950px' - orderElement.style.minHeight = '800px' + printResult(` + You are going to sign: + ${JSON.stringify(orderToSign, null, 4)} + `) + }, + async onSignAndSendOrder() { + const orderId = await quoteAndPost.postSwapOrderFromQuote() - document.body.appendChild(orderElement) + printResult(`Order created, id: ${orderId}`) + }, + }) })() diff --git a/examples/vanilla/src/pageActions.ts b/examples/vanilla/src/pageActions.ts new file mode 100644 index 00000000..f8e0b5bf --- /dev/null +++ b/examples/vanilla/src/pageActions.ts @@ -0,0 +1,113 @@ +import { tokensSelect } from './pageHtml' + +const sendOrderText = 'Send order' + +interface Actions { + onFormReset(): void + onGetQuote(): Promise + onConfirmOrder(): Promise + onSignAndSendOrder(): Promise +} + +export function pageActions(actions: Actions) { + onFormReset(actions.onFormReset) + onNetworkChange() + onGetQuote(actions) +} + +export function printResult(text: string) { + const resultsEl = document.getElementById('results')! as HTMLTextAreaElement + + resultsEl.value = text + .split('\n') + .map((t) => t.trim()) + .join('\n') +} + +function onFormReset(callback: () => void) { + document.getElementById('form')?.addEventListener('change', () => { + printResult('') + + document.getElementById('sendOrder').innerText = sendOrderText + callback() + }) +} + +function onNetworkChange() { + document.getElementById('chainId').addEventListener('change', (event) => { + const chainId = +(event.target as unknown as { value: string }).value + + document.getElementById('sellToken')!.parentElement.innerHTML = tokensSelect(chainId, 'sellToken') + document.getElementById('buyToken')!.parentElement.innerHTML = tokensSelect(chainId, 'buyToken') + }) +} + +function onGetQuote(actions: Actions) { + const connectWallet = document.getElementById('connectWallet') as HTMLButtonElement + + connectWallet.addEventListener('click', async (event) => { + event.preventDefault() + + try { + const accounts: string[] = await (window as any).ethereum.request({ method: 'eth_requestAccounts' }) + + if (accounts.length) { + connectWallet.disabled = true + connectWallet.innerText = 'Connected' + } + } catch (error) { + printError(error) + } + }) + + document.getElementById('getQuote').addEventListener('click', (event) => { + event.preventDefault() + + printResult('Loading...') + + const sendOrderEl = document.getElementById('sendOrder') + + sendOrderEl.innerText = sendOrderText + + actions.onFormReset() + + actions + .onGetQuote() + .then(() => { + const sendOrderEl = document.getElementById('sendOrder') as HTMLButtonElement + + sendOrderEl.style.display = 'inline-block' + + sendOrderEl.addEventListener('click', async (event) => { + event.preventDefault() + + sendOrderEl.disabled = true + sendOrderEl.innerText = 'Loading...' + + try { + if (sendOrderEl.innerText === sendOrderText) { + await actions.onConfirmOrder() + + sendOrderEl.innerText = 'Sign and send' + } else { + await actions.onSignAndSendOrder() + + sendOrderEl.innerText = sendOrderText + sendOrderEl.style.display = 'none' + } + } catch (error) { + printError(error) + } finally { + sendOrderEl.disabled = false + } + }) + }) + .catch((error) => { + printError(error) + }) + }) +} + +function printError(error: any) { + printResult(JSON.stringify(error.body || error.message || error.toString(), null, 4)) +} diff --git a/examples/vanilla/src/pageHtml.ts b/examples/vanilla/src/pageHtml.ts new file mode 100644 index 00000000..868712f0 --- /dev/null +++ b/examples/vanilla/src/pageHtml.ts @@ -0,0 +1,84 @@ +import { ALL_SUPPORTED_CHAIN_IDS, SupportedChainId } from '../../../src' +import { TOKENS } from './tokens' + +const chainId = SupportedChainId.MAINNET + +export const tokensSelect = (chainId: SupportedChainId, name: string) => + `` + +export function pageHtml() { + const page = document.createElement('div') + + const networksSelect = () => + `` + + page.innerHTML = ` + + + + + +
+
+

Swap

+ +
+ +
+
+ or + +
+ +
+ +
${networksSelect()}
+
+ +
+ +
${tokensSelect(chainId, 'sellToken')}
+
+ +
+ +
${tokensSelect(chainId, 'buyToken')}
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ + +
+
+
+

Result:

+ +
+ ` + + return page +} diff --git a/examples/vanilla/src/styles.css b/examples/vanilla/src/styles.css new file mode 100644 index 00000000..3e6ccb54 --- /dev/null +++ b/examples/vanilla/src/styles.css @@ -0,0 +1,41 @@ +#layout { + margin: 0 auto; +} + +#layout td { + vertical-align: top; +} + +#form { + min-width: 400px; +} + +#form > div { + border: 1px solid #d4d9de; + margin: 10px; + padding: 10px; +} + +#form input, #form select { + width: 100%; + box-sizing: border-box; +} + +#form button { + background: #282c34; + color: #fff; + font-size: 18px; + padding: 5px 10px; + cursor: pointer; + border: 0; + border-radius: 4px; +} + +#results { + min-height: 400px; + min-width: 600px; +} + +#sendOrder { + display: none; +} diff --git a/examples/vanilla/src/tokens.ts b/examples/vanilla/src/tokens.ts new file mode 100644 index 00000000..acf489bc --- /dev/null +++ b/examples/vanilla/src/tokens.ts @@ -0,0 +1,60 @@ +import { ETH_ADDRESS, SupportedChainId } from '../../../src' + +class Token { + constructor( + public readonly chainId: SupportedChainId, + public readonly address: string, + public readonly decimals: number, + public readonly symbol: string, + public readonly name: string + ) {} +} + +export const TOKENS: Record = { + [SupportedChainId.MAINNET]: [ + new Token(SupportedChainId.MAINNET, ETH_ADDRESS, 18, 'ETH', 'Ether'), + new Token(SupportedChainId.MAINNET, '0xdAC17F958D2ee523a2206206994597C13D831ec7', 6, 'USDT', 'Tether USD'), + new Token(SupportedChainId.MAINNET, '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', 8, 'WBTC', 'Wrapped BTC'), + new Token(SupportedChainId.MAINNET, '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', 6, 'USDC', 'USD Coin'), + new Token(SupportedChainId.MAINNET, '0xDEf1CA1fb7FBcDC777520aa7f396b4E015F497aB', 18, 'COW', 'CoW Protocol Token'), + ], + [SupportedChainId.GNOSIS_CHAIN]: [ + new Token(SupportedChainId.MAINNET, ETH_ADDRESS, 18, 'xDAI', 'xDAI'), + new Token(SupportedChainId.GNOSIS_CHAIN, '0x4ECaBa5870353805a9F068101A40E0f32ed605C6', 6, 'USDT', 'Tether USD'), + new Token(SupportedChainId.GNOSIS_CHAIN, '0x8e5bbbb09ed1ebde8674cda39a0c169401db4252', 8, 'WBTC', 'Wrapped BTC'), + new Token(SupportedChainId.GNOSIS_CHAIN, '0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83', 6, 'USDC', 'USD Coin'), + new Token( + SupportedChainId.GNOSIS_CHAIN, + '0x177127622c4A00F3d409B75571e12cB3c8973d3c', + 18, + 'COW', + 'CoW Protocol Token' + ), + ], + [SupportedChainId.ARBITRUM_ONE]: [ + new Token(SupportedChainId.ARBITRUM_ONE, ETH_ADDRESS, 18, 'ETH', 'Ether'), + new Token(SupportedChainId.ARBITRUM_ONE, '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9', 6, 'USDT', 'Tether USD'), + new Token(SupportedChainId.ARBITRUM_ONE, '0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f', 8, 'WBTC', 'Wrapped BTC'), + new Token(SupportedChainId.ARBITRUM_ONE, '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', 6, 'USDC', 'USD Coin'), + new Token( + SupportedChainId.ARBITRUM_ONE, + '0xcb8b5cd20bdcaea9a010ac1f8d835824f5c87a04', + 18, + 'COW', + 'CoW Protocol Token' + ), + ], + [SupportedChainId.SEPOLIA]: [ + new Token(SupportedChainId.SEPOLIA, ETH_ADDRESS, 18, 'ETH', 'Ether'), + new Token(SupportedChainId.SEPOLIA, '0x58eb19ef91e8a6327fed391b51ae1887b833cc91', 6, 'USDT', 'Tether USD'), + new Token(SupportedChainId.SEPOLIA, '0xd3f3d46FeBCD4CdAa2B83799b7A5CdcB69d135De', 18, 'GNO', 'GNO (test)'), + new Token(SupportedChainId.SEPOLIA, '0xbe72E441BF55620febc26715db68d3494213D8Cb', 18, 'USDC', 'USDC (test)'), + new Token(SupportedChainId.SEPOLIA, '0x0625aFB445C3B6B7B929342a04A22599fd5dBB59', 18, 'COW', 'CoW Protocol Token'), + ], + [SupportedChainId.BASE]: [ + new Token(SupportedChainId.BASE, ETH_ADDRESS, 18, 'ETH', 'Ether'), + new Token(SupportedChainId.BASE, '0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2', 6, 'USDT', 'Tether USD'), + new Token(SupportedChainId.BASE, '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', 6, 'USDC', 'USD Coin'), + new Token(SupportedChainId.BASE, '0x60a3e35cc302bfa44cb288bc5a4f316fdb1adb42', 6, 'EURC', 'EURC'), + ], +} diff --git a/examples/vanilla/src/utils.ts b/examples/vanilla/src/utils.ts new file mode 100644 index 00000000..992372d0 --- /dev/null +++ b/examples/vanilla/src/utils.ts @@ -0,0 +1,3 @@ +export function atomsToAmount(value: bigint, decimals: number): string { + return (value / BigInt(10 ** decimals)).toString() +} diff --git a/examples/vanilla/tsconfig.json b/examples/vanilla/tsconfig.json index 207d4d4b..bf27f2fc 100644 --- a/examples/vanilla/tsconfig.json +++ b/examples/vanilla/tsconfig.json @@ -2,8 +2,8 @@ "compilerOptions": { "outDir": "./dist/", "noImplicitAny": true, - "module": "es6", - "target": "es5", + "module": "esnext", + "target": "esnext", "allowJs": true, "moduleResolution": "node" } diff --git a/examples/vanilla/webpack.config.js b/examples/vanilla/webpack.config.js index a84fe8c8..98e03dcc 100644 --- a/examples/vanilla/webpack.config.js +++ b/examples/vanilla/webpack.config.js @@ -9,11 +9,21 @@ module.exports = { }, module: { rules: [ + { + test: /\.css$/i, + use: ['style-loader', 'css-loader'], + }, { test: /\.tsx?$/, use: 'ts-loader', exclude: /node_modules/, }, + { + test: /\.m?js$/, + resolve: { + fullySpecified: false, + }, + }, ], }, resolve: { diff --git a/examples/vanilla/yarn.lock b/examples/vanilla/yarn.lock index 93f507bf..3cdb7399 100644 --- a/examples/vanilla/yarn.lock +++ b/examples/vanilla/yarn.lock @@ -2,207 +2,11 @@ # yarn lockfile v1 -"@cowprotocol/contracts@^1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@cowprotocol/contracts/-/contracts-1.4.0.tgz#e93e5f25aac76feeaa348fa57231903274676247" - integrity sha512-XLs3SlPmXD4lbiWIO7mxxuCn1eE5isuO6EUlE1cj17HqN/wukDAN0xXYPx6umOH/XdjGS33miMiPHELEyY9siw== - -"@cowprotocol/cow-sdk@^4.0.3": - version "4.0.3" - resolved "https://registry.yarnpkg.com/@cowprotocol/cow-sdk/-/cow-sdk-4.0.3.tgz#bd4da3e1821c33e6f5a29e04786aaa16c346bb02" - integrity sha512-bEGpHwfFpUv4he5kH99gmmJU1kZaqRH4JUCeFqIzZAxey86i+qLzVS00r3GDw5o/tKYY/0677hgusH2srr8MZw== - dependencies: - "@cowprotocol/contracts" "^1.4.0" - "@ethersproject/abstract-signer" "^5.7.0" - "@openzeppelin/merkle-tree" "^1.0.5" - cross-fetch "^3.1.5" - exponential-backoff "^3.1.1" - graphql "^16.3.0" - graphql-request "^4.3.0" - limiter "^2.1.0" - "@discoveryjs/json-ext@^0.5.0": version "0.5.7" resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== -"@ethersproject/abi@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" - integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA== - dependencies: - "@ethersproject/address" "^5.7.0" - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/constants" "^5.7.0" - "@ethersproject/hash" "^5.7.0" - "@ethersproject/keccak256" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/strings" "^5.7.0" - -"@ethersproject/abstract-provider@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz#b0a8550f88b6bf9d51f90e4795d48294630cb9ef" - integrity sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw== - dependencies: - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/networks" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/transactions" "^5.7.0" - "@ethersproject/web" "^5.7.0" - -"@ethersproject/abstract-signer@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz#13f4f32117868452191a4649723cb086d2b596b2" - integrity sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ== - dependencies: - "@ethersproject/abstract-provider" "^5.7.0" - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - -"@ethersproject/address@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.7.0.tgz#19b56c4d74a3b0a46bfdbb6cfcc0a153fc697f37" - integrity sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA== - dependencies: - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/keccak256" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/rlp" "^5.7.0" - -"@ethersproject/base64@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.7.0.tgz#ac4ee92aa36c1628173e221d0d01f53692059e1c" - integrity sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ== - dependencies: - "@ethersproject/bytes" "^5.7.0" - -"@ethersproject/bignumber@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.7.0.tgz#e2f03837f268ba655ffba03a57853e18a18dc9c2" - integrity sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw== - dependencies: - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - bn.js "^5.2.1" - -"@ethersproject/bytes@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.7.0.tgz#a00f6ea8d7e7534d6d87f47188af1148d71f155d" - integrity sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A== - dependencies: - "@ethersproject/logger" "^5.7.0" - -"@ethersproject/constants@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.7.0.tgz#df80a9705a7e08984161f09014ea012d1c75295e" - integrity sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA== - dependencies: - "@ethersproject/bignumber" "^5.7.0" - -"@ethersproject/hash@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.7.0.tgz#eb7aca84a588508369562e16e514b539ba5240a7" - integrity sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g== - dependencies: - "@ethersproject/abstract-signer" "^5.7.0" - "@ethersproject/address" "^5.7.0" - "@ethersproject/base64" "^5.7.0" - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/keccak256" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/strings" "^5.7.0" - -"@ethersproject/keccak256@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.7.0.tgz#3186350c6e1cd6aba7940384ec7d6d9db01f335a" - integrity sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg== - dependencies: - "@ethersproject/bytes" "^5.7.0" - js-sha3 "0.8.0" - -"@ethersproject/logger@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.7.0.tgz#6ce9ae168e74fecf287be17062b590852c311892" - integrity sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig== - -"@ethersproject/networks@^5.7.0": - version "5.7.1" - resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.7.1.tgz#118e1a981d757d45ccea6bb58d9fd3d9db14ead6" - integrity sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ== - dependencies: - "@ethersproject/logger" "^5.7.0" - -"@ethersproject/properties@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.7.0.tgz#a6e12cb0439b878aaf470f1902a176033067ed30" - integrity sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw== - dependencies: - "@ethersproject/logger" "^5.7.0" - -"@ethersproject/rlp@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.7.0.tgz#de39e4d5918b9d74d46de93af80b7685a9c21304" - integrity sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w== - dependencies: - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - -"@ethersproject/signing-key@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.7.0.tgz#06b2df39411b00bc57c7c09b01d1e41cf1b16ab3" - integrity sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q== - dependencies: - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - bn.js "^5.2.1" - elliptic "6.5.4" - hash.js "1.1.7" - -"@ethersproject/strings@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.7.0.tgz#54c9d2a7c57ae8f1205c88a9d3a56471e14d5ed2" - integrity sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg== - dependencies: - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/constants" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - -"@ethersproject/transactions@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.7.0.tgz#91318fc24063e057885a6af13fdb703e1f993d3b" - integrity sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ== - dependencies: - "@ethersproject/address" "^5.7.0" - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/constants" "^5.7.0" - "@ethersproject/keccak256" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/rlp" "^5.7.0" - "@ethersproject/signing-key" "^5.7.0" - -"@ethersproject/web@^5.7.0": - version "5.7.1" - resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.7.1.tgz#de1f285b373149bee5928f4eb7bcb87ee5fbb4ae" - integrity sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w== - dependencies: - "@ethersproject/base64" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/strings" "^5.7.0" - "@jridgewell/gen-mapping@^0.3.0": version "0.3.2" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" @@ -248,46 +52,6 @@ resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== -"@noble/hashes@1.2.0", "@noble/hashes@~1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12" - integrity sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ== - -"@noble/secp256k1@1.7.1", "@noble/secp256k1@~1.7.0": - version "1.7.1" - resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c" - integrity sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw== - -"@openzeppelin/merkle-tree@^1.0.5": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@openzeppelin/merkle-tree/-/merkle-tree-1.0.5.tgz#4836d377777a7e39f31674f06ec3d6909def7913" - integrity sha512-JkwG2ysdHeIphrScNxYagPy6jZeNONgDRyqU6lbFgE8HKCZFSkcP8r6AjZs+3HZk4uRNV0kNBBzuWhKQ3YV7Kw== - dependencies: - "@ethersproject/abi" "^5.7.0" - ethereum-cryptography "^1.1.2" - -"@scure/base@~1.1.0": - version "1.1.3" - resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.3.tgz#8584115565228290a6c6c4961973e0903bb3df2f" - integrity sha512-/+SgoRjLq7Xlf0CWuLHq2LUZeL/w65kfzAPG5NH9pcmBhs+nunQTn4gvdwgMTIXnt9b2C/1SeL2XiysZEyIC9Q== - -"@scure/bip32@1.1.5": - version "1.1.5" - resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.1.5.tgz#d2ccae16dcc2e75bc1d75f5ef3c66a338d1ba300" - integrity sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw== - dependencies: - "@noble/hashes" "~1.2.0" - "@noble/secp256k1" "~1.7.0" - "@scure/base" "~1.1.0" - -"@scure/bip39@1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.1.1.tgz#b54557b2e86214319405db819c4b6a370cf340c5" - integrity sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg== - dependencies: - "@noble/hashes" "~1.2.0" - "@scure/base" "~1.1.0" - "@types/body-parser@*": version "1.19.2" resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" @@ -672,11 +436,6 @@ array-flatten@^2.1.2: resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== - balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -692,16 +451,6 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -bn.js@^4.11.9: - version "4.12.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" - integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== - -bn.js@^5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" - integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== - body-parser@1.20.1: version "1.20.1" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" @@ -750,11 +499,6 @@ braces@^3.0.2, braces@~3.0.2: dependencies: fill-range "^7.0.1" -brorand@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" - integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== - browserslist@^4.14.5: version "4.21.5" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" @@ -862,13 +606,6 @@ colorette@^2.0.10, colorette@^2.0.14: resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== -combined-stream@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -941,13 +678,6 @@ core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== -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.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -957,6 +687,20 @@ cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +css-loader@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-7.1.2.tgz#64671541c6efe06b0e22e750503106bdd86880f8" + integrity sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA== + dependencies: + icss-utils "^5.1.0" + postcss "^8.4.33" + postcss-modules-extract-imports "^3.1.0" + postcss-modules-local-by-default "^4.0.5" + postcss-modules-scope "^3.2.0" + postcss-modules-values "^4.0.0" + postcss-value-parser "^4.2.0" + semver "^7.5.4" + css-select@^4.1.3: version "4.3.0" resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b" @@ -973,6 +717,11 @@ css-what@^6.0.1: resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + debug@2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -999,11 +748,6 @@ define-lazy-prop@^2.0.0: resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== - depd@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" @@ -1091,19 +835,6 @@ electron-to-chromium@^1.4.284: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.341.tgz#ab31e9e57ef7758a14c7a7977a1978d599514470" integrity sha512-R4A8VfUBQY9WmAhuqY5tjHRf5fH2AAf6vqitBOE0y6u2PgHgqHSrhZmu78dIX3fVZtjqlwJNX1i2zwC3VpHtQQ== -elliptic@6.5.4: - version "6.5.4" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" - integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== - dependencies: - bn.js "^4.11.9" - brorand "^1.1.0" - hash.js "^1.0.0" - hmac-drbg "^1.0.1" - inherits "^2.0.4" - minimalistic-assert "^1.0.1" - minimalistic-crypto-utils "^1.0.1" - encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -1172,16 +903,6 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== -ethereum-cryptography@^1.1.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-1.2.0.tgz#5ccfa183e85fdaf9f9b299a79430c044268c9b3a" - integrity sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw== - dependencies: - "@noble/hashes" "1.2.0" - "@noble/secp256k1" "1.7.1" - "@scure/bip32" "1.1.5" - "@scure/bip39" "1.1.1" - eventemitter3@^4.0.0: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" @@ -1207,11 +928,6 @@ execa@^5.0.0: signal-exit "^3.0.3" strip-final-newline "^2.0.0" -exponential-backoff@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6" - integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw== - express@^4.17.3: version "4.18.2" resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" @@ -1249,11 +965,6 @@ express@^4.17.3: utils-merge "1.0.1" vary "~1.1.2" -extract-files@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/extract-files/-/extract-files-9.0.0.tgz#8a7744f2437f81f5ed3250ed9f1550de902fe54a" - integrity sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ== - fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -1309,15 +1020,6 @@ follow-redirects@^1.0.0: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== -form-data@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" - integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -1391,20 +1093,6 @@ graceful-fs@^4.1.2, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== -graphql-request@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-4.3.0.tgz#b934e08fcae764aa2cdc697d3c821f046cb5dbf2" - integrity sha512-2v6hQViJvSsifK606AliqiNiijb1uwWp6Re7o0RTyH+uRTv/u7Uqm2g4Fjq/LgZIzARB38RZEvVBFOQOVdlBow== - dependencies: - cross-fetch "^3.1.5" - extract-files "^9.0.0" - form-data "^3.0.0" - -graphql@^16.3.0: - version "16.6.0" - resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.6.0.tgz#c2dcffa4649db149f6282af726c8c83f1c7c5fdb" - integrity sha512-KPIBPDlW7NxrbT/eh4qPXz5FiFdL5UbaA0XUNz2Rp3Z3hqBSkbj0GVjwFDztsWVauZUWsbKHgMg++sk8UX0bkw== - handle-thing@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" @@ -1427,28 +1115,11 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" -hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3: - version "1.1.7" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" - integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== - dependencies: - inherits "^2.0.3" - minimalistic-assert "^1.0.1" - he@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== -hmac-drbg@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" - integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== - dependencies: - hash.js "^1.0.3" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.1" - hpack.js@^2.1.6: version "2.1.6" resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" @@ -1561,6 +1232,11 @@ iconv-lite@0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" +icss-utils@^5.0.0, icss-utils@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" + integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== + import-local@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" @@ -1577,7 +1253,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -1686,11 +1362,6 @@ jest-worker@^27.4.5: merge-stream "^2.0.0" supports-color "^8.0.0" -js-sha3@0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" - integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== - json-parse-even-better-errors@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" @@ -1706,11 +1377,6 @@ json-schema-traverse@^1.0.0: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== -just-performance@4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/just-performance/-/just-performance-4.3.0.tgz#cc2bc8c9227f09e97b6b1df4cd0de2df7ae16db1" - integrity sha512-L7RjvtJsL0QO8xFs5wEoDDzzJwoiowRw6Rn/GnvldlchS2JQr9wFYPiwZcDfrbbujEKqKN0tvENdbjXdYhDp5Q== - kind-of@^6.0.2: version "6.0.3" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" @@ -1724,13 +1390,6 @@ launch-editor@^2.6.0: picocolors "^1.0.0" shell-quote "^1.7.3" -limiter@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/limiter/-/limiter-2.1.0.tgz#d38d7c5b63729bb84fb0c4d8594b7e955a5182a2" - integrity sha512-361TYz6iay6n+9KvUUImqdLuFigK+K79qrUtBsXhJTLdH4rIt/r1y8r1iozwh8KbZNpujbFTSh74mJ7bwbAMOw== - dependencies: - just-performance "4.3.0" - loader-runner@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" @@ -1802,7 +1461,7 @@ mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: +mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -1819,16 +1478,11 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: +minimalistic-assert@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== -minimalistic-crypto-utils@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" - integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== - minimatch@^3.1.1: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -1859,6 +1513,11 @@ multicast-dns@^7.2.5: dns-packet "^5.2.2" thunky "^1.0.2" +nanoid@^3.3.7: + version "3.3.7" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" + integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== + negotiator@0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" @@ -1877,13 +1536,6 @@ no-case@^3.0.4: lower-case "^2.0.2" tslib "^2.0.3" -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-forge@^1: version "1.3.1" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" @@ -2048,6 +1700,56 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +postcss-modules-extract-imports@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz#b4497cb85a9c0c4b5aabeb759bb25e8d89f15002" + integrity sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q== + +postcss-modules-local-by-default@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz#f1b9bd757a8edf4d8556e8d0f4f894260e3df78f" + integrity sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw== + dependencies: + icss-utils "^5.0.0" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.1.0" + +postcss-modules-scope@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz#a43d28289a169ce2c15c00c4e64c0858e43457d5" + integrity sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ== + dependencies: + postcss-selector-parser "^6.0.4" + +postcss-modules-values@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c" + integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ== + dependencies: + icss-utils "^5.0.0" + +postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: + version "6.1.0" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.1.0.tgz#49694cb4e7c649299fea510a29fa6577104bcf53" + integrity sha512-UMz42UD0UY0EApS0ZL9o1XnLhSTtvvvLe5Dc2H2O56fvRZi+KulDyf5ctDhhtYJBGKStV2FL1fy6253cmLgqVQ== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + +postcss@^8.4.33: + version "8.4.38" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.38.tgz#b387d533baf2054288e337066d81c6bee9db9e0e" + integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A== + dependencies: + nanoid "^3.3.7" + picocolors "^1.0.0" + source-map-js "^1.2.0" + pretty-error@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-4.0.0.tgz#90a703f46dd7234adb46d0f84823e9d1cb8f10d6" @@ -2251,6 +1953,11 @@ semver@^7.3.4: dependencies: lru-cache "^6.0.0" +semver@^7.5.4: + version "7.6.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" + integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== + send@0.18.0: version "0.18.0" resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" @@ -2357,6 +2064,11 @@ sockjs@^0.3.24: uuid "^8.3.2" websocket-driver "^0.7.4" +source-map-js@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" + integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== + source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" @@ -2429,6 +2141,11 @@ strip-final-newline@^2.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== +style-loader@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-4.0.0.tgz#0ea96e468f43c69600011e0589cb05c44f3b17a5" + integrity sha512-1V4WqhhZZgjVAVJyt7TdDPZoPBPNHbekX4fWnCJL1yQukhCeZhJySUL+gL9y6sNdN95uEOS83Y55SqHcP7MzLA== + supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" @@ -2491,11 +2208,6 @@ toidentifier@1.0.1: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== - ts-loader@^9.4.2: version "9.4.2" resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.4.2.tgz#80a45eee92dd5170b900b3d00abcfa14949aeb78" @@ -2539,7 +2251,7 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -util-deprecate@^1.0.1, util-deprecate@~1.0.1: +util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== @@ -2579,11 +2291,6 @@ wbuf@^1.1.0, wbuf@^1.7.3: dependencies: minimalistic-assert "^1.0.0" -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== - webpack-cli@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-5.0.1.tgz#95fc0495ac4065e9423a722dec9175560b6f2d9a" @@ -2707,14 +2414,6 @@ websocket-extensions@>=0.1.1: resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" diff --git a/package.json b/package.json index 73224c8e..94470ece 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@cowprotocol/cow-sdk", - "version": "5.8.0", + "version": "5.8.0-RC.9", "license": "(MIT OR Apache-2.0)", "files": [ "/dist" @@ -17,7 +17,7 @@ "prebuild": "rm -rf dist && yarn run codegen", "build": "microbundle -f modern,esm,cjs", "start": "microbundle -f modern,esm,cjs watch", - "postbuild": "cp package.json dist && cp README.md dist", + "postbuild": "cp package.json dist && cp README.md dist && yarn run trading:generateSchemas", "lint": "eslint src", "format": "prettier --write \"src/**/*.+(ts|json)\"", "test": "jest", @@ -27,10 +27,12 @@ "prepare": "npm run build", "prepublishOnly": "npm test && npm run lint", "graphql:codegen": "graphql-codegen --config graphql-codegen.yml", - "swagger:codegen": " openapi --input https://raw.githubusercontent.com/cowprotocol/services/v2.281.0/crates/orderbook/openapi.yml --output src/order-book/generated --exportServices false --exportCore false", - "typechain:codegen": "typechain --target ethers-v5 --out-dir ./src/common/generated './abi/*.json'" + "swagger:codegen": " openapi --input https://raw.githubusercontent.com/cowprotocol/services/v2.285.0/crates/orderbook/openapi.yml --output src/order-book/generated --exportServices false --exportCore false", + "typechain:codegen": "typechain --target ethers-v5 --out-dir ./src/common/generated './abi/*.json'", + "trading:generateSchemas": "ts-node scripts/generateTradingSchemas.ts" }, "dependencies": { + "@cowprotocol/app-data": "^2.4.0", "@cowprotocol/contracts": "^1.6.0", "@ethersproject/abstract-signer": "^5.7.0", "@openzeppelin/merkle-tree": "^1.0.5", @@ -53,7 +55,7 @@ "@graphql-codegen/typescript-operations": "^3.0.0", "@typechain/ethers-v5": "^11.0.0", "@types/jest": "^29.4.0", - "@types/node": "^18.13.0", + "@types/node": "^22.9.0", "@typescript-eslint/eslint-plugin": "^5.51.0", "@typescript-eslint/parser": "^5.51.0", "babel-plugin-inline-import": "^3.0.0", @@ -68,6 +70,7 @@ "microbundle": "^0.15.1", "openapi-typescript-codegen": "^0.23.0", "prettier": "^2.5.1", + "ts-json-schema-generator": "^2.3.0", "ts-mockito": "^2.6.1", "tsc-watch": "^6.0.0", "typechain": "^8.2.0", diff --git a/scripts/generateTradingSchemas.ts b/scripts/generateTradingSchemas.ts new file mode 100644 index 00000000..e358192e --- /dev/null +++ b/scripts/generateTradingSchemas.ts @@ -0,0 +1,30 @@ +import { Config, createGenerator } from 'ts-json-schema-generator' +import { writeFileSync, mkdirSync } from 'fs' + +const config: Config = { + path: 'src/trading/types.ts', + expose: 'none', + topRef: false, +} + +const types = [ + 'QuoterParameters', + 'TradeParameters', + 'LimitTradeParameters', + 'SwapAdvancedSettings', + 'LimitOrderAdvancedSettings', + 'QuoteResultsSerialized', +] + +const outputPath = 'dist/schemas/trading/' + +const generator = createGenerator(config) + +mkdirSync(outputPath, { recursive: true }) + +types.forEach((type) => { + const schema = generator.createSchema(type) + const schemaString = JSON.stringify(schema, null, 2) + + writeFileSync(outputPath + `${type}.ts`, `export default ${schemaString} as const`) +}) diff --git a/src/common/consts.ts b/src/common/consts.ts index 42bd6704..7b811cf0 100644 --- a/src/common/consts.ts +++ b/src/common/consts.ts @@ -1,6 +1,6 @@ import { SupportedChainId } from './chains' -export const BUY_ETH_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' +export const ETH_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' export const EXTENSIBLE_FALLBACK_HANDLER = '0x2f55e8b20D0B9FEFA187AA7d00B6Cbe563605bF5' export const COMPOSABLE_COW = '0xfdaFc9d1902f4e0b84f65F49f244b32b31013b74' @@ -52,3 +52,35 @@ export const EXTENSIBLE_FALLBACK_HANDLER_CONTRACT_ADDRESS = mapAddressToSupporte * An object containing the addresses of the `ComposableCow` contracts for each supported chain. */ export const COMPOSABLE_COW_CONTRACT_ADDRESS = mapAddressToSupportedNetworks(COMPOSABLE_COW) + +/** + * An object containing the addresses of wrapped native currencies for each supported chain. + */ +export const WRAPPED_NATIVE_CURRENCIES: Record = { + [SupportedChainId.MAINNET]: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', + [SupportedChainId.GNOSIS_CHAIN]: '0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d', + [SupportedChainId.ARBITRUM_ONE]: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', + [SupportedChainId.SEPOLIA]: '0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14', + [SupportedChainId.BASE]: '0x4200000000000000000000000000000000000006', +} + +/** + * An object containing the addresses of ETH flow contracts for each supported chain. + */ +export const ETH_FLOW_ADDRESSES: Record = { + [SupportedChainId.MAINNET]: '0x40A50cf069e992AA4536211B23F286eF88752187', + [SupportedChainId.GNOSIS_CHAIN]: '0x40A50cf069e992AA4536211B23F286eF88752187', + [SupportedChainId.ARBITRUM_ONE]: '0x552fcecc218158fff20e505c8f3ad24f8e1dd33c', + [SupportedChainId.SEPOLIA]: '0x0b7795E18767259CC253a2dF471db34c72B49516', + [SupportedChainId.BASE]: '0x3d1b389f1707DB3d4c5344d5669DBda6b5D6Ab51', +} + +export const BARN_ETH_FLOW_ADDRESSES: Record = { + [SupportedChainId.MAINNET]: '0xD02De8Da0B71E1B59489794F423FaBBa2AdC4d93', + [SupportedChainId.GNOSIS_CHAIN]: '0xD02De8Da0B71E1B59489794F423FaBBa2AdC4d93', + [SupportedChainId.ARBITRUM_ONE]: '0x6dfe75b5ddce1ade279d4fa6bd6aef3cbb6f49db', + [SupportedChainId.SEPOLIA]: '0x2671994c7D224ac4799ac2cf6Ef9EF187d42C69f', + [SupportedChainId.BASE]: '0x3C3eA1829891BC9bEC3d06A81d5d169e52a415e3', +} + +export const MAX_VALID_TO_EPOCH = 4294967295 // Max uint32 (Feb 07 2106 07:28:15 GMT+0100) diff --git a/src/index.ts b/src/index.ts index 505922c6..07382de6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,3 +4,4 @@ export * from './subgraph' export * from './order-signing' export * from './composable' export * from './cow-shed' +export * from './trading' diff --git a/src/order-book/api.spec.ts b/src/order-book/api.spec.ts index e9beaf59..2122a16e 100644 --- a/src/order-book/api.spec.ts +++ b/src/order-book/api.spec.ts @@ -15,7 +15,7 @@ import { CowError } from '../common/cow-error' import { OrderBookApi } from './api' import { BuyTokenDestination, EcdsaSigningScheme, OrderKind, SellTokenSource, SigningScheme } from './generated' import { SupportedChainId } from '../common/chains' -import { BUY_ETH_ADDRESS } from '../common/consts' +import { ETH_ADDRESS } from '../common/consts' import { AUCTION } from './mock' enableFetchMocks() @@ -534,7 +534,7 @@ describe('CoW Api', () => { // then expect(order?.owner).toEqual(order?.onchainUser) expect(order?.validTo).toEqual(order?.ethflowData?.userValidTo) - expect(order?.sellToken).toEqual(BUY_ETH_ADDRESS) + expect(order?.sellToken).toEqual(ETH_ADDRESS) }) test('getOrders', async () => { @@ -553,7 +553,7 @@ describe('CoW Api', () => { // eth flow order expect(orders[0].owner).toEqual(orders[0].onchainUser) expect(orders[0].validTo).toEqual(orders[0].ethflowData?.userValidTo) - expect(orders[0].sellToken).toEqual(BUY_ETH_ADDRESS) + expect(orders[0].sellToken).toEqual(ETH_ADDRESS) // regular order expect(orders[1].owner).toEqual(ORDER_RESPONSE.owner) expect(orders[1].validTo).toEqual(ORDER_RESPONSE.validTo) @@ -573,7 +573,7 @@ describe('CoW Api', () => { // eth flow order expect(txOrders[0].owner).toEqual(txOrders[0].onchainUser) expect(txOrders[0].validTo).toEqual(txOrders[0].ethflowData?.userValidTo) - expect(txOrders[0].sellToken).toEqual(BUY_ETH_ADDRESS) + expect(txOrders[0].sellToken).toEqual(ETH_ADDRESS) // regular order expect(txOrders[1].owner).toEqual(ORDER_RESPONSE.owner) expect(txOrders[1].validTo).toEqual(ORDER_RESPONSE.validTo) diff --git a/src/order-book/quoteAmountsAndCostsUtils.ts b/src/order-book/quoteAmountsAndCostsUtils.ts index d934b68e..d1acab87 100644 --- a/src/order-book/quoteAmountsAndCostsUtils.ts +++ b/src/order-book/quoteAmountsAndCostsUtils.ts @@ -16,7 +16,7 @@ export function getQuoteAmountsAndCosts(params: Params): QuoteAmountsAndCosts { const partnerFeeBps = params.partnerFeeBps ?? 0 const isSell = orderParams.kind === OrderKind.SELL /** - * Wrap raw values into CurrencyAmount objects + * Wrap raw values into bigNumbers * We also make amounts names more specific with "beforeNetworkCosts" and "afterNetworkCosts" suffixes */ const networkCostAmount = getBigNumber(orderParams.feeAmount, sellDecimals) diff --git a/src/order-book/transformOrder.ts b/src/order-book/transformOrder.ts index cbc7d298..e1c8b6a8 100644 --- a/src/order-book/transformOrder.ts +++ b/src/order-book/transformOrder.ts @@ -1,4 +1,4 @@ -import { BUY_ETH_ADDRESS } from '../common/consts' +import { ETH_ADDRESS } from '../common/consts' import { Order } from './generated' import { EnrichedOrder } from './types' @@ -58,7 +58,7 @@ function transformEthFlowOrder(order: EnrichedOrder): EnrichedOrder { const { userValidTo: validTo } = ethflowData const owner = order.onchainUser || order.owner - const sellToken = BUY_ETH_ADDRESS + const sellToken = ETH_ADDRESS return { ...order, validTo, owner, sellToken } } diff --git a/src/order-signing/orderSigningUtils.ts b/src/order-signing/orderSigningUtils.ts index 1259571f..025411c6 100644 --- a/src/order-signing/orderSigningUtils.ts +++ b/src/order-signing/orderSigningUtils.ts @@ -1,6 +1,6 @@ import type { SupportedChainId } from '../common' import type { Signer } from '@ethersproject/abstract-signer' -import type { TypedDataDomain } from '@cowprotocol/contracts' +import type { Order, TypedDataDomain, OrderUidParams } from '@cowprotocol/contracts' import type { SigningResult, UnsignedOrder } from './types' const getSignUtils = () => import('./utils') @@ -14,7 +14,7 @@ const ethersUtils = () => import('ethers/lib/utils') * @example * * ```typescript - * import { OrderSigningUtils, SupportedChainId } from '@cowprotocol/cow-sdk' + * import { OrderSigningUtils, SupportedChainId, UnsignedOrder } from '@cowprotocol/cow-sdk' * import { Web3Provider } from '@ethersproject/providers' * * const account = 'YOUR_WALLET_ADDRESS' @@ -23,10 +23,10 @@ const ethersUtils = () => import('ethers/lib/utils') * const signer = provider.getSigner() * * async function main() { - * const { order: Order } = { ... } - * const orderSigningResult = await OrderSigningUtils.signOrder(quote, chainId, signer) + * const orderToSign: UnsignedOrder = { ... } + * const orderSigningResult = await OrderSigningUtils.signOrder(orderToSign, chainId, signer) * - * const orderId = await orderBookApi.sendOrder({ ...quote, ...orderSigningResult }) + * const orderId = await orderBookApi.sendOrder({ ...orderToSign, ...orderSigningResult }) * * const order = await orderBookApi.getOrder(orderId) * @@ -76,7 +76,7 @@ export class OrderSigningUtils { /** * Sign a cancellation message of multiple order intents with the specified signer. * @param {string[]} orderUids An array of `orderUid` to cancel. - * @param {SupportedChainId} chainId The CoW Protocol protocol `chainId` context that's being used. + * @param {SupportedChainId} chainId The CoW Protocol `chainId` context that's being used. * @param {Signer} signer The signer who initially placed the order intents. * @returns {Promise} Encoded signature including signing scheme for the cancellation. */ @@ -91,7 +91,7 @@ export class OrderSigningUtils { /** * Get the EIP-712 typed domain data being used for signing. - * @param {SupportedChainId} chainId The CoW Protocol protocol `chainId` context that's being used. + * @param {SupportedChainId} chainId The CoW Protocol `chainId` context that's being used. * @return The EIP-712 typed domain data. * @see https://eips.ethereum.org/EIPS/eip-712 */ @@ -100,6 +100,21 @@ export class OrderSigningUtils { return getDomain(chainId) } + /** + * Hashes the order intent and generate deterministic order ID. + * @param {SupportedChainId} chainId The CoW Protocol `chainId` context that's being used. + * @param {Order} order order to sign + * @param {Pick} params order unique identifier parameters. + */ + static async generateOrderId( + chainId: SupportedChainId, + order: Order, + params: Pick + ): Promise<{ orderId: string; orderDigest: string }> { + const { generateOrderId } = await getSignUtils() + return generateOrderId(chainId, order, params) + } + /** * Get the domain separator hash for the EIP-712 typed domain data being used for signing. * @param chainId {SupportedChainId} chainId The CoW Protocol protocol `chainId` context that's being used. @@ -116,7 +131,7 @@ export class OrderSigningUtils { * signing orders using smart contracts, whereby this SDK cannot do the EIP-1271 signing for you. * @returns The EIP-712 types used for signing. */ - static getEIP712Types(): Record { + static getEIP712Types(): Record { return { Order: [ { name: 'sellToken', type: 'address' }, diff --git a/src/order-signing/utils.ts b/src/order-signing/utils.ts index 9c481fca..f3f2c2eb 100644 --- a/src/order-signing/utils.ts +++ b/src/order-signing/utils.ts @@ -3,12 +3,16 @@ import type { Signature, TypedDataDomain, EcdsaSigningScheme as EcdsaSigningSchemeContract, + Order, + OrderUidParams, } from '@cowprotocol/contracts' import { domain as domainGp, EcdsaSignature, IntChainIdTypedDataV4Signer, SigningScheme, + hashOrder, + packOrderUidParams, signOrder as signOrderGp, signOrderCancellation as signOrderCancellationGp, signOrderCancellations as signOrderCancellationsGp, @@ -60,7 +64,7 @@ async function _signOrder(params: SignOrderParams): Promise { const domain = getDomain(chainId) - return signOrderGp(domain, order as OrderFromContract, signer, mapSigningSchema[signingScheme]) + return signOrderGp(domain, order as unknown as OrderFromContract, signer, mapSigningSchema[signingScheme]) } async function _signOrderCancellation(params: SignOrderCancellationParams): Promise { @@ -230,3 +234,27 @@ export function getDomain(chainId: SupportedChainId): TypedDataDomain { return domainGp(chainId, settlementContract) } + +/** + * Generate a deterministic order ID for the specified order. + * @param {SupportedChainId} chainId The chain Id + * @param {Order} order order to sign + * @param {Pick} params order unique identifier parameters. + */ +export async function generateOrderId( + chainId: SupportedChainId, + order: Order, + params: Pick +): Promise<{ orderId: string; orderDigest: string }> { + const domain = await getDomain(chainId) + const orderDigest = hashOrder(domain, order) + // Generate the orderId from owner, orderDigest, and max validTo + const orderId = packOrderUidParams({ + ...params, + orderDigest, + // Different validTo when signing because EthFlow contract expects it to be max for all orders + validTo: order.validTo, + }) + + return { orderId, orderDigest } +} diff --git a/src/trading/README.md b/src/trading/README.md new file mode 100644 index 00000000..a113ed94 --- /dev/null +++ b/src/trading/README.md @@ -0,0 +1,421 @@ +# Trading SDK + +The CoW Protocol provides very flexible and powerful trading capabilities. +However, this flexibility comes with a cost: the complexity of the protocol. +This SDK serves to simplify the interaction with the CoW Protocol. +It will put all necessary parameters to your order, calculates proper amounts, and signs the order. + +> You can find an example of the SDK usage in the [examples](../../examples/vanilla/src/index.ts). + +### What constitutes the complexity? + + - [app-data](https://docs.cow.fi/cow-protocol/reference/sdks/app-data) (order's metadata) + - [order signing](https://docs.cow.fi/cow-protocol/reference/sdks/cow-sdk/classes/OrderSigningUtils) + - network costs, partner fee and slippage + - order parameters (validTo, partiallyFillable, etc.) + - quote API (priceQuality, signingScheme, etc.) + - order kind (sell/buy) + - order class (swap/limit/and others) + - on-chain trades + +## TradingSdk + +The SDK provides three main functions: + - `postSwapOrder` - get quote with market price and create a swap order + - `postLimitOrder` - create a limit order + - `getQuote` - fetch a quote for a swap order + +### Initialization + +The SDK requires the following parameters: + - `chainId` - one of supported chain ids (see [`SupportedChainId`](../common/chains.ts)) + - `signer` - private key or ethers signer or `Eip1193` provider. The signer is used to sign the order. If you use a private key, the SDK will create an ethers signer from it. If you use an ethers signer, the SDK will use it directly. + - `appCode` - a unique identifier for your application. It is used to identify orders created by your application. + +#### Example +```typescript +import { SupportedChainId, TradingSdk } from '@cowprotocol/cow-sdk' + +const sdk = new TradingSdk({ + chainId: SupportedChainId.SEPOLIA, + signer: '', + appCode: '', +}) +``` + +### postSwapOrder + +This function fetches a quote for a swap order and just creates the order. + +The parameters required are: + - `kind` - the order kind (sell/buy) + - `sellToken` - the sell token address + - `sellTokenDecimals` - the sell token decimals + - `buyToken` - the buy token address + - `buyTokenDecimals` - the buy token decimals + - `amount` - the amount to sell/buy in atoms + +> When sell token is a blockchain native token (ETH for Ethereum), then order will be created as an on-chain transaction. See [postSellNativeCurrencyOrder](#postSellNativeCurrencyOrder) + +#### Example + +```typescript +import { + SupportedChainId, + OrderKind, + TradeParameters, + TradingSdk +} from '@cowprotocol/cow-sdk' + +const sdk = new TradingSdk({ + chainId: SupportedChainId.SEPOLIA, + signer: '', + appCode: '', +}) + +const parameters: TradeParameters = { + kind: OrderKind.BUY, + sellToken: '0xfff9976782d46cc05630d1f6ebab18b2324d6b14', + sellTokenDecimals: 18, + buyToken: '0x0625afb445c3b6b7b929342a04a22599fd5dbb59', + buyTokenDecimals: 18, + amount: '120000000000000000' +} + +const orderId = await sdk.postSwapOrder(parameters) + +console.log('Order created, id: ', orderId) +``` + +### getQuote + +In case if you want to get a quote and only then create an order, you can use the `getQuote` function. + +The parameters required are the same as for the `postSwapOrder` function. + +The function returns `quoteResults` object with the following properties: + - `tradeParameters` - trade type, assets, amounts and other optional parameters + - `amountsAndCosts` - the order sell/buy amounts including network costs, fees and slippage + - `orderToSign` - order parameters to sign (see [order signing](https://docs.cow.fi/cow-protocol/reference/sdks/cow-sdk/classes/OrderSigningUtils)) + - `quoteResponse` - DTO from [quote API](https://api.cow.fi/docs/#/default/post_api_v1_quote) + - `appDataInfo` - [order's metadata](https://docs.cow.fi/cow-protocol/reference/sdks/app-data) + - `orderTypedData` - EIP-712 typed data for signing + +Another parameter is returned by this function is `postSwapOrderFromQuote`. +It can be used to create an order from the received quote. + +#### Example + +```typescript +import { + SupportedChainId, + OrderKind, + TradeParameters, + TradingSdk +} from '@cowprotocol/cow-sdk' + +const sdk = new TradingSdk({ + chainId: SupportedChainId.SEPOLIA, + signer: '', + appCode: '', +}) + +const parameters: TradeParameters = { + kind: OrderKind.BUY, + sellToken: '0xfff9976782d46cc05630d1f6ebab18b2324d6b14', + sellTokenDecimals: 18, + buyToken: '0x0625afb445c3b6b7b929342a04a22599fd5dbb59', + buyTokenDecimals: 18, + amount: '120000000000000000' +} + +const { quoteResults, postSwapOrderFromQuote } = await sdk.getQuote(parameters) + +const buyAmount = quoteResults.amountsAndCosts.afterSlippage.buyAmount + +if (confirm(`You will get at least: ${buyAmount}, ok?`)) { + const orderId = await postSwapOrderFromQuote() + + console.log('Order created, id: ', orderId) +} +``` + +### Get quote for a smart-contract wallet + +If you want to use a smart-contract wallet to sign the order, you should specify the `signingScheme` parameter in order to get more accurate quote in terms of gas efficiency. +Smart-contract wallets are supported by using a different signing scheme - `SigningScheme.PRESIGN`. + +#### Example + +```typescript +import { + SupportedChainId, + OrderKind, + TradeParameters, + SwapAdvancedSettings, + SigningScheme, + TradingSdk +} from '@cowprotocol/cow-sdk' + +const sdk = new TradingSdk({ + chainId: SupportedChainId.SEPOLIA, + signer: '', + appCode: '', +}) + +const parameters: TradeParameters = { + kind: OrderKind.BUY, + sellToken: '0xfff9976782d46cc05630d1f6ebab18b2324d6b14', + sellTokenDecimals: 18, + buyToken: '0x0625afb445c3b6b7b929342a04a22599fd5dbb59', + buyTokenDecimals: 18, + amount: '120000000000000000' +} + +const advancedParameters: SwapAdvancedSettings = { + quoteRequest: { + // Specify the signing scheme + signingScheme: SigningScheme.PRESIGN + } +} + +const { quoteResults } = await sdk.getQuote(parameters) + +console.log('Quote:', quoteResults) +```` + +### Create an order with smart-contract wallet + +If you want to create an order with a smart-contract wallet, you should specify the `signingScheme` parameter in the `postSwapOrder` function. +And then you need to send a transaction from `getPreSignTransaction` result in order to sign the order. + +#### Example + +```typescript +import { + SupportedChainId, + OrderKind, + TradeParameters, + TradingSdk +} from '@cowprotocol/cow-sdk' + +const sdk = new TradingSdk({ + chainId: SupportedChainId.SEPOLIA, + signer: '', + appCode: '', +}) + +const parameters: TradeParameters = { + kind: OrderKind.BUY, + sellToken: '0xfff9976782d46cc05630d1f6ebab18b2324d6b14', + sellTokenDecimals: 18, + buyToken: '0x0625afb445c3b6b7b929342a04a22599fd5dbb59', + buyTokenDecimals: 18, + amount: '120000000000000000' +} + +const advancedParameters: SwapAdvancedSettings = { + quoteRequest: { + // Specify the signing scheme + signingScheme: SigningScheme.PRESIGN + } +} + +const smartContractWalletAddress = '0x' +const orderId = await sdk.postSwapOrder(parameters, advancedParameters) +const preSignTransaction = await sdk.getPreSignTransaction({ orderId, account: smartContractWalletAddress }) + +console.log('Order created with "pre-sign" state, id: ', orderId) +console.log('Execute the transaction to sign the order', preSignTransaction) +``` + + +### postLimitOrder + +This main difference between this function and `postSwapOrder` is that here you need to specify both sell and buy amounts. + +You need to provide the following parameters: + - `kind` - the order kind (sell/buy) + - `sellToken` - the sell token address + - `sellTokenDecimals` - the sell token decimals + - `buyToken` - the buy token address + - `buyTokenDecimals` - the buy token decimals + - `sellAmount` - the amount to sell in atoms + - `buyAmount` - the amount to buy in atoms + +And optional parameters: + - `quoteId` - id of the quote from the quote API (see getQuote function) + - `validTo` - the order expiration time in seconds + +```typescript +import { + SupportedChainId, + OrderKind, + LimitTradeParameters, + TradingSdk +} from '@cowprotocol/cow-sdk' + +const sdk = new TradingSdk({ + chainId: SupportedChainId.SEPOLIA, + signer: '', + appCode: '', +}) + +const limitOrderParameters: LimitTradeParameters = { + kind: OrderKind.BUY, + sellToken: '0xfff9976782d46cc05630d1f6ebab18b2324d6b14', + sellTokenDecimals: 18, + buyToken: '0x0625afb445c3b6b7b929342a04a22599fd5dbb59', + buyTokenDecimals: 18, + sellAmount: '120000000000000000', + buyAmount: '66600000000000000000', +} + +const orderId = await sdk.postLimitOrder(limitOrderParameters) + +console.log('Order created, id: ', orderId) +``` + +### postSellNativeCurrencyOrder + +CoW Protocol supports on-chain trades for selling blockchain native tokens (ETH for Ethereum). +In this case, the order is created as an on-chain transaction. +You don't have to think about the case when you use `postSwapOrder` function, it will be handled automatically. +But if you need more flexible way to create an order to sell native token, you can use the `postSellNativeCurrencyOrder` function. + +> We consider the order as native token selling order if the sell token has '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' address. + +```typescript +import { + SupportedChainId, + OrderKind, + TradeParameters, + TradingSdk +} from '@cowprotocol/cow-sdk' + +const sdk = new TradingSdk({ + chainId: SupportedChainId.SEPOLIA, + signer: '', + appCode: '', +}) + +const parameters: TradeParameters = { + kind: OrderKind.BUY, + sellToken: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', + sellTokenDecimals: 18, + buyToken: '0x0625afb445c3b6b7b929342a04a22599fd5dbb59', + buyTokenDecimals: 18, + amount: '120000000000000000' +} + +const orderId = await sdk.postSellNativeCurrencyOrder(parameters) + +console.log('Order created, id: ', orderId) +``` + +### Optional parameters + +Both `postSwapOrder` and `postLimitOrder` functions have optional parameters. +See `TradeOptionalParameters` type for more details. + +| **Parameter** | **Type** | **Default Value** | **Description** | +|-----------------------|-----------------|-------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `env` | `Env` | `prod` | The environment to use (`prod` or `staging`). | +| `partiallyFillable` | `boolean` | `false` | Indicates whether the order is fill-or-kill or partially fillable. | +| `slippageBps` | `number` | 0 | Slippage tolerance applied to the order to get the limit price. Expressed in Basis Points (BPS). One basis point is equivalent to 0.01% (1/100th of a percent). | +| `receiver` | `string` | order creator | The address that will receive the order's tokens. | +| `validFor` | `number` | 10 mins | The order expiration time in seconds. | +| `partnerFee` | `PartnerFee` | - | Partners of the protocol can specify their fee for the order, including the fee in basis points (BPS) and the fee recipient address. [Read more](https://docs.cow.fi/governance/fees/partner-fee) | + +##### Example + +```typescript +import { SupportedChainId, OrderKind, TradeParameters, TradingSdk } from '@cowprotocol/cow-sdk' + +const sdk = new TradingSdk({ + chainId: SupportedChainId.SEPOLIA, + signer: '', + appCode: '', +}) + +const parameters: TradeParameters = { + kind: OrderKind.BUY, + sellToken: '0xfff9976782d46cc05630d1f6ebab18b2324d6b14', + sellTokenDecimals: 18, + buyToken: '0x0625afb445c3b6b7b929342a04a22599fd5dbb59', + buyTokenDecimals: 18, + amount: '120000000000000000', + // Optional parameters + slippageBps: 200, // 2% + validFor: 1200, // 20 mins + receiver: '0xdef1ca1fb7f1232777520aa7f396b4e015f497ab' // Just a random address, don't use it! +} + +const orderId = await sdk.postSwapOrder(parameters) + +console.log('Order created, id: ', orderId) +``` + +### Advanced swap order creation + +By default, the SDK requires only the basic parameters to create an order. +However, you can provide additional parameters to customize the order creation. + +#### Swap + +1. `quoteRequest` - the quote request object. It is used to get a quote from the quote API ([read more](https://docs.cow.fi/cow-protocol/reference/sdks/cow-sdk/modules#orderquoterequest)) +2. `appData` - the order's metadata ([read more](https://docs.cow.fi/cow-protocol/reference/sdks/app-data/modules#appdataparams)) + +##### Example + +```typescript +import { + SupportedChainId, + OrderKind, + TradeParameters, + TradingSdk, + SwapAdvancedSettings, + PriceQuality +} from '@cowprotocol/cow-sdk' + +const sdk = new TradingSdk({ + chainId: SupportedChainId.SEPOLIA, + signer: '', + appCode: '', +}) + +const parameters: TradeParameters = { + kind: OrderKind.BUY, + sellToken: '0xfff9976782d46cc05630d1f6ebab18b2324d6b14', + sellTokenDecimals: 18, + buyToken: '0x0625afb445c3b6b7b929342a04a22599fd5dbb59', + buyTokenDecimals: 18, + amount: '120000000000000000' +} + +const advancedSettings: SwapAdvancedSettings = { + quoteRequest: { + priceQuality: PriceQuality.FAST, + validFor: 120, + }, + appData: { + hooks: { + version: 1, + pre: [ + { + target: '0xdef1ca1fb7fbcdc777520aa7f396b4e015f497ab', + callData: '0x70a08231000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa96045', + gasLimit: 21000 + } + ] + } + }, +} +const orderId = await sdk.postSwapOrder(parameters, advancedSettings) + +console.log('Order created, id: ', orderId) +``` + +#### Limit order + +Same as for the swap order but without the `quoteRequest` parameter. diff --git a/src/trading/appDataUtils.test.ts b/src/trading/appDataUtils.test.ts new file mode 100644 index 00000000..ae0edc69 --- /dev/null +++ b/src/trading/appDataUtils.test.ts @@ -0,0 +1,51 @@ +import { buildAppData, generateAppDataFromDoc } from './appDataUtils' + +describe('AppData utils', () => { + it('Should add all required parameters to the doc', async () => { + const data = await buildAppData({ + slippageBps: 100, + appCode: 'cowswap', + orderClass: 'market', + }) + const parsedData = JSON.parse(data.fullAppData) + + expect(parsedData.metadata.quote.slippageBips).toBe(100) + expect(parsedData.appCode).toBe('cowswap') + expect(parsedData.metadata.orderClass.orderClass).toBe('market') + }) + + it('Should add advanced parameters to the doc', async () => { + const data = await buildAppData( + { + slippageBps: 100, + appCode: 'cowswap', + orderClass: 'market', + }, + { + environment: 'staging', + metadata: { + partnerFee: { + bps: 66, + recipient: '0xccc', + }, + replacedOrder: { + uid: '0xaaa', + }, + }, + } + ) + const parsedData = JSON.parse(data.fullAppData) + + expect(parsedData.environment).toBe('staging') + expect(parsedData.metadata.partnerFee.bps).toBe(66) + expect(parsedData.metadata.partnerFee.recipient).toBe('0xccc') + expect(parsedData.metadata.replacedOrder.uid).toBe('0xaaa') + }) + + it('App data doc should be stringified in deterministic way', async () => { + const data1 = await generateAppDataFromDoc({ version: '1.0', appCode: 'code', metadata: {} }) + const data2 = await generateAppDataFromDoc({ appCode: 'code', metadata: {}, version: '1.0' }) + + expect(data1.fullAppData).toBe(data2.fullAppData) + }) +}) diff --git a/src/trading/appDataUtils.ts b/src/trading/appDataUtils.ts new file mode 100644 index 00000000..6c4d9f2a --- /dev/null +++ b/src/trading/appDataUtils.ts @@ -0,0 +1,35 @@ +import { AppDataInfo, AppDataRootSchema, BuildAppDataParams } from './types' +import { AppDataParams, MetadataApi, stringifyDeterministic } from '@cowprotocol/app-data' +import { keccak256, toUtf8Bytes } from 'ethers/lib/utils' + +export async function buildAppData( + { slippageBps, appCode, orderClass: orderClassName }: BuildAppDataParams, + advancedParams?: AppDataParams +): Promise { + const metadataApiSDK = new MetadataApi() + + const quoteParams = { slippageBips: slippageBps } + const orderClass = { orderClass: orderClassName } + + const doc = await metadataApiSDK.generateAppDataDoc({ + appCode, + metadata: { + quote: quoteParams, + orderClass, + }, + ...advancedParams, + }) + + const { fullAppData, appDataKeccak256 } = await generateAppDataFromDoc(doc) + + return { doc, fullAppData, appDataKeccak256 } +} + +export async function generateAppDataFromDoc( + doc: AppDataRootSchema +): Promise> { + const fullAppData = await stringifyDeterministic(doc) + const appDataKeccak256 = keccak256(toUtf8Bytes(fullAppData)) + + return { fullAppData, appDataKeccak256 } +} diff --git a/src/trading/calculateUniqueOrderId.test.ts b/src/trading/calculateUniqueOrderId.test.ts new file mode 100644 index 00000000..1b22ea53 --- /dev/null +++ b/src/trading/calculateUniqueOrderId.test.ts @@ -0,0 +1,91 @@ +jest.mock('../order-signing', () => ({ + OrderSigningUtils: { + generateOrderId: jest.fn(), + }, +})) + +import { calculateUniqueOrderId } from './calculateUniqueOrderId' +import { MAX_VALID_TO_EPOCH, SupportedChainId, WRAPPED_NATIVE_CURRENCIES } from '../common' +import { OrderSigningUtils as OrderSigningUtilsMock, UnsignedOrder } from '../order-signing' +import { BuyTokenDestination, OrderKind, SellTokenSource } from '../order-book/generated' + +const orderMock: UnsignedOrder = { + buyAmount: '100', + buyToken: '0xb', + buyTokenBalance: BuyTokenDestination.ERC20, + sellAmount: '30', + sellToken: '0xa', + sellTokenBalance: SellTokenSource.ERC20, + validTo: 10000033, + feeAmount: '0', + kind: OrderKind.BUY, + partiallyFillable: false, + receiver: '0x123', + appData: '0x0004', +} + +describe('calculateUniqueOrderId', () => { + let generateOrderId: jest.SpyInstance + + beforeAll(() => { + generateOrderId = OrderSigningUtilsMock.generateOrderId as unknown as jest.SpyInstance + }) + + beforeEach(() => { + generateOrderId.mockResolvedValue({ orderDigest: '0x000dd', orderId: '0xab444' }) + }) + + afterEach(() => { + generateOrderId.mockReset() + }) + + it('Should always set validTo to the maximum value', async () => { + await calculateUniqueOrderId(SupportedChainId.MAINNET, orderMock) + + const [chainId, order] = generateOrderId.mock.calls[0] + + expect(chainId).toBe(SupportedChainId.MAINNET) + expect(order.validTo).toBe(MAX_VALID_TO_EPOCH) + }) + it('Should always set sellToken to wrapped native token', async () => { + await calculateUniqueOrderId(SupportedChainId.MAINNET, orderMock) + + const [chainId, order] = generateOrderId.mock.calls[0] + + expect(chainId).toBe(SupportedChainId.MAINNET) + expect(order.sellToken).toBe(WRAPPED_NATIVE_CURRENCIES[SupportedChainId.MAINNET]) + }) + + describe('When checkEthFlowOrderExists is set', () => { + it('Then the callback should be called with the orderId and orderDigest', async () => { + const checkEthFlowOrderExists = jest.fn().mockResolvedValue(false) + await calculateUniqueOrderId(SupportedChainId.MAINNET, orderMock, checkEthFlowOrderExists) + + expect(checkEthFlowOrderExists).toHaveBeenCalledWith('0xab444', '0x000dd') + expect(checkEthFlowOrderExists).toHaveBeenCalledTimes(1) + }) + + describe('When checkEthFlowOrderExists returns true', () => { + it('Then it should call itself with the adjusted order', async () => { + let alreadyCalled = false + + const checkEthFlowOrderExists = jest.fn().mockImplementation(() => { + return Promise.resolve( + (() => { + if (alreadyCalled) return false + alreadyCalled = true + + return true + })() + ) + }) + await calculateUniqueOrderId(SupportedChainId.MAINNET, orderMock, checkEthFlowOrderExists) + + const [chainId, order] = generateOrderId.mock.calls[1] + + expect(chainId).toBe(SupportedChainId.MAINNET) + expect(order.buyAmount).toBe('99') + }) + }) + }) +}) diff --git a/src/trading/calculateUniqueOrderId.ts b/src/trading/calculateUniqueOrderId.ts new file mode 100644 index 00000000..418d9b72 --- /dev/null +++ b/src/trading/calculateUniqueOrderId.ts @@ -0,0 +1,58 @@ +import { OrderSigningUtils, UnsignedOrder } from '../order-signing' +import { + BARN_ETH_FLOW_ADDRESSES, + CowEnv, + ETH_FLOW_ADDRESSES, + MAX_VALID_TO_EPOCH, + SupportedChainId, + WRAPPED_NATIVE_CURRENCIES, +} from '../common' +import type { Order, OrderBalance } from '@cowprotocol/contracts' + +export interface EthFlowOrderExistsCallback { + (orderId: string, orderDigest: string): Promise +} + +export async function calculateUniqueOrderId( + chainId: SupportedChainId, + order: UnsignedOrder, + checkEthFlowOrderExists?: EthFlowOrderExistsCallback, + env?: CowEnv +): Promise { + const { orderDigest, orderId } = await OrderSigningUtils.generateOrderId( + chainId, + { + ...order, + sellTokenBalance: order.sellTokenBalance as string as OrderBalance, + buyTokenBalance: order.buyTokenBalance as string as OrderBalance, + validTo: MAX_VALID_TO_EPOCH, + sellToken: WRAPPED_NATIVE_CURRENCIES[chainId], + } as Order, + { + owner: (env === 'staging' ? BARN_ETH_FLOW_ADDRESSES : ETH_FLOW_ADDRESSES)[chainId], + } + ) + + if (checkEthFlowOrderExists && (await checkEthFlowOrderExists(orderId, orderDigest))) { + console.error('ETH FLOW', '[calculateUniqueOrderId] ❌ Collision detected: ' + orderId, { + sellAmount: order.sellAmount, + fee: order.feeAmount, + }) + + // Recursive call, increment one fee until we get an unique order Id + return calculateUniqueOrderId(chainId, adjustAmounts(order), checkEthFlowOrderExists) + } + + return orderId +} + +function adjustAmounts(order: UnsignedOrder): UnsignedOrder { + const buyAmount = BigInt(order.buyAmount) + + // On fee=0, fee is, well, 0. Thus, we cannot shift amounts around and remain with the exact same price. + // Also, we don't want to touch the sell amount. + // If we move it down, the price might become "too good", if we move it up, the user might not have enough funds! + // Thus, we make the buy amount a tad bit worse by 1 wei. + // We can only hope this doesn't happen for an order buying 0 a decimals token 🤞 + return { ...order, buyAmount: (buyAmount - BigInt(1)).toString() } +} diff --git a/src/trading/consts.ts b/src/trading/consts.ts new file mode 100644 index 00000000..0d91d0c3 --- /dev/null +++ b/src/trading/consts.ts @@ -0,0 +1,13 @@ +import { EcdsaSigningScheme, SigningScheme } from '../order-book' + +export const log = (text: string) => console.log(`[COW TRADING SDK] ${text}`) + +export const DEFAULT_QUOTE_VALIDITY = 60 * 10 // 10 min + +export const SIGN_SCHEME_MAP = { + [EcdsaSigningScheme.EIP712]: SigningScheme.EIP712, + [EcdsaSigningScheme.ETHSIGN]: SigningScheme.ETHSIGN, +} + +// Use a 150K gas as a fallback if there's issue calculating the gas estimation (fixes some issues with some nodes failing to calculate gas costs for SC wallets) +export const GAS_LIMIT_DEFAULT = BigInt(150000) diff --git a/src/trading/getEthFlowTransaction.test.ts b/src/trading/getEthFlowTransaction.test.ts new file mode 100644 index 00000000..a15ea909 --- /dev/null +++ b/src/trading/getEthFlowTransaction.test.ts @@ -0,0 +1,80 @@ +const GAS = '0x1e848' // 125000 + +jest.mock('cross-fetch', () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const fetchMock = require('jest-fetch-mock') + // Require the original module to not be mocked... + const originalFetch = jest.requireActual('cross-fetch') + return { + __esModule: true, + ...originalFetch, + default: fetchMock, + } +}) +jest.mock('../common/generated', () => { + const original = jest.requireActual('../common/generated') + + return { + ...original, + EthFlow__factory: { + connect: jest.fn().mockReturnValue({ + address: '0xaa1', + estimateGas: { + createOrder: jest.fn().mockResolvedValue({ toHexString: () => GAS }), + }, + interface: { + encodeFunctionData: jest.fn().mockReturnValue('0x0ac'), + }, + }), + }, + } +}) + +import { getEthFlowTransaction } from './getEthFlowTransaction' +import { VoidSigner } from '@ethersproject/abstract-signer' +import { SupportedChainId, WRAPPED_NATIVE_CURRENCIES } from '../common' +import { LimitTradeParameters } from './types' +import { OrderKind } from '../order-book' + +const appDataKeccak256 = '0x578c975b1cfd3e24c21fb599076c4f7879c4268efd33eed3eb9efa5e30efac21' + +const params: LimitTradeParameters = { + kind: OrderKind.SELL, + sellToken: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + buyToken: '0xdef1ca1fb7fbcdc777520aa7f396b4e015f497ab', + sellAmount: '12000000000000000', + buyAmount: '36520032402342342322', + quoteId: 3, + sellTokenDecimals: 18, + buyTokenDecimals: 18, +} + +describe('getEthFlowTransaction', () => { + const chainId = SupportedChainId.GNOSIS_CHAIN + const account = '0x21c3de23d98caddc406e3d31b25e807addf33333' + const signer = new VoidSigner(account) + + signer.getChainId = jest.fn().mockResolvedValue(chainId) + signer.getAddress = jest.fn().mockResolvedValue(account) + + it('Should always override sell token with wrapped native token', async () => { + const result = await getEthFlowTransaction(signer, appDataKeccak256, params) + const wrappedToken = WRAPPED_NATIVE_CURRENCIES[chainId] + + expect(result.transaction.data.includes(params.sellToken.slice(2))).toBe(false) + expect(result.transaction.data.includes(wrappedToken.slice(2))).toBe(false) + }) + + it('Should call gas estimation and return estimated value + 20%', async () => { + const result = await getEthFlowTransaction(signer, appDataKeccak256, params) + const gasNum = +GAS + + expect(+result.transaction.gas).toBe(gasNum + gasNum * 0.2) + }) + + it('Transaction value should be equal to sell amount', async () => { + const result = await getEthFlowTransaction(signer, appDataKeccak256, params) + + expect(result.transaction.value).toBe('0x' + BigInt(params.sellAmount).toString(16)) + }) +}) diff --git a/src/trading/getEthFlowTransaction.ts b/src/trading/getEthFlowTransaction.ts new file mode 100644 index 00000000..dd62a68f --- /dev/null +++ b/src/trading/getEthFlowTransaction.ts @@ -0,0 +1,68 @@ +import { Signer } from 'ethers' +import { LimitTradeParametersFromQuote, TransactionParams } from './types' +import { calculateUniqueOrderId, EthFlowOrderExistsCallback } from './calculateUniqueOrderId' +import { getOrderToSign } from './getOrderToSign' +import { type EthFlow, EthFlow__factory } from '../common/generated' +import { + BARN_ETH_FLOW_ADDRESSES, + CowEnv, + ETH_FLOW_ADDRESSES, + SupportedChainId, + WRAPPED_NATIVE_CURRENCIES, +} from '../common' +import { GAS_LIMIT_DEFAULT } from './consts' +import type { EthFlowOrder } from '../common/generated/EthFlow' +import { calculateGasMargin } from './utils' + +export async function getEthFlowTransaction( + signer: Signer, + appDataKeccak256: string, + _params: LimitTradeParametersFromQuote, + networkCostsAmount = '0', + checkEthFlowOrderExists?: EthFlowOrderExistsCallback +): Promise<{ orderId: string; transaction: TransactionParams }> { + const chainId = (await signer.getChainId()) as SupportedChainId + const from = await signer.getAddress() + + const params = { + ..._params, + sellToken: WRAPPED_NATIVE_CURRENCIES[chainId], + } + const { quoteId } = params + + const contract = getEthFlowContract(chainId, signer, params.env) + const orderToSign = getOrderToSign({ from, networkCostsAmount }, params, appDataKeccak256) + const orderId = await calculateUniqueOrderId(chainId, orderToSign, checkEthFlowOrderExists, params.env) + + const ethOrderParams: EthFlowOrder.DataStruct = { + ...orderToSign, + quoteId, + appData: appDataKeccak256, + validTo: orderToSign.validTo.toString(), + } + + const estimatedGas = await contract.estimateGas + .createOrder(ethOrderParams, { value: orderToSign.sellAmount }) + .then((res) => BigInt(res.toHexString())) + .catch((error) => { + console.error(error) + + return GAS_LIMIT_DEFAULT + }) + + const data = contract.interface.encodeFunctionData('createOrder', [ethOrderParams]) + + return { + orderId, + transaction: { + data, + gas: '0x' + calculateGasMargin(estimatedGas).toString(16), + to: contract.address, + value: '0x' + BigInt(orderToSign.sellAmount).toString(16), + }, + } +} + +function getEthFlowContract(chainId: SupportedChainId, signer: Signer, env?: CowEnv): EthFlow { + return EthFlow__factory.connect((env === 'staging' ? BARN_ETH_FLOW_ADDRESSES : ETH_FLOW_ADDRESSES)[chainId], signer) +} diff --git a/src/trading/getOrderToSign.test.ts b/src/trading/getOrderToSign.test.ts new file mode 100644 index 00000000..1dd2f665 --- /dev/null +++ b/src/trading/getOrderToSign.test.ts @@ -0,0 +1,81 @@ +jest.mock('cross-fetch', () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const fetchMock = require('jest-fetch-mock') + // Require the original module to not be mocked... + const originalFetch = jest.requireActual('cross-fetch') + return { + __esModule: true, + ...originalFetch, + default: fetchMock, + } +}) + +import { getOrderToSign } from './getOrderToSign' +import { LimitOrderParameters } from './types' +import { SupportedChainId } from '../common' +import { OrderKind } from '../order-book' +import { DEFAULT_QUOTE_VALIDITY } from './consts' + +const currentTimestamp = 1487076708000 + +const params = { from: '0xaaa444' } + +const defaultOrderParams: LimitOrderParameters = { + chainId: SupportedChainId.GNOSIS_CHAIN, + signer: '0x006', + appCode: '0x007', + sellToken: '0xaaa', + sellTokenDecimals: 18, + buyToken: '0xbbb', + buyTokenDecimals: 18, + sellAmount: '1000000000000000000', + buyAmount: '2000000000000000000', + kind: OrderKind.SELL, + quoteId: 31, + slippageBps: 50, +} + +const appDataKeccak256 = '0x00355666666' + +describe('getOrderToSign', () => { + beforeEach(() => { + Date.now = jest.fn(() => currentTimestamp) + }) + it('When receiver is not set, then should use "from" parameter instead', () => { + const result = getOrderToSign(params, { ...defaultOrderParams, receiver: undefined }, appDataKeccak256) + + expect(result.receiver).toBe('0xaaa444') + }) + + it('When validTo is not set, then should use "validFor" parameter instead', () => { + const result = getOrderToSign( + params, + { ...defaultOrderParams, validTo: undefined, validFor: 600 }, + appDataKeccak256 + ) + + expect(result.validTo).toBe(currentTimestamp / 1000 + 600) + }) + + it('When both validTo and validFor are not set, then should use default value', () => { + const result = getOrderToSign( + params, + { ...defaultOrderParams, validTo: undefined, validFor: undefined }, + appDataKeccak256 + ) + + expect(result.validTo).toBe(currentTimestamp / 1000 + DEFAULT_QUOTE_VALIDITY) + }) + + it('When sell order, then buy amount should be adjusted to slippage', () => { + const result = getOrderToSign(params, { ...defaultOrderParams, kind: OrderKind.SELL }, appDataKeccak256) + + expect(result.buyAmount).toBe('1990000000000000000') + }) + + it('When buy order, then sell amount should be adjusted to slippage', () => { + const result = getOrderToSign(params, { ...defaultOrderParams, kind: OrderKind.BUY }, appDataKeccak256) + + expect(result.sellAmount).toBe('1005000000000000000') + }) +}) diff --git a/src/trading/getOrderToSign.ts b/src/trading/getOrderToSign.ts new file mode 100644 index 00000000..1b0992e1 --- /dev/null +++ b/src/trading/getOrderToSign.ts @@ -0,0 +1,68 @@ +import { BuyTokenDestination, getQuoteAmountsAndCosts, type OrderParameters, SellTokenSource } from '../order-book' +import { UnsignedOrder } from '../order-signing' +import { LimitTradeParameters } from './types' +import { DEFAULT_QUOTE_VALIDITY } from './consts' + +interface OrderToSignParams { + from: string + networkCostsAmount?: string +} + +export function getOrderToSign( + { from, networkCostsAmount = '0' }: OrderToSignParams, + limitOrderParams: LimitTradeParameters, + appDataKeccak256: string +): UnsignedOrder { + const { + sellAmount, + buyAmount, + sellToken, + sellTokenDecimals, + buyToken, + buyTokenDecimals, + kind, + partiallyFillable = false, + slippageBps = 0, + partnerFee, + validFor, + } = limitOrderParams + + const receiver = limitOrderParams.receiver || from + const validTo = limitOrderParams.validTo || Math.floor(Date.now() / 1000) + (validFor || DEFAULT_QUOTE_VALIDITY) + + const orderParams: OrderParameters = { + sellToken, + buyToken, + sellAmount, + buyAmount, + receiver, + validTo, + kind, + feeAmount: networkCostsAmount, + appData: appDataKeccak256, + partiallyFillable, + } + + const { afterSlippage } = getQuoteAmountsAndCosts({ + orderParams, + slippagePercentBps: slippageBps, + partnerFeeBps: partnerFee?.bps, + sellDecimals: sellTokenDecimals, + buyDecimals: buyTokenDecimals, + }) + + return { + sellToken, + buyToken, + sellAmount: afterSlippage.sellAmount.toString(), + buyAmount: afterSlippage.buyAmount.toString(), + validTo, + kind, + partiallyFillable, + appData: appDataKeccak256, + receiver, + feeAmount: '0', + sellTokenBalance: SellTokenSource.ERC20, + buyTokenBalance: BuyTokenDestination.ERC20, + } +} diff --git a/src/trading/getOrderTypedData.ts b/src/trading/getOrderTypedData.ts new file mode 100644 index 00000000..327b9acf --- /dev/null +++ b/src/trading/getOrderTypedData.ts @@ -0,0 +1,28 @@ +import { ORDER_TYPE_FIELDS } from '@cowprotocol/contracts' +import { ORDER_PRIMARY_TYPE, OrderTypedData } from './types' +import { OrderSigningUtils, UnsignedOrder } from '../order-signing' +import { SupportedChainId } from '../common' + +const EIP712DomainTypes = [ + { name: 'name', type: 'string' }, + { name: 'version', type: 'string' }, + { name: 'chainId', type: 'uint256' }, + { name: 'verifyingContract', type: 'address' }, +] + +export async function getOrderTypedData( + chainId: SupportedChainId, + orderToSign: UnsignedOrder +): Promise { + const domain = (await OrderSigningUtils.getDomain(chainId)) as OrderTypedData['domain'] + + return { + domain, + primaryType: ORDER_PRIMARY_TYPE, + types: { + [ORDER_PRIMARY_TYPE]: ORDER_TYPE_FIELDS, + EIP712Domain: EIP712DomainTypes, + }, + message: orderToSign, + } +} diff --git a/src/trading/getPreSignTransaction.test.ts b/src/trading/getPreSignTransaction.test.ts new file mode 100644 index 00000000..980fd4d3 --- /dev/null +++ b/src/trading/getPreSignTransaction.test.ts @@ -0,0 +1,60 @@ +const GAS = '0x1e848' // 125000 + +jest.mock('cross-fetch', () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const fetchMock = require('jest-fetch-mock') + // Require the original module to not be mocked... + const originalFetch = jest.requireActual('cross-fetch') + return { + __esModule: true, + ...originalFetch, + default: fetchMock, + } +}) +jest.mock('../common/generated', () => { + const original = jest.requireActual('../common/generated') + + return { + ...original, + GPv2Settlement__factory: { + connect: jest.fn().mockReturnValue({ + address: '0xaa1', + estimateGas: { + setPreSignature: jest.fn().mockResolvedValue({ toHexString: () => GAS }), + }, + interface: { + encodeFunctionData: jest.fn().mockReturnValue('0x0ac'), + }, + }), + }, + } +}) + +import { SupportedChainId } from '../common' +import { VoidSigner } from '@ethersproject/abstract-signer' +import { getPreSignTransaction } from './getPreSignTransaction' + +const chainId = SupportedChainId.GNOSIS_CHAIN +const account = '0x21c3de23d98caddc406e3d31b25e807addf33333' +const orderId = + '0xd64389693b6cf89ad6c140a113b10df08073e5ef3063d05a02f3f42e1a42f0ad0b7795e18767259cc253a2af471dbc4c72b49516ffffffff' + +describe('getPreSignTransaction', () => { + const signer = new VoidSigner(account) + + signer.getChainId = jest.fn().mockResolvedValue(chainId) + signer.getAddress = jest.fn().mockResolvedValue(account) + + it('Should call gas estimation and return estimated value + 20%', async () => { + const result = await getPreSignTransaction(signer, chainId, account, orderId) + const gasNum = +GAS + + expect(+result.gas).toBe(gasNum * 1.2) + }) + + it('Tx value should always be zero', async () => { + const result = await getPreSignTransaction(signer, chainId, account, orderId) + + expect(result.value).toBe('0') + }) +}) diff --git a/src/trading/getPreSignTransaction.ts b/src/trading/getPreSignTransaction.ts new file mode 100644 index 00000000..86ff2fac --- /dev/null +++ b/src/trading/getPreSignTransaction.ts @@ -0,0 +1,35 @@ +import { COW_PROTOCOL_SETTLEMENT_CONTRACT_ADDRESS, SupportedChainId } from '../common' +import type { Signer } from 'ethers' +import { GAS_LIMIT_DEFAULT } from './consts' +import { calculateGasMargin } from './utils' + +import { GPv2Settlement__factory } from '../common/generated' +import { TransactionParams } from './types' + +export async function getPreSignTransaction( + signer: Signer, + chainId: SupportedChainId, + account: string, + orderId: string +): Promise { + const contract = GPv2Settlement__factory.connect(account, signer) + + const settlementContractAddress = COW_PROTOCOL_SETTLEMENT_CONTRACT_ADDRESS[chainId] as `0x${string}` + const preSignatureCall = contract.interface.encodeFunctionData('setPreSignature', [orderId, true]) + + const gas = await contract.estimateGas + .setPreSignature(orderId, true) + .then((res) => BigInt(res.toHexString())) + .catch((error) => { + console.error(error) + + return GAS_LIMIT_DEFAULT + }) + + return { + data: preSignatureCall, + gas: '0x' + calculateGasMargin(gas).toString(16), + to: settlementContractAddress, + value: '0', + } +} diff --git a/src/trading/getQuote.test.ts b/src/trading/getQuote.test.ts new file mode 100644 index 00000000..b2c0d0ee --- /dev/null +++ b/src/trading/getQuote.test.ts @@ -0,0 +1,169 @@ +jest.mock('cross-fetch', () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const fetchMock = require('jest-fetch-mock') + // Require the original module to not be mocked... + const originalFetch = jest.requireActual('cross-fetch') + return { + __esModule: true, + ...originalFetch, + default: fetchMock, + } +}) + +import { getQuoteWithSigner } from './getQuote' +import { SwapParameters } from './types' +import { ETH_ADDRESS, SupportedChainId, WRAPPED_NATIVE_CURRENCIES } from '../common' +import { OrderBookApi, OrderKind, OrderQuoteResponse } from '../order-book' + +const quoteResponseMock = { + quote: { + sellToken: '0xfff9976782d46cc05630d1f6ebab18b2324d6b14', + buyToken: '0x0625afb445c3b6b7b929342a04a22599fd5dbb59', + receiver: '0xfb3c7eb936caa12b5a884d612393969a557d4307', + sellAmount: '98115217044683860', + buyAmount: '984440000000', + validTo: 1731059375, + appData: + '{"appCode":"CoW Swap","environment":"production","metadata":{"orderClass":{"orderClass":"market"},"quote":{"slippageBips":50,"smartSlippage":false}},"version":"1.3.0"}', + appDataHash: '0x05fb36aed7ba01f92544e72888fb354cdeab68b6bbb0b9ea5e64edc364093b42', + feeAmount: '1884782955316140', + kind: 'sell', + partiallyFillable: false, + sellTokenBalance: 'erc20', + buyTokenBalance: 'erc20', + signingScheme: 'eip712', + }, + from: '0xfb3c7eb936caa12b5a884d612393969a557d4307', + expiration: '2024-11-08T09:21:35.442772888Z', + id: 486289, + verified: true, +} as OrderQuoteResponse + +const defaultOrderParams: SwapParameters = { + chainId: SupportedChainId.GNOSIS_CHAIN, + signer: '1bb337bafb276f779c3035874b8914e4b851bb989dbb34e776397076576f3804', + appCode: '0x007', + sellToken: '0xfff9976782d46cc05630d1f6ebab18b2324d6b14', + sellTokenDecimals: 18, + buyToken: '0x0625afb445c3b6b7b929342a04a22599fd5dbb59', + buyTokenDecimals: 18, + amount: '100000000000000000', + kind: OrderKind.SELL, + slippageBps: 50, +} + +const getQuoteMock = jest.fn() +const orderBookApiMock = { + getQuote: getQuoteMock, +} as unknown as OrderBookApi + +describe('getQuoteToSign', () => { + beforeEach(() => { + getQuoteMock.mockReset() + getQuoteMock.mockResolvedValue(quoteResponseMock) + }) + + describe('App data', () => { + it('Should add slippageBps and appCode from parameters', async () => { + const { result } = await getQuoteWithSigner({ ...defaultOrderParams, slippageBps: 76 }, {}, orderBookApiMock) + const appData = JSON.parse(result.appDataInfo.fullAppData) + + expect(appData.metadata.quote.slippageBips).toBe(76) + expect(appData.appCode).toBe(defaultOrderParams.appCode) + }) + + it('Should add advanced appData parameters', async () => { + const { result } = await getQuoteWithSigner( + defaultOrderParams, + { + appData: { + environment: 'barn', + }, + }, + orderBookApiMock + ) + + const appData = JSON.parse(result.appDataInfo.fullAppData) + + expect(appData.environment).toBe('barn') + }) + }) + + describe('Quote request', () => { + it('When sell ETH, then should override sell token with wrapped one', async () => { + await getQuoteWithSigner({ ...defaultOrderParams, sellToken: ETH_ADDRESS }, {}, orderBookApiMock) + + const call = getQuoteMock.mock.calls[0][0] + + expect(call.sellToken).toBe(WRAPPED_NATIVE_CURRENCIES[defaultOrderParams.chainId]) + }) + it('Should add appData to the request', async () => { + await getQuoteWithSigner(defaultOrderParams, {}, orderBookApiMock) + + const call = getQuoteMock.mock.calls[0][0] + const appData = JSON.parse(call.appData) + + expect(appData.appCode).toBe(defaultOrderParams.appCode) + expect(appData.metadata.quote.slippageBips).toBe(defaultOrderParams.slippageBps) + }) + it('priceQuality must always be OPTIMAL', async () => { + await getQuoteWithSigner(defaultOrderParams, {}, orderBookApiMock) + + const call = getQuoteMock.mock.calls[0][0] + + expect(call.priceQuality).toBe('optimal') + }) + it('When is sell order, then should set sellAmountBeforeFee', async () => { + await getQuoteWithSigner({ ...defaultOrderParams, kind: OrderKind.SELL }, {}, orderBookApiMock) + + const call = getQuoteMock.mock.calls[0][0] + + expect(call.sellAmountBeforeFee).toBe(defaultOrderParams.amount) + }) + it('When is buy order, then should set buyAmountAfterFee', async () => { + await getQuoteWithSigner({ ...defaultOrderParams, kind: OrderKind.BUY }, {}, orderBookApiMock) + + const call = getQuoteMock.mock.calls[0][0] + + expect(call.buyAmountAfterFee).toBe(defaultOrderParams.amount) + }) + it('Should add advanced quote parameters', async () => { + await getQuoteWithSigner(defaultOrderParams, { quoteRequest: { onchainOrder: { foo: 'bar' } } }, orderBookApiMock) + + const call = getQuoteMock.mock.calls[0][0] + + expect(call.onchainOrder).toEqual({ foo: 'bar' }) + }) + }) + + describe('Amounts and costs', () => { + it('Should take slippage value into account', async () => { + const { result } = await getQuoteWithSigner({ ...defaultOrderParams, slippageBps: 20 }, {}, orderBookApiMock) + const buyAmount = +quoteResponseMock.quote.buyAmount + + expect(+result.amountsAndCosts.afterSlippage.buyAmount.toString()).toBe( + buyAmount - (buyAmount * 20) / (100 * 100) + ) + }) + it('Should calculate network costs based on quote API response', async () => { + const { result } = await getQuoteWithSigner(defaultOrderParams, {}, orderBookApiMock) + + expect(result.amountsAndCosts.costs.networkFee.amountInSellCurrency.toString()).toBe( + quoteResponseMock.quote.feeAmount + ) + }) + }) + + describe('Order to sign', () => { + it('feeAmount should always be zero', async () => { + const { result } = await getQuoteWithSigner(defaultOrderParams, {}, orderBookApiMock) + + expect(result.orderToSign.feeAmount).toBe('0') + }) + it('Should add appDataKeccak256 to the order', async () => { + const { result } = await getQuoteWithSigner(defaultOrderParams, {}, orderBookApiMock) + + expect(result.orderToSign.appData.length).toBe(2 + 64) + }) + }) +}) diff --git a/src/trading/getQuote.ts b/src/trading/getQuote.ts new file mode 100644 index 00000000..41c6b58f --- /dev/null +++ b/src/trading/getQuote.ts @@ -0,0 +1,161 @@ +import { + AccountAddress, + QuoteResults, + QuoterParameters, + SwapAdvancedSettings, + SwapParameters, + TradeParameters, +} from './types' +import { DEFAULT_QUOTE_VALIDITY, log } from './consts' + +import { + getQuoteAmountsAndCosts, + OrderBookApi, + OrderQuoteRequest, + OrderQuoteSideKindBuy, + OrderQuoteSideKindSell, + PriceQuality, + SigningScheme, +} from '../order-book' +import { buildAppData } from './appDataUtils' +import { getOrderToSign } from './getOrderToSign' +import { getIsEthFlowOrder, getSigner, swapParamsToLimitOrderParams } from './utils' +import { Signer } from 'ethers' +import { WRAPPED_NATIVE_CURRENCIES } from '../common' +import { getOrderTypedData } from './getOrderTypedData' + +// ETH-FLOW orders require different quote params +// check the isEthFlow flag and set in quote req obj +const ETH_FLOW_AUX_QUOTE_PARAMS = { + signingScheme: SigningScheme.EIP1271, + onchainOrder: true, + // Ethflow orders are subsidized in the backend. + // This means we can assume the verification gas costs are zero for the quote/fee estimation + verificationGasLimit: 0, +} + +export type QuoteResultsWithSigner = { result: QuoteResults & { signer: Signer }; orderBookApi: OrderBookApi } + +export async function getQuote( + _tradeParameters: TradeParameters, + trader: QuoterParameters, + advancedSettings?: SwapAdvancedSettings, + _orderBookApi?: OrderBookApi +): Promise<{ result: QuoteResults; orderBookApi: OrderBookApi }> { + const { appCode, chainId, account: from } = trader + const isEthFlow = getIsEthFlowOrder(_tradeParameters) + + const tradeParameters = isEthFlow + ? { + ..._tradeParameters, + sellToken: WRAPPED_NATIVE_CURRENCIES[chainId], + } + : _tradeParameters + + const { + sellToken, + sellTokenDecimals, + buyToken, + buyTokenDecimals, + amount, + kind, + partnerFee, + validFor = DEFAULT_QUOTE_VALIDITY, + slippageBps = 0, + env = 'prod', + } = tradeParameters + + log(`Swap ${amount} ${sellToken} for ${buyToken} on chain ${chainId}`) + + const orderBookApi = _orderBookApi || new OrderBookApi({ chainId, env }) + + const receiver = tradeParameters.receiver || from + const isSell = kind === 'sell' + + log('Building app data...') + + const appDataInfo = await buildAppData( + { + slippageBps, + orderClass: 'market', + appCode, + }, + advancedSettings?.appData + ) + + const { appDataKeccak256, fullAppData } = appDataInfo + + const quoteRequest: OrderQuoteRequest = { + from, + sellToken, + buyToken, + receiver, + validFor, + appData: fullAppData, + appDataHash: appDataKeccak256, + priceQuality: PriceQuality.OPTIMAL, // Do not change this parameter because we rely on the fact that quote has id + signingScheme: SigningScheme.EIP712, + ...(isEthFlow ? ETH_FLOW_AUX_QUOTE_PARAMS : {}), + ...(isSell + ? { kind: OrderQuoteSideKindSell.SELL, sellAmountBeforeFee: amount } + : { kind: OrderQuoteSideKindBuy.BUY, buyAmountAfterFee: amount }), + ...advancedSettings?.quoteRequest, + } + + log('Getting quote...') + + const quoteResponse = await orderBookApi.getQuote(quoteRequest) + + const amountsAndCosts = getQuoteAmountsAndCosts({ + orderParams: quoteResponse.quote, + slippagePercentBps: slippageBps, + partnerFeeBps: partnerFee?.bps, + sellDecimals: sellTokenDecimals, + buyDecimals: buyTokenDecimals, + }) + + const orderToSign = getOrderToSign( + { from, networkCostsAmount: quoteResponse.quote.feeAmount }, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + swapParamsToLimitOrderParams(tradeParameters, quoteResponse.id!, amountsAndCosts), + appDataInfo.appDataKeccak256 + ) + + const orderTypedData = await getOrderTypedData(chainId, orderToSign) + + return { + result: { + tradeParameters, + amountsAndCosts, + orderToSign, + quoteResponse, + appDataInfo, + orderTypedData, + }, + orderBookApi, + } +} + +export async function getQuoteWithSigner( + swapParameters: SwapParameters, + advancedSettings?: SwapAdvancedSettings, + orderBookApi?: OrderBookApi +): Promise { + const signer = getSigner(swapParameters.signer) + + const trader = { + chainId: swapParameters.chainId, + appCode: swapParameters.appCode, + account: (await signer.getAddress()) as AccountAddress, + } + + const result = await getQuote(swapParameters, trader, advancedSettings, orderBookApi) + + return { + result: { + ...result.result, + signer, + }, + orderBookApi: result.orderBookApi, + } +} diff --git a/src/trading/index.ts b/src/trading/index.ts new file mode 100644 index 00000000..1cc5e8a1 --- /dev/null +++ b/src/trading/index.ts @@ -0,0 +1,22 @@ +export * from './types' +export * from './tradingSdk' + +/** + * Main trading functions + */ +export { getQuote, getQuoteWithSigner } from './getQuote' +export { postSwapOrder, postSwapOrderFromQuote } from './postSwapOrder' +export { postLimitOrder } from './postLimitOrder' +export { postCoWProtocolTrade } from './postCoWProtocolTrade' +export { getOrderToSign } from './getOrderToSign' +export { postSellNativeCurrencyOrder } from './postSellNativeCurrencyOrder' +export { getEthFlowTransaction } from './getEthFlowTransaction' +export { getPreSignTransaction } from './getPreSignTransaction' + +/** + * Helpers + */ + +export * from './appDataUtils' +export * from './calculateUniqueOrderId' +export { swapParamsToLimitOrderParams, mapQuoteAmountsAndCosts } from './utils' diff --git a/src/trading/postCoWProtocolTrade.test.ts b/src/trading/postCoWProtocolTrade.test.ts new file mode 100644 index 00000000..dcc373b2 --- /dev/null +++ b/src/trading/postCoWProtocolTrade.test.ts @@ -0,0 +1,127 @@ +jest.mock('cross-fetch', () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const fetchMock = require('jest-fetch-mock') + // Require the original module to not be mocked... + const originalFetch = jest.requireActual('cross-fetch') + return { + __esModule: true, + ...originalFetch, + default: fetchMock, + } +}) + +jest.mock('../order-signing', () => { + return { + OrderSigningUtils: { + signOrder: jest.fn(), + }, + } +}) + +jest.mock('./postSellNativeCurrencyOrder', () => { + return { + postSellNativeCurrencyOrder: jest.fn(), + } +}) + +import { postCoWProtocolTrade } from './postCoWProtocolTrade' +import { postSellNativeCurrencyOrder } from './postSellNativeCurrencyOrder' + +import { AppDataInfo, LimitOrderParameters } from './types' +import { ETH_ADDRESS, SupportedChainId } from '../common' +import { OrderBookApi, OrderKind } from '../order-book' +import { OrderSigningUtils as OrderSigningUtilsMock } from '../order-signing' +import { VoidSigner } from '@ethersproject/abstract-signer' + +const defaultOrderParams: LimitOrderParameters = { + chainId: SupportedChainId.GNOSIS_CHAIN, + signer: '0x006', + appCode: '0x007', + sellToken: '0xaaa', + sellTokenDecimals: 18, + buyToken: '0xbbb', + buyTokenDecimals: 18, + sellAmount: '1000000000000000000', + buyAmount: '2000000000000000000', + kind: OrderKind.SELL, + quoteId: 31, + slippageBps: 50, +} + +const currentTimestamp = 1487076708000 + +const signatureMock = { signature: '0x000a1', signingScheme: 'eip712' } + +const signer = new VoidSigner('0x21c3de23d98caddc406e3d31b25e807addf33333') +signer.getChainId = jest.fn().mockResolvedValue(SupportedChainId.GNOSIS_CHAIN) + +const sendOrderMock = jest.fn() +const orderBookApiMock = { + sendOrder: sendOrderMock, +} as unknown as OrderBookApi +const appDataMock = { + appDataKeccak256: '0xaf1908d8e30f63bf4a6dbd41d2191eb092ac0af626b37c720596426130717658', + fullAppData: + '{\\"appCode\\":\\"CoW Swap\\",\\"environment\\":\\"barn\\",\\"metadata\\":{\\"orderClass\\":{\\"orderClass\\":\\"market\\"},\\"quote\\":{\\"slippageBips\\":201,\\"smartSlippage\\":true}},\\"version\\":\\"1.3.0\\"}', +} as unknown as AppDataInfo + +describe('postCoWProtocolTrade', () => { + let signOrderMock: jest.SpyInstance + let postSellNativeCurrencyOrderMock: jest.SpyInstance + + beforeAll(() => { + signOrderMock = OrderSigningUtilsMock.signOrder as unknown as jest.SpyInstance + postSellNativeCurrencyOrderMock = postSellNativeCurrencyOrder as unknown as jest.SpyInstance + }) + + beforeEach(() => { + Date.now = jest.fn(() => currentTimestamp) + signOrderMock.mockResolvedValue(signatureMock) + }) + + afterEach(() => { + signOrderMock.mockReset() + postSellNativeCurrencyOrderMock.mockReset() + sendOrderMock.mockReset() + }) + + it('When sell token is native, then should post on-chain order', async () => { + postSellNativeCurrencyOrderMock.mockResolvedValue({ orderId: '0x01' }) + + const order = { ...defaultOrderParams, sellToken: ETH_ADDRESS } + await postCoWProtocolTrade(orderBookApiMock, signer, appDataMock, order) + + expect(postSellNativeCurrencyOrderMock).toHaveBeenCalledTimes(1) + expect(postSellNativeCurrencyOrderMock).toHaveBeenCalledWith(orderBookApiMock, signer, appDataMock, order, '0') + }) + + it('API request should contain all specified parameters', async () => { + sendOrderMock.mockResolvedValue('0x02') + + const order = { ...defaultOrderParams } + await postCoWProtocolTrade(orderBookApiMock, signer, appDataMock, order) + + const callBody = sendOrderMock.mock.calls[0][0] + + expect(sendOrderMock).toHaveBeenCalledTimes(1) + expect(callBody).toEqual({ + appData: appDataMock.fullAppData, + appDataHash: appDataMock.appDataKeccak256, + sellToken: '0xaaa', + sellAmount: '1000000000000000000', + sellTokenBalance: 'erc20', + buyToken: '0xbbb', + buyAmount: '1990000000000000000', // Slippage is taken into account + buyTokenBalance: 'erc20', + feeAmount: '0', + from: '0x21c3de23d98caddc406e3d31b25e807addf33333', + kind: 'sell', + partiallyFillable: false, + quoteId: 31, + receiver: '0x21c3de23d98caddc406e3d31b25e807addf33333', + signature: '0x000a1', + signingScheme: 'eip712', + validTo: 1487077308, + }) + }) +}) diff --git a/src/trading/postCoWProtocolTrade.ts b/src/trading/postCoWProtocolTrade.ts new file mode 100644 index 00000000..098cabcf --- /dev/null +++ b/src/trading/postCoWProtocolTrade.ts @@ -0,0 +1,64 @@ +import { OrderBookApi, OrderCreation } from '../order-book' +import type { Signer } from 'ethers' +import { AppDataInfo, LimitTradeParameters } from './types' +import { log, SIGN_SCHEME_MAP } from './consts' +import { OrderSigningUtils } from '../order-signing' +import { getOrderToSign } from './getOrderToSign' +import { postSellNativeCurrencyOrder } from './postSellNativeCurrencyOrder' +import { getIsEthFlowOrder } from './utils' + +export async function postCoWProtocolTrade( + orderBookApi: OrderBookApi, + signer: Signer, + appData: AppDataInfo, + params: LimitTradeParameters, + networkCostsAmount = '0' +): Promise { + if (getIsEthFlowOrder(params)) { + const quoteId = params.quoteId + + if (typeof quoteId === 'number') { + const { orderId } = await postSellNativeCurrencyOrder( + orderBookApi, + signer, + appData, + { ...params, quoteId }, + networkCostsAmount + ) + + return orderId + } else { + throw new Error('quoteId is required for EthFlow orders') + } + } + + const { quoteId = null } = params + const { appDataKeccak256, fullAppData } = appData + + const chainId = await signer.getChainId() + const from = await signer.getAddress() + + const orderToSign = getOrderToSign({ from, networkCostsAmount }, params, appData.appDataKeccak256) + + log('Signing order...') + + const { signature, signingScheme } = await OrderSigningUtils.signOrder(orderToSign, chainId, signer) + + const orderBody: OrderCreation = { + ...orderToSign, + from, + signature, + signingScheme: SIGN_SCHEME_MAP[signingScheme], + quoteId, + appData: fullAppData, + appDataHash: appDataKeccak256, + } + + log('Posting order...') + + const orderId = await orderBookApi.sendOrder(orderBody) + + log(`Order created, id: ${orderId}`) + + return orderId +} diff --git a/src/trading/postLimitOrder.test.ts b/src/trading/postLimitOrder.test.ts new file mode 100644 index 00000000..6883593b --- /dev/null +++ b/src/trading/postLimitOrder.test.ts @@ -0,0 +1,94 @@ +jest.mock('cross-fetch', () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const fetchMock = require('jest-fetch-mock') + // Require the original module to not be mocked... + const originalFetch = jest.requireActual('cross-fetch') + return { + __esModule: true, + ...originalFetch, + default: fetchMock, + } +}) + +jest.mock('./postCoWProtocolTrade', () => { + return { + postCoWProtocolTrade: jest.fn(), + } +}) + +jest.mock('./appDataUtils', () => { + return { + buildAppData: jest.fn(), + } +}) + +import { postCoWProtocolTrade } from './postCoWProtocolTrade' +import { buildAppData } from './appDataUtils' + +import { AppDataInfo, LimitOrderParameters } from './types' +import { SupportedChainId } from '../common' +import { OrderBookApi, OrderKind } from '../order-book' +import { postLimitOrder } from './postLimitOrder' + +const defaultOrderParams: LimitOrderParameters = { + chainId: SupportedChainId.GNOSIS_CHAIN, + signer: '1bb337bafb276f779c3035874b8914e4b851bb989dbb34e776397076576f3804', + appCode: '0x007', + sellToken: '0xaaa', + sellTokenDecimals: 18, + buyToken: '0xbbb', + buyTokenDecimals: 18, + sellAmount: '1000000000000000000', + buyAmount: '2000000000000000000', + kind: OrderKind.SELL, + quoteId: 31, + slippageBps: 50, +} + +const currentTimestamp = 1487076708000 + +const orderBookApiMock = {} as unknown as OrderBookApi +const appDataMock = {} as unknown as AppDataInfo + +describe('postLimitOrder', () => { + let buildAppDataMock: jest.SpyInstance + let postCoWProtocolTradeMock: jest.SpyInstance + + beforeAll(() => { + buildAppDataMock = buildAppData as unknown as jest.SpyInstance + postCoWProtocolTradeMock = postCoWProtocolTrade as unknown as jest.SpyInstance + }) + + beforeEach(() => { + Date.now = jest.fn(() => currentTimestamp) + + buildAppDataMock.mockResolvedValue(appDataMock) + }) + + afterEach(() => { + buildAppDataMock.mockReset() + postCoWProtocolTradeMock.mockReset() + }) + + it('Should add advanced appData parameters', async () => { + const advancedData = { appData: { environment: 'sandbox' } } + + await postLimitOrder(defaultOrderParams, advancedData, orderBookApiMock) + + const call = buildAppDataMock.mock.calls[0][1] + + expect(call).toEqual(advancedData.appData) + }) + + it('Should call order posting with all specified parameters', async () => { + await postLimitOrder(defaultOrderParams, {}, orderBookApiMock) + + expect(postCoWProtocolTradeMock).toHaveBeenCalledTimes(1) + expect(postCoWProtocolTradeMock).toHaveBeenCalledWith( + orderBookApiMock, + expect.anything(), + appDataMock, + defaultOrderParams + ) + }) +}) diff --git a/src/trading/postLimitOrder.ts b/src/trading/postLimitOrder.ts new file mode 100644 index 00000000..51f6b605 --- /dev/null +++ b/src/trading/postLimitOrder.ts @@ -0,0 +1,32 @@ +import { LimitOrderAdvancedSettings, LimitOrderParameters } from './types' +import { log } from './consts' +import { OrderBookApi } from '../order-book' +import { buildAppData } from './appDataUtils' +import { postCoWProtocolTrade } from './postCoWProtocolTrade' +import { getSigner } from './utils' + +export async function postLimitOrder( + params: LimitOrderParameters, + advancedSettings?: LimitOrderAdvancedSettings, + _orderBookApi?: OrderBookApi +): Promise { + const { appCode, chainId, sellToken, buyToken, sellAmount, buyAmount, slippageBps = 0, env = 'prod' } = params + + log(`Limit order ${sellAmount} ${sellToken} for ${buyAmount} ${buyToken} on chain ${chainId}`) + + const signer = getSigner(params.signer) + const orderBookApi = _orderBookApi || new OrderBookApi({ chainId, env }) + + log('Building app data...') + + const appDataInfo = await buildAppData( + { + slippageBps, + orderClass: 'limit', + appCode, + }, + advancedSettings?.appData + ) + + return postCoWProtocolTrade(orderBookApi, signer, appDataInfo, params) +} diff --git a/src/trading/postSellNativeCurrencyOrder.test.ts b/src/trading/postSellNativeCurrencyOrder.test.ts new file mode 100644 index 00000000..308fbe3e --- /dev/null +++ b/src/trading/postSellNativeCurrencyOrder.test.ts @@ -0,0 +1,150 @@ +import { EthFlow__factory } from '../common/generated' +import { VoidSigner } from '@ethersproject/abstract-signer' +import { AppDataInfo, LimitOrderParameters } from './types' +import { SupportedChainId, WRAPPED_NATIVE_CURRENCIES } from '../common' +import { OrderBookApi, OrderKind } from '../order-book' +import { postSellNativeCurrencyOrder } from './postSellNativeCurrencyOrder' + +jest.mock('cross-fetch', () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const fetchMock = require('jest-fetch-mock') + // Require the original module to not be mocked... + const originalFetch = jest.requireActual('cross-fetch') + return { + __esModule: true, + ...originalFetch, + default: fetchMock, + } +}) + +jest.mock('../common/generated', () => { + const original = jest.requireActual('../common/generated') + + return { + ...original, + EthFlow__factory: { + connect: jest.fn(), + }, + } +}) + +const defaultOrderParams: LimitOrderParameters & { quoteId: number } = { + chainId: SupportedChainId.GNOSIS_CHAIN, + signer: '1bb337bafb276f779c3035874b8914e4b851bb989dbb34e776397076576f3804', + appCode: '0x007', + sellToken: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', + sellTokenDecimals: 18, + buyToken: '0x0625afb445c3b6b7b929342a04a22599fd5dbb59', + buyTokenDecimals: 18, + sellAmount: '1000000000000000000', + buyAmount: '2000000000000000000', + kind: OrderKind.SELL, + quoteId: 31, + slippageBps: 50, + validTo: 520, +} + +const account = '0x21c3de23d98caddc406e3d31b25e807addf33333' +const signer = new VoidSigner(account) + +signer.getChainId = jest.fn().mockResolvedValue(SupportedChainId.GNOSIS_CHAIN) + +const callData = '0x123456' +const currentTimestamp = 1487076708000 + +const uploadAppDataMock = jest.fn() +const orderBookApiMock = { uploadAppData: uploadAppDataMock } as unknown as OrderBookApi +const appDataMock = { + appDataKeccak256: '0xaf1908d8e30f63bf4a6dbd41d2191eb092ac0af626b37c720596426130717658', + fullAppData: + '{\\"appCode\\":\\"CoW Swap\\",\\"environment\\":\\"barn\\",\\"metadata\\":{\\"orderClass\\":{\\"orderClass\\":\\"market\\"},\\"quote\\":{\\"slippageBips\\":201,\\"smartSlippage\\":true}},\\"version\\":\\"1.3.0\\"}', +} as unknown as AppDataInfo + +let ethFlowContractFactoryMock: jest.SpyInstance +const ethFlowContractMock = { + estimateGas: { + createOrder: jest.fn(), + }, + createOrder: jest.fn(), + interface: { + encodeFunctionData: jest.fn().mockReturnValue(callData), + }, +} + +describe('postSellNativeCurrencyTrade', () => { + beforeAll(() => { + ethFlowContractFactoryMock = EthFlow__factory.connect as unknown as jest.SpyInstance + }) + + beforeEach(() => { + ethFlowContractFactoryMock.mockReturnValue(ethFlowContractMock) + uploadAppDataMock.mockResolvedValue(undefined) + ethFlowContractMock.estimateGas.createOrder.mockResolvedValue({ toHexString: () => '0x1' }) + signer.sendTransaction = jest.fn().mockImplementation(() => { + return Promise.resolve({ hash: '0xccdd11', orderId: '0xabc22' }) + }) + + Date.now = jest.fn(() => currentTimestamp) + }) + + afterEach(() => { + uploadAppDataMock.mockReset() + ethFlowContractFactoryMock.mockReset() + ethFlowContractMock.estimateGas.createOrder.mockReset() + ethFlowContractMock.interface.encodeFunctionData.mockReset() + }) + + it('Should call checkEthFlowOrderExists if it is set', async () => { + const checkEthFlowOrderExists = jest.fn().mockResolvedValue(false) + + await postSellNativeCurrencyOrder( + orderBookApiMock, + signer, + appDataMock, + defaultOrderParams, + '0', + checkEthFlowOrderExists + ) + + expect(checkEthFlowOrderExists).toHaveBeenCalledTimes(1) + }) + + it('Should upload appData', async () => { + await postSellNativeCurrencyOrder(orderBookApiMock, signer, appDataMock, defaultOrderParams) + + expect(uploadAppDataMock).toHaveBeenCalledWith(appDataMock.appDataKeccak256, appDataMock.fullAppData) + }) + + it('When transaction gas estimation is failed, then should use fallback value + 20%', async () => { + ethFlowContractMock.estimateGas.createOrder.mockRejectedValue(new Error('Estimation failed')) + + await postSellNativeCurrencyOrder(orderBookApiMock, signer, appDataMock, defaultOrderParams) + + const call = (signer.sendTransaction as jest.Mock).mock.calls[0][0] + + expect(+call.gas).toBe(180000) // 150000 by default + 20% + }) + + it('Should create an on-chain transaction with all specified parameters', async () => { + await postSellNativeCurrencyOrder(orderBookApiMock, signer, appDataMock, defaultOrderParams) + + expect(ethFlowContractMock.interface.encodeFunctionData).toHaveBeenCalledTimes(1) + expect(ethFlowContractMock.interface.encodeFunctionData).toHaveBeenCalledWith('createOrder', [ + { + appData: appDataMock.appDataKeccak256, + sellToken: WRAPPED_NATIVE_CURRENCIES[defaultOrderParams.chainId], + sellAmount: defaultOrderParams.sellAmount, + sellTokenBalance: 'erc20', + buyAmount: '1990000000000000000', // defaultOrderParams.buyAmount - slippage + buyToken: defaultOrderParams.buyToken, + buyTokenBalance: 'erc20', + feeAmount: '0', + partiallyFillable: false, + kind: defaultOrderParams.kind, + quoteId: defaultOrderParams.quoteId, + receiver: account, + validTo: defaultOrderParams.validTo?.toString(), + }, + ]) + }) +}) diff --git a/src/trading/postSellNativeCurrencyOrder.ts b/src/trading/postSellNativeCurrencyOrder.ts new file mode 100644 index 00000000..66692338 --- /dev/null +++ b/src/trading/postSellNativeCurrencyOrder.ts @@ -0,0 +1,35 @@ +import { Signer } from 'ethers' +import { AppDataInfo, LimitTradeParametersFromQuote } from './types' +import { EthFlowOrderExistsCallback } from './calculateUniqueOrderId' + +import { log } from './consts' +import { OrderBookApi } from '../order-book' +import { getEthFlowTransaction } from './getEthFlowTransaction' + +export async function postSellNativeCurrencyOrder( + orderBookApi: OrderBookApi, + signer: Signer, + appData: Pick, + _params: LimitTradeParametersFromQuote, + networkCostsAmount = '0', + checkEthFlowOrderExists?: EthFlowOrderExistsCallback +): Promise<{ txHash: string; orderId: string }> { + const { appDataKeccak256, fullAppData } = appData + + const { orderId, transaction } = await getEthFlowTransaction( + signer, + appDataKeccak256, + _params, + networkCostsAmount, + checkEthFlowOrderExists + ) + + log('Uploading app-data') + await orderBookApi.uploadAppData(appDataKeccak256, fullAppData) + + log('Sending on-chain order transaction') + const txReceipt = await signer.sendTransaction(transaction) + + log(`On-chain order transaction sent, txHash: ${txReceipt.hash}, order: ${orderId}`) + return { txHash: txReceipt.hash, orderId } +} diff --git a/src/trading/postSwapOrder.ts b/src/trading/postSwapOrder.ts new file mode 100644 index 00000000..88902054 --- /dev/null +++ b/src/trading/postSwapOrder.ts @@ -0,0 +1,23 @@ +import { SwapAdvancedSettings, SwapParameters } from './types' + +import { postCoWProtocolTrade } from './postCoWProtocolTrade' +import { getQuoteWithSigner, QuoteResultsWithSigner } from './getQuote' +import { swapParamsToLimitOrderParams } from './utils' + +export async function postSwapOrder(params: SwapParameters, advancedSettings?: SwapAdvancedSettings) { + return postSwapOrderFromQuote(await getQuoteWithSigner(params, advancedSettings)) +} + +export async function postSwapOrderFromQuote({ + orderBookApi, + result: { signer, appDataInfo, quoteResponse, tradeParameters, amountsAndCosts }, +}: QuoteResultsWithSigner): Promise { + return postCoWProtocolTrade( + orderBookApi, + signer, + appDataInfo, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + swapParamsToLimitOrderParams(tradeParameters, quoteResponse.id!, amountsAndCosts), + quoteResponse.quote.feeAmount + ) +} diff --git a/src/trading/tradingSdk.ts b/src/trading/tradingSdk.ts new file mode 100644 index 00000000..735b18b1 --- /dev/null +++ b/src/trading/tradingSdk.ts @@ -0,0 +1,62 @@ +import { + LimitOrderAdvancedSettings, + LimitTradeParameters, + QuoteAndPost, + SwapAdvancedSettings, + TradeParameters, + TraderParameters, +} from './types' +import { postSwapOrder, postSwapOrderFromQuote } from './postSwapOrder' +import { postLimitOrder } from './postLimitOrder' +import { getQuoteWithSigner } from './getQuote' +import { postSellNativeCurrencyOrder } from './postSellNativeCurrencyOrder' +import { getSigner, swapParamsToLimitOrderParams } from './utils' +import { getPreSignTransaction } from './getPreSignTransaction' + +export class TradingSdk { + constructor(public readonly traderParams: TraderParameters) {} + + async getQuote(params: TradeParameters, advancedSettings?: SwapAdvancedSettings): Promise { + const quoteResults = await getQuoteWithSigner(this.mergeParams(params), advancedSettings) + + return { + quoteResults: quoteResults.result, + postSwapOrderFromQuote: () => postSwapOrderFromQuote(quoteResults), + } + } + + async postSwapOrder(params: TradeParameters, advancedSettings?: SwapAdvancedSettings): Promise { + return postSwapOrder(this.mergeParams(params), advancedSettings) + } + + async postLimitOrder(params: LimitTradeParameters, advancedSettings?: LimitOrderAdvancedSettings): Promise { + return postLimitOrder(this.mergeParams(params), advancedSettings) + } + + async postSellNativeCurrencyOrder( + params: TradeParameters, + advancedSettings?: SwapAdvancedSettings + ): Promise> { + const quoteResults = await getQuoteWithSigner(this.mergeParams(params), advancedSettings) + + const { tradeParameters, quoteResponse, amountsAndCosts } = quoteResults.result + return postSellNativeCurrencyOrder( + quoteResults.orderBookApi, + quoteResults.result.signer, + quoteResults.result.appDataInfo, + // Quote response response always has an id + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + swapParamsToLimitOrderParams(tradeParameters, quoteResponse.id!, amountsAndCosts) + ) + } + + async getPreSignTransaction(params: { orderId: string; account: string }): ReturnType { + const signer = getSigner(this.traderParams.signer) + + return getPreSignTransaction(signer, this.traderParams.chainId, params.account, params.orderId) + } + + private mergeParams(params: T): T & TraderParameters { + return { ...params, ...this.traderParams } + } +} diff --git a/src/trading/types.ts b/src/trading/types.ts new file mode 100644 index 00000000..bd1821f2 --- /dev/null +++ b/src/trading/types.ts @@ -0,0 +1,165 @@ +import type { AppDataParams, latest, LatestAppDataDocVersion } from '@cowprotocol/app-data' +import { + AppData, + AppDataHash, + OrderKind, + OrderParameters, + OrderQuoteRequest, + OrderQuoteResponse, + QuoteAmountsAndCosts, + TokenAmount, +} from '../order-book' +import type { Signer } from '@ethersproject/abstract-signer' +import type { CowEnv, SupportedChainId } from '../common' +import type { ExternalProvider } from '@ethersproject/providers' +import type { UnsignedOrder } from '../order-signing' + +export type PrivateKey = string // 64 characters +export type AccountAddress = `0x${string}` // 42 characters + +export const ORDER_PRIMARY_TYPE = 'Order' as const + +/** + * EIP-712 typed data domain. + */ +interface TypedDataDomain { + name: string + version: string + chainId: number + verifyingContract: string +} + +/** + * EIP-712 typed data field. + */ +interface TypedDataField { + name: string + type: string +} + +/** + * EIP-712 typed data for an order. + */ +export interface OrderTypedData { + domain: TypedDataDomain + primaryType: typeof ORDER_PRIMARY_TYPE + types: Record + message: UnsignedOrder +} + +/** + * Minimal set of parameters to create a trade. + */ +export interface TradeBaseParameters { + kind: OrderKind + sellToken: OrderParameters['sellToken'] + sellTokenDecimals: number + buyToken: OrderParameters['buyToken'] + buyTokenDecimals: number + amount: TokenAmount +} + +/** + * Optional parameters to create a trade. + */ +export interface TradeOptionalParameters { + env?: CowEnv + partiallyFillable?: OrderParameters['partiallyFillable'] + slippageBps?: latest.SlippageBips + receiver?: OrderParameters['receiver'] + validFor?: OrderParameters['validTo'] + partnerFee?: latest.PartnerFee +} + +/** + * Information about the trader. + */ +export interface TraderParameters { + chainId: SupportedChainId + appCode: latest.AppCode + signer: Signer | ExternalProvider | PrivateKey +} + +export type QuoterParameters = Omit & { account: AccountAddress } + +/** + * Trade type, assets, amounts, and optional parameters. + */ +export interface TradeParameters extends TradeBaseParameters, TradeOptionalParameters {} + +export interface SwapParameters extends TradeParameters, TraderParameters {} + +export interface LimitTradeParameters extends Omit { + sellAmount: OrderParameters['sellAmount'] + buyAmount: OrderParameters['buyAmount'] + /** + * Id of the quote to be used for the limit order. + */ + quoteId?: number + validTo?: OrderParameters['validTo'] +} + +export interface LimitTradeParametersFromQuote extends LimitTradeParameters { + quoteId: number +} + +export interface LimitOrderParameters extends TraderParameters, LimitTradeParameters {} + +export interface SwapAdvancedSettings { + quoteRequest?: Partial> + appData?: AppDataParams +} + +export interface LimitOrderAdvancedSettings { + appData?: AppDataParams +} + +/** + * Exhaustive set of data which includes information about trade, quote, order, "app-data", and more. + * This data is used to create a trade, sign an order, and post it to the order book. + */ +export interface QuoteResults { + tradeParameters: TradeParameters + amountsAndCosts: QuoteAmountsAndCosts + orderToSign: UnsignedOrder + quoteResponse: OrderQuoteResponse + appDataInfo: AppDataInfo + orderTypedData: OrderTypedData +} + +export interface QuoteResultsSerialized extends Omit { + amountsAndCosts: QuoteAmountsAndCosts +} + +export interface QuoteAndPost { + quoteResults: QuoteResults + + postSwapOrderFromQuote(): Promise +} + +export type AppDataRootSchema = latest.AppDataRootSchema + +export interface BuildAppDataParams { + appCode: latest.AppCode + slippageBps: latest.SlippageBips + orderClass: latest.OrderClass['orderClass'] +} + +/** + * https://github.com/cowprotocol/app-data + */ +export interface AppDataInfo { + doc: LatestAppDataDocVersion + fullAppData: AppData + appDataKeccak256: AppDataHash +} + +/** + * A standard Ethereum transaction object + */ +export interface TransactionParams { + data: string + gas: string + to: string + value: string +} diff --git a/src/trading/utils.ts b/src/trading/utils.ts new file mode 100644 index 00000000..4e136d0e --- /dev/null +++ b/src/trading/utils.ts @@ -0,0 +1,78 @@ +import { LimitTradeParametersFromQuote, PrivateKey, TradeParameters } from './types' +import { QuoteAmountsAndCosts } from '../order-book' +import { ETH_ADDRESS } from '../common' +import { ethers, Signer } from 'ethers' +import { type ExternalProvider, Web3Provider } from '@ethersproject/providers' + +export function swapParamsToLimitOrderParams( + params: TradeParameters, + quoteId: number, + amounts: QuoteAmountsAndCosts +): LimitTradeParametersFromQuote { + return { + ...params, + sellAmount: amounts.afterSlippage.sellAmount.toString(), + buyAmount: amounts.afterSlippage.buyAmount.toString(), + quoteId, + } +} + +export function getIsEthFlowOrder(params: { sellToken: string }): boolean { + return params.sellToken.toLowerCase() === ETH_ADDRESS.toLowerCase() +} + +export function getSigner(signer: Signer | ExternalProvider | PrivateKey): Signer { + if (typeof signer === 'string') return new ethers.Wallet(signer) + + if ('request' in signer || 'send' in signer) { + const provider = new Web3Provider(signer) + + return provider.getSigner() + } + + return signer as Signer +} + +/** + * Returns the gas value plus a margin for unexpected or variable gas costs (20%) + * @param value the gas value to pad + */ +export function calculateGasMargin(value: bigint): bigint { + return value + (value * BigInt(20)) / BigInt(100) +} + +export function mapQuoteAmountsAndCosts( + value: QuoteAmountsAndCosts, + mapper: (value: T) => R +): QuoteAmountsAndCosts { + const { + costs: { networkFee, partnerFee }, + } = value + + function serializeAmounts(value: { sellAmount: T; buyAmount: T }): { sellAmount: R; buyAmount: R } { + return { + sellAmount: mapper(value.sellAmount), + buyAmount: mapper(value.buyAmount), + } + } + + return { + ...value, + costs: { + ...value.costs, + networkFee: { + ...networkFee, + amountInSellCurrency: mapper(networkFee.amountInSellCurrency), + amountInBuyCurrency: mapper(networkFee.amountInBuyCurrency), + }, + partnerFee: { + ...partnerFee, + amount: mapper(partnerFee.amount), + }, + }, + beforeNetworkCosts: serializeAmounts(value.beforeNetworkCosts), + afterNetworkCosts: serializeAmounts(value.afterNetworkCosts), + afterPartnerFees: serializeAmounts(value.afterPartnerFees), + afterSlippage: serializeAmounts(value.afterSlippage), + } +} diff --git a/yarn.lock b/yarn.lock index db9d8159..f8820f4c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -50,6 +50,11 @@ dependencies: node-fetch "^2.6.1" +"@assemblyscript/loader@^0.9.4": + version "0.9.4" + resolved "https://registry.yarnpkg.com/@assemblyscript/loader/-/loader-0.9.4.tgz#a483c54c1253656bb33babd464e3154a173e1577" + integrity sha512-HazVq9zwTVwGmqdwYzu7WyQ6FQVZ7SwET0KKQuKm55jD0IfUpZgN0OPIiZG3zV1iSrVYcN0bdwLRXI/VNCYsUA== + "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" @@ -1761,6 +1766,17 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@cowprotocol/app-data@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@cowprotocol/app-data/-/app-data-2.4.0.tgz#326e20065161e06308cf0ff429fe9dd2908c1c30" + integrity sha512-aG3CicUdR7jpY5/linxXmpL4axmiUvEwiHlOM0qKO/QdbNSntKNXjSu3r4QtHZ7BUiF1VUkcDVvvFW4D2MA0Rw== + dependencies: + ajv "^8.11.0" + cross-fetch "^3.1.5" + ipfs-only-hash "^4.0.0" + json-stringify-deterministic "^1.0.8" + multiformats "^9.6.4" + "@cowprotocol/contracts@^1.6.0": version "1.6.0" resolved "https://registry.yarnpkg.com/@cowprotocol/contracts/-/contracts-1.6.0.tgz#d0fc83ed8c624b968d1a68bb5c74712c11ec81e0" @@ -2557,6 +2573,18 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -2878,6 +2906,11 @@ resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796" integrity sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg== +"@multiformats/base-x@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@multiformats/base-x/-/base-x-4.0.1.tgz#95ff0fa58711789d53aefb2590a8b7a4e715d121" + integrity sha512-eMk0b9ReBbV23xXU693TAIrLyeO5iTgBZGSJfpqriG8UkYvr/hC9u9pyMlAakDNHWmbhMZCDs6KQO0jzKD8OTw== + "@noble/hashes@1.2.0", "@noble/hashes@~1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12" @@ -2944,6 +2977,64 @@ tslib "^2.5.0" webcrypto-core "^1.7.7" +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + +"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" + integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== + +"@protobufjs/base64@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" + integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== + +"@protobufjs/codegen@^2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" + integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== + +"@protobufjs/eventemitter@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" + integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q== + +"@protobufjs/fetch@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" + integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ== + dependencies: + "@protobufjs/aspromise" "^1.1.1" + "@protobufjs/inquire" "^1.1.0" + +"@protobufjs/float@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" + integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== + +"@protobufjs/inquire@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" + integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q== + +"@protobufjs/path@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" + integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA== + +"@protobufjs/pool@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" + integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== + +"@protobufjs/utf8@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" + integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== + "@repeaterjs/repeater@3.0.4", "@repeaterjs/repeater@^3.0.4": version "3.0.4" resolved "https://registry.yarnpkg.com/@repeaterjs/repeater/-/repeater-3.0.4.tgz#a04d63f4d1bf5540a41b01a921c9a7fddc3bd1ca" @@ -3184,6 +3275,11 @@ resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.5.tgz#738dd390a6ecc5442f35e7f03fa1431353f7e138" integrity sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA== +"@types/json-schema@^7.0.15": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + "@types/json-schema@^7.0.6", "@types/json-schema@^7.0.9": version "7.0.11" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" @@ -3194,15 +3290,39 @@ resolved "https://registry.yarnpkg.com/@types/json-stable-stringify/-/json-stable-stringify-1.0.34.tgz#c0fb25e4d957e0ee2e497c1f553d7f8bb668fd75" integrity sha512-s2cfwagOQAS8o06TcwKfr9Wx11dNGbH2E9vJz1cqV+a/LOyhWNLUNd6JSRYNzvB4d29UuJX2M0Dj9vE1T8fRXw== +"@types/long@^4.0.1": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a" + integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA== + +"@types/minimist@^1.2.0": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.5.tgz#ec10755e871497bcd83efe927e43ec46e8c0747e" + integrity sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag== + "@types/node@*": version "18.6.1" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.6.1.tgz#828e4785ccca13f44e2fb6852ae0ef11e3e20ba5" integrity sha512-z+2vB6yDt1fNwKOeGbckpmirO+VBDuQqecXkgeIqDlaOtmKn6hPR/viQ8cxCfqLU4fTlvM3+YjM367TukWdxpg== -"@types/node@^18.13.0": - version "18.13.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.13.0.tgz#0400d1e6ce87e9d3032c19eb6c58205b0d3f7850" - integrity sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg== +"@types/node@>=13.7.0": + version "20.14.9" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.9.tgz#12e8e765ab27f8c421a1820c99f5f313a933b420" + integrity sha512-06OCtnTXtWOZBJlRApleWndH4JsRVs1pDCc8dLSQp+7PpUpX3ePdHyeNSFTeSe7FtKyQkrlPvHwJOW3SLd8Oyg== + dependencies: + undici-types "~5.26.4" + +"@types/node@^22.9.0": + version "22.9.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.9.0.tgz#b7f16e5c3384788542c72dc3d561a7ceae2c0365" + integrity sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ== + dependencies: + undici-types "~6.19.8" + +"@types/normalize-package-data@^2.4.0": + version "2.4.4" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz#56e2cc26c397c038fab0e3a917a12d5c5909e901" + integrity sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA== "@types/parse-json@^4.0.0": version "4.0.0" @@ -3436,6 +3556,16 @@ ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: json-schema-traverse "^0.4.1" 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== + dependencies: + fast-deep-equal "^3.1.3" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.4.1" + ansi-escapes@^4.2.1, ansi-escapes@^4.3.0: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" @@ -3453,6 +3583,11 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +ansi-regex@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" + integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== + ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" @@ -3477,6 +3612,11 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + anymatch@^3.0.3: version "3.1.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" @@ -3525,6 +3665,11 @@ array-union@^2.1.0: resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== +arrify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + integrity sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA== + asap@~2.0.3: version "2.0.6" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" @@ -3789,6 +3934,20 @@ bl@^4.1.0: inherits "^2.0.4" readable-stream "^3.4.0" +bl@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-5.1.0.tgz#183715f678c7188ecef9fe475d90209400624273" + integrity sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ== + dependencies: + buffer "^6.0.3" + inherits "^2.0.4" + readable-stream "^3.4.0" + +blakejs@^1.1.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.2.1.tgz#5057e4206eadb4a97f7c0b6e197a505042fc3814" + integrity sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ== + bn.js@^4.11.9: version "4.12.0" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" @@ -3888,6 +4047,14 @@ buffer@^5.5.0: base64-js "^1.3.1" ieee754 "^1.1.13" +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + builtin-modules@^3.1.0: version "3.3.0" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" @@ -3926,6 +4093,15 @@ camel-case@^4.1.2: pascal-case "^3.1.2" tslib "^2.0.3" +camelcase-keys@^6.2.2: + version "6.2.2" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0" + integrity sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg== + dependencies: + camelcase "^5.3.1" + map-obj "^4.0.0" + quick-lru "^4.0.1" + camelcase@^5.0.0, camelcase@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" @@ -4072,6 +4248,16 @@ ci-info@^3.2.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.2.tgz#6d2967ffa407466481c6c90b6e16b3098f080128" integrity sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg== +cids@^1.0.0, cids@^1.1.5, cids@^1.1.6: + version "1.1.9" + resolved "https://registry.yarnpkg.com/cids/-/cids-1.1.9.tgz#402c26db5c07059377bcd6fb82f2a24e7f2f4a4f" + integrity sha512-l11hWRfugIcbGuTZwAM5PwpjPPjyb6UZOGwlHSnOBV5o07XhQ4gNpBN67FbODvpjyHtd+0Xs6KNvUcGBiDRsdg== + dependencies: + multibase "^4.0.1" + multicodec "^3.0.1" + multihashes "^4.0.1" + uint8arrays "^3.0.0" + cjs-module-lexer@^1.0.0: version "1.2.2" resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" @@ -4201,6 +4387,11 @@ command-line-usage@^6.1.0: table-layout "^1.0.2" typical "^5.2.0" +commander@^12.0.0: + version "12.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3" + integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA== + commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -4320,6 +4511,15 @@ cross-fetch@^3.0.4, cross-fetch@^3.1.5: dependencies: node-fetch "2.6.7" +cross-spawn@^7.0.0: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -4443,7 +4643,15 @@ debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: dependencies: ms "2.1.2" -decamelize@^1.2.0: +decamelize-keys@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.1.tgz#04a2d523b2f18d80d0158a43b895d56dff8d19d8" + integrity sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg== + dependencies: + decamelize "^1.1.0" + map-obj "^1.0.0" + +decamelize@^1.1.0, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== @@ -4603,6 +4811,11 @@ duplexer@^0.1.1, duplexer@^0.1.2, duplexer@~0.1.1: resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" @@ -4656,11 +4869,21 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + entities@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== +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" + integrity sha512-GiaH0KJUewYok+eeY05IIgjtAe4Yltygk9Wqp1V5yVWLdhf0hYZchRjNIT9bb0mSwRcIusT3cx7PJUf3zEIfUA== + error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" @@ -5225,6 +5448,14 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" +foreground-child@^3.1.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.0.tgz#0ac8644c06e431439f8561db8ecf29a7b5519c77" + integrity sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -5291,6 +5522,11 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function-bind@^1.1.2: + version "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: version "1.1.5" resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" @@ -5392,6 +5628,18 @@ glob@7.1.7: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^10.3.12: + version "10.4.5" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" + integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" + glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" @@ -5532,6 +5780,14 @@ gzip-size@^6.0.0: dependencies: duplexer "^0.1.2" +hamt-sharding@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/hamt-sharding/-/hamt-sharding-2.0.1.tgz#f45686d0339e74b03b233bee1bde9587727129b6" + integrity sha512-vnjrmdXG9dDs1m/H4iJ6z0JFI2NtgsW5keRkTcM85NGak69Mkf5PHUqBz+Xs0T4sg0ppvj9O5EGAJo40FTxmmA== + dependencies: + sparse-array "^1.3.1" + uint8arrays "^3.0.0" + handlebars@^4.7.7: version "4.7.7" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" @@ -5557,6 +5813,11 @@ har-validator@~5.1.3: ajv "^6.12.3" har-schema "^2.0.0" +hard-rejection@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" + integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== + has-ansi@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" @@ -5618,6 +5879,13 @@ hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.1" +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + header-case@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/header-case/-/header-case-2.0.4.tgz#5a42e63b55177349cf405beb8d775acabb92c063" @@ -5635,6 +5903,18 @@ hmac-drbg@^1.0.1: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" +hosted-git-info@^2.1.4: + version "2.8.9" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== + +hosted-git-info@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz#827b82867e9ff1c8d0c4d9d53880397d2c86d224" + integrity sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA== + dependencies: + lru-cache "^6.0.0" + html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" @@ -5687,7 +5967,7 @@ icss-utils@^5.0.0: resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== -ieee754@^1.1.13: +ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -5781,6 +6061,15 @@ inquirer@^8.0.0: through "^2.3.6" wrap-ansi "^6.0.1" +interface-ipld-format@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/interface-ipld-format/-/interface-ipld-format-1.0.1.tgz#bee39c70c584a033e186ff057a2be89f215963e3" + integrity sha512-WV/ar+KQJVoQpqRDYdo7YPGYIUHJxCuOEhdvsRpzLqoOIVCqPKdMMYmsLL1nCRsF3yYNio+PAJbCKiv6drrEAg== + dependencies: + cids "^1.1.6" + multicodec "^3.0.1" + multihashes "^4.0.2" + internal-slot@^1.0.3, internal-slot@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" @@ -5797,6 +6086,55 @@ invariant@^2.2.4: dependencies: loose-envify "^1.0.0" +ipfs-only-hash@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/ipfs-only-hash/-/ipfs-only-hash-4.0.0.tgz#b3bd60a244d9eb7394961aa9d812a2e5ac7c04d6" + integrity sha512-TE1DZCvfw8i3gcsTq3P4TFx3cKFJ3sluu/J3XINkJhIN9OwJgNMqKA+WnKx6ByCb1IoPXsTp1KM7tupElb6SyA== + dependencies: + ipfs-unixfs-importer "^7.0.1" + meow "^9.0.0" + +ipfs-unixfs-importer@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/ipfs-unixfs-importer/-/ipfs-unixfs-importer-7.0.3.tgz#b850e831ca9647d589ef50bc33421f65bab7bba6" + integrity sha512-qeFOlD3AQtGzr90sr5Tq1Bi8pT5Nr2tSI8z310m7R4JDYgZc6J1PEZO3XZQ8l1kuGoqlAppBZuOYmPEqaHcVQQ== + dependencies: + bl "^5.0.0" + cids "^1.1.5" + err-code "^3.0.1" + hamt-sharding "^2.0.0" + ipfs-unixfs "^4.0.3" + ipld-dag-pb "^0.22.2" + it-all "^1.0.5" + it-batch "^1.0.8" + it-first "^1.0.6" + it-parallel-batch "^1.0.9" + merge-options "^3.0.4" + multihashing-async "^2.1.0" + rabin-wasm "^0.1.4" + uint8arrays "^2.1.2" + +ipfs-unixfs@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/ipfs-unixfs/-/ipfs-unixfs-4.0.3.tgz#7c43e5726052ade4317245358ac541ef3d63d94e" + integrity sha512-hzJ3X4vlKT8FQ3Xc4M1szaFVjsc1ZydN+E4VQ91aXxfpjFn9G2wsMo1EFdAXNq/BUnN5dgqIOMP5zRYr3DTsAw== + dependencies: + err-code "^3.0.1" + protobufjs "^6.10.2" + +ipld-dag-pb@^0.22.2: + version "0.22.3" + resolved "https://registry.yarnpkg.com/ipld-dag-pb/-/ipld-dag-pb-0.22.3.tgz#6d5af28b5752236a5cb0e0a1888c87dd733b55cd" + integrity sha512-dfG5C5OVAR4FEP7Al2CrHWvAyIM7UhAQrjnOYOIxXGQz5NlEj6wGX0XQf6Ru6or1na6upvV3NQfstapQG8X2rg== + dependencies: + cids "^1.0.0" + interface-ipld-format "^1.0.0" + multicodec "^3.0.1" + multihashing-async "^2.0.0" + protobufjs "^6.10.2" + stable "^0.1.8" + uint8arrays "^2.0.5" + is-absolute@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-1.0.0.tgz#395e1ae84b11f26ad1795e73c17378e48a301576" @@ -5846,6 +6184,13 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== +is-core-module@^2.13.0, is-core-module@^2.5.0: + 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== + dependencies: + hasown "^2.0.2" + is-core-module@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" @@ -5926,6 +6271,16 @@ is-path-inside@^3.0.3: resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== +is-plain-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg== + +is-plain-obj@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== + is-reference@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" @@ -6096,6 +6451,37 @@ istanbul-reports@^3.1.3: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" +it-all@^1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/it-all/-/it-all-1.0.6.tgz#852557355367606295c4c3b7eff0136f07749335" + integrity sha512-3cmCc6Heqe3uWi3CVM/k51fa/XbMFpQVzFoDsV0IZNHSQDyAXl3c4MjHkFX5kF3922OGj7Myv1nSEUgRtcuM1A== + +it-batch@^1.0.8, it-batch@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/it-batch/-/it-batch-1.0.9.tgz#7e95aaacb3f9b1b8ca6c8b8367892171d6a5b37f" + integrity sha512-7Q7HXewMhNFltTsAMdSz6luNhyhkhEtGGbYek/8Xb/GiqYMtwUmopE1ocPSiJKKp3rM4Dt045sNFoUu+KZGNyA== + +it-first@^1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/it-first/-/it-first-1.0.7.tgz#a4bef40da8be21667f7d23e44dae652f5ccd7ab1" + integrity sha512-nvJKZoBpZD/6Rtde6FXqwDqDZGF1sCADmr2Zoc0hZsIvnE449gRFnGctxDf09Bzc/FWnHXAdaHVIetY6lrE0/g== + +it-parallel-batch@^1.0.9: + version "1.0.11" + resolved "https://registry.yarnpkg.com/it-parallel-batch/-/it-parallel-batch-1.0.11.tgz#f889b4e1c7a62ef24111dbafbaaa010b33d00f69" + integrity sha512-UWsWHv/kqBpMRmyZJzlmZeoAMA0F3SZr08FBdbhtbe+MtoEBgr/ZUAKrnenhXCBrsopy76QjRH2K/V8kNdupbQ== + dependencies: + it-batch "^1.0.9" + +jackspeak@^3.1.2: + version "3.4.3" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" + integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + jake@^10.8.5: version "10.8.5" resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46" @@ -6606,6 +6992,11 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + json-schema@0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" @@ -6623,6 +7014,11 @@ json-stable-stringify@^1.0.1: dependencies: jsonify "^0.0.1" +json-stringify-deterministic@^1.0.8: + version "1.0.12" + resolved "https://registry.yarnpkg.com/json-stringify-deterministic/-/json-stringify-deterministic-1.0.12.tgz#aaa3f907466ed01e3afd77b898d0a2b3b132820a" + integrity sha512-q3PN0lbUdv0pmurkBNdJH3pfFvOTL/Zp0lquqpvcjfKzt6Y0j49EPHAmVHCAS4Ceq/Y+PejWTzyiVpoY71+D6g== + json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" @@ -6636,7 +7032,7 @@ json-to-pretty-yaml@^1.2.2: remedial "^1.0.7" remove-trailing-spaces "^1.0.6" -json5@^2.2.0, json5@^2.2.2: +json5@^2.2.0, json5@^2.2.2, json5@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== @@ -6682,6 +7078,11 @@ just-performance@4.3.0: resolved "https://registry.yarnpkg.com/just-performance/-/just-performance-4.3.0.tgz#cc2bc8c9227f09e97b6b1df4cd0de2df7ae16db1" integrity sha512-L7RjvtJsL0QO8xFs5wEoDDzzJwoiowRw6Rn/GnvldlchS2JQr9wFYPiwZcDfrbbujEKqKN0tvENdbjXdYhDp5Q== +kind-of@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + kleur@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" @@ -6813,6 +7214,11 @@ log-update@^4.0.0: slice-ansi "^4.0.0" wrap-ansi "^6.2.0" +long@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" + integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== + loose-envify@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -6834,6 +7240,11 @@ lower-case@^2.0.2: dependencies: tslib "^2.0.3" +lru-cache@^10.2.0: + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -6879,6 +7290,16 @@ map-cache@^0.2.0: resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" integrity sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg== +map-obj@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + integrity sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg== + +map-obj@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.3.0.tgz#9304f906e93faae70880da102a9f1df0ea8bb05a" + integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== + map-stream@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" @@ -6899,6 +7320,31 @@ mdn-data@2.0.14: resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== +meow@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-9.0.0.tgz#cd9510bc5cac9dee7d03c73ee1f9ad959f4ea364" + integrity sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ== + dependencies: + "@types/minimist" "^1.2.0" + camelcase-keys "^6.2.2" + decamelize "^1.2.0" + decamelize-keys "^1.1.0" + hard-rejection "^2.1.0" + minimist-options "4.1.0" + normalize-package-data "^3.0.0" + read-pkg-up "^7.0.1" + redent "^3.0.0" + trim-newlines "^3.0.0" + type-fest "^0.18.0" + yargs-parser "^20.2.3" + +merge-options@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/merge-options/-/merge-options-3.0.4.tgz#84709c2aa2a4b24c1981f66c179fe5565cc6dbb7" + integrity sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ== + dependencies: + is-plain-obj "^2.1.0" + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -6987,6 +7433,11 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== + minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" @@ -7018,11 +7469,32 @@ minimatch@^5.0.1: dependencies: brace-expansion "^2.0.1" +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + +minimist-options@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" + integrity sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A== + dependencies: + arrify "^1.0.1" + is-plain-obj "^1.1.0" + kind-of "^6.0.3" + minimist@^1.2.5: version "1.2.6" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + mkdirp@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" @@ -7038,6 +7510,52 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +multibase@^4.0.1: + version "4.0.6" + resolved "https://registry.yarnpkg.com/multibase/-/multibase-4.0.6.tgz#6e624341483d6123ca1ede956208cb821b440559" + integrity sha512-x23pDe5+svdLz/k5JPGCVdfn7Q5mZVMBETiC+ORfO+sor9Sgs0smJzAjfTbM5tckeCqnaUuMYoz+k3RXMmJClQ== + dependencies: + "@multiformats/base-x" "^4.0.1" + +multicodec@^3.0.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/multicodec/-/multicodec-3.2.1.tgz#82de3254a0fb163a107c1aab324f2a91ef51efb2" + integrity sha512-+expTPftro8VAW8kfvcuNNNBgb9gPeNYV9dn+z1kJRWF2vih+/S79f2RVeIwmrJBUJ6NT9IUPWnZDQvegEh5pw== + dependencies: + uint8arrays "^3.0.0" + varint "^6.0.0" + +multiformats@^9.4.2, multiformats@^9.6.4: + version "9.9.0" + resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.9.0.tgz#c68354e7d21037a8f1f8833c8ccd68618e8f1d37" + integrity sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg== + +multihashes@^4.0.1, multihashes@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/multihashes/-/multihashes-4.0.3.tgz#426610539cd2551edbf533adeac4c06b3b90fb05" + integrity sha512-0AhMH7Iu95XjDLxIeuCOOE4t9+vQZsACyKZ9Fxw2pcsRmlX4iCn1mby0hS0bb+nQOVpdQYWPpnyusw4da5RPhA== + dependencies: + multibase "^4.0.1" + uint8arrays "^3.0.0" + varint "^5.0.2" + +multihashing-async@^2.0.0, multihashing-async@^2.1.0: + version "2.1.4" + resolved "https://registry.yarnpkg.com/multihashing-async/-/multihashing-async-2.1.4.tgz#26dce2ec7a40f0e7f9e732fc23ca5f564d693843" + integrity sha512-sB1MiQXPSBTNRVSJc2zM157PXgDtud2nMFUEIvBrsq5Wv96sUclMRK/ecjoP1T/W61UJBqt4tCTwMkUpt2Gbzg== + dependencies: + blakejs "^1.1.0" + err-code "^3.0.0" + js-sha3 "^0.8.0" + multihashes "^4.0.1" + murmurhash3js-revisited "^3.0.0" + uint8arrays "^3.0.0" + +murmurhash3js-revisited@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/murmurhash3js-revisited/-/murmurhash3js-revisited-3.0.0.tgz#6bd36e25de8f73394222adc6e41fa3fac08a5869" + integrity sha512-/sF3ee6zvScXMb1XFJ8gDsSnY+X8PbOyjIuBhtgis10W2Jx4ZjIhikUCIF9c4gpJxVnQIsPAFrSwTCuAjicP6g== + mute-stream@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" @@ -7110,6 +7628,26 @@ node-releases@^2.0.8: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f" integrity sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w== +normalize-package-data@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-package-data@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-3.0.3.tgz#dbcc3e2da59509a0983422884cd172eefdfa525e" + integrity sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA== + dependencies: + hosted-git-info "^4.0.1" + is-core-module "^2.5.0" + semver "^7.3.4" + validate-npm-package-license "^3.0.1" + normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" @@ -7312,6 +7850,11 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +package-json-from-dist@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== + param-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" @@ -7399,6 +7942,14 @@ path-root@^0.1.1: dependencies: path-root-regex "^0.1.0" +path-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" @@ -7789,6 +8340,25 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.5" +protobufjs@^6.10.2: + version "6.11.4" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.4.tgz#29a412c38bf70d89e537b6d02d904a6f448173aa" + integrity sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/long" "^4.0.1" + "@types/node" ">=13.7.0" + long "^4.0.0" + ps-tree@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.2.0.tgz#5e7425b89508736cdd4f2224d028f7bb3f722ebd" @@ -7838,6 +8408,23 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +quick-lru@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" + integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== + +rabin-wasm@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/rabin-wasm/-/rabin-wasm-0.1.5.tgz#5b625ca007d6a2cbc1456c78ae71d550addbc9c9" + integrity sha512-uWgQTo7pim1Rnj5TuWcCewRDTf0PEFTSlaUjWP4eY9EbLV9em08v89oCz/WO+wRxpYuO36XEHp4wgYQnAgOHzA== + dependencies: + "@assemblyscript/loader" "^0.9.4" + bl "^5.0.0" + debug "^4.3.1" + minimist "^1.2.5" + node-fetch "^2.6.1" + readable-stream "^3.6.0" + randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -7850,7 +8437,26 @@ react-is@^18.0.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== -readable-stream@^3.4.0: +read-pkg-up@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" + integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== + dependencies: + find-up "^4.1.0" + read-pkg "^5.2.0" + type-fest "^0.8.1" + +read-pkg@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" + integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== + dependencies: + "@types/normalize-package-data" "^2.4.0" + normalize-package-data "^2.5.0" + parse-json "^5.0.0" + type-fest "^0.6.0" + +readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.2" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== @@ -7866,6 +8472,14 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" +redent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" + integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== + dependencies: + indent-string "^4.0.0" + strip-indent "^3.0.0" + reduce-flatten@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/reduce-flatten/-/reduce-flatten-2.0.0.tgz#734fd84e65f375d7ca4465c69798c25c9d10ae27" @@ -7988,6 +8602,11 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + require-main-filename@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" @@ -8022,6 +8641,15 @@ resolve.exports@^2.0.0: resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.0.tgz#c1a0028c2d166ec2fbf7d0644584927e76e7400e" integrity sha512-6K/gDlqgQscOlg9fSRpWstA8sYe8rbELsSTNpx+3kTrsVCzvSl0zIvRErM7fdl9ERWDsKnrLnwB+Ne89918XOg== +resolve@^1.10.0: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + resolve@^1.14.2, resolve@^1.17.0, resolve@^1.19.0, resolve@^1.20.0: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" @@ -8178,6 +8806,11 @@ safe-regex-test@^1.0.0: get-intrinsic "^1.1.3" is-regex "^1.1.4" +safe-stable-stringify@^2.4.3: + version "2.5.0" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz#4ca2f8e385f2831c432a719b108a3bf7af42a1dd" + integrity sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA== + "safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -8193,6 +8826,11 @@ scuid@^1.1.0: resolved "https://registry.yarnpkg.com/scuid/-/scuid-1.1.0.tgz#d3f9f920956e737a60f72d0e4ad280bf324d5dab" integrity sha512-MuCAyrGZcTLfQoH2XoBlQ8C6bzwN88XT/0slOGz0pn8+gIP85BOAfYa44ZXQUTOwRwPU0QvgU+V+OSajl/59Xg== +"semver@2 || 3 || 4 || 5": + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" @@ -8203,6 +8841,11 @@ semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== +semver@^7.3.4: + version "7.6.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" + integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== + semver@^7.3.7: version "7.3.7" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" @@ -8274,6 +8917,11 @@ signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + signedsource@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/signedsource/-/signedsource-1.0.0.tgz#1ddace4981798f93bd833973803d80d52e93ad6a" @@ -8351,6 +8999,37 @@ sourcemap-codec@^1.4.8: resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== +sparse-array@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/sparse-array/-/sparse-array-1.3.2.tgz#0e1a8b71706d356bc916fe754ff496d450ec20b0" + integrity sha512-ZT711fePGn3+kQyLuv1fpd3rNSkNF8vd5Kv2D+qnOANeyKs3fx6bUMGWRPvgTTcYV64QMqZKZwcuaQSP3AZ0tg== + +spdx-correct@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" + integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz#5d607d27fc806f66d7b64a766650fa890f04ed66" + integrity sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.18" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.18.tgz#22aa922dcf2f2885a6494a261f2d8b75345d0326" + integrity sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ== + split@0.3: version "0.3.3" resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" @@ -8437,6 +9116,15 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" @@ -8446,6 +9134,15 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + string.prototype.matchall@^4.0.6: version "4.0.8" resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz#3bf85722021816dcd1bf38bb714915887ca79fd3" @@ -8485,6 +9182,13 @@ string_decoder@^1.1.1: dependencies: safe-buffer "~5.2.0" +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" @@ -8499,6 +9203,13 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + strip-bom@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" @@ -8509,6 +9220,13 @@ strip-final-newline@^2.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" + integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== + dependencies: + min-indent "^1.0.0" + strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" @@ -8669,6 +9387,11 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== +trim-newlines@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" + integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== + ts-command-line-args@^2.2.0: version "2.5.1" resolved "https://registry.yarnpkg.com/ts-command-line-args/-/ts-command-line-args-2.5.1.tgz#e64456b580d1d4f6d948824c274cf6fa5f45f7f0" @@ -8684,6 +9407,20 @@ ts-essentials@^7.0.1: resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-7.0.3.tgz#686fd155a02133eedcc5362dc8b5056cde3e5a38" integrity sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ== +ts-json-schema-generator@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/ts-json-schema-generator/-/ts-json-schema-generator-2.3.0.tgz#d533027cdb13b625acba0a3e931a4ba88f0e44ad" + integrity sha512-t4lBQAwZc0sOJq9LJt3NgbznIcslVnm0JeEMFq8qIRklpMRY8jlYD0YmnRWbqBKANxkby91P1XanSSlSOFpUmg== + dependencies: + "@types/json-schema" "^7.0.15" + commander "^12.0.0" + glob "^10.3.12" + json5 "^2.2.3" + normalize-path "^3.0.0" + safe-stable-stringify "^2.4.3" + tslib "^2.6.2" + typescript "^5.4.5" + ts-log@^2.2.3: version "2.2.5" resolved "https://registry.yarnpkg.com/ts-log/-/ts-log-2.2.5.tgz#aef3252f1143d11047e2cb6f7cfaac7408d96623" @@ -8740,6 +9477,11 @@ tslib@^2.0.3, tslib@^2.4.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== +tslib@^2.6.2: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + tslib@~2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" @@ -8781,6 +9523,11 @@ type-detect@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-fest@^0.18.0: + version "0.18.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" + integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw== + type-fest@^0.20.2: version "0.20.2" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" @@ -8791,6 +9538,16 @@ type-fest@^0.21.3: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== +type-fest@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" + integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + typechain@^8.2.0: version "8.3.1" resolved "https://registry.yarnpkg.com/typechain/-/typechain-8.3.1.tgz#dccbc839b94877997536c356380eff7325395cfb" @@ -8821,6 +9578,11 @@ typescript@^4.1.3, typescript@^4.9.5: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== +typescript@^5.4.5: + version "5.6.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.3.tgz#5f3449e31c9d94febb17de03cc081dd56d81db5b" + integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw== + typical@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/typical/-/typical-4.0.0.tgz#cbeaff3b9d7ae1e2bbfaf5a4e6f11eccfde94fc4" @@ -8841,6 +9603,20 @@ uglify-js@^3.1.4: resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c" integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g== +uint8arrays@^2.0.5, uint8arrays@^2.1.2: + version "2.1.10" + resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-2.1.10.tgz#34d023c843a327c676e48576295ca373c56e286a" + integrity sha512-Q9/hhJa2836nQfEJSZTmr+pg9+cDJS9XEAp7N2Vg5MzL3bK/mkMVfjscRGYruP9jNda6MAdf4QD/y78gSzkp6A== + dependencies: + multiformats "^9.4.2" + +uint8arrays@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-3.1.1.tgz#2d8762acce159ccd9936057572dade9459f65ae0" + integrity sha512-+QJa8QRnbdXVpHYjLoTpJIdCTiw9Ir62nocClWuXIq2JIh4Uta0cQsTSpFL678p2CN8B+XSApwcU+pQEqVpKWg== + dependencies: + multiformats "^9.4.2" + unbox-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" @@ -8856,6 +9632,16 @@ unc-path-regex@^0.1.2: resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" integrity sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg== +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.19.8: + version "6.19.8" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" + integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== + unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" @@ -8934,7 +9720,7 @@ upper-case@^2.0.2: dependencies: tslib "^2.0.3" -uri-js@^4.2.2: +uri-js@^4.2.2, uri-js@^4.4.1: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== @@ -8977,11 +9763,29 @@ v8-to-istanbul@^9.0.1: "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^1.6.0" +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + value-or-promise@^1.0.11, value-or-promise@^1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/value-or-promise/-/value-or-promise-1.0.12.tgz#0e5abfeec70148c78460a849f6b003ea7986f15c" integrity sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q== +varint@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/varint/-/varint-5.0.2.tgz#5b47f8a947eb668b848e034dcfa87d0ff8a7f7a4" + integrity sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow== + +varint@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/varint/-/varint-6.0.0.tgz#9881eb0ce8feaea6512439d19ddf84bf551661d0" + integrity sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg== + verror@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" @@ -9087,6 +9891,15 @@ wordwrapjs@^4.0.0: reduce-flatten "^2.0.0" typical "^5.2.0" +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" @@ -9105,6 +9918,15 @@ wrap-ansi@^7.0.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -9173,6 +9995,11 @@ yargs-parser@^18.1.2: camelcase "^5.0.0" decamelize "^1.2.0" +yargs-parser@^20.2.3: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + yargs-parser@^21.1.1: version "21.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"