feat: revamp testnet4 gasoracle/igp calculations (#4562)

looking to resolve https://github.com/hyperlane-xyz/issues/issues/1374
more longer term

feat: revamp testnet4 gasoracle/igp calculations
- update print-gas-prices script to support testnets
- update print-token-prices script to support testnets (requires
https://github.com/hyperlane-xyz/hyperlane-registry/pull/228)
- remove the common/rare/mythic nomenclature, use mainnet prices
- refactor igp utils, reuse across both mainnet3 + testnet4
pull/4535/head
Paul Balaji 2 months ago committed by GitHub
parent 50319d8bad
commit ba15abef17
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      .changeset/popular-cars-burn.md
  2. 2
      .registryrc
  3. 4
      rust/main/config/mainnet_config.json
  4. 42
      rust/main/config/testnet_config.json
  5. 50
      typescript/infra/config/environments/mainnet3/igp.ts
  6. 10
      typescript/infra/config/environments/testnet4/chains.ts
  7. 100
      typescript/infra/config/environments/testnet4/gas-oracle.ts
  8. 62
      typescript/infra/config/environments/testnet4/gasPrices.json
  9. 41
      typescript/infra/config/environments/testnet4/igp.ts
  10. 17
      typescript/infra/config/environments/testnet4/tokenPrices.json
  11. 19
      typescript/infra/scripts/print-gas-prices.ts
  12. 26
      typescript/infra/scripts/print-token-prices.ts
  13. 33
      typescript/infra/src/config/gas-oracle.ts

@ -1,5 +1,5 @@
--- ---
"@hyperlane-xyz/sdk": patch '@hyperlane-xyz/sdk': patch
--- ---
fix: warn on submodule metadata builder failures fix: warn on submodule metadata builder failures

@ -1 +1 @@
v4.3.6 d7dc44d8894962648a544686cdce4536ce9569ce

@ -2758,10 +2758,10 @@
"solanamainnet": { "solanamainnet": {
"blockExplorers": [ "blockExplorers": [
{ {
"apiUrl": "https://explorer.solana.com?cluster=mainnet-beta", "apiUrl": "https://solscan.io",
"family": "other", "family": "other",
"name": "Solana Explorer", "name": "Solana Explorer",
"url": "https://explorer.solana.com?cluster=mainnet-beta" "url": "https://solscan.io"
} }
], ],
"blocks": { "blocks": {

@ -68,7 +68,8 @@
"timelockController": "0x0000000000000000000000000000000000000000", "timelockController": "0x0000000000000000000000000000000000000000",
"validatorAnnounce": "0x3726EE36a2A9e11a40d1ffD7D9A1A16e0154cDA0", "validatorAnnounce": "0x3726EE36a2A9e11a40d1ffD7D9A1A16e0154cDA0",
"staticMerkleRootWeightedMultisigIsmFactory": "0x374961678da5911083599314974B94094513F95c", "staticMerkleRootWeightedMultisigIsmFactory": "0x374961678da5911083599314974B94094513F95c",
"staticMessageIdWeightedMultisigIsmFactory": "0x1Fa22d908f5a5E7F5429D9146E5a3740D8AC10d7" "staticMessageIdWeightedMultisigIsmFactory": "0x1Fa22d908f5a5E7F5429D9146E5a3740D8AC10d7",
"gasCurrencyCoinGeckoId": "celo"
}, },
"arbitrumsepolia": { "arbitrumsepolia": {
"aggregationHook": "0xD2670EedcD21116c6F0B331Ce391eA4B3Bf1aB19", "aggregationHook": "0xD2670EedcD21116c6F0B331Ce391eA4B3Bf1aB19",
@ -132,7 +133,8 @@
"testRecipient": "0x6c13643B3927C57DB92c790E4E3E7Ee81e13f78C", "testRecipient": "0x6c13643B3927C57DB92c790E4E3E7Ee81e13f78C",
"validatorAnnounce": "0x1b33611fCc073aB0737011d5512EF673Bff74962", "validatorAnnounce": "0x1b33611fCc073aB0737011d5512EF673Bff74962",
"staticMerkleRootWeightedMultisigIsmFactory": "0x1aFD5191738d365C8079e955E4cEdDfe7e01C62d", "staticMerkleRootWeightedMultisigIsmFactory": "0x1aFD5191738d365C8079e955E4cEdDfe7e01C62d",
"staticMessageIdWeightedMultisigIsmFactory": "0xC81e6D1070aFA48DA4e4f35E744CC1aE43532a10" "staticMessageIdWeightedMultisigIsmFactory": "0xC81e6D1070aFA48DA4e4f35E744CC1aE43532a10",
"gasCurrencyCoinGeckoId": "ethereum"
}, },
"basesepolia": { "basesepolia": {
"aggregationHook": "0xccA408a6A9A6dc405C3278647421eb4317466943", "aggregationHook": "0xccA408a6A9A6dc405C3278647421eb4317466943",
@ -195,7 +197,8 @@
"testRecipient": "0x783c4a0bB6663359281aD4a637D5af68F83ae213", "testRecipient": "0x783c4a0bB6663359281aD4a637D5af68F83ae213",
"validatorAnnounce": "0x20c44b1E3BeaDA1e9826CFd48BeEDABeE9871cE9", "validatorAnnounce": "0x20c44b1E3BeaDA1e9826CFd48BeEDABeE9871cE9",
"staticMerkleRootWeightedMultisigIsmFactory": "0xB057Fb841027a8554521DcCdeC3c3474CaC99AB5", "staticMerkleRootWeightedMultisigIsmFactory": "0xB057Fb841027a8554521DcCdeC3c3474CaC99AB5",
"staticMessageIdWeightedMultisigIsmFactory": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F" "staticMessageIdWeightedMultisigIsmFactory": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F",
"gasCurrencyCoinGeckoId": "ethereum"
}, },
"bsctestnet": { "bsctestnet": {
"aggregationHook": "0x3d675bB93250Ab7603F40cbb9194bae210784627", "aggregationHook": "0x3d675bB93250Ab7603F40cbb9194bae210784627",
@ -266,7 +269,8 @@
}, },
"validatorAnnounce": "0xf09701B0a93210113D175461b6135a96773B5465", "validatorAnnounce": "0xf09701B0a93210113D175461b6135a96773B5465",
"staticMerkleRootWeightedMultisigIsmFactory": "0xCa152b249791Adf7A09C6c1bdbAb05e4A594966e", "staticMerkleRootWeightedMultisigIsmFactory": "0xCa152b249791Adf7A09C6c1bdbAb05e4A594966e",
"staticMessageIdWeightedMultisigIsmFactory": "0xaa80d23299861b7D7ab1bE665579029Ed9137BD1" "staticMessageIdWeightedMultisigIsmFactory": "0xaa80d23299861b7D7ab1bE665579029Ed9137BD1",
"gasCurrencyCoinGeckoId": "binancecoin"
}, },
"connextsepolia": { "connextsepolia": {
"aggregationHook": "0x331eb40963dc11F5BB271308c42d97ac6e41F124", "aggregationHook": "0x331eb40963dc11F5BB271308c42d97ac6e41F124",
@ -327,7 +331,8 @@
"testRecipient": "0xAb9B273366D794B7F80B4378bc8Aaca75C6178E2", "testRecipient": "0xAb9B273366D794B7F80B4378bc8Aaca75C6178E2",
"validatorAnnounce": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C", "validatorAnnounce": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C",
"staticMerkleRootWeightedMultisigIsmFactory": "0x8584590ad637C61C7cDF72eFF3381Ee1c3D1bC8E", "staticMerkleRootWeightedMultisigIsmFactory": "0x8584590ad637C61C7cDF72eFF3381Ee1c3D1bC8E",
"staticMessageIdWeightedMultisigIsmFactory": "0xcCB305B1f21e5FbC85D1DD7Be5cd8d5bf5B7f863" "staticMessageIdWeightedMultisigIsmFactory": "0xcCB305B1f21e5FbC85D1DD7Be5cd8d5bf5B7f863",
"gasCurrencyCoinGeckoId": "ethereum"
}, },
"eclipsetestnet": { "eclipsetestnet": {
"blocks": { "blocks": {
@ -362,7 +367,8 @@
"http": "https://testnet.dev2.eclipsenetwork.xyz" "http": "https://testnet.dev2.eclipsenetwork.xyz"
} }
], ],
"validatorAnnounce": "8qNYSi9EP1xSnRjtMpyof88A26GBbdcrsa61uSaHiwx3" "validatorAnnounce": "8qNYSi9EP1xSnRjtMpyof88A26GBbdcrsa61uSaHiwx3",
"gasCurrencyCoinGeckoId": "ethereum"
}, },
"ecotestnet": { "ecotestnet": {
"aggregationHook": "0xccA408a6A9A6dc405C3278647421eb4317466943", "aggregationHook": "0xccA408a6A9A6dc405C3278647421eb4317466943",
@ -422,7 +428,8 @@
"testRecipient": "0x783c4a0bB6663359281aD4a637D5af68F83ae213", "testRecipient": "0x783c4a0bB6663359281aD4a637D5af68F83ae213",
"validatorAnnounce": "0x20c44b1E3BeaDA1e9826CFd48BeEDABeE9871cE9", "validatorAnnounce": "0x20c44b1E3BeaDA1e9826CFd48BeEDABeE9871cE9",
"staticMerkleRootWeightedMultisigIsmFactory": "0xA2cf52064c921C11adCd83588CbEa08cc3bfF5d8", "staticMerkleRootWeightedMultisigIsmFactory": "0xA2cf52064c921C11adCd83588CbEa08cc3bfF5d8",
"staticMessageIdWeightedMultisigIsmFactory": "0x628BC518ED1e0E8C6cbcD574EbA0ee29e7F6943E" "staticMessageIdWeightedMultisigIsmFactory": "0x628BC518ED1e0E8C6cbcD574EbA0ee29e7F6943E",
"gasCurrencyCoinGeckoId": "ethereum"
}, },
"fuji": { "fuji": {
"aggregationHook": "0x8E9b4006171c6B75111823e7545Ee5400CEce0B3", "aggregationHook": "0x8E9b4006171c6B75111823e7545Ee5400CEce0B3",
@ -489,7 +496,8 @@
"timelockController": "0x0000000000000000000000000000000000000000", "timelockController": "0x0000000000000000000000000000000000000000",
"validatorAnnounce": "0x4f7179A691F8a684f56cF7Fed65171877d30739a", "validatorAnnounce": "0x4f7179A691F8a684f56cF7Fed65171877d30739a",
"staticMerkleRootWeightedMultisigIsmFactory": "0xff93F32997Ac5450995121385aCE96b184efe89E", "staticMerkleRootWeightedMultisigIsmFactory": "0xff93F32997Ac5450995121385aCE96b184efe89E",
"staticMessageIdWeightedMultisigIsmFactory": "0x8eAB8cBb9037e818C321f675c0bc2EA4649003CF" "staticMessageIdWeightedMultisigIsmFactory": "0x8eAB8cBb9037e818C321f675c0bc2EA4649003CF",
"gasCurrencyCoinGeckoId": "avalanche-2"
}, },
"holesky": { "holesky": {
"aggregationHook": "0xb1FfD51f03c69A0a3e5AFEBDE639752DB1d56bc9", "aggregationHook": "0xb1FfD51f03c69A0a3e5AFEBDE639752DB1d56bc9",
@ -549,7 +557,8 @@
"testRecipient": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", "testRecipient": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD",
"validatorAnnounce": "0xAb9B273366D794B7F80B4378bc8Aaca75C6178E2", "validatorAnnounce": "0xAb9B273366D794B7F80B4378bc8Aaca75C6178E2",
"staticMerkleRootWeightedMultisigIsmFactory": "0xFb55597F07417b08195Ba674f4dd58aeC9B89FBB", "staticMerkleRootWeightedMultisigIsmFactory": "0xFb55597F07417b08195Ba674f4dd58aeC9B89FBB",
"staticMessageIdWeightedMultisigIsmFactory": "0x0E18b28D98C2efDb59252c021320F203305b1B66" "staticMessageIdWeightedMultisigIsmFactory": "0x0E18b28D98C2efDb59252c021320F203305b1B66",
"gasCurrencyCoinGeckoId": "ethereum"
}, },
"optimismsepolia": { "optimismsepolia": {
"aggregationHook": "0xccA408a6A9A6dc405C3278647421eb4317466943", "aggregationHook": "0xccA408a6A9A6dc405C3278647421eb4317466943",
@ -744,7 +753,8 @@
"testRecipient": "0x04438ef7622f5412f82915F59caD4f704C61eA48", "testRecipient": "0x04438ef7622f5412f82915F59caD4f704C61eA48",
"validatorAnnounce": "0x11918DC33E067C5DA83EEF58E50F856398b8Df4C", "validatorAnnounce": "0x11918DC33E067C5DA83EEF58E50F856398b8Df4C",
"staticMerkleRootWeightedMultisigIsmFactory": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F", "staticMerkleRootWeightedMultisigIsmFactory": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F",
"staticMessageIdWeightedMultisigIsmFactory": "0x7c5B5bdA7F1d1F70A6678ABb4d894612Fc76498F" "staticMessageIdWeightedMultisigIsmFactory": "0x7c5B5bdA7F1d1F70A6678ABb4d894612Fc76498F",
"gasCurrencyCoinGeckoId": "polygon-ecosystem-token"
}, },
"scrollsepolia": { "scrollsepolia": {
"aggregationHook": "0x7b63Aa270335F8896717c2A809205F4b650E4268", "aggregationHook": "0x7b63Aa270335F8896717c2A809205F4b650E4268",
@ -823,7 +833,8 @@
}, },
"validatorAnnounce": "0x527768930D889662Fe7ACF64294871e86e4C2381", "validatorAnnounce": "0x527768930D889662Fe7ACF64294871e86e4C2381",
"staticMerkleRootWeightedMultisigIsmFactory": "0x339B46496D60b1b6B42e9715DeD8B3D2154dA0Bb", "staticMerkleRootWeightedMultisigIsmFactory": "0x339B46496D60b1b6B42e9715DeD8B3D2154dA0Bb",
"staticMessageIdWeightedMultisigIsmFactory": "0x63dFf524F1c7361f4F1bf07D658Bf7f2d5Dd5B20" "staticMessageIdWeightedMultisigIsmFactory": "0x63dFf524F1c7361f4F1bf07D658Bf7f2d5Dd5B20",
"gasCurrencyCoinGeckoId": "ethereum"
}, },
"sepolia": { "sepolia": {
"aggregationHook": "0xe3147d5618f5e2e100690B50ec923009a4cde14A", "aggregationHook": "0xe3147d5618f5e2e100690B50ec923009a4cde14A",
@ -894,7 +905,8 @@
"timelockController": "0x0000000000000000000000000000000000000000", "timelockController": "0x0000000000000000000000000000000000000000",
"validatorAnnounce": "0xE6105C59480a1B7DD3E4f28153aFdbE12F4CfCD9", "validatorAnnounce": "0xE6105C59480a1B7DD3E4f28153aFdbE12F4CfCD9",
"staticMerkleRootWeightedMultisigIsmFactory": "0x4afB48e864d308409d0D80E98fB7d5d6aA5b245f", "staticMerkleRootWeightedMultisigIsmFactory": "0x4afB48e864d308409d0D80E98fB7d5d6aA5b245f",
"staticMessageIdWeightedMultisigIsmFactory": "0x196Ce28ED1Afdf015849ddEE82F03a903Bee9E94" "staticMessageIdWeightedMultisigIsmFactory": "0x196Ce28ED1Afdf015849ddEE82F03a903Bee9E94",
"gasCurrencyCoinGeckoId": "ethereum"
}, },
"solanatestnet": { "solanatestnet": {
"blockExplorers": [ "blockExplorers": [
@ -938,7 +950,8 @@
"http": "https://api.testnet.solana.com" "http": "https://api.testnet.solana.com"
} }
], ],
"validatorAnnounce": "8qNYSi9EP1xSnRjtMpyof88A26GBbdcrsa61uSaHiwx3" "validatorAnnounce": "8qNYSi9EP1xSnRjtMpyof88A26GBbdcrsa61uSaHiwx3",
"gasCurrencyCoinGeckoId": "solana"
}, },
"superpositiontestnet": { "superpositiontestnet": {
"aggregationHook": "0x331eb40963dc11F5BB271308c42d97ac6e41F124", "aggregationHook": "0x331eb40963dc11F5BB271308c42d97ac6e41F124",
@ -995,7 +1008,8 @@
"testRecipient": "0xAb9B273366D794B7F80B4378bc8Aaca75C6178E2", "testRecipient": "0xAb9B273366D794B7F80B4378bc8Aaca75C6178E2",
"validatorAnnounce": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C", "validatorAnnounce": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C",
"staticMerkleRootWeightedMultisigIsmFactory": "0xE67CfA164cDa449Ae38a0a09391eF6bCDf8e4e2c", "staticMerkleRootWeightedMultisigIsmFactory": "0xE67CfA164cDa449Ae38a0a09391eF6bCDf8e4e2c",
"staticMessageIdWeightedMultisigIsmFactory": "0x867f2089D09903f208AeCac84E599B90E5a4A821" "staticMessageIdWeightedMultisigIsmFactory": "0x867f2089D09903f208AeCac84E599B90E5a4A821",
"gasCurrencyCoinGeckoId": "superposition"
} }
}, },
"defaultRpcConsensusType": "fallback" "defaultRpcConsensusType": "fallback"

