Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions contract_manager/scripts/generate_upgrade_ton_contract_proposal.ts
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renamed from upgrade_ton_contract.ts to better reflect the script's purpose

Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import { DefaultStore, loadHotWallet } from "../src";
import { TonChain } from "../src/chains";
import { CHAINS, toChainName } from "@pythnetwork/xc-admin-common";
import fs from "fs";
import path from "path";

const parser = yargs(hideBin(process.argv))
.usage(
"Upgrades the Pyth contract on TON and creates a governance proposal for it.\n" +
"Usage: $0 --network <mainnet|testnet> --contract-address <address> --ops-key-path <ops_key_path>"
)
.options({
network: {
type: "string",
choices: ["mainnet", "testnet"],
description: "Network to deploy to",
demandOption: true,
},
"contract-address": {
type: "string",
description: "Address of the contract to upgrade",
demandOption: true,
},
"ops-key-path": {
type: "string",
description: "Path to operations key file",
demandOption: true,
},
});

async function main() {
const argv = await parser.argv;
const isMainnet = argv.network === "mainnet";

// Get chain ID and name from CHAINS mapping
const chainId = isMainnet ? CHAINS.ton_mainnet : CHAINS.ton_testnet;
const wormholeChainName = toChainName(chainId);

// Get the TON chain instance with appropriate RPC URL based on network
const chain = new TonChain(
chainId.toString(),
isMainnet,
wormholeChainName,
undefined,
isMainnet
? "https://toncenter.com/api/v2/jsonRPC"
: "https://testnet.toncenter.com/api/v2/jsonRPC"
);

const vault =
DefaultStore.vaults[
"mainnet-beta_FVQyHcooAtThJ83XFrNnv74BcinbRH3bRmfFamAHBfuj"
];

console.log(
`Upgrading contract on TON ${argv.network} (Chain ID: ${chainId}, Wormhole Chain Name: ${wormholeChainName})`
);

// Read the compiled contract from the build directory
// NOTE: Remember to rebuild contract_manager before running this script because it will also build the ton contract
const compiledPath = path.resolve(
__dirname,
"../../target_chains/ton/contracts/build/Main.compiled.json"
);
const compiled = JSON.parse(fs.readFileSync(compiledPath, "utf8"));
const newCodeHash = compiled.hash;
console.log("New code hash:", newCodeHash);

// Generate governance payload for the upgrade
const payload = chain.generateGovernanceUpgradePayload(newCodeHash);
console.log("Generated governance payload");
console.log("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);
});
63 changes: 17 additions & 46 deletions contract_manager/scripts/upgrade_ton_contract.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import { DefaultStore, loadHotWallet } from "../src";
import { TonChain } from "../src/chains";
import { CHAINS, toChainName } from "@pythnetwork/xc-admin-common";
import { DefaultStore, TonPriceFeedContract, toPrivateKey } from "../src";
import fs from "fs";
import path from "path";
import { Cell } from "@ton/ton";

