worm: add aptos commands

This commit is contained in:
Csongor Kiss 2022-10-14 01:21:01 +00:00 committed by jumpsiegel
parent be0f58a537
commit 85a1dcd6fd
7 changed files with 853 additions and 16 deletions

191
clients/js/aptos.ts Normal file
View File

@ -0,0 +1,191 @@
import { AptosAccount, TxnBuilderTypes, AptosClient, BCS } from "aptos";
import { NETWORKS } from "./networks";
import { impossible, Payload } from "./vaa";
import { assertChain, ChainId, CONTRACTS } from "@certusone/wormhole-sdk";
import { Bytes, Seq } from "aptos/dist/transaction_builder/bcs/types";
import { TypeTag } from "aptos/dist/transaction_builder/aptos_types";
import { sha3_256 } from "js-sha3";
import { ethers } from "ethers";
export async function execute_aptos(
payload: Payload,
vaa: Buffer,
network: "MAINNET" | "TESTNET" | "DEVNET",
contract: string | undefined,
rpc: string | undefined
) {
const chain = "aptos";
// turn VAA bytes into BCS format. That is, add a length prefix
const serializer = new BCS.Serializer();
serializer.serializeBytes(vaa);
const bcsVAA = serializer.getBytes();
switch (payload.module) {
case "Core":
contract = contract ?? CONTRACTS[network][chain]["core"];
if (contract === undefined) {
throw Error("core bridge contract is undefined")
}
switch (payload.type) {
case "GuardianSetUpgrade":
console.log("Submitting new guardian set")
await callEntryFunc(network, rpc, `${contract}::guardian_set_upgrade`, "submit_vaa", [], [bcsVAA]);
break
case "ContractUpgrade":
console.log("Upgrading core contract")
await callEntryFunc(network, rpc, `${contract}::contract_upgrade`, "submit_vaa", [], [bcsVAA]);
break
default:
impossible(payload)
}
break
case "NFTBridge":
contract = contract ?? CONTRACTS[network][chain]["nft_bridge"];
if (contract === undefined) {
throw Error("nft bridge contract is undefined")
}
break
case "TokenBridge":
contract = contract ?? CONTRACTS[network][chain]["token_bridge"];
if (contract === undefined) {
throw Error("token bridge contract is undefined")
}
switch (payload.type) {
case "ContractUpgrade":
console.log("Upgrading contract")
await callEntryFunc(network, rpc, `${contract}::contract_upgrade`, "submit_vaa", [], [bcsVAA]);
break
case "RegisterChain":
console.log("Registering chain")
await callEntryFunc(network, rpc, `${contract}::register_chain`, "submit_vaa", [], [bcsVAA]);
break
case "AttestMeta":
console.log("Creating wrapped token")
// Deploying a wrapped asset requires two transactions:
// 1. Publish a new module under a resource account that defines a type T
// 2. Initialise a new coin with that type T
// These need to be done in separate transactions, becasue a
// transaction that deploys a module cannot use that module
//
// Tx 1.
await callEntryFunc(network, rpc, `${contract}::wrapped`, "create_wrapped_coin_type", [], [bcsVAA]);
// We just deployed the module (notice the "wait" argument which makes
// the previous step block until finality).
// Now we're ready to do Tx 2. The module above got deployed to a new
// resource account, which is seeded by the token bridge's address and
// the origin information of the token. We can recompute this address
// offline:
let tokenAddress = payload.tokenAddress;
let tokenChain = payload.tokenChain;
assertChain(tokenChain);
let wrappedContract = deriveWrappedAssetAddress(hex(contract), tokenChain, hex(tokenAddress));
// Tx 2.
console.log(`Deploying resource account ${wrappedContract}`);
const token = new TxnBuilderTypes.TypeTagStruct(TxnBuilderTypes.StructTag.fromString(`${wrappedContract}::coin::T`));
await callEntryFunc(network, rpc, `${contract}::wrapped`, "create_wrapped_coin", [token], [bcsVAA]);
break
case "Transfer":
console.log("Completing transfer")
await callEntryFunc(network, rpc, `${contract}::complete_transfer`, "submit_vaa", [], [bcsVAA]);
break
case "TransferWithPayload":
throw Error("Can't complete payload 3 transfer from CLI")
default:
impossible(payload)
break
}
break
default:
impossible(payload)
}
}
export function deriveWrappedAssetAddress(
token_bridge_address: Uint8Array, // 32 bytes
origin_chain: ChainId,
origin_address: Uint8Array, // 32 bytes
): string {
let chain: Buffer = Buffer.alloc(2);
chain.writeUInt16BE(origin_chain);
return sha3_256(Buffer.concat([token_bridge_address, chain, Buffer.from("::", "ascii"), origin_address]));
}
export function deriveResourceAccount(
deployer: Uint8Array, // 32 bytes
seed: string,
) {
// from https://github.com/aptos-labs/aptos-core/blob/25696fd266498d81d346fe86e01c330705a71465/aptos-move/framework/aptos-framework/sources/account.move#L90-L95
let DERIVE_RESOURCE_ACCOUNT_SCHEME = Buffer.alloc(1);
DERIVE_RESOURCE_ACCOUNT_SCHEME.writeUInt8(255);
return sha3_256(Buffer.concat([deployer, Buffer.from(seed, "ascii"), DERIVE_RESOURCE_ACCOUNT_SCHEME]))
}
export async function callEntryFunc(
network: "MAINNET" | "TESTNET" | "DEVNET",
rpc: string | undefined,
module: string,
func: string,
ty_args: Seq<TypeTag>,
args: Seq<Bytes>,
) {
let key: string | undefined = NETWORKS[network]["aptos"].key;
if (key === undefined) {
throw new Error("No key for aptos");
}
const accountFrom = new AptosAccount(new Uint8Array(Buffer.from(key, "hex")));
let client: AptosClient;
// if rpc arg is passed in, then override default rpc value for that network
if (typeof rpc != 'undefined'){
client = new AptosClient(rpc);
} else {
client = new AptosClient(NETWORKS[network]["aptos"].rpc);
}
const [{ sequence_number: sequenceNumber }, chainId] = await Promise.all([
client.getAccount(accountFrom.address()),
client.getChainId(),
]);
const txPayload = new TxnBuilderTypes.TransactionPayloadEntryFunction(
TxnBuilderTypes.EntryFunction.natural(
module,
func,
ty_args,
args
)
);
const rawTxn = new TxnBuilderTypes.RawTransaction(
TxnBuilderTypes.AccountAddress.fromHex(accountFrom.address()),
BigInt(sequenceNumber),
txPayload,
BigInt(20000), //max gas to be used. TODO(csongor): we could compute this from the simulation below...
BigInt(100), //price per unit gas TODO(csongor): we should get this dynamically
BigInt(Math.floor(Date.now() / 1000) + 10),
new TxnBuilderTypes.ChainId(chainId),
);
// simulate transaction before submitting
const sim = await client.simulateTransaction(accountFrom, rawTxn);
sim.forEach((tx) => {
if (!tx.success) {
console.error(JSON.stringify(tx, null, 2));
throw new Error(`Transaction failed: ${tx.vm_status}`);
}
});
// simulation successful... let's do it
const bcsTxn = AptosClient.generateBCSTransaction(accountFrom, rawTxn);
const transactionRes = await client.submitSignedBCSTransaction(bcsTxn);
await client.waitForTransaction(transactionRes.hash);
return transactionRes.hash;
}
// strip the 0x prefix from a hex string
function hex(x: string): Buffer {
return Buffer.from(ethers.utils.hexlify(x, { allowMissingPrefix: true }).substring(2), "hex");
}

