workspaces.rs 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014
  1. use {
  2. near_sdk::json_types::U128,
  3. pyth::{
  4. governance::{
  5. GovernanceAction,
  6. GovernanceInstruction,
  7. GovernanceModule,
  8. },
  9. state::{
  10. Chain,
  11. Price,
  12. PriceIdentifier,
  13. Source,
  14. },
  15. },
  16. pyth_wormhole_attester_sdk::{
  17. BatchPriceAttestation,
  18. Identifier,
  19. PriceAttestation,
  20. PriceStatus,
  21. },
  22. pythnet_sdk::test_utils::{
  23. create_accumulator_message,
  24. create_dummy_price_feed_message,
  25. create_vaa_from_payload,
  26. DEFAULT_DATA_SOURCE,
  27. DEFAULT_GOVERNANCE_SOURCE,
  28. DEFAULT_VALID_TIME_PERIOD,
  29. SECONDARY_DATA_SOURCE,
  30. SECONDARY_GOVERNANCE_SOURCE,
  31. },
  32. serde_json::json,
  33. std::collections::HashMap,
  34. wormhole_sdk::Chain as WormholeChain,
  35. };
  36. async fn initialize_chain() -> (
  37. workspaces::Worker<workspaces::network::Sandbox>,
  38. workspaces::Contract,
  39. workspaces::Contract,
  40. ) {
  41. let worker = workspaces::sandbox().await.expect("Workspaces Failed");
  42. // Deploy Pyth
  43. let contract = worker
  44. .dev_deploy(&std::fs::read("pyth.wasm").expect("Failed to find pyth.wasm"))
  45. .await
  46. .expect("Failed to deploy pyth.wasm");
  47. // Deploy Wormhole Stub, this is a dummy contract that always verifies VAA's correctly so we
  48. // can test the ext_wormhole API.
  49. let wormhole = worker
  50. .dev_deploy(
  51. &std::fs::read("wormhole_stub.wasm").expect("Failed to find wormhole_stub.wasm"),
  52. )
  53. .await
  54. .expect("Failed to deploy wormhole_stub.wasm");
  55. // Initialize Wormhole.
  56. let _ = wormhole
  57. .call("new")
  58. .args_json(&json!({}))
  59. .gas(300_000_000_000_000)
  60. .transact_async()
  61. .await
  62. .expect("Failed to initialize Wormhole")
  63. .await
  64. .unwrap();
  65. // Initialize Pyth, one time operation that sets the Wormhole contract address.
  66. let codehash = [0u8; 32];
  67. let _ = contract
  68. .call("new")
  69. .args_json(&json!({
  70. "wormhole": wormhole.id(),
  71. "codehash": codehash,
  72. "initial_source": Source {
  73. emitter: DEFAULT_DATA_SOURCE.address.0,
  74. chain: Chain::from(WormholeChain::from(u16::from(DEFAULT_DATA_SOURCE.chain))),
  75. },
  76. "gov_source": Source {
  77. emitter: DEFAULT_GOVERNANCE_SOURCE.address.0,
  78. chain: Chain::from(WormholeChain::from(u16::from(DEFAULT_GOVERNANCE_SOURCE.chain))),
  79. },
  80. "update_fee": U128::from(1u128),
  81. "stale_threshold": DEFAULT_VALID_TIME_PERIOD,
  82. }))
  83. .gas(300_000_000_000_000)
  84. .transact_async()
  85. .await
  86. .expect("Failed to initialize Pyth")
  87. .await
  88. .unwrap();
  89. (worker, contract, wormhole)
  90. }
  91. #[tokio::test]
  92. async fn test_set_sources() {
  93. let (_, contract, _) = initialize_chain().await;
  94. // Submit a new Source to the contract, this will trigger a cross-contract call to wormhole
  95. let vaa = create_vaa_from_payload(
  96. &GovernanceInstruction {
  97. target: Chain::from(WormholeChain::Any),
  98. module: GovernanceModule::Target,
  99. action: GovernanceAction::SetDataSources {
  100. data_sources: vec![
  101. Source {
  102. emitter: DEFAULT_DATA_SOURCE.address.0,
  103. chain: Chain::from(WormholeChain::from(u16::from(
  104. DEFAULT_DATA_SOURCE.chain,
  105. ))),
  106. },
  107. Source {
  108. emitter: SECONDARY_DATA_SOURCE.address.0,
  109. chain: Chain::from(WormholeChain::from(u16::from(
  110. SECONDARY_DATA_SOURCE.chain,
  111. ))),
  112. },
  113. ],
  114. },
  115. }
  116. .serialize()
  117. .unwrap(),
  118. DEFAULT_GOVERNANCE_SOURCE.address,
  119. DEFAULT_GOVERNANCE_SOURCE.chain,
  120. 1,
  121. );
  122. let vaa = hex::encode(serde_wormhole::to_vec(&vaa).unwrap());
  123. assert!(contract
  124. .call("execute_governance_instruction")
  125. .gas(300_000_000_000_000)
  126. .deposit(300_000_000_000_000_000_000_000)
  127. .args_json(&json!({
  128. "vaa": vaa,
  129. }))
  130. .transact_async()
  131. .await
  132. .expect("Failed to submit VAA")
  133. .await
  134. .unwrap()
  135. .failures()
  136. .is_empty());
  137. // There should now be a two sources in the contract state.
  138. assert_eq!(
  139. serde_json::from_slice::<Vec<Source>>(&contract.view("get_sources").await.unwrap().result)
  140. .unwrap(),
  141. &[
  142. Source {
  143. emitter: DEFAULT_DATA_SOURCE.address.0,
  144. chain: Chain::from(WormholeChain::from(u16::from(DEFAULT_DATA_SOURCE.chain))),
  145. },
  146. Source {
  147. emitter: SECONDARY_DATA_SOURCE.address.0,
  148. chain: Chain::from(WormholeChain::from(u16::from(SECONDARY_DATA_SOURCE.chain))),
  149. },
  150. ]
  151. );
  152. }
  153. #[tokio::test]
  154. async fn test_set_governance_source() {
  155. let (_, contract, _) = initialize_chain().await;
  156. // Data Source Upgrades are submitted with an embedded VAA, generate that one here first
  157. // before we embed it.
  158. let request_vaa = create_vaa_from_payload(
  159. &GovernanceInstruction {
  160. target: Chain::from(WormholeChain::Near),
  161. module: GovernanceModule::Target,
  162. action: GovernanceAction::RequestGovernanceDataSourceTransfer {
  163. governance_data_source_index: 1,
  164. },
  165. }
  166. .serialize()
  167. .unwrap(),
  168. SECONDARY_GOVERNANCE_SOURCE.address,
  169. SECONDARY_GOVERNANCE_SOURCE.chain,
  170. 1,
  171. );
  172. // Submit a new Source to the contract, this will trigger a cross-contract call to wormhole
  173. let vaa = create_vaa_from_payload(
  174. &GovernanceInstruction {
  175. target: Chain::from(WormholeChain::Near),
  176. module: GovernanceModule::Target,
  177. action: GovernanceAction::AuthorizeGovernanceDataSourceTransfer {
  178. claim_vaa: serde_wormhole::to_vec(&request_vaa).unwrap(),
  179. },
  180. }
  181. .serialize()
  182. .unwrap(),
  183. DEFAULT_GOVERNANCE_SOURCE.address,
  184. DEFAULT_GOVERNANCE_SOURCE.chain,
  185. 2,
  186. );
  187. let vaa = hex::encode(serde_wormhole::to_vec(&vaa).unwrap());
  188. assert!(contract
  189. .call("execute_governance_instruction")
  190. .gas(300_000_000_000_000)
  191. .deposit(300_000_000_000_000_000_000_000)
  192. .args_json(&json!({
  193. "vaa": vaa,
  194. }))
  195. .transact()
  196. .await
  197. .expect("Failed to submit VAA")
  198. .unwrap()
  199. .failures()
  200. .is_empty());
  201. // An action from the new source should now be accepted.
  202. let vaa = create_vaa_from_payload(
  203. &GovernanceInstruction {
  204. target: Chain::from(WormholeChain::Near),
  205. module: GovernanceModule::Target,
  206. action: GovernanceAction::SetDataSources {
  207. data_sources: vec![
  208. Source {
  209. emitter: DEFAULT_DATA_SOURCE.address.0,
  210. chain: Chain::from(WormholeChain::from(u16::from(
  211. DEFAULT_DATA_SOURCE.chain,
  212. ))),
  213. },
  214. Source {
  215. emitter: SECONDARY_DATA_SOURCE.address.0,
  216. chain: Chain::from(WormholeChain::from(u16::from(
  217. SECONDARY_DATA_SOURCE.chain,
  218. ))),
  219. },
  220. ],
  221. },
  222. }
  223. .serialize()
  224. .unwrap(),
  225. SECONDARY_GOVERNANCE_SOURCE.address,
  226. SECONDARY_GOVERNANCE_SOURCE.chain,
  227. 2,
  228. );
  229. let vaa = hex::encode(serde_wormhole::to_vec(&vaa).unwrap());
  230. assert!(contract
  231. .call("execute_governance_instruction")
  232. .gas(300_000_000_000_000)
  233. .deposit(300_000_000_000_000_000_000_000)
  234. .args_json(&json!({
  235. "vaa": vaa,
  236. }))
  237. .transact_async()
  238. .await
  239. .expect("Failed to submit VAA")
  240. .await
  241. .unwrap()
  242. .failures()
  243. .is_empty());
  244. // But not from the old source.
  245. let vaa = create_vaa_from_payload(
  246. &GovernanceInstruction {
  247. target: Chain::from(WormholeChain::Near),
  248. module: GovernanceModule::Target,
  249. action: GovernanceAction::SetDataSources {
  250. data_sources: vec![
  251. Source::default(),
  252. Source {
  253. emitter: DEFAULT_DATA_SOURCE.address.0,
  254. chain: Chain::from(WormholeChain::from(u16::from(
  255. DEFAULT_DATA_SOURCE.chain,
  256. ))),
  257. },
  258. Source {
  259. emitter: SECONDARY_DATA_SOURCE.address.0,
  260. chain: Chain::from(WormholeChain::from(u16::from(
  261. SECONDARY_DATA_SOURCE.chain,
  262. ))),
  263. },
  264. ],
  265. },
  266. }
  267. .serialize()
  268. .unwrap(),
  269. DEFAULT_GOVERNANCE_SOURCE.address,
  270. DEFAULT_GOVERNANCE_SOURCE.chain,
  271. 4,
  272. );
  273. let vaa = hex::encode(serde_wormhole::to_vec(&vaa).unwrap());
  274. assert!(contract
  275. .call("execute_governance_instruction")
  276. .gas(300_000_000_000_000)
  277. .deposit(300_000_000_000_000_000_000_000)
  278. .args_json(&json!({
  279. "vaa": vaa,
  280. }))
  281. .transact_async()
  282. .await
  283. .expect("Failed to submit VAA")
  284. .await
  285. .unwrap()
  286. .outcome()
  287. .is_success());
  288. }
  289. #[tokio::test]
  290. async fn test_stale_threshold() {
  291. let (_, contract, _) = initialize_chain().await;
  292. // Get current UNIX timestamp and subtract a minute from it to place the price attestation in
  293. // the past. This should be accepted but untrusted.
  294. let now = std::time::SystemTime::now()
  295. .duration_since(std::time::UNIX_EPOCH)
  296. .expect("Failed to get UNIX timestamp")
  297. .as_secs()
  298. - DEFAULT_VALID_TIME_PERIOD;
  299. // Submit a Price Attestation to the contract.
  300. let vaa = create_vaa_from_payload(
  301. &BatchPriceAttestation {
  302. price_attestations: vec![PriceAttestation {
  303. product_id: Identifier::default(),
  304. price_id: Identifier::default(),
  305. price: 100,
  306. conf: 1,
  307. expo: 8,
  308. ema_price: 100,
  309. ema_conf: 1,
  310. status: PriceStatus::Trading,
  311. num_publishers: 8,
  312. max_num_publishers: 8,
  313. attestation_time: now.try_into().unwrap(),
  314. publish_time: now.try_into().unwrap(),
  315. prev_publish_time: now.try_into().unwrap(),
  316. prev_price: 100,
  317. prev_conf: 1,
  318. last_attested_publish_time: now.try_into().unwrap(),
  319. }],
  320. }
  321. .serialize()
  322. .unwrap(),
  323. DEFAULT_DATA_SOURCE.address,
  324. DEFAULT_DATA_SOURCE.chain,
  325. 1,
  326. );
  327. let vaa = hex::encode(serde_wormhole::to_vec(&vaa).unwrap());
  328. let update_fee = serde_json::from_slice::<U128>(
  329. &contract
  330. .view("get_update_fee_estimate")
  331. .args_json(&json!({
  332. "data": vaa,
  333. }))
  334. .await
  335. .unwrap()
  336. .result,
  337. )
  338. .unwrap();
  339. // Submit price. As there are no prices this should succeed despite being old.
  340. assert!(contract
  341. .call("update_price_feeds")
  342. .gas(300_000_000_000_000)
  343. .deposit(update_fee.into())
  344. .args_json(&json!({
  345. "data": vaa,
  346. }))
  347. .transact_async()
  348. .await
  349. .expect("Failed to submit VAA")
  350. .await
  351. .unwrap()
  352. .failures()
  353. .is_empty());
  354. // Despite succeeding, assert Price cannot be requested, 60 seconds in the past should be
  355. // considered stale. [tag:failed_price_check]
  356. assert_eq!(
  357. None,
  358. serde_json::from_slice::<Option<Price>>(
  359. &contract
  360. .view("get_price")
  361. .args_json(&json!({ "price_identifier": PriceIdentifier([0; 32]) }))
  362. .await
  363. .unwrap()
  364. .result
  365. )
  366. .unwrap(),
  367. );
  368. assert_eq!(
  369. &None,
  370. serde_json::from_slice::<HashMap<PriceIdentifier, Option<Price>>>(
  371. &contract
  372. .view("list_prices")
  373. .args_json(&json!({ "price_ids": vec![PriceIdentifier([0; 32])] }))
  374. .await
  375. .unwrap()
  376. .result
  377. )
  378. .unwrap()
  379. .get(&PriceIdentifier([0; 32]))
  380. .unwrap(),
  381. );
  382. // Submit another Price Attestation to the contract with an even older timestamp. Which
  383. // should now fail due to the existing newer price.
  384. let vaa = create_vaa_from_payload(
  385. &BatchPriceAttestation {
  386. price_attestations: vec![PriceAttestation {
  387. product_id: Identifier::default(),
  388. price_id: Identifier::default(),
  389. price: 1000,
  390. conf: 1,
  391. expo: 8,
  392. ema_price: 1000,
  393. ema_conf: 1,
  394. status: PriceStatus::Trading,
  395. num_publishers: 8,
  396. max_num_publishers: 8,
  397. attestation_time: (now - 1024).try_into().unwrap(),
  398. publish_time: (now - 1024).try_into().unwrap(),
  399. prev_publish_time: (now - 1024).try_into().unwrap(),
  400. prev_price: 90,
  401. prev_conf: 1,
  402. last_attested_publish_time: (now - 1024).try_into().unwrap(),
  403. }],
  404. }
  405. .serialize()
  406. .unwrap(),
  407. DEFAULT_DATA_SOURCE.address,
  408. DEFAULT_DATA_SOURCE.chain,
  409. 2,
  410. );
  411. let vaa = hex::encode(serde_wormhole::to_vec(&vaa).unwrap());
  412. // The update handler should now succeed even if price is old, but simply not update the price.
  413. assert!(contract
  414. .call("update_price_feeds")
  415. .gas(300_000_000_000_000)
  416. .deposit(update_fee.into())
  417. .args_json(&json!({
  418. "data": vaa,
  419. }))
  420. .transact_async()
  421. .await
  422. .expect("Failed to submit VAA")
  423. .await
  424. .unwrap()
  425. .failures()
  426. .is_empty());
  427. // The price however should _not_ have updated and if we check the unsafe stored price the
  428. // timestamp and price should be unchanged.
  429. assert_eq!(
  430. Price {
  431. price: 100.into(),
  432. conf: 1.into(),
  433. expo: 8,
  434. publish_time: now as i64,
  435. },
  436. serde_json::from_slice::<Price>(
  437. &contract
  438. .view("get_price_unsafe")
  439. .args_json(&json!({ "price_identifier": PriceIdentifier([0; 32]) }))
  440. .await
  441. .unwrap()
  442. .result
  443. )
  444. .unwrap(),
  445. );
  446. // Now we extend the staleness threshold with a Governance VAA.
  447. let vaa = create_vaa_from_payload(
  448. &GovernanceInstruction {
  449. target: Chain::from(WormholeChain::Near),
  450. module: GovernanceModule::Target,
  451. action: GovernanceAction::SetValidPeriod { valid_seconds: 256 },
  452. }
  453. .serialize()
  454. .unwrap(),
  455. DEFAULT_GOVERNANCE_SOURCE.address,
  456. DEFAULT_GOVERNANCE_SOURCE.chain,
  457. 3,
  458. );
  459. let vaa = hex::encode(serde_wormhole::to_vec(&vaa).unwrap());
  460. assert!(contract
  461. .call("execute_governance_instruction")
  462. .gas(300_000_000_000_000)
  463. .deposit(300_000_000_000_000_000_000_000)
  464. .args_json(&json!({
  465. "vaa": vaa,
  466. }))
  467. .transact_async()
  468. .await
  469. .expect("Failed to submit VAA")
  470. .await
  471. .unwrap()
  472. .failures()
  473. .is_empty());
  474. // It should now be possible to request the price that previously returned None.
  475. // [ref:failed_price_check]
  476. assert_eq!(
  477. Some(Price {
  478. price: 100.into(),
  479. conf: 1.into(),
  480. expo: 8,
  481. publish_time: now as i64,
  482. }),
  483. serde_json::from_slice::<Option<Price>>(
  484. &contract
  485. .view("get_price")
  486. .args_json(&json!({ "price_identifier": PriceIdentifier([0; 32]) }))
  487. .await
  488. .unwrap()
  489. .result
  490. )
  491. .unwrap(),
  492. );
  493. assert_eq!(
  494. &Some(Price {
  495. price: 100.into(),
  496. conf: 1.into(),
  497. expo: 8,
  498. publish_time: now as i64,
  499. }),
  500. serde_json::from_slice::<HashMap<PriceIdentifier, Option<Price>>>(
  501. &contract
  502. .view("list_prices")
  503. .args_json(&json!({ "price_ids": vec![PriceIdentifier([0; 32])] }))
  504. .await
  505. .unwrap()
  506. .result
  507. )
  508. .unwrap()
  509. .get(&PriceIdentifier([0; 32]))
  510. .unwrap(),
  511. );
  512. }
  513. #[tokio::test]
  514. async fn test_contract_fees() {
  515. let (_, contract, _) = initialize_chain().await;
  516. let now = std::time::SystemTime::now()
  517. .duration_since(std::time::UNIX_EPOCH)
  518. .expect("Failed to get UNIX timestamp")
  519. .as_secs();
  520. // Set a high fee for the contract needed to submit a price.
  521. let vaa = create_vaa_from_payload(
  522. &GovernanceInstruction {
  523. target: Chain::from(WormholeChain::Near),
  524. module: GovernanceModule::Target,
  525. action: GovernanceAction::SetFee { base: 128, expo: 8 },
  526. }
  527. .serialize()
  528. .unwrap(),
  529. DEFAULT_GOVERNANCE_SOURCE.address,
  530. DEFAULT_GOVERNANCE_SOURCE.chain,
  531. 1,
  532. );
  533. let vaa = hex::encode(serde_wormhole::to_vec(&vaa).unwrap());
  534. // Fetch Update fee before changing it.
  535. let update_fee = serde_json::from_slice::<U128>(
  536. &contract
  537. .view("get_update_fee_estimate")
  538. .args_json(&json!({
  539. "data": vaa,
  540. }))
  541. .await
  542. .unwrap()
  543. .result,
  544. )
  545. .unwrap();
  546. // Now set the update_fee so that it is too high for the deposit to cover.
  547. assert!(contract
  548. .call("execute_governance_instruction")
  549. .gas(300_000_000_000_000)
  550. .deposit(300_000_000_000_000_000_000_000)
  551. .args_json(&json!({
  552. "vaa": vaa,
  553. }))
  554. .transact_async()
  555. .await
  556. .expect("Failed to submit VAA")
  557. .await
  558. .unwrap()
  559. .failures()
  560. .is_empty());
  561. // Check the state has actually changed before we try and execute another VAA.
  562. assert_ne!(
  563. u128::from(update_fee),
  564. u128::from(
  565. serde_json::from_slice::<U128>(
  566. &contract
  567. .view("get_update_fee_estimate")
  568. .args_json(&json!({
  569. "data": vaa,
  570. }))
  571. .await
  572. .unwrap()
  573. .result,
  574. )
  575. .unwrap()
  576. )
  577. );
  578. // Attempt to update the price feed with a now too low deposit.
  579. let vaa = create_vaa_from_payload(
  580. &BatchPriceAttestation {
  581. price_attestations: vec![PriceAttestation {
  582. product_id: Identifier::default(),
  583. price_id: Identifier::default(),
  584. price: 1000,
  585. conf: 1,
  586. expo: 8,
  587. ema_price: 1000,
  588. ema_conf: 1,
  589. status: PriceStatus::Trading,
  590. num_publishers: 8,
  591. max_num_publishers: 8,
  592. attestation_time: (now - 1024).try_into().unwrap(),
  593. publish_time: (now - 1024).try_into().unwrap(),
  594. prev_publish_time: (now - 1024).try_into().unwrap(),
  595. prev_price: 90,
  596. prev_conf: 1,
  597. last_attested_publish_time: (now - 1024).try_into().unwrap(),
  598. }],
  599. }
  600. .serialize()
  601. .unwrap(),
  602. DEFAULT_DATA_SOURCE.address,
  603. DEFAULT_DATA_SOURCE.chain,
  604. 2,
  605. );
  606. let vaa = hex::encode(serde_wormhole::to_vec(&vaa).unwrap());
  607. assert!(contract
  608. .call("update_price_feeds")
  609. .gas(300_000_000_000_000)
  610. .deposit(update_fee.into())
  611. .args_json(&json!({
  612. "data": vaa,
  613. }))
  614. .transact_async()
  615. .await
  616. .expect("Failed to submit VAA")
  617. .await
  618. .unwrap()
  619. .failures()
  620. .is_empty());
  621. // Submitting a Price should have failed because the fee was not enough.
  622. assert_eq!(
  623. None,
  624. serde_json::from_slice::<Option<Price>>(
  625. &contract
  626. .view("get_price")
  627. .args_json(&json!({ "price_identifier": PriceIdentifier([0; 32]) }))
  628. .await
  629. .unwrap()
  630. .result
  631. )
  632. .unwrap(),
  633. );
  634. }
  635. // A test that attempts to SetFee twice with the same governance action, the first should succeed,
  636. // the second should fail.
  637. #[tokio::test]
  638. async fn test_same_governance_sequence_fails() {
  639. let (_, contract, _) = initialize_chain().await;
  640. // Set a high fee for the contract needed to submit a price.
  641. let vaa = create_vaa_from_payload(
  642. &GovernanceInstruction {
  643. target: Chain::from(WormholeChain::Near),
  644. module: GovernanceModule::Target,
  645. action: GovernanceAction::SetFee { base: 128, expo: 8 },
  646. }
  647. .serialize()
  648. .unwrap(),
  649. DEFAULT_GOVERNANCE_SOURCE.address,
  650. DEFAULT_GOVERNANCE_SOURCE.chain,
  651. 1,
  652. );
  653. let vaa = hex::encode(serde_wormhole::to_vec(&vaa).unwrap());
  654. // Attempt our first SetFee.
  655. assert!(contract
  656. .call("execute_governance_instruction")
  657. .gas(300_000_000_000_000)
  658. .deposit(300_000_000_000_000_000_000_000)
  659. .args_json(&json!({
  660. "vaa": vaa,
  661. }))
  662. .transact_async()
  663. .await
  664. .expect("Failed to submit VAA")
  665. .await
  666. .unwrap()
  667. .failures()
  668. .is_empty());
  669. // Attempt to run the same VAA again.
  670. assert!(!contract
  671. .call("execute_governance_instruction")
  672. .gas(300_000_000_000_000)
  673. .deposit(300_000_000_000_000_000_000_000)
  674. .args_json(&json!({
  675. "vaa": vaa,
  676. }))
  677. .transact_async()
  678. .await
  679. .expect("Failed to submit VAA")
  680. .await
  681. .unwrap()
  682. .failures()
  683. .is_empty());
  684. }
  685. // A test that attempts to SetFee twice with the same governance action, the first should succeed,
  686. // the second should fail.
  687. #[tokio::test]
  688. async fn test_out_of_order_sequences_fail() {
  689. let (_, contract, _) = initialize_chain().await;
  690. // Set a high fee for the contract needed to submit a price.
  691. let vaa = create_vaa_from_payload(
  692. &GovernanceInstruction {
  693. target: Chain::from(WormholeChain::Near),
  694. module: GovernanceModule::Target,
  695. action: GovernanceAction::SetFee { base: 128, expo: 8 },
  696. }
  697. .serialize()
  698. .unwrap(),
  699. DEFAULT_GOVERNANCE_SOURCE.address,
  700. DEFAULT_GOVERNANCE_SOURCE.chain,
  701. 1,
  702. );
  703. let vaa = hex::encode(serde_wormhole::to_vec(&vaa).unwrap());
  704. // Attempt our first SetFee.
  705. assert!(contract
  706. .call("execute_governance_instruction")
  707. .gas(300_000_000_000_000)
  708. .deposit(300_000_000_000_000_000_000_000)
  709. .args_json(&json!({
  710. "vaa": vaa,
  711. }))
  712. .transact_async()
  713. .await
  714. .expect("Failed to submit VAA")
  715. .await
  716. .unwrap()
  717. .failures()
  718. .is_empty());
  719. // Generate another VAA with sequence 3.
  720. let vaa = create_vaa_from_payload(
  721. &GovernanceInstruction {
  722. target: Chain::from(WormholeChain::Near),
  723. module: GovernanceModule::Target,
  724. action: GovernanceAction::SetFee { base: 128, expo: 8 },
  725. }
  726. .serialize()
  727. .unwrap(),
  728. DEFAULT_GOVERNANCE_SOURCE.address,
  729. DEFAULT_GOVERNANCE_SOURCE.chain,
  730. 3,
  731. );
  732. let vaa = hex::encode(serde_wormhole::to_vec(&vaa).unwrap());
  733. // This should succeed.
  734. assert!(contract
  735. .call("execute_governance_instruction")
  736. .gas(300_000_000_000_000)
  737. .deposit(300_000_000_000_000_000_000_000)
  738. .args_json(&json!({
  739. "vaa": vaa,
  740. }))
  741. .transact_async()
  742. .await
  743. .expect("Failed to submit VAA")
  744. .await
  745. .unwrap()
  746. .failures()
  747. .is_empty());
  748. // Generate another VAA with sequence 2.
  749. let vaa = create_vaa_from_payload(
  750. &GovernanceInstruction {
  751. target: Chain::from(WormholeChain::Near),
  752. module: GovernanceModule::Target,
  753. action: GovernanceAction::SetFee { base: 128, expo: 8 },
  754. }
  755. .serialize()
  756. .unwrap(),
  757. DEFAULT_GOVERNANCE_SOURCE.address,
  758. DEFAULT_GOVERNANCE_SOURCE.chain,
  759. 2,
  760. );
  761. let vaa = hex::encode(serde_wormhole::to_vec(&vaa).unwrap());
  762. // This should fail due to being out of order.
  763. assert!(!contract
  764. .call("execute_governance_instruction")
  765. .gas(300_000_000_000_000)
  766. .deposit(300_000_000_000_000_000_000_000)
  767. .args_json(&json!({
  768. "vaa": vaa,
  769. }))
  770. .transact_async()
  771. .await
  772. .expect("Failed to submit VAA")
  773. .await
  774. .unwrap()
  775. .failures()
  776. .is_empty());
  777. }
  778. // A test that fails if the governance action payload target is not NEAR.
  779. #[tokio::test]
  780. async fn test_governance_target_fails_if_not_near() {
  781. let (_, contract, _) = initialize_chain().await;
  782. let vaa = create_vaa_from_payload(
  783. &GovernanceInstruction {
  784. target: Chain::from(WormholeChain::Solana),
  785. module: GovernanceModule::Target,
  786. action: GovernanceAction::SetFee { base: 128, expo: 8 },
  787. }
  788. .serialize()
  789. .unwrap(),
  790. DEFAULT_GOVERNANCE_SOURCE.address,
  791. DEFAULT_GOVERNANCE_SOURCE.chain,
  792. 1,
  793. );
  794. let vaa = hex::encode(serde_wormhole::to_vec(&vaa).unwrap());
  795. // This should fail as the target is Solana, when Near is expected.
  796. assert!(!contract
  797. .call("execute_governance_instruction")
  798. .gas(300_000_000_000_000)
  799. .deposit(300_000_000_000_000_000_000_000)
  800. .args_json(&json!({
  801. "vaa": vaa,
  802. }))
  803. .transact_async()
  804. .await
  805. .expect("Failed to submit VAA")
  806. .await
  807. .unwrap()
  808. .failures()
  809. .is_empty());
  810. }
  811. // A test to check accumulator style updates work as intended.
  812. #[tokio::test]
  813. async fn test_accumulator_updates() {
  814. let (_, contract, _) = initialize_chain().await;
  815. // Submit a new Source to the contract, this will trigger a cross-contract call to wormhole
  816. let vaa = create_vaa_from_payload(
  817. &GovernanceInstruction {
  818. target: Chain::from(WormholeChain::Any),
  819. module: GovernanceModule::Target,
  820. action: GovernanceAction::SetDataSources {
  821. data_sources: vec![Source {
  822. emitter: DEFAULT_DATA_SOURCE.address.0,
  823. chain: Chain::from(WormholeChain::from(u16::from(DEFAULT_DATA_SOURCE.chain))),
  824. }],
  825. },
  826. }
  827. .serialize()
  828. .unwrap(),
  829. DEFAULT_GOVERNANCE_SOURCE.address,
  830. DEFAULT_GOVERNANCE_SOURCE.chain,
  831. 1,
  832. );
  833. let vaa = hex::encode(serde_wormhole::to_vec(&vaa).unwrap());
  834. assert!(contract
  835. .call("execute_governance_instruction")
  836. .gas(300_000_000_000_000)
  837. .deposit(300_000_000_000_000_000_000_000)
  838. .args_json(&json!({
  839. "vaa": vaa,
  840. }))
  841. .transact_async()
  842. .await
  843. .expect("Failed to submit VAA")
  844. .await
  845. .unwrap()
  846. .failures()
  847. .is_empty());
  848. // Create a couple of test feeds.
  849. let feed_1 = create_dummy_price_feed_message(100);
  850. let feed_2 = create_dummy_price_feed_message(200);
  851. let message = create_accumulator_message(&[&feed_1, &feed_2], &[&feed_1], false, false, None);
  852. let message = hex::encode(message);
  853. // Call the usual UpdatePriceFeed function.
  854. assert!(contract
  855. .call("update_price_feeds")
  856. .gas(300_000_000_000_000)
  857. .deposit(300_000_000_000_000_000_000_000)
  858. .args_json(&json!({
  859. "data": message,
  860. }))
  861. .transact_async()
  862. .await
  863. .expect("Failed to submit VAA")
  864. .await
  865. .unwrap()
  866. .failures()
  867. .is_empty());
  868. // Check the price feed actually updated. Check both types of serialized PriceIdentifier.
  869. let mut identifier = [0; 32];
  870. identifier[0] = 100;
  871. assert_eq!(
  872. Some(Price {
  873. price: 100.into(),
  874. conf: 100.into(),
  875. expo: 100,
  876. publish_time: 100,
  877. }),
  878. serde_json::from_slice::<Option<Price>>(
  879. &contract
  880. .view("get_price_unsafe")
  881. .args_json(&json!({ "price_identifier": PriceIdentifier(identifier) }))
  882. .await
  883. .unwrap()
  884. .result
  885. )
  886. .unwrap(),
  887. );
  888. }
  889. #[tokio::test]
  890. async fn test_sdk_compat() {
  891. let price = pyth_sdk::Price {
  892. price: i64::MAX,
  893. conf: u64::MAX,
  894. expo: 100,
  895. publish_time: 100,
  896. };
  897. let encoded = serde_json::to_string(&price).unwrap();
  898. let decoded_price: Price = serde_json::from_str(&encoded).unwrap();
  899. assert_eq!(
  900. decoded_price,
  901. Price {
  902. price: i64::MAX.into(),
  903. conf: u64::MAX.into(),
  904. expo: 100,
  905. publish_time: 100,
  906. }
  907. );
  908. }
  909. #[tokio::test]
  910. async fn test_borsh_field_cmopat() {
  911. use near_sdk::borsh::{
  912. self,
  913. BorshDeserialize,
  914. BorshSerialize,
  915. };
  916. let price = pyth_sdk::Price {
  917. price: i64::MAX,
  918. conf: u64::MAX,
  919. expo: 100,
  920. publish_time: 100,
  921. };
  922. // Verify that we can still BorshDeserialize a struct with a different field name. Confirms
  923. // we don't have to migrate the state.
  924. #[derive(Eq, PartialEq, Debug, BorshSerialize, BorshDeserialize)]
  925. struct PriceTester {
  926. price: i64,
  927. conf: u64,
  928. expo: u32,
  929. bad_field_name: u64,
  930. }
  931. let encoded = near_sdk::borsh::BorshSerialize::try_to_vec(&price).unwrap();
  932. let decoded_price: PriceTester =
  933. near_sdk::borsh::BorshDeserialize::try_from_slice(&encoded).unwrap();
  934. assert_eq!(
  935. decoded_price,
  936. PriceTester {
  937. price: i64::MAX,
  938. conf: u64::MAX,
  939. expo: 100,
  940. bad_field_name: 100,
  941. }
  942. );
  943. }