From 102d8708220ac32cc65cd3d7e191055e4b082717 Mon Sep 17 00:00:00 2001 From: nigiri <168690269+0xnigir1@users.noreply.github.com> Date: Fri, 9 Aug 2024 12:15:31 -0300 Subject: [PATCH] feat: use multicall getEthBalance --- libs/metrics/src/l1/abis/index.ts | 1 + libs/metrics/src/l1/abis/multicall3.abi.ts | 440 ++++++++++++++++++ libs/metrics/src/l1/l1MetricsService.ts | 20 +- .../test/unit/l1/l1MetricsService.spec.ts | 29 +- libs/shared/src/constants/addresses.ts | 1 + 5 files changed, 476 insertions(+), 15 deletions(-) create mode 100644 libs/metrics/src/l1/abis/multicall3.abi.ts diff --git a/libs/metrics/src/l1/abis/index.ts b/libs/metrics/src/l1/abis/index.ts index a8c48ff..a8ff14a 100644 --- a/libs/metrics/src/l1/abis/index.ts +++ b/libs/metrics/src/l1/abis/index.ts @@ -1,3 +1,4 @@ export * from "./bridgeHub.abi"; export * from "./diamondProxy.abi"; export * from "./sharedBridge.abi"; +export * from "./multicall3.abi"; diff --git a/libs/metrics/src/l1/abis/multicall3.abi.ts b/libs/metrics/src/l1/abis/multicall3.abi.ts new file mode 100644 index 0000000..e664a83 --- /dev/null +++ b/libs/metrics/src/l1/abis/multicall3.abi.ts @@ -0,0 +1,440 @@ +export const multicall3Abi = [ + { + inputs: [ + { + components: [ + { + internalType: "address", + name: "target", + type: "address", + }, + { + internalType: "bytes", + name: "callData", + type: "bytes", + }, + ], + internalType: "struct Multicall3.Call[]", + name: "calls", + type: "tuple[]", + }, + ], + name: "aggregate", + outputs: [ + { + internalType: "uint256", + name: "blockNumber", + type: "uint256", + }, + { + internalType: "bytes[]", + name: "returnData", + type: "bytes[]", + }, + ], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + components: [ + { + internalType: "address", + name: "target", + type: "address", + }, + { + internalType: "bool", + name: "allowFailure", + type: "bool", + }, + { + internalType: "bytes", + name: "callData", + type: "bytes", + }, + ], + internalType: "struct Multicall3.Call3[]", + name: "calls", + type: "tuple[]", + }, + ], + name: "aggregate3", + outputs: [ + { + components: [ + { + internalType: "bool", + name: "success", + type: "bool", + }, + { + internalType: "bytes", + name: "returnData", + type: "bytes", + }, + ], + internalType: "struct Multicall3.Result[]", + name: "returnData", + type: "tuple[]", + }, + ], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + components: [ + { + internalType: "address", + name: "target", + type: "address", + }, + { + internalType: "bool", + name: "allowFailure", + type: "bool", + }, + { + internalType: "uint256", + name: "value", + type: "uint256", + }, + { + internalType: "bytes", + name: "callData", + type: "bytes", + }, + ], + internalType: "struct Multicall3.Call3Value[]", + name: "calls", + type: "tuple[]", + }, + ], + name: "aggregate3Value", + outputs: [ + { + components: [ + { + internalType: "bool", + name: "success", + type: "bool", + }, + { + internalType: "bytes", + name: "returnData", + type: "bytes", + }, + ], + internalType: "struct Multicall3.Result[]", + name: "returnData", + type: "tuple[]", + }, + ], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + components: [ + { + internalType: "address", + name: "target", + type: "address", + }, + { + internalType: "bytes", + name: "callData", + type: "bytes", + }, + ], + internalType: "struct Multicall3.Call[]", + name: "calls", + type: "tuple[]", + }, + ], + name: "blockAndAggregate", + outputs: [ + { + internalType: "uint256", + name: "blockNumber", + type: "uint256", + }, + { + internalType: "bytes32", + name: "blockHash", + type: "bytes32", + }, + { + components: [ + { + internalType: "bool", + name: "success", + type: "bool", + }, + { + internalType: "bytes", + name: "returnData", + type: "bytes", + }, + ], + internalType: "struct Multicall3.Result[]", + name: "returnData", + type: "tuple[]", + }, + ], + stateMutability: "payable", + type: "function", + }, + { + inputs: [], + name: "getBasefee", + outputs: [ + { + internalType: "uint256", + name: "basefee", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "blockNumber", + type: "uint256", + }, + ], + name: "getBlockHash", + outputs: [ + { + internalType: "bytes32", + name: "blockHash", + type: "bytes32", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "getBlockNumber", + outputs: [ + { + internalType: "uint256", + name: "blockNumber", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "getChainId", + outputs: [ + { + internalType: "uint256", + name: "chainid", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "getCurrentBlockCoinbase", + outputs: [ + { + internalType: "address", + name: "coinbase", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "getCurrentBlockDifficulty", + outputs: [ + { + internalType: "uint256", + name: "difficulty", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "getCurrentBlockGasLimit", + outputs: [ + { + internalType: "uint256", + name: "gaslimit", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "getCurrentBlockTimestamp", + outputs: [ + { + internalType: "uint256", + name: "timestamp", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "addr", + type: "address", + }, + ], + name: "getEthBalance", + outputs: [ + { + internalType: "uint256", + name: "balance", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "getLastBlockHash", + outputs: [ + { + internalType: "bytes32", + name: "blockHash", + type: "bytes32", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "bool", + name: "requireSuccess", + type: "bool", + }, + { + components: [ + { + internalType: "address", + name: "target", + type: "address", + }, + { + internalType: "bytes", + name: "callData", + type: "bytes", + }, + ], + internalType: "struct Multicall3.Call[]", + name: "calls", + type: "tuple[]", + }, + ], + name: "tryAggregate", + outputs: [ + { + components: [ + { + internalType: "bool", + name: "success", + type: "bool", + }, + { + internalType: "bytes", + name: "returnData", + type: "bytes", + }, + ], + internalType: "struct Multicall3.Result[]", + name: "returnData", + type: "tuple[]", + }, + ], + stateMutability: "payable", + type: "function", + }, + { + inputs: [ + { + internalType: "bool", + name: "requireSuccess", + type: "bool", + }, + { + components: [ + { + internalType: "address", + name: "target", + type: "address", + }, + { + internalType: "bytes", + name: "callData", + type: "bytes", + }, + ], + internalType: "struct Multicall3.Call[]", + name: "calls", + type: "tuple[]", + }, + ], + name: "tryBlockAndAggregate", + outputs: [ + { + internalType: "uint256", + name: "blockNumber", + type: "uint256", + }, + { + internalType: "bytes32", + name: "blockHash", + type: "bytes32", + }, + { + components: [ + { + internalType: "bool", + name: "success", + type: "bool", + }, + { + internalType: "bytes", + name: "returnData", + type: "bytes", + }, + ], + internalType: "struct Multicall3.Result[]", + name: "returnData", + type: "tuple[]", + }, + ], + stateMutability: "payable", + type: "function", + }, +] as const; diff --git a/libs/metrics/src/l1/l1MetricsService.ts b/libs/metrics/src/l1/l1MetricsService.ts index 67d34ca..73b7123 100644 --- a/libs/metrics/src/l1/l1MetricsService.ts +++ b/libs/metrics/src/l1/l1MetricsService.ts @@ -17,7 +17,12 @@ import { InvalidChainType, L1MetricsServiceException, } from "@zkchainhub/metrics/exceptions"; -import { bridgeHubAbi, diamondProxyAbi, sharedBridgeAbi } from "@zkchainhub/metrics/l1/abis"; +import { + bridgeHubAbi, + diamondProxyAbi, + multicall3Abi, + sharedBridgeAbi, +} from "@zkchainhub/metrics/l1/abis"; import { AssetTvl, GasInfo } from "@zkchainhub/metrics/types"; import { IPricingService, PRICING_PROVIDER } from "@zkchainhub/pricing"; import { EvmProviderService } from "@zkchainhub/providers"; @@ -29,7 +34,7 @@ import { L1_CONTRACTS, vitalikAddress, } from "@zkchainhub/shared"; -import { ETH_TOKEN_ADDRESS } from "@zkchainhub/shared/constants"; +import { ETH_TOKEN_ADDRESS, multicall3EthereumAddress } from "@zkchainhub/shared/constants"; import { erc20Tokens, isNativeToken, @@ -144,14 +149,19 @@ export class L1MetricsService { args: [this.sharedBridge.address], } as const; }), + { + address: multicall3EthereumAddress, + abi: multicall3Abi, + functionName: "getEthBalance", + args: [this.sharedBridge.address], + } as const, ], allowFailure: false, }); - const ethBalance = await this.evmProviderService.getBalance(this.sharedBridge.address); - assert(balances.length === addresses.length, "Invalid balances length"); + assert(balances.length === addresses.length + 1, "Invalid balances length"); - return { ethBalance: ethBalance, addressesBalance: balances }; + return { ethBalance: balances[addresses.length]!, addressesBalance: balances }; } /** diff --git a/libs/metrics/test/unit/l1/l1MetricsService.spec.ts b/libs/metrics/test/unit/l1/l1MetricsService.spec.ts index 17089a1..4f1c442 100644 --- a/libs/metrics/test/unit/l1/l1MetricsService.spec.ts +++ b/libs/metrics/test/unit/l1/l1MetricsService.spec.ts @@ -10,7 +10,12 @@ import { L1MetricsServiceException, } from "@zkchainhub/metrics/exceptions"; import { L1MetricsService } from "@zkchainhub/metrics/l1/"; -import { bridgeHubAbi, diamondProxyAbi, sharedBridgeAbi } from "@zkchainhub/metrics/l1/abis"; +import { + bridgeHubAbi, + diamondProxyAbi, + multicall3Abi, + sharedBridgeAbi, +} from "@zkchainhub/metrics/l1/abis"; import { IPricingService, PRICING_PROVIDER } from "@zkchainhub/pricing"; import { EvmProviderService } from "@zkchainhub/providers"; import { @@ -18,6 +23,7 @@ import { ChainType, ETH_TOKEN_ADDRESS, L1_CONTRACTS, + multicall3EthereumAddress, vitalikAddress, } from "@zkchainhub/shared"; import { nativeToken, WETH } from "@zkchainhub/shared/tokens/tokens"; @@ -160,14 +166,16 @@ describe("L1MetricsService", () => { describe("l1Tvl", () => { it("return the TVL on L1 Shared Bridge", async () => { - const mockMulticallBalances = [60_841_657_140641n, 135_63005559n]; // Mocked balances - const mockEtherBalance = 123_803_824374847279970609n; + const mockMulticallBalances = [ + 60_841_657_140641n, + 135_63005559n, + 123_803_824374847279970609n, + ]; // Mocked balances const mockPrices = { "wrapped-bitcoin": 66_129, "usd-coin": 0.999, ethereum: 3_181.09 }; // Mocked prices jest.spyOn(mockEvmProviderService, "multicall").mockResolvedValue( mockMulticallBalances, ); - jest.spyOn(mockEvmProviderService, "getBalance").mockResolvedValue(mockEtherBalance); jest.spyOn(mockPricingService, "getTokenPrices").mockResolvedValue(mockPrices); const result = await l1MetricsService.l1Tvl(); @@ -225,12 +233,15 @@ describe("L1MetricsService", () => { functionName: "balanceOf", args: [L1_CONTRACTS.SHARED_BRIDGE], }, + { + address: multicall3EthereumAddress, + abi: multicall3Abi, + functionName: "getEthBalance", + args: [L1_CONTRACTS.SHARED_BRIDGE], + }, ], allowFailure: false, }); - expect(mockEvmProviderService.getBalance).toHaveBeenCalledWith( - L1_CONTRACTS.SHARED_BRIDGE, - ); expect(mockPricingService.getTokenPrices).toHaveBeenCalledWith([ "ethereum", "usd-coin", @@ -248,10 +259,8 @@ describe("L1MetricsService", () => { jest.spyOn(mockEvmProviderService, "multicall").mockResolvedValue([ 60_841_657_140641n, 135_63005559n, - ]); - jest.spyOn(mockEvmProviderService, "getBalance").mockResolvedValue( 123_803_824374847279970609n, - ); + ]); jest.spyOn(mockPricingService, "getTokenPrices").mockResolvedValue({ ethereum: 3_181.09, "usd-coin": 0.999, diff --git a/libs/shared/src/constants/addresses.ts b/libs/shared/src/constants/addresses.ts index f8f3817..414815f 100644 --- a/libs/shared/src/constants/addresses.ts +++ b/libs/shared/src/constants/addresses.ts @@ -4,3 +4,4 @@ import { Address } from "abitype"; // See: https://github.com/matter-labs/era-contracts/blob/8a70bbbc48125f5bde6189b4e3c6a3ee79631678/l1-contracts/contracts/common/Config.sol#L105 export const ETH_TOKEN_ADDRESS: Address = "0x0000000000000000000000000000000000000001"; export const vitalikAddress: Address = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"; +export const multicall3EthereumAddress: Address = "0xcA11bde05977b3631167028862bE2a173976CA11";