Refactor chain metadata (#1449)

- Merge the chain metadata from the explorer, the existing sdk metadata, and the sdk's chain connection configs, all into one
- DRY up configs in infra + sdk
- Alphabetize chain names and configs
- Fix circular dep in types
- Add convenience utils for retrieving chain metadata
pull/1473/head
J M Rossy 2 years ago committed by GitHub
parent ff8ac2eabd
commit 343225a7c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 14
      typescript/infra/config/environments/testnet3/agent.ts
  2. 1
      typescript/infra/config/environments/testnet3/chains.ts
  3. 2
      typescript/infra/src/core/deploy.ts
  4. 1
      typescript/sdk/package.json
  5. 210
      typescript/sdk/src/consts/chainConnectionConfigs.ts
  6. 643
      typescript/sdk/src/consts/chainMetadata.ts
  7. 49
      typescript/sdk/src/consts/chains.ts
  8. 46
      typescript/sdk/src/consts/metamask.ts
  9. 36
      typescript/sdk/src/events.ts
  10. 2
      typescript/sdk/src/gas/calculator.ts
  11. 18
      typescript/sdk/src/index.ts
  12. 9
      typescript/sdk/src/providers/ChainConnection.ts
  13. 8
      typescript/sdk/src/types.ts
  14. 8
      yarn.lock

@ -1,3 +1,5 @@
import { chainMetadata } from '@hyperlane-xyz/sdk';
import { ALL_KEY_ROLES } from '../../../src/agents/roles';
import { AgentConfig } from '../../../src/config';
import {
@ -44,22 +46,22 @@ export const hyperlane: AgentConfig<TestnetChains> = {
},
chainOverrides: {
alfajores: {
reorgPeriod: 0,
reorgPeriod: chainMetadata.alfajores.blocks.reorgPeriod,
},
fuji: {
reorgPeriod: 3,
reorgPeriod: chainMetadata.fuji.blocks.reorgPeriod,
},
mumbai: {
reorgPeriod: 32,
reorgPeriod: chainMetadata.mumbai.blocks.reorgPeriod,
},
bsctestnet: {
reorgPeriod: 9,
reorgPeriod: chainMetadata.bsctestnet.blocks.reorgPeriod,
},
goerli: {
reorgPeriod: 3,
reorgPeriod: chainMetadata.goerli.blocks.reorgPeriod,
},
moonbasealpha: {
reorgPeriod: 0,
reorgPeriod: chainMetadata.moonbasealpha.blocks.reorgPeriod,
},
},
},

@ -5,7 +5,6 @@ export const testnetConfigs = {
fuji: chainConnectionConfigs.fuji,
mumbai: {
...chainConnectionConfigs.mumbai,
confirmations: 3,
overrides: {
maxFeePerGas: 70 * 10 ** 9, // 70 gwei
maxPriorityFeePerGas: 40 * 10 ** 9, // 40 gwei

@ -102,7 +102,7 @@ export class HyperlaneCoreInfraDeployer<
multisigIsm: contracts.multisigIsm.address,
},
rpcStyle: 'ethereum',
finalityBlocks: metadata.finalityBlocks.toString(),
finalityBlocks: metadata.blocks.reorgPeriod.toString(),
connection: {
type: ConnectionType.Http,
url: '',

@ -17,6 +17,7 @@
"@nomiclabs/hardhat-waffle": "^2.0.2",
"@types/coingecko-api": "^1.0.10",
"@types/node": "^16.9.1",
"@wagmi/chains": "^0.1.3",
"chai": "^4.3.6",
"dotenv": "^10.0.0",
"ethereum-waffle": "^3.4.4",

@ -3,206 +3,40 @@ import { ethers } from 'ethers';
import { StaticCeloJsonRpcProvider } from '@hyperlane-xyz/celo-ethers-provider';
import { ChainMap, ChainName, IChainConnection } from '../types';
import { objMap } from '../utils/objects';
export const ethereum: IChainConnection = {
provider: new ethers.providers.JsonRpcProvider(
'https://cloudflare-eth.com',
1,
),
confirmations: 7,
blockExplorerUrl: 'https://etherscan.io',
};
export const celo: IChainConnection = {
provider: new StaticCeloJsonRpcProvider('https://forno.celo.org', 42220),
confirmations: 1,
blockExplorerUrl: 'https://celoscan.io',
};
export const polygon: IChainConnection = {
provider: new ethers.providers.JsonRpcProvider(
'https://rpc-mainnet.matic.quiknode.pro',
137,
),
confirmations: 200,
blockExplorerUrl: 'https://polygonscan.com',
};
export const avalanche: IChainConnection = {
provider: new ethers.providers.JsonRpcProvider(
'https://api.avax.network/ext/bc/C/rpc',
43114,
),
confirmations: 3,
blockExplorerUrl: 'https://snowtrace.io',
};
export const arbitrum: IChainConnection = {
provider: new ethers.providers.JsonRpcProvider(
'https://arb1.arbitrum.io/rpc',
42161,
),
confirmations: 1,
blockExplorerUrl: 'https://arbiscan.io',
};
export const optimism: IChainConnection = {
provider: new ethers.providers.JsonRpcProvider(
'https://mainnet.optimism.io',
10,
),
confirmations: 1,
blockExplorerUrl: 'https://optimistic.etherscan.io',
apiPrefix: 'api-',
};
export const bsc: IChainConnection = {
provider: new ethers.providers.JsonRpcProvider(
'https://rpc.ankr.com/bsc',
56,
),
confirmations: 1,
blockExplorerUrl: 'https://bscscan.com',
};
export const alfajores: IChainConnection = {
provider: new StaticCeloJsonRpcProvider(
'https://alfajores-forno.celo-testnet.org',
44787,
),
confirmations: 1,
blockExplorerUrl: 'https://alfajores.celoscan.io',
apiPrefix: 'api-',
};
export const fuji: IChainConnection = {
provider: new ethers.providers.JsonRpcProvider(
'https://api.avax-test.network/ext/bc/C/rpc',
43113,
),
confirmations: 3,
blockExplorerUrl: 'https://testnet.snowtrace.io',
apiPrefix: 'api-',
};
export const goerli: IChainConnection = {
provider: new ethers.providers.JsonRpcProvider(
'https://rpc.ankr.com/eth_goerli',
5,
),
confirmations: 1,
blockExplorerUrl: 'https://goerli.etherscan.io/',
apiPrefix: 'api-',
};
import { chainMetadata } from './chainMetadata';
import { Chains, TestChains } from './chains';
export const optimismgoerli: IChainConnection = {
provider: new ethers.providers.JsonRpcProvider(
'https://goerli.optimism.io',
420,
),
confirmations: 1,
blockExplorerUrl: 'https://goerli-optimism.etherscan.io/',
apiPrefix: 'api-',
};
export const arbitrumgoerli: IChainConnection = {
provider: new ethers.providers.JsonRpcProvider(
'https://goerli-rollup.arbitrum.io/rpc ',
421613,
),
confirmations: 1,
blockExplorerUrl: 'https://goerli.arbiscan.io',
apiPrefix: 'api-',
};
export const mumbai: IChainConnection = {
provider: new ethers.providers.JsonRpcProvider(
'https://rpc-mumbai.maticvigil.com',
80001,
),
confirmations: 30,
blockExplorerUrl: 'https://mumbai.polygonscan.com',
apiPrefix: 'api-',
};
export const bsctestnet: IChainConnection = {
provider: new ethers.providers.JsonRpcProvider(
'https://data-seed-prebsc-1-s3.binance.org:8545',
97,
),
confirmations: 1,
blockExplorerUrl: 'https://testnet.bscscan.com',
apiPrefix: 'api-',
};
export const moonbasealpha: IChainConnection = {
provider: new ethers.providers.JsonRpcProvider(
'https://rpc.api.moonbase.moonbeam.network',
1287,
),
confirmations: 1,
blockExplorerUrl: 'https://moonbase.moonscan.io',
apiPrefix: 'api-',
};
export const moonbeam: IChainConnection = {
provider: new ethers.providers.JsonRpcProvider(
'https://rpc.api.moonbeam.network',
1284,
),
confirmations: 1,
blockExplorerUrl: 'https://moonscan.io',
apiPrefix: 'api-moonbeam.',
};
export const test1: IChainConnection = {
function testChainConnection() {
return {
provider: new ethers.providers.JsonRpcProvider(
'http://localhost:8545',
31337,
),
confirmations: 1,
};
}
export const test2: IChainConnection = {
provider: new ethers.providers.JsonRpcProvider(
'http://localhost:8545',
31337,
),
confirmations: 1,
};
export const chainConnectionConfigs: ChainMap<ChainName, IChainConnection> =
objMap(chainMetadata, (chainName, metadata) => {
if (TestChains.includes(chainName)) return testChainConnection();
export const test3: IChainConnection = {
provider: new ethers.providers.JsonRpcProvider(
'http://localhost:8545',
31337,
),
confirmations: 1,
};
const providerClass =
chainName === Chains.alfajores || chainName === Chains.celo
? StaticCeloJsonRpcProvider
: ethers.providers.JsonRpcProvider;
export const chainConnectionConfigs: ChainMap<ChainName, IChainConnection> = {
arbitrum,
bsc,
ethereum,
celo,
polygon,
avalanche,
alfajores,
fuji,
goerli,
mumbai,
bsctestnet,
optimism,
moonbasealpha,
moonbeam,
optimismgoerli,
arbitrumgoerli,
test1,
test2,
test3,
return {
provider: new providerClass(metadata.publicRpcUrls[0].http, metadata.id),
confirmations: metadata.blocks.confirmations,
blockExplorerUrl: metadata.blockExplorers[0].url,
blockExplorerApiUrl: metadata.blockExplorers[0].apiUrl,
};
});
export const testChainConnectionConfigs = {
test1,
test2,
test3,
test1: testChainConnection(),
test2: testChainConnection(),
test3: testChainConnection(),
};

@ -1,187 +1,628 @@
import { ChainName } from '../types';
import type { Chain as WagmiChain } from '@wagmi/chains';
import { objMap } from '../utils/objects';
import { ChainName, Chains, Mainnets, Testnets } from './chains';
export enum ExplorerFamily {
Etherscan = 'etherscan',
Blockscout = 'blockscout',
Other = 'other',
}
/**
* A Chain and its characteristics
* Collection of useful properties and settings
* for Hyperlane-supported chains
*/
export type ChainMetadata = {
export interface ChainMetadata {
id: number;
finalityBlocks: number;
nativeTokenDecimals?: number;
paginate?: RpcPagination;
// The CoinGecko API expects, in some cases, IDs that do not match
// ChainNames.
name: ChainName;
/** Human-readable name */
displayName: string;
/** Shorter human-readable name */
displayNameShort?: string;
/** Default currency/token used by chain */
nativeToken: {
name: string;
symbol: string;
decimals: number;
};
/** Collection of RPC endpoints */
publicRpcUrls: Array<{
http: string;
webSocket?: string;
pagination?: RpcPagination;
}>;
/** Collection of block explorers */
blockExplorers: Array<{
name: string;
url: string;
family: ExplorerFamily;
apiUrl?: string;
}>;
blocks: {
// Number of blocks to wait before considering a transaction confirmed
confirmations: number;
// TODO consider merging with confirmations, require agent code changes
// Number of blocks before a transaction has a near-zero chance of reverting
reorgPeriod: number;
// Rough estimate of time per block in seconds
estimateBlockTime: number;
};
// The CoinGecko API sometimes expects IDs that do not match ChainNames
gasCurrencyCoinGeckoId?: string;
// URL of the gnosis safe transaction service.
gnosisSafeTransactionServiceUrl?: string;
};
}
/**
* RPC Pagination information
*/
export interface RpcPagination {
blocks: number;
from: number;
}
// IDs can be generated in many ways-- for example, in JS:
// > Array.from('celo').map((c, i) => c.charCodeAt(0).toString(16).padStart(2, '0')).join('')
// '63656c6f'
/**
* Mainnets
* Common native currencies
*/
export const celo: ChainMetadata = {
id: 42220,
finalityBlocks: 0,
gnosisSafeTransactionServiceUrl:
'https://transaction-service.gnosis-safe-staging.celo-networks-dev.org',
const avaxToken = {
decimals: 18,
name: 'Avalanche',
symbol: 'AVAX',
};
const bnbToken = {
decimals: 18,
name: 'BNB',
symbol: 'BNB',
};
const celoToken = {
decimals: 18,
name: 'CELO',
symbol: 'CELO',
};
const etherToken = { name: 'Ether', symbol: 'ETH', decimals: 18 };
const maticToken = { name: 'MATIC', symbol: 'MATIC', decimals: 18 };
export const ethereum: ChainMetadata = {
id: 1,
finalityBlocks: 20,
gnosisSafeTransactionServiceUrl: 'https://safe-transaction.gnosis.io',
/**
* Chain metadata
*/
export const alfajores: ChainMetadata = {
id: 44787,
name: Chains.alfajores,
displayName: 'Alfajores',
nativeToken: celoToken,
publicRpcUrls: [{ http: 'https://alfajores-forno.celo-testnet.org' }],
blockExplorers: [
{
name: 'CeloScan',
url: 'https://alfajores.celoscan.io',
family: ExplorerFamily.Etherscan,
},
{
name: 'Blockscout',
url: 'https://explorer.celo.org/alfajores',
family: ExplorerFamily.Blockscout,
},
],
blocks: {
confirmations: 1,
reorgPeriod: 0,
estimateBlockTime: 5,
},
};
export const arbitrum: ChainMetadata = {
id: 42161,
finalityBlocks: 0,
name: Chains.arbitrum,
displayName: 'Arbitrum',
nativeToken: etherToken,
publicRpcUrls: [{ http: 'https://arb1.arbitrum.io/rpc' }],
blockExplorers: [
{
name: 'Arbiscan',
url: 'https://arbiscan.io',
apiUrl: 'https://api.arbiscan.io',
family: ExplorerFamily.Etherscan,
},
],
blocks: {
confirmations: 1,
reorgPeriod: 0,
estimateBlockTime: 3,
},
gasCurrencyCoinGeckoId: 'ethereum', // ETH is used for gas
gnosisSafeTransactionServiceUrl:
'https://safe-transaction.arbitrum.gnosis.io/',
};
export const optimism: ChainMetadata = {
id: 10,
finalityBlocks: 0,
gasCurrencyCoinGeckoId: 'ethereum', // ETH is used for gas
gnosisSafeTransactionServiceUrl:
'https://safe-transaction.optimism.gnosis.io/',
};
export const bsc: ChainMetadata = {
id: 56,
finalityBlocks: 15,
gasCurrencyCoinGeckoId: 'binancecoin',
gnosisSafeTransactionServiceUrl: 'https://safe-transaction.bsc.gnosis.io/',
export const arbitrumgoerli: ChainMetadata = {
id: 421613,
name: Chains.arbitrumgoerli,
displayName: 'Arbitrum Goerli',
displayNameShort: 'Arb. Goerli',
nativeToken: etherToken,
publicRpcUrls: [{ http: 'https://goerli-rollup.arbitrum.io/rpc' }],
blockExplorers: [
{
name: 'Arbiscan',
url: 'https://goerli.arbiscan.io/',
apiUrl: 'https://api-goerli.arbiscan.io',
family: ExplorerFamily.Etherscan,
},
],
blocks: {
confirmations: 1,
reorgPeriod: 1,
estimateBlockTime: 3,
},
};
export const avalanche: ChainMetadata = {
id: 43114,
finalityBlocks: 3,
paginate: {
// Needs to be low to avoid RPC timeouts
name: Chains.avalanche,
displayName: 'Avalanche',
nativeToken: avaxToken,
publicRpcUrls: [
{
http: 'https://api.avax.network/ext/bc/C/rpc',
pagination: {
blocks: 100000,
from: 6765067,
},
},
],
blockExplorers: [
{
name: 'SnowTrace',
url: 'https://snowtrace.io',
apiUrl: 'https://api.snowtrace.io',
family: ExplorerFamily.Other,
},
],
blocks: {
confirmations: 3,
reorgPeriod: 3,
estimateBlockTime: 2,
},
gasCurrencyCoinGeckoId: 'avalanche-2',
gnosisSafeTransactionServiceUrl:
'https://safe-transaction.avalanche.gnosis.io/',
};
export const polygon: ChainMetadata = {
id: 137,
finalityBlocks: 256,
paginate: {
// Needs to be low to avoid RPC timeouts
blocks: 10000,
from: 19657100,
export const bsc: ChainMetadata = {
id: 56,
name: Chains.bsc,
displayName: 'Binance Smart Chain',
displayNameShort: 'Binance',
nativeToken: bnbToken,
publicRpcUrls: [
{ http: 'https://bsc-dataseed.binance.org' },
{ http: 'https://rpc.ankr.com/bsc' },
],
blockExplorers: [
{
name: 'BscScan',
url: 'https://bscscan.com',
apiUrl: 'https://api.bscscan.com',
family: ExplorerFamily.Etherscan,
},
],
blocks: {
confirmations: 1,
reorgPeriod: 15,
estimateBlockTime: 3,
},
gasCurrencyCoinGeckoId: 'binancecoin',
gnosisSafeTransactionServiceUrl: 'https://safe-transaction.bsc.gnosis.io/',
};
export const bsctestnet: ChainMetadata = {
id: 97,
name: Chains.bsctestnet,
displayName: 'BSC Testnet',
nativeToken: bnbToken,
publicRpcUrls: [{ http: 'https://data-seed-prebsc-1-s3.binance.org:8545' }],
blockExplorers: [
{
name: 'BscScan',
url: 'https://testnet.bscscan.com',
apiUrl: 'https://api-testnet.bscscan.com',
family: ExplorerFamily.Etherscan,
},
],
blocks: {
confirmations: 1,
reorgPeriod: 9,
estimateBlockTime: 3,
},
};
export const celo: ChainMetadata = {
id: 42220,
name: Chains.celo,
displayName: 'Celo',
nativeToken: celoToken,
publicRpcUrls: [{ http: 'https://forno.celo.org' }],
blockExplorers: [
{
name: 'CeloScan',
url: 'https://celoscan.io',
apiUrl: 'https://api.celoscan.io',
family: ExplorerFamily.Etherscan,
},
{
name: 'Blockscout',
url: 'https://explorer.celo.org',
family: ExplorerFamily.Blockscout,
},
],
blocks: {
confirmations: 1,
reorgPeriod: 0,
estimateBlockTime: 5,
},
gasCurrencyCoinGeckoId: 'matic-network',
gnosisSafeTransactionServiceUrl:
'https://safe-transaction.polygon.gnosis.io/',
'https://transaction-service.gnosis-safe-staging.celo-networks-dev.org',
};
/**
* Testnets
*/
export const alfajores: ChainMetadata = {
id: 44787,
finalityBlocks: 0,
export const ethereum: ChainMetadata = {
id: 1,
name: Chains.ethereum,
displayName: 'Ethereum',
nativeToken: etherToken,
publicRpcUrls: [{ http: 'https://cloudflare-eth.com' }],
blockExplorers: [
{
name: 'Etherscan',
url: 'https://etherscan.io',
apiUrl: 'https://api.etherscan.io',
family: ExplorerFamily.Etherscan,
},
{
name: 'Blockscout',
url: 'https://blockscout.com/eth/mainnet',
family: ExplorerFamily.Blockscout,
},
],
blocks: {
confirmations: 7,
reorgPeriod: 20,
estimateBlockTime: 13,
},
gnosisSafeTransactionServiceUrl: 'https://safe-transaction.gnosis.io',
};
export const fuji: ChainMetadata = {
id: 43113,
finalityBlocks: 3,
name: Chains.fuji,
displayName: 'Fuji',
nativeToken: avaxToken,
publicRpcUrls: [{ http: 'https://api.avax-test.network/ext/bc/C/rpc' }],
blockExplorers: [
{
name: 'SnowTrace',
url: 'https://testnet.snowtrace.io',
apiUrl: 'https://api-testnet.snowtrace.io',
family: ExplorerFamily.Other,
},
],
blocks: {
confirmations: 3,
reorgPeriod: 3,
estimateBlockTime: 2,
},
};
export const goerli: ChainMetadata = {
id: 5,
finalityBlocks: 2,
};
export const optimismgoerli: ChainMetadata = {
id: 420,
finalityBlocks: 1,
name: Chains.goerli,
displayName: 'Goerli',
nativeToken: etherToken,
publicRpcUrls: [{ http: 'https://rpc.ankr.com/eth_goerli' }],
blockExplorers: [
{
name: 'Etherscan',
url: 'https://goerli.etherscan.io',
apiUrl: 'https://api-goerli.etherscan.io',
family: ExplorerFamily.Etherscan,
},
],
blocks: {
confirmations: 1,
reorgPeriod: 2,
estimateBlockTime: 13,
},
};
export const arbitrumgoerli: ChainMetadata = {
id: 421613,
finalityBlocks: 1,
export const moonbasealpha: ChainMetadata = {
id: 1287,
name: Chains.moonbasealpha,
displayName: 'Moonbase Alpha',
displayNameShort: 'Moonbase',
nativeToken: {
decimals: 18,
name: 'DEV',
symbol: 'DEV',
},
publicRpcUrls: [{ http: 'https://rpc.api.moonbase.moonbeam.network' }],
blockExplorers: [
{
name: 'MoonScan',
url: 'https://moonbase.moonscan.io',
apiUrl: 'https://api-moonbase.moonscan.io',
family: ExplorerFamily.Etherscan,
},
],
blocks: {
confirmations: 1,
reorgPeriod: 1,
estimateBlockTime: 12,
},
};
export const zksync2testnet: ChainMetadata = {
id: 280,
finalityBlocks: 1,
export const moonbeam: ChainMetadata = {
id: 1284,
name: Chains.moonbeam,
displayName: 'Moonbeam',
nativeToken: {
decimals: 18,
name: 'GLMR',
symbol: 'GLMR',
},
publicRpcUrls: [{ http: 'https://rpc.api.moonbeam.network' }],
blockExplorers: [
{
name: 'MoonScan',
url: 'https://moonscan.io',
apiUrl: 'https://api-moonbeam.moonscan.io',
family: ExplorerFamily.Etherscan,
},
],
blocks: {
confirmations: 1,
reorgPeriod: 1,
estimateBlockTime: 12,
},
};
export const mumbai: ChainMetadata = {
id: 80001,
finalityBlocks: 32,
paginate: {
name: Chains.mumbai,
displayName: 'Mumbai',
nativeToken: maticToken,
publicRpcUrls: [
{
http: 'https://rpc-mumbai.maticvigil.com',
pagination: {
// eth_getLogs and eth_newFilter are limited to a 10,000 blocks range
blocks: 10000,
from: 22900000,
},
},
{
http: 'https://matic-mumbai.chainstacklabs.com',
},
],
blockExplorers: [
{
name: 'PolygonScan',
url: 'https://mumbai.polygonscan.com',
apiUrl: 'https://api-testnet.polygonscan.com',
family: ExplorerFamily.Etherscan,
},
],
blocks: {
confirmations: 3,
reorgPeriod: 32,
estimateBlockTime: 5,
},
};
const testChains = {
test1: {
id: 13371,
finalityBlocks: 0,
export const optimism: ChainMetadata = {
id: 10,
name: Chains.optimism,
displayName: 'Optimism',
nativeToken: etherToken,
publicRpcUrls: [{ http: 'https://mainnet.optimism.io' }],
blockExplorers: [
{
name: 'Etherscan',
url: 'https://optimistic.etherscan.io',
apiUrl: 'https://api-optimistic.etherscan.io',
family: ExplorerFamily.Etherscan,
},
test2: {
id: 13372,
finalityBlocks: 1,
],
blocks: {
confirmations: 1,
reorgPeriod: 0,
estimateBlockTime: 3,
},
test3: {
id: 13373,
finalityBlocks: 2,
gasCurrencyCoinGeckoId: 'ethereum', // ETH is used for gas
gnosisSafeTransactionServiceUrl:
'https://safe-transaction.optimism.gnosis.io/',
};
export const optimismgoerli: ChainMetadata = {
id: 420,
name: Chains.optimismgoerli,
displayName: 'Optimism Goerli',
displayNameShort: 'Opt. Goerli',
nativeToken: etherToken,
publicRpcUrls: [{ http: 'https://goerli.optimism.io' }],
blockExplorers: [
{
name: 'Etherscan',
url: 'https://goerli-optimism.etherscan.io',
apiUrl: 'https://api-goerli-optimism.etherscan.io',
family: ExplorerFamily.Etherscan,
},
],
blocks: {
confirmations: 1,
reorgPeriod: 1,
estimateBlockTime: 3,
},
};
export const bsctestnet: ChainMetadata = {
id: 97,
finalityBlocks: 9,
export const polygon: ChainMetadata = {
id: 137,
name: Chains.polygon,
displayName: 'Polygon',
nativeToken: etherToken,
publicRpcUrls: [
{
http: 'https://rpc-mainnet.matic.quiknode.pro',
pagination: {
// Needs to be low to avoid RPC timeouts
blocks: 10000,
from: 19657100,
},
},
{ http: 'https://polygon-rpc.com' },
],
blockExplorers: [
{
name: 'PolygonScan',
url: 'https://polygonscan.com',
apiUrl: 'https://api.polygonscan.com',
family: ExplorerFamily.Etherscan,
},
],
blocks: {
confirmations: 200,
reorgPeriod: 256,
estimateBlockTime: 2,
},
gasCurrencyCoinGeckoId: 'matic-network',
gnosisSafeTransactionServiceUrl:
'https://safe-transaction.polygon.gnosis.io/',
};
export const moonbasealpha: ChainMetadata = {
id: 1287,
finalityBlocks: 1,
export const test1: ChainMetadata = {
id: 13371,
name: Chains.test1,
displayName: 'Test 1',
nativeToken: etherToken,
publicRpcUrls: [{ http: 'http://localhost:8545' }],
blockExplorers: [],
blocks: {
confirmations: 1,
reorgPeriod: 0,
estimateBlockTime: 3,
},
};
export const moonbeam: ChainMetadata = {
id: 1284,
finalityBlocks: 1,
export const test2: ChainMetadata = {
id: 13372,
name: Chains.test2,
displayName: 'Test 2',
nativeToken: etherToken,
publicRpcUrls: [{ http: 'http://localhost:8545' }],
blockExplorers: [],
blocks: {
confirmations: 1,
reorgPeriod: 1,
estimateBlockTime: 3,
},
};
export const test3: ChainMetadata = {
id: 13373,
name: Chains.test3,
displayName: 'Test 3',
nativeToken: etherToken,
publicRpcUrls: [{ http: 'http://localhost:8545' }],
blockExplorers: [],
blocks: {
confirmations: 1,
reorgPeriod: 2,
estimateBlockTime: 3,
},
};
/**
* Collection maps
*/
export const chainMetadata = {
alfajores,
arbitrum,
arbitrumgoerli,
avalanche,
bsc,
bsctestnet,
celo,
ethereum,
avalanche,
optimism,
polygon,
alfajores,
fuji,
goerli,
mumbai,
bsctestnet,
moonbasealpha,
moonbeam,
mumbai,
optimism,
optimismgoerli,
arbitrumgoerli,
zksync2testnet,
...testChains,
polygon,
test1,
test2,
test3,
} as Record<ChainName, ChainMetadata>;
// For convenient use in wagmi-based apps
export const wagmiChainMetadata: Record<ChainName, WagmiChain> = objMap(
chainMetadata,
(_, metadata) => ({
id: metadata.id,
name: metadata.displayName,
network: metadata.name as string,
nativeCurrency: metadata.nativeToken,
rpcUrls: { default: { http: [metadata.publicRpcUrls[0].http] } },
blockExplorers: metadata.blockExplorers.length
? {
default: {
name: metadata.blockExplorers[0].name,
url: metadata.blockExplorers[0].url,
},
}
: undefined,
testnet: Testnets.includes(metadata.name),
}),
);
export const chainIdToMetadata = Object.values(chainMetadata).reduce<
Record<number, ChainMetadata>
>((result, chain) => {
result[chain.id] = chain;
return result;
}, {});
export const mainnetChainsMetadata: Array<ChainMetadata> = Mainnets.map(
(chainName) => chainMetadata[chainName],
);
export const testnetChainsMetadata: Array<ChainMetadata> = Testnets.map(
(chainName) => chainMetadata[chainName],
);
/**
* @deprecated use ChainMetadata
*/
export type PartialChainMetadata = {
id: number;
finalityBlocks: number;
nativeTokenDecimals?: number;
paginate?: RpcPagination;
// The CoinGecko API expects, in some cases, IDs that do not match
// ChainNames.
gasCurrencyCoinGeckoId?: string;
// URL of the gnosis safe transaction service.
gnosisSafeTransactionServiceUrl?: string;
};
/**
* @deprecated use chainMetadata
*/
export const partialChainMetadata: Record<ChainName, PartialChainMetadata> =
objMap(chainMetadata, (_, metadata) => ({
id: metadata.id,
finalityBlocks: metadata.blocks.confirmations,
nativeTokenDecimals: metadata.nativeToken.decimals,
paginate: metadata.publicRpcUrls[0]?.pagination,
gasCurrencyCoinGeckoId: metadata.gasCurrencyCoinGeckoId,
gnosisSafeTransactionServiceUrl: metadata.gnosisSafeTransactionServiceUrl,
}));

@ -1,38 +1,40 @@
import { ChainName } from '../types';
/**
* Enumeration of Hyperlane supported chains
* Must be string type to be used with Object.keys
*/
export enum Chains { // must be string type to be used with Object.keys
arbitrum = 'arbitrum',
export enum Chains {
alfajores = 'alfajores',
arbitrum = 'arbitrum',
arbitrumgoerli = 'arbitrumgoerli',
avalanche = 'avalanche',
bsc = 'bsc',
mumbai = 'mumbai',
goerli = 'goerli',
fuji = 'fuji',
bsctestnet = 'bsctestnet',
celo = 'celo',
ethereum = 'ethereum',
avalanche = 'avalanche',
optimism = 'optimism',
polygon = 'polygon',
bsctestnet = 'bsctestnet',
fuji = 'fuji',
goerli = 'goerli',
moonbasealpha = 'moonbasealpha',
moonbeam = 'moonbeam',
mumbai = 'mumbai',
optimism = 'optimism',
optimismgoerli = 'optimismgoerli',
arbitrumgoerli = 'arbitrumgoerli',
polygon = 'polygon',
test1 = 'test1',
test2 = 'test2',
test3 = 'test3',
}
export type ChainName = keyof typeof Chains;
export enum DeprecatedChains {
rinkeby = 'rinkeby',
optimismrinkeby = 'optimismrinkeby',
arbitrumkovan = 'arbitrumkovan',
arbitrumrinkeby = 'arbitrumrinkeby',
kovan = 'kovan',
rinkeby = 'rinkeby',
optimismkovan = 'optimismkovan',
arbitrumkovan = 'arbitrumkovan',
optimismrinkeby = 'optimismrinkeby',
}
export const AllDeprecatedChains = Object.keys(DeprecatedChains) as string[];
export const Mainnets = [
@ -46,4 +48,21 @@ export const Mainnets = [
Chains.moonbeam,
] as Array<ChainName>;
export const Testnets = [
Chains.alfajores,
Chains.arbitrumgoerli,
Chains.bsctestnet,
Chains.fuji,
Chains.goerli,
Chains.moonbasealpha,
Chains.mumbai,
Chains.optimismgoerli,
] as Array<ChainName>;
export const TestChains = [
Chains.test1,
Chains.test2,
Chains.test3,
] as Array<ChainName>;
export const AllChains = Object.keys(Chains) as Array<ChainName>;

@ -1,46 +0,0 @@
export type MetamaskNetwork = {
chainId: string;
chainName: string;
nativeCurrency: { name: string; symbol: string; decimals: number };
rpcUrls: string[];
blockExplorerUrls: string[];
iconUrls: string[];
};
export const CELO_PARAMS: MetamaskNetwork = {
chainId: '0xa4ec',
chainName: 'Celo',
nativeCurrency: { name: 'Celo', symbol: 'CELO', decimals: 18 },
rpcUrls: ['https://forno.celo.org'],
blockExplorerUrls: ['https://explorer.celo.org/'],
iconUrls: ['future'],
};
export const ALFAJORES_PARAMS: MetamaskNetwork = {
chainId: '0xaef3',
chainName: 'Alfajores Testnet',
nativeCurrency: { name: 'Alfajores Celo', symbol: 'A-CELO', decimals: 18 },
rpcUrls: ['https://alfajores-forno.celo-testnet.org'],
blockExplorerUrls: ['https://alfajores-blockscout.celo-testnet.org/'],
iconUrls: ['future'],
};
export const BAKLAVA_PARAMS: MetamaskNetwork = {
chainId: '0xf370',
chainName: 'Baklava Testnet',
nativeCurrency: { name: 'Baklava Celo', symbol: 'B-CELO', decimals: 18 },
rpcUrls: ['https://baklava-forno.celo-testnet.org'],
blockExplorerUrls: ['https://baklava-blockscout.celo-testnet.org/'],
iconUrls: ['future'],
};
export async function connect(params: MetamaskNetwork): Promise<void> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const w = window as any;
if (w.ethereum) {
await w.ethereum.request({
method: 'wallet_addEthereumChain',
params: [params],
});
}
}

@ -123,8 +123,8 @@ export async function getEvents<T extends TypedEvent>(
startBlock?: number,
endBlock?: number,
): Promise<Array<T>> {
const domain = chainMetadata[chain];
if (domain.paginate) {
const mustPaginate = !!chainMetadata[chain].publicRpcUrls[0].pagination;
if (mustPaginate) {
return getPaginatedEvents(
multiprovider,
chain,
@ -144,8 +144,8 @@ export async function findEvent<T extends TypedEvent>(
filter: TypedEventFilter<T>,
startBlock?: number,
): Promise<Array<T>> {
const domain = chainMetadata[chain];
if (domain.paginate) {
const mustPaginate = !!chainMetadata[chain].publicRpcUrls[0].pagination;
if (mustPaginate) {
return findFromPaginatedEvents(
multiprovider,
chain,
@ -165,15 +165,15 @@ async function getPaginatedEvents<T extends TypedEvent>(
startBlock?: number,
endBlock?: number,
): Promise<Array<T>> {
const domain = chainMetadata[chain];
if (!domain.paginate) {
const pagination = chainMetadata[chain].publicRpcUrls[0].pagination;
if (!pagination) {
throw new Error('Domain need not be paginated');
}
// get the first block by params
// or domain deployment block
const firstBlock = startBlock
? Math.max(startBlock, domain.paginate.from)
: domain.paginate.from;
? Math.max(startBlock, pagination.from)
: pagination.from;
// get the last block by params
// or current block number
let lastBlock;
@ -185,12 +185,8 @@ async function getPaginatedEvents<T extends TypedEvent>(
}
// query domain pagination limit at a time, concurrently
const eventArrayPromises = [];
for (
let from = firstBlock;
from <= lastBlock;
from += domain.paginate.blocks
) {
const nextFrom = from + domain.paginate.blocks;
for (let from = firstBlock; from <= lastBlock; from += pagination.blocks) {
const nextFrom = from + pagination.blocks;
const to = Math.min(nextFrom, lastBlock);
const eventArrayPromise = contract.queryFilter(filter, from, to);
eventArrayPromises.push(eventArrayPromise);
@ -212,15 +208,15 @@ async function findFromPaginatedEvents<T extends TypedEvent>(
startBlock?: number,
endBlock?: number,
): Promise<Array<T>> {
const domain = chainMetadata[chain];
if (!domain.paginate) {
const pagination = chainMetadata[chain].publicRpcUrls[0].pagination;
if (!pagination) {
throw new Error('Domain need not be paginated');
}
// get the first block by params
// or domain deployment block
const firstBlock = startBlock
? Math.max(startBlock, domain.paginate.from)
: domain.paginate.from;
? Math.max(startBlock, pagination.from)
: pagination.from;
// get the last block by params
// or current block number
let lastBlock;
@ -232,8 +228,8 @@ async function findFromPaginatedEvents<T extends TypedEvent>(
}
// query domain pagination limit at a time, concurrently
// eslint-disable-next-line for-direction
for (let end = lastBlock; end > firstBlock; end -= domain.paginate.blocks) {
const nextEnd = end - domain.paginate.blocks;
for (let end = lastBlock; end > firstBlock; end -= pagination.blocks) {
const nextEnd = end - pagination.blocks;
const from = Math.max(nextEnd, firstBlock);
const queriedEvents = await contract.queryFilter(filter, from, end);
if (queriedEvents.length > 0) {

@ -252,7 +252,7 @@ export class InterchainGasCalculator<Chain extends ChainName> {
* @returns The number of decimals of `chain`'s native token.
*/
protected tokenDecimals(chain: Chain): number {
return chainMetadata[chain].nativeTokenDecimals ?? DEFAULT_TOKEN_DECIMALS;
return chainMetadata[chain].nativeToken.decimals ?? DEFAULT_TOKEN_DECIMALS;
}
/**

@ -1,10 +1,23 @@
export {
AllChains,
Chains,
ChainName,
DeprecatedChains,
AllChains,
Mainnets,
AllDeprecatedChains,
} from './consts/chains';
export { chainMetadata } from './consts/chainMetadata';
export {
ChainMetadata,
RpcPagination,
ExplorerFamily,
chainMetadata,
chainIdToMetadata,
mainnetChainsMetadata,
testnetChainsMetadata,
wagmiChainMetadata,
PartialChainMetadata,
partialChainMetadata,
} from './consts/chainMetadata';
export {
chainConnectionConfigs,
testChainConnectionConfigs,
@ -16,7 +29,6 @@ export {
export {
ChainMap,
ChainName,
CompleteChainMap,
Connection,
NameOrDomain,

@ -9,7 +9,7 @@ export class ChainConnection {
overrides: ethers.Overrides;
confirmations: number;
blockExplorerUrl: string;
apiPrefix: string;
blockExplorerApiUrl: string;
logger: Debugger;
constructor(dc: IChainConnection) {
@ -18,7 +18,7 @@ export class ChainConnection {
this.overrides = dc.overrides ?? {};
this.confirmations = dc.confirmations ?? 0;
this.blockExplorerUrl = dc.blockExplorerUrl ?? 'UNKNOWN_EXPLORER';
this.apiPrefix = dc.apiPrefix ?? 'api.';
this.blockExplorerApiUrl = dc.blockExplorerApiUrl ?? this.blockExplorerUrl;
this.logger = debug('hyperlane:ChainConnection');
}
@ -38,10 +38,7 @@ export class ChainConnection {
}
getApiUrl(): string {
const prefix = 'https://';
return `${prefix}${this.apiPrefix}${this.blockExplorerUrl.slice(
prefix.length,
)}/api`;
return this.blockExplorerApiUrl;
}
async handleTx(

@ -1,9 +1,9 @@
import type { ethers } from 'ethers';
import type { Chains } from './consts/chains';
import type { ChainName } from './consts/chains';
// A union type of the keys in the Chains enum
export type ChainName = keyof typeof Chains;
// Re-export ChainName for convenience
export { ChainName };
// A full object map of all chains to a value type
export type CompleteChainMap<Value> = Record<ChainName, Value>;
export type LooseChainMap<Value> = Record<ChainName | string, Value>;
@ -35,5 +35,5 @@ export interface IChainConnection {
overrides?: ethers.Overrides;
confirmations?: number;
blockExplorerUrl?: string;
apiPrefix?: string;
blockExplorerApiUrl?: string;
}

@ -3846,6 +3846,7 @@ __metadata:
"@types/coingecko-api": ^1.0.10
"@types/debug": ^4.1.7
"@types/node": ^16.9.1
"@wagmi/chains": ^0.1.3
chai: ^4.3.6
coingecko-api: ^1.0.10
cross-fetch: ^3.1.5
@ -4945,6 +4946,13 @@ __metadata:
languageName: node
linkType: hard
"@wagmi/chains@npm:^0.1.3":
version: 0.1.3
resolution: "@wagmi/chains@npm:0.1.3"
checksum: 341ad5bca7b1ebc425395974dc03f3e32d0aca14f44c75950ced8cd0baed30561b9c53c9f1f7cbc60e4d949f38b257d086b772f15c98b334e4a28b4ef02690ee
languageName: node
linkType: hard
"@yarnpkg/lockfile@npm:^1.1.0":
version: 1.1.0
resolution: "@yarnpkg/lockfile@npm:1.1.0"

Loading…
Cancel
Save