clients/js: set strict = true in tsconfig

This commit is contained in:
heyitaki 2023-05-06 08:11:59 +00:00 committed by aki
parent 0cff646fed
commit 9d50254893
40 changed files with 1463 additions and 1246 deletions

View File

@ -45,6 +45,7 @@
"@truffle/hdwallet-provider": "^2.0.15",
"@types/bn.js": "^5.1.0",
"@types/bs58": "^4.0.1",
"@types/node-fetch": "^2.6.3",
"@types/yargs": "^17.0.2",
"copy-dir": "^1.3.0",
"typescript": "^4.6"
@ -3440,6 +3441,30 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.6.tgz",
"integrity": "sha512-EdxgKRXgYsNITy5mjjXjVE/CS8YENSdhiagGrLqjG0pvA2owgJ6i4l7wy/PFZGC0B1/H20lWKN7ONVDNYDZm7A=="
},
"node_modules/@types/node-fetch": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.3.tgz",
"integrity": "sha512-ETTL1mOEdq/sxUtgtOhKjyB2Irra4cjxksvcMUR5Zr4n+PxVhsCD9WS46oPbHL3et9Zde7CNRr+WUNlcHvsX+w==",
"dev": true,
"dependencies": {
"@types/node": "*",
"form-data": "^3.0.0"
}
},
"node_modules/@types/node-fetch/node_modules/form-data": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
"integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
"dev": true,
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/@types/pbkdf2": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.0.tgz",
@ -10753,6 +10778,29 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.6.tgz",
"integrity": "sha512-EdxgKRXgYsNITy5mjjXjVE/CS8YENSdhiagGrLqjG0pvA2owgJ6i4l7wy/PFZGC0B1/H20lWKN7ONVDNYDZm7A=="
},
"@types/node-fetch": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.3.tgz",
"integrity": "sha512-ETTL1mOEdq/sxUtgtOhKjyB2Irra4cjxksvcMUR5Zr4n+PxVhsCD9WS46oPbHL3et9Zde7CNRr+WUNlcHvsX+w==",
"dev": true,
"requires": {
"@types/node": "*",
"form-data": "^3.0.0"
},
"dependencies": {
"form-data": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
"integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
"dev": true,
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
}
}
}
},
"@types/pbkdf2": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.0.tgz",

View File

@ -59,6 +59,7 @@
"@truffle/hdwallet-provider": "^2.0.15",
"@types/bn.js": "^5.1.0",
"@types/bs58": "^4.0.1",
"@types/node-fetch": "^2.6.3",
"@types/yargs": "^17.0.2",
"copy-dir": "^1.3.0",
"typescript": "^4.6"

View File