401
clients/js/cmds/aptos.ts Normal file
View File

@ -0,0 +1,401 @@
import { CHAIN_ID_APTOS, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
import { BCS, FaucetClient } from "aptos";
import { ethers } from "ethers";
import yargs from "yargs";
import { callEntryFunc, deriveResourceAccount } from "../aptos";
import { spawnSync } from 'child_process';
import { config } from '../config';
import fs from 'fs';
import sha3 from 'js-sha3';
import { NETWORKS } from "../networks";
type Network = "MAINNET" | "TESTNET" | "DEVNET"
interface Package {
meta_file: string,
mv_files: string[]
}
interface PackageBCS {
meta: Uint8Array,
bytecodes: Uint8Array,
codeHash: Uint8Array
}
const network_options = {
alias: "n",
describe: "network",
type: "string",
choices: ["mainnet", "testnet", "devnet"],
required: true,
} as const;
const rpc_description = {
alias: "r",
describe: "override default rpc endpoint url",
type: "string",
required: false,
} as const;
const named_addresses = {
describe: "named addresses in the format addr1=0x0,addr2=0x1,...",
type: "string",
require: false
} as const;
// TODO(csongor): this could be useful elsewhere
function assertNetwork(n: string): asserts n is Network {
if (
n !== "MAINNET" &&
n !== "TESTNET" &&
n !== "DEVNET"
) {
throw Error(`Unknown network: ${n}`);
}
}
exports.command = 'aptos';
exports.desc = 'Aptos utilities ';
exports.builder = function (y: typeof yargs) {
return y
.command("init-token-bridge", "Init token bridge contract", (yargs) => {
return yargs
.option("network", network_options)
.option("rpc", rpc_description)
// TODO(csongor): once the sdk has this, just use it from there
.option("contract-address", {
alias: "a",
required: true,
describe: "Address where the wormhole module is deployed",
type: "string",
})
}, async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
const contract_address = evm_address(argv["contract-address"]);
const rpc = argv.rpc ?? NETWORKS[network]["aptos"].rpc;
await callEntryFunc(network, rpc, `${contract_address}::token_bridge`, "init", [], []);
})
.command("init-wormhole", "Init Wormhole core contract", (yargs) => {
return yargs
.option("network", network_options)
.option("rpc", rpc_description)
.option("chain-id", {
describe: "Chain id",
type: "number",
default: CHAIN_ID_APTOS,
required: false
})
.option("governance-chain-id", {
describe: "Governance chain id",
type: "number",
default: CHAIN_ID_SOLANA,
required: false
})
.option("governance-address", {
describe: "Governance address",
type: "string",
default: "0x0000000000000000000000000000000000000000000000000000000000000004",
required: false
})
// TODO(csongor): once the sdk has this, just use it from there
.option("contract-address", {
alias: "a",
required: true,
describe: "Address where the wormhole module is deployed",
type: "string",
})
.option("guardian-address", {
alias: "g",
required: true,
describe: "Initial guardian's address",
type: "string",
})
}, async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
const contract_address = evm_address(argv["contract-address"]);
const guardian_address = evm_address(argv["guardian-address"]).substring(24);
const chain_id = argv["chain-id"];
const governance_address = evm_address(argv["governance-address"]);
const governance_chain_id = argv["governance-chain-id"];
const args = [
BCS.bcsSerializeUint64(chain_id),
BCS.bcsSerializeUint64(governance_chain_id),
BCS.bcsSerializeBytes(Buffer.from(governance_address, "hex")),
BCS.bcsSerializeBytes(Buffer.from(guardian_address, "hex"))
]
const rpc = argv.rpc ?? NETWORKS[network]["aptos"].rpc;
await callEntryFunc(network, rpc, `${contract_address}::wormhole`, "init", [], args);
})
.command("deploy <package-dir>", "Deploy an Aptos package", (yargs) => {
return yargs
.positional("package-dir", {
type: "string"
})
.option("network", network_options)
.option("rpc", rpc_description)
.option("named-addresses", named_addresses)
}, async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
checkAptosBinary();
const p = buildPackage(argv["package-dir"], argv["named-addresses"]);
const b = serializePackage(p);
const rpc = argv.rpc ?? NETWORKS[network]["aptos"].rpc;
await callEntryFunc(network, rpc, "0x1::code", "publish_package_txn", [], [b.meta, b.bytecodes])
console.log("Deployed:", p.mv_files)
})
.command("deploy-resource <seed> <package-dir>", "Deploy an Aptos package using a resource account", (yargs) => {
return yargs
.positional("seed", {
type: "string"
})
.positional("package-dir", {
type: "string"
})
.option("network", network_options)
.option("rpc", rpc_description)
.option("named-addresses", named_addresses)
}, async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
checkAptosBinary();
const p = buildPackage(argv["package-dir"], argv["named-addresses"]);
const b = serializePackage(p);
const seed = Buffer.from(argv["seed"], "ascii")
// TODO(csongor): use deployer address from sdk (when it's there)
let module_name = "0x277fa055b6a73c42c0662d5236c65c864ccbf2d4abd21f174a30c8b786eab84b::deployer";
if (network == "TESTNET") {
module_name = "0x5ad53ef0cb7cd21816a0371c367be38e7874a9d2f71c77af7592f6b0791f6ca3::deployer";
}
const rpc = argv.rpc ?? NETWORKS[network]["aptos"].rpc;
await callEntryFunc(
network,
rpc,
module_name,
"deploy_derived",
[],
[
b.meta,
b.bytecodes,
BCS.bcsSerializeBytes(seed)
])
console.log("Deployed:", p.mv_files)
})
.command("send-example-message <message>", "Send example message", (yargs) => {
return yargs
.positional("message", {
type: "string"
})
.option("network", network_options)
}, async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
const rpc = NETWORKS[network]["aptos"].rpc;
// TODO(csongor): use sdk address
let module_name = "0x277fa055b6a73c42c0662d5236c65c864ccbf2d4abd21f174a30c8b786eab84b::sender";
if (network == "TESTNET") {
module_name = "0x5ad53ef0cb7cd21816a0371c367be38e7874a9d2f71c77af7592f6b0791f6ca3::sender";
}
await callEntryFunc(network, rpc, module_name, "send_message", [], [BCS.bcsSerializeBytes(Buffer.from(argv["message"], "ascii"))])
})
.command("derive-resource-account <account> <seed>", "Derive resource account address", (yargs) => {
return yargs
.positional("account", {
type: "string"
})
.positional("seed", {
type: "string"
})
}, async (argv) => {
console.log(deriveResourceAccount(Buffer.from(hex(argv['account']).substring(2), 'hex'), argv['seed']))
})
.command("hash-contracts <package-dir>", "Hash contract bytecodes for upgrade", (yargs) => {
return yargs
.positional("seed", {
type: "string"
})
.positional("package-dir", {
type: "string"
})
.option("named-addresses", named_addresses)
}, (argv) => {
checkAptosBinary();
const p = buildPackage(argv["package-dir"], argv["named-addresses"]);
const b = serializePackage(p);
console.log(Buffer.from(b.codeHash).toString("hex"));
})
.command("upgrade <package-dir>", "Perform upgrade after VAA has been submitted", (_yargs) => {
return yargs
.positional("package-dir", {
type: "string"
})
// TODO(csongor): once the sdk has the addresses, just look that up
// based on the module
.option("contract-address", {
alias: "a",
required: true,
describe: "Address where the wormhole module is deployed",
type: "string",
})
.option("network", network_options)
.option("rpc", rpc_description)
.option("named-addresses", named_addresses)
}, async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
checkAptosBinary();
const p = buildPackage(argv["package-dir"], argv["named-addresses"]);
const b = serializePackage(p);
const rpc = argv.rpc ?? NETWORKS[network]["aptos"].rpc;
// TODO(csongor): use deployer address from sdk (when it's there)
const hash = await callEntryFunc(
network,
rpc,
`${argv["contract-address"]}::contract_upgrade`,
"upgrade",
[],
[
b.meta,
b.bytecodes,
])
console.log("Deployed:", p.mv_files)
console.log(hash)
})
.command("migrate", "Perform migration after contract upgrade", (_yargs) => {
return yargs
// TODO(csongor): once the sdk has the addresses, just look that up
// based on the module
.option("contract-address", {
alias: "a",
required: true,
describe: "Address where the wormhole module is deployed",
type: "string",
})
.option("network", network_options)
.option("rpc", rpc_description)
}, async (argv) => {
const network = argv.network.toUpperCase();
assertNetwork(network);
checkAptosBinary();
const rpc = argv.rpc ?? NETWORKS[network]["aptos"].rpc;
// TODO(csongor): use deployer address from sdk (when it's there)
const hash = await callEntryFunc(
network,
rpc,
`${argv["contract-address"]}::contract_upgrade`,
"migrate",
[],
[])
console.log(hash)
})
// TODO - make faucet support testnet in additional to localnet
.command("faucet", "Request money from the faucet for a given account", (yargs) => {
return yargs
.option("rpc", rpc_description)
.option("faucet", {
alias: "f",
required: false,
describe: "faucet url",
type: "string",
})
.option("account", {
alias: "a",
required: false,
describe: "account to fund",
type: "string",
})
},
async (argv) => {
let NODE_URL = "http://0.0.0.0:8080/v1";
let FAUCET_URL = "http://0.0.0.0:8081";
let account = "0x277fa055b6a73c42c0662d5236c65c864ccbf2d4abd21f174a30c8b786eab84b";
if (argv.faucet != undefined) {
FAUCET_URL = argv.faucet as string;
}
if (argv.rpc != undefined) {
NODE_URL = argv.rpc as string;
}
if (argv.account != undefined) {
account = argv.account as string;
}
const faucetClient = new FaucetClient(NODE_URL, FAUCET_URL);
const coins = 20000000;
await faucetClient.fundAccount(account, coins);
console.log(`Funded ${account} with ${coins} coins`);
})
.strict().demandCommand();
}
function hex(x: string): string {
return ethers.utils.hexlify(x, { allowMissingPrefix: true });
}
function evm_address(x: string): string {
return hex(x).substring(2).padStart(64, "0");
}
export function checkAptosBinary(): void {
const dir = `${config.wormholeDir}/aptos`;
const aptos = spawnSync("aptos", ["--version"]);
if (aptos.status !== 0) {
console.error("aptos is not installed. Please install aptos and try again.");
console.error(`See ${dir}/README.md for instructions.`);
process.exit(1);
}
}
function buildPackage(dir: string, addrs?: string): Package {
const named_addresses =
addrs
? ["--named-addresses", addrs]
: [];
const aptos = spawnSync("aptos",
["move", "compile", "--save-metadata", "--included-artifacts", "none", "--package-dir", dir, ...named_addresses])
if (aptos.status !== 0) {
console.error(aptos.stderr.toString('utf8'))
console.error(aptos.stdout.toString('utf8'))
process.exit(1)
}
const result: any = JSON.parse(aptos.stdout.toString('utf8'))
const buildDirs =
fs.readdirSync(`${dir}/build`, { withFileTypes: true })
.filter(dirent => dirent.isDirectory())
.map(dirent => dirent.name)
if (buildDirs.length !== 1) {
console.error(`Unexpected directory structure in ${dir}/build: expected a single directory`)
process.exit(1)
}
const buildDir = `${dir}/build/${buildDirs[0]}`
return {
meta_file: `${buildDir}/package-metadata.bcs`,
mv_files: result["Result"].map((mod: string) => `${buildDir}/bytecode_modules/${mod.split("::")[1]}.mv`)
}
}
function serializePackage(p: Package): PackageBCS {
const metaBytes = fs.readFileSync(p.meta_file);
const packageMetadataSerializer = new BCS.Serializer();
packageMetadataSerializer.serializeBytes(metaBytes)
const serializedPackageMetadata = packageMetadataSerializer.getBytes();
const modules = p.mv_files.map(file => fs.readFileSync(file))
const serializer = new BCS.Serializer();
serializer.serializeU32AsUleb128(modules.length);
modules.forEach(module => serializer.serializeBytes(module));
const serializedModules = serializer.getBytes();
const hashes = [metaBytes].concat(modules).map((x) => Buffer.from(sha3.keccak256(x), "hex"));
const codeHash = Buffer.from(sha3.keccak256(Buffer.concat(hashes)), "hex")
return {
meta: serializedPackageMetadata,
bytecodes: serializedModules,
codeHash
}
}

