[xc-admin] Contract management tool (#885)
* cleanup * blah * gr * stuff * hm * hmm * wtf * ah fix this * ok finally it does something * ok * hrm * hrm * blah * blah
This commit is contained in:
parent
e1377e5627
commit
25c1ac2c33
|
@ -16,7 +16,8 @@
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"format": "prettier --write \"src/**/*.ts\""
|
"format": "prettier --write \"src/**/*.ts\"",
|
||||||
|
"cli": "npm run build && node lib/cli.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@coral-xyz/anchor": "^0.26.0",
|
"@coral-xyz/anchor": "^0.26.0",
|
||||||
|
|
|
@ -0,0 +1,178 @@
|
||||||
|
import { program } from "commander";
|
||||||
|
import { loadContractConfig, ContractType, SyncOp } from "xc_admin_common";
|
||||||
|
import * as fs from "fs";
|
||||||
|
|
||||||
|
// TODO: extract this configuration to a file
|
||||||
|
const contractsConfig = [
|
||||||
|
{
|
||||||
|
type: ContractType.EvmPythUpgradable,
|
||||||
|
networkId: "arbitrum",
|
||||||
|
address: "0xff1a0f4744e8582DF1aE09D5611b887B6a12925C",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ContractType.EvmWormholeReceiver,
|
||||||
|
networkId: "canto",
|
||||||
|
address: "0x87047526937246727E4869C5f76A347160e08672",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ContractType.EvmPythUpgradable,
|
||||||
|
networkId: "canto",
|
||||||
|
address: "0x98046Bd286715D3B0BC227Dd7a956b83D8978603",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: ContractType.EvmPythUpgradable,
|
||||||
|
networkId: "avalanche",
|
||||||
|
address: "0x4305FB66699C3B2702D4d05CF36551390A4c69C6",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const networksConfig = {
|
||||||
|
evm: {
|
||||||
|
optimism_goerli: {
|
||||||
|
url: `https://rpc.ankr.com/optimism_testnet`,
|
||||||
|
},
|
||||||
|
arbitrum: {
|
||||||
|
url: "https://arb1.arbitrum.io/rpc",
|
||||||
|
},
|
||||||
|
avalanche: {
|
||||||
|
url: "https://api.avax.network/ext/bc/C/rpc",
|
||||||
|
},
|
||||||
|
canto: {
|
||||||
|
url: "https://canto.gravitychain.io",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: we will need configuration of this stuff to decide which multisig to run.
|
||||||
|
const multisigs = [
|
||||||
|
{
|
||||||
|
name: "",
|
||||||
|
wormholeNetwork: "mainnet",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
program
|
||||||
|
.name("pyth_governance")
|
||||||
|
.description("CLI for governing Pyth contracts")
|
||||||
|
.version("0.1.0");
|
||||||
|
|
||||||
|
program
|
||||||
|
.command("get")
|
||||||
|
.description("Find Pyth contracts matching the given search criteria")
|
||||||
|
.option("-n, --network <network-id>", "Find contracts on the given network")
|
||||||
|
.option("-a, --address <address>", "Find contracts with the given address")
|
||||||
|
.option("-t, --type <type-id>", "Find contracts of the given type")
|
||||||
|
.action(async (options: any) => {
|
||||||
|
const contracts = loadContractConfig(contractsConfig, networksConfig);
|
||||||
|
|
||||||
|
console.log(JSON.stringify(options));
|
||||||
|
|
||||||
|
const matches = [];
|
||||||
|
for (const contract of contracts) {
|
||||||
|
if (
|
||||||
|
(options.network === undefined ||
|
||||||
|
contract.networkId == options.network) &&
|
||||||
|
(options.address === undefined ||
|
||||||
|
contract.getAddress() == options.address) &&
|
||||||
|
(options.type === undefined || contract.type == options.type)
|
||||||
|
) {
|
||||||
|
matches.push(contract);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const contract of matches) {
|
||||||
|
const state = await contract.getState();
|
||||||
|
console.log({
|
||||||
|
networkId: contract.networkId,
|
||||||
|
address: contract.getAddress(),
|
||||||
|
type: contract.type,
|
||||||
|
state: state,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
class Cache {
|
||||||
|
private path: string;
|
||||||
|
|
||||||
|
constructor(path: string) {
|
||||||
|
this.path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
private opFilePath(op: SyncOp): string {
|
||||||
|
return `${this.path}/${op.id()}.json`;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readOpCache(op: SyncOp): Record<string, any> {
|
||||||
|
const path = this.opFilePath(op);
|
||||||
|
if (fs.existsSync(path)) {
|
||||||
|
return JSON.parse(fs.readFileSync(path).toString("utf-8"));
|
||||||
|
} else {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeOpCache(op: SyncOp, cache: Record<string, any>) {
|
||||||
|
fs.writeFileSync(this.opFilePath(op), JSON.stringify(cache));
|
||||||
|
}
|
||||||
|
|
||||||
|
public deleteCache(op: SyncOp) {
|
||||||
|
fs.rmSync(this.opFilePath(op));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
program
|
||||||
|
.command("set")
|
||||||
|
.description("Set a configuration parameter for one or more Pyth contracts")
|
||||||
|
.option("-n, --network <network-id>", "Find contracts on the given network")
|
||||||
|
.option("-a, --address <address>", "Find contracts with the given address")
|
||||||
|
.option("-t, --type <type-id>", "Find contracts of the given type")
|
||||||
|
.argument("<fields...>", "Fields to set on the given contracts")
|
||||||
|
.action(async (fields, options: any, command) => {
|
||||||
|
const contracts = loadContractConfig(contractsConfig, networksConfig);
|
||||||
|
|
||||||
|
console.log(JSON.stringify(fields));
|
||||||
|
console.log(JSON.stringify(options));
|
||||||
|
|
||||||
|
const setters = fields.map((value: string) => value.split("="));
|
||||||
|
|
||||||
|
const matches = [];
|
||||||
|
for (const contract of contracts) {
|
||||||
|
if (
|
||||||
|
(options.network === undefined ||
|
||||||
|
contract.networkId == options.network) &&
|
||||||
|
(options.address === undefined ||
|
||||||
|
contract.getAddress() == options.address) &&
|
||||||
|
(options.type === undefined || contract.type == options.type)
|
||||||
|
) {
|
||||||
|
matches.push(contract);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ops = [];
|
||||||
|
for (const contract of matches) {
|
||||||
|
const state = await contract.getState();
|
||||||
|
// TODO: make a decent format for this
|
||||||
|
for (const [field, value] of setters) {
|
||||||
|
state[field] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
ops.push(...(await contract.sync(state)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: extract constant
|
||||||
|
const cacheDir = "cache";
|
||||||
|
fs.mkdirSync(cacheDir, { recursive: true });
|
||||||
|
const cache = new Cache(cacheDir);
|
||||||
|
|
||||||
|
for (const op of ops) {
|
||||||
|
const opCache = cache.readOpCache(op);
|
||||||
|
const isDone = await op.run(opCache);
|
||||||
|
if (isDone) {
|
||||||
|
cache.deleteCache(op);
|
||||||
|
} else {
|
||||||
|
cache.writeOpCache(op, opCache);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
program.parse();
|
|
@ -23,9 +23,12 @@
|
||||||
"@certusone/wormhole-sdk": "^0.9.8",
|
"@certusone/wormhole-sdk": "^0.9.8",
|
||||||
"@coral-xyz/anchor": "^0.26.0",
|
"@coral-xyz/anchor": "^0.26.0",
|
||||||
"@pythnetwork/client": "^2.17.0",
|
"@pythnetwork/client": "^2.17.0",
|
||||||
|
"@pythnetwork/pyth-sdk-solidity": "*",
|
||||||
|
"@pythnetwork/xc-governance-sdk": "*",
|
||||||
"@solana/buffer-layout": "^4.0.1",
|
"@solana/buffer-layout": "^4.0.1",
|
||||||
"@solana/web3.js": "^1.73.0",
|
"@solana/web3.js": "^1.73.0",
|
||||||
"@sqds/mesh": "^1.0.6",
|
"@sqds/mesh": "^1.0.6",
|
||||||
|
"ethers": "^5.7.2",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"typescript": "^4.9.4"
|
"typescript": "^4.9.4"
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,139 @@
|
||||||
|
import { ChainId, Instruction } from "@pythnetwork/xc-governance-sdk";
|
||||||
|
import { ethers } from "ethers";
|
||||||
|
|
||||||
|
export enum ContractType {
|
||||||
|
Oracle,
|
||||||
|
EvmPythUpgradable,
|
||||||
|
EvmWormholeReceiver,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A unique identifier for a blockchain. Note that we cannot use ChainId for this, as ChainId currently reuses
|
||||||
|
* some ids across mainnet / testnet chains (e.g., ethereum goerli has the same id as ethereum mainnet).
|
||||||
|
*/
|
||||||
|
export type NetworkId = string;
|
||||||
|
|
||||||
|
/** A unique identifier for message senders across all wormhole networks. */
|
||||||
|
export interface WormholeAddress {
|
||||||
|
emitter: string;
|
||||||
|
chainId: ChainId;
|
||||||
|
// which network this sender is on
|
||||||
|
network: WormholeNetwork;
|
||||||
|
}
|
||||||
|
export type WormholeNetwork = "mainnet" | "testnet";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Contract is the basic unit of on-chain state that is managed by xc_admin.
|
||||||
|
* Each contracts lives at a specific address of a specific network, and has a type
|
||||||
|
* representing which of several known contract types (evm target chain, wormhole receiver, etc)
|
||||||
|
* that it is.
|
||||||
|
*
|
||||||
|
* Contracts further expose a state representing values that can be modified by governance.
|
||||||
|
* The fields of the state object vary depending on what type of contract this is.
|
||||||
|
* Finally, contracts expose a sync method that generates the needed operations to bring the on-chain state
|
||||||
|
* in sync with a provided desired state.
|
||||||
|
*/
|
||||||
|
export interface Contract<State> {
|
||||||
|
type: ContractType;
|
||||||
|
networkId: NetworkId;
|
||||||
|
/** The address of the contract. The address may be written in different formats for different networks. */
|
||||||
|
getAddress(): string;
|
||||||
|
|
||||||
|
/** Get the on-chain state of all governance-controllable fields of this contract. */
|
||||||
|
getState(): Promise<State>;
|
||||||
|
|
||||||
|
/** Generate a set of operations that, if executed, will update the on-chain contract state to be `target`. */
|
||||||
|
sync(target: State): Promise<SyncOp[]>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An idempotent synchronization operation to update on-chain state. The operation may depend on
|
||||||
|
* external approvals or actions to complete, in which case the operation will pause and need to
|
||||||
|
* be resumed later.
|
||||||
|
*/
|
||||||
|
export interface SyncOp {
|
||||||
|
/**
|
||||||
|
* A unique identifier for this operation. The id represents the content of the operation (e.g., "sets the X
|
||||||
|
* field to Y on contract Z"), so can be used to identify the "same" operation across multiple runs of this program.
|
||||||
|
*/
|
||||||
|
id(): string;
|
||||||
|
/**
|
||||||
|
* Run this operation from a previous state (recorded in cache). The operation can modify cache
|
||||||
|
* to record progress, then returns true if the operation has completed. If this function returns false,
|
||||||
|
* it is waiting on an external operation to complete (e.g., a multisig transaction to be approved).
|
||||||
|
* Re-run this function again once that operation is completed to continue making progress.
|
||||||
|
*
|
||||||
|
* The caller of this function is responsible for preserving the contents of `cache` between calls to
|
||||||
|
* this function.
|
||||||
|
*/
|
||||||
|
run(cache: Record<string, any>): Promise<boolean>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SendGovernanceInstruction implements SyncOp {
|
||||||
|
private instruction: Instruction;
|
||||||
|
private sender: WormholeAddress;
|
||||||
|
// function to submit the signed VAA to the target chain contract
|
||||||
|
private submitVaa: (vaa: string) => Promise<boolean>;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
instruction: Instruction,
|
||||||
|
from: WormholeAddress,
|
||||||
|
submitVaa: (vaa: string) => Promise<boolean>
|
||||||
|
) {
|
||||||
|
this.instruction = instruction;
|
||||||
|
this.sender = from;
|
||||||
|
this.submitVaa = submitVaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
public id(): string {
|
||||||
|
// TODO: use a more understandable identifier (also this may not be unique)
|
||||||
|
return ethers.utils.sha256(this.instruction.serialize());
|
||||||
|
}
|
||||||
|
|
||||||
|
public async run(cache: Record<string, any>): Promise<boolean> {
|
||||||
|
// FIXME: this implementation is temporary. replace with something like the commented out code below.
|
||||||
|
if (cache["multisigTx"] === undefined) {
|
||||||
|
cache["multisigTx"] = "fooooo";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cache["vaa"] === undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// VAA is guaranteed to be defined here
|
||||||
|
const vaa = cache["vaa"];
|
||||||
|
|
||||||
|
// assertVaaPayloadEquals(vaa, payload);
|
||||||
|
|
||||||
|
return await this.submitVaa(vaa);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
public async run(cache: Record<string,any>): Promise<boolean> {
|
||||||
|
if (cache["multisigTx"] === undefined) {
|
||||||
|
// Have not yet submitted this operation to the multisig.
|
||||||
|
const payload = this.instruction.serialize();
|
||||||
|
const txKey = vault.sendWormholeInstruction(payload);
|
||||||
|
cache["multisigTx"] = txKey;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cache["vaa"] === undefined) {
|
||||||
|
const vaa = await executeMultisigTxAndGetVaa(txKey, payloadHex);
|
||||||
|
if (vaa === undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
cache["vaa"] = vaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
// VAA is guaranteed to be defined here
|
||||||
|
const vaa = cache["vaa"];
|
||||||
|
|
||||||
|
assertVaaPayloadEquals(vaa, payload);
|
||||||
|
|
||||||
|
// await proxy.executeGovernanceInstruction("0x" + vaa);
|
||||||
|
await submitVaa(vaa);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
import {
|
||||||
|
Contract,
|
||||||
|
ContractType,
|
||||||
|
NetworkId,
|
||||||
|
SendGovernanceInstruction,
|
||||||
|
SyncOp,
|
||||||
|
WormholeAddress,
|
||||||
|
WormholeNetwork,
|
||||||
|
} from "./Contract";
|
||||||
|
import {
|
||||||
|
ChainId,
|
||||||
|
SetValidPeriodInstruction,
|
||||||
|
} from "@pythnetwork/xc-governance-sdk";
|
||||||
|
import { ethers } from "ethers";
|
||||||
|
|
||||||
|
export class EvmPythUpgradable implements Contract<EvmPythUpgradableState> {
|
||||||
|
public type = ContractType.EvmPythUpgradable;
|
||||||
|
public networkId;
|
||||||
|
private address;
|
||||||
|
|
||||||
|
private contract: ethers.Contract;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
networkId: NetworkId,
|
||||||
|
address: string,
|
||||||
|
contract: ethers.Contract
|
||||||
|
) {
|
||||||
|
this.networkId = networkId;
|
||||||
|
this.address = address;
|
||||||
|
this.contract = contract;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getAddress() {
|
||||||
|
return this.address;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: these getters will need the full PythUpgradable ABI
|
||||||
|
public async getAuthority(): Promise<WormholeAddress> {
|
||||||
|
// FIXME: read from data sources
|
||||||
|
return {
|
||||||
|
emitter: "123454",
|
||||||
|
chainId: 1,
|
||||||
|
network: "mainnet",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the chainId that identifies this contract
|
||||||
|
public async getChainId(): Promise<ChainId> {
|
||||||
|
// FIXME: read from data sources
|
||||||
|
return 23;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getState(): Promise<EvmPythUpgradableState> {
|
||||||
|
const bytecodeSha = ethers.utils.sha256(
|
||||||
|
(await this.contract.provider.getCode(this.contract.address)) as string
|
||||||
|
);
|
||||||
|
const validTimePeriod =
|
||||||
|
(await this.contract.getValidTimePeriod()) as bigint;
|
||||||
|
return {
|
||||||
|
bytecodeSha,
|
||||||
|
validTimePeriod: validTimePeriod.toString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async sync(target: EvmPythUpgradableState): Promise<SyncOp[]> {
|
||||||
|
const myState = await this.getState();
|
||||||
|
const authority = await this.getAuthority();
|
||||||
|
const myChainId = await this.getChainId();
|
||||||
|
const whInstructions = [];
|
||||||
|
|
||||||
|
if (myState.validTimePeriod !== target.validTimePeriod) {
|
||||||
|
whInstructions.push(
|
||||||
|
new SetValidPeriodInstruction(myChainId, BigInt(target.validTimePeriod))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return whInstructions.map(
|
||||||
|
(value) =>
|
||||||
|
new SendGovernanceInstruction(
|
||||||
|
value,
|
||||||
|
authority,
|
||||||
|
this.submitGovernanceVaa
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async submitGovernanceVaa(vaa: string): Promise<boolean> {
|
||||||
|
// FIXME: also needs the full PythUpgradable ABI
|
||||||
|
// await this.contract.executeGovernanceInstruction("0x" + vaa)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EvmPythUpgradableState {
|
||||||
|
bytecodeSha: string;
|
||||||
|
// bigint serialized as a string
|
||||||
|
validTimePeriod: string;
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
import { ethers } from "ethers";
|
||||||
|
import { Contract, ContractType, NetworkId, SyncOp } from "./Contract";
|
||||||
|
|
||||||
|
export class EvmWormholeReceiver implements Contract<EvmWormholeReceiverState> {
|
||||||
|
public type = ContractType.EvmWormholeReceiver;
|
||||||
|
public networkId;
|
||||||
|
private address;
|
||||||
|
|
||||||
|
private contract: ethers.Contract;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
networkId: NetworkId,
|
||||||
|
address: string,
|
||||||
|
contract: ethers.Contract
|
||||||
|
) {
|
||||||
|
this.networkId = networkId;
|
||||||
|
this.address = address;
|
||||||
|
this.contract = contract;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getAddress() {
|
||||||
|
return this.address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getState(): Promise<EvmWormholeReceiverState> {
|
||||||
|
const bytecodeSha = ethers.utils.sha256(
|
||||||
|
(await this.contract.provider.getCode(this.contract.address)) as string
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
bytecodeSha,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async sync(target: EvmWormholeReceiverState): Promise<SyncOp[]> {
|
||||||
|
// TODO
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EvmWormholeReceiverState {
|
||||||
|
bytecodeSha: string;
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
import { ethers } from "ethers";
|
||||||
|
import PythAbi from "@pythnetwork/pyth-sdk-solidity/abis/IPyth.json";
|
||||||
|
import { Contract, ContractType, NetworkId } from "./Contract";
|
||||||
|
import { EvmPythUpgradable } from "./EvmPythUpgradable";
|
||||||
|
import { EvmWormholeReceiver } from "./EvmWormholeReceiver";
|
||||||
|
|
||||||
|
export function getEvmProvider(
|
||||||
|
networkId: NetworkId,
|
||||||
|
networksConfig: any
|
||||||
|
): ethers.providers.Provider {
|
||||||
|
const networkConfig = networksConfig["evm"][networkId]!;
|
||||||
|
return ethers.getDefaultProvider(networkConfig.url);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function loadContractConfig(
|
||||||
|
contractsConfig: any,
|
||||||
|
networksConfig: any
|
||||||
|
): Contract<any>[] {
|
||||||
|
const contracts = [];
|
||||||
|
for (const contractConfig of contractsConfig) {
|
||||||
|
contracts.push(fromConfig(contractConfig, networksConfig));
|
||||||
|
}
|
||||||
|
return contracts;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fromConfig(contractConfig: any, networksConfig: any): Contract<any> {
|
||||||
|
switch (contractConfig.type) {
|
||||||
|
case ContractType.EvmPythUpgradable: {
|
||||||
|
const ethersContract = new ethers.Contract(
|
||||||
|
contractConfig.address,
|
||||||
|
PythAbi,
|
||||||
|
getEvmProvider(contractConfig.networkId, networksConfig)
|
||||||
|
);
|
||||||
|
|
||||||
|
return new EvmPythUpgradable(
|
||||||
|
contractConfig.networkId,
|
||||||
|
contractConfig.address,
|
||||||
|
ethersContract
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case ContractType.EvmWormholeReceiver: {
|
||||||
|
const ethersContract = new ethers.Contract(
|
||||||
|
contractConfig.address,
|
||||||
|
// TODO: pass in an appropriate ABI here
|
||||||
|
[],
|
||||||
|
getEvmProvider(contractConfig.networkId, networksConfig)
|
||||||
|
);
|
||||||
|
|
||||||
|
return new EvmWormholeReceiver(
|
||||||
|
contractConfig.networkId,
|
||||||
|
contractConfig.address,
|
||||||
|
ethersContract
|
||||||
|
);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new Error(`unknown contract type: ${contractConfig.type}`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
export * from "./config";
|
||||||
|
export * from "./Contract";
|
||||||
|
export * from "./EvmPythUpgradable";
|
||||||
|
export * from "./EvmWormholeReceiver";
|
|
@ -9,3 +9,4 @@ export * from "./bpf_upgradable_loader";
|
||||||
export * from "./deterministic_oracle_accounts";
|
export * from "./deterministic_oracle_accounts";
|
||||||
export * from "./cranks";
|
export * from "./cranks";
|
||||||
export * from "./message_buffer";
|
export * from "./message_buffer";
|
||||||
|
export * from "./contracts";
|
||||||
|
|
|
@ -5,6 +5,7 @@ const nextConfig = {
|
||||||
externalDir: true,
|
externalDir: true,
|
||||||
},
|
},
|
||||||
webpack(config) {
|
webpack(config) {
|
||||||
|
config.experiments = { asyncWebAssembly: true }
|
||||||
config.resolve.fallback = { fs: false }
|
config.resolve.fallback = { fs: false }
|
||||||
const fileLoaderRule = config.module.rules.find(
|
const fileLoaderRule = config.module.rules.find(
|
||||||
(rule) => rule.test && rule.test.test('.svg')
|
(rule) => rule.test && rule.test.test('.svg')
|
||||||
|
|
|
@ -1335,9 +1335,11 @@
|
||||||
"@certusone/wormhole-sdk": "^0.9.8",
|
"@certusone/wormhole-sdk": "^0.9.8",
|
||||||
"@coral-xyz/anchor": "^0.26.0",
|
"@coral-xyz/anchor": "^0.26.0",
|
||||||
"@pythnetwork/client": "^2.17.0",
|
"@pythnetwork/client": "^2.17.0",
|
||||||
|
"@pythnetwork/pyth-sdk-solidity": "*",
|
||||||
"@solana/buffer-layout": "^4.0.1",
|
"@solana/buffer-layout": "^4.0.1",
|
||||||
"@solana/web3.js": "^1.73.0",
|
"@solana/web3.js": "^1.73.0",
|
||||||
"@sqds/mesh": "^1.0.6",
|
"@sqds/mesh": "^1.0.6",
|
||||||
|
"ethers": "^5.7.2",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"typescript": "^4.9.4"
|
"typescript": "^4.9.4"
|
||||||
},
|
},
|
||||||
|
@ -106547,12 +106549,14 @@
|
||||||
"@certusone/wormhole-sdk": "^0.9.8",
|
"@certusone/wormhole-sdk": "^0.9.8",
|
||||||
"@coral-xyz/anchor": "^0.26.0",
|
"@coral-xyz/anchor": "^0.26.0",
|
||||||
"@pythnetwork/client": "^2.17.0",
|
"@pythnetwork/client": "^2.17.0",
|
||||||
|
"@pythnetwork/pyth-sdk-solidity": "*",
|
||||||
"@solana/buffer-layout": "^4.0.1",
|
"@solana/buffer-layout": "^4.0.1",
|
||||||
"@solana/web3.js": "^1.73.0",
|
"@solana/web3.js": "^1.73.0",
|
||||||
"@sqds/mesh": "^1.0.6",
|
"@sqds/mesh": "^1.0.6",
|
||||||
"@types/bn.js": "^5.1.1",
|
"@types/bn.js": "^5.1.1",
|
||||||
"@types/jest": "^29.2.5",
|
"@types/jest": "^29.2.5",
|
||||||
"@types/lodash": "^4.14.191",
|
"@types/lodash": "^4.14.191",
|
||||||
|
"ethers": "^5.7.2",
|
||||||
"jest": "^29.3.1",
|
"jest": "^29.3.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"prettier": "^2.8.1",
|
"prettier": "^2.8.1",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { readFileSync } from "fs";
|
import { readFileSync } from "fs";
|
||||||
import { Bech32, toHex } from "@cosmjs/encoding";
|
import { Bech32, toHex } from "@cosmjs/encoding";
|
||||||
import { zeroPad } from "ethers/lib/utils.js";
|
import { ethers } from "ethers";
|
||||||
import assert from "assert";
|
import assert from "assert";
|
||||||
import { getNetworkInfo, Network } from "@injectivelabs/networks";
|
import { getNetworkInfo, Network } from "@injectivelabs/networks";
|
||||||
import {
|
import {
|
||||||
|
@ -210,7 +210,7 @@ export class InjectiveDeployer implements Deployer {
|
||||||
// Injective addresses are "human-readable", but for cross-chain registrations, we
|
// Injective addresses are "human-readable", but for cross-chain registrations, we
|
||||||
// want the "canonical" version
|
// want the "canonical" version
|
||||||
function convert_injective_address_to_hex(human_addr: string) {
|
function convert_injective_address_to_hex(human_addr: string) {
|
||||||
return "0x" + toHex(zeroPad(Bech32.decode(human_addr).data, 32));
|
return "0x" + toHex(ethers.utils.zeroPad(Bech32.decode(human_addr).data, 32));
|
||||||
}
|
}
|
||||||
|
|
||||||
// enter key of what to extract
|
// enter key of what to extract
|
||||||
|
|
|
@ -12,7 +12,7 @@ import {
|
||||||
} from "@terra-money/terra.js";
|
} from "@terra-money/terra.js";
|
||||||
import { readFileSync } from "fs";
|
import { readFileSync } from "fs";
|
||||||
import { Bech32, toHex } from "@cosmjs/encoding";
|
import { Bech32, toHex } from "@cosmjs/encoding";
|
||||||
import { zeroPad } from "ethers/lib/utils.js";
|
import { ethers } from "ethers";
|
||||||
import assert from "assert";
|
import assert from "assert";
|
||||||
import { ContractInfo, Deployer } from ".";
|
import { ContractInfo, Deployer } from ".";
|
||||||
|
|
||||||
|
@ -179,7 +179,7 @@ export class TerraDeployer implements Deployer {
|
||||||
// Terra addresses are "human-readable", but for cross-chain registrations, we
|
// Terra addresses are "human-readable", but for cross-chain registrations, we
|
||||||
// want the "canonical" version
|
// want the "canonical" version
|
||||||
export function convert_terra_address_to_hex(human_addr: string) {
|
export function convert_terra_address_to_hex(human_addr: string) {
|
||||||
return "0x" + toHex(zeroPad(Bech32.decode(human_addr).data, 32));
|
return "0x" + toHex(ethers.utils.zeroPad(Bech32.decode(human_addr).data, 32));
|
||||||
}
|
}
|
||||||
|
|
||||||
// enter key of what to extract
|
// enter key of what to extract
|
||||||
|
|
Loading…
Reference in New Issue