pyth.move 75 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562
  1. module pyth::pyth {
  2. use std::vector;
  3. use sui::tx_context::{TxContext};
  4. use sui::coin::{Self, Coin};
  5. use sui::sui::{SUI};
  6. use sui::transfer::{Self};
  7. use sui::clock::{Self, Clock};
  8. use sui::package::{UpgradeCap};
  9. use pyth::event::{Self as pyth_event};
  10. use pyth::data_source::{Self, DataSource};
  11. use pyth::state::{Self as state, State as PythState, LatestOnly};
  12. use pyth::price_info::{Self, PriceInfo, PriceInfoObject};
  13. use pyth::batch_price_attestation::{Self};
  14. use pyth::price_feed::{Self};
  15. use pyth::price::{Self, Price};
  16. use pyth::price_identifier::{PriceIdentifier};
  17. use pyth::setup::{Self, DeployerCap};
  18. use pyth::hot_potato_vector::{Self, HotPotatoVector};
  19. use pyth::accumulator::{Self};
  20. use wormhole::external_address::{Self};
  21. use wormhole::vaa::{Self, VAA};
  22. use wormhole::bytes32::{Self};
  23. use wormhole::cursor::{Self};
  24. const E_DATA_SOURCE_EMITTER_ADDRESS_AND_CHAIN_IDS_DIFFERENT_LENGTHS: u64 = 0;
  25. const E_INVALID_DATA_SOURCE: u64 = 1;
  26. const E_INSUFFICIENT_FEE: u64 = 2;
  27. const E_STALE_PRICE_UPDATE: u64 = 3;
  28. const E_UPDATE_AND_PRICE_INFO_OBJECT_MISMATCH: u64 = 4;
  29. const E_PRICE_UPDATE_NOT_FOUND_FOR_PRICE_INFO_OBJECT: u64 = 5;
  30. const E_INVALID_ACCUMULATOR_MAGIC: u64 = 7;
  31. const PYTHNET_ACCUMULATOR_UPDATE_MAGIC: u64 = 1347305813;
  32. #[test_only]
  33. friend pyth::pyth_tests;
  34. #[test_only]
  35. friend pyth::set_data_sources_tests;
  36. #[test_only]
  37. friend pyth::set_stale_price_threshold_test;
  38. #[test_only]
  39. friend pyth::set_update_fee_tests;
  40. /// Init state and emit event corresponding to Pyth initialization.
  41. public entry fun init_pyth(
  42. deployer: DeployerCap,
  43. upgrade_cap: UpgradeCap,
  44. stale_price_threshold: u64,
  45. governance_emitter_chain_id: u64,
  46. governance_emitter_address: vector<u8>,
  47. data_sources_emitter_chain_ids: vector<u64>,
  48. data_sources_emitter_addresses: vector<vector<u8>>,
  49. update_fee: u64,
  50. ctx: &mut TxContext
  51. ) {
  52. setup::init_and_share_state(
  53. deployer,
  54. upgrade_cap,
  55. stale_price_threshold,
  56. update_fee,
  57. data_source::new(
  58. governance_emitter_chain_id,
  59. external_address::new((bytes32::from_bytes(governance_emitter_address)))
  60. ),
  61. parse_data_sources(
  62. data_sources_emitter_chain_ids,
  63. data_sources_emitter_addresses,
  64. ),
  65. ctx
  66. );
  67. // Emit Pyth initialization event.
  68. pyth_event::emit_pyth_initialization_event();
  69. }
  70. fun parse_data_sources(
  71. emitter_chain_ids: vector<u64>,
  72. emitter_addresses: vector<vector<u8>>
  73. ): vector<DataSource> {
  74. assert!(vector::length(&emitter_chain_ids) == vector::length(&emitter_addresses),
  75. E_DATA_SOURCE_EMITTER_ADDRESS_AND_CHAIN_IDS_DIFFERENT_LENGTHS);
  76. let sources = vector::empty();
  77. let i = 0;
  78. while (i < vector::length(&emitter_chain_ids)) {
  79. vector::push_back(&mut sources, data_source::new(
  80. *vector::borrow(&emitter_chain_ids, i),
  81. external_address::new(bytes32::from_bytes(*vector::borrow(&emitter_addresses, i)))
  82. ));
  83. i = i + 1;
  84. };
  85. sources
  86. }
  87. /// Create and share new price feed objects if they don't already exist using accumulator message.
  88. public fun create_price_feeds_using_accumulator(
  89. pyth_state: &mut PythState,
  90. accumulator_message: vector<u8>,
  91. vaa: VAA, // the verified version of the vaa bytes encoded within the accumulator_message
  92. clock: &Clock,
  93. ctx: &mut TxContext
  94. ){
  95. // This capability ensures that the current build version is used.
  96. let latest_only = state::assert_latest_only(pyth_state);
  97. // Check that the VAA is from a valid data source (emitter)
  98. assert!(
  99. state::is_valid_data_source(
  100. pyth_state,
  101. data_source::new(
  102. (vaa::emitter_chain(&vaa) as u64),
  103. vaa::emitter_address(&vaa))
  104. ),
  105. E_INVALID_DATA_SOURCE
  106. );
  107. // decode the price info updates from the VAA payload (first check if it is an accumulator or batch price update)
  108. let accumulator_message_cursor = cursor::new(accumulator_message);
  109. let price_infos = accumulator::parse_and_verify_accumulator_message(&mut accumulator_message_cursor, vaa::take_payload(vaa), clock);
  110. // Create and share new price info objects, if not already exists.
  111. create_and_share_price_feeds_using_verified_price_infos(&latest_only, pyth_state, price_infos, ctx);
  112. // destroy rest of cursor
  113. cursor::take_rest(accumulator_message_cursor);
  114. }
  115. /// Create and share new price feed objects if they don't already exist using batch price attestation.
  116. /// The name of the function is kept as is to remain backward compatible
  117. public fun create_price_feeds(
  118. pyth_state: &mut PythState,
  119. // These vaas have been verified and consumed, so we don't have to worry about
  120. // doing replay protection for them.
  121. verified_vaas: vector<VAA>,
  122. clock: &Clock,
  123. ctx: &mut TxContext
  124. ){
  125. // This capability ensures that the current build version is used.
  126. let latest_only = state::assert_latest_only(pyth_state);
  127. while (!vector::is_empty(&verified_vaas)) {
  128. let vaa = vector::pop_back(&mut verified_vaas);
  129. // Check that the VAA is from a valid data source (emitter)
  130. assert!(
  131. state::is_valid_data_source(
  132. pyth_state,
  133. data_source::new(
  134. (vaa::emitter_chain(&vaa) as u64),
  135. vaa::emitter_address(&vaa))
  136. ),
  137. E_INVALID_DATA_SOURCE
  138. );
  139. // Deserialize the batch price attestation
  140. let price_infos = batch_price_attestation::destroy(batch_price_attestation::deserialize(vaa::take_payload(vaa), clock));
  141. // Create and share new price info objects, if not already exists.
  142. create_and_share_price_feeds_using_verified_price_infos(&latest_only, pyth_state, price_infos, ctx);
  143. };
  144. vector::destroy_empty(verified_vaas);
  145. }
  146. // create_and_share_price_feeds_using_verified_price_infos is a private function used by
  147. // 1) create_price_feeds
  148. // 2) create_price_feeds_using_accumulator
  149. // to create new price feeds for symbols.
  150. fun create_and_share_price_feeds_using_verified_price_infos(latest_only: &LatestOnly, pyth_state: &mut PythState, price_infos: vector<PriceInfo>, ctx: &mut TxContext){
  151. while (!vector::is_empty(&price_infos)){
  152. let cur_price_info = vector::pop_back(&mut price_infos);
  153. // Only create new Sui PriceInfoObject if not already
  154. // registered with the Pyth State object.
  155. if (!state::price_feed_object_exists(
  156. pyth_state,
  157. price_feed::get_price_identifier(
  158. price_info::get_price_feed(&cur_price_info)
  159. )
  160. )
  161. ){
  162. // Create and share newly created Sui PriceInfoObject containing a price feed,
  163. // and then register a copy of its ID with State.
  164. let new_price_info_object = price_info::new_price_info_object(cur_price_info, ctx);
  165. let price_identifier = price_info::get_price_identifier(&cur_price_info);
  166. let id = price_info::uid_to_inner(&new_price_info_object);
  167. state::register_price_info_object(latest_only, pyth_state, price_identifier, id);
  168. transfer::public_share_object(new_price_info_object);
  169. }
  170. }
  171. }
  172. // verified_vaa is the verified version of the VAA encoded within the accumulator_message
  173. public fun create_authenticated_price_infos_using_accumulator(
  174. pyth_state: &PythState,
  175. accumulator_message: vector<u8>,
  176. verified_vaa: VAA,
  177. clock: &Clock,
  178. ): HotPotatoVector<PriceInfo> {
  179. state::assert_latest_only(pyth_state);
  180. // verify that the VAA originates from a valid data source
  181. assert!(
  182. state::is_valid_data_source(
  183. pyth_state,
  184. data_source::new(
  185. (vaa::emitter_chain(&verified_vaa) as u64),
  186. vaa::emitter_address(&verified_vaa))
  187. ),
  188. E_INVALID_DATA_SOURCE
  189. );
  190. // decode the price info updates from the VAA payload (first check if it is an accumulator or batch price update)
  191. let accumulator_message_cursor = cursor::new(accumulator_message);
  192. let price_infos = accumulator::parse_and_verify_accumulator_message(&mut accumulator_message_cursor, vaa::take_payload(verified_vaa), clock);
  193. // check that accumulator message has been fully consumed
  194. cursor::destroy_empty(accumulator_message_cursor);
  195. hot_potato_vector::new(price_infos)
  196. }
  197. /// Creates authenticated price infos using batch price attestation
  198. /// Name is kept as is to remain backward compatible
  199. public fun create_price_infos_hot_potato(
  200. pyth_state: &PythState,
  201. verified_vaas: vector<VAA>,
  202. clock: &Clock
  203. ): HotPotatoVector<PriceInfo> {
  204. state::assert_latest_only(pyth_state);
  205. let price_updates = vector::empty<PriceInfo>();
  206. while (vector::length(&verified_vaas) != 0){
  207. let cur_vaa = vector::pop_back(&mut verified_vaas);
  208. assert!(
  209. state::is_valid_data_source(
  210. pyth_state,
  211. data_source::new(
  212. (vaa::emitter_chain(&cur_vaa) as u64),
  213. vaa::emitter_address(&cur_vaa))
  214. ),
  215. E_INVALID_DATA_SOURCE
  216. );
  217. let price_infos = batch_price_attestation::destroy(batch_price_attestation::deserialize(vaa::take_payload(cur_vaa), clock));
  218. while (vector::length(&price_infos) !=0 ){
  219. let cur_price_info = vector::pop_back(&mut price_infos);
  220. vector::push_back(&mut price_updates, cur_price_info);
  221. }
  222. };
  223. vector::destroy_empty(verified_vaas);
  224. return hot_potato_vector::new(price_updates)
  225. }
  226. /// Update a singular Pyth PriceInfoObject (containing a price feed) with the
  227. /// price data in the authenticated price infos vector (a vector of PriceInfo objects).
  228. ///
  229. /// For more information on the end-to-end process for updating a price feed, please see the README.
  230. ///
  231. /// The given fee must contain a sufficient number of coins to pay the update fee for the given vaas.
  232. /// The update fee amount can be queried by calling get_update_fee(&vaas).
  233. ///
  234. /// Please read more information about the update fee here: https://docs.pyth.network/documentation/pythnet-price-feeds/on-demand#fees
  235. public fun update_single_price_feed(
  236. pyth_state: &PythState,
  237. price_updates: HotPotatoVector<PriceInfo>,
  238. price_info_object: &mut PriceInfoObject,
  239. fee: Coin<SUI>,
  240. clock: &Clock
  241. ): HotPotatoVector<PriceInfo> {
  242. let latest_only = state::assert_latest_only(pyth_state);
  243. // On Sui, users get to choose which price feeds to update. They specify a single price feed to
  244. // update at a time. We therefore charge the base fee for each such individual update.
  245. // This is a departure from Eth, where users don't get to necessarily choose.
  246. assert!(state::get_base_update_fee(pyth_state) <= coin::value(&fee), E_INSUFFICIENT_FEE);
  247. // store fee coins within price info object
  248. price_info::deposit_fee_coins(price_info_object, fee);
  249. // Find price update corresponding to PriceInfoObject within the array of price_updates
  250. // and use it to update PriceInfoObject.
  251. let i = 0;
  252. let found = false;
  253. while (i < hot_potato_vector::length<PriceInfo>(&price_updates)){
  254. let cur_price_info = hot_potato_vector::borrow<PriceInfo>(&price_updates, i);
  255. if (has_same_price_identifier(cur_price_info, price_info_object)){
  256. found = true;
  257. update_cache(latest_only, cur_price_info, price_info_object, clock);
  258. break
  259. };
  260. i = i + 1;
  261. };
  262. if (found==false){
  263. abort E_PRICE_UPDATE_NOT_FOUND_FOR_PRICE_INFO_OBJECT
  264. };
  265. price_updates
  266. }
  267. fun has_same_price_identifier(price_info: &PriceInfo, price_info_object: &PriceInfoObject) : bool {
  268. let price_info_from_object = price_info::get_price_info_from_price_info_object(price_info_object);
  269. let price_identifier_from_object = price_info::get_price_identifier(&price_info_from_object);
  270. let price_identifier_from_price_info = price_info::get_price_identifier(price_info);
  271. price_identifier_from_object == price_identifier_from_price_info
  272. }
  273. /// Update PriceInfoObject with updated data from a PriceInfo
  274. public(friend) fun update_cache(
  275. _: LatestOnly,
  276. update: &PriceInfo,
  277. price_info_object: &mut PriceInfoObject,
  278. clock: &Clock,
  279. ){
  280. let has_same_price_identifier = has_same_price_identifier(update, price_info_object);
  281. assert!(has_same_price_identifier, E_UPDATE_AND_PRICE_INFO_OBJECT_MISMATCH);
  282. // Update the price info object with the new updated price info.
  283. if (is_fresh_update(update, price_info_object)){
  284. pyth_event::emit_price_feed_update(price_feed::from(price_info::get_price_feed(update)), clock::timestamp_ms(clock)/1000);
  285. price_info::update_price_info_object(
  286. price_info_object,
  287. update
  288. );
  289. }
  290. }
  291. /// Determine if the given price update is "fresh": we have nothing newer already cached for that
  292. /// price feed within a PriceInfoObject.
  293. fun is_fresh_update(update: &PriceInfo, price_info_object: &PriceInfoObject): bool {
  294. // Get the timestamp of the update's current price
  295. let price_feed = price_info::get_price_feed(update);
  296. let update_timestamp = price::get_timestamp(&price_feed::get_price(price_feed));
  297. // Get the timestamp of the cached data for the price identifier
  298. let cached_price_info = price_info::get_price_info_from_price_info_object(price_info_object);
  299. let cached_price_feed = price_info::get_price_feed(&cached_price_info);
  300. let cached_timestamp = price::get_timestamp(&price_feed::get_price(cached_price_feed));
  301. update_timestamp > cached_timestamp
  302. }
  303. // -----------------------------------------------------------------------------
  304. // Query the cached prices
  305. //
  306. // It is strongly recommended to update the cached prices using the functions above,
  307. // before using the functions below to query the cached data.
  308. /// Determine if a price feed for the given price_identifier exists
  309. public fun price_feed_exists(state: &PythState, price_identifier: PriceIdentifier): bool {
  310. state::price_feed_object_exists(state, price_identifier)
  311. }
  312. /// Get the latest available price cached for the given price identifier, if that price is
  313. /// no older than the stale price threshold.
  314. ///
  315. /// Please refer to the documentation at https://docs.pyth.network/documentation/pythnet-price-feeds/best-practices for
  316. /// how to how this price safely.
  317. ///
  318. /// Important: Pyth uses an on-demand update model, where consumers need to update the
  319. /// cached prices before using them. Please read more about this at https://docs.pyth.network/documentation/pythnet-price-feeds/on-demand.
  320. /// get_price() is likely to abort unless you call update_price_feeds() to update the cached price
  321. /// beforehand, as the cached prices may be older than the stale price threshold.
  322. ///
  323. /// The price_info_object is a Sui object with the key ability that uniquely
  324. /// contains a price feed for a given price_identifier.
  325. ///
  326. public fun get_price(state: &PythState, price_info_object: &PriceInfoObject, clock: &Clock): Price {
  327. get_price_no_older_than(price_info_object, clock, state::get_stale_price_threshold_secs(state))
  328. }
  329. /// Get the latest available price cached for the given price identifier, if that price is
  330. /// no older than the given age.
  331. public fun get_price_no_older_than(price_info_object: &PriceInfoObject, clock: &Clock, max_age_secs: u64): Price {
  332. let price = get_price_unsafe(price_info_object);
  333. check_price_is_fresh(&price, clock, max_age_secs);
  334. price
  335. }
  336. /// Get the latest available price cached for the given price identifier.
  337. ///
  338. /// WARNING: the returned price can be from arbitrarily far in the past.
  339. /// This function makes no guarantees that the returned price is recent or
  340. /// useful for any particular application. Users of this function should check
  341. /// the returned timestamp to ensure that the returned price is sufficiently
  342. /// recent for their application. The checked get_price_no_older_than()
  343. /// function should be used in preference to this.
  344. public fun get_price_unsafe(price_info_object: &PriceInfoObject): Price {
  345. // TODO: extract Price from this guy...
  346. let price_info = price_info::get_price_info_from_price_info_object(price_info_object);
  347. price_feed::get_price(
  348. price_info::get_price_feed(&price_info)
  349. )
  350. }
  351. fun abs_diff(x: u64, y: u64): u64 {
  352. if (x > y) {
  353. return x - y
  354. } else {
  355. return y - x
  356. }
  357. }
  358. /// Get the stale price threshold: the amount of time after which a cached price
  359. /// is considered stale and no longer returned by get_price()/get_ema_price().
  360. public fun get_stale_price_threshold_secs(state: &PythState): u64 {
  361. state::get_stale_price_threshold_secs(state)
  362. }
  363. fun check_price_is_fresh(price: &Price, clock: &Clock, max_age_secs: u64) {
  364. let age = abs_diff(clock::timestamp_ms(clock)/1000, price::get_timestamp(price));
  365. assert!(age < max_age_secs, E_STALE_PRICE_UPDATE);
  366. }
  367. /// Please read more information about the update fee here: https://docs.pyth.network/documentation/pythnet-price-feeds/on-demand#fees
  368. public fun get_total_update_fee(pyth_state: &PythState, n: u64): u64 {
  369. state::get_base_update_fee(pyth_state) * n
  370. }
  371. }
  372. #[test_only]
  373. module pyth::pyth_tests{
  374. use std::vector::{Self};
  375. use sui::sui::SUI;
  376. use sui::coin::{Self, Coin};
  377. use sui::test_scenario::{Self, Scenario, ctx, take_shared, return_shared};
  378. use sui::package::Self;
  379. use sui::object::{Self, ID};
  380. use sui::clock::{Self, Clock};
  381. use pyth::state::{State as PythState};
  382. use pyth::setup::{Self};
  383. use pyth::price_info::{Self, PriceInfo, PriceInfoObject};//, PriceInfo, PriceInfoObject};
  384. use pyth::data_source::{Self, DataSource};
  385. use pyth::pyth::{Self, create_price_infos_hot_potato, update_single_price_feed};
  386. use pyth::hot_potato_vector::{Self};
  387. use pyth::price_identifier::{Self};
  388. use pyth::price_feed::{Self};
  389. use pyth::accumulator::{Self};
  390. use pyth::deserialize::{Self};
  391. use wormhole::setup::{Self as wormhole_setup, DeployerCap};
  392. use wormhole::external_address::{Self};
  393. use wormhole::bytes32::{Self};
  394. use wormhole::state::{State as WormState};
  395. use wormhole::vaa::{Self, VAA};
  396. use wormhole::cursor::{Self};
  397. const DEPLOYER: address = @0x1234;
  398. const ACCUMULATOR_TESTS_EMITTER_ADDRESS: vector<u8> = x"71f8dcb863d176e2c420ad6610cf687359612b6fb392e0642b0ca6b1f186aa3b";
  399. const ACCUMULATOR_TESTS_INITIAL_GUARDIANS: vector<vector<u8>> = vector[x"7E5F4552091A69125d5DfCb7b8C2659029395Bdf"];
  400. const DEFAULT_BASE_UPDATE_FEE: u64 = 50;
  401. const DEFAULT_COIN_TO_MINT: u64 = 5000;
  402. const BATCH_ATTESTATION_TEST_INITIAL_GUARDIANS: vector<vector<u8>> = vector[x"beFA429d57cD18b7F8A4d91A2da9AB4AF05d0FBe"];
  403. fun ACCUMULATOR_TESTS_DATA_SOURCE(): vector<DataSource> {
  404. vector[data_source::new(1, external_address::new(bytes32::from_bytes(ACCUMULATOR_TESTS_EMITTER_ADDRESS)))]
  405. }
  406. // /// A vector containing a single VAA with:
  407. // /// - emitter chain ID 17
  408. // /// - emitter address 0x71f8dcb863d176e2c420ad6610cf687359612b6fb392e0642b0ca6b1f186aa3b
  409. // /// - payload corresponding to the batch price attestation of the prices returned by get_mock_price_infos()
  410. const TEST_VAAS: vector<vector<u8>> = vector[x"0100000000010036eb563b80a24f4253bee6150eb8924e4bdf6e4fa1dfc759a6664d2e865b4b134651a7b021b7f1ce3bd078070b688b6f2e37ce2de0d9b48e6a78684561e49d5201527e4f9b00000001001171f8dcb863d176e2c420ad6610cf687359612b6fb392e0642b0ca6b1f186aa3b0000000000000001005032574800030000000102000400951436e0be37536be96f0896366089506a59763d036728332d3e3038047851aea7c6c75c89f14810ec1c54c03ab8f1864a4c4032791f05747f560faec380a695d1000000000000049a0000000000000008fffffffb00000000000005dc0000000000000003000000000100000001000000006329c0eb000000006329c0e9000000006329c0e400000000000006150000000000000007215258d81468614f6b7e194c5d145609394f67b041e93e6695dcc616faadd0603b9551a68d01d954d6387aff4df1529027ffb2fee413082e509feb29cc4904fe000000000000041a0000000000000003fffffffb00000000000005cb0000000000000003010000000100000001000000006329c0eb000000006329c0e9000000006329c0e4000000000000048600000000000000078ac9cf3ab299af710d735163726fdae0db8465280502eb9f801f74b3c1bd190333832fad6e36eb05a8972fe5f219b27b5b2bb2230a79ce79beb4c5c5e7ecc76d00000000000003f20000000000000002fffffffb00000000000005e70000000000000003010000000100000001000000006329c0eb000000006329c0e9000000006329c0e40000000000000685000000000000000861db714e9ff987b6fedf00d01f9fea6db7c30632d6fc83b7bc9459d7192bc44a21a28b4c6619968bd8c20e95b0aaed7df2187fd310275347e0376a2cd7427db800000000000006cb0000000000000001fffffffb00000000000005e40000000000000003010000000100000001000000006329c0eb000000006329c0e9000000006329c0e400000000000007970000000000000001"];
  411. fun get_verified_test_vaas(worm_state: &WormState, clock: &Clock): vector<VAA> {
  412. let test_vaas_: vector<vector<u8>> = vector[x"0100000000010036eb563b80a24f4253bee6150eb8924e4bdf6e4fa1dfc759a6664d2e865b4b134651a7b021b7f1ce3bd078070b688b6f2e37ce2de0d9b48e6a78684561e49d5201527e4f9b00000001001171f8dcb863d176e2c420ad6610cf687359612b6fb392e0642b0ca6b1f186aa3b0000000000000001005032574800030000000102000400951436e0be37536be96f0896366089506a59763d036728332d3e3038047851aea7c6c75c89f14810ec1c54c03ab8f1864a4c4032791f05747f560faec380a695d1000000000000049a0000000000000008fffffffb00000000000005dc0000000000000003000000000100000001000000006329c0eb000000006329c0e9000000006329c0e400000000000006150000000000000007215258d81468614f6b7e194c5d145609394f67b041e93e6695dcc616faadd0603b9551a68d01d954d6387aff4df1529027ffb2fee413082e509feb29cc4904fe000000000000041a0000000000000003fffffffb00000000000005cb0000000000000003010000000100000001000000006329c0eb000000006329c0e9000000006329c0e4000000000000048600000000000000078ac9cf3ab299af710d735163726fdae0db8465280502eb9f801f74b3c1bd190333832fad6e36eb05a8972fe5f219b27b5b2bb2230a79ce79beb4c5c5e7ecc76d00000000000003f20000000000000002fffffffb00000000000005e70000000000000003010000000100000001000000006329c0eb000000006329c0e9000000006329c0e40000000000000685000000000000000861db714e9ff987b6fedf00d01f9fea6db7c30632d6fc83b7bc9459d7192bc44a21a28b4c6619968bd8c20e95b0aaed7df2187fd310275347e0376a2cd7427db800000000000006cb0000000000000001fffffffb00000000000005e40000000000000003010000000100000001000000006329c0eb000000006329c0e9000000006329c0e400000000000007970000000000000001"];
  413. let verified_vaas_reversed = vector::empty<VAA>();
  414. let test_vaas = test_vaas_;
  415. let i = 0;
  416. while (i < vector::length(&test_vaas_)) {
  417. let cur_test_vaa = vector::pop_back(&mut test_vaas);
  418. let verified_vaa = vaa::parse_and_verify(worm_state, cur_test_vaa, clock);
  419. vector::push_back(&mut verified_vaas_reversed, verified_vaa);
  420. i=i+1;
  421. };
  422. let verified_vaas = vector::empty<VAA>();
  423. while (vector::length<VAA>(&verified_vaas_reversed)!=0){
  424. let cur = vector::pop_back(&mut verified_vaas_reversed);
  425. vector::push_back(&mut verified_vaas, cur);
  426. };
  427. vector::destroy_empty(verified_vaas_reversed);
  428. verified_vaas
  429. }
  430. // get_verified_vaa_from_accumulator_message parses the accumulator message up until the vaa, then
  431. // parses the vaa, yielding a verified wormhole::vaa::VAA object
  432. fun get_verified_vaa_from_accumulator_message(worm_state: &WormState, accumulator_message: vector<u8>, clock: &Clock): VAA {
  433. let _PYTHNET_ACCUMULATOR_UPDATE_MAGIC: u64 = 1347305813;
  434. let cursor = cursor::new(accumulator_message);
  435. let header: u32 = deserialize::deserialize_u32(&mut cursor);
  436. assert!((header as u64) == _PYTHNET_ACCUMULATOR_UPDATE_MAGIC, 0);
  437. let _major = deserialize::deserialize_u8(&mut cursor);
  438. let _minor = deserialize::deserialize_u8(&mut cursor);
  439. let trailing_size = deserialize::deserialize_u8(&mut cursor);
  440. deserialize::deserialize_vector(&mut cursor, (trailing_size as u64));
  441. let proof_type = deserialize::deserialize_u8(&mut cursor);
  442. assert!(proof_type == 0, 0);
  443. let vaa_size = deserialize::deserialize_u16(&mut cursor);
  444. let vaa = deserialize::deserialize_vector(&mut cursor, (vaa_size as u64));
  445. cursor::take_rest(cursor);
  446. vaa::parse_and_verify(worm_state, vaa, clock)
  447. }
  448. #[test_only]
  449. /// Init Wormhole core bridge state.
  450. /// Init Pyth state.
  451. /// Set initial Sui clock time.
  452. /// Mint some SUI fee coins.
  453. public fun setup_test(
  454. stale_price_threshold: u64,
  455. governance_emitter_chain_id: u64,
  456. governance_emitter_address: vector<u8>,
  457. data_sources: vector<DataSource>,
  458. initial_guardians: vector<vector<u8>>,
  459. base_update_fee: u64,
  460. to_mint: u64
  461. ): (Scenario, Coin<SUI>, Clock) {
  462. let scenario = test_scenario::begin(DEPLOYER);
  463. // Initialize Wormhole core bridge.
  464. wormhole_setup::init_test_only(ctx(&mut scenario));
  465. test_scenario::next_tx(&mut scenario, DEPLOYER);
  466. // Take the `DeployerCap` from the sender of the transaction.
  467. let deployer_cap =
  468. test_scenario::take_from_address<DeployerCap>(
  469. &scenario,
  470. DEPLOYER
  471. );
  472. // This will be created and sent to the transaction sender automatically
  473. // when the contract is published. This exists in place of grabbing
  474. // it from the sender.
  475. let upgrade_cap =
  476. package::test_publish(
  477. object::id_from_address(@wormhole),
  478. test_scenario::ctx(&mut scenario)
  479. );
  480. let governance_chain = 1234;
  481. let governance_contract =
  482. x"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
  483. let guardian_set_seconds_to_live = 5678;
  484. let message_fee = 350;
  485. let guardian_set_index = 0;
  486. wormhole_setup::complete(
  487. deployer_cap,
  488. upgrade_cap,
  489. governance_chain,
  490. governance_contract,
  491. guardian_set_index,
  492. initial_guardians,
  493. guardian_set_seconds_to_live,
  494. message_fee,
  495. test_scenario::ctx(&mut scenario)
  496. );
  497. // Initialize Pyth state.
  498. let pyth_upgrade_cap=
  499. package::test_publish(
  500. object::id_from_address(@pyth),
  501. test_scenario::ctx(&mut scenario)
  502. );
  503. setup::init_test_only(ctx(&mut scenario));
  504. test_scenario::next_tx(&mut scenario, DEPLOYER);
  505. let pyth_deployer_cap = test_scenario::take_from_address<setup::DeployerCap>(
  506. &scenario,
  507. DEPLOYER
  508. );
  509. setup::init_and_share_state(
  510. pyth_deployer_cap,
  511. pyth_upgrade_cap,
  512. stale_price_threshold,
  513. base_update_fee,
  514. data_source::new(governance_emitter_chain_id, external_address::new(bytes32::from_bytes(governance_emitter_address))),
  515. data_sources,
  516. ctx(&mut scenario)
  517. );
  518. let coins = coin::mint_for_testing<SUI>(to_mint, ctx(&mut scenario));
  519. let clock = clock::create_for_testing(ctx(&mut scenario));
  520. (scenario, coins, clock)
  521. }
  522. fun get_mock_price_infos(): vector<PriceInfo> {
  523. use pyth::i64::Self;
  524. use pyth::price::{Self};
  525. vector<PriceInfo>[
  526. price_info::new_price_info(
  527. 1663680747,
  528. 1663074349,
  529. price_feed::new(
  530. price_identifier::from_byte_vec(x"c6c75c89f14810ec1c54c03ab8f1864a4c4032791f05747f560faec380a695d1"),
  531. price::new(i64::new(1557, false), 7, i64::new(5, true), 1663680740),
  532. price::new(i64::new(1500, false), 3, i64::new(5, true), 1663680740),
  533. ),
  534. ),
  535. price_info::new_price_info(
  536. 1663680747,
  537. 1663074349,
  538. price_feed::new(
  539. price_identifier::from_byte_vec(x"3b9551a68d01d954d6387aff4df1529027ffb2fee413082e509feb29cc4904fe"),
  540. price::new(i64::new(1050, false), 3, i64::new(5, true), 1663680745),
  541. price::new(i64::new(1483, false), 3, i64::new(5, true), 1663680745),
  542. ),
  543. ),
  544. price_info::new_price_info(
  545. 1663680747,
  546. 1663074349,
  547. price_feed::new(
  548. price_identifier::from_byte_vec(x"33832fad6e36eb05a8972fe5f219b27b5b2bb2230a79ce79beb4c5c5e7ecc76d"),
  549. price::new(i64::new(1010, false), 2, i64::new(5, true), 1663680745),
  550. price::new(i64::new(1511, false), 3, i64::new(5, true), 1663680745),
  551. ),
  552. ),
  553. price_info::new_price_info(
  554. 1663680747,
  555. 1663074349,
  556. price_feed::new(
  557. price_identifier::from_byte_vec(x"21a28b4c6619968bd8c20e95b0aaed7df2187fd310275347e0376a2cd7427db8"),
  558. price::new(i64::new(1739, false), 1, i64::new(5, true), 1663680745),
  559. price::new(i64::new(1508, false), 3, i64::new(5, true), 1663680745),
  560. ),
  561. ),
  562. ]
  563. }
  564. /// Compare the expected price feed with the actual Pyth price feeds.
  565. fun check_price_feeds_cached(expected: &vector<PriceInfo>, actual: &vector<PriceInfoObject>) {
  566. // Check that we can retrieve the correct current price and ema price for each price feed
  567. let i = 0;
  568. while (i < vector::length(expected)) {
  569. let price_feed = price_info::get_price_feed(vector::borrow(expected, i));
  570. let price = price_feed::get_price(price_feed);
  571. let ema_price = price_feed::get_ema_price(price_feed);
  572. let price_identifier = price_info::get_price_identifier(vector::borrow(expected, i));
  573. let actual_price_info = price_info::get_price_info_from_price_info_object(vector::borrow(actual, i));
  574. let actual_price_feed = price_info::get_price_feed(&actual_price_info);
  575. let actual_price = price_feed::get_price(actual_price_feed);
  576. let actual_ema_price = price_feed::get_ema_price(actual_price_feed);
  577. let actual_price_identifier = price_info::get_price_identifier(&actual_price_info);
  578. assert!(price == actual_price, 0);
  579. assert!(ema_price == actual_ema_price, 0);
  580. assert!(price_identifier::get_bytes(&price_identifier) == price_identifier::get_bytes(&actual_price_identifier), 0);
  581. i = i + 1;
  582. };
  583. }
  584. #[test]
  585. fun test_get_update_fee() {
  586. let (scenario, test_coins, _clock) = setup_test(500 /* stale_price_threshold */, 23 /* governance emitter chain */, x"5d1f252d5de865279b00c84bce362774c2804294ed53299bc4a0389a5defef92", vector[], BATCH_ATTESTATION_TEST_INITIAL_GUARDIANS, DEFAULT_BASE_UPDATE_FEE, 0);
  587. test_scenario::next_tx(&mut scenario, DEPLOYER, );
  588. let pyth_state = take_shared<PythState>(&scenario);
  589. // Pass in a single VAA
  590. let single_vaa = vector[
  591. x"fb1543888001083cf2e6ef3afdcf827e89b11efd87c563638df6e1995ada9f93",
  592. ];
  593. assert!(pyth::get_total_update_fee(&pyth_state, vector::length<vector<u8>>(&single_vaa)) == DEFAULT_BASE_UPDATE_FEE, 1);
  594. let multiple_vaas = vector[
  595. x"4ee17a1a4524118de513fddcf82b77454e51be5d6fc9e29fc72dd6c204c0e4fa",
  596. x"c72fdf81cfc939d4286c93fbaaae2eec7bae28a5926fa68646b43a279846ccc1",
  597. x"d9a8123a793529c31200339820a3210059ecace6c044f81ecad62936e47ca049",
  598. x"84e4f21b3e65cef47fda25d15b4eddda1edf720a1d062ccbf441d6396465fbe6",
  599. x"9e73f9041476a93701a0b9c7501422cc2aa55d16100bec628cf53e0281b6f72f"
  600. ];
  601. // Pass in multiple VAAs
  602. assert!(pyth::get_total_update_fee(&pyth_state, vector::length<vector<u8>>(&multiple_vaas)) == 5*DEFAULT_BASE_UPDATE_FEE, 1);
  603. return_shared(pyth_state);
  604. coin::burn_for_testing<SUI>(test_coins);
  605. clock::destroy_for_testing(_clock);
  606. test_scenario::end(scenario);
  607. }
  608. #[test]
  609. #[expected_failure(abort_code = wormhole::vaa::E_WRONG_VERSION)]
  610. fun test_create_price_feeds_corrupt_vaa() {
  611. let (scenario, test_coins, clock) = setup_test(500 /* stale_price_threshold */, 23 /* governance emitter chain */, x"5d1f252d5de865279b00c84bce362774c2804294ed53299bc4a0389a5defef92", vector[], vector[x"beFA429d57cD18b7F8A4d91A2da9AB4AF05d0FBe"], 50, 0);
  612. test_scenario::next_tx(&mut scenario, DEPLOYER);
  613. let (pyth_state, worm_state) = take_wormhole_and_pyth_states(&scenario);
  614. // Pass in a corrupt VAA, which should fail deseriaizing
  615. let corrupt_vaa = x"90F8bf6A479f320ead074411a4B0e7944Ea8c9C1";
  616. let verified_vaas = vector[vaa::parse_and_verify(&worm_state, corrupt_vaa, &clock)];
  617. // Create Pyth price feed
  618. pyth::create_price_feeds(
  619. &mut pyth_state,
  620. verified_vaas,
  621. &clock,
  622. ctx(&mut scenario)
  623. );
  624. cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock);
  625. coin::burn_for_testing<SUI>(test_coins);
  626. test_scenario::end(scenario);
  627. }
  628. #[test]
  629. #[expected_failure(abort_code = pyth::pyth::E_INVALID_DATA_SOURCE)]
  630. fun test_create_price_feeds_invalid_data_source() {
  631. // Initialize the contract with some valid data sources, excluding our test VAA's source
  632. let data_sources = vector<DataSource>[
  633. data_source::new(
  634. 4, external_address::new(bytes32::new(x"0000000000000000000000000000000000000000000000000000000000007742"))
  635. ),
  636. data_source::new(
  637. 5, external_address::new(bytes32::new(x"0000000000000000000000000000000000000000000000000000000000007637"))
  638. )
  639. ];
  640. let (scenario, test_coins, clock) = setup_test(500, 23, x"5d1f252d5de865279b00c84bce362774c2804294ed53299bc4a0389a5defef92", data_sources, BATCH_ATTESTATION_TEST_INITIAL_GUARDIANS, 50, 0);
  641. test_scenario::next_tx(&mut scenario, DEPLOYER);
  642. let (pyth_state, worm_state) = take_wormhole_and_pyth_states(&scenario);
  643. let verified_vaas = get_verified_test_vaas(&worm_state, &clock);
  644. pyth::create_price_feeds(
  645. &mut pyth_state,
  646. verified_vaas,
  647. &clock,
  648. ctx(&mut scenario)
  649. );
  650. cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock);
  651. coin::burn_for_testing<SUI>(test_coins);
  652. test_scenario::end(scenario);
  653. }
  654. public fun data_sources_for_test_vaa(): vector<DataSource> {
  655. // Set some valid data sources, including our test VAA's source
  656. vector<DataSource>[
  657. data_source::new(
  658. 1, external_address::new(bytes32::from_bytes(x"0000000000000000000000000000000000000000000000000000000000000004"))),
  659. data_source::new(
  660. 5, external_address::new(bytes32::new(x"0000000000000000000000000000000000000000000000000000000000007637"))),
  661. data_source::new(
  662. 17, external_address::new(bytes32::new(ACCUMULATOR_TESTS_EMITTER_ADDRESS)))
  663. ]
  664. }
  665. #[test]
  666. // test_create_and_update_price_feeds_with_batch_attestation_success tests the creation and updating of price
  667. // feeds, as well as depositing fee coins into price info objects
  668. fun test_create_and_update_price_feeds_with_batch_attestation_success() {
  669. let (scenario, test_coins, clock) = setup_test(500, 23, x"5d1f252d5de865279b00c84bce362774c2804294ed53299bc4a0389a5defef92", data_sources_for_test_vaa(), vector[x"beFA429d57cD18b7F8A4d91A2da9AB4AF05d0FBe"], DEFAULT_BASE_UPDATE_FEE, DEFAULT_COIN_TO_MINT);
  670. test_scenario::next_tx(&mut scenario, DEPLOYER);
  671. let (pyth_state, worm_state) = take_wormhole_and_pyth_states(&scenario);
  672. let verified_vaas = get_verified_test_vaas(&worm_state, &clock);
  673. test_scenario::next_tx(&mut scenario, DEPLOYER);
  674. pyth::create_price_feeds(
  675. &mut pyth_state,
  676. verified_vaas,
  677. &clock,
  678. ctx(&mut scenario)
  679. );
  680. // Affirm that 4 objects, which correspond to the 4 new price info objects
  681. // containing the price feeds were created and shared.
  682. let effects = test_scenario::next_tx(&mut scenario, DEPLOYER);
  683. let shared_ids = test_scenario::shared(&effects);
  684. let created_ids = test_scenario::created(&effects);
  685. assert!(vector::length<ID>(&shared_ids)==4, 0);
  686. assert!(vector::length<ID>(&created_ids)==4, 0);
  687. let price_info_object_1 = take_shared<PriceInfoObject>(&scenario);
  688. let price_info_object_2 = take_shared<PriceInfoObject>(&scenario);
  689. let price_info_object_3 = take_shared<PriceInfoObject>(&scenario);
  690. let price_info_object_4 = take_shared<PriceInfoObject>(&scenario);
  691. // Create vector of price info objects (Sui objects with key ability and living in global store),
  692. // which contain the price feeds we want to update. Note that these can be passed into
  693. // update_price_feeds in any order!
  694. //let price_info_object_vec = vector[price_info_object_1, price_info_object_2, price_info_object_3, price_info_object_4];
  695. verified_vaas = get_verified_test_vaas(&worm_state, &clock);
  696. test_scenario::next_tx(&mut scenario, DEPLOYER);
  697. let vaa_1 = vector::pop_back<VAA>(&mut verified_vaas);
  698. test_scenario::next_tx(&mut scenario, DEPLOYER);
  699. // Create authenticated price infos
  700. let vec = create_price_infos_hot_potato(
  701. &pyth_state,
  702. vector[vaa_1],
  703. &clock
  704. );
  705. test_scenario::next_tx(&mut scenario, DEPLOYER);
  706. let fee_coins = coin::split(&mut test_coins, DEFAULT_BASE_UPDATE_FEE, ctx(&mut scenario));
  707. vec = update_single_price_feed(
  708. &mut pyth_state,
  709. vec,
  710. &mut price_info_object_1,
  711. fee_coins,
  712. &clock
  713. );
  714. test_scenario::next_tx(&mut scenario, DEPLOYER);
  715. // check price feed updated
  716. assert!(price_feeds_equal(hot_potato_vector::borrow(&vec, 3), &price_info::get_price_info_from_price_info_object(&price_info_object_1)), 0);
  717. // check fee coins are deposited in the price info object
  718. assert!(price_info::get_balance(&price_info_object_1)==DEFAULT_BASE_UPDATE_FEE, 0);
  719. test_scenario::next_tx(&mut scenario, DEPLOYER);
  720. hot_potato_vector::destroy<PriceInfo>(vec);
  721. vector::destroy_empty(verified_vaas);
  722. return_shared(price_info_object_1);
  723. return_shared(price_info_object_2);
  724. return_shared(price_info_object_3);
  725. return_shared(price_info_object_4);
  726. coin::burn_for_testing(test_coins);
  727. cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock);
  728. test_scenario::end(scenario);
  729. }
  730. // TEST_ACCUMULATOR_SINGLE_FEED details:
  731. // Price Identifier: 0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6
  732. // Price: 6887568746747646632
  733. // Conf: 13092246197863718329
  734. // Exponent: 1559537863
  735. // EMA Price: 4772242609775910581
  736. // EMA Conf: 358129956189946877
  737. // EMA Expo: 1559537863
  738. // Published Time: 1687276661
  739. const TEST_ACCUMULATOR_SINGLE_FEED: vector<u8> = x"504e41550100000000a0010000000001005d461ac1dfffa8451edda17e4b28a46c8ae912422b2dc0cb7732828c497778ea27147fb95b4d250651931845e7f3e22c46326716bcf82be2874a9c9ab94b6e42000000000000000000000171f8dcb863d176e2c420ad6610cf687359612b6fb392e0642b0ca6b1f186aa3b0000000000000000004155575600000000000000000000000000da936d73429246d131873a0bab90ad7b416510be01005500b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65f958f4883f9d2a8b5b1008d1fa01db95cf4a8c7000000006491cc757be59f3f377c0d3f423a695e81ad1eb504f8554c3620c3fd02f2ee15ea639b73fa3db9b34a245bdfa015c260c5a8a1180177cf30b2c0bebbb1adfe8f7985d051d2";
  740. #[test]
  741. fun test_create_and_update_single_price_feed_with_accumulator_success() {
  742. let (scenario, coins, clock) = setup_test(500, 23, ACCUMULATOR_TESTS_EMITTER_ADDRESS, ACCUMULATOR_TESTS_DATA_SOURCE(), ACCUMULATOR_TESTS_INITIAL_GUARDIANS, DEFAULT_BASE_UPDATE_FEE, DEFAULT_COIN_TO_MINT);
  743. test_scenario::next_tx(&mut scenario, DEPLOYER);
  744. let (pyth_state, worm_state) = take_wormhole_and_pyth_states(&scenario);
  745. let verified_vaa = get_verified_vaa_from_accumulator_message(&worm_state, TEST_ACCUMULATOR_SINGLE_FEED, &clock);
  746. test_scenario::next_tx(&mut scenario, DEPLOYER);
  747. pyth::create_price_feeds_using_accumulator(
  748. &mut pyth_state,
  749. TEST_ACCUMULATOR_SINGLE_FEED,
  750. verified_vaa,
  751. &clock,
  752. ctx(&mut scenario)
  753. );
  754. // Affirm that 1 object, which correspond to the 1 new price info object
  755. // containing the price feeds were created and shared.
  756. let effects = test_scenario::next_tx(&mut scenario, DEPLOYER);
  757. let shared_ids = test_scenario::shared(&effects);
  758. let created_ids = test_scenario::created(&effects);
  759. assert!(vector::length<ID>(&shared_ids)==1, 0);
  760. assert!(vector::length<ID>(&created_ids)==1, 0);
  761. let price_info_object_1 = take_shared<PriceInfoObject>(&scenario);
  762. // Create authenticated price infos
  763. verified_vaa = get_verified_vaa_from_accumulator_message(&worm_state, TEST_ACCUMULATOR_SINGLE_FEED, &clock);
  764. let auth_price_infos = pyth::create_authenticated_price_infos_using_accumulator(
  765. &pyth_state,
  766. TEST_ACCUMULATOR_SINGLE_FEED,
  767. verified_vaa,
  768. &clock
  769. );
  770. test_scenario::next_tx(&mut scenario, DEPLOYER);
  771. auth_price_infos = update_single_price_feed(
  772. &mut pyth_state,
  773. auth_price_infos,
  774. &mut price_info_object_1,
  775. coins,
  776. &clock
  777. );
  778. // assert that price info obejct is as expected
  779. let expected = accumulator_test_1_to_price_info();
  780. assert!(price_feeds_equal(&expected, &price_info::get_price_info_from_price_info_object(&price_info_object_1)), 0);
  781. // clean up test scenario
  782. test_scenario::next_tx(&mut scenario, DEPLOYER);
  783. hot_potato_vector::destroy<PriceInfo>(auth_price_infos);
  784. return_shared(price_info_object_1);
  785. cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock);
  786. test_scenario::end(scenario);
  787. }
  788. #[test]
  789. #[expected_failure(abort_code = pyth::accumulator::E_INVALID_PROOF)]
  790. fun test_create_and_update_single_price_feed_with_accumulator_failure() {
  791. let (scenario, coins, clock) = setup_test(500, 23, ACCUMULATOR_TESTS_EMITTER_ADDRESS, ACCUMULATOR_TESTS_DATA_SOURCE(), ACCUMULATOR_TESTS_INITIAL_GUARDIANS, DEFAULT_BASE_UPDATE_FEE, DEFAULT_COIN_TO_MINT);
  792. test_scenario::next_tx(&mut scenario, DEPLOYER);
  793. let (pyth_state, worm_state) = take_wormhole_and_pyth_states(&scenario);
  794. // the verified vaa here contains the wrong merkle root
  795. let verified_vaa = get_verified_vaa_from_accumulator_message(&worm_state, TEST_ACCUMULATOR_3_MSGS, &clock);
  796. test_scenario::next_tx(&mut scenario, DEPLOYER);
  797. pyth::create_price_feeds_using_accumulator(
  798. &mut pyth_state,
  799. TEST_ACCUMULATOR_SINGLE_FEED,
  800. verified_vaa,
  801. &clock,
  802. ctx(&mut scenario)
  803. );
  804. // clean up test scenario
  805. test_scenario::next_tx(&mut scenario, DEPLOYER);
  806. coin::burn_for_testing<SUI>(coins);
  807. cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock);
  808. test_scenario::end(scenario);
  809. }
  810. #[test_only]
  811. const TEST_ACCUMULATOR_INVALID_PROOF_1: vector<u8> = x"504e41550100000000a001000000000100110db9cd8325ccfab0dae92eeb9ea70a1faba5c5e96dc21ff46a8ddc560afc9a60df096b8ff21172804692bbdc958153e838437d8b474cbf45f0dc2a8acae831000000000000000000000171f8dcb863d176e2c420ad6610cf687359612b6fb392e0642b0ca6b1f186aa3b0000000000000000004155575600000000000000000000000000a8bea2b5f12f3177ff9b3929d77c3476ab2d32c602005500b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6fa75cd3aa3bb5ace5e2516446f71f85be36bd19bb0703f3154bb3db07be59f3f377c0d3f44661d9a8736c68884c8169e8b636ee3043202397384073120dce9e5d0efe24b44b4a0d62da8a1180177cf30b2c0bebbb1adfe8f7985d051d205a01e2504d9f0c06e7e7cb0cf24116098ca202ac5f6ade2e8f5a12ec006b16d46be1f0228b94d950055006e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af5f958f4883f9d2a8b5b1008d1fa01db95cf4a8c7423a695e81ad1eb504f8554c3620c3fd40b40f7d581ac802e2de5cb82a9ae672043202397384073120dce9e5d0efe24b44b4a0d62da8a1180177cf30b2c0bebbb1adfe8f7985d051d205a01e2504d9f0c06e7e7cb0cf24116098ca202ac5f6ade2e8f5a12ec006b16d46be1f0228b94d95";
  812. #[test]
  813. #[expected_failure(abort_code = pyth::accumulator::E_INVALID_PROOF)]
  814. fun test_accumulator_invalid_proof() {
  815. let (scenario, coins, clock) = setup_test(500, 23, ACCUMULATOR_TESTS_EMITTER_ADDRESS, ACCUMULATOR_TESTS_DATA_SOURCE(), ACCUMULATOR_TESTS_INITIAL_GUARDIANS, DEFAULT_BASE_UPDATE_FEE, DEFAULT_COIN_TO_MINT);
  816. test_scenario::next_tx(&mut scenario, DEPLOYER);
  817. let (pyth_state, worm_state) = take_wormhole_and_pyth_states(&scenario);
  818. let verified_vaa = get_verified_vaa_from_accumulator_message(&worm_state, TEST_ACCUMULATOR_INVALID_PROOF_1, &clock);
  819. test_scenario::next_tx(&mut scenario, DEPLOYER);
  820. pyth::create_price_feeds_using_accumulator(
  821. &mut pyth_state,
  822. TEST_ACCUMULATOR_INVALID_PROOF_1,
  823. verified_vaa,
  824. &clock,
  825. ctx(&mut scenario)
  826. );
  827. // clean up test scenario
  828. test_scenario::next_tx(&mut scenario, DEPLOYER);
  829. coin::burn_for_testing<SUI>(coins);
  830. cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock);
  831. test_scenario::end(scenario);
  832. }
  833. #[test_only]
  834. const TEST_ACCUMULATOR_INVALID_MAJOR_VERSION: vector<u8> = x"504e41553c00000000a001000000000100496b7fbd18dca2f0e690712fd8ca522ff79ca7d9d6d22e9f5d753fba4bd16fff440a811bad710071c79859290bcb1700de49dd8400db90b048437b521200123e010000000000000000000171f8dcb863d176e2c420ad6610cf687359612b6fb392e0642b0ca6b1f186aa3b000000000000000000415557560000000000000000000000000005f5db4488a7cae9f9a6c1938340c0fbf4beb9090200550031ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6879bc5a3617ec3444d93c06501cf6a0909c38d4ec81d96026b71ec475e87d69c7b5124289adbf24212bed8c15db354391d2378d2e0454d2655c6c34e7e50580fd8c94511322968bbc6da8a1180177cf30b2c0bebbb1adfe8f7985d051d205a01e2504d9f0c06e7e7cb0cf24116098ca202ac5f6ade2e8f5a12ec006b16d46be1f0228b94d95005500944998273e477b495144fb8794c914197f3ccb46be2900f4698fd0ef743c9695a573a6ff665ff63edb5f9a85ad579dc14500a2112c09680fc146134f9a539ca82cb6e3501c801278fd08d80732a24118292866bb049e6e88181a1e1e8b6d3c6bbb95135a73041f3b56a8a1180177cf30b2c0bebbb1adfe8f7985d051d205a01e2504d9f0c06e7e7cb0cf24116098ca202ac5f6ade2e8f5a12ec006b16d46be1f0228b94d95";
  835. #[test]
  836. #[expected_failure(abort_code = pyth::accumulator::E_INVALID_ACCUMULATOR_PAYLOAD)]
  837. fun test_accumulator_invalid_major_version() {
  838. let (scenario, coins, clock) = setup_test(500, 23, ACCUMULATOR_TESTS_EMITTER_ADDRESS, ACCUMULATOR_TESTS_DATA_SOURCE(), ACCUMULATOR_TESTS_INITIAL_GUARDIANS, DEFAULT_BASE_UPDATE_FEE, DEFAULT_COIN_TO_MINT);
  839. test_scenario::next_tx(&mut scenario, DEPLOYER);
  840. let (pyth_state, worm_state) = take_wormhole_and_pyth_states(&scenario);
  841. let verified_vaa = get_verified_vaa_from_accumulator_message(&worm_state, TEST_ACCUMULATOR_INVALID_MAJOR_VERSION, &clock);
  842. test_scenario::next_tx(&mut scenario, DEPLOYER);
  843. pyth::create_price_feeds_using_accumulator(
  844. &mut pyth_state,
  845. TEST_ACCUMULATOR_INVALID_MAJOR_VERSION,
  846. verified_vaa,
  847. &clock,
  848. ctx(&mut scenario)
  849. );
  850. // clean up test scenario
  851. test_scenario::next_tx(&mut scenario, DEPLOYER);
  852. coin::burn_for_testing<SUI>(coins);
  853. cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock);
  854. test_scenario::end(scenario);
  855. }
  856. #[test_only]
  857. const TEST_ACCUMULATOR_INVALID_WH_MSG: vector<u8> = x"504e41550100000000a001000000000100e87f98238c5357730936cfdfde3a37249e5219409a4f41b301924b8eb10815a43ea2f96e4fe1bc8cd398250f39448d3b8ca57c96f9cf7a2be292517280683caa010000000000000000000171f8dcb863d176e2c420ad6610cf687359612b6fb392e0642b0ca6b1f186aa3b00000000000000000041555755000000000000000000000000000fb6f9f2b3b6cc1c9ef6708985fef226d92a3c0801005500b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6fa75cd3aa3bb5ace5e2516446f71f85be36bd19b000000006491cc747be59f3f377c0d3f44661d9a8736c68884c8169e8b636ee301f2ee15ea639b73fa3db9b34a245bdfa015c260c5";
  858. #[test]
  859. #[expected_failure(abort_code = pyth::accumulator::E_INVALID_WORMHOLE_MESSAGE)]
  860. fun test_accumulator_invalid_wormhole_message() {
  861. let (scenario, coins, clock) = setup_test(500, 23, ACCUMULATOR_TESTS_EMITTER_ADDRESS, ACCUMULATOR_TESTS_DATA_SOURCE(), ACCUMULATOR_TESTS_INITIAL_GUARDIANS, DEFAULT_BASE_UPDATE_FEE, DEFAULT_COIN_TO_MINT);
  862. test_scenario::next_tx(&mut scenario, DEPLOYER);
  863. let (pyth_state, worm_state) = take_wormhole_and_pyth_states(&scenario);
  864. let verified_vaa = get_verified_vaa_from_accumulator_message(&worm_state, TEST_ACCUMULATOR_INVALID_WH_MSG, &clock);
  865. test_scenario::next_tx(&mut scenario, DEPLOYER);
  866. pyth::create_price_feeds_using_accumulator(
  867. &mut pyth_state,
  868. TEST_ACCUMULATOR_INVALID_WH_MSG,
  869. verified_vaa,
  870. &clock,
  871. ctx(&mut scenario)
  872. );
  873. // clean up test scenario
  874. test_scenario::next_tx(&mut scenario, DEPLOYER);
  875. coin::burn_for_testing<SUI>(coins);
  876. cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock);
  877. test_scenario::end(scenario);
  878. }
  879. #[test_only]
  880. const TEST_ACCUMULATOR_INCREASED_MINOR_VERSION: vector<u8> = x"504e4155010a000000a001000000000100496b7fbd18dca2f0e690712fd8ca522ff79ca7d9d6d22e9f5d753fba4bd16fff440a811bad710071c79859290bcb1700de49dd8400db90b048437b521200123e010000000000000000000171f8dcb863d176e2c420ad6610cf687359612b6fb392e0642b0ca6b1f186aa3b000000000000000000415557560000000000000000000000000005f5db4488a7cae9f9a6c1938340c0fbf4beb9090200550031ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6879bc5a3617ec3444d93c06501cf6a0909c38d4ec81d96026b71ec475e87d69c7b5124289adbf24212bed8c15db354391d2378d2e0454d2655c6c34e7e50580fd8c94511322968bbc6da8a1180177cf30b2c0bebbb1adfe8f7985d051d205a01e2504d9f0c06e7e7cb0cf24116098ca202ac5f6ade2e8f5a12ec006b16d46be1f0228b94d95005500944998273e477b495144fb8794c914197f3ccb46be2900f4698fd0ef743c9695a573a6ff665ff63edb5f9a85ad579dc14500a2112c09680fc146134f9a539ca82cb6e3501c801278fd08d80732a24118292866bb049e6e88181a1e1e8b6d3c6bbb95135a73041f3b56a8a1180177cf30b2c0bebbb1adfe8f7985d051d205a01e2504d9f0c06e7e7cb0cf24116098ca202ac5f6ade2e8f5a12ec006b16d46be1f0228b94d95";
  881. #[test_only]
  882. const TEST_ACCUMULATOR_EXTRA_PAYLOAD: vector<u8> = x"504e41550100000000a001000000000100b2d11f181d81b4ff10beca30091754b464dc48bc1f7432d114f64a7a8f660e7964f2a0c6121bae6c1977514d46ee7a29d9395b20a45f2086071715c1dc19ab74000000000000000000000171f8dcb863d176e2c420ad6610cf687359612b6fb392e0642b0ca6b1f186aa3b000000000000000000415557560000000000000000000000000013f83cfdf63a5a1b3189182fa0a52e6de53ba7d002005d0031ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6879bc5a3617ec3444d93c06501cf6a0909c38d4ec81d96026b71ec475e87d69c7b5124289adbf24212bed8c15db354391d2378d2e000000000000000004a576f4a87f443f7d961a682f508c4f7b06ee1595a8a1180177cf30b2c0bebbb1adfe8f7985d051d205a01e2504d9f0c06e7e7cb0cf24116098ca202ac5f6ade2e8f5a12ec006b16d46be1f0228b94d95005d00944998273e477b495144fb8794c914197f3ccb46be2900f4698fd0ef743c9695a573a6ff665ff63edb5f9a85ad579dc14500a2112c09680fc146134f9a539ca82cb6e3501c801278fd08d80732a24118292866bb0000000000000000045be67ba87a8dfbea404827ccbf07790299b6c023a8a1180177cf30b2c0bebbb1adfe8f7985d051d205a01e2504d9f0c06e7e7cb0cf24116098ca202ac5f6ade2e8f5a12ec006b16d46be1f0228b94d95";
  883. #[test]
  884. fun test_accumulator_forward_compatibility() {
  885. let (scenario, coins, clock) = setup_test(500, 23, ACCUMULATOR_TESTS_EMITTER_ADDRESS, ACCUMULATOR_TESTS_DATA_SOURCE(), ACCUMULATOR_TESTS_INITIAL_GUARDIANS, DEFAULT_BASE_UPDATE_FEE, DEFAULT_COIN_TO_MINT);
  886. test_scenario::next_tx(&mut scenario, DEPLOYER);
  887. let (pyth_state, worm_state) = take_wormhole_and_pyth_states(&scenario);
  888. test_scenario::next_tx(&mut scenario, DEPLOYER);
  889. pyth::create_price_feeds_using_accumulator(
  890. &mut pyth_state,
  891. TEST_ACCUMULATOR_EXTRA_PAYLOAD,
  892. get_verified_vaa_from_accumulator_message(&worm_state, TEST_ACCUMULATOR_EXTRA_PAYLOAD, &clock),
  893. &clock,
  894. ctx(&mut scenario)
  895. );
  896. test_scenario::next_tx(&mut scenario, DEPLOYER);
  897. pyth::create_price_feeds_using_accumulator(
  898. &mut pyth_state,
  899. TEST_ACCUMULATOR_INCREASED_MINOR_VERSION,
  900. get_verified_vaa_from_accumulator_message(&worm_state, TEST_ACCUMULATOR_INCREASED_MINOR_VERSION, &clock),
  901. &clock,
  902. ctx(&mut scenario)
  903. );
  904. // clean up test scenario
  905. test_scenario::next_tx(&mut scenario, DEPLOYER);
  906. coin::burn_for_testing<SUI>(coins);
  907. cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock);
  908. test_scenario::end(scenario);
  909. }
  910. // TEST_ACCUMULATOR_3_MSGS details:
  911. // Price Identifier: 0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6
  912. // Price: 100
  913. // Conf: 50
  914. // Exponent: 9
  915. // EMA Price: 99
  916. // EMA Conf: 52
  917. // EMA Expo: 9
  918. // Published Time: 1687276660
  919. // Price Identifier: 0x6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af
  920. // Price: 101
  921. // Conf: 51
  922. // Exponent: 10
  923. // EMA Price: 100
  924. // EMA Conf: 53
  925. // EMA Expo: 10
  926. // Published Time: 1687276661
  927. // Price Identifier: 0x31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c68
  928. // Price: 102
  929. // Conf: 52
  930. // Exponent: 11
  931. // EMA Price: 101
  932. // EMA Conf: 54
  933. // EMA Expo: 11
  934. // Published Time: 1687276662
  935. const TEST_ACCUMULATOR_3_MSGS: vector<u8> = x"504e41550100000000a001000000000100d39b55fa311213959f91866d52624f3a9c07350d8956f6d42cfbb037883f31575c494a2f09fea84e4884dc9c244123fd124bc7825cd64d7c11e33ba5cfbdea7e010000000000000000000171f8dcb863d176e2c420ad6610cf687359612b6fb392e0642b0ca6b1f186aa3b000000000000000000415557560000000000000000000000000029da4c066b6e03b16a71e77811570dd9e19f258103005500b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60000000000000064000000000000003200000009000000006491cc747be59f3f377c0d3f000000000000006300000000000000340436992facb15658a7e9f08c4df4848ca80750f61fadcd96993de66b1fe7aef94e29e3bbef8b12db2305a01e2504d9f0c06e7e7cb0cf24116098ca202ac5f6ade2e8f5a12ec006b16d46be1f0228b94d950055006e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af000000000000006500000000000000330000000a000000006491cc7504f8554c3620c3fd0000000000000064000000000000003504171ed10ac4f1eacf3a4951e1da6b119f07c45da5adcd96993de66b1fe7aef94e29e3bbef8b12db2305a01e2504d9f0c06e7e7cb0cf24116098ca202ac5f6ade2e8f5a12ec006b16d46be1f0228b94d9500550031ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c68000000000000006600000000000000340000000b000000006491cc76e87d69c7b51242890000000000000065000000000000003604f2ee15ea639b73fa3db9b34a245bdfa015c260c5fe83e4772e0e346613de00e5348158a01bcb27b305a01e2504d9f0c06e7e7cb0cf24116098ca202ac5f6ade2e8f5a12ec006b16d46be1f0228b94d95";
  936. #[test]
  937. fun test_create_and_update_multiple_price_feeds_with_accumulator_success() {
  938. use sui::coin::Self;
  939. let (scenario, coins, clock) = setup_test(500, 23, ACCUMULATOR_TESTS_EMITTER_ADDRESS, ACCUMULATOR_TESTS_DATA_SOURCE(), ACCUMULATOR_TESTS_INITIAL_GUARDIANS, DEFAULT_BASE_UPDATE_FEE, DEFAULT_COIN_TO_MINT);
  940. test_scenario::next_tx(&mut scenario, DEPLOYER);
  941. let (pyth_state, worm_state) = take_wormhole_and_pyth_states(&scenario);
  942. let verified_vaa = get_verified_vaa_from_accumulator_message(&worm_state, TEST_ACCUMULATOR_3_MSGS, &clock);
  943. test_scenario::next_tx(&mut scenario, DEPLOYER);
  944. pyth::create_price_feeds_using_accumulator(
  945. &mut pyth_state,
  946. TEST_ACCUMULATOR_3_MSGS,
  947. verified_vaa,
  948. &clock,
  949. ctx(&mut scenario)
  950. );
  951. // Affirm that 3 objects, which correspond to the 3 new price info objects
  952. // containing the price feeds were created and shared.
  953. let effects = test_scenario::next_tx(&mut scenario, DEPLOYER);
  954. let shared_ids = test_scenario::shared(&effects);
  955. let created_ids = test_scenario::created(&effects);
  956. assert!(vector::length<ID>(&shared_ids)==3, 0);
  957. assert!(vector::length<ID>(&created_ids)==3, 0);
  958. // Create authenticated price infos
  959. verified_vaa = get_verified_vaa_from_accumulator_message(&worm_state, TEST_ACCUMULATOR_3_MSGS, &clock);
  960. let auth_price_infos = pyth::create_authenticated_price_infos_using_accumulator(
  961. &pyth_state,
  962. TEST_ACCUMULATOR_3_MSGS,
  963. verified_vaa,
  964. &clock
  965. );
  966. let idx = 0;
  967. let expected_price_infos = accumulator_test_3_to_price_info(0 /*offset argument*/);
  968. while (idx < 3){
  969. let coin_split = coin::split(&mut coins, 1000, ctx(&mut scenario));
  970. let price_info_object = take_shared<PriceInfoObject>(&scenario);
  971. auth_price_infos = update_single_price_feed(
  972. &mut pyth_state,
  973. auth_price_infos,
  974. &mut price_info_object,
  975. coin_split,
  976. &clock
  977. );
  978. let price_info = price_info::get_price_info_from_price_info_object(&price_info_object);
  979. assert!(price_feeds_equal(&price_info, vector::borrow(&expected_price_infos, idx)), 0);
  980. return_shared(price_info_object);
  981. idx = idx + 1;
  982. };
  983. coin::burn_for_testing<SUI>(coins);
  984. // clean up test scenario
  985. test_scenario::next_tx(&mut scenario, DEPLOYER);
  986. hot_potato_vector::destroy<PriceInfo>(auth_price_infos);
  987. cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock);
  988. test_scenario::end(scenario);
  989. }
  990. #[test]
  991. #[expected_failure(abort_code = pyth::pyth::E_INSUFFICIENT_FEE)]
  992. fun test_create_and_update_price_feeds_insufficient_fee() {
  993. // this is not enough fee and will cause a failure
  994. let coins_to_mint = 1;
  995. let (scenario, test_coins, clock) = setup_test(500, 23, x"5d1f252d5de865279b00c84bce362774c2804294ed53299bc4a0389a5defef92", data_sources_for_test_vaa(), vector[x"beFA429d57cD18b7F8A4d91A2da9AB4AF05d0FBe"], DEFAULT_BASE_UPDATE_FEE, coins_to_mint);
  996. test_scenario::next_tx(&mut scenario, DEPLOYER);
  997. let (pyth_state, worm_state) = take_wormhole_and_pyth_states(&scenario);
  998. let verified_vaas = get_verified_test_vaas(&worm_state, &clock);
  999. test_scenario::next_tx(&mut scenario, DEPLOYER);
  1000. pyth::create_price_feeds(
  1001. &mut pyth_state,
  1002. verified_vaas,
  1003. &clock,
  1004. ctx(&mut scenario)
  1005. );
  1006. // Affirm that 4 objects, which correspond to the 4 new price info objects
  1007. // containing the price feeds were created and shared.
  1008. let effects = test_scenario::next_tx(&mut scenario, DEPLOYER);
  1009. let shared_ids = test_scenario::shared(&effects);
  1010. let created_ids = test_scenario::created(&effects);
  1011. assert!(vector::length<ID>(&shared_ids)==4, 0);
  1012. assert!(vector::length<ID>(&created_ids)==4, 0);
  1013. let price_info_object_1 = take_shared<PriceInfoObject>(&scenario);
  1014. let price_info_object_2 = take_shared<PriceInfoObject>(&scenario);
  1015. let price_info_object_3 = take_shared<PriceInfoObject>(&scenario);
  1016. let price_info_object_4 = take_shared<PriceInfoObject>(&scenario);
  1017. // Create vector of price info objects (Sui objects with key ability and living in global store),
  1018. // which contain the price feeds we want to update. Note that these can be passed into
  1019. // update_price_feeds in any order!
  1020. //let price_info_object_vec = vector[price_info_object_1, price_info_object_2, price_info_object_3, price_info_object_4];
  1021. verified_vaas = get_verified_test_vaas(&worm_state, &clock);
  1022. test_scenario::next_tx(&mut scenario, DEPLOYER);
  1023. let vaa_1 = vector::pop_back<VAA>(&mut verified_vaas);
  1024. test_scenario::next_tx(&mut scenario, DEPLOYER);
  1025. // Create authenticated price infos
  1026. let vec = create_price_infos_hot_potato(
  1027. &pyth_state,
  1028. vector[vaa_1],
  1029. &clock
  1030. );
  1031. test_scenario::next_tx(&mut scenario, DEPLOYER);
  1032. vec = update_single_price_feed(
  1033. &mut pyth_state,
  1034. vec,
  1035. &mut price_info_object_1,
  1036. test_coins,
  1037. &clock
  1038. );
  1039. test_scenario::next_tx(&mut scenario, DEPLOYER);
  1040. hot_potato_vector::destroy<PriceInfo>(vec);
  1041. vector::destroy_empty(verified_vaas);
  1042. return_shared(price_info_object_1);
  1043. return_shared(price_info_object_2);
  1044. return_shared(price_info_object_3);
  1045. return_shared(price_info_object_4);
  1046. cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock);
  1047. test_scenario::end(scenario);
  1048. }
  1049. #[test]
  1050. fun test_update_cache(){
  1051. let (scenario, test_coins, clock) = setup_test(500, 23, x"5d1f252d5de865279b00c84bce362774c2804294ed53299bc4a0389a5defef92", data_sources_for_test_vaa(), BATCH_ATTESTATION_TEST_INITIAL_GUARDIANS, DEFAULT_BASE_UPDATE_FEE, DEFAULT_COIN_TO_MINT);
  1052. test_scenario::next_tx(&mut scenario, DEPLOYER);
  1053. let (pyth_state, worm_state) = take_wormhole_and_pyth_states(&scenario);
  1054. let verified_vaas = get_verified_test_vaas(&worm_state, &clock);
  1055. // Update cache is called by create_price_feeds.
  1056. pyth::create_price_feeds(
  1057. &mut pyth_state,
  1058. verified_vaas,
  1059. &clock,
  1060. ctx(&mut scenario)
  1061. );
  1062. test_scenario::next_tx(&mut scenario, DEPLOYER);
  1063. let price_info_object_1 = take_shared<PriceInfoObject>(&scenario);
  1064. let price_info_object_2 = take_shared<PriceInfoObject>(&scenario);
  1065. let price_info_object_3 = take_shared<PriceInfoObject>(&scenario);
  1066. let price_info_object_4 = take_shared<PriceInfoObject>(&scenario);
  1067. // These updates are price infos that correspond to the ones in TEST_VAAS.
  1068. let updates = get_mock_price_infos();
  1069. let price_info_object_vec = vector[
  1070. price_info_object_1,
  1071. price_info_object_2,
  1072. price_info_object_3,
  1073. price_info_object_4
  1074. ];
  1075. // Check that TEST_VAAS was indeed used to instantiate the price feeds correctly,
  1076. // by confirming that the info in updates is contained in price_info_object_vec.
  1077. check_price_feeds_cached(&updates, &price_info_object_vec);
  1078. price_info_object_4 = vector::pop_back(&mut price_info_object_vec);
  1079. price_info_object_3 = vector::pop_back(&mut price_info_object_vec);
  1080. price_info_object_2 = vector::pop_back(&mut price_info_object_vec);
  1081. price_info_object_1 = vector::pop_back(&mut price_info_object_vec);
  1082. vector::destroy_empty(price_info_object_vec);
  1083. return_shared(price_info_object_1);
  1084. return_shared(price_info_object_2);
  1085. return_shared(price_info_object_3);
  1086. return_shared(price_info_object_4);
  1087. coin::burn_for_testing<SUI>(test_coins);
  1088. cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock);
  1089. test_scenario::end(scenario);
  1090. }
  1091. #[test]
  1092. fun test_update_cache_old_update() {
  1093. use pyth::i64::Self;
  1094. use pyth::price::Self;
  1095. let (scenario, test_coins, clock) = setup_test(500, 23, x"5d1f252d5de865279b00c84bce362774c2804294ed53299bc4a0389a5defef92", data_sources_for_test_vaa(), BATCH_ATTESTATION_TEST_INITIAL_GUARDIANS, DEFAULT_BASE_UPDATE_FEE, DEFAULT_COIN_TO_MINT);
  1096. test_scenario::next_tx(&mut scenario, DEPLOYER);
  1097. let (pyth_state, worm_state) = take_wormhole_and_pyth_states(&scenario);
  1098. let verified_vaas = get_verified_test_vaas(&worm_state, &clock);
  1099. pyth::create_price_feeds(
  1100. &mut pyth_state,
  1101. verified_vaas,
  1102. &clock,
  1103. ctx(&mut scenario)
  1104. );
  1105. test_scenario::next_tx(&mut scenario, DEPLOYER);
  1106. let price_info_object_1 = take_shared<PriceInfoObject>(&scenario);
  1107. let price_info_object_2 = take_shared<PriceInfoObject>(&scenario);
  1108. let price_info_object_3 = take_shared<PriceInfoObject>(&scenario);
  1109. let price_info_object_4 = take_shared<PriceInfoObject>(&scenario);
  1110. // Hardcode the price identifier, price, and ema_price for price_info_object_1, because
  1111. // it's easier than unwrapping price_info_object_1 and getting the quantities via getters.
  1112. let timestamp = 1663680740;
  1113. let price_identifier = price_identifier::from_byte_vec(x"c6c75c89f14810ec1c54c03ab8f1864a4c4032791f05747f560faec380a695d1");
  1114. let price = price::new(i64::new(1557, false), 7, i64::new(5, true), timestamp);
  1115. let ema_price = price::new(i64::new(1500, false), 3, i64::new(5, true), timestamp);
  1116. // Attempt to update the price with an update older than the current cached one.
  1117. let old_price = price::new(i64::new(1243, true), 9802, i64::new(6, false), timestamp - 200);
  1118. let old_ema_price = price::new(i64::new(8976, true), 234, i64::new(897, false), timestamp - 200);
  1119. let old_update = price_info::new_price_info(
  1120. 1257278600,
  1121. 1690226180,
  1122. price_feed::new(
  1123. price_identifier,
  1124. old_price,
  1125. old_ema_price,
  1126. )
  1127. );
  1128. let latest_only = pyth::state::create_latest_only_for_test();
  1129. pyth::update_cache(latest_only, &old_update, &mut price_info_object_1, &clock);
  1130. let current_price_info = price_info::get_price_info_from_price_info_object(&price_info_object_1);
  1131. let current_price_feed = price_info::get_price_feed(&current_price_info);
  1132. let current_price = price_feed::get_price(current_price_feed);
  1133. let current_ema_price = price_feed::get_ema_price(current_price_feed);
  1134. // Confirm that no price update occurred when we tried to update cache with an
  1135. // outdated update: old_update.
  1136. assert!(current_price == price, 1);
  1137. assert!(current_ema_price == ema_price, 1);
  1138. test_scenario::next_tx(&mut scenario, DEPLOYER);
  1139. // Update the cache with a fresh update.
  1140. let fresh_price = price::new(i64::new(5243, true), 2, i64::new(3, false), timestamp + 200);
  1141. let fresh_ema_price = price::new(i64::new(8976, true), 21, i64::new(32, false), timestamp + 200);
  1142. let fresh_update = price_info::new_price_info(
  1143. 1257278600,
  1144. 1690226180,
  1145. price_feed::new(
  1146. price_identifier,
  1147. fresh_price,
  1148. fresh_ema_price,
  1149. )
  1150. );
  1151. let latest_only = pyth::state::create_latest_only_for_test();
  1152. pyth::update_cache(latest_only, &fresh_update, &mut price_info_object_1, &clock);
  1153. // Confirm that the Pyth cached price got updated to fresh_price.
  1154. let current_price_info = price_info::get_price_info_from_price_info_object(&price_info_object_1);
  1155. let current_price_feed = price_info::get_price_feed(&current_price_info);
  1156. let current_price = price_feed::get_price(current_price_feed);
  1157. let current_ema_price = price_feed::get_ema_price(current_price_feed);
  1158. assert!(current_price==fresh_price, 0);
  1159. assert!(current_ema_price==fresh_ema_price, 0);
  1160. return_shared(price_info_object_1);
  1161. return_shared(price_info_object_2);
  1162. return_shared(price_info_object_3);
  1163. return_shared(price_info_object_4);
  1164. coin::burn_for_testing<SUI>(test_coins);
  1165. cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock);
  1166. test_scenario::end(scenario);
  1167. }
  1168. // pyth accumulator tests (included in this file instead of pyth_accumulator.move to avoid dependency cycle - as we need pyth_tests::setup_test)
  1169. #[test]
  1170. fun test_parse_and_verify_accumulator_updates(){
  1171. use sui::test_scenario::{Self, take_shared, return_shared};
  1172. use sui::transfer::{Self};
  1173. let (scenario, coins, clock) = setup_test(500, 23, ACCUMULATOR_TESTS_EMITTER_ADDRESS, vector[], ACCUMULATOR_TESTS_INITIAL_GUARDIANS, 50, 0);
  1174. let worm_state = take_shared<WormState>(&scenario);
  1175. test_scenario::next_tx(&mut scenario, @0x123);
  1176. let verified_vaa = get_verified_vaa_from_accumulator_message(&worm_state, TEST_ACCUMULATOR_3_MSGS, &clock);
  1177. let cur = cursor::new(TEST_ACCUMULATOR_3_MSGS);
  1178. let price_info_updates = accumulator::parse_and_verify_accumulator_message(&mut cur, vaa::take_payload(verified_vaa), &clock);
  1179. let expected_price_infos = accumulator_test_3_to_price_info(0);
  1180. let num_updates = vector::length<PriceInfo>(&price_info_updates);
  1181. let i = 0;
  1182. while (i < num_updates){
  1183. assert!(price_feeds_equal(vector::borrow(&price_info_updates, i), vector::borrow(&expected_price_infos, i)), 0);
  1184. i = i + 1;
  1185. };
  1186. // clean-up
  1187. cursor::take_rest(cur);
  1188. transfer::public_transfer(coins, @0x1234);
  1189. clock::destroy_for_testing(clock);
  1190. return_shared(worm_state);
  1191. test_scenario::end(scenario);
  1192. }
  1193. #[test]
  1194. fun test_parse_and_verify_accumulator_updates_with_extra_bytes_at_end_of_message(){
  1195. use sui::test_scenario::{Self, take_shared, return_shared};
  1196. use sui::transfer::{Self};
  1197. let (scenario, coins, clock) = setup_test(500, 23, ACCUMULATOR_TESTS_EMITTER_ADDRESS, vector[], ACCUMULATOR_TESTS_INITIAL_GUARDIANS, 50, 0);
  1198. let worm_state = take_shared<WormState>(&scenario);
  1199. test_scenario::next_tx(&mut scenario, @0x123);
  1200. let verified_vaa = get_verified_vaa_from_accumulator_message(&worm_state, TEST_ACCUMULATOR_3_MSGS, &clock);
  1201. // append some extra garbage bytes at the end of the accumulator message, and make sure
  1202. // that parse_and_verify_accumulator_message does not error out
  1203. vector::append(&mut TEST_ACCUMULATOR_3_MSGS, x"1234123412341234");
  1204. let cur = cursor::new(TEST_ACCUMULATOR_3_MSGS);
  1205. let price_info_updates = accumulator::parse_and_verify_accumulator_message(&mut cur, vaa::take_payload(verified_vaa), &clock);
  1206. let expected_price_infos = accumulator_test_3_to_price_info(0);
  1207. let num_updates = vector::length<PriceInfo>(&price_info_updates);
  1208. let i = 0;
  1209. while (i < num_updates){
  1210. assert!(price_feeds_equal(vector::borrow(&price_info_updates, i), vector::borrow(&expected_price_infos, i)), 0);
  1211. i = i + 1;
  1212. };
  1213. // clean-up
  1214. cursor::take_rest(cur);
  1215. transfer::public_transfer(coins, @0x1234);
  1216. clock::destroy_for_testing(clock);
  1217. return_shared(worm_state);
  1218. test_scenario::end(scenario);
  1219. }
  1220. fun price_feeds_equal(p1: &PriceInfo, p2: &PriceInfo): bool{
  1221. price_info::get_price_feed(p1)== price_info::get_price_feed(p2)
  1222. }
  1223. // helper functions for setting up tests
  1224. // accumulator_test_3_to_price_info gets the data encoded within TEST_ACCUMULATOR_3_MSGS
  1225. fun accumulator_test_3_to_price_info(offset: u64): vector<PriceInfo> {
  1226. use pyth::i64::{Self};
  1227. use pyth::price::{Self};
  1228. let i = 0;
  1229. let feed_ids = vector[x"b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6",
  1230. x"6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af",
  1231. x"31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c68"];
  1232. let expected: vector<PriceInfo> = vector[];
  1233. while (i < 3) {
  1234. vector::push_back(&mut expected, price_info::new_price_info(
  1235. 1663680747,
  1236. 1663074349,
  1237. price_feed::new(
  1238. price_identifier::from_byte_vec(
  1239. *vector::borrow(&feed_ids, i)
  1240. ),
  1241. price::new(
  1242. i64::new(100 + i + offset, false),
  1243. 50 + i + offset,
  1244. i64::new(9 + i + offset, false),
  1245. 1687276660 + i + offset
  1246. ),
  1247. price::new(
  1248. i64::new(99 + i + offset, false),
  1249. 52 + i + offset,
  1250. i64::new(9 + i + offset, false),
  1251. 1687276660 + i + offset
  1252. ),
  1253. ),
  1254. ));
  1255. i = i + 1;
  1256. };
  1257. return expected
  1258. }
  1259. // accumulator_test_1_to_price_info gets the data encoded within TEST_ACCUMULATOR_SINGLE_FEED
  1260. fun accumulator_test_1_to_price_info(): PriceInfo {
  1261. use pyth::i64::{Self};
  1262. use pyth::price::{Self};
  1263. price_info::new_price_info(
  1264. 1663680747,
  1265. 1663074349,
  1266. price_feed::new(
  1267. price_identifier::from_byte_vec(
  1268. x"b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6"
  1269. ),
  1270. price::new(
  1271. i64::new(6887568746747646632, false),
  1272. 13092246197863718329,
  1273. i64::new(1559537863, false),
  1274. 1687276661
  1275. ),
  1276. price::new(
  1277. i64::new(4772242609775910581, false),
  1278. 358129956189946877,
  1279. i64::new(1559537863, false),
  1280. 1687276661
  1281. ),
  1282. ),
  1283. )
  1284. }
  1285. public fun cleanup_worm_state_pyth_state_and_clock(worm_state: WormState, pyth_state: PythState, clock: Clock){
  1286. return_shared(worm_state);
  1287. return_shared(pyth_state);
  1288. clock::destroy_for_testing(clock);
  1289. }
  1290. public fun take_wormhole_and_pyth_states(scenario: &Scenario): (PythState, WormState){
  1291. (take_shared<PythState>(scenario), take_shared<WormState>(scenario))
  1292. }
  1293. }