From ba15abef1793b04b50b3b33034c2d9b1ecc44722 Mon Sep 17 00:00:00 2001 From: Paul Balaji <10051819+paulbalaji@users.noreply.github.com> Date: Wed, 25 Sep 2024 11:36:45 -0400 Subject: [PATCH] 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 --- .changeset/popular-cars-burn.md | 2 +- .registryrc | 2 +- rust/main/config/mainnet_config.json | 4 +- rust/main/config/testnet_config.json | 42 +++++--- .../infra/config/environments/mainnet3/igp.ts | 50 ++------- .../config/environments/testnet4/chains.ts | 10 ++ .../environments/testnet4/gas-oracle.ts | 100 ------------------ .../environments/testnet4/gasPrices.json | 62 +++++++++++ .../infra/config/environments/testnet4/igp.ts | 41 +++---- .../environments/testnet4/tokenPrices.json | 17 +++ typescript/infra/scripts/print-gas-prices.ts | 19 +++- .../infra/scripts/print-token-prices.ts | 26 ++++- typescript/infra/src/config/gas-oracle.ts | 33 +++++- 13 files changed, 222 insertions(+), 186 deletions(-) delete mode 100644 typescript/infra/config/environments/testnet4/gas-oracle.ts create mode 100644 typescript/infra/config/environments/testnet4/gasPrices.json create mode 100644 typescript/infra/config/environments/testnet4/tokenPrices.json diff --git a/.changeset/popular-cars-burn.md b/.changeset/popular-cars-burn.md index e0faa46da..d402136d5 100644 --- a/.changeset/popular-cars-burn.md +++ b/.changeset/popular-cars-burn.md @@ -1,5 +1,5 @@ --- -"@hyperlane-xyz/sdk": patch +'@hyperlane-xyz/sdk': patch --- fix: warn on submodule metadata builder failures diff --git a/.registryrc b/.registryrc index 4a24b8cb7..9d73cdf16 100644 --- a/.registryrc +++ b/.registryrc @@ -1 +1 @@ -v4.3.6 +d7dc44d8894962648a544686cdce4536ce9569ce diff --git a/rust/main/config/mainnet_config.json b/rust/main/config/mainnet_config.json index b3c01dccf..9a2257b5f 100644 --- a/rust/main/config/mainnet_config.json +++ b/rust/main/config/mainnet_config.json @@ -2758,10 +2758,10 @@ "solanamainnet": { "blockExplorers": [ { - "apiUrl": "https://explorer.solana.com?cluster=mainnet-beta", + "apiUrl": "https://solscan.io", "family": "other", "name": "Solana Explorer", - "url": "https://explorer.solana.com?cluster=mainnet-beta" + "url": "https://solscan.io" } ], "blocks": { diff --git a/rust/main/config/testnet_config.json b/rust/main/config/testnet_config.json index 3b9d19ac9..b5ac9c957 100644 --- a/rust/main/config/testnet_config.json +++ b/rust/main/config/testnet_config.json @@ -68,7 +68,8 @@ "timelockController": "0x0000000000000000000000000000000000000000", "validatorAnnounce": "0x3726EE36a2A9e11a40d1ffD7D9A1A16e0154cDA0", "staticMerkleRootWeightedMultisigIsmFactory": "0x374961678da5911083599314974B94094513F95c", - "staticMessageIdWeightedMultisigIsmFactory": "0x1Fa22d908f5a5E7F5429D9146E5a3740D8AC10d7" + "staticMessageIdWeightedMultisigIsmFactory": "0x1Fa22d908f5a5E7F5429D9146E5a3740D8AC10d7", + "gasCurrencyCoinGeckoId": "celo" }, "arbitrumsepolia": { "aggregationHook": "0xD2670EedcD21116c6F0B331Ce391eA4B3Bf1aB19", @@ -132,7 +133,8 @@ "testRecipient": "0x6c13643B3927C57DB92c790E4E3E7Ee81e13f78C", "validatorAnnounce": "0x1b33611fCc073aB0737011d5512EF673Bff74962", "staticMerkleRootWeightedMultisigIsmFactory": "0x1aFD5191738d365C8079e955E4cEdDfe7e01C62d", - "staticMessageIdWeightedMultisigIsmFactory": "0xC81e6D1070aFA48DA4e4f35E744CC1aE43532a10" + "staticMessageIdWeightedMultisigIsmFactory": "0xC81e6D1070aFA48DA4e4f35E744CC1aE43532a10", + "gasCurrencyCoinGeckoId": "ethereum" }, "basesepolia": { "aggregationHook": "0xccA408a6A9A6dc405C3278647421eb4317466943", @@ -195,7 +197,8 @@ "testRecipient": "0x783c4a0bB6663359281aD4a637D5af68F83ae213", "validatorAnnounce": "0x20c44b1E3BeaDA1e9826CFd48BeEDABeE9871cE9", "staticMerkleRootWeightedMultisigIsmFactory": "0xB057Fb841027a8554521DcCdeC3c3474CaC99AB5", - "staticMessageIdWeightedMultisigIsmFactory": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F" + "staticMessageIdWeightedMultisigIsmFactory": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F", + "gasCurrencyCoinGeckoId": "ethereum" }, "bsctestnet": { "aggregationHook": "0x3d675bB93250Ab7603F40cbb9194bae210784627", @@ -266,7 +269,8 @@ }, "validatorAnnounce": "0xf09701B0a93210113D175461b6135a96773B5465", "staticMerkleRootWeightedMultisigIsmFactory": "0xCa152b249791Adf7A09C6c1bdbAb05e4A594966e", - "staticMessageIdWeightedMultisigIsmFactory": "0xaa80d23299861b7D7ab1bE665579029Ed9137BD1" + "staticMessageIdWeightedMultisigIsmFactory": "0xaa80d23299861b7D7ab1bE665579029Ed9137BD1", + "gasCurrencyCoinGeckoId": "binancecoin" }, "connextsepolia": { "aggregationHook": "0x331eb40963dc11F5BB271308c42d97ac6e41F124", @@ -327,7 +331,8 @@ "testRecipient": "0xAb9B273366D794B7F80B4378bc8Aaca75C6178E2", "validatorAnnounce": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C", "staticMerkleRootWeightedMultisigIsmFactory": "0x8584590ad637C61C7cDF72eFF3381Ee1c3D1bC8E", - "staticMessageIdWeightedMultisigIsmFactory": "0xcCB305B1f21e5FbC85D1DD7Be5cd8d5bf5B7f863" + "staticMessageIdWeightedMultisigIsmFactory": "0xcCB305B1f21e5FbC85D1DD7Be5cd8d5bf5B7f863", + "gasCurrencyCoinGeckoId": "ethereum" }, "eclipsetestnet": { "blocks": { @@ -362,7 +367,8 @@ "http": "https://testnet.dev2.eclipsenetwork.xyz" } ], - "validatorAnnounce": "8qNYSi9EP1xSnRjtMpyof88A26GBbdcrsa61uSaHiwx3" + "validatorAnnounce": "8qNYSi9EP1xSnRjtMpyof88A26GBbdcrsa61uSaHiwx3", + "gasCurrencyCoinGeckoId": "ethereum" }, "ecotestnet": { "aggregationHook": "0xccA408a6A9A6dc405C3278647421eb4317466943", @@ -422,7 +428,8 @@ "testRecipient": "0x783c4a0bB6663359281aD4a637D5af68F83ae213", "validatorAnnounce": "0x20c44b1E3BeaDA1e9826CFd48BeEDABeE9871cE9", "staticMerkleRootWeightedMultisigIsmFactory": "0xA2cf52064c921C11adCd83588CbEa08cc3bfF5d8", - "staticMessageIdWeightedMultisigIsmFactory": "0x628BC518ED1e0E8C6cbcD574EbA0ee29e7F6943E" + "staticMessageIdWeightedMultisigIsmFactory": "0x628BC518ED1e0E8C6cbcD574EbA0ee29e7F6943E", + "gasCurrencyCoinGeckoId": "ethereum" }, "fuji": { "aggregationHook": "0x8E9b4006171c6B75111823e7545Ee5400CEce0B3", @@ -489,7 +496,8 @@ "timelockController": "0x0000000000000000000000000000000000000000", "validatorAnnounce": "0x4f7179A691F8a684f56cF7Fed65171877d30739a", "staticMerkleRootWeightedMultisigIsmFactory": "0xff93F32997Ac5450995121385aCE96b184efe89E", - "staticMessageIdWeightedMultisigIsmFactory": "0x8eAB8cBb9037e818C321f675c0bc2EA4649003CF" + "staticMessageIdWeightedMultisigIsmFactory": "0x8eAB8cBb9037e818C321f675c0bc2EA4649003CF", + "gasCurrencyCoinGeckoId": "avalanche-2" }, "holesky": { "aggregationHook": "0xb1FfD51f03c69A0a3e5AFEBDE639752DB1d56bc9", @@ -549,7 +557,8 @@ "testRecipient": "0x86fb9F1c124fB20ff130C41a79a432F770f67AFD", "validatorAnnounce": "0xAb9B273366D794B7F80B4378bc8Aaca75C6178E2", "staticMerkleRootWeightedMultisigIsmFactory": "0xFb55597F07417b08195Ba674f4dd58aeC9B89FBB", - "staticMessageIdWeightedMultisigIsmFactory": "0x0E18b28D98C2efDb59252c021320F203305b1B66" + "staticMessageIdWeightedMultisigIsmFactory": "0x0E18b28D98C2efDb59252c021320F203305b1B66", + "gasCurrencyCoinGeckoId": "ethereum" }, "optimismsepolia": { "aggregationHook": "0xccA408a6A9A6dc405C3278647421eb4317466943", @@ -744,7 +753,8 @@ "testRecipient": "0x04438ef7622f5412f82915F59caD4f704C61eA48", "validatorAnnounce": "0x11918DC33E067C5DA83EEF58E50F856398b8Df4C", "staticMerkleRootWeightedMultisigIsmFactory": "0xe0B988062A0C6492177d64823Ab95a9c256c2a5F", - "staticMessageIdWeightedMultisigIsmFactory": "0x7c5B5bdA7F1d1F70A6678ABb4d894612Fc76498F" + "staticMessageIdWeightedMultisigIsmFactory": "0x7c5B5bdA7F1d1F70A6678ABb4d894612Fc76498F", + "gasCurrencyCoinGeckoId": "polygon-ecosystem-token" }, "scrollsepolia": { "aggregationHook": "0x7b63Aa270335F8896717c2A809205F4b650E4268", @@ -823,7 +833,8 @@ }, "validatorAnnounce": "0x527768930D889662Fe7ACF64294871e86e4C2381", "staticMerkleRootWeightedMultisigIsmFactory": "0x339B46496D60b1b6B42e9715DeD8B3D2154dA0Bb", - "staticMessageIdWeightedMultisigIsmFactory": "0x63dFf524F1c7361f4F1bf07D658Bf7f2d5Dd5B20" + "staticMessageIdWeightedMultisigIsmFactory": "0x63dFf524F1c7361f4F1bf07D658Bf7f2d5Dd5B20", + "gasCurrencyCoinGeckoId": "ethereum" }, "sepolia": { "aggregationHook": "0xe3147d5618f5e2e100690B50ec923009a4cde14A", @@ -894,7 +905,8 @@ "timelockController": "0x0000000000000000000000000000000000000000", "validatorAnnounce": "0xE6105C59480a1B7DD3E4f28153aFdbE12F4CfCD9", "staticMerkleRootWeightedMultisigIsmFactory": "0x4afB48e864d308409d0D80E98fB7d5d6aA5b245f", - "staticMessageIdWeightedMultisigIsmFactory": "0x196Ce28ED1Afdf015849ddEE82F03a903Bee9E94" + "staticMessageIdWeightedMultisigIsmFactory": "0x196Ce28ED1Afdf015849ddEE82F03a903Bee9E94", + "gasCurrencyCoinGeckoId": "ethereum" }, "solanatestnet": { "blockExplorers": [ @@ -938,7 +950,8 @@ "http": "https://api.testnet.solana.com" } ], - "validatorAnnounce": "8qNYSi9EP1xSnRjtMpyof88A26GBbdcrsa61uSaHiwx3" + "validatorAnnounce": "8qNYSi9EP1xSnRjtMpyof88A26GBbdcrsa61uSaHiwx3", + "gasCurrencyCoinGeckoId": "solana" }, "superpositiontestnet": { "aggregationHook": "0x331eb40963dc11F5BB271308c42d97ac6e41F124", @@ -995,7 +1008,8 @@ "testRecipient": "0xAb9B273366D794B7F80B4378bc8Aaca75C6178E2", "validatorAnnounce": "0xAD34A66Bf6dB18E858F6B686557075568c6E031C", "staticMerkleRootWeightedMultisigIsmFactory": "0xE67CfA164cDa449Ae38a0a09391eF6bCDf8e4e2c", - "staticMessageIdWeightedMultisigIsmFactory": "0x867f2089D09903f208AeCac84E599B90E5a4A821" + "staticMessageIdWeightedMultisigIsmFactory": "0x867f2089D09903f208AeCac84E599B90E5a4A821", + "gasCurrencyCoinGeckoId": "superposition" } }, "defaultRpcConsensusType": "fallback" diff --git a/typescript/infra/config/environments/mainnet3/igp.ts b/typescript/infra/config/environments/mainnet3/igp.ts index ca167f356..7275cbe02 100644 --- a/typescript/infra/config/environments/mainnet3/igp.ts +++ b/typescript/infra/config/environments/mainnet3/igp.ts @@ -1,20 +1,11 @@ -import { BigNumber, ethers } from 'ethers'; - -import { - ChainMap, - ChainName, - HookType, - IgpConfig, - TOKEN_EXCHANGE_RATE_DECIMALS, - defaultMultisigConfigs, - multisigIsmVerificationCost, -} from '@hyperlane-xyz/sdk'; +import { ChainMap, ChainName, HookType, IgpConfig } from '@hyperlane-xyz/sdk'; import { exclude, objMap } from '@hyperlane-xyz/utils'; import { AllStorageGasOracleConfigs, getAllStorageGasOracleConfigs, getTokenExchangeRateFromValues, + remoteOverhead, } from '../../../src/config/gas-oracle.js'; import { ethereumChainNames } from './chains.js'; @@ -25,43 +16,22 @@ import rawTokenPrices from './tokenPrices.json'; const tokenPrices: ChainMap = rawTokenPrices; -const FOREIGN_DEFAULT_OVERHEAD = 600_000; // cosmwasm warp route somewhat arbitrarily chosen - -function remoteOverhead(remote: ChainName) { - 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 +const remoteOverheadWithOverrides = (chain: ChainName) => { + let overhead = remoteOverhead(chain, ethereumChainNames); + if (chain === 'moonbeam') { overhead *= 4; } 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 = getAllStorageGasOracleConfigs( supportedChainNames, gasPrices, - getTokenExchangeRate, + (local, remote) => + getTokenExchangeRateFromValues(local, remote, tokenPrices), (local) => parseFloat(tokenPrices[local]), - (local) => remoteOverhead(local), + remoteOverheadWithOverrides, ); export const igp: ChainMap = objMap( @@ -79,7 +49,7 @@ export const igp: ChainMap = objMap( overhead: Object.fromEntries( exclude(local, supportedChainNames).map((remote) => [ remote, - remoteOverhead(remote), + remoteOverhead(remote, ethereumChainNames), ]), ), oracleConfig: storageGasOracleConfig[local], diff --git a/typescript/infra/config/environments/testnet4/chains.ts b/typescript/infra/config/environments/testnet4/chains.ts index 4d6923f3f..61073930f 100644 --- a/typescript/infra/config/environments/testnet4/chains.ts +++ b/typescript/infra/config/environments/testnet4/chains.ts @@ -1,5 +1,7 @@ +import { IRegistry } from '@hyperlane-xyz/registry'; import { ChainMap, ChainMetadata } from '@hyperlane-xyz/sdk'; +import { getRegistryForEnvironment } from '../../../src/config/chain.js'; import { isEthereumProtocolChain } from '../../../src/utils/utils.js'; import { supportedChainNames } from './supportedChainNames.js'; @@ -22,3 +24,11 @@ export const chainMetadataOverrides: ChainMap> = { }, }, }; + +export const getRegistry = async (useSecrets = true): Promise => + getRegistryForEnvironment( + environment, + supportedChainNames, + chainMetadataOverrides, + useSecrets, + ); diff --git a/typescript/infra/config/environments/testnet4/gas-oracle.ts b/typescript/infra/config/environments/testnet4/gas-oracle.ts deleted file mode 100644 index b09184c44..000000000 --- a/typescript/infra/config/environments/testnet4/gas-oracle.ts +++ /dev/null @@ -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.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, - ); diff --git a/typescript/infra/config/environments/testnet4/gasPrices.json b/typescript/infra/config/environments/testnet4/gasPrices.json new file mode 100644 index 000000000..0c8369fb3 --- /dev/null +++ b/typescript/infra/config/environments/testnet4/gasPrices.json @@ -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 + } +} diff --git a/typescript/infra/config/environments/testnet4/igp.ts b/typescript/infra/config/environments/testnet4/igp.ts index a564a3360..92d0506d1 100644 --- a/typescript/infra/config/environments/testnet4/igp.ts +++ b/typescript/infra/config/environments/testnet4/igp.ts @@ -1,27 +1,30 @@ -import { - ChainMap, - ChainName, - HookType, - IgpConfig, - defaultMultisigConfigs, - multisigIsmVerificationCost, -} from '@hyperlane-xyz/sdk'; +import { ChainMap, HookType, IgpConfig } from '@hyperlane-xyz/sdk'; 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 { supportedChainNames } from './supportedChainNames.js'; +import rawTokenPrices from './tokenPrices.json'; + +const tokenPrices: ChainMap = rawTokenPrices; -const FOREIGN_DEFAULT_OVERHEAD = 600_000; // cosmwasm warp route somewhat arbitrarily chosen -const remoteOverhead = (remote: ChainName) => - supportedChainNames.filter(isEthereumProtocolChain).includes(remote as any) - ? multisigIsmVerificationCost( - defaultMultisigConfigs[remote].threshold, - defaultMultisigConfigs[remote].validators.length, - ) - : FOREIGN_DEFAULT_OVERHEAD; // non-ethereum overhead +export const storageGasOracleConfig: AllStorageGasOracleConfigs = + getAllStorageGasOracleConfigs( + supportedChainNames, + gasPrices, + (local, remote) => + getTokenExchangeRateFromValues(local, remote, tokenPrices), + (local) => parseFloat(tokenPrices[local]), + (local) => remoteOverhead(local, ethereumChainNames), + ); export const igp: ChainMap = objMap( owners, @@ -35,7 +38,7 @@ export const igp: ChainMap = objMap( overhead: Object.fromEntries( exclude(chain, supportedChainNames).map((remote) => [ remote, - remoteOverhead(remote), + remoteOverhead(remote, ethereumChainNames), ]), ), }; diff --git a/typescript/infra/config/environments/testnet4/tokenPrices.json b/typescript/infra/config/environments/testnet4/tokenPrices.json new file mode 100644 index 000000000..8f2f1eb53 --- /dev/null +++ b/typescript/infra/config/environments/testnet4/tokenPrices.json @@ -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" +} diff --git a/typescript/infra/scripts/print-gas-prices.ts b/typescript/infra/scripts/print-gas-prices.ts index 4f94dd26a..805122ad1 100644 --- a/typescript/infra/scripts/print-gas-prices.ts +++ b/typescript/infra/scripts/print-gas-prices.ts @@ -8,19 +8,34 @@ import { ProtocolType } from '@hyperlane-xyz/utils'; // to avoid circular dependencies. import { getRegistry as getMainnet3Registry } from '../config/environments/mainnet3/chains.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 { GasPriceConfig, getCosmosChainGasPrice, } from '../src/config/gas-oracle.js'; +import { getArgs } from './agent-utils.js'; + 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 mpp = new MultiProtocolProvider(chainMetadata); const prices: ChainMap = Object.fromEntries( await Promise.all( - mainnet3SupportedChainNames.map(async (chain) => [ + supportedChainNames.map(async (chain) => [ chain, await getGasPrice(mpp, chain), ]), diff --git a/typescript/infra/scripts/print-token-prices.ts b/typescript/infra/scripts/print-token-prices.ts index d2d2a941c..5a3e848eb 100644 --- a/typescript/infra/scripts/print-token-prices.ts +++ b/typescript/infra/scripts/print-token-prices.ts @@ -1,22 +1,38 @@ import { ChainMetadata } from '@hyperlane-xyz/sdk'; 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. 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'; 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 metadata = pick( chainMetadata as Record< - (typeof mainnet3SupportedChainNames)[number], + (typeof supportedChainNames)[number], ChainMetadata >, - [...mainnet3SupportedChainNames], + [...supportedChainNames], ); const ids = objMap( diff --git a/typescript/infra/src/config/gas-oracle.ts b/typescript/infra/src/config/gas-oracle.ts index acc232c5a..8cbc6194c 100644 --- a/typescript/infra/src/config/gas-oracle.ts +++ b/typescript/infra/src/config/gas-oracle.ts @@ -5,8 +5,11 @@ import { ChainMap, ChainName, StorageGasOracleConfig as DestinationOracleConfig, + TOKEN_EXCHANGE_RATE_DECIMALS, TOKEN_EXCHANGE_RATE_SCALE, + defaultMultisigConfigs, getCosmosRegistryChain, + multisigIsmVerificationCost, } from '@hyperlane-xyz/sdk'; import { ProtocolType, convertDecimals } from '@hyperlane-xyz/utils'; @@ -180,6 +183,22 @@ function getUsdQuote( 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 export function getAllStorageGasOracleConfigs( chainNames: ChainName[], @@ -204,12 +223,21 @@ export function getAllStorageGasOracleConfigs( }, {}) as AllStorageGasOracleConfigs; } +// Gets the exchange rate of the remote quoted in local tokens export function getTokenExchangeRateFromValues( local: ChainName, - localValue: BigNumber, remote: ChainName, - remoteValue: BigNumber, + tokenPrices: ChainMap, ): 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! let exchangeRate = remoteValue.mul(TOKEN_EXCHANGE_RATE_SCALE).div(localValue); // Apply the premium @@ -224,6 +252,7 @@ export function getTokenExchangeRateFromValues( ); } +// Gets the gas price for a Cosmos chain export async function getCosmosChainGasPrice( chain: ChainName, ): Promise {