Skip to content

Commit 4556454

Browse files
authored
add upgrade script to contract_manager (#2147)
1 parent d6ba2b6 commit 4556454

File tree

5 files changed

+148
-48
lines changed

5 files changed

+148
-48
lines changed
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import yargs from "yargs";
2+
import { hideBin } from "yargs/helpers";
3+
import { DefaultStore, loadHotWallet } from "../src";
4+
import { TonChain } from "../src/chains";
5+
import { CHAINS, toChainName } from "@pythnetwork/xc-admin-common";
6+
import fs from "fs";
7+
import path from "path";
8+
9+
const parser = yargs(hideBin(process.argv))
10+
.usage(
11+
"Upgrades the Pyth contract on TON and creates a governance proposal for it.\n" +
12+
"Usage: $0 --network <mainnet|testnet> --contract-address <address> --ops-key-path <ops_key_path>"
13+
)
14+
.options({
15+
network: {
16+
type: "string",
17+
choices: ["mainnet", "testnet"],
18+
description: "Network to deploy to",
19+
demandOption: true,
20+
},
21+
"contract-address": {
22+
type: "string",
23+
description: "Address of the contract to upgrade",
24+
demandOption: true,
25+
},
26+
"ops-key-path": {
27+
type: "string",
28+
description: "Path to operations key file",
29+
demandOption: true,
30+
},
31+
});
32+
33+
async function main() {
34+
const argv = await parser.argv;
35+
const isMainnet = argv.network === "mainnet";
36+
37+
// Get chain ID and name from CHAINS mapping
38+
const chainId = isMainnet ? CHAINS.ton_mainnet : CHAINS.ton_testnet;
39+
const wormholeChainName = toChainName(chainId);
40+
41+
// Get the TON chain instance with appropriate RPC URL based on network
42+
const chain = new TonChain(
43+
chainId.toString(),
44+
isMainnet,
45+
wormholeChainName,
46+
undefined,
47+
isMainnet
48+
? "https://toncenter.com/api/v2/jsonRPC"
49+
: "https://testnet.toncenter.com/api/v2/jsonRPC"
50+
);
51+
52+
const vault =
53+
DefaultStore.vaults[
54+
"mainnet-beta_FVQyHcooAtThJ83XFrNnv74BcinbRH3bRmfFamAHBfuj"
55+
];
56+
57+
console.log(
58+
`Upgrading contract on TON ${argv.network} (Chain ID: ${chainId}, Wormhole Chain Name: ${wormholeChainName})`
59+
);
60+
61+
// Read the compiled contract from the build directory
62+
// NOTE: Remember to rebuild contract_manager before running this script because it will also build the ton contract
63+
const compiledPath = path.resolve(
64+
__dirname,
65+
"../../target_chains/ton/contracts/build/Main.compiled.json"
66+
);
67+
const compiled = JSON.parse(fs.readFileSync(compiledPath, "utf8"));
68+
const newCodeHash = compiled.hash;
69+
console.log("New code hash:", newCodeHash);
70+
71+
// Generate governance payload for the upgrade
72+
const payload = chain.generateGovernanceUpgradePayload(newCodeHash);
73+
console.log("Generated governance payload");
74+
console.log("Payload:", payload);
75+
76+
// Create and submit governance proposal
77+
console.log("Using vault for proposal:", vault.getId());
78+
const keypair = await loadHotWallet(argv["ops-key-path"] as string);
79+
console.log("Using wallet:", keypair.publicKey.toBase58());
80+
vault.connect(keypair);
81+
const proposal = await vault.proposeWormholeMessage([payload]);
82+
console.log("Proposal address:", proposal.address.toBase58());
83+
}
84+
85+
main().catch((error) => {
86+
console.error("Error during upgrade:", error);
87+
process.exit(1);
88+
});

contract_manager/scripts/upgrade_ton_contract.ts

Lines changed: 17 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
import yargs from "yargs";
22
import { hideBin } from "yargs/helpers";
3-
import { DefaultStore, loadHotWallet } from "../src";
4-
import { TonChain } from "../src/chains";
5-
import { CHAINS, toChainName } from "@pythnetwork/xc-admin-common";
3+
import { DefaultStore, TonPriceFeedContract, toPrivateKey } from "../src";
64
import fs from "fs";
75
import path from "path";
6+
import { Cell } from "@ton/ton";
87

98
const parser = yargs(hideBin(process.argv))
109
.usage(
1110
"Upgrades the Pyth contract on TON and creates a governance proposal for it.\n" +
12-
"Usage: $0 --network <mainnet|testnet> --contract-address <address> --ops-key-path <ops_key_path>"
11+
"Usage: $0 --network <mainnet|testnet> --contract <contract_name> --private-key <private_key>"
1312
)
1413
.options({
1514
network: {
@@ -18,45 +17,24 @@ const parser = yargs(hideBin(process.argv))
1817
description: "Network to deploy to",
1918
demandOption: true,
2019
},
21-
"contract-address": {
20+
contract: {
2221
type: "string",
23-
description: "Address of the contract to upgrade",
22+
description: "Contract name",
2423
demandOption: true,
2524
},
26-
"ops-key-path": {
25+
"private-key": {
2726
type: "string",
28-
description: "Path to operations key file",
27+
description: "Private key of the sender",
2928
demandOption: true,
3029
},
3130
});
3231

3332
async function main() {
3433
const argv = await parser.argv;
35-
const isMainnet = argv.network === "mainnet";
3634

37-
// Get chain ID and name from CHAINS mapping
38-
const chainId = isMainnet ? CHAINS.ton_mainnet : CHAINS.ton_testnet;
39-
const wormholeChainName = toChainName(chainId);
40-
41-
// Get the TON chain instance with appropriate RPC URL based on network
42-
const chain = new TonChain(
43-
chainId.toString(),
44-
isMainnet,
45-
wormholeChainName,
46-
undefined,
47-
isMainnet
48-
? "https://toncenter.com/api/v2/jsonRPC"
49-
: "https://testnet.toncenter.com/api/v2/jsonRPC"
50-
);
51-
52-
const vault =
53-
DefaultStore.vaults[
54-
"mainnet-beta_FVQyHcooAtThJ83XFrNnv74BcinbRH3bRmfFamAHBfuj"
55-
];
56-
57-
console.log(
58-
`Upgrading contract on TON ${argv.network} (Chain ID: ${chainId}, Wormhole Chain Name: ${wormholeChainName})`
59-
);
35+
const contract = DefaultStore.contracts[
36+
argv.contract
37+
] as TonPriceFeedContract;
6038

6139
// Read the compiled contract from the build directory
6240
// NOTE: Remember to rebuild contract_manager before running this script because it will also build the ton contract
@@ -65,21 +43,14 @@ async function main() {
6543
"../../target_chains/ton/contracts/build/Main.compiled.json"
6644
);
6745
const compiled = JSON.parse(fs.readFileSync(compiledPath, "utf8"));
68-
const newCodeHash = compiled.hash;
69-
console.log("New code hash:", newCodeHash);
70-
71-
// Generate governance payload for the upgrade
72-
const payload = chain.generateGovernanceUpgradePayload(newCodeHash);
73-
console.log("Generated governance payload");
74-
console.log("Payload:", payload);
46+
const newCode = Cell.fromHex(compiled.hex);
47+
console.log(newCode);
7548

76-
// Create and submit governance proposal
77-
console.log("Using vault for proposal:", vault.getId());
78-
const keypair = await loadHotWallet(argv["ops-key-path"] as string);
79-
console.log("Using wallet:", keypair.publicKey.toBase58());
80-
vault.connect(keypair);
81-
const proposal = await vault.proposeWormholeMessage([payload]);
82-
console.log("Proposal address:", proposal.address.toBase58());
49+
const tx = await contract.upgradeContract(
50+
toPrivateKey(argv["private-key"]),
51+
newCode
52+
);
53+
console.log("Upgrade transaction:", tx);
8354
}
8455

8556
main().catch((error) => {

contract_manager/src/contracts/ton.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { WormholeContract } from "./wormhole";
33
import { PriceFeed, PriceFeedContract, PrivateKey, TxResult } from "../base";
44
import { TokenQty } from "../token";
55
import { DataSource } from "@pythnetwork/xc-admin-common";
6-
import { Address, OpenedContract } from "@ton/ton";
6+
import { Address, Cell, OpenedContract } from "@ton/ton";
77
import {
88
calculateUpdatePriceFeedsFee,
99
PythContract,
@@ -279,6 +279,30 @@ export class TonPriceFeedContract extends PriceFeedContract {
279279
};
280280
}
281281

282+
async upgradeContract(
283+
senderPrivateKey: PrivateKey,
284+
newCode: Cell
285+
): Promise<TxResult> {
286+
const client = await this.chain.getClient();
287+
const contract = await this.getContract();
288+
const wallet = await this.chain.getWallet(senderPrivateKey);
289+
const sender = await this.chain.getSender(senderPrivateKey);
290+
await contract.sendUpgradeContract(sender, newCode);
291+
292+
const txDetails = await client.getTransactions(wallet.address, {
293+
limit: 1,
294+
});
295+
const txHash = Buffer.from(txDetails[0].hash()).toString("hex");
296+
const txInfo = JSON.stringify(txDetails[0].description, (_, value) =>
297+
typeof value === "bigint" ? value.toString() : value
298+
);
299+
300+
return {
301+
id: txHash,
302+
info: txInfo,
303+
};
304+
}
305+
282306
toJson() {
283307
return {
284308
chain: this.chain.getId(),

target_chains/ton/sdk/js/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pythnetwork/pyth-ton-js",
3-
"version": "0.1.1",
3+
"version": "0.1.2",
44
"description": "Pyth Network TON Utilities",
55
"homepage": "https://pyth.network",
66
"author": {

target_chains/ton/sdk/js/src/index.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,23 @@ export class PythContract implements Contract {
9393
});
9494
}
9595

96+
async sendUpgradeContract(
97+
provider: ContractProvider,
98+
via: Sender,
99+
newCode: Cell
100+
) {
101+
const messageBody = beginCell()
102+
.storeUint(4, 32) // OP_UPGRADE_CONTRACT
103+
.storeRef(newCode)
104+
.endCell();
105+
106+
await provider.internal(via, {
107+
value: toNano("0.1"),
108+
sendMode: SendMode.PAY_GAS_SEPARATELY,
109+
body: messageBody,
110+
});
111+
}
112+
96113
async getPriceUnsafe(provider: ContractProvider, priceFeedId: string) {
97114
const result = await provider.get("get_price_unsafe", [
98115
{ type: "int", value: BigInt(priceFeedId) },

0 commit comments

Comments
 (0)