diff --git a/libs/metrics/src/l1/abis/index.ts b/libs/metrics/src/l1/abis/index.ts index a8c48ff..9700b77 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 "./stateTransitionManager.abi"; diff --git a/libs/metrics/src/l1/abis/stateTransitionManager.abi.ts b/libs/metrics/src/l1/abis/stateTransitionManager.abi.ts new file mode 100644 index 0000000..b01bfcb --- /dev/null +++ b/libs/metrics/src/l1/abis/stateTransitionManager.abi.ts @@ -0,0 +1,1329 @@ +export const stateTransitionManagerAbi = [ + { + inputs: [ + { + internalType: "address", + name: "_bridgehub", + type: "address", + }, + { + internalType: "uint256", + name: "_maxNumberOfHyperchains", + type: "uint256", + }, + ], + stateMutability: "nonpayable", + type: "constructor", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "uint8", + name: "version", + type: "uint8", + }, + ], + name: "Initialized", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "oldAdmin", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "newAdmin", + type: "address", + }, + ], + name: "NewAdmin", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "genesisUpgrade", + type: "address", + }, + { + indexed: false, + internalType: "bytes32", + name: "genesisBatchHash", + type: "bytes32", + }, + { + indexed: false, + internalType: "uint64", + name: "genesisIndexRepeatedStorageChanges", + type: "uint64", + }, + { + indexed: false, + internalType: "bytes32", + name: "genesisBatchCommitment", + type: "bytes32", + }, + { + indexed: false, + internalType: "bytes32", + name: "newInitialCutHash", + type: "bytes32", + }, + ], + name: "NewChainCreationParams", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "uint256", + name: "_chainId", + type: "uint256", + }, + { + indexed: true, + internalType: "address", + name: "_hyperchainContract", + type: "address", + }, + ], + name: "NewHyperchain", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "oldPendingAdmin", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "newPendingAdmin", + type: "address", + }, + ], + name: "NewPendingAdmin", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "uint256", + name: "oldProtocolVersion", + type: "uint256", + }, + { + indexed: true, + internalType: "uint256", + name: "newProtocolVersion", + type: "uint256", + }, + ], + name: "NewProtocolVersion", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "uint256", + name: "protocolVersion", + type: "uint256", + }, + { + indexed: true, + internalType: "bytes32", + name: "upgradeCutHash", + type: "bytes32", + }, + ], + name: "NewUpgradeCutHash", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "oldValidatorTimelock", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "newValidatorTimelock", + type: "address", + }, + ], + name: "NewValidatorTimelock", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "previousOwner", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "newOwner", + type: "address", + }, + ], + name: "OwnershipTransferStarted", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "previousOwner", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "newOwner", + type: "address", + }, + ], + name: "OwnershipTransferred", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "_hyperchain", + type: "address", + }, + { + components: [ + { + internalType: "uint256", + name: "txType", + type: "uint256", + }, + { + internalType: "uint256", + name: "from", + type: "uint256", + }, + { + internalType: "uint256", + name: "to", + type: "uint256", + }, + { + internalType: "uint256", + name: "gasLimit", + type: "uint256", + }, + { + internalType: "uint256", + name: "gasPerPubdataByteLimit", + type: "uint256", + }, + { + internalType: "uint256", + name: "maxFeePerGas", + type: "uint256", + }, + { + internalType: "uint256", + name: "maxPriorityFeePerGas", + type: "uint256", + }, + { + internalType: "uint256", + name: "paymaster", + type: "uint256", + }, + { + internalType: "uint256", + name: "nonce", + type: "uint256", + }, + { + internalType: "uint256", + name: "value", + type: "uint256", + }, + { + internalType: "uint256[4]", + name: "reserved", + type: "uint256[4]", + }, + { + internalType: "bytes", + name: "data", + type: "bytes", + }, + { + internalType: "bytes", + name: "signature", + type: "bytes", + }, + { + internalType: "uint256[]", + name: "factoryDeps", + type: "uint256[]", + }, + { + internalType: "bytes", + name: "paymasterInput", + type: "bytes", + }, + { + internalType: "bytes", + name: "reservedDynamic", + type: "bytes", + }, + ], + indexed: false, + internalType: "struct L2CanonicalTransaction", + name: "_l2Transaction", + type: "tuple", + }, + { + indexed: true, + internalType: "uint256", + name: "_protocolVersion", + type: "uint256", + }, + ], + name: "SetChainIdUpgrade", + type: "event", + }, + { + inputs: [], + name: "BRIDGE_HUB", + outputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "MAX_NUMBER_OF_HYPERCHAINS", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "acceptAdmin", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "acceptOwnership", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "admin", + outputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_chainId", + type: "uint256", + }, + { + components: [ + { + internalType: "enum PubdataPricingMode", + name: "pubdataPricingMode", + type: "uint8", + }, + { + internalType: "uint32", + name: "batchOverheadL1Gas", + type: "uint32", + }, + { + internalType: "uint32", + name: "maxPubdataPerBatch", + type: "uint32", + }, + { + internalType: "uint32", + name: "maxL2GasPerBatch", + type: "uint32", + }, + { + internalType: "uint32", + name: "priorityTxMaxPubdata", + type: "uint32", + }, + { + internalType: "uint64", + name: "minimalL2GasPrice", + type: "uint64", + }, + ], + internalType: "struct FeeParams", + name: "_newFeeParams", + type: "tuple", + }, + ], + name: "changeFeeParams", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_chainId", + type: "uint256", + }, + { + internalType: "address", + name: "_baseToken", + type: "address", + }, + { + internalType: "address", + name: "_sharedBridge", + type: "address", + }, + { + internalType: "address", + name: "_admin", + type: "address", + }, + { + internalType: "bytes", + name: "_diamondCut", + type: "bytes", + }, + ], + name: "createNewChain", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_chainId", + type: "uint256", + }, + { + components: [ + { + components: [ + { + internalType: "address", + name: "facet", + type: "address", + }, + { + internalType: "enum Diamond.Action", + name: "action", + type: "uint8", + }, + { + internalType: "bool", + name: "isFreezable", + type: "bool", + }, + { + internalType: "bytes4[]", + name: "selectors", + type: "bytes4[]", + }, + ], + internalType: "struct Diamond.FacetCut[]", + name: "facetCuts", + type: "tuple[]", + }, + { + internalType: "address", + name: "initAddress", + type: "address", + }, + { + internalType: "bytes", + name: "initCalldata", + type: "bytes", + }, + ], + internalType: "struct Diamond.DiamondCutData", + name: "_diamondCut", + type: "tuple", + }, + ], + name: "executeUpgrade", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_chainId", + type: "uint256", + }, + ], + name: "freezeChain", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "genesisUpgrade", + outputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "getAllHyperchainChainIDs", + outputs: [ + { + internalType: "uint256[]", + name: "", + type: "uint256[]", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "getAllHyperchains", + outputs: [ + { + internalType: "address[]", + name: "chainAddresses", + type: "address[]", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_chainId", + type: "uint256", + }, + ], + name: "getChainAdmin", + outputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_chainId", + type: "uint256", + }, + ], + name: "getHyperchain", + outputs: [ + { + internalType: "address", + name: "chainAddress", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "getSemverProtocolVersion", + outputs: [ + { + internalType: "uint32", + name: "", + type: "uint32", + }, + { + internalType: "uint32", + name: "", + type: "uint32", + }, + { + internalType: "uint32", + name: "", + type: "uint32", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "initialCutHash", + outputs: [ + { + internalType: "bytes32", + name: "", + type: "bytes32", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + components: [ + { + internalType: "address", + name: "owner", + type: "address", + }, + { + internalType: "address", + name: "validatorTimelock", + type: "address", + }, + { + components: [ + { + internalType: "address", + name: "genesisUpgrade", + type: "address", + }, + { + internalType: "bytes32", + name: "genesisBatchHash", + type: "bytes32", + }, + { + internalType: "uint64", + name: "genesisIndexRepeatedStorageChanges", + type: "uint64", + }, + { + internalType: "bytes32", + name: "genesisBatchCommitment", + type: "bytes32", + }, + { + components: [ + { + components: [ + { + internalType: "address", + name: "facet", + type: "address", + }, + { + internalType: "enum Diamond.Action", + name: "action", + type: "uint8", + }, + { + internalType: "bool", + name: "isFreezable", + type: "bool", + }, + { + internalType: "bytes4[]", + name: "selectors", + type: "bytes4[]", + }, + ], + internalType: "struct Diamond.FacetCut[]", + name: "facetCuts", + type: "tuple[]", + }, + { + internalType: "address", + name: "initAddress", + type: "address", + }, + { + internalType: "bytes", + name: "initCalldata", + type: "bytes", + }, + ], + internalType: "struct Diamond.DiamondCutData", + name: "diamondCut", + type: "tuple", + }, + ], + internalType: "struct ChainCreationParams", + name: "chainCreationParams", + type: "tuple", + }, + { + internalType: "uint256", + name: "protocolVersion", + type: "uint256", + }, + ], + internalType: "struct StateTransitionManagerInitializeData", + name: "_initializeData", + type: "tuple", + }, + ], + name: "initialize", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "owner", + outputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "pendingOwner", + outputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "protocolVersion", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_protocolVersion", + type: "uint256", + }, + ], + name: "protocolVersionDeadline", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_protocolVersion", + type: "uint256", + }, + ], + name: "protocolVersionIsActive", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_chainId", + type: "uint256", + }, + { + internalType: "address", + name: "_hyperchain", + type: "address", + }, + ], + name: "registerAlreadyDeployedHyperchain", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "renounceOwnership", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_chainId", + type: "uint256", + }, + { + internalType: "uint256", + name: "_newLastBatch", + type: "uint256", + }, + ], + name: "revertBatches", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + components: [ + { + internalType: "address", + name: "genesisUpgrade", + type: "address", + }, + { + internalType: "bytes32", + name: "genesisBatchHash", + type: "bytes32", + }, + { + internalType: "uint64", + name: "genesisIndexRepeatedStorageChanges", + type: "uint64", + }, + { + internalType: "bytes32", + name: "genesisBatchCommitment", + type: "bytes32", + }, + { + components: [ + { + components: [ + { + internalType: "address", + name: "facet", + type: "address", + }, + { + internalType: "enum Diamond.Action", + name: "action", + type: "uint8", + }, + { + internalType: "bool", + name: "isFreezable", + type: "bool", + }, + { + internalType: "bytes4[]", + name: "selectors", + type: "bytes4[]", + }, + ], + internalType: "struct Diamond.FacetCut[]", + name: "facetCuts", + type: "tuple[]", + }, + { + internalType: "address", + name: "initAddress", + type: "address", + }, + { + internalType: "bytes", + name: "initCalldata", + type: "bytes", + }, + ], + internalType: "struct Diamond.DiamondCutData", + name: "diamondCut", + type: "tuple", + }, + ], + internalType: "struct ChainCreationParams", + name: "_chainCreationParams", + type: "tuple", + }, + ], + name: "setChainCreationParams", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + components: [ + { + components: [ + { + internalType: "address", + name: "facet", + type: "address", + }, + { + internalType: "enum Diamond.Action", + name: "action", + type: "uint8", + }, + { + internalType: "bool", + name: "isFreezable", + type: "bool", + }, + { + internalType: "bytes4[]", + name: "selectors", + type: "bytes4[]", + }, + ], + internalType: "struct Diamond.FacetCut[]", + name: "facetCuts", + type: "tuple[]", + }, + { + internalType: "address", + name: "initAddress", + type: "address", + }, + { + internalType: "bytes", + name: "initCalldata", + type: "bytes", + }, + ], + internalType: "struct Diamond.DiamondCutData", + name: "_cutData", + type: "tuple", + }, + { + internalType: "uint256", + name: "_oldProtocolVersion", + type: "uint256", + }, + { + internalType: "uint256", + name: "_oldProtocolVersionDeadline", + type: "uint256", + }, + { + internalType: "uint256", + name: "_newProtocolVersion", + type: "uint256", + }, + ], + name: "setNewVersionUpgrade", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "_newPendingAdmin", + type: "address", + }, + ], + name: "setPendingAdmin", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_chainId", + type: "uint256", + }, + { + internalType: "bool", + name: "_zkPorterIsAvailable", + type: "bool", + }, + ], + name: "setPorterAvailability", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_chainId", + type: "uint256", + }, + { + internalType: "uint256", + name: "_maxGasLimit", + type: "uint256", + }, + ], + name: "setPriorityTxMaxGasLimit", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_protocolVersion", + type: "uint256", + }, + { + internalType: "uint256", + name: "_timestamp", + type: "uint256", + }, + ], + name: "setProtocolVersionDeadline", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_chainId", + type: "uint256", + }, + { + internalType: "uint128", + name: "_nominator", + type: "uint128", + }, + { + internalType: "uint128", + name: "_denominator", + type: "uint128", + }, + ], + name: "setTokenMultiplier", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + components: [ + { + components: [ + { + internalType: "address", + name: "facet", + type: "address", + }, + { + internalType: "enum Diamond.Action", + name: "action", + type: "uint8", + }, + { + internalType: "bool", + name: "isFreezable", + type: "bool", + }, + { + internalType: "bytes4[]", + name: "selectors", + type: "bytes4[]", + }, + ], + internalType: "struct Diamond.FacetCut[]", + name: "facetCuts", + type: "tuple[]", + }, + { + internalType: "address", + name: "initAddress", + type: "address", + }, + { + internalType: "bytes", + name: "initCalldata", + type: "bytes", + }, + ], + internalType: "struct Diamond.DiamondCutData", + name: "_cutData", + type: "tuple", + }, + { + internalType: "uint256", + name: "_oldProtocolVersion", + type: "uint256", + }, + ], + name: "setUpgradeDiamondCut", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_chainId", + type: "uint256", + }, + { + internalType: "address", + name: "_validator", + type: "address", + }, + { + internalType: "bool", + name: "_active", + type: "bool", + }, + ], + name: "setValidator", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "_validatorTimelock", + type: "address", + }, + ], + name: "setValidatorTimelock", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "storedBatchZero", + outputs: [ + { + internalType: "bytes32", + name: "", + type: "bytes32", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "newOwner", + type: "address", + }, + ], + name: "transferOwnership", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_chainId", + type: "uint256", + }, + ], + name: "unfreezeChain", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_chainId", + type: "uint256", + }, + { + internalType: "uint256", + name: "_oldProtocolVersion", + type: "uint256", + }, + { + components: [ + { + components: [ + { + internalType: "address", + name: "facet", + type: "address", + }, + { + internalType: "enum Diamond.Action", + name: "action", + type: "uint8", + }, + { + internalType: "bool", + name: "isFreezable", + type: "bool", + }, + { + internalType: "bytes4[]", + name: "selectors", + type: "bytes4[]", + }, + ], + internalType: "struct Diamond.FacetCut[]", + name: "facetCuts", + type: "tuple[]", + }, + { + internalType: "address", + name: "initAddress", + type: "address", + }, + { + internalType: "bytes", + name: "initCalldata", + type: "bytes", + }, + ], + internalType: "struct Diamond.DiamondCutData", + name: "_diamondCut", + type: "tuple", + }, + ], + name: "upgradeChainFromVersion", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "protocolVersion", + type: "uint256", + }, + ], + name: "upgradeCutHash", + outputs: [ + { + internalType: "bytes32", + name: "cutHash", + type: "bytes32", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "validatorTimelock", + outputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, +] as const; diff --git a/libs/metrics/src/l1/l1MetricsService.ts b/libs/metrics/src/l1/l1MetricsService.ts index 67d34ca..27f261f 100644 --- a/libs/metrics/src/l1/l1MetricsService.ts +++ b/libs/metrics/src/l1/l1MetricsService.ts @@ -17,18 +17,16 @@ import { InvalidChainType, L1MetricsServiceException, } from "@zkchainhub/metrics/exceptions"; -import { bridgeHubAbi, diamondProxyAbi, sharedBridgeAbi } from "@zkchainhub/metrics/l1/abis"; +import { + bridgeHubAbi, + diamondProxyAbi, + sharedBridgeAbi, + stateTransitionManagerAbi, +} from "@zkchainhub/metrics/l1/abis"; import { AssetTvl, GasInfo } from "@zkchainhub/metrics/types"; import { IPricingService, PRICING_PROVIDER } from "@zkchainhub/pricing"; import { EvmProviderService } from "@zkchainhub/providers"; -import { - BatchesInfo, - ChainId, - Chains, - ChainType, - L1_CONTRACTS, - vitalikAddress, -} from "@zkchainhub/shared"; +import { BatchesInfo, ChainId, Chains, ChainType, vitalikAddress } from "@zkchainhub/shared"; import { ETH_TOKEN_ADDRESS } from "@zkchainhub/shared/constants"; import { erc20Tokens, @@ -45,17 +43,12 @@ const ONE_ETHER = parseEther("1"); */ @Injectable() export class L1MetricsService { - private readonly bridgeHub = { - abi: bridgeHubAbi, - address: L1_CONTRACTS.BRIDGE_HUB, - }; - private readonly sharedBridge = { - abi: sharedBridgeAbi, - address: L1_CONTRACTS.SHARED_BRIDGE, - }; private readonly diamondContracts: Map = new Map(); - + private chainIds?: ChainId[]; constructor( + private readonly bridgeHubAddress: Address, + private readonly sharedBridgeAddress: Address, + private readonly stateTransitionManagerAddresses: Address[], private readonly evmProviderService: EvmProviderService, @Inject(PRICING_PROVIDER) private readonly pricingService: IPricingService, @Inject(WINSTON_MODULE_NEST_PROVIDER) private readonly logger: LoggerService, @@ -141,13 +134,13 @@ export class L1MetricsService { address: tokenAddress, abi: erc20Abi, functionName: "balanceOf", - args: [this.sharedBridge.address], + args: [this.sharedBridgeAddress], } as const; }), ], allowFailure: false, }); - const ethBalance = await this.evmProviderService.getBalance(this.sharedBridge.address); + const ethBalance = await this.evmProviderService.getBalance(this.sharedBridgeAddress); assert(balances.length === addresses.length, "Invalid balances length"); @@ -215,15 +208,15 @@ export class L1MetricsService { contracts: [ ...addresses.map((tokenAddress) => { return { - address: this.sharedBridge.address, - abi: this.sharedBridge.abi, + address: this.sharedBridgeAddress, + abi: sharedBridgeAbi, functionName: "chainBalance", args: [chainId, tokenAddress], } as const; }), { - address: this.sharedBridge.address, - abi: this.sharedBridge.abi, + address: this.sharedBridgeAddress, + abi: sharedBridgeAbi, functionName: "chainBalance", args: [chainId, ETH_TOKEN_ADDRESS], } as const, @@ -259,8 +252,8 @@ export class L1MetricsService { if (!diamondProxyAddress) { diamondProxyAddress = await this.evmProviderService.readContract( - this.bridgeHub.address, - this.bridgeHub.abi, + this.bridgeHubAddress, + bridgeHubAbi, "getHyperchain", [chainId], ); @@ -292,7 +285,7 @@ export class L1MetricsService { data: encodeFunctionData({ abi: erc20Abi, functionName: "transfer", - args: [L1_CONTRACTS.SHARED_BRIDGE, ONE_ETHER], + args: [this.sharedBridgeAddress, ONE_ETHER], }), }), // Get the current gas price. @@ -324,6 +317,47 @@ export class L1MetricsService { } } + /** + * Get the chainIds for the ecosystem + * @returns A list of chainIds + */ + async getChainIds(): Promise { + if (!this.chainIds) { + const chainIds = await this.evmProviderService.multicall({ + contracts: this.stateTransitionManagerAddresses.map((address) => { + return { + address, + abi: stateTransitionManagerAbi, + functionName: "getAllHyperchainChainIDs", + args: [], + } as const; + }), + allowFailure: false, + }); + this.chainIds = chainIds.flat(); + } + return this.chainIds; + } + /** + * Get the base token for each chain + * @returns A map of chainId to base token address + */ + async getBaseTokens(chainIds: ChainId[]): Promise { + if (chainIds.length === 0) return []; + const baseTokens = await this.evmProviderService.multicall({ + contracts: chainIds.map((chainId) => { + return { + address: this.bridgeHubAddress, + abi: bridgeHubAbi, + functionName: "baseToken", + args: [chainId], + } as const; + }), + allowFailure: false, + }); + return baseTokens; + } + //TODO: Implement feeParams. async feeParams(_chainId: ChainId): Promise<{ batchOverheadL1Gas: number; diff --git a/libs/metrics/test/unit/l1/l1MetricsService.spec.ts b/libs/metrics/test/unit/l1/l1MetricsService.spec.ts index 17089a1..dc8778f 100644 --- a/libs/metrics/test/unit/l1/l1MetricsService.spec.ts +++ b/libs/metrics/test/unit/l1/l1MetricsService.spec.ts @@ -2,7 +2,7 @@ import { createMock } from "@golevelup/ts-jest"; import { Logger } from "@nestjs/common"; import { Test, TestingModule } from "@nestjs/testing"; import { WINSTON_MODULE_PROVIDER } from "nest-winston"; -import { encodeFunctionData, erc20Abi, parseEther, zeroAddress } from "viem"; +import { Address, encodeFunctionData, erc20Abi, parseEther, zeroAddress } from "viem"; import { InvalidChainId, @@ -15,9 +15,9 @@ import { IPricingService, PRICING_PROVIDER } from "@zkchainhub/pricing"; import { EvmProviderService } from "@zkchainhub/providers"; import { BatchesInfo, + ChainId, ChainType, ETH_TOKEN_ADDRESS, - L1_CONTRACTS, vitalikAddress, } from "@zkchainhub/shared"; import { nativeToken, WETH } from "@zkchainhub/shared/tokens/tokens"; @@ -97,44 +97,64 @@ export const mockLogger: Partial = { debug: jest.fn(), }; +const mockMetricsModule = async ( + mockedBridgeHubAddress: Address, + mockedSharedBridgeAddress: Address, + mockedSTMAddresses: Address[], +) => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + L1MetricsService, + { + provide: L1MetricsService, + useFactory: ( + mockEvmProviderService: EvmProviderService, + mockPricingService: IPricingService, + logger: Logger, + ) => { + return new L1MetricsService( + mockedBridgeHubAddress, + mockedSharedBridgeAddress, + mockedSTMAddresses, + mockEvmProviderService, + mockPricingService, + logger, + ); + }, + inject: [EvmProviderService, PRICING_PROVIDER, WINSTON_MODULE_PROVIDER], + }, + { + provide: EvmProviderService, + useValue: mockEvmProviderService, + }, + { + provide: PRICING_PROVIDER, + useValue: mockPricingService, + }, + { + provide: WINSTON_MODULE_PROVIDER, + useValue: mockLogger, + }, + ], + }).compile(); + + return module.get(L1MetricsService); +}; + describe("L1MetricsService", () => { let l1MetricsService: L1MetricsService; - + const mockedBridgeHubAddress = "0x1234567890123456789012345678901234567890"; + const mockedSharedBridgeAddress = "0x1234567890123456789012345678901234567891"; + const mockedSTMAddresses: Address[] = [ + "0x1234567890123456789012345678901234567892", + "0x1234567890123456789012345678901234567893", + ]; beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - L1MetricsService, - { - provide: L1MetricsService, - useFactory: ( - mockEvmProviderService: EvmProviderService, - mockPricingService: IPricingService, - logger: Logger, - ) => { - return new L1MetricsService( - mockEvmProviderService, - mockPricingService, - logger, - ); - }, - inject: [EvmProviderService, PRICING_PROVIDER, WINSTON_MODULE_PROVIDER], - }, - { - provide: EvmProviderService, - useValue: mockEvmProviderService, - }, - { - provide: PRICING_PROVIDER, - useValue: mockPricingService, - }, - { - provide: WINSTON_MODULE_PROVIDER, - useValue: mockLogger, - }, - ], - }).compile(); - - l1MetricsService = module.get(L1MetricsService); + l1MetricsService = await mockMetricsModule( + mockedBridgeHubAddress, + mockedSharedBridgeAddress, + mockedSTMAddresses, + ); }); afterEach(() => { @@ -143,14 +163,9 @@ describe("L1MetricsService", () => { describe("constructor", () => { it("initialize bridgeHub and sharedBridge", () => { - expect(l1MetricsService["bridgeHub"]).toEqual({ - abi: bridgeHubAbi, - address: L1_CONTRACTS.BRIDGE_HUB, - }); - expect(l1MetricsService["sharedBridge"]).toEqual({ - abi: sharedBridgeAbi, - address: L1_CONTRACTS.SHARED_BRIDGE, - }); + expect(l1MetricsService["bridgeHubAddress"]).toEqual(mockedBridgeHubAddress); + expect(l1MetricsService["sharedBridgeAddress"]).toEqual(mockedSharedBridgeAddress); + expect(l1MetricsService["stateTransitionManagerAddresses"]).toEqual(mockedSTMAddresses); }); it("initialize diamondContracts map as empty", () => { @@ -217,19 +232,19 @@ describe("L1MetricsService", () => { address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", abi: erc20Abi, functionName: "balanceOf", - args: [L1_CONTRACTS.SHARED_BRIDGE], + args: [l1MetricsService["sharedBridgeAddress"]], }, { address: "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", abi: erc20Abi, functionName: "balanceOf", - args: [L1_CONTRACTS.SHARED_BRIDGE], + args: [l1MetricsService["sharedBridgeAddress"]], }, ], allowFailure: false, }); expect(mockEvmProviderService.getBalance).toHaveBeenCalledWith( - L1_CONTRACTS.SHARED_BRIDGE, + l1MetricsService["sharedBridgeAddress"], ); expect(mockPricingService.getTokenPrices).toHaveBeenCalledWith([ "ethereum", @@ -340,10 +355,10 @@ describe("L1MetricsService", () => { mockedDiamondProxyAddress, ); expect(mockEvmProviderService.readContract).toHaveBeenCalledWith( - l1MetricsService["bridgeHub"].address, - l1MetricsService["bridgeHub"].abi, + l1MetricsService["bridgeHubAddress"], + bridgeHubAbi, "getHyperchain", - [BigInt(chainId)], + [chainId], ); expect(mockEvmProviderService.multicall).toHaveBeenCalledWith({ contracts: [ @@ -404,10 +419,10 @@ describe("L1MetricsService", () => { mockedDiamondProxyAddress, ); expect(mockEvmProviderService.readContract).toHaveBeenCalledWith( - l1MetricsService["bridgeHub"].address, - l1MetricsService["bridgeHub"].abi, + l1MetricsService["bridgeHubAddress"], + bridgeHubAbi, "getHyperchain", - [BigInt(chainId)], + [chainId], ); }); }); @@ -465,22 +480,22 @@ describe("L1MetricsService", () => { expect(mockEvmProviderService.multicall).toHaveBeenCalledWith({ contracts: [ { - address: L1_CONTRACTS.SHARED_BRIDGE, + address: l1MetricsService["sharedBridgeAddress"], abi: sharedBridgeAbi, functionName: "chainBalance", - args: [BigInt(chainId), "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"], + args: [chainId, "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"], }, { - address: L1_CONTRACTS.SHARED_BRIDGE, + address: l1MetricsService["sharedBridgeAddress"], abi: sharedBridgeAbi, functionName: "chainBalance", - args: [BigInt(chainId), "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"], + args: [chainId, "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"], }, { - address: L1_CONTRACTS.SHARED_BRIDGE, + address: l1MetricsService["sharedBridgeAddress"], abi: sharedBridgeAbi, functionName: "chainBalance", - args: [BigInt(chainId), ETH_TOKEN_ADDRESS], + args: [chainId, ETH_TOKEN_ADDRESS], }, ], allowFailure: false, @@ -595,7 +610,7 @@ describe("L1MetricsService", () => { data: encodeFunctionData({ abi: erc20Abi, functionName: "transfer", - args: [L1_CONTRACTS.SHARED_BRIDGE, ONE_ETHER], + args: [l1MetricsService["sharedBridgeAddress"], ONE_ETHER], }), }); @@ -644,7 +659,7 @@ describe("L1MetricsService", () => { data: encodeFunctionData({ abi: erc20Abi, functionName: "transfer", - args: [L1_CONTRACTS.SHARED_BRIDGE, ONE_ETHER], + args: [l1MetricsService["sharedBridgeAddress"], ONE_ETHER], }), }); @@ -706,7 +721,7 @@ describe("L1MetricsService", () => { data: encodeFunctionData({ abi: erc20Abi, functionName: "transfer", - args: [L1_CONTRACTS.SHARED_BRIDGE, ONE_ETHER], + args: [l1MetricsService["sharedBridgeAddress"], ONE_ETHER], }), }); @@ -716,6 +731,86 @@ describe("L1MetricsService", () => { }); }); + describe("getChainIds", () => { + it("returns chainIds", async () => { + true && true; + l1MetricsService = await mockMetricsModule( + mockedBridgeHubAddress, + mockedSharedBridgeAddress, + mockedSTMAddresses, + ); + const mockedMulticallReturnValue = [ + [1n, 2n, 3n], + [4n, 5n, 6n], + ]; + jest.spyOn(mockEvmProviderService, "multicall").mockResolvedValue( + mockedMulticallReturnValue, + ); + + const result = await l1MetricsService.getChainIds(); + + expect(result).toEqual(mockedMulticallReturnValue.flat()); + expect(l1MetricsService["chainIds"]).toEqual(mockedMulticallReturnValue.flat()); + }); + it("returns chainIds previously setted up", async () => { + const mockedChainIds = [1n, 2n, 3n, 4n, 5n]; + l1MetricsService = await mockMetricsModule( + mockedBridgeHubAddress, + mockedSharedBridgeAddress, + mockedSTMAddresses, + ); + l1MetricsService["chainIds"] = mockedChainIds; + + const result = await l1MetricsService.getChainIds(); + + expect(result).toEqual(mockedChainIds); + }); + it("returns empty array if chainIds are empty", async () => { + const mockedChainIds: bigint[] = []; + l1MetricsService = await mockMetricsModule( + mockedBridgeHubAddress, + mockedSharedBridgeAddress, + mockedSTMAddresses, + ); + l1MetricsService["chainIds"] = mockedChainIds; + + const result = await l1MetricsService.getChainIds(); + + expect(result).toEqual(mockedChainIds); + }); + it("throws if multicall throws", async () => { + jest.spyOn(mockEvmProviderService, "multicall").mockRejectedValue(new Error()); + await expect(l1MetricsService.getChainIds()).rejects.toThrow(Error); + }); + }); + + describe("getBaseTokens", () => { + it("returns baseTokens", async () => { + const mockedChainIds = [1n, 2n]; + const mockedMulticallReturnValue = [ + "0x1234567890123456789012345678901234567123", + "0x1234567890123456789012345678901234567345", + ]; + jest.spyOn(mockEvmProviderService, "multicall").mockResolvedValue( + mockedMulticallReturnValue, + ); + + const result = await l1MetricsService.getBaseTokens(mockedChainIds); + + expect(result).toEqual(mockedMulticallReturnValue); + }); + it("returns baseTokens", async () => { + const mockedChainIds: ChainId[] = []; + const result = await l1MetricsService.getBaseTokens(mockedChainIds); + expect(result).toEqual([]); + }); + it("throws if multicall fails", async () => { + const mockedChainIds: ChainId[] = [1n, 2n]; + jest.spyOn(mockEvmProviderService, "multicall").mockRejectedValue(new Error()); + await expect(l1MetricsService.getBaseTokens(mockedChainIds)).rejects.toThrow(Error); + }); + }); + describe("feeParams", () => { it("return feeParams", async () => { const result = await l1MetricsService.feeParams(1n); diff --git a/libs/shared/src/constants/index.ts b/libs/shared/src/constants/index.ts index 4016c2e..ffc0de8 100644 --- a/libs/shared/src/constants/index.ts +++ b/libs/shared/src/constants/index.ts @@ -1,4 +1,3 @@ -export * from "./l1"; export * from "./addresses"; export const TOKEN_CACHE_TTL_IN_SEC = 60; export const BASE_CURRENCY = "usd"; diff --git a/libs/shared/src/constants/l1.ts b/libs/shared/src/constants/l1.ts deleted file mode 100644 index b1edade..0000000 --- a/libs/shared/src/constants/l1.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const L1_CONTRACTS = { - BRIDGE_HUB: "0x303a465B659cBB0ab36eE643eA362c509EEb5213", - DIAMOND_PROXY: "0x32400084C286CF3E17e7B677ea9583e60a000324", - SHARED_BRIDGE: "0xD7f9f54194C633F36CCD5F3da84ad4a1c38cB2cB", -} as const;