cli: Refactor js cli to support all contracts + add improvements (#1100)

* cli: Refactor js cli to support all contracts + add improvements

* Fix path in Makefile

* Don't be strict

* Fix registration VAA deserialisation

* Add karura gas calculation logic

* cli: fix after sdk rebase

* cli: improve makefile

* cli: depend on @certusone/wormhole-sdk locally instead of importing relative paths

* cli: add polygon overrides

* client: update for devnet

* fix tx signing in solana cli

* client: alias network

Co-authored-by: Csongor Kiss <ckiss@jumptrading.com>
Co-authored-by: Evan Gray <battledingo@gmail.com>
This commit is contained in:
Csongor Kiss 2022-05-06 02:51:24 +02:00 committed by GitHub
parent c626e591e8
commit 1c006f9bed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 2116 additions and 27995 deletions

View File

@ -14,19 +14,11 @@ RUN --mount=type=cache,target=/root/.cache \
ENV SOLANA_BIN_PATH="/root/.local/share/solana/install/active_release/bin"
ENV PATH="$SOLANA_BIN_PATH:$PATH"
WORKDIR /usr/src/clients/token_bridge
COPY clients/token_bridge/package.json clients/token_bridge/package-lock.json ./
WORKDIR /usr/src/clients/js
COPY clients/js/package.json clients/js/package-lock.json ./
RUN --mount=type=cache,uid=1000,gid=1000,target=/home/node/.npm \
npm ci
COPY clients/token_bridge ./
RUN npm run build
WORKDIR /usr/src/clients/nft_bridge
COPY clients/nft_bridge/package.json clients/nft_bridge/package-lock.json ./
RUN --mount=type=cache,uid=1000,gid=1000,target=/home/node/.npm \
npm ci
COPY clients/nft_bridge ./
RUN npm run build
COPY clients/js ./
ADD solana /usr/src/solana
ADD proto /usr/src/proto

View File

@ -20,21 +20,13 @@ ENV NODE_OPTIONS=--use-openssl-ca
RUN if [ -e /certs/cert.pem ]; then npm config set cafile /certs/cert.pem; fi
# install token_bridge deps & build
WORKDIR /clients/token_bridge
WORKDIR /clients/js
# copy package.json & package-lock.json by themselves to create a cache layer
COPY clients/token_bridge/package.json clients/token_bridge/package-lock.json ./
COPY clients/js/package.json clients/js/package-lock.json ./
# mount the buildkit cache on npm's cache dir, install dependencies
RUN --mount=type=cache,target=/root/.npm npm ci
# copy the rest of the source files, as a layer on top of the deps
COPY clients/token_bridge ./
RUN npm run build
# install nft_bridge deps & build
WORKDIR /clients/nft_bridge
COPY clients/nft_bridge/package.json clients/nft_bridge/package-lock.json ./
RUN --mount=type=cache,target=/root/.npm npm ci
COPY clients/nft_bridge ./
RUN npm run build
COPY clients/js ./
WORKDIR /

6
clients/js/.env.sample Normal file
View File

@ -0,0 +1,6 @@
#!/usr/bin/env bash
INFURA_KEY=""
ETH_KEY=""
TERRA_MNEMONIC=""
SOLANA_KEY=""

1
clients/js/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.js

27
clients/js/Makefile Normal file
View File

@ -0,0 +1,27 @@
SOURCE_FILES:=$(shell find . -name "*.ts" -not -path "./node_modules/*")
.PHONY: all
all: build
package-lock.json: package.json
npm install
node_modules: package-lock.json
@touch -m node_modules
npm ci
.PHONY: dependencies
dependencies: node_modules
build: node_modules $(SOURCE_FILES)
@mkdir -p build
@touch -m build
npm run build
install: build
@echo Linking binaries
chmod +x build/main.js
npm link
clean:
rm -rf build node_modules

118
clients/js/README.md Normal file
View File

@ -0,0 +1,118 @@
# Wormhole CLI
## Installation
make install
This installs two binaries, `worm-fetch-governance` and `worm` on your `$PATH`.
## Usage
### `worm-fetch-governance`
Usage:
worm-fetch-governance [sequence]
Fetch a governance VAA by sequence number, and print it as hex.
For example
worm-fetch-governance 13940208096455381020
prints
01000000010d0012e6b39c6da90c5dfd3c228edbb78c7...
### `worm`
This is the main CLI tool. To use it, set up `$HOME/.wormhole/.env` with your
private keys, based on `.env.sample` in this folder.
worm [command]
Commands:
worm generate generate VAAs (devnet and testnet only)
worm parse <vaa> Parse a VAA
worm submit <vaa> Execute a VAA
Options:
--help Show help [boolean]
--version Show version number [boolean]
Consult the `--help` flag for using subcommands.
Use `generate` to create VAAs for testing. For example, to create an NFT bridge registration VAA:
worm generate registration --module NFTBridge \
--chain-id 2 \
--contract-address 706abc4E45D419950511e474C7B9Ed348A4a716c \
--guardian-secret cfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0
Use `parse` to parse a VAA into JSON. For example,
worm parse $(worm-fetch-governance 13940208096455381020)
will fetch governance VAA `13940208096455381020` and print it as JSON.
# ...signatures elided
timestamp: 1651416474,
nonce: 1570649151,
emitterChain: 1,
emitterAddress: '0000000000000000000000000000000000000000000000000000000000000004',
sequence: 13940208096455381020n,
consistencyLevel: 32,
payload: {
module: 'Core',
type: 'GuardianSetUpgrade',
chain: 0,
newGuardianSetIndex: 2,
newGuardianSetLength: 19,
newGuardianSet: [
'58cc3ae5c097b213ce3c81979e1b9f9570746aa5',
'ff6cb952589bde862c25ef4392132fb9d4a42157',
'114de8460193bdf3a2fcf81f86a09765f4762fd1',
'107a0086b32d7a0977926a205131d8731d39cbeb',
'8c82b2fd82faed2711d59af0f2499d16e726f6b2',
'11b39756c042441be6d8650b69b54ebe715e2343',
'54ce5b4d348fb74b958e8966e2ec3dbd4958a7cd',
'66b9590e1c41e0b226937bf9217d1d67fd4e91f5',
'74a3bf913953d695260d88bc1aa25a4eee363ef0',
'000ac0076727b35fbea2dac28fee5ccb0fea768e',
'af45ced136b9d9e24903464ae889f5c8a723fc14',
'f93124b7c738843cbb89e864c862c38cddcccf95',
'd2cc37a4dc036a8d232b48f62cdd4731412f4890',
'da798f6896a3331f64b48c12d1d57fd9cbe70811',
'71aa1be1d36cafe3867910f99c09e347899c19c3',
'8192b6e7387ccd768277c17dab1b7a5027c0b3cf',
'178e21ad2e77ae06711549cfbb1f9c7a9d8096e8',
'5e1487f35515d02a92753504a8d75471b9f49edb',
'6fbebc898f403e4773e95feb15e80c9a99c8348d'
]
}
Use `submit` to submit a VAA to a chain. It first parses the VAA and figures out
what's the destination chain and module. For example, a contract upgrade contains both the target chain and module, so the only required argument is the network moniker (`mainnet` or `testnet`):
worm submit $(cat my-nft-registration.txt) --network mainnet
For VAAs that don't have a specific target chain (like registrations or guardian
set upgrades), the script will ask you to specify the target chain.
For example, to submit a guardian set upgrade on all chains, simply run:
worm-fetch-governance 13940208096455381020 > guardian-upgrade.txt
worm submit $(cat guardian-upgrade.txt) --network mainnet --chain oasis
worm submit $(cat guardian-upgrade.txt) --network mainnet --chain aurora
worm submit $(cat guardian-upgrade.txt) --network mainnet --chain fantom
worm submit $(cat guardian-upgrade.txt) --network mainnet --chain karura
worm submit $(cat guardian-upgrade.txt) --network mainnet --chain avalanche
worm submit $(cat guardian-upgrade.txt) --network mainnet --chain polygon
worm submit $(cat guardian-upgrade.txt) --network mainnet --chain bsc
worm submit $(cat guardian-upgrade.txt) --network mainnet --chain solana
worm submit $(cat guardian-upgrade.txt) --network mainnet --chain terra
worm submit $(cat guardian-upgrade.txt) --network mainnet --chain ethereum
The VAA payload type (guardian set upgrade) specifies that this VAA should go to the core bridge, and the tool directs it there.

30
clients/js/elliptic.d.ts vendored Normal file
View File

@ -0,0 +1,30 @@
declare module 'elliptic' {
export interface BN {
length: number;
negative: number;
words: Uint8Array;
toString(format: string?): string;
}
export interface Point {
x: BN;
y: BN;
}
export interface KeyPair {
getPrivate(): BN;
getPublic(): Point;
sign(message: Buffer, options: any): {
r: BN,
s: BN,
recoveryParam: number
};
}
export class ec {
constructor(curveName: string);
genKeyPair(): KeyPair;
keyFromPrivate(priv: any, enc?: any): KeyPair;
keyFromPublic(priv: any, enc: any): KeyPair;
}
}

134
clients/js/evm.ts Normal file
View File

@ -0,0 +1,134 @@
import { BridgeImplementation__factory, Implementation__factory, NFTBridgeImplementation__factory } from "@certusone/wormhole-sdk"
import { ethers } from "ethers"
import { NETWORKS } from "./networks"
import { impossible, Payload } from "./vaa"
import { Contracts, CONTRACTS, EVMChainName } from "@certusone/wormhole-sdk"
import axios from "axios";
export async function execute_governance_evm(
payload: Payload,
vaa: Buffer,
network: "MAINNET" | "TESTNET" | "DEVNET",
chain: EVMChainName
) {
let n = NETWORKS[network][chain]
if (!n.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 rpc: string = n.rpc
let key: string = n.key
let contracts: Contracts = CONTRACTS[network][chain]
let provider = new ethers.providers.JsonRpcProvider(rpc)
let signer = new ethers.Wallet(key, provider)
// Here we apply a set of chain-specific overrides.
// NOTE: some of these might have only been tested on mainnet. If it fails in
// testnet (or devnet), they might require additional guards
let overrides: ethers.Overrides = {}
if (chain === "karura") {
overrides = await getKaruraGasParams(n.rpc)
} else if (chain === "polygon") {
let feeData = await provider.getFeeData();
overrides = {
maxFeePerGas: feeData.maxFeePerGas?.mul(50) || undefined,
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas?.mul(50) || undefined,
};
}
switch (payload.module) {
case "Core":
if (contracts.core === undefined) {
throw Error(`Unknown core contract on ${network} for ${chain}`)
}
let c = new Implementation__factory(signer)
let cb = c.attach(contracts.core)
switch (payload.type) {
case "GuardianSetUpgrade":
console.log("Submitting new guardian set")
console.log("Hash: " + (await cb.submitNewGuardianSet(vaa, overrides)).hash)
break
case "ContractUpgrade":
console.log("Upgrading core contract")
console.log("Hash: " + (await cb.submitContractUpgrade(vaa, overrides)).hash)
break
default:
impossible(payload)
}
break
case "NFTBridge":
if (contracts.nft_bridge === undefined) {
throw Error(`Unknown nft bridge contract on ${network} for ${chain}`)
}
let n = new NFTBridgeImplementation__factory(signer)
let nb = n.attach(contracts.nft_bridge)
switch (payload.type) {
case "ContractUpgrade":
console.log("Upgrading contract")
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")
break
case "RegisterChain":
console.log("Registering chain")
console.log("Hash: " + (await nb.registerChain(vaa, overrides)).hash)
break
default:
impossible(payload)
}
break
case "TokenBridge":
if (contracts.token_bridge === undefined) {
throw Error(`Unknown token bridge contract on ${network} for ${chain}`)
}
let t = new BridgeImplementation__factory(signer)
let tb = t.attach(contracts.token_bridge)
switch (payload.type) {
case "ContractUpgrade":
console.log("Upgrading contract")
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")
break
case "RegisterChain":
console.log("Registering chain")
console.log("Hash: " + (await tb.registerChain(vaa, overrides)).hash)
break
default:
impossible(payload)
}
break
default:
impossible(payload)
}
}
export async function getKaruraGasParams(rpc: string): Promise<{
gasPrice: number;
gasLimit: number;
}> {
const gasLimit = 21000000;
const storageLimit = 64001;
const res = (
await axios.post(rpc, {
id: 0,
jsonrpc: "2.0",
method: "eth_getEthGas",
params: [
{
gasLimit,
storageLimit,
},
],
})
).data.result;
return {
gasLimit: parseInt(res.gasLimit, 16),
gasPrice: parseInt(res.gasPrice, 16),
};
}

288
clients/js/main.ts Normal file
View File

@ -0,0 +1,288 @@
#!/usr/bin/env node
import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import { setDefaultWasm } from "@certusone/wormhole-sdk";
import { execute_governance_solana } from "./solana";
import { execute_governance_evm } from "./evm";
import { execute_governance_terra } from "./terra";
import * as vaa from "./vaa";
import { impossible, Payload, serialiseVAA, VAA } from "./vaa";
import {
assertChain,
ChainName,
CHAINS,
toChainName,
isEVMChain,
toChainId,
} from "@certusone/wormhole-sdk";
setDefaultWasm("node");
const GOVERNANCE_CHAIN = 1;
const GOVERNANCE_EMITTER =
"0000000000000000000000000000000000000000000000000000000000000004";
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))
////////////////////////////////////////////////////////////////////////////////
// Generate
.command(
"generate",
"generate VAAs (devnet and testnet only)",
(yargs) => {
return (
yargs
.option("guardian-secret", {
alias: "g",
required: true,
describe: "Guardians' secret keys",
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: Buffer.from(
argv["contract-address"].padStart(64, "0"),
"hex"
),
};
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: Payload = {
module,
type: "ContractUpgrade",
chain: toChainId(argv["chain"]),
address: Buffer.from(
argv["contract-address"].padStart(64, "0"),
"hex"
),
};
let v = makeVAA(
GOVERNANCE_CHAIN,
GOVERNANCE_EMITTER,
argv["guardian-secret"].split(","),
payload
);
console.log(serialiseVAA(v));
}
)
);
},
(_) => {
yargs.showHelp();
}
)
////////////////////////////////////////////////////////////////////////////////
// Parse
.command(
"parse <vaa>",
"Parse a VAA",
(yargs) => {
return yargs.positional("vaa", {
describe: "vaa",
type: "string",
});
},
async (argv) => {
const buf = Buffer.from(String(argv.vaa), "hex");
const parsed_vaa = vaa.parse(buf);
console.log(parsed_vaa);
}
)
////////////////////////////////////////////////////////////////////////////////
// Submit
.command(
"submit <vaa>",
"Execute a VAA",
(yargs) => {
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,
});
},
async (argv) => {
const vaa_hex = String(argv.vaa);
const buf = Buffer.from(vaa_hex, "hex");
const parsed_vaa = vaa.parse(buf);
if (!vaa.hasPayload(parsed_vaa)) {
throw Error("Couldn't parse VAA payload");
}
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)) {
await execute_governance_evm(parsed_vaa.payload, buf, network, chain);
} else if (chain === "terra") {
await execute_governance_terra(parsed_vaa.payload, buf, network);
} else if (chain === "solana") {
await execute_governance_solana(parsed_vaa, buf, network);
} else if (chain === "algorand") {
throw Error("Algorand is not supported yet");
} else if (chain === "near") {
throw Error("NEAR 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);
}
}
).argv;

