clients/js: Added generate RecoverChainId cmd, split cmds into separate files
This commit is contained in:
parent
992efa44a3
commit
4c7df41984
|
@ -45,6 +45,8 @@ export async function execute_algorand(
|
||||||
case "ContractUpgrade":
|
case "ContractUpgrade":
|
||||||
console.log("Upgrading core contract");
|
console.log("Upgrading core contract");
|
||||||
break;
|
break;
|
||||||
|
case "RecoverChainId":
|
||||||
|
throw new Error("RecoverChainId not supported on algorand")
|
||||||
default:
|
default:
|
||||||
impossible(payload);
|
impossible(payload);
|
||||||
}
|
}
|
||||||
|
@ -61,6 +63,8 @@ export async function execute_algorand(
|
||||||
case "ContractUpgrade":
|
case "ContractUpgrade":
|
||||||
console.log("Upgrading contract");
|
console.log("Upgrading contract");
|
||||||
break;
|
break;
|
||||||
|
case "RecoverChainId":
|
||||||
|
throw new Error("RecoverChainId not supported on algorand")
|
||||||
case "RegisterChain":
|
case "RegisterChain":
|
||||||
console.log("Registering chain");
|
console.log("Registering chain");
|
||||||
break;
|
break;
|
||||||
|
@ -80,6 +84,8 @@ export async function execute_algorand(
|
||||||
case "ContractUpgrade":
|
case "ContractUpgrade":
|
||||||
console.log("Upgrading contract");
|
console.log("Upgrading contract");
|
||||||
break;
|
break;
|
||||||
|
case "RecoverChainId":
|
||||||
|
throw new Error("RecoverChainId not supported on algorand")
|
||||||
case "RegisterChain":
|
case "RegisterChain":
|
||||||
console.log("Registering chain");
|
console.log("Registering chain");
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -34,6 +34,8 @@ export async function execute_aptos(
|
||||||
console.log("Upgrading core contract")
|
console.log("Upgrading core contract")
|
||||||
await callEntryFunc(network, rpc, `${contract}::contract_upgrade`, "submit_vaa_entry", [], [bcsVAA]);
|
await callEntryFunc(network, rpc, `${contract}::contract_upgrade`, "submit_vaa_entry", [], [bcsVAA]);
|
||||||
break
|
break
|
||||||
|
case "RecoverChainId":
|
||||||
|
throw new Error("RecoverChainId not supported on aptos")
|
||||||
default:
|
default:
|
||||||
impossible(payload)
|
impossible(payload)
|
||||||
}
|
}
|
||||||
|
@ -48,6 +50,8 @@ export async function execute_aptos(
|
||||||
console.log("Upgrading contract")
|
console.log("Upgrading contract")
|
||||||
await callEntryFunc(network, rpc, `${contract}::contract_upgrade`, "submit_vaa_entry", [], [bcsVAA]);
|
await callEntryFunc(network, rpc, `${contract}::contract_upgrade`, "submit_vaa_entry", [], [bcsVAA]);
|
||||||
break
|
break
|
||||||
|
case "RecoverChainId":
|
||||||
|
throw new Error("RecoverChainId not supported on aptos")
|
||||||
case "RegisterChain":
|
case "RegisterChain":
|
||||||
console.log("Registering chain")
|
console.log("Registering chain")
|
||||||
await callEntryFunc(network, rpc, `${contract}::register_chain`, "submit_vaa_entry", [], [bcsVAA]);
|
await callEntryFunc(network, rpc, `${contract}::register_chain`, "submit_vaa_entry", [], [bcsVAA]);
|
||||||
|
@ -57,6 +61,8 @@ export async function execute_aptos(
|
||||||
await callEntryFunc(network, rpc, `${contract}::complete_transfer`, "submit_vaa_entry", [], [bcsVAA]);
|
await callEntryFunc(network, rpc, `${contract}::complete_transfer`, "submit_vaa_entry", [], [bcsVAA]);
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
impossible(payload)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case "TokenBridge":
|
case "TokenBridge":
|
||||||
|
@ -69,6 +75,8 @@ export async function execute_aptos(
|
||||||
console.log("Upgrading contract")
|
console.log("Upgrading contract")
|
||||||
await callEntryFunc(network, rpc, `${contract}::contract_upgrade`, "submit_vaa_entry", [], [bcsVAA]);
|
await callEntryFunc(network, rpc, `${contract}::contract_upgrade`, "submit_vaa_entry", [], [bcsVAA]);
|
||||||
break
|
break
|
||||||
|
case "RecoverChainId":
|
||||||
|
throw new Error("RecoverChainId not supported on aptos")
|
||||||
case "RegisterChain":
|
case "RegisterChain":
|
||||||
console.log("Registering chain")
|
console.log("Registering chain")
|
||||||
await callEntryFunc(network, rpc, `${contract}::register_chain`, "submit_vaa_entry", [], [bcsVAA]);
|
await callEntryFunc(network, rpc, `${contract}::register_chain`, "submit_vaa_entry", [], [bcsVAA]);
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import { assertChain, CHAIN_ID_APTOS, CHAIN_ID_SOLANA, coalesceChainId, CONTRACTS } from "@certusone/wormhole-sdk/lib/cjs/utils/consts";
|
import { assertChain, CHAIN_ID_APTOS, CHAIN_ID_SOLANA, coalesceChainId, CONTRACTS } from "@certusone/wormhole-sdk/lib/cjs/utils/consts";
|
||||||
import { BCS, FaucetClient } from "aptos";
|
import { BCS, FaucetClient } from "aptos";
|
||||||
import { spawnSync } from 'child_process';
|
import { spawnSync } from 'child_process';
|
||||||
import { ethers } from "ethers";
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import sha3 from 'js-sha3';
|
import sha3 from 'js-sha3';
|
||||||
import yargs from "yargs";
|
import yargs from "yargs";
|
||||||
import { callEntryFunc, deriveResourceAccount, deriveWrappedAssetAddress } from "../aptos";
|
import { callEntryFunc, deriveResourceAccount, deriveWrappedAssetAddress } from "../aptos";
|
||||||
import { config } from '../config';
|
import { config } from '../config';
|
||||||
import { NETWORKS } from "../networks";
|
import { NETWORKS } from "../networks";
|
||||||
|
import { evm_address, hex } from "../consts";
|
||||||
|
|
||||||
type Network = "MAINNET" | "TESTNET" | "DEVNET"
|
type Network = "MAINNET" | "TESTNET" | "DEVNET"
|
||||||
|
|
||||||
|
@ -354,13 +354,6 @@ exports.builder = function(y: typeof yargs) {
|
||||||
.strict().demandCommand();
|
.strict().demandCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
function hex(x: string): string {
|
|
||||||
return ethers.utils.hexlify(x, { allowMissingPrefix: true });
|
|
||||||
}
|
|
||||||
function evm_address(x: string): string {
|
|
||||||
return hex(x).substring(2).padStart(64, "0");
|
|
||||||
}
|
|
||||||
|
|
||||||
export function checkAptosBinary(): void {
|
export function checkAptosBinary(): void {
|
||||||
const dir = `${config.wormholeDir}/aptos`;
|
const dir = `${config.wormholeDir}/aptos`;
|
||||||
const aptos = spawnSync("aptos", ["--version"]);
|
const aptos = spawnSync("aptos", ["--version"]);
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
import yargs from "yargs";
|
||||||
|
import {
|
||||||
|
CHAINS,
|
||||||
|
assertChain,
|
||||||
|
coalesceChainId,
|
||||||
|
} from "@certusone/wormhole-sdk/lib/cjs/utils/consts";
|
||||||
|
|
||||||
|
exports.command = "chain-id <chain>";
|
||||||
|
exports.desc =
|
||||||
|
"Print the wormhole chain ID integer associated with the specified chain name";
|
||||||
|
exports.builder = (y: typeof yargs) => {
|
||||||
|
return y.positional("chain", {
|
||||||
|
describe: "Chain to query",
|
||||||
|
type: "string",
|
||||||
|
choices: Object.keys(CHAINS),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
exports.handler = (argv) => {
|
||||||
|
assertChain(argv["chain"]);
|
||||||
|
console.log(coalesceChainId(argv["chain"]));
|
||||||
|
};
|
|
@ -0,0 +1,75 @@
|
||||||
|
import yargs from "yargs";
|
||||||
|
import {
|
||||||
|
CHAINS,
|
||||||
|
assertChain,
|
||||||
|
isCosmWasmChain,
|
||||||
|
} from "@certusone/wormhole-sdk/lib/cjs/utils/consts";
|
||||||
|
import { impossible } from "../vaa";
|
||||||
|
import { CONTRACTS } from "../consts";
|
||||||
|
|
||||||
|
exports.command = "contract <network> <chain> <module>";
|
||||||
|
exports.desc = "Print contract address";
|
||||||
|
exports.builder = (y: typeof yargs) => {
|
||||||
|
return y
|
||||||
|
.positional("network", {
|
||||||
|
describe: "network",
|
||||||
|
type: "string",
|
||||||
|
choices: ["mainnet", "testnet", "devnet"],
|
||||||
|
})
|
||||||
|
.positional("chain", {
|
||||||
|
describe: "Chain to query",
|
||||||
|
type: "string",
|
||||||
|
choices: Object.keys(CHAINS),
|
||||||
|
})
|
||||||
|
.positional("module", {
|
||||||
|
describe: "Module to query",
|
||||||
|
type: "string",
|
||||||
|
choices: ["Core", "NFTBridge", "TokenBridge"],
|
||||||
|
})
|
||||||
|
.option("emitter", {
|
||||||
|
alias: "e",
|
||||||
|
describe: "Print in emitter address format",
|
||||||
|
type: "boolean",
|
||||||
|
default: false,
|
||||||
|
required: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
exports.handler = async (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 = "";
|
||||||
|
switch (module) {
|
||||||
|
case "Core":
|
||||||
|
addr = CONTRACTS[network][chain]["core"];
|
||||||
|
break;
|
||||||
|
case "NFTBridge":
|
||||||
|
addr = CONTRACTS[network][chain]["nft_bridge"];
|
||||||
|
break;
|
||||||
|
case "TokenBridge":
|
||||||
|
addr = CONTRACTS[network][chain]["token_bridge"];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
impossible(module);
|
||||||
|
}
|
||||||
|
if (argv["emitter"]) {
|
||||||
|
const emitter = require("@certusone/wormhole-sdk/lib/cjs/bridge/getEmitterAddress");
|
||||||
|
if (chain === "solana" || chain === "pythnet") {
|
||||||
|
// TODO: Create an isSolanaChain()
|
||||||
|
addr = await emitter.getEmitterAddressSolana(addr);
|
||||||
|
} else if (isCosmWasmChain(chain)) {
|
||||||
|
addr = await emitter.getEmitterAddressTerra(addr);
|
||||||
|
} else if (chain === "algorand") {
|
||||||
|
addr = emitter.getEmitterAddressAlgorand(BigInt(addr));
|
||||||
|
} else if (chain === "near") {
|
||||||
|
addr = emitter.getEmitterAddressNear(addr);
|
||||||
|
} else {
|
||||||
|
addr = emitter.getEmitterAddressEth(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(addr);
|
||||||
|
};
|
|
@ -0,0 +1,198 @@
|
||||||
|
import yargs from "yargs";
|
||||||
|
import { ethers } from "ethers";
|
||||||
|
import { NETWORKS } from "../networks";
|
||||||
|
import {
|
||||||
|
assertChain,
|
||||||
|
assertEVMChain,
|
||||||
|
CHAINS,
|
||||||
|
CONTRACTS,
|
||||||
|
isEVMChain,
|
||||||
|
toChainName,
|
||||||
|
} from "@certusone/wormhole-sdk/lib/cjs/utils/consts";
|
||||||
|
import { evm_address } from "../consts";
|
||||||
|
|
||||||
|
exports.command = "evm";
|
||||||
|
exports.desc = "EVM utilities";
|
||||||
|
exports.builder = function (y: typeof yargs) {
|
||||||
|
const evm = require("../evm");
|
||||||
|
return y
|
||||||
|
.option("rpc", {
|
||||||
|
describe: "RPC endpoint",
|
||||||
|
type: "string",
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
.command(
|
||||||
|
"address-from-secret <secret>",
|
||||||
|
"Compute a 20 byte eth address from a 32 byte private key",
|
||||||
|
(yargs) => {
|
||||||
|
return yargs.positional("secret", {
|
||||||
|
type: "string",
|
||||||
|
describe: "Secret key (32 bytes)",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
(argv) => {
|
||||||
|
console.log(ethers.utils.computeAddress(argv["secret"]));
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.command(
|
||||||
|
"storage-update",
|
||||||
|
"Update a storage slot on an EVM fork during testing (anvil or hardhat)",
|
||||||
|
(yargs) => {
|
||||||
|
return yargs
|
||||||
|
.option("contract-address", {
|
||||||
|
alias: "a",
|
||||||
|
describe: "Contract address",
|
||||||
|
type: "string",
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
.option("storage-slot", {
|
||||||
|
alias: "k",
|
||||||
|
describe: "Storage slot to modify",
|
||||||
|
type: "string",
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
.option("value", {
|
||||||
|
alias: "v",
|
||||||
|
describe: "Value to write into the slot (32 bytes)",
|
||||||
|
type: "string",
|
||||||
|
required: true,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async (argv) => {
|
||||||
|
const result = await evm.setStorageAt(
|
||||||
|
argv["rpc"],
|
||||||
|
evm_address(argv["contract-address"]),
|
||||||
|
argv["storage-slot"],
|
||||||
|
["uint256"],
|
||||||
|
[argv["value"]]
|
||||||
|
);
|
||||||
|
console.log(result);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.command("chains", "Return all EVM chains", async (_) => {
|
||||||
|
console.log(
|
||||||
|
Object.values(CHAINS)
|
||||||
|
.map((id) => toChainName(id))
|
||||||
|
.filter((name) => isEVMChain(name))
|
||||||
|
.join(" ")
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.command(
|
||||||
|
"info",
|
||||||
|
"Query info about the on-chain state of the contract",
|
||||||
|
(yargs) => {
|
||||||
|
return yargs
|
||||||
|
.option("chain", {
|
||||||
|
alias: "c",
|
||||||
|
describe: "Chain to query",
|
||||||
|
type: "string",
|
||||||
|
choices: Object.keys(CHAINS),
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
.option("module", {
|
||||||
|
alias: "m",
|
||||||
|
describe: "Module to query",
|
||||||
|
type: "string",
|
||||||
|
choices: ["Core", "NFTBridge", "TokenBridge"],
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
.option("network", {
|
||||||
|
alias: "n",
|
||||||
|
describe: "network",
|
||||||
|
type: "string",
|
||||||
|
choices: ["mainnet", "testnet", "devnet"],
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
.option("contract-address", {
|
||||||
|
alias: "a",
|
||||||
|
describe: "Contract to query (override config)",
|
||||||
|
type: "string",
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
.option("implementation-only", {
|
||||||
|
alias: "i",
|
||||||
|
describe: "Only query implementation (faster)",
|
||||||
|
type: "boolean",
|
||||||
|
default: false,
|
||||||
|
required: 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;
|
||||||
|
if (argv["implementation-only"]) {
|
||||||
|
console.log(
|
||||||
|
await evm.getImplementation(
|
||||||
|
network,
|
||||||
|
argv["chain"],
|
||||||
|
module,
|
||||||
|
argv["contract-address"],
|
||||||
|
rpc
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
JSON.stringify(
|
||||||
|
await evm.query_contract_evm(
|
||||||
|
network,
|
||||||
|
argv["chain"],
|
||||||
|
module,
|
||||||
|
argv["contract-address"],
|
||||||
|
rpc
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.command(
|
||||||
|
"hijack",
|
||||||
|
"Override the guardian set of the core bridge contract during testing (anvil or hardhat)",
|
||||||
|
(yargs) => {
|
||||||
|
return yargs
|
||||||
|
.option("core-contract-address", {
|
||||||
|
alias: "a",
|
||||||
|
describe: "Core contract address",
|
||||||
|
type: "string",
|
||||||
|
default: CONTRACTS.MAINNET.ethereum.core,
|
||||||
|
})
|
||||||
|
.option("guardian-address", {
|
||||||
|
alias: "g",
|
||||||
|
required: true,
|
||||||
|
describe: "Guardians' public addresses (CSV)",
|
||||||
|
type: "string",
|
||||||
|
})
|
||||||
|
.option("guardian-set-index", {
|
||||||
|
alias: "i",
|
||||||
|
required: 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;
|
||||||
|
await evm.hijack_evm(
|
||||||
|
rpc,
|
||||||
|
argv["core-contract-address"],
|
||||||
|
guardian_addresses,
|
||||||
|
argv["guardian-set-index"]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.strict()
|
||||||
|
.demandCommand();
|
||||||
|
};
|
|
@ -0,0 +1,316 @@
|
||||||
|
import {
|
||||||
|
CHAINS,
|
||||||
|
assertChain,
|
||||||
|
toChainId,
|
||||||
|
ChainName,
|
||||||
|
isCosmWasmChain,
|
||||||
|
isEVMChain,
|
||||||
|
} from "@certusone/wormhole-sdk/lib/cjs/utils/consts";
|
||||||
|
import { sha3_256 } from "js-sha3";
|
||||||
|
import yargs from "yargs";
|
||||||
|
import {
|
||||||
|
ContractUpgrade,
|
||||||
|
Payload,
|
||||||
|
PortalRegisterChain,
|
||||||
|
RecoverChainId,
|
||||||
|
TokenBridgeAttestMeta,
|
||||||
|
VAA,
|
||||||
|
impossible,
|
||||||
|
serialiseVAA,
|
||||||
|
sign,
|
||||||
|
} from "../vaa";
|
||||||
|
import { fromBech32, toHex } from "@cosmjs/encoding";
|
||||||
|
import base58 from "bs58";
|
||||||
|
import { evm_address, hex } from "../consts";
|
||||||
|
|
||||||
|
function makeVAA(
|
||||||
|
emitterChain: number,
|
||||||
|
emitterAddress: string,
|
||||||
|
signers: string[],
|
||||||
|
p: Payload
|
||||||
|
): VAA<Payload> {
|
||||||
|
let v: VAA<Payload> = {
|
||||||
|
version: 1,
|
||||||
|
guardianSetIndex: 0,
|
||||||
|
signatures: [],
|
||||||
|
timestamp: 1,
|
||||||
|
nonce: 1,
|
||||||
|
emitterChain: emitterChain,
|
||||||
|
emitterAddress: emitterAddress,
|
||||||
|
sequence: BigInt(Math.floor(Math.random() * 100000000)),
|
||||||
|
consistencyLevel: 0,
|
||||||
|
payload: p,
|
||||||
|
};
|
||||||
|
v.signatures = sign(signers, v);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
const GOVERNANCE_CHAIN = 1;
|
||||||
|
const GOVERNANCE_EMITTER =
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000004";
|
||||||
|
|
||||||
|
exports.command = "generate";
|
||||||
|
exports.desc = "generate VAAs (devnet and testnet only)";
|
||||||
|
exports.builder = function (y: typeof yargs) {
|
||||||
|
return (
|
||||||
|
y
|
||||||
|
.option("guardian-secret", {
|
||||||
|
alias: "g",
|
||||||
|
required: true,
|
||||||
|
describe: "Guardians' secret keys (CSV)",
|
||||||
|
type: "string",
|
||||||
|
})
|
||||||
|
// Registration
|
||||||
|
.command(
|
||||||
|
"registration",
|
||||||
|
"Generate registration VAA",
|
||||||
|
(yargs) => {
|
||||||
|
return yargs
|
||||||
|
.option("chain", {
|
||||||
|
alias: "c",
|
||||||
|
describe: "Chain to register",
|
||||||
|
type: "string",
|
||||||
|
choices: Object.keys(CHAINS),
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
.option("contract-address", {
|
||||||
|
alias: "a",
|
||||||
|
describe: "Contract to register",
|
||||||
|
type: "string",
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
.option("module", {
|
||||||
|
alias: "m",
|
||||||
|
describe: "Module to upgrade",
|
||||||
|
type: "string",
|
||||||
|
choices: ["NFTBridge", "TokenBridge"],
|
||||||
|
required: true,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
(argv) => {
|
||||||
|
let module = argv["module"] as "NFTBridge" | "TokenBridge";
|
||||||
|
assertChain(argv["chain"]);
|
||||||
|
let payload: PortalRegisterChain<typeof module> = {
|
||||||
|
module,
|
||||||
|
type: "RegisterChain",
|
||||||
|
chain: 0,
|
||||||
|
emitterChain: toChainId(argv["chain"]),
|
||||||
|
emitterAddress: parseAddress(
|
||||||
|
argv["chain"],
|
||||||
|
argv["contract-address"]
|
||||||
|
),
|
||||||
|
};
|
||||||
|
let v = makeVAA(
|
||||||
|
GOVERNANCE_CHAIN,
|
||||||
|
GOVERNANCE_EMITTER,
|
||||||
|
argv["guardian-secret"].split(","),
|
||||||
|
payload
|
||||||
|
);
|
||||||
|
console.log(serialiseVAA(v));
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// Upgrade
|
||||||
|
.command(
|
||||||
|
"upgrade",
|
||||||
|
"Generate contract upgrade VAA",
|
||||||
|
(yargs) => {
|
||||||
|
return yargs
|
||||||
|
.option("chain", {
|
||||||
|
alias: "c",
|
||||||
|
describe: "Chain to upgrade",
|
||||||
|
type: "string",
|
||||||
|
choices: Object.keys(CHAINS),
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
.option("contract-address", {
|
||||||
|
alias: "a",
|
||||||
|
describe: "Contract to upgrade to",
|
||||||
|
type: "string",
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
.option("module", {
|
||||||
|
alias: "m",
|
||||||
|
describe: "Module to upgrade",
|
||||||
|
type: "string",
|
||||||
|
choices: ["Core", "NFTBridge", "TokenBridge"],
|
||||||
|
required: true,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
(argv) => {
|
||||||
|
assertChain(argv["chain"]);
|
||||||
|
let module = argv["module"] as "Core" | "NFTBridge" | "TokenBridge";
|
||||||
|
let payload: ContractUpgrade = {
|
||||||
|
module,
|
||||||
|
type: "ContractUpgrade",
|
||||||
|
chain: toChainId(argv["chain"]),
|
||||||
|
address: parseCodeAddress(argv["chain"], argv["contract-address"]),
|
||||||
|
};
|
||||||
|
let v = makeVAA(
|
||||||
|
GOVERNANCE_CHAIN,
|
||||||
|
GOVERNANCE_EMITTER,
|
||||||
|
argv["guardian-secret"].split(","),
|
||||||
|
payload
|
||||||
|
);
|
||||||
|
console.log(serialiseVAA(v));
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.command(
|
||||||
|
"attestation",
|
||||||
|
"Generate a token attestation VAA",
|
||||||
|
(yargs) => {
|
||||||
|
return yargs
|
||||||
|
.option("emitter-chain", {
|
||||||
|
alias: "e",
|
||||||
|
describe: "Emitter chain of the VAA",
|
||||||
|
type: "string",
|
||||||
|
choices: Object.keys(CHAINS),
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
.option("emitter-address", {
|
||||||
|
alias: "f",
|
||||||
|
describe: "Emitter address of the VAA",
|
||||||
|
type: "string",
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
.option("chain", {
|
||||||
|
alias: "c",
|
||||||
|
describe: "Token's chain",
|
||||||
|
type: "string",
|
||||||
|
choices: Object.keys(CHAINS),
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
.option("token-address", {
|
||||||
|
alias: "a",
|
||||||
|
describe: "Token's address",
|
||||||
|
type: "string",
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
.option("decimals", {
|
||||||
|
alias: "d",
|
||||||
|
describe: "Token's decimals",
|
||||||
|
type: "number",
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
.option("symbol", {
|
||||||
|
alias: "s",
|
||||||
|
describe: "Token's symbol",
|
||||||
|
type: "string",
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
.option("name", {
|
||||||
|
alias: "n",
|
||||||
|
describe: "Token's name",
|
||||||
|
type: "string",
|
||||||
|
required: true,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
(argv) => {
|
||||||
|
let emitter_chain = argv["emitter-chain"] as string;
|
||||||
|
assertChain(argv["chain"]);
|
||||||
|
assertChain(emitter_chain);
|
||||||
|
let payload: TokenBridgeAttestMeta = {
|
||||||
|
module: "TokenBridge",
|
||||||
|
type: "AttestMeta",
|
||||||
|
chain: 0,
|
||||||
|
tokenAddress: parseAddress(argv["chain"], argv["token-address"]),
|
||||||
|
tokenChain: toChainId(argv["chain"]),
|
||||||
|
decimals: argv["decimals"],
|
||||||
|
symbol: argv["symbol"],
|
||||||
|
name: argv["name"],
|
||||||
|
};
|
||||||
|
let v = makeVAA(
|
||||||
|
toChainId(emitter_chain),
|
||||||
|
parseAddress(emitter_chain, argv["emitter-address"] as string),
|
||||||
|
argv["guardian-secret"].split(","),
|
||||||
|
payload
|
||||||
|
);
|
||||||
|
console.log(serialiseVAA(v));
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// RecoverChainId
|
||||||
|
.command(
|
||||||
|
"recover-chain-id",
|
||||||
|
"Generate a recover chain ID VAA",
|
||||||
|
(yargs) => {
|
||||||
|
return yargs
|
||||||
|
.option("module", {
|
||||||
|
alias: "m",
|
||||||
|
describe: "Module to upgrade",
|
||||||
|
type: "string",
|
||||||
|
choices: ["Core", "NFTBridge", "TokenBridge"],
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
.option("evm-chain-id", {
|
||||||
|
alias: "e",
|
||||||
|
describe: "EVM chain ID to set",
|
||||||
|
type: "string",
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
.option("new-chain-id", {
|
||||||
|
alias: "c",
|
||||||
|
describe: "New chain ID to set",
|
||||||
|
type: "number",
|
||||||
|
required: true,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
(argv) => {
|
||||||
|
let module = argv["module"] as "Core" | "NFTBridge" | "TokenBridge";
|
||||||
|
let payload: RecoverChainId = {
|
||||||
|
module,
|
||||||
|
type: "RecoverChainId",
|
||||||
|
evmChainId: BigInt(argv["evm-chain-id"]),
|
||||||
|
newChainId: argv["new-chain-id"],
|
||||||
|
};
|
||||||
|
let v = makeVAA(
|
||||||
|
GOVERNANCE_CHAIN,
|
||||||
|
GOVERNANCE_EMITTER,
|
||||||
|
argv["guardian-secret"].split(","),
|
||||||
|
payload
|
||||||
|
);
|
||||||
|
console.log(serialiseVAA(v));
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
function parseAddress(chain: ChainName, address: string): string {
|
||||||
|
if (chain === "unset") {
|
||||||
|
throw Error("Chain unset");
|
||||||
|
} else if (isEVMChain(chain)) {
|
||||||
|
return "0x" + evm_address(address);
|
||||||
|
} else if (isCosmWasmChain(chain)) {
|
||||||
|
return "0x" + toHex(fromBech32(address).data).padStart(64, "0");
|
||||||
|
} else if (chain === "solana" || chain === "pythnet") {
|
||||||
|
return "0x" + toHex(base58.decode(address)).padStart(64, "0");
|
||||||
|
} else if (chain === "algorand") {
|
||||||
|
// TODO: is there a better native format for algorand?
|
||||||
|
return "0x" + evm_address(address);
|
||||||
|
} else if (chain === "near") {
|
||||||
|
return "0x" + hex(address).substring(2).padStart(64, "0");
|
||||||
|
} else if (chain === "osmosis") {
|
||||||
|
throw Error("OSMOSIS is not supported yet");
|
||||||
|
} else if (chain === "sui") {
|
||||||
|
throw Error("SUI is not supported yet");
|
||||||
|
} else if (chain === "aptos") {
|
||||||
|
if (/^(0x)?[0-9a-fA-F]+$/.test(address)) {
|
||||||
|
return "0x" + evm_address(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sha3_256(Buffer.from(address)); // address is hash of fully qualified type
|
||||||
|
} else if (chain === "wormchain") {
|
||||||
|
const sdk = require("@certusone/wormhole-sdk/lib/cjs/utils/array");
|
||||||
|
return "0x" + sdk.tryNativeToHexString(address, chain);
|
||||||
|
} else if (chain === "btc") {
|
||||||
|
throw Error("btc is not supported yet");
|
||||||
|
} else {
|
||||||
|
impossible(chain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseCodeAddress(chain: ChainName, address: string): string {
|
||||||
|
if (isCosmWasmChain(chain)) {
|
||||||
|
return "0x" + parseInt(address, 10).toString(16).padStart(64, "0");
|
||||||
|
} else {
|
||||||
|
return parseAddress(chain, address);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
import yargs from "yargs";
|
||||||
|
|
||||||
|
// Near utilities
|
||||||
|
exports.command = "near";
|
||||||
|
exports.desc = "NEAR utilities";
|
||||||
|
exports.builder = function (y: typeof yargs) {
|
||||||
|
const near = require("../near");
|
||||||
|
return y
|
||||||
|
.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,
|
||||||
|
})
|
||||||
|
.option("account", {
|
||||||
|
describe: "near deployment account",
|
||||||
|
type: "string",
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
.option("attach", {
|
||||||
|
describe: "attach some near",
|
||||||
|
type: "string",
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
.option("target", {
|
||||||
|
describe: "near account to upgrade",
|
||||||
|
type: "string",
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
.option("mnemonic", {
|
||||||
|
describe: "near private keys",
|
||||||
|
type: "string",
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
.option("keys", {
|
||||||
|
describe: "near private keys",
|
||||||
|
type: "string",
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
.command(
|
||||||
|
"contract-update <file>",
|
||||||
|
"Submit a contract update using our specific APIs",
|
||||||
|
(yargs) => {
|
||||||
|
return yargs.positional("file", {
|
||||||
|
type: "string",
|
||||||
|
describe: "wasm",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async (argv) => {
|
||||||
|
await near.upgrade_near(argv);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.command(
|
||||||
|
"deploy <file>",
|
||||||
|
"Submit a contract update using near APIs",
|
||||||
|
(yargs) => {
|
||||||
|
return yargs.positional("file", {
|
||||||
|
type: "string",
|
||||||
|
describe: "wasm",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async (argv) => {
|
||||||
|
await near.deploy_near(argv);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,29 @@
|
||||||
|
import yargs from "yargs";
|
||||||
|
import { parse, vaaDigest } from "../vaa";
|
||||||
|
|
||||||
|
exports.command = "parse <vaa>";
|
||||||
|
exports.desc = "Parse a VAA (can be in either hex or base64 format)";
|
||||||
|
exports.builder = (y: typeof yargs) => {
|
||||||
|
return y.positional("vaa", {
|
||||||
|
describe: "vaa",
|
||||||
|
type: "string",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
exports.handler = (argv) => {
|
||||||
|
let buf: Buffer;
|
||||||
|
try {
|
||||||
|
buf = Buffer.from(String(argv.vaa), "hex");
|
||||||
|
if (buf.length == 0) {
|
||||||
|
throw Error("Couldn't parse VAA as hex");
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
buf = Buffer.from(String(argv.vaa), "base64");
|
||||||
|
if (buf.length == 0) {
|
||||||
|
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(parsed_vaa_with_digest);
|
||||||
|
};
|
|
@ -0,0 +1,22 @@
|
||||||
|
import yargs from "yargs";
|
||||||
|
import { ethers } from "ethers";
|
||||||
|
import { hex } from "../consts";
|
||||||
|
|
||||||
|
exports.command = "recover <digest> <signature>";
|
||||||
|
exports.desc = "Recover an address from a signature";
|
||||||
|
exports.builder = (y: typeof yargs) => {
|
||||||
|
return y
|
||||||
|
.positional("digest", {
|
||||||
|
describe: "digest",
|
||||||
|
type: "string",
|
||||||
|
})
|
||||||
|
.positional("signature", {
|
||||||
|
describe: "signature",
|
||||||
|
type: "string",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
exports.handler = async (argv) => {
|
||||||
|
console.log(
|
||||||
|
ethers.utils.recoverAddress(hex(argv["digest"]), hex(argv["signature"]))
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,30 @@
|
||||||
|
import yargs from "yargs";
|
||||||
|
import {
|
||||||
|
CHAINS,
|
||||||
|
assertChain,
|
||||||
|
} from "@certusone/wormhole-sdk/lib/cjs/utils/consts";
|
||||||
|
import { NETWORKS } from "../networks";
|
||||||
|
|
||||||
|
exports.command = "rpc <network> <chain>";
|
||||||
|
exports.desc = "Print RPC address";
|
||||||
|
exports.builder = (y: typeof yargs) => {
|
||||||
|
return y
|
||||||
|
.positional("network", {
|
||||||
|
describe: "network",
|
||||||
|
type: "string",
|
||||||
|
choices: ["mainnet", "testnet", "devnet"],
|
||||||
|
})
|
||||||
|
.positional("chain", {
|
||||||
|
describe: "Chain to query",
|
||||||
|
type: "string",
|
||||||
|
choices: Object.keys(CHAINS),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
exports.handler = async (argv) => {
|
||||||
|
assertChain(argv["chain"]);
|
||||||
|
const network = argv.network.toUpperCase();
|
||||||
|
if (network !== "MAINNET" && network !== "TESTNET" && network !== "DEVNET") {
|
||||||
|
throw Error(`Unknown network: ${network}`);
|
||||||
|
}
|
||||||
|
console.log(NETWORKS[network][argv["chain"]].rpc);
|
||||||
|
};
|
|
@ -0,0 +1,155 @@
|
||||||
|
import yargs from "yargs";
|
||||||
|
import {
|
||||||
|
CHAINS,
|
||||||
|
assertChain,
|
||||||
|
toChainName,
|
||||||
|
ChainName,
|
||||||
|
isEVMChain,
|
||||||
|
isTerraChain,
|
||||||
|
coalesceChainName,
|
||||||
|
} from "@certusone/wormhole-sdk/lib/cjs/utils/consts";
|
||||||
|
import * as vaa from "../vaa";
|
||||||
|
|
||||||
|
exports.command = "submit <vaa>";
|
||||||
|
exports.desc = "Execute a VAA";
|
||||||
|
exports.builder = (y: typeof yargs) => {
|
||||||
|
return y
|
||||||
|
.positional("vaa", {
|
||||||
|
describe: "vaa",
|
||||||
|
type: "string",
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
.option("chain", {
|
||||||
|
alias: "c",
|
||||||
|
describe: "chain name",
|
||||||
|
type: "string",
|
||||||
|
choices: Object.keys(CHAINS),
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
.option("network", {
|
||||||
|
alias: "n",
|
||||||
|
describe: "network",
|
||||||
|
type: "string",
|
||||||
|
choices: ["mainnet", "testnet", "devnet"],
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
.option("contract-address", {
|
||||||
|
alias: "a",
|
||||||
|
describe: "Contract to submit VAA to (override config)",
|
||||||
|
type: "string",
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
.option("rpc", {
|
||||||
|
describe: "RPC endpoint",
|
||||||
|
type: "string",
|
||||||
|
required: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
exports.handler = async (argv) => {
|
||||||
|
const vaa_hex = String(argv.vaa);
|
||||||
|
const buf = Buffer.from(vaa_hex, "hex");
|
||||||
|
const parsed_vaa = vaa.parse(buf);
|
||||||
|
|
||||||
|
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}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We figure out the target chain to submit the VAA to.
|
||||||
|
// The VAA might specify this itself (for example a contract upgrade VAA
|
||||||
|
// or a token transfer VAA), in which case we just submit the VAA to
|
||||||
|
// that target chain.
|
||||||
|
//
|
||||||
|
// If the VAA does not have a target (e.g. chain registration VAAs or
|
||||||
|
// guardian set upgrade VAAs), we require the '--chain' argument to be
|
||||||
|
// set on the command line.
|
||||||
|
//
|
||||||
|
// As a sanity check, in the event that the VAA does specify a target
|
||||||
|
// and the '--chain' argument is also set, we issue an error if those
|
||||||
|
// two don't agree instead of silently taking the VAA's target chain.
|
||||||
|
|
||||||
|
// get VAA chain
|
||||||
|
const vaa_chain_id =
|
||||||
|
"chain" in parsed_vaa.payload ? parsed_vaa.payload.chain : 0;
|
||||||
|
assertChain(vaa_chain_id);
|
||||||
|
const vaa_chain = toChainName(vaa_chain_id);
|
||||||
|
|
||||||
|
// get chain from command line arg
|
||||||
|
const cli_chain = argv["chain"];
|
||||||
|
|
||||||
|
let chain: ChainName;
|
||||||
|
if (cli_chain !== undefined) {
|
||||||
|
assertChain(cli_chain);
|
||||||
|
if (vaa_chain !== "unset" && cli_chain !== vaa_chain) {
|
||||||
|
throw Error(
|
||||||
|
`Specified target chain (${cli_chain}) does not match VAA target chain (${vaa_chain})`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
chain = coalesceChainName(cli_chain);
|
||||||
|
} else {
|
||||||
|
chain = vaa_chain;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chain === "unset") {
|
||||||
|
throw Error(
|
||||||
|
"This VAA does not specify the target chain, please provide it by hand using the '--chain' flag."
|
||||||
|
);
|
||||||
|
} else if (isEVMChain(chain)) {
|
||||||
|
const evm = require("../evm");
|
||||||
|
await evm.execute_evm(
|
||||||
|
parsed_vaa.payload,
|
||||||
|
buf,
|
||||||
|
network,
|
||||||
|
chain,
|
||||||
|
argv["contract-address"],
|
||||||
|
argv["rpc"]
|
||||||
|
);
|
||||||
|
} else if (isTerraChain(chain)) {
|
||||||
|
const terra = require("../terra");
|
||||||
|
await terra.execute_terra(parsed_vaa.payload, buf, network, chain);
|
||||||
|
} else if (chain === "solana" || chain === "pythnet") {
|
||||||
|
const solana = require("../solana");
|
||||||
|
await solana.execute_solana(parsed_vaa, buf, network, chain);
|
||||||
|
} else if (chain === "algorand") {
|
||||||
|
const algorand = require("../algorand");
|
||||||
|
await algorand.execute_algorand(
|
||||||
|
parsed_vaa.payload,
|
||||||
|
new Uint8Array(Buffer.from(vaa_hex, "hex")),
|
||||||
|
network
|
||||||
|
);
|
||||||
|
} else if (chain === "near") {
|
||||||
|
const near = require("../near");
|
||||||
|
await near.execute_near(parsed_vaa.payload, vaa_hex, network);
|
||||||
|
} else if (chain === "injective") {
|
||||||
|
const injective = require("../injective");
|
||||||
|
await injective.execute_injective(parsed_vaa.payload, buf, network);
|
||||||
|
} else if (chain === "xpla") {
|
||||||
|
const xpla = require("../xpla");
|
||||||
|
await xpla.execute_xpla(parsed_vaa.payload, buf, network);
|
||||||
|
} else if (chain === "osmosis") {
|
||||||
|
throw Error("OSMOSIS is not supported yet");
|
||||||
|
} else if (chain === "sui") {
|
||||||
|
throw Error("SUI is not supported yet");
|
||||||
|
} else if (chain === "aptos") {
|
||||||
|
const aptos = require("../aptos");
|
||||||
|
await aptos.execute_aptos(
|
||||||
|
parsed_vaa.payload,
|
||||||
|
buf,
|
||||||
|
network,
|
||||||
|
argv["contract-address"],
|
||||||
|
argv["rpc"]
|
||||||
|
);
|
||||||
|
} else if (chain === "wormchain") {
|
||||||
|
throw Error("Wormchain is not supported yet");
|
||||||
|
} else if (chain === "btc") {
|
||||||
|
throw Error("btc is not supported yet");
|
||||||
|
} else {
|
||||||
|
// If you get a type error here, hover over `chain`'s type and it tells you
|
||||||
|
// which cases are not handled
|
||||||
|
vaa.impossible(chain);
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { CONTRACTS as SDK_CONTRACTS } from "@certusone/wormhole-sdk/lib/cjs/utils/consts";
|
||||||
|
import { ethers } from "ethers";
|
||||||
|
|
||||||
|
const OVERRIDES = {
|
||||||
|
MAINNET: {
|
||||||
|
aptos: {
|
||||||
|
token_bridge:
|
||||||
|
"0x576410486a2da45eee6c949c995670112ddf2fbeedab20350d506328eefc9d4f",
|
||||||
|
core: "0x5bc11445584a763c1fa7ed39081f1b920954da14e04b32440cba863d03e19625",
|
||||||
|
nft_bridge:
|
||||||
|
"0x1bdffae984043833ed7fe223f7af7a3f8902d04129b14f801823e64827da7130",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TESTNET: {
|
||||||
|
aptos: {
|
||||||
|
token_bridge:
|
||||||
|
"0x576410486a2da45eee6c949c995670112ddf2fbeedab20350d506328eefc9d4f",
|
||||||
|
core: "0x5bc11445584a763c1fa7ed39081f1b920954da14e04b32440cba863d03e19625",
|
||||||
|
nft_bridge:
|
||||||
|
"0x1bdffae984043833ed7fe223f7af7a3f8902d04129b14f801823e64827da7130",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DEVNET: {
|
||||||
|
aptos: {
|
||||||
|
token_bridge:
|
||||||
|
"0x84a5f374d29fc77e370014dce4fd6a55b58ad608de8074b0be5571701724da31",
|
||||||
|
core: "0xde0036a9600559e295d5f6802ef6f3f802f510366e0c23912b0655d972166017",
|
||||||
|
nft_bridge:
|
||||||
|
"0x46da3d4c569388af61f951bdd1153f4c875f90c2991f6b2d0a38e2161a40852c",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CONTRACTS = {
|
||||||
|
MAINNET: { ...SDK_CONTRACTS.MAINNET, ...OVERRIDES.MAINNET },
|
||||||
|
TESTNET: { ...SDK_CONTRACTS.TESTNET, ...OVERRIDES.TESTNET },
|
||||||
|
DEVNET: { ...SDK_CONTRACTS.DEVNET, ...OVERRIDES.DEVNET },
|
||||||
|
};
|
||||||
|
|
||||||
|
export function evm_address(x: string): string {
|
||||||
|
return hex(x).substring(2).padStart(64, "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hex(x: string): string {
|
||||||
|
return ethers.utils.hexlify(x, { allowMissingPrefix: true });
|
||||||
|
}
|
|
@ -283,6 +283,10 @@ export async function execute_evm(
|
||||||
console.log("Upgrading core contract")
|
console.log("Upgrading core contract")
|
||||||
console.log("Hash: " + (await cb.submitContractUpgrade(vaa, overrides)).hash)
|
console.log("Hash: " + (await cb.submitContractUpgrade(vaa, overrides)).hash)
|
||||||
break
|
break
|
||||||
|
case "RecoverChainId":
|
||||||
|
console.log("Recovering chain ID")
|
||||||
|
console.log("Hash: " + (await cb.submitRecoverChainId(vaa, overrides)).hash)
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
impossible(payload)
|
impossible(payload)
|
||||||
}
|
}
|
||||||
|
@ -300,6 +304,10 @@ export async function execute_evm(
|
||||||
console.log("Hash: " + (await nb.upgrade(vaa, overrides)).hash)
|
console.log("Hash: " + (await nb.upgrade(vaa, overrides)).hash)
|
||||||
console.log("Don't forget to verify the new implementation! See ethereum/VERIFY.md for instructions")
|
console.log("Don't forget to verify the new implementation! See ethereum/VERIFY.md for instructions")
|
||||||
break
|
break
|
||||||
|
case "RecoverChainId":
|
||||||
|
console.log("Recovering chain ID")
|
||||||
|
console.log("Hash: " + (await nb.submitRecoverChainId(vaa, overrides)).hash)
|
||||||
|
break
|
||||||
case "RegisterChain":
|
case "RegisterChain":
|
||||||
console.log("Registering chain")
|
console.log("Registering chain")
|
||||||
console.log("Hash: " + (await nb.registerChain(vaa, overrides)).hash)
|
console.log("Hash: " + (await nb.registerChain(vaa, overrides)).hash)
|
||||||
|
@ -326,6 +334,10 @@ export async function execute_evm(
|
||||||
console.log("Hash: " + (await tb.upgrade(vaa, overrides)).hash)
|
console.log("Hash: " + (await tb.upgrade(vaa, overrides)).hash)
|
||||||
console.log("Don't forget to verify the new implementation! See ethereum/VERIFY.md for instructions")
|
console.log("Don't forget to verify the new implementation! See ethereum/VERIFY.md for instructions")
|
||||||
break
|
break
|
||||||
|
case "RecoverChainId":
|
||||||
|
console.log("Recovering chain ID")
|
||||||
|
console.log("Hash: " + (await tb.submitRecoverChainId(vaa, overrides)).hash)
|
||||||
|
break
|
||||||
case "RegisterChain":
|
case "RegisterChain":
|
||||||
console.log("Registering chain")
|
console.log("Registering chain")
|
||||||
console.log("Hash: " + (await tb.registerChain(vaa, overrides)).hash)
|
console.log("Hash: " + (await tb.registerChain(vaa, overrides)).hash)
|
||||||
|
@ -567,7 +579,7 @@ export async function setStorageAt(rpc: string, contract_address: string, storag
|
||||||
})).data
|
})).data
|
||||||
}
|
}
|
||||||
|
|
||||||
async function maybeUnsupported<T>(query: Promise<T>): Promise<T> {
|
async function maybeUnsupported<T>(query: Promise<T>): Promise<T | "unsupported"> {
|
||||||
try {
|
try {
|
||||||
return await query
|
return await query
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -55,6 +55,8 @@ export async function execute_injective(
|
||||||
case "ContractUpgrade":
|
case "ContractUpgrade":
|
||||||
console.log("Upgrading core contract");
|
console.log("Upgrading core contract");
|
||||||
break;
|
break;
|
||||||
|
case "RecoverChainId":
|
||||||
|
throw new Error("RecoverChainId not supported on injective")
|
||||||
default:
|
default:
|
||||||
impossible(payload);
|
impossible(payload);
|
||||||
}
|
}
|
||||||
|
@ -77,6 +79,8 @@ export async function execute_injective(
|
||||||
case "ContractUpgrade":
|
case "ContractUpgrade":
|
||||||
console.log("Upgrading contract");
|
console.log("Upgrading contract");
|
||||||
break;
|
break;
|
||||||
|
case "RecoverChainId":
|
||||||
|
throw new Error("RecoverChainId not supported on injective")
|
||||||
case "RegisterChain":
|
case "RegisterChain":
|
||||||
console.log("Registering chain");
|
console.log("Registering chain");
|
||||||
break;
|
break;
|
||||||
|
@ -103,6 +107,8 @@ export async function execute_injective(
|
||||||
case "ContractUpgrade":
|
case "ContractUpgrade":
|
||||||
console.log("Upgrading contract");
|
console.log("Upgrading contract");
|
||||||
break;
|
break;
|
||||||
|
case "RecoverChainId":
|
||||||
|
throw new Error("RecoverChainId not supported on injective")
|
||||||
case "RegisterChain":
|
case "RegisterChain":
|
||||||
console.log("Registering chain");
|
console.log("Registering chain");
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -11,25 +11,15 @@
|
||||||
// drop that particular message...
|
// drop that particular message...
|
||||||
// </sigh>
|
// </sigh>
|
||||||
const info = console.info;
|
const info = console.info;
|
||||||
console.info = function(x: string) {
|
console.info = function (x: string) {
|
||||||
if (x != "secp256k1 unavailable, reverting to browser version") {
|
if (x != "secp256k1 unavailable, reverting to browser version") {
|
||||||
info(x);
|
info(x);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
import yargs from "yargs";
|
import yargs from "yargs";
|
||||||
|
|
||||||
import { hideBin } from "yargs/helpers";
|
import { hideBin } from "yargs/helpers";
|
||||||
|
|
||||||
import { fromBech32, toHex } from "@cosmjs/encoding";
|
|
||||||
import * as vaa from "./vaa";
|
|
||||||
import { impossible, Payload, serialiseVAA, VAA } from "./vaa";
|
|
||||||
import { ethers } from "ethers";
|
|
||||||
import { NETWORKS } from "./networks";
|
|
||||||
import base58 from "bs58";
|
|
||||||
import { sha3_256 } from "js-sha3";
|
|
||||||
import { isOutdated } from "./cmds/update";
|
import { isOutdated } from "./cmds/update";
|
||||||
import { assertChain, assertEVMChain, ChainName, CHAINS, CONTRACTS as SDK_CONTRACTS, isCosmWasmChain, isEVMChain, isTerraChain, toChainId, toChainName } from "@certusone/wormhole-sdk/lib/cjs/utils/consts";
|
|
||||||
|
|
||||||
if (isOutdated()) {
|
if (isOutdated()) {
|
||||||
console.error(
|
console.error(
|
||||||
|
@ -38,911 +28,4 @@ if (isOutdated()) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const GOVERNANCE_CHAIN = 1;
|
yargs(hideBin(process.argv)).commandDir("cmds").strict().demandCommand().argv;
|
||||||
const GOVERNANCE_EMITTER =
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000004";
|
|
||||||
|
|
||||||
const OVERRIDES = {
|
|
||||||
MAINNET: {
|
|
||||||
aptos: {
|
|
||||||
token_bridge:
|
|
||||||
"0x576410486a2da45eee6c949c995670112ddf2fbeedab20350d506328eefc9d4f",
|
|
||||||
core: "0x5bc11445584a763c1fa7ed39081f1b920954da14e04b32440cba863d03e19625",
|
|
||||||
nft_bridge: "0x1bdffae984043833ed7fe223f7af7a3f8902d04129b14f801823e64827da7130",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
TESTNET: {
|
|
||||||
aptos: {
|
|
||||||
token_bridge:
|
|
||||||
"0x576410486a2da45eee6c949c995670112ddf2fbeedab20350d506328eefc9d4f",
|
|
||||||
core: "0x5bc11445584a763c1fa7ed39081f1b920954da14e04b32440cba863d03e19625",
|
|
||||||
nft_bridge: "0x1bdffae984043833ed7fe223f7af7a3f8902d04129b14f801823e64827da7130",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
DEVNET: {
|
|
||||||
aptos: {
|
|
||||||
token_bridge:
|
|
||||||
"0x84a5f374d29fc77e370014dce4fd6a55b58ad608de8074b0be5571701724da31",
|
|
||||||
core: "0xde0036a9600559e295d5f6802ef6f3f802f510366e0c23912b0655d972166017",
|
|
||||||
nft_bridge: "0x46da3d4c569388af61f951bdd1153f4c875f90c2991f6b2d0a38e2161a40852c"
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const CONTRACTS = {
|
|
||||||
MAINNET: { ...SDK_CONTRACTS.MAINNET, ...OVERRIDES.MAINNET },
|
|
||||||
TESTNET: { ...SDK_CONTRACTS.TESTNET, ...OVERRIDES.TESTNET },
|
|
||||||
DEVNET: { ...SDK_CONTRACTS.DEVNET, ...OVERRIDES.DEVNET },
|
|
||||||
};
|
|
||||||
|
|
||||||
function makeVAA(
|
|
||||||
emitterChain: number,
|
|
||||||
emitterAddress: string,
|
|
||||||
signers: string[],
|
|
||||||
p: Payload
|
|
||||||
): VAA<Payload> {
|
|
||||||
let v: VAA<Payload> = {
|
|
||||||
version: 1,
|
|
||||||
guardianSetIndex: 0,
|
|
||||||
signatures: [],
|
|
||||||
timestamp: 1,
|
|
||||||
nonce: 1,
|
|
||||||
emitterChain: emitterChain,
|
|
||||||
emitterAddress: emitterAddress,
|
|
||||||
sequence: BigInt(Math.floor(Math.random() * 100000000)),
|
|
||||||
consistencyLevel: 0,
|
|
||||||
payload: p,
|
|
||||||
};
|
|
||||||
v.signatures = vaa.sign(signers, v);
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
yargs(hideBin(process.argv))
|
|
||||||
//TODO(csongor): refactor all commands into the directory structure.
|
|
||||||
.commandDir("cmds")
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Generate
|
|
||||||
.command(
|
|
||||||
"generate",
|
|
||||||
"generate VAAs (devnet and testnet only)",
|
|
||||||
(yargs) => {
|
|
||||||
return (
|
|
||||||
yargs
|
|
||||||
.option("guardian-secret", {
|
|
||||||
alias: "g",
|
|
||||||
required: true,
|
|
||||||
describe: "Guardians' secret keys (CSV)",
|
|
||||||
type: "string",
|
|
||||||
})
|
|
||||||
// Registration
|
|
||||||
.command(
|
|
||||||
"registration",
|
|
||||||
"Generate registration VAA",
|
|
||||||
(yargs) => {
|
|
||||||
return yargs
|
|
||||||
.option("chain", {
|
|
||||||
alias: "c",
|
|
||||||
describe: "Chain to register",
|
|
||||||
type: "string",
|
|
||||||
choices: Object.keys(CHAINS),
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
.option("contract-address", {
|
|
||||||
alias: "a",
|
|
||||||
describe: "Contract to register",
|
|
||||||
type: "string",
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
.option("module", {
|
|
||||||
alias: "m",
|
|
||||||
describe: "Module to upgrade",
|
|
||||||
type: "string",
|
|
||||||
choices: ["NFTBridge", "TokenBridge"],
|
|
||||||
required: true,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
(argv) => {
|
|
||||||
let module = argv["module"] as "NFTBridge" | "TokenBridge";
|
|
||||||
assertChain(argv["chain"]);
|
|
||||||
let payload: vaa.PortalRegisterChain<typeof module> = {
|
|
||||||
module,
|
|
||||||
type: "RegisterChain",
|
|
||||||
chain: 0,
|
|
||||||
emitterChain: toChainId(argv["chain"]),
|
|
||||||
emitterAddress: parseAddress(
|
|
||||||
argv["chain"],
|
|
||||||
argv["contract-address"]
|
|
||||||
),
|
|
||||||
};
|
|
||||||
let v = makeVAA(
|
|
||||||
GOVERNANCE_CHAIN,
|
|
||||||
GOVERNANCE_EMITTER,
|
|
||||||
argv["guardian-secret"].split(","),
|
|
||||||
payload
|
|
||||||
);
|
|
||||||
console.log(serialiseVAA(v));
|
|
||||||
}
|
|
||||||
)
|
|
||||||
// Upgrade
|
|
||||||
.command(
|
|
||||||
"upgrade",
|
|
||||||
"Generate contract upgrade VAA",
|
|
||||||
(yargs) => {
|
|
||||||
return yargs
|
|
||||||
.option("chain", {
|
|
||||||
alias: "c",
|
|
||||||
describe: "Chain to upgrade",
|
|
||||||
type: "string",
|
|
||||||
choices: Object.keys(CHAINS),
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
.option("contract-address", {
|
|
||||||
alias: "a",
|
|
||||||
describe: "Contract to upgrade to",
|
|
||||||
type: "string",
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
.option("module", {
|
|
||||||
alias: "m",
|
|
||||||
describe: "Module to upgrade",
|
|
||||||
type: "string",
|
|
||||||
choices: ["Core", "NFTBridge", "TokenBridge"],
|
|
||||||
required: true,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
(argv) => {
|
|
||||||
assertChain(argv["chain"]);
|
|
||||||
let module = argv["module"] as
|
|
||||||
| "Core"
|
|
||||||
| "NFTBridge"
|
|
||||||
| "TokenBridge";
|
|
||||||
let payload: vaa.ContractUpgrade = {
|
|
||||||
module,
|
|
||||||
type: "ContractUpgrade",
|
|
||||||
chain: toChainId(argv["chain"]),
|
|
||||||
address: parseCodeAddress(
|
|
||||||
argv["chain"],
|
|
||||||
argv["contract-address"]
|
|
||||||
),
|
|
||||||
};
|
|
||||||
let v = makeVAA(
|
|
||||||
GOVERNANCE_CHAIN,
|
|
||||||
GOVERNANCE_EMITTER,
|
|
||||||
argv["guardian-secret"].split(","),
|
|
||||||
payload
|
|
||||||
);
|
|
||||||
console.log(serialiseVAA(v));
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.command(
|
|
||||||
"attestation",
|
|
||||||
"Generate a token attestation VAA",
|
|
||||||
// TODO: putting 'any' here is a workaround for the following error:
|
|
||||||
//
|
|
||||||
// Type instantiation is excessively deep and possibly infinite.
|
|
||||||
//
|
|
||||||
// The type of the yargs builder grows too big for typescript's
|
|
||||||
// liking, and there's no way to increase the limit. So we
|
|
||||||
// overapproximate with the 'any' type which reduces the typechecking stack.
|
|
||||||
// This is not a great solution, and instead we should move toward
|
|
||||||
// breaking up the commands into multiple modules in the 'cmds' folder.
|
|
||||||
(yargs: any) => {
|
|
||||||
return yargs
|
|
||||||
.option("emitter-chain", {
|
|
||||||
alias: "e",
|
|
||||||
describe: "Emitter chain of the VAA",
|
|
||||||
type: "string",
|
|
||||||
choices: Object.keys(CHAINS),
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
.option("emitter-address", {
|
|
||||||
alias: "f",
|
|
||||||
describe: "Emitter address of the VAA",
|
|
||||||
type: "string",
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
.option("chain", {
|
|
||||||
alias: "c",
|
|
||||||
describe: "Token's chain",
|
|
||||||
type: "string",
|
|
||||||
choices: Object.keys(CHAINS),
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
.option("token-address", {
|
|
||||||
alias: "a",
|
|
||||||
describe: "Token's address",
|
|
||||||
type: "string",
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
.option("decimals", {
|
|
||||||
alias: "d",
|
|
||||||
describe: "Token's decimals",
|
|
||||||
type: "number",
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
.option("symbol", {
|
|
||||||
alias: "s",
|
|
||||||
describe: "Token's symbol",
|
|
||||||
type: "string",
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
.option("name", {
|
|
||||||
alias: "n",
|
|
||||||
describe: "Token's name",
|
|
||||||
type: "string",
|
|
||||||
required: true,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
(argv) => {
|
|
||||||
let emitter_chain = argv["emitter-chain"] as string;
|
|
||||||
assertChain(argv["chain"]);
|
|
||||||
assertChain(emitter_chain);
|
|
||||||
let payload: vaa.TokenBridgeAttestMeta = {
|
|
||||||
module: "TokenBridge",
|
|
||||||
type: "AttestMeta",
|
|
||||||
chain: 0,
|
|
||||||
// TODO: remove these casts (only here because of the workaround above)
|
|
||||||
tokenAddress: parseAddress(
|
|
||||||
argv["chain"],
|
|
||||||
argv["token-address"] as string
|
|
||||||
),
|
|
||||||
tokenChain: toChainId(argv["chain"]),
|
|
||||||
decimals: argv["decimals"] as number,
|
|
||||||
symbol: argv["symbol"] as string,
|
|
||||||
name: argv["name"] as string,
|
|
||||||
};
|
|
||||||
let v = makeVAA(
|
|
||||||
toChainId(emitter_chain),
|
|
||||||
parseAddress(emitter_chain, argv["emitter-address"] as string),
|
|
||||||
argv["guardian-secret"].split(","),
|
|
||||||
payload
|
|
||||||
);
|
|
||||||
console.log(serialiseVAA(v));
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
(_) => {
|
|
||||||
yargs.showHelp();
|
|
||||||
}
|
|
||||||
)
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Misc
|
|
||||||
.command(
|
|
||||||
"parse <vaa>",
|
|
||||||
"Parse a VAA (can be in either hex or base64 format)",
|
|
||||||
(yargs) => {
|
|
||||||
return yargs.positional("vaa", {
|
|
||||||
describe: "vaa",
|
|
||||||
type: "string",
|
|
||||||
});
|
|
||||||
},
|
|
||||||
async (argv) => {
|
|
||||||
let buf: Buffer;
|
|
||||||
try {
|
|
||||||
buf = Buffer.from(String(argv.vaa), "hex");
|
|
||||||
if (buf.length == 0) {
|
|
||||||
throw Error("Couldn't parse VAA as hex");
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
buf = Buffer.from(String(argv.vaa), "base64");
|
|
||||||
if (buf.length == 0) {
|
|
||||||
throw Error("Couldn't parse VAA as base64 or hex");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const parsed_vaa = vaa.parse(buf);
|
|
||||||
let parsed_vaa_with_digest = parsed_vaa;
|
|
||||||
parsed_vaa_with_digest["digest"] = vaa.vaaDigest(parsed_vaa);
|
|
||||||
console.log(parsed_vaa_with_digest);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.command(
|
|
||||||
"recover <digest> <signature>",
|
|
||||||
"Recover an address from a signature",
|
|
||||||
(yargs) => {
|
|
||||||
return yargs
|
|
||||||
.positional("digest", {
|
|
||||||
describe: "digest",
|
|
||||||
type: "string",
|
|
||||||
})
|
|
||||||
.positional("signature", {
|
|
||||||
describe: "signature",
|
|
||||||
type: "string",
|
|
||||||
});
|
|
||||||
},
|
|
||||||
async (argv) => {
|
|
||||||
console.log(
|
|
||||||
ethers.utils.recoverAddress(hex(argv["digest"]), hex(argv["signature"]))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.command(
|
|
||||||
"contract <network> <chain> <module>",
|
|
||||||
"Print contract address",
|
|
||||||
(yargs) => {
|
|
||||||
return yargs
|
|
||||||
.positional("network", {
|
|
||||||
describe: "network",
|
|
||||||
type: "string",
|
|
||||||
choices: ["mainnet", "testnet", "devnet"],
|
|
||||||
})
|
|
||||||
.positional("chain", {
|
|
||||||
describe: "Chain to query",
|
|
||||||
type: "string",
|
|
||||||
choices: Object.keys(CHAINS),
|
|
||||||
})
|
|
||||||
.positional("module", {
|
|
||||||
describe: "Module to query",
|
|
||||||
type: "string",
|
|
||||||
choices: ["Core", "NFTBridge", "TokenBridge"],
|
|
||||||
})
|
|
||||||
.option("emitter", {
|
|
||||||
alias: "e",
|
|
||||||
describe: "Print in emitter address format",
|
|
||||||
type: "boolean",
|
|
||||||
default: false,
|
|
||||||
required: false,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
async (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 = "";
|
|
||||||
switch (module) {
|
|
||||||
case "Core":
|
|
||||||
addr = CONTRACTS[network][chain]["core"];
|
|
||||||
break;
|
|
||||||
case "NFTBridge":
|
|
||||||
addr = CONTRACTS[network][chain]["nft_bridge"];
|
|
||||||
break;
|
|
||||||
case "TokenBridge":
|
|
||||||
addr = CONTRACTS[network][chain]["token_bridge"];
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
impossible(module);
|
|
||||||
}
|
|
||||||
if (argv["emitter"]) {
|
|
||||||
const emitter = require("@certusone/wormhole-sdk/lib/cjs/bridge/getEmitterAddress")
|
|
||||||
if (chain === "solana" || chain === "pythnet") {
|
|
||||||
// TODO: Create an isSolanaChain()
|
|
||||||
addr = await emitter.getEmitterAddressSolana(addr);
|
|
||||||
} else if (isCosmWasmChain(chain)) {
|
|
||||||
addr = await emitter.getEmitterAddressTerra(addr);
|
|
||||||
} else if (chain === "algorand") {
|
|
||||||
addr = emitter.getEmitterAddressAlgorand(BigInt(addr));
|
|
||||||
} else if (chain === "near") {
|
|
||||||
addr = emitter.getEmitterAddressNear(addr);
|
|
||||||
} else {
|
|
||||||
addr = emitter.getEmitterAddressEth(addr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.log(addr);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.command(
|
|
||||||
"chain-id <chain>",
|
|
||||||
"Print the wormhole chain ID integer associated with the specified chain name",
|
|
||||||
(yargs) => {
|
|
||||||
return yargs.positional("chain", {
|
|
||||||
describe: "Chain to query",
|
|
||||||
type: "string",
|
|
||||||
choices: Object.keys(CHAINS),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
async (argv) => {
|
|
||||||
assertChain(argv["chain"]);
|
|
||||||
console.log(toChainId(argv["chain"]));
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.command(
|
|
||||||
"rpc <network> <chain>",
|
|
||||||
"Print RPC address",
|
|
||||||
(yargs) => {
|
|
||||||
return yargs
|
|
||||||
.positional("network", {
|
|
||||||
describe: "network",
|
|
||||||
type: "string",
|
|
||||||
choices: ["mainnet", "testnet", "devnet"],
|
|
||||||
})
|
|
||||||
.positional("chain", {
|
|
||||||
describe: "Chain to query",
|
|
||||||
type: "string",
|
|
||||||
choices: Object.keys(CHAINS),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
async (argv) => {
|
|
||||||
assertChain(argv["chain"]);
|
|
||||||
const network = argv.network.toUpperCase();
|
|
||||||
if (
|
|
||||||
network !== "MAINNET" &&
|
|
||||||
network !== "TESTNET" &&
|
|
||||||
network !== "DEVNET"
|
|
||||||
) {
|
|
||||||
throw Error(`Unknown network: ${network}`);
|
|
||||||
}
|
|
||||||
console.log(NETWORKS[network][argv["chain"]].rpc);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Near utilities
|
|
||||||
.command(
|
|
||||||
"near",
|
|
||||||
"NEAR utilites",
|
|
||||||
(yargs) => {
|
|
||||||
const near = require("./near")
|
|
||||||
return (
|
|
||||||
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,
|
|
||||||
})
|
|
||||||
.option("account", {
|
|
||||||
describe: "near deployment account",
|
|
||||||
type: "string",
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
.option("attach", {
|
|
||||||
describe: "attach some near",
|
|
||||||
type: "string",
|
|
||||||
required: false,
|
|
||||||
})
|
|
||||||
.option("target", {
|
|
||||||
describe: "near account to upgrade",
|
|
||||||
type: "string",
|
|
||||||
required: false,
|
|
||||||
})
|
|
||||||
.option("mnemonic", {
|
|
||||||
describe: "near private keys",
|
|
||||||
type: "string",
|
|
||||||
required: false,
|
|
||||||
})
|
|
||||||
.option("keys", {
|
|
||||||
describe: "near private keys",
|
|
||||||
type: "string",
|
|
||||||
required: false,
|
|
||||||
})
|
|
||||||
.command(
|
|
||||||
"contract-update <file>",
|
|
||||||
"Submit a contract update using our specific APIs",
|
|
||||||
(yargs) => {
|
|
||||||
return yargs.positional("file", {
|
|
||||||
type: "string",
|
|
||||||
describe: "wasm",
|
|
||||||
});
|
|
||||||
},
|
|
||||||
async (argv) => {
|
|
||||||
await near.upgrade_near(argv);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.command(
|
|
||||||
"deploy <file>",
|
|
||||||
"Submit a contract update using near APIs",
|
|
||||||
(yargs) => {
|
|
||||||
return yargs.positional("file", {
|
|
||||||
type: "string",
|
|
||||||
describe: "wasm",
|
|
||||||
});
|
|
||||||
},
|
|
||||||
async (argv) => {
|
|
||||||
await near.deploy_near(argv);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
(_) => {
|
|
||||||
yargs.showHelp();
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Evm utilities
|
|
||||||
.command(
|
|
||||||
"evm",
|
|
||||||
"EVM utilites",
|
|
||||||
(yargs) => {
|
|
||||||
const evm = require("./evm")
|
|
||||||
return yargs
|
|
||||||
.option("rpc", {
|
|
||||||
describe: "RPC endpoint",
|
|
||||||
type: "string",
|
|
||||||
required: false,
|
|
||||||
})
|
|
||||||
.command(
|
|
||||||
"address-from-secret <secret>",
|
|
||||||
"Compute a 20 byte eth address from a 32 byte private key",
|
|
||||||
(yargs) => {
|
|
||||||
return yargs.positional("secret", {
|
|
||||||
type: "string",
|
|
||||||
describe: "Secret key (32 bytes)",
|
|
||||||
});
|
|
||||||
},
|
|
||||||
(argv) => {
|
|
||||||
console.log(ethers.utils.computeAddress(argv["secret"]));
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.command(
|
|
||||||
"storage-update",
|
|
||||||
"Update a storage slot on an EVM fork during testing (anvil or hardhat)",
|
|
||||||
(yargs) => {
|
|
||||||
return yargs
|
|
||||||
.option("contract-address", {
|
|
||||||
alias: "a",
|
|
||||||
describe: "Contract address",
|
|
||||||
type: "string",
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
.option("storage-slot", {
|
|
||||||
alias: "k",
|
|
||||||
describe: "Storage slot to modify",
|
|
||||||
type: "string",
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
.option("value", {
|
|
||||||
alias: "v",
|
|
||||||
describe: "Value to write into the slot (32 bytes)",
|
|
||||||
type: "string",
|
|
||||||
required: true,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
async (argv) => {
|
|
||||||
const result = await evm.setStorageAt(
|
|
||||||
argv["rpc"],
|
|
||||||
evm_address(argv["contract-address"]),
|
|
||||||
argv["storage-slot"],
|
|
||||||
["uint256"],
|
|
||||||
[argv["value"]]
|
|
||||||
);
|
|
||||||
console.log(result);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.command("chains", "Return all EVM chains", async (_) => {
|
|
||||||
console.log(
|
|
||||||
Object.values(CHAINS)
|
|
||||||
.map((id) => toChainName(id))
|
|
||||||
.filter((name) => isEVMChain(name))
|
|
||||||
.join(" ")
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.command(
|
|
||||||
"info",
|
|
||||||
"Query info about the on-chain state of the contract",
|
|
||||||
(yargs) => {
|
|
||||||
return yargs
|
|
||||||
.option("chain", {
|
|
||||||
alias: "c",
|
|
||||||
describe: "Chain to query",
|
|
||||||
type: "string",
|
|
||||||
choices: Object.keys(CHAINS),
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
.option("module", {
|
|
||||||
alias: "m",
|
|
||||||
describe: "Module to query",
|
|
||||||
type: "string",
|
|
||||||
choices: ["Core", "NFTBridge", "TokenBridge"],
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
.option("network", {
|
|
||||||
alias: "n",
|
|
||||||
describe: "network",
|
|
||||||
type: "string",
|
|
||||||
choices: ["mainnet", "testnet", "devnet"],
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
.option("contract-address", {
|
|
||||||
alias: "a",
|
|
||||||
describe: "Contract to query (override config)",
|
|
||||||
type: "string",
|
|
||||||
required: false,
|
|
||||||
})
|
|
||||||
.option("implementation-only", {
|
|
||||||
alias: "i",
|
|
||||||
describe: "Only query implementation (faster)",
|
|
||||||
type: "boolean",
|
|
||||||
default: false,
|
|
||||||
required: 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;
|
|
||||||
if (argv["implementation-only"]) {
|
|
||||||
console.log(
|
|
||||||
await evm.getImplementation(
|
|
||||||
network,
|
|
||||||
argv["chain"],
|
|
||||||
module,
|
|
||||||
argv["contract-address"],
|
|
||||||
rpc
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
console.log(
|
|
||||||
JSON.stringify(
|
|
||||||
await evm.query_contract_evm(
|
|
||||||
network,
|
|
||||||
argv["chain"],
|
|
||||||
module,
|
|
||||||
argv["contract-address"],
|
|
||||||
rpc
|
|
||||||
),
|
|
||||||
null,
|
|
||||||
2
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.command(
|
|
||||||
"hijack",
|
|
||||||
"Override the guardian set of the core bridge contract during testing (anvil or hardhat)",
|
|
||||||
(yargs) => {
|
|
||||||
return yargs
|
|
||||||
.option("core-contract-address", {
|
|
||||||
alias: "a",
|
|
||||||
describe: "Core contract address",
|
|
||||||
type: "string",
|
|
||||||
default: CONTRACTS.MAINNET.ethereum.core,
|
|
||||||
})
|
|
||||||
.option("guardian-address", {
|
|
||||||
alias: "g",
|
|
||||||
required: true,
|
|
||||||
describe: "Guardians' public addresses (CSV)",
|
|
||||||
type: "string",
|
|
||||||
})
|
|
||||||
.option("guardian-set-index", {
|
|
||||||
alias: "i",
|
|
||||||
required: 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;
|
|
||||||
await evm.hijack_evm(
|
|
||||||
rpc,
|
|
||||||
argv["core-contract-address"],
|
|
||||||
guardian_addresses,
|
|
||||||
argv["guardian-set-index"]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
(_) => {
|
|
||||||
yargs.showHelp();
|
|
||||||
}
|
|
||||||
)
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Submit
|
|
||||||
.command(
|
|
||||||
"submit <vaa>",
|
|
||||||
"Execute a VAA",
|
|
||||||
(yargs) => {
|
|
||||||
// @ts-ignore
|
|
||||||
return yargs
|
|
||||||
.positional("vaa", {
|
|
||||||
describe: "vaa",
|
|
||||||
type: "string",
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
.option("chain", {
|
|
||||||
alias: "c",
|
|
||||||
describe: "chain name",
|
|
||||||
type: "string",
|
|
||||||
choices: Object.keys(CHAINS),
|
|
||||||
required: false,
|
|
||||||
})
|
|
||||||
.option("network", {
|
|
||||||
alias: "n",
|
|
||||||
describe: "network",
|
|
||||||
type: "string",
|
|
||||||
choices: ["mainnet", "testnet", "devnet"],
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
.option("contract-address", {
|
|
||||||
alias: "a",
|
|
||||||
describe: "Contract to submit VAA to (override config)",
|
|
||||||
type: "string",
|
|
||||||
required: false,
|
|
||||||
})
|
|
||||||
.option("rpc", {
|
|
||||||
describe: "RPC endpoint",
|
|
||||||
type: "string",
|
|
||||||
required: false,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
async (argv) => {
|
|
||||||
const vaa_hex = String(argv.vaa);
|
|
||||||
const buf = Buffer.from(vaa_hex, "hex");
|
|
||||||
const parsed_vaa = vaa.parse(buf);
|
|
||||||
|
|
||||||
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}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We figure out the target chain to submit the VAA to.
|
|
||||||
// The VAA might specify this itself (for example a contract upgrade VAA
|
|
||||||
// or a token transfer VAA), in which case we just submit the VAA to
|
|
||||||
// that target chain.
|
|
||||||
//
|
|
||||||
// If the VAA does not have a target (e.g. chain registration VAAs or
|
|
||||||
// guardian set upgrade VAAs), we require the '--chain' argument to be
|
|
||||||
// set on the command line.
|
|
||||||
//
|
|
||||||
// As a sanity check, in the event that the VAA does specify a target
|
|
||||||
// and the '--chain' argument is also set, we issue an error if those
|
|
||||||
// two don't agree instead of silently taking the VAA's target chain.
|
|
||||||
|
|
||||||
// get VAA chain
|
|
||||||
const vaa_chain_id = parsed_vaa.payload.chain;
|
|
||||||
assertChain(vaa_chain_id);
|
|
||||||
const vaa_chain = toChainName(vaa_chain_id);
|
|
||||||
|
|
||||||
// get chain from command line arg
|
|
||||||
const cli_chain = argv["chain"];
|
|
||||||
|
|
||||||
let chain: ChainName;
|
|
||||||
if (cli_chain !== undefined) {
|
|
||||||
assertChain(cli_chain);
|
|
||||||
if (vaa_chain !== "unset" && cli_chain !== vaa_chain) {
|
|
||||||
throw Error(
|
|
||||||
`Specified target chain (${cli_chain}) does not match VAA target chain (${vaa_chain})`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
chain = cli_chain;
|
|
||||||
} else {
|
|
||||||
chain = vaa_chain;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chain === "unset") {
|
|
||||||
throw Error(
|
|
||||||
"This VAA does not specify the target chain, please provide it by hand using the '--chain' flag."
|
|
||||||
);
|
|
||||||
} else if (isEVMChain(chain)) {
|
|
||||||
const evm = require("./evm")
|
|
||||||
await evm.execute_evm(
|
|
||||||
parsed_vaa.payload,
|
|
||||||
buf,
|
|
||||||
network,
|
|
||||||
chain,
|
|
||||||
argv["contract-address"],
|
|
||||||
argv["rpc"]
|
|
||||||
);
|
|
||||||
} else if (isTerraChain(chain)) {
|
|
||||||
const terra = require("./terra")
|
|
||||||
await terra.execute_terra(parsed_vaa.payload, buf, network, chain);
|
|
||||||
} else if (chain === "solana" || chain === "pythnet") {
|
|
||||||
const solana = require("./solana")
|
|
||||||
await solana.execute_solana(parsed_vaa, buf, network, chain);
|
|
||||||
} else if (chain === "algorand") {
|
|
||||||
const algorand = require("./algorand")
|
|
||||||
await algorand.execute_algorand(
|
|
||||||
parsed_vaa.payload,
|
|
||||||
new Uint8Array(Buffer.from(vaa_hex, "hex")),
|
|
||||||
network
|
|
||||||
);
|
|
||||||
} else if (chain === "near") {
|
|
||||||
const near = require("./near")
|
|
||||||
await near.execute_near(parsed_vaa.payload, vaa_hex, network);
|
|
||||||
} else if (chain === "injective") {
|
|
||||||
const injective = require("./injective")
|
|
||||||
await injective.execute_injective(parsed_vaa.payload, buf, network);
|
|
||||||
} else if (chain === "xpla") {
|
|
||||||
const xpla = require("./xpla")
|
|
||||||
await xpla.execute_xpla(parsed_vaa.payload, buf, network);
|
|
||||||
} else if (chain === "osmosis") {
|
|
||||||
throw Error("OSMOSIS is not supported yet");
|
|
||||||
} else if (chain === "sui") {
|
|
||||||
throw Error("SUI is not supported yet");
|
|
||||||
} else if (chain === "aptos") {
|
|
||||||
const aptos = require("./aptos")
|
|
||||||
await aptos.execute_aptos(
|
|
||||||
parsed_vaa.payload,
|
|
||||||
buf,
|
|
||||||
network,
|
|
||||||
argv["contract-address"],
|
|
||||||
argv["rpc"]
|
|
||||||
);
|
|
||||||
} else if (chain === "wormchain") {
|
|
||||||
throw Error("Wormchain is not supported yet");
|
|
||||||
} else if (chain === "btc") {
|
|
||||||
throw Error("BTC is not supported yet")
|
|
||||||
} else {
|
|
||||||
// If you get a type error here, hover over `chain`'s type and it tells you
|
|
||||||
// which cases are not handled
|
|
||||||
impossible(chain);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.strict()
|
|
||||||
.demandCommand().argv;
|
|
||||||
|
|
||||||
function hex(x: string): string {
|
|
||||||
return ethers.utils.hexlify(x, { allowMissingPrefix: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
function evm_address(x: string): string {
|
|
||||||
return hex(x).substring(2).padStart(64, "0");
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseAddress(chain: ChainName, address: string): string {
|
|
||||||
if (chain === "unset") {
|
|
||||||
throw Error("Chain unset");
|
|
||||||
} else if (isEVMChain(chain)) {
|
|
||||||
return "0x" + evm_address(address);
|
|
||||||
} else if (isCosmWasmChain(chain)) {
|
|
||||||
return "0x" + toHex(fromBech32(address).data).padStart(64, "0");
|
|
||||||
} else if (chain === "solana" || chain === "pythnet") {
|
|
||||||
return "0x" + toHex(base58.decode(address)).padStart(64, "0");
|
|
||||||
} else if (chain === "algorand") {
|
|
||||||
// TODO: is there a better native format for algorand?
|
|
||||||
return "0x" + evm_address(address);
|
|
||||||
} else if (chain === "near") {
|
|
||||||
return "0x" + hex(address).substring(2).padStart(64, "0");
|
|
||||||
} else if (chain === "osmosis") {
|
|
||||||
throw Error("OSMOSIS is not supported yet");
|
|
||||||
} else if (chain === "sui") {
|
|
||||||
throw Error("SUI is not supported yet");
|
|
||||||
} else if (chain === "aptos") {
|
|
||||||
if (/^(0x)?[0-9a-fA-F]+$/.test(address)) {
|
|
||||||
return "0x" + evm_address(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
return sha3_256(Buffer.from(address)); // address is hash of fully qualified type
|
|
||||||
} else if (chain === "wormchain") {
|
|
||||||
const sdk = require("@certusone/wormhole-sdk/lib/cjs/utils/array")
|
|
||||||
return "0x" + sdk.tryNativeToHexString(address, chain);
|
|
||||||
} else if (chain == "btc") {
|
|
||||||
throw Error("BTC is not supported yet")
|
|
||||||
} else {
|
|
||||||
impossible(chain);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseCodeAddress(chain: ChainName, address: string): string {
|
|
||||||
if (isCosmWasmChain(chain)) {
|
|
||||||
return "0x" + parseInt(address, 10).toString(16).padStart(64, "0");
|
|
||||||
} else {
|
|
||||||
return parseAddress(chain, address);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -131,6 +131,8 @@ export async function execute_near(
|
||||||
case "ContractUpgrade":
|
case "ContractUpgrade":
|
||||||
console.log("Upgrading core contract");
|
console.log("Upgrading core contract");
|
||||||
break;
|
break;
|
||||||
|
case "RecoverChainId":
|
||||||
|
throw new Error("RecoverChainId not supported on near")
|
||||||
default:
|
default:
|
||||||
impossible(payload);
|
impossible(payload);
|
||||||
}
|
}
|
||||||
|
@ -145,6 +147,8 @@ export async function execute_near(
|
||||||
case "ContractUpgrade":
|
case "ContractUpgrade":
|
||||||
console.log("Upgrading contract");
|
console.log("Upgrading contract");
|
||||||
break;
|
break;
|
||||||
|
case "RecoverChainId":
|
||||||
|
throw new Error("RecoverChainId not supported on near")
|
||||||
case "RegisterChain":
|
case "RegisterChain":
|
||||||
console.log("Registering chain");
|
console.log("Registering chain");
|
||||||
break;
|
break;
|
||||||
|
@ -165,6 +169,8 @@ export async function execute_near(
|
||||||
case "ContractUpgrade":
|
case "ContractUpgrade":
|
||||||
console.log("Upgrading contract");
|
console.log("Upgrading contract");
|
||||||
break;
|
break;
|
||||||
|
case "RecoverChainId":
|
||||||
|
throw new Error("RecoverChainId not supported on near")
|
||||||
case "RegisterChain":
|
case "RegisterChain":
|
||||||
console.log("Registering chain");
|
console.log("Registering chain");
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -66,6 +66,8 @@ export async function execute_solana(
|
||||||
vaa
|
vaa
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case "RecoverChainId":
|
||||||
|
throw new Error("RecoverChainId not supported on solana")
|
||||||
default:
|
default:
|
||||||
ix = impossible(v.payload);
|
ix = impossible(v.payload);
|
||||||
}
|
}
|
||||||
|
@ -84,6 +86,8 @@ export async function execute_solana(
|
||||||
vaa
|
vaa
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case "RecoverChainId":
|
||||||
|
throw new Error("RecoverChainId not supported on solana")
|
||||||
case "RegisterChain":
|
case "RegisterChain":
|
||||||
console.log("Registering chain");
|
console.log("Registering chain");
|
||||||
ix = createNFTBridgeRegisterChainInstruction(
|
ix = createNFTBridgeRegisterChainInstruction(
|
||||||
|
@ -115,6 +119,8 @@ export async function execute_solana(
|
||||||
vaa
|
vaa
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case "RecoverChainId":
|
||||||
|
throw new Error("RecoverChainId not supported on solana")
|
||||||
case "RegisterChain":
|
case "RegisterChain":
|
||||||
console.log("Registering chain");
|
console.log("Registering chain");
|
||||||
ix = createTokenBridgeRegisterChainInstruction(
|
ix = createTokenBridgeRegisterChainInstruction(
|
||||||
|
|
|
@ -51,6 +51,8 @@ export async function execute_terra(
|
||||||
case "ContractUpgrade":
|
case "ContractUpgrade":
|
||||||
console.log("Upgrading core contract");
|
console.log("Upgrading core contract");
|
||||||
break;
|
break;
|
||||||
|
case "RecoverChainId":
|
||||||
|
throw new Error("RecoverChainId not supported on terra")
|
||||||
default:
|
default:
|
||||||
impossible(payload);
|
impossible(payload);
|
||||||
}
|
}
|
||||||
|
@ -72,6 +74,8 @@ export async function execute_terra(
|
||||||
case "ContractUpgrade":
|
case "ContractUpgrade":
|
||||||
console.log("Upgrading contract");
|
console.log("Upgrading contract");
|
||||||
break;
|
break;
|
||||||
|
case "RecoverChainId":
|
||||||
|
throw new Error("RecoverChainId not supported on terra")
|
||||||
case "RegisterChain":
|
case "RegisterChain":
|
||||||
console.log("Registering chain");
|
console.log("Registering chain");
|
||||||
break;
|
break;
|
||||||
|
@ -93,6 +97,8 @@ export async function execute_terra(
|
||||||
case "ContractUpgrade":
|
case "ContractUpgrade":
|
||||||
console.log("Upgrading contract");
|
console.log("Upgrading contract");
|
||||||
break;
|
break;
|
||||||
|
case "RecoverChainId":
|
||||||
|
throw new Error("RecoverChainId not supported on terra")
|
||||||
case "RegisterChain":
|
case "RegisterChain":
|
||||||
console.log("Registering chain");
|
console.log("Registering chain");
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -70,12 +70,20 @@ export type Payload =
|
||||||
| TokenBridgeTransferWithPayload
|
| TokenBridgeTransferWithPayload
|
||||||
| TokenBridgeAttestMeta
|
| TokenBridgeAttestMeta
|
||||||
| NFTBridgeTransfer
|
| NFTBridgeTransfer
|
||||||
|
| CoreContractRecoverChainId
|
||||||
|
| PortalContractRecoverChainId<"TokenBridge">
|
||||||
|
| PortalContractRecoverChainId<"NFTBridge">
|
||||||
|
|
||||||
export type ContractUpgrade =
|
export type ContractUpgrade =
|
||||||
CoreContractUpgrade
|
CoreContractUpgrade
|
||||||
| PortalContractUpgrade<"TokenBridge">
|
| PortalContractUpgrade<"TokenBridge">
|
||||||
| PortalContractUpgrade<"NFTBridge">
|
| PortalContractUpgrade<"NFTBridge">
|
||||||
|
|
||||||
|
export type RecoverChainId =
|
||||||
|
CoreContractRecoverChainId
|
||||||
|
| PortalContractRecoverChainId<"TokenBridge">
|
||||||
|
| PortalContractRecoverChainId<"NFTBridge">
|
||||||
|
|
||||||
export function parse(buffer: Buffer): VAA<Payload | Other> {
|
export function parse(buffer: Buffer): VAA<Payload | Other> {
|
||||||
const vaa = parseEnvelope(buffer)
|
const vaa = parseEnvelope(buffer)
|
||||||
const parser = guardianSetUpgradeParser
|
const parser = guardianSetUpgradeParser
|
||||||
|
@ -88,6 +96,9 @@ export function parse(buffer: Buffer): VAA<Payload | Other> {
|
||||||
.or(tokenBridgeTransferWithPayloadParser())
|
.or(tokenBridgeTransferWithPayloadParser())
|
||||||
.or(tokenBridgeAttestMetaParser())
|
.or(tokenBridgeAttestMetaParser())
|
||||||
.or(nftBridgeTransferParser())
|
.or(nftBridgeTransferParser())
|
||||||
|
.or(coreContractRecoverChainId())
|
||||||
|
.or(portalContractRecoverChainId("TokenBridge"))
|
||||||
|
.or(portalContractRecoverChainId("NFTBridge"))
|
||||||
let payload : Payload | Other | null = parser.parse(vaa.payload)
|
let payload : Payload | Other | null = parser.parse(vaa.payload)
|
||||||
if (payload === null) {
|
if (payload === null) {
|
||||||
payload = {type: "Other", hex: Buffer.from(vaa.payload).toString("hex"), ascii: Buffer.from(vaa.payload).toString('utf8')}
|
payload = {type: "Other", hex: Buffer.from(vaa.payload).toString("hex"), ascii: Buffer.from(vaa.payload).toString('utf8')}
|
||||||
|
@ -192,6 +203,9 @@ function vaaBody(vaa: VAA<Payload | Other>) {
|
||||||
case "ContractUpgrade":
|
case "ContractUpgrade":
|
||||||
payload_str = serialiseCoreContractUpgrade(payload)
|
payload_str = serialiseCoreContractUpgrade(payload)
|
||||||
break
|
break
|
||||||
|
case "RecoverChainId":
|
||||||
|
payload_str = serialiseCoreContractRecoverChainId(payload)
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
impossible(payload)
|
impossible(payload)
|
||||||
break
|
break
|
||||||
|
@ -202,6 +216,9 @@ function vaaBody(vaa: VAA<Payload | Other>) {
|
||||||
case "ContractUpgrade":
|
case "ContractUpgrade":
|
||||||
payload_str = serialisePortalContractUpgrade(payload)
|
payload_str = serialisePortalContractUpgrade(payload)
|
||||||
break
|
break
|
||||||
|
case "RecoverChainId":
|
||||||
|
payload_str = serialisePortalContractRecoverChainId(payload)
|
||||||
|
break
|
||||||
case "RegisterChain":
|
case "RegisterChain":
|
||||||
payload_str = serialisePortalRegisterChain(payload)
|
payload_str = serialisePortalRegisterChain(payload)
|
||||||
break
|
break
|
||||||
|
@ -218,6 +235,9 @@ function vaaBody(vaa: VAA<Payload | Other>) {
|
||||||
case "ContractUpgrade":
|
case "ContractUpgrade":
|
||||||
payload_str = serialisePortalContractUpgrade(payload)
|
payload_str = serialisePortalContractUpgrade(payload)
|
||||||
break
|
break
|
||||||
|
case "RecoverChainId":
|
||||||
|
payload_str = serialisePortalContractRecoverChainId(payload)
|
||||||
|
break
|
||||||
case "RegisterChain":
|
case "RegisterChain":
|
||||||
payload_str = serialisePortalRegisterChain(payload)
|
payload_str = serialisePortalRegisterChain(payload)
|
||||||
break
|
break
|
||||||
|
@ -468,6 +488,95 @@ function serialisePortalRegisterChain<Module extends "NFTBridge" | "TokenBridge"
|
||||||
return body.join("")
|
return body.join("")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// RecoverChainId
|
||||||
|
|
||||||
|
export interface CoreContractRecoverChainId {
|
||||||
|
module: "Core"
|
||||||
|
type: "RecoverChainId"
|
||||||
|
evmChainId: bigint
|
||||||
|
newChainId: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse a core contract recoverChainId payload
|
||||||
|
function coreContractRecoverChainId(): P<CoreContractRecoverChainId> {
|
||||||
|
return new P(new Parser()
|
||||||
|
.endianess("big")
|
||||||
|
.string("module", {
|
||||||
|
length: 32,
|
||||||
|
encoding: "hex",
|
||||||
|
assert: Buffer.from("Core").toString("hex").padStart(64, "0"),
|
||||||
|
formatter: (_str) => "Core"
|
||||||
|
})
|
||||||
|
.uint8("type", {
|
||||||
|
assert: 5,
|
||||||
|
formatter: (_action) => "RecoverChainId"
|
||||||
|
})
|
||||||
|
.array("evmChainId", {
|
||||||
|
type: "uint8",
|
||||||
|
lengthInBytes: 32,
|
||||||
|
formatter: (bytes) => BigNumber.from(bytes).toBigInt()
|
||||||
|
})
|
||||||
|
.uint16("newChainId")
|
||||||
|
.string("end", {
|
||||||
|
greedy: true,
|
||||||
|
assert: str => str === ""
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
function serialiseCoreContractRecoverChainId(payload: CoreContractRecoverChainId): string {
|
||||||
|
const body = [
|
||||||
|
encode("bytes32", encodeString(payload.module)),
|
||||||
|
encode("uint8", 5),
|
||||||
|
encode("uint256", payload.evmChainId),
|
||||||
|
encode("uint16", payload.newChainId)
|
||||||
|
]
|
||||||
|
return body.join("")
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PortalContractRecoverChainId<Module extends "NFTBridge" | "TokenBridge"> {
|
||||||
|
module: Module
|
||||||
|
type: "RecoverChainId"
|
||||||
|
evmChainId: bigint
|
||||||
|
newChainId: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse a portal contract recoverChainId payload
|
||||||
|
function portalContractRecoverChainId<Module extends "NFTBridge" | "TokenBridge">(module: Module): P<PortalContractRecoverChainId<Module>> {
|
||||||
|
return new P(new Parser()
|
||||||
|
.endianess("big")
|
||||||
|
.string("module", {
|
||||||
|
length: 32,
|
||||||
|
encoding: "hex",
|
||||||
|
assert: Buffer.from(module).toString("hex").padStart(64, "0"),
|
||||||
|
formatter: (_str: string) => module
|
||||||
|
})
|
||||||
|
.uint8("type", {
|
||||||
|
assert: 3,
|
||||||
|
formatter: (_action: number) => "RecoverChainId"
|
||||||
|
})
|
||||||
|
.array("evmChainId", {
|
||||||
|
type: "uint8",
|
||||||
|
lengthInBytes: 32,
|
||||||
|
formatter: (bytes) => BigNumber.from(bytes).toBigInt()
|
||||||
|
})
|
||||||
|
.uint16("newChainId")
|
||||||
|
.string("end", {
|
||||||
|
greedy: true,
|
||||||
|
assert: str => str === ""
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
function serialisePortalContractRecoverChainId<Module extends "NFTBridge" | "TokenBridge">(payload: PortalContractRecoverChainId<Module>): string {
|
||||||
|
const body = [
|
||||||
|
encode("bytes32", encodeString(payload.module)),
|
||||||
|
encode("uint8", 3),
|
||||||
|
encode("uint256", payload.evmChainId),
|
||||||
|
encode("uint16", payload.newChainId)
|
||||||
|
]
|
||||||
|
return body.join("")
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Token bridge
|
// Token bridge
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,8 @@ export async function execute_xpla(
|
||||||
case "ContractUpgrade":
|
case "ContractUpgrade":
|
||||||
console.log("Upgrading core contract");
|
console.log("Upgrading core contract");
|
||||||
break;
|
break;
|
||||||
|
case "RecoverChainId":
|
||||||
|
throw new Error("RecoverChainId not supported on XPLA")
|
||||||
default:
|
default:
|
||||||
impossible(payload);
|
impossible(payload);
|
||||||
}
|
}
|
||||||
|
@ -57,7 +59,7 @@ export async function execute_xpla(
|
||||||
// NOTE: this code can safely be removed once the terra NFT bridge is
|
// 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
|
// released, but it's fine for it to stay, as the condition will just be
|
||||||
// skipped once 'contracts.nft_bridge' is defined
|
// 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 XPLA");
|
||||||
}
|
}
|
||||||
target_contract = contracts.nft_bridge;
|
target_contract = contracts.nft_bridge;
|
||||||
execute_msg = {
|
execute_msg = {
|
||||||
|
@ -69,6 +71,8 @@ export async function execute_xpla(
|
||||||
case "ContractUpgrade":
|
case "ContractUpgrade":
|
||||||
console.log("Upgrading contract");
|
console.log("Upgrading contract");
|
||||||
break;
|
break;
|
||||||
|
case "RecoverChainId":
|
||||||
|
throw new Error("RecoverChainId not supported on XPLA")
|
||||||
case "RegisterChain":
|
case "RegisterChain":
|
||||||
console.log("Registering chain");
|
console.log("Registering chain");
|
||||||
break;
|
break;
|
||||||
|
@ -90,6 +94,8 @@ export async function execute_xpla(
|
||||||
case "ContractUpgrade":
|
case "ContractUpgrade":
|
||||||
console.log("Upgrading contract");
|
console.log("Upgrading contract");
|
||||||
break;
|
break;
|
||||||
|
case "RecoverChainId":
|
||||||
|
throw new Error("RecoverChainId not supported on XPLA")
|
||||||
case "RegisterChain":
|
case "RegisterChain":
|
||||||
console.log("Registering chain");
|
console.log("Registering chain");
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in New Issue