diff --git a/.github/workflows/balancer-js.yaml b/.github/workflows/balancer-js.yaml index 1a524660b..1005ebdec 100644 --- a/.github/workflows/balancer-js.yaml +++ b/.github/workflows/balancer-js.yaml @@ -78,9 +78,12 @@ jobs: run: yarn subgraph:generate - name: Compile run: yarn build + - name: Run node in background for integration tests + run: npx hardhat node --hostname 127.0.0.1 & - name: Test run: yarn test env: CI: true INFURA: ${{ secrets.INFURA }} + ALCHEMY_URL: ${{ secrets.ALCHEMY_URL }} diff --git a/balancer-js/.eslintrc.js b/balancer-js/.eslintrc.js index b7c354ad5..00a9809b5 100644 --- a/balancer-js/.eslintrc.js +++ b/balancer-js/.eslintrc.js @@ -1,11 +1,11 @@ module.exports = { - root: true, - parser: '@typescript-eslint/parser', - plugins: ['@typescript-eslint', 'prettier', 'mocha-no-only'], - extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], - rules: { - 'comma-spacing': ['error', { before: false, after: true }], - 'prettier/prettier': 'error', - 'mocha-no-only/mocha-no-only': ['error'], - }, + root: true, + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint', 'prettier', 'mocha-no-only'], + extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], + rules: { + 'comma-spacing': ['error', { before: false, after: true }], + 'prettier/prettier': 'error', + 'mocha-no-only/mocha-no-only': ['error'], + }, }; diff --git a/balancer-js/.gitignore b/balancer-js/.gitignore index 60489f21e..4f9e5b2ba 100644 --- a/balancer-js/.gitignore +++ b/balancer-js/.gitignore @@ -5,4 +5,5 @@ yarn-error.log .env .idea/ dist/ -src/subgraph/generated/ \ No newline at end of file +src/subgraph/generated/ +cache/ diff --git a/balancer-js/.prettierrc b/balancer-js/.prettierrc index d2a3ebea3..0104164bf 100644 --- a/balancer-js/.prettierrc +++ b/balancer-js/.prettierrc @@ -1,11 +1,11 @@ { - "printWidth": 80, - "tabWidth": 4, - "singleQuote": true, - "bracketSpacing": true, - "trailingComma": "es5", - "semi": true, - "newline-before-return": true, - "no-duplicate-variable": [true, "check-parameters"], - "no-var-keyword": true + "printWidth": 80, + "tabWidth": 2, + "singleQuote": true, + "bracketSpacing": true, + "trailingComma": "es5", + "semi": true, + "newline-before-return": true, + "no-duplicate-variable": [true, "check-parameters"], + "no-var-keyword": true } diff --git a/balancer-js/README.md b/balancer-js/README.md index c8805f522..ab9b99952 100644 --- a/balancer-js/README.md +++ b/balancer-js/README.md @@ -16,6 +16,55 @@ const config: BalancerSdkConfig = { const balancer = new BalancerSDK(config); ``` +In some examples we present a way to make end to end trades against mainnet state. To run them you will need to setup a localhost test node using tools like ganache, hardhat, anvil. + +Installation instructions for: + +* [Hardhat](https://hardhat.org/getting-started/#installation) + + To start a forked node: + ``` + npm run node + ``` + +* [Anvil](https://github.com/foundry-rs/foundry/tree/master/anvil#installation) - use with caution, still experimental. + + To start a forked node: + ``` + anvil -f FORKABLE_RPC_URL (optional pinned block: --fork-block-number XXX) + ``` + +## Swaps Module + +Exposes complete functionality for token swapping. An example of using the module with data fetched from the subgraph: + +```js +// Uses SOR to find optimal route for a trading pair and amount +const route = balancer.swaps.findRouteGivenIn({ + tokenIn, + tokenOut, + amount, + gasPrice, + maxPools, +}) + +// Prepares transaction attributes based on the route +const transactionAttributes = balancer.swaps.buildSwap({ + userAddress, + swapInfo: route, + kind: 0, // 0 - givenIn, 1 - givenOut + deadline, + maxSlippage, +}) + +// Extract parameters required for sendTransaction +const { to, data, value } = transactionAttributes + +// Execution with ethers.js +const transactionResponse = await signer.sendTransaction({ to, data, value }) +``` + + ## SwapsService The SwapsService provides function to query and make swaps using Balancer V2 liquidity. diff --git a/balancer-js/examples/batchSwap.ts b/balancer-js/examples/batchSwap.ts index f089125d6..1a8e98e89 100644 --- a/balancer-js/examples/batchSwap.ts +++ b/balancer-js/examples/batchSwap.ts @@ -17,63 +17,65 @@ const SENDER_WALLET_ADDRESS = '0x35f5a330FD2F8e521ebd259FA272bA8069590741'; Example showing how to encode and send a batch swap transaction. */ async function runBatchSwap() { - console.log('PRIVATE_KEY', TRADER_KEY); + console.log('PRIVATE_KEY', TRADER_KEY); - const encodedBatchSwapData = Swaps.encodeBatchSwap({ - kind: SwapType.SwapExactIn, - swaps: [ - // First pool swap: 10000 USDC => ? DAI - { - poolId: '0x0cdab06b07197d96369fea6f3bea6efc7ecdf7090002000000000000000003de', - // USDC - assetInIndex: 0, - // DAI - assetOutIndex: 1, - amount: '10000', - userData: '0x', - }, - // Second pool swap: 10000 DAI => ? USDC - { - poolId: '0x17018c2f7c345add873474879ff0ed98ebd6346a000200000000000000000642', - // DAI - assetInIndex: 1, - // USDC - assetOutIndex: 0, - amount: '10000', - userData: '0x', - }, - ], - assets: [ - // USDC - '0xc2569dd7d0fd715b054fbf16e75b001e5c0c1115', - // DAI - '0x04df6e4121c27713ed22341e7c7df330f56f289b', - ], - funds: { - fromInternalBalance: false, - // These can be different addresses! - recipient: RECIPIENT_WALLET_ADDRESS, - sender: SENDER_WALLET_ADDRESS, - toInternalBalance: false, - }, - limits: ['0', '0'], // No limits - deadline: '999999999999999999', // Infinity - }); + const encodedBatchSwapData = Swaps.encodeBatchSwap({ + kind: SwapType.SwapExactIn, + swaps: [ + // First pool swap: 10000 USDC => ? DAI + { + poolId: + '0x0cdab06b07197d96369fea6f3bea6efc7ecdf7090002000000000000000003de', + // USDC + assetInIndex: 0, + // DAI + assetOutIndex: 1, + amount: '10000', + userData: '0x', + }, + // Second pool swap: 10000 DAI => ? USDC + { + poolId: + '0x17018c2f7c345add873474879ff0ed98ebd6346a000200000000000000000642', + // DAI + assetInIndex: 1, + // USDC + assetOutIndex: 0, + amount: '10000', + userData: '0x', + }, + ], + assets: [ + // USDC + '0xc2569dd7d0fd715b054fbf16e75b001e5c0c1115', + // DAI + '0x04df6e4121c27713ed22341e7c7df330f56f289b', + ], + funds: { + fromInternalBalance: false, + // These can be different addresses! + recipient: RECIPIENT_WALLET_ADDRESS, + sender: SENDER_WALLET_ADDRESS, + toInternalBalance: false, + }, + limits: ['0', '0'], // No limits + deadline: '999999999999999999', // Infinity + }); - const provider = new InfuraProvider(Network.KOVAN, process.env.INFURA); - const wallet = new Wallet(TRADER_KEY as string, provider); + const provider = new InfuraProvider(Network.KOVAN, process.env.INFURA); + const wallet = new Wallet(TRADER_KEY as string, provider); - const tx = await wallet.sendTransaction({ - data: encodedBatchSwapData, - to: balancerVault, - /** - * The following gas inputs are optional, - **/ - // gasPrice: '6000000000', - // gasLimit: '2000000', - }); + const tx = await wallet.sendTransaction({ + data: encodedBatchSwapData, + to: balancerVault, + /** + * The following gas inputs are optional, + **/ + // gasPrice: '6000000000', + // gasLimit: '2000000', + }); - console.log(tx); + console.log(tx); } // yarn examples:run ./examples/batchSwap.ts diff --git a/balancer-js/examples/constants.ts b/balancer-js/examples/constants.ts index 094707f0c..d0b389bed 100644 --- a/balancer-js/examples/constants.ts +++ b/balancer-js/examples/constants.ts @@ -1,64 +1,64 @@ interface TestToken { - address: string; - decimals: number; + address: string; + decimals: number; } // Kovan version export const bbausd: TestToken = { - address: '0x8fd162f338b770f7e879030830cde9173367f301', - decimals: 18, + address: '0x8fd162f338b770f7e879030830cde9173367f301', + decimals: 18, }; // Kovan version export const AAVE_USDT: TestToken = { - address: '0x13512979ade267ab5100878e2e0f485b568328a4', - decimals: 6, + address: '0x13512979ade267ab5100878e2e0f485b568328a4', + decimals: 6, }; // Kovan version export const AAVE_USDC: TestToken = { - address: '0xe22da380ee6b445bb8273c81944adeb6e8450422', - decimals: 6, + address: '0xe22da380ee6b445bb8273c81944adeb6e8450422', + decimals: 6, }; // Kovan version export const AAVE_DAI: TestToken = { - address: '0xff795577d9ac8bd7d90ee22b6c1703490b6512fd', - decimals: 18, + address: '0xff795577d9ac8bd7d90ee22b6c1703490b6512fd', + decimals: 18, }; // Kovan version export const WRAPPED_AAVE_USDT: TestToken = { - address: '0xe8191aacfcdb32260cda25830dc6c9342142f310', - decimals: 6, + address: '0xe8191aacfcdb32260cda25830dc6c9342142f310', + decimals: 6, }; // Kovan version export const WRAPPED_AAVE_USDC: TestToken = { - address: '0x0fbddc06a4720408a2f5eb78e62bc31ac6e2a3c4', - decimals: 6, + address: '0x0fbddc06a4720408a2f5eb78e62bc31ac6e2a3c4', + decimals: 6, }; // Kovan version export const WRAPPED_AAVE_DAI: TestToken = { - address: '0x4811a7bb9061a46753486e5e84b3cd3d668fb596', - decimals: 18, + address: '0x4811a7bb9061a46753486e5e84b3cd3d668fb596', + decimals: 18, }; // Kovan version export const USDC: TestToken = { - address: '0xc2569dd7d0fd715b054fbf16e75b001e5c0c1115', - decimals: 6, + address: '0xc2569dd7d0fd715b054fbf16e75b001e5c0c1115', + decimals: 6, }; // Kovan version export const USDT: TestToken = { - address: '0xcc08220af469192c53295fdd34cfb8df29aa17ab', - decimals: 6, + address: '0xcc08220af469192c53295fdd34cfb8df29aa17ab', + decimals: 6, }; // Kovan version export const DAI: TestToken = { - address: '0x04df6e4121c27713ed22341e7c7df330f56f289b', - decimals: 6, + address: '0x04df6e4121c27713ed22341e7c7df330f56f289b', + decimals: 6, }; diff --git a/balancer-js/examples/queryBatchSwap.ts b/balancer-js/examples/queryBatchSwap.ts index d46c214f2..ce2f3e65b 100644 --- a/balancer-js/examples/queryBatchSwap.ts +++ b/balancer-js/examples/queryBatchSwap.ts @@ -1,59 +1,62 @@ import dotenv from 'dotenv'; import { - BalancerSDK, - Network, - SwapType, - BatchSwapStep, - BalancerSdkConfig, + BalancerSDK, + Network, + SwapType, + BatchSwapStep, + BalancerSdkConfig, } from '../src/index'; dotenv.config(); async function runQueryBatchSwap() { - const config: BalancerSdkConfig = { - network: Network.KOVAN, - rpcUrl: `https://kovan.infura.io/v3/${process.env.INFURA}`, - }; - const balancer = new BalancerSDK(config); + const config: BalancerSdkConfig = { + network: Network.KOVAN, + rpcUrl: `https://kovan.infura.io/v3/${process.env.INFURA}`, + }; + const balancer = new BalancerSDK(config); - const swapType = SwapType.SwapExactOut; - const swaps: BatchSwapStep[] = [ - { - poolId: '0x6a8c3239695613c0710dc971310b36f9b81e115e00000000000000000000023e', - assetInIndex: 2, - assetOutIndex: 3, - amount: '123456', - userData: '0x', - }, - { - poolId: '0x21ff756ca0cfcc5fff488ad67babadffee0c4149000000000000000000000240', - assetInIndex: 1, - assetOutIndex: 2, - amount: '0', - userData: '0x', - }, - { - poolId: '0xcd32a460b6fecd053582e43b07ed6e2c04e1536900000000000000000000023c', - assetInIndex: 0, - assetOutIndex: 1, - amount: '0', - userData: '0x', - }, - ]; + const swapType = SwapType.SwapExactOut; + const swaps: BatchSwapStep[] = [ + { + poolId: + '0x6a8c3239695613c0710dc971310b36f9b81e115e00000000000000000000023e', + assetInIndex: 2, + assetOutIndex: 3, + amount: '123456', + userData: '0x', + }, + { + poolId: + '0x21ff756ca0cfcc5fff488ad67babadffee0c4149000000000000000000000240', + assetInIndex: 1, + assetOutIndex: 2, + amount: '0', + userData: '0x', + }, + { + poolId: + '0xcd32a460b6fecd053582e43b07ed6e2c04e1536900000000000000000000023c', + assetInIndex: 0, + assetOutIndex: 1, + amount: '0', + userData: '0x', + }, + ]; - const assets: string[] = [ - '0xff795577d9ac8bd7d90ee22b6c1703490b6512fd', - '0xcd32a460b6fecd053582e43b07ed6e2c04e15369', - '0x6a8c3239695613c0710dc971310b36f9b81e115e', - '0x13512979ade267ab5100878e2e0f485b568328a4', - ]; + const assets: string[] = [ + '0xff795577d9ac8bd7d90ee22b6c1703490b6512fd', + '0xcd32a460b6fecd053582e43b07ed6e2c04e15369', + '0x6a8c3239695613c0710dc971310b36f9b81e115e', + '0x13512979ade267ab5100878e2e0f485b568328a4', + ]; - const deltas = await balancer.swaps.queryBatchSwap({ - kind: swapType, - swaps, - assets, - }); - console.log(deltas.toString()); + const deltas = await balancer.swaps.queryBatchSwap({ + kind: swapType, + swaps, + assets, + }); + console.log(deltas.toString()); } // yarn examples:run ./examples/queryBatchSwap.ts diff --git a/balancer-js/examples/queryBatchSwapWithSor.ts b/balancer-js/examples/queryBatchSwapWithSor.ts index 36ee4fa1e..399285853 100644 --- a/balancer-js/examples/queryBatchSwapWithSor.ts +++ b/balancer-js/examples/queryBatchSwapWithSor.ts @@ -1,99 +1,87 @@ import dotenv from 'dotenv'; import { parseFixed, BigNumber } from '@ethersproject/bignumber'; import { - BalancerSDK, - BalancerSdkConfig, - Network, - SwapType, + BalancerSDK, + BalancerSdkConfig, + Network, + SwapType, } from '../src/index'; import { AAVE_DAI, AAVE_USDC, AAVE_USDT, bbausd } from './constants'; dotenv.config(); async function runQueryBatchSwapWithSor() { - const config: BalancerSdkConfig = { - network: Network.KOVAN, - rpcUrl: `https://kovan.infura.io/v3/${process.env.INFURA}`, - }; - const balancer = new BalancerSDK(config); + const config: BalancerSdkConfig = { + network: Network.KOVAN, + rpcUrl: `https://kovan.infura.io/v3/${process.env.INFURA}`, + }; + const balancer = new BalancerSDK(config); - const poolsFetched = await balancer.swaps.fetchPools(); - if (!poolsFetched) { - console.log(`Error fetching pools data.`); - return; - } + const poolsFetched = await balancer.swaps.fetchPools(); + if (!poolsFetched) { + console.log(`Error fetching pools data.`); + return; + } - // Example showing how to join bb-a-usd pool by swapping stables > BPT - let queryResult = await balancer.swaps.queryBatchSwapWithSor({ - tokensIn: [AAVE_DAI.address, AAVE_USDC.address, AAVE_USDT.address], - tokensOut: [ - bbausd.address, - bbausd.address, - bbausd.address, - ], - swapType: SwapType.SwapExactIn, - amounts: [ - parseFixed('100', 18).toString(), - parseFixed('100', 6).toString(), - parseFixed('100', 6).toString(), - ], - fetchPools: { - fetchPools: false, // Because pools were previously fetched we can reuse to speed things up - fetchOnChain: false, - }, - }); - console.log(`\n******* stables > BPT ExactIn`); - console.log(queryResult.swaps); - console.log(queryResult.assets); - console.log(queryResult.deltas.toString()); - console.log(queryResult.returnAmounts.toString()); + // Example showing how to join bb-a-usd pool by swapping stables > BPT + let queryResult = await balancer.swaps.queryBatchSwapWithSor({ + tokensIn: [AAVE_DAI.address, AAVE_USDC.address, AAVE_USDT.address], + tokensOut: [bbausd.address, bbausd.address, bbausd.address], + swapType: SwapType.SwapExactIn, + amounts: [ + parseFixed('100', 18).toString(), + parseFixed('100', 6).toString(), + parseFixed('100', 6).toString(), + ], + fetchPools: { + fetchPools: false, // Because pools were previously fetched we can reuse to speed things up + fetchOnChain: false, + }, + }); + console.log(`\n******* stables > BPT ExactIn`); + console.log(queryResult.swaps); + console.log(queryResult.assets); + console.log(queryResult.deltas.toString()); + console.log(queryResult.returnAmounts.toString()); - // Example showing how to exit bb-a-usd pool by swapping BPT > stables - queryResult = await balancer.swaps.queryBatchSwapWithSor({ - tokensIn: [ - bbausd.address, - bbausd.address, - bbausd.address, - ], - tokensOut: [AAVE_DAI.address, AAVE_USDC.address, AAVE_USDT.address], - swapType: SwapType.SwapExactIn, - amounts: [ - parseFixed('1', 18).toString(), - parseFixed('1', 18).toString(), - parseFixed('1', 18).toString(), - ], - fetchPools: { - fetchPools: false, - fetchOnChain: false, - }, - }); - console.log(`\n******* BPT > stables ExactIn`); - console.log(queryResult.swaps); - console.log(queryResult.assets); - console.log(queryResult.deltas.toString()); - console.log(queryResult.returnAmounts.toString()); + // Example showing how to exit bb-a-usd pool by swapping BPT > stables + queryResult = await balancer.swaps.queryBatchSwapWithSor({ + tokensIn: [bbausd.address, bbausd.address, bbausd.address], + tokensOut: [AAVE_DAI.address, AAVE_USDC.address, AAVE_USDT.address], + swapType: SwapType.SwapExactIn, + amounts: [ + parseFixed('1', 18).toString(), + parseFixed('1', 18).toString(), + parseFixed('1', 18).toString(), + ], + fetchPools: { + fetchPools: false, + fetchOnChain: false, + }, + }); + console.log(`\n******* BPT > stables ExactIn`); + console.log(queryResult.swaps); + console.log(queryResult.assets); + console.log(queryResult.deltas.toString()); + console.log(queryResult.returnAmounts.toString()); - queryResult = await balancer.swaps.queryBatchSwapWithSor({ - tokensIn: [ - bbausd.address, - bbausd.address, - bbausd.address, - ], - tokensOut: [AAVE_DAI.address, AAVE_USDC.address, AAVE_USDT.address], - swapType: SwapType.SwapExactOut, - amounts: queryResult.returnAmounts.map((amt) => - BigNumber.from(amt).abs().toString() - ), - fetchPools: { - fetchPools: false, - fetchOnChain: false, - }, - }); - console.log(`\n******* BPT > stables Exact Out`); - console.log(queryResult.swaps); - console.log(queryResult.assets); - console.log(queryResult.deltas.toString()); - console.log(queryResult.returnAmounts.toString()); + queryResult = await balancer.swaps.queryBatchSwapWithSor({ + tokensIn: [bbausd.address, bbausd.address, bbausd.address], + tokensOut: [AAVE_DAI.address, AAVE_USDC.address, AAVE_USDT.address], + swapType: SwapType.SwapExactOut, + amounts: queryResult.returnAmounts.map((amt) => + BigNumber.from(amt).abs().toString() + ), + fetchPools: { + fetchPools: false, + fetchOnChain: false, + }, + }); + console.log(`\n******* BPT > stables Exact Out`); + console.log(queryResult.swaps); + console.log(queryResult.assets); + console.log(queryResult.deltas.toString()); + console.log(queryResult.returnAmounts.toString()); } // yarn examples:run ./examples/queryBatchSwapWithSor.ts diff --git a/balancer-js/examples/querySimpleFlashSwap.ts b/balancer-js/examples/querySimpleFlashSwap.ts index 822f8bc2e..1dd67cc56 100644 --- a/balancer-js/examples/querySimpleFlashSwap.ts +++ b/balancer-js/examples/querySimpleFlashSwap.ts @@ -23,20 +23,20 @@ const rpcUrl = `https://kovan.infura.io/v3/${INFURA}`; - If the flash swap isn't profitable, the interal flash loan will fail. */ async function runQueryFlashSwap() { - const config: BalancerSdkConfig = { network, rpcUrl }; + const config: BalancerSdkConfig = { network, rpcUrl }; - const balancer = new BalancerSDK(config); + const balancer = new BalancerSDK(config); - const response = await balancer.swaps.querySimpleFlashSwap({ - flashLoanAmount: '100', - poolIds: [ - '0x0cdab06b07197d96369fea6f3bea6efc7ecdf7090002000000000000000003de', - '0x17018c2f7c345add873474879ff0ed98ebd6346a000200000000000000000642', - ], - assets: [USDC.address, DAI.address], - }); + const response = await balancer.swaps.querySimpleFlashSwap({ + flashLoanAmount: '100', + poolIds: [ + '0x0cdab06b07197d96369fea6f3bea6efc7ecdf7090002000000000000000003de', + '0x17018c2f7c345add873474879ff0ed98ebd6346a000200000000000000000642', + ], + assets: [USDC.address, DAI.address], + }); - console.table(response); + console.table(response); } // yarn examples:run ./examples/queryFlashSwap.ts diff --git a/balancer-js/examples/relayerExitPoolAndBatchSwap.ts b/balancer-js/examples/relayerExitPoolAndBatchSwap.ts index 05e4b2400..a6b45fb9a 100644 --- a/balancer-js/examples/relayerExitPoolAndBatchSwap.ts +++ b/balancer-js/examples/relayerExitPoolAndBatchSwap.ts @@ -4,10 +4,10 @@ import { Wallet } from '@ethersproject/wallet'; import { JsonRpcProvider } from '@ethersproject/providers'; import { Contract } from '@ethersproject/contracts'; import { - BalancerSDK, - BalancerSdkConfig, - Network, - StablePoolEncoder, + BalancerSDK, + BalancerSdkConfig, + Network, + StablePoolEncoder, } from '../src/index'; import { AAVE_DAI, AAVE_USDT, bbausd } from './constants'; @@ -21,72 +21,73 @@ User must approve relayer. Vault must have approvals for tokens. */ async function relayerExitPoolAndBatchSwap() { - const config: BalancerSdkConfig = { - network: Network.KOVAN, - rpcUrl: `https://kovan.infura.io/v3/${process.env.INFURA}`, - }; + const config: BalancerSdkConfig = { + network: Network.KOVAN, + rpcUrl: `https://kovan.infura.io/v3/${process.env.INFURA}`, + }; - const provider = new JsonRpcProvider(config.rpcUrl); - const key: any = process.env.TRADER_KEY; - const relayerAddress = '0x3C255DE4a73Dd251A33dac2ab927002C964Eb2cB'; - const wallet = new Wallet(key, provider); + const provider = new JsonRpcProvider(config.rpcUrl); + const key: any = process.env.TRADER_KEY; + const relayerAddress = '0x3C255DE4a73Dd251A33dac2ab927002C964Eb2cB'; + const wallet = new Wallet(key, provider); - const balancer = new BalancerSDK(config); + const balancer = new BalancerSDK(config); - /* + /* This creates pool request for exactTokensOut. Here minAmoutsOut is known because it just matches the exact out amounts. maxBptIn should set to a known amount based on pool balances, etc. */ - // const exactPoolTokensOut = ['100000', '100000000000000000']; - // const expectedAmountsOut = exactPoolTokensOut; - // const maxBptIn = MaxUint256; - // const userData = StablePoolEncoder.exitBPTInForExactTokensOut( - // exactPoolTokensOut, - // maxBptIn - // ); + // const exactPoolTokensOut = ['100000', '100000000000000000']; + // const expectedAmountsOut = exactPoolTokensOut; + // const maxBptIn = MaxUint256; + // const userData = StablePoolEncoder.exitBPTInForExactTokensOut( + // exactPoolTokensOut, + // maxBptIn + // ); - /* + /* This creates pool request for exactBPTIn. expectedAmountsOut should be set to a known/realistic value as this is used to estimate swap/limit amounts and can cause issues if off. */ - const bptAmountIn = '2049658696117824796'; - const expectedAmountsOut = ['1019700', '1029989699999948233']; - const userData = StablePoolEncoder.exitExactBPTInForTokensOut(bptAmountIn); + const bptAmountIn = '2049658696117824796'; + const expectedAmountsOut = ['1019700', '1029989699999948233']; + const userData = StablePoolEncoder.exitExactBPTInForTokensOut(bptAmountIn); - const txInfo = await balancer.relayer.exitPoolAndBatchSwap({ - exiter: wallet.address, // exiter is address that holds BPT used to exit pool - swapRecipient: wallet.address, // recipient is address that receives final tokens - poolId: '0xf5f6fb82649df7991054ef796c39da81b93364df0002000000000000000004a5', // USDT/DAI pool - exitTokens: [AAVE_USDT.address, AAVE_DAI.address], - userData, - expectedAmountsOut, - finalTokensOut: [bbausd.address, bbausd.address], - slippage: '50000000000000000', // Slippage for swap 5% - fetchPools: { - fetchPools: true, - fetchOnChain: false, - }, - }); + const txInfo = await balancer.relayer.exitPoolAndBatchSwap({ + exiter: wallet.address, // exiter is address that holds BPT used to exit pool + swapRecipient: wallet.address, // recipient is address that receives final tokens + poolId: + '0xf5f6fb82649df7991054ef796c39da81b93364df0002000000000000000004a5', // USDT/DAI pool + exitTokens: [AAVE_USDT.address, AAVE_DAI.address], + userData, + expectedAmountsOut, + finalTokensOut: [bbausd.address, bbausd.address], + slippage: '50000000000000000', // Slippage for swap 5% + fetchPools: { + fetchPools: true, + fetchOnChain: false, + }, + }); - console.log(`Amounts of tokensOut:`); - console.log(txInfo.outputs?.amountsOut?.toString()); + console.log(`Amounts of tokensOut:`); + console.log(txInfo.outputs?.amountsOut?.toString()); - const relayerContract = new Contract( - relayerAddress, - balancerRelayerAbi, - provider - ); - const tx = await relayerContract - .connect(wallet) - .callStatic[txInfo.function](txInfo.params, { - value: '0', - // gasPrice: '6000000000', - // gasLimit: '2000000', - }); + const relayerContract = new Contract( + relayerAddress, + balancerRelayerAbi, + provider + ); + const tx = await relayerContract + .connect(wallet) + .callStatic[txInfo.function](txInfo.params, { + value: '0', + // gasPrice: '6000000000', + // gasLimit: '2000000', + }); - console.log(`Swap Deltas:`); - console.log(defaultAbiCoder.decode(['int256[]'], tx[1]).toString()); + console.log(`Swap Deltas:`); + console.log(defaultAbiCoder.decode(['int256[]'], tx[1]).toString()); } // yarn examples:run ./examples/relayerExitPoolAndBatchSwap.ts diff --git a/balancer-js/examples/relayerSwapUnwrap.ts b/balancer-js/examples/relayerSwapUnwrap.ts index 375b6d8af..c5a0319e4 100644 --- a/balancer-js/examples/relayerSwapUnwrap.ts +++ b/balancer-js/examples/relayerSwapUnwrap.ts @@ -6,11 +6,7 @@ import { JsonRpcProvider } from '@ethersproject/providers'; import { Contract } from '@ethersproject/contracts'; import linearPoolAbi from '../src/lib/abi/LinearPool.json'; -import { - BalancerSDK, - Network, - BalancerSdkConfig, -} from '../src/index'; +import { BalancerSDK, Network, BalancerSdkConfig } from '../src/index'; import { FundManagement } from '../src/modules/swaps/types'; import balancerRelayerAbi from '../src/lib/abi/BalancerRelayer.json'; @@ -24,76 +20,76 @@ User must approve relayer Vault must have approvals for tokens */ async function runRelayerSwapUnwrapExactIn() { - const config: BalancerSdkConfig = { - network: Network.MAINNET, - rpcUrl: `https://mainnet.infura.io/v3/${process.env.INFURA}`, - }; - - const provider = new JsonRpcProvider(config.rpcUrl); - const key: any = process.env.TRADER_KEY; - const relayerAddress = '0xAc9f49eF3ab0BbC929f7b1bb0A17E1Fca5786251'; - const wallet = new Wallet(key, provider); - - const balancer = new BalancerSDK(config); - - // Creates fund management info for swap part of call - const funds: FundManagement = { - sender: wallet.address, - recipient: relayerAddress, // Note relayer is recipient of swaps - fromInternalBalance: false, - toInternalBalance: false, - }; - - const bbausd = '0x7b50775383d3d6f0215a8f290f2c9e2eebbeceb2'; - const bbadai = '0x804CdB9116a10bB78768D3252355a1b18067bF8f'; - const bbausdc = '0x9210F1204b5a24742Eba12f710636D76240dF3d0'; - const bbausdt = '0x2BBf681cC4eb09218BEe85EA2a5d3D13Fa40fC0C'; - const daiLinearPool = new Contract(bbadai, linearPoolAbi, provider); - const usdcLinearPool = new Contract(bbausdc, linearPoolAbi, provider); - const usdtLinearPool = new Contract(bbausdt, linearPoolAbi, provider); - // This is gets the up to date rates for the Aave tokens - const daiRate = await daiLinearPool.getWrappedTokenRate(); - const usdcRate = await usdcLinearPool.getWrappedTokenRate(); - const usdtRate = await usdtLinearPool.getWrappedTokenRate(); - - console.log(`DAI Rate: ${daiRate.toString()}`); - console.log(`USDC Rate: ${usdcRate.toString()}`); - console.log(`USDT Rate: ${usdtRate.toString()}`); - - const txInfo = await balancer.relayer.swapUnwrapAaveStaticExactIn( - [bbausd, bbausd, bbausd], - [ - '0x02d60b84491589974263d922d9cc7a3152618ef6', // waDAI - '0xd093fa4fb80d09bb30817fdcd442d4d02ed3e5de', // waUSDC - '0xf8fd466f12e236f4c96f7cce6c79eadb819abf58', // waUSDT - ], - [ - parseFixed('30000000', 18).toString(), - parseFixed('30000000', 18).toString(), - parseFixed('30000000', 18).toString(), - ], - [daiRate, usdcRate, usdtRate], - funds, - '50000000000000000' // Slippage 5% - ); - - const relayerContract = new Contract( - relayerAddress, - balancerRelayerAbi, - provider - ); - - console.log(`Unwrapped Amounts Out:`); - console.log(txInfo.outputs?.amountsOut?.toString()); - const tx = await relayerContract - .connect(wallet) - .callStatic[txInfo.function](txInfo.params, { - value: '0', - // gasLimit: '2000000', - }); - - console.log(`Swap Deltas:`); - console.log(defaultAbiCoder.decode(['int256[]'], tx[0]).toString()); + const config: BalancerSdkConfig = { + network: Network.MAINNET, + rpcUrl: `https://mainnet.infura.io/v3/${process.env.INFURA}`, + }; + + const provider = new JsonRpcProvider(config.rpcUrl); + const key: any = process.env.TRADER_KEY; + const relayerAddress = '0xAc9f49eF3ab0BbC929f7b1bb0A17E1Fca5786251'; + const wallet = new Wallet(key, provider); + + const balancer = new BalancerSDK(config); + + // Creates fund management info for swap part of call + const funds: FundManagement = { + sender: wallet.address, + recipient: relayerAddress, // Note relayer is recipient of swaps + fromInternalBalance: false, + toInternalBalance: false, + }; + + const bbausd = '0x7b50775383d3d6f0215a8f290f2c9e2eebbeceb2'; + const bbadai = '0x804CdB9116a10bB78768D3252355a1b18067bF8f'; + const bbausdc = '0x9210F1204b5a24742Eba12f710636D76240dF3d0'; + const bbausdt = '0x2BBf681cC4eb09218BEe85EA2a5d3D13Fa40fC0C'; + const daiLinearPool = new Contract(bbadai, linearPoolAbi, provider); + const usdcLinearPool = new Contract(bbausdc, linearPoolAbi, provider); + const usdtLinearPool = new Contract(bbausdt, linearPoolAbi, provider); + // This is gets the up to date rates for the Aave tokens + const daiRate = await daiLinearPool.getWrappedTokenRate(); + const usdcRate = await usdcLinearPool.getWrappedTokenRate(); + const usdtRate = await usdtLinearPool.getWrappedTokenRate(); + + console.log(`DAI Rate: ${daiRate.toString()}`); + console.log(`USDC Rate: ${usdcRate.toString()}`); + console.log(`USDT Rate: ${usdtRate.toString()}`); + + const txInfo = await balancer.relayer.swapUnwrapAaveStaticExactIn( + [bbausd, bbausd, bbausd], + [ + '0x02d60b84491589974263d922d9cc7a3152618ef6', // waDAI + '0xd093fa4fb80d09bb30817fdcd442d4d02ed3e5de', // waUSDC + '0xf8fd466f12e236f4c96f7cce6c79eadb819abf58', // waUSDT + ], + [ + parseFixed('30000000', 18).toString(), + parseFixed('30000000', 18).toString(), + parseFixed('30000000', 18).toString(), + ], + [daiRate, usdcRate, usdtRate], + funds, + '50000000000000000' // Slippage 5% + ); + + const relayerContract = new Contract( + relayerAddress, + balancerRelayerAbi, + provider + ); + + console.log(`Unwrapped Amounts Out:`); + console.log(txInfo.outputs?.amountsOut?.toString()); + const tx = await relayerContract + .connect(wallet) + .callStatic[txInfo.function](txInfo.params, { + value: '0', + // gasLimit: '2000000', + }); + + console.log(`Swap Deltas:`); + console.log(defaultAbiCoder.decode(['int256[]'], tx[0]).toString()); } /* @@ -103,76 +99,72 @@ User must approve relayer Vault must have approvals for tokens */ async function runRelayerSwapUnwrapExactOut() { - const config: BalancerSdkConfig = { - network: Network.MAINNET, - rpcUrl: `https://mainnet.infura.io/v3/${process.env.INFURA}`, - }; - - const provider = new JsonRpcProvider(config.rpcUrl); - const key: any = process.env.TRADER_KEY; - const relayerAddress = '0xAc9f49eF3ab0BbC929f7b1bb0A17E1Fca5786251'; - const wallet = new Wallet(key, provider); - - const balancer = new BalancerSDK(config); - - // Creates fund management info for swap part of call - const funds: FundManagement = { - sender: wallet.address, - recipient: relayerAddress, // Note relayer is recipient of swaps - fromInternalBalance: false, - toInternalBalance: false, - }; - - const bbausd = '0x7b50775383d3d6f0215a8f290f2c9e2eebbeceb2'; - const bbadai = '0x804CdB9116a10bB78768D3252355a1b18067bF8f'; - const bbausdc = '0x9210F1204b5a24742Eba12f710636D76240dF3d0'; - const bbausdt = '0x2BBf681cC4eb09218BEe85EA2a5d3D13Fa40fC0C'; - const daiLinearPool = new Contract(bbadai, linearPoolAbi, provider); - const usdcLinearPool = new Contract(bbausdc, linearPoolAbi, provider); - const usdtLinearPool = new Contract(bbausdt, linearPoolAbi, provider); - // This is gets the up to date rates for the Aave tokens - const daiRate = await daiLinearPool.getWrappedTokenRate(); - const usdcRate = await usdcLinearPool.getWrappedTokenRate(); - const usdtRate = await usdtLinearPool.getWrappedTokenRate(); - - console.log(`DAI Rate: ${daiRate.toString()}`); - console.log(`USDC Rate: ${usdcRate.toString()}`); - console.log(`USDT Rate: ${usdtRate.toString()}`); - - const txInfo = await balancer.relayer.swapUnwrapAaveStaticExactOut( - [ - bbausd, - bbausd, - bbausd, - ], - [ - '0x02d60b84491589974263d922d9cc7a3152618ef6', // waDAI - '0xd093fa4fb80d09bb30817fdcd442d4d02ed3e5de', // waUSDC - '0xf8fd466f12e236f4c96f7cce6c79eadb819abf58', // waUSDT - ], - [parseFixed('1', 16).toString(), '1000', '1000'], // Amount of unwrapped Aave token we want to receive - [daiRate, usdcRate, usdtRate], - funds, - '50000000000000000' // Slippage 5% - ); - - console.log(`Amounts In:`); - console.log(txInfo.outputs?.amountsIn?.toString()); - - const relayerContract = new Contract( - relayerAddress, - balancerRelayerAbi, - provider - ); - const tx = await relayerContract - .connect(wallet) - .callStatic[txInfo.function](txInfo.params, { - value: '0', - // gasLimit: '2000000', - }); - - console.log(`Swap Deltas:`); - console.log(defaultAbiCoder.decode(['int256[]'], tx[0]).toString()); + const config: BalancerSdkConfig = { + network: Network.MAINNET, + rpcUrl: `https://mainnet.infura.io/v3/${process.env.INFURA}`, + }; + + const provider = new JsonRpcProvider(config.rpcUrl); + const key: any = process.env.TRADER_KEY; + const relayerAddress = '0xAc9f49eF3ab0BbC929f7b1bb0A17E1Fca5786251'; + const wallet = new Wallet(key, provider); + + const balancer = new BalancerSDK(config); + + // Creates fund management info for swap part of call + const funds: FundManagement = { + sender: wallet.address, + recipient: relayerAddress, // Note relayer is recipient of swaps + fromInternalBalance: false, + toInternalBalance: false, + }; + + const bbausd = '0x7b50775383d3d6f0215a8f290f2c9e2eebbeceb2'; + const bbadai = '0x804CdB9116a10bB78768D3252355a1b18067bF8f'; + const bbausdc = '0x9210F1204b5a24742Eba12f710636D76240dF3d0'; + const bbausdt = '0x2BBf681cC4eb09218BEe85EA2a5d3D13Fa40fC0C'; + const daiLinearPool = new Contract(bbadai, linearPoolAbi, provider); + const usdcLinearPool = new Contract(bbausdc, linearPoolAbi, provider); + const usdtLinearPool = new Contract(bbausdt, linearPoolAbi, provider); + // This is gets the up to date rates for the Aave tokens + const daiRate = await daiLinearPool.getWrappedTokenRate(); + const usdcRate = await usdcLinearPool.getWrappedTokenRate(); + const usdtRate = await usdtLinearPool.getWrappedTokenRate(); + + console.log(`DAI Rate: ${daiRate.toString()}`); + console.log(`USDC Rate: ${usdcRate.toString()}`); + console.log(`USDT Rate: ${usdtRate.toString()}`); + + const txInfo = await balancer.relayer.swapUnwrapAaveStaticExactOut( + [bbausd, bbausd, bbausd], + [ + '0x02d60b84491589974263d922d9cc7a3152618ef6', // waDAI + '0xd093fa4fb80d09bb30817fdcd442d4d02ed3e5de', // waUSDC + '0xf8fd466f12e236f4c96f7cce6c79eadb819abf58', // waUSDT + ], + [parseFixed('1', 16).toString(), '1000', '1000'], // Amount of unwrapped Aave token we want to receive + [daiRate, usdcRate, usdtRate], + funds, + '50000000000000000' // Slippage 5% + ); + + console.log(`Amounts In:`); + console.log(txInfo.outputs?.amountsIn?.toString()); + + const relayerContract = new Contract( + relayerAddress, + balancerRelayerAbi, + provider + ); + const tx = await relayerContract + .connect(wallet) + .callStatic[txInfo.function](txInfo.params, { + value: '0', + // gasLimit: '2000000', + }); + + console.log(`Swap Deltas:`); + console.log(defaultAbiCoder.decode(['int256[]'], tx[0]).toString()); } // yarn examples:run ./examples/relayerSwapUnwrap.ts diff --git a/balancer-js/examples/simpleFlashSwap.ts b/balancer-js/examples/simpleFlashSwap.ts index 8dc5ea13c..9ba07b610 100644 --- a/balancer-js/examples/simpleFlashSwap.ts +++ b/balancer-js/examples/simpleFlashSwap.ts @@ -20,32 +20,32 @@ https://kovan.etherscan.io/tx/0x2bca23b1c98e9bfe51aa4fbdd16db8c1b81484a92486233c NB: If this fails, test first the querySimpleFlashSwap yields a profitable flashSwap */ async function runFlashSwap() { - console.log('PRIVATE_KEY', TRADER_KEY); - - const encodedBatchSwapData = Swaps.encodeSimpleFlashSwap({ - flashLoanAmount: '100', - poolIds: [ - '0x0cdab06b07197d96369fea6f3bea6efc7ecdf7090002000000000000000003de', - '0x17018c2f7c345add873474879ff0ed98ebd6346a000200000000000000000642', - ], - assets: [USDC.address, DAI.address], - walletAddress: '0x35f5a330FD2F8e521ebd259FA272bA8069590741', - }); - - const provider = new InfuraProvider(Network.KOVAN, process.env.INFURA); - const wallet = new Wallet(TRADER_KEY as string, provider); - - const tx = await wallet.sendTransaction({ - data: encodedBatchSwapData, - to: balancerVault, - /** - * The following gas inputs are optional, - **/ - // gasPrice: '6000000000', - // gasLimit: '2000000', - }); - - console.log(tx); + console.log('PRIVATE_KEY', TRADER_KEY); + + const encodedBatchSwapData = Swaps.encodeSimpleFlashSwap({ + flashLoanAmount: '100', + poolIds: [ + '0x0cdab06b07197d96369fea6f3bea6efc7ecdf7090002000000000000000003de', + '0x17018c2f7c345add873474879ff0ed98ebd6346a000200000000000000000642', + ], + assets: [USDC.address, DAI.address], + walletAddress: '0x35f5a330FD2F8e521ebd259FA272bA8069590741', + }); + + const provider = new InfuraProvider(Network.KOVAN, process.env.INFURA); + const wallet = new Wallet(TRADER_KEY as string, provider); + + const tx = await wallet.sendTransaction({ + data: encodedBatchSwapData, + to: balancerVault, + /** + * The following gas inputs are optional, + **/ + // gasPrice: '6000000000', + // gasLimit: '2000000', + }); + + console.log(tx); } // yarn examples:run ./examples/simpleFlashSwap.ts diff --git a/balancer-js/examples/spotPrice.ts b/balancer-js/examples/spotPrice.ts index 285f47305..31621f04c 100644 --- a/balancer-js/examples/spotPrice.ts +++ b/balancer-js/examples/spotPrice.ts @@ -1,35 +1,43 @@ import dotenv from 'dotenv'; -import { - BalancerSDK, - Network, - BalancerSdkConfig, -} from '../src/index'; +import { BalancerSDK, Network, BalancerSdkConfig } from '../src/index'; import { ADDRESSES } from '../src/test/lib/constants'; dotenv.config(); + /* Example showing how to use SDK to get spot price for a pair. */ async function getSpotPrice() { - const network = Network.MAINNET; - const config: BalancerSdkConfig = { - network, - rpcUrl: `https://mainnet.infura.io/v3/${process.env.INFURA}`, - }; + const network = Network.MAINNET; + const config: BalancerSdkConfig = { + network, + rpcUrl: `https://mainnet.infura.io/v3/${process.env.INFURA}`, + }; - const balancer = new BalancerSDK(config); + const balancer = new BalancerSDK(config); - const wethDaiPoolId = '0x0b09dea16768f0799065c475be02919503cb2a3500020000000000000000001a'; - // This will fetch pools information using data provider - const spotPriceEthDai = await balancer.pricing.getSpotPrice(ADDRESSES[network].DAI.address, ADDRESSES[network].WETH.address, wethDaiPoolId); - console.log(spotPriceEthDai.toString()); + const wethDaiPoolId = + '0x0b09dea16768f0799065c475be02919503cb2a3500020000000000000000001a'; + // This will fetch pools information using data provider + const spotPriceEthDai = await balancer.pricing.getSpotPrice( + ADDRESSES[network].DAI.address, + ADDRESSES[network].WETH.address, + wethDaiPoolId + ); + console.log(spotPriceEthDai.toString()); - const balDaiPoolId = '0x4626d81b3a1711beb79f4cecff2413886d461677000200000000000000000011'; - // Reuses previously fetched pools data - const pools = balancer.pricing.getPools(); - const spotPriceBalDai = await balancer.pricing.getSpotPrice(ADDRESSES[network].DAI.address, ADDRESSES[network].BAL.address, balDaiPoolId, pools); - console.log(spotPriceBalDai.toString()); + const balDaiPoolId = + '0x4626d81b3a1711beb79f4cecff2413886d461677000200000000000000000011'; + // Reuses previously fetched pools data + const pools = balancer.pricing.getPools(); + const spotPriceBalDai = await balancer.pricing.getSpotPrice( + ADDRESSES[network].DAI.address, + ADDRESSES[network].BAL.address, + balDaiPoolId, + pools + ); + console.log(spotPriceBalDai.toString()); } // yarn examples:run ./examples/spotPrice.ts diff --git a/balancer-js/examples/swap.ts b/balancer-js/examples/swap.ts new file mode 100644 index 000000000..cfb1c0c0b --- /dev/null +++ b/balancer-js/examples/swap.ts @@ -0,0 +1,107 @@ +/** + * Example showing how to find a swap and send it using ethers. + */ +import dotenv from 'dotenv'; +import { BalancerSDK, Network } from '../src/index'; +import mainnetTop10 from '@/test/lib/mainnet-top-10.json'; +import { MockPoolDataService } from '@/test/lib/mockPool'; +import { getOnChainBalances } from '@/modules/sor/pool-data/onChainData'; +import { mapPools } from '@/modules/sor/pool-data/subgraphPoolDataService'; +import { JsonRpcProvider } from '@ethersproject/providers'; +import { Wallet } from '@ethersproject/wallet'; +import { getNetworkConfig } from '../src/modules/sdk.helpers'; +import { BigNumber, parseFixed, formatFixed } from '@ethersproject/bignumber'; +import { AddressZero } from '@ethersproject/constants'; +import { Contract } from '@ethersproject/contracts'; + +dotenv.config(); + +const { TRADER_KEY, TRADER_ADDRESS } = process.env; + +const network = Network.MAINNET; +const rpcUrl = `http://127.0.0.1:8545`; +const provider = new JsonRpcProvider(rpcUrl, network); +const { addresses } = getNetworkConfig({ network, rpcUrl }); +const wallet = new Wallet(TRADER_KEY as string, provider); + +const tokenOut = '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599'; // wBTC +const tokenOutContract = new Contract( + tokenOut, + ['function balanceOf(address) external view returns (uint256)'], + provider +); + +async function executePoolFetching() { + const pools = mapPools(mainnetTop10); + + const onchain = await getOnChainBalances( + pools, + addresses.contracts.multicall, + addresses.contracts.vault, + provider + ); + + const mockPoolDataService = new MockPoolDataService(onchain); + + const balancer = new BalancerSDK({ + network, + rpcUrl, + sor: { + tokenPriceService: 'coingecko', + poolDataService: mockPoolDataService, + }, + }); + + await balancer.swaps.fetchPools(); + + const swapInfo = await balancer.swaps.findRouteGivenIn({ + // tokenIn: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // weth + tokenIn: AddressZero, // eth + tokenOut, + amount: parseFixed('1', 18), + gasPrice: parseFixed('1', 9), + maxPools: 4, + }); + + const userAddress = TRADER_ADDRESS as string; + const deadline = BigNumber.from(`${Math.ceil(Date.now() / 1000) + 60}`); // 60 seconds from now + const maxSlippage = 50; // 50 bsp = 0.5% + + const transactionAttributes = balancer.swaps.buildSwap({ + userAddress, + swapInfo, + kind: 0, + deadline, + maxSlippage, + }); + + // Extract parameters required for sendTransaction + const { to, data, value } = transactionAttributes; + + // Execution with ethers.js + try { + const balanceBefore = await tokenOutContract.balanceOf(userAddress); + + await ( + await wallet.sendTransaction({ + to, + data, + value, + }) + ).wait(); + + // check delta + const balanceAfter = await tokenOutContract.balanceOf(userAddress); + console.log( + `Amount received: ${formatFixed( + balanceAfter.sub(balanceBefore), + 8 + )} Amount expected: ${formatFixed(swapInfo.returnAmount, 8)}` + ); + } catch (err) { + console.log(err); + } +} + +// yarn examples:run ./examples/fetch-pools.ts +executePoolFetching(); diff --git a/balancer-js/hardhat.config.js b/balancer-js/hardhat.config.js new file mode 100644 index 000000000..2ccc59695 --- /dev/null +++ b/balancer-js/hardhat.config.js @@ -0,0 +1,12 @@ +require('@nomiclabs/hardhat-ethers'); + +/** + * @type import('hardhat/config').HardhatUserConfig + */ +module.exports = { + networks: { + hardhat: { + chainId: 1, + }, + }, +}; diff --git a/balancer-js/package.json b/balancer-js/package.json index 2e7862c01..e253d6ebc 100644 --- a/balancer-js/package.json +++ b/balancer-js/package.json @@ -1,6 +1,6 @@ { "name": "@balancer-labs/sdk", - "version": "0.1.11", + "version": "0.1.12", "description": "JavaScript SDK for interacting with the Balancer Protocol V2", "license": "GPL-3.0-only", "homepage": "https://github.com/balancer-labs/balancer-sdk/balancer-js#readme", @@ -20,13 +20,15 @@ "dist/" ], "scripts": { - "build": "rollup -c", + "build": "rimraf dist && rollup -c", "dev": "rollup -c -w", "test": "ts-mocha --paths --recursive -p tsconfig.testing.json 'src/**/*.spec.ts'", + "test:only": "ts-mocha --paths --recursive -p tsconfig.testing.json", "lint": "eslint ./src --ext .ts --max-warnings 0", "lint:fix": "eslint ./src --ext .ts --max-warnings 0 --fix", "subgraph:generate": "graphql-codegen --config src/modules/subgraph/codegen.yml -r dotenv/config", - "examples:run": "TS_NODE_PROJECT='tsconfig.testing.json' ts-node -r tsconfig-paths/register" + "examples:run": "TS_NODE_PROJECT='tsconfig.testing.json' ts-node -r tsconfig-paths/register", + "node": "npx hardhat node --fork $(grep ALCHEMY_URL .env | cut -d '=' -f2) --fork-block-number 14828550" }, "devDependencies": { "@ethersproject/abi": "^5.4.0", @@ -47,6 +49,7 @@ "@graphql-codegen/typescript-graphql-request": "^4.3.0", "@graphql-codegen/typescript-operations": "^2.2.0", "@graphql-codegen/typescript-resolvers": "2.4.1", + "@nomiclabs/hardhat-ethers": "^2.0.5", "@rollup/plugin-commonjs": "^21.0.1", "@rollup/plugin-json": "^4.1.0", "@rollup/plugin-node-resolve": "^13.0.0", @@ -63,8 +66,12 @@ "eslint": "^7.9.0", "eslint-plugin-mocha-no-only": "^1.1.1", "eslint-plugin-prettier": "^3.1.4", + "ethers": "^5.0.0", + "fishery": "^2.2.2", + "hardhat": "^2.9.3", "mocha": "^8.2.1", "prettier": "^2.1.2", + "rimraf": "^3.0.2", "rollup": "^2.52.8", "rollup-plugin-dts": "^3.0.2", "tiny-invariant": "^1.1.0", diff --git a/balancer-js/rollup.config.ts b/balancer-js/rollup.config.ts index bb0e45007..2116c2526 100644 --- a/balancer-js/rollup.config.ts +++ b/balancer-js/rollup.config.ts @@ -6,8 +6,8 @@ import dts from 'rollup-plugin-dts'; import pkg from './package.json'; const external = [ - ...Object.keys(pkg.dependencies), - ...Object.keys(pkg.peerDependencies), + ...Object.keys(pkg.dependencies), + ...Object.keys(pkg.peerDependencies), ]; export default [ @@ -38,12 +38,22 @@ export default [ { file: pkg.main, format: 'cjs', sourcemap: true }, { file: pkg.module, format: 'es', sourcemap: true }, ], - plugins: [nodeResolve(), json(), commonjs(), typescript()], + plugins: [ + nodeResolve(), + json(), + commonjs(), + typescript({ + exclude: ['node_modules', '**/*.spec.ts'], + }), + ], external, }, { input: 'src/index.ts', output: [{ file: 'dist/index.d.ts', format: 'es' }], - plugins: [dts(), typescript()], + plugins: [ + dts(), + typescript({ exclude: ['node_modules', '**/*.spec.ts'] }), + ], }, ]; diff --git a/balancer-js/src/balancerErrors.ts b/balancer-js/src/balancerErrors.ts index 3ce55995e..48ff79f4b 100644 --- a/balancer-js/src/balancerErrors.ts +++ b/balancer-js/src/balancerErrors.ts @@ -1,40 +1,40 @@ export enum BalancerErrorCode { - SWAP_ZERO_RETURN_AMOUNT = 'SWAP_ZERO_RETURN_AMOUNT', - UNWRAP_ZERO_AMOUNT = 'UNWRAP_ZERO_AMOUNT', - WRAP_ZERO_AMOUNT = 'WRAP_ZERO_AMOUNT', - QUERY_BATCH_SWAP = 'QUERY_BATCH_SWAP', - POOL_DOESNT_EXIST = 'POOL_DOESNT_EXIST', - UNSUPPORTED_POOL_TYPE = 'UNSUPPORTED_POOL_TYPE', - UNSUPPORTED_PAIR = 'UNSUPPORTED_PAIR', - NO_POOL_DATA = 'NO_POOL_DATA', + SWAP_ZERO_RETURN_AMOUNT = 'SWAP_ZERO_RETURN_AMOUNT', + UNWRAP_ZERO_AMOUNT = 'UNWRAP_ZERO_AMOUNT', + WRAP_ZERO_AMOUNT = 'WRAP_ZERO_AMOUNT', + QUERY_BATCH_SWAP = 'QUERY_BATCH_SWAP', + POOL_DOESNT_EXIST = 'POOL_DOESNT_EXIST', + UNSUPPORTED_POOL_TYPE = 'UNSUPPORTED_POOL_TYPE', + UNSUPPORTED_PAIR = 'UNSUPPORTED_PAIR', + NO_POOL_DATA = 'NO_POOL_DATA', } export class BalancerError extends Error { - constructor(public code: BalancerErrorCode) { - super(BalancerError.getMessage(code)); - this.name = 'BalancerError'; - } + constructor(public code: BalancerErrorCode) { + super(BalancerError.getMessage(code)); + this.name = 'BalancerError'; + } - static getMessage(code: BalancerErrorCode): string { - switch (code) { - case BalancerErrorCode.SWAP_ZERO_RETURN_AMOUNT: - return 'queryBatchSwapWithSor returned 0 amount'; - case BalancerErrorCode.UNWRAP_ZERO_AMOUNT: - return 'swapUnwrapAaveStaticExactIn unwrapped amount < 0'; - case BalancerErrorCode.WRAP_ZERO_AMOUNT: - return 'swapUnwrapAaveStaticExactOut wrapped amount < 0'; - case BalancerErrorCode.QUERY_BATCH_SWAP: - return 'queryBatchSwap on chain call error'; - case BalancerErrorCode.POOL_DOESNT_EXIST: - return 'balancer pool does not exist'; - case BalancerErrorCode.UNSUPPORTED_POOL_TYPE: - return 'unsupported pool type'; - case BalancerErrorCode.UNSUPPORTED_PAIR: - return 'unsupported token pair'; - case BalancerErrorCode.NO_POOL_DATA: - return 'no pool data'; - default: - return 'Unknown error'; - } + static getMessage(code: BalancerErrorCode): string { + switch (code) { + case BalancerErrorCode.SWAP_ZERO_RETURN_AMOUNT: + return 'queryBatchSwapWithSor returned 0 amount'; + case BalancerErrorCode.UNWRAP_ZERO_AMOUNT: + return 'swapUnwrapAaveStaticExactIn unwrapped amount < 0'; + case BalancerErrorCode.WRAP_ZERO_AMOUNT: + return 'swapUnwrapAaveStaticExactOut wrapped amount < 0'; + case BalancerErrorCode.QUERY_BATCH_SWAP: + return 'queryBatchSwap on chain call error'; + case BalancerErrorCode.POOL_DOESNT_EXIST: + return 'balancer pool does not exist'; + case BalancerErrorCode.UNSUPPORTED_POOL_TYPE: + return 'unsupported pool type'; + case BalancerErrorCode.UNSUPPORTED_PAIR: + return 'unsupported token pair'; + case BalancerErrorCode.NO_POOL_DATA: + return 'no pool data'; + default: + return 'Unknown error'; } + } } diff --git a/balancer-js/src/index.ts b/balancer-js/src/index.ts index 54057e11f..6827f7f0d 100644 --- a/balancer-js/src/index.ts +++ b/balancer-js/src/index.ts @@ -15,23 +15,23 @@ export * from './modules/pools/pools.module'; export * from './modules/pools/types'; export * from './balancerErrors'; export { - SwapInfo, - SubgraphPoolBase, - SwapTypes, - SwapOptions, - PoolFilter, - SwapV2, - queryBatchSwapTokensIn, - queryBatchSwapTokensOut, - phantomStableBPTForTokensZeroPriceImpact, - stableBPTForTokensZeroPriceImpact, - weightedBPTForTokensZeroPriceImpact, - SOR, - PoolDataService, - RouteProposer, - NewPath, - parseToPoolsDict, - PoolDictionary, - formatSequence, - getTokenAddressesForSwap, + SwapInfo, + SubgraphPoolBase, + SwapTypes, + SwapOptions, + PoolFilter, + SwapV2, + queryBatchSwapTokensIn, + queryBatchSwapTokensOut, + phantomStableBPTForTokensZeroPriceImpact, + stableBPTForTokensZeroPriceImpact, + weightedBPTForTokensZeroPriceImpact, + SOR, + PoolDataService, + RouteProposer, + NewPath, + parseToPoolsDict, + PoolDictionary, + formatSequence, + getTokenAddressesForSwap, } from '@balancer-labs/sor'; diff --git a/balancer-js/src/lib/constants/config.ts b/balancer-js/src/lib/constants/config.ts index 7a10a0ab5..f645b8562 100644 --- a/balancer-js/src/lib/constants/config.ts +++ b/balancer-js/src/lib/constants/config.ts @@ -4,144 +4,145 @@ import { BalancerNetworkConfig } from '@/types'; export const balancerVault = '0xBA12222222228d8Ba445958a75a0704d566BF2C8'; export const BALANCER_NETWORK_CONFIG: Record = { - [Network.MAINNET]: { - chainId: Network.MAINNET, //1 - addresses: { - contracts: { - vault: '0xBA12222222228d8Ba445958a75a0704d566BF2C8', - multicall: '0xeefba1e63905ef1d7acba5a8513c70307c1ce441', - }, - tokens: { - wrappedNativeAsset: - '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', - lbpRaisingTokens: [ - '0x6B175474E89094C44Da98b954EedeAC495271d0F', - '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', - ], - }, - }, - urls: { - subgraph: - 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-v2', - }, - pools: { - bbausd: { - id: '0x7b50775383d3d6f0215a8f290f2c9e2eebbeceb20000000000000000000000fe', - address: '0x7b50775383d3d6f0215a8f290f2c9e2eebbeceb2', - }, - }, - }, - [Network.POLYGON]: { - chainId: Network.POLYGON, //137 - addresses: { - contracts: { - vault: '0xBA12222222228d8Ba445958a75a0704d566BF2C8', - multicall: '0xa1B2b503959aedD81512C37e9dce48164ec6a94d', - }, - tokens: { - wrappedNativeAsset: - '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270', - }, - }, - urls: { - subgraph: - 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-polygon-v2', - }, - pools: {}, - }, - [Network.ARBITRUM]: { - chainId: Network.ARBITRUM, //42161 - addresses: { - contracts: { - vault: '0xBA12222222228d8Ba445958a75a0704d566BF2C8', - multicall: '0x269ff446d9892c9e19082564df3f5e8741e190a1', - }, - tokens: { - wrappedNativeAsset: - '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', - }, - }, - urls: { - subgraph: - 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-arbitrum-v2', - }, - pools: {}, - }, - [Network.KOVAN]: { - chainId: Network.KOVAN, //42 - addresses: { - contracts: { - vault: '0xBA12222222228d8Ba445958a75a0704d566BF2C8', - multicall: '0x2cc8688C5f75E365aaEEb4ea8D6a480405A48D2A', - }, - tokens: { - wrappedNativeAsset: - '0xdFCeA9088c8A88A76FF74892C1457C17dfeef9C1', - }, - }, - urls: { - subgraph: - 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-kovan-v2', - }, - pools: { - bbausd: { - id: '0x8fd162f338b770f7e879030830cde9173367f3010000000000000000000004d8', - address: '0x8fd162f338b770f7e879030830cde9173367f301', - }, - }, - }, - [Network.ROPSTEN]: { - chainId: Network.ROPSTEN, //3 - addresses: { - contracts: { - vault: '0xBA12222222228d8Ba445958a75a0704d566BF2C8', - multicall: '0x53c43764255c17bd724f74c4ef150724ac50a3ed', - }, - tokens: { - wrappedNativeAsset: - '0xdFCeA9088c8A88A76FF74892C1457C17dfeef9C1', - }, - }, - urls: { - subgraph: '', - }, - pools: {}, - }, - [Network.RINKEBY]: { - chainId: Network.RINKEBY, //4 - addresses: { - contracts: { - vault: '0xBA12222222228d8Ba445958a75a0704d566BF2C8', - multicall: '0x42ad527de7d4e9d9d011ac45b31d8551f8fe9821', - }, - tokens: { - wrappedNativeAsset: - '0xdFCeA9088c8A88A76FF74892C1457C17dfeef9C1', - }, - }, - urls: { - subgraph: - 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-rinkeby-v2', - }, - pools: {}, - }, - [Network.GÖRLI]: { - chainId: Network.GÖRLI, //5 - addresses: { - contracts: { - vault: '0x65748E8287Ce4B9E6D83EE853431958851550311', - multicall: '0x42ad527de7d4e9d9d011ac45b31d8551f8fe9821', - }, - tokens: { - wrappedNativeAsset: - '0x9A1000D492d40bfccbc03f413A48F5B6516Ec0Fd', - }, - }, - urls: { - subgraph: - 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-goerli-v2', - }, - pools: {}, + [Network.MAINNET]: { + chainId: Network.MAINNET, //1 + addresses: { + contracts: { + vault: '0xBA12222222228d8Ba445958a75a0704d566BF2C8', + multicall: '0xeefba1e63905ef1d7acba5a8513c70307c1ce441', + lidoRelayer: '0xdcdbf71A870cc60C6F9B621E28a7D3Ffd6Dd4965', + }, + tokens: { + wrappedNativeAsset: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', + lbpRaisingTokens: [ + '0x6B175474E89094C44Da98b954EedeAC495271d0F', + '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2', + ], + stETH: '0xae7ab96520de3a18e5e111b5eaab095312d7fe84', + wstETH: '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0', + }, }, + urls: { + subgraph: + 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-v2', + }, + pools: { + bbausd: { + id: '0x7b50775383d3d6f0215a8f290f2c9e2eebbeceb20000000000000000000000fe', + address: '0x7b50775383d3d6f0215a8f290f2c9e2eebbeceb2', + }, + }, + }, + [Network.POLYGON]: { + chainId: Network.POLYGON, //137 + addresses: { + contracts: { + vault: '0xBA12222222228d8Ba445958a75a0704d566BF2C8', + multicall: '0xa1B2b503959aedD81512C37e9dce48164ec6a94d', + }, + tokens: { + wrappedNativeAsset: '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270', + }, + }, + urls: { + subgraph: + 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-polygon-v2', + }, + pools: {}, + }, + [Network.ARBITRUM]: { + chainId: Network.ARBITRUM, //42161 + addresses: { + contracts: { + vault: '0xBA12222222228d8Ba445958a75a0704d566BF2C8', + multicall: '0x269ff446d9892c9e19082564df3f5e8741e190a1', + }, + tokens: { + wrappedNativeAsset: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', + }, + }, + urls: { + subgraph: + 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-arbitrum-v2', + }, + pools: {}, + }, + [Network.KOVAN]: { + chainId: Network.KOVAN, //42 + addresses: { + contracts: { + vault: '0xBA12222222228d8Ba445958a75a0704d566BF2C8', + multicall: '0x2cc8688C5f75E365aaEEb4ea8D6a480405A48D2A', + }, + tokens: { + wrappedNativeAsset: '0xdFCeA9088c8A88A76FF74892C1457C17dfeef9C1', + }, + }, + urls: { + subgraph: + 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-kovan-v2', + }, + pools: { + bbausd: { + id: '0x8fd162f338b770f7e879030830cde9173367f3010000000000000000000004d8', + address: '0x8fd162f338b770f7e879030830cde9173367f301', + }, + }, + }, + [Network.ROPSTEN]: { + chainId: Network.ROPSTEN, //3 + addresses: { + contracts: { + vault: '0xBA12222222228d8Ba445958a75a0704d566BF2C8', + multicall: '0x53c43764255c17bd724f74c4ef150724ac50a3ed', + }, + tokens: { + wrappedNativeAsset: '0xdFCeA9088c8A88A76FF74892C1457C17dfeef9C1', + }, + }, + urls: { + subgraph: '', + }, + pools: {}, + }, + [Network.RINKEBY]: { + chainId: Network.RINKEBY, //4 + addresses: { + contracts: { + vault: '0xBA12222222228d8Ba445958a75a0704d566BF2C8', + multicall: '0x42ad527de7d4e9d9d011ac45b31d8551f8fe9821', + }, + tokens: { + wrappedNativeAsset: '0xdFCeA9088c8A88A76FF74892C1457C17dfeef9C1', + }, + }, + urls: { + subgraph: + 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-rinkeby-v2', + }, + pools: {}, + }, + [Network.GOERLI]: { + chainId: Network.GOERLI, //5 + addresses: { + contracts: { + vault: '0xBA12222222228d8Ba445958a75a0704d566BF2C8', + multicall: '0x77dCa2C955b15e9dE4dbBCf1246B4B85b651e50e', + }, + tokens: { + wrappedNativeAsset: '0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6', + }, + }, + urls: { + subgraph: + 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-goerli-v2', + }, + pools: {}, + }, }; + +export const networkAddresses = ( + chainId: number +): BalancerNetworkConfig['addresses'] => + BALANCER_NETWORK_CONFIG[chainId as Network].addresses; diff --git a/balancer-js/src/lib/constants/network.ts b/balancer-js/src/lib/constants/network.ts index 7eea2457d..c97b32da7 100644 --- a/balancer-js/src/lib/constants/network.ts +++ b/balancer-js/src/lib/constants/network.ts @@ -1,9 +1,9 @@ export enum Network { - MAINNET = 1, - ROPSTEN = 3, - RINKEBY = 4, - GÖRLI = 5, - KOVAN = 42, - POLYGON = 137, - ARBITRUM = 42161, + MAINNET = 1, + ROPSTEN = 3, + RINKEBY = 4, + GOERLI = 5, + KOVAN = 42, + POLYGON = 137, + ARBITRUM = 42161, } diff --git a/balancer-js/src/lib/utils/aaveHelpers.ts b/balancer-js/src/lib/utils/aaveHelpers.ts index e2312b882..c1e04c11e 100644 --- a/balancer-js/src/lib/utils/aaveHelpers.ts +++ b/balancer-js/src/lib/utils/aaveHelpers.ts @@ -4,17 +4,17 @@ import { Contract } from '@ethersproject/contracts'; import aTokenRateProviderAbi from '../abi/StaticATokenRateProvider.json'; export class AaveHelpers { - static async getRate( - rateProviderAddress: string, - provider: JsonRpcProvider - ): Promise { - const rateProviderContract = new Contract( - rateProviderAddress, - aTokenRateProviderAbi, - provider - ); + static async getRate( + rateProviderAddress: string, + provider: JsonRpcProvider + ): Promise { + const rateProviderContract = new Contract( + rateProviderAddress, + aTokenRateProviderAbi, + provider + ); - const rate = await rateProviderContract.getRate(); - return rate.toString(); - } + const rate = await rateProviderContract.getRate(); + return rate.toString(); + } } diff --git a/balancer-js/src/lib/utils/assetHelpers.ts b/balancer-js/src/lib/utils/assetHelpers.ts index dd399f775..026f4a477 100644 --- a/balancer-js/src/lib/utils/assetHelpers.ts +++ b/balancer-js/src/lib/utils/assetHelpers.ts @@ -3,91 +3,89 @@ import { AddressZero } from '@ethersproject/constants'; import invariant from 'tiny-invariant'; const cmpTokens = (tokenA: string, tokenB: string): number => - tokenA.toLowerCase() > tokenB.toLowerCase() ? 1 : -1; + tokenA.toLowerCase() > tokenB.toLowerCase() ? 1 : -1; const transposeMatrix = (matrix: unknown[][]): unknown[][] => - matrix[0].map((_, columnIndex) => matrix.map((row) => row[columnIndex])); + matrix[0].map((_, columnIndex) => matrix.map((row) => row[columnIndex])); export class AssetHelpers { - public readonly ETH: string = AddressZero; - public readonly WETH: string; + public readonly ETH: string = AddressZero; + public readonly WETH: string; - constructor(wethAddress: string) { - this.WETH = getAddress(wethAddress); - } + constructor(wethAddress: string) { + this.WETH = getAddress(wethAddress); + } - static isEqual = (addressA: string, addressB: string): boolean => - getAddress(addressA) === getAddress(addressB); + static isEqual = (addressA: string, addressB: string): boolean => + getAddress(addressA) === getAddress(addressB); - /** - * Tests whether `token` is ETH (represented by `0x0000...0000`). - * - * @param token - the address of the asset to be checked - */ - isETH = (token: string): boolean => AssetHelpers.isEqual(token, this.ETH); + /** + * Tests whether `token` is ETH (represented by `0x0000...0000`). + * + * @param token - the address of the asset to be checked + */ + isETH = (token: string): boolean => AssetHelpers.isEqual(token, this.ETH); - /** - * Tests whether `token` is WETH. - * - * @param token - the address of the asset to be checked - */ - isWETH = (token: string): boolean => AssetHelpers.isEqual(token, this.WETH); + /** + * Tests whether `token` is WETH. + * + * @param token - the address of the asset to be checked + */ + isWETH = (token: string): boolean => AssetHelpers.isEqual(token, this.WETH); - /** - * Converts an asset to the equivalent ERC20 address. - * - * For ERC20s this will return the passed address but passing ETH (`0x0000...0000`) will return the WETH address - * @param token - the address of the asset to be translated to an equivalent ERC20 - * @returns the address of translated ERC20 asset - */ - translateToERC20 = (token: string): string => - this.isETH(token) ? this.WETH : token; + /** + * Converts an asset to the equivalent ERC20 address. + * + * For ERC20s this will return the passed address but passing ETH (`0x0000...0000`) will return the WETH address + * @param token - the address of the asset to be translated to an equivalent ERC20 + * @returns the address of translated ERC20 asset + */ + translateToERC20 = (token: string): string => + this.isETH(token) ? this.WETH : token; - /** - * Sorts an array of token addresses into ascending order to match the format expected by the Vault. - * - * Passing additional arrays will result in each being sorted to maintain relative ordering to token addresses. - * - * The zero address (representing ETH) is sorted as if it were the WETH address. - * This matches the behaviour expected by the Vault when receiving an array of addresses. - * - * @param tokens - an array of token addresses to be sorted in ascending order - * @param others - a set of arrays to be sorted in the same order as the tokens, e.g. token weights or asset manager addresses - * @returns an array of the form `[tokens, ...others]` where each subarray has been sorted to maintain its ordering relative to `tokens` - * - * @example - * const [tokens] = sortTokens([tokenB, tokenC, tokenA]) - * const [tokens, weights] = sortTokens([tokenB, tokenC, tokenA], [weightB, weightC, weightA]) - * // where tokens = [tokenA, tokenB, tokenC], weights = [weightA, weightB, weightC] - */ - sortTokens( - tokens: string[], - ...others: unknown[][] - ): [string[], ...unknown[][]] { - others.forEach((array) => - invariant(tokens.length === array.length, 'array length mismatch') - ); + /** + * Sorts an array of token addresses into ascending order to match the format expected by the Vault. + * + * Passing additional arrays will result in each being sorted to maintain relative ordering to token addresses. + * + * The zero address (representing ETH) is sorted as if it were the WETH address. + * This matches the behaviour expected by the Vault when receiving an array of addresses. + * + * @param tokens - an array of token addresses to be sorted in ascending order + * @param others - a set of arrays to be sorted in the same order as the tokens, e.g. token weights or asset manager addresses + * @returns an array of the form `[tokens, ...others]` where each subarray has been sorted to maintain its ordering relative to `tokens` + * + * @example + * const [tokens] = sortTokens([tokenB, tokenC, tokenA]) + * const [tokens, weights] = sortTokens([tokenB, tokenC, tokenA], [weightB, weightC, weightA]) + * // where tokens = [tokenA, tokenB, tokenC], weights = [weightA, weightB, weightC] + */ + sortTokens( + tokens: string[], + ...others: unknown[][] + ): [string[], ...unknown[][]] { + others.forEach((array) => + invariant(tokens.length === array.length, 'array length mismatch') + ); - // We want to sort ETH as if were WETH so we translate to ERC20s - const erc20Tokens = tokens.map(this.translateToERC20); + // We want to sort ETH as if were WETH so we translate to ERC20s + const erc20Tokens = tokens.map(this.translateToERC20); - const transpose = transposeMatrix([erc20Tokens, ...others]) as [ - string, - ...unknown[] - ][]; - const sortedTranspose = transpose.sort(([tokenA], [tokenB]) => - cmpTokens(tokenA, tokenB) - ); - const [sortedErc20s, ...sortedOthers] = transposeMatrix( - sortedTranspose - ) as [string[], ...unknown[][]]; + const transpose = transposeMatrix([erc20Tokens, ...others]) as [ + string, + ...unknown[] + ][]; + const sortedTranspose = transpose.sort(([tokenA], [tokenB]) => + cmpTokens(tokenA, tokenB) + ); + const [sortedErc20s, ...sortedOthers] = transposeMatrix( + sortedTranspose + ) as [string[], ...unknown[][]]; - // If one of the tokens was ETH, we need to translate back from WETH - const sortedTokens = tokens.includes(this.ETH) - ? sortedErc20s.map((token) => - this.isWETH(token) ? this.ETH : token - ) - : sortedErc20s; - return [sortedTokens, ...sortedOthers]; - } + // If one of the tokens was ETH, we need to translate back from WETH + const sortedTokens = tokens.includes(this.ETH) + ? sortedErc20s.map((token) => (this.isWETH(token) ? this.ETH : token)) + : sortedErc20s; + return [sortedTokens, ...sortedOthers]; + } } diff --git a/balancer-js/src/lib/utils/errors.ts b/balancer-js/src/lib/utils/errors.ts index 44c6b7b93..87b0825f1 100644 --- a/balancer-js/src/lib/utils/errors.ts +++ b/balancer-js/src/lib/utils/errors.ts @@ -1,209 +1,209 @@ const balancerErrorCodes: Record = { - '000': 'ADD_OVERFLOW', - '001': 'SUB_OVERFLOW', - '002': 'SUB_UNDERFLOW', - '003': 'MUL_OVERFLOW', - '004': 'ZERO_DIVISION', - '005': 'DIV_INTERNAL', - '006': 'X_OUT_OF_BOUNDS', - '007': 'Y_OUT_OF_BOUNDS', - '008': 'PRODUCT_OUT_OF_BOUNDS', - '009': 'INVALID_EXPONENT', - '100': 'OUT_OF_BOUNDS', - '101': 'UNSORTED_ARRAY', - '102': 'UNSORTED_TOKENS', - '103': 'INPUT_LENGTH_MISMATCH', - '104': 'ZERO_TOKEN', - '200': 'MIN_TOKENS', - '201': 'MAX_TOKENS', - '202': 'MAX_SWAP_FEE_PERCENTAGE', - '203': 'MIN_SWAP_FEE_PERCENTAGE', - '204': 'MINIMUM_BPT', - '205': 'CALLER_NOT_VAULT', - '206': 'UNINITIALIZED', - '207': 'BPT_IN_MAX_AMOUNT', - '208': 'BPT_OUT_MIN_AMOUNT', - '209': 'EXPIRED_PERMIT', - '210': 'NOT_TWO_TOKENS', - '211': 'DISABLED', - '300': 'MIN_AMP', - '301': 'MAX_AMP', - '302': 'MIN_WEIGHT', - '303': 'MAX_STABLE_TOKENS', - '304': 'MAX_IN_RATIO', - '305': 'MAX_OUT_RATIO', - '306': 'MIN_BPT_IN_FOR_TOKEN_OUT', - '307': 'MAX_OUT_BPT_FOR_TOKEN_IN', - '308': 'NORMALIZED_WEIGHT_INVARIANT', - '309': 'INVALID_TOKEN', - '310': 'UNHANDLED_JOIN_KIND', - '311': 'ZERO_INVARIANT', - '312': 'ORACLE_INVALID_SECONDS_QUERY', - '313': 'ORACLE_NOT_INITIALIZED', - '314': 'ORACLE_QUERY_TOO_OLD', - '315': 'ORACLE_INVALID_INDEX', - '316': 'ORACLE_BAD_SECS', - '317': 'AMP_END_TIME_TOO_CLOSE', - '318': 'AMP_ONGOING_UPDATE', - '319': 'AMP_RATE_TOO_HIGH', - '320': 'AMP_NO_ONGOING_UPDATE', - '321': 'STABLE_INVARIANT_DIDNT_CONVERGE', - '322': 'STABLE_GET_BALANCE_DIDNT_CONVERGE', - '323': 'RELAYER_NOT_CONTRACT', - '324': 'BASE_POOL_RELAYER_NOT_CALLED', - '325': 'REBALANCING_RELAYER_REENTERED', - '326': 'GRADUAL_UPDATE_TIME_TRAVEL', - '327': 'SWAPS_DISABLED', - '328': 'CALLER_IS_NOT_LBP_OWNER', - '329': 'PRICE_RATE_OVERFLOW', - '330': 'INVALID_JOIN_EXIT_KIND_WHILE_SWAPS_DISABLED', - '331': 'WEIGHT_CHANGE_TOO_FAST', - '332': 'LOWER_GREATER_THAN_UPPER_TARGET', - '333': 'UPPER_TARGET_TOO_HIGH', - '334': 'UNHANDLED_BY_LINEAR_POOL', - '335': 'OUT_OF_TARGET_RANGE', - '336': 'UNHANDLED_EXIT_KIND ', - '337': 'UNAUTHORIZED_EXIT', - '338': 'MAX_MANAGEMENT_SWAP_FEE_PERCENTAGE', - '339': 'UNHANDLED_BY_MANAGED_POOL', - '340': 'UNHANDLED_BY_PHANTOM_POOL', - '341': 'TOKEN_DOES_NOT_HAVE_RATE_PROVIDER', - '342': 'INVALID_INITIALIZATION', - '343': 'OUT_OF_NEW_TARGET_RANGE', - '344': 'UNAUTHORIZED_OPERATION', - '345': 'UNINITIALIZED_POOL_CONTROLLER', - '400': 'REENTRANCY', - '401': 'SENDER_NOT_ALLOWED', - '402': 'PAUSED', - '403': 'PAUSE_WINDOW_EXPIRED', - '404': 'MAX_PAUSE_WINDOW_DURATION', - '405': 'MAX_BUFFER_PERIOD_DURATION', - '406': 'INSUFFICIENT_BALANCE', - '407': 'INSUFFICIENT_ALLOWANCE', - '408': 'ERC20_TRANSFER_FROM_ZERO_ADDRESS', - '409': 'ERC20_TRANSFER_TO_ZERO_ADDRESS', - '410': 'ERC20_MINT_TO_ZERO_ADDRESS', - '411': 'ERC20_BURN_FROM_ZERO_ADDRESS', - '412': 'ERC20_APPROVE_FROM_ZERO_ADDRESS', - '413': 'ERC20_APPROVE_TO_ZERO_ADDRESS', - '414': 'ERC20_TRANSFER_EXCEEDS_ALLOWANCE', - '415': 'ERC20_DECREASED_ALLOWANCE_BELOW_ZERO', - '416': 'ERC20_TRANSFER_EXCEEDS_BALANCE', - '417': 'ERC20_BURN_EXCEEDS_ALLOWANCE', - '418': 'SAFE_ERC20_CALL_FAILED', - '419': 'ADDRESS_INSUFFICIENT_BALANCE', - '420': 'ADDRESS_CANNOT_SEND_VALUE', - '421': 'SAFE_CAST_VALUE_CANT_FIT_INT256', - '422': 'GRANT_SENDER_NOT_ADMIN', - '423': 'REVOKE_SENDER_NOT_ADMIN', - '424': 'RENOUNCE_SENDER_NOT_ALLOWED', - '425': 'BUFFER_PERIOD_EXPIRED', - '426': 'CALLER_IS_NOT_OWNER', - '427': 'NEW_OWNER_IS_ZERO', - '428': 'CODE_DEPLOYMENT_FAILED', - '429': 'CALL_TO_NON_CONTRACT', - '430': 'LOW_LEVEL_CALL_FAILED', - '431': 'NOT_PAUSED', - '432': 'ADDRESS_ALREADY_ALLOWLISTED', - '433': 'ADDRESS_NOT_ALLOWLISTED', - '434': 'ERC20_BURN_EXCEEDS_BALANCE', - '500': 'INVALID_POOL_ID', - '501': 'CALLER_NOT_POOL', - '502': 'SENDER_NOT_ASSET_MANAGER', - '503': 'USER_DOESNT_ALLOW_RELAYER', - '504': 'INVALID_SIGNATURE', - '505': 'EXIT_BELOW_MIN', - '506': 'JOIN_ABOVE_MAX', - '507': 'SWAP_LIMIT', - '508': 'SWAP_DEADLINE', - '509': 'CANNOT_SWAP_SAME_TOKEN', - '510': 'UNKNOWN_AMOUNT_IN_FIRST_SWAP', - '511': 'MALCONSTRUCTED_MULTIHOP_SWAP', - '512': 'INTERNAL_BALANCE_OVERFLOW', - '513': 'INSUFFICIENT_INTERNAL_BALANCE', - '514': 'INVALID_ETH_INTERNAL_BALANCE', - '515': 'INVALID_POST_LOAN_BALANCE', - '516': 'INSUFFICIENT_ETH', - '517': 'UNALLOCATED_ETH', - '518': 'ETH_TRANSFER', - '519': 'CANNOT_USE_ETH_SENTINEL', - '520': 'TOKENS_MISMATCH', - '521': 'TOKEN_NOT_REGISTERED', - '522': 'TOKEN_ALREADY_REGISTERED', - '523': 'TOKENS_ALREADY_SET', - '524': 'TOKENS_LENGTH_MUST_BE_2', - '525': 'NONZERO_TOKEN_BALANCE', - '526': 'BALANCE_TOTAL_OVERFLOW', - '527': 'POOL_NO_TOKENS', - '528': 'INSUFFICIENT_FLASH_LOAN_BALANCE', - '600': 'SWAP_FEE_PERCENTAGE_TOO_HIGH', - '601': 'FLASH_LOAN_FEE_PERCENTAGE_TOO_HIGH', - '602': 'INSUFFICIENT_FLASH_LOAN_FEE_AMOUNT', + '000': 'ADD_OVERFLOW', + '001': 'SUB_OVERFLOW', + '002': 'SUB_UNDERFLOW', + '003': 'MUL_OVERFLOW', + '004': 'ZERO_DIVISION', + '005': 'DIV_INTERNAL', + '006': 'X_OUT_OF_BOUNDS', + '007': 'Y_OUT_OF_BOUNDS', + '008': 'PRODUCT_OUT_OF_BOUNDS', + '009': 'INVALID_EXPONENT', + '100': 'OUT_OF_BOUNDS', + '101': 'UNSORTED_ARRAY', + '102': 'UNSORTED_TOKENS', + '103': 'INPUT_LENGTH_MISMATCH', + '104': 'ZERO_TOKEN', + '200': 'MIN_TOKENS', + '201': 'MAX_TOKENS', + '202': 'MAX_SWAP_FEE_PERCENTAGE', + '203': 'MIN_SWAP_FEE_PERCENTAGE', + '204': 'MINIMUM_BPT', + '205': 'CALLER_NOT_VAULT', + '206': 'UNINITIALIZED', + '207': 'BPT_IN_MAX_AMOUNT', + '208': 'BPT_OUT_MIN_AMOUNT', + '209': 'EXPIRED_PERMIT', + '210': 'NOT_TWO_TOKENS', + '211': 'DISABLED', + '300': 'MIN_AMP', + '301': 'MAX_AMP', + '302': 'MIN_WEIGHT', + '303': 'MAX_STABLE_TOKENS', + '304': 'MAX_IN_RATIO', + '305': 'MAX_OUT_RATIO', + '306': 'MIN_BPT_IN_FOR_TOKEN_OUT', + '307': 'MAX_OUT_BPT_FOR_TOKEN_IN', + '308': 'NORMALIZED_WEIGHT_INVARIANT', + '309': 'INVALID_TOKEN', + '310': 'UNHANDLED_JOIN_KIND', + '311': 'ZERO_INVARIANT', + '312': 'ORACLE_INVALID_SECONDS_QUERY', + '313': 'ORACLE_NOT_INITIALIZED', + '314': 'ORACLE_QUERY_TOO_OLD', + '315': 'ORACLE_INVALID_INDEX', + '316': 'ORACLE_BAD_SECS', + '317': 'AMP_END_TIME_TOO_CLOSE', + '318': 'AMP_ONGOING_UPDATE', + '319': 'AMP_RATE_TOO_HIGH', + '320': 'AMP_NO_ONGOING_UPDATE', + '321': 'STABLE_INVARIANT_DIDNT_CONVERGE', + '322': 'STABLE_GET_BALANCE_DIDNT_CONVERGE', + '323': 'RELAYER_NOT_CONTRACT', + '324': 'BASE_POOL_RELAYER_NOT_CALLED', + '325': 'REBALANCING_RELAYER_REENTERED', + '326': 'GRADUAL_UPDATE_TIME_TRAVEL', + '327': 'SWAPS_DISABLED', + '328': 'CALLER_IS_NOT_LBP_OWNER', + '329': 'PRICE_RATE_OVERFLOW', + '330': 'INVALID_JOIN_EXIT_KIND_WHILE_SWAPS_DISABLED', + '331': 'WEIGHT_CHANGE_TOO_FAST', + '332': 'LOWER_GREATER_THAN_UPPER_TARGET', + '333': 'UPPER_TARGET_TOO_HIGH', + '334': 'UNHANDLED_BY_LINEAR_POOL', + '335': 'OUT_OF_TARGET_RANGE', + '336': 'UNHANDLED_EXIT_KIND ', + '337': 'UNAUTHORIZED_EXIT', + '338': 'MAX_MANAGEMENT_SWAP_FEE_PERCENTAGE', + '339': 'UNHANDLED_BY_MANAGED_POOL', + '340': 'UNHANDLED_BY_PHANTOM_POOL', + '341': 'TOKEN_DOES_NOT_HAVE_RATE_PROVIDER', + '342': 'INVALID_INITIALIZATION', + '343': 'OUT_OF_NEW_TARGET_RANGE', + '344': 'UNAUTHORIZED_OPERATION', + '345': 'UNINITIALIZED_POOL_CONTROLLER', + '400': 'REENTRANCY', + '401': 'SENDER_NOT_ALLOWED', + '402': 'PAUSED', + '403': 'PAUSE_WINDOW_EXPIRED', + '404': 'MAX_PAUSE_WINDOW_DURATION', + '405': 'MAX_BUFFER_PERIOD_DURATION', + '406': 'INSUFFICIENT_BALANCE', + '407': 'INSUFFICIENT_ALLOWANCE', + '408': 'ERC20_TRANSFER_FROM_ZERO_ADDRESS', + '409': 'ERC20_TRANSFER_TO_ZERO_ADDRESS', + '410': 'ERC20_MINT_TO_ZERO_ADDRESS', + '411': 'ERC20_BURN_FROM_ZERO_ADDRESS', + '412': 'ERC20_APPROVE_FROM_ZERO_ADDRESS', + '413': 'ERC20_APPROVE_TO_ZERO_ADDRESS', + '414': 'ERC20_TRANSFER_EXCEEDS_ALLOWANCE', + '415': 'ERC20_DECREASED_ALLOWANCE_BELOW_ZERO', + '416': 'ERC20_TRANSFER_EXCEEDS_BALANCE', + '417': 'ERC20_BURN_EXCEEDS_ALLOWANCE', + '418': 'SAFE_ERC20_CALL_FAILED', + '419': 'ADDRESS_INSUFFICIENT_BALANCE', + '420': 'ADDRESS_CANNOT_SEND_VALUE', + '421': 'SAFE_CAST_VALUE_CANT_FIT_INT256', + '422': 'GRANT_SENDER_NOT_ADMIN', + '423': 'REVOKE_SENDER_NOT_ADMIN', + '424': 'RENOUNCE_SENDER_NOT_ALLOWED', + '425': 'BUFFER_PERIOD_EXPIRED', + '426': 'CALLER_IS_NOT_OWNER', + '427': 'NEW_OWNER_IS_ZERO', + '428': 'CODE_DEPLOYMENT_FAILED', + '429': 'CALL_TO_NON_CONTRACT', + '430': 'LOW_LEVEL_CALL_FAILED', + '431': 'NOT_PAUSED', + '432': 'ADDRESS_ALREADY_ALLOWLISTED', + '433': 'ADDRESS_NOT_ALLOWLISTED', + '434': 'ERC20_BURN_EXCEEDS_BALANCE', + '500': 'INVALID_POOL_ID', + '501': 'CALLER_NOT_POOL', + '502': 'SENDER_NOT_ASSET_MANAGER', + '503': 'USER_DOESNT_ALLOW_RELAYER', + '504': 'INVALID_SIGNATURE', + '505': 'EXIT_BELOW_MIN', + '506': 'JOIN_ABOVE_MAX', + '507': 'SWAP_LIMIT', + '508': 'SWAP_DEADLINE', + '509': 'CANNOT_SWAP_SAME_TOKEN', + '510': 'UNKNOWN_AMOUNT_IN_FIRST_SWAP', + '511': 'MALCONSTRUCTED_MULTIHOP_SWAP', + '512': 'INTERNAL_BALANCE_OVERFLOW', + '513': 'INSUFFICIENT_INTERNAL_BALANCE', + '514': 'INVALID_ETH_INTERNAL_BALANCE', + '515': 'INVALID_POST_LOAN_BALANCE', + '516': 'INSUFFICIENT_ETH', + '517': 'UNALLOCATED_ETH', + '518': 'ETH_TRANSFER', + '519': 'CANNOT_USE_ETH_SENTINEL', + '520': 'TOKENS_MISMATCH', + '521': 'TOKEN_NOT_REGISTERED', + '522': 'TOKEN_ALREADY_REGISTERED', + '523': 'TOKENS_ALREADY_SET', + '524': 'TOKENS_LENGTH_MUST_BE_2', + '525': 'NONZERO_TOKEN_BALANCE', + '526': 'BALANCE_TOTAL_OVERFLOW', + '527': 'POOL_NO_TOKENS', + '528': 'INSUFFICIENT_FLASH_LOAN_BALANCE', + '600': 'SWAP_FEE_PERCENTAGE_TOO_HIGH', + '601': 'FLASH_LOAN_FEE_PERCENTAGE_TOO_HIGH', + '602': 'INSUFFICIENT_FLASH_LOAN_FEE_AMOUNT', }; export class BalancerErrors { - /** - * Cannot be constructed. - */ - private constructor() { - // eslint-disable-next-line @typescript-eslint/no-empty-function - } + /** + * Cannot be constructed. + */ + private constructor() { + // eslint-disable-next-line @typescript-eslint/no-empty-function + } - static isErrorCode = (error: string): boolean => { - if (!error.includes('BAL#')) return false; + static isErrorCode = (error: string): boolean => { + if (!error.includes('BAL#')) return false; - const errorCode = error.replace('BAL#', ''); - return Object.keys(balancerErrorCodes).includes(errorCode); - }; + const errorCode = error.replace('BAL#', ''); + return Object.keys(balancerErrorCodes).includes(errorCode); + }; - /** - * Decodes a Balancer error code into the corresponding reason - * @param error - a Balancer error code of the form `BAL#000` - * @returns The decoded error reason - */ - static parseErrorCode = (error: string): string => { - if (!error.includes('BAL#')) throw new Error('Error code not found'); - const errorCode = error.replace('BAL#', ''); + /** + * Decodes a Balancer error code into the corresponding reason + * @param error - a Balancer error code of the form `BAL#000` + * @returns The decoded error reason + */ + static parseErrorCode = (error: string): string => { + if (!error.includes('BAL#')) throw new Error('Error code not found'); + const errorCode = error.replace('BAL#', ''); - const actualError = balancerErrorCodes[errorCode]; + const actualError = balancerErrorCodes[errorCode]; - if (!actualError) throw new Error('Error code not found'); + if (!actualError) throw new Error('Error code not found'); - return actualError; - }; + return actualError; + }; - /** - * Decodes a Balancer error code into the corresponding reason - * @param error - a Balancer error code of the form `BAL#000` - * @returns The decoded error reason if passed a valid error code, otherwise returns passed input - */ - static tryParseErrorCode = (error: string): string => { - try { - return BalancerErrors.parseErrorCode(error); - } catch { - return error; - } - }; + /** + * Decodes a Balancer error code into the corresponding reason + * @param error - a Balancer error code of the form `BAL#000` + * @returns The decoded error reason if passed a valid error code, otherwise returns passed input + */ + static tryParseErrorCode = (error: string): string => { + try { + return BalancerErrors.parseErrorCode(error); + } catch { + return error; + } + }; - /** - * Tests whether a string is a known Balancer error message - * @param error - a string to be checked verified as a Balancer error message - */ - static isBalancerError = (error: string): boolean => - Object.values(balancerErrorCodes).includes(error); + /** + * Tests whether a string is a known Balancer error message + * @param error - a string to be checked verified as a Balancer error message + */ + static isBalancerError = (error: string): boolean => + Object.values(balancerErrorCodes).includes(error); - /** - * Encodes an error string into the corresponding error code - * @param error - a Balancer error message string - * @returns a Balancer error code of the form `BAL#000` - */ - static encodeError = (error: string): string => { - const encodedError = Object.entries(balancerErrorCodes).find( - ([, message]) => message === error - ); + /** + * Encodes an error string into the corresponding error code + * @param error - a Balancer error message string + * @returns a Balancer error code of the form `BAL#000` + */ + static encodeError = (error: string): string => { + const encodedError = Object.entries(balancerErrorCodes).find( + ([, message]) => message === error + ); - if (!encodedError) throw Error('Error message not found'); + if (!encodedError) throw Error('Error message not found'); - return `BAL#${encodedError[0]}`; - }; + return `BAL#${encodedError[0]}`; + }; } diff --git a/balancer-js/src/lib/utils/index.ts b/balancer-js/src/lib/utils/index.ts index f1e37cc01..d854d7baf 100644 --- a/balancer-js/src/lib/utils/index.ts +++ b/balancer-js/src/lib/utils/index.ts @@ -7,4 +7,4 @@ export * from './assetHelpers'; export * from './aaveHelpers'; export const isSameAddress = (address1: string, address2: string): boolean => - getAddress(address1) === getAddress(address2); + getAddress(address1) === getAddress(address2); diff --git a/balancer-js/src/lib/utils/multiCaller.ts b/balancer-js/src/lib/utils/multiCaller.ts index 8df9e3494..692cbe2a2 100644 --- a/balancer-js/src/lib/utils/multiCaller.ts +++ b/balancer-js/src/lib/utils/multiCaller.ts @@ -5,70 +5,70 @@ import { Provider } from '@ethersproject/providers'; import { BytesLike } from '@ethersproject/bytes'; export class Multicaller { - private multiAddress: string; - private provider: Provider; - private interface: Interface; - public options: CallOverrides = {}; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private calls: [string, string, any][] = []; - private paths: string[] = []; + private multiAddress: string; + private provider: Provider; + private interface: Interface; + public options: CallOverrides = {}; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private calls: [string, string, any][] = []; + private paths: string[] = []; - constructor( - multiAddress: string, - provider: Provider, - abi: string | Array, - options = {} - ) { - this.multiAddress = multiAddress; - this.provider = provider; - this.interface = new Interface(abi); - this.options = options; - } + constructor( + multiAddress: string, + provider: Provider, + abi: string | Array, + options = {} + ) { + this.multiAddress = multiAddress; + this.provider = provider; + this.interface = new Interface(abi); + this.options = options; + } - call( - path: string, - address: string, - functionName: string, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - params?: any[] - ): Multicaller { - this.calls.push([address, functionName, params]); - this.paths.push(path); - return this; - } + call( + path: string, + address: string, + functionName: string, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + params?: any[] + ): Multicaller { + this.calls.push([address, functionName, params]); + this.paths.push(path); + return this; + } - async execute( - from: Record = {} - ): Promise> { - const obj = from; - const results = await this.executeMulticall(); - results.forEach((result, i) => - set(obj, this.paths[i], result.length > 1 ? result : result[0]) - ); - this.calls = []; - this.paths = []; - return obj; - } + async execute( + from: Record = {} + ): Promise> { + const obj = from; + const results = await this.executeMulticall(); + results.forEach((result, i) => + set(obj, this.paths[i], result.length > 1 ? result : result[0]) + ); + this.calls = []; + this.paths = []; + return obj; + } - private async executeMulticall(): Promise { - const multi = new Contract( - this.multiAddress, - [ - 'function aggregate(tuple[](address target, bytes callData) memory calls) public view returns (uint256 blockNumber, bytes[] memory returnData)', - ], - this.provider - ); + private async executeMulticall(): Promise { + const multi = new Contract( + this.multiAddress, + [ + 'function aggregate(tuple[](address target, bytes callData) memory calls) public view returns (uint256 blockNumber, bytes[] memory returnData)', + ], + this.provider + ); - const [, res] = await multi.aggregate( - this.calls.map(([address, functionName, params]) => [ - address, - this.interface.encodeFunctionData(functionName, params), - ]), - this.options - ); + const [, res] = await multi.aggregate( + this.calls.map(([address, functionName, params]) => [ + address, + this.interface.encodeFunctionData(functionName, params), + ]), + this.options + ); - return res.map((result: BytesLike, i: number) => - this.interface.decodeFunctionResult(this.calls[i][1], result) - ); - } + return res.map((result: BytesLike, i: number) => + this.interface.decodeFunctionResult(this.calls[i][1], result) + ); + } } diff --git a/balancer-js/src/lib/utils/permit.ts b/balancer-js/src/lib/utils/permit.ts index 680d051dc..ecdae17d1 100644 --- a/balancer-js/src/lib/utils/permit.ts +++ b/balancer-js/src/lib/utils/permit.ts @@ -6,53 +6,53 @@ import { Contract } from '@ethersproject/contracts'; import { Account, accountToAddress } from './signatures'; export const signPermit = async ( - token: Contract, - owner: Signer & TypedDataSigner, - spender: Account, - amount: BigNumberish, - deadline: BigNumberish = MAX_DEADLINE, - nonce?: BigNumberish + token: Contract, + owner: Signer & TypedDataSigner, + spender: Account, + amount: BigNumberish, + deadline: BigNumberish = MAX_DEADLINE, + nonce?: BigNumberish ): Promise<{ - v: number; - r: string; - s: string; - deadline: BigNumber; - nonce: BigNumber; + v: number; + r: string; + s: string; + deadline: BigNumber; + nonce: BigNumber; }> => { - const { chainId } = await token.provider.getNetwork(); - const ownerAddress = await owner.getAddress(); + const { chainId } = await token.provider.getNetwork(); + const ownerAddress = await owner.getAddress(); - if (!nonce) nonce = (await token.nonces(ownerAddress)) as BigNumberish; + if (!nonce) nonce = (await token.nonces(ownerAddress)) as BigNumberish; - const domain = { - name: await token.name(), - version: '1', - chainId, - verifyingContract: token.address, - }; + const domain = { + name: await token.name(), + version: '1', + chainId, + verifyingContract: token.address, + }; - const types = { - Permit: [ - { name: 'owner', type: 'address' }, - { name: 'spender', type: 'address' }, - { name: 'value', type: 'uint256' }, - { name: 'nonce', type: 'uint256' }, - { name: 'deadline', type: 'uint256' }, - ], - }; + const types = { + Permit: [ + { name: 'owner', type: 'address' }, + { name: 'spender', type: 'address' }, + { name: 'value', type: 'uint256' }, + { name: 'nonce', type: 'uint256' }, + { name: 'deadline', type: 'uint256' }, + ], + }; - const value = { - owner: ownerAddress, - spender: await accountToAddress(spender), - value: amount, - nonce, - deadline, - }; + const value = { + owner: ownerAddress, + spender: await accountToAddress(spender), + value: amount, + nonce, + deadline, + }; - const signature = await owner._signTypedData(domain, types, value); - return { - ...splitSignature(signature), - deadline: BigNumber.from(deadline), - nonce: BigNumber.from(nonce), - }; + const signature = await owner._signTypedData(domain, types, value); + return { + ...splitSignature(signature), + deadline: BigNumber.from(deadline), + nonce: BigNumber.from(nonce), + }; }; diff --git a/balancer-js/src/lib/utils/signatures.ts b/balancer-js/src/lib/utils/signatures.ts index e5aadbb2f..fce681d02 100644 --- a/balancer-js/src/lib/utils/signatures.ts +++ b/balancer-js/src/lib/utils/signatures.ts @@ -7,225 +7,223 @@ import { Signer, TypedDataSigner } from '@ethersproject/abstract-signer'; export type Account = string | Signer | Contract; export async function accountToAddress(account: Account): Promise { - if (typeof account == 'string') return account; - if (Signer.isSigner(account)) return account.getAddress(); - if (account.address) return account.address; - throw new Error('Could not read account address'); + if (typeof account == 'string') return account; + if (Signer.isSigner(account)) return account.getAddress(); + if (account.address) return account.address; + throw new Error('Could not read account address'); } export enum RelayerAction { - JoinPool = 'JoinPool', - ExitPool = 'ExitPool', - Swap = 'Swap', - BatchSwap = 'BatchSwap', - SetRelayerApproval = 'SetRelayerApproval', + JoinPool = 'JoinPool', + ExitPool = 'ExitPool', + Swap = 'Swap', + BatchSwap = 'BatchSwap', + SetRelayerApproval = 'SetRelayerApproval', } export class RelayerAuthorization { - /** - * Cannot be constructed. - */ - private constructor() { - // eslint-disable-next-line @typescript-eslint/no-empty-function + /** + * Cannot be constructed. + */ + private constructor() { + // eslint-disable-next-line @typescript-eslint/no-empty-function + } + + static encodeCalldataAuthorization = ( + calldata: string, + deadline: BigNumberish, + signature: string + ): string => { + const encodedDeadline = hexZeroPad(hexValue(deadline), 32).slice(2); + const { v, r, s } = splitSignature(signature); + const encodedV = hexZeroPad(hexValue(v), 32).slice(2); + const encodedR = r.slice(2); + const encodedS = s.slice(2); + return `${calldata}${encodedDeadline}${encodedV}${encodedR}${encodedS}`; + }; + + static signJoinAuthorization = ( + validator: Contract, + user: Signer & TypedDataSigner, + allowedSender: Account, + allowedCalldata: string, + deadline?: BigNumberish, + nonce?: BigNumberish + ): Promise => + RelayerAuthorization.signAuthorizationFor( + RelayerAction.JoinPool, + validator, + user, + allowedSender, + allowedCalldata, + deadline, + nonce + ); + + static signExitAuthorization = ( + validator: Contract, + user: Signer & TypedDataSigner, + allowedSender: Account, + allowedCalldata: string, + deadline?: BigNumberish, + nonce?: BigNumberish + ): Promise => + RelayerAuthorization.signAuthorizationFor( + RelayerAction.ExitPool, + validator, + user, + allowedSender, + allowedCalldata, + deadline, + nonce + ); + + static signSwapAuthorization = ( + validator: Contract, + user: Signer & TypedDataSigner, + allowedSender: Account, + allowedCalldata: string, + deadline?: BigNumberish, + nonce?: BigNumberish + ): Promise => + RelayerAuthorization.signAuthorizationFor( + RelayerAction.Swap, + validator, + user, + allowedSender, + allowedCalldata, + deadline, + nonce + ); + + static signBatchSwapAuthorization = ( + validator: Contract, + user: Signer & TypedDataSigner, + allowedSender: Account, + allowedCalldata: string, + deadline?: BigNumberish, + nonce?: BigNumberish + ): Promise => + RelayerAuthorization.signAuthorizationFor( + RelayerAction.BatchSwap, + validator, + user, + allowedSender, + allowedCalldata, + deadline, + nonce + ); + + static signSetRelayerApprovalAuthorization = ( + validator: Contract, + user: Signer & TypedDataSigner, + allowedSender: Account, + allowedCalldata: string, + deadline?: BigNumberish, + nonce?: BigNumberish + ): Promise => + RelayerAuthorization.signAuthorizationFor( + RelayerAction.SetRelayerApproval, + validator, + user, + allowedSender, + allowedCalldata, + deadline, + nonce + ); + + static signAuthorizationFor = async ( + type: RelayerAction, + validator: Contract, + user: Signer & TypedDataSigner, + allowedSender: Account, + allowedCalldata: string, + deadline: BigNumberish = MAX_DEADLINE, + nonce?: BigNumberish + ): Promise => { + const { chainId } = await validator.provider.getNetwork(); + if (!nonce) { + const userAddress = await user.getAddress(); + nonce = (await validator.getNextNonce(userAddress)) as BigNumberish; } - static encodeCalldataAuthorization = ( - calldata: string, - deadline: BigNumberish, - signature: string - ): string => { - const encodedDeadline = hexZeroPad(hexValue(deadline), 32).slice(2); - const { v, r, s } = splitSignature(signature); - const encodedV = hexZeroPad(hexValue(v), 32).slice(2); - const encodedR = r.slice(2); - const encodedS = s.slice(2); - return `${calldata}${encodedDeadline}${encodedV}${encodedR}${encodedS}`; + const domain = { + name: 'Balancer V2 Vault', + version: '1', + chainId, + verifyingContract: validator.address, }; - static signJoinAuthorization = ( - validator: Contract, - user: Signer & TypedDataSigner, - allowedSender: Account, - allowedCalldata: string, - deadline?: BigNumberish, - nonce?: BigNumberish - ): Promise => - RelayerAuthorization.signAuthorizationFor( - RelayerAction.JoinPool, - validator, - user, - allowedSender, - allowedCalldata, - deadline, - nonce - ); - - static signExitAuthorization = ( - validator: Contract, - user: Signer & TypedDataSigner, - allowedSender: Account, - allowedCalldata: string, - deadline?: BigNumberish, - nonce?: BigNumberish - ): Promise => - RelayerAuthorization.signAuthorizationFor( - RelayerAction.ExitPool, - validator, - user, - allowedSender, - allowedCalldata, - deadline, - nonce - ); - - static signSwapAuthorization = ( - validator: Contract, - user: Signer & TypedDataSigner, - allowedSender: Account, - allowedCalldata: string, - deadline?: BigNumberish, - nonce?: BigNumberish - ): Promise => - RelayerAuthorization.signAuthorizationFor( - RelayerAction.Swap, - validator, - user, - allowedSender, - allowedCalldata, - deadline, - nonce - ); - - static signBatchSwapAuthorization = ( - validator: Contract, - user: Signer & TypedDataSigner, - allowedSender: Account, - allowedCalldata: string, - deadline?: BigNumberish, - nonce?: BigNumberish - ): Promise => - RelayerAuthorization.signAuthorizationFor( - RelayerAction.BatchSwap, - validator, - user, - allowedSender, - allowedCalldata, - deadline, - nonce - ); - - static signSetRelayerApprovalAuthorization = ( - validator: Contract, - user: Signer & TypedDataSigner, - allowedSender: Account, - allowedCalldata: string, - deadline?: BigNumberish, - nonce?: BigNumberish - ): Promise => - RelayerAuthorization.signAuthorizationFor( - RelayerAction.SetRelayerApproval, - validator, - user, - allowedSender, - allowedCalldata, - deadline, - nonce - ); - - static signAuthorizationFor = async ( - type: RelayerAction, - validator: Contract, - user: Signer & TypedDataSigner, - allowedSender: Account, - allowedCalldata: string, - deadline: BigNumberish = MAX_DEADLINE, - nonce?: BigNumberish - ): Promise => { - const { chainId } = await validator.provider.getNetwork(); - if (!nonce) { - const userAddress = await user.getAddress(); - nonce = (await validator.getNextNonce(userAddress)) as BigNumberish; - } - - const domain = { - name: 'Balancer V2 Vault', - version: '1', - chainId, - verifyingContract: validator.address, - }; - - const types = { - [type]: [ - { name: 'calldata', type: 'bytes' }, - { name: 'sender', type: 'address' }, - { name: 'nonce', type: 'uint256' }, - { name: 'deadline', type: 'uint256' }, - ], - }; - - const value = { - calldata: allowedCalldata, - sender: await accountToAddress(allowedSender), - nonce: nonce.toString(), - deadline: deadline.toString(), - }; - - return user._signTypedData(domain, types, value); + const types = { + [type]: [ + { name: 'calldata', type: 'bytes' }, + { name: 'sender', type: 'address' }, + { name: 'nonce', type: 'uint256' }, + { name: 'deadline', type: 'uint256' }, + ], }; + + const value = { + calldata: allowedCalldata, + sender: await accountToAddress(allowedSender), + nonce: nonce.toString(), + deadline: deadline.toString(), + }; + + return user._signTypedData(domain, types, value); + }; } export class BalancerMinterAuthorization { - /** - * Cannot be constructed. - */ - private constructor() { - // eslint-disable-next-line @typescript-eslint/no-empty-function + /** + * Cannot be constructed. + */ + private constructor() { + // eslint-disable-next-line @typescript-eslint/no-empty-function + } + + static signSetMinterApproval = async ( + minterContract: Contract, + minter: Account, + approval: boolean, + user: Signer & TypedDataSigner, + deadline: BigNumberish = MAX_DEADLINE, + nonce?: BigNumberish + ): Promise<{ v: number; r: string; s: string; deadline: BigNumber }> => { + const { chainId } = await minterContract.provider.getNetwork(); + if (!nonce) { + const userAddress = await user.getAddress(); + nonce = (await minterContract.getNextNonce(userAddress)) as BigNumberish; } - static signSetMinterApproval = async ( - minterContract: Contract, - minter: Account, - approval: boolean, - user: Signer & TypedDataSigner, - deadline: BigNumberish = MAX_DEADLINE, - nonce?: BigNumberish - ): Promise<{ v: number; r: string; s: string; deadline: BigNumber }> => { - const { chainId } = await minterContract.provider.getNetwork(); - if (!nonce) { - const userAddress = await user.getAddress(); - nonce = (await minterContract.getNextNonce( - userAddress - )) as BigNumberish; - } - - const domain = { - name: 'Balancer Minter', - version: '1', - chainId, - verifyingContract: minterContract.address, - }; - - const types = { - SetMinterApproval: [ - { name: 'minter', type: 'address' }, - { name: 'approval', type: 'bool' }, - { name: 'nonce', type: 'uint256' }, - { name: 'deadline', type: 'uint256' }, - ], - }; - - const value = { - minter: await accountToAddress(minter), - approval, - nonce: nonce.toString(), - deadline: deadline.toString(), - }; - - const signature = await user._signTypedData(domain, types, value); - - return { - ...splitSignature(signature), - deadline: BigNumber.from(deadline), - }; + const domain = { + name: 'Balancer Minter', + version: '1', + chainId, + verifyingContract: minterContract.address, + }; + + const types = { + SetMinterApproval: [ + { name: 'minter', type: 'address' }, + { name: 'approval', type: 'bool' }, + { name: 'nonce', type: 'uint256' }, + { name: 'deadline', type: 'uint256' }, + ], + }; + + const value = { + minter: await accountToAddress(minter), + approval, + nonce: nonce.toString(), + deadline: deadline.toString(), + }; + + const signature = await user._signTypedData(domain, types, value); + + return { + ...splitSignature(signature), + deadline: BigNumber.from(deadline), }; + }; } diff --git a/balancer-js/src/modules/pools/pool-types/concerns/linear/liquidity.concern.ts b/balancer-js/src/modules/pools/pool-types/concerns/linear/liquidity.concern.ts index 3ea3cdd57..cce4f3997 100644 --- a/balancer-js/src/modules/pools/pool-types/concerns/linear/liquidity.concern.ts +++ b/balancer-js/src/modules/pools/pool-types/concerns/linear/liquidity.concern.ts @@ -2,15 +2,15 @@ import { LiquidityConcern } from '../types'; import { BigNumberish } from '@ethersproject/bignumber'; export class LinearPoolLiquidity implements LiquidityConcern { - calcTotal( - tokenBalances: BigNumberish[], - tokenDecimals: number[], - tokenPriceRates: BigNumberish[], - tokenPrices: (number | null)[] - ): string { - // TODO implementation - console.log(tokenBalances, tokenDecimals, tokenPriceRates, tokenPrices); - throw new Error('To be implemented'); - return '1000'; - } + calcTotal( + tokenBalances: BigNumberish[], + tokenDecimals: number[], + tokenPriceRates: BigNumberish[], + tokenPrices: (number | null)[] + ): string { + // TODO implementation + console.log(tokenBalances, tokenDecimals, tokenPriceRates, tokenPrices); + throw new Error('To be implemented'); + return '1000'; + } } diff --git a/balancer-js/src/modules/pools/pool-types/concerns/linear/spotPrice.concern.ts b/balancer-js/src/modules/pools/pool-types/concerns/linear/spotPrice.concern.ts index 7597ce129..b34d75f92 100644 --- a/balancer-js/src/modules/pools/pool-types/concerns/linear/spotPrice.concern.ts +++ b/balancer-js/src/modules/pools/pool-types/concerns/linear/spotPrice.concern.ts @@ -2,15 +2,15 @@ import { SpotPriceConcern } from '../types'; import { SubgraphPoolBase, LinearPool, ZERO } from '@balancer-labs/sor'; export class LinearPoolSpotPrice implements SpotPriceConcern { - calcPoolSpotPrice( - tokenIn: string, - tokenOut: string, - pool: SubgraphPoolBase - ): string { - const poolClass = LinearPool.fromPool(pool); - const poolPairData = poolClass.parsePoolPairData(tokenIn, tokenOut); - return poolClass - ._spotPriceAfterSwapExactTokenInForTokenOut(poolPairData, ZERO) - .toString(); - } + calcPoolSpotPrice( + tokenIn: string, + tokenOut: string, + pool: SubgraphPoolBase + ): string { + const poolClass = LinearPool.fromPool(pool); + const poolPairData = poolClass.parsePoolPairData(tokenIn, tokenOut); + return poolClass + ._spotPriceAfterSwapExactTokenInForTokenOut(poolPairData, ZERO) + .toString(); + } } diff --git a/balancer-js/src/modules/pools/pool-types/concerns/metaStable/liquidity.concern.ts b/balancer-js/src/modules/pools/pool-types/concerns/metaStable/liquidity.concern.ts index 16a732911..53de6116d 100644 --- a/balancer-js/src/modules/pools/pool-types/concerns/metaStable/liquidity.concern.ts +++ b/balancer-js/src/modules/pools/pool-types/concerns/metaStable/liquidity.concern.ts @@ -2,15 +2,15 @@ import { LiquidityConcern } from '../types'; import { BigNumberish } from '@ethersproject/bignumber'; export class MetaStablePoolLiquidity implements LiquidityConcern { - calcTotal( - tokenBalances: BigNumberish[], - tokenDecimals: number[], - tokenPriceRates: BigNumberish[], - tokenPrices: (number | null)[] - ): string { - // TODO implementation - console.log(tokenBalances, tokenDecimals, tokenPriceRates, tokenPrices); - throw new Error('To be implemented'); - return '1000'; - } + calcTotal( + tokenBalances: BigNumberish[], + tokenDecimals: number[], + tokenPriceRates: BigNumberish[], + tokenPrices: (number | null)[] + ): string { + // TODO implementation + console.log(tokenBalances, tokenDecimals, tokenPriceRates, tokenPrices); + throw new Error('To be implemented'); + return '1000'; + } } diff --git a/balancer-js/src/modules/pools/pool-types/concerns/metaStable/spotPrice.concern.ts b/balancer-js/src/modules/pools/pool-types/concerns/metaStable/spotPrice.concern.ts index 856ec86f0..0e8eae8e7 100644 --- a/balancer-js/src/modules/pools/pool-types/concerns/metaStable/spotPrice.concern.ts +++ b/balancer-js/src/modules/pools/pool-types/concerns/metaStable/spotPrice.concern.ts @@ -2,15 +2,15 @@ import { SpotPriceConcern } from '../types'; import { SubgraphPoolBase, MetaStablePool, ZERO } from '@balancer-labs/sor'; export class MetaStablePoolSpotPrice implements SpotPriceConcern { - calcPoolSpotPrice( - tokenIn: string, - tokenOut: string, - pool: SubgraphPoolBase - ): string { - const poolClass = MetaStablePool.fromPool(pool); - const poolPairData = poolClass.parsePoolPairData(tokenIn, tokenOut); - return poolClass - ._spotPriceAfterSwapExactTokenInForTokenOut(poolPairData, ZERO) - .toString(); - } + calcPoolSpotPrice( + tokenIn: string, + tokenOut: string, + pool: SubgraphPoolBase + ): string { + const poolClass = MetaStablePool.fromPool(pool); + const poolPairData = poolClass.parsePoolPairData(tokenIn, tokenOut); + return poolClass + ._spotPriceAfterSwapExactTokenInForTokenOut(poolPairData, ZERO) + .toString(); + } } diff --git a/balancer-js/src/modules/pools/pool-types/concerns/stable/liquidity.concern.ts b/balancer-js/src/modules/pools/pool-types/concerns/stable/liquidity.concern.ts index b2416311c..7ebd1e4f4 100644 --- a/balancer-js/src/modules/pools/pool-types/concerns/stable/liquidity.concern.ts +++ b/balancer-js/src/modules/pools/pool-types/concerns/stable/liquidity.concern.ts @@ -2,15 +2,15 @@ import { LiquidityConcern } from '../types'; import { BigNumberish } from '@ethersproject/bignumber'; export class StablePoolLiquidity implements LiquidityConcern { - calcTotal( - tokenBalances: BigNumberish[], - tokenDecimals: number[], - tokenPriceRates: BigNumberish[], - tokenPrices: (number | null)[] - ): string { - // TODO implementation - console.log(tokenBalances, tokenDecimals, tokenPriceRates, tokenPrices); - throw new Error('To be implemented'); - return '1000'; - } + calcTotal( + tokenBalances: BigNumberish[], + tokenDecimals: number[], + tokenPriceRates: BigNumberish[], + tokenPrices: (number | null)[] + ): string { + // TODO implementation + console.log(tokenBalances, tokenDecimals, tokenPriceRates, tokenPrices); + throw new Error('To be implemented'); + return '1000'; + } } diff --git a/balancer-js/src/modules/pools/pool-types/concerns/stable/spotPrice.concern.ts b/balancer-js/src/modules/pools/pool-types/concerns/stable/spotPrice.concern.ts index e3a0ee123..de55a6c35 100644 --- a/balancer-js/src/modules/pools/pool-types/concerns/stable/spotPrice.concern.ts +++ b/balancer-js/src/modules/pools/pool-types/concerns/stable/spotPrice.concern.ts @@ -2,15 +2,15 @@ import { SpotPriceConcern } from '../types'; import { SubgraphPoolBase, StablePool, ZERO } from '@balancer-labs/sor'; export class StablePoolSpotPrice implements SpotPriceConcern { - calcPoolSpotPrice( - tokenIn: string, - tokenOut: string, - pool: SubgraphPoolBase - ): string { - const poolClass = StablePool.fromPool(pool); - const poolPairData = poolClass.parsePoolPairData(tokenIn, tokenOut); - return poolClass - ._spotPriceAfterSwapExactTokenInForTokenOut(poolPairData, ZERO) - .toString(); - } + calcPoolSpotPrice( + tokenIn: string, + tokenOut: string, + pool: SubgraphPoolBase + ): string { + const poolClass = StablePool.fromPool(pool); + const poolPairData = poolClass.parsePoolPairData(tokenIn, tokenOut); + return poolClass + ._spotPriceAfterSwapExactTokenInForTokenOut(poolPairData, ZERO) + .toString(); + } } diff --git a/balancer-js/src/modules/pools/pool-types/concerns/stablePhantom/liquidity.concern.ts b/balancer-js/src/modules/pools/pool-types/concerns/stablePhantom/liquidity.concern.ts index 48db372a9..8a27c71a6 100644 --- a/balancer-js/src/modules/pools/pool-types/concerns/stablePhantom/liquidity.concern.ts +++ b/balancer-js/src/modules/pools/pool-types/concerns/stablePhantom/liquidity.concern.ts @@ -2,15 +2,15 @@ import { LiquidityConcern } from '../types'; import { BigNumberish } from '@ethersproject/bignumber'; export class StablePhantomPoolLiquidity implements LiquidityConcern { - calcTotal( - tokenBalances: BigNumberish[], - tokenDecimals: number[], - tokenPriceRates: BigNumberish[], - tokenPrices: (number | null)[] - ): string { - // TODO implementation - console.log(tokenBalances, tokenDecimals, tokenPriceRates, tokenPrices); - throw new Error('To be implemented'); - return '1000'; - } + calcTotal( + tokenBalances: BigNumberish[], + tokenDecimals: number[], + tokenPriceRates: BigNumberish[], + tokenPrices: (number | null)[] + ): string { + // TODO implementation + console.log(tokenBalances, tokenDecimals, tokenPriceRates, tokenPrices); + throw new Error('To be implemented'); + return '1000'; + } } diff --git a/balancer-js/src/modules/pools/pool-types/concerns/stablePhantom/spotPrice.concern.ts b/balancer-js/src/modules/pools/pool-types/concerns/stablePhantom/spotPrice.concern.ts index 951883749..cfc7048a1 100644 --- a/balancer-js/src/modules/pools/pool-types/concerns/stablePhantom/spotPrice.concern.ts +++ b/balancer-js/src/modules/pools/pool-types/concerns/stablePhantom/spotPrice.concern.ts @@ -2,15 +2,15 @@ import { SpotPriceConcern } from '../types'; import { SubgraphPoolBase, PhantomStablePool, ZERO } from '@balancer-labs/sor'; export class StablePhantomPoolSpotPrice implements SpotPriceConcern { - calcPoolSpotPrice( - tokenIn: string, - tokenOut: string, - pool: SubgraphPoolBase - ): string { - const poolClass = PhantomStablePool.fromPool(pool); - const poolPairData = poolClass.parsePoolPairData(tokenIn, tokenOut); - return poolClass - ._spotPriceAfterSwapExactTokenInForTokenOut(poolPairData, ZERO) - .toString(); - } + calcPoolSpotPrice( + tokenIn: string, + tokenOut: string, + pool: SubgraphPoolBase + ): string { + const poolClass = PhantomStablePool.fromPool(pool); + const poolPairData = poolClass.parsePoolPairData(tokenIn, tokenOut); + return poolClass + ._spotPriceAfterSwapExactTokenInForTokenOut(poolPairData, ZERO) + .toString(); + } } diff --git a/balancer-js/src/modules/pools/pool-types/concerns/types.ts b/balancer-js/src/modules/pools/pool-types/concerns/types.ts index 9209d0cb4..7ea1abfd3 100644 --- a/balancer-js/src/modules/pools/pool-types/concerns/types.ts +++ b/balancer-js/src/modules/pools/pool-types/concerns/types.ts @@ -3,13 +3,13 @@ import { SubgraphPoolBase } from '@balancer-labs/sor'; export interface LiquidityConcern { - calcTotal: (...args: any[]) => string; + calcTotal: (...args: any[]) => string; } export interface SpotPriceConcern { - calcPoolSpotPrice: ( - tokenIn: string, - tokenOut: string, - pool: SubgraphPoolBase - ) => string; + calcPoolSpotPrice: ( + tokenIn: string, + tokenOut: string, + pool: SubgraphPoolBase + ) => string; } diff --git a/balancer-js/src/modules/pools/pool-types/concerns/weighted/liquidity.concern.ts b/balancer-js/src/modules/pools/pool-types/concerns/weighted/liquidity.concern.ts index e164a4655..a600f67bc 100644 --- a/balancer-js/src/modules/pools/pool-types/concerns/weighted/liquidity.concern.ts +++ b/balancer-js/src/modules/pools/pool-types/concerns/weighted/liquidity.concern.ts @@ -2,22 +2,22 @@ import { LiquidityConcern } from '../types'; import { BigNumberish } from '@ethersproject/bignumber'; export class WeightedPoolLiquidity implements LiquidityConcern { - calcTotal( - tokenBalances: BigNumberish[], - tokenDecimals: number[], - tokenPriceRates: BigNumberish[], - tokenPrices: (number | null)[], - someOtherInput: number - ): string { - // TODO implementation - console.log( - tokenBalances, - tokenDecimals, - tokenPriceRates, - tokenPrices, - someOtherInput - ); - throw new Error('To be implemented'); - return '100'; - } + calcTotal( + tokenBalances: BigNumberish[], + tokenDecimals: number[], + tokenPriceRates: BigNumberish[], + tokenPrices: (number | null)[], + someOtherInput: number + ): string { + // TODO implementation + console.log( + tokenBalances, + tokenDecimals, + tokenPriceRates, + tokenPrices, + someOtherInput + ); + throw new Error('To be implemented'); + return '100'; + } } diff --git a/balancer-js/src/modules/pools/pool-types/concerns/weighted/spotPrice.concern.ts b/balancer-js/src/modules/pools/pool-types/concerns/weighted/spotPrice.concern.ts index 6cfba680a..77c247ba2 100644 --- a/balancer-js/src/modules/pools/pool-types/concerns/weighted/spotPrice.concern.ts +++ b/balancer-js/src/modules/pools/pool-types/concerns/weighted/spotPrice.concern.ts @@ -2,15 +2,15 @@ import { SpotPriceConcern } from '../types'; import { SubgraphPoolBase, WeightedPool, ZERO } from '@balancer-labs/sor'; export class WeightedPoolSpotPrice implements SpotPriceConcern { - calcPoolSpotPrice( - tokenIn: string, - tokenOut: string, - pool: SubgraphPoolBase - ): string { - const weightedPool = WeightedPool.fromPool(pool); - const poolPairData = weightedPool.parsePoolPairData(tokenIn, tokenOut); - return weightedPool - ._spotPriceAfterSwapExactTokenInForTokenOut(poolPairData, ZERO) - .toString(); - } + calcPoolSpotPrice( + tokenIn: string, + tokenOut: string, + pool: SubgraphPoolBase + ): string { + const weightedPool = WeightedPool.fromPool(pool); + const poolPairData = weightedPool.parsePoolPairData(tokenIn, tokenOut); + return weightedPool + ._spotPriceAfterSwapExactTokenInForTokenOut(poolPairData, ZERO) + .toString(); + } } diff --git a/balancer-js/src/modules/pools/pool-types/linear.module.ts b/balancer-js/src/modules/pools/pool-types/linear.module.ts index 7d8c8328a..3b1ca2dc8 100644 --- a/balancer-js/src/modules/pools/pool-types/linear.module.ts +++ b/balancer-js/src/modules/pools/pool-types/linear.module.ts @@ -4,14 +4,14 @@ import { PoolType } from './pool-type.interface'; import { LiquidityConcern, SpotPriceConcern } from './concerns/types'; export class Linear implements PoolType { - public liquidityCalculator: LiquidityConcern; - public spotPriceCalculator: SpotPriceConcern; + public liquidityCalculator: LiquidityConcern; + public spotPriceCalculator: SpotPriceConcern; - constructor( - private liquidityCalculatorConcern = LinearPoolLiquidity, - private spotPriceCalculatorConcern = LinearPoolSpotPrice - ) { - this.liquidityCalculator = new this.liquidityCalculatorConcern(); - this.spotPriceCalculator = new this.spotPriceCalculatorConcern(); - } + constructor( + private liquidityCalculatorConcern = LinearPoolLiquidity, + private spotPriceCalculatorConcern = LinearPoolSpotPrice + ) { + this.liquidityCalculator = new this.liquidityCalculatorConcern(); + this.spotPriceCalculator = new this.spotPriceCalculatorConcern(); + } } diff --git a/balancer-js/src/modules/pools/pool-types/metaStable.module.ts b/balancer-js/src/modules/pools/pool-types/metaStable.module.ts index 0595d4ad5..b0ca76bfc 100644 --- a/balancer-js/src/modules/pools/pool-types/metaStable.module.ts +++ b/balancer-js/src/modules/pools/pool-types/metaStable.module.ts @@ -4,14 +4,14 @@ import { PoolType } from './pool-type.interface'; import { LiquidityConcern, SpotPriceConcern } from './concerns/types'; export class MetaStable implements PoolType { - public liquidityCalculator: LiquidityConcern; - public spotPriceCalculator: SpotPriceConcern; + public liquidityCalculator: LiquidityConcern; + public spotPriceCalculator: SpotPriceConcern; - constructor( - private liquidityCalculatorConcern = MetaStablePoolLiquidity, - private spotPriceCalculatorConcern = MetaStablePoolSpotPrice - ) { - this.liquidityCalculator = new this.liquidityCalculatorConcern(); - this.spotPriceCalculator = new this.spotPriceCalculatorConcern(); - } + constructor( + private liquidityCalculatorConcern = MetaStablePoolLiquidity, + private spotPriceCalculatorConcern = MetaStablePoolSpotPrice + ) { + this.liquidityCalculator = new this.liquidityCalculatorConcern(); + this.spotPriceCalculator = new this.spotPriceCalculatorConcern(); + } } diff --git a/balancer-js/src/modules/pools/pool-types/pool-type.interface.ts b/balancer-js/src/modules/pools/pool-types/pool-type.interface.ts index 1bfd13305..1eb1bbc94 100644 --- a/balancer-js/src/modules/pools/pool-types/pool-type.interface.ts +++ b/balancer-js/src/modules/pools/pool-types/pool-type.interface.ts @@ -1,6 +1,6 @@ import { LiquidityConcern, SpotPriceConcern } from './concerns/types'; export interface PoolType { - liquidityCalculator: LiquidityConcern; - spotPriceCalculator: SpotPriceConcern; + liquidityCalculator: LiquidityConcern; + spotPriceCalculator: SpotPriceConcern; } diff --git a/balancer-js/src/modules/pools/pool-types/stable.module.ts b/balancer-js/src/modules/pools/pool-types/stable.module.ts index 94658a50a..c6e48f2ba 100644 --- a/balancer-js/src/modules/pools/pool-types/stable.module.ts +++ b/balancer-js/src/modules/pools/pool-types/stable.module.ts @@ -4,14 +4,14 @@ import { PoolType } from './pool-type.interface'; import { LiquidityConcern, SpotPriceConcern } from './concerns/types'; export class Stable implements PoolType { - public liquidityCalculator: LiquidityConcern; - public spotPriceCalculator: SpotPriceConcern; + public liquidityCalculator: LiquidityConcern; + public spotPriceCalculator: SpotPriceConcern; - constructor( - private liquidityCalculatorConcern = StablePoolLiquidity, - private spotPriceCalculatorConcern = StablePoolSpotPrice - ) { - this.liquidityCalculator = new this.liquidityCalculatorConcern(); - this.spotPriceCalculator = new this.spotPriceCalculatorConcern(); - } + constructor( + private liquidityCalculatorConcern = StablePoolLiquidity, + private spotPriceCalculatorConcern = StablePoolSpotPrice + ) { + this.liquidityCalculator = new this.liquidityCalculatorConcern(); + this.spotPriceCalculator = new this.spotPriceCalculatorConcern(); + } } diff --git a/balancer-js/src/modules/pools/pool-types/stablePhantom.module.ts b/balancer-js/src/modules/pools/pool-types/stablePhantom.module.ts index ee659d7fc..258f71e54 100644 --- a/balancer-js/src/modules/pools/pool-types/stablePhantom.module.ts +++ b/balancer-js/src/modules/pools/pool-types/stablePhantom.module.ts @@ -4,14 +4,14 @@ import { PoolType } from './pool-type.interface'; import { LiquidityConcern, SpotPriceConcern } from './concerns/types'; export class StablePhantom implements PoolType { - public liquidityCalculator: LiquidityConcern; - public spotPriceCalculator: SpotPriceConcern; + public liquidityCalculator: LiquidityConcern; + public spotPriceCalculator: SpotPriceConcern; - constructor( - private liquidityCalculatorConcern = StablePhantomPoolLiquidity, - private spotPriceCalculatorConcern = StablePhantomPoolSpotPrice - ) { - this.liquidityCalculator = new this.liquidityCalculatorConcern(); - this.spotPriceCalculator = new this.spotPriceCalculatorConcern(); - } + constructor( + private liquidityCalculatorConcern = StablePhantomPoolLiquidity, + private spotPriceCalculatorConcern = StablePhantomPoolSpotPrice + ) { + this.liquidityCalculator = new this.liquidityCalculatorConcern(); + this.spotPriceCalculator = new this.spotPriceCalculatorConcern(); + } } diff --git a/balancer-js/src/modules/pools/pool-types/weighted.module.ts b/balancer-js/src/modules/pools/pool-types/weighted.module.ts index dde60f142..e5f520a59 100644 --- a/balancer-js/src/modules/pools/pool-types/weighted.module.ts +++ b/balancer-js/src/modules/pools/pool-types/weighted.module.ts @@ -4,14 +4,14 @@ import { PoolType } from './pool-type.interface'; import { LiquidityConcern, SpotPriceConcern } from './concerns/types'; export class Weighted implements PoolType { - public liquidityCalculator: LiquidityConcern; - public spotPriceCalculator: SpotPriceConcern; + public liquidityCalculator: LiquidityConcern; + public spotPriceCalculator: SpotPriceConcern; - constructor( - private liquidityCalculatorConcern = WeightedPoolLiquidity, - private spotPriceCalculatorConcern = WeightedPoolSpotPrice - ) { - this.liquidityCalculator = new this.liquidityCalculatorConcern(); - this.spotPriceCalculator = new this.spotPriceCalculatorConcern(); - } + constructor( + private liquidityCalculatorConcern = WeightedPoolLiquidity, + private spotPriceCalculatorConcern = WeightedPoolSpotPrice + ) { + this.liquidityCalculator = new this.liquidityCalculatorConcern(); + this.spotPriceCalculator = new this.spotPriceCalculatorConcern(); + } } diff --git a/balancer-js/src/modules/pools/pools.module.ts b/balancer-js/src/modules/pools/pools.module.ts index 3083d3637..26ef44047 100644 --- a/balancer-js/src/modules/pools/pools.module.ts +++ b/balancer-js/src/modules/pools/pools.module.ts @@ -8,42 +8,40 @@ import { SubgraphPoolBase } from '@balancer-labs/sor'; import { BalancerError, BalancerErrorCode } from '@/balancerErrors'; export class Pools { - constructor( - config: BalancerSdkConfig, - public weighted = new Weighted(), - public stable = new Stable(), - public metaStable = new MetaStable(), - public stablePhantom = new StablePhantom(), - public linear = new Linear() - ) {} + constructor( + config: BalancerSdkConfig, + public weighted = new Weighted(), + public stable = new Stable(), + public metaStable = new MetaStable(), + public stablePhantom = new StablePhantom(), + public linear = new Linear() + ) {} - static from( - pool: SubgraphPoolBase - ): Weighted | Stable | MetaStable | StablePhantom | Linear { - // Calculate spot price using pool type - switch (pool.poolType) { - case 'Weighted': - case 'Investment': - case 'LiquidityBootstrapping': { - return new Weighted(); - } - case 'Stable': { - return new Stable(); - } - case 'MetaStable': { - return new MetaStable(); - } - case 'StablePhantom': { - return new StablePhantom(); - } - case 'AaveLinear': - case 'ERC4626Linear': { - return new Linear(); - } - default: - throw new BalancerError( - BalancerErrorCode.UNSUPPORTED_POOL_TYPE - ); - } + static from( + pool: SubgraphPoolBase + ): Weighted | Stable | MetaStable | StablePhantom | Linear { + // Calculate spot price using pool type + switch (pool.poolType) { + case 'Weighted': + case 'Investment': + case 'LiquidityBootstrapping': { + return new Weighted(); + } + case 'Stable': { + return new Stable(); + } + case 'MetaStable': { + return new MetaStable(); + } + case 'StablePhantom': { + return new StablePhantom(); + } + case 'AaveLinear': + case 'ERC4626Linear': { + return new Linear(); + } + default: + throw new BalancerError(BalancerErrorCode.UNSUPPORTED_POOL_TYPE); } + } } diff --git a/balancer-js/src/modules/pools/types.ts b/balancer-js/src/modules/pools/types.ts index 170bcc7ca..964b8d110 100644 --- a/balancer-js/src/modules/pools/types.ts +++ b/balancer-js/src/modules/pools/types.ts @@ -1,8 +1,8 @@ export enum PoolType { - Weighted = 'Weighted', - Investment = 'Investment', - Stable = 'Stable', - MetaStable = 'MetaStable', - StablePhantom = 'StablePhantom', - LiquidityBootstrapping = 'LiquidityBootstrapping', + Weighted = 'Weighted', + Investment = 'Investment', + Stable = 'Stable', + MetaStable = 'MetaStable', + StablePhantom = 'StablePhantom', + LiquidityBootstrapping = 'LiquidityBootstrapping', } diff --git a/balancer-js/src/modules/pricing/pricing.module.spec.ts b/balancer-js/src/modules/pricing/pricing.module.spec.ts index 6960fb236..a31a7ce08 100644 --- a/balancer-js/src/modules/pricing/pricing.module.spec.ts +++ b/balancer-js/src/modules/pricing/pricing.module.spec.ts @@ -1,10 +1,10 @@ import dotenv from 'dotenv'; import { expect } from 'chai'; import { - BalancerSdkConfig, - BalancerSdkSorConfig, - Network, - BalancerSDK, + BalancerSdkConfig, + BalancerSdkSorConfig, + Network, + BalancerSDK, } from '@/.'; import { Pricing } from './pricing.module'; import { MockPoolDataService } from '@/test/lib/mockPool'; @@ -18,231 +18,227 @@ let sdkConfig: BalancerSdkConfig; dotenv.config(); const weth_usdc_pool_id = - '0x96646936b91d6b9d7d0c47c496afbf3d6ec7b6f8000200000000000000000019'; + '0x96646936b91d6b9d7d0c47c496afbf3d6ec7b6f8000200000000000000000019'; const weth_bal_pool_id = - '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; + '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014'; describe('pricing module', () => { - before(() => { - // Mainnet pool snapshot taken at block 14717479 - const mockPoolDataService = new MockPoolDataService( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - pools_14717479 as any + before(() => { + // Mainnet pool snapshot taken at block 14717479 + const mockPoolDataService = new MockPoolDataService( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + pools_14717479 as any + ); + + const sorConfig: BalancerSdkSorConfig = { + tokenPriceService: 'coingecko', + poolDataService: mockPoolDataService, + fetchOnChainBalances: false, + }; + + sdkConfig = { + network: Network.MAINNET, + rpcUrl: ``, + sor: sorConfig, + }; + }); + + context('instantiation', () => { + it('instantiate via module', async () => { + const pricing = new Pricing(sdkConfig); + await pricing.fetchPools(); + const pools = pricing.getPools(); + expect(pools).to.deep.eq(pools_14717479); + }); + + it('instantiate via SDK', async () => { + const balancer = new BalancerSDK(sdkConfig); + await balancer.pricing.fetchPools(); + const pools = balancer.pricing.getPools(); + expect(pools).to.deep.eq(pools_14717479); + }); + }); + + context('spot price for pool', () => { + describe('via module', () => { + it('should fetch pools from poolDataService if no pools passed as param', async () => { + const pricing = new Pricing(sdkConfig); + const sp = await pricing.getSpotPrice( + ADDRESSES[Network.MAINNET].WETH.address, + ADDRESSES[Network.MAINNET].USDC.address, + weth_usdc_pool_id ); + expect(sp).to.deep.eq('0.0003423365526722167'); + }); + + it('should fetch pools from poolDataService if empty pools passed as param', async () => { + const pricing = new Pricing(sdkConfig); + const sp = await pricing.getSpotPrice( + ADDRESSES[Network.MAINNET].USDC.address, + ADDRESSES[Network.MAINNET].WETH.address, + weth_usdc_pool_id, + [] + ); + expect(sp).to.deep.eq('2925.488620398681'); + }); + it('should throw if poolDataService returns no pools', async () => { + const emptyPoolDataService = new MockPoolDataService([]); const sorConfig: BalancerSdkSorConfig = { - tokenPriceService: 'coingecko', - poolDataService: mockPoolDataService, - fetchOnChainBalances: false, + tokenPriceService: 'coingecko', + poolDataService: emptyPoolDataService, + fetchOnChainBalances: false, }; - - sdkConfig = { - network: Network.MAINNET, - rpcUrl: ``, - sor: sorConfig, + const sdkConfig: BalancerSdkConfig = { + network: Network.MAINNET, + rpcUrl: ``, + sor: sorConfig, }; + const balancer = new BalancerSDK(sdkConfig); + let error = null; + try { + await balancer.pricing.getSpotPrice( + ADDRESSES[Network.MAINNET].WETH.address, + ADDRESSES[Network.MAINNET].USDC.address, + weth_usdc_pool_id + ); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (err: any) { + error = err.message; + } + expect(error).to.eq( + BalancerError.getMessage(BalancerErrorCode.POOL_DOESNT_EXIST) + ); + }); + + it('should throw with unsupported pool type', async () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const nonValidPool = { ...(pools_14717479[0] as any) }; + nonValidPool.poolType = 'UnsupportedPool'; + + const balancer = new BalancerSDK(sdkConfig); + let error = null; + try { + await balancer.pricing.getSpotPrice( + ADDRESSES[Network.MAINNET].WETH.address, + ADDRESSES[Network.MAINNET].BAL.address, + pools_14717479[0].id, + [nonValidPool] + ); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (err: any) { + error = err.message; + } + expect(error).to.eq( + BalancerError.getMessage(BalancerErrorCode.UNSUPPORTED_POOL_TYPE) + ); + }); + }); + describe('via SDK', () => { + it('should fetch pools with no pools data param', async () => { + const balancer = new BalancerSDK(sdkConfig); + const sp = await balancer.pricing.getSpotPrice( + ADDRESSES[Network.MAINNET].WETH.address, + ADDRESSES[Network.MAINNET].BAL.address, + weth_bal_pool_id + ); + expect(sp).to.deep.eq('0.004981212133448337'); + }); }); + }); + + context('spot price without pool - finds most liquid path', () => { + describe('via module', () => { + it('should throw for pair with no liquidity', async () => { + let error = null; + try { + const pricing = new Pricing(sdkConfig); + await pricing.getSpotPrice('', ''); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (err: any) { + error = err.message; + } + expect(error).to.eq( + BalancerError.getMessage(BalancerErrorCode.UNSUPPORTED_PAIR) + ); + }); - context('instantiation', () => { - it('instantiate via module', async () => { - const pricing = new Pricing(sdkConfig); - await pricing.fetchPools(); - const pools = pricing.getPools(); - expect(pools).to.deep.eq(pools_14717479); - }); - - it('instantiate via SDK', async () => { - const balancer = new BalancerSDK(sdkConfig); - await balancer.pricing.fetchPools(); - const pools = balancer.pricing.getPools(); - expect(pools).to.deep.eq(pools_14717479); - }); + it('should fetch pools with no pools data param', async () => { + const pricing = new Pricing(sdkConfig); + const sp = await pricing.getSpotPrice( + ADDRESSES[Network.MAINNET].WETH.address, + ADDRESSES[Network.MAINNET].USDC.address + ); + expect(sp).to.deep.eq('0.0003423365526722167'); + }); + + it('should fetch pools with no pools data param', async () => { + const pricing = new Pricing(sdkConfig); + const sp = await pricing.getSpotPrice( + ADDRESSES[Network.MAINNET].USDC.address, + ADDRESSES[Network.MAINNET].WETH.address + ); + expect(sp).to.deep.eq('2925.488620398681'); + }); }); - context('spot price for pool', () => { - describe('via module', () => { - it('should fetch pools from poolDataService if no pools passed as param', async () => { - const pricing = new Pricing(sdkConfig); - const sp = await pricing.getSpotPrice( - ADDRESSES[Network.MAINNET].WETH.address, - ADDRESSES[Network.MAINNET].USDC.address, - weth_usdc_pool_id - ); - expect(sp).to.deep.eq('0.0003423365526722167'); - }); - - it('should fetch pools from poolDataService if empty pools passed as param', async () => { - const pricing = new Pricing(sdkConfig); - const sp = await pricing.getSpotPrice( - ADDRESSES[Network.MAINNET].USDC.address, - ADDRESSES[Network.MAINNET].WETH.address, - weth_usdc_pool_id, - [] - ); - expect(sp).to.deep.eq('2925.488620398681'); - }); - - it('should throw if poolDataService returns no pools', async () => { - const emptyPoolDataService = new MockPoolDataService([]); - const sorConfig: BalancerSdkSorConfig = { - tokenPriceService: 'coingecko', - poolDataService: emptyPoolDataService, - fetchOnChainBalances: false, - }; - const sdkConfig: BalancerSdkConfig = { - network: Network.MAINNET, - rpcUrl: ``, - sor: sorConfig, - }; - const balancer = new BalancerSDK(sdkConfig); - let error = null; - try { - await balancer.pricing.getSpotPrice( - ADDRESSES[Network.MAINNET].WETH.address, - ADDRESSES[Network.MAINNET].USDC.address, - weth_usdc_pool_id - ); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (err: any) { - error = err.message; - } - expect(error).to.eq( - BalancerError.getMessage( - BalancerErrorCode.POOL_DOESNT_EXIST - ) - ); - }); - - it('should throw with unsupported pool type', async () => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const nonValidPool = { ...(pools_14717479[0] as any) }; - nonValidPool.poolType = 'UnsupportedPool'; - - const balancer = new BalancerSDK(sdkConfig); - let error = null; - try { - await balancer.pricing.getSpotPrice( - ADDRESSES[Network.MAINNET].WETH.address, - ADDRESSES[Network.MAINNET].BAL.address, - pools_14717479[0].id, - [nonValidPool] - ); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (err: any) { - error = err.message; - } - expect(error).to.eq( - BalancerError.getMessage( - BalancerErrorCode.UNSUPPORTED_POOL_TYPE - ) - ); - }); - }); - describe('via SDK', () => { - it('should fetch pools with no pools data param', async () => { - const balancer = new BalancerSDK(sdkConfig); - const sp = await balancer.pricing.getSpotPrice( - ADDRESSES[Network.MAINNET].WETH.address, - ADDRESSES[Network.MAINNET].BAL.address, - weth_bal_pool_id - ); - expect(sp).to.deep.eq('0.004981212133448337'); - }); - }); + describe('via SDK', () => { + it('should fetch pools with no pools data param', async () => { + const balancer = new BalancerSDK(sdkConfig); + const sp = await balancer.pricing.getSpotPrice( + ADDRESSES[Network.MAINNET].WETH.address, + ADDRESSES[Network.MAINNET].USDC.address + ); + expect(sp).to.deep.eq('0.0003423365526722167'); + }); + }); + + describe('stable pool', () => { + it('should fetch correct sp', async () => { + const balancer = new BalancerSDK(sdkConfig); + const sp = await balancer.pricing.getSpotPrice( + ADDRESSES[Network.MAINNET].DAI.address, + ADDRESSES[Network.MAINNET].USDC.address, + '0x06df3b2bbb68adc8b0e302443692037ed9f91b42000000000000000000000063' + ); + expect(sp).to.deep.eq('1.000051911328148725'); + }); }); - context('spot price without pool - finds most liquid path', () => { - describe('via module', () => { - it('should throw for pair with no liquidity', async () => { - let error = null; - try { - const pricing = new Pricing(sdkConfig); - await pricing.getSpotPrice('', ''); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } catch (err: any) { - error = err.message; - } - expect(error).to.eq( - BalancerError.getMessage(BalancerErrorCode.UNSUPPORTED_PAIR) - ); - }); - - it('should fetch pools with no pools data param', async () => { - const pricing = new Pricing(sdkConfig); - const sp = await pricing.getSpotPrice( - ADDRESSES[Network.MAINNET].WETH.address, - ADDRESSES[Network.MAINNET].USDC.address - ); - expect(sp).to.deep.eq('0.0003423365526722167'); - }); - - it('should fetch pools with no pools data param', async () => { - const pricing = new Pricing(sdkConfig); - const sp = await pricing.getSpotPrice( - ADDRESSES[Network.MAINNET].USDC.address, - ADDRESSES[Network.MAINNET].WETH.address - ); - expect(sp).to.deep.eq('2925.488620398681'); - }); - }); - - describe('via SDK', () => { - it('should fetch pools with no pools data param', async () => { - const balancer = new BalancerSDK(sdkConfig); - const sp = await balancer.pricing.getSpotPrice( - ADDRESSES[Network.MAINNET].WETH.address, - ADDRESSES[Network.MAINNET].USDC.address - ); - expect(sp).to.deep.eq('0.0003423365526722167'); - }); - }); - - describe('stable pool', () => { - it('should fetch correct sp', async () => { - const balancer = new BalancerSDK(sdkConfig); - const sp = await balancer.pricing.getSpotPrice( - ADDRESSES[Network.MAINNET].DAI.address, - ADDRESSES[Network.MAINNET].USDC.address, - '0x06df3b2bbb68adc8b0e302443692037ed9f91b42000000000000000000000063' - ); - expect(sp).to.deep.eq('1.000051911328148725'); - }); - }); - - describe('metastable pool', () => { - it('should fetch correct sp', async () => { - const balancer = new BalancerSDK(sdkConfig); - const sp = await balancer.pricing.getSpotPrice( - ADDRESSES[Network.MAINNET].WETH.address, - ADDRESSES[Network.MAINNET].wSTETH.address, - '0x32296969ef14eb0c6d29669c550d4a0449130230000200000000000000000080' - ); - expect(sp).to.deep.eq('1.070497605163895290828158545877174735'); - }); - }); - - describe('phantomstable pool', () => { - it('should fetch correct sp', async () => { - const balancer = new BalancerSDK(sdkConfig); - const sp = await balancer.pricing.getSpotPrice( - ADDRESSES[Network.MAINNET].bbausd.address, - ADDRESSES[Network.MAINNET].bbausdc.address, - '0x7b50775383d3d6f0215a8f290f2c9e2eebbeceb20000000000000000000000fe' - ); - expect(sp).to.deep.eq('0.997873677414938406552928560423740375'); - }); - }); - - describe('linear pool', () => { - it('should fetch correct sp', async () => { - const balancer = new BalancerSDK(sdkConfig); - const sp = await balancer.pricing.getSpotPrice( - ADDRESSES[Network.MAINNET].USDC.address, - ADDRESSES[Network.MAINNET].bbausdc.address, - '0x9210f1204b5a24742eba12f710636d76240df3d00000000000000000000000fc' - ); - expect(sp).to.deep.eq('1.008078200925769181'); - }); - }); + describe('metastable pool', () => { + it('should fetch correct sp', async () => { + const balancer = new BalancerSDK(sdkConfig); + const sp = await balancer.pricing.getSpotPrice( + ADDRESSES[Network.MAINNET].WETH.address, + ADDRESSES[Network.MAINNET].wSTETH.address, + '0x32296969ef14eb0c6d29669c550d4a0449130230000200000000000000000080' + ); + expect(sp).to.deep.eq('1.070497605163895290828158545877174735'); + }); + }); + + describe('phantomstable pool', () => { + it('should fetch correct sp', async () => { + const balancer = new BalancerSDK(sdkConfig); + const sp = await balancer.pricing.getSpotPrice( + ADDRESSES[Network.MAINNET].bbausd.address, + ADDRESSES[Network.MAINNET].bbausdc.address, + '0x7b50775383d3d6f0215a8f290f2c9e2eebbeceb20000000000000000000000fe' + ); + expect(sp).to.deep.eq('0.997873677414938406552928560423740375'); + }); + }); + + describe('linear pool', () => { + it('should fetch correct sp', async () => { + const balancer = new BalancerSDK(sdkConfig); + const sp = await balancer.pricing.getSpotPrice( + ADDRESSES[Network.MAINNET].USDC.address, + ADDRESSES[Network.MAINNET].bbausdc.address, + '0x9210f1204b5a24742eba12f710636d76240df3d00000000000000000000000fc' + ); + expect(sp).to.deep.eq('1.008078200925769181'); + }); }); + }); }); diff --git a/balancer-js/src/modules/pricing/pricing.module.ts b/balancer-js/src/modules/pricing/pricing.module.ts index 0ae65de75..b986e9733 100644 --- a/balancer-js/src/modules/pricing/pricing.module.ts +++ b/balancer-js/src/modules/pricing/pricing.module.ts @@ -1,92 +1,91 @@ import { Swaps } from '@/modules/swaps/swaps.module'; import { BalancerSdkConfig } from '@/types'; import { - SubgraphPoolBase, - ZERO, - parseToPoolsDict, - getSpotPriceAfterSwapForPath, + SubgraphPoolBase, + ZERO, + parseToPoolsDict, + getSpotPriceAfterSwapForPath, } from '@balancer-labs/sor'; import { BalancerError, BalancerErrorCode } from '@/balancerErrors'; import { Pools } from '@/modules/pools/pools.module'; export class Pricing { - private readonly swaps: Swaps; - private pools: Pools; + private readonly swaps: Swaps; + private pools: Pools; - constructor(config: BalancerSdkConfig, swaps?: Swaps) { - if (swaps) { - this.swaps = swaps; - } else { - this.swaps = new Swaps(config); - } - this.pools = new Pools(config); + constructor(config: BalancerSdkConfig, swaps?: Swaps) { + if (swaps) { + this.swaps = swaps; + } else { + this.swaps = new Swaps(config); } + this.pools = new Pools(config); + } - /** - * Retrieves pools using poolDataService. - * @returns {boolean} Boolean indicating whether pools data was fetched correctly (true) or not (false). - */ - async fetchPools(): Promise { - return this.swaps.fetchPools(); - } + /** + * Retrieves pools using poolDataService. + * @returns {boolean} Boolean indicating whether pools data was fetched correctly (true) or not (false). + */ + async fetchPools(): Promise { + return this.swaps.fetchPools(); + } - /** - * Get currently saved pools list (fetched using fetchPools()). - * @returns {SubgraphPoolBase[]} pools list. - */ - public getPools(): SubgraphPoolBase[] { - return this.swaps.getPools(); - } + /** + * Get currently saved pools list (fetched using fetchPools()). + * @returns {SubgraphPoolBase[]} pools list. + */ + public getPools(): SubgraphPoolBase[] { + return this.swaps.getPools(); + } - /** - * Calculates Spot Price for a token pair - for specific pool if ID otherwise finds most liquid path and uses this as reference SP. - * @param { string } tokenIn Token in address. - * @param { string } tokenOut Token out address. - * @param { string } poolId Optional - if specified this pool will be used for SP calculation. - * @param { SubgraphPoolBase[] } pools Optional - Pool data. Will be fetched via dataProvider if not supplied. - * @returns { string } Spot price. - */ - async getSpotPrice( - tokenIn: string, - tokenOut: string, - poolId = '', - pools: SubgraphPoolBase[] = [] - ): Promise { - // If pools list isn't supplied fetch it from swaps data provider - if (pools.length === 0) { - await this.fetchPools(); - pools = this.getPools(); - } + /** + * Calculates Spot Price for a token pair - for specific pool if ID otherwise finds most liquid path and uses this as reference SP. + * @param { string } tokenIn Token in address. + * @param { string } tokenOut Token out address. + * @param { string } poolId Optional - if specified this pool will be used for SP calculation. + * @param { SubgraphPoolBase[] } pools Optional - Pool data. Will be fetched via dataProvider if not supplied. + * @returns { string } Spot price. + */ + async getSpotPrice( + tokenIn: string, + tokenOut: string, + poolId = '', + pools: SubgraphPoolBase[] = [] + ): Promise { + // If pools list isn't supplied fetch it from swaps data provider + if (pools.length === 0) { + await this.fetchPools(); + pools = this.getPools(); + } - // If a poolId isn't specified we find the path for the pair with the highest liquidity and use this as the ref SP - if (poolId === '') { - const poolsDict = parseToPoolsDict(pools, 0); - // This creates all paths for tokenIn>Out ordered by liquidity - const paths = - this.swaps.sor.routeProposer.getCandidatePathsFromDict( - tokenIn, - tokenOut, - 0, - poolsDict, - 4 - ); + // If a poolId isn't specified we find the path for the pair with the highest liquidity and use this as the ref SP + if (poolId === '') { + const poolsDict = parseToPoolsDict(pools, 0); + // This creates all paths for tokenIn>Out ordered by liquidity + const paths = this.swaps.sor.routeProposer.getCandidatePathsFromDict( + tokenIn, + tokenOut, + 0, + poolsDict, + 4 + ); - if (paths.length === 0) - throw new BalancerError(BalancerErrorCode.UNSUPPORTED_PAIR); - return getSpotPriceAfterSwapForPath(paths[0], 0, ZERO).toString(); - } else { - // Find pool of interest from pools list - const poolData = pools.find( - (p) => p.id.toLowerCase() === poolId.toLowerCase() - ); - if (!poolData) - throw new BalancerError(BalancerErrorCode.POOL_DOESNT_EXIST); - const pool = Pools.from(poolData); - return pool.spotPriceCalculator.calcPoolSpotPrice( - tokenIn, - tokenOut, - poolData - ); - } + if (paths.length === 0) + throw new BalancerError(BalancerErrorCode.UNSUPPORTED_PAIR); + return getSpotPriceAfterSwapForPath(paths[0], 0, ZERO).toString(); + } else { + // Find pool of interest from pools list + const poolData = pools.find( + (p) => p.id.toLowerCase() === poolId.toLowerCase() + ); + if (!poolData) + throw new BalancerError(BalancerErrorCode.POOL_DOESNT_EXIST); + const pool = Pools.from(poolData); + return pool.spotPriceCalculator.calcPoolSpotPrice( + tokenIn, + tokenOut, + poolData + ); } + } } diff --git a/balancer-js/src/modules/relayer/relayer.module.spec.ts b/balancer-js/src/modules/relayer/relayer.module.spec.ts index 3884352b8..32be914ef 100644 --- a/balancer-js/src/modules/relayer/relayer.module.spec.ts +++ b/balancer-js/src/modules/relayer/relayer.module.spec.ts @@ -1,10 +1,10 @@ import dotenv from 'dotenv'; import { expect } from 'chai'; import { - BalancerSdkConfig, - BalancerSdkSorConfig, - Network, - BalancerSDK, + BalancerSdkConfig, + BalancerSdkSorConfig, + Network, + BalancerSDK, } from '@/.'; import { Relayer } from './relayer.module'; import { mockPool, mockPoolDataService } from '@/test/lib/mockPool'; @@ -12,32 +12,32 @@ import { mockPool, mockPoolDataService } from '@/test/lib/mockPool'; dotenv.config(); const sorConfig: BalancerSdkSorConfig = { - tokenPriceService: 'coingecko', - poolDataService: mockPoolDataService, - fetchOnChainBalances: false, + tokenPriceService: 'coingecko', + poolDataService: mockPoolDataService, + fetchOnChainBalances: false, }; const sdkConfig: BalancerSdkConfig = { - network: Network.KOVAN, - rpcUrl: `https://kovan.infura.io/v3/${process.env.INFURA}`, - sor: sorConfig, + network: Network.KOVAN, + rpcUrl: `https://kovan.infura.io/v3/${process.env.INFURA}`, + sor: sorConfig, }; describe('relayer module', () => { - context('instantiation', () => { - it('instantiate via module', async () => { - const relayer = new Relayer(sdkConfig); - await relayer.fetchPools(); - const pools = relayer.getPools(); - expect(pools).to.deep.eq([mockPool]); - }); + context('instantiation', () => { + it('instantiate via module', async () => { + const relayer = new Relayer(sdkConfig); + await relayer.fetchPools(); + const pools = relayer.getPools(); + expect(pools).to.deep.eq([mockPool]); + }); - it('instantiate via SDK', async () => { - const balancer = new BalancerSDK(sdkConfig); + it('instantiate via SDK', async () => { + const balancer = new BalancerSDK(sdkConfig); - await balancer.relayer.fetchPools(); - const pools = balancer.relayer.getPools(); - expect(pools).to.deep.eq([mockPool]); - }); + await balancer.relayer.fetchPools(); + const pools = balancer.relayer.getPools(); + expect(pools).to.deep.eq([mockPool]); }); + }); }); diff --git a/balancer-js/src/modules/relayer/relayer.module.ts b/balancer-js/src/modules/relayer/relayer.module.ts index 7af33384a..e5086fff2 100644 --- a/balancer-js/src/modules/relayer/relayer.module.ts +++ b/balancer-js/src/modules/relayer/relayer.module.ts @@ -5,19 +5,19 @@ import { MaxUint256, WeiPerEther, Zero } from '@ethersproject/constants'; import { Swaps } from '@/modules/swaps/swaps.module'; import { BalancerError, BalancerErrorCode } from '@/balancerErrors'; import { - EncodeBatchSwapInput, - EncodeUnwrapAaveStaticTokenInput, - OutputReference, - EncodeExitPoolInput, - ExitAndBatchSwapInput, - ExitPoolData, + EncodeBatchSwapInput, + EncodeUnwrapAaveStaticTokenInput, + OutputReference, + EncodeExitPoolInput, + ExitAndBatchSwapInput, + ExitPoolData, } from './types'; import { TransactionData, ExitPoolRequest, BalancerSdkConfig } from '@/types'; import { - SwapType, - FundManagement, - BatchSwapStep, - FetchPoolsInput, + SwapType, + FundManagement, + BatchSwapStep, + FetchPoolsInput, } from '../swaps/types'; import { SubgraphPoolBase } from '@balancer-labs/sor'; @@ -27,471 +27,464 @@ import aaveWrappingAbi from '@/lib/abi/AaveWrapping.json'; export * from './types'; export class Relayer { - private readonly swaps: Swaps; + private readonly swaps: Swaps; - static CHAINED_REFERENCE_PREFIX = 'ba10'; + static CHAINED_REFERENCE_PREFIX = 'ba10'; - constructor(swapsOrConfig: Swaps | BalancerSdkConfig) { - if (swapsOrConfig instanceof Swaps) { - this.swaps = swapsOrConfig; - } else { - this.swaps = new Swaps(swapsOrConfig); - } + constructor(swapsOrConfig: Swaps | BalancerSdkConfig) { + if (swapsOrConfig instanceof Swaps) { + this.swaps = swapsOrConfig; + } else { + this.swaps = new Swaps(swapsOrConfig); } - - static encodeBatchSwap(params: EncodeBatchSwapInput): string { - const relayerLibrary = new Interface(relayerLibraryAbi); - - return relayerLibrary.encodeFunctionData('batchSwap', [ - params.swapType, - params.swaps, - params.assets, - params.funds, - params.limits, - params.deadline, - params.value, - params.outputReferences, - ]); - } - - static encodeExitPool(params: EncodeExitPoolInput): string { - const relayerLibrary = new Interface(relayerLibraryAbi); - - return relayerLibrary.encodeFunctionData('exitPool', [ - params.poolId, - params.poolKind, - params.sender, - params.recipient, - params.exitPoolRequest, - params.outputReferences, - ]); - } - - static encodeUnwrapAaveStaticToken( - params: EncodeUnwrapAaveStaticTokenInput - ): string { - const aaveWrappingLibrary = new Interface(aaveWrappingAbi); - - return aaveWrappingLibrary.encodeFunctionData('unwrapAaveStaticToken', [ - params.staticToken, - params.sender, - params.recipient, - params.amount, - params.toUnderlying, - params.outputReferences, - ]); - } - - static toChainedReference(key: BigNumberish): BigNumber { - // The full padded prefix is 66 characters long, with 64 hex characters and the 0x prefix. - const paddedPrefix = `0x${Relayer.CHAINED_REFERENCE_PREFIX}${'0'.repeat( - 64 - Relayer.CHAINED_REFERENCE_PREFIX.length - )}`; - return BigNumber.from(paddedPrefix).add(key); - } - - static constructExitCall(params: ExitPoolData): string { - const { - assets, - minAmountsOut, - userData, - toInternalBalance, - poolId, - poolKind, - sender, - recipient, - outputReferences, - } = params; - - const exitPoolRequest: ExitPoolRequest = { - assets, - minAmountsOut, - userData, - toInternalBalance, - }; - - const exitPoolInput: EncodeExitPoolInput = { - poolId, - poolKind, - sender, - recipient, - outputReferences, - exitPoolRequest, - }; - - const exitEncoded = Relayer.encodeExitPool(exitPoolInput); - return exitEncoded; - } - - /** - * fetchPools saves updated pools data to SOR internal onChainBalanceCache. - * @param {SubgraphPoolBase[]} [poolsData=[]] If poolsData passed uses this as pools source otherwise fetches from config.subgraphUrl. - * @param {boolean} [isOnChain=true] If isOnChain is true will retrieve all required onChain data via multicall otherwise uses subgraph values. - * @returns {boolean} Boolean indicating whether pools data was fetched correctly (true) or not (false). - */ - async fetchPools(): Promise { - return this.swaps.fetchPools(); - } - - public getPools(): SubgraphPoolBase[] { - return this.swaps.getPools(); - } - - /** - * exitPoolAndBatchSwap Chains poolExit with batchSwap to final tokens. - * @param {ExitAndBatchSwapInput} params - * @param {string} exiter - Address used to exit pool. - * @param {string} swapRecipient - Address that receives final tokens. - * @param {string} poolId - Id of pool being exited. - * @param {string[]} exitTokens - Array containing addresses of tokens to receive after exiting pool. (must have the same length and order as the array returned by `getPoolTokens`.) - * @param {string} userData - Encoded exitPool data. - * @param {string[]} expectedAmountsOut - Expected amounts of exitTokens to receive when exiting pool. - * @param {string[]} finalTokensOut - Array containing the addresses of the final tokens out. - * @param {string} slippage - Slippage to be applied to swap section. i.e. 5%=50000000000000000. - * @param {FetchPoolsInput} fetchPools - Set whether SOR will fetch updated pool info. - * @returns Transaction data with calldata. Outputs.amountsOut has amounts of finalTokensOut returned. - */ - async exitPoolAndBatchSwap( - params: ExitAndBatchSwapInput - ): Promise { - const slippageAmountNegative = WeiPerEther.sub( - BigNumber.from(params.slippage) - ); - // Set min amounts out of exit pool based on slippage - const minAmountsOut = params.expectedAmountsOut.map((amt) => - BigNumber.from(amt) - .mul(slippageAmountNegative) - .div(WeiPerEther) - .toString() - ); - - // Output of exit is used as input to swaps - const outputReferences: OutputReference[] = []; - params.exitTokens.forEach((asset, i) => { - const key = Relayer.toChainedReference(i); - outputReferences.push({ - index: i, - key: key, - }); - }); - - const exitCall = Relayer.constructExitCall({ - assets: params.exitTokens, - minAmountsOut, - userData: params.userData, - toInternalBalance: true, // Creates exitPool request with exit to internal balance to save gas for following swaps - poolId: params.poolId, - poolKind: 0, // This will always be 0 to match supported Relayer types - sender: params.exiter, - recipient: params.exiter, - outputReferences: outputReferences, - exitPoolRequest: {} as ExitPoolRequest, - }); - - // Use swapsService to get swap info for exitTokens>finalTokens - // This will give batchSwap swap paths - // Amounts out will be worst case amounts - const queryResult = await this.swaps.queryBatchSwapWithSor({ - tokensIn: params.exitTokens, - tokensOut: params.finalTokensOut, - swapType: SwapType.SwapExactIn, - amounts: minAmountsOut, // Use minAmountsOut as input to swap to account for slippage - fetchPools: params.fetchPools, - }); - - // This is a safety check to avoid issues when a swap path exists with 0 value - if (queryResult.returnAmounts.includes('0')) - throw new BalancerError(BalancerErrorCode.SWAP_ZERO_RETURN_AMOUNT); - - // Update swap amounts with ref outputs from exitPool - queryResult.swaps.forEach((swap) => { - const token = queryResult.assets[swap.assetInIndex]; - const index = params.exitTokens.indexOf(token); - if (index !== -1) - swap.amount = outputReferences[index].key.toString(); - }); - - // const tempDeltas = ['10096980', '0', '0', '10199896999999482390', '0']; // Useful for debug - - // Replace tokenIn delta for swaps with amount + slippage. - // This gives tolerance for limit incase amount out of exitPool is larger min, - const slippageAmountPositive = WeiPerEther.add(params.slippage); - params.exitTokens.forEach((exitToken, i) => { - const index = queryResult.assets - .map((elem) => elem.toLowerCase()) - .indexOf(exitToken.toLowerCase()); - if (index !== -1) { - queryResult.deltas[index] = BigNumber.from( - params.expectedAmountsOut[i] - ) - .mul(slippageAmountPositive) - .div(WeiPerEther) - .toString(); - } - }); - - // Creates limit array. - // Slippage set to 0. Already accounted for as swap used amounts out of pool with worst case slippage. - const limits = Swaps.getLimitsForSlippage( - params.exitTokens, // tokensIn - params.finalTokensOut, // tokensOut - SwapType.SwapExactIn, - queryResult.deltas, // tempDeltas // Useful for debug - queryResult.assets, - '0' - ); - - // Creates fund management using internal balance as source of tokens - const funds: FundManagement = { - sender: params.exiter, - recipient: params.swapRecipient, - fromInternalBalance: true, - toInternalBalance: false, - }; - - const encodedBatchSwap = Relayer.encodeBatchSwap({ - swapType: SwapType.SwapExactIn, - swaps: queryResult.swaps, - assets: queryResult.assets, - funds: funds, - limits: limits.map((l) => l.toString()), - deadline: MaxUint256, - value: '0', - outputReferences: [], - }); - - // Return amounts from swap - const calls = [exitCall, encodedBatchSwap]; - return { - function: 'multicall', - params: calls, - outputs: { - amountsOut: queryResult.returnAmounts, - }, - }; - } - - /** - * swapUnwrapAaveStaticExactIn Finds swaps for tokenIn>wrapped Aave static tokens and chains with unwrap to underlying stable. - * @param {string[]} tokensIn - array to token addresses for swapping as tokens in. - * @param {string[]} aaveStaticTokens - array contains the addresses of the Aave static tokens that tokenIn will be swapped to. These will be unwrapped. - * @param {string[]} amountsIn - amounts to be swapped for each token in. - * @param {string[]} rates - The rate used to convert wrappedToken to underlying. - * @param {FundManagement} funds - Funding info for swap. Note - recipient should be relayer and sender should be caller. - * @param {string} slippage - Slippage to be applied to swap section. i.e. 5%=50000000000000000. - * @param {FetchPoolsInput} fetchPools - Set whether SOR will fetch updated pool info. - * @returns Transaction data with calldata. Outputs.amountsOut has final amounts out of unwrapped tokens. - */ - async swapUnwrapAaveStaticExactIn( - tokensIn: string[], - aaveStaticTokens: string[], - amountsIn: string[], - rates: string[], - funds: FundManagement, - slippage: string, - fetchPools: FetchPoolsInput = { - fetchPools: true, - fetchOnChain: false, - } - ): Promise { - // Use swapsService to get swap info for tokensIn>wrappedTokens - const queryResult = await this.swaps.queryBatchSwapWithSor({ - tokensIn, - tokensOut: aaveStaticTokens, - swapType: SwapType.SwapExactIn, - amounts: amountsIn, - fetchPools, - }); - - // This is a safety check to avoid issues when a swap path exists with 0 value - if (queryResult.returnAmounts.includes('0')) - throw new BalancerError(BalancerErrorCode.SWAP_ZERO_RETURN_AMOUNT); - - // Gets limits array for tokensIn>wrappedTokens based on input slippage - const limits = Swaps.getLimitsForSlippage( - tokensIn, // tokensIn - aaveStaticTokens, // tokensOut - SwapType.SwapExactIn, - queryResult.deltas, - queryResult.assets, - slippage - ); - - const calls = this.encodeSwapUnwrap( - aaveStaticTokens, - SwapType.SwapExactIn, - queryResult.swaps, - queryResult.assets, - funds, - limits - ); - - const amountsUnwrapped = queryResult.returnAmounts.map( - (amountWrapped, i) => { - const amountUnwrapped = BigNumber.from(amountWrapped) - .abs() - .mul(rates[i]) - .div(WeiPerEther); - - // This is a safety check to avoid issues when a swap path exists with 0 value - if (!amountUnwrapped.gt(Zero)) - throw new BalancerError( - BalancerErrorCode.UNWRAP_ZERO_AMOUNT - ); - - return amountUnwrapped.toString(); - } - ); - - return { - function: 'multicall', - params: calls, - outputs: { - amountsOut: amountsUnwrapped, - }, - }; + } + + static encodeBatchSwap(params: EncodeBatchSwapInput): string { + const relayerLibrary = new Interface(relayerLibraryAbi); + + return relayerLibrary.encodeFunctionData('batchSwap', [ + params.swapType, + params.swaps, + params.assets, + params.funds, + params.limits, + params.deadline, + params.value, + params.outputReferences, + ]); + } + + static encodeExitPool(params: EncodeExitPoolInput): string { + const relayerLibrary = new Interface(relayerLibraryAbi); + + return relayerLibrary.encodeFunctionData('exitPool', [ + params.poolId, + params.poolKind, + params.sender, + params.recipient, + params.exitPoolRequest, + params.outputReferences, + ]); + } + + static encodeUnwrapAaveStaticToken( + params: EncodeUnwrapAaveStaticTokenInput + ): string { + const aaveWrappingLibrary = new Interface(aaveWrappingAbi); + + return aaveWrappingLibrary.encodeFunctionData('unwrapAaveStaticToken', [ + params.staticToken, + params.sender, + params.recipient, + params.amount, + params.toUnderlying, + params.outputReferences, + ]); + } + + static toChainedReference(key: BigNumberish): BigNumber { + // The full padded prefix is 66 characters long, with 64 hex characters and the 0x prefix. + const paddedPrefix = `0x${Relayer.CHAINED_REFERENCE_PREFIX}${'0'.repeat( + 64 - Relayer.CHAINED_REFERENCE_PREFIX.length + )}`; + return BigNumber.from(paddedPrefix).add(key); + } + + static constructExitCall(params: ExitPoolData): string { + const { + assets, + minAmountsOut, + userData, + toInternalBalance, + poolId, + poolKind, + sender, + recipient, + outputReferences, + } = params; + + const exitPoolRequest: ExitPoolRequest = { + assets, + minAmountsOut, + userData, + toInternalBalance, + }; + + const exitPoolInput: EncodeExitPoolInput = { + poolId, + poolKind, + sender, + recipient, + outputReferences, + exitPoolRequest, + }; + + const exitEncoded = Relayer.encodeExitPool(exitPoolInput); + return exitEncoded; + } + + /** + * fetchPools saves updated pools data to SOR internal onChainBalanceCache. + * @param {SubgraphPoolBase[]} [poolsData=[]] If poolsData passed uses this as pools source otherwise fetches from config.subgraphUrl. + * @param {boolean} [isOnChain=true] If isOnChain is true will retrieve all required onChain data via multicall otherwise uses subgraph values. + * @returns {boolean} Boolean indicating whether pools data was fetched correctly (true) or not (false). + */ + async fetchPools(): Promise { + return this.swaps.fetchPools(); + } + + public getPools(): SubgraphPoolBase[] { + return this.swaps.getPools(); + } + + /** + * exitPoolAndBatchSwap Chains poolExit with batchSwap to final tokens. + * @param {ExitAndBatchSwapInput} params + * @param {string} exiter - Address used to exit pool. + * @param {string} swapRecipient - Address that receives final tokens. + * @param {string} poolId - Id of pool being exited. + * @param {string[]} exitTokens - Array containing addresses of tokens to receive after exiting pool. (must have the same length and order as the array returned by `getPoolTokens`.) + * @param {string} userData - Encoded exitPool data. + * @param {string[]} expectedAmountsOut - Expected amounts of exitTokens to receive when exiting pool. + * @param {string[]} finalTokensOut - Array containing the addresses of the final tokens out. + * @param {string} slippage - Slippage to be applied to swap section. i.e. 5%=50000000000000000. + * @param {FetchPoolsInput} fetchPools - Set whether SOR will fetch updated pool info. + * @returns Transaction data with calldata. Outputs.amountsOut has amounts of finalTokensOut returned. + */ + async exitPoolAndBatchSwap( + params: ExitAndBatchSwapInput + ): Promise { + const slippageAmountNegative = WeiPerEther.sub( + BigNumber.from(params.slippage) + ); + // Set min amounts out of exit pool based on slippage + const minAmountsOut = params.expectedAmountsOut.map((amt) => + BigNumber.from(amt) + .mul(slippageAmountNegative) + .div(WeiPerEther) + .toString() + ); + + // Output of exit is used as input to swaps + const outputReferences: OutputReference[] = []; + params.exitTokens.forEach((asset, i) => { + const key = Relayer.toChainedReference(i); + outputReferences.push({ + index: i, + key: key, + }); + }); + + const exitCall = Relayer.constructExitCall({ + assets: params.exitTokens, + minAmountsOut, + userData: params.userData, + toInternalBalance: true, // Creates exitPool request with exit to internal balance to save gas for following swaps + poolId: params.poolId, + poolKind: 0, // This will always be 0 to match supported Relayer types + sender: params.exiter, + recipient: params.exiter, + outputReferences: outputReferences, + exitPoolRequest: {} as ExitPoolRequest, + }); + + // Use swapsService to get swap info for exitTokens>finalTokens + // This will give batchSwap swap paths + // Amounts out will be worst case amounts + const queryResult = await this.swaps.queryBatchSwapWithSor({ + tokensIn: params.exitTokens, + tokensOut: params.finalTokensOut, + swapType: SwapType.SwapExactIn, + amounts: minAmountsOut, // Use minAmountsOut as input to swap to account for slippage + fetchPools: params.fetchPools, + }); + + // This is a safety check to avoid issues when a swap path exists with 0 value + if (queryResult.returnAmounts.includes('0')) + throw new BalancerError(BalancerErrorCode.SWAP_ZERO_RETURN_AMOUNT); + + // Update swap amounts with ref outputs from exitPool + queryResult.swaps.forEach((swap) => { + const token = queryResult.assets[swap.assetInIndex]; + const index = params.exitTokens.indexOf(token); + if (index !== -1) swap.amount = outputReferences[index].key.toString(); + }); + + // const tempDeltas = ['10096980', '0', '0', '10199896999999482390', '0']; // Useful for debug + + // Replace tokenIn delta for swaps with amount + slippage. + // This gives tolerance for limit incase amount out of exitPool is larger min, + const slippageAmountPositive = WeiPerEther.add(params.slippage); + params.exitTokens.forEach((exitToken, i) => { + const index = queryResult.assets + .map((elem) => elem.toLowerCase()) + .indexOf(exitToken.toLowerCase()); + if (index !== -1) { + queryResult.deltas[index] = BigNumber.from(params.expectedAmountsOut[i]) + .mul(slippageAmountPositive) + .div(WeiPerEther) + .toString(); + } + }); + + // Creates limit array. + // Slippage set to 0. Already accounted for as swap used amounts out of pool with worst case slippage. + const limits = Swaps.getLimitsForSlippage( + params.exitTokens, // tokensIn + params.finalTokensOut, // tokensOut + SwapType.SwapExactIn, + queryResult.deltas, // tempDeltas // Useful for debug + queryResult.assets, + '0' + ); + + // Creates fund management using internal balance as source of tokens + const funds: FundManagement = { + sender: params.exiter, + recipient: params.swapRecipient, + fromInternalBalance: true, + toInternalBalance: false, + }; + + const encodedBatchSwap = Relayer.encodeBatchSwap({ + swapType: SwapType.SwapExactIn, + swaps: queryResult.swaps, + assets: queryResult.assets, + funds: funds, + limits: limits.map((l) => l.toString()), + deadline: MaxUint256, + value: '0', + outputReferences: [], + }); + + // Return amounts from swap + const calls = [exitCall, encodedBatchSwap]; + return { + function: 'multicall', + params: calls, + outputs: { + amountsOut: queryResult.returnAmounts, + }, + }; + } + + /** + * swapUnwrapAaveStaticExactIn Finds swaps for tokenIn>wrapped Aave static tokens and chains with unwrap to underlying stable. + * @param {string[]} tokensIn - array to token addresses for swapping as tokens in. + * @param {string[]} aaveStaticTokens - array contains the addresses of the Aave static tokens that tokenIn will be swapped to. These will be unwrapped. + * @param {string[]} amountsIn - amounts to be swapped for each token in. + * @param {string[]} rates - The rate used to convert wrappedToken to underlying. + * @param {FundManagement} funds - Funding info for swap. Note - recipient should be relayer and sender should be caller. + * @param {string} slippage - Slippage to be applied to swap section. i.e. 5%=50000000000000000. + * @param {FetchPoolsInput} fetchPools - Set whether SOR will fetch updated pool info. + * @returns Transaction data with calldata. Outputs.amountsOut has final amounts out of unwrapped tokens. + */ + async swapUnwrapAaveStaticExactIn( + tokensIn: string[], + aaveStaticTokens: string[], + amountsIn: string[], + rates: string[], + funds: FundManagement, + slippage: string, + fetchPools: FetchPoolsInput = { + fetchPools: true, + fetchOnChain: false, } - - /** - * swapUnwrapAaveStaticExactOut Finds swaps for tokenIn>wrapped Aave static tokens and chains with unwrap to underlying stable. - * @param {string[]} tokensIn - array to token addresses for swapping as tokens in. - * @param {string[]} aaveStaticTokens - array contains the addresses of the Aave static tokens that tokenIn will be swapped to. These will be unwrapped. - * @param {string[]} amountsUnwrapped - amounts of unwrapped tokens out. - * @param {string[]} rates - The rate used to convert wrappedToken to underlying. - * @param {FundManagement} funds - Funding info for swap. Note - recipient should be relayer and sender should be caller. - * @param {string} slippage - Slippage to be applied to swap section. i.e. 5%=50000000000000000. - * @param {FetchPoolsInput} fetchPools - Set whether SOR will fetch updated pool info. - * @returns Transaction data with calldata. Outputs.amountsIn has the amounts of tokensIn. - */ - async swapUnwrapAaveStaticExactOut( - tokensIn: string[], - aaveStaticTokens: string[], - amountsUnwrapped: string[], - rates: string[], - funds: FundManagement, - slippage: string, - fetchPools: FetchPoolsInput = { - fetchPools: true, - fetchOnChain: false, - } - ): Promise { - const amountsWrapped = amountsUnwrapped.map((amountInwrapped, i) => { - const amountWrapped = BigNumber.from(amountInwrapped) - .mul(WeiPerEther) - .div(rates[i]); - - // This is a safety check to avoid issues when a swap path exists with 0 value - if (!amountWrapped.gt(Zero)) - throw new BalancerError(BalancerErrorCode.WRAP_ZERO_AMOUNT); - - return amountWrapped.toString(); - }); - - // Use swapsService to get swap info for tokensIn>wrappedTokens - const queryResult = await this.swaps.queryBatchSwapWithSor({ - tokensIn, - tokensOut: aaveStaticTokens, - swapType: SwapType.SwapExactOut, - amounts: amountsWrapped, - fetchPools, - }); + ): Promise { + // Use swapsService to get swap info for tokensIn>wrappedTokens + const queryResult = await this.swaps.queryBatchSwapWithSor({ + tokensIn, + tokensOut: aaveStaticTokens, + swapType: SwapType.SwapExactIn, + amounts: amountsIn, + fetchPools, + }); + + // This is a safety check to avoid issues when a swap path exists with 0 value + if (queryResult.returnAmounts.includes('0')) + throw new BalancerError(BalancerErrorCode.SWAP_ZERO_RETURN_AMOUNT); + + // Gets limits array for tokensIn>wrappedTokens based on input slippage + const limits = Swaps.getLimitsForSlippage( + tokensIn, // tokensIn + aaveStaticTokens, // tokensOut + SwapType.SwapExactIn, + queryResult.deltas, + queryResult.assets, + slippage + ); + + const calls = this.encodeSwapUnwrap( + aaveStaticTokens, + SwapType.SwapExactIn, + queryResult.swaps, + queryResult.assets, + funds, + limits + ); + + const amountsUnwrapped = queryResult.returnAmounts.map( + (amountWrapped, i) => { + const amountUnwrapped = BigNumber.from(amountWrapped) + .abs() + .mul(rates[i]) + .div(WeiPerEther); // This is a safety check to avoid issues when a swap path exists with 0 value - if (queryResult.returnAmounts.includes('0')) - throw new BalancerError(BalancerErrorCode.SWAP_ZERO_RETURN_AMOUNT); - - // Gets limits array for tokensIn>wrappedTokens based on input slippage - const limits = Swaps.getLimitsForSlippage( - tokensIn, // tokensIn - aaveStaticTokens, // tokensOut - SwapType.SwapExactOut, - queryResult.deltas, - queryResult.assets, - slippage - ); - - const calls = this.encodeSwapUnwrap( - aaveStaticTokens, - SwapType.SwapExactOut, - queryResult.swaps, - queryResult.assets, - funds, - limits - ); - - return { - function: 'multicall', - params: calls, - outputs: { - amountsIn: queryResult.returnAmounts.map((amount) => - amount.toString() - ), - }, - }; - } - - /** - * Creates encoded multicalls using swap outputs as input amounts for token unwrap. - * @param wrappedTokens - * @param swapType - * @param swaps - * @param assets - * @param funds - * @param limits - * @returns - */ - encodeSwapUnwrap( - wrappedTokens: string[], - swapType: SwapType, - swaps: BatchSwapStep[], - assets: string[], - funds: FundManagement, - limits: BigNumberish[] - ): string[] { - // Output of swaps (wrappedTokens) is used as input to unwrap - // Need indices of output tokens and outputReferences need to be made with those as key - const outputReferences: OutputReference[] = []; - const unwrapCalls: string[] = []; - wrappedTokens.forEach((wrappedToken, i) => { - // Find index of wrappedToken in asset array. This is used as ref in Relayer. - const index = assets.findIndex( - (token) => token.toLowerCase() === wrappedToken.toLowerCase() - ); - // There may be cases where swap isn't possible for wrappedToken - if (index === -1) return; - - const key = Relayer.toChainedReference(i); - - outputReferences.push({ - index: index, - key: key, - }); - - // console.log(`Unwrapping ${wrappedToken} with amt: ${key.toHexString()}`); - - const encodedUnwrap = Relayer.encodeUnwrapAaveStaticToken({ - staticToken: wrappedToken, - sender: funds.recipient, // This should be relayer - recipient: funds.sender, // This will be caller - amount: key, // Use output of swap as input for unwrap - toUnderlying: true, - outputReferences: 0, - }); - - unwrapCalls.push(encodedUnwrap); - }); - - const encodedBatchSwap = Relayer.encodeBatchSwap({ - swapType: swapType, - swaps: swaps, - assets: assets, - funds: funds, // Note - this should have Relayer as recipient - limits: limits.map((l) => l.toString()), - deadline: MaxUint256, - value: '0', - outputReferences: outputReferences, - }); - - return [encodedBatchSwap, ...unwrapCalls]; + if (!amountUnwrapped.gt(Zero)) + throw new BalancerError(BalancerErrorCode.UNWRAP_ZERO_AMOUNT); + + return amountUnwrapped.toString(); + } + ); + + return { + function: 'multicall', + params: calls, + outputs: { + amountsOut: amountsUnwrapped, + }, + }; + } + + /** + * swapUnwrapAaveStaticExactOut Finds swaps for tokenIn>wrapped Aave static tokens and chains with unwrap to underlying stable. + * @param {string[]} tokensIn - array to token addresses for swapping as tokens in. + * @param {string[]} aaveStaticTokens - array contains the addresses of the Aave static tokens that tokenIn will be swapped to. These will be unwrapped. + * @param {string[]} amountsUnwrapped - amounts of unwrapped tokens out. + * @param {string[]} rates - The rate used to convert wrappedToken to underlying. + * @param {FundManagement} funds - Funding info for swap. Note - recipient should be relayer and sender should be caller. + * @param {string} slippage - Slippage to be applied to swap section. i.e. 5%=50000000000000000. + * @param {FetchPoolsInput} fetchPools - Set whether SOR will fetch updated pool info. + * @returns Transaction data with calldata. Outputs.amountsIn has the amounts of tokensIn. + */ + async swapUnwrapAaveStaticExactOut( + tokensIn: string[], + aaveStaticTokens: string[], + amountsUnwrapped: string[], + rates: string[], + funds: FundManagement, + slippage: string, + fetchPools: FetchPoolsInput = { + fetchPools: true, + fetchOnChain: false, } + ): Promise { + const amountsWrapped = amountsUnwrapped.map((amountInwrapped, i) => { + const amountWrapped = BigNumber.from(amountInwrapped) + .mul(WeiPerEther) + .div(rates[i]); + + // This is a safety check to avoid issues when a swap path exists with 0 value + if (!amountWrapped.gt(Zero)) + throw new BalancerError(BalancerErrorCode.WRAP_ZERO_AMOUNT); + + return amountWrapped.toString(); + }); + + // Use swapsService to get swap info for tokensIn>wrappedTokens + const queryResult = await this.swaps.queryBatchSwapWithSor({ + tokensIn, + tokensOut: aaveStaticTokens, + swapType: SwapType.SwapExactOut, + amounts: amountsWrapped, + fetchPools, + }); + + // This is a safety check to avoid issues when a swap path exists with 0 value + if (queryResult.returnAmounts.includes('0')) + throw new BalancerError(BalancerErrorCode.SWAP_ZERO_RETURN_AMOUNT); + + // Gets limits array for tokensIn>wrappedTokens based on input slippage + const limits = Swaps.getLimitsForSlippage( + tokensIn, // tokensIn + aaveStaticTokens, // tokensOut + SwapType.SwapExactOut, + queryResult.deltas, + queryResult.assets, + slippage + ); + + const calls = this.encodeSwapUnwrap( + aaveStaticTokens, + SwapType.SwapExactOut, + queryResult.swaps, + queryResult.assets, + funds, + limits + ); + + return { + function: 'multicall', + params: calls, + outputs: { + amountsIn: queryResult.returnAmounts.map((amount) => amount.toString()), + }, + }; + } + + /** + * Creates encoded multicalls using swap outputs as input amounts for token unwrap. + * @param wrappedTokens + * @param swapType + * @param swaps + * @param assets + * @param funds + * @param limits + * @returns + */ + encodeSwapUnwrap( + wrappedTokens: string[], + swapType: SwapType, + swaps: BatchSwapStep[], + assets: string[], + funds: FundManagement, + limits: BigNumberish[] + ): string[] { + // Output of swaps (wrappedTokens) is used as input to unwrap + // Need indices of output tokens and outputReferences need to be made with those as key + const outputReferences: OutputReference[] = []; + const unwrapCalls: string[] = []; + wrappedTokens.forEach((wrappedToken, i) => { + // Find index of wrappedToken in asset array. This is used as ref in Relayer. + const index = assets.findIndex( + (token) => token.toLowerCase() === wrappedToken.toLowerCase() + ); + // There may be cases where swap isn't possible for wrappedToken + if (index === -1) return; + + const key = Relayer.toChainedReference(i); + + outputReferences.push({ + index: index, + key: key, + }); + + // console.log(`Unwrapping ${wrappedToken} with amt: ${key.toHexString()}`); + + const encodedUnwrap = Relayer.encodeUnwrapAaveStaticToken({ + staticToken: wrappedToken, + sender: funds.recipient, // This should be relayer + recipient: funds.sender, // This will be caller + amount: key, // Use output of swap as input for unwrap + toUnderlying: true, + outputReferences: 0, + }); + + unwrapCalls.push(encodedUnwrap); + }); + + const encodedBatchSwap = Relayer.encodeBatchSwap({ + swapType: swapType, + swaps: swaps, + assets: assets, + funds: funds, // Note - this should have Relayer as recipient + limits: limits.map((l) => l.toString()), + deadline: MaxUint256, + value: '0', + outputReferences: outputReferences, + }); + + return [encodedBatchSwap, ...unwrapCalls]; + } } diff --git a/balancer-js/src/modules/relayer/types.ts b/balancer-js/src/modules/relayer/types.ts index 6d8d10cd4..9bf072316 100644 --- a/balancer-js/src/modules/relayer/types.ts +++ b/balancer-js/src/modules/relayer/types.ts @@ -2,56 +2,56 @@ import { BigNumber, BigNumberish } from '@ethersproject/bignumber'; import { ExitPoolRequest } from '@/types'; import { - SwapType, - BatchSwapStep, - FundManagement, - FetchPoolsInput, + SwapType, + BatchSwapStep, + FundManagement, + FetchPoolsInput, } from '@/modules/swaps/types'; export type OutputReference = { - index: number; - key: BigNumber; + index: number; + key: BigNumber; }; export interface EncodeBatchSwapInput { - swapType: SwapType; - swaps: BatchSwapStep[]; - assets: string[]; - funds: FundManagement; - limits: string[]; - deadline: BigNumberish; - value: BigNumberish; - outputReferences: OutputReference[]; + swapType: SwapType; + swaps: BatchSwapStep[]; + assets: string[]; + funds: FundManagement; + limits: string[]; + deadline: BigNumberish; + value: BigNumberish; + outputReferences: OutputReference[]; } export interface EncodeExitPoolInput { - poolId: string; - poolKind: number; - sender: string; - recipient: string; - outputReferences: OutputReference[]; - exitPoolRequest: ExitPoolRequest; + poolId: string; + poolKind: number; + sender: string; + recipient: string; + outputReferences: OutputReference[]; + exitPoolRequest: ExitPoolRequest; } export interface EncodeUnwrapAaveStaticTokenInput { - staticToken: string; - sender: string; - recipient: string; - amount: BigNumberish; - toUnderlying: boolean; - outputReferences: BigNumberish; + staticToken: string; + sender: string; + recipient: string; + amount: BigNumberish; + toUnderlying: boolean; + outputReferences: BigNumberish; } export interface ExitAndBatchSwapInput { - exiter: string; - swapRecipient: string; - poolId: string; - exitTokens: string[]; - userData: string; - expectedAmountsOut: string[]; - finalTokensOut: string[]; - slippage: string; - fetchPools: FetchPoolsInput; + exiter: string; + swapRecipient: string; + poolId: string; + exitTokens: string[]; + userData: string; + expectedAmountsOut: string[]; + finalTokensOut: string[]; + slippage: string; + fetchPools: FetchPoolsInput; } export type ExitPoolData = ExitPoolRequest & EncodeExitPoolInput; diff --git a/balancer-js/src/modules/sdk.helpers.ts b/balancer-js/src/modules/sdk.helpers.ts index 2ce4d3bb0..038d0ecbc 100644 --- a/balancer-js/src/modules/sdk.helpers.ts +++ b/balancer-js/src/modules/sdk.helpers.ts @@ -2,26 +2,25 @@ import { BALANCER_NETWORK_CONFIG } from '@/lib/constants/config'; import { BalancerNetworkConfig, BalancerSdkConfig } from '@/types'; export function getNetworkConfig( - config: BalancerSdkConfig + config: BalancerSdkConfig ): BalancerNetworkConfig { - if (typeof config.network === 'number') { - const networkConfig = BALANCER_NETWORK_CONFIG[config.network]; - - return { - ...networkConfig, - urls: { - ...networkConfig.urls, - subgraph: - config.customSubgraphUrl ?? networkConfig.urls.subgraph, - }, - }; - } + if (typeof config.network === 'number') { + const networkConfig = BALANCER_NETWORK_CONFIG[config.network]; return { - ...config.network, - urls: { - ...config.network.urls, - subgraph: config.customSubgraphUrl ?? config.network.urls.subgraph, - }, + ...networkConfig, + urls: { + ...networkConfig.urls, + subgraph: config.customSubgraphUrl ?? networkConfig.urls.subgraph, + }, }; + } + + return { + ...config.network, + urls: { + ...config.network.urls, + subgraph: config.customSubgraphUrl ?? config.network.urls.subgraph, + }, + }; } diff --git a/balancer-js/src/modules/sdk.module.ts b/balancer-js/src/modules/sdk.module.ts index 3fad7b602..cc86e2a58 100644 --- a/balancer-js/src/modules/sdk.module.ts +++ b/balancer-js/src/modules/sdk.module.ts @@ -7,23 +7,33 @@ import { getNetworkConfig } from './sdk.helpers'; import { Pools } from './pools/pools.module'; import { Pricing } from './pricing/pricing.module'; -export class BalancerSDK { - public readonly swaps: Swaps; - public readonly relayer: Relayer; - public readonly pricing: Pricing; +export interface BalancerSDKRoot { + config: BalancerSdkConfig; + sor: Sor; + subgraph: Subgraph; + pools: Pools; + swaps: Swaps; + relayer: Relayer; + networkConfig: BalancerNetworkConfig; +} + +export class BalancerSDK implements BalancerSDKRoot { + readonly swaps: Swaps; + readonly relayer: Relayer; + readonly pricing: Pricing; - constructor( - public config: BalancerSdkConfig, - public sor = new Sor(config), - public subgraph = new Subgraph(config), - public pools = new Pools(config) - ) { - this.swaps = new Swaps(this.sor); - this.relayer = new Relayer(this.swaps); - this.pricing = new Pricing(config, this.swaps); - } + constructor( + public config: BalancerSdkConfig, + public sor = new Sor(config), + public subgraph = new Subgraph(config), + public pools = new Pools(config) + ) { + this.swaps = new Swaps(this.config); + this.relayer = new Relayer(this.swaps); + this.pricing = new Pricing(config, this.swaps); + } - public get networkConfig(): BalancerNetworkConfig { - return getNetworkConfig(this.config); - } + get networkConfig(): BalancerNetworkConfig { + return getNetworkConfig(this.config); + } } diff --git a/balancer-js/src/modules/sor/pool-data/onChainData.ts b/balancer-js/src/modules/sor/pool-data/onChainData.ts index 3040dc563..b2a527d51 100644 --- a/balancer-js/src/modules/sor/pool-data/onChainData.ts +++ b/balancer-js/src/modules/sor/pool-data/onChainData.ts @@ -13,200 +13,192 @@ import elementPoolAbi from '@/lib/abi/ConvergentCurvePool.json'; import linearPoolAbi from '@/lib/abi/LinearPool.json'; export async function getOnChainBalances( - subgraphPoolsOriginal: SubgraphPoolBase[], - multiAddress: string, - vaultAddress: string, - provider: Provider + subgraphPoolsOriginal: SubgraphPoolBase[], + multiAddress: string, + vaultAddress: string, + provider: Provider ): Promise { - if (subgraphPoolsOriginal.length === 0) return subgraphPoolsOriginal; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const abis: any = Object.values( - // Remove duplicate entries using their names - Object.fromEntries( - [ - ...vaultAbi, - ...aTokenRateProvider, - ...weightedPoolAbi, - ...stablePoolAbi, - ...elementPoolAbi, - ...linearPoolAbi, - ].map((row) => [row.name, row]) - ) - ); - - const multiPool = new Multicaller(multiAddress, provider, abis); - - const supportedPoolTypes: string[] = Object.values(PoolFilter); - const subgraphPools: SubgraphPoolBase[] = []; - subgraphPoolsOriginal.forEach((pool) => { - if (!supportedPoolTypes.includes(pool.poolType)) { - console.error(`Unknown pool type: ${pool.poolType} ${pool.id}`); - return; - } + if (subgraphPoolsOriginal.length === 0) return subgraphPoolsOriginal; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const abis: any = Object.values( + // Remove duplicate entries using their names + Object.fromEntries( + [ + ...vaultAbi, + ...aTokenRateProvider, + ...weightedPoolAbi, + ...stablePoolAbi, + ...elementPoolAbi, + ...linearPoolAbi, + ].map((row) => [row.name, row]) + ) + ); + + const multiPool = new Multicaller(multiAddress, provider, abis); + + const supportedPoolTypes: string[] = Object.values(PoolFilter); + const subgraphPools: SubgraphPoolBase[] = []; + subgraphPoolsOriginal.forEach((pool) => { + if (!supportedPoolTypes.includes(pool.poolType)) { + console.error(`Unknown pool type: ${pool.poolType} ${pool.id}`); + return; + } - subgraphPools.push(pool); - - multiPool.call(`${pool.id}.poolTokens`, vaultAddress, 'getPoolTokens', [ - pool.id, - ]); - multiPool.call(`${pool.id}.totalSupply`, pool.address, 'totalSupply'); - - // TO DO - Make this part of class to make more flexible? - if ( - pool.poolType === 'Weighted' || - pool.poolType === 'LiquidityBootstrapping' || - pool.poolType === 'Investment' - ) { - multiPool.call( - `${pool.id}.weights`, - pool.address, - 'getNormalizedWeights' - ); - multiPool.call( - `${pool.id}.swapFee`, - pool.address, - 'getSwapFeePercentage' - ); - } else if ( - pool.poolType === 'Stable' || - pool.poolType === 'MetaStable' || - pool.poolType === 'StablePhantom' - ) { - // MetaStable & StablePhantom is the same as Stable for multicall purposes - multiPool.call( - `${pool.id}.amp`, - pool.address, - 'getAmplificationParameter' - ); - multiPool.call( - `${pool.id}.swapFee`, - pool.address, - 'getSwapFeePercentage' - ); - } else if (pool.poolType === 'Element') { - multiPool.call(`${pool.id}.swapFee`, pool.address, 'percentFee'); - } else if (pool.poolType.toString().includes('Linear')) { - multiPool.call( - `${pool.id}.swapFee`, - pool.address, - 'getSwapFeePercentage' - ); - - multiPool.call(`${pool.id}.targets`, pool.address, 'getTargets'); - multiPool.call( - `${pool.id}.rate`, - pool.address, - 'getWrappedTokenRate' - ); - } - }); - - let pools = {} as Record< - string, - { - amp?: string[]; - swapFee: string; - weights?: string[]; - targets?: string[]; - poolTokens: { - tokens: string[]; - balances: string[]; - }; - rate?: string; - } + subgraphPools.push(pool); + + multiPool.call(`${pool.id}.poolTokens`, vaultAddress, 'getPoolTokens', [ + pool.id, + ]); + multiPool.call(`${pool.id}.totalSupply`, pool.address, 'totalSupply'); + + // TO DO - Make this part of class to make more flexible? + if ( + pool.poolType === 'Weighted' || + pool.poolType === 'LiquidityBootstrapping' || + pool.poolType === 'Investment' + ) { + multiPool.call( + `${pool.id}.weights`, + pool.address, + 'getNormalizedWeights' + ); + multiPool.call( + `${pool.id}.swapFee`, + pool.address, + 'getSwapFeePercentage' + ); + } else if ( + pool.poolType === 'Stable' || + pool.poolType === 'MetaStable' || + pool.poolType === 'StablePhantom' + ) { + // MetaStable & StablePhantom is the same as Stable for multicall purposes + multiPool.call( + `${pool.id}.amp`, + pool.address, + 'getAmplificationParameter' + ); + multiPool.call( + `${pool.id}.swapFee`, + pool.address, + 'getSwapFeePercentage' + ); + } else if (pool.poolType === 'Element') { + multiPool.call(`${pool.id}.swapFee`, pool.address, 'percentFee'); + } else if (pool.poolType.toString().includes('Linear')) { + multiPool.call( + `${pool.id}.swapFee`, + pool.address, + 'getSwapFeePercentage' + ); + + multiPool.call(`${pool.id}.targets`, pool.address, 'getTargets'); + multiPool.call(`${pool.id}.rate`, pool.address, 'getWrappedTokenRate'); + } + }); + + let pools = {} as Record< + string, + { + amp?: string[]; + swapFee: string; + weights?: string[]; + targets?: string[]; + poolTokens: { + tokens: string[]; + balances: string[]; + }; + rate?: string; + } + >; + + try { + pools = (await multiPool.execute()) as Record< + string, + { + amp?: string[]; + swapFee: string; + weights?: string[]; + poolTokens: { + tokens: string[]; + balances: string[]; + }; + rate?: string; + } >; + } catch (err) { + throw `Issue with multicall execution.`; + } + + const onChainPools: SubgraphPoolBase[] = []; + Object.entries(pools).forEach(([poolId, onchainData], index) => { try { - pools = (await multiPool.execute()) as Record< - string, - { - amp?: string[]; - swapFee: string; - weights?: string[]; - poolTokens: { - tokens: string[]; - balances: string[]; - }; - rate?: string; - } - >; - } catch (err) { - throw `Issue with multicall execution.`; - } + const { poolTokens, swapFee, weights } = onchainData; + + if ( + subgraphPools[index].poolType === 'Stable' || + subgraphPools[index].poolType === 'MetaStable' || + subgraphPools[index].poolType === 'StablePhantom' + ) { + if (!onchainData.amp) { + console.error(`Stable Pool Missing Amp: ${poolId}`); + return; + } else { + // Need to scale amp by precision to match expected Subgraph scale + // amp is stored with 3 decimals of precision + subgraphPools[index].amp = formatFixed(onchainData.amp[0], 3); + } + } + + if (subgraphPools[index].poolType.includes('Linear')) { + if (!onchainData.targets) { + console.error(`Linear Pool Missing Targets: ${poolId}`); + return; + } else { + subgraphPools[index].lowerTarget = formatFixed( + onchainData.targets[0], + 18 + ); + subgraphPools[index].upperTarget = formatFixed( + onchainData.targets[1], + 18 + ); + } - const onChainPools: SubgraphPoolBase[] = []; - - Object.entries(pools).forEach(([poolId, onchainData], index) => { - try { - const { poolTokens, swapFee, weights } = onchainData; - - if ( - subgraphPools[index].poolType === 'Stable' || - subgraphPools[index].poolType === 'MetaStable' || - subgraphPools[index].poolType === 'StablePhantom' - ) { - if (!onchainData.amp) { - console.error(`Stable Pool Missing Amp: ${poolId}`); - return; - } else { - // Need to scale amp by precision to match expected Subgraph scale - // amp is stored with 3 decimals of precision - subgraphPools[index].amp = formatFixed( - onchainData.amp[0], - 3 - ); - } - } - - if (subgraphPools[index].poolType.includes('Linear')) { - if (!onchainData.targets) { - console.error(`Linear Pool Missing Targets: ${poolId}`); - return; - } else { - subgraphPools[index].lowerTarget = formatFixed( - onchainData.targets[0], - 18 - ); - subgraphPools[index].upperTarget = formatFixed( - onchainData.targets[1], - 18 - ); - } - - const wrappedIndex = subgraphPools[index].wrappedIndex; - if ( - wrappedIndex === undefined || - onchainData.rate === undefined - ) { - console.error( - `Linear Pool Missing WrappedIndex or PriceRate: ${poolId}` - ); - return; - } - // Update priceRate of wrappedToken - subgraphPools[index].tokens[wrappedIndex].priceRate = - formatFixed(onchainData.rate, 18); - } - - subgraphPools[index].swapFee = formatFixed(swapFee, 18); - - poolTokens.tokens.forEach((token, i) => { - const T = subgraphPools[index].tokens.find((t) => - isSameAddress(t.address, token) - ); - if (!T) throw `Pool Missing Expected Token: ${poolId} ${token}`; - T.balance = formatFixed(poolTokens.balances[i], T.decimals); - if (weights) { - // Only expected for WeightedPools - T.weight = formatFixed(weights[i], 18); - } - }); - onChainPools.push(subgraphPools[index]); - } catch (err) { - throw `Issue with pool onchain data: ${err}`; + const wrappedIndex = subgraphPools[index].wrappedIndex; + if (wrappedIndex === undefined || onchainData.rate === undefined) { + console.error( + `Linear Pool Missing WrappedIndex or PriceRate: ${poolId}` + ); + return; } - }); + // Update priceRate of wrappedToken + subgraphPools[index].tokens[wrappedIndex].priceRate = formatFixed( + onchainData.rate, + 18 + ); + } + + subgraphPools[index].swapFee = formatFixed(swapFee, 18); + + poolTokens.tokens.forEach((token, i) => { + const T = subgraphPools[index].tokens.find((t) => + isSameAddress(t.address, token) + ); + if (!T) throw `Pool Missing Expected Token: ${poolId} ${token}`; + T.balance = formatFixed(poolTokens.balances[i], T.decimals); + if (weights) { + // Only expected for WeightedPools + T.weight = formatFixed(weights[i], 18); + } + }); + onChainPools.push(subgraphPools[index]); + } catch (err) { + throw `Issue with pool onchain data: ${err}`; + } + }); - return onChainPools; + return onChainPools; } diff --git a/balancer-js/src/modules/sor/pool-data/subgraphPoolDataService.ts b/balancer-js/src/modules/sor/pool-data/subgraphPoolDataService.ts index 722a2edfd..4e778147d 100644 --- a/balancer-js/src/modules/sor/pool-data/subgraphPoolDataService.ts +++ b/balancer-js/src/modules/sor/pool-data/subgraphPoolDataService.ts @@ -1,8 +1,8 @@ import { PoolDataService, SubgraphPoolBase } from '@balancer-labs/sor'; import { - OrderDirection, - Pool_OrderBy, - SubgraphClient, + OrderDirection, + Pool_OrderBy, + SubgraphClient, } from '@/modules/subgraph/subgraph'; import { parseInt } from 'lodash'; import { getOnChainBalances } from './onChainData'; @@ -11,80 +11,84 @@ import { Network } from '@/lib/constants/network'; import { BalancerNetworkConfig, BalancerSdkSorConfig } from '@/types'; const NETWORKS_WITH_LINEAR_POOLS = [ - Network.MAINNET, - Network.POLYGON, - Network.ROPSTEN, - Network.RINKEBY, - Network.GÖRLI, - Network.KOVAN, + Network.MAINNET, + Network.POLYGON, + Network.ROPSTEN, + Network.RINKEBY, + Network.GOERLI, + Network.KOVAN, ]; -export class SubgraphPoolDataService implements PoolDataService { - constructor( - private readonly client: SubgraphClient, - private readonly provider: Provider, - private readonly network: BalancerNetworkConfig, - private readonly sorConfig: BalancerSdkSorConfig - ) {} +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function mapPools(pools: any[]): SubgraphPoolBase[] { + return pools.map((pool) => ({ + ...pool, + poolType: pool.poolType || '', + // eslint-disable-next-line @typescript-eslint/no-explicit-any + tokens: (pool.tokens || []).map((token: any) => ({ + ...token, + weight: token.weight || null, + })), + totalWeight: pool.totalWeight || undefined, + amp: pool.amp || undefined, + expiryTime: pool.expiryTime ? parseInt(pool.expiryTime) : undefined, + unitSeconds: pool.unitSeconds ? parseInt(pool.unitSeconds) : undefined, + principalToken: pool.principalToken || undefined, + baseToken: pool.baseToken || undefined, + })); +} - public async getPools(): Promise { - const pools = this.supportsLinearPools - ? await this.getLinearPools() - : await this.getNonLinearPools(); +export class SubgraphPoolDataService implements PoolDataService { + constructor( + private readonly client: SubgraphClient, + private readonly provider: Provider, + private readonly network: BalancerNetworkConfig, + private readonly sorConfig: BalancerSdkSorConfig + ) {} - const mapped = pools.map((pool) => ({ - ...pool, - poolType: pool.poolType || '', - tokens: (pool.tokens || []).map((token) => ({ - ...token, - weight: token.weight || null, - })), - totalWeight: pool.totalWeight || undefined, - amp: pool.amp || undefined, - expiryTime: pool.expiryTime ? parseInt(pool.expiryTime) : undefined, - unitSeconds: pool.unitSeconds - ? parseInt(pool.unitSeconds) - : undefined, - principalToken: pool.principalToken || undefined, - baseToken: pool.baseToken || undefined, - })); + public async getPools(): Promise { + const pools = this.supportsLinearPools + ? await this.getLinearPools() + : await this.getNonLinearPools(); - if (this.sorConfig.fetchOnChainBalances === false) { - return mapped; - } + const mapped = mapPools(pools); - return getOnChainBalances( - mapped, - this.network.addresses.contracts.multicall, - this.network.addresses.contracts.vault, - this.provider - ); + if (this.sorConfig.fetchOnChainBalances === false) { + return mapped; } - private get supportsLinearPools() { - return NETWORKS_WITH_LINEAR_POOLS.includes(this.network.chainId); - } + return getOnChainBalances( + mapped, + this.network.addresses.contracts.multicall, + this.network.addresses.contracts.vault, + this.provider + ); + } - private async getLinearPools() { - const { pool0, pool1000 } = await this.client.Pools({ - where: { swapEnabled: true }, - orderBy: Pool_OrderBy.TotalLiquidity, - orderDirection: OrderDirection.Desc, - }); + private get supportsLinearPools() { + return NETWORKS_WITH_LINEAR_POOLS.includes(this.network.chainId); + } - const pools = [...pool0, ...pool1000]; + private async getLinearPools() { + const { pool0, pool1000 } = await this.client.Pools({ + where: { swapEnabled: true }, + orderBy: Pool_OrderBy.TotalLiquidity, + orderDirection: OrderDirection.Desc, + }); - return pools; - } + const pools = [...pool0, ...pool1000]; - private async getNonLinearPools() { - const { pools } = await this.client.PoolsWithoutLinear({ - where: { swapEnabled: true }, - orderBy: Pool_OrderBy.TotalLiquidity, - orderDirection: OrderDirection.Desc, - first: 1000, - }); + return pools; + } - return pools; - } + private async getNonLinearPools() { + const { pools } = await this.client.PoolsWithoutLinear({ + where: { swapEnabled: true }, + orderBy: Pool_OrderBy.TotalLiquidity, + orderDirection: OrderDirection.Desc, + first: 1000, + }); + + return pools; + } } diff --git a/balancer-js/src/modules/sor/sor.module.spec.ts b/balancer-js/src/modules/sor/sor.module.spec.ts index f770877f8..db12521ae 100644 --- a/balancer-js/src/modules/sor/sor.module.spec.ts +++ b/balancer-js/src/modules/sor/sor.module.spec.ts @@ -1,10 +1,10 @@ import dotenv from 'dotenv'; import { expect } from 'chai'; import { - BalancerSdkConfig, - BalancerSdkSorConfig, - Network, - BalancerSDK, + BalancerSdkConfig, + BalancerSdkSorConfig, + Network, + BalancerSDK, } from '@/.'; import { mockPool, mockPoolDataService } from '@/test/lib/mockPool'; import { Sor } from './sor.module'; @@ -12,36 +12,40 @@ import { Sor } from './sor.module'; dotenv.config(); const sorConfig: BalancerSdkSorConfig = { - tokenPriceService: 'coingecko', - poolDataService: mockPoolDataService, - fetchOnChainBalances: false, + tokenPriceService: 'coingecko', + poolDataService: mockPoolDataService, + fetchOnChainBalances: false, }; const sdkConfig: BalancerSdkConfig = { - network: Network.KOVAN, - rpcUrl: `https://kovan.infura.io/v3/${process.env.INFURA}`, - sor: sorConfig, + network: Network.KOVAN, + rpcUrl: `https://kovan.infura.io/v3/${process.env.INFURA}`, + sor: sorConfig, }; describe('sor module', () => { - context('instantiation', () => { - it('instantiate via module', async () => { - const sor = new Sor(sdkConfig); - await sor.fetchPools(); - const pools = sor.getPools(); - expect(pools).to.deep.eq([mockPool]); - const providerNetwork = await sor.provider.getNetwork(); - expect(providerNetwork.chainId).to.eq(sdkConfig.network); - }); + context('instantiation', () => { + it('instantiate via module', async () => { + const sor = new Sor(sdkConfig); + await sor.fetchPools(); + const pools = sor.getPools(); + expect(pools).to.deep.eq([mockPool]); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const providerNetwork = (sor.provider)['_network']['chainId']; + expect(providerNetwork).to.eq(sdkConfig.network); + }); - it('instantiate via SDK', async () => { - const balancer = new BalancerSDK(sdkConfig); + it('instantiate via SDK', async () => { + const balancer = new BalancerSDK(sdkConfig); - await balancer.sor.fetchPools(); - const pools = balancer.sor.getPools(); - expect(pools).to.deep.eq([mockPool]); - const providerNetwork = await balancer.sor.provider.getNetwork(); - expect(providerNetwork.chainId).to.eq(sdkConfig.network); - }); + await balancer.sor.fetchPools(); + const pools = balancer.sor.getPools(); + expect(pools).to.deep.eq([mockPool]); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const providerNetwork = (balancer.sor.provider)['_network'][ + 'chainId' + ]; + expect(providerNetwork).to.eq(sdkConfig.network); }); + }); }); diff --git a/balancer-js/src/modules/sor/sor.module.ts b/balancer-js/src/modules/sor/sor.module.ts index 84567d596..2dd10856a 100644 --- a/balancer-js/src/modules/sor/sor.module.ts +++ b/balancer-js/src/modules/sor/sor.module.ts @@ -3,97 +3,98 @@ import { Provider, JsonRpcProvider } from '@ethersproject/providers'; import { SubgraphPoolDataService } from './pool-data/subgraphPoolDataService'; import { CoingeckoTokenPriceService } from './token-price/coingeckoTokenPriceService'; import { - SubgraphClient, - createSubgraphClient, + SubgraphClient, + createSubgraphClient, } from '@/modules/subgraph/subgraph'; import { - BalancerNetworkConfig, - BalancerSdkConfig, - BalancerSdkSorConfig, + BalancerNetworkConfig, + BalancerSdkConfig, + BalancerSdkSorConfig, } from '@/types'; import { SubgraphTokenPriceService } from './token-price/subgraphTokenPriceService'; import { getNetworkConfig } from '@/modules/sdk.helpers'; export class Sor extends SOR { - constructor(sdkConfig: BalancerSdkConfig) { - const network = getNetworkConfig(sdkConfig); - const sorConfig = Sor.getSorConfig(sdkConfig); - const sorNetworkConfig = Sor.getSorNetworkConfig(network); - const provider = new JsonRpcProvider(sdkConfig.rpcUrl); - const subgraphClient = createSubgraphClient(network.urls.subgraph); + constructor(sdkConfig: BalancerSdkConfig) { + const network = getNetworkConfig(sdkConfig); + const sorConfig = Sor.getSorConfig(sdkConfig); + const sorNetworkConfig = Sor.getSorNetworkConfig(network); + const provider = new JsonRpcProvider( + sdkConfig.rpcUrl, + sdkConfig.network as number + ); + const subgraphClient = createSubgraphClient(network.urls.subgraph); - const poolDataService = Sor.getPoolDataService( - network, - sorConfig, - provider, - subgraphClient - ); + const poolDataService = Sor.getPoolDataService( + network, + sorConfig, + provider, + subgraphClient + ); - const tokenPriceService = Sor.getTokenPriceService( - network, - sorConfig, - subgraphClient - ); + const tokenPriceService = Sor.getTokenPriceService( + network, + sorConfig, + subgraphClient + ); - super(provider, sorNetworkConfig, poolDataService, tokenPriceService); - } + super(provider, sorNetworkConfig, poolDataService, tokenPriceService); + } - private static getSorConfig( - config: BalancerSdkConfig - ): BalancerSdkSorConfig { - return { - tokenPriceService: 'coingecko', - poolDataService: 'subgraph', - fetchOnChainBalances: true, - ...config.sor, - }; - } + private static getSorConfig(config: BalancerSdkConfig): BalancerSdkSorConfig { + return { + tokenPriceService: 'coingecko', + poolDataService: 'subgraph', + fetchOnChainBalances: true, + ...config.sor, + }; + } - private static getSorNetworkConfig( - network: BalancerNetworkConfig - ): SorConfig { - return { - ...network, - vault: network.addresses.contracts.vault, - weth: network.addresses.tokens.wrappedNativeAsset, - staBal3Pool: network.pools?.staBal3Pool, - wethStaBal3: network.pools?.wethStaBal3, - bbausd: network.pools?.bbausd, - wethBBausd: network.pools?.wethBBausd, - lbpRaisingTokens: network.addresses.tokens?.lbpRaisingTokens, - }; - } + private static getSorNetworkConfig( + network: BalancerNetworkConfig + ): SorConfig { + return { + ...network, + vault: network.addresses.contracts.vault, + weth: network.addresses.tokens.wrappedNativeAsset, + staBal3Pool: network.pools?.staBal3Pool, + wethStaBal3: network.pools?.wethStaBal3, + bbausd: network.pools?.bbausd, + wethBBausd: network.pools?.wethBBausd, + lbpRaisingTokens: network.addresses.tokens?.lbpRaisingTokens, + }; + } - private static getPoolDataService( - network: BalancerNetworkConfig, - sorConfig: BalancerSdkSorConfig, - provider: Provider, - subgraphClient: SubgraphClient - ) { - return typeof sorConfig.poolDataService === 'object' - ? sorConfig.poolDataService - : new SubgraphPoolDataService( - subgraphClient, - provider, - network, - sorConfig - ); - } - - private static getTokenPriceService( - network: BalancerNetworkConfig, - sorConfig: BalancerSdkSorConfig, - subgraphClient: SubgraphClient - ): TokenPriceService { - if (typeof sorConfig.tokenPriceService === 'object') { - return sorConfig.tokenPriceService; - } else if (sorConfig.tokenPriceService === 'subgraph') { - new SubgraphTokenPriceService( - subgraphClient, - network.addresses.tokens.wrappedNativeAsset - ); - } + private static getPoolDataService( + network: BalancerNetworkConfig, + sorConfig: BalancerSdkSorConfig, + provider: Provider, + subgraphClient: SubgraphClient + ) { + return typeof sorConfig.poolDataService === 'object' + ? sorConfig.poolDataService + : new SubgraphPoolDataService( + subgraphClient, + provider, + network, + sorConfig + ); + } - return new CoingeckoTokenPriceService(network.chainId); + private static getTokenPriceService( + network: BalancerNetworkConfig, + sorConfig: BalancerSdkSorConfig, + subgraphClient: SubgraphClient + ): TokenPriceService { + if (typeof sorConfig.tokenPriceService === 'object') { + return sorConfig.tokenPriceService; + } else if (sorConfig.tokenPriceService === 'subgraph') { + new SubgraphTokenPriceService( + subgraphClient, + network.addresses.tokens.wrappedNativeAsset + ); } + + return new CoingeckoTokenPriceService(network.chainId); + } } diff --git a/balancer-js/src/modules/sor/token-price/coingeckoTokenPriceService.ts b/balancer-js/src/modules/sor/token-price/coingeckoTokenPriceService.ts index c5e1ffcd7..306c29325 100644 --- a/balancer-js/src/modules/sor/token-price/coingeckoTokenPriceService.ts +++ b/balancer-js/src/modules/sor/token-price/coingeckoTokenPriceService.ts @@ -2,69 +2,67 @@ import { TokenPriceService } from '@balancer-labs/sor'; import axios from 'axios'; export class CoingeckoTokenPriceService implements TokenPriceService { - constructor(private readonly chainId: number) {} + constructor(private readonly chainId: number) {} - public async getNativeAssetPriceInToken( - tokenAddress: string - ): Promise { - const ethPerToken = await this.getTokenPriceInNativeAsset(tokenAddress); + public async getNativeAssetPriceInToken( + tokenAddress: string + ): Promise { + const ethPerToken = await this.getTokenPriceInNativeAsset(tokenAddress); - // We get the price of token in terms of ETH - // We want the price of 1 ETH in terms of the token base units - return `${1 / parseFloat(ethPerToken)}`; - } - - /** - * @dev Assumes that the native asset has 18 decimals - * @param tokenAddress - the address of the token contract - * @returns the price of 1 ETH in terms of the token base units - */ - async getTokenPriceInNativeAsset(tokenAddress: string): Promise { - const endpoint = `https://api.coingecko.com/api/v3/simple/token_price/${this.platformId}?contract_addresses=${tokenAddress}&vs_currencies=${this.nativeAssetId}`; + // We get the price of token in terms of ETH + // We want the price of 1 ETH in terms of the token base units + return `${1 / parseFloat(ethPerToken)}`; + } - const { data } = await axios.get(endpoint, { - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - }, - }); + /** + * @dev Assumes that the native asset has 18 decimals + * @param tokenAddress - the address of the token contract + * @returns the price of 1 ETH in terms of the token base units + */ + async getTokenPriceInNativeAsset(tokenAddress: string): Promise { + const endpoint = `https://api.coingecko.com/api/v3/simple/token_price/${this.platformId}?contract_addresses=${tokenAddress}&vs_currencies=${this.nativeAssetId}`; - if ( - data[tokenAddress.toLowerCase()][this.nativeAssetId] === undefined - ) { - throw Error('No price returned from Coingecko'); - } + const { data } = await axios.get(endpoint, { + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + }); - return data[tokenAddress.toLowerCase()][this.nativeAssetId]; + if (data[tokenAddress.toLowerCase()][this.nativeAssetId] === undefined) { + throw Error('No price returned from Coingecko'); } - private get platformId(): string { - switch (this.chainId) { - case 1: - return 'ethereum'; - case 42: - return 'ethereum'; - case 137: - return 'polygon-pos'; - case 42161: - return 'arbitrum-one'; - } + return data[tokenAddress.toLowerCase()][this.nativeAssetId]; + } - return '2'; + private get platformId(): string { + switch (this.chainId) { + case 1: + return 'ethereum'; + case 42: + return 'ethereum'; + case 137: + return 'polygon-pos'; + case 42161: + return 'arbitrum-one'; } - private get nativeAssetId(): string { - switch (this.chainId) { - case 1: - return 'eth'; - case 42: - return 'eth'; - case 137: - return ''; - case 42161: - return 'eth'; - } + return '2'; + } + private get nativeAssetId(): string { + switch (this.chainId) { + case 1: + return 'eth'; + case 42: + return 'eth'; + case 137: return ''; + case 42161: + return 'eth'; } + + return ''; + } } diff --git a/balancer-js/src/modules/sor/token-price/subgraphTokenPriceService.ts b/balancer-js/src/modules/sor/token-price/subgraphTokenPriceService.ts index 1504d2a4d..50ae12b81 100644 --- a/balancer-js/src/modules/sor/token-price/subgraphTokenPriceService.ts +++ b/balancer-js/src/modules/sor/token-price/subgraphTokenPriceService.ts @@ -3,66 +3,64 @@ import { SubgraphClient } from '@/modules/subgraph/subgraph'; import { keyBy } from 'lodash'; export class SubgraphTokenPriceService implements TokenPriceService { - private readonly weth: string; + private readonly weth: string; - constructor(private readonly client: SubgraphClient, weth: string) { - //the subgraph addresses are all toLowerCase - this.weth = weth.toLowerCase(); - } - - public async getNativeAssetPriceInToken( - tokenAddress: string - ): Promise { - const ethPerToken = await this.getLatestPriceInEthFromSubgraph( - tokenAddress - ); + constructor(private readonly client: SubgraphClient, weth: string) { + //the subgraph addresses are all toLowerCase + this.weth = weth.toLowerCase(); + } - if (!ethPerToken) { - throw Error('No price found in the subgraph'); - } + public async getNativeAssetPriceInToken( + tokenAddress: string + ): Promise { + const ethPerToken = await this.getLatestPriceInEthFromSubgraph( + tokenAddress + ); - // We want the price of 1 ETH in terms of the token base units - return `${1 / ethPerToken}`; + if (!ethPerToken) { + throw Error('No price found in the subgraph'); } - public async getLatestPriceInEthFromSubgraph( - tokenAddress: string - ): Promise { - tokenAddress = tokenAddress.toLowerCase(); + // We want the price of 1 ETH in terms of the token base units + return `${1 / ethPerToken}`; + } - const { latestPrices } = await this.client.TokenLatestPrices({ - where: { asset_in: [tokenAddress, this.weth] }, - }); - const pricesKeyedOnId = keyBy(latestPrices, 'id'); + public async getLatestPriceInEthFromSubgraph( + tokenAddress: string + ): Promise { + tokenAddress = tokenAddress.toLowerCase(); - //the ids are set as ${asset}-${pricingAsset} - //first try to find an exact match - if (pricesKeyedOnId[`${tokenAddress}-${this.weth}`]) { - return parseFloat( - pricesKeyedOnId[`${tokenAddress}-${this.weth}`].price - ); - } + const { latestPrices } = await this.client.TokenLatestPrices({ + where: { asset_in: [tokenAddress, this.weth] }, + }); + const pricesKeyedOnId = keyBy(latestPrices, 'id'); - //no exact match, try to traverse the path - const matchingLatestPrices = latestPrices.filter( - (price) => price.asset === tokenAddress - ); + //the ids are set as ${asset}-${pricingAsset} + //first try to find an exact match + if (pricesKeyedOnId[`${tokenAddress}-${this.weth}`]) { + return parseFloat(pricesKeyedOnId[`${tokenAddress}-${this.weth}`].price); + } - //pick the first one we match on. - //There is no timestamp on latestPrice, should get introduced to allow for sorting by latest - for (const tokenPrice of matchingLatestPrices) { - const pricingAssetPricedInEth = - pricesKeyedOnId[`${tokenPrice.pricingAsset}-${this.weth}`]; + //no exact match, try to traverse the path + const matchingLatestPrices = latestPrices.filter( + (price) => price.asset === tokenAddress + ); - //1 BAL = 20 USDC, 1 USDC = 0.00025 ETH, 1 BAL = 20 * 0.00025 - if (pricingAssetPricedInEth) { - return ( - parseFloat(tokenPrice.price) * - parseFloat(pricingAssetPricedInEth.price) - ); - } - } + //pick the first one we match on. + //There is no timestamp on latestPrice, should get introduced to allow for sorting by latest + for (const tokenPrice of matchingLatestPrices) { + const pricingAssetPricedInEth = + pricesKeyedOnId[`${tokenPrice.pricingAsset}-${this.weth}`]; - return null; + //1 BAL = 20 USDC, 1 USDC = 0.00025 ETH, 1 BAL = 20 * 0.00025 + if (pricingAssetPricedInEth) { + return ( + parseFloat(tokenPrice.price) * + parseFloat(pricingAssetPricedInEth.price) + ); + } } + + return null; + } } diff --git a/balancer-js/src/modules/subgraph/subgraph.module.spec.ts b/balancer-js/src/modules/subgraph/subgraph.module.spec.ts index 83a1b0e70..ce46e485d 100644 --- a/balancer-js/src/modules/subgraph/subgraph.module.spec.ts +++ b/balancer-js/src/modules/subgraph/subgraph.module.spec.ts @@ -6,23 +6,23 @@ import { Subgraph } from './subgraph.module'; dotenv.config(); const sdkConfig: BalancerSdkConfig = { - network: Network.KOVAN, - rpcUrl: `https://kovan.infura.io/v3/${process.env.INFURA}`, - customSubgraphUrl: 'https://thegraph.com/custom-subgraph', + network: Network.KOVAN, + rpcUrl: `https://kovan.infura.io/v3/${process.env.INFURA}`, + customSubgraphUrl: 'https://thegraph.com/custom-subgraph', }; describe('subgraph module', () => { - context('instantiation', () => { - it('instantiate via module', async () => { - const subgraph = new Subgraph(sdkConfig); - expect(subgraph.url).to.eq('https://thegraph.com/custom-subgraph'); - }); + context('instantiation', () => { + it('instantiate via module', async () => { + const subgraph = new Subgraph(sdkConfig); + expect(subgraph.url).to.eq('https://thegraph.com/custom-subgraph'); + }); - it('instantiate via SDK', async () => { - const balancer = new BalancerSDK(sdkConfig); - expect(balancer.subgraph.url).to.eq( - 'https://thegraph.com/custom-subgraph' - ); - }); + it('instantiate via SDK', async () => { + const balancer = new BalancerSDK(sdkConfig); + expect(balancer.subgraph.url).to.eq( + 'https://thegraph.com/custom-subgraph' + ); }); + }); }); diff --git a/balancer-js/src/modules/subgraph/subgraph.module.ts b/balancer-js/src/modules/subgraph/subgraph.module.ts index 981c56ca8..099bb25e5 100644 --- a/balancer-js/src/modules/subgraph/subgraph.module.ts +++ b/balancer-js/src/modules/subgraph/subgraph.module.ts @@ -5,16 +5,16 @@ import { getSdk } from './generated/balancer-subgraph-types'; import { SubgraphClient } from './subgraph'; export class Subgraph { - public readonly url: string; - public readonly client: SubgraphClient; + public readonly url: string; + public readonly client: SubgraphClient; - constructor(config: BalancerSdkConfig) { - this.url = getNetworkConfig(config).urls.subgraph; - this.client = this.initClient(); - } + constructor(config: BalancerSdkConfig) { + this.url = getNetworkConfig(config).urls.subgraph; + this.client = this.initClient(); + } - private initClient(): SubgraphClient { - const client = new GraphQLClient(this.url); - return getSdk(client); - } + private initClient(): SubgraphClient { + const client = new GraphQLClient(this.url); + return getSdk(client); + } } diff --git a/balancer-js/src/modules/subgraph/subgraph.ts b/balancer-js/src/modules/subgraph/subgraph.ts index c8bff7009..87ebfda84 100644 --- a/balancer-js/src/modules/subgraph/subgraph.ts +++ b/balancer-js/src/modules/subgraph/subgraph.ts @@ -6,7 +6,7 @@ export * from './generated/balancer-subgraph-types'; export type SubgraphClient = Sdk; export function createSubgraphClient(subgraphUrl: string): SubgraphClient { - const client = new GraphQLClient(subgraphUrl); + const client = new GraphQLClient(subgraphUrl); - return getSdk(client); + return getSdk(client); } diff --git a/balancer-js/src/modules/swaps/flashSwap/flashSwap.spec.ts b/balancer-js/src/modules/swaps/flashSwap/flashSwap.spec.ts index de00f04e9..fc116e32a 100644 --- a/balancer-js/src/modules/swaps/flashSwap/flashSwap.spec.ts +++ b/balancer-js/src/modules/swaps/flashSwap/flashSwap.spec.ts @@ -1,8 +1,8 @@ import { Contract } from '@ethersproject/contracts'; import { expect } from 'chai'; import { - convertSimpleFlashSwapToBatchSwapParameters, - querySimpleFlashSwap, + convertSimpleFlashSwapToBatchSwapParameters, + querySimpleFlashSwap, } from '.'; import { SwapType } from '../types'; import vaultAbi from '@/lib/abi/Vault.json'; @@ -10,98 +10,100 @@ import { balancerVault } from '../../../lib/constants/config'; import MockProvider from '../../../test/lib/MockProvider'; class MockVaultContract extends Contract { - // Stubbed methods - async queryBatchSwap(...rest: unknown[]) { - // This validates the input to the method - this.interface.encodeFunctionData('queryBatchSwap', rest); + // Stubbed methods + async queryBatchSwap(...rest: unknown[]) { + // This validates the input to the method + this.interface.encodeFunctionData('queryBatchSwap', rest); - return [1, 2]; - } + return [1, 2]; + } } describe('convertSimpleFlashSwapToBatchSwapParameters', () => { - it('should convert flash swap parameters to batch swap parameters', () => { - const flashSwapParams = { - flashLoanAmount: '10000', - poolIds: [ - '0x0cdab06b07197d96369fea6f3bea6efc7ecdf7090002000000000000000003de', - '0x17018c2f7c345add873474879ff0ed98ebd6346a000200000000000000000642', - ], - assets: [ - '0xc2569dd7d0fd715b054fbf16e75b001e5c0c1115', - '0x04df6e4121c27713ed22341e7c7df330f56f289b', - ], - walletAddress: '0x35f5a330FD2F8e521ebd259FA272bA8069590741', - }; + it('should convert flash swap parameters to batch swap parameters', () => { + const flashSwapParams = { + flashLoanAmount: '10000', + poolIds: [ + '0x0cdab06b07197d96369fea6f3bea6efc7ecdf7090002000000000000000003de', + '0x17018c2f7c345add873474879ff0ed98ebd6346a000200000000000000000642', + ], + assets: [ + '0xc2569dd7d0fd715b054fbf16e75b001e5c0c1115', + '0x04df6e4121c27713ed22341e7c7df330f56f289b', + ], + walletAddress: '0x35f5a330FD2F8e521ebd259FA272bA8069590741', + }; - const batchSwapParameters = { - kind: SwapType.SwapExactIn, - swaps: [ - { - poolId: '0x0cdab06b07197d96369fea6f3bea6efc7ecdf7090002000000000000000003de', - assetInIndex: 0, - assetOutIndex: 1, - amount: '10000', - userData: '0x', - }, - { - poolId: '0x17018c2f7c345add873474879ff0ed98ebd6346a000200000000000000000642', - assetInIndex: 1, - assetOutIndex: 0, - amount: '0', - userData: '0x', - }, - ], - assets: [ - '0xc2569dd7d0fd715b054fbf16e75b001e5c0c1115', - '0x04df6e4121c27713ed22341e7c7df330f56f289b', - ], - funds: { - fromInternalBalance: false, - recipient: '0x35f5a330FD2F8e521ebd259FA272bA8069590741', - sender: '0x35f5a330FD2F8e521ebd259FA272bA8069590741', - toInternalBalance: false, - }, - limits: ['0', '0'], // No limits - deadline: '999999999999999999', // Infinity - }; + const batchSwapParameters = { + kind: SwapType.SwapExactIn, + swaps: [ + { + poolId: + '0x0cdab06b07197d96369fea6f3bea6efc7ecdf7090002000000000000000003de', + assetInIndex: 0, + assetOutIndex: 1, + amount: '10000', + userData: '0x', + }, + { + poolId: + '0x17018c2f7c345add873474879ff0ed98ebd6346a000200000000000000000642', + assetInIndex: 1, + assetOutIndex: 0, + amount: '0', + userData: '0x', + }, + ], + assets: [ + '0xc2569dd7d0fd715b054fbf16e75b001e5c0c1115', + '0x04df6e4121c27713ed22341e7c7df330f56f289b', + ], + funds: { + fromInternalBalance: false, + recipient: '0x35f5a330FD2F8e521ebd259FA272bA8069590741', + sender: '0x35f5a330FD2F8e521ebd259FA272bA8069590741', + toInternalBalance: false, + }, + limits: ['0', '0'], // No limits + deadline: '999999999999999999', // Infinity + }; - expect( - convertSimpleFlashSwapToBatchSwapParameters(flashSwapParams) - ).to.eql(batchSwapParameters); - }); + expect(convertSimpleFlashSwapToBatchSwapParameters(flashSwapParams)).to.eql( + batchSwapParameters + ); + }); }); describe('querySimpleFlashSwap', () => { - const vaultContract = new MockVaultContract( - balancerVault, - vaultAbi, - new MockProvider() - ); + const vaultContract = new MockVaultContract( + balancerVault, + vaultAbi, + new MockProvider() + ); - describe('response', async function () { - const response = await querySimpleFlashSwap({ - vaultContract, - flashLoanAmount: '10000', - poolIds: [ - '0x0cdab06b07197d96369fea6f3bea6efc7ecdf7090002000000000000000003de', - '0x17018c2f7c345add873474879ff0ed98ebd6346a000200000000000000000642', - ], - assets: [ - '0xc2569dd7d0fd715b054fbf16e75b001e5c0c1115', - '0x04df6e4121c27713ed22341e7c7df330f56f289b', - ], - }); + describe('response', async function () { + const response = await querySimpleFlashSwap({ + vaultContract, + flashLoanAmount: '10000', + poolIds: [ + '0x0cdab06b07197d96369fea6f3bea6efc7ecdf7090002000000000000000003de', + '0x17018c2f7c345add873474879ff0ed98ebd6346a000200000000000000000642', + ], + assets: [ + '0xc2569dd7d0fd715b054fbf16e75b001e5c0c1115', + '0x04df6e4121c27713ed22341e7c7df330f56f289b', + ], + }); - it('should return the estimated profits', async () => { - expect(response.profits).to.eql({ - '0xc2569dd7d0fd715b054fbf16e75b001e5c0c1115': '-1', - '0x04df6e4121c27713ed22341e7c7df330f56f289b': '-2', - }); - }); + it('should return the estimated profits', async () => { + expect(response.profits).to.eql({ + '0xc2569dd7d0fd715b054fbf16e75b001e5c0c1115': '-1', + '0x04df6e4121c27713ed22341e7c7df330f56f289b': '-2', + }); + }); - it('should indicated if flash swap will be profitable', async () => { - expect(response.isProfitable).to.equal(false); - }); + it('should indicated if flash swap will be profitable', async () => { + expect(response.isProfitable).to.equal(false); }); + }); }); diff --git a/balancer-js/src/modules/swaps/flashSwap/index.ts b/balancer-js/src/modules/swaps/flashSwap/index.ts index 03bf3469c..039571c8d 100644 --- a/balancer-js/src/modules/swaps/flashSwap/index.ts +++ b/balancer-js/src/modules/swaps/flashSwap/index.ts @@ -1,91 +1,89 @@ import { - QuerySimpleFlashSwapParameters, - QuerySimpleFlashSwapResponse, - SimpleFlashSwapParameters, - SwapType, + QuerySimpleFlashSwapParameters, + QuerySimpleFlashSwapResponse, + SimpleFlashSwapParameters, + SwapType, } from '../types'; import { queryBatchSwap } from '../queryBatchSwap'; import { BatchSwap } from '../types'; import { sum } from 'lodash'; function checkSimpleFlashSwapParams(params: { - poolIds: string[]; - assets: string[]; + poolIds: string[]; + assets: string[]; }) { - if (params.poolIds.length > 2) { - throw new Error( - 'Simple flash swap only supports a maximum of two pools' - ); - } - - if (params.assets.length > 2) { - throw new Error( - 'Simple flash swap only supports a maximum of to two assets (tokens)' - ); - } + if (params.poolIds.length > 2) { + throw new Error('Simple flash swap only supports a maximum of two pools'); + } + + if (params.assets.length > 2) { + throw new Error( + 'Simple flash swap only supports a maximum of to two assets (tokens)' + ); + } } function createSwaps( - poolIds: SimpleFlashSwapParameters['poolIds'], - amount: string + poolIds: SimpleFlashSwapParameters['poolIds'], + amount: string ): BatchSwap['swaps'] { - return [ - { - poolId: poolIds[0], - assetInIndex: 0, - assetOutIndex: 1, - amount, - userData: '0x', - }, - { - poolId: poolIds[1], - assetInIndex: 1, - assetOutIndex: 0, - amount: '0', - userData: '0x', - }, - ]; + return [ + { + poolId: poolIds[0], + assetInIndex: 0, + assetOutIndex: 1, + amount, + userData: '0x', + }, + { + poolId: poolIds[1], + assetInIndex: 1, + assetOutIndex: 0, + amount: '0', + userData: '0x', + }, + ]; } export function convertSimpleFlashSwapToBatchSwapParameters({ - poolIds, - assets, - flashLoanAmount, - walletAddress, + poolIds, + assets, + flashLoanAmount, + walletAddress, }: SimpleFlashSwapParameters & { - walletAddress: string; + walletAddress: string; }): BatchSwap { - checkSimpleFlashSwapParams({ poolIds, assets }); + checkSimpleFlashSwapParams({ poolIds, assets }); - const swaps = createSwaps(poolIds, flashLoanAmount); + const swaps = createSwaps(poolIds, flashLoanAmount); - const funds = { - sender: walletAddress, - fromInternalBalance: false, - recipient: walletAddress, - toInternalBalance: false, - }; + const funds = { + sender: walletAddress, + fromInternalBalance: false, + recipient: walletAddress, + toInternalBalance: false, + }; - const limits = ['0', '0']; + const limits = ['0', '0']; - const deadline = '999999999999999999'; + const deadline = '999999999999999999'; - return { - kind: SwapType.SwapExactIn, - swaps, - assets, - funds, - limits, - deadline, - }; + return { + kind: SwapType.SwapExactIn, + swaps, + assets, + funds, + limits, + deadline, + }; } function deltaToExpectedProfit(delta: string) { - return Number(delta) * -1; + return Number(delta) * -1; } function calcProfit(profits: string[]) { - return sum(profits); + return sum(profits); } /** @@ -107,32 +105,31 @@ function calcProfit(profits: string[]) { * @returns {Promise} Returns an ethersjs transaction response */ export async function querySimpleFlashSwap( - params: QuerySimpleFlashSwapParameters + params: QuerySimpleFlashSwapParameters ): Promise { - checkSimpleFlashSwapParams(params); - - const [tokenAddress0, tokenAddress1] = params.assets; - - try { - const deltas = await queryBatchSwap( - params.vaultContract, - SwapType.SwapExactIn, - createSwaps(params.poolIds, params.flashLoanAmount), - params.assets - ); - - const profits = { - [tokenAddress0]: deltaToExpectedProfit(deltas[0]).toString(), - [tokenAddress1]: deltaToExpectedProfit(deltas[1]).toString(), - }; - - return { - profits, - isProfitable: - calcProfit([profits[tokenAddress0], profits[tokenAddress1]]) > - 0, - }; - } catch (err) { - throw `Failed to querySimpleFlashSwap: ${err}`; - } + checkSimpleFlashSwapParams(params); + + const [tokenAddress0, tokenAddress1] = params.assets; + + try { + const deltas = await queryBatchSwap( + params.vaultContract, + SwapType.SwapExactIn, + createSwaps(params.poolIds, params.flashLoanAmount), + params.assets + ); + + const profits = { + [tokenAddress0]: deltaToExpectedProfit(deltas[0]).toString(), + [tokenAddress1]: deltaToExpectedProfit(deltas[1]).toString(), + }; + + return { + profits, + isProfitable: + calcProfit([profits[tokenAddress0], profits[tokenAddress1]]) > 0, + }; + } catch (err) { + throw `Failed to querySimpleFlashSwap: ${err}`; + } } diff --git a/balancer-js/src/modules/swaps/helpers.spec.ts b/balancer-js/src/modules/swaps/helpers.spec.ts index e921c4e9e..9584dc1d4 100644 --- a/balancer-js/src/modules/swaps/helpers.spec.ts +++ b/balancer-js/src/modules/swaps/helpers.spec.ts @@ -3,153 +3,129 @@ import { expect } from 'chai'; import { getLimitsForSlippage, SwapType } from '@/.'; describe('swaps service helpers', () => { - context('getLimits', () => { - context('SwapExactIn', () => { - it('Applies 0 slippage to limits', () => { - const limits = getLimitsForSlippage( - [ - '0x21ff756ca0cfcc5fff488ad67babadffee0c4149', - '0x21ff756ca0cfcc5fff488ad67babadffee0c4149', - '0x21ff756ca0cfcc5fff488ad67babadffee0c4149', - ], - [ - '0x4811a7bb9061a46753486e5e84b3cd3d668fb596', - '0x0fbddc06a4720408a2f5eb78e62bc31ac6e2a3c4', - '0xe8191aacfcdb32260cda25830dc6c9342142f310', - ], - SwapType.SwapExactIn, - [ - '300000000000000000', - '0', - '-86145686129706527', - '0', - '-99314', - ], - [ - '0x21ff756ca0cfcc5fff488ad67babadffee0c4149', - '0xcd32a460b6fecd053582e43b07ed6e2c04e15369', - '0x4811a7bb9061a46753486e5e84b3cd3d668fb596', - '0x6a8c3239695613c0710dc971310b36f9b81e115e', - '0xe8191aacfcdb32260cda25830dc6c9342142f310', - ], - '0' - ); - expect(limits[0].toString()).to.eq('300000000000000000'); - expect(limits[1].toString()).to.eq('0'); - expect(limits[2].toString()).to.eq('-86145686129706527'); - expect(limits[3].toString()).to.eq('0'); - expect(limits[4].toString()).to.eq('-99314'); - }); + context('getLimits', () => { + context('SwapExactIn', () => { + it('Applies 0 slippage to limits', () => { + const limits = getLimitsForSlippage( + [ + '0x21ff756ca0cfcc5fff488ad67babadffee0c4149', + '0x21ff756ca0cfcc5fff488ad67babadffee0c4149', + '0x21ff756ca0cfcc5fff488ad67babadffee0c4149', + ], + [ + '0x4811a7bb9061a46753486e5e84b3cd3d668fb596', + '0x0fbddc06a4720408a2f5eb78e62bc31ac6e2a3c4', + '0xe8191aacfcdb32260cda25830dc6c9342142f310', + ], + SwapType.SwapExactIn, + ['300000000000000000', '0', '-86145686129706527', '0', '-99314'], + [ + '0x21ff756ca0cfcc5fff488ad67babadffee0c4149', + '0xcd32a460b6fecd053582e43b07ed6e2c04e15369', + '0x4811a7bb9061a46753486e5e84b3cd3d668fb596', + '0x6a8c3239695613c0710dc971310b36f9b81e115e', + '0xe8191aacfcdb32260cda25830dc6c9342142f310', + ], + '0' + ); + expect(limits[0].toString()).to.eq('300000000000000000'); + expect(limits[1].toString()).to.eq('0'); + expect(limits[2].toString()).to.eq('-86145686129706527'); + expect(limits[3].toString()).to.eq('0'); + expect(limits[4].toString()).to.eq('-99314'); + }); - it('Applies slippage to limits for tokenOut', () => { - const limits = getLimitsForSlippage( - [ - '0x21ff756ca0cfcc5fff488ad67babadffee0c4149', - '0x21ff756ca0cfcc5fff488ad67babadffee0c4149', - '0x21ff756ca0cfcc5fff488ad67babadffee0c4149', - ], - [ - '0x4811a7bb9061a46753486e5e84b3cd3d668fb596', - '0x0fbddc06a4720408a2f5eb78e62bc31ac6e2a3c4', - '0xe8191aacfcdb32260cda25830dc6c9342142f310', - ], - SwapType.SwapExactIn, - [ - '300000000000000000', - '0', - '-86145686129706527', - '0', - '-99314', - ], - [ - '0x21ff756ca0cfcc5fff488ad67babadffee0c4149', - '0xcd32a460b6fecd053582e43b07ed6e2c04e15369', - '0x4811a7bb9061a46753486e5e84b3cd3d668fb596', - '0x6a8c3239695613c0710dc971310b36f9b81e115e', - '0xe8191aacfcdb32260cda25830dc6c9342142f310', - ], - '50000000000000000' // 5% - ); - expect(limits[0].toString()).to.eq('300000000000000000'); - expect(limits[1].toString()).to.eq('0'); - expect(limits[2].toString()).to.eq('-81838401823221200'); - expect(limits[3].toString()).to.eq('0'); - expect(limits[4].toString()).to.eq('-94348'); - }); - }); + it('Applies slippage to limits for tokenOut', () => { + const limits = getLimitsForSlippage( + [ + '0x21ff756ca0cfcc5fff488ad67babadffee0c4149', + '0x21ff756ca0cfcc5fff488ad67babadffee0c4149', + '0x21ff756ca0cfcc5fff488ad67babadffee0c4149', + ], + [ + '0x4811a7bb9061a46753486e5e84b3cd3d668fb596', + '0x0fbddc06a4720408a2f5eb78e62bc31ac6e2a3c4', + '0xe8191aacfcdb32260cda25830dc6c9342142f310', + ], + SwapType.SwapExactIn, + ['300000000000000000', '0', '-86145686129706527', '0', '-99314'], + [ + '0x21ff756ca0cfcc5fff488ad67babadffee0c4149', + '0xcd32a460b6fecd053582e43b07ed6e2c04e15369', + '0x4811a7bb9061a46753486e5e84b3cd3d668fb596', + '0x6a8c3239695613c0710dc971310b36f9b81e115e', + '0xe8191aacfcdb32260cda25830dc6c9342142f310', + ], + '50000000000000000' // 5% + ); + expect(limits[0].toString()).to.eq('300000000000000000'); + expect(limits[1].toString()).to.eq('0'); + expect(limits[2].toString()).to.eq('-81838401823221200'); + expect(limits[3].toString()).to.eq('0'); + expect(limits[4].toString()).to.eq('-94348'); + }); + }); - context('SwapExactOut', () => { - it('Applies 0 slippage to limits', () => { - const limits = getLimitsForSlippage( - [ - '0x21ff756ca0cfcc5fff488ad67babadffee0c4149', - '0x21ff756ca0cfcc5fff488ad67babadffee0c4149', - '0x21ff756ca0cfcc5fff488ad67babadffee0c4149', - ], - [ - '0x4811a7bb9061a46753486e5e84b3cd3d668fb596', - '0x0fbddc06a4720408a2f5eb78e62bc31ac6e2a3c4', - '0xe8191aacfcdb32260cda25830dc6c9342142f310', - ], - SwapType.SwapExactOut, - [ - '300000000000000000', - '0', - '-86145686129706527', - '0', - '-99314', - ], - [ - '0x21ff756ca0cfcc5fff488ad67babadffee0c4149', - '0xcd32a460b6fecd053582e43b07ed6e2c04e15369', - '0x4811a7bb9061a46753486e5e84b3cd3d668fb596', - '0x6a8c3239695613c0710dc971310b36f9b81e115e', - '0xe8191aacfcdb32260cda25830dc6c9342142f310', - ], - '0' - ); - expect(limits[0].toString()).to.eq('300000000000000000'); - expect(limits[1].toString()).to.eq('0'); - expect(limits[2].toString()).to.eq('-86145686129706527'); - expect(limits[3].toString()).to.eq('0'); - expect(limits[4].toString()).to.eq('-99314'); - }); + context('SwapExactOut', () => { + it('Applies 0 slippage to limits', () => { + const limits = getLimitsForSlippage( + [ + '0x21ff756ca0cfcc5fff488ad67babadffee0c4149', + '0x21ff756ca0cfcc5fff488ad67babadffee0c4149', + '0x21ff756ca0cfcc5fff488ad67babadffee0c4149', + ], + [ + '0x4811a7bb9061a46753486e5e84b3cd3d668fb596', + '0x0fbddc06a4720408a2f5eb78e62bc31ac6e2a3c4', + '0xe8191aacfcdb32260cda25830dc6c9342142f310', + ], + SwapType.SwapExactOut, + ['300000000000000000', '0', '-86145686129706527', '0', '-99314'], + [ + '0x21ff756ca0cfcc5fff488ad67babadffee0c4149', + '0xcd32a460b6fecd053582e43b07ed6e2c04e15369', + '0x4811a7bb9061a46753486e5e84b3cd3d668fb596', + '0x6a8c3239695613c0710dc971310b36f9b81e115e', + '0xe8191aacfcdb32260cda25830dc6c9342142f310', + ], + '0' + ); + expect(limits[0].toString()).to.eq('300000000000000000'); + expect(limits[1].toString()).to.eq('0'); + expect(limits[2].toString()).to.eq('-86145686129706527'); + expect(limits[3].toString()).to.eq('0'); + expect(limits[4].toString()).to.eq('-99314'); + }); - it('Applies slippage to limits for tokenIn', () => { - const limits = getLimitsForSlippage( - [ - '0x21ff756ca0cfcc5fff488ad67babadffee0c4149', - '0x21ff756ca0cfcc5fff488ad67babadffee0c4149', - '0x21ff756ca0cfcc5fff488ad67babadffee0c4149', - ], - [ - '0x4811a7bb9061a46753486e5e84b3cd3d668fb596', - '0x0fbddc06a4720408a2f5eb78e62bc31ac6e2a3c4', - '0xe8191aacfcdb32260cda25830dc6c9342142f310', - ], - SwapType.SwapExactOut, - [ - '300000000000000000', - '0', - '-86145686129706527', - '0', - '-99314', - ], - [ - '0x21ff756ca0cfcc5fff488ad67babadffee0c4149', - '0xcd32a460b6fecd053582e43b07ed6e2c04e15369', - '0x4811a7bb9061a46753486e5e84b3cd3d668fb596', - '0x6a8c3239695613c0710dc971310b36f9b81e115e', - '0xe8191aacfcdb32260cda25830dc6c9342142f310', - ], - '50000000000000000' // 5% - ); - expect(limits[0].toString()).to.eq('315000000000000000'); - expect(limits[1].toString()).to.eq('0'); - expect(limits[2].toString()).to.eq('-86145686129706527'); - expect(limits[3].toString()).to.eq('0'); - expect(limits[4].toString()).to.eq('-99314'); - }); - }); + it('Applies slippage to limits for tokenIn', () => { + const limits = getLimitsForSlippage( + [ + '0x21ff756ca0cfcc5fff488ad67babadffee0c4149', + '0x21ff756ca0cfcc5fff488ad67babadffee0c4149', + '0x21ff756ca0cfcc5fff488ad67babadffee0c4149', + ], + [ + '0x4811a7bb9061a46753486e5e84b3cd3d668fb596', + '0x0fbddc06a4720408a2f5eb78e62bc31ac6e2a3c4', + '0xe8191aacfcdb32260cda25830dc6c9342142f310', + ], + SwapType.SwapExactOut, + ['300000000000000000', '0', '-86145686129706527', '0', '-99314'], + [ + '0x21ff756ca0cfcc5fff488ad67babadffee0c4149', + '0xcd32a460b6fecd053582e43b07ed6e2c04e15369', + '0x4811a7bb9061a46753486e5e84b3cd3d668fb596', + '0x6a8c3239695613c0710dc971310b36f9b81e115e', + '0xe8191aacfcdb32260cda25830dc6c9342142f310', + ], + '50000000000000000' // 5% + ); + expect(limits[0].toString()).to.eq('315000000000000000'); + expect(limits[1].toString()).to.eq('0'); + expect(limits[2].toString()).to.eq('-86145686129706527'); + expect(limits[3].toString()).to.eq('0'); + expect(limits[4].toString()).to.eq('-99314'); + }); }); + }); }); diff --git a/balancer-js/src/modules/swaps/helpers.ts b/balancer-js/src/modules/swaps/helpers.ts index 1c0c31d37..e303c0e0f 100644 --- a/balancer-js/src/modules/swaps/helpers.ts +++ b/balancer-js/src/modules/swaps/helpers.ts @@ -15,46 +15,42 @@ import { isSameAddress } from '@/lib/utils'; * @returns Returns an array (same length as assets) with limits applied for each asset. */ export function getLimitsForSlippage( - tokensIn: string[], - tokensOut: string[], - swapType: SwapType, - deltas: BigNumberish[], - assets: string[], - slippage: BigNumberish + tokensIn: string[], + tokensOut: string[], + swapType: SwapType, + deltas: BigNumberish[], + assets: string[], + slippage: BigNumberish ): BigNumberish[] { - // Limits: - // +ve means max to send - // -ve mean min to receive - // For a multihop the intermediate tokens should be 0 - const limits: BigNumber[] = new Array(assets.length).fill(Zero); + // Limits: + // +ve means max to send + // -ve mean min to receive + // For a multihop the intermediate tokens should be 0 + const limits: BigNumber[] = new Array(assets.length).fill(Zero); - assets.forEach((token, i) => { - if (tokensIn.some((tokenIn) => isSameAddress(token, tokenIn))) { - // For SwapExactOut slippage is on tokenIn, i.e. amtIn + slippage - const slippageAmount = BigNumber.from(slippage).add(WeiPerEther); - limits[i] = - swapType === SwapType.SwapExactOut - ? limits[i].add( - BigNumber.from(deltas[i]) - .mul(slippageAmount) - .div(WeiPerEther) - ) - : limits[i].add(deltas[i]); - } + assets.forEach((token, i) => { + if (tokensIn.some((tokenIn) => isSameAddress(token, tokenIn))) { + // For SwapExactOut slippage is on tokenIn, i.e. amtIn + slippage + const slippageAmount = BigNumber.from(slippage).add(WeiPerEther); + limits[i] = + swapType === SwapType.SwapExactOut + ? limits[i].add( + BigNumber.from(deltas[i]).mul(slippageAmount).div(WeiPerEther) + ) + : limits[i].add(deltas[i]); + } - if (tokensOut.some((tokenOut) => isSameAddress(token, tokenOut))) { - // For SwapExactIn slippage is on tokenOut, i.e. amtOut - slippage - const slippageAmount = WeiPerEther.sub(BigNumber.from(slippage)); - limits[i] = - swapType === SwapType.SwapExactIn - ? limits[i].add( - BigNumber.from(deltas[i]) - .mul(slippageAmount) - .div(WeiPerEther) - ) - : limits[i].add(deltas[i]); - } - }); + if (tokensOut.some((tokenOut) => isSameAddress(token, tokenOut))) { + // For SwapExactIn slippage is on tokenOut, i.e. amtOut - slippage + const slippageAmount = WeiPerEther.sub(BigNumber.from(slippage)); + limits[i] = + swapType === SwapType.SwapExactIn + ? limits[i].add( + BigNumber.from(deltas[i]).mul(slippageAmount).div(WeiPerEther) + ) + : limits[i].add(deltas[i]); + } + }); - return limits; + return limits; } diff --git a/balancer-js/src/modules/swaps/queryBatchSwap.ts b/balancer-js/src/modules/swaps/queryBatchSwap.ts index 5184c7922..81238b75c 100644 --- a/balancer-js/src/modules/swaps/queryBatchSwap.ts +++ b/balancer-js/src/modules/swaps/queryBatchSwap.ts @@ -4,11 +4,11 @@ import { AddressZero, Zero } from '@ethersproject/constants'; import { SOR, SwapTypes, SwapInfo } from '@balancer-labs/sor'; import { BalancerError, BalancerErrorCode } from '@/balancerErrors'; import { - SwapType, - BatchSwapStep, - FundManagement, - QueryWithSorInput, - QueryWithSorOutput, + SwapType, + BatchSwapStep, + FundManagement, + QueryWithSorInput, + QueryWithSorOutput, } from './types'; /* @@ -20,97 +20,96 @@ import { * receives are the same that an equivalent `batchSwap` call would receive. */ export async function queryBatchSwap( - vaultContract: Contract, - swapType: SwapType, - swaps: BatchSwapStep[], - assets: string[] + vaultContract: Contract, + swapType: SwapType, + swaps: BatchSwapStep[], + assets: string[] ): Promise { - const funds: FundManagement = { - sender: AddressZero, - recipient: AddressZero, - fromInternalBalance: false, - toInternalBalance: false, - }; + const funds: FundManagement = { + sender: AddressZero, + recipient: AddressZero, + fromInternalBalance: false, + toInternalBalance: false, + }; - try { - const deltas = await vaultContract.queryBatchSwap( - swapType, - swaps, - assets, - funds - ); - return deltas.map((d: BigNumberish) => d.toString()); - } catch (err) { - throw `queryBatchSwap call error: ${err}`; - } + try { + const deltas = await vaultContract.queryBatchSwap( + swapType, + swaps, + assets, + funds + ); + return deltas.map((d: BigNumberish) => d.toString()); + } catch (err) { + throw `queryBatchSwap call error: ${err}`; + } } /* Uses SOR to create a batchSwap which is then queried onChain. */ export async function queryBatchSwapWithSor( - sor: SOR, - vaultContract: Contract, - queryWithSor: QueryWithSorInput + sor: SOR, + vaultContract: Contract, + queryWithSor: QueryWithSorInput ): Promise { - if (queryWithSor.fetchPools.fetchPools) await sor.fetchPools(); + if (queryWithSor.fetchPools.fetchPools) await sor.fetchPools(); - const swaps: BatchSwapStep[][] = []; - const assetArray: string[][] = []; - // get path information for each tokenIn - for (let i = 0; i < queryWithSor.tokensIn.length; i++) { - const swap = await getSorSwapInfo( - queryWithSor.tokensIn[i], - queryWithSor.tokensOut[i], - queryWithSor.swapType, - queryWithSor.amounts[i].toString(), - sor - ); - if (!swap.returnAmount.gt(Zero)) - // Throw here because swaps with 0 amounts has no path and has misleading result for query - throw new BalancerError(BalancerErrorCode.SWAP_ZERO_RETURN_AMOUNT); + const swaps: BatchSwapStep[][] = []; + const assetArray: string[][] = []; + // get path information for each tokenIn + for (let i = 0; i < queryWithSor.tokensIn.length; i++) { + const swap = await getSorSwapInfo( + queryWithSor.tokensIn[i], + queryWithSor.tokensOut[i], + queryWithSor.swapType, + queryWithSor.amounts[i].toString(), + sor + ); + if (!swap.returnAmount.gt(Zero)) + // Throw here because swaps with 0 amounts has no path and has misleading result for query + throw new BalancerError(BalancerErrorCode.SWAP_ZERO_RETURN_AMOUNT); - swaps.push(swap.swaps); - assetArray.push(swap.tokenAddresses); - } + swaps.push(swap.swaps); + assetArray.push(swap.tokenAddresses); + } - // Join swaps and assets together correctly - const batchedSwaps = batchSwaps(assetArray, swaps); + // Join swaps and assets together correctly + const batchedSwaps = batchSwaps(assetArray, swaps); - const returnTokens = - queryWithSor.swapType === SwapType.SwapExactIn - ? queryWithSor.tokensOut - : queryWithSor.tokensIn; - const returnAmounts: string[] = Array(returnTokens.length).fill('0'); - let deltas: BigNumberish[] = Array(batchedSwaps.assets.length).fill('0'); - try { - // Onchain query - deltas = await queryBatchSwap( - vaultContract, - queryWithSor.swapType, - batchedSwaps.swaps, - batchedSwaps.assets - ); + const returnTokens = + queryWithSor.swapType === SwapType.SwapExactIn + ? queryWithSor.tokensOut + : queryWithSor.tokensIn; + const returnAmounts: string[] = Array(returnTokens.length).fill('0'); + let deltas: BigNumberish[] = Array(batchedSwaps.assets.length).fill('0'); + try { + // Onchain query + deltas = await queryBatchSwap( + vaultContract, + queryWithSor.swapType, + batchedSwaps.swaps, + batchedSwaps.assets + ); - if (deltas.length > 0) { - returnTokens.forEach( - (t, i) => - (returnAmounts[i] = - deltas[ - batchedSwaps.assets.indexOf(t.toLowerCase()) - ].toString() ?? Zero.toString()) - ); - } - } catch (err) { - throw new BalancerError(BalancerErrorCode.QUERY_BATCH_SWAP); + if (deltas.length > 0) { + returnTokens.forEach( + (t, i) => + (returnAmounts[i] = + deltas[batchedSwaps.assets.indexOf(t.toLowerCase())].toString() ?? + Zero.toString()) + ); } + } catch (err) { + throw new BalancerError(BalancerErrorCode.QUERY_BATCH_SWAP); + } - return { - returnAmounts, - swaps: batchedSwaps.swaps, - assets: batchedSwaps.assets, - deltas: deltas.map((d) => d.toString()), - }; + return { + returnAmounts, + swaps: batchedSwaps.swaps, + assets: batchedSwaps.assets, + deltas: deltas.map((d) => d.toString()), + }; } /* @@ -118,48 +117,48 @@ Use SOR to get swapInfo for tokenIn>tokenOut. SwapInfos.swaps has path information. */ export async function getSorSwapInfo( - tokenIn: string, - tokenOut: string, - swapType: SwapType, - amount: string, - sor: SOR + tokenIn: string, + tokenOut: string, + swapType: SwapType, + amount: string, + sor: SOR ): Promise { - const swapTypeSOR: SwapTypes = - swapType === SwapType.SwapExactIn - ? SwapTypes.SwapExactIn - : SwapTypes.SwapExactOut; - const swapInfo = await sor.getSwaps( - tokenIn.toLowerCase(), - tokenOut.toLowerCase(), - swapTypeSOR, - amount - ); - return swapInfo; + const swapTypeSOR: SwapTypes = + swapType === SwapType.SwapExactIn + ? SwapTypes.SwapExactIn + : SwapTypes.SwapExactOut; + const swapInfo = await sor.getSwaps( + tokenIn.toLowerCase(), + tokenOut.toLowerCase(), + swapTypeSOR, + amount + ); + return swapInfo; } /* Format multiple individual swaps/assets into a single swap/asset. */ function batchSwaps( - assetArray: string[][], - swaps: BatchSwapStep[][] + assetArray: string[][], + swaps: BatchSwapStep[][] ): { swaps: BatchSwapStep[]; assets: string[] } { - // asset addresses without duplicates - const newAssetArray = [...new Set(assetArray.flat())]; + // asset addresses without duplicates + const newAssetArray = [...new Set(assetArray.flat())]; - // Update indices of each swap to use new asset array - swaps.forEach((swap, i) => { - swap.forEach((poolSwap) => { - poolSwap.assetInIndex = newAssetArray.indexOf( - assetArray[i][poolSwap.assetInIndex] - ); - poolSwap.assetOutIndex = newAssetArray.indexOf( - assetArray[i][poolSwap.assetOutIndex] - ); - }); + // Update indices of each swap to use new asset array + swaps.forEach((swap, i) => { + swap.forEach((poolSwap) => { + poolSwap.assetInIndex = newAssetArray.indexOf( + assetArray[i][poolSwap.assetInIndex] + ); + poolSwap.assetOutIndex = newAssetArray.indexOf( + assetArray[i][poolSwap.assetOutIndex] + ); }); + }); - // Join Swaps into a single batchSwap - const batchedSwaps = swaps.flat(); - return { swaps: batchedSwaps, assets: newAssetArray }; + // Join Swaps into a single batchSwap + const batchedSwaps = swaps.flat(); + return { swaps: batchedSwaps, assets: newAssetArray }; } diff --git a/balancer-js/src/modules/swaps/swap_builder/batch_swap_builder.spec.ts b/balancer-js/src/modules/swaps/swap_builder/batch_swap_builder.spec.ts new file mode 100644 index 000000000..a691d9d2a --- /dev/null +++ b/balancer-js/src/modules/swaps/swap_builder/batch_swap_builder.spec.ts @@ -0,0 +1,47 @@ +import { BatchSwapBuilder } from './batch_swap_builder'; +import { factories } from '@/test/factories'; +import { SwapType } from '../types'; +import { expect } from 'chai'; +import { BigNumber } from '@ethersproject/bignumber'; + +describe('SwapBuilder', () => { + const swapAmountForSwaps = BigNumber.from('1000'); + const returnAmountFromSwaps = BigNumber.from('2000'); + const swapInfo = factories.swapInfo.build({ + swapAmountForSwaps, + returnAmountFromSwaps, + }); + + describe('.setLimits for GIVEN_IN', () => { + const builder = new BatchSwapBuilder(swapInfo, SwapType.SwapExactIn, 1); + + it('for 1 bsp 0.01%', () => { + const maxSlippage = 1; + builder.setLimits(maxSlippage); + expect(builder.limits).to.eql([ + swapAmountForSwaps.toString(), + returnAmountFromSwaps + .mul(1e3 - maxSlippage) + .div(1e3) + .mul(-1) + .toString(), + ]); + }); + }); + + describe('.setLimits for GIVEN_OUT', () => { + const builder = new BatchSwapBuilder(swapInfo, SwapType.SwapExactOut, 1); + + it('for 1 bsp 0.01%', () => { + const maxSlippage = 1; + builder.setLimits(maxSlippage); + expect(builder.limits).to.eql([ + swapAmountForSwaps + .mul(1e3 + maxSlippage) + .div(1e3) + .toString(), + returnAmountFromSwaps.mul(-1).toString(), + ]); + }); + }); +}); diff --git a/balancer-js/src/modules/swaps/swap_builder/batch_swap_builder.ts b/balancer-js/src/modules/swaps/swap_builder/batch_swap_builder.ts new file mode 100644 index 000000000..d77f168d6 --- /dev/null +++ b/balancer-js/src/modules/swaps/swap_builder/batch_swap_builder.ts @@ -0,0 +1,160 @@ +import { BigNumber, BigNumberish } from '@ethersproject/bignumber'; +import { BatchSwap, FundManagement, SwapType } from '../types'; +import { Interface, JsonFragment } from '@ethersproject/abi'; +import { SwapInfo } from '@balancer-labs/sor'; +import { SDKSwapInfo, decorateSorSwapInfo } from './swap_info_decorator'; +import { AddressZero } from '@ethersproject/constants'; +import { swapFragment, relayerResolver, SwapRelayer } from './swap_utils'; + +class BatchSwapBuilder { + private swapInfo: SDKSwapInfo; + funds?: FundManagement; + limits?: BigNumberish[]; + deadline?: BigNumberish; + relayer: SwapRelayer; + readonly functionName = 'batchSwap'; + + /** + * Building swap transaction data + * + * @param swapInfo SOR result + * @param kind + */ + constructor( + swapInfo: SwapInfo, + private readonly kind: SwapType, + private readonly chainId: number + ) { + this.swapInfo = decorateSorSwapInfo(swapInfo); + this.relayer = relayerResolver( + this.swapInfo.tokenIn, + this.swapInfo.tokenOut, + this.chainId + ); + } + + setFunds(userAddress: string): void { + this.funds = { + sender: userAddress, + recipient: userAddress, + fromInternalBalance: false, + toInternalBalance: false, + }; + } + + /** + * @param deadline block timestamp + */ + setDeadline(deadline: BigNumber): void { + this.deadline = deadline; + } + + /** + * Given IN it's the minimum amount we are willing to accept. + * Given OUT it's the fixed amount defined as output. + */ + minAmountOut(maxSlippage: number): BigNumber { + return this.kind === SwapType.SwapExactIn + ? this.swapInfo.amountOutForLimits.min(maxSlippage) + : this.swapInfo.amountOutForLimits.amount; + } + + /** + * Given IN it's the fixed amount we define as input. + * Given OUT it's the maximum amount we are willing to pay for the request. + */ + maxAmountIn(maxSlippage: number): BigNumber { + return this.kind === SwapType.SwapExactOut + ? this.swapInfo.amountInForLimits.max(maxSlippage) + : this.swapInfo.amountInForLimits.amount; + } + + /** + * Calculates limits for token amount. + * Maximum number of tokens to send is a positive number + * Minimum amount of tokens to receive is a negative number + * https://dev.balancer.fi/guides/swaps/batch-swaps + * https://dev.balancer.fi/resources/swaps/batch-swaps#multi-hop-examples + * + * For batchSwap: + * An array of of the maximum net amounts of each asset which can be taken to perform the swap. + * Should the total trade require more than limits[i] tokens to be taken from sender for any i + * then the transaction shall fail. + * + * @param maxSlippage [bps], eg: 1 === 0.01%, 100 === 1% + */ + setLimits(maxSlippage: number): void { + // TODO: This implementation handles only a single input. We might want to extend it for multiple token inputs + this.limits = this.swapInfo.tokenAddresses + .map((token) => { + let amount = BigNumber.from(0); + if (token === this.swapInfo.tokenInForSwaps) { + amount = this.maxAmountIn(maxSlippage); + } + if (token === this.swapInfo.tokenOutFromSwaps) { + amount = this.minAmountOut(maxSlippage).mul(-1); + } + return amount; + }) + .map((limit) => limit.toString().split('.')[0]); + } + + attributes(): BatchSwap { + // TODO: Raise errors when some parameters are missing + if (!this.funds || !this.limits || !this.deadline) { + throw new Error('Uninitialized arguments'); + } + + let attrs: BatchSwap = { + kind: this.kind, + swaps: this.swapInfo.swaps, + assets: this.swapInfo.tokenAddresses, + funds: this.funds, + limits: this.limits, + deadline: this.deadline, + }; + + const fragment = this.fragment(); + + // TODO: Call this logic from a relayer module maybe? Do we actually need to do that? + // additional parameters on a contract: + // https://github.com/balancer-labs/balancer-v2-monorepo/blob/master/pkg/standalone-utils/contracts/relayer/VaultActions.sol#L67 + if (fragment[0].inputs && fragment[0].inputs?.length > 6) { + attrs = { + ...attrs, + value: '0', + outputReferences: [], + }; + } + + return attrs; + } + + data(): string { + const contractInterface = new Interface(this.fragment()); + + return contractInterface.encodeFunctionData( + 'batchSwap', + Object.values(this.attributes()) + ); + } + + value(maxSlippage: number): BigNumber { + let amount = BigNumber.from(0); + if (this.swapInfo.tokenIn === AddressZero) + amount = this.maxAmountIn(maxSlippage); + return amount; + } + + to(): string { + return this.relayer.address; + } + + private fragment(): JsonFragment[] { + return swapFragment(this.relayer).filter( + (f) => f.name === this.functionName + ); + } +} + +export { BatchSwapBuilder }; diff --git a/balancer-js/src/modules/swaps/swap_builder/index.ts b/balancer-js/src/modules/swaps/swap_builder/index.ts new file mode 100644 index 000000000..53903a411 --- /dev/null +++ b/balancer-js/src/modules/swaps/swap_builder/index.ts @@ -0,0 +1,2 @@ +export { SingleSwapBuilder } from './single_swap_builder'; +export { BatchSwapBuilder } from './batch_swap_builder'; diff --git a/balancer-js/src/modules/swaps/swap_builder/single_swap_builder.ts b/balancer-js/src/modules/swaps/swap_builder/single_swap_builder.ts new file mode 100644 index 000000000..1598abfcc --- /dev/null +++ b/balancer-js/src/modules/swaps/swap_builder/single_swap_builder.ts @@ -0,0 +1,154 @@ +import { BigNumber, BigNumberish } from '@ethersproject/bignumber'; +import { FundManagement, SingleSwap, Swap, SwapType } from '../types'; +import { Interface, JsonFragment } from '@ethersproject/abi'; +import { SwapInfo } from '@balancer-labs/sor'; +import { SDKSwapInfo, decorateSorSwapInfo } from './swap_info_decorator'; +import { AddressZero } from '@ethersproject/constants'; +import { swapFragment, relayerResolver, SwapRelayer } from './swap_utils'; + +class SingleSwapBuilder { + private swapInfo: SDKSwapInfo; + funds?: FundManagement; + limit?: BigNumberish; + deadline?: BigNumberish; + relayer: SwapRelayer; + readonly functionName = 'swap'; + + /** + * Building swap transaction data + * + * @param swapInfo SOR result + * @param kind + * @param chainId used to resolve relayer addresses + */ + constructor( + swapInfo: SwapInfo, + private readonly kind: SwapType, + private readonly chainId: number + ) { + this.swapInfo = decorateSorSwapInfo(swapInfo); + this.relayer = relayerResolver( + this.swapInfo.tokenIn, + this.swapInfo.tokenOut, + this.chainId + ); + } + + setFunds(userAddress: string): void { + this.funds = { + sender: userAddress, + recipient: userAddress, + fromInternalBalance: false, + toInternalBalance: false, + }; + } + + /** + * @param deadline block timestamp + */ + setDeadline(deadline: BigNumber): void { + this.deadline = deadline.toString(); + } + + get amount(): BigNumber { + return this.kind === SwapType.SwapExactOut + ? this.swapInfo.amountOutForLimits.amount + : this.swapInfo.amountInForLimits.amount; + } + + /** + * Calculates the limit for token amount. + * https://dev.balancer.fi/guides/swaps/single-swaps + * https://dev.balancer.fi/resources/swaps/single-swap + * + * For swap: + * The meaning of limit depends on the value of kind + * GIVEN_IN: The minimum amount of tokens we would accept to receive from the swap. + * GIVEN_OUT: The maximum amount of tokens we would be sending to swap. + * + * @param maxSlippage [bps], eg: 1 === 0.01%, 100 === 1% + */ + setLimits(maxSlippage: number): void { + this.limit = + this.kind === SwapType.SwapExactIn + ? this.swapInfo.amountOutForLimits.min(maxSlippage).toString() + : this.swapInfo.amountInForLimits.max(maxSlippage).toString(); + } + + get singleSwap(): SingleSwap { + const poolId = this.swapInfo.swaps[0].poolId; + const kind = this.kind; + const assetIn = this.swapInfo.tokenInForSwaps; + const assetOut = this.swapInfo.tokenOutFromSwaps; + const amount = this.amount.toString(); + const userData = '0x'; + + return { + poolId, + kind, + assetIn, + assetOut, + amount, + userData, + }; + } + + attributes(): Swap { + if (!this.funds || !this.limit || !this.deadline) { + throw new Error('Uninitialized arguments'); + } + + // TODO: Raise errors when some parameters are missing + let attrs: Swap = { + request: this.singleSwap, + funds: this.funds, + limit: this.limit, + deadline: this.deadline, + }; + + // TODO: Call this logic from a relayer module maybe? Do we actually need to do that? + // additional parameters on a contract: + // https://github.com/balancer-labs/balancer-v2-monorepo/blob/master/pkg/standalone-utils/contracts/relayer/VaultActions.sol#L44 + const fragment = this.fragment(); + if (fragment[0].inputs && fragment[0].inputs?.length > 4) { + attrs = { + ...attrs, + value: '0', + outputReference: '0', + }; + } + + return attrs; + } + + data(): string { + const contractInterface = new Interface(this.fragment()); + + return contractInterface.encodeFunctionData( + 'swap', + Object.values(this.attributes()) + ); + } + + value(maxSlippage: number): BigNumber { + let amount = BigNumber.from(0); + if (this.swapInfo.tokenIn === AddressZero) + amount = + this.kind === SwapType.SwapExactIn + ? this.swapInfo.amountIn + : this.swapInfo.amountInForLimits.max(maxSlippage); + return amount; + } + + to(): string { + return this.relayer.address; + } + + private fragment(): JsonFragment[] { + return swapFragment(this.relayer).filter( + (f) => f.name === this.functionName + ); + } +} + +export { SingleSwapBuilder }; diff --git a/balancer-js/src/modules/swaps/swap_builder/swap_info_decorator.spec.ts b/balancer-js/src/modules/swaps/swap_builder/swap_info_decorator.spec.ts new file mode 100644 index 000000000..07110d031 --- /dev/null +++ b/balancer-js/src/modules/swaps/swap_builder/swap_info_decorator.spec.ts @@ -0,0 +1,37 @@ +import { decorateSorSwapInfo } from './swap_info_decorator'; +import { factories } from '@/test/factories'; +import { BigNumber } from '@ethersproject/bignumber'; +import { expect } from 'chai'; + +describe('decorated SwapInfo', () => { + const swapAmountForSwaps = BigNumber.from('100'); + const returnAmountFromSwaps = BigNumber.from('200'); + const swapInfo = factories.swapInfo.build({ + swapAmountForSwaps, + returnAmountFromSwaps, + }); + const sdkSwapInfo = decorateSorSwapInfo(swapInfo); + + it('.amountInForLimits is equal to swapAmountForSwaps', () => { + expect(sdkSwapInfo.amountInForLimits.amount).to.eq(swapAmountForSwaps); + }); + + it('.amountOutForLimits is equal to returnAmountFromSwaps', () => { + expect(sdkSwapInfo.amountOutForLimits.amount).to.eq(returnAmountFromSwaps); + }); + + context('when using relayer', () => { + const swapInfo = factories.swapInfo.build({ + // stETH + tokenIn: '0xae7ab96520de3a18e5e111b5eaab095312d7fe84', + }); + const sdkSwapInfo = decorateSorSwapInfo(swapInfo); + + it('.tokenInForSwaps should be a wrapped token address', () => { + expect(sdkSwapInfo.tokenInForSwaps).to.eq( + // wstETH + '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0' + ); + }); + }); +}); diff --git a/balancer-js/src/modules/swaps/swap_builder/swap_info_decorator.ts b/balancer-js/src/modules/swaps/swap_builder/swap_info_decorator.ts new file mode 100644 index 000000000..958594cbf --- /dev/null +++ b/balancer-js/src/modules/swaps/swap_builder/swap_info_decorator.ts @@ -0,0 +1,56 @@ +import { SwapInfo } from '@balancer-labs/sor'; +import { BigNumber } from '@ethersproject/bignumber'; +import { tokenForSwaps } from './swap_utils'; + +interface AmountForLimit { + amount: BigNumber; + max: (slippage: number) => BigNumber; + min: (slippage: number) => BigNumber; +} + +interface SDKSwapInfo extends SwapInfo { + /** Name mapping to improve readability. */ + amountIn: BigNumber; + amountOut: BigNumber; + /** Name mapping for amounts used specifically for limits calculations. */ + amountInForLimits: AmountForLimit; + amountOutForLimits: AmountForLimit; + /** Wrapped token addresses used in the swap. */ + tokenInForSwaps: string; + tokenOutFromSwaps: string; +} + +/** Applies slippage to a number */ +function amountForLimit(amount: BigNumber): AmountForLimit { + return { + amount, + max: (maxSlippage: number): BigNumber => { + return amount.mul(1e3 + maxSlippage).div(1e3); + }, + min: (maxSlippage: number): BigNumber => { + return amount.mul(1e3 - maxSlippage).div(1e3); + }, + }; +} + +function decorateSorSwapInfo(swapInfo: SwapInfo): SDKSwapInfo { + const amountIn = swapInfo.swapAmount; + const amountOut = swapInfo.returnAmount; + const amountInForLimits = swapInfo.swapAmountForSwaps || swapInfo.swapAmount; + const amountOutForLimits = + swapInfo.returnAmountFromSwaps || swapInfo.returnAmount; + const tokenInForSwaps = tokenForSwaps(swapInfo.tokenIn); + const tokenOutFromSwaps = tokenForSwaps(swapInfo.tokenOut); + + return { + ...swapInfo, + amountIn, + amountOut, + amountInForLimits: amountForLimit(amountInForLimits), + amountOutForLimits: amountForLimit(amountOutForLimits), + tokenInForSwaps, + tokenOutFromSwaps, + }; +} + +export { SDKSwapInfo, tokenForSwaps, decorateSorSwapInfo }; diff --git a/balancer-js/src/modules/swaps/swap_builder/swap_utils.ts b/balancer-js/src/modules/swaps/swap_builder/swap_utils.ts new file mode 100644 index 000000000..a857e5837 --- /dev/null +++ b/balancer-js/src/modules/swaps/swap_builder/swap_utils.ts @@ -0,0 +1,119 @@ +import vaultAbi from '@/lib/abi/Vault.json'; +import vaultActionsAbi from '@/lib/abi/VaultActions.json'; +import { JsonFragment } from '@ethersproject/abi'; +import { networkAddresses } from '@/lib/constants/config'; + +/** + * Maps SOR data to get the tokenIn used in swaps. + * Logic related to a relayer wrapping and unwrapping tokens. + * SOR returns list of already wrapped tokenAddresses used in the swap. + * However tokenIn defined as an input is the unwrapped token. + * Note: tokenAddresses are transformed in SOR lib wrapInfo.setWrappedInfo + * TODO: Once PR is merged, this table can be removed. + */ +type WrappedList = { + [key: string]: string; +}; + +const underlyingToWrappedMap: WrappedList = { + // stETH => wstETH + '0xae7ab96520de3a18e5e111b5eaab095312d7fe84': + '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0', + + // AMPL => WAMPL + '0xd46ba6d942050d489dbd938a2c909a5d5039a161': + '0xedb171c18ce90b633db442f2a6f72874093b49ef', + + // aAMPL -> ubAAMPL + '0x1e6bb68acec8fefbd87d192be09bb274170a0548': + '0xF03387d8d0FF326ab586A58E0ab4121d106147DF', +}; + +/** + * Vault swaps are operating on wrapped tokens. When user is sending an unwrapped token, it's wrapped in a relayer. + * SOR is returning an array of tokens already wrapped. + * Converts tokenIn to match tokenIn used in a swap. + * + * TODO: add tokenIn and tokenOut addressed used for swap in the SOR results as tokenInForSwap, tokenOutForSwap + * + * @param token token address + * @returns wrapped token address + */ +function tokenForSwaps(token: string): string { + let wrapped = token; + // eslint-disable-next-line no-prototype-builtins + if (underlyingToWrappedMap.hasOwnProperty(token)) { + wrapped = underlyingToWrappedMap[token as keyof WrappedList]; + } + return wrapped; +} + +export enum Relayers { + vault = 1, + lido = 2, +} + +export interface SwapRelayer { + id: Relayers; + address: string; +} + +/** + * Resolves a contract address for sending swap transaction to. + * Balancer is using relayers to automatically wrap / unwrap tokens not compatibile with ERC20. + */ +function relayerResolver( + assetIn: string, + assetOut: string, + chainId: number +): SwapRelayer { + const { tokens, contracts } = networkAddresses(chainId); + + let to = { + id: Relayers.vault, + address: contracts.vault, + }; + + if (tokens.stETH && contracts.lidoRelayer) + if ([assetIn, assetOut].includes(tokens.stETH)) + to = { + id: Relayers.lido, + address: contracts.lidoRelayer, + }; + + return to; +} + +function swapFragment(relayer: SwapRelayer): JsonFragment[] { + let source = vaultAbi; + if (relayer.id === Relayers.lido) source = vaultActionsAbi; + + const signatures = source.filter( + (fn) => fn.name && ['swap', 'batchSwap'].includes(fn.name) + ); + + return signatures; +} + +function batchSwapFragment( + assetIn: string, + assetOut: string, + chainId: number +): JsonFragment[] { + const vaultSignaturesForSwaps = vaultAbi.filter( + (fn) => fn.name && ['batchSwap'].includes(fn.name) + ); + const relayerSignaturesForSwaps = vaultActionsAbi.filter( + (fn) => fn.name && ['batchSwap'].includes(fn.name) + ); + let returnSignatures = vaultSignaturesForSwaps; + const { tokens, contracts } = networkAddresses(chainId); + if (tokens.stETH && contracts.lidoRelayer) { + if ([assetIn, assetOut].includes(tokens.stETH)) + returnSignatures = relayerSignaturesForSwaps; + } + + return returnSignatures; +} + +export { tokenForSwaps, relayerResolver, swapFragment, batchSwapFragment }; diff --git a/balancer-js/src/modules/swaps/swaps.module.integration.spec.ts b/balancer-js/src/modules/swaps/swaps.module.integration.spec.ts new file mode 100644 index 000000000..e0a093980 --- /dev/null +++ b/balancer-js/src/modules/swaps/swaps.module.integration.spec.ts @@ -0,0 +1,189 @@ +import dotenv from 'dotenv'; +import { expect } from 'chai'; +import { Network, Swaps } from '@/.'; +import { MockPoolDataService } from '@/test/lib/mockPool'; +import { AddressZero, MaxUint256 } from '@ethersproject/constants'; +import { SwapInfo } from '@balancer-labs/sor'; +import hardhat from 'hardhat'; +import { JsonRpcProvider, TransactionReceipt } from '@ethersproject/providers'; +import { BigNumber } from 'ethers'; +import { getForkedPools } from '@/test/lib/mainnetPools'; + +dotenv.config(); + +const { ALCHEMY_URL: jsonRpcUrl } = process.env; +const { ethers } = hardhat; + +const getERC20Contract = (address: string) => { + return new ethers.Contract( + address, + ['function balanceOf(address) view returns (uint256)'], + provider + ); +}; + +const rpcUrl = 'http://127.0.0.1:8545'; +const provider = new ethers.providers.JsonRpcProvider(rpcUrl, 1); +const signer = provider.getSigner(); + +const setupSwaps = async (provider: JsonRpcProvider) => { + const pools = await getForkedPools(provider); + + const swaps = new Swaps({ + network: Network.MAINNET, + rpcUrl, + sor: { + tokenPriceService: 'coingecko', + poolDataService: new MockPoolDataService(pools), + fetchOnChainBalances: true, + }, + }); + + await swaps.fetchPools(); + + return swaps; +}; + +const tokenIn = AddressZero; // ETH +const tokenOut = '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599'; // wBTC +const amount = ethers.utils.parseEther('1'); +const gasPrice = ethers.utils.parseUnits('1', 'gwei'); // not important +const maxPools = 4; +const deadline = MaxUint256; +const maxSlippage = 1; + +describe('swaps execution', async () => { + let swaps: Swaps; + let transactionReceipt: TransactionReceipt; + let balanceBefore: BigNumber; + let balanceExpected: BigNumber; + const tokenOutContract = getERC20Contract(tokenOut); + + // Setup chain + before(async function () { + this.timeout(20000); + + await provider.send('hardhat_reset', [ + { + forking: { + jsonRpcUrl, + }, + }, + ]); + + swaps = await setupSwaps(provider); + }); + + context('single transaction', () => { + before(async function () { + this.timeout(20000); + + balanceBefore = await tokenOutContract.balanceOf( + await signer.getAddress() + ); + + const swapInfo: SwapInfo = await swaps.findRouteGivenIn({ + tokenIn, + tokenOut, + amount, + gasPrice, + maxPools, + }); + + const { to, data, value } = swaps.buildSwap({ + userAddress: await signer.getAddress(), + swapInfo, + kind: 0, + deadline, + maxSlippage, + }); + + const tx = { to, data, value }; + + balanceExpected = swapInfo.returnAmount; + transactionReceipt = await (await signer.sendTransaction(tx)).wait(); + }); + + it('should work', async () => { + expect(transactionReceipt.status).to.eql(1); + }); + + it('balance should increase', async () => { + const balanceAfter: BigNumber = await tokenOutContract.balanceOf( + await signer.getAddress() + ); + + expect(balanceAfter.sub(balanceBefore).toNumber()).to.eql( + balanceExpected.toNumber() + ); + }); + }); + + context('in mempool', () => { + before(async () => { + await provider.send('evm_setAutomine', [false]); + }); + + after(async () => { + await provider.send('evm_setAutomine', [true]); + }); + + context('in mempool', () => { + const getTx = async (amount: BigNumber, userAddress: string) => { + const swapInfo: SwapInfo = await swaps.findRouteGivenIn({ + tokenIn, + tokenOut, + amount, + gasPrice, + maxPools, + }); + + const { to, data, value } = swaps.buildSwap({ + userAddress, + swapInfo, + kind: 0, + deadline, + maxSlippage, + }); + + return { to, data, value }; + }; + + before(async () => { + await provider.send('evm_setAutomine', [false]); + }); + + after(async () => { + await provider.send('evm_setAutomine', [true]); + }); + + it('fails on slippage', async () => { + const frontrunner = provider.getSigner(1); + const frTx = await getTx( + ethers.utils.parseEther('100'), + await frontrunner.getAddress() + ); + const userTx = await getTx( + ethers.utils.parseEther('1'), + await signer.getAddress() + ); + + await frontrunner.sendTransaction(frTx); + + let reason; + try { + await signer.sendTransaction(userTx); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (err: any) { + // Slippage should trigger 507 error: + // https://github.com/balancer-labs/balancer-v2-monorepo/blob/master/pkg/solidity-utils/contracts/helpers/BalancerErrors.sol#L218 + reason = err.reason; + } + + expect(reason).to.contain('BAL#507'); + + await provider.send('evm_mine', []); + }); + }); + }); +}).timeout(20000); diff --git a/balancer-js/src/modules/swaps/swaps.module.spec.ts b/balancer-js/src/modules/swaps/swaps.module.spec.ts index 50afc004d..70acded37 100644 --- a/balancer-js/src/modules/swaps/swaps.module.spec.ts +++ b/balancer-js/src/modules/swaps/swaps.module.spec.ts @@ -1,105 +1,256 @@ import dotenv from 'dotenv'; import { expect } from 'chai'; - +import { factories } from '@/test/factories'; import { - BalancerSdkConfig, - BalancerSdkSorConfig, - Network, - BalancerSDK, - Swaps, + BalancerSdkConfig, + BalancerSdkSorConfig, + Network, + BalancerSDK, + Swaps, } from '@/.'; +import { getNetworkConfig } from '@/modules/sdk.helpers'; import { mockPool, mockPoolDataService } from '@/test/lib/mockPool'; -import { SwapType } from './types'; +import { SwapTransactionRequest, SwapType } from './types'; +import vaultAbi from '@/lib/abi/Vault.json'; +import vaultActionsAbi from '@/lib/abi/VaultActions.json'; +import { Interface } from '@ethersproject/abi'; +import { BigNumber } from '@ethersproject/bignumber'; +import { AddressZero, MaxUint256 } from '@ethersproject/constants'; +import { SwapInfo } from '@balancer-labs/sor'; +import hardhat from 'hardhat'; dotenv.config(); const sorConfig: BalancerSdkSorConfig = { - tokenPriceService: 'coingecko', - poolDataService: mockPoolDataService, - fetchOnChainBalances: false, + tokenPriceService: 'coingecko', + poolDataService: mockPoolDataService, + fetchOnChainBalances: false, }; const sdkConfig: BalancerSdkConfig = { - network: Network.KOVAN, - rpcUrl: `https://kovan.infura.io/v3/${process.env.INFURA}`, - sor: sorConfig, + network: Network.KOVAN, + rpcUrl: `https://kovan.infura.io/v3/${process.env.INFURA}`, + sor: sorConfig, +}; + +const forkedSdkConfig: BalancerSdkConfig = { + network: Network.MAINNET, + rpcUrl: `localhost:8545`, + sor: sorConfig, +}; + +const vault = new Interface(vaultAbi); +const vaultActions = new Interface(vaultActionsAbi); + +const funds = { + fromInternalBalance: false, + recipient: '0x35f5a330FD2F8e521ebd259FA272bA8069590741', + sender: '0x35f5a330FD2F8e521ebd259FA272bA8069590741', + toInternalBalance: false, }; +const assets = [ + '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174', + '0x9a71012B13CA4d3D0Cdc72A177DF3ef03b0E76A3', +]; + +const deadline = '999999999999999999'; // Infinity + describe('swaps module', () => { - context('instantiation', () => { - it('instantiate via module', async () => { - const swaps = new Swaps(sdkConfig); - await swaps.fetchPools(); - const pools = swaps.getPools(); - expect(pools).to.deep.eq([mockPool]); + context('instantiation', () => { + it('instantiate via module', async () => { + const swaps = new Swaps(sdkConfig); + await swaps.fetchPools(); + const pools = swaps.getPools(); + expect(pools).to.deep.eq([mockPool]); + }); + + it('instantiate via SDK', async () => { + const balancer = new BalancerSDK(sdkConfig); + await balancer.swaps.fetchPools(); + const pools = balancer.swaps.getPools(); + expect(pools).to.deep.eq([mockPool]); + }); + }); + + describe('#encodeBatchSwap', () => { + it('should returns an ABI byte string', () => { + const params = { + kind: SwapType.SwapExactIn, + swaps: [ + { + poolId: + '0x7320d680ca9bce8048a286f00a79a2c9f8dcd7b3000100000000000000000044', + assetInIndex: 0, + assetOutIndex: 1, + amount: '10000', + userData: '0x', + }, + { + poolId: + '0x36128d5436d2d70cab39c9af9cce146c38554ff0000100000000000000000008', + assetInIndex: 1, + assetOutIndex: 0, + amount: '0', + userData: '0x', + }, + ], + assets, + funds, + limits: [0, 0], // No limits + deadline, + }; + + expect(Swaps.encodeBatchSwap(params)).to.equal( + '0x945bcec900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000030000000000000000000000000035f5a330fd2f8e521ebd259fa272ba8069590741000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035f5a330fd2f8e521ebd259fa272ba8069590741000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000de0b6b3a763ffff0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001007320d680ca9bce8048a286f00a79a2c9f8dcd7b300010000000000000000004400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000036128d5436d2d70cab39c9af9cce146c38554ff000010000000000000000000800000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000002791bca1f2de4661ed88a30c99a7a9449aa841740000000000000000000000009a71012b13ca4d3d0cdc72a177df3ef03b0e76a3000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + ); + }); + }); + + describe('#encodeSimpleFlashSwap', () => { + it('should returns an ABI byte string', () => { + const params = { + flashLoanAmount: '10000', + poolIds: [ + '0x7320d680ca9bce8048a286f00a79a2c9f8dcd7b3000100000000000000000044', + '0x36128d5436d2d70cab39c9af9cce146c38554ff0000100000000000000000008', + ], + assets, + walletAddress: '0x35f5a330FD2F8e521ebd259FA272bA8069590741', + }; + + expect(Swaps.encodeSimpleFlashSwap(params)).to.equal( + '0x945bcec900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000030000000000000000000000000035f5a330fd2f8e521ebd259fa272ba8069590741000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035f5a330fd2f8e521ebd259fa272ba8069590741000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000de0b6b3a763ffff0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001007320d680ca9bce8048a286f00a79a2c9f8dcd7b300010000000000000000004400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000036128d5436d2d70cab39c9af9cce146c38554ff000010000000000000000000800000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000002791bca1f2de4661ed88a30c99a7a9449aa841740000000000000000000000009a71012b13ca4d3d0cdc72a177df3ef03b0e76a3000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + ); + }); + }); + + describe('#buildTransaction', () => { + const configWithLido = { ...sdkConfig, network: 1 }; + const networkConfig = getNetworkConfig(configWithLido); + const { stETH } = networkConfig.addresses.tokens; + const { contracts } = networkConfig.addresses; + const swaps = new Swaps(configWithLido); + + const subject = (swapInfo: SwapInfo): SwapTransactionRequest => { + return swaps.buildSwap({ + userAddress: '0x2940211793749d9edbDdBa80ac142Fb18BE44257', + swapInfo, + kind: SwapType.SwapExactIn, + deadline: BigNumber.from('0'), + maxSlippage: 1, + }); + }; + + context('singleSwap', () => { + context('when all ERC20', () => { + const swapInfo = factories.swapInfo.build({ + tokenIn: AddressZero, + tokenOut: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', // wBTC }); + const { data, to } = subject(swapInfo); - it('instantiate via SDK', async () => { - const balancer = new BalancerSDK(sdkConfig); - await balancer.swaps.fetchPools(); - const pools = balancer.swaps.getPools(); - expect(pools).to.deep.eq([mockPool]); + it('expect execution via vault', () => { + const decoded = vault.decodeFunctionData('swap', data); + + expect(decoded.length).to.eql(4); + expect(to).to.eql(contracts.vault); }); - }); + }); + + context('when tokenIn is stETH', () => { + const swapInfo = factories.swapInfo.build({ + tokenIn: stETH, + tokenOut: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', // wBTC + }); + const { data, to } = subject(swapInfo); + + it('relayer should be lido', () => { + const decoded = vaultActions.decodeFunctionData('swap', data); - describe('#encodeBatchSwap', () => { - it('should returns an ABI byte string', () => { - const params = { - kind: SwapType.SwapExactIn, - swaps: [ - { - poolId: '0x7320d680ca9bce8048a286f00a79a2c9f8dcd7b3000100000000000000000044', - assetInIndex: 0, - assetOutIndex: 1, - amount: '10000', - userData: '0x', - }, - { - poolId: '0x36128d5436d2d70cab39c9af9cce146c38554ff0000100000000000000000008', - assetInIndex: 1, - assetOutIndex: 0, - amount: '0', - userData: '0x', - }, - ], - assets: [ - '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174', - '0x9a71012B13CA4d3D0Cdc72A177DF3ef03b0E76A3', - ], - funds: { - fromInternalBalance: false, - recipient: '0x35f5a330FD2F8e521ebd259FA272bA8069590741', - sender: '0x35f5a330FD2F8e521ebd259FA272bA8069590741', - toInternalBalance: false, - }, - limits: [0, 0], // No limits - deadline: '999999999999999999', // Infinity - }; - - expect(Swaps.encodeBatchSwap(params)).to.equal( - '0x945bcec900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000030000000000000000000000000035f5a330fd2f8e521ebd259fa272ba8069590741000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035f5a330fd2f8e521ebd259fa272ba8069590741000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000de0b6b3a763ffff0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001007320d680ca9bce8048a286f00a79a2c9f8dcd7b300010000000000000000004400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000036128d5436d2d70cab39c9af9cce146c38554ff000010000000000000000000800000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000002791bca1f2de4661ed88a30c99a7a9449aa841740000000000000000000000009a71012b13ca4d3d0cdc72a177df3ef03b0e76a3000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' - ); + expect(decoded.length).to.eql(6); + expect(to).to.eql(contracts.lidoRelayer); }); + }); }); - describe('#encodeSimpleFlashSwap', () => { - it('should returns an ABI byte string', () => { - const params = { - flashLoanAmount: '10000', - poolIds: [ - '0x7320d680ca9bce8048a286f00a79a2c9f8dcd7b3000100000000000000000044', - '0x36128d5436d2d70cab39c9af9cce146c38554ff0000100000000000000000008', - ], - assets: [ - '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174', - '0x9a71012B13CA4d3D0Cdc72A177DF3ef03b0E76A3', - ], - walletAddress: '0x35f5a330FD2F8e521ebd259FA272bA8069590741', - }; - - expect(Swaps.encodeSimpleFlashSwap(params)).to.equal( - '0x945bcec900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000030000000000000000000000000035f5a330fd2f8e521ebd259fa272ba8069590741000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035f5a330fd2f8e521ebd259fa272ba8069590741000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003600000000000000000000000000000000000000000000000000de0b6b3a763ffff0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001007320d680ca9bce8048a286f00a79a2c9f8dcd7b300010000000000000000004400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000036128d5436d2d70cab39c9af9cce146c38554ff000010000000000000000000800000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000002791bca1f2de4661ed88a30c99a7a9449aa841740000000000000000000000009a71012b13ca4d3d0cdc72a177df3ef03b0e76a3000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' - ); + context('batchSwap', () => { + context('when tokens are ERC20', () => { + const swapInfo = factories.swapInfo + .afterBuild((swap) => { + swap.swaps.push(factories.swapV2.build()); + }) + .build({ + tokenIn: AddressZero, + tokenOut: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', // wBTC + }); + const { data, to } = subject(swapInfo); + + it('expect execution via vault', () => { + const decoded = vault.decodeFunctionData('batchSwap', data); + + expect(decoded.length).to.eql(6); + expect(to).to.eql(contracts.vault); + }); + }); + context('when tokenIn is stETH', () => { + const swapInfo = factories.swapInfo + .afterBuild((swap) => { + swap.swaps.push(factories.swapV2.build()); + }) + .build({ + tokenIn: stETH, + tokenOut: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', // wBTC + }); + const { data, to } = subject(swapInfo); + + it('relayer should be lido', () => { + const decoded = vaultActions.decodeFunctionData('batchSwap', data); + + // batchSwap for a relayer recieves 8 arguments + expect(decoded.length).to.eql(8); + expect(to).to.eql(contracts.lidoRelayer); }); + }); + }); + }); + + describe('full flow', async () => { + const [signer] = await hardhat.ethers.getSigners(); + const swaps = new Swaps(forkedSdkConfig); + + await swaps.fetchPools(); + + const tokenIn = AddressZero; // ETH + const tokenOut = '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599'; // wBTC + const amount = hardhat.ethers.utils.parseEther('1'); + const gasPrice = hardhat.ethers.utils.parseUnits('50', 'gwei'); + const maxPools = 4; + + it('should work', async () => { + const swapInfo: SwapInfo = await swaps.findRouteGivenIn({ + tokenIn, + tokenOut, + amount, + gasPrice, + maxPools, + }); + + const deadline = MaxUint256; + const maxSlippage = 10; + const tx = swaps.buildSwap({ + userAddress: signer.address, + swapInfo, + kind: SwapType.SwapExactIn, + deadline, + maxSlippage, + }); + + const receipt = await signer.sendTransaction(tx); + + console.log(receipt); + + expect(receipt).to.be('1'); }); + }); }); diff --git a/balancer-js/src/modules/swaps/swaps.module.ts b/balancer-js/src/modules/swaps/swaps.module.ts index 85850e466..aa1f9f0cc 100644 --- a/balancer-js/src/modules/swaps/swaps.module.ts +++ b/balancer-js/src/modules/swaps/swaps.module.ts @@ -1,18 +1,21 @@ import { Contract } from '@ethersproject/contracts'; -import { SOR, SubgraphPoolBase, SwapInfo } from '@balancer-labs/sor'; +import { SOR, SubgraphPoolBase, SwapInfo, SwapTypes } from '@balancer-labs/sor'; import { - BatchSwap, - QuerySimpleFlashSwapParameters, - QuerySimpleFlashSwapResponse, - QueryWithSorInput, - QueryWithSorOutput, - SimpleFlashSwapParameters, - SwapType, + BatchSwap, + QuerySimpleFlashSwapParameters, + QuerySimpleFlashSwapResponse, + QueryWithSorInput, + QueryWithSorOutput, + SimpleFlashSwapParameters, + FindRouteParameters, + BuildTransactionParameters, + SwapAttributes, + SwapType, } from './types'; import { - queryBatchSwap, - queryBatchSwapWithSor, - getSorSwapInfo, + queryBatchSwap, + queryBatchSwapWithSor, + getSorSwapInfo, } from './queryBatchSwap'; import { balancerVault } from '@/lib/constants/config'; import { getLimitsForSlippage } from './helpers'; @@ -21,214 +24,305 @@ import { BalancerSdkConfig } from '@/types'; import { SwapInput } from './types'; import { Sor } from '@/modules/sor/sor.module'; import { - convertSimpleFlashSwapToBatchSwapParameters, - querySimpleFlashSwap, + convertSimpleFlashSwapToBatchSwapParameters, + querySimpleFlashSwap, } from './flashSwap'; import { Interface } from '@ethersproject/abi'; +import { + SingleSwapBuilder, + BatchSwapBuilder, +} from '@/modules/swaps/swap_builder'; export class Swaps { - readonly sor: SOR; - - constructor(sorOrConfig: SOR | BalancerSdkConfig) { - if (sorOrConfig instanceof SOR) { - this.sor = sorOrConfig; - } else { - this.sor = new Sor(sorOrConfig); - } - } + readonly sor: SOR; + chainId: number; - static getLimitsForSlippage( - tokensIn: string[], - tokensOut: string[], - swapType: SwapType, - deltas: string[], - assets: string[], - slippage: string - ): string[] { - // TO DO - Check best way to do this? - const limits = getLimitsForSlippage( - tokensIn, - tokensOut, - swapType, - deltas, - assets, - slippage - ); - - return limits.map((l) => l.toString()); + // TODO: sorOrConfig - let's make it more predictable and always pass configuration explicitly + constructor(sorOrConfig: SOR | BalancerSdkConfig) { + if (sorOrConfig instanceof SOR) { + this.sor = sorOrConfig; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + this.chainId = (this.sor.provider)['_network']['chainId']; + } else { + this.sor = new Sor(sorOrConfig); + this.chainId = sorOrConfig.network as number; } + } - /** - * Encode batchSwap in an ABI byte string - * - * [See method for a batchSwap](https://dev.balancer.fi/references/contracts/apis/the-vault#batch-swaps). - * - * _NB: This method doesn't execute a batchSwap -- it returns an [ABI byte string](https://docs.soliditylang.org/en/latest/abi-spec.html) - * containing the data of the function call on a contract, which can then be sent to the network to be executed. - * (ex. [sendTransaction](https://web3js.readthedocs.io/en/v1.2.11/web3-eth.html#sendtransaction)). - * - * @param {BatchSwap} batchSwap - BatchSwap information used for query. - * @param {SwapType} batchSwap.kind - either exactIn or exactOut - * @param {BatchSwapSteps[]} batchSwap.swaps - sequence of swaps - * @param {string[]} batchSwap.assets - array contains the addresses of all assets involved in the swaps - * @param {FundManagement} batchSwap.funds - object containing information about where funds should be taken/sent - * @param {number[]} batchSwap.limits - limits for each token involved in the swap, where either the maximum number of tokens to send (by passing a positive value) or the minimum amount of tokens to receive (by passing a negative value) is specified - * @param {string} batchSwap.deadline - time (in Unix timestamp) after which it will no longer attempt to make a trade - * @returns {string} encodedBatchSwapData - Returns an ABI byte string containing the data of the function call on a contract - */ - static encodeBatchSwap(batchSwap: BatchSwap): string { - const vault = new Interface(vaultAbi); - - return vault.encodeFunctionData('batchSwap', [ - batchSwap.kind, - batchSwap.swaps, - batchSwap.assets, - batchSwap.funds, - batchSwap.limits, - batchSwap.deadline, - ]); - } + static getLimitsForSlippage( + tokensIn: string[], + tokensOut: string[], + swapType: SwapType, + deltas: string[], + assets: string[], + slippage: string + ): string[] { + // TO DO - Check best way to do this? + const limits = getLimitsForSlippage( + tokensIn, + tokensOut, + swapType, + deltas, + assets, + slippage + ); - /** - * Encode simple flash swap into a ABI byte string - * - * A "simple" flash swap is an arbitrage executed with only two tokens and two pools, - * swapping in the first pool and then back in the second pool for a profit. For more - * complex flash swaps, you will have to use the batch swap method. - * - * Learn more: A [Flash Swap](https://dev.balancer.fi/resources/swaps/flash-swaps). - * - * @param {SimpleFlashSwapParameters} params - BatchSwap information used for query. - * @param {string} params.flashLoanAmount - initial input amount for the flash loan (first asset) - * @param {string[]} params.poolIds - array of Balancer pool ids - * @param {string[]} params.assets - array of token addresses - * @param {string} params.walletAddress - array of token addresses - * @returns {string} encodedBatchSwapData - Returns an ABI byte string containing the data of the function call on a contract - */ - static encodeSimpleFlashSwap(params: SimpleFlashSwapParameters): string { - return this.encodeBatchSwap( - convertSimpleFlashSwapToBatchSwapParameters(params) - ); - } + return limits.map((l) => l.toString()); + } - /** - * fetchPools saves updated pools data to SOR internal onChainBalanceCache. - * @param {SubgraphPoolBase[]} [poolsData=[]] If poolsData passed uses this as pools source otherwise fetches from config.subgraphUrl. - * @param {boolean} [isOnChain=true] If isOnChain is true will retrieve all required onChain data via multicall otherwise uses subgraph values. - * @returns {boolean} Boolean indicating whether pools data was fetched correctly (true) or not (false). - */ - async fetchPools(): Promise { - return this.sor.fetchPools(); - } + /** + * Uses SOR to find optimal route for a trading pair and amount + * + * @param FindRouteParameters + * @param FindRouteParameters.tokenIn Address + * @param FindRouteParameters.tokenOut Address + * @param FindRouteParameters.amount BigNumber with a trade amount + * @param FindRouteParameters.gasPrice BigNumber current gas price + * @param FindRouteParameters.maxPools number of pool included in path + * @returns Best trade route information + */ + async findRouteGivenIn({ + tokenIn, + tokenOut, + amount, + gasPrice, + maxPools = 4, + }: FindRouteParameters): Promise { + return this.sor.getSwaps(tokenIn, tokenOut, SwapTypes.SwapExactIn, amount, { + gasPrice, + maxPools, + }); + } - public getPools(): SubgraphPoolBase[] { - return this.sor.getPools(); - } + /** + * Uses SOR to find optimal route for a trading pair and amount + * + * @param FindRouteParameters + * @param FindRouteParameters.tokenIn Address + * @param FindRouteParameters.tokenOut Address + * @param FindRouteParameters.amount BigNumber with a trade amount + * @param FindRouteParameters.gasPrice BigNumber current gas price + * @param FindRouteParameters.maxPools number of pool included in path + * @returns Best trade route information + */ + async findRouteGivenOut({ + tokenIn, + tokenOut, + amount, + gasPrice, + maxPools, + }: FindRouteParameters): Promise { + return this.sor.getSwaps(tokenIn, tokenOut, SwapTypes.SwapExactIn, amount, { + gasPrice, + maxPools, + }); + } - /** - * queryBatchSwap simulates a call to `batchSwap`, returning an array of Vault asset deltas. - * @param batchSwap - BatchSwap information used for query. - * @param {SwapType} batchSwap.kind - either exactIn or exactOut. - * @param {BatchSwapStep[]} batchSwap.swaps - sequence of swaps. - * @param {string[]} batchSwap.assets - array contains the addresses of all assets involved in the swaps. - * @returns {Promise} Returns an array with the net Vault asset balance deltas. Positive amounts represent tokens (or ETH) sent to the - * Vault, and negative amounts represent tokens (or ETH) sent by the Vault. Each delta corresponds to the asset at - * the same index in the `assets` array. - */ - async queryBatchSwap( - batchSwap: Pick - ): Promise { - // TO DO - Pull in a ContractsService and use this to pass Vault to queryBatchSwap. - const vaultContract = new Contract( - balancerVault, - vaultAbi, - this.sor.provider - ); - - return await queryBatchSwap( - vaultContract, - batchSwap.kind, - batchSwap.swaps, - batchSwap.assets - ); - } + /** + * Uses SOR to find optimal route for a trading pair and amount + * + * @param BuildTransactionParameters + * @param BuildTransactionParameters.userAddress Address + * @param BuildTransactionParameters.swapInfo result of route finding + * @param BuildTransactionParameters.kind 0 - givenIn, 1 - givenOut + * @param BuildTransactionParameters.deadline BigNumber block timestamp + * @param BuildTransactionParameters.maxSlippage [bps], eg: 1 === 0.01%, 100 === 1% + * @returns transaction request ready to send with signer.sendTransaction + */ + buildSwap({ + userAddress, + swapInfo, + kind, + deadline, + maxSlippage, + }: BuildTransactionParameters): SwapAttributes { + if (!this.chainId) throw 'Missing network configuration'; - /** - * Uses SOR to create and query a batchSwap. - * @param {QueryWithSorInput} queryWithSor - Swap information used for querying using SOR. - * @param {string[]} queryWithSor.tokensIn - Array of addresses of assets in. - * @param {string[]} queryWithSor.tokensOut - Array of addresses of assets out. - * @param {SwapType} queryWithSor.swapType - Type of Swap, ExactIn/Out. - * @param {string[]} queryWithSor.amounts - Array of amounts used in swap. - * @param {FetchPoolsInput} queryWithSor.fetchPools - Set whether SOR will fetch updated pool info. - * @returns {Promise} Returns amount of tokens swaps along with swap and asset info that can be submitted to a batchSwap call. - */ - async queryBatchSwapWithSor( - queryWithSor: QueryWithSorInput - ): Promise { - // TO DO - Pull in a ContractsService and use this to pass Vault to queryBatchSwap. - const vaultContract = new Contract( - balancerVault, - vaultAbi, - this.sor.provider - ); - - return await queryBatchSwapWithSor( - this.sor, - vaultContract, - queryWithSor - ); - } + // one vs batch (gas cost optimisation when using single swap) + const builder = + swapInfo.swaps.length > 1 + ? new BatchSwapBuilder(swapInfo, kind, this.chainId) + : new SingleSwapBuilder(swapInfo, kind, this.chainId); + builder.setFunds(userAddress); + builder.setDeadline(deadline); + builder.setLimits(maxSlippage); - /** - * Simple interface to test if a simple flash swap is valid and see potential profits. - * - * A "simple" flash swap is an arbitrage executed with only two tokens and two pools, - * swapping in the first pool and then back in the second pool for a profit. For more - * complex flash swaps, you will have to use the batch swap method. - * - * Learn more: A [Flash Swap](https://dev.balancer.fi/resources/swaps/flash-swaps). - * - * _NB: This method doesn't execute a flashSwap - * - * @param {SimpleFlashSwapParameters} params - BatchSwap information used for query. - * @param {string} params.flashLoanAmount - initial input amount for the flash loan (first asset) - * @param {string[]} params.poolIds - array of Balancer pool ids - * @param {string[]} params.assets - array of token addresses - * @returns {Promise<{profits: Record, isProfitable: boolean}>} Returns an ethersjs transaction response - */ - async querySimpleFlashSwap( - params: Omit - ): Promise { - // TO DO - Pull in a ContractsService and use this to pass Vault to queryBatchSwap. - const vaultContract = new Contract( - balancerVault, - vaultAbi, - this.sor.provider - ); - - return await querySimpleFlashSwap({ - ...params, - vaultContract, - }); - } + const to = builder.to(); + const { functionName } = builder; + const attributes = builder.attributes(); + const data = builder.data(); + const value = builder.value(maxSlippage); - /** - * Use SOR to get swapInfo for tokenIn<>tokenOut. - * @param {SwapInput} swapInput - Swap information used for querying using SOR. - * @param {string} swapInput.tokenIn - Addresse of asset in. - * @param {string} swapInput.tokenOut - Addresse of asset out. - * @param {SwapType} swapInput.swapType - Type of Swap, ExactIn/Out. - * @param {string} swapInput.amount - Amount used in swap. - * @returns {Promise} SOR swap info. - */ - async getSorSwap(swapInput: SwapInput): Promise { - return await getSorSwapInfo( - swapInput.tokenIn, - swapInput.tokenOut, - swapInput.swapType, - swapInput.amount, - this.sor - ); - } + return { to, functionName, attributes, data, value }; + } + + /** + * Encode batchSwap in an ABI byte string + * + * [See method for a batchSwap](https://dev.balancer.fi/references/contracts/apis/the-vault#batch-swaps). + * + * _NB: This method doesn't execute a batchSwap -- it returns an [ABI byte string](https://docs.soliditylang.org/en/latest/abi-spec.html) + * containing the data of the function call on a contract, which can then be sent to the network to be executed. + * (ex. [sendTransaction](https://web3js.readthedocs.io/en/v1.2.11/web3-eth.html#sendtransaction)). + * + * @param {BatchSwap} batchSwap - BatchSwap information used for query. + * @param {SwapType} batchSwap.kind - either exactIn or exactOut + * @param {BatchSwapSteps[]} batchSwap.swaps - sequence of swaps + * @param {string[]} batchSwap.assets - array contains the addresses of all assets involved in the swaps + * @param {FundManagement} batchSwap.funds - object containing information about where funds should be taken/sent + * @param {number[]} batchSwap.limits - limits for each token involved in the swap, where either the maximum number of tokens to send (by passing a positive value) or the minimum amount of tokens to receive (by passing a negative value) is specified + * @param {string} batchSwap.deadline - time (in Unix timestamp) after which it will no longer attempt to make a trade + * @returns {string} encodedBatchSwapData - Returns an ABI byte string containing the data of the function call on a contract + */ + static encodeBatchSwap(batchSwap: BatchSwap): string { + const vault = new Interface(vaultAbi); + + return vault.encodeFunctionData('batchSwap', [ + batchSwap.kind, + batchSwap.swaps, + batchSwap.assets, + batchSwap.funds, + batchSwap.limits, + batchSwap.deadline, + ]); + } + + /** + * Encode simple flash swap into a ABI byte string + * + * A "simple" flash swap is an arbitrage executed with only two tokens and two pools, + * swapping in the first pool and then back in the second pool for a profit. For more + * complex flash swaps, you will have to use the batch swap method. + * + * Learn more: A [Flash Swap](https://dev.balancer.fi/resources/swaps/flash-swaps). + * + * @param {SimpleFlashSwapParameters} params - BatchSwap information used for query. + * @param {string} params.flashLoanAmount - initial input amount for the flash loan (first asset) + * @param {string[]} params.poolIds - array of Balancer pool ids + * @param {string[]} params.assets - array of token addresses + * @param {string} params.walletAddress - array of token addresses + * @returns {string} encodedBatchSwapData - Returns an ABI byte string containing the data of the function call on a contract + */ + static encodeSimpleFlashSwap(params: SimpleFlashSwapParameters): string { + return this.encodeBatchSwap( + convertSimpleFlashSwapToBatchSwapParameters(params) + ); + } + + /** + * fetchPools saves updated pools data to SOR internal onChainBalanceCache. + * @param {SubgraphPoolBase[]} [poolsData=[]] If poolsData passed uses this as pools source otherwise fetches from config.subgraphUrl. + * @param {boolean} [isOnChain=true] If isOnChain is true will retrieve all required onChain data via multicall otherwise uses subgraph values. + * @returns {boolean} Boolean indicating whether pools data was fetched correctly (true) or not (false). + */ + async fetchPools(): Promise { + return this.sor.fetchPools(); + } + + public getPools(): SubgraphPoolBase[] { + return this.sor.getPools(); + } + + /** + * queryBatchSwap simulates a call to `batchSwap`, returning an array of Vault asset deltas. + * @param batchSwap - BatchSwap information used for query. + * @param {SwapType} batchSwap.kind - either exactIn or exactOut. + * @param {BatchSwapStep[]} batchSwap.swaps - sequence of swaps. + * @param {string[]} batchSwap.assets - array contains the addresses of all assets involved in the swaps. + * @returns {Promise} Returns an array with the net Vault asset balance deltas. Positive amounts represent tokens (or ETH) sent to the + * Vault, and negative amounts represent tokens (or ETH) sent by the Vault. Each delta corresponds to the asset at + * the same index in the `assets` array. + */ + async queryBatchSwap( + batchSwap: Pick + ): Promise { + // TO DO - Pull in a ContractsService and use this to pass Vault to queryBatchSwap. + const vaultContract = new Contract( + balancerVault, + vaultAbi, + this.sor.provider + ); + + return await queryBatchSwap( + vaultContract, + batchSwap.kind, + batchSwap.swaps, + batchSwap.assets + ); + } + + /** + * Uses SOR to create and query a batchSwap. + * @param {QueryWithSorInput} queryWithSor - Swap information used for querying using SOR. + * @param {string[]} queryWithSor.tokensIn - Array of addresses of assets in. + * @param {string[]} queryWithSor.tokensOut - Array of addresses of assets out. + * @param {SwapType} queryWithSor.swapType - Type of Swap, ExactIn/Out. + * @param {string[]} queryWithSor.amounts - Array of amounts used in swap. + * @param {FetchPoolsInput} queryWithSor.fetchPools - Set whether SOR will fetch updated pool info. + * @returns {Promise} Returns amount of tokens swaps along with swap and asset info that can be submitted to a batchSwap call. + */ + async queryBatchSwapWithSor( + queryWithSor: QueryWithSorInput + ): Promise { + // TO DO - Pull in a ContractsService and use this to pass Vault to queryBatchSwap. + const vaultContract = new Contract( + balancerVault, + vaultAbi, + this.sor.provider + ); + + return await queryBatchSwapWithSor(this.sor, vaultContract, queryWithSor); + } + + /** + * Simple interface to test if a simple flash swap is valid and see potential profits. + * + * A "simple" flash swap is an arbitrage executed with only two tokens and two pools, + * swapping in the first pool and then back in the second pool for a profit. For more + * complex flash swaps, you will have to use the batch swap method. + * + * Learn more: A [Flash Swap](https://dev.balancer.fi/resources/swaps/flash-swaps). + * + * _NB: This method doesn't execute a flashSwap + * + * @param {SimpleFlashSwapParameters} params - BatchSwap information used for query. + * @param {string} params.flashLoanAmount - initial input amount for the flash loan (first asset) + * @param {string[]} params.poolIds - array of Balancer pool ids + * @param {string[]} params.assets - array of token addresses + * @returns {Promise<{profits: Record, isProfitable: boolean}>} Returns an ethersjs transaction response + */ + async querySimpleFlashSwap( + params: Omit + ): Promise { + // TO DO - Pull in a ContractsService and use this to pass Vault to queryBatchSwap. + const vaultContract = new Contract( + balancerVault, + vaultAbi, + this.sor.provider + ); + + return await querySimpleFlashSwap({ + ...params, + vaultContract, + }); + } + + /** + * Use SOR to get swapInfo for tokenIn<>tokenOut. + * @param {SwapInput} swapInput - Swap information used for querying using SOR. + * @param {string} swapInput.tokenIn - Addresse of asset in. + * @param {string} swapInput.tokenOut - Addresse of asset out. + * @param {SwapType} swapInput.swapType - Type of Swap, ExactIn/Out. + * @param {string} swapInput.amount - Amount used in swap. + * @returns {Promise} SOR swap info. + */ + async getSorSwap(swapInput: SwapInput): Promise { + return await getSorSwapInfo( + swapInput.tokenIn, + swapInput.tokenOut, + swapInput.swapType, + swapInput.amount, + this.sor + ); + } } diff --git a/balancer-js/src/modules/swaps/types.ts b/balancer-js/src/modules/swaps/types.ts index 6d2d8a0f1..69ed0e6fa 100644 --- a/balancer-js/src/modules/swaps/types.ts +++ b/balancer-js/src/modules/swaps/types.ts @@ -1,93 +1,128 @@ -import { BigNumberish } from '@ethersproject/bignumber'; +import { SwapInfo } from '@balancer-labs/sor'; +import { BigNumber, BigNumberish } from '@ethersproject/bignumber'; import { Contract } from '@ethersproject/contracts'; export enum SwapType { - SwapExactIn, - SwapExactOut, + SwapExactIn, + SwapExactOut, } export type FundManagement = { - sender: string; - recipient: string; - fromInternalBalance: boolean; - toInternalBalance: boolean; + sender: string; + recipient: string; + fromInternalBalance: boolean; + toInternalBalance: boolean; }; export type SingleSwap = { - poolId: string; - kind: SwapType; - assetIn: string; - assetOut: string; - amount: BigNumberish; - userData: string; + poolId: string; + kind: SwapType; + assetIn: string; + assetOut: string; + amount: BigNumberish; + userData: string; }; export type Swap = { - kind: SwapType; - singleSwap: SingleSwap; - limit: BigNumberish; - deadline: BigNumberish; + request: SingleSwap; + funds: FundManagement; + limit: BigNumberish; + deadline: BigNumberish; + value?: BigNumberish; + outputReference?: BigNumberish; }; export type BatchSwapStep = { - poolId: string; - assetInIndex: number; - assetOutIndex: number; - amount: string; - userData: string; + poolId: string; + assetInIndex: number; + assetOutIndex: number; + amount: string; + userData: string; }; export type BatchSwap = { - kind: SwapType; - swaps: BatchSwapStep[]; - assets: string[]; - funds: FundManagement; - limits: BigNumberish[]; - deadline: BigNumberish; + kind: SwapType; + swaps: BatchSwapStep[]; + assets: string[]; + funds: FundManagement; + limits: BigNumberish[]; + deadline: BigNumberish; + value?: BigNumberish; + outputReferences?: { index: BigNumberish; key: BigNumberish }[]; }; export interface FetchPoolsInput { - fetchPools: boolean; - fetchOnChain: boolean; + fetchPools: boolean; + fetchOnChain: boolean; } export interface QueryWithSorInput { - tokensIn: string[]; - tokensOut: string[]; - swapType: SwapType; - amounts: string[]; - fetchPools: FetchPoolsInput; + tokensIn: string[]; + tokensOut: string[]; + swapType: SwapType; + amounts: string[]; + fetchPools: FetchPoolsInput; } export interface SwapInput { - tokenIn: string; - tokenOut: string; - swapType: SwapType; - amount: string; + tokenIn: string; + tokenOut: string; + swapType: SwapType; + amount: string; } export interface QueryWithSorOutput { - returnAmounts: string[]; - swaps: BatchSwapStep[]; - assets: string[]; - deltas: string[]; + returnAmounts: string[]; + swaps: BatchSwapStep[]; + assets: string[]; + deltas: string[]; } export interface QuerySimpleFlashSwapParameters { - poolIds: string[]; - assets: BatchSwap['assets']; - flashLoanAmount: string; - vaultContract: Contract; + poolIds: string[]; + assets: BatchSwap['assets']; + flashLoanAmount: string; + vaultContract: Contract; } export interface SimpleFlashSwapParameters { - poolIds: string[]; - assets: BatchSwap['assets']; - flashLoanAmount: string; - walletAddress: string; + poolIds: string[]; + assets: BatchSwap['assets']; + flashLoanAmount: string; + walletAddress: string; } export interface QuerySimpleFlashSwapResponse { - profits: Record; - isProfitable: boolean; + profits: Record; + isProfitable: boolean; +} + +export interface FindRouteParameters { + tokenIn: string; + tokenOut: string; + amount: BigNumber; + gasPrice: BigNumber; + maxPools: number; +} + +export interface BuildTransactionParameters { + userAddress: string; + swapInfo: SwapInfo; + kind: SwapType; + deadline: BigNumber; + maxSlippage: number; +} + +export interface SwapTransactionRequest { + to: string; + data: string; + value?: BigNumber; +} + +export interface SwapAttributes { + to: string; + functionName: string; + attributes: Swap | BatchSwap; + data: string; + value?: BigNumber; } diff --git a/balancer-js/src/pool-stable/encoder.ts b/balancer-js/src/pool-stable/encoder.ts index 92424f61b..44a885398 100644 --- a/balancer-js/src/pool-stable/encoder.ts +++ b/balancer-js/src/pool-stable/encoder.ts @@ -2,128 +2,124 @@ import { defaultAbiCoder } from '@ethersproject/abi'; import { BigNumberish } from '@ethersproject/bignumber'; export enum StablePoolJoinKind { - INIT = 0, - EXACT_TOKENS_IN_FOR_BPT_OUT, - TOKEN_IN_FOR_EXACT_BPT_OUT, + INIT = 0, + EXACT_TOKENS_IN_FOR_BPT_OUT, + TOKEN_IN_FOR_EXACT_BPT_OUT, } export enum StablePhantomPoolJoinKind { - INIT = 0, - COLLECT_PROTOCOL_FEES, + INIT = 0, + COLLECT_PROTOCOL_FEES, } export enum StablePoolExitKind { - EXACT_BPT_IN_FOR_ONE_TOKEN_OUT = 0, - EXACT_BPT_IN_FOR_TOKENS_OUT, - BPT_IN_FOR_EXACT_TOKENS_OUT, + EXACT_BPT_IN_FOR_ONE_TOKEN_OUT = 0, + EXACT_BPT_IN_FOR_TOKENS_OUT, + BPT_IN_FOR_EXACT_TOKENS_OUT, } export class StablePoolEncoder { - /** - * Cannot be constructed. - */ - private constructor() { - // eslint-disable-next-line @typescript-eslint/no-empty-function - } + /** + * Cannot be constructed. + */ + private constructor() { + // eslint-disable-next-line @typescript-eslint/no-empty-function + } - /** - * Encodes the userData parameter for providing the initial liquidity to a StablePool - * @param initialBalances - the amounts of tokens to send to the pool to form the initial balances - */ - static joinInit = (amountsIn: BigNumberish[]): string => - defaultAbiCoder.encode( - ['uint256', 'uint256[]'], - [StablePoolJoinKind.INIT, amountsIn] - ); + /** + * Encodes the userData parameter for providing the initial liquidity to a StablePool + * @param initialBalances - the amounts of tokens to send to the pool to form the initial balances + */ + static joinInit = (amountsIn: BigNumberish[]): string => + defaultAbiCoder.encode( + ['uint256', 'uint256[]'], + [StablePoolJoinKind.INIT, amountsIn] + ); - /** - * Encodes the userData parameter for collecting protocol fees for StablePhantomPool - */ - static joinCollectProtocolFees = (): string => - defaultAbiCoder.encode( - ['uint256'], - [StablePhantomPoolJoinKind.COLLECT_PROTOCOL_FEES] - ); + /** + * Encodes the userData parameter for collecting protocol fees for StablePhantomPool + */ + static joinCollectProtocolFees = (): string => + defaultAbiCoder.encode( + ['uint256'], + [StablePhantomPoolJoinKind.COLLECT_PROTOCOL_FEES] + ); - /** - * Encodes the userData parameter for joining a StablePool with exact token inputs - * @param amountsIn - the amounts each of token to deposit in the pool as liquidity - * @param minimumBPT - the minimum acceptable BPT to receive in return for deposited tokens - */ - static joinExactTokensInForBPTOut = ( - amountsIn: BigNumberish[], - minimumBPT: BigNumberish - ): string => - defaultAbiCoder.encode( - ['uint256', 'uint256[]', 'uint256'], - [ - StablePoolJoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT, - amountsIn, - minimumBPT, - ] - ); + /** + * Encodes the userData parameter for joining a StablePool with exact token inputs + * @param amountsIn - the amounts each of token to deposit in the pool as liquidity + * @param minimumBPT - the minimum acceptable BPT to receive in return for deposited tokens + */ + static joinExactTokensInForBPTOut = ( + amountsIn: BigNumberish[], + minimumBPT: BigNumberish + ): string => + defaultAbiCoder.encode( + ['uint256', 'uint256[]', 'uint256'], + [StablePoolJoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT, amountsIn, minimumBPT] + ); - /** - * Encodes the userData parameter for joining a StablePool with to receive an exact amount of BPT - * @param bptAmountOut - the amount of BPT to be minted - * @param enterTokenIndex - the index of the token to be provided as liquidity - */ - static joinTokenInForExactBPTOut = ( - bptAmountOut: BigNumberish, - enterTokenIndex: number - ): string => - defaultAbiCoder.encode( - ['uint256', 'uint256', 'uint256'], - [ - StablePoolJoinKind.TOKEN_IN_FOR_EXACT_BPT_OUT, - bptAmountOut, - enterTokenIndex, - ] - ); + /** + * Encodes the userData parameter for joining a StablePool with to receive an exact amount of BPT + * @param bptAmountOut - the amount of BPT to be minted + * @param enterTokenIndex - the index of the token to be provided as liquidity + */ + static joinTokenInForExactBPTOut = ( + bptAmountOut: BigNumberish, + enterTokenIndex: number + ): string => + defaultAbiCoder.encode( + ['uint256', 'uint256', 'uint256'], + [ + StablePoolJoinKind.TOKEN_IN_FOR_EXACT_BPT_OUT, + bptAmountOut, + enterTokenIndex, + ] + ); - /** - * Encodes the userData parameter for exiting a StablePool by removing a single token in return for an exact amount of BPT - * @param bptAmountIn - the amount of BPT to be burned - * @param enterTokenIndex - the index of the token to removed from the pool - */ - static exitExactBPTInForOneTokenOut = ( - bptAmountIn: BigNumberish, - exitTokenIndex: number - ): string => - defaultAbiCoder.encode( - ['uint256', 'uint256', 'uint256'], - [ - StablePoolExitKind.EXACT_BPT_IN_FOR_ONE_TOKEN_OUT, - bptAmountIn, - exitTokenIndex, - ] - ); + /** + * Encodes the userData parameter for exiting a StablePool by removing a single token in return for an exact amount of BPT + * @param bptAmountIn - the amount of BPT to be burned + * @param enterTokenIndex - the index of the token to removed from the pool + */ + static exitExactBPTInForOneTokenOut = ( + bptAmountIn: BigNumberish, + exitTokenIndex: number + ): string => + defaultAbiCoder.encode( + ['uint256', 'uint256', 'uint256'], + [ + StablePoolExitKind.EXACT_BPT_IN_FOR_ONE_TOKEN_OUT, + bptAmountIn, + exitTokenIndex, + ] + ); - /** - * Encodes the userData parameter for exiting a StablePool by removing tokens in return for an exact amount of BPT - * @param bptAmountIn - the amount of BPT to be burned - */ - static exitExactBPTInForTokensOut = (bptAmountIn: BigNumberish): string => - defaultAbiCoder.encode( - ['uint256', 'uint256'], - [StablePoolExitKind.EXACT_BPT_IN_FOR_TOKENS_OUT, bptAmountIn] - ); + /** + * Encodes the userData parameter for exiting a StablePool by removing tokens in return for an exact amount of BPT + * @param bptAmountIn - the amount of BPT to be burned + */ + static exitExactBPTInForTokensOut = (bptAmountIn: BigNumberish): string => + defaultAbiCoder.encode( + ['uint256', 'uint256'], + [StablePoolExitKind.EXACT_BPT_IN_FOR_TOKENS_OUT, bptAmountIn] + ); - /** - * Encodes the userData parameter for exiting a StablePool by removing exact amounts of tokens - * @param amountsOut - the amounts of each token to be withdrawn from the pool - * @param maxBPTAmountIn - the minimum acceptable BPT to burn in return for withdrawn tokens - */ - static exitBPTInForExactTokensOut = ( - amountsOut: BigNumberish[], - maxBPTAmountIn: BigNumberish - ): string => - defaultAbiCoder.encode( - ['uint256', 'uint256[]', 'uint256'], - [ - StablePoolExitKind.BPT_IN_FOR_EXACT_TOKENS_OUT, - amountsOut, - maxBPTAmountIn, - ] - ); + /** + * Encodes the userData parameter for exiting a StablePool by removing exact amounts of tokens + * @param amountsOut - the amounts of each token to be withdrawn from the pool + * @param maxBPTAmountIn - the minimum acceptable BPT to burn in return for withdrawn tokens + */ + static exitBPTInForExactTokensOut = ( + amountsOut: BigNumberish[], + maxBPTAmountIn: BigNumberish + ): string => + defaultAbiCoder.encode( + ['uint256', 'uint256[]', 'uint256'], + [ + StablePoolExitKind.BPT_IN_FOR_EXACT_TOKENS_OUT, + amountsOut, + maxBPTAmountIn, + ] + ); } diff --git a/balancer-js/src/pool-utils/poolId.ts b/balancer-js/src/pool-utils/poolId.ts index 2e5e70a95..5cc2dd1e8 100644 --- a/balancer-js/src/pool-utils/poolId.ts +++ b/balancer-js/src/pool-utils/poolId.ts @@ -8,17 +8,17 @@ import invariant from 'tiny-invariant'; * @returns an object with the decomposed poolId */ export const splitPoolId = ( - poolId: string + poolId: string ): { - address: string; - specialization: PoolSpecialization; - nonce: BigNumber; + address: string; + specialization: PoolSpecialization; + nonce: BigNumber; } => { - return { - address: getPoolAddress(poolId), - specialization: getPoolSpecialization(poolId), - nonce: getPoolNonce(poolId), - }; + return { + address: getPoolAddress(poolId), + specialization: getPoolSpecialization(poolId), + nonce: getPoolNonce(poolId), + }; }; /** @@ -27,8 +27,8 @@ export const splitPoolId = ( * @returns the pool's address */ export const getPoolAddress = (poolId: string): string => { - invariant(poolId.length === 66, 'Invalid poolId length'); - return poolId.slice(0, 42); + invariant(poolId.length === 66, 'Invalid poolId length'); + return poolId.slice(0, 42); }; /** @@ -37,13 +37,13 @@ export const getPoolAddress = (poolId: string): string => { * @returns the pool's specialization */ export const getPoolSpecialization = (poolId: string): PoolSpecialization => { - invariant(poolId.length === 66, 'Invalid poolId length'); + invariant(poolId.length === 66, 'Invalid poolId length'); - // Only have 3 pool specializations so we can just pull the relevant character - const specializationCode = parseInt(poolId[45]); - invariant(specializationCode < 3, 'Invalid pool specialization'); + // Only have 3 pool specializations so we can just pull the relevant character + const specializationCode = parseInt(poolId[45]); + invariant(specializationCode < 3, 'Invalid pool specialization'); - return specializationCode; + return specializationCode; }; /** @@ -52,6 +52,6 @@ export const getPoolSpecialization = (poolId: string): PoolSpecialization => { * @returns the pool's nonce */ export const getPoolNonce = (poolId: string): BigNumber => { - invariant(poolId.length === 66, 'Invalid poolId length'); - return BigNumber.from(`0x${poolId.slice(46)}`); + invariant(poolId.length === 66, 'Invalid poolId length'); + return BigNumber.from(`0x${poolId.slice(46)}`); }; diff --git a/balancer-js/src/pool-weighted/encoder.ts b/balancer-js/src/pool-weighted/encoder.ts index a5c93dbc7..018b1aff9 100644 --- a/balancer-js/src/pool-weighted/encoder.ts +++ b/balancer-js/src/pool-weighted/encoder.ts @@ -2,147 +2,141 @@ import { defaultAbiCoder } from '@ethersproject/abi'; import { BigNumberish } from '@ethersproject/bignumber'; export enum WeightedPoolJoinKind { - INIT = 0, - EXACT_TOKENS_IN_FOR_BPT_OUT, - TOKEN_IN_FOR_EXACT_BPT_OUT, - ALL_TOKENS_IN_FOR_EXACT_BPT_OUT, + INIT = 0, + EXACT_TOKENS_IN_FOR_BPT_OUT, + TOKEN_IN_FOR_EXACT_BPT_OUT, + ALL_TOKENS_IN_FOR_EXACT_BPT_OUT, } export enum WeightedPoolExitKind { - EXACT_BPT_IN_FOR_ONE_TOKEN_OUT = 0, - EXACT_BPT_IN_FOR_TOKENS_OUT, - BPT_IN_FOR_EXACT_TOKENS_OUT, - MANAGEMENT_FEE_TOKENS_OUT, + EXACT_BPT_IN_FOR_ONE_TOKEN_OUT = 0, + EXACT_BPT_IN_FOR_TOKENS_OUT, + BPT_IN_FOR_EXACT_TOKENS_OUT, + MANAGEMENT_FEE_TOKENS_OUT, } export class WeightedPoolEncoder { - /** - * Cannot be constructed. - */ - private constructor() { - // eslint-disable-next-line @typescript-eslint/no-empty-function - } + /** + * Cannot be constructed. + */ + private constructor() { + // eslint-disable-next-line @typescript-eslint/no-empty-function + } - /** - * Encodes the userData parameter for providing the initial liquidity to a WeightedPool - * @param initialBalances - the amounts of tokens to send to the pool to form the initial balances - */ - static joinInit = (amountsIn: BigNumberish[]): string => - defaultAbiCoder.encode( - ['uint256', 'uint256[]'], - [WeightedPoolJoinKind.INIT, amountsIn] - ); + /** + * Encodes the userData parameter for providing the initial liquidity to a WeightedPool + * @param initialBalances - the amounts of tokens to send to the pool to form the initial balances + */ + static joinInit = (amountsIn: BigNumberish[]): string => + defaultAbiCoder.encode( + ['uint256', 'uint256[]'], + [WeightedPoolJoinKind.INIT, amountsIn] + ); - /** - * Encodes the userData parameter for joining a WeightedPool with exact token inputs - * @param amountsIn - the amounts each of token to deposit in the pool as liquidity - * @param minimumBPT - the minimum acceptable BPT to receive in return for deposited tokens - */ - static joinExactTokensInForBPTOut = ( - amountsIn: BigNumberish[], - minimumBPT: BigNumberish - ): string => - defaultAbiCoder.encode( - ['uint256', 'uint256[]', 'uint256'], - [ - WeightedPoolJoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT, - amountsIn, - minimumBPT, - ] - ); + /** + * Encodes the userData parameter for joining a WeightedPool with exact token inputs + * @param amountsIn - the amounts each of token to deposit in the pool as liquidity + * @param minimumBPT - the minimum acceptable BPT to receive in return for deposited tokens + */ + static joinExactTokensInForBPTOut = ( + amountsIn: BigNumberish[], + minimumBPT: BigNumberish + ): string => + defaultAbiCoder.encode( + ['uint256', 'uint256[]', 'uint256'], + [WeightedPoolJoinKind.EXACT_TOKENS_IN_FOR_BPT_OUT, amountsIn, minimumBPT] + ); - /** - * Encodes the userData parameter for joining a WeightedPool with a single token to receive an exact amount of BPT - * @param bptAmountOut - the amount of BPT to be minted - * @param enterTokenIndex - the index of the token to be provided as liquidity - */ - static joinTokenInForExactBPTOut = ( - bptAmountOut: BigNumberish, - enterTokenIndex: number - ): string => - defaultAbiCoder.encode( - ['uint256', 'uint256', 'uint256'], - [ - WeightedPoolJoinKind.TOKEN_IN_FOR_EXACT_BPT_OUT, - bptAmountOut, - enterTokenIndex, - ] - ); + /** + * Encodes the userData parameter for joining a WeightedPool with a single token to receive an exact amount of BPT + * @param bptAmountOut - the amount of BPT to be minted + * @param enterTokenIndex - the index of the token to be provided as liquidity + */ + static joinTokenInForExactBPTOut = ( + bptAmountOut: BigNumberish, + enterTokenIndex: number + ): string => + defaultAbiCoder.encode( + ['uint256', 'uint256', 'uint256'], + [ + WeightedPoolJoinKind.TOKEN_IN_FOR_EXACT_BPT_OUT, + bptAmountOut, + enterTokenIndex, + ] + ); - /** - * Encodes the userData parameter for joining a WeightedPool proportionally to receive an exact amount of BPT - * @param bptAmountOut - the amount of BPT to be minted - */ - static joinAllTokensInForExactBPTOut = ( - bptAmountOut: BigNumberish - ): string => - defaultAbiCoder.encode( - ['uint256', 'uint256'], - [WeightedPoolJoinKind.ALL_TOKENS_IN_FOR_EXACT_BPT_OUT, bptAmountOut] - ); + /** + * Encodes the userData parameter for joining a WeightedPool proportionally to receive an exact amount of BPT + * @param bptAmountOut - the amount of BPT to be minted + */ + static joinAllTokensInForExactBPTOut = (bptAmountOut: BigNumberish): string => + defaultAbiCoder.encode( + ['uint256', 'uint256'], + [WeightedPoolJoinKind.ALL_TOKENS_IN_FOR_EXACT_BPT_OUT, bptAmountOut] + ); - /** - * Encodes the userData parameter for exiting a WeightedPool by removing a single token in return for an exact amount of BPT - * @param bptAmountIn - the amount of BPT to be burned - * @param enterTokenIndex - the index of the token to removed from the pool - */ - static exitExactBPTInForOneTokenOut = ( - bptAmountIn: BigNumberish, - exitTokenIndex: number - ): string => - defaultAbiCoder.encode( - ['uint256', 'uint256', 'uint256'], - [ - WeightedPoolExitKind.EXACT_BPT_IN_FOR_ONE_TOKEN_OUT, - bptAmountIn, - exitTokenIndex, - ] - ); + /** + * Encodes the userData parameter for exiting a WeightedPool by removing a single token in return for an exact amount of BPT + * @param bptAmountIn - the amount of BPT to be burned + * @param enterTokenIndex - the index of the token to removed from the pool + */ + static exitExactBPTInForOneTokenOut = ( + bptAmountIn: BigNumberish, + exitTokenIndex: number + ): string => + defaultAbiCoder.encode( + ['uint256', 'uint256', 'uint256'], + [ + WeightedPoolExitKind.EXACT_BPT_IN_FOR_ONE_TOKEN_OUT, + bptAmountIn, + exitTokenIndex, + ] + ); - /** - * Encodes the userData parameter for exiting a WeightedPool by removing tokens in return for an exact amount of BPT - * @param bptAmountIn - the amount of BPT to be burned - */ - static exitExactBPTInForTokensOut = (bptAmountIn: BigNumberish): string => - defaultAbiCoder.encode( - ['uint256', 'uint256'], - [WeightedPoolExitKind.EXACT_BPT_IN_FOR_TOKENS_OUT, bptAmountIn] - ); + /** + * Encodes the userData parameter for exiting a WeightedPool by removing tokens in return for an exact amount of BPT + * @param bptAmountIn - the amount of BPT to be burned + */ + static exitExactBPTInForTokensOut = (bptAmountIn: BigNumberish): string => + defaultAbiCoder.encode( + ['uint256', 'uint256'], + [WeightedPoolExitKind.EXACT_BPT_IN_FOR_TOKENS_OUT, bptAmountIn] + ); - /** - * Encodes the userData parameter for exiting a WeightedPool by removing exact amounts of tokens - * @param amountsOut - the amounts of each token to be withdrawn from the pool - * @param maxBPTAmountIn - the minimum acceptable BPT to burn in return for withdrawn tokens - */ - static exitBPTInForExactTokensOut = ( - amountsOut: BigNumberish[], - maxBPTAmountIn: BigNumberish - ): string => - defaultAbiCoder.encode( - ['uint256', 'uint256[]', 'uint256'], - [ - WeightedPoolExitKind.BPT_IN_FOR_EXACT_TOKENS_OUT, - amountsOut, - maxBPTAmountIn, - ] - ); + /** + * Encodes the userData parameter for exiting a WeightedPool by removing exact amounts of tokens + * @param amountsOut - the amounts of each token to be withdrawn from the pool + * @param maxBPTAmountIn - the minimum acceptable BPT to burn in return for withdrawn tokens + */ + static exitBPTInForExactTokensOut = ( + amountsOut: BigNumberish[], + maxBPTAmountIn: BigNumberish + ): string => + defaultAbiCoder.encode( + ['uint256', 'uint256[]', 'uint256'], + [ + WeightedPoolExitKind.BPT_IN_FOR_EXACT_TOKENS_OUT, + amountsOut, + maxBPTAmountIn, + ] + ); } export class ManagedPoolEncoder { - /** - * Cannot be constructed. - */ - private constructor() { - // eslint-disable-next-line @typescript-eslint/no-empty-function - } + /** + * Cannot be constructed. + */ + private constructor() { + // eslint-disable-next-line @typescript-eslint/no-empty-function + } - /** - * Encodes the userData parameter for exiting a ManagedPool for withdrawing management fees. - * This can only be done by the pool owner. - */ - static exitForManagementFees = (): string => - defaultAbiCoder.encode( - ['uint256'], - [WeightedPoolExitKind.MANAGEMENT_FEE_TOKENS_OUT] - ); + /** + * Encodes the userData parameter for exiting a ManagedPool for withdrawing management fees. + * This can only be done by the pool owner. + */ + static exitForManagementFees = (): string => + defaultAbiCoder.encode( + ['uint256'], + [WeightedPoolExitKind.MANAGEMENT_FEE_TOKENS_OUT] + ); } diff --git a/balancer-js/src/pool-weighted/normalizedWeights.ts b/balancer-js/src/pool-weighted/normalizedWeights.ts index 9825e1823..3c15e1d4a 100644 --- a/balancer-js/src/pool-weighted/normalizedWeights.ts +++ b/balancer-js/src/pool-weighted/normalizedWeights.ts @@ -11,27 +11,27 @@ const MaxWeightedTokens = 100; * @returns an equivalent set of normalized weights */ export function toNormalizedWeights(weights: BigNumber[]): BigNumber[] { - // When the number is exactly equal to the max, normalizing with common inputs - // leads to a value < 0.01, which reverts. In this case fill in the weights exactly. - if (weights.length == MaxWeightedTokens) { - return Array(MaxWeightedTokens).fill(ONE.div(MaxWeightedTokens)); - } + // When the number is exactly equal to the max, normalizing with common inputs + // leads to a value < 0.01, which reverts. In this case fill in the weights exactly. + if (weights.length == MaxWeightedTokens) { + return Array(MaxWeightedTokens).fill(ONE.div(MaxWeightedTokens)); + } - const sum = weights.reduce((total, weight) => total.add(weight), Zero); - if (sum.eq(ONE)) return weights; + const sum = weights.reduce((total, weight) => total.add(weight), Zero); + if (sum.eq(ONE)) return weights; - const normalizedWeights = []; - let normalizedSum = Zero; - for (let index = 0; index < weights.length; index++) { - if (index < weights.length - 1) { - normalizedWeights[index] = weights[index].mul(ONE).div(sum); - normalizedSum = normalizedSum.add(normalizedWeights[index]); - } else { - normalizedWeights[index] = ONE.sub(normalizedSum); - } + const normalizedWeights = []; + let normalizedSum = Zero; + for (let index = 0; index < weights.length; index++) { + if (index < weights.length - 1) { + normalizedWeights[index] = weights[index].mul(ONE).div(sum); + normalizedSum = normalizedSum.add(normalizedWeights[index]); + } else { + normalizedWeights[index] = ONE.sub(normalizedSum); } + } - return normalizedWeights; + return normalizedWeights; } /** @@ -40,9 +40,9 @@ export function toNormalizedWeights(weights: BigNumber[]): BigNumber[] { * @returns a boolean of whether the weights are normalized */ export const isNormalizedWeights = (weights: BigNumberish[]): boolean => { - const totalWeight = weights.reduce( - (total: BigNumber, weight) => total.add(weight), - Zero - ); - return totalWeight.eq(ONE); + const totalWeight = weights.reduce( + (total: BigNumber, weight) => total.add(weight), + Zero + ); + return totalWeight.eq(ONE); }; diff --git a/balancer-js/src/test/factories/index.ts b/balancer-js/src/test/factories/index.ts new file mode 100644 index 000000000..cbf064da9 --- /dev/null +++ b/balancer-js/src/test/factories/index.ts @@ -0,0 +1,5 @@ +import * as sor from './sor'; + +const factories = { ...sor }; + +export { factories }; diff --git a/balancer-js/src/test/factories/sor.ts b/balancer-js/src/test/factories/sor.ts new file mode 100644 index 000000000..bc2555081 --- /dev/null +++ b/balancer-js/src/test/factories/sor.ts @@ -0,0 +1,83 @@ +import { Factory } from 'fishery'; +import { + SubgraphPoolBase, + SubgraphToken, + SwapInfo, + SwapV2, +} from '@balancer-labs/sor'; +import { BigNumber } from '@ethersproject/bignumber'; + +const swapV2 = Factory.define(() => ({ + poolId: '0xe2957c36816c1033e15dd3149ddf2508c3cfe79076ce4bde6cb3ecd34d4084b4', + assetInIndex: 0, + assetOutIndex: 1, + amount: '1000000000000000000', + userData: '0x', +})); + +const swapInfo = Factory.define(() => ({ + swaps: [swapV2.build()], + tokenAddresses: [ + '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0', + '0x0000000000000000000000000000000000000000', + ], + tokenIn: '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0', + tokenOut: '0x0000000000000000000000000000000000000000', + marketSp: '1', + swapAmount: BigNumber.from('1000000000000000000'), + swapAmountForSwaps: BigNumber.from('1000000000000000000'), + returnAmount: BigNumber.from('1000000000000000000'), + returnAmountFromSwaps: BigNumber.from('1000000000000000000'), + returnAmountConsideringFees: BigNumber.from('1000000000000000000'), +})); + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const namedTokens: Record = { + wETH: { + address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + decimals: 18, + }, + wBTC: { + address: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', + decimals: 8, + }, +}; + +const subgraphToken = Factory.define(({ transientParams }) => { + const { symbol } = transientParams; + const namedToken = namedTokens[symbol]; + + return { + ...namedToken, + balance: '1', + priceRate: '1', + weight: '0.5', + }; +}); + +const subgraphPoolBase = Factory.define( + ({ params, afterBuild }) => { + afterBuild((pool) => { + pool.tokensList = pool.tokens.map((t) => t.address); + }); + + const tokens = params.tokens || [ + subgraphToken.transient({ symbol: 'wETH' }).build(), + subgraphToken.transient({ symbol: 'wBTC' }).build(), + ]; + + return { + id: '0xa6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e', + address: '0xa6f548df93de924d73be7d25dc02554c6bd66db5', + poolType: 'Weighted', + swapFee: '0.001', + swapEnabled: true, + tokens, + tokensList: [], + totalWeight: '1', + totalShares: '1', + }; + } +); + +export { swapInfo, swapV2, subgraphPoolBase, subgraphToken }; diff --git a/balancer-js/src/test/lib/MockProvider.ts b/balancer-js/src/test/lib/MockProvider.ts index ad2e962ff..2aa07f74c 100644 --- a/balancer-js/src/test/lib/MockProvider.ts +++ b/balancer-js/src/test/lib/MockProvider.ts @@ -5,14 +5,14 @@ import { Network } from '../..'; * This MockProvider serves to prevent/catch external calls the Provider might make. */ export default class MockProvider extends BaseProvider { - constructor(network = Network.KOVAN) { - super(network); - } + constructor(network = Network.KOVAN) { + super(network); + } - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types - call = function () { - throw new Error( - 'MockProvider: API calls are blocked! Please stub out your method' - ); - }; + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + call = function () { + throw new Error( + 'MockProvider: API calls are blocked! Please stub out your method' + ); + }; } diff --git a/balancer-js/src/test/lib/constants.ts b/balancer-js/src/test/lib/constants.ts index 39b9ac5ac..f40246abb 100644 --- a/balancer-js/src/test/lib/constants.ts +++ b/balancer-js/src/test/lib/constants.ts @@ -2,249 +2,249 @@ import { Network } from '@/lib/constants/network'; import { AddressZero } from '@ethersproject/constants'; export const ADDRESSES = { - [Network.MAINNET]: { - BatchRelayer: { - address: '0xdcdbf71A870cc60C6F9B621E28a7D3Ffd6Dd4965', - }, - ETH: { - address: AddressZero, - decimals: 18, - symbol: 'ETH', - }, - BAL: { - address: '0xba100000625a3754423978a60c9317c58a424e3d', - decimals: 18, - symbol: 'BAL', - }, - USDC: { - address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', - decimals: 6, - symbol: 'USDC', - }, - WBTC: { - address: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', - decimals: 8, - symbol: 'WBTC', - }, - WETH: { - address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', - decimals: 18, - symbol: 'WETH', - }, - DAI: { - address: '0x6b175474e89094c44da98b954eedeac495271d0f', - decimals: 18, - symbol: 'DAI', - }, - STETH: { - address: '0xae7ab96520de3a18e5e111b5eaab095312d7fe84', - decimals: 18, - symbol: 'STETH', - }, - wSTETH: { - address: '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0', - decimals: 18, - symbol: 'wSTETH', - }, - bbausd: { - address: '0x7b50775383d3d6f0215a8f290f2c9e2eebbeceb2', - decimals: 18, - symbol: 'bbausd', - }, - bbausdc: { - address: '0x9210F1204b5a24742Eba12f710636D76240dF3d0', - decimals: 18, - symbol: 'bbausdc', - }, - waDAI: { - address: '0x02d60b84491589974263d922d9cc7a3152618ef6', - decimals: 18, - symbol: 'waDAI', - }, - waUSDC: { - address: '0xd093fa4fb80d09bb30817fdcd442d4d02ed3e5de', - decimals: 6, - symbol: 'waUSDC', - }, - }, - [Network.KOVAN]: { - // Visit https://balancer-faucet.on.fleek.co/#/faucet for test tokens - BatchRelayer: { - address: '0x41B953164995c11C81DA73D212ED8Af25741b7Ac', - }, - ETH: { - address: AddressZero, - decimals: 18, - symbol: 'ETH', - }, - BAL: { - address: '0x41286Bb1D3E870f3F750eB7E1C25d7E48c8A1Ac7', - decimals: 18, - symbol: 'BAL', - }, - USDC: { - address: '0xc2569dd7d0fd715B054fBf16E75B001E5c0C1115', - decimals: 6, - symbol: 'USDC', - }, - WBTC: { - address: '0x1C8E3Bcb3378a443CC591f154c5CE0EBb4dA9648', - decimals: 8, - symbol: 'WBTC', - }, - WETH: { - address: '0xdFCeA9088c8A88A76FF74892C1457C17dfeef9C1', - decimals: 18, - symbol: 'WETH', - }, - DAI: { - address: '0x04DF6e4121c27713ED22341E7c7Df330F56f289B', - decimals: 18, - symbol: 'DAI', - }, - STETH: { - address: '0x4803bb90d18a1cb7a2187344fe4feb0e07878d05', - decimals: 18, - symbol: 'STETH', - }, - wSTETH: { - address: '0xa387b91e393cfb9356a460370842bc8dbb2f29af', - decimals: 18, - symbol: 'wSTETH', - }, - USDT_from_AAVE: { - address: '0x13512979ade267ab5100878e2e0f485b568328a4', - decimals: 6, - symbol: 'USDT_from_AAVE', - }, - aUSDT: { - address: '0xe8191aacfcdb32260cda25830dc6c9342142f310', - decimals: 6, - symbol: 'aUSDT', - }, - bUSDT: { - address: '0xe667d48618e71c2a02e4a1b66ed9def1426938b6', - decimals: 18, - symbol: 'bUSDT', - }, - USDC_from_AAVE: { - address: '0xe22da380ee6b445bb8273c81944adeb6e8450422', - decimals: 6, - symbol: 'USDC_from_AAVE', - }, - aUSDC: { - address: '0x0fbddc06a4720408a2f5eb78e62bc31ac6e2a3c4', - decimals: 6, - symbol: 'aUSDC', - }, - DAI_from_AAVE: { - address: '0xff795577d9ac8bd7d90ee22b6c1703490b6512fd', - decimals: 18, - symbol: 'DAI_from_AAVE', - }, - bDAI: { - address: '0xfcccb77a946b6a3bd59d149f083b5bfbb8004d6d', - decimals: 18, - symbol: 'bDAI', - }, - STABAL3: { - address: '0x8fd162f338b770f7e879030830cde9173367f301', - decimals: 18, - symbol: 'STABAL3', - }, - }, - [Network.POLYGON]: { - MATIC: { - address: AddressZero, - decimals: 18, - symbol: 'MATIC', - }, - LINK: { - address: '0x53E0bca35eC356BD5ddDFebbD1Fc0fD03FaBad39', - decimals: 18, - symbol: 'LINK', - }, - BAL: { - address: '0x9a71012b13ca4d3d0cdc72a177df3ef03b0e76a3', - decimals: 18, - symbol: 'BAL', - }, - USDC: { - address: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174', - decimals: 6, - symbol: 'USDC', - }, - WBTC: { - address: '0x1bfd67037b42cf73acf2047067bd4f2c47d9bfd6', - decimals: 8, - symbol: 'WBTC', - }, - WETH: { - address: '0x7ceb23fd6bc0add59e62ac25578270cff1b9f619', - decimals: 18, - symbol: 'WETH', - }, - DAI: { - address: '0x8f3cf7ad23cd3cadbd9735aff958023239c6a063', - decimals: 18, - symbol: 'DAI', - }, - STETH: { - address: '0xae7ab96520de3a18e5e111b5eaab095312d7fe84', - decimals: 18, - symbol: 'STETH', - }, - stUSD_PLUS: { - address: '0x5a5c6aa6164750b530b8f7658b827163b3549a4d', - decimals: 6, - symbol: 'stUSD+', - }, - bstUSD_PLUS: { - address: '0x1aafc31091d93c3ff003cff5d2d8f7ba2e728425', - decimals: 18, - symbol: 'bstUSD+', - }, - USD_PLUS: { - address: '0x5d9d8509c522a47d9285b9e4e9ec686e6a580850', - decimals: 6, - symbol: 'USD_PLUS', - }, - USDT: { - address: '0xc2132D05D31c914a87C6611C10748AEb04B58e8F', - decimals: 6, - symbol: 'USDT', - }, - DHT: { - address: '0x8C92e38eCA8210f4fcBf17F0951b198Dd7668292', - decimals: 18, - symbol: 'DHT', - }, - dUSD: { - address: '0xbAe28251B2a4E621aA7e20538c06DEe010Bc06DE', - decimals: 18, - symbol: 'dUSD', - }, - }, - [Network.ARBITRUM]: { - WETH: { - address: '0x82af49447d8a07e3bd95bd0d56f35241523fbab1', - decimals: 18, - symbol: 'WETH', - }, - BAL: { - address: '0x040d1edc9569d4bab2d15287dc5a4f10f56a56b8', - decimals: 18, - symbol: 'BAL', - }, - USDC: { - address: '0xff970a61a04b1ca14834a43f5de4533ebddb5cc8', - decimals: 6, - symbol: 'USDC', - }, - STETH: { - address: 'N/A', - decimals: 18, - symbol: 'STETH', - }, + [Network.MAINNET]: { + BatchRelayer: { + address: '0xdcdbf71A870cc60C6F9B621E28a7D3Ffd6Dd4965', }, + ETH: { + address: AddressZero, + decimals: 18, + symbol: 'ETH', + }, + BAL: { + address: '0xba100000625a3754423978a60c9317c58a424e3d', + decimals: 18, + symbol: 'BAL', + }, + USDC: { + address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', + decimals: 6, + symbol: 'USDC', + }, + WBTC: { + address: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', + decimals: 8, + symbol: 'WBTC', + }, + WETH: { + address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + decimals: 18, + symbol: 'WETH', + }, + DAI: { + address: '0x6b175474e89094c44da98b954eedeac495271d0f', + decimals: 18, + symbol: 'DAI', + }, + STETH: { + address: '0xae7ab96520de3a18e5e111b5eaab095312d7fe84', + decimals: 18, + symbol: 'STETH', + }, + wSTETH: { + address: '0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0', + decimals: 18, + symbol: 'wSTETH', + }, + bbausd: { + address: '0x7b50775383d3d6f0215a8f290f2c9e2eebbeceb2', + decimals: 18, + symbol: 'bbausd', + }, + bbausdc: { + address: '0x9210F1204b5a24742Eba12f710636D76240dF3d0', + decimals: 18, + symbol: 'bbausdc', + }, + waDAI: { + address: '0x02d60b84491589974263d922d9cc7a3152618ef6', + decimals: 18, + symbol: 'waDAI', + }, + waUSDC: { + address: '0xd093fa4fb80d09bb30817fdcd442d4d02ed3e5de', + decimals: 6, + symbol: 'waUSDC', + }, + }, + [Network.KOVAN]: { + // Visit https://balancer-faucet.on.fleek.co/#/faucet for test tokens + BatchRelayer: { + address: '0x41B953164995c11C81DA73D212ED8Af25741b7Ac', + }, + ETH: { + address: AddressZero, + decimals: 18, + symbol: 'ETH', + }, + BAL: { + address: '0x41286Bb1D3E870f3F750eB7E1C25d7E48c8A1Ac7', + decimals: 18, + symbol: 'BAL', + }, + USDC: { + address: '0xc2569dd7d0fd715B054fBf16E75B001E5c0C1115', + decimals: 6, + symbol: 'USDC', + }, + WBTC: { + address: '0x1C8E3Bcb3378a443CC591f154c5CE0EBb4dA9648', + decimals: 8, + symbol: 'WBTC', + }, + WETH: { + address: '0xdFCeA9088c8A88A76FF74892C1457C17dfeef9C1', + decimals: 18, + symbol: 'WETH', + }, + DAI: { + address: '0x04DF6e4121c27713ED22341E7c7Df330F56f289B', + decimals: 18, + symbol: 'DAI', + }, + STETH: { + address: '0x4803bb90d18a1cb7a2187344fe4feb0e07878d05', + decimals: 18, + symbol: 'STETH', + }, + wSTETH: { + address: '0xa387b91e393cfb9356a460370842bc8dbb2f29af', + decimals: 18, + symbol: 'wSTETH', + }, + USDT_from_AAVE: { + address: '0x13512979ade267ab5100878e2e0f485b568328a4', + decimals: 6, + symbol: 'USDT_from_AAVE', + }, + aUSDT: { + address: '0xe8191aacfcdb32260cda25830dc6c9342142f310', + decimals: 6, + symbol: 'aUSDT', + }, + bUSDT: { + address: '0xe667d48618e71c2a02e4a1b66ed9def1426938b6', + decimals: 18, + symbol: 'bUSDT', + }, + USDC_from_AAVE: { + address: '0xe22da380ee6b445bb8273c81944adeb6e8450422', + decimals: 6, + symbol: 'USDC_from_AAVE', + }, + aUSDC: { + address: '0x0fbddc06a4720408a2f5eb78e62bc31ac6e2a3c4', + decimals: 6, + symbol: 'aUSDC', + }, + DAI_from_AAVE: { + address: '0xff795577d9ac8bd7d90ee22b6c1703490b6512fd', + decimals: 18, + symbol: 'DAI_from_AAVE', + }, + bDAI: { + address: '0xfcccb77a946b6a3bd59d149f083b5bfbb8004d6d', + decimals: 18, + symbol: 'bDAI', + }, + STABAL3: { + address: '0x8fd162f338b770f7e879030830cde9173367f301', + decimals: 18, + symbol: 'STABAL3', + }, + }, + [Network.POLYGON]: { + MATIC: { + address: AddressZero, + decimals: 18, + symbol: 'MATIC', + }, + LINK: { + address: '0x53E0bca35eC356BD5ddDFebbD1Fc0fD03FaBad39', + decimals: 18, + symbol: 'LINK', + }, + BAL: { + address: '0x9a71012b13ca4d3d0cdc72a177df3ef03b0e76a3', + decimals: 18, + symbol: 'BAL', + }, + USDC: { + address: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174', + decimals: 6, + symbol: 'USDC', + }, + WBTC: { + address: '0x1bfd67037b42cf73acf2047067bd4f2c47d9bfd6', + decimals: 8, + symbol: 'WBTC', + }, + WETH: { + address: '0x7ceb23fd6bc0add59e62ac25578270cff1b9f619', + decimals: 18, + symbol: 'WETH', + }, + DAI: { + address: '0x8f3cf7ad23cd3cadbd9735aff958023239c6a063', + decimals: 18, + symbol: 'DAI', + }, + STETH: { + address: '0xae7ab96520de3a18e5e111b5eaab095312d7fe84', + decimals: 18, + symbol: 'STETH', + }, + stUSD_PLUS: { + address: '0x5a5c6aa6164750b530b8f7658b827163b3549a4d', + decimals: 6, + symbol: 'stUSD+', + }, + bstUSD_PLUS: { + address: '0x1aafc31091d93c3ff003cff5d2d8f7ba2e728425', + decimals: 18, + symbol: 'bstUSD+', + }, + USD_PLUS: { + address: '0x5d9d8509c522a47d9285b9e4e9ec686e6a580850', + decimals: 6, + symbol: 'USD_PLUS', + }, + USDT: { + address: '0xc2132D05D31c914a87C6611C10748AEb04B58e8F', + decimals: 6, + symbol: 'USDT', + }, + DHT: { + address: '0x8C92e38eCA8210f4fcBf17F0951b198Dd7668292', + decimals: 18, + symbol: 'DHT', + }, + dUSD: { + address: '0xbAe28251B2a4E621aA7e20538c06DEe010Bc06DE', + decimals: 18, + symbol: 'dUSD', + }, + }, + [Network.ARBITRUM]: { + WETH: { + address: '0x82af49447d8a07e3bd95bd0d56f35241523fbab1', + decimals: 18, + symbol: 'WETH', + }, + BAL: { + address: '0x040d1edc9569d4bab2d15287dc5a4f10f56a56b8', + decimals: 18, + symbol: 'BAL', + }, + USDC: { + address: '0xff970a61a04b1ca14834a43f5de4533ebddb5cc8', + decimals: 6, + symbol: 'USDC', + }, + STETH: { + address: 'N/A', + decimals: 18, + symbol: 'STETH', + }, + }, }; diff --git a/balancer-js/src/test/lib/mainnet-top-10.json b/balancer-js/src/test/lib/mainnet-top-10.json new file mode 100644 index 000000000..c53516182 --- /dev/null +++ b/balancer-js/src/test/lib/mainnet-top-10.json @@ -0,0 +1,436 @@ +[ + { + "id": "0x7b50775383d3d6f0215a8f290f2c9e2eebbeceb20000000000000000000000fe", + "address": "0x7b50775383d3d6f0215a8f290f2c9e2eebbeceb2", + "poolType": "StablePhantom", + "swapFee": "0.00001", + "swapEnabled": true, + "totalShares": "185345925.677602644927340579", + "tokens": [ + { + "address": "0x2bbf681cc4eb09218bee85ea2a5d3d13fa40fc0c", + "symbol": "bb-a-USDT", + "balance": "110099182.938061876600984833", + "weight": null, + "priceRate": "1.010489741245550747", + "decimals": 18 + }, + { + "address": "0x7b50775383d3d6f0215a8f290f2c9e2eebbeceb2", + "symbol": "bb-a-USD", + "balance": "5192296673188901.950927851401879516", + "weight": null, + "priceRate": "1", + "decimals": 18 + }, + { + "address": "0x804cdb9116a10bb78768d3252355a1b18067bf8f", + "symbol": "bb-a-DAI", + "balance": "38910679.869475244352250601", + "weight": null, + "priceRate": "1.008597867373721822", + "decimals": 18 + }, + { + "address": "0x9210f1204b5a24742eba12f710636d76240df3d0", + "symbol": "bb-a-USDC", + "balance": "36627640.70011837584569211", + "weight": null, + "priceRate": "1.008733188238467688", + "decimals": 18 + } + ], + "tokensList": [ + "0x2bbf681cc4eb09218bee85ea2a5d3d13fa40fc0c", + "0x7b50775383d3d6f0215a8f290f2c9e2eebbeceb2", + "0x804cdb9116a10bb78768d3252355a1b18067bf8f", + "0x9210f1204b5a24742eba12f710636d76240df3d0" + ], + "totalWeight": "0", + "amp": "1472", + "principalToken": null, + "baseToken": null, + "wrappedIndex": 0, + "mainIndex": 0, + "lowerTarget": null, + "upperTarget": null + }, + { + "id": "0x32296969ef14eb0c6d29669c550d4a0449130230000200000000000000000080", + "address": "0x32296969ef14eb0c6d29669c550d4a0449130230", + "poolType": "MetaStable", + "swapFee": "0.0004", + "swapEnabled": true, + "totalShares": "91159.560567453827466139", + "tokens": [ + { + "address": "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0", + "symbol": "wstETH", + "balance": "60578.775583770045200186", + "weight": null, + "priceRate": "1.072339505663582219", + "decimals": 18 + }, + { + "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "symbol": "WETH", + "balance": "28389.031209848992719846", + "weight": null, + "priceRate": "1", + "decimals": 18 + } + ], + "tokensList": [ + "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0", + "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" + ], + "totalWeight": "0", + "amp": "50", + "principalToken": null, + "baseToken": null, + "wrappedIndex": 0, + "mainIndex": 0, + "lowerTarget": null, + "upperTarget": null + }, + { + "id": "0x2bbf681cc4eb09218bee85ea2a5d3d13fa40fc0c0000000000000000000000fd", + "address": "0x2bbf681cc4eb09218bee85ea2a5d3d13fa40fc0c", + "poolType": "AaveLinear", + "swapFee": "0.0002", + "swapEnabled": true, + "totalShares": "110099193.728395255706152132", + "tokens": [ + { + "address": "0x2bbf681cc4eb09218bee85ea2a5d3d13fa40fc0c", + "symbol": "bb-a-USDT", + "balance": "5192296748435633.900135240623067963", + "weight": null, + "priceRate": "1", + "decimals": 18 + }, + { + "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "symbol": "USDT", + "balance": "5505576.541109", + "weight": null, + "priceRate": "1", + "decimals": 6 + }, + { + "address": "0xf8fd466f12e236f4c96f7cce6c79eadb819abf58", + "symbol": "aUSDT", + "balance": "97239386.185478", + "weight": null, + "priceRate": "1", + "decimals": 6 + } + ], + "tokensList": [ + "0x2bbf681cc4eb09218bee85ea2a5d3d13fa40fc0c", + "0xdac17f958d2ee523a2206206994597c13d831ec7", + "0xf8fd466f12e236f4c96f7cce6c79eadb819abf58" + ], + "totalWeight": "0", + "amp": null, + "principalToken": null, + "baseToken": null, + "wrappedIndex": 2, + "mainIndex": 1, + "lowerTarget": "2900000", + "upperTarget": "10000000" + }, + { + "id": "0xa6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e", + "address": "0xa6f548df93de924d73be7d25dc02554c6bd66db5", + "poolType": "Weighted", + "swapFee": "0.001", + "swapEnabled": true, + "totalShares": "8870.405080904069291925", + "tokens": [ + { + "address": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599", + "symbol": "WBTC", + "balance": "1175.76669749", + "weight": "0.5", + "priceRate": "1", + "decimals": 8 + }, + { + "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "symbol": "WETH", + "balance": "17349.605103772407539053", + "weight": "0.5", + "priceRate": "1", + "decimals": 18 + } + ], + "tokensList": [ + "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599", + "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" + ], + "totalWeight": "1", + "amp": null, + "principalToken": null, + "baseToken": null, + "wrappedIndex": 0, + "mainIndex": 0, + "lowerTarget": null, + "upperTarget": null + }, + { + "id": "0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014", + "address": "0x5c6ee304399dbdb9c8ef030ab642b10820db8f56", + "poolType": "Weighted", + "swapFee": "0.02", + "swapEnabled": true, + "totalShares": "5111749.770967309172389442", + "tokens": [ + { + "address": "0xba100000625a3754423978a60c9317c58a424e3d", + "symbol": "BAL", + "balance": "10417022.165794507073015422", + "weight": "0.8", + "priceRate": "1", + "decimals": 18 + }, + { + "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "symbol": "WETH", + "balance": "9952.394490029745769164", + "weight": "0.2", + "priceRate": "1", + "decimals": 18 + } + ], + "tokensList": [ + "0xba100000625a3754423978a60c9317c58a424e3d", + "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" + ], + "totalWeight": "1", + "amp": null, + "principalToken": null, + "baseToken": null, + "wrappedIndex": 0, + "mainIndex": 0, + "lowerTarget": null, + "upperTarget": null + }, + { + "id": "0x90291319f1d4ea3ad4db0dd8fe9e12baf749e84500020000000000000000013c", + "address": "0x90291319f1d4ea3ad4db0dd8fe9e12baf749e845", + "poolType": "Weighted", + "swapFee": "0.003", + "swapEnabled": true, + "totalShares": "373535.492782594721150015", + "tokens": [ + { + "address": "0x956f47f50a910163d8bf957cf5846d573e7f87ca", + "symbol": "FEI", + "balance": "21102434.242590829677362", + "weight": "0.3", + "priceRate": "1", + "decimals": 18 + }, + { + "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "symbol": "WETH", + "balance": "25016.461771494761501949", + "weight": "0.7", + "priceRate": "1", + "decimals": 18 + } + ], + "tokensList": [ + "0x956f47f50a910163d8bf957cf5846d573e7f87ca", + "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" + ], + "totalWeight": "1", + "amp": null, + "principalToken": null, + "baseToken": null, + "wrappedIndex": 0, + "mainIndex": 0, + "lowerTarget": null, + "upperTarget": null + }, + { + "id": "0x06df3b2bbb68adc8b0e302443692037ed9f91b42000000000000000000000063", + "address": "0x06df3b2bbb68adc8b0e302443692037ed9f91b42", + "poolType": "Stable", + "swapFee": "0.00005", + "swapEnabled": true, + "totalShares": "53146228.386067028913388454", + "tokens": [ + { + "address": "0x6b175474e89094c44da98b954eedeac495271d0f", + "symbol": "DAI", + "balance": "11181465.657414779214513023", + "weight": null, + "priceRate": "1", + "decimals": 18 + }, + { + "address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + "symbol": "USDC", + "balance": "10990768.033916", + "weight": null, + "priceRate": "1", + "decimals": 6 + }, + { + "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "symbol": "USDT", + "balance": "31414868.941632", + "weight": null, + "priceRate": "1", + "decimals": 6 + } + ], + "tokensList": [ + "0x6b175474e89094c44da98b954eedeac495271d0f", + "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + "0xdac17f958d2ee523a2206206994597c13d831ec7" + ], + "totalWeight": "0", + "amp": "1390", + "principalToken": null, + "baseToken": null, + "wrappedIndex": 0, + "mainIndex": 0, + "lowerTarget": null, + "upperTarget": null + }, + { + "id": "0x804cdb9116a10bb78768d3252355a1b18067bf8f0000000000000000000000fb", + "address": "0x804cdb9116a10bb78768d3252355a1b18067bf8f", + "poolType": "AaveLinear", + "swapFee": "0.0002", + "swapEnabled": true, + "totalShares": "38910797.060224867234359145", + "tokens": [ + { + "address": "0x02d60b84491589974263d922d9cc7a3152618ef6", + "symbol": "aDAI", + "balance": "33896107.766318549296694753", + "weight": null, + "priceRate": "1", + "decimals": 18 + }, + { + "address": "0x6b175474e89094c44da98b954eedeac495271d0f", + "symbol": "DAI", + "balance": "2900834.596380034678063367", + "weight": null, + "priceRate": "1", + "decimals": 18 + }, + { + "address": "0x804cdb9116a10bb78768d3252355a1b18067bf8f", + "symbol": "bb-a-DAI", + "balance": "5192296819624030.56830562909486095", + "weight": null, + "priceRate": "1", + "decimals": 18 + } + ], + "tokensList": [ + "0x02d60b84491589974263d922d9cc7a3152618ef6", + "0x6b175474e89094c44da98b954eedeac495271d0f", + "0x804cdb9116a10bb78768d3252355a1b18067bf8f" + ], + "totalWeight": "0", + "amp": null, + "principalToken": null, + "baseToken": null, + "wrappedIndex": 0, + "mainIndex": 1, + "lowerTarget": "2900000", + "upperTarget": "10000000" + }, + { + "id": "0x9210f1204b5a24742eba12f710636d76240df3d00000000000000000000000fc", + "address": "0x9210f1204b5a24742eba12f710636d76240df3d0", + "poolType": "AaveLinear", + "swapFee": "0.0002", + "swapEnabled": true, + "totalShares": "36627769.356868515534842465", + "tokens": [ + { + "address": "0x9210f1204b5a24742eba12f710636d76240df3d0", + "symbol": "bb-a-USDC", + "balance": "5192296821907058.27166198079437763", + "weight": null, + "priceRate": "1", + "decimals": 18 + }, + { + "address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + "symbol": "USDC", + "balance": "9203233.688654", + "weight": null, + "priceRate": "1", + "decimals": 6 + }, + { + "address": "0xd093fa4fb80d09bb30817fdcd442d4d02ed3e5de", + "symbol": "aUSDC", + "balance": "25798924.081938", + "weight": null, + "priceRate": "1", + "decimals": 6 + } + ], + "tokensList": [ + "0x9210f1204b5a24742eba12f710636d76240df3d0", + "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + "0xd093fa4fb80d09bb30817fdcd442d4d02ed3e5de" + ], + "totalWeight": "0", + "amp": null, + "principalToken": null, + "baseToken": null, + "wrappedIndex": 2, + "mainIndex": 1, + "lowerTarget": "2900000", + "upperTarget": "10000000" + }, + { + "id": "0x96646936b91d6b9d7d0c47c496afbf3d6ec7b6f8000200000000000000000019", + "address": "0x96646936b91d6b9d7d0c47c496afbf3d6ec7b6f8", + "poolType": "Weighted", + "swapFee": "0.00075", + "swapEnabled": true, + "totalShares": "717368.27182997399072352", + "tokens": [ + { + "address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + "symbol": "USDC", + "balance": "17981073.730052", + "weight": "0.5", + "priceRate": "1", + "decimals": 6 + }, + { + "address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", + "symbol": "WETH", + "balance": "8928.966265274874436087", + "weight": "0.5", + "priceRate": "1", + "decimals": 18 + } + ], + "tokensList": [ + "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" + ], + "totalWeight": "1", + "amp": null, + "principalToken": null, + "baseToken": null, + "wrappedIndex": 0, + "mainIndex": 0, + "lowerTarget": null, + "upperTarget": null + } +] diff --git a/balancer-js/src/test/lib/mainnetPools.ts b/balancer-js/src/test/lib/mainnetPools.ts new file mode 100644 index 000000000..8c7709196 --- /dev/null +++ b/balancer-js/src/test/lib/mainnetPools.ts @@ -0,0 +1,31 @@ +import { SubgraphPoolBase, Network } from '@/.'; +import { getNetworkConfig } from '@/modules/sdk.helpers'; +import { getOnChainBalances } from '@/modules/sor/pool-data/onChainData'; +import { JsonRpcProvider } from '@ethersproject/providers'; +import { factories } from '../factories'; + +export const B_50WBTC_50WETH = factories.subgraphPoolBase.build({ + id: '0xa6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e', + address: '0xa6f548df93de924d73be7d25dc02554c6bd66db5', + tokens: [ + factories.subgraphToken.transient({ symbol: 'wETH' }).build(), + factories.subgraphToken.transient({ symbol: 'wBTC' }).build(), + ], +}); + +export const getForkedPools = async ( + provider: JsonRpcProvider, + pools: SubgraphPoolBase[] = [B_50WBTC_50WETH] +): Promise => { + const network = getNetworkConfig({ network: Network.MAINNET, rpcUrl: '' }); + + // btcEthPool from mainnet, balances and total shares are fetched from on chain data + const onChainPools = await getOnChainBalances( + pools, + network.addresses.contracts.multicall, + network.addresses.contracts.vault, + provider + ); + + return onChainPools; +}; diff --git a/balancer-js/src/test/lib/mockPool.ts b/balancer-js/src/test/lib/mockPool.ts index d99aa8b60..19e8626b8 100644 --- a/balancer-js/src/test/lib/mockPool.ts +++ b/balancer-js/src/test/lib/mockPool.ts @@ -2,45 +2,45 @@ import { PoolDataService } from '@balancer-labs/sor'; import { SubgraphPoolBase } from '@/.'; export class MockPoolDataService implements PoolDataService { - constructor(private pools: SubgraphPoolBase[] = []) {} + constructor(private pools: SubgraphPoolBase[] = []) {} - public async getPools(): Promise { - return this.pools; - } + public async getPools(): Promise { + return this.pools; + } - public setPools(pools: SubgraphPoolBase[]): void { - this.pools = pools; - } + public setPools(pools: SubgraphPoolBase[]): void { + this.pools = pools; + } } export const mockPool: SubgraphPoolBase = { - address: '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56', - id: '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014', - poolType: 'Weighted', - swapEnabled: true, - swapFee: '0.0005', - tokens: [ - { - address: '0xba100000625a3754423978a60c9317c58a424e3d', - balance: '5489603.901499267423530886', - decimals: 18, - priceRate: '1', - weight: '0.8', - }, - { - address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', - balance: '6627.784151437690672979', - decimals: 18, - priceRate: '1', - weight: '0.2', - }, - ], - tokensList: [ - '0xba100000625a3754423978a60c9317c58a424e3d', - '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', - ], - totalShares: '2848354.78492663257738526', - totalWeight: '1', + address: '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56', + id: '0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014', + poolType: 'Weighted', + swapEnabled: true, + swapFee: '0.0005', + tokens: [ + { + address: '0xba100000625a3754423978a60c9317c58a424e3d', + balance: '5489603.901499267423530886', + decimals: 18, + priceRate: '1', + weight: '0.8', + }, + { + address: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + balance: '6627.784151437690672979', + decimals: 18, + priceRate: '1', + weight: '0.2', + }, + ], + tokensList: [ + '0xba100000625a3754423978a60c9317c58a424e3d', + '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', + ], + totalShares: '2848354.78492663257738526', + totalWeight: '1', }; export const mockPoolDataService = new MockPoolDataService([mockPool]); diff --git a/balancer-js/src/test/tokens.spec.ts b/balancer-js/src/test/tokens.spec.ts index 693747316..b1e168351 100644 --- a/balancer-js/src/test/tokens.spec.ts +++ b/balancer-js/src/test/tokens.spec.ts @@ -4,85 +4,79 @@ import { expect } from 'chai'; import { AssetHelpers } from '@/lib/utils'; describe('sortTokens', () => { - const ETH = AddressZero; - const WETH = '0x000000000000000000000000000000000000000F'; - const assetHelpers = new AssetHelpers(WETH); + const ETH = AddressZero; + const WETH = '0x000000000000000000000000000000000000000F'; + const assetHelpers = new AssetHelpers(WETH); - const UNSORTED_TOKENS = [ - '0x0000000000000000000000000000000000000002', - '0x0000000000000000000000000000000000000001', - '0x0000000000000000000000000000000000000004', - '0x0000000000000000000000000000000000000003', - ]; + const UNSORTED_TOKENS = [ + '0x0000000000000000000000000000000000000002', + '0x0000000000000000000000000000000000000001', + '0x0000000000000000000000000000000000000004', + '0x0000000000000000000000000000000000000003', + ]; - const SORTED_TOKENS = [ - '0x0000000000000000000000000000000000000001', - '0x0000000000000000000000000000000000000002', - '0x0000000000000000000000000000000000000003', - '0x0000000000000000000000000000000000000004', - ]; + const SORTED_TOKENS = [ + '0x0000000000000000000000000000000000000001', + '0x0000000000000000000000000000000000000002', + '0x0000000000000000000000000000000000000003', + '0x0000000000000000000000000000000000000004', + ]; - context('when provided only tokens', () => { - context('when provided only ERC20s', () => { - it('sorts the tokens in ascending order', () => { - const [sortedTokens] = assetHelpers.sortTokens(UNSORTED_TOKENS); - expect(sortedTokens).to.be.deep.eq(SORTED_TOKENS); + context('when provided only tokens', () => { + context('when provided only ERC20s', () => { + it('sorts the tokens in ascending order', () => { + const [sortedTokens] = assetHelpers.sortTokens(UNSORTED_TOKENS); + expect(sortedTokens).to.be.deep.eq(SORTED_TOKENS); - const UNSORTED_TOKENS_WITH_WETH = [WETH, ...UNSORTED_TOKENS]; - const SORTED_TOKENS_WITH_WETH = [...SORTED_TOKENS, WETH]; - const [sortedTokensWithWeth] = assetHelpers.sortTokens( - UNSORTED_TOKENS_WITH_WETH - ); - expect(sortedTokensWithWeth).to.be.deep.eq( - SORTED_TOKENS_WITH_WETH - ); - }); - }); + const UNSORTED_TOKENS_WITH_WETH = [WETH, ...UNSORTED_TOKENS]; + const SORTED_TOKENS_WITH_WETH = [...SORTED_TOKENS, WETH]; + const [sortedTokensWithWeth] = assetHelpers.sortTokens( + UNSORTED_TOKENS_WITH_WETH + ); + expect(sortedTokensWithWeth).to.be.deep.eq(SORTED_TOKENS_WITH_WETH); + }); + }); - context('when provided a mix of ERC20s and ETH', () => { - const UNSORTED_TOKENS_WITH_ETH = [ETH, ...UNSORTED_TOKENS]; - const SORTED_TOKENS_WITH_ETH = [...SORTED_TOKENS, ETH]; + context('when provided a mix of ERC20s and ETH', () => { + const UNSORTED_TOKENS_WITH_ETH = [ETH, ...UNSORTED_TOKENS]; + const SORTED_TOKENS_WITH_ETH = [...SORTED_TOKENS, ETH]; - it('sorts ETH as if it were WETH', () => { - const [sortedTokens] = assetHelpers.sortTokens( - UNSORTED_TOKENS_WITH_ETH - ); - expect(sortedTokens).to.be.deep.eq(SORTED_TOKENS_WITH_ETH); - }); - }); + it('sorts ETH as if it were WETH', () => { + const [sortedTokens] = assetHelpers.sortTokens( + UNSORTED_TOKENS_WITH_ETH + ); + expect(sortedTokens).to.be.deep.eq(SORTED_TOKENS_WITH_ETH); + }); }); + }); - context('when provided additional arrays', () => { - const UNSORTED_NUMBERS = [1, 2, 3, 4]; - const UNSORTED_LETTERS = ['a', 'b', 'c', 'd']; + context('when provided additional arrays', () => { + const UNSORTED_NUMBERS = [1, 2, 3, 4]; + const UNSORTED_LETTERS = ['a', 'b', 'c', 'd']; - it('sorts the tokens in ascending order', () => { - const [sortedTokens] = assetHelpers.sortTokens( - UNSORTED_TOKENS, - UNSORTED_NUMBERS, - UNSORTED_LETTERS - ); - expect(sortedTokens).to.be.deep.eq(SORTED_TOKENS); - }); + it('sorts the tokens in ascending order', () => { + const [sortedTokens] = assetHelpers.sortTokens( + UNSORTED_TOKENS, + UNSORTED_NUMBERS, + UNSORTED_LETTERS + ); + expect(sortedTokens).to.be.deep.eq(SORTED_TOKENS); + }); - it('maintains relative ordering with tokens array', () => { - const [sortedTokens, sortedNumbers, sortedLetters] = - assetHelpers.sortTokens( - UNSORTED_TOKENS, - UNSORTED_NUMBERS, - UNSORTED_LETTERS - ) as [string[], number[], string[]]; + it('maintains relative ordering with tokens array', () => { + const [sortedTokens, sortedNumbers, sortedLetters] = + assetHelpers.sortTokens( + UNSORTED_TOKENS, + UNSORTED_NUMBERS, + UNSORTED_LETTERS + ) as [string[], number[], string[]]; - // Find the index of each token in the unsorted array and check that other values are mapped to the same position - sortedTokens.forEach((token, index) => { - const unsortedIndex = UNSORTED_TOKENS.indexOf(token); - expect(sortedNumbers[index]).to.be.eq( - UNSORTED_NUMBERS[unsortedIndex] - ); - expect(sortedLetters[index]).to.be.eq( - UNSORTED_LETTERS[unsortedIndex] - ); - }); - }); + // Find the index of each token in the unsorted array and check that other values are mapped to the same position + sortedTokens.forEach((token, index) => { + const unsortedIndex = UNSORTED_TOKENS.indexOf(token); + expect(sortedNumbers[index]).to.be.eq(UNSORTED_NUMBERS[unsortedIndex]); + expect(sortedLetters[index]).to.be.eq(UNSORTED_LETTERS[unsortedIndex]); + }); }); + }); }); diff --git a/balancer-js/src/types.ts b/balancer-js/src/types.ts index ea46dc6a3..e293bb421 100644 --- a/balancer-js/src/types.ts +++ b/balancer-js/src/types.ts @@ -4,115 +4,118 @@ import { Contract } from '@ethersproject/contracts'; import { PoolDataService, TokenPriceService } from '@balancer-labs/sor'; export interface BalancerSdkConfig { - //use a known network or provide an entirely custom config - network: Network | BalancerNetworkConfig; - rpcUrl: string; - //overwrite the subgraph url if you don't want to use the balancer labs maintained version - customSubgraphUrl?: string; - //optionally overwrite parts of the standard SOR config - sor?: Partial; + //use a known network or provide an entirely custom config + network: Network | BalancerNetworkConfig; + rpcUrl: string; + //overwrite the subgraph url if you don't want to use the balancer labs maintained version + customSubgraphUrl?: string; + //optionally overwrite parts of the standard SOR config + sor?: Partial; } export interface BalancerSdkSorConfig { - //use a built-in service or provide a custom implementation of a TokenPriceService - //defaults to coingecko - tokenPriceService: 'coingecko' | 'subgraph' | TokenPriceService; - //use a built-in service or provide a custom implementation of a PoolDataService - //defaults to subgraph - poolDataService: 'subgraph' | PoolDataService; - //if a custom PoolDataService is provided, on chain balance fetching needs to be handled externally - //default to true. - fetchOnChainBalances: boolean; + //use a built-in service or provide a custom implementation of a TokenPriceService + //defaults to coingecko + tokenPriceService: 'coingecko' | 'subgraph' | TokenPriceService; + //use a built-in service or provide a custom implementation of a PoolDataService + //defaults to subgraph + poolDataService: 'subgraph' | PoolDataService; + //if a custom PoolDataService is provided, on chain balance fetching needs to be handled externally + //default to true. + fetchOnChainBalances: boolean; } export interface BalancerNetworkConfig { - chainId: Network; - addresses: { - contracts: { - vault: string; - multicall: string; - }; - tokens: { - wrappedNativeAsset: string; - lbpRaisingTokens?: string[]; - }; + chainId: Network; + addresses: { + contracts: { + vault: string; + multicall: string; + lidoRelayer?: string; }; - urls: { - subgraph: string; - }; - pools: { - staBal3Pool?: PoolReference; - wethStaBal3?: PoolReference; - bbausd?: PoolReference; - wethBBausd?: PoolReference; + tokens: { + wrappedNativeAsset: string; + lbpRaisingTokens?: string[]; + stETH?: string; + wstETH?: string; }; + }; + urls: { + subgraph: string; + }; + pools: { + staBal3Pool?: PoolReference; + wethStaBal3?: PoolReference; + bbausd?: PoolReference; + wethBBausd?: PoolReference; + }; } export type PoolReference = { - id: string; - address: string; + id: string; + address: string; }; export enum PoolSpecialization { - GeneralPool = 0, - MinimalSwapInfoPool, - TwoTokenPool, + GeneralPool = 0, + MinimalSwapInfoPool, + TwoTokenPool, } // Joins export type JoinPoolRequest = { - assets: string[]; - maxAmountsIn: BigNumberish[]; - userData: string; - fromInternalBalance: boolean; + assets: string[]; + maxAmountsIn: BigNumberish[]; + userData: string; + fromInternalBalance: boolean; }; // Exit export type ExitPoolRequest = { - assets: string[]; - minAmountsOut: string[]; - userData: string; - toInternalBalance: boolean; + assets: string[]; + minAmountsOut: string[]; + userData: string; + toInternalBalance: boolean; }; // Balance Operations export enum UserBalanceOpKind { - DepositInternal = 0, - WithdrawInternal, - TransferInternal, - TransferExternal, + DepositInternal = 0, + WithdrawInternal, + TransferInternal, + TransferExternal, } export type UserBalanceOp = { - kind: UserBalanceOpKind; - asset: string; - amount: BigNumberish; - sender: string; - recipient: string; + kind: UserBalanceOpKind; + asset: string; + amount: BigNumberish; + sender: string; + recipient: string; }; export enum PoolBalanceOpKind { - Withdraw = 0, - Deposit = 1, - Update = 2, + Withdraw = 0, + Deposit = 1, + Update = 2, } export type PoolBalanceOp = { - kind: PoolBalanceOpKind; - poolId: string; - token: string; - amount: BigNumberish; + kind: PoolBalanceOpKind; + poolId: string; + token: string; + amount: BigNumberish; }; export interface TransactionData { - contract?: Contract; - function: string; - params: string[]; - outputs?: { - amountsIn?: string[]; - amountsOut?: string[]; - }; + contract?: Contract; + function: string; + params: string[]; + outputs?: { + amountsIn?: string[]; + amountsOut?: string[]; + }; } diff --git a/balancer-js/tsconfig.json b/balancer-js/tsconfig.json index 99457d9ac..615afddc1 100644 --- a/balancer-js/tsconfig.json +++ b/balancer-js/tsconfig.json @@ -17,5 +17,7 @@ ] } }, - "include": ["./src", "src/abi/*.json"] + "include": ["./src", "src/abi/*.json"], + "exclude": ["node_modules"], + "files": ["hardhat.config.js"] } diff --git a/balancer-js/tsconfig.testing.json b/balancer-js/tsconfig.testing.json index 1b7a59159..2c7b28416 100644 --- a/balancer-js/tsconfig.testing.json +++ b/balancer-js/tsconfig.testing.json @@ -1,6 +1,6 @@ { - "extends": "./tsconfig.json", - "compilerOptions": { - "module": "commonjs" - } + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs" + } } diff --git a/balancer-js/yarn.lock b/balancer-js/yarn.lock index 15e48b3e5..f587da3d2 100644 --- a/balancer-js/yarn.lock +++ b/balancer-js/yarn.lock @@ -513,6 +513,90 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" +"@ethereumjs/block@^3.5.0", "@ethereumjs/block@^3.6.2": + version "3.6.2" + resolved "https://registry.yarnpkg.com/@ethereumjs/block/-/block-3.6.2.tgz#63d1e26d0b7a7a3684fce920de6ebabec1e5b674" + integrity sha512-mOqYWwMlAZpYUEOEqt7EfMFuVL2eyLqWWIzcf4odn6QgXY8jBI2NhVuJncrMCKeMZrsJAe7/auaRRB6YcdH+Qw== + dependencies: + "@ethereumjs/common" "^2.6.3" + "@ethereumjs/tx" "^3.5.1" + ethereumjs-util "^7.1.4" + merkle-patricia-tree "^4.2.4" + +"@ethereumjs/blockchain@^5.5.2": + version "5.5.2" + resolved "https://registry.yarnpkg.com/@ethereumjs/blockchain/-/blockchain-5.5.2.tgz#1848abd9dc1ee56acf8cec4c84304d7f4667d027" + integrity sha512-Jz26iJmmsQtngerW6r5BDFaew/f2mObLrRZo3rskLOx1lmtMZ8+TX/vJexmivrnWgmAsTdNWhlKUYY4thPhPig== + dependencies: + "@ethereumjs/block" "^3.6.2" + "@ethereumjs/common" "^2.6.3" + "@ethereumjs/ethash" "^1.1.0" + debug "^4.3.3" + ethereumjs-util "^7.1.4" + level-mem "^5.0.1" + lru-cache "^5.1.1" + semaphore-async-await "^1.5.1" + +"@ethereumjs/common@^2.6.3", "@ethereumjs/common@^2.6.4": + version "2.6.4" + resolved "https://registry.yarnpkg.com/@ethereumjs/common/-/common-2.6.4.tgz#1b3cdd3aa4ee3b0ca366756fc35e4a03022a01cc" + integrity sha512-RDJh/R/EAr+B7ZRg5LfJ0BIpf/1LydFgYdvZEuTraojCbVypO2sQ+QnpP5u2wJf9DASyooKqu8O4FJEWUV6NXw== + dependencies: + crc-32 "^1.2.0" + ethereumjs-util "^7.1.4" + +"@ethereumjs/ethash@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/ethash/-/ethash-1.1.0.tgz#7c5918ffcaa9cb9c1dc7d12f77ef038c11fb83fb" + integrity sha512-/U7UOKW6BzpA+Vt+kISAoeDie1vAvY4Zy2KF5JJb+So7+1yKmJeJEHOGSnQIj330e9Zyl3L5Nae6VZyh2TJnAA== + dependencies: + "@ethereumjs/block" "^3.5.0" + "@types/levelup" "^4.3.0" + buffer-xor "^2.0.1" + ethereumjs-util "^7.1.1" + miller-rabin "^4.0.0" + +"@ethereumjs/tx@^3.5.1": + version "3.5.1" + resolved "https://registry.yarnpkg.com/@ethereumjs/tx/-/tx-3.5.1.tgz#8d941b83a602b4a89949c879615f7ea9a90e6671" + integrity sha512-xzDrTiu4sqZXUcaBxJ4n4W5FrppwxLxZB4ZDGVLtxSQR4lVuOnFR6RcUHdg1mpUhAPVrmnzLJpxaeXnPxIyhWA== + dependencies: + "@ethereumjs/common" "^2.6.3" + ethereumjs-util "^7.1.4" + +"@ethereumjs/vm@^5.9.0": + version "5.9.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/vm/-/vm-5.9.0.tgz#54e485097c6dbb42554d541ef8d84d06b7ddf12f" + integrity sha512-0IRsj4IuF8lFDWVVLc4mFOImaSX8VWF8CGm3mXHG/LLlQ/Tryy/kKXMw/bU9D+Zw03CdteW+wCGqNFS6+mPjpg== + dependencies: + "@ethereumjs/block" "^3.6.2" + "@ethereumjs/blockchain" "^5.5.2" + "@ethereumjs/common" "^2.6.4" + "@ethereumjs/tx" "^3.5.1" + async-eventemitter "^0.2.4" + core-js-pure "^3.0.1" + debug "^4.3.3" + ethereumjs-util "^7.1.4" + functional-red-black-tree "^1.0.1" + mcl-wasm "^0.7.1" + merkle-patricia-tree "^4.2.4" + rustbn.js "~0.2.0" + +"@ethersproject/abi@5.6.2", "@ethersproject/abi@^5.1.2", "@ethersproject/abi@^5.6.0": + version "5.6.2" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.6.2.tgz#f2956f2ac724cd720e581759d9e3840cd9744818" + integrity sha512-40Ixjhy+YzFtnvzIqFU13FW9hd1gMoLa3cJfSDnfnL4o8EnEG1qLiV8sNJo3sHYi9UYMfFeRuZ7kv5+vhzU7gQ== + dependencies: + "@ethersproject/address" "^5.6.0" + "@ethersproject/bignumber" "^5.6.0" + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/constants" "^5.6.0" + "@ethersproject/hash" "^5.6.0" + "@ethersproject/keccak256" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/properties" "^5.6.0" + "@ethersproject/strings" "^5.6.0" + "@ethersproject/abi@^5.4.0", "@ethersproject/abi@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.5.0.tgz#fb52820e22e50b854ff15ce1647cc508d6660613" @@ -528,6 +612,19 @@ "@ethersproject/properties" "^5.5.0" "@ethersproject/strings" "^5.5.0" +"@ethersproject/abstract-provider@5.6.0", "@ethersproject/abstract-provider@^5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.6.0.tgz#0c4ac7054650dbd9c476cf5907f588bbb6ef3061" + integrity sha512-oPMFlKLN+g+y7a79cLK3WiLcjWFnZQtXWgnLAbHZcN3s7L4v90UHpTOrLk+m3yr0gt+/h9STTM6zrr7PM8uoRw== + dependencies: + "@ethersproject/bignumber" "^5.6.0" + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/networks" "^5.6.0" + "@ethersproject/properties" "^5.6.0" + "@ethersproject/transactions" "^5.6.0" + "@ethersproject/web" "^5.6.0" + "@ethersproject/abstract-provider@^5.5.0": version "5.5.1" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.5.1.tgz#2f1f6e8a3ab7d378d8ad0b5718460f85649710c5" @@ -541,6 +638,17 @@ "@ethersproject/transactions" "^5.5.0" "@ethersproject/web" "^5.5.0" +"@ethersproject/abstract-signer@5.6.1", "@ethersproject/abstract-signer@^5.6.0": + version "5.6.1" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.6.1.tgz#54df786bdf1aabe20d0ed508ec05e0aa2d06674f" + integrity sha512-xhSLo6y0nGJS7NxfvOSzCaWKvWb1TLT7dQ0nnpHZrDnC67xfnWm9NXflTMFPUXXMtjr33CdV0kWDEmnbrQZ74Q== + dependencies: + "@ethersproject/abstract-provider" "^5.6.0" + "@ethersproject/bignumber" "^5.6.0" + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/properties" "^5.6.0" + "@ethersproject/abstract-signer@^5.4.0", "@ethersproject/abstract-signer@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.5.0.tgz#590ff6693370c60ae376bf1c7ada59eb2a8dd08d" @@ -552,6 +660,17 @@ "@ethersproject/logger" "^5.5.0" "@ethersproject/properties" "^5.5.0" +"@ethersproject/address@5.6.0", "@ethersproject/address@^5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.6.0.tgz#13c49836d73e7885fc148ad633afad729da25012" + integrity sha512-6nvhYXjbXsHPS+30sHZ+U4VMagFC/9zAk6Gd/h3S21YW4+yfb0WfRtaAIZ4kfM4rrVwqiy284LP0GtL5HXGLxQ== + dependencies: + "@ethersproject/bignumber" "^5.6.0" + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/keccak256" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/rlp" "^5.6.0" + "@ethersproject/address@^5.4.0", "@ethersproject/address@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.5.0.tgz#bcc6f576a553f21f3dd7ba17248f81b473c9c78f" @@ -563,6 +682,13 @@ "@ethersproject/logger" "^5.5.0" "@ethersproject/rlp" "^5.5.0" +"@ethersproject/base64@5.6.0", "@ethersproject/base64@^5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.6.0.tgz#a12c4da2a6fb86d88563216b0282308fc15907c9" + integrity sha512-2Neq8wxJ9xHxCF9TUgmKeSh9BXJ6OAxWfeGWvbauPh8FuHEjamgHilllx8KkSd5ErxyHIX7Xv3Fkcud2kY9ezw== + dependencies: + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/base64@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.5.0.tgz#881e8544e47ed976930836986e5eb8fab259c090" @@ -570,6 +696,14 @@ dependencies: "@ethersproject/bytes" "^5.5.0" +"@ethersproject/basex@5.6.0", "@ethersproject/basex@^5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.6.0.tgz#9ea7209bf0a1c3ddc2a90f180c3a7f0d7d2e8a69" + integrity sha512-qN4T+hQd/Md32MoJpc69rOwLYRUXwjTlhHDIeUkUmiN/JyWkkLLMoG0TqvSQKNqZOMgN5stbUYN6ILC+eD7MEQ== + dependencies: + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/properties" "^5.6.0" + "@ethersproject/basex@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.5.0.tgz#e40a53ae6d6b09ab4d977bd037010d4bed21b4d3" @@ -578,6 +712,15 @@ "@ethersproject/bytes" "^5.5.0" "@ethersproject/properties" "^5.5.0" +"@ethersproject/bignumber@5.6.1", "@ethersproject/bignumber@^5.6.0": + version "5.6.1" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.6.1.tgz#d5e0da518eb82ab8d08ca9db501888bbf5f0c8fb" + integrity sha512-UtMeZ3GaUuF9sx2u9nPZiPP3ULcAFmXyvynR7oHl/tPrM+vldZh7ocMsoa1PqKYGnQnqUZJoqxZnGN6J0qdipA== + dependencies: + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + bn.js "^4.11.9" + "@ethersproject/bignumber@^5.4.0", "@ethersproject/bignumber@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.5.0.tgz#875b143f04a216f4f8b96245bde942d42d279527" @@ -587,6 +730,13 @@ "@ethersproject/logger" "^5.5.0" bn.js "^4.11.9" +"@ethersproject/bytes@5.6.1", "@ethersproject/bytes@^5.6.0": + version "5.6.1" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.6.1.tgz#24f916e411f82a8a60412344bf4a813b917eefe7" + integrity sha512-NwQt7cKn5+ZE4uDn+X5RAXLp46E1chXoaMmrxAyA0rblpxz8t58lVkrHXoRIn0lz1joQElQ8410GqhTqMOwc6g== + dependencies: + "@ethersproject/logger" "^5.6.0" + "@ethersproject/bytes@^5.4.0", "@ethersproject/bytes@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.5.0.tgz#cb11c526de657e7b45d2e0f0246fb3b9d29a601c" @@ -594,6 +744,13 @@ dependencies: "@ethersproject/logger" "^5.5.0" +"@ethersproject/constants@5.6.0", "@ethersproject/constants@^5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.6.0.tgz#55e3eb0918584d3acc0688e9958b0cedef297088" + integrity sha512-SrdaJx2bK0WQl23nSpV/b1aq293Lh0sUaZT/yYKPDKn4tlAbkH96SPJwIhwSwTsoQQZxuh1jnqsKwyymoiBdWA== + dependencies: + "@ethersproject/bignumber" "^5.6.0" + "@ethersproject/constants@^5.4.0", "@ethersproject/constants@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.5.0.tgz#d2a2cd7d94bd1d58377d1d66c4f53c9be4d0a45e" @@ -601,6 +758,22 @@ dependencies: "@ethersproject/bignumber" "^5.5.0" +"@ethersproject/contracts@5.6.1": + version "5.6.1" + resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.6.1.tgz#c0eba3f8a2226456f92251a547344fd0593281d2" + integrity sha512-0fpBBDoPqJMsutE6sNjg6pvCJaIcl7tliMQTMRcoUWDACfjO68CpKOJBlsEhEhmzdnu/41KbrfAeg+sB3y35MQ== + dependencies: + "@ethersproject/abi" "^5.6.0" + "@ethersproject/abstract-provider" "^5.6.0" + "@ethersproject/abstract-signer" "^5.6.0" + "@ethersproject/address" "^5.6.0" + "@ethersproject/bignumber" "^5.6.0" + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/constants" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/properties" "^5.6.0" + "@ethersproject/transactions" "^5.6.0" + "@ethersproject/contracts@^5.4.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.5.0.tgz#b735260d4bd61283a670a82d5275e2a38892c197" @@ -617,6 +790,20 @@ "@ethersproject/properties" "^5.5.0" "@ethersproject/transactions" "^5.5.0" +"@ethersproject/hash@5.6.0", "@ethersproject/hash@^5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.6.0.tgz#d24446a5263e02492f9808baa99b6e2b4c3429a2" + integrity sha512-fFd+k9gtczqlr0/BruWLAu7UAOas1uRRJvOR84uDf4lNZ+bTkGl366qvniUZHKtlqxBRU65MkOobkmvmpHU+jA== + dependencies: + "@ethersproject/abstract-signer" "^5.6.0" + "@ethersproject/address" "^5.6.0" + "@ethersproject/bignumber" "^5.6.0" + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/keccak256" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/properties" "^5.6.0" + "@ethersproject/strings" "^5.6.0" + "@ethersproject/hash@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.5.0.tgz#7cee76d08f88d1873574c849e0207dcb32380cc9" @@ -631,6 +818,24 @@ "@ethersproject/properties" "^5.5.0" "@ethersproject/strings" "^5.5.0" +"@ethersproject/hdnode@5.6.1", "@ethersproject/hdnode@^5.6.0": + version "5.6.1" + resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.6.1.tgz#37fa1eb91f6e20ca39cc5fcb7acd3da263d85dab" + integrity sha512-6IuYDmbH5Bv/WH/A2cUd0FjNr4qTLAvyHAECiFZhNZp69pPvU7qIDwJ7CU7VAkwm4IVBzqdYy9mpMAGhQdwCDA== + dependencies: + "@ethersproject/abstract-signer" "^5.6.0" + "@ethersproject/basex" "^5.6.0" + "@ethersproject/bignumber" "^5.6.0" + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/pbkdf2" "^5.6.0" + "@ethersproject/properties" "^5.6.0" + "@ethersproject/sha2" "^5.6.0" + "@ethersproject/signing-key" "^5.6.0" + "@ethersproject/strings" "^5.6.0" + "@ethersproject/transactions" "^5.6.0" + "@ethersproject/wordlists" "^5.6.0" + "@ethersproject/hdnode@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.5.0.tgz#4a04e28f41c546f7c978528ea1575206a200ddf6" @@ -649,6 +854,25 @@ "@ethersproject/transactions" "^5.5.0" "@ethersproject/wordlists" "^5.5.0" +"@ethersproject/json-wallets@5.6.0", "@ethersproject/json-wallets@^5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.6.0.tgz#4c2fc27f17e36c583e7a252fb938bc46f98891e5" + integrity sha512-fmh86jViB9r0ibWXTQipxpAGMiuxoqUf78oqJDlCAJXgnJF024hOOX7qVgqsjtbeoxmcLwpPsXNU0WEe/16qPQ== + dependencies: + "@ethersproject/abstract-signer" "^5.6.0" + "@ethersproject/address" "^5.6.0" + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/hdnode" "^5.6.0" + "@ethersproject/keccak256" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/pbkdf2" "^5.6.0" + "@ethersproject/properties" "^5.6.0" + "@ethersproject/random" "^5.6.0" + "@ethersproject/strings" "^5.6.0" + "@ethersproject/transactions" "^5.6.0" + aes-js "3.0.0" + scrypt-js "3.0.1" + "@ethersproject/json-wallets@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.5.0.tgz#dd522d4297e15bccc8e1427d247ec8376b60e325" @@ -668,6 +892,14 @@ aes-js "3.0.0" scrypt-js "3.0.1" +"@ethersproject/keccak256@5.6.0", "@ethersproject/keccak256@^5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.6.0.tgz#fea4bb47dbf8f131c2e1774a1cecbfeb9d606459" + integrity sha512-tk56BJ96mdj/ksi7HWZVWGjCq0WVl/QvfhFQNeL8fxhBlGoP+L80uDCiQcpJPd+2XxkivS3lwRm3E0CXTfol0w== + dependencies: + "@ethersproject/bytes" "^5.6.0" + js-sha3 "0.8.0" + "@ethersproject/keccak256@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.5.0.tgz#e4b1f9d7701da87c564ffe336f86dcee82983492" @@ -676,11 +908,23 @@ "@ethersproject/bytes" "^5.5.0" js-sha3 "0.8.0" +"@ethersproject/logger@5.6.0", "@ethersproject/logger@^5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.6.0.tgz#d7db1bfcc22fd2e4ab574cba0bb6ad779a9a3e7a" + integrity sha512-BiBWllUROH9w+P21RzoxJKzqoqpkyM1pRnEKG69bulE9TSQD8SAIvTQqIMZmmCO8pUNkgLP1wndX1gKghSpBmg== + "@ethersproject/logger@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.5.0.tgz#0c2caebeff98e10aefa5aef27d7441c7fd18cf5d" integrity sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg== +"@ethersproject/networks@5.6.2", "@ethersproject/networks@^5.6.0": + version "5.6.2" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.6.2.tgz#2bacda62102c0b1fcee408315f2bed4f6fbdf336" + integrity sha512-9uEzaJY7j5wpYGTojGp8U89mSsgQLc40PCMJLMCnFXTs7nhBveZ0t7dbqWUNrepWTszDbFkYD6WlL8DKx5huHA== + dependencies: + "@ethersproject/logger" "^5.6.0" + "@ethersproject/networks@^5.5.0": version "5.5.2" resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.5.2.tgz#784c8b1283cd2a931114ab428dae1bd00c07630b" @@ -688,6 +932,14 @@ dependencies: "@ethersproject/logger" "^5.5.0" +"@ethersproject/pbkdf2@5.6.0", "@ethersproject/pbkdf2@^5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.6.0.tgz#04fcc2d7c6bff88393f5b4237d906a192426685a" + integrity sha512-Wu1AxTgJo3T3H6MIu/eejLFok9TYoSdgwRr5oGY1LTLfmGesDoSx05pemsbrPT2gG4cQME+baTSCp5sEo2erZQ== + dependencies: + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/sha2" "^5.6.0" + "@ethersproject/pbkdf2@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.5.0.tgz#e25032cdf02f31505d47afbf9c3e000d95c4a050" @@ -696,6 +948,13 @@ "@ethersproject/bytes" "^5.5.0" "@ethersproject/sha2" "^5.5.0" +"@ethersproject/properties@5.6.0", "@ethersproject/properties@^5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.6.0.tgz#38904651713bc6bdd5bdd1b0a4287ecda920fa04" + integrity sha512-szoOkHskajKePTJSZ46uHUWWkbv7TzP2ypdEK6jGMqJaEt2sb0jCgfBo0gH0m2HBpRixMuJ6TBRaQCF7a9DoCg== + dependencies: + "@ethersproject/logger" "^5.6.0" + "@ethersproject/properties@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.5.0.tgz#61f00f2bb83376d2071baab02245f92070c59995" @@ -703,6 +962,31 @@ dependencies: "@ethersproject/logger" "^5.5.0" +"@ethersproject/providers@5.6.6": + version "5.6.6" + resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.6.6.tgz#1967149cb4557d253f8c176a44aabda155f228cd" + integrity sha512-6X6agj3NeQ4tgnvBMCjHK+CjQbz+Qmn20JTxCYZ/uymrgCEOpJtY9zeRxJIDsSi0DPw8xNAxypj95JMCsapUfA== + dependencies: + "@ethersproject/abstract-provider" "^5.6.0" + "@ethersproject/abstract-signer" "^5.6.0" + "@ethersproject/address" "^5.6.0" + "@ethersproject/basex" "^5.6.0" + "@ethersproject/bignumber" "^5.6.0" + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/constants" "^5.6.0" + "@ethersproject/hash" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/networks" "^5.6.0" + "@ethersproject/properties" "^5.6.0" + "@ethersproject/random" "^5.6.0" + "@ethersproject/rlp" "^5.6.0" + "@ethersproject/sha2" "^5.6.0" + "@ethersproject/strings" "^5.6.0" + "@ethersproject/transactions" "^5.6.0" + "@ethersproject/web" "^5.6.0" + bech32 "1.1.4" + ws "7.4.6" + "@ethersproject/providers@^5.4.5": version "5.5.3" resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.5.3.tgz#56c2b070542ac44eb5de2ed3cf6784acd60a3130" @@ -728,6 +1012,14 @@ bech32 "1.1.4" ws "7.4.6" +"@ethersproject/random@5.6.0", "@ethersproject/random@^5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.6.0.tgz#1505d1ab6a250e0ee92f436850fa3314b2cb5ae6" + integrity sha512-si0PLcLjq+NG/XHSZz90asNf+YfKEqJGVdxoEkSukzbnBgC8rydbgbUgBbBGLeHN4kAJwUFEKsu3sCXT93YMsw== + dependencies: + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/random@^5.5.0": version "5.5.1" resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.5.1.tgz#7cdf38ea93dc0b1ed1d8e480ccdaf3535c555415" @@ -736,6 +1028,14 @@ "@ethersproject/bytes" "^5.5.0" "@ethersproject/logger" "^5.5.0" +"@ethersproject/rlp@5.6.0", "@ethersproject/rlp@^5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.6.0.tgz#55a7be01c6f5e64d6e6e7edb6061aa120962a717" + integrity sha512-dz9WR1xpcTL+9DtOT/aDO+YyxSSdO8YIS0jyZwHHSlAmnxA6cKU3TrTd4Xc/bHayctxTgGLYNuVVoiXE4tTq1g== + dependencies: + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/rlp@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.5.0.tgz#530f4f608f9ca9d4f89c24ab95db58ab56ab99a0" @@ -744,6 +1044,15 @@ "@ethersproject/bytes" "^5.5.0" "@ethersproject/logger" "^5.5.0" +"@ethersproject/sha2@5.6.0", "@ethersproject/sha2@^5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.6.0.tgz#364c4c11cc753bda36f31f001628706ebadb64d9" + integrity sha512-1tNWCPFLu1n3JM9t4/kytz35DkuF9MxqkGGEHNauEbaARdm2fafnOyw1s0tIQDPKF/7bkP1u3dbrmjpn5CelyA== + dependencies: + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + hash.js "1.1.7" + "@ethersproject/sha2@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.5.0.tgz#a40a054c61f98fd9eee99af2c3cc6ff57ec24db7" @@ -753,6 +1062,18 @@ "@ethersproject/logger" "^5.5.0" hash.js "1.1.7" +"@ethersproject/signing-key@5.6.1", "@ethersproject/signing-key@^5.6.0": + version "5.6.1" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.6.1.tgz#31b0a531520616254eb0465b9443e49515c4d457" + integrity sha512-XvqQ20DH0D+bS3qlrrgh+axRMth5kD1xuvqUQUTeezxUTXBOeR6hWz2/C6FBEu39FRytyybIWrYf7YLSAKr1LQ== + dependencies: + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/properties" "^5.6.0" + bn.js "^4.11.9" + elliptic "6.5.4" + hash.js "1.1.7" + "@ethersproject/signing-key@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.5.0.tgz#2aa37169ce7e01e3e80f2c14325f624c29cedbe0" @@ -765,6 +1086,27 @@ elliptic "6.5.4" hash.js "1.1.7" +"@ethersproject/solidity@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.6.0.tgz#64657362a596bf7f5630bdc921c07dd78df06dc3" + integrity sha512-YwF52vTNd50kjDzqKaoNNbC/r9kMDPq3YzDWmsjFTRBcIF1y4JCQJ8gB30wsTfHbaxgxelI5BfxQSxD/PbJOww== + dependencies: + "@ethersproject/bignumber" "^5.6.0" + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/keccak256" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/sha2" "^5.6.0" + "@ethersproject/strings" "^5.6.0" + +"@ethersproject/strings@5.6.0", "@ethersproject/strings@^5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.6.0.tgz#9891b26709153d996bf1303d39a7f4bc047878fd" + integrity sha512-uv10vTtLTZqrJuqBZR862ZQjTIa724wGPWQqZrofaPI/kUsf53TBG0I0D+hQ1qyNtllbNzaW+PDPHHUI6/65Mg== + dependencies: + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/constants" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/strings@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.5.0.tgz#e6784d00ec6c57710755699003bc747e98c5d549" @@ -774,6 +1116,21 @@ "@ethersproject/constants" "^5.5.0" "@ethersproject/logger" "^5.5.0" +"@ethersproject/transactions@5.6.0", "@ethersproject/transactions@^5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.6.0.tgz#4b594d73a868ef6e1529a2f8f94a785e6791ae4e" + integrity sha512-4HX+VOhNjXHZyGzER6E/LVI2i6lf9ejYeWD6l4g50AdmimyuStKc39kvKf1bXWQMg7QNVh+uC7dYwtaZ02IXeg== + dependencies: + "@ethersproject/address" "^5.6.0" + "@ethersproject/bignumber" "^5.6.0" + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/constants" "^5.6.0" + "@ethersproject/keccak256" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/properties" "^5.6.0" + "@ethersproject/rlp" "^5.6.0" + "@ethersproject/signing-key" "^5.6.0" + "@ethersproject/transactions@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.5.0.tgz#7e9bf72e97bcdf69db34fe0d59e2f4203c7a2908" @@ -789,6 +1146,36 @@ "@ethersproject/rlp" "^5.5.0" "@ethersproject/signing-key" "^5.5.0" +"@ethersproject/units@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.6.0.tgz#e5cbb1906988f5740254a21b9ded6bd51e826d9c" + integrity sha512-tig9x0Qmh8qbo1w8/6tmtyrm/QQRviBh389EQ+d8fP4wDsBrJBf08oZfoiz1/uenKK9M78yAP4PoR7SsVoTjsw== + dependencies: + "@ethersproject/bignumber" "^5.6.0" + "@ethersproject/constants" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + +"@ethersproject/wallet@5.6.1": + version "5.6.1" + resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.6.1.tgz#5df4f75f848ed84ca30fd6ca75d2c66b19c5552b" + integrity sha512-oXWoOslEWtwZiViIMlGVjeKDQz/tI7JF9UkyzN9jaGj8z7sXt2SyFMb0Ev6vSAqjIzrCrNrJ/+MkAhtKnGOfZw== + dependencies: + "@ethersproject/abstract-provider" "^5.6.0" + "@ethersproject/abstract-signer" "^5.6.0" + "@ethersproject/address" "^5.6.0" + "@ethersproject/bignumber" "^5.6.0" + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/hash" "^5.6.0" + "@ethersproject/hdnode" "^5.6.0" + "@ethersproject/json-wallets" "^5.6.0" + "@ethersproject/keccak256" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/properties" "^5.6.0" + "@ethersproject/random" "^5.6.0" + "@ethersproject/signing-key" "^5.6.0" + "@ethersproject/transactions" "^5.6.0" + "@ethersproject/wordlists" "^5.6.0" + "@ethersproject/wallet@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.5.0.tgz#322a10527a440ece593980dca6182f17d54eae75" @@ -810,6 +1197,17 @@ "@ethersproject/transactions" "^5.5.0" "@ethersproject/wordlists" "^5.5.0" +"@ethersproject/web@5.6.0", "@ethersproject/web@^5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.6.0.tgz#4bf8b3cbc17055027e1a5dd3c357e37474eaaeb8" + integrity sha512-G/XHj0hV1FxI2teHRfCGvfBUHFmU+YOSbCxlAMqJklxSa7QMiHFQfAxvwY2PFqgvdkxEKwRNr/eCjfAPEm2Ctg== + dependencies: + "@ethersproject/base64" "^5.6.0" + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/properties" "^5.6.0" + "@ethersproject/strings" "^5.6.0" + "@ethersproject/web@^5.5.0": version "5.5.1" resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.5.1.tgz#cfcc4a074a6936c657878ac58917a61341681316" @@ -821,6 +1219,17 @@ "@ethersproject/properties" "^5.5.0" "@ethersproject/strings" "^5.5.0" +"@ethersproject/wordlists@5.6.0", "@ethersproject/wordlists@^5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.6.0.tgz#79e62c5276e091d8575f6930ba01a29218ded032" + integrity sha512-q0bxNBfIX3fUuAo9OmjlEYxP40IB8ABgb7HjEZCL5IKubzV3j30CWi2rqQbjTS2HfoyQbfINoKcTVWP4ejwR7Q== + dependencies: + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/hash" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/properties" "^5.6.0" + "@ethersproject/strings" "^5.6.0" + "@ethersproject/wordlists@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.5.0.tgz#aac74963aa43e643638e5172353d931b347d584f" @@ -1280,6 +1689,17 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" +"@metamask/eth-sig-util@^4.0.0": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz#3ad61f6ea9ad73ba5b19db780d40d9aae5157088" + integrity sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ== + dependencies: + ethereumjs-abi "^0.6.8" + ethereumjs-util "^6.2.1" + ethjs-util "^0.1.6" + tweetnacl "^1.0.3" + tweetnacl-util "^0.15.1" + "@n1ru4l/graphql-live-query@^0.9.0": version "0.9.0" resolved "https://registry.yarnpkg.com/@n1ru4l/graphql-live-query/-/graphql-live-query-0.9.0.tgz#defaebdd31f625bee49e6745934f36312532b2bc" @@ -1306,6 +1726,11 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@nomiclabs/hardhat-ethers@^2.0.5": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.0.6.tgz#1c695263d5b46a375dcda48c248c4fba9dfe2fc2" + integrity sha512-q2Cjp20IB48rEn2NPjR1qxsIQBvFVYW9rFRCFq+bC4RUrn1Ljz3g4wM8uSlgIBZYBi2JMXxmOzFqHraczxq4Ng== + "@rollup/plugin-commonjs@^21.0.1": version "21.0.2" resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-21.0.2.tgz#0b9c539aa1837c94abfaf87945838b0fc8564891" @@ -1362,11 +1787,86 @@ dependencies: any-observable "^0.3.0" +"@sentry/core@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.30.0.tgz#6b203664f69e75106ee8b5a2fe1d717379b331f3" + integrity sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg== + dependencies: + "@sentry/hub" "5.30.0" + "@sentry/minimal" "5.30.0" + "@sentry/types" "5.30.0" + "@sentry/utils" "5.30.0" + tslib "^1.9.3" + +"@sentry/hub@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.30.0.tgz#2453be9b9cb903404366e198bd30c7ca74cdc100" + integrity sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ== + dependencies: + "@sentry/types" "5.30.0" + "@sentry/utils" "5.30.0" + tslib "^1.9.3" + +"@sentry/minimal@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.30.0.tgz#ce3d3a6a273428e0084adcb800bc12e72d34637b" + integrity sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw== + dependencies: + "@sentry/hub" "5.30.0" + "@sentry/types" "5.30.0" + tslib "^1.9.3" + +"@sentry/node@^5.18.1": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-5.30.0.tgz#4ca479e799b1021285d7fe12ac0858951c11cd48" + integrity sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg== + dependencies: + "@sentry/core" "5.30.0" + "@sentry/hub" "5.30.0" + "@sentry/tracing" "5.30.0" + "@sentry/types" "5.30.0" + "@sentry/utils" "5.30.0" + cookie "^0.4.1" + https-proxy-agent "^5.0.0" + lru_map "^0.3.3" + tslib "^1.9.3" + +"@sentry/tracing@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-5.30.0.tgz#501d21f00c3f3be7f7635d8710da70d9419d4e1f" + integrity sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw== + dependencies: + "@sentry/hub" "5.30.0" + "@sentry/minimal" "5.30.0" + "@sentry/types" "5.30.0" + "@sentry/utils" "5.30.0" + tslib "^1.9.3" + +"@sentry/types@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.30.0.tgz#19709bbe12a1a0115bc790b8942917da5636f402" + integrity sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw== + +"@sentry/utils@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.30.0.tgz#9a5bd7ccff85ccfe7856d493bffa64cabc41e980" + integrity sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww== + dependencies: + "@sentry/types" "5.30.0" + tslib "^1.9.3" + "@sindresorhus/is@^0.14.0": version "0.14.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== +"@solidity-parser/parser@^0.14.1": + version "0.14.1" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.14.1.tgz#179afb29f4e295a77cc141151f26b3848abc3c46" + integrity sha512-eLjj2L6AuQjBB6s/ibwCAc0DwrR5Ge+ys+wgWo+bviU7fV2nTMQhU63CGaDKXg9iTmMxwhkyoggdIR7ZGRfMgw== + dependencies: + antlr4ts "^0.5.0-alpha.4" + "@szmarczak/http-timer@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" @@ -1407,6 +1907,25 @@ lodash "^4.17.15" ts-essentials "^7.0.1" +"@types/abstract-leveldown@*": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@types/abstract-leveldown/-/abstract-leveldown-7.2.0.tgz#f055979a99f7654e84d6b8e6267419e9c4cfff87" + integrity sha512-q5veSX6zjUy/DlDhR4Y4cU0k2Ar+DT2LUraP00T19WLmTO6Se1djepCCaqU6nQrwcJ5Hyo/CWqxTzrrFg8eqbQ== + +"@types/bn.js@^4.11.3": + version "4.11.6" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" + integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== + dependencies: + "@types/node" "*" + +"@types/bn.js@^5.1.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.0.tgz#32c5d271503a12653c62cf4d2b45e6eab8cebc68" + integrity sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA== + dependencies: + "@types/node" "*" + "@types/chai@^4.2.12": version "4.3.0" resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.0.tgz#23509ebc1fa32f1b4d50d6a66c4032d5b8eaabdc" @@ -1449,11 +1968,30 @@ dependencies: "@types/node" "*" +"@types/level-errors@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/level-errors/-/level-errors-3.0.0.tgz#15c1f4915a5ef763b51651b15e90f6dc081b96a8" + integrity sha512-/lMtoq/Cf/2DVOm6zE6ORyOM+3ZVm/BvzEZVxUhf6bgh8ZHglXlBqxbxSlJeVp8FCbD3IVvk/VbsaNmDjrQvqQ== + +"@types/levelup@^4.3.0": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@types/levelup/-/levelup-4.3.3.tgz#4dc2b77db079b1cf855562ad52321aa4241b8ef4" + integrity sha512-K+OTIjJcZHVlZQN1HmU64VtrC0jC3dXWQozuEIR9zVvltIk90zaGPM2AgT+fIkChpzHhFE3YnvFLCbLtzAmexA== + dependencies: + "@types/abstract-leveldown" "*" + "@types/level-errors" "*" + "@types/node" "*" + "@types/lodash@^4.14.177": version "4.14.179" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.179.tgz#490ec3288088c91295780237d2497a3aa9dfb5c5" integrity sha512-uwc1x90yCKqGcIOAT6DwOSuxnrAbpkdPsUOZtwrXb4D/6wZs+6qG7QnIawDuZWg0sWpxl+ltIKCaLoMlna678w== +"@types/lru-cache@^5.1.0": + version "5.1.1" + resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-5.1.1.tgz#c48c2e27b65d2a153b19bfc1a317e30872e01eef" + integrity sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw== + "@types/mocha@^8.0.3": version "8.2.3" resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-8.2.3.tgz#bbeb55fbc73f28ea6de601fbfa4613f58d785323" @@ -1474,6 +2012,13 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== +"@types/pbkdf2@^3.0.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@types/pbkdf2/-/pbkdf2-3.1.0.tgz#039a0e9b67da0cdc4ee5dab865caa6b267bb66b1" + integrity sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ== + dependencies: + "@types/node" "*" + "@types/prettier@^2.1.1": version "2.4.4" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.4.tgz#5d9b63132df54d8909fce1c3f8ca260fdd693e17" @@ -1486,6 +2031,13 @@ dependencies: "@types/node" "*" +"@types/secp256k1@^4.0.1": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.3.tgz#1b8e55d8e00f08ee7220b4d59a6abe89c37a901c" + integrity sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w== + dependencies: + "@types/node" "*" + "@types/websocket@^1.0.4": version "1.0.5" resolved "https://registry.yarnpkg.com/@types/websocket/-/websocket-1.0.5.tgz#3fb80ed8e07f88e51961211cd3682a3a4a81569c" @@ -1582,6 +2134,28 @@ abort-controller@^3.0.0: dependencies: event-target-shim "^5.0.0" +abstract-leveldown@^6.2.1: + version "6.3.0" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.3.0.tgz#d25221d1e6612f820c35963ba4bd739928f6026a" + integrity sha512-TU5nlYgta8YrBMNpc9FwQzRbiXsj49gsALsXadbGHt9CROPzX5fB0rWDR5mtdpOOKa5XqRFpbj1QroPAoPzVjQ== + dependencies: + buffer "^5.5.0" + immediate "^3.2.3" + level-concat-iterator "~2.0.0" + level-supports "~1.0.0" + xtend "~4.0.0" + +abstract-leveldown@~6.2.1: + version "6.2.3" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz#036543d87e3710f2528e47040bc3261b77a9a8eb" + integrity sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ== + dependencies: + buffer "^5.5.0" + immediate "^3.2.3" + level-concat-iterator "~2.0.0" + level-supports "~1.0.0" + xtend "~4.0.0" + acorn-jsx@^5.3.1: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" @@ -1602,6 +2176,11 @@ acorn@^8.4.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== +adm-zip@^0.4.16: + version "0.4.16" + resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.16.tgz#cf4c508fdffab02c269cbc7f471a875f05570365" + integrity sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg== + aes-js@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d" @@ -1614,6 +2193,14 @@ agent-base@6: dependencies: debug "4" +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + ajv@^6.10.0, ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -1644,7 +2231,7 @@ ansi-escapes@^3.0.0: resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== -ansi-escapes@^4.2.1, ansi-escapes@^4.3.1: +ansi-escapes@^4.2.1, ansi-escapes@^4.3.0, ansi-escapes@^4.3.1: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== @@ -1685,6 +2272,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" +antlr4ts@^0.5.0-alpha.4: + version "0.5.0-alpha.4" + resolved "https://registry.yarnpkg.com/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz#71702865a87478ed0b40c0709f422cf14d51652a" + integrity sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ== + any-observable@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.3.0.tgz#af933475e5806a67d0d7df090dd5e8bef65d119b" @@ -1754,6 +2346,20 @@ astral-regex@^2.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== +async-eventemitter@^0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/async-eventemitter/-/async-eventemitter-0.2.4.tgz#f5e7c8ca7d3e46aab9ec40a292baf686a0bafaca" + integrity sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw== + dependencies: + async "^2.4.0" + +async@^2.4.0: + version "2.6.4" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" + integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== + dependencies: + lodash "^4.17.14" + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -1826,6 +2432,13 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +base-x@^3.0.2: + version "3.0.9" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320" + integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ== + dependencies: + safe-buffer "^5.0.1" + base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" @@ -1850,11 +2463,21 @@ bl@^4.1.0: inherits "^2.0.4" readable-stream "^3.4.0" -bn.js@^4.11.9: +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.0.0, bn.js@^4.11.0, bn.js@^4.11.8, 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.1.2, bn.js@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" + integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -1870,7 +2493,7 @@ braces@^3.0.1, braces@~3.0.2: dependencies: fill-range "^7.0.1" -brorand@^1.1.0: +brorand@^1.0.1, brorand@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= @@ -1880,6 +2503,18 @@ browser-stdout@1.3.1: resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== +browserify-aes@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + browserslist@^4.17.5: version "4.20.0" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.20.0.tgz#35951e3541078c125d36df76056e94738a52ebe9" @@ -1891,6 +2526,22 @@ browserslist@^4.17.5: node-releases "^2.0.2" picocolors "^1.0.0" +bs58@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" + integrity sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw== + dependencies: + base-x "^3.0.2" + +bs58check@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc" + integrity sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA== + dependencies: + bs58 "^4.0.0" + create-hash "^1.1.0" + safe-buffer "^5.1.2" + bser@2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" @@ -1908,7 +2559,19 @@ buffer-from@^1.0.0, buffer-from@^1.1.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -buffer@^5.5.0, buffer@^5.7.0: +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ== + +buffer-xor@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-2.0.2.tgz#34f7c64f04c777a1f8aac5e661273bb9dd320289" + integrity sha512-eHslX0bin3GB+Lx2p7lEYRShRewuNZL3fUl4qlVJGGiwoPGftmt8JQgk2Y9Ji5/01TnVDo33E5b5O3vUB1HdqQ== + dependencies: + safe-buffer "^5.1.1" + +buffer@^5.5.0, buffer@^5.6.0, buffer@^5.7.0: version "5.7.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== @@ -1921,6 +2584,11 @@ builtin-modules@^3.1.0: resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.2.0.tgz#45d5db99e7ee5e6bc4f362e008bf917ab5049887" integrity sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA== +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + cacheable-request@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" @@ -2003,7 +2671,7 @@ chalk@^1.0.0, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.4.1: +chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -2079,7 +2747,7 @@ chokidar@3.5.1: optionalDependencies: fsevents "~2.3.1" -chokidar@^3.5.2: +chokidar@3.5.3, chokidar@^3.4.0, chokidar@^3.5.2: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== @@ -2094,6 +2762,24 @@ chokidar@^3.5.2: optionalDependencies: fsevents "~2.3.2" +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + cli-cursor@^2.0.0, cli-cursor@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" @@ -2192,6 +2878,11 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" +command-exists@^1.2.8: + version "1.2.9" + resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" + integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== + command-line-args@^4.0.7: version "4.0.7" resolved "https://registry.yarnpkg.com/command-line-args/-/command-line-args-4.0.7.tgz#f8d1916ecb90e9e121eda6428e41300bfb64cc46" @@ -2201,6 +2892,11 @@ command-line-args@^4.0.7: find-replace "^1.0.3" typical "^2.6.1" +commander@3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e" + integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow== + common-tags@1.8.2, common-tags@^1.8.0: version "1.8.2" resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6" @@ -2232,6 +2928,16 @@ convert-source-map@^1.7.0: dependencies: safe-buffer "~5.1.1" +cookie@^0.4.1: + version "0.4.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" + integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== + +core-js-pure@^3.0.1: + version "3.22.5" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.22.5.tgz#bdee0ed2f9b78f2862cda4338a07b13a49b6c9a9" + integrity sha512-8xo9R00iYD7TcV7OrC98GwxiUEAabVWO3dix+uyWjnYrx9fyASLlIX+f/3p5dW5qByaP2bcZ8X/T47s55et/tA== + cosmiconfig-toml-loader@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/cosmiconfig-toml-loader/-/cosmiconfig-toml-loader-1.0.0.tgz#0681383651cceff918177debe9084c0d3769509b" @@ -2250,6 +2956,34 @@ cosmiconfig@7.0.1, cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" +crc-32@^1.2.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" + integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ== + +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.4, create-hmac@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + create-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" @@ -2298,7 +3032,7 @@ debounce@^1.2.0: resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5" integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug== -debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: +debug@4, debug@4.3.3, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: version "4.3.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== @@ -2312,6 +3046,13 @@ debug@4.3.1: dependencies: ms "2.1.2" +debug@^4.3.3: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -2363,6 +3104,14 @@ defer-to-connect@^1.0.1: resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== +deferred-leveldown@~5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-5.3.0.tgz#27a997ad95408b61161aa69bd489b86c71b78058" + integrity sha512-a59VOT+oDy7vtAbLRCZwWgxu2BaCfd5Hk7wxJd48ei7I+nsg8Orlb9CLG0PMZienk9BSUKgeAqkO2+Lw+1+Ukw== + dependencies: + abstract-leveldown "~6.2.1" + inherits "^2.0.3" + define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" @@ -2375,6 +3124,11 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + dependency-graph@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/dependency-graph/-/dependency-graph-0.11.0.tgz#ac0ce7ed68a54da22165a85e97a01d53f5eb2e27" @@ -2459,7 +3213,7 @@ elegant-spinner@^1.0.1: resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e" integrity sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4= -elliptic@6.5.4: +elliptic@6.5.4, elliptic@^6.5.2, 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== @@ -2477,6 +3231,16 @@ 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== +encoding-down@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/encoding-down/-/encoding-down-6.3.0.tgz#b1c4eb0e1728c146ecaef8e32963c549e76d082b" + integrity sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw== + dependencies: + abstract-leveldown "^6.2.1" + inherits "^2.0.3" + level-codec "^9.0.0" + level-errors "^2.0.0" + encoding@^0.1.11: version "0.1.13" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" @@ -2491,13 +3255,25 @@ end-of-stream@^1.1.0: dependencies: once "^1.4.0" -enquirer@^2.3.5: +enquirer@^2.3.0, enquirer@^2.3.5: version "2.3.6" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== dependencies: ansi-colors "^4.1.1" +env-paths@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" + integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== + +errno@~0.1.1: + version "0.1.8" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" + integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== + dependencies: + prr "~1.0.1" + error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" @@ -2665,6 +3441,103 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== +ethereum-cryptography@^0.1.2, ethereum-cryptography@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz#8d6143cfc3d74bf79bbd8edecdf29e4ae20dd191" + integrity sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ== + dependencies: + "@types/pbkdf2" "^3.0.0" + "@types/secp256k1" "^4.0.1" + blakejs "^1.1.0" + browserify-aes "^1.2.0" + bs58check "^2.1.2" + create-hash "^1.2.0" + create-hmac "^1.1.7" + hash.js "^1.1.7" + keccak "^3.0.0" + pbkdf2 "^3.0.17" + randombytes "^2.1.0" + safe-buffer "^5.1.2" + scrypt-js "^3.0.0" + secp256k1 "^4.0.1" + setimmediate "^1.0.5" + +ethereumjs-abi@^0.6.8: + version "0.6.8" + resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz#71bc152db099f70e62f108b7cdfca1b362c6fcae" + integrity sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA== + dependencies: + bn.js "^4.11.8" + ethereumjs-util "^6.0.0" + +ethereumjs-util@^6.0.0, ethereumjs-util@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz#fcb4e4dd5ceacb9d2305426ab1a5cd93e3163b69" + integrity sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw== + dependencies: + "@types/bn.js" "^4.11.3" + bn.js "^4.11.0" + create-hash "^1.1.2" + elliptic "^6.5.2" + ethereum-cryptography "^0.1.3" + ethjs-util "0.1.6" + rlp "^2.2.3" + +ethereumjs-util@^7.1.1, ethereumjs-util@^7.1.4: + version "7.1.4" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.1.4.tgz#a6885bcdd92045b06f596c7626c3e89ab3312458" + integrity sha512-p6KmuPCX4mZIqsQzXfmSx9Y0l2hqf+VkAiwSisW3UKUFdk8ZkAt+AYaor83z2nSi6CU2zSsXMlD80hAbNEGM0A== + dependencies: + "@types/bn.js" "^5.1.0" + bn.js "^5.1.2" + create-hash "^1.1.2" + ethereum-cryptography "^0.1.3" + rlp "^2.2.4" + +ethers@^5.0.0: + version "5.6.6" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.6.6.tgz#a37aa7e265a484a1b4d2ef91d4d89d6b43808a57" + integrity sha512-2B2ZmSGvRcJpHnFMBk58mkXP50njFipUBCgLK8jUTFbomhVs501cLzyMU6+Vx8YnUDQxywC3qkZvd33xWS+2FA== + dependencies: + "@ethersproject/abi" "5.6.2" + "@ethersproject/abstract-provider" "5.6.0" + "@ethersproject/abstract-signer" "5.6.1" + "@ethersproject/address" "5.6.0" + "@ethersproject/base64" "5.6.0" + "@ethersproject/basex" "5.6.0" + "@ethersproject/bignumber" "5.6.1" + "@ethersproject/bytes" "5.6.1" + "@ethersproject/constants" "5.6.0" + "@ethersproject/contracts" "5.6.1" + "@ethersproject/hash" "5.6.0" + "@ethersproject/hdnode" "5.6.1" + "@ethersproject/json-wallets" "5.6.0" + "@ethersproject/keccak256" "5.6.0" + "@ethersproject/logger" "5.6.0" + "@ethersproject/networks" "5.6.2" + "@ethersproject/pbkdf2" "5.6.0" + "@ethersproject/properties" "5.6.0" + "@ethersproject/providers" "5.6.6" + "@ethersproject/random" "5.6.0" + "@ethersproject/rlp" "5.6.0" + "@ethersproject/sha2" "5.6.0" + "@ethersproject/signing-key" "5.6.1" + "@ethersproject/solidity" "5.6.0" + "@ethersproject/strings" "5.6.0" + "@ethersproject/transactions" "5.6.0" + "@ethersproject/units" "5.6.0" + "@ethersproject/wallet" "5.6.1" + "@ethersproject/web" "5.6.0" + "@ethersproject/wordlists" "5.6.0" + +ethjs-util@0.1.6, ethjs-util@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ethjs-util/-/ethjs-util-0.1.6.tgz#f308b62f185f9fe6237132fb2a9818866a5cd536" + integrity sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w== + dependencies: + is-hex-prefixed "1.0.0" + strip-hex-prefix "1.0.0" + event-target-shim@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" @@ -2675,6 +3548,14 @@ eventemitter3@^3.1.0: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== +evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + external-editor@^3.0.3: version "3.1.0" resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" @@ -2809,6 +3690,13 @@ find-up@5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" +find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= + dependencies: + locate-path "^2.0.0" + find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" @@ -2817,6 +3705,13 @@ find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" +fishery@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/fishery/-/fishery-2.2.2.tgz#94d3d9380295dd3ce555021e9353c5348b8beb77" + integrity sha512-jeU0nDhPHJkupmjX+r9niKgVMTBDB8X+U/pktoGHAiWOSyNlMd0HhmqnjrpjUOCDPJYaSSu4Ze16h6dZOKSp2w== + dependencies: + lodash.mergewith "^4.6.2" + flat-cache@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" @@ -2835,6 +3730,11 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3" integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== +follow-redirects@^1.12.1: + version "1.15.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.0.tgz#06441868281c86d0dda4ad8bdaead2d02dca89d4" + integrity sha512-aExlJShTV4qOUOL7yF1U5tvLCB0xQuudbf6toyYA0E/acBNw71mvjFTnLaRp50aQaYocMR0a/RMMBIHeZnGyjQ== + follow-redirects@^1.14.4: version "1.14.9" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7" @@ -2862,7 +3762,28 @@ formdata-node@^4.3.1: node-domexception "1.0.0" web-streams-polyfill "4.0.0-beta.1" -fs-extra@^7.0.0: +fp-ts@1.19.3: + version "1.19.3" + resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-1.19.3.tgz#261a60d1088fbff01f91256f91d21d0caaaaa96f" + integrity sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg== + +fp-ts@^1.0.0: + version "1.19.5" + resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-1.19.5.tgz#3da865e585dfa1fdfd51785417357ac50afc520a" + integrity sha512-wDNqTimnzs8QqpldiId9OavWK2NptormjXnRJTQecNjzwfyp6P/8s/zG8e4h3ja3oqkKaY72UlTjQYt/1yXf9A== + +fs-extra@^0.30.0: + version "0.30.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0" + integrity sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A= + dependencies: + graceful-fs "^4.1.2" + jsonfile "^2.1.0" + klaw "^1.0.0" + path-is-absolute "^1.0.0" + rimraf "^2.2.8" + +fs-extra@^7.0.0, fs-extra@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== @@ -2886,7 +3807,7 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -functional-red-black-tree@^1.0.1: +functional-red-black-tree@^1.0.1, functional-red-black-tree@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= @@ -2948,7 +3869,7 @@ glob@7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.1.1, glob@^7.1.3, glob@^7.1.6: +glob@7.2.0, glob@^7.1.1, glob@^7.1.3, glob@^7.1.6: version "7.2.0" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== @@ -3006,6 +3927,11 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.6: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== +graceful-fs@^4.1.9: + version "4.2.10" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" + integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== + graphql-config@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/graphql-config/-/graphql-config-4.1.0.tgz#a3b28d3fb537952ebeb69c75e4430605a10695e3" @@ -3073,6 +3999,60 @@ growl@1.10.5: resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== +hardhat@^2.9.3: + version "2.9.5" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.9.5.tgz#6814a9f3afd5630ffe6bcb05a4367eb259c9222a" + integrity sha512-UIhjLQmccFOH87ODfFnVatI5vpwycsJ+D5+gmgOQNxUWp4c0ZenkeCE4yDEQ0tQm/zc/vz/mpskULz4aSFsPAg== + dependencies: + "@ethereumjs/block" "^3.6.2" + "@ethereumjs/blockchain" "^5.5.2" + "@ethereumjs/common" "^2.6.4" + "@ethereumjs/tx" "^3.5.1" + "@ethereumjs/vm" "^5.9.0" + "@ethersproject/abi" "^5.1.2" + "@metamask/eth-sig-util" "^4.0.0" + "@sentry/node" "^5.18.1" + "@solidity-parser/parser" "^0.14.1" + "@types/bn.js" "^5.1.0" + "@types/lru-cache" "^5.1.0" + abort-controller "^3.0.0" + adm-zip "^0.4.16" + aggregate-error "^3.0.0" + ansi-escapes "^4.3.0" + chalk "^2.4.2" + chokidar "^3.4.0" + ci-info "^2.0.0" + debug "^4.1.1" + enquirer "^2.3.0" + env-paths "^2.2.0" + ethereum-cryptography "^0.1.2" + ethereumjs-abi "^0.6.8" + ethereumjs-util "^7.1.4" + find-up "^2.1.0" + fp-ts "1.19.3" + fs-extra "^7.0.1" + glob "^7.1.3" + immutable "^4.0.0-rc.12" + io-ts "1.10.4" + lodash "^4.17.11" + merkle-patricia-tree "^4.2.4" + mnemonist "^0.38.0" + mocha "^9.2.0" + p-map "^4.0.0" + qs "^6.7.0" + raw-body "^2.4.1" + resolve "1.17.0" + semver "^6.3.0" + slash "^3.0.0" + solc "0.7.3" + source-map-support "^0.5.13" + stacktrace-parser "^0.1.10" + "true-case-path" "^2.2.1" + tsort "0.0.1" + undici "^4.14.1" + uuid "^8.3.2" + ws "^7.4.6" + has-ansi@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" @@ -3102,7 +4082,16 @@ 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: +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== @@ -3137,6 +4126,17 @@ http-cache-semantics@^4.0.0: resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + http-proxy-agent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" @@ -3154,7 +4154,7 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" -iconv-lite@^0.4.24: +iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -3183,6 +4183,21 @@ ignore@^5.1.8, ignore@^5.2.0: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== +immediate@^3.2.3: + version "3.3.0" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.3.0.tgz#1aef225517836bcdf7f2a2de2600c79ff0269266" + integrity sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q== + +immediate@~3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.2.3.tgz#d140fa8f614659bd6541233097ddaac25cdd991c" + integrity sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw= + +immutable@^4.0.0-rc.12: + version "4.0.0" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.0.0.tgz#b86f78de6adef3608395efb269a91462797e2c23" + integrity sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw== + immutable@~3.7.6: version "3.7.6" resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.7.6.tgz#13b4d3cb12befa15482a26fe1b2ebae640071e4b" @@ -3211,6 +4226,11 @@ indent-string@^3.0.0: resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok= +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -3219,7 +4239,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.3, inherits@^2.0.4: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -3256,6 +4276,13 @@ invariant@^2.2.4: dependencies: loose-envify "^1.0.0" +io-ts@1.10.4: + version "1.10.4" + resolved "https://registry.yarnpkg.com/io-ts/-/io-ts-1.10.4.tgz#cd5401b138de88e4f920adbcb7026e2d1967e6e2" + integrity sha512-b23PteSnYXSONJ6JQXRAlvJhuw8KOtkqa87W4wDtvMrud/DTJd5X+NpOOI+O/zZwVq6v0VLAaJ+1EDViKEuN9g== + dependencies: + fp-ts "^1.0.0" + is-absolute@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-1.0.0.tgz#395e1ae84b11f26ad1795e73c17378e48a301576" @@ -3312,6 +4339,11 @@ is-glob@4.0.3, is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-hex-prefixed@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz#7d8d37e6ad77e5d127148913c573e082d777f554" + integrity sha1-fY035q135dEnFIkTxXPggtd39VQ= + is-interactive@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" @@ -3442,6 +4474,13 @@ js-yaml@4.0.0: dependencies: argparse "^2.0.1" +js-yaml@4.1.0, js-yaml@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + js-yaml@^3.13.1: version "3.14.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" @@ -3450,13 +4489,6 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== - dependencies: - argparse "^2.0.1" - jsesc@^2.5.1: version "2.5.2" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" @@ -3516,6 +4548,13 @@ json5@^2.1.2: dependencies: minimist "^1.2.5" +jsonfile@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" + integrity sha1-NzaitCi4e72gzIO1P6PWM6NcKug= + optionalDependencies: + graceful-fs "^4.1.6" + jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -3561,6 +4600,15 @@ jws@^3.2.2: jwa "^1.4.1" safe-buffer "^5.0.1" +keccak@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.2.tgz#4c2c6e8c54e04f2670ee49fa734eb9da152206e0" + integrity sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ== + dependencies: + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + readable-stream "^3.6.0" + keyv@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" @@ -3568,6 +4616,13 @@ keyv@^3.0.0: dependencies: json-buffer "3.0.0" +klaw@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" + integrity sha1-QIhDO0azsbolnXh4XY6W9zugJDk= + optionalDependencies: + graceful-fs "^4.1.9" + latest-version@5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" @@ -3575,6 +4630,77 @@ latest-version@5.1.0: dependencies: package-json "^6.3.0" +level-codec@^9.0.0: + version "9.0.2" + resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-9.0.2.tgz#fd60df8c64786a80d44e63423096ffead63d8cbc" + integrity sha512-UyIwNb1lJBChJnGfjmO0OR+ezh2iVu1Kas3nvBS/BzGnx79dv6g7unpKIDNPMhfdTEGoc7mC8uAu51XEtX+FHQ== + dependencies: + buffer "^5.6.0" + +level-concat-iterator@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz#1d1009cf108340252cb38c51f9727311193e6263" + integrity sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw== + +level-errors@^2.0.0, level-errors@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-2.0.1.tgz#2132a677bf4e679ce029f517c2f17432800c05c8" + integrity sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw== + dependencies: + errno "~0.1.1" + +level-iterator-stream@~4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-4.0.2.tgz#7ceba69b713b0d7e22fcc0d1f128ccdc8a24f79c" + integrity sha512-ZSthfEqzGSOMWoUGhTXdX9jv26d32XJuHz/5YnuHZzH6wldfWMOVwI9TBtKcya4BKTyTt3XVA0A3cF3q5CY30Q== + dependencies: + inherits "^2.0.4" + readable-stream "^3.4.0" + xtend "^4.0.2" + +level-mem@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/level-mem/-/level-mem-5.0.1.tgz#c345126b74f5b8aa376dc77d36813a177ef8251d" + integrity sha512-qd+qUJHXsGSFoHTziptAKXoLX87QjR7v2KMbqncDXPxQuCdsQlzmyX+gwrEHhlzn08vkf8TyipYyMmiC6Gobzg== + dependencies: + level-packager "^5.0.3" + memdown "^5.0.0" + +level-packager@^5.0.3: + version "5.1.1" + resolved "https://registry.yarnpkg.com/level-packager/-/level-packager-5.1.1.tgz#323ec842d6babe7336f70299c14df2e329c18939" + integrity sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ== + dependencies: + encoding-down "^6.3.0" + levelup "^4.3.2" + +level-supports@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-1.0.1.tgz#2f530a596834c7301622521988e2c36bb77d122d" + integrity sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg== + dependencies: + xtend "^4.0.2" + +level-ws@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/level-ws/-/level-ws-2.0.0.tgz#207a07bcd0164a0ec5d62c304b4615c54436d339" + integrity sha512-1iv7VXx0G9ec1isqQZ7y5LmoZo/ewAsyDHNA8EFDW5hqH2Kqovm33nSFkSdnLLAK+I5FlT+lo5Cw9itGe+CpQA== + dependencies: + inherits "^2.0.3" + readable-stream "^3.1.0" + xtend "^4.0.1" + +levelup@^4.3.2: + version "4.4.0" + resolved "https://registry.yarnpkg.com/levelup/-/levelup-4.4.0.tgz#f89da3a228c38deb49c48f88a70fb71f01cafed6" + integrity sha512-94++VFO3qN95cM/d6eBXvd894oJE0w3cInq9USsyQzzoJxmiYzPAocNcuGCPGGjoXqDVJcr3C1jzt1TSjyaiLQ== + dependencies: + deferred-leveldown "~5.3.0" + level-errors "~2.0.0" + level-iterator-stream "~4.0.0" + level-supports "~1.0.0" + xtend "~4.0.0" + levn@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" @@ -3632,6 +4758,14 @@ listr@^0.14.3: p-map "^2.0.0" rxjs "^6.3.3" +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + locate-path@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" @@ -3686,6 +4820,11 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.mergewith@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55" + integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== + lodash.once@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" @@ -3696,7 +4835,7 @@ lodash.truncate@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= -lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@~4.17.0: +lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@~4.17.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -3708,14 +4847,7 @@ log-symbols@4.0.0: dependencies: chalk "^4.0.0" -log-symbols@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" - integrity sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg= - dependencies: - chalk "^1.0.0" - -log-symbols@^4.0.0, log-symbols@^4.1.0: +log-symbols@4.1.0, log-symbols@^4.0.0, log-symbols@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== @@ -3723,6 +4855,13 @@ log-symbols@^4.0.0, log-symbols@^4.1.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" +log-symbols@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18" + integrity sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg= + dependencies: + chalk "^1.0.0" + log-update@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/log-update/-/log-update-2.3.0.tgz#88328fd7d1ce7938b29283746f0b1bc126b24708" @@ -3770,6 +4909,13 @@ lowercase-keys@^2.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -3777,6 +4923,16 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lru_map@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" + integrity sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0= + +ltgt@~2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5" + integrity sha1-81ypHEk/e3PaDgdJUwTxezH4fuU= + magic-string@^0.25.7: version "0.25.9" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" @@ -3794,11 +4950,54 @@ map-cache@^0.2.0: resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= +mcl-wasm@^0.7.1: + version "0.7.9" + resolved "https://registry.yarnpkg.com/mcl-wasm/-/mcl-wasm-0.7.9.tgz#c1588ce90042a8700c3b60e40efb339fc07ab87f" + integrity sha512-iJIUcQWA88IJB/5L15GnJVnSQJmf/YaxxV6zRavv83HILHaJQb6y0iFyDMdDO0gN8X37tdxmAOrH/P8B6RB8sQ== + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +memdown@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/memdown/-/memdown-5.1.0.tgz#608e91a9f10f37f5b5fe767667a8674129a833cb" + integrity sha512-B3J+UizMRAlEArDjWHTMmadet+UKwHd3UjMgGBkZcKAxAYVPS9o0Yeiha4qvz7iGiL2Sb3igUft6p7nbFWctpw== + dependencies: + abstract-leveldown "~6.2.1" + functional-red-black-tree "~1.0.1" + immediate "~3.2.3" + inherits "~2.0.1" + ltgt "~2.2.0" + safe-buffer "~5.2.0" + +memorystream@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" + integrity sha1-htcJCzDORV1j+64S3aUaR93K+bI= + merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== +merkle-patricia-tree@^4.2.4: + version "4.2.4" + resolved "https://registry.yarnpkg.com/merkle-patricia-tree/-/merkle-patricia-tree-4.2.4.tgz#ff988d045e2bf3dfa2239f7fabe2d59618d57413" + integrity sha512-eHbf/BG6eGNsqqfbLED9rIqbsF4+sykEaBn6OLNs71tjclbMcMOk1tEPmJKcNcNCLkvbpY/lwyOlizWsqPNo8w== + dependencies: + "@types/levelup" "^4.3.0" + ethereumjs-util "^7.1.4" + level-mem "^5.0.1" + level-ws "^2.0.0" + readable-stream "^3.6.0" + semaphore-async-await "^1.5.1" + meros@^1.1.4: version "1.2.0" resolved "https://registry.yarnpkg.com/meros/-/meros-1.2.0.tgz#096cdede2eb0b1610b219b1031b935260de1ad08" @@ -3812,6 +5011,14 @@ micromatch@^4.0.4: braces "^3.0.1" picomatch "^2.2.3" +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + mime-db@1.51.0: version "1.51.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.51.0.tgz#d9ff62451859b18342d960850dc3cfb77e63fb0c" @@ -3856,6 +5063,13 @@ minimatch@3.0.4: dependencies: brace-expansion "^1.1.7" +minimatch@4.2.1, minimatch@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-4.2.1.tgz#40d9d511a46bdc4e563c22c3080cde9c0d8299b4" + integrity sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g== + dependencies: + brace-expansion "^1.1.7" + minimatch@^3.0.4: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -3863,13 +5077,6 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" -minimatch@^4.0.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-4.2.1.tgz#40d9d511a46bdc4e563c22c3080cde9c0d8299b4" - integrity sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g== - dependencies: - brace-expansion "^1.1.7" - minimist@^1.2.0, minimist@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" @@ -3887,6 +5094,13 @@ mkdirp@^1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== +mnemonist@^0.38.0: + version "0.38.5" + resolved "https://registry.yarnpkg.com/mnemonist/-/mnemonist-0.38.5.tgz#4adc7f4200491237fe0fa689ac0b86539685cade" + integrity sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg== + dependencies: + obliterator "^2.0.0" + mocha@^8.2.1: version "8.4.0" resolved "https://registry.yarnpkg.com/mocha/-/mocha-8.4.0.tgz#677be88bf15980a3cae03a73e10a0fc3997f0cff" @@ -3918,6 +5132,36 @@ mocha@^8.2.1: yargs-parser "20.2.4" yargs-unparser "2.0.0" +mocha@^9.2.0: + version "9.2.2" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.2.2.tgz#d70db46bdb93ca57402c809333e5a84977a88fb9" + integrity sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g== + dependencies: + "@ungap/promise-all-settled" "1.1.2" + ansi-colors "4.1.1" + browser-stdout "1.3.1" + chokidar "3.5.3" + debug "4.3.3" + diff "5.0.0" + escape-string-regexp "4.0.0" + find-up "5.0.0" + glob "7.2.0" + growl "1.10.5" + he "1.2.0" + js-yaml "4.1.0" + log-symbols "4.1.0" + minimatch "4.2.1" + ms "2.1.3" + nanoid "3.3.1" + serialize-javascript "6.0.0" + strip-json-comments "3.1.1" + supports-color "8.1.1" + which "2.0.2" + workerpool "6.2.0" + yargs "16.2.0" + yargs-parser "20.2.4" + yargs-unparser "2.0.0" + ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" @@ -3938,6 +5182,11 @@ nanoid@3.1.20: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788" integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw== +nanoid@3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35" + integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -3951,6 +5200,11 @@ no-case@^3.0.4: lower-case "^2.0.2" tslib "^2.0.3" +node-addon-api@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" + integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== + node-domexception@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" @@ -3971,6 +5225,11 @@ node-fetch@^1.0.1: encoding "^0.1.11" is-stream "^1.0.1" +node-gyp-build@^4.2.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.4.0.tgz#42e99687ce87ddeaf3a10b99dc06abc11021f3f4" + integrity sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ== + node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -4013,6 +5272,11 @@ object-assign@^4.1.0: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= +object-inspect@^1.9.0: + version "1.12.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" + integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== + object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -4028,6 +5292,11 @@ object.assign@^4.1.0: has-symbols "^1.0.1" object-keys "^1.1.1" +obliterator@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/obliterator/-/obliterator-2.0.4.tgz#fa650e019b2d075d745e44f1effeb13a2adbe816" + integrity sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ== + once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -4093,6 +5362,13 @@ p-limit@3.1.0, p-limit@^3.0.2: dependencies: yocto-queue "^0.1.0" +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== + dependencies: + p-try "^1.0.0" + p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -4100,6 +5376,13 @@ p-limit@^2.2.0: dependencies: p-try "^2.0.0" +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= + dependencies: + p-limit "^1.1.0" + p-locate@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" @@ -4119,6 +5402,18 @@ p-map@^2.0.0: resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= + p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" @@ -4184,6 +5479,11 @@ path-case@^3.0.4: dot-case "^3.0.4" tslib "^2.0.3" +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= + path-exists@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" @@ -4199,7 +5499,7 @@ path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -path-parse@^1.0.7: +path-parse@^1.0.6, path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== @@ -4226,6 +5526,17 @@ pathval@^1.1.1: resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== +pbkdf2@^3.0.17: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" + integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" @@ -4270,6 +5581,11 @@ promise@^7.1.1: dependencies: asap "~2.0.3" +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= + pump@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" @@ -4283,6 +5599,13 @@ punycode@^2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +qs@^6.7.0: + version "6.10.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e" + integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ== + dependencies: + side-channel "^1.0.4" + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -4295,6 +5618,16 @@ randombytes@^2.1.0: dependencies: safe-buffer "^5.1.0" +raw-body@^2.4.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" + integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + rc@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" @@ -4305,7 +5638,7 @@ rc@^1.2.8: minimist "^1.2.0" strip-json-comments "~2.0.1" -readable-stream@^3.4.0: +readable-stream@^3.1.0, readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -4409,7 +5742,7 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= -require-from-string@^2.0.2: +require-from-string@^2.0.0, 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== @@ -4434,6 +5767,13 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== +resolve@1.17.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== + dependencies: + path-parse "^1.0.6" + resolve@^1.17.0, resolve@^1.19.0: version "1.22.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" @@ -4471,6 +5811,13 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== +rimraf@^2.2.8: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -4478,6 +5825,21 @@ rimraf@^3.0.2: dependencies: glob "^7.1.3" +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +rlp@^2.2.3, rlp@^2.2.4: + version "2.2.7" + resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.7.tgz#33f31c4afac81124ac4b283e2bd4d9720b30beaf" + integrity sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ== + dependencies: + bn.js "^5.2.0" + rollup-plugin-dts@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rollup-plugin-dts/-/rollup-plugin-dts-3.0.2.tgz#2b628d88f864d271d6eaec2e4c2a60ae4e944c5c" @@ -4506,6 +5868,11 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" +rustbn.js@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/rustbn.js/-/rustbn.js-0.2.0.tgz#8082cb886e707155fd1cb6f23bd591ab8d55d0ca" + integrity sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA== + rxjs@^6.3.3: version "6.6.7" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" @@ -4520,7 +5887,7 @@ rxjs@^7.5.5: dependencies: tslib "^2.1.0" -safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0: +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -4535,7 +5902,7 @@ safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -scrypt-js@3.0.1: +scrypt-js@3.0.1, scrypt-js@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== @@ -4545,7 +5912,21 @@ 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@^5.6.0: +secp256k1@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.3.tgz#c4559ecd1b8d3c1827ed2d1b94190d69ce267303" + integrity sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA== + dependencies: + elliptic "^6.5.4" + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + +semaphore-async-await@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/semaphore-async-await/-/semaphore-async-await-1.5.1.tgz#857bef5e3644601ca4b9570b87e9df5ca12974fa" + integrity sha1-hXvvXjZEYBykuVcLh+nfXKEpdPo= + +semver@^5.5.0, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -4578,6 +5959,13 @@ serialize-javascript@5.0.1: dependencies: randombytes "^2.1.0" +serialize-javascript@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" + integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== + dependencies: + randombytes "^2.1.0" + set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -4588,6 +5976,19 @@ setimmediate@^1.0.5: resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -4600,6 +6001,15 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + signal-exit@^3.0.2: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" @@ -4637,7 +6047,22 @@ snake-case@^3.0.4: dot-case "^3.0.4" tslib "^2.0.3" -source-map-support@^0.5.17, source-map-support@^0.5.6: +solc@0.7.3: + version "0.7.3" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.7.3.tgz#04646961bd867a744f63d2b4e3c0701ffdc7d78a" + integrity sha512-GAsWNAjGzIDg7VxzP6mPjdurby3IkGCjQcM8GFYZT6RyaoUZKmMU6Y7YwG+tFGhv7dwZ8rmR4iwFDrrD99JwqA== + dependencies: + command-exists "^1.2.8" + commander "3.0.2" + follow-redirects "^1.12.1" + fs-extra "^0.30.0" + js-sha3 "0.8.0" + memorystream "^0.3.1" + require-from-string "^2.0.0" + semver "^5.5.0" + tmp "0.0.33" + +source-map-support@^0.5.13, source-map-support@^0.5.17, source-map-support@^0.5.6: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== @@ -4672,6 +6097,18 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= +stacktrace-parser@^0.1.10: + version "0.1.10" + resolved "https://registry.yarnpkg.com/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz#29fb0cae4e0d0b85155879402857a1639eb6051a" + integrity sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg== + dependencies: + type-fest "^0.7.1" + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + string-env-interpolation@1.0.1, string-env-interpolation@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/string-env-interpolation/-/string-env-interpolation-1.0.1.tgz#ad4397ae4ac53fe6c91d1402ad6f6a52862c7152" @@ -4736,6 +6173,13 @@ strip-bom@^3.0.0: resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= +strip-hex-prefix@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz#0c5f155fef1151373377de9dbb588da05500e36f" + integrity sha1-DF8VX+8RUTczd96du1iNoFUA428= + dependencies: + is-hex-prefixed "1.0.0" + strip-json-comments@3.1.1, 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" @@ -4849,7 +6293,7 @@ title-case@^3.0.3: dependencies: tslib "^2.0.3" -tmp@^0.0.33: +tmp@0.0.33, tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== @@ -4873,11 +6317,21 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +toidentifier@1.0.1: + version "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 sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= +"true-case-path@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-2.2.1.tgz#c5bf04a5bbec3fd118be4084461b3a27c4d796bf" + integrity sha512-0z3j8R7MCjy10kc/g+qg7Ln3alJTodw9aDuVWZa3uiWqfuBMKeAeP2ocWcxoyM3D73yz3Jt/Pu4qPr4wHSdB/Q== + ts-essentials@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-7.0.3.tgz#686fd155a02133eedcc5362dc8b5056cde3e5a38" @@ -4952,7 +6406,7 @@ tsconfig-paths@^3.12.0, tsconfig-paths@^3.5.0: minimist "^1.2.0" strip-bom "^3.0.0" -tslib@^1.8.1, tslib@^1.9.0: +tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== @@ -4962,6 +6416,11 @@ tslib@^2, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@~2.3.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== +tsort@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/tsort/-/tsort-0.0.1.tgz#e2280f5e817f8bf4275657fd0f9aebd44f5a2786" + integrity sha1-4igPXoF/i/QnVlf9D5rr1E9aJ4Y= + tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" @@ -4969,6 +6428,16 @@ tsutils@^3.21.0: dependencies: tslib "^1.8.1" +tweetnacl-util@^0.15.1: + version "0.15.1" + resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz#b80fcdb5c97bcc508be18c44a4be50f022eea00b" + integrity sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw== + +tweetnacl@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" + integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -4991,6 +6460,11 @@ 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.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48" + integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg== + typechain@^5.1.1: version "5.2.0" resolved "https://registry.yarnpkg.com/typechain/-/typechain-5.2.0.tgz#10525a44773a34547eb2eed8978cb72c0a39a0f4" @@ -5027,6 +6501,11 @@ unc-path-regex@^0.1.2: resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" integrity sha1-5z3T17DXxe2G+6xrCufYxqadUPo= +undici@^4.14.1: + version "4.16.0" + resolved "https://registry.yarnpkg.com/undici/-/undici-4.16.0.tgz#469bb87b3b918818d3d7843d91a1d08da357d5ff" + integrity sha512-tkZSECUYi+/T1i4u+4+lwZmQgLXd4BLGlrc7KZPcLIW7Jpq99+Xpc30ONv7nS6F5UNOxp/HBZSSL9MafUrvJbw== + undici@^4.9.3: version "4.15.1" resolved "https://registry.yarnpkg.com/undici/-/undici-4.15.1.tgz#c2c0e75f232178f0e6781f6b46c81ccc15065f6e" @@ -5044,6 +6523,11 @@ unixify@^1.0.0: dependencies: normalize-path "^2.1.1" +unpipe@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + upper-case-first@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-2.0.2.tgz#992c3273f882abd19d1e02894cc147117f844324" @@ -5077,6 +6561,11 @@ util-deprecate@^1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + v8-compile-cache-lib@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz#0582bcb1c74f3a2ee46487ceecf372e46bce53e8" @@ -5161,6 +6650,11 @@ workerpool@6.1.0: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.1.0.tgz#a8e038b4c94569596852de7a8ea4228eefdeb37b" integrity sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg== +workerpool@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.0.tgz#827d93c9ba23ee2019c3ffaff5c27fccea289e8b" + integrity sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A== + wrap-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-3.0.1.tgz#288a04d87eda5c286e060dfe8f135ce8d007f8ba" @@ -5197,7 +6691,7 @@ ws@7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== -"ws@^5.2.0 || ^6.0.0 || ^7.0.0": +"ws@^5.2.0 || ^6.0.0 || ^7.0.0", ws@^7.4.6: version "7.5.7" resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.7.tgz#9e0ac77ee50af70d58326ecff7e85eb3fa375e67" integrity sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A== @@ -5207,6 +6701,11 @@ ws@^8.3.0: resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== +xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + y18n@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" @@ -5217,6 +6716,11 @@ y18n@^5.0.5: resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"