263
clients/js/networks.ts Normal file
View File

@ -0,0 +1,263 @@
import { ChainName } from "@certusone/wormhole-sdk";
require("dotenv").config({ path: `${process.env.HOME}/.wormhole/.env` });
function get_env_var(env: string): string | undefined {
const v = process.env[env];
return v;
}
export type Connection = {
rpc: string | undefined;
key: string | undefined;
};
export type ChainConnections = {
[chain in ChainName]: Connection;
};
const MAINNET = {
unset: {
rpc: undefined,
key: undefined,
},
solana: {
rpc: "https://api.mainnet-beta.solana.com",
key: get_env_var("SOLANA_KEY"),
},
terra: {
rpc: "https://lcd.terra.dev",
chain_id: "columbus-5",
key: get_env_var("TERRA_MNEMONIC"),
},
ethereum: {
rpc: `https://mainnet.infura.io/v3/${get_env_var("INFURA_KEY")}`,
key: get_env_var("ETH_KEY"),
},
bsc: {
rpc: "https://bsc-dataseed.binance.org/",
key: get_env_var("ETH_KEY"),
},
polygon: {
rpc: "https://polygon-rpc.com",
key: get_env_var("ETH_KEY"),
},
avalanche: {
rpc: "https://api.avax.network/ext/bc/C/rpc",
key: get_env_var("ETH_KEY"),
},
algorand: {
rpc: undefined,
key: undefined,
},
oasis: {
rpc: "https://emerald.oasis.dev/",
key: get_env_var("ETH_KEY"),
},
fantom: {
rpc: "https://rpc.ftm.tools/",
key: get_env_var("ETH_KEY"),
},
aurora: {
rpc: "https://mainnet.aurora.dev",
key: get_env_var("ETH_KEY"),
},
karura: {
rpc: "https://eth-rpc-karura.aca-api.network/",
key: get_env_var("ETH_KEY"),
},
acala: {
rpc: undefined,
key: get_env_var("ETH_KEY"),
},
klaytn: {
rpc: undefined,
key: get_env_var("ETH_KEY"),
},
celo: {
rpc: undefined,
key: get_env_var("ETH_KEY"),
},
near: {
rpc: undefined,
key: undefined,
},
ropsten: {
rpc: `https://ropsten.infura.io/v3/${get_env_var("INFURA_KEY")}`,
key: get_env_var("ETH_KEY"),
},
};
const TESTNET = {
unset: {
rpc: undefined,
key: undefined,
},
solana: {
rpc: "https://api.devnet.solana.com",
key: get_env_var("SOLANA_KEY"),
},
terra: {
rpc: "https://bombay-lcd.terra.dev",
chain_id: "bombay-12",
key: get_env_var("TERRA_MNEMONIC"),
},
ethereum: {
rpc: `https://goerli.infura.io/v3/${get_env_var("INFURA_KEY")}`,
key: get_env_var("ETH_KEY"),
},
bsc: {
rpc: "https://data-seed-prebsc-1-s1.binance.org:8545",
key: get_env_var("ETH_KEY"),
},
polygon: {
rpc: `https://polygon-mumbai.infura.io/v3/${get_env_var("INFURA_KEY")}`,
key: get_env_var("ETH_KEY"),
},
avalanche: {
rpc: "https://api.avax-test.network/ext/bc/C/rpc",
key: get_env_var("ETH_KEY"),
},
oasis: {
rpc: "https://testnet.emerald.oasis.dev",
key: get_env_var("ETH_KEY"),
},
algorand: {
rpc: undefined,
key: undefined,
},
fantom: {
rpc: "https://rpc.testnet.fantom.network",
key: get_env_var("ETH_KEY"),
},
aurora: {
rpc: "https://testnet.aurora.dev",
key: get_env_var("ETH_KEY"),
},
karura: {
rpc: "http://103.253.145.222:8545",
key: get_env_var("ETH_KEY"),
},
acala: {
rpc: "http://157.245.252.103:8545",
key: get_env_var("ETH_KEY"),
},
klaytn: {
rpc: "https://api.baobab.klaytn.net:8651",
key: get_env_var("ETH_KEY"),
},
celo: {
rpc: undefined,
key: get_env_var("ETH_KEY"),
},
near: {
rpc: undefined,
key: undefined,
},
ropsten: {
rpc: `https://ropsten.infura.io/v3/${get_env_var("INFURA_KEY")}`,
key: get_env_var("ETH_KEY"),
},
};
const DEVNET = {
unset: {
rpc: undefined,
key: undefined,
},
solana: {
rpc: "http://localhost:8899",
key: "J2D4pwDred8P9ioyPEZVLPht885AeYpifsFGUyuzVmiKQosAvmZP4EegaKFrSprBC5vVP1xTvu61vYDWsxBNsYx",
},
terra: {
rpc: "http://localhost:1317",
chain_id: "columbus-5",
key: "notice oak worry limit wrap speak medal online prefer cluster roof addict wrist behave treat actual wasp year salad speed social layer crew genius",
},
ethereum: {
rpc: "http://localhost:8545",
key: "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d",
},
bsc: {
rpc: "http://localhost:8546",
key: "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d",
},
polygon: {
rpc: undefined,
key: undefined,
},
avalanche: {
rpc: undefined,
key: undefined,
},
oasis: {
rpc: undefined,
key: undefined,
},
algorand: {
rpc: undefined,
key: undefined,
},
fantom: {
rpc: undefined,
key: undefined,
},
aurora: {
rpc: undefined,
key: undefined,
},
karura: {
rpc: undefined,
key: undefined,
},
acala: {
rpc: undefined,
key: undefined,
},
klaytn: {
rpc: undefined,
key: undefined,
},
celo: {
rpc: undefined,
key: undefined,
},
near: {
rpc: undefined,
key: undefined,
},
ropsten: {
rpc: undefined,
key: undefined,
},
};
/**
*
* If you get a type error here, it means that a chain you just added does not
* have an entry in TESTNET.
* This is implemented as an ad-hoc type assertion instead of a type annotation
* on TESTNET so that e.g.
*
* ```typescript
* TESTNET['solana'].rpc
* ```
* has type 'string' instead of 'string | undefined'.
*
* (Do not delete this declaration!)
*/
const isTestnetConnections: ChainConnections = TESTNET;
/**
*
* See [[isTestnetContracts]]
*/
const isMainnetConnections: ChainConnections = MAINNET;
/**
*
* See [[isTestnetContracts]]
*/
const isDevnetConnections: ChainConnections = DEVNET;
export const NETWORKS = { MAINNET, TESTNET, DEVNET };

View File

