[contract-manager] General improvements (#1024)

* Better return types for transaction execution functions

* Use new type for passing PrivateKey

* Small typing improvements

* Stricter liniting

* Rename edge to beta for DeploymentType

* Reduce any usage across the contract manager

* Fix explicit any linting warnings
This commit is contained in:
Mohammad Amin Khashkhashi Moghaddam 2023-08-23 16:29:43 +02:00 committed by GitHub
parent f316a512c3
commit b9dbd54d5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 317 additions and 208 deletions

View File

@ -0,0 +1,6 @@
module.exports = {
root: true,
parser: "@typescript-eslint/parser",
plugins: ["@typescript-eslint"],
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
};

View File

@ -10,7 +10,8 @@
], ],
"scripts": { "scripts": {
"build": "tsc", "build": "tsc",
"shell": "ts-node ./src/shell.ts" "shell": "ts-node ./src/shell.ts",
"lint": "eslint src/"
}, },
"author": "", "author": "",
"license": "Apache-2.0", "license": "Apache-2.0",

View File

@ -1,6 +1,6 @@
import yargs from "yargs"; import yargs from "yargs";
import { hideBin } from "yargs/helpers"; import { hideBin } from "yargs/helpers";
import { DefaultStore } from "../src"; import { DefaultStore, toPrivateKey } from "../src";
import { PriceServiceConnection } from "@pythnetwork/price-service-client"; import { PriceServiceConnection } from "@pythnetwork/price-service-client";
const parser = yargs(hideBin(process.argv)) const parser = yargs(hideBin(process.argv))
@ -43,9 +43,12 @@ async function main() {
argv.endpoint || defaultEndpoint argv.endpoint || defaultEndpoint
); );
const vaas = await priceService.getLatestVaas(argv["feed-id"] as string[]); const vaas = await priceService.getLatestVaas(argv["feed-id"] as string[]);
await contract.executeUpdatePriceFeed( const privateKey = toPrivateKey(argv["private-key"]);
argv["private-key"], console.log(
vaas.map((vaa) => Buffer.from(vaa, "base64")) await contract.executeUpdatePriceFeed(
privateKey,
vaas.map((vaa) => Buffer.from(vaa, "base64"))
)
); );
} }

View File

