main.sw 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937
  1. contract;
  2. use std::{
  3. asset_id::AssetId,
  4. block::timestamp,
  5. bytes::Bytes,
  6. call_frames::msg_asset_id,
  7. constants::{
  8. ZERO_B256,
  9. },
  10. context::msg_amount,
  11. hash::{
  12. Hash,
  13. keccak256,
  14. sha256,
  15. },
  16. revert::revert,
  17. storage::{
  18. storage_map::StorageMap,
  19. storage_vec::*,
  20. },
  21. };
  22. use pyth_interface::{
  23. data_structures::{
  24. batch_attestation_update::*,
  25. data_source::*,
  26. governance_instruction::*,
  27. governance_payload::*,
  28. price::*,
  29. update_type::UpdateType,
  30. wormhole_light::*,
  31. },
  32. errors::{
  33. PythError,
  34. WormholeError,
  35. },
  36. events::{
  37. ConstructedEvent,
  38. ContractUpgradedEvent,
  39. DataSourcesSetEvent,
  40. FeeSetEvent,
  41. GovernanceDataSourceSetEvent,
  42. NewGuardianSetEvent,
  43. UpdatedPriceFeedsEvent,
  44. ValidPeriodSetEvent,
  45. },
  46. pyth_merkle_proof::validate_proof,
  47. PythCore,
  48. PythInfo,
  49. PythInit,
  50. utils::total_fee,
  51. WormholeGuardians,
  52. };
  53. use ownership::*;
  54. use src5::{SRC5, State};
  55. const GUARDIAN_SET_EXPIRATION_TIME_SECONDS: u64 = 86400; // 24 hours in seconds
  56. configurable {
  57. DEPLOYER: Identity = Identity::Address(Address::from(b256::zero())),
  58. }
  59. storage {
  60. // | |
  61. // --+-- PYTH STATE --+--
  62. // | |
  63. // (chainId, emitterAddress) => isValid; takes advantage of
  64. // constant-time mapping lookup for VM verification
  65. is_valid_data_source: StorageMap<DataSource, bool> = StorageMap {},
  66. // Mapping of cached price information
  67. // priceId => PriceInfo
  68. latest_price_feed: StorageMap<PriceFeedId, PriceFeed> = StorageMap {},
  69. // Fee required for each update
  70. single_update_fee: u64 = 0,
  71. // For tracking all active emitter/chain ID pairs
  72. valid_data_sources: StorageVec<DataSource> = StorageVec {},
  73. /// Maximum acceptable time period before price is considered to be stale.
  74. /// This includes attestation delay, block time, and potential clock drift
  75. /// between the source/target chains.
  76. valid_time_period_seconds: u64 = 0,
  77. /// Governance data source. VAA messages from this source can change this contract
  78. /// state. e.g., upgrade the contract, change the valid data sources, and more.
  79. governance_data_source: DataSource = DataSource {
  80. chain_id: 0u16,
  81. emitter_address: ZERO_B256,
  82. },
  83. /// Index of the governance data source, increased each time the governance data source changes.
  84. governance_data_source_index: u32 = 0,
  85. /// Sequence number of the last executed governance message. Any governance message
  86. /// with a lower or equal sequence number will be discarded. This prevents double-execution,
  87. /// and also makes sure that messages are executed in the right order.
  88. last_executed_governance_sequence: u64 = 0,
  89. /// Chain ID of the contract
  90. chain_id: u16 = 0,
  91. /// | |
  92. /// --+-- WORMHOLE STATE --+--
  93. /// | |
  94. /// Mapping of consumed governance actions
  95. wormhole_consumed_governance_actions: StorageMap<b256, bool> = StorageMap {},
  96. /// Mapping of guardian_set_index => guardian set
  97. wormhole_guardian_sets: StorageMap<u32, StorageGuardianSet> = StorageMap {},
  98. /// Current active guardian set index
  99. wormhole_guardian_set_index: u32 = 0,
  100. /// Using Ethereum's Wormhole governance
  101. wormhole_governance_data_source: DataSource = DataSource {
  102. chain_id: 0u16,
  103. emitter_address: ZERO_B256,
  104. },
  105. /// | |
  106. /// --+-- GOVERNANCE STATE --+--
  107. /// | |
  108. current_implementation: Identity = Identity::Address(Address::from(ZERO_B256)),
  109. }
  110. impl SRC5 for Contract {
  111. #[storage(read)]
  112. fn owner() -> State {
  113. _owner()
  114. }
  115. }
  116. impl PythCore for Contract {
  117. #[storage(read)]
  118. fn ema_price(price_feed_id: PriceFeedId) -> Price {
  119. ema_price_no_older_than(valid_time_period(), price_feed_id)
  120. }
  121. #[storage(read)]
  122. fn ema_price_no_older_than(time_period: u64, price_feed_id: PriceFeedId) -> Price {
  123. ema_price_no_older_than(time_period, price_feed_id)
  124. }
  125. #[storage(read)]
  126. fn ema_price_unsafe(price_feed_id: PriceFeedId) -> Price {
  127. ema_price_unsafe(price_feed_id)
  128. }
  129. #[storage(read), payable]
  130. fn parse_price_feed_updates(
  131. max_publish_time: u64,
  132. min_publish_time: u64,
  133. target_price_feed_ids: Vec<PriceFeedId>,
  134. update_data: Vec<Bytes>,
  135. ) -> Vec<PriceFeed> {
  136. require(
  137. msg_asset_id() == AssetId::base(),
  138. PythError::FeesCanOnlyBePaidInTheBaseAsset,
  139. );
  140. let required_fee = update_fee(update_data);
  141. require(msg_amount() >= required_fee, PythError::InsufficientFee);
  142. let mut output_price_feeds: Vec<PriceFeed> = Vec::with_capacity(target_price_feed_ids.len());
  143. let mut i = 0;
  144. while i < update_data.len() {
  145. let data = update_data.get(i).unwrap();
  146. match UpdateType::determine_type(data) {
  147. UpdateType::Accumulator(accumulator_update) => {
  148. let (mut offset, digest, number_of_updates, encoded) = accumulator_update.verify_and_parse(
  149. current_guardian_set_index(),
  150. storage
  151. .wormhole_guardian_sets,
  152. storage
  153. .is_valid_data_source,
  154. );
  155. let mut i_2 = 0;
  156. while i_2 < number_of_updates {
  157. let (new_offset, price_feed) = PriceFeed::extract_from_merkle_proof(digest, encoded, offset);
  158. offset = new_offset;
  159. if price_feed.id.is_target(target_price_feed_ids) == false {
  160. i_2 += 1;
  161. continue;
  162. }
  163. if price_feed.price.publish_time >= min_publish_time && price_feed.price.publish_time <= max_publish_time {
  164. // check if output_price_feeds already contains a PriceFeed with price_feed.id, if so continue as we only want 1
  165. // output PriceFeed per target ID
  166. if price_feed.id.is_contained_within(output_price_feeds) {
  167. i_2 += 1;
  168. continue;
  169. }
  170. output_price_feeds.push(price_feed)
  171. }
  172. i_2 += 1;
  173. }
  174. require(offset == encoded.len(), PythError::InvalidUpdateDataLength);
  175. },
  176. UpdateType::BatchAttestation(batch_attestation_update) => {
  177. let vm = WormholeVM::parse_and_verify_pyth_vm(
  178. current_guardian_set_index(),
  179. batch_attestation_update
  180. .data,
  181. storage
  182. .wormhole_guardian_sets,
  183. storage
  184. .is_valid_data_source,
  185. );
  186. let (mut attestation_index, number_of_attestations, attestation_size) = parse_and_verify_batch_attestation_header(vm.payload);
  187. let attestation_size_u16 = attestation_size.as_u64();
  188. let mut i_2: u16 = 0;
  189. while i_2 < number_of_attestations {
  190. let (_, slice) = vm.payload.split_at(attestation_index + 32);
  191. let (price_feed_id, _) = slice.split_at(32);
  192. let price_feed_id: PriceFeedId = b256::from_be_bytes(price_feed_id.clone());
  193. if price_feed_id.is_target(target_price_feed_ids) == false {
  194. attestation_index += attestation_size_u16;
  195. i_2 += 1;
  196. continue;
  197. }
  198. let price_feed = PriceFeed::parse_attestation(attestation_size, vm.payload, attestation_index);
  199. if price_feed.price.publish_time >= min_publish_time && price_feed.price.publish_time <= max_publish_time {
  200. // check if output_price_feeds already contains a PriceFeed with price_feed.id, if so continue;
  201. // as we only want 1 output PriceFeed per target ID
  202. if price_feed.id.is_contained_within(output_price_feeds) {
  203. attestation_index += attestation_size_u16;
  204. i_2 += 1;
  205. continue;
  206. }
  207. output_price_feeds.push(price_feed)
  208. }
  209. attestation_index += attestation_size_u16;
  210. i_2 += 1;
  211. }
  212. }
  213. }
  214. i += 1;
  215. }
  216. require(
  217. target_price_feed_ids
  218. .len() == output_price_feeds
  219. .len(),
  220. PythError::PriceFeedNotFoundWithinRange,
  221. );
  222. output_price_feeds
  223. }
  224. #[storage(read)]
  225. fn price(price_feed_id: PriceFeedId) -> Price {
  226. price_no_older_than(valid_time_period(), price_feed_id)
  227. }
  228. #[storage(read)]
  229. fn price_no_older_than(time_period: u64, price_feed_id: PriceFeedId) -> Price {
  230. price_no_older_than(time_period, price_feed_id)
  231. }
  232. #[storage(read)]
  233. fn price_unsafe(price_feed_id: PriceFeedId) -> Price {
  234. price_unsafe(price_feed_id)
  235. }
  236. #[storage(read)]
  237. fn update_fee(update_data: Vec<Bytes>) -> u64 {
  238. update_fee(update_data)
  239. }
  240. #[storage(read, write), payable]
  241. fn update_price_feeds(update_data: Vec<Bytes>) {
  242. update_price_feeds(update_data)
  243. }
  244. #[storage(read, write), payable]
  245. fn update_price_feeds_if_necessary(
  246. price_feed_ids: Vec<PriceFeedId>,
  247. publish_times: Vec<u64>,
  248. update_data: Vec<Bytes>,
  249. ) {
  250. require(
  251. price_feed_ids
  252. .len() == publish_times
  253. .len(),
  254. PythError::LengthOfPriceFeedIdsAndPublishTimesMustMatch,
  255. );
  256. let mut i = 0;
  257. while i < price_feed_ids.len() {
  258. if latest_publish_time(price_feed_ids.get(i).unwrap()) < publish_times.get(i).unwrap()
  259. {
  260. update_price_feeds(update_data);
  261. return;
  262. }
  263. i += 1;
  264. }
  265. }
  266. #[storage(read)]
  267. fn valid_time_period() -> u64 {
  268. valid_time_period()
  269. }
  270. }
  271. /// PythCore Private Functions ///
  272. #[storage(read)]
  273. fn ema_price_no_older_than(time_period: u64, price_feed_id: PriceFeedId) -> Price {
  274. let price = ema_price_unsafe(price_feed_id);
  275. let current_time = timestamp();
  276. require(
  277. current_time - price.publish_time <= time_period,
  278. PythError::OutdatedPrice,
  279. );
  280. price
  281. }
  282. #[storage(read)]
  283. fn ema_price_unsafe(price_feed_id: PriceFeedId) -> Price {
  284. let price_feed = storage.latest_price_feed.get(price_feed_id).try_read();
  285. require(price_feed.is_some(), PythError::PriceFeedNotFound);
  286. price_feed.unwrap().ema_price
  287. }
  288. #[storage(read)]
  289. fn price_no_older_than(time_period: u64, price_feed_id: PriceFeedId) -> Price {
  290. let price = price_unsafe(price_feed_id);
  291. let current_time = timestamp();
  292. // Mimicking saturating subtraction to avoid underflow
  293. let time_difference = if current_time > price.publish_time {
  294. current_time - price.publish_time
  295. } else {
  296. 0
  297. };
  298. require(time_difference <= time_period, PythError::OutdatedPrice);
  299. price
  300. }
  301. #[storage(read)]
  302. fn price_unsafe(price_feed_id: PriceFeedId) -> Price {
  303. let price_feed = storage.latest_price_feed.get(price_feed_id).try_read();
  304. require(price_feed.is_some(), PythError::PriceFeedNotFound);
  305. price_feed.unwrap().price
  306. }
  307. #[storage(read)]
  308. fn update_fee(update_data: Vec<Bytes>) -> u64 {
  309. let mut total_number_of_updates = 0;
  310. let mut i = 0;
  311. while i < update_data.len() {
  312. let data = update_data.get(i).unwrap();
  313. match UpdateType::determine_type(data) {
  314. UpdateType::Accumulator(accumulator_update) => {
  315. let proof_size_offset = accumulator_update.verify();
  316. total_number_of_updates += accumulator_update.total_updates(proof_size_offset);
  317. },
  318. UpdateType::BatchAttestation => {
  319. total_number_of_updates += 1;
  320. },
  321. }
  322. i += 1;
  323. }
  324. total_fee(total_number_of_updates, storage.single_update_fee)
  325. }
  326. #[storage(read, write)]
  327. fn update_price_feeds(update_data: Vec<Bytes>) {
  328. require(
  329. msg_asset_id() == AssetId::base(),
  330. PythError::FeesCanOnlyBePaidInTheBaseAsset,
  331. );
  332. let mut total_number_of_updates = 0;
  333. // let mut updated_price_feeds: Vec<PriceFeedId> = Vec::new(); // TODO: requires append for Vec
  334. let mut i = 0;
  335. while i < update_data.len() {
  336. let data = update_data.get(i).unwrap();
  337. match UpdateType::determine_type(data) {
  338. UpdateType::Accumulator(accumulator_update) => {
  339. let (number_of_updates, _updated_ids) = accumulator_update.update_price_feeds(
  340. current_guardian_set_index(),
  341. storage
  342. .wormhole_guardian_sets,
  343. storage
  344. .latest_price_feed,
  345. storage
  346. .is_valid_data_source,
  347. );
  348. // updated_price_feeds.append(updated_ids); // TODO: requires append for Vec
  349. total_number_of_updates += number_of_updates;
  350. },
  351. UpdateType::BatchAttestation(batch_attestation_update) => {
  352. let _updated_ids = batch_attestation_update.update_price_feeds(
  353. current_guardian_set_index(),
  354. storage
  355. .wormhole_guardian_sets,
  356. storage
  357. .latest_price_feed,
  358. storage
  359. .is_valid_data_source,
  360. );
  361. // updated_price_feeds.append(updated_ids); // TODO: requires append for Vec
  362. total_number_of_updates += 1;
  363. },
  364. }
  365. i += 1;
  366. }
  367. let required_fee = total_fee(total_number_of_updates, storage.single_update_fee);
  368. require(msg_amount() >= required_fee, PythError::InsufficientFee);
  369. // log(UpdatedPriceFeedsEvent { // TODO: requires append for Vec
  370. // updated_price_feeds,
  371. // })
  372. }
  373. #[storage(read)]
  374. fn valid_time_period() -> u64 {
  375. storage.valid_time_period_seconds.read()
  376. }
  377. #[storage(read)]
  378. fn governance_data_source() -> DataSource {
  379. storage.governance_data_source.read()
  380. }
  381. #[storage(write)]
  382. fn set_governance_data_source(data_source: DataSource) {
  383. storage.governance_data_source.write(data_source);
  384. }
  385. #[storage(read)]
  386. fn governance_data_source_index() -> u32 {
  387. storage.governance_data_source_index.read()
  388. }
  389. #[storage(write)]
  390. fn set_governance_data_source_index(index: u32) {
  391. storage.governance_data_source_index.write(index);
  392. }
  393. #[storage(read)]
  394. fn last_executed_governance_sequence() -> u64 {
  395. storage.last_executed_governance_sequence.read()
  396. }
  397. #[storage(write)]
  398. fn set_last_executed_governance_sequence(sequence: u64) {
  399. storage.last_executed_governance_sequence.write(sequence);
  400. }
  401. #[storage(read)]
  402. fn chain_id() -> u16 {
  403. storage.chain_id.read()
  404. }
  405. #[storage(read)]
  406. fn current_implementation() -> Identity {
  407. storage.current_implementation.read()
  408. }
  409. impl PythInit for Contract {
  410. #[storage(read, write)]
  411. fn constructor(
  412. data_sources: Vec<DataSource>,
  413. governance_data_source: DataSource,
  414. wormhole_governance_data_source: DataSource,
  415. single_update_fee: u64,
  416. valid_time_period_seconds: u64,
  417. wormhole_guardian_set_addresses: Vec<b256>,
  418. wormhole_guardian_set_index: u32,
  419. chain_id: u16,
  420. ) {
  421. // This function sets the passed identity as the initial owner. https://github.com/FuelLabs/sway-libs/blob/8045a19e3297599750abdf6300c11e9927a29d40/libs/src/ownership.sw#L127-L138
  422. initialize_ownership(DEPLOYER);
  423. // This function ensures that the sender is the owner. https://github.com/FuelLabs/sway-libs/blob/8045a19e3297599750abdf6300c11e9927a29d40/libs/src/ownership.sw#L59-L65
  424. only_owner();
  425. require(data_sources.len() > 0, PythError::InvalidDataSourcesLength);
  426. let mut i = 0;
  427. while i < data_sources.len() {
  428. let data_source = data_sources.get(i).unwrap();
  429. storage.is_valid_data_source.insert(data_source, true);
  430. storage.valid_data_sources.push(data_source);
  431. i += 1;
  432. }
  433. storage
  434. .latest_price_feed
  435. .write(StorageMap::<PriceFeedId, PriceFeed> {});
  436. storage
  437. .valid_time_period_seconds
  438. .write(valid_time_period_seconds);
  439. storage.single_update_fee.write(single_update_fee);
  440. let guardian_length: u8 = wormhole_guardian_set_addresses.len().try_as_u8().unwrap();
  441. let mut new_guardian_set = StorageGuardianSet::new(
  442. 0,
  443. StorageKey::<StorageVec<b256>>::new(
  444. sha256(("guardian_set_keys", wormhole_guardian_set_index)),
  445. 0,
  446. ZERO_B256,
  447. ),
  448. );
  449. let mut i: u8 = 0;
  450. while i < guardian_length {
  451. let key: b256 = wormhole_guardian_set_addresses.get(i.as_u64()).unwrap();
  452. new_guardian_set.keys.push(key);
  453. i += 1;
  454. }
  455. storage
  456. .wormhole_guardian_set_index
  457. .write(wormhole_guardian_set_index);
  458. storage
  459. .wormhole_guardian_sets
  460. .insert(wormhole_guardian_set_index, new_guardian_set);
  461. storage.governance_data_source.write(governance_data_source);
  462. storage
  463. .wormhole_governance_data_source
  464. .write(wormhole_governance_data_source);
  465. storage.governance_data_source_index.write(0);
  466. storage
  467. .wormhole_consumed_governance_actions
  468. .write(StorageMap::<b256, bool> {});
  469. storage.chain_id.write(chain_id);
  470. storage.last_executed_governance_sequence.write(0);
  471. storage
  472. .current_implementation
  473. .write(Identity::Address(Address::from(ZERO_B256)));
  474. // This function revokes ownership of the current owner and disallows any new owners. https://github.com/FuelLabs/sway-libs/blob/8045a19e3297599750abdf6300c11e9927a29d40/libs/src/ownership.sw#L89-L99
  475. renounce_ownership();
  476. log(ConstructedEvent {
  477. guardian_set_index: wormhole_guardian_set_index,
  478. })
  479. }
  480. }
  481. impl PythInfo for Contract {
  482. #[storage(read)]
  483. fn valid_data_sources() -> Vec<DataSource> {
  484. storage.valid_data_sources.load_vec()
  485. }
  486. #[storage(read)]
  487. fn latest_publish_time(price_feed_id: PriceFeedId) -> u64 {
  488. latest_publish_time(price_feed_id)
  489. }
  490. #[storage(read)]
  491. fn price_feed_exists(price_feed_id: PriceFeedId) -> bool {
  492. match storage.latest_price_feed.get(price_feed_id).try_read() {
  493. Some(_) => true,
  494. None => false,
  495. }
  496. }
  497. #[storage(read)]
  498. fn price_feed_unsafe(price_feed_id: PriceFeedId) -> PriceFeed {
  499. let price_feed = storage.latest_price_feed.get(price_feed_id).try_read();
  500. require(price_feed.is_some(), PythError::PriceFeedNotFound);
  501. price_feed.unwrap()
  502. }
  503. #[storage(read)]
  504. fn single_update_fee() -> u64 {
  505. storage.single_update_fee.read()
  506. }
  507. #[storage(read)]
  508. fn is_valid_data_source(data_source: DataSource) -> bool {
  509. data_source.is_valid_data_source(storage.is_valid_data_source)
  510. }
  511. #[storage(read)]
  512. fn last_executed_governance_sequence() -> u64 {
  513. last_executed_governance_sequence()
  514. }
  515. #[storage(read)]
  516. fn chain_id() -> u16 {
  517. chain_id()
  518. }
  519. }
  520. /// PythInfo Private Functions ///
  521. #[storage(read)]
  522. fn latest_publish_time(price_feed_id: PriceFeedId) -> u64 {
  523. match storage.latest_price_feed.get(price_feed_id).try_read() {
  524. Some(price_feed) => price_feed.price.publish_time,
  525. None => 0,
  526. }
  527. }
  528. impl WormholeGuardians for Contract {
  529. #[storage(read)]
  530. fn current_guardian_set_index() -> u32 {
  531. current_guardian_set_index()
  532. }
  533. #[storage(read)]
  534. fn current_wormhole_provider() -> DataSource {
  535. current_wormhole_provider()
  536. }
  537. #[storage(read)]
  538. fn guardian_set(index: u32) -> GuardianSet {
  539. let stored_guardian_set = storage.wormhole_guardian_sets.get(index).try_read();
  540. require(
  541. stored_guardian_set
  542. .is_some(),
  543. PythError::GuardianSetNotFound,
  544. );
  545. GuardianSet::from_stored(stored_guardian_set.unwrap())
  546. }
  547. #[storage(read)]
  548. fn governance_action_is_consumed(governance_action_hash: b256) -> bool {
  549. governance_action_is_consumed(governance_action_hash)
  550. }
  551. #[storage(read, write)]
  552. fn submit_new_guardian_set(encoded_vm: Bytes) {
  553. submit_new_guardian_set(encoded_vm)
  554. }
  555. }
  556. /// WormholeGuardians Private Functions ///
  557. #[storage(read)]
  558. fn current_guardian_set_index() -> u32 {
  559. storage.wormhole_guardian_set_index.read()
  560. }
  561. #[storage(read)]
  562. fn current_wormhole_provider() -> DataSource {
  563. storage.wormhole_governance_data_source.read()
  564. }
  565. #[storage(read)]
  566. fn governance_action_is_consumed(governance_action_hash: b256) -> bool {
  567. match storage.wormhole_consumed_governance_actions.get(governance_action_hash).try_read() {
  568. Some(bool_) => bool_,
  569. None => false,
  570. }
  571. }
  572. #[storage(read, write)]
  573. fn submit_new_guardian_set(encoded_vm: Bytes) {
  574. let vm: WormholeVM = WormholeVM::parse_and_verify_wormhole_vm(
  575. current_guardian_set_index(),
  576. encoded_vm,
  577. storage
  578. .wormhole_guardian_sets,
  579. );
  580. require(
  581. vm.guardian_set_index == current_guardian_set_index(),
  582. WormholeError::NotSignedByCurrentGuardianSet,
  583. );
  584. let current_wormhole_provider: DataSource = current_wormhole_provider();
  585. require(
  586. vm.emitter_chain_id == current_wormhole_provider
  587. .chain_id,
  588. WormholeError::InvalidGovernanceChain,
  589. );
  590. require(
  591. vm.emitter_address == current_wormhole_provider
  592. .emitter_address,
  593. WormholeError::InvalidGovernanceContract,
  594. );
  595. require(
  596. governance_action_is_consumed(vm.governance_action_hash) == false,
  597. WormholeError::GovernanceActionAlreadyConsumed,
  598. );
  599. let current_guardian_set_index: u32 = current_guardian_set_index();
  600. let upgrade: GuardianSetUpgrade = GuardianSetUpgrade::parse_encoded_upgrade(current_guardian_set_index, vm.payload);
  601. storage
  602. .wormhole_consumed_governance_actions
  603. .insert(vm.governance_action_hash, true);
  604. // Set expiry if current GuardianSet exists
  605. let current_guardian_set = storage.wormhole_guardian_sets.get(current_guardian_set_index).try_read();
  606. if current_guardian_set.is_some() {
  607. let mut current_guardian_set = current_guardian_set.unwrap();
  608. current_guardian_set.expiration_time = timestamp() + GUARDIAN_SET_EXPIRATION_TIME_SECONDS;
  609. storage
  610. .wormhole_guardian_sets
  611. .insert(current_guardian_set_index, current_guardian_set);
  612. }
  613. storage
  614. .wormhole_guardian_sets
  615. .insert(upgrade.new_guardian_set_index, upgrade.new_guardian_set);
  616. storage
  617. .wormhole_guardian_set_index
  618. .write(upgrade.new_guardian_set_index);
  619. log(NewGuardianSetEvent {
  620. governance_action_hash: vm.governance_action_hash,
  621. new_guardian_set_index: upgrade.new_guardian_set_index,
  622. })
  623. }
  624. /// Transfer the governance data source to a new value with sanity checks to ensure the new governance data source can manage the contract.
  625. #[storage(read, write)]
  626. fn authorize_governance_data_source_transfer(
  627. payload: AuthorizeGovernanceDataSourceTransferPayload,
  628. ) {
  629. let old_governance_data_source = governance_data_source();
  630. // Parse and verify the VAA contained in the payload to ensure it's valid and can manage the contract
  631. let vm: WormholeVM = WormholeVM::parse_and_verify_wormhole_vm(
  632. current_guardian_set_index(),
  633. payload
  634. .claim_vaa,
  635. storage
  636. .wormhole_guardian_sets,
  637. );
  638. let gi = GovernanceInstruction::parse_governance_instruction(vm.payload);
  639. require(
  640. gi.target_chain_id == chain_id() || gi.target_chain_id == 0,
  641. PythError::InvalidGovernanceTarget,
  642. );
  643. require(
  644. match gi.action {
  645. GovernanceAction::RequestGovernanceDataSourceTransfer => true,
  646. _ => false,
  647. },
  648. PythError::InvalidGovernanceMessage,
  649. );
  650. let claim_payload = GovernanceInstruction::parse_request_governance_data_source_transfer_payload(gi.payload);
  651. require(
  652. governance_data_source_index() < claim_payload
  653. .governance_data_source_index,
  654. PythError::OldGovernanceMessage,
  655. );
  656. set_governance_data_source_index(claim_payload.governance_data_source_index);
  657. let new_governance_data_source = DataSource {
  658. chain_id: vm.emitter_chain_id,
  659. emitter_address: vm.emitter_address,
  660. };
  661. set_governance_data_source(new_governance_data_source);
  662. // Setting the last executed governance to the claimVaa sequence to avoid using older sequences.
  663. set_last_executed_governance_sequence(vm.sequence);
  664. log(GovernanceDataSourceSetEvent {
  665. old_data_source: old_governance_data_source,
  666. new_data_source: new_governance_data_source,
  667. initial_sequence: vm.sequence,
  668. });
  669. }
  670. #[storage(read, write)]
  671. fn set_data_sources(payload: SetDataSourcesPayload) {
  672. let old_data_sources = storage.valid_data_sources.load_vec();
  673. let mut i = 0;
  674. while i < old_data_sources.len() {
  675. let data_source = old_data_sources.get(i).unwrap();
  676. storage.is_valid_data_source.insert(data_source, false);
  677. i += 1;
  678. }
  679. // Clear the current list of valid data sources
  680. storage.valid_data_sources.clear();
  681. i = 0;
  682. // Add new data sources from the payload and mark them as valid
  683. while i < payload.data_sources.len() {
  684. let data_source = payload.data_sources.get(i).unwrap();
  685. storage.valid_data_sources.push(data_source);
  686. storage.is_valid_data_source.insert(data_source, true);
  687. i += 1;
  688. }
  689. // Emit an event with the old and new data sources
  690. log(DataSourcesSetEvent {
  691. old_data_sources: old_data_sources,
  692. new_data_sources: storage.valid_data_sources.load_vec(),
  693. });
  694. }
  695. #[storage(read, write)]
  696. fn set_fee(payload: SetFeePayload) {
  697. let old_fee = storage.single_update_fee.read();
  698. storage.single_update_fee.write(payload.new_fee);
  699. log(FeeSetEvent {
  700. old_fee,
  701. new_fee: payload.new_fee,
  702. });
  703. }
  704. #[storage(read, write)]
  705. fn set_valid_period(payload: SetValidPeriodPayload) {
  706. let old_valid_period = storage.valid_time_period_seconds.read();
  707. storage
  708. .valid_time_period_seconds
  709. .write(payload.new_valid_period);
  710. log(ValidPeriodSetEvent {
  711. old_valid_period,
  712. new_valid_period: payload.new_valid_period,
  713. });
  714. }
  715. abi PythGovernance {
  716. #[storage(read)]
  717. fn governance_data_source() -> DataSource;
  718. #[storage(read, write)]
  719. fn execute_governance_instruction(encoded_vm: Bytes);
  720. }
  721. impl PythGovernance for Contract {
  722. #[storage(read)]
  723. fn governance_data_source() -> DataSource {
  724. governance_data_source()
  725. }
  726. #[storage(read, write)]
  727. fn execute_governance_instruction(encoded_vm: Bytes) {
  728. execute_governance_instruction(encoded_vm)
  729. }
  730. }
  731. #[storage(read, write)]
  732. fn execute_governance_instruction(encoded_vm: Bytes) {
  733. let vm = verify_governance_vm(encoded_vm);
  734. // Log so that the WormholeVM struct will show up in the ABI and can be used in the tests
  735. log(vm);
  736. let gi = GovernanceInstruction::parse_governance_instruction(vm.payload);
  737. // Log so that the GovernanceInstruction struct will show up in the ABI and can be used in the tests
  738. log(gi);
  739. require(
  740. gi.target_chain_id == chain_id() || gi.target_chain_id == 0,
  741. PythError::InvalidGovernanceTarget,
  742. );
  743. match gi.action {
  744. GovernanceAction::UpgradeContract => {
  745. require(gi.target_chain_id != 0, PythError::InvalidGovernanceTarget);
  746. // TODO: implement upgrade_upgradeable_contract(uc) when Fuel releases the upgrade standard library;
  747. log("Upgrade functionality not implemented");
  748. revert(0u64);
  749. },
  750. GovernanceAction::AuthorizeGovernanceDataSourceTransfer => {
  751. let agdst = GovernanceInstruction::parse_authorize_governance_data_source_transfer_payload(gi.payload);
  752. log(agdst);
  753. authorize_governance_data_source_transfer(agdst);
  754. },
  755. GovernanceAction::SetDataSources => {
  756. let sdsp = GovernanceInstruction::parse_set_data_sources_payload(gi.payload);
  757. log(sdsp);
  758. set_data_sources(sdsp);
  759. },
  760. GovernanceAction::SetFee => {
  761. let sf = GovernanceInstruction::parse_set_fee_payload(gi.payload);
  762. log(sf);
  763. set_fee(sf);
  764. },
  765. GovernanceAction::SetValidPeriod => {
  766. let svp = GovernanceInstruction::parse_set_valid_period_payload(gi.payload);
  767. log(svp);
  768. set_valid_period(svp);
  769. },
  770. GovernanceAction::RequestGovernanceDataSourceTransfer => {
  771. // RequestGovernanceDataSourceTransfer can be only part of AuthorizeGovernanceDataSourceTransfer message
  772. // The `revert` function only accepts u64, so as
  773. // a workaround we use require.
  774. require(false, PythError::InvalidGovernanceMessage);
  775. },
  776. _ => {
  777. // The `revert` function only accepts u64, so as
  778. // a workaround we use require.
  779. require(false, PythError::InvalidGovernanceMessage);
  780. }
  781. }
  782. }
  783. #[storage(read, write)]
  784. fn verify_governance_vm(encoded_vm: Bytes) -> WormholeVM {
  785. let vm: WormholeVM = WormholeVM::parse_and_verify_wormhole_vm(
  786. current_guardian_set_index(),
  787. encoded_vm,
  788. storage
  789. .wormhole_guardian_sets,
  790. );
  791. require(
  792. storage
  793. .governance_data_source
  794. .read()
  795. .is_valid_governance_data_source(vm.emitter_chain_id, vm.emitter_address),
  796. PythError::InvalidGovernanceDataSource,
  797. );
  798. require(
  799. vm.sequence > last_executed_governance_sequence(),
  800. PythError::OldGovernanceMessage,
  801. );
  802. set_last_executed_governance_sequence(vm.sequence);
  803. vm
  804. }