From 4c49f6179a6783dbb14b2f225cb5755642e99784 Mon Sep 17 00:00:00 2001 From: Yorke Rhodes Date: Sat, 4 Nov 2023 10:12:00 -0400 Subject: [PATCH] Add Cosmos support to Utils and SDK (#2859) Co-authored-by: J M Rossy Co-authored-by: Nam Chu Hoai Co-authored-by: Kunal Arora <55632507+aroralanuk@users.noreply.github.com> --- .gitattributes | 2 + solidity/package.json | 4 +- typescript/helloworld/package.json | 6 +- .../environments/mainnet3/liquidityLayer.ts | 11 +- .../environments/mainnet3/token-bridge.ts | 8 +- .../environments/testnet4/liquidityLayer.ts | 27 +- .../environments/testnet4/token-bridge.ts | 27 +- .../helm/warp-routes/templates/_helpers.tpl | 4 +- typescript/infra/package.json | 6 +- .../warp-routes/deploy-warp-monitor.ts | 6 +- typescript/infra/scripts/warp-routes/helm.ts | 12 +- .../monitor-warp-routes-balances.ts | 168 ++++--- typescript/infra/src/config/agent/relayer.ts | 9 +- ...oken_config.ts => grafana_token_config.ts} | 55 ++- typescript/sdk/package.json | 9 +- .../sdk/src/app/MultiProtocolApp.test.ts | 2 + typescript/sdk/src/app/MultiProtocolApp.ts | 18 + typescript/sdk/src/consts/chainMetadata.ts | 64 +++ typescript/sdk/src/consts/chains.ts | 4 + typescript/sdk/src/core/MultiProtocolCore.ts | 2 + .../src/core/adapters/CosmWasmCoreAdapter.ts | 184 ++++++++ .../src/core/adapters/CosmWasmIgpAdapter.ts | 142 ++++++ typescript/sdk/src/cw-types/Cw20Base.types.ts | 228 ++++++++++ .../sdk/src/cw-types/HookAggregate.types.ts | 92 ++++ .../sdk/src/cw-types/HookMerkle.types.ts | 180 ++++++++ .../sdk/src/cw-types/HookPausable.types.ts | 97 ++++ .../sdk/src/cw-types/HookRouting.types.ts | 127 ++++++ .../src/cw-types/HookRoutingCustom.types.ts | 174 ++++++++ .../src/cw-types/HookRoutingFallback.types.ts | 132 ++++++ typescript/sdk/src/cw-types/Igp.types.ts | 215 +++++++++ .../sdk/src/cw-types/IgpOracle.types.ts | 71 +++ .../sdk/src/cw-types/IsmAggregate.types.ts | 97 ++++ .../sdk/src/cw-types/IsmMultisig.types.ts | 127 ++++++ .../sdk/src/cw-types/IsmRouting.types.ts | 102 +++++ typescript/sdk/src/cw-types/Mailbox.types.ts | 154 +++++++ .../src/cw-types/ValidatorAnnounce.types.ts | 33 ++ typescript/sdk/src/cw-types/WarpCw20.types.ts | 256 +++++++++++ .../sdk/src/cw-types/WarpNative.types.ts | 232 ++++++++++ typescript/sdk/src/index.ts | 19 +- .../ism/adapters/CosmWasmMultisigAdapter.ts | 122 +++++ .../sdk/src/metadata/ChainMetadataManager.ts | 35 +- typescript/sdk/src/metadata/agentConfig.ts | 4 +- .../sdk/src/metadata/chainMetadata.test.ts | 25 ++ .../sdk/src/metadata/chainMetadataTypes.ts | 65 ++- .../src/providers/MultiProtocolProvider.ts | 66 ++- typescript/sdk/src/providers/ProviderType.ts | 78 +++- .../sdk/src/providers/providerBuilders.ts | 29 ++ .../sdk/src/router/MultiProtocolRouterApps.ts | 1 + .../adapters/CosmWasmTokenAdapter.test.ts | 398 +++++++++++++++++ .../token/adapters/CosmWasmTokenAdapter.ts | 409 +++++++++++++++++ .../src/token/adapters/CosmosTokenAdapter.ts | 65 +++ .../token/adapters/SealevelTokenAdapter.ts | 2 +- typescript/sdk/src/utils/wagmi.ts | 15 +- typescript/utils/index.ts | 9 + typescript/utils/package.json | 3 +- typescript/utils/src/addresses.ts | 165 ++++--- typescript/utils/src/types.ts | 2 + yarn.lock | 422 +++++++++++++++++- 58 files changed, 4795 insertions(+), 226 deletions(-) create mode 100644 .gitattributes rename typescript/infra/src/config/{nautilus_token_config.ts => grafana_token_config.ts} (53%) create mode 100644 typescript/sdk/src/core/adapters/CosmWasmCoreAdapter.ts create mode 100644 typescript/sdk/src/core/adapters/CosmWasmIgpAdapter.ts create mode 100644 typescript/sdk/src/cw-types/Cw20Base.types.ts create mode 100644 typescript/sdk/src/cw-types/HookAggregate.types.ts create mode 100644 typescript/sdk/src/cw-types/HookMerkle.types.ts create mode 100644 typescript/sdk/src/cw-types/HookPausable.types.ts create mode 100644 typescript/sdk/src/cw-types/HookRouting.types.ts create mode 100644 typescript/sdk/src/cw-types/HookRoutingCustom.types.ts create mode 100644 typescript/sdk/src/cw-types/HookRoutingFallback.types.ts create mode 100644 typescript/sdk/src/cw-types/Igp.types.ts create mode 100644 typescript/sdk/src/cw-types/IgpOracle.types.ts create mode 100644 typescript/sdk/src/cw-types/IsmAggregate.types.ts create mode 100644 typescript/sdk/src/cw-types/IsmMultisig.types.ts create mode 100644 typescript/sdk/src/cw-types/IsmRouting.types.ts create mode 100644 typescript/sdk/src/cw-types/Mailbox.types.ts create mode 100644 typescript/sdk/src/cw-types/ValidatorAnnounce.types.ts create mode 100644 typescript/sdk/src/cw-types/WarpCw20.types.ts create mode 100644 typescript/sdk/src/cw-types/WarpNative.types.ts create mode 100644 typescript/sdk/src/ism/adapters/CosmWasmMultisigAdapter.ts create mode 100644 typescript/sdk/src/token/adapters/CosmWasmTokenAdapter.test.ts create mode 100644 typescript/sdk/src/token/adapters/CosmWasmTokenAdapter.ts create mode 100644 typescript/sdk/src/token/adapters/CosmosTokenAdapter.ts diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..e0732994f --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +typescript/sdk/src/cw-types/*.types.ts linguist-generated=true +rust/chains/hyperlane-ethereum/abis/*.abi.json linguist-generated=true diff --git a/solidity/package.json b/solidity/package.json index d9bae0c38..cc1ed89cc 100644 --- a/solidity/package.json +++ b/solidity/package.json @@ -1,10 +1,10 @@ { "name": "@hyperlane-xyz/core", "description": "Core solidity contracts for Hyperlane", - "version": "1.5.4-beta0", + "version": "3.1.0-beta0", "dependencies": { "@eth-optimism/contracts": "^0.6.0", - "@hyperlane-xyz/utils": "1.5.4-beta0", + "@hyperlane-xyz/utils": "3.1.0-beta0", "@openzeppelin/contracts": "^4.8.0", "@openzeppelin/contracts-upgradeable": "^4.8.0" }, diff --git a/typescript/helloworld/package.json b/typescript/helloworld/package.json index 8be3bda9e..5b95890f7 100644 --- a/typescript/helloworld/package.json +++ b/typescript/helloworld/package.json @@ -1,10 +1,10 @@ { "name": "@hyperlane-xyz/helloworld", "description": "A basic skeleton of an Hyperlane app", - "version": "1.5.4-beta0", + "version": "3.1.0-beta0", "dependencies": { - "@hyperlane-xyz/core": "1.5.4-beta0", - "@hyperlane-xyz/sdk": "1.5.4-beta0", + "@hyperlane-xyz/core": "3.1.0-beta0", + "@hyperlane-xyz/sdk": "3.1.0-beta0", "@openzeppelin/contracts-upgradeable": "^4.8.0", "ethers": "^5.7.2" }, diff --git a/typescript/infra/config/environments/mainnet3/liquidityLayer.ts b/typescript/infra/config/environments/mainnet3/liquidityLayer.ts index e81d7564a..6d0759f82 100644 --- a/typescript/infra/config/environments/mainnet3/liquidityLayer.ts +++ b/typescript/infra/config/environments/mainnet3/liquidityLayer.ts @@ -5,6 +5,7 @@ import { Chains, RpcConsensusType, chainMetadata, + getDomainId, } from '@hyperlane-xyz/sdk'; import { LiquidityLayerRelayerConfig } from '../../../src/config/middleware'; @@ -12,8 +13,14 @@ import { LiquidityLayerRelayerConfig } from '../../../src/config/middleware'; import { environment } from './chains'; const circleDomainMapping = [ - { hyperlaneDomain: chainMetadata[Chains.ethereum].chainId, circleDomain: 0 }, - { hyperlaneDomain: chainMetadata[Chains.avalanche].chainId, circleDomain: 1 }, + { + hyperlaneDomain: getDomainId(chainMetadata[Chains.ethereum]), + circleDomain: 0, + }, + { + hyperlaneDomain: getDomainId(chainMetadata[Chains.avalanche]), + circleDomain: 1, + }, ]; export const bridgeAdapterConfigs: ChainMap = { diff --git a/typescript/infra/config/environments/mainnet3/token-bridge.ts b/typescript/infra/config/environments/mainnet3/token-bridge.ts index 66838a802..16e568a41 100644 --- a/typescript/infra/config/environments/mainnet3/token-bridge.ts +++ b/typescript/infra/config/environments/mainnet3/token-bridge.ts @@ -4,11 +4,15 @@ import { Chains, CircleBridgeAdapterConfig, chainMetadata, + getDomainId, } from '@hyperlane-xyz/sdk'; const circleDomainMapping = [ - { hyperlaneDomain: chainMetadata[Chains.goerli].chainId, circleDomain: 0 }, - { hyperlaneDomain: chainMetadata[Chains.fuji].chainId, circleDomain: 1 }, + { + hyperlaneDomain: getDomainId(chainMetadata[Chains.goerli]), + circleDomain: 0, + }, + { hyperlaneDomain: getDomainId(chainMetadata[Chains.fuji]), circleDomain: 1 }, ]; // Circle deployed contracts diff --git a/typescript/infra/config/environments/testnet4/liquidityLayer.ts b/typescript/infra/config/environments/testnet4/liquidityLayer.ts index bba05105d..0ae83c4a0 100644 --- a/typescript/infra/config/environments/testnet4/liquidityLayer.ts +++ b/typescript/infra/config/environments/testnet4/liquidityLayer.ts @@ -4,23 +4,36 @@ import { ChainMap, Chains, chainMetadata, + getDomainId, } from '@hyperlane-xyz/sdk'; const circleDomainMapping = [ - { hyperlaneDomain: chainMetadata[Chains.goerli].chainId, circleDomain: 0 }, - { hyperlaneDomain: chainMetadata[Chains.fuji].chainId, circleDomain: 1 }, + { + hyperlaneDomain: getDomainId(chainMetadata[Chains.goerli]), + circleDomain: 0, + }, + { hyperlaneDomain: getDomainId(chainMetadata[Chains.fuji]), circleDomain: 1 }, ]; const wormholeDomainMapping = [ - { hyperlaneDomain: chainMetadata[Chains.goerli].chainId, wormholeDomain: 2 }, - { hyperlaneDomain: chainMetadata[Chains.fuji].chainId, wormholeDomain: 6 }, - { hyperlaneDomain: chainMetadata[Chains.mumbai].chainId, wormholeDomain: 5 }, { - hyperlaneDomain: chainMetadata[Chains.bsctestnet].chainId, + hyperlaneDomain: getDomainId(chainMetadata[Chains.goerli]), + wormholeDomain: 2, + }, + { + hyperlaneDomain: getDomainId(chainMetadata[Chains.fuji]), + wormholeDomain: 6, + }, + { + hyperlaneDomain: getDomainId(chainMetadata[Chains.mumbai]), + wormholeDomain: 5, + }, + { + hyperlaneDomain: getDomainId(chainMetadata[Chains.bsctestnet]), wormholeDomain: 4, }, { - hyperlaneDomain: chainMetadata[Chains.alfajores].chainId, + hyperlaneDomain: getDomainId(chainMetadata[Chains.alfajores]), wormholeDomain: 14, }, ]; diff --git a/typescript/infra/config/environments/testnet4/token-bridge.ts b/typescript/infra/config/environments/testnet4/token-bridge.ts index bba05105d..0ae83c4a0 100644 --- a/typescript/infra/config/environments/testnet4/token-bridge.ts +++ b/typescript/infra/config/environments/testnet4/token-bridge.ts @@ -4,23 +4,36 @@ import { ChainMap, Chains, chainMetadata, + getDomainId, } from '@hyperlane-xyz/sdk'; const circleDomainMapping = [ - { hyperlaneDomain: chainMetadata[Chains.goerli].chainId, circleDomain: 0 }, - { hyperlaneDomain: chainMetadata[Chains.fuji].chainId, circleDomain: 1 }, + { + hyperlaneDomain: getDomainId(chainMetadata[Chains.goerli]), + circleDomain: 0, + }, + { hyperlaneDomain: getDomainId(chainMetadata[Chains.fuji]), circleDomain: 1 }, ]; const wormholeDomainMapping = [ - { hyperlaneDomain: chainMetadata[Chains.goerli].chainId, wormholeDomain: 2 }, - { hyperlaneDomain: chainMetadata[Chains.fuji].chainId, wormholeDomain: 6 }, - { hyperlaneDomain: chainMetadata[Chains.mumbai].chainId, wormholeDomain: 5 }, { - hyperlaneDomain: chainMetadata[Chains.bsctestnet].chainId, + hyperlaneDomain: getDomainId(chainMetadata[Chains.goerli]), + wormholeDomain: 2, + }, + { + hyperlaneDomain: getDomainId(chainMetadata[Chains.fuji]), + wormholeDomain: 6, + }, + { + hyperlaneDomain: getDomainId(chainMetadata[Chains.mumbai]), + wormholeDomain: 5, + }, + { + hyperlaneDomain: getDomainId(chainMetadata[Chains.bsctestnet]), wormholeDomain: 4, }, { - hyperlaneDomain: chainMetadata[Chains.alfajores].chainId, + hyperlaneDomain: getDomainId(chainMetadata[Chains.alfajores]), wormholeDomain: 14, }, ]; diff --git a/typescript/infra/helm/warp-routes/templates/_helpers.tpl b/typescript/infra/helm/warp-routes/templates/_helpers.tpl index fe17261f4..338d8ad50 100644 --- a/typescript/infra/helm/warp-routes/templates/_helpers.tpl +++ b/typescript/infra/helm/warp-routes/templates/_helpers.tpl @@ -61,6 +61,8 @@ The warp-routes container command: - ./node_modules/.bin/ts-node - ./typescript/infra/scripts/warp-routes/monitor-warp-routes-balances.ts - - -c + - -l - "10000" + - -c + - {{ .Values.config }} {{- end }} diff --git a/typescript/infra/package.json b/typescript/infra/package.json index fcd9e5589..1cdba73ed 100644 --- a/typescript/infra/package.json +++ b/typescript/infra/package.json @@ -11,9 +11,9 @@ "@ethersproject/experimental": "^5.7.0", "@ethersproject/hardware-wallets": "^5.7.0", "@ethersproject/providers": "^5.7.2", - "@hyperlane-xyz/helloworld": "1.5.4-beta0", - "@hyperlane-xyz/sdk": "1.5.4-beta0", - "@hyperlane-xyz/utils": "1.5.4-beta0", + "@hyperlane-xyz/helloworld": "3.1.0-beta0", + "@hyperlane-xyz/sdk": "3.1.0-beta0", + "@hyperlane-xyz/utils": "3.1.0-beta0", "@nomiclabs/hardhat-etherscan": "^3.0.3", "@safe-global/api-kit": "^1.3.0", "@safe-global/protocol-kit": "^1.2.0", diff --git a/typescript/infra/scripts/warp-routes/deploy-warp-monitor.ts b/typescript/infra/scripts/warp-routes/deploy-warp-monitor.ts index 30418d906..534267004 100644 --- a/typescript/infra/scripts/warp-routes/deploy-warp-monitor.ts +++ b/typescript/infra/scripts/warp-routes/deploy-warp-monitor.ts @@ -3,7 +3,11 @@ import { HelmCommand } from '../../src/utils/helm'; import { runWarpRouteHelmCommand } from './helm'; async function main() { - await runWarpRouteHelmCommand(HelmCommand.InstallOrUpgrade, 'mainnet3'); + await runWarpRouteHelmCommand( + HelmCommand.InstallOrUpgrade, + 'mainnet3', + 'neutron', + ); } main() diff --git a/typescript/infra/scripts/warp-routes/helm.ts b/typescript/infra/scripts/warp-routes/helm.ts index 4a00d3ed3..170800e00 100644 --- a/typescript/infra/scripts/warp-routes/helm.ts +++ b/typescript/infra/scripts/warp-routes/helm.ts @@ -6,17 +6,18 @@ import { assertCorrectKubeContext, getEnvironmentConfig } from '../utils'; export async function runWarpRouteHelmCommand( helmCommand: HelmCommand, runEnv: DeployEnvironment, + config: string, ) { const envConfig = getEnvironmentConfig(runEnv); await assertCorrectKubeContext(envConfig); - const values = getWarpRoutesHelmValues(); + const values = getWarpRoutesHelmValues(config); return execCmd( `helm ${helmCommand} ${getHelmReleaseName( - 'zebec', + config, )} ./helm/warp-routes --namespace ${runEnv} ${values.join( ' ', - )} --set fullnameOverride="${getHelmReleaseName('zebec')}"`, + )} --set fullnameOverride="${getHelmReleaseName(config)}"`, ); } @@ -24,12 +25,13 @@ function getHelmReleaseName(route: string): string { return `hyperlane-warp-route-${route}`; } -function getWarpRoutesHelmValues() { +function getWarpRoutesHelmValues(config: string) { const values = { image: { repository: 'gcr.io/abacus-labs-dev/hyperlane-monorepo', - tag: '962d34b-20230905-194531', + tag: 'ae8ce44-20231101-012032', }, + config: config, // nautilus or neutron }; return helmifyValues(values); } diff --git a/typescript/infra/scripts/warp-routes/monitor-warp-routes-balances.ts b/typescript/infra/scripts/warp-routes/monitor-warp-routes-balances.ts index b864a2f6f..b2631a87c 100644 --- a/typescript/infra/scripts/warp-routes/monitor-warp-routes-balances.ts +++ b/typescript/infra/scripts/warp-routes/monitor-warp-routes-balances.ts @@ -7,8 +7,8 @@ import { ERC20__factory } from '@hyperlane-xyz/core'; import { ChainMap, ChainName, + CwNativeTokenAdapter, MultiProtocolProvider, - MultiProvider, SealevelHypCollateralAdapter, TokenType, } from '@hyperlane-xyz/sdk'; @@ -21,8 +21,9 @@ import { import { WarpTokenConfig, - tokenList, -} from '../../src/config/nautilus_token_config'; + nautilusList, + neutronList, +} from '../../src/config/grafana_token_config'; import { startMetricsServer } from '../../src/utils/metrics'; const metricsRegister = new Registry(); @@ -40,21 +41,29 @@ const warpRouteTokenBalance = new Gauge({ }); async function main(): Promise { - const { checkFrequency } = await yargs(process.argv.slice(2)) + const { checkFrequency, config } = await yargs(process.argv.slice(2)) .describe('checkFrequency', 'frequency to check balances in ms') .demandOption('checkFrequency') - .alias('c', 'checkFrequency') + .alias('l', 'checkFrequency') .number('checkFrequency') + .alias('c', 'config') + .describe('config', 'choose warp token config') + .demandOption('config') + .choices('config', ['neutron', 'nautilus']) .parse(); + const tokenList: WarpTokenConfig = + config === 'neutron' ? neutronList : nautilusList; + startMetricsServer(metricsRegister); - const multiProvider = new MultiProvider(); + console.log('Starting Warp Route balance monitor'); + const multiProtocolProvider = new MultiProtocolProvider(); setInterval(async () => { try { - console.log('Checking balances'); - const balances = await checkBalance(tokenList, multiProvider); + debug('Checking balances'); + const balances = await checkBalance(tokenList, multiProtocolProvider); updateTokenBalanceMetrics(tokenList, balances); } catch (e) { console.error('Error checking balances', e); @@ -66,52 +75,107 @@ async function main(): Promise { // TODO: see issue https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/2708 async function checkBalance( tokenConfig: WarpTokenConfig, - multiprovider: MultiProvider, + multiProtocolProvider: MultiProtocolProvider, ): Promise> { const output: ChainMap> = objMap( tokenConfig, async (chain: ChainName, token: WarpTokenConfig[ChainName]) => { - const provider = multiprovider.getProvider(chain); - if (token.type === TokenType.native) { - if (token.protocolType === ProtocolType.Ethereum) { - const nativeBalance = await provider.getBalance( - token.hypNativeAddress, - ); - return parseFloat( - ethers.utils.formatUnits(nativeBalance, token.decimals), - ); - } else { - // TODO - solana native - return 0; + switch (token.type) { + case TokenType.native: { + switch (token.protocolType) { + case ProtocolType.Ethereum: { + const provider = multiProtocolProvider.getEthersV5Provider(chain); + const nativeBalance = await provider.getBalance( + token.hypNativeAddress, + ); + return parseFloat( + ethers.utils.formatUnits(nativeBalance, token.decimals), + ); + } + case ProtocolType.Sealevel: + // TODO - solana native + return 0; + case ProtocolType.Cosmos: + // TODO - cosmos native + return 0; + } + break; } - } else { - if (token.protocolType === ProtocolType.Ethereum) { - const tokenContract = ERC20__factory.connect(token.address, provider); - const collateralBalance = await tokenContract.balanceOf( - token.hypCollateralAddress, - ); + case TokenType.collateral: { + switch (token.protocolType) { + case ProtocolType.Ethereum: { + const provider = multiProtocolProvider.getEthersV5Provider(chain); + const tokenContract = ERC20__factory.connect( + token.address, + provider, + ); + const collateralBalance = await tokenContract.balanceOf( + token.hypCollateralAddress, + ); - return parseFloat( - ethers.utils.formatUnits(collateralBalance, token.decimals), - ); - } else { - const adapter = new SealevelHypCollateralAdapter( - chain, - MultiProtocolProvider.fromMultiProvider(multiprovider), - { - token: token.address, - warpRouter: token.hypCollateralAddress, - // Mailbox only required for transfers, using system as placeholder - mailbox: SystemProgram.programId.toBase58(), - }, - token.isSpl2022, - ); - const collateralBalance = ethers.BigNumber.from( - await adapter.getBalance(token.hypCollateralAddress), - ); - return parseFloat( - ethers.utils.formatUnits(collateralBalance, token.decimals), - ); + return parseFloat( + ethers.utils.formatUnits(collateralBalance, token.decimals), + ); + } + case ProtocolType.Sealevel: { + const adapter = new SealevelHypCollateralAdapter( + chain, + multiProtocolProvider, + { + token: token.address, + warpRouter: token.hypCollateralAddress, + // Mailbox only required for transfers, using system as placeholder + mailbox: SystemProgram.programId.toBase58(), + }, + token.isSpl2022, + ); + const collateralBalance = ethers.BigNumber.from( + await adapter.getBalance(token.hypCollateralAddress), + ); + return parseFloat( + ethers.utils.formatUnits(collateralBalance, token.decimals), + ); + } + case ProtocolType.Cosmos: { + const adapter = new CwNativeTokenAdapter( + chain, + multiProtocolProvider, + { + token: token.address, + }, + token.address, + ); + const collateralBalance = ethers.BigNumber.from( + await adapter.getBalance(token.hypCollateralAddress), + ); + return parseFloat( + ethers.utils.formatUnits(collateralBalance, token.decimals), + ); + } + } + break; + } + case TokenType.synthetic: { + switch (token.protocolType) { + case ProtocolType.Ethereum: { + const provider = multiProtocolProvider.getEthersV5Provider(chain); + const tokenContract = ERC20__factory.connect( + token.hypSyntheticAddress, + provider, + ); + const syntheticBalance = await tokenContract.totalSupply(); + return parseFloat( + ethers.utils.formatUnits(syntheticBalance, token.decimals), + ); + } + case ProtocolType.Sealevel: + // TODO - solana native + return 0; + case ProtocolType.Cosmos: + // TODO - cosmos native + return 0; + } + break; } } }, @@ -128,11 +192,15 @@ function updateTokenBalanceMetrics( const tokenAddress = token.type === TokenType.native ? ethers.constants.AddressZero - : token.address; + : token.type === TokenType.collateral + ? token.address + : token.hypSyntheticAddress; const walletAddress = token.type === TokenType.native ? token.hypNativeAddress - : token.hypCollateralAddress; + : token.type === TokenType.collateral + ? token.hypCollateralAddress + : token.hypSyntheticAddress; warpRouteTokenBalance .labels({ diff --git a/typescript/infra/src/config/agent/relayer.ts b/typescript/infra/src/config/agent/relayer.ts index f68f37c15..a4e4f2a1e 100644 --- a/typescript/infra/src/config/agent/relayer.ts +++ b/typescript/infra/src/config/agent/relayer.ts @@ -4,11 +4,12 @@ import { AgentConfig, AgentSignerKeyType, ChainMap, + GasPaymentEnforcement, MatchingList, + RelayerConfig as RelayerAgentConfig, chainMetadata, + getDomainId, } from '@hyperlane-xyz/sdk'; -import { GasPaymentEnforcement } from '@hyperlane-xyz/sdk'; -import { RelayerConfig as RelayerAgentConfig } from '@hyperlane-xyz/sdk'; import { ProtocolType } from '@hyperlane-xyz/utils'; import { AgentAwsUser } from '../../agents/aws'; @@ -147,9 +148,9 @@ export function routerMatchingList( } matchingList.push({ - originDomain: chainMetadata[source].chainId, + originDomain: getDomainId(chainMetadata[source]), senderAddress: routers[source].router, - destinationDomain: chainMetadata[destination].chainId, + destinationDomain: getDomainId(chainMetadata[destination]), recipientAddress: routers[destination].router, }); } diff --git a/typescript/infra/src/config/nautilus_token_config.ts b/typescript/infra/src/config/grafana_token_config.ts similarity index 53% rename from typescript/infra/src/config/nautilus_token_config.ts rename to typescript/infra/src/config/grafana_token_config.ts index 3f4eb601f..9d7e6f4c3 100644 --- a/typescript/infra/src/config/nautilus_token_config.ts +++ b/typescript/infra/src/config/grafana_token_config.ts @@ -2,37 +2,53 @@ import { ChainMap, TokenType } from '@hyperlane-xyz/sdk'; import { ProtocolType } from '@hyperlane-xyz/utils'; interface NativeTokenConfig { - chainId: number; symbol: string; name: string; type: TokenType.native; decimals: number; hypNativeAddress: string; - protocolType: ProtocolType.Ethereum | ProtocolType.Sealevel; + protocolType: + | ProtocolType.Ethereum + | ProtocolType.Sealevel + | ProtocolType.Cosmos; } interface CollateralTokenConfig { type: TokenType.collateral; address: string; - chainId: number; decimals: number; symbol: string; name: string; hypCollateralAddress: string; isSpl2022?: boolean; - protocolType: ProtocolType.Ethereum | ProtocolType.Sealevel; + protocolType: + | ProtocolType.Ethereum + | ProtocolType.Sealevel + | ProtocolType.Cosmos; +} + +interface SyntheticTokenConfig { + type: TokenType.synthetic; + hypSyntheticAddress: string; + decimals: number; + symbol: string; + name: string; + protocolType: + | ProtocolType.Ethereum + | ProtocolType.Sealevel + | ProtocolType.Cosmos; } // TODO: migrate and dedupe to SDK from infra and Warp UI export type WarpTokenConfig = ChainMap< - CollateralTokenConfig | NativeTokenConfig + CollateralTokenConfig | NativeTokenConfig | SyntheticTokenConfig >; -export const tokenList: WarpTokenConfig = { +/// nautilus configs +export const nautilusList: WarpTokenConfig = { // bsc bsc: { type: TokenType.collateral, - chainId: 56, address: '0x37a56cdcD83Dce2868f721De58cB3830C44C6303', hypCollateralAddress: '0xC27980812E2E66491FD457D488509b7E04144b98', symbol: 'ZBC', @@ -44,7 +60,6 @@ export const tokenList: WarpTokenConfig = { // nautilus nautilus: { type: TokenType.native, - chainId: 22222, hypNativeAddress: '0x4501bBE6e731A4bC5c60C03A77435b2f6d5e9Fe7', symbol: 'ZBC', name: 'Zebec', @@ -55,7 +70,6 @@ export const tokenList: WarpTokenConfig = { // solana solana: { type: TokenType.collateral, - chainId: 1399811149, address: 'wzbcJyhGhQDLTV1S99apZiiBdE4jmYfbw99saMMdP59', hypCollateralAddress: 'EJqwFjvVJSAxH8Ur2PYuMfdvoJeutjmH6GkoEFQ4MdSa', name: 'Zebec', @@ -65,3 +79,26 @@ export const tokenList: WarpTokenConfig = { protocolType: ProtocolType.Sealevel, }, }; + +/// neutron configs +export const neutronList: WarpTokenConfig = { + neutron: { + type: TokenType.collateral, + address: + 'ibc/773B4D0A3CD667B2275D5A4A7A2F0909C0BA0F4059C0B9181E680DDF4965DCC7', + hypCollateralAddress: + 'neutron1ch7x3xgpnj62weyes8vfada35zff6z59kt2psqhnx9gjnt2ttqdqtva3pa', + name: 'Celestia', + symbol: 'TIA', + decimals: 6, + protocolType: ProtocolType.Cosmos, + }, + mantapacific: { + type: TokenType.synthetic, + hypSyntheticAddress: '0x6Fae4D9935E2fcb11fC79a64e917fb2BF14DaFaa', + name: 'Celestia', + symbol: 'TIA', + decimals: 6, + protocolType: ProtocolType.Ethereum, + }, +}; diff --git a/typescript/sdk/package.json b/typescript/sdk/package.json index 9716ab2af..c2260b291 100644 --- a/typescript/sdk/package.json +++ b/typescript/sdk/package.json @@ -1,16 +1,19 @@ { "name": "@hyperlane-xyz/sdk", "description": "The official SDK for the Hyperlane Network", - "version": "1.5.4-beta0", + "version": "3.1.0-beta0", "dependencies": { - "@hyperlane-xyz/core": "1.5.4-beta0", - "@hyperlane-xyz/utils": "1.5.4-beta0", + "@cosmjs/cosmwasm-stargate": "^0.31.3", + "@cosmjs/stargate": "^0.31.3", + "@hyperlane-xyz/core": "3.1.0-beta0", + "@hyperlane-xyz/utils": "3.1.0-beta0", "@solana/spl-token": "^0.3.8", "@solana/web3.js": "^1.78.0", "@types/coingecko-api": "^1.0.10", "@types/debug": "^4.1.7", "@wagmi/chains": "^0.2.6", "coingecko-api": "^1.0.10", + "cosmjs-types": "^0.9.0", "cross-fetch": "^3.1.5", "debug": "^4.3.4", "ethers": "^5.7.2", diff --git a/typescript/sdk/src/app/MultiProtocolApp.test.ts b/typescript/sdk/src/app/MultiProtocolApp.test.ts index 4239bfa89..68f695ba5 100644 --- a/typescript/sdk/src/app/MultiProtocolApp.test.ts +++ b/typescript/sdk/src/app/MultiProtocolApp.test.ts @@ -7,6 +7,7 @@ import { MultiProtocolProvider } from '../providers/MultiProtocolProvider'; import { BaseAppAdapter, + BaseCosmWasmAdapter, BaseEvmAdapter, BaseSealevelAdapter, MultiProtocolApp, @@ -16,6 +17,7 @@ class TestMultiProtocolApp extends MultiProtocolApp { override protocolToAdapter(protocol: ProtocolType) { if (protocol === ProtocolType.Ethereum) return BaseEvmAdapter; if (protocol === ProtocolType.Sealevel) return BaseSealevelAdapter; + if (protocol === ProtocolType.Cosmos) return BaseCosmWasmAdapter; throw new Error(`No adapter for protocol ${protocol}`); } } diff --git a/typescript/sdk/src/app/MultiProtocolApp.ts b/typescript/sdk/src/app/MultiProtocolApp.ts index 1a027924f..daf841dca 100644 --- a/typescript/sdk/src/app/MultiProtocolApp.ts +++ b/typescript/sdk/src/app/MultiProtocolApp.ts @@ -11,6 +11,8 @@ import { import { ChainMetadata } from '../metadata/chainMetadataTypes'; import { MultiProtocolProvider } from '../providers/MultiProtocolProvider'; import { + CosmJsProvider, + CosmJsWasmProvider, EthersV5Provider, SolanaWeb3Provider, TypedProvider, @@ -49,6 +51,22 @@ export class BaseEvmAdapter extends BaseAppAdapter { } } +export class BaseCosmWasmAdapter extends BaseAppAdapter { + public readonly protocol: ProtocolType = ProtocolType.Cosmos; + + public getProvider(): CosmJsWasmProvider['provider'] { + return this.multiProvider.getCosmJsWasmProvider(this.chainName); + } +} + +export class BaseCosmosAdapter extends BaseAppAdapter { + public readonly protocol: ProtocolType = ProtocolType.Cosmos; + + public getProvider(): CosmJsProvider['provider'] { + return this.multiProvider.getCosmJsProvider(this.chainName); + } +} + export class BaseSealevelAdapter extends BaseAppAdapter { public readonly protocol: ProtocolType = ProtocolType.Sealevel; diff --git a/typescript/sdk/src/consts/chainMetadata.ts b/typescript/sdk/src/consts/chainMetadata.ts index d0c9800ad..1b7d75385 100644 --- a/typescript/sdk/src/consts/chainMetadata.ts +++ b/typescript/sdk/src/consts/chainMetadata.ts @@ -736,6 +736,35 @@ export const proteustestnet: ChainMetadata = { }, }; +export const mantapacific: ChainMetadata = { + chainId: 169, + domainId: 169, + name: Chains.mantapacific, + protocol: ProtocolType.Ethereum, + displayName: 'Manta Pacific', + displayNameShort: 'Manta', + nativeToken: { + name: 'Ether', + symbol: 'ETH', + decimals: 18, + }, + blocks: { + confirmations: 1, + reorgPeriod: 0, + estimateBlockTime: 3, + }, + blockExplorers: [ + { + name: 'Manta Pacific Explorer', + url: 'https://pacific-explorer.manta.network/', + apiUrl: 'https://pacific-explorer.manta.network/api', + family: ExplorerFamily.Blockscout, + }, + ], + rpcUrls: [{ http: 'https://pacific-rpc.manta.network/http' }], + isTestnet: false, +}; + export const nautilus: ChainMetadata = { chainId: 22222, domainId: 22222, @@ -759,6 +788,39 @@ export const nautilus: ChainMetadata = { }, }; +export const neutron: ChainMetadata = { + chainId: 'neutron-1', + domainId: 1853125230, + name: Chains.neutron, + protocol: ProtocolType.Cosmos, + displayName: 'Neutron', + bech32Prefix: 'neutron', + slip44: 118, + nativeToken: { + name: 'Neutron', + symbol: 'NTRN', + decimals: 6, + }, + rpcUrls: [ + { http: 'https://rpc-kralum.neutron-1.neutron.org' }, + { http: 'grpc-kralum.neutron-1.neutron.org:80' }, + ], + blocks: { + confirmations: 1, + reorgPeriod: 0, + estimateBlockTime: 3, + }, + blockExplorers: [ + { + name: 'Mintscan', + url: 'https://www.mintscan.io/neutron', + apiUrl: 'https://www.mintscan.io/neutron', + family: ExplorerFamily.Other, + }, + ], + isTestnet: false, +}; + /** * Metadata for local test chains */ @@ -967,9 +1029,11 @@ export const chainMetadata: ChainMap = { scroll, scrollsepolia, sepolia, + mantapacific, moonbasealpha, moonbeam, mumbai, + neutron, optimism, optimismgoerli, polygon, diff --git a/typescript/sdk/src/consts/chains.ts b/typescript/sdk/src/consts/chains.ts index d7cc8a1ab..b73faf0a0 100644 --- a/typescript/sdk/src/consts/chains.ts +++ b/typescript/sdk/src/consts/chains.ts @@ -21,10 +21,12 @@ export enum Chains { scroll = 'scroll', scrollsepolia = 'scrollsepolia', sepolia = 'sepolia', + mantapacific = 'mantapacific', moonbasealpha = 'moonbasealpha', moonbeam = 'moonbeam', mumbai = 'mumbai', nautilus = 'nautilus', + neutron = 'neutron', optimism = 'optimism', optimismgoerli = 'optimismgoerli', polygon = 'polygon', @@ -57,6 +59,8 @@ export const Mainnets: Array = [ Chains.bsc, Chains.celo, Chains.ethereum, + Chains.neutron, + Chains.mantapacific, Chains.moonbeam, Chains.optimism, Chains.polygon, diff --git a/typescript/sdk/src/core/MultiProtocolCore.ts b/typescript/sdk/src/core/MultiProtocolCore.ts index 971394f54..4f5cca13b 100644 --- a/typescript/sdk/src/core/MultiProtocolCore.ts +++ b/typescript/sdk/src/core/MultiProtocolCore.ts @@ -11,6 +11,7 @@ import { MultiProtocolProvider } from '../providers/MultiProtocolProvider'; import { TypedTransactionReceipt } from '../providers/ProviderType'; import { ChainMap, ChainName } from '../types'; +import { CosmWasmCoreAdapter } from './adapters/CosmWasmCoreAdapter'; import { EvmCoreAdapter } from './adapters/EvmCoreAdapter'; import { SealevelCoreAdapter } from './adapters/SealevelCoreAdapter'; import { ICoreAdapter } from './adapters/types'; @@ -54,6 +55,7 @@ export class MultiProtocolCore extends MultiProtocolApp< ): AdapterClassType { if (protocol === ProtocolType.Ethereum) return EvmCoreAdapter; if (protocol === ProtocolType.Sealevel) return SealevelCoreAdapter; + if (protocol === ProtocolType.Cosmos) return CosmWasmCoreAdapter; throw new Error(`No adapter for protocol ${protocol}`); } diff --git a/typescript/sdk/src/core/adapters/CosmWasmCoreAdapter.ts b/typescript/sdk/src/core/adapters/CosmWasmCoreAdapter.ts new file mode 100644 index 000000000..2046bcc93 --- /dev/null +++ b/typescript/sdk/src/core/adapters/CosmWasmCoreAdapter.ts @@ -0,0 +1,184 @@ +import { ExecuteInstruction } from '@cosmjs/cosmwasm-stargate'; + +import { Address, HexString } from '@hyperlane-xyz/utils'; + +import { BaseCosmWasmAdapter } from '../../app/MultiProtocolApp'; +import { + Coin, + DefaultHookResponse, + DefaultIsmResponse, + ExecuteMsg, + LatestDispatchedIdResponse, + MessageDeliveredResponse, + NonceResponse, + OwnerResponse, + QueryMsg, + RequiredHookResponse, +} from '../../cw-types/Mailbox.types'; +import { MultiProtocolProvider } from '../../providers/MultiProtocolProvider'; +import { + ProviderType, + TypedTransactionReceipt, +} from '../../providers/ProviderType'; +import { ChainName } from '../../types'; + +import { ICoreAdapter } from './types'; + +type MailboxResponse = + | DefaultHookResponse + | RequiredHookResponse + | DefaultIsmResponse + | NonceResponse + | LatestDispatchedIdResponse + | OwnerResponse + | MessageDeliveredResponse; + +export class CosmWasmCoreAdapter + extends BaseCosmWasmAdapter + implements ICoreAdapter +{ + constructor( + public readonly chainName: ChainName, + public readonly multiProvider: MultiProtocolProvider, + public readonly addresses: { mailbox: Address }, + ) { + super(chainName, multiProvider, addresses); + } + + prepareMailbox(msg: ExecuteMsg, funds?: Coin[]): ExecuteInstruction { + return { + contractAddress: this.addresses.mailbox, + msg, + funds, + }; + } + + initTransferOwner(newOwner: Address): ExecuteInstruction { + return this.prepareMailbox({ + ownable: { + init_ownership_transfer: { + next_owner: newOwner, + }, + }, + }); + } + + claimTransferOwner(): ExecuteInstruction { + return this.prepareMailbox({ + ownable: { + claim_ownership: {}, + }, + }); + } + + setDefaultHook(address: Address): ExecuteInstruction { + return this.prepareMailbox({ + set_default_hook: { + hook: address, + }, + }); + } + + setRequiredHook(address: Address): ExecuteInstruction { + return this.prepareMailbox({ + set_required_hook: { + hook: address, + }, + }); + } + + async queryMailbox(msg: QueryMsg): Promise { + const provider = await this.getProvider(); + const response: R = await provider.queryContractSmart( + this.addresses.mailbox, + msg, + ); + return response; + } + + async defaultHook(): Promise { + const response = await this.queryMailbox({ + mailbox: { + default_hook: {}, + }, + }); + return response.default_hook; + } + + async defaultIsm(): Promise { + const response = await this.queryMailbox({ + mailbox: { + default_ism: {}, + }, + }); + return response.default_ism; + } + + async requiredHook(): Promise { + const response = await this.queryMailbox({ + mailbox: { + required_hook: {}, + }, + }); + return response.required_hook; + } + + async nonce(): Promise { + const response = await this.queryMailbox({ + mailbox: { + nonce: {}, + }, + }); + return response.nonce; + } + + async latestDispatchedId(): Promise { + const response = await this.queryMailbox({ + mailbox: { + latest_dispatch_id: {}, + }, + }); + return response.message_id; + } + + async owner(): Promise { + const response = await this.queryMailbox({ + ownable: { + get_owner: {}, + }, + }); + return response.owner; + } + + async delivered(id: string): Promise { + const response = await this.queryMailbox({ + mailbox: { + message_delivered: { + id, + }, + }, + }); + return response.delivered; + } + + extractMessageIds( + sourceTx: TypedTransactionReceipt, + ): Array<{ messageId: string; destination: ChainName }> { + if (sourceTx.type !== ProviderType.CosmJsWasm) { + throw new Error( + `Unsupported provider type for CosmosCoreAdapter ${sourceTx.type}`, + ); + } + // TODO: parse mailbox logs and extract message ids + throw new Error('Method not implemented.'); + } + + async waitForMessageProcessed( + _messageId: HexString, + _destination: ChainName, + _delayMs?: number, + _maxAttempts?: number, + ): Promise { + throw new Error('Method not implemented.'); + } +} diff --git a/typescript/sdk/src/core/adapters/CosmWasmIgpAdapter.ts b/typescript/sdk/src/core/adapters/CosmWasmIgpAdapter.ts new file mode 100644 index 000000000..1925e62aa --- /dev/null +++ b/typescript/sdk/src/core/adapters/CosmWasmIgpAdapter.ts @@ -0,0 +1,142 @@ +import { ExecuteInstruction } from '@cosmjs/cosmwasm-stargate'; + +import { Address } from '@hyperlane-xyz/utils'; + +import { BaseCosmWasmAdapter } from '../../app/MultiProtocolApp'; +import { + BeneficiaryResponse, + DefaultGasResponse, + DomainsResponse, + GetExchangeRateAndGasPriceResponse, + OwnerResponse, + QueryMsg, + QuoteGasPaymentResponse, + RouteResponseForAddr, + RoutesResponseForAddr, +} from '../../cw-types/Igp.types'; +import { MultiProtocolProvider } from '../../providers/MultiProtocolProvider'; +import { ChainMap, ChainName } from '../../types'; + +// TODO: import more +type IgpResponse = + | OwnerResponse + | BeneficiaryResponse + | DomainsResponse + | GetExchangeRateAndGasPriceResponse + | RoutesResponseForAddr + | RouteResponseForAddr + | DefaultGasResponse + | QuoteGasPaymentResponse; + +export class CosmWasmIgpAdapter extends BaseCosmWasmAdapter { + constructor( + public readonly chainName: ChainName, + public readonly multiProvider: MultiProtocolProvider, + public readonly addresses: { igp: Address }, + ) { + super(chainName, multiProvider, addresses); + } + + async queryIgp(msg: QueryMsg): Promise { + const provider = await this.getProvider(); + const response: R = await provider.queryContractSmart( + this.addresses.igp, + msg, + ); + return response; + } + + async owner(): Promise { + const response = await this.queryIgp({ + ownable: { + get_owner: {}, + }, + }); + return response.owner; + } + + async beneficiary(): Promise { + const beneficiaryResponse: BeneficiaryResponse = await this.queryIgp({ + igp: { + beneficiary: {}, + }, + }); + return beneficiaryResponse.beneficiary; + } + + async getOracles(): Promise> { + const domainResponse: RoutesResponseForAddr = await this.queryIgp({ + router: { + list_routes: {}, + }, + }); + + return Object.fromEntries( + domainResponse.routes.map((_) => [ + this.multiProvider.getChainName(_.domain), + _.route ?? '', + ]), + ); + } + + async defaultGas(): Promise { + const defaultGas = await this.queryIgp({ + igp: { + default_gas: {}, + }, + }); + return defaultGas.gas; + } + + async getOracleData( + chain: ChainName, + ): Promise { + const provider = await this.getProvider(); + const domain = this.multiProvider.getDomainId(chain); + const oracles = await this.getOracles(); + const oracle = oracles[chain]; + const oracleResponse: GetExchangeRateAndGasPriceResponse = + await provider.queryContractSmart(oracle, { + oracle: { + get_exchange_rate_and_gas_price: { + dest_domain: domain, + }, + }, + }); + return oracleResponse; + } + + async quoteGasPayment( + domain: number, + destinationGasAmount: number, + ): Promise { + const quote: QuoteGasPaymentResponse = await this.queryIgp({ + igp: { + quote_gas_payment: { + dest_domain: domain, + gas_amount: destinationGasAmount.toString(), + }, + }, + }); + return Number(quote.gas_needed); + } + + setOracleForDomain( + domain: number, + oracle: string, + oracleData: GetExchangeRateAndGasPriceResponse, + ): ExecuteInstruction { + return { + contractAddress: oracle, + msg: { + set_remote_gas_data: { + config: { + gas_price: oracleData.gas_price, + token_exchange_rate: oracleData.exchange_rate, + remote_domain: domain, + }, + }, + }, + }; + } +} diff --git a/typescript/sdk/src/cw-types/Cw20Base.types.ts b/typescript/sdk/src/cw-types/Cw20Base.types.ts new file mode 100644 index 000000000..f8b45f50b --- /dev/null +++ b/typescript/sdk/src/cw-types/Cw20Base.types.ts @@ -0,0 +1,228 @@ +/** + * This file was automatically generated by @cosmwasm/ts-codegen@0.35.3. + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, + * and run the @cosmwasm/ts-codegen generate command to regenerate this file. + */ + +export type Uint128 = string; +export type Logo = + | { + url: string; + } + | { + embedded: EmbeddedLogo; + }; +export type EmbeddedLogo = + | { + svg: Binary; + } + | { + png: Binary; + }; +export type Binary = string; +export interface InstantiateMsg { + decimals: number; + initial_balances: Cw20Coin[]; + marketing?: InstantiateMarketingInfo | null; + mint?: MinterResponse | null; + name: string; + symbol: string; +} +export interface Cw20Coin { + address: string; + amount: Uint128; +} +export interface InstantiateMarketingInfo { + description?: string | null; + logo?: Logo | null; + marketing?: string | null; + project?: string | null; +} +export interface MinterResponse { + cap?: Uint128 | null; + minter: string; +} +export type ExecuteMsg = + | { + transfer: { + amount: Uint128; + recipient: string; + }; + } + | { + burn: { + amount: Uint128; + }; + } + | { + send: { + amount: Uint128; + contract: string; + msg: Binary; + }; + } + | { + increase_allowance: { + amount: Uint128; + expires?: Expiration | null; + spender: string; + }; + } + | { + decrease_allowance: { + amount: Uint128; + expires?: Expiration | null; + spender: string; + }; + } + | { + transfer_from: { + amount: Uint128; + owner: string; + recipient: string; + }; + } + | { + send_from: { + amount: Uint128; + contract: string; + msg: Binary; + owner: string; + }; + } + | { + burn_from: { + amount: Uint128; + owner: string; + }; + } + | { + mint: { + amount: Uint128; + recipient: string; + }; + } + | { + update_minter: { + new_minter?: string | null; + }; + } + | { + update_marketing: { + description?: string | null; + marketing?: string | null; + project?: string | null; + }; + } + | { + upload_logo: Logo; + }; +export type Expiration = + | { + at_height: number; + } + | { + at_time: Timestamp; + } + | { + never: {}; + }; +export type Timestamp = Uint64; +export type Uint64 = string; +export type QueryMsg = + | { + balance: { + address: string; + }; + } + | { + token_info: {}; + } + | { + minter: {}; + } + | { + allowance: { + owner: string; + spender: string; + }; + } + | { + all_allowances: { + limit?: number | null; + owner: string; + start_after?: string | null; + }; + } + | { + all_spender_allowances: { + limit?: number | null; + spender: string; + start_after?: string | null; + }; + } + | { + all_accounts: { + limit?: number | null; + start_after?: string | null; + }; + } + | { + marketing_info: {}; + } + | { + download_logo: {}; + }; +export interface AllAccountsResponse { + accounts: string[]; + [k: string]: unknown; +} +export interface AllAllowancesResponse { + allowances: AllowanceInfo[]; + [k: string]: unknown; +} +export interface AllowanceInfo { + allowance: Uint128; + expires: Expiration; + spender: string; +} +export interface AllSpenderAllowancesResponse { + allowances: SpenderAllowanceInfo[]; + [k: string]: unknown; +} +export interface SpenderAllowanceInfo { + allowance: Uint128; + expires: Expiration; + owner: string; +} +export interface AllowanceResponse { + allowance: Uint128; + expires: Expiration; + [k: string]: unknown; +} +export interface BalanceResponse { + balance: Uint128; +} +export interface DownloadLogoResponse { + data: Binary; + mime_type: string; +} +export type LogoInfo = + | { + url: string; + } + | 'embedded'; +export type Addr = string; +export interface MarketingInfoResponse { + description?: string | null; + logo?: LogoInfo | null; + marketing?: Addr | null; + project?: string | null; + [k: string]: unknown; +} +export interface TokenInfoResponse { + decimals: number; + name: string; + symbol: string; + total_supply: Uint128; +} diff --git a/typescript/sdk/src/cw-types/HookAggregate.types.ts b/typescript/sdk/src/cw-types/HookAggregate.types.ts new file mode 100644 index 000000000..e2aaea12e --- /dev/null +++ b/typescript/sdk/src/cw-types/HookAggregate.types.ts @@ -0,0 +1,92 @@ +/** + * This file was automatically generated by @cosmwasm/ts-codegen@0.35.3. + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, + * and run the @cosmwasm/ts-codegen generate command to regenerate this file. + */ + +export interface InstantiateMsg { + hooks: string[]; + owner: string; +} +export type ExecuteMsg = + | { + ownable: OwnableMsg; + } + | { + post_dispatch: PostDispatchMsg; + } + | { + set_hooks: { + hooks: string[]; + }; + }; +export type OwnableMsg = + | { + init_ownership_transfer: { + next_owner: string; + }; + } + | { + revoke_ownership_transfer: {}; + } + | { + claim_ownership: {}; + }; +export type HexBinary = string; +export interface PostDispatchMsg { + message: HexBinary; + metadata: HexBinary; +} +export type QueryMsg = + | { + ownable: OwnableQueryMsg; + } + | { + hook: HookQueryMsg; + } + | { + aggregate_hook: AggregateHookQueryMsg; + }; +export type OwnableQueryMsg = + | { + get_owner: {}; + } + | { + get_pending_owner: {}; + }; +export type HookQueryMsg = + | { + quote_dispatch: QuoteDispatchMsg; + } + | { + mailbox: {}; + }; +export type AggregateHookQueryMsg = { + hooks: {}; +}; +export interface QuoteDispatchMsg { + message: HexBinary; + metadata: HexBinary; +} +export type Addr = string; +export interface OwnerResponse { + owner: Addr; +} +export interface PendingOwnerResponse { + pending_owner?: Addr | null; +} +export interface HooksResponse { + hooks: string[]; +} +export interface MailboxResponse { + mailbox: string; +} +export type Uint128 = string; +export interface QuoteDispatchResponse { + gas_amount?: Coin | null; +} +export interface Coin { + amount: Uint128; + denom: string; + [k: string]: unknown; +} diff --git a/typescript/sdk/src/cw-types/HookMerkle.types.ts b/typescript/sdk/src/cw-types/HookMerkle.types.ts new file mode 100644 index 000000000..32765e303 --- /dev/null +++ b/typescript/sdk/src/cw-types/HookMerkle.types.ts @@ -0,0 +1,180 @@ +/** + * This file was automatically generated by @cosmwasm/ts-codegen@0.35.3. + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, + * and run the @cosmwasm/ts-codegen generate command to regenerate this file. + */ + +export interface InstantiateMsg { + mailbox: string; + owner: string; +} +export type ExecuteMsg = + | { + ownable: OwnableMsg; + } + | { + post_dispatch: PostDispatchMsg; + }; +export type OwnableMsg = + | { + init_ownership_transfer: { + next_owner: string; + }; + } + | { + revoke_ownership_transfer: {}; + } + | { + claim_ownership: {}; + }; +export type HexBinary = string; +export interface PostDispatchMsg { + message: HexBinary; + metadata: HexBinary; +} +export type QueryMsg = + | { + ownable: OwnableQueryMsg; + } + | { + hook: HookQueryMsg; + } + | { + merkle_hook: MerkleHookQueryMsg; + }; +export type OwnableQueryMsg = + | { + get_owner: {}; + } + | { + get_pending_owner: {}; + }; +export type HookQueryMsg = + | { + quote_dispatch: QuoteDispatchMsg; + } + | { + mailbox: {}; + }; +export type MerkleHookQueryMsg = + | { + count: {}; + } + | { + root: {}; + } + | { + branch: {}; + } + | { + tree: {}; + } + | { + check_point: {}; + }; +export interface QuoteDispatchMsg { + message: HexBinary; + metadata: HexBinary; +} +export interface BranchResponse { + branch: [ + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + ]; +} +export interface CheckPointResponse { + count: number; + root: HexBinary; +} +export interface CountResponse { + count: number; +} +export type Addr = string; +export interface OwnerResponse { + owner: Addr; +} +export interface PendingOwnerResponse { + pending_owner?: Addr | null; +} +export interface MailboxResponse { + mailbox: string; +} +export type Uint128 = string; +export interface QuoteDispatchResponse { + gas_amount?: Coin | null; +} +export interface Coin { + amount: Uint128; + denom: string; + [k: string]: unknown; +} +export interface RootResponse { + root: HexBinary; +} +export interface TreeResponse { + branch: [ + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + HexBinary, + ]; + count: number; +} diff --git a/typescript/sdk/src/cw-types/HookPausable.types.ts b/typescript/sdk/src/cw-types/HookPausable.types.ts new file mode 100644 index 000000000..918a462f2 --- /dev/null +++ b/typescript/sdk/src/cw-types/HookPausable.types.ts @@ -0,0 +1,97 @@ +/** + * This file was automatically generated by @cosmwasm/ts-codegen@0.35.3. + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, + * and run the @cosmwasm/ts-codegen generate command to regenerate this file. + */ + +export interface InstantiateMsg { + owner: string; + paused: boolean; +} +export type ExecuteMsg = + | { + ownable: OwnableMsg; + } + | { + pausable: PausableMsg; + } + | { + post_dispatch: PostDispatchMsg; + }; +export type OwnableMsg = + | { + init_ownership_transfer: { + next_owner: string; + }; + } + | { + revoke_ownership_transfer: {}; + } + | { + claim_ownership: {}; + }; +export type PausableMsg = + | { + pause: {}; + } + | { + release: {}; + }; +export type HexBinary = string; +export interface PostDispatchMsg { + message: HexBinary; + metadata: HexBinary; +} +export type QueryMsg = + | { + pausable: PausableQueryMsg; + } + | { + ownable: OwnableQueryMsg; + } + | { + hook: HookQueryMsg; + }; +export type PausableQueryMsg = { + pause_info: {}; +}; +export type OwnableQueryMsg = + | { + get_owner: {}; + } + | { + get_pending_owner: {}; + }; +export type HookQueryMsg = + | { + quote_dispatch: QuoteDispatchMsg; + } + | { + mailbox: {}; + }; +export interface QuoteDispatchMsg { + message: HexBinary; + metadata: HexBinary; +} +export type Addr = string; +export interface OwnerResponse { + owner: Addr; +} +export interface PendingOwnerResponse { + pending_owner?: Addr | null; +} +export interface MailboxResponse { + mailbox: string; +} +export interface PauseInfoResponse { + paused: boolean; +} +export type Uint128 = string; +export interface QuoteDispatchResponse { + gas_amount?: Coin | null; +} +export interface Coin { + amount: Uint128; + denom: string; + [k: string]: unknown; +} diff --git a/typescript/sdk/src/cw-types/HookRouting.types.ts b/typescript/sdk/src/cw-types/HookRouting.types.ts new file mode 100644 index 000000000..564a01077 --- /dev/null +++ b/typescript/sdk/src/cw-types/HookRouting.types.ts @@ -0,0 +1,127 @@ +/** + * This file was automatically generated by @cosmwasm/ts-codegen@0.35.3. + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, + * and run the @cosmwasm/ts-codegen generate command to regenerate this file. + */ + +export interface InstantiateMsg { + owner: string; +} +export type ExecuteMsg = + | { + ownable: OwnableMsg; + } + | { + post_dispatch: PostDispatchMsg; + } + | { + router: RouterMsgForAddr; + }; +export type OwnableMsg = + | { + init_ownership_transfer: { + next_owner: string; + }; + } + | { + revoke_ownership_transfer: {}; + } + | { + claim_ownership: {}; + }; +export type HexBinary = string; +export type RouterMsgForAddr = + | { + set_route: { + set: DomainRouteSetForAddr; + }; + } + | { + set_routes: { + set: DomainRouteSetForAddr[]; + }; + }; +export type Addr = string; +export interface PostDispatchMsg { + message: HexBinary; + metadata: HexBinary; +} +export interface DomainRouteSetForAddr { + domain: number; + route?: Addr | null; +} +export type QueryMsg = + | { + ownable: OwnableQueryMsg; + } + | { + router: RouterQueryForAddr; + } + | { + hook: HookQueryMsg; + }; +export type OwnableQueryMsg = + | { + get_owner: {}; + } + | { + get_pending_owner: {}; + }; +export type RouterQueryForAddr = + | { + domains: {}; + } + | { + get_route: { + domain: number; + }; + } + | { + list_routes: { + limit?: number | null; + offset?: number | null; + order?: Order | null; + }; + }; +export type Order = 'asc' | 'desc'; +export type HookQueryMsg = + | { + quote_dispatch: QuoteDispatchMsg; + } + | { + mailbox: {}; + }; +export interface QuoteDispatchMsg { + message: HexBinary; + metadata: HexBinary; +} +export interface DomainsResponse { + domains: number[]; +} +export interface OwnerResponse { + owner: Addr; +} +export interface PendingOwnerResponse { + pending_owner?: Addr | null; +} +export interface RouteResponseForAddr { + route: DomainRouteSetForAddr; +} +export interface RoutesResponseForAddr { + routes: DomainRouteSetForAddr[]; +} +export interface MailboxResponse { + mailbox: string; +} +export interface Empty { + [k: string]: unknown; +} +export type Uint128 = string; +export interface QuoteDispatchResponse { + gas_amount?: Coin | null; +} +export interface Coin { + amount: Uint128; + denom: string; + [k: string]: unknown; +} diff --git a/typescript/sdk/src/cw-types/HookRoutingCustom.types.ts b/typescript/sdk/src/cw-types/HookRoutingCustom.types.ts new file mode 100644 index 000000000..ad8ecfdda --- /dev/null +++ b/typescript/sdk/src/cw-types/HookRoutingCustom.types.ts @@ -0,0 +1,174 @@ +/** + * This file was automatically generated by @cosmwasm/ts-codegen@0.35.3. + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, + * and run the @cosmwasm/ts-codegen generate command to regenerate this file. + */ + +export interface InstantiateMsg { + owner: string; +} +export type ExecuteMsg = + | { + ownable: OwnableMsg; + } + | { + post_dispatch: PostDispatchMsg; + } + | { + router: RouterMsgForAddr; + } + | { + register_custom_hook: RegisterCustomHookMsg; + } + | { + register_custom_hooks: RegisterCustomHookMsg[]; + } + | { + clear_custom_hook: ClearCustomHookMsg; + } + | { + clear_custom_hooks: ClearCustomHookMsg[]; + }; +export type OwnableMsg = + | { + init_ownership_transfer: { + next_owner: string; + }; + } + | { + revoke_ownership_transfer: {}; + } + | { + claim_ownership: {}; + }; +export type HexBinary = string; +export type RouterMsgForAddr = + | { + set_route: { + set: DomainRouteSetForAddr; + }; + } + | { + set_routes: { + set: DomainRouteSetForAddr[]; + }; + }; +export type Addr = string; +export interface PostDispatchMsg { + message: HexBinary; + metadata: HexBinary; +} +export interface DomainRouteSetForAddr { + domain: number; + route?: Addr | null; +} +export interface RegisterCustomHookMsg { + dest_domain: number; + hook: string; + recipient: string; +} +export interface ClearCustomHookMsg { + dest_domain: number; + recipient: string; +} +export type QueryMsg = + | { + ownable: OwnableQueryMsg; + } + | { + router: RouterQueryForAddr; + } + | { + hook: HookQueryMsg; + } + | { + custom_routing_hook: CustomRoutingHookQueryMsg; + }; +export type OwnableQueryMsg = + | { + get_owner: {}; + } + | { + get_pending_owner: {}; + }; +export type RouterQueryForAddr = + | { + domains: {}; + } + | { + get_route: { + domain: number; + }; + } + | { + list_routes: { + limit?: number | null; + offset?: number | null; + order?: Order | null; + }; + }; +export type Order = 'asc' | 'desc'; +export type HookQueryMsg = + | { + quote_dispatch: QuoteDispatchMsg; + } + | { + mailbox: {}; + }; +export type CustomRoutingHookQueryMsg = + | { + custom_hook: { + dest_domain: number; + recipient: string; + }; + } + | { + custom_hooks: { + dest_domain: number; + limit?: number | null; + offset?: string | null; + order?: Order | null; + }; + }; +export interface QuoteDispatchMsg { + message: HexBinary; + metadata: HexBinary; +} +export interface CustomHookResponse { + dest_domain: number; + hook: string; + recipient: string; +} +export interface CustomHooksResponse { + custom_hooks: CustomHookResponse[]; +} +export interface DomainsResponse { + domains: number[]; +} +export interface OwnerResponse { + owner: Addr; +} +export interface PendingOwnerResponse { + pending_owner?: Addr | null; +} +export interface RouteResponseForAddr { + route: DomainRouteSetForAddr; +} +export interface RoutesResponseForAddr { + routes: DomainRouteSetForAddr[]; +} +export interface MailboxResponse { + mailbox: string; +} +export interface Empty { + [k: string]: unknown; +} +export type Uint128 = string; +export interface QuoteDispatchResponse { + gas_amount?: Coin | null; +} +export interface Coin { + amount: Uint128; + denom: string; + [k: string]: unknown; +} diff --git a/typescript/sdk/src/cw-types/HookRoutingFallback.types.ts b/typescript/sdk/src/cw-types/HookRoutingFallback.types.ts new file mode 100644 index 000000000..93f9bd088 --- /dev/null +++ b/typescript/sdk/src/cw-types/HookRoutingFallback.types.ts @@ -0,0 +1,132 @@ +/** + * This file was automatically generated by @cosmwasm/ts-codegen@0.35.3. + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, + * and run the @cosmwasm/ts-codegen generate command to regenerate this file. + */ + +export interface InstantiateMsg { + owner: string; +} +export type ExecuteMsg = + | { + ownable: OwnableMsg; + } + | { + post_dispatch: PostDispatchMsg; + } + | { + router: RouterMsgForAddr; + } + | { + set_fallback_hook: { + hook: string; + }; + }; +export type OwnableMsg = + | { + init_ownership_transfer: { + next_owner: string; + }; + } + | { + revoke_ownership_transfer: {}; + } + | { + claim_ownership: {}; + }; +export type HexBinary = string; +export type RouterMsgForAddr = + | { + set_route: { + set: DomainRouteSetForAddr; + }; + } + | { + set_routes: { + set: DomainRouteSetForAddr[]; + }; + }; +export type Addr = string; +export interface PostDispatchMsg { + message: HexBinary; + metadata: HexBinary; +} +export interface DomainRouteSetForAddr { + domain: number; + route?: Addr | null; +} +export type QueryMsg = + | { + ownable: OwnableQueryMsg; + } + | { + router: RouterQueryForAddr; + } + | { + hook: HookQueryMsg; + }; +export type OwnableQueryMsg = + | { + get_owner: {}; + } + | { + get_pending_owner: {}; + }; +export type RouterQueryForAddr = + | { + domains: {}; + } + | { + get_route: { + domain: number; + }; + } + | { + list_routes: { + limit?: number | null; + offset?: number | null; + order?: Order | null; + }; + }; +export type Order = 'asc' | 'desc'; +export type HookQueryMsg = + | { + quote_dispatch: QuoteDispatchMsg; + } + | { + mailbox: {}; + }; +export interface QuoteDispatchMsg { + message: HexBinary; + metadata: HexBinary; +} +export interface DomainsResponse { + domains: number[]; +} +export interface OwnerResponse { + owner: Addr; +} +export interface PendingOwnerResponse { + pending_owner?: Addr | null; +} +export interface RouteResponseForAddr { + route: DomainRouteSetForAddr; +} +export interface RoutesResponseForAddr { + routes: DomainRouteSetForAddr[]; +} +export interface MailboxResponse { + mailbox: string; +} +export interface Empty { + [k: string]: unknown; +} +export type Uint128 = string; +export interface QuoteDispatchResponse { + gas_amount?: Coin | null; +} +export interface Coin { + amount: Uint128; + denom: string; + [k: string]: unknown; +} diff --git a/typescript/sdk/src/cw-types/Igp.types.ts b/typescript/sdk/src/cw-types/Igp.types.ts new file mode 100644 index 000000000..091e381d7 --- /dev/null +++ b/typescript/sdk/src/cw-types/Igp.types.ts @@ -0,0 +1,215 @@ +/** + * This file was automatically generated by @cosmwasm/ts-codegen@0.35.3. + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, + * and run the @cosmwasm/ts-codegen generate command to regenerate this file. + */ + +export interface InstantiateMsg { + beneficiary: string; + default_gas_usage: number; + gas_token: string; + hrp: string; + owner: string; +} +export type ExecuteMsg = + | { + ownable: OwnableMsg; + } + | { + router: RouterMsgForAddr; + } + | { + post_dispatch: PostDispatchMsg; + } + | { + set_default_gas: { + gas: number; + }; + } + | { + set_gas_for_domain: { + config: [number, number][]; + }; + } + | { + unset_gas_for_domain: { + domains: number[]; + }; + } + | { + set_beneficiary: { + beneficiary: string; + }; + } + | { + pay_for_gas: { + dest_domain: number; + gas_amount: Uint256; + message_id: HexBinary; + refund_address: string; + }; + } + | { + claim: {}; + }; +export type OwnableMsg = + | { + init_ownership_transfer: { + next_owner: string; + }; + } + | { + revoke_ownership_transfer: {}; + } + | { + claim_ownership: {}; + }; +export type RouterMsgForAddr = + | { + set_route: { + set: DomainRouteSetForAddr; + }; + } + | { + set_routes: { + set: DomainRouteSetForAddr[]; + }; + }; +export type Addr = string; +export type HexBinary = string; +export type Uint256 = string; +export interface DomainRouteSetForAddr { + domain: number; + route?: Addr | null; +} +export interface PostDispatchMsg { + message: HexBinary; + metadata: HexBinary; +} +export type QueryMsg = + | { + ownable: OwnableQueryMsg; + } + | { + hook: HookQueryMsg; + } + | { + router: RouterQueryForAddr; + } + | { + oracle: IgpGasOracleQueryMsg; + } + | { + igp: IgpQueryMsg; + }; +export type OwnableQueryMsg = + | { + get_owner: {}; + } + | { + get_pending_owner: {}; + }; +export type HookQueryMsg = + | { + quote_dispatch: QuoteDispatchMsg; + } + | { + mailbox: {}; + }; +export type RouterQueryForAddr = + | { + domains: {}; + } + | { + get_route: { + domain: number; + }; + } + | { + list_routes: { + limit?: number | null; + offset?: number | null; + order?: Order | null; + }; + }; +export type Order = 'asc' | 'desc'; +export type IgpGasOracleQueryMsg = { + get_exchange_rate_and_gas_price: { + dest_domain: number; + }; +}; +export type IgpQueryMsg = + | { + default_gas: {}; + } + | { + gas_for_domain: { + domains: number[]; + }; + } + | { + list_gas_for_domains: { + limit?: number | null; + offset?: number | null; + order?: Order | null; + }; + } + | { + beneficiary: {}; + } + | { + quote_gas_payment: { + dest_domain: number; + gas_amount: Uint256; + }; + }; +export interface QuoteDispatchMsg { + message: HexBinary; + metadata: HexBinary; +} +export interface BeneficiaryResponse { + beneficiary: string; +} +export interface DefaultGasResponse { + gas: number; +} +export interface DomainsResponse { + domains: number[]; +} +export interface GasForDomainResponse { + gas: [number, number][]; +} +export type Uint128 = string; +export interface GetExchangeRateAndGasPriceResponse { + exchange_rate: Uint128; + gas_price: Uint128; +} +export interface OwnerResponse { + owner: Addr; +} +export interface PendingOwnerResponse { + pending_owner?: Addr | null; +} +export interface RouteResponseForAddr { + route: DomainRouteSetForAddr; +} +export interface RoutesResponseForAddr { + routes: DomainRouteSetForAddr[]; +} +export interface MailboxResponse { + mailbox: string; +} +export interface Empty { + [k: string]: unknown; +} +export interface QuoteDispatchResponse { + gas_amount?: Coin | null; +} +export interface Coin { + amount: Uint128; + denom: string; + [k: string]: unknown; +} +export interface QuoteGasPaymentResponse { + gas_needed: Uint256; +} diff --git a/typescript/sdk/src/cw-types/IgpOracle.types.ts b/typescript/sdk/src/cw-types/IgpOracle.types.ts new file mode 100644 index 000000000..2a9255f7d --- /dev/null +++ b/typescript/sdk/src/cw-types/IgpOracle.types.ts @@ -0,0 +1,71 @@ +/** + * This file was automatically generated by @cosmwasm/ts-codegen@0.35.3. + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, + * and run the @cosmwasm/ts-codegen generate command to regenerate this file. + */ + +export interface InstantiateMsg { + owner: string; +} +export type ExecuteMsg = + | { + ownership: OwnableMsg; + } + | { + set_remote_gas_data_configs: { + configs: RemoteGasDataConfig[]; + }; + } + | { + set_remote_gas_data: { + config: RemoteGasDataConfig; + }; + }; +export type OwnableMsg = + | { + init_ownership_transfer: { + next_owner: string; + }; + } + | { + revoke_ownership_transfer: {}; + } + | { + claim_ownership: {}; + }; +export type Uint128 = string; +export interface RemoteGasDataConfig { + gas_price: Uint128; + remote_domain: number; + token_exchange_rate: Uint128; +} +export type QueryMsg = + | { + ownable: OwnableQueryMsg; + } + | { + oracle: IgpGasOracleQueryMsg; + }; +export type OwnableQueryMsg = + | { + get_owner: {}; + } + | { + get_pending_owner: {}; + }; +export type IgpGasOracleQueryMsg = { + get_exchange_rate_and_gas_price: { + dest_domain: number; + }; +}; +export interface GetExchangeRateAndGasPriceResponse { + exchange_rate: Uint128; + gas_price: Uint128; +} +export type Addr = string; +export interface OwnerResponse { + owner: Addr; +} +export interface PendingOwnerResponse { + pending_owner?: Addr | null; +} diff --git a/typescript/sdk/src/cw-types/IsmAggregate.types.ts b/typescript/sdk/src/cw-types/IsmAggregate.types.ts new file mode 100644 index 000000000..f57d20b54 --- /dev/null +++ b/typescript/sdk/src/cw-types/IsmAggregate.types.ts @@ -0,0 +1,97 @@ +/** + * This file was automatically generated by @cosmwasm/ts-codegen@0.35.3. + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, + * and run the @cosmwasm/ts-codegen generate command to regenerate this file. + */ + +export interface InstantiateMsg { + isms: string[]; + owner: string; + threshold: number; +} +export type ExecuteMsg = + | { + ownable: OwnableMsg; + } + | { + set_isms: { + isms: string[]; + }; + }; +export type OwnableMsg = + | { + init_ownership_transfer: { + next_owner: string; + }; + } + | { + revoke_ownership_transfer: {}; + } + | { + claim_ownership: {}; + }; +export type QueryMsg = + | { + ownable: OwnableQueryMsg; + } + | { + ism: IsmQueryMsg; + } + | { + aggregate_ism: AggregateIsmQueryMsg; + }; +export type OwnableQueryMsg = + | { + get_owner: {}; + } + | { + get_pending_owner: {}; + }; +export type IsmQueryMsg = + | { + module_type: {}; + } + | { + verify: { + message: HexBinary; + metadata: HexBinary; + }; + } + | { + verify_info: { + message: HexBinary; + }; + }; +export type HexBinary = string; +export type AggregateIsmQueryMsg = { + isms: {}; +}; +export type Addr = string; +export interface OwnerResponse { + owner: Addr; +} +export interface PendingOwnerResponse { + pending_owner?: Addr | null; +} +export interface IsmsResponse { + isms: string[]; +} +export type IsmType = + | 'unused' + | 'routing' + | 'aggregation' + | 'legacy_multisig' + | 'merkle_root_multisig' + | 'message_id_multisig' + | 'null' + | 'ccip_read'; +export interface ModuleTypeResponse { + type: IsmType; +} +export interface VerifyResponse { + verified: boolean; +} +export interface VerifyInfoResponse { + threshold: number; + validators: HexBinary[]; +} diff --git a/typescript/sdk/src/cw-types/IsmMultisig.types.ts b/typescript/sdk/src/cw-types/IsmMultisig.types.ts new file mode 100644 index 000000000..d214d9208 --- /dev/null +++ b/typescript/sdk/src/cw-types/IsmMultisig.types.ts @@ -0,0 +1,127 @@ +/** + * This file was automatically generated by @cosmwasm/ts-codegen@0.35.3. + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, + * and run the @cosmwasm/ts-codegen generate command to regenerate this file. + */ + +export interface InstantiateMsg { + owner: string; +} +export type ExecuteMsg = + | { + ownable: OwnableMsg; + } + | { + enroll_validator: { + set: ValidatorSet; + }; + } + | { + enroll_validators: { + set: ValidatorSet[]; + }; + } + | { + unenroll_validator: { + domain: number; + validator: HexBinary; + }; + } + | { + set_threshold: { + set: ThresholdSet; + }; + } + | { + set_thresholds: { + set: ThresholdSet[]; + }; + }; +export type OwnableMsg = + | { + init_ownership_transfer: { + next_owner: string; + }; + } + | { + revoke_ownership_transfer: {}; + } + | { + claim_ownership: {}; + }; +export type HexBinary = string; +export interface ValidatorSet { + domain: number; + validator: HexBinary; +} +export interface ThresholdSet { + domain: number; + threshold: number; +} +export type QueryMsg = + | { + ownable: OwnableQueryMsg; + } + | { + ism: IsmQueryMsg; + } + | { + multisig_ism: MultisigIsmQueryMsg; + }; +export type OwnableQueryMsg = + | { + get_owner: {}; + } + | { + get_pending_owner: {}; + }; +export type IsmQueryMsg = + | { + module_type: {}; + } + | { + verify: { + message: HexBinary; + metadata: HexBinary; + }; + } + | { + verify_info: { + message: HexBinary; + }; + }; +export type MultisigIsmQueryMsg = { + enrolled_validators: { + domain: number; + }; +}; +export interface EnrolledValidatorsResponse { + threshold: number; + validators: HexBinary[]; +} +export type Addr = string; +export interface OwnerResponse { + owner: Addr; +} +export interface PendingOwnerResponse { + pending_owner?: Addr | null; +} +export type IsmType = + | 'unused' + | 'routing' + | 'aggregation' + | 'legacy_multisig' + | 'merkle_root_multisig' + | 'message_id_multisig' + | 'null' + | 'ccip_read'; +export interface ModuleTypeResponse { + type: IsmType; +} +export interface VerifyResponse { + verified: boolean; +} +export interface VerifyInfoResponse { + threshold: number; + validators: HexBinary[]; +} diff --git a/typescript/sdk/src/cw-types/IsmRouting.types.ts b/typescript/sdk/src/cw-types/IsmRouting.types.ts new file mode 100644 index 000000000..41c8185f4 --- /dev/null +++ b/typescript/sdk/src/cw-types/IsmRouting.types.ts @@ -0,0 +1,102 @@ +/** + * This file was automatically generated by @cosmwasm/ts-codegen@0.35.3. + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, + * and run the @cosmwasm/ts-codegen generate command to regenerate this file. + */ + +export interface InstantiateMsg { + isms: IsmSet[]; + owner: string; +} +export interface IsmSet { + address: string; + domain: number; +} +export type ExecuteMsg = + | { + ownable: OwnableMsg; + } + | { + set: { + ism: IsmSet; + }; + }; +export type OwnableMsg = + | { + init_ownership_transfer: { + next_owner: string; + }; + } + | { + revoke_ownership_transfer: {}; + } + | { + claim_ownership: {}; + }; +export type QueryMsg = + | { + ownable: OwnableQueryMsg; + } + | { + ism: IsmQueryMsg; + } + | { + routing_ism: RoutingIsmQueryMsg; + }; +export type OwnableQueryMsg = + | { + get_owner: {}; + } + | { + get_pending_owner: {}; + }; +export type IsmQueryMsg = + | { + module_type: {}; + } + | { + verify: { + message: HexBinary; + metadata: HexBinary; + }; + } + | { + verify_info: { + message: HexBinary; + }; + }; +export type HexBinary = string; +export type RoutingIsmQueryMsg = { + route: { + message: HexBinary; + }; +}; +export type Addr = string; +export interface OwnerResponse { + owner: Addr; +} +export interface PendingOwnerResponse { + pending_owner?: Addr | null; +} +export type IsmType = + | 'unused' + | 'routing' + | 'aggregation' + | 'legacy_multisig' + | 'merkle_root_multisig' + | 'message_id_multisig' + | 'null' + | 'ccip_read'; +export interface ModuleTypeResponse { + type: IsmType; +} +export interface RouteResponse { + ism: string; +} +export interface VerifyResponse { + verified: boolean; +} +export interface VerifyInfoResponse { + threshold: number; + validators: HexBinary[]; +} diff --git a/typescript/sdk/src/cw-types/Mailbox.types.ts b/typescript/sdk/src/cw-types/Mailbox.types.ts new file mode 100644 index 000000000..b2e8c3236 --- /dev/null +++ b/typescript/sdk/src/cw-types/Mailbox.types.ts @@ -0,0 +1,154 @@ +/** + * This file was automatically generated by @cosmwasm/ts-codegen@0.35.3. + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, + * and run the @cosmwasm/ts-codegen generate command to regenerate this file. + */ + +export interface InstantiateMsg { + domain: number; + hrp: string; + owner: string; +} +export type ExecuteMsg = + | { + ownable: OwnableMsg; + } + | { + set_default_ism: { + ism: string; + }; + } + | { + set_default_hook: { + hook: string; + }; + } + | { + set_required_hook: { + hook: string; + }; + } + | { + dispatch: DispatchMsg; + } + | { + process: { + message: HexBinary; + metadata: HexBinary; + }; + }; +export type OwnableMsg = + | { + init_ownership_transfer: { + next_owner: string; + }; + } + | { + revoke_ownership_transfer: {}; + } + | { + claim_ownership: {}; + }; +export type HexBinary = string; +export interface DispatchMsg { + dest_domain: number; + hook?: string | null; + metadata?: HexBinary | null; + msg_body: HexBinary; + recipient_addr: HexBinary; +} +export type QueryMsg = + | { + ownable: OwnableQueryMsg; + } + | { + hook: MailboxHookQueryMsg; + } + | { + mailbox: MailboxQueryMsg; + }; +export type OwnableQueryMsg = + | { + get_owner: {}; + } + | { + get_pending_owner: {}; + }; +export type MailboxHookQueryMsg = { + quote_dispatch: DispatchMsg; +}; +export type MailboxQueryMsg = + | { + hrp: {}; + } + | { + local_domain: {}; + } + | { + message_delivered: { + id: HexBinary; + }; + } + | { + default_ism: {}; + } + | { + default_hook: {}; + } + | { + required_hook: {}; + } + | { + nonce: {}; + } + | { + recipient_ism: { + recipient_addr: string; + }; + } + | { + latest_dispatch_id: {}; + }; +export interface DefaultHookResponse { + default_hook: string; +} +export interface DefaultIsmResponse { + default_ism: string; +} +export type Addr = string; +export interface OwnerResponse { + owner: Addr; +} +export interface PendingOwnerResponse { + pending_owner?: Addr | null; +} +export interface HrpResponse { + hrp: string; +} +export interface LatestDispatchedIdResponse { + message_id: HexBinary; +} +export interface LocalDomainResponse { + local_domain: number; +} +export interface MessageDeliveredResponse { + delivered: boolean; +} +export interface NonceResponse { + nonce: number; +} +export type Uint128 = string; +export interface QuoteDispatchResponse { + gas_amount?: Coin | null; +} +export interface Coin { + amount: Uint128; + denom: string; + [k: string]: unknown; +} +export interface RecipientIsmResponse { + ism: string; +} +export interface RequiredHookResponse { + required_hook: string; +} diff --git a/typescript/sdk/src/cw-types/ValidatorAnnounce.types.ts b/typescript/sdk/src/cw-types/ValidatorAnnounce.types.ts new file mode 100644 index 000000000..0d5970682 --- /dev/null +++ b/typescript/sdk/src/cw-types/ValidatorAnnounce.types.ts @@ -0,0 +1,33 @@ +/** + * This file was automatically generated by @cosmwasm/ts-codegen@0.35.3. + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, + * and run the @cosmwasm/ts-codegen generate command to regenerate this file. + */ + +export interface InstantiateMsg { + hrp: string; + mailbox: string; +} +export type ExecuteMsg = { + announce: { + signature: HexBinary; + storage_location: string; + validator: HexBinary; + }; +}; +export type HexBinary = string; +export type QueryMsg = + | { + get_announce_storage_locations: { + validators: HexBinary[]; + }; + } + | { + get_announced_validators: {}; + }; +export interface GetAnnounceStorageLocationsResponse { + storage_locations: [string, string[]][]; +} +export interface GetAnnouncedValidatorsResponse { + validators: string[]; +} diff --git a/typescript/sdk/src/cw-types/WarpCw20.types.ts b/typescript/sdk/src/cw-types/WarpCw20.types.ts new file mode 100644 index 000000000..d7a4bc415 --- /dev/null +++ b/typescript/sdk/src/cw-types/WarpCw20.types.ts @@ -0,0 +1,256 @@ +/** + * This file was automatically generated by @cosmwasm/ts-codegen@0.35.3. + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, + * and run the @cosmwasm/ts-codegen generate command to regenerate this file. + */ + +export type TokenModeMsgForCw20ModeBridgedAndCw20ModeCollateral = + | { + bridged: Cw20ModeBridged; + } + | { + collateral: Cw20ModeCollateral; + }; +export type Uint128 = string; +export type Logo = + | { + url: string; + } + | { + embedded: EmbeddedLogo; + }; +export type EmbeddedLogo = + | { + svg: Binary; + } + | { + png: Binary; + }; +export type Binary = string; +export interface InstantiateMsg { + hrp: string; + mailbox: string; + owner: string; + token: TokenModeMsgForCw20ModeBridgedAndCw20ModeCollateral; +} +export interface Cw20ModeBridged { + code_id: number; + init_msg: InstantiateMsg1; +} +export interface InstantiateMsg1 { + decimals: number; + initial_balances: Cw20Coin[]; + marketing?: InstantiateMarketingInfo | null; + mint?: MinterResponse | null; + name: string; + symbol: string; +} +export interface Cw20Coin { + address: string; + amount: Uint128; +} +export interface InstantiateMarketingInfo { + description?: string | null; + logo?: Logo | null; + marketing?: string | null; + project?: string | null; +} +export interface MinterResponse { + cap?: Uint128 | null; + minter: string; +} +export interface Cw20ModeCollateral { + address: string; +} +export type ExecuteMsg = + | { + ownable: OwnableMsg; + } + | { + router: RouterMsgForHexBinary; + } + | { + connection: ConnectionMsg; + } + | { + handle: HandleMsg; + } + | { + transfer_remote: { + amount: Uint128; + dest_domain: number; + recipient: HexBinary; + }; + }; +export type OwnableMsg = + | { + init_ownership_transfer: { + next_owner: string; + }; + } + | { + revoke_ownership_transfer: {}; + } + | { + claim_ownership: {}; + }; +export type RouterMsgForHexBinary = + | { + set_route: { + set: DomainRouteSetForHexBinary; + }; + } + | { + set_routes: { + set: DomainRouteSetForHexBinary[]; + }; + }; +export type HexBinary = string; +export type ConnectionMsg = + | { + set_mailbox: { + mailbox: string; + }; + } + | { + set_hook: { + hook: string; + }; + } + | { + set_ism: { + ism: string; + }; + }; +export interface DomainRouteSetForHexBinary { + domain: number; + route?: HexBinary | null; +} +export interface HandleMsg { + body: HexBinary; + origin: number; + sender: HexBinary; +} +export type QueryMsg = + | { + ownable: OwnableQueryMsg; + } + | { + router: RouterQueryForHexBinary; + } + | { + connection: ConnectionQueryMsg; + } + | { + token_default: TokenWarpDefaultQueryMsg; + } + | { + ism_specifier: IsmSpecifierQueryMsg; + }; +export type OwnableQueryMsg = + | { + get_owner: {}; + } + | { + get_pending_owner: {}; + }; +export type RouterQueryForHexBinary = + | { + domains: {}; + } + | { + get_route: { + domain: number; + }; + } + | { + list_routes: { + limit?: number | null; + offset?: number | null; + order?: Order | null; + }; + }; +export type Order = 'asc' | 'desc'; +export type ConnectionQueryMsg = + | { + get_mailbox: {}; + } + | { + get_hook: {}; + } + | { + get_ism: {}; + }; +export type TokenWarpDefaultQueryMsg = + | { + token_type: {}; + } + | { + token_mode: {}; + }; +export type IsmSpecifierQueryMsg = { + interchain_security_module: []; +}; +export interface DomainsResponse { + domains: number[]; +} +export interface HookResponse { + hook?: string | null; +} +export interface IsmResponse { + ism?: string | null; +} +export interface MailboxResponse { + mailbox?: string | null; +} +export type Addr = string; +export interface OwnerResponse { + owner: Addr; +} +export interface PendingOwnerResponse { + pending_owner?: Addr | null; +} +export interface RouteResponseForHexBinary { + route: DomainRouteSetForHexBinary; +} +export interface InterchainSecurityModuleResponse { + ism?: Addr | null; +} +export interface RoutesResponseForHexBinary { + routes: DomainRouteSetForHexBinary[]; +} +export interface Empty { + [k: string]: unknown; +} +export type TokenMode = 'bridged' | 'collateral'; +export interface TokenModeResponse { + mode: TokenMode; +} +export type TokenType = + | { + native: TokenTypeNative; + } + | { + c_w20: { + contract: string; + }; + } + | { + c_w721: { + contract: string; + }; + }; +export type TokenTypeNative = + | { + fungible: { + denom: string; + }; + } + | { + non_fungible: { + class: string; + }; + }; +export interface TokenTypeResponse { + type: TokenType; +} diff --git a/typescript/sdk/src/cw-types/WarpNative.types.ts b/typescript/sdk/src/cw-types/WarpNative.types.ts new file mode 100644 index 000000000..073bee339 --- /dev/null +++ b/typescript/sdk/src/cw-types/WarpNative.types.ts @@ -0,0 +1,232 @@ +/** + * This file was automatically generated by @cosmwasm/ts-codegen@0.35.3. + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file, + * and run the @cosmwasm/ts-codegen generate command to regenerate this file. + */ + +export type TokenModeMsgForNativeModeBrigedAndNativeModeCollateral = + | { + bridged: NativeModeBriged; + } + | { + collateral: NativeModeCollateral; + }; +export interface InstantiateMsg { + hrp: string; + mailbox: string; + owner: string; + token: TokenModeMsgForNativeModeBrigedAndNativeModeCollateral; +} +export interface NativeModeBriged { + denom: string; + metadata?: Metadata | null; +} +export interface Metadata { + base: string; + denom_units: DenomUnit[]; + description: string; + display: string; + name: string; + symbol: string; +} +export interface DenomUnit { + aliases: string[]; + denom: string; + exponent: number; +} +export interface NativeModeCollateral { + denom: string; +} +export type ExecuteMsg = + | { + ownable: OwnableMsg; + } + | { + router: RouterMsgForHexBinary; + } + | { + connection: ConnectionMsg; + } + | { + handle: HandleMsg; + } + | { + transfer_remote: { + amount: Uint128; + dest_domain: number; + recipient: HexBinary; + }; + }; +export type OwnableMsg = + | { + init_ownership_transfer: { + next_owner: string; + }; + } + | { + revoke_ownership_transfer: {}; + } + | { + claim_ownership: {}; + }; +export type RouterMsgForHexBinary = + | { + set_route: { + set: DomainRouteSetForHexBinary; + }; + } + | { + set_routes: { + set: DomainRouteSetForHexBinary[]; + }; + }; +export type HexBinary = string; +export type ConnectionMsg = + | { + set_mailbox: { + mailbox: string; + }; + } + | { + set_hook: { + hook: string; + }; + } + | { + set_ism: { + ism: string; + }; + }; +export type Uint128 = string; +export interface DomainRouteSetForHexBinary { + domain: number; + route?: HexBinary | null; +} +export interface HandleMsg { + body: HexBinary; + origin: number; + sender: HexBinary; +} +export type QueryMsg = + | { + ownable: OwnableQueryMsg; + } + | { + router: RouterQueryForHexBinary; + } + | { + connection: ConnectionQueryMsg; + } + | { + token_default: TokenWarpDefaultQueryMsg; + } + | { + ism_specifier: IsmSpecifierQueryMsg; + }; +export type OwnableQueryMsg = + | { + get_owner: {}; + } + | { + get_pending_owner: {}; + }; +export type RouterQueryForHexBinary = + | { + domains: {}; + } + | { + get_route: { + domain: number; + }; + } + | { + list_routes: { + limit?: number | null; + offset?: number | null; + order?: Order | null; + }; + }; +export type Order = 'asc' | 'desc'; +export type ConnectionQueryMsg = + | { + get_mailbox: {}; + } + | { + get_hook: {}; + } + | { + get_ism: {}; + }; +export type TokenWarpDefaultQueryMsg = + | { + token_type: {}; + } + | { + token_mode: {}; + }; +export type IsmSpecifierQueryMsg = { + interchain_security_module: []; +}; +export interface DomainsResponse { + domains: number[]; +} +export interface HookResponse { + hook?: string | null; +} +export interface IsmResponse { + ism?: string | null; +} +export interface MailboxResponse { + mailbox?: string | null; +} +export type Addr = string; +export interface OwnerResponse { + owner: Addr; +} +export interface PendingOwnerResponse { + pending_owner?: Addr | null; +} +export interface RouteResponseForHexBinary { + route: DomainRouteSetForHexBinary; +} +export interface InterchainSecurityModuleResponse { + ism?: Addr | null; +} +export interface RoutesResponseForHexBinary { + routes: DomainRouteSetForHexBinary[]; +} +export interface Empty { + [k: string]: unknown; +} +export type TokenMode = 'bridged' | 'collateral'; +export interface TokenModeResponse { + mode: TokenMode; +} +export type TokenType = + | { + native: TokenTypeNative; + } + | { + c_w20: { + contract: string; + }; + } + | { + c_w721: { + contract: string; + }; + }; +export type TokenTypeNative = + | { + fungible: { + denom: string; + }; + } + | { + non_fungible: { + class: string; + }; + }; +export interface TokenTypeResponse { + type: TokenType; +} diff --git a/typescript/sdk/src/index.ts b/typescript/sdk/src/index.ts index 33f5cffa0..23a3efdaf 100644 --- a/typescript/sdk/src/index.ts +++ b/typescript/sdk/src/index.ts @@ -164,10 +164,12 @@ export { export { ChainMetadata, ChainMetadataSchema, + ChainMetadataSchemaObject, ExplorerFamily, ExplorerFamilyValue, RpcUrl, RpcUrlSchema, + getChainIdNumber, getDomainId, isValidChainMetadata, } from './metadata/chainMetadataTypes'; @@ -206,6 +208,14 @@ export { } from './providers/MultiProtocolProvider'; export { MultiProvider, MultiProviderOptions } from './providers/MultiProvider'; export { + CosmJsContract, + CosmJsProvider, + CosmJsTransaction, + CosmJsTransactionReceipt, + CosmJsWasmContract, + CosmJsWasmProvider, + CosmJsWasmTransaction, + CosmJsWasmTransactionReceipt, EthersV5Contract, EthersV5Provider, EthersV5Transaction, @@ -276,6 +286,14 @@ export { RouterViolationType, proxiedFactories, } from './router/types'; +export { + CW20Metadata, + CwHypCollateralAdapter, + CwHypNativeAdapter, + CwHypSyntheticAdapter, + CwNativeTokenAdapter, + CwTokenAdapter, +} from './token/adapters/CosmWasmTokenAdapter'; export { EvmHypCollateralAdapter, EvmHypSyntheticAdapter, @@ -292,7 +310,6 @@ export { SealevelHypCollateralAdapter, SealevelHypNativeAdapter, SealevelHypSyntheticAdapter, - SealevelHypTokenAdapter, SealevelNativeTokenAdapter, SealevelTokenAdapter, } from './token/adapters/SealevelTokenAdapter'; diff --git a/typescript/sdk/src/ism/adapters/CosmWasmMultisigAdapter.ts b/typescript/sdk/src/ism/adapters/CosmWasmMultisigAdapter.ts new file mode 100644 index 000000000..8f2e7e768 --- /dev/null +++ b/typescript/sdk/src/ism/adapters/CosmWasmMultisigAdapter.ts @@ -0,0 +1,122 @@ +import { ExecuteInstruction } from '@cosmjs/cosmwasm-stargate'; + +import { + Address, + difference, + objMap, + promiseObjAll, +} from '@hyperlane-xyz/utils'; + +import { BaseCosmWasmAdapter } from '../../app/MultiProtocolApp'; +import { + EnrolledValidatorsResponse, + ExecuteMsg as MultisigExecute, + QueryMsg as MultisigQuery, +} from '../../cw-types/IsmMultisig.types'; +import { MultisigConfig } from '../../ism/types'; +import { MultiProtocolProvider } from '../../providers/MultiProtocolProvider'; +import { ChainMap, ChainName } from '../../types'; + +type MultisigResponse = EnrolledValidatorsResponse; + +export class CosmWasmMultisigAdapter extends BaseCosmWasmAdapter { + constructor( + public readonly chainName: ChainName, + public readonly multiProvider: MultiProtocolProvider, + public readonly addresses: { multisig: Address }, + ) { + super(chainName, multiProvider, addresses); + } + + async queryMultisig( + msg: MultisigQuery, + ): Promise { + const provider = await this.getProvider(); + const response: R = await provider.queryContractSmart( + this.addresses.multisig, + msg, + ); + return response; + } + + async getConfig(chain: ChainName): Promise { + return this.queryMultisig({ + multisig_ism: { + enrolled_validators: { + domain: this.multiProvider.getDomainId(chain), + }, + }, + }); + } + + prepareMultisig(msg: MultisigExecute): ExecuteInstruction { + return { + contractAddress: this.addresses.multisig, + msg, + }; + } + + async configureMultisig( + configMap: ChainMap, + ): Promise { + const configuredMap = await promiseObjAll( + objMap(configMap, (origin, _) => this.getConfig(origin)), + ); + + const validatorInstructions = Object.entries(configMap).flatMap( + ([origin, config]) => { + const domain = this.multiProvider.getDomainId(origin); + const configuredSet = new Set(configuredMap[origin].validators); + const configSet = new Set(config.validators); + const unenrollList = Array.from( + difference(configuredSet, configSet).values(), + ); + const enrollList = Array.from( + difference(configSet, configuredSet).values(), + ); + return unenrollList + .map((validator) => + this.prepareMultisig({ + unenroll_validator: { + domain, + validator, + }, + }), + ) + .concat( + enrollList.map((validator) => + this.prepareMultisig({ + enroll_validator: { + set: { + domain, + validator, + }, + }, + }), + ), + ); + }, + ); + + const setThresholds = Object.entries(configMap) + .filter( + ([origin, { threshold }]) => + threshold !== configuredMap[origin].threshold, + ) + .map(([origin, config]) => ({ + domain: this.multiProvider.getDomainId(origin), + threshold: config.threshold, + })); + + if (setThresholds.length > 0) { + const thresholdInstruction = this.prepareMultisig({ + set_thresholds: { + set: setThresholds, + }, + }); + return [...validatorInstructions, thresholdInstruction]; + } + + return validatorInstructions; + } +} diff --git a/typescript/sdk/src/metadata/ChainMetadataManager.ts b/typescript/sdk/src/metadata/ChainMetadataManager.ts index 2651dbea1..237cdccb2 100644 --- a/typescript/sdk/src/metadata/ChainMetadataManager.ts +++ b/typescript/sdk/src/metadata/ChainMetadataManager.ts @@ -1,6 +1,6 @@ import { Debugger, debug } from 'debug'; -import { ProtocolType, exclude, isNumeric, pick } from '@hyperlane-xyz/utils'; +import { ProtocolType, exclude, pick } from '@hyperlane-xyz/utils'; import { chainMetadata as defaultChainMetadata, @@ -64,7 +64,7 @@ export class ChainMetadataManager { chainId == metadata.chainId || domainId == metadata.chainId || (metadata.domainId && - (chainId == metadata.domainId || domainId === metadata.domainId)); + (chainId == metadata.domainId || domainId == metadata.domainId)); if (idCollision) throw new Error( `Chain/Domain id collision: ${name} and ${metadata.name}`, @@ -80,16 +80,12 @@ export class ChainMetadataManager { tryGetChainMetadata( chainNameOrId: ChainName | number, ): ChainMetadata | null { - let chainMetadata: ChainMetadata | undefined; - if (isNumeric(chainNameOrId)) { - // Should be chain id or domain id - chainMetadata = Object.values(this.metadata).find( - (m) => m.chainId == chainNameOrId || m.domainId == chainNameOrId, - ); - } else if (typeof chainNameOrId === 'string') { - // Should be chain name - chainMetadata = this.metadata[chainNameOrId]; - } + // First check if it's a chain name + if (this.metadata[chainNameOrId]) return this.metadata[chainNameOrId]; + // Otherwise search by chain id and domain id + const chainMetadata = Object.values(this.metadata).find( + (m) => m.chainId == chainNameOrId || m.domainId == chainNameOrId, + ); return chainMetadata || null; } @@ -130,7 +126,7 @@ export class ChainMetadataManager { /** * Get the id for a given chain name, chain id, or domain id */ - tryGetChainId(chainNameOrId: ChainName | number): number | null { + tryGetChainId(chainNameOrId: ChainName | number): number | string | null { return this.tryGetChainMetadata(chainNameOrId)?.chainId ?? null; } @@ -138,14 +134,14 @@ export class ChainMetadataManager { * Get the id for a given chain name, chain id, or domain id * @throws if chain's metadata has not been set */ - getChainId(chainNameOrId: ChainName | number): number { + getChainId(chainNameOrId: ChainName | number): number | string { return this.getChainMetadata(chainNameOrId).chainId; } /** * Get the ids for all chains known to this MultiProvider */ - getKnownChainIds(): number[] { + getKnownChainIds(): Array { return Object.values(this.metadata).map((c) => c.chainId); } @@ -154,7 +150,8 @@ export class ChainMetadataManager { */ tryGetDomainId(chainNameOrId: ChainName | number): number | null { const metadata = this.tryGetChainMetadata(chainNameOrId); - return metadata?.domainId ?? metadata?.chainId ?? null; + if (!metadata) return null; + return getDomainId(metadata) ?? null; } /** @@ -162,8 +159,9 @@ export class ChainMetadataManager { * @throws if chain's metadata has not been set */ getDomainId(chainNameOrId: ChainName | number): number { - const metadata = this.getChainMetadata(chainNameOrId); - return getDomainId(metadata); + const domainId = this.tryGetDomainId(chainNameOrId); + if (!domainId) throw new Error(`No domain id set for ${chainNameOrId}`); + return domainId; } /** @@ -224,6 +222,7 @@ export class ChainMetadataManager { ) { url.searchParams.set('cluster', solanaChainToClusterName[metadata.name]); } + // TODO cosmos support here return url.toString(); } diff --git a/typescript/sdk/src/metadata/agentConfig.ts b/typescript/sdk/src/metadata/agentConfig.ts index b9c1a8545..8431ea738 100644 --- a/typescript/sdk/src/metadata/agentConfig.ts +++ b/typescript/sdk/src/metadata/agentConfig.ts @@ -7,7 +7,7 @@ import { z } from 'zod'; import { MultiProvider } from '../providers/MultiProvider'; import { ChainMap, ChainName } from '../types'; -import { ChainMetadata, ChainMetadataSchema } from './chainMetadataTypes'; +import { ChainMetadata, ChainMetadataSchemaObject } from './chainMetadataTypes'; import { ZHash, ZNzUint, ZUWei, ZUint } from './customZodTypes'; import { HyperlaneDeploymentArtifacts, @@ -80,7 +80,7 @@ export type AgentSignerAwsKey = z.infer; export type AgentSignerNode = z.infer; export type AgentSigner = z.infer; -export const AgentChainMetadataSchema = ChainMetadataSchema.merge( +export const AgentChainMetadataSchema = ChainMetadataSchemaObject.merge( HyperlaneDeploymentArtifactsSchema, ).extend({ customRpcUrls: z diff --git a/typescript/sdk/src/metadata/chainMetadata.test.ts b/typescript/sdk/src/metadata/chainMetadata.test.ts index e58f0dfac..16de256ba 100644 --- a/typescript/sdk/src/metadata/chainMetadata.test.ts +++ b/typescript/sdk/src/metadata/chainMetadata.test.ts @@ -50,6 +50,16 @@ describe('ChainMetadataSchema', () => { blocks, }), ).to.eq(true); + + expect( + isValidChainMetadata({ + ...minimalSchema, + protocol: ProtocolType.Cosmos, + chainId: 'cosmos', + bech32Prefix: 'cosmos', + slip44: 118, + }), + ).to.eq(true); }); it('Rejects invalid schemas', () => { expect( @@ -80,5 +90,20 @@ describe('ChainMetadataSchema', () => { name: 'Invalid name', }), ).to.eq(false); + + expect( + isValidChainMetadata({ + ...minimalSchema, + chainId: 'string-id', + }), + ).to.eq(false); + + expect( + isValidChainMetadata({ + ...minimalSchema, + protocol: ProtocolType.Cosmos, + chainId: 'string-id', + }), + ).to.eq(false); }); }); diff --git a/typescript/sdk/src/metadata/chainMetadataTypes.ts b/typescript/sdk/src/metadata/chainMetadataTypes.ts index caa9297e2..7e185878e 100644 --- a/typescript/sdk/src/metadata/chainMetadataTypes.ts +++ b/typescript/sdk/src/metadata/chainMetadataTypes.ts @@ -59,7 +59,7 @@ export type RpcUrl = z.infer; * A collection of useful properties and settings for chains using Hyperlane * Specified as a Zod schema */ -export const ChainMetadataSchema = z.object({ +export const ChainMetadataSchemaObject = z.object({ name: z .string() .regex(/^[a-z][a-z0-9]*$/) @@ -71,9 +71,9 @@ export const ChainMetadataSchema = z.object({ .describe( 'The type of protocol used by this chain. See ProtocolType for valid values.', ), - chainId: ZNzUint.describe( - `The chainId of the chain. Uses EIP-155 for EVM chains`, - ), + chainId: z + .union([ZNzUint, z.string()]) + .describe(`The chainId of the chain. Uses EIP-155 for EVM chains`), domainId: ZNzUint.optional().describe( 'The domainId of the chain, should generally default to `chainId`. Consumer of `ChainMetadata` should use this value if present, but otherwise fallback to `chainId`.', ), @@ -161,12 +161,59 @@ export const ChainMetadataSchema = z.object({ .string() .optional() .describe('The URL of the gnosis safe transaction service.'), + bech32Prefix: z + .string() + .optional() + .describe('The human readable address prefix for the chains using bech32.'), + slip44: z.number().optional().describe('The SLIP-0044 coin type.'), isTestnet: z .boolean() .optional() .describe('Whether the chain is considered a testnet or a mainnet.'), }); +// Add refinements to the object schema to conditionally validate certain fields +export const ChainMetadataSchema = ChainMetadataSchemaObject.refine( + (metadata) => { + if ( + [ProtocolType.Ethereum, ProtocolType.Sealevel].includes( + metadata.protocol, + ) && + typeof metadata.chainId !== 'number' + ) + return false; + else if ( + metadata.protocol === ProtocolType.Cosmos && + typeof metadata.chainId !== 'string' + ) + return false; + else return true; + }, + { message: 'Invalid Chain Id', path: ['chainId'] }, +) + .refine( + (metadata) => { + if (typeof metadata.chainId === 'string' && !metadata.domainId) + return false; + else return true; + }, + { message: 'Domain Id required', path: ['domainId'] }, + ) + .refine( + (metadata) => { + if ( + metadata.protocol === ProtocolType.Cosmos && + (!metadata.bech32Prefix || !metadata.slip44) + ) + return false; + else return true; + }, + { + message: 'Bech32Prefix and Slip44 required for Cosmos chains', + path: ['bech32Prefix', 'slip44'], + }, + ); + export type ChainMetadata = z.infer & Ext; @@ -175,5 +222,13 @@ export function isValidChainMetadata(c: ChainMetadata): boolean { } export function getDomainId(chainMetadata: ChainMetadata): number { - return chainMetadata.domainId ?? chainMetadata.chainId; + if (chainMetadata.domainId) return chainMetadata.domainId; + else if (typeof chainMetadata.chainId === 'number') + return chainMetadata.chainId; + else throw new Error('Invalid chain metadata, no valid domainId'); +} + +export function getChainIdNumber(chainMetadata: ChainMetadata): number { + if (typeof chainMetadata.chainId === 'number') return chainMetadata.chainId; + else throw new Error('ChainId is not a number, chain may be of Cosmos type'); } diff --git a/typescript/sdk/src/providers/MultiProtocolProvider.ts b/typescript/sdk/src/providers/MultiProtocolProvider.ts index 559d6049d..868c65bb6 100644 --- a/typescript/sdk/src/providers/MultiProtocolProvider.ts +++ b/typescript/sdk/src/providers/MultiProtocolProvider.ts @@ -9,6 +9,8 @@ import type { ChainMap, ChainName } from '../types'; import { MultiProvider, MultiProviderOptions } from './MultiProvider'; import { + CosmJsProvider, + CosmJsWasmProvider, EthersV5Provider, ProviderMap, ProviderType, @@ -26,6 +28,7 @@ export const PROTOCOL_DEFAULT_PROVIDER_TYPE: Partial< > = { [ProtocolType.Ethereum]: ProviderType.EthersV5, [ProtocolType.Sealevel]: ProviderType.SolanaWeb3, + [ProtocolType.Cosmos]: ProviderType.CosmJsWasm, }; export interface MultiProtocolProviderOptions { @@ -143,38 +146,59 @@ export class MultiProtocolProvider< return provider; } + protected getSpecificProvider( + chainNameOrId: ChainName | number, + type: ProviderType, + ): T { + const provider = this.getProvider(chainNameOrId, type); + if (provider.type !== type) + throw new Error( + `Invalid provider type, expected ${type} but found ${provider.type}`, + ); + return provider.provider as T; + } + getEthersV5Provider( chainNameOrId: ChainName | number, ): EthersV5Provider['provider'] { - const provider = this.getProvider(chainNameOrId, ProviderType.EthersV5); - if (provider.type !== ProviderType.EthersV5) - throw new Error('Invalid provider type'); - return provider.provider; + return this.getSpecificProvider( + chainNameOrId, + ProviderType.EthersV5, + ); } - // getEthersV6Provider( - // chainNameOrId: ChainName | number, - // ): EthersV6Provider['provider'] { - // const provider = this.getProvider(chainNameOrId, ProviderType.EthersV6); - // if (provider.type !== ProviderType.EthersV6) - // throw new Error('Invalid provider type'); - // return provider.provider; - // } - getViemProvider(chainNameOrId: ChainName | number): ViemProvider['provider'] { - const provider = this.getProvider(chainNameOrId, ProviderType.Viem); - if (provider.type !== ProviderType.Viem) - throw new Error('Invalid provider type'); - return provider.provider; + return this.getSpecificProvider( + chainNameOrId, + ProviderType.Viem, + ); } getSolanaWeb3Provider( chainNameOrId: ChainName | number, ): SolanaWeb3Provider['provider'] { - const provider = this.getProvider(chainNameOrId, ProviderType.SolanaWeb3); - if (provider.type !== ProviderType.SolanaWeb3) - throw new Error('Invalid provider type'); - return provider.provider; + return this.getSpecificProvider( + chainNameOrId, + ProviderType.SolanaWeb3, + ); + } + + getCosmJsProvider( + chainNameOrId: ChainName | number, + ): CosmJsProvider['provider'] { + return this.getSpecificProvider( + chainNameOrId, + ProviderType.CosmJs, + ); + } + + getCosmJsWasmProvider( + chainNameOrId: ChainName | number, + ): CosmJsWasmProvider['provider'] { + return this.getSpecificProvider( + chainNameOrId, + ProviderType.CosmJsWasm, + ); } setProvider( diff --git a/typescript/sdk/src/providers/ProviderType.ts b/typescript/sdk/src/providers/ProviderType.ts index b5e9bdcc9..b59a71e1b 100644 --- a/typescript/sdk/src/providers/ProviderType.ts +++ b/typescript/sdk/src/providers/ProviderType.ts @@ -1,3 +1,10 @@ +import type { + CosmWasmClient, + Contract as CosmWasmContract, + ExecuteInstruction, +} from '@cosmjs/cosmwasm-stargate'; +import type { EncodeObject as CmTransaction } from '@cosmjs/proto-signing'; +import type { DeliverTxResponse, StargateClient } from '@cosmjs/stargate'; import type { Connection, Transaction as SolTransaction, @@ -21,6 +28,8 @@ export enum ProviderType { // EthersV6 = 'ethers-v6', Disabled for now to simplify build tooling Viem = 'viem', SolanaWeb3 = 'solana-web3', + CosmJs = 'cosmjs', + CosmJsWasm = 'cosmjs-wasm', } export type ProviderMap = Partial>; @@ -55,11 +64,25 @@ export interface SolanaWeb3Provider extends TypedProviderBase { provider: Connection; } +export interface CosmJsProvider + extends TypedProviderBase> { + type: ProviderType.CosmJs; + provider: Promise; +} + +export interface CosmJsWasmProvider + extends TypedProviderBase> { + type: ProviderType.CosmJsWasm; + provider: Promise; +} + export type TypedProvider = | EthersV5Provider // | EthersV6Provider | ViemProvider - | SolanaWeb3Provider; + | SolanaWeb3Provider + | CosmJsProvider + | CosmJsWasmProvider; /** * Contracts with discriminated union of provider type @@ -72,7 +95,7 @@ interface TypedContractBase { export interface EthersV5Contract extends TypedContractBase { type: ProviderType.EthersV5; - transaction: EV5Contract; + contract: EV5Contract; } // export interface EthersV6Contract extends TypedContractBase { @@ -82,20 +105,34 @@ export interface EthersV5Contract extends TypedContractBase { export interface ViemContract extends TypedContractBase { type: ProviderType.Viem; - transaction: GetContractReturnType; + contract: GetContractReturnType; } export interface SolanaWeb3Contract extends TypedContractBase { type: ProviderType.SolanaWeb3; // Contract concept doesn't exist in @solana/web3.js - transaction: never; + contract: never; +} + +export interface CosmJsContract extends TypedContractBase { + type: ProviderType.CosmJs; + // TODO, research if cosmos sdk modules have an equivalent for here + contract: never; +} + +export interface CosmJsWasmContract + extends TypedContractBase { + type: ProviderType.CosmJsWasm; + contract: CosmWasmContract; } export type TypedContract = | EthersV5Contract // | EthersV6Contract | ViemContract - | SolanaWeb3Contract; + | SolanaWeb3Contract + | CosmJsContract + | CosmJsWasmContract; /** * Transactions with discriminated union of provider type @@ -128,11 +165,24 @@ export interface SolanaWeb3Transaction transaction: SolTransaction; } +export interface CosmJsTransaction extends TypedTransactionBase { + type: ProviderType.CosmJs; + transaction: CmTransaction; +} + +export interface CosmJsWasmTransaction + extends TypedTransactionBase { + type: ProviderType.CosmJs; + transaction: ExecuteInstruction; +} + export type TypedTransaction = | EthersV5Transaction // | EthersV6Transaction | ViemTransaction - | SolanaWeb3Transaction; + | SolanaWeb3Transaction + | CosmJsTransaction + | CosmJsWasmTransaction; /** * Transaction receipt/response with discriminated union of provider type @@ -161,7 +211,21 @@ export interface SolanaWeb3TransactionReceipt receipt: SolTransactionReceipt; } +export interface CosmJsTransactionReceipt + extends TypedTransactionReceiptBase { + type: ProviderType.CosmJs; + receipt: DeliverTxResponse; +} + +export interface CosmJsWasmTransactionReceipt + extends TypedTransactionReceiptBase { + type: ProviderType.CosmJsWasm; + receipt: DeliverTxResponse; +} + export type TypedTransactionReceipt = | EthersV5TransactionReceipt | ViemTransactionReceipt - | SolanaWeb3TransactionReceipt; + | SolanaWeb3TransactionReceipt + | CosmJsTransactionReceipt + | CosmJsWasmTransactionReceipt; diff --git a/typescript/sdk/src/providers/providerBuilders.ts b/typescript/sdk/src/providers/providerBuilders.ts index f30a36000..5cab49453 100644 --- a/typescript/sdk/src/providers/providerBuilders.ts +++ b/typescript/sdk/src/providers/providerBuilders.ts @@ -1,3 +1,5 @@ +import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate'; +import { StargateClient } from '@cosmjs/stargate'; import { Connection } from '@solana/web3.js'; import { providers } from 'ethers'; import { createPublicClient, http } from 'viem'; @@ -7,6 +9,8 @@ import { ProtocolType, isNumeric } from '@hyperlane-xyz/utils'; import { ChainMetadata } from '../metadata/chainMetadataTypes'; import { + CosmJsProvider, + CosmJsWasmProvider, EthersV5Provider, ProviderType, SolanaWeb3Provider, @@ -102,6 +106,28 @@ export function defaultFuelProviderBuilder( throw new Error('TODO fuel support'); } +export function defaultCosmJsProviderBuilder( + rpcUrls: ChainMetadata['rpcUrls'], + _network: number | string, +): CosmJsProvider { + if (!rpcUrls.length) throw new Error('No RPC URLs provided'); + return { + type: ProviderType.CosmJs, + provider: StargateClient.connect(rpcUrls[0].http), + }; +} + +export function defaultCosmJsWasmProviderBuilder( + rpcUrls: ChainMetadata['rpcUrls'], + _network: number | string, +): CosmJsWasmProvider { + if (!rpcUrls.length) throw new Error('No RPC URLs provided'); + return { + type: ProviderType.CosmJsWasm, + provider: CosmWasmClient.connect(rpcUrls[0].http), + }; +} + // Kept for backwards compatibility export function defaultProviderBuilder( rpcUrls: ChainMetadata['rpcUrls'], @@ -119,6 +145,8 @@ export const defaultProviderBuilderMap: ProviderBuilderMap = { // [ProviderType.EthersV6]: defaultEthersV6ProviderBuilder, [ProviderType.Viem]: defaultViemProviderBuilder, [ProviderType.SolanaWeb3]: defaultSolProviderBuilder, + [ProviderType.CosmJs]: defaultCosmJsProviderBuilder, + [ProviderType.CosmJsWasm]: defaultCosmJsWasmProviderBuilder, }; export const protocolToDefaultProviderBuilder: Record< @@ -128,4 +156,5 @@ export const protocolToDefaultProviderBuilder: Record< [ProtocolType.Ethereum]: defaultEthersV5ProviderBuilder, [ProtocolType.Sealevel]: defaultSolProviderBuilder, [ProtocolType.Fuel]: defaultFuelProviderBuilder, + [ProtocolType.Cosmos]: defaultCosmJsWasmProviderBuilder, }; diff --git a/typescript/sdk/src/router/MultiProtocolRouterApps.ts b/typescript/sdk/src/router/MultiProtocolRouterApps.ts index 5b7248d95..b60bd9f91 100644 --- a/typescript/sdk/src/router/MultiProtocolRouterApps.ts +++ b/typescript/sdk/src/router/MultiProtocolRouterApps.ts @@ -27,6 +27,7 @@ export class MultiProtocolRouterApp< // enabling extensible generic types if (protocol === ProtocolType.Ethereum) return EvmRouterAdapter as any; if (protocol === ProtocolType.Sealevel) return SealevelRouterAdapter as any; + // TODO cosmos support here throw new Error(`No adapter for protocol ${protocol}`); } diff --git a/typescript/sdk/src/token/adapters/CosmWasmTokenAdapter.test.ts b/typescript/sdk/src/token/adapters/CosmWasmTokenAdapter.test.ts new file mode 100644 index 000000000..2c0da0b7f --- /dev/null +++ b/typescript/sdk/src/token/adapters/CosmWasmTokenAdapter.test.ts @@ -0,0 +1,398 @@ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ + +/* eslint-disable no-console */ +import { + CosmWasmClient, + ExecuteInstruction, + SigningCosmWasmClient, +} from '@cosmjs/cosmwasm-stargate'; +import { Secp256k1, keccak256 } from '@cosmjs/crypto'; +import { DirectSecp256k1Wallet } from '@cosmjs/proto-signing'; +import { GasPrice, SigningStargateClient } from '@cosmjs/stargate'; +import { Tendermint37Client } from '@cosmjs/tendermint-rpc'; + +import { Address } from '@hyperlane-xyz/utils'; + +import { chainMetadata } from '../../consts/chainMetadata'; +import { Chains } from '../../consts/chains'; +import { CosmWasmCoreAdapter } from '../../core/adapters/CosmWasmCoreAdapter'; +import { + MailboxResponse, + QueryMsg as MerkleQuery, + OwnerResponse, +} from '../../cw-types/HookMerkle.types'; +import { CosmWasmMultisigAdapter } from '../../ism/adapters/CosmWasmMultisigAdapter'; +import { MultiProtocolProvider } from '../../providers/MultiProtocolProvider'; + +const neutronAddresses = { + mailbox: 'neutron1sjzzd4gwkggy6hrrs8kxxatexzcuz3jecsxm3wqgregkulzj8r7qlnuef4', + validatorAnnounce: + 'neutron17w4q6efzym3p4c6umyp4cjf2ustjtmwfqdhd7rt2fpcpk9fmjzsq0kj0f8', +}; + +const neutron = chainMetadata.neutron; +const mantapacific = chainMetadata.mantapacific; + +const multiProtocolProvider = new MultiProtocolProvider(); + +const adapter = new CosmWasmCoreAdapter( + Chains.neutron, + multiProtocolProvider, + neutronAddresses, +); + +export async function getSigningClient(pkey: string) { + const wallet = await DirectSecp256k1Wallet.fromKey( + Buffer.from(pkey, 'hex'), + neutron.bech32Prefix!, + ); + + const [account] = await wallet.getAccounts(); + + const clientBase = await Tendermint37Client.connect(neutron.rpcUrls[0].http); + + const gasPrice = GasPrice.fromString('0.1untrn'); + + const wasm = await SigningCosmWasmClient.createWithSigner( + clientBase, + wallet, + { + gasPrice, + }, + ); + const stargate = await SigningStargateClient.createWithSigner( + clientBase, + wallet, + { + gasPrice, + }, + ); + + const pubkey = Secp256k1.uncompressPubkey(account.pubkey); + const ethaddr = keccak256(pubkey.slice(1)).slice(-20); + + return { + wasm, + stargate, + signer: account.address, + signer_addr: Buffer.from(ethaddr).toString('hex'), + signer_pubkey: Buffer.from(account.pubkey).toString('hex'), + }; +} + +const initTransferOwner = ( + address: Address, + newOwner: Address, + key = 'ownable', +): ExecuteInstruction => { + return { + contractAddress: address, + msg: { + [key]: { + init_ownership_transfer: { + next_owner: newOwner, + }, + }, + }, + }; +}; + +const claimTransferOwner = ( + address: Address, + key = 'ownable', +): ExecuteInstruction => { + return { + contractAddress: address, + msg: { + [key]: { + claim_ownership: {}, + }, + }, + }; +}; + +const getOwner = async ( + provider: CosmWasmClient, + address: Address, +): Promise
=> { + const ownableQuery = { + ownable: { get_owner: {} }, + }; + const ownerResponse: OwnerResponse = await provider.queryContractSmart( + address, + ownableQuery, + ); + return ownerResponse.owner; +}; + +export async function rotateHooks() { + const desiredDefault = + 'neutron1e5c2qqquc86rd3q77aj2wyht40z6z3q5pclaq040ue9f5f8yuf7qnpvkzk'; + + const desiredRequired = + 'neutron19qjplhq7jsmk7haneafqxyyhltgllvvag8c4g7jkmxw6mvd4h8sq7rqh02'; + + const safe = await getSigningClient( + '2ac7230628b8b4a587c4005798184735471b9240fc57fc75d97824e1fb6b5409', + ); + + const tx = await safe.wasm.executeMultiple( + safe.signer, + [ + adapter.setDefaultHook(desiredDefault), + adapter.setRequiredHook(desiredRequired), + ], + 'auto', + ); + + console.log(tx); +} + +export async function rotateAuth() { + const safe = await getSigningClient( + '2ac7230628b8b4a587c4005798184735471b9240fc57fc75d97824e1fb6b5409', + ); + + const desiredOwner = + 'neutron1fqf5mprg3f5hytvzp3t7spmsum6rjrw80mq8zgkc0h6rxga0dtzqws3uu7'; + + const addresses: string[] = [ + 'neutron1sjzzd4gwkggy6hrrs8kxxatexzcuz3jecsxm3wqgregkulzj8r7qlnuef4', // mailbox + 'neutron17w4q6efzym3p4c6umyp4cjf2ustjtmwfqdhd7rt2fpcpk9fmjzsq0kj0f8', // validator announce + 'neutron1q75ky8reksqzh0lkhk9k3csvjwv74jjquahrj233xc7dvzz5fv4qtvw0qg', // multisig ISM + 'neutron1e5c2qqquc86rd3q77aj2wyht40z6z3q5pclaq040ue9f5f8yuf7qnpvkzk', // merkle + 'neutron19qjplhq7jsmk7haneafqxyyhltgllvvag8c4g7jkmxw6mvd4h8sq7rqh02', // pausable + 'neutron1ch7x3xgpnj62weyes8vfada35zff6z59kt2psqhnx9gjnt2ttqdqtva3pa', // warp route + ]; + + const transferInstructions: ExecuteInstruction[] = []; + const claimInstructions: ExecuteInstruction[] = []; + + for (const address of addresses) { + const info = await safe.wasm.getContract(address); + console.log({ address, info }); + try { + await getOwner(safe.wasm, address); + + const transferInstruction = initTransferOwner(address, desiredOwner); + transferInstructions.push(transferInstruction); + + const claimInstruction = claimTransferOwner(address); + claimInstructions.push(claimInstruction); + } catch (e: any) { + if (e.message.includes('unknown variant `ownable`')) { + console.log( + `Skipping ${info.label} (${address}) because it is not ownable`, + ); + } else { + throw e; + } + } + // } + + console.log(JSON.stringify({ transferInstructions, claimInstructions })); + + // const tx = await safe.wasm.executeMultiple( + // safe.signer, + // transferInstructions, + // 'auto', + // ); + // console.log(tx); + + // const claimTx = await safe.wasm.execute( + // safe.signer, + // address, + // claimInstruction.msg, + // 'auto', + // ); + // console.log(claimTx); + + const res = await safe.wasm.updateAdmin( + safe.signer, + address, + desiredOwner, + 'auto', + ); + console.log(res); + } +} + +export async function summary() { + const summary: any = {}; + + const provider = await adapter.getProvider(); + + const getOwner = async (address: Address): Promise
=> { + const ownableQuery = { + ownable: { get_owner: {} }, + }; + const ownerResponse: OwnerResponse = await provider.queryContractSmart( + address, + ownableQuery, + ); + return ownerResponse.owner; + }; + + const owner = await getOwner(neutronAddresses.mailbox); + const info = await provider.getContract(neutronAddresses.mailbox); + const defaultHook = await adapter.defaultHook(); + const requiredHook = await adapter.requiredHook(); + const defaultIsm = await adapter.defaultIsm(); + + summary.mailbox = { + owner, + ...info, + defaultHook, + requiredHook, + defaultIsm, + }; + + const router = + 'neutron1ch7x3xgpnj62weyes8vfada35zff6z59kt2psqhnx9gjnt2ttqdqtva3pa'; + summary.warproute = { + owner: await getOwner(router), + ...(await provider.getContract(router)), + }; + + summary.validatorAnnounce = { + // owner: await getOwner(neutronAddresses.validatorAnnounce), + ...(await provider.getContract(neutronAddresses.validatorAnnounce)), + }; + + const defaultIsmContract = await provider.getContract(defaultIsm); + + if (defaultIsmContract.label === 'hpl_ism_multisig') { + const multisigAdapter = new CosmWasmMultisigAdapter( + neutron.name, + multiProtocolProvider, + { multisig: defaultIsm }, + ); + const multisigConfig = await multisigAdapter.getConfig(mantapacific.name); + const owner = await getOwner(defaultIsm); + summary.defaultIsm = { + ...multisigConfig, + ...defaultIsmContract, + owner, + }; + } + + const getMailbox = async (hook: Address): Promise
=> { + const merkleMailboxQuery: MerkleQuery = { + hook: { + mailbox: {}, + }, + }; + const merkleMailboxResponse: MailboxResponse = + await provider.queryContractSmart(hook, merkleMailboxQuery); + return merkleMailboxResponse.mailbox; + }; + + const requiredHookContract = await provider.getContract(requiredHook); + if (requiredHookContract.label === 'hpl_hook_pausable') { + summary.requiredHook = { + ...requiredHookContract, + owner: await getOwner(requiredHook), + mailbox: await getMailbox(requiredHook), + }; + } + // else if (requiredHookContract.label === 'hpl_hook_aggregate') { + // const aggregateHookQuery: AggregateQuery = { + // aggregate_hook: { + // hooks: {}, + // }, + // }; + // const hooksResponse: HooksResponse = await provider.queryContractSmart( + // requiredHook, + // aggregateHookQuery, + // ); + // summary.requiredHook = { + // ...requiredHookContract, + // hooks: hooksResponse.hooks, + // owner: await getOwner(requiredHook), + // mailbox: await getMailbox(requiredHook), + // }; + + const defaultHookContract = await provider.getContract(defaultHook); + if (defaultHookContract.label === 'hpl_hook_merkle') { + summary.defaultHook = defaultHookContract; + } + + // for (const hook of hooksResponse.hooks) { + // const hook = defaultHook; + // const hookContract = await provider.getContract(hook); + // if (hookContract.label === 'hpl_hook_merkle') { + // // summary.requiredHook.merkleHook = { + // summary.merkleHook = { + // ...hookContract, + // mailbox: await getMailbox(hook), + // owner: await getOwner(hook), + // }; + // } + // } else if (hookContract.label === 'hpl_igp') { + // const igpAdapter = new CosmWasmIgpAdapter( + // neutron.name, + // multiProtocolProvider, + // { igp: hook }, + // ); + // const oracles = await igpAdapter.getOracles(); + // const defaultGas = await igpAdapter.defaultGas(); + // const beneficiary = await igpAdapter.beneficiary(); + + // const mantaData = await igpAdapter.getOracleData(mantapacific.name); + // const igpOracle = oracles[mantapacific.name]; + + // summary.requiredHook.igpHook = { + // oracles, + // mantaOracle: { + // ...mantaData, + // owner: await getOwner(igpOracle), + // ...(await provider.getContract(igpOracle)), + // }, + // defaultGas, + // beneficiary, + // mailbox: await getMailbox(hook), + // owner: await getOwner(hook), + // ...hookContract, + // }; + // } + // } + + console.log(JSON.stringify(summary)); +} + +export async function rotateValidators() { + const multisigAdapter = new CosmWasmMultisigAdapter( + neutron.name, + multiProtocolProvider, + { + multisig: + 'neutron1q75ky8reksqzh0lkhk9k3csvjwv74jjquahrj233xc7dvzz5fv4qtvw0qg', + }, + ); + const instructions = await multisigAdapter.configureMultisig({ + [mantapacific.name]: { + threshold: 5, + validators: [ + '8e668c97ad76d0e28375275c41ece4972ab8a5bc', // hyperlane + '521a3e6bf8d24809fde1c1fd3494a859a16f132c', // cosmosstation + '25b9a0961c51e74fd83295293bc029131bf1e05a', // neutron (pablo) + '14025fe092f5f8a401dd9819704d9072196d2125', // p2p + 'a0ee95e280d46c14921e524b075d0c341e7ad1c8', // cosmos spaces + 'cc9a0b6de7fe314bd99223687d784730a75bb957', // dsrv + '42b6de2edbaa62c2ea2309ad85d20b3e37d38acf', // sg-1 + ], + }, + }); + + console.log(JSON.stringify(instructions)); + + // const safe = await getSigningClient( + // '2ac7230628b8b4a587c4005798184735471b9240fc57fc75d97824e1fb6b5409', + // ); + + // const tx = await safe.wasm.executeMultiple(safe.signer, instructions, 'auto'); + + // console.log(tx); +} + +summary().catch(console.error); diff --git a/typescript/sdk/src/token/adapters/CosmWasmTokenAdapter.ts b/typescript/sdk/src/token/adapters/CosmWasmTokenAdapter.ts new file mode 100644 index 000000000..594018cf9 --- /dev/null +++ b/typescript/sdk/src/token/adapters/CosmWasmTokenAdapter.ts @@ -0,0 +1,409 @@ +import { ExecuteInstruction } from '@cosmjs/cosmwasm-stargate'; +import { Coin } from '@cosmjs/stargate'; + +import { + Address, + Domain, + addressToBytes32, + strip0x, +} from '@hyperlane-xyz/utils'; + +import { BaseCosmWasmAdapter } from '../../app/MultiProtocolApp'; +import { + BalanceResponse, + ExecuteMsg as Cw20Execute, + QueryMsg as Cw20Query, + TokenInfoResponse, +} from '../../cw-types/Cw20Base.types'; +import { + DomainsResponse, + InterchainSecurityModuleResponse, + OwnerResponse, + RouteResponseForHexBinary, + RoutesResponseForHexBinary, + TokenType, + TokenTypeResponse, + ExecuteMsg as WarpCw20Execute, + QueryMsg as WarpCw20Query, +} from '../../cw-types/WarpCw20.types'; +import { MultiProtocolProvider } from '../../providers/MultiProtocolProvider'; +import { ChainName } from '../../types'; +import { ERC20Metadata } from '../config'; + +import { + IHypTokenAdapter, + ITokenAdapter, + TransferParams, + TransferRemoteParams, +} from './ITokenAdapter'; + +// Interacts with IBC denom tokens in CosmWasm +export class CwNativeTokenAdapter + extends BaseCosmWasmAdapter + implements ITokenAdapter +{ + constructor( + public readonly chainName: string, + public readonly multiProvider: MultiProtocolProvider, + public readonly addresses: Record, + public readonly ibcDenom: string = 'untrn', + ) { + super(chainName, multiProvider, addresses); + } + + async getBalance(address: Address): Promise { + const provider = await this.getProvider(); + const balance = await provider.getBalance(address, this.ibcDenom); + return balance.amount; + } + + async getMetadata(): Promise { + throw new Error('Metadata not available to native tokens'); + } + + async populateApproveTx( + _params: TransferParams, + ): Promise { + throw new Error('Approve not required for native tokens'); + } + + async populateTransferTx({ + recipient, + weiAmountOrId, + }: TransferParams): Promise { + // TODO: check if this works with execute instruction? (contract type, empty message) + return { + contractAddress: recipient, + msg: {}, + funds: [ + { + amount: weiAmountOrId.toString(), + denom: this.ibcDenom, + }, + ], + }; + } +} + +export type CW20Metadata = ERC20Metadata; +type CW20Response = TokenInfoResponse | BalanceResponse; + +// Interacts with CW20/721 contracts +export class CwTokenAdapter + extends BaseCosmWasmAdapter + implements ITokenAdapter +{ + constructor( + public readonly chainName: string, + public readonly multiProvider: MultiProtocolProvider, + public readonly addresses: { token: Address }, + public readonly denom = 'untrn', + ) { + super(chainName, multiProvider, addresses); + } + + async queryToken(msg: Cw20Query): Promise { + const provider = await this.getProvider(); + const response: R = await provider.queryContractSmart( + this.addresses.token, + msg, + ); + return response; + } + + prepareToken(msg: Cw20Execute, funds?: Coin[]): ExecuteInstruction { + return { + contractAddress: this.addresses.token, + msg, + funds, + }; + } + + async getBalance(address: Address): Promise { + const resp = await this.queryToken({ + balance: { + address, + }, + }); + return resp.balance; + } + + async getMetadata(): Promise { + const resp = await this.queryToken({ + token_info: {}, + }); + return { + ...resp, + totalSupply: resp.total_supply, + }; + } + + async populateApproveTx({ + weiAmountOrId, + recipient, + }: TransferParams): Promise { + // TODO: check existing allowance + return this.prepareToken({ + increase_allowance: { + spender: recipient, + amount: weiAmountOrId.toString(), + expires: { + never: {}, + }, + }, + }); + } + + async populateTransferTx({ + weiAmountOrId, + recipient, + }: TransferParams): Promise { + return this.prepareToken({ + transfer: { + recipient, + amount: weiAmountOrId.toString(), + }, + }); + } +} + +type TokenRouterResponse = + | TokenTypeResponse + | InterchainSecurityModuleResponse + | DomainsResponse + | OwnerResponse + | RouteResponseForHexBinary + | RoutesResponseForHexBinary; + +export class CwHypSyntheticAdapter + extends CwTokenAdapter + implements IHypTokenAdapter +{ + constructor( + public readonly chainName: ChainName, + public readonly multiProvider: MultiProtocolProvider, + public readonly addresses: { token: Address; warpRouter: Address }, + public readonly gasDenom = 'untrn', + ) { + super(chainName, multiProvider, addresses); + } + + async queryRouter( + msg: WarpCw20Query, + ): Promise { + const provider = await this.getProvider(); + const response: R = await provider.queryContractSmart( + this.addresses.warpRouter, + msg, + ); + return response; + } + + prepareRouter(msg: WarpCw20Execute, funds?: Coin[]): ExecuteInstruction { + return { + contractAddress: this.addresses.warpRouter, + msg, + funds, + }; + } + + async tokenType(): Promise { + const resp = await this.queryRouter({ + token_default: { + token_type: {}, + }, + }); + return resp.type; + } + + async interchainSecurityModule(): Promise
{ + throw new Error('Router does not support ISM config yet.'); + } + + async owner(): Promise
{ + const resp = await this.queryRouter({ + ownable: { + get_owner: {}, + }, + }); + return resp.owner; + } + + async getDomains(): Promise { + const resp = await this.queryRouter({ + router: { + domains: {}, + }, + }); + return resp.domains; + } + + async getRouterAddress(domain: Domain): Promise { + const resp = await this.queryRouter({ + router: { + get_route: { + domain, + }, + }, + }); + const route = resp.route.route; + if (!route) { + throw new Error(`No route found for domain ${domain}`); + } + return Buffer.from(route, 'hex'); + } + + async getAllRouters(): Promise> { + const resp = await this.queryRouter({ + router: { + list_routes: {}, + }, + }); + return resp.routes + .filter((r) => r.route != null) + .map((r) => ({ + domain: r.domain, + address: Buffer.from(r.route!, 'hex'), + })); + } + + quoteGasPayment(_destination: number): Promise { + throw new Error('Method not implemented.'); + } + + populateTransferRemoteTx({ + destination, + recipient, + weiAmountOrId, + txValue, + }: TransferRemoteParams): ExecuteInstruction { + if (!txValue) { + throw new Error('txValue is required for native tokens'); + } + return this.prepareRouter( + { + transfer_remote: { + dest_domain: destination, + recipient: strip0x(addressToBytes32(recipient)), + amount: weiAmountOrId.toString(), + }, + }, + [ + { + amount: txValue.toString(), + denom: this.gasDenom, + }, + ], + ); + } +} + +export class CwHypNativeAdapter + extends CwNativeTokenAdapter + implements IHypTokenAdapter +{ + private readonly cw20adapter: CwHypSyntheticAdapter; + + constructor( + public readonly chainName: ChainName, + public readonly multiProvider: MultiProtocolProvider, + public readonly addresses: { warpRouter: Address }, + public readonly gasDenom = 'untrn', + ) { + super(chainName, multiProvider, addresses, gasDenom); + this.cw20adapter = new CwHypSyntheticAdapter( + chainName, + multiProvider, + { token: '', warpRouter: addresses.warpRouter }, + gasDenom, + ); + } + + async getBalance(address: string): Promise { + const provider = await this.getProvider(); + const denom = await this.denom(); + const balance = await provider.getBalance(address, denom); + return balance.amount; + } + + async interchainSecurityModule(): Promise
{ + return this.cw20adapter.interchainSecurityModule(); + } + + async owner(): Promise
{ + return this.cw20adapter.owner(); + } + + async getDomains(): Promise { + return this.cw20adapter.getDomains(); + } + + async getRouterAddress(domain: Domain): Promise { + return this.cw20adapter.getRouterAddress(domain); + } + + async getAllRouters(): Promise> { + return this.cw20adapter.getAllRouters(); + } + + quoteGasPayment(destination: number): Promise { + return this.cw20adapter.quoteGasPayment(destination); + } + + async denom(): Promise { + const tokenType = await this.cw20adapter.tokenType(); + if ('native' in tokenType) { + if ('fungible' in tokenType.native) { + return tokenType.native.fungible.denom; + } + } + + throw new Error(`Token type not supported: ${tokenType}`); + } + + async populateTransferRemoteTx({ + destination, + recipient, + weiAmountOrId, + txValue, + }: TransferRemoteParams): Promise { + if (!txValue) { + throw new Error('txValue is required for native tokens'); + } + + const collateralDenom = await this.denom(); + return this.cw20adapter.prepareRouter( + { + transfer_remote: { + dest_domain: destination, + recipient: strip0x(addressToBytes32(recipient)), + amount: weiAmountOrId.toString(), + }, + }, + [ + { + amount: weiAmountOrId.toString(), + denom: collateralDenom, + }, + { + amount: txValue.toString(), + denom: this.gasDenom, + }, + ], + ); + } +} + +export class CwHypCollateralAdapter + extends CwHypNativeAdapter + implements IHypTokenAdapter +{ + constructor( + public readonly chainName: ChainName, + public readonly multiProvider: MultiProtocolProvider, + public readonly addresses: { warpRouter: Address; token: Address }, + public readonly gasDenom = 'untrn', + ) { + super(chainName, multiProvider, addresses, gasDenom); + } +} diff --git a/typescript/sdk/src/token/adapters/CosmosTokenAdapter.ts b/typescript/sdk/src/token/adapters/CosmosTokenAdapter.ts new file mode 100644 index 000000000..9830265a2 --- /dev/null +++ b/typescript/sdk/src/token/adapters/CosmosTokenAdapter.ts @@ -0,0 +1,65 @@ +import { MsgTransferEncodeObject } from '@cosmjs/stargate'; +import { MsgTransfer } from 'cosmjs-types/ibc/applications/transfer/v1/tx'; + +import { Address } from '@hyperlane-xyz/utils'; + +import { BaseCosmosAdapter } from '../../app/MultiProtocolApp'; +import { MultiProtocolProvider } from '../../providers/MultiProtocolProvider'; +import { MinimalTokenMetadata } from '../config'; + +import { ITokenAdapter, TransferParams } from './ITokenAdapter'; + +// Interacts with IBC denom tokens +export class NativeTokenAdapter + extends BaseCosmosAdapter + implements ITokenAdapter +{ + constructor( + public readonly chainName: string, + public readonly multiProvider: MultiProtocolProvider, + public readonly addresses: Record, + public readonly ibcDenom: string = 'untrn', + ) { + super(chainName, multiProvider, addresses); + } + + async getBalance(address: string): Promise { + const provider = await this.getProvider(); + const coin = await provider.getBalance(address, this.ibcDenom); + return coin.amount; + } + + getMetadata(): Promise { + throw new Error('Metadata not available to native tokens'); + } + + populateApproveTx(_transferParams: TransferParams): unknown { + throw new Error('Approve not required for native tokens'); + } + + async populateTransferTx( + transferParams: TransferParams, + ): Promise { + const transfer: MsgTransfer = { + sourcePort: '', + sourceChannel: '', + token: { + denom: this.ibcDenom, + amount: transferParams.weiAmountOrId.toString(), + }, + sender: '', + receiver: '', + timeoutHeight: { + revisionNumber: 0n, + revisionHeight: 0n, + }, + timeoutTimestamp: 0n, + memo: '', // how to encode this? + }; + return { + typeUrl: '/ibc.applications.transfer.v1.MsgTransfer', + // @ts-ignore + value: transfer, + }; + } +} diff --git a/typescript/sdk/src/token/adapters/SealevelTokenAdapter.ts b/typescript/sdk/src/token/adapters/SealevelTokenAdapter.ts index b6d3b0024..362288169 100644 --- a/typescript/sdk/src/token/adapters/SealevelTokenAdapter.ts +++ b/typescript/sdk/src/token/adapters/SealevelTokenAdapter.ts @@ -162,7 +162,7 @@ export class SealevelTokenAdapter // we generously request 1M units. const TRANSFER_REMOTE_COMPUTE_LIMIT = 1_000_000; -export abstract class SealevelHypTokenAdapter +abstract class SealevelHypTokenAdapter extends SealevelTokenAdapter implements IHypTokenAdapter { diff --git a/typescript/sdk/src/utils/wagmi.ts b/typescript/sdk/src/utils/wagmi.ts index 2f08d80b7..43d1589fd 100644 --- a/typescript/sdk/src/utils/wagmi.ts +++ b/typescript/sdk/src/utils/wagmi.ts @@ -1,20 +1,27 @@ import type { Chain as WagmiChain } from '@wagmi/chains'; -import { objMap } from '@hyperlane-xyz/utils'; +import { ProtocolType, objFilter, objMap } from '@hyperlane-xyz/utils'; import { chainMetadata, etherToken } from '../consts/chainMetadata'; -import type { ChainMetadata } from '../metadata/chainMetadataTypes'; +import { + ChainMetadata, + getChainIdNumber, +} from '../metadata/chainMetadataTypes'; import type { ChainMap } from '../types'; // For convenient use in wagmi-based apps export const wagmiChainMetadata: ChainMap = objMap( - chainMetadata, + objFilter( + chainMetadata, + (_, metadata): metadata is ChainMetadata => + metadata.protocol === ProtocolType.Ethereum, + ), (_, metadata) => chainMetadataToWagmiChain(metadata), ); export function chainMetadataToWagmiChain(metadata: ChainMetadata): WagmiChain { return { - id: metadata.chainId, + id: getChainIdNumber(metadata), name: metadata.displayName || metadata.name, network: metadata.name as string, nativeCurrency: metadata.nativeToken || etherToken, diff --git a/typescript/utils/index.ts b/typescript/utils/index.ts index a60d68b8d..e2351b6f6 100644 --- a/typescript/utils/index.ts +++ b/typescript/utils/index.ts @@ -2,27 +2,36 @@ export { addressToByteHexString, addressToBytes, addressToBytes32, + addressToBytesCosmos, addressToBytesEvm, addressToBytesSol, bytes32ToAddress, + bytesToAddressCosmos, + bytesToAddressEvm, + bytesToAddressSol, bytesToProtocolAddress, capitalizeAddress, convertToProtocolAddress, ensure0x, eqAddress, + eqAddressCosmos, eqAddressEvm, eqAddressSol, getAddressProtocolType, + isAddressCosmos, isAddressEvm, isAddressSealevel, isValidAddress, + isValidAddressCosmos, isValidAddressEvm, isValidAddressSealevel, isValidTransactionHash, + isValidTransactionHashCosmos, isValidTransactionHashEvm, isValidTransactionHashSealevel, isZeroishAddress, normalizeAddress, + normalizeAddressCosmos, normalizeAddressEvm, normalizeAddressSealevel, shortenAddress, diff --git a/typescript/utils/package.json b/typescript/utils/package.json index 54ff1856d..f2ce16c19 100644 --- a/typescript/utils/package.json +++ b/typescript/utils/package.json @@ -1,8 +1,9 @@ { "name": "@hyperlane-xyz/utils", "description": "General utilities and types for the Hyperlane network", - "version": "1.5.4-beta0", + "version": "3.1.0-beta0", "dependencies": { + "@cosmjs/encoding": "^0.31.3", "@solana/web3.js": "^1.78.0", "bignumber.js": "^9.1.1", "ethers": "^5.7.2" diff --git a/typescript/utils/src/addresses.ts b/typescript/utils/src/addresses.ts index 4ad170803..131c12c1c 100644 --- a/typescript/utils/src/addresses.ts +++ b/typescript/utils/src/addresses.ts @@ -1,3 +1,4 @@ +import { fromBech32, normalizeBech32, toBech32 } from '@cosmjs/encoding'; import { PublicKey } from '@solana/web3.js'; import { utils as ethersUtils } from 'ethers'; @@ -5,11 +6,16 @@ import { Address, HexString, ProtocolType } from './types'; const EVM_ADDRESS_REGEX = /^0x[a-fA-F0-9]{40}$/; const SEALEVEL_ADDRESS_REGEX = /^[a-zA-Z0-9]{36,44}$/; +const COSMOS_ADDRESS_REGEX = + /^[a-z]{1,10}1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{38,58}$/; // Bech32 +export const IBC_DENOM_REGEX = /^ibc\/([A-Fa-f0-9]{64})$/; const EVM_TX_HASH_REGEX = /^0x([A-Fa-f0-9]{64})$/; const SEALEVEL_TX_HASH_REGEX = /^[a-zA-Z1-9]{88}$/; +const COSMOS_TX_HASH_REGEX = /^(0x)?[A-Fa-f0-9]{64}$/; const ZEROISH_ADDRESS_REGEX = /^(0x)?0*$/; +const COSMOS_ZEROISH_ADDRESS_REGEX = /^[a-z]{1,10}?1[0]{38}$/; export function isAddressEvm(address: Address) { return EVM_ADDRESS_REGEX.test(address); @@ -19,10 +25,16 @@ export function isAddressSealevel(address: Address) { return SEALEVEL_ADDRESS_REGEX.test(address); } +export function isAddressCosmos(address: Address) { + return COSMOS_ADDRESS_REGEX.test(address) || IBC_DENOM_REGEX.test(address); +} + export function getAddressProtocolType(address: Address) { if (!address) return undefined; if (isAddressEvm(address)) { return ProtocolType.Ethereum; + } else if (isAddressCosmos(address)) { + return ProtocolType.Cosmos; } else if (isAddressSealevel(address)) { return ProtocolType.Sealevel; } else { @@ -31,20 +43,15 @@ export function getAddressProtocolType(address: Address) { } function routeAddressUtil( - evmFn: (param: string) => T, - sealevelFn: (param: string) => T, - fallback: T, + fns: Partial T>>, param: string, + fallback?: T, protocol?: ProtocolType, ) { - protocol = protocol || getAddressProtocolType(param); - if (protocol === ProtocolType.Ethereum) { - return evmFn(param); - } else if (protocol === ProtocolType.Sealevel) { - return sealevelFn(param); - } else { - return fallback; - } + protocol ||= getAddressProtocolType(param); + if (protocol && fns[protocol]) return fns[protocol]!(param); + else if (fallback) return fallback; + else throw new Error(`Unsupported protocol ${protocol}`); } // Slower than isAddressEvm above but actually validates content and checksum @@ -68,12 +75,26 @@ export function isValidAddressSealevel(address: Address) { } } +// Slower than isAddressCosmos above but actually validates content and checksum +export function isValidAddressCosmos(address: Address) { + try { + const isValid = + address && (IBC_DENOM_REGEX.test(address) || fromBech32(address)); + return !!isValid; + } catch (error) { + return false; + } +} + export function isValidAddress(address: Address, protocol?: ProtocolType) { return routeAddressUtil( - isValidAddressEvm, - isValidAddressSealevel, - false, + { + [ProtocolType.Ethereum]: isValidAddressEvm, + [ProtocolType.Sealevel]: isValidAddressSealevel, + [ProtocolType.Cosmos]: isValidAddressCosmos, + }, address, + false, protocol, ); } @@ -96,10 +117,22 @@ export function normalizeAddressSealevel(address: Address) { } } +export function normalizeAddressCosmos(address: Address) { + if (isZeroishAddress(address)) return address; + try { + return normalizeBech32(address); + } catch (error) { + return address; + } +} + export function normalizeAddress(address: Address, protocol?: ProtocolType) { return routeAddressUtil( - normalizeAddressEvm, - normalizeAddressSealevel, + { + [ProtocolType.Ethereum]: normalizeAddressEvm, + [ProtocolType.Sealevel]: normalizeAddressSealevel, + [ProtocolType.Cosmos]: normalizeAddressCosmos, + }, address, address, protocol, @@ -114,15 +147,22 @@ export function eqAddressSol(a1: Address, a2: Address) { return normalizeAddressSealevel(a1) === normalizeAddressSealevel(a2); } +export function eqAddressCosmos(a1: Address, a2: Address) { + return normalizeAddressCosmos(a1) === normalizeAddressCosmos(a2); +} + export function eqAddress(a1: Address, a2: Address) { const p1 = getAddressProtocolType(a1); const p2 = getAddressProtocolType(a2); if (p1 !== p2) return false; return routeAddressUtil( - (_a1) => eqAddressEvm(_a1, a2), - (_a1) => eqAddressSol(_a1, a2), - false, + { + [ProtocolType.Ethereum]: (_a1) => eqAddressEvm(_a1, a2), + [ProtocolType.Sealevel]: (_a1) => eqAddressSol(_a1, a2), + [ProtocolType.Cosmos]: (_a1) => eqAddressCosmos(_a1, a2), + }, a1, + false, p1, ); } @@ -135,18 +175,27 @@ export function isValidTransactionHashSealevel(input: string) { return SEALEVEL_TX_HASH_REGEX.test(input); } +export function isValidTransactionHashCosmos(input: string) { + return COSMOS_TX_HASH_REGEX.test(input); +} + export function isValidTransactionHash(input: string, protocol: ProtocolType) { if (protocol === ProtocolType.Ethereum) { return isValidTransactionHashEvm(input); } else if (protocol === ProtocolType.Sealevel) { return isValidTransactionHashSealevel(input); + } else if (protocol === ProtocolType.Cosmos) { + return isValidTransactionHashCosmos(input); } else { return false; } } export function isZeroishAddress(address: Address) { - return ZEROISH_ADDRESS_REGEX.test(address); + return ( + ZEROISH_ADDRESS_REGEX.test(address) || + COSMOS_ZEROISH_ADDRESS_REGEX.test(address) + ); } export function shortenAddress(address: Address, capitalize?: boolean) { @@ -166,31 +215,40 @@ export function capitalizeAddress(address: Address) { else return address.toUpperCase(); } +// For EVM addresses only, kept for backwards compatibility and convenience export function addressToBytes32(address: Address): string { return ethersUtils .hexZeroPad(ethersUtils.hexStripZeros(address), 32) .toLowerCase(); } +// For EVM addresses only, kept for backwards compatibility and convenience export function bytes32ToAddress(bytes32: HexString): Address { return ethersUtils.getAddress(bytes32.slice(-40)); } export function addressToBytesEvm(address: Address): Uint8Array { const addrBytes32 = addressToBytes32(address); - return Buffer.from(addrBytes32.substring(2), 'hex'); + return Buffer.from(strip0x(addrBytes32), 'hex'); } export function addressToBytesSol(address: Address): Uint8Array { return new PublicKey(address).toBytes(); } +export function addressToBytesCosmos(address: Address): Uint8Array { + return fromBech32(address).data; +} + export function addressToBytes(address: Address, protocol?: ProtocolType) { return routeAddressUtil( - addressToBytesEvm, - addressToBytesSol, - new Uint8Array(), + { + [ProtocolType.Ethereum]: addressToBytesEvm, + [ProtocolType.Sealevel]: addressToBytesSol, + [ProtocolType.Cosmos]: addressToBytesCosmos, + }, address, + new Uint8Array(), protocol, ); } @@ -199,17 +257,38 @@ export function addressToByteHexString( address: string, protocol?: ProtocolType, ) { - return '0x' + Buffer.from(addressToBytes(address, protocol)).toString('hex'); + return ensure0x( + Buffer.from(addressToBytes(address, protocol)).toString('hex'), + ); +} + +export function bytesToAddressEvm(bytes: Uint8Array): Address { + return bytes32ToAddress(Buffer.from(bytes).toString('hex')); +} + +export function bytesToAddressSol(bytes: Uint8Array): Address { + return new PublicKey(bytes).toBase58(); +} + +export function bytesToAddressCosmos( + bytes: Uint8Array, + prefix: string, +): Address { + if (!prefix) throw new Error('Prefix required for Cosmos address'); + return toBech32(prefix, bytes); } export function bytesToProtocolAddress( - bytes: Buffer, + bytes: Uint8Array, toProtocol: ProtocolType, + prefix?: string, ) { - if (toProtocol === ProtocolType.Sealevel) { - return new PublicKey(bytes).toBase58(); - } else if (toProtocol === ProtocolType.Ethereum) { - return bytes32ToAddress(bytes.toString('hex')); + if (toProtocol === ProtocolType.Ethereum) { + return bytesToAddressEvm(bytes); + } else if (toProtocol === ProtocolType.Sealevel) { + return bytesToAddressSol(bytes); + } else if (toProtocol === ProtocolType.Cosmos) { + return bytesToAddressCosmos(bytes, prefix!); } else { throw new Error(`Unsupported protocol for address ${toProtocol}`); } @@ -218,30 +297,14 @@ export function bytesToProtocolAddress( export function convertToProtocolAddress( address: string, protocol: ProtocolType, + prefix?: string, ) { const currentProtocol = getAddressProtocolType(address); + if (!currentProtocol) + throw new Error(`Unknown address protocol for ${address}`); if (currentProtocol === protocol) return address; - if ( - currentProtocol === ProtocolType.Ethereum && - protocol === ProtocolType.Sealevel - ) { - return new PublicKey( - addressToBytes(address, ProtocolType.Ethereum), - ).toBase58(); - } else if ( - currentProtocol === ProtocolType.Sealevel && - protocol === ProtocolType.Ethereum - ) { - return bytes32ToAddress( - Buffer.from(addressToBytes(address, ProtocolType.Sealevel)).toString( - 'hex', - ), - ); - } else { - throw new Error( - `Unsupported protocol combination ${currentProtocol} -> ${protocol}`, - ); - } + const addressBytes = addressToBytes(address, currentProtocol); + return bytesToProtocolAddress(addressBytes, protocol, prefix); } export function ensure0x(hexstr: string) { diff --git a/typescript/utils/src/types.ts b/typescript/utils/src/types.ts index 525f26feb..81799c6b4 100644 --- a/typescript/utils/src/types.ts +++ b/typescript/utils/src/types.ts @@ -4,6 +4,7 @@ export enum ProtocolType { Ethereum = 'ethereum', Sealevel = 'sealevel', Fuel = 'fuel', + Cosmos = 'cosmos', } // A type that also allows for literal values of the enum export type ProtocolTypeValue = `${ProtocolType}`; @@ -11,6 +12,7 @@ export type ProtocolTypeValue = `${ProtocolType}`; export const ProtocolSmallestUnit = { [ProtocolType.Ethereum]: 'wei', [ProtocolType.Sealevel]: 'lamports', + [ProtocolType.Cosmos]: 'uATOM', }; /********* BASIC TYPES *********/ diff --git a/yarn.lock b/yarn.lock index a59e9cfca..fcf86ba33 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2758,6 +2758,173 @@ __metadata: languageName: node linkType: hard +"@confio/ics23@npm:^0.6.8": + version: 0.6.8 + resolution: "@confio/ics23@npm:0.6.8" + dependencies: + "@noble/hashes": ^1.0.0 + protobufjs: ^6.8.8 + checksum: 376d72f6440db60611b002b00a13e3a5bfd0d3503e7682358dbcf79641e74d8c26c234c321452fb4a758baf66eecef25d950e08bdea270486d9d03ee489e2960 + languageName: node + linkType: hard + +"@cosmjs/amino@npm:^0.31.3": + version: 0.31.3 + resolution: "@cosmjs/amino@npm:0.31.3" + dependencies: + "@cosmjs/crypto": ^0.31.3 + "@cosmjs/encoding": ^0.31.3 + "@cosmjs/math": ^0.31.3 + "@cosmjs/utils": ^0.31.3 + checksum: 30e55ed256e1ba8a84b8a92062fd48aed43b1d638b8917af8b28ae007a1eff3ffc9ffb743400db23c583dc2fefae12c3dd8b315451a09f6da9c10a07ce714dfa + languageName: node + linkType: hard + +"@cosmjs/cosmwasm-stargate@npm:^0.31.3": + version: 0.31.3 + resolution: "@cosmjs/cosmwasm-stargate@npm:0.31.3" + dependencies: + "@cosmjs/amino": ^0.31.3 + "@cosmjs/crypto": ^0.31.3 + "@cosmjs/encoding": ^0.31.3 + "@cosmjs/math": ^0.31.3 + "@cosmjs/proto-signing": ^0.31.3 + "@cosmjs/stargate": ^0.31.3 + "@cosmjs/tendermint-rpc": ^0.31.3 + "@cosmjs/utils": ^0.31.3 + cosmjs-types: ^0.8.0 + long: ^4.0.0 + pako: ^2.0.2 + checksum: 7f5a4c809fb3b54fa70887a2e7cf2bc9c2a460891a1cd01f6721f8dbe43efa0d6611b642c2e3601c779295008dbe922e8a9985ffecc3bca55533fb43d83d000d + languageName: node + linkType: hard + +"@cosmjs/crypto@npm:^0.31.3": + version: 0.31.3 + resolution: "@cosmjs/crypto@npm:0.31.3" + dependencies: + "@cosmjs/encoding": ^0.31.3 + "@cosmjs/math": ^0.31.3 + "@cosmjs/utils": ^0.31.3 + "@noble/hashes": ^1 + bn.js: ^5.2.0 + elliptic: ^6.5.4 + libsodium-wrappers-sumo: ^0.7.11 + checksum: e562bbcb7cce2c2992aa7fc808fb2b9bcc6d6a27b2567323f41349e7e1aca1b8a4e5b6e0442512cdd7e2bbe54f4b6a0b7ccf71eb574522d0bc405e609dcece8c + languageName: node + linkType: hard + +"@cosmjs/encoding@npm:^0.31.3": + version: 0.31.3 + resolution: "@cosmjs/encoding@npm:0.31.3" + dependencies: + base64-js: ^1.3.0 + bech32: ^1.1.4 + readonly-date: ^1.0.0 + checksum: dadef0579828299be20a64edf820ac8770c0cc47a842594bc9b494f160a347b745941d795360755ccbe385b9d0912aa54753479d1a70ff762d2d334693952ff9 + languageName: node + linkType: hard + +"@cosmjs/json-rpc@npm:^0.31.3": + version: 0.31.3 + resolution: "@cosmjs/json-rpc@npm:0.31.3" + dependencies: + "@cosmjs/stream": ^0.31.3 + xstream: ^11.14.0 + checksum: 6f050cf0d02f2a4f9df5391cc77e661684e5c7cc1df0fb71ae903984cb4f10cc765c08e866e417323910cbc63b91e30c38b7f2585ef5e473a8b086cddacc882a + languageName: node + linkType: hard + +"@cosmjs/math@npm:^0.31.3": + version: 0.31.3 + resolution: "@cosmjs/math@npm:0.31.3" + dependencies: + bn.js: ^5.2.0 + checksum: 1685ad41ed78e78854649ca933817c56d39f4b36bba59b5dbdb1728048f431da5531265f4d77bfc9280cdea6c368817109b9f4540d5cfc2093f6ea6ff9e9a8d2 + languageName: node + linkType: hard + +"@cosmjs/proto-signing@npm:^0.31.3": + version: 0.31.3 + resolution: "@cosmjs/proto-signing@npm:0.31.3" + dependencies: + "@cosmjs/amino": ^0.31.3 + "@cosmjs/crypto": ^0.31.3 + "@cosmjs/encoding": ^0.31.3 + "@cosmjs/math": ^0.31.3 + "@cosmjs/utils": ^0.31.3 + cosmjs-types: ^0.8.0 + long: ^4.0.0 + checksum: c27c4d921c99f5c06ac92ebba59e78c53b7c115334932dd1365263b98c1a67c7323e3a69ae933babf5a36682c019bbc7da3c9597ca1bf1a4858546bdd681453a + languageName: node + linkType: hard + +"@cosmjs/socket@npm:^0.31.3": + version: 0.31.3 + resolution: "@cosmjs/socket@npm:0.31.3" + dependencies: + "@cosmjs/stream": ^0.31.3 + isomorphic-ws: ^4.0.1 + ws: ^7 + xstream: ^11.14.0 + checksum: 29cc5120732a3badd0d3e4358aa645aa6ad50fedf4a619e2a99a2ec85274bc6df9791f0fb9674417b6eca72762916e8f25277fafb318f3e0a77effa2c52da16b + languageName: node + linkType: hard + +"@cosmjs/stargate@npm:^0.31.3": + version: 0.31.3 + resolution: "@cosmjs/stargate@npm:0.31.3" + dependencies: + "@confio/ics23": ^0.6.8 + "@cosmjs/amino": ^0.31.3 + "@cosmjs/encoding": ^0.31.3 + "@cosmjs/math": ^0.31.3 + "@cosmjs/proto-signing": ^0.31.3 + "@cosmjs/stream": ^0.31.3 + "@cosmjs/tendermint-rpc": ^0.31.3 + "@cosmjs/utils": ^0.31.3 + cosmjs-types: ^0.8.0 + long: ^4.0.0 + protobufjs: ~6.11.3 + xstream: ^11.14.0 + checksum: 9b680d50f0818e3cfaffccd022d6034c283c1e350a1b8d8f74ffa22352e342ce1cb00533007ba7b5a6a1c1bc30fe327bd09c23ac8b7486691ad127a34c47690c + languageName: node + linkType: hard + +"@cosmjs/stream@npm:^0.31.3": + version: 0.31.3 + resolution: "@cosmjs/stream@npm:0.31.3" + dependencies: + xstream: ^11.14.0 + checksum: 0d273604af4d7093b877582e223eedbcce4a1a4d7d9f80a4f5e215fd8be42ea6546f3778cc918cb0cdb144de52e7d8d4c476b9b4c6f678cebe914224f54d19ad + languageName: node + linkType: hard + +"@cosmjs/tendermint-rpc@npm:^0.31.3": + version: 0.31.3 + resolution: "@cosmjs/tendermint-rpc@npm:0.31.3" + dependencies: + "@cosmjs/crypto": ^0.31.3 + "@cosmjs/encoding": ^0.31.3 + "@cosmjs/json-rpc": ^0.31.3 + "@cosmjs/math": ^0.31.3 + "@cosmjs/socket": ^0.31.3 + "@cosmjs/stream": ^0.31.3 + "@cosmjs/utils": ^0.31.3 + axios: ^0.21.2 + readonly-date: ^1.0.0 + xstream: ^11.14.0 + checksum: 403e220ee4aeb65977a4416d48930d7381e3d4c10bf300fa6f07698c72b85a55f2314ba1a3d45849ce8549de2ff2005988188fc5fe60ac09188edbb89692115d + languageName: node + linkType: hard + +"@cosmjs/utils@npm:^0.31.3": + version: 0.31.3 + resolution: "@cosmjs/utils@npm:0.31.3" + checksum: 2ff2b270954ab00cc5ae8f23625b562676d0a061c8076905509a5f0701e302e46d24a51a0c3283072e0ce01fbd860baceb25e62303ff17826672fe5f8674b00d + languageName: node + linkType: hard + "@cspotcode/source-map-support@npm:^0.8.0": version: 0.8.1 resolution: "@cspotcode/source-map-support@npm:0.8.1" @@ -3930,12 +4097,12 @@ __metadata: languageName: node linkType: hard -"@hyperlane-xyz/core@1.5.4-beta0, @hyperlane-xyz/core@workspace:solidity": +"@hyperlane-xyz/core@3.1.0-beta0, @hyperlane-xyz/core@workspace:solidity": version: 0.0.0-use.local resolution: "@hyperlane-xyz/core@workspace:solidity" dependencies: "@eth-optimism/contracts": ^0.6.0 - "@hyperlane-xyz/utils": 1.5.4-beta0 + "@hyperlane-xyz/utils": 3.1.0-beta0 "@nomiclabs/hardhat-ethers": ^2.2.1 "@nomiclabs/hardhat-waffle": ^2.0.3 "@openzeppelin/contracts": ^4.8.0 @@ -3958,12 +4125,12 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/helloworld@1.5.4-beta0, @hyperlane-xyz/helloworld@workspace:typescript/helloworld": +"@hyperlane-xyz/helloworld@3.1.0-beta0, @hyperlane-xyz/helloworld@workspace:typescript/helloworld": version: 0.0.0-use.local resolution: "@hyperlane-xyz/helloworld@workspace:typescript/helloworld" dependencies: - "@hyperlane-xyz/core": 1.5.4-beta0 - "@hyperlane-xyz/sdk": 1.5.4-beta0 + "@hyperlane-xyz/core": 3.1.0-beta0 + "@hyperlane-xyz/sdk": 3.1.0-beta0 "@nomiclabs/hardhat-ethers": ^2.2.1 "@nomiclabs/hardhat-waffle": ^2.0.3 "@openzeppelin/contracts-upgradeable": ^4.8.0 @@ -4003,9 +4170,9 @@ __metadata: "@ethersproject/experimental": ^5.7.0 "@ethersproject/hardware-wallets": ^5.7.0 "@ethersproject/providers": ^5.7.2 - "@hyperlane-xyz/helloworld": 1.5.4-beta0 - "@hyperlane-xyz/sdk": 1.5.4-beta0 - "@hyperlane-xyz/utils": 1.5.4-beta0 + "@hyperlane-xyz/helloworld": 3.1.0-beta0 + "@hyperlane-xyz/sdk": 3.1.0-beta0 + "@hyperlane-xyz/utils": 3.1.0-beta0 "@nomiclabs/hardhat-ethers": ^2.2.1 "@nomiclabs/hardhat-etherscan": ^3.0.3 "@nomiclabs/hardhat-waffle": ^2.0.3 @@ -4049,12 +4216,14 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/sdk@1.5.4-beta0, @hyperlane-xyz/sdk@workspace:typescript/sdk": +"@hyperlane-xyz/sdk@3.1.0-beta0, @hyperlane-xyz/sdk@workspace:typescript/sdk": version: 0.0.0-use.local resolution: "@hyperlane-xyz/sdk@workspace:typescript/sdk" dependencies: - "@hyperlane-xyz/core": 1.5.4-beta0 - "@hyperlane-xyz/utils": 1.5.4-beta0 + "@cosmjs/cosmwasm-stargate": ^0.31.3 + "@cosmjs/stargate": ^0.31.3 + "@hyperlane-xyz/core": 3.1.0-beta0 + "@hyperlane-xyz/utils": 3.1.0-beta0 "@nomiclabs/hardhat-ethers": ^2.2.1 "@nomiclabs/hardhat-waffle": ^2.0.3 "@solana/spl-token": ^0.3.8 @@ -4066,6 +4235,7 @@ __metadata: "@wagmi/chains": ^0.2.6 chai: ^4.3.6 coingecko-api: ^1.0.10 + cosmjs-types: ^0.9.0 cross-fetch: ^3.1.5 debug: ^4.3.4 dotenv: ^10.0.0 @@ -4084,10 +4254,11 @@ __metadata: languageName: unknown linkType: soft -"@hyperlane-xyz/utils@1.5.4-beta0, @hyperlane-xyz/utils@workspace:typescript/utils": +"@hyperlane-xyz/utils@3.1.0-beta0, @hyperlane-xyz/utils@workspace:typescript/utils": version: 0.0.0-use.local resolution: "@hyperlane-xyz/utils@workspace:typescript/utils" dependencies: + "@cosmjs/encoding": ^0.31.3 "@solana/web3.js": ^1.78.0 bignumber.js: ^9.1.1 chai: ^4.3.0 @@ -4312,7 +4483,7 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:^1.3.1": +"@noble/hashes@npm:^1, @noble/hashes@npm:^1.0.0, @noble/hashes@npm:^1.3.1": version: 1.3.2 resolution: "@noble/hashes@npm:1.3.2" checksum: fe23536b436539d13f90e4b9be843cc63b1b17666a07634a2b1259dded6f490be3d050249e6af98076ea8f2ea0d56f578773c2197f2aa0eeaa5fba5bc18ba474 @@ -4869,6 +5040,79 @@ __metadata: languageName: node linkType: hard +"@protobufjs/aspromise@npm:^1.1.1, @protobufjs/aspromise@npm:^1.1.2": + version: 1.1.2 + resolution: "@protobufjs/aspromise@npm:1.1.2" + checksum: 011fe7ef0826b0fd1a95935a033a3c0fd08483903e1aa8f8b4e0704e3233406abb9ee25350ec0c20bbecb2aad8da0dcea58b392bbd77d6690736f02c143865d2 + languageName: node + linkType: hard + +"@protobufjs/base64@npm:^1.1.2": + version: 1.1.2 + resolution: "@protobufjs/base64@npm:1.1.2" + checksum: 67173ac34de1e242c55da52c2f5bdc65505d82453893f9b51dc74af9fe4c065cf4a657a4538e91b0d4a1a1e0a0642215e31894c31650ff6e3831471061e1ee9e + languageName: node + linkType: hard + +"@protobufjs/codegen@npm:^2.0.4": + version: 2.0.4 + resolution: "@protobufjs/codegen@npm:2.0.4" + checksum: 59240c850b1d3d0b56d8f8098dd04787dcaec5c5bd8de186fa548de86b86076e1c50e80144b90335e705a044edf5bc8b0998548474c2a10a98c7e004a1547e4b + languageName: node + linkType: hard + +"@protobufjs/eventemitter@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/eventemitter@npm:1.1.0" + checksum: 0369163a3d226851682f855f81413cbf166cd98f131edb94a0f67f79e75342d86e89df9d7a1df08ac28be2bc77e0a7f0200526bb6c2a407abbfee1f0262d5fd7 + languageName: node + linkType: hard + +"@protobufjs/fetch@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/fetch@npm:1.1.0" + dependencies: + "@protobufjs/aspromise": ^1.1.1 + "@protobufjs/inquire": ^1.1.0 + checksum: 3fce7e09eb3f1171dd55a192066450f65324fd5f7cc01a431df01bb00d0a895e6bfb5b0c5561ce157ee1d886349c90703d10a4e11a1a256418ff591b969b3477 + languageName: node + linkType: hard + +"@protobufjs/float@npm:^1.0.2": + version: 1.0.2 + resolution: "@protobufjs/float@npm:1.0.2" + checksum: 5781e1241270b8bd1591d324ca9e3a3128d2f768077a446187a049e36505e91bc4156ed5ac3159c3ce3d2ba3743dbc757b051b2d723eea9cd367bfd54ab29b2f + languageName: node + linkType: hard + +"@protobufjs/inquire@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/inquire@npm:1.1.0" + checksum: ca06f02eaf65ca36fb7498fc3492b7fc087bfcc85c702bac5b86fad34b692bdce4990e0ef444c1e2aea8c034227bd1f0484be02810d5d7e931c55445555646f4 + languageName: node + linkType: hard + +"@protobufjs/path@npm:^1.1.2": + version: 1.1.2 + resolution: "@protobufjs/path@npm:1.1.2" + checksum: 856eeb532b16a7aac071cacde5c5620df800db4c80cee6dbc56380524736205aae21e5ae47739114bf669ab5e8ba0e767a282ad894f3b5e124197cb9224445ee + languageName: node + linkType: hard + +"@protobufjs/pool@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/pool@npm:1.1.0" + checksum: d6a34fbbd24f729e2a10ee915b74e1d77d52214de626b921b2d77288bd8f2386808da2315080f2905761527cceffe7ec34c7647bd21a5ae41a25e8212ff79451 + languageName: node + linkType: hard + +"@protobufjs/utf8@npm:^1.1.0": + version: 1.1.0 + resolution: "@protobufjs/utf8@npm:1.1.0" + checksum: f9bf3163d13aaa3b6f5e6fbf37a116e094ea021c0e1f2a7ccd0e12a29e2ce08dafba4e8b36e13f8ed7397e1591610ce880ed1289af4d66cf4ace8a36a9557278 + languageName: node + linkType: hard + "@resolver-engine/core@npm:^0.3.3": version: 0.3.3 resolution: "@resolver-engine/core@npm:0.3.3" @@ -5486,6 +5730,13 @@ __metadata: languageName: node linkType: hard +"@types/long@npm:^4.0.1": + version: 4.0.2 + resolution: "@types/long@npm:4.0.2" + checksum: d16cde7240d834cf44ba1eaec49e78ae3180e724cd667052b194a372f350d024cba8dd3f37b0864931683dab09ca935d52f0c4c1687178af5ada9fc85b0635f4 + languageName: node + linkType: hard + "@types/lru-cache@npm:^5.1.0": version: 5.1.1 resolution: "@types/lru-cache@npm:5.1.1" @@ -5540,6 +5791,15 @@ __metadata: languageName: node linkType: hard +"@types/node@npm:>=13.7.0": + version: 20.8.9 + resolution: "@types/node@npm:20.8.9" + dependencies: + undici-types: ~5.26.4 + checksum: 0c05f3502a9507ff27e91dd6fd574fa6f391b3fafedcfe8e0c8d33351fb22d02c0121f854e5b6b3ecb9a8a468407ddf6e7ac0029fb236d4c7e1361ffc758a01f + languageName: node + linkType: hard + "@types/node@npm:^10.0.3": version: 10.17.60 resolution: "@types/node@npm:10.17.60" @@ -6601,6 +6861,15 @@ __metadata: languageName: node linkType: hard +"axios@npm:^0.21.2": + version: 0.21.4 + resolution: "axios@npm:0.21.4" + dependencies: + follow-redirects: ^1.14.0 + checksum: 44245f24ac971e7458f3120c92f9d66d1fc695e8b97019139de5b0cc65d9b8104647db01e5f46917728edfc0cfd88eb30fc4c55e6053eef4ace76768ce95ff3c + languageName: node + linkType: hard + "babel-code-frame@npm:^6.26.0": version: 6.26.0 resolution: "babel-code-frame@npm:6.26.0" @@ -7255,7 +7524,7 @@ __metadata: languageName: node linkType: hard -"base64-js@npm:^1.3.1": +"base64-js@npm:^1.3.0, base64-js@npm:^1.3.1": version: 1.5.1 resolution: "base64-js@npm:1.5.1" checksum: 669632eb3745404c2f822a18fc3a0122d2f9a7a13f7fb8b5823ee19d1d2ff9ee5b52c53367176ea4ad093c332fd5ab4bd0ebae5a8e27917a4105a4cfc86b1005 @@ -7286,7 +7555,7 @@ __metadata: languageName: node linkType: hard -"bech32@npm:1.1.4": +"bech32@npm:1.1.4, bech32@npm:^1.1.4": version: 1.1.4 resolution: "bech32@npm:1.1.4" checksum: 0e98db619191548390d6f09ff68b0253ba7ae6a55db93dfdbb070ba234c1fd3308c0606fbcc95fad50437227b10011e2698b89f0181f6e7f845c499bd14d0f4b @@ -8559,6 +8828,23 @@ __metadata: languageName: node linkType: hard +"cosmjs-types@npm:^0.8.0": + version: 0.8.0 + resolution: "cosmjs-types@npm:0.8.0" + dependencies: + long: ^4.0.0 + protobufjs: ~6.11.2 + checksum: 99714ec956d2cb2e521d39896c9c9a24cf9df0d370265c203646ea015b51e86472efc0cb11f67a80f0649d178b0bcff77ac659e67fdfc8b2437cd7a42018577f + languageName: node + linkType: hard + +"cosmjs-types@npm:^0.9.0": + version: 0.9.0 + resolution: "cosmjs-types@npm:0.9.0" + checksum: 9b00d169eca334f27418bb80b39e0cff0196af40b0079e1f85536246059279207b853bdb6ec224ead0a02d15d4b7f6bf16bc096d41c436426aa5f8976ed2b430 + languageName: node + linkType: hard + "crc-32@npm:^1.2.0": version: 1.2.2 resolution: "crc-32@npm:1.2.2" @@ -10866,6 +11152,16 @@ __metadata: languageName: node linkType: hard +"follow-redirects@npm:^1.14.0": + version: 1.15.3 + resolution: "follow-redirects@npm:1.15.3" + peerDependenciesMeta: + debug: + optional: true + checksum: 584da22ec5420c837bd096559ebfb8fe69d82512d5585004e36a3b4a6ef6d5905780e0c74508c7b72f907d1fa2b7bd339e613859e9c304d0dc96af2027fd0231 + languageName: node + linkType: hard + "for-each@npm:^0.3.3, for-each@npm:~0.3.3": version: 0.3.3 resolution: "for-each@npm:0.3.3" @@ -11501,6 +11797,15 @@ __metadata: languageName: node linkType: hard +"globalthis@npm:^1.0.1": + version: 1.0.3 + resolution: "globalthis@npm:1.0.3" + dependencies: + define-properties: ^1.1.3 + checksum: fbd7d760dc464c886d0196166d92e5ffb4c84d0730846d6621a39fbbc068aeeb9c8d1421ad330e94b7bca4bb4ea092f5f21f3d36077812af5d098b4dc006c998 + languageName: node + linkType: hard + "globby@npm:^10.0.1": version: 10.0.2 resolution: "globby@npm:10.0.2" @@ -13531,6 +13836,22 @@ __metadata: languageName: node linkType: hard +"libsodium-sumo@npm:^0.7.13": + version: 0.7.13 + resolution: "libsodium-sumo@npm:0.7.13" + checksum: d0905530c53c27a0c01348eed8abc2ecf3725c0647545cc528ea4bbd0ee63b7a471b56abefec5b293086ee64b5ba7cf911a655cd2c36f400a4bfec6e2d152ebd + languageName: node + linkType: hard + +"libsodium-wrappers-sumo@npm:^0.7.11": + version: 0.7.13 + resolution: "libsodium-wrappers-sumo@npm:0.7.13" + dependencies: + libsodium-sumo: ^0.7.13 + checksum: cdaa7ae5d64e71e860b40b5f2fbaec156adc7bc5606f7d32655b6ab84c9878fd90b3a41e99cb96380f0b5727d1ee1c6ad5b440bff35ce8289832e5c8cac99973 + languageName: node + linkType: hard + "lilconfig@npm:2.0.5": version: 2.0.5 resolution: "lilconfig@npm:2.0.5" @@ -13698,6 +14019,13 @@ __metadata: languageName: node linkType: hard +"long@npm:^4.0.0": + version: 4.0.0 + resolution: "long@npm:4.0.0" + checksum: 16afbe8f749c7c849db1f4de4e2e6a31ac6e617cead3bdc4f9605cb703cd20e1e9fc1a7baba674ffcca57d660a6e5b53a9e236d7b25a295d3855cca79cc06744 + languageName: node + linkType: hard + "looper@npm:^2.0.0": version: 2.0.0 resolution: "looper@npm:2.0.0" @@ -15325,6 +15653,13 @@ __metadata: languageName: node linkType: hard +"pako@npm:^2.0.2": + version: 2.1.0 + resolution: "pako@npm:2.1.0" + checksum: 71666548644c9a4d056bcaba849ca6fd7242c6cf1af0646d3346f3079a1c7f4a66ffec6f7369ee0dc88f61926c10d6ab05da3e1fca44b83551839e89edd75a3e + languageName: node + linkType: hard + "parent-module@npm:^1.0.0": version: 1.0.1 resolution: "parent-module@npm:1.0.1" @@ -15841,6 +16176,30 @@ __metadata: languageName: node linkType: hard +"protobufjs@npm:^6.8.8, protobufjs@npm:~6.11.2, protobufjs@npm:~6.11.3": + version: 6.11.4 + resolution: "protobufjs@npm:6.11.4" + dependencies: + "@protobufjs/aspromise": ^1.1.2 + "@protobufjs/base64": ^1.1.2 + "@protobufjs/codegen": ^2.0.4 + "@protobufjs/eventemitter": ^1.1.0 + "@protobufjs/fetch": ^1.1.0 + "@protobufjs/float": ^1.0.2 + "@protobufjs/inquire": ^1.1.0 + "@protobufjs/path": ^1.1.2 + "@protobufjs/pool": ^1.1.0 + "@protobufjs/utf8": ^1.1.0 + "@types/long": ^4.0.1 + "@types/node": ">=13.7.0" + long: ^4.0.0 + bin: + pbjs: bin/pbjs + pbts: bin/pbts + checksum: b2fc6a01897b016c2a7e43a854ab4a3c57080f61be41e552235436e7a730711b8e89e47cb4ae52f0f065b5ab5d5989fc932f390337ce3a8ccf07203415700850 + languageName: node + linkType: hard + "proxy-addr@npm:~2.0.7": version: 2.0.7 resolution: "proxy-addr@npm:2.0.7" @@ -16193,6 +16552,13 @@ __metadata: languageName: node linkType: hard +"readonly-date@npm:^1.0.0": + version: 1.0.0 + resolution: "readonly-date@npm:1.0.0" + checksum: 78481e2abf3c2f9bc526029458aee3e2b1c476ca1434c4cc9db5c9aba51bf8f1323c1995d764ff01f2055b01f13e05416b2e14b387f644b0a5a56554c3ee9d0a + languageName: node + linkType: hard + "rechoir@npm:^0.6.2": version: 0.6.2 resolution: "rechoir@npm:0.6.2" @@ -17901,6 +18267,13 @@ __metadata: languageName: node linkType: hard +"symbol-observable@npm:^2.0.3": + version: 2.0.3 + resolution: "symbol-observable@npm:2.0.3" + checksum: 533dcf7a7925bada60dbaa06d678e7c4966dbf0959ccba7f60c22b0494ba5d9160d6a66f2951d45a80bf20e655a89f8b91c5f0458dd12faef28716b54f91f49c + languageName: node + linkType: hard + "sync-request@npm:^6.0.0": version: 6.1.0 resolution: "sync-request@npm:6.1.0" @@ -18648,6 +19021,13 @@ __metadata: languageName: node linkType: hard +"undici-types@npm:~5.26.4": + version: 5.26.5 + resolution: "undici-types@npm:5.26.5" + checksum: 3192ef6f3fd5df652f2dc1cd782b49d6ff14dc98e5dced492aa8a8c65425227da5da6aafe22523c67f035a272c599bb89cfe803c1db6311e44bed3042fc25487 + languageName: node + linkType: hard + "undici@npm:^5.11": version: 5.11.0 resolution: "undici@npm:5.11.0" @@ -20005,7 +20385,7 @@ __metadata: languageName: node linkType: hard -"ws@npm:^7.4.5": +"ws@npm:^7, ws@npm:^7.4.5": version: 7.5.9 resolution: "ws@npm:7.5.9" peerDependencies: @@ -20102,6 +20482,16 @@ __metadata: languageName: node linkType: hard +"xstream@npm:^11.14.0": + version: 11.14.0 + resolution: "xstream@npm:11.14.0" + dependencies: + globalthis: ^1.0.1 + symbol-observable: ^2.0.3 + checksum: eb96b5f9cd7e6a30d18688f337b8d1c658c85bb08754f2af4247275e25c0605c8435ad8125e04ad7d606c1b760fab4679841906f92718f35f8ce327074e1375a + languageName: node + linkType: hard + "xtend@npm:^4.0.0, xtend@npm:^4.0.1, xtend@npm:~4.0.0, xtend@npm:~4.0.1": version: 4.0.2 resolution: "xtend@npm:4.0.2"