@ -1,20 +1,11 @@
import { BigNumber, ethers } from 'ethers'; import { ChainMap, ChainName, HookType, IgpConfig } from '@hyperlane-xyz/sdk';
import {
ChainMap,
ChainName,
HookType,
IgpConfig,
TOKEN_EXCHANGE_RATE_DECIMALS,
defaultMultisigConfigs,
multisigIsmVerificationCost,
} from '@hyperlane-xyz/sdk';
import { exclude, objMap } from '@hyperlane-xyz/utils'; import { exclude, objMap } from '@hyperlane-xyz/utils';
import { import {
AllStorageGasOracleConfigs, AllStorageGasOracleConfigs,
getAllStorageGasOracleConfigs, getAllStorageGasOracleConfigs,
getTokenExchangeRateFromValues, getTokenExchangeRateFromValues,
remoteOverhead,
} from '../../../src/config/gas-oracle.js'; } from '../../../src/config/gas-oracle.js';
import { ethereumChainNames } from './chains.js'; import { ethereumChainNames } from './chains.js';
@ -25,43 +16,22 @@ import rawTokenPrices from './tokenPrices.json';
const tokenPrices: ChainMap<string> = rawTokenPrices; const tokenPrices: ChainMap<string> = rawTokenPrices;
const FOREIGN_DEFAULT_OVERHEAD = 600_000; // cosmwasm warp route somewhat arbitrarily chosen const remoteOverheadWithOverrides = (chain: ChainName) => {
let overhead = remoteOverhead(chain, ethereumChainNames);
function remoteOverhead(remote: ChainName) { if (chain === 'moonbeam') {
let overhead = ethereumChainNames.includes(remote as any)
? multisigIsmVerificationCost(
defaultMultisigConfigs[remote].threshold,
defaultMultisigConfigs[remote].validators.length,
)
: FOREIGN_DEFAULT_OVERHEAD; // non-ethereum overhead
if (remote === 'moonbeam') {
// Moonbeam has (roughly) 4x'd their gas costs, some context in https://discord.com/channels/935678348330434570/1287816893553705041
overhead *= 4; overhead *= 4;
} }
return overhead; return overhead;
} };
// Gets the exchange rate of the remote quoted in local tokens
function getTokenExchangeRate(local: ChainName, remote: ChainName): BigNumber {
const localValue = ethers.utils.parseUnits(
tokenPrices[local],
TOKEN_EXCHANGE_RATE_DECIMALS,
);
const remoteValue = ethers.utils.parseUnits(
tokenPrices[remote],
TOKEN_EXCHANGE_RATE_DECIMALS,
);
return getTokenExchangeRateFromValues(local, localValue, remote, remoteValue);
}
const storageGasOracleConfig: AllStorageGasOracleConfigs = const storageGasOracleConfig: AllStorageGasOracleConfigs =
getAllStorageGasOracleConfigs( getAllStorageGasOracleConfigs(
supportedChainNames, supportedChainNames,
gasPrices, gasPrices,
getTokenExchangeRate, (local, remote) =>
getTokenExchangeRateFromValues(local, remote, tokenPrices),
(local) => parseFloat(tokenPrices[local]), (local) => parseFloat(tokenPrices[local]),
(local) => remoteOverhead(local), remoteOverheadWithOverrides,
); );
export const igp: ChainMap<IgpConfig> = objMap( export const igp: ChainMap<IgpConfig> = objMap(
@ -79,7 +49,7 @@ export const igp: ChainMap<IgpConfig> = objMap(
overhead: Object.fromEntries( overhead: Object.fromEntries(
exclude(local, supportedChainNames).map((remote) => [ exclude(local, supportedChainNames).map((remote) => [
remote, remote,
remoteOverhead(remote), remoteOverhead(remote, ethereumChainNames),
]), ]),
), ),
oracleConfig: storageGasOracleConfig[local], oracleConfig: storageGasOracleConfig[local],

@ -1,5 +1,7 @@
import { IRegistry } from '@hyperlane-xyz/registry';
import { ChainMap, ChainMetadata } from '@hyperlane-xyz/sdk'; import { ChainMap, ChainMetadata } from '@hyperlane-xyz/sdk';
import { getRegistryForEnvironment } from '../../../src/config/chain.js';
import { isEthereumProtocolChain } from '../../../src/utils/utils.js'; import { isEthereumProtocolChain } from '../../../src/utils/utils.js';
import { supportedChainNames } from './supportedChainNames.js'; import { supportedChainNames } from './supportedChainNames.js';
@ -22,3 +24,11 @@ export const chainMetadataOverrides: ChainMap<Partial<ChainMetadata>> = {
}, },
}, },
}; };
export const getRegistry = async (useSecrets = true): Promise<IRegistry> =>
getRegistryForEnvironment(
environment,
supportedChainNames,
chainMetadataOverrides,
useSecrets,
);

