PythTest.spec.ts 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722
  1. import { Blockchain, SandboxContract, TreasuryContract } from "@ton/sandbox";
  2. import { Cell, toNano } from "@ton/core";
  3. import "@ton/test-utils";
  4. import { compile } from "@ton/blueprint";
  5. import { HexString, Price } from "@pythnetwork/price-service-sdk";
  6. import { PythTest, PythTestConfig } from "../wrappers/PythTest";
  7. import {
  8. BTC_PRICE_FEED_ID,
  9. HERMES_BTC_ETH_UPDATE,
  10. PYTH_SET_DATA_SOURCES,
  11. PYTH_SET_FEE,
  12. TEST_GUARDIAN_ADDRESS1,
  13. PYTH_AUTHORIZE_GOVERNANCE_DATA_SOURCE_TRANSFER,
  14. PYTH_REQUEST_GOVERNANCE_DATA_SOURCE_TRANSFER,
  15. } from "./utils/pyth";
  16. import { GUARDIAN_SET_0, MAINNET_UPGRADE_VAAS } from "./utils/wormhole";
  17. import { DataSource } from "@pythnetwork/xc-admin-common";
  18. import { parseDataSource } from "./utils";
  19. const TIME_PERIOD = 60;
  20. const PRICE = new Price({
  21. price: "1",
  22. conf: "2",
  23. expo: 3,
  24. publishTime: 4,
  25. });
  26. const EMA_PRICE = new Price({
  27. price: "5",
  28. conf: "6",
  29. expo: 7,
  30. publishTime: 8,
  31. });
  32. const SINGLE_UPDATE_FEE = 1;
  33. const DATA_SOURCES: DataSource[] = [
  34. {
  35. emitterChain: 26,
  36. emitterAddress:
  37. "e101faedac5851e32b9b23b5f9411a8c2bac4aae3ed4dd7b811dd1a72ea4aa71",
  38. },
  39. ];
  40. const TEST_GOVERNANCE_DATA_SOURCES: DataSource[] = [
  41. {
  42. emitterChain: 1,
  43. emitterAddress:
  44. "0000000000000000000000000000000000000000000000000000000000000029",
  45. },
  46. {
  47. emitterChain: 2,
  48. emitterAddress:
  49. "000000000000000000000000000000000000000000000000000000000000002b",
  50. },
  51. ];
  52. describe("PythTest", () => {
  53. let code: Cell;
  54. beforeAll(async () => {
  55. code = await compile("PythTest");
  56. });
  57. let blockchain: Blockchain;
  58. let deployer: SandboxContract<TreasuryContract>;
  59. let pythTest: SandboxContract<PythTest>;
  60. beforeEach(async () => {
  61. blockchain = await Blockchain.create();
  62. deployer = await blockchain.treasury("deployer");
  63. });
  64. async function deployContract(
  65. priceFeedId: HexString = BTC_PRICE_FEED_ID,
  66. timePeriod: number = TIME_PERIOD,
  67. price: Price = PRICE,
  68. emaPrice: Price = EMA_PRICE,
  69. singleUpdateFee: number = SINGLE_UPDATE_FEE,
  70. dataSources: DataSource[] = DATA_SOURCES,
  71. guardianSetIndex: number = 0,
  72. guardianSet: string[] = GUARDIAN_SET_0,
  73. chainId: number = 1,
  74. governanceChainId: number = 1,
  75. governanceContract: string = "0000000000000000000000000000000000000000000000000000000000000004",
  76. governanceDataSource?: DataSource
  77. ) {
  78. const config: PythTestConfig = {
  79. priceFeedId,
  80. timePeriod,
  81. price,
  82. emaPrice,
  83. singleUpdateFee,
  84. dataSources,
  85. guardianSetIndex,
  86. guardianSet,
  87. chainId,
  88. governanceChainId,
  89. governanceContract,
  90. governanceDataSource,
  91. };
  92. pythTest = blockchain.openContract(PythTest.createFromConfig(config, code));
  93. const deployResult = await pythTest.sendDeploy(
  94. deployer.getSender(),
  95. toNano("0.05")
  96. );
  97. expect(deployResult.transactions).toHaveTransaction({
  98. from: deployer.address,
  99. to: pythTest.address,
  100. deploy: true,
  101. success: true,
  102. });
  103. }
  104. async function updateGuardianSets(
  105. pythTest: SandboxContract<PythTest>,
  106. deployer: SandboxContract<TreasuryContract>
  107. ) {
  108. for (const vaa of MAINNET_UPGRADE_VAAS) {
  109. const result = await pythTest.sendUpdateGuardianSet(
  110. deployer.getSender(),
  111. Buffer.from(vaa, "hex")
  112. );
  113. expect(result.transactions).toHaveTransaction({
  114. from: deployer.address,
  115. to: pythTest.address,
  116. success: true,
  117. });
  118. }
  119. }
  120. it("should correctly get price unsafe", async () => {
  121. await deployContract();
  122. const result = await pythTest.getPriceUnsafe(BTC_PRICE_FEED_ID);
  123. expect(result.price).toBe(1);
  124. expect(result.conf).toBe(2);
  125. expect(result.expo).toBe(3);
  126. expect(result.publishTime).toBe(4);
  127. });
  128. it("should correctly get price no older than", async () => {
  129. const timeNow = Math.floor(Date.now() / 1000) - TIME_PERIOD + 5; // 5 seconds buffer
  130. const price = new Price({
  131. price: "1",
  132. conf: "2",
  133. expo: 3,
  134. publishTime: timeNow,
  135. });
  136. await deployContract(BTC_PRICE_FEED_ID, TIME_PERIOD, price, EMA_PRICE);
  137. const result = await pythTest.getPriceNoOlderThan(
  138. TIME_PERIOD,
  139. BTC_PRICE_FEED_ID
  140. );
  141. expect(result.price).toBe(1);
  142. expect(result.conf).toBe(2);
  143. expect(result.expo).toBe(3);
  144. expect(result.publishTime).toBe(timeNow);
  145. });
  146. it("should fail to get price no older than", async () => {
  147. await deployContract();
  148. await expect(
  149. pythTest.getPriceNoOlderThan(TIME_PERIOD, BTC_PRICE_FEED_ID)
  150. ).rejects.toThrow("Unable to execute get method. Got exit_code: 1020"); // ERROR_OUTDATED_PRICE = 1020
  151. });
  152. it("should correctly get ema price no older than", async () => {
  153. const timeNow = Math.floor(Date.now() / 1000) - TIME_PERIOD + 5; // 5 seconds buffer
  154. const emaPrice = new Price({
  155. price: "5",
  156. conf: "6",
  157. expo: 7,
  158. publishTime: timeNow,
  159. });
  160. await deployContract(BTC_PRICE_FEED_ID, TIME_PERIOD, PRICE, emaPrice);
  161. const result = await pythTest.getEmaPriceNoOlderThan(
  162. TIME_PERIOD,
  163. BTC_PRICE_FEED_ID
  164. );
  165. expect(result.price).toBe(5);
  166. expect(result.conf).toBe(6);
  167. expect(result.expo).toBe(7);
  168. expect(result.publishTime).toBe(timeNow);
  169. });
  170. it("should fail to get ema price no older than", async () => {
  171. await deployContract();
  172. await expect(
  173. pythTest.getEmaPriceNoOlderThan(TIME_PERIOD, BTC_PRICE_FEED_ID)
  174. ).rejects.toThrow("Unable to execute get method. Got exit_code: 1020"); // ERROR_OUTDATED_PRICE = 1020
  175. });
  176. it("should correctly get ema price unsafe", async () => {
  177. await deployContract();
  178. const result = await pythTest.getEmaPriceUnsafe(BTC_PRICE_FEED_ID);
  179. expect(result.price).toBe(5);
  180. expect(result.conf).toBe(6);
  181. expect(result.expo).toBe(7);
  182. expect(result.publishTime).toBe(8);
  183. });
  184. it("should correctly get update fee", async () => {
  185. await deployContract();
  186. const result = await pythTest.getUpdateFee(
  187. Buffer.from(HERMES_BTC_ETH_UPDATE, "hex")
  188. );
  189. expect(result).toBe(2);
  190. });
  191. it("should correctly update price feeds", async () => {
  192. await deployContract();
  193. let result;
  194. await updateGuardianSets(pythTest, deployer);
  195. const updateData = Buffer.from(HERMES_BTC_ETH_UPDATE, "hex");
  196. const updateFee = await pythTest.getUpdateFee(updateData);
  197. result = await pythTest.sendUpdatePriceFeeds(
  198. deployer.getSender(),
  199. updateData,
  200. toNano(updateFee)
  201. );
  202. expect(result.transactions).toHaveTransaction({
  203. from: deployer.address,
  204. to: pythTest.address,
  205. success: true,
  206. });
  207. // Check if the price has been updated correctly
  208. const updatedPrice = await pythTest.getPriceUnsafe(BTC_PRICE_FEED_ID);
  209. expect(updatedPrice.price).not.toBe(Number(PRICE.price)); // Since we updated the price, it should not be the same as the initial price
  210. expect(updatedPrice.publishTime).toBeGreaterThan(PRICE.publishTime);
  211. });
  212. it("should fail to get update fee with invalid data", async () => {
  213. await deployContract();
  214. await updateGuardianSets(pythTest, deployer);
  215. const invalidUpdateData = Buffer.from("invalid data");
  216. await expect(pythTest.getUpdateFee(invalidUpdateData)).rejects.toThrow(
  217. "Unable to execute get method. Got exit_code: 1021"
  218. ); // ERROR_INVALID_MAGIC = 1021
  219. });
  220. it("should fail to update price feeds with invalid data", async () => {
  221. await deployContract();
  222. await updateGuardianSets(pythTest, deployer);
  223. const invalidUpdateData = Buffer.from("invalid data");
  224. // Use a fixed value for updateFee since we can't get it from getUpdateFee
  225. const updateFee = toNano("0.1"); // Use a reasonable amount
  226. const result = await pythTest.sendUpdatePriceFeeds(
  227. deployer.getSender(),
  228. invalidUpdateData,
  229. updateFee
  230. );
  231. expect(result.transactions).toHaveTransaction({
  232. from: deployer.address,
  233. to: pythTest.address,
  234. success: false,
  235. exitCode: 1021, // ERROR_INVALID_MAGIC
  236. });
  237. });
  238. it("should fail to update price feeds with outdated guardian set", async () => {
  239. await deployContract();
  240. // Don't update guardian sets
  241. const updateData = Buffer.from(HERMES_BTC_ETH_UPDATE, "hex");
  242. const updateFee = await pythTest.getUpdateFee(updateData);
  243. const result = await pythTest.sendUpdatePriceFeeds(
  244. deployer.getSender(),
  245. updateData,
  246. toNano(updateFee)
  247. );
  248. expect(result.transactions).toHaveTransaction({
  249. from: deployer.address,
  250. to: pythTest.address,
  251. success: false,
  252. exitCode: 1002, // ERROR_GUARDIAN_SET_NOT_FOUND
  253. });
  254. });
  255. it("should fail to update price feeds with invalid data source", async () => {
  256. await deployContract(
  257. BTC_PRICE_FEED_ID,
  258. TIME_PERIOD,
  259. PRICE,
  260. EMA_PRICE,
  261. SINGLE_UPDATE_FEE,
  262. [] // Empty data sources
  263. );
  264. await updateGuardianSets(pythTest, deployer);
  265. const updateData = Buffer.from(HERMES_BTC_ETH_UPDATE, "hex");
  266. const updateFee = await pythTest.getUpdateFee(updateData);
  267. const result = await pythTest.sendUpdatePriceFeeds(
  268. deployer.getSender(),
  269. updateData,
  270. toNano(updateFee)
  271. );
  272. expect(result.transactions).toHaveTransaction({
  273. from: deployer.address,
  274. to: pythTest.address,
  275. success: false,
  276. exitCode: 1024, // ERROR_UPDATE_DATA_SOURCE_NOT_FOUND
  277. });
  278. });
  279. it("should fail to update price feeds with insufficient gas", async () => {
  280. await deployContract();
  281. await updateGuardianSets(pythTest, deployer);
  282. const updateData = Buffer.from(HERMES_BTC_ETH_UPDATE, "hex");
  283. const result = await pythTest.sendUpdatePriceFeeds(
  284. deployer.getSender(),
  285. updateData,
  286. toNano("0.1") // Insufficient gas
  287. );
  288. expect(result.transactions).toHaveTransaction({
  289. from: deployer.address,
  290. to: pythTest.address,
  291. success: false,
  292. exitCode: 1037, // ERROR_INSUFFICIENT_GAS
  293. });
  294. });
  295. it("should fail to update price feeds with insufficient fee", async () => {
  296. await deployContract();
  297. await updateGuardianSets(pythTest, deployer);
  298. const updateData = Buffer.from(HERMES_BTC_ETH_UPDATE, "hex");
  299. const updateFee = await pythTest.getUpdateFee(updateData);
  300. // Send less than the required fee
  301. const insufficientFee = updateFee - 1;
  302. const result = await pythTest.sendUpdatePriceFeeds(
  303. deployer.getSender(),
  304. updateData,
  305. 156000000n + BigInt(insufficientFee) // 156000000 = 390000 (estimated gas used for the transaction, this is defined in contracts/common/gas.fc as UPDATE_PRICE_FEEDS_GAS) * 400 (current settings in basechain are as follows: 1 unit of gas costs 400 nanotons)
  306. );
  307. // Check that the transaction did not succeed
  308. expect(result.transactions).toHaveTransaction({
  309. from: deployer.address,
  310. to: pythTest.address,
  311. success: false,
  312. exitCode: 1030, // ERROR_INSUFFICIENT_FEE = 1030
  313. });
  314. });
  315. it("should fail to get price for non-existent price feed", async () => {
  316. await deployContract();
  317. const nonExistentPriceFeedId =
  318. "0000000000000000000000000000000000000000000000000000000000000000";
  319. await expect(
  320. pythTest.getPriceUnsafe(nonExistentPriceFeedId)
  321. ).rejects.toThrow("Unable to execute get method. Got exit_code: 1019"); // ERROR_PRICE_FEED_NOT_FOUND = 1019
  322. });
  323. it("should correctly get chain ID", async () => {
  324. await deployContract();
  325. const result = await pythTest.getChainId();
  326. expect(result).toEqual(1);
  327. });
  328. it("should correctly get last executed governance sequence", async () => {
  329. await deployContract(
  330. BTC_PRICE_FEED_ID,
  331. TIME_PERIOD,
  332. PRICE,
  333. EMA_PRICE,
  334. SINGLE_UPDATE_FEE,
  335. DATA_SOURCES,
  336. 0,
  337. [TEST_GUARDIAN_ADDRESS1],
  338. 60051,
  339. 1,
  340. "0000000000000000000000000000000000000000000000000000000000000004",
  341. TEST_GOVERNANCE_DATA_SOURCES[0]
  342. );
  343. // Check initial value
  344. let result = await pythTest.getLastExecutedGovernanceSequence();
  345. expect(result).toEqual(0);
  346. // Execute a governance action (e.g., set fee)
  347. await pythTest.sendExecuteGovernanceAction(
  348. deployer.getSender(),
  349. Buffer.from(PYTH_SET_FEE, "hex")
  350. );
  351. // Check that the sequence has increased
  352. result = await pythTest.getLastExecutedGovernanceSequence();
  353. expect(result).toEqual(1);
  354. });
  355. it("should correctly get governance data source index", async () => {
  356. // Deploy contract with initial governance data source
  357. await deployContract(
  358. BTC_PRICE_FEED_ID,
  359. TIME_PERIOD,
  360. PRICE,
  361. EMA_PRICE,
  362. SINGLE_UPDATE_FEE,
  363. DATA_SOURCES,
  364. 0,
  365. [TEST_GUARDIAN_ADDRESS1],
  366. 60051,
  367. 1,
  368. "0000000000000000000000000000000000000000000000000000000000000004",
  369. TEST_GOVERNANCE_DATA_SOURCES[0]
  370. );
  371. // Check initial value
  372. let result = await pythTest.getGovernanceDataSourceIndex();
  373. expect(result).toEqual(0);
  374. // Execute governance action to change data source
  375. await pythTest.sendExecuteGovernanceAction(
  376. deployer.getSender(),
  377. Buffer.from(PYTH_AUTHORIZE_GOVERNANCE_DATA_SOURCE_TRANSFER, "hex")
  378. );
  379. // Check that the index has increased
  380. result = await pythTest.getGovernanceDataSourceIndex();
  381. expect(result).toEqual(1);
  382. });
  383. it("should correctly get governance data source", async () => {
  384. // Deploy contract without initial governance data source
  385. await deployContract();
  386. // Check initial value (should be empty)
  387. let result = await pythTest.getGovernanceDataSource();
  388. expect(result).toBeDefined();
  389. expect(result.bits.length).toBe(0);
  390. expect(result.refs.length).toBe(0);
  391. // Deploy contract with initial governance data source
  392. await deployContract(
  393. BTC_PRICE_FEED_ID,
  394. TIME_PERIOD,
  395. PRICE,
  396. EMA_PRICE,
  397. SINGLE_UPDATE_FEE,
  398. DATA_SOURCES,
  399. 0,
  400. [TEST_GUARDIAN_ADDRESS1],
  401. 60051,
  402. 1,
  403. "0000000000000000000000000000000000000000000000000000000000000004",
  404. TEST_GOVERNANCE_DATA_SOURCES[0]
  405. );
  406. // Check that the governance data source is set
  407. result = await pythTest.getGovernanceDataSource();
  408. let dataSource = parseDataSource(result);
  409. expect(dataSource).toEqual(TEST_GOVERNANCE_DATA_SOURCES[0]);
  410. // Execute governance action to change data source
  411. await pythTest.sendExecuteGovernanceAction(
  412. deployer.getSender(),
  413. Buffer.from(PYTH_AUTHORIZE_GOVERNANCE_DATA_SOURCE_TRANSFER, "hex")
  414. );
  415. // Check that the data source has changed
  416. result = await pythTest.getGovernanceDataSource();
  417. dataSource = parseDataSource(result);
  418. expect(dataSource).toEqual(TEST_GOVERNANCE_DATA_SOURCES[1]);
  419. });
  420. it("should correctly get single update fee", async () => {
  421. await deployContract();
  422. // Get the initial fee
  423. const result = await pythTest.getSingleUpdateFee();
  424. expect(result).toBe(SINGLE_UPDATE_FEE);
  425. });
  426. it("should execute set fee governance instruction", async () => {
  427. await deployContract(
  428. BTC_PRICE_FEED_ID,
  429. TIME_PERIOD,
  430. PRICE,
  431. EMA_PRICE,
  432. SINGLE_UPDATE_FEE,
  433. DATA_SOURCES,
  434. 0,
  435. [TEST_GUARDIAN_ADDRESS1],
  436. 60051, // CHAIN_ID of starknet since we are using the test payload for starknet
  437. 1,
  438. "0000000000000000000000000000000000000000000000000000000000000004",
  439. TEST_GOVERNANCE_DATA_SOURCES[0]
  440. );
  441. // Execute the governance action
  442. const result = await pythTest.sendExecuteGovernanceAction(
  443. deployer.getSender(),
  444. Buffer.from(PYTH_SET_DATA_SOURCES, "hex")
  445. );
  446. expect(result.transactions).toHaveTransaction({
  447. from: deployer.address,
  448. to: pythTest.address,
  449. success: true,
  450. });
  451. // Verify that the new data sources are set correctly
  452. const newDataSources: DataSource[] = [
  453. {
  454. emitterChain: 1,
  455. emitterAddress:
  456. "6bb14509a612f01fbbc4cffeebd4bbfb492a86df717ebe92eb6df432a3f00a25",
  457. },
  458. {
  459. emitterChain: 3,
  460. emitterAddress:
  461. "000000000000000000000000000000000000000000000000000000000000012d",
  462. },
  463. ];
  464. for (const dataSource of newDataSources) {
  465. const isValid = await pythTest.getIsValidDataSource(dataSource);
  466. expect(isValid).toBe(true);
  467. }
  468. // Verify that the old data source is no longer valid
  469. const oldDataSource = DATA_SOURCES[0];
  470. const oldDataSourceIsValid = await pythTest.getIsValidDataSource(
  471. oldDataSource
  472. );
  473. expect(oldDataSourceIsValid).toBe(false);
  474. });
  475. it("should execute authorize governance data source transfer", async () => {
  476. await deployContract(
  477. BTC_PRICE_FEED_ID,
  478. TIME_PERIOD,
  479. PRICE,
  480. EMA_PRICE,
  481. SINGLE_UPDATE_FEE,
  482. DATA_SOURCES,
  483. 0,
  484. [TEST_GUARDIAN_ADDRESS1],
  485. 60051, // CHAIN_ID of starknet since we are using the test payload for starknet
  486. 1,
  487. "0000000000000000000000000000000000000000000000000000000000000004",
  488. TEST_GOVERNANCE_DATA_SOURCES[0]
  489. );
  490. // Get the initial governance data source index
  491. const initialIndex = await pythTest.getGovernanceDataSourceIndex();
  492. expect(initialIndex).toEqual(0); // Initial value should be 0
  493. // Get the initial governance data source
  494. const initialDataSourceCell = await pythTest.getGovernanceDataSource();
  495. const initialDataSource = parseDataSource(initialDataSourceCell);
  496. expect(initialDataSource).toEqual(TEST_GOVERNANCE_DATA_SOURCES[0]);
  497. // Get the initial last executed governance sequence
  498. const initialSequence = await pythTest.getLastExecutedGovernanceSequence();
  499. expect(initialSequence).toEqual(0); // Initial value should be 0
  500. // Execute the governance action
  501. const result = await pythTest.sendExecuteGovernanceAction(
  502. deployer.getSender(),
  503. Buffer.from(PYTH_AUTHORIZE_GOVERNANCE_DATA_SOURCE_TRANSFER, "hex")
  504. );
  505. expect(result.transactions).toHaveTransaction({
  506. from: deployer.address,
  507. to: pythTest.address,
  508. success: true,
  509. });
  510. // Get the new governance data source index
  511. const newIndex = await pythTest.getGovernanceDataSourceIndex();
  512. expect(newIndex).toEqual(1); // The new index value should match the one in the test payload
  513. // Get the new governance data source
  514. const newDataSourceCell = await pythTest.getGovernanceDataSource();
  515. const newDataSource = parseDataSource(newDataSourceCell);
  516. expect(newDataSource).not.toEqual(initialDataSource); // The data source should have changed
  517. expect(newDataSource).toEqual(TEST_GOVERNANCE_DATA_SOURCES[1]); // The data source should have changed
  518. // Get the new last executed governance sequence
  519. const newSequence = await pythTest.getLastExecutedGovernanceSequence();
  520. expect(newSequence).toBeGreaterThan(initialSequence); // The sequence should have increased
  521. expect(newSequence).toBe(1);
  522. });
  523. it("should fail when executing request governance data source transfer directly", async () => {
  524. await deployContract(
  525. BTC_PRICE_FEED_ID,
  526. TIME_PERIOD,
  527. PRICE,
  528. EMA_PRICE,
  529. SINGLE_UPDATE_FEE,
  530. DATA_SOURCES,
  531. 0,
  532. [TEST_GUARDIAN_ADDRESS1],
  533. 60051, // CHAIN_ID of starknet since we are using the test payload for starknet
  534. 1,
  535. "0000000000000000000000000000000000000000000000000000000000000004",
  536. TEST_GOVERNANCE_DATA_SOURCES[1]
  537. );
  538. const result = await pythTest.sendExecuteGovernanceAction(
  539. deployer.getSender(),
  540. Buffer.from(PYTH_REQUEST_GOVERNANCE_DATA_SOURCE_TRANSFER, "hex")
  541. );
  542. // Check that the transaction did not succeed
  543. expect(result.transactions).toHaveTransaction({
  544. from: deployer.address,
  545. to: pythTest.address,
  546. success: false,
  547. exitCode: 1012, // ERROR_INVALID_GOVERNANCE_ACTION = 1012
  548. });
  549. // Verify that the governance data source index hasn't changed
  550. const index = await pythTest.getGovernanceDataSourceIndex();
  551. expect(index).toEqual(0); // Should still be the initial value
  552. // Verify that the governance data source hasn't changed
  553. const dataSourceCell = await pythTest.getGovernanceDataSource();
  554. const dataSource = parseDataSource(dataSourceCell);
  555. expect(dataSource).toEqual(TEST_GOVERNANCE_DATA_SOURCES[1]); // Should still be the initial value
  556. });
  557. it("should fail to execute governance action with invalid governance data source", async () => {
  558. await deployContract(
  559. BTC_PRICE_FEED_ID,
  560. TIME_PERIOD,
  561. PRICE,
  562. EMA_PRICE,
  563. SINGLE_UPDATE_FEE,
  564. DATA_SOURCES,
  565. 0,
  566. [TEST_GUARDIAN_ADDRESS1],
  567. 60051,
  568. 1,
  569. "0000000000000000000000000000000000000000000000000000000000000004",
  570. TEST_GOVERNANCE_DATA_SOURCES[1]
  571. );
  572. const result = await pythTest.sendExecuteGovernanceAction(
  573. deployer.getSender(),
  574. Buffer.from(PYTH_SET_FEE, "hex")
  575. );
  576. expect(result.transactions).toHaveTransaction({
  577. from: deployer.address,
  578. to: pythTest.address,
  579. success: false,
  580. exitCode: 1032, // ERROR_INVALID_GOVERNANCE_DATA_SOURCE
  581. });
  582. });
  583. it("should fail to execute governance action with old sequence number", async () => {
  584. await deployContract(
  585. BTC_PRICE_FEED_ID,
  586. TIME_PERIOD,
  587. PRICE,
  588. EMA_PRICE,
  589. SINGLE_UPDATE_FEE,
  590. DATA_SOURCES,
  591. 0,
  592. [TEST_GUARDIAN_ADDRESS1],
  593. 60051,
  594. 1,
  595. "0000000000000000000000000000000000000000000000000000000000000004",
  596. TEST_GOVERNANCE_DATA_SOURCES[0]
  597. );
  598. // Execute a governance action to increase the sequence number
  599. await pythTest.sendExecuteGovernanceAction(
  600. deployer.getSender(),
  601. Buffer.from(PYTH_SET_FEE, "hex")
  602. );
  603. // Try to execute the same governance action again
  604. const result = await pythTest.sendExecuteGovernanceAction(
  605. deployer.getSender(),
  606. Buffer.from(PYTH_SET_FEE, "hex")
  607. );
  608. expect(result.transactions).toHaveTransaction({
  609. from: deployer.address,
  610. to: pythTest.address,
  611. success: false,
  612. exitCode: 1033, // ERROR_OLD_GOVERNANCE_MESSAGE
  613. });
  614. });
  615. });