@ -1,25 +1,32 @@
{
"name": "wormhole-client-solana",
"name": "wormhole-client",
"version": "1.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "wormhole-client-solana",
"name": "wormhole-client",
"version": "1.0.0",
"dependencies": {
"@certusone/wormhole-sdk": "^0.2.0",
"@certusone/wormhole-sdk": "^0.3.0",
"@solana/web3.js": "^1.22.0",
"@terra-money/terra.js": "^1.8.9",
"axios": "^0.24.0",
"binary-parser": "^2.0.2",
"bn.js": "^5.2.0",
"bs58": "^4.0.1",
"buffer-layout": "^1.2.2",
"dotenv": "^10.0.0",
"ethers": "^5.4.1",
"js-base64": "^3.6.1",
"npm": "^7.20.0",
"web3": "^1.5.0",
"yargs": "^17.0.1"
},
"bin": {
"worm": "build/main.js",
"worm-fetch-governance": "worm-fetch-governance"
},
"devDependencies": {
"@truffle/hdwallet-provider": "^1.4.1",
"@types/bn.js": "^5.1.0",
@ -27,7 +34,43 @@
"@types/yargs": "^17.0.2",
"copy-dir": "^1.3.0",
"ts-node": "^10.7.0",
"typescript": "^4.3.5"
"typescript": "^4.6"
}
},
"../../sdk/js": {
"name": "@certusone/wormhole-sdk",
"version": "0.3.0",
"extraneous": true,
"license": "Apache-2.0",
"dependencies": {
"@improbable-eng/grpc-web": "^0.14.0",
"@solana/spl-token": "^0.1.8",
"@solana/web3.js": "^1.24.0",
"@terra-money/terra.js": "^3.0.7",
"algosdk": "^1.15.0",
"axios": "^0.24.0",
"bech32": "^2.0.0",
"js-base64": "^3.6.1",
"protobufjs": "^6.11.2",
"rxjs": "^7.3.0"
},
"devDependencies": {
"@improbable-eng/grpc-web-node-http-transport": "^0.15.0",
"@openzeppelin/contracts": "^4.2.0",
"@typechain/ethers-v5": "^7.0.1",
"@types/jest": "^27.0.2",
"@types/long": "^4.0.1",
"@types/node": "^16.6.1",
"@types/react": "^17.0.19",
"copy-dir": "^1.3.0",
"ethers": "^5.4.4",
"jest": "^27.3.1",
"prettier": "^2.3.2",
"ts-jest": "^27.0.7",
"tslint": "^6.1.3",
"tslint-config-prettier": "^1.18.0",
"typescript": "^4.3.5",
"web3": "^1.6.1"
}
},
"node_modules/@babel/code-frame": {
@ -487,14 +530,15 @@
}
},
"node_modules/@certusone/wormhole-sdk": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/@certusone/wormhole-sdk/-/wormhole-sdk-0.2.0.tgz",
"integrity": "sha512-M5DnyPbt8Wm2gSG596yH3Fw1cXulQSzJ/4b1wVeQBrZ4g2s0ztSLgSctUGTGwR4wacK5R1IeGo9jfn29KBmdwA==",
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@certusone/wormhole-sdk/-/wormhole-sdk-0.3.0.tgz",
"integrity": "sha512-jLZ5x8oIwBpFwVK4zYOf8ejZD7dFIkEc0P2/k7yazM+4G+CY2fKBlZnQ06OFjqbj8BvI0q/kshZcLqJx2iwObw==",
"dependencies": {
"@improbable-eng/grpc-web": "^0.14.0",
"@solana/spl-token": "^0.1.8",
"@solana/web3.js": "^1.24.0",
"@terra-money/terra.js": "^3.0.7",
"algosdk": "^1.15.0",
"axios": "^0.24.0",
"bech32": "^2.0.0",
"js-base64": "^3.6.1",
@ -503,12 +547,12 @@
}
},
"node_modules/@certusone/wormhole-sdk/node_modules/@terra-money/terra.js": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/@terra-money/terra.js/-/terra.js-3.0.7.tgz",
"integrity": "sha512-moeVBWqIPZaV0HmCY127Y9H/MsuFtH1VgW0xEvDQWqu1jpKhK5CtPHMLKNje3mKSjU8A7vXZ8hlW3KobqP2poQ==",
"version": "3.0.11",
"resolved": "https://registry.npmjs.org/@terra-money/terra.js/-/terra.js-3.0.11.tgz",
"integrity": "sha512-qabXxsycWF1tEcFE3dG7FFXuIcXPlSeYgha5IYU+x4QNKiZuxT9Asi84HrCUSajQkZZ3N7ORfr+aGGE84HD+uw==",
"dependencies": {
"@terra-money/terra.proto": "^0.1.7",
"axios": "^0.24.0",
"axios": "^0.26.1",
"bech32": "^2.0.0",
"bip32": "^2.0.6",
"bip39": "^3.0.3",
@ -525,6 +569,14 @@
"node": ">=14"
}
},
"node_modules/@certusone/wormhole-sdk/node_modules/@terra-money/terra.js/node_modules/axios": {
"version": "0.26.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz",
"integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==",
"dependencies": {
"follow-redirects": "^1.14.8"
}
},
"node_modules/@certusone/wormhole-sdk/node_modules/bech32": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz",
@ -1949,9 +2001,9 @@
"license": "MIT"
},
"node_modules/@types/long": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz",
"integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w=="
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
"integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA=="
},
"node_modules/@types/node": {
"version": "16.4.1",
@ -2074,6 +2126,54 @@
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/algo-msgpack-with-bigint": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/algo-msgpack-with-bigint/-/algo-msgpack-with-bigint-2.1.1.tgz",
"integrity": "sha512-F1tGh056XczEaEAqu7s+hlZUDWwOBT70Eq0lfMpBP2YguSQVyxRbprLq5rELXKQOyOaixTWYhMeMQMzP0U5FoQ==",
"engines": {
"node": ">= 10"
}
},
"node_modules/algosdk": {
"version": "1.16.0",
"resolved": "https://registry.npmjs.org/algosdk/-/algosdk-1.16.0.tgz",
"integrity": "sha512-oD1PEuzjJFXSx7/zwFB5N337kqykhxaPXEGgbw4IWKgjq1eRn++RAa+FH9PRwv+n9nOdzFfy83uWYwkVqliRuw==",
"dependencies": {
"algo-msgpack-with-bigint": "^2.1.1",
"buffer": "^6.0.2",
"hi-base32": "^0.5.1",
"js-sha256": "^0.9.0",
"js-sha3": "^0.8.0",
"js-sha512": "^0.8.0",
"json-bigint": "^1.0.0",
"superagent": "^6.1.0",
"tweetnacl": "^1.0.3",
"url-parse": "^1.5.1"
}
},
"node_modules/algosdk/node_modules/buffer": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"dependencies": {
"base64-js": "^1.3.1",
"ieee754": "^1.2.1"
}
},
"node_modules/ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
@ -2324,6 +2424,14 @@
"node": "*"
}
},
"node_modules/binary-parser": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/binary-parser/-/binary-parser-2.0.2.tgz",
"integrity": "sha512-F2JeaLmExQ0vOEbS5ERzkxePKWTPqJPV6Z04/owaXMQLlW1Kt9v2gUz3SocR40JQGtrUeZu3j6prZDeuEG8Dng==",
"engines": {
"node": ">=12"
}
},
"node_modules/bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
@ -2854,6 +2962,11 @@
"version": "2.20.3",
"license": "MIT"
},
"node_modules/component-emitter": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
},
"node_modules/compound-subject": {
"version": "0.0.1",
"license": "MIT"
@ -4072,8 +4185,7 @@
"node_modules/fast-safe-stringify": {
"version": "2.0.8",
"resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.8.tgz",
"integrity": "sha512-lXatBjf3WPjmWD6DpIZxkeSsCOwqI0maYMpgDlx8g4U2qi4lbjA9oH/HD2a87G+KfsUmo5WbJFmqBZlPxtptag==",
"dev": true
"integrity": "sha512-lXatBjf3WPjmWD6DpIZxkeSsCOwqI0maYMpgDlx8g4U2qi4lbjA9oH/HD2a87G+KfsUmo5WbJFmqBZlPxtptag=="
},
"node_modules/fetch-ponyfill": {
"version": "4.1.0",
@ -4161,6 +4273,15 @@
"node": ">= 0.12"
}
},
"node_modules/formidable": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz",
"integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==",
"deprecated": "Please upgrade to latest, formidable@v2 or formidable@v3! Check these notes: https://bit.ly/2ZEqIau",
"funding": {
"url": "https://ko-fi.com/tunnckoCore/commissions"
}
},
"node_modules/forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@ -4302,9 +4423,9 @@
}
},
"node_modules/google-protobuf": {
"version": "3.19.4",
"resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.19.4.tgz",
"integrity": "sha512-OIPNCxsG2lkIvf+P5FNfJ/Km95CsXOBecS9ZcAU6m2Rq3svc0Apl9nB3GMDNKfQ9asNv4KjyAqGwPQFrVle3Yg=="
"version": "3.20.1",
"resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.20.1.tgz",
"integrity": "sha512-XMf1+O32FjYIV3CYu6Tuh5PNbfNEU5Xu22X+Xkdb/DUexFlCzhvv7d5Iirm4AOwn8lv4al1YvIhzGrg2j9Zfzw=="
},
"node_modules/got": {
"version": "9.6.0",
@ -4432,6 +4553,11 @@
"minimalistic-assert": "^1.0.1"
}
},
"node_modules/hi-base32": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/hi-base32/-/hi-base32-0.5.1.tgz",
"integrity": "sha512-EmBBpvdYh/4XxsnUybsPag6VikPYnN30td+vQk+GI3qpahVEG9+gTkG0aXVxTjBqQ5T6ijbWIu77O+C5WFWsnA=="
},
"node_modules/hmac-drbg": {
"version": "1.0.1",
"license": "MIT",
@ -4870,10 +4996,20 @@
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.6.1.tgz",
"integrity": "sha512-Frdq2+tRRGLQUIQOgsIGSCd1VePCS2fsddTG5dTCqR0JHgltXWfsxnY0gIXPoMeRmdom6Oyq+UMOFg5suduOjQ=="
},
"node_modules/js-sha256": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz",
"integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA=="
},
"node_modules/js-sha3": {
"version": "0.8.0",
"license": "MIT"
},
"node_modules/js-sha512": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/js-sha512/-/js-sha512-0.8.0.tgz",
"integrity": "sha512-PWsmefG6Jkodqt+ePTvBZCSMFgN7Clckjd0O7su3I0+BW2QWUTJNzjktHsztGLhncP2h8mcF9V9Y2Ha59pAViQ=="
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@ -4905,6 +5041,14 @@
"node": ">=4"
}
},
"node_modules/json-bigint": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
"integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
"dependencies": {
"bignumber.js": "^9.0.0"
}
},
"node_modules/json-buffer": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz",
@ -5201,6 +5345,22 @@
"node": ">=0.10.0"
}
},
"node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/lru-cache/node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"node_modules/ltgt": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz",
@ -8622,6 +8782,11 @@
"node": ">=0.10.0"
}
},
"node_modules/querystringify": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="
},
"node_modules/randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
@ -8725,6 +8890,11 @@
"node": ">=0.10.0"
}
},
"node_modules/requires-port": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
},
"node_modules/resolve": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
@ -8805,9 +8975,9 @@
"dev": true
},
"node_modules/rxjs": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.4.tgz",
"integrity": "sha512-h5M3Hk78r6wAheJF0a5YahB1yRQKCsZ4MsGdZ5O9ETbVtjPcScGfrMmoOq7EBsCRzd4BDkvDJ7ogP8Sz5tTFiQ==",
"version": "7.5.5",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz",
"integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==",
"dependencies": {
"tslib": "^2.1.0"
}
@ -9133,6 +9303,101 @@
"npm": ">=3"
}
},
"node_modules/superagent": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/superagent/-/superagent-6.1.0.tgz",
"integrity": "sha512-OUDHEssirmplo3F+1HWKUrUjvnQuA+nZI6i/JJBdXb5eq9IyEQwPyPpqND+SSsxf6TygpBEkUjISVRN4/VOpeg==",
"deprecated": "Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at <https://github.com/visionmedia/superagent/releases>.",
"dependencies": {
"component-emitter": "^1.3.0",
"cookiejar": "^2.1.2",
"debug": "^4.1.1",
"fast-safe-stringify": "^2.0.7",
"form-data": "^3.0.0",
"formidable": "^1.2.2",
"methods": "^1.1.2",
"mime": "^2.4.6",
"qs": "^6.9.4",
"readable-stream": "^3.6.0",
"semver": "^7.3.2"
},
"engines": {
"node": ">= 7.0.0"
}
},
"node_modules/superagent/node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"dependencies": {
"ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/superagent/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==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/superagent/node_modules/mime": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
"integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
"bin": {
"mime": "cli.js"
},
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/superagent/node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/superagent/node_modules/qs": {
"version": "6.10.3",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz",
"integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==",
"dependencies": {
"side-channel": "^1.0.4"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/superagent/node_modules/semver": {
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"dependencies": {
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/superstruct": {
"version": "0.14.2",
"license": "MIT"
@ -9424,9 +9689,9 @@
}
},
"node_modules/tslib": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
},
"node_modules/tunnel-agent": {
"version": "0.6.0",
@ -9481,9 +9746,10 @@
"integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g=="
},
"node_modules/typescript": {
"version": "4.3.5",
"version": "4.6.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz",
"integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@ -9535,6 +9801,15 @@
"punycode": "^2.1.0"
}
},
"node_modules/url-parse": {
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
"dependencies": {
"querystringify": "^2.1.1",
"requires-port": "^1.0.0"
}
},
"node_modules/url-parse-lax": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz",
@ -10657,14 +10932,15 @@
}
},
"@certusone/wormhole-sdk": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/@certusone/wormhole-sdk/-/wormhole-sdk-0.2.0.tgz",
"integrity": "sha512-M5DnyPbt8Wm2gSG596yH3Fw1cXulQSzJ/4b1wVeQBrZ4g2s0ztSLgSctUGTGwR4wacK5R1IeGo9jfn29KBmdwA==",
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@certusone/wormhole-sdk/-/wormhole-sdk-0.3.0.tgz",
"integrity": "sha512-jLZ5x8oIwBpFwVK4zYOf8ejZD7dFIkEc0P2/k7yazM+4G+CY2fKBlZnQ06OFjqbj8BvI0q/kshZcLqJx2iwObw==",
"requires": {
"@improbable-eng/grpc-web": "^0.14.0",
"@solana/spl-token": "^0.1.8",
"@solana/web3.js": "^1.24.0",
"@terra-money/terra.js": "^3.0.7",
"algosdk": "^1.15.0",
"axios": "^0.24.0",
"bech32": "^2.0.0",
"js-base64": "^3.6.1",
@ -10673,12 +10949,12 @@
},
"dependencies": {
"@terra-money/terra.js": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/@terra-money/terra.js/-/terra.js-3.0.7.tgz",
"integrity": "sha512-moeVBWqIPZaV0HmCY127Y9H/MsuFtH1VgW0xEvDQWqu1jpKhK5CtPHMLKNje3mKSjU8A7vXZ8hlW3KobqP2poQ==",
"version": "3.0.11",
"resolved": "https://registry.npmjs.org/@terra-money/terra.js/-/terra.js-3.0.11.tgz",
"integrity": "sha512-qabXxsycWF1tEcFE3dG7FFXuIcXPlSeYgha5IYU+x4QNKiZuxT9Asi84HrCUSajQkZZ3N7ORfr+aGGE84HD+uw==",
"requires": {
"@terra-money/terra.proto": "^0.1.7",
"axios": "^0.24.0",
"axios": "^0.26.1",
"bech32": "^2.0.0",
"bip32": "^2.0.6",
"bip39": "^3.0.3",
@ -10690,6 +10966,16 @@
"tmp": "^0.2.1",
"utf-8-validate": "^5.0.5",
"ws": "^7.5.5"
},
"dependencies": {
"axios": {
"version": "0.26.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz",
"integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==",
"requires": {
"follow-redirects": "^1.14.8"
}
}
}
},
"bech32": {
@ -11717,9 +12003,9 @@
"version": "4.14.171"
},
"@types/long": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz",
"integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w=="
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
"integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA=="
},
"@types/node": {
"version": "16.4.1"
@ -11813,6 +12099,39 @@
"uri-js": "^4.2.2"
}
},
"algo-msgpack-with-bigint": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/algo-msgpack-with-bigint/-/algo-msgpack-with-bigint-2.1.1.tgz",
"integrity": "sha512-F1tGh056XczEaEAqu7s+hlZUDWwOBT70Eq0lfMpBP2YguSQVyxRbprLq5rELXKQOyOaixTWYhMeMQMzP0U5FoQ=="
},
"algosdk": {
"version": "1.16.0",
"resolved": "https://registry.npmjs.org/algosdk/-/algosdk-1.16.0.tgz",
"integrity": "sha512-oD1PEuzjJFXSx7/zwFB5N337kqykhxaPXEGgbw4IWKgjq1eRn++RAa+FH9PRwv+n9nOdzFfy83uWYwkVqliRuw==",
"requires": {
"algo-msgpack-with-bigint": "^2.1.1",
"buffer": "^6.0.2",
"hi-base32": "^0.5.1",
"js-sha256": "^0.9.0",
"js-sha3": "^0.8.0",
"js-sha512": "^0.8.0",
"json-bigint": "^1.0.0",
"superagent": "^6.1.0",
"tweetnacl": "^1.0.3",
"url-parse": "^1.5.1"
},
"dependencies": {
"buffer": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
"requires": {
"base64-js": "^1.3.1",
"ieee754": "^1.2.1"
}
}
}
},
"ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
@ -12014,6 +12333,11 @@
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz",
"integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA=="
},
"binary-parser": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/binary-parser/-/binary-parser-2.0.2.tgz",
"integrity": "sha512-F2JeaLmExQ0vOEbS5ERzkxePKWTPqJPV6Z04/owaXMQLlW1Kt9v2gUz3SocR40JQGtrUeZu3j6prZDeuEG8Dng=="
},
"bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
@ -12450,6 +12774,11 @@
"commander": {
"version": "2.20.3"
},
"component-emitter": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
},
"compound-subject": {
"version": "0.0.1"
},
@ -13565,8 +13894,7 @@
"fast-safe-stringify": {
"version": "2.0.8",
"resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.8.tgz",
"integrity": "sha512-lXatBjf3WPjmWD6DpIZxkeSsCOwqI0maYMpgDlx8g4U2qi4lbjA9oH/HD2a87G+KfsUmo5WbJFmqBZlPxtptag==",
"dev": true
"integrity": "sha512-lXatBjf3WPjmWD6DpIZxkeSsCOwqI0maYMpgDlx8g4U2qi4lbjA9oH/HD2a87G+KfsUmo5WbJFmqBZlPxtptag=="
},
"fetch-ponyfill": {
"version": "4.1.0",
@ -13633,6 +13961,11 @@
"mime-types": "^2.1.12"
}
},
"formidable": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz",
"integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ=="
},
"forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@ -13747,9 +14080,9 @@
"dev": true
},
"google-protobuf": {
"version": "3.19.4",
"resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.19.4.tgz",
"integrity": "sha512-OIPNCxsG2lkIvf+P5FNfJ/Km95CsXOBecS9ZcAU6m2Rq3svc0Apl9nB3GMDNKfQ9asNv4KjyAqGwPQFrVle3Yg=="
"version": "3.20.1",
"resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.20.1.tgz",
"integrity": "sha512-XMf1+O32FjYIV3CYu6Tuh5PNbfNEU5Xu22X+Xkdb/DUexFlCzhvv7d5Iirm4AOwn8lv4al1YvIhzGrg2j9Zfzw=="
},
"got": {
"version": "9.6.0",
@ -13842,6 +14175,11 @@
"minimalistic-assert": "^1.0.1"
}
},
"hi-base32": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/hi-base32/-/hi-base32-0.5.1.tgz",
"integrity": "sha512-EmBBpvdYh/4XxsnUybsPag6VikPYnN30td+vQk+GI3qpahVEG9+gTkG0aXVxTjBqQ5T6ijbWIu77O+C5WFWsnA=="
},
"hmac-drbg": {
"version": "1.0.1",
"requires": {
@ -14137,9 +14475,19 @@
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.6.1.tgz",
"integrity": "sha512-Frdq2+tRRGLQUIQOgsIGSCd1VePCS2fsddTG5dTCqR0JHgltXWfsxnY0gIXPoMeRmdom6Oyq+UMOFg5suduOjQ=="
},
"js-sha256": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz",
"integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA=="
},
"js-sha3": {
"version": "0.8.0"
},
"js-sha512": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/js-sha512/-/js-sha512-0.8.0.tgz",
"integrity": "sha512-PWsmefG6Jkodqt+ePTvBZCSMFgN7Clckjd0O7su3I0+BW2QWUTJNzjktHsztGLhncP2h8mcF9V9Y2Ha59pAViQ=="
},
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@ -14162,6 +14510,14 @@
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
"dev": true
},
"json-bigint": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
"integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
"requires": {
"bignumber.js": "^9.0.0"
}
},
"json-buffer": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz",
@ -14425,6 +14781,21 @@
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
"integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA=="
},
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"requires": {
"yallist": "^4.0.0"
},
"dependencies": {
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
}
}
},
"ltgt": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz",
@ -16881,6 +17252,11 @@
"strict-uri-encode": "^1.0.0"
}
},
"querystringify": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="
},
"randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
@ -16966,6 +17342,11 @@
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
},
"requires-port": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
},
"resolve": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
@ -17033,9 +17414,9 @@
"dev": true
},
"rxjs": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.4.tgz",
"integrity": "sha512-h5M3Hk78r6wAheJF0a5YahB1yRQKCsZ4MsGdZ5O9ETbVtjPcScGfrMmoOq7EBsCRzd4BDkvDJ7ogP8Sz5tTFiQ==",
"version": "7.5.5",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz",
"integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==",
"requires": {
"tslib": "^2.1.0"
}
@ -17273,6 +17654,70 @@
"is-hex-prefixed": "1.0.0"
}
},
"superagent": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/superagent/-/superagent-6.1.0.tgz",
"integrity": "sha512-OUDHEssirmplo3F+1HWKUrUjvnQuA+nZI6i/JJBdXb5eq9IyEQwPyPpqND+SSsxf6TygpBEkUjISVRN4/VOpeg==",
"requires": {
"component-emitter": "^1.3.0",
"cookiejar": "^2.1.2",
"debug": "^4.1.1",
"fast-safe-stringify": "^2.0.7",
"form-data": "^3.0.0",
"formidable": "^1.2.2",
"methods": "^1.1.2",
"mime": "^2.4.6",
"qs": "^6.9.4",
"readable-stream": "^3.6.0",
"semver": "^7.3.2"
},
"dependencies": {
"debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"requires": {
"ms": "2.1.2"
}
},
"form-data": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
"integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
}
},
"mime": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
"integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg=="
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"qs": {
"version": "6.10.3",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz",
"integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==",
"requires": {
"side-channel": "^1.0.4"
}
},
"semver": {
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"requires": {
"lru-cache": "^6.0.0"
}
}
}
},
"superstruct": {
"version": "0.14.2"
},
@ -17482,9 +17927,9 @@
}
},
"tslib": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
"integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
},
"tunnel-agent": {
"version": "0.6.0",
@ -17528,7 +17973,9 @@
"integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g=="
},
"typescript": {
"version": "4.3.5",
"version": "4.6.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.3.tgz",
"integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==",
"dev": true
},
"ultron": {
@ -17565,6 +18012,15 @@
"punycode": "^2.1.0"
}
},
"url-parse": {
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
"requires": {
"querystringify": "^2.1.1",
"requires-port": "^1.0.0"
}
},
"url-parse-lax": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz",

