clients/js: Added transfer token command
This commit is contained in:
parent
44273c0641
commit
817f179b34
|
@ -18,6 +18,7 @@
|
||||||
"@injectivelabs/utils": "^1.10.5",
|
"@injectivelabs/utils": "^1.10.5",
|
||||||
"@mysten/sui.js": "^0.32.2",
|
"@mysten/sui.js": "^0.32.2",
|
||||||
"@sei-js/core": "^1.3.2",
|
"@sei-js/core": "^1.3.2",
|
||||||
|
"@solana/spl-token": "^0.3.5",
|
||||||
"@solana/web3.js": "^1.22.0",
|
"@solana/web3.js": "^1.22.0",
|
||||||
"@terra-money/terra.js": "^3.1.3",
|
"@terra-money/terra.js": "^3.1.3",
|
||||||
"@types/config": "^3.3.0",
|
"@types/config": "^3.3.0",
|
||||||
|
@ -3223,9 +3224,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@solana/spl-token": {
|
"node_modules/@solana/spl-token": {
|
||||||
"version": "0.3.7",
|
"version": "0.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/@solana/spl-token/-/spl-token-0.3.7.tgz",
|
"resolved": "https://registry.npmjs.org/@solana/spl-token/-/spl-token-0.3.5.tgz",
|
||||||
"integrity": "sha512-bKGxWTtIw6VDdCBngjtsGlKGLSmiu/8ghSt/IOYJV24BsymRbgq7r12GToeetpxmPaZYLddKwAz7+EwprLfkfg==",
|
"integrity": "sha512-0bGC6n415lGjKu02gkLOIpP1wzndSP0SHwN9PefJ+wKAhmfU1rl3AV1Pa41uap2kzSCD6F9642ngNO8KXPvh/g==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@solana/buffer-layout": "^4.0.0",
|
"@solana/buffer-layout": "^4.0.0",
|
||||||
"@solana/buffer-layout-utils": "^0.2.0",
|
"@solana/buffer-layout-utils": "^0.2.0",
|
||||||
|
@ -10594,9 +10595,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@solana/spl-token": {
|
"@solana/spl-token": {
|
||||||
"version": "0.3.7",
|
"version": "0.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/@solana/spl-token/-/spl-token-0.3.7.tgz",
|
"resolved": "https://registry.npmjs.org/@solana/spl-token/-/spl-token-0.3.5.tgz",
|
||||||
"integrity": "sha512-bKGxWTtIw6VDdCBngjtsGlKGLSmiu/8ghSt/IOYJV24BsymRbgq7r12GToeetpxmPaZYLddKwAz7+EwprLfkfg==",
|
"integrity": "sha512-0bGC6n415lGjKu02gkLOIpP1wzndSP0SHwN9PefJ+wKAhmfU1rl3AV1Pa41uap2kzSCD6F9642ngNO8KXPvh/g==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@solana/buffer-layout": "^4.0.0",
|
"@solana/buffer-layout": "^4.0.0",
|
||||||
"@solana/buffer-layout-utils": "^0.2.0",
|
"@solana/buffer-layout-utils": "^0.2.0",
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
"@injectivelabs/utils": "^1.10.5",
|
"@injectivelabs/utils": "^1.10.5",
|
||||||
"@mysten/sui.js": "^0.32.2",
|
"@mysten/sui.js": "^0.32.2",
|
||||||
"@sei-js/core": "^1.3.2",
|
"@sei-js/core": "^1.3.2",
|
||||||
|
"@solana/spl-token": "^0.3.5",
|
||||||
"@solana/web3.js": "^1.22.0",
|
"@solana/web3.js": "^1.22.0",
|
||||||
"@terra-money/terra.js": "^3.1.3",
|
"@terra-money/terra.js": "^3.1.3",
|
||||||
"@types/config": "^3.3.0",
|
"@types/config": "^3.3.0",
|
||||||
|
|
|
@ -2,11 +2,16 @@ import {
|
||||||
_submitVAAAlgorand,
|
_submitVAAAlgorand,
|
||||||
signSendAndConfirmAlgorand,
|
signSendAndConfirmAlgorand,
|
||||||
} from "@certusone/wormhole-sdk/lib/esm/algorand";
|
} from "@certusone/wormhole-sdk/lib/esm/algorand";
|
||||||
import { CONTRACTS } from "@certusone/wormhole-sdk/lib/esm/utils/consts";
|
import {
|
||||||
|
CONTRACTS,
|
||||||
|
ChainName,
|
||||||
|
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
|
||||||
import { Account, Algodv2, mnemonicToSecretKey } from "algosdk";
|
import { Account, Algodv2, mnemonicToSecretKey } from "algosdk";
|
||||||
import { NETWORKS } from "./consts";
|
import { NETWORKS } from "./consts";
|
||||||
import { Network } from "./utils";
|
import { Network } from "./utils";
|
||||||
import { Payload, impossible } from "./vaa";
|
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";
|
||||||
|
|
||||||
export async function execute_algorand(
|
export async function execute_algorand(
|
||||||
payload: Payload,
|
payload: Payload,
|
||||||
|
@ -25,16 +30,6 @@ export async function execute_algorand(
|
||||||
|
|
||||||
const contracts = CONTRACTS[network][chainName];
|
const contracts = CONTRACTS[network][chainName];
|
||||||
console.log("contracts", contracts);
|
console.log("contracts", contracts);
|
||||||
const ALGORAND_HOST = {
|
|
||||||
algodToken: "",
|
|
||||||
algodServer: rpc,
|
|
||||||
algodPort: "",
|
|
||||||
};
|
|
||||||
if (network === "DEVNET") {
|
|
||||||
ALGORAND_HOST.algodToken =
|
|
||||||
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
|
|
||||||
ALGORAND_HOST.algodPort = "4001";
|
|
||||||
}
|
|
||||||
|
|
||||||
let target_contract: string;
|
let target_contract: string;
|
||||||
switch (payload.module) {
|
switch (payload.module) {
|
||||||
|
@ -127,11 +122,7 @@ export async function execute_algorand(
|
||||||
|
|
||||||
const target = BigInt(parseInt(target_contract));
|
const target = BigInt(parseInt(target_contract));
|
||||||
const CORE_ID = BigInt(parseInt(contracts.core));
|
const CORE_ID = BigInt(parseInt(contracts.core));
|
||||||
const algodClient = new Algodv2(
|
const algodClient = getClient(network, rpc);
|
||||||
ALGORAND_HOST.algodToken,
|
|
||||||
ALGORAND_HOST.algodServer,
|
|
||||||
ALGORAND_HOST.algodPort
|
|
||||||
);
|
|
||||||
const algoWallet: Account = mnemonicToSecretKey(key);
|
const algoWallet: Account = mnemonicToSecretKey(key);
|
||||||
|
|
||||||
// Create transaction
|
// Create transaction
|
||||||
|
@ -147,3 +138,59 @@ export async function execute_algorand(
|
||||||
const result = await signSendAndConfirmAlgorand(algodClient, txs, algoWallet);
|
const result = await signSendAndConfirmAlgorand(algodClient, txs, algoWallet);
|
||||||
console.log("Confirmed in round:", result["confirmed-round"]);
|
console.log("Confirmed in round:", result["confirmed-round"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function transferAlgorand(
|
||||||
|
dstChain: ChainName,
|
||||||
|
dstAddress: string,
|
||||||
|
tokenAddress: string,
|
||||||
|
amount: string,
|
||||||
|
network: Network,
|
||||||
|
rpc: string
|
||||||
|
) {
|
||||||
|
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);
|
||||||
|
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,
|
||||||
|
CORE_ID,
|
||||||
|
wallet.addr,
|
||||||
|
assetId,
|
||||||
|
BigInt(amount),
|
||||||
|
recipient,
|
||||||
|
dstChain,
|
||||||
|
BigInt(0)
|
||||||
|
);
|
||||||
|
const result = await signSendAndConfirmAlgorand(client, txs, wallet);
|
||||||
|
console.log("Confirmed in round:", result["confirmed-round"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getClient(network: Network, rpc: string) {
|
||||||
|
const ALGORAND_HOST = {
|
||||||
|
algodToken: "",
|
||||||
|
algodServer: rpc,
|
||||||
|
algodPort: "",
|
||||||
|
};
|
||||||
|
if (network === "DEVNET") {
|
||||||
|
ALGORAND_HOST.algodToken =
|
||||||
|
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
|
||||||
|
ALGORAND_HOST.algodPort = "4001";
|
||||||
|
}
|
||||||
|
const client = new Algodv2(
|
||||||
|
ALGORAND_HOST.algodToken,
|
||||||
|
ALGORAND_HOST.algodServer,
|
||||||
|
ALGORAND_HOST.algodPort
|
||||||
|
);
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import {
|
import {
|
||||||
CONTRACTS,
|
CONTRACTS,
|
||||||
ChainId,
|
ChainId,
|
||||||
|
ChainName,
|
||||||
assertChain,
|
assertChain,
|
||||||
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
|
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
|
||||||
import { AptosAccount, AptosClient, BCS, TxnBuilderTypes } from "aptos";
|
import { transferFromAptos } from "@certusone/wormhole-sdk/lib/esm/token_bridge/transfer";
|
||||||
|
import { AptosAccount, AptosClient, BCS, TxnBuilderTypes, Types } from "aptos";
|
||||||
import { ethers } from "ethers";
|
import { ethers } from "ethers";
|
||||||
import { sha3_256 } from "js-sha3";
|
import { sha3_256 } from "js-sha3";
|
||||||
import { NETWORKS } from "./consts";
|
import { NETWORKS } from "./consts";
|
||||||
|
@ -11,6 +13,10 @@ import { Network } from "./utils";
|
||||||
import { Payload, impossible } from "./vaa";
|
import { Payload, impossible } from "./vaa";
|
||||||
import { CHAINS, ensureHexPrefix } from "@certusone/wormhole-sdk";
|
import { CHAINS, ensureHexPrefix } from "@certusone/wormhole-sdk";
|
||||||
import { TokenBridgeState } from "@certusone/wormhole-sdk/lib/esm/aptos/types";
|
import { TokenBridgeState } from "@certusone/wormhole-sdk/lib/esm/aptos/types";
|
||||||
|
import {
|
||||||
|
generateSignAndSubmitEntryFunction,
|
||||||
|
tryNativeToUint8Array,
|
||||||
|
} from "@certusone/wormhole-sdk/lib/esm/utils";
|
||||||
|
|
||||||
export async function execute_aptos(
|
export async function execute_aptos(
|
||||||
payload: Payload,
|
payload: Payload,
|
||||||
|
@ -236,6 +242,44 @@ export async function execute_aptos(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function transferAptos(
|
||||||
|
dstChain: ChainName,
|
||||||
|
dstAddress: string,
|
||||||
|
tokenAddress: string,
|
||||||
|
amount: string,
|
||||||
|
network: Network,
|
||||||
|
rpc: string
|
||||||
|
) {
|
||||||
|
const { key } = NETWORKS[network].aptos;
|
||||||
|
if (!key) {
|
||||||
|
throw new Error("No key for aptos");
|
||||||
|
}
|
||||||
|
rpc = rpc ?? NETWORKS[network].aptos.rpc;
|
||||||
|
if (!rpc) {
|
||||||
|
throw new Error("No rpc for aptos");
|
||||||
|
}
|
||||||
|
const { token_bridge } = CONTRACTS[network].aptos;
|
||||||
|
if (!token_bridge) {
|
||||||
|
throw new Error("token bridge contract is undefined");
|
||||||
|
}
|
||||||
|
const account = new AptosAccount(new Uint8Array(Buffer.from(key, "hex")));
|
||||||
|
const client = new AptosClient(rpc);
|
||||||
|
const transferPayload = transferFromAptos(
|
||||||
|
token_bridge,
|
||||||
|
tokenAddress === "native" ? "0x1::aptos_coin::AptosCoin" : tokenAddress,
|
||||||
|
amount,
|
||||||
|
dstChain,
|
||||||
|
tryNativeToUint8Array(dstAddress, dstChain)
|
||||||
|
);
|
||||||
|
const tx = (await generateSignAndSubmitEntryFunction(
|
||||||
|
client,
|
||||||
|
account,
|
||||||
|
transferPayload
|
||||||
|
)) as Types.UserTransaction;
|
||||||
|
await client.waitForTransaction(tx.hash);
|
||||||
|
console.log(`hash: ${tx.hash}`);
|
||||||
|
}
|
||||||
|
|
||||||
export function deriveWrappedAssetAddress(
|
export function deriveWrappedAssetAddress(
|
||||||
token_bridge_address: Uint8Array, // 32 bytes
|
token_bridge_address: Uint8Array, // 32 bytes
|
||||||
origin_chain: ChainId,
|
origin_chain: ChainId,
|
||||||
|
|
|
@ -129,7 +129,7 @@ export const submit = async (
|
||||||
console.log(` Type ${getWrappedCoinType(coinPackageId)}`);
|
console.log(` Type ${getWrappedCoinType(coinPackageId)}`);
|
||||||
|
|
||||||
if (!rpc && network !== "DEVNET") {
|
if (!rpc && network !== "DEVNET") {
|
||||||
// Wait for wrapped asset creation to be propogated to other
|
// Wait for wrapped asset creation to be propagated to other
|
||||||
// nodes in case this complete registration call is load balanced
|
// nodes in case this complete registration call is load balanced
|
||||||
// to another node.
|
// to another node.
|
||||||
await sleep(5000);
|
await sleep(5000);
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
import { transferFromSui } from "@certusone/wormhole-sdk/lib/esm/token_bridge/transfer";
|
||||||
|
import {
|
||||||
|
executeTransactionBlock,
|
||||||
|
getProvider,
|
||||||
|
getSigner,
|
||||||
|
setMaxGasBudgetDevnet,
|
||||||
|
} from "./utils";
|
||||||
|
import {
|
||||||
|
CONTRACTS,
|
||||||
|
ChainName,
|
||||||
|
Network,
|
||||||
|
tryNativeToUint8Array,
|
||||||
|
} from "@certusone/wormhole-sdk/lib/esm/utils";
|
||||||
|
|
||||||
|
export async function transferSui(
|
||||||
|
dstChain: ChainName,
|
||||||
|
dstAddress: string,
|
||||||
|
tokenAddress: string,
|
||||||
|
amount: string,
|
||||||
|
network: Network,
|
||||||
|
rpc: string
|
||||||
|
) {
|
||||||
|
const { core, token_bridge } = CONTRACTS[network]["sui"];
|
||||||
|
if (!core) {
|
||||||
|
throw Error("Core bridge object ID is undefined");
|
||||||
|
}
|
||||||
|
if (!token_bridge) {
|
||||||
|
throw new Error("Token bridge object ID is undefined");
|
||||||
|
}
|
||||||
|
const provider = getProvider(network, rpc);
|
||||||
|
const signer = getSigner(provider, network);
|
||||||
|
const owner = await signer.getAddress();
|
||||||
|
const coinType = tokenAddress === "native" ? "0x2::sui::SUI" : tokenAddress;
|
||||||
|
const coins = (
|
||||||
|
await provider.getCoins({
|
||||||
|
owner,
|
||||||
|
coinType,
|
||||||
|
})
|
||||||
|
).data;
|
||||||
|
const tx = await transferFromSui(
|
||||||
|
provider,
|
||||||
|
core,
|
||||||
|
token_bridge,
|
||||||
|
coins,
|
||||||
|
coinType,
|
||||||
|
BigInt(amount),
|
||||||
|
dstChain,
|
||||||
|
tryNativeToUint8Array(dstAddress, dstChain)
|
||||||
|
);
|
||||||
|
setMaxGasBudgetDevnet(network, tx);
|
||||||
|
const result = await executeTransactionBlock(signer, tx);
|
||||||
|
console.log(JSON.stringify(result));
|
||||||
|
}
|
|
@ -0,0 +1,146 @@
|
||||||
|
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";
|
||||||
|
import { transferSolana } from "../solana";
|
||||||
|
import { transferAlgorand } from "../algorand";
|
||||||
|
import { transferNear } from "../near";
|
||||||
|
import { transferSui } from "../chains/sui/transfer";
|
||||||
|
import { transferAptos } from "../aptos";
|
||||||
|
|
||||||
|
export const command = "transfer";
|
||||||
|
export const desc = "Transfer a token";
|
||||||
|
export const builder = (y: typeof yargs) =>
|
||||||
|
y
|
||||||
|
.option("src-chain", {
|
||||||
|
describe: "source chain",
|
||||||
|
choices: CHAIN_NAME_CHOICES,
|
||||||
|
demandOption: true,
|
||||||
|
})
|
||||||
|
.option("dst-chain", {
|
||||||
|
describe: "destination chain",
|
||||||
|
choices: CHAIN_NAME_CHOICES,
|
||||||
|
demandOption: true,
|
||||||
|
})
|
||||||
|
.option("dst-addr", {
|
||||||
|
describe: "destination address",
|
||||||
|
type: "string",
|
||||||
|
demandOption: true,
|
||||||
|
})
|
||||||
|
.option("token-addr", {
|
||||||
|
describe: "token address",
|
||||||
|
type: "string",
|
||||||
|
default: "native",
|
||||||
|
defaultDescription: "native token",
|
||||||
|
demandOption: false,
|
||||||
|
})
|
||||||
|
.option("amount", {
|
||||||
|
describe: "token amount",
|
||||||
|
type: "string",
|
||||||
|
demandOption: true,
|
||||||
|
})
|
||||||
|
.option("network", NETWORK_OPTIONS)
|
||||||
|
.option("rpc", {
|
||||||
|
describe: "RPC endpoint",
|
||||||
|
type: "string",
|
||||||
|
demandOption: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
// TODO: support transfers to sei
|
||||||
|
if (dstChain === "sei") {
|
||||||
|
throw new Error("transfer to sei currently unsupported");
|
||||||
|
}
|
||||||
|
if (srcChain === dstChain) {
|
||||||
|
throw new Error("source and destination chains can't be the same");
|
||||||
|
}
|
||||||
|
const amount = argv.amount;
|
||||||
|
if (BigInt(amount) <= 0) {
|
||||||
|
throw new Error("amount must be greater than 0");
|
||||||
|
}
|
||||||
|
const tokenAddr = argv["token-addr"];
|
||||||
|
if (tokenAddr === "native" && isCosmWasmChain(srcChain)) {
|
||||||
|
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;
|
||||||
|
if (!rpc) {
|
||||||
|
throw new Error(`No ${network} rpc defined for ${srcChain}`);
|
||||||
|
}
|
||||||
|
if (isEVMChain(srcChain)) {
|
||||||
|
await transferEVM(
|
||||||
|
srcChain,
|
||||||
|
dstChain,
|
||||||
|
dstAddr,
|
||||||
|
tokenAddr,
|
||||||
|
amount,
|
||||||
|
network,
|
||||||
|
rpc
|
||||||
|
);
|
||||||
|
} else if (isTerraChain(srcChain)) {
|
||||||
|
await transferTerra(
|
||||||
|
srcChain,
|
||||||
|
dstChain,
|
||||||
|
dstAddr,
|
||||||
|
tokenAddr,
|
||||||
|
amount,
|
||||||
|
network,
|
||||||
|
rpc
|
||||||
|
);
|
||||||
|
} else if (srcChain === "solana" || srcChain === "pythnet") {
|
||||||
|
await transferSolana(
|
||||||
|
srcChain,
|
||||||
|
dstChain,
|
||||||
|
dstAddr,
|
||||||
|
tokenAddr,
|
||||||
|
amount,
|
||||||
|
network,
|
||||||
|
rpc
|
||||||
|
);
|
||||||
|
} else if (srcChain === "algorand") {
|
||||||
|
await transferAlgorand(dstChain, dstAddr, tokenAddr, amount, network, rpc);
|
||||||
|
} else if (srcChain === "near") {
|
||||||
|
await transferNear(dstChain, dstAddr, tokenAddr, amount, network, rpc);
|
||||||
|
} else if (srcChain === "injective") {
|
||||||
|
await transferInjective(dstChain, dstAddr, tokenAddr, amount, network, rpc);
|
||||||
|
} else if (srcChain === "xpla") {
|
||||||
|
await transferXpla(dstChain, dstAddr, tokenAddr, amount, network, rpc);
|
||||||
|
} else if (srcChain === "sei") {
|
||||||
|
throw new Error("sei is not supported yet");
|
||||||
|
} else if (srcChain === "osmosis") {
|
||||||
|
throw Error("OSMOSIS is not supported yet");
|
||||||
|
} else if (srcChain === "sui") {
|
||||||
|
await transferSui(dstChain, dstAddr, tokenAddr, amount, network, rpc);
|
||||||
|
} else if (srcChain === "aptos") {
|
||||||
|
await transferAptos(dstChain, dstAddr, tokenAddr, amount, network, rpc);
|
||||||
|
} else if (srcChain === "wormchain") {
|
||||||
|
throw Error("Wormchain is not supported yet");
|
||||||
|
} else if (srcChain === "btc") {
|
||||||
|
throw Error("btc 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);
|
||||||
|
}
|
||||||
|
};
|
|
@ -42,3 +42,7 @@ export const CHAIN_ID_OR_NAME_CHOICES = [
|
||||||
...Object.keys(CHAINS),
|
...Object.keys(CHAINS),
|
||||||
...Object.values(CHAINS),
|
...Object.values(CHAINS),
|
||||||
] as (ChainName | ChainId)[];
|
] as (ChainName | ChainId)[];
|
||||||
|
|
||||||
|
export const CHAIN_NAME_CHOICES = Object.keys(CHAINS).filter(
|
||||||
|
(c) => c !== "unset"
|
||||||
|
) as ChainName[];
|
||||||
|
|
|
@ -3,16 +3,16 @@ import {
|
||||||
BridgeImplementation__factory,
|
BridgeImplementation__factory,
|
||||||
Implementation__factory,
|
Implementation__factory,
|
||||||
NFTBridgeImplementation__factory,
|
NFTBridgeImplementation__factory,
|
||||||
WormholeRelayer__factory
|
WormholeRelayer__factory,
|
||||||
} from "@certusone/wormhole-sdk/lib/esm/ethers-contracts";
|
} from "@certusone/wormhole-sdk/lib/esm/ethers-contracts";
|
||||||
import {
|
import { getWormholeRelayerAddress } from "@certusone/wormhole-sdk/lib/esm/relayer";
|
||||||
getWormholeRelayerAddress
|
|
||||||
} from "@certusone/wormhole-sdk/lib/esm/relayer"
|
|
||||||
import {
|
import {
|
||||||
CHAINS,
|
CHAINS,
|
||||||
CONTRACTS,
|
CONTRACTS,
|
||||||
|
ChainName,
|
||||||
Contracts,
|
Contracts,
|
||||||
EVMChainName,
|
EVMChainName,
|
||||||
|
toChainId,
|
||||||
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
|
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { ethers } from "ethers";
|
import { ethers } from "ethers";
|
||||||
|
@ -20,6 +20,13 @@ import { solidityKeccak256 } from "ethers/lib/utils";
|
||||||
import { NETWORKS } from "./consts";
|
import { NETWORKS } from "./consts";
|
||||||
import { Network } from "./utils";
|
import { Network } from "./utils";
|
||||||
import { Encoding, Payload, encode, impossible, typeWidth } from "./vaa";
|
import { Encoding, Payload, encode, impossible, typeWidth } from "./vaa";
|
||||||
|
import {
|
||||||
|
approveEth,
|
||||||
|
getAllowanceEth,
|
||||||
|
transferFromEth,
|
||||||
|
transferFromEthNative,
|
||||||
|
} from "@certusone/wormhole-sdk/lib/esm/token_bridge/transfer";
|
||||||
|
import { tryNativeToUint8Array } from "@certusone/wormhole-sdk/lib/esm/utils";
|
||||||
|
|
||||||
const _IMPLEMENTATION_SLOT =
|
const _IMPLEMENTATION_SLOT =
|
||||||
"0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
|
"0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
|
||||||
|
@ -264,6 +271,39 @@ export async function getImplementation(
|
||||||
)[0];
|
)[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getSigner(chain: EVMChainName, key: string, rpc: string) {
|
||||||
|
let provider: ethers.providers.JsonRpcProvider;
|
||||||
|
let signer: ethers.Wallet;
|
||||||
|
if (chain === "celo") {
|
||||||
|
provider = new celo.CeloProvider(rpc);
|
||||||
|
await provider.ready;
|
||||||
|
signer = new celo.CeloWallet(key, provider);
|
||||||
|
} else {
|
||||||
|
provider = new ethers.providers.JsonRpcProvider(rpc);
|
||||||
|
signer = new ethers.Wallet(key, provider);
|
||||||
|
}
|
||||||
|
// Here we apply a set of chain-specific overrides.
|
||||||
|
// 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") {
|
||||||
|
overrides = await getKaruraGasParams(rpc);
|
||||||
|
} 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") {
|
||||||
|
overrides = { gasPrice: (await signer.getGasPrice()).toString() };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
signer,
|
||||||
|
provider,
|
||||||
|
overrides,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export async function execute_evm(
|
export async function execute_evm(
|
||||||
payload: Payload,
|
payload: Payload,
|
||||||
vaa: Buffer,
|
vaa: Buffer,
|
||||||
|
@ -284,32 +324,7 @@ export async function execute_evm(
|
||||||
|
|
||||||
const key: string = n.key;
|
const key: string = n.key;
|
||||||
const contracts: Contracts = CONTRACTS[network][chain];
|
const contracts: Contracts = CONTRACTS[network][chain];
|
||||||
let provider: ethers.providers.JsonRpcProvider;
|
const { signer, overrides } = await getSigner(chain, key, rpc);
|
||||||
let signer: ethers.Wallet;
|
|
||||||
if (chain === "celo") {
|
|
||||||
provider = new celo.CeloProvider(rpc);
|
|
||||||
await provider.ready;
|
|
||||||
signer = new celo.CeloWallet(key, provider);
|
|
||||||
} else {
|
|
||||||
provider = new ethers.providers.JsonRpcProvider(rpc);
|
|
||||||
signer = new ethers.Wallet(key, provider);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Here we apply a set of chain-specific overrides.
|
|
||||||
// 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") {
|
|
||||||
overrides = await getKaruraGasParams(rpc);
|
|
||||||
} 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") {
|
|
||||||
overrides = { gasPrice: (await signer.getGasPrice()).toString() };
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (payload.module) {
|
switch (payload.module) {
|
||||||
case "Core": {
|
case "Core": {
|
||||||
|
@ -434,38 +449,95 @@ export async function execute_evm(
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "WormholeRelayer":
|
case "WormholeRelayer":
|
||||||
contract_address = contract_address
|
contract_address = contract_address
|
||||||
? contract_address
|
? contract_address
|
||||||
: getWormholeRelayerAddress(chain, network);
|
: getWormholeRelayerAddress(chain, network);
|
||||||
if (contract_address === undefined) {
|
if (contract_address === undefined) {
|
||||||
throw Error(`Unknown Wormhole Relayer contract on ${network} for ${chain}`)
|
throw Error(
|
||||||
}
|
`Unknown Wormhole Relayer contract on ${network} for ${chain}`
|
||||||
let rb = WormholeRelayer__factory.connect(contract_address, signer)
|
);
|
||||||
switch (payload.type) {
|
}
|
||||||
case "ContractUpgrade":
|
let rb = WormholeRelayer__factory.connect(contract_address, signer);
|
||||||
console.log("Upgrading contract")
|
switch (payload.type) {
|
||||||
console.log("Hash: " + (await rb.submitContractUpgrade(vaa, overrides)).hash)
|
case "ContractUpgrade":
|
||||||
console.log("Don't forget to verify the new implementation! See ethereum/VERIFY.md for instructions")
|
console.log("Upgrading contract");
|
||||||
break
|
console.log(
|
||||||
case "RegisterChain":
|
"Hash: " + (await rb.submitContractUpgrade(vaa, overrides)).hash
|
||||||
console.log("Registering chain")
|
);
|
||||||
console.log("Hash: " + (await rb.registerWormholeRelayerContract(vaa, overrides)).hash)
|
console.log(
|
||||||
break
|
"Don't forget to verify the new implementation! See ethereum/VERIFY.md for instructions"
|
||||||
case "SetDefaultDeliveryProvider":
|
);
|
||||||
console.log("Setting default relay provider")
|
break;
|
||||||
console.log("Hash: " + (await rb.setDefaultDeliveryProvider(vaa, overrides)).hash)
|
case "RegisterChain":
|
||||||
break
|
console.log("Registering chain");
|
||||||
default:
|
console.log(
|
||||||
impossible(payload)
|
"Hash: " +
|
||||||
break
|
(await rb.registerWormholeRelayerContract(vaa, overrides)).hash
|
||||||
|
);
|
||||||
}
|
break;
|
||||||
break
|
case "SetDefaultDeliveryProvider":
|
||||||
|
console.log("Setting default relay provider");
|
||||||
|
console.log(
|
||||||
|
"Hash: " +
|
||||||
|
(await rb.setDefaultDeliveryProvider(vaa, overrides)).hash
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
impossible(payload);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
impossible(payload);
|
impossible(payload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function transferEVM(
|
||||||
|
srcChain: EVMChainName,
|
||||||
|
dstChain: ChainName,
|
||||||
|
dstAddress: string,
|
||||||
|
tokenAddress: string,
|
||||||
|
amount: string,
|
||||||
|
network: Network,
|
||||||
|
rpc: string
|
||||||
|
) {
|
||||||
|
const n = NETWORKS[network][srcChain];
|
||||||
|
if (!n.key) {
|
||||||
|
throw Error(`No ${network} key defined for ${srcChain} (see networks.ts)`);
|
||||||
|
}
|
||||||
|
const { token_bridge } = CONTRACTS[network][srcChain];
|
||||||
|
if (!token_bridge) {
|
||||||
|
throw Error(`Unknown token bridge contract on ${network} for ${srcChain}`);
|
||||||
|
}
|
||||||
|
const { signer, overrides } = await getSigner(srcChain, n.key, rpc);
|
||||||
|
let tx;
|
||||||
|
if (tokenAddress === "native") {
|
||||||
|
tx = await transferFromEthNative(
|
||||||
|
token_bridge,
|
||||||
|
signer,
|
||||||
|
amount,
|
||||||
|
toChainId(dstChain),
|
||||||
|
tryNativeToUint8Array(dstAddress, dstChain)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const allowance = await getAllowanceEth(token_bridge, tokenAddress, signer);
|
||||||
|
if (allowance.lt(amount)) {
|
||||||
|
await approveEth(token_bridge, tokenAddress, signer, amount, overrides);
|
||||||
|
}
|
||||||
|
tx = await transferFromEth(
|
||||||
|
token_bridge,
|
||||||
|
signer,
|
||||||
|
tokenAddress,
|
||||||
|
amount,
|
||||||
|
dstChain,
|
||||||
|
tryNativeToUint8Array(dstAddress, dstChain),
|
||||||
|
undefined,
|
||||||
|
overrides
|
||||||
|
);
|
||||||
|
}
|
||||||
|
console.log(`Hash: ${tx.transactionHash}`);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Hijack a core contract. This function is useful when working with a mainnet
|
* Hijack a core contract. This function is useful when working with a mainnet
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import {
|
import {
|
||||||
CHAINS,
|
CHAINS,
|
||||||
CONTRACTS,
|
CONTRACTS,
|
||||||
|
ChainName,
|
||||||
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
|
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
|
||||||
import {
|
import {
|
||||||
getNetworkInfo,
|
getNetworkInfo,
|
||||||
|
@ -11,6 +12,7 @@ import {
|
||||||
ChainRestAuthApi,
|
ChainRestAuthApi,
|
||||||
createTransaction,
|
createTransaction,
|
||||||
MsgExecuteContractCompat,
|
MsgExecuteContractCompat,
|
||||||
|
Msgs,
|
||||||
PrivateKey,
|
PrivateKey,
|
||||||
TxGrpcApi,
|
TxGrpcApi,
|
||||||
} from "@injectivelabs/sdk-ts";
|
} from "@injectivelabs/sdk-ts";
|
||||||
|
@ -19,6 +21,8 @@ import { fromUint8Array } from "js-base64";
|
||||||
import { NETWORKS } from "./consts";
|
import { NETWORKS } from "./consts";
|
||||||
import { Network } from "./utils";
|
import { Network } from "./utils";
|
||||||
import { impossible, Payload } from "./vaa";
|
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";
|
||||||
|
|
||||||
export async function execute_injective(
|
export async function execute_injective(
|
||||||
payload: Payload,
|
payload: Payload,
|
||||||
|
@ -157,11 +161,61 @@ export async function execute_injective(
|
||||||
});
|
});
|
||||||
console.log("transaction:", transaction);
|
console.log("transaction:", transaction);
|
||||||
|
|
||||||
|
await signAndSendTx(walletPK, network, transaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function transferInjective(
|
||||||
|
dstChain: ChainName,
|
||||||
|
dstAddress: string,
|
||||||
|
tokenAddress: string,
|
||||||
|
amount: string,
|
||||||
|
network: Network,
|
||||||
|
rpc: string
|
||||||
|
) {
|
||||||
|
if (network === "DEVNET") {
|
||||||
|
throw new Error("Injective is not supported in DEVNET");
|
||||||
|
}
|
||||||
|
const chain = "injective";
|
||||||
|
const { key } = NETWORKS[network][chain];
|
||||||
|
if (!key) {
|
||||||
|
throw Error(`No ${network} key defined for Injective`);
|
||||||
|
}
|
||||||
|
const { token_bridge } = CONTRACTS[network][chain];
|
||||||
|
if (token_bridge == undefined) {
|
||||||
|
throw Error(`Unknown token bridge contract on ${network} for ${chain}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const walletPK = PrivateKey.fromMnemonic(key);
|
||||||
|
const walletInjAddr = walletPK.toBech32();
|
||||||
|
|
||||||
|
const msgs = await transferFromInjective(
|
||||||
|
walletInjAddr,
|
||||||
|
token_bridge,
|
||||||
|
tokenAddress,
|
||||||
|
amount,
|
||||||
|
dstChain,
|
||||||
|
tryNativeToUint8Array(dstAddress, dstChain)
|
||||||
|
);
|
||||||
|
|
||||||
|
await signAndSendTx(walletPK, network, msgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function signAndSendTx(
|
||||||
|
walletPK: PrivateKey,
|
||||||
|
network: string,
|
||||||
|
msgs: Msgs | Msgs[]
|
||||||
|
) {
|
||||||
|
const endPoint =
|
||||||
|
network === "MAINNET"
|
||||||
|
? InjectiveNetwork.MainnetK8s
|
||||||
|
: InjectiveNetwork.TestnetK8s;
|
||||||
|
const networkInfo = getNetworkInfo(endPoint);
|
||||||
|
const walletPublicKey = walletPK.toPublicKey().toBase64();
|
||||||
const accountDetails = await new ChainRestAuthApi(
|
const accountDetails = await new ChainRestAuthApi(
|
||||||
networkInfo.rest
|
networkInfo.rest
|
||||||
).fetchAccount(walletInjAddr);
|
).fetchAccount(walletPK.toBech32());
|
||||||
const { signBytes, txRaw } = createTransaction({
|
const { signBytes, txRaw } = createTransaction({
|
||||||
message: transaction,
|
message: msgs,
|
||||||
memo: "",
|
memo: "",
|
||||||
fee: getStdFee((parseInt(DEFAULT_STD_FEE.gas, 10) * 2.5).toString()),
|
fee: getStdFee((parseInt(DEFAULT_STD_FEE.gas, 10) * 2.5).toString()),
|
||||||
pubKey: walletPublicKey,
|
pubKey: walletPublicKey,
|
||||||
|
|
|
@ -15,6 +15,7 @@ import * as parse from "./cmds/parse";
|
||||||
import * as recover from "./cmds/recover";
|
import * as recover from "./cmds/recover";
|
||||||
import * as submit from "./cmds/submit";
|
import * as submit from "./cmds/submit";
|
||||||
import * as sui from "./cmds/sui";
|
import * as sui from "./cmds/sui";
|
||||||
|
import * as transfer from "./cmds/transfer";
|
||||||
import * as verifyVaa from "./cmds/verifyVaa";
|
import * as verifyVaa from "./cmds/verifyVaa";
|
||||||
|
|
||||||
yargs(hideBin(process.argv))
|
yargs(hideBin(process.argv))
|
||||||
|
@ -30,6 +31,7 @@ yargs(hideBin(process.argv))
|
||||||
.command(recover)
|
.command(recover)
|
||||||
.command(submit)
|
.command(submit)
|
||||||
.command(sui)
|
.command(sui)
|
||||||
|
.command(transfer)
|
||||||
.command(verifyVaa)
|
.command(verifyVaa)
|
||||||
.strict()
|
.strict()
|
||||||
.demandCommand().argv;
|
.demandCommand().argv;
|
||||||
|
|
|
@ -1,10 +1,18 @@
|
||||||
import { CONTRACTS } from "@certusone/wormhole-sdk/lib/esm/utils/consts";
|
import {
|
||||||
|
ChainName,
|
||||||
|
CONTRACTS,
|
||||||
|
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
|
||||||
import BN from "bn.js";
|
import BN from "bn.js";
|
||||||
import { Account, connect, KeyPair } from "near-api-js";
|
import { Account, connect, KeyPair } from "near-api-js";
|
||||||
import { InMemoryKeyStore } from "near-api-js/lib/key_stores";
|
import { InMemoryKeyStore } from "near-api-js/lib/key_stores";
|
||||||
import { NETWORKS } from "./consts";
|
import { NETWORKS } from "./consts";
|
||||||
import { Network } from "./utils";
|
import { Network } from "./utils";
|
||||||
import { impossible, Payload } from "./vaa";
|
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";
|
||||||
|
|
||||||
export const execute_near = async (
|
export const execute_near = async (
|
||||||
payload: Payload,
|
payload: Payload,
|
||||||
|
@ -142,3 +150,62 @@ export const execute_near = async (
|
||||||
const txHash = result1.transaction.hash + ":" + result2.transaction.hash;
|
const txHash = result1.transaction.hash + ":" + result2.transaction.hash;
|
||||||
console.log("Hash: " + txHash);
|
console.log("Hash: " + txHash);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export async function transferNear(
|
||||||
|
dstChain: ChainName,
|
||||||
|
dstAddress: string,
|
||||||
|
tokenAddress: string,
|
||||||
|
amount: string,
|
||||||
|
network: Network,
|
||||||
|
rpc: string
|
||||||
|
) {
|
||||||
|
const { key, networkId, deployerAccount } = NETWORKS[network].near;
|
||||||
|
if (!key) {
|
||||||
|
throw Error(`No ${network} key defined for NEAR`);
|
||||||
|
}
|
||||||
|
const { core, token_bridge } = CONTRACTS[network].near;
|
||||||
|
if (core === undefined) {
|
||||||
|
throw Error(`Unknown core contract on ${network} for NEAR`);
|
||||||
|
}
|
||||||
|
if (token_bridge === undefined) {
|
||||||
|
throw Error(`Unknown token bridge contract on ${network} for NEAR`);
|
||||||
|
}
|
||||||
|
const keyStore = new InMemoryKeyStore();
|
||||||
|
keyStore.setKey(networkId, deployerAccount, KeyPair.fromString(key));
|
||||||
|
const near = await connect({
|
||||||
|
keyStore,
|
||||||
|
networkId,
|
||||||
|
nodeUrl: rpc,
|
||||||
|
headers: {},
|
||||||
|
});
|
||||||
|
const nearAccount = new Account(near.connection, deployerAccount);
|
||||||
|
if (tokenAddress === "native") {
|
||||||
|
const msg = await transferNearFromNear(
|
||||||
|
near.connection.provider,
|
||||||
|
core,
|
||||||
|
token_bridge,
|
||||||
|
BigInt(amount),
|
||||||
|
tryNativeToUint8Array(dstAddress, dstChain),
|
||||||
|
dstChain,
|
||||||
|
BigInt(0)
|
||||||
|
);
|
||||||
|
const result = await nearAccount.functionCall(msg);
|
||||||
|
console.log(result.transaction.hash);
|
||||||
|
} else {
|
||||||
|
const msgs = await transferTokenFromNear(
|
||||||
|
near.connection.provider,
|
||||||
|
nearAccount.accountId,
|
||||||
|
core,
|
||||||
|
token_bridge,
|
||||||
|
tokenAddress,
|
||||||
|
BigInt(amount),
|
||||||
|
tryNativeToUint8Array(dstAddress, dstChain),
|
||||||
|
dstChain,
|
||||||
|
BigInt(0)
|
||||||
|
);
|
||||||
|
for (const msg of msgs) {
|
||||||
|
const result = await nearAccount.functionCall(msg);
|
||||||
|
console.log(result.transaction.hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -19,15 +19,25 @@ import {
|
||||||
import {
|
import {
|
||||||
CHAINS,
|
CHAINS,
|
||||||
CONTRACTS,
|
CONTRACTS,
|
||||||
|
ChainName,
|
||||||
|
Network,
|
||||||
SolanaChainName,
|
SolanaChainName,
|
||||||
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
|
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
|
||||||
import * as web3s from "@solana/web3.js";
|
import * as web3s from "@solana/web3.js";
|
||||||
import base58 from "bs58";
|
import base58 from "bs58";
|
||||||
import { NETWORKS } from "./consts";
|
import { NETWORKS } from "./consts";
|
||||||
import { Payload, VAA, impossible } from "./vaa";
|
import { Payload, VAA, impossible } from "./vaa";
|
||||||
import { ChainName, hexToUint8Array } from "@certusone/wormhole-sdk";
|
|
||||||
import { getEmitterAddress } from "./emitter";
|
import { getEmitterAddress } from "./emitter";
|
||||||
import { Network } from "./utils";
|
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";
|
||||||
|
|
||||||
export async function execute_solana(
|
export async function execute_solana(
|
||||||
v: VAA<Payload>,
|
v: VAA<Payload>,
|
||||||
|
@ -217,6 +227,84 @@ export async function execute_solana(
|
||||||
console.log("SIGNATURE", signature);
|
console.log("SIGNATURE", signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function transferSolana(
|
||||||
|
srcChain: SolanaChainName,
|
||||||
|
dstChain: ChainName,
|
||||||
|
dstAddress: string,
|
||||||
|
tokenAddress: string,
|
||||||
|
amount: string,
|
||||||
|
network: Network,
|
||||||
|
rpc: string
|
||||||
|
) {
|
||||||
|
const { key } = NETWORKS[network][srcChain];
|
||||||
|
if (!key) {
|
||||||
|
throw Error(`No ${network} key defined for ${srcChain}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const connection = setupConnection(rpc);
|
||||||
|
const keypair = web3s.Keypair.fromSecretKey(base58.decode(key));
|
||||||
|
|
||||||
|
const { core, token_bridge } = CONTRACTS[network][srcChain];
|
||||||
|
if (!core) {
|
||||||
|
throw new Error(
|
||||||
|
`Core bridge address not defined for ${srcChain} ${network}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!token_bridge) {
|
||||||
|
throw new Error(
|
||||||
|
`Token bridge address not defined for ${srcChain} ${network}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const bridgeId = new web3s.PublicKey(core);
|
||||||
|
const tokenBridgeId = new web3s.PublicKey(token_bridge);
|
||||||
|
const payerAddress = keypair.publicKey.toString();
|
||||||
|
|
||||||
|
let transaction;
|
||||||
|
if (tokenAddress === "native") {
|
||||||
|
transaction = await transferNativeSol(
|
||||||
|
connection,
|
||||||
|
bridgeId,
|
||||||
|
tokenBridgeId,
|
||||||
|
payerAddress,
|
||||||
|
BigInt(amount),
|
||||||
|
tryNativeToUint8Array(dstAddress, dstChain),
|
||||||
|
dstChain
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// find the associated token account
|
||||||
|
const fromAddress = (
|
||||||
|
await getAssociatedTokenAddress(
|
||||||
|
new PublicKey(tokenAddress),
|
||||||
|
keypair.publicKey
|
||||||
|
)
|
||||||
|
).toString();
|
||||||
|
transaction = await transferFromSolana(
|
||||||
|
connection,
|
||||||
|
bridgeId,
|
||||||
|
tokenBridgeId,
|
||||||
|
payerAddress,
|
||||||
|
fromAddress,
|
||||||
|
tokenAddress, // mintAddress
|
||||||
|
BigInt(amount),
|
||||||
|
tryNativeToUint8Array(dstAddress, dstChain),
|
||||||
|
dstChain
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// sign, send, and confirm transaction
|
||||||
|
transaction.partialSign(keypair);
|
||||||
|
const signature = await connection.sendRawTransaction(
|
||||||
|
transaction.serialize()
|
||||||
|
);
|
||||||
|
await connection.confirmTransaction(signature);
|
||||||
|
const info = await connection.getTransaction(signature);
|
||||||
|
if (!info) {
|
||||||
|
throw new Error("An error occurred while fetching the transaction info");
|
||||||
|
}
|
||||||
|
console.log("SIGNATURE", signature);
|
||||||
|
}
|
||||||
|
|
||||||
const setupConnection = (rpc: string): web3s.Connection =>
|
const setupConnection = (rpc: string): web3s.Connection =>
|
||||||
new web3s.Connection(rpc, "confirmed");
|
new web3s.Connection(rpc, "confirmed");
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import {
|
import {
|
||||||
CHAINS,
|
CHAINS,
|
||||||
CONTRACTS,
|
CONTRACTS,
|
||||||
|
ChainName,
|
||||||
TerraChainName,
|
TerraChainName,
|
||||||
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
|
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
|
||||||
import {
|
import {
|
||||||
|
@ -9,12 +10,15 @@ import {
|
||||||
LCDClient,
|
LCDClient,
|
||||||
MnemonicKey,
|
MnemonicKey,
|
||||||
MsgExecuteContract,
|
MsgExecuteContract,
|
||||||
|
Wallet,
|
||||||
} from "@terra-money/terra.js";
|
} from "@terra-money/terra.js";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { fromUint8Array } from "js-base64";
|
import { fromUint8Array } from "js-base64";
|
||||||
import { NETWORKS } from "./consts";
|
import { NETWORKS } from "./consts";
|
||||||
import { Network } from "./utils";
|
import { Network } from "./utils";
|
||||||
import { Payload, impossible } from "./vaa";
|
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";
|
||||||
|
|
||||||
export async function execute_terra(
|
export async function execute_terra(
|
||||||
payload: Payload,
|
payload: Payload,
|
||||||
|
@ -152,6 +156,55 @@ export async function execute_terra(
|
||||||
{ uluna: 1000 }
|
{ uluna: 1000 }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await signAndSendTx(terra, wallet, [transaction]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function transferTerra(
|
||||||
|
srcChain: TerraChainName,
|
||||||
|
dstChain: ChainName,
|
||||||
|
dstAddress: string,
|
||||||
|
tokenAddress: string,
|
||||||
|
amount: string,
|
||||||
|
network: Network,
|
||||||
|
rpc: string
|
||||||
|
) {
|
||||||
|
const n = NETWORKS[network][srcChain];
|
||||||
|
if (!n.key) {
|
||||||
|
throw Error(`No ${network} key defined for ${srcChain} (see networks.ts)`);
|
||||||
|
}
|
||||||
|
const { token_bridge } = CONTRACTS[network][srcChain];
|
||||||
|
if (!token_bridge) {
|
||||||
|
throw Error(`Unknown token bridge contract on ${network} for ${srcChain}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const terra = new LCDClient({
|
||||||
|
URL: rpc,
|
||||||
|
chainID: n.chain_id,
|
||||||
|
isClassic: srcChain === "terra",
|
||||||
|
});
|
||||||
|
|
||||||
|
const wallet = terra.wallet(
|
||||||
|
new MnemonicKey({
|
||||||
|
mnemonic: n.key,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const msgs = await transferFromTerra(
|
||||||
|
wallet.key.accAddress,
|
||||||
|
token_bridge,
|
||||||
|
tokenAddress,
|
||||||
|
amount,
|
||||||
|
dstChain,
|
||||||
|
tryNativeToUint8Array(dstAddress, dstChain)
|
||||||
|
);
|
||||||
|
await signAndSendTx(terra, wallet, msgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function signAndSendTx(
|
||||||
|
terra: LCDClient,
|
||||||
|
wallet: Wallet,
|
||||||
|
msgs: MsgExecuteContract[]
|
||||||
|
) {
|
||||||
const feeDenoms = ["uluna"];
|
const feeDenoms = ["uluna"];
|
||||||
const gasPrices = await axios
|
const gasPrices = await axios
|
||||||
.get("https://terra-classic-fcd.publicnode.com/v1/txs/gas_prices")
|
.get("https://terra-classic-fcd.publicnode.com/v1/txs/gas_prices")
|
||||||
|
@ -164,7 +217,7 @@ export async function execute_terra(
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
msgs: [transaction],
|
msgs,
|
||||||
memo: "",
|
memo: "",
|
||||||
feeDenoms,
|
feeDenoms,
|
||||||
gasPrices,
|
gasPrices,
|
||||||
|
@ -173,7 +226,7 @@ export async function execute_terra(
|
||||||
|
|
||||||
return wallet
|
return wallet
|
||||||
.createAndSignTx({
|
.createAndSignTx({
|
||||||
msgs: [transaction],
|
msgs,
|
||||||
memo: "",
|
memo: "",
|
||||||
fee: new Fee(
|
fee: new Fee(
|
||||||
feeEstimate.gas_limit,
|
feeEstimate.gas_limit,
|
||||||
|
|
|
@ -1,15 +1,21 @@
|
||||||
import { CONTRACTS } from "@certusone/wormhole-sdk/lib/esm/utils/consts";
|
import {
|
||||||
|
CONTRACTS,
|
||||||
|
ChainName,
|
||||||
|
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
|
||||||
import {
|
import {
|
||||||
Coin,
|
Coin,
|
||||||
Fee,
|
Fee,
|
||||||
LCDClient,
|
LCDClient,
|
||||||
MnemonicKey,
|
MnemonicKey,
|
||||||
MsgExecuteContract,
|
MsgExecuteContract,
|
||||||
|
Wallet,
|
||||||
} from "@xpla/xpla.js";
|
} from "@xpla/xpla.js";
|
||||||
import { fromUint8Array } from "js-base64";
|
import { fromUint8Array } from "js-base64";
|
||||||
import { NETWORKS } from "./consts";
|
import { NETWORKS } from "./consts";
|
||||||
import { Network } from "./utils";
|
import { Network } from "./utils";
|
||||||
import { Payload, impossible } from "./vaa";
|
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";
|
||||||
|
|
||||||
export async function execute_xpla(
|
export async function execute_xpla(
|
||||||
payload: Payload,
|
payload: Payload,
|
||||||
|
@ -146,6 +152,50 @@ export async function execute_xpla(
|
||||||
{ axpla: "1700000000000000000" }
|
{ axpla: "1700000000000000000" }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await signAndSendTx(client, wallet, [transaction]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function transferXpla(
|
||||||
|
dstChain: ChainName,
|
||||||
|
dstAddress: string,
|
||||||
|
tokenAddress: string,
|
||||||
|
amount: string,
|
||||||
|
network: Network,
|
||||||
|
rpc: string
|
||||||
|
) {
|
||||||
|
const { key, chain_id } = NETWORKS[network].xpla;
|
||||||
|
if (!key) {
|
||||||
|
throw Error(`No ${network} key defined for XPLA`);
|
||||||
|
}
|
||||||
|
const { token_bridge } = CONTRACTS[network].xpla;
|
||||||
|
if (token_bridge == undefined) {
|
||||||
|
throw Error(`Unknown token bridge contract on ${network} for XPLA`);
|
||||||
|
}
|
||||||
|
const client = new LCDClient({
|
||||||
|
URL: rpc,
|
||||||
|
chainID: chain_id,
|
||||||
|
});
|
||||||
|
const wallet = client.wallet(
|
||||||
|
new MnemonicKey({
|
||||||
|
mnemonic: key,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
const msgs = transferFromXpla(
|
||||||
|
wallet.key.accAddress,
|
||||||
|
token_bridge,
|
||||||
|
tokenAddress,
|
||||||
|
amount,
|
||||||
|
dstChain,
|
||||||
|
tryNativeToUint8Array(dstAddress, dstChain)
|
||||||
|
);
|
||||||
|
await signAndSendTx(client, wallet, msgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function signAndSendTx(
|
||||||
|
client: LCDClient,
|
||||||
|
wallet: Wallet,
|
||||||
|
msgs: MsgExecuteContract[]
|
||||||
|
) {
|
||||||
const feeDenoms = ["axpla"];
|
const feeDenoms = ["axpla"];
|
||||||
// const gasPrices = await axios
|
// const gasPrices = await axios
|
||||||
// .get("https://dimension-lcd.xpla.dev/v1/txs/gas_prices")
|
// .get("https://dimension-lcd.xpla.dev/v1/txs/gas_prices")
|
||||||
|
@ -158,7 +208,7 @@ export async function execute_xpla(
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
msgs: [transaction],
|
msgs,
|
||||||
memo: "",
|
memo: "",
|
||||||
feeDenoms,
|
feeDenoms,
|
||||||
// gasPrices,
|
// gasPrices,
|
||||||
|
@ -167,7 +217,7 @@ export async function execute_xpla(
|
||||||
|
|
||||||
wallet
|
wallet
|
||||||
.createAndSignTx({
|
.createAndSignTx({
|
||||||
msgs: [transaction],
|
msgs,
|
||||||
memo: "",
|
memo: "",
|
||||||
fee: new Fee(
|
fee: new Fee(
|
||||||
feeEstimate.gas_limit,
|
feeEstimate.gas_limit,
|
||||||
|
|
|
@ -145,7 +145,7 @@ export async function transferFromEthNative(
|
||||||
tokenBridgeAddress: string,
|
tokenBridgeAddress: string,
|
||||||
signer: ethers.Signer,
|
signer: ethers.Signer,
|
||||||
amount: ethers.BigNumberish,
|
amount: ethers.BigNumberish,
|
||||||
recipientChain: ChainId | ChainId,
|
recipientChain: ChainId | ChainName,
|
||||||
recipientAddress: Uint8Array,
|
recipientAddress: Uint8Array,
|
||||||
relayerFee: ethers.BigNumberish = 0,
|
relayerFee: ethers.BigNumberish = 0,
|
||||||
overrides: PayableOverrides & { from?: string | Promise<string> } = {},
|
overrides: PayableOverrides & { from?: string | Promise<string> } = {},
|
||||||
|
|
Loading…
Reference in New Issue