| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347 |
- import { Chain, CosmWasmChain } from "../chains";
- import { readFileSync } from "fs";
- import {
- ContractInfoResponse,
- CosmwasmQuerier,
- Price,
- PythWrapperExecutor,
- PythWrapperQuerier,
- } from "@pythnetwork/cosmwasm-deploy-tools";
- import { Coin } from "@cosmjs/stargate";
- import { DataSource } from "xc_admin_common";
- import { CosmWasmClient } from "@cosmjs/cosmwasm-stargate";
- import {
- Contract,
- getDefaultDeploymentConfig,
- PrivateKey,
- TxResult,
- } from "../base";
- import { WormholeContract } from "./wormhole";
- /**
- * Variables here need to be snake case to match the on-chain contract configs
- */
- export interface WormholeSource {
- emitter: string;
- chain_id: number;
- }
- export interface DeploymentConfig {
- data_sources: WormholeSource[];
- governance_source: WormholeSource;
- wormhole_contract: string;
- governance_source_index: number;
- governance_sequence_number: number;
- chain_id: number;
- valid_time_period_secs: number;
- fee: { amount: string; denom: string };
- }
- export class WormholeCosmWasmContract extends WormholeContract {
- constructor(public chain: CosmWasmChain, public address: string) {
- super();
- }
- async getConfig() {
- const chainQuerier = await CosmwasmQuerier.connect(this.chain.endpoint);
- return (await chainQuerier.getAllContractState({
- contractAddr: this.address,
- })) as Record<string, string>;
- }
- async getCurrentGuardianSetIndex(): Promise<number> {
- const config = await this.getConfig();
- return JSON.parse(config["\x00\x06config"])["guardian_set_index"];
- }
- async getGuardianSet(): Promise<string[]> {
- const config = await this.getConfig();
- const guardianSetIndex = JSON.parse(config["\x00\x06config"])[
- "guardian_set_index"
- ];
- let key = "\x00\fguardian_set";
- //append guardianSetIndex as 4 bytes to key string
- key += Buffer.from(guardianSetIndex.toString(16).padStart(8, "0"), "hex");
- const guardianSet = JSON.parse(config[key])["addresses"];
- return guardianSet.map((entry: { bytes: string }) =>
- Buffer.from(entry.bytes, "base64").toString("hex")
- );
- }
- async upgradeGuardianSets(
- senderPrivateKey: PrivateKey,
- vaa: Buffer
- ): Promise<TxResult> {
- const executor = await this.chain.getExecutor(senderPrivateKey);
- const result = await executor.executeContract({
- contractAddr: this.address,
- msg: {
- submit_v_a_a: { vaa: vaa.toString("base64") },
- },
- });
- return { id: result.txHash, info: result };
- }
- }
- export class CosmWasmContract extends Contract {
- async getDataSources(): Promise<DataSource[]> {
- const config = await this.getConfig();
- return config.config_v1.data_sources.map(
- ({ emitter, chain_id }: WormholeSource) => {
- return {
- emitterChain: Number(chain_id),
- emitterAddress: Buffer.from(emitter, "base64").toString("hex"),
- };
- }
- );
- }
- async getGovernanceDataSource(): Promise<DataSource> {
- const config = await this.getConfig();
- const { emitter: emitterAddress, chain_id: chainId } =
- config.config_v1.governance_source;
- return {
- emitterChain: Number(chainId),
- emitterAddress: Buffer.from(emitterAddress, "base64").toString("hex"),
- };
- }
- static type = "CosmWasmContract";
- constructor(public chain: CosmWasmChain, public address: string) {
- super();
- }
- static fromJson(
- chain: Chain,
- parsed: { type: string; address: string }
- ): CosmWasmContract {
- if (parsed.type !== CosmWasmContract.type) throw new Error("Invalid type");
- if (!(chain instanceof CosmWasmChain))
- throw new Error(`Wrong chain type ${chain}`);
- return new CosmWasmContract(chain, parsed.address);
- }
- getType(): string {
- return CosmWasmContract.type;
- }
- /**
- * Stores the wasm code on the specified chain using the provided private key as the signer
- * You can find the wasm artifacts from the repo releases
- * @param chain chain to store the code on
- * @param privateKey private key to use for signing the transaction in hex format without 0x prefix
- * @param wasmPath path in your local filesystem to the wasm artifact
- */
- static async storeCode(
- chain: CosmWasmChain,
- privateKey: PrivateKey,
- wasmPath: string
- ) {
- const contractBytes = readFileSync(wasmPath);
- const executor = await chain.getExecutor(privateKey);
- return executor.storeCode({ contractBytes });
- }
- /**
- * Deploys a new contract to the specified chain using the uploaded wasm code codeId
- * @param chain chain to deploy to
- * @param codeId codeId of the uploaded wasm code. You can get this from the storeCode result
- * @param config deployment config for initializing the contract (data sources, governance source, etc)
- * @param privateKey private key to use for signing the transaction in hex format without 0x prefix
- */
- static async initialize(
- chain: CosmWasmChain,
- codeId: number,
- config: DeploymentConfig,
- privateKey: PrivateKey
- ): Promise<CosmWasmContract> {
- const executor = await chain.getExecutor(privateKey);
- const result = await executor.instantiateContract({
- codeId: codeId,
- instMsg: config,
- label: "pyth",
- });
- await executor.updateContractAdmin({
- newAdminAddr: result.contractAddr,
- contractAddr: result.contractAddr,
- });
- return new CosmWasmContract(chain, result.contractAddr);
- }
- getId(): string {
- return `${this.chain.getId()}_${this.address}`;
- }
- toJson() {
- return {
- chain: this.chain.getId(),
- address: this.address,
- type: CosmWasmContract.type,
- };
- }
- async getQuerier(): Promise<PythWrapperQuerier> {
- const chainQuerier = await CosmwasmQuerier.connect(this.chain.endpoint);
- const pythQuerier = new PythWrapperQuerier(chainQuerier);
- return pythQuerier;
- }
- async getCodeId(): Promise<number> {
- const result = await this.getWasmContractInfo();
- return result.codeId;
- }
- async getWasmContractInfo(): Promise<ContractInfoResponse> {
- const chainQuerier = await CosmwasmQuerier.connect(this.chain.endpoint);
- return chainQuerier.getContractInfo({ contractAddr: this.address });
- }
- async getConfig() {
- const chainQuerier = await CosmwasmQuerier.connect(this.chain.endpoint);
- const allStates = (await chainQuerier.getAllContractState({
- contractAddr: this.address,
- })) as Record<string, string>;
- const config = {
- config_v1: JSON.parse(allStates["\x00\tconfig_v1"]),
- contract_version: JSON.parse(allStates["\x00\x10contract_version"]),
- };
- return config;
- }
- async getLastExecutedGovernanceSequence() {
- const config = await this.getConfig();
- return Number(config.config_v1.governance_sequence_number);
- }
- private parsePrice(priceInfo: Price) {
- return {
- conf: priceInfo.conf.toString(),
- publishTime: priceInfo.publish_time.toString(),
- expo: priceInfo.expo.toString(),
- price: priceInfo.price.toString(),
- };
- }
- async getPriceFeed(feedId: string) {
- const querier = await this.getQuerier();
- try {
- const response = await querier.getPriceFeed(this.address, feedId);
- return {
- price: this.parsePrice(response.price),
- emaPrice: this.parsePrice(response.ema_price),
- };
- } catch (e) {
- return undefined;
- }
- }
- equalDataSources(
- dataSources1: WormholeSource[],
- dataSources2: WormholeSource[]
- ): boolean {
- if (dataSources1.length !== dataSources2.length) return false;
- for (let i = 0; i < dataSources1.length; i++) {
- let found = false;
- for (let j = 0; j < dataSources2.length; j++) {
- if (
- dataSources1[i].emitter === dataSources2[j].emitter &&
- dataSources1[i].chain_id === dataSources2[j].chain_id
- ) {
- found = true;
- break;
- }
- }
- if (!found) return false;
- }
- return true;
- }
- async getDeploymentType(): Promise<string> {
- const config = await this.getConfig();
- const convertDataSource = (source: DataSource) => {
- return {
- emitter: Buffer.from(source.emitterAddress, "hex").toString("base64"),
- chain_id: source.emitterChain,
- };
- };
- const stableDataSources =
- getDefaultDeploymentConfig("stable").dataSources.map(convertDataSource);
- const betaDataSources =
- getDefaultDeploymentConfig("beta").dataSources.map(convertDataSource);
- if (this.equalDataSources(config.config_v1.data_sources, stableDataSources))
- return "stable";
- else if (
- this.equalDataSources(config.config_v1.data_sources, betaDataSources)
- )
- return "beta";
- else return "unknown";
- }
- async executeUpdatePriceFeed(senderPrivateKey: PrivateKey, vaas: Buffer[]) {
- const base64Vaas = vaas.map((v) => v.toString("base64"));
- const fund = await this.getUpdateFee(base64Vaas);
- const executor = await this.chain.getExecutor(senderPrivateKey);
- const pythExecutor = new PythWrapperExecutor(executor);
- const result = await pythExecutor.executeUpdatePriceFeeds({
- contractAddr: this.address,
- vaas: base64Vaas,
- fund,
- });
- return { id: result.txHash, info: result };
- }
- async executeGovernanceInstruction(privateKey: PrivateKey, vaa: Buffer) {
- const executor = await this.chain.getExecutor(privateKey);
- const pythExecutor = new PythWrapperExecutor(executor);
- const result = await pythExecutor.executeGovernanceInstruction({
- contractAddr: this.address,
- vaa: vaa.toString("base64"),
- });
- return { id: result.txHash, info: result };
- }
- async getWormholeContract(): Promise<WormholeCosmWasmContract> {
- const config = await this.getConfig();
- const wormholeAddress = config.config_v1.wormhole_contract;
- return new WormholeCosmWasmContract(this.chain, wormholeAddress);
- }
- async getUpdateFee(msgs: string[]): Promise<Coin> {
- const querier = await this.getQuerier();
- return querier.getUpdateFee(this.address, msgs);
- }
- async getBaseUpdateFee(): Promise<{ amount: string; denom: string }> {
- const config = await this.getConfig();
- return config.config_v1.fee;
- }
- async getVersion(): Promise<string> {
- const config = await this.getConfig();
- return config.contract_version;
- }
- getChain(): CosmWasmChain {
- return this.chain;
- }
- async getTotalFee(): Promise<bigint> {
- const client = await CosmWasmClient.connect(this.chain.endpoint);
- const coin = await client.getBalance(
- this.address,
- this.getChain().feeDenom
- );
- return BigInt(coin.amount);
- }
- async getValidTimePeriod() {
- const client = await CosmWasmClient.connect(this.chain.endpoint);
- const result = await client.queryContractSmart(
- this.address,
- "get_valid_time_period"
- );
- return Number(result.secs + result.nanos * 1e-9);
- }
- }
|