@ -1,100 +0,0 @@
import { BigNumber, ethers } from 'ethers';
import { ChainName, TOKEN_EXCHANGE_RATE_DECIMALS } from '@hyperlane-xyz/sdk';
import { objMap } from '@hyperlane-xyz/utils';
import {
AllStorageGasOracleConfigs,
getAllStorageGasOracleConfigs,
getTokenExchangeRateFromValues,
} from '../../../src/config/gas-oracle.js';
import { ethereumChainNames } from './chains.js';
import { testnet4SupportedChainNames } from './supportedChainNames.js';
// Taken by looking at each testnet and overestimating gas prices
const gasPrices: Record<
(typeof testnet4SupportedChainNames)[number],
BigNumber
> = {
alfajores: ethers.utils.parseUnits('10', 'gwei'),
arbitrumsepolia: ethers.utils.parseUnits('0.5', 'gwei'),
basesepolia: ethers.utils.parseUnits('0.1', 'gwei'),
bsctestnet: ethers.utils.parseUnits('15', 'gwei'),
connextsepolia: ethers.utils.parseUnits('0.5', 'gwei'),
ecotestnet: ethers.utils.parseUnits('0.001', 'gwei'),
eclipsetestnet: ethers.BigNumber.from('28'),
fuji: ethers.utils.parseUnits('30', 'gwei'),
holesky: ethers.utils.parseUnits('10', 'gwei'),
optimismsepolia: ethers.utils.parseUnits('0.5', 'gwei'),
// Disabling plumetestnet on Sept 16, 2024: chain is paused for "airplane mode"
// plumetestnet: ethers.utils.parseUnits('0.01', 'gwei'),
polygonamoy: ethers.utils.parseUnits('100', 'gwei'),
scrollsepolia: ethers.utils.parseUnits('0.5', 'gwei'),
sepolia: ethers.utils.parseUnits('5', 'gwei'),
solanatestnet: ethers.BigNumber.from('28'),
superpositiontestnet: ethers.utils.parseUnits('0.001', 'gwei'),
};
// Used to categorize rarity of testnet tokens & approximate exchange rates.
// Unashamedly borrowed from Fortnite
enum Rarity {
Common,
Rare,
Mythic,
}
// "Value" of the testnet tokens with 10 decimals of precision.
// Imagine these as quoted in USD
const RARITY_APPROXIMATE_VALUE: Record<Rarity, BigNumber> = {
[Rarity.Common]: ethers.utils.parseUnits('0.5', TOKEN_EXCHANGE_RATE_DECIMALS),
[Rarity.Rare]: ethers.utils.parseUnits('1', TOKEN_EXCHANGE_RATE_DECIMALS),
[Rarity.Mythic]: ethers.utils.parseUnits('5', TOKEN_EXCHANGE_RATE_DECIMALS),
};
const chainTokenRarity: Record<
(typeof testnet4SupportedChainNames)[number],
Rarity
> = {
alfajores: Rarity.Common,
arbitrumsepolia: Rarity.Common,
basesepolia: Rarity.Common,
bsctestnet: Rarity.Rare,
connextsepolia: Rarity.Common,
ecotestnet: Rarity.Rare,
eclipsetestnet: Rarity.Common,
fuji: Rarity.Rare,
holesky: Rarity.Common,
optimismsepolia: Rarity.Common,
// Disabling plumetestnet on Sept 16, 2024: chain is paused for "airplane mode"
// plumetestnet: Rarity.Common,
polygonamoy: Rarity.Rare,
scrollsepolia: Rarity.Rare,
sepolia: Rarity.Mythic,
solanatestnet: Rarity.Common,
superpositiontestnet: Rarity.Common,
};
// Gets the "value" of a testnet chain
function getApproximateValue(chain: ChainName): BigNumber {
const rarity = chainTokenRarity[chain as keyof typeof chainTokenRarity];
return RARITY_APPROXIMATE_VALUE[rarity];
}
// Gets the exchange rate of the remote quoted in local tokens
function getTokenExchangeRate(local: ChainName, remote: ChainName): BigNumber {
const localValue = getApproximateValue(local);
const remoteValue = getApproximateValue(remote);
return getTokenExchangeRateFromValues(local, localValue, remote, remoteValue);
}
export const storageGasOracleConfig: AllStorageGasOracleConfigs =
getAllStorageGasOracleConfigs(
ethereumChainNames,
objMap(gasPrices, (_, gasPrice) => ({
amount: gasPrice.toString(),
decimals: 1,
})),
getTokenExchangeRate,
);

