Abolish xc governance sdk (#957)

* Replace xc-governance-sdk with xc_admin_common package

xc_admin_package was not using the CHAIN overrides declared in the governance-sdk so it was moved
to that package as well

* Replace xc-governance-sdk with xc_admin_common in other packages

* Remove the package and all of its references

* Fix tests

* Fix bug in GovernanceDataSourceTransfer encoding

* Rename all references to the old package

* Redeploy neutron_testnet contract with new chain id

* Move SetWormholeAddress to separate file
This commit is contained in:
Mohammad Amin Khashkhashi Moghaddam 2023-07-17 15:24:54 +02:00 committed by GitHub
parent b64090aa75
commit c732fcf586
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
56 changed files with 362 additions and 2759 deletions

View File

@ -2,13 +2,13 @@ on:
pull_request:
paths:
- target_chains/ethereum/contracts/**
- governance/xc_governance_sdk_js/**
- governance/xc_admin/packages/xc_admin_common/**
push:
branches:
- main
paths:
- target_chains/ethereum/contracts/**
- governance/xc_governance_sdk_js/**
- governance/xc_admin/packages/xc_admin_common/**
name: Ethereum Contract
@ -22,10 +22,6 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Install XC-governance sdk dependencies
run: npm ci
working-directory: governance/xc_governance_sdk_js
- name: Install contract npm dependencies
run: npm ci

1
contract_manager/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
lib/

View File

@ -17,7 +17,6 @@
"@certusone/wormhole-sdk": "^0.9.8",
"@pythnetwork/cosmwasm-deploy-tools": "*",
"@pythnetwork/price-service-client": "*",
"@pythnetwork/xc-governance-sdk": "*",
"bs58": "^5.0.0",
"ts-node": "^10.9.1",
"typescript": "^4.9.3"

View File

@ -1,7 +1,6 @@
import { Contract } from "./base";
import { AptosChain, Chain } from "./chains";
import { DataSource, HexString32Bytes } from "@pythnetwork/xc-governance-sdk";
import { AptosClient } from "aptos";
import { DataSource } from "xc_admin_common";
export class AptosContract extends Contract {
static type: string = "AptosContract";
@ -64,19 +63,25 @@ export class AptosContract extends Contract {
async getDataSources(): Promise<DataSource[]> {
const data = (await this.findResource("DataSources")) as any;
return data.sources.keys.map((source: any) => {
return new DataSource(
Number(source.emitter_chain),
new HexString32Bytes(source.emitter_address.external_address)
);
return {
emitterChain: Number(source.emitter_chain),
emitterAddress: source.emitter_address.external_address.replace(
"0x",
""
),
};
});
}
async getGovernanceDataSource(): Promise<DataSource> {
const data = (await this.findResource("GovernanceDataSource")) as any;
return new DataSource(
Number(data.source.emitter_chain),
new HexString32Bytes(data.source.emitter_address.external_address)
);
return {
emitterChain: Number(data.source.emitter_chain),
emitterAddress: data.source.emitter_address.external_address.replace(
"0x",
""
),
};
}
getId(): string {

View File

@ -1,9 +1,4 @@
import {
CHAINS,
DataSource,
HexString32Bytes,
SetFeeInstruction,
} from "@pythnetwork/xc-governance-sdk";
import { DataSource } from "xc_admin_common";
import { Chain } from "./chains";
export abstract class Storable {

View File

@ -1,15 +1,13 @@
import { readdirSync, readFileSync, writeFileSync } from "fs";
import { Storable } from "./base";
import {
ChainName,
CHAINS,
CosmwasmUpgradeContractInstruction,
EthereumUpgradeContractInstruction,
HexString20Bytes,
HexString32Bytes,
SetFeeInstruction,
SuiAuthorizeUpgradeContractInstruction,
} from "@pythnetwork/xc-governance-sdk";
import { BufferBuilder } from "@pythnetwork/xc-governance-sdk/lib/serialize";
SetFee,
CosmosUpgradeContract,
EvmUpgradeContract,
SuiAuthorizeUpgradeContract,
AptosAuthorizeUpgradeContract,
} from "xc_admin_common";
import { AptosClient } from "aptos";
export abstract class Chain extends Storable {
@ -27,11 +25,11 @@ export abstract class Chain extends Storable {
* @param exponent the new fee exponent to set
*/
generateGovernanceSetFeePayload(fee: number, exponent: number): Buffer {
return new SetFeeInstruction(
CHAINS[this.getId() as keyof typeof CHAINS],
return new SetFee(
this.getId() as ChainName,
BigInt(fee),
BigInt(exponent)
).serialize();
).encode();
}
/**
@ -84,10 +82,10 @@ export class CosmWasmChain extends Chain {
}
generateGovernanceUpgradePayload(codeId: bigint): Buffer {
return new CosmwasmUpgradeContractInstruction(
CHAINS[this.getId() as keyof typeof CHAINS],
return new CosmosUpgradeContract(
this.getId() as ChainName,
codeId
).serialize();
).encode();
}
}
@ -115,37 +113,42 @@ export class SuiChain extends Chain {
return SuiChain.type;
}
//TODO: Move this logic to xc_admin_common
private wrapWithWormholeGovernancePayload(
actionVariant: number,
payload: Buffer
): Buffer {
const builder = new BufferBuilder();
builder.addBuffer(
const actionVariantBuffer = Buffer.alloc(1);
actionVariantBuffer.writeUint8(actionVariant, 0);
const chainBuffer = Buffer.alloc(2);
chainBuffer.writeUint16BE(CHAINS["sui"], 0);
const result = Buffer.concat([
Buffer.from(
"0000000000000000000000000000000000000000000000000000000000000001",
"hex"
)
);
builder.addUint8(actionVariant);
builder.addUint16(CHAINS["sui"]); // should always be sui (21) no matter devnet or testnet
builder.addBuffer(payload);
return builder.build();
),
actionVariantBuffer,
chainBuffer,
payload,
]);
return result;
}
/**
* Returns the payload for a governance contract upgrade instruction for contracts deployed on this chain
* @param digest hex string of the 32 byte digest for the new package without the 0x prefix
*/
generateGovernanceUpgradePayload(digest: string): Buffer {
let setFee = new SuiAuthorizeUpgradeContractInstruction(
CHAINS["sui"],
new HexString32Bytes(digest)
).serialize();
let setFee = new SuiAuthorizeUpgradeContract("sui", digest).encode();
return this.wrapWithWormholeGovernancePayload(0, setFee);
}
generateGovernanceSetFeePayload(fee: number, exponent: number): Buffer {
let setFee = new SetFeeInstruction(
CHAINS["sui"],
let setFee = new SetFee(
"sui", // should always be sui no matter devnet or testnet or mainnet
BigInt(fee),
BigInt(exponent)
).serialize();
).encode();
return this.wrapWithWormholeGovernancePayload(3, setFee);
}
}
@ -162,11 +165,12 @@ export class EVMChain extends Chain {
return new EVMChain(parsed.id, parsed.rpcUrl);
}
generateGovernanceUpgradePayload(address: HexString20Bytes): Buffer {
return new EthereumUpgradeContractInstruction(
CHAINS[this.getId() as keyof typeof CHAINS],
address
).serialize();
/**
* Returns the payload for a governance contract upgrade instruction for contracts deployed on this chain
* @param address hex string of the 20 byte address of the contract to upgrade to without the 0x prefix
*/
generateGovernanceUpgradePayload(address: string): Buffer {
return new EvmUpgradeContract(this.getId() as ChainName, address).encode();
}
toJson(): any {
@ -193,19 +197,20 @@ export class AptosChain extends Chain {
return new AptosClient(this.rpcUrl);
}
/**
* Returns the payload for a governance contract upgrade instruction for contracts deployed on this chain
* @param digest hex string of the 32 byte digest for the new package without the 0x prefix
*/
generateGovernanceUpgradePayload(digest: string): Buffer {
return new SuiAuthorizeUpgradeContractInstruction(
CHAINS["aptos"],
new HexString32Bytes(digest)
).serialize();
return new AptosAuthorizeUpgradeContract("aptos", digest).encode();
}
generateGovernanceSetFeePayload(fee: number, exponent: number): Buffer {
return new SetFeeInstruction(
CHAINS["aptos"], // should always be aptos (22) no matter devnet or testnet or mainnet
return new SetFee(
"aptos", // should always be aptos no matter devnet or testnet or mainnet
BigInt(fee),
BigInt(exponent)
).serialize();
).encode();
}
getType(): string {

View File

@ -1,11 +1,7 @@
import { Chain, CosmWasmChain } from "./chains";
import { readFileSync } from "fs";
import { getPythConfig } from "@pythnetwork/cosmwasm-deploy-tools/lib/configs";
import {
CHAINS,
DataSource,
HexString32Bytes,
} from "@pythnetwork/xc-governance-sdk";
import { CHAINS, DataSource } from "xc_admin_common";
import { DeploymentType } from "@pythnetwork/cosmwasm-deploy-tools/lib/helper";
import {
CosmwasmExecutor,
@ -45,10 +41,10 @@ export class CosmWasmContract extends Contract {
async getDataSources(): Promise<DataSource[]> {
const config = await this.getConfig();
return config.config_v1.data_sources.map(({ emitter, chain_id }: any) => {
return new DataSource(
Number(chain_id),
new HexString32Bytes(Buffer.from(emitter, "base64").toString("hex"))
);
return {
emitterChain: Number(chain_id),
emitterAddress: Buffer.from(emitter, "base64").toString("hex"),
};
});
}
@ -56,12 +52,10 @@ export class CosmWasmContract extends Contract {
const config = await this.getConfig();
const { emitter: emitterAddress, chain_id: chainId } =
config.config_v1.governance_source;
return new DataSource(
Number(chainId),
new HexString32Bytes(
Buffer.from(emitterAddress, "base64").toString("hex")
)
);
return {
emitterChain: Number(chainId),
emitterAddress: Buffer.from(emitterAddress, "base64").toString("hex"),
};
}
static type = "CosmWasmContract";

View File

@ -2,7 +2,7 @@ import Web3 from "web3"; //TODO: decide on using web3 or ethers.js
import PythInterfaceAbi from "@pythnetwork/pyth-sdk-solidity/abis/IPyth.json";
import { Contract } from "./base";
import { Chain, EVMChain } from "./chains";
import { DataSource, HexString32Bytes } from "@pythnetwork/xc-governance-sdk";
import { DataSource } from "xc_admin_common";
const EXTENDED_PYTH_ABI = [
{
@ -136,12 +136,20 @@ export class EVMContract extends Contract {
async getDataSources(): Promise<DataSource[]> {
const pythContract = this.getContract();
const result = await pythContract.methods.validDataSources().call();
return result.map(({ chainId, emitterAddress }: any) => {
return new DataSource(
Number(chainId),
new HexString32Bytes(emitterAddress)
);
});
return result.map(
({
chainId,
emitterAddress,
}: {
chainId: string;
emitterAddress: string;
}) => {
return {
emitterChain: Number(chainId),
emitterAddress: emitterAddress.replace("0x", ""),
};
}
);
}
async getGovernanceDataSource(): Promise<DataSource> {
@ -149,10 +157,10 @@ export class EVMContract extends Contract {
const [chainId, emitterAddress] = await pythContract.methods
.governanceDataSource()
.call();
return new DataSource(
Number(chainId),
new HexString32Bytes(emitterAddress)
);
return {
emitterChain: Number(chainId),
emitterAddress: emitterAddress.replace("0x", ""),
};
}
async executeGovernanceInstruction(privateKey: string, vaa: Buffer) {

View File

@ -8,7 +8,7 @@ import {
TransactionBlock,
} from "@mysten/sui.js";
import { Chain, SuiChain } from "./chains";
import { DataSource, HexString32Bytes } from "@pythnetwork/xc-governance-sdk";
import { DataSource } from "xc_admin_common";
import { Contract } from "./base";
export class SuiContract extends Contract {
@ -294,14 +294,12 @@ export class SuiContract extends Contract {
}
return result.data.content.fields.value.fields.keys.map(
({ fields }: any) => {
return new DataSource(
Number(fields.emitter_chain),
new HexString32Bytes(
Buffer.from(
fields.emitter_address.fields.value.fields.data
).toString("hex")
)
);
return {
emitterChain: Number(fields.emitter_chain),
emitterAddress: Buffer.from(
fields.emitter_address.fields.value.fields.data
).toString("hex"),
};
}
);
}
@ -312,10 +310,10 @@ export class SuiContract extends Contract {
const chainId = governanceFields.emitter_chain;
const emitterAddress =
governanceFields.emitter_address.fields.value.fields.data;
return new DataSource(
Number(chainId),
new HexString32Bytes(Buffer.from(emitterAddress).toString("hex"))
);
return {
emitterChain: Number(chainId),
emitterAddress: Buffer.from(emitterAddress).toString("hex"),
};
}
async getBaseUpdateFee() {

View File

@ -1,51 +1,10 @@
import {
Vault,
Contracts,
Vaults,
loadHotWallet,
WormholeEmitter,
SubmittedWormholeMessage,
} from "./entities";
import { SuiContract } from "./sui";
import { CosmWasmContract } from "./cosmwasm";
import { Ed25519Keypair, RawSigner } from "@mysten/sui.js";
import { DefaultStore } from "./store";
import { Chains } from "./chains";
import { executeProposal } from "xc_admin_common";
import { EVMContract } from "./evm";
async function test() {
// Deploy the same cosmwasm code with different config
// let c = Contracts.osmosis_testnet_5_osmo1lltupx02sj99suakmuk4sr4ppqf34ajedaxut3ukjwkv6469erwqtpg9t3 as CosmWasmContract;
// let old_conf = await c.getConfig();
// let config = CosmWasmContract.getDeploymentConfig(c.chain, 'edge', old_conf.config_v1.wormhole_contract);
// console.log(config);
// config.governance_source.emitter = wallet.publicKey.toBuffer().toString('base64');
// let mnemonic = 'FILLME'
// console.log(await CosmWasmContract.deploy(c.chain, await c.getCodeId(), config, mnemonic));
let s = DefaultStore;
Object.values(Contracts).forEach((c) => {
console.log(c);
s.save(c);
});
Object.values(Chains).forEach((c) => {
console.log(c);
s.save(c);
});
// Execute some governance instruction on sui contract
// let c = Contracts.sui_testnet_0x651dcb84d579fcdf51f15d79eb28f7e10b416c9202b6a156495bb1a4aecd55ea as SuiContract
// let wallet = await loadHotWallet('/tmp/priv.json');
// let emitter = new WormholeEmitter("devnet", wallet);
// let proposal = c.setUpdateFee(200);
// let submittedWormholeMessage = await emitter.sendMessage(proposal);
// let vaa = await submittedWormholeMessage.fetchVAA(10);
// const keypair = Ed25519Keypair.fromSecretKey(Buffer.from('FILLME', "hex"));
// await c.executeGovernanceInstruction(vaa);
for (const contract of Object.values(DefaultStore.contracts)) {
console.log("Contract", contract.getId());
console.log(await contract.getGovernanceDataSource());
}
}
test();

View File

@ -1,5 +1,5 @@
querierEndpoint: https://rpc.pion.rs-testnet.polypore.xyz/
executorEndpoint: https://rpc.pion.rs-testnet.polypore.xyz/
querierEndpoint: https://rpc-palvus.pion-1.ntrn.tech/
executorEndpoint: https://rpc-palvus.pion-1.ntrn.tech/
id: neutron_testnet_pion_1
gasPrice: "0.05"
prefix: neutron

View File

@ -1,3 +1,3 @@
chain: neutron_testnet_pion_1
address: neutron1xxmcu6wxgawjlajx8jalyk9cxsudnygjg0tvjesfyurh4utvtpes5wmpjp
address: neutron16zwrmx3zgggmxhzau86xfycm42cr4sj888hdvzsxya3qarp6zhhqzhlkvz
type: CosmWasmContract

View File

@ -24,7 +24,6 @@
"@coral-xyz/anchor": "^0.26.0",
"@pythnetwork/client": "^2.17.0",
"@pythnetwork/pyth-sdk-solidity": "*",
"@pythnetwork/xc-governance-sdk": "*",
"@solana/buffer-layout": "^4.0.1",
"@solana/web3.js": "^1.73.0",
"@sqds/mesh": "^1.0.6",

View File

@ -9,20 +9,16 @@ import {
ActionName,
PythGovernanceAction,
decodeGovernancePayload,
EvmSetWormholeAddress,
} from "..";
import * as fc from "fast-check";
import {
ChainId,
ChainName,
CHAINS,
toChainId,
toChainName,
} from "@certusone/wormhole-sdk";
import { ChainName, CHAINS } from "../chains";
import { Arbitrary, IntArrayConstraints } from "fast-check";
import {
AptosAuthorizeUpgradeContract,
CosmosUpgradeContract,
EvmUpgradeContract,
SuiAuthorizeUpgradeContract,
} from "../governance_payload/UpgradeContract";
import {
AuthorizeGovernanceDataSourceTransfer,
@ -219,6 +215,12 @@ function governanceActionArb(): Arbitrary<PythGovernanceAction> {
);
}
);
const suiArb = hexBytesArb({ minLength: 32, maxLength: 32 }).map(
(buffer) => {
return new SuiAuthorizeUpgradeContract(header.targetChainId, buffer);
}
);
const evmArb = hexBytesArb({ minLength: 20, maxLength: 20 }).map(
(address) => {
return new EvmUpgradeContract(header.targetChainId, address);
@ -254,6 +256,10 @@ function governanceActionArb(): Arbitrary<PythGovernanceAction> {
parseInt(index.toString())
);
});
} else if (header.action === "SetWormholeAddress") {
return hexBytesArb({ minLength: 20, maxLength: 20 }).map((address) => {
return new EvmSetWormholeAddress(header.targetChainId, address);
});
} else {
throw new Error("Unsupported action type");
}

View File

@ -1,6 +1,4 @@
import { CHAINS as WORMHOLE_CHAINS } from "@certusone/wormhole-sdk";
export { CHAINS as WORMHOLE_CHAINS } from "@certusone/wormhole-sdk";
// GUIDELINES to add a chain
// PYTH will have:
// 1. Mainnet Deployment - which will have pyth mainnet governance and data sources
@ -34,12 +32,12 @@ export const RECEIVER_CHAINS = {
sei_pacific_1: 60017,
sei_testnet_atlantic_2: 60018,
neutron: 60019,
neutron_testnet_pion_1: 60020,
juno: 60020,
juno_testnet: 60021,
kava: 60022,
wemix: 60023,
linea: 60024,
neutron_testnet_pion_1: 60025,
};
// If there is any overlapping value the receiver chain will replace the wormhole
@ -47,3 +45,16 @@ export const RECEIVER_CHAINS = {
export const CHAINS = { ...WORMHOLE_CHAINS, ...RECEIVER_CHAINS };
export declare type ChainName = keyof typeof CHAINS;
export declare type ChainId = typeof CHAINS[ChainName];
export function toChainId(chainName: ChainName): ChainId {
return CHAINS[chainName];
}
const CHAIN_ID_TO_NAME = Object.entries(CHAINS).reduce((obj, [name, id]) => {
obj[id] = name;
return obj;
}, {} as any);
export function toChainName(chainId: ChainId): ChainName {
return CHAIN_ID_TO_NAME[chainId];
}

View File

@ -1,5 +1,6 @@
import { ChainId, Instruction } from "@pythnetwork/xc-governance-sdk";
import { ChainId } from "../chains";
import { ethers } from "ethers";
import { PythGovernanceAction } from "../governance_payload";
export enum ContractType {
Oracle,
@ -70,13 +71,13 @@ export interface SyncOp {
}
export class SendGovernanceInstruction implements SyncOp {
private instruction: Instruction;
private instruction: PythGovernanceAction;
private sender: WormholeAddress;
// function to submit the signed VAA to the target chain contract
private submitVaa: (vaa: string) => Promise<boolean>;
constructor(
instruction: Instruction,
instruction: PythGovernanceAction,
from: WormholeAddress,
submitVaa: (vaa: string) => Promise<boolean>
) {
@ -87,7 +88,7 @@ export class SendGovernanceInstruction implements SyncOp {
public id(): string {
// TODO: use a more understandable identifier (also this may not be unique)
return ethers.utils.sha256(this.instruction.serialize());
return ethers.utils.sha256(this.instruction.encode());
}
public async run(cache: Record<string, any>): Promise<boolean> {

View File

@ -7,11 +7,9 @@ import {
WormholeAddress,
WormholeNetwork,
} from "./Contract";
import {
ChainId,
SetValidPeriodInstruction,
} from "@pythnetwork/xc-governance-sdk";
import { ethers } from "ethers";
import { ChainName } from "../chains";
import { SetValidPeriod } from "../governance_payload";
export class EvmPythUpgradable implements Contract<EvmPythUpgradableState> {
public type = ContractType.EvmPythUpgradable;
@ -45,9 +43,9 @@ export class EvmPythUpgradable implements Contract<EvmPythUpgradableState> {
}
// get the chainId that identifies this contract
public async getChainId(): Promise<ChainId> {
public async getChain(): Promise<ChainName> {
// FIXME: read from data sources
return 23;
return "polygon";
}
public async getState(): Promise<EvmPythUpgradableState> {
@ -65,12 +63,12 @@ export class EvmPythUpgradable implements Contract<EvmPythUpgradableState> {
public async sync(target: EvmPythUpgradableState): Promise<SyncOp[]> {
const myState = await this.getState();
const authority = await this.getAuthority();
const myChainId = await this.getChainId();
const myChainId = await this.getChain();
const whInstructions = [];
if (myState.validTimePeriod !== target.validTimePeriod) {
whInstructions.push(
new SetValidPeriodInstruction(myChainId, BigInt(target.validTimePeriod))
new SetValidPeriod(myChainId, BigInt(target.validTimePeriod))
);
}

View File

@ -1,4 +1,4 @@
import { ChainId, ChainName } from "@certusone/wormhole-sdk";
import { ChainName } from "../chains";
import * as BufferLayout from "@solana/buffer-layout";
import {
PythGovernanceAction,

View File

@ -5,7 +5,7 @@ import {
PythGovernanceHeader,
} from "./PythGovernanceAction";
import * as BufferLayout from "@solana/buffer-layout";
import { ChainName } from "@certusone/wormhole-sdk";
import { ChainName } from "../chains";
/**
* Authorize transferring the governance data source from the sender's emitter address to another emitter.
@ -53,7 +53,7 @@ export class AuthorizeGovernanceDataSourceTransfer
export class RequestGovernanceDataSourceTransfer extends PythGovernanceActionImpl {
static layout: BufferLayout.Structure<
Readonly<{ governanceDataSourceIndex: number }>
> = BufferLayout.struct([BufferLayout.u32be()]);
> = BufferLayout.struct([BufferLayout.u32be("governanceDataSourceIndex")]);
constructor(
targetChainId: ChainName,

View File

@ -1,9 +1,4 @@
import {
ChainId,
ChainName,
toChainId,
toChainName,
} from "@certusone/wormhole-sdk";
import { ChainId, ChainName, toChainId, toChainName } from "../chains";
import * as BufferLayout from "@solana/buffer-layout";
import { PACKET_DATA_SIZE } from "@solana/web3.js";
@ -19,6 +14,7 @@ export const TargetAction = {
SetFee: 3,
SetValidPeriod: 4,
RequestGovernanceDataSourceTransfer: 5,
SetWormholeAddress: 6,
} as const;
/** Helper to get the ActionName from a (moduleId, actionId) tuple*/
@ -41,6 +37,8 @@ export function toActionName(
return "SetValidPeriod";
case 5:
return "RequestGovernanceDataSourceTransfer";
case 6:
return "SetWormholeAddress";
}
}
return undefined;

View File

@ -4,7 +4,7 @@ import {
PythGovernanceActionImpl,
PythGovernanceHeader,
} from "./PythGovernanceAction";
import { ChainName } from "@certusone/wormhole-sdk";
import { ChainName } from "../chains";
import * as BufferLayout from "@solana/buffer-layout";
import * as BufferLayoutExt from "./BufferLayoutExt";

View File

@ -1,7 +1,7 @@
import { PythGovernanceActionImpl } from "./PythGovernanceAction";
import * as BufferLayout from "@solana/buffer-layout";
import * as BufferLayoutExt from "./BufferLayoutExt";
import { ChainName } from "@certusone/wormhole-sdk";
import { ChainName } from "../chains";
/** Set the fee on the target chain to newFeeValue * 10^newFeeExpo */
export class SetFee extends PythGovernanceActionImpl {

View File

@ -4,7 +4,7 @@ import {
} from "./PythGovernanceAction";
import * as BufferLayout from "@solana/buffer-layout";
import * as BufferLayoutExt from "./BufferLayoutExt";
import { ChainName } from "@certusone/wormhole-sdk";
import { ChainName } from "../chains";
/** Set the valid period (the default amount of time in which prices are considered fresh) to the provided value */
export class SetValidPeriod extends PythGovernanceActionImpl {

View File

@ -0,0 +1,33 @@
import { ChainName } from "../chains";
import { PythGovernanceActionImpl } from "./PythGovernanceAction";
import * as BufferLayout from "@solana/buffer-layout";
import * as BufferLayoutExt from "./BufferLayoutExt";
export class EvmSetWormholeAddress extends PythGovernanceActionImpl {
static layout: BufferLayout.Structure<Readonly<{ address: string }>> =
BufferLayout.struct([BufferLayoutExt.hexBytes(20, "address")]);
constructor(targetChainId: ChainName, readonly address: string) {
super(targetChainId, "SetWormholeAddress");
}
static decode(data: Buffer): EvmSetWormholeAddress | undefined {
const decoded = PythGovernanceActionImpl.decodeWithPayload(
data,
"SetWormholeAddress",
this.layout
);
if (!decoded) return undefined;
return new EvmSetWormholeAddress(
decoded[0].targetChainId,
decoded[1].address
);
}
encode(): Buffer {
return super.encodeWithPayload(EvmSetWormholeAddress.layout, {
address: this.address,
});
}
}

View File

@ -1,4 +1,4 @@
import { ChainName } from "@certusone/wormhole-sdk";
import { ChainName } from "../chains";
import { PythGovernanceActionImpl } from "./PythGovernanceAction";
import * as BufferLayout from "@solana/buffer-layout";
import * as BufferLayoutExt from "./BufferLayoutExt";
@ -63,6 +63,35 @@ export class AptosAuthorizeUpgradeContract extends PythGovernanceActionImpl {
}
}
export class SuiAuthorizeUpgradeContract extends PythGovernanceActionImpl {
static layout: BufferLayout.Structure<Readonly<{ hash: string }>> =
BufferLayout.struct([BufferLayoutExt.hexBytes(32, "hash")]);
constructor(targetChainId: ChainName, readonly hash: string) {
super(targetChainId, "UpgradeContract");
}
static decode(data: Buffer): SuiAuthorizeUpgradeContract | undefined {
const decoded = PythGovernanceActionImpl.decodeWithPayload(
data,
"UpgradeContract",
this.layout
);
if (!decoded) return undefined;
return new SuiAuthorizeUpgradeContract(
decoded[0].targetChainId,
decoded[1].hash
);
}
encode(): Buffer {
return super.encodeWithPayload(SuiAuthorizeUpgradeContract.layout, {
hash: this.hash,
});
}
}
export class EvmUpgradeContract extends PythGovernanceActionImpl {
static layout: BufferLayout.Structure<Readonly<{ address: string }>> =
BufferLayout.struct([BufferLayoutExt.hexBytes(20, "address")]);

View File

@ -15,6 +15,7 @@ import {
import { SetDataSources } from "./SetDataSources";
import { SetValidPeriod } from "./SetValidPeriod";
import { SetFee } from "./SetFee";
import { EvmSetWormholeAddress } from "./SetWormholeAddress";
/** Decode a governance payload */
export function decodeGovernancePayload(
@ -50,6 +51,8 @@ export function decodeGovernancePayload(
return SetValidPeriod.decode(data);
case "RequestGovernanceDataSourceTransfer":
return RequestGovernanceDataSourceTransfer.decode(data);
case "SetWormholeAddress":
return EvmSetWormholeAddress.decode(data);
default:
return undefined;
}
@ -57,3 +60,10 @@ export function decodeGovernancePayload(
export { ExecutePostedVaa } from "./ExecutePostedVaa";
export * from "./PythGovernanceAction";
export * from "./UpgradeContract";
export * from "./PythGovernanceAction";
export * from "./GovernanceDataSourceTransfer";
export * from "./SetDataSources";
export * from "./SetValidPeriod";
export * from "./SetFee";
export * from "./SetWormholeAddress";

View File

@ -11,3 +11,4 @@ export * from "./cranks";
export * from "./message_buffer";
export * from "./contracts";
export * from "./executor";
export * from "./chains";

View File

@ -9,7 +9,7 @@ USER 1000
COPY --chown=1000:1000 governance/xc_admin governance/xc_admin
COPY --chown=1000:1000 pythnet/message_buffer pythnet/message_buffer
COPY --chown=1000:1000 target_chains/ethereum/sdk/solidity target_chains/ethereum/sdk/solidity
COPY --chown=1000:1000 governance/xc_governance_sdk_js governance/xc_governance_sdk_js
COPY --chown=1000:1000 governance/xc_admin/packages/xc_admin_common governance/xc_admin/packages/xc_admin_common
ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1

View File

@ -1,11 +0,0 @@
module.exports = {
root: true,
parser: "@typescript-eslint/parser",
plugins: ["@typescript-eslint"],
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
rules: {
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-empty-function": "off",
},
};

View File

@ -1,3 +0,0 @@
node_modules
lib
.dccache

View File

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [2021] [Pyth Data Foundation]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,5 +0,0 @@
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
};

View File

@ -1,42 +0,0 @@
{
"name": "@pythnetwork/xc-governance-sdk",
"version": "0.4.0",
"description": "Pyth Cross-chain Governance SDK",
"private": "true",
"homepage": "https://pyth.network",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"files": [
"lib/**/*"
],
"repository": "https://github.com/pyth-network/pyth-crosschain/",
"scripts": {
"test": "jest src/ --passWithNoTests",
"build": "tsc",
"format": "prettier --write \"src/**/*.ts\"",
"lint": "eslint src/",
"prepublishOnly": "npm run build && npm test && npm run lint",
"preversion": "npm run lint",
"version": "npm run format && git add -A src"
},
"keywords": [
"pyth",
"oracle"
],
"license": "Apache-2.0",
"devDependencies": {
"@types/jest": "^28.0.8",
"@typescript-eslint/eslint-plugin": "^5.36.2",
"@typescript-eslint/parser": "^5.36.2",
"eslint": "^8.23.0",
"jest": "^28.0.8",
"prettier": "^2.7.1",
"ts-jest": "^28.0.8",
"ts-node": "^10.9.1",
"typescript": "^4.8.3"
},
"dependencies": {
"@certusone/wormhole-sdk": "^0.6.2",
"ethers": "^5.7.0"
}
}

View File

@ -1,24 +0,0 @@
export {
DataSource,
AptosAuthorizeUpgradeContractInstruction,
SuiAuthorizeUpgradeContractInstruction,
EthereumUpgradeContractInstruction,
EthereumSetWormholeAddress,
HexString20Bytes,
HexString32Bytes,
SetDataSourcesInstruction,
SetFeeInstruction,
SetValidPeriodInstruction,
RequestGovernanceDataSourceTransferInstruction,
AuthorizeGovernanceDataSourceTransferInstruction,
CosmwasmUpgradeContractInstruction,
Instruction,
} from "./instructions";
export {
WORMHOLE_CHAINS,
RECEIVER_CHAINS,
CHAINS,
ChainId,
ChainName,
} from "./chains";

View File

@ -1,217 +0,0 @@
import { ChainId } from "./chains";
import { Serializable, BufferBuilder } from "./serialize";
enum Module {
Executor = 0,
Target,
}
enum TargetAction {
UpgradeContract = 0,
AuthorizeGovernanceDataSourceTransfer,
SetDataSources,
SetFee,
SetValidPeriod,
RequestGovernanceDataSourceTransfer,
SetWormholeAddress,
}
abstract class HexString implements Serializable {
private readonly addressBuffer: Buffer;
constructor(address: string, byteLen: number) {
if (address.startsWith("0x")) {
address = address.substring(2);
}
if (address.length !== 2 * byteLen) {
throw new Error(
`Expected address of length ${2 * byteLen}, found ${address.length}`
);
}
this.addressBuffer = Buffer.from(address, "hex");
if (this.addressBuffer.length === 0) {
throw new Error(`Given address is not in hex format`);
}
}
serialize(): Buffer {
return this.addressBuffer;
}
}
export class HexString20Bytes extends HexString {
constructor(address: string) {
super(address, 20);
}
}
export class HexString32Bytes extends HexString {
constructor(address: string) {
super(address, 32);
}
}
export class DataSource implements Serializable {
constructor(
private readonly emitterChain: ChainId,
private readonly emitterAddress: HexString32Bytes
) {}
serialize(): Buffer {
return new BufferBuilder()
.addUint16(Number(this.emitterChain))
.addObject(this.emitterAddress)
.build();
}
}
// Magic is `PTGM` encoded as a 4 byte data: Pyth Governance Message
const MAGIC = 0x5054474d;
export abstract class Instruction implements Serializable {
constructor(
private module: Module,
private action: number,
private targetChainId: ChainId
) {}
protected abstract serializePayload(): Buffer;
private serializeHeader(): Buffer {
return new BufferBuilder()
.addUint32(MAGIC)
.addUint8(this.module)
.addUint8(this.action)
.addUint16(Number(this.targetChainId))
.build();
}
public serialize(): Buffer {
return new BufferBuilder()
.addBuffer(this.serializeHeader())
.addBuffer(this.serializePayload())
.build();
}
}
abstract class TargetInstruction extends Instruction {
constructor(action: TargetAction, targetChainId: ChainId) {
super(Module.Target, Number(action), targetChainId);
}
}
export class AptosAuthorizeUpgradeContractInstruction extends TargetInstruction {
constructor(targetChainId: ChainId, private hash: HexString32Bytes) {
super(TargetAction.UpgradeContract, targetChainId);
}
protected serializePayload(): Buffer {
return this.hash.serialize();
}
}
export class SuiAuthorizeUpgradeContractInstruction extends TargetInstruction {
constructor(targetChainId: ChainId, private digest: HexString32Bytes) {
super(TargetAction.UpgradeContract, targetChainId);
}
protected serializePayload(): Buffer {
return this.digest.serialize();
}
}
export class EthereumUpgradeContractInstruction extends TargetInstruction {
constructor(targetChainId: ChainId, private address: HexString20Bytes) {
super(TargetAction.UpgradeContract, targetChainId);
}
protected serializePayload(): Buffer {
return this.address.serialize();
}
}
export class CosmwasmUpgradeContractInstruction extends TargetInstruction {
constructor(targetChainId: ChainId, private codeId: bigint) {
super(TargetAction.UpgradeContract, targetChainId);
}
protected serializePayload(): Buffer {
return new BufferBuilder().addBigUint64(this.codeId).build();
}
}
export class AuthorizeGovernanceDataSourceTransferInstruction extends TargetInstruction {
constructor(targetChainId: ChainId, private claimVaa: Buffer) {
super(TargetAction.AuthorizeGovernanceDataSourceTransfer, targetChainId);
}
protected serializePayload(): Buffer {
return this.claimVaa;
}
}
export class SetDataSourcesInstruction extends TargetInstruction {
constructor(targetChainId: ChainId, private dataSources: DataSource[]) {
super(TargetAction.SetDataSources, targetChainId);
}
protected serializePayload(): Buffer {
const builder = new BufferBuilder();
builder.addUint8(this.dataSources.length);
this.dataSources.forEach((datasource) => builder.addObject(datasource));
return builder.build();
}
}
export class SetFeeInstruction extends TargetInstruction {
constructor(
targetChainId: ChainId,
private newFeeValue: bigint,
private newFeeExpo: bigint
) {
super(TargetAction.SetFee, targetChainId);
}
protected serializePayload(): Buffer {
return new BufferBuilder()
.addBigUint64(this.newFeeValue)
.addBigUint64(this.newFeeExpo)
.build();
}
}
export class SetValidPeriodInstruction extends TargetInstruction {
constructor(targetChainId: ChainId, private newValidPeriod: bigint) {
super(TargetAction.SetValidPeriod, targetChainId);
}
protected serializePayload(): Buffer {
return new BufferBuilder().addBigUint64(this.newValidPeriod).build();
}
}
export class RequestGovernanceDataSourceTransferInstruction extends TargetInstruction {
constructor(
targetChainId: ChainId,
private governanceDataSourceIndex: number
) {
super(TargetAction.RequestGovernanceDataSourceTransfer, targetChainId);
}
protected serializePayload(): Buffer {
return new BufferBuilder()
.addUint32(this.governanceDataSourceIndex)
.build();
}
}
export class EthereumSetWormholeAddress extends TargetInstruction {
constructor(targetChainId: ChainId, private address: HexString20Bytes) {
super(TargetAction.SetWormholeAddress, targetChainId);
}
protected serializePayload(): Buffer {
return this.address.serialize();
}
}

View File

@ -1,63 +0,0 @@
export interface Serializable {
serialize(): Buffer;
}
export class BufferBuilder {
private items: Buffer[];
constructor() {
this.items = [];
}
addUint8(value: number): BufferBuilder {
const buffer = Buffer.alloc(1);
buffer.writeUint8(value);
this.items.push(buffer);
return this;
}
addUint16(value: number): BufferBuilder {
const buffer = Buffer.alloc(2);
buffer.writeUint16BE(value);
this.items.push(buffer);
return this;
}
addUint32(value: number): BufferBuilder {
const buffer = Buffer.alloc(4);
buffer.writeUint32BE(value);
this.items.push(buffer);
return this;
}
addBigUint64(value: bigint): BufferBuilder {
const buffer = Buffer.alloc(8);
buffer.writeBigInt64BE(value);
this.items.push(buffer);
return this;
}
addObject(obj: Serializable): BufferBuilder {
this.items.push(obj.serialize());
return this;
}
addBuffer(buffer: Buffer): BufferBuilder {
this.items.push(buffer);
return this;
}
build(): Buffer {
const totalLength = this.items.reduce((prev, cur) => prev + cur.length, 0);
const result = Buffer.alloc(totalLength);
let offset = 0;
for (const arr of this.items) {
result.set(arr, offset);
offset += arr.length;
}
return result;
}
}

View File

@ -1,9 +0,0 @@
{
"extends": "../../tsconfig.base.json",
"include": ["src"],
"exclude": ["node_modules", "**/__tests__/*"],
"compilerOptions": {
"rootDir": "src/",
"outDir": "./lib"
}
}

1846
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,6 @@
"name": "root",
"workspaces": [
"governance/xc_admin/packages/*",
"governance/xc_governance_sdk_js",
"governance/multisig_wh_message_builder",
"price_pusher",
"price_service/server",

View File

@ -19,7 +19,7 @@
"@injectivelabs/networks": "1.0.68",
"@injectivelabs/sdk-ts": "1.0.354",
"@ltd/j-toml": "^1.38.0",
"@pythnetwork/xc-governance-sdk": "*",
"xc_admin_common": "*",
"@terra-money/terra.js": "^3.1.3",
"adm-zip": "^0.5.10",
"chain-registry": "^1.6.0",

View File

@ -93,8 +93,8 @@ export const CHAINS_NETWORK_CONFIG: Record<ChainId, ChainNetworkConfig> = {
[ChainId.NEUTRON_TESTNET_PION_1]: {
chainId: ChainId.NEUTRON_TESTNET_PION_1,
chainType: ChainType.COSMWASM,
executorEndpoint: "https://rpc.pion.rs-testnet.polypore.xyz/",
querierEndpoint: "https://rpc.pion.rs-testnet.polypore.xyz/",
executorEndpoint: "https://rpc-palvus.pion-1.ntrn.tech/",
querierEndpoint: "https://rpc-palvus.pion-1.ntrn.tech/",
prefix: "neutron",
gasPrice: "0.025untrn",
},

View File

@ -1,4 +1,4 @@
import { CHAINS } from "@pythnetwork/xc-governance-sdk";
import { CHAINS } from "xc_admin_common";
import { ChainId } from "./chains-manager/chains";
import { DeploymentType } from "./helper";

View File

@ -2,21 +2,21 @@
"deploy-pyth-code": {
"status": "fulfilled",
"result": {
"codeId": 473,
"txHash": "73EFB113F0AA5D4C7DC98E96F1452E5DCC1C8C43F335CADEB7351777F0D7F7F1"
"codeId": 1175,
"txHash": "5A9961A345856E14C2F42D04A881BEF1678A90DEDCC5C9F7B87EE2F6CAA7CA0C"
}
},
"instantiate-contract": {
"status": "fulfilled",
"result": {
"contractAddr": "neutron1xxmcu6wxgawjlajx8jalyk9cxsudnygjg0tvjesfyurh4utvtpes5wmpjp",
"txHash": "CD0D6CB6C80757DB1CBC6B4FF3E1C7526DFD4F8877743A04EB77B5848195D3FF"
"contractAddr": "neutron16zwrmx3zgggmxhzau86xfycm42cr4sj888hdvzsxya3qarp6zhhqzhlkvz",
"txHash": "1B483BFC11C7D155167E8BAB1D4083B685B69E3A7667CA541C9AE7B9F8C8611B"
}
},
"set-own-admin": {
"status": "fulfilled",
"result": {
"txHash": "B26BCEB73B826C797B78BE1E1AD35BC23752E3E670E106A396CD1D1648089304"
"txHash": "5E160580E230DA5ADD0576ACFDEA02FD0776E272AB84E0AE4C520DD8E5CF70C5"
}
}
}

View File

@ -2,7 +2,7 @@
"push-price-update": {
"status": "fulfilled",
"result": {
"txHash": "20B98E3DD44B950E390303281A862734C4DB4BC5252588B02DB7BD0652637E51"
"txHash": "ECA134B6CB6C9240AA80B49CF15C8215CA08949C3F68B7720D7E8B9B8E998E41"
}
},
"fetch-price-feed-update": {
@ -11,16 +11,16 @@
"price_feed": {
"id": "f9c0172ba10dfa4d19088d94f5bf61d3b54d5bd7483a322a982e1373ee8ea31b",
"price": {
"price": "2628800000000",
"conf": "988214743",
"price": "3012903119923",
"conf": "722024077",
"expo": -8,
"publish_time": 1684939947
"publish_time": 1689590826
},
"ema_price": {
"price": "2646641040000",
"conf": "828376020",
"price": "3017327600000",
"conf": "741279200",
"expo": -8,
"publish_time": 1684939947
"publish_time": 1689590826
}
}
}

View File

@ -2,21 +2,21 @@
"deploy-pyth-code": {
"status": "fulfilled",
"result": {
"codeId": 472,
"txHash": "00A7125012E7B5ECE6DB5938DC177B37A37C0AEC42B08D3D5A7B3CC9C1A2161D"
"codeId": 1176,
"txHash": "E71CB5A163B58222D0458F4C661369D00190C81FF6BBF0C6A5442CE5E8558235"
}
},
"instantiate-contract": {
"status": "fulfilled",
"result": {
"contractAddr": "neutron1f86ct5az9qpz2hqfd5uxru02px2a3tz5zkw7hugd7acqq496dcms22ehpy",
"txHash": "3DEBFABE0AFB4BB2F9C40427DB2E2C91BC4F5BC34324FD3E3962158063BFB88D"
"contractAddr": "neutron15ldst8t80982akgr8w8ekcytejzkmfpgdkeq4xgtge48qs7435jqp87u3t",
"txHash": "EA0B91043F0281D4A4E58D9749C109304BEC3C5A309E70C3918F5C0F8951A079"
}
},
"set-own-admin": {
"status": "fulfilled",
"result": {
"txHash": "5D4E120C11134B610DD15BD3C32F834300D4662358E10BBABD9907961E34DFF1"
"txHash": "572A32BCFF99FB5A83DD5B50A77A20214FA54064F2D4FEA3BD8E70F8372FEA02"
}
}
}

View File

@ -2,7 +2,7 @@
"push-price-update": {
"status": "fulfilled",
"result": {
"txHash": "C236ECE2ACA96AE4325AB02C7FCF7BFE5974C3B2D4BF52903FB4F483F1CA5970"
"txHash": "0747A3C2A608C154B291BB11C621E1C7C3F30A1356FAA051E138659471765F4E"
}
},
"fetch-price-feed-update": {
@ -11,16 +11,16 @@
"price_feed": {
"id": "e62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43",
"price": {
"price": "2630007893200",
"conf": "1119111601",
"price": "3015369873290",
"conf": "941626709",
"expo": -8,
"publish_time": 1684939862
"publish_time": 1689590919
},
"ema_price": {
"price": "2648559910000",
"conf": "802705780",
"price": "3017698600000",
"conf": "899541390",
"expo": -8,
"publish_time": 1684939862
"publish_time": 1689590919
}
}
}

View File

@ -8,7 +8,7 @@ MIGRATIONS_NETWORK= # xyz
# This value should derive from Pyth to wormhole latency, and target chain blocktime and latency.
VALID_TIME_PERIOD_SECONDS= # 60
WORMHOLE_CHAIN_NAME= # ethereum, defined in <repo-root>/third_party/pyth/xc_governance_sdk_js/src/chains.ts
WORMHOLE_CHAIN_NAME= # ethereum, defined in <repo-root>/governance/xc_admin/packages/xc_admin_common/src/chains.ts
CLUSTER= #mainnet/testnet The configs below are read from the cluster file

View File

@ -26,7 +26,7 @@ Each network that Pyth is deployed on has some configuration stored on this repo
- `MIGRATIONS_NETWORK`: Network name in the [`truffle-config.js`](./truffle-config.js) file.
- `WORMHOLE_CHAIN_NAME`: Chain name in Wormhole. It is either defined in the
[Wormhole SDK constants](https://github.com/wormhole-foundation/wormhole/blob/dev.v2/sdk/js/src/utils/consts.ts)
or is defined in [Wormhole Receiver names](../../../governance/xc_governance_sdk_js/src/chains.ts). If the new
or is defined in [Wormhole Receiver names](../../../governance/xc_admin/packages/xc_admin_common/src/chains.ts). If the new
network requires a Receiver contract you need to update the latter file and add the network there.
- `CLUSTER`: Cluster of this network. It is either `testnet` or `mainnet`. There are some cluster specific
configuration that are loaded from [`.env.cluster.testnet`](./.env.cluster.testnet) or
@ -69,7 +69,7 @@ This is the deployment process:
5. Make sure the deployment account has proper balance on this network and top it up if needed. Search
for testnet faucets if it is a testnet network. Sometimes you need to bridge the network token (e.g., L2s).
6. Deploy the new contract or changes using the [`deploy.sh`](./deploy.sh) script. If you have made changes
to [`chains.ts`](../../../governance/xc_governance_sdk_js/src/chains.ts), please make sure to
to [`chains.ts`](../../../governance/xc_admin/packages/xc_admin_common/src/chains.ts), please make sure to
run `npx lerna run build --scope="@pythnetwork/pyth-evm-contract" --include-dependencies` in the
root directory before running the deployment script.
You might need to repeat this script because of busy RPCs. Repeating would not cause any problem even

View File

@ -2,7 +2,7 @@ import { utils, Wallet } from "zksync-web3";
import { HardhatRuntimeEnvironment } from "hardhat/types";
import { Deployer } from "@matterlabs/hardhat-zksync-deploy";
import loadEnv from "../scripts/loadEnv";
import { CHAINS } from "@pythnetwork/xc-governance-sdk";
import { CHAINS } from "xc_admin_common";
import { assert } from "chai";
import { writeFileSync } from "fs";

View File

@ -2,7 +2,6 @@ import { utils, Wallet } from "zksync-web3";
import { HardhatRuntimeEnvironment } from "hardhat/types";
import { Deployer } from "@matterlabs/hardhat-zksync-deploy";
import loadEnv from "../scripts/loadEnv";
import { CHAINS } from "@pythnetwork/xc-governance-sdk";
import { assert } from "chai";
import { writeFileSync } from "fs";
import { ethers } from "ethers";

View File

@ -2,7 +2,7 @@ const loadEnv = require("../../scripts/loadEnv");
loadEnv("../../");
const tdr = require("truffle-deploy-registry");
const governance = require("@pythnetwork/xc-governance-sdk");
const governance = require("xc_admin_common");
const { assert } = require("chai");
const ReceiverSetup = artifacts.require("ReceiverSetup");

View File

@ -34,7 +34,7 @@
"@openzeppelin/hardhat-upgrades": "^1.22.1",
"@pythnetwork/pyth-multisig-wh-message-builder": "*",
"@pythnetwork/pyth-sdk-solidity": "^2.2.0",
"@pythnetwork/xc-governance-sdk": "*",
"xc_admin_common": "*",
"dotenv": "^10.0.0",
"elliptic": "^6.5.2",
"ethers": "^5.7.2",

View File

@ -11,7 +11,7 @@
* the previous step in the next run.
*/
const governance = require("@pythnetwork/xc-governance-sdk");
const governance = require("xc_admin_common");
const wormhole = require("@certusone/wormhole-sdk");
const assertVaaPayloadEquals = require("./assertVaaPayloadEquals");
const { assert } = require("chai");
@ -233,10 +233,10 @@ async function upgradeContract(proxy, desiredVersion) {
newImplementationAddress = newImplementation.address;
}
const upgradePayload = new governance.EthereumUpgradeContractInstruction(
governance.CHAINS[chainName],
new governance.HexString20Bytes(newImplementationAddress)
).serialize();
const upgradePayload = new governance.EvmUpgradeContract(
chainName,
newImplementationAddress.replace("0x", "")
).encode();
const upgradePayloadHex = upgradePayload.toString("hex");
@ -277,11 +277,11 @@ async function syncUpdateFee(proxy) {
`desired update fee: ${desiredUpdateFee}. Updating...`
);
const setFeePayload = new governance.SetFeeInstruction(
governance.CHAINS[chainName],
const setFeePayload = new governance.SetFee(
chainName,
BigInt(desiredUpdateFee),
BigInt(0)
).serialize();
).encode();
await createAndExecuteVaaFromPayloadThroughMultiSig(proxy, setFeePayload);
@ -308,10 +308,10 @@ async function syncValidTimePeriod(proxy) {
`desired valid time period: ${desiredValidTimePeriod}s. Updating...`
);
const setValidPeriodPayload = new governance.SetValidPeriodInstruction(
governance.CHAINS[chainName],
const setValidPeriodPayload = new governance.SetValidPeriod(
chainName,
BigInt(desiredValidTimePeriod)
).serialize();
).encode();
await createAndExecuteVaaFromPayloadThroughMultiSig(
proxy,
@ -357,16 +357,13 @@ async function syncDataSources(proxy) {
// Usually this change is universal, so the Payload is generated for all
// the chains.
const setDataSourcesPayload = new governance.SetDataSourcesInstruction(
governance.CHAINS[chainName],
Array.from(desiredDataSources).map(
(ds) =>
new governance.DataSource(
Number(ds[0]),
new governance.HexString32Bytes(ds[1])
)
)
).serialize();
const setDataSourcesPayload = new governance.SetDataSources(
chainName,
Array.from(desiredDataSources).map((ds) => ({
emitterChain: Number(ds[0]),
emitterAddress: ds[1].replace("0x", ""),
}))
).encode();
await createAndExecuteVaaFromPayloadThroughMultiSig(
proxy,
setDataSourcesPayload

View File

@ -1,5 +1,5 @@
const elliptic = require("elliptic");
const governance = require("@pythnetwork/xc-governance-sdk");
const governance = require("xc_admin_common");
const { deployProxy, upgradeProxy } = require("@openzeppelin/truffle-upgrades");
const {
@ -8,6 +8,7 @@ const {
time,
} = require("@openzeppelin/test-helpers");
const { assert, expect } = require("chai");
const { EvmSetWormholeAddress } = require("xc_admin_common");
// Use "WormholeReceiver" if you are testing with Wormhole Receiver
const Setup = artifacts.require("Setup");
@ -226,7 +227,7 @@ contract("Pyth", function () {
* Create a governance instruction VAA from the Instruction object. Then
* Submit and execute it on the contract.
* @param contract Pyth contract
* @param {governance.Instruction} governanceInstruction
* @param {governance.PythGovernanceAction} governanceInstruction
* @param {number} sequence
*/
async function createAndThenSubmitGovernanceInstructionVaa(
@ -236,7 +237,7 @@ contract("Pyth", function () {
) {
await contract.executeGovernanceInstruction(
await createVAAFromUint8Array(
governanceInstruction.serialize(),
governanceInstruction.encode(),
testGovernanceChainId,
testGovernanceEmitter,
sequence
@ -299,11 +300,7 @@ contract("Pyth", function () {
async function setFeeTo(contract, newFee, governanceSequence) {
await createAndThenSubmitGovernanceInstructionVaa(
contract,
new governance.SetFeeInstruction(
governance.CHAINS.ethereum,
BigInt(newFee),
BigInt(0)
),
new governance.SetFee("ethereum", BigInt(newFee), BigInt(0)),
governanceSequence ?? 1
);
}
@ -514,10 +511,7 @@ contract("Pyth", function () {
) {
await createAndThenSubmitGovernanceInstructionVaa(
contract,
new governance.SetValidPeriodInstruction(
governance.CHAINS.ethereum,
BigInt(newValidPeriod)
),
new governance.SetValidPeriod("ethereum", BigInt(newValidPeriod)),
governanceSequence ?? 1
);
}
@ -606,10 +600,7 @@ contract("Pyth", function () {
// Logics that apply to all governance messages
it("Make sure invalid magic and module won't work", async function () {
// First 4 bytes of data are magic and the second byte after that is module
const data = new governance.SetValidPeriodInstruction(
governance.CHAINS.ethereum,
BigInt(10)
).serialize();
const data = new governance.SetValidPeriod("ethereum", BigInt(10)).encode();
const wrongMagic = Buffer.from(data);
wrongMagic[1] = 0;
@ -658,10 +649,7 @@ contract("Pyth", function () {
});
it("Make sure governance with wrong sender won't work", async function () {
const data = new governance.SetValidPeriodInstruction(
governance.CHAINS.ethereum,
BigInt(10)
).serialize();
const data = new governance.SetValidPeriod("ethereum", BigInt(10)).encode();
const vaaWrongEmitter = await createVAAFromUint8Array(
data,
@ -689,10 +677,10 @@ contract("Pyth", function () {
});
it("Make sure governance with only target chain id and 0 work", async function () {
const wrongChainData = new governance.SetValidPeriodInstruction(
governance.CHAINS.solana,
const wrongChainData = new governance.SetValidPeriod(
"solana",
BigInt(10)
).serialize();
).encode();
const wrongChainVaa = await createVAAFromUint8Array(
wrongChainData,
@ -706,10 +694,10 @@ contract("Pyth", function () {
"InvalidGovernanceTarget"
);
const dataForAllChains = new governance.SetValidPeriodInstruction(
governance.CHAINS.unset,
const dataForAllChains = new governance.SetValidPeriod(
"unset",
BigInt(10)
).serialize();
).encode();
const vaaForAllChains = await createVAAFromUint8Array(
dataForAllChains,
@ -720,10 +708,10 @@ contract("Pyth", function () {
await this.pythProxy.executeGovernanceInstruction(vaaForAllChains);
const dataForEth = new governance.SetValidPeriodInstruction(
governance.CHAINS.ethereum,
const dataForEth = new governance.SetValidPeriod(
"ethereum",
BigInt(10)
).serialize();
).encode();
const vaaForEth = await createVAAFromUint8Array(
dataForEth,
@ -736,10 +724,7 @@ contract("Pyth", function () {
});
it("Make sure that governance messages are executed in order and cannot be reused", async function () {
const data = new governance.SetValidPeriodInstruction(
governance.CHAINS.ethereum,
BigInt(10)
).serialize();
const data = new governance.SetValidPeriod("ethereum", BigInt(10)).encode();
const vaaSeq1 = await createVAAFromUint8Array(
data,
@ -778,10 +763,10 @@ contract("Pyth", function () {
it("Upgrading the contract with chain id 0 is invalid", async function () {
const newImplementation = await PythUpgradable.new();
const data = new governance.EthereumUpgradeContractInstruction(
governance.CHAINS.unset, // 0
new governance.HexString20Bytes(newImplementation.address)
).serialize();
const data = new governance.EvmUpgradeContract(
"unset", // 0
newImplementation.address.replace("0x", "")
).encode();
const vaa = await createVAAFromUint8Array(
data,
@ -799,10 +784,10 @@ contract("Pyth", function () {
it("Upgrading the contract should work", async function () {
const newImplementation = await PythUpgradable.new();
const data = new governance.EthereumUpgradeContractInstruction(
governance.CHAINS.ethereum,
new governance.HexString20Bytes(newImplementation.address)
).serialize();
const data = new governance.EvmUpgradeContract(
"ethereum",
newImplementation.address.replace("0x", "")
).encode();
const vaa = await createVAAFromUint8Array(
data,
@ -825,10 +810,10 @@ contract("Pyth", function () {
it("Upgrading the contract to a non-pyth contract won't work", async function () {
const newImplementation = await MockUpgradeableProxy.new();
const data = new governance.EthereumUpgradeContractInstruction(
governance.CHAINS.ethereum,
new governance.HexString20Bytes(newImplementation.address)
).serialize();
const data = new governance.EvmUpgradeContract(
"ethereum",
newImplementation.address.replace("0x", "")
).encode();
const vaa = await createVAAFromUint8Array(
data,
@ -850,10 +835,7 @@ contract("Pyth", function () {
const newEmitterChain = governance.CHAINS.acala;
const claimInstructionData =
new governance.RequestGovernanceDataSourceTransferInstruction(
governance.CHAINS.unset,
1
).serialize();
new governance.RequestGovernanceDataSourceTransfer("unset", 1).encode();
const claimVaaHexString = await createVAAFromUint8Array(
claimInstructionData,
@ -869,11 +851,10 @@ contract("Pyth", function () {
const claimVaa = Buffer.from(claimVaaHexString.substring(2), "hex");
const data =
new governance.AuthorizeGovernanceDataSourceTransferInstruction(
governance.CHAINS.unset,
claimVaa
).serialize();
const data = new governance.AuthorizeGovernanceDataSourceTransfer(
"unset",
claimVaa
).encode();
const vaa = await createVAAFromUint8Array(
data,
@ -904,11 +885,10 @@ contract("Pyth", function () {
// Make sure a claim vaa does not get executed
const claimLonely =
new governance.RequestGovernanceDataSourceTransferInstruction(
governance.CHAINS.unset,
2
).serialize();
const claimLonely = new governance.RequestGovernanceDataSourceTransfer(
"unset",
2
).encode();
const claimLonelyVaa = await createVAAFromUint8Array(
claimLonely,
@ -927,10 +907,10 @@ contract("Pyth", function () {
// A wrong vaa that does not move the governance index
const transferBackClaimInstructionDataWrong =
new governance.RequestGovernanceDataSourceTransferInstruction(
governance.CHAINS.unset,
new governance.RequestGovernanceDataSourceTransfer(
"unset",
1 // The same governance data source index => Should fail
).serialize();
).encode();
const transferBackClaimVaaHexStringWrong = await createVAAFromUint8Array(
transferBackClaimInstructionDataWrong,
@ -945,10 +925,10 @@ contract("Pyth", function () {
);
const transferBackDataWrong =
new governance.AuthorizeGovernanceDataSourceTransferInstruction(
governance.CHAINS.unset,
new governance.AuthorizeGovernanceDataSourceTransfer(
"unset",
transferBackClaimVaaWrong
).serialize();
).encode();
const transferBackVaaWrong = await createVAAFromUint8Array(
transferBackDataWrong,
@ -964,17 +944,13 @@ contract("Pyth", function () {
});
it("Setting data sources should work", async function () {
const data = new governance.SetDataSourcesInstruction(
governance.CHAINS.ethereum,
[
new governance.DataSource(
governance.CHAINS.acala,
new governance.HexString32Bytes(
"0x0000000000000000000000000000000000000000000000000000000000001111"
)
),
]
).serialize();
const data = new governance.SetDataSources("ethereum", [
{
emitterChain: governance.CHAINS.acala,
emitterAddress:
"0000000000000000000000000000000000000000000000000000000000001111",
},
]).encode();
const vaa = await createVAAFromUint8Array(
data,
@ -1020,11 +996,11 @@ contract("Pyth", function () {
});
it("Setting fee should work", async function () {
const data = new governance.SetFeeInstruction(
governance.CHAINS.ethereum,
const data = new governance.SetFee(
"ethereum",
BigInt(5),
BigInt(3) // 5*10**3 = 5000
).serialize();
).encode();
const vaa = await createVAAFromUint8Array(
data,
@ -1053,10 +1029,7 @@ contract("Pyth", function () {
});
it("Setting valid period should work", async function () {
const data = new governance.SetValidPeriodInstruction(
governance.CHAINS.ethereum,
BigInt(0)
).serialize();
const data = new governance.SetValidPeriod("ethereum", BigInt(0)).encode();
const vaa = await createVAAFromUint8Array(
data,
@ -1098,10 +1071,10 @@ contract("Pyth", function () {
const newWormhole = await Wormhole.new(newSetup.address, initData);
// Creating the vaa to set the new wormhole address
const data = new governance.EthereumSetWormholeAddress(
governance.CHAINS.ethereum,
new governance.HexString20Bytes(newWormhole.address)
).serialize();
const data = new governance.EvmSetWormholeAddress(
"ethereum",
newWormhole.address.replace("0x", "")
).encode();
const vaa = await createVAAFromUint8Array(
data,
@ -1146,10 +1119,10 @@ contract("Pyth", function () {
);
// Creating the vaa to set the new wormhole address
const data = new governance.EthereumSetWormholeAddress(
governance.CHAINS.ethereum,
new governance.HexString20Bytes(newWormholeReceiver.address)
).serialize();
const data = new governance.EvmSetWormholeAddress(
"ethereum",
newWormholeReceiver.address.replace("0x", "")
).encode();
const vaa = await createVAAFromUint8Array(
data,
@ -1191,10 +1164,10 @@ contract("Pyth", function () {
const newWormhole = await Wormhole.new(newSetup.address, initData);
// Creating the vaa to set the new wormhole address
const data = new governance.EthereumSetWormholeAddress(
governance.CHAINS.ethereum,
new governance.HexString20Bytes(newWormhole.address)
).serialize();
const data = new governance.EvmSetWormholeAddress(
"ethereum",
newWormhole.address.replace("0x", "")
).encode();
const wrongVaa = await createVAAFromUint8Array(
data,

View File

@ -37,7 +37,7 @@ COPY --from=pyth_builder /code/artifacts/pyth_cosmwasm.wasm /home/node/target_ch
WORKDIR /home/node/
COPY --chown=1000:1000 governance/xc_governance_sdk_js governance/xc_governance_sdk_js
COPY --chown=1000:1000 governance/xc_admin/packages/xc_admin_common/ governance/xc_admin/packages/xc_admin_common/
COPY --chown=1000:1000 target_chains/cosmwasm/tools target_chains/cosmwasm/tools
RUN npx lerna run build --scope="@pythnetwork/cosmwasm-deploy-tools" --include-dependencies

View File

@ -8,7 +8,7 @@ RUN apt-get update && apt-get install -y ncat
USER 1000
WORKDIR /home/node
COPY --chown=1000:1000 governance/multisig_wh_message_builder governance/multisig_wh_message_builder
COPY --chown=1000:1000 governance/xc_governance_sdk_js governance/xc_governance_sdk_js
COPY --chown=1000:1000 governance/xc_admin/packages/xc_admin_common/ governance/xc_admin/packages/xc_admin_common/
COPY --chown=1000:1000 target_chains/ethereum/sdk/solidity target_chains/ethereum/sdk/solidity
COPY --chown=1000:1000 target_chains/ethereum/contracts target_chains/ethereum/contracts