diff --git a/contract_manager/scripts/generate_upgrade_near_contract_proposal.ts b/contract_manager/scripts/generate_upgrade_near_contract_proposal.ts new file mode 100644 index 0000000000..d4241b7323 --- /dev/null +++ b/contract_manager/scripts/generate_upgrade_near_contract_proposal.ts @@ -0,0 +1,70 @@ +import yargs from "yargs"; +import { hideBin } from "yargs/helpers"; +import { DefaultStore, loadHotWallet } from "../src"; +import { NearChain } from "../src/chains"; + +const parser = yargs(hideBin(process.argv)) + .usage( + "Creates a governance proposal to upgrade the price feeds contract on Near.\n" + + "Usage: $0 --network --code-hash --ops-key-path \n" + ) + .options({ + network: { + type: "string", + choices: ["mainnet", "testnet"], + description: "Network to deploy to", + demandOption: true, + }, + "code-hash": { + type: "string", + description: "Sha-256 HEX of the wasm file", + demandOption: true, + }, + "ops-key-path": { + type: "string", + description: "Path to operations key file", + demandOption: true, + }, + }); + +async function main() { + const argv = await parser.argv; + + // Near wormhole contracts have the same id on testnet and mainnet. + const chain = DefaultStore.chains.near; + if (!(chain instanceof NearChain)) { + throw new Error("Near chain is missing"); + } + + const vault = + DefaultStore.vaults[ + argv.network === "mainnet" + ? "mainnet-beta_FVQyHcooAtThJ83XFrNnv74BcinbRH3bRmfFamAHBfuj" + : "devnet_6baWtW1zTUVMSJHJQVxDUXWzqrQeYBr6mu31j3bTKwY3" + ]; + + const codeHash = argv["code-hash"]; + if (Buffer.from(codeHash, "hex").length != 32) { + throw new Error("invalid code hash format"); + } + console.log( + `Upgrading contract on Near ${argv.network} to code hash: ${codeHash}` + ); + + // Generate governance payload for the upgrade + const payload = chain.generateGovernanceUpgradePayload(codeHash); + console.log("Governance payload:", payload); + + // Create and submit governance proposal + console.log("Using vault for proposal:", vault.getId()); + const keypair = await loadHotWallet(argv["ops-key-path"] as string); + console.log("Using wallet:", keypair.publicKey.toBase58()); + vault.connect(keypair); + const proposal = await vault.proposeWormholeMessage([payload]); + console.log("Proposal address:", proposal.address.toBase58()); +} + +main().catch((error) => { + console.error("Error during upgrade:", error); + process.exit(1); +}); diff --git a/contract_manager/src/contracts/near.ts b/contract_manager/src/contracts/near.ts index f5864074d6..966c34291a 100644 --- a/contract_manager/src/contracts/near.ts +++ b/contract_manager/src/contracts/near.ts @@ -88,7 +88,12 @@ export class NearWormholeContract extends WormholeContract { export class NearPriceFeedContract extends PriceFeedContract { public static type = "NearPriceFeedContract"; - constructor(public chain: NearChain, public address: string) { + constructor( + public chain: NearChain, + public address: string, + public governanceDataSource: DataSource, + public lastExecutedGovernanceSequence: number + ) { super(); } @@ -108,13 +113,22 @@ export class NearPriceFeedContract extends PriceFeedContract { return { chain: this.chain.getId(), address: this.address, + governanceDataSourceChain: this.governanceDataSource.emitterChain, + governanceDataSourceAddress: this.governanceDataSource.emitterAddress, + lastExecutedGovernanceSequence: this.lastExecutedGovernanceSequence, type: NearPriceFeedContract.type, }; } static fromJson( chain: Chain, - parsed: { type: string; address: string } + parsed: { + type: string; + address: string; + governanceDataSourceAddress: string; + governanceDataSourceChain: number; + lastExecutedGovernanceSequence: number; + } ): NearPriceFeedContract { if (parsed.type !== NearPriceFeedContract.type) { throw new Error("Invalid type"); @@ -122,7 +136,15 @@ export class NearPriceFeedContract extends PriceFeedContract { if (!(chain instanceof NearChain)) { throw new Error(`Wrong chain type ${chain}`); } - return new NearPriceFeedContract(chain, parsed.address); + return new NearPriceFeedContract( + chain, + parsed.address, + { + emitterAddress: parsed.governanceDataSourceAddress, + emitterChain: parsed.governanceDataSourceChain, + }, + parsed.lastExecutedGovernanceSequence + ); } async getContractNearAccount( @@ -251,14 +273,12 @@ export class NearPriceFeedContract extends PriceFeedContract { getBaseUpdateFee(): Promise<{ amount: string; denom?: string }> { throw new Error("near contract doesn't implement getBaseUpdateFee method"); } - getLastExecutedGovernanceSequence(): Promise { - throw new Error( - "near contract doesn't implement getLastExecutedGovernanceSequence method" - ); + async getLastExecutedGovernanceSequence(): Promise { + // near contract doesn't implement getLastExecutedGovernanceSequence method + return this.lastExecutedGovernanceSequence; } - getGovernanceDataSource(): Promise { - throw new Error( - "near contract doesn't implement getGovernanceDataSource method" - ); + async getGovernanceDataSource(): Promise { + // near contract doesn't implement getGovernanceDataSource method + return this.governanceDataSource; } } diff --git a/contract_manager/store/contracts/NearPriceFeedContracts.yaml b/contract_manager/store/contracts/NearPriceFeedContracts.yaml index f8a44b862a..b507051289 100644 --- a/contract_manager/store/contracts/NearPriceFeedContracts.yaml +++ b/contract_manager/store/contracts/NearPriceFeedContracts.yaml @@ -1,6 +1,12 @@ - chain: near address: pyth-oracle.near type: NearPriceFeedContract + governanceDataSourceChain: 1 + governanceDataSourceAddress: 5635979a221c34931e32620b9293a463065555ea71fe97cd6237ade875b12e9e + lastExecutedGovernanceSequence: 0 - chain: near_testnet address: pyth-oracle.testnet type: NearPriceFeedContract + governanceDataSourceChain: 1 + governanceDataSourceAddress: 63278d271099bfd491951b3e648f08b1c71631e4a53674ad43e8f9f98068c385 + lastExecutedGovernanceSequence: 100 diff --git a/governance/xc_admin/packages/xc_admin_common/src/chains.ts b/governance/xc_admin/packages/xc_admin_common/src/chains.ts index c2df9b3f0a..27e7b1d6b9 100644 --- a/governance/xc_admin/packages/xc_admin_common/src/chains.ts +++ b/governance/xc_admin/packages/xc_admin_common/src/chains.ts @@ -11,9 +11,6 @@ import { CHAINS as WORMHOLE_CHAINS } from "@certusone/wormhole-sdk"; export const RECEIVER_CHAINS = { unset: 0, // The global chain id. For messages that are not chain specific. - // On the following networks we use Wormhole's contract - near: 15, - // On the following networks we use our own version of Wormhole receiver contract ethereum: 2, bsc: 4,