@ -0,0 +1,62 @@
{
"alfajores": {
"amount": "50.0",
"decimals": 9
},
"arbitrumsepolia": {
"amount": "0.1",
"decimals": 9
},
"basesepolia": {
"amount": "0.001000263",
"decimals": 9
},
"bsctestnet": {
"amount": "5.532492",
"decimals": 9
},
"connextsepolia": {
"amount": "0.1",
"decimals": 9
},
"ecotestnet": {
"amount": "0.001000252",
"decimals": 9
},
"eclipsetestnet": {
"amount": "0.001",
"decimals": 9
},
"fuji": {
"amount": "25.000000001",
"decimals": 9
},
"holesky": {
"amount": "0.191141087",
"decimals": 9
},
"optimismsepolia": {
"amount": "0.001000327",
"decimals": 9
},
"polygonamoy": {
"amount": "31.092000015",
"decimals": 9
},
"scrollsepolia": {
"amount": "0.059465192",
"decimals": 9
},
"sepolia": {
"amount": "7.101334009",
"decimals": 9
},
"solanatestnet": {
"amount": "0.001",
"decimals": 9
},
"superpositiontestnet": {
"amount": "0.01",
"decimals": 9
}
}

@ -1,27 +1,30 @@
import { import { ChainMap, HookType, IgpConfig } from '@hyperlane-xyz/sdk';
ChainMap,
ChainName,
HookType,
IgpConfig,
defaultMultisigConfigs,
multisigIsmVerificationCost,
} from '@hyperlane-xyz/sdk';
import { Address, exclude, objMap } from '@hyperlane-xyz/utils'; import { Address, exclude, objMap } from '@hyperlane-xyz/utils';
import { isEthereumProtocolChain } from '../../../src/utils/utils.js'; import {
AllStorageGasOracleConfigs,
getAllStorageGasOracleConfigs,
getTokenExchangeRateFromValues,
remoteOverhead,
} from '../../../src/config/gas-oracle.js';
import { storageGasOracleConfig } from './gas-oracle.js'; import { ethereumChainNames } from './chains.js';
import gasPrices from './gasPrices.json';
import { owners } from './owners.js'; import { owners } from './owners.js';
import { supportedChainNames } from './supportedChainNames.js'; import { supportedChainNames } from './supportedChainNames.js';
import rawTokenPrices from './tokenPrices.json';
const tokenPrices: ChainMap<string> = rawTokenPrices;
const FOREIGN_DEFAULT_OVERHEAD = 600_000; // cosmwasm warp route somewhat arbitrarily chosen export const storageGasOracleConfig: AllStorageGasOracleConfigs =
const remoteOverhead = (remote: ChainName) => getAllStorageGasOracleConfigs(
supportedChainNames.filter(isEthereumProtocolChain).includes(remote as any) supportedChainNames,
? multisigIsmVerificationCost( gasPrices,
defaultMultisigConfigs[remote].threshold, (local, remote) =>
defaultMultisigConfigs[remote].validators.length, getTokenExchangeRateFromValues(local, remote, tokenPrices),
) (local) => parseFloat(tokenPrices[local]),
: FOREIGN_DEFAULT_OVERHEAD; // non-ethereum overhead (local) => remoteOverhead(local, ethereumChainNames),
);
export const igp: ChainMap<IgpConfig> = objMap( export const igp: ChainMap<IgpConfig> = objMap(
owners, owners,
@ -35,7 +38,7 @@ export const igp: ChainMap<IgpConfig> = objMap(
overhead: Object.fromEntries( overhead: Object.fromEntries(
exclude(chain, supportedChainNames).map((remote) => [ exclude(chain, supportedChainNames).map((remote) => [
remote, remote,
remoteOverhead(remote), remoteOverhead(remote, ethereumChainNames),
]), ]),
), ),
}; };

@ -0,0 +1,17 @@
{
"alfajores": "0.538515",
"arbitrumsepolia": "2651.43",
"basesepolia": "2651.43",
"bsctestnet": "608.49",
"connextsepolia": "2651.43",
"ecotestnet": "2651.43",
"eclipsetestnet": "2651.43",
"fuji": "27.33",
"holesky": "2651.43",
"optimismsepolia": "2651.43",
"polygonamoy": "0.405534",
"scrollsepolia": "2651.43",
"sepolia": "2651.43",
"solanatestnet": "144.13",
"superpositiontestnet": "0.999824"
}

@ -8,19 +8,34 @@ import { ProtocolType } from '@hyperlane-xyz/utils';
// to avoid circular dependencies. // to avoid circular dependencies.
import { getRegistry as getMainnet3Registry } from '../config/environments/mainnet3/chains.js'; import { getRegistry as getMainnet3Registry } from '../config/environments/mainnet3/chains.js';
import { supportedChainNames as mainnet3SupportedChainNames } from '../config/environments/mainnet3/supportedChainNames.js'; import { supportedChainNames as mainnet3SupportedChainNames } from '../config/environments/mainnet3/supportedChainNames.js';
import { getRegistry as getTestnet4Registry } from '../config/environments/testnet4/chains.js';
import { supportedChainNames as testnet4SupportedChainNames } from '../config/environments/testnet4/supportedChainNames.js';
import { import {
GasPriceConfig, GasPriceConfig,
getCosmosChainGasPrice, getCosmosChainGasPrice,
} from '../src/config/gas-oracle.js'; } from '../src/config/gas-oracle.js';
import { getArgs } from './agent-utils.js';
async function main() { async function main() {
const registry = await getMainnet3Registry(); const { environment } = await getArgs().argv;
const { registry, supportedChainNames } =
environment === 'mainnet3'
? {
registry: await getMainnet3Registry(),
supportedChainNames: mainnet3SupportedChainNames,
}
: {
registry: await getTestnet4Registry(),
supportedChainNames: testnet4SupportedChainNames,
};
const chainMetadata = await registry.getMetadata(); const chainMetadata = await registry.getMetadata();
const mpp = new MultiProtocolProvider(chainMetadata); const mpp = new MultiProtocolProvider(chainMetadata);
const prices: ChainMap<GasPriceConfig> = Object.fromEntries( const prices: ChainMap<GasPriceConfig> = Object.fromEntries(
await Promise.all( await Promise.all(
mainnet3SupportedChainNames.map(async (chain) => [ supportedChainNames.map(async (chain) => [
chain, chain,
await getGasPrice(mpp, chain), await getGasPrice(mpp, chain),
]), ]),

@ -1,22 +1,38 @@
import { ChainMetadata } from '@hyperlane-xyz/sdk'; import { ChainMetadata } from '@hyperlane-xyz/sdk';
import { objMap, pick } from '@hyperlane-xyz/utils'; import { objMap, pick } from '@hyperlane-xyz/utils';
// Intentionally circumvent `mainnet3/index.ts` and `getEnvironmentConfig('mainnet3')` // Intentionally circumvent `{mainnet3,testnet4}/index.ts` and `getEnvironmentConfig({'mainnet3','testnet4'})`
// to avoid circular dependencies. // to avoid circular dependencies.
import { getRegistry as getMainnet3Registry } from '../config/environments/mainnet3/chains.js'; import { getRegistry as getMainnet3Registry } from '../config/environments/mainnet3/chains.js';
import { mainnet3SupportedChainNames } from '../config/environments/mainnet3/supportedChainNames.js'; import { supportedChainNames as mainnet3SupportedChainNames } from '../config/environments/mainnet3/supportedChainNames.js';
import { getRegistry as getTestnet4Registry } from '../config/environments/testnet4/chains.js';
import { supportedChainNames as testnet4SupportedChainNames } from '../config/environments/testnet4/supportedChainNames.js';
import { getArgs } from './agent-utils.js';
const CURRENCY = 'usd'; const CURRENCY = 'usd';
async function main() { async function main() {
const registry = await getMainnet3Registry(); const { environment } = await getArgs().argv;
const { registry, supportedChainNames } =
environment === 'mainnet3'
? {
registry: await getMainnet3Registry(),
supportedChainNames: mainnet3SupportedChainNames,
}
: {
registry: await getTestnet4Registry(),
supportedChainNames: testnet4SupportedChainNames,
};
const chainMetadata = await registry.getMetadata(); const chainMetadata = await registry.getMetadata();
const metadata = pick( const metadata = pick(
chainMetadata as Record< chainMetadata as Record<
(typeof mainnet3SupportedChainNames)[number], (typeof supportedChainNames)[number],
ChainMetadata ChainMetadata
>, >,
[...mainnet3SupportedChainNames], [...supportedChainNames],
); );
const ids = objMap( const ids = objMap(

@ -5,8 +5,11 @@ import {
ChainMap, ChainMap,
ChainName, ChainName,
StorageGasOracleConfig as DestinationOracleConfig, StorageGasOracleConfig as DestinationOracleConfig,
TOKEN_EXCHANGE_RATE_DECIMALS,
TOKEN_EXCHANGE_RATE_SCALE, TOKEN_EXCHANGE_RATE_SCALE,
defaultMultisigConfigs,
getCosmosRegistryChain, getCosmosRegistryChain,
multisigIsmVerificationCost,
} from '@hyperlane-xyz/sdk'; } from '@hyperlane-xyz/sdk';
import { ProtocolType, convertDecimals } from '@hyperlane-xyz/utils'; import { ProtocolType, convertDecimals } from '@hyperlane-xyz/utils';
@ -180,6 +183,22 @@ function getUsdQuote(
return quoteUsd; return quoteUsd;
} }
// cosmwasm warp route somewhat arbitrarily chosen
const FOREIGN_DEFAULT_OVERHEAD = 600_000;
// Overhead for interchain messaging
export function remoteOverhead(
remote: ChainName,
ethereumChainNames: ChainName[],
): number {
return ethereumChainNames.includes(remote as any)
? multisigIsmVerificationCost(
defaultMultisigConfigs[remote].threshold,
defaultMultisigConfigs[remote].validators.length,
)
: FOREIGN_DEFAULT_OVERHEAD; // non-ethereum overhead
}
// Gets the StorageGasOracleConfig for each local chain // Gets the StorageGasOracleConfig for each local chain
export function getAllStorageGasOracleConfigs( export function getAllStorageGasOracleConfigs(
chainNames: ChainName[], chainNames: ChainName[],
@ -204,12 +223,21 @@ export function getAllStorageGasOracleConfigs(
}, {}) as AllStorageGasOracleConfigs; }, {}) as AllStorageGasOracleConfigs;
} }
// Gets the exchange rate of the remote quoted in local tokens
export function getTokenExchangeRateFromValues( export function getTokenExchangeRateFromValues(
local: ChainName, local: ChainName,
localValue: BigNumber,
remote: ChainName, remote: ChainName,
remoteValue: BigNumber, tokenPrices: ChainMap<string>,
): BigNumber { ): BigNumber {
const localValue = ethers.utils.parseUnits(
tokenPrices[local],
TOKEN_EXCHANGE_RATE_DECIMALS,
);
const remoteValue = ethers.utils.parseUnits(
tokenPrices[remote],
TOKEN_EXCHANGE_RATE_DECIMALS,
);
// This does not yet account for decimals! // This does not yet account for decimals!
let exchangeRate = remoteValue.mul(TOKEN_EXCHANGE_RATE_SCALE).div(localValue); let exchangeRate = remoteValue.mul(TOKEN_EXCHANGE_RATE_SCALE).div(localValue);
// Apply the premium // Apply the premium
@ -224,6 +252,7 @@ export function getTokenExchangeRateFromValues(
); );
} }
// Gets the gas price for a Cosmos chain
export async function getCosmosChainGasPrice( export async function getCosmosChainGasPrice(
chain: ChainName, chain: ChainName,
): Promise<AgentCosmosGasPrice> { ): Promise<AgentCosmosGasPrice> {

Loading…
Cancel
Save