View File

@ -1,24 +1,30 @@
{
"name": "wormhole-client-solana",
"name": "wormhole-client",
"version": "1.0.0",
"dependencies": {
"@certusone/wormhole-sdk": "^0.2.0",
"@certusone/wormhole-sdk": "^0.3.0",
"@solana/web3.js": "^1.22.0",
"@terra-money/terra.js": "^1.8.9",
"axios": "^0.24.0",
"binary-parser": "^2.0.2",
"bn.js": "^5.2.0",
"bs58": "^4.0.1",
"buffer-layout": "^1.2.2",
"dotenv": "^10.0.0",
"ethers": "^5.4.1",
"js-base64": "^3.6.1",
"npm": "^7.20.0",
"web3": "^1.5.0",
"yargs": "^17.0.1"
},
"bin": {
"worm": "./build/main.js",
"worm-fetch-governance": "./worm-fetch-governance"
},
"scripts": {
"start": "tsc && node main.js",
"start": "ts-node main.ts",
"build": "tsc",
"test": "echo \"Error: no test specified\" && exit 1",
"main": "ts-node main.ts"
"test": "echo \"Error: no test specified\" && exit 1"
},
"devDependencies": {
"@truffle/hdwallet-provider": "^1.4.1",
@ -27,6 +33,6 @@
"@types/yargs": "^17.0.2",
"copy-dir": "^1.3.0",
"ts-node": "^10.7.0",
"typescript": "^4.3.5"
"typescript": "^4.6"
}
}

101
clients/js/solana.ts Normal file
View File

@ -0,0 +1,101 @@
import * as web3s from '@solana/web3.js'
import { NETWORKS } from "./networks";
import { impossible, Payload, VAA } from "./vaa";
import base58 from "bs58";
import { importCoreWasm, importNftWasm, importTokenWasm, ixFromRust } from "@certusone/wormhole-sdk";
import { CONTRACTS } from "@certusone/wormhole-sdk"
import { postVaaSolanaWithRetry } from "@certusone/wormhole-sdk"
export async function execute_governance_solana(
v: VAA<Payload>,
vaa: Buffer,
network: "MAINNET" | "TESTNET" | "DEVNET"
) {
let ix: web3s.TransactionInstruction
let connection = setupConnection(NETWORKS[network].solana.rpc)
let bridge_id = new web3s.PublicKey(CONTRACTS[network].solana.core)
let token_bridge_id = new web3s.PublicKey(CONTRACTS[network].solana.token_bridge)
let nft_bridge_id = new web3s.PublicKey(CONTRACTS[network].solana.nft_bridge)
let from = web3s.Keypair.fromSecretKey(base58.decode(NETWORKS[network].solana.key))
switch (v.payload.module) {
case "Core":
const bridge = await importCoreWasm()
switch (v.payload.type) {
case "GuardianSetUpgrade":
console.log("Submitting new guardian set")
ix = bridge.update_guardian_set_ix(bridge_id.toString(), from.publicKey.toString(), vaa);
break
case "ContractUpgrade":
console.log("Upgrading core contract")
ix = bridge.upgrade_contract_ix(bridge_id.toString(), from.publicKey.toString(), from.publicKey.toString(), vaa);
break
default:
ix = impossible(v.payload)
}
break
case "NFTBridge":
const nft_bridge = await importNftWasm()
switch (v.payload.type) {
case "ContractUpgrade":
console.log("Upgrading contract")
ix = nft_bridge.upgrade_contract_ix(nft_bridge_id.toString(), bridge_id.toString(), from.publicKey.toString(), from.publicKey.toString(), vaa);
break
case "RegisterChain":
console.log("Registering chain")
ix = nft_bridge.register_chain_ix(nft_bridge_id.toString(), bridge_id.toString(), from.publicKey.toString(), vaa);
break
default:
ix = impossible(v.payload)
}
break
case "TokenBridge":
const token_bridge = await importTokenWasm()
switch (v.payload.type) {
case "ContractUpgrade":
console.log("Upgrading contract")
ix = token_bridge.upgrade_contract_ix(token_bridge_id.toString(), bridge_id.toString(), from.publicKey.toString(), from.publicKey.toString(), vaa)
break
case "RegisterChain":
console.log("Registering chain")
ix = token_bridge.register_chain_ix(token_bridge_id.toString(), bridge_id.toString(), from.publicKey.toString(), vaa)
break
default:
ix = impossible(v.payload)
}
break
default:
ix = impossible(v.payload)
}
// First upload the VAA
await postVaaSolanaWithRetry(connection,
async (tx) => {
tx.partialSign(from)
return tx
},
bridge_id.toString(), from.publicKey.toString(), vaa, 5)
// Then do the actual thing
let transaction = new web3s.Transaction().add(ixFromRust(ix))
let signature = await web3s.sendAndConfirmTransaction(
connection,
transaction,
[from],
{
skipPreflight: true
}
)
console.log('SIGNATURE', signature)
}
function setupConnection(rpc: string): web3s.Connection {
return new web3s.Connection(
rpc,
'confirmed',
)
}

115
clients/js/terra.ts Normal file
View File

@ -0,0 +1,115 @@
import { LCDClient, MnemonicKey, MsgExecuteContract } from "@terra-money/terra.js";
import { fromUint8Array } from "js-base64";
import { impossible, Payload } from "./vaa";
import { NETWORKS } from "./networks"
import { CONTRACTS } from "@certusone/wormhole-sdk"
export async function execute_governance_terra(
payload: Payload,
vaa: Buffer,
network: "MAINNET" | "TESTNET" | "DEVNET"
) {
let n = NETWORKS[network]['terra']
let contracts = CONTRACTS[network]['terra']
const terra = new LCDClient({
URL: n.rpc,
chainID: n.chain_id,
})
const wallet = terra.wallet(new MnemonicKey({
mnemonic: n.key
}))
let target_contract: string
let execute_msg: object
switch (payload.module) {
case "Core":
target_contract = contracts.core
// sigh...
execute_msg = {
submit_v_a_a: {
vaa: fromUint8Array(vaa)
},
}
switch (payload.type) {
case "GuardianSetUpgrade":
console.log("Submitting new guardian set")
break
case "ContractUpgrade":
console.log("Upgrading core contract")
break
default:
impossible(payload)
}
break
case "NFTBridge":
if (contracts.nft_bridge === undefined) {
// 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")
}
target_contract = contracts.nft_bridge
execute_msg = {
submit_vaa: {
data: fromUint8Array(vaa)
},
}
switch (payload.type) {
case "ContractUpgrade":
console.log("Upgrading contract")
break
case "RegisterChain":
console.log("Registering chain")
break
default:
impossible(payload)
}
break
case "TokenBridge":
target_contract = contracts.token_bridge
execute_msg = {
submit_vaa: {
data: fromUint8Array(vaa)
},
}
switch (payload.type) {
case "ContractUpgrade":
console.log("Upgrading contract")
break
case "RegisterChain":
console.log("Registering chain")
break
default:
impossible(payload)
execute_msg = impossible(payload)
}
break
default:
target_contract = impossible(payload)
execute_msg = impossible(payload)
}
const transaction = new MsgExecuteContract(
wallet.key.accAddress,
target_contract,
execute_msg,
{ uluna: 1000 }
)
wallet
.createAndSignTx({
msgs: [transaction],
memo: '',
})
.then(tx => terra.tx.broadcast(tx))
.then(result => {
console.log(result)
console.log(`TX hash: ${result.txhash}`)
})
}

View File

@ -3,8 +3,8 @@
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
"incremental": true, /* Enable incremental compilation */
"target": "es2019", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
@ -14,19 +14,19 @@
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
// "outDir": "./", /* Redirect output structure to the directory. */
"outDir": "./build", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
"downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
"strict": false, /* Enable all strict type-checking options. */
"noImplicitAny": false, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */

7
clients/js/tsfmt.json Normal file
View File

@ -0,0 +1,7 @@
{
"indentSize": 2,
"tabSize": 2,
"insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces": false,
"placeOpenBraceOnNewLineForFunctions": false,
"placeOpenBraceOnNewLineForControlBlocks": false
}

450
clients/js/vaa.ts Normal file
View File