const parser = yargs(hideBin(process.argv))
.usage(
"Upgrades the Pyth contract on TON and creates a governance proposal for it.\n" +
"Usage: $0 --network <mainnet|testnet> --contract-address <address> --ops-key-path <ops_key_path>"
"Usage: $0 --network <mainnet|testnet> --contract <contract_name> --private-key <private_key>"
)
.options({
network: {
Expand All @@ -18,45 +17,24 @@ const parser = yargs(hideBin(process.argv))
description: "Network to deploy to",
demandOption: true,
},
"contract-address": {
contract: {
type: "string",
description: "Address of the contract to upgrade",
description: "Contract name",
demandOption: true,
},
"ops-key-path": {
"private-key": {
type: "string",
description: "Path to operations key file",
description: "Private key of the sender",
demandOption: true,
},
});

async function main() {
const argv = await parser.argv;
const isMainnet = argv.network === "mainnet";

// Get chain ID and name from CHAINS mapping
const chainId = isMainnet ? CHAINS.ton_mainnet : CHAINS.ton_testnet;
const wormholeChainName = toChainName(chainId);

// Get the TON chain instance with appropriate RPC URL based on network
const chain = new TonChain(
chainId.toString(),
isMainnet,
wormholeChainName,
undefined,
isMainnet
? "https://toncenter.com/api/v2/jsonRPC"
: "https://testnet.toncenter.com/api/v2/jsonRPC"
);

const vault =
DefaultStore.vaults[
"mainnet-beta_FVQyHcooAtThJ83XFrNnv74BcinbRH3bRmfFamAHBfuj"
];

console.log(
`Upgrading contract on TON ${argv.network} (Chain ID: ${chainId}, Wormhole Chain Name: ${wormholeChainName})`
);
const contract = DefaultStore.contracts[
argv.contract
] as TonPriceFeedContract;

// Read the compiled contract from the build directory
// NOTE: Remember to rebuild contract_manager before running this script because it will also build the ton contract
Expand All @@ -65,21 +43,14 @@ async function main() {
"../../target_chains/ton/contracts/build/Main.compiled.json"
);
const compiled = JSON.parse(fs.readFileSync(compiledPath, "utf8"));
const newCodeHash = compiled.hash;
console.log("New code hash:", newCodeHash);

// Generate governance payload for the upgrade
const payload = chain.generateGovernanceUpgradePayload(newCodeHash);
console.log("Generated governance payload");
console.log("Payload:", payload);
const newCode = Cell.fromHex(compiled.hex);
console.log(newCode);

// 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());
const tx = await contract.upgradeContract(
toPrivateKey(argv["private-key"]),
newCode
);
console.log("Upgrade transaction:", tx);
}

main().catch((error) => {
Expand Down
26 changes: 25 additions & 1 deletion contract_manager/src/contracts/ton.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { WormholeContract } from "./wormhole";
import { PriceFeed, PriceFeedContract, PrivateKey, TxResult } from "../base";
import { TokenQty } from "../token";
import { DataSource } from "@pythnetwork/xc-admin-common";
import { Address, OpenedContract } from "@ton/ton";
import { Address, Cell, OpenedContract } from "@ton/ton";
import {
calculateUpdatePriceFeedsFee,
PythContract,
Expand Down Expand Up @@ -279,6 +279,30 @@ export class TonPriceFeedContract extends PriceFeedContract {
};
}

async upgradeContract(
senderPrivateKey: PrivateKey,
newCode: Cell
): Promise<TxResult> {
const client = await this.chain.getClient();
const contract = await this.getContract();
const wallet = await this.chain.getWallet(senderPrivateKey);
const sender = await this.chain.getSender(senderPrivateKey);
await contract.sendUpgradeContract(sender, newCode);

const txDetails = await client.getTransactions(wallet.address, {
limit: 1,
});
const txHash = Buffer.from(txDetails[0].hash()).toString("hex");
const txInfo = JSON.stringify(txDetails[0].description, (_, value) =>
typeof value === "bigint" ? value.toString() : value
);

return {
id: txHash,
info: txInfo,
};
}

toJson() {
return {
chain: this.chain.getId(),
Expand Down
2 changes: 1 addition & 1 deletion target_chains/ton/sdk/js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@pythnetwork/pyth-ton-js",
"version": "0.1.1",
"version": "0.1.2",
"description": "Pyth Network TON Utilities",
"homepage": "https://pyth.network",
"author": {
Expand Down
17 changes: 17 additions & 0 deletions target_chains/ton/sdk/js/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,23 @@ export class PythContract implements Contract {
});
}

async sendUpgradeContract(
provider: ContractProvider,
via: Sender,
newCode: Cell
) {
const messageBody = beginCell()
.storeUint(4, 32) // OP_UPGRADE_CONTRACT
.storeRef(newCode)
.endCell();

await provider.internal(via, {
value: toNano("0.1"),
sendMode: SendMode.PAY_GAS_SEPARATELY,
body: messageBody,
});
}

async getPriceUnsafe(provider: ContractProvider, priceFeedId: string) {
const result = await provider.get("get_price_unsafe", [
{ type: "int", value: BigInt(priceFeedId) },
Expand Down
Loading