From 48e295faec05879ee15482aeaaece9d8c19e479b Mon Sep 17 00:00:00 2001 From: Charles Bergeron Date: Thu, 13 Jul 2023 16:56:29 -0700 Subject: [PATCH] replace ethereum-multicall with ethers-multicall-provider for more consistent interface, less complexity, better batch handling for super large query sets, etc --- package.json | 7 +- packages/arb-liquidator/package.json | 2 +- packages/arb-liquidator/tsup.config.ts | 2 +- packages/draw-reserve/tsup.config.ts | 2 +- .../src/utils/arbLiquidatorMulticall.ts | 197 ++++++------------ packages/prize-claimer/package.json | 1 - packages/prize-claimer/tsup.config.ts | 2 +- packages/testnet-complete-draw/tsup.config.ts | 2 +- .../withdraw-claim-rewards/tsup.config.ts | 2 +- packages/yieldvault-mintrate/tsup.config.ts | 2 +- yarn.lock | 34 +-- 11 files changed, 87 insertions(+), 166 deletions(-) diff --git a/package.json b/package.json index 87ffc77..9ab94d0 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,5 @@ "prettier": "2.7.1", "typescript": "4.8.4" }, - "packageManager": "yarn@3.5.0", - "dependencies": { - "@generationsoftware/pt-v5-utils-js": "file:.yalc/@generationsoftware/pt-v5-utils-js" - } -} + "packageManager": "yarn@3.5.0" +} \ No newline at end of file diff --git a/packages/arb-liquidator/package.json b/packages/arb-liquidator/package.json index 2c2daff..6254e76 100644 --- a/packages/arb-liquidator/package.json +++ b/packages/arb-liquidator/package.json @@ -33,7 +33,7 @@ }, "dependencies": { "defender-relay-client": "1.39.0", - "ethereum-multicall": "^2.17.0", + "ethers-multicall-provider": "^3.0.5", "figlet": "^1.6.0", "tsdx": "^0.14.1" }, diff --git a/packages/arb-liquidator/tsup.config.ts b/packages/arb-liquidator/tsup.config.ts index 858754b..a953846 100644 --- a/packages/arb-liquidator/tsup.config.ts +++ b/packages/arb-liquidator/tsup.config.ts @@ -26,7 +26,7 @@ export default defineConfig((opt) => { 'inquirer', '@generationsoftware/pt-v5-autotasks-library', '@generationsoftware/pt-v5-utils-js', - 'ethereum-multicall', + 'ethers-multicall-provider', 'configstore', ], format: 'cjs', diff --git a/packages/draw-reserve/tsup.config.ts b/packages/draw-reserve/tsup.config.ts index 3a4afb3..a167827 100644 --- a/packages/draw-reserve/tsup.config.ts +++ b/packages/draw-reserve/tsup.config.ts @@ -26,7 +26,7 @@ export default defineConfig((opt) => { 'inquirer', '@generationsoftware/pt-v5-autotasks-library', '@generationsoftware/pt-v5-utils-js', - 'ethereum-multicall', + 'ethers-multicall-provider', 'configstore', ], format: 'cjs', diff --git a/packages/library/src/utils/arbLiquidatorMulticall.ts b/packages/library/src/utils/arbLiquidatorMulticall.ts index e41cc06..0ff3477 100644 --- a/packages/library/src/utils/arbLiquidatorMulticall.ts +++ b/packages/library/src/utils/arbLiquidatorMulticall.ts @@ -1,12 +1,19 @@ import { Contract, BigNumber } from 'ethers'; import { Provider } from '@ethersproject/providers'; -import { ContractCallContext } from 'ethereum-multicall'; -import { ContractsBlob, getComplexMulticallResults } from '@generationsoftware/pt-v5-utils-js'; +import { + ContractsBlob, + getEthersMulticallProviderResults, +} from '@generationsoftware/pt-v5-utils-js'; import { ArbLiquidatorContext, Token, TokenWithRate } from '../types'; import { parseBigNumberAsFloat, MARKET_RATE_CONTRACT_DECIMALS } from '../utils'; import { ERC20Abi } from '../abis/ERC20Abi'; +import { ethers } from 'ethers'; + +import ethersMulticallProviderPkg from 'ethers-multicall-provider'; +const { MulticallWrapper } = ethersMulticallProviderPkg; + /** * Gather information about this specific liquidation pair * `tokenIn` is the token to supply (likely the prize token, which is probably POOL), @@ -27,183 +34,115 @@ export const arbLiquidatorMulticall = async ( readProvider: Provider, relayerAddress: string, ): Promise => { - const tokenInCalls: ContractCallContext['calls'] = []; + // @ts-ignore Provider == BaseProvider + const multicallProvider = MulticallWrapper.wrap(readProvider); + + let queries: Record = {}; // 1. IN TOKEN const tokenInAddress = await liquidationPair.tokenIn(); + const tokenInContract = new ethers.Contract(tokenInAddress, ERC20Abi, multicallProvider); - tokenInCalls.push({ - reference: `decimals`, - methodName: 'decimals', - methodParameters: [], - }); - tokenInCalls.push({ - reference: `name`, - methodName: 'name', - methodParameters: [], - }); - tokenInCalls.push({ - reference: `symbol`, - methodName: 'symbol', - methodParameters: [], - }); - - const tokenOutCalls: ContractCallContext['calls'] = []; + queries[`tokenIn-decimals`] = tokenInContract.decimals(); + queries[`tokenIn-name`] = tokenInContract.name(); + queries[`tokenIn-symbol`] = tokenInContract.symbol(); // 2. OUT TOKEN const tokenOutAddress = await liquidationPair.tokenOut(); + const tokenOutContract = new ethers.Contract(tokenOutAddress, ERC20Abi, multicallProvider); - tokenOutCalls.push({ - reference: `decimals`, - methodName: 'decimals', - methodParameters: [], - }); - tokenOutCalls.push({ - reference: `name`, - methodName: 'name', - methodParameters: [], - }); - tokenOutCalls.push({ - reference: `symbol`, - methodName: 'symbol', - methodParameters: [], - }); + queries[`tokenOut-decimals`] = tokenOutContract.decimals(); + queries[`tokenOut-name`] = tokenOutContract.name(); + queries[`tokenOut-symbol`] = tokenOutContract.symbol(); // // 3. VAULT UNDERLYING ASSET TOKEN - const vaultUnderlyingAssetCalls: ContractCallContext['calls'] = []; - const vaultContract = contracts.contracts.find( (contract) => contract.type === 'Vault' && contract.address === tokenOutAddress, ); const vaultUnderlyingAsset = vaultContract.tokens[0].extensions.underlyingAsset; const vaultUnderlyingAssetAddress = vaultUnderlyingAsset.address; - vaultUnderlyingAssetCalls.push({ - reference: `decimals`, - methodName: 'decimals', - methodParameters: [], - }); - vaultUnderlyingAssetCalls.push({ - reference: `name`, - methodName: 'name', - methodParameters: [], - }); - vaultUnderlyingAssetCalls.push({ - reference: `symbol`, - methodName: 'symbol', - methodParameters: [], - }); - - // // 4. RELAYER tokenIn BALANCE - tokenInCalls.push({ - reference: `balanceOf`, - methodName: 'balanceOf', - methodParameters: [relayerAddress], - }); - - // // 5. RELAYER tokenIn ALLOWANCE for spender LiquidationRouter - tokenInCalls.push({ - reference: `allowance`, - methodName: 'allowance', - methodParameters: [relayerAddress, liquidationRouter.address], - }); + const vaultUnderlyingAssetContract = new ethers.Contract( + vaultUnderlyingAssetAddress, + ERC20Abi, + multicallProvider, + ); + + queries[`vaultUnderlyingAsset-decimals`] = vaultUnderlyingAssetContract.decimals(); + queries[`vaultUnderlyingAsset-name`] = vaultUnderlyingAssetContract.name(); + queries[`vaultUnderlyingAsset-symbol`] = vaultUnderlyingAssetContract.symbol(); + + // 4. RELAYER tokenIn BALANCE + queries[`tokenIn-balanceOf`] = tokenInContract.balanceOf(relayerAddress); + queries[`tokenIn-allowance`] = tokenInContract.allowance( + relayerAddress, + liquidationRouter.address, + ); + + // 5. RELAYER tokenIn ALLOWANCE for spender LiquidationRouter // 6. MarketRate Calls - const marketRateCalls: ContractCallContext['calls'] = []; - // // // prize token/pool const marketRateContractBlob = contracts.contracts.find( (contract) => contract.type === 'MarketRate', ); const marketRateAddress = marketRate.address; - marketRateCalls.push({ - reference: `priceFeed-${tokenInAddress}`, - methodName: 'priceFeed', - methodParameters: [tokenInAddress, 'USD'], - }); - - // // yield token/vault underlying asset rate - marketRateCalls.push({ - reference: `priceFeed-${vaultUnderlyingAssetAddress}`, - methodName: 'priceFeed', - methodParameters: [vaultUnderlyingAssetAddress, 'USD'], - }); - - const queries: ContractCallContext[] = [ - { - reference: tokenInAddress, - contractAddress: tokenInAddress, - abi: ERC20Abi, - calls: tokenInCalls, - }, - { - reference: tokenOutAddress, - contractAddress: tokenOutAddress, - abi: ERC20Abi, - calls: tokenOutCalls, - }, - { - reference: vaultUnderlyingAssetAddress, - contractAddress: vaultUnderlyingAssetAddress, - abi: ERC20Abi, - calls: vaultUnderlyingAssetCalls, - }, - { - reference: marketRateAddress, - contractAddress: marketRateAddress, - abi: marketRateContractBlob.abi, - calls: marketRateCalls, - }, - ]; - - // Get and process results! - const multicallResults = await getComplexMulticallResults(readProvider, queries); - - const marketRateMulticallResults = multicallResults[marketRateAddress]; - const tokenInPriceFeedResults = marketRateMulticallResults[`priceFeed-${tokenInAddress}`][0]; + const marketRateContract = new ethers.Contract( + marketRateAddress, + marketRateContractBlob.abi, + multicallProvider, + ); + + queries[`priceFeed-${tokenInAddress}`] = marketRateContract.priceFeed(tokenInAddress, 'USD'); + queries[`priceFeed-${vaultUnderlyingAssetAddress}`] = marketRateContract.priceFeed( + vaultUnderlyingAssetAddress, + 'USD', + ); + + // 7. Get and process results! + const results = await getEthersMulticallProviderResults(multicallProvider, queries); + + // const marketRateMulticallResults = results[marketRateAddress]; + const tokenInPriceFeedResults = results[`priceFeed-${tokenInAddress}`]; // 1. tokenIn results - const tokenInMulticallResults = multicallResults[tokenInAddress]; const tokenInAssetRateUsd = parseBigNumberAsFloat( BigNumber.from(tokenInPriceFeedResults), MARKET_RATE_CONTRACT_DECIMALS, ); const tokenIn: TokenWithRate = { address: tokenInAddress, - decimals: tokenInMulticallResults.decimals[0], - name: tokenInMulticallResults.name[0], - symbol: tokenInMulticallResults.symbol[0], + decimals: results['tokenIn-decimals'], + name: results['tokenIn-name'], + symbol: results['tokenIn-symbol'], assetRateUsd: tokenInAssetRateUsd, }; // 2. tokenOut results (vault token) - const tokenOutMulticallResults = multicallResults[tokenOutAddress]; const tokenOut: Token = { address: tokenOutAddress, - decimals: tokenOutMulticallResults.decimals[0], - name: tokenOutMulticallResults.name[0], - symbol: tokenOutMulticallResults.symbol[0], + decimals: results['tokenOut-decimals'], + name: results['tokenOut-name'], + symbol: results['tokenOut-symbol'], }; // 3. vault underlying asset (hard asset such as DAI or USDC) results - const vaultUnderlyingAssetMulticallResults = multicallResults[vaultUnderlyingAssetAddress]; - const vaultUnderlyingAssetPriceFeedResults = - marketRateMulticallResults[`priceFeed-${vaultUnderlyingAssetAddress}`][0]; + const vaultUnderlyingAssetPriceFeedResults = results[`priceFeed-${vaultUnderlyingAssetAddress}`]; const vaultUnderlyingAssetAssetRateUsd = parseBigNumberAsFloat( BigNumber.from(vaultUnderlyingAssetPriceFeedResults), MARKET_RATE_CONTRACT_DECIMALS, ); const vaultUnderlyingAssetUnderlyingAsset: TokenWithRate = { address: vaultUnderlyingAsset.address, - decimals: vaultUnderlyingAssetMulticallResults.decimals[0], - name: vaultUnderlyingAssetMulticallResults.name[0], - symbol: vaultUnderlyingAssetMulticallResults.symbol[0], + decimals: results['vaultUnderlyingAsset-decimals'], + name: results['vaultUnderlyingAsset-name'], + symbol: results['vaultUnderlyingAsset-symbol'], assetRateUsd: vaultUnderlyingAssetAssetRateUsd, }; const relayer = { - tokenInBalance: BigNumber.from(tokenInMulticallResults.balanceOf[0]), - tokenInAllowance: BigNumber.from(tokenInMulticallResults.allowance[0]), + tokenInBalance: BigNumber.from(results['tokenIn-balanceOf']), + tokenInAllowance: BigNumber.from(results['tokenIn-allowance']), }; return { diff --git a/packages/prize-claimer/package.json b/packages/prize-claimer/package.json index 44ac704..9ab64ca 100644 --- a/packages/prize-claimer/package.json +++ b/packages/prize-claimer/package.json @@ -34,7 +34,6 @@ "dependencies": { "configstore": "^6.0.0", "defender-relay-client": "1.39.0", - "ethereum-multicall": "^2.16.1", "figlet": "^1.6.0", "graphql-request": "^5.2.0", "lodash.groupby": "^4.6.0", diff --git a/packages/prize-claimer/tsup.config.ts b/packages/prize-claimer/tsup.config.ts index 9aafc3e..4def341 100644 --- a/packages/prize-claimer/tsup.config.ts +++ b/packages/prize-claimer/tsup.config.ts @@ -26,7 +26,7 @@ export default defineConfig((opt) => { 'inquirer', '@generationsoftware/pt-v5-autotasks-library', '@generationsoftware/pt-v5-utils-js', - 'ethereum-multicall', + 'ethers-multicall-provider', 'configstore', 'lodash.groupby', ], diff --git a/packages/testnet-complete-draw/tsup.config.ts b/packages/testnet-complete-draw/tsup.config.ts index e899d0d..24d6f22 100644 --- a/packages/testnet-complete-draw/tsup.config.ts +++ b/packages/testnet-complete-draw/tsup.config.ts @@ -22,7 +22,7 @@ export default defineConfig((opt) => { noExternal: [ '@generationsoftware/pt-v5-autotasks-library', '@generationsoftware/pt-v5-utils-js', - 'ethereum-multicall', + 'ethers-multicall-provider', 'configstore', ], format: 'cjs', diff --git a/packages/withdraw-claim-rewards/tsup.config.ts b/packages/withdraw-claim-rewards/tsup.config.ts index 66656a4..8d7cc9d 100644 --- a/packages/withdraw-claim-rewards/tsup.config.ts +++ b/packages/withdraw-claim-rewards/tsup.config.ts @@ -26,7 +26,7 @@ export default defineConfig((opt) => { 'inquirer', '@generationsoftware/pt-v5-autotasks-library', '@generationsoftware/pt-v5-utils-js', - 'ethereum-multicall', + 'ethers-multicall-provider', 'configstore', ], format: 'cjs', diff --git a/packages/yieldvault-mintrate/tsup.config.ts b/packages/yieldvault-mintrate/tsup.config.ts index 5fdee34..44d4071 100644 --- a/packages/yieldvault-mintrate/tsup.config.ts +++ b/packages/yieldvault-mintrate/tsup.config.ts @@ -21,7 +21,7 @@ export default defineConfig((opt) => { noExternal: [ '@generationsoftware/pt-v5-autotasks-library', '@generationsoftware/pt-v5-utils-js', - 'ethereum-multicall', + 'ethers-multicall-provider', 'configstore', ], format: 'cjs', diff --git a/yarn.lock b/yarn.lock index fee5763..1860a80 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1447,22 +1447,6 @@ resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== -"@generationsoftware/pt-v5-utils-js@file:.yalc/@generationsoftware/pt-v5-utils-js": - version "0.0.1-beta.29" - dependencies: - "@pooltogether/contract-list-schema" "^0.1.4" - "@types/lodash" "^4.14.195" - ethereum-multicall "^2.17.0" - ethers "^5.5.1" - ethers-multicall-provider "^3.0.3" - graphql "^16.6.0" - graphql-request "^6.0.0" - lodash "^4.17.21" - node-fetch "^2.6.11" - tsc "^2.0.4" - tsc-watch "^6.0.4" - typescript "^5.0.4" - "@graphql-typed-document-node/core@^3.1.1", "@graphql-typed-document-node/core@^3.2.0": version "3.2.0" resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861" @@ -5377,14 +5361,6 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== -ethereum-multicall@^2.16.1: - version "2.16.1" - resolved "https://registry.yarnpkg.com/ethereum-multicall/-/ethereum-multicall-2.16.1.tgz#4a0e110b02b4c7a3431361822035fa8dfa3e11cd" - integrity sha512-24MgG22WGHxavIsb6E46Pa6MpkHAkP3pau6R0rQFB4m9gQIxpv5by0Gj4vx7FMjXisFbjlcxVP32hZQXYRP6Yw== - dependencies: - "@ethersproject/providers" "^5.0.10" - ethers "^5.0.15" - ethereum-multicall@^2.17.0: version "2.17.0" resolved "https://registry.yarnpkg.com/ethereum-multicall/-/ethereum-multicall-2.17.0.tgz#c56a8884f2d895a87bc90b1257653e2f131ad8a5" @@ -5403,6 +5379,16 @@ ethers-multicall-provider@^3.0.3: ethers "^5.0.0" lodash "^4.17.0" +ethers-multicall-provider@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/ethers-multicall-provider/-/ethers-multicall-provider-3.0.5.tgz#2f2c465cf4603c27b342aecfb38bc55883195f99" + integrity sha512-f7Qjl6q6jcgiGdA8B3P0sQar+PdxUxcvikx27F8wZX9O0jFdpTaJ99oKPTYVdWZ39vT13mV+4kQ+i0LiMAuuZA== + dependencies: + "@ethersproject/abi" "^5.7.0" + "@ethersproject/providers" "^5.7.2" + ethers "^5.0.0" + lodash "^4.17.0" + ethers@5.7.2, ethers@^5.0.0, ethers@^5.0.15, ethers@^5.5.1: version "5.7.2" resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e"