@ -0,0 +1,450 @@
import { Parser } from "binary-parser"
import { ethers } from "ethers"
import { solidityKeccak256 } from "ethers/lib/utils"
import * as elliptic from "elliptic"
export interface Signature {
guardianSetIndex: number
signature: string
}
export interface VAA<T> {
version: number
guardianSetIndex: number
signatures: Signature[]
timestamp: number
nonce: number
emitterChain: number
emitterAddress: string
sequence: bigint
consistencyLevel: number
payload: T
}
class P<T> {
private parser: Parser
constructor(parser: Parser) {
this.parser = parser
}
// Try to parse a buffer with a parser, and return null if it failed due to an
// assertion error.
parse(buffer: Buffer): T | null {
try {
let result = this.parser.parse(buffer)
delete result['end']
return result
} catch (e: any) {
if (e.message?.includes("Assertion error")) {
return null
} else {
throw e
}
}
}
or<U>(other: P<U>): P<T | U> {
let p = new P<T | U>(other.parser);
p.parse = (buffer: Buffer): T | U | null => {
return this.parse(buffer) ?? other.parse(buffer)
}
return p
}
}
// All the different types of payloads
export type Payload =
GuardianSetUpgrade
| CoreContractUpgrade
| PortalContractUpgrade<"TokenBridge">
| PortalContractUpgrade<"NFTBridge">
| PortalRegisterChain<"TokenBridge">
| PortalRegisterChain<"NFTBridge">
// TODO: add other types of payloads
export function parse(buffer: Buffer): VAA<Payload | null> {
const vaa = parseEnvelope(buffer)
const parser = guardianSetUpgradeParser
.or(coreContractUpgradeParser)
.or(portalContractUpgradeParser("TokenBridge"))
.or(portalContractUpgradeParser("NFTBridge"))
.or(portalRegisterChainParser("TokenBridge"))
.or(portalRegisterChainParser("NFTBridge"))
const payload = parser.parse(vaa.payload)
var myVAA = { ...vaa, payload }
return myVAA
}
export function hasPayload(vaa: VAA<Payload | null>): vaa is VAA<Payload> {
return vaa.payload !== null
}
// Parse the VAA envelope without looking into the payload.
// If you want to parse the payload as well, use 'parse'.
export function parseEnvelope(buffer: Buffer): VAA<Buffer> {
var vaa = vaaParser.parse(buffer)
delete vaa['end']
delete vaa['signatureCount']
vaa.payload = Buffer.from(vaa.payload)
return vaa
}
// Parse a signature
const signatureParser = new Parser()
.endianess("big")
.uint8("guardianSetIndex")
.array("signature", {
type: "uint8",
lengthInBytes: 65,
formatter: arr => Buffer.from(arr).toString("hex")
})
function serialiseSignature(sig: Signature): string {
const body = [
encode("uint8", sig.guardianSetIndex),
sig.signature
]
return body.join("")
}
// Parse a vaa envelope. The payload is returned as a byte array.
const vaaParser = new Parser()
.endianess("big")
.uint8("version")
.uint32("guardianSetIndex")
.uint8("signatureCount")
.array("signatures", {
type: signatureParser,
length: "signatureCount"
})
.uint32("timestamp")
.uint32("nonce")
.uint16("emitterChain")
.array("emitterAddress", {
type: "uint8",
lengthInBytes: 32,
formatter: arr => Buffer.from(arr).toString("hex")
})
.uint64("sequence")
.uint8("consistencyLevel")
.array("payload", {
type: "uint8",
readUntil: "eof"
})
.string("end", {
greedy: true,
assert: str => str === ""
})
export function serialiseVAA(vaa: VAA<Payload>) {
const body = [
encode("uint8", vaa.version),
encode("uint32", vaa.guardianSetIndex),
encode("uint8", vaa.signatures.length),
...(vaa.signatures.map((sig) => serialiseSignature(sig))),
vaaBody(vaa)
]
return body.join("")
}
function vaaBody(vaa: VAA<Payload>) {
let payload = vaa.payload
let payload_str: string
switch (payload.module) {
case "Core":
switch (payload.type) {
case "GuardianSetUpgrade":
payload_str = serialiseGuardianSetUpgrade(payload)
break
case "ContractUpgrade":
payload_str = serialiseCoreContractUpgrade(payload)
break
}
break
case "NFTBridge":
case "TokenBridge":
switch (payload.type) {
case "ContractUpgrade":
payload_str = serialisePortalContractUpgrade(payload)
break
case "RegisterChain":
payload_str = serialisePortalRegisterChain(payload)
break
}
break
}
const body = [
encode("uint32", vaa.timestamp),
encode("uint32", vaa.nonce),
encode("uint16", vaa.emitterChain),
encode("bytes32", Buffer.from(vaa.emitterAddress, "hex")),
encode("uint64", vaa.sequence),
encode("uint8", vaa.consistencyLevel),
payload_str
]
return body.join("")
}
export function sign(signers: string[], vaa: VAA<Payload>): Signature[] {
const body = vaaBody(vaa)
const hash = solidityKeccak256(["bytes"], [solidityKeccak256(["bytes"], ["0x" + body])])
const ec = new elliptic.ec("secp256k1")
return signers.map((signer, i) => {
const key = ec.keyFromPrivate(signer)
const signature = key.sign(Buffer.from(hash.substr(2), "hex"), { canonical: true })
const packed = [
signature.r.toString("hex").padStart(64, "0"),
signature.s.toString("hex").padStart(64, "0"),
encode("uint8", signature.recoveryParam)
].join("")
return {
guardianSetIndex: i,
signature: packed
}
})
}
// Parse an address of given length, and render it as hex
const addressParser = (length: number) => new Parser()
.endianess("big")
.array("address", {
type: "uint8",
lengthInBytes: length,
formatter: (arr) => Buffer.from(arr).toString("hex")
})
////////////////////////////////////////////////////////////////////////////////
// Guardian set upgrade
export interface GuardianSetUpgrade {
module: "Core"
type: "GuardianSetUpgrade"
chain: number
newGuardianSetIndex: number
newGuardianSetLength: number
newGuardianSet: string[]
}
// Parse a guardian set upgrade payload
const guardianSetUpgradeParser: P<GuardianSetUpgrade> = 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: 2,
formatter: (_action) => "GuardianSetUpgrade"
})
.uint16("chain")
.uint32("newGuardianSetIndex")
.uint8("newGuardianSetLength")
.array("newGuardianSet", {
type: addressParser(20),
length: "newGuardianSetLength",
formatter: (arr: [{ address: string }]) => arr.map((addr) => addr.address)
})
.string("end", {
greedy: true,
assert: str => str === ""
}))
function serialiseGuardianSetUpgrade(payload: GuardianSetUpgrade): string {
const body = [
encode("bytes32", Buffer.from(Buffer.from(payload.module).toString("hex").padStart(64, "0"), "hex")),
encode("uint8", 2),
encode("uint16", payload.chain),
encode("uint32", payload.newGuardianSetIndex),
encode("uint8", payload.newGuardianSet.length),
...payload.newGuardianSet
]
return body.join("")
}
////////////////////////////////////////////////////////////////////////////////
// Contract upgrades
export interface CoreContractUpgrade {
module: "Core"
type: "ContractUpgrade"
chain: number
address: Uint8Array
}
// Parse a core contract upgrade payload
const coreContractUpgradeParser: P<CoreContractUpgrade> =
new P(new Parser()
.endianess("big")
.string("module", {
length: 32,
encoding: "hex",
assert: Buffer.from("Core").toString("hex").padStart(64, "0"),
formatter: (_str) => module
})
.uint8("type", {
assert: 1,
formatter: (_action) => "ContractUpgrade"
})
.uint16("chain")
.array("address", {
type: "uint8",
lengthInBytes: 32,
// formatter: (arr) => Buffer.from(arr).toString("hex")
})
.string("end", {
greedy: true,
assert: str => str === ""
}))
function serialiseCoreContractUpgrade(payload: CoreContractUpgrade): string {
const body = [
encode("bytes32", encodeString(payload.module)),
encode("uint8", 1),
encode("uint16", payload.chain),
encode("bytes32", payload.address)
]
return body.join("")
}
export interface PortalContractUpgrade<Module extends "NFTBridge" | "TokenBridge"> {
module: Module
type: "ContractUpgrade"
chain: number
address: Uint8Array
}
// Parse a portal contract upgrade payload
function portalContractUpgradeParser<Module extends "NFTBridge" | "TokenBridge">(module: Module): P<PortalContractUpgrade<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: 2,
formatter: (_action: number) => "ContractUpgrade"
})
.uint16("chain")
.array("address", {
type: "uint8",
lengthInBytes: 32,
// formatter: (arr) => Buffer.from(arr).toString("hex")
})
.string("end", {
greedy: true,
assert: str => str === ""
}))
}
function serialisePortalContractUpgrade<Module extends "NFTBridge" | "TokenBridge">(payload: PortalContractUpgrade<Module>): string {
const body = [
encode("bytes32", encodeString(payload.module)),
encode("uint8", 2),
encode("uint16", payload.chain),
encode("bytes32", payload.address)
]
return body.join("")
}
////////////////////////////////////////////////////////////////////////////////
// Registrations
export interface PortalRegisterChain<Module extends "NFTBridge" | "TokenBridge"> {
module: Module
type: "RegisterChain"
chain: number
emitterChain: number
emitterAddress: Uint8Array
}
// Parse a portal chain registration payload
function portalRegisterChainParser<Module extends "NFTBridge" | "TokenBridge">(module: Module): P<PortalRegisterChain<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) => module
})
.uint8("type", {
assert: 1,
formatter: (_action) => "RegisterChain"
})
.uint16("chain")
.uint16("emitterChain")
.array("emitterAddress", {
type: "uint8",
lengthInBytes: 32,
// formatter: (arr) => Buffer.from(arr).toString("hex")
})
.string("end", {
greedy: true,
assert: str => str === ""
})
)
}
function serialisePortalRegisterChain<Module extends "NFTBridge" | "TokenBridge">(payload: PortalRegisterChain<Module>): string {
const body = [
encode("bytes32", encodeString(payload.module)),
encode("uint8", 1),
encode("uint16", payload.chain),
encode("uint16", payload.emitterChain),
encode("bytes32", payload.emitterAddress)
]
return body.join("")
}
// This function should be called after pattern matching on all possible options
// of an enum (union) type, so that typescript can derive that no other options
// are possible. If (from JavaScript land) an unsupported argument is passed
// 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 {
throw new Error(`Impossible: ${a}`)
}
////////////////////////////////////////////////////////////////////////////////
// Encoder utils
type Encoding
= "uint8"
| "uint16"
| "uint32"
| "uint64"
| "bytes32"
function typeWidth(type: Encoding): number {
switch (type) {
case "uint8": return 1
case "uint16": return 2
case "uint32": return 4
case "uint64": return 8
case "bytes32": return 32
}
}
// Couldn't find a satisfactory binary serialisation solution, so we just use
// the ethers library's encoding logic
function encode(type: Encoding, val: any): string {
// ethers operates on hex strings (sigh) and left pads everything to 32
// bytes (64 characters). We take last 2*n characters where n is the width
// of the type being serialised in bytes (since a byte is represented as 2
// digits in hex).
return ethers.utils.defaultAbiCoder.encode([type], [val]).substr(-2 * typeWidth(type))
}
// Encode a string as binary left-padded to 32 bytes, represented as a hex
// string (64 chars long)
function encodeString(str: string): Buffer {
return Buffer.from(Buffer.from(str).toString("hex").padStart(64, "0"), "hex")
}

View File

@ -0,0 +1,29 @@
#!/bin/bash
usage="Usage:
$(basename "$0") [sequence]
Fetch a governance VAA by sequence number, and print it as hex."
sequence=$1
if [ -z "$sequence" ]; then
echo "$usage"
exit 1
fi
mkdir -p "$TMPDIR/vaa"
cached="$TMPDIR/vaa/$sequence"
# We cache the result once it's ready, so we don't keep making requests if the
# VAA has already been pulled
if [ ! -f "$cached" ]; then
result=$(curl -s "https://wormhole-v2-mainnet-api.certus.one/v1/signed_vaa/1/0000000000000000000000000000000000000000000000000000000000000004/$sequence" | jq '.vaaBytes' -r)
# The 'vaaBytes' field is set once quorum has been reached. Otherwise, 'jq'
# returns "null", in which case we just exit 1
if [ "$result" == "null" ]; then
exit 1
fi
# vaaBytes is base64, we convert it to hex and write it to cache
echo "$result" | base64 -d | hexdump -v -e '/1 "%02x" ' > "$cached"
fi
cat "$cached"

View File

@ -1,29 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "main.ts generate-register-chain-vaa",
"type": "node",
"request": "launch",
"args": [
"${workspaceFolder}/main.ts",
"generate_register_chain_vaa",
"1",
"0x00000000000000000000000026b4afb60d6c903165150c6f0aa14f8016be4aec",
"--guardian_secret",
"cfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0,c3b2e45c422a1602333a64078aeb42637370b0f48fe385f9cfa6ad54a8e0c47e,9f790d3f08bc4b5cd910d4278f3deb406e57bb5e924906ccd52052bb078ccd47,b20cc49d6f2c82a5e6519015fc18aa3e562867f85f872c58f1277cfbd2a0c8e4",
],
"runtimeArgs": [
"--nolazy",
"-r",
"${workspaceRoot}/node_modules/ts-node/register"
],
"sourceMaps": true,
"cwd": "${workspaceRoot}",
"protocol": "inspector",
},
]
}

View File

@ -1,9 +0,0 @@
### usage
either build the typescript to javascript to run:
npm run build && node main.js solana execute_governance_vaa vaaHex...
or, run the typescript with ts-node:
npm run main -- solana execute_governance_vaa vaaHex...

View File

