Skip to content

Commit c26d575

Browse files
authored
feat(contract_manager): add support for near contract upgrade (#2393)
* feat(contract_manager): add near propose upgrade script * feat(contract_manager): manually track governance source and sequence in near contract * fix(governance/xc_admin): remove near from RECEIVER_CHAINS because it's already in WORMHOLE_CHAINS
1 parent a691e18 commit c26d575

File tree

4 files changed

+107
-14
lines changed

4 files changed

+107
-14
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import yargs from "yargs";
2+
import { hideBin } from "yargs/helpers";
3+
import { DefaultStore, loadHotWallet } from "../src";
4+
import { NearChain } from "../src/chains";
5+
6+
const parser = yargs(hideBin(process.argv))
7+
.usage(
8+
"Creates a governance proposal to upgrade the price feeds contract on Near.\n" +
9+
"Usage: $0 --network <mainnet|testnet> --code-hash <hash> --ops-key-path <ops_key_path>\n"
10+
)
11+
.options({
12+
network: {
13+
type: "string",
14+
choices: ["mainnet", "testnet"],
15+
description: "Network to deploy to",
16+
demandOption: true,
17+
},
18+
"code-hash": {
19+
type: "string",
20+
description: "Sha-256 HEX of the wasm file",
21+
demandOption: true,
22+
},
23+
"ops-key-path": {
24+
type: "string",
25+
description: "Path to operations key file",
26+
demandOption: true,
27+
},
28+
});
29+
30+
async function main() {
31+
const argv = await parser.argv;
32+
33+
// Near wormhole contracts have the same id on testnet and mainnet.
34+
const chain = DefaultStore.chains.near;
35+
if (!(chain instanceof NearChain)) {
36+
throw new Error("Near chain is missing");
37+
}
38+
39+
const vault =
40+
DefaultStore.vaults[
41+
argv.network === "mainnet"
42+
? "mainnet-beta_FVQyHcooAtThJ83XFrNnv74BcinbRH3bRmfFamAHBfuj"
43+
: "devnet_6baWtW1zTUVMSJHJQVxDUXWzqrQeYBr6mu31j3bTKwY3"
44+
];
45+
46+
const codeHash = argv["code-hash"];
47+
if (Buffer.from(codeHash, "hex").length != 32) {
48+
throw new Error("invalid code hash format");
49+
}
50+
console.log(
51+
`Upgrading contract on Near ${argv.network} to code hash: ${codeHash}`
52+
);
53+
54+
// Generate governance payload for the upgrade
55+
const payload = chain.generateGovernanceUpgradePayload(codeHash);
56+
console.log("Governance payload:", payload);
57+
58+
// Create and submit governance proposal
59+
console.log("Using vault for proposal:", vault.getId());
60+
const keypair = await loadHotWallet(argv["ops-key-path"] as string);
61+
console.log("Using wallet:", keypair.publicKey.toBase58());
62+
vault.connect(keypair);
63+
const proposal = await vault.proposeWormholeMessage([payload]);
64+
console.log("Proposal address:", proposal.address.toBase58());
65+
}
66+
67+
main().catch((error) => {
68+
console.error("Error during upgrade:", error);
69+
process.exit(1);
70+
});

contract_manager/src/contracts/near.ts

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,12 @@ export class NearWormholeContract extends WormholeContract {
8888
export class NearPriceFeedContract extends PriceFeedContract {
8989
public static type = "NearPriceFeedContract";
9090

91-
constructor(public chain: NearChain, public address: string) {
91+
constructor(
92+
public chain: NearChain,
93+
public address: string,
94+
public governanceDataSource: DataSource,
95+
public lastExecutedGovernanceSequence: number
96+
) {
9297
super();
9398
}
9499

@@ -108,21 +113,38 @@ export class NearPriceFeedContract extends PriceFeedContract {
108113
return {
109114
chain: this.chain.getId(),
110115
address: this.address,
116+
governanceDataSourceChain: this.governanceDataSource.emitterChain,
117+
governanceDataSourceAddress: this.governanceDataSource.emitterAddress,
118+
lastExecutedGovernanceSequence: this.lastExecutedGovernanceSequence,
111119
type: NearPriceFeedContract.type,
112120
};
113121
}
114122

115123
static fromJson(
116124
chain: Chain,
117-
parsed: { type: string; address: string }
125+
parsed: {
126+
type: string;
127+
address: string;
128+
governanceDataSourceAddress: string;
129+
governanceDataSourceChain: number;
130+
lastExecutedGovernanceSequence: number;
131+
}
118132
): NearPriceFeedContract {
119133
if (parsed.type !== NearPriceFeedContract.type) {
120134
throw new Error("Invalid type");
121135
}
122136
if (!(chain instanceof NearChain)) {
123137
throw new Error(`Wrong chain type ${chain}`);
124138
}
125-
return new NearPriceFeedContract(chain, parsed.address);
139+
return new NearPriceFeedContract(
140+
chain,
141+
parsed.address,
142+
{
143+
emitterAddress: parsed.governanceDataSourceAddress,
144+
emitterChain: parsed.governanceDataSourceChain,
145+
},
146+
parsed.lastExecutedGovernanceSequence
147+
);
126148
}
127149

128150
async getContractNearAccount(
@@ -251,14 +273,12 @@ export class NearPriceFeedContract extends PriceFeedContract {
251273
getBaseUpdateFee(): Promise<{ amount: string; denom?: string }> {
252274
throw new Error("near contract doesn't implement getBaseUpdateFee method");
253275
}
254-
getLastExecutedGovernanceSequence(): Promise<number> {
255-
throw new Error(
256-
"near contract doesn't implement getLastExecutedGovernanceSequence method"
257-
);
276+
async getLastExecutedGovernanceSequence(): Promise<number> {
277+
// near contract doesn't implement getLastExecutedGovernanceSequence method
278+
return this.lastExecutedGovernanceSequence;
258279
}
259-
getGovernanceDataSource(): Promise<DataSource> {
260-
throw new Error(
261-
"near contract doesn't implement getGovernanceDataSource method"
262-
);
280+
async getGovernanceDataSource(): Promise<DataSource> {
281+
// near contract doesn't implement getGovernanceDataSource method
282+
return this.governanceDataSource;
263283
}
264284
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
- chain: near
22
address: pyth-oracle.near
33
type: NearPriceFeedContract
4+
governanceDataSourceChain: 1
5+
governanceDataSourceAddress: 5635979a221c34931e32620b9293a463065555ea71fe97cd6237ade875b12e9e
6+
lastExecutedGovernanceSequence: 0
47
- chain: near_testnet
58
address: pyth-oracle.testnet
69
type: NearPriceFeedContract
10+
governanceDataSourceChain: 1
11+
governanceDataSourceAddress: 63278d271099bfd491951b3e648f08b1c71631e4a53674ad43e8f9f98068c385
12+
lastExecutedGovernanceSequence: 100

governance/xc_admin/packages/xc_admin_common/src/chains.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,6 @@ import { CHAINS as WORMHOLE_CHAINS } from "@certusone/wormhole-sdk";
1111
export const RECEIVER_CHAINS = {
1212
unset: 0, // The global chain id. For messages that are not chain specific.
1313

14-
// On the following networks we use Wormhole's contract
15-
near: 15,
16-
1714
// On the following networks we use our own version of Wormhole receiver contract
1815
ethereum: 2,
1916
bsc: 4,

0 commit comments

Comments
 (0)