diff --git a/README.md b/README.md index c57916793..6c7039ca8 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,21 @@ See the "Arbitrary Token" example in [the config docs](https://docs.wormhole.com Please note you have to [register a token](https://portalbridge.com/advanced-tools/#/register) with the token bridge before you can use it in Connect. +### Configuring Custom NTT (Native Token Transfer) Groups +To configure custom NTT groups, include an `nttGroups` key in your configuration. + +```ts +const config: WormholeConnectConfig = { + nttGroups: { + // Your custom NTT groups go here + } +} +``` + +For a practical example of how to structure your custom NTT groups, refer to the [nttGroups.ts](https://github.com/wormhole-foundation/wormhole-connect/blob/development/wormhole-connect/src/config/testnet/nttGroups.ts) file. + +Please note that the `tokenKey` specified in your custom NTT group must correspond to an existing entry in the tokensConfig, whether it's a built-in or a custom token. + ### Custom Theme You can also customize Connect's color scheme by providing a `WormholeConnectTheme` as the `theme` prop. diff --git a/builder/src/consts.ts b/builder/src/consts.ts index 6795c9342..5f9238a8d 100644 --- a/builder/src/consts.ts +++ b/builder/src/consts.ts @@ -253,6 +253,20 @@ export const ROUTE_INFOS: RouteInfo[] = [ title: "wstETH Bridge", description: "Permissionlessly transfer wstETH cross-chain with Wormhole.", }, + { + key: "nttManual", + title: "Native Token Transfer", + description: + "Permissionlessly transfer native tokens cross-chain with Wormhole.", + link: "https://github.com/wormhole-foundation/example-native-token-transfers/blob/main/README.md", + }, + { + key: "nttRelay", + title: "Native Token Transfer Automatic Redeems", + description: + "Automatic redeems for Native Token Transfers powered by xLabs.", + link: "https://github.com/wormhole-foundation/example-native-token-transfers/blob/main/README.md", + }, ]; export const ROUTES = ROUTE_INFOS.map((r) => r.key); diff --git a/package-lock.json b/package-lock.json index 329a67e27..b9288ad81 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2229,6 +2229,66 @@ "pbts": "bin/pbts" } }, + "node_modules/@coral-xyz/anchor": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@coral-xyz/anchor/-/anchor-0.29.0.tgz", + "integrity": "sha512-eny6QNG0WOwqV0zQ7cs/b1tIuzZGmP7U7EcH+ogt4Gdbl8HDmIYVMh/9aTmYZPaFWjtUaI8qSn73uYEXWfATdA==", + "dependencies": { + "@coral-xyz/borsh": "^0.29.0", + "@noble/hashes": "^1.3.1", + "@solana/web3.js": "^1.68.0", + "bn.js": "^5.1.2", + "bs58": "^4.0.1", + "buffer-layout": "^1.2.2", + "camelcase": "^6.3.0", + "cross-fetch": "^3.1.5", + "crypto-hash": "^1.3.0", + "eventemitter3": "^4.0.7", + "pako": "^2.0.3", + "snake-case": "^3.0.4", + "superstruct": "^0.15.4", + "toml": "^3.0.0" + }, + "engines": { + "node": ">=11" + } + }, + "node_modules/@coral-xyz/anchor/node_modules/@coral-xyz/borsh": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/@coral-xyz/borsh/-/borsh-0.29.0.tgz", + "integrity": "sha512-s7VFVa3a0oqpkuRloWVPdCK7hMbAMY270geZOGfCnaqexrP5dTIpbEHL33req6IYPPJ0hYa71cdvJ1h6V55/oQ==", + "dependencies": { + "bn.js": "^5.1.2", + "buffer-layout": "^1.2.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@solana/web3.js": "^1.68.0" + } + }, + "node_modules/@coral-xyz/anchor/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@coral-xyz/anchor/node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "node_modules/@coral-xyz/anchor/node_modules/superstruct": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-0.15.5.tgz", + "integrity": "sha512-4AOeU+P5UuE/4nOUkmcQdW5y7i9ndt1cQd/3iUe+LTz3RxESf/W/5lg4B74HbDMMv8PHnPnGCQFH45kBcrQYoQ==" + }, "node_modules/@coral-xyz/borsh": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/@coral-xyz/borsh/-/borsh-0.2.6.tgz", @@ -13423,6 +13483,23 @@ "@xtuc/long": "4.2.2" } }, + "node_modules/@wormhole-foundation/sdk-base": { + "version": "0.5.2-beta.0", + "resolved": "https://registry.npmjs.org/@wormhole-foundation/sdk-base/-/sdk-base-0.5.2-beta.0.tgz", + "integrity": "sha512-K3GR/fsh07wlSoy/ugJooPT2hboi8jrQxJRVsrJhF2FIFYIVkcMORvUiIgDDhUbcMlX7jAt4Nl77bPcVoj3+tg==", + "dependencies": { + "@scure/base": "^1.1.3" + } + }, + "node_modules/@wormhole-foundation/sdk-definitions": { + "version": "0.5.2-beta.0", + "resolved": "https://registry.npmjs.org/@wormhole-foundation/sdk-definitions/-/sdk-definitions-0.5.2-beta.0.tgz", + "integrity": "sha512-/9S74lRgv8oGHW4A3RiGzlzwSWzFQBg9w93ePJcidB9XoUY54TgudCnCVOJkmDrVfSlAj1Iyqy+qjihbP+5uQA==", + "dependencies": { + "@noble/hashes": "^1.3.1", + "@wormhole-foundation/sdk-base": "0.5.2-beta.0" + } + }, "node_modules/@wormhole-foundation/wormhole-connect": { "resolved": "wormhole-connect", "link": true @@ -13901,6 +13978,23 @@ "node": ">= 6" } }, + "node_modules/@xlabs-libs/wallet-aggregator-evm": { + "version": "0.0.1-alpha.37", + "resolved": "https://registry.npmjs.org/@xlabs-libs/wallet-aggregator-evm/-/wallet-aggregator-evm-0.0.1-alpha.37.tgz", + "integrity": "sha512-mtkIyWJeirLWZ5YfLXvas4/0sp9t7TfcNZ2MPm+Sseq3VcZeuaLDd58NwLfh3S3QYatC3koT6pOK8OYlNG5f0Q==", + "dependencies": { + "@ethersproject/abi": "^5.7.0", + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/providers": "^5.7.2", + "@wagmi/core": "^1.4.13", + "@web3modal/standalone": "^2.2.1", + "@xlabs-libs/wallet-aggregator-core": "^0.0.1-alpha.18", + "ethers": "^5.7.2", + "versions": "^10.4.1", + "viem": "^1.20.3" + } + }, "node_modules/@xlabs-libs/wallet-aggregator-sei": { "version": "0.0.1-alpha.14", "resolved": "https://registry.npmjs.org/@xlabs-libs/wallet-aggregator-sei/-/wallet-aggregator-sei-0.0.1-alpha.14.tgz", @@ -36535,6 +36629,7 @@ "version": "0.0.1-beta.0", "dependencies": { "@certusone/wormhole-sdk": "^0.10.10", + "@coral-xyz/anchor": "^0.29.0", "@cosmjs/cosmwasm-stargate": "^0.31.3", "@cosmjs/stargate": "^0.31.3", "@cosmjs/tendermint-rpc": "^0.31.3", @@ -36547,7 +36642,7 @@ "@reduxjs/toolkit": "^1.9.1", "@solana/wallet-adapter-wallets": "^0.19.25", "@solana/web3.js": "^1.73.0", - "@wormhole-foundation/sdk-definitions": "^0.5.0", + "@wormhole-foundation/sdk-definitions": "^0.5.2-beta.0", "@xlabs-libs/wallet-aggregator-aptos": "^0.0.1-alpha.14", "@xlabs-libs/wallet-aggregator-core": "^0.0.1-alpha.18", "@xlabs-libs/wallet-aggregator-cosmos": "^0.0.1-alpha.14", @@ -36643,40 +36738,6 @@ "peerDependencies": { "@babel/core": "^7.0.0-0" } - }, - "wormhole-connect/node_modules/@wormhole-foundation/sdk-base": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@wormhole-foundation/sdk-base/-/sdk-base-0.5.0.tgz", - "integrity": "sha512-5+Y0Wrw6eihkbUqVpXDowvD3Xs3IIF0gvLK6JQ3xsuya/LiJeP8S7YdLVRjnGE5rEAvsf46yrgxuzy5hQ3xuVQ==", - "dependencies": { - "@scure/base": "^1.1.3" - } - }, - "wormhole-connect/node_modules/@wormhole-foundation/sdk-definitions": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@wormhole-foundation/sdk-definitions/-/sdk-definitions-0.5.0.tgz", - "integrity": "sha512-Qm8quIquf9Sx7SGUVxch9+EZRlkMdz9vYFLBUru+PCqxnHoVg0yobhUHCMi+9wn4n2BWU3F8UC/zv/VDJmMrGg==", - "dependencies": { - "@noble/hashes": "^1.3.1", - "@wormhole-foundation/sdk-base": "0.5.0" - } - }, - "wormhole-connect/node_modules/@xlabs-libs/wallet-aggregator-evm": { - "version": "0.0.1-alpha.37", - "resolved": "https://registry.npmjs.org/@xlabs-libs/wallet-aggregator-evm/-/wallet-aggregator-evm-0.0.1-alpha.37.tgz", - "integrity": "sha512-mtkIyWJeirLWZ5YfLXvas4/0sp9t7TfcNZ2MPm+Sseq3VcZeuaLDd58NwLfh3S3QYatC3koT6pOK8OYlNG5f0Q==", - "dependencies": { - "@ethersproject/abi": "^5.7.0", - "@ethersproject/abstract-provider": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/providers": "^5.7.2", - "@wagmi/core": "^1.4.13", - "@web3modal/standalone": "^2.2.1", - "@xlabs-libs/wallet-aggregator-core": "^0.0.1-alpha.18", - "ethers": "^5.7.2", - "versions": "^10.4.1", - "viem": "^1.20.3" - } } } } diff --git a/sdk/src/config/TESTNET.ts b/sdk/src/config/TESTNET.ts index b8e8687ed..5112a2a3b 100644 --- a/sdk/src/config/TESTNET.ts +++ b/sdk/src/config/TESTNET.ts @@ -55,12 +55,6 @@ const TESTNET: { [chain in TestnetChainName]: ChainConfig } = { contracts: { ...CONTRACTS.TESTNET.ethereum, relayer: '0x9563a59c15842a6f322b10f69d1dd88b41f2e97b', - cctpContracts: { - cctpTokenMessenger: '0xd0c3da58f55358142b8d3e06c1c30c5c6114efe8', - cctpMessageTransmitter: '0x26413e8157cd32011e726065a5462e97dd4d03d9', - wormholeCircleRelayer: '0x17da1ff5386d044c63f00747b5b8ad1e3806448d', - wormholeCCTP: '0x0a69146716b3a21622287efa1607424c663069a4', - }, }, finalityThreshold: 64, nativeTokenDecimals: 18, @@ -323,9 +317,14 @@ const TESTNET: { [chain in TestnetChainName]: ChainConfig } = { context: Context.ETH, contracts: { ...CONTRACTS.TESTNET.sepolia, + cctpContracts: { + cctpTokenMessenger: '0x9f3b8679c73c2fef8b59b4f3444d4e156fb70aa5', + cctpMessageTransmitter: '0x7865fafc2db2093669d92c0f33aeef291086befd', + }, }, finalityThreshold: 0, nativeTokenDecimals: 18, + cctpDomain: 0, }, arbitrum_sepolia: { key: 'arbitrum_sepolia', @@ -397,7 +396,7 @@ const TESTNET_CONFIG: WormholeConfig = { klaytn: 'https://rpc.ankr.com/klaytn_testnet', sepolia: 'https://rpc.ankr.com/eth_sepolia', arbitrum_sepolia: 'https://sepolia-rollup.arbitrum.io/rpc', - base_sepolia: 'https://sepolia.base.org', + base_sepolia: 'https://base-sepolia-rpc.publicnode.com', optimism_sepolia: 'https://sepolia.optimism.io', }, rest: { diff --git a/wormhole-connect/package.json b/wormhole-connect/package.json index f3e8148fe..b8caf2746 100644 --- a/wormhole-connect/package.json +++ b/wormhole-connect/package.json @@ -18,6 +18,7 @@ ], "dependencies": { "@certusone/wormhole-sdk": "^0.10.10", + "@coral-xyz/anchor": "^0.29.0", "@cosmjs/cosmwasm-stargate": "^0.31.3", "@cosmjs/stargate": "^0.31.3", "@cosmjs/tendermint-rpc": "^0.31.3", @@ -30,7 +31,7 @@ "@reduxjs/toolkit": "^1.9.1", "@solana/wallet-adapter-wallets": "^0.19.25", "@solana/web3.js": "^1.73.0", - "@wormhole-foundation/sdk-definitions": "^0.5.0", + "@wormhole-foundation/sdk-definitions": "^0.5.2-beta.0", "@xlabs-libs/wallet-aggregator-aptos": "^0.0.1-alpha.14", "@xlabs-libs/wallet-aggregator-core": "^0.0.1-alpha.18", "@xlabs-libs/wallet-aggregator-cosmos": "^0.0.1-alpha.14", @@ -62,7 +63,8 @@ "prettier": "prettier --write ./src", "analyze": "NODE_ENV=production NODE_OPTIONS=--max-old-space-size=6144 vite-bundle-visualizer", "test": "jest ./tests/*.test.ts --detectOpenHandles", - "checksdn": "npx tsx scripts/ofac/checkSdnListForUpdates.ts" + "checksdn": "npx tsx scripts/ofac/checkSdnListForUpdates.ts", + "preview": "vite preview" }, "eslintConfig": { "extends": [ diff --git a/wormhole-connect/src/AppRouter.tsx b/wormhole-connect/src/AppRouter.tsx index 700f84ddf..3c29ac9c5 100644 --- a/wormhole-connect/src/AppRouter.tsx +++ b/wormhole-connect/src/AppRouter.tsx @@ -19,6 +19,8 @@ import { setRoute } from './store/router'; import { clearWallets } from './store/wallet'; import { clearPorticoBridge } from 'store/porticoBridge'; import { useExternalSearch } from 'hooks/useExternalSearch'; +import { clearNtt } from 'store/ntt'; +import internalConfig from 'config'; const useStyles = makeStyles()((theme: any) => ({ appContent: { @@ -71,6 +73,8 @@ function AppRouter({ config }: Props) { if (prevRoute === redeemRoute && route !== redeemRoute) { dispatch(clearRedeem()); dispatch(clearWallets()); + dispatch(clearNtt()); + internalConfig.wh.registerProviders(); // reset providers that may have been set during transfer } // reset transfer state on leave if (prevRoute === bridgeRoute && route !== bridgeRoute) { diff --git a/wormhole-connect/src/config/devnet/index.ts b/wormhole-connect/src/config/devnet/index.ts index 027b1d712..34b5353d6 100644 --- a/wormhole-connect/src/config/devnet/index.ts +++ b/wormhole-connect/src/config/devnet/index.ts @@ -7,6 +7,7 @@ import { DEVNET_RPC_MAPPING, } from './rpcs'; import { DEVNET_TOKENS } from './tokens'; +import { DEVNET_NTT_GROUPS } from './nttGroups'; export * from './chains'; export * from './gasEstimates'; @@ -20,6 +21,7 @@ const DEVNET: NetworkData = { rpcs: DEVNET_RPC_MAPPING, rest: DEVNET_REST_MAPPING, graphql: DEVNET_GRAPHQL_MAPPING, + nttGroups: DEVNET_NTT_GROUPS, }; export default DEVNET; diff --git a/wormhole-connect/src/config/devnet/nttGroups.ts b/wormhole-connect/src/config/devnet/nttGroups.ts new file mode 100644 index 000000000..e3f03f625 --- /dev/null +++ b/wormhole-connect/src/config/devnet/nttGroups.ts @@ -0,0 +1,3 @@ +import { NttGroups } from 'config/types'; + +export const DEVNET_NTT_GROUPS: NttGroups = {}; diff --git a/wormhole-connect/src/config/index.ts b/wormhole-connect/src/config/index.ts index 35fd91d26..4f35f77ec 100644 --- a/wormhole-connect/src/config/index.ts +++ b/wormhole-connect/src/config/index.ts @@ -11,7 +11,11 @@ import TESTNET from './testnet'; import DEVNET from './devnet'; import type { WormholeConnectConfig } from './types'; import { Network, InternalConfig, Route, TokensConfig } from './types'; -import { mergeCustomTokensConfig, validateDefaults } from './utils'; +import { + mergeCustomTokensConfig, + mergeNttGroups, + validateDefaults, +} from './utils'; export function buildConfig( customConfig?: WormholeConnectConfig, @@ -132,6 +136,13 @@ export function buildConfig( // Route options ethBridgeMaxAmount: customConfig?.ethBridgeMaxAmount ?? 5, wstETHBridgeMaxAmount: customConfig?.wstETHBridgeMaxAmount ?? 5, + + // NTT config + nttGroups: mergeNttGroups( + tokens, + networkData.nttGroups, + customConfig?.nttGroups, + ), }; } diff --git a/wormhole-connect/src/config/mainnet/gasEstimates.ts b/wormhole-connect/src/config/mainnet/gasEstimates.ts index d2bdf33f5..dbfe4c18d 100644 --- a/wormhole-connect/src/config/mainnet/gasEstimates.ts +++ b/wormhole-connect/src/config/mainnet/gasEstimates.ts @@ -32,6 +32,13 @@ export const MAINNET_GAS_ESTIMATES: GasEstimates = { sendToken: 300000, claim: 450000, }, + [Route.NttManual]: { + sendToken: 10000000, + claim: 10000000, + }, + [Route.NttRelay]: { + sendToken: 10000000, + }, }, polygon: { [Route.Bridge]: { @@ -107,6 +114,13 @@ export const MAINNET_GAS_ESTIMATES: GasEstimates = { sendNative: 250000, sendToken: 300000, }, + [Route.NttManual]: { + sendToken: 10000000, + claim: 10000000, + }, + [Route.NttRelay]: { + sendToken: 10000000, + }, }, celo: { [Route.Bridge]: { diff --git a/wormhole-connect/src/config/mainnet/index.ts b/wormhole-connect/src/config/mainnet/index.ts index 034279826..075d559da 100644 --- a/wormhole-connect/src/config/mainnet/index.ts +++ b/wormhole-connect/src/config/mainnet/index.ts @@ -7,6 +7,7 @@ import { MAINNET_RPC_MAPPING, } from './rpcs'; import { MAINNET_TOKENS } from './tokens'; +import { MAINNET_NTT_GROUPS } from './nttGroups'; export * from './chains'; export * from './gasEstimates'; @@ -20,6 +21,7 @@ const MAINNET: NetworkData = { rpcs: MAINNET_RPC_MAPPING, rest: MAINNET_REST_MAPPING, graphql: MAINNET_GRAPHQL_MAPPING, + nttGroups: MAINNET_NTT_GROUPS, }; export default MAINNET; diff --git a/wormhole-connect/src/config/mainnet/nttGroups.ts b/wormhole-connect/src/config/mainnet/nttGroups.ts new file mode 100644 index 000000000..9727df37f --- /dev/null +++ b/wormhole-connect/src/config/mainnet/nttGroups.ts @@ -0,0 +1,30 @@ +import { NttGroups } from 'config/types'; + +export const MAINNET_NTT_GROUPS: NttGroups = { + FANTOM_USDC: { + nttManagers: [ + { + chainName: 'ethereum', + address: '0xeBdCe9a913d9400EE75ef31Ce8bd34462D01a1c1', + tokenKey: 'USDCeth', + transceivers: [ + { + address: '0x55f7820357FA17A1ECb48E959D5E637bFF956d6F', + type: 'wormhole', + }, + ], + }, + { + chainName: 'fantom', + address: '0x68dB2f05Aa2d77DEf981fd2be32661340c9222FB', + tokenKey: 'USDCfantom', + transceivers: [ + { + address: '0x8b47f02e7e20174c76af910adc0ad8a4b0342f4c', + type: 'wormhole', + }, + ], + }, + ], + }, +}; diff --git a/wormhole-connect/src/config/mainnet/tokens.ts b/wormhole-connect/src/config/mainnet/tokens.ts index 3f97eb4e3..213edc655 100644 --- a/wormhole-connect/src/config/mainnet/tokens.ts +++ b/wormhole-connect/src/config/mainnet/tokens.ts @@ -1126,6 +1126,21 @@ export const MAINNET_TOKENS: TokensConfig = { }, }, }, + USDCfantom: { + key: 'USDCfantom', + symbol: 'USDC.e', + nativeChain: 'fantom', + icon: Icon.USDC, + tokenId: { + chain: 'fantom', + address: '0x2F733095B80A04b38b0D10cC884524a3d09b836a', + }, + coinGeckoId: 'usd-coin', + color: '#2774CA', + decimals: { + default: 6, + }, + }, CELO: { key: 'CELO', symbol: 'CELO', diff --git a/wormhole-connect/src/config/routes.ts b/wormhole-connect/src/config/routes.ts index cd621f7c4..6b1ceab66 100644 --- a/wormhole-connect/src/config/routes.ts +++ b/wormhole-connect/src/config/routes.ts @@ -91,4 +91,20 @@ export const RoutesConfig: { icon: WormholeIcon, pendingMessage: 'Waiting for Wormhole network consensus . . .', }, + [Route.NttManual]: { + route: Route.NttManual, + name: 'Native Token Transfer', + providedBy: 'Wormhole', + link: 'https://wormhole.com/', + icon: WormholeIcon, + pendingMessage: 'Waiting for Wormhole network consensus . . .', + }, + [Route.NttRelay]: { + route: Route.NttRelay, + name: 'Native Token Transfer', + providedBy: 'xLabs', + link: 'https://xlabs.xyz', + icon: XLabsIcon, + pendingMessage: 'Waiting for Wormhole network consensus . . .', + }, }; diff --git a/wormhole-connect/src/config/testnet/gasEstimates.ts b/wormhole-connect/src/config/testnet/gasEstimates.ts index b300d5ea6..cfbe37815 100644 --- a/wormhole-connect/src/config/testnet/gasEstimates.ts +++ b/wormhole-connect/src/config/testnet/gasEstimates.ts @@ -118,6 +118,13 @@ export const TESTNET_GAS_ESTIMATES: GasEstimates = { sendToken: 5000, claim: 5000, }, + [Route.NttManual]: { + sendToken: 15000, + claim: 25000, + }, + [Route.NttRelay]: { + sendToken: 15000, + }, }, sui: { [Route.Bridge]: { @@ -248,6 +255,17 @@ export const TESTNET_GAS_ESTIMATES: GasEstimates = { sendToken: 150000, claim: 200000, }, + [Route.CCTPManual]: { + sendToken: 150000, + claim: 300000, + }, + [Route.NttManual]: { + sendToken: 200000, + claim: 250000, + }, + [Route.NttRelay]: { + sendToken: 450000, + }, }, arbitrum_sepolia: { [Route.Bridge]: { @@ -255,6 +273,13 @@ export const TESTNET_GAS_ESTIMATES: GasEstimates = { sendToken: 150000, claim: 200000, }, + [Route.NttManual]: { + sendToken: 200000, + claim: 250000, + }, + [Route.NttRelay]: { + sendToken: 450000, + }, }, base_sepolia: { [Route.Bridge]: { @@ -262,6 +287,13 @@ export const TESTNET_GAS_ESTIMATES: GasEstimates = { sendToken: 150000, claim: 200000, }, + [Route.NttManual]: { + sendToken: 200000, + claim: 250000, + }, + [Route.NttRelay]: { + sendToken: 450000, + }, }, optimism_sepolia: { [Route.Bridge]: { @@ -269,6 +301,13 @@ export const TESTNET_GAS_ESTIMATES: GasEstimates = { sendToken: 150000, claim: 200000, }, + [Route.NttManual]: { + sendToken: 200000, + claim: 250000, + }, + [Route.NttRelay]: { + sendToken: 450000, + }, }, injective: { [Route.Bridge]: { diff --git a/wormhole-connect/src/config/testnet/index.ts b/wormhole-connect/src/config/testnet/index.ts index c4bf27dd3..1e994b1a2 100644 --- a/wormhole-connect/src/config/testnet/index.ts +++ b/wormhole-connect/src/config/testnet/index.ts @@ -7,6 +7,7 @@ import { TESTNET_RPC_MAPPING, } from './rpcs'; import { TESTNET_TOKENS } from './tokens'; +import { TESTNET_NTT_GROUPS } from './nttGroups'; export * from './chains'; export * from './gasEstimates'; @@ -20,6 +21,7 @@ const TESTNET: NetworkData = { rpcs: TESTNET_RPC_MAPPING, rest: TESTNET_REST_MAPPING, graphql: TESTNET_GRAPHQL_MAPPING, + nttGroups: TESTNET_NTT_GROUPS, }; export default TESTNET; diff --git a/wormhole-connect/src/config/testnet/nttGroups.ts b/wormhole-connect/src/config/testnet/nttGroups.ts new file mode 100644 index 000000000..07d20d78d --- /dev/null +++ b/wormhole-connect/src/config/testnet/nttGroups.ts @@ -0,0 +1,30 @@ +import { NttGroups } from 'config/types'; + +export const TESTNET_NTT_GROUPS: NttGroups = { + USDC_NTT: { + nttManagers: [ + { + chainName: 'fuji', + address: '0x22D00F8aCcC2da440c937104BA49AfD8261a660F', + tokenKey: 'USDCavax', + transceivers: [ + { + address: '0xeA8D34fa9147863e486d2d07AB92b8218CF58C0E', + type: 'wormhole', + }, + ], + }, + { + chainName: 'alfajores', + address: '0xdc86639219fD880A30C71B58E1cfA2707B645516', + tokenKey: 'USDCalfajores', + transceivers: [ + { + address: '0x76516c0b966B4D4cFEFB107755562b16427dAE52', + type: 'wormhole', + }, + ], + }, + ], + }, +}; diff --git a/wormhole-connect/src/config/testnet/tokens.ts b/wormhole-connect/src/config/testnet/tokens.ts index 5b766c716..612393cc5 100644 --- a/wormhole-connect/src/config/testnet/tokens.ts +++ b/wormhole-connect/src/config/testnet/tokens.ts @@ -900,6 +900,21 @@ export const TESTNET_TOKENS: TokensConfig = { }, }, }, + USDCalfajores: { + key: 'USDCalfajores', + symbol: 'USDC.e', + nativeChain: 'alfajores', + icon: Icon.USDC, + tokenId: { + chain: 'alfajores', + address: '0x72CAaa7e9889E0a63e016748179b43911A3ec9e5', + }, + coinGeckoId: 'usd-coin', + color: '#2774CA', + decimals: { + default: 6, + }, + }, GLMR: { key: 'GLMR', symbol: 'GLMR', @@ -2124,6 +2139,21 @@ export const TESTNET_TOKENS: TokensConfig = { default: 8, }, }, + USDCsepolia: { + key: 'USDCsepolia', + symbol: 'USDC', + nativeChain: 'sepolia', + icon: Icon.USDC, + tokenId: { + chain: 'sepolia', + address: '0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238', + }, + coinGeckoId: 'usd-coin', + color: '#2774CA', + decimals: { + default: 6, + }, + }, ETHarbitrum_sepolia: { key: 'ETHarbitrum_sepolia', symbol: 'ETH', diff --git a/wormhole-connect/src/config/types.ts b/wormhole-connect/src/config/types.ts index 13cca7bf2..36e569309 100644 --- a/wormhole-connect/src/config/types.ts +++ b/wormhole-connect/src/config/types.ts @@ -41,6 +41,7 @@ export enum Icon { 'PYTH', 'INJ', 'KLAY', + 'NTT', } export enum Route { @@ -53,6 +54,8 @@ export enum Route { TBTC = 'tbtc', ETHBridge = 'ethBridge', wstETHBridge = 'wstETHBridge', + NttManual = 'nttManual', + NttRelay = 'nttRelay', } // Used in bridging components @@ -120,6 +123,9 @@ export interface WormholeConnectConfig { // Route settings ethBridgeMaxAmount?: number; wstETHBridgeMaxAmount?: number; + + // NTT config + nttGroups?: NttGroups; } // This is the exported config value used throughout the code base @@ -171,6 +177,9 @@ export interface InternalConfig { // Route settings ethBridgeMaxAmount: number; wstETHBridgeMaxAmount: number; + + // NTT config + nttGroups: NttGroups; } export type ExplorerConfig = { @@ -275,6 +284,7 @@ export type NetworkData = { rpcs: RpcMapping; rest: RpcMapping; graphql: RpcMapping; + nttGroups: NttGroups; }; export interface MenuEntry { @@ -283,3 +293,22 @@ export interface MenuEntry { target?: string; order?: number; } + +export type NttTransceiverConfig = { + address: string; + type: 'wormhole'; // only wormhole is supported for now +}; + +export type NttManagerConfig = { + chainName: ChainName; + address: string; + tokenKey: string; // token key for the token this NTT manager has configured + transceivers: NttTransceiverConfig[]; + solanaQuoter?: string; +}; + +export type NttGroup = { + nttManagers: NttManagerConfig[]; +}; + +export type NttGroups = { [key: string]: NttGroup }; diff --git a/wormhole-connect/src/config/utils.ts b/wormhole-connect/src/config/utils.ts index bec41c943..782adb398 100644 --- a/wormhole-connect/src/config/utils.ts +++ b/wormhole-connect/src/config/utils.ts @@ -1,5 +1,6 @@ +import { isEqualCaseInsensitive } from 'utils'; import config from '.'; -import { BridgeDefaults, TokensConfig } from './types'; +import { BridgeDefaults, NttGroups, TokensConfig } from './types'; const error = (msg: string) => { console.error(`Wormhole Connect: ${msg}`); @@ -85,6 +86,64 @@ export const mergeCustomTokensConfig = ( return builtin; }; +export const mergeNttGroups = ( + tokens: TokensConfig, + builtin: NttGroups, + custom?: NttGroups, +) => { + if (!custom) return builtin; + + for (const key in custom) { + if (key in builtin) { + console.warn( + `Skipping custom NTT group config for "${key}" because it conflicts with a built-in`, + ); + continue; + } + + const customGroup = custom[key]; + // if any of the managers in the custom group exist in the built-in groups, skip + if ( + customGroup.nttManagers.some((manager) => + Object.values(builtin).some((group) => + group.nttManagers.some((builtinManager) => + isEqualCaseInsensitive(builtinManager.address, manager.address), + ), + ), + ) + ) { + console.warn( + `Skipping custom NTT group config for "${key}" because it conflicts with a built-in`, + ); + continue; + } + + // if any of the token keys in the custom group don't exist in the tokens config, skip + if (customGroup.nttManagers.some((manager) => !tokens[manager.tokenKey])) { + console.warn( + `Skipping custom NTT group config for "${key}" because it references a token that does not exist`, + ); + continue; + } + + // if any of the chain names in the custom group are duplicated, skip + if ( + new Set(customGroup.nttManagers.map((manager) => manager.chainName)) + .size !== customGroup.nttManagers.length + ) { + console.warn( + `Skipping custom NTT group config for "${key}" because it contains duplicate chain names`, + ); + continue; + } + + console.info(`Accepted custom NTT group config for "${key}"`); + builtin[key] = custom[key]; + } + + return builtin; +}; + export const validateDefaults = (defaults: BridgeDefaults | undefined) => { if (!defaults) return; const { diff --git a/wormhole-connect/src/hooks/useCheckInboundQueuedTransfer.ts b/wormhole-connect/src/hooks/useCheckInboundQueuedTransfer.ts new file mode 100644 index 000000000..b60309098 --- /dev/null +++ b/wormhole-connect/src/hooks/useCheckInboundQueuedTransfer.ts @@ -0,0 +1,68 @@ +import { useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { RootState } from 'store'; +import { sleep } from 'utils'; +import { + resetInboundQueuedTransfer, + setInboundQueuedTransfer, +} from 'store/ntt'; +import { isSignedNttMessage } from 'routes/types'; +import { isNttRoute } from 'routes'; +import RouteOperator from 'routes/operator'; +import { NttBase } from 'routes/ntt/nttBase'; + +const RETRY_DELAY = 15_000; + +const useCheckInboundQueuedTransfer = (): void => { + const dispatch = useDispatch(); + const route = useSelector((state: RootState) => state.redeem.route); + const signedMessage = useSelector( + (state: RootState) => state.redeem.signedMessage, + ); + const transferComplete = useSelector( + (state: RootState) => state.redeem.transferComplete, + ); + + useEffect(() => { + dispatch(resetInboundQueuedTransfer()); + if ( + !route || + !isNttRoute(route) || + !signedMessage || + !isSignedNttMessage(signedMessage) || + transferComplete + ) { + return; + } + const { toChain, recipientNttManager, messageDigest } = signedMessage; + const nttRoute = RouteOperator.getRoute(route) as NttBase; + let active = true; + const fetchData = async () => { + // We continue polling for the inbound queued transfer even after fetching the data once, + // because the transfer could be released by anyone. + while (active) { + try { + const queuedTransfer = await nttRoute.getInboundQueuedTransfer( + toChain, + recipientNttManager, + messageDigest, + ); + if (active) { + dispatch(setInboundQueuedTransfer(queuedTransfer)); + } + } catch (e) { + console.error(e); + } + if (active) { + await sleep(RETRY_DELAY); + } + } + }; + fetchData(); + return () => { + active = false; + }; + }, [route, transferComplete, signedMessage]); +}; + +export default useCheckInboundQueuedTransfer; diff --git a/wormhole-connect/src/hooks/useDeliveryStatus.ts b/wormhole-connect/src/hooks/useDeliveryStatus.ts new file mode 100644 index 000000000..6a974d6e3 --- /dev/null +++ b/wormhole-connect/src/hooks/useDeliveryStatus.ts @@ -0,0 +1,82 @@ +import { DeliveryStatus } from '@certusone/wormhole-sdk/lib/esm/relayer'; +import axios from 'axios'; +import { Route } from 'config/types'; +import { useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { RootState } from 'store'; +import { setRedeemTx, setDeliveryStatus } from 'store/redeem'; +import { sleep } from 'utils'; +import { isEvmChain } from 'utils/sdk'; +import { getEmitterAndSequence } from 'utils/vaa'; +import config from 'config'; + +const RETRY_DELAY = 15_000; + +interface RelayResponse { + data?: { + delivery?: { + execution?: { + status: DeliveryStatus; + }; + }; + toTxHash?: string; + }; +} + +// Polls for standard relayer delivery status +const useDeliveryStatus = (): void => { + const dispatch = useDispatch(); + const route = useSelector((state: RootState) => state.redeem.route); + const signedMessage = useSelector( + (state: RootState) => state.redeem.signedMessage, + ); + useEffect(() => { + if ( + !signedMessage || + route !== Route.NttRelay || + // Currently, only EVM chains support standard relayer + !isEvmChain(signedMessage.toChain) || + !isEvmChain(signedMessage.fromChain) + ) { + return; + } + const { emitterChain, emitterAddress, sequence } = + getEmitterAndSequence(signedMessage); + const baseUrl = `https://api.${ + config.isMainnet ? '' : 'testnet.' + }wormholescan.io/api/v1/relays`; + let active = true; + const fetchData = async () => { + while (active) { + try { + const response = await axios.get( + `${baseUrl}/${emitterChain}/${emitterAddress}/${sequence}`, + ); + if (active) { + const { delivery, toTxHash } = response.data?.data || {}; + if (delivery?.execution) { + dispatch(setDeliveryStatus(delivery.execution.status)); + } + if (toTxHash) { + dispatch(setRedeemTx(toTxHash)); + } + break; + } + } catch (e) { + if (!axios.isAxiosError(e) || e.status !== 404) { + console.error(e); + } + } + if (active) { + await sleep(RETRY_DELAY); + } + } + }; + fetchData(); + return () => { + active = false; + }; + }, [signedMessage, route]); +}; + +export default useDeliveryStatus; diff --git a/wormhole-connect/src/hooks/usePorticoRelayerFee.ts b/wormhole-connect/src/hooks/usePorticoRelayerFee.ts index d1a07b414..4542188ca 100644 --- a/wormhole-connect/src/hooks/usePorticoRelayerFee.ts +++ b/wormhole-connect/src/hooks/usePorticoRelayerFee.ts @@ -56,15 +56,15 @@ export const usePorticoRelayerFee = (): void => { dispatch(setFetchingRelayerFee()); while (!cancelled) { try { - const fee = await RouteOperator.getRelayerFee( + const result = await RouteOperator.getRelayerFee( route, fromChain, toChain, token, destToken, ); - if (!cancelled) { - dispatch(setRelayerFee(fee.toString())); + if (!cancelled && result) { + dispatch(setRelayerFee(result.fee.toString())); } } catch { if (!cancelled) { diff --git a/wormhole-connect/src/icons/RouteIcons.tsx b/wormhole-connect/src/icons/RouteIcons.tsx index 133e03ab8..a50b5ae05 100644 --- a/wormhole-connect/src/icons/RouteIcons.tsx +++ b/wormhole-connect/src/icons/RouteIcons.tsx @@ -48,6 +48,12 @@ export const getIcon = (route: Route) => { case Route.wstETHBridge: { return WormholeIcon; } + case Route.NttManual: { + return WormholeIcon; + } + case Route.NttRelay: { + return WormholeIcon; + } default: { return noIcon; } diff --git a/wormhole-connect/src/icons/TokenIcons.tsx b/wormhole-connect/src/icons/TokenIcons.tsx index 5fa9ebecb..e5dcd5fc0 100644 --- a/wormhole-connect/src/icons/TokenIcons.tsx +++ b/wormhole-connect/src/icons/TokenIcons.tsx @@ -33,6 +33,7 @@ import KUJI from './Tokens/KUJI'; import KLAY from './Tokens/KLAY'; import PYTH from './Tokens/PYTH'; import INJ from './Tokens/INJ'; +import NTT from './Tokens/NTT'; const useStyles = makeStyles<{ size: number }>()((theme, { size }) => ({ container: { @@ -80,6 +81,7 @@ const iconMap: { [key in Icon]: React.JSX.Element } = { [Icon.PYTH]: PYTH(), [Icon.KLAY]: KLAY(), [Icon.INJ]: INJ(), + [Icon.NTT]: NTT(), }; function isBuiltinIcon(icon?: Icon | string): icon is Icon { diff --git a/wormhole-connect/src/icons/Tokens/NTT.tsx b/wormhole-connect/src/icons/Tokens/NTT.tsx new file mode 100644 index 000000000..5be40def2 --- /dev/null +++ b/wormhole-connect/src/icons/Tokens/NTT.tsx @@ -0,0 +1,84 @@ +import React from 'react'; + +function NTT() { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} + +export default NTT; diff --git a/wormhole-connect/src/routes/abstracts/routeAbstract.ts b/wormhole-connect/src/routes/abstracts/routeAbstract.ts index 15ebc0b24..e921086c6 100644 --- a/wormhole-connect/src/routes/abstracts/routeAbstract.ts +++ b/wormhole-connect/src/routes/abstracts/routeAbstract.ts @@ -12,6 +12,7 @@ import { TransferDisplayData, TransferInfoBaseParams, TransferDestInfo, + RelayerFee, } from '../types'; import { ParsedRelayerMessage, ParsedMessage } from 'utils/sdk'; import { TokenPrices } from 'store/tokenPrices'; @@ -173,11 +174,12 @@ export abstract class RouteAbstract { destChain: ChainName | ChainId, token: string, destToken: string, - ): Promise; + ): Promise; abstract getForeignAsset( token: TokenId, chain: ChainName | ChainId, + destToken?: TokenConfig, ): Promise; abstract getMessage( diff --git a/wormhole-connect/src/routes/bridge/baseRoute.ts b/wormhole-connect/src/routes/bridge/baseRoute.ts index 63c123b83..86c268b3a 100644 --- a/wormhole-connect/src/routes/bridge/baseRoute.ts +++ b/wormhole-connect/src/routes/bridge/baseRoute.ts @@ -297,4 +297,8 @@ export abstract class BaseRoute extends RouteAbstract { getMaxSendAmount(): number { return Infinity; } + + getMinSendAmount(routeOptions: any): number { + return 0; + } } diff --git a/wormhole-connect/src/routes/bridge/bridge.ts b/wormhole-connect/src/routes/bridge/bridge.ts index 9bd1a5e96..5b712d77f 100644 --- a/wormhole-connect/src/routes/bridge/bridge.ts +++ b/wormhole-connect/src/routes/bridge/bridge.ts @@ -7,7 +7,7 @@ import { import { BigNumber } from 'ethers'; import { hexlify, parseUnits, arrayify } from 'ethers/lib/utils.js'; import config from 'config'; -import { Route } from 'config/types'; +import { Route, TokenConfig } from 'config/types'; import { getTokenDecimals } from 'utils'; import { TransferWallet, postVaa, signAndSendTransaction } from 'utils/wallet'; import { @@ -15,6 +15,7 @@ import { isSignedWormholeMessage, TokenTransferMessage, SignedTokenTransferMessage, + RelayerFee, } from '../types'; import { BaseRoute } from './baseRoute'; import { adaptParsedMessage } from '../utils'; @@ -150,9 +151,6 @@ export class BridgeRoute extends BaseRoute { /** * These operations have to be implemented in subclasses. */ - getMinSendAmount(routeOptions: any): number { - return 0; - } async send( token: TokenId | 'native', @@ -240,13 +238,14 @@ export class BridgeRoute extends BaseRoute { destChain: ChainName | ChainId, token: string, destToken: string, - ): Promise { - return BigNumber.from(0); + ): Promise { + return null; } async getForeignAsset( token: TokenId, chain: ChainName | ChainId, + destToken?: TokenConfig, ): Promise { return config.wh.getForeignAsset(token, chain); } diff --git a/wormhole-connect/src/routes/cctpManual/cctpManual.ts b/wormhole-connect/src/routes/cctpManual/cctpManual.ts index 7f3b6ed58..ba42512db 100644 --- a/wormhole-connect/src/routes/cctpManual/cctpManual.ts +++ b/wormhole-connect/src/routes/cctpManual/cctpManual.ts @@ -22,6 +22,7 @@ import { getAssociatedTokenAddress } from '@solana/spl-token'; import { BaseRoute } from '../bridge/baseRoute'; import { ManualCCTPMessage, + RelayerFee, SignedCCTPMessage, SignedMessage, TransferDestInfo, @@ -236,9 +237,6 @@ export class CCTPManualRoute extends BaseRoute { /** * These operations have to be implemented in subclasses. */ - getMinSendAmount(routeOptions: any): number { - return 0; - } async send( token: TokenId | 'native', @@ -377,13 +375,14 @@ export class CCTPManualRoute extends BaseRoute { destChain: ChainName | ChainId, token: string, destToken: string, - ): Promise { - return BigNumber.from(0); + ): Promise { + return null; } async getForeignAsset( token: TokenId, chain: ChainName | ChainId, + destToken?: TokenConfig, ): Promise { // assumes USDC const addr = config.tokensArr.find( diff --git a/wormhole-connect/src/routes/cctpManual/utils/chains.ts b/wormhole-connect/src/routes/cctpManual/utils/chains.ts index 90ac1043e..0e65175c0 100644 --- a/wormhole-connect/src/routes/cctpManual/utils/chains.ts +++ b/wormhole-connect/src/routes/cctpManual/utils/chains.ts @@ -5,7 +5,7 @@ export const CCTPManual_CHAINS: ChainName[] = [ 'ethereum', 'avalanche', 'fuji', - 'goerli', + 'sepolia', 'base', 'optimism', 'arbitrum', @@ -21,7 +21,7 @@ export const CCTPDomains: Partial> = { ethereum: 0, avalanche: 1, fuji: 1, - goerli: 0, + sepolia: 0, base: 6, optimism: 2, arbitrum: 3, @@ -36,7 +36,7 @@ export const CCTPDomains: Partial> = { export function getChainNameCCTP(domain: number): ChainName { switch (domain) { case 0: - return config.isMainnet ? 'ethereum' : 'goerli'; + return config.isMainnet ? 'ethereum' : 'sepolia'; case 1: return config.isMainnet ? 'avalanche' : 'fuji'; case 2: diff --git a/wormhole-connect/src/routes/cctpRelay/cctpRelay.ts b/wormhole-connect/src/routes/cctpRelay/cctpRelay.ts index c119e7437..bbd779098 100644 --- a/wormhole-connect/src/routes/cctpRelay/cctpRelay.ts +++ b/wormhole-connect/src/routes/cctpRelay/cctpRelay.ts @@ -18,6 +18,7 @@ import { getTokenById, getDisplayName, calculateUSDPrice, + getWrappedTokenId, } from 'utils'; import { ParsedMessage, @@ -34,6 +35,7 @@ import { RelayCCTPMessage, TransferDestInfoBaseParams, TransferDestInfo, + RelayerFee, } from '../types'; import { toDecimals, toFixedDecimals } from '../../utils/balance'; import { RelayOptions } from '../relay'; @@ -205,12 +207,13 @@ export class CCTPRelayRoute extends CCTPManualRoute implements RelayAbstract { ): Promise { let relayerFee; try { - relayerFee = await this.getRelayerFee( + const result = await this.getRelayerFee( sourceChain, destChain, sourceToken, destToken, ); + relayerFee = result?.fee; } catch (e) { console.error(e); } @@ -477,7 +480,7 @@ export class CCTPRelayRoute extends CCTPManualRoute implements RelayAbstract { destChain: ChainName | ChainId, token: string, destToken: string, - ): Promise { + ): Promise { const tokenConfig = config.tokens[token]; if (!tokenConfig) throw new Error('could not get token config'); const tokenId = tokenConfig.tokenId; @@ -490,7 +493,7 @@ export class CCTPRelayRoute extends CCTPManualRoute implements RelayAbstract { chainContext.contracts.mustGetWormholeCircleRelayer(sourceChain); const destChainId = config.wh.toChainId(destChain); const fee = await circleRelayer.relayerFee(destChainId, tokenId?.address); - return fee; + return { fee, feeToken: tokenId || getWrappedTokenId(tokenConfig) }; } async getMessage( diff --git a/wormhole-connect/src/routes/cosmosGateway/cosmosGateway.ts b/wormhole-connect/src/routes/cosmosGateway/cosmosGateway.ts index 51a18ec47..f639310b1 100644 --- a/wormhole-connect/src/routes/cosmosGateway/cosmosGateway.ts +++ b/wormhole-connect/src/routes/cosmosGateway/cosmosGateway.ts @@ -35,6 +35,7 @@ import { TransferDestInfoBaseParams, TransferInfoBaseParams, TransferDestInfo, + RelayerFee, } from '../types'; import { BridgeRoute } from '../bridge/bridge'; import { fetchVaa } from '../../utils/vaa'; @@ -142,14 +143,11 @@ export class CosmosGatewayRoute extends BaseRoute { getForeignAsset( token: TokenId, chain: ChainId | ChainName, + destToken?: TokenConfig, ): Promise { return config.wh.getForeignAsset(token, chain); } - getMinSendAmount(routeOptions: any): number { - return 0; - } - async send( token: TokenId | 'native', amount: string, @@ -287,8 +285,8 @@ export class CosmosGatewayRoute extends BaseRoute { destChain: ChainName | ChainId, token: string, destToken: string, - ): Promise { - return BigNumber.from(0); + ): Promise { + return null; } async isTransferCompleted( diff --git a/wormhole-connect/src/routes/hashflow/hashflow.ts b/wormhole-connect/src/routes/hashflow/hashflow.ts index 3f62a2afd..7e0a1e607 100644 --- a/wormhole-connect/src/routes/hashflow/hashflow.ts +++ b/wormhole-connect/src/routes/hashflow/hashflow.ts @@ -11,6 +11,7 @@ import { UnsignedMessage, SignedMessage, TransferDestInfo, + RelayerFee, } from '../types'; import { TransferDisplayData } from '../types'; import { RouteAbstract } from 'routes/abstracts'; @@ -137,12 +138,13 @@ export class HashflowRoute extends RouteAbstract { sourceChain: ChainName | ChainId, destChain: ChainName | ChainId, token: string, - ): Promise { + ): Promise { throw new Error('Method not implemented.'); } getForeignAsset( token: TokenId, chain: ChainName | ChainId, + destToken?: TokenConfig, ): Promise { throw new Error('Method not implemented.'); } diff --git a/wormhole-connect/src/routes/ntt/chains/evm/abis/0.1.0/NttManager.ts b/wormhole-connect/src/routes/ntt/chains/evm/abis/0.1.0/NttManager.ts new file mode 100644 index 000000000..480fcd0bb --- /dev/null +++ b/wormhole-connect/src/routes/ntt/chains/evm/abis/0.1.0/NttManager.ts @@ -0,0 +1,1970 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ +import type { + BaseContract, + BigNumber, + BigNumberish, + BytesLike, + CallOverrides, + ContractTransaction, + Overrides, + PayableOverrides, + PopulatedTransaction, + Signer, + utils, +} from 'ethers'; +import type { + FunctionFragment, + Result, + EventFragment, +} from '@ethersproject/abi'; +import type { Listener, Provider } from '@ethersproject/providers'; +import type { + TypedEventFilter, + TypedEvent, + TypedListener, + OnEvent, +} from '../common'; + +export declare namespace TransceiverStructs { + export type NttManagerMessageStruct = { + id: BytesLike; + sender: BytesLike; + payload: BytesLike; + }; + + export type NttManagerMessageStructOutput = [string, string, string] & { + id: string; + sender: string; + payload: string; + }; +} + +export declare namespace IRateLimiter { + export type RateLimitParamsStruct = { + limit: BigNumberish; + currentCapacity: BigNumberish; + lastTxTimestamp: BigNumberish; + }; + + export type RateLimitParamsStructOutput = [ + BigNumber, + BigNumber, + BigNumber, + ] & { + limit: BigNumber; + currentCapacity: BigNumber; + lastTxTimestamp: BigNumber; + }; + + export type InboundQueuedTransferStruct = { + amount: BigNumberish; + txTimestamp: BigNumberish; + recipient: string; + }; + + export type InboundQueuedTransferStructOutput = [ + BigNumber, + BigNumber, + string, + ] & { amount: BigNumber; txTimestamp: BigNumber; recipient: string }; + + export type OutboundQueuedTransferStruct = { + recipient: BytesLike; + refundAddress: BytesLike; + amount: BigNumberish; + txTimestamp: BigNumberish; + recipientChain: BigNumberish; + sender: string; + transceiverInstructions: BytesLike; + }; + + export type OutboundQueuedTransferStructOutput = [ + string, + string, + BigNumber, + BigNumber, + number, + string, + string, + ] & { + recipient: string; + refundAddress: string; + amount: BigNumber; + txTimestamp: BigNumber; + recipientChain: number; + sender: string; + transceiverInstructions: string; + }; +} + +export declare namespace INttManager { + export type NttManagerPeerStruct = { + peerAddress: BytesLike; + tokenDecimals: BigNumberish; + }; + + export type NttManagerPeerStructOutput = [string, number] & { + peerAddress: string; + tokenDecimals: number; + }; +} + +export interface NttManagerInterface extends utils.Interface { + functions: { + 'NTT_MANAGER_VERSION()': FunctionFragment; + 'attestationReceived(uint16,bytes32,(bytes32,bytes32,bytes))': FunctionFragment; + 'cancelOutboundQueuedTransfer(uint64)': FunctionFragment; + 'chainId()': FunctionFragment; + 'completeInboundQueuedTransfer(bytes32)': FunctionFragment; + 'completeOutboundQueuedTransfer(uint64)': FunctionFragment; + 'executeMsg(uint16,bytes32,(bytes32,bytes32,bytes))': FunctionFragment; + 'getCurrentInboundCapacity(uint16)': FunctionFragment; + 'getCurrentOutboundCapacity()': FunctionFragment; + 'getInboundLimitParams(uint16)': FunctionFragment; + 'getInboundQueuedTransfer(bytes32)': FunctionFragment; + 'getMigratesImmutables()': FunctionFragment; + 'getMode()': FunctionFragment; + 'getOutboundLimitParams()': FunctionFragment; + 'getOutboundQueuedTransfer(uint64)': FunctionFragment; + 'getPeer(uint16)': FunctionFragment; + 'getThreshold()': FunctionFragment; + 'getTransceivers()': FunctionFragment; + 'initialize()': FunctionFragment; + 'isMessageApproved(bytes32)': FunctionFragment; + 'isMessageExecuted(bytes32)': FunctionFragment; + 'isPaused()': FunctionFragment; + 'messageAttestations(bytes32)': FunctionFragment; + 'migrate()': FunctionFragment; + 'mode()': FunctionFragment; + 'nextMessageSequence()': FunctionFragment; + 'owner()': FunctionFragment; + 'pause()': FunctionFragment; + 'pauser()': FunctionFragment; + 'quoteDeliveryPrice(uint16,bytes)': FunctionFragment; + 'rateLimitDuration()': FunctionFragment; + 'removeTransceiver(address)': FunctionFragment; + 'setInboundLimit(uint256,uint16)': FunctionFragment; + 'setOutboundLimit(uint256)': FunctionFragment; + 'setPeer(uint16,bytes32,uint8,uint256)': FunctionFragment; + 'setThreshold(uint8)': FunctionFragment; + 'setTransceiver(address)': FunctionFragment; + 'token()': FunctionFragment; + 'tokenDecimals()': FunctionFragment; + 'transceiverAttestedToMessage(bytes32,uint8)': FunctionFragment; + 'transfer(uint256,uint16,bytes32)': FunctionFragment; + 'transfer(uint256,uint16,bytes32,bytes32,bool,bytes)': FunctionFragment; + 'transferOwnership(address)': FunctionFragment; + 'transferPauserCapability(address)': FunctionFragment; + 'unpause()': FunctionFragment; + 'upgrade(address)': FunctionFragment; + }; + + getFunction( + nameOrSignatureOrTopic: + | 'NTT_MANAGER_VERSION' + | 'attestationReceived' + | 'cancelOutboundQueuedTransfer' + | 'chainId' + | 'completeInboundQueuedTransfer' + | 'completeOutboundQueuedTransfer' + | 'executeMsg' + | 'getCurrentInboundCapacity' + | 'getCurrentOutboundCapacity' + | 'getInboundLimitParams' + | 'getInboundQueuedTransfer' + | 'getMigratesImmutables' + | 'getMode' + | 'getOutboundLimitParams' + | 'getOutboundQueuedTransfer' + | 'getPeer' + | 'getThreshold' + | 'getTransceivers' + | 'initialize' + | 'isMessageApproved' + | 'isMessageExecuted' + | 'isPaused' + | 'messageAttestations' + | 'migrate' + | 'mode' + | 'nextMessageSequence' + | 'owner' + | 'pause' + | 'pauser' + | 'quoteDeliveryPrice' + | 'rateLimitDuration' + | 'removeTransceiver' + | 'setInboundLimit' + | 'setOutboundLimit' + | 'setPeer' + | 'setThreshold' + | 'setTransceiver' + | 'token' + | 'tokenDecimals' + | 'transceiverAttestedToMessage' + | 'transfer(uint256,uint16,bytes32)' + | 'transfer(uint256,uint16,bytes32,bytes32,bool,bytes)' + | 'transferOwnership' + | 'transferPauserCapability' + | 'unpause' + | 'upgrade', + ): FunctionFragment; + + encodeFunctionData( + functionFragment: 'NTT_MANAGER_VERSION', + values?: undefined, + ): string; + encodeFunctionData( + functionFragment: 'attestationReceived', + values: [ + BigNumberish, + BytesLike, + TransceiverStructs.NttManagerMessageStruct, + ], + ): string; + encodeFunctionData( + functionFragment: 'cancelOutboundQueuedTransfer', + values: [BigNumberish], + ): string; + encodeFunctionData(functionFragment: 'chainId', values?: undefined): string; + encodeFunctionData( + functionFragment: 'completeInboundQueuedTransfer', + values: [BytesLike], + ): string; + encodeFunctionData( + functionFragment: 'completeOutboundQueuedTransfer', + values: [BigNumberish], + ): string; + encodeFunctionData( + functionFragment: 'executeMsg', + values: [ + BigNumberish, + BytesLike, + TransceiverStructs.NttManagerMessageStruct, + ], + ): string; + encodeFunctionData( + functionFragment: 'getCurrentInboundCapacity', + values: [BigNumberish], + ): string; + encodeFunctionData( + functionFragment: 'getCurrentOutboundCapacity', + values?: undefined, + ): string; + encodeFunctionData( + functionFragment: 'getInboundLimitParams', + values: [BigNumberish], + ): string; + encodeFunctionData( + functionFragment: 'getInboundQueuedTransfer', + values: [BytesLike], + ): string; + encodeFunctionData( + functionFragment: 'getMigratesImmutables', + values?: undefined, + ): string; + encodeFunctionData(functionFragment: 'getMode', values?: undefined): string; + encodeFunctionData( + functionFragment: 'getOutboundLimitParams', + values?: undefined, + ): string; + encodeFunctionData( + functionFragment: 'getOutboundQueuedTransfer', + values: [BigNumberish], + ): string; + encodeFunctionData( + functionFragment: 'getPeer', + values: [BigNumberish], + ): string; + encodeFunctionData( + functionFragment: 'getThreshold', + values?: undefined, + ): string; + encodeFunctionData( + functionFragment: 'getTransceivers', + values?: undefined, + ): string; + encodeFunctionData( + functionFragment: 'initialize', + values?: undefined, + ): string; + encodeFunctionData( + functionFragment: 'isMessageApproved', + values: [BytesLike], + ): string; + encodeFunctionData( + functionFragment: 'isMessageExecuted', + values: [BytesLike], + ): string; + encodeFunctionData(functionFragment: 'isPaused', values?: undefined): string; + encodeFunctionData( + functionFragment: 'messageAttestations', + values: [BytesLike], + ): string; + encodeFunctionData(functionFragment: 'migrate', values?: undefined): string; + encodeFunctionData(functionFragment: 'mode', values?: undefined): string; + encodeFunctionData( + functionFragment: 'nextMessageSequence', + values?: undefined, + ): string; + encodeFunctionData(functionFragment: 'owner', values?: undefined): string; + encodeFunctionData(functionFragment: 'pause', values?: undefined): string; + encodeFunctionData(functionFragment: 'pauser', values?: undefined): string; + encodeFunctionData( + functionFragment: 'quoteDeliveryPrice', + values: [BigNumberish, BytesLike], + ): string; + encodeFunctionData( + functionFragment: 'rateLimitDuration', + values?: undefined, + ): string; + encodeFunctionData( + functionFragment: 'removeTransceiver', + values: [string], + ): string; + encodeFunctionData( + functionFragment: 'setInboundLimit', + values: [BigNumberish, BigNumberish], + ): string; + encodeFunctionData( + functionFragment: 'setOutboundLimit', + values: [BigNumberish], + ): string; + encodeFunctionData( + functionFragment: 'setPeer', + values: [BigNumberish, BytesLike, BigNumberish, BigNumberish], + ): string; + encodeFunctionData( + functionFragment: 'setThreshold', + values: [BigNumberish], + ): string; + encodeFunctionData( + functionFragment: 'setTransceiver', + values: [string], + ): string; + encodeFunctionData(functionFragment: 'token', values?: undefined): string; + encodeFunctionData( + functionFragment: 'tokenDecimals', + values?: undefined, + ): string; + encodeFunctionData( + functionFragment: 'transceiverAttestedToMessage', + values: [BytesLike, BigNumberish], + ): string; + encodeFunctionData( + functionFragment: 'transfer(uint256,uint16,bytes32)', + values: [BigNumberish, BigNumberish, BytesLike], + ): string; + encodeFunctionData( + functionFragment: 'transfer(uint256,uint16,bytes32,bytes32,bool,bytes)', + values: [ + BigNumberish, + BigNumberish, + BytesLike, + BytesLike, + boolean, + BytesLike, + ], + ): string; + encodeFunctionData( + functionFragment: 'transferOwnership', + values: [string], + ): string; + encodeFunctionData( + functionFragment: 'transferPauserCapability', + values: [string], + ): string; + encodeFunctionData(functionFragment: 'unpause', values?: undefined): string; + encodeFunctionData(functionFragment: 'upgrade', values: [string]): string; + + decodeFunctionResult( + functionFragment: 'NTT_MANAGER_VERSION', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'attestationReceived', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'cancelOutboundQueuedTransfer', + data: BytesLike, + ): Result; + decodeFunctionResult(functionFragment: 'chainId', data: BytesLike): Result; + decodeFunctionResult( + functionFragment: 'completeInboundQueuedTransfer', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'completeOutboundQueuedTransfer', + data: BytesLike, + ): Result; + decodeFunctionResult(functionFragment: 'executeMsg', data: BytesLike): Result; + decodeFunctionResult( + functionFragment: 'getCurrentInboundCapacity', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'getCurrentOutboundCapacity', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'getInboundLimitParams', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'getInboundQueuedTransfer', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'getMigratesImmutables', + data: BytesLike, + ): Result; + decodeFunctionResult(functionFragment: 'getMode', data: BytesLike): Result; + decodeFunctionResult( + functionFragment: 'getOutboundLimitParams', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'getOutboundQueuedTransfer', + data: BytesLike, + ): Result; + decodeFunctionResult(functionFragment: 'getPeer', data: BytesLike): Result; + decodeFunctionResult( + functionFragment: 'getThreshold', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'getTransceivers', + data: BytesLike, + ): Result; + decodeFunctionResult(functionFragment: 'initialize', data: BytesLike): Result; + decodeFunctionResult( + functionFragment: 'isMessageApproved', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'isMessageExecuted', + data: BytesLike, + ): Result; + decodeFunctionResult(functionFragment: 'isPaused', data: BytesLike): Result; + decodeFunctionResult( + functionFragment: 'messageAttestations', + data: BytesLike, + ): Result; + decodeFunctionResult(functionFragment: 'migrate', data: BytesLike): Result; + decodeFunctionResult(functionFragment: 'mode', data: BytesLike): Result; + decodeFunctionResult( + functionFragment: 'nextMessageSequence', + data: BytesLike, + ): Result; + decodeFunctionResult(functionFragment: 'owner', data: BytesLike): Result; + decodeFunctionResult(functionFragment: 'pause', data: BytesLike): Result; + decodeFunctionResult(functionFragment: 'pauser', data: BytesLike): Result; + decodeFunctionResult( + functionFragment: 'quoteDeliveryPrice', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'rateLimitDuration', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'removeTransceiver', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'setInboundLimit', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'setOutboundLimit', + data: BytesLike, + ): Result; + decodeFunctionResult(functionFragment: 'setPeer', data: BytesLike): Result; + decodeFunctionResult( + functionFragment: 'setThreshold', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'setTransceiver', + data: BytesLike, + ): Result; + decodeFunctionResult(functionFragment: 'token', data: BytesLike): Result; + decodeFunctionResult( + functionFragment: 'tokenDecimals', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'transceiverAttestedToMessage', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'transfer(uint256,uint16,bytes32)', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'transfer(uint256,uint16,bytes32,bytes32,bool,bytes)', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'transferOwnership', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'transferPauserCapability', + data: BytesLike, + ): Result; + decodeFunctionResult(functionFragment: 'unpause', data: BytesLike): Result; + decodeFunctionResult(functionFragment: 'upgrade', data: BytesLike): Result; + + events: { + 'AdminChanged(address,address)': EventFragment; + 'BeaconUpgraded(address)': EventFragment; + 'InboundTransferQueued(bytes32)': EventFragment; + 'Initialized(uint64)': EventFragment; + 'MessageAlreadyExecuted(bytes32,bytes32)': EventFragment; + 'MessageAttestedTo(bytes32,address,uint8)': EventFragment; + 'NotPaused(bool)': EventFragment; + 'OutboundTransferCancelled(uint256,address,uint256)': EventFragment; + 'OutboundTransferQueued(uint64)': EventFragment; + 'OutboundTransferRateLimited(address,uint64,uint256,uint256)': EventFragment; + 'OwnershipTransferred(address,address)': EventFragment; + 'Paused(bool)': EventFragment; + 'PauserTransferred(address,address)': EventFragment; + 'PeerUpdated(uint16,bytes32,uint8,bytes32,uint8)': EventFragment; + 'ThresholdChanged(uint8,uint8)': EventFragment; + 'TransceiverAdded(address,uint256,uint8)': EventFragment; + 'TransceiverRemoved(address,uint8)': EventFragment; + 'TransferRedeemed(bytes32)': EventFragment; + 'TransferSent(bytes32,bytes32,uint256,uint256,uint16,uint64)': EventFragment; + 'Upgraded(address)': EventFragment; + }; + + getEvent(nameOrSignatureOrTopic: 'AdminChanged'): EventFragment; + getEvent(nameOrSignatureOrTopic: 'BeaconUpgraded'): EventFragment; + getEvent(nameOrSignatureOrTopic: 'InboundTransferQueued'): EventFragment; + getEvent(nameOrSignatureOrTopic: 'Initialized'): EventFragment; + getEvent(nameOrSignatureOrTopic: 'MessageAlreadyExecuted'): EventFragment; + getEvent(nameOrSignatureOrTopic: 'MessageAttestedTo'): EventFragment; + getEvent(nameOrSignatureOrTopic: 'NotPaused'): EventFragment; + getEvent(nameOrSignatureOrTopic: 'OutboundTransferCancelled'): EventFragment; + getEvent(nameOrSignatureOrTopic: 'OutboundTransferQueued'): EventFragment; + getEvent( + nameOrSignatureOrTopic: 'OutboundTransferRateLimited', + ): EventFragment; + getEvent(nameOrSignatureOrTopic: 'OwnershipTransferred'): EventFragment; + getEvent(nameOrSignatureOrTopic: 'Paused'): EventFragment; + getEvent(nameOrSignatureOrTopic: 'PauserTransferred'): EventFragment; + getEvent(nameOrSignatureOrTopic: 'PeerUpdated'): EventFragment; + getEvent(nameOrSignatureOrTopic: 'ThresholdChanged'): EventFragment; + getEvent(nameOrSignatureOrTopic: 'TransceiverAdded'): EventFragment; + getEvent(nameOrSignatureOrTopic: 'TransceiverRemoved'): EventFragment; + getEvent(nameOrSignatureOrTopic: 'TransferRedeemed'): EventFragment; + getEvent(nameOrSignatureOrTopic: 'TransferSent'): EventFragment; + getEvent(nameOrSignatureOrTopic: 'Upgraded'): EventFragment; +} + +export interface AdminChangedEventObject { + previousAdmin: string; + newAdmin: string; +} +export type AdminChangedEvent = TypedEvent< + [string, string], + AdminChangedEventObject +>; + +export type AdminChangedEventFilter = TypedEventFilter; + +export interface BeaconUpgradedEventObject { + beacon: string; +} +export type BeaconUpgradedEvent = TypedEvent< + [string], + BeaconUpgradedEventObject +>; + +export type BeaconUpgradedEventFilter = TypedEventFilter; + +export interface InboundTransferQueuedEventObject { + digest: string; +} +export type InboundTransferQueuedEvent = TypedEvent< + [string], + InboundTransferQueuedEventObject +>; + +export type InboundTransferQueuedEventFilter = + TypedEventFilter; + +export interface InitializedEventObject { + version: BigNumber; +} +export type InitializedEvent = TypedEvent<[BigNumber], InitializedEventObject>; + +export type InitializedEventFilter = TypedEventFilter; + +export interface MessageAlreadyExecutedEventObject { + sourceNttManager: string; + msgHash: string; +} +export type MessageAlreadyExecutedEvent = TypedEvent< + [string, string], + MessageAlreadyExecutedEventObject +>; + +export type MessageAlreadyExecutedEventFilter = + TypedEventFilter; + +export interface MessageAttestedToEventObject { + digest: string; + transceiver: string; + index: number; +} +export type MessageAttestedToEvent = TypedEvent< + [string, string, number], + MessageAttestedToEventObject +>; + +export type MessageAttestedToEventFilter = + TypedEventFilter; + +export interface NotPausedEventObject { + notPaused: boolean; +} +export type NotPausedEvent = TypedEvent<[boolean], NotPausedEventObject>; + +export type NotPausedEventFilter = TypedEventFilter; + +export interface OutboundTransferCancelledEventObject { + sequence: BigNumber; + recipient: string; + amount: BigNumber; +} +export type OutboundTransferCancelledEvent = TypedEvent< + [BigNumber, string, BigNumber], + OutboundTransferCancelledEventObject +>; + +export type OutboundTransferCancelledEventFilter = + TypedEventFilter; + +export interface OutboundTransferQueuedEventObject { + queueSequence: BigNumber; +} +export type OutboundTransferQueuedEvent = TypedEvent< + [BigNumber], + OutboundTransferQueuedEventObject +>; + +export type OutboundTransferQueuedEventFilter = + TypedEventFilter; + +export interface OutboundTransferRateLimitedEventObject { + sender: string; + sequence: BigNumber; + amount: BigNumber; + currentCapacity: BigNumber; +} +export type OutboundTransferRateLimitedEvent = TypedEvent< + [string, BigNumber, BigNumber, BigNumber], + OutboundTransferRateLimitedEventObject +>; + +export type OutboundTransferRateLimitedEventFilter = + TypedEventFilter; + +export interface OwnershipTransferredEventObject { + previousOwner: string; + newOwner: string; +} +export type OwnershipTransferredEvent = TypedEvent< + [string, string], + OwnershipTransferredEventObject +>; + +export type OwnershipTransferredEventFilter = + TypedEventFilter; + +export interface PausedEventObject { + paused: boolean; +} +export type PausedEvent = TypedEvent<[boolean], PausedEventObject>; + +export type PausedEventFilter = TypedEventFilter; + +export interface PauserTransferredEventObject { + oldPauser: string; + newPauser: string; +} +export type PauserTransferredEvent = TypedEvent< + [string, string], + PauserTransferredEventObject +>; + +export type PauserTransferredEventFilter = + TypedEventFilter; + +export interface PeerUpdatedEventObject { + chainId_: number; + oldPeerContract: string; + oldPeerDecimals: number; + peerContract: string; + peerDecimals: number; +} +export type PeerUpdatedEvent = TypedEvent< + [number, string, number, string, number], + PeerUpdatedEventObject +>; + +export type PeerUpdatedEventFilter = TypedEventFilter; + +export interface ThresholdChangedEventObject { + oldThreshold: number; + threshold: number; +} +export type ThresholdChangedEvent = TypedEvent< + [number, number], + ThresholdChangedEventObject +>; + +export type ThresholdChangedEventFilter = + TypedEventFilter; + +export interface TransceiverAddedEventObject { + transceiver: string; + transceiversNum: BigNumber; + threshold: number; +} +export type TransceiverAddedEvent = TypedEvent< + [string, BigNumber, number], + TransceiverAddedEventObject +>; + +export type TransceiverAddedEventFilter = + TypedEventFilter; + +export interface TransceiverRemovedEventObject { + transceiver: string; + threshold: number; +} +export type TransceiverRemovedEvent = TypedEvent< + [string, number], + TransceiverRemovedEventObject +>; + +export type TransceiverRemovedEventFilter = + TypedEventFilter; + +export interface TransferRedeemedEventObject { + digest: string; +} +export type TransferRedeemedEvent = TypedEvent< + [string], + TransferRedeemedEventObject +>; + +export type TransferRedeemedEventFilter = + TypedEventFilter; + +export interface TransferSentEventObject { + recipient: string; + refundAddress: string; + amount: BigNumber; + fee: BigNumber; + recipientChain: number; + msgSequence: BigNumber; +} +export type TransferSentEvent = TypedEvent< + [string, string, BigNumber, BigNumber, number, BigNumber], + TransferSentEventObject +>; + +export type TransferSentEventFilter = TypedEventFilter; + +export interface UpgradedEventObject { + implementation: string; +} +export type UpgradedEvent = TypedEvent<[string], UpgradedEventObject>; + +export type UpgradedEventFilter = TypedEventFilter; + +export interface NttManager extends BaseContract { + connect(signerOrProvider: Signer | Provider | string): this; + attach(addressOrName: string): this; + deployed(): Promise; + + interface: NttManagerInterface; + + queryFilter( + event: TypedEventFilter, + fromBlockOrBlockhash?: string | number | undefined, + toBlock?: string | number | undefined, + ): Promise>; + + listeners( + eventFilter?: TypedEventFilter, + ): Array>; + listeners(eventName?: string): Array; + removeAllListeners( + eventFilter: TypedEventFilter, + ): this; + removeAllListeners(eventName?: string): this; + off: OnEvent; + on: OnEvent; + once: OnEvent; + removeListener: OnEvent; + + functions: { + NTT_MANAGER_VERSION(overrides?: CallOverrides): Promise<[string]>; + + attestationReceived( + sourceChainId: BigNumberish, + sourceNttManagerAddress: BytesLike, + payload: TransceiverStructs.NttManagerMessageStruct, + overrides?: Overrides & { from?: string }, + ): Promise; + + cancelOutboundQueuedTransfer( + messageSequence: BigNumberish, + overrides?: Overrides & { from?: string }, + ): Promise; + + chainId(overrides?: CallOverrides): Promise<[number]>; + + completeInboundQueuedTransfer( + digest: BytesLike, + overrides?: Overrides & { from?: string }, + ): Promise; + + completeOutboundQueuedTransfer( + messageSequence: BigNumberish, + overrides?: PayableOverrides & { from?: string }, + ): Promise; + + executeMsg( + sourceChainId: BigNumberish, + sourceNttManagerAddress: BytesLike, + message: TransceiverStructs.NttManagerMessageStruct, + overrides?: Overrides & { from?: string }, + ): Promise; + + getCurrentInboundCapacity( + chainId_: BigNumberish, + overrides?: CallOverrides, + ): Promise<[BigNumber]>; + + getCurrentOutboundCapacity(overrides?: CallOverrides): Promise<[BigNumber]>; + + getInboundLimitParams( + chainId_: BigNumberish, + overrides?: CallOverrides, + ): Promise<[IRateLimiter.RateLimitParamsStructOutput]>; + + getInboundQueuedTransfer( + digest: BytesLike, + overrides?: CallOverrides, + ): Promise<[IRateLimiter.InboundQueuedTransferStructOutput]>; + + getMigratesImmutables(overrides?: CallOverrides): Promise<[boolean]>; + + getMode(overrides?: CallOverrides): Promise<[number]>; + + getOutboundLimitParams( + overrides?: CallOverrides, + ): Promise<[IRateLimiter.RateLimitParamsStructOutput]>; + + getOutboundQueuedTransfer( + queueSequence: BigNumberish, + overrides?: CallOverrides, + ): Promise<[IRateLimiter.OutboundQueuedTransferStructOutput]>; + + getPeer( + chainId_: BigNumberish, + overrides?: CallOverrides, + ): Promise<[INttManager.NttManagerPeerStructOutput]>; + + getThreshold(overrides?: CallOverrides): Promise<[number]>; + + getTransceivers( + overrides?: CallOverrides, + ): Promise<[string[]] & { result: string[] }>; + + initialize( + overrides?: Overrides & { from?: string }, + ): Promise; + + isMessageApproved( + digest: BytesLike, + overrides?: CallOverrides, + ): Promise<[boolean]>; + + isMessageExecuted( + digest: BytesLike, + overrides?: CallOverrides, + ): Promise<[boolean]>; + + isPaused(overrides?: CallOverrides): Promise<[boolean]>; + + messageAttestations( + digest: BytesLike, + overrides?: CallOverrides, + ): Promise<[number] & { count: number }>; + + migrate( + overrides?: Overrides & { from?: string }, + ): Promise; + + mode(overrides?: CallOverrides): Promise<[number]>; + + nextMessageSequence(overrides?: CallOverrides): Promise<[BigNumber]>; + + owner(overrides?: CallOverrides): Promise<[string]>; + + pause( + overrides?: Overrides & { from?: string }, + ): Promise; + + pauser(overrides?: CallOverrides): Promise<[string]>; + + quoteDeliveryPrice( + recipientChain: BigNumberish, + transceiverInstructions: BytesLike, + overrides?: CallOverrides, + ): Promise<[BigNumber[], BigNumber]>; + + rateLimitDuration(overrides?: CallOverrides): Promise<[BigNumber]>; + + removeTransceiver( + transceiver: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + setInboundLimit( + limit: BigNumberish, + chainId_: BigNumberish, + overrides?: Overrides & { from?: string }, + ): Promise; + + setOutboundLimit( + limit: BigNumberish, + overrides?: Overrides & { from?: string }, + ): Promise; + + setPeer( + peerChainId: BigNumberish, + peerContract: BytesLike, + decimals: BigNumberish, + inboundLimit: BigNumberish, + overrides?: Overrides & { from?: string }, + ): Promise; + + setThreshold( + threshold: BigNumberish, + overrides?: Overrides & { from?: string }, + ): Promise; + + setTransceiver( + transceiver: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + token(overrides?: CallOverrides): Promise<[string]>; + + tokenDecimals(overrides?: CallOverrides): Promise<[number]>; + + transceiverAttestedToMessage( + digest: BytesLike, + index: BigNumberish, + overrides?: CallOverrides, + ): Promise<[boolean]>; + + 'transfer(uint256,uint16,bytes32)'( + amount: BigNumberish, + recipientChain: BigNumberish, + recipient: BytesLike, + overrides?: PayableOverrides & { from?: string }, + ): Promise; + + 'transfer(uint256,uint16,bytes32,bytes32,bool,bytes)'( + amount: BigNumberish, + recipientChain: BigNumberish, + recipient: BytesLike, + refundAddress: BytesLike, + shouldQueue: boolean, + transceiverInstructions: BytesLike, + overrides?: PayableOverrides & { from?: string }, + ): Promise; + + transferOwnership( + newOwner: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + transferPauserCapability( + newPauser: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + unpause( + overrides?: Overrides & { from?: string }, + ): Promise; + + upgrade( + newImplementation: string, + overrides?: Overrides & { from?: string }, + ): Promise; + }; + + NTT_MANAGER_VERSION(overrides?: CallOverrides): Promise; + + attestationReceived( + sourceChainId: BigNumberish, + sourceNttManagerAddress: BytesLike, + payload: TransceiverStructs.NttManagerMessageStruct, + overrides?: Overrides & { from?: string }, + ): Promise; + + cancelOutboundQueuedTransfer( + messageSequence: BigNumberish, + overrides?: Overrides & { from?: string }, + ): Promise; + + chainId(overrides?: CallOverrides): Promise; + + completeInboundQueuedTransfer( + digest: BytesLike, + overrides?: Overrides & { from?: string }, + ): Promise; + + completeOutboundQueuedTransfer( + messageSequence: BigNumberish, + overrides?: PayableOverrides & { from?: string }, + ): Promise; + + executeMsg( + sourceChainId: BigNumberish, + sourceNttManagerAddress: BytesLike, + message: TransceiverStructs.NttManagerMessageStruct, + overrides?: Overrides & { from?: string }, + ): Promise; + + getCurrentInboundCapacity( + chainId_: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + getCurrentOutboundCapacity(overrides?: CallOverrides): Promise; + + getInboundLimitParams( + chainId_: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + getInboundQueuedTransfer( + digest: BytesLike, + overrides?: CallOverrides, + ): Promise; + + getMigratesImmutables(overrides?: CallOverrides): Promise; + + getMode(overrides?: CallOverrides): Promise; + + getOutboundLimitParams( + overrides?: CallOverrides, + ): Promise; + + getOutboundQueuedTransfer( + queueSequence: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + getPeer( + chainId_: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + getThreshold(overrides?: CallOverrides): Promise; + + getTransceivers(overrides?: CallOverrides): Promise; + + initialize( + overrides?: Overrides & { from?: string }, + ): Promise; + + isMessageApproved( + digest: BytesLike, + overrides?: CallOverrides, + ): Promise; + + isMessageExecuted( + digest: BytesLike, + overrides?: CallOverrides, + ): Promise; + + isPaused(overrides?: CallOverrides): Promise; + + messageAttestations( + digest: BytesLike, + overrides?: CallOverrides, + ): Promise; + + migrate( + overrides?: Overrides & { from?: string }, + ): Promise; + + mode(overrides?: CallOverrides): Promise; + + nextMessageSequence(overrides?: CallOverrides): Promise; + + owner(overrides?: CallOverrides): Promise; + + pause( + overrides?: Overrides & { from?: string }, + ): Promise; + + pauser(overrides?: CallOverrides): Promise; + + quoteDeliveryPrice( + recipientChain: BigNumberish, + transceiverInstructions: BytesLike, + overrides?: CallOverrides, + ): Promise<[BigNumber[], BigNumber]>; + + rateLimitDuration(overrides?: CallOverrides): Promise; + + removeTransceiver( + transceiver: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + setInboundLimit( + limit: BigNumberish, + chainId_: BigNumberish, + overrides?: Overrides & { from?: string }, + ): Promise; + + setOutboundLimit( + limit: BigNumberish, + overrides?: Overrides & { from?: string }, + ): Promise; + + setPeer( + peerChainId: BigNumberish, + peerContract: BytesLike, + decimals: BigNumberish, + inboundLimit: BigNumberish, + overrides?: Overrides & { from?: string }, + ): Promise; + + setThreshold( + threshold: BigNumberish, + overrides?: Overrides & { from?: string }, + ): Promise; + + setTransceiver( + transceiver: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + token(overrides?: CallOverrides): Promise; + + tokenDecimals(overrides?: CallOverrides): Promise; + + transceiverAttestedToMessage( + digest: BytesLike, + index: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + 'transfer(uint256,uint16,bytes32)'( + amount: BigNumberish, + recipientChain: BigNumberish, + recipient: BytesLike, + overrides?: PayableOverrides & { from?: string }, + ): Promise; + + 'transfer(uint256,uint16,bytes32,bytes32,bool,bytes)'( + amount: BigNumberish, + recipientChain: BigNumberish, + recipient: BytesLike, + refundAddress: BytesLike, + shouldQueue: boolean, + transceiverInstructions: BytesLike, + overrides?: PayableOverrides & { from?: string }, + ): Promise; + + transferOwnership( + newOwner: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + transferPauserCapability( + newPauser: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + unpause( + overrides?: Overrides & { from?: string }, + ): Promise; + + upgrade( + newImplementation: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + callStatic: { + NTT_MANAGER_VERSION(overrides?: CallOverrides): Promise; + + attestationReceived( + sourceChainId: BigNumberish, + sourceNttManagerAddress: BytesLike, + payload: TransceiverStructs.NttManagerMessageStruct, + overrides?: CallOverrides, + ): Promise; + + cancelOutboundQueuedTransfer( + messageSequence: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + chainId(overrides?: CallOverrides): Promise; + + completeInboundQueuedTransfer( + digest: BytesLike, + overrides?: CallOverrides, + ): Promise; + + completeOutboundQueuedTransfer( + messageSequence: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + executeMsg( + sourceChainId: BigNumberish, + sourceNttManagerAddress: BytesLike, + message: TransceiverStructs.NttManagerMessageStruct, + overrides?: CallOverrides, + ): Promise; + + getCurrentInboundCapacity( + chainId_: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + getCurrentOutboundCapacity(overrides?: CallOverrides): Promise; + + getInboundLimitParams( + chainId_: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + getInboundQueuedTransfer( + digest: BytesLike, + overrides?: CallOverrides, + ): Promise; + + getMigratesImmutables(overrides?: CallOverrides): Promise; + + getMode(overrides?: CallOverrides): Promise; + + getOutboundLimitParams( + overrides?: CallOverrides, + ): Promise; + + getOutboundQueuedTransfer( + queueSequence: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + getPeer( + chainId_: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + getThreshold(overrides?: CallOverrides): Promise; + + getTransceivers(overrides?: CallOverrides): Promise; + + initialize(overrides?: CallOverrides): Promise; + + isMessageApproved( + digest: BytesLike, + overrides?: CallOverrides, + ): Promise; + + isMessageExecuted( + digest: BytesLike, + overrides?: CallOverrides, + ): Promise; + + isPaused(overrides?: CallOverrides): Promise; + + messageAttestations( + digest: BytesLike, + overrides?: CallOverrides, + ): Promise; + + migrate(overrides?: CallOverrides): Promise; + + mode(overrides?: CallOverrides): Promise; + + nextMessageSequence(overrides?: CallOverrides): Promise; + + owner(overrides?: CallOverrides): Promise; + + pause(overrides?: CallOverrides): Promise; + + pauser(overrides?: CallOverrides): Promise; + + quoteDeliveryPrice( + recipientChain: BigNumberish, + transceiverInstructions: BytesLike, + overrides?: CallOverrides, + ): Promise<[BigNumber[], BigNumber]>; + + rateLimitDuration(overrides?: CallOverrides): Promise; + + removeTransceiver( + transceiver: string, + overrides?: CallOverrides, + ): Promise; + + setInboundLimit( + limit: BigNumberish, + chainId_: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + setOutboundLimit( + limit: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + setPeer( + peerChainId: BigNumberish, + peerContract: BytesLike, + decimals: BigNumberish, + inboundLimit: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + setThreshold( + threshold: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + setTransceiver( + transceiver: string, + overrides?: CallOverrides, + ): Promise; + + token(overrides?: CallOverrides): Promise; + + tokenDecimals(overrides?: CallOverrides): Promise; + + transceiverAttestedToMessage( + digest: BytesLike, + index: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + 'transfer(uint256,uint16,bytes32)'( + amount: BigNumberish, + recipientChain: BigNumberish, + recipient: BytesLike, + overrides?: CallOverrides, + ): Promise; + + 'transfer(uint256,uint16,bytes32,bytes32,bool,bytes)'( + amount: BigNumberish, + recipientChain: BigNumberish, + recipient: BytesLike, + refundAddress: BytesLike, + shouldQueue: boolean, + transceiverInstructions: BytesLike, + overrides?: CallOverrides, + ): Promise; + + transferOwnership( + newOwner: string, + overrides?: CallOverrides, + ): Promise; + + transferPauserCapability( + newPauser: string, + overrides?: CallOverrides, + ): Promise; + + unpause(overrides?: CallOverrides): Promise; + + upgrade( + newImplementation: string, + overrides?: CallOverrides, + ): Promise; + }; + + filters: { + 'AdminChanged(address,address)'( + previousAdmin?: null, + newAdmin?: null, + ): AdminChangedEventFilter; + AdminChanged( + previousAdmin?: null, + newAdmin?: null, + ): AdminChangedEventFilter; + + 'BeaconUpgraded(address)'( + beacon?: string | null, + ): BeaconUpgradedEventFilter; + BeaconUpgraded(beacon?: string | null): BeaconUpgradedEventFilter; + + 'InboundTransferQueued(bytes32)'( + digest?: null, + ): InboundTransferQueuedEventFilter; + InboundTransferQueued(digest?: null): InboundTransferQueuedEventFilter; + + 'Initialized(uint64)'(version?: null): InitializedEventFilter; + Initialized(version?: null): InitializedEventFilter; + + 'MessageAlreadyExecuted(bytes32,bytes32)'( + sourceNttManager?: BytesLike | null, + msgHash?: BytesLike | null, + ): MessageAlreadyExecutedEventFilter; + MessageAlreadyExecuted( + sourceNttManager?: BytesLike | null, + msgHash?: BytesLike | null, + ): MessageAlreadyExecutedEventFilter; + + 'MessageAttestedTo(bytes32,address,uint8)'( + digest?: null, + transceiver?: null, + index?: null, + ): MessageAttestedToEventFilter; + MessageAttestedTo( + digest?: null, + transceiver?: null, + index?: null, + ): MessageAttestedToEventFilter; + + 'NotPaused(bool)'(notPaused?: null): NotPausedEventFilter; + NotPaused(notPaused?: null): NotPausedEventFilter; + + 'OutboundTransferCancelled(uint256,address,uint256)'( + sequence?: null, + recipient?: null, + amount?: null, + ): OutboundTransferCancelledEventFilter; + OutboundTransferCancelled( + sequence?: null, + recipient?: null, + amount?: null, + ): OutboundTransferCancelledEventFilter; + + 'OutboundTransferQueued(uint64)'( + queueSequence?: null, + ): OutboundTransferQueuedEventFilter; + OutboundTransferQueued( + queueSequence?: null, + ): OutboundTransferQueuedEventFilter; + + 'OutboundTransferRateLimited(address,uint64,uint256,uint256)'( + sender?: string | null, + sequence?: null, + amount?: null, + currentCapacity?: null, + ): OutboundTransferRateLimitedEventFilter; + OutboundTransferRateLimited( + sender?: string | null, + sequence?: null, + amount?: null, + currentCapacity?: null, + ): OutboundTransferRateLimitedEventFilter; + + 'OwnershipTransferred(address,address)'( + previousOwner?: string | null, + newOwner?: string | null, + ): OwnershipTransferredEventFilter; + OwnershipTransferred( + previousOwner?: string | null, + newOwner?: string | null, + ): OwnershipTransferredEventFilter; + + 'Paused(bool)'(paused?: null): PausedEventFilter; + Paused(paused?: null): PausedEventFilter; + + 'PauserTransferred(address,address)'( + oldPauser?: string | null, + newPauser?: string | null, + ): PauserTransferredEventFilter; + PauserTransferred( + oldPauser?: string | null, + newPauser?: string | null, + ): PauserTransferredEventFilter; + + 'PeerUpdated(uint16,bytes32,uint8,bytes32,uint8)'( + chainId_?: BigNumberish | null, + oldPeerContract?: null, + oldPeerDecimals?: null, + peerContract?: null, + peerDecimals?: null, + ): PeerUpdatedEventFilter; + PeerUpdated( + chainId_?: BigNumberish | null, + oldPeerContract?: null, + oldPeerDecimals?: null, + peerContract?: null, + peerDecimals?: null, + ): PeerUpdatedEventFilter; + + 'ThresholdChanged(uint8,uint8)'( + oldThreshold?: null, + threshold?: null, + ): ThresholdChangedEventFilter; + ThresholdChanged( + oldThreshold?: null, + threshold?: null, + ): ThresholdChangedEventFilter; + + 'TransceiverAdded(address,uint256,uint8)'( + transceiver?: null, + transceiversNum?: null, + threshold?: null, + ): TransceiverAddedEventFilter; + TransceiverAdded( + transceiver?: null, + transceiversNum?: null, + threshold?: null, + ): TransceiverAddedEventFilter; + + 'TransceiverRemoved(address,uint8)'( + transceiver?: null, + threshold?: null, + ): TransceiverRemovedEventFilter; + TransceiverRemoved( + transceiver?: null, + threshold?: null, + ): TransceiverRemovedEventFilter; + + 'TransferRedeemed(bytes32)'( + digest?: BytesLike | null, + ): TransferRedeemedEventFilter; + TransferRedeemed(digest?: BytesLike | null): TransferRedeemedEventFilter; + + 'TransferSent(bytes32,bytes32,uint256,uint256,uint16,uint64)'( + recipient?: null, + refundAddress?: null, + amount?: null, + fee?: null, + recipientChain?: null, + msgSequence?: null, + ): TransferSentEventFilter; + TransferSent( + recipient?: null, + refundAddress?: null, + amount?: null, + fee?: null, + recipientChain?: null, + msgSequence?: null, + ): TransferSentEventFilter; + + 'Upgraded(address)'(implementation?: string | null): UpgradedEventFilter; + Upgraded(implementation?: string | null): UpgradedEventFilter; + }; + + estimateGas: { + NTT_MANAGER_VERSION(overrides?: CallOverrides): Promise; + + attestationReceived( + sourceChainId: BigNumberish, + sourceNttManagerAddress: BytesLike, + payload: TransceiverStructs.NttManagerMessageStruct, + overrides?: Overrides & { from?: string }, + ): Promise; + + cancelOutboundQueuedTransfer( + messageSequence: BigNumberish, + overrides?: Overrides & { from?: string }, + ): Promise; + + chainId(overrides?: CallOverrides): Promise; + + completeInboundQueuedTransfer( + digest: BytesLike, + overrides?: Overrides & { from?: string }, + ): Promise; + + completeOutboundQueuedTransfer( + messageSequence: BigNumberish, + overrides?: PayableOverrides & { from?: string }, + ): Promise; + + executeMsg( + sourceChainId: BigNumberish, + sourceNttManagerAddress: BytesLike, + message: TransceiverStructs.NttManagerMessageStruct, + overrides?: Overrides & { from?: string }, + ): Promise; + + getCurrentInboundCapacity( + chainId_: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + getCurrentOutboundCapacity(overrides?: CallOverrides): Promise; + + getInboundLimitParams( + chainId_: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + getInboundQueuedTransfer( + digest: BytesLike, + overrides?: CallOverrides, + ): Promise; + + getMigratesImmutables(overrides?: CallOverrides): Promise; + + getMode(overrides?: CallOverrides): Promise; + + getOutboundLimitParams(overrides?: CallOverrides): Promise; + + getOutboundQueuedTransfer( + queueSequence: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + getPeer( + chainId_: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + getThreshold(overrides?: CallOverrides): Promise; + + getTransceivers(overrides?: CallOverrides): Promise; + + initialize(overrides?: Overrides & { from?: string }): Promise; + + isMessageApproved( + digest: BytesLike, + overrides?: CallOverrides, + ): Promise; + + isMessageExecuted( + digest: BytesLike, + overrides?: CallOverrides, + ): Promise; + + isPaused(overrides?: CallOverrides): Promise; + + messageAttestations( + digest: BytesLike, + overrides?: CallOverrides, + ): Promise; + + migrate(overrides?: Overrides & { from?: string }): Promise; + + mode(overrides?: CallOverrides): Promise; + + nextMessageSequence(overrides?: CallOverrides): Promise; + + owner(overrides?: CallOverrides): Promise; + + pause(overrides?: Overrides & { from?: string }): Promise; + + pauser(overrides?: CallOverrides): Promise; + + quoteDeliveryPrice( + recipientChain: BigNumberish, + transceiverInstructions: BytesLike, + overrides?: CallOverrides, + ): Promise; + + rateLimitDuration(overrides?: CallOverrides): Promise; + + removeTransceiver( + transceiver: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + setInboundLimit( + limit: BigNumberish, + chainId_: BigNumberish, + overrides?: Overrides & { from?: string }, + ): Promise; + + setOutboundLimit( + limit: BigNumberish, + overrides?: Overrides & { from?: string }, + ): Promise; + + setPeer( + peerChainId: BigNumberish, + peerContract: BytesLike, + decimals: BigNumberish, + inboundLimit: BigNumberish, + overrides?: Overrides & { from?: string }, + ): Promise; + + setThreshold( + threshold: BigNumberish, + overrides?: Overrides & { from?: string }, + ): Promise; + + setTransceiver( + transceiver: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + token(overrides?: CallOverrides): Promise; + + tokenDecimals(overrides?: CallOverrides): Promise; + + transceiverAttestedToMessage( + digest: BytesLike, + index: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + 'transfer(uint256,uint16,bytes32)'( + amount: BigNumberish, + recipientChain: BigNumberish, + recipient: BytesLike, + overrides?: PayableOverrides & { from?: string }, + ): Promise; + + 'transfer(uint256,uint16,bytes32,bytes32,bool,bytes)'( + amount: BigNumberish, + recipientChain: BigNumberish, + recipient: BytesLike, + refundAddress: BytesLike, + shouldQueue: boolean, + transceiverInstructions: BytesLike, + overrides?: PayableOverrides & { from?: string }, + ): Promise; + + transferOwnership( + newOwner: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + transferPauserCapability( + newPauser: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + unpause(overrides?: Overrides & { from?: string }): Promise; + + upgrade( + newImplementation: string, + overrides?: Overrides & { from?: string }, + ): Promise; + }; + + populateTransaction: { + NTT_MANAGER_VERSION( + overrides?: CallOverrides, + ): Promise; + + attestationReceived( + sourceChainId: BigNumberish, + sourceNttManagerAddress: BytesLike, + payload: TransceiverStructs.NttManagerMessageStruct, + overrides?: Overrides & { from?: string }, + ): Promise; + + cancelOutboundQueuedTransfer( + messageSequence: BigNumberish, + overrides?: Overrides & { from?: string }, + ): Promise; + + chainId(overrides?: CallOverrides): Promise; + + completeInboundQueuedTransfer( + digest: BytesLike, + overrides?: Overrides & { from?: string }, + ): Promise; + + completeOutboundQueuedTransfer( + messageSequence: BigNumberish, + overrides?: PayableOverrides & { from?: string }, + ): Promise; + + executeMsg( + sourceChainId: BigNumberish, + sourceNttManagerAddress: BytesLike, + message: TransceiverStructs.NttManagerMessageStruct, + overrides?: Overrides & { from?: string }, + ): Promise; + + getCurrentInboundCapacity( + chainId_: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + getCurrentOutboundCapacity( + overrides?: CallOverrides, + ): Promise; + + getInboundLimitParams( + chainId_: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + getInboundQueuedTransfer( + digest: BytesLike, + overrides?: CallOverrides, + ): Promise; + + getMigratesImmutables( + overrides?: CallOverrides, + ): Promise; + + getMode(overrides?: CallOverrides): Promise; + + getOutboundLimitParams( + overrides?: CallOverrides, + ): Promise; + + getOutboundQueuedTransfer( + queueSequence: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + getPeer( + chainId_: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + getThreshold(overrides?: CallOverrides): Promise; + + getTransceivers(overrides?: CallOverrides): Promise; + + initialize( + overrides?: Overrides & { from?: string }, + ): Promise; + + isMessageApproved( + digest: BytesLike, + overrides?: CallOverrides, + ): Promise; + + isMessageExecuted( + digest: BytesLike, + overrides?: CallOverrides, + ): Promise; + + isPaused(overrides?: CallOverrides): Promise; + + messageAttestations( + digest: BytesLike, + overrides?: CallOverrides, + ): Promise; + + migrate( + overrides?: Overrides & { from?: string }, + ): Promise; + + mode(overrides?: CallOverrides): Promise; + + nextMessageSequence( + overrides?: CallOverrides, + ): Promise; + + owner(overrides?: CallOverrides): Promise; + + pause( + overrides?: Overrides & { from?: string }, + ): Promise; + + pauser(overrides?: CallOverrides): Promise; + + quoteDeliveryPrice( + recipientChain: BigNumberish, + transceiverInstructions: BytesLike, + overrides?: CallOverrides, + ): Promise; + + rateLimitDuration(overrides?: CallOverrides): Promise; + + removeTransceiver( + transceiver: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + setInboundLimit( + limit: BigNumberish, + chainId_: BigNumberish, + overrides?: Overrides & { from?: string }, + ): Promise; + + setOutboundLimit( + limit: BigNumberish, + overrides?: Overrides & { from?: string }, + ): Promise; + + setPeer( + peerChainId: BigNumberish, + peerContract: BytesLike, + decimals: BigNumberish, + inboundLimit: BigNumberish, + overrides?: Overrides & { from?: string }, + ): Promise; + + setThreshold( + threshold: BigNumberish, + overrides?: Overrides & { from?: string }, + ): Promise; + + setTransceiver( + transceiver: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + token(overrides?: CallOverrides): Promise; + + tokenDecimals(overrides?: CallOverrides): Promise; + + transceiverAttestedToMessage( + digest: BytesLike, + index: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + 'transfer(uint256,uint16,bytes32)'( + amount: BigNumberish, + recipientChain: BigNumberish, + recipient: BytesLike, + overrides?: PayableOverrides & { from?: string }, + ): Promise; + + 'transfer(uint256,uint16,bytes32,bytes32,bool,bytes)'( + amount: BigNumberish, + recipientChain: BigNumberish, + recipient: BytesLike, + refundAddress: BytesLike, + shouldQueue: boolean, + transceiverInstructions: BytesLike, + overrides?: PayableOverrides & { from?: string }, + ): Promise; + + transferOwnership( + newOwner: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + transferPauserCapability( + newPauser: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + unpause( + overrides?: Overrides & { from?: string }, + ): Promise; + + upgrade( + newImplementation: string, + overrides?: Overrides & { from?: string }, + ): Promise; + }; +} diff --git a/wormhole-connect/src/routes/ntt/chains/evm/abis/0.1.0/NttManager__factory.ts b/wormhole-connect/src/routes/ntt/chains/evm/abis/0.1.0/NttManager__factory.ts new file mode 100644 index 000000000..e92b4433a --- /dev/null +++ b/wormhole-connect/src/routes/ntt/chains/evm/abis/0.1.0/NttManager__factory.ts @@ -0,0 +1,1928 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ +import { + Signer, + utils, + Contract, + ContractFactory, + BigNumberish, + Overrides, +} from 'ethers'; +import type { Provider, TransactionRequest } from '@ethersproject/providers'; +import type { NttManager, NttManagerInterface } from './NttManager'; + +const _abi = [ + { + inputs: [ + { + internalType: 'address', + name: '_token', + type: 'address', + }, + { + internalType: 'enum IManagerBase.Mode', + name: '_mode', + type: 'uint8', + }, + { + internalType: 'uint16', + name: '_chainId', + type: 'uint16', + }, + { + internalType: 'uint64', + name: '_rateLimitDuration', + type: 'uint64', + }, + { + internalType: 'bool', + name: '_skipRateLimiting', + type: 'bool', + }, + ], + stateMutability: 'nonpayable', + type: 'constructor', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'burnAmount', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'balanceDiff', + type: 'uint256', + }, + ], + name: 'BurnAmountDifferentThanBalanceDiff', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'caller', + type: 'address', + }, + ], + name: 'CallerNotTransceiver', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'canceller', + type: 'address', + }, + { + internalType: 'address', + name: 'sender', + type: 'address', + }, + ], + name: 'CancellerNotSender', + type: 'error', + }, + { + inputs: [ + { + internalType: 'TrimmedAmount', + name: 'newCurrentCapacity', + type: 'uint72', + }, + { + internalType: 'TrimmedAmount', + name: 'newLimit', + type: 'uint72', + }, + ], + name: 'CapacityCannotExceedLimit', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'requiredPayment', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'providedPayment', + type: 'uint256', + }, + ], + name: 'DeliveryPaymentTooLow', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'transceiver', + type: 'address', + }, + ], + name: 'DisabledTransceiver', + type: 'error', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: 'digest', + type: 'bytes32', + }, + ], + name: 'InboundQueuedTransferNotFound', + type: 'error', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: 'digest', + type: 'bytes32', + }, + { + internalType: 'uint256', + name: 'transferTimestamp', + type: 'uint256', + }, + ], + name: 'InboundQueuedTransferStillQueued', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'evmChainId', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'blockChainId', + type: 'uint256', + }, + ], + name: 'InvalidFork', + type: 'error', + }, + { + inputs: [], + name: 'InvalidInitialization', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint8', + name: 'mode', + type: 'uint8', + }, + ], + name: 'InvalidMode', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'account', + type: 'address', + }, + ], + name: 'InvalidPauser', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint16', + name: 'chainId', + type: 'uint16', + }, + { + internalType: 'bytes32', + name: 'peerAddress', + type: 'bytes32', + }, + ], + name: 'InvalidPeer', + type: 'error', + }, + { + inputs: [], + name: 'InvalidPeerChainIdZero', + type: 'error', + }, + { + inputs: [], + name: 'InvalidPeerDecimals', + type: 'error', + }, + { + inputs: [], + name: 'InvalidPeerZeroAddress', + type: 'error', + }, + { + inputs: [], + name: 'InvalidRecipient', + type: 'error', + }, + { + inputs: [], + name: 'InvalidRefundAddress', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint16', + name: 'targetChain', + type: 'uint16', + }, + { + internalType: 'uint16', + name: 'thisChain', + type: 'uint16', + }, + ], + name: 'InvalidTargetChain', + type: 'error', + }, + { + inputs: [], + name: 'InvalidTransceiverZeroAddress', + type: 'error', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: 'msgHash', + type: 'bytes32', + }, + ], + name: 'MessageNotApproved', + type: 'error', + }, + { + inputs: [], + name: 'NoEnabledTransceivers', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'transceiver', + type: 'address', + }, + ], + name: 'NonRegisteredTransceiver', + type: 'error', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: '', + type: 'bytes32', + }, + ], + name: 'NotAnEvmAddress', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'currentCapacity', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'NotEnoughCapacity', + type: 'error', + }, + { + inputs: [], + name: 'NotInitializing', + type: 'error', + }, + { + inputs: [], + name: 'NotMigrating', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint8', + name: 'decimals', + type: 'uint8', + }, + { + internalType: 'uint8', + name: 'decimalsOther', + type: 'uint8', + }, + ], + name: 'NumberOfDecimalsNotEqual', + type: 'error', + }, + { + inputs: [], + name: 'OnlyDelegateCall', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint64', + name: 'queueSequence', + type: 'uint64', + }, + ], + name: 'OutboundQueuedTransferNotFound', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint64', + name: 'queueSequence', + type: 'uint64', + }, + { + internalType: 'uint256', + name: 'transferTimestamp', + type: 'uint256', + }, + ], + name: 'OutboundQueuedTransferStillQueued', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'owner', + type: 'address', + }, + ], + name: 'OwnableInvalidOwner', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'account', + type: 'address', + }, + ], + name: 'OwnableUnauthorizedAccount', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint16', + name: 'chainId', + type: 'uint16', + }, + ], + name: 'PeerNotRegistered', + type: 'error', + }, + { + inputs: [], + name: 'ReentrancyGuardReentrantCall', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'refundAmount', + type: 'uint256', + }, + ], + name: 'RefundFailed', + type: 'error', + }, + { + inputs: [], + name: 'RequireContractIsNotPaused', + type: 'error', + }, + { + inputs: [], + name: 'RequireContractIsPaused', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'retrieved', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'registered', + type: 'uint256', + }, + ], + name: 'RetrievedIncorrectRegisteredTransceivers', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'threshold', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'transceivers', + type: 'uint256', + }, + ], + name: 'ThresholdTooHigh', + type: 'error', + }, + { + inputs: [], + name: 'TooManyTransceivers', + type: 'error', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: 'nttManagerMessageHash', + type: 'bytes32', + }, + ], + name: 'TransceiverAlreadyAttestedToMessage', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'transceiver', + type: 'address', + }, + ], + name: 'TransceiverAlreadyEnabled', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'dust', + type: 'uint256', + }, + ], + name: 'TransferAmountHasDust', + type: 'error', + }, + { + inputs: [], + name: 'UndefinedRateLimiting', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'expectedOwner', + type: 'address', + }, + { + internalType: 'address', + name: 'owner', + type: 'address', + }, + ], + name: 'UnexpectedDeployer', + type: 'error', + }, + { + inputs: [], + name: 'ZeroAmount', + type: 'error', + }, + { + inputs: [], + name: 'ZeroThreshold', + type: 'error', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'previousAdmin', + type: 'address', + }, + { + indexed: false, + internalType: 'address', + name: 'newAdmin', + type: 'address', + }, + ], + name: 'AdminChanged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'beacon', + type: 'address', + }, + ], + name: 'BeaconUpgraded', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'bytes32', + name: 'digest', + type: 'bytes32', + }, + ], + name: 'InboundTransferQueued', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint64', + name: 'version', + type: 'uint64', + }, + ], + name: 'Initialized', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'bytes32', + name: 'sourceNttManager', + type: 'bytes32', + }, + { + indexed: true, + internalType: 'bytes32', + name: 'msgHash', + type: 'bytes32', + }, + ], + name: 'MessageAlreadyExecuted', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'bytes32', + name: 'digest', + type: 'bytes32', + }, + { + indexed: false, + internalType: 'address', + name: 'transceiver', + type: 'address', + }, + { + indexed: false, + internalType: 'uint8', + name: 'index', + type: 'uint8', + }, + ], + name: 'MessageAttestedTo', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'bool', + name: 'notPaused', + type: 'bool', + }, + ], + name: 'NotPaused', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint256', + name: 'sequence', + type: 'uint256', + }, + { + indexed: false, + internalType: 'address', + name: 'recipient', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + ], + name: 'OutboundTransferCancelled', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint64', + name: 'queueSequence', + type: 'uint64', + }, + ], + name: 'OutboundTransferQueued', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'sender', + type: 'address', + }, + { + indexed: false, + internalType: 'uint64', + name: 'sequence', + type: 'uint64', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'currentCapacity', + type: 'uint256', + }, + ], + name: 'OutboundTransferRateLimited', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'previousOwner', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'newOwner', + type: 'address', + }, + ], + name: 'OwnershipTransferred', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'bool', + name: 'paused', + type: 'bool', + }, + ], + name: 'Paused', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'oldPauser', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'newPauser', + type: 'address', + }, + ], + name: 'PauserTransferred', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'uint16', + name: 'chainId_', + type: 'uint16', + }, + { + indexed: false, + internalType: 'bytes32', + name: 'oldPeerContract', + type: 'bytes32', + }, + { + indexed: false, + internalType: 'uint8', + name: 'oldPeerDecimals', + type: 'uint8', + }, + { + indexed: false, + internalType: 'bytes32', + name: 'peerContract', + type: 'bytes32', + }, + { + indexed: false, + internalType: 'uint8', + name: 'peerDecimals', + type: 'uint8', + }, + ], + name: 'PeerUpdated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint8', + name: 'oldThreshold', + type: 'uint8', + }, + { + indexed: false, + internalType: 'uint8', + name: 'threshold', + type: 'uint8', + }, + ], + name: 'ThresholdChanged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'transceiver', + type: 'address', + }, + { + indexed: false, + internalType: 'uint256', + name: 'transceiversNum', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint8', + name: 'threshold', + type: 'uint8', + }, + ], + name: 'TransceiverAdded', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'transceiver', + type: 'address', + }, + { + indexed: false, + internalType: 'uint8', + name: 'threshold', + type: 'uint8', + }, + ], + name: 'TransceiverRemoved', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'bytes32', + name: 'digest', + type: 'bytes32', + }, + ], + name: 'TransferRedeemed', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'bytes32', + name: 'recipient', + type: 'bytes32', + }, + { + indexed: false, + internalType: 'bytes32', + name: 'refundAddress', + type: 'bytes32', + }, + { + indexed: false, + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint256', + name: 'fee', + type: 'uint256', + }, + { + indexed: false, + internalType: 'uint16', + name: 'recipientChain', + type: 'uint16', + }, + { + indexed: false, + internalType: 'uint64', + name: 'msgSequence', + type: 'uint64', + }, + ], + name: 'TransferSent', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'implementation', + type: 'address', + }, + ], + name: 'Upgraded', + type: 'event', + }, + { + inputs: [], + name: 'NTT_MANAGER_VERSION', + outputs: [ + { + internalType: 'string', + name: '', + type: 'string', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint16', + name: 'sourceChainId', + type: 'uint16', + }, + { + internalType: 'bytes32', + name: 'sourceNttManagerAddress', + type: 'bytes32', + }, + { + components: [ + { + internalType: 'bytes32', + name: 'id', + type: 'bytes32', + }, + { + internalType: 'bytes32', + name: 'sender', + type: 'bytes32', + }, + { + internalType: 'bytes', + name: 'payload', + type: 'bytes', + }, + ], + internalType: 'struct TransceiverStructs.NttManagerMessage', + name: 'payload', + type: 'tuple', + }, + ], + name: 'attestationReceived', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint64', + name: 'messageSequence', + type: 'uint64', + }, + ], + name: 'cancelOutboundQueuedTransfer', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'chainId', + outputs: [ + { + internalType: 'uint16', + name: '', + type: 'uint16', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: 'digest', + type: 'bytes32', + }, + ], + name: 'completeInboundQueuedTransfer', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint64', + name: 'messageSequence', + type: 'uint64', + }, + ], + name: 'completeOutboundQueuedTransfer', + outputs: [ + { + internalType: 'uint64', + name: '', + type: 'uint64', + }, + ], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint16', + name: 'sourceChainId', + type: 'uint16', + }, + { + internalType: 'bytes32', + name: 'sourceNttManagerAddress', + type: 'bytes32', + }, + { + components: [ + { + internalType: 'bytes32', + name: 'id', + type: 'bytes32', + }, + { + internalType: 'bytes32', + name: 'sender', + type: 'bytes32', + }, + { + internalType: 'bytes', + name: 'payload', + type: 'bytes', + }, + ], + internalType: 'struct TransceiverStructs.NttManagerMessage', + name: 'message', + type: 'tuple', + }, + ], + name: 'executeMsg', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint16', + name: 'chainId_', + type: 'uint16', + }, + ], + name: 'getCurrentInboundCapacity', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getCurrentOutboundCapacity', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint16', + name: 'chainId_', + type: 'uint16', + }, + ], + name: 'getInboundLimitParams', + outputs: [ + { + components: [ + { + internalType: 'TrimmedAmount', + name: 'limit', + type: 'uint72', + }, + { + internalType: 'TrimmedAmount', + name: 'currentCapacity', + type: 'uint72', + }, + { + internalType: 'uint64', + name: 'lastTxTimestamp', + type: 'uint64', + }, + ], + internalType: 'struct IRateLimiter.RateLimitParams', + name: '', + type: 'tuple', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: 'digest', + type: 'bytes32', + }, + ], + name: 'getInboundQueuedTransfer', + outputs: [ + { + components: [ + { + internalType: 'TrimmedAmount', + name: 'amount', + type: 'uint72', + }, + { + internalType: 'uint64', + name: 'txTimestamp', + type: 'uint64', + }, + { + internalType: 'address', + name: 'recipient', + type: 'address', + }, + ], + internalType: 'struct IRateLimiter.InboundQueuedTransfer', + name: '', + type: 'tuple', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getMigratesImmutables', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getMode', + outputs: [ + { + internalType: 'uint8', + name: '', + type: 'uint8', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getOutboundLimitParams', + outputs: [ + { + components: [ + { + internalType: 'TrimmedAmount', + name: 'limit', + type: 'uint72', + }, + { + internalType: 'TrimmedAmount', + name: 'currentCapacity', + type: 'uint72', + }, + { + internalType: 'uint64', + name: 'lastTxTimestamp', + type: 'uint64', + }, + ], + internalType: 'struct IRateLimiter.RateLimitParams', + name: '', + type: 'tuple', + }, + ], + stateMutability: 'pure', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint64', + name: 'queueSequence', + type: 'uint64', + }, + ], + name: 'getOutboundQueuedTransfer', + outputs: [ + { + components: [ + { + internalType: 'bytes32', + name: 'recipient', + type: 'bytes32', + }, + { + internalType: 'bytes32', + name: 'refundAddress', + type: 'bytes32', + }, + { + internalType: 'TrimmedAmount', + name: 'amount', + type: 'uint72', + }, + { + internalType: 'uint64', + name: 'txTimestamp', + type: 'uint64', + }, + { + internalType: 'uint16', + name: 'recipientChain', + type: 'uint16', + }, + { + internalType: 'address', + name: 'sender', + type: 'address', + }, + { + internalType: 'bytes', + name: 'transceiverInstructions', + type: 'bytes', + }, + ], + internalType: 'struct IRateLimiter.OutboundQueuedTransfer', + name: '', + type: 'tuple', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint16', + name: 'chainId_', + type: 'uint16', + }, + ], + name: 'getPeer', + outputs: [ + { + components: [ + { + internalType: 'bytes32', + name: 'peerAddress', + type: 'bytes32', + }, + { + internalType: 'uint8', + name: 'tokenDecimals', + type: 'uint8', + }, + ], + internalType: 'struct INttManager.NttManagerPeer', + name: '', + type: 'tuple', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getThreshold', + outputs: [ + { + internalType: 'uint8', + name: '', + type: 'uint8', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getTransceivers', + outputs: [ + { + internalType: 'address[]', + name: 'result', + type: 'address[]', + }, + ], + stateMutability: 'pure', + type: 'function', + }, + { + inputs: [], + name: 'initialize', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: 'digest', + type: 'bytes32', + }, + ], + name: 'isMessageApproved', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: 'digest', + type: 'bytes32', + }, + ], + name: 'isMessageExecuted', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'isPaused', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: 'digest', + type: 'bytes32', + }, + ], + name: 'messageAttestations', + outputs: [ + { + internalType: 'uint8', + name: 'count', + type: 'uint8', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'migrate', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'mode', + outputs: [ + { + internalType: 'enum IManagerBase.Mode', + name: '', + type: 'uint8', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'nextMessageSequence', + outputs: [ + { + internalType: 'uint64', + name: '', + type: 'uint64', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'owner', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'pause', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'pauser', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint16', + name: 'recipientChain', + type: 'uint16', + }, + { + internalType: 'bytes', + name: 'transceiverInstructions', + type: 'bytes', + }, + ], + name: 'quoteDeliveryPrice', + outputs: [ + { + internalType: 'uint256[]', + name: '', + type: 'uint256[]', + }, + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'rateLimitDuration', + outputs: [ + { + internalType: 'uint64', + name: '', + type: 'uint64', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'transceiver', + type: 'address', + }, + ], + name: 'removeTransceiver', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'limit', + type: 'uint256', + }, + { + internalType: 'uint16', + name: 'chainId_', + type: 'uint16', + }, + ], + name: 'setInboundLimit', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'limit', + type: 'uint256', + }, + ], + name: 'setOutboundLimit', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint16', + name: 'peerChainId', + type: 'uint16', + }, + { + internalType: 'bytes32', + name: 'peerContract', + type: 'bytes32', + }, + { + internalType: 'uint8', + name: 'decimals', + type: 'uint8', + }, + { + internalType: 'uint256', + name: 'inboundLimit', + type: 'uint256', + }, + ], + name: 'setPeer', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint8', + name: 'threshold', + type: 'uint8', + }, + ], + name: 'setThreshold', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'transceiver', + type: 'address', + }, + ], + name: 'setTransceiver', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'token', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'tokenDecimals', + outputs: [ + { + internalType: 'uint8', + name: '', + type: 'uint8', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: 'digest', + type: 'bytes32', + }, + { + internalType: 'uint8', + name: 'index', + type: 'uint8', + }, + ], + name: 'transceiverAttestedToMessage', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + internalType: 'uint16', + name: 'recipientChain', + type: 'uint16', + }, + { + internalType: 'bytes32', + name: 'recipient', + type: 'bytes32', + }, + ], + name: 'transfer', + outputs: [ + { + internalType: 'uint64', + name: '', + type: 'uint64', + }, + ], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'amount', + type: 'uint256', + }, + { + internalType: 'uint16', + name: 'recipientChain', + type: 'uint16', + }, + { + internalType: 'bytes32', + name: 'recipient', + type: 'bytes32', + }, + { + internalType: 'bytes32', + name: 'refundAddress', + type: 'bytes32', + }, + { + internalType: 'bool', + name: 'shouldQueue', + type: 'bool', + }, + { + internalType: 'bytes', + name: 'transceiverInstructions', + type: 'bytes', + }, + ], + name: 'transfer', + outputs: [ + { + internalType: 'uint64', + name: '', + type: 'uint64', + }, + ], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'newOwner', + type: 'address', + }, + ], + name: 'transferOwnership', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'newPauser', + type: 'address', + }, + ], + name: 'transferPauserCapability', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'unpause', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'newImplementation', + type: 'address', + }, + ], + name: 'upgrade', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, +] as const; + +const _bytecode = + '0x6101606040523480156200001257600080fd5b5060405162006d9338038062006d938339810160408190526200003591620005a6565b84848484846001600160401b03821615801562000050575080155b806200006d57506001600160401b038216158015906200006d5750805b156200008c5760405163e543ef0560e01b815260040160405180910390fd5b506001600160401b0316608052620000a36200010d565b620000ad62000279565b3060a0526001600160a01b03831660c052816001811115620000d357620000d362000640565b610100816001811115620000eb57620000eb62000640565b90525061ffff16610120525050466101405250503360e05250620006e0915050565b6000620001196200032d565b905060006200012762000363565b82548154919250610100900460ff1690811462000148576200014862000656565b60005b818110156200019d57620001888382815481106200016d576200016d6200066c565b6000918252602090912001546001600160a01b031662000392565b80620001948162000698565b9150506200014b565b5060005b8181101562000259576000620001b9826001620006b4565b90505b828110156200024357838181548110620001da57620001da6200066c565b60009182526020909120015484546001600160a01b03909116908590849081106200020957620002096200066c565b6000918252602090912001546001600160a01b0316036200022e576200022e62000656565b806200023a8162000698565b915050620001bc565b5080620002508162000698565b915050620001a1565b508254604060ff909116111562000274576200027462000656565b505050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff1615620002ca5760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b03908116146200032a5780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b6000806200035d60017f8561949d1c6242cee5c5a5aeb6b9c190ee611d7742fcec65d9e5b1341ea04d8a620006ca565b92915050565b6000806200035d60017e758a264b9bdbe3295fe36bd6ff7abaa122f48bf70e90af04a1b8a32d21e4e2620006ca565b60006200039e62000530565b90506000620003ac62000560565b90506000620003ba6200032d565b90506000620003c862000363565b6001600160a01b038616600090815260208681526040918290208251606081018452905460ff80821615158084526101008304821615159484019490945262010000909104169281019290925291925090806200043857508060200151158015620004385750604081015160ff16155b62000447576200044762000656565b604081015184546020830151600160ff9093169290921b166001600160401b03161515906000805b8654610100900460ff16811015620004da57896001600160a01b0316868281548110620004a057620004a06200066c565b6000918252602090912001546001600160a01b031603620004c55760019150620004da565b80620004d18162000698565b9150506200046f565b5081151583151514620004f157620004f162000656565b8115158115151462000507576200050762000656565b8554604085015160ff91821691161062000525576200052562000656565b505050505050505050565b6000806200035d60017f49bca747e973430e858f2f5de357b8dba36ea6d375b81bdb5d53dfaabf0b3a80620006ca565b6000806200035d60017ffd6568c039679b3b7cc93c26c41d9379b7b1bec1677120493b467688302cb120620006ca565b80518015158114620005a157600080fd5b919050565b600080600080600060a08688031215620005bf57600080fd5b85516001600160a01b0381168114620005d757600080fd5b602087015190955060028110620005ed57600080fd5b604087015190945061ffff811681146200060657600080fd5b60608701519093506001600160401b03811681146200062457600080fd5b9150620006346080870162000590565b90509295509295909350565b634e487b7160e01b600052602160045260246000fd5b634e487b7160e01b600052600160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600060018201620006ad57620006ad62000682565b5060010190565b808201808211156200035d576200035d62000682565b818103818111156200035d576200035d62000682565b60805160a05160c05160e05161010051610120516101405161657f620008146000396000611acb01526000818161067c01528181611b9001528181611be001526150900152600081816103f301528181610d7801528181612a2701528181612a9901528181612b5701528181612fa90152614fde01526000818161421d01526142590152600081816108a801528181610cd701528181612a6901528181612af401528181612f0f01528181612f4301528181612f7201528181612ff40152818161305f0152818161322d0152614f3c015260006128c70152600081816104ba015281816110b10152818161168a015281816120ff0152818161216001528181613a4f01528181613b7501528181613bdb01528181613eb50152818161456e0152818161478001526147d0015261657f6000f3fe60806040526004361061027d5760003560e01c80638e3ba8c91161014f578063b4d591bb116100c1578063e75235b81161007a578063e75235b81461082c578063f2fde38b14610841578063f5cfec1814610861578063f7514fbc14610876578063fc0c546a14610896578063fd96063c146108ca57600080fd5b8063b4d591bb1461072e578063c0b07bde14610750578063c128d1701461078e578063d788c147146107cc578063da4856a1146107ec578063e5a986031461080c57600080fd5b80639a8a0592116101135780639a8a05921461066a5780639d782454146106b15780639f86029c146106d15780639fd0506d146106f1578063b187bd2614610706578063b293f97f1461071b57600080fd5b80638e3ba8c9146105e15780638fd3ab80146106015780639057412d14610616578063961b94d01461064457806397c351461461065757600080fd5b80633b97e856116101f35780638129fc1c116101ac5780638129fc1c146104fc5780638413bcba146105115780638456cb591461053157806386e11ffa1461054657806389c619dd146105945780638da5cb5b146105b457600080fd5b80633b97e856146104425780633f4ba83a146104695780634b4fd03b1461047e578063689f90c31461049357806374aa7bfc146104a85780637c918634146104dc57600080fd5b8063190171751161024557806319017175146103475780631f97c9a814610367578063203e4a9b1461039457806323d75e31146103b4578063295a5212146103e1578063396c16b71461042257600080fd5b80630271725014610282578063036de8af146102b55780630677df54146102d75780630900f01014610307578063186ce61214610327575b600080fd5b34801561028e57600080fd5b506102a261029d36600461577e565b610927565b6040519081526020015b60405180910390f35b3480156102c157600080fd5b506102d56102d03660046157b0565b610964565b005b3480156102e357600080fd5b506102f76102f23660046157cd565b6109d0565b60405190151581526020016102ac565b34801561031357600080fd5b506102d56103223660046157b0565b610a05565b34801561033357600080fd5b506102d56103423660046157e6565b610a19565b34801561035357600080fd5b506102d56103623660046157cd565b610a46565b34801561037357600080fd5b5061038761038236600461582b565b610a71565b6040516102ac9190615898565b3480156103a057600080fd5b506102d56103af3660046157b0565b610bcd565b3480156103c057600080fd5b506103c9610c67565b6040516001600160401b0390911681526020016102ac565b3480156103ed57600080fd5b506104157f000000000000000000000000000000000000000000000000000000000000000081565b6040516102ac9190615924565b34801561042e57600080fd5b506102f761043d3660046157cd565b610c80565b34801561044e57600080fd5b50610457610c9e565b60405160ff90911681526020016102ac565b34801561047557600080fd5b506102d5610d5f565b34801561048a57600080fd5b50610457610d74565b34801561049f57600080fd5b506102f7610dad565b3480156104b457600080fd5b506103c97f000000000000000000000000000000000000000000000000000000000000000081565b3480156104e857600080fd5b506102d56104f736600461595b565b610dc0565b34801561050857600080fd5b506102d5610f3c565b34801561051d57600080fd5b506102d561052c3660046157cd565b61103e565b34801561053d57600080fd5b506102d561118a565b34801561055257600080fd5b5061055b61119d565b6040805182516001600160481b03908116825260208085015190911690820152918101516001600160401b0316908201526060016102ac565b3480156105a057600080fd5b506104576105af3660046157cd565b611201565b3480156105c057600080fd5b506105c961121a565b6040516001600160a01b0390911681526020016102ac565b3480156105ed57600080fd5b506102f76105fc3660046159a3565b611248565b34801561060d57600080fd5b506102d5611282565b34801561062257600080fd5b50610636610631366004615ab3565b611384565b6040516102ac929190615b02565b6103c9610652366004615b4a565b611487565b6103c961066536600461582b565b611501565b34801561067657600080fd5b5061069e7f000000000000000000000000000000000000000000000000000000000000000081565b60405161ffff90911681526020016102ac565b3480156106bd57600080fd5b506102d56106cc366004615b82565b6117a8565b3480156106dd57600080fd5b506102d56106ec3660046157b0565b611845565b3480156106fd57600080fd5b506105c96118de565b34801561071257600080fd5b506102f76118f7565b6103c9610729366004615c46565b61190c565b34801561073a57600080fd5b5061074361196d565b6040516102ac9190615cc3565b34801561075c57600080fd5b50610781604051806040016040528060058152602001640302e312e360dc1b81525081565b6040516102ac9190615d10565b34801561079a57600080fd5b506107ae6107a936600461577e565b6119d5565b604080518251815260209283015160ff1692810192909252016102ac565b3480156107d857600080fd5b5061055b6107e736600461577e565b611a25565b3480156107f857600080fd5b506102d5610807366004615b82565b611aa0565b34801561081857600080fd5b506102d5610827366004615d23565b611cb3565b34801561083857600080fd5b50610457611d3e565b34801561084d57600080fd5b506102d561085c3660046157b0565b611d48565b34801561086d57600080fd5b506102a2611e06565b34801561088257600080fd5b506102d561089136600461582b565b611e3c565b3480156108a257600080fd5b506105c97f000000000000000000000000000000000000000000000000000000000000000081565b3480156108d657600080fd5b506108ea6108e53660046157cd565b612081565b6040805182516001600160481b031681526020808401516001600160401b031690820152918101516001600160a01b0316908201526060016102ac565b60008061093b61093684611a25565b6120fb565b90506000610947610c9e565b905061095c6001600160481b03831682612209565b949350505050565b61097461096f61121a565b612229565b600061097e612272565b80546001600160a01b038481166001600160a01b031983168117845560405193945091169182907f51c4874e0f23f262e04a38c51751336dde72126d67f53eb672aaff02996b3ef690600090a3505050565b6000806109db611d3e565b90508060ff166109ea84611201565b60ff16101580156109fe575060008160ff16115b9392505050565b610a0d6122a0565b610a16816122d2565b50565b610a216122a0565b6000610a2b610c9e565b9050610a41610a3b8483806123e0565b8361242d565b505050565b610a4e6122a0565b6000610a58610c9e565b9050610a6d610a688383806123e0565b612452565b5050565b6040805160e08101825260008082526020820181905291810182905260608082018390526080820183905260a082019290925260c0810191909152610ab4612463565b6001600160401b0383811660009081526020928352604090819020815160e0810183528154815260018201549481019490945260028101546001600160481b03811692850192909252600160481b82049092166060840152600160881b900461ffff16608083015260038101546001600160a01b031660a083015260048101805460c084019190610b4490615d40565b80601f0160208091040260200160405190810160405280929190818152602001828054610b7090615d40565b8015610bbd5780601f10610b9257610100808354040283529160200191610bbd565b820191906000526020600020905b815481529060010190602001808311610ba057829003601f168201915b5050505050815250509050919050565b610bd56122a0565b610bde81612491565b506000610be9612743565b805490915060ff16600003610c0457805460ff191660011781555b7ff05962b5774c658e85ed80c91a75af9d66d2af2253dda480f90bce78aff5eda582610c2e612771565b548354604080516001600160a01b03909416845261010090920460ff908116602085015216908201526060015b60405180910390a15050565b6000610c7161279f565b546001600160401b0316919050565b6000610c8a6127cd565b600092835260205250604090205460ff1690565b60408051600481526024810182526020810180516001600160e01b031663313ce56760e01b179052905160009182916001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001691610d0191615d7a565b600060405180830381855afa9150503d8060008114610d3c576040519150601f19603f3d011682016040523d82523d6000602084013e610d41565b606091505b5091505080806020019051810190610d599190615d96565b91505090565b610d6a61096f61121a565b610d726127fb565b565b60007f00000000000000000000000000000000000000000000000000000000000000006001811115610da857610da861590e565b905090565b6000610db7612861565b5460ff16919050565b610dc86122a0565b8361ffff16600003610ded5760405163100b0f2760e11b815260040160405180910390fd5b82610e0b5760405163f839a0cb60e01b815260040160405180910390fd5b8160ff16600003610e2f5760405163ade64f0b60e01b815260040160405180910390fd5b6000610e3961288f565b61ffff8616600090815260209182526040908190208151808301909252805482526001015460ff1691810191909152905083610e7361288f565b61ffff87166000908152602091909152604090205582610e9161288f565b61ffff8716600090815260209190915260408120600101805460ff191660ff9390931692909217909155610ec3610c9e565b9050610ed9610ed38483806123e0565b8761242d565b81516020808401516040805193845260ff918216928401929092529082018790528516606082015261ffff8716907f1456404e7f41f35c3daac941bb50bad417a66275c3040061b4287d787719599d9060800160405180910390a2505050505050565b610f446128bd565b60008051602061652a8339815191528054600160401b810460ff1615906001600160401b0316600081158015610f775750825b90506000826001600160401b03166001148015610f935750303b155b905081158015610fa1575080155b15610fbf5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610fe957845460ff60401b1916600160401b1785555b610ff1612906565b831561103757845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050565b61104661291e565b61104e6118f7565b1561106c576040516309e3d0f360e11b815260040160405180910390fd5b600061107782612081565b905080602001516001600160401b03166000036110af57604051630301bcaf60e61b8152600481018390526024015b60405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160401b031681602001516001600160401b0316426110f29190615dc9565b10156111295760208101516040516301cb739d60e71b8152600481018490526001600160401b0390911660248201526044016110a6565b611131612956565b6000838152602091909152604080822080546001600160881b031916815560010180546001600160a01b031916905582015182516111729285929190612984565b50610a16600160008051602061650a83398151915255565b61119561096f61121a565b610d72612bba565b60408051606081018252600080825260208201819052918101919091526111c2612c1b565b6040805160608101825291546001600160481b038082168452600160481b8204166020840152600160901b90046001600160401b031690820152919050565b600061121461120f83612c49565b612c85565b92915050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b600080600160ff84161b61125a6127cd565b600086815260209190915260409020546101009004166001600160401b031611905092915050565b61128a6128bd565b611292612cb8565b61129d906001615ddc565b60008051602061652a8339815191528054600160401b900460ff16806112d0575080546001600160401b03808416911610155b156112ee5760405163f92ee8a960e01b815260040160405180910390fd5b805468ffffffffffffffffff19166001600160401b03831617600160401b178155611317612cce565b5460ff1661133857604051632866815360e11b815260040160405180910390fd5b61134061290e565b805460ff60401b191681556040516001600160401b03831681527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602001610c5b565b6060600080611391612cfc565b8054806020026020016040519081016040528092919081815260200182805480156113e557602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116113c7575b50505050509050600073__$93083e246e55d56d98f3df2872cd16bfd0$__63b620e8728684516040518363ffffffff1660e01b8152600401611428929190615e03565b600060405180830381865af4158015611445573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261146d9190810190615e6a565b905061147a868284612d29565b9350935050509250929050565b600061149161291e565b6114996118f7565b156114b7576040516309e3d0f360e11b815260040160405180910390fd5b6040805160018082528183019092526114e89186918691869182916000919060208201818036833701905050612ea9565b90506109fe600160008051602061650a83398151915255565b600061150b61291e565b6115136118f7565b15611531576040516309e3d0f360e11b815260040160405180910390fd5b600061153b612463565b6001600160401b0384811660009081526020928352604090819020815160e0810183528154815260018201549481019490945260028101546001600160481b03811692850192909252600160481b82049092166060840152600160881b900461ffff16608083015260038101546001600160a01b031660a083015260048101805460c0840191906115cb90615d40565b80601f01602080910402602001604051908101604052809291908181526020018280546115f790615d40565b80156116445780601f1061161957610100808354040283529160200191611644565b820191906000526020600020905b81548152906001019060200180831161162757829003601f168201915b505050505081525050905080606001516001600160401b031660000361168857604051635feafa3160e11b81526001600160401b03841660048201526024016110a6565b7f00000000000000000000000000000000000000000000000000000000000000006001600160401b031681606001516001600160401b0316426116cb9190615dc9565b101561170357606081015160405163c06cf05f60e01b81526001600160401b03808616600483015290911660248201526044016110a6565b61170b612463565b6001600160401b038416600090815260209190915260408120818155600181018290556002810180546001600160981b03191690556003810180546001600160a01b0319169055906117606004830182615724565b50506117898382604001518360800151846000015185602001518660a001518760c001516131ed565b9150506117a3600160008051602061650a83398151915255565b919050565b6117b0613483565b3360009081526020919091526040902054610100900460ff166117e85760405163a0ae911d60e01b81523360048201526024016110a6565b6117f06118f7565b1561180e576040516309e3d0f360e11b815260040160405180910390fd5b61181883836134b1565b600061182484836134fa565b905061182f816109d0565b1561183f5761183f848484611aa0565b50505050565b61184d6122a0565b611856816135cf565b6000611860612743565b9050600061186c612771565b54825460ff610100909204821692501681101561189157815460ff191660ff82161782555b8154604080516001600160a01b038616815260ff90921660208301527f697a3853515b88013ad432f29f53d406debc9509ed6d9313dcfe115250fcd18f91015b60405180910390a1505050565b60006118e8612272565b546001600160a01b0316919050565b600080611902613897565b5460021492915050565b600061191661291e565b61191e6118f7565b1561193c576040516309e3d0f360e11b815260040160405180910390fd5b61194a878787878787612ea9565b9050611963600160008051602061650a83398151915255565b9695505050505050565b6060611977612cfc565b8054806020026020016040519081016040528092919081815260200182805480156119cb57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116119ad575b5050505050905090565b60408051808201909152600080825260208201526119f161288f565b61ffff909216600090815260209283526040908190208151808301909252805482526001015460ff16928101929092525090565b6040805160608101825260008082526020820181905291810191909152611a4a6138c5565b61ffff9290921660009081526020928352604090819020815160608101835290546001600160481b038082168352600160481b82041694820194909452600160901b9093046001600160401b0316908301525090565b611aa86118f7565b15611ac6576040516309e3d0f360e11b815260040160405180910390fd5b611aef7f00000000000000000000000000000000000000000000000000000000000000006138f3565b600080611afd85858561391c565b915091508015611b0e575050505050565b6040808401519051635399ded560e11b815260009173__$93083e246e55d56d98f3df2872cd16bfd0$__9163a733bdaa91611b4b91600401615d10565b608060405180830381865af4158015611b68573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b8c9190615f73565b90507f000000000000000000000000000000000000000000000000000000000000000061ffff16816060015161ffff1614611c0f576060810151604051631ee5902560e11b815261ffff91821660048201527f000000000000000000000000000000000000000000000000000000000000000090911660248201526044016110a6565b6000611c19610c9e565b90506000611c498283611c428587600001516001600160481b031661220990919063ffffffff16565b91906123e0565b90506000611c5a8460400151613a1f565b90506000611c68838b613a4b565b90508015611c8757611c7b878484613a9a565b50505050505050505050565b50611c92828a613b73565b611c9b82613bd9565b611ca88682846000612984565b505050505050505050565b611cbb6122a0565b8060ff16600003611cdf5760405163831761d760e01b815260040160405180910390fd5b6000611ce9612743565b805460ff84811660ff19831617835591925016611d04613c29565b6040805160ff8084168252851660208201527f2a855b929b9a53c6fb5b5ed248b27e502b709c088e036a5aa17620c8fc5085a991016118d1565b6000610db7612743565b611d506122a0565b611d5981613cca565b6000611d63613d05565b9050611d6d613d33565b60005b8154811015610a4157818181548110611d8b57611d8b615ff3565b600091825260209091200154604051632c7b84dd60e11b81526001600160a01b038581166004830152909116906358f709ba90602401600060405180830381600087803b158015611ddb57600080fd5b505af1158015611def573d6000803e3d6000fd5b505050508080611dfe90616009565b915050611d70565b600080611e1461093661119d565b90506000611e20610c9e565b9050611e356001600160481b03831682612209565b9250505090565b611e4461291e565b611e4c6118f7565b15611e6a576040516309e3d0f360e11b815260040160405180910390fd5b6000611e74612463565b6001600160401b0383811660009081526020928352604090819020815160e0810183528154815260018201549481019490945260028101546001600160481b03811692850192909252600160481b82049092166060840152600160881b900461ffff16608083015260038101546001600160a01b031660a083015260048101805460c084019190611f0490615d40565b80601f0160208091040260200160405190810160405280929190818152602001828054611f3090615d40565b8015611f7d5780601f10611f5257610100808354040283529160200191611f7d565b820191906000526020600020905b815481529060010190602001808311611f6057829003601f168201915b505050505081525050905080606001516001600160401b0316600003611fc157604051635feafa3160e11b81526001600160401b03831660048201526024016110a6565b60a08101516001600160a01b031633146120055760a081015160405163ceb40a8560e01b81523360048201526001600160a01b0390911660248201526044016110a6565b61200d612463565b6001600160401b038316600090815260209190915260408120818155600181018290556002810180546001600160981b03191690556003810180546001600160a01b0319169055906120626004830182615724565b5050611172826001600160401b031660001b3383604001516001612984565b60408051606081018252600080825260208201819052918101919091526120a6612956565b60009283526020908152604092839020835160608101855281546001600160481b0381168252600160481b90046001600160401b031692810192909252600101546001600160a01b0316928101929092525090565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160401b031660000361214657602082015160ff1668ffffffffffffffff0017611214565b604082015182516001600160401b039182164203916000917f0000000000000000000000000000000000000000000000000000000000000000821691849160081c166001600160401b0316028161219f5761219f616022565b602086015186516001600160401b03600892831c8116949093049390930193506000926121d29285929190911c16613d84565b90506122006121e082613d9a565b602087015160ff1660ff1660089190911b68ffffffffffffffff00161790565b95945050505050565b60006001600160401b03600884901c1660ff841682611963838387613e02565b336122326118de565b6001600160a01b03161415801561225257506001600160a01b0381163314155b15610a165760405163e2a08e5d60e01b81523360048201526024016110a6565b60008061121460017fbfa91572ce1e5fe8776a160d3b1f862e83f5ee2c080a7423b4761602a3ad124a615dc9565b336122a961121a565b6001600160a01b031614610d725760405163118cdaa760e01b81523360048201526024016110a6565b6122da6128bd565b6122e381613e6b565b60006122ed612cce565b805490915060ff161561230257612302616038565b805460ff191660011781556040805163011fa75760e71b815290513091638fd3ab8091600480830192600092919082900301818387803b15801561234557600080fd5b505af1158015612359573d6000803e3d6000fd5b50505050306001600160a01b031663689f90c36040518163ffffffff1660e01b8152600401602060405180830381865afa15801561239b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123bf919061604e565b6123cb576123cb613eab565b6123d56000613f55565b805460ff1916905550565b6000806123f76123f1600886613f70565b84613f70565b90506000612406868684613e02565b905061196361241482613d9a565b8360ff1660089190911b68ffffffffffffffff00161790565b610a6d826124396138c5565b61ffff8416600090815260209190915260409020613f85565b610a168161245e612c1b565b613f85565b60008061121460017f852fa0677fef8612c6c15b518c9fa56761e9ed15cfd5c6e5399e5467985ac7ee615dc9565b60008061249c613483565b905060006124a861408e565b905060006124b4612cfc565b905060006124c0612771565b90506001600160a01b0386166124e957604051632f44bd7760e01b815260040160405180910390fd5b6001600160a01b03861660009081526020859052604090205460ff1615612534576001600160a01b0386166000908152602085905260409020805461ff001916610100179055612631565b8054604060ff9091161061255b5760405163891684c360e01b815260040160405180910390fd5b6040805160608101825260018082526020808301918252845460ff9081168486019081526001600160a01b038c166000908152928a905294822093518454935195518216620100000262ff0000199615156101000261ff00199215159290921661ffff199095169490941717949094169190911790915582549091169082906125e38361606b565b91906101000a81548160ff021916908360ff16021790555050612604613d05565b8054600181018255600091825260209091200180546001600160a01b0319166001600160a01b0388161790555b81546001808201845560008481526020902090910180546001600160a01b0319166001600160a01b0389161790558154610100900460ff169082906126758361606b565b82546101009290920a60ff8181021990931691831602179091556001600160a01b03881660009081526020879052604090205485546001620100009092049092161b6001600160401b03918216818117935091168117036126f457604051638d68f84d60e01b81526001600160a01b03881660048201526024016110a6565b835467ffffffffffffffff19166001600160401b0382161784556127166140bc565b505050506001600160a01b0392909216600090815260209290925250604090205462010000900460ff1690565b60008061121460017f47028352a8b3feae1a85fba43bc13f990568bb1637dce33d3dbfd791a0808b80615dc9565b60008061121460017f8561949d1c6242cee5c5a5aeb6b9c190ee611d7742fcec65d9e5b1341ea04d8a615dc9565b60008061121460017fad78307a8b51804c575f26039dcb87c58925afb3b7c08732f3b21b942aed7a77615dc9565b60008061121460017f68dfeeddfa5e4e9adceec01a3aba274bdcbab3f6ac9956417a4332f2b08abddb615dc9565b6128036118f7565b61282057604051637e38d1d360e11b815260040160405180910390fd5b61282a60016141fd565b604051600081527fe11c2112add17fb763d3bd59f63b10429c3e11373da4fb8ef6725107a2fdc4b0906020015b60405180910390a1565b60008061121460017f5443fea4dc453d96b81ce55b62e11a4094cc4cbb8a360956a7253cfdb42506cc615dc9565b60008061121460017febcc9f646b0f459ff8f387587d536b0af3484cf442e1577400f322334e7d1ef1615dc9565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163003610d7257604051633c64f99360e21b815260040160405180910390fd5b61290e61420a565b612916613c29565b610d726140bc565b60008051602061650a83398151915280546001190161295057604051633ee5aeb560e01b815260040160405180910390fd5b60029055565b60008061121460017f4e8909a01183a67951f5a6147d6e76ed02ea890c72afea01a9edee91edc609b9615dc9565b60006129a1612991610c9e565b6001600160481b03851690612209565b905081156129f757604080518681526001600160a01b03861660208201529081018290527ff80e572ae1b63e2449629b6c7d783add85c36473926f216077f17ee002bcfd079060600160405180910390a1612a23565b60405185907f504e6efe18ab9eed10dc6501a417f5b12a2f7f2b1593aed9b89f9bce3cf29a9190600090a25b60007f00000000000000000000000000000000000000000000000000000000000000006001811115612a5757612a5761590e565b03612a9557612a906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001685836142b1565b611037565b60017f00000000000000000000000000000000000000000000000000000000000000006001811115612ac957612ac961590e565b03612b55576040516340c10f1960e01b81526001600160a01b038581166004830152602482018390527f000000000000000000000000000000000000000000000000000000000000000016906340c10f1990604401600060405180830381600087803b158015612b3857600080fd5b505af1158015612b4c573d6000803e3d6000fd5b50505050611037565b7f00000000000000000000000000000000000000000000000000000000000000006001811115612b8757612b8761590e565b6040516366001a8960e01b815260ff90911660048201526024016110a6565b600160008051602061650a83398151915255565b612bc26118f7565b15612be0576040516309e3d0f360e11b815260040160405180910390fd5b612bea60026141fd565b604051600181527f0e2fb031ee032dc02d8011dc50b816eb450cf856abd8261680dac74f72165bd290602001612857565b60008061121460017f7c25289a27ec8c9be54d4a154cf80490d69bda989cdb8328232e08fea9220421615dc9565b600080612c54614314565b905080612c5f6127cd565b60009485526020526040909320546101009004929092166001600160401b031692915050565b60005b6001600160401b038216156117a357612ca260018361608a565b9091169080612cb08161606b565b915050612c88565b600060008051602061652a833981519152610c71565b60008061121460017f7487ca88d037ca20519908b1ee7556206bef53bce0226a348750cb9d4f688e4f615dc9565b60008061121460017e758a264b9bdbe3295fe36bd6ff7abaa122f48bf70e90af04a1b8a32d21e4e2615dc9565b805160609060009081612d3a613483565b90506000826001600160401b03811115612d5657612d566159c8565b604051908082528060200260200182016040528015612d7f578160200160208202803683370190505b5090506000805b84811015612e98576000888281518110612da257612da2615ff3565b6020908102919091018101516001600160a01b03811660008181529288905260408320548d5192945062010000900460ff16929163b5634c73908f908f9086908110612df057612df0615ff3565b60200260200101516040518363ffffffff1660e01b8152600401612e159291906160cc565b602060405180830381865afa158015612e32573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e5691906160e9565b905080868581518110612e6b57612e6b615ff3565b6020908102919091010152612e808186616102565b94505050508080612e9090616009565b915050612d86565b50909450925050505b935093915050565b600086600003612ecc57604051631f2a200560e01b815260040160405180910390fd5b84612eea57604051634e46966960e11b815260040160405180910390fd5b83612f085760405163717f139360e11b815260040160405180910390fd5b6000612f347f00000000000000000000000000000000000000000000000000000000000000003061431e565b9050612f6b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633308b6143ce565b6000612f977f00000000000000000000000000000000000000000000000000000000000000003061431e565b9050612fa38282615dc9565b985060017f00000000000000000000000000000000000000000000000000000000000000006001811115612fd957612fd961590e565b036130b257604051630852cd8d60e31b8152600481018a90527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906342966c6890602401600060405180830381600087803b15801561304057600080fd5b505af1158015613054573d6000803e3d6000fd5b5050505060006130847f00000000000000000000000000000000000000000000000000000000000000003061431e565b90508083146130b0576040516302156a8f60e01b815260048101849052602481018290526044016110a6565b505b505060006130c08888614406565b905060006130df6130cf610c9e565b6001600160481b038416906144be565b905060006130eb61450d565b905060006130f88361456a565b9050861580156131055750805b1561313657613112611e06565b6040516326fb55dd60e01b81526004810191909152602481018c90526044016110a6565b8680156131405750805b156131bc57337ff33512b84e24a49905c26c6991942fc5a9652411769fc1e448f967cdb049f08a838d613171611e06565b604080516001600160401b03909416845260208401929092529082015260600160405180910390a26131a882858c8c8c338c6145b8565b6131b134614712565b509250611963915050565b506131c68261477e565b6131d0828a6147ce565b6131df81848b8b8b338b6131ed565b9a9950505050505050505050565b60008060008060006131ff8a87614834565b935093509350935060008c9050600060405180608001604052808e6001600160481b031681526020016132587f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690565b81526020018c81526020018d61ffff168152509050600073__$93083e246e55d56d98f3df2872cd16bfd0$__638b4979b86040518060600160405280866001600160401b031660001b81526020016132b68e6001600160a01b031690565b8152604080516315cfa3cb60e11b815287516001600160481b031660048201526020888101516024830152918801516044820152606088015161ffff16606482015291019073__$93083e246e55d56d98f3df2872cd16bfd0$__90632b9f479690608401600060405180830381865af4158015613337573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261335f9190810190616115565b8152506040518263ffffffff1660e01b815260040161337e9190616172565b600060405180830381865af415801561339b573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526133c39190810190616115565b90508c6133f1818d6133d361288f565b61ffff851660009081526020919091526040902054898b8d886149bf565b8e7fe54e51e42099622516fa3b48e9733581c9dbdcb771cafb093f745a0532a359828e8e613430613420610c9e565b6001600160481b03861690612209565b604080519384526020840192909252908201526060810188905261ffff841660808201526001600160401b03871660a082015260c00160405180910390a150929f9e505050505050505050505050505050565b60008061121460017f49bca747e973430e858f2f5de357b8dba36ea6d375b81bdb5d53dfaabf0b3a80615dc9565b806134ba61288f565b61ffff84166000908152602091909152604090205414610a6d57604051635788c0fd60e11b815261ffff83166004820152602481018290526044016110a6565b60008073__$93083e246e55d56d98f3df2872cd16bfd0$__63b3f07bbd85856040518363ffffffff1660e01b8152600401613536929190616185565b602060405180830381865af4158015613553573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061357791906160e9565b90506135a481613585613483565b336000908152602091909152604090205462010000900460ff16611248565b156135c557604051631089c4a160e11b8152600481018290526024016110a6565b6109fe8133614b0c565b60006135d9613483565b905060006135e561408e565b905060006135f1612cfc565b90506001600160a01b03841661361a57604051632f44bd7760e01b815260040160405180910390fd5b6001600160a01b03841660009081526020849052604090205460ff1661365e57604051630d583f4760e41b81526001600160a01b03851660048201526024016110a6565b6001600160a01b038416600090815260208490526040902054610100900460ff166136a7576040516307d86e9160e21b81526001600160a01b03851660048201526024016110a6565b6001600160a01b0384166000908152602084905260409020805461ff00191690556136d0612771565b8054610100900460ff169060016136e6836161a2565b82546101009290920a60ff8181021990931691831602179091556001600160a01b03861660009081526020869052604090205484546001600160401b036001620100009093049093169190911b1981168216925016811061374957613749616038565b825467ffffffffffffffff19166001600160401b0382161783558154600090815b8181101561386f57876001600160a01b031685828154811061378e5761378e615ff3565b6000918252602090912001546001600160a01b03160361385d57846137b4600184615dc9565b815481106137c4576137c4615ff3565b9060005260206000200160009054906101000a90046001600160a01b03168582815481106137f4576137f4615ff3565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555084805480613832576138326161bf565b600082815260209020810160001990810180546001600160a01b03191690550190556001925061386f565b8061386781616009565b91505061376a565b508161387d5761387d616038565b6138856140bc565b61388e87614bad565b50505050505050565b60008061121460017f64bacf405c5d7f563d3ba5252584a52c37e4fee380fd825b10666c27b8258023615dc9565b60008061121460017fefb21dcaedea63b55c44882f329622e13a8d0f5b947b3a372826208a9003da16615dc9565b468114610a16576040516377d879fb60e01b8152600481018290524660248201526044016110a6565b600080600073__$93083e246e55d56d98f3df2872cd16bfd0$__63b3f07bbd87866040518363ffffffff1660e01b815260040161395a929190616185565b602060405180830381865af4158015613977573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061399b91906160e9565b90506139a6816109d0565b6139c657604051630451c4fb60e41b8152600481018290526024016110a6565b60006139d182614d21565b90508015613a1357604051829087907f4069dff8c9df7e38d2867c0910bd96fd61787695e5380281148c04932d02bef290600090a3600093509150612ea19050565b90969095509350505050565b600060a082901c15613a475760405163033b960d60e41b8152600481018390526024016110a6565b5090565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160401b0316600003613a855760006109fe565b6109fe613a9461093684611a25565b84614d62565b6040518060600160405280836001600160481b03168152602001426001600160401b03168152602001826001600160a01b0316815250613ad8612956565b6000858152602091825260409081902083518154858501516001600160481b039092166001600160881b031990911617600160481b6001600160401b039092169190910217815592810151600190930180546001600160a01b0319166001600160a01b039094169390931790925590518481527f7f63c9251d82a933210c2b6d0b0f116252c3c116788120e64e8e8215df6f316291016118d1565b7f00000000000000000000000000000000000000000000000000000000000000006001600160401b0316600003613ba8575050565b610a6d82613bb861093684611a25565b613bc06138c5565b61ffff8516600090815260209190915260409020614d6e565b7f00000000000000000000000000000000000000000000000000000000000000006001600160401b0316600003613c0d5750565b610a1681613c1c61093661119d565b613c24612c1b565b614dc7565b6000613c33612743565b5460ff1690506000613c43612771565b60408051808201909152905460ff808216835261010090910481166020830181905291925083161115613c9c5760208101516040516313c3d1b160e01b815260ff808516600483015290911660248201526044016110a6565b805160ff1615610a6d578160ff16600003610a6d5760405163831761d760e01b815260040160405180910390fd5b613cd26122a0565b6001600160a01b038116613cfc57604051631e4fbdf760e01b8152600060048201526024016110a6565b610a1681614e1b565b60008061121460017f3031d39df71efbb605646fc51d7571499445af538fa6dd17ce8c07e8118ed979615dc9565b613d3b612771565b5460ff16613d47613d05565b5414610d7257613d55613d05565b54613d5e612771565b5460405163d2a13a2960e01b8152600481019290925260ff1660248201526044016110a6565b6000818310613d9357816109fe565b5090919050565b60006001600160401b03821115613a475760405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203660448201526534206269747360d01b60648201526084016110a6565b60008160ff168360ff1603613e185750826109fe565b8160ff168360ff161115613e4c57613e3082846161d5565b613e3b90600a6162d2565b613e4590856162e1565b90506109fe565b613e5683836161d5565b613e6190600a6162d2565b613e459085616303565b613e7481614e8c565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b613eb3614f3a565b7f00000000000000000000000000000000000000000000000000000000000000006001600160401b0316306001600160a01b03166374aa7bfc6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613f1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f3f919061631a565b6001600160401b031614610d7257610d72616038565b80613f5e612861565b805460ff191691151591909117905550565b60008160ff168360ff1610613d9357816109fe565b80546001600160481b0316613f9981615126565b15613fc757815468ffffffffffffffffff60481b1916600160481b6001600160481b03851602178255614047565b6040805160608101825283546001600160481b038082168352600160481b8204166020830152600160901b90046001600160401b031691810191909152600090614010906120fb565b905061401d848383615146565b83546001600160481b0391909116600160481b0268ffffffffffffffffff60481b19909116178355505b5080546001600160481b039290921679ffffffffffffffff000000000000000000ffffffffffffffffff1990921691909117600160901b426001600160401b031602179055565b60008061121460017ffd6568c039679b3b7cc93c26c41d9379b7b1bec1677120493b467688302cb120615dc9565b60006140c6612771565b905060006140d2612cfc565b82548154919250610100900460ff169081146140f0576140f0616038565b60005b8181101561413c5761412a83828154811061411057614110615ff3565b6000918252602090912001546001600160a01b0316614bad565b8061413481616009565b9150506140f3565b5060005b818110156141e5576000614155826001616102565b90505b828110156141d25783818154811061417257614172615ff3565b60009182526020909120015484546001600160a01b039091169085908490811061419e5761419e615ff3565b6000918252602090912001546001600160a01b0316036141c0576141c0616038565b806141ca81616009565b915050614158565b50806141dd81616009565b915050614140565b508254604060ff9091161115610a4157610a41616038565b80614206613897565b5550565b6142126151e8565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461428c57604051636345072160e11b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660048201523360248201526044016110a6565b614296333361521f565b61429e615239565b610d72610a686142ac610c9e565b615249565b6040516001600160a01b038316602482015260448101829052610a4190849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915261526d565b6000610c7161408e565b604080516001600160a01b0383811660248084019190915283518084039091018152604490920183526020820180516001600160e01b03166370a0823160e01b17905291516000928392908616916143769190615d7a565b600060405180830381855afa9150503d80600081146143b1576040519150601f19603f3d011682016040523d82523d6000602084013e6143b6565b606091505b509150508080602001905181019061095c91906160e9565b6040516001600160a01b038085166024830152831660448201526064810182905261183f9085906323b872dd60e01b906084016142dd565b60008061441161288f565b61ffff84166000908152602091909152604081206001015460ff16915081900361444e5760405163ade64f0b60e01b815260040160405180910390fd5b600080614459610c9e565b90506144668682856123e0565b9150600061447d6001600160481b03841683612209565b90508087146144b357866144918282615dc9565b6040516338f831a560e11b8152600481019290925260248201526044016110a6565b509095945050505050565b6000806144cc600884613f70565b905061095c6144f46144ef6001600160401b03600888901c1660ff881685613e02565b613d9a565b8260ff1660089190911b68ffffffffffffffff00161790565b600061451761279f565b546001600160401b0316905061452b61279f565b80546001600160401b031690600061454283616337565b91906101000a8154816001600160401b0302191690836001600160401b031602179055505090565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160401b03166000036145a4576000611214565b6112146145b261093661119d565b83614d62565b6040518060e00160405280858152602001848152602001876001600160481b03168152602001426001600160401b031681526020018661ffff168152602001836001600160a01b0316815260200182815250614612612463565b6001600160401b03808a1660009081526020928352604090819020845181559284015160018401558301516002830180546060860151608087015161ffff16600160881b0261ffff60881b1991909516600160481b026001600160881b03199092166001600160481b0390941693909317179190911691909117905560a08201516003820180546001600160a01b039092166001600160a01b031990921691909117905560c082015160048201906146ca90826163ab565b50506040516001600160401b03891681527f69add1952a6a6b9cb86f04d05f0cb605cbb469a50ae916139d34495a9991481f915060200160405180910390a150505050505050565b604051600090339083908381818185875af1925050503d8060008114614754576040519150601f19603f3d011682016040523d82523d6000602084013e614759565b606091505b5050905080610a6d57604051630b288dc560e21b8152600481018390526024016110a6565b7f00000000000000000000000000000000000000000000000000000000000000006001600160401b03166000036147b25750565b610a16816147c161093661119d565b6147c9612c1b565b614d6e565b7f00000000000000000000000000000000000000000000000000000000000000006001600160401b0316600003614803575050565b610a6d8261481361093684611a25565b61481b6138c5565b61ffff8516600090815260209190915260409020614dc7565b6060806060600080614844612cfc565b80548060200260200160405190810160405280929190818152602001828054801561489857602002820191906000526020600020905b81546001600160a01b0316815260019091019060200180831161487a575b50505050509050606060006148ab613d05565b54835190915060008190036148d3576040516334e7b19560e11b815260040160405180910390fd5b604051635b10743960e11b815273__$93083e246e55d56d98f3df2872cd16bfd0$__9063b620e8729061490c908c908690600401615e03565b600060405180830381865af4158015614929573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526149519190810190615e6a565b925050506000806149638a8486612d29565b9150915080341015614991576040516306a91e3760e51b8152600481018290523460248201526044016110a6565b600061499d8234615dc9565b905080156149ae576149ae81614712565b509299919850965090945092505050565b815160006149cb613483565b9050866149f157604051630ebc95af60e21b815261ffff8a1660048201526024016110a6565b8760005b83811015614aff576000868281518110614a1157614a11615ff3565b60200260200101519050806001600160a01b0316634b5b05058a8481518110614a3c57614a3c615ff3565b60200260200101518e8b886000876001600160a01b03166001600160a01b0316815260200190815260200160002060000160029054906101000a900460ff1660ff1681518110614a8e57614a8e615ff3565b60200260200101518a8f896040518763ffffffff1660e01b8152600401614ab995949392919061646a565b6000604051808303818588803b158015614ad257600080fd5b505af1158015614ae6573d6000803e3d6000fd5b5050505050508080614af790616009565b9150506149f5565b5050505050505050505050565b614b4082614b18613483565b6001600160a01b0384166000908152602091909152604090205462010000900460ff1661533f565b7f35a2101eaac94b493e0dfca061f9a7f087913fde8678e7cde0aca9897edba0e58282614b6b613483565b6001600160a01b038581166000908152602092835260409081902054815195865293909116918401919091526201000090910460ff1690820152606001610c5b565b6000614bb7613483565b90506000614bc361408e565b90506000614bcf612771565b90506000614bdb612cfc565b6001600160a01b038616600090815260208681526040918290208251606081018452905460ff8082161515808452610100830482161515948401949094526201000090910416928101929092529192509080614c4957508060200151158015614c495750604081015160ff16155b614c5557614c55616038565b604081015184546020830151600160ff9093169290921b166001600160401b03161515906000805b8654610100900460ff16811015614cdf57896001600160a01b0316868281548110614caa57614caa615ff3565b6000918252602090912001546001600160a01b031603614ccd5760019150614cdf565b80614cd781616009565b915050614c7d565b5081151583151514614cf357614cf3616038565b81151581151514614d0657614d06616038565b8554604085015160ff918216911610611ca857611ca8616038565b6000614d2c82610c80565b15614d3957506001919050565b6001614d436127cd565b600093845260205260408320805460ff19169115159190911790555090565b60006109fe838361538d565b805467ffffffffffffffff60901b1916600160901b426001600160401b031602178155614d9b82846153b2565b81546001600160481b0391909116600160481b0268ffffffffffffffffff60481b199091161790555050565b80546001600160401b034216600160901b0267ffffffffffffffff60901b19821681178355614d9b916001600160481b039182169082161790614e0c908516866153f7565b6001600160481b03169061545a565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b6001600160a01b0381163b614ef95760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b60648201526084016110a6565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316306001600160a01b031663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015614fa2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614fc691906164ae565b6001600160a01b031614614fdc57614fdc616038565b7f0000000000000000000000000000000000000000000000000000000000000000600181111561500e5761500e61590e565b306001600160a01b031663295a52126040518163ffffffff1660e01b8152600401602060405180830381865afa15801561504c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061507091906164cb565b60018111156150815761508161590e565b1461508e5761508e616038565b7f000000000000000000000000000000000000000000000000000000000000000061ffff16306001600160a01b0316639a8a05926040518163ffffffff1660e01b8152600401602060405180830381865afa1580156150f1573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061511591906164ec565b61ffff1614610d7257610d72616038565b60006001600160401b03600883901c1615801561121457505060ff161590565b6000806151538486615486565b1561518c5761516284866153b2565b905061516e8382615486565b61517b5760ff8316615185565b61518583826153b2565b91506151a5565b61519685856153b2565b90506151a283826154ab565b91505b6151af8286615486565b156151e057604051631e74e8fb60e31b81526001600160481b038084166004830152861660248201526044016110a6565b509392505050565b60008051602061652a83398151915254600160401b900460ff16610d7257604051631afcd79f60e31b815260040160405180910390fd5b6152276151e8565b615230826154f0565b610a6d81615501565b6152416151e8565b610d72615512565b600080615257600884613f70565b905068ffffffffffffffff0060ff8216176109fe565b60006152c2826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661551a9092919063ffffffff16565b805190915015610a4157808060200190518101906152e0919061604e565b610a415760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016110a6565b600160ff82161b61534e6127cd565b60009384526020526040909220805468ffffffffffffffff00198116610100918290046001600160401b03908116951694909417029290921790915550565b60006153998383615529565b506001600160401b03600891821c81169290911c161090565b60006153be8383615529565b6109fe6153de6001600160401b03600885811c8216919087901c1661608a565b68ffffffffffffffff0060089190911b1660ff85161790565b60006154038383615529565b600883811c6001600160401b039081169184901c81168281019291831161542a5782615433565b6001600160401b035b925061196361544184613d9a565b68ffffffffffffffff0060089190911b1660ff88161790565b60006154668383615529565b600882811c6001600160401b039081169185901c1610613d9357816109fe565b60006154928383615529565b506001600160401b03600891821c81169290911c161190565b60006154b78383615529565b6109fe6154d76001600160401b03600885811c8216919087901c16615ddc565b68ffffffffffffffff0060089190911b1660ff84161790565b6154f86151e8565b610a168161555e565b6155096151e8565b610a16816155a3565b612ba66151e8565b606061095c84846000856155ab565b60ff82811690821680821461183f57604051635ce6db6160e11b815260ff8084166004830152821660248201526044016110a6565b6155666151e8565b6000615570613897565b6001815590506000615580612272565b80546001600160a01b0319166001600160a01b0394909416939093179092555050565b613cd26151e8565b60608247101561560c5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016110a6565b600080866001600160a01b031685876040516156289190615d7a565b60006040518083038185875af1925050503d8060008114615665576040519150601f19603f3d011682016040523d82523d6000602084013e61566a565b606091505b509150915061567b87838387615686565b979650505050505050565b606083156156f55782516000036156ee576001600160a01b0385163b6156ee5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016110a6565b508161095c565b61095c838381511561570a5781518083602001fd5b8060405162461bcd60e51b81526004016110a69190615d10565b50805461573090615d40565b6000825580601f10615740575050565b601f016020900490600052602060002090810190610a1691905b80821115613a47576000815560010161575a565b61ffff81168114610a1657600080fd5b60006020828403121561579057600080fd5b81356109fe8161576e565b6001600160a01b0381168114610a1657600080fd5b6000602082840312156157c257600080fd5b81356109fe8161579b565b6000602082840312156157df57600080fd5b5035919050565b600080604083850312156157f957600080fd5b82359150602083013561580b8161576e565b809150509250929050565b6001600160401b0381168114610a1657600080fd5b60006020828403121561583d57600080fd5b81356109fe81615816565b60005b8381101561586357818101518382015260200161584b565b50506000910152565b60008151808452615884816020860160208601615848565b601f01601f19169290920160200192915050565b6020815281516020820152602082015160408201526001600160481b0360408301511660608201526001600160401b03606083015116608082015261ffff60808301511660a082015260018060a01b0360a08301511660c0820152600060c083015160e08084015261095c61010084018261586c565b634e487b7160e01b600052602160045260246000fd5b602081016002831061594657634e487b7160e01b600052602160045260246000fd5b91905290565b60ff81168114610a1657600080fd5b6000806000806080858703121561597157600080fd5b843561597c8161576e565b93506020850135925060408501356159938161594c565b9396929550929360600135925050565b600080604083850312156159b657600080fd5b82359150602083013561580b8161594c565b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b0381118282101715615a0057615a006159c8565b60405290565b604051601f8201601f191681016001600160401b0381118282101715615a2e57615a2e6159c8565b604052919050565b60006001600160401b03821115615a4f57615a4f6159c8565b50601f01601f191660200190565b600082601f830112615a6e57600080fd5b8135615a81615a7c82615a36565b615a06565b818152846020838601011115615a9657600080fd5b816020850160208301376000918101602001919091529392505050565b60008060408385031215615ac657600080fd5b8235615ad18161576e565b915060208301356001600160401b03811115615aec57600080fd5b615af885828601615a5d565b9150509250929050565b604080825283519082018190526000906020906060840190828701845b82811015615b3b57815184529284019290840190600101615b1f565b50505092019290925292915050565b600080600060608486031215615b5f57600080fd5b833592506020840135615b718161576e565b929592945050506040919091013590565b600080600060608486031215615b9757600080fd5b8335615ba28161576e565b92506020840135915060408401356001600160401b0380821115615bc557600080fd5b9085019060608288031215615bd957600080fd5b604051606081018181108382111715615bf457615bf46159c8565b80604052508235815260208301356020820152604083013582811115615c1957600080fd5b615c2589828601615a5d565b6040830152508093505050509250925092565b8015158114610a1657600080fd5b60008060008060008060c08789031215615c5f57600080fd5b863595506020870135615c718161576e565b945060408701359350606087013592506080870135615c8f81615c38565b915060a08701356001600160401b03811115615caa57600080fd5b615cb689828a01615a5d565b9150509295509295509295565b6020808252825182820181905260009190848201906040850190845b81811015615d045783516001600160a01b031683529284019291840191600101615cdf565b50909695505050505050565b6020815260006109fe602083018461586c565b600060208284031215615d3557600080fd5b81356109fe8161594c565b600181811c90821680615d5457607f821691505b602082108103615d7457634e487b7160e01b600052602260045260246000fd5b50919050565b60008251615d8c818460208701615848565b9190910192915050565b600060208284031215615da857600080fd5b81516109fe8161594c565b634e487b7160e01b600052601160045260246000fd5b8181038181111561121457611214615db3565b6001600160401b03818116838216019080821115615dfc57615dfc615db3565b5092915050565b604081526000615e16604083018561586c565b90508260208301529392505050565b600082601f830112615e3657600080fd5b8151615e44615a7c82615a36565b818152846020838601011115615e5957600080fd5b61095c826020830160208701615848565b60006020808385031215615e7d57600080fd5b82516001600160401b0380821115615e9457600080fd5b818501915085601f830112615ea857600080fd5b815181811115615eba57615eba6159c8565b8060051b615ec9858201615a06565b9182528381018501918581019089841115615ee357600080fd5b86860192505b83831015615f6657825185811115615f015760008081fd5b86016040818c03601f1901811315615f195760008081fd5b615f216159de565b89830151615f2e8161594c565b8152908201519087821115615f435760008081fd5b615f518d8b84860101615e25565b818b0152845250509186019190860190615ee9565b9998505050505050505050565b600060808284031215615f8557600080fd5b604051608081018181106001600160401b0382111715615fa757615fa76159c8565b60405282516001600160481b0381168114615fc157600080fd5b8082525060208301516020820152604083015160408201526060830151615fe78161576e565b60608201529392505050565b634e487b7160e01b600052603260045260246000fd5b60006001820161601b5761601b615db3565b5060010190565b634e487b7160e01b600052601260045260246000fd5b634e487b7160e01b600052600160045260246000fd5b60006020828403121561606057600080fd5b81516109fe81615c38565b600060ff821660ff810361608157616081615db3565b60010192915050565b6001600160401b03828116828216039080821115615dfc57615dfc615db3565b60ff8151168252600060208201516040602085015261095c604085018261586c565b61ffff8316815260406020820152600061095c60408301846160aa565b6000602082840312156160fb57600080fd5b5051919050565b8082018082111561121457611214615db3565b60006020828403121561612757600080fd5b81516001600160401b0381111561613d57600080fd5b61095c84828501615e25565b8051825260208101516020830152600060408201516060604085015261095c606085018261586c565b6020815260006109fe6020830184616149565b61ffff8316815260406020820152600061095c6040830184616149565b600060ff8216806161b5576161b5615db3565b6000190192915050565b634e487b7160e01b600052603160045260246000fd5b60ff828116828216039081111561121457611214615db3565b600181815b8085111561622957816000190482111561620f5761620f615db3565b8085161561621c57918102915b93841c93908002906161f3565b509250929050565b60008261624057506001611214565b8161624d57506000611214565b8160018114616263576002811461626d57616289565b6001915050611214565b60ff84111561627e5761627e615db3565b50506001821b611214565b5060208310610133831016604e8410600b84101617156162ac575081810a611214565b6162b683836161ee565b80600019048211156162ca576162ca615db3565b029392505050565b60006109fe60ff841683616231565b6000826162fe57634e487b7160e01b600052601260045260246000fd5b500490565b808202811582820484141761121457611214615db3565b60006020828403121561632c57600080fd5b81516109fe81615816565b60006001600160401b0380831681810361635357616353615db3565b6001019392505050565b601f821115610a4157600081815260208120601f850160051c810160208610156163845750805b601f850160051c820191505b818110156163a357828155600101616390565b505050505050565b81516001600160401b038111156163c4576163c46159c8565b6163d8816163d28454615d40565b8461635d565b602080601f83116001811461640d57600084156163f55750858301515b600019600386901b1c1916600185901b1785556163a3565b600085815260208120601f198616915b8281101561643c5788860151825594840194600190910190840161641d565b508582101561645a5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b61ffff8616815260a06020820152600061648760a08301876160aa565b8281036040840152616499818761586c565b60608401959095525050608001529392505050565b6000602082840312156164c057600080fd5b81516109fe8161579b565b6000602082840312156164dd57600080fd5b8151600281106109fe57600080fd5b6000602082840312156164fe57600080fd5b81516109fe8161576e56fe9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a26469706673582212202816781291593866e4755341ea70627066ba55fdf891a5240ced7b47be62ab0864736f6c63430008130033'; + +type NttManagerConstructorParams = + | [linkLibraryAddresses: NttManagerLibraryAddresses, signer?: Signer] + | ConstructorParameters; + +const isSuperArgs = ( + xs: NttManagerConstructorParams, +): xs is ConstructorParameters => { + return ( + typeof xs[0] === 'string' || + (Array.isArray as (arg: any) => arg is readonly any[])(xs[0]) || + '_isInterface' in xs[0] + ); +}; + +export class NttManager__factory extends ContractFactory { + constructor(...args: NttManagerConstructorParams) { + if (isSuperArgs(args)) { + super(...args); + } else { + const [linkLibraryAddresses, signer] = args; + super( + _abi, + NttManager__factory.linkBytecode(linkLibraryAddresses), + signer, + ); + } + } + + static linkBytecode( + linkLibraryAddresses: NttManagerLibraryAddresses, + ): string { + let linkedBytecode = _bytecode; + + linkedBytecode = linkedBytecode.replace( + new RegExp('__\\$93083e246e55d56d98f3df2872cd16bfd0\\$__', 'g'), + linkLibraryAddresses[ + 'src/libraries/TransceiverStructs.sol:TransceiverStructs' + ] + .replace(/^0x/, '') + .toLowerCase(), + ); + + return linkedBytecode; + } + + override deploy( + _token: string, + _mode: BigNumberish, + _chainId: BigNumberish, + _rateLimitDuration: BigNumberish, + _skipRateLimiting: boolean, + overrides?: Overrides & { from?: string }, + ): Promise { + return super.deploy( + _token, + _mode, + _chainId, + _rateLimitDuration, + _skipRateLimiting, + overrides || {}, + ) as Promise; + } + override getDeployTransaction( + _token: string, + _mode: BigNumberish, + _chainId: BigNumberish, + _rateLimitDuration: BigNumberish, + _skipRateLimiting: boolean, + overrides?: Overrides & { from?: string }, + ): TransactionRequest { + return super.getDeployTransaction( + _token, + _mode, + _chainId, + _rateLimitDuration, + _skipRateLimiting, + overrides || {}, + ); + } + override attach(address: string): NttManager { + return super.attach(address) as NttManager; + } + override connect(signer: Signer): NttManager__factory { + return super.connect(signer) as NttManager__factory; + } + + static readonly bytecode = _bytecode; + static readonly abi = _abi; + static createInterface(): NttManagerInterface { + return new utils.Interface(_abi) as NttManagerInterface; + } + static connect( + address: string, + signerOrProvider: Signer | Provider, + ): NttManager { + return new Contract(address, _abi, signerOrProvider) as NttManager; + } +} + +export interface NttManagerLibraryAddresses { + ['src/libraries/TransceiverStructs.sol:TransceiverStructs']: string; +} diff --git a/wormhole-connect/src/routes/ntt/chains/evm/abis/0.1.0/WormholeTransceiver.ts b/wormhole-connect/src/routes/ntt/chains/evm/abis/0.1.0/WormholeTransceiver.ts new file mode 100644 index 000000000..631d0be01 --- /dev/null +++ b/wormhole-connect/src/routes/ntt/chains/evm/abis/0.1.0/WormholeTransceiver.ts @@ -0,0 +1,1477 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ +import type { + BaseContract, + BigNumber, + BigNumberish, + BytesLike, + CallOverrides, + ContractTransaction, + Overrides, + PayableOverrides, + PopulatedTransaction, + Signer, + utils, +} from 'ethers'; +import type { + FunctionFragment, + Result, + EventFragment, +} from '@ethersproject/abi'; +import type { Listener, Provider } from '@ethersproject/providers'; +import type { + TypedEventFilter, + TypedEvent, + TypedListener, + OnEvent, +} from '../common'; + +export declare namespace TransceiverStructs { + export type TransceiverMessageStruct = { + sourceNttManagerAddress: BytesLike; + recipientNttManagerAddress: BytesLike; + nttManagerPayload: BytesLike; + transceiverPayload: BytesLike; + }; + + export type TransceiverMessageStructOutput = [ + string, + string, + string, + string, + ] & { + sourceNttManagerAddress: string; + recipientNttManagerAddress: string; + nttManagerPayload: string; + transceiverPayload: string; + }; + + export type TransceiverInstructionStruct = { + index: BigNumberish; + payload: BytesLike; + }; + + export type TransceiverInstructionStructOutput = [number, string] & { + index: number; + payload: string; + }; +} + +export declare namespace IWormholeTransceiver { + export type WormholeTransceiverInstructionStruct = { + shouldSkipRelayerSend: boolean; + }; + + export type WormholeTransceiverInstructionStructOutput = [boolean] & { + shouldSkipRelayerSend: boolean; + }; +} + +export interface WormholeTransceiverInterface extends utils.Interface { + functions: { + 'WORMHOLE_TRANSCEIVER_VERSION()': FunctionFragment; + 'consistencyLevel()': FunctionFragment; + 'encodeWormholeTransceiverInstruction((bool))': FunctionFragment; + 'gasLimit()': FunctionFragment; + 'getMigratesImmutables()': FunctionFragment; + 'getNttManagerOwner()': FunctionFragment; + 'getNttManagerToken()': FunctionFragment; + 'getWormholePeer(uint16)': FunctionFragment; + 'initialize()': FunctionFragment; + 'isPaused()': FunctionFragment; + 'isSpecialRelayingEnabled(uint16)': FunctionFragment; + 'isVAAConsumed(bytes32)': FunctionFragment; + 'isWormholeEvmChain(uint16)': FunctionFragment; + 'isWormholeRelayingEnabled(uint16)': FunctionFragment; + 'migrate()': FunctionFragment; + 'nttManager()': FunctionFragment; + 'nttManagerToken()': FunctionFragment; + 'owner()': FunctionFragment; + 'parseWormholeTransceiverInstruction(bytes)': FunctionFragment; + 'pauser()': FunctionFragment; + 'quoteDeliveryPrice(uint16,(uint8,bytes))': FunctionFragment; + 'receiveMessage(bytes)': FunctionFragment; + 'receiveWormholeMessages(bytes,bytes[],bytes32,uint16,bytes32)': FunctionFragment; + 'sendMessage(uint16,(uint8,bytes),bytes,bytes32,bytes32)': FunctionFragment; + 'setIsSpecialRelayingEnabled(uint16,bool)': FunctionFragment; + 'setIsWormholeEvmChain(uint16,bool)': FunctionFragment; + 'setIsWormholeRelayingEnabled(uint16,bool)': FunctionFragment; + 'setWormholePeer(uint16,bytes32)': FunctionFragment; + 'specialRelayer()': FunctionFragment; + 'transferOwnership(address)': FunctionFragment; + 'transferPauserCapability(address)': FunctionFragment; + 'transferTransceiverOwnership(address)': FunctionFragment; + 'upgrade(address)': FunctionFragment; + 'wormhole()': FunctionFragment; + 'wormholeRelayer()': FunctionFragment; + }; + + getFunction( + nameOrSignatureOrTopic: + | 'WORMHOLE_TRANSCEIVER_VERSION' + | 'consistencyLevel' + | 'encodeWormholeTransceiverInstruction' + | 'gasLimit' + | 'getMigratesImmutables' + | 'getNttManagerOwner' + | 'getNttManagerToken' + | 'getWormholePeer' + | 'initialize' + | 'isPaused' + | 'isSpecialRelayingEnabled' + | 'isVAAConsumed' + | 'isWormholeEvmChain' + | 'isWormholeRelayingEnabled' + | 'migrate' + | 'nttManager' + | 'nttManagerToken' + | 'owner' + | 'parseWormholeTransceiverInstruction' + | 'pauser' + | 'quoteDeliveryPrice' + | 'receiveMessage' + | 'receiveWormholeMessages' + | 'sendMessage' + | 'setIsSpecialRelayingEnabled' + | 'setIsWormholeEvmChain' + | 'setIsWormholeRelayingEnabled' + | 'setWormholePeer' + | 'specialRelayer' + | 'transferOwnership' + | 'transferPauserCapability' + | 'transferTransceiverOwnership' + | 'upgrade' + | 'wormhole' + | 'wormholeRelayer', + ): FunctionFragment; + + encodeFunctionData( + functionFragment: 'WORMHOLE_TRANSCEIVER_VERSION', + values?: undefined, + ): string; + encodeFunctionData( + functionFragment: 'consistencyLevel', + values?: undefined, + ): string; + encodeFunctionData( + functionFragment: 'encodeWormholeTransceiverInstruction', + values: [IWormholeTransceiver.WormholeTransceiverInstructionStruct], + ): string; + encodeFunctionData(functionFragment: 'gasLimit', values?: undefined): string; + encodeFunctionData( + functionFragment: 'getMigratesImmutables', + values?: undefined, + ): string; + encodeFunctionData( + functionFragment: 'getNttManagerOwner', + values?: undefined, + ): string; + encodeFunctionData( + functionFragment: 'getNttManagerToken', + values?: undefined, + ): string; + encodeFunctionData( + functionFragment: 'getWormholePeer', + values: [BigNumberish], + ): string; + encodeFunctionData( + functionFragment: 'initialize', + values?: undefined, + ): string; + encodeFunctionData(functionFragment: 'isPaused', values?: undefined): string; + encodeFunctionData( + functionFragment: 'isSpecialRelayingEnabled', + values: [BigNumberish], + ): string; + encodeFunctionData( + functionFragment: 'isVAAConsumed', + values: [BytesLike], + ): string; + encodeFunctionData( + functionFragment: 'isWormholeEvmChain', + values: [BigNumberish], + ): string; + encodeFunctionData( + functionFragment: 'isWormholeRelayingEnabled', + values: [BigNumberish], + ): string; + encodeFunctionData(functionFragment: 'migrate', values?: undefined): string; + encodeFunctionData( + functionFragment: 'nttManager', + values?: undefined, + ): string; + encodeFunctionData( + functionFragment: 'nttManagerToken', + values?: undefined, + ): string; + encodeFunctionData(functionFragment: 'owner', values?: undefined): string; + encodeFunctionData( + functionFragment: 'parseWormholeTransceiverInstruction', + values: [BytesLike], + ): string; + encodeFunctionData(functionFragment: 'pauser', values?: undefined): string; + encodeFunctionData( + functionFragment: 'quoteDeliveryPrice', + values: [BigNumberish, TransceiverStructs.TransceiverInstructionStruct], + ): string; + encodeFunctionData( + functionFragment: 'receiveMessage', + values: [BytesLike], + ): string; + encodeFunctionData( + functionFragment: 'receiveWormholeMessages', + values: [BytesLike, BytesLike[], BytesLike, BigNumberish, BytesLike], + ): string; + encodeFunctionData( + functionFragment: 'sendMessage', + values: [ + BigNumberish, + TransceiverStructs.TransceiverInstructionStruct, + BytesLike, + BytesLike, + BytesLike, + ], + ): string; + encodeFunctionData( + functionFragment: 'setIsSpecialRelayingEnabled', + values: [BigNumberish, boolean], + ): string; + encodeFunctionData( + functionFragment: 'setIsWormholeEvmChain', + values: [BigNumberish, boolean], + ): string; + encodeFunctionData( + functionFragment: 'setIsWormholeRelayingEnabled', + values: [BigNumberish, boolean], + ): string; + encodeFunctionData( + functionFragment: 'setWormholePeer', + values: [BigNumberish, BytesLike], + ): string; + encodeFunctionData( + functionFragment: 'specialRelayer', + values?: undefined, + ): string; + encodeFunctionData( + functionFragment: 'transferOwnership', + values: [string], + ): string; + encodeFunctionData( + functionFragment: 'transferPauserCapability', + values: [string], + ): string; + encodeFunctionData( + functionFragment: 'transferTransceiverOwnership', + values: [string], + ): string; + encodeFunctionData(functionFragment: 'upgrade', values: [string]): string; + encodeFunctionData(functionFragment: 'wormhole', values?: undefined): string; + encodeFunctionData( + functionFragment: 'wormholeRelayer', + values?: undefined, + ): string; + + decodeFunctionResult( + functionFragment: 'WORMHOLE_TRANSCEIVER_VERSION', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'consistencyLevel', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'encodeWormholeTransceiverInstruction', + data: BytesLike, + ): Result; + decodeFunctionResult(functionFragment: 'gasLimit', data: BytesLike): Result; + decodeFunctionResult( + functionFragment: 'getMigratesImmutables', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'getNttManagerOwner', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'getNttManagerToken', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'getWormholePeer', + data: BytesLike, + ): Result; + decodeFunctionResult(functionFragment: 'initialize', data: BytesLike): Result; + decodeFunctionResult(functionFragment: 'isPaused', data: BytesLike): Result; + decodeFunctionResult( + functionFragment: 'isSpecialRelayingEnabled', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'isVAAConsumed', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'isWormholeEvmChain', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'isWormholeRelayingEnabled', + data: BytesLike, + ): Result; + decodeFunctionResult(functionFragment: 'migrate', data: BytesLike): Result; + decodeFunctionResult(functionFragment: 'nttManager', data: BytesLike): Result; + decodeFunctionResult( + functionFragment: 'nttManagerToken', + data: BytesLike, + ): Result; + decodeFunctionResult(functionFragment: 'owner', data: BytesLike): Result; + decodeFunctionResult( + functionFragment: 'parseWormholeTransceiverInstruction', + data: BytesLike, + ): Result; + decodeFunctionResult(functionFragment: 'pauser', data: BytesLike): Result; + decodeFunctionResult( + functionFragment: 'quoteDeliveryPrice', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'receiveMessage', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'receiveWormholeMessages', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'sendMessage', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'setIsSpecialRelayingEnabled', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'setIsWormholeEvmChain', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'setIsWormholeRelayingEnabled', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'setWormholePeer', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'specialRelayer', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'transferOwnership', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'transferPauserCapability', + data: BytesLike, + ): Result; + decodeFunctionResult( + functionFragment: 'transferTransceiverOwnership', + data: BytesLike, + ): Result; + decodeFunctionResult(functionFragment: 'upgrade', data: BytesLike): Result; + decodeFunctionResult(functionFragment: 'wormhole', data: BytesLike): Result; + decodeFunctionResult( + functionFragment: 'wormholeRelayer', + data: BytesLike, + ): Result; + + events: { + 'AdminChanged(address,address)': EventFragment; + 'BeaconUpgraded(address)': EventFragment; + 'Initialized(uint64)': EventFragment; + 'NotPaused(bool)': EventFragment; + 'OwnershipTransferred(address,address)': EventFragment; + 'Paused(bool)': EventFragment; + 'PauserTransferred(address,address)': EventFragment; + 'ReceivedMessage(bytes32,uint16,bytes32,uint64)': EventFragment; + 'ReceivedRelayedMessage(bytes32,uint16,bytes32)': EventFragment; + 'RelayingInfo(uint8,bytes32,uint256)': EventFragment; + 'SendTransceiverMessage(uint16,(bytes32,bytes32,bytes,bytes))': EventFragment; + 'SetIsSpecialRelayingEnabled(uint16,bool)': EventFragment; + 'SetIsWormholeEvmChain(uint16,bool)': EventFragment; + 'SetIsWormholeRelayingEnabled(uint16,bool)': EventFragment; + 'SetWormholePeer(uint16,bytes32)': EventFragment; + 'Upgraded(address)': EventFragment; + }; + + getEvent(nameOrSignatureOrTopic: 'AdminChanged'): EventFragment; + getEvent(nameOrSignatureOrTopic: 'BeaconUpgraded'): EventFragment; + getEvent(nameOrSignatureOrTopic: 'Initialized'): EventFragment; + getEvent(nameOrSignatureOrTopic: 'NotPaused'): EventFragment; + getEvent(nameOrSignatureOrTopic: 'OwnershipTransferred'): EventFragment; + getEvent(nameOrSignatureOrTopic: 'Paused'): EventFragment; + getEvent(nameOrSignatureOrTopic: 'PauserTransferred'): EventFragment; + getEvent(nameOrSignatureOrTopic: 'ReceivedMessage'): EventFragment; + getEvent(nameOrSignatureOrTopic: 'ReceivedRelayedMessage'): EventFragment; + getEvent(nameOrSignatureOrTopic: 'RelayingInfo'): EventFragment; + getEvent(nameOrSignatureOrTopic: 'SendTransceiverMessage'): EventFragment; + getEvent( + nameOrSignatureOrTopic: 'SetIsSpecialRelayingEnabled', + ): EventFragment; + getEvent(nameOrSignatureOrTopic: 'SetIsWormholeEvmChain'): EventFragment; + getEvent( + nameOrSignatureOrTopic: 'SetIsWormholeRelayingEnabled', + ): EventFragment; + getEvent(nameOrSignatureOrTopic: 'SetWormholePeer'): EventFragment; + getEvent(nameOrSignatureOrTopic: 'Upgraded'): EventFragment; +} + +export interface AdminChangedEventObject { + previousAdmin: string; + newAdmin: string; +} +export type AdminChangedEvent = TypedEvent< + [string, string], + AdminChangedEventObject +>; + +export type AdminChangedEventFilter = TypedEventFilter; + +export interface BeaconUpgradedEventObject { + beacon: string; +} +export type BeaconUpgradedEvent = TypedEvent< + [string], + BeaconUpgradedEventObject +>; + +export type BeaconUpgradedEventFilter = TypedEventFilter; + +export interface InitializedEventObject { + version: BigNumber; +} +export type InitializedEvent = TypedEvent<[BigNumber], InitializedEventObject>; + +export type InitializedEventFilter = TypedEventFilter; + +export interface NotPausedEventObject { + notPaused: boolean; +} +export type NotPausedEvent = TypedEvent<[boolean], NotPausedEventObject>; + +export type NotPausedEventFilter = TypedEventFilter; + +export interface OwnershipTransferredEventObject { + previousOwner: string; + newOwner: string; +} +export type OwnershipTransferredEvent = TypedEvent< + [string, string], + OwnershipTransferredEventObject +>; + +export type OwnershipTransferredEventFilter = + TypedEventFilter; + +export interface PausedEventObject { + paused: boolean; +} +export type PausedEvent = TypedEvent<[boolean], PausedEventObject>; + +export type PausedEventFilter = TypedEventFilter; + +export interface PauserTransferredEventObject { + oldPauser: string; + newPauser: string; +} +export type PauserTransferredEvent = TypedEvent< + [string, string], + PauserTransferredEventObject +>; + +export type PauserTransferredEventFilter = + TypedEventFilter; + +export interface ReceivedMessageEventObject { + digest: string; + emitterChainId: number; + emitterAddress: string; + sequence: BigNumber; +} +export type ReceivedMessageEvent = TypedEvent< + [string, number, string, BigNumber], + ReceivedMessageEventObject +>; + +export type ReceivedMessageEventFilter = TypedEventFilter; + +export interface ReceivedRelayedMessageEventObject { + digest: string; + emitterChainId: number; + emitterAddress: string; +} +export type ReceivedRelayedMessageEvent = TypedEvent< + [string, number, string], + ReceivedRelayedMessageEventObject +>; + +export type ReceivedRelayedMessageEventFilter = + TypedEventFilter; + +export interface RelayingInfoEventObject { + relayingType: number; + refundAddress: string; + deliveryPayment: BigNumber; +} +export type RelayingInfoEvent = TypedEvent< + [number, string, BigNumber], + RelayingInfoEventObject +>; + +export type RelayingInfoEventFilter = TypedEventFilter; + +export interface SendTransceiverMessageEventObject { + recipientChain: number; + message: TransceiverStructs.TransceiverMessageStructOutput; +} +export type SendTransceiverMessageEvent = TypedEvent< + [number, TransceiverStructs.TransceiverMessageStructOutput], + SendTransceiverMessageEventObject +>; + +export type SendTransceiverMessageEventFilter = + TypedEventFilter; + +export interface SetIsSpecialRelayingEnabledEventObject { + chainId: number; + isRelayingEnabled: boolean; +} +export type SetIsSpecialRelayingEnabledEvent = TypedEvent< + [number, boolean], + SetIsSpecialRelayingEnabledEventObject +>; + +export type SetIsSpecialRelayingEnabledEventFilter = + TypedEventFilter; + +export interface SetIsWormholeEvmChainEventObject { + chainId: number; + isEvm: boolean; +} +export type SetIsWormholeEvmChainEvent = TypedEvent< + [number, boolean], + SetIsWormholeEvmChainEventObject +>; + +export type SetIsWormholeEvmChainEventFilter = + TypedEventFilter; + +export interface SetIsWormholeRelayingEnabledEventObject { + chainId: number; + isRelayingEnabled: boolean; +} +export type SetIsWormholeRelayingEnabledEvent = TypedEvent< + [number, boolean], + SetIsWormholeRelayingEnabledEventObject +>; + +export type SetIsWormholeRelayingEnabledEventFilter = + TypedEventFilter; + +export interface SetWormholePeerEventObject { + chainId: number; + peerContract: string; +} +export type SetWormholePeerEvent = TypedEvent< + [number, string], + SetWormholePeerEventObject +>; + +export type SetWormholePeerEventFilter = TypedEventFilter; + +export interface UpgradedEventObject { + implementation: string; +} +export type UpgradedEvent = TypedEvent<[string], UpgradedEventObject>; + +export type UpgradedEventFilter = TypedEventFilter; + +export interface WormholeTransceiver extends BaseContract { + connect(signerOrProvider: Signer | Provider | string): this; + attach(addressOrName: string): this; + deployed(): Promise; + + interface: WormholeTransceiverInterface; + + queryFilter( + event: TypedEventFilter, + fromBlockOrBlockhash?: string | number | undefined, + toBlock?: string | number | undefined, + ): Promise>; + + listeners( + eventFilter?: TypedEventFilter, + ): Array>; + listeners(eventName?: string): Array; + removeAllListeners( + eventFilter: TypedEventFilter, + ): this; + removeAllListeners(eventName?: string): this; + off: OnEvent; + on: OnEvent; + once: OnEvent; + removeListener: OnEvent; + + functions: { + WORMHOLE_TRANSCEIVER_VERSION(overrides?: CallOverrides): Promise<[string]>; + + consistencyLevel(overrides?: CallOverrides): Promise<[number]>; + + encodeWormholeTransceiverInstruction( + instruction: IWormholeTransceiver.WormholeTransceiverInstructionStruct, + overrides?: CallOverrides, + ): Promise<[string]>; + + gasLimit(overrides?: CallOverrides): Promise<[BigNumber]>; + + getMigratesImmutables(overrides?: CallOverrides): Promise<[boolean]>; + + getNttManagerOwner(overrides?: CallOverrides): Promise<[string]>; + + getNttManagerToken(overrides?: CallOverrides): Promise<[string]>; + + getWormholePeer( + chainId: BigNumberish, + overrides?: CallOverrides, + ): Promise<[string]>; + + initialize( + overrides?: Overrides & { from?: string }, + ): Promise; + + isPaused(overrides?: CallOverrides): Promise<[boolean]>; + + isSpecialRelayingEnabled( + chainId: BigNumberish, + overrides?: CallOverrides, + ): Promise<[boolean]>; + + isVAAConsumed( + hash: BytesLike, + overrides?: CallOverrides, + ): Promise<[boolean]>; + + isWormholeEvmChain( + chainId: BigNumberish, + overrides?: CallOverrides, + ): Promise<[boolean]>; + + isWormholeRelayingEnabled( + chainId: BigNumberish, + overrides?: CallOverrides, + ): Promise<[boolean]>; + + migrate( + overrides?: Overrides & { from?: string }, + ): Promise; + + nttManager(overrides?: CallOverrides): Promise<[string]>; + + nttManagerToken(overrides?: CallOverrides): Promise<[string]>; + + owner(overrides?: CallOverrides): Promise<[string]>; + + parseWormholeTransceiverInstruction( + encoded: BytesLike, + overrides?: CallOverrides, + ): Promise< + [IWormholeTransceiver.WormholeTransceiverInstructionStructOutput] & { + instruction: IWormholeTransceiver.WormholeTransceiverInstructionStructOutput; + } + >; + + pauser(overrides?: CallOverrides): Promise<[string]>; + + quoteDeliveryPrice( + targetChain: BigNumberish, + instruction: TransceiverStructs.TransceiverInstructionStruct, + overrides?: CallOverrides, + ): Promise<[BigNumber]>; + + receiveMessage( + encodedMessage: BytesLike, + overrides?: Overrides & { from?: string }, + ): Promise; + + receiveWormholeMessages( + payload: BytesLike, + additionalMessages: BytesLike[], + sourceAddress: BytesLike, + sourceChain: BigNumberish, + deliveryHash: BytesLike, + overrides?: PayableOverrides & { from?: string }, + ): Promise; + + sendMessage( + recipientChain: BigNumberish, + instruction: TransceiverStructs.TransceiverInstructionStruct, + nttManagerMessage: BytesLike, + recipientNttManagerAddress: BytesLike, + refundAddress: BytesLike, + overrides?: PayableOverrides & { from?: string }, + ): Promise; + + setIsSpecialRelayingEnabled( + chainId: BigNumberish, + isEnabled: boolean, + overrides?: Overrides & { from?: string }, + ): Promise; + + setIsWormholeEvmChain( + chainId: BigNumberish, + isEvm: boolean, + overrides?: Overrides & { from?: string }, + ): Promise; + + setIsWormholeRelayingEnabled( + chainId: BigNumberish, + isEnabled: boolean, + overrides?: Overrides & { from?: string }, + ): Promise; + + setWormholePeer( + peerChainId: BigNumberish, + peerContract: BytesLike, + overrides?: Overrides & { from?: string }, + ): Promise; + + specialRelayer(overrides?: CallOverrides): Promise<[string]>; + + transferOwnership( + newOwner: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + transferPauserCapability( + newPauser: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + transferTransceiverOwnership( + newOwner: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + upgrade( + newImplementation: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + wormhole(overrides?: CallOverrides): Promise<[string]>; + + wormholeRelayer(overrides?: CallOverrides): Promise<[string]>; + }; + + WORMHOLE_TRANSCEIVER_VERSION(overrides?: CallOverrides): Promise; + + consistencyLevel(overrides?: CallOverrides): Promise; + + encodeWormholeTransceiverInstruction( + instruction: IWormholeTransceiver.WormholeTransceiverInstructionStruct, + overrides?: CallOverrides, + ): Promise; + + gasLimit(overrides?: CallOverrides): Promise; + + getMigratesImmutables(overrides?: CallOverrides): Promise; + + getNttManagerOwner(overrides?: CallOverrides): Promise; + + getNttManagerToken(overrides?: CallOverrides): Promise; + + getWormholePeer( + chainId: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + initialize( + overrides?: Overrides & { from?: string }, + ): Promise; + + isPaused(overrides?: CallOverrides): Promise; + + isSpecialRelayingEnabled( + chainId: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + isVAAConsumed(hash: BytesLike, overrides?: CallOverrides): Promise; + + isWormholeEvmChain( + chainId: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + isWormholeRelayingEnabled( + chainId: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + migrate( + overrides?: Overrides & { from?: string }, + ): Promise; + + nttManager(overrides?: CallOverrides): Promise; + + nttManagerToken(overrides?: CallOverrides): Promise; + + owner(overrides?: CallOverrides): Promise; + + parseWormholeTransceiverInstruction( + encoded: BytesLike, + overrides?: CallOverrides, + ): Promise; + + pauser(overrides?: CallOverrides): Promise; + + quoteDeliveryPrice( + targetChain: BigNumberish, + instruction: TransceiverStructs.TransceiverInstructionStruct, + overrides?: CallOverrides, + ): Promise; + + receiveMessage( + encodedMessage: BytesLike, + overrides?: Overrides & { from?: string }, + ): Promise; + + receiveWormholeMessages( + payload: BytesLike, + additionalMessages: BytesLike[], + sourceAddress: BytesLike, + sourceChain: BigNumberish, + deliveryHash: BytesLike, + overrides?: PayableOverrides & { from?: string }, + ): Promise; + + sendMessage( + recipientChain: BigNumberish, + instruction: TransceiverStructs.TransceiverInstructionStruct, + nttManagerMessage: BytesLike, + recipientNttManagerAddress: BytesLike, + refundAddress: BytesLike, + overrides?: PayableOverrides & { from?: string }, + ): Promise; + + setIsSpecialRelayingEnabled( + chainId: BigNumberish, + isEnabled: boolean, + overrides?: Overrides & { from?: string }, + ): Promise; + + setIsWormholeEvmChain( + chainId: BigNumberish, + isEvm: boolean, + overrides?: Overrides & { from?: string }, + ): Promise; + + setIsWormholeRelayingEnabled( + chainId: BigNumberish, + isEnabled: boolean, + overrides?: Overrides & { from?: string }, + ): Promise; + + setWormholePeer( + peerChainId: BigNumberish, + peerContract: BytesLike, + overrides?: Overrides & { from?: string }, + ): Promise; + + specialRelayer(overrides?: CallOverrides): Promise; + + transferOwnership( + newOwner: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + transferPauserCapability( + newPauser: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + transferTransceiverOwnership( + newOwner: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + upgrade( + newImplementation: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + wormhole(overrides?: CallOverrides): Promise; + + wormholeRelayer(overrides?: CallOverrides): Promise; + + callStatic: { + WORMHOLE_TRANSCEIVER_VERSION(overrides?: CallOverrides): Promise; + + consistencyLevel(overrides?: CallOverrides): Promise; + + encodeWormholeTransceiverInstruction( + instruction: IWormholeTransceiver.WormholeTransceiverInstructionStruct, + overrides?: CallOverrides, + ): Promise; + + gasLimit(overrides?: CallOverrides): Promise; + + getMigratesImmutables(overrides?: CallOverrides): Promise; + + getNttManagerOwner(overrides?: CallOverrides): Promise; + + getNttManagerToken(overrides?: CallOverrides): Promise; + + getWormholePeer( + chainId: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + initialize(overrides?: CallOverrides): Promise; + + isPaused(overrides?: CallOverrides): Promise; + + isSpecialRelayingEnabled( + chainId: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + isVAAConsumed(hash: BytesLike, overrides?: CallOverrides): Promise; + + isWormholeEvmChain( + chainId: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + isWormholeRelayingEnabled( + chainId: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + migrate(overrides?: CallOverrides): Promise; + + nttManager(overrides?: CallOverrides): Promise; + + nttManagerToken(overrides?: CallOverrides): Promise; + + owner(overrides?: CallOverrides): Promise; + + parseWormholeTransceiverInstruction( + encoded: BytesLike, + overrides?: CallOverrides, + ): Promise; + + pauser(overrides?: CallOverrides): Promise; + + quoteDeliveryPrice( + targetChain: BigNumberish, + instruction: TransceiverStructs.TransceiverInstructionStruct, + overrides?: CallOverrides, + ): Promise; + + receiveMessage( + encodedMessage: BytesLike, + overrides?: CallOverrides, + ): Promise; + + receiveWormholeMessages( + payload: BytesLike, + additionalMessages: BytesLike[], + sourceAddress: BytesLike, + sourceChain: BigNumberish, + deliveryHash: BytesLike, + overrides?: CallOverrides, + ): Promise; + + sendMessage( + recipientChain: BigNumberish, + instruction: TransceiverStructs.TransceiverInstructionStruct, + nttManagerMessage: BytesLike, + recipientNttManagerAddress: BytesLike, + refundAddress: BytesLike, + overrides?: CallOverrides, + ): Promise; + + setIsSpecialRelayingEnabled( + chainId: BigNumberish, + isEnabled: boolean, + overrides?: CallOverrides, + ): Promise; + + setIsWormholeEvmChain( + chainId: BigNumberish, + isEvm: boolean, + overrides?: CallOverrides, + ): Promise; + + setIsWormholeRelayingEnabled( + chainId: BigNumberish, + isEnabled: boolean, + overrides?: CallOverrides, + ): Promise; + + setWormholePeer( + peerChainId: BigNumberish, + peerContract: BytesLike, + overrides?: CallOverrides, + ): Promise; + + specialRelayer(overrides?: CallOverrides): Promise; + + transferOwnership( + newOwner: string, + overrides?: CallOverrides, + ): Promise; + + transferPauserCapability( + newPauser: string, + overrides?: CallOverrides, + ): Promise; + + transferTransceiverOwnership( + newOwner: string, + overrides?: CallOverrides, + ): Promise; + + upgrade( + newImplementation: string, + overrides?: CallOverrides, + ): Promise; + + wormhole(overrides?: CallOverrides): Promise; + + wormholeRelayer(overrides?: CallOverrides): Promise; + }; + + filters: { + 'AdminChanged(address,address)'( + previousAdmin?: null, + newAdmin?: null, + ): AdminChangedEventFilter; + AdminChanged( + previousAdmin?: null, + newAdmin?: null, + ): AdminChangedEventFilter; + + 'BeaconUpgraded(address)'( + beacon?: string | null, + ): BeaconUpgradedEventFilter; + BeaconUpgraded(beacon?: string | null): BeaconUpgradedEventFilter; + + 'Initialized(uint64)'(version?: null): InitializedEventFilter; + Initialized(version?: null): InitializedEventFilter; + + 'NotPaused(bool)'(notPaused?: null): NotPausedEventFilter; + NotPaused(notPaused?: null): NotPausedEventFilter; + + 'OwnershipTransferred(address,address)'( + previousOwner?: string | null, + newOwner?: string | null, + ): OwnershipTransferredEventFilter; + OwnershipTransferred( + previousOwner?: string | null, + newOwner?: string | null, + ): OwnershipTransferredEventFilter; + + 'Paused(bool)'(paused?: null): PausedEventFilter; + Paused(paused?: null): PausedEventFilter; + + 'PauserTransferred(address,address)'( + oldPauser?: string | null, + newPauser?: string | null, + ): PauserTransferredEventFilter; + PauserTransferred( + oldPauser?: string | null, + newPauser?: string | null, + ): PauserTransferredEventFilter; + + 'ReceivedMessage(bytes32,uint16,bytes32,uint64)'( + digest?: null, + emitterChainId?: null, + emitterAddress?: null, + sequence?: null, + ): ReceivedMessageEventFilter; + ReceivedMessage( + digest?: null, + emitterChainId?: null, + emitterAddress?: null, + sequence?: null, + ): ReceivedMessageEventFilter; + + 'ReceivedRelayedMessage(bytes32,uint16,bytes32)'( + digest?: null, + emitterChainId?: null, + emitterAddress?: null, + ): ReceivedRelayedMessageEventFilter; + ReceivedRelayedMessage( + digest?: null, + emitterChainId?: null, + emitterAddress?: null, + ): ReceivedRelayedMessageEventFilter; + + 'RelayingInfo(uint8,bytes32,uint256)'( + relayingType?: null, + refundAddress?: null, + deliveryPayment?: null, + ): RelayingInfoEventFilter; + RelayingInfo( + relayingType?: null, + refundAddress?: null, + deliveryPayment?: null, + ): RelayingInfoEventFilter; + + 'SendTransceiverMessage(uint16,(bytes32,bytes32,bytes,bytes))'( + recipientChain?: null, + message?: null, + ): SendTransceiverMessageEventFilter; + SendTransceiverMessage( + recipientChain?: null, + message?: null, + ): SendTransceiverMessageEventFilter; + + 'SetIsSpecialRelayingEnabled(uint16,bool)'( + chainId?: null, + isRelayingEnabled?: null, + ): SetIsSpecialRelayingEnabledEventFilter; + SetIsSpecialRelayingEnabled( + chainId?: null, + isRelayingEnabled?: null, + ): SetIsSpecialRelayingEnabledEventFilter; + + 'SetIsWormholeEvmChain(uint16,bool)'( + chainId?: null, + isEvm?: null, + ): SetIsWormholeEvmChainEventFilter; + SetIsWormholeEvmChain( + chainId?: null, + isEvm?: null, + ): SetIsWormholeEvmChainEventFilter; + + 'SetIsWormholeRelayingEnabled(uint16,bool)'( + chainId?: null, + isRelayingEnabled?: null, + ): SetIsWormholeRelayingEnabledEventFilter; + SetIsWormholeRelayingEnabled( + chainId?: null, + isRelayingEnabled?: null, + ): SetIsWormholeRelayingEnabledEventFilter; + + 'SetWormholePeer(uint16,bytes32)'( + chainId?: null, + peerContract?: null, + ): SetWormholePeerEventFilter; + SetWormholePeer( + chainId?: null, + peerContract?: null, + ): SetWormholePeerEventFilter; + + 'Upgraded(address)'(implementation?: string | null): UpgradedEventFilter; + Upgraded(implementation?: string | null): UpgradedEventFilter; + }; + + estimateGas: { + WORMHOLE_TRANSCEIVER_VERSION(overrides?: CallOverrides): Promise; + + consistencyLevel(overrides?: CallOverrides): Promise; + + encodeWormholeTransceiverInstruction( + instruction: IWormholeTransceiver.WormholeTransceiverInstructionStruct, + overrides?: CallOverrides, + ): Promise; + + gasLimit(overrides?: CallOverrides): Promise; + + getMigratesImmutables(overrides?: CallOverrides): Promise; + + getNttManagerOwner(overrides?: CallOverrides): Promise; + + getNttManagerToken(overrides?: CallOverrides): Promise; + + getWormholePeer( + chainId: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + initialize(overrides?: Overrides & { from?: string }): Promise; + + isPaused(overrides?: CallOverrides): Promise; + + isSpecialRelayingEnabled( + chainId: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + isVAAConsumed( + hash: BytesLike, + overrides?: CallOverrides, + ): Promise; + + isWormholeEvmChain( + chainId: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + isWormholeRelayingEnabled( + chainId: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + migrate(overrides?: Overrides & { from?: string }): Promise; + + nttManager(overrides?: CallOverrides): Promise; + + nttManagerToken(overrides?: CallOverrides): Promise; + + owner(overrides?: CallOverrides): Promise; + + parseWormholeTransceiverInstruction( + encoded: BytesLike, + overrides?: CallOverrides, + ): Promise; + + pauser(overrides?: CallOverrides): Promise; + + quoteDeliveryPrice( + targetChain: BigNumberish, + instruction: TransceiverStructs.TransceiverInstructionStruct, + overrides?: CallOverrides, + ): Promise; + + receiveMessage( + encodedMessage: BytesLike, + overrides?: Overrides & { from?: string }, + ): Promise; + + receiveWormholeMessages( + payload: BytesLike, + additionalMessages: BytesLike[], + sourceAddress: BytesLike, + sourceChain: BigNumberish, + deliveryHash: BytesLike, + overrides?: PayableOverrides & { from?: string }, + ): Promise; + + sendMessage( + recipientChain: BigNumberish, + instruction: TransceiverStructs.TransceiverInstructionStruct, + nttManagerMessage: BytesLike, + recipientNttManagerAddress: BytesLike, + refundAddress: BytesLike, + overrides?: PayableOverrides & { from?: string }, + ): Promise; + + setIsSpecialRelayingEnabled( + chainId: BigNumberish, + isEnabled: boolean, + overrides?: Overrides & { from?: string }, + ): Promise; + + setIsWormholeEvmChain( + chainId: BigNumberish, + isEvm: boolean, + overrides?: Overrides & { from?: string }, + ): Promise; + + setIsWormholeRelayingEnabled( + chainId: BigNumberish, + isEnabled: boolean, + overrides?: Overrides & { from?: string }, + ): Promise; + + setWormholePeer( + peerChainId: BigNumberish, + peerContract: BytesLike, + overrides?: Overrides & { from?: string }, + ): Promise; + + specialRelayer(overrides?: CallOverrides): Promise; + + transferOwnership( + newOwner: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + transferPauserCapability( + newPauser: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + transferTransceiverOwnership( + newOwner: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + upgrade( + newImplementation: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + wormhole(overrides?: CallOverrides): Promise; + + wormholeRelayer(overrides?: CallOverrides): Promise; + }; + + populateTransaction: { + WORMHOLE_TRANSCEIVER_VERSION( + overrides?: CallOverrides, + ): Promise; + + consistencyLevel(overrides?: CallOverrides): Promise; + + encodeWormholeTransceiverInstruction( + instruction: IWormholeTransceiver.WormholeTransceiverInstructionStruct, + overrides?: CallOverrides, + ): Promise; + + gasLimit(overrides?: CallOverrides): Promise; + + getMigratesImmutables( + overrides?: CallOverrides, + ): Promise; + + getNttManagerOwner( + overrides?: CallOverrides, + ): Promise; + + getNttManagerToken( + overrides?: CallOverrides, + ): Promise; + + getWormholePeer( + chainId: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + initialize( + overrides?: Overrides & { from?: string }, + ): Promise; + + isPaused(overrides?: CallOverrides): Promise; + + isSpecialRelayingEnabled( + chainId: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + isVAAConsumed( + hash: BytesLike, + overrides?: CallOverrides, + ): Promise; + + isWormholeEvmChain( + chainId: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + isWormholeRelayingEnabled( + chainId: BigNumberish, + overrides?: CallOverrides, + ): Promise; + + migrate( + overrides?: Overrides & { from?: string }, + ): Promise; + + nttManager(overrides?: CallOverrides): Promise; + + nttManagerToken(overrides?: CallOverrides): Promise; + + owner(overrides?: CallOverrides): Promise; + + parseWormholeTransceiverInstruction( + encoded: BytesLike, + overrides?: CallOverrides, + ): Promise; + + pauser(overrides?: CallOverrides): Promise; + + quoteDeliveryPrice( + targetChain: BigNumberish, + instruction: TransceiverStructs.TransceiverInstructionStruct, + overrides?: CallOverrides, + ): Promise; + + receiveMessage( + encodedMessage: BytesLike, + overrides?: Overrides & { from?: string }, + ): Promise; + + receiveWormholeMessages( + payload: BytesLike, + additionalMessages: BytesLike[], + sourceAddress: BytesLike, + sourceChain: BigNumberish, + deliveryHash: BytesLike, + overrides?: PayableOverrides & { from?: string }, + ): Promise; + + sendMessage( + recipientChain: BigNumberish, + instruction: TransceiverStructs.TransceiverInstructionStruct, + nttManagerMessage: BytesLike, + recipientNttManagerAddress: BytesLike, + refundAddress: BytesLike, + overrides?: PayableOverrides & { from?: string }, + ): Promise; + + setIsSpecialRelayingEnabled( + chainId: BigNumberish, + isEnabled: boolean, + overrides?: Overrides & { from?: string }, + ): Promise; + + setIsWormholeEvmChain( + chainId: BigNumberish, + isEvm: boolean, + overrides?: Overrides & { from?: string }, + ): Promise; + + setIsWormholeRelayingEnabled( + chainId: BigNumberish, + isEnabled: boolean, + overrides?: Overrides & { from?: string }, + ): Promise; + + setWormholePeer( + peerChainId: BigNumberish, + peerContract: BytesLike, + overrides?: Overrides & { from?: string }, + ): Promise; + + specialRelayer(overrides?: CallOverrides): Promise; + + transferOwnership( + newOwner: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + transferPauserCapability( + newPauser: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + transferTransceiverOwnership( + newOwner: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + upgrade( + newImplementation: string, + overrides?: Overrides & { from?: string }, + ): Promise; + + wormhole(overrides?: CallOverrides): Promise; + + wormholeRelayer(overrides?: CallOverrides): Promise; + }; +} diff --git a/wormhole-connect/src/routes/ntt/chains/evm/abis/0.1.0/WormholeTransceiver__factory.ts b/wormhole-connect/src/routes/ntt/chains/evm/abis/0.1.0/WormholeTransceiver__factory.ts new file mode 100644 index 000000000..9fe97c4ea --- /dev/null +++ b/wormhole-connect/src/routes/ntt/chains/evm/abis/0.1.0/WormholeTransceiver__factory.ts @@ -0,0 +1,1375 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ +import { + Signer, + utils, + Contract, + ContractFactory, + BigNumberish, + Overrides, +} from 'ethers'; +import type { Provider, TransactionRequest } from '@ethersproject/providers'; +import type { + WormholeTransceiver, + WormholeTransceiverInterface, +} from './WormholeTransceiver'; + +const _abi = [ + { + inputs: [ + { + internalType: 'address', + name: 'nttManager', + type: 'address', + }, + { + internalType: 'address', + name: 'wormholeCoreBridge', + type: 'address', + }, + { + internalType: 'address', + name: 'wormholeRelayerAddr', + type: 'address', + }, + { + internalType: 'address', + name: 'specialRelayerAddr', + type: 'address', + }, + { + internalType: 'uint8', + name: '_consistencyLevel', + type: 'uint8', + }, + { + internalType: 'uint256', + name: '_gasLimit', + type: 'uint256', + }, + ], + stateMutability: 'nonpayable', + type: 'constructor', + }, + { + inputs: [ + { + internalType: 'address', + name: 'caller', + type: 'address', + }, + ], + name: 'CallerNotNttManager', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'caller', + type: 'address', + }, + ], + name: 'CallerNotRelayer', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'currentOwner', + type: 'address', + }, + ], + name: 'CannotRenounceTransceiverOwnership', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'currentOwner', + type: 'address', + }, + { + internalType: 'address', + name: 'newOwner', + type: 'address', + }, + ], + name: 'CannotTransferTransceiverOwnership', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint8', + name: 'val', + type: 'uint8', + }, + ], + name: 'InvalidBoolVal', + type: 'error', + }, + { + inputs: [ + { + internalType: 'BooleanFlag', + name: 'value', + type: 'uint256', + }, + ], + name: 'InvalidBoolValue', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'evmChainId', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'blockChainId', + type: 'uint256', + }, + ], + name: 'InvalidFork', + type: 'error', + }, + { + inputs: [], + name: 'InvalidInitialization', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'account', + type: 'address', + }, + ], + name: 'InvalidPauser', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint16', + name: 'chainId', + type: 'uint16', + }, + ], + name: 'InvalidRelayingConfig', + type: 'error', + }, + { + inputs: [ + { + internalType: 'string', + name: 'reason', + type: 'string', + }, + ], + name: 'InvalidVaa', + type: 'error', + }, + { + inputs: [], + name: 'InvalidWormholeChainIdZero', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint16', + name: 'chainId', + type: 'uint16', + }, + { + internalType: 'bytes32', + name: 'peerAddress', + type: 'bytes32', + }, + ], + name: 'InvalidWormholePeer', + type: 'error', + }, + { + inputs: [], + name: 'InvalidWormholePeerZeroAddress', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint256', + name: 'encodedLength', + type: 'uint256', + }, + { + internalType: 'uint256', + name: 'expectedLength', + type: 'uint256', + }, + ], + name: 'LengthMismatch', + type: 'error', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: '', + type: 'bytes32', + }, + ], + name: 'NotAnEvmAddress', + type: 'error', + }, + { + inputs: [], + name: 'NotInitializing', + type: 'error', + }, + { + inputs: [], + name: 'NotMigrating', + type: 'error', + }, + { + inputs: [], + name: 'OnlyDelegateCall', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'owner', + type: 'address', + }, + ], + name: 'OwnableInvalidOwner', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'account', + type: 'address', + }, + ], + name: 'OwnableUnauthorizedAccount', + type: 'error', + }, + { + inputs: [ + { + internalType: 'uint16', + name: 'chainId', + type: 'uint16', + }, + { + internalType: 'bytes32', + name: 'peerAddress', + type: 'bytes32', + }, + ], + name: 'PeerAlreadySet', + type: 'error', + }, + { + inputs: [], + name: 'ReentrancyGuardReentrantCall', + type: 'error', + }, + { + inputs: [], + name: 'RequireContractIsNotPaused', + type: 'error', + }, + { + inputs: [], + name: 'RequireContractIsPaused', + type: 'error', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: 'vaaHash', + type: 'bytes32', + }, + ], + name: 'TransferAlreadyCompleted', + type: 'error', + }, + { + inputs: [], + name: 'UnexpectedAdditionalMessages', + type: 'error', + }, + { + inputs: [ + { + internalType: 'address', + name: 'deployer', + type: 'address', + }, + { + internalType: 'address', + name: 'caller', + type: 'address', + }, + ], + name: 'UnexpectedDeployer', + type: 'error', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: 'recipientNttManagerAddress', + type: 'bytes32', + }, + { + internalType: 'bytes32', + name: 'expectedRecipientNttManagerAddress', + type: 'bytes32', + }, + ], + name: 'UnexpectedRecipientNttManagerAddress', + type: 'error', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'previousAdmin', + type: 'address', + }, + { + indexed: false, + internalType: 'address', + name: 'newAdmin', + type: 'address', + }, + ], + name: 'AdminChanged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'beacon', + type: 'address', + }, + ], + name: 'BeaconUpgraded', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint64', + name: 'version', + type: 'uint64', + }, + ], + name: 'Initialized', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'bool', + name: 'notPaused', + type: 'bool', + }, + ], + name: 'NotPaused', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'previousOwner', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'newOwner', + type: 'address', + }, + ], + name: 'OwnershipTransferred', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'bool', + name: 'paused', + type: 'bool', + }, + ], + name: 'Paused', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'oldPauser', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'newPauser', + type: 'address', + }, + ], + name: 'PauserTransferred', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'bytes32', + name: 'digest', + type: 'bytes32', + }, + { + indexed: false, + internalType: 'uint16', + name: 'emitterChainId', + type: 'uint16', + }, + { + indexed: false, + internalType: 'bytes32', + name: 'emitterAddress', + type: 'bytes32', + }, + { + indexed: false, + internalType: 'uint64', + name: 'sequence', + type: 'uint64', + }, + ], + name: 'ReceivedMessage', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'bytes32', + name: 'digest', + type: 'bytes32', + }, + { + indexed: false, + internalType: 'uint16', + name: 'emitterChainId', + type: 'uint16', + }, + { + indexed: false, + internalType: 'bytes32', + name: 'emitterAddress', + type: 'bytes32', + }, + ], + name: 'ReceivedRelayedMessage', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint8', + name: 'relayingType', + type: 'uint8', + }, + { + indexed: false, + internalType: 'bytes32', + name: 'refundAddress', + type: 'bytes32', + }, + { + indexed: false, + internalType: 'uint256', + name: 'deliveryPayment', + type: 'uint256', + }, + ], + name: 'RelayingInfo', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint16', + name: 'recipientChain', + type: 'uint16', + }, + { + components: [ + { + internalType: 'bytes32', + name: 'sourceNttManagerAddress', + type: 'bytes32', + }, + { + internalType: 'bytes32', + name: 'recipientNttManagerAddress', + type: 'bytes32', + }, + { + internalType: 'bytes', + name: 'nttManagerPayload', + type: 'bytes', + }, + { + internalType: 'bytes', + name: 'transceiverPayload', + type: 'bytes', + }, + ], + indexed: false, + internalType: 'struct TransceiverStructs.TransceiverMessage', + name: 'message', + type: 'tuple', + }, + ], + name: 'SendTransceiverMessage', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint16', + name: 'chainId', + type: 'uint16', + }, + { + indexed: false, + internalType: 'bool', + name: 'isRelayingEnabled', + type: 'bool', + }, + ], + name: 'SetIsSpecialRelayingEnabled', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint16', + name: 'chainId', + type: 'uint16', + }, + { + indexed: false, + internalType: 'bool', + name: 'isEvm', + type: 'bool', + }, + ], + name: 'SetIsWormholeEvmChain', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint16', + name: 'chainId', + type: 'uint16', + }, + { + indexed: false, + internalType: 'bool', + name: 'isRelayingEnabled', + type: 'bool', + }, + ], + name: 'SetIsWormholeRelayingEnabled', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'uint16', + name: 'chainId', + type: 'uint16', + }, + { + indexed: false, + internalType: 'bytes32', + name: 'peerContract', + type: 'bytes32', + }, + ], + name: 'SetWormholePeer', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'implementation', + type: 'address', + }, + ], + name: 'Upgraded', + type: 'event', + }, + { + inputs: [], + name: 'WORMHOLE_TRANSCEIVER_VERSION', + outputs: [ + { + internalType: 'string', + name: '', + type: 'string', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'consistencyLevel', + outputs: [ + { + internalType: 'uint8', + name: '', + type: 'uint8', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + components: [ + { + internalType: 'bool', + name: 'shouldSkipRelayerSend', + type: 'bool', + }, + ], + internalType: + 'struct IWormholeTransceiver.WormholeTransceiverInstruction', + name: 'instruction', + type: 'tuple', + }, + ], + name: 'encodeWormholeTransceiverInstruction', + outputs: [ + { + internalType: 'bytes', + name: '', + type: 'bytes', + }, + ], + stateMutability: 'pure', + type: 'function', + }, + { + inputs: [], + name: 'gasLimit', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getMigratesImmutables', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getNttManagerOwner', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'getNttManagerToken', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint16', + name: 'chainId', + type: 'uint16', + }, + ], + name: 'getWormholePeer', + outputs: [ + { + internalType: 'bytes32', + name: '', + type: 'bytes32', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'initialize', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'isPaused', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint16', + name: 'chainId', + type: 'uint16', + }, + ], + name: 'isSpecialRelayingEnabled', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes32', + name: 'hash', + type: 'bytes32', + }, + ], + name: 'isVAAConsumed', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint16', + name: 'chainId', + type: 'uint16', + }, + ], + name: 'isWormholeEvmChain', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint16', + name: 'chainId', + type: 'uint16', + }, + ], + name: 'isWormholeRelayingEnabled', + outputs: [ + { + internalType: 'bool', + name: '', + type: 'bool', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'migrate', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'nttManager', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'nttManagerToken', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'owner', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes', + name: 'encoded', + type: 'bytes', + }, + ], + name: 'parseWormholeTransceiverInstruction', + outputs: [ + { + components: [ + { + internalType: 'bool', + name: 'shouldSkipRelayerSend', + type: 'bool', + }, + ], + internalType: + 'struct IWormholeTransceiver.WormholeTransceiverInstruction', + name: 'instruction', + type: 'tuple', + }, + ], + stateMutability: 'pure', + type: 'function', + }, + { + inputs: [], + name: 'pauser', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint16', + name: 'targetChain', + type: 'uint16', + }, + { + components: [ + { + internalType: 'uint8', + name: 'index', + type: 'uint8', + }, + { + internalType: 'bytes', + name: 'payload', + type: 'bytes', + }, + ], + internalType: 'struct TransceiverStructs.TransceiverInstruction', + name: 'instruction', + type: 'tuple', + }, + ], + name: 'quoteDeliveryPrice', + outputs: [ + { + internalType: 'uint256', + name: '', + type: 'uint256', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes', + name: 'encodedMessage', + type: 'bytes', + }, + ], + name: 'receiveMessage', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'bytes', + name: 'payload', + type: 'bytes', + }, + { + internalType: 'bytes[]', + name: 'additionalMessages', + type: 'bytes[]', + }, + { + internalType: 'bytes32', + name: 'sourceAddress', + type: 'bytes32', + }, + { + internalType: 'uint16', + name: 'sourceChain', + type: 'uint16', + }, + { + internalType: 'bytes32', + name: 'deliveryHash', + type: 'bytes32', + }, + ], + name: 'receiveWormholeMessages', + outputs: [], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint16', + name: 'recipientChain', + type: 'uint16', + }, + { + components: [ + { + internalType: 'uint8', + name: 'index', + type: 'uint8', + }, + { + internalType: 'bytes', + name: 'payload', + type: 'bytes', + }, + ], + internalType: 'struct TransceiverStructs.TransceiverInstruction', + name: 'instruction', + type: 'tuple', + }, + { + internalType: 'bytes', + name: 'nttManagerMessage', + type: 'bytes', + }, + { + internalType: 'bytes32', + name: 'recipientNttManagerAddress', + type: 'bytes32', + }, + { + internalType: 'bytes32', + name: 'refundAddress', + type: 'bytes32', + }, + ], + name: 'sendMessage', + outputs: [], + stateMutability: 'payable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint16', + name: 'chainId', + type: 'uint16', + }, + { + internalType: 'bool', + name: 'isEnabled', + type: 'bool', + }, + ], + name: 'setIsSpecialRelayingEnabled', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint16', + name: 'chainId', + type: 'uint16', + }, + { + internalType: 'bool', + name: 'isEvm', + type: 'bool', + }, + ], + name: 'setIsWormholeEvmChain', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint16', + name: 'chainId', + type: 'uint16', + }, + { + internalType: 'bool', + name: 'isEnabled', + type: 'bool', + }, + ], + name: 'setIsWormholeRelayingEnabled', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'uint16', + name: 'peerChainId', + type: 'uint16', + }, + { + internalType: 'bytes32', + name: 'peerContract', + type: 'bytes32', + }, + ], + name: 'setWormholePeer', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'specialRelayer', + outputs: [ + { + internalType: 'contract ISpecialRelayer', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'newOwner', + type: 'address', + }, + ], + name: 'transferOwnership', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'newPauser', + type: 'address', + }, + ], + name: 'transferPauserCapability', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'newOwner', + type: 'address', + }, + ], + name: 'transferTransceiverOwnership', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'newImplementation', + type: 'address', + }, + ], + name: 'upgrade', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'wormhole', + outputs: [ + { + internalType: 'contract IWormhole', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'wormholeRelayer', + outputs: [ + { + internalType: 'contract IWormholeRelayer', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, +] as const; + +const _bytecode = + '0x6101c06040523480156200001257600080fd5b5060405162004018380380620040188339810160408190526200003591620001d3565b858585858585856200004662000102565b306080526001600160a01b03811660a081905260408051637e062a3560e11b8152905163fc0c546a916004808201926020929091908290030181865afa15801562000095573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000bb919062000254565b6001600160a01b0390811660c0523360e052958616610120525092841661014052921661016052466101805260ff909116610100526101a052506200027995505050505050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff1615620001535760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b0390811614620001b35780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b80516001600160a01b0381168114620001ce57600080fd5b919050565b60008060008060008060c08789031215620001ed57600080fd5b620001f887620001b6565b95506200020860208801620001b6565b94506200021860408801620001b6565b93506200022860608801620001b6565b9250608087015160ff811681146200023f57600080fd5b8092505060a087015190509295509295509295565b6000602082840312156200026757600080fd5b6200027282620001b6565b9392505050565b60805160a05160c05160e05161010051610120516101405161016051610180516101a051613c0b6200040d600039600081816106fc015281816117d901526120ea01526000612c0101526000818161054101528181611a13015281816121a801526126b7015260008181610642015281816109690152818161179c0152818161211101526126150152600081816104e301528181610d5d015281816118ce0152818161195401528181611b47015281816120050152818161226b015281816123000152818161236e015281816125730152612ac801526000818161067601528181610df20152818161198701528181611b7b015281816127590152612b5801526000818161285a015261289601526000818161025d0152818161042701528181611a44015281816121cf015281816129c10152612d900152600081816102ae0152818161084201528181610b670152818161121401528181611d1201528181611d4701528181611d9a015281816129080152818161292f015281816129e80152612cee01526000611f100152613c0b6000f3fe6080604052600436106102045760003560e01c806381e8ec7f11610118578063b5634c73116100a0578063e8dfd5081161006f578063e8dfd50814610664578063f2fde38b146106aa578063f48066a8146106ca578063f68016b7146106ea578063f953cec71461071e57600080fd5b8063b5634c73146105db578063bc7f6d37146105fb578063d8d284181461061b578063da25b7251461063057600080fd5b806390ea5428116100e757806390ea54281461052f578063935dec071461056357806396dddc63146105915780639fd0506d146105b1578063b187bd26146105c657600080fd5b806381e8ec7f146104a057806384acd1bb146104d15780638da5cb5b146105055780638fd3ab801461051a57600080fd5b8063529dca321161019b578063689f90c31161016a578063689f90c314610403578063694977d7146104185780637ab564031461044b57806380eb32391461046b5780638129fc1c1461048b57600080fd5b8063529dca321461036457806358f709ba14610377578063657b3b2f1461039757806366152efc146103b757600080fd5b8063320d0d8e116101d7578063320d0d8e146102d057806348b330d6146103005780634b5b0505146103315780634b795b211461034457600080fd5b8063036de8af146102095780630900f0101461022b5780630b4a1e891461024b57806324fb21db1461029c575b600080fd5b34801561021557600080fd5b50610229610224366004612f46565b61073e565b005b34801561023757600080fd5b50610229610246366004612f46565b6107aa565b34801561025757600080fd5b5061027f7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156102a857600080fd5b5061027f7f000000000000000000000000000000000000000000000000000000000000000081565b3480156102dc57600080fd5b506102f06102eb366004612f7e565b6107be565b6040519015158152602001610293565b34801561030c57600080fd5b5061032061031b3660046130a9565b6107eb565b604051905115158152602001610293565b61022961033f36600461316d565b61082f565b34801561035057600080fd5b5061022961035f366004613202565b6108c6565b61022961037236600461325e565b61095e565b34801561038357600080fd5b50610229610392366004612f46565b610b5c565b3480156103a357600080fd5b506102296103b2366004613202565b610bb0565b3480156103c357600080fd5b506103f66103d2366004613353565b516040805191151560f81b6020830152805160018184030181526021909201905290565b60405161029391906133ee565b34801561040f57600080fd5b506102f0610c40565b34801561042457600080fd5b507f000000000000000000000000000000000000000000000000000000000000000061027f565b34801561045757600080fd5b50610229610466366004613401565b610c53565b34801561047757600080fd5b506102f061048636600461342d565b610eb6565b34801561049757600080fd5b50610229610ed4565b3480156104ac57600080fd5b506103f6604051806040016040528060058152602001640302e312e360dc1b81525081565b3480156104dd57600080fd5b5061027f7f000000000000000000000000000000000000000000000000000000000000000081565b34801561051157600080fd5b5061027f610fd5565b34801561052657600080fd5b50610229611003565b34801561053b57600080fd5b5061027f7f000000000000000000000000000000000000000000000000000000000000000081565b34801561056f57600080fd5b5061058361057e366004612f7e565b61110e565b604051908152602001610293565b34801561059d57600080fd5b506102296105ac366004613202565b611132565b3480156105bd57600080fd5b5061027f6111c2565b3480156105d257600080fd5b506102f06111db565b3480156105e757600080fd5b506105836105f6366004613446565b6111f0565b34801561060757600080fd5b506102f0610616366004612f7e565b611203565b34801561062757600080fd5b5061027f611210565b34801561063c57600080fd5b5061027f7f000000000000000000000000000000000000000000000000000000000000000081565b34801561067057600080fd5b506106987f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff9091168152602001610293565b3480156106b657600080fd5b506102296106c5366004612f46565b611299565b3480156106d657600080fd5b506102f06106e5366004612f7e565b6112cb565b3480156106f657600080fd5b506105837f000000000000000000000000000000000000000000000000000000000000000081565b34801561072a57600080fd5b506102296107393660046130a9565b6112d8565b61074e610749610fd5565b6113c8565b6000610758611411565b80546001600160a01b038481166001600160a01b031983168117845560405193945091169182907f51c4874e0f23f262e04a38c51751336dde72126d67f53eb672aaff02996b3ef690600090a3505050565b6107b261143f565b6107bb81611473565b50565b60006107e56107cb611581565b61ffff8416600090815260209190915260409020546115af565b92915050565b604080516020810190915260008152815160000361080c5760008152919050565b600061081883826115ed565b901515835290506108298382611642565b50919050565b610837611674565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146108875760405163c5aa615360e01b81523360048201526024015b60405180910390fd5b610896853433858589896116be565b6108bf60017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0055565b5050505050565b6108ce61143f565b8161ffff166000036108f357604051630f7662c960e21b815260040160405180910390fd5b6108fe811515611c95565b610906611cb2565b61ffff841660008181526020928352604090819020939093558251908152831515918101919091527f0fe301480713b2c2072ee91b3bcfcbf2c0014f0447c89046f020f0f80727003c91015b60405180910390a15050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146109a957604051631c26958960e01b815233600482015260240161087e565b826109b38361110e565b146109de57604051633cd8e72b60e11b815261ffff831660048201526024810184905260440161087e565b6109e781610eb6565b15610a0857604051632d30ec0360e21b81526004810182905260240161087e565b610a1181611ce0565b835115610a315760405163c504ea2960e01b815260040160405180910390fd5b6040805182815261ffff841660208201529081018490527ff557dbbb087662f52c815f6c7ee350628a37a51eae9608ff840d996b65f874759060600160405180910390a1604080516080810182526000808252602082015260609181018290528181019190915260408051606080820183526000808352602083015291810191909152604051630453806b60e11b815273__$93083e246e55d56d98f3df2872cd16bfd0$__906308a700d690610af4906309945ff160e41b908b90600401613495565b600060405180830381865af4158015610b11573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610b39919081019061357e565b81516020830151929450909250610b539186919084611d08565b50505050505050565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610ba75760405163c5aa615360e01b815233600482015260240161087e565b6107bb81611e0b565b610bb861143f565b8161ffff16600003610bdd57604051630f7662c960e21b815260040160405180910390fd5b610be8811515611c95565b610bf0611581565b61ffff841660008181526020928352604090819020939093558251908152831515918101919091527f528b18a533e892b5401d1fb63597275df9d2bb45b13e7695c3147cd07b9746c39101610952565b6000610c4a611e7c565b5460ff16919050565b610c5b61143f565b8161ffff16600003610c8057604051630f7662c960e21b815260040160405180910390fd5b80610c9e5760405163137063ef60e11b815260040160405180910390fd5b6000610ca8611eaa565b61ffff84166000908152602091909152604090205490508015610ceb5760405163b55eeae960e01b815261ffff841660048201526024810182905260440161087e565b81610cf4611eaa565b61ffff858116600081815260209384526040808220959095558451606081018652630c7e33e160e11b8152938401918252838501878152945163077650fb60e51b815284516001600160e01b0319166004820152915190921660248201529251604484015290917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169163b19a437e9173__$93083e246e55d56d98f3df2872cd16bfd0$__9063eeca1f6090606401600060405180830381865af4158015610dc8573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610df0919081019061363d565b7f00000000000000000000000000000000000000000000000000000000000000006040518463ffffffff1660e01b8152600401610e2f93929190613671565b6020604051808303816000875af1158015610e4e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e7291906136ba565b506040805161ffff86168152602081018590527fa559263ee060c7a2560843b3a064ff0376c9753ae3e2449b595a3b615d326466910160405180910390a150505050565b6000610ec0611ed8565b600092835260205250604090205460ff1690565b610edc611f06565b600080516020613bb68339815191528054600160401b810460ff1615906001600160401b0316600081158015610f0f5750825b90506000826001600160401b03166001148015610f2b5750303b155b905081158015610f39575080155b15610f575760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315610f8157845460ff60401b1916600160401b1785555b610f89611f4f565b83156108bf57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15050505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300546001600160a01b031690565b61100b611f06565b600080516020613bb6833981519152546001600160401b031661102f9060016136eb565b600080516020613bb68339815191528054600160401b900460ff1680611062575080546001600160401b03808416911610155b156110805760405163f92ee8a960e01b815260040160405180910390fd5b805468ffffffffffffffffff19166001600160401b03831617600160401b1781556110a9611f5f565b5460ff166110ca57604051632866815360e11b815260040160405180910390fd5b805460ff60401b191681556040516001600160401b03831681527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602001610952565b6000611118611eaa565b61ffff909216600090815260209290925250604090205490565b61113a61143f565b8161ffff1660000361115f57604051630f7662c960e21b815260040160405180910390fd5b61116a811515611c95565b611172611f8d565b61ffff841660008181526020928352604090819020939093558251908152831515918101919091527f4add57d97a7bf5035340ea1212aeeb3d4d3887eb1faf3821a8224c3a6956a10c9101610952565b60006111cc611411565b546001600160a01b0316919050565b6000806111e6611fbb565b5460021492915050565b60006111fc8383611fe9565b9392505050565b60006107e56107cb611cb2565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611270573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611294919061370b565b905090565b6112a161143f565b6001600160a01b038116610ba757604051631e4fbdf760e01b81526000600482015260240161087e565b60006107e56107cb611f8d565b600060606112e583612363565b6040805160808101825260008082526020820152606091810182905281810191909152919350915060408051606080820183526000808352602083015291810191909152604051630453806b60e11b815273__$93083e246e55d56d98f3df2872cd16bfd0$__906308a700d690611369906309945ff160e41b908790600401613495565b600060405180830381865af4158015611386573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526113ae919081019061357e565b815160208301519294509092506108bf9186919084611d08565b336113d16111c2565b6001600160a01b0316141580156113f157506001600160a01b0381163314155b156107bb5760405163e2a08e5d60e01b815233600482015260240161087e565b6000806107e560017fbfa91572ce1e5fe8776a160d3b1f862e83f5ee2c080a7423b4761602a3ad124a613728565b33611448610fd5565b6001600160a01b0316146114715760405163118cdaa760e01b815233600482015260240161087e565b565b61147b611f06565b61148481612529565b600061148e611f5f565b805490915060ff16156114a3576114a361373b565b805460ff191660011781556040805163011fa75760e71b815290513091638fd3ab8091600480830192600092919082900301818387803b1580156114e657600080fd5b505af11580156114fa573d6000803e3d6000fd5b50505050306001600160a01b031663689f90c36040518163ffffffff1660e01b8152600401602060405180830381865afa15801561153c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611560919061375c565b61156c5761156c612569565b61157660006127ed565b805460ff1916905550565b6000806107e560017f16ee6ac6bf7a8d7c37112a9426e00852b215ac4f5c50536beb6c95f1ba47b4b0613728565b6000816000036115c157506000919050565b816001036115d157506001919050565b60405163b998bad560e01b81526004810183905260240161087e565b6000806000806116068686600191810182015192910190565b909250905060fe8216156116325760405163f7a37b0760e01b815260ff8316600482015260240161087e565b60ff909116925090509250929050565b808251146116705781516040516355c5b3e360e11b815260048101919091526024810182905260440161087e565b5050565b7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f008054600119016116b857604051633ee5aeb560e01b815260040160405180910390fd5b60029055565b60408051600080825260208201928390526304616c8f60e21b909252819073__$93083e246e55d56d98f3df2872cd16bfd0$__90631185b23c9061171d906309945ff160e41b906001600160a01b038c16908b90899060248101613779565b600060405180830381865af415801561173a573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261176291908101906137c5565b91509150600061177585602001516107eb565b805190915015801561178b575061178b8a612808565b156118b257858a6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016634b5ca6f48c836117d46117cf8261110e565b612823565b8860007f0000000000000000000000000000000000000000000000000000000000000000886118028b612823565b6040518963ffffffff1660e01b8152600401611824979695949392919061381e565b60206040518083038185885af1158015611842573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061186791906136ba565b506040805160008152602081018a90529081018c90527fc3192e083c87c556db539f071d8a298869f487e951327b5616a6f85ae3da958e906060015b60405180910390a15050611c2a565b80511580156118c557506118c58a611203565b15611b305760007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316631a90a2196040518163ffffffff1660e01b8152600401602060405180830381865afa15801561192a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061194e9190613877565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663b19a437e836000877f00000000000000000000000000000000000000000000000000000000000000006040518563ffffffff1660e01b81526004016119c493929190613671565b60206040518083038185885af11580156119e2573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190611a0791906136ba565b90506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166316ac49c2611a42848e613728565b7f00000000000000000000000000000000000000000000000000000000000000008f6000866040518663ffffffff1660e01b8152600401611ab494939291906001600160a01b0394909416845261ffff92909216602084015260408301526001600160401b0316606082015260800190565b6000604051808303818588803b158015611acd57600080fd5b505af1158015611ae1573d6000803e3d6000fd5b507fc3192e083c87c556db539f071d8a298869f487e951327b5616a6f85ae3da958e935060019250611b11915050565b6040805160ff90921682526000602083015281018d90526060016118a3565b6040516358cd21bf60e11b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063b19a437e908b90611ba39060009087907f000000000000000000000000000000000000000000000000000000000000000090600401613671565b60206040518083038185885af1158015611bc1573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190611be691906136ba565b506040805160028152600060208201529081018a90527fc3192e083c87c556db539f071d8a298869f487e951327b5616a6f85ae3da958e9060600160405180910390a15b7f79376a0dc6cbfe6f6f8f89ad24c262a8c6233f8df181d3fe5abb2e2442e8c7388a84604051611c5b929190613890565b60405180910390a150505050505050505050565b60017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0055565b60008115611ca557506001919050565b506000919050565b919050565b6000806107e560017fd4cbc7742a1e11dcc33209ac33862ef146cfcb6d7f860df12df42105dfedb1ca613728565b6001611cea611ed8565b60009283526020526040909120805460ff1916911515919091179055565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168214611d83576040516339dee99160e11b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031660048201526024810183905260440161087e565b60405163275e091560e21b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690639d78245490611dd3908790879086906004016138f0565b600060405180830381600087803b158015611ded57600080fd5b505af1158015611e01573d6000803e3d6000fd5b5050505050505050565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080546001600160a01b031981166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b6000806107e560017f5443fea4dc453d96b81ce55b62e11a4094cc4cbb8a360956a7253cfdb42506cc613728565b6000806107e560017f48c66ef0992b4bcc14f2551075db9459fc23bf706abb76034f1c66c1ba284685613728565b6000806107e560017f5e6aacb3f16e5b01c8b072a0ab4c38e919266014a45db863ee64a4580fb3e7e3613728565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016300361147157604051633c64f99360e21b815260040160405180910390fd5b611f5761284f565b6114716128e2565b6000806107e560017f7487ca88d037ca20519908b1ee7556206bef53bce0226a348750cb9d4f688e4f613728565b6000806107e560017fe79d2e5e46f62be2ac831294e423b664f30320fb9ebaefd78a978cafe07c0f8d613728565b6000806107e560017f64bacf405c5d7f563d3ba5252584a52c37e4fee380fd825b10666c27b8258023613728565b600080611ff983602001516107eb565b80519091501561208d577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316631a90a2196040518163ffffffff1660e01b8152600401602060405180830381865afa158015612061573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120859190613877565b9150506107e5565b61209684612bd8565b156120ba5760405163251268db60e21b815261ffff8516600482015260240161087e565b6120c384612808565b1561218e5760405163c23ee3c360e01b815261ffff851660048201526000602482018190527f00000000000000000000000000000000000000000000000000000000000000006044830152907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063c23ee3c3906064016040805180830381865afa15801561215f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121839190613930565b5092506107e5915050565b61219784611203565b156122fe5760006001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166382745ce47f00000000000000000000000000000000000000000000000000000000000000006040516001600160e01b031960e084901b1681526001600160a01b03909116600482015261ffff8816602482015260006044820152606401602060405180830381865afa158015612243573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122679190613877565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316631a90a2196040518163ffffffff1660e01b8152600401602060405180830381865afa1580156122c7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122eb9190613877565b6122f59082613954565b925050506107e5565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316631a90a2196040518163ffffffff1660e01b8152600401602060405180830381865afa158015612061573d6000803e3d6000fd5b5092915050565b6000606060008060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c0fd8bde876040518263ffffffff1660e01b81526004016123b891906133ee565b600060405180830381865afa1580156123d5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526123fd9190810190613a41565b925092509250816124235780604051634771719b60e11b815260040161087e91906133ee565b61242c83612bfa565b61245f5760608301516080840151604051633cd8e72b60e11b815261ffff9092166004830152602482015260440161087e565b61246d836101400151610eb6565b1561249457826101400151604051632d30ec0360e21b815260040161087e91815260200190565b6124a2836101400151611ce0565b7ff6fc529540981400dc64edf649eb5e2e0eb5812a27f8c81bac2c1d317e71a5f0836101400151846060015185608001518660a0015160405161250b949392919093845261ffff92909216602084015260408301526001600160401b0316606082015260800190565b60405180910390a182606001518360e0015194509450505050915091565b61253281612c3e565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b612571612cec565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316306001600160a01b03166384acd1bb6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156125d9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125fd919061370b565b6001600160a01b0316146126135761261361373b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316306001600160a01b031663da25b7256040518163ffffffff1660e01b8152600401602060405180830381865afa15801561267b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061269f919061370b565b6001600160a01b0316146126b5576126b561373b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316306001600160a01b03166390ea54286040518163ffffffff1660e01b8152600401602060405180830381865afa15801561271d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612741919061370b565b6001600160a01b0316146127575761275761373b565b7f000000000000000000000000000000000000000000000000000000000000000060ff16306001600160a01b031663e8dfd5086040518163ffffffff1660e01b8152600401602060405180830381865afa1580156127b9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127dd9190613b98565b60ff16146114715761147161373b565b806127f6611e7c565b805460ff191691151591909117905550565b6000612813826107be565b80156107e557506107e5826112cb565b600060a082901c1561284b5760405163033b960d60e41b81526004810183905260240161087e565b5090565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146128c957604051636345072160e11b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016600482015233602482015260440161087e565b6128d1612e30565b611471336128dd611210565b612e40565b6040805160a08101909152639c23bd3b60e01b8152600090602081016001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316634b4fd03b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561298b573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906129af9190613b98565b60ff1681526020016001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316633b97e8566040518163ffffffff1660e01b8152600401602060405180830381865afa158015612a44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a689190613b98565b60ff908116909152604080516301529f6160e21b815283516001600160e01b031916600482015260208401516024820152908301518216604482015260608301516064820152608083015190911660848201529091506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063b19a437e9060009073__$93083e246e55d56d98f3df2872cd16bfd0$__9063054a7d849060a401600060405180830381865af4158015612b2e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052612b56919081019061363d565b7f00000000000000000000000000000000000000000000000000000000000000006040518463ffffffff1660e01b8152600401612b9593929190613671565b6020604051808303816000875af1158015612bb4573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061167091906136ba565b6000612be3826107be565b80156107e55750612bf3826112cb565b1592915050565b6000612c257f0000000000000000000000000000000000000000000000000000000000000000612e5a565b8160800151612c37836060015161110e565b1492915050565b6001600160a01b0381163b612cab5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b606482015260840161087e565b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316306001600160a01b03166324fb21db6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612d54573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d78919061370b565b6001600160a01b031614612d8e57612d8e61373b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316306001600160a01b0316630b4a1e896040518163ffffffff1660e01b8152600401602060405180830381865afa158015612df6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e1a919061370b565b6001600160a01b0316146114715761147161373b565b612e38612e83565b611471612eba565b612e48612e83565b612e5182612ec2565b61167081612ed3565b4681146107bb576040516377d879fb60e01b81526004810182905246602482015260440161087e565b600080516020613bb683398151915254600160401b900460ff1661147157604051631afcd79f60e31b815260040160405180910390fd5b611c6f612e83565b612eca612e83565b6107bb81612ee4565b612edb612e83565b6107bb81612f29565b612eec612e83565b6000612ef6611fbb565b6001815590506000612f06611411565b80546001600160a01b0319166001600160a01b0394909416939093179092555050565b6112a1612e83565b6001600160a01b03811681146107bb57600080fd5b600060208284031215612f5857600080fd5b81356111fc81612f31565b61ffff811681146107bb57600080fd5b8035611cad81612f63565b600060208284031215612f9057600080fd5b81356111fc81612f63565b634e487b7160e01b600052604160045260246000fd5b604051608081016001600160401b0381118282101715612fd357612fd3612f9b565b60405290565b60405161016081016001600160401b0381118282101715612fd357612fd3612f9b565b604051601f8201601f191681016001600160401b038111828210171561302457613024612f9b565b604052919050565b60006001600160401b0382111561304557613045612f9b565b50601f01601f191660200190565b600082601f83011261306457600080fd5b81356130776130728261302c565b612ffc565b81815284602083860101111561308c57600080fd5b816020850160208301376000918101602001919091529392505050565b6000602082840312156130bb57600080fd5b81356001600160401b038111156130d157600080fd5b6130dd84828501613053565b949350505050565b60ff811681146107bb57600080fd5b60006040828403121561310657600080fd5b604051604081016001600160401b03828210818311171561312957613129612f9b565b816040528293508435915061313d826130e5565b9082526020840135908082111561315357600080fd5b5061316085828601613053565b6020830152505092915050565b600080600080600060a0868803121561318557600080fd5b853561319081612f63565b945060208601356001600160401b03808211156131ac57600080fd5b6131b889838a016130f4565b955060408801359150808211156131ce57600080fd5b506131db88828901613053565b9598949750949560608101359550608001359392505050565b80151581146107bb57600080fd5b6000806040838503121561321557600080fd5b823561322081612f63565b91506020830135613230816131f4565b809150509250929050565b60006001600160401b0382111561325457613254612f9b565b5060051b60200190565b600080600080600060a0868803121561327657600080fd5b85356001600160401b038082111561328d57600080fd5b61329989838a01613053565b96506020915081880135818111156132b057600080fd5b8801601f81018a136132c157600080fd5b80356132cf6130728261323b565b81815260059190911b8201840190848101908c8311156132ee57600080fd5b8584015b838110156133265780358681111561330a5760008081fd5b6133188f8983890101613053565b8452509186019186016132f2565b5098505050506040880135945061334291505060608701612f73565b949793965091946080013592915050565b60006020828403121561336557600080fd5b604051602081018181106001600160401b038211171561338757613387612f9b565b6040528235613395816131f4565b81529392505050565b60005b838110156133b95781810151838201526020016133a1565b50506000910152565b600081518084526133da81602086016020860161339e565b601f01601f19169290920160200192915050565b6020815260006111fc60208301846133c2565b6000806040838503121561341457600080fd5b823561341f81612f63565b946020939093013593505050565b60006020828403121561343f57600080fd5b5035919050565b6000806040838503121561345957600080fd5b823561346481612f63565b915060208301356001600160401b0381111561347f57600080fd5b61348b858286016130f4565b9150509250929050565b63ffffffff60e01b831681526040602082015260006130dd60408301846133c2565b600082601f8301126134c857600080fd5b81516134d66130728261302c565b8181528460208386010111156134eb57600080fd5b6130dd82602083016020870161339e565b60006080828403121561350e57600080fd5b613516612fb1565b9050815181526020820151602082015260408201516001600160401b038082111561354057600080fd5b61354c858386016134b7565b6040840152606084015191508082111561356557600080fd5b50613572848285016134b7565b60608301525092915050565b6000806040838503121561359157600080fd5b82516001600160401b03808211156135a857600080fd5b6135b4868387016134fc565b935060208501519150808211156135ca57600080fd5b90840190606082870312156135de57600080fd5b6040516060810181811083821117156135f9576135f9612f9b565b8060405250825181526020830151602082015260408301518281111561361e57600080fd5b61362a888286016134b7565b6040830152508093505050509250929050565b60006020828403121561364f57600080fd5b81516001600160401b0381111561366557600080fd5b6130dd848285016134b7565b63ffffffff8416815260606020820152600061369060608301856133c2565b905060ff83166040830152949350505050565b80516001600160401b0381168114611cad57600080fd5b6000602082840312156136cc57600080fd5b6111fc826136a3565b634e487b7160e01b600052601160045260246000fd5b6001600160401b0381811683821601908082111561235c5761235c6136d5565b60006020828403121561371d57600080fd5b81516111fc81612f31565b818103818111156107e5576107e56136d5565b634e487b7160e01b600052600160045260246000fd5b8051611cad816131f4565b60006020828403121561376e57600080fd5b81516111fc816131f4565b63ffffffff60e01b8616815284602082015283604082015260a0606082015260006137a760a08301856133c2565b82810360808401526137b981856133c2565b98975050505050505050565b600080604083850312156137d857600080fd5b82516001600160401b03808211156137ef57600080fd5b6137fb868387016134fc565b9350602085015191508082111561381157600080fd5b5061348b858286016134b7565b600061ffff808a16835260018060a01b03808a16602085015260e0604085015261384b60e085018a6133c2565b925087606085015286608085015281861660a085015280851660c0850152505098975050505050505050565b60006020828403121561388957600080fd5b5051919050565b61ffff83168152604060208201528151604082015260208201516060820152600060408301516080808401526138c960c08401826133c2565b90506060840151603f198483030160a08501526138e682826133c2565b9695505050505050565b61ffff8416815282602082015260606040820152815160608201526020820151608082015260006040830151606060a08401526138e660c08401826133c2565b6000806040838503121561394357600080fd5b505080516020909101519092909150565b808201808211156107e5576107e56136d5565b8051611cad816130e5565b805163ffffffff81168114611cad57600080fd5b8051611cad81612f63565b600082601f8301126139a257600080fd5b815160206139b26130728361323b565b82815260079290921b840181019181810190868411156139d157600080fd5b8286015b84811015613a3657608081890312156139ee5760008081fd5b6139f6612fb1565b815181528482015185820152604080830151613a11816130e5565b90820152606082810151613a24816130e5565b908201528352918301916080016139d5565b509695505050505050565b600080600060608486031215613a5657600080fd5b83516001600160401b0380821115613a6d57600080fd5b908501906101608288031215613a8257600080fd5b613a8a612fd9565b613a9383613967565b8152613aa160208401613972565b6020820152613ab260408401613972565b6040820152613ac360608401613986565b606082015260808301516080820152613ade60a084016136a3565b60a0820152613aef60c08401613967565b60c082015260e083015182811115613b0657600080fd5b613b12898286016134b7565b60e083015250610100613b26818501613972565b908201526101208381015183811115613b3e57600080fd5b613b4a8a828701613991565b918301919091525061014083810151908201529450613b6b60208701613751565b93506040860151915080821115613b8157600080fd5b50613b8e868287016134b7565b9150509250925092565b600060208284031215613baa57600080fd5b81516111fc816130e556fef0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a26469706673582212206adc35ffd75f2ee0be1f33f47fcf181adacfa3d302fa7767704a2a41cc43bba164736f6c63430008130033'; + +type WormholeTransceiverConstructorParams = + | [linkLibraryAddresses: WormholeTransceiverLibraryAddresses, signer?: Signer] + | ConstructorParameters; + +const isSuperArgs = ( + xs: WormholeTransceiverConstructorParams, +): xs is ConstructorParameters => { + return ( + typeof xs[0] === 'string' || + (Array.isArray as (arg: any) => arg is readonly any[])(xs[0]) || + '_isInterface' in xs[0] + ); +}; + +export class WormholeTransceiver__factory extends ContractFactory { + constructor(...args: WormholeTransceiverConstructorParams) { + if (isSuperArgs(args)) { + super(...args); + } else { + const [linkLibraryAddresses, signer] = args; + super( + _abi, + WormholeTransceiver__factory.linkBytecode(linkLibraryAddresses), + signer, + ); + } + } + + static linkBytecode( + linkLibraryAddresses: WormholeTransceiverLibraryAddresses, + ): string { + let linkedBytecode = _bytecode; + + linkedBytecode = linkedBytecode.replace( + new RegExp('__\\$93083e246e55d56d98f3df2872cd16bfd0\\$__', 'g'), + linkLibraryAddresses[ + 'src/libraries/TransceiverStructs.sol:TransceiverStructs' + ] + .replace(/^0x/, '') + .toLowerCase(), + ); + + return linkedBytecode; + } + + override deploy( + nttManager: string, + wormholeCoreBridge: string, + wormholeRelayerAddr: string, + specialRelayerAddr: string, + _consistencyLevel: BigNumberish, + _gasLimit: BigNumberish, + overrides?: Overrides & { from?: string }, + ): Promise { + return super.deploy( + nttManager, + wormholeCoreBridge, + wormholeRelayerAddr, + specialRelayerAddr, + _consistencyLevel, + _gasLimit, + overrides || {}, + ) as Promise; + } + override getDeployTransaction( + nttManager: string, + wormholeCoreBridge: string, + wormholeRelayerAddr: string, + specialRelayerAddr: string, + _consistencyLevel: BigNumberish, + _gasLimit: BigNumberish, + overrides?: Overrides & { from?: string }, + ): TransactionRequest { + return super.getDeployTransaction( + nttManager, + wormholeCoreBridge, + wormholeRelayerAddr, + specialRelayerAddr, + _consistencyLevel, + _gasLimit, + overrides || {}, + ); + } + override attach(address: string): WormholeTransceiver { + return super.attach(address) as WormholeTransceiver; + } + override connect(signer: Signer): WormholeTransceiver__factory { + return super.connect(signer) as WormholeTransceiver__factory; + } + + static readonly bytecode = _bytecode; + static readonly abi = _abi; + static createInterface(): WormholeTransceiverInterface { + return new utils.Interface(_abi) as WormholeTransceiverInterface; + } + static connect( + address: string, + signerOrProvider: Signer | Provider, + ): WormholeTransceiver { + return new Contract(address, _abi, signerOrProvider) as WormholeTransceiver; + } +} + +export interface WormholeTransceiverLibraryAddresses { + ['src/libraries/TransceiverStructs.sol:TransceiverStructs']: string; +} diff --git a/wormhole-connect/src/routes/ntt/chains/evm/abis/common.ts b/wormhole-connect/src/routes/ntt/chains/evm/abis/common.ts new file mode 100644 index 000000000..c24bb0674 --- /dev/null +++ b/wormhole-connect/src/routes/ntt/chains/evm/abis/common.ts @@ -0,0 +1,46 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ +import type { Listener } from '@ethersproject/providers'; +import type { Event, EventFilter } from 'ethers'; + +export interface TypedEvent< + TArgsArray extends Array = any, + TArgsObject = any, +> extends Event { + args: TArgsArray & TArgsObject; +} + +export interface TypedEventFilter<_TEvent extends TypedEvent> + extends EventFilter {} + +export interface TypedListener { + (...listenerArg: [...__TypechainArgsArray, TEvent]): void; +} + +type __TypechainArgsArray = T extends TypedEvent ? U : never; + +export interface OnEvent { + ( + eventFilter: TypedEventFilter, + listener: TypedListener, + ): TRes; + (eventName: string, listener: Listener): TRes; +} + +export type MinEthersFactory = { + deploy(...a: ARGS[]): Promise; +}; + +export type GetContractTypeFromFactory = F extends MinEthersFactory< + infer C, + any +> + ? C + : never; + +export type GetARGsTypeFromFactory = F extends MinEthersFactory + ? Parameters + : never; + +export type PromiseOrValue = T | Promise; diff --git a/wormhole-connect/src/routes/ntt/chains/evm/getMessage.ts b/wormhole-connect/src/routes/ntt/chains/evm/getMessage.ts new file mode 100644 index 000000000..65a8ae85a --- /dev/null +++ b/wormhole-connect/src/routes/ntt/chains/evm/getMessage.ts @@ -0,0 +1,133 @@ +import { ChainName, ChainId } from '@wormhole-foundation/wormhole-connect-sdk'; +import { Implementation__factory } from '@certusone/wormhole-sdk/lib/esm/ethers-contracts'; +import { + parseWormholeLog, + RelayerPayloadId, + DeliveryInstruction, +} from '@certusone/wormhole-sdk/lib/esm/relayer'; +import { ethers } from 'ethers'; +import { hexlify } from 'ethers/lib/utils'; +import { NttRelayingType, UnsignedNttMessage } from 'routes/types'; +import { getTokenById, getTokenDecimals } from 'utils'; +import { getWormholeLogEvm } from 'utils/vaa'; +import config from 'config'; +import { toChainName } from 'utils/sdk'; +import { deserializePayload, Ntt } from '@wormhole-foundation/sdk-definitions'; +import { toChain, toChainId } from '@wormhole-foundation/sdk-base'; +import { + getNttGroupKeyByAddress, + getNttManagerConfigByGroupKey, + isNttToken, +} from 'utils/ntt'; + +const RELAYING_INFO_EVENT_TOPIC = + '0xc3192e083c87c556db539f071d8a298869f487e951327b5616a6f85ae3da958e'; +const RELAYING_INFO_IFACE = new ethers.utils.Interface([ + 'event RelayingInfo(uint8 relayingType, bytes32 refundAddress, uint256 deliveryPayment)', +]); + +export const getMessageEvm = async ( + tx: string, + chain: ChainName | ChainId, + receipt?: ethers.providers.TransactionReceipt, +): Promise => { + const provider = config.wh.mustGetProvider(chain); + if (!receipt) { + receipt = await provider.getTransactionReceipt(tx); + if (!receipt) throw new Error(`No receipt for tx ${tx} on ${chain}`); + } + const contract = new ethers.Contract( + receipt.to, + ['function token() public view returns (address)'], + provider, + ); + const tokenAddress = await contract.token(); + if (!tokenAddress) throw new Error('No token address'); + const fromChain = toChainName(chain); + const tokenId = { + chain: fromChain, + address: tokenAddress, + }; + const token = getTokenById(tokenId); + if (!token || !isNttToken(token)) + throw new Error(`Token ${tokenId} is not an NTT token`); + const wormholeLog = await getWormholeLogEvm(fromChain, receipt); + const parsedWormholeLog = + Implementation__factory.createInterface().parseLog(wormholeLog); + const relayingInfoEvent = receipt.logs.find( + (log) => log.topics[0] === RELAYING_INFO_EVENT_TOPIC, + ); + if (!relayingInfoEvent) throw new Error('RelayingInfo event not found'); + const parsedRelayingInfo = RELAYING_INFO_IFACE.parseLog(relayingInfoEvent); + const { relayingType, deliveryPayment } = parsedRelayingInfo.args; + let payload: Buffer; + if (relayingType === NttRelayingType.Standard) { + const { type, parsed } = parseWormholeLog(wormholeLog); + if (type !== RelayerPayloadId.Delivery) { + throw new Error(`Unexpected standard relayer payload type ${type}`); + } + payload = (parsed as DeliveryInstruction).payload; + } else if ( + relayingType === NttRelayingType.Manual || + relayingType === NttRelayingType.Special + ) { + payload = Buffer.from(parsedWormholeLog.args.payload.slice(2), 'hex'); + } else { + throw new Error(`Unexpected relaying type ${relayingType}`); + } + const transceiverMessage = deserializePayload( + 'Ntt:WormholeTransfer', + payload, + ); + const nttManagerMessage = transceiverMessage.nttManagerPayload; + const recipientChain = toChainName( + toChainId(nttManagerMessage.payload.recipientChain) as ChainId, + ); + const groupKey = getNttGroupKeyByAddress(receipt.to, fromChain); + if (!groupKey) throw new Error(`No NTT group key for ${receipt.to}`); + const recipientNttManagerConfig = getNttManagerConfigByGroupKey( + groupKey, + recipientChain, + ); + if (!recipientNttManagerConfig) + throw new Error('Recipient NTT manager not found'); + const receivedTokenKey = recipientNttManagerConfig.tokenKey; + if (!receivedTokenKey) + throw new Error(`Received token key not found for ${tokenId}`); + return { + sendTx: receipt.transactionHash, + sender: receipt.from, + amount: nttManagerMessage.payload.trimmedAmount.amount.toString(), + payloadID: 0, + recipient: config.wh.parseAddress( + nttManagerMessage.payload.recipientAddress.toString(), + recipientChain, + ), + toChain: recipientChain, + fromChain, + tokenAddress, + tokenChain: token.nativeChain, + tokenId, + tokenKey: token.key, + tokenDecimals: getTokenDecimals(config.wh.toChainId(fromChain), tokenId), + receivedTokenKey, + emitterAddress: hexlify( + config.wh.formatAddress(parsedWormholeLog.args.sender, fromChain), + ), + sequence: parsedWormholeLog.args.sequence.toString(), + block: receipt.blockNumber, + gasFee: receipt.gasUsed.mul(receipt.effectiveGasPrice).toString(), + recipientNttManager: config.wh.parseAddress( + hexlify(transceiverMessage.recipientNttManager.toString()), + recipientChain, + ), + messageDigest: hexlify( + Ntt.messageDigest( + toChain(config.wh.toChainId(fromChain) as number), + nttManagerMessage, + ), + ), + relayerFee: deliveryPayment.toString(), + relayingType, + }; +}; diff --git a/wormhole-connect/src/routes/ntt/chains/evm/index.ts b/wormhole-connect/src/routes/ntt/chains/evm/index.ts new file mode 100644 index 000000000..7ce665aea --- /dev/null +++ b/wormhole-connect/src/routes/ntt/chains/evm/index.ts @@ -0,0 +1,3 @@ +export * from './nttManager'; +export * from './getMessage'; +export * from './wormholeTransceiver'; diff --git a/wormhole-connect/src/routes/ntt/chains/evm/nttManager.ts b/wormhole-connect/src/routes/ntt/chains/evm/nttManager.ts new file mode 100644 index 000000000..0b10aca4a --- /dev/null +++ b/wormhole-connect/src/routes/ntt/chains/evm/nttManager.ts @@ -0,0 +1,256 @@ +import { + ChainId, + ChainName, + EthContext, + TokenId, + WormholeContext, +} from '@wormhole-foundation/wormhole-connect-sdk'; +import { getTokenById, tryParseErrorMessage } from 'utils'; +import { TransferWallet, signAndSendTransaction } from 'utils/wallet'; +import { BigNumber, ethers, PopulatedTransaction } from 'ethers'; +import { InboundQueuedTransfer } from '../../types'; +import { + InboundQueuedTransferNotFoundError, + InboundQueuedTransferStillQueuedError, + NotEnoughCapacityError, + ContractIsPausedError, + UnsupportedContractAbiVersion, +} from '../../errors'; +import { + encodeTransceiverInstructions, + encodeWormholeTransceiverInstruction, +} from 'routes/ntt/utils'; +import { NttManager__factory as NttManager__factory_0_1_0 } from './abis/0.1.0/NttManager__factory'; +import { NttManager as NttManager_0_1_0 } from './abis/0.1.0/NttManager'; +import config from 'config'; +import { toChainId, toChainName } from 'utils/sdk'; + +const ABI_VERSION_0_1_0 = '0.1.0'; + +export class NttManagerEvm { + static readonly abiVersionCache = new Map(); + + constructor(readonly chain: ChainName | ChainId, readonly address: string) {} + + async signAndSendTransaction( + tx: PopulatedTransaction, + walletType: TransferWallet, + ): Promise { + const signer = await config.wh.mustGetSigner(this.chain); + const response = await signer.sendTransaction(tx); + const receipt = await response.wait(); + const txId = await signAndSendTransaction( + toChainName(this.chain), + receipt, + walletType, + ); + return txId; + } + + async quoteDeliveryPrice( + destChain: ChainName | ChainId, + shouldSkipRelayerSend: boolean, + ): Promise { + const { abi } = await this.getAbi(); + const transceiverIxs = encodeTransceiverInstructions([ + { + index: 0, + payload: encodeWormholeTransceiverInstruction({ + shouldSkipRelayerSend, + }), + }, + ]); + const [, deliveryPrice] = await abi.quoteDeliveryPrice( + toChainId(destChain), + transceiverIxs, + ); + return deliveryPrice; + } + + async send( + token: TokenId, + sender: string, + recipient: string, + amount: bigint, + toChain: ChainName | ChainId, + shouldSkipRelayerSend: boolean, + ): Promise { + const tokenConfig = getTokenById(token); + if (!tokenConfig) throw new Error('token not found'); + const { abi } = await this.getAbi(); + const deliveryPrice = await this.quoteDeliveryPrice( + toChain, + shouldSkipRelayerSend, + ); + const transceiverIxs = encodeTransceiverInstructions([ + { + index: 0, + payload: encodeWormholeTransceiverInstruction({ + shouldSkipRelayerSend, + }), + }, + ]); + const formattedRecipient = config.wh.formatAddress(recipient, toChain); + const tx = await abi.populateTransaction[ + 'transfer(uint256,uint16,bytes32,bytes32,bool,bytes)' + ]( + amount, + toChainId(toChain), + formattedRecipient, + formattedRecipient, // SR gas refund goes to recipient + false, // revert instead of getting outbound queued + transceiverIxs, + { value: deliveryPrice }, + ); + const context = config.wh.getContext( + this.chain, + ) as EthContext; + await context.approve(this.chain, this.address, token.address, amount); + try { + return await this.signAndSendTransaction(tx, TransferWallet.SENDING); + } catch (e: any) { + this.throwParsedError(abi.interface, e); + } + } + + async getCurrentOutboundCapacity(): Promise { + const { abi } = await this.getAbi(); + return (await abi.getCurrentOutboundCapacity()).toString(); + } + + async getCurrentInboundCapacity( + fromChain: ChainName | ChainId, + ): Promise { + const { abi } = await this.getAbi(); + return ( + await abi.getCurrentInboundCapacity(toChainId(fromChain)) + ).toString(); + } + + async getRateLimitDuration(): Promise { + const { abi } = await this.getAbi(); + return (await abi.rateLimitDuration()).toNumber(); + } + + async getInboundQueuedTransfer( + messageDigest: string, + ): Promise { + const { abi } = await this.getAbi(); + const queuedTransfer = await abi.getInboundQueuedTransfer(messageDigest); + if (queuedTransfer.txTimestamp.gt(0)) { + const { recipient, amount, txTimestamp } = queuedTransfer; + const duration = await this.getRateLimitDuration(); + return { + recipient: config.wh.parseAddress(recipient, this.chain), + amount: amount.toString(), + rateLimitExpiryTimestamp: txTimestamp.add(duration).toNumber(), + }; + } + return undefined; + } + + async completeInboundQueuedTransfer( + messageDigest: string, + recipient: string, + payer: string, + ): Promise { + const { abi } = await this.getAbi(); + try { + const tx = await abi.populateTransaction.completeInboundQueuedTransfer( + messageDigest, + ); + return await this.signAndSendTransaction(tx, TransferWallet.RECEIVING); + } catch (e) { + this.throwParsedError(abi.interface, e); + } + } + + // The transfer is "complete" when the message is executed and not inbound queued + async isTransferCompleted(messageDigest: string): Promise { + const { abi } = await this.getAbi(); + const isMessageExecuted = await abi.isMessageExecuted(messageDigest); + if (isMessageExecuted) { + const queuedTransfer = await this.getInboundQueuedTransfer(messageDigest); + return !queuedTransfer; + } + return false; + } + + async isPaused(): Promise { + const { abi } = await this.getAbi(); + return await abi.isPaused(); + } + + async fetchRedeemTx(messageDigest: string): Promise { + const { abi } = await this.getAbi(); + const eventFilter = abi.filters.TransferRedeemed(messageDigest); + const provider = config.wh.mustGetProvider(this.chain); + const currentBlock = await provider.getBlockNumber(); + const chainName = toChainName(this.chain); + const chainConfig = config.chains[chainName]!; + const events = await abi.queryFilter( + eventFilter, + currentBlock - chainConfig.maxBlockSearch, + ); + return events?.[0]?.transactionHash || undefined; + } + + throwParsedError(iface: ethers.utils.Interface, e: any): never { + const message = tryParseErrorMessage(iface, e); + if (message === 'InboundQueuedTransferNotFound') { + throw new InboundQueuedTransferNotFoundError(); + } + if (message === 'InboundQueuedTransferStillQueued') { + throw new InboundQueuedTransferStillQueuedError(); + } + if (message === 'RequireContractIsNotPaused') { + throw new ContractIsPausedError(); + } + if (message === 'NotEnoughCapacity') { + throw new NotEnoughCapacityError(); + } + throw e; + } + + async getAbi(): Promise<{ + abi: NttManager_0_1_0; + version: string; + }> { + const provider = config.wh.mustGetProvider(this.chain); + const abiVersionKey = `${this.address}-${toChainName(this.chain)}`; + let abiVersion = NttManagerEvm.abiVersionCache.get(abiVersionKey); + if (!abiVersion) { + const contract = new ethers.Contract( + this.address, + ['function NTT_MANAGER_VERSION() public view returns (string)'], + provider, + ); + try { + abiVersion = await contract.NTT_MANAGER_VERSION(); + } catch (e) { + console.error( + `Failed to get NTT_MANAGER_VERSION from contract ${ + this.address + } on chain ${toChainName(this.chain)}`, + ); + throw e; + } + if (!abiVersion) { + throw new Error('NTT_MANAGER_VERSION not found'); + } + NttManagerEvm.abiVersionCache.set(abiVersionKey, abiVersion); + } + if (abiVersion === ABI_VERSION_0_1_0) { + return { + abi: NttManager__factory_0_1_0.connect(this.address, provider), + version: abiVersion, + }; + } + console.error( + `Unsupported NttManager version ${abiVersion} for chain ${toChainName( + this.chain, + )}`, + ); + throw new UnsupportedContractAbiVersion(); + } +} diff --git a/wormhole-connect/src/routes/ntt/chains/evm/wormholeTransceiver.ts b/wormhole-connect/src/routes/ntt/chains/evm/wormholeTransceiver.ts new file mode 100644 index 000000000..89b10e813 --- /dev/null +++ b/wormhole-connect/src/routes/ntt/chains/evm/wormholeTransceiver.ts @@ -0,0 +1,88 @@ +import { ChainId, ChainName } from '@wormhole-foundation/wormhole-connect-sdk'; +import { WormholeTransceiver__factory as WormholeTransceiver__factory_0_1_0 } from './abis/0.1.0/WormholeTransceiver__factory'; +import { WormholeTransceiver as WormholeTransceiver_0_1_0 } from './abis/0.1.0/WormholeTransceiver'; +import { TransferWallet, signAndSendTransaction } from 'utils/wallet'; +import config from 'config'; +import { toChainId, toChainName } from 'utils/sdk'; +import { ethers } from 'ethers'; +import { UnsupportedContractAbiVersion } from 'routes/ntt/errors'; + +const ABI_VERSION_0_1_0 = '0.1.0'; + +export class WormholeTransceiver { + static readonly abiVersionCache = new Map(); + + constructor(readonly chain: ChainName | ChainId, readonly address: string) {} + + async isWormholeRelayingEnabled( + destChain: ChainName | ChainId, + ): Promise { + const { abi } = await this.getAbi(); + return await abi.isWormholeRelayingEnabled(toChainId(destChain)); + } + + async isSpecialRelayingEnabled( + destChain: ChainName | ChainId, + ): Promise { + const { abi } = await this.getAbi(); + return await abi.isSpecialRelayingEnabled(toChainId(destChain)); + } + + async receiveMessage(vaa: string, payer: string): Promise { + const { abi } = await this.getAbi(); + const tx = await abi.populateTransaction.receiveMessage(vaa); + const signer = await config.wh.mustGetSigner(this.chain); + const response = await signer.sendTransaction(tx); + const receipt = await response.wait(); + const txId = await signAndSendTransaction( + toChainName(this.chain), + receipt, + TransferWallet.RECEIVING, + ); + return txId; + } + + async getAbi(): Promise<{ + abi: WormholeTransceiver_0_1_0; + version: string; + }> { + const provider = config.wh.mustGetProvider(this.chain); + const abiVersionKey = `${this.address}-${toChainName(this.chain)}`; + let abiVersion = WormholeTransceiver.abiVersionCache.get(abiVersionKey); + if (!abiVersion) { + const contract = new ethers.Contract( + this.address, + [ + 'function WORMHOLE_TRANSCEIVER_VERSION() public view returns (string)', + ], + provider, + ); + try { + abiVersion = await contract.WORMHOLE_TRANSCEIVER_VERSION(); + } catch (e) { + console.error( + `Failed to get WORMHOLE_TRANSCEIVER_VERSION from contract ${ + this.address + } on chain ${toChainName(this.chain)}`, + ); + throw e; + } + if (!abiVersion) { + throw new Error('WORMHOLE_TRANSCEIVER_VERSION not found'); + } + WormholeTransceiver.abiVersionCache.set(abiVersionKey, abiVersion); + } + if (abiVersion === ABI_VERSION_0_1_0) { + return { + abi: WormholeTransceiver__factory_0_1_0.connect(this.address, provider), + version: abiVersion, + }; + } + console.error( + `Unsupported WormholeTransceiver version ${abiVersion} for chain ${toChainName( + this.chain, + )}`, + ); + throw new UnsupportedContractAbiVersion(); + } +} diff --git a/wormhole-connect/src/routes/ntt/chains/index.ts b/wormhole-connect/src/routes/ntt/chains/index.ts new file mode 100644 index 000000000..180df6b7b --- /dev/null +++ b/wormhole-connect/src/routes/ntt/chains/index.ts @@ -0,0 +1,17 @@ +import { ChainName, ChainId } from '@wormhole-foundation/wormhole-connect-sdk'; +import { isEvmChain, toChainName } from 'utils/sdk'; +import { NttManagerEvm } from './evm'; +import { NttManagerSolana } from './solana'; + +export const getNttManager = ( + chain: ChainName | ChainId, + nttManagerAddress: string, +) => { + if (isEvmChain(chain)) { + return new NttManagerEvm(chain, nttManagerAddress); + } + if (toChainName(chain) === 'solana') { + return new NttManagerSolana(nttManagerAddress); + } + throw new Error(`Unsupported chain: ${chain}`); +}; diff --git a/wormhole-connect/src/routes/ntt/chains/solana/getMessage.ts b/wormhole-connect/src/routes/ntt/chains/solana/getMessage.ts new file mode 100644 index 000000000..81504be3f --- /dev/null +++ b/wormhole-connect/src/routes/ntt/chains/solana/getMessage.ts @@ -0,0 +1,120 @@ +import { solanaContext } from 'utils/sdk'; +import { PostedMessageData } from '@certusone/wormhole-sdk/lib/esm/solana/wormhole'; +import { hexlify } from 'ethers/lib/utils'; +import { NttRelayingType, UnsignedNttMessage } from 'routes/types'; +import { getTokenById, getTokenDecimals } from 'utils'; +import config from 'config'; +import { deserializePayload, Ntt } from '@wormhole-foundation/sdk-definitions'; +import { toChainId } from '@wormhole-foundation/sdk-base'; +import { ChainName } from '@wormhole-foundation/wormhole-connect-sdk'; +import { + getNttGroupKeyByAddress, + getNttManagerConfigByGroupKey, + isNttToken, +} from 'utils/ntt'; +import { PublicKey } from '@solana/web3.js'; + +export const getMessageSolana = async ( + tx: string, +): Promise => { + const context = solanaContext(); + const connection = context.connection; + if (!connection) throw new Error('Connection not found'); + const response = await connection.getTransaction(tx, { + maxSupportedTransactionVersion: 0, + }); + if (!response) throw new Error('Transaction not found'); + const fromChain: ChainName = 'solana'; + const core = config.wh.mustGetContracts(fromChain).core; + const accounts = response.transaction.message.getAccountKeys(); + const wormholeIx = response.meta?.innerInstructions + ?.flatMap((ix) => ix.instructions) + .find((ix) => accounts?.get(ix.programIdIndex)?.toString() === core); + if (!wormholeIx) throw new Error('Wormhole instruction not found'); + const wormholeMessageAccountKey = accounts?.get(wormholeIx.accounts[1]); + if (!wormholeMessageAccountKey) throw new Error('Message account not found'); + const wormholeMessageAccount = await connection.getAccountInfo( + wormholeMessageAccountKey, + ); + if (wormholeMessageAccount === null) + throw new Error('wormhole message account not found'); + const messageData = PostedMessageData.deserialize( + wormholeMessageAccount.data, + ); + const transceiverMessage = deserializePayload( + 'Ntt:WormholeTransfer', + messageData.message.payload, + ); + const nttManagerMessage = transceiverMessage.nttManagerPayload; + const recipientChain = config.wh.toChainName( + toChainId(nttManagerMessage.payload.recipientChain), + ); + const tokenAddress = config.wh.parseAddress( + hexlify(nttManagerMessage.payload.sourceToken.toString()), + fromChain, + ); + const tokenId = { + chain: fromChain, + address: tokenAddress, + }; + const token = getTokenById(tokenId); + if (!token || !isNttToken(token)) + throw new Error(`Token ${tokenId} is not an NTT token`); + const groupKey = getNttGroupKeyByAddress( + new PublicKey( + transceiverMessage.sourceNttManager.toUint8Array(), + ).toString(), + fromChain, + ); + if (!groupKey) throw new Error('Group key not found'); + const recipientNttManagerConfig = getNttManagerConfigByGroupKey( + groupKey, + recipientChain, + ); + if (!recipientNttManagerConfig) + throw new Error('Recipient NTT manager not found'); + const receivedTokenKey = recipientNttManagerConfig.tokenKey; + if (!receivedTokenKey) + throw new Error(`Received token key not found for ${tokenId}`); + const logMsgs = response.meta?.logMessages || []; + const regex = /total fee in lamports: (\d+)/; + const relayerFeeMsg = logMsgs.find((msg) => regex.test(msg)); + const relayerFee = relayerFeeMsg ? regex.exec(relayerFeeMsg)?.[1] : ''; + return { + sendTx: tx, + sender: config.wh.parseAddress( + hexlify(nttManagerMessage.sender.toUint8Array()), + fromChain, + ), + amount: nttManagerMessage.payload.trimmedAmount.amount.toString(), + payloadID: 0, + recipient: config.wh.parseAddress( + hexlify(nttManagerMessage.payload.recipientAddress.toString()), + recipientChain, + ), + toChain: recipientChain, + fromChain, + tokenAddress: config.wh.parseAddress( + hexlify(nttManagerMessage.payload.sourceToken.toString()), + fromChain, + ), + tokenChain: token.nativeChain, + tokenId, + tokenKey: token.key, + tokenDecimals: getTokenDecimals(config.wh.toChainId(fromChain), tokenId), + receivedTokenKey, + emitterAddress: hexlify( + context.formatAddress(messageData.message.emitterAddress), + ), + sequence: messageData.message.sequence.toString(), + block: response.slot, + gasFee: response.meta?.fee.toString(), + recipientNttManager: config.wh.parseAddress( + hexlify(transceiverMessage.recipientNttManager.toString()), + recipientChain, + ), + messageDigest: hexlify(Ntt.messageDigest('Solana', nttManagerMessage)), + relayerFee: relayerFee || '', + relayingType: relayerFee ? NttRelayingType.Special : NttRelayingType.Manual, + }; +}; diff --git a/wormhole-connect/src/routes/ntt/chains/solana/index.ts b/wormhole-connect/src/routes/ntt/chains/solana/index.ts new file mode 100644 index 000000000..6a3c0bbfd --- /dev/null +++ b/wormhole-connect/src/routes/ntt/chains/solana/index.ts @@ -0,0 +1,2 @@ +export * from './nttManager'; +export * from './getMessage'; diff --git a/wormhole-connect/src/routes/ntt/chains/solana/nttManager.ts b/wormhole-connect/src/routes/ntt/chains/solana/nttManager.ts new file mode 100644 index 000000000..7ec872d8d --- /dev/null +++ b/wormhole-connect/src/routes/ntt/chains/solana/nttManager.ts @@ -0,0 +1,793 @@ +import { + addComputeBudget, + ChainId, + ChainName, + TokenId, +} from '@wormhole-foundation/wormhole-connect-sdk'; +import { InboundQueuedTransfer } from '../../types'; +import { solanaContext, toChainId, toChainName } from 'utils/sdk'; +import { TransferWallet, postVaa, signAndSendTransaction } from 'utils/wallet'; +import { + Connection, + Keypair, + PublicKey, + Transaction, + TransactionInstruction, +} from '@solana/web3.js'; +import { + createApproveInstruction, + getAssociatedTokenAddressSync, + createAssociatedTokenAccountInstruction, +} from '@solana/spl-token'; +import { BN, IdlAccounts, Program } from '@coral-xyz/anchor'; +import { SignedVaa, parseVaa } from '@certusone/wormhole-sdk/lib/esm'; +import { utils } from 'ethers'; +import { deserializePayload, Ntt } from '@wormhole-foundation/sdk-definitions'; +import { + ExampleNativeTokenTransfers, + IDL, +} from './types/example_native_token_transfers'; +import { + derivePostedVaaKey, + getWormholeDerivedAccounts, +} from '@certusone/wormhole-sdk/lib/esm/solana/wormhole'; +import { associatedAddress } from '@coral-xyz/anchor/dist/esm/utils/token'; +import { NttQuoter } from './nttQuoter'; +import { Keccak } from 'sha3'; +import CONFIG from 'config'; +import { toChain as SDKv2toChain } from '@wormhole-foundation/sdk-base'; +import { hexlify } from 'ethers/lib/utils'; +import { getNttManagerConfigByAddress } from 'utils/ntt'; + +// TODO: make sure this is in sync with the contract +const RATE_LIMIT_DURATION = 24 * 60 * 60; + +type Config = IdlAccounts['config']; +type InboxItem = IdlAccounts['inboxItem']; +type OutboxRateLimit = + IdlAccounts['outboxRateLimit']; +type InboxRateLimit = + IdlAccounts['inboxRateLimit']; + +interface TransferArgs { + amount: BN; + recipientChain: { id: ChainId }; + recipientAddress: number[]; + shouldQueue: boolean; +} + +export class NttManagerSolana { + readonly connection: Connection; + readonly program: Program; + readonly wormholeId: string; + + constructor(readonly nttId: string) { + const { connection } = solanaContext(); + if (!connection) throw new Error('Connection not found'); + this.connection = connection; + this.program = new Program(IDL, nttId, { connection }); + const core = CONFIG.wh.mustGetContracts('solana').core; + if (!core) throw new Error('Core not found'); + this.wormholeId = core; + } + + async send( + token: TokenId, + sender: string, + recipient: string, + amount: bigint, + toChain: ChainName | ChainId, + shouldSkipRelayerSend: boolean, + ): Promise { + const config = await this.getConfig(); + const outboxItem = Keypair.generate(); + const destContext = CONFIG.wh.getContext(toChain); + const payer = new PublicKey(sender); + const tokenAccount = getAssociatedTokenAddressSync( + new PublicKey(token.address), + payer, + ); + const txArgs = { + payer, + from: tokenAccount, + amount: new BN(amount.toString()), + recipientChain: toChainName(toChain), + recipientAddress: destContext.formatAddress(recipient), + fromAuthority: payer, + outboxItem: outboxItem.publicKey, + config, + shouldQueue: false, // revert instead of getting queued + }; + let transferIx; + if (config.mode.locking) { + transferIx = await this.createTransferLockInstruction(txArgs); + } else { + transferIx = await this.createTransferBurnInstruction(txArgs); + } + const releaseIx = await this.createReleaseOutboundInstruction({ + payer, + outboxItem: outboxItem.publicKey, + revertOnDelay: !txArgs.shouldQueue, + }); + const transferArgs: TransferArgs = { + amount: txArgs.amount, + recipientChain: { id: toChainId(txArgs.recipientChain) }, + recipientAddress: Array.from(txArgs.recipientAddress), + shouldQueue: txArgs.shouldQueue, + }; + const approveIx = createApproveInstruction( + tokenAccount, + this.sessionAuthorityAddress(txArgs.fromAuthority, transferArgs), + payer, + BigInt(amount.toString()), + ); + const tx = new Transaction(); + tx.add(approveIx, transferIx, releaseIx); + if (!shouldSkipRelayerSend) { + const nttConfig = getNttManagerConfigByAddress( + this.program.programId.toString(), + 'solana', + ); + if (!nttConfig || !nttConfig.solanaQuoter) throw new Error('no quoter'); + const quoter = new NttQuoter(nttConfig.solanaQuoter); + const fee = await quoter.calcRelayCost(toChain); + const relayIx = await quoter.createRequestRelayInstruction( + payer, + outboxItem.publicKey, + toChain, + fee, + ); + tx.add(relayIx); + } + tx.feePayer = payer; + const { blockhash } = await this.connection.getLatestBlockhash('finalized'); + tx.recentBlockhash = blockhash; + await addComputeBudget(this.connection, tx); + tx.partialSign(outboxItem); + const txId = await signAndSendTransaction( + 'solana', + tx, + TransferWallet.SENDING, + { commitment: 'finalized' }, + ); + return txId; + } + + async receiveMessage(vaa: string, payer: string): Promise { + const core = CONFIG.wh.mustGetContracts('solana').core; + if (!core) throw new Error('Core not found'); + const config = await this.getConfig(); + const vaaArray = utils.arrayify(vaa, { allowMissingPrefix: true }); + const payerPublicKey = new PublicKey(payer); + const redeemArgs = { + payer: payerPublicKey, + vaa: vaaArray, + config, + }; + const parsedVaa = parseVaa(vaaArray); + const chainId = toChainId(parsedVaa.emitterChain as ChainId); + // First post the VAA + await postVaa(this.connection, core, Buffer.from(vaaArray)); + const tx = new Transaction(); + // Create the ATA if it doesn't exist + const mint = await this.mintAccountAddress(config); + const ata = getAssociatedTokenAddressSync(mint, payerPublicKey); + if (!(await this.connection.getAccountInfo(ata))) { + const createAtaIx = createAssociatedTokenAccountInstruction( + payerPublicKey, + ata, + payerPublicKey, + mint, + ); + tx.add(createAtaIx); + } + // Here we create a transaction with three instructions: + // 1. receive wormhole message (vaa) + // 2. redeem + // 3. releaseInboundMint or releaseInboundUnlock (depending on mode) + // + // The first instruction verifies the VAA. + // The second instruction places the transfer in the inbox, then the third instruction + // releases it. + // + // In case the redeemed amount exceeds the remaining inbound rate limit capacity, + // the transaction gets delayed. If this happens, the second instruction will not actually + // be able to release the transfer yet. + // To make sure the transaction still succeeds, we set revertOnDelay to false, which will + // just make the second instruction a no-op in case the transfer is delayed. + tx.add(await this.createReceiveWormholeMessageInstruction(redeemArgs)); + tx.add(await this.createRedeemInstruction(redeemArgs)); + const { nttManagerPayload } = deserializePayload( + 'Ntt:WormholeTransfer', + parsedVaa.payload, + ); + const messageDigest = Ntt.messageDigest( + SDKv2toChain(chainId), + nttManagerPayload, + ); + const releaseArgs = { + ...redeemArgs, + messageDigest: hexlify(messageDigest), + recipient: new PublicKey( + nttManagerPayload.payload.recipientAddress.toUint8Array(), + ), + chain: chainId, + revertOnDelay: false, + }; + if (config.mode.locking) { + tx.add(await this.createReleaseInboundUnlockInstruction(releaseArgs)); + } else { + tx.add(await this.createReleaseInboundMintInstruction(releaseArgs)); + } + tx.feePayer = payerPublicKey; + const { blockhash } = await this.connection.getLatestBlockhash('finalized'); + tx.recentBlockhash = blockhash; + await addComputeBudget(this.connection, tx); + const txId = await signAndSendTransaction( + 'solana', + tx, + TransferWallet.RECEIVING, + { commitment: 'finalized' }, + ); + return txId; + } + + async getCurrentOutboundCapacity(): Promise { + const { + rateLimit: { limit, capacityAtLastTx, lastTxTimestamp }, + } = await this.getOutboxRateLimit(); + return await this.getCurrentCapacity( + limit, + capacityAtLastTx, + lastTxTimestamp, + ); + } + + async getCurrentInboundCapacity( + fromChain: ChainName | ChainId, + ): Promise { + const { + rateLimit: { limit, capacityAtLastTx, lastTxTimestamp }, + } = await this.getInboxRateLimit(fromChain); + return await this.getCurrentCapacity( + limit, + capacityAtLastTx, + lastTxTimestamp, + ); + } + + async getCurrentCapacity( + limit: BN, + capacityAtLastTx: BN, + lastTxTimestamp: BN, + ) { + const timePassed = BN.max( + new BN(Date.now() / 1000).sub(lastTxTimestamp), + new BN(0), + ); + const duration = await this.getRateLimitDuration(); + const calculatedCapacity = capacityAtLastTx.add( + timePassed.mul(limit).div(new BN(duration)), + ); + return calculatedCapacity.lt(limit) + ? calculatedCapacity.toString() + : limit.toString(); + } + + async getRateLimitDuration(): Promise { + // TODO: how will solana implement this? + // const config = await this.getConfig(); + // return config.rateLimitDuration.toNumber(); + return RATE_LIMIT_DURATION; + } + + async getInboundQueuedTransfer( + messageDigest: string, + ): Promise { + let inboxItem; + try { + inboxItem = await this.getInboxItem(messageDigest); + } catch (e: any) { + if (e.message?.includes('Account does not exist')) { + return undefined; + } + throw e; + } + if (inboxItem.releaseStatus.releaseAfter) { + return { + recipient: inboxItem.recipientAddress.toString(), + amount: inboxItem.amount.toString(), + rateLimitExpiryTimestamp: + inboxItem.releaseStatus.releaseAfter[0].toNumber(), + }; + } + return undefined; + } + + async completeInboundQueuedTransfer( + messageDigest: string, + recipientAddress: string, + payer: string, + ): Promise { + const payerPublicKey = new PublicKey(payer); + const releaseArgs = { + payer: payerPublicKey, + messageDigest, + recipient: new PublicKey(recipientAddress), + revertOnDelay: false, + }; + const config = await this.getConfig(); + const tx = new Transaction(); + // Create the ATA if it doesn't exist + const mint = await this.mintAccountAddress(config); + const ata = getAssociatedTokenAddressSync(mint, payerPublicKey); + if (!(await this.connection.getAccountInfo(ata))) { + const createAtaIx = createAssociatedTokenAccountInstruction( + payerPublicKey, + ata, + payerPublicKey, + mint, + ); + tx.add(createAtaIx); + } + if (config.mode.locking) { + tx.add(await this.createReleaseInboundUnlockInstruction(releaseArgs)); + } else { + tx.add(await this.createReleaseInboundMintInstruction(releaseArgs)); + } + tx.feePayer = payerPublicKey; + const { blockhash } = await this.connection.getLatestBlockhash('finalized'); + tx.recentBlockhash = blockhash; + await addComputeBudget(this.connection, tx); + const txId = await signAndSendTransaction( + 'solana', + tx, + TransferWallet.RECEIVING, + { commitment: 'finalized' }, + ); + return txId; + } + + async isTransferCompleted(messageDigest: string): Promise { + try { + const inboxItem = await this.getInboxItem(messageDigest); + return !!inboxItem.releaseStatus.released; + } catch (e: any) { + if (e.message?.includes('Account does not exist')) { + return false; + } + throw e; + } + } + + async fetchRedeemTx(messageDigest: string): Promise { + const address = await this.inboxItemAccountAddress(messageDigest); + // fetch the most recent signature + const signatures = await this.connection.getSignaturesForAddress(address, { + limit: 1, + }); + return signatures?.[0]?.signature; + } + + // Account addresses + + derivePda(seeds: Buffer | Array): PublicKey { + const seedsArray = seeds instanceof Buffer ? [seeds] : seeds; + const [address] = PublicKey.findProgramAddressSync( + seedsArray, + this.program.programId, + ); + return address; + } + + configAccountAddress(): PublicKey { + return this.derivePda(Buffer.from('config')); + } + + outboxRateLimitAccountAddress(): PublicKey { + return this.derivePda(Buffer.from('outbox_rate_limit')); + } + + inboxRateLimitAccountAddress(chain: ChainName | ChainId): PublicKey { + const chainId = toChainId(chain); + return this.derivePda([ + Buffer.from('inbox_rate_limit'), + new BN(chainId).toBuffer('be', 2), + ]); + } + + inboxItemAccountAddress(messageDigest: string): PublicKey { + return this.derivePda([ + Buffer.from('inbox_item'), + Buffer.from(messageDigest.slice(2), 'hex'), + ]); + } + + sessionAuthorityAddress(sender: PublicKey, args: TransferArgs): PublicKey { + const { amount, recipientChain, recipientAddress, shouldQueue } = args; + const serialized = Buffer.concat([ + amount.toArrayLike(Buffer, 'be', 8), + Buffer.from(new BN(recipientChain.id).toArrayLike(Buffer, 'be', 2)), + Buffer.from(new Uint8Array(recipientAddress)), + Buffer.from([shouldQueue ? 1 : 0]), + ]); + const hasher = new Keccak(256); + hasher.update(serialized); + return this.derivePda([ + Buffer.from('session_authority'), + sender.toBytes(), + hasher.digest(), + ]); + } + + tokenAuthorityAddress(): PublicKey { + return this.derivePda([Buffer.from('token_authority')]); + } + + emitterAccountAddress(): PublicKey { + return this.derivePda([Buffer.from('emitter')]); + } + + wormholeMessageAccountAddress(outboxItem: PublicKey): PublicKey { + return this.derivePda([Buffer.from('message'), outboxItem.toBuffer()]); + } + + peerAccountAddress(chain: ChainName | ChainId): PublicKey { + const chainId = toChainId(chain); + return this.derivePda([ + Buffer.from('peer'), + new BN(chainId).toBuffer('be', 2), + ]); + } + + transceiverPeerAccountAddress(chain: ChainName | ChainId): PublicKey { + const chainId = toChainId(chain); + return this.derivePda([ + Buffer.from('transceiver_peer'), + new BN(chainId).toBuffer('be', 2), + ]); + } + + transceiverMessageAccountAddress( + chain: ChainName | ChainId, + id: Buffer, + ): PublicKey { + const chainId = toChainId(chain); + if (id.length !== 32) { + throw new Error('id must be 32 bytes'); + } + return this.derivePda([ + Buffer.from('transceiver_message'), + new BN(chainId).toBuffer('be', 2), + id, + ]); + } + + registeredTransceiverAddress(transceiver: PublicKey): PublicKey { + return this.derivePda([ + Buffer.from('registered_transceiver'), + transceiver.toBuffer(), + ]); + } + + // Instructions + + /** + * Creates a transfer_burn instruction. The `payer` and `fromAuthority` + * arguments must sign the transaction + */ + async createTransferBurnInstruction(args: { + payer: PublicKey; + from: PublicKey; + fromAuthority: PublicKey; + amount: BN; + recipientChain: ChainName; + recipientAddress: ArrayLike; + outboxItem: PublicKey; + shouldQueue: boolean; + config?: Config; + }): Promise { + const config = await this.getConfig(args.config); + const chainId = toChainId(args.recipientChain); + const mint = await this.mintAccountAddress(config); + const transferArgs: TransferArgs = { + amount: args.amount, + recipientChain: { id: chainId }, + recipientAddress: Array.from(args.recipientAddress), + shouldQueue: args.shouldQueue, + }; + return await this.program.methods + .transferBurn({ + amount: args.amount, + recipientChain: { id: chainId }, + recipientAddress: Array.from(args.recipientAddress), + shouldQueue: args.shouldQueue, + }) + .accounts({ + common: { + payer: args.payer, + config: { config: this.configAccountAddress() }, + mint, + from: args.from, + outboxItem: args.outboxItem, + outboxRateLimit: this.outboxRateLimitAccountAddress(), + }, + peer: this.peerAccountAddress(args.recipientChain), + inboxRateLimit: this.inboxRateLimitAccountAddress(args.recipientChain), + sessionAuthority: this.sessionAuthorityAddress( + args.fromAuthority, + transferArgs, + ), + }) + .instruction(); + } + + /** + * Creates a transfer_lock instruction. The `payer`, `fromAuthority`, and `outboxItem` + * arguments must sign the transaction + */ + async createTransferLockInstruction(args: { + payer: PublicKey; + from: PublicKey; + fromAuthority: PublicKey; + amount: BN; + recipientChain: ChainName; + recipientAddress: ArrayLike; + shouldQueue: boolean; + outboxItem: PublicKey; + config: Config; + }): Promise { + const chainId = toChainId(args.recipientChain); + const mint = await this.mintAccountAddress(args.config); + const transferArgs: TransferArgs = { + amount: args.amount, + recipientChain: { id: chainId }, + recipientAddress: Array.from(args.recipientAddress), + shouldQueue: args.shouldQueue, + }; + return await this.program.methods + .transferLock({ + amount: args.amount, + recipientChain: { id: chainId }, + recipientAddress: Array.from(args.recipientAddress), + shouldQueue: args.shouldQueue, + }) + .accounts({ + common: { + payer: args.payer, + config: { config: this.configAccountAddress() }, + mint, + from: args.from, + tokenProgram: await this.tokenProgram(args.config), + outboxItem: args.outboxItem, + outboxRateLimit: this.outboxRateLimitAccountAddress(), + }, + peer: this.peerAccountAddress(args.recipientChain), + inboxRateLimit: this.inboxRateLimitAccountAddress(args.recipientChain), + custody: await this.custodyAccountAddress(args.config), + sessionAuthority: this.sessionAuthorityAddress( + args.fromAuthority, + transferArgs, + ), + }) + .instruction(); + } + + /** + * Creates a release_outbound instruction. The `payer` needs to sign the transaction. + */ + async createReleaseOutboundInstruction(args: { + payer: PublicKey; + outboxItem: PublicKey; + revertOnDelay: boolean; + }): Promise { + const whAccs = getWormholeDerivedAccounts( + this.program.programId, + this.wormholeId, + ); + return await this.program.methods + .releaseWormholeOutbound({ + revertOnDelay: args.revertOnDelay, + }) + .accounts({ + payer: args.payer, + config: { config: this.configAccountAddress() }, + outboxItem: args.outboxItem, + wormholeMessage: this.wormholeMessageAccountAddress(args.outboxItem), + emitter: whAccs.wormholeEmitter, + transceiver: this.registeredTransceiverAddress(this.program.programId), + wormhole: { + bridge: whAccs.wormholeBridge, + feeCollector: whAccs.wormholeFeeCollector, + sequence: whAccs.wormholeSequence, + program: this.wormholeId, + }, + }) + .instruction(); + } + + async createReleaseInboundMintInstruction(args: { + payer: PublicKey; + messageDigest: string; + revertOnDelay: boolean; + recipient: PublicKey; + config?: Config; + }): Promise { + const config = await this.getConfig(args.config); + const mint = await this.mintAccountAddress(config); + return await this.program.methods + .releaseInboundMint({ + revertOnDelay: args.revertOnDelay, + }) + .accounts({ + common: { + payer: args.payer, + config: { config: this.configAccountAddress() }, + inboxItem: this.inboxItemAccountAddress(args.messageDigest), + recipient: getAssociatedTokenAddressSync(mint, args.recipient), + mint, + tokenAuthority: this.tokenAuthorityAddress(), + }, + }) + .instruction(); + } + + async createReleaseInboundUnlockInstruction(args: { + payer: PublicKey; + messageDigest: string; + revertOnDelay: boolean; + recipient: PublicKey; + config?: Config; + }): Promise { + const config = await this.getConfig(args.config); + const mint = await this.mintAccountAddress(config); + return await this.program.methods + .releaseInboundUnlock({ + revertOnDelay: args.revertOnDelay, + }) + .accounts({ + common: { + payer: args.payer, + config: { config: this.configAccountAddress() }, + inboxItem: this.inboxItemAccountAddress(args.messageDigest), + recipient: getAssociatedTokenAddressSync(mint, args.recipient), + mint, + tokenAuthority: this.tokenAuthorityAddress(), + }, + custody: await this.custodyAccountAddress(config), + }) + .instruction(); + } + + async createReceiveWormholeMessageInstruction(args: { + payer: PublicKey; + vaa: SignedVaa; + config?: Config; + }): Promise { + const parsedVaa = parseVaa(args.vaa); + const { nttManagerPayload } = deserializePayload( + 'Ntt:WormholeTransfer', + parsedVaa.payload, + ); + const chainId = toChainId(parsedVaa.emitterChain as ChainId); + const transceiverPeer = this.transceiverPeerAccountAddress(chainId); + return await this.program.methods + .receiveWormholeMessage() + .accounts({ + payer: args.payer, + config: this.configAccountAddress(), + peer: transceiverPeer, + vaa: derivePostedVaaKey(this.wormholeId, parseVaa(args.vaa).hash), + transceiverMessage: this.transceiverMessageAccountAddress( + chainId, + Buffer.from(nttManagerPayload.id), + ), + }) + .instruction(); + } + + async createRedeemInstruction(args: { + payer: PublicKey; + vaa: SignedVaa; + config?: Config; + }): Promise { + const config = await this.getConfig(args.config); + const parsedVaa = parseVaa(args.vaa); + const { nttManagerPayload } = deserializePayload( + 'Ntt:WormholeTransfer', + parsedVaa.payload, + ); + const chainId = toChainId(parsedVaa.emitterChain as ChainId); + const messageDigest = Ntt.messageDigest( + SDKv2toChain(chainId), + nttManagerPayload, + ); + const nttManagerPeer = this.peerAccountAddress(chainId); + const inboxRateLimit = this.inboxRateLimitAccountAddress(chainId); + return await this.program.methods + .redeem({}) + .accounts({ + payer: args.payer, + config: this.configAccountAddress(), + peer: nttManagerPeer, + transceiverMessage: this.transceiverMessageAccountAddress( + chainId, + Buffer.from(nttManagerPayload.id), + ), + transceiver: this.registeredTransceiverAddress(this.program.programId), + mint: await this.mintAccountAddress(config), + inboxItem: this.inboxItemAccountAddress(hexlify(messageDigest)), + inboxRateLimit, + outboxRateLimit: this.outboxRateLimitAccountAddress(), + }) + .instruction(); + } + + // Account access + + /** + * Fetches the Config account from the contract. + * + * @param config If provided, the config is just returned without making a + * network request. This is handy in case multiple config + * accessor functions are used, the config can just be queried + * once and passed around. + */ + async getConfig(config?: Config): Promise { + return ( + config ?? + (await this.program.account.config.fetch(this.configAccountAddress())) + ); + } + + async isPaused(config?: Config): Promise { + return (await this.getConfig(config)).paused; + } + + async mintAccountAddress(config?: Config): Promise { + return (await this.getConfig(config)).mint; + } + + async tokenProgram(config?: Config): Promise { + return (await this.getConfig(config)).tokenProgram; + } + + async getInboxItem(messageDigest: string): Promise { + return await this.program.account.inboxItem.fetch( + this.inboxItemAccountAddress(messageDigest), + ); + } + + async getOutboxRateLimit(): Promise { + return await this.program.account.outboxRateLimit.fetch( + this.outboxRateLimitAccountAddress(), + ); + } + + async getInboxRateLimit(chain: ChainName | ChainId): Promise { + return await this.program.account.inboxRateLimit.fetch( + this.inboxRateLimitAccountAddress(chain), + ); + } + + /** + * Returns the address of the custody account. If the config is available + * (i.e. the program is initialized), the mint is derived from the config. + * Otherwise, the mint must be provided. + */ + async custodyAccountAddress( + configOrMint: Config | PublicKey, + ): Promise { + if (configOrMint instanceof PublicKey) { + return associatedAddress({ + mint: configOrMint, + owner: this.tokenAuthorityAddress(), + }); + } else { + return associatedAddress({ + mint: await this.mintAccountAddress(configOrMint), + owner: this.tokenAuthorityAddress(), + }); + } + } +} diff --git a/wormhole-connect/src/routes/ntt/chains/solana/nttQuoter.ts b/wormhole-connect/src/routes/ntt/chains/solana/nttQuoter.ts new file mode 100644 index 000000000..250117ee9 --- /dev/null +++ b/wormhole-connect/src/routes/ntt/chains/solana/nttQuoter.ts @@ -0,0 +1,162 @@ +import { ChainName, ChainId } from '@wormhole-foundation/wormhole-connect-sdk'; +import { + PublicKeyInitData, + PublicKey, + SystemProgram, + LAMPORTS_PER_SOL, + Connection, +} from '@solana/web3.js'; +import { BN, Program } from '@coral-xyz/anchor'; +import { NttQuoter as NttQuoterType, IDL } from './types/ntt_quoter'; +import { solanaContext, toChainId } from 'utils/sdk'; + +//constants that must match ntt-quoter lib.rs / implementation: +const EVM_GAS_COST = 250_000; // TODO: make sure this is right +const USD_UNIT = 1e6; +const WEI_PER_GWEI = 1e9; +const GWEI_PER_ETH = 1e9; +const SEED_PREFIX_INSTANCE = 'instance'; +const SEED_PREFIX_REGISTERED_CHAIN = 'registered_chain'; +const SEED_PREFIX_RELAY_REQUEST = 'relay_request'; + +const U64 = { + MAX: new BN((2n ** 64n - 1n).toString()), + to: (amount: number, unit: number) => { + const ret = new BN(Math.round(amount * unit)); + + if (ret.isNeg()) throw new Error('Value negative'); + + if (ret.bitLength() > 64) throw new Error('Value too large'); + + return ret; + }, + from: (amount: BN, unit: number) => amount.toNumber() / unit, +}; + +export class NttQuoter { + readonly connection: Connection; + readonly program: Program; + readonly instance: PublicKey; + + constructor(programId: PublicKeyInitData) { + const { connection } = solanaContext(); + if (!connection) throw new Error('Connection not found'); + this.connection = connection; + this.program = new Program(IDL, new PublicKey(programId), { + connection, + }); + this.instance = this.derivePda(Buffer.from(SEED_PREFIX_INSTANCE)); + } + + async isRelayEnabled(destChain: ChainName | ChainId) { + try { + const { paused } = await this.getRegisteredChain(destChain); + return !paused; + } catch (e: any) { + if (e.message?.includes('Account does not exist')) { + return false; + } + throw e; + } + } + + // TODO: will change with https://github.dev/wormhole-foundation/example-native-token-transfers/pull/319 + async calcRelayCost(chain: ChainName | ChainId) { + const [chainData, instanceData, rentCost] = await Promise.all([ + this.getRegisteredChain(chain), + this.getInstance(), + this.program.provider.connection.getMinimumBalanceForRentExemption( + this.program.account.relayRequest.size, + ), + ]); + + if (chainData.nativePriceUsd === 0) { + throw new Error('Native price is 0'); + } + if (instanceData.solPriceUsd === 0) { + throw new Error('SOL price is 0'); + } + + const totalNativeGasCostUsd = + chainData.nativePriceUsd * + ((chainData.gasPriceGwei * EVM_GAS_COST) / GWEI_PER_ETH); + + const totalCostSol = + rentCost / LAMPORTS_PER_SOL + + (chainData.basePriceUsd + totalNativeGasCostUsd) / + instanceData.solPriceUsd; + + // Add 5% to account for possible price updates while the tx is in flight + const cost = U64.to(totalCostSol * 1.05, LAMPORTS_PER_SOL); + return cost; + } + + async createRequestRelayInstruction( + payer: PublicKey, + outboxItem: PublicKey, + chain: ChainName | ChainId, + maxFee: BN, + ) { + return this.program.methods + .requestRelay({ + maxFee, + gasDropoff: new BN(0), + }) + .accounts({ + payer, + instance: this.instance, + registeredChain: this.registeredChainPda(toChainId(chain)), + outboxItem, + relayRequest: this.relayRequestPda(outboxItem), + systemProgram: SystemProgram.programId, + }) + .instruction(); + } + + async getInstance() { + const data = await this.program.account.instance.fetch(this.instance); + return { + owner: data.owner, + assistant: data.assistant, + feeRecipient: data.feeRecipient, + solPriceUsd: U64.from(data.solPrice, USD_UNIT), + }; + } + + async getRegisteredChain(chain: ChainName | ChainId) { + const data = await this.program.account.registeredChain.fetch( + this.registeredChainPda(toChainId(chain)), + ); + + return { + paused: data.basePrice.eq(U64.MAX), + maxGasDropoffEth: U64.from(data.maxGasDropoff, GWEI_PER_ETH), + basePriceUsd: U64.from(data.basePrice, USD_UNIT), + nativePriceUsd: U64.from(data.nativePrice, USD_UNIT), + gasPriceGwei: U64.from(data.gasPrice, WEI_PER_GWEI), + }; + } + + private registeredChainPda(chainId: number) { + return this.derivePda([ + Buffer.from(SEED_PREFIX_REGISTERED_CHAIN), + new BN(chainId).toBuffer('be', 2), + ]); + } + + private relayRequestPda(outboxItem: PublicKey) { + return this.derivePda([ + Buffer.from(SEED_PREFIX_RELAY_REQUEST), + outboxItem.toBytes(), + ]); + } + + private derivePda(seeds: Buffer | Array): PublicKey { + const seedsArray = seeds instanceof Buffer ? [seeds] : seeds; + const [address] = PublicKey.findProgramAddressSync( + seedsArray, + this.program.programId, + ); + return address; + } +} diff --git a/wormhole-connect/src/routes/ntt/chains/solana/types/example_native_token_transfers.ts b/wormhole-connect/src/routes/ntt/chains/solana/types/example_native_token_transfers.ts new file mode 100644 index 000000000..db8567df3 --- /dev/null +++ b/wormhole-connect/src/routes/ntt/chains/solana/types/example_native_token_transfers.ts @@ -0,0 +1,3341 @@ +export type ExampleNativeTokenTransfers = { + version: '0.1.0'; + name: 'example_native_token_transfers'; + instructions: [ + { + name: 'initialize'; + accounts: [ + { + name: 'payer'; + isMut: true; + isSigner: true; + }, + { + name: 'deployer'; + isMut: false; + isSigner: true; + }, + { + name: 'programData'; + isMut: false; + isSigner: false; + }, + { + name: 'config'; + isMut: true; + isSigner: false; + }, + { + name: 'mint'; + isMut: false; + isSigner: false; + }, + { + name: 'rateLimit'; + isMut: true; + isSigner: false; + }, + { + name: 'tokenAuthority'; + isMut: false; + isSigner: false; + }, + { + name: 'custody'; + isMut: true; + isSigner: false; + docs: [ + 'The custody account that holds tokens in locking mode.', + 'NOTE: the account is unconditionally initialized, but not used in', + 'burning mode.', + ]; + }, + { + name: 'tokenProgram'; + isMut: false; + isSigner: false; + docs: ['associated token account for the given mint.']; + }, + { + name: 'associatedTokenProgram'; + isMut: false; + isSigner: false; + }, + { + name: 'bpfLoaderUpgradeableProgram'; + isMut: false; + isSigner: false; + }, + { + name: 'systemProgram'; + isMut: false; + isSigner: false; + }, + ]; + args: [ + { + name: 'args'; + type: { + defined: 'InitializeArgs'; + }; + }, + ]; + }, + { + name: 'transferBurn'; + accounts: [ + { + name: 'common'; + accounts: [ + { + name: 'payer'; + isMut: true; + isSigner: true; + }, + { + name: 'config'; + accounts: [ + { + name: 'config'; + isMut: false; + isSigner: false; + }, + ]; + }, + { + name: 'mint'; + isMut: true; + isSigner: false; + }, + { + name: 'from'; + isMut: true; + isSigner: false; + docs: ['account can spend these tokens.']; + }, + { + name: 'tokenProgram'; + isMut: false; + isSigner: false; + }, + { + name: 'outboxItem'; + isMut: true; + isSigner: true; + }, + { + name: 'outboxRateLimit'; + isMut: true; + isSigner: false; + }, + { + name: 'systemProgram'; + isMut: false; + isSigner: false; + }, + ]; + }, + { + name: 'inboxRateLimit'; + isMut: true; + isSigner: false; + }, + { + name: 'peer'; + isMut: false; + isSigner: false; + }, + { + name: 'sessionAuthority'; + isMut: false; + isSigner: false; + }, + ]; + args: [ + { + name: 'args'; + type: { + defined: 'TransferArgs'; + }; + }, + ]; + }, + { + name: 'transferLock'; + accounts: [ + { + name: 'common'; + accounts: [ + { + name: 'payer'; + isMut: true; + isSigner: true; + }, + { + name: 'config'; + accounts: [ + { + name: 'config'; + isMut: false; + isSigner: false; + }, + ]; + }, + { + name: 'mint'; + isMut: true; + isSigner: false; + }, + { + name: 'from'; + isMut: true; + isSigner: false; + docs: ['account can spend these tokens.']; + }, + { + name: 'tokenProgram'; + isMut: false; + isSigner: false; + }, + { + name: 'outboxItem'; + isMut: true; + isSigner: true; + }, + { + name: 'outboxRateLimit'; + isMut: true; + isSigner: false; + }, + { + name: 'systemProgram'; + isMut: false; + isSigner: false; + }, + ]; + }, + { + name: 'inboxRateLimit'; + isMut: true; + isSigner: false; + }, + { + name: 'peer'; + isMut: false; + isSigner: false; + }, + { + name: 'sessionAuthority'; + isMut: false; + isSigner: false; + }, + { + name: 'custody'; + isMut: true; + isSigner: false; + }, + ]; + args: [ + { + name: 'args'; + type: { + defined: 'TransferArgs'; + }; + }, + ]; + }, + { + name: 'redeem'; + accounts: [ + { + name: 'payer'; + isMut: true; + isSigner: true; + }, + { + name: 'config'; + isMut: false; + isSigner: false; + }, + { + name: 'peer'; + isMut: false; + isSigner: false; + }, + { + name: 'transceiverMessage'; + isMut: false; + isSigner: false; + }, + { + name: 'transceiver'; + isMut: false; + isSigner: false; + }, + { + name: 'mint'; + isMut: false; + isSigner: false; + }, + { + name: 'inboxItem'; + isMut: true; + isSigner: false; + docs: [ + 'NOTE: This account is content-addressed (PDA seeded by the message hash).', + 'This is because in a multi-transceiver configuration, the different', + 'transceivers "vote" on messages (by delivering them). By making the inbox', + "items content-addressed, we can ensure that disagreeing votes don't", + 'interfere with each other.', + ]; + }, + { + name: 'inboxRateLimit'; + isMut: true; + isSigner: false; + }, + { + name: 'outboxRateLimit'; + isMut: true; + isSigner: false; + }, + { + name: 'systemProgram'; + isMut: false; + isSigner: false; + }, + ]; + args: [ + { + name: 'args'; + type: { + defined: 'RedeemArgs'; + }; + }, + ]; + }, + { + name: 'releaseInboundMint'; + accounts: [ + { + name: 'common'; + accounts: [ + { + name: 'payer'; + isMut: true; + isSigner: true; + }, + { + name: 'config'; + accounts: [ + { + name: 'config'; + isMut: false; + isSigner: false; + }, + ]; + }, + { + name: 'inboxItem'; + isMut: true; + isSigner: false; + }, + { + name: 'recipient'; + isMut: true; + isSigner: false; + }, + { + name: 'tokenAuthority'; + isMut: false; + isSigner: false; + }, + { + name: 'mint'; + isMut: true; + isSigner: false; + }, + { + name: 'tokenProgram'; + isMut: false; + isSigner: false; + }, + ]; + }, + ]; + args: [ + { + name: 'args'; + type: { + defined: 'ReleaseInboundArgs'; + }; + }, + ]; + }, + { + name: 'releaseInboundUnlock'; + accounts: [ + { + name: 'common'; + accounts: [ + { + name: 'payer'; + isMut: true; + isSigner: true; + }, + { + name: 'config'; + accounts: [ + { + name: 'config'; + isMut: false; + isSigner: false; + }, + ]; + }, + { + name: 'inboxItem'; + isMut: true; + isSigner: false; + }, + { + name: 'recipient'; + isMut: true; + isSigner: false; + }, + { + name: 'tokenAuthority'; + isMut: false; + isSigner: false; + }, + { + name: 'mint'; + isMut: true; + isSigner: false; + }, + { + name: 'tokenProgram'; + isMut: false; + isSigner: false; + }, + ]; + }, + { + name: 'custody'; + isMut: true; + isSigner: false; + }, + ]; + args: [ + { + name: 'args'; + type: { + defined: 'ReleaseInboundArgs'; + }; + }, + ]; + }, + { + name: 'transferOwnership'; + accounts: [ + { + name: 'config'; + isMut: true; + isSigner: false; + }, + { + name: 'owner'; + isMut: false; + isSigner: true; + }, + { + name: 'newOwner'; + isMut: false; + isSigner: false; + }, + { + name: 'upgradeLock'; + isMut: false; + isSigner: false; + }, + { + name: 'programData'; + isMut: true; + isSigner: false; + }, + { + name: 'bpfLoaderUpgradeableProgram'; + isMut: false; + isSigner: false; + }, + ]; + args: []; + }, + { + name: 'claimOwnership'; + accounts: [ + { + name: 'config'; + isMut: true; + isSigner: false; + }, + { + name: 'upgradeLock'; + isMut: false; + isSigner: false; + }, + { + name: 'newOwner'; + isMut: false; + isSigner: true; + }, + { + name: 'programData'; + isMut: true; + isSigner: false; + }, + { + name: 'bpfLoaderUpgradeableProgram'; + isMut: false; + isSigner: false; + }, + ]; + args: []; + }, + { + name: 'setPaused'; + accounts: [ + { + name: 'owner'; + isMut: false; + isSigner: true; + }, + { + name: 'config'; + isMut: true; + isSigner: false; + }, + ]; + args: [ + { + name: 'pause'; + type: 'bool'; + }, + ]; + }, + { + name: 'setPeer'; + accounts: [ + { + name: 'payer'; + isMut: true; + isSigner: true; + }, + { + name: 'owner'; + isMut: false; + isSigner: true; + }, + { + name: 'config'; + isMut: false; + isSigner: false; + }, + { + name: 'peer'; + isMut: true; + isSigner: false; + }, + { + name: 'inboxRateLimit'; + isMut: true; + isSigner: false; + }, + { + name: 'systemProgram'; + isMut: false; + isSigner: false; + }, + ]; + args: [ + { + name: 'args'; + type: { + defined: 'SetPeerArgs'; + }; + }, + ]; + }, + { + name: 'registerTransceiver'; + accounts: [ + { + name: 'config'; + isMut: true; + isSigner: false; + }, + { + name: 'owner'; + isMut: false; + isSigner: true; + }, + { + name: 'payer'; + isMut: true; + isSigner: true; + }, + { + name: 'transceiver'; + isMut: false; + isSigner: false; + }, + { + name: 'registeredTransceiver'; + isMut: true; + isSigner: false; + }, + { + name: 'systemProgram'; + isMut: false; + isSigner: false; + }, + ]; + args: []; + }, + { + name: 'setOutboundLimit'; + accounts: [ + { + name: 'config'; + isMut: false; + isSigner: false; + }, + { + name: 'owner'; + isMut: false; + isSigner: true; + }, + { + name: 'rateLimit'; + isMut: true; + isSigner: false; + }, + ]; + args: [ + { + name: 'args'; + type: { + defined: 'SetOutboundLimitArgs'; + }; + }, + ]; + }, + { + name: 'setInboundLimit'; + accounts: [ + { + name: 'config'; + isMut: false; + isSigner: false; + }, + { + name: 'owner'; + isMut: false; + isSigner: true; + }, + { + name: 'rateLimit'; + isMut: true; + isSigner: false; + }, + ]; + args: [ + { + name: 'args'; + type: { + defined: 'SetInboundLimitArgs'; + }; + }, + ]; + }, + { + name: 'setWormholePeer'; + accounts: [ + { + name: 'config'; + isMut: false; + isSigner: false; + }, + { + name: 'owner'; + isMut: false; + isSigner: true; + }, + { + name: 'payer'; + isMut: true; + isSigner: true; + }, + { + name: 'peer'; + isMut: true; + isSigner: false; + }, + { + name: 'systemProgram'; + isMut: false; + isSigner: false; + }, + ]; + args: [ + { + name: 'args'; + type: { + defined: 'SetTransceiverPeerArgs'; + }; + }, + ]; + }, + { + name: 'receiveWormholeMessage'; + accounts: [ + { + name: 'payer'; + isMut: true; + isSigner: true; + }, + { + name: 'config'; + isMut: false; + isSigner: false; + }, + { + name: 'peer'; + isMut: false; + isSigner: false; + }, + { + name: 'vaa'; + isMut: false; + isSigner: false; + }, + { + name: 'transceiverMessage'; + isMut: true; + isSigner: false; + }, + { + name: 'systemProgram'; + isMut: false; + isSigner: false; + }, + ]; + args: []; + }, + { + name: 'releaseWormholeOutbound'; + accounts: [ + { + name: 'payer'; + isMut: true; + isSigner: true; + }, + { + name: 'config'; + accounts: [ + { + name: 'config'; + isMut: false; + isSigner: false; + }, + ]; + }, + { + name: 'outboxItem'; + isMut: true; + isSigner: false; + }, + { + name: 'transceiver'; + isMut: false; + isSigner: false; + }, + { + name: 'wormholeMessage'; + isMut: true; + isSigner: false; + }, + { + name: 'emitter'; + isMut: true; + isSigner: false; + }, + { + name: 'wormhole'; + accounts: [ + { + name: 'bridge'; + isMut: true; + isSigner: false; + }, + { + name: 'feeCollector'; + isMut: true; + isSigner: false; + }, + { + name: 'sequence'; + isMut: true; + isSigner: false; + }, + { + name: 'program'; + isMut: false; + isSigner: false; + }, + { + name: 'systemProgram'; + isMut: false; + isSigner: false; + }, + { + name: 'clock'; + isMut: false; + isSigner: false; + }, + { + name: 'rent'; + isMut: false; + isSigner: false; + }, + ]; + }, + ]; + args: [ + { + name: 'args'; + type: { + defined: 'ReleaseOutboundArgs'; + }; + }, + ]; + }, + { + name: 'broadcastWormholeId'; + accounts: [ + { + name: 'payer'; + isMut: true; + isSigner: true; + }, + { + name: 'config'; + isMut: false; + isSigner: false; + }, + { + name: 'mint'; + isMut: false; + isSigner: false; + }, + { + name: 'wormholeMessage'; + isMut: true; + isSigner: true; + }, + { + name: 'emitter'; + isMut: true; + isSigner: false; + }, + { + name: 'wormhole'; + accounts: [ + { + name: 'bridge'; + isMut: true; + isSigner: false; + }, + { + name: 'feeCollector'; + isMut: true; + isSigner: false; + }, + { + name: 'sequence'; + isMut: true; + isSigner: false; + }, + { + name: 'program'; + isMut: false; + isSigner: false; + }, + { + name: 'systemProgram'; + isMut: false; + isSigner: false; + }, + { + name: 'clock'; + isMut: false; + isSigner: false; + }, + { + name: 'rent'; + isMut: false; + isSigner: false; + }, + ]; + }, + ]; + args: []; + }, + { + name: 'broadcastWormholePeer'; + accounts: [ + { + name: 'payer'; + isMut: true; + isSigner: true; + }, + { + name: 'config'; + isMut: false; + isSigner: false; + }, + { + name: 'peer'; + isMut: false; + isSigner: false; + }, + { + name: 'wormholeMessage'; + isMut: true; + isSigner: true; + }, + { + name: 'emitter'; + isMut: true; + isSigner: false; + }, + { + name: 'wormhole'; + accounts: [ + { + name: 'bridge'; + isMut: true; + isSigner: false; + }, + { + name: 'feeCollector'; + isMut: true; + isSigner: false; + }, + { + name: 'sequence'; + isMut: true; + isSigner: false; + }, + { + name: 'program'; + isMut: false; + isSigner: false; + }, + { + name: 'systemProgram'; + isMut: false; + isSigner: false; + }, + { + name: 'clock'; + isMut: false; + isSigner: false; + }, + { + name: 'rent'; + isMut: false; + isSigner: false; + }, + ]; + }, + ]; + args: [ + { + name: 'args'; + type: { + defined: 'BroadcastPeerArgs'; + }; + }, + ]; + }, + ]; + accounts: [ + { + name: 'config'; + type: { + kind: 'struct'; + fields: [ + { + name: 'bump'; + type: 'u8'; + }, + { + name: 'owner'; + docs: ['Owner of the program.']; + type: 'publicKey'; + }, + { + name: 'pendingOwner'; + docs: ['Pending next owner (before claiming ownership).']; + type: { + option: 'publicKey'; + }; + }, + { + name: 'mint'; + docs: ['Mint address of the token managed by this program.']; + type: 'publicKey'; + }, + { + name: 'tokenProgram'; + docs: [ + 'Address of the token program (token or token22). This could always be queried', + "from the [`mint`] account's owner, but storing it here avoids an indirection", + 'on the client side.', + ]; + type: 'publicKey'; + }, + { + name: 'mode'; + docs: [ + 'The mode that this program is running in. This is used to determine', + 'whether the program is burning tokens or locking tokens.', + ]; + type: { + defined: 'Mode'; + }; + }, + { + name: 'chainId'; + docs: [ + "The chain id of the chain that this program is running on. We don't", + 'hardcode this so that the program is deployable on any potential SVM', + 'forks.', + ]; + type: { + defined: 'ChainId'; + }; + }, + { + name: 'nextTransceiverId'; + docs: [ + 'The next transceiver id to use when registering an transceiver.', + ]; + type: 'u8'; + }, + { + name: 'threshold'; + docs: [ + 'The number of transceivers that must attest to a transfer before it is', + 'accepted.', + ]; + type: 'u8'; + }, + { + name: 'enabledTransceivers'; + docs: ['Bitmap of enabled transceivers']; + type: { + defined: 'Bitmap'; + }; + }, + { + name: 'paused'; + docs: [ + 'Pause the program. This is useful for upgrades and other maintenance.', + ]; + type: 'bool'; + }, + { + name: 'custody'; + docs: ['The custody account that holds tokens in locking mode.']; + type: 'publicKey'; + }, + ]; + }; + }, + { + name: 'nttManagerPeer'; + docs: [ + 'A peer on another chain. Stored in a PDA seeded by the chain id.', + ]; + type: { + kind: 'struct'; + fields: [ + { + name: 'bump'; + type: 'u8'; + }, + { + name: 'address'; + type: { + array: ['u8', 32]; + }; + }, + { + name: 'tokenDecimals'; + type: 'u8'; + }, + ]; + }; + }, + { + name: 'inboxItem'; + type: { + kind: 'struct'; + fields: [ + { + name: 'init'; + type: 'bool'; + }, + { + name: 'bump'; + type: 'u8'; + }, + { + name: 'amount'; + type: 'u64'; + }, + { + name: 'recipientAddress'; + type: 'publicKey'; + }, + { + name: 'votes'; + type: { + defined: 'Bitmap'; + }; + }, + { + name: 'releaseStatus'; + type: { + defined: 'ReleaseStatus'; + }; + }, + ]; + }; + }, + { + name: 'inboxRateLimit'; + docs: [ + 'Inbound rate limit per chain.', + 'SECURITY: must check the PDA (since there are multiple PDAs, namely one for each chain.)', + ]; + type: { + kind: 'struct'; + fields: [ + { + name: 'bump'; + type: 'u8'; + }, + { + name: 'rateLimit'; + type: { + defined: 'RateLimitState'; + }; + }, + ]; + }; + }, + { + name: 'outboxItem'; + type: { + kind: 'struct'; + fields: [ + { + name: 'amount'; + type: { + defined: 'TrimmedAmount'; + }; + }, + { + name: 'sender'; + type: 'publicKey'; + }, + { + name: 'recipientChain'; + type: { + defined: 'ChainId'; + }; + }, + { + name: 'recipientNttManager'; + type: { + array: ['u8', 32]; + }; + }, + { + name: 'recipientAddress'; + type: { + array: ['u8', 32]; + }; + }, + { + name: 'releaseTimestamp'; + type: 'i64'; + }, + { + name: 'released'; + type: { + defined: 'Bitmap'; + }; + }, + ]; + }; + }, + { + name: 'outboxRateLimit'; + type: { + kind: 'struct'; + fields: [ + { + name: 'rateLimit'; + type: { + defined: 'RateLimitState'; + }; + }, + ]; + }; + }, + { + name: 'registeredTransceiver'; + type: { + kind: 'struct'; + fields: [ + { + name: 'bump'; + type: 'u8'; + }, + { + name: 'id'; + type: 'u8'; + }, + { + name: 'transceiverAddress'; + type: 'publicKey'; + }, + ]; + }; + }, + { + name: 'transceiverPeer'; + docs: [ + 'A peer on another chain. Stored in a PDA seeded by the chain id.', + ]; + type: { + kind: 'struct'; + fields: [ + { + name: 'bump'; + type: 'u8'; + }, + { + name: 'address'; + type: { + array: ['u8', 32]; + }; + }, + ]; + }; + }, + { + name: 'bridgeData'; + type: { + kind: 'struct'; + fields: [ + { + name: 'guardianSetIndex'; + docs: [ + 'The current guardian set index, used to decide which signature sets to accept.', + ]; + type: 'u32'; + }, + { + name: 'lastLamports'; + docs: ['Lamports in the collection account']; + type: 'u64'; + }, + { + name: 'config'; + docs: [ + 'Bridge configuration, which is set once upon initialization.', + ]; + type: { + defined: 'BridgeConfig'; + }; + }, + ]; + }; + }, + ]; + types: [ + { + name: 'Bitmap'; + type: { + kind: 'struct'; + fields: [ + { + name: 'map'; + type: 'u128'; + }, + ]; + }; + }, + { + name: 'SetInboundLimitArgs'; + type: { + kind: 'struct'; + fields: [ + { + name: 'limit'; + type: 'u64'; + }, + { + name: 'chainId'; + type: { + defined: 'ChainId'; + }; + }, + ]; + }; + }, + { + name: 'SetOutboundLimitArgs'; + type: { + kind: 'struct'; + fields: [ + { + name: 'limit'; + type: 'u64'; + }, + ]; + }; + }, + { + name: 'SetPeerArgs'; + type: { + kind: 'struct'; + fields: [ + { + name: 'chainId'; + type: { + defined: 'ChainId'; + }; + }, + { + name: 'address'; + type: { + array: ['u8', 32]; + }; + }, + { + name: 'limit'; + type: 'u64'; + }, + { + name: 'tokenDecimals'; + docs: ['The token decimals on the peer chain.']; + type: 'u8'; + }, + ]; + }; + }, + { + name: 'InitializeArgs'; + type: { + kind: 'struct'; + fields: [ + { + name: 'chainId'; + type: 'u16'; + }, + { + name: 'limit'; + type: 'u64'; + }, + { + name: 'mode'; + type: { + defined: 'Mode'; + }; + }, + ]; + }; + }, + { + name: 'RedeemArgs'; + type: { + kind: 'struct'; + fields: []; + }; + }, + { + name: 'ReleaseInboundArgs'; + type: { + kind: 'struct'; + fields: [ + { + name: 'revertOnDelay'; + type: 'bool'; + }, + ]; + }; + }, + { + name: 'TransferArgs'; + type: { + kind: 'struct'; + fields: [ + { + name: 'amount'; + type: 'u64'; + }, + { + name: 'recipientChain'; + type: { + defined: 'ChainId'; + }; + }, + { + name: 'recipientAddress'; + type: { + array: ['u8', 32]; + }; + }, + { + name: 'shouldQueue'; + type: 'bool'; + }, + ]; + }; + }, + { + name: 'ReleaseStatus'; + type: { + kind: 'enum'; + variants: [ + { + name: 'NotApproved'; + }, + { + name: 'ReleaseAfter'; + fields: ['i64']; + }, + { + name: 'Released'; + }, + ]; + }; + }, + { + name: 'RateLimitState'; + type: { + kind: 'struct'; + fields: [ + { + name: 'limit'; + docs: ['The maximum capacity of the rate limiter.']; + type: 'u64'; + }, + { + name: 'capacityAtLastTx'; + docs: [ + 'The capacity of the rate limiter at `last_tx_timestamp`.', + 'The actual current capacity is calculated in `capacity_at`, by', + 'accounting for the time that has passed since `last_tx_timestamp` and', + 'the refill rate.', + ]; + type: 'u64'; + }, + { + name: 'lastTxTimestamp'; + docs: [ + 'The timestamp of the last transaction that counted towards the current', + 'capacity. Transactions that exceeded the capacity do not count, they are', + 'just delayed.', + ]; + type: 'i64'; + }, + ]; + }; + }, + { + name: 'SetTransceiverPeerArgs'; + type: { + kind: 'struct'; + fields: [ + { + name: 'chainId'; + type: { + defined: 'ChainId'; + }; + }, + { + name: 'address'; + type: { + array: ['u8', 32]; + }; + }, + ]; + }; + }, + { + name: 'BroadcastPeerArgs'; + type: { + kind: 'struct'; + fields: [ + { + name: 'chainId'; + type: 'u16'; + }, + ]; + }; + }, + { + name: 'ReleaseOutboundArgs'; + type: { + kind: 'struct'; + fields: [ + { + name: 'revertOnDelay'; + type: 'bool'; + }, + ]; + }; + }, + { + name: 'ChainId'; + type: { + kind: 'struct'; + fields: [ + { + name: 'id'; + type: 'u16'; + }, + ]; + }; + }, + { + name: 'Mode'; + type: { + kind: 'enum'; + variants: [ + { + name: 'Locking'; + }, + { + name: 'Burning'; + }, + ]; + }; + }, + { + name: 'TrimmedAmount'; + type: { + kind: 'struct'; + fields: [ + { + name: 'amount'; + type: 'u64'; + }, + { + name: 'decimals'; + type: 'u8'; + }, + ]; + }; + }, + { + name: 'BridgeConfig'; + type: { + kind: 'struct'; + fields: [ + { + name: 'guardianSetExpirationTime'; + docs: [ + 'Period for how long a guardian set is valid after it has been replaced by a new one. This', + 'guarantees that VAAs issued by that set can still be submitted for a certain period. In', + 'this period we still trust the old guardian set.', + ]; + type: 'u32'; + }, + { + name: 'fee'; + docs: [ + 'Amount of lamports that needs to be paid to the protocol to post a message', + ]; + type: 'u64'; + }, + ]; + }; + }, + ]; + errors: [ + { + code: 6000; + name: 'CantReleaseYet'; + msg: 'CantReleaseYet'; + }, + { + code: 6001; + name: 'InvalidPendingOwner'; + msg: 'InvalidPendingOwner'; + }, + { + code: 6002; + name: 'InvalidChainId'; + msg: 'InvalidChainId'; + }, + { + code: 6003; + name: 'InvalidRecipientAddress'; + msg: 'InvalidRecipientAddress'; + }, + { + code: 6004; + name: 'InvalidTransceiverPeer'; + msg: 'InvalidTransceiverPeer'; + }, + { + code: 6005; + name: 'InvalidNttManagerPeer'; + msg: 'InvalidNttManagerPeer'; + }, + { + code: 6006; + name: 'InvalidRecipientNttManager'; + msg: 'InvalidRecipientNttManager'; + }, + { + code: 6007; + name: 'TransferAlreadyRedeemed'; + msg: 'TransferAlreadyRedeemed'; + }, + { + code: 6008; + name: 'TransferCannotBeRedeemed'; + msg: 'TransferCannotBeRedeemed'; + }, + { + code: 6009; + name: 'TransferNotApproved'; + msg: 'TransferNotApproved'; + }, + { + code: 6010; + name: 'MessageAlreadySent'; + msg: 'MessageAlreadySent'; + }, + { + code: 6011; + name: 'InvalidMode'; + msg: 'InvalidMode'; + }, + { + code: 6012; + name: 'InvalidMintAuthority'; + msg: 'InvalidMintAuthority'; + }, + { + code: 6013; + name: 'TransferExceedsRateLimit'; + msg: 'TransferExceedsRateLimit'; + }, + { + code: 6014; + name: 'Paused'; + msg: 'Paused'; + }, + { + code: 6015; + name: 'DisabledTransceiver'; + msg: 'DisabledTransceiver'; + }, + { + code: 6016; + name: 'InvalidDeployer'; + msg: 'InvalidDeployer'; + }, + ]; +}; + +export const IDL: ExampleNativeTokenTransfers = { + version: '0.1.0', + name: 'example_native_token_transfers', + instructions: [ + { + name: 'initialize', + accounts: [ + { + name: 'payer', + isMut: true, + isSigner: true, + }, + { + name: 'deployer', + isMut: false, + isSigner: true, + }, + { + name: 'programData', + isMut: false, + isSigner: false, + }, + { + name: 'config', + isMut: true, + isSigner: false, + }, + { + name: 'mint', + isMut: false, + isSigner: false, + }, + { + name: 'rateLimit', + isMut: true, + isSigner: false, + }, + { + name: 'tokenAuthority', + isMut: false, + isSigner: false, + }, + { + name: 'custody', + isMut: true, + isSigner: false, + docs: [ + 'The custody account that holds tokens in locking mode.', + 'NOTE: the account is unconditionally initialized, but not used in', + 'burning mode.', + ], + }, + { + name: 'tokenProgram', + isMut: false, + isSigner: false, + docs: ['associated token account for the given mint.'], + }, + { + name: 'associatedTokenProgram', + isMut: false, + isSigner: false, + }, + { + name: 'bpfLoaderUpgradeableProgram', + isMut: false, + isSigner: false, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'args', + type: { + defined: 'InitializeArgs', + }, + }, + ], + }, + { + name: 'transferBurn', + accounts: [ + { + name: 'common', + accounts: [ + { + name: 'payer', + isMut: true, + isSigner: true, + }, + { + name: 'config', + accounts: [ + { + name: 'config', + isMut: false, + isSigner: false, + }, + ], + }, + { + name: 'mint', + isMut: true, + isSigner: false, + }, + { + name: 'from', + isMut: true, + isSigner: false, + docs: ['account can spend these tokens.'], + }, + { + name: 'tokenProgram', + isMut: false, + isSigner: false, + }, + { + name: 'outboxItem', + isMut: true, + isSigner: true, + }, + { + name: 'outboxRateLimit', + isMut: true, + isSigner: false, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + ], + }, + { + name: 'inboxRateLimit', + isMut: true, + isSigner: false, + }, + { + name: 'peer', + isMut: false, + isSigner: false, + }, + { + name: 'sessionAuthority', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'args', + type: { + defined: 'TransferArgs', + }, + }, + ], + }, + { + name: 'transferLock', + accounts: [ + { + name: 'common', + accounts: [ + { + name: 'payer', + isMut: true, + isSigner: true, + }, + { + name: 'config', + accounts: [ + { + name: 'config', + isMut: false, + isSigner: false, + }, + ], + }, + { + name: 'mint', + isMut: true, + isSigner: false, + }, + { + name: 'from', + isMut: true, + isSigner: false, + docs: ['account can spend these tokens.'], + }, + { + name: 'tokenProgram', + isMut: false, + isSigner: false, + }, + { + name: 'outboxItem', + isMut: true, + isSigner: true, + }, + { + name: 'outboxRateLimit', + isMut: true, + isSigner: false, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + ], + }, + { + name: 'inboxRateLimit', + isMut: true, + isSigner: false, + }, + { + name: 'peer', + isMut: false, + isSigner: false, + }, + { + name: 'sessionAuthority', + isMut: false, + isSigner: false, + }, + { + name: 'custody', + isMut: true, + isSigner: false, + }, + ], + args: [ + { + name: 'args', + type: { + defined: 'TransferArgs', + }, + }, + ], + }, + { + name: 'redeem', + accounts: [ + { + name: 'payer', + isMut: true, + isSigner: true, + }, + { + name: 'config', + isMut: false, + isSigner: false, + }, + { + name: 'peer', + isMut: false, + isSigner: false, + }, + { + name: 'transceiverMessage', + isMut: false, + isSigner: false, + }, + { + name: 'transceiver', + isMut: false, + isSigner: false, + }, + { + name: 'mint', + isMut: false, + isSigner: false, + }, + { + name: 'inboxItem', + isMut: true, + isSigner: false, + docs: [ + 'NOTE: This account is content-addressed (PDA seeded by the message hash).', + 'This is because in a multi-transceiver configuration, the different', + 'transceivers "vote" on messages (by delivering them). By making the inbox', + "items content-addressed, we can ensure that disagreeing votes don't", + 'interfere with each other.', + ], + }, + { + name: 'inboxRateLimit', + isMut: true, + isSigner: false, + }, + { + name: 'outboxRateLimit', + isMut: true, + isSigner: false, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'args', + type: { + defined: 'RedeemArgs', + }, + }, + ], + }, + { + name: 'releaseInboundMint', + accounts: [ + { + name: 'common', + accounts: [ + { + name: 'payer', + isMut: true, + isSigner: true, + }, + { + name: 'config', + accounts: [ + { + name: 'config', + isMut: false, + isSigner: false, + }, + ], + }, + { + name: 'inboxItem', + isMut: true, + isSigner: false, + }, + { + name: 'recipient', + isMut: true, + isSigner: false, + }, + { + name: 'tokenAuthority', + isMut: false, + isSigner: false, + }, + { + name: 'mint', + isMut: true, + isSigner: false, + }, + { + name: 'tokenProgram', + isMut: false, + isSigner: false, + }, + ], + }, + ], + args: [ + { + name: 'args', + type: { + defined: 'ReleaseInboundArgs', + }, + }, + ], + }, + { + name: 'releaseInboundUnlock', + accounts: [ + { + name: 'common', + accounts: [ + { + name: 'payer', + isMut: true, + isSigner: true, + }, + { + name: 'config', + accounts: [ + { + name: 'config', + isMut: false, + isSigner: false, + }, + ], + }, + { + name: 'inboxItem', + isMut: true, + isSigner: false, + }, + { + name: 'recipient', + isMut: true, + isSigner: false, + }, + { + name: 'tokenAuthority', + isMut: false, + isSigner: false, + }, + { + name: 'mint', + isMut: true, + isSigner: false, + }, + { + name: 'tokenProgram', + isMut: false, + isSigner: false, + }, + ], + }, + { + name: 'custody', + isMut: true, + isSigner: false, + }, + ], + args: [ + { + name: 'args', + type: { + defined: 'ReleaseInboundArgs', + }, + }, + ], + }, + { + name: 'transferOwnership', + accounts: [ + { + name: 'config', + isMut: true, + isSigner: false, + }, + { + name: 'owner', + isMut: false, + isSigner: true, + }, + { + name: 'newOwner', + isMut: false, + isSigner: false, + }, + { + name: 'upgradeLock', + isMut: false, + isSigner: false, + }, + { + name: 'programData', + isMut: true, + isSigner: false, + }, + { + name: 'bpfLoaderUpgradeableProgram', + isMut: false, + isSigner: false, + }, + ], + args: [], + }, + { + name: 'claimOwnership', + accounts: [ + { + name: 'config', + isMut: true, + isSigner: false, + }, + { + name: 'upgradeLock', + isMut: false, + isSigner: false, + }, + { + name: 'newOwner', + isMut: false, + isSigner: true, + }, + { + name: 'programData', + isMut: true, + isSigner: false, + }, + { + name: 'bpfLoaderUpgradeableProgram', + isMut: false, + isSigner: false, + }, + ], + args: [], + }, + { + name: 'setPaused', + accounts: [ + { + name: 'owner', + isMut: false, + isSigner: true, + }, + { + name: 'config', + isMut: true, + isSigner: false, + }, + ], + args: [ + { + name: 'pause', + type: 'bool', + }, + ], + }, + { + name: 'setPeer', + accounts: [ + { + name: 'payer', + isMut: true, + isSigner: true, + }, + { + name: 'owner', + isMut: false, + isSigner: true, + }, + { + name: 'config', + isMut: false, + isSigner: false, + }, + { + name: 'peer', + isMut: true, + isSigner: false, + }, + { + name: 'inboxRateLimit', + isMut: true, + isSigner: false, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'args', + type: { + defined: 'SetPeerArgs', + }, + }, + ], + }, + { + name: 'registerTransceiver', + accounts: [ + { + name: 'config', + isMut: true, + isSigner: false, + }, + { + name: 'owner', + isMut: false, + isSigner: true, + }, + { + name: 'payer', + isMut: true, + isSigner: true, + }, + { + name: 'transceiver', + isMut: false, + isSigner: false, + }, + { + name: 'registeredTransceiver', + isMut: true, + isSigner: false, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + ], + args: [], + }, + { + name: 'setOutboundLimit', + accounts: [ + { + name: 'config', + isMut: false, + isSigner: false, + }, + { + name: 'owner', + isMut: false, + isSigner: true, + }, + { + name: 'rateLimit', + isMut: true, + isSigner: false, + }, + ], + args: [ + { + name: 'args', + type: { + defined: 'SetOutboundLimitArgs', + }, + }, + ], + }, + { + name: 'setInboundLimit', + accounts: [ + { + name: 'config', + isMut: false, + isSigner: false, + }, + { + name: 'owner', + isMut: false, + isSigner: true, + }, + { + name: 'rateLimit', + isMut: true, + isSigner: false, + }, + ], + args: [ + { + name: 'args', + type: { + defined: 'SetInboundLimitArgs', + }, + }, + ], + }, + { + name: 'setWormholePeer', + accounts: [ + { + name: 'config', + isMut: false, + isSigner: false, + }, + { + name: 'owner', + isMut: false, + isSigner: true, + }, + { + name: 'payer', + isMut: true, + isSigner: true, + }, + { + name: 'peer', + isMut: true, + isSigner: false, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'args', + type: { + defined: 'SetTransceiverPeerArgs', + }, + }, + ], + }, + { + name: 'receiveWormholeMessage', + accounts: [ + { + name: 'payer', + isMut: true, + isSigner: true, + }, + { + name: 'config', + isMut: false, + isSigner: false, + }, + { + name: 'peer', + isMut: false, + isSigner: false, + }, + { + name: 'vaa', + isMut: false, + isSigner: false, + }, + { + name: 'transceiverMessage', + isMut: true, + isSigner: false, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + ], + args: [], + }, + { + name: 'releaseWormholeOutbound', + accounts: [ + { + name: 'payer', + isMut: true, + isSigner: true, + }, + { + name: 'config', + accounts: [ + { + name: 'config', + isMut: false, + isSigner: false, + }, + ], + }, + { + name: 'outboxItem', + isMut: true, + isSigner: false, + }, + { + name: 'transceiver', + isMut: false, + isSigner: false, + }, + { + name: 'wormholeMessage', + isMut: true, + isSigner: false, + }, + { + name: 'emitter', + isMut: true, + isSigner: false, + }, + { + name: 'wormhole', + accounts: [ + { + name: 'bridge', + isMut: true, + isSigner: false, + }, + { + name: 'feeCollector', + isMut: true, + isSigner: false, + }, + { + name: 'sequence', + isMut: true, + isSigner: false, + }, + { + name: 'program', + isMut: false, + isSigner: false, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + { + name: 'clock', + isMut: false, + isSigner: false, + }, + { + name: 'rent', + isMut: false, + isSigner: false, + }, + ], + }, + ], + args: [ + { + name: 'args', + type: { + defined: 'ReleaseOutboundArgs', + }, + }, + ], + }, + { + name: 'broadcastWormholeId', + accounts: [ + { + name: 'payer', + isMut: true, + isSigner: true, + }, + { + name: 'config', + isMut: false, + isSigner: false, + }, + { + name: 'mint', + isMut: false, + isSigner: false, + }, + { + name: 'wormholeMessage', + isMut: true, + isSigner: true, + }, + { + name: 'emitter', + isMut: true, + isSigner: false, + }, + { + name: 'wormhole', + accounts: [ + { + name: 'bridge', + isMut: true, + isSigner: false, + }, + { + name: 'feeCollector', + isMut: true, + isSigner: false, + }, + { + name: 'sequence', + isMut: true, + isSigner: false, + }, + { + name: 'program', + isMut: false, + isSigner: false, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + { + name: 'clock', + isMut: false, + isSigner: false, + }, + { + name: 'rent', + isMut: false, + isSigner: false, + }, + ], + }, + ], + args: [], + }, + { + name: 'broadcastWormholePeer', + accounts: [ + { + name: 'payer', + isMut: true, + isSigner: true, + }, + { + name: 'config', + isMut: false, + isSigner: false, + }, + { + name: 'peer', + isMut: false, + isSigner: false, + }, + { + name: 'wormholeMessage', + isMut: true, + isSigner: true, + }, + { + name: 'emitter', + isMut: true, + isSigner: false, + }, + { + name: 'wormhole', + accounts: [ + { + name: 'bridge', + isMut: true, + isSigner: false, + }, + { + name: 'feeCollector', + isMut: true, + isSigner: false, + }, + { + name: 'sequence', + isMut: true, + isSigner: false, + }, + { + name: 'program', + isMut: false, + isSigner: false, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + { + name: 'clock', + isMut: false, + isSigner: false, + }, + { + name: 'rent', + isMut: false, + isSigner: false, + }, + ], + }, + ], + args: [ + { + name: 'args', + type: { + defined: 'BroadcastPeerArgs', + }, + }, + ], + }, + ], + accounts: [ + { + name: 'config', + type: { + kind: 'struct', + fields: [ + { + name: 'bump', + type: 'u8', + }, + { + name: 'owner', + docs: ['Owner of the program.'], + type: 'publicKey', + }, + { + name: 'pendingOwner', + docs: ['Pending next owner (before claiming ownership).'], + type: { + option: 'publicKey', + }, + }, + { + name: 'mint', + docs: ['Mint address of the token managed by this program.'], + type: 'publicKey', + }, + { + name: 'tokenProgram', + docs: [ + 'Address of the token program (token or token22). This could always be queried', + "from the [`mint`] account's owner, but storing it here avoids an indirection", + 'on the client side.', + ], + type: 'publicKey', + }, + { + name: 'mode', + docs: [ + 'The mode that this program is running in. This is used to determine', + 'whether the program is burning tokens or locking tokens.', + ], + type: { + defined: 'Mode', + }, + }, + { + name: 'chainId', + docs: [ + "The chain id of the chain that this program is running on. We don't", + 'hardcode this so that the program is deployable on any potential SVM', + 'forks.', + ], + type: { + defined: 'ChainId', + }, + }, + { + name: 'nextTransceiverId', + docs: [ + 'The next transceiver id to use when registering an transceiver.', + ], + type: 'u8', + }, + { + name: 'threshold', + docs: [ + 'The number of transceivers that must attest to a transfer before it is', + 'accepted.', + ], + type: 'u8', + }, + { + name: 'enabledTransceivers', + docs: ['Bitmap of enabled transceivers'], + type: { + defined: 'Bitmap', + }, + }, + { + name: 'paused', + docs: [ + 'Pause the program. This is useful for upgrades and other maintenance.', + ], + type: 'bool', + }, + { + name: 'custody', + docs: ['The custody account that holds tokens in locking mode.'], + type: 'publicKey', + }, + ], + }, + }, + { + name: 'nttManagerPeer', + docs: [ + 'A peer on another chain. Stored in a PDA seeded by the chain id.', + ], + type: { + kind: 'struct', + fields: [ + { + name: 'bump', + type: 'u8', + }, + { + name: 'address', + type: { + array: ['u8', 32], + }, + }, + { + name: 'tokenDecimals', + type: 'u8', + }, + ], + }, + }, + { + name: 'inboxItem', + type: { + kind: 'struct', + fields: [ + { + name: 'init', + type: 'bool', + }, + { + name: 'bump', + type: 'u8', + }, + { + name: 'amount', + type: 'u64', + }, + { + name: 'recipientAddress', + type: 'publicKey', + }, + { + name: 'votes', + type: { + defined: 'Bitmap', + }, + }, + { + name: 'releaseStatus', + type: { + defined: 'ReleaseStatus', + }, + }, + ], + }, + }, + { + name: 'inboxRateLimit', + docs: [ + 'Inbound rate limit per chain.', + 'SECURITY: must check the PDA (since there are multiple PDAs, namely one for each chain.)', + ], + type: { + kind: 'struct', + fields: [ + { + name: 'bump', + type: 'u8', + }, + { + name: 'rateLimit', + type: { + defined: 'RateLimitState', + }, + }, + ], + }, + }, + { + name: 'outboxItem', + type: { + kind: 'struct', + fields: [ + { + name: 'amount', + type: { + defined: 'TrimmedAmount', + }, + }, + { + name: 'sender', + type: 'publicKey', + }, + { + name: 'recipientChain', + type: { + defined: 'ChainId', + }, + }, + { + name: 'recipientNttManager', + type: { + array: ['u8', 32], + }, + }, + { + name: 'recipientAddress', + type: { + array: ['u8', 32], + }, + }, + { + name: 'releaseTimestamp', + type: 'i64', + }, + { + name: 'released', + type: { + defined: 'Bitmap', + }, + }, + ], + }, + }, + { + name: 'outboxRateLimit', + type: { + kind: 'struct', + fields: [ + { + name: 'rateLimit', + type: { + defined: 'RateLimitState', + }, + }, + ], + }, + }, + { + name: 'registeredTransceiver', + type: { + kind: 'struct', + fields: [ + { + name: 'bump', + type: 'u8', + }, + { + name: 'id', + type: 'u8', + }, + { + name: 'transceiverAddress', + type: 'publicKey', + }, + ], + }, + }, + { + name: 'transceiverPeer', + docs: [ + 'A peer on another chain. Stored in a PDA seeded by the chain id.', + ], + type: { + kind: 'struct', + fields: [ + { + name: 'bump', + type: 'u8', + }, + { + name: 'address', + type: { + array: ['u8', 32], + }, + }, + ], + }, + }, + { + name: 'bridgeData', + type: { + kind: 'struct', + fields: [ + { + name: 'guardianSetIndex', + docs: [ + 'The current guardian set index, used to decide which signature sets to accept.', + ], + type: 'u32', + }, + { + name: 'lastLamports', + docs: ['Lamports in the collection account'], + type: 'u64', + }, + { + name: 'config', + docs: [ + 'Bridge configuration, which is set once upon initialization.', + ], + type: { + defined: 'BridgeConfig', + }, + }, + ], + }, + }, + ], + types: [ + { + name: 'Bitmap', + type: { + kind: 'struct', + fields: [ + { + name: 'map', + type: 'u128', + }, + ], + }, + }, + { + name: 'SetInboundLimitArgs', + type: { + kind: 'struct', + fields: [ + { + name: 'limit', + type: 'u64', + }, + { + name: 'chainId', + type: { + defined: 'ChainId', + }, + }, + ], + }, + }, + { + name: 'SetOutboundLimitArgs', + type: { + kind: 'struct', + fields: [ + { + name: 'limit', + type: 'u64', + }, + ], + }, + }, + { + name: 'SetPeerArgs', + type: { + kind: 'struct', + fields: [ + { + name: 'chainId', + type: { + defined: 'ChainId', + }, + }, + { + name: 'address', + type: { + array: ['u8', 32], + }, + }, + { + name: 'limit', + type: 'u64', + }, + { + name: 'tokenDecimals', + docs: ['The token decimals on the peer chain.'], + type: 'u8', + }, + ], + }, + }, + { + name: 'InitializeArgs', + type: { + kind: 'struct', + fields: [ + { + name: 'chainId', + type: 'u16', + }, + { + name: 'limit', + type: 'u64', + }, + { + name: 'mode', + type: { + defined: 'Mode', + }, + }, + ], + }, + }, + { + name: 'RedeemArgs', + type: { + kind: 'struct', + fields: [], + }, + }, + { + name: 'ReleaseInboundArgs', + type: { + kind: 'struct', + fields: [ + { + name: 'revertOnDelay', + type: 'bool', + }, + ], + }, + }, + { + name: 'TransferArgs', + type: { + kind: 'struct', + fields: [ + { + name: 'amount', + type: 'u64', + }, + { + name: 'recipientChain', + type: { + defined: 'ChainId', + }, + }, + { + name: 'recipientAddress', + type: { + array: ['u8', 32], + }, + }, + { + name: 'shouldQueue', + type: 'bool', + }, + ], + }, + }, + { + name: 'ReleaseStatus', + type: { + kind: 'enum', + variants: [ + { + name: 'NotApproved', + }, + { + name: 'ReleaseAfter', + fields: ['i64'], + }, + { + name: 'Released', + }, + ], + }, + }, + { + name: 'RateLimitState', + type: { + kind: 'struct', + fields: [ + { + name: 'limit', + docs: ['The maximum capacity of the rate limiter.'], + type: 'u64', + }, + { + name: 'capacityAtLastTx', + docs: [ + 'The capacity of the rate limiter at `last_tx_timestamp`.', + 'The actual current capacity is calculated in `capacity_at`, by', + 'accounting for the time that has passed since `last_tx_timestamp` and', + 'the refill rate.', + ], + type: 'u64', + }, + { + name: 'lastTxTimestamp', + docs: [ + 'The timestamp of the last transaction that counted towards the current', + 'capacity. Transactions that exceeded the capacity do not count, they are', + 'just delayed.', + ], + type: 'i64', + }, + ], + }, + }, + { + name: 'SetTransceiverPeerArgs', + type: { + kind: 'struct', + fields: [ + { + name: 'chainId', + type: { + defined: 'ChainId', + }, + }, + { + name: 'address', + type: { + array: ['u8', 32], + }, + }, + ], + }, + }, + { + name: 'BroadcastPeerArgs', + type: { + kind: 'struct', + fields: [ + { + name: 'chainId', + type: 'u16', + }, + ], + }, + }, + { + name: 'ReleaseOutboundArgs', + type: { + kind: 'struct', + fields: [ + { + name: 'revertOnDelay', + type: 'bool', + }, + ], + }, + }, + { + name: 'ChainId', + type: { + kind: 'struct', + fields: [ + { + name: 'id', + type: 'u16', + }, + ], + }, + }, + { + name: 'Mode', + type: { + kind: 'enum', + variants: [ + { + name: 'Locking', + }, + { + name: 'Burning', + }, + ], + }, + }, + { + name: 'TrimmedAmount', + type: { + kind: 'struct', + fields: [ + { + name: 'amount', + type: 'u64', + }, + { + name: 'decimals', + type: 'u8', + }, + ], + }, + }, + { + name: 'BridgeConfig', + type: { + kind: 'struct', + fields: [ + { + name: 'guardianSetExpirationTime', + docs: [ + 'Period for how long a guardian set is valid after it has been replaced by a new one. This', + 'guarantees that VAAs issued by that set can still be submitted for a certain period. In', + 'this period we still trust the old guardian set.', + ], + type: 'u32', + }, + { + name: 'fee', + docs: [ + 'Amount of lamports that needs to be paid to the protocol to post a message', + ], + type: 'u64', + }, + ], + }, + }, + ], + errors: [ + { + code: 6000, + name: 'CantReleaseYet', + msg: 'CantReleaseYet', + }, + { + code: 6001, + name: 'InvalidPendingOwner', + msg: 'InvalidPendingOwner', + }, + { + code: 6002, + name: 'InvalidChainId', + msg: 'InvalidChainId', + }, + { + code: 6003, + name: 'InvalidRecipientAddress', + msg: 'InvalidRecipientAddress', + }, + { + code: 6004, + name: 'InvalidTransceiverPeer', + msg: 'InvalidTransceiverPeer', + }, + { + code: 6005, + name: 'InvalidNttManagerPeer', + msg: 'InvalidNttManagerPeer', + }, + { + code: 6006, + name: 'InvalidRecipientNttManager', + msg: 'InvalidRecipientNttManager', + }, + { + code: 6007, + name: 'TransferAlreadyRedeemed', + msg: 'TransferAlreadyRedeemed', + }, + { + code: 6008, + name: 'TransferCannotBeRedeemed', + msg: 'TransferCannotBeRedeemed', + }, + { + code: 6009, + name: 'TransferNotApproved', + msg: 'TransferNotApproved', + }, + { + code: 6010, + name: 'MessageAlreadySent', + msg: 'MessageAlreadySent', + }, + { + code: 6011, + name: 'InvalidMode', + msg: 'InvalidMode', + }, + { + code: 6012, + name: 'InvalidMintAuthority', + msg: 'InvalidMintAuthority', + }, + { + code: 6013, + name: 'TransferExceedsRateLimit', + msg: 'TransferExceedsRateLimit', + }, + { + code: 6014, + name: 'Paused', + msg: 'Paused', + }, + { + code: 6015, + name: 'DisabledTransceiver', + msg: 'DisabledTransceiver', + }, + { + code: 6016, + name: 'InvalidDeployer', + msg: 'InvalidDeployer', + }, + ], +}; diff --git a/wormhole-connect/src/routes/ntt/chains/solana/types/ntt_quoter.ts b/wormhole-connect/src/routes/ntt/chains/solana/types/ntt_quoter.ts new file mode 100644 index 000000000..2a9c6393e --- /dev/null +++ b/wormhole-connect/src/routes/ntt/chains/solana/types/ntt_quoter.ts @@ -0,0 +1,885 @@ +export type NttQuoter = { + version: '0.1.0'; + name: 'ntt_quoter'; + instructions: [ + { + name: 'requestRelay'; + accounts: [ + { + name: 'payer'; + isMut: true; + isSigner: true; + }, + { + name: 'instance'; + isMut: false; + isSigner: false; + }, + { + name: 'registeredChain'; + isMut: false; + isSigner: false; + }, + { + name: 'outboxItem'; + isMut: false; + isSigner: false; + }, + { + name: 'relayRequest'; + isMut: true; + isSigner: false; + }, + { + name: 'systemProgram'; + isMut: false; + isSigner: false; + }, + ]; + args: [ + { + name: 'args'; + type: { + defined: 'RequestRelayArgs'; + }; + }, + ]; + }, + { + name: 'closeRelay'; + accounts: [ + { + name: 'authority'; + isMut: false; + isSigner: true; + }, + { + name: 'instance'; + isMut: false; + isSigner: false; + }, + { + name: 'feeRecipient'; + isMut: true; + isSigner: false; + }, + { + name: 'relayRequest'; + isMut: true; + isSigner: false; + }, + { + name: 'systemProgram'; + isMut: false; + isSigner: false; + }, + ]; + args: []; + }, + { + name: 'initialize'; + accounts: [ + { + name: 'owner'; + isMut: true; + isSigner: true; + }, + { + name: 'instance'; + isMut: true; + isSigner: false; + }, + { + name: 'feeRecipient'; + isMut: false; + isSigner: false; + }, + { + name: 'programData'; + isMut: true; + isSigner: false; + docs: [ + 'We use the program data to make sure this owner is the upgrade authority (the true owner,', + 'who deployed this program).', + ]; + }, + { + name: 'systemProgram'; + isMut: false; + isSigner: false; + }, + ]; + args: []; + }, + { + name: 'setAssistant'; + accounts: [ + { + name: 'owner'; + isMut: false; + isSigner: true; + }, + { + name: 'instance'; + isMut: true; + isSigner: false; + }, + { + name: 'assistant'; + isMut: false; + isSigner: false; + isOptional: true; + }, + ]; + args: []; + }, + { + name: 'setFeeRecipient'; + accounts: [ + { + name: 'owner'; + isMut: false; + isSigner: true; + }, + { + name: 'instance'; + isMut: true; + isSigner: false; + }, + { + name: 'feeRecipient'; + isMut: false; + isSigner: false; + }, + ]; + args: []; + }, + { + name: 'registerChain'; + accounts: [ + { + name: 'authority'; + isMut: true; + isSigner: true; + }, + { + name: 'instance'; + isMut: false; + isSigner: false; + }, + { + name: 'registeredChain'; + isMut: true; + isSigner: false; + }, + { + name: 'systemProgram'; + isMut: false; + isSigner: false; + }, + ]; + args: [ + { + name: 'args'; + type: { + defined: 'RegisterChainArgs'; + }; + }, + ]; + }, + { + name: 'updateSolPrice'; + accounts: [ + { + name: 'authority'; + isMut: false; + isSigner: true; + }, + { + name: 'instance'; + isMut: false; + isSigner: false; + }, + ]; + args: [ + { + name: 'args'; + type: { + defined: 'UpdateSolPriceArgs'; + }; + }, + ]; + }, + { + name: 'updateChainPrices'; + accounts: [ + { + name: 'authority'; + isMut: false; + isSigner: true; + }, + { + name: 'instance'; + isMut: false; + isSigner: false; + }, + { + name: 'registeredChain'; + isMut: true; + isSigner: false; + }, + ]; + args: [ + { + name: 'args'; + type: { + defined: 'UpdateChainPricesArgs'; + }; + }, + ]; + }, + { + name: 'updateChainParams'; + accounts: [ + { + name: 'authority'; + isMut: false; + isSigner: true; + }, + { + name: 'instance'; + isMut: false; + isSigner: false; + }, + { + name: 'registeredChain'; + isMut: true; + isSigner: false; + }, + ]; + args: [ + { + name: 'args'; + type: { + defined: 'UpdateChainParamsArgs'; + }; + }, + ]; + }, + ]; + accounts: [ + { + name: 'instance'; + type: { + kind: 'struct'; + fields: [ + { + name: 'owner'; + type: 'publicKey'; + }, + { + name: 'assistant'; + type: 'publicKey'; + }, + { + name: 'feeRecipient'; + type: 'publicKey'; + }, + { + name: 'solPrice'; + type: 'u64'; + }, + ]; + }; + }, + { + name: 'registeredChain'; + type: { + kind: 'struct'; + fields: [ + { + name: 'bump'; + type: 'u8'; + }, + { + name: 'maxGasDropoff'; + type: 'u64'; + }, + { + name: 'basePrice'; + type: 'u64'; + }, + { + name: 'nativePrice'; + type: 'u64'; + }, + { + name: 'gasPrice'; + type: 'u64'; + }, + ]; + }; + }, + { + name: 'relayRequest'; + type: { + kind: 'struct'; + fields: [ + { + name: 'requestedGasDropoff'; + type: 'u64'; + }, + ]; + }; + }, + ]; + types: [ + { + name: 'RegisterChainArgs'; + type: { + kind: 'struct'; + fields: [ + { + name: 'chainId'; + type: 'u16'; + }, + ]; + }; + }, + { + name: 'RequestRelayArgs'; + type: { + kind: 'struct'; + fields: [ + { + name: 'gasDropoff'; + type: 'u64'; + }, + { + name: 'maxFee'; + type: 'u64'; + }, + ]; + }; + }, + { + name: 'UpdateSolPriceArgs'; + type: { + kind: 'struct'; + fields: [ + { + name: 'solPrice'; + type: 'u64'; + }, + ]; + }; + }, + { + name: 'UpdateChainPricesArgs'; + type: { + kind: 'struct'; + fields: [ + { + name: 'nativePrice'; + type: 'u64'; + }, + { + name: 'gasPrice'; + type: 'u64'; + }, + ]; + }; + }, + { + name: 'UpdateChainParamsArgs'; + type: { + kind: 'struct'; + fields: [ + { + name: 'maxGasDropoff'; + type: 'u64'; + }, + { + name: 'basePrice'; + type: 'u64'; + }, + ]; + }; + }, + ]; + errors: [ + { + code: 6001; + name: 'ExceedsUserMaxFee'; + msg: 'Relay fees exceeds specified max'; + }, + { + code: 6002; + name: 'ExceedsMaxGasDropoff'; + msg: 'Requested gas dropoff exceeds max allowed for chain'; + }, + { + code: 6003; + name: 'InvalidFeeRecipient'; + msg: 'The specified fee recipient does not match the address in the instance accound'; + }, + { + code: 6004; + name: 'RelayingToChainDisabled'; + msg: 'Relaying to the specified chain is disabled'; + }, + { + code: 6257; + name: 'FeeRecipientCannotBeDefault'; + msg: 'The fee recipient cannot be the default address (0x0)'; + }, + { + code: 6258; + name: 'NotAuthorized'; + msg: 'Must be owner or assistant'; + }, + ]; +}; + +export const IDL: NttQuoter = { + version: '0.1.0', + name: 'ntt_quoter', + instructions: [ + { + name: 'requestRelay', + accounts: [ + { + name: 'payer', + isMut: true, + isSigner: true, + }, + { + name: 'instance', + isMut: false, + isSigner: false, + }, + { + name: 'registeredChain', + isMut: false, + isSigner: false, + }, + { + name: 'outboxItem', + isMut: false, + isSigner: false, + }, + { + name: 'relayRequest', + isMut: true, + isSigner: false, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'args', + type: { + defined: 'RequestRelayArgs', + }, + }, + ], + }, + { + name: 'closeRelay', + accounts: [ + { + name: 'authority', + isMut: false, + isSigner: true, + }, + { + name: 'instance', + isMut: false, + isSigner: false, + }, + { + name: 'feeRecipient', + isMut: true, + isSigner: false, + }, + { + name: 'relayRequest', + isMut: true, + isSigner: false, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + ], + args: [], + }, + { + name: 'initialize', + accounts: [ + { + name: 'owner', + isMut: true, + isSigner: true, + }, + { + name: 'instance', + isMut: true, + isSigner: false, + }, + { + name: 'feeRecipient', + isMut: false, + isSigner: false, + }, + { + name: 'programData', + isMut: true, + isSigner: false, + docs: [ + 'We use the program data to make sure this owner is the upgrade authority (the true owner,', + 'who deployed this program).', + ], + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + ], + args: [], + }, + { + name: 'setAssistant', + accounts: [ + { + name: 'owner', + isMut: false, + isSigner: true, + }, + { + name: 'instance', + isMut: true, + isSigner: false, + }, + { + name: 'assistant', + isMut: false, + isSigner: false, + isOptional: true, + }, + ], + args: [], + }, + { + name: 'setFeeRecipient', + accounts: [ + { + name: 'owner', + isMut: false, + isSigner: true, + }, + { + name: 'instance', + isMut: true, + isSigner: false, + }, + { + name: 'feeRecipient', + isMut: false, + isSigner: false, + }, + ], + args: [], + }, + { + name: 'registerChain', + accounts: [ + { + name: 'authority', + isMut: true, + isSigner: true, + }, + { + name: 'instance', + isMut: false, + isSigner: false, + }, + { + name: 'registeredChain', + isMut: true, + isSigner: false, + }, + { + name: 'systemProgram', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'args', + type: { + defined: 'RegisterChainArgs', + }, + }, + ], + }, + { + name: 'updateSolPrice', + accounts: [ + { + name: 'authority', + isMut: false, + isSigner: true, + }, + { + name: 'instance', + isMut: false, + isSigner: false, + }, + ], + args: [ + { + name: 'args', + type: { + defined: 'UpdateSolPriceArgs', + }, + }, + ], + }, + { + name: 'updateChainPrices', + accounts: [ + { + name: 'authority', + isMut: false, + isSigner: true, + }, + { + name: 'instance', + isMut: false, + isSigner: false, + }, + { + name: 'registeredChain', + isMut: true, + isSigner: false, + }, + ], + args: [ + { + name: 'args', + type: { + defined: 'UpdateChainPricesArgs', + }, + }, + ], + }, + { + name: 'updateChainParams', + accounts: [ + { + name: 'authority', + isMut: false, + isSigner: true, + }, + { + name: 'instance', + isMut: false, + isSigner: false, + }, + { + name: 'registeredChain', + isMut: true, + isSigner: false, + }, + ], + args: [ + { + name: 'args', + type: { + defined: 'UpdateChainParamsArgs', + }, + }, + ], + }, + ], + accounts: [ + { + name: 'instance', + type: { + kind: 'struct', + fields: [ + { + name: 'owner', + type: 'publicKey', + }, + { + name: 'assistant', + type: 'publicKey', + }, + { + name: 'feeRecipient', + type: 'publicKey', + }, + { + name: 'solPrice', + type: 'u64', + }, + ], + }, + }, + { + name: 'registeredChain', + type: { + kind: 'struct', + fields: [ + { + name: 'bump', + type: 'u8', + }, + { + name: 'maxGasDropoff', + type: 'u64', + }, + { + name: 'basePrice', + type: 'u64', + }, + { + name: 'nativePrice', + type: 'u64', + }, + { + name: 'gasPrice', + type: 'u64', + }, + ], + }, + }, + { + name: 'relayRequest', + type: { + kind: 'struct', + fields: [ + { + name: 'requestedGasDropoff', + type: 'u64', + }, + ], + }, + }, + ], + types: [ + { + name: 'RegisterChainArgs', + type: { + kind: 'struct', + fields: [ + { + name: 'chainId', + type: 'u16', + }, + ], + }, + }, + { + name: 'RequestRelayArgs', + type: { + kind: 'struct', + fields: [ + { + name: 'gasDropoff', + type: 'u64', + }, + { + name: 'maxFee', + type: 'u64', + }, + ], + }, + }, + { + name: 'UpdateSolPriceArgs', + type: { + kind: 'struct', + fields: [ + { + name: 'solPrice', + type: 'u64', + }, + ], + }, + }, + { + name: 'UpdateChainPricesArgs', + type: { + kind: 'struct', + fields: [ + { + name: 'nativePrice', + type: 'u64', + }, + { + name: 'gasPrice', + type: 'u64', + }, + ], + }, + }, + { + name: 'UpdateChainParamsArgs', + type: { + kind: 'struct', + fields: [ + { + name: 'maxGasDropoff', + type: 'u64', + }, + { + name: 'basePrice', + type: 'u64', + }, + ], + }, + }, + ], + errors: [ + { + code: 6001, + name: 'ExceedsUserMaxFee', + msg: 'Relay fees exceeds specified max', + }, + { + code: 6002, + name: 'ExceedsMaxGasDropoff', + msg: 'Requested gas dropoff exceeds max allowed for chain', + }, + { + code: 6003, + name: 'InvalidFeeRecipient', + msg: 'The specified fee recipient does not match the address in the instance accound', + }, + { + code: 6004, + name: 'RelayingToChainDisabled', + msg: 'Relaying to the specified chain is disabled', + }, + { + code: 6257, + name: 'FeeRecipientCannotBeDefault', + msg: 'The fee recipient cannot be the default address (0x0)', + }, + { + code: 6258, + name: 'NotAuthorized', + msg: 'Must be owner or assistant', + }, + ], +}; diff --git a/wormhole-connect/src/routes/ntt/errors.ts b/wormhole-connect/src/routes/ntt/errors.ts new file mode 100644 index 000000000..651b82463 --- /dev/null +++ b/wormhole-connect/src/routes/ntt/errors.ts @@ -0,0 +1,41 @@ +export class InboundQueuedTransferNotFoundError extends Error { + static MESSAGE = 'Inbound queued transfer not found'; + constructor() { + super(InboundQueuedTransferNotFoundError.MESSAGE); + } +} + +export class InboundQueuedTransferStillQueuedError extends Error { + static MESSAGE = 'Inbound queued transfer still queued'; + constructor() { + super(InboundQueuedTransferStillQueuedError.MESSAGE); + } +} + +export class NotEnoughCapacityError extends Error { + static MESSAGE = 'Not enough capacity'; + constructor() { + super(NotEnoughCapacityError.MESSAGE); + } +} + +export class ContractIsPausedError extends Error { + static MESSAGE = 'Contract is paused'; + constructor() { + super(ContractIsPausedError.MESSAGE); + } +} + +export class DestinationContractIsPausedError extends Error { + static MESSAGE = 'Destination contract is paused'; + constructor() { + super(DestinationContractIsPausedError.MESSAGE); + } +} + +export class UnsupportedContractAbiVersion extends Error { + static MESSAGE = 'Unsupported contract ABI version'; + constructor() { + super(UnsupportedContractAbiVersion.MESSAGE); + } +} diff --git a/wormhole-connect/src/routes/ntt/index.ts b/wormhole-connect/src/routes/ntt/index.ts new file mode 100644 index 000000000..52748d033 --- /dev/null +++ b/wormhole-connect/src/routes/ntt/index.ts @@ -0,0 +1,6 @@ +export * from './nttBase'; +export * from './nttManual'; +export * from './nttRelay'; +export * from './errors'; +export * from './types'; +export * from './utils'; diff --git a/wormhole-connect/src/routes/ntt/nttBase.ts b/wormhole-connect/src/routes/ntt/nttBase.ts new file mode 100644 index 000000000..0392fe58f --- /dev/null +++ b/wormhole-connect/src/routes/ntt/nttBase.ts @@ -0,0 +1,465 @@ +import { BigNumber } from 'ethers'; +import { + ChainId, + ChainName, + TokenId, +} from '@wormhole-foundation/wormhole-connect-sdk'; +import { Route, TokenConfig } from 'config/types'; +import { + SignedMessage, + UnsignedMessage, + UnsignedNttMessage, + SignedNttMessage, + TransferDestInfoBaseParams, + TransferDestInfo, + isSignedNttMessage, + RelayerFee, +} from '../types'; +import { fetchVaa } from 'utils/vaa'; +import { hexlify, parseUnits } from 'ethers/lib/utils.js'; +import { BaseRoute } from '../bridge'; +import { isEvmChain, solanaContext, toChainId, toChainName } from 'utils/sdk'; +import { + MAX_DECIMALS, + calculateUSDPrice, + getDisplayName, + getTokenById, + getTokenDecimals, + removeDust, + toNormalizedDecimals, +} from 'utils'; +import { getNttManager } from './chains'; +import { InboundQueuedTransfer } from './types'; +import { + ContractIsPausedError, + DestinationContractIsPausedError, +} from './errors'; +import { WormholeTransceiver, getMessageEvm } from './chains/evm'; +import { NttManagerSolana, getMessageSolana } from './chains/solana'; +import { formatGasFee } from 'routes/utils'; +import { NO_INPUT } from 'utils/style'; +import { estimateAverageGasFee } from 'utils/gas'; +import config from 'config'; +import { + getNttGroupKey, + getNttManagerAddress, + getNttManagerConfigByAddress, + isNttToken, + isNttTokenPair, +} from 'utils/ntt'; + +export abstract class NttBase extends BaseRoute { + isSupportedChain(chain: ChainName): boolean { + return isEvmChain(chain) || chain === 'solana'; + } + + async isSupportedSourceToken( + token?: TokenConfig, + destToken?: TokenConfig, + sourceChain?: ChainName | ChainId, + destChain?: ChainName | ChainId, + ): Promise { + if ( + !token || + !isNttToken(token) || + !sourceChain || + !this.isSupportedToken(token, sourceChain) + ) { + return false; + } + if ( + destToken && + ((destChain && !this.isSupportedToken(destToken, destChain)) || + !isNttTokenPair(destToken, token)) + ) { + return false; + } + return true; + } + + async isSupportedDestToken( + token?: TokenConfig, + sourceToken?: TokenConfig, + sourceChain?: ChainName | ChainId, + destChain?: ChainName | ChainId, + ): Promise { + if ( + !token || + !isNttToken(token) || + !destChain || + !this.isSupportedToken(token, destChain) + ) { + return false; + } + if ( + sourceToken && + ((sourceChain && !this.isSupportedToken(sourceToken, sourceChain)) || + !isNttTokenPair(sourceToken, token)) + ) { + return false; + } + return true; + } + + isSupportedToken(token: TokenConfig, chain: ChainName | ChainId): boolean { + return ( + this.isSupportedChain(token.nativeChain) && + isNttToken(token) && + toChainName(chain) === token.nativeChain + ); + } + + async isRouteSupported( + sourceToken: string, + destToken: string, + amount: string, + sourceChain: ChainName | ChainId, + destChain: ChainName | ChainId, + ): Promise { + // Note: Solana is currently disabled in mainnet + if ( + config.isMainnet && + (toChainName(sourceChain) === 'solana' || + toChainName(destChain) === 'solana') + ) { + return false; + } + return await Promise.all([ + this.isSupportedChain(toChainName(sourceChain)), + this.isSupportedChain(toChainName(destChain)), + this.isSupportedSourceToken( + config.tokens[sourceToken], + config.tokens[destToken], + sourceChain, + destChain, + ), + this.isSupportedDestToken( + config.tokens[destToken], + config.tokens[sourceToken], + sourceChain, + destChain, + ), + ]).then((results) => results.every((result) => result)); + } + + async computeReceiveAmount( + sendAmount: number, + token: string, + destToken: string, + sendingChain: ChainName | undefined, + recipientChain: ChainName | undefined, + routeOptions: any, + ): Promise { + if (!sendAmount) return 0; + return sendAmount; + } + + async computeSendAmount( + receiveAmount: number | undefined, + routeOptions: any, + ): Promise { + if (!receiveAmount) return 0; + return receiveAmount; + } + + async validate( + token: TokenId | 'native', + amount: string, + sendingChain: ChainName | ChainId, + senderAddress: string, + recipientChain: ChainName | ChainId, + recipientAddress: string, + routeOptions: any, + ): Promise { + throw new Error('not implemented'); + } + + async estimateSendGas( + token: TokenId | 'native', + amount: string, + sendingChain: ChainName | ChainId, + senderAddress: string, + recipientChain: ChainName | ChainId, + recipientAddress: string, + routeOptions?: any, + ): Promise { + if (isEvmChain(sendingChain)) { + const gasLimit = this.TYPE === Route.NttManual ? 250_000 : 300_000; + return await estimateAverageGasFee(sendingChain, gasLimit); + } else if (toChainName(sendingChain) === 'solana') { + return BigNumber.from(10_000); + } + throw new Error('Unsupported chain'); + } + + async estimateClaimGas( + destChain: ChainName | ChainId, + signedMessage?: SignedMessage, + ): Promise { + if (this.TYPE === Route.NttRelay) { + // relayer pays claim gas + return BigNumber.from(0); + } + if (isEvmChain(destChain)) { + const gasLimit = 300_000; + return await estimateAverageGasFee(destChain, gasLimit); + } else if (toChainName(destChain) === 'solana') { + return BigNumber.from(65_000); // TODO: check this + } + throw new Error('Unsupported chain'); + } + + async send( + token: TokenId | 'native', + amount: string, + sendingChain: ChainName | ChainId, + senderAddress: string, + recipientChain: ChainName | ChainId, + recipientAddress: string, + destToken: string, + routeOptions: any, + ): Promise { + if (token === 'native') throw new Error('invalid token'); + const tokenConfig = getTokenById(token); + if (!tokenConfig || !isNttToken(tokenConfig)) + throw new Error('invalid token'); + const destTokenConfig = config.tokens[destToken]; + if (!isNttToken(destTokenConfig)) throw new Error('invalid dest token'); + const nttGroupKey = getNttGroupKey(tokenConfig, destTokenConfig); + if (!nttGroupKey) throw new Error('invalid token pair'); + const recipientNttManagerAddress = getNttManagerAddress( + destTokenConfig, + nttGroupKey, + ); + if (!recipientNttManagerAddress) + throw new Error('recipient ntt manager not found'); + if ( + await getNttManager(recipientChain, recipientNttManagerAddress).isPaused() + ) { + throw new DestinationContractIsPausedError(); + } + const nttManagerAddress = getNttManagerAddress(tokenConfig, nttGroupKey); + if (!nttManagerAddress) throw new Error('ntt manager not found'); + const nttManager = getNttManager(sendingChain, nttManagerAddress); + if (await nttManager.isPaused()) { + throw new ContractIsPausedError(); + } + const decimals = getTokenDecimals( + toChainId(sendingChain), + tokenConfig.tokenId, + ); + const sendAmount = removeDust(parseUnits(amount, decimals), decimals); + if (sendAmount.isZero()) { + throw new Error('Amount too low'); + } + const shouldSkipRelayerSend = this.TYPE !== Route.NttRelay; + return await nttManager.send( + token, + senderAddress, + recipientAddress, + sendAmount.toBigInt(), + recipientChain, + shouldSkipRelayerSend, + ); + } + + async redeem( + chain: ChainName | ChainId, + signedMessage: SignedMessage, + payer: string, + ): Promise { + if (!isSignedNttMessage(signedMessage)) + throw new Error('Not a signed NttMessage'); + const { recipientNttManager, vaa } = signedMessage; + const nttManager = getNttManager(chain, recipientNttManager); + if (await nttManager.isPaused()) throw new ContractIsPausedError(); + const nttConfig = getNttManagerConfigByAddress( + recipientNttManager, + toChainName(chain), + ); + if (!nttConfig) throw new Error('ntt config not found'); + if (isEvmChain(chain)) { + if (nttConfig.transceivers[0].type !== 'wormhole') + throw new Error('Unsupported transceiver type'); + const transceiver = new WormholeTransceiver( + chain, + nttConfig.transceivers[0].address, + ); + return await transceiver.receiveMessage(vaa, payer); + } else if (toChainName(chain) === 'solana') + return await (nttManager as NttManagerSolana).receiveMessage(vaa, payer); + throw new Error('Unsupported chain'); + } + + async getRelayerFee( + sourceChain: ChainName | ChainId, + destChain: ChainName | ChainId, + token: string, + destToken: string, + ): Promise { + return null; + } + + async getCurrentOutboundCapacity( + chain: ChainId | ChainName, + nttManagerAddress: string, + ): Promise { + return await getNttManager( + chain, + nttManagerAddress, + ).getCurrentOutboundCapacity(); + } + + async getCurrentInboundCapacity( + chain: ChainId | ChainName, + nttManagerAddress: string, + fromChain: ChainId | ChainName, + ): Promise { + return await getNttManager( + chain, + nttManagerAddress, + ).getCurrentInboundCapacity(fromChain); + } + + async getRateLimitDuration( + chain: ChainId | ChainName, + nttManagerAddress: string, + ): Promise { + return await getNttManager(chain, nttManagerAddress).getRateLimitDuration(); + } + + async getInboundQueuedTransfer( + chain: ChainName | ChainId, + nttManagerAddress: string, + messageDigest: string, + ): Promise { + return await getNttManager( + chain, + nttManagerAddress, + ).getInboundQueuedTransfer(messageDigest); + } + + async completeInboundQueuedTransfer( + chain: ChainName | ChainId, + nttManagerAddress: string, + messageDigest: string, + recipientAddress: string, + payer: string, + ): Promise { + const nttManager = getNttManager(chain, nttManagerAddress); + if (await nttManager.isPaused()) { + throw new ContractIsPausedError(); + } + return await nttManager.completeInboundQueuedTransfer( + messageDigest, + recipientAddress, + payer, + ); + } + + async getForeignAsset( + token: TokenId, + chain: ChainName | ChainId, + destToken?: TokenConfig, + ): Promise { + return destToken?.tokenId?.address || null; + } + + async getMessage( + tx: string, + chain: ChainName | ChainId, + ): Promise { + if (isEvmChain(chain)) { + return await getMessageEvm(tx, chain); + } + if (toChainName(chain) === 'solana') { + return await getMessageSolana(tx); + } + throw new Error('Unsupported chain'); + } + + async getSignedMessage( + unsigned: UnsignedNttMessage, + ): Promise { + const vaa = await fetchVaa(unsigned, true); + + if (!vaa) { + throw new Error('VAA not found'); + } + + return { + ...unsigned, + vaa: hexlify(vaa), + }; + } + + async tryFetchRedeemTx(txData: UnsignedMessage): Promise { + return undefined; + } + + async isTransferCompleted( + chain: ChainName | ChainId, + signedMessage: SignedNttMessage, + ): Promise { + if (!isSignedNttMessage(signedMessage)) { + throw new Error('Invalid signed message'); + } + const { recipientNttManager, messageDigest } = signedMessage; + const nttManager = getNttManager(chain, recipientNttManager); + return await nttManager.isTransferCompleted(messageDigest); + } + + async getTransferDestInfo( + params: T, + ): Promise { + const { + txData: { receivedTokenKey, amount, tokenDecimals, toChain }, + tokenPrices, + gasEstimate, + receiveTx, + } = params; + const token = config.tokens[receivedTokenKey]; + const formattedAmt = toNormalizedDecimals( + amount, + tokenDecimals, + MAX_DECIMALS, + ); + const result = { + route: this.TYPE, + displayData: [ + { + title: 'Amount', + value: `${formattedAmt} ${getDisplayName(token)}`, + valueUSD: calculateUSDPrice(formattedAmt, tokenPrices, token), + }, + ], + }; + if (this.TYPE === Route.NttManual) { + const { gasToken } = config.chains[toChain]!; + let gas = gasEstimate; + if (receiveTx) { + if (isEvmChain(toChain)) { + const gasFee = await config.wh.getTxGasFee(toChain, receiveTx); + if (gasFee) { + gas = formatGasFee(toChain, gasFee); + } + } else if (toChainName(toChain) === 'solana') { + const connection = solanaContext().connection; + if (!connection) throw new Error('Connection not found'); + const tx = await connection.getParsedTransaction(receiveTx); + if (tx?.meta?.fee) { + gas = formatGasFee(toChain, BigNumber.from(tx.meta.fee)); + } + } + } + result.displayData.push({ + title: receiveTx ? 'Gas fee' : 'Gas estimate', + value: gas + ? `${gas} ${getDisplayName(config.tokens[gasToken])}` + : NO_INPUT, + valueUSD: calculateUSDPrice(gas, tokenPrices, config.tokens[gasToken]), + }); + } + return result; + } +} diff --git a/wormhole-connect/src/routes/ntt/nttManual.ts b/wormhole-connect/src/routes/ntt/nttManual.ts new file mode 100644 index 000000000..ae6308334 --- /dev/null +++ b/wormhole-connect/src/routes/ntt/nttManual.ts @@ -0,0 +1,8 @@ +import { Route } from 'config/types'; +import { NttBase } from './nttBase'; + +export class NttManual extends NttBase { + readonly NATIVE_GAS_DROPOFF_SUPPORTED: boolean = false; + readonly AUTOMATIC_DEPOSIT: boolean = false; + readonly TYPE: Route = Route.NttManual; +} diff --git a/wormhole-connect/src/routes/ntt/nttRelay.ts b/wormhole-connect/src/routes/ntt/nttRelay.ts new file mode 100644 index 000000000..18de73d29 --- /dev/null +++ b/wormhole-connect/src/routes/ntt/nttRelay.ts @@ -0,0 +1,261 @@ +import { Route, TokenConfig } from 'config/types'; +import { BigNumber } from 'ethers'; +import { getNttManager } from './chains'; +import { ChainName, ChainId } from '@wormhole-foundation/wormhole-connect-sdk'; +import { NttBase } from './nttBase'; +import { + RelayerFee, + TransferDisplayData, + TransferInfoBaseParams, + UnsignedMessage, + isUnsignedNttMessage, +} from 'routes/types'; +import { + MAX_DECIMALS, + calculateUSDPrice, + getDisplayName, + getTokenDecimals, + toNormalizedDecimals, +} from 'utils'; +import { + ParsedRelayerMessage, + isEvmChain, + toChainId, + toChainName, +} from 'utils/sdk'; +import { NO_INPUT } from 'utils/style'; +import { TokenPrices } from 'store/tokenPrices'; +import { toDecimals, toFixedDecimals } from 'utils/balance'; +import { NttManagerEvm, WormholeTransceiver } from './chains/evm'; +import { NttQuoter } from './chains/solana/nttQuoter'; +import config from 'config'; +import { + getNttGroupKey, + getNttManagerAddress, + getNttManagerConfig, + getNttManagerConfigByAddress, +} from 'utils/ntt'; + +export class NttRelay extends NttBase { + readonly NATIVE_GAS_DROPOFF_SUPPORTED: boolean = false; + readonly AUTOMATIC_DEPOSIT: boolean = true; + readonly TYPE: Route = Route.NttRelay; + + async isRouteSupported( + sourceToken: string, + destToken: string, + amount: string, + sourceChain: ChainName | ChainId, + destChain: ChainName | ChainId, + ): Promise { + const groupKey = getNttGroupKey( + config.tokens[sourceToken], + config.tokens[destToken], + ); + if (!groupKey) return false; + const nttConfig = getNttManagerConfig(config.tokens[sourceToken], groupKey); + if (!nttConfig) return false; + if ( + !(await super.isRouteSupported( + sourceToken, + destToken, + amount, + sourceChain, + destChain, + )) + ) { + return false; + } + if (isEvmChain(sourceChain)) { + if (nttConfig.transceivers[0].type !== 'wormhole') return false; + const transceiver = new WormholeTransceiver( + sourceChain, + nttConfig.transceivers[0].address, + ); + return await Promise.all([ + transceiver.isWormholeRelayingEnabled(destChain), + transceiver.isSpecialRelayingEnabled(destChain), + ]).then((results) => results.some((r) => r)); + } + if (toChainName(sourceChain) === 'solana') { + if (!nttConfig.solanaQuoter) return false; + const quoter = new NttQuoter(nttConfig.solanaQuoter); + return await quoter.isRelayEnabled(destChain); + } + return false; + } + + async getRelayerFee( + sourceChain: ChainName | ChainId, + destChain: ChainName | ChainId, + token: string, + destToken: string, + ): Promise { + const groupKey = getNttGroupKey( + config.tokens[token], + config.tokens[destToken], + ); + if (!groupKey) throw new Error('no ntt group'); + const nttManagerAddress = getNttManagerAddress( + config.tokens[token], + groupKey, + ); + if (!nttManagerAddress) throw new Error('no ntt manager address'); + const nttConfig = getNttManagerConfigByAddress( + nttManagerAddress, + toChainName(sourceChain), + ); + if (!nttConfig) throw new Error('no ntt config'); + if (isEvmChain(sourceChain)) { + const nttManager = new NttManagerEvm(sourceChain, nttManagerAddress); + if (nttConfig.transceivers[0].type !== 'wormhole') + throw new Error('no wormhole transceiver'); + const deliveryPrice = await nttManager.quoteDeliveryPrice( + destChain, + false, + ); + return { fee: deliveryPrice, feeToken: 'native' }; + } + if (toChainName(sourceChain) === 'solana') { + if (!nttConfig.solanaQuoter) throw new Error('no solana quoter'); + const quoter = new NttQuoter(nttConfig.solanaQuoter); + const relayCost = await quoter.calcRelayCost(destChain); + return { fee: BigNumber.from(relayCost.toString()), feeToken: 'native' }; + } + throw new Error('unsupported chain'); + } + + async getPreview( + token: TokenConfig, + destToken: TokenConfig, + amount: number, + sendingChain: ChainName | ChainId, + receipientChain: ChainName | ChainId, + sendingGasEst: string, + claimingGasEst: string, + receiveAmount: string, + tokenPrices: TokenPrices, + routeOptions: { relayerFee: number }, + ): Promise { + const sendingChainName = toChainName(sendingChain); + const sourceGasToken = config.chains[sendingChainName]?.gasToken; + const sourceGasTokenSymbol = sourceGasToken + ? getDisplayName(config.tokens[sourceGasToken]) + : ''; + // Calculate the USD value of the gas + const sendingGasEstPrice = calculateUSDPrice( + sendingGasEst, + tokenPrices, + config.tokens[sourceGasToken || ''], + ); + const relayerFeePrice = calculateUSDPrice( + routeOptions.relayerFee, + tokenPrices, + config.tokens[sourceGasToken || ''], + ); + let totalFeesText = ''; + let totalFeesPrice = ''; + if ( + sendingGasEst && + sourceGasToken && + routeOptions.relayerFee !== undefined + ) { + const feeValue = + routeOptions.relayerFee + Number.parseFloat(sendingGasEst); + totalFeesText = toFixedDecimals(feeValue.toString(), 6); + totalFeesPrice = calculateUSDPrice( + feeValue, + tokenPrices, + config.tokens[sourceGasToken], + ); + } + return [ + { + title: 'Amount', + value: `${!isNaN(amount) ? amount : '0'} ${getDisplayName(destToken)}`, + valueUSD: calculateUSDPrice(amount, tokenPrices, destToken), + }, + { + title: 'Total fee estimates', + value: totalFeesText ? `${totalFeesText} ${sourceGasTokenSymbol}` : '', + valueUSD: totalFeesPrice ? `${totalFeesPrice || NO_INPUT}` : '', + rows: [ + { + title: 'Source chain gas estimate', + value: sendingGasEst + ? `~ ${sendingGasEst} ${sourceGasTokenSymbol}` + : 'Not available', + valueUSD: sendingGasEstPrice, + }, + { + title: 'Relayer fee', + value: routeOptions.relayerFee + ? `${routeOptions.relayerFee} ${sourceGasTokenSymbol}` + : NO_INPUT, + valueUSD: relayerFeePrice, + }, + ], + }, + ]; + } + + async getTransferSourceInfo( + params: T, + ): Promise { + const txData = params.txData as ParsedRelayerMessage; + const { tokenKey, amount, tokenDecimals, fromChain, gasFee, relayerFee } = + txData; + const tokenPrices = params.tokenPrices; + const formattedAmt = toNormalizedDecimals( + amount, + tokenDecimals, + MAX_DECIMALS, + ); + const { gasToken: sourceGasTokenKey } = config.chains[fromChain]!; + const sourceGasToken = config.tokens[sourceGasTokenKey]; + const decimals = getTokenDecimals( + toChainId(sourceGasToken.nativeChain), + 'native', + ); + const formattedGas = gasFee && toDecimals(gasFee, decimals, MAX_DECIMALS); + const formattedFee = + relayerFee && toDecimals(relayerFee, decimals, MAX_DECIMALS); + const token = config.tokens[tokenKey]; + + return [ + { + title: 'Amount', + value: `${formattedAmt} ${getDisplayName(token)}`, + valueUSD: calculateUSDPrice(formattedAmt, tokenPrices, token), + }, + { + title: 'Gas fee', + value: formattedGas + ? `${formattedGas} ${getDisplayName(sourceGasToken)}` + : NO_INPUT, + valueUSD: calculateUSDPrice(formattedGas, tokenPrices, sourceGasToken), + }, + { + title: 'Relayer fee', + value: formattedFee + ? `${formattedFee} ${getDisplayName(sourceGasToken)}` + : NO_INPUT, + valueUSD: calculateUSDPrice(formattedFee, tokenPrices, sourceGasToken), + }, + ]; + } + + async tryFetchRedeemTx(txData: UnsignedMessage): Promise { + if (!isUnsignedNttMessage(txData)) { + throw new Error('invalid txData'); + } + const { toChain, recipientNttManager, messageDigest } = txData; + const nttManager = getNttManager(toChain, recipientNttManager); + try { + return await nttManager.fetchRedeemTx(messageDigest); + } catch (e) { + console.error(e); + } + return undefined; + } +} diff --git a/wormhole-connect/src/routes/ntt/types.ts b/wormhole-connect/src/routes/ntt/types.ts new file mode 100644 index 000000000..c5d46d1f4 --- /dev/null +++ b/wormhole-connect/src/routes/ntt/types.ts @@ -0,0 +1,5 @@ +export type InboundQueuedTransfer = { + recipient: string; + amount: string; + rateLimitExpiryTimestamp: number; +}; diff --git a/wormhole-connect/src/routes/ntt/utils.ts b/wormhole-connect/src/routes/ntt/utils.ts new file mode 100644 index 000000000..676a75b24 --- /dev/null +++ b/wormhole-connect/src/routes/ntt/utils.ts @@ -0,0 +1,45 @@ +export interface WormholeTransceiverInstruction { + shouldSkipRelayerSend: boolean; +} + +export const encodeWormholeTransceiverInstruction = ( + instruction: WormholeTransceiverInstruction, +): Buffer => { + const buffer = Buffer.alloc(1); + buffer.writeUInt8(instruction.shouldSkipRelayerSend ? 1 : 0); + return buffer; +}; + +export interface TransceiverInstruction { + index: number; + payload: Buffer; +} + +export const encodeTransceiverInstruction = ( + instruction: TransceiverInstruction, +): Buffer => { + if (instruction.payload.length > 255) { + throw new Error(`PayloadTooLong: ${instruction.payload.length}`); + } + const payloadLength = Buffer.from([instruction.payload.length]); + const indexBuffer = Buffer.from([instruction.index]); + return Buffer.concat([indexBuffer, payloadLength, instruction.payload]); +}; + +export const encodeTransceiverInstructions = ( + instructions: TransceiverInstruction[], +): Buffer => { + if (instructions.length > 255) { + throw new Error(`PayloadTooLong: ${instructions.length}`); + } + + const instructionsLength = Buffer.from([instructions.length]); + let encoded = Buffer.alloc(0); + + for (let i = 0; i < instructions.length; i++) { + const innerEncoded = encodeTransceiverInstruction(instructions[i]); + encoded = Buffer.concat([encoded, innerEncoded]); + } + + return Buffer.concat([instructionsLength, encoded]); +}; diff --git a/wormhole-connect/src/routes/operator.ts b/wormhole-connect/src/routes/operator.ts index 141b317fa..38d33efeb 100644 --- a/wormhole-connect/src/routes/operator.ts +++ b/wormhole-connect/src/routes/operator.ts @@ -24,6 +24,8 @@ import { TransferDisplayData, TransferInfoBaseParams, TransferDestInfo, + NttRelayingType, + RelayerFee, } from './types'; import { CCTPManualRoute, @@ -34,6 +36,10 @@ import { getTokenById, isEqualCaseInsensitive } from 'utils'; import { ETHBridge } from './porticoBridge/ethBridge'; import { wstETHBridge } from './porticoBridge/wstETHBridge'; import { TokenPrices } from 'store/tokenPrices'; +import { NttManual, NttRelay } from './ntt'; +import { getMessageEvm } from './ntt/chains/evm'; +import { getMessageSolana } from './ntt/chains/solana'; +import { getNttManagerConfigByAddress } from 'utils/ntt'; export class Operator { getRoute(route: Route): RouteAbstract { @@ -65,6 +71,12 @@ export class Operator { case Route.wstETHBridge: { return new wstETHBridge(); } + case Route.NttManual: { + return new NttManual(); + } + case Route.NttRelay: { + return new NttRelay(); + } default: { throw new Error(`${route} is not a valid route`); } @@ -96,6 +108,34 @@ export class Operator { return Route.CCTPRelay; else return Route.CCTPManual; } + + // Check if is Ntt Route (NttRelay or NttManual) + if ( + getNttManagerConfigByAddress(receipt.to, config.wh.toChainName(chain)) + ) { + const { relayingType } = await getMessageEvm(txHash, chain, receipt); + return relayingType === NttRelayingType.Manual + ? Route.NttManual + : Route.NttRelay; + } + } + + if (chain === 'solana') { + // Check if is Ntt Route (NttRelay or NttManual) + const connection = solanaContext().connection; + if (!connection) throw new Error('Connection not found'); + const tx = await connection.getParsedTransaction(txHash); + if (!tx) throw new Error('Transaction not found'); + if ( + tx.transaction.message.instructions.some((ix) => + getNttManagerConfigByAddress(ix.programId.toString(), chain), + ) + ) { + const { relayingType } = await getMessageSolana(txHash); + return relayingType === NttRelayingType.Manual + ? Route.NttManual + : Route.NttRelay; + } } if (chain === 'solana') { @@ -521,7 +561,7 @@ export class Operator { destChain: ChainName | ChainId, token: string, destToken: string, - ): Promise { + ): Promise { const r = this.getRoute(route); return r.getRelayerFee(sourceChain, destChain, token, destToken); } @@ -530,9 +570,10 @@ export class Operator { route: Route, tokenId: TokenId, chain: ChainName | ChainId, + destToken?: TokenConfig, ): Promise { const r = this.getRoute(route); - return r.getForeignAsset(tokenId, chain); + return r.getForeignAsset(tokenId, chain, destToken); } async isTransferCompleted( diff --git a/wormhole-connect/src/routes/porticoBridge/porticoBridge.ts b/wormhole-connect/src/routes/porticoBridge/porticoBridge.ts index cefeb7af3..c6779e530 100644 --- a/wormhole-connect/src/routes/porticoBridge/porticoBridge.ts +++ b/wormhole-connect/src/routes/porticoBridge/porticoBridge.ts @@ -14,6 +14,7 @@ import { SignedTokenTransferMessage, TransferDisplayData, TransferInfoBaseParams, + RelayerFee, } from '../types'; import { fetchGlobalTx, fetchVaa, getEmitterAndSequence } from 'utils/vaa'; import { hexZeroPad, hexlify, parseUnits } from 'ethers/lib/utils.js'; @@ -367,10 +368,6 @@ export abstract class PorticoBridge extends BaseRoute { return BigNumber.from(0); } - getMinSendAmount(routeOptions: PorticoBridgeState): number { - return 0; - } - getMaxSendAmount(): number { return this.maxAmount; } @@ -566,7 +563,7 @@ export abstract class PorticoBridge extends BaseRoute { destChain: ChainName | ChainId, token: string, destToken: string, - ): Promise { + ): Promise { if (!destToken) { throw new Error('destToken is required'); } @@ -593,12 +590,16 @@ export abstract class PorticoBridge extends BaseRoute { if (response.status !== 200) { throw new Error(`Error getting relayer fee: ${response.statusText}`); } - return BigNumber.from(response.data.fee); + return { + fee: BigNumber.from(response.data.fee), + feeToken: finalToken.tokenId, + }; } async getForeignAsset( token: TokenId, chain: ChainName | ChainId, + destToken?: TokenConfig, ): Promise { return await config.wh.getForeignAsset(token, chain); } diff --git a/wormhole-connect/src/routes/relay/relay.ts b/wormhole-connect/src/routes/relay/relay.ts index 218a64271..0f423a718 100644 --- a/wormhole-connect/src/routes/relay/relay.ts +++ b/wormhole-connect/src/routes/relay/relay.ts @@ -35,6 +35,7 @@ import { BridgeRoute } from '../bridge/bridge'; import { toDecimals, toFixedDecimals } from '../../utils/balance'; import { RelayTransferMessage, + RelayerFee, SignedRelayTransferMessage, TransferDestInfo, TransferDisplayData, @@ -102,12 +103,13 @@ export class RelayRoute extends BridgeRoute implements RelayAbstract { const tokenId = getWrappedTokenId(tokenConfig); let relayerFee; try { - relayerFee = await this.getRelayerFee( + const result = await this.getRelayerFee( sourceChain, destChain, sourceToken, destToken, ); + relayerFee = result?.fee; } catch (e) { console.error(e); } @@ -514,14 +516,15 @@ export class RelayRoute extends BridgeRoute implements RelayAbstract { destChain: ChainName | ChainId, token: string, destToken: string, - ): Promise { + ): Promise { const context: any = config.wh.getContext(sourceChain); const tokenConfig = config.tokens[token]; if (!tokenConfig) throw new Error('could not get token config'); const tokenId = tokenConfig.tokenId || getWrappedTokenId(tokenConfig); - return context.getRelayerFee + const fee = context.getRelayerFee ? await context.getRelayerFee(sourceChain, destChain, tokenId) : BigNumber.from(0); + return { fee, feeToken: tokenId }; } async getTransferSourceInfo({ diff --git a/wormhole-connect/src/routes/tbtc/tbtc.ts b/wormhole-connect/src/routes/tbtc/tbtc.ts index 7cbe1fe11..405680f72 100644 --- a/wormhole-connect/src/routes/tbtc/tbtc.ts +++ b/wormhole-connect/src/routes/tbtc/tbtc.ts @@ -22,6 +22,7 @@ import { TokenTransferMessage, SignedTokenTransferMessage, TBTCMessage, + RelayerFee, } from '../types'; import { adaptParsedMessage } from '../utils'; import { @@ -162,10 +163,6 @@ export class TBTCRoute extends BaseRoute { throw new Error('not implemented'); } - getMinSendAmount(routeOptions: any): number { - return 0; - } - async send( token: TokenId | 'native', amount: string, @@ -417,13 +414,14 @@ export class TBTCRoute extends BaseRoute { destChain: ChainName | ChainId, token: string, destToken: string, - ): Promise { - return BigNumber.from(0); + ): Promise { + return null; } async getForeignAsset( token: TokenId, chain: ChainName | ChainId, + destToken?: TokenConfig, ): Promise { const chainId = config.wh.toChainId(chain); if (isTBTCCanonicalChain(chainId)) { diff --git a/wormhole-connect/src/routes/types.ts b/wormhole-connect/src/routes/types.ts index 5ec453904..bcfcd97c8 100644 --- a/wormhole-connect/src/routes/types.ts +++ b/wormhole-connect/src/routes/types.ts @@ -1,6 +1,8 @@ import { Route } from 'config/types'; import { ParsedMessage, ParsedRelayerMessage } from '../utils/sdk'; import { TokenPrices } from 'store/tokenPrices'; +import { BigNumber } from 'ethers'; +import { TokenId } from '@wormhole-foundation/wormhole-connect-sdk'; export type TokenTransferMessage = ParsedMessage; export type RelayTransferMessage = ParsedRelayerMessage; @@ -11,17 +13,42 @@ export type ManualCCTPMessage = CCTPMessage & ParsedMessage; export type RelayCCTPMessage = CCTPMessage & ParsedRelayerMessage; export type UnsignedCCTPMessage = ManualCCTPMessage | RelayCCTPMessage; export type TBTCMessage = TokenTransferMessage & { to: string }; +export enum NttRelayingType { + Standard, + Special, + Manual, +} +export type UnsignedNttMessage = ParsedMessage & { + recipientNttManager: string; + messageDigest: string; + relayerFee: string; + relayingType: NttRelayingType; +}; export type UnsignedMessage = | TokenTransferMessage | RelayTransferMessage | UnsignedCCTPMessage - | TBTCMessage; + | TBTCMessage + | UnsignedNttMessage; + +export const isUnsignedNttMessage = ( + message: UnsignedMessage, +): message is UnsignedNttMessage => + typeof message === 'object' && + 'recipientNttManager' in message && + 'messageDigest' in message && + 'relayerFee' in message && + 'relayingType' in message; export type SignedTokenTransferMessage = TokenTransferMessage & { vaa: string }; // hex encoded vaa bytes export type SignedRelayTransferMessage = RelayTransferMessage & { vaa: string }; // hex encoded vaa bytes +export type SignedNttMessage = UnsignedNttMessage & { + vaa: string; +}; // hex encoded vaa bytes export type SignedWormholeMessage = | SignedTokenTransferMessage - | SignedRelayTransferMessage; + | SignedRelayTransferMessage + | SignedNttMessage; export type SignedManualCCTPMessage = ManualCCTPMessage & { attestation: string; }; @@ -41,6 +68,10 @@ export const isSignedCCTPMessage = ( typeof message === 'object' && 'message' in message && 'attestation' in message; +export const isSignedNttMessage = ( + message: SignedMessage, +): message is SignedNttMessage => + isSignedWormholeMessage(message) && isUnsignedNttMessage(message); export interface TransferInfoBaseParams { txData: ParsedMessage | ParsedRelayerMessage; @@ -70,3 +101,8 @@ export type TransferDestInfo = { }; export type TransferDisplayData = NestedRow[]; + +export interface RelayerFee { + fee: BigNumber; + feeToken: TokenId | 'native'; +} diff --git a/wormhole-connect/src/routes/utils.ts b/wormhole-connect/src/routes/utils.ts index 5e18be269..a05f1b8fc 100644 --- a/wormhole-connect/src/routes/utils.ts +++ b/wormhole-connect/src/routes/utils.ts @@ -16,7 +16,7 @@ import { } from 'utils/sdk'; import { getTokenById } from 'utils'; import { CHAIN_ID_ETH } from '@certusone/wormhole-sdk/lib/esm/utils'; -import { TokenConfig } from 'config/types'; +import { Route, TokenConfig } from 'config/types'; // adapts the sdk returned parsed message to the type that // wh connect uses @@ -87,5 +87,30 @@ export const isIlliquidDestToken = ( return true; } } + // Users should send USDC to Fantom via NTT instead of the token bridge + if ( + symbol === 'USDC' && + nativeChain === 'ethereum' && + destChain === 'fantom' + ) { + return true; + } + if ( + symbol === 'USDC' && + nativeChain === 'fuji' && + destChain === 'alfajores' + ) { + return true; + } + if ( + symbol === 'USDC.e' && + (nativeChain === 'fantom' || nativeChain === 'alfajores') + ) { + return true; + } return false; }; + +export const isNttRoute = (route?: Route) => { + return route === Route.NttManual || route === Route.NttRelay; +}; diff --git a/wormhole-connect/src/store/helpers.ts b/wormhole-connect/src/store/helpers.ts index b45915c13..f9ebd0a38 100644 --- a/wormhole-connect/src/store/helpers.ts +++ b/wormhole-connect/src/store/helpers.ts @@ -2,6 +2,7 @@ export type DataWrapper = { data: T | null; error: any | null; isFetching: boolean; + receivedAt: string | null; }; export function getEmptyDataWrapper() { @@ -9,6 +10,7 @@ export function getEmptyDataWrapper() { data: null, error: null, isFetching: false, + receivedAt: null, }; } @@ -17,6 +19,7 @@ export function receiveDataWrapper(data: T): DataWrapper { data, error: null, isFetching: false, + receivedAt: new Date().toISOString(), }; } @@ -25,6 +28,7 @@ export function errorDataWrapper(error: string): DataWrapper { data: null, error, isFetching: false, + receivedAt: null, }; } @@ -33,5 +37,6 @@ export function fetchDataWrapper() { data: null, error: null, isFetching: true, + receivedAt: null, }; } diff --git a/wormhole-connect/src/store/index.ts b/wormhole-connect/src/store/index.ts index bbb302436..ee8b8f65d 100644 --- a/wormhole-connect/src/store/index.ts +++ b/wormhole-connect/src/store/index.ts @@ -6,6 +6,7 @@ import routerReducer from './router'; import walletReducer from './wallet'; import porticoBridgeReducer from './porticoBridge'; import tokenPricesReducer from './tokenPrices'; +import nttReducer from './ntt'; export const store = configureStore({ reducer: { @@ -16,6 +17,7 @@ export const store = configureStore({ relay: relayReducer, porticoBridge: porticoBridgeReducer, tokenPrices: tokenPricesReducer, + ntt: nttReducer, }, }); diff --git a/wormhole-connect/src/store/ntt.ts b/wormhole-connect/src/store/ntt.ts new file mode 100644 index 000000000..1db443d63 --- /dev/null +++ b/wormhole-connect/src/store/ntt.ts @@ -0,0 +1,40 @@ +import { createSlice, PayloadAction } from '@reduxjs/toolkit'; +import { + DataWrapper, + getEmptyDataWrapper, + receiveDataWrapper, +} from './helpers'; +import { InboundQueuedTransfer } from 'routes/ntt/types'; + +export interface NttState { + inboundQueuedTransfer: DataWrapper; +} + +const initialState: NttState = { + inboundQueuedTransfer: getEmptyDataWrapper(), +}; + +export const nttSlice = createSlice({ + name: 'ntt', + initialState, + reducers: { + setInboundQueuedTransfer: ( + state: NttState, + { payload }: PayloadAction, + ) => { + state.inboundQueuedTransfer = receiveDataWrapper(payload); + }, + resetInboundQueuedTransfer: (state: NttState) => { + state.inboundQueuedTransfer = getEmptyDataWrapper(); + }, + clearNtt: () => initialState, + }, +}); + +export const { + setInboundQueuedTransfer, + resetInboundQueuedTransfer, + clearNtt, +} = nttSlice.actions; + +export default nttSlice.reducer; diff --git a/wormhole-connect/src/store/redeem.ts b/wormhole-connect/src/store/redeem.ts index 18e8db19d..71b1d99ea 100644 --- a/wormhole-connect/src/store/redeem.ts +++ b/wormhole-connect/src/store/redeem.ts @@ -1,7 +1,8 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import { ParsedMessage, ParsedRelayerMessage } from 'utils/sdk'; -import { SignedMessage, TransferDestInfo } from 'routes'; +import { UnsignedNttMessage, SignedMessage, TransferDestInfo } from 'routes'; import { Route } from 'config/types'; +import { DeliveryStatus } from '@certusone/wormhole-sdk/lib/esm/relayer'; export enum MessageType { BRIDGE = 1, @@ -9,7 +10,7 @@ export enum MessageType { } export interface RedeemState { - txData: ParsedMessage | ParsedRelayerMessage | undefined; + txData: ParsedMessage | ParsedRelayerMessage | UnsignedNttMessage | undefined; signedMessage: SignedMessage | undefined; sendTx: string; redeemTx: string; @@ -17,6 +18,7 @@ export interface RedeemState { isVaaEnqueued: boolean; route: Route | undefined; transferDestInfo: TransferDestInfo | undefined; + deliveryStatus: DeliveryStatus | undefined; } const initialState: RedeemState = { @@ -28,6 +30,7 @@ const initialState: RedeemState = { isVaaEnqueued: false, route: undefined, transferDestInfo: undefined, + deliveryStatus: undefined, }; export const redeemSlice = createSlice({ @@ -36,7 +39,11 @@ export const redeemSlice = createSlice({ reducers: { setTxDetails: ( state: RedeemState, - { payload }: PayloadAction, + { + payload, + }: PayloadAction< + ParsedMessage | ParsedRelayerMessage | UnsignedNttMessage + >, ) => { state.txData = payload; }, @@ -79,6 +86,12 @@ export const redeemSlice = createSlice({ ) => { state.signedMessage = payload; }, + setDeliveryStatus: ( + state: RedeemState, + { payload }: PayloadAction, + ) => { + state.deliveryStatus = payload; + }, }, }); @@ -92,6 +105,7 @@ export const { clearRedeem, setRoute, setSignedMessage, + setDeliveryStatus, } = redeemSlice.actions; export default redeemSlice.reducer; diff --git a/wormhole-connect/src/store/transferInput.ts b/wormhole-connect/src/store/transferInput.ts index c4aaf4ced..f4f14a412 100644 --- a/wormhole-connect/src/store/transferInput.ts +++ b/wormhole-connect/src/store/transferInput.ts @@ -20,6 +20,8 @@ import { receiveDataWrapper, } from './helpers'; import { isPorticoRoute } from 'routes/porticoBridge/utils'; +import { isNttRoute } from 'routes'; +import { getNttGroupKey, getNttTokenByGroupKey } from 'utils/ntt'; export type Balances = { [key: string]: string | null }; export type ChainBalances = { @@ -169,7 +171,7 @@ function getInitialState(): TransferInputState { } const performModificationsIfFromChainChanged = (state: TransferInputState) => { - const { fromChain, token, route } = state; + const { fromChain, token, route, destToken } = state; if (token) { const tokenConfig = config.tokens[token]; // clear token and amount if not supported on the selected network @@ -182,7 +184,14 @@ const performModificationsIfFromChainChanged = (state: TransferInputState) => { state.amount = ''; } } - if ( + if (isNttRoute(route) && destToken) { + const groupKey = getNttGroupKey(tokenConfig, config.tokens[destToken]); + if (groupKey && fromChain) { + state.token = getNttTokenByGroupKey(groupKey, fromChain)?.key || ''; + } else { + state.token = ''; + } + } else if ( tokenConfig.symbol === 'USDC' && tokenConfig.nativeChain !== fromChain ) { @@ -204,14 +213,24 @@ const performModificationsIfFromChainChanged = (state: TransferInputState) => { }; const performModificationsIfToChainChanged = (state: TransferInputState) => { - const { toChain, destToken, route } = state; + const { toChain, destToken, route, token } = state; if (destToken) { const tokenConfig = config.tokens[destToken]; if (!toChain) { state.destToken = ''; } - if (tokenConfig.symbol === 'USDC' && tokenConfig.nativeChain !== toChain) { + if (isNttRoute(route) && token) { + const groupKey = getNttGroupKey(tokenConfig, config.tokens[token]); + if (groupKey && toChain) { + state.destToken = getNttTokenByGroupKey(groupKey, toChain)?.key || ''; + } else { + state.destToken = ''; + } + } else if ( + tokenConfig.symbol === 'USDC' && + tokenConfig.nativeChain !== toChain + ) { state.destToken = getNativeVersionOfToken('USDC', toChain!); } else if ( tokenConfig.symbol === 'tBTC' && @@ -247,6 +266,8 @@ const establishRoute = (state: TransferInputState) => { Route.TBTC, Route.ETHBridge, Route.wstETHBridge, + Route.NttRelay, + Route.NttManual, Route.Relay, Route.Bridge, ]; diff --git a/wormhole-connect/src/utils/index.ts b/wormhole-connect/src/utils/index.ts index 9b9671319..fa69975f1 100644 --- a/wormhole-connect/src/utils/index.ts +++ b/wormhole-connect/src/utils/index.ts @@ -1,5 +1,5 @@ import { useEffect, useRef } from 'react'; -import { BigNumber, BigNumberish, utils } from 'ethers'; +import { BigNumber, BigNumberish, ethers, utils } from 'ethers'; import { isHexString } from 'ethers/lib/utils.js'; import { isValidTransactionDigest, SUI_TYPE_ARG } from '@mysten/sui.js'; import { @@ -321,3 +321,20 @@ export const calculateUSDPrice = ( } return ''; }; + +export const tryParseErrorMessage = ( + iface: ethers.utils.Interface, + error: any, +): string | undefined => { + const errorData = error?.error?.data?.originalError?.data; + if (!errorData) return ''; + try { + return iface.parseError(errorData)?.name || undefined; + } catch { + return undefined; + } +}; + +export const removeDust = (amount: BigNumber, decimals: number): BigNumber => { + return deNormalizeAmount(normalizeAmount(amount, decimals), decimals); +}; diff --git a/wormhole-connect/src/utils/ntt.ts b/wormhole-connect/src/utils/ntt.ts new file mode 100644 index 000000000..d86737e85 --- /dev/null +++ b/wormhole-connect/src/utils/ntt.ts @@ -0,0 +1,124 @@ +import config from 'config'; +import { NttManagerConfig, TokenConfig } from 'config/types'; +import { ChainName } from '@wormhole-foundation/wormhole-connect-sdk'; +import { isEqualCaseInsensitive } from 'utils'; + +export const isNttToken = (token: TokenConfig): boolean => { + if (!token) return false; + return Object.values(config.nttGroups).some((group) => + group.nttManagers.some( + (manager) => + manager.tokenKey === token.key && + manager.chainName === token.nativeChain, + ), + ); +}; + +export const isNttTokenPair = ( + token1: TokenConfig, + token2: TokenConfig, +): boolean => { + if (!token1 || !token2) return false; + // Find the groups that token1 belongs to + const token1Groups = Object.values(config.nttGroups).filter((group) => + group.nttManagers.some( + (manager) => + manager.tokenKey === token1.key && + manager.chainName === token1.nativeChain, + ), + ); + // then check if token2 belongs to any of those groups + return token1Groups.some((group) => + group.nttManagers.some( + (manager) => + manager.tokenKey === token2.key && + manager.chainName === token2.nativeChain, + ), + ); +}; + +export const getNttManagerConfig = ( + token: TokenConfig, + groupKey: string, +): NttManagerConfig | undefined => { + if (!token) return; + const group = config.nttGroups[groupKey]; + if (!group) return; + return group.nttManagers.find( + (manager) => + manager.tokenKey === token.key && manager.chainName === token.nativeChain, + ); +}; + +export const getNttManagerAddress = ( + token: TokenConfig, + groupKey: string, +): string | undefined => { + return getNttManagerConfig(token, groupKey)?.address; +}; + +export const getNttGroupKey = ( + token1: TokenConfig, + token2: TokenConfig, +): string | undefined => { + if (!token1 || !token2) return; + const [key] = + Object.entries(config.nttGroups).find( + ([, group]) => + group.nttManagers.some( + (manager) => + manager.tokenKey === token1.key && + manager.chainName === token1.nativeChain, + ) && + group.nttManagers.some( + (manager) => + manager.tokenKey === token2.key && + manager.chainName === token2.nativeChain, + ), + ) || []; + return key; +}; + +export const getNttGroupKeyByAddress = ( + managerAddress: string, + chainName: ChainName, +): string | undefined => { + return Object.entries(config.nttGroups).find(([, group]) => + group.nttManagers.some( + (manager) => + isEqualCaseInsensitive(manager.address, managerAddress) && + manager.chainName === chainName, + ), + )?.[0]; +}; + +export const getNttManagerConfigByAddress = ( + managerAddress: string, + chainName: ChainName, +): NttManagerConfig | undefined => { + return Object.values(config.nttGroups).flatMap((group) => + group.nttManagers.filter( + (manager) => + isEqualCaseInsensitive(manager.address, managerAddress) && + manager.chainName === chainName, + ), + )?.[0]; +}; + +export const getNttManagerConfigByGroupKey = ( + groupKey: string, + chainName: ChainName, +): NttManagerConfig | undefined => { + return config.nttGroups[groupKey]?.nttManagers.find( + (manager) => manager.chainName === chainName, + ); +}; + +export const getNttTokenByGroupKey = ( + groupKey: string, + chainName: ChainName, +): TokenConfig | undefined => { + const manager = getNttManagerConfigByGroupKey(groupKey, chainName); + if (!manager) return; + return config.tokens[manager.tokenKey]; +}; diff --git a/wormhole-connect/src/utils/transferValidation.ts b/wormhole-connect/src/utils/transferValidation.ts index 1db589504..ea1c95a91 100644 --- a/wormhole-connect/src/utils/transferValidation.ts +++ b/wormhole-connect/src/utils/transferValidation.ts @@ -24,6 +24,7 @@ import { PorticoBridgeState } from 'store/porticoBridge'; import { DataWrapper } from 'store/helpers'; import { CCTPManual_CHAINS as CCTP_CHAINS } from 'routes/cctpManual'; import { CCTP_MAX_TRANSFER_LIMIT } from 'consts'; +import { isNttRoute } from 'routes'; export const validateFromChain = ( chain: ChainName | undefined, @@ -180,7 +181,8 @@ export const validateSolanaTokenAccount = ( route: Route | undefined, ): ValidationErr => { if (destChain !== 'solana') return ''; - if (route === Route.Relay || route === Route.TBTC) return ''; + if (route === Route.Relay || route === Route.TBTC || isNttRoute(route)) + return ''; if (!destTokenAddr) return ''; if (destTokenAddr && !solanaTokenAccount) { return 'The associated token account for this asset does not exist on Solana, you must create it first'; @@ -312,6 +314,10 @@ export const validateAll = async ( }; if (isAutomatic) { + if (route === Route.NttRelay) { + // Ntt does not support native gas drop-off + return baseValidations; + } return { ...baseValidations, toNativeToken: validateToNativeAmt(toNativeToken, maxSwapAmt), diff --git a/wormhole-connect/src/utils/vaa.ts b/wormhole-connect/src/utils/vaa.ts index 682f18803..a4d241249 100644 --- a/wormhole-connect/src/utils/vaa.ts +++ b/wormhole-connect/src/utils/vaa.ts @@ -45,6 +45,19 @@ export async function getUnsignedVaaEvm( sequence: BigNumberish; payload: string; }> { + const bridgeLog = await getWormholeLogEvm(chain, receipt); + const parsed = Implementation__factory.createInterface().parseLog(bridgeLog); + return { + emitterAddress: parsed.args.sender, + sequence: parsed.args.sequence, + payload: parsed.args.payload.toString('hex'), + }; +} + +export async function getWormholeLogEvm( + chain: ChainId | ChainName, + receipt: providers.TransactionReceipt, +): Promise { if (!isEvmChain(chain)) { throw new Error('Not an evm chain'); } @@ -52,20 +65,10 @@ export async function getUnsignedVaaEvm( const bridgeLogs = receipt.logs.filter((l: any) => { return l.address === core; }); - if (bridgeLogs.length === 0) { throw new Error(NO_VAA_FOUND); } - - const parsed = Implementation__factory.createInterface().parseLog( - bridgeLogs[0], - ); - - return { - emitterAddress: parsed.args.sender, - sequence: parsed.args.sequence, - payload: parsed.args.payload.toString('hex'), - }; + return bridgeLogs[0]; } export function getEmitterAndSequence( @@ -89,9 +92,20 @@ export function getEmitterAndSequence( export async function fetchVaa( txData: ParsedMessage | ParsedRelayerMessage, -): Promise { + bytesOnly: true, +): Promise; + +export async function fetchVaa( + txData: ParsedMessage | ParsedRelayerMessage, + bytesOnly?: false, +): Promise; + +export async function fetchVaa( + txData: ParsedMessage | ParsedRelayerMessage, + bytesOnly = false, +): Promise { try { - const vaa = await fetchVaaWormscan(txData); + const vaa = await fetchVaaWormscan(txData, bytesOnly); if (vaa === undefined) { console.warn('VAA not found in Wormscan'); @@ -102,13 +116,14 @@ export async function fetchVaa( 'Error fetching VAA from wormscan. Falling back to guardian.', e, ); - return await fetchVaaGuardian(txData); + return await fetchVaaGuardian(txData, bytesOnly); } } export async function fetchVaaWormscan( txData: ParsedMessage | ParsedRelayerMessage, -): Promise { + bytesOnly: boolean, +): Promise { // return if the number of block confirmations hasn't been met const chainName = config.wh.toChainName(txData.fromChain); const { finalityThreshold } = config.chains[chainName]! as any; @@ -127,6 +142,7 @@ export async function fetchVaaWormscan( if (!response.data.data) return; const data = response.data.data; const vaa = utils.base64.decode(data.vaa); + if (bytesOnly) return vaa; const parsed = parseTokenTransferVaa(vaa); const vaaData: ParsedVaa = { @@ -161,7 +177,8 @@ export async function fetchVaaWormscan( export async function fetchVaaGuardian( txData: ParsedMessage | ParsedRelayerMessage, -): Promise { + bytesOnly: boolean, +): Promise { // return if the number of block confirmations hasn't been met const chainName = config.wh.toChainName(txData.fromChain); const { finalityThreshold } = config.chains[chainName]! as any; @@ -192,6 +209,9 @@ export async function fetchVaaGuardian( if (!vaa) { throw new Error('Failed to fetch VAA from all hosts'); } + if (bytesOnly) { + return vaa; + } const parsed = parseTokenTransferVaa(vaa); diff --git a/wormhole-connect/src/utils/wallet/index.ts b/wormhole-connect/src/utils/wallet/index.ts index 9f635772f..409f15eba 100644 --- a/wormhole-connect/src/utils/wallet/index.ts +++ b/wormhole-connect/src/utils/wallet/index.ts @@ -107,6 +107,7 @@ export const signAndSendTransaction = async ( chain: ChainName, transaction: SendResult, walletType: TransferWallet, + options: any = {}, ): Promise => { const chainConfig = config.chains[chain]!; @@ -121,7 +122,7 @@ export const signAndSendTransaction = async ( } case Context.SOLANA: { const { signAndSendTransaction } = await import('utils/wallet/solana'); - const tx = await signAndSendTransaction(transaction, wallet); + const tx = await signAndSendTransaction(transaction, wallet, options); return tx.id; } case Context.SUI: { diff --git a/wormhole-connect/src/views/Bridge/Bridge.tsx b/wormhole-connect/src/views/Bridge/Bridge.tsx index 7cdcc6441..03a79d623 100644 --- a/wormhole-connect/src/views/Bridge/Bridge.tsx +++ b/wormhole-connect/src/views/Bridge/Bridge.tsx @@ -47,6 +47,8 @@ import { wstETHBridge } from 'routes/porticoBridge/wstETHBridge'; import { usePorticoSwapInfo } from 'hooks/usePorticoSwapInfo'; import { usePorticoRelayerFee } from 'hooks/usePorticoRelayerFee'; import { useFetchTokenPrices } from 'hooks/useFetchTokenPrices'; +import NttInboundCapacityWarning from './NttInboundCapacityWarning'; +import { isNttRoute } from 'routes/utils'; const useStyles = makeStyles()((_theme) => ({ spacer: { @@ -321,7 +323,7 @@ function Bridge() { - + {isNttRoute(route) && } diff --git a/wormhole-connect/src/views/Bridge/Inputs/TokenWarnings.tsx b/wormhole-connect/src/views/Bridge/Inputs/TokenWarnings.tsx index e30c9efae..53d755e4a 100644 --- a/wormhole-connect/src/views/Bridge/Inputs/TokenWarnings.tsx +++ b/wormhole-connect/src/views/Bridge/Inputs/TokenWarnings.tsx @@ -18,6 +18,7 @@ import AlertBanner from 'components/AlertBanner'; import RouteOperator from 'routes/operator'; import { Route } from '../../../config/types'; import { CCTPManual_CHAINS as CCTP_CHAINS } from 'routes/cctpManual'; +import { isNttRoute } from 'routes'; const useStyles = makeStyles()((theme: any) => ({ associatedTokenWarning: { @@ -132,6 +133,7 @@ function TokenWarnings() { route, tokenId, toChain, + destTokenConfig, ); if (!active) return; @@ -147,7 +149,7 @@ function TokenWarnings() { return () => { active = false; }; - }, [toChain, tokenConfig, route, dispatch]); + }, [toChain, tokenConfig, route, destTokenConfig, dispatch]); // the associated token account address is deterministic, so we still // need to check if there is an account created for that address @@ -244,7 +246,13 @@ function TokenWarnings() { if (!toChain || !token || !receiving.address) return; // The tBTC associated token account will be created if it doesn't exist in the redeem tx - if (toChain === 'solana' && foreignAsset && route !== Route.TBTC) { + // The NTT ATA will be created if it doesn't exist in the redeem tx + if ( + toChain === 'solana' && + foreignAsset && + route !== Route.TBTC && + !isNttRoute(route) + ) { checkSolanaAssociatedTokenAccount(); } if (usdcAndNoCCTP) { diff --git a/wormhole-connect/src/views/Bridge/NttInboundCapacityWarning.tsx b/wormhole-connect/src/views/Bridge/NttInboundCapacityWarning.tsx new file mode 100644 index 000000000..dd84484a9 --- /dev/null +++ b/wormhole-connect/src/views/Bridge/NttInboundCapacityWarning.tsx @@ -0,0 +1,118 @@ +import React, { useEffect, useMemo, useState } from 'react'; +import AlertBanner from 'components/AlertBanner'; +import { NttManual } from 'routes/ntt'; +import { parseUnits } from 'ethers/lib/utils'; +import { getTokenDecimals } from 'utils'; +import { BigNumber } from 'ethers'; +import { useSelector } from 'react-redux'; +import { RootState } from 'store'; +import config from 'config'; +import { getNttGroupKey, getNttManagerAddress, isNttToken } from 'utils/ntt'; + +function formatDuration(seconds: number) { + if (seconds < 60) { + return seconds === 1 ? `${seconds} second` : `${seconds} seconds`; + } else if (seconds < 3600) { + const minutes = Math.floor(seconds / 60); + return minutes === 1 ? `${minutes} minute` : `${minutes} minutes`; + } else { + const hours = Math.floor(seconds / 3600); + return hours === 1 ? `${hours} hour` : `${hours} hours`; + } +} + +const NttInboundCapacityWarning = () => { + const { fromChain, toChain, token, destToken, receiveAmount } = useSelector( + (state: RootState) => state.transferInput, + ); + const amount = receiveAmount.data || '0'; + const [capacity, setCapacity] = useState(undefined); + const [duration, setDuration] = useState(0); + const groupKey = getNttGroupKey( + config.tokens[token], + config.tokens[destToken], + ); + const nttManagerAddress = + groupKey && getNttManagerAddress(config.tokens[destToken], groupKey); + + useEffect(() => { + if (!toChain || !nttManagerAddress || !fromChain) return; + let active = true; + const fetchCapacity = async () => { + try { + const ntt = new NttManual(); + const duration = await ntt.getRateLimitDuration( + toChain, + nttManagerAddress, + ); + // if the rate limit duration 0, then rate limiting is disabled + if (duration === 0) { + if (active) { + setCapacity(undefined); + setDuration(0); + } + return; + } + const capacity = await ntt.getCurrentInboundCapacity( + toChain, + nttManagerAddress, + fromChain, + ); + if (active) { + setCapacity(capacity ? BigNumber.from(capacity) : undefined); + setDuration(duration); + } + } catch (error) { + console.error('Failed to fetch capacity:', error); + if (active) { + setCapacity(undefined); + setDuration(0); + } + } + }; + fetchCapacity(); + return () => { + active = false; + }; + }, [toChain, nttManagerAddress, fromChain]); + + const showWarning = useMemo(() => { + if ( + !destToken || + !amount || + !toChain || + !capacity || + !duration || + !fromChain || + !groupKey + ) + return false; + const destTokenConfig = config.tokens[destToken]; + if (!destTokenConfig || !isNttToken(destTokenConfig)) return false; + // capacity is in destination token decimals, so we need to convert the amount to the same decimals + const decimals = getTokenDecimals( + config.wh.toChainId(toChain), + destTokenConfig.tokenId, + ); + const parsedAmount = parseUnits(Number(amount).toFixed(decimals), decimals); + const threshold = capacity.mul(95).div(100); // 95% of capacity + return parsedAmount.gt(threshold); + }, [destToken, amount, toChain, groupKey]); + + if (!showWarning || !toChain) return null; + + const content = ( + <> + {`Your transfer to ${ + config.chains[toChain]?.displayName || 'UNKNOWN' + } may be delayed due to rate limits set by ${ + config.tokens[destToken]?.symbol || 'UNKNOWN' + }. If your transfer is delayed, you will need to return after ${formatDuration( + duration, + )} to complete the transfer. Please consider this before proceeding.`} + + ); + return ; +}; + +export default NttInboundCapacityWarning; diff --git a/wormhole-connect/src/views/Bridge/Preview.tsx b/wormhole-connect/src/views/Bridge/Preview.tsx index 631c7675e..497882c63 100644 --- a/wormhole-connect/src/views/Bridge/Preview.tsx +++ b/wormhole-connect/src/views/Bridge/Preview.tsx @@ -109,17 +109,16 @@ function Preview(props: { collapsed: boolean }) { const tokenConfig = token && config.tokens[token]; if (!tokenConfig) return; - const fee = await RouteOperator.getRelayerFee( + const result = await RouteOperator.getRelayerFee( route, fromChain, toChain, token, destToken, ); - const decimals = getTokenDecimals( - toChainId(fromChain), - tokenConfig.tokenId || 'native', - ); + if (result === null) return; + const { fee, feeToken } = result; + const decimals = getTokenDecimals(toChainId(fromChain), feeToken); const formattedFee = Number.parseFloat(toDecimals(fee, decimals, 6)); dispatch(setRelayerFee(formattedFee)); } catch (e: any) { diff --git a/wormhole-connect/src/views/Bridge/Send.tsx b/wormhole-connect/src/views/Bridge/Send.tsx index 62e98b469..c6b19da06 100644 --- a/wormhole-connect/src/views/Bridge/Send.tsx +++ b/wormhole-connect/src/views/Bridge/Send.tsx @@ -39,6 +39,12 @@ import { validateSolanaTokenAccount } from '../../utils/transferValidation'; import { useDebounce } from 'use-debounce'; import { isPorticoRoute } from 'routes/porticoBridge/utils'; import { SWAP_ERROR } from 'routes/porticoBridge/consts'; +import { + DestinationContractIsPausedError, + NotEnoughCapacityError, + ContractIsPausedError, + UnsupportedContractAbiVersion, +} from 'routes/ntt/errors'; const useStyles = makeStyles()((theme) => ({ body: { @@ -102,12 +108,12 @@ function Send(props: { valid: boolean }) { try { const fromConfig = config.chains[fromChain!]; if (fromConfig?.context === Context.ETH) { - registerWalletSigner(fromChain!, TransferWallet.SENDING); const chainId = fromConfig.chainId; if (typeof chainId !== 'number') { throw new Error('invalid evm chain ID'); } await switchChain(chainId, TransferWallet.SENDING); + registerWalletSigner(fromChain!, TransferWallet.SENDING); } if (fromConfig?.context === Context.COSMOS) { await switchChain(fromConfig.chainId, TransferWallet.SENDING); @@ -159,6 +165,15 @@ function Send(props: { valid: boolean }) { ? 'Error due to insufficient token allowance, please try again' : e?.message === SWAP_ERROR ? SWAP_ERROR + : e?.message === NotEnoughCapacityError.MESSAGE + ? `This transfer would be rate-limited due to high volume on ${ + config.chains[transferInput.fromChain!]?.displayName + }, please try again later` + : e?.message === ContractIsPausedError.MESSAGE || + e?.message === DestinationContractIsPausedError.MESSAGE + ? e.message + : e?.message === UnsupportedContractAbiVersion.MESSAGE + ? 'Unsupported contract ABI version' : 'Error with transfer, please try again', ); } diff --git a/wormhole-connect/src/views/Redeem/NttInboundQueued.tsx b/wormhole-connect/src/views/Redeem/NttInboundQueued.tsx new file mode 100644 index 000000000..ce58c17d1 --- /dev/null +++ b/wormhole-connect/src/views/Redeem/NttInboundQueued.tsx @@ -0,0 +1,216 @@ +import InputContainer from 'components/InputContainer'; +import React, { useCallback, useEffect, useState } from 'react'; +import { useSelector } from 'react-redux'; +import { RootState } from 'store'; +import { isSignedNttMessage as isSignedNttMessage } from 'routes'; +import { NttManual, NttRelay } from 'routes/ntt'; +import Header from './Header'; +import { useDispatch } from 'react-redux'; +import Button from 'components/Button'; +import CircularProgress from '@mui/material/CircularProgress'; +import { setInboundQueuedTransfer } from 'store/ntt'; +import { + TransferWallet, + registerWalletSigner, + switchChain, +} from 'utils/wallet'; +import { Context } from '@wormhole-foundation/wormhole-connect-sdk'; +import WalletsModal from '../WalletModal'; +import AlertBanner from 'components/AlertBanner'; +import Spacer from 'components/Spacer'; +import { Route } from 'config/types'; +import { + InboundQueuedTransferNotFoundError, + InboundQueuedTransferStillQueuedError, + ContractIsPausedError, +} from 'routes/ntt/errors'; +import { setRedeemTx, setTransferComplete } from 'store/redeem'; +import { OPACITY } from 'utils/style'; +import { useTheme } from '@mui/material'; +import config from 'config'; + +const NttInboundQueued = () => { + const dispatch = useDispatch(); + const theme: any = useTheme(); + const route = useSelector((state: RootState) => state.redeem.route); + const wallet = useSelector((state: RootState) => state.wallet.receiving); + const signedMessage = useSelector( + (state: RootState) => state.redeem.signedMessage, + )!; + const inboundQueuedTransfer = useSelector( + (state: RootState) => state.ntt.inboundQueuedTransfer, + ); + + const checkConnection = useCallback(() => { + if (!isSignedNttMessage(signedMessage)) return; + const addr = wallet.address.toLowerCase(); + const curAddr = wallet.currentAddress.toLowerCase(); + return addr === curAddr; + }, [wallet, signedMessage]); + + const [inProgress, setInProgress] = useState(false); + const [sendError, setSendError] = useState(''); + const [isConnected, setIsConnected] = useState(checkConnection()); + const [openWalletModal, setWalletModal] = useState(false); + const [expired, setExpired] = useState(false); + const [rateLimitExpiry, setRateLimitExpiry] = useState(''); + + useEffect(() => { + if (!inboundQueuedTransfer.data) { + setExpired(true); + setRateLimitExpiry(''); + return; + } + const expiry = new Date( + inboundQueuedTransfer.data.rateLimitExpiryTimestamp * 1000, + ); + setRateLimitExpiry(expiry.toLocaleString()); + const now = new Date(); + if (now < expiry) { + setExpired(false); + const timeoutId = setTimeout( + () => setExpired(true), + expiry.getTime() - now.getTime(), + ); + return () => clearTimeout(timeoutId); + } else { + setExpired(true); + } + }, [inboundQueuedTransfer]); + + const connect = () => { + setWalletModal(true); + }; + + useEffect(() => { + setIsConnected(checkConnection()); + }, [wallet, checkConnection]); + + const handleClick = useCallback(async () => { + if (!isSignedNttMessage(signedMessage)) return; + const { toChain, recipientNttManager, messageDigest, recipient } = + signedMessage; + setInProgress(true); + const nttRoute = + route === Route.NttManual ? new NttManual() : new NttRelay(); + let tx: string | undefined; + try { + const toConfig = config.chains[toChain]!; + if (toConfig?.context === Context.ETH) { + await switchChain(toConfig.chainId, TransferWallet.RECEIVING); + registerWalletSigner(toChain, TransferWallet.RECEIVING); + } + tx = await nttRoute.completeInboundQueuedTransfer( + toChain, + recipientNttManager, + messageDigest, + recipient, + wallet.address, + ); + } catch (e: any) { + switch (e.message) { + case InboundQueuedTransferNotFoundError.MESSAGE: + case InboundQueuedTransferStillQueuedError.MESSAGE: + case ContractIsPausedError.MESSAGE: + setSendError(e.message); + break; + default: + setSendError('Error with transfer, please try again'); + break; + } + console.error(e); + } + if (tx !== undefined) { + try { + // Check that the transfer is not still queued + const inboundQueuedTransfer = await nttRoute.getInboundQueuedTransfer( + toChain, + recipientNttManager, + messageDigest, + ); + if (!inboundQueuedTransfer) { + dispatch(setInboundQueuedTransfer(undefined)); + dispatch(setRedeemTx(tx)); + const isTransferCompleted = await nttRoute.isTransferCompleted( + toChain, + signedMessage, + ); + dispatch(setTransferComplete(isTransferCompleted)); + } + } catch (e) { + console.error(e); + } + } + setInProgress(false); + }, [signedMessage, wallet, route]); + + return ( + <> + + <> +
+ {!expired ? ( +
+ {`Your transfer to ${ + config.chains[signedMessage.toChain]?.displayName || 'UNKNOWN' + } is delayed due to rate limits configured by ${ + config.tokens[signedMessage.receivedTokenKey]?.symbol || + 'UNKNOWN' + }. After the delay ends on ${ + rateLimitExpiry || 'UNKNOWN' + }, you will need to return to submit a new transaction to complete your transfer.`} +
+ ) : ( +
+ {`Your transfer to ${ + config.chains[signedMessage.toChain]?.displayName || 'UNKNOWN' + } was delayed due to rate limits configured by ${ + config.tokens[signedMessage.receivedTokenKey]?.symbol || + 'UNKNOWN' + }. You will need to submit a new transaction to complete your transfer.`} +
+ )} + + + + + {wallet.address ? ( + isConnected ? ( + + ) : ( + + ) + ) : ( + + )} + {openWalletModal && ( + setWalletModal(false)} + /> + )} + + ); +}; + +export default NttInboundQueued; diff --git a/wormhole-connect/src/views/Redeem/NttStepper.tsx b/wormhole-connect/src/views/Redeem/NttStepper.tsx new file mode 100644 index 000000000..7eba7c30d --- /dev/null +++ b/wormhole-connect/src/views/Redeem/NttStepper.tsx @@ -0,0 +1,63 @@ +import * as React from 'react'; +import { useSelector } from 'react-redux'; +import { RootState } from 'store'; +import Stepper from 'components/Stepper/Stepper'; +import SendFrom from './SendFrom'; +import SendTo from './SendTo'; +import BridgeComplete from './BridgeComplete'; +import NttInboundQueued from './NttInboundQueued'; +import RelayerDeliveryFailed from './RelayerDeliveryFailed'; +import { DeliveryStatus } from '@certusone/wormhole-sdk/lib/esm/relayer'; + +const SEND_FROM_STEP = 1; +const SEND_TO_STEP = 2; +const TRANSACTION_COMPLETE_STEP = 4; + +export default function NttStepper() { + const signedMessage = useSelector( + (state: RootState) => state.redeem.signedMessage, + ); + const deliveryStatus = useSelector( + (state: RootState) => state.redeem.deliveryStatus, + ); + const transferComplete = useSelector( + (state: RootState) => state.redeem.transferComplete, + ); + const inboundQueuedTransfer = useSelector( + (state: RootState) => state.ntt.inboundQueuedTransfer, + ); + + const deliveryFailed = deliveryStatus === DeliveryStatus.ReceiverFailure; + const isInboundQueued = !!inboundQueuedTransfer.data; + const showWarning = isInboundQueued || deliveryFailed; + + const activeStep = transferComplete + ? TRANSACTION_COMPLETE_STEP + : signedMessage || deliveryFailed + ? SEND_TO_STEP + : SEND_FROM_STEP; + + const steps = [ + { + label: 'Send from', + component: , + warningLine: showWarning, + }, + { + label: 'Send to', + component: deliveryFailed ? ( + + ) : isInboundQueued ? ( + + ) : ( + + ), + warningLabel: showWarning, + }, + { + label: 'Transaction complete', + component: , + }, + ]; + return ; +} diff --git a/wormhole-connect/src/views/Redeem/Redeem.tsx b/wormhole-connect/src/views/Redeem/Redeem.tsx index 0539053ec..40b42f6ac 100644 --- a/wormhole-connect/src/views/Redeem/Redeem.tsx +++ b/wormhole-connect/src/views/Redeem/Redeem.tsx @@ -10,7 +10,7 @@ import { } from 'store/redeem'; import { sleep } from 'utils'; import { fetchIsVAAEnqueued } from 'utils/vaa'; -import { SignedMessage } from 'routes'; +import { SignedMessage, isNttRoute } from 'routes'; import RouteOperator from 'routes/operator'; import { ParsedMessage, ParsedRelayerMessage } from 'utils/sdk'; @@ -18,8 +18,11 @@ import PageHeader from 'components/PageHeader'; import Spacer from 'components/Spacer'; import ChainsTag from './Tag'; import Stepper from './Stepper'; +import NttStepper from './NttStepper'; import GovernorEnqueuedWarning from './GovernorEnqueuedWarning'; import config from 'config'; +import useDeliveryStatus from 'hooks/useDeliveryStatus'; +import useCheckInboundQueuedTransfer from 'hooks/useCheckInboundQueuedTransfer'; import useConfirmBeforeLeaving from 'utils/confirmBeforeLeaving'; @@ -50,7 +53,8 @@ function Redeem({ if ( !txData?.sendTx || !txData.emitterAddress || // no VAA exists, e.g. CCTP route - !!signedMessage // if we have the VAA, then it's not enqueued + !!signedMessage || // if we have the VAA, then it's not enqueued + isNttRoute(route) // NTT route doesn't use token bridge / governor ) { return; } @@ -72,7 +76,7 @@ function Redeem({ return () => { cancelled = true; }; - }, [txData, signedMessage, setIsVaaEnqueued]); + }, [txData, signedMessage, route, setIsVaaEnqueued]); // fetch the VAA useEffect(() => { @@ -140,6 +144,9 @@ function Redeem({ }; }, [route, txData, transferComplete, setTransferComplete, signedMessage]); + useCheckInboundQueuedTransfer(); + useDeliveryStatus(); + return txData?.fromChain ? (
- + {isNttRoute(route) ? : }
) : ( <> diff --git a/wormhole-connect/src/views/Redeem/RelayerDeliveryFailed.tsx b/wormhole-connect/src/views/Redeem/RelayerDeliveryFailed.tsx new file mode 100644 index 000000000..4b10be521 --- /dev/null +++ b/wormhole-connect/src/views/Redeem/RelayerDeliveryFailed.tsx @@ -0,0 +1,63 @@ +import React from 'react'; +import { getChainConfig } from 'utils'; +import { makeStyles } from 'tss-react/mui'; +import InputContainer from 'components/InputContainer'; +import { OPACITY } from 'utils/style'; +import { Typography, useTheme } from '@mui/material'; +import { RootState } from 'store'; +import { useSelector } from 'react-redux'; +import Header from './Header'; + +const useStyles = makeStyles()((theme: any) => ({ + root: { + display: 'flex', + flexDirection: 'column', + gap: '8px', + marginTop: theme.spacing(1), + }, + link: { + color: theme.palette.text.primary, + textDecoration: 'underline', + cursor: 'pointer', + }, + revertString: { + wordWrap: 'break-word', + }, +})); + +const RelayerDeliveryFailed = () => { + const { classes } = useStyles(); + const theme: any = useTheme(); + const txData = useSelector((state: RootState) => state.redeem.txData)!; + const redeemTx = useSelector((state: RootState) => state.redeem.redeemTx); + const chainDisplayName = getChainConfig(txData.toChain).displayName; + return ( + +
+
+ + {`Your transfer failed to be delivered on ${chainDisplayName}.`} + + + Please reach out to the Wormhole community on{' '} + + Discord + {' '} + if you have questions. + +
+ + ); +}; + +export default RelayerDeliveryFailed; diff --git a/wormhole-connect/src/views/Redeem/SendTo.tsx b/wormhole-connect/src/views/Redeem/SendTo.tsx index bf3c6eced..d6d46681d 100644 --- a/wormhole-connect/src/views/Redeem/SendTo.tsx +++ b/wormhole-connect/src/views/Redeem/SendTo.tsx @@ -34,6 +34,9 @@ import { AssociatedTokenWarning } from '../Bridge/Inputs/TokenWarnings'; import { Route } from 'config/types'; import SwitchToManualClaim from './SwitchToManualClaim'; import { isPorticoRoute } from 'routes/porticoBridge/utils'; +import { isNttRoute, isSignedNttMessage } from 'routes'; +import { ContractIsPausedError, NttBase } from 'routes/ntt'; +import { setInboundQueuedTransfer } from 'store/ntt'; function AssociatedTokenAlert() { const dispatch = useDispatch(); @@ -196,32 +199,71 @@ function SendTo() { setClaimError('Your claim has failed, please try again'); throw new Error('invalid destination chain'); } + let txId: string | undefined; try { if ( chainConfig!.context === Context.ETH && typeof chainConfig.chainId === 'number' ) { - registerWalletSigner(txData.toChain, TransferWallet.RECEIVING); await switchChain(chainConfig.chainId, TransferWallet.RECEIVING); + registerWalletSigner(txData.toChain, TransferWallet.RECEIVING); } if (!signedMessage) { throw new Error('failed to get vaa, cannot redeem'); } - const txId = await RouteOperator.redeem( + txId = await RouteOperator.redeem( routeName, txData.toChain, signedMessage, wallet.address, ); - dispatch(setRedeemTx(txId)); - dispatch(setTransferComplete(true)); setInProgress(false); setClaimError(''); - } catch (e) { + } catch (e: any) { + if (e.message === ContractIsPausedError.MESSAGE) { + setClaimError('The contract is paused, please try again later'); + } else { + setClaimError('Your claim has failed, please try again'); + } setInProgress(false); - setClaimError('Your claim has failed, please try again'); console.error(e); } + if (txId !== undefined) { + const route = RouteOperator.getRoute(routeName); + if ( + isNttRoute(route.TYPE) && + signedMessage && + isSignedNttMessage(signedMessage) + ) { + // The redeem may have resulted in the transfer being inbound queued + // so we need to check that before we set the transfer as complete + try { + const nttRoute = route as NttBase; + const inboundQueuedTransfer = await nttRoute.getInboundQueuedTransfer( + txData.toChain, + signedMessage.recipientNttManager, + signedMessage.messageDigest, + ); + if (inboundQueuedTransfer) { + dispatch(setInboundQueuedTransfer(inboundQueuedTransfer)); + } else { + const isTransferCompleted = await nttRoute.isTransferCompleted( + txData.toChain, + signedMessage, + ); + if (isTransferCompleted) { + dispatch(setRedeemTx(txId)); + dispatch(setTransferComplete(true)); + } + } + } catch (e) { + console.error(e); + } + } else { + dispatch(setRedeemTx(txId)); + dispatch(setTransferComplete(true)); + } + } }; // sometimes the ATA might be closed even after the transfer began