Skip to content

Commit 29e5c9b

Browse files
authored
feat(target_chains/ton): add ton deploy script (#2039)
* init * update deploy script * precommit * remove temp * refactor test wrappers * feat(target_chains/ton): add ton js sdk (#2044) * init ton js sdk * fix docs * fix * address comments
1 parent 9526387 commit 29e5c9b

File tree

18 files changed

+1247
-457
lines changed

18 files changed

+1247
-457
lines changed

pnpm-lock.yaml

Lines changed: 252 additions & 36 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pnpm-workspace.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,5 @@ packages:
2626
- "target_chains/solana/sdk/js/solana_utils"
2727
- "target_chains/solana/sdk/js/pyth_solana_receiver"
2828
- "target_chains/ton/contracts"
29+
- "target_chains/ton/sdk/js"
2930
- "contract_manager"

target_chains/ton/contracts/contracts/Main.fc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
;; * The remainder of the message body is specific for each supported value of `op`.
2020
if (op == OP_UPDATE_GUARDIAN_SET) {
2121
update_guardian_set(data_slice);
22+
} elseif (op == OP_UPDATE_PRICE_FEEDS) {
23+
update_price_feeds(msg_value, data_slice);
2224
} elseif (op == OP_EXECUTE_GOVERNANCE_ACTION) {
2325
execute_governance_action(data_slice);
2426
} elseif (op == OP_UPGRADE_CONTRACT) {

target_chains/ton/contracts/package.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@
88
"test": "jest --verbose"
99
},
1010
"devDependencies": {
11+
"@pythnetwork/pyth-ton-js": "workspace:*",
1112
"@pythnetwork/price-service-sdk": "workspace:*",
1213
"@pythnetwork/xc-admin-common": "workspace:*",
14+
"@pythnetwork/hermes-client": "workspace:*",
1315
"@ton/blueprint": "^0.22.0",
14-
"@ton/core": "~0",
15-
"@ton/crypto": "^3.2.0",
16+
"@ton/core": "^0.59.0",
17+
"@ton/crypto": "^3.3.0",
1618
"@ton/sandbox": "^0.20.0",
1719
"@ton/test-utils": "^0.4.2",
18-
"@ton/ton": "^13.11.2",
20+
"@ton/ton": "^15.1.0",
1921
"@types/jest": "^29.5.12",
2022
"@types/node": "^20.14.10",
2123
"@wormhole-foundation/sdk-definitions": "^0.10.7",
Lines changed: 113 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,122 @@
11
import { toNano } from "@ton/core";
2+
import { MainConfig } from "../wrappers/Main";
3+
import { compile, NetworkProvider, sleep } from "@ton/blueprint";
4+
import { DataSource } from "@pythnetwork/xc-admin-common";
5+
import { HermesClient } from "@pythnetwork/hermes-client";
26
import { Main } from "../wrappers/Main";
3-
import { compile, NetworkProvider } from "@ton/blueprint";
7+
import {
8+
GOVERNANCE_DATA_SOURCE,
9+
GUARDIAN_SET_0,
10+
MAINNET_UPGRADE_VAAS,
11+
} from "../tests/utils/wormhole";
12+
import { BTC_PRICE_FEED_ID, ETH_PRICE_FEED_ID } from "../tests/utils/pyth";
413

514
export async function run(provider: NetworkProvider) {
6-
const main = provider.open(Main.createFromConfig({}, await compile("Main")));
15+
const SINGLE_UPDATE_FEE = 1;
16+
const DATA_SOURCES: DataSource[] = [
17+
{
18+
emitterChain: 26,
19+
emitterAddress:
20+
"e101faedac5851e32b9b23b5f9411a8c2bac4aae3ed4dd7b811dd1a72ea4aa71",
21+
},
22+
];
723

8-
await main.sendDeploy(provider.sender(), toNano("0.05"));
24+
const config: MainConfig = {
25+
singleUpdateFee: SINGLE_UPDATE_FEE,
26+
dataSources: DATA_SOURCES,
27+
guardianSetIndex: 0,
28+
guardianSet: GUARDIAN_SET_0,
29+
chainId: 1,
30+
governanceChainId: 1,
31+
governanceContract:
32+
"0000000000000000000000000000000000000000000000000000000000000004",
33+
governanceDataSource: GOVERNANCE_DATA_SOURCE,
34+
};
35+
36+
const main = provider.open(
37+
Main.createFromConfig(config, await compile("Main"))
38+
);
39+
40+
await main.sendDeploy(provider.sender(), toNano("0.005"));
941

1042
await provider.waitForDeploy(main.address);
1143

12-
// run methods on `main`
44+
console.log("Main contract deployed at:", main.address.toString());
45+
46+
// Call sendUpdateGuardianSet for each VAA
47+
const currentGuardianSetIndex = await main.getCurrentGuardianSetIndex();
48+
console.log(`Current guardian set index: ${currentGuardianSetIndex}`);
49+
50+
for (let i = currentGuardianSetIndex; i < MAINNET_UPGRADE_VAAS.length; i++) {
51+
const vaa = MAINNET_UPGRADE_VAAS[i];
52+
const vaaBuffer = Buffer.from(vaa, "hex");
53+
await main.sendUpdateGuardianSet(provider.sender(), vaaBuffer);
54+
console.log(
55+
`Successfully updated guardian set ${i + 1} with VAA: ${vaa.slice(
56+
0,
57+
20
58+
)}...`
59+
);
60+
61+
// Wait for 30 seconds before checking the guardian set index
62+
console.log("Waiting for 30 seconds before checking guardian set index...");
63+
await sleep(30000);
64+
65+
// Verify the update
66+
const newIndex = await main.getCurrentGuardianSetIndex();
67+
if (newIndex !== i + 1) {
68+
console.error(
69+
`Failed to update guardian set. Expected index ${
70+
i + 1
71+
}, got ${newIndex}`
72+
);
73+
break;
74+
}
75+
}
76+
77+
console.log("Guardian set update process completed.");
78+
79+
// Initialize HermesClient
80+
const hermesEndpoint = "https://hermes.pyth.network";
81+
const hermesClient = new HermesClient(hermesEndpoint);
82+
83+
// Fetch latest price updates for BTC and ETH
84+
const priceIds = [BTC_PRICE_FEED_ID, ETH_PRICE_FEED_ID];
85+
const latestPriceUpdates = await hermesClient.getLatestPriceUpdates(
86+
priceIds,
87+
{ encoding: "hex" }
88+
);
89+
console.log("Hermes BTC price:", latestPriceUpdates.parsed?.[0].price);
90+
console.log("Hermes ETH price:", latestPriceUpdates.parsed?.[1].price);
91+
// Combine updates into a single buffer
92+
const updateData = Buffer.from(latestPriceUpdates.binary.data[0], "hex");
93+
console.log("Update data:", latestPriceUpdates.binary.data[0]);
94+
95+
const singleUpdateFee = await main.getSingleUpdateFee();
96+
console.log("Single update fee:", singleUpdateFee);
97+
98+
// NOTE: As of 2024/10/14 There's a bug with TON Access (https://ton.access.orbs.network) RPC service where if you provide an update data buffer with length of more than ~320 then the rpc returns error 404 and the function fails
99+
const updateFee = await main.getUpdateFee(updateData);
100+
console.log("Update fee:", updateFee);
101+
102+
await main.sendUpdatePriceFeeds(
103+
provider.sender(),
104+
updateData,
105+
toNano(updateFee)
106+
);
107+
console.log("Price feeds updated successfully.");
108+
109+
console.log("Waiting for 30 seconds before checking price feeds...");
110+
await sleep(30000);
111+
112+
// Query updated price feeds
113+
const btcPrice = await main.getPriceUnsafe(BTC_PRICE_FEED_ID);
114+
console.log(
115+
`Updated BTC price: ${btcPrice.price}, publish time: ${btcPrice.publishTime}`
116+
);
117+
118+
const ethPrice = await main.getPriceUnsafe(ETH_PRICE_FEED_ID);
119+
console.log(
120+
`Updated ETH price: ${ethPrice.price}, publish time: ${ethPrice.publishTime}`
121+
);
13122
}

target_chains/ton/contracts/tests/Main.spec.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Blockchain, SandboxContract, TreasuryContract } from "@ton/sandbox";
22
import { Cell, toNano } from "@ton/core";
3-
import { Main } from "../wrappers/Main";
3+
import { Main, MainConfig } from "../wrappers/Main";
44
import "@ton/test-utils";
55
import { compile } from "@ton/blueprint";
66

@@ -17,8 +17,23 @@ describe("Main", () => {
1717

1818
beforeEach(async () => {
1919
blockchain = await Blockchain.create();
20-
21-
main = blockchain.openContract(Main.createFromConfig({}, code));
20+
const config: MainConfig = {
21+
singleUpdateFee: 0,
22+
dataSources: [],
23+
guardianSetIndex: 0,
24+
guardianSet: [],
25+
chainId: 0,
26+
governanceChainId: 0,
27+
governanceContract:
28+
"0000000000000000000000000000000000000000000000000000000000000000",
29+
governanceDataSource: {
30+
emitterChain: 0,
31+
emitterAddress:
32+
"0000000000000000000000000000000000000000000000000000000000000000",
33+
},
34+
};
35+
36+
main = blockchain.openContract(Main.createFromConfig(config, code));
2237

2338
deployer = await blockchain.treasury("deployer");
2439

target_chains/ton/contracts/tests/utils.ts

Lines changed: 1 addition & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,12 @@
11
import { DataSource } from "@pythnetwork/xc-admin-common";
2-
import { Cell, Transaction, beginCell } from "@ton/core";
2+
import { Cell, Transaction } from "@ton/core";
33
import { Buffer } from "buffer";
44

55
const GOVERNANCE_MAGIC = 0x5054474d;
66
const GOVERNANCE_MODULE = 1;
77
const AUTHORIZE_UPGRADE_CONTRACT_ACTION = 0;
88
const TARGET_CHAIN_ID = 1;
99

10-
export function createCellChain(buffer: Buffer): Cell {
11-
let chunks = bufferToChunks(buffer, 127);
12-
let lastCell: Cell | null = null;
13-
// Iterate through chunks in reverse order
14-
for (let i = chunks.length - 1; i >= 0; i--) {
15-
const chunk = chunks[i];
16-
const cellBuilder = beginCell();
17-
const buffer = Buffer.from(chunk);
18-
cellBuilder.storeBuffer(buffer);
19-
20-
if (lastCell) {
21-
cellBuilder.storeRef(lastCell);
22-
}
23-
24-
lastCell = cellBuilder.endCell();
25-
}
26-
27-
// lastCell will be the root cell of our chain
28-
if (!lastCell) {
29-
throw new Error("Failed to create cell chain");
30-
}
31-
return lastCell;
32-
}
33-
34-
function bufferToChunks(
35-
buff: Buffer,
36-
chunkSizeBytes: number = 127
37-
): Uint8Array[] {
38-
const chunks: Uint8Array[] = [];
39-
const uint8Array = new Uint8Array(
40-
buff.buffer,
41-
buff.byteOffset,
42-
buff.byteLength
43-
);
44-
45-
for (let offset = 0; offset < uint8Array.length; offset += chunkSizeBytes) {
46-
const remainingBytes = Math.min(chunkSizeBytes, uint8Array.length - offset);
47-
const chunk = uint8Array.subarray(offset, offset + remainingBytes);
48-
chunks.push(chunk);
49-
}
50-
51-
return chunks;
52-
}
53-
5410
// Helper function to parse DataSource from a Cell
5511
export function parseDataSource(cell: Cell): DataSource {
5612
const slice = cell.beginParse();

target_chains/ton/contracts/tests/utils/wormhole.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { DataSource } from "@pythnetwork/xc-admin-common";
12
import { beginCell, Cell, Dictionary } from "@ton/core";
23

34
export const GUARDIAN_SET_0 = ["0x58CC3AE5C097B213CE3C81979E1B9F9570746AA5"];
@@ -191,3 +192,9 @@ export const MAINNET_UPGRADE_VAAS = [
191192
"01000000020d00ce45474d9e1b1e7790a2d210871e195db53a70ffd6f237cfe70e2686a32859ac43c84a332267a8ef66f59719cf91cc8df0101fd7c36aa1878d5139241660edc0010375cc906156ae530786661c0cd9aef444747bc3d8d5aa84cac6a6d2933d4e1a031cffa30383d4af8131e929d9f203f460b07309a647d6cd32ab1cc7724089392c000452305156cfc90343128f97e499311b5cae174f488ff22fbc09591991a0a73d8e6af3afb8a5968441d3ab8437836407481739e9850ad5c95e6acfcc871e951bc30105a7956eefc23e7c945a1966d5ddbe9e4be376c2f54e45e3d5da88c2f8692510c7429b1ea860ae94d929bd97e84923a18187e777aa3db419813a80deb84cc8d22b00061b2a4f3d2666608e0aa96737689e3ba5793810ff3a52ff28ad57d8efb20967735dc5537a2e43ef10f583d144c12a1606542c207f5b79af08c38656d3ac40713301086b62c8e130af3411b3c0d91b5b50dcb01ed5f293963f901fc36e7b0e50114dce203373b32eb45971cef8288e5d928d0ed51cd86e2a3006b0af6a65c396c009080009e93ab4d2c8228901a5f4525934000b2c26d1dc679a05e47fdf0ff3231d98fbc207103159ff4116df2832eea69b38275283434e6cd4a4af04d25fa7a82990b707010aa643f4cf615dfff06ffd65830f7f6cf6512dabc3690d5d9e210fdc712842dc2708b8b2c22e224c99280cd25e5e8bfb40e3d1c55b8c41774e287c1e2c352aecfc010b89c1e85faa20a30601964ccc6a79c0ae53cfd26fb10863db37783428cd91390a163346558239db3cd9d420cfe423a0df84c84399790e2e308011b4b63e6b8015010ca31dcb564ac81a053a268d8090e72097f94f366711d0c5d13815af1ec7d47e662e2d1bde22678113d15963da100b668ba26c0c325970d07114b83c5698f46097010dc9fda39c0d592d9ed92cd22b5425cc6b37430e236f02d0d1f8a2ef45a00bde26223c0a6eb363c8b25fd3bf57234a1d9364976cefb8360e755a267cbbb674b39501108db01e444ab1003dd8b6c96f8eb77958b40ba7a85fefecf32ad00b7a47c0ae7524216262495977e09c0989dd50f280c21453d3756843608eacd17f4fdfe47600001261025228ef5af837cb060bcd986fcfa84ccef75b3fa100468cfd24e7fadf99163938f3b841a33496c2706d0208faab088bd155b2e20fd74c625bb1cc8c43677a0163c53c409e0c5dfa000100000000000000000000000000000000000000000000000000000000000000046c5a054d7833d1e42000000000000000000000000000000000000000000000000000000000436f7265020000000000031358cc3ae5c097b213ce3c81979e1b9f9570746aa5ff6cb952589bde862c25ef4392132fb9d4a42157114de8460193bdf3a2fcf81f86a09765f4762fd1107a0086b32d7a0977926a205131d8731d39cbeb8c82b2fd82faed2711d59af0f2499d16e726f6b211b39756c042441be6d8650b69b54ebe715e234354ce5b4d348fb74b958e8966e2ec3dbd4958a7cd15e7caf07c4e3dc8e7c469f92c8cd88fb8005a2074a3bf913953d695260d88bc1aa25a4eee363ef0000ac0076727b35fbea2dac28fee5ccb0fea768eaf45ced136b9d9e24903464ae889f5c8a723fc14f93124b7c738843cbb89e864c862c38cddcccf95d2cc37a4dc036a8d232b48f62cdd4731412f4890da798f6896a3331f64b48c12d1d57fd9cbe7081171aa1be1d36cafe3867910f99c09e347899c19c38192b6e7387ccd768277c17dab1b7a5027c0b3cf178e21ad2e77ae06711549cfbb1f9c7a9d8096e85e1487f35515d02a92753504a8d75471b9f49edb6fbebc898f403e4773e95feb15e80c9a99c8348d",
192193
"01000000030d03d4a37a6ff4361d91714730831e9d49785f61624c8f348a9c6c1d82bc1d98cadc5e936338204445c6250bb4928f3f3e165ad47ca03a5d63111168a2de4576856301049a5df10464ea4e1961589fd30fc18d1970a7a2ffaad617e56a0f7777f25275253af7d10a0f0f2494dc6e99fc80e444ab9ebbbee252ded2d5dcb50cbf7a54bb5a01055f4603b553b9ba9e224f9c55c7bca3da00abb10abd19e0081aecd3b352be061a70f79f5f388ebe5190838ef3cd13a2f22459c9a94206883b739c90b40d5d74640006a8fade3997f650a36e46bceb1f609edff201ab32362266f166c5c7da713f6a19590c20b68ed3f0119cb24813c727560ede086b3d610c2d7a1efa66f655bad90900080f5e495a75ea52241c59d145c616bfac01e57182ad8d784cbcc9862ed3afb60c0983ccbc690553961ffcf115a0c917367daada8e60be2cbb8b8008bac6341a8c010935ab11e0eea28b87a1edc5ccce3f1fac25f75b5f640fe6b0673a7cd74513c9dc01c544216cf364cc9993b09fda612e0cd1ced9c00fb668b872a16a64ebb55d27010ab2bc39617a2396e7defa24cd7c22f42dc31f3c42ffcd9d1472b02df8468a4d0563911e8fb6a4b5b0ce0bd505daa53779b08ff660967b31f246126ed7f6f29a7e000bdb6d3fd7b33bdc9ac3992916eb4aacb97e7e21d19649e7fa28d2dd6e337937e4274516a96c13ac7a8895da9f91948ea3a09c25f44b982c62ce8842b58e20c8a9000d3d1b19c8bb000856b6610b9d28abde6c35cb7705c6ca5db711f7be96d60eed9d72cfa402a6bfe8bf0496dbc7af35796fc768da51a067b95941b3712dce8ae1e7010ec80085033157fd1a5628fc0c56267469a86f0e5a66d7dede1ad4ce74ecc3dff95b60307a39c3bfbeedc915075070da30d0395def9635130584f709b3885e1bdc0010fc480eb9ee715a2d151b23722b48b42581d7f4001fc1696c75425040bfc1ffc5394fe418adb2b64bd3dc692efda4cc408163677dbe233b16bcdabb853a20843301118ee9e115e1a0c981f19d0772b850e666591322da742a9a12cce9f52a5665bd474abdd59c580016bee8aae67fdf39b315be2528d12eec3a652910e03cc4c6fa3801129d0d1e2e429e969918ec163d16a7a5b2c6729aa44af5dccad07d25d19891556a79b574f42d9adbd9e2a9ae5a6b8750331d2fccb328dd94c3bf8791ee1bfe85aa00661e99781981faea00010000000000000000000000000000000000000000000000000000000000000004fd4c6c55ec8dfd342000000000000000000000000000000000000000000000000000000000436f726502000000000004135893b5a76c3f739645648885bdccc06cd70a3cd3ff6cb952589bde862c25ef4392132fb9d4a42157114de8460193bdf3a2fcf81f86a09765f4762fd1107a0086b32d7a0977926a205131d8731d39cbeb8c82b2fd82faed2711d59af0f2499d16e726f6b211b39756c042441be6d8650b69b54ebe715e234354ce5b4d348fb74b958e8966e2ec3dbd4958a7cd15e7caf07c4e3dc8e7c469f92c8cd88fb8005a2074a3bf913953d695260d88bc1aa25a4eee363ef0000ac0076727b35fbea2dac28fee5ccb0fea768eaf45ced136b9d9e24903464ae889f5c8a723fc14f93124b7c738843cbb89e864c862c38cddcccf95d2cc37a4dc036a8d232b48f62cdd4731412f4890da798f6896a3331f64b48c12d1d57fd9cbe7081171aa1be1d36cafe3867910f99c09e347899c19c38192b6e7387ccd768277c17dab1b7a5027c0b3cf178e21ad2e77ae06711549cfbb1f9c7a9d8096e85e1487f35515d02a92753504a8d75471b9f49edb6fbebc898f403e4773e95feb15e80c9a99c8348d",
193194
];
195+
196+
export const GOVERNANCE_DATA_SOURCE: DataSource = {
197+
emitterChain: 1,
198+
emitterAddress:
199+
"0000000000000000000000000000000000000000000000000000000000000029",
200+
};

0 commit comments

Comments
 (0)