Skip to content

Commit 64c9a28

Browse files
authored
feat(target_chains/ton): add get_update_fee (#1853)
* add get_update_fee * remove unused error * add comment * address comments * update tests names * precommit
1 parent 627c317 commit 64c9a28

File tree

11 files changed

+145
-36
lines changed

11 files changed

+145
-36
lines changed

target_chains/ton/contracts/contracts/Pyth.fc

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,36 @@
11
#include "imports/stdlib.fc";
22
#include "common/errors.fc";
33
#include "common/storage.fc";
4+
#include "common/utils.fc";
5+
#include "./Wormhole.fc";
6+
7+
const int ACCUMULATOR_MAGIC = 0x504e4155; ;; "PNAU" (Pyth Network Accumulator Update)
8+
const int MAJOR_VERSION = 1;
9+
const int MINIMUM_ALLOWED_MINOR_VERSION = 0;
10+
11+
slice verify_header(slice data) {
12+
int magic = data~load_uint(32);
13+
throw_unless(ERROR_INVALID_MAGIC, magic == ACCUMULATOR_MAGIC);
14+
int major_version = data~load_uint(8);
15+
throw_unless(ERROR_INVALID_MAJOR_VERSION, major_version == MAJOR_VERSION);
16+
int minor_version = data~load_uint(8);
17+
throw_if(ERROR_INVALID_MINOR_VERSION, minor_version < MINIMUM_ALLOWED_MINOR_VERSION);
18+
int trailing_header_size = data~load_uint(8);
19+
;; skip trailing headers and update type (uint8)
20+
data~skip_bits(trailing_header_size);
21+
data~skip_bits(8);
22+
return data;
23+
}
24+
25+
(int) get_update_fee(slice data) method_id {
26+
load_data();
27+
slice cs = verify_header(data);
28+
int wormhole_proof_size_bytes = cs~load_uint(16);
29+
(cell wormhole_proof, slice cs) = read_and_store_large_data(cs, wormhole_proof_size_bytes * 8);
30+
int num_updates = cs~load_uint(8);
31+
return single_update_fee * num_updates;
32+
}
433

5-
;; Opcodes
6-
const int OP_UPDATE_GUARDIAN_SET = 1;
7-
const int OP_EXECUTE_GOVERNANCE_ACTION = 2;
834

935
(int, int, int, int) parse_price(slice price_feed) {
1036
int price = price_feed~load_int(256);
@@ -14,22 +40,25 @@ const int OP_EXECUTE_GOVERNANCE_ACTION = 2;
1440
return (price, conf, expo, publish_time);
1541
}
1642

17-
(int, int, int, int) price_unsafe(int price_feed_id) method_id {
43+
(int, int, int, int) get_price_unsafe(int price_feed_id) method_id {
44+
load_data();
1845
(slice result, int success) = latest_price_feeds.udict_get?(256, price_feed_id);
1946
throw_unless(ERROR_PRICE_FEED_NOT_FOUND, success);
2047
slice price_feed = result~load_ref().begin_parse();
2148
slice price = price_feed~load_ref().begin_parse();
2249
return parse_price(price);
2350
}
2451

25-
(int, int, int, int) price_no_older_than(int time_period, int price_feed_id) method_id {
26-
(int price, int conf, int expo, int publish_time) = price_unsafe(price_feed_id);
52+
(int, int, int, int) get_price_no_older_than(int time_period, int price_feed_id) method_id {
53+
load_data();
54+
(int price, int conf, int expo, int publish_time) = get_price_unsafe(price_feed_id);
2755
int current_time = now();
2856
throw_if(ERROR_OUTDATED_PRICE, current_time - publish_time > time_period);
2957
return (price, conf, expo, publish_time);
3058
}
3159

32-
(int, int, int, int) ema_price_unsafe(int price_feed_id) method_id {
60+
(int, int, int, int) get_ema_price_unsafe(int price_feed_id) method_id {
61+
load_data();
3362
(slice result, int success) = latest_price_feeds.udict_get?(256, price_feed_id);
3463
throw_unless(ERROR_PRICE_FEED_NOT_FOUND, success);
3564
slice price_feed = result~load_ref().begin_parse();
@@ -38,8 +67,9 @@ const int OP_EXECUTE_GOVERNANCE_ACTION = 2;
3867
return parse_price(ema_price);
3968
}
4069

41-
(int, int, int, int) ema_price_no_older_than(int time_period, int price_feed_id) method_id {
42-
(int price, int conf, int expo, int publish_time) = ema_price_unsafe(price_feed_id);
70+
(int, int, int, int) get_ema_price_no_older_than(int time_period, int price_feed_id) method_id {
71+
load_data();
72+
(int price, int conf, int expo, int publish_time) = get_ema_price_unsafe(price_feed_id);
4373
int current_time = now();
4474
throw_if(ERROR_OUTDATED_PRICE, current_time - publish_time > time_period);
4575
return (price, conf, expo, publish_time);

target_chains/ton/contracts/contracts/Wormhole.fc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,12 @@ const int UPGRADE_MODULE = 0x000000000000000000000000000000000000000000000000000
5858

5959
;; Get methods
6060
int get_current_guardian_set_index() method_id {
61+
load_data();
6162
return current_guardian_set_index;
6263
}
6364

6465
(int, cell, int) get_guardian_set(int index) method_id {
66+
load_data();
6567
return get_guardian_set_internal(index);
6668
}
6769

@@ -111,6 +113,7 @@ int governance_action_is_consumed(int hash) method_id {
111113
}
112114

113115
(int, int, int, int, int, int, int, int, slice, int) parse_and_verify_wormhole_vm(slice in_msg_body) impure {
116+
load_data();
114117
;; Parse VM fields
115118
int version = in_msg_body~load_uint(8);
116119
throw_unless(ERROR_INVALID_VERSION, version == 1);
@@ -247,6 +250,7 @@ int governance_action_is_consumed(int hash) method_id {
247250

248251
;; Mark the governance action as consumed
249252
consumed_governance_actions~udict_set(256, hash, begin_cell().store_int(true, 1).end_cell().begin_parse());
253+
store_data();
250254
}
251255

252256
() execute_governance_action(slice in_msg_body) impure {

target_chains/ton/contracts/contracts/common/errors.fc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,6 @@ const int ERROR_INVALID_GUARDIAN_ADDRESS = 1018;
2424
;; Pyth
2525
const int ERROR_PRICE_FEED_NOT_FOUND = 1019;
2626
const int ERROR_OUTDATED_PRICE = 1020;
27+
const int ERROR_INVALID_MAGIC = 1021;
28+
const int ERROR_INVALID_MAJOR_VERSION = 1022;
29+
const int ERROR_INVALID_MINOR_VERSION = 1023;

target_chains/ton/contracts/contracts/common/storage.fc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
;; Price struct: {price: int, conf: int, expo: int, publish_time: int}
55
;; PriceFeed struct: {price: Price, ema_price: Price}
66
global cell latest_price_feeds; ;; Dictionary of PriceFeed structs, keyed by price_feed_id (256-bit)
7+
global int single_update_fee;
78

89
;; Wormhole
910
global int current_guardian_set_index;
@@ -14,14 +15,14 @@ global int current_guardian_set_index;
1415
global cell guardian_sets;
1516
global int chain_id;
1617
global int governance_chain_id;
17-
;; GovernanceContract struct: {chain_id: int, address: slice}
1818
global int governance_contract;
1919
global cell consumed_governance_actions;
2020

2121

2222
() store_data() impure inline_ref {
2323
begin_cell()
2424
.store_dict(latest_price_feeds)
25+
.store_uint(single_update_fee, 256)
2526
.store_uint(current_guardian_set_index, 32)
2627
.store_dict(guardian_sets)
2728
.store_uint(chain_id, 16)
@@ -36,6 +37,7 @@ global cell consumed_governance_actions;
3637
() load_data() impure inline_ref {
3738
var ds = get_data().begin_parse();
3839
latest_price_feeds = ds~load_dict();
40+
single_update_fee = ds~load_uint(256);
3941
current_guardian_set_index = ds~load_uint(32);
4042
guardian_sets = ds~load_dict();
4143
chain_id = ds~load_uint(16);

target_chains/ton/contracts/contracts/tests/PythTest.fc

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,22 @@
55
;; nop;
66
}
77

8-
(int, int, int, int) test_price_unsafe(int price_feed_id) method_id {
9-
load_data();
10-
return price_unsafe(price_feed_id);
8+
(int, int, int, int) test_get_price_unsafe(int price_feed_id) method_id {
9+
return get_price_unsafe(price_feed_id);
1110
}
1211

13-
(int, int, int, int) test_price_no_older_than(int time_period, int price_feed_id) method_id {
14-
load_data();
15-
return price_no_older_than(time_period, price_feed_id);
12+
(int, int, int, int) test_get_price_no_older_than(int time_period, int price_feed_id) method_id {
13+
return get_price_no_older_than(time_period, price_feed_id);
1614
}
1715

18-
(int, int, int, int) test_ema_price_unsafe(int price_feed_id) method_id {
19-
load_data();
20-
return ema_price_unsafe(price_feed_id);
16+
(int, int, int, int) test_get_ema_price_unsafe(int price_feed_id) method_id {
17+
return get_ema_price_unsafe(price_feed_id);
2118
}
2219

23-
(int, int, int, int) test_ema_price_no_older_than(int time_period, int price_feed_id) method_id {
24-
load_data();
25-
return ema_price_no_older_than(time_period, price_feed_id);
20+
(int, int, int, int) test_get_ema_price_no_older_than(int time_period, int price_feed_id) method_id {
21+
return get_ema_price_no_older_than(time_period, price_feed_id);
22+
}
23+
24+
(int) test_get_update_fee(slice in_msg_body) method_id {
25+
return get_update_fee(in_msg_body);
2626
}

target_chains/ton/contracts/contracts/tests/WormholeTest.fc

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,18 @@
1010
}
1111

1212
(int, int, int, int, int, int, int, int, slice, int) test_parse_and_verify_wormhole_vm(slice in_msg_body) method_id {
13-
load_data();
1413
return parse_and_verify_wormhole_vm(in_msg_body);
1514
}
1615

1716
(int) test_update_guardian_set(slice in_msg_body) method_id {
18-
load_data();
1917
int old_guardian_set_index = get_current_guardian_set_index();
2018
update_guardian_set(in_msg_body);
2119
int new_guardian_set_index = get_current_guardian_set_index();
2220
return new_guardian_set_index > old_guardian_set_index;
2321
}
2422

2523
(int) test_get_current_guardian_set_index() method_id {
26-
load_data();
27-
return current_guardian_set_index;
24+
return get_current_guardian_set_index();
2825
}
2926

3027
(int, cell, int) test_get_guardian_set(int index) method_id {

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

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@ import { Blockchain, SandboxContract, TreasuryContract } from "@ton/sandbox";
22
import { Cell, toNano } from "@ton/core";
33
import "@ton/test-utils";
44
import { compile } from "@ton/blueprint";
5-
import { HexString, Price } from "@pythnetwork/price-service-sdk";
5+
import {
6+
HexString,
7+
parseAccumulatorUpdateData,
8+
Price,
9+
} from "@pythnetwork/price-service-sdk";
610
import { PythTest, PythTestConfig } from "../wrappers/PythTest";
11+
import { HERMES_BTC_ETH_UPDATE } from "./utils/pyth";
712

813
const PRICE_FEED_ID =
914
"0x0000000000000000000000000000000000000000000000000000000000000000";
@@ -20,6 +25,7 @@ const EMA_PRICE = new Price({
2025
expo: 7,
2126
publishTime: 8,
2227
});
28+
const SINGLE_UPDATE_FEE = 1;
2329

2430
describe("PythTest", () => {
2531
let code: Cell;
@@ -41,13 +47,15 @@ describe("PythTest", () => {
4147
priceFeedId: HexString = PRICE_FEED_ID,
4248
timePeriod: number = TIME_PERIOD,
4349
price: Price = PRICE,
44-
emaPrice: Price = EMA_PRICE
50+
emaPrice: Price = EMA_PRICE,
51+
singleUpdateFee: number = SINGLE_UPDATE_FEE
4552
) {
4653
const config: PythTestConfig = {
4754
priceFeedId,
4855
timePeriod,
4956
price,
5057
emaPrice,
58+
singleUpdateFee,
5159
};
5260

5361
pythTest = blockchain.openContract(PythTest.createFromConfig(config, code));
@@ -69,7 +77,6 @@ describe("PythTest", () => {
6977
await deployContract();
7078

7179
const result = await pythTest.getPriceUnsafe(PRICE_FEED_ID);
72-
7380
expect(result.price).toBe(1);
7481
expect(result.conf).toBe(2);
7582
expect(result.expo).toBe(3);
@@ -144,4 +151,14 @@ describe("PythTest", () => {
144151
expect(result.expo).toBe(7);
145152
expect(result.publishTime).toBe(8);
146153
});
154+
155+
it("should correctly get update fee", async () => {
156+
await deployContract();
157+
158+
const result = await pythTest.getUpdateFee(
159+
Buffer.from(HERMES_BTC_ETH_UPDATE, "hex")
160+
);
161+
162+
expect(result).toBe(2);
163+
});
147164
});

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,4 +154,11 @@ describe("WormholeTest", () => {
154154
)
155155
).rejects.toThrow("Unable to execute get method. Got exit_code: 1001"); // ERROR_INVALID_VERSION = 1001
156156
});
157+
158+
it("should correctly get guardian set", async () => {
159+
await deployContract();
160+
161+
const getGuardianSetResult = await wormholeTest.getGuardianSet(0);
162+
expect(getGuardianSetResult.keys).toEqual(GUARDIAN_SET_0);
163+
});
157164
});
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
This update contains price information for two assets:
3+
1. BTC/USD (id: e62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43)
4+
price:
5+
- price: 5924002645461
6+
- conf: 2528354539
7+
- expo: -8
8+
- publish_time: 1724826310
9+
ema_price:
10+
- price: 5938984900000
11+
- conf: 2304424610
12+
- expo: -8
13+
- publish_time: 1724826310
14+
metadata:
15+
- slot: 161371489
16+
- proof_available_time: 1724826311
17+
- prev_publish_time: 1724826310
18+
2. ETH/USD (id: ff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace)
19+
price:
20+
- price: 246682322909
21+
- conf: 87014791
22+
- expo: -8
23+
- publish_time: 1724826310
24+
ema_price:
25+
- price: 247166473000
26+
- conf: 113877153
27+
- expo: -8
28+
- publish_time: 1724826310
29+
metadata:
30+
- slot: 161371489
31+
- proof_available_time: 1724826311
32+
- prev_publish_time: 1724826310
33+
*/
34+
export const HERMES_BTC_ETH_UPDATE =
35+
"504e41550100000003b801000000040d00a0bb18e08c0a4152eba8293e88d0ed43084dfd4677fd5dc0ff48b05d065e25511ea12181325e92541290f28e00487a7ed852fdecbee414cab803dbe1dac2392201023e177888eba8922eac9b0668566f15c61e945cd47c10fa4ca2e4d472d7d216f149f3378b4edc5f8d802c3ef9b8156ca53c9ae2d4f75dd91f7713946b4108c5910003af26c2426a1bf19f24a171bcc990dad056b670f76894e3bdb9925b21b40b3904757d8e6175133b8608431d7435e29c5fcc2912349c2c8b5588803c06f203c73401048a30050ebafd161c3cfa5531896040b6da88502734c8e42ca3197d52ea08f6ec785a24f24bc9325c16ee7b6a9791bc523523f9086162ed4ccf746b55e1b0f192010886c5256df6ca2719fe97c10b79a4a8c8574fb70add9bfe0d879ae5f6c69b2459360b50b58c43a65e881081174cce56827880e0c330b5c5681294dc3fcb78a86d010a4e0ebb1992f0e48263f6188cb5f8e871cdcd7879f54fe7ad53bbd28d5e7ff70e73441836f0d11076bd7a791aceb05d501500f6878cf26e641fffa7c8fd143371000b3ad70bd80e52a82cd740ffbd4a080bd22523bc7ac2b1242169516d7aaf2753cd7ee5b500134ef32c02284e9d806fbeab2e055ea4a94be9cbfcfbc39b249b5e6b010c97d60c0f15b18c8fb2f36331ab0a1ce0efa13e9f2118c32140bd2118823d50f12deffc40b5d0c9642b4e44a6bd1cf4f38de471536a6610e698a942f049abef35010da2004619d8b31e33037ffed4afdd97459a241dfc7fa3bcc426f175461c938a182db560547dfcdd8ede345f0cc69da33fd588c30e912b7521c3ac1b0455882628000e81868d37eb16e1988451c26cfea8bb7969ce11c89488cedea30c80e3416dd2147c0554e9a9cce1a864eb0db625baa2cbb226ae2c2f1051f84b0a711c4bf69647010f02f18088ddbabd7c4528a1f7582f5fb11e60c5e434e9fd4ca2b33d6646e2ac6e6459c651778d1531711b44d2a1204a0d9c17e218aba5e60800e80aade9f1d90400108c783ad40f93184ad4f7e84229b207b17099e78b8bd93ddf2434cba21c99b4a904d74555ced9977e6becc34fa346c3cca9332b3598e66e58eb56f9ac700074e0001270a31d95bd5426ffe943dcc2b93f05b93f8301848f0b19c56e0dea51b7742c467b6bb557f6fc6762ef4600988c2dbcad0a2be84d4c6839fbae05d227e30ce5f50166cec2c600000000001ae101faedac5851e32b9b23b5f9411a8c2bac4aae3ed4dd7b811dd1a72ea4aa71000000000493073601415557560000000000099e556100002710c0905b1576f0bb86fe861a51273f2bcc43d12dd702005500e62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43000005634a12c5d50000000096b3a0ebfffffff80000000066cec2c60000000066cec2c600000566c715d5a000000000895abaa20a83db6da7dfbe7cc34f56265123320d7765dda3ae132c1518c53ead6cde500c139f68a894f564d817c0dfaeefa80d4ed93d36b82f7fcfe80e4092bb54d4dae770124803c592f17cb918c9ac381ce82bd6817041aa5ae95d917d75687b7a389a188846dd79bd55cb6cb9b9d0e1c0c040f1110362a8e1e87c74887326d66b213d19f7dcd1766b6f8505be50f5c98783c07ec08f913cbe38c20a31e440e42bb5a8883356dd19288e618e938ae4e7031d52d684f7bd1ddcf0d4ff63844800e14ff0c6888ff26626ea9874005500ff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace000000396f6987dd00000000052fbd87fffffff80000000066cec2c60000000066cec2c6000000398c4513280000000006c9a0a10a5307d6b780c8c5579b4b61de5fe0f12a789efdc628f14603df77ba31734c5611291df9a86e51430b243b3b61ee716bd758360783bdb7ef2f96ab8e92d509d85940a832b3de426d243a6d52ccf1e538af48b1bfad061dec1e4293898ca9ca9f37d0050d7b9e6a54357e77e7d79156fa1c70d54484ce52df49e79ab3fde203359f760fd9cf78be10644a43b52a171026829814590e8ba4853f6c8f0a16ce0fe3b532bcc96ea72723ab7d19f700c153e7cfb950f4aaa691e731e6f1de4edf43a6b9dd532a8ee785f084";

0 commit comments

Comments
 (0)