@ -1,376 +0,0 @@
import yargs from "yargs";
import { hideBin } from 'yargs/helpers'
import * as elliptic from "elliptic";
import * as ethers from "ethers";
import * as web3s from '@solana/web3.js';
import { PublicKey, TransactionInstruction, AccountMeta, Keypair, Connection } from "@solana/web3.js";
import { base58, solidityKeccak256 } from "ethers/lib/utils";
import { setDefaultWasm, importCoreWasm, importNftWasm, ixFromRust, BridgeImplementation__factory } from '@certusone/wormhole-sdk'
setDefaultWasm("node")
const signAndEncodeVM = function (
timestamp,
nonce,
emitterChainId,
emitterAddress,
sequence,
data,
signers,
guardianSetIndex,
consistencyLevel
) {
const body = [
ethers.utils.defaultAbiCoder.encode(["uint32"], [timestamp]).substring(2 + (64 - 8)),
ethers.utils.defaultAbiCoder.encode(["uint32"], [nonce]).substring(2 + (64 - 8)),
ethers.utils.defaultAbiCoder.encode(["uint16"], [emitterChainId]).substring(2 + (64 - 4)),
ethers.utils.defaultAbiCoder.encode(["bytes32"], [emitterAddress]).substring(2),
ethers.utils.defaultAbiCoder.encode(["uint64"], [sequence]).substring(2 + (64 - 16)),
ethers.utils.defaultAbiCoder.encode(["uint8"], [consistencyLevel]).substring(2 + (64 - 2)),
data.substr(2)
]
const hash = solidityKeccak256(["bytes"], [solidityKeccak256(["bytes"], ["0x" + body.join("")])])
let signatures = "";
for (let i in signers) {
const ec = new elliptic.ec("secp256k1");
const key = ec.keyFromPrivate(signers[i]);
const signature = key.sign(Buffer.from(hash.substr(2), "hex"), { canonical: true });
const packSig = [
ethers.utils.defaultAbiCoder.encode(["uint8"], [i]).substring(2 + (64 - 2)),
zeroPadBytes(signature.r.toString(16), 32),
zeroPadBytes(signature.s.toString(16), 32),
ethers.utils.defaultAbiCoder.encode(["uint8"], [signature.recoveryParam]).substr(2 + (64 - 2)),
]
signatures += packSig.join("")
}
const vm = [
ethers.utils.defaultAbiCoder.encode(["uint8"], [1]).substring(2 + (64 - 2)),
ethers.utils.defaultAbiCoder.encode(["uint32"], [guardianSetIndex]).substring(2 + (64 - 8)),
ethers.utils.defaultAbiCoder.encode(["uint8"], [signers.length]).substring(2 + (64 - 2)),
signatures,
body.join("")
].join("");
return vm
}
function zeroPadBytes(value, length) {
while (value.length < 2 * length) {
value = "0" + value;
}
return value;
}
yargs(hideBin(process.argv))
.command('generate_register_chain_vaa [chain_id] [contract_address]', 'create a VAA to register a chain (debug-only)', (yargs) => {
return yargs
.positional('chain_id', {
describe: 'chain id to register',
type: "number",
required: true
})
.positional('contract_address', {
describe: 'contract to register',
type: "string",
required: true
})
.option('guardian_secret', {
describe: 'Guardian\'s secret key, CSV allowed',
type: "string",
default: "cfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0"
})
}, async (argv) => {
let data = [
"0x",
"00000000000000000000000000000000000000000000004e4654427269646765", // NFT Bridge header
"01",
"0000",
ethers.utils.defaultAbiCoder.encode(["uint16"], [argv.chain_id]).substring(2 + (64 - 4)),
ethers.utils.defaultAbiCoder.encode(["bytes32"], [fmtAddress(String(argv.contract_address))]).substring(2),
].join('')
const vm = signAndEncodeVM(
1,
1,
1,
"0x0000000000000000000000000000000000000000000000000000000000000004",
Math.floor(Math.random() * 100000000),
data,
argv.guardian_secret.split(','),
0,
0
);
console.log(vm)
})
.command('generate_upgrade_chain_vaa [chain_id] [contract_address]', 'create a VAA to upgrade a chain (debug-only)', (yargs) => {
return yargs
.positional('chain_id', {
describe: 'chain id to upgrade',
type: "number",
required: true
})
.positional('contract_address', {
describe: 'contract to upgrade to',
type: "string",
required: true
})
.option('guardian_secret', {
describe: 'Guardian\'s secret key, CSV allowed',
type: "string",
default: "cfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0"
})
}, async (argv) => {
let data = [
"0x",
"00000000000000000000000000000000000000000000004e4654427269646765", // NFT Bridge header
"02",
ethers.utils.defaultAbiCoder.encode(["uint16"], [argv.chain_id]).substring(2 + (64 - 4)),
ethers.utils.defaultAbiCoder.encode(["bytes32"], [fmtAddress(String(argv.contract_address))]).substring(2),
].join('')
const vm = signAndEncodeVM(
1,
1,
1,
"0x0000000000000000000000000000000000000000000000000000000000000004",
Math.floor(Math.random() * 100000000),
data,
argv.guardian_secret.split(','),
0,
0
);
console.log(vm)
})
.command('solana execute_governance_vaa [vaa]', 'execute a governance VAA on Solana', (yargs) => {
return yargs
.positional('vaa', {
describe: 'vaa to post',
type: "string",
required: true
})
.option('rpc', {
alias: 'u',
type: 'string',
description: 'URL of the Solana RPC',
default: "http://localhost:8899"
})
.option('bridge', {
alias: 'b',
type: 'string',
description: 'Bridge address',
default: "Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o"
})
.option('nft_bridge', {
alias: 't',
type: 'string',
description: 'NFT Bridge address',
default: "NFTWqJR8YnRVqPDvTJrYuLrQDitTG5AScqbeghi4zSA"
})
.option('key', {
alias: 'k',
type: 'string',
description: 'Private key of the wallet',
required: false
})
}, async (argv) => {
const bridge = await importCoreWasm()
const nft_bridge = await importNftWasm()
let connection = setupConnection(argv);
let bridge_id = new PublicKey(argv.bridge);
let nft_bridge_id = new PublicKey(argv.nft_bridge);
var from: web3s.Keypair;
if (argv.key) {
from = web3s.Keypair.fromSecretKey(base58.decode(argv.key));
} else {
from = web3s.Keypair.generate();
let airdropSignature = await connection.requestAirdrop(
from.publicKey,
web3s.LAMPORTS_PER_SOL,
);
await connection.confirmTransaction(airdropSignature);
}
let vaa = Buffer.from(String(argv.vaa), "hex");
await post_vaa(connection, bridge_id, from, vaa);
let parsed_vaa = await bridge.parse_vaa(vaa);
let ix: TransactionInstruction;
switch (parsed_vaa.payload[32]) {
case 1:
console.log("Registering chain")
ix = nft_bridge.register_chain_ix(nft_bridge_id.toString(), bridge_id.toString(), from.publicKey.toString(), vaa);
break
case 2:
console.log("Upgrading contract")
ix = nft_bridge.upgrade_contract_ix(nft_bridge_id.toString(), bridge_id.toString(), from.publicKey.toString(), from.publicKey.toString(), vaa);
break
default:
throw new Error("unknown governance action")
}
let transaction = new web3s.Transaction().add(ixFromRust(ix));
// Sign transaction, broadcast, and confirm
let signature = await web3s.sendAndConfirmTransaction(
connection,
transaction,
[from],
{
skipPreflight: true
}
);
console.log('SIGNATURE', signature);
})
.command('eth execute_governance_vaa [vaa]', 'execute a governance VAA on evm', (yargs) => {
return yargs
.positional('vaa', {
describe: 'vaa to post',
type: "string",
required: true
})
.option('rpc', {
alias: 'u',
type: 'string',
description: 'URL of the ETH RPC',
default: "http://localhost:8545"
})
.option('nft_bridge', {
alias: 't',
type: 'string',
description: 'NFT Bridge address',
default: "0x26b4afb60d6c903165150c6f0aa14f8016be4aec"
})
.option('key', {
alias: 'k',
type: 'string',
description: 'Private key of the wallet',
default: "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d"
})
}, async (argv) => {
const bridge = await importCoreWasm()
let provider = new ethers.providers.JsonRpcProvider(argv.rpc)
let signer = new ethers.Wallet(argv.key, provider)
let t = new BridgeImplementation__factory(signer);
let tb = t.attach(argv.nft_bridge);
let vaa = Buffer.from(String(argv.vaa), "hex");
let parsed_vaa = await bridge.parse_vaa(vaa);
switch (parsed_vaa.payload[32]) {
case 1:
console.log("Registering chain")
console.log("Hash: " + (await tb.registerChain(vaa)).hash)
break
case 2:
console.log("Upgrading contract")
console.log("Hash: " + (await tb.upgrade(vaa)).hash)
break
default:
throw new Error("unknown governance action")
}
})
.argv;
async function post_vaa(connection: Connection, bridge_id: PublicKey, payer: Keypair, vaa: Buffer) {
const bridge = await importCoreWasm()
let bridge_state = await get_bridge_state(connection, bridge_id);
let guardian_addr = new PublicKey(bridge.guardian_set_address(bridge_id.toString(), bridge_state.guardian_set_index));
let acc = await connection.getAccountInfo(guardian_addr);
if (acc?.data === undefined) {
return
}
let guardian_data = bridge.parse_guardian_set(new Uint8Array(acc?.data));
let signature_set = Keypair.generate();
let txs = bridge.verify_signatures_ix(bridge_id.toString(), payer.publicKey.toString(), bridge_state.guardian_set_index, guardian_data, signature_set.publicKey.toString(), vaa)
// Add transfer instruction to transaction
for (let tx of txs) {
let ixs: Array<TransactionInstruction> = tx.map((v: any) => {
return ixFromRust(v)
})
let transaction = new web3s.Transaction().add(ixs[0], ixs[1]);
// Sign transaction, broadcast, and confirm
await web3s.sendAndConfirmTransaction(
connection,
transaction,
[payer, signature_set],
{
skipPreflight: true
}
);
}
let ix = ixFromRust(bridge.post_vaa_ix(bridge_id.toString(), payer.publicKey.toString(), signature_set.publicKey.toString(), vaa));
let transaction = new web3s.Transaction().add(ix);
// Sign transaction, broadcast, and confirm
let signature = await web3s.sendAndConfirmTransaction(
connection,
transaction,
[payer],
{
skipPreflight: true
}
);
console.log('SIGNATURE', signature);
}
async function get_bridge_state(connection: Connection, bridge_id: PublicKey): Promise<BridgeState> {
const bridge = await importCoreWasm()
let bridge_state = new PublicKey(bridge.state_address(bridge_id.toString()));
let acc = await connection.getAccountInfo(bridge_state);
if (acc?.data === undefined) {
throw new Error("bridge state not found")
}
return bridge.parse_state(new Uint8Array(acc?.data));
}
function setupConnection(argv: yargs.Arguments): web3s.Connection {
return new web3s.Connection(
argv.rpc as string,
'confirmed',
);
}
function fmtAddress(addr: string): string {
let address = (addr.search("0x") == 0) ? addr.substring(2) : addr;
return "0x" + zeroPadBytes(address, 32);
}
interface BridgeState {
// The current guardian set index, used to decide which signature sets to accept.
guardian_set_index: number,
// Lamports in the collection account
last_lamports: number,
// Bridge configuration, which is set once upon initialization.
config: BridgeConfig,
}
interface BridgeConfig {
// Period for how long a guardian set is valid after it has been replaced by a new one. This
// guarantees that VAAs issued by that set can still be submitted for a certain period. In
// this period we still trust the old guardian set.
guardian_set_expiration_time: number,
// Amount of lamports that needs to be paid to the protocol to post a message
fee: number,
}

File diff suppressed because it is too large Load Diff

View File

@ -1,31 +0,0 @@
{
"name": "wormhole-client-solana",
"version": "1.0.0",
"dependencies": {
"@certusone/wormhole-sdk": "^0.2.0",
"@solana/web3.js": "^1.22.0",
"bn.js": "^5.2.0",
"bs58": "^4.0.1",
"buffer-layout": "^1.2.2",
"ethers": "^5.4.1",
"js-base64": "^3.6.1",
"npm": "^7.20.0",
"web3": "^1.5.0",
"yargs": "^17.0.1"
},
"scripts": {
"start": "tsc && node main.js",
"build": "tsc",
"test": "echo \"Error: no test specified\" && exit 1",
"main": "ts-node main.ts"
},
"devDependencies": {
"@truffle/hdwallet-provider": "^1.4.1",
"@types/bn.js": "^5.1.0",
"@types/bs58": "^4.0.1",
"@types/yargs": "^17.0.2",
"copy-dir": "^1.3.0",
"ts-node": "^10.7.0",
"typescript": "^4.3.5"
}
}

View File

@ -1,72 +0,0 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
// "outDir": "./", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
"noImplicitAny": false, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */
/* Module Resolution Options */
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
/* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
/* Advanced Options */
"skipLibCheck": true, /* Skip type checking of declaration files. */
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
}
}

View File

@ -1,260 +0,0 @@
import yargs from "yargs";
import { hideBin } from 'yargs/helpers';
import * as web3 from '@solana/web3.js';
import { PublicKey, TransactionInstruction, Keypair, Connection } from "@solana/web3.js";
import { base58 } from "ethers/lib/utils";
import { setDefaultWasm, importCoreWasm, ixFromRust } from '@certusone/wormhole-sdk'
setDefaultWasm("node")
yargs(hideBin(process.argv))
.command('post_message [nonce] [message] [consistency]', 'post a message', (yargs) => {
return yargs
.positional('nonce', {
describe: 'nonce of the message',
type: "number",
required: true
})
.positional('message', {
describe: 'message to post',
type: "string",
required: true
})
.positional('consistency', {
describe: 'confirmation level that this message requires <CONFIRMED|FINALIZED>',
type: "string",
required: true
})
}, async (argv: any) => {
const bridge = await importCoreWasm()
let connection = setupConnection(argv);
let bridge_id = new PublicKey(argv.bridge);
// Generate a new random public key
let from = web3.Keypair.generate();
let emitter = web3.Keypair.generate();
let message = web3.Keypair.generate();
let airdropSignature = await connection.requestAirdrop(
from.publicKey,
web3.LAMPORTS_PER_SOL,
);
await connection.confirmTransaction(airdropSignature);
let fee_acc = bridge.fee_collector_address(bridge_id.toString());
let bridge_state = await get_bridge_state(connection, bridge_id);
let transferIx = web3.SystemProgram.transfer({
fromPubkey: from.publicKey,
toPubkey: new PublicKey(fee_acc),
lamports: bridge_state.config.fee,
});
if (argv.consistency !== "CONFIRMED" && argv.consistency !== "FINALIZED") {
throw new Error("invalid consistency level")
}
let ix = ixFromRust(bridge.post_message_ix(bridge_id.toString(), from.publicKey.toString(), emitter.publicKey.toString(), message.publicKey.toString(), argv.nonce, Buffer.from(argv.message, "hex"), argv.consistency));
// Add transfer instruction to transaction
let transaction = new web3.Transaction().add(transferIx, ix);
// Sign transaction, broadcast, and confirm
let signature = await web3.sendAndConfirmTransaction(
connection,
transaction,
[from, emitter, message],
{
skipPreflight: true
}
);
console.log('SIGNATURE', signature);
})
.command('post_vaa [vaa]', 'post a VAA on Solana', (yargs) => {
return yargs
.positional('vaa', {
describe: 'vaa to post',
type: "string",
required: true
})
}, async (argv: any) => {
let connection = setupConnection(argv);
let bridge_id = new PublicKey(argv.bridge);
// Generate a new random public key
let from = web3.Keypair.generate();
let airdropSignature = await connection.requestAirdrop(
from.publicKey,
web3.LAMPORTS_PER_SOL,
);
await connection.confirmTransaction(airdropSignature);
let vaa = Buffer.from(argv.vaa, "hex");
await post_vaa(connection, bridge_id, from, vaa);
})
.command('execute_governance_vaa [vaa]', 'execute a governance VAA on Solana', (yargs) => {
return yargs
.positional('vaa', {
describe: 'vaa to post',
type: "string",
required: true
})
}, async (argv: any) => {
const bridge = await importCoreWasm()
let connection = setupConnection(argv);
let bridge_id = new PublicKey(argv.bridge);
var from: web3.Keypair;
if (argv.key) {
from = web3.Keypair.fromSecretKey(base58.decode(argv.key));
} else {
from = web3.Keypair.generate();
let airdropSignature = await connection.requestAirdrop(
from.publicKey,
web3.LAMPORTS_PER_SOL,
);
await connection.confirmTransaction(airdropSignature);
}
let vaa = Buffer.from(argv.vaa, "hex");
await post_vaa(connection, bridge_id, from, vaa);
let parsed_vaa = await bridge.parse_vaa(vaa);
let ix: TransactionInstruction;
switch (parsed_vaa.payload[32]) {
case 1:
console.log("Upgrading contract")
ix = bridge.upgrade_contract_ix(bridge_id.toString(), from.publicKey.toString(), from.publicKey.toString(), vaa);
break
case 2:
console.log("Updating guardian set")
ix = bridge.update_guardian_set_ix(bridge_id.toString(), from.publicKey.toString(), vaa);
break
case 3:
console.log("Setting fees")
ix = bridge.set_fees_ix(bridge_id.toString(), from.publicKey.toString(), vaa);
break
case 4:
console.log("Transferring fees")
ix = bridge.transfer_fees_ix(bridge_id.toString(), from.publicKey.toString(), vaa);
break
default:
throw new Error("unknown governance action")
}
let transaction = new web3.Transaction().add(ixFromRust(ix));
// Sign transaction, broadcast, and confirm
let signature = await web3.sendAndConfirmTransaction(
connection,
transaction,
[from],
{
skipPreflight: true
}
);
console.log('SIGNATURE', signature);
})
.option('rpc', {
alias: 'u',
type: 'string',
description: 'URL of the Solana RPC',
default: "http://localhost:8899"
})
.option('bridge', {
alias: 'b',
type: 'string',
description: 'Bridge address',
default: "Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o"
})
.option('key', {
alias: 'k',
type: 'string',
description: 'Private key of the wallet',
required: false
})
.argv;
async function post_vaa(connection: Connection, bridge_id: PublicKey, payer: Keypair, vaa: Buffer) {
const bridge = await importCoreWasm()
let bridge_state = await get_bridge_state(connection, bridge_id);
let guardian_addr = new PublicKey(bridge.guardian_set_address(bridge_id.toString(), bridge_state.guardian_set_index));
let acc = await connection.getAccountInfo(guardian_addr);
if (acc?.data === undefined) {
return
}
let guardian_data = bridge.parse_guardian_set(new Uint8Array(acc?.data));
let signature_set = Keypair.generate();
let txs = bridge.verify_signatures_ix(bridge_id.toString(), payer.publicKey.toString(), bridge_state.guardian_set_index, guardian_data, signature_set.publicKey.toString(), vaa)
// Add transfer instruction to transaction
for (let tx of txs) {
let ixs: Array<TransactionInstruction> = tx.map((v: any) => {
return ixFromRust(v)
})
let transaction = new web3.Transaction().add(ixs[0], ixs[1]);
// Sign transaction, broadcast, and confirm
await web3.sendAndConfirmTransaction(
connection,
transaction,
[payer, signature_set],
{
skipPreflight: true
}
);
}
let ix = ixFromRust(bridge.post_vaa_ix(bridge_id.toString(), payer.publicKey.toString(), signature_set.publicKey.toString(), vaa));
let transaction = new web3.Transaction().add(ix);
// Sign transaction, broadcast, and confirm
let signature = await web3.sendAndConfirmTransaction(
connection,
transaction,
[payer],
{
skipPreflight: true
}
);
console.log('SIGNATURE', signature);
}
async function get_bridge_state(connection: Connection, bridge_id: PublicKey): Promise<BridgeState> {
const bridge = await importCoreWasm()
let bridge_state = new PublicKey(bridge.state_address(bridge_id.toString()));
let acc = await connection.getAccountInfo(bridge_state);
if (acc?.data === undefined) {
throw new Error("bridge state not found")
}
return bridge.parse_state(new Uint8Array(acc?.data));
}
function setupConnection(argv: yargs.Arguments): web3.Connection {
return new web3.Connection(
argv.rpc as string,
'confirmed',
);
}
interface BridgeState {
// The current guardian set index, used to decide which signature sets to accept.
guardian_set_index: number,
// Lamports in the collection account
last_lamports: number,
// Bridge configuration, which is set once upon initialization.
config: BridgeConfig,
}
interface BridgeConfig {
// Period for how long a guardian set is valid after it has been replaced by a new one. This
// guarantees that VAAs issued by that set can still be submitted for a certain period. In
// this period we still trust the old guardian set.
guardian_set_expiration_time: number,
// Amount of lamports that needs to be paid to the protocol to post a message
fee: number,
}

