clients/js: attempt sdkV2 migration

This commit is contained in:
Paul Noel 2024-06-12 12:58:02 -05:00
parent 33b2fbe72a
commit 0a55a85291
55 changed files with 8354 additions and 2011 deletions

View File

@ -244,7 +244,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: "16"
node-version: "20"
- run: |
cd clients/js && make test
make docs

View File

@ -248,16 +248,7 @@ Positionals:
Options:
--help Show help [boolean]
--version Show version number [boolean]
-c, --chain chain name
[choices: "unset", "solana", "ethereum", "terra", "bsc", "polygon",
"avalanche", "oasis", "algorand", "aurora", "fantom", "karura", "acala",
"klaytn", "celo", "near", "moonbeam", "neon", "terra2", "injective",
"osmosis", "sui", "aptos", "arbitrum", "optimism", "gnosis", "pythnet",
"xpla", "btc", "base", "sei", "rootstock", "scroll", "mantle", "blast",
"xlayer", "linea", "berachain", "seievm", "wormchain", "cosmoshub", "evmos",
"kujira", "neutron", "celestia", "stargaze", "seda", "dymension",
"provenance", "sepolia", "arbitrum_sepolia", "base_sepolia",
"optimism_sepolia", "holesky", "polygon_sepolia"]
-c, --chain chain name [string]
-n, --network Network
[required] [choices: "mainnet", "testnet", "devnet"]
-a, --contract-address Contract to submit VAA to (override config) [string]
@ -307,26 +298,8 @@ Options:
Options:
--help Show help [boolean]
--version Show version number [boolean]
--src-chain source chain
[required] [choices: "solana", "ethereum", "terra", "bsc", "polygon",
"avalanche", "oasis", "algorand", "aurora", "fantom", "karura", "acala",
"klaytn", "celo", "near", "moonbeam", "neon", "terra2", "injective",
"osmosis", "sui", "aptos", "arbitrum", "optimism", "gnosis", "pythnet",
"xpla", "btc", "base", "sei", "rootstock", "scroll", "mantle", "blast",
"xlayer", "linea", "berachain", "seievm", "wormchain", "cosmoshub", "evmos",
"kujira", "neutron", "celestia", "stargaze", "seda", "dymension",
"provenance", "sepolia", "arbitrum_sepolia", "base_sepolia",
"optimism_sepolia", "holesky", "polygon_sepolia"]
--dst-chain destination chain
[required] [choices: "solana", "ethereum", "terra", "bsc", "polygon",
"avalanche", "oasis", "algorand", "aurora", "fantom", "karura", "acala",
"klaytn", "celo", "near", "moonbeam", "neon", "terra2", "injective",
"osmosis", "sui", "aptos", "arbitrum", "optimism", "gnosis", "pythnet",
"xpla", "btc", "base", "sei", "rootstock", "scroll", "mantle", "blast",
"xlayer", "linea", "berachain", "seievm", "wormchain", "cosmoshub", "evmos",
"kujira", "neutron", "celestia", "stargaze", "seda", "dymension",
"provenance", "sepolia", "arbitrum_sepolia", "base_sepolia",
"optimism_sepolia", "holesky", "polygon_sepolia"]
--src-chain source chain [string] [required]
--dst-chain destination chain [string] [required]
--dst-addr destination address [string] [required]
--token-addr token address [string] [default: native token]
--amount token amount [string] [required]
@ -353,16 +326,7 @@ Options:
```sh
Positionals:
network Network [choices: "mainnet", "testnet", "devnet"]
chain Source chain
[choices: "unset", "solana", "ethereum", "terra", "bsc", "polygon",
"avalanche", "oasis", "algorand", "aurora", "fantom", "karura", "acala",
"klaytn", "celo", "near", "moonbeam", "neon", "terra2", "injective",
"osmosis", "sui", "aptos", "arbitrum", "optimism", "gnosis", "pythnet",
"xpla", "btc", "base", "sei", "rootstock", "scroll", "mantle", "blast",
"xlayer", "linea", "berachain", "seievm", "wormchain", "cosmoshub", "evmos",
"kujira", "neutron", "celestia", "stargaze", "seda", "dymension",
"provenance", "sepolia", "arbitrum_sepolia", "base_sepolia",
"optimism_sepolia", "holesky", "polygon_sepolia"]
chain Source chain [string]
tx Source transaction hash [string]
Options:

File diff suppressed because it is too large Load Diff

View File

@ -42,11 +42,13 @@
"@solana/web3.js": "^1.22.0",
"@terra-money/terra.js": "^3.1.9",
"@types/config": "^3.3.0",
"@wormhole-foundation/sdk": "^0.7.0-beta.5",
"@xpla/xpla.js": "^0.2.1",
"algosdk": "^2.4.0",
"aptos": "^1.3.16",
"axios": "^0.24.0",
"base-64": "^1.0.0",
"bech32": "^2.0.0",
"binary-parser": "^2.0.2",
"bn.js": "^5.2.0",
"bs58": "^4.0.1",
@ -65,12 +67,13 @@
"@types/bn.js": "^5.1.0",
"@types/bs58": "^4.0.1",
"@types/jest": "^29.5.12",
"@types/node": "^20.12.12",
"@types/node-fetch": "^2.6.3",
"@types/yargs": "^17.0.24",
"copy-dir": "^1.3.0",
"jest": "^29.7.0",
"ts-jest": "^29.1.2",
"tsx": "^4.12.0",
"typescript": "^4.6"
"typescript": "^5.4.5"
}
}

View File

@ -2,24 +2,26 @@ import {
_submitVAAAlgorand,
signSendAndConfirmAlgorand,
} from "@certusone/wormhole-sdk/lib/esm/algorand";
import {
CONTRACTS,
ChainName,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import { Account, Algodv2, mnemonicToSecretKey } from "algosdk";
import { NETWORKS } from "./consts";
import { Network } from "./utils";
import { Payload, impossible } from "./vaa";
import { transferFromAlgorand } from "@certusone/wormhole-sdk/lib/esm/token_bridge/transfer";
import { tryNativeToHexString } from "@certusone/wormhole-sdk/lib/esm/utils";
import { tryNativeToHexString } from "./sdk/array";
import {
Chain,
chainToChainId,
contracts,
Network,
toChainId,
} from "@wormhole-foundation/sdk-base";
export async function execute_algorand(
payload: Payload,
vaa: Uint8Array,
network: Network
) {
const chainName = "algorand";
const { key, rpc } = NETWORKS[network][chainName];
const chain: Chain = "Algorand";
const { key, rpc } = NETWORKS[network][chain];
if (!key) {
throw Error(`No ${network} key defined for Algorand`);
}
@ -28,19 +30,19 @@ export async function execute_algorand(
throw Error(`No ${network} rpc defined for Algorand`);
}
const contracts = CONTRACTS[network][chainName];
const coreContract = contracts.coreBridge(network, chain);
console.log("contracts", contracts);
let target_contract: string;
switch (payload.module) {
case "Core": {
if (!contracts.core) {
if (!coreContract) {
throw new Error(
`Core bridge address not defined for Algorand ${network}`
);
}
target_contract = contracts.core;
target_contract = coreContract;
switch (payload.type) {
case "GuardianSetUpgrade":
console.log("Submitting new guardian set");
@ -57,14 +59,12 @@ export async function execute_algorand(
break;
}
case "NFTBridge": {
if (!contracts.nft_bridge) {
// NOTE: this code can safely be removed once the algorand NFT bridge is
// released, but it's fine for it to stay, as the condition will just be
// skipped once 'contracts.nft_bridge' is defined
const nftContract = contracts.nftBridge.get(network, chain);
if (!nftContract) {
throw new Error("NFT bridge not supported yet for Algorand");
}
target_contract = contracts.nft_bridge;
target_contract = nftContract;
switch (payload.type) {
case "ContractUpgrade":
console.log("Upgrading contract");
@ -84,13 +84,14 @@ export async function execute_algorand(
break;
}
case "TokenBridge": {
if (!contracts.token_bridge) {
const tbContract = contracts.tokenBridge(network, chain);
if (!tbContract) {
throw new Error(
`Token bridge address not defined for Algorand ${network}`
);
}
target_contract = contracts.token_bridge;
target_contract = tbContract;
switch (payload.type) {
case "ContractUpgrade":
console.log("Upgrading contract");
@ -121,7 +122,7 @@ export async function execute_algorand(
}
const target = BigInt(parseInt(target_contract));
const CORE_ID = BigInt(parseInt(contracts.core));
const CORE_ID = BigInt(parseInt(coreContract));
const algodClient = getClient(network, rpc);
const algoWallet: Account = mnemonicToSecretKey(key);
@ -140,27 +141,29 @@ export async function execute_algorand(
}
export async function transferAlgorand(
dstChain: ChainName,
dstChain: Chain,
dstAddress: string,
tokenAddress: string,
amount: string,
network: Network,
rpc: string
) {
const { key } = NETWORKS[network].algorand;
const { key } = NETWORKS[network].Algorand;
if (!key) {
throw Error(`No ${network} key defined for Algorand`);
}
const contracts = CONTRACTS[network].algorand;
const client = getClient(network, rpc);
const wallet: Account = mnemonicToSecretKey(key);
const CORE_ID = BigInt(parseInt(contracts.core));
const TOKEN_BRIDGE_ID = BigInt(parseInt(contracts.token_bridge));
const recipient = tryNativeToHexString(dstAddress, dstChain);
const CORE_ID = BigInt(parseInt(contracts.coreBridge(network, "Algorand")));
const TOKEN_BRIDGE_ID = BigInt(
parseInt(contracts.tokenBridge(network, "Algorand"))
);
const recipient = tryNativeToHexString(dstAddress, chainToChainId(dstChain));
if (!recipient) {
throw new Error("Failed to convert recipient address");
}
const assetId = tokenAddress === "native" ? BigInt(0) : BigInt(tokenAddress);
const txs = await transferFromAlgorand(
client,
TOKEN_BRIDGE_ID,
@ -169,7 +172,7 @@ export async function transferAlgorand(
assetId,
BigInt(amount),
recipient,
dstChain,
toChainId(dstChain),
BigInt(0)
);
const result = await signSendAndConfirmAlgorand(client, txs, wallet);
@ -182,7 +185,7 @@ function getClient(network: Network, rpc: string) {
algodServer: rpc,
algodPort: "",
};
if (network === "DEVNET") {
if (network === "Devnet") {
ALGORAND_HOST.algodToken =
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
ALGORAND_HOST.algodPort = "4001";

View File

@ -1,22 +1,21 @@
import {
CONTRACTS,
ChainId,
ChainName,
assertChain,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import { transferFromAptos } from "@certusone/wormhole-sdk/lib/esm/token_bridge/transfer";
import { AptosAccount, AptosClient, BCS, TxnBuilderTypes, Types } from "aptos";
import { ethers } from "ethers";
import { sha3_256 } from "js-sha3";
import { NETWORKS } from "./consts";
import { Network } from "./utils";
import { Payload, impossible } from "./vaa";
import { CHAINS, ensureHexPrefix } from "@certusone/wormhole-sdk";
import { TokenBridgeState } from "@certusone/wormhole-sdk/lib/esm/aptos/types";
import { generateSignAndSubmitEntryFunction } from "@certusone/wormhole-sdk/lib/esm/utils";
import {
generateSignAndSubmitEntryFunction,
tryNativeToUint8Array,
} from "@certusone/wormhole-sdk/lib/esm/utils";
Chain,
ChainId,
Network,
assertChainId,
contracts,
toChainId,
} from "@wormhole-foundation/sdk-base";
import { tryNativeToUint8Array } from "./sdk/array";
export async function execute_aptos(
payload: Payload,
@ -25,7 +24,7 @@ export async function execute_aptos(
contract: string | undefined,
rpc: string | undefined
) {
const chain = "aptos";
const chain: Chain = "Aptos";
// turn VAA bytes into BCS format. That is, add a length prefix
const serializer = new BCS.Serializer();
@ -34,7 +33,7 @@ export async function execute_aptos(
switch (payload.module) {
case "Core": {
contract = contract ?? CONTRACTS[network][chain]["core"];
contract = contract ?? contracts.coreBridge(network, chain);
if (contract === undefined) {
throw Error("core bridge contract is undefined");
}
@ -71,7 +70,7 @@ export async function execute_aptos(
break;
}
case "NFTBridge": {
contract = contract ?? CONTRACTS[network][chain]["nft_bridge"];
contract = contract ?? contracts.nftBridge(network, chain);
if (contract === undefined) {
throw Error("nft bridge contract is undefined");
}
@ -120,7 +119,7 @@ export async function execute_aptos(
break;
}
case "TokenBridge": {
contract = contract ?? CONTRACTS[network][chain]["token_bridge"];
contract = contract ?? contracts.tokenBridge(network, chain);
if (contract === undefined) {
throw Error("token bridge contract is undefined");
}
@ -180,7 +179,7 @@ export async function execute_aptos(
// offline:
const tokenAddress = payload.tokenAddress;
const tokenChain = payload.tokenChain;
assertChain(tokenChain);
assertChainId(tokenChain);
let wrappedContract = deriveWrappedAssetAddress(
hex(contract),
tokenChain,
@ -208,7 +207,7 @@ export async function execute_aptos(
// TODO: only handles wrapped assets for now
const tokenAddress = payload.tokenAddress;
const tokenChain = payload.tokenChain;
assertChain(tokenChain);
assertChainId(tokenChain);
let wrappedContract = deriveWrappedAssetAddress(
hex(contract),
tokenChain,
@ -243,22 +242,22 @@ export async function execute_aptos(
}
export async function transferAptos(
dstChain: ChainName,
dstChain: Chain,
dstAddress: string,
tokenAddress: string,
amount: string,
network: Network,
rpc: string
) {
const { key } = NETWORKS[network].aptos;
const { key } = NETWORKS[network].Aptos;
if (!key) {
throw new Error("No key for aptos");
}
rpc = rpc ?? NETWORKS[network].aptos.rpc;
rpc = rpc ?? NETWORKS[network].Aptos.rpc;
if (!rpc) {
throw new Error("No rpc for aptos");
}
const { token_bridge } = CONTRACTS[network].aptos;
const token_bridge = contracts.tokenBridge(network, "Aptos");
if (!token_bridge) {
throw new Error("token bridge contract is undefined");
}
@ -268,8 +267,8 @@ export async function transferAptos(
token_bridge,
tokenAddress === "native" ? "0x1::aptos_coin::AptosCoin" : tokenAddress,
amount,
dstChain,
tryNativeToUint8Array(dstAddress, dstChain)
toChainId(dstChain),
tryNativeToUint8Array(dstAddress, toChainId(dstChain))
);
const tx = (await generateSignAndSubmitEntryFunction(
client,
@ -321,14 +320,14 @@ export function deriveResourceAccount(
}
export async function callEntryFunc(
network: "MAINNET" | "TESTNET" | "DEVNET",
network: Network,
rpc: string | undefined,
module: string,
func: string,
ty_args: BCS.Seq<TxnBuilderTypes.TypeTag>,
args: BCS.Seq<BCS.Bytes>
): Promise<string> {
let key: string | undefined = NETWORKS[network]["aptos"].key;
let key: string | undefined = NETWORKS[network]["Aptos"].key;
if (key === undefined) {
throw new Error("No key for aptos");
}
@ -338,7 +337,7 @@ export async function callEntryFunc(
if (typeof rpc != "undefined") {
client = new AptosClient(rpc);
} else {
client = new AptosClient(NETWORKS[network]["aptos"].rpc);
client = new AptosClient(NETWORKS[network]["Aptos"].rpc);
}
const [{ sequence_number: sequenceNumber }, chainId] = await Promise.all([
client.getAccount(accountFrom.address()),
@ -388,14 +387,13 @@ export async function queryRegistrationsAptos(
network: Network,
module: "Core" | "NFTBridge" | "TokenBridge"
): Promise<Object> {
const n = NETWORKS[network]["aptos"];
const n = NETWORKS[network]["Aptos"];
const client = new AptosClient(n.rpc);
const contracts = CONTRACTS[network]["aptos"];
let stateObjectId: string | undefined;
switch (module) {
case "TokenBridge":
stateObjectId = contracts.token_bridge;
stateObjectId = contracts.tokenBridge(network, "Aptos");
if (stateObjectId === undefined) {
throw Error(`Unknown token bridge contract on ${network} for Aptos`);
}

View File

@ -10,25 +10,38 @@ import {
getOriginalAssetXpla,
} from "@certusone/wormhole-sdk/lib/esm/token_bridge/getOriginalAsset";
import { getOriginalAssetInjective } from "@certusone/wormhole-sdk/lib/esm/token_bridge/injective";
import {
ChainId,
ChainName,
coalesceChainName,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import { CONTRACTS } from "../../consts";
import { Network } from "../../utils";
import { impossible } from "../../vaa";
import { getOriginalAssetSei } from "../sei/sdk";
import { getProviderForChain } from "./provider";
import {
Chain,
ChainId,
Network,
chainToPlatform,
contracts,
toChain,
} from "@wormhole-foundation/sdk-base";
import {
TokenId,
Wormhole,
toChainId,
wormhole,
} from "@wormhole-foundation/sdk";
import evm from "@wormhole-foundation/sdk/evm";
import solana from "@wormhole-foundation/sdk/solana";
import algorand from "@wormhole-foundation/sdk/algorand";
import aptos from "@wormhole-foundation/sdk/aptos";
import cosmwasm from "@wormhole-foundation/sdk/cosmwasm";
import sui from "@wormhole-foundation/sdk/sui";
export const getOriginalAsset = async (
chain: ChainId | ChainName,
chain: ChainId | Chain,
network: Network,
assetAddress: string,
rpc?: string
): Promise<WormholeWrappedInfo> => {
const chainName = coalesceChainName(chain);
const tokenBridgeAddress = CONTRACTS[network][chainName].token_bridge;
const chainName = toChain(chain);
const tokenBridgeAddress = contracts.tokenBridge.get(network, chainName);
if (!tokenBridgeAddress) {
throw new Error(
`Token bridge address not defined for ${chainName} ${network}`
@ -36,69 +49,66 @@ export const getOriginalAsset = async (
}
switch (chainName) {
case "unset":
throw new Error("Chain not set");
case "solana": {
case "Solana": {
const provider = getProviderForChain(chainName, network, { rpc });
return getOriginalAssetSolana(provider, tokenBridgeAddress, assetAddress);
}
case "acala":
case "arbitrum":
case "aurora":
case "avalanche":
case "base":
case "bsc":
case "celo":
case "ethereum":
case "fantom":
case "gnosis":
case "karura":
case "klaytn":
case "moonbeam":
case "neon":
case "oasis":
case "optimism":
case "polygon":
// case "rootstock":
case "scroll":
case "mantle":
case "blast":
case "xlayer":
case "linea":
case "berachain":
case "seievm":
case "sepolia":
case "arbitrum_sepolia":
case "base_sepolia":
case "optimism_sepolia":
case "polygon_sepolia":
case "holesky": {
case "Acala":
case "Arbitrum":
case "Aurora":
case "Avalanche":
case "Base":
case "Bsc":
case "Celo":
case "Ethereum":
case "Fantom":
case "Gnosis":
case "Karura":
case "Klaytn":
case "Moonbeam":
case "Neon":
case "Oasis":
case "Optimism":
case "Polygon":
case "Scroll":
case "Mantle":
case "Blast":
case "Xlayer":
case "Linea":
case "Berachain":
case "Seievm":
case "Sepolia":
case "ArbitrumSepolia":
case "BaseSepolia":
case "OptimismSepolia":
case "PolygonSepolia":
case "Holesky": {
const provider = getProviderForChain(chainName, network, { rpc });
return getOriginalAssetEth(
tokenBridgeAddress,
provider,
assetAddress,
chain
toChainId(chain)
);
}
case "terra":
case "terra2": {
case "Terra":
case "Terra2": {
const provider = getProviderForChain(chainName, network, { rpc });
return getOriginalAssetTerra(provider, assetAddress);
}
case "injective": {
case "Injective": {
const provider = getProviderForChain(chainName, network, { rpc });
return getOriginalAssetInjective(assetAddress, provider);
}
case "sei": {
case "Sei": {
const provider = await getProviderForChain(chainName, network, { rpc });
return getOriginalAssetSei(assetAddress, provider);
}
case "xpla": {
case "Xpla": {
const provider = getProviderForChain(chainName, network, { rpc });
return getOriginalAssetXpla(provider, assetAddress);
}
case "algorand": {
case "Algorand": {
const provider = getProviderForChain(chainName, network, { rpc });
return getOriginalAssetAlgorand(
provider,
@ -106,32 +116,32 @@ export const getOriginalAsset = async (
BigInt(assetAddress)
);
}
case "near": {
case "Near": {
const provider = await getProviderForChain(chainName, network, { rpc });
return getOriginalAssetNear(provider, tokenBridgeAddress, assetAddress);
}
case "aptos": {
case "Aptos": {
const provider = getProviderForChain(chainName, network, { rpc });
return getOriginalAssetAptos(provider, tokenBridgeAddress, assetAddress);
}
case "sui": {
case "Sui": {
const provider = getProviderForChain(chainName, network, { rpc });
return getOriginalAssetSui(provider, tokenBridgeAddress, assetAddress);
}
case "btc":
case "osmosis":
case "pythnet":
case "wormchain":
case "cosmoshub":
case "evmos":
case "kujira":
case "neutron":
case "celestia":
case "stargaze":
case "seda":
case "dymension":
case "provenance":
case "rootstock":
case "Btc":
case "Osmosis":
case "Pythnet":
case "Wormchain":
case "Cosmoshub":
case "Evmos":
case "Kujira":
case "Neutron":
case "Celestia":
case "Stargaze":
case "Seda":
case "Dymension":
case "Provenance":
case "Rootstock":
throw new Error(`${chainName} not supported`);
default:
impossible(chainName);

View File

@ -9,31 +9,32 @@ import {
getForeignAssetXpla,
} from "@certusone/wormhole-sdk/lib/esm/token_bridge/getForeignAsset";
import { getForeignAssetInjective } from "@certusone/wormhole-sdk/lib/esm/token_bridge/injective";
import { tryNativeToUint8Array } from "@certusone/wormhole-sdk/lib/esm/utils/array";
import {
ChainId,
ChainName,
coalesceChainName,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import { CONTRACTS } from "../../consts";
import { Network } from "../../utils";
import { impossible } from "../../vaa";
import { getForeignAssetSei } from "../sei/sdk";
import { getProviderForChain } from "./provider";
import {
Chain,
ChainId,
Network,
contracts,
toChain,
toChainId,
} from "@wormhole-foundation/sdk-base";
import { tryNativeToUint8Array } from "../../sdk/array";
export const getWrappedAssetAddress = async (
chain: ChainId | ChainName,
chain: ChainId | Chain,
network: Network,
originChain: ChainId | ChainName,
originChain: ChainId | Chain,
originAddress: string,
rpc?: string
): Promise<string | null> => {
const chainName = coalesceChainName(chain);
const chainName = toChain(chain);
const originAddressUint8Array = tryNativeToUint8Array(
originAddress,
originChain
);
const tokenBridgeAddress = CONTRACTS[network][chainName].token_bridge;
const tokenBridgeAddress = contracts.tokenBridge.get(network, chainName);
if (!tokenBridgeAddress) {
throw new Error(
`Token bridge address not defined for ${chainName} ${network}`
@ -41,143 +42,141 @@ export const getWrappedAssetAddress = async (
}
switch (chainName) {
case "unset":
throw new Error("Chain not set");
case "solana": {
case "Solana": {
const provider = getProviderForChain(chainName, network, { rpc });
return getForeignAssetSolana(
provider,
tokenBridgeAddress,
originChain,
toChainId(originChain),
originAddressUint8Array
);
}
case "acala":
case "arbitrum":
case "aurora":
case "avalanche":
case "base":
case "bsc":
case "celo":
case "ethereum":
case "fantom":
case "gnosis":
case "karura":
case "klaytn":
case "moonbeam":
case "neon":
case "oasis":
case "optimism":
case "polygon":
// case "rootstock":
case "scroll":
case "mantle":
case "blast":
case "xlayer":
case "linea":
case "berachain":
case "seievm":
case "sepolia":
case "arbitrum_sepolia":
case "base_sepolia":
case "optimism_sepolia":
case "polygon_sepolia":
case "holesky": {
case "Acala":
case "Arbitrum":
case "Aurora":
case "Avalanche":
case "Base":
case "Bsc":
case "Celo":
case "Ethereum":
case "Fantom":
case "Gnosis":
case "Karura":
case "Klaytn":
case "Moonbeam":
case "Neon":
case "Oasis":
case "Optimism":
case "Polygon":
// case "Rootstock":
case "Scroll":
case "Mantle":
case "Blast":
case "Xlayer":
case "Linea":
case "Berachain":
case "Seievm":
case "Sepolia":
case "ArbitrumSepolia":
case "BaseSepolia":
case "OptimismSepolia":
case "PolygonSepolia":
case "Holesky": {
const provider = getProviderForChain(chainName, network, { rpc });
return getForeignAssetEth(
tokenBridgeAddress,
provider,
originChain,
toChainId(originChain),
originAddressUint8Array
);
}
case "terra":
case "terra2": {
case "Terra":
case "Terra2": {
const provider = getProviderForChain(chainName, network, { rpc });
return getForeignAssetTerra(
tokenBridgeAddress,
provider,
originChain,
toChainId(originChain),
originAddressUint8Array
);
}
case "injective": {
case "Injective": {
const provider = getProviderForChain(chainName, network, { rpc });
return getForeignAssetInjective(
tokenBridgeAddress,
provider,
originChain,
toChainId(originChain),
originAddressUint8Array
);
}
case "sei": {
case "Sei": {
const provider = await getProviderForChain(chainName, network, { rpc });
return getForeignAssetSei(
tokenBridgeAddress,
provider,
originChain,
toChainId(originChain),
originAddressUint8Array
);
}
case "xpla": {
case "Xpla": {
const provider = getProviderForChain(chainName, network, { rpc });
return getForeignAssetXpla(
tokenBridgeAddress,
provider,
originChain,
toChainId(originChain),
originAddressUint8Array
);
}
case "algorand": {
case "Algorand": {
const provider = getProviderForChain(chainName, network, { rpc });
return getForeignAssetAlgorand(
provider,
BigInt(tokenBridgeAddress),
originChain,
toChainId(originChain),
originAddress
).then((x) => x?.toString() ?? null);
}
case "near": {
case "Near": {
const provider = await getProviderForChain(chainName, network, { rpc });
return getForeignAssetNear(
provider,
tokenBridgeAddress,
originChain,
toChainId(originChain),
originAddress
);
}
case "aptos": {
case "Aptos": {
const provider = getProviderForChain(chainName, network, { rpc });
return getForeignAssetAptos(
provider,
tokenBridgeAddress,
originChain,
toChainId(originChain),
originAddress
);
}
case "sui": {
case "Sui": {
const provider = getProviderForChain(chainName, network, { rpc });
return getForeignAssetSui(
provider,
tokenBridgeAddress,
originChain,
toChainId(originChain),
originAddressUint8Array
);
}
case "btc":
case "osmosis":
case "pythnet":
case "wormchain":
case "cosmoshub":
case "evmos":
case "kujira":
case "neutron":
case "celestia":
case "rootstock":
case "stargaze":
case "seda":
case "dymension":
case "provenance":
case "Btc":
case "Osmosis":
case "Pythnet":
case "Wormchain":
case "Cosmoshub":
case "Evmos":
case "Kujira":
case "Neutron":
case "Celestia":
case "Rootstock":
case "Stargaze":
case "Seda":
case "Dymension":
case "Provenance":
throw new Error(`${chainName} not supported`);
default:
impossible(chainName);

View File

@ -1,20 +1,3 @@
import {
CHAIN_ID_ALGORAND,
CHAIN_ID_APTOS,
CHAIN_ID_INJECTIVE,
CHAIN_ID_NEAR,
CHAIN_ID_SEI,
CHAIN_ID_SOLANA,
CHAIN_ID_SUI,
CHAIN_ID_TERRA,
CHAIN_ID_TERRA2,
CHAIN_ID_XPLA,
ChainId,
ChainName,
EVMChainId,
EVMChainName,
coalesceChainName,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import { CosmWasmClient } from "@cosmjs/cosmwasm-stargate";
import {
Network as InjectiveNetwork,
@ -32,104 +15,107 @@ import { ethers } from "ethers";
import { connect } from "near-api-js";
import { Provider as NearProvider } from "near-api-js/lib/providers";
import { NETWORKS } from "../../consts";
import { Network } from "../../utils";
import { impossible } from "../../vaa";
import {
Chain,
ChainId,
Network,
PlatformToChains,
chainIds,
toChain,
toChainId,
} from "@wormhole-foundation/sdk-base";
export type ChainProvider<T extends ChainId | ChainName> = T extends
| "algorand"
| typeof CHAIN_ID_ALGORAND
export type ChainProvider<T extends Chain> = T extends "Algorand"
? Algodv2
: T extends "aptos" | typeof CHAIN_ID_APTOS
: T extends "Aptos"
? AptosClient
: T extends EVMChainName | EVMChainId
: T extends PlatformToChains<"Evm">
? ethers.providers.JsonRpcProvider
: T extends "injective" | typeof CHAIN_ID_INJECTIVE
: T extends "Injective"
? ChainGrpcWasmApi
: T extends "near" | typeof CHAIN_ID_NEAR
: T extends "Near"
? Promise<NearProvider>
: T extends
| "terra"
| "terra2"
| typeof CHAIN_ID_TERRA
| typeof CHAIN_ID_TERRA2
: T extends "Terra" | "Terra2"
? TerraLCDClient
: T extends "sei" | typeof CHAIN_ID_SEI
: T extends "Sei"
? Promise<CosmWasmClient>
: T extends "solana" | typeof CHAIN_ID_SOLANA
: T extends "Solana"
? SolanaConnection
: T extends "sui" | typeof CHAIN_ID_SUI
: T extends "Sui"
? JsonRpcProvider
: T extends "xpla" | typeof CHAIN_ID_XPLA
: T extends "Xpla"
? XplaLCDClient
: never;
export const getProviderForChain = <T extends ChainId | ChainName>(
export const getProviderForChain = <T extends Chain>(
chain: T,
network: Network,
options?: { rpc?: string; [opt: string]: any }
): ChainProvider<T> => {
const chainName = coalesceChainName(chain);
const rpc = options?.rpc ?? NETWORKS[network][chainName].rpc;
const rpc = options?.rpc ?? NETWORKS[network][chain].rpc;
if (!rpc) {
throw new Error(`No ${network} rpc defined for ${chainName}`);
throw new Error(`No ${network} rpc defined for ${chain}`);
}
switch (chainName) {
case "unset":
throw new Error("Chain not set");
case "solana":
switch (chain) {
case "Solana":
return new SolanaConnection(rpc, "confirmed") as ChainProvider<T>;
case "acala":
case "arbitrum":
case "aurora":
case "avalanche":
case "base":
case "bsc":
case "celo":
case "ethereum":
case "fantom":
case "gnosis":
case "karura":
case "klaytn":
case "moonbeam":
case "neon":
case "oasis":
case "optimism":
case "polygon":
// case "rootstock":
case "scroll":
case "mantle":
case "blast":
case "xlayer":
case "linea":
case "berachain":
case "seievm":
case "sepolia":
case "arbitrum_sepolia":
case "base_sepolia":
case "optimism_sepolia":
case "polygon_sepolia":
case "holesky":
case "Acala":
case "Arbitrum":
case "Aurora":
case "Avalanche":
case "Base":
case "Bsc":
case "Celo":
case "Ethereum":
case "Fantom":
case "Gnosis":
case "Karura":
case "Klaytn":
case "Moonbeam":
case "Neon":
case "Oasis":
case "Optimism":
case "Polygon":
// case "Rootstock":
case "Scroll":
case "Mantle":
case "Blast":
case "Xlayer":
case "Linea":
case "Berachain":
case "Seievm":
case "Sepolia":
case "ArbitrumSepolia":
case "BaseSepolia":
case "OptimismSepolia":
case "PolygonSepolia":
case "Holesky":
return new ethers.providers.JsonRpcProvider(rpc) as ChainProvider<T>;
case "terra":
case "terra2":
case "Terra":
case "Terra2":
const chain_id =
chain === "Terra"
? NETWORKS[network].Terra.chain_id
: NETWORKS[network].Terra2.chain_id;
return new TerraLCDClient({
URL: rpc,
chainID: NETWORKS[network][chainName].chain_id,
isClassic: chainName === "terra",
chainID: chain_id,
isClassic: chain === "Terra",
}) as ChainProvider<T>;
case "injective": {
case "Injective": {
const endpoints = getNetworkEndpoints(
network === "MAINNET"
network === "Mainnet"
? InjectiveNetwork.MainnetK8s
: InjectiveNetwork.TestnetK8s
);
return new ChainGrpcWasmApi(endpoints.grpc) as ChainProvider<T>;
}
case "sei":
case "Sei":
return getCosmWasmClient(rpc) as ChainProvider<T>;
case "xpla": {
const chainId = NETWORKS[network].xpla.chain_id;
case "Xpla": {
const chainId = NETWORKS[network].Xpla.chain_id;
if (!chainId) {
throw new Error(`No ${network} chain ID defined for XPLA.`);
}
@ -139,7 +125,7 @@ export const getProviderForChain = <T extends ChainId | ChainName>(
chainID: chainId,
}) as ChainProvider<T>;
}
case "algorand": {
case "Algorand": {
const { token, port } = {
...{
token:
@ -150,34 +136,34 @@ export const getProviderForChain = <T extends ChainId | ChainName>(
};
return new Algodv2(token, rpc, port) as ChainProvider<T>;
}
case "near":
case "Near":
return connect({
networkId: NETWORKS[network].near.networkId,
networkId: NETWORKS[network].Near.networkId,
nodeUrl: rpc,
headers: {},
}).then(({ connection }) => connection.provider) as ChainProvider<T>;
case "aptos":
case "Aptos":
return new AptosClient(rpc) as ChainProvider<T>;
case "sui":
case "Sui":
return new JsonRpcProvider(
new SuiConnection({ fullnode: rpc })
) as ChainProvider<T>;
case "btc":
case "osmosis":
case "pythnet":
case "wormchain":
case "cosmoshub":
case "evmos":
case "kujira":
case "neutron":
case "celestia":
case "stargaze":
case "seda":
case "dymension":
case "provenance":
case "rootstock":
throw new Error(`${chainName} not supported`);
case "Btc":
case "Osmosis":
case "Pythnet":
case "Wormchain":
case "Cosmoshub":
case "Evmos":
case "Kujira":
case "Neutron":
case "Celestia":
case "Stargaze":
case "Seda":
case "Dymension":
case "Provenance":
case "Rootstock":
throw new Error(`${chain} not supported`);
default:
impossible(chainName);
impossible(chain);
}
};

View File

@ -1,28 +1,22 @@
import { getCosmWasmClient } from "@sei-js/core";
import {
ChainName,
CHAINS,
CONTRACTS,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import { NETWORKS } from "../../consts/networks";
import { Network } from "../../utils";
import { Chain, Network, chains, contracts } from "@wormhole-foundation/sdk";
export async function queryRegistrationsSei(
network: Network,
module: "Core" | "NFTBridge" | "TokenBridge"
): Promise<Object> {
const chain = "sei" as ChainName;
const chain: Chain = "Sei";
const n = NETWORKS[network][chain];
const contracts = CONTRACTS[network][chain];
let target_contract: string | undefined;
switch (module) {
case "TokenBridge":
target_contract = contracts.token_bridge;
target_contract = contracts.tokenBridge.get(network, chain);
break;
case "NFTBridge":
target_contract = contracts.nft_bridge;
target_contract = contracts.nftBridge.get(network, chain);
break;
default:
throw new Error(`Invalid module: ${module}`);
@ -41,14 +35,14 @@ export async function queryRegistrationsSei(
// Query the bridge registration for all the chains in parallel.
const registrations = await Promise.all(
Object.entries(CHAINS)
.filter(([c_name, _]) => c_name !== chain && c_name !== "unset")
.map(async ([c_name, c_id]) => [
chains
.filter((c_name) => c_name !== chain)
.map(async (c_name) => [
c_name,
await (async () => {
let query_msg = {
chain_registration: {
chain: c_id,
chain: c_name,
},
};

View File

@ -5,13 +5,13 @@ import {
} from "@certusone/wormhole-sdk/lib/esm/cosmwasm/address";
import { WormholeWrappedInfo } from "@certusone/wormhole-sdk/lib/esm/token_bridge/getOriginalAsset";
import { hexToUint8Array } from "@certusone/wormhole-sdk/lib/esm/utils/array";
import {
CHAIN_ID_SEI,
ChainId,
ChainName,
coalesceChainId,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import { CosmWasmClient } from "@cosmjs/cosmwasm-stargate";
import {
Chain,
ChainId,
chainToChainId,
toChainId,
} from "@wormhole-foundation/sdk";
import { fromUint8Array } from "js-base64";
/**
@ -25,7 +25,7 @@ import { fromUint8Array } from "js-base64";
export async function getForeignAssetSei(
tokenBridgeAddress: string,
cosmwasmClient: CosmWasmClient,
originChain: ChainId | ChainName,
originChain: ChainId | Chain,
originAsset: Uint8Array
): Promise<string | null> {
try {
@ -33,7 +33,7 @@ export async function getForeignAssetSei(
tokenBridgeAddress,
{
wrapped_registry: {
chain: coalesceChainId(originChain),
chain: toChainId(originChain),
address: fromUint8Array(originAsset),
},
}
@ -74,7 +74,7 @@ export async function getOriginalAssetSei(
wrappedAddress: string,
client: CosmWasmClient
): Promise<WormholeWrappedInfo> {
const chainId = CHAIN_ID_SEI;
const chainId = chainToChainId("Sei");
if (isNativeCosmWasmDenom(chainId, wrappedAddress)) {
return {
isWrapped: false,

View File

@ -5,10 +5,9 @@ import { toUtf8 } from "@cosmjs/encoding";
import { MsgExecuteContract } from "cosmjs-types/cosmwasm/wasm/v1/tx";
import { getSigningCosmWasmClient } from "@sei-js/core";
import { CONTRACTS } from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import { NETWORKS } from "../../consts";
import { Network } from "../../utils";
import { impossible, Payload } from "../../vaa";
import { contracts, Network } from "@wormhole-foundation/sdk";
export const submit = async (
payload: Payload,
@ -16,8 +15,7 @@ export const submit = async (
network: Network,
rpc?: string
) => {
const contracts = CONTRACTS[network].sei;
const networkInfo = NETWORKS[network].sei;
const networkInfo = NETWORKS[network].Sei;
rpc = rpc || networkInfo.rpc;
const key = networkInfo.key;
if (!key) {
@ -32,12 +30,12 @@ export const submit = async (
let execute_msg: object;
switch (payload.module) {
case "Core": {
if (!contracts.core) {
const core = contracts.coreBridge.get(network, "Sei");
if (!core) {
throw new Error(`Core bridge address not defined for Sei ${network}`);
}
target_contract = contracts.core;
// sigh...
target_contract = core;
execute_msg = {
submit_v_a_a: {
vaa: vaa.toString("base64"),
@ -59,14 +57,12 @@ export const submit = async (
break;
}
case "NFTBridge": {
if (!contracts.nft_bridge) {
// NOTE: this code can safely be removed once the sei NFT bridge is
// released, but it's fine for it to stay, as the condition will just be
// skipped once 'contracts.nft_bridge' is defined
const nft = contracts.nftBridge.get(network, "Sei");
if (!nft) {
throw new Error("NFT bridge not supported yet for Sei");
}
target_contract = contracts.nft_bridge;
target_contract = nft;
execute_msg = {
submit_vaa: {
data: vaa.toString("base64"),
@ -91,11 +87,12 @@ export const submit = async (
break;
}
case "TokenBridge": {
if (!contracts.token_bridge) {
const tb = contracts.tokenBridge.get(network, "Sei");
if (!tb) {
throw new Error(`Token bridge address not defined for Sei ${network}`);
}
target_contract = contracts.token_bridge;
target_contract = tb;
execute_msg = {
submit_vaa: {
data: vaa.toString("base64"),

View File

@ -1,6 +1,5 @@
import { JsonRpcProvider } from "@mysten/sui.js";
import fs from "fs";
import { Network } from "../../utils";
import { MoveToml } from "./MoveToml";
import {
buildPackage,
@ -12,6 +11,7 @@ import {
} from "./publish";
import { SuiBuildOutput } from "./types";
import { getPackageId } from "./utils";
import { Network } from "@wormhole-foundation/sdk";
export const buildCoin = async (
provider: JsonRpcProvider,
@ -93,8 +93,8 @@ const setupCoin = (
for (const dependencyPath of paths) {
// todo(aki): the 4th param is a hack that makes this work, but doesn't
// necessarily make sense. We should probably revisit this later.
setupMainToml(dependencyPath, network, false, network !== "DEVNET");
if (network === "DEVNET") {
setupMainToml(dependencyPath, network, false, network !== "Devnet");
if (network === "Devnet") {
const dependencyToml = new MoveToml(getDefaultTomlPath(dependencyPath));
switch (getPackageNameFromPath(dependencyPath)) {
case "wormhole":

View File

@ -8,10 +8,10 @@ import {
import { execSync } from "child_process";
import fs from "fs";
import { resolve } from "path";
import { Network } from "../../utils";
import { MoveToml } from "./MoveToml";
import { SuiBuildOutput } from "./types";
import { executeTransactionBlock } from "./utils";
import { Network } from "@wormhole-foundation/sdk";
export const buildPackage = (packagePath: string): SuiBuildOutput => {
if (!fs.existsSync(packagePath)) {
@ -76,7 +76,7 @@ export const publishPackage = async (
// Publish contracts
const tx = new TransactionBlock();
if (network === "DEVNET") {
if (network === "Devnet") {
// Avoid Error checking transaction input objects: GasBudgetTooHigh { gas_budget: 50000000000, max_budget: 10000000000 }
tx.setGasBudget(10000000000);
}
@ -186,7 +186,7 @@ export const setupMainToml = (
// don't have to manually reset them repeatedly during local development.
// This is not recursive because we assume that packages are deployed bottom
// up.
if (!isDependency && network === "DEVNET") {
if (!isDependency && network === "Devnet") {
resetNetworkToml(packagePath, network);
}

View File

@ -1,25 +1,24 @@
import { getObjectFields } from "@certusone/wormhole-sdk/lib/esm/sui";
import {
CHAIN_ID_TO_NAME,
CONTRACTS,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import { NETWORKS } from "../../consts/networks";
import { Network } from "../../utils";
import { getProvider } from "./utils";
import { ChainId } from "@certusone/wormhole-sdk";
import {
ChainId,
Network,
chainIdToChain,
contracts,
} from "@wormhole-foundation/sdk";
export async function queryRegistrationsSui(
network: Network,
module: "Core" | "NFTBridge" | "TokenBridge"
): Promise<Object> {
const n = NETWORKS[network]["sui"];
const n = NETWORKS[network]["Sui"];
const provider = getProvider(network, n.rpc);
const contracts = CONTRACTS[network]["sui"];
let state_object_id: string;
switch (module) {
case "TokenBridge":
state_object_id = contracts.token_bridge;
state_object_id = contracts.tokenBridge(network, "Sui");
if (state_object_id === undefined) {
throw Error(`Unknown token bridge contract on ${network} for Sui`);
}
@ -49,7 +48,7 @@ export async function queryRegistrationsSui(
const emitterAddress: Uint8Array =
emitter.data?.content?.fields.value.fields.value.fields.data;
const emitterAddrStr = Buffer.from(emitterAddress).toString("hex");
results[CHAIN_ID_TO_NAME[chainId]] = emitterAddrStr;
results[chainIdToChain(chainId)] = emitterAddrStr;
}
}

View File

@ -6,16 +6,7 @@ import {
createWrappedOnSui,
createWrappedOnSuiPrepare,
} from "@certusone/wormhole-sdk/lib/esm/token_bridge/createWrapped";
import { getForeignAssetSui } from "@certusone/wormhole-sdk/lib/esm/token_bridge/getForeignAsset";
import {
CHAIN_ID_SUI,
CHAIN_ID_TO_NAME,
CONTRACTS,
assertChain,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import { parseAttestMetaVaa } from "@certusone/wormhole-sdk/lib/esm/vaa/tokenBridge";
import { SUI_CLOCK_OBJECT_ID, TransactionBlock } from "@mysten/sui.js";
import { Network } from "../../utils";
import { Payload, impossible } from "../../vaa";
import {
assertSuccess,
@ -28,6 +19,15 @@ import {
registerChain,
setMaxGasBudgetDevnet,
} from "./utils";
import {
Chain,
Network,
VAA,
assertChain,
contracts,
deserialize,
} from "@wormhole-foundation/sdk";
import { getForeignAssetSui } from "../../sdk/sui";
export const submit = async (
payload: Payload,
@ -39,13 +39,13 @@ export const submit = async (
const consoleWarnTemp = console.warn;
console.warn = () => {};
const chain = CHAIN_ID_TO_NAME[CHAIN_ID_SUI];
const chain: Chain = "Sui";
const provider = getProvider(network, rpc);
const signer = getSigner(provider, network, privateKey);
switch (payload.module) {
case "Core": {
const coreObjectId = CONTRACTS[network][chain].core;
const coreObjectId = contracts.coreBridge(network, chain);
if (!coreObjectId) {
throw Error("Core bridge object ID is undefined");
}
@ -103,12 +103,12 @@ export const submit = async (
throw new Error("NFT bridge not supported on Sui");
}
case "TokenBridge": {
const coreBridgeStateObjectId = CONTRACTS[network][chain].core;
const coreBridgeStateObjectId = contracts.coreBridge(network, chain);
if (!coreBridgeStateObjectId) {
throw Error("Core bridge object ID is undefined");
}
const tokenBridgeStateObjectId = CONTRACTS[network][chain].token_bridge;
const tokenBridgeStateObjectId = contracts.tokenBridge(network, chain);
if (!tokenBridgeStateObjectId) {
throw Error("Token bridge object ID is undefined");
}
@ -116,13 +116,19 @@ export const submit = async (
switch (payload.type) {
case "AttestMeta": {
// Test attest VAA: 01000000000100d87023087588d8a482d6082c57f3c93649c9a61a98848fc3a0b271f4041394ff7b28abefc8e5e19b83f45243d073d677e122e41425c2dbae3eb5ae1c7c0ac0ee01000000c056a8000000020000000000000000000000000290fb167208af455bb137780163b7b7a9a10c16000000000000000001020000000000000000000000002d8be6bf0baa74e0a907016679cae9190e80dd0a000212544b4e0000000000000000000000000000000000000000000000000000000000457468657265756d205465737420546f6b656e00000000000000000000000000
const { tokenChain, tokenAddress } = parseAttestMetaVaa(vaa);
const parsedAttest: VAA<"TokenBridge:AttestMeta"> = deserialize(
"TokenBridge:AttestMeta",
vaa
);
const tokenChain = parsedAttest.payload.token.chain;
assertChain(tokenChain);
const tokenAddress = parsedAttest.payload.token.address;
const decimals = parsedAttest.payload.decimals;
const coinType = await getForeignAssetSui(
provider,
tokenBridgeStateObjectId,
tokenChain,
tokenAddress
tokenAddress.toUint8Array()
);
if (coinType) {
// Coin already exists, so we update it
@ -135,7 +141,7 @@ export const submit = async (
provider,
coreBridgeStateObjectId,
tokenBridgeStateObjectId,
parseAttestMetaVaa(vaa).decimals,
decimals,
await signer.getAddress()
);
setMaxGasBudgetDevnet(network, prepareTx);
@ -153,7 +159,7 @@ export const submit = async (
console.log(` Published to ${coinPackageId}`);
console.log(` Type ${getWrappedCoinType(coinPackageId)}`);
if (!rpc && network !== "DEVNET") {
if (!rpc && network !== "Devnet") {
// Wait for wrapped asset creation to be propagated to other
// nodes in case this complete registration call is load balanced
// to another node.

View File

@ -6,24 +6,26 @@ import {
setMaxGasBudgetDevnet,
} from "./utils";
import {
CONTRACTS,
ChainName,
Chain,
Network,
tryNativeToUint8Array,
} from "@certusone/wormhole-sdk/lib/esm/utils";
chainToChainId,
contracts,
} from "@wormhole-foundation/sdk-base";
import { tryNativeToUint8Array } from "../../sdk/array";
export async function transferSui(
dstChain: ChainName,
dstChain: Chain,
dstAddress: string,
tokenAddress: string,
amount: string,
network: Network,
rpc: string
) {
const { core, token_bridge } = CONTRACTS[network]["sui"];
const core = contracts.coreBridge(network, "Sui");
if (!core) {
throw Error("Core bridge object ID is undefined");
}
const token_bridge = contracts.tokenBridge.get(network, "Sui");
if (!token_bridge) {
throw new Error("Token bridge object ID is undefined");
}
@ -44,8 +46,8 @@ export async function transferSui(
coins,
coinType,
BigInt(amount),
dstChain,
tryNativeToUint8Array(dstAddress, dstChain)
chainToChainId(dstChain),
tryNativeToUint8Array(dstAddress, chainToChainId(dstChain))
);
setMaxGasBudgetDevnet(network, tx);
const result = await executeTransactionBlock(signer, tx);

View File

@ -13,9 +13,10 @@ import {
} from "@mysten/sui.js";
import { DynamicFieldPage } from "@mysten/sui.js/dist/types/dynamic_fields";
import { NETWORKS } from "../../consts";
import { Network } from "../../utils";
import { Payload, VAA, parse, serialiseVAA } from "../../vaa";
import { SuiRpcValidationError } from "./error";
import { Network } from "@wormhole-foundation/sdk";
import { isValidSuiAddress } from "../../sdk/sui";
const UPGRADE_CAP_TYPE = "0x2::package::UpgradeCap";
@ -207,7 +208,7 @@ export const getProvider = (
throw new Error("Must provide network or RPC to initialize provider");
}
rpc = rpc || NETWORKS[network!].sui.rpc;
rpc = rpc || NETWORKS[network!].Sui.rpc;
if (!rpc) {
throw new Error(`No default RPC found for Sui ${network}`);
}
@ -235,7 +236,7 @@ export const getSigner = (
customPrivateKey?: string
): RawSigner => {
const privateKey: string | undefined =
customPrivateKey || NETWORKS[network].sui.key;
customPrivateKey || NETWORKS[network].Sui.key;
if (!privateKey) {
throw new Error(`No private key found for Sui ${network}`);
}
@ -323,9 +324,6 @@ export const isSuiPublishEvent = <
event: T
): event is K => event?.type === "published";
export const isValidSuiAddress = (objectId: string): boolean =>
/^(0x)?[0-9a-f]{1,64}$/.test(objectId);
// todo(aki): this needs to correctly handle types such as
// 0x2::dynamic_field::Field<0x3c6d386861470e6f9cb35f3c91f69e6c1f1737bd5d217ca06a15f582e1dc1ce3::state::MigrationControl, bool>
export const normalizeSuiType = (type: string): string => {
@ -345,7 +343,7 @@ export const registerChain = async (
tokenBridgeStateObjectId: string,
transactionBlock?: TransactionBlock
): Promise<TransactionBlock> => {
if (network === "DEVNET") {
if (network === "Devnet") {
// Modify the VAA to only have 1 guardian signature
// TODO: remove this when we can deploy the devnet core contract
// deterministically with multiple guardians in the initial guardian set
@ -418,7 +416,7 @@ export const setMaxGasBudgetDevnet = (
network: Network,
tx: TransactionBlock
) => {
if (network === "DEVNET") {
if (network === "Devnet") {
// Avoid Error checking transaction input objects: GasBudgetTooHigh { gas_budget: 50000000000, max_budget: 10000000000 }
tx.setGasBudget(10000000000);
}

View File

@ -1,10 +1,3 @@
import { APTOS_DEPLOYER_ADDRESS_DEVNET } from "@certusone/wormhole-sdk";
import {
assertChain,
CHAIN_ID_APTOS,
coalesceChainId,
CONTRACTS,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import { BCS, FaucetClient } from "aptos";
import { spawnSync } from "child_process";
import fs from "fs";
@ -25,13 +18,23 @@ import {
RPC_OPTIONS,
} from "../consts";
import { runCommand, VALIDATOR_OPTIONS } from "../startValidator";
import { assertNetwork, checkBinary, evm_address, hex } from "../utils";
import { checkBinary, evm_address, getNetwork, hex } from "../utils";
import {
assertChain,
chainToChainId,
contracts,
toChain,
toChainId,
} from "@wormhole-foundation/sdk-base";
const APTOS_NODE_URL = "http://0.0.0.0:8080/v1";
const APTOS_FAUCET_URL = "http://0.0.0.0:8081";
const README_URL =
"https://github.com/wormhole-foundation/wormhole/blob/main/aptos/README.md";
export const APTOS_DEPLOYER_ADDRESS_DEVNET =
"277fa055b6a73c42c0662d5236c65c864ccbf2d4abd21f174a30c8b786eab84b";
interface Package {
meta_file: string;
mv_files: string[];
@ -56,12 +59,11 @@ export const builder = (y: typeof yargs) =>
(yargs) =>
yargs.option("network", NETWORK_OPTIONS).option("rpc", RPC_OPTIONS),
async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
const network = getNetwork(argv.network);
const contract_address = evm_address(
CONTRACTS[network].aptos.token_bridge
contracts.tokenBridge(network, "Aptos")
);
const rpc = argv.rpc ?? NETWORKS[network].aptos.rpc;
const rpc = argv.rpc ?? NETWORKS[network].Aptos.rpc;
await callEntryFunc(
network,
rpc,
@ -82,7 +84,7 @@ export const builder = (y: typeof yargs) =>
.option("chain-id", {
describe: "Chain id",
type: "number",
default: CHAIN_ID_APTOS,
default: chainToChainId("Aptos"),
demandOption: false,
})
.option("governance-chain-id", {
@ -104,10 +106,11 @@ export const builder = (y: typeof yargs) =>
type: "string",
}),
async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
const network = getNetwork(argv.network);
const contract_address = evm_address(CONTRACTS[network].aptos.core);
const contract_address = evm_address(
contracts.coreBridge(network, "Aptos")
);
const guardian_addresses = argv["guardian-address"]
.split(",")
.map((address) => evm_address(address).substring(24));
@ -127,7 +130,7 @@ export const builder = (y: typeof yargs) =>
BCS.bcsSerializeBytes(Buffer.from(governance_address, "hex")),
guardians_serializer.getBytes(),
];
const rpc = argv.rpc ?? NETWORKS[network].aptos.rpc;
const rpc = argv.rpc ?? NETWORKS[network].Aptos.rpc;
await callEntryFunc(
network,
rpc,
@ -152,12 +155,11 @@ export const builder = (y: typeof yargs) =>
.option("rpc", RPC_OPTIONS)
.option("named-addresses", NAMED_ADDRESSES_OPTIONS),
async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
const network = getNetwork(argv.network);
checkBinary("aptos", README_URL);
const p = buildPackage(argv["package-dir"], argv["named-addresses"]);
const b = serializePackage(p);
const rpc = argv.rpc ?? NETWORKS[network].aptos.rpc;
const rpc = argv.rpc ?? NETWORKS[network].Aptos.rpc;
await callEntryFunc(
network,
rpc,
@ -188,21 +190,18 @@ export const builder = (y: typeof yargs) =>
.option("rpc", RPC_OPTIONS)
.option("named-addresses", NAMED_ADDRESSES_OPTIONS),
async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
const network = getNetwork(argv.network);
checkBinary("aptos", README_URL);
const p = buildPackage(argv["package-dir"], argv["named-addresses"]);
const b = serializePackage(p);
const seed = Buffer.from(argv["seed"], "ascii");
// TODO(csongor): use deployer address from sdk (when it's there)
let module_name =
"0x277fa055b6a73c42c0662d5236c65c864ccbf2d4abd21f174a30c8b786eab84b::deployer";
if (network == "TESTNET" || network == "MAINNET") {
let module_name = APTOS_DEPLOYER_ADDRESS_DEVNET + "::deployer";
if (network == "Testnet" || network == "Mainnet") {
module_name =
"0x0108bc32f7de18a5f6e1e7d6ee7aff9f5fc858d0d87ac0da94dd8d2a5d267d6b::deployer";
}
const rpc = argv.rpc ?? NETWORKS[network].aptos.rpc;
const rpc = argv.rpc ?? NETWORKS[network].Aptos.rpc;
await callEntryFunc(
network,
rpc,
@ -226,13 +225,10 @@ export const builder = (y: typeof yargs) =>
})
.option("network", NETWORK_OPTIONS),
async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
const rpc = NETWORKS[network].aptos.rpc;
// TODO(csongor): use sdk address
let module_name =
"0x277fa055b6a73c42c0662d5236c65c864ccbf2d4abd21f174a30c8b786eab84b::sender";
if (network == "TESTNET" || network == "MAINNET") {
const network = getNetwork(argv.network);
const rpc = NETWORKS[network].Aptos.rpc;
let module_name = APTOS_DEPLOYER_ADDRESS_DEVNET + "::sender";
if (network == "Testnet" || network == "Mainnet") {
module_name =
"0x0108bc32f7de18a5f6e1e7d6ee7aff9f5fc858d0d87ac0da94dd8d2a5d267d6b::sender";
}
@ -287,13 +283,12 @@ export const builder = (y: typeof yargs) =>
})
.option("network", NETWORK_OPTIONS),
async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
let address = CONTRACTS[network].aptos.token_bridge;
const network = getNetwork(argv.network);
let address: string = contracts.tokenBridge(network, "Aptos");
if (address.startsWith("0x")) address = address.substring(2);
const token_bridge_address = Buffer.from(address, "hex");
assertChain(argv.chain);
const chain = coalesceChainId(argv.chain);
assertChain(toChain(argv.chain));
const chain = toChainId(argv.chain);
const origin_address = Buffer.from(
evm_address(argv["origin-address"]),
"hex"
@ -343,12 +338,11 @@ export const builder = (y: typeof yargs) =>
.option("rpc", RPC_OPTIONS)
.option("named-addresses", NAMED_ADDRESSES_OPTIONS),
async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
const network = getNetwork(argv.network);
checkBinary("aptos", README_URL);
const p = buildPackage(argv["package-dir"], argv["named-addresses"]);
const b = serializePackage(p);
const rpc = argv.rpc ?? NETWORKS[network].aptos.rpc;
const rpc = argv.rpc ?? NETWORKS[network].Aptos.rpc;
// TODO(csongor): use deployer address from sdk (when it's there)
const hash = await callEntryFunc(
network,
@ -378,10 +372,9 @@ export const builder = (y: typeof yargs) =>
.option("network", NETWORK_OPTIONS)
.option("rpc", RPC_OPTIONS),
async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
const network = getNetwork(argv.network);
checkBinary("aptos", README_URL);
const rpc = argv.rpc ?? NETWORKS[network].aptos.rpc;
const rpc = argv.rpc ?? NETWORKS[network].Aptos.rpc;
// TODO(csongor): use deployer address from sdk (when it's there)
const hash = await callEntryFunc(
network,

View File

@ -17,14 +17,14 @@
//
import { Implementation__factory } from "@certusone/wormhole-sdk/lib/esm/ethers-contracts";
import { CONTRACTS } from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import { Other } from "@certusone/wormhole-sdk/lib/esm/vaa";
import axios from "axios";
import { ethers } from "ethers";
import yargs from "yargs";
import { NETWORK_OPTIONS, NETWORKS } from "../consts";
import { assertNetwork, Network } from "../utils";
import { parse, Payload, serialiseVAA, sign, Signature, VAA } from "../vaa";
import { contracts, Network } from "@wormhole-foundation/sdk-base";
import { getNetwork } from "../utils";
export const command = "edit-vaa";
export const desc = "Edits or generates a VAA";
@ -102,8 +102,7 @@ export const builder = (y: typeof yargs) =>
export const handler = async (
argv: Awaited<ReturnType<typeof builder>["argv"]>
) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
const network = getNetwork(argv.network);
let numSigs = 0;
if (argv.signatures) {
@ -235,8 +234,8 @@ const getGuardianSet = async (
network: Network,
guardianSetIndex: number
): Promise<string[]> => {
let n = NETWORKS[network].ethereum;
let contract_address = CONTRACTS[network].ethereum.core;
let n = NETWORKS[network].Ethereum;
let contract_address = contracts.coreBridge(network, "Ethereum");
if (contract_address === undefined) {
throw Error(`Unknown core contract on ${network} for ethereum`);
}

View File

@ -1,12 +1,3 @@
import {
assertChain,
assertEVMChain,
ChainName,
CHAINS,
CONTRACTS,
isEVMChain,
toChainName,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import { ethers } from "ethers";
import { homedir } from "os";
import yargs from "yargs";
@ -18,7 +9,18 @@ import {
setStorageAt,
} from "../evm";
import { runCommand, VALIDATOR_OPTIONS } from "../startValidator";
import { assertNetwork, evm_address } from "../utils";
import {
assertEVMChain,
chainToChain,
evm_address,
getNetwork,
} from "../utils";
import {
assertChain,
chains,
contracts,
platformToChains,
} from "@wormhole-foundation/sdk-base";
export const command = "evm";
export const desc = "EVM utilities";
@ -81,12 +83,7 @@ export const builder = function (y: typeof yargs) {
}
)
.command("chains", "Return all EVM chains", async (_) => {
console.log(
Object.values(CHAINS)
.map((id) => toChainName(id))
.filter((name) => isEVMChain(name))
.join(" ")
);
console.log(...platformToChains("Evm"));
})
.command(
"info",
@ -96,7 +93,7 @@ export const builder = function (y: typeof yargs) {
.option("chain", {
alias: "c",
describe: "Chain to query",
choices: Object.keys(CHAINS) as ChainName[],
type: "string",
demandOption: true,
} as const)
.option("module", {
@ -120,11 +117,9 @@ export const builder = function (y: typeof yargs) {
demandOption: false,
}),
async (argv) => {
const chain = argv.chain;
assertChain(chain);
const chain = chainToChain(argv.chain);
assertEVMChain(chain);
const network = argv.network.toUpperCase();
assertNetwork(network);
const network = getNetwork(argv.network);
const module = argv.module;
const rpc = argv.rpc ?? NETWORKS[network][chain].rpc;
if (argv["implementation-only"]) {
@ -163,7 +158,7 @@ export const builder = function (y: typeof yargs) {
alias: "a",
describe: "Core contract address",
type: "string",
default: CONTRACTS.MAINNET.ethereum.core,
default: contracts.coreBridge("Mainnet", "Ethereum"),
})
.option("guardian-address", {
alias: "g",
@ -180,7 +175,7 @@ export const builder = function (y: typeof yargs) {
}),
async (argv) => {
const guardian_addresses = argv["guardian-address"].split(",");
let rpc = argv.rpc ?? NETWORKS.DEVNET.ethereum.rpc;
let rpc = argv.rpc ?? NETWORKS.Devnet.Ethereum.rpc;
await hijack_evm(
rpc,
argv["core-contract-address"],

View File

@ -1,21 +1,11 @@
import { tryNativeToHexString } from "@certusone/wormhole-sdk/lib/esm/utils/array";
import {
assertChain,
ChainName,
CHAINS,
isCosmWasmChain,
isEVMChain,
toChainId,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import { fromBech32, toHex } from "@cosmjs/encoding";
import base58 from "bs58";
import { sha3_256 } from "js-sha3";
import yargs from "yargs";
import { GOVERNANCE_CHAIN, GOVERNANCE_EMITTER } from "../consts";
import { evm_address } from "../utils";
import { chainToChain, evm_address } from "../utils";
import {
ContractUpgrade,
impossible,
Payload,
PortalRegisterChain,
RecoverChainId,
@ -25,6 +15,13 @@ import {
VAA,
WormholeRelayerSetDefaultDeliveryProvider,
} from "../vaa";
import {
Chain,
assertChain,
chainToPlatform,
chains,
toChainId,
} from "@wormhole-foundation/sdk-base";
function makeVAA(
emitterChain: number,
@ -68,7 +65,7 @@ export const builder = function (y: typeof yargs) {
.option("chain", {
alias: "c",
describe: "Chain to register",
choices: Object.keys(CHAINS) as ChainName[],
type: "string",
demandOption: true,
} as const)
.option("contract-address", {
@ -85,13 +82,13 @@ export const builder = function (y: typeof yargs) {
} as const),
(argv) => {
const module = argv["module"];
assertChain(argv.chain);
const chain = chainToChain(argv.chain);
const payload: PortalRegisterChain<typeof module> = {
module,
type: "RegisterChain",
chain: 0,
emitterChain: toChainId(argv.chain),
emitterAddress: parseAddress(argv.chain, argv["contract-address"]),
emitterChain: toChainId(chain),
emitterAddress: parseAddress(chain, argv["contract-address"]),
};
const vaa = makeVAA(
GOVERNANCE_CHAIN,
@ -111,7 +108,7 @@ export const builder = function (y: typeof yargs) {
.option("chain", {
alias: "c",
describe: "Chain to upgrade",
choices: Object.keys(CHAINS) as ChainName[],
type: "string",
demandOption: true,
} as const)
.option("contract-address", {
@ -127,13 +124,13 @@ export const builder = function (y: typeof yargs) {
demandOption: true,
} as const),
(argv) => {
assertChain(argv.chain);
const chain = chainToChain(argv.chain);
const module = argv["module"];
const payload: ContractUpgrade = {
module,
type: "ContractUpgrade",
chain: toChainId(argv.chain),
address: parseCodeAddress(argv.chain, argv["contract-address"]),
chain: toChainId(chain),
address: parseCodeAddress(chain, argv["contract-address"]),
};
const vaa = makeVAA(
GOVERNANCE_CHAIN,
@ -152,7 +149,7 @@ export const builder = function (y: typeof yargs) {
.option("emitter-chain", {
alias: "e",
describe: "Emitter chain of the VAA",
choices: Object.keys(CHAINS) as ChainName[],
type: "string",
demandOption: true,
} as const)
.option("emitter-address", {
@ -164,7 +161,7 @@ export const builder = function (y: typeof yargs) {
.option("chain", {
alias: "c",
describe: "Token's chain",
choices: Object.keys(CHAINS) as ChainName[],
type: "string",
demandOption: true,
} as const)
.option("token-address", {
@ -192,15 +189,14 @@ export const builder = function (y: typeof yargs) {
demandOption: true,
}),
(argv) => {
const emitter_chain = argv["emitter-chain"];
assertChain(argv.chain);
assertChain(emitter_chain);
const emitter_chain = chainToChain(argv["emitter-chain"]);
const chain = chainToChain(argv.chain);
const payload: TokenBridgeAttestMeta = {
module: "TokenBridge",
type: "AttestMeta",
chain: 0,
tokenAddress: parseAddress(argv.chain, argv["token-address"]),
tokenChain: toChainId(argv.chain),
tokenAddress: parseAddress(chain, argv["token-address"]),
tokenChain: toChainId(chain),
decimals: argv["decimals"],
symbol: argv["symbol"],
name: argv["name"],
@ -263,7 +259,7 @@ export const builder = function (y: typeof yargs) {
.option("chain", {
alias: "c",
describe: "Chain of Wormhole Relayer contract",
choices: Object.keys(CHAINS),
type: "string",
demandOption: true,
} as const)
.option("delivery-provider-address", {
@ -274,13 +270,13 @@ export const builder = function (y: typeof yargs) {
});
},
(argv) => {
assertChain(argv.chain);
const chain = chainToChain(argv.chain);
const payload: WormholeRelayerSetDefaultDeliveryProvider = {
module: "WormholeRelayer",
type: "SetDefaultDeliveryProvider",
chain: toChainId(argv["chain"]),
chain: toChainId(chain),
relayProviderAddress: parseAddress(
argv["chain"],
chain,
argv["delivery-provider-address"]
),
};
@ -297,45 +293,43 @@ export const builder = function (y: typeof yargs) {
};
export const handler = () => {};
function parseAddress(chain: ChainName, address: string): string {
if (chain === "unset") {
throw Error("Chain unset");
} else if (isEVMChain(chain)) {
function parseAddress(chain: Chain, address: string): string {
if (chainToPlatform(chain) === "Evm") {
return "0x" + evm_address(address);
} else if (isCosmWasmChain(chain)) {
} else if (chainToPlatform(chain) === "Cosmwasm") {
return "0x" + toHex(fromBech32(address).data).padStart(64, "0");
} else if (chain === "solana" || chain === "pythnet") {
} else if (chain === "Solana" || chain === "Pythnet") {
return "0x" + toHex(base58.decode(address)).padStart(64, "0");
} else if (chain === "algorand") {
} else if (chain === "Algorand") {
// TODO: is there a better native format for algorand?
return "0x" + evm_address(address);
} else if (chain === "near") {
} else if (chain === "Near") {
return "0x" + evm_address(address);
} else if (chain === "sui") {
} else if (chain === "Sui") {
return "0x" + evm_address(address);
} else if (chain === "aptos") {
} else if (chain === "Aptos") {
if (/^(0x)?[0-9a-fA-F]+$/.test(address)) {
return "0x" + evm_address(address);
}
return sha3_256(Buffer.from(address)); // address is hash of fully qualified type
} else if (chain === "btc") {
} else if (chain === "Btc") {
throw Error("btc is not supported yet");
} else if (chain === "cosmoshub") {
} else if (chain === "Cosmoshub") {
throw Error("cosmoshub is not supported yet");
} else if (chain === "evmos") {
} else if (chain === "Evmos") {
throw Error("evmos is not supported yet");
} else if (chain === "kujira") {
} else if (chain === "Kujira") {
throw Error("kujira is not supported yet");
} else if (chain === "rootstock") {
} else if (chain === "Rootstock") {
throw Error("rootstock is not supported yet");
} else {
impossible(chain);
throw Error(`Unsupported chain: ${chain}`);
}
}
function parseCodeAddress(chain: ChainName, address: string): string {
if (isCosmWasmChain(chain)) {
function parseCodeAddress(chain: Chain, address: string): string {
if (chainToPlatform(chain) === "Cosmwasm") {
return "0x" + parseInt(address, 10).toString(16).padStart(64, "0");
} else {
return parseAddress(chain, address);

View File

@ -1,9 +1,6 @@
import {
assertChain,
coalesceChainId,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import yargs from "yargs";
import { CHAIN_ID_OR_NAME_CHOICES } from "../../consts";
import { assertChain, toChain } from "@wormhole-foundation/sdk-base";
export const command = "chain-id <chain>";
export const desc =
@ -16,6 +13,6 @@ export const builder = (y: typeof yargs) => {
} as const);
};
export const handler = (argv: Awaited<ReturnType<typeof builder>["argv"]>) => {
assertChain(argv.chain);
console.log(coalesceChainId(argv.chain));
assertChain(toChain(argv.chain));
console.log(toChain(argv.chain));
};

View File

@ -1,13 +1,7 @@
import {
CHAINS,
ChainName,
assertChain,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import { relayer } from "@certusone/wormhole-sdk";
import yargs from "yargs";
import { CONTRACTS } from "../../consts";
import { assertNetwork } from "../../utils";
import { impossible } from "../../vaa";
import { contracts } from "@wormhole-foundation/sdk-base";
import { chainToChain, getNetwork } from "../../utils";
export const command = "contract <network> <chain> <module>";
export const desc = "Print contract address";
@ -20,7 +14,7 @@ export const builder = (y: typeof yargs) =>
} as const)
.positional("chain", {
describe: "Chain to query",
choices: Object.keys(CHAINS) as ChainName[],
type: "string",
demandOption: true,
} as const)
.positional("module", {
@ -31,30 +25,27 @@ export const builder = (y: typeof yargs) =>
export const handler = async (
argv: Awaited<ReturnType<typeof builder>["argv"]>
) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
const chain = argv.chain;
assertChain(chain);
const network = getNetwork(argv.network);
const chain = chainToChain(argv.chain);
const module = argv["module"];
let addr: string | undefined;
switch (module) {
case "Core":
addr = CONTRACTS[network][chain].core;
addr = contracts.coreBridge.get(network, chain);
break;
case "NFTBridge":
const addresses = CONTRACTS[network][chain];
if (!("nft_bridge" in addresses)) {
addr = contracts.nftBridge.get(network, chain);
if (!addr) {
throw new Error(`NFTBridge not deployed on ${chain}`);
}
addr = addresses.nft_bridge;
break;
case "TokenBridge":
addr = CONTRACTS[network][chain].token_bridge;
addr = contracts.tokenBridge.get(network, chain);
break;
case "WormholeRelayer":
addr = relayer.RELAYER_CONTRACTS[network][chain]?.wormholeRelayerAddress;
addr = contracts.relayer.get(network, chain);
break;
default:
impossible(module);

View File

@ -1,10 +1,6 @@
import {
CHAINS,
ChainName,
assertChain,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import yargs from "yargs";
import { getEmitterAddress } from "../../emitter";
import { assertChain, chains } from "@wormhole-foundation/sdk-base";
export const command = "emitter <chain> <address>";
export const desc = "Print address in emitter address format";
@ -13,7 +9,7 @@ export const builder = (y: typeof yargs) =>
.positional("chain", {
describe: "Chain to query",
type: "string",
choices: Object.keys(CHAINS) as ChainName[],
choices: chains,
demandOption: true,
} as const)
.positional("address", {

View File

@ -1,8 +1,9 @@
import { tryUint8ArrayToNative } from "@certusone/wormhole-sdk/lib/esm/utils";
import yargs from "yargs";
import { getOriginalAsset } from "../../chains/generic";
import { CHAIN_ID_OR_NAME_CHOICES, RPC_OPTIONS } from "../../consts";
import { assertNetwork } from "../../utils";
import { getNetwork } from "../../utils";
import { tryUint8ArrayToNative } from "../../sdk/array";
import { toChain } from "@wormhole-foundation/sdk-base";
export const command = "origin <chain> <address>";
export const desc = `Print the origin chain and address of the asset that corresponds to the given chain and address.`;
@ -32,12 +33,11 @@ export const handler = async (
const consoleWarnTemp = console.warn;
console.warn = () => {};
const network = argv.network.toUpperCase();
assertNetwork(network);
const network = getNetwork(argv.network);
const res = await getOriginalAsset(argv.chain, network, argv.address);
console.log({
...res,
assetAddress: tryUint8ArrayToNative(res.assetAddress, res.chainId),
assetAddress: tryUint8ArrayToNative(res.assetAddress, toChain(res.chainId)),
});
console.warn = consoleWarnTemp;

View File

@ -3,16 +3,17 @@
// is defined in the consts.ts file in the SDK (to verify that all chains // are properly registered.)
import yargs from "yargs";
import {
assertChain,
ChainName,
CHAINS,
Contracts,
CONTRACTS,
isEVMChain,
isTerraChain,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import { getEmitterAddress } from "../../emitter";
import {
Network,
assertChain,
chainToPlatform,
chains,
contracts,
toChain,
} from "@wormhole-foundation/sdk-base";
import { chainToChain, getNetwork } from "../../utils";
import { Chain } from "@wormhole-foundation/sdk";
export const command = "registrations <network> <chain> <module>";
export const desc = "Print chain registrations";
@ -25,7 +26,7 @@ export const builder = (y: typeof yargs) => {
} as const)
.positional("chain", {
describe: "Chain to query",
choices: Object.keys(CHAINS) as ChainName[],
type: "string",
demandOption: true,
} as const)
.positional("module", {
@ -45,43 +46,39 @@ export const builder = (y: typeof yargs) => {
export const handler = async (
argv: Awaited<ReturnType<typeof builder>["argv"]>
) => {
assertChain(argv.chain);
const chain = argv.chain;
const network = argv.network.toUpperCase();
if (network !== "MAINNET" && network !== "TESTNET" && network !== "DEVNET") {
throw Error(`Unknown network: ${network}`);
}
const chain = chainToChain(argv.chain);
const network = getNetwork(argv.network);
const module = argv.module;
if (module !== "TokenBridge" && module !== "NFTBridge") {
throw Error(`Module must be TokenBridge or NFTBridge`);
}
let results: object;
if (chain === "solana") {
if (chain === "Solana") {
const solana = require("../../solana");
results = await solana.queryRegistrationsSolana(network, module);
} else if (isEVMChain(chain)) {
} else if (chainToPlatform(chain) === "Evm") {
const evm = require("../../evm");
results = await evm.queryRegistrationsEvm(network, chain, module);
} else if (isTerraChain(chain) || chain === "xpla") {
} else if (chain === "Terra" || chain === "Terra2" || chain === "Xpla") {
const terra = require("../../terra");
results = await terra.queryRegistrationsTerra(network, chain, module);
} else if (chain === "injective") {
} else if (chain === "Injective") {
const injective = require("../../injective");
results = await injective.queryRegistrationsInjective(network, module);
} else if (chain === "sei") {
} else if (chain === "Sei") {
const sei = require("../../chains/sei/registrations");
results = await sei.queryRegistrationsSei(network, module);
} else if (chain === "sui") {
} else if (chain === "Sui") {
const sui = require("../../chains/sui/registrations");
results = await sui.queryRegistrationsSui(network, module);
} else if (chain === "aptos") {
} else if (chain === "Aptos") {
const aptos = require("../../aptos");
results = await aptos.queryRegistrationsAptos(network, module);
} else {
throw Error(`Command not supported for chain ${chain}`);
}
if (argv["verify"]) {
verifyRegistrations(network, chain as string, module, results);
verifyRegistrations(network, chain, module, results);
} else {
console.log(results);
}
@ -89,8 +86,8 @@ export const handler = async (
// verifyRegistrations takes the results returned above and verifies them against the expected values in the consts file.
async function verifyRegistrations(
network: "MAINNET" | "TESTNET" | "DEVNET",
chain: string,
network: Network,
chain: Chain,
module: "NFTBridge" | "TokenBridge",
input: Object
) {
@ -104,25 +101,21 @@ async function verifyRegistrations(
// Loop over the chains and make sure everything is in our input, and the values match.
const results: { [key: string]: string } = {};
for (const chainStr in CHAINS) {
const thisChain = chainStr as ChainName;
if (thisChain === "unset" || thisChain === chain) {
for (const chainStr of chains) {
const thisChain = toChain(chainStr);
if (thisChain === chain) {
continue;
}
const contracts: Contracts = CONTRACTS[network][thisChain];
let expectedAddr: string | undefined;
if (module === "TokenBridge") {
expectedAddr = contracts.token_bridge;
expectedAddr = contracts.tokenBridge.get(network, thisChain);
} else {
expectedAddr = contracts.nft_bridge;
expectedAddr = contracts.nftBridge.get(network, thisChain);
}
if (expectedAddr !== undefined) {
expectedAddr = await getEmitterAddress(
thisChain as ChainName,
expectedAddr
);
expectedAddr = await getEmitterAddress(thisChain, expectedAddr);
if (!expectedAddr.startsWith("0x")) {
expectedAddr = "0x" + expectedAddr;
}

View File

@ -1,11 +1,6 @@
import {
CHAINS,
ChainName,
assertChain,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import yargs from "yargs";
import { NETWORKS } from "../../consts";
import { assertNetwork } from "../../utils";
import { chainToChain, getNetwork } from "../../utils";
export const command = "rpc <network> <chain>";
export const desc = "Print RPC address";
@ -18,14 +13,12 @@ export const builder = (y: typeof yargs) =>
} as const)
.positional("chain", {
describe: "Chain to query",
choices: Object.keys(CHAINS) as ChainName[],
type: "string",
demandOption: true,
} as const);
export const handler = async (
argv: Awaited<ReturnType<typeof builder>["argv"]>
) => {
assertChain(argv.chain);
const network = argv.network.toUpperCase();
assertNetwork(network);
console.log(NETWORKS[network][argv.chain].rpc);
const network = getNetwork(argv.network);
console.log(NETWORKS[network][chainToChain(argv.chain)].rpc);
};

View File

@ -1,8 +1,8 @@
import { assertChain } from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import yargs from "yargs";
import { getWrappedAssetAddress } from "../../chains/generic/getWrappedAssetAddress";
import { CHAIN_ID_OR_NAME_CHOICES, RPC_OPTIONS } from "../../consts";
import { assertNetwork } from "../../utils";
import { RPC_OPTIONS } from "../../consts";
import { chainToChain, getNetwork } from "../../utils";
export const command = "wrapped <origin-chain> <origin-address> <target-chain>";
export const desc =
@ -11,7 +11,7 @@ export const builder = (y: typeof yargs) =>
y
.positional("origin-chain", {
describe: "Chain that wrapped asset came from",
choices: CHAIN_ID_OR_NAME_CHOICES,
type: "string",
demandOption: true,
} as const)
.positional("origin-address", {
@ -21,7 +21,7 @@ export const builder = (y: typeof yargs) =>
})
.positional("target-chain", {
describe: "Chain to query for wrapped asset address",
choices: CHAIN_ID_OR_NAME_CHOICES,
type: "string",
demandOption: true,
} as const)
.option("network", {
@ -42,14 +42,13 @@ export const handler = async (
const consoleWarnTemp = console.warn;
console.warn = () => {};
const originChain = argv["origin-chain"];
const originChain = chainToChain(argv["origin-chain"]);
const originAddress = argv["origin-address"];
const targetChain = argv["target-chain"];
const network = argv.network.toUpperCase();
const targetChain = chainToChain(argv["target-chain"]);
const network = getNetwork(argv.network);
assertChain(originChain);
assertChain(targetChain);
assertNetwork(network);
console.log(
await getWrappedAssetAddress(

View File

@ -4,8 +4,9 @@ import { Account, KeyPair, connect } from "near-api-js";
import { InMemoryKeyStore } from "near-api-js/lib/key_stores";
import { parseSeedPhrase } from "near-seed-phrase";
import yargs from "yargs";
import { CONTRACTS, NETWORKS, NETWORK_OPTIONS, RPC_OPTIONS } from "../consts";
import { assertNetwork } from "../utils";
import { NETWORKS, NETWORK_OPTIONS, RPC_OPTIONS } from "../consts";
import { contracts } from "@wormhole-foundation/sdk-base";
import { getNetwork } from "../utils";
// Near utilities
export const command = "near";
@ -55,14 +56,12 @@ export const builder = function (y: typeof yargs) {
demandOption: true,
}),
async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
const contracts = CONTRACTS[network].near;
const network = getNetwork(argv.network);
const {
rpc: defaultRpc,
key: defaultKey,
networkId,
} = NETWORKS[network].near;
} = NETWORKS[network].Near;
const key =
argv.key ??
@ -80,12 +79,12 @@ export const builder = function (y: typeof yargs) {
let target = argv.target;
if (!argv.target && argv.module) {
if (argv.module === "Core") {
target = contracts.core;
target = contracts.coreBridge(network, "Near");
console.log("Setting target to core");
}
if (argv.module === "TokenBridge") {
target = contracts.token_bridge;
target = contracts.tokenBridge(network, "Near");
console.log("Setting target to token_bridge");
}
}
@ -125,14 +124,12 @@ export const builder = function (y: typeof yargs) {
demandOption: true,
}),
async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
const contracts = CONTRACTS[network].near;
const network = getNetwork(argv.network);
const {
rpc: defaultRpc,
key: defaultKey,
networkId,
} = NETWORKS[network].near;
} = NETWORKS[network].Near;
const key =
argv.key ??
@ -150,12 +147,12 @@ export const builder = function (y: typeof yargs) {
let target = argv.target;
if (!argv.target && argv.module) {
if (argv.module === "Core") {
target = contracts.core;
target = contracts.coreBridge(network, "Near");
console.log("Setting target to core");
}
if (argv.module === "TokenBridge") {
target = contracts.token_bridge;
target = contracts.tokenBridge(network, "Near");
console.log("Setting target to token_bridge");
}
}

View File

@ -1,14 +1,9 @@
import {
CHAINS,
ChainName,
assertChain,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import { relayer, Network } from "@certusone/wormhole-sdk";
import yargs, { string } from "yargs";
import { CONTRACTS, NETWORKS } from "../consts";
import { assertNetwork } from "../utils";
import { impossible } from "../vaa";
import yargs from "yargs";
import { ethers } from "ethers";
import { NETWORKS } from "../consts";
import { chainToChain, getNetwork } from "../utils";
import { Chain, assertChain, contracts } from "@wormhole-foundation/sdk-base";
import { relayer } from "@certusone/wormhole-sdk";
export const command = "status <network> <chain> <tx>";
export const desc =
@ -22,7 +17,7 @@ export const builder = (y: typeof yargs) =>
} as const)
.positional("chain", {
describe: "Source chain",
choices: Object.keys(CHAINS) as ChainName[],
type: "string",
demandOption: true,
} as const)
.positional("tx", {
@ -33,34 +28,32 @@ export const builder = (y: typeof yargs) =>
export const handler = async (
argv: Awaited<ReturnType<typeof builder>["argv"]>
) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
const chain = argv.chain;
const network = getNetwork(argv.network);
const chain = chainToChain(argv.chain);
assertChain(chain);
const addr =
relayer.RELAYER_CONTRACTS[network][chain]?.wormholeRelayerAddress;
const addr = contracts.relayer.get(network, chain);
if (!addr) {
throw new Error(`Wormhole Relayer not deployed on ${chain} in ${network}`);
}
const sourceRPC = NETWORKS[network as Network][chain as ChainName].rpc;
const sourceRPC = NETWORKS[network][chain].rpc;
const sourceChainProvider = new ethers.providers.JsonRpcProvider(sourceRPC);
const targetChainProviders = new Map<ChainName, ethers.providers.Provider>();
const targetChainProviders = new Map<Chain, ethers.providers.Provider>();
for (const key in NETWORKS[network]) {
targetChainProviders.set(
key as ChainName,
new ethers.providers.JsonRpcProvider(
NETWORKS[network as Network][key as ChainName].rpc
)
key as Chain,
new ethers.providers.JsonRpcProvider(NETWORKS[network][key as Chain].rpc)
);
}
const info = await relayer.getWormholeRelayerInfo(chain, argv.tx, {
environment: network,
sourceChainProvider,
targetChainProviders,
});
// TODO: Convert this over to sdkv2
// const info = await relayer.getWormholeRelayerInfo(chain, argv.tx, {
// environment: network,
// sourceChainProvider,
// targetChainProviders,
// });
// console.log(relayer.stringifyWormholeRelayerInfo(info));
console.log(relayer.stringifyWormholeRelayerInfo(info));
console.log("Not implemented");
};

View File

@ -1,15 +1,3 @@
import {
assertChain,
ChainId,
ChainName,
CHAINS,
coalesceChainName,
Contracts,
CONTRACTS,
isEVMChain,
isTerraChain,
toChainName,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import yargs from "yargs";
import { execute_algorand } from "../algorand";
import { execute_aptos } from "../aptos";
@ -21,11 +9,22 @@ import { execute_injective } from "../injective";
import { execute_near } from "../near";
import { execute_solana } from "../solana";
import { execute_terra } from "../terra";
import { assertNetwork } from "../utils";
import { assertKnownPayload, impossible, parse, Payload, VAA } from "../vaa";
import { execute_xpla } from "../xpla";
import { NETWORKS } from "../consts";
import { Network } from "../utils";
import { chainToChain, getNetwork } from "../utils";
import {
Chain,
Network,
PlatformToChains,
assertChain,
assertChainId,
chainIdToChain,
chainToPlatform,
chains,
contracts,
toChain,
} from "@wormhole-foundation/sdk";
export const command = "submit <vaa>";
export const desc = "Execute a VAA";
@ -39,7 +38,7 @@ export const builder = (y: typeof yargs) =>
.option("chain", {
alias: "c",
describe: "chain name",
choices: Object.keys(CHAINS) as ChainName[],
type: "string",
demandOption: false,
} as const)
.option("network", NETWORK_OPTIONS)
@ -72,8 +71,7 @@ export const handler = async (
assertKnownPayload(parsed_vaa);
console.log(parsed_vaa.payload);
const network = argv.network.toUpperCase();
assertNetwork(network);
const network = getNetwork(argv.network);
if (argv["all-chains"]) {
if (argv.rpc) {
@ -104,22 +102,34 @@ export const handler = async (
// get VAA chain
const vaa_chain_id =
"chain" in parsed_vaa.payload ? parsed_vaa.payload.chain : 0;
assertChain(vaa_chain_id);
const vaa_chain = toChainName(vaa_chain_id);
// if vaa_chain_id is 0, it means the chain is not specified in the VAA.
// We don't have a notion of an unsupported chain, so we don't want to just assert.
let vaa_chain;
if (vaa_chain_id !== 0) {
assertChainId(vaa_chain_id);
vaa_chain = chainIdToChain(vaa_chain_id);
}
// get chain from command line arg
const cli_chain = argv.chain;
const cli_chain = argv.chain ? chainToChain(argv.chain) : argv.chain;
let chain: ChainName;
let chain: Chain;
if (cli_chain !== undefined) {
assertChain(cli_chain);
if (vaa_chain !== "unset" && cli_chain !== vaa_chain) {
if (vaa_chain && cli_chain !== vaa_chain) {
throw Error(
`Specified target chain (${cli_chain}) does not match VAA target chain (${vaa_chain})`
);
}
chain = coalesceChainName(cli_chain);
chain = toChain(cli_chain);
} else {
if (!vaa_chain) {
throw Error(
`VAA does not specify a target chain and one was not provided, please specify one with --chain or -c`
);
}
assertChain(vaa_chain);
chain = vaa_chain;
}
@ -139,75 +149,59 @@ async function executeSubmit(
parsedVaa: VAA<Payload>,
buf: Buffer,
network: Network,
chain: ChainName,
chain: Chain,
rpc: string | undefined,
contractAddress: string | undefined
) {
if (chain === "unset") {
throw Error(
"This VAA does not specify the target chain, please provide it by hand using the '--chain' flag."
);
} else if (isEVMChain(chain)) {
if (chainToPlatform(chain) === "Evm") {
await execute_evm(
parsedVaa.payload,
buf,
network,
chain,
chain as PlatformToChains<"Evm">,
contractAddress,
rpc
);
} else if (isTerraChain(chain)) {
} else if (chain === "Terra" || chain === "Terra2") {
await execute_terra(parsedVaa.payload, buf, network, chain);
} else if (chain === "solana" || chain === "pythnet") {
} else if (chain === "Solana" || chain === "Pythnet") {
await execute_solana(parsedVaa, buf, network, chain);
} else if (chain === "algorand") {
} else if (chain === "Algorand") {
await execute_algorand(
parsedVaa.payload,
new Uint8Array(Buffer.from(vaaHex, "hex")),
network
);
} else if (chain === "near") {
} else if (chain === "Near") {
await execute_near(parsedVaa.payload, vaaHex, network);
} else if (chain === "injective") {
} else if (chain === "Injective") {
await execute_injective(parsedVaa.payload, buf, network);
} else if (chain === "xpla") {
} else if (chain === "Xpla") {
await execute_xpla(parsedVaa.payload, buf, network);
} else if (chain === "sei") {
} else if (chain === "Sei") {
await submitSei(parsedVaa.payload, buf, network, rpc);
} else if (chain === "osmosis") {
} else if (chain === "Osmosis") {
throw Error("OSMOSIS is not supported yet");
} else if (chain === "sui") {
} else if (chain === "Sui") {
await submitSui(parsedVaa.payload, buf, network, rpc);
} else if (chain === "aptos") {
} else if (chain === "Aptos") {
await execute_aptos(parsedVaa.payload, buf, network, contractAddress, rpc);
} else if (chain === "wormchain") {
} else if (chain === "Wormchain") {
throw Error("Wormchain is not supported yet");
} else if (chain === "btc") {
} else if (chain === "Btc") {
throw Error("btc is not supported yet");
} else if (chain === "cosmoshub") {
} else if (chain === "Cosmoshub") {
throw Error("Cosmoshub is not supported yet");
} else if (chain === "evmos") {
} else if (chain === "Evmos") {
throw Error("Evmos is not supported yet");
} else if (chain === "kujira") {
} else if (chain === "Kujira") {
throw Error("kujira is not supported yet");
} else if (chain === "neutron") {
} else if (chain === "Neutron") {
throw Error("neutron is not supported yet");
} else if (chain === "celestia") {
} else if (chain === "Celestia") {
throw Error("celestia is not supported yet");
} else if (chain === "stargaze") {
throw Error("stargaze is not supported yet");
} else if (chain === "seda") {
throw Error("seda is not supported yet");
} else if (chain === "dymension") {
throw Error("dymension is not supported yet");
} else if (chain === "provenance") {
throw Error("provenance is not supported yet");
} else if (chain === "rootstock") {
throw Error("rootstock is not supported yet");
} else {
// If you get a type error here, hover over `chain`'s type and it tells you
// which cases are not handled
impossible(chain);
throw new Error(`Unsupported chain: ${chain}`);
}
}
@ -217,24 +211,19 @@ async function submitToAll(
buf: Buffer,
network: Network
) {
let skip_chain: ChainName = "unset";
let skip_chain: Chain;
if (parsedVaa.payload.type === "RegisterChain") {
skip_chain = toChainName(parsedVaa.payload.emitterChain as ChainId);
skip_chain = toChain(parsedVaa.payload.emitterChain);
} else if (parsedVaa.payload.type === "AttestMeta") {
skip_chain = toChainName(parsedVaa.payload.tokenChain as ChainId);
skip_chain = toChain(parsedVaa.payload.tokenChain);
} else {
throw Error(
`Invalid VAA payload type (${parsedVaa.payload.type}), only "RegisterChain" and "AttestMeta" are supported with --all-chains`
);
}
for (const chainStr in CHAINS) {
let chain = chainStr as ChainName;
if (chain === "unset") {
continue;
}
for (const chain of chains) {
const n = NETWORKS[network][chain];
const contracts: Contracts = CONTRACTS[network][chain];
if (chain == skip_chain) {
console.log(`Skipping ${chain} because it's the origin chain`);
continue;
@ -243,15 +232,11 @@ async function submitToAll(
console.log(`Skipping ${chain} because the rpc is not defined`);
continue;
}
if (!contracts) {
console.log(
`Skipping ${chain} because the contract entry is not defined`
);
return true;
}
if (
(parsedVaa.payload.module === "TokenBridge" && !contracts.token_bridge) ||
(parsedVaa.payload.module === "NFTBridge" && !contracts.nft_bridge)
(parsedVaa.payload.module === "TokenBridge" &&
!contracts.tokenBridge.get(network, chain)) ||
(parsedVaa.payload.module === "NFTBridge" &&
!contracts.nftBridge.get(network, chain))
) {
console.log(`Skipping ${chain} because the contract is not defined`);
continue;

View File

@ -1,14 +1,10 @@
import path from "path";
import yargs from "yargs";
import { buildCoin, getProvider } from "../../chains/sui";
import {
CONTRACTS,
NETWORKS,
NETWORK_OPTIONS,
RPC_OPTIONS,
} from "../../consts";
import { assertNetwork, checkBinary } from "../../utils";
import { NETWORKS, NETWORK_OPTIONS, RPC_OPTIONS } from "../../consts";
import { checkBinary, getNetwork } from "../../utils";
import { YargsAddCommandsFn } from "../Yargs";
import { contracts } from "@wormhole-foundation/sdk";
const README_URL =
"https://github.com/wormhole-foundation/wormhole/blob/main/sui/README.md";
@ -58,17 +54,16 @@ export const addBuildCommands: YargsAddCommandsFn = (y: typeof yargs) =>
async (argv) => {
checkBinary("sui", README_URL);
const network = argv.network.toUpperCase();
assertNetwork(network);
const network = getNetwork(argv.network);
const decimals = argv["decimals"];
const version = argv["version-struct"];
const packagePath =
argv["package-path"] ??
path.resolve(__dirname, "../../../../../sui/examples");
const coreBridgeStateObjectId =
argv["wormhole-state"] ?? CONTRACTS[network].sui.core;
argv["wormhole-state"] ?? contracts.coreBridge(network, "Sui");
const tokenBridgeStateObjectId =
argv["token-bridge-state"] ?? CONTRACTS[network].sui.token_bridge;
argv["token-bridge-state"] ?? contracts.tokenBridge(network, "Sui");
if (!coreBridgeStateObjectId) {
throw new Error(
@ -84,7 +79,7 @@ export const addBuildCommands: YargsAddCommandsFn = (y: typeof yargs) =>
const provider = getProvider(
network,
argv.rpc ?? NETWORKS[network].sui.rpc
argv.rpc ?? NETWORKS[network].Sui.rpc
);
const build = await buildCoin(
provider,

View File

@ -17,8 +17,9 @@ import {
PRIVATE_KEY_OPTIONS,
RPC_OPTIONS,
} from "../../consts";
import { Network, assertNetwork, checkBinary } from "../../utils";
import { checkBinary, getNetwork } from "../../utils";
import { YargsAddCommandsFn } from "../Yargs";
import { Network } from "@wormhole-foundation/sdk";
const README_URL =
"https://github.com/wormhole-foundation/wormhole/blob/main/sui/README.md";
@ -42,8 +43,7 @@ export const addDeployCommands: YargsAddCommandsFn = (y: typeof yargs) =>
checkBinary("sui", README_URL);
const packageDir = argv["package-dir"];
const network = argv.network.toUpperCase();
assertNetwork(network);
const network = getNetwork(argv.network);
const debug = argv.debug ?? false;
const privateKey = argv["private-key"];
const rpc = argv.rpc;
@ -66,7 +66,7 @@ export const deploy = async (
rpc?: string,
privateKey?: string
): Promise<SuiTransactionBlockResponse> => {
rpc = rpc ?? NETWORKS[network].sui.rpc;
rpc = rpc ?? NETWORKS[network].Sui.rpc;
const provider = getProvider(network, rpc);
const signer = getSigner(provider, network, privateKey);

View File

@ -22,8 +22,9 @@ import {
PRIVATE_KEY_OPTIONS,
RPC_OPTIONS,
} from "../../consts";
import { Network, assertNetwork } from "../../utils";
import { YargsAddCommandsFn } from "../Yargs";
import { getNetwork } from "../../utils";
import { Network } from "@wormhole-foundation/sdk";
export const addInitCommands: YargsAddCommandsFn = (y: typeof yargs) =>
y
@ -48,8 +49,7 @@ export const addInitCommands: YargsAddCommandsFn = (y: typeof yargs) =>
.option("private-key", PRIVATE_KEY_OPTIONS)
.option("rpc", RPC_OPTIONS),
async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
const network = getNetwork(argv.network);
const packageId = argv["package-id"];
const wormholeStateObjectId = argv["wormhole-state"];
const privateKey = argv["private-key"];
@ -108,14 +108,13 @@ export const addInitCommands: YargsAddCommandsFn = (y: typeof yargs) =>
.option("private-key", PRIVATE_KEY_OPTIONS)
.option("rpc", RPC_OPTIONS),
async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
const network = getNetwork(argv.network);
const packageId = argv["package-id"];
const wormholeStateObjectId = argv["wormhole-state"];
const governanceChainId = argv["governance-chain-id"];
const governanceContract = argv["governance-address"];
const privateKey = argv["private-key"];
const rpc = argv.rpc ?? NETWORKS[network].sui.rpc;
const rpc = argv.rpc ?? NETWORKS[network].Sui.rpc;
const res = await initTokenBridge(
network,
@ -180,8 +179,7 @@ export const addInitCommands: YargsAddCommandsFn = (y: typeof yargs) =>
.option("private-key", PRIVATE_KEY_OPTIONS)
.option("rpc", RPC_OPTIONS),
async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
const network = getNetwork(argv.network);
const packageId = argv["package-id"];
const initialGuardian = argv["initial-guardian"];
const debug = argv.debug ?? false;
@ -222,7 +220,7 @@ export const initExampleApp = async (
rpc?: string,
privateKey?: string
): Promise<SuiTransactionBlockResponse> => {
rpc = rpc ?? NETWORKS[network].sui.rpc;
rpc = rpc ?? NETWORKS[network].Sui.rpc;
const provider = getProvider(network, rpc);
const signer = getSigner(provider, network, privateKey);
@ -244,7 +242,7 @@ export const initTokenBridge = async (
rpc?: string,
privateKey?: string
): Promise<SuiTransactionBlockResponse> => {
rpc = rpc ?? NETWORKS[network].sui.rpc;
rpc = rpc ?? NETWORKS[network].Sui.rpc;
const provider = getProvider(network, rpc);
const signer = getSigner(provider, network, privateKey);
const owner = await signer.getAddress();
@ -307,7 +305,7 @@ export const initWormhole = async (
rpc?: string,
privateKey?: string
): Promise<SuiTransactionBlockResponse> => {
rpc = rpc ?? NETWORKS[network].sui.rpc;
rpc = rpc ?? NETWORKS[network].Sui.rpc;
const provider = getProvider(network, rpc);
const signer = getSigner(provider, network, privateKey);
const owner = await signer.getAddress();

View File

@ -13,8 +13,8 @@ import {
setMaxGasBudgetDevnet,
} from "../../chains/sui";
import { NETWORK_OPTIONS, NETWORKS, RPC_OPTIONS } from "../../consts";
import { assertNetwork } from "../../utils";
import { YargsAddCommandsFn } from "../Yargs";
import { getNetwork } from "../../utils";
export const addPublishMessageCommands: YargsAddCommandsFn = (
y: typeof yargs
@ -57,14 +57,13 @@ export const addPublishMessageCommands: YargsAddCommandsFn = (
})
.option("rpc", RPC_OPTIONS),
async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
const network = getNetwork(argv.network);
const packageId = argv["package-id"];
const stateObjectId = argv.state;
const wormholeStateObjectId = argv["wormhole-state"];
const message = argv.message;
const privateKey = argv["private-key"];
const rpc = argv.rpc ?? NETWORKS[network].sui.rpc;
const rpc = argv.rpc ?? NETWORKS[network].Sui.rpc;
const provider = getProvider(network, rpc);
const signer = getSigner(provider, network, privateKey);

View File

@ -1,8 +1,3 @@
import {
ChainId,
ChainName,
coalesceChainName,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import { parseTokenBridgeRegisterChainVaa } from "@certusone/wormhole-sdk/lib/esm/vaa/tokenBridge";
import {
JsonRpcProvider,
@ -36,6 +31,7 @@ import {
import { YargsAddCommandsFn } from "../Yargs";
import { deploy } from "./deploy";
import { initExampleApp, initTokenBridge, initWormhole } from "./init";
import { Chain, chainIdToChain, toChainId } from "@wormhole-foundation/sdk";
export const addSetupCommands: YargsAddCommandsFn = (y: typeof yargs) =>
y.command(
@ -51,9 +47,9 @@ export const addSetupCommands: YargsAddCommandsFn = (y: typeof yargs) =>
})
.option("rpc", RPC_OPTIONS),
async (argv) => {
const network = "DEVNET";
const network = "Devnet";
const privateKey = argv["private-key"];
const rpc = argv.rpc ?? NETWORKS[network].sui.rpc;
const rpc = argv.rpc ?? NETWORKS[network].Sui.rpc;
// Deploy core bridge
console.log("[1/4] Deploying core bridge...");
@ -200,15 +196,15 @@ export const addSetupCommands: YargsAddCommandsFn = (y: typeof yargs) =>
dotenv.config({ path: envPath });
const tx = new TransactionBlock();
setMaxGasBudgetDevnet("DEVNET", tx);
const registrations: { chain: ChainName; module: string }[] = [];
setMaxGasBudgetDevnet("Devnet", tx);
const registrations: { chain: Chain; module: string }[] = [];
for (const key in process.env) {
if (/^REGISTER_(.+)_TOKEN_BRIDGE_VAA$/.test(key)) {
// Get VAA info
const vaa = Buffer.from(String(process.env[key]), "hex");
const { foreignChain, module } =
parseTokenBridgeRegisterChainVaa(vaa);
const chain = coalesceChainName(foreignChain as ChainId);
const chain = chainIdToChain(toChainId(foreignChain));
registrations.push({ chain, module });
// Register

View File

@ -2,8 +2,8 @@ import { PaginatedObjectsResponse } from "@mysten/sui.js";
import yargs from "yargs";
import { getPackageId, getProvider } from "../../chains/sui";
import { NETWORKS, NETWORK_OPTIONS, RPC_OPTIONS } from "../../consts";
import { assertNetwork } from "../../utils";
import { YargsAddCommandsFn } from "../Yargs";
import { getNetwork } from "../../utils";
export const addUtilsCommands: YargsAddCommandsFn = (y: typeof yargs) =>
y
@ -20,9 +20,8 @@ export const addUtilsCommands: YargsAddCommandsFn = (y: typeof yargs) =>
.option("network", NETWORK_OPTIONS)
.option("rpc", RPC_OPTIONS),
async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
const rpc = argv.rpc ?? NETWORKS[network].sui.rpc;
const network = getNetwork(argv.network);
const rpc = argv.rpc ?? NETWORKS[network].Sui.rpc;
const owner = argv.owner;
const provider = getProvider(network, rpc);
@ -61,9 +60,8 @@ export const addUtilsCommands: YargsAddCommandsFn = (y: typeof yargs) =>
.option("network", NETWORK_OPTIONS)
.option("rpc", RPC_OPTIONS),
async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
const rpc = argv.rpc ?? NETWORKS[network].sui.rpc;
const network = getNetwork(argv.network);
const rpc = argv.rpc ?? NETWORKS[network].Sui.rpc;
const provider = getProvider(network, rpc);
console.log(await getPackageId(provider, argv["state-object-id"]));
}
@ -89,9 +87,8 @@ export const addUtilsCommands: YargsAddCommandsFn = (y: typeof yargs) =>
} as const)
.option("rpc", RPC_OPTIONS),
async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
const rpc = argv.rpc ?? NETWORKS[network].sui.rpc;
const network = getNetwork(argv.network);
const rpc = argv.rpc ?? NETWORKS[network].Sui.rpc;
const provider = getProvider(network, rpc);
console.log(
JSON.stringify(

View File

@ -1,13 +1,6 @@
import {
isCosmWasmChain,
isEVMChain,
isTerraChain,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import yargs from "yargs";
import { impossible } from "../vaa";
import { transferEVM } from "../evm";
import { CHAIN_NAME_CHOICES, NETWORK_OPTIONS, NETWORKS } from "../consts";
import { assertNetwork } from "../utils";
import { transferTerra } from "../terra";
import { transferInjective } from "../injective";
import { transferXpla } from "../xpla";
@ -16,6 +9,13 @@ import { transferAlgorand } from "../algorand";
import { transferNear } from "../near";
import { transferSui } from "../chains/sui/transfer";
import { transferAptos } from "../aptos";
import {
Chain,
PlatformToChains,
chainToPlatform,
toChain,
} from "@wormhole-foundation/sdk-base";
import { chainToChain, getNetwork } from "../utils";
export const command = "transfer";
export const desc = "Transfer a token";
@ -23,12 +23,12 @@ export const builder = (y: typeof yargs) =>
y
.option("src-chain", {
describe: "source chain",
choices: CHAIN_NAME_CHOICES,
type: "string",
demandOption: true,
})
.option("dst-chain", {
describe: "destination chain",
choices: CHAIN_NAME_CHOICES,
type: "string",
demandOption: true,
})
.option("dst-addr", {
@ -58,16 +58,10 @@ export const builder = (y: typeof yargs) =>
export const handler = async (
argv: Awaited<ReturnType<typeof builder>["argv"]>
) => {
const srcChain = argv["src-chain"];
const dstChain = argv["dst-chain"];
if (srcChain === "unset") {
throw new Error("source chain is unset");
}
if (dstChain === "unset") {
throw new Error("destination chain is unset");
}
const srcChain: Chain = chainToChain(argv["src-chain"]);
const dstChain: Chain = chainToChain(argv["dst-chain"]);
// TODO: support transfers to sei
if (dstChain === "sei") {
if (dstChain === "Sei") {
throw new Error("transfer to sei currently unsupported");
}
if (srcChain === dstChain) {
@ -78,19 +72,18 @@ export const handler = async (
throw new Error("amount must be greater than 0");
}
const tokenAddr = argv["token-addr"];
if (tokenAddr === "native" && isCosmWasmChain(srcChain)) {
if (tokenAddr === "native" && chainToPlatform(srcChain) === "Cosmwasm") {
throw new Error(`token-addr must be specified for ${srcChain}`);
}
const dstAddr = argv["dst-addr"];
const network = argv.network.toUpperCase();
assertNetwork(network);
const rpc = argv.rpc ?? NETWORKS[network][srcChain].rpc;
const network = getNetwork(argv.network);
const rpc = argv.rpc ?? NETWORKS[network][toChain(srcChain)].rpc;
if (!rpc) {
throw new Error(`No ${network} rpc defined for ${srcChain}`);
}
if (isEVMChain(srcChain)) {
if (chainToPlatform(srcChain) === "Evm") {
await transferEVM(
srcChain,
srcChain as PlatformToChains<"Evm">,
dstChain,
dstAddr,
tokenAddr,
@ -98,7 +91,7 @@ export const handler = async (
network,
rpc
);
} else if (isTerraChain(srcChain)) {
} else if (srcChain === "Terra" || srcChain === "Terra2") {
await transferTerra(
srcChain,
dstChain,
@ -108,7 +101,7 @@ export const handler = async (
network,
rpc
);
} else if (srcChain === "solana" || srcChain === "pythnet") {
} else if (srcChain === "Solana" || srcChain === "Pythnet") {
await transferSolana(
srcChain,
dstChain,
@ -118,49 +111,37 @@ export const handler = async (
network,
rpc
);
} else if (srcChain === "algorand") {
} else if (srcChain === "Algorand") {
await transferAlgorand(dstChain, dstAddr, tokenAddr, amount, network, rpc);
} else if (srcChain === "near") {
} else if (srcChain === "Near") {
await transferNear(dstChain, dstAddr, tokenAddr, amount, network, rpc);
} else if (srcChain === "injective") {
} else if (srcChain === "Injective") {
await transferInjective(dstChain, dstAddr, tokenAddr, amount, network, rpc);
} else if (srcChain === "xpla") {
} else if (srcChain === "Xpla") {
await transferXpla(dstChain, dstAddr, tokenAddr, amount, network, rpc);
} else if (srcChain === "sei") {
} else if (srcChain === "Sei") {
throw new Error("sei is not supported yet");
} else if (srcChain === "osmosis") {
} else if (srcChain === "Osmosis") {
throw Error("OSMOSIS is not supported yet");
} else if (srcChain === "sui") {
} else if (srcChain === "Sui") {
await transferSui(dstChain, dstAddr, tokenAddr, amount, network, rpc);
} else if (srcChain === "aptos") {
} else if (srcChain === "Aptos") {
await transferAptos(dstChain, dstAddr, tokenAddr, amount, network, rpc);
} else if (srcChain === "wormchain") {
} else if (srcChain === "Wormchain") {
throw Error("Wormchain is not supported yet");
} else if (srcChain === "btc") {
} else if (srcChain === "Btc") {
throw Error("btc is not supported yet");
} else if (srcChain === "cosmoshub") {
} else if (srcChain === "Cosmoshub") {
throw Error("cosmoshub is not supported yet");
} else if (srcChain === "evmos") {
} else if (srcChain === "Evmos") {
throw Error("evmos is not supported yet");
} else if (srcChain === "kujira") {
} else if (srcChain === "Kujira") {
throw Error("kujira is not supported yet");
} else if (srcChain === "neutron") {
} else if (srcChain === "Neutron") {
throw Error("neutron is not supported yet");
} else if (srcChain === "celestia") {
} else if (srcChain === "Celestia") {
throw Error("celestia is not supported yet");
} else if (srcChain === "stargaze") {
throw Error("stargaze is not supported yet");
} else if (srcChain === "seda") {
throw Error("seda is not supported yet");
} else if (srcChain === "dymension") {
throw Error("dymension is not supported yet");
} else if (srcChain === "provenance") {
throw Error("provenance is not supported yet");
} else if (srcChain === "rootstock") {
throw Error("rootstock is not supported yet");
} else {
// If you get a type error here, hover over `chain`'s type and it tells you
// which cases are not handled
impossible(srcChain);
throw new Error(`${srcChain} is not supported yet`);
}
};

View File

@ -1,11 +1,11 @@
// The verify-vaa command invokes the parseAndVerifyVM method on the core contract on Ethereum to verify the specified VAA.
import { Implementation__factory } from "@certusone/wormhole-sdk/lib/esm/ethers-contracts";
import { CONTRACTS } from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import { ethers } from "ethers";
import yargs from "yargs";
import { NETWORKS, NETWORK_OPTIONS } from "../consts";
import { assertNetwork } from "../utils";
import { getNetwork } from "../utils";
import { contracts } from "@wormhole-foundation/sdk";
export const command = "verify-vaa";
export const desc = "Verifies a VAA by querying the core contract on Ethereum";
@ -21,17 +21,16 @@ export const builder = (y: typeof yargs) =>
export const handler = async (
argv: Awaited<ReturnType<typeof builder>["argv"]>
) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
const network = getNetwork(argv.network);
const buf = Buffer.from(String(argv.vaa), "hex");
const contract_address = CONTRACTS[network].ethereum.core;
const contract_address = contracts.coreBridge(network, "Ethereum");
if (!contract_address) {
throw Error(`Unknown core contract on ${network} for ethereum`);
}
const provider = new ethers.providers.JsonRpcProvider(
NETWORKS[network].ethereum.rpc
NETWORKS[network].Ethereum.rpc
);
const contract = Implementation__factory.connect(contract_address, provider);
const result = await contract.parseAndVerifyVM(buf);

View File

@ -1,60 +1,6 @@
import {
CHAIN_ID_SOLANA,
CONTRACTS as SDK_CONTRACTS,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import { chainToChainId } from "@wormhole-foundation/sdk-base";
const OVERRIDES = {
MAINNET: {
sui: {
core: "0xaeab97f96cf9877fee2883315d459552b2b921edc16d7ceac6eab944dd88919c",
token_bridge:
"0xc57508ee0d4595e5a8728974a4a93a787d38f339757230d441e895422c07aba9",
},
aptos: {
token_bridge:
"0x576410486a2da45eee6c949c995670112ddf2fbeedab20350d506328eefc9d4f",
core: "0x5bc11445584a763c1fa7ed39081f1b920954da14e04b32440cba863d03e19625",
nft_bridge:
"0x1bdffae984043833ed7fe223f7af7a3f8902d04129b14f801823e64827da7130",
},
},
TESTNET: {
sui: {
core: "0x31358d198147da50db32eda2562951d53973a0c0ad5ed738e9b17d88b213d790",
token_bridge:
"0x6fb10cdb7aa299e9a4308752dadecb049ff55a892de92992a1edbd7912b3d6da",
},
aptos: {
token_bridge:
"0x576410486a2da45eee6c949c995670112ddf2fbeedab20350d506328eefc9d4f",
core: "0x5bc11445584a763c1fa7ed39081f1b920954da14e04b32440cba863d03e19625",
nft_bridge: undefined,
},
},
DEVNET: {
sui: {
core: "0x12253210c90f89e7a8525e6c52d41309ff5bfb31f43f561b5fe6f50cd72f9668", // wormhole module State object ID
token_bridge:
"0x830ed228c6f1bcb40003bb49af3277df2cbf933d63a6bcdcb0ba4580a1a7654e", // token_bridge module State object ID
},
aptos: {
token_bridge:
"0x84a5f374d29fc77e370014dce4fd6a55b58ad608de8074b0be5571701724da31",
core: "0xde0036a9600559e295d5f6802ef6f3f802f510366e0c23912b0655d972166017",
nft_bridge:
"0x46da3d4c569388af61f951bdd1153f4c875f90c2991f6b2d0a38e2161a40852c",
},
},
};
// TODO(aki): move this to SDK at some point
export const CONTRACTS = {
MAINNET: { ...SDK_CONTRACTS.MAINNET, ...OVERRIDES.MAINNET },
TESTNET: { ...SDK_CONTRACTS.TESTNET, ...OVERRIDES.TESTNET },
DEVNET: { ...SDK_CONTRACTS.DEVNET, ...OVERRIDES.DEVNET },
};
export const GOVERNANCE_CHAIN = CHAIN_ID_SOLANA;
export const GOVERNANCE_CHAIN = chainToChainId("Solana");
export const GOVERNANCE_EMITTER =
"0000000000000000000000000000000000000000000000000000000000000004";
export const INITIAL_GUARDIAN_DEVNET =

View File

@ -1,4 +1,4 @@
import { ChainName } from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import { Chain } from "@wormhole-foundation/sdk-base";
import { config } from "dotenv";
import { homedir } from "os";
@ -12,785 +12,773 @@ export type Connection = {
};
export type ChainConnections = {
[chain in ChainName]: Connection;
[chain in Chain]: Connection;
};
const MAINNET = {
unset: {
rpc: undefined,
key: undefined,
},
solana: {
const Mainnet = {
Solana: {
rpc: "https://api.mainnet-beta.solana.com",
key: getEnvVar("SOLANA_KEY"),
},
terra: {
Terra: {
rpc: "https://lcd.terra.dev",
chain_id: "columbus-5",
key: getEnvVar("TERRA_MNEMONIC"),
},
ethereum: {
Ethereum: {
rpc: `https://rpc.ankr.com/eth`,
key: getEnvVar("ETH_KEY"),
chain_id: 1,
},
bsc: {
Bsc: {
rpc: "https://bsc-dataseed.binance.org/",
key: getEnvVar("ETH_KEY"),
chain_id: 56,
},
polygon: {
Polygon: {
rpc: "https://rpc.ankr.com/polygon",
key: getEnvVar("ETH_KEY"),
chain_id: 137,
},
avalanche: {
Avalanche: {
rpc: "https://rpc.ankr.com/avalanche",
key: getEnvVar("ETH_KEY"),
chain_id: 43114,
},
algorand: {
Algorand: {
rpc: "https://mainnet-api.algonode.cloud",
key: getEnvVar("ALGORAND_KEY"),
},
oasis: {
Oasis: {
rpc: "https://emerald.oasis.dev/",
key: getEnvVar("ETH_KEY"),
chain_id: 42262,
},
fantom: {
Fantom: {
rpc: "https://rpc.ftm.tools/",
key: getEnvVar("ETH_KEY"),
chain_id: 250,
},
aurora: {
Aurora: {
rpc: "https://mainnet.aurora.dev",
key: getEnvVar("ETH_KEY"),
chain_id: 1313161554,
},
karura: {
Karura: {
rpc: "https://eth-rpc-karura.aca-api.network/",
key: getEnvVar("ETH_KEY"),
chain_id: 686,
},
acala: {
Acala: {
rpc: "https://eth-rpc-acala.aca-api.network/",
key: getEnvVar("ETH_KEY"),
chain_id: 787,
},
klaytn: {
Klaytn: {
rpc: "https://public-node-api.klaytnapi.com/v1/cypress",
key: getEnvVar("ETH_KEY"),
chain_id: 8217,
},
celo: {
Celo: {
rpc: "https://forno.celo.org",
key: getEnvVar("ETH_KEY"),
chain_id: 42220,
},
near: {
Near: {
rpc: "https://rpc.mainnet.near.org",
key: getEnvVar("NEAR_KEY"),
networkId: "mainnet",
},
injective: {
Injective: {
rpc: "http://sentry0.injective.network:26657",
chain_id: "injective-1",
key: getEnvVar("INJECTIVE_KEY"),
},
osmosis: {
Osmosis: {
rpc: undefined,
key: undefined,
},
aptos: {
Aptos: {
rpc: "https://fullnode.mainnet.aptoslabs.com/v1",
key: getEnvVar("APTOS_KEY"),
},
sui: {
Sui: {
rpc: "https://fullnode.mainnet.sui.io:443",
key: getEnvVar("SUI_KEY"),
},
pythnet: {
Pythnet: {
rpc: "http://api.pythnet.pyth.network:8899/",
key: getEnvVar("SOLANA_KEY"),
},
xpla: {
Xpla: {
rpc: "https://dimension-lcd.xpla.dev",
chain_id: "dimension_37-1",
key: getEnvVar("XPLA_KEY"),
},
btc: {
Btc: {
rpc: undefined,
key: undefined,
},
wormchain: {
Wormchain: {
rpc: undefined,
key: undefined,
},
moonbeam: {
Moonbeam: {
rpc: "https://rpc.api.moonbeam.network",
key: getEnvVar("ETH_KEY"),
chain_id: 1284,
},
neon: {
Neon: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
terra2: {
Terra2: {
rpc: "https://phoenix-lcd.terra.dev",
chain_id: "phoenix-1",
key: getEnvVar("TERRA_MNEMONIC"),
},
arbitrum: {
Arbitrum: {
rpc: "https://arb1.arbitrum.io/rpc",
key: getEnvVar("ETH_KEY"),
chain_id: 42161,
},
optimism: {
Optimism: {
rpc: "https://mainnet.optimism.io",
key: getEnvVar("ETH_KEY"),
chain_id: 10,
},
gnosis: {
Gnosis: {
rpc: "https://rpc.gnosischain.com/",
key: getEnvVar("ETH_KEY"),
chain_id: 100,
},
base: {
Base: {
rpc: "https://mainnet.base.org",
key: getEnvVar("ETH_KEY"),
chain_id: 8453,
},
sei: {
Sei: {
rpc: "https://sei-rpc.polkachu.com/",
key: getEnvVar("SEI_KEY"),
},
rootstock: {
Rootstock: {
rpc: "https://public-node.rsk.co",
key: getEnvVar("ETH_KEY"),
chain_id: 30,
},
scroll: {
Scroll: {
rpc: "https://rpc.ankr.com/scroll",
key: getEnvVar("ETH_KEY"),
chain_id: 534352,
},
mantle: {
Mantle: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
blast: {
Blast: {
rpc: "https://rpc.ankr.com/blast",
key: getEnvVar("ETH_KEY"),
chain_id: 81457,
},
xlayer: {
Xlayer: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
linea: {
Linea: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
berachain: {
Berachain: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
seievm: {
Seievm: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
sepolia: {
Sepolia: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
holesky: {
Holesky: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
cosmoshub: {
Cosmoshub: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
evmos: {
Evmos: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
kujira: {
Kujira: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
neutron: {
Neutron: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
celestia: {
Celestia: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
arbitrum_sepolia: {
ArbitrumSepolia: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
base_sepolia: {
BaseSepolia: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
optimism_sepolia: {
OptimismSepolia: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
polygon_sepolia: {
PolygonSepolia: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
stargaze: {
Stargaze: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
seda: {
Seda: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
dymension: {
Dymension: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
provenance: {
Provenance: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
};
const TESTNET = {
unset: {
rpc: undefined,
key: undefined,
},
solana: {
const Testnet = {
Solana: {
rpc: "https://api.devnet.solana.com",
key: getEnvVar("SOLANA_KEY_TESTNET"),
},
terra: {
Terra: {
rpc: "https://bombay-lcd.terra.dev",
chain_id: "bombay-12",
key: getEnvVar("TERRA_MNEMONIC_TESTNET"),
},
ethereum: {
Ethereum: {
rpc: `https://rpc.ankr.com/eth_goerli`,
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 5,
},
bsc: {
Bsc: {
rpc: "https://data-seed-prebsc-1-s1.binance.org:8545",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 97,
},
polygon: {
Polygon: {
rpc: `https://rpc.ankr.com/polygon_mumbai`,
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 80001,
},
avalanche: {
Avalanche: {
rpc: "https://rpc.ankr.com/avalanche_fuji",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 43113,
},
oasis: {
Oasis: {
rpc: "https://testnet.emerald.oasis.dev",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 42261,
},
algorand: {
Algorand: {
rpc: "https://testnet-api.algonode.cloud",
key: getEnvVar("ALGORAND_KEY_TESTNET"),
},
fantom: {
Fantom: {
rpc: "https://rpc.testnet.fantom.network",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 4002,
},
aurora: {
Aurora: {
rpc: "https://testnet.aurora.dev",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 1313161555,
},
karura: {
Karura: {
rpc: "https://eth-rpc-karura-testnet.aca-staging.network",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 596,
},
acala: {
Acala: {
rpc: "https://eth-rpc-acala-testnet.aca-staging.network",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 595,
},
klaytn: {
Klaytn: {
rpc: "https://api.baobab.klaytn.net:8651",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 1001,
},
celo: {
Celo: {
rpc: "https://alfajores-forno.celo-testnet.org",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 44787,
},
near: {
Near: {
rpc: "https://rpc.testnet.near.org",
key: getEnvVar("NEAR_KEY_TESTNET"),
networkId: "testnet",
},
injective: {
Injective: {
rpc: "https://k8s.testnet.tm.injective.network:443",
chain_id: "injective-888",
key: getEnvVar("INJECTIVE_KEY_TESTNET"),
},
osmosis: {
Osmosis: {
rpc: undefined,
chain_id: "osmo-test-4",
key: getEnvVar("OSMOSIS_KEY_TESTNET"),
},
aptos: {
Aptos: {
rpc: "https://fullnode.testnet.aptoslabs.com/v1",
key: getEnvVar("APTOS_TESTNET"),
},
sui: {
Sui: {
rpc: "https://fullnode.testnet.sui.io:443",
key: getEnvVar("SUI_KEY_TESTNET"),
},
pythnet: {
Pythnet: {
rpc: "https://api.pythtest.pyth.network/",
key: getEnvVar("SOLANA_KEY_TESTNET"),
},
xpla: {
Xpla: {
rpc: "https://cube-lcd.xpla.dev:443",
chain_id: "cube_47-5",
key: getEnvVar("XPLA_KEY_TESTNET"),
},
sei: {
Sei: {
rpc: "https://rpc.atlantic-2.seinetwork.io",
key: getEnvVar("SEI_KEY_TESTNET"),
},
scroll: {
Scroll: {
rpc: "https://rpc.ankr.com/scroll_sepolia_testnet",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 534353,
},
mantle: {
Mantle: {
rpc: "https://mantle-sepolia.drpc.org",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 5003,
},
blast: {
Blast: {
rpc: "https://blast-sepolia.drpc.org",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 168587773,
},
xlayer: {
Xlayer: {
rpc: "https://testrpc.xlayer.tech/",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 195,
},
linea: {
Linea: {
rpc: "https://rpc.sepolia.linea.build",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 59141,
},
berachain: {
Berachain: {
rpc: "https://artio.rpc.berachain.com/",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 80085,
},
seievm: {
Seievm: {
rpc: "https://evm-rpc-arctic-1.sei-apis.com/",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 713715,
},
sepolia: {
Sepolia: {
rpc: "https://rpc.ankr.com/eth_sepolia",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 11155111,
},
holesky: {
Holesky: {
rpc: "https://rpc.ankr.com/eth_holesky",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 17000,
},
btc: {
Btc: {
rpc: undefined,
key: undefined,
},
wormchain: {
Wormchain: {
rpc: undefined,
key: undefined,
},
moonbeam: {
Moonbeam: {
rpc: "https://rpc.api.moonbase.moonbeam.network",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 1287,
},
neon: {
Neon: {
rpc: "https://proxy.devnet.neonlabs.org/solana",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: undefined,
},
terra2: {
Terra2: {
rpc: "https://pisco-lcd.terra.dev",
chain_id: "pisco-1",
key: getEnvVar("TERRA_MNEMONIC_TESTNET"),
},
arbitrum: {
Arbitrum: {
rpc: "https://goerli-rollup.arbitrum.io/rpc",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 421613,
},
optimism: {
Optimism: {
rpc: "https://goerli.optimism.io",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 420,
},
gnosis: {
Gnosis: {
rpc: "https://sokol.poa.network/",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 77,
},
base: {
Base: {
rpc: "https://goerli.base.org",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 84531,
},
rootstock: {
Rootstock: {
rpc: "https://public-node.testnet.rsk.co",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 31,
},
cosmoshub: {
Cosmoshub: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
evmos: {
Evmos: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
kujira: {
Kujira: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
neutron: {
Neutron: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
celestia: {
Celestia: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
arbitrum_sepolia: {
ArbitrumSepolia: {
rpc: "https://arbitrum-sepolia.publicnode.com",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 421614,
},
base_sepolia: {
BaseSepolia: {
rpc: "https://sepolia.base.org",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 84532,
},
optimism_sepolia: {
OptimismSepolia: {
rpc: "https://rpc.ankr.com/optimism_sepolia",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 11155420,
},
polygon_sepolia: {
PolygonSepolia: {
rpc: "https://rpc-amoy.polygon.technology/",
key: getEnvVar("ETH_KEY_TESTNET"),
chain_id: 80002,
},
stargaze: {
Stargaze: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
seda: {
Seda: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
dymension: {
Dymension: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
provenance: {
Provenance: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
};
const DEVNET = {
unset: {
rpc: undefined,
key: undefined,
},
solana: {
const Devnet = {
Solana: {
rpc: "http://localhost:8899",
key: "J2D4pwDred8P9ioyPEZVLPht885AeYpifsFGUyuzVmiKQosAvmZP4EegaKFrSprBC5vVP1xTvu61vYDWsxBNsYx",
},
terra: {
Terra: {
rpc: "http://localhost:1317",
chain_id: "columbus-5",
key: "notice oak worry limit wrap speak medal online prefer cluster roof addict wrist behave treat actual wasp year salad speed social layer crew genius",
},
ethereum: {
Ethereum: {
rpc: "http://localhost:8545",
key: "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d",
},
bsc: {
Bsc: {
rpc: "http://localhost:8546",
key: "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d",
},
polygon: {
Polygon: {
rpc: undefined,
key: "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d",
},
avalanche: {
Avalanche: {
rpc: undefined,
key: "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d",
},
oasis: {
Oasis: {
rpc: undefined,
key: "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d",
},
algorand: {
Algorand: {
rpc: "http://localhost",
key: getEnvVar("ALGORAND_KEY_DEVNET"),
},
fantom: {
Fantom: {
rpc: undefined,
key: "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d",
},
aurora: {
Aurora: {
rpc: undefined,
key: "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d",
},
karura: {
Karura: {
rpc: undefined,
key: "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d",
},
acala: {
Acala: {
rpc: undefined,
key: "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d",
},
klaytn: {
Klaytn: {
rpc: undefined,
key: "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d",
},
celo: {
Celo: {
rpc: undefined,
key: "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d",
},
near: {
Near: {
rpc: undefined,
key: undefined,
networkId: "sandbox",
},
injective: {
Injective: {
rpc: undefined,
key: undefined,
},
osmosis: {
Osmosis: {
rpc: undefined,
key: undefined,
},
pythnet: {
Pythnet: {
rpc: undefined,
key: undefined,
},
btc: {
Btc: {
rpc: undefined,
key: undefined,
},
xpla: {
Xpla: {
rpc: undefined,
chain_id: undefined,
key: undefined,
},
sei: {
Sei: {
rpc: undefined,
key: undefined,
},
scroll: {
Scroll: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
mantle: {
Mantle: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
blast: {
Blast: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
xlayer: {
Xlayer: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
linea: {
Linea: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
berachain: {
Berachain: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
seievm: {
Seievm: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
sepolia: {
Sepolia: {
rpc: undefined,
key: undefined,
},
holesky: {
Holesky: {
rpc: undefined,
key: undefined,
},
wormchain: {
Wormchain: {
rpc: "http://localhost:1319",
chain_id: "wormchain",
key: undefined,
},
aptos: {
Aptos: {
rpc: "http://0.0.0.0:8080",
key: "537c1f91e56891445b491068f519b705f8c0f1a1e66111816dd5d4aa85b8113d",
},
sui: {
Sui: {
rpc: "http://0.0.0.0:9000",
key: "AGA20wtGcwbcNAG4nwapbQ5wIuXwkYQEWFUoSVAxctHb",
},
moonbeam: {
Moonbeam: {
rpc: undefined,
key: "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d",
},
neon: {
Neon: {
rpc: undefined,
key: "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d",
},
terra2: {
Terra2: {
rpc: "http://localhost:1318",
chain_id: "phoenix-1",
key: "notice oak worry limit wrap speak medal online prefer cluster roof addict wrist behave treat actual wasp year salad speed social layer crew genius",
},
arbitrum: {
Arbitrum: {
rpc: undefined,
key: undefined,
},
optimism: {
Optimism: {
rpc: undefined,
key: undefined,
},
gnosis: {
Gnosis: {
rpc: undefined,
key: undefined,
},
base: {
Base: {
rpc: undefined,
key: undefined,
},
rootstock: {
Rootstock: {
rpc: undefined,
key: undefined,
},
cosmoshub: {
Cosmoshub: {
rpc: undefined,
key: undefined,
},
evmos: {
Evmos: {
rpc: undefined,
key: undefined,
},
kujira: {
Kujira: {
rpc: undefined,
key: undefined,
},
neutron: {
Neutron: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
celestia: {
Celestia: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
arbitrum_sepolia: {
ArbitrumSepolia: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
base_sepolia: {
BaseSepolia: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
optimism_sepolia: {
OptimismSepolia: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
polygon_sepolia: {
PolygonSepolia: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
stargaze: {
Stargaze: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
seda: {
Seda: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
dymension: {
Dymension: {
rpc: undefined,
key: undefined,
chain_id: undefined,
},
provenance: {
Provenance: {
rpc: undefined,
key: undefined,
chain_id: undefined,
@ -811,18 +799,18 @@ const DEVNET = {
*
* (Do not delete this declaration!)
*/
const isTestnetConnections: ChainConnections = TESTNET;
const isTestnetConnections: ChainConnections = Testnet;
/**
*
* See [[isTestnetContracts]]
*/
const isMainnetConnections: ChainConnections = MAINNET;
const isMainnetConnections: ChainConnections = Mainnet;
/**
*
* See [[isTestnetContracts]]
*/
const isDevnetConnections: ChainConnections = DEVNET;
const isDevnetConnections: ChainConnections = Devnet;
export const NETWORKS = { MAINNET, TESTNET, DEVNET };
export const NETWORKS = { Mainnet, Testnet, Devnet };

View File

@ -1,8 +1,9 @@
import {
CHAINS,
Chain,
ChainId,
ChainName,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
chainIds,
chains,
} from "@wormhole-foundation/sdk-base";
export const DEBUG_OPTIONS = {
alias: "d",
@ -38,11 +39,9 @@ export const RPC_OPTIONS = {
demandOption: false,
} as const;
export const CHAIN_ID_OR_NAME_CHOICES = [
...Object.keys(CHAINS),
...Object.values(CHAINS),
] as (ChainName | ChainId)[];
export const CHAIN_ID_OR_NAME_CHOICES = [...chains, ...chainIds] as (
| Chain
| ChainId
)[];
export const CHAIN_NAME_CHOICES = Object.keys(CHAINS).filter(
(c) => c !== "unset"
) as ChainName[];
export const CHAIN_NAME_CHOICES = [...chains];

View File

@ -1,9 +1,3 @@
import {
ChainId,
ChainName,
isCosmWasmChain,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import {
getEmitterAddressAlgorand,
getEmitterAddressEth,
@ -11,21 +5,25 @@ import {
getEmitterAddressSolana,
getEmitterAddressTerra,
} from "@certusone/wormhole-sdk/lib/esm/bridge/getEmitterAddress";
import {
Chain,
ChainId,
chainToPlatform,
toChain,
} from "@wormhole-foundation/sdk-base";
export async function getEmitterAddress(
chain: ChainId | ChainName,
addr: string
) {
if (chain === "solana" || chain === "pythnet") {
export async function getEmitterAddress(chain: ChainId | Chain, addr: string) {
const localChain = toChain(chain);
if (localChain === "Solana" || localChain === "Pythnet") {
// TODO: Create an isSolanaChain()
addr = getEmitterAddressSolana(addr);
} else if (isCosmWasmChain(chain)) {
} else if (chainToPlatform(localChain) === "Cosmwasm") {
addr = await getEmitterAddressTerra(addr);
} else if (chain === "algorand") {
} else if (localChain === "Algorand") {
addr = getEmitterAddressAlgorand(BigInt(addr));
} else if (chain === "near") {
} else if (localChain === "Near") {
addr = getEmitterAddressNear(addr);
} else if (chain === "aptos") {
} else if (localChain === "Aptos") {
// TODO: There should be something in the SDK to do this.
if (
addr ===
@ -42,7 +40,7 @@ export async function getEmitterAddress(
} else {
throw Error(`Unsupported Aptos address: ${addr}`);
}
} else if (chain === "sui") {
} else if (localChain === "Sui") {
// TODO: There should be something in the SDK to do this.
if (
addr ===

View File

@ -5,20 +5,10 @@ import {
NFTBridgeImplementation__factory,
} from "@certusone/wormhole-sdk/lib/esm/ethers-contracts";
import { WormholeRelayer__factory } from "@certusone/wormhole-sdk/lib/esm/ethers-relayer-contracts";
import { getWormholeRelayerAddress } from "@certusone/wormhole-sdk/lib/esm/relayer";
import {
CHAINS,
CONTRACTS,
ChainName,
Contracts,
EVMChainName,
toChainId,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import axios from "axios";
import { ethers } from "ethers";
import { solidityKeccak256 } from "ethers/lib/utils";
import { NETWORKS } from "./consts";
import { Network } from "./utils";
import { Encoding, Payload, encode, impossible, typeWidth } from "./vaa";
import {
approveEth,
@ -26,14 +16,22 @@ import {
transferFromEth,
transferFromEthNative,
} from "@certusone/wormhole-sdk/lib/esm/token_bridge/transfer";
import { tryNativeToUint8Array } from "@certusone/wormhole-sdk/lib/esm/utils";
import {
Chain,
Network,
PlatformToChains,
chainToChainId,
chains,
contracts,
} from "@wormhole-foundation/sdk-base";
import { tryNativeToUint8Array } from "./sdk/array";
const _IMPLEMENTATION_SLOT =
"0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
export async function query_contract_evm(
network: Network,
chain: EVMChainName,
chain: PlatformToChains<"Evm">,
module: "Core" | "NFTBridge" | "TokenBridge",
contract_address: string | undefined,
_rpc: string | undefined
@ -44,12 +42,13 @@ export async function query_contract_evm(
throw Error(`No ${network} rpc defined for ${chain} (see networks.ts)`);
}
const contracts: Contracts = CONTRACTS[network][chain];
const provider = new ethers.providers.JsonRpcProvider(rpc);
const result: any = {};
switch (module) {
case "Core":
contract_address = contract_address ? contract_address : contracts.core;
contract_address = contract_address
? contract_address
: contracts.coreBridge.get(network, chain);
if (!contract_address) {
throw Error(`Unknown core contract on ${network} for ${chain}`);
}
@ -105,7 +104,7 @@ export async function query_contract_evm(
case "TokenBridge":
contract_address = contract_address
? contract_address
: contracts.token_bridge;
: contracts.tokenBridge.get(network, chain);
if (contract_address === undefined) {
throw Error(`Unknown token bridge contract on ${network} for ${chain}`);
}
@ -115,11 +114,11 @@ export async function query_contract_evm(
);
result.address = contract_address;
const registrationsPromise = Promise.all(
Object.entries(CHAINS)
.filter(([c_name, _]) => c_name !== chain && c_name !== "unset")
.map(async ([c_name, c_id]) => [
chains
.filter((c_name) => c_name !== chain)
.map(async (c_name) => [
c_name,
await tb.bridgeContracts(c_id),
await tb.bridgeContracts(chainToChainId(c_name)),
])
);
const [
@ -167,7 +166,7 @@ export async function query_contract_evm(
case "NFTBridge":
contract_address = contract_address
? contract_address
: contracts.nft_bridge;
: contracts.nftBridge.get(network, chain);
if (contract_address === undefined) {
throw Error(`Unknown nft bridge contract on ${network} for ${chain}`);
}
@ -177,11 +176,11 @@ export async function query_contract_evm(
);
result.address = contract_address;
const registrationsPromiseNb = Promise.all(
Object.entries(CHAINS)
.filter(([c_name, _]) => c_name !== chain && c_name !== "unset")
.map(async ([c_name, c_id]) => [
chains
.filter(([c_name, _]) => c_name !== chain)
.map(async (c_name) => [
c_name,
await nb.bridgeContracts(c_id),
await nb.bridgeContracts(chainToChainId(c_name)),
])
);
const [
@ -232,7 +231,7 @@ export async function query_contract_evm(
export async function getImplementation(
network: Network,
chain: EVMChainName,
chain: PlatformToChains<"Evm">,
module: "Core" | "NFTBridge" | "TokenBridge",
contract_address: string | undefined,
_rpc: string | undefined
@ -243,20 +242,21 @@ export async function getImplementation(
throw Error(`No ${network} rpc defined for ${chain} (see networks.ts)`);
}
const contracts: Contracts = CONTRACTS[network][chain];
switch (module) {
case "Core":
contract_address = contract_address ? contract_address : contracts.core;
contract_address = contract_address
? contract_address
: contracts.coreBridge.get(network, chain);
break;
case "TokenBridge":
contract_address = contract_address
? contract_address
: contracts.token_bridge;
: contracts.tokenBridge.get(network, chain);
break;
case "NFTBridge":
contract_address = contract_address
? contract_address
: contracts.nft_bridge;
: contracts.nftBridge.get(network, chain);
break;
default:
impossible(module);
@ -271,10 +271,14 @@ export async function getImplementation(
)[0];
}
async function getSigner(chain: EVMChainName, key: string, rpc: string) {
async function getSigner(
chain: PlatformToChains<"Evm">,
key: string,
rpc: string
) {
let provider: ethers.providers.JsonRpcProvider;
let signer: ethers.Wallet;
if (chain === "celo") {
if (chain === "Celo") {
provider = new celo.CeloProvider(rpc);
await provider.ready;
signer = new celo.CeloWallet(key, provider);
@ -286,15 +290,15 @@ async function getSigner(chain: EVMChainName, key: string, rpc: string) {
// NOTE: some of these might have only been tested on mainnet. If it fails in
// testnet (or devnet), they might require additional guards
let overrides: ethers.Overrides = {};
if (chain === "karura" || chain == "acala") {
if (chain === "Karura" || chain == "Acala") {
overrides = await getKaruraGasParams(rpc);
} else if (chain === "polygon") {
} else if (chain === "Polygon") {
const feeData = await provider.getFeeData();
overrides = {
maxFeePerGas: feeData.maxFeePerGas?.mul(50) || undefined,
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas?.mul(50) || undefined,
};
} else if (chain === "klaytn" || chain === "fantom") {
} else if (chain === "Klaytn" || chain === "Fantom") {
overrides = { gasPrice: (await signer.getGasPrice()).toString() };
}
return {
@ -308,7 +312,7 @@ export async function execute_evm(
payload: Payload,
vaa: Buffer,
network: Network,
chain: EVMChainName,
chain: PlatformToChains<"Evm">,
contract_address: string | undefined,
_rpc: string | undefined
) {
@ -323,12 +327,14 @@ export async function execute_evm(
}
const key: string = n.key;
const contracts: Contracts = CONTRACTS[network][chain];
// const contracts: Contracts = CONTRACTS[network][chain];
const { signer, overrides } = await getSigner(chain, key, rpc);
switch (payload.module) {
case "Core": {
contract_address = contract_address ? contract_address : contracts.core;
contract_address = contract_address
? contract_address
: contracts.coreBridge.get(network, chain);
if (contract_address === undefined) {
throw Error(`Unknown core contract on ${network} for ${chain}`);
}
@ -362,7 +368,7 @@ export async function execute_evm(
case "NFTBridge": {
contract_address = contract_address
? contract_address
: contracts.nft_bridge;
: contracts.nftBridge.get(network, chain);
if (contract_address === undefined) {
throw Error(`Unknown nft bridge contract on ${network} for ${chain}`);
}
@ -401,7 +407,7 @@ export async function execute_evm(
case "TokenBridge": {
contract_address = contract_address
? contract_address
: contracts.token_bridge;
: contracts.tokenBridge.get(network, chain);
if (contract_address === undefined) {
throw Error(`Unknown token bridge contract on ${network} for ${chain}`);
}
@ -451,7 +457,7 @@ export async function execute_evm(
case "WormholeRelayer":
contract_address = contract_address
? contract_address
: getWormholeRelayerAddress(chain, network);
: contracts.relayer.get(network, chain);
if (contract_address === undefined) {
throw Error(
`Unknown Wormhole Relayer contract on ${network} for ${chain}`
@ -493,8 +499,8 @@ export async function execute_evm(
}
export async function transferEVM(
srcChain: EVMChainName,
dstChain: ChainName,
srcChain: PlatformToChains<"Evm">,
dstChain: Chain,
dstAddress: string,
tokenAddress: string,
amount: string,
@ -505,7 +511,7 @@ export async function transferEVM(
if (!n.key) {
throw Error(`No ${network} key defined for ${srcChain} (see networks.ts)`);
}
const { token_bridge } = CONTRACTS[network][srcChain];
const token_bridge = contracts.tokenBridge.get(network, srcChain);
if (!token_bridge) {
throw Error(`Unknown token bridge contract on ${network} for ${srcChain}`);
}
@ -516,8 +522,8 @@ export async function transferEVM(
token_bridge,
signer,
amount,
toChainId(dstChain),
tryNativeToUint8Array(dstAddress, dstChain)
chainToChainId(dstChain),
tryNativeToUint8Array(dstAddress, chainToChainId(dstChain))
);
} else {
const allowance = await getAllowanceEth(token_bridge, tokenAddress, signer);
@ -529,8 +535,8 @@ export async function transferEVM(
signer,
tokenAddress,
amount,
dstChain,
tryNativeToUint8Array(dstAddress, dstChain),
chainToChainId(dstChain),
tryNativeToUint8Array(dstAddress, chainToChainId(dstChain)),
undefined,
overrides
);
@ -828,11 +834,10 @@ const isUnsupportedError = (e: any): e is { reason: string } =>
export async function queryRegistrationsEvm(
network: Network,
chain: EVMChainName,
chain: PlatformToChains<"Evm">,
module: "Core" | "NFTBridge" | "TokenBridge"
): Promise<Object> {
const n = NETWORKS[network][chain];
const contracts = CONTRACTS[network][chain];
let targetContract: string | undefined;
let contract: any;
@ -841,7 +846,7 @@ export async function queryRegistrationsEvm(
switch (module) {
case "TokenBridge":
targetContract = contracts.token_bridge;
targetContract = contracts.tokenBridge.get(network, chain);
if (targetContract === undefined) {
throw Error(`Unknown token bridge contract on ${network} for ${chain}`);
}
@ -851,7 +856,7 @@ export async function queryRegistrationsEvm(
);
break;
case "NFTBridge":
targetContract = contracts.nft_bridge;
targetContract = contracts.nftBridge.get(network, chain);
if (targetContract === undefined) {
throw Error(`Unknown NFT bridge contract on ${network} for ${chain}`);
}
@ -865,9 +870,12 @@ export async function queryRegistrationsEvm(
}
const registrations: string[][] = await Promise.all(
Object.entries(CHAINS)
.filter(([cname, _]) => cname !== chain && cname !== "unset")
.map(async ([cname, cid]) => [cname, await contract.bridgeContracts(cid)])
chains
.filter((cname) => cname !== chain)
.map(async (cname) => [
cname,
await contract.bridgeContracts(chainToChainId(cname)),
])
);
const results: { [key: string]: string } = {};

View File

@ -1,8 +1,3 @@
import {
CHAINS,
CONTRACTS,
ChainName,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import {
getNetworkInfo,
Network as InjectiveNetwork,
@ -19,28 +14,33 @@ import {
import { DEFAULT_STD_FEE, getStdFee } from "@injectivelabs/utils";
import { fromUint8Array } from "js-base64";
import { NETWORKS } from "./consts";
import { Network } from "./utils";
import { impossible, Payload } from "./vaa";
import { transferFromInjective } from "@certusone/wormhole-sdk/lib/esm/token_bridge/injective";
import { tryNativeToUint8Array } from "@certusone/wormhole-sdk/lib/esm/utils";
import {
Chain,
chainToChainId,
contracts,
Network,
} from "@wormhole-foundation/sdk-base";
import { chains } from "@wormhole-foundation/sdk";
import { tryNativeToUint8Array } from "./sdk/array";
export async function execute_injective(
payload: Payload,
vaa: Buffer,
network: Network
) {
if (network === "DEVNET") {
if (network === "Devnet") {
throw new Error("Injective is not supported in DEVNET");
}
const chain = "injective";
const chain = "Injective";
let { key } = NETWORKS[network][chain];
if (!key) {
throw Error(`No ${network} key defined for Injective`);
}
let contracts = CONTRACTS[network][chain];
const endPoint =
network === "MAINNET"
network === "Mainnet"
? InjectiveNetwork.MainnetK8s
: InjectiveNetwork.TestnetK8s;
@ -55,7 +55,7 @@ export async function execute_injective(
switch (payload.module) {
case "Core": {
target_contract = contracts.core;
target_contract = contracts.coreBridge(network, "Injective");
action = "submit_v_a_a";
execute_msg = {
vaa: fromUint8Array(vaa),
@ -76,14 +76,15 @@ export async function execute_injective(
break;
}
case "NFTBridge": {
if (!contracts.nft_bridge) {
const nftContract = contracts.nftBridge.get(network, "Injective");
if (!nftContract) {
// NOTE: this code can safely be removed once the injective NFT bridge is
// released, but it's fine for it to stay, as the condition will just be
// skipped once 'contracts.nft_bridge' is defined
throw new Error("NFT bridge not supported yet for injective");
}
target_contract = contracts.nft_bridge;
target_contract = nftContract;
action = "submit_vaa";
execute_msg = {
data: fromUint8Array(vaa),
@ -107,12 +108,12 @@ export async function execute_injective(
break;
}
case "TokenBridge": {
console.log("contracts:", contracts);
if (!contracts.token_bridge) {
const tbContract = contracts.tokenBridge.get(network, "Injective");
if (!tbContract) {
throw new Error("contracts.token_bridge is undefined");
}
target_contract = contracts.token_bridge;
target_contract = tbContract;
action = "submit_vaa";
execute_msg = {
data: fromUint8Array(vaa),
@ -165,22 +166,22 @@ export async function execute_injective(
}
export async function transferInjective(
dstChain: ChainName,
dstChain: Chain,
dstAddress: string,
tokenAddress: string,
amount: string,
network: Network,
rpc: string
) {
if (network === "DEVNET") {
if (network === "Devnet") {
throw new Error("Injective is not supported in DEVNET");
}
const chain = "injective";
const chain = "Injective";
const { key } = NETWORKS[network][chain];
if (!key) {
throw Error(`No ${network} key defined for Injective`);
}
const { token_bridge } = CONTRACTS[network][chain];
const token_bridge = contracts.tokenBridge.get(network, "Injective");
if (token_bridge == undefined) {
throw Error(`Unknown token bridge contract on ${network} for ${chain}`);
}
@ -193,8 +194,8 @@ export async function transferInjective(
token_bridge,
tokenAddress,
amount,
dstChain,
tryNativeToUint8Array(dstAddress, dstChain)
chainToChainId(dstChain),
tryNativeToUint8Array(dstAddress, chainToChainId(dstChain))
);
await signAndSendTx(walletPK, network, msgs);
@ -268,18 +269,17 @@ export async function queryRegistrationsInjective(
network: Network,
module: "Core" | "NFTBridge" | "TokenBridge"
) {
const chain = "injective";
const chain = "Injective";
const n = NETWORKS[network][chain];
const contracts = CONTRACTS[network][chain];
let targetContract: string | undefined;
switch (module) {
case "TokenBridge":
targetContract = contracts.token_bridge;
targetContract = contracts.tokenBridge.get(network, "Injective");
break;
case "NFTBridge":
targetContract = contracts.nft_bridge;
targetContract = contracts.nftBridge.get(network, "Injective");
break;
default:
throw new Error(`Invalid module: ${module}`);
@ -297,14 +297,14 @@ export async function queryRegistrationsInjective(
// Query the bridge registration for all the chains in parallel.
const registrations: (any | null)[][] = await Promise.all(
Object.entries(CHAINS)
.filter(([cname, _]) => cname !== chain && cname !== "unset")
.map(async ([cname, cid]) => [
chains
.filter((cname) => cname !== chain)
.map(async (cname) => [
cname,
await (async () => {
let query_msg = {
chain_registration: {
chain: cid,
chain: chainToChainId(cname),
},
};

View File

@ -1,18 +1,19 @@
import {
ChainName,
CONTRACTS,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import BN from "bn.js";
import { Account, connect, KeyPair } from "near-api-js";
import { InMemoryKeyStore } from "near-api-js/lib/key_stores";
import { NETWORKS } from "./consts";
import { Network } from "./utils";
import { impossible, Payload } from "./vaa";
import {
transferNearFromNear,
transferTokenFromNear,
} from "@certusone/wormhole-sdk/lib/esm/token_bridge/transfer";
import { tryNativeToUint8Array } from "@certusone/wormhole-sdk/lib/esm/utils";
import {
Chain,
chainToChainId,
contracts,
Network,
} from "@wormhole-foundation/sdk-base";
import { tryNativeToUint8Array } from "./sdk/array";
export function keyPairToImplicitAccount(keyPair: KeyPair): string {
return Buffer.from(keyPair.getPublicKey().data).toString("hex");
@ -23,7 +24,7 @@ export const execute_near = async (
vaa: string,
network: Network
): Promise<void> => {
const { rpc, key, networkId } = NETWORKS[network].near;
const { rpc, key, networkId } = NETWORKS[network].Near;
if (!key) {
throw Error(`No ${network} key defined for NEAR`);
}
@ -32,16 +33,15 @@ export const execute_near = async (
throw Error(`No ${network} rpc defined for NEAR`);
}
const contracts = CONTRACTS[network].near;
let target_contract: string;
let numSubmits = 1;
switch (payload.module) {
case "Core": {
if (!contracts.core) {
const coreContract = contracts.coreBridge(network, "Near");
if (!coreContract) {
throw new Error(`Core bridge address not defined for NEAR ${network}`);
}
target_contract = contracts.core;
target_contract = coreContract;
switch (payload.type) {
case "GuardianSetUpgrade":
console.log("Submitting new guardian set");
@ -57,12 +57,13 @@ export const execute_near = async (
break;
}
case "NFTBridge": {
if (!contracts.nft_bridge) {
const nftContract = contracts.nftBridge.get(network, "Near");
if (!nftContract) {
throw new Error(`NFT bridge address not defined for NEAR ${network}`);
}
numSubmits = 2;
target_contract = contracts.nft_bridge;
target_contract = nftContract;
switch (payload.type) {
case "ContractUpgrade":
console.log("Upgrading contract");
@ -82,12 +83,13 @@ export const execute_near = async (
break;
}
case "TokenBridge": {
if (!contracts.token_bridge) {
const tbContract = contracts.tokenBridge(network, "Near");
if (!tbContract) {
throw new Error(`Token bridge address not defined for NEAR ${network}`);
}
numSubmits = 2;
target_contract = contracts.token_bridge;
target_contract = tbContract;
switch (payload.type) {
case "ContractUpgrade":
console.log("Upgrading contract");
@ -158,18 +160,19 @@ export const execute_near = async (
};
export async function transferNear(
dstChain: ChainName,
dstChain: Chain,
dstAddress: string,
tokenAddress: string,
amount: string,
network: Network,
rpc: string
) {
const { key, networkId } = NETWORKS[network].near;
const { key, networkId } = NETWORKS[network].Near;
if (!key) {
throw Error(`No ${network} key defined for NEAR`);
}
const { core, token_bridge } = CONTRACTS[network].near;
const core = contracts.coreBridge(network, "Near");
const token_bridge = contracts.tokenBridge(network, "Near");
if (core === undefined) {
throw Error(`Unknown core contract on ${network} for NEAR`);
}
@ -193,8 +196,8 @@ export async function transferNear(
core,
token_bridge,
BigInt(amount),
tryNativeToUint8Array(dstAddress, dstChain),
dstChain,
tryNativeToUint8Array(dstAddress, chainToChainId(dstChain)),
chainToChainId(dstChain),
BigInt(0)
);
const result = await nearAccount.functionCall(msg);
@ -207,8 +210,8 @@ export async function transferNear(
token_bridge,
tokenAddress,
BigInt(amount),
tryNativeToUint8Array(dstAddress, dstChain),
dstChain,
tryNativeToUint8Array(dstAddress, chainToChainId(dstChain)),
chainToChainId(dstChain),
BigInt(0)
);
for (const msg of msgs) {

315
clients/js/src/sdk/array.ts Normal file
View File

@ -0,0 +1,315 @@
import { arrayify, zeroPad } from "@ethersproject/bytes";
import { PublicKey } from "@solana/web3.js";
import {
hexValue,
hexZeroPad,
keccak256,
sha256,
stripZeros,
} from "ethers/lib/utils";
import { bech32 } from "bech32";
import {
Chain,
ChainId,
chainToChainId,
chainToPlatform,
toChain,
toChainId,
} from "@wormhole-foundation/sdk-base";
import {
PlatformToChains,
UniversalAddress,
encoding,
} from "@wormhole-foundation/sdk";
import { isValidSuiAddress } from "@mysten/sui.js";
import { sha3_256 } from "js-sha3";
import {
nativeStringToHexAlgorand,
uint8ArrayToNativeStringAlgorand,
} from "@certusone/wormhole-sdk/lib/esm/algorand";
import { isValidSuiType } from "@certusone/wormhole-sdk/lib/esm/sui";
/**
*
* Returns true iff the hex string represents a native Terra denom.
*
* Native assets on terra don't have an associated smart contract address, just
* like eth isn't an ERC-20 contract on Ethereum.
*
* The difference is that the EVM implementations of Portal don't support eth
* directly, and instead require swapping to an ERC-20 wrapped eth (WETH)
* contract first.
*
* The Terra implementation instead supports Terra-native denoms without
* wrapping to CW-20 token first. As these denoms don't have an address, they
* are encoded in the Portal payloads by the setting the first byte to 1. This
* encoding is safe, because the first 12 bytes of the 32-byte wormhole address
* space are not used on Terra otherwise, as cosmos addresses are 20 bytes wide.
*/
export const isHexNativeTerra = (h: string): boolean => h.startsWith("01");
const isLikely20ByteCosmwasm = (h: string): boolean =>
h.startsWith("000000000000000000000000");
export const nativeTerraHexToDenom = (h: string): string =>
Buffer.from(stripZeros(hexToUint8Array(h.substr(2)))).toString("ascii");
export const isNativeDenom = (string = "") =>
isNativeTerra(string) || string === "uluna";
export const isNativeTerra = (string = "") =>
string.startsWith("u") && string.length === 4;
export const uint8ArrayToHex = (a: Uint8Array): string =>
encoding.hex.encode(a);
export const hexToUint8Array = (h: string): Uint8Array =>
encoding.hex.decode(h);
export function canonicalAddress(humanAddress: string) {
return new Uint8Array(bech32.fromWords(bech32.decode(humanAddress).words));
}
export function humanAddress(hrp: string, canonicalAddress: Uint8Array) {
return bech32.encode(hrp, bech32.toWords(canonicalAddress));
}
export function buildTokenId(
chain: Exclude<PlatformToChains<"Cosmwasm">, "Terra">,
address: string
) {
return (
(isNativeCosmWasmDenom(chain, address) ? "01" : "00") +
keccak256(Buffer.from(address, "utf-8")).substring(4)
);
}
/**
*
* Convert an address in a wormhole's 32-byte array representation into a chain's
* native string representation.
*
* @throws if address is not the right length for the given chain
*/
export const tryUint8ArrayToNative = (
a: Uint8Array,
chain: ChainId | Chain
): string => {
const chainName = toChain(chain);
if (chainToPlatform(chainName) === "Evm") {
// if (isEVMChain(chainId)) {
return hexZeroPad(hexValue(a), 20);
} else if (chainToPlatform(chainName) === "Solana") {
return new PublicKey(a).toString();
} else if (chainName === "Terra" || chainName === "Terra2") {
const h = uint8ArrayToHex(a);
if (isHexNativeTerra(h)) {
return nativeTerraHexToDenom(h);
} else {
if (chainName === "Terra2" && !isLikely20ByteCosmwasm(h)) {
// terra 2 has 32 byte addresses for contracts and 20 for wallets
return humanAddress("terra", a);
}
return humanAddress("terra", a.slice(-20));
}
} else if (chainName === "Injective") {
const h = uint8ArrayToHex(a);
return humanAddress("inj", isLikely20ByteCosmwasm(h) ? a.slice(-20) : a);
} else if (chainName === "Algorand") {
return uint8ArrayToNativeStringAlgorand(a);
} else if (chainName == "Wormchain") {
const h = uint8ArrayToHex(a);
return humanAddress(
"wormhole",
isLikely20ByteCosmwasm(h) ? a.slice(-20) : a
);
} else if (chainName === "Xpla") {
const h = uint8ArrayToHex(a);
return humanAddress("xpla", isLikely20ByteCosmwasm(h) ? a.slice(-20) : a);
} else if (chainName === "Sei") {
const h = uint8ArrayToHex(a);
return humanAddress("sei", isLikely20ByteCosmwasm(h) ? a.slice(-20) : a);
} else if (chainName === "Near") {
throw Error("uint8ArrayToNative: Use tryHexToNativeStringNear instead.");
} else if (chainName === "Osmosis") {
throw Error("uint8ArrayToNative: Osmosis not supported yet.");
} else if (chainName === "Cosmoshub") {
throw Error("uint8ArrayToNative: CosmosHub not supported yet.");
} else if (chainName === "Evmos") {
throw Error("uint8ArrayToNative: Evmos not supported yet.");
} else if (chainName === "Kujira") {
throw Error("uint8ArrayToNative: Kujira not supported yet.");
} else if (chainName === "Neutron") {
throw Error("uint8ArrayToNative: Neutron not supported yet.");
} else if (chainName === "Celestia") {
throw Error("uint8ArrayToNative: Celestia not supported yet.");
} else if (chainName === "Stargaze") {
throw Error("uint8ArrayToNative: Stargaze not supported yet.");
} else if (chainName === "Seda") {
throw Error("uint8ArrayToNative: Seda not supported yet.");
} else if (chainName === "Dymension") {
throw Error("uint8ArrayToNative: Dymension not supported yet.");
} else if (chainName === "Provenance") {
throw Error("uint8ArrayToNative: Provenance not supported yet.");
} else if (chainName === "Sui") {
throw Error("uint8ArrayToNative: Sui not supported yet.");
} else if (chainName === "Aptos") {
throw Error("uint8ArrayToNative: Aptos not supported yet.");
} else if (chainName === "Btc") {
throw Error("uint8ArrayToNative: Btc not supported");
} else {
// This case is never reached
// const _: never = chainName;
throw Error("Don't know how to convert address for chain " + chainName);
}
};
/**
*
* Convert an address in a wormhole's 32-byte hex representation into a chain's native
* string representation.
*
* @throws if address is not the right length for the given chain
*/
export const tryHexToNativeAssetString = (h: string, c: ChainId): string =>
c === chainToChainId("Algorand")
? // Algorand assets are represented by their asset ids, not an address
new UniversalAddress(h).toNative("Algorand").toBigInt().toString()
: new UniversalAddress(h).toNative(toChain(c)).toString();
/**
*
* Convert an address in a chain's native representation into a 32-byte hex string
* understood by wormhole (UniversalAddress).
*
* @throws if address is a malformed string for the given chain id
*/
export const tryNativeToHexString = (
address: string,
chain: ChainId | Chain
): string => {
const chainName = toChain(chain);
if (chainToPlatform(chainName) === "Evm") {
return uint8ArrayToHex(zeroPad(arrayify(address), 32));
} else if (chainToPlatform(chainName) === "Solana") {
return uint8ArrayToHex(zeroPad(new PublicKey(address).toBytes(), 32));
} else if (chainName === "Terra") {
if (isNativeDenom(address)) {
return (
"01" +
uint8ArrayToHex(
zeroPad(new Uint8Array(Buffer.from(address, "ascii")), 31)
)
);
} else {
return uint8ArrayToHex(zeroPad(canonicalAddress(address), 32));
}
} else if (
chainName === "Terra2" ||
chainName === "Injective" ||
chainName === "Xpla" ||
chainName === "Sei"
) {
return buildTokenId(chainName, address);
} else if (chainName === "Algorand") {
return nativeStringToHexAlgorand(address);
} else if (chainName == "Wormchain") {
return uint8ArrayToHex(zeroPad(canonicalAddress(address), 32));
} else if (chainName === "Near") {
return uint8ArrayToHex(arrayify(sha256(Buffer.from(address))));
} else if (chainName === "Sui") {
if (!isValidSuiType(address) && isValidSuiAddress(address)) {
return uint8ArrayToHex(
zeroPad(arrayify(address, { allowMissingPrefix: true }), 32)
);
}
throw Error("nativeToHexString: Sui types not supported yet.");
} else if (chainName === "Aptos") {
if (isValidAptosType(address)) {
return getExternalAddressFromType(address);
}
return uint8ArrayToHex(
zeroPad(arrayify(address, { allowMissingPrefix: true }), 32)
);
} else {
// If this case is reached
throw Error(`nativeToHexString: ${chainName} not supported yet.`);
}
};
/**
*
* Convert an address in a chain's native representation into a 32-byte array
* understood by wormhole.
*
* @throws if address is a malformed string for the given chain id
*/
export function tryNativeToUint8Array(
address: string,
chain: ChainId | Chain
): Uint8Array {
const chainId = toChainId(chain);
return hexToUint8Array(tryNativeToHexString(address, chainId));
}
export const isNativeDenomInjective = (denom: string) => denom === "inj";
export const isNativeDenomXpla = (denom: string) => denom === "axpla";
export const isNativeDenomSei = (denom: string) => denom === "usei";
export const isNativeDenomWormchain = (denom: string) => denom === "uworm";
export const isNativeDenomOsmosis = (denom: string) => denom === "uosmo";
export const isNativeDenomCosmosHub = (denom: string) => denom === "uatom";
export const isNativeDenomEvmos = (denom: string) =>
denom === "aevmos" || denom === "atevmos";
export const isNativeDenomKujira = (denom: string) => denom === "ukuji";
export const isNativeDenomNeutron = (denom: string) => denom === "untrn";
export const isNativeDenomCelestia = (denom: string) => denom === "utia";
export const isNativeDenomStargaze = (denom: string) => denom === "ustars";
export const isNativeDenomSeda = (denom: string) => denom === "aseda";
export const isNativeDenomDymension = (denom: string) => denom === "adym";
export const isNativeDenomProvenance = (denom: string) => denom === "nhash";
export function isNativeCosmWasmDenom(
chain: PlatformToChains<"Cosmwasm">,
address: string
) {
return (
((chain === "Terra" || chain === "Terra2") && isNativeDenom(address)) ||
(chain === "Injective" && isNativeDenomInjective(address)) ||
(chain === "Xpla" && isNativeDenomXpla(address)) ||
(chain === "Sei" && isNativeDenomSei(address)) ||
(chain === "Wormchain" && isNativeDenomWormchain(address)) ||
(chain === "Osmosis" && isNativeDenomOsmosis(address)) ||
(chain === "Cosmoshub" && isNativeDenomCosmosHub(address)) ||
(chain === "Evmos" && isNativeDenomEvmos(address)) ||
(chain === "Kujira" && isNativeDenomKujira(address)) ||
(chain === "Neutron" && isNativeDenomNeutron(address)) ||
(chain === "Celestia" && isNativeDenomCelestia(address)) ||
(chain === "Stargaze" && isNativeDenomStargaze(address)) ||
(chain === "Seda" && isNativeDenomSeda(address)) ||
(chain === "Dymension" && isNativeDenomDymension(address)) ||
(chain === "Provenance" && isNativeDenomProvenance(address))
);
}
/**
* Test if given string is a valid fully qualified type of moduleAddress::moduleName::structName.
* @param str String to test
* @returns Whether or not given string is a valid type
*/
export const isValidAptosType = (str: string): boolean =>
/^(0x)?[0-9a-fA-F]+::\w+::\w+$/.test(str);
/**
* Hashes the given type. Because fully qualified types are a concept unique to Aptos, this
* output acts as the address on other chains.
* @param fullyQualifiedType Fully qualified type on Aptos
* @returns External address corresponding to given type
*/
export const getExternalAddressFromType = (
fullyQualifiedType: string
): string => {
// hash the type so it fits into 32 bytes
return sha3_256(fullyQualifiedType);
};

130
clients/js/src/sdk/sui.ts Normal file
View File

@ -0,0 +1,130 @@
import {
JsonRpcProvider,
SuiObjectResponse,
isValidSuiAddress as isValidFullSuiAddress,
normalizeSuiAddress,
} from "@mysten/sui.js";
import { Chain, chainToChainId } from "@wormhole-foundation/sdk";
export async function getForeignAssetSui(
provider: JsonRpcProvider,
tokenBridgeStateObjectId: string,
originChain: Chain,
originAddress: Uint8Array
): Promise<string | null> {
const originChainId = chainToChainId(originChain);
return getTokenCoinType(
provider,
tokenBridgeStateObjectId,
originAddress,
originChainId
);
}
export const getTokenCoinType = async (
provider: JsonRpcProvider,
tokenBridgeStateObjectId: string,
tokenAddress: Uint8Array,
tokenChain: number
): Promise<string | null> => {
const tokenBridgeStateFields = await getObjectFields(
provider,
tokenBridgeStateObjectId
);
if (!tokenBridgeStateFields) {
throw new Error("Unable to fetch object fields from token bridge state");
}
const coinTypes = tokenBridgeStateFields?.token_registry?.fields?.coin_types;
const coinTypesObjectId = coinTypes?.fields?.id?.id;
if (!coinTypesObjectId) {
throw new Error("Unable to fetch coin types");
}
const keyType = getTableKeyType(coinTypes?.type);
if (!keyType) {
throw new Error("Unable to get key type");
}
const response = await provider.getDynamicFieldObject({
parentId: coinTypesObjectId,
name: {
type: keyType,
value: {
addr: [...tokenAddress],
chain: tokenChain,
},
},
});
if (response.error) {
if (response.error.code === "dynamicFieldNotFound") {
return null;
}
throw new Error(
`Unexpected getDynamicFieldObject response ${response.error}`
);
}
const fields = getFieldsFromObjectResponse(response);
return fields?.value ? trimSuiType(ensureHexPrefix(fields.value)) : null;
};
export const getObjectFields = async (
provider: JsonRpcProvider,
objectId: string
): Promise<Record<string, any> | null> => {
if (!isValidSuiAddress(objectId)) {
throw new Error(`Invalid object ID: ${objectId}`);
}
const res = await provider.getObject({
id: objectId,
options: {
showContent: true,
},
});
return getFieldsFromObjectResponse(res);
};
export const getFieldsFromObjectResponse = (object: SuiObjectResponse) => {
const content = object.data?.content;
return content && content.dataType === "moveObject" ? content.fields : null;
};
export function ensureHexPrefix(x: string): string {
return x.substring(0, 2) !== "0x" ? `0x${x}` : x;
}
/**
* This method validates any Sui address, even if it's not 32 bytes long, i.e.
* "0x2". This differs from Mysten's implementation, which requires that the
* given address is 32 bytes long.
* @param address Address to check
* @returns If given address is a valid Sui address or not
*/
export const isValidSuiAddress = (address: string): boolean =>
isValidFullSuiAddress(normalizeSuiAddress(address));
export const getTableKeyType = (tableType: string): string | null => {
if (!tableType) return null;
const match = trimSuiType(tableType).match(/0x2::table::Table<(.*)>/);
if (!match) return null;
const [keyType] = match[1].split(",");
if (!isValidSuiType(keyType)) return null;
return keyType;
};
/**
* This method removes leading zeroes for types in order to normalize them
* since some types returned from the RPC have leading zeroes and others don't.
*/
export const trimSuiType = (type: string): string =>
type.replace(/(0x)(0*)/g, "0x");
export const isValidSuiType = (type: string): boolean => {
const tokens = type.split("::");
if (tokens.length !== 3) {
return false;
}
return isValidSuiAddress(tokens[0]) && !!tokens[1] && !!tokens[2];
};

View File

@ -16,13 +16,6 @@ import {
createUpgradeGuardianSetInstruction,
createUpgradeContractInstruction as createWormholeUpgradeContractInstruction,
} from "@certusone/wormhole-sdk/lib/esm/solana/wormhole";
import {
CHAINS,
CONTRACTS,
ChainName,
Network,
SolanaChainName,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import * as web3s from "@solana/web3.js";
import base58 from "bs58";
import { NETWORKS } from "./consts";
@ -32,19 +25,30 @@ import {
transferFromSolana,
transferNativeSol,
} from "@certusone/wormhole-sdk/lib/esm/token_bridge/transfer";
import {
hexToUint8Array,
tryNativeToUint8Array,
} from "@certusone/wormhole-sdk/lib/esm/utils";
import { PublicKey } from "@solana/web3.js";
import { getAssociatedTokenAddress } from "@solana/spl-token";
import {
Chain,
Network,
PlatformToChains,
chainToChainId,
chainToPlatform,
chains,
contracts,
platformToChains,
} from "@wormhole-foundation/sdk-base";
import { hexToUint8Array, tryNativeToUint8Array } from "./sdk/array";
export async function execute_solana(
v: VAA<Payload>,
vaa: Buffer,
network: "MAINNET" | "TESTNET" | "DEVNET",
chain: SolanaChainName
network: Network,
chain: Chain
) {
if (chainToPlatform(chain) !== "Solana") {
// This "Solana" platform, also, includes Pythnet
throw new Error("Invalid chain");
}
const { rpc, key } = NETWORKS[network][chain];
if (!key) {
throw Error(`No ${network} key defined for ${chain}`);
@ -57,22 +61,24 @@ export async function execute_solana(
const connection = setupConnection(rpc);
const from = web3s.Keypair.fromSecretKey(base58.decode(key));
const contracts = CONTRACTS[network][chain];
if (!contracts.core) {
const coreContract = contracts.coreBridge.get(network, chain);
if (!coreContract) {
throw new Error(`Core bridge address not defined for ${chain} ${network}`);
}
if (!contracts.nft_bridge) {
const nftContract = contracts.nftBridge.get(network, chain);
if (!nftContract) {
throw new Error(`NFT bridge address not defined for ${chain} ${network}`);
}
if (!contracts.token_bridge) {
const tbContract = contracts.tokenBridge.get(network, chain);
if (!tbContract) {
throw new Error(`Token bridge address not defined for ${chain} ${network}`);
}
const bridgeId = new web3s.PublicKey(contracts.core);
const tokenBridgeId = new web3s.PublicKey(contracts.token_bridge);
const nftBridgeId = new web3s.PublicKey(contracts.nft_bridge);
const bridgeId = new web3s.PublicKey(coreContract);
const tokenBridgeId = new web3s.PublicKey(tbContract);
const nftBridgeId = new web3s.PublicKey(nftContract);
let ix: web3s.TransactionInstruction;
switch (v.payload.module) {
@ -163,7 +169,7 @@ export async function execute_solana(
break;
case "Transfer":
console.log("Completing transfer");
if (payload.tokenChain === CHAINS[chain]) {
if (payload.tokenChain === chainToChainId(chain)) {
ix = createCompleteTransferNativeInstruction(
tokenBridgeId,
bridgeId,
@ -228,14 +234,15 @@ export async function execute_solana(
}
export async function transferSolana(
srcChain: SolanaChainName,
dstChain: ChainName,
srcChain: PlatformToChains<"Solana">,
dstChain: Chain,
dstAddress: string,
tokenAddress: string,
amount: string,
network: Network,
rpc: string
) {
platformToChains("Solana");
const { key } = NETWORKS[network][srcChain];
if (!key) {
throw Error(`No ${network} key defined for ${srcChain}`);
@ -244,12 +251,13 @@ export async function transferSolana(
const connection = setupConnection(rpc);
const keypair = web3s.Keypair.fromSecretKey(base58.decode(key));
const { core, token_bridge } = CONTRACTS[network][srcChain];
const core = contracts.coreBridge.get(network, srcChain);
if (!core) {
throw new Error(
`Core bridge address not defined for ${srcChain} ${network}`
);
}
const token_bridge = contracts.tokenBridge.get(network, srcChain);
if (!token_bridge) {
throw new Error(
`Token bridge address not defined for ${srcChain} ${network}`
@ -268,8 +276,8 @@ export async function transferSolana(
tokenBridgeId,
payerAddress,
BigInt(amount),
tryNativeToUint8Array(dstAddress, dstChain),
dstChain
tryNativeToUint8Array(dstAddress, chainToChainId(dstChain)),
chainToChainId(dstChain)
);
} else {
// find the associated token account
@ -287,8 +295,8 @@ export async function transferSolana(
fromAddress,
tokenAddress, // mintAddress
BigInt(amount),
tryNativeToUint8Array(dstAddress, dstChain),
dstChain
tryNativeToUint8Array(dstAddress, chainToChainId(dstChain)),
chainToChainId(dstChain)
);
}
@ -316,18 +324,17 @@ export async function queryRegistrationsSolana(
network: Network,
module: "Core" | "NFTBridge" | "TokenBridge"
): Promise<Object> {
const chain = "solana" as ChainName;
const chain = "Solana";
const n = NETWORKS[network][chain];
const contracts = CONTRACTS[network][chain];
let targetAddress: string | undefined;
switch (module) {
case "TokenBridge":
targetAddress = contracts.token_bridge;
targetAddress = contracts.tokenBridge(network, chain);
break;
case "NFTBridge":
targetAddress = contracts.nft_bridge;
targetAddress = contracts.nftBridge(network, chain);
break;
default:
throw new Error(`Invalid module: ${module}`);
@ -346,26 +353,26 @@ export async function queryRegistrationsSolana(
// Query the bridge registration for all the chains in parallel.
const registrations: (string | null)[][] = await Promise.all(
Object.entries(CHAINS)
.filter(([cname, _]) => cname !== chain && cname !== "unset")
.map(async ([cstr, cid]) => [
chains
.filter((cname) => cname !== chain)
.map(async (cstr) => [
cstr,
await (async () => {
let cname = cstr as ChainName;
// let cname = cstr as Chain;
let addr: string | undefined;
if (module === "TokenBridge") {
addr = CONTRACTS[network][cname].token_bridge;
addr = contracts.tokenBridge.get(network, cstr);
} else {
addr = CONTRACTS[network][cname].nft_bridge;
addr = contracts.nftBridge.get(network, cstr);
}
if (addr === undefined) {
return null;
}
let emitter_addr = await getEmitterAddress(cname as ChainName, addr);
let emitter_addr = await getEmitterAddress(cstr, addr);
const endpoint = deriveEndpointKey(
programId,
cid,
chainToChainId(cstr),
hexToUint8Array(emitter_addr)
);

View File

@ -1,9 +1,3 @@
import {
CHAINS,
CONTRACTS,
ChainName,
TerraChainName,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import {
Coin,
Fee,
@ -15,24 +9,29 @@ import {
import axios from "axios";
import { fromUint8Array } from "js-base64";
import { NETWORKS } from "./consts";
import { Network } from "./utils";
import { Payload, impossible } from "./vaa";
import { transferFromTerra } from "@certusone/wormhole-sdk/lib/esm/token_bridge/transfer";
import { tryNativeToUint8Array } from "@certusone/wormhole-sdk/lib/esm/utils";
import {
Chain,
Network,
chains,
contracts,
toChainId,
} from "@wormhole-foundation/sdk-base";
import { tryNativeToUint8Array } from "./sdk/array";
export async function execute_terra(
payload: Payload,
vaa: Buffer,
network: Network,
chain: TerraChainName
chain: "Terra" | "Terra2"
): Promise<void> {
const { rpc, key, chain_id } = NETWORKS[network][chain];
const contracts = CONTRACTS[network][chain];
const terra = new LCDClient({
URL: rpc,
chainID: chain_id,
isClassic: chain === "terra",
isClassic: chain === "Terra",
});
const wallet = terra.wallet(
@ -46,13 +45,14 @@ export async function execute_terra(
switch (payload.module) {
case "Core": {
if (!contracts.core) {
const coreContract = contracts.coreBridge(network, chain);
if (!coreContract) {
throw new Error(
`Core bridge address not defined for ${chain} ${network}`
);
}
target_contract = contracts.core;
target_contract = coreContract;
// sigh...
execute_msg = {
submit_v_a_a: {
@ -75,14 +75,15 @@ export async function execute_terra(
break;
}
case "NFTBridge": {
if (!contracts.nft_bridge) {
const nftContract = contracts.nftBridge.get(network, chain);
if (!nftContract) {
// NOTE: this code can safely be removed once the terra NFT bridge is
// released, but it's fine for it to stay, as the condition will just be
// skipped once 'contracts.nft_bridge' is defined
throw new Error(`NFT bridge not supported yet for ${chain}`);
}
target_contract = contracts.nft_bridge;
target_contract = nftContract;
execute_msg = {
submit_vaa: {
data: fromUint8Array(vaa),
@ -107,13 +108,14 @@ export async function execute_terra(
break;
}
case "TokenBridge": {
if (!contracts.token_bridge) {
const tbContract = contracts.tokenBridge.get(network, chain);
if (!tbContract) {
throw new Error(
`Token bridge address not defined for ${chain} ${network}`
);
}
target_contract = contracts.token_bridge;
target_contract = tbContract;
execute_msg = {
submit_vaa: {
data: fromUint8Array(vaa),
@ -160,8 +162,8 @@ export async function execute_terra(
}
export async function transferTerra(
srcChain: TerraChainName,
dstChain: ChainName,
srcChain: "Terra" | "Terra2",
dstChain: Chain,
dstAddress: string,
tokenAddress: string,
amount: string,
@ -172,7 +174,7 @@ export async function transferTerra(
if (!n.key) {
throw Error(`No ${network} key defined for ${srcChain} (see networks.ts)`);
}
const { token_bridge } = CONTRACTS[network][srcChain];
const token_bridge = contracts.tokenBridge.get(network, srcChain);
if (!token_bridge) {
throw Error(`Unknown token bridge contract on ${network} for ${srcChain}`);
}
@ -180,7 +182,7 @@ export async function transferTerra(
const terra = new LCDClient({
URL: rpc,
chainID: n.chain_id,
isClassic: srcChain === "terra",
isClassic: srcChain === "Terra",
});
const wallet = terra.wallet(
@ -194,8 +196,8 @@ export async function transferTerra(
token_bridge,
tokenAddress,
amount,
dstChain,
tryNativeToUint8Array(dstAddress, dstChain)
toChainId(dstChain),
tryNativeToUint8Array(dstAddress, toChainId(dstChain))
);
await signAndSendTx(terra, wallet, msgs);
}
@ -242,20 +244,19 @@ async function signAndSendTx(
export async function queryRegistrationsTerra(
network: Network,
chain: TerraChainName,
chain: "Terra" | "Terra2",
module: "Core" | "NFTBridge" | "TokenBridge"
): Promise<Object> {
const n = NETWORKS[network][chain];
const contracts = CONTRACTS[network][chain];
let targetContract: string | undefined;
switch (module) {
case "TokenBridge":
targetContract = contracts.token_bridge;
targetContract = contracts.tokenBridge(network, chain);
break;
case "NFTBridge":
targetContract = contracts.nft_bridge;
targetContract = contracts.nftBridge.get(network, chain);
break;
default:
throw new Error(`Invalid module: ${module}`);
@ -276,19 +277,19 @@ export async function queryRegistrationsTerra(
const client = new LCDClient({
URL: n.rpc,
chainID: n.chain_id,
isClassic: chain === "terra",
isClassic: chain === "Terra",
});
// Query the bridge registration for all the chains in parallel.
const registrations: (string | null)[][] = await Promise.all(
Object.entries(CHAINS)
.filter(([cname, _]) => cname !== chain && cname !== "unset")
.map(async ([cname, cid]) => [
chains
.filter((cname) => cname !== chain)
.map(async (cname) => [
cname,
await (async () => {
let query_msg = {
chain_registration: {
chain: cid,
chain: toChainId(cname),
},
};

View File

@ -0,0 +1,76 @@
// > paul@W7windows:~$ worm evm info -n mainnet -c ethereum -m TokenBridge
// {
// "address": "0x3ee18B2214AFF97000D974cf647E7C347E8fa585",
// "wormhole": "0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B",
// "implementation": "0x381752f5458282d317d12C30D2Bd4D6E1FD8841e",
// "isInitialized": true,
// "tokenImplementation": "0x0fD04a68d3c3A692d6Fa30384D1A87Ef93554eE6",
// "chainId": 2,
// "finality": 1,
// "evmChainId": "1",
// "isFork": false,
// "governanceChainId": 1,
// "governanceContract": "0x0000000000000000000000000000000000000000000000000000000000000004",
// "WETH": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
// "registrations": {
// "solana": "0xec7372995d5cc8732397fb0ad35c0121e0eaa90d26f828a534cab54391b3a4f5",
// "terra": "0x0000000000000000000000007cf7b764e38a0a5e967972c1df77d432510564e2",
// "bsc": "0x000000000000000000000000b6f6d86a8f9879a9c87f643768d9efc38c1da6e7",
// "polygon": "0x0000000000000000000000005a58505a96d1dbf8df91cb21b54419fc36e93fde",
// "avalanche": "0x0000000000000000000000000e082f06ff657d94310cb8ce8b0d9a04541d8052",
// "oasis": "0x0000000000000000000000005848c791e09901b40a9ef749f2a6735b418d7564",
// "algorand": "0x67e93fa6c8ac5c819990aa7340c0c16b508abb1178be9b30d024b8ac25193d45",
// "aurora": "0x00000000000000000000000051b5123a7b0f9b2ba265f9c4c8de7d78d52f510f",
// "fantom": "0x0000000000000000000000007c9fc5741288cdfdd83ceb07f3ea7e22618d79d2",
// "karura": "0x000000000000000000000000ae9d7fe007b3327aa64a32824aaac52c42a6e624",
// "acala": "0x000000000000000000000000ae9d7fe007b3327aa64a32824aaac52c42a6e624",
// "klaytn": "0x0000000000000000000000005b08ac39eaed75c0439fc750d9fe7e1f9dd0193f",
// "celo": "0x000000000000000000000000796dff6d74f3e27060b71255fe517bfb23c93eed",
// "near": "0x148410499d3fcda4dcfd68a1ebfcdddda16ab28326448d4aae4d2f0465cdfcb7",
// "moonbeam": "0x000000000000000000000000b1731c586ca89a23809861c6103f0b96b3f57d92",
// "neon": "0x0000000000000000000000000000000000000000000000000000000000000000",
// "terra2": "0xa463ad028fb79679cfc8ce1efba35ac0e77b35080a1abe9bebe83461f176b0a3",
// "injective": "0x00000000000000000000000045dbea4617971d93188eda21530bc6503d153313",
// "osmosis": "0x0000000000000000000000000000000000000000000000000000000000000000",
// "sui": "0xccceeb29348f71bdd22ffef43a2a19c1f5b5e17c5cca5411529120182672ade5",
// "aptos": "0x0000000000000000000000000000000000000000000000000000000000000001",
// "arbitrum": "0x0000000000000000000000000b2402144bb366a632d14b83f244d2e0e21bd39c",
// "optimism": "0x0000000000000000000000001d68124e65fafc907325e3edbf8c4d84499daa8b",
// "gnosis": "0x0000000000000000000000000000000000000000000000000000000000000000",
// "pythnet": "0x0000000000000000000000000000000000000000000000000000000000000000",
// "xpla": "0x8f9cf727175353b17a5f574270e370776123d90fd74956ae4277962b4fdee24c",
// "btc": "0x0000000000000000000000000000000000000000000000000000000000000000",
// "base": "0x0000000000000000000000008d2de8d2f73f1f4cab472ac9a881c9b123c79627",
// "sei": "0x86c5fd957e2db8389553e1728f9c27964b22a8154091ccba54d75f4b10c61f5e",
// "rootstock": "0x0000000000000000000000000000000000000000000000000000000000000000",
// "scroll": "0x00000000000000000000000024850c6f61c438823f01b7a3bf2b89b72174fa9d",
// "mantle": "0x0000000000000000000000000000000000000000000000000000000000000000",
// "blast": "0x00000000000000000000000024850c6f61c438823f01b7a3bf2b89b72174fa9d",
// "xlayer": "0x0000000000000000000000000000000000000000000000000000000000000000",
// "linea": "0x0000000000000000000000000000000000000000000000000000000000000000",
// "berachain": "0x0000000000000000000000000000000000000000000000000000000000000000",
// "seievm": "0x0000000000000000000000000000000000000000000000000000000000000000",
// "wormchain": "0xaeb534c45c3049d380b9d9b966f9895f53abd4301bfaff407fa09dea8ae7a924",
// "cosmoshub": "0x0000000000000000000000000000000000000000000000000000000000000000",
// "evmos": "0x0000000000000000000000000000000000000000000000000000000000000000",
// "kujira": "0x0000000000000000000000000000000000000000000000000000000000000000",
// "neutron": "0x0000000000000000000000000000000000000000000000000000000000000000",
// "celestia": "0x0000000000000000000000000000000000000000000000000000000000000000",
// "stargaze": "0x0000000000000000000000000000000000000000000000000000000000000000",
// "seda": "0x0000000000000000000000000000000000000000000000000000000000000000",
// "dymension": "0x0000000000000000000000000000000000000000000000000000000000000000",
// "provenance": "0x0000000000000000000000000000000000000000000000000000000000000000",
// "sepolia": "0x0000000000000000000000000000000000000000000000000000000000000000",
// "arbitrum_sepolia": "0x0000000000000000000000000000000000000000000000000000000000000000",
// "base_sepolia": "0x0000000000000000000000000000000000000000000000000000000000000000",
// "optimism_sepolia": "0x0000000000000000000000000000000000000000000000000000000000000000",
// "holesky": "0x0000000000000000000000000000000000000000000000000000000000000000",
// "polygon_sepolia": "0x0000000000000000000000000000000000000000000000000000000000000000"
// }
// }
// >>>worm info registrations mainnet ethereum TokenBridge -v
// Verification of ethereum MAINNET succeeded!
// >>>paul@W7windows:~$ worm info contract mainnet ethereum TokenBridge
// 0x3ee18B2214AFF97000D974cf647E7C347E8fa585

View File

@ -1,14 +1,14 @@
import {
Chain,
ChainId,
Network,
PlatformToChains,
chainToPlatform,
toChain,
} from "@wormhole-foundation/sdk-base";
import { spawnSync } from "child_process";
import { ethers } from "ethers";
export type Network = "MAINNET" | "TESTNET" | "DEVNET";
export function assertNetwork(n: string): asserts n is Network {
if (n !== "MAINNET" && n !== "TESTNET" && n !== "DEVNET") {
throw Error(`Unknown network: ${n}`);
}
}
export const checkBinary = (binaryName: string, readmeUrl?: string): void => {
const binary = spawnSync(binaryName, ["--version"]);
if (binary.status !== 0) {
@ -29,3 +29,33 @@ export const evm_address = (x: string): string => {
export const hex = (x: string): string => {
return ethers.utils.hexlify(x, { allowMissingPrefix: true });
};
export function assertEVMChain(
chain: ChainId | Chain
): asserts chain is PlatformToChains<"Evm"> {
if (chainToPlatform(toChain(chain)) !== "Evm") {
throw Error(`Expected an EVM chain, but ${chain} is not`);
}
}
export function getNetwork(network: string): Network {
const lcNetwork: string = network.toLowerCase();
if (lcNetwork === "mainnet") {
return "Mainnet";
}
if (lcNetwork === "testnet") {
return "Testnet";
}
if (lcNetwork === "devnet") {
return "Devnet";
}
throw new Error(`Unknown network: ${network}`);
}
export function chainToChain(input: string): Chain {
if (input.length < 2) {
throw new Error(`Invalid chain: ${input}`);
}
const chainStr = input[0].toUpperCase() + input.slice(1).toLowerCase();
return toChain(chainStr);
}

View File

@ -1,7 +1,3 @@
import {
CONTRACTS,
ChainName,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import {
Coin,
Fee,
@ -12,18 +8,22 @@ import {
} from "@xpla/xpla.js";
import { fromUint8Array } from "js-base64";
import { NETWORKS } from "./consts";
import { Network } from "./utils";
import { Payload, impossible } from "./vaa";
import { transferFromXpla } from "@certusone/wormhole-sdk/lib/esm/token_bridge/transfer";
import { tryNativeToUint8Array } from "@certusone/wormhole-sdk/lib/esm/utils";
import {
Chain,
chainToChainId,
contracts,
Network,
} from "@wormhole-foundation/sdk-base";
import { tryNativeToUint8Array } from "./sdk/array";
export async function execute_xpla(
payload: Payload,
vaa: Buffer,
network: Network
) {
const { rpc, key, chain_id } = NETWORKS[network].xpla;
const contracts = CONTRACTS[network].xpla;
const { rpc, key, chain_id } = NETWORKS[network].Xpla;
if (!key) {
throw Error(`No ${network} key defined for XPLA`);
}
@ -47,11 +47,12 @@ export async function execute_xpla(
let execute_msg: object;
switch (payload.module) {
case "Core": {
if (!contracts.core) {
const coreContract = contracts.coreBridge.get(network, "Xpla");
if (!coreContract) {
throw new Error(`Core bridge address not defined for XPLA ${network}`);
}
target_contract = contracts.core;
target_contract = coreContract;
execute_msg = {
submit_v_a_a: {
vaa: fromUint8Array(vaa),
@ -73,14 +74,15 @@ export async function execute_xpla(
break;
}
case "NFTBridge": {
if (!contracts.nft_bridge) {
const nftContract = contracts.nftBridge.get(network, "Xpla");
if (!nftContract) {
// NOTE: this code can safely be removed once the terra NFT bridge is
// released, but it's fine for it to stay, as the condition will just be
// skipped once 'contracts.nft_bridge' is defined
throw new Error("NFT bridge not supported yet for XPLA");
}
target_contract = contracts.nft_bridge;
target_contract = nftContract;
execute_msg = {
submit_vaa: {
data: fromUint8Array(vaa),
@ -105,11 +107,12 @@ export async function execute_xpla(
break;
}
case "TokenBridge": {
if (!contracts.token_bridge) {
const tbContract = contracts.tokenBridge.get(network, "Xpla");
if (!tbContract) {
throw new Error(`Token bridge address not defined for XPLA ${network}`);
}
target_contract = contracts.token_bridge;
target_contract = tbContract;
execute_msg = {
submit_vaa: {
data: fromUint8Array(vaa),
@ -156,18 +159,18 @@ export async function execute_xpla(
}
export async function transferXpla(
dstChain: ChainName,
dstChain: Chain,
dstAddress: string,
tokenAddress: string,
amount: string,
network: Network,
rpc: string
) {
const { key, chain_id } = NETWORKS[network].xpla;
const { key, chain_id } = NETWORKS[network].Xpla;
if (!key) {
throw Error(`No ${network} key defined for XPLA`);
}
const { token_bridge } = CONTRACTS[network].xpla;
const token_bridge = contracts.tokenBridge.get(network, "Xpla");
if (token_bridge == undefined) {
throw Error(`Unknown token bridge contract on ${network} for XPLA`);
}
@ -185,8 +188,8 @@ export async function transferXpla(
token_bridge,
tokenAddress,
amount,
dstChain,
tryNativeToUint8Array(dstAddress, dstChain)
chainToChainId(dstChain),
tryNativeToUint8Array(dstAddress, chainToChainId(dstChain))
);
await signAndSendTx(client, wallet, msgs);
}

View File

@ -6,6 +6,7 @@
"outDir": "./build",
"moduleResolution": "node",
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"strict": true
},