workspaces.rs 28 KB

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