File diff suppressed because it is too large Load Diff

View File

@ -1,25 +0,0 @@
{
"name": "wormhole-client-solana",
"version": "1.0.0",
"dependencies": {
"@certusone/wormhole-sdk": "^0.2.0",
"@solana/web3.js": "^1.22.0",
"bn.js": "^5.2.0",
"bs58": "^4.0.1",
"buffer-layout": "^1.2.2",
"ethers": "^5.4.1",
"npm": "^7.20.0",
"yargs": "^17.0.1"
},
"scripts": {
"start": "tsc && node main.js",
"build": "tsc",
"test": "echo \"Error: no test specified\" && exit 1"
},
"devDependencies": {
"@types/bn.js": "^5.1.0",
"@types/bs58": "^4.0.1",
"@types/yargs": "^17.0.2",
"typescript": "^4.3.5"
}
}

View File

@ -1,115 +0,0 @@
import argparse
from terra_sdk.client.lcd import AsyncLCDClient
import asyncio
from terra_sdk.core.wasm import (
MsgExecuteContract,
)
from terra_sdk.key.mnemonic import MnemonicKey
import base64
def add_default_args(parser):
parser.add_argument('--rpc', required=True, help='Terra lcd address')
parser.add_argument('--chain-id', dest="chain_id", required=True, help='Chain ID')
parser.add_argument('--mnemonic', dest="mnemonic", required=True, help='Mnemonic of the wallet to be used')
parser.add_argument('--contract', dest="contract", required=True, help='Address of the Wormhole contract')
parser = argparse.ArgumentParser(prog='terra_cli')
subparsers = parser.add_subparsers(help='sub-command help', dest="command")
gov_parser = subparsers.add_parser('execute_governance', help='Execute a governance VAA')
add_default_args(gov_parser)
gov_parser.add_argument('vaa', help='Hex encoded VAA')
post_parser = subparsers.add_parser('post_message', help='Publish a message over the wormhole')
add_default_args(post_parser)
post_parser.add_argument('nonce', help='Nonce of the message', type=int)
post_parser.add_argument('message', help='Hex-encoded message')
gas_prices = {
"uluna": "0.15",
"usdr": "0.1018",
"uusd": "0.15",
"ukrw": "178.05",
"umnt": "431.6259",
"ueur": "0.125",
"ucny": "0.97",
"ujpy": "16",
"ugbp": "0.11",
"uinr": "11",
"ucad": "0.19",
"uchf": "0.13",
"uaud": "0.19",
"usgd": "0.2",
}
async def sign_and_broadcast(sequence, deployer, terra, *msgs):
tx = await deployer.create_and_sign_tx(
msgs=msgs, fee_denoms=["ukrw", "uusd", "uluna"], sequence=sequence, gas_adjustment=1.4,
)
result = await terra.tx.broadcast(tx)
sequence += 1
if result.is_tx_error():
raise RuntimeError(result.raw_log)
return result
class ContractQuerier:
def __init__(self, address, terra):
self.address = address
self.terra = terra
def __getattr__(self, item):
async def result_fxn(**kwargs):
return await self.terra.wasm.contract_query(self.address, {item: kwargs})
return result_fxn
class Contract:
def __init__(self, address, deployer, terra, sequence):
self.address = address
self.deployer = deployer
self.terra = terra
self.sequence = sequence
def __getattr__(self, item):
async def result_fxn(coins=None, **kwargs):
execute = MsgExecuteContract(
self.deployer.key.acc_address, self.address, {item: kwargs}, coins=coins
)
return await sign_and_broadcast(self.sequence, self.deployer, self.terra, execute)
return result_fxn
@property
def query(self):
return ContractQuerier(self.address, self.terra)
async def main():
args = parser.parse_args()
async with AsyncLCDClient(
args.rpc, args.chain_id, gas_prices=gas_prices, loop=asyncio.get_event_loop()
) as terra:
deployer = terra.wallet(MnemonicKey(
mnemonic=args.mnemonic))
sequence = await deployer.sequence()
wormhole = Contract(args.contract, deployer, terra, sequence)
res = dict()
if args.command == "execute_governance":
res = await wormhole.submit_v_a_a(vaa=base64.b64encode(bytes.fromhex(args.vaa)).decode("utf-8"))
elif args.command == "post_message":
state = await wormhole.query.get_state()
fee = state["fee"]
res = await wormhole.post_message(nonce=args.nonce,
message=base64.b64encode(bytes.fromhex(args.message)).decode("utf-8"),
coins={fee["denom"]: fee["amount"]})
print(res.logs[0].events_by_type["from_contract"])
if __name__ == "__main__":
asyncio.get_event_loop().run_until_complete(main())

View File

@ -1 +0,0 @@
terra_sdk==0.14.0

View File

@ -1,9 +0,0 @@
### usage
either build the typescript to javascript to run:
npm run build && node main.js solana execute_governance_vaa vaaHex...
or, run the typescript with ts-node:
npm run main -- solana execute_governance_vaa vaaHex...

View File