@ -1,40 +1,42 @@
import { NETWORKS } from "./networks";
import { impossible, Payload } from "./vaa";
import { Account, Algodv2, mnemonicToSecretKey } from "algosdk";
import {
signSendAndConfirmAlgorand,
_submitVAAAlgorand,
signSendAndConfirmAlgorand,
} from "@certusone/wormhole-sdk/lib/esm/algorand";
import { CONTRACTS } from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import { Account, Algodv2, mnemonicToSecretKey } from "algosdk";
import { NETWORKS } from "./networks";
import { Network } from "./utils";
import { Payload, impossible } from "./vaa";
export async function execute_algorand(
payload: Payload,
vaa: Uint8Array,
environment: "MAINNET" | "TESTNET" | "DEVNET"
network: Network
) {
const chainName = "algorand";
let n = NETWORKS[environment][chainName];
if (!n.key) {
throw Error(`No ${environment} key defined for Algorand`);
const { key, rpc } = NETWORKS[network][chainName];
if (!key) {
throw Error(`No ${network} key defined for Algorand`);
}
if (!n.rpc) {
throw Error(`No ${environment} rpc defined for Algorand`);
if (!rpc) {
throw Error(`No ${network} rpc defined for Algorand`);
}
let contracts = CONTRACTS[environment][chainName];
const contracts = CONTRACTS[network][chainName];
console.log("contracts", contracts);
const ALGORAND_HOST = {
algodToken: "",
algodServer: n.rpc,
algodServer: rpc,
algodPort: "",
};
if (environment === "DEVNET") {
if (network === "DEVNET") {
ALGORAND_HOST.algodToken =
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
ALGORAND_HOST.algodPort = "4001";
}
let target_contract: string;
switch (payload.module) {
case "Core":
target_contract = contracts.core;
@ -99,22 +101,20 @@ export async function execute_algorand(
throw Error("Can't complete payload 3 transfer from CLI");
default:
impossible(payload);
break;
}
break;
default:
target_contract = impossible(payload);
}
const target = BigInt(parseInt(target_contract));
const CORE_ID = BigInt(parseInt(contracts.core));
const algodClient = new Algodv2(
ALGORAND_HOST.algodToken,
ALGORAND_HOST.algodServer,
ALGORAND_HOST.algodPort
);
const algoWallet: Account = mnemonicToSecretKey(n.key);
const algoWallet: Account = mnemonicToSecretKey(key);
// Create transaction
const txs = await _submitVAAAlgorand(
@ -124,6 +124,7 @@ export async function execute_algorand(
vaa,
algoWallet.addr
);
// Sign and send transaction
const result = await signSendAndConfirmAlgorand(algodClient, txs, algoWallet);
console.log("Confirmed in round:", result["confirmed-round"]);

View File

@ -1,18 +1,19 @@
import { AptosAccount, TxnBuilderTypes, AptosClient, BCS } from "aptos";
import { NETWORKS } from "./networks";
import { impossible, Payload } from "./vaa";
import { sha3_256 } from "js-sha3";
import { ethers } from "ethers";
import {
assertChain,
ChainId,
CONTRACTS,
ChainId,
assertChain,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import { AptosAccount, AptosClient, BCS, TxnBuilderTypes } from "aptos";
import { ethers } from "ethers";
import { sha3_256 } from "js-sha3";
import { NETWORKS } from "./networks";
import { Network } from "./utils";
import { Payload, impossible } from "./vaa";
export async function execute_aptos(
payload: Payload,
vaa: Buffer,
network: "MAINNET" | "TESTNET" | "DEVNET",
network: Network,
contract: string | undefined,
rpc: string | undefined
) {
@ -24,11 +25,12 @@ export async function execute_aptos(
const bcsVAA = serializer.getBytes();
switch (payload.module) {
case "Core":
case "Core": {
contract = contract ?? CONTRACTS[network][chain]["core"];
if (contract === undefined) {
throw Error("core bridge contract is undefined");
}
switch (payload.type) {
case "GuardianSetUpgrade":
console.log("Submitting new guardian set");
@ -57,12 +59,15 @@ export async function execute_aptos(
default:
impossible(payload);
}
break;
case "NFTBridge":
}
case "NFTBridge": {
contract = contract ?? CONTRACTS[network][chain]["nft_bridge"];
if (contract === undefined) {
throw Error("nft bridge contract is undefined");
}
switch (payload.type) {
case "ContractUpgrade":
console.log("Upgrading contract");
@ -103,12 +108,15 @@ export async function execute_aptos(
default:
impossible(payload);
}
break;
case "TokenBridge":
}
case "TokenBridge": {
contract = contract ?? CONTRACTS[network][chain]["token_bridge"];
if (contract === undefined) {
throw Error("token bridge contract is undefined");
}
switch (payload.type) {
case "ContractUpgrade":
console.log("Upgrading contract");
@ -215,9 +223,10 @@ export async function execute_aptos(
throw Error("Can't complete payload 3 transfer from CLI");
default:
impossible(payload);
}
break;
}
break;
default:
impossible(payload);
}
@ -250,7 +259,7 @@ export function deriveWrappedAssetAddress(
export function deriveResourceAccount(
deployer: Uint8Array, // 32 bytes
seed: string
) {
): string {
// from https://github.com/aptos-labs/aptos-core/blob/25696fd266498d81d346fe86e01c330705a71465/aptos-move/framework/aptos-framework/sources/account.move#L90-L95
let DERIVE_RESOURCE_ACCOUNT_SCHEME = Buffer.alloc(1);
DERIVE_RESOURCE_ACCOUNT_SCHEME.writeUInt8(255);
@ -270,7 +279,7 @@ export async function callEntryFunc(
func: string,
ty_args: BCS.Seq<TxnBuilderTypes.TypeTag>,
args: BCS.Seq<BCS.Bytes>
) {
): Promise<string> {
let key: string | undefined = NETWORKS[network]["aptos"].key;
if (key === undefined) {
throw new Error("No key for aptos");
@ -310,6 +319,7 @@ export async function callEntryFunc(
throw new Error(`Transaction failed: ${tx.vm_status}`);
}
});
// simulation successful... let's do it
const bcsTxn = AptosClient.generateBCSTransaction(accountFrom, rawTxn);
const transactionRes = await client.submitSignedBCSTransaction(bcsTxn);

View File

@ -1,3 +1,4 @@
import { APTOS_DEPLOYER_ADDRESS_DEVNET } from "@certusone/wormhole-sdk";
import {
assertChain,
CHAIN_ID_APTOS,
@ -23,9 +24,11 @@ import {
RPC_OPTIONS,
} from "../consts";
import { NETWORKS } from "../networks";
import { runCommand, validator_args } from "../start-validator";
import { runCommand, VALIDATOR_OPTIONS } from "../start-validator";
import { assertNetwork, checkBinary, evm_address, hex } from "../utils";
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";
@ -42,8 +45,7 @@ interface PackageBCS {
export const command = "aptos";
export const desc = "Aptos utilities";
export const builder = function (y: typeof yargs) {
return (
export const builder = (y: typeof yargs) =>
y
// NOTE: there's no init-nft-bridge, because the native module initialiser
// functionality has stabilised on mainnet, so we just use that one (which
@ -51,11 +53,8 @@ export const builder = function (y: typeof yargs) {
.command(
"init-token-bridge",
"Init token bridge contract",
(yargs) => {
return yargs
.option("network", NETWORK_OPTIONS)
.option("rpc", RPC_OPTIONS);
},
(yargs) =>
yargs.option("network", NETWORK_OPTIONS).option("rpc", RPC_OPTIONS),
async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
@ -76,35 +75,34 @@ export const builder = function (y: typeof yargs) {
.command(
"init-wormhole",
"Init Wormhole core contract",
(yargs) => {
return yargs
(yargs) =>
yargs
.option("network", NETWORK_OPTIONS)
.option("rpc", RPC_OPTIONS)
.option("chain-id", {
describe: "Chain id",
type: "number",
default: CHAIN_ID_APTOS,
required: false,
demandOption: false,
})
.option("governance-chain-id", {
describe: "Governance chain id",
type: "number",
default: GOVERNANCE_CHAIN,
required: false,
demandOption: false,
})
.option("governance-address", {
describe: "Governance address",
type: "string",
default: GOVERNANCE_EMITTER,
required: false,
demandOption: false,
})
.option("guardian-address", {
alias: "g",
required: true,
demandOption: true,
describe: "Initial guardian's addresses (CSV)",
type: "string",
});
},
}),
async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
@ -143,15 +141,16 @@ export const builder = function (y: typeof yargs) {
.command(
"deploy <package-dir>",
"Deploy an Aptos package",
(yargs) => {
return yargs
(yargs) =>
yargs
.positional("package-dir", {
type: "string",
describe: "Path to package directory",
demandOption: true,
})
.option("network", NETWORK_OPTIONS)
.option("rpc", RPC_OPTIONS)
.option("named-addresses", NAMED_ADDRESSES_OPTIONS);
},
.option("named-addresses", NAMED_ADDRESSES_OPTIONS),
async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
@ -173,18 +172,21 @@ export const builder = function (y: typeof yargs) {
.command(
"deploy-resource <seed> <package-dir>",
"Deploy an Aptos package using a resource account",
(yargs) => {
return yargs
(yargs) =>
yargs
.positional("seed", {
type: "string",
describe: "Seed for resource account",
demandOption: true,
})
.positional("package-dir", {
type: "string",
describe: "Path to package directory",
demandOption: true,
})
.option("network", NETWORK_OPTIONS)
.option("rpc", RPC_OPTIONS)
.option("named-addresses", NAMED_ADDRESSES_OPTIONS);
},
.option("named-addresses", NAMED_ADDRESSES_OPTIONS),
async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
@ -215,13 +217,14 @@ export const builder = function (y: typeof yargs) {
.command(
"send-example-message <message>",
"Send example message",
(yargs) => {
return yargs
(yargs) =>
yargs
.positional("message", {
type: "string",
describe: "Message to send",
demandOption: true,
})
.option("network", NETWORK_OPTIONS);
},
.option("network", NETWORK_OPTIONS),
async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
@ -246,15 +249,18 @@ export const builder = function (y: typeof yargs) {
.command(
"derive-resource-account <account> <seed>",
"Derive resource account address",
(yargs) => {
return yargs
(yargs) =>
yargs
.positional("account", {
type: "string",
describe: "Account address",
demandOption: true,
})
.positional("seed", {
type: "string",
});
},
describe: "Seed for resource account",
demandOption: true,
}),
async (argv) => {
console.log(
deriveResourceAccount(
@ -267,16 +273,19 @@ export const builder = function (y: typeof yargs) {
.command(
"derive-wrapped-address <chain> <origin-address>",
"Derive wrapped coin type",
(yargs) => {
return yargs
(yargs) =>
yargs
.positional("chain", {
type: "string",
describe: "Origin chain name",
demandOption: true,
})
.positional("origin-address", {
type: "string",
describe: "Address on origin chain",
demandOption: true,
})
.option("network", NETWORK_OPTIONS);
},
.option("network", NETWORK_OPTIONS),
async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
@ -290,27 +299,21 @@ export const builder = function (y: typeof yargs) {
"hex"
);
console.log(
deriveWrappedAssetAddress(
token_bridge_address,
chain,
origin_address
)
deriveWrappedAssetAddress(token_bridge_address, chain, origin_address)
);
}
)
.command(
"hash-contracts <package-dir>",
"Hash contract bytecodes for upgrade",
(yargs) => {
return yargs
.positional("seed", {
type: "string",
})
(yargs) =>
yargs
.positional("package-dir", {
type: "string",
describe: "Path to package directory",
demandOption: true,
})
.option("named-addresses", NAMED_ADDRESSES_OPTIONS);
},
.option("named-addresses", NAMED_ADDRESSES_OPTIONS),
(argv) => {
checkBinary("aptos", README_URL);
const p = buildPackage(argv["package-dir"], argv["named-addresses"]);
@ -321,25 +324,24 @@ export const builder = function (y: typeof yargs) {
.command(
"upgrade <package-dir>",
"Perform upgrade after VAA has been submitted",
(_yargs) => {
return (
(_yargs) =>
yargs
.positional("package-dir", {
type: "string",
describe: "Path to package directory",
demandOption: true,
})
// TODO(csongor): once the sdk has the addresses, just look that up
// based on the module
.option("contract-address", {
alias: "a",
required: true,
demandOption: true,
describe: "Address where the wormhole module is deployed",
type: "string",
})
.option("network", NETWORK_OPTIONS)
.option("rpc", RPC_OPTIONS)
.option("named-addresses", NAMED_ADDRESSES_OPTIONS)
);
},
.option("named-addresses", NAMED_ADDRESSES_OPTIONS),
async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
@ -363,21 +365,18 @@ export const builder = function (y: typeof yargs) {
.command(
"migrate",
"Perform migration after contract upgrade",
(_yargs) => {
return (
(_yargs) =>
yargs
// TODO(csongor): once the sdk has the addresses, just look that up
// based on the module
.option("contract-address", {
alias: "a",
required: true,
demandOption: true,
describe: "Address where the wormhole module is deployed",
type: "string",
})
.option("network", NETWORK_OPTIONS)
.option("rpc", RPC_OPTIONS)
);
},
.option("rpc", RPC_OPTIONS),
async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
@ -399,58 +398,46 @@ export const builder = function (y: typeof yargs) {
.command(
"faucet",
"Request money from the faucet for a given account",
(yargs) => {
return yargs
.option("rpc", RPC_OPTIONS)
(_yargs) =>
yargs
.option("rpc", {
alias: "r",
describe: "Override default rpc endpoint url",
type: "string",
demandOption: false,
default: APTOS_NODE_URL,
})
.option("faucet", {
alias: "f",
required: false,
demandOption: false,
describe: "Faucet url",
type: "string",
default: APTOS_FAUCET_URL,
})
.option("amount", {
alias: "m",
required: false,
demandOption: false,
describe: "Amount to request",
type: "number",
default: 40000000,
})
.option("account", {
alias: "a",
required: false,
demandOption: false,
describe: "Account to fund",
type: "string",
});
},
default: APTOS_DEPLOYER_ADDRESS_DEVNET,
}),
async (argv) => {
let NODE_URL = "http://0.0.0.0:8080/v1";
let FAUCET_URL = "http://0.0.0.0:8081";
let account =
"0x277fa055b6a73c42c0662d5236c65c864ccbf2d4abd21f174a30c8b786eab84b";
let amount = 40000000;
if (argv.faucet != undefined) {
FAUCET_URL = argv.faucet as string;
}
if (argv.rpc != undefined) {
NODE_URL = argv.rpc as string;
}
if (argv.amount != undefined) {
amount = argv.amount as number;
}
if (argv.account != undefined) {
account = argv.account as string;
}
const faucetClient = new FaucetClient(NODE_URL, FAUCET_URL);
await faucetClient.fundAccount(account, amount);
console.log(`Funded ${account} with ${amount} coins`);
const faucetClient = new FaucetClient(argv.rpc, argv.faucet);
await faucetClient.fundAccount(argv.account, argv.amount);
console.log(`Funded ${argv.account} with ${argv.amount} coins`);
}
)
.command(
"start-validator",
"Start a local aptos validator",
(yargs) => {
return yargs.option("validator-args", validator_args);
},
(yargs) => yargs.option("validator-args", VALIDATOR_OPTIONS),
(argv) => {
checkBinary("aptos", README_URL);
const cmd = `cd ${homedir()} && aptos node run-local-testnet --with-faucet --force-restart --assume-yes`;
@ -458,11 +445,10 @@ export const builder = function (y: typeof yargs) {
}
)
.strict()
.demandCommand()
);
};
.demandCommand();
export const handler = () => {};
function buildPackage(dir: string, addrs?: string): Package {
const buildPackage = (dir: string, addrs?: string): Package => {
const named_addresses = addrs ? ["--named-addresses", addrs] : [];
const aptos = spawnSync("aptos", [
"move",
@ -498,9 +484,9 @@ function buildPackage(dir: string, addrs?: string): Package {
(mod: string) => `${buildDir}/bytecode_modules/${mod.split("::")[1]}.mv`
),
};
}
};
function serializePackage(p: Package): PackageBCS {
const serializePackage = (p: Package): PackageBCS => {
const metaBytes = fs.readFileSync(p.meta_file);
const packageMetadataSerializer = new BCS.Serializer();
packageMetadataSerializer.serializeBytes(metaBytes);
@ -522,6 +508,4 @@ function serializePackage(p: Package): PackageBCS {
bytecodes: serializedModules,
codeHash,
};
}
export const handler = (argv) => {};
};

View File

@ -13,9 +13,10 @@ export const builder = (y: typeof yargs) => {
describe: "Chain to query",
type: "string",
choices: Object.keys(CHAINS),
});
demandOption: true,
} as const);
};
export const handler = (argv) => {
export const handler = (argv: Awaited<ReturnType<typeof builder>["argv"]>) => {
assertChain(argv["chain"]);
console.log(coalesceChainId(argv["chain"]));
};

View File

@ -5,59 +5,70 @@ import {
import yargs from "yargs";
import { CONTRACTS } from "../consts";
import { getEmitterAddress } from "../emitter";
import { assertNetwork } from "../utils";
import { impossible } from "../vaa";
export const command = "contract <network> <chain> <module>";
export const desc = "Print contract address";
export const builder = (y: typeof yargs) => {
return y
export const builder = (y: typeof yargs) =>
y
.positional("network", {
describe: "network",
type: "string",
describe: "Network",
choices: ["mainnet", "testnet", "devnet"],
})
demandOption: true,
} as const)
.positional("chain", {
describe: "Chain to query",
type: "string",
choices: Object.keys(CHAINS),
})
demandOption: true,
} as const)
.positional("module", {
describe: "Module to query",
type: "string",
choices: ["Core", "NFTBridge", "TokenBridge"],
})
demandOption: true,
} as const)
.option("emitter", {
alias: "e",
describe: "Print in emitter address format",
type: "boolean",
default: false,
required: false,
demandOption: false,
});
};
export const handler = async (argv) => {
export const handler = async (
argv: Awaited<ReturnType<typeof builder>["argv"]>
) => {
assertChain(argv["chain"]);
const network = argv.network.toUpperCase();
if (network !== "MAINNET" && network !== "TESTNET" && network !== "DEVNET") {
throw Error(`Unknown network: ${network}`);
}
let chain = argv["chain"];
let module = argv["module"] as "Core" | "NFTBridge" | "TokenBridge";
let addr = "";
assertNetwork(network);
const chain = argv["chain"];
const module = argv["module"];
let addr: string | undefined;
switch (module) {
case "Core":
addr = CONTRACTS[network][chain]["core"];
addr = CONTRACTS[network][chain].core;
break;
case "NFTBridge":
addr = CONTRACTS[network][chain]["nft_bridge"];
const addresses = CONTRACTS[network][chain];
if (!("nft_bridge" in addresses)) {
throw new Error(`NFTBridge not deployed on ${chain}`);
}
addr = addresses.nft_bridge;
break;
case "TokenBridge":
addr = CONTRACTS[network][chain]["token_bridge"];
addr = CONTRACTS[network][chain].token_bridge;
break;
default:
impossible(module);
}
if (!addr) {
throw new Error(`${module} not deployed on ${chain}`);
}
if (argv["emitter"]) {
addr = await getEmitterAddress(chain, addr);
}
console.log(addr);
};

View File

@ -1,26 +1,30 @@
import yargs from "yargs";
import {
CHAINS,
assertChain,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import yargs from "yargs";
import { getEmitterAddress } from "../emitter";
export const command = "convert-to-emitter <chain> <address-to-convert>";
export const desc = "Print address in emitter address format";
export const builder = (y: typeof yargs) => {
return y
export const builder = (y: typeof yargs) =>
y
.positional("chain", {
describe: "Chain to query",
type: "string",
choices: Object.keys(CHAINS),
})
demandOption: true,
} as const)
.positional("address-to-convert", {
describe: "Address to be converted to emitter address format",
type: "string",
demandOption: true,
});
};
export const handler = async (argv) => {
export const handler = async (
argv: Awaited<ReturnType<typeof builder>["argv"]>
) => {
assertChain(argv["chain"]);
let chain = argv["chain"];
console.log(await getEmitterAddress(chain, argv["address-to-convert"]));
console.log(
await getEmitterAddress(argv["chain"], argv["address-to-convert"])
);
};

View File

@ -23,23 +23,25 @@ import axios from "axios";
import { ethers } from "ethers";
import yargs from "yargs";
import { NETWORKS } from "../networks";
import { assertNetwork, Network } from "../utils";
import { parse, Payload, serialiseVAA, sign, Signature, VAA } from "../vaa";
export const command = "edit-vaa";
export const desc = "Edits or generates a VAA";
export const builder = (y: typeof yargs) => {
return y
export const builder = (y: typeof yargs) =>
y
.option("vaa", {
alias: "v",
describe: "vaa in hex format",
type: "string",
demandOption: true,
})
.option("network", {
alias: "n",
describe: "network",
type: "string",
choices: ["mainnet", "testnet", "devnet"],
})
demandOption: true,
} as const)
.option("guardian-set-index", {
alias: "gsi",
describe: "guardian set index",
@ -65,7 +67,7 @@ export const builder = (y: typeof yargs) => {
alias: "ec",
describe: "emitter chain id to be used in the vaa",
type: "number",
required: false,
demandOption: false,
})
.option("emitter-address", {
alias: "ea",
@ -102,21 +104,29 @@ export const builder = (y: typeof yargs) => {
describe: "Guardian's secret key",
type: "string",
});
};
export const handler = async (argv) => {
export const handler = async (
argv: Awaited<ReturnType<typeof builder>["argv"]>
) => {
const network = argv["network"].toUpperCase();
assertNetwork(network);
let numSigs = 0;
if (argv["signatures"]) {
numSigs += 1;
}
if (argv["wormscanfile"]) {
numSigs += 1;
}
if (argv["wormscanurl"]) {
numSigs += 1;
}
if (argv["guardian-secret"]) {
numSigs += 1;
}
if (numSigs > 1) {
throw new Error(
`may only specify one of "--signatures", "--wormscanfile", "--wormscanurl" or "--guardian-secret"`
@ -161,20 +171,17 @@ export const handler = async (argv) => {
}
if (argv["signatures"]) {
vaa.signatures = argv["signatures"].split(",");
vaa.signatures = argv["signatures"].split(",").map((s, i) => ({
signature: s,
guardianSetIndex: i,
}));
} else if (argv["wormscanfile"]) {
const wormscanData = require(argv["wormscanfile"]);
const guardianSet = await getGuardianSet(
argv["network"],
vaa.guardianSetIndex
);
const guardianSet = await getGuardianSet(network, vaa.guardianSetIndex);
vaa.signatures = await getSigsFromWormscanData(wormscanData, guardianSet);
} else if (argv["wormscanurl"]) {
const wormscanData = await axios.get(argv["wormscanurl"]);
const guardianSet = await getGuardianSet(
argv["network"],
vaa.guardianSetIndex
);
const guardianSet = await getGuardianSet(network, vaa.guardianSetIndex);
vaa.signatures = await getSigsFromWormscanData(
wormscanData.data,
guardianSet
@ -219,19 +226,10 @@ export const handler = async (argv) => {
};
// getGuardianSet queries the core contract on Ethereum for the guardian set and returns it.
async function getGuardianSet(
nwork: string,
const getGuardianSet = async (
network: Network,
guardianSetIndex: number
): Promise<string[]> {
if (!nwork) {
throw Error(`"--network" is required to read guardian set`);
}
const network = nwork.toUpperCase();
if (network !== "MAINNET" && network !== "TESTNET" && network !== "DEVNET") {
throw Error(`Unknown network: ${network}`);
}
): Promise<string[]> => {
let n = NETWORKS[network]["ethereum"];
let contract_address = CONTRACTS[network]["ethereum"].core;
if (contract_address === undefined) {
@ -242,14 +240,14 @@ async function getGuardianSet(
const contract = Implementation__factory.connect(contract_address, provider);
const result = await contract.getGuardianSet(guardianSetIndex);
return result[0];
}
};
// getSigsFromWormscanData reads the guardian address / signature pairs from the wormscan data
// and generates an array of signature objects. It then sorts them into order by address.
function getSigsFromWormscanData(
const getSigsFromWormscanData = (
wormscanData: any,
guardianSet: string[]
): any {
): Signature[] => {
let sigs: Signature[] = [];
for (let data in wormscanData) {
let guardianAddr = wormscanData[data].guardianAddr;
@ -272,6 +270,7 @@ function getSigsFromWormscanData(
sigs.push(sig);
}
return sigs.sort((s1, s2) => {
if (s1.guardianSetIndex > s2.guardianSetIndex) {
return 1;
@ -283,4 +282,4 @@ function getSigsFromWormscanData(
return 0;
});
}
};

View File

@ -16,8 +16,8 @@ import {
setStorageAt,
} from "../evm";
import { NETWORKS } from "../networks";
import { runCommand, validator_args } from "../start-validator";
import { evm_address } from "../utils";
import { runCommand, VALIDATOR_OPTIONS } from "../start-validator";
import { assertNetwork, evm_address } from "../utils";
export const command = "evm";
export const desc = "EVM utilities";
@ -26,17 +26,17 @@ export const builder = function (y: typeof yargs) {
.option("rpc", {
describe: "RPC endpoint",
type: "string",
required: false,
demandOption: false,
})
.command(
"address-from-secret <secret>",
"Compute a 20 byte eth address from a 32 byte private key",
(yargs) => {
return yargs.positional("secret", {
(yargs) =>
yargs.positional("secret", {
type: "string",
describe: "Secret key (32 bytes)",
});
},
demandOption: true,
} as const),
(argv) => {
console.log(ethers.utils.computeAddress(argv["secret"]));
}
@ -44,28 +44,31 @@ export const builder = function (y: typeof yargs) {
.command(
"storage-update",
"Update a storage slot on an EVM fork during testing (anvil or hardhat)",
(yargs) => {
return yargs
(yargs) =>
yargs
.option("contract-address", {
alias: "a",
describe: "Contract address",
type: "string",
required: true,
demandOption: true,
})
.option("storage-slot", {
alias: "k",
describe: "Storage slot to modify",
type: "string",
required: true,
demandOption: true,
})
.option("value", {
alias: "v",
describe: "Value to write into the slot (32 bytes)",
type: "string",
required: true,
});
},
demandOption: true,
}),
async (argv) => {
if (!argv["rpc"]) {
throw new Error("RPC required");
}
const result = await setStorageAt(
argv["rpc"],
evm_address(argv["contract-address"]),
@ -87,56 +90,49 @@ export const builder = function (y: typeof yargs) {
.command(
"info",
"Query info about the on-chain state of the contract",
(yargs) => {
return yargs
(yargs) =>
yargs
.option("chain", {
alias: "c",
describe: "Chain to query",
type: "string",
choices: Object.keys(CHAINS),
required: true,
demandOption: true,
})
.option("module", {
alias: "m",
describe: "Module to query",
type: "string",
choices: ["Core", "NFTBridge", "TokenBridge"],
required: true,
demandOption: true,
})
.option("network", {
alias: "n",
describe: "network",
type: "string",
choices: ["mainnet", "testnet", "devnet"],
required: true,
demandOption: true,
})
.option("contract-address", {
alias: "a",
describe: "Contract to query (override config)",
type: "string",
required: false,
demandOption: false,
})
.option("implementation-only", {
alias: "i",
describe: "Only query implementation (faster)",
type: "boolean",
default: false,
required: false,
});
},
demandOption: false,
}),
async (argv) => {
assertChain(argv["chain"]);
assertEVMChain(argv["chain"]);
const network = argv.network.toUpperCase();
if (
network !== "MAINNET" &&
network !== "TESTNET" &&
network !== "DEVNET"
) {
throw Error(`Unknown network: ${network}`);
}
let module = argv["module"] as "Core" | "NFTBridge" | "TokenBridge";
let rpc = argv["rpc"] ?? NETWORKS[network][argv["chain"]].rpc;
assertNetwork(network);
const module = argv["module"] as "Core" | "NFTBridge" | "TokenBridge";
const rpc = argv["rpc"] ?? NETWORKS[network][argv["chain"]].rpc;
if (argv["implementation-only"]) {
console.log(
await getImplementation(
@ -167,8 +163,8 @@ export const builder = function (y: typeof yargs) {
.command(
"hijack",
"Override the guardian set of the core bridge contract during testing (anvil or hardhat)",
(yargs) => {
return yargs
(yargs) =>
yargs
.option("core-contract-address", {
alias: "a",
describe: "Core contract address",
@ -177,18 +173,17 @@ export const builder = function (y: typeof yargs) {
})
.option("guardian-address", {
alias: "g",
required: true,
demandOption: true,
describe: "Guardians' public addresses (CSV)",
type: "string",
})
.option("guardian-set-index", {
alias: "i",
required: false,
demandOption: false,
describe:
"New guardian set index (if unspecified, default to overriding the current index)",
type: "number",
});
},
}),
async (argv) => {
const guardian_addresses = argv["guardian-address"].split(",");
let rpc = argv["rpc"] ?? NETWORKS.DEVNET.ethereum.rpc;
@ -203,9 +198,7 @@ export const builder = function (y: typeof yargs) {
.command(
"start-validator",
"Start a local EVM validator",
(yargs) => {
return yargs.option("validator-args", validator_args);
},
(yargs) => yargs.option("validator-args", VALIDATOR_OPTIONS),
(argv) => {
const cmd = `cd ${homedir()} && npx ganache-cli -e 10000 --deterministic --time="1970-01-01T00:00:00+00:00"`;
runCommand(cmd, argv["validator-args"]);
@ -214,5 +207,4 @@ export const builder = function (y: typeof yargs) {
.strict()
.demandCommand();
};
export const handler = (argv) => {};
export const handler = () => {};

View File

@ -54,7 +54,7 @@ export const builder = function (y: typeof yargs) {
y
.option("guardian-secret", {
alias: "g",
required: true,
demandOption: true,
describe: "Guardians' secret keys (CSV)",
type: "string",
})
@ -62,33 +62,30 @@ export const builder = function (y: typeof yargs) {
.command(
"registration",
"Generate registration VAA",
(yargs) => {
return yargs
(yargs) =>
yargs
.option("chain", {
alias: "c",
describe: "Chain to register",
type: "string",
choices: Object.keys(CHAINS),
required: true,
})
demandOption: true,
} as const)
.option("contract-address", {
alias: "a",
describe: "Contract to register",
type: "string",
required: true,
demandOption: true,
})
.option("module", {
alias: "m",
describe: "Module to upgrade",
type: "string",
choices: ["NFTBridge", "TokenBridge"],
required: true,
});
},
demandOption: true,
} as const),
(argv) => {
let module = argv["module"] as "NFTBridge" | "TokenBridge";
const module = argv["module"];
assertChain(argv["chain"]);
let payload: PortalRegisterChain<typeof module> = {
const payload: PortalRegisterChain<typeof module> = {
module,
type: "RegisterChain",
chain: 0,
@ -98,115 +95,110 @@ export const builder = function (y: typeof yargs) {
argv["contract-address"]
),
};
let v = makeVAA(
const vaa = makeVAA(
GOVERNANCE_CHAIN,
GOVERNANCE_EMITTER,
argv["guardian-secret"].split(","),
payload
);
console.log(serialiseVAA(v));
console.log(serialiseVAA(vaa));
}
)
// Upgrade
.command(
"upgrade",
"Generate contract upgrade VAA",
(yargs) => {
return yargs
(yargs) =>
yargs
.option("chain", {
alias: "c",
describe: "Chain to upgrade",
type: "string",
choices: Object.keys(CHAINS),
required: true,
})
demandOption: true,
} as const)
.option("contract-address", {
alias: "a",
describe: "Contract to upgrade to",
type: "string",
required: true,
demandOption: true,
})
.option("module", {
alias: "m",
describe: "Module to upgrade",
type: "string",
choices: ["Core", "NFTBridge", "TokenBridge"],
required: true,
});
},
demandOption: true,
} as const),
(argv) => {
assertChain(argv["chain"]);
let module = argv["module"] as "Core" | "NFTBridge" | "TokenBridge";
let payload: ContractUpgrade = {
const module = argv["module"];
const payload: ContractUpgrade = {
module,
type: "ContractUpgrade",
chain: toChainId(argv["chain"]),
address: parseCodeAddress(argv["chain"], argv["contract-address"]),
};
let v = makeVAA(
const vaa = makeVAA(
GOVERNANCE_CHAIN,
GOVERNANCE_EMITTER,
argv["guardian-secret"].split(","),
payload
);
console.log(serialiseVAA(v));
console.log(serialiseVAA(vaa));
}
)
.command(
"attestation",
"Generate a token attestation VAA",
(yargs) => {
return yargs
(yargs) =>
yargs
.option("emitter-chain", {
alias: "e",
describe: "Emitter chain of the VAA",
type: "string",
choices: Object.keys(CHAINS),
required: true,
})
demandOption: true,
} as const)
.option("emitter-address", {
alias: "f",
describe: "Emitter address of the VAA",
type: "string",
required: true,
demandOption: true,
})
.option("chain", {
alias: "c",
describe: "Token's chain",
type: "string",
choices: Object.keys(CHAINS),
required: true,
demandOption: true,
})
.option("token-address", {
alias: "a",
describe: "Token's address",
type: "string",
required: true,
demandOption: true,
})
.option("decimals", {
alias: "d",
describe: "Token's decimals",
type: "number",
required: true,
demandOption: true,
})
.option("symbol", {
alias: "s",
describe: "Token's symbol",
type: "string",
required: true,
demandOption: true,
})
.option("name", {
alias: "n",
describe: "Token's name",
type: "string",
required: true,
});
},
demandOption: true,
}),
(argv) => {
let emitter_chain = argv["emitter-chain"] as string;
const emitter_chain = argv["emitter-chain"];
assertChain(argv["chain"]);
assertChain(emitter_chain);
let payload: TokenBridgeAttestMeta = {
const payload: TokenBridgeAttestMeta = {
module: "TokenBridge",
type: "AttestMeta",
chain: 0,
@ -216,60 +208,59 @@ export const builder = function (y: typeof yargs) {
symbol: argv["symbol"],
name: argv["name"],
};
let v = makeVAA(
const vaa = makeVAA(
toChainId(emitter_chain),
parseAddress(emitter_chain, argv["emitter-address"] as string),
parseAddress(emitter_chain, argv["emitter-address"]),
argv["guardian-secret"].split(","),
payload
);
console.log(serialiseVAA(v));
console.log(serialiseVAA(vaa));
}
)
// RecoverChainId
.command(
"recover-chain-id",
"Generate a recover chain ID VAA",
(yargs) => {
return yargs
(yargs) =>
yargs
.option("module", {
alias: "m",
describe: "Module to upgrade",
type: "string",
choices: ["Core", "NFTBridge", "TokenBridge"],
required: true,
})
demandOption: true,
} as const)
.option("evm-chain-id", {
alias: "e",
describe: "EVM chain ID to set",
type: "string",
required: true,
demandOption: true,
})
.option("new-chain-id", {
alias: "c",
describe: "New chain ID to set",
type: "number",
required: true,
});
},
demandOption: true,
}),
(argv) => {
let module = argv["module"] as "Core" | "NFTBridge" | "TokenBridge";
let payload: RecoverChainId = {
const module = argv["module"];
const payload: RecoverChainId = {
module,
type: "RecoverChainId",
evmChainId: BigInt(argv["evm-chain-id"]),
newChainId: argv["new-chain-id"],
};
let v = makeVAA(
const vaa = makeVAA(
GOVERNANCE_CHAIN,
GOVERNANCE_EMITTER,
argv["guardian-secret"].split(","),
payload
);
console.log(serialiseVAA(v));
console.log(serialiseVAA(vaa));
}
)
);
};
export const handler = () => {};
function parseAddress(chain: ChainName, address: string): string {
if (chain === "unset") {
@ -311,5 +302,3 @@ function parseCodeAddress(chain: ChainName, address: string): string {
return parseAddress(chain, address);
}
}
export const handler = (argv) => {};

View File

@ -13,5 +13,4 @@ export const builder = (y: typeof yargs) =>
.command(contractAddress)
.command(convertToEmitter)
.command(rpc);
export const handler = (argv) => {};
export const handler = () => {};

View File

@ -1,5 +1,12 @@
import BN from "bn.js";
import { readFileSync } from "fs";
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 { deploy_near, upgrade_near } from "../near";
import { CONTRACTS, NETWORK_OPTIONS, RPC_OPTIONS } from "../consts";
import { NETWORKS } from "../networks";
import { assertNetwork } from "../utils";
// Near utilities
export const command = "near";
@ -9,68 +16,188 @@ export const builder = function (y: typeof yargs) {
.option("module", {
alias: "m",
describe: "Module to query",
type: "string",
choices: ["Core", "NFTBridge", "TokenBridge"],
required: false,
})
.option("network", {
alias: "n",
describe: "network",
type: "string",
choices: ["mainnet", "testnet", "devnet"],
required: true,
})
demandOption: false,
} as const)
.option("network", NETWORK_OPTIONS)
.option("account", {
describe: "near deployment account",
describe: "Near deployment account",
type: "string",
required: true,
demandOption: true,
})
.option("attach", {
describe: "attach some near",
describe: "Attach some near",
type: "string",
required: false,
demandOption: false,
})
.option("target", {
describe: "near account to upgrade",
describe: "Near account to upgrade",
type: "string",
required: false,
demandOption: false,
})
.option("mnemonic", {
describe: "near private keys",
describe: "Near private keys",
type: "string",
required: false,
demandOption: false,
})
.option("keys", {
describe: "near private keys",
.option("key", {
describe: "Near private key",
type: "string",
required: false,
demandOption: false,
})
.option("rpc", RPC_OPTIONS)
.command(
"contract-update <file>",
"Submit a contract update using our specific APIs",
(yargs) => {
return yargs.positional("file", {
(yargs) =>
yargs.positional("file", {
type: "string",
describe: "wasm",
});
},
demandOption: true,
}),
async (argv) => {
await upgrade_near(argv);
const network = argv.network.toUpperCase();
assertNetwork(network);
const contracts = CONTRACTS[network].near;
const {
rpc: defaultRpc,
key: defaultKey,
networkId,
} = NETWORKS[network].near;
const key =
argv.key ??
(argv.mnemonic && parseSeedPhrase(argv.mnemonic).secretKey) ??
defaultKey;
if (!key) {
throw Error(`No ${network} key defined for NEAR`);
}
const rpc = argv.rpc ?? defaultRpc;
if (!rpc) {
throw Error(`No ${network} rpc defined for NEAR`);
}
let target = argv.target;
if (!argv.target && argv.module) {
if (argv.module === "Core") {
target = contracts.core;
console.log("Setting target to core");
}
if (argv.module === "TokenBridge") {
target = contracts.token_bridge;
console.log("Setting target to token_bridge");
}
}
if (!target) {
throw Error(`No target defined for NEAR`);
}
const masterKey = KeyPair.fromString(key);
const keyStore = new InMemoryKeyStore();
keyStore.setKey(networkId, argv["account"], masterKey);
const near = await connect({
deps: {
keyStore,
},
networkId,
nodeUrl: rpc,
headers: {},
});
const masterAccount = new Account(near.connection, argv["account"]);
const result = await masterAccount.functionCall({
contractId: target,
methodName: "update_contract",
args: readFileSync(argv["file"]),
attachedDeposit: new BN("22797900000000000000000000"),
gas: new BN("300000000000000"),
});
console.log(result);
}
)
.command(
"deploy <file>",
"Submit a contract update using near APIs",
(yargs) => {
return yargs.positional("file", {
(yargs) =>
yargs.positional("file", {
type: "string",
describe: "wasm",
});
},
demandOption: true,
}),
async (argv) => {
await deploy_near(argv);
const network = argv.network.toUpperCase();
assertNetwork(network);
const contracts = CONTRACTS[network].near;
const {
rpc: defaultRpc,
key: defaultKey,
networkId,
} = NETWORKS[network].near;
const key =
argv.key ??
(argv.mnemonic && parseSeedPhrase(argv.mnemonic).secretKey) ??
defaultKey;
if (!key) {
throw Error(`No ${network} key defined for NEAR`);
}
const rpc = argv.rpc ?? defaultRpc;
if (!rpc) {
throw Error(`No ${network} rpc defined for NEAR`);
}
let target = argv.target;
if (!argv.target && argv.module) {
if (argv.module === "Core") {
target = contracts.core;
console.log("Setting target to core");
}
if (argv.module === "TokenBridge") {
target = contracts.token_bridge;
console.log("Setting target to token_bridge");
}
}
if (!target) {
throw Error(`No target defined for NEAR`);
}
const masterKey = KeyPair.fromString(key);
const keyStore = new InMemoryKeyStore();
keyStore.setKey(networkId, argv["account"], masterKey);
keyStore.setKey(networkId, target, masterKey);
const near = await connect({
deps: {
keyStore,
},
networkId: networkId,
nodeUrl: rpc,
headers: {},
});
const masterAccount = new Account(near.connection, argv["account"]);
const targetAccount = new Account(near.connection, target);
console.log({ ...argv, key, rpc, target });
if (argv.attach) {
console.log(
`Sending money: ${target} from ${argv["account"]} being sent ${argv["attach"]}`
);
console.log(
await masterAccount.sendMoney(target, new BN(argv.attach))
);
}
console.log("deploying contract");
console.log(
await targetAccount.deployContract(readFileSync(argv["file"]))
);
}
);
};
export const handler = (argv) => {};
export const handler = () => {};

View File

@ -7,9 +7,10 @@ export const builder = (y: typeof yargs) => {
return y.positional("vaa", {
describe: "vaa",
type: "string",
demandOption: true,
});
};
export const handler = (argv) => {
export const handler = (argv: Awaited<ReturnType<typeof builder>["argv"]>) => {
let buf: Buffer;
try {
buf = Buffer.from(String(argv.vaa), "hex");
@ -22,8 +23,9 @@ export const handler = (argv) => {
throw Error("Couldn't parse VAA as base64 or hex");
}
}
const parsed_vaa = parse(buf);
let parsed_vaa_with_digest = parsed_vaa;
parsed_vaa_with_digest["digest"] = vaaDigest(parsed_vaa);
console.log(JSON.stringify(parsed_vaa_with_digest, null, 2));
const parsedVaa = parse(buf);
console.log(
JSON.stringify({ ...parsedVaa, digest: vaaDigest(parsedVaa) }, null, 2)
);
};

View File

@ -4,18 +4,22 @@ import { hex } from "../utils";
export const command = "recover <digest> <signature>";
export const desc = "Recover an address from a signature";
export const builder = (y: typeof yargs) => {
return y
export const builder = (y: typeof yargs) =>
y
.positional("digest", {
describe: "digest",
type: "string",
demandOption: true,
})
.positional("signature", {
describe: "signature",
type: "string",
demandOption: true,
});
};
export const handler = async (argv) => {
export const handler = async (
argv: Awaited<ReturnType<typeof builder>["argv"]>
) => {
console.log(
ethers.utils.recoverAddress(hex(argv["digest"]), hex(argv["signature"]))
);

View File

@ -4,27 +4,27 @@ import {
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import yargs from "yargs";
import { NETWORKS } from "../networks";
import { assertNetwork } from "../utils";
export const command = "rpc <network> <chain>";
export const desc = "Print RPC address";
export const builder = (y: typeof yargs) => {
return y
export const builder = (y: typeof yargs) =>
y
.positional("network", {
describe: "network",
type: "string",
choices: ["mainnet", "testnet", "devnet"],
})
demandOption: true,
} as const)
.positional("chain", {
describe: "Chain to query",
type: "string",
choices: Object.keys(CHAINS),
});
};
export const handler = async (argv) => {
demandOption: true,
} as const);
export const handler = async (
argv: Awaited<ReturnType<typeof builder>["argv"]>
) => {
assertChain(argv["chain"]);
const network = argv.network.toUpperCase();
if (network !== "MAINNET" && network !== "TESTNET" && network !== "DEVNET") {
throw Error(`Unknown network: ${network}`);
}
assertNetwork(network);
console.log(NETWORKS[network][argv["chain"]].rpc);
};

View File

@ -9,6 +9,7 @@ import {
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import yargs from "yargs";
import { execute_algorand } from "../algorand";
import { execute_aptos } from "../aptos";
import { execute_evm } from "../evm";
import { execute_injective } from "../injective";
import { execute_near } from "../near";
@ -16,58 +17,55 @@ import { execute_sei } from "../sei";
import { execute_solana } from "../solana";
import { submit as submitSui } from "../sui";
import { execute_terra } from "../terra";
import * as vaa from "../vaa";
import { assertNetwork } from "../utils";
import { assertKnownPayload, impossible, parse } from "../vaa";
import { execute_xpla } from "../xpla";
import { execute_aptos } from "../aptos";
export const command = "submit <vaa>";
export const desc = "Execute a VAA";
export const builder = (y: typeof yargs) => {
return y
export const builder = (y: typeof yargs) =>
y
.positional("vaa", {
describe: "vaa",
type: "string",
required: true,
demandOption: true,
})
.option("chain", {
alias: "c",
describe: "chain name",
type: "string",
choices: Object.keys(CHAINS),
required: false,
})
demandOption: false,
} as const)
.option("network", {
alias: "n",
describe: "network",
type: "string",
choices: ["mainnet", "testnet", "devnet"],
required: true,
})
demandOption: true,
} as const)
.option("contract-address", {
alias: "a",
describe: "Contract to submit VAA to (override config)",
type: "string",
required: false,
demandOption: false,
})
.option("rpc", {
describe: "RPC endpoint",
type: "string",
required: false,
demandOption: false,
});
};
export const handler = async (argv) => {
export const handler = async (
argv: Awaited<ReturnType<typeof builder>["argv"]>
) => {
const vaa_hex = String(argv.vaa);
const buf = Buffer.from(vaa_hex, "hex");
const parsed_vaa = vaa.parse(buf);
const parsed_vaa = parse(buf);
vaa.assertKnownPayload(parsed_vaa);
assertKnownPayload(parsed_vaa);
console.log(parsed_vaa.payload);
const network = argv.network.toUpperCase();
if (network !== "MAINNET" && network !== "TESTNET" && network !== "DEVNET") {
throw Error(`Unknown network: ${network}`);
}
assertNetwork(network);
// We figure out the target chain to submit the VAA to.
// The VAA might specify this itself (for example a contract upgrade VAA
@ -156,6 +154,6 @@ export const handler = async (argv) => {
} else {
// If you get a type error here, hover over `chain`'s type and it tells you
// which cases are not handled
vaa.impossible(chain);
impossible(chain);
}
};

View File

@ -21,33 +21,33 @@ export const addBuildCommands: YargsAddCommandsFn = (y: typeof yargs) =>
.option("decimals", {
alias: "d",
describe: "Decimals of asset",
required: true,
demandOption: true,
type: "number",
})
// Can't be called version because of a conflict with the native version option
.option("version-struct", {
alias: "v",
describe: "Version control struct name (e.g. V__0_1_0)",
required: true,
demandOption: true,
type: "string",
})
.option("network", NETWORK_OPTIONS)
.option("package-path", {
alias: "p",
describe: "Path to coin module",
required: false,
demandOption: false,
type: "string",
})
.option("wormhole-state", {
alias: "w",
describe: "Wormhole state object ID",
required: false,
demandOption: false,
type: "string",
})
.option("token-bridge-state", {
alias: "t",
describe: "Token bridge state object ID",
required: false,
demandOption: false,
type: "string",
})
.option("rpc", RPC_OPTIONS),
@ -65,11 +65,23 @@ export const addBuildCommands: YargsAddCommandsFn = (y: typeof yargs) =>
argv["wormhole-state"] ?? CONTRACTS[network].sui.core;
const tokenBridgeStateObjectId =
argv["token-bridge-state"] ?? CONTRACTS[network].sui.token_bridge;
if (!coreBridgeStateObjectId) {
throw new Error(
`Couldn't find core bridge state object ID for network ${network}`
);
}
if (!tokenBridgeStateObjectId) {
throw new Error(
`Couldn't find token bridge state object ID for network ${network}`
);
}
const provider = getProvider(
network,
argv.rpc ?? NETWORKS[network].sui.rpc
);
const build = await buildCoin(
provider,
network,

View File

@ -27,16 +27,17 @@ export const addDeployCommands: YargsAddCommandsFn = (y: typeof yargs) =>
y.command(
"deploy <package-dir>",
"Deploy a Sui package",
(yargs) => {
return yargs
(yargs) =>
yargs
.positional("package-dir", {
type: "string",
describe: "Path to package directory",
demandOption: true,
})
.option("network", NETWORK_OPTIONS)
.option("debug", DEBUG_OPTIONS)
.option("private-key", PRIVATE_KEY_OPTIONS)
.option("rpc", RPC_OPTIONS);
},
.option("rpc", RPC_OPTIONS),
async (argv) => {
checkBinary("sui", README_URL);

View File

@ -9,8 +9,8 @@ import { addUtilsCommands } from "./utils";
export const command = "sui";
export const desc = "Sui utilities";
export const builder = function (y: typeof yargs) {
return new Yargs(y)
export const builder = (y: typeof yargs) =>
new Yargs(y)
.addCommands(addBuildCommands)
.addCommands(addDeployCommands)
.addCommands(addInitCommands)
@ -20,5 +20,4 @@ export const builder = function (y: typeof yargs) {
.y()
.strict()
.demandCommand();
};
export const handler = (argv) => {};
export const handler = () => {};

View File

@ -20,6 +20,7 @@ import {
isSameType,
logTransactionDigest,
logTransactionSender,
setMaxGasBudgetDevnet,
} from "../../sui";
import { Network, assertNetwork } from "../../utils";
import { YargsAddCommandsFn } from "../Yargs";
@ -29,24 +30,23 @@ export const addInitCommands: YargsAddCommandsFn = (y: typeof yargs) =>
.command(
"init-example-message-app",
"Initialize example core message app",
(yargs) => {
return yargs
(yargs) =>
yargs
.option("network", NETWORK_OPTIONS)
.option("package-id", {
alias: "p",
describe: "Example app package ID",
required: true,
demandOption: true,
type: "string",
})
.option("wormhole-state", {
alias: "w",
describe: "Wormhole state object ID",
required: true,
demandOption: true,
type: "string",
})
.option("private-key", PRIVATE_KEY_OPTIONS)
.option("rpc", RPC_OPTIONS);
},
.option("rpc", RPC_OPTIONS),
async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
@ -69,26 +69,26 @@ export const addInitCommands: YargsAddCommandsFn = (y: typeof yargs) =>
"Example app state object ID",
getCreatedObjects(res).find((e) =>
isSameType(e.type, `${packageId}::sender::State`)
).objectId
)?.objectId
);
}
)
.command(
"init-token-bridge",
"Initialize token bridge contract",
(yargs) => {
return yargs
(yargs) =>
yargs
.option("network", NETWORK_OPTIONS)
.option("package-id", {
alias: "p",
describe: "Token bridge package ID",
required: true,
demandOption: true,
type: "string",
})
.option("wormhole-state", {
alias: "w",
describe: "Wormhole state object ID",
required: true,
demandOption: true,
type: "string",
})
.option("governance-chain-id", {
@ -96,18 +96,17 @@ export const addInitCommands: YargsAddCommandsFn = (y: typeof yargs) =>
describe: "Governance chain ID",
default: GOVERNANCE_CHAIN,
type: "number",
required: false,
demandOption: false,
})
.option("governance-address", {
alias: "a",
describe: "Governance contract address",
type: "string",
default: GOVERNANCE_EMITTER,
required: false,
demandOption: false,
})
.option("private-key", PRIVATE_KEY_OPTIONS)
.option("rpc", RPC_OPTIONS);
},
.option("rpc", RPC_OPTIONS),
async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
@ -134,25 +133,25 @@ export const addInitCommands: YargsAddCommandsFn = (y: typeof yargs) =>
"Token bridge state object ID",
getCreatedObjects(res).find((e) =>
isSameType(e.type, `${packageId}::state::State`)
).objectId
)?.objectId
);
}
)
.command(
"init-wormhole",
"Initialize wormhole core contract",
(yargs) => {
return yargs
(yargs) =>
yargs
.option("network", NETWORK_OPTIONS)
.option("package-id", {
alias: "p",
describe: "Core bridge package ID",
required: true,
demandOption: true,
type: "string",
})
.option("initial-guardian", {
alias: "i",
required: true,
demandOption: true,
describe: "Initial guardian public keys",
type: "string",
})
@ -162,25 +161,24 @@ export const addInitCommands: YargsAddCommandsFn = (y: typeof yargs) =>
describe: "Governance chain ID",
default: GOVERNANCE_CHAIN,
type: "number",
required: false,
demandOption: false,
})
.option("guardian-set-index", {
alias: "s",
describe: "Governance set index",
default: 0,
type: "number",
required: false,
demandOption: false,
})
.option("governance-address", {
alias: "a",
describe: "Governance contract address",
type: "string",
default: GOVERNANCE_EMITTER,
required: false,
demandOption: false,
})
.option("private-key", PRIVATE_KEY_OPTIONS)
.option("rpc", RPC_OPTIONS);
},
.option("rpc", RPC_OPTIONS),
async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
@ -209,7 +207,7 @@ export const addInitCommands: YargsAddCommandsFn = (y: typeof yargs) =>
"Wormhole state object ID",
getCreatedObjects(res).find((e) =>
isSameType(e.type, `${packageId}::state::State`)
).objectId
)?.objectId
);
if (debug) {
logTransactionSender(res);
@ -228,16 +226,13 @@ export const initExampleApp = async (
const provider = getProvider(network, rpc);
const signer = getSigner(provider, network, privateKey);
const transactionBlock = new TransactionBlock();
if (network === "DEVNET") {
// Avoid Error checking transaction input objects: GasBudgetTooHigh { gas_budget: 50000000000, max_budget: 10000000000 }
transactionBlock.setGasBudget(10000000000);
}
transactionBlock.moveCall({
const tx = new TransactionBlock();
setMaxGasBudgetDevnet(network, tx);
tx.moveCall({
target: `${packageId}::sender::init_with_params`,
arguments: [transactionBlock.object(wormholeStateObjectId)],
arguments: [tx.object(wormholeStateObjectId)],
});
return executeTransactionBlock(signer, transactionBlock);
return executeTransactionBlock(signer, tx);
};
export const initTokenBridge = async (
@ -283,26 +278,23 @@ export const initTokenBridge = async (
coreBridgeStateObjectId
);
const transactionBlock = new TransactionBlock();
if (network === "DEVNET") {
// Avoid Error checking transaction input objects: GasBudgetTooHigh { gas_budget: 50000000000, max_budget: 10000000000 }
transactionBlock.setGasBudget(10000000000);
}
const [emitterCap] = transactionBlock.moveCall({
const tx = new TransactionBlock();
setMaxGasBudgetDevnet(network, tx);
const [emitterCap] = tx.moveCall({
target: `${wormholePackageId}::emitter::new`,
arguments: [transactionBlock.object(coreBridgeStateObjectId)],
arguments: [tx.object(coreBridgeStateObjectId)],
});
transactionBlock.moveCall({
tx.moveCall({
target: `${tokenBridgePackageId}::setup::complete`,
arguments: [
transactionBlock.object(deployerCapObjectId),
transactionBlock.object(upgradeCapObjectId),
tx.object(deployerCapObjectId),
tx.object(upgradeCapObjectId),
emitterCap,
transactionBlock.pure(governanceChainId),
transactionBlock.pure([...Buffer.from(governanceContract, "hex")]),
tx.pure(governanceChainId),
tx.pure([...Buffer.from(governanceContract, "hex")]),
],
});
return executeTransactionBlock(signer, transactionBlock);
return executeTransactionBlock(signer, tx);
};
export const initWormhole = async (
@ -344,25 +336,22 @@ export const initWormhole = async (
);
}
const transactionBlock = new TransactionBlock();
if (network === "DEVNET") {
// Avoid Error checking transaction input objects: GasBudgetTooHigh { gas_budget: 50000000000, max_budget: 10000000000 }
transactionBlock.setGasBudget(10000000000);
}
transactionBlock.moveCall({
const tx = new TransactionBlock();
setMaxGasBudgetDevnet(network, tx);
tx.moveCall({
target: `${coreBridgePackageId}::setup::complete`,
arguments: [
transactionBlock.object(deployerCapObjectId),
transactionBlock.object(upgradeCapObjectId),
transactionBlock.pure(governanceChainId),
transactionBlock.pure([...Buffer.from(governanceContract, "hex")]),
transactionBlock.pure(guardianSetIndex),
transactionBlock.pure(
tx.object(deployerCapObjectId),
tx.object(upgradeCapObjectId),
tx.pure(governanceChainId),
tx.pure([...Buffer.from(governanceContract, "hex")]),
tx.pure(guardianSetIndex),
tx.pure(
initialGuardians.split(",").map((g) => [...Buffer.from(g, "hex")])
),
transactionBlock.pure(24 * 60 * 60), // Guardian set TTL in seconds
transactionBlock.pure("0"), // Message fee
tx.pure(24 * 60 * 60), // Guardian set TTL in seconds
tx.pure("0"), // Message fee
],
});
return executeTransactionBlock(signer, transactionBlock);
return executeTransactionBlock(signer, tx);
};

View File

@ -12,6 +12,7 @@ import {
getSigner,
logTransactionDigest,
logTransactionSender,
setMaxGasBudgetDevnet,
} from "../../sui";
import { assertNetwork } from "../../utils";
import { YargsAddCommandsFn } from "../Yargs";
@ -22,41 +23,40 @@ export const addPublishMessageCommands: YargsAddCommandsFn = (
y.command(
"publish-example-message",
"Publish message from example app via core bridge",
(yargs) => {
return yargs
(yargs) =>
yargs
.option("network", NETWORK_OPTIONS)
.option("package-id", {
alias: "p",
describe: "Package ID/module address",
required: true,
demandOption: true,
type: "string",
})
.option("state", {
alias: "s",
describe: "Core messages app state object ID",
required: true,
demandOption: true,
type: "string",
})
.option("wormhole-state", {
alias: "w",
describe: "Wormhole state object ID",
required: true,
demandOption: true,
type: "string",
})
.option("message", {
alias: "m",
describe: "Message payload",
required: true,
demandOption: true,
type: "string",
})
.option("private-key", {
alias: "k",
describe: "Custom private key to sign txs",
required: false,
demandOption: false,
type: "string",
})
.option("rpc", RPC_OPTIONS);
},
.option("rpc", RPC_OPTIONS),
async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
@ -71,26 +71,23 @@ export const addPublishMessageCommands: YargsAddCommandsFn = (
const signer = getSigner(provider, network, privateKey);
// Publish message
const transactionBlock = new TransactionBlock();
if (network === "DEVNET") {
// Avoid Error checking transaction input objects: GasBudgetTooHigh { gas_budget: 50000000000, max_budget: 10000000000 }
transactionBlock.setGasBudget(10000000000);
}
transactionBlock.moveCall({
const tx = new TransactionBlock();
setMaxGasBudgetDevnet(network, tx);
tx.moveCall({
target: `${packageId}::sender::send_message_entry`,
arguments: [
transactionBlock.object(stateObjectId),
transactionBlock.object(wormholeStateObjectId),
transactionBlock.pure(message),
transactionBlock.object(SUI_CLOCK_OBJECT_ID),
tx.object(stateObjectId),
tx.object(wormholeStateObjectId),
tx.pure(message),
tx.object(SUI_CLOCK_OBJECT_ID),
],
});
const res = await executeTransactionBlock(signer, transactionBlock);
const res = await executeTransactionBlock(signer, tx);
// Hacky way to grab event since we don't require package ID of the
// core bridge as input. Doesn't really matter since this is a test
// command.
const event = res.events.find(
const event = res.events?.find(
(e) =>
normalizeSuiAddress(e.packageId) === normalizeSuiAddress(packageId) &&
e.type.includes("publish_message::WormholeMessage")
@ -107,10 +104,10 @@ export const addPublishMessageCommands: YargsAddCommandsFn = (
console.log("Publish message succeeded:", {
sender: event.sender,
type: event.type,
payload: Buffer.from(event.parsedJson.payload).toString(),
emitter: Buffer.from(event.parsedJson.sender).toString("hex"),
sequence: event.parsedJson.sequence,
nonce: event.parsedJson.nonce,
payload: Buffer.from(event.parsedJson?.payload).toString(),
emitter: Buffer.from(event.parsedJson?.sender).toString("hex"),
sequence: event.parsedJson?.sequence,
nonce: event.parsedJson?.nonce,
});
}
);

View File

@ -1,5 +1,6 @@
import {
ChainId,
ChainName,
coalesceChainName,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import { parseTokenBridgeRegisterChainVaa } from "@certusone/wormhole-sdk/lib/esm/vaa/tokenBridge";
@ -30,6 +31,7 @@ import {
logPublishedPackageId,
logTransactionDigest,
registerChain,
setMaxGasBudgetDevnet,
} from "../../sui";
import { YargsAddCommandsFn } from "../Yargs";
import { deploy } from "./deploy";
@ -39,16 +41,15 @@ export const addSetupCommands: YargsAddCommandsFn = (y: typeof yargs) =>
y.command(
"setup-devnet",
"Setup devnet by deploying and initializing core and token bridges and submitting chain registrations.",
(yargs) => {
return yargs
(yargs) =>
yargs
.option("private-key", {
alias: "k",
describe: "Custom private key to sign txs",
required: false,
demandOption: false,
type: "string",
})
.option("rpc", RPC_OPTIONS);
},
.option("rpc", RPC_OPTIONS),
async (argv) => {
const network = "DEVNET";
const privateKey = argv["private-key"];
@ -62,8 +63,8 @@ export const addSetupCommands: YargsAddCommandsFn = (y: typeof yargs) =>
rpc,
privateKey
);
assertSuccess(coreBridgeDeployRes, "Core bridge deployment failed.");
logTransactionDigest(coreBridgeDeployRes);
assertSuccess(coreBridgeDeployRes, "Core bridge deployment failed.");
logPublishedPackageId(coreBridgeDeployRes);
// Init core bridge
@ -79,11 +80,17 @@ export const addSetupCommands: YargsAddCommandsFn = (y: typeof yargs) =>
rpc,
privateKey
);
logTransactionDigest(coreBridgeInitRes);
assertSuccess(coreBridgeInitRes, "Core bridge initialization failed.");
// Get core bridge state object ID
const coreBridgeStateObjectId = getCreatedObjects(coreBridgeInitRes).find(
(e) => isSameType(e.type, `${coreBridgePackageId}::state::State`)
).objectId;
assertSuccess(coreBridgeInitRes, "Core bridge initialization failed.");
logTransactionDigest(coreBridgeInitRes);
)?.objectId;
if (!coreBridgeStateObjectId) {
throw new Error("Couldn't find core bridge state object ID.");
}
console.log("Core bridge state object ID", coreBridgeStateObjectId);
// Deploy token bridge
@ -94,8 +101,8 @@ export const addSetupCommands: YargsAddCommandsFn = (y: typeof yargs) =>
rpc,
privateKey
);
assertSuccess(tokenBridgeDeployRes, "Token bridge deployment failed.");
logTransactionDigest(tokenBridgeDeployRes);
assertSuccess(tokenBridgeDeployRes, "Token bridge deployment failed.");
logPublishedPackageId(tokenBridgeDeployRes);
// Init token bridge
@ -110,13 +117,19 @@ export const addSetupCommands: YargsAddCommandsFn = (y: typeof yargs) =>
rpc,
privateKey
);
logTransactionDigest(tokenBridgeInitRes);
assertSuccess(tokenBridgeInitRes, "Token bridge initialization failed.");
// Get token bridge state object ID
const tokenBridgeStateObjectId = getCreatedObjects(
tokenBridgeInitRes
).find((e) =>
isSameType(e.type, `${tokenBridgePackageId}::state::State`)
).objectId;
assertSuccess(tokenBridgeInitRes, "Token bridge initialization failed.");
logTransactionDigest(tokenBridgeInitRes);
)?.objectId;
if (!tokenBridgeStateObjectId) {
throw new Error("Couldn't find token bridge state object ID.");
}
console.log("Token bridge state object ID", tokenBridgeStateObjectId);
// Deploy example app
@ -142,7 +155,7 @@ export const addSetupCommands: YargsAddCommandsFn = (y: typeof yargs) =>
);
const exampleAppStateObjectId = getCreatedObjects(exampleAppInitRes).find(
(e) => isSameType(e.type, `${exampleAppPackageId}::sender::State`)
).objectId;
)?.objectId;
logTransactionDigest(exampleAppInitRes);
console.log("Example app state object ID", exampleAppStateObjectId);
@ -185,10 +198,10 @@ export const addSetupCommands: YargsAddCommandsFn = (y: typeof yargs) =>
}
dotenv.config({ path: envPath });
const signer = getSigner(provider, network, privateKey);
const tx = new TransactionBlock();
tx.setGasBudget(10000000000);
const registrations = [];
setMaxGasBudgetDevnet("DEVNET", tx);
const registrations: { chain: ChainName; module: string }[] = [];
for (const key in process.env) {
if (/^REGISTER_(.+)_TOKEN_BRIDGE_VAA$/.test(key)) {
// Get VAA info
@ -210,6 +223,7 @@ export const addSetupCommands: YargsAddCommandsFn = (y: typeof yargs) =>
}
}
const signer = getSigner(provider, network, privateKey);
const registerRes = await executeTransactionBlock(signer, tx);
assertSuccess(registerRes, "Chain registrations failed.");
@ -228,13 +242,12 @@ export const addSetupCommands: YargsAddCommandsFn = (y: typeof yargs) =>
const getEmitterCapObjectId = async (
provider: JsonRpcProvider,
tokenBridgeStateObjectId: string
): Promise<string> => {
return getObjectFields(
): Promise<string> =>
getObjectFields(
await provider.getObject({
id: tokenBridgeStateObjectId,
options: {
showContent: true,
},
})
).emitter_cap.fields.id.id;
};
)?.emitter_cap.fields.id.id;

View File

@ -1,3 +1,4 @@
import { PaginatedObjectsResponse } from "@mysten/sui.js";
import yargs from "yargs";
import { NETWORK_OPTIONS, RPC_OPTIONS } from "../../consts";
import { NETWORKS } from "../../networks";
@ -15,6 +16,7 @@ export const addUtilsCommands: YargsAddCommandsFn = (y: typeof yargs) =>
.positional("owner", {
describe: "Owner address",
type: "string",
demandOption: true,
})
.option("network", NETWORK_OPTIONS)
.option("rpc", RPC_OPTIONS),
@ -25,11 +27,15 @@ export const addUtilsCommands: YargsAddCommandsFn = (y: typeof yargs) =>
const owner = argv.owner;
const provider = getProvider(network, rpc);
const objects = [];
const objects: PaginatedObjectsResponse["data"] = [];
let cursor = undefined;
let cursor: PaginatedObjectsResponse["nextCursor"] | undefined =
undefined;
while (true) {
const res = await provider.getOwnedObjects({ owner, cursor });
const res: PaginatedObjectsResponse = await provider.getOwnedObjects({
owner,
cursor,
});
objects.push(...res.data);
if (res.hasNextPage) {
cursor = res.nextCursor;
@ -51,6 +57,7 @@ export const addUtilsCommands: YargsAddCommandsFn = (y: typeof yargs) =>
.positional("state-object-id", {
describe: "Object ID of State object",
type: "string",
demandOption: true,
})
.option("network", NETWORK_OPTIONS)
.option("rpc", RPC_OPTIONS),
@ -72,6 +79,7 @@ export const addUtilsCommands: YargsAddCommandsFn = (y: typeof yargs) =>
.positional("transaction-digest", {
describe: "Digest of transaction to fetch",
type: "string",
demandOption: true,
})
.option("network", {
alias: "n",
@ -79,7 +87,7 @@ export const addUtilsCommands: YargsAddCommandsFn = (y: typeof yargs) =>
type: "string",
choices: ["mainnet", "testnet", "devnet"],
default: "devnet",
required: false,
demandOption: false,
})
.option("rpc", RPC_OPTIONS),
async (argv) => {

View File

@ -5,43 +5,41 @@ import { CONTRACTS } from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import { ethers } from "ethers";
import yargs from "yargs";
import { NETWORKS } from "../networks";
import { assertNetwork } from "../utils";
export const command = "verify-vaa";
export const desc = "Verifies a VAA by querying the core contract on Ethereum";
export const builder = (y: typeof yargs) => {
return y
export const builder = (y: typeof yargs) =>
y
.option("vaa", {
alias: "v",
describe: "vaa in hex format",
type: "string",
required: true,
demandOption: true,
})
.option("network", {
alias: "n",
describe: "network",
type: "string",
choices: ["mainnet", "testnet", "devnet"],
required: true,
});
};
export const handler = async (argv) => {
demandOption: true,
} as const);
export const handler = async (
argv: Awaited<ReturnType<typeof builder>["argv"]>
) => {
const network = argv.network.toUpperCase();
if (network !== "MAINNET" && network !== "TESTNET" && network !== "DEVNET") {
throw Error(`Unknown network: ${network}`);
}
assertNetwork(network);
const buf = Buffer.from(String(argv.vaa), "hex");
let n = NETWORKS[network]["ethereum"];
let contract_address = CONTRACTS[network]["ethereum"].core;
if (contract_address === undefined) {
const contract_address = CONTRACTS[network].ethereum.core;
if (!contract_address) {
throw Error(`Unknown core contract on ${network} for ethereum`);
}
const provider = new ethers.providers.JsonRpcProvider(n.rpc);
const provider = new ethers.providers.JsonRpcProvider(
NETWORKS[network].ethereum.rpc
);
const contract = Implementation__factory.connect(contract_address, provider);
const result = await contract.parseAndVerifyVM(buf);
if (result[1]) {
console.log("Verification succeeded!");
} else {

View File

@ -58,27 +58,26 @@ export const DEBUG_OPTIONS = {
alias: "d",
describe: "Log debug info",
type: "boolean",
required: false,
demandOption: false,
} as const;
export const NAMED_ADDRESSES_OPTIONS = {
describe: "Named addresses in the format addr1=0x0,addr2=0x1,...",
type: "string",
require: false,
demandOption: false,
} as const;
export const NETWORK_OPTIONS = {
alias: "n",
describe: "Network",
type: "string",
choices: ["mainnet", "testnet", "devnet"],
required: true,
demandOption: true,
} as const;
export const PRIVATE_KEY_OPTIONS = {
alias: "k",
describe: "Custom private key to sign transactions",
required: false,
demandOption: false,
type: "string",
} as const;
@ -86,7 +85,7 @@ export const RPC_OPTIONS = {
alias: "r",
describe: "Override default rpc endpoint url",
type: "string",
required: false,
demandOption: false,
} as const;
export const GOVERNANCE_CHAIN = CHAIN_ID_SOLANA;

View File

@ -1,58 +1,57 @@
import { ethers } from "ethers";
import { NETWORKS } from "./networks";
import { encode, Encoding, impossible, Payload, typeWidth } from "./vaa";
import axios from "axios";
import * as celo from "@celo-tools/celo-ethers-wrapper";
import { solidityKeccak256 } from "ethers/lib/utils";
import {
BridgeImplementation__factory,
Implementation__factory,
NFTBridgeImplementation__factory,
} from "@certusone/wormhole-sdk/lib/esm/ethers-contracts";
import {
CHAINS,
CONTRACTS,
Contracts,
EVMChainName,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import {
BridgeImplementation__factory,
Implementation__factory,
NFTBridgeImplementation__factory,
} from "@certusone/wormhole-sdk/lib/esm/ethers-contracts";
import axios from "axios";
import { ethers } from "ethers";
import { solidityKeccak256 } from "ethers/lib/utils";
import { NETWORKS } from "./networks";
import { Network } from "./utils";
import { Encoding, Payload, encode, impossible, typeWidth } from "./vaa";
const _IMPLEMENTATION_SLOT =
"0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
export async function query_contract_evm(
network: "MAINNET" | "TESTNET" | "DEVNET",
network: Network,
chain: EVMChainName,
module: "Core" | "NFTBridge" | "TokenBridge",
contract_address: string | undefined,
_rpc: string | undefined
): Promise<object> {
let n = NETWORKS[network][chain];
let rpc: string | undefined = _rpc ?? n.rpc;
if (rpc === undefined) {
const n = NETWORKS[network][chain];
const rpc: string | undefined = _rpc ?? n.rpc;
if (!rpc) {
throw Error(`No ${network} rpc defined for ${chain} (see networks.ts)`);
}
let contracts: Contracts = CONTRACTS[network][chain];
const contracts: Contracts = CONTRACTS[network][chain];
const provider = new ethers.providers.JsonRpcProvider(rpc);
let result: any = {};
const result: any = {};
switch (module) {
case "Core":
contract_address = contract_address ? contract_address : contracts.core;
if (contract_address === undefined) {
if (!contract_address) {
throw Error(`Unknown core contract on ${network} for ${chain}`);
}
const core = Implementation__factory.connect(contract_address, provider);
result.address = contract_address;
result.currentGuardianSetIndex = await core.getCurrentGuardianSetIndex();
let guardianSetsPromise = Promise.all(
const guardianSetsPromise = Promise.all(
[...Array(result.currentGuardianSetIndex + 1).keys()].map((i) =>
core.getGuardianSet(i)
)
);
let [
const [
guardianSetExpiry,
chainId,
evmChainId,
@ -84,7 +83,7 @@ export async function query_contract_evm(
result.isInitialized = await core.isInitialized(result.implementation);
result.guardianSet = {};
for (let [i, guardianSet] of guardianSets.entries()) {
for (const [i, guardianSet] of guardianSets.entries()) {
result.guardianSet[i] = {
keys: guardianSet[0],
expiry: guardianSet[1],
@ -112,7 +111,7 @@ export async function query_contract_evm(
await tb.bridgeContracts(c_id),
])
);
let [
const [
wormhole,
implementationSlotTb,
tokenImplementation,
@ -150,7 +149,7 @@ export async function query_contract_evm(
result.WETH = WETH;
result.registrations = {};
for (let [c_name, c] of registrations) {
for (const [c_name, c] of registrations) {
result.registrations[c_name] = c;
}
break;
@ -174,7 +173,7 @@ export async function query_contract_evm(
await nb.bridgeContracts(c_id),
])
);
let [
const [
wormholeNb,
implementationSlotNb,
tokenImplementationNb,
@ -209,7 +208,7 @@ export async function query_contract_evm(
result.governanceContract = governanceContractNb;
result.registrations = {};
for (let [c_name, c] of registrationsNb) {
for (const [c_name, c] of registrationsNb) {
result.registrations[c_name] = c;
}
break;
@ -221,20 +220,19 @@ export async function query_contract_evm(
}
export async function getImplementation(
network: "MAINNET" | "TESTNET" | "DEVNET",
network: Network,
chain: EVMChainName,
module: "Core" | "NFTBridge" | "TokenBridge",
contract_address: string | undefined,
_rpc: string | undefined
): Promise<ethers.BigNumber> {
let n = NETWORKS[network][chain];
let rpc: string | undefined = _rpc ?? n.rpc;
if (rpc === undefined) {
const n = NETWORKS[network][chain];
const rpc: string | undefined = _rpc ?? n.rpc;
if (!rpc) {
throw Error(`No ${network} rpc defined for ${chain} (see networks.ts)`);
}
let contracts: Contracts = CONTRACTS[network][chain];
const contracts: Contracts = CONTRACTS[network][chain];
switch (module) {
case "Core":
contract_address = contract_address ? contract_address : contracts.core;
@ -253,6 +251,10 @@ export async function getImplementation(
impossible(module);
}
if (!contract_address) {
throw Error(`Unknown ${module} contract on ${network} for ${chain}`);
}
return (
await getStorageAt(rpc, contract_address, _IMPLEMENTATION_SLOT, ["address"])
)[0];
@ -261,23 +263,23 @@ export async function getImplementation(
export async function execute_evm(
payload: Payload,
vaa: Buffer,
network: "MAINNET" | "TESTNET" | "DEVNET",
network: Network,
chain: EVMChainName,
contract_address: string | undefined,
_rpc: string | undefined
) {
let n = NETWORKS[network][chain];
let rpc: string | undefined = _rpc ?? n.rpc;
if (rpc === undefined) {
const n = NETWORKS[network][chain];
const rpc: string | undefined = _rpc ?? n.rpc;
if (!rpc) {
throw Error(`No ${network} rpc defined for ${chain} (see networks.ts)`);
}
if (!n.key) {
throw Error(`No ${network} key defined for ${chain} (see networks.ts)`);
}
let key: string = n.key;
let contracts: Contracts = CONTRACTS[network][chain];
const key: string = n.key;
const contracts: Contracts = CONTRACTS[network][chain];
let provider: ethers.providers.JsonRpcProvider;
let signer: ethers.Wallet;
if (chain === "celo") {
@ -294,9 +296,9 @@ export async function execute_evm(
// testnet (or devnet), they might require additional guards
let overrides: ethers.Overrides = {};
if (chain === "karura" || chain == "acala") {
overrides = await getKaruraGasParams(n.rpc);
overrides = await getKaruraGasParams(rpc);
} else if (chain === "polygon") {
let feeData = await provider.getFeeData();
const feeData = await provider.getFeeData();
overrides = {
maxFeePerGas: feeData.maxFeePerGas?.mul(50) || undefined,
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas?.mul(50) || undefined,
@ -306,13 +308,13 @@ export async function execute_evm(
}
switch (payload.module) {
case "Core":
case "Core": {
contract_address = contract_address ? contract_address : contracts.core;
if (contract_address === undefined) {
throw Error(`Unknown core contract on ${network} for ${chain}`);
}
let c = new Implementation__factory(signer);
let cb = c.attach(contract_address);
const c = new Implementation__factory(signer);
const cb = c.attach(contract_address);
switch (payload.type) {
case "GuardianSetUpgrade":
console.log("Submitting new guardian set");
@ -335,16 +337,18 @@ export async function execute_evm(
default:
impossible(payload);
}
break;
case "NFTBridge":
}
case "NFTBridge": {
contract_address = contract_address
? contract_address
: contracts.nft_bridge;
if (contract_address === undefined) {
throw Error(`Unknown nft bridge contract on ${network} for ${chain}`);
}
let n = new NFTBridgeImplementation__factory(signer);
let nb = n.attach(contract_address);
const n = new NFTBridgeImplementation__factory(signer);
const nb = n.attach(contract_address);
switch (payload.type) {
case "ContractUpgrade":
console.log("Upgrading contract");
@ -372,16 +376,18 @@ export async function execute_evm(
default:
impossible(payload);
}
break;
case "TokenBridge":
}
case "TokenBridge": {
contract_address = contract_address
? contract_address
: contracts.token_bridge;
if (contract_address === undefined) {
throw Error(`Unknown token bridge contract on ${network} for ${chain}`);
}
let t = new BridgeImplementation__factory(signer);
let tb = t.attach(contract_address);
const t = new BridgeImplementation__factory(signer);
const tb = t.attach(contract_address);
switch (payload.type) {
case "ContractUpgrade":
console.log("Upgrading contract");
@ -419,9 +425,10 @@ export async function execute_evm(
break;
default:
impossible(payload);
}
break;
}
break;
default:
impossible(payload);
}
@ -440,7 +447,7 @@ export async function execute_evm(
* @param rpc the JSON RPC endpoint (needs to be hardhat of anvil)
* @param contract_address address of the core bridge contract
* @param guardian_addresses addresses of the desired guardian set to upgrade to
* @param new_guardian_set_index if specified, the new guardian set will be
* @param newGuardianSetIndex if specified, the new guardian set will be
* written into this guardian set index, and the guardian set index of the
* contract changed to it.
* If unspecified, then the current guardian set index will be overridden.
@ -452,16 +459,14 @@ export async function hijack_evm(
rpc: string,
contract_address: string,
guardian_addresses: string[],
new_guardian_set_index: number | undefined
newGuardianSetIndex: number | undefined
): Promise<void> {
const GUARDIAN_SETS_SLOT = 0x02;
const GUARDIAN_SET_INDEX_SLOT = 0x3;
const provider = new ethers.providers.JsonRpcProvider(rpc);
const core = Implementation__factory.connect(contract_address, provider);
let guardianSetIndex: number;
let guardianSetExpiry: number;
[guardianSetIndex, guardianSetExpiry] = await getStorageAt(
let [guardianSetIndex, guardianSetExpiry] = await getStorageAt(
rpc,
contract_address,
GUARDIAN_SET_INDEX_SLOT,
@ -472,21 +477,22 @@ export async function hijack_evm(
console.log(`Current guardian set (index ${guardianSetIndex}):`);
console.log(current_set[0]);
if (new_guardian_set_index !== undefined) {
if (newGuardianSetIndex !== undefined) {
await setStorageAt(
rpc,
contract_address,
GUARDIAN_SET_INDEX_SLOT,
["uint32", "uint32"],
[new_guardian_set_index, guardianSetExpiry]
[newGuardianSetIndex, guardianSetExpiry]
);
guardianSetIndex = await core.getCurrentGuardianSetIndex();
if (new_guardian_set_index !== guardianSetIndex) {
if (newGuardianSetIndex !== guardianSetIndex) {
throw Error("Failed to update guardian set index.");
} else {
console.log(`Guardian set index updated to ${new_guardian_set_index}`);
console.log(`Guardian set index updated to ${newGuardianSetIndex}`);
}
}
const addresses_slot = computeMappingElemSlot(
GUARDIAN_SETS_SLOT,
guardianSetIndex
@ -621,7 +627,7 @@ async function getStorageAt(
rpc
).getStorageAt(contract_address, storage_slot);
let val = ethers.BigNumber.from(string_val);
let ret: any[] = [];
const ret: any[] = [];
// we decode the elements one by one, by shifting down the stuff we've parsed already
types.forEach((typ) => {
const padded = ethers.utils.defaultAbiCoder.encode(["uint256"], [val]);
@ -699,15 +705,19 @@ export async function setStorageAt(
).data;
}
async function maybeUnsupported<T>(
const maybeUnsupported = async <T>(
query: Promise<T>
): Promise<T | "unsupported"> {
): Promise<T | "unsupported"> => {
try {
return await query;
return query;
} catch (e) {
if (e.reason === "unsupported") {
return e.reason;
if (isUnsupportedError(e)) {
return "unsupported";
}
throw e;
}
}
};
const isUnsupportedError = (e: any): e is { reason: string } =>
e && typeof e === "object" && "reason" in e && e.reason === "unsupported";

View File

@ -1,5 +1,8 @@
import { CONTRACTS } from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import { getNetworkInfo, Network } from "@injectivelabs/networks";
import {
getNetworkInfo,
Network as InjectiveNetwork,
} from "@injectivelabs/networks";
import {
ChainRestAuthApi,
createTransaction,
@ -10,43 +13,44 @@ import {
import { DEFAULT_STD_FEE, getStdFee } from "@injectivelabs/utils";
import { fromUint8Array } from "js-base64";
import { NETWORKS } from "./networks";
import { Network } from "./utils";
import { impossible, Payload } from "./vaa";
export async function execute_injective(
payload: Payload,
vaa: Buffer,
environment: "MAINNET" | "TESTNET" | "DEVNET"
network: Network
) {
if (environment === "DEVNET") {
if (network === "DEVNET") {
throw new Error("Injective is not supported in DEVNET");
}
const chainName = "injective";
let n = NETWORKS[environment][chainName];
if (!n.key) {
throw Error(`No ${environment} key defined for Injective`);
const chain = "injective";
let { key } = NETWORKS[network][chain];
if (!key) {
throw Error(`No ${network} key defined for Injective`);
}
let contracts = CONTRACTS[environment][chainName];
const endPoint =
environment === "MAINNET" ? Network.MainnetK8s : Network.TestnetK8s;
const network = getNetworkInfo(endPoint);
const walletPKHash = n.key;
const walletPK = PrivateKey.fromMnemonic(walletPKHash);
let contracts = CONTRACTS[network][chain];
const endPoint =
network === "MAINNET"
? InjectiveNetwork.MainnetK8s
: InjectiveNetwork.TestnetK8s;
const networkInfo = getNetworkInfo(endPoint);
const walletPK = PrivateKey.fromMnemonic(key);
const walletInjAddr = walletPK.toBech32();
const walletPublicKey = walletPK.toPublicKey().toBase64();
let target_contract: string;
let action: string;
let execute_msg: object;
let action: "submit_v_a_a" | "submit_vaa";
let execute_msg: { vaa: string } | { data: string };
switch (payload.module) {
case "Core":
case "Core": {
target_contract = contracts.core;
action = "submit_v_a_a";
execute_msg = {
[action]: {
vaa: fromUint8Array(vaa),
},
};
switch (payload.type) {
case "GuardianSetUpgrade":
@ -60,20 +64,21 @@ export async function execute_injective(
default:
impossible(payload);
}
break;
case "NFTBridge":
if (contracts.nft_bridge === undefined) {
}
case "NFTBridge": {
if (!contracts.nft_bridge) {
// 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;
action = "submit_vaa";
execute_msg = {
[action]: {
data: fromUint8Array(vaa),
},
};
switch (payload.type) {
case "ContractUpgrade":
@ -90,18 +95,19 @@ export async function execute_injective(
default:
impossible(payload);
}
break;
case "TokenBridge":
}
case "TokenBridge": {
console.log("contracts:", contracts);
if (contracts.token_bridge === undefined) {
if (!contracts.token_bridge) {
throw new Error("contracts.token_bridge is undefined");
}
target_contract = contracts.token_bridge;
action = "submit_vaa";
execute_msg = {
[action]: {
data: fromUint8Array(vaa),
},
};
switch (payload.type) {
case "ContractUpgrade":
@ -122,30 +128,32 @@ export async function execute_injective(
throw Error("Can't complete payload 3 transfer from CLI");
default:
impossible(payload);
}
break;
}
break;
default:
action = impossible(payload);
target_contract = impossible(payload);
execute_msg = impossible(payload);
}
console.log("execute_msg", execute_msg);
console.log("execute_msg", { [action]: execute_msg });
const transaction = MsgExecuteContractCompat.fromJSON({
sender: walletInjAddr,
contractAddress: target_contract,
exec: {
action,
msg: {
...execute_msg[action],
...execute_msg,
},
},
});
console.log("transaction:", transaction);
const accountDetails = await new ChainRestAuthApi(network.rest).fetchAccount(
walletInjAddr
);
const accountDetails = await new ChainRestAuthApi(
networkInfo.rest
).fetchAccount(walletInjAddr);
const { signBytes, txRaw } = createTransaction({
message: transaction,
memo: "",
@ -156,21 +164,20 @@ export async function execute_injective(
accountDetails.account.base_account.account_number,
10
),
chainId: network.chainId,
chainId: networkInfo.chainId,
});
console.log("txRaw", txRaw);
// Sign transaction
console.log("sign transaction...");
/** Sign transaction */
const sig = await walletPK.sign(Buffer.from(signBytes));
/** Append Signatures */
// Append Signatures
txRaw.signatures = [sig];
const txService = new TxGrpcApi(network.grpc);
// Simulate transaction
console.log("simulate transaction...");
/** Simulate transaction */
const txService = new TxGrpcApi(networkInfo.grpc);
try {
const simulationResponse = await txService.simulate(txRaw);
console.log(
@ -183,8 +190,8 @@ export async function execute_injective(
return;
}
// Broadcast transaction
console.log("broadcast transaction...");
/** Broadcast transaction */
const txResponse = await txService.broadcast(txRaw);
console.log("txResponse", txResponse);

10
clients/js/src/near-seed-phrase.d.ts vendored Normal file
View File

@ -0,0 +1,10 @@
declare module "near-seed-phrase" {
export function parseSeedPhrase(
seedPhrase: string,
derivationPath?: string
): {
seedPhrase: string;
secretKey: string;
publicKey: string;
};
}

View File

@ -1,128 +1,34 @@
import { impossible, Payload } from "./vaa";
import { NETWORKS } from "./networks";
import { CONTRACTS } from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import { parseSeedPhrase, generateSeedPhrase } from "near-seed-phrase";
import BN from "bn.js";
import { readFileSync } from "fs";
import { Account, connect, KeyPair } from "near-api-js";
import { InMemoryKeyStore } from "near-api-js/lib/key_stores";
import { NETWORKS } from "./networks";
import { Network } from "./utils";
import { impossible, Payload } from "./vaa";
function default_near_args(argv) {
let network = argv["n"].toUpperCase();
let contracts = CONTRACTS[network]["near"];
let n = NETWORKS[network]["near"];
if (!("rpc" in argv)) {
argv["rpc"] = n["rpc"];
}
if (!("target" in argv) && "module" in argv) {
if (argv["module"] == "Core") {
argv["target"] = contracts["core"];
console.log("Setting target to core");
}
if (argv["module"] == "TokenBridge") {
argv["target"] = contracts["token_bridge"];
console.log("Setting target to token_bridge");
}
}
if (!("key" in argv)) {
if (n["key"]) {
argv["key"] = n["key"];
}
}
if (!("key" in argv)) {
if ("mnemonic" in argv) {
let k = parseSeedPhrase(argv["mnemonic"]);
argv["key"] = k["secretKey"];
}
}
}
export async function deploy_near(argv) {
default_near_args(argv);
let masterKey = KeyPair.fromString(argv["key"]);
let keyStore = new InMemoryKeyStore();
keyStore.setKey(argv["networkId"], argv["account"], masterKey);
keyStore.setKey(argv["networkId"], argv["target"], masterKey);
let near = await connect({
deps: {
keyStore,
},
networkId: argv["networkId"],
nodeUrl: argv["rpc"],
headers: {},
});
let masterAccount = new Account(near.connection, argv["account"]);
let targetAccount = new Account(near.connection, argv["target"]);
console.log(argv);
if ("attach" in argv) {
console.log(
"Sending money: " +
argv["target"] +
" from " +
argv["account"] +
" being sent " +
argv["attach"]
);
console.log(await masterAccount.sendMoney(argv["target"], argv["attach"]));
}
console.log("deploying contract");
console.log(await targetAccount.deployContract(readFileSync(argv["file"])));
}
export async function upgrade_near(argv) {
default_near_args(argv);
let masterKey = KeyPair.fromString(argv["key"]);
let keyStore = new InMemoryKeyStore();
keyStore.setKey(argv["networkId"], argv["account"], masterKey);
let near = await connect({
deps: {
keyStore,
},
networkId: argv["networkId"],
nodeUrl: argv["rpc"],
headers: {},
});
let masterAccount = new Account(near.connection, argv["account"]);
let result = await masterAccount.functionCall({
contractId: argv["target"],
methodName: "update_contract",
args: readFileSync(argv["file"]),
attachedDeposit: new BN("22797900000000000000000000"),
gas: new BN("300000000000000"),
});
console.log(result);
}
export async function execute_near(
export const execute_near = async (
payload: Payload,
vaa: string,
network: "MAINNET" | "TESTNET" | "DEVNET"
) {
let n = NETWORKS[network]["near"];
let contracts = CONTRACTS[network]["near"];
let target_contract = "";
let numSubmits = 1;
switch (payload.module) {
case "Core":
if (contracts.core === undefined) {
throw new Error("Core bridge not supported yet for near");
network: Network
): Promise<void> => {
const { rpc, key, networkId, deployerAccount } = NETWORKS[network].near;
if (!key) {
throw Error(`No ${network} key defined for NEAR`);
}
if (!rpc) {
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) {
throw new Error(`Core bridge address not defined for NEAR ${network}`);
}
target_contract = contracts.core;
switch (payload.type) {
case "GuardianSetUpgrade":
@ -137,10 +43,12 @@ export async function execute_near(
impossible(payload);
}
break;
case "NFTBridge":
if (contracts.nft_bridge === undefined) {
throw new Error("NFT bridge not supported yet for near");
}
case "NFTBridge": {
if (!contracts.nft_bridge) {
throw new Error(`NFT bridge address not defined for NEAR ${network}`);
}
numSubmits = 2;
target_contract = contracts.nft_bridge;
switch (payload.type) {
@ -158,11 +66,14 @@ export async function execute_near(
default:
impossible(payload);
}
break;
case "TokenBridge":
if (contracts.token_bridge === undefined) {
throw new Error("Token bridge not supported yet for near");
}
case "TokenBridge": {
if (!contracts.token_bridge) {
throw new Error(`Token bridge address not defined for NEAR ${network}`);
}
numSubmits = 2;
target_contract = contracts.token_bridge;
switch (payload.type) {
@ -184,37 +95,32 @@ export async function execute_near(
throw Error("Can't complete payload 3 transfer from CLI");
default:
impossible(payload);
}
break;
}
break;
default:
impossible(payload);
}
let key = KeyPair.fromString(n.key);
let keyStore = new InMemoryKeyStore();
keyStore.setKey(n.networkId, n.deployerAccount, key);
let near = await connect({
const keyStore = new InMemoryKeyStore();
keyStore.setKey(networkId, deployerAccount, KeyPair.fromString(key));
const near = await connect({
keyStore,
networkId: n.networkId,
nodeUrl: n.rpc,
networkId,
nodeUrl: rpc,
headers: {},
});
let nearAccount = new Account(near.connection, n.deployerAccount);
const nearAccount = new Account(near.connection, deployerAccount);
console.log("submitting vaa the first time");
let result1 = await nearAccount.functionCall({
const result1 = await nearAccount.functionCall({
contractId: target_contract,
methodName: "submit_vaa",
args: {
vaa: vaa,
},
args: { vaa },
attachedDeposit: new BN("100000000000000000000000"),
gas: new BN("300000000000000"),
});
if (numSubmits <= 1) {
console.log("Hash: " + result1.transaction.hash);
return;
@ -224,16 +130,13 @@ export async function execute_near(
// The first time, it checks if it has been seen at all.
// The second time, it executes.
console.log("submitting vaa the second time");
let result2 = await nearAccount.functionCall({
const result2 = await nearAccount.functionCall({
contractId: target_contract,
methodName: "submit_vaa",
args: {
vaa: vaa,
},
args: { vaa },
attachedDeposit: new BN("100000000000000000000000"),
gas: new BN("300000000000000"),
});
let txHash = result1.transaction.hash + ":" + result2.transaction.hash;
const txHash = result1.transaction.hash + ":" + result2.transaction.hash;
console.log("Hash: " + txHash);
}
};

View File

@ -2,24 +2,34 @@ import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing";
import { calculateFee } from "@cosmjs/stargate";
import { getSigningCosmWasmClient } from "@sei-js/core";
import { impossible, Payload } from "./vaa";
import { NETWORKS } from "./networks";
import { CONTRACTS } from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import { NETWORKS } from "./networks";
import { Network } from "./utils";
import { impossible, Payload } from "./vaa";
export async function execute_sei(
payload: Payload,
vaa: Buffer,
network: "MAINNET" | "TESTNET" | "DEVNET"
network: Network
) {
let chain = "sei";
let n = NETWORKS[network][chain];
let contracts = CONTRACTS[network][chain];
const contracts = CONTRACTS[network].sei;
const { rpc, key } = NETWORKS[network].sei;
if (!key) {
throw Error(`No ${network} key defined for NEAR`);
}
if (!rpc) {
throw Error(`No ${network} rpc defined for NEAR`);
}
let target_contract: string;
let execute_msg: object;
switch (payload.module) {
case "Core":
case "Core": {
if (!contracts.core) {
throw new Error(`Core bridge address not defined for Sei ${network}`);
}
target_contract = contracts.core;
// sigh...
execute_msg = {
@ -39,14 +49,17 @@ export async function execute_sei(
default:
impossible(payload);
}
break;
case "NFTBridge":
if (contracts.nft_bridge === undefined) {
}
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
throw new Error("NFT bridge not supported yet for sei");
throw new Error("NFT bridge not supported yet for Sei");
}
target_contract = contracts.nft_bridge;
execute_msg = {
submit_vaa: {
@ -68,8 +81,14 @@ export async function execute_sei(
default:
impossible(payload);
}
break;
case "TokenBridge":
}
case "TokenBridge": {
if (!contracts.token_bridge) {
throw new Error(`Token bridge address not defined for Sei ${network}`);
}
target_contract = contracts.token_bridge;
execute_msg = {
submit_vaa: {
@ -97,17 +116,19 @@ export async function execute_sei(
impossible(payload);
break;
}
break;
}
default:
target_contract = impossible(payload);
execute_msg = impossible(payload);
}
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(n.key, {
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(key, {
prefix: "sei",
});
const [account] = await wallet.getAccounts();
const client = await getSigningCosmWasmClient(n.rpc, wallet);
const client = await getSigningCosmWasmClient(rpc, wallet);
const fee = calculateFee(300000, "0.1usei");
const result = await client.execute(
account.address,

View File

@ -14,6 +14,7 @@ console.info = function (x: string) {
info(x);
}
};
const warn = console.warn;
console.warn = function (x: string) {
if (

View File

@ -1,17 +1,8 @@
import * as web3s from "@solana/web3.js";
import { NETWORKS } from "./networks";
import { impossible, Payload, VAA } from "./vaa";
import base58 from "bs58";
import { postVaaSolanaWithRetry } from "@certusone/wormhole-sdk/lib/esm/solana";
import {
CHAINS,
CONTRACTS,
SolanaChainName,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import {
createUpgradeContractInstruction as createWormholeUpgradeContractInstruction,
createUpgradeGuardianSetInstruction,
} from "@certusone/wormhole-sdk/lib/esm/solana/wormhole";
createRegisterChainInstruction as createNFTBridgeRegisterChainInstruction,
createUpgradeContractInstruction as createNFTBridgeUpgradeContractInstruction,
} from "@certusone/wormhole-sdk/lib/esm/solana/nftBridge";
import {
createCompleteTransferNativeInstruction,
createCompleteTransferWrappedInstruction,
@ -20,9 +11,18 @@ import {
createUpgradeContractInstruction as createTokenBridgeUpgradeContractInstruction,
} from "@certusone/wormhole-sdk/lib/esm/solana/tokenBridge";
import {
createRegisterChainInstruction as createNFTBridgeRegisterChainInstruction,
createUpgradeContractInstruction as createNFTBridgeUpgradeContractInstruction,
} from "@certusone/wormhole-sdk/lib/esm/solana/nftBridge";
createUpgradeGuardianSetInstruction,
createUpgradeContractInstruction as createWormholeUpgradeContractInstruction,
} from "@certusone/wormhole-sdk/lib/esm/solana/wormhole";
import {
CHAINS,
CONTRACTS,
SolanaChainName,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import * as web3s from "@solana/web3.js";
import base58 from "bs58";
import { NETWORKS } from "./networks";
import { Payload, VAA, impossible } from "./vaa";
export async function execute_solana(
v: VAA<Payload>,
@ -30,20 +30,36 @@ export async function execute_solana(
network: "MAINNET" | "TESTNET" | "DEVNET",
chain: SolanaChainName
) {
const { rpc, key } = NETWORKS[network][chain];
if (!key) {
throw Error(`No ${network} key defined for NEAR`);
}
if (!rpc) {
throw Error(`No ${network} rpc defined for NEAR`);
}
const connection = setupConnection(rpc);
const from = web3s.Keypair.fromSecretKey(base58.decode(key));
const contracts = CONTRACTS[network][chain];
if (!contracts.core) {
throw new Error(`Core bridge address not defined for ${chain} ${network}`);
}
if (!contracts.nft_bridge) {
throw new Error(`NFT bridge address not defined for ${chain} ${network}`);
}
if (!contracts.token_bridge) {
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);
let ix: web3s.TransactionInstruction;
const connection = setupConnection(NETWORKS[network][chain].rpc);
const bridgeId = new web3s.PublicKey(CONTRACTS[network][chain].core);
const tokenBridgeId =
CONTRACTS[network][chain].token_bridge &&
new web3s.PublicKey(CONTRACTS[network][chain].token_bridge);
const nftBridgeId =
CONTRACTS[network][chain].nft_bridge &&
new web3s.PublicKey(CONTRACTS[network][chain].nft_bridge);
const from = web3s.Keypair.fromSecretKey(
base58.decode(NETWORKS[network][chain].key)
);
switch (v.payload.module) {
case "Core":
if (bridgeId === undefined) {
@ -194,6 +210,5 @@ export async function execute_solana(
console.log("SIGNATURE", signature);
}
function setupConnection(rpc: string): web3s.Connection {
return new web3s.Connection(rpc, "confirmed");
}
const setupConnection = (rpc: string): web3s.Connection =>
new web3s.Connection(rpc, "confirmed");

View File

@ -1,6 +1,6 @@
import { spawnSync } from 'child_process';
import { spawnSync } from "child_process";
export const validator_args = {
export const VALIDATOR_OPTIONS = {
alias: "a",
type: "string",
array: true,
@ -8,9 +8,9 @@ export const validator_args = {
describe: "Additional args to validator",
} as const;
export function runCommand(baseCmd: string, args: readonly string[]) {
const args_string = args.map(a => `"${a}"`).join(" ");
export const runCommand = (baseCmd: string, args: readonly string[]): void => {
const args_string = args.map((a) => `"${a}"`).join(" ");
const cmd = `${baseCmd} ${args_string}`;
console.log("\x1b[33m%s\x1b[0m", cmd);
spawnSync(cmd, { shell: true, stdio: "inherit" });
}
};

View File

@ -1,16 +1,16 @@
import { parseAttestMetaVaa } from "@certusone/wormhole-sdk/lib/esm/vaa/tokenBridge";
import { getForeignAssetSui } from "@certusone/wormhole-sdk/lib/esm/token_bridge/getForeignAsset";
import { getWrappedCoinType } from "@certusone/wormhole-sdk/lib/esm/sui";
import {
createWrappedOnSui,
createWrappedOnSuiPrepare,
} from "@certusone/wormhole-sdk/lib/esm/token_bridge/createWrapped";
import { assertChain } from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import { getWrappedCoinType } from "@certusone/wormhole-sdk/lib/esm/sui";
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";
@ -23,6 +23,7 @@ import {
isSuiCreateEvent,
isSuiPublishEvent,
registerChain,
setMaxGasBudgetDevnet,
} from "./utils";
export const submit = async (
@ -114,10 +115,16 @@ export const submit = async (
);
setMaxGasBudgetDevnet(network, prepareTx);
const prepareRes = await executeTransactionBlock(signer, prepareTx);
assertSuccess(prepareRes, "Prepare registration failed.");
const coinPackageId =
prepareRes.objectChanges.find(isSuiPublishEvent).packageId;
console.log(` Digest ${prepareRes.digest}`);
assertSuccess(prepareRes, "Prepare registration failed.");
// Get the coin package ID from the publish event
const coinPackageId =
prepareRes.objectChanges?.find(isSuiPublishEvent)?.packageId;
if (!coinPackageId) {
throw new Error("Publish coin failed.");
}
console.log(` Published to ${coinPackageId}`);
console.log(` Type ${getWrappedCoinType(coinPackageId)}`);
@ -130,10 +137,17 @@ export const submit = async (
console.log("\n[2/2] Registering asset...");
const wrappedAssetSetup = prepareRes.objectChanges
.filter(isSuiCreateEvent)
?.filter(isSuiCreateEvent)
.find((e) =>
/create_wrapped::WrappedAssetSetup/.test(e.objectType)
);
if (!wrappedAssetSetup) {
throw new Error(
"Wrapped asset setup not found. Object changes: " +
JSON.stringify(prepareRes.objectChanges)
);
}
const completeTx = await createWrappedOnSui(
provider,
coreBridgeStateObjectId,
@ -191,21 +205,6 @@ export const submit = async (
console.warn = consoleWarnTemp;
};
/**
* Currently, (Sui SDK version 0.32.2 and Sui 1.0.0 testnet), there is a
* mismatch in the max gas budget that causes an error when executing a
* transaction. Because these values are hardcoded, we set the max gas budget
* as a temporary workaround.
* @param network
* @param tx
*/
const setMaxGasBudgetDevnet = (network: Network, tx: TransactionBlock) => {
if (network === "DEVNET") {
// Avoid Error checking transaction input objects: GasBudgetTooHigh { gas_budget: 50000000000, max_budget: 10000000000 }
tx.setGasBudget(10000000000);
}
};
const sleep = (ms: number): Promise<void> => {
return new Promise((resolve) => setTimeout(resolve, ms));
};

View File

@ -11,6 +11,7 @@ import {
getPublishedObjectChanges,
normalizeSuiAddress,
} from "@mysten/sui.js";
import { DynamicFieldPage } from "@mysten/sui.js/dist/types/dynamic_fields";
import { NETWORKS } from "../networks";
import { Network } from "../utils";
import { Payload, VAA, parse, serialiseVAA } from "../vaa";
@ -71,7 +72,7 @@ export const findOwnedObjectByType = async (
throw new SuiRpcValidationError(res);
}
const object = res.data.find((d) => d.data.type === type);
const object = res.data.find((d) => d.data?.type === type);
if (!object && res.hasNextPage) {
return findOwnedObjectByType(
@ -83,19 +84,31 @@ export const findOwnedObjectByType = async (
} else if (!object && !res.hasNextPage) {
return null;
} else {
return object.data.objectId;
return object?.data?.objectId ?? null;
}
};
export const getCreatedObjects = (
res: SuiTransactionBlockResponse
): { type: string; objectId: string; owner: string }[] => {
return res.objectChanges.filter(isSuiCreateEvent).map((e) => ({
): { type: string; objectId: string; owner: string }[] =>
res.objectChanges?.filter(isSuiCreateEvent).map((e) => {
let owner: string;
if (typeof e.owner === "string") {
owner = e.owner;
} else if ("AddressOwner" in e.owner) {
owner = e.owner.AddressOwner;
} else if ("ObjectOwner" in e.owner) {
owner = e.owner.ObjectOwner;
} else {
owner = "Shared";
}
return {
owner,
type: e.objectType,
objectId: e.objectId,
owner: e.owner["AddressOwner"] || e.owner["ObjectOwner"] || e.owner,
}));
};
};
}) ?? [];
export const getOwnedObjectId = async (
provider: JsonRpcProvider,
@ -127,7 +140,7 @@ export const getOwnedObjectId = async (
const objects = res.data.filter((o) => o.data?.objectId);
if (objects.length === 1) {
return objects[0].data?.objectId;
return objects[0].data?.objectId ?? null;
} else if (objects.length > 1) {
const objectsStr = JSON.stringify(objects, null, 2);
throw new Error(
@ -148,18 +161,19 @@ export const getOwnedObjectId = async (
};
// TODO(kp): remove this once it's in the sdk
export async function getPackageId(
export const getPackageId = async (
provider: JsonRpcProvider,
objectId: string
): Promise<string> {
): Promise<string> => {
let currentPackage;
let nextCursor;
do {
const dynamicFields = await provider.getDynamicFields({
const dynamicFields: DynamicFieldPage = await provider.getDynamicFields({
parentId: objectId,
cursor: nextCursor,
});
currentPackage = dynamicFields.data.find((field) =>
currentPackage = dynamicFields.data.find(
(field: DynamicFieldPage["data"][number]) =>
field.name.type.endsWith("CurrentPackage")
);
nextCursor = dynamicFields.hasNextPage ? dynamicFields.nextCursor : null;
@ -167,6 +181,7 @@ export async function getPackageId(
if (!currentPackage) {
throw new Error("CurrentPackage not found");
}
const obj = await provider.getObject({
id: currentPackage.objectId,
options: {
@ -180,8 +195,9 @@ export async function getPackageId(
if (!packageId) {
throw new Error("Unable to get current package");
}
return packageId;
}
};
export const getProvider = (
network?: Network,
@ -191,7 +207,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}`);
}
@ -265,7 +281,7 @@ export const getUpgradeCapObjectId = async (
);
if (objects.length === 1) {
// We've found the object we're looking for
return objects[0].data?.objectId;
return objects[0].data?.objectId ?? null;
} else if (objects.length > 1) {
const objectsStr = JSON.stringify(objects, null, 2);
throw new Error(
@ -285,26 +301,21 @@ export const isSameType = (a: string, b: string) => {
};
export const isSuiCreateEvent = <
T extends SuiTransactionBlockResponse["objectChanges"][number],
T extends NonNullable<SuiTransactionBlockResponse["objectChanges"]>[number],
K extends Extract<T, { type: "created" }>
>(
event: T
): event is K => {
return event.type === "created";
};
): event is K => event?.type === "created";
export const isSuiPublishEvent = <
T extends SuiTransactionBlockResponse["objectChanges"][number],
T extends NonNullable<SuiTransactionBlockResponse["objectChanges"]>[number],
K extends Extract<T, { type: "published" }>
>(
event: T
): event is K => {
return event.type === "published";
};
): event is K => event?.type === "published";
export const isValidSuiAddress = (objectId: string): boolean => {
return /^(0x)?[0-9a-f]{1,64}$/.test(objectId);
};
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>
@ -385,3 +396,21 @@ export const registerChain = async (
return tx;
};
/**
* Currently, (Sui SDK version 0.32.2 and Sui 1.0.0 testnet), there is a
* mismatch in the max gas budget that causes an error when executing a
* transaction. Because these values are hardcoded, we set the max gas budget
* as a temporary workaround.
* @param network
* @param tx
*/
export const setMaxGasBudgetDevnet = (
network: Network,
tx: TransactionBlock
) => {
if (network === "DEVNET") {
// Avoid Error checking transaction input objects: GasBudgetTooHigh { gas_budget: 50000000000, max_budget: 10000000000 }
tx.setGasBudget(10000000000);
}
};

View File

@ -1,3 +1,7 @@
import {
CONTRACTS,
TerraChainName,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import {
Coin,
Fee,
@ -5,33 +9,30 @@ import {
MnemonicKey,
MsgExecuteContract,
} from "@terra-money/terra.js";
import { fromUint8Array } from "js-base64";
import { impossible, Payload } from "./vaa";
import { NETWORKS } from "./networks";
import axios from "axios";
import {
CONTRACTS,
TerraChainName,
} from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import { fromUint8Array } from "js-base64";
import { NETWORKS } from "./networks";
import { Network } from "./utils";
import { Payload, impossible } from "./vaa";
export async function execute_terra(
payload: Payload,
vaa: Buffer,
network: "MAINNET" | "TESTNET" | "DEVNET",
network: Network,
chain: TerraChainName
) {
let n = NETWORKS[network][chain];
let contracts = CONTRACTS[network][chain];
): Promise<void> {
const { rpc, key, chain_id } = NETWORKS[network][chain];
const contracts = CONTRACTS[network][chain];
const terra = new LCDClient({
URL: n.rpc,
chainID: n.chain_id,
URL: rpc,
chainID: chain_id,
isClassic: chain === "terra",
});
const wallet = terra.wallet(
new MnemonicKey({
mnemonic: n.key,
mnemonic: key,
})
);
@ -39,7 +40,13 @@ export async function execute_terra(
let execute_msg: object;
switch (payload.module) {
case "Core":
case "Core": {
if (!contracts.core) {
throw new Error(
`Core bridge address not defined for ${chain} ${network}`
);
}
target_contract = contracts.core;
// sigh...
execute_msg = {
@ -59,14 +66,17 @@ export async function execute_terra(
default:
impossible(payload);
}
break;
case "NFTBridge":
if (contracts.nft_bridge === undefined) {
}
case "NFTBridge": {
if (!contracts.nft_bridge) {
// 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 terra");
throw new Error(`NFT bridge not supported yet for ${chain}`);
}
target_contract = contracts.nft_bridge;
execute_msg = {
submit_vaa: {
@ -88,8 +98,16 @@ export async function execute_terra(
default:
impossible(payload);
}
break;
case "TokenBridge":
}
case "TokenBridge": {
if (!contracts.token_bridge) {
throw new Error(
`Token bridge address not defined for ${chain} ${network}`
);
}
target_contract = contracts.token_bridge;
execute_msg = {
submit_vaa: {
@ -115,9 +133,10 @@ export async function execute_terra(
throw Error("Can't complete payload 3 transfer from CLI");
default:
impossible(payload);
}
break;
}
break;
default:
target_contract = impossible(payload);
execute_msg = impossible(payload);
@ -131,11 +150,9 @@ export async function execute_terra(
);
const feeDenoms = ["uluna"];
const gasPrices = await axios
.get("https://terra-classic-fcd.publicnode.com/v1/txs/gas_prices")
.then((result) => result.data);
const feeEstimate = await terra.tx.estimateFee(
[
{
@ -151,7 +168,7 @@ export async function execute_terra(
}
);
wallet
return wallet
.createAndSignTx({
msgs: [transaction],
memo: "",

View File

@ -1,7 +1,7 @@
import { Parser } from "binary-parser"
import * as elliptic from "elliptic"
import { BigNumber, ethers } from "ethers"
import { solidityKeccak256 } from "ethers/lib/utils"
import * as elliptic from "elliptic"
export interface Signature {
guardianSetIndex: number
@ -103,7 +103,7 @@ export function parse(buffer: Buffer): VAA<Payload | Other> {
if (payload === null) {
payload = {type: "Other", hex: Buffer.from(vaa.payload).toString("hex"), ascii: Buffer.from(vaa.payload).toString('utf8')}
} else {
delete payload['tokenURILength']
delete (payload as any)['tokenURILength']
}
var myVAA = { ...vaa, payload }
@ -829,7 +829,7 @@ function nftBridgeTransferParser(): P<NFTBridgeTransfer> {
.array("tokenURI", {
type: "uint8",
lengthInBytes: function() {
return this.tokenURILength
return (this as any).tokenURILength
},
formatter: (arr: Uint8Array) => Buffer.from(arr).toString("utf8")
})
@ -868,7 +868,7 @@ function serialiseNFTBridgeTransfer(payload: NFTBridgeTransfer): string {
// in, this function just throws. If the enum type is extended with new cases,
// the call to this function will then fail to compile, drawing attention to an
// unhandled case somewhere.
export function impossible(a: never): any {
export function impossible(a: never): never {
throw new Error(`Impossible: ${a}`)
}

View File

@ -1,3 +1,4 @@
import { CONTRACTS } from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import {
Coin,
Fee,
@ -6,35 +7,44 @@ import {
MsgExecuteContract,
} from "@xpla/xpla.js";
import { fromUint8Array } from "js-base64";
import { impossible, Payload } from "./vaa";
import { NETWORKS } from "./networks";
import { CONTRACTS } from "@certusone/wormhole-sdk/lib/esm/utils/consts";
import { Network } from "./utils";
import { Payload, impossible } from "./vaa";
export async function execute_xpla(
payload: Payload,
vaa: Buffer,
network: "MAINNET" | "TESTNET" | "DEVNET"
network: Network
) {
const chain = "xpla";
let n = NETWORKS[network][chain];
let contracts = CONTRACTS[network][chain];
const { rpc, key, chain_id } = NETWORKS[network].xpla;
const contracts = CONTRACTS[network].xpla;
if (!key) {
throw Error(`No ${network} key defined for XPLA`);
}
if (!rpc) {
throw Error(`No ${network} rpc defined for XPLA`);
}
const client = new LCDClient({
URL: n.rpc,
chainID: n.chain_id,
URL: rpc,
chainID: chain_id,
});
const wallet = client.wallet(
new MnemonicKey({
mnemonic: n.key,
mnemonic: key,
})
);
let target_contract: string;
let execute_msg: object;
switch (payload.module) {
case "Core":
case "Core": {
if (!contracts.core) {
throw new Error(`Core bridge address not defined for XPLA ${network}`);
}
target_contract = contracts.core;
execute_msg = {
submit_v_a_a: {
@ -53,14 +63,17 @@ export async function execute_xpla(
default:
impossible(payload);
}
break;
case "NFTBridge":
if (contracts.nft_bridge === undefined) {
}
case "NFTBridge": {
if (!contracts.nft_bridge) {
// 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;
execute_msg = {
submit_vaa: {
@ -82,8 +95,14 @@ export async function execute_xpla(
default:
impossible(payload);
}
break;
case "TokenBridge":
}
case "TokenBridge": {
if (!contracts.token_bridge) {
throw new Error(`Token bridge address not defined for XPLA ${network}`);
}
target_contract = contracts.token_bridge;
execute_msg = {
submit_vaa: {
@ -109,9 +128,10 @@ export async function execute_xpla(
throw Error("Can't complete payload 3 transfer from CLI");
default:
impossible(payload);
}
break;
}
break;
default:
target_contract = impossible(payload);
execute_msg = impossible(payload);
@ -121,15 +141,13 @@ export async function execute_xpla(
wallet.key.accAddress,
target_contract,
execute_msg,
{ axpla: 1700000000000000000 }
{ axpla: "1700000000000000000" }
);
const feeDenoms = ["axpla"];
// const gasPrices = await axios
// .get("https://dimension-lcd.xpla.dev/v1/txs/gas_prices")
// .then((result) => result.data);
const feeEstimate = await client.tx.estimateFee(
[
{

View File

@ -6,7 +6,8 @@
"outDir": "./build",
"moduleResolution": "node",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true
"forceConsistentCasingInFileNames": true,
"strict": true
},
"include": ["src"]
}