View File

@ -1,6 +1,7 @@
import yargs from "yargs";
import { spawnSync } from 'child_process';
import { config } from '../config';
import { checkAptosBinary } from "./aptos";
exports.command = 'start-validator';
exports.desc = 'Start a local validator';
@ -14,13 +15,7 @@ exports.builder = function(y: typeof yargs) {
}).command("aptos", "Start a local aptos validator", (_yargs) => {
}, (argv) => {
const dir = `${config.wormholeDir}/aptos`;
// check if aptos is installed
const aptos = spawnSync("aptos", ["--version"]);
if (aptos.status !== 0) {
console.error("aptos is not installed. Please install aptos and try again.");
console.error(`See ${dir}/README.md for instructions.`);
process.exit(1);
}
checkAptosBinary();
const cmd = `cd ${dir} && aptos node run-local-testnet --with-faucet --force-restart --assume-yes`;
runCommand(cmd, argv['validator-args']);
}).command("evm", "Start a local EVM validator", (_yargs) => {

View File

@ -22,10 +22,10 @@ import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import { fromBech32, toHex } from "@cosmjs/encoding";
import { CONTRACTS as SDK_CONTRACTS } from "@certusone/wormhole-sdk";
import {
isTerraChain,
assertEVMChain,
CONTRACTS,
setDefaultWasm,
hexToUint8Array,
getEmitterAddressSolana,
@ -44,6 +44,7 @@ import {
setStorageAt,
} from "./evm";
import { execute_terra } from "./terra";
import { execute_aptos } from "./aptos";
import { execute_near } from "./near";
import * as vaa from "./vaa";
import { impossible, Payload, serialiseVAA, VAA } from "./vaa";
@ -67,13 +68,37 @@ import { isOutdated } from "./cmds/update";
setDefaultWasm("node");
if (isOutdated()) {
console.error("\x1b[33m%s\x1b[0m", "WARNING: 'worm' is out of date. Run 'worm update' to update.");
console.error("\x1b[33m%s\x1b[0m", "WARNING: 'worm' is out of date. Run 'worm update' to update.");
}
const GOVERNANCE_CHAIN = 1;
const GOVERNANCE_EMITTER =
"0000000000000000000000000000000000000000000000000000000000000004";
// TODO: put this into the sdk when things have finalised
const OVERRIDES = {
MAINNET: {
},
TESTNET: {
aptos: {
token_bridge: "0xdc406ec328a21eacbbe0c145f7884612d7094ebd637288149ac7879bbff43493",
core: "0xaf4faf174bad7dba8092fc5ac37b9b8fea3929f05fcb0677fd16dc735bc3ffc8"
}
},
DEVNET: {
aptos: {
token_bridge: "0x84a5f374d29fc77e370014dce4fd6a55b58ad608de8074b0be5571701724da31",
core: "0xde0036a9600559e295d5f6802ef6f3f802f510366e0c23912b0655d972166017"
}
}
}
const CONTRACTS = {
MAINNET: { ...SDK_CONTRACTS.MAINNET, ...OVERRIDES.MAINNET },
TESTNET: { ...SDK_CONTRACTS.TESTNET, ...OVERRIDES.TESTNET },
DEVNET: { ...SDK_CONTRACTS.DEVNET, ...OVERRIDES.DEVNET },
};
function makeVAA(
emitterChain: number,
emitterAddress: string,
@ -213,6 +238,89 @@ yargs(hideBin(process.argv))
console.log(serialiseVAA(v));
}
)
.command(
"attestation",
"Generate a token attestation VAA",
// TODO: putting 'any' here is a workaround for the following error:
//
// Type instantiation is excessively deep and possibly infinite.
//
// The type of the yargs builder grows too big for typescript's
// liking, and there's no way to increase the limit. So we
// overapproximate with the 'any' type which reduces the typechecking stack.
// This is not a great solution, and instead we should move toward
// breaking up the commands into multiple modules in the 'cmds' folder.
(yargs: any) => {
return yargs
.option("emitter-chain", {
alias: "e",
describe: "Emitter chain of the VAA",
type: "string",
choices: Object.keys(CHAINS),
required: true,
})
.option("emitter-address", {
alias: "f",
describe: "Emitter address of the VAA",
type: "string",
required: true,
})
.option("chain", {
alias: "c",
describe: "Token's chain",
type: "string",
choices: Object.keys(CHAINS),
required: true,
})
.option("token-address", {
alias: "a",
describe: "Token's address",
type: "string",
required: true,
})
.option("decimals", {
alias: "d",
describe: "Token's decimals",
type: "number",
required: true,
})
.option("symbol", {
alias: "s",
describe: "Token's symbol",
type: "string",
required: true,
})
.option("name", {
alias: "n",
describe: "Token's name",
type: "string",
required: true,
});
},
(argv) => {
let emitter_chain = argv["emitter-chain"] as string;
assertChain(argv["chain"]);
assertChain(emitter_chain);
let payload: vaa.TokenBridgeAttestMeta = {
module: "TokenBridge",
type: "AttestMeta",
chain: 0,
// TODO: remove these casts (only here because of the workaround above)
tokenAddress: parseAddress(argv["chain"], argv["token-address"] as string),
tokenChain: toChainId(argv["chain"]),
decimals: argv["decimals"] as number,
symbol: argv["symbol"] as string,
name: argv["name"] as string
}
let v = makeVAA(
toChainId(emitter_chain),
parseAddress(emitter_chain, argv["emitter-address"] as string),
argv["guardian-secret"].split(","),
payload
);
console.log(serialiseVAA(v));
}
)
);
},
(_) => {
@ -373,7 +481,6 @@ yargs(hideBin(process.argv))
},
async (argv) => {
assertChain(argv["chain"]);
assertEVMChain(argv["chain"]);
const network = argv.network.toUpperCase();
if (
network !== "MAINNET" &&
@ -698,7 +805,7 @@ yargs(hideBin(process.argv))
} else if (chain === "sui") {
throw Error("SUI is not supported yet");
} else if (chain === "aptos") {
throw Error("APTOS is not supported yet");
await execute_aptos(parsed_vaa.payload, buf, network, argv["contract-address"], argv["rpc"]);
} else if (chain === "wormholechain") {
throw Error("Wormhole Chain is not supported yet");
} else {
@ -739,7 +846,8 @@ function parseAddress(chain: ChainName, address: string): string {
} else if (chain === "sui") {
throw Error("SUI is not supported yet");
} else if (chain === "aptos") {
throw Error("APTOS is not supported yet");
// TODO: is there a better native format for aptos?
return "0x" + evm_address(address);
} else if (chain === "wormholechain") {
return "0x" + tryNativeToHexString(address, chain);
} else {

View File

@ -224,8 +224,8 @@ const TESTNET = {
key: get_env_var("OSMOSIS_KEY_TESTNET"),
},
aptos: {
rpc: undefined,
key: undefined,
rpc: "https://fullnode.testnet.aptoslabs.com/v1",
key: get_env_var("APTOS_TESTNET"),
},
sui: {
rpc: undefined,
@ -366,8 +366,8 @@ const DEVNET = {
key: undefined,
},
aptos: {
rpc: undefined,
key: undefined,
rpc: "http://0.0.0.0:8080",
key: "537c1f91e56891445b491068f519b705f8c0f1a1e66111816dd5d4aa85b8113d",
},
sui: {
rpc: undefined,

View File

@ -19,6 +19,7 @@
"@types/config": "^3.3.0",
"@xpla/xpla.js": "^0.2.1",
"algosdk": "^1.15.0",
"aptos": "^1.3.11",
"axios": "^0.24.0",
"binary-parser": "^2.0.2",
"bn.js": "^5.2.0",
@ -2741,6 +2742,32 @@
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
},
"node_modules/@scure/base": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz",
"integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==",
"funding": [
{
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
]
},
"node_modules/@scure/bip39": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.0.tgz",
"integrity": "sha512-pwrPOS16VeTKg98dYXQyIjJEcWfz7/1YJIwxUEPFfQPtc86Ym/1sVgQ2RLoD43AazMk2l/unK4ITySSpW2+82w==",
"funding": [
{
"type": "individual",
"url": "https://paulmillr.com/funding/"
}
],
"dependencies": {
"@noble/hashes": "~1.1.1",
"@scure/base": "~1.1.0"
}
},
"node_modules/@sindresorhus/is": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
@ -3630,6 +3657,44 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/aptos": {
"version": "1.3.11",
"resolved": "https://registry.npmjs.org/aptos/-/aptos-1.3.11.tgz",
"integrity": "sha512-IaiXvKGFrL7/dg931KjOU6ny59lCV724NyNNJF/L4zigTsV0+EIGGAyaJBOvWedPr43qrY6CKiEdkDjXsOQJSA==",
"dependencies": {
"@scure/bip39": "^1.1.0",
"axios": "^0.27.2",
"ed25519-hd-key": "^1.2.0",
"js-sha3": "^0.8.0",
"tweetnacl": "^1.0.3",
"typescript-memoize": "^1.1.0"
},
"engines": {
"node": ">=11.0.0"
}
},
"node_modules/aptos/node_modules/axios": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
"integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
"dependencies": {
"follow-redirects": "^1.14.9",
"form-data": "^4.0.0"
}
},
"node_modules/aptos/node_modules/form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
@ -4960,6 +5025,15 @@
"node": ">=4.0.0"
}
},
"node_modules/ed25519-hd-key": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/ed25519-hd-key/-/ed25519-hd-key-1.3.0.tgz",
"integrity": "sha512-IWwAyiiuJQhgu3L8NaHb68eJxTu2pgCwxIBdgpLJdKpYZM46+AXePSVTr7fkNKaUOfOL4IrjEUaQvyVRIDP7fg==",
"dependencies": {
"create-hmac": "1.1.7",
"tweetnacl": "1.0.3"
}
},
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@ -12883,6 +12957,11 @@
"node": ">=4.2.0"
}
},
"node_modules/typescript-memoize": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/typescript-memoize/-/typescript-memoize-1.1.1.tgz",
"integrity": "sha512-GQ90TcKpIH4XxYTI2F98yEQYZgjNMOGPpOgdjIBhaLaWji5HPWlRnZ4AeA1hfBxtY7bCGDJsqDDHk/KaHOl5bA=="
},
"node_modules/u3": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/u3/-/u3-0.1.1.tgz",
@ -15578,6 +15657,20 @@
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
},
"@scure/base": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz",
"integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA=="
},
"@scure/bip39": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.0.tgz",
"integrity": "sha512-pwrPOS16VeTKg98dYXQyIjJEcWfz7/1YJIwxUEPFfQPtc86Ym/1sVgQ2RLoD43AazMk2l/unK4ITySSpW2+82w==",
"requires": {
"@noble/hashes": "~1.1.1",
"@scure/base": "~1.1.0"
}
},
"@sindresorhus/is": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
@ -16345,6 +16438,40 @@
"color-convert": "^2.0.1"
}
},
"aptos": {
"version": "1.3.11",
"resolved": "https://registry.npmjs.org/aptos/-/aptos-1.3.11.tgz",
"integrity": "sha512-IaiXvKGFrL7/dg931KjOU6ny59lCV724NyNNJF/L4zigTsV0+EIGGAyaJBOvWedPr43qrY6CKiEdkDjXsOQJSA==",
"requires": {
"@scure/bip39": "^1.1.0",
"axios": "^0.27.2",
"ed25519-hd-key": "^1.2.0",
"js-sha3": "^0.8.0",
"tweetnacl": "^1.0.3",
"typescript-memoize": "^1.1.0"
},
"dependencies": {
"axios": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
"integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
"requires": {
"follow-redirects": "^1.14.9",
"form-data": "^4.0.0"
}
},
"form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
}
}
}
},
"arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
@ -17458,6 +17585,15 @@
}
}
},
"ed25519-hd-key": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/ed25519-hd-key/-/ed25519-hd-key-1.3.0.tgz",
"integrity": "sha512-IWwAyiiuJQhgu3L8NaHb68eJxTu2pgCwxIBdgpLJdKpYZM46+AXePSVTr7fkNKaUOfOL4IrjEUaQvyVRIDP7fg==",
"requires": {
"create-hmac": "1.1.7",
"tweetnacl": "1.0.3"
}
},
"ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@ -23284,6 +23420,11 @@
"integrity": "sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==",
"dev": true
},
"typescript-memoize": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/typescript-memoize/-/typescript-memoize-1.1.1.tgz",
"integrity": "sha512-GQ90TcKpIH4XxYTI2F98yEQYZgjNMOGPpOgdjIBhaLaWji5HPWlRnZ4AeA1hfBxtY7bCGDJsqDDHk/KaHOl5bA=="
},
"u3": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/u3/-/u3-0.1.1.tgz",

View File

@ -13,6 +13,7 @@
"@types/config": "^3.3.0",
"@xpla/xpla.js": "^0.2.1",
"algosdk": "^1.15.0",
"aptos": "^1.3.11",
"axios": "^0.24.0",
"binary-parser": "^2.0.2",
"bn.js": "^5.2.0",