Pyth.fc 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  1. #include "imports/stdlib.fc";
  2. #include "common/errors.fc";
  3. #include "common/storage.fc";
  4. #include "common/utils.fc";
  5. #include "common/constants.fc";
  6. #include "common/merkle_tree.fc";
  7. #include "common/governance_actions.fc";
  8. #include "common/gas.fc";
  9. #include "common/op.fc";
  10. #include "./Wormhole.fc";
  11. cell store_price(int price, int conf, int expo, int publish_time) {
  12. return begin_cell()
  13. .store_int(price, 64)
  14. .store_uint(conf, 64)
  15. .store_int(expo, 32)
  16. .store_uint(publish_time, 64)
  17. .end_cell();
  18. }
  19. slice read_and_verify_header(slice data) impure {
  20. int magic = data~load_uint(32);
  21. throw_unless(ERROR_INVALID_MAGIC, magic == ACCUMULATOR_MAGIC);
  22. int major_version = data~load_uint(8);
  23. throw_unless(ERROR_INVALID_MAJOR_VERSION, major_version == MAJOR_VERSION);
  24. int minor_version = data~load_uint(8);
  25. throw_if(ERROR_INVALID_MINOR_VERSION, minor_version < MINIMUM_ALLOWED_MINOR_VERSION);
  26. int trailing_header_size = data~load_uint(8);
  27. ;; skip trailing headers
  28. data~skip_bits(trailing_header_size * 8);
  29. int update_type = data~load_uint(8);
  30. throw_unless(ERROR_INVALID_UPDATE_DATA_TYPE, update_type == WORMHOLE_MERKLE_UPDATE_TYPE);
  31. return data;
  32. }
  33. (int, int, int, int, int, int, int, int, slice) read_and_verify_message(slice cs, int root_digest) impure {
  34. int message_size = cs~load_uint(16);
  35. (cell message, slice cs) = read_and_store_large_data(cs, message_size * 8);
  36. slice message = message.begin_parse();
  37. slice cs = read_and_verify_proof(root_digest, message, cs);
  38. int message_type = message~load_uint(8);
  39. throw_unless(ERROR_INVALID_MESSAGE_TYPE, message_type == PRICE_FEED_MESSAGE_TYPE);
  40. int price_id = message~load_uint(256);
  41. int price = message~load_int(64);
  42. int conf = message~load_uint(64);
  43. int expo = message~load_int(32);
  44. int publish_time = message~load_uint(64);
  45. int prev_publish_time = message~load_uint(64);
  46. int ema_price = message~load_int(64);
  47. int ema_conf = message~load_uint(64);
  48. return (price_id, price, conf, expo, publish_time, prev_publish_time, ema_price, ema_conf, cs);
  49. }
  50. (int, int, int, int) parse_price(slice price_feed) {
  51. int price = price_feed~load_int(64);
  52. int conf = price_feed~load_uint(64);
  53. int expo = price_feed~load_int(32);
  54. int publish_time = price_feed~load_uint(64);
  55. return (price, conf, expo, publish_time);
  56. }
  57. int get_update_fee(slice data) method_id {
  58. load_data();
  59. slice cs = read_and_verify_header(data);
  60. int wormhole_proof_size_bytes = cs~load_uint(16);
  61. (cell wormhole_proof, slice cs) = read_and_store_large_data(cs, wormhole_proof_size_bytes * 8);
  62. int num_updates = cs~load_uint(8);
  63. return single_update_fee * num_updates;
  64. }
  65. int get_single_update_fee() method_id {
  66. load_data();
  67. return single_update_fee;
  68. }
  69. int get_governance_data_source_index() method_id {
  70. load_data();
  71. return governance_data_source_index;
  72. }
  73. cell get_governance_data_source() method_id {
  74. load_data();
  75. return governance_data_source;
  76. }
  77. int get_last_executed_governance_sequence() method_id {
  78. load_data();
  79. return last_executed_governance_sequence;
  80. }
  81. int get_is_valid_data_source(cell data_source) method_id {
  82. load_data();
  83. int data_source_key = cell_hash(data_source);
  84. (slice value, int found?) = is_valid_data_source.udict_get?(256, data_source_key);
  85. if (found?) {
  86. return value~load_int(1);
  87. } else {
  88. return 0;
  89. }
  90. }
  91. (int, int, int, int) get_price_unsafe(int price_feed_id) method_id {
  92. load_data();
  93. (slice result, int success) = latest_price_feeds.udict_get?(256, price_feed_id);
  94. throw_unless(ERROR_PRICE_FEED_NOT_FOUND, success);
  95. slice price_feed = result~load_ref().begin_parse();
  96. slice price = price_feed~load_ref().begin_parse();
  97. return parse_price(price);
  98. }
  99. (int, int, int, int) get_price_no_older_than(int time_period, int price_feed_id) method_id {
  100. load_data();
  101. (int price, int conf, int expo, int publish_time) = get_price_unsafe(price_feed_id);
  102. int current_time = now();
  103. throw_if(ERROR_OUTDATED_PRICE, max(0, current_time - publish_time) > time_period);
  104. return (price, conf, expo, publish_time);
  105. }
  106. (int, int, int, int) get_ema_price_unsafe(int price_feed_id) method_id {
  107. load_data();
  108. (slice result, int success) = latest_price_feeds.udict_get?(256, price_feed_id);
  109. throw_unless(ERROR_PRICE_FEED_NOT_FOUND, success);
  110. slice price_feed = result~load_ref().begin_parse();
  111. slice price = price_feed~load_ref().begin_parse();
  112. slice ema_price = price_feed~load_ref().begin_parse();
  113. return parse_price(ema_price);
  114. }
  115. (int, int, int, int) get_ema_price_no_older_than(int time_period, int price_feed_id) method_id {
  116. load_data();
  117. (int price, int conf, int expo, int publish_time) = get_ema_price_unsafe(price_feed_id);
  118. int current_time = now();
  119. throw_if(ERROR_OUTDATED_PRICE, max(0, current_time - publish_time) > time_period);
  120. return (price, conf, expo, publish_time);
  121. }
  122. (int, int) parse_data_source(cell data_source) {
  123. slice ds = data_source.begin_parse();
  124. int emitter_chain = ds~load_uint(16);
  125. int emitter_address = ds~load_uint(256);
  126. return (emitter_chain, emitter_address);
  127. }
  128. int parse_pyth_payload_in_wormhole_vm(slice payload) impure {
  129. int accumulator_wormhole_magic = payload~load_uint(32);
  130. throw_unless(ERROR_INVALID_MAGIC, accumulator_wormhole_magic == ACCUMULATOR_WORMHOLE_MAGIC);
  131. int update_type = payload~load_uint(8);
  132. throw_unless(ERROR_INVALID_UPDATE_DATA_TYPE, update_type == WORMHOLE_MERKLE_UPDATE_TYPE);
  133. payload~load_uint(64); ;; Skip slot
  134. payload~load_uint(32); ;; Skip ring_size
  135. return payload~load_uint(160); ;; Return root_digest
  136. }
  137. () calculate_and_validate_fees(int msg_value, int num_updates) impure {
  138. int update_fee = single_update_fee * num_updates;
  139. int compute_fee = get_compute_fee(
  140. WORKCHAIN,
  141. UPDATE_PRICE_FEEDS_BASE_GAS + (UPDATE_PRICE_FEEDS_PER_UPDATE_GAS * num_updates)
  142. );
  143. throw_unless(ERROR_INSUFFICIENT_GAS, msg_value >= compute_fee);
  144. int remaining_msg_value = msg_value - compute_fee;
  145. ;; Check if the sender has sent enough TON to cover the update_fee
  146. throw_unless(ERROR_INSUFFICIENT_FEE, remaining_msg_value >= update_fee);
  147. }
  148. (int) find_price_id_index(tuple price_ids, int price_id) {
  149. int len = price_ids.tlen();
  150. int i = 0;
  151. while (i < len) {
  152. if (price_ids.at(i) == price_id) {
  153. return i;
  154. }
  155. i += 1;
  156. }
  157. return -1; ;; Not found
  158. }
  159. tuple parse_price_feeds_from_data(int msg_value, slice data, tuple price_ids, int min_publish_time, int max_publish_time, int unique) {
  160. slice cs = read_and_verify_header(data);
  161. int wormhole_proof_size_bytes = cs~load_uint(16);
  162. (cell wormhole_proof, slice new_cs) = read_and_store_large_data(cs, wormhole_proof_size_bytes * 8);
  163. cs = new_cs;
  164. int num_updates = cs~load_uint(8);
  165. calculate_and_validate_fees(msg_value, num_updates);
  166. (_, _, _, _, int emitter_chain_id, int emitter_address, _, _, slice payload, _) = parse_and_verify_wormhole_vm(wormhole_proof.begin_parse());
  167. ;; Check if the data source is valid
  168. cell data_source = begin_cell()
  169. .store_uint(emitter_chain_id, 16)
  170. .store_uint(emitter_address, 256)
  171. .end_cell();
  172. ;; Dictionary doesn't support cell as key, so we use cell_hash to create a 256-bit integer key
  173. int data_source_key = cell_hash(data_source);
  174. (slice value, int found?) = is_valid_data_source.udict_get?(256, data_source_key);
  175. throw_unless(ERROR_UPDATE_DATA_SOURCE_NOT_FOUND, found?);
  176. int valid = value~load_int(1);
  177. throw_unless(ERROR_INVALID_UPDATE_DATA_SOURCE, valid);
  178. int root_digest = parse_pyth_payload_in_wormhole_vm(payload);
  179. ;; Create dictionary to store price feeds in order (dict has a udict_get_next? method which returns the next key in order)
  180. cell ordered_feeds = new_dict();
  181. ;; Track which price IDs we've found
  182. cell found_price_ids = new_dict();
  183. int index = 0;
  184. repeat(num_updates) {
  185. (int price_id, int price, int conf, int expo, int publish_time, int prev_publish_time, int ema_price, int ema_conf, slice new_cs) = read_and_verify_message(cs, root_digest);
  186. cs = new_cs;
  187. int price_ids_len = price_ids.tlen();
  188. ;; Check if we've already processed this price_id to avoid duplicates
  189. (_, int already_processed?) = found_price_ids.udict_get?(256, price_id);
  190. if (~ already_processed?) { ;; Only process if we haven't seen this price_id yet
  191. int should_include = (price_ids_len == 0)
  192. | ((price_ids_len > 0)
  193. & (publish_time >= min_publish_time)
  194. & (publish_time <= max_publish_time)
  195. & ((unique == 0) | (min_publish_time > prev_publish_time)));
  196. if (should_include) {
  197. ;; Create price feed cell containing both current and EMA prices
  198. cell price_feed_cell = begin_cell()
  199. .store_ref(store_price(price, conf, expo, publish_time))
  200. .store_ref(store_price(ema_price, ema_conf, expo, publish_time))
  201. .end_cell();
  202. if (price_ids_len == 0) {
  203. ordered_feeds~udict_set(8, index, begin_cell()
  204. .store_uint(price_id, 256)
  205. .store_ref(price_feed_cell)
  206. .end_cell().begin_parse());
  207. index += 1;
  208. } else {
  209. index = find_price_id_index(price_ids, price_id);
  210. if (index >= 0) {
  211. ordered_feeds~udict_set(8, index, begin_cell()
  212. .store_uint(price_id, 256)
  213. .store_ref(price_feed_cell)
  214. .end_cell().begin_parse());
  215. }
  216. }
  217. ;; Mark this price ID as found
  218. found_price_ids~udict_set(256, price_id, begin_cell().store_int(true, 1).end_cell().begin_parse());
  219. }
  220. }
  221. }
  222. throw_if(ERROR_INVALID_UPDATE_DATA_LENGTH, ~ cs.slice_empty?());
  223. ;; Verify all requested price IDs were found
  224. if (price_ids.tlen() > 0) {
  225. int i = 0;
  226. repeat(price_ids.tlen()) {
  227. int requested_id = price_ids.at(i);
  228. (_, int found?) = found_price_ids.udict_get?(256, requested_id);
  229. throw_unless(ERROR_PRICE_FEED_NOT_FOUND_WITHIN_RANGE, found?);
  230. i += 1;
  231. }
  232. }
  233. ;; Create final ordered tuple from dictionary
  234. tuple price_feeds = empty_tuple();
  235. int index = -1;
  236. do {
  237. (index, slice value, int success) = ordered_feeds.udict_get_next?(8, index);
  238. if (success) {
  239. tuple price_feed = empty_tuple();
  240. price_feed~tpush(value~load_uint(256)); ;; price_id
  241. price_feed~tpush(value~load_ref()); ;; price_feed_cell
  242. price_feeds~tpush(price_feed);
  243. }
  244. } until (~ success);
  245. return price_feeds;
  246. }
  247. ;; Creates a chain of cells from price feeds, with each cell containing exactly one price_id (256 bits)
  248. ;; and one ref to the price feed cell. Returns the head of the chain.
  249. ;; Each cell now contains exactly:
  250. ;; - One price_id (256 bits)
  251. ;; - One ref to price_feed_cell
  252. ;; - One optional ref to next cell in chain
  253. ;; This approach is:
  254. ;; - More consistent with TON's cell model
  255. ;; - Easier to traverse and read individual price feeds
  256. ;; - Cleaner separation of data
  257. ;; - More predictable in terms of cell structure
  258. cell create_price_feed_cell_chain(tuple price_feeds) {
  259. cell result = null();
  260. int i = price_feeds.tlen() - 1;
  261. while (i >= 0) {
  262. tuple price_feed = price_feeds.at(i);
  263. int price_id = price_feed.at(0);
  264. cell price_feed_cell = price_feed.at(1);
  265. ;; Create new cell with single price feed and chain to previous result
  266. builder current_builder = begin_cell()
  267. .store_uint(price_id, 256) ;; Store price_id
  268. .store_ref(price_feed_cell); ;; Store price data ref
  269. ;; Chain to previous cells if they exist
  270. if (~ cell_null?(result)) {
  271. current_builder = current_builder.store_ref(result);
  272. }
  273. result = current_builder.end_cell();
  274. i -= 1;
  275. }
  276. return result;
  277. }
  278. () send_price_feeds_response(tuple price_feeds, int msg_value, int op, slice sender_address) impure {
  279. ;; Build response cell with price feeds
  280. builder response = begin_cell()
  281. .store_uint(op, 32) ;; Response op
  282. .store_uint(price_feeds.tlen(), 8); ;; Number of price feeds
  283. ;; Create and store price feed cell chain
  284. cell price_feeds_cell = create_price_feed_cell_chain(price_feeds);
  285. response = response.store_ref(price_feeds_cell);
  286. ;; Build the complete message cell (https://docs.ton.org/v3/documentation/smart-contracts/message-management/sending-messages#message-layout)
  287. cell msg = begin_cell()
  288. .store_uint(0x18, 6)
  289. .store_slice(sender_address)
  290. .store_coins(0) ;; Will fill in actual amount after fee calculations
  291. .store_uint(1, 1 + 4 + 4 + 64 + 32 + 1 + 1)
  292. .store_ref(response.end_cell())
  293. .end_cell();
  294. int num_price_feeds = price_feeds.tlen();
  295. ;; Number of cells in the message
  296. ;; - 2 cells: msg + response
  297. int cells = 2 + num_price_feeds;
  298. ;; Bit layout per TL-B spec (https://github.com/ton-blockchain/ton/blob/master/crypto/block/block.tlb):
  299. ;; - 6 bits: optimized way of serializing the tag and the first 4 fields
  300. ;; - 256 bits: owner address
  301. ;; - 128 bits: coins (VarUInteger 16) from grams$_ amount:(VarUInteger 16) = Grams
  302. ;; - 107 bits: other data (extra_currencies + ihr_fee + fwd_fee + lt of transaction + unixtime of transaction + no init-field flag + inplace message body flag)
  303. ;; - PRICE_FEED_BITS * num_price_feeds: space for each price feed
  304. int bits = 6 + 256 + 128 + 107 + (PRICE_FEED_BITS * num_price_feeds);
  305. int fwd_fee = get_forward_fee(cells, bits, WORKCHAIN);
  306. ;; Calculate all fees
  307. int compute_fee = get_compute_fee(WORKCHAIN, get_gas_consumed());
  308. int update_fee = single_update_fee * price_feeds.tlen();
  309. ;; Calculate total fees and remaining excess
  310. int total_fees = compute_fee + update_fee + fwd_fee;
  311. int excess = msg_value - total_fees;
  312. ;; Send response message back to sender with exact excess amount
  313. send_raw_message(begin_cell()
  314. .store_uint(0x18, 6)
  315. .store_slice(sender_address)
  316. .store_coins(excess)
  317. .store_uint(1, 1 + 4 + 4 + 64 + 32 + 1 + 1)
  318. .store_ref(response.end_cell())
  319. .end_cell(),
  320. 0);
  321. }
  322. () parse_price_feed_updates(int msg_value, slice update_data_slice, slice price_ids_slice, int min_publish_time, int max_publish_time, slice sender_address) impure {
  323. load_data();
  324. ;; Load price_ids tuple
  325. int price_ids_len = price_ids_slice~load_uint(8);
  326. tuple price_ids = empty_tuple();
  327. repeat(price_ids_len) {
  328. int price_id = price_ids_slice~load_uint(256);
  329. price_ids~tpush(price_id);
  330. }
  331. tuple price_feeds = parse_price_feeds_from_data(msg_value, update_data_slice, price_ids, min_publish_time, max_publish_time, false);
  332. send_price_feeds_response(price_feeds, msg_value, OP_PARSE_PRICE_FEED_UPDATES, sender_address);
  333. }
  334. () parse_unique_price_feed_updates(int msg_value, slice update_data_slice, slice price_ids_slice, int publish_time, int max_staleness, slice sender_address) impure {
  335. load_data();
  336. ;; Load price_ids tuple
  337. int price_ids_len = price_ids_slice~load_uint(8);
  338. tuple price_ids = empty_tuple();
  339. repeat(price_ids_len) {
  340. int price_id = price_ids_slice~load_uint(256);
  341. price_ids~tpush(price_id);
  342. }
  343. tuple price_feeds = parse_price_feeds_from_data(msg_value, update_data_slice, price_ids, publish_time, publish_time + max_staleness, true);
  344. send_price_feeds_response(price_feeds, msg_value, OP_PARSE_UNIQUE_PRICE_FEED_UPDATES, sender_address);
  345. }
  346. () update_price_feeds(int msg_value, slice data) impure {
  347. load_data();
  348. tuple price_feeds = parse_price_feeds_from_data(msg_value, data, empty_tuple(), 0, 0, false);
  349. int num_updates = price_feeds.tlen();
  350. int i = 0;
  351. while(i < num_updates) {
  352. tuple price_feed = price_feeds.at(i);
  353. int price_id = price_feed.at(0);
  354. cell price_feed_cell = price_feed.at(1);
  355. slice price_feed = price_feed_cell.begin_parse();
  356. slice price = price_feed~load_ref().begin_parse();
  357. slice ema_price = price_feed~load_ref().begin_parse();
  358. (int price_, int conf, int expo, int publish_time) = parse_price(price);
  359. (slice latest_price_info, int found?) = latest_price_feeds.udict_get?(256, price_id);
  360. int latest_publish_time = 0;
  361. if (found?) {
  362. slice price_feed_slice = latest_price_info~load_ref().begin_parse();
  363. slice price_slice = price_feed_slice~load_ref().begin_parse();
  364. price_slice~load_int(64); ;; Skip price
  365. price_slice~load_uint(64); ;; Skip conf
  366. price_slice~load_int(32); ;; Skip expo
  367. latest_publish_time = price_slice~load_uint(64);
  368. }
  369. if (publish_time > latest_publish_time) {
  370. latest_price_feeds~udict_set(256, price_id, begin_cell().store_ref(price_feed_cell).end_cell().begin_parse());
  371. }
  372. i += 1;
  373. }
  374. store_data();
  375. }
  376. () verify_governance_vm(int emitter_chain_id, int emitter_address, int sequence) impure {
  377. (int gov_chain_id, int gov_address) = parse_data_source(governance_data_source);
  378. throw_unless(ERROR_INVALID_GOVERNANCE_DATA_SOURCE, emitter_chain_id == gov_chain_id);
  379. throw_unless(ERROR_INVALID_GOVERNANCE_DATA_SOURCE, emitter_address == gov_address);
  380. throw_if(ERROR_OLD_GOVERNANCE_MESSAGE, sequence <= last_executed_governance_sequence);
  381. last_executed_governance_sequence = sequence;
  382. }
  383. (int, int, slice) parse_governance_instruction(slice payload) impure {
  384. int magic = payload~load_uint(32);
  385. throw_unless(ERROR_INVALID_GOVERNANCE_MAGIC, magic == GOVERNANCE_MAGIC);
  386. int module = payload~load_uint(8);
  387. throw_unless(ERROR_INVALID_GOVERNANCE_MODULE, module == GOVERNANCE_MODULE);
  388. int action = payload~load_uint(8);
  389. int target_chain_id = payload~load_uint(16);
  390. return (target_chain_id, action, payload);
  391. }
  392. int apply_decimal_expo(int value, int expo) {
  393. int result = value;
  394. repeat (expo) {
  395. result *= 10;
  396. }
  397. return result;
  398. }
  399. () execute_upgrade_contract(cell new_code) impure {
  400. load_data();
  401. int hash_code = cell_hash(new_code);
  402. throw_unless(ERROR_INVALID_CODE_HASH, upgrade_code_hash == hash_code);
  403. ;; Set the new code
  404. set_code(new_code);
  405. ;; Set the code continuation to the new code
  406. set_c3(new_code.begin_parse().bless());
  407. ;; Reset the upgrade code hash
  408. upgrade_code_hash = 0;
  409. ;; Throw an exception to end the current execution
  410. ;; The contract will be restarted with the new code
  411. throw(0);
  412. }
  413. () execute_authorize_upgrade_contract(slice payload) impure {
  414. int code_hash = payload~load_uint(256);
  415. upgrade_code_hash = code_hash;
  416. }
  417. () execute_authorize_governance_data_source_transfer(slice payload) impure {
  418. ;; Verify the claim VAA
  419. (_, _, _, _, int emitter_chain_id, int emitter_address, int sequence, _, slice claim_payload, _) = parse_and_verify_wormhole_vm(payload);
  420. ;; Parse the claim payload
  421. (int target_chain_id, int action, slice claim_payload) = parse_governance_instruction(claim_payload);
  422. ;; Verify that this is a valid governance action for this chain
  423. throw_if(ERROR_INVALID_GOVERNANCE_TARGET, (target_chain_id != 0) & (target_chain_id != chain_id));
  424. throw_unless(ERROR_INVALID_GOVERNANCE_ACTION, action == REQUEST_GOVERNANCE_DATA_SOURCE_TRANSFER);
  425. ;; Extract the new governance data source index from the claim payload
  426. int new_governance_data_source_index = claim_payload~load_uint(32);
  427. ;; Verify that the new index is greater than the current index
  428. int current_index = governance_data_source_index;
  429. throw_if(ERROR_OLD_GOVERNANCE_MESSAGE, new_governance_data_source_index <= current_index);
  430. ;; Update the governance data source
  431. governance_data_source = begin_cell()
  432. .store_uint(emitter_chain_id, 16)
  433. .store_uint(emitter_address, 256)
  434. .end_cell();
  435. governance_data_source_index = new_governance_data_source_index;
  436. ;; Update the last executed governance sequence
  437. last_executed_governance_sequence = sequence;
  438. }
  439. () execute_set_data_sources(slice payload) impure {
  440. int num_sources = payload~load_uint(8);
  441. cell new_data_sources = new_dict();
  442. repeat(num_sources) {
  443. (cell data_source, slice new_payload) = read_and_store_large_data(payload, 272); ;; 272 = 256 + 16
  444. payload = new_payload;
  445. slice data_source_slice = data_source.begin_parse();
  446. int emitter_chain_id = data_source_slice~load_uint(16);
  447. int emitter_address = data_source_slice~load_uint(256);
  448. cell data_source = begin_cell()
  449. .store_uint(emitter_chain_id, 16)
  450. .store_uint(emitter_address, 256)
  451. .end_cell();
  452. int data_source_key = cell_hash(data_source);
  453. new_data_sources~udict_set(256, data_source_key, begin_cell().store_int(true, 1).end_cell().begin_parse());
  454. }
  455. ;; Verify that all data in the payload was processed
  456. throw_unless(ERROR_INVALID_PAYLOAD_LENGTH, payload.slice_empty?());
  457. is_valid_data_source = new_data_sources;
  458. }
  459. () execute_set_fee(slice payload) impure {
  460. int value = payload~load_uint(64);
  461. int expo = payload~load_uint(64);
  462. int new_fee = apply_decimal_expo(value, expo);
  463. single_update_fee = new_fee;
  464. }
  465. () execute_governance_payload(int action, slice payload) impure {
  466. if (action == AUTHORIZE_UPGRADE_CONTRACT) {
  467. execute_authorize_upgrade_contract(payload);
  468. } elseif (action == AUTHORIZE_GOVERNANCE_DATA_SOURCE_TRANSFER) {
  469. execute_authorize_governance_data_source_transfer(payload);
  470. } elseif (action == SET_DATA_SOURCES) {
  471. execute_set_data_sources(payload);
  472. } elseif (action == SET_FEE) {
  473. execute_set_fee(payload);
  474. } elseif (action == SET_VALID_PERIOD) {
  475. ;; Unsupported governance action
  476. throw(ERROR_INVALID_GOVERNANCE_ACTION);
  477. } elseif (action == REQUEST_GOVERNANCE_DATA_SOURCE_TRANSFER) {
  478. ;; RequestGovernanceDataSourceTransfer can only be part of
  479. ;; AuthorizeGovernanceDataSourceTransfer message
  480. throw(ERROR_INVALID_GOVERNANCE_ACTION);
  481. } else {
  482. throw(ERROR_INVALID_GOVERNANCE_ACTION);
  483. }
  484. }
  485. () execute_governance_action(slice in_msg_body) impure {
  486. load_data();
  487. (_, _, _, _, int emitter_chain_id, int emitter_address, int sequence, _, slice payload, _) = parse_and_verify_wormhole_vm(in_msg_body);
  488. verify_governance_vm(emitter_chain_id, emitter_address, sequence);
  489. (int target_chain_id, int action, slice payload) = parse_governance_instruction(payload);
  490. throw_if(ERROR_INVALID_GOVERNANCE_TARGET, (target_chain_id != 0) & (target_chain_id != chain_id));
  491. execute_governance_payload(action, payload);
  492. store_data();
  493. }