@ -1,6 +1,24 @@
import { DataSource } from "xc_admin_common"; import { DataSource } from "xc_admin_common";
import { Chain } from "./chains"; import { Chain } from "./chains";
export interface TxResult {
id: string;
info: unknown; // chain specific info
}
export type DeploymentType = "stable" | "beta";
export type PrivateKey = string & { __type: "PrivateKey" };
function checkIsPrivateKey(key: string): asserts key is PrivateKey {
if (Buffer.from(key, "hex").length !== 32)
throw new Error("Invalid private key, must be 64 hex chars");
}
export function toPrivateKey(key: string): PrivateKey {
checkIsPrivateKey(key);
return key;
}
export type KeyValueConfig = Record<string, string | number | boolean>;
export abstract class Storable { export abstract class Storable {
/** /**
* Returns the unique identifier for this object * Returns the unique identifier for this object
@ -17,7 +35,7 @@ export abstract class Storable {
* Returns a JSON representation of this object. It should be possible to * Returns a JSON representation of this object. It should be possible to
* reconstruct the object from the JSON using the fromJson method. * reconstruct the object from the JSON using the fromJson method.
*/ */
abstract toJson(): any; abstract toJson(): KeyValueConfig;
} }
export interface Price { export interface Price {
@ -75,9 +93,9 @@ export abstract class Contract extends Storable {
* @param vaas an array of VAAs containing price update messages to execute * @param vaas an array of VAAs containing price update messages to execute
*/ */
abstract executeUpdatePriceFeed( abstract executeUpdatePriceFeed(
senderPrivateKey: string, senderPrivateKey: PrivateKey,
vaas: Buffer[] vaas: Buffer[]
): Promise<any>; ): Promise<TxResult>;
/** /**
* Executes the governance instruction contained in the VAA using the sender credentials * Executes the governance instruction contained in the VAA using the sender credentials
@ -85,9 +103,9 @@ export abstract class Contract extends Storable {
* @param vaa the VAA to execute * @param vaa the VAA to execute
*/ */
abstract executeGovernanceInstruction( abstract executeGovernanceInstruction(
senderPrivateKey: string, senderPrivateKey: PrivateKey,
vaa: Buffer vaa: Buffer
): Promise<any>; ): Promise<TxResult>;
/** /**
* Returns the single data source that this contract accepts governance messages from * Returns the single data source that this contract accepts governance messages from
@ -95,11 +113,11 @@ export abstract class Contract extends Storable {
abstract getGovernanceDataSource(): Promise<DataSource>; abstract getGovernanceDataSource(): Promise<DataSource>;
} }
export function getDefaultDeploymentConfig(deploymentType: "stable" | "edge"): { export function getDefaultDeploymentConfig(deploymentType: DeploymentType): {
dataSources: DataSource[]; dataSources: DataSource[];
governanceDataSource: DataSource; governanceDataSource: DataSource;
wormholeConfig: { wormholeConfig: {
governanceChainId: Number; governanceChainId: number;
governanceContract: string; // 32 byte address in 64 char hex format governanceContract: string; // 32 byte address in 64 char hex format
initialGuardianSet: string[]; // 20 byte addresses in 40 char hex format initialGuardianSet: string[]; // 20 byte addresses in 40 char hex format
}; };
@ -135,7 +153,7 @@ export function getDefaultDeploymentConfig(deploymentType: "stable" | "edge"): {
initialGuardianSet: ["58cc3ae5c097b213ce3c81979e1b9f9570746aa5"], initialGuardianSet: ["58cc3ae5c097b213ce3c81979e1b9f9570746aa5"],
}, },
}; };
else if (deploymentType === "edge") else if (deploymentType === "beta")
return { return {
dataSources: [ dataSources: [
{ {

View File

@ -1,7 +1,6 @@
import { Storable } from "./base"; import { KeyValueConfig, PrivateKey, Storable } from "./base";
import { import {
ChainName, ChainName,
CHAINS,
SetFee, SetFee,
CosmosUpgradeContract, CosmosUpgradeContract,
EvmUpgradeContract, EvmUpgradeContract,
@ -22,6 +21,10 @@ import {
} from "@pythnetwork/cosmwasm-deploy-tools"; } from "@pythnetwork/cosmwasm-deploy-tools";
import { Network } from "@injectivelabs/networks"; import { Network } from "@injectivelabs/networks";
export type ChainConfig = Record<string, string> & {
mainnet: boolean;
id: string;
};
export abstract class Chain extends Storable { export abstract class Chain extends Storable {
public wormholeChainName: ChainName; public wormholeChainName: ChainName;
@ -92,15 +95,15 @@ export abstract class Chain extends Storable {
* Returns the payload for a governance contract upgrade instruction for contracts deployed on this chain * Returns the payload for a governance contract upgrade instruction for contracts deployed on this chain
* @param upgradeInfo based on the contract type, this can be a contract address, codeId, package digest, etc. * @param upgradeInfo based on the contract type, this can be a contract address, codeId, package digest, etc.
*/ */
abstract generateGovernanceUpgradePayload(upgradeInfo: any): Buffer; abstract generateGovernanceUpgradePayload(upgradeInfo: unknown): Buffer;
} }
export class GlobalChain extends Chain { export class GlobalChain extends Chain {
static type: string = "GlobalChain"; static type = "GlobalChain";
constructor() { constructor() {
super("global", true, "unset"); super("global", true, "unset");
} }
generateGovernanceUpgradePayload(upgradeInfo: any): Buffer { generateGovernanceUpgradePayload(): Buffer {
throw new Error( throw new Error(
"Can not create a governance message for upgrading contracts on all chains!" "Can not create a governance message for upgrading contracts on all chains!"
); );
@ -110,7 +113,7 @@ export class GlobalChain extends Chain {
return GlobalChain.type; return GlobalChain.type;
} }
toJson(): any { toJson(): KeyValueConfig {
return { return {
id: this.id, id: this.id,
wormholeChainName: this.wormholeChainName, wormholeChainName: this.wormholeChainName,
@ -121,7 +124,7 @@ export class GlobalChain extends Chain {
} }
export class CosmWasmChain extends Chain { export class CosmWasmChain extends Chain {
static type: string = "CosmWasmChain"; static type = "CosmWasmChain";
constructor( constructor(
id: string, id: string,
@ -135,7 +138,7 @@ export class CosmWasmChain extends Chain {
super(id, mainnet, wormholeChainName); super(id, mainnet, wormholeChainName);
} }
static fromJson(parsed: any): CosmWasmChain { static fromJson(parsed: ChainConfig): CosmWasmChain {
if (parsed.type !== CosmWasmChain.type) throw new Error("Invalid type"); if (parsed.type !== CosmWasmChain.type) throw new Error("Invalid type");
return new CosmWasmChain( return new CosmWasmChain(
parsed.id, parsed.id,
@ -148,7 +151,7 @@ export class CosmWasmChain extends Chain {
); );
} }
toJson(): any { toJson(): KeyValueConfig {
return { return {
endpoint: this.endpoint, endpoint: this.endpoint,
id: this.id, id: this.id,
@ -174,7 +177,7 @@ export class CosmWasmChain extends Chain {
return new CosmosUpgradeContract(this.wormholeChainName, codeId).encode(); return new CosmosUpgradeContract(this.wormholeChainName, codeId).encode();
} }
async getExecutor(privateKey: string) { async getExecutor(privateKey: PrivateKey) {
if (this.getId().indexOf("injective") > -1) { if (this.getId().indexOf("injective") > -1) {
return InjectiveExecutor.fromPrivateKey( return InjectiveExecutor.fromPrivateKey(
this.isMainnet() ? Network.Mainnet : Network.Testnet, this.isMainnet() ? Network.Mainnet : Network.Testnet,
@ -190,7 +193,7 @@ export class CosmWasmChain extends Chain {
} }
export class SuiChain extends Chain { export class SuiChain extends Chain {
static type: string = "SuiChain"; static type = "SuiChain";
constructor( constructor(
id: string, id: string,
@ -201,7 +204,7 @@ export class SuiChain extends Chain {
super(id, mainnet, wormholeChainName); super(id, mainnet, wormholeChainName);
} }
static fromJson(parsed: any): SuiChain { static fromJson(parsed: ChainConfig): SuiChain {
if (parsed.type !== SuiChain.type) throw new Error("Invalid type"); if (parsed.type !== SuiChain.type) throw new Error("Invalid type");
return new SuiChain( return new SuiChain(
parsed.id, parsed.id,
@ -211,7 +214,7 @@ export class SuiChain extends Chain {
); );
} }
toJson(): any { toJson(): KeyValueConfig {
return { return {
id: this.id, id: this.id,
wormholeChainName: this.wormholeChainName, wormholeChainName: this.wormholeChainName,
@ -238,7 +241,7 @@ export class SuiChain extends Chain {
} }
export class EvmChain extends Chain { export class EvmChain extends Chain {
static type: string = "EvmChain"; static type = "EvmChain";
constructor( constructor(
id: string, id: string,
@ -250,7 +253,7 @@ export class EvmChain extends Chain {
super(id, mainnet, wormholeChainName); super(id, mainnet, wormholeChainName);
} }
static fromJson(parsed: any): EvmChain { static fromJson(parsed: ChainConfig & { networkId: number }): EvmChain {
if (parsed.type !== EvmChain.type) throw new Error("Invalid type"); if (parsed.type !== EvmChain.type) throw new Error("Invalid type");
return new EvmChain( return new EvmChain(
parsed.id, parsed.id,
@ -277,7 +280,7 @@ export class EvmChain extends Chain {
return new EvmSetWormholeAddress(this.wormholeChainName, address).encode(); return new EvmSetWormholeAddress(this.wormholeChainName, address).encode();
} }
toJson(): any { toJson(): KeyValueConfig {
return { return {
id: this.id, id: this.id,
wormholeChainName: this.wormholeChainName, wormholeChainName: this.wormholeChainName,
@ -311,10 +314,10 @@ export class EvmChain extends Chain {
* @returns the address of the deployed contract * @returns the address of the deployed contract
*/ */
async deploy( async deploy(
privateKey: string, privateKey: PrivateKey,
abi: any, abi: any, // eslint-disable-line @typescript-eslint/no-explicit-any
bytecode: string, bytecode: string,
deployArgs: any[] deployArgs: any[] // eslint-disable-line @typescript-eslint/no-explicit-any
): Promise<string> { ): Promise<string> {
const web3 = new Web3(this.getRpcUrl()); const web3 = new Web3(this.getRpcUrl());
const signer = web3.eth.accounts.privateKeyToAccount(privateKey); const signer = web3.eth.accounts.privateKeyToAccount(privateKey);
@ -375,7 +378,7 @@ export class AptosChain extends Chain {
return AptosChain.type; return AptosChain.type;
} }
toJson(): any { toJson(): KeyValueConfig {
return { return {
id: this.id, id: this.id,
wormholeChainName: this.wormholeChainName, wormholeChainName: this.wormholeChainName,
@ -385,7 +388,7 @@ export class AptosChain extends Chain {
}; };
} }
static fromJson(parsed: any): AptosChain { static fromJson(parsed: ChainConfig): AptosChain {
if (parsed.type !== AptosChain.type) throw new Error("Invalid type"); if (parsed.type !== AptosChain.type) throw new Error("Invalid type");
return new AptosChain( return new AptosChain(
parsed.id, parsed.id,

View File

@ -1,11 +1,11 @@
import { Contract, PriceFeed } from "../base"; import { Contract, PriceFeed, PrivateKey, TxResult } from "../base";
import { AptosAccount, BCS, TxnBuilderTypes } from "aptos"; import { ApiError, AptosAccount, BCS, TxnBuilderTypes } from "aptos";
import { AptosChain, Chain } from "../chains"; import { AptosChain, Chain } from "../chains";
import { DataSource } from "xc_admin_common"; import { DataSource } from "xc_admin_common";
import { CoinClient } from "aptos"; import { CoinClient } from "aptos";
export class AptosContract extends Contract { export class AptosContract extends Contract {
static type: string = "AptosContract"; static type = "AptosContract";
/** /**
* Given the ids of the pyth state and wormhole state, create a new AptosContract * Given the ids of the pyth state and wormhole state, create a new AptosContract
@ -23,7 +23,10 @@ export class AptosContract extends Contract {
super(); super();
} }
static fromJson(chain: Chain, parsed: any): AptosContract { static fromJson(
chain: Chain,
parsed: { type: string; stateId: string; wormholeStateId: string }
): AptosContract {
if (parsed.type !== AptosContract.type) throw new Error("Invalid type"); if (parsed.type !== AptosContract.type) throw new Error("Invalid type");
if (!(chain instanceof AptosChain)) if (!(chain instanceof AptosChain))
throw new Error(`Wrong chain type ${chain}`); throw new Error(`Wrong chain type ${chain}`);
@ -31,9 +34,9 @@ export class AptosContract extends Contract {
} }
async executeGovernanceInstruction( async executeGovernanceInstruction(
senderPrivateKey: string, senderPrivateKey: PrivateKey,
vaa: Buffer vaa: Buffer
): Promise<any> { ): Promise<TxResult> {
const txPayload = new TxnBuilderTypes.TransactionPayloadEntryFunction( const txPayload = new TxnBuilderTypes.TransactionPayloadEntryFunction(
TxnBuilderTypes.EntryFunction.natural( TxnBuilderTypes.EntryFunction.natural(
`${this.stateId}::governance`, `${this.stateId}::governance`,
@ -45,23 +48,28 @@ export class AptosContract extends Contract {
return this.sendTransaction(senderPrivateKey, txPayload); return this.sendTransaction(senderPrivateKey, txPayload);
} }
private sendTransaction( private async sendTransaction(
senderPrivateKey: string, senderPrivateKey: PrivateKey,
txPayload: TxnBuilderTypes.TransactionPayloadEntryFunction txPayload: TxnBuilderTypes.TransactionPayloadEntryFunction
) { ): Promise<TxResult> {
const client = this.chain.getClient(); const client = this.chain.getClient();
const sender = new AptosAccount( const sender = new AptosAccount(
new Uint8Array(Buffer.from(senderPrivateKey, "hex")) new Uint8Array(Buffer.from(senderPrivateKey, "hex"))
); );
return client.generateSignSubmitWaitForTransaction(sender, txPayload, { const result = await client.generateSignSubmitWaitForTransaction(
maxGasAmount: BigInt(30000), sender,
}); txPayload,
{
maxGasAmount: BigInt(30000),
}
);
return { id: result.hash, info: result };
} }
async executeUpdatePriceFeed( async executeUpdatePriceFeed(
senderPrivateKey: string, senderPrivateKey: PrivateKey,
vaas: Buffer[] vaas: Buffer[]
): Promise<any> { ): Promise<TxResult> {
const txPayload = new TxnBuilderTypes.TransactionPayloadEntryFunction( const txPayload = new TxnBuilderTypes.TransactionPayloadEntryFunction(
TxnBuilderTypes.EntryFunction.natural( TxnBuilderTypes.EntryFunction.natural(
`${this.stateId}::pyth`, `${this.stateId}::pyth`,
@ -93,7 +101,7 @@ export class AptosContract extends Contract {
} }
async getBaseUpdateFee() { async getBaseUpdateFee() {
const data = (await this.findResource("BaseUpdateFee")) as any; const data = (await this.findResource("BaseUpdateFee")) as { fee: string };
return { amount: data.fee }; return { amount: data.fee };
} }
@ -101,7 +109,12 @@ export class AptosContract extends Contract {
return this.chain; return this.chain;
} }
private parsePrice(priceInfo: any) { private parsePrice(priceInfo: {
expo: { magnitude: string; negative: boolean };
price: { magnitude: string; negative: boolean };
conf: string;
timestamp: string;
}) {
let expo = priceInfo.expo.magnitude; let expo = priceInfo.expo.magnitude;
if (priceInfo.expo.negative) expo = "-" + expo; if (priceInfo.expo.negative) expo = "-" + expo;
let price = priceInfo.price.magnitude; let price = priceInfo.price.magnitude;
@ -116,7 +129,9 @@ export class AptosContract extends Contract {
async getPriceFeed(feedId: string): Promise<PriceFeed | undefined> { async getPriceFeed(feedId: string): Promise<PriceFeed | undefined> {
const client = this.chain.getClient(); const client = this.chain.getClient();
const res = (await this.findResource("LatestPriceInfo")) as any; const res = (await this.findResource("LatestPriceInfo")) as {
info: { handle: string };
};
const handle = res.info.handle; const handle = res.info.handle;
try { try {
const priceItemRes = await client.getTableItem(handle, { const priceItemRes = await client.getTableItem(handle, {
@ -130,15 +145,23 @@ export class AptosContract extends Contract {
price: this.parsePrice(priceItemRes.price_feed.price), price: this.parsePrice(priceItemRes.price_feed.price),
emaPrice: this.parsePrice(priceItemRes.price_feed.ema_price), emaPrice: this.parsePrice(priceItemRes.price_feed.ema_price),
}; };
} catch (e: any) { } catch (e) {
if (e.errorCode === "table_item_not_found") return undefined; if (e instanceof ApiError && e.errorCode === "table_item_not_found")
return undefined;
throw e; throw e;
} }
} }
async getDataSources(): Promise<DataSource[]> { async getDataSources(): Promise<DataSource[]> {
const data = (await this.findResource("DataSources")) as any; const data = (await this.findResource("DataSources")) as {
return data.sources.keys.map((source: any) => { sources: {
keys: {
emitter_chain: string;
emitter_address: { external_address: string };
}[];
};
};
return data.sources.keys.map((source) => {
return { return {
emitterChain: Number(source.emitter_chain), emitterChain: Number(source.emitter_chain),
emitterAddress: source.emitter_address.external_address.replace( emitterAddress: source.emitter_address.external_address.replace(
@ -150,7 +173,12 @@ export class AptosContract extends Contract {
} }
async getGovernanceDataSource(): Promise<DataSource> { async getGovernanceDataSource(): Promise<DataSource> {
const data = (await this.findResource("GovernanceDataSource")) as any; const data = (await this.findResource("GovernanceDataSource")) as {
source: {
emitter_chain: string;
emitter_address: { external_address: string };
};
};
return { return {
emitterChain: Number(data.source.emitter_chain), emitterChain: Number(data.source.emitter_chain),
emitterAddress: data.source.emitter_address.external_address.replace( emitterAddress: data.source.emitter_address.external_address.replace(
@ -163,7 +191,7 @@ export class AptosContract extends Contract {
async getLastExecutedGovernanceSequence() { async getLastExecutedGovernanceSequence() {
const data = (await this.findResource( const data = (await this.findResource(
"LastExecutedGovernanceSequence" "LastExecutedGovernanceSequence"
)) as any; )) as { sequence: string };
return Number(data.sequence); return Number(data.sequence);
} }
@ -181,7 +209,9 @@ export class AptosContract extends Contract {
} }
async getValidTimePeriod() { async getValidTimePeriod() {
const data = (await this.findResource("StalePriceThreshold")) as any; const data = (await this.findResource("StalePriceThreshold")) as {
threshold_secs: string;
};
return Number(data.threshold_secs); return Number(data.threshold_secs);
} }

View File

@ -9,30 +9,29 @@ import {
PythWrapperExecutor, PythWrapperExecutor,
PythWrapperQuerier, PythWrapperQuerier,
} from "@pythnetwork/cosmwasm-deploy-tools"; } from "@pythnetwork/cosmwasm-deploy-tools";
import { Coin } from "@cosmjs/stargate";
import { CHAINS, DataSource } from "xc_admin_common"; import { CHAINS, DataSource } from "xc_admin_common";
import { CosmWasmClient } from "@cosmjs/cosmwasm-stargate"; import { CosmWasmClient } from "@cosmjs/cosmwasm-stargate";
import { Contract } from "../base"; import { Contract, PrivateKey, TxResult } from "../base";
import { WormholeContract } from "./wormhole"; import { WormholeContract } from "./wormhole";
/** /**
* Variables here need to be snake case to match the on-chain contract configs * Variables here need to be snake case to match the on-chain contract configs
*/ */
namespace CosmWasmContract { export interface WormholeSource {
export interface WormholeSource { emitter: string;
emitter: string; chain_id: number;
chain_id: number; }
}
export interface DeploymentConfig { export interface DeploymentConfig {
data_sources: WormholeSource[]; data_sources: WormholeSource[];
governance_source: WormholeSource; governance_source: WormholeSource;
wormhole_contract: string; wormhole_contract: string;
governance_source_index: number; governance_source_index: number;
governance_sequence_number: number; governance_sequence_number: number;
chain_id: number; chain_id: number;
valid_time_period_secs: number; valid_time_period_secs: number;
fee: { amount: string; denom: string }; fee: { amount: string; denom: string };
}
} }
export class WormholeCosmWasmContract extends WormholeContract { export class WormholeCosmWasmContract extends WormholeContract {
@ -44,7 +43,7 @@ export class WormholeCosmWasmContract extends WormholeContract {
const chainQuerier = await CosmwasmQuerier.connect(this.chain.endpoint); const chainQuerier = await CosmwasmQuerier.connect(this.chain.endpoint);
return (await chainQuerier.getAllContractState({ return (await chainQuerier.getAllContractState({
contractAddr: this.address, contractAddr: this.address,
})) as any; })) as Record<string, string>;
} }
async getCurrentGuardianSetIndex(): Promise<number> { async getCurrentGuardianSetIndex(): Promise<number> {
@ -68,28 +67,31 @@ export class WormholeCosmWasmContract extends WormholeContract {
} }
async upgradeGuardianSets( async upgradeGuardianSets(
senderPrivateKey: string, senderPrivateKey: PrivateKey,
vaa: Buffer vaa: Buffer
): Promise<any> { ): Promise<TxResult> {
const executor = await this.chain.getExecutor(senderPrivateKey); const executor = await this.chain.getExecutor(senderPrivateKey);
return executor.executeContract({ const result = await executor.executeContract({
contractAddr: this.address, contractAddr: this.address,
msg: { msg: {
submit_v_a_a: { vaa: vaa.toString("base64") }, submit_v_a_a: { vaa: vaa.toString("base64") },
}, },
}); });
return { id: result.txHash, info: result };
} }
} }
export class CosmWasmContract extends Contract { export class CosmWasmContract extends Contract {
async getDataSources(): Promise<DataSource[]> { async getDataSources(): Promise<DataSource[]> {
const config = await this.getConfig(); const config = await this.getConfig();
return config.config_v1.data_sources.map(({ emitter, chain_id }: any) => { return config.config_v1.data_sources.map(
return { ({ emitter, chain_id }: { emitter: string; chain_id: string }) => {
emitterChain: Number(chain_id), return {
emitterAddress: Buffer.from(emitter, "base64").toString("hex"), emitterChain: Number(chain_id),
}; emitterAddress: Buffer.from(emitter, "base64").toString("hex"),
}); };
}
);
} }
async getGovernanceDataSource(): Promise<DataSource> { async getGovernanceDataSource(): Promise<DataSource> {
@ -108,7 +110,10 @@ export class CosmWasmContract extends Contract {
super(); super();
} }
static fromJson(chain: Chain, parsed: any): CosmWasmContract { static fromJson(
chain: Chain,
parsed: { type: string; address: string }
): CosmWasmContract {
if (parsed.type !== CosmWasmContract.type) throw new Error("Invalid type"); if (parsed.type !== CosmWasmContract.type) throw new Error("Invalid type");
if (!(chain instanceof CosmWasmChain)) if (!(chain instanceof CosmWasmChain))
throw new Error(`Wrong chain type ${chain}`); throw new Error(`Wrong chain type ${chain}`);
@ -119,17 +124,16 @@ export class CosmWasmContract extends Contract {
return CosmWasmContract.type; return CosmWasmContract.type;
} }
//TODO : make deploymentType enum stable | edge
static getDeploymentConfig( static getDeploymentConfig(
chain: CosmWasmChain, chain: CosmWasmChain,
deploymentType: string, deploymentType: DeploymentType,
wormholeContract: string wormholeContract: string
): CosmWasmContract.DeploymentConfig { ): DeploymentConfig {
return getPythConfig({ return getPythConfig({
feeDenom: chain.feeDenom, feeDenom: chain.feeDenom,
wormholeChainId: CHAINS[chain.getId() as keyof typeof CHAINS], wormholeChainId: CHAINS[chain.wormholeChainName],
wormholeContract, wormholeContract,
deploymentType: deploymentType as DeploymentType, deploymentType: deploymentType,
}); });
} }
@ -142,11 +146,11 @@ export class CosmWasmContract extends Contract {
*/ */
static async storeCode( static async storeCode(
chain: CosmWasmChain, chain: CosmWasmChain,
privateKey: string, privateKey: PrivateKey,
wasmPath: string wasmPath: string
) { ) {
const contractBytes = readFileSync(wasmPath); const contractBytes = readFileSync(wasmPath);
let executor = await chain.getExecutor(privateKey); const executor = await chain.getExecutor(privateKey);
return executor.storeCode({ contractBytes }); return executor.storeCode({ contractBytes });
} }
@ -160,11 +164,11 @@ export class CosmWasmContract extends Contract {
static async initialize( static async initialize(
chain: CosmWasmChain, chain: CosmWasmChain,
codeId: number, codeId: number,
config: CosmWasmContract.DeploymentConfig, config: DeploymentConfig,
privateKey: string privateKey: PrivateKey
): Promise<CosmWasmContract> { ): Promise<CosmWasmContract> {
let executor = await chain.getExecutor(privateKey); const executor = await chain.getExecutor(privateKey);
let result = await executor.instantiateContract({ const result = await executor.instantiateContract({
codeId: codeId, codeId: codeId,
instMsg: config, instMsg: config,
label: "pyth", label: "pyth",
@ -190,10 +194,10 @@ export class CosmWasmContract extends Contract {
static async deploy( static async deploy(
chain: CosmWasmChain, chain: CosmWasmChain,
wormholeContract: string, wormholeContract: string,
privateKey: string, privateKey: PrivateKey,
wasmPath: string wasmPath: string
): Promise<CosmWasmContract> { ): Promise<CosmWasmContract> {
let config = this.getDeploymentConfig(chain, "edge", wormholeContract); const config = this.getDeploymentConfig(chain, "beta", wormholeContract);
const { codeId } = await this.storeCode(chain, privateKey, wasmPath); const { codeId } = await this.storeCode(chain, privateKey, wasmPath);
return this.initialize(chain, codeId, config, privateKey); return this.initialize(chain, codeId, config, privateKey);
} }
@ -217,7 +221,7 @@ export class CosmWasmContract extends Contract {
} }
async getCodeId(): Promise<number> { async getCodeId(): Promise<number> {
let result = await this.getWasmContractInfo(); const result = await this.getWasmContractInfo();
return result.codeId; return result.codeId;
} }
@ -228,10 +232,10 @@ export class CosmWasmContract extends Contract {
async getConfig() { async getConfig() {
const chainQuerier = await CosmwasmQuerier.connect(this.chain.endpoint); const chainQuerier = await CosmwasmQuerier.connect(this.chain.endpoint);
let allStates = (await chainQuerier.getAllContractState({ const allStates = (await chainQuerier.getAllContractState({
contractAddr: this.address, contractAddr: this.address,
})) as any; })) as Record<string, string>;
let config = { const config = {
config_v1: JSON.parse(allStates["\x00\tconfig_v1"]), config_v1: JSON.parse(allStates["\x00\tconfig_v1"]),
contract_version: JSON.parse(allStates["\x00\x10contract_version"]), contract_version: JSON.parse(allStates["\x00\x10contract_version"]),
}; };
@ -256,7 +260,7 @@ export class CosmWasmContract extends Contract {
} }
async getPriceFeed(feedId: string) { async getPriceFeed(feedId: string) {
let querier = await this.getQuerier(); const querier = await this.getQuerier();
try { try {
const response = await querier.getPriceFeed(this.address, feedId); const response = await querier.getPriceFeed(this.address, feedId);
return { return {
@ -269,8 +273,8 @@ export class CosmWasmContract extends Contract {
} }
equalDataSources( equalDataSources(
dataSources1: CosmWasmContract.WormholeSource[], dataSources1: WormholeSource[],
dataSources2: CosmWasmContract.WormholeSource[] dataSources2: WormholeSource[]
): boolean { ): boolean {
if (dataSources1.length !== dataSources2.length) return false; if (dataSources1.length !== dataSources2.length) return false;
for (let i = 0; i < dataSources1.length; i++) { for (let i = 0; i < dataSources1.length; i++) {
@ -290,19 +294,19 @@ export class CosmWasmContract extends Contract {
} }
async getDeploymentType(): Promise<string> { async getDeploymentType(): Promise<string> {
let config = await this.getConfig(); const config = await this.getConfig();
let wormholeContract = config.config_v1.wormhole_contract; const wormholeContract = config.config_v1.wormhole_contract;
let stableConfig = getPythConfig({ const stableConfig = getPythConfig({
feeDenom: this.chain.feeDenom, feeDenom: this.chain.feeDenom,
wormholeChainId: CHAINS[this.chain.getId() as keyof typeof CHAINS], wormholeChainId: CHAINS[this.chain.getId() as keyof typeof CHAINS],
wormholeContract, wormholeContract,
deploymentType: "stable", deploymentType: "stable",
}); });
let edgeConfig = getPythConfig({ const betaConfig = getPythConfig({
feeDenom: this.chain.feeDenom, feeDenom: this.chain.feeDenom,
wormholeChainId: CHAINS[this.chain.getId() as keyof typeof CHAINS], wormholeChainId: CHAINS[this.chain.getId() as keyof typeof CHAINS],
wormholeContract, wormholeContract,
deploymentType: "edge", deploymentType: "beta",
}); });
if ( if (
this.equalDataSources( this.equalDataSources(
@ -314,52 +318,53 @@ export class CosmWasmContract extends Contract {
else if ( else if (
this.equalDataSources( this.equalDataSources(
config.config_v1.data_sources, config.config_v1.data_sources,
edgeConfig.data_sources betaConfig.data_sources
) )
) )
return "edge"; return "beta";
else return "unknown"; else return "unknown";
} }
async executeUpdatePriceFeed(senderPrivateKey: string, vaas: Buffer[]) { async executeUpdatePriceFeed(senderPrivateKey: PrivateKey, vaas: Buffer[]) {
const base64Vaas = vaas.map((v) => v.toString("base64")); const base64Vaas = vaas.map((v) => v.toString("base64"));
const fund = await this.getUpdateFee(base64Vaas); const fund = await this.getUpdateFee(base64Vaas);
let executor = await this.chain.getExecutor(senderPrivateKey); const executor = await this.chain.getExecutor(senderPrivateKey);
let pythExecutor = new PythWrapperExecutor(executor); const pythExecutor = new PythWrapperExecutor(executor);
return pythExecutor.executeUpdatePriceFeeds({ const result = await pythExecutor.executeUpdatePriceFeeds({
contractAddr: this.address, contractAddr: this.address,
vaas: base64Vaas, vaas: base64Vaas,
fund, fund,
}); });
return { id: result.txHash, info: result };
} }
async executeGovernanceInstruction(privateKey: string, vaa: Buffer) { async executeGovernanceInstruction(privateKey: PrivateKey, vaa: Buffer) {
let executor = await this.chain.getExecutor(privateKey); const executor = await this.chain.getExecutor(privateKey);
let pythExecutor = new PythWrapperExecutor(executor); const pythExecutor = new PythWrapperExecutor(executor);
return pythExecutor.executeGovernanceInstruction({ const result = await pythExecutor.executeGovernanceInstruction({
contractAddr: this.address, contractAddr: this.address,
vaa: vaa.toString("base64"), vaa: vaa.toString("base64"),
}); });
return { id: result.txHash, info: result };
} }
async getWormholeContract(): Promise<WormholeCosmWasmContract> { async getWormholeContract(): Promise<WormholeCosmWasmContract> {
let config = await this.getConfig(); const config = await this.getConfig();
const chainQuerier = await CosmwasmQuerier.connect(this.chain.endpoint);
const wormholeAddress = config.config_v1.wormhole_contract; const wormholeAddress = config.config_v1.wormhole_contract;
return new WormholeCosmWasmContract(this.chain, wormholeAddress); return new WormholeCosmWasmContract(this.chain, wormholeAddress);
} }
async getUpdateFee(msgs: string[]): Promise<any> { async getUpdateFee(msgs: string[]): Promise<Coin> {
let querier = await this.getQuerier(); const querier = await this.getQuerier();
return querier.getUpdateFee(this.address, msgs); return querier.getUpdateFee(this.address, msgs);
} }
async getBaseUpdateFee(): Promise<any> { async getBaseUpdateFee(): Promise<{ amount: string; denom: string }> {
const config = await this.getConfig(); const config = await this.getConfig();
return config.config_v1.fee; return config.config_v1.fee;
} }
async getVersion(): Promise<any> { async getVersion(): Promise<string> {
const config = await this.getConfig(); const config = await this.getConfig();
return config.contract_version; return config.contract_version;
} }
@ -378,8 +383,8 @@ export class CosmWasmContract extends Contract {
} }
async getValidTimePeriod() { async getValidTimePeriod() {
let client = await CosmWasmClient.connect(this.chain.endpoint); const client = await CosmWasmClient.connect(this.chain.endpoint);
let result = await client.queryContractSmart( const result = await client.queryContractSmart(
this.address, this.address,
"get_valid_time_period" "get_valid_time_period"
); );

View File

@ -1,6 +1,6 @@
import Web3 from "web3"; //TODO: decide on using web3 or ethers.js import Web3 from "web3";
import PythInterfaceAbi from "@pythnetwork/pyth-sdk-solidity/abis/IPyth.json"; import PythInterfaceAbi from "@pythnetwork/pyth-sdk-solidity/abis/IPyth.json";
import { Contract } from "../base"; import { Contract, PrivateKey } from "../base";
import { Chain, EvmChain } from "../chains"; import { Chain, EvmChain } from "../chains";
import { DataSource } from "xc_admin_common"; import { DataSource } from "xc_admin_common";
import { WormholeContract } from "./wormhole"; import { WormholeContract } from "./wormhole";
@ -138,7 +138,7 @@ const EXTENDED_PYTH_ABI = [
type: "function", type: "function",
}, },
...PythInterfaceAbi, ...PythInterfaceAbi,
] as any; ] as any; // eslint-disable-line @typescript-eslint/no-explicit-any
const WORMHOLE_ABI = [ const WORMHOLE_ABI = [
{ {
@ -211,7 +211,7 @@ const WORMHOLE_ABI = [
stateMutability: "view", stateMutability: "view",
type: "function", type: "function",
}, },
] as any; ] as any; // eslint-disable-line @typescript-eslint/no-explicit-any
export class WormholeEvmContract extends WormholeContract { export class WormholeEvmContract extends WormholeContract {
constructor(public chain: EvmChain, public address: string) { constructor(public chain: EvmChain, public address: string) {
super(); super();
@ -256,7 +256,7 @@ export class WormholeEvmContract extends WormholeContract {
} }
} }
async upgradeGuardianSets(senderPrivateKey: string, vaa: Buffer) { async upgradeGuardianSets(senderPrivateKey: PrivateKey, vaa: Buffer) {
const web3 = new Web3(this.chain.getRpcUrl()); const web3 = new Web3(this.chain.getRpcUrl());
const { address } = web3.eth.accounts.wallet.add(senderPrivateKey); const { address } = web3.eth.accounts.wallet.add(senderPrivateKey);
const wormholeContract = new web3.eth.Contract(WORMHOLE_ABI, this.address); const wormholeContract = new web3.eth.Contract(WORMHOLE_ABI, this.address);
@ -267,11 +267,12 @@ export class WormholeEvmContract extends WormholeContract {
from: address, from: address,
gas: 100000000, gas: 100000000,
}); });
return transactionObject.send({ const result = await transactionObject.send({
from: address, from: address,
gas: gasEstiamte * GAS_ESTIMATE_MULTIPLIER, gas: gasEstiamte * GAS_ESTIMATE_MULTIPLIER,
gasPrice: await this.chain.getGasPrice(), gasPrice: await this.chain.getGasPrice(),
}); });
return { id: result.transactionHash, info: result };
} }
} }
@ -282,7 +283,10 @@ export class EvmContract extends Contract {
super(); super();
} }
static fromJson(chain: Chain, parsed: any): EvmContract { static fromJson(
chain: Chain,
parsed: { type: string; address: string }
): EvmContract {
if (parsed.type !== EvmContract.type) throw new Error("Invalid type"); if (parsed.type !== EvmContract.type) throw new Error("Invalid type");
if (!(chain instanceof EvmChain)) if (!(chain instanceof EvmChain))
throw new Error(`Wrong chain type ${chain}`); throw new Error(`Wrong chain type ${chain}`);
@ -321,7 +325,7 @@ export class EvmContract extends Contract {
async getImplementationAddress(): Promise<string> { async getImplementationAddress(): Promise<string> {
const web3 = new Web3(this.chain.getRpcUrl()); const web3 = new Web3(this.chain.getRpcUrl());
// bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) according to EIP-1967 // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) according to EIP-1967
let storagePosition = const storagePosition =
"0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"; "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
let address = await web3.eth.getStorageAt(this.address, storagePosition); let address = await web3.eth.getStorageAt(this.address, storagePosition);
address = "0x" + address.slice(26); address = "0x" + address.slice(26);
@ -433,7 +437,7 @@ export class EvmContract extends Contract {
}; };
} }
async executeUpdatePriceFeed(senderPrivateKey: string, vaas: Buffer[]) { async executeUpdatePriceFeed(senderPrivateKey: PrivateKey, vaas: Buffer[]) {
const web3 = new Web3(this.chain.getRpcUrl()); const web3 = new Web3(this.chain.getRpcUrl());
const { address } = web3.eth.accounts.wallet.add(senderPrivateKey); const { address } = web3.eth.accounts.wallet.add(senderPrivateKey);
const pythContract = new web3.eth.Contract(EXTENDED_PYTH_ABI, this.address); const pythContract = new web3.eth.Contract(EXTENDED_PYTH_ABI, this.address);
@ -448,15 +452,19 @@ export class EvmContract extends Contract {
gas: 100000000, gas: 100000000,
value: updateFee, value: updateFee,
}); });
return transactionObject.send({ const result = await transactionObject.send({
from: address, from: address,
value: updateFee, value: updateFee,
gas: gasEstiamte * GAS_ESTIMATE_MULTIPLIER, gas: gasEstiamte * GAS_ESTIMATE_MULTIPLIER,
gasPrice: await this.chain.getGasPrice(), gasPrice: await this.chain.getGasPrice(),
}); });
return { id: result.transactionHash, info: result };
} }
async executeGovernanceInstruction(senderPrivateKey: string, vaa: Buffer) { async executeGovernanceInstruction(
senderPrivateKey: PrivateKey,
vaa: Buffer
) {
const web3 = new Web3(this.chain.getRpcUrl()); const web3 = new Web3(this.chain.getRpcUrl());
const { address } = web3.eth.accounts.wallet.add(senderPrivateKey); const { address } = web3.eth.accounts.wallet.add(senderPrivateKey);
const pythContract = new web3.eth.Contract(EXTENDED_PYTH_ABI, this.address); const pythContract = new web3.eth.Contract(EXTENDED_PYTH_ABI, this.address);
@ -467,11 +475,12 @@ export class EvmContract extends Contract {
from: address, from: address,
gas: 100000000, gas: 100000000,
}); });
return transactionObject.send({ const result = await transactionObject.send({
from: address, from: address,
gas: gasEstiamte * GAS_ESTIMATE_MULTIPLIER, gas: gasEstiamte * GAS_ESTIMATE_MULTIPLIER,
gasPrice: await this.chain.getGasPrice(), gasPrice: await this.chain.getGasPrice(),
}); });
return { id: result.transactionHash, info: result };
} }
getChain(): EvmChain { getChain(): EvmChain {

View File

@ -9,7 +9,7 @@ import {
} from "@mysten/sui.js"; } from "@mysten/sui.js";
import { Chain, SuiChain } from "../chains"; import { Chain, SuiChain } from "../chains";
import { DataSource } from "xc_admin_common"; import { DataSource } from "xc_admin_common";
import { Contract } from "../base"; import { Contract, PrivateKey, TxResult } from "../base";
import { SuiPythClient } from "@pythnetwork/pyth-sui-js"; import { SuiPythClient } from "@pythnetwork/pyth-sui-js";
export class SuiContract extends Contract { export class SuiContract extends Contract {
@ -31,7 +31,10 @@ export class SuiContract extends Contract {
super(); super();
} }
static fromJson(chain: Chain, parsed: any): SuiContract { static fromJson(
chain: Chain,
parsed: { type: string; stateId: string; wormholeStateId: string }
): SuiContract {
if (parsed.type !== SuiContract.type) throw new Error("Invalid type"); if (parsed.type !== SuiContract.type) throw new Error("Invalid type");
if (!(chain instanceof SuiChain)) if (!(chain instanceof SuiChain))
throw new Error(`Wrong chain type ${chain}`); throw new Error(`Wrong chain type ${chain}`);
@ -81,7 +84,7 @@ export class SuiContract extends Contract {
*/ */
async getPriceTableId(): Promise<ObjectId> { async getPriceTableId(): Promise<ObjectId> {
const provider = this.getProvider(); const provider = this.getProvider();
let result = await provider.getDynamicFieldObject({ const result = await provider.getDynamicFieldObject({
parentId: this.stateId, parentId: this.stateId,
name: { name: {
type: "vector<u8>", type: "vector<u8>",
@ -94,7 +97,15 @@ export class SuiContract extends Contract {
return result.data.objectId; return result.data.objectId;
} }
private async parsePrice(priceInfo: any) { private async parsePrice(priceInfo: {
type: string;
fields: {
expo: { fields: { magnitude: string; negative: boolean } };
price: { fields: { magnitude: string; negative: boolean } };
conf: string;
timestamp: string;
};
}) {
const packageId = await this.getPythPackageId(); const packageId = await this.getPythPackageId();
const expectedType = `${packageId}::price::Price`; const expectedType = `${packageId}::price::Price`;
if (priceInfo.type !== expectedType) { if (priceInfo.type !== expectedType) {
@ -117,7 +128,7 @@ export class SuiContract extends Contract {
async getPriceFeedObjectId(feedId: string): Promise<ObjectId | undefined> { async getPriceFeedObjectId(feedId: string): Promise<ObjectId | undefined> {
const tableId = await this.getPriceTableId(); const tableId = await this.getPriceTableId();
const provider = this.getProvider(); const provider = this.getProvider();
let result = await provider.getDynamicFieldObject({ const result = await provider.getDynamicFieldObject({
parentId: tableId, parentId: tableId,
name: { name: {
type: `${await this.getPythPackageId()}::price_identifier::PriceIdentifier`, type: `${await this.getPythPackageId()}::price_identifier::PriceIdentifier`,
@ -137,9 +148,9 @@ export class SuiContract extends Contract {
async getPriceFeed(feedId: string) { async getPriceFeed(feedId: string) {
const provider = this.getProvider(); const provider = this.getProvider();
let priceInfoObjectId = await this.getPriceFeedObjectId(feedId); const priceInfoObjectId = await this.getPriceFeedObjectId(feedId);
if (!priceInfoObjectId) return undefined; if (!priceInfoObjectId) return undefined;
let priceInfo = await provider.getObject({ const priceInfo = await provider.getObject({
id: priceInfoObjectId, id: priceInfoObjectId,
options: { showContent: true }, options: { showContent: true },
}); });
@ -173,7 +184,7 @@ export class SuiContract extends Contract {
async executeMigrateInstruction(vaa: Buffer, keypair: Ed25519Keypair) { async executeMigrateInstruction(vaa: Buffer, keypair: Ed25519Keypair) {
const tx = new TransactionBlock(); const tx = new TransactionBlock();
const packageId = await this.getPythPackageId(); const packageId = await this.getPythPackageId();
let verificationReceipt = await this.getVaaVerificationReceipt( const verificationReceipt = await this.getVaaVerificationReceipt(
tx, tx,
packageId, packageId,
vaa vaa
@ -187,7 +198,7 @@ export class SuiContract extends Contract {
return this.executeTransaction(tx, keypair); return this.executeTransaction(tx, keypair);
} }
async executeUpdatePriceFeed(senderPrivateKey: string, vaas: Buffer[]) { async executeUpdatePriceFeed(): Promise<TxResult> {
// We need the feed ids to be able to execute the transaction // We need the feed ids to be able to execute the transaction
// it may be possible to get them from the VAA but in batch transactions, // it may be possible to get them from the VAA but in batch transactions,
// it is also possible to hava fewer feeds that user wants to update compared to // it is also possible to hava fewer feeds that user wants to update compared to
@ -207,17 +218,20 @@ export class SuiContract extends Contract {
senderPrivateKey: string, senderPrivateKey: string,
vaas: Buffer[], vaas: Buffer[],
feedIds: string[] feedIds: string[]
) { ): Promise<TxResult> {
const tx = new TransactionBlock(); const tx = new TransactionBlock();
const client = this.getSdkClient(); const client = this.getSdkClient();
await client.updatePriceFeeds(tx, vaas, feedIds); await client.updatePriceFeeds(tx, vaas, feedIds);
const keypair = Ed25519Keypair.fromSecretKey( const keypair = Ed25519Keypair.fromSecretKey(
Buffer.from(senderPrivateKey, "hex") Buffer.from(senderPrivateKey, "hex")
); );
let result = await this.executeTransaction(tx, keypair); const result = await this.executeTransaction(tx, keypair);
return result.digest; return { id: result.digest, info: result };
} }
async executeCreatePriceFeed(senderPrivateKey: string, vaas: Buffer[]) { async executeCreatePriceFeed(
senderPrivateKey: string,
vaas: Buffer[]
): Promise<TxResult> {
const tx = new TransactionBlock(); const tx = new TransactionBlock();
const client = this.getSdkClient(); const client = this.getSdkClient();
await client.createPriceFeed(tx, vaas); await client.createPriceFeed(tx, vaas);
@ -225,17 +239,20 @@ export class SuiContract extends Contract {
Buffer.from(senderPrivateKey, "hex") Buffer.from(senderPrivateKey, "hex")
); );
let result = await this.executeTransaction(tx, keypair); const result = await this.executeTransaction(tx, keypair);
return result.digest; return { id: result.digest, info: result };
} }
async executeGovernanceInstruction(senderPrivateKey: string, vaa: Buffer) { async executeGovernanceInstruction(
senderPrivateKey: PrivateKey,
vaa: Buffer
): Promise<TxResult> {
const keypair = Ed25519Keypair.fromSecretKey( const keypair = Ed25519Keypair.fromSecretKey(
Buffer.from(senderPrivateKey, "hex") Buffer.from(senderPrivateKey, "hex")
); );
const tx = new TransactionBlock(); const tx = new TransactionBlock();
const packageId = await this.getPythPackageId(); const packageId = await this.getPythPackageId();
let verificationReceipt = await this.getVaaVerificationReceipt( const verificationReceipt = await this.getVaaVerificationReceipt(
tx, tx,
packageId, packageId,
vaa vaa
@ -246,7 +263,8 @@ export class SuiContract extends Contract {
arguments: [tx.object(this.stateId), verificationReceipt], arguments: [tx.object(this.stateId), verificationReceipt],
}); });
return this.executeTransaction(tx, keypair); const result = await this.executeTransaction(tx, keypair);
return { id: result.digest, info: result };
} }
async executeUpgradeInstruction( async executeUpgradeInstruction(
@ -257,7 +275,7 @@ export class SuiContract extends Contract {
) { ) {
const tx = new TransactionBlock(); const tx = new TransactionBlock();
const packageId = await this.getPythPackageId(); const packageId = await this.getPythPackageId();
let verificationReceipt = await this.getVaaVerificationReceipt( const verificationReceipt = await this.getVaaVerificationReceipt(
tx, tx,
packageId, packageId,
vaa vaa
@ -279,7 +297,8 @@ export class SuiContract extends Contract {
target: `${packageId}::contract_upgrade::commit_upgrade`, target: `${packageId}::contract_upgrade::commit_upgrade`,
arguments: [tx.object(this.stateId), upgradeReceipt], arguments: [tx.object(this.stateId), upgradeReceipt],
}); });
return this.executeTransaction(tx, keypair); const result = await this.executeTransaction(tx, keypair);
return { id: result.digest, info: result };
} }
/** /**
@ -297,7 +316,7 @@ export class SuiContract extends Contract {
) { ) {
const wormholePackageId = await this.getWormholePackageId(); const wormholePackageId = await this.getWormholePackageId();
let [verifiedVAA] = tx.moveCall({ const [verifiedVAA] = tx.moveCall({
target: `${wormholePackageId}::vaa::parse_and_verify`, target: `${wormholePackageId}::vaa::parse_and_verify`,
arguments: [ arguments: [
tx.object(this.wormholeStateId), tx.object(this.wormholeStateId),
@ -306,7 +325,7 @@ export class SuiContract extends Contract {
], ],
}); });
let [verificationReceipt] = tx.moveCall({ const [verificationReceipt] = tx.moveCall({
target: `${packageId}::governance::verify_vaa`, target: `${packageId}::governance::verify_vaa`,
arguments: [tx.object(this.stateId), verifiedVAA], arguments: [tx.object(this.stateId), verifiedVAA],
}); });
@ -325,7 +344,7 @@ export class SuiContract extends Contract {
keypair: Ed25519Keypair keypair: Ed25519Keypair
) { ) {
const provider = this.getProvider(); const provider = this.getProvider();
let txBlock = { const txBlock = {
transactionBlock: tx, transactionBlock: tx,
options: { options: {
showEffects: true, showEffects: true,
@ -333,7 +352,7 @@ export class SuiContract extends Contract {
}, },
}; };
const wallet = new RawSigner(keypair, provider); const wallet = new RawSigner(keypair, provider);
let gasCost = await wallet.getGasCostEstimation(txBlock); const gasCost = await wallet.getGasCostEstimation(txBlock);
tx.setGasBudget(gasCost * BigInt(2)); tx.setGasBudget(gasCost * BigInt(2));
return wallet.signAndExecuteTransactionBlock(txBlock); return wallet.signAndExecuteTransactionBlock(txBlock);
} }
@ -345,7 +364,7 @@ export class SuiContract extends Contract {
async getDataSources(): Promise<DataSource[]> { async getDataSources(): Promise<DataSource[]> {
const provider = this.getProvider(); const provider = this.getProvider();
let result = await provider.getDynamicFieldObject({ const result = await provider.getDynamicFieldObject({
parentId: this.stateId, parentId: this.stateId,
name: { name: {
type: "vector<u8>", type: "vector<u8>",
@ -361,7 +380,14 @@ export class SuiContract extends Contract {
throw new Error("Data Sources type mismatch"); throw new Error("Data Sources type mismatch");
} }
return result.data.content.fields.value.fields.keys.map( return result.data.content.fields.value.fields.keys.map(
({ fields }: any) => { ({
fields,
}: {
fields: {
emitter_address: { fields: { value: { fields: { data: string } } } };
emitter_chain: string;
};
}) => {
return { return {
emitterChain: Number(fields.emitter_chain), emitterChain: Number(fields.emitter_chain),
emitterAddress: Buffer.from( emitterAddress: Buffer.from(

View File

@ -1,3 +1,5 @@
import { PrivateKey, TxResult } from "../base";
export abstract class WormholeContract { export abstract class WormholeContract {
abstract getCurrentGuardianSetIndex(): Promise<number>; abstract getCurrentGuardianSetIndex(): Promise<number>;
@ -12,15 +14,15 @@ export abstract class WormholeContract {
* @param vaa * @param vaa
*/ */
abstract upgradeGuardianSets( abstract upgradeGuardianSets(
senderPrivateKey: string, senderPrivateKey: PrivateKey,
vaa: Buffer vaa: Buffer
): Promise<any>; ): Promise<TxResult>;
/** /**
* Upgrades the guardian set of this contract with the 3 pre-configured VAAs for mainnet assuming this is a mainnet contract * Upgrades the guardian set of this contract with the 3 pre-configured VAAs for mainnet assuming this is a mainnet contract
* @param senderPrivateKey * @param senderPrivateKey
*/ */
async syncMainnetGuardianSets(senderPrivateKey: string) { async syncMainnetGuardianSets(senderPrivateKey: PrivateKey) {
const MAINNET_UPGRADE_VAAS = [ const MAINNET_UPGRADE_VAAS = [
"010000000001007ac31b282c2aeeeb37f3385ee0de5f8e421d30b9e5ae8ba3d4375c1c77a86e77159bb697d9c456d6f8c02d22a94b1279b65b0d6a9957e7d3857423845ac758e300610ac1d2000000030001000000000000000000000000000000000000000000000000000000000000000400000000000005390000000000000000000000000000000000000000000000000000000000436f7265020000000000011358cc3ae5c097b213ce3c81979e1b9f9570746aa5ff6cb952589bde862c25ef4392132fb9d4a42157114de8460193bdf3a2fcf81f86a09765f4762fd1107a0086b32d7a0977926a205131d8731d39cbeb8c82b2fd82faed2711d59af0f2499d16e726f6b211b39756c042441be6d8650b69b54ebe715e234354ce5b4d348fb74b958e8966e2ec3dbd4958a7cdeb5f7389fa26941519f0863349c223b73a6ddee774a3bf913953d695260d88bc1aa25a4eee363ef0000ac0076727b35fbea2dac28fee5ccb0fea768eaf45ced136b9d9e24903464ae889f5c8a723fc14f93124b7c738843cbb89e864c862c38cddcccf95d2cc37a4dc036a8d232b48f62cdd4731412f4890da798f6896a3331f64b48c12d1d57fd9cbe7081171aa1be1d36cafe3867910f99c09e347899c19c38192b6e7387ccd768277c17dab1b7a5027c0b3cf178e21ad2e77ae06711549cfbb1f9c7a9d8096e85e1487f35515d02a92753504a8d75471b9f49edb6fbebc898f403e4773e95feb15e80c9a99c8348d", "010000000001007ac31b282c2aeeeb37f3385ee0de5f8e421d30b9e5ae8ba3d4375c1c77a86e77159bb697d9c456d6f8c02d22a94b1279b65b0d6a9957e7d3857423845ac758e300610ac1d2000000030001000000000000000000000000000000000000000000000000000000000000000400000000000005390000000000000000000000000000000000000000000000000000000000436f7265020000000000011358cc3ae5c097b213ce3c81979e1b9f9570746aa5ff6cb952589bde862c25ef4392132fb9d4a42157114de8460193bdf3a2fcf81f86a09765f4762fd1107a0086b32d7a0977926a205131d8731d39cbeb8c82b2fd82faed2711d59af0f2499d16e726f6b211b39756c042441be6d8650b69b54ebe715e234354ce5b4d348fb74b958e8966e2ec3dbd4958a7cdeb5f7389fa26941519f0863349c223b73a6ddee774a3bf913953d695260d88bc1aa25a4eee363ef0000ac0076727b35fbea2dac28fee5ccb0fea768eaf45ced136b9d9e24903464ae889f5c8a723fc14f93124b7c738843cbb89e864c862c38cddcccf95d2cc37a4dc036a8d232b48f62cdd4731412f4890da798f6896a3331f64b48c12d1d57fd9cbe7081171aa1be1d36cafe3867910f99c09e347899c19c38192b6e7387ccd768277c17dab1b7a5027c0b3cf178e21ad2e77ae06711549cfbb1f9c7a9d8096e85e1487f35515d02a92753504a8d75471b9f49edb6fbebc898f403e4773e95feb15e80c9a99c8348d",
"01000000010d0012e6b39c6da90c5dfd3c228edbb78c7a4c97c488ff8a346d161a91db067e51d638c17216f368aa9bdf4836b8645a98018ca67d2fec87d769cabfdf2406bf790a0002ef42b288091a670ef3556596f4f47323717882881eaf38e03345078d07a156f312b785b64dae6e9a87e3d32872f59cb1931f728cecf511762981baf48303668f0103cef2616b84c4e511ff03329e0853f1bd7ee9ac5ba71d70a4d76108bddf94f69c2a8a84e4ee94065e8003c334e899184943634e12043d0dda78d93996da073d190104e76d166b9dac98f602107cc4b44ac82868faf00b63df7d24f177aa391e050902413b71046434e67c770b19aecdf7fce1d1435ea0be7262e3e4c18f50ddc8175c0105d9450e8216d741e0206a50f93b750a47e0a258b80eb8fed1314cc300b3d905092de25cd36d366097b7103ae2d184121329ba3aa2d7c6cc53273f11af14798110010687477c8deec89d36a23e7948feb074df95362fc8dcbd8ae910ac556a1dee1e755c56b9db5d710c940938ed79bc1895a3646523a58bc55f475a23435a373ecfdd0107fb06734864f79def4e192497362513171530daea81f07fbb9f698afe7e66c6d44db21323144f2657d4a5386a954bb94eef9f64148c33aef6e477eafa2c5c984c01088769e82216310d1827d9bd48645ec23e90de4ef8a8de99e2d351d1df318608566248d80cdc83bdcac382b3c30c670352be87f9069aab5037d0b747208eae9c650109e9796497ff9106d0d1c62e184d83716282870cef61a1ee13d6fc485b521adcce255c96f7d1bca8d8e7e7d454b65783a830bddc9d94092091a268d311ecd84c26010c468c9fb6d41026841ff9f8d7368fa309d4dbea3ea4bbd2feccf94a92cc8a20a226338a8e2126cd16f70eaf15b4fc9be2c3fa19def14e071956a605e9d1ac4162010e23fcb6bd445b7c25afb722250c1acbc061ed964ba9de1326609ae012acdfb96942b2a102a2de99ab96327859a34a2b49a767dbdb62e0a1fb26af60fe44fd496a00106bb0bac77ac68b347645f2fb1ad789ea9bd76fb9b2324f25ae06f97e65246f142df717f662e73948317182c62ce87d79c73def0dba12e5242dfc038382812cfe00126da03c5e56cb15aeeceadc1e17a45753ab4dc0ec7bf6a75ca03143ed4a294f6f61bc3f478a457833e43084ecd7c985bf2f55a55f168aac0e030fc49e845e497101626e9d9a5d9e343f00010000000000000000000000000000000000000000000000000000000000000004c1759167c43f501c2000000000000000000000000000000000000000000000000000000000436f7265020000000000021358cc3ae5c097b213ce3c81979e1b9f9570746aa5ff6cb952589bde862c25ef4392132fb9d4a42157114de8460193bdf3a2fcf81f86a09765f4762fd1107a0086b32d7a0977926a205131d8731d39cbeb8c82b2fd82faed2711d59af0f2499d16e726f6b211b39756c042441be6d8650b69b54ebe715e234354ce5b4d348fb74b958e8966e2ec3dbd4958a7cd66b9590e1c41e0b226937bf9217d1d67fd4e91f574a3bf913953d695260d88bc1aa25a4eee363ef0000ac0076727b35fbea2dac28fee5ccb0fea768eaf45ced136b9d9e24903464ae889f5c8a723fc14f93124b7c738843cbb89e864c862c38cddcccf95d2cc37a4dc036a8d232b48f62cdd4731412f4890da798f6896a3331f64b48c12d1d57fd9cbe7081171aa1be1d36cafe3867910f99c09e347899c19c38192b6e7387ccd768277c17dab1b7a5027c0b3cf178e21ad2e77ae06711549cfbb1f9c7a9d8096e85e1487f35515d02a92753504a8d75471b9f49edb6fbebc898f403e4773e95feb15e80c9a99c8348d", "01000000010d0012e6b39c6da90c5dfd3c228edbb78c7a4c97c488ff8a346d161a91db067e51d638c17216f368aa9bdf4836b8645a98018ca67d2fec87d769cabfdf2406bf790a0002ef42b288091a670ef3556596f4f47323717882881eaf38e03345078d07a156f312b785b64dae6e9a87e3d32872f59cb1931f728cecf511762981baf48303668f0103cef2616b84c4e511ff03329e0853f1bd7ee9ac5ba71d70a4d76108bddf94f69c2a8a84e4ee94065e8003c334e899184943634e12043d0dda78d93996da073d190104e76d166b9dac98f602107cc4b44ac82868faf00b63df7d24f177aa391e050902413b71046434e67c770b19aecdf7fce1d1435ea0be7262e3e4c18f50ddc8175c0105d9450e8216d741e0206a50f93b750a47e0a258b80eb8fed1314cc300b3d905092de25cd36d366097b7103ae2d184121329ba3aa2d7c6cc53273f11af14798110010687477c8deec89d36a23e7948feb074df95362fc8dcbd8ae910ac556a1dee1e755c56b9db5d710c940938ed79bc1895a3646523a58bc55f475a23435a373ecfdd0107fb06734864f79def4e192497362513171530daea81f07fbb9f698afe7e66c6d44db21323144f2657d4a5386a954bb94eef9f64148c33aef6e477eafa2c5c984c01088769e82216310d1827d9bd48645ec23e90de4ef8a8de99e2d351d1df318608566248d80cdc83bdcac382b3c30c670352be87f9069aab5037d0b747208eae9c650109e9796497ff9106d0d1c62e184d83716282870cef61a1ee13d6fc485b521adcce255c96f7d1bca8d8e7e7d454b65783a830bddc9d94092091a268d311ecd84c26010c468c9fb6d41026841ff9f8d7368fa309d4dbea3ea4bbd2feccf94a92cc8a20a226338a8e2126cd16f70eaf15b4fc9be2c3fa19def14e071956a605e9d1ac4162010e23fcb6bd445b7c25afb722250c1acbc061ed964ba9de1326609ae012acdfb96942b2a102a2de99ab96327859a34a2b49a767dbdb62e0a1fb26af60fe44fd496a00106bb0bac77ac68b347645f2fb1ad789ea9bd76fb9b2324f25ae06f97e65246f142df717f662e73948317182c62ce87d79c73def0dba12e5242dfc038382812cfe00126da03c5e56cb15aeeceadc1e17a45753ab4dc0ec7bf6a75ca03143ed4a294f6f61bc3f478a457833e43084ecd7c985bf2f55a55f168aac0e030fc49e845e497101626e9d9a5d9e343f00010000000000000000000000000000000000000000000000000000000000000004c1759167c43f501c2000000000000000000000000000000000000000000000000000000000436f7265020000000000021358cc3ae5c097b213ce3c81979e1b9f9570746aa5ff6cb952589bde862c25ef4392132fb9d4a42157114de8460193bdf3a2fcf81f86a09765f4762fd1107a0086b32d7a0977926a205131d8731d39cbeb8c82b2fd82faed2711d59af0f2499d16e726f6b211b39756c042441be6d8650b69b54ebe715e234354ce5b4d348fb74b958e8966e2ec3dbd4958a7cd66b9590e1c41e0b226937bf9217d1d67fd4e91f574a3bf913953d695260d88bc1aa25a4eee363ef0000ac0076727b35fbea2dac28fee5ccb0fea768eaf45ced136b9d9e24903464ae889f5c8a723fc14f93124b7c738843cbb89e864c862c38cddcccf95d2cc37a4dc036a8d232b48f62cdd4731412f4890da798f6896a3331f64b48c12d1d57fd9cbe7081171aa1be1d36cafe3867910f99c09e347899c19c38192b6e7387ccd768277c17dab1b7a5027c0b3cf178e21ad2e77ae06711549cfbb1f9c7a9d8096e85e1487f35515d02a92753504a8d75471b9f49edb6fbebc898f403e4773e95feb15e80c9a99c8348d",

View File

@ -1,13 +1,14 @@
import { parseVaa } from "@certusone/wormhole-sdk"; import { parseVaa } from "@certusone/wormhole-sdk";
import { decodeGovernancePayload } from "xc_admin_common"; import { decodeGovernancePayload } from "xc_admin_common";
import { DefaultStore } from "./store"; import { DefaultStore } from "./store";
import { PrivateKey } from "./base";
/** /**
* A general executor that tries to find any contract that can execute a given VAA and executes it * A general executor that tries to find any contract that can execute a given VAA and executes it
* @param senderPrivateKey the private key to execute the governance instruction with * @param senderPrivateKey the private key to execute the governance instruction with
* @param vaa the VAA to execute * @param vaa the VAA to execute
*/ */
export async function executeVaa(senderPrivateKey: string, vaa: Buffer) { export async function executeVaa(senderPrivateKey: PrivateKey, vaa: Buffer) {
const parsedVaa = parseVaa(vaa); const parsedVaa = parseVaa(vaa);
const action = decodeGovernancePayload(parsedVaa.payload); const action = decodeGovernancePayload(parsedVaa.payload);
if (!action) return; //TODO: handle other actions if (!action) return; //TODO: handle other actions

View File

@ -31,7 +31,7 @@ import {
deriveFeeCollectorKey, deriveFeeCollectorKey,
deriveWormholeBridgeDataKey, deriveWormholeBridgeDataKey,
} from "@certusone/wormhole-sdk/lib/cjs/solana/wormhole"; } from "@certusone/wormhole-sdk/lib/cjs/solana/wormhole";
import { Storable } from "./base"; import { KeyValueConfig, Storable } from "./base";
class InvalidTransactionError extends Error { class InvalidTransactionError extends Error {
constructor(message: string) { constructor(message: string) {
@ -75,8 +75,8 @@ export class SubmittedWormholeMessage {
) )
); );
const wormholeAddress = const wormholeAddress = WORMHOLE_ADDRESS[cluster];
WORMHOLE_ADDRESS[cluster as keyof typeof WORMHOLE_ADDRESS]!; if (!wormholeAddress) throw new Error(`Invalid cluster ${cluster}`);
let emitter: PublicKey | undefined = undefined; let emitter: PublicKey | undefined = undefined;
let allInstructions: (ParsedInstruction | PartiallyDecodedInstruction)[] = let allInstructions: (ParsedInstruction | PartiallyDecodedInstruction)[] =
@ -103,10 +103,10 @@ export class SubmittedWormholeMessage {
* before giving up and throwing an error * before giving up and throwing an error
* @param waitingSeconds how long to wait before giving up * @param waitingSeconds how long to wait before giving up
*/ */
async fetchVaa(waitingSeconds: number = 1): Promise<Buffer> { async fetchVaa(waitingSeconds = 1): Promise<Buffer> {
let rpcUrl = WORMHOLE_API_ENDPOINT[this.cluster]; const rpcUrl = WORMHOLE_API_ENDPOINT[this.cluster];
let startTime = Date.now(); const startTime = Date.now();
while (Date.now() - startTime < waitingSeconds * 1000) { while (Date.now() - startTime < waitingSeconds * 1000) {
const response = await fetch( const response = await fetch(
`${rpcUrl}/v1/signed_vaa/1/${this.emitter.toBuffer().toString("hex")}/${ `${rpcUrl}/v1/signed_vaa/1/${this.emitter.toBuffer().toString("hex")}/${
@ -158,12 +158,12 @@ export class WormholeEmitter {
preflightCommitment: "confirmed", preflightCommitment: "confirmed",
} }
); );
let wormholeAddress = const wormholeAddress = WORMHOLE_ADDRESS[this.cluster];
WORMHOLE_ADDRESS[this.cluster as keyof typeof WORMHOLE_ADDRESS]!; if (!wormholeAddress) throw new Error(`Invalid cluster ${this.cluster}`);
let kp = Keypair.generate(); const kp = Keypair.generate();
let feeCollector = deriveFeeCollectorKey(wormholeAddress); const feeCollector = deriveFeeCollectorKey(wormholeAddress);
let emitter = this.wallet.publicKey; const emitter = this.wallet.publicKey;
let accounts = { const accounts = {
bridge: deriveWormholeBridgeDataKey(wormholeAddress), bridge: deriveWormholeBridgeDataKey(wormholeAddress),
message: kp.publicKey, message: kp.publicKey,
emitter: emitter, emitter: emitter,
@ -230,7 +230,7 @@ export class WormholeMultiSigTransaction {
this.cluster this.cluster
) )
); );
} catch (e: any) { } catch (e) {
if (!(e instanceof InvalidTransactionError)) throw e; if (!(e instanceof InvalidTransactionError)) throw e;
} }
} }
@ -240,7 +240,7 @@ export class WormholeMultiSigTransaction {
} }
export class Vault extends Storable { export class Vault extends Storable {
static type: string = "vault"; static type = "vault";
key: PublicKey; key: PublicKey;
squad?: SquadsMesh; squad?: SquadsMesh;
cluster: PythCluster; cluster: PythCluster;
@ -255,7 +255,11 @@ export class Vault extends Storable {
return Vault.type; return Vault.type;
} }
static fromJson(parsed: any): Vault { static fromJson(parsed: {
type: string;
key: string;
cluster: string;
}): Vault {
if (parsed.type !== Vault.type) throw new Error("Invalid type"); if (parsed.type !== Vault.type) throw new Error("Invalid type");
return new Vault(parsed.key, parsed.cluster); return new Vault(parsed.key, parsed.cluster);
} }
@ -264,7 +268,7 @@ export class Vault extends Storable {
return `${this.cluster}_${this.key.toString()}`; return `${this.cluster}_${this.key.toString()}`;
} }
toJson(): any { toJson(): KeyValueConfig {
return { return {
key: this.key.toString(), key: this.key.toString(),
cluster: this.cluster, cluster: this.cluster,

View File

@ -12,5 +12,6 @@ repl.evalCode(
"import { WormholeEvmContract, EvmContract } from './src/contracts/evm';" + "import { WormholeEvmContract, EvmContract } from './src/contracts/evm';" +
"import { AptosContract } from './src/contracts/aptos';" + "import { AptosContract } from './src/contracts/aptos';" +
"import { DefaultStore } from './src/store';" + "import { DefaultStore } from './src/store';" +
"import { toPrivateKey } from './src/base';" +
"DefaultStore" "DefaultStore"
); );

View File

@ -53,7 +53,7 @@ export class Store {
} }
loadAllChains() { loadAllChains() {
let allChainClasses = { const allChainClasses = {
[CosmWasmChain.type]: CosmWasmChain, [CosmWasmChain.type]: CosmWasmChain,
[SuiChain.type]: SuiChain, [SuiChain.type]: SuiChain,
[EvmChain.type]: EvmChain, [EvmChain.type]: EvmChain,
@ -61,10 +61,10 @@ export class Store {
}; };
this.getYamlFiles(`${this.path}/chains/`).forEach((yamlFile) => { this.getYamlFiles(`${this.path}/chains/`).forEach((yamlFile) => {
let parsedArray = parse(readFileSync(yamlFile, "utf-8")); const parsedArray = parse(readFileSync(yamlFile, "utf-8"));
for (const parsed of parsedArray) { for (const parsed of parsedArray) {
if (allChainClasses[parsed.type] === undefined) return; if (allChainClasses[parsed.type] === undefined) return;
let chain = allChainClasses[parsed.type].fromJson(parsed); const chain = allChainClasses[parsed.type].fromJson(parsed);
if (this.chains[chain.getId()]) if (this.chains[chain.getId()])
throw new Error(`Multiple chains with id ${chain.getId()} found`); throw new Error(`Multiple chains with id ${chain.getId()} found`);
this.chains[chain.getId()] = chain; this.chains[chain.getId()] = chain;
@ -73,7 +73,7 @@ export class Store {
} }
saveAllContracts() { saveAllContracts() {
let contractsByType: Record<string, Contract[]> = {}; const contractsByType: Record<string, Contract[]> = {};
for (const contract of Object.values(this.contracts)) { for (const contract of Object.values(this.contracts)) {
if (!contractsByType[contract.getType()]) { if (!contractsByType[contract.getType()]) {
contractsByType[contract.getType()] = []; contractsByType[contract.getType()] = [];
@ -89,7 +89,7 @@ export class Store {
} }
saveAllChains() { saveAllChains() {
let chainsByType: Record<string, Chain[]> = {}; const chainsByType: Record<string, Chain[]> = {};
for (const chain of Object.values(this.chains)) { for (const chain of Object.values(this.chains)) {
if (!chainsByType[chain.getType()]) { if (!chainsByType[chain.getType()]) {
chainsByType[chain.getType()] = []; chainsByType[chain.getType()] = [];
@ -105,20 +105,20 @@ export class Store {
} }
loadAllContracts() { loadAllContracts() {
let allContractClasses = { const allContractClasses = {
[CosmWasmContract.type]: CosmWasmContract, [CosmWasmContract.type]: CosmWasmContract,
[SuiContract.type]: SuiContract, [SuiContract.type]: SuiContract,
[EvmContract.type]: EvmContract, [EvmContract.type]: EvmContract,
[AptosContract.type]: AptosContract, [AptosContract.type]: AptosContract,
}; };
this.getYamlFiles(`${this.path}/contracts/`).forEach((yamlFile) => { this.getYamlFiles(`${this.path}/contracts/`).forEach((yamlFile) => {
let parsedArray = parse(readFileSync(yamlFile, "utf-8")); const parsedArray = parse(readFileSync(yamlFile, "utf-8"));
for (const parsed of parsedArray) { for (const parsed of parsedArray) {
if (allContractClasses[parsed.type] === undefined) return; if (allContractClasses[parsed.type] === undefined) return;
if (!this.chains[parsed.chain]) if (!this.chains[parsed.chain])
throw new Error(`Chain ${parsed.chain} not found`); throw new Error(`Chain ${parsed.chain} not found`);
const chain = this.chains[parsed.chain]; const chain = this.chains[parsed.chain];
let chainContract = allContractClasses[parsed.type].fromJson( const chainContract = allContractClasses[parsed.type].fromJson(
chain, chain,
parsed parsed
); );
@ -133,7 +133,7 @@ export class Store {
loadAllVaults() { loadAllVaults() {
this.getYamlFiles(`${this.path}/vaults/`).forEach((yamlFile) => { this.getYamlFiles(`${this.path}/vaults/`).forEach((yamlFile) => {
let parsedArray = parse(readFileSync(yamlFile, "utf-8")); const parsedArray = parse(readFileSync(yamlFile, "utf-8"));
for (const parsed of parsedArray) { for (const parsed of parsedArray) {
if (parsed.type !== Vault.type) return; if (parsed.type !== Vault.type) return;

View File

@ -18,7 +18,7 @@ export function getChainIdsForStableDeployment(): ChainId[] {
]; ];
} }
export function getChainIdsForEdgeDeployment(): ChainId[] { export function getChainIdsForBetaDeployment(): ChainId[] {
return [ return [
ChainId.INJECTIVE_TESTNET, ChainId.INJECTIVE_TESTNET,
ChainId.OSMOSIS_TESTNET_5, ChainId.OSMOSIS_TESTNET_5,
@ -28,7 +28,7 @@ export function getChainIdsForEdgeDeployment(): ChainId[] {
]; ];
} }
export type DeploymentType = "stable" | "edge"; export type DeploymentType = "stable" | "beta";
// We have released the compile contacts on github. If a chain needs some specific // We have released the compile contacts on github. If a chain needs some specific
// feature in a contract, a version of the contract with that specific features is // feature in a contract, a version of the contract with that specific features is

View File

@ -12,7 +12,7 @@ import { Pipeline } from "./pipeline";
import { import {
DeploymentType, DeploymentType,
WORMHOLE_CONTRACT_VERSION, WORMHOLE_CONTRACT_VERSION,
getChainIdsForEdgeDeployment, getChainIdsForBetaDeployment,
getChainIdsForStableDeployment, getChainIdsForStableDeployment,
getContractBytesDict, getContractBytesDict,
getPythInstantiateFileName, getPythInstantiateFileName,
@ -35,7 +35,7 @@ const argv = yargs(hideBin(process.argv))
.option("deploy", { .option("deploy", {
type: "string", type: "string",
desc: "Execute this script for the given networks.", desc: "Execute this script for the given networks.",
choices: ["edge", "stable"], choices: ["beta", "stable"],
demandOption: "Please provide the deployment type", demandOption: "Please provide the deployment type",
}) })
.help() .help()
@ -48,7 +48,7 @@ async function run() {
if (argv.deploy === "stable") { if (argv.deploy === "stable") {
chainIds = getChainIdsForStableDeployment(); chainIds = getChainIdsForStableDeployment();
} else { } else {
chainIds = getChainIdsForEdgeDeployment(); chainIds = getChainIdsForBetaDeployment();
} }
// get the wasm code from github // get the wasm code from github

View File

@ -8,7 +8,7 @@ import { Pipeline } from "./pipeline";
import { PythWrapperExecutor, PythWrapperQuerier } from "./pyth-wrapper"; import { PythWrapperExecutor, PythWrapperQuerier } from "./pyth-wrapper";
import { import {
DeploymentType, DeploymentType,
getChainIdsForEdgeDeployment, getChainIdsForBetaDeployment,
getChainIdsForStableDeployment, getChainIdsForStableDeployment,
getPythContractAddress, getPythContractAddress,
getTestPythContractFileName, getTestPythContractFileName,
@ -29,7 +29,7 @@ const argv = yargs(hideBin(process.argv))
.option("deploy", { .option("deploy", {
type: "string", type: "string",
desc: "test the following deployment type.", desc: "test the following deployment type.",
choices: ["stable", "edge"], choices: ["stable", "beta"],
demandOption: "Please provide the deployment type", demandOption: "Please provide the deployment type",
}) })
.help() .help()
@ -42,7 +42,7 @@ async function run() {
if (argv.deploy === "stable") { if (argv.deploy === "stable") {
chainIds = getChainIdsForStableDeployment(); chainIds = getChainIdsForStableDeployment();
} else { } else {
chainIds = getChainIdsForEdgeDeployment(); chainIds = getChainIdsForBetaDeployment();
} }
for (let chainId of chainIds) { for (let chainId of chainIds) {

View File

@ -12,7 +12,7 @@ import {
import { Pipeline } from "./pipeline"; import { Pipeline } from "./pipeline";
import { import {
DeploymentType, DeploymentType,
getChainIdsForEdgeDeployment, getChainIdsForBetaDeployment,
getChainIdsForStableDeployment, getChainIdsForStableDeployment,
getWormholeFileName, getWormholeFileName,
hexToBase64, hexToBase64,
@ -33,7 +33,7 @@ const argv = yargs(hideBin(process.argv))
.option("deploy", { .option("deploy", {
type: "string", type: "string",
desc: "Execute this script for the given deployment type.", desc: "Execute this script for the given deployment type.",
choices: ["stable", "edge"], choices: ["stable", "beta"],
demandOption: "Please provide the deployment type", demandOption: "Please provide the deployment type",
}) })
.help() .help()
@ -60,7 +60,7 @@ async function run() {
if (argv.deploy === "stable") { if (argv.deploy === "stable") {
chainIds = getChainIdsForStableDeployment(); chainIds = getChainIdsForStableDeployment();
} else { } else {
chainIds = getChainIdsForEdgeDeployment(); chainIds = getChainIdsForBetaDeployment();
} }
for (let chainId of chainIds) { for (let chainId of chainIds) {

View File

@ -16,7 +16,7 @@ function convertChainId(number) {
function getDefaultConfig(chainName) { function getDefaultConfig(chainName) {
const chain = DefaultStore.chains[chainName]; const chain = DefaultStore.chains[chainName];
const { dataSources, governanceDataSource, wormholeConfig } = const { dataSources, governanceDataSource, wormholeConfig } =
getDefaultDeploymentConfig(chain.isMainnet() ? "stable" : "edge"); getDefaultDeploymentConfig(chain.isMainnet() ? "stable" : "beta");
const emitterChainIds = dataSources.map((dataSource) => const emitterChainIds = dataSources.map((dataSource) =>
convertChainId(dataSource.emitterChain) convertChainId(dataSource.emitterChain)

View File

@ -182,7 +182,7 @@ yargs(hideBin(process.argv))
provider provider
); );
const result = await publishPackage(wallet, argv.path); const result = await publishPackage(wallet, argv.path);
const deploymentType = chain.isMainnet() ? "stable" : "edge"; const deploymentType = chain.isMainnet() ? "stable" : "beta";
const config = getDefaultDeploymentConfig(deploymentType); const config = getDefaultDeploymentConfig(deploymentType);
await initPyth( await initPyth(
wallet, wallet,