@ -1,450 +0,0 @@
import yargs from "yargs";
import { hideBin } from 'yargs/helpers';
import * as elliptic from "elliptic";
import * as ethers from "ethers";
import * as web3s from '@solana/web3.js';
import { fromUint8Array } from "js-base64";
import { LCDClient, MnemonicKey } from '@terra-money/terra.js';
import { MsgExecuteContract } from "@terra-money/terra.js";
import { PublicKey, TransactionInstruction, Keypair, Connection } from "@solana/web3.js";
import { base58, solidityKeccak256 } from "ethers/lib/utils";
import { setDefaultWasm, importCoreWasm, importTokenWasm, ixFromRust, BridgeImplementation__factory } from '@certusone/wormhole-sdk'
setDefaultWasm("node")
const signAndEncodeVM = function (
timestamp,
nonce,
emitterChainId,
emitterAddress,
sequence,
data,
signers,
guardianSetIndex,
consistencyLevel
) {
const body = [
ethers.utils.defaultAbiCoder.encode(["uint32"], [timestamp]).substring(2 + (64 - 8)),
ethers.utils.defaultAbiCoder.encode(["uint32"], [nonce]).substring(2 + (64 - 8)),
ethers.utils.defaultAbiCoder.encode(["uint16"], [emitterChainId]).substring(2 + (64 - 4)),
ethers.utils.defaultAbiCoder.encode(["bytes32"], [emitterAddress]).substring(2),
ethers.utils.defaultAbiCoder.encode(["uint64"], [sequence]).substring(2 + (64 - 16)),
ethers.utils.defaultAbiCoder.encode(["uint8"], [consistencyLevel]).substring(2 + (64 - 2)),
data.substr(2)
]
const hash = solidityKeccak256(["bytes"], [solidityKeccak256(["bytes"], ["0x" + body.join("")])])
let signatures = "";
for (let i in signers) {
const ec = new elliptic.ec("secp256k1");
const key = ec.keyFromPrivate(signers[i]);
const signature = key.sign(Buffer.from(hash.substr(2), "hex"), { canonical: true });
const packSig = [
ethers.utils.defaultAbiCoder.encode(["uint8"], [i]).substring(2 + (64 - 2)),
zeroPadBytes(signature.r.toString(16), 32),
zeroPadBytes(signature.s.toString(16), 32),
ethers.utils.defaultAbiCoder.encode(["uint8"], [signature.recoveryParam]).substr(2 + (64 - 2)),
]
signatures += packSig.join("")
}
const vm = [
ethers.utils.defaultAbiCoder.encode(["uint8"], [1]).substring(2 + (64 - 2)),
ethers.utils.defaultAbiCoder.encode(["uint32"], [guardianSetIndex]).substring(2 + (64 - 8)),
ethers.utils.defaultAbiCoder.encode(["uint8"], [signers.length]).substring(2 + (64 - 2)),
signatures,
body.join("")
].join("");
return vm
}
function zeroPadBytes(value, length) {
while (value.length < 2 * length) {
value = "0" + value;
}
return value;
}
yargs(hideBin(process.argv))
.command('generate_register_chain_vaa [chain_id] [contract_address]', 'create a VAA to register a chain (debug-only)', (yargs) => {
return yargs
.positional('chain_id', {
describe: 'chain id to register',
type: "number",
required: true
})
.positional('contract_address', {
describe: 'contract to register',
type: "string",
required: true
})
.option('guardian_secret', {
describe: 'Guardian\'s secret key, CSV allowed',
type: "string",
default: "cfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0"
})
}, async (argv) => {
let data = [
"0x",
"000000000000000000000000000000000000000000546f6b656e427269646765", // Token Bridge header
"01",
"0000",
ethers.utils.defaultAbiCoder.encode(["uint16"], [argv.chain_id]).substring(2 + (64 - 4)),
ethers.utils.defaultAbiCoder.encode(["bytes32"], [fmtAddress(String(argv.contract_address))]).substring(2),
].join('')
const vm = signAndEncodeVM(
1,
1,
1,
"0x0000000000000000000000000000000000000000000000000000000000000004",
Math.floor(Math.random() * 100000000),
data,
argv.guardian_secret.split(','),
0,
0
);
console.log(vm)
})
.command('generate_upgrade_chain_vaa [chain_id] [contract_address]', 'create a VAA to upgrade a chain (debug-only)', (yargs) => {
return yargs
.positional('chain_id', {
describe: 'chain id to upgrade',
type: "number",
required: true
})
.positional('contract_address', {
describe: 'contract to upgrade to',
type: "string",
required: true
})
.option('guardian_secret', {
describe: 'Guardian\'s secret key, CSV allowed',
type: "string",
default: "cfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0"
})
}, async (argv) => {
let data = [
"0x",
"000000000000000000000000000000000000000000546f6b656e427269646765", // Token Bridge header
"02",
ethers.utils.defaultAbiCoder.encode(["uint16"], [argv.chain_id]).substring(2 + (64 - 4)),
ethers.utils.defaultAbiCoder.encode(["bytes32"], [fmtAddress(String(argv.contract_address))]).substring(2),
].join('')
const vm = signAndEncodeVM(
1,
1,
1,
"0x0000000000000000000000000000000000000000000000000000000000000004",
Math.floor(Math.random() * 100000000),
data,
argv.guardian_secret.split(','),
0,
0
);
console.log(vm)
})
.command('terra execute_governance_vaa [vaa]', 'execute a governance VAA on Terra', (yargs) => {
return yargs
.positional('vaa', {
describe: 'vaa to post',
type: "string",
required: true
})
.option('rpc', {
alias: 'u',
type: 'string',
description: 'URL of the Terra RPC',
default: "http://localhost:1317"
})
.option('token_bridge', {
alias: 't',
type: 'string',
description: 'Token Bridge address',
default: "terra10pyejy66429refv3g35g2t7am0was7ya7kz2a4"
})
.option('chain_id', {
alias: 'c',
type: 'string',
description: 'Chain ID',
// Should be localterra in theory, however Terra Station will
// assume columbus-4 when localterra is set, while our current
// dev environment is based on columbus-4. Should change when
// change ID within terra/devnet/config/genesis.json is also
// changed.
default: 'columbus-4'
})
.option('mnemonic', {
alias: 'm',
type: 'string',
description: 'Wallet Mnemonic',
default: 'notice oak worry limit wrap speak medal online prefer cluster roof addict wrist behave treat actual wasp year salad speed social layer crew genius',
})
}, async (argv) => {
const terra = new LCDClient({
URL: argv.rpc,
chainID: argv.chain_id,
});
const wallet = terra.wallet(new MnemonicKey({
mnemonic: argv.mnemonic
}));
// create a simple message that moves coin balances
const vaa = Buffer.from(String(argv.vaa), "hex");
const transaction = new MsgExecuteContract(
wallet.key.accAddress,
argv.token_bridge,
{
submit_vaa: {
data: fromUint8Array(vaa)
},
},
{ uluna: 1000 }
);
wallet
.createAndSignTx({
msgs: [transaction],
memo: '',
})
.then(tx => terra.tx.broadcast(tx))
.then(result => {
console.log(result);
console.log(`TX hash: ${result.txhash}`);
});
})
.command('solana execute_governance_vaa [vaa]', 'execute a governance VAA on Solana', (yargs) => {
return yargs
.positional('vaa', {
describe: 'vaa to post',
type: "string",
required: true
})
.option('rpc', {
alias: 'u',
type: 'string',
description: 'URL of the Solana RPC',
default: "http://localhost:8899"
})
.option('bridge', {
alias: 'b',
type: 'string',
description: 'Bridge address',
default: "Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o"
})
.option('token_bridge', {
alias: 't',
type: 'string',
description: 'Token Bridge address',
default: "B6RHG3mfcckmrYN1UhmJzyS1XX3fZKbkeUcpJe9Sy3FE"
})
.option('key', {
alias: 'k',
type: 'string',
description: 'Private key of the wallet',
required: false
})
}, async (argv) => {
const bridge = await importCoreWasm()
const token_bridge = await importTokenWasm()
let connection = setupConnection(argv);
let bridge_id = new PublicKey(argv.bridge);
let token_bridge_id = new PublicKey(argv.token_bridge);
var from: web3s.Keypair;
if (argv.key) {
from = web3s.Keypair.fromSecretKey(base58.decode(argv.key));
} else {
from = web3s.Keypair.generate();
let airdropSignature = await connection.requestAirdrop(
from.publicKey,
web3s.LAMPORTS_PER_SOL,
);
await connection.confirmTransaction(airdropSignature);
}
let vaa = Buffer.from(String(argv.vaa), "hex");
await post_vaa(connection, bridge_id, from, vaa);
let parsed_vaa = await bridge.parse_vaa(vaa);
let ix: TransactionInstruction;
switch (parsed_vaa.payload[32]) {
case 1:
console.log("Registering chain")
ix = token_bridge.register_chain_ix(token_bridge_id.toString(), bridge_id.toString(), from.publicKey.toString(), vaa);
break
case 2:
console.log("Upgrading contract")
ix = token_bridge.upgrade_contract_ix(token_bridge_id.toString(), bridge_id.toString(), from.publicKey.toString(), from.publicKey.toString(), vaa);
break
default:
throw new Error("unknown governance action")
}
let transaction = new web3s.Transaction().add(ixFromRust(ix));
// Sign transaction, broadcast, and confirm
let signature = await web3s.sendAndConfirmTransaction(
connection,
transaction,
[from],
{
skipPreflight: true
}
);
console.log('SIGNATURE', signature);
})
.command('eth execute_governance_vaa [vaa]', 'execute a governance VAA on evm', (yargs) => {
return yargs
.positional('vaa', {
describe: 'vaa to post',
type: "string",
required: true
})
.option('rpc', {
alias: 'u',
type: 'string',
description: 'URL of the ETH RPC',
default: "http://localhost:8545"
})
.option('token_bridge', {
alias: 't',
type: 'string',
description: 'Token Bridge address',
default: "0x0290FB167208Af455bB137780163b7B7a9a10C16"
})
.option('key', {
alias: 'k',
type: 'string',
description: 'Private key of the wallet',
default: "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d"
})
}, async (argv) => {
const bridge = await importCoreWasm()
let provider = new ethers.providers.JsonRpcProvider(argv.rpc)
let signer = new ethers.Wallet(argv.key, provider)
let t = new BridgeImplementation__factory(signer);
let tb = t.attach(argv.token_bridge);
let vaa = Buffer.from(String(argv.vaa), "hex");
let parsed_vaa = await bridge.parse_vaa(vaa);
switch (parsed_vaa.payload[32]) {
case 1:
console.log("Registering chain")
console.log("Hash: " + (await tb.registerChain(vaa)).hash)
break
case 2:
console.log("Upgrading contract")
console.log("Hash: " + (await tb.upgrade(vaa)).hash)
console.log("Don't forget to verify the new implementation! See ethereum/VERIFY.md for instructions")
break
default:
throw new Error("unknown governance action")
}
})
.argv;
async function post_vaa(connection: Connection, bridge_id: PublicKey, payer: Keypair, vaa: Buffer) {
const bridge = await importCoreWasm()
let bridge_state = await get_bridge_state(connection, bridge_id);
let guardian_addr = new PublicKey(bridge.guardian_set_address(bridge_id.toString(), bridge_state.guardian_set_index));
let acc = await connection.getAccountInfo(guardian_addr);
if (acc?.data === undefined) {
return
}
let guardian_data = bridge.parse_guardian_set(new Uint8Array(acc?.data));
let signature_set = Keypair.generate();
let txs = bridge.verify_signatures_ix(bridge_id.toString(), payer.publicKey.toString(), bridge_state.guardian_set_index, guardian_data, signature_set.publicKey.toString(), vaa)
// Add transfer instruction to transaction
for (let tx of txs) {
let ixs: Array<TransactionInstruction> = tx.map((v: any) => {
return ixFromRust(v)
})
let transaction = new web3s.Transaction().add(ixs[0], ixs[1]);
// Sign transaction, broadcast, and confirm
await web3s.sendAndConfirmTransaction(
connection,
transaction,
[payer, signature_set],
{
skipPreflight: true
}
);
}
let ix = ixFromRust(bridge.post_vaa_ix(bridge_id.toString(), payer.publicKey.toString(), signature_set.publicKey.toString(), vaa));
let transaction = new web3s.Transaction().add(ix);
// Sign transaction, broadcast, and confirm
let signature = await web3s.sendAndConfirmTransaction(
connection,
transaction,
[payer],
{
skipPreflight: true
}
);
console.log('SIGNATURE', signature);
}
async function get_bridge_state(connection: Connection, bridge_id: PublicKey): Promise<BridgeState> {
const bridge = await importCoreWasm()
let bridge_state = new PublicKey(bridge.state_address(bridge_id.toString()));
let acc = await connection.getAccountInfo(bridge_state);
if (acc?.data === undefined) {
throw new Error("bridge state not found")
}
return bridge.parse_state(new Uint8Array(acc?.data));
}
function setupConnection(argv: yargs.Arguments): web3s.Connection {
return new web3s.Connection(
argv.rpc as string,
'confirmed',
);
}
function fmtAddress(addr: string): string {
let address = (addr.search("0x") == 0) ? addr.substring(2) : addr;
return "0x" + zeroPadBytes(address, 32);
}
interface BridgeState {
// The current guardian set index, used to decide which signature sets to accept.
guardian_set_index: number,
// Lamports in the collection account
last_lamports: number,
// Bridge configuration, which is set once upon initialization.
config: BridgeConfig,
}
interface BridgeConfig {
// Period for how long a guardian set is valid after it has been replaced by a new one. This
// guarantees that VAAs issued by that set can still be submitted for a certain period. In
// this period we still trust the old guardian set.
guardian_set_expiration_time: number,
// Amount of lamports that needs to be paid to the protocol to post a message
fee: number,
}

View File

@ -1,72 +0,0 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig.json to read more about this file */
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
// "lib": [], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
// "outDir": "./", /* Redirect output structure to the directory. */
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
// "composite": true, /* Enable project compilation */
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
// "removeComments": true, /* Do not emit comments to output. */
// "noEmit": true, /* Do not emit outputs. */
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
"noImplicitAny": false, /* Raise error on expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */
/* Module Resolution Options */
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
// "typeRoots": [], /* List of folders to include type definitions from. */
// "types": [], /* Type declaration files to be included in compilation. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
/* Source Map Options */
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
/* Advanced Options */
"skipLibCheck": true, /* Skip type checking of declaration files. */
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
}
}

View File

@ -85,28 +85,23 @@ terraNFTBridge=$(jq --raw-output '.chains."3".contracts.nftBridgeEmitterAddress'
# 4) create token bridge registration VAAs
echo "generating contract registration VAAs for token bridges"
# fetch dependencies for the clients/token_bridge script that generates token bridge registration VAAs
if [[ ! -d ./clients/token_bridge/node_modules ]]; then
echo "going to install node modules in clients/token_bridge"
npm ci --prefix clients/token_bridge && npm run build --prefix clients/token_bridge
if [[ ! -d ./clients/js/node_modules ]]; then
echo "going to install node modules in clients/js"
npm ci --prefix clients/js
fi
# invoke clients/token_bridge commands to create registration VAAs
solTokenBridgeVAA=$(npm --prefix clients/token_bridge run --silent main -- generate_register_chain_vaa 1 0x${solTokenBridge} --guardian_secret ${guardiansPrivateCSV})
ethTokenBridgeVAA=$(npm --prefix clients/token_bridge run --silent main -- generate_register_chain_vaa 2 0x${ethTokenBridge} --guardian_secret ${guardiansPrivateCSV} )
terraTokenBridgeVAA=$(npm --prefix clients/token_bridge run --silent main -- generate_register_chain_vaa 3 0x${terraTokenBridge} --guardian_secret ${guardiansPrivateCSV})
bscTokenBridgeVAA=$(npm --prefix clients/token_bridge run --silent main -- generate_register_chain_vaa 4 0x${bscTokenBridge} --guardian_secret ${guardiansPrivateCSV})
algoTokenBridgeVAA=$(npm --prefix clients/token_bridge run --silent main -- generate_register_chain_vaa 8 0x${algoTokenBridge} --guardian_secret ${guardiansPrivateCSV})
solTokenBridgeVAA=$(npm --prefix clients/js start --silent -- generate registration -m TokenBridge -c solana -a ${solTokenBridge} -g ${guardiansPrivateCSV})
ethTokenBridgeVAA=$(npm --prefix clients/js start --silent -- generate registration -m TokenBridge -c ethereum -a ${ethTokenBridge} -g ${guardiansPrivateCSV} )
terraTokenBridgeVAA=$(npm --prefix clients/js start --silent -- generate registration -m TokenBridge -c terra -a ${terraTokenBridge} -g ${guardiansPrivateCSV})
bscTokenBridgeVAA=$(npm --prefix clients/js start --silent -- generate registration -m TokenBridge -c bsc -a ${bscTokenBridge} -g ${guardiansPrivateCSV})
algoTokenBridgeVAA=$(npm --prefix clients/js start --silent -- generate registration -m TokenBridge -c algorand -a ${algoTokenBridge} -g ${guardiansPrivateCSV})
# 5) create nft bridge registration VAAs
# fetch dependencies for the clients/nft_bridge script that generates nft bridge registration VAAs
if [[ ! -d ./clients/nft_bridge/node_modules ]]; then
echo "going to install node modules in clients/nft_bridge"
npm ci --prefix clients/nft_bridge && npm run build --prefix clients/nft_bridge
fi
echo "generating contract registration VAAs for nft bridges"
solNFTBridgeVAA=$(npm --prefix clients/nft_bridge run --silent main -- generate_register_chain_vaa 1 0x${solNFTBridge} --guardian_secret ${guardiansPrivateCSV})
ethNFTBridgeVAA=$(npm --prefix clients/nft_bridge run --silent main -- generate_register_chain_vaa 2 0x${ethNFTBridge} --guardian_secret ${guardiansPrivateCSV})
terraNFTBridgeVAA=$(npm --prefix clients/nft_bridge run --silent main -- generate_register_chain_vaa 3 0x${terraNFTBridge} --guardian_secret ${guardiansPrivateCSV})
solNFTBridgeVAA=$(npm --prefix clients/js start --silent -- generate registration -m NFTBridge -c solana -a ${solNFTBridge} -g ${guardiansPrivateCSV})
ethNFTBridgeVAA=$(npm --prefix clients/js start --silent -- generate registration -m NFTBridge -c ethereum -a ${ethNFTBridge} -g ${guardiansPrivateCSV})
terraNFTBridgeVAA=$(npm --prefix clients/js start --silent -- generate registration -m NFTBridge -c terra -a ${terraNFTBridge} -g ${guardiansPrivateCSV})

View File

@ -93,18 +93,15 @@ retry token-bridge-client create-bridge "$token_bridge_address" "$bridge_address
retry token-bridge-client create-bridge "$nft_bridge_address" "$bridge_address"
# pass the chain registration VAAs sourced from .env to the client's execute-governance command:
pushd /usr/src/clients/token_bridge
pushd /usr/src/clients/js
# Register the Token Bridge Endpoint on ETH
node main.js solana execute_governance_vaa "$REGISTER_ETH_TOKEN_BRIDGE_VAA"
node main.js solana execute_governance_vaa "$REGISTER_TERRA_TOKEN_BRIDGE_VAA"
node main.js solana execute_governance_vaa "$REGISTER_BSC_TOKEN_BRIDGE_VAA"
node main.js solana execute_governance_vaa "$REGISTER_ALGO_TOKEN_BRIDGE_VAA"
popd
pushd /usr/src/clients/nft_bridge
npm start -- submit -c solana -n devnet "$REGISTER_ETH_TOKEN_BRIDGE_VAA"
npm start -- submit -c solana -n devnet "$REGISTER_TERRA_TOKEN_BRIDGE_VAA"
npm start -- submit -c solana -n devnet "$REGISTER_BSC_TOKEN_BRIDGE_VAA"
npm start -- submit -c solana -n devnet "$REGISTER_ALGO_TOKEN_BRIDGE_VAA"
# Register the NFT Bridge Endpoint on ETH
node main.js solana execute_governance_vaa "$REGISTER_ETH_NFT_BRIDGE_VAA"
node main.js solana execute_governance_vaa "$REGISTER_TERRA_NFT_BRIDGE_VAA"
npm start -- submit -c solana -n devnet "$REGISTER_ETH_NFT_BRIDGE_VAA"
npm start -- submit -c solana -n devnet "$REGISTER_TERRA_NFT_BRIDGE_VAA"
popd
# Let k8s startup probe succeed