PythGovernance.t.sol 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706
  1. // SPDX-License-Identifier: Apache 2
  2. pragma solidity ^0.8.0;
  3. import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
  4. import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
  5. import "forge-std/Test.sol";
  6. import "@pythnetwork/pyth-sdk-solidity/IPyth.sol";
  7. import "@pythnetwork/pyth-sdk-solidity/PythErrors.sol";
  8. import "@pythnetwork/pyth-sdk-solidity/PythStructs.sol";
  9. import "../contracts/pyth/PythInternalStructs.sol";
  10. import "../contracts/pyth/PythGovernanceInstructions.sol";
  11. import "../contracts/pyth/PythUpgradable.sol";
  12. import "../contracts/pyth/PythGetters.sol";
  13. import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
  14. import "../contracts/wormhole/interfaces/IWormhole.sol";
  15. import "../contracts/wormhole/Implementation.sol";
  16. import "../contracts/wormhole/Setup.sol";
  17. import "../contracts/wormhole/Wormhole.sol";
  18. import "../contracts/wormhole-receiver/WormholeReceiver.sol";
  19. import "../contracts/wormhole-receiver/ReceiverImplementation.sol";
  20. import "../contracts/wormhole-receiver/ReceiverSetup.sol";
  21. import "../contracts/wormhole-receiver/ReceiverGovernanceStructs.sol";
  22. import "../contracts/wormhole-receiver/ReceiverStructs.sol";
  23. import "../contracts/wormhole-receiver/ReceiverGovernance.sol";
  24. import "../contracts/libraries/external/BytesLib.sol";
  25. import "../contracts/pyth/mock/MockUpgradeableProxy.sol";
  26. import "./utils/WormholeTestUtils.t.sol";
  27. import "./utils/PythTestUtils.t.sol";
  28. import "./utils/RandTestUtils.t.sol";
  29. contract PythGovernanceTest is
  30. Test,
  31. WormholeTestUtils,
  32. PythTestUtils,
  33. PythGovernanceInstructions
  34. {
  35. using BytesLib for bytes;
  36. IPyth public pyth;
  37. address constant TEST_SIGNER1 = 0xbeFA429d57cD18b7F8A4d91A2da9AB4AF05d0FBe;
  38. address constant TEST_SIGNER2 = 0x4ba0C2db9A26208b3bB1a50B01b16941c10D76db;
  39. uint16 constant TEST_GOVERNANCE_CHAIN_ID = 1;
  40. bytes32 constant TEST_GOVERNANCE_EMITTER =
  41. 0x0000000000000000000000000000000000000000000000000000000000000011;
  42. uint16 constant TEST_PYTH2_WORMHOLE_CHAIN_ID = 1;
  43. bytes32 constant TEST_PYTH2_WORMHOLE_EMITTER =
  44. 0x71f8dcb863d176e2c420ad6610cf687359612b6fb392e0642b0ca6b1f186aa3b;
  45. uint16 constant TARGET_CHAIN_ID = 2;
  46. function setUp() public {
  47. pyth = IPyth(setUpPyth(setUpWormholeReceiver(1)));
  48. }
  49. function testNoOwner() public {
  50. // Check that the ownership is renounced
  51. assertEq(OwnableUpgradeable(address(pyth)).owner(), address(0));
  52. }
  53. function testValidDataSources() public {
  54. assertTrue(
  55. PythGetters(address(pyth)).isValidDataSource(
  56. TEST_PYTH2_WORMHOLE_CHAIN_ID,
  57. TEST_PYTH2_WORMHOLE_EMITTER
  58. )
  59. );
  60. }
  61. function testSetFee() public {
  62. // Set fee to 5000 (5000 = 5 * 10^3)
  63. bytes memory setFeeMessage = abi.encodePacked(
  64. MAGIC,
  65. uint8(GovernanceModule.Target),
  66. uint8(GovernanceAction.SetFee),
  67. TARGET_CHAIN_ID,
  68. uint64(5), // value
  69. uint64(3) // exponent
  70. );
  71. bytes memory vaa = encodeAndSignMessage(
  72. setFeeMessage,
  73. TEST_GOVERNANCE_CHAIN_ID,
  74. TEST_GOVERNANCE_EMITTER,
  75. 1
  76. );
  77. uint oldFee = PythGetters(address(pyth)).singleUpdateFeeInWei();
  78. vm.expectEmit(true, true, true, true);
  79. emit FeeSet(oldFee, 5000);
  80. PythGovernance(address(pyth)).executeGovernanceInstruction(vaa);
  81. assertEq(PythGetters(address(pyth)).singleUpdateFeeInWei(), 5000);
  82. }
  83. function testSetValidPeriod() public {
  84. // Create governance VAA to set valid period to 0
  85. bytes memory data = abi.encodePacked(
  86. MAGIC,
  87. uint8(GovernanceModule.Target),
  88. uint8(GovernanceAction.SetValidPeriod),
  89. TARGET_CHAIN_ID, // Target chain ID
  90. uint64(0) // New valid period
  91. );
  92. bytes memory vaa = encodeAndSignMessage(
  93. data,
  94. TEST_GOVERNANCE_CHAIN_ID,
  95. TEST_GOVERNANCE_EMITTER,
  96. 1
  97. );
  98. uint oldValidPeriod = PythGetters(address(pyth))
  99. .validTimePeriodSeconds();
  100. vm.expectEmit(true, true, true, true);
  101. emit ValidPeriodSet(oldValidPeriod, 0);
  102. PythGovernance(address(pyth)).executeGovernanceInstruction(vaa);
  103. assertEq(PythGetters(address(pyth)).validTimePeriodSeconds(), 0);
  104. }
  105. function testInvalidGovernanceMessage() public {
  106. // Test with wrong magic number
  107. bytes memory data = abi.encodePacked(
  108. bytes4(0x12345678), // Wrong magic
  109. uint8(GovernanceModule.Target),
  110. uint8(GovernanceAction.SetValidPeriod),
  111. uint16(1), // Target chain ID
  112. uint64(0)
  113. );
  114. bytes memory vaa = encodeAndSignMessage(
  115. data,
  116. TEST_GOVERNANCE_CHAIN_ID,
  117. TEST_GOVERNANCE_EMITTER,
  118. 1
  119. );
  120. vm.expectRevert(PythErrors.InvalidGovernanceMessage.selector);
  121. PythGovernance(address(pyth)).executeGovernanceInstruction(vaa);
  122. }
  123. function testInvalidGovernanceTarget() public {
  124. // Test with wrong chain target
  125. bytes memory data = abi.encodePacked(
  126. MAGIC,
  127. uint8(GovernanceModule.Target),
  128. uint8(GovernanceAction.SetValidPeriod),
  129. uint16(3), // Different chain ID for testing invalid target
  130. uint64(0)
  131. );
  132. bytes memory vaa = encodeAndSignMessage(
  133. data,
  134. TEST_GOVERNANCE_CHAIN_ID,
  135. TEST_GOVERNANCE_EMITTER,
  136. 1
  137. );
  138. vm.expectRevert(PythErrors.InvalidGovernanceTarget.selector);
  139. PythGovernance(address(pyth)).executeGovernanceInstruction(vaa);
  140. }
  141. function testInvalidGovernanceDataSource() public {
  142. // Test with wrong emitter
  143. bytes memory data = abi.encodePacked(
  144. MAGIC,
  145. uint8(GovernanceModule.Target),
  146. uint8(GovernanceAction.SetValidPeriod),
  147. TARGET_CHAIN_ID,
  148. uint64(0)
  149. );
  150. bytes memory vaa = encodeAndSignMessage(
  151. data,
  152. TEST_GOVERNANCE_CHAIN_ID,
  153. bytes32(uint256(0x1111)), // Wrong emitter
  154. 1
  155. );
  156. vm.expectRevert(PythErrors.InvalidGovernanceDataSource.selector);
  157. PythGovernance(address(pyth)).executeGovernanceInstruction(vaa);
  158. }
  159. function testSetDataSources() public {
  160. // Create governance VAA to set new data sources
  161. bytes memory data = abi.encodePacked(
  162. MAGIC,
  163. uint8(GovernanceModule.Target),
  164. uint8(GovernanceAction.SetDataSources),
  165. TARGET_CHAIN_ID, // Target chain ID
  166. uint8(1), // Number of data sources
  167. uint16(1), // Chain ID
  168. bytes32(
  169. 0x0000000000000000000000000000000000000000000000000000000000001111
  170. ) // Emitter
  171. );
  172. bytes memory vaa = encodeAndSignMessage(
  173. data,
  174. TEST_GOVERNANCE_CHAIN_ID,
  175. TEST_GOVERNANCE_EMITTER,
  176. 1
  177. );
  178. PythInternalStructs.DataSource[] memory oldDataSources = PythGetters(
  179. address(pyth)
  180. ).validDataSources();
  181. PythInternalStructs.DataSource[]
  182. memory newDataSources = new PythInternalStructs.DataSource[](1);
  183. newDataSources[0] = PythInternalStructs.DataSource(
  184. 1,
  185. 0x0000000000000000000000000000000000000000000000000000000000001111
  186. );
  187. vm.expectEmit(true, true, true, true);
  188. emit DataSourcesSet(oldDataSources, newDataSources);
  189. PythGovernance(address(pyth)).executeGovernanceInstruction(vaa);
  190. // Verify old data source is no longer valid
  191. assertFalse(
  192. PythGetters(address(pyth)).isValidDataSource(
  193. TEST_PYTH2_WORMHOLE_CHAIN_ID,
  194. TEST_PYTH2_WORMHOLE_EMITTER
  195. )
  196. );
  197. // Verify new data source is valid
  198. assertTrue(
  199. PythGetters(address(pyth)).isValidDataSource(
  200. 1,
  201. 0x0000000000000000000000000000000000000000000000000000000000001111
  202. )
  203. );
  204. }
  205. function testSetWormholeAddress() public {
  206. // Deploy a new wormhole contract
  207. address newWormhole = address(setUpWormholeReceiver(1));
  208. // Create governance VAA to set new wormhole address
  209. bytes memory data = abi.encodePacked(
  210. MAGIC,
  211. uint8(GovernanceModule.Target),
  212. uint8(GovernanceAction.SetWormholeAddress),
  213. TARGET_CHAIN_ID, // Target chain ID
  214. newWormhole // New wormhole address
  215. );
  216. bytes memory vaa = encodeAndSignMessage(
  217. data,
  218. TEST_GOVERNANCE_CHAIN_ID,
  219. TEST_GOVERNANCE_EMITTER,
  220. 1
  221. );
  222. address oldWormhole = address(PythGetters(address(pyth)).wormhole());
  223. vm.expectEmit(true, true, true, true);
  224. emit WormholeAddressSet(oldWormhole, newWormhole);
  225. PythGovernance(address(pyth)).executeGovernanceInstruction(vaa);
  226. assertEq(address(PythGetters(address(pyth)).wormhole()), newWormhole);
  227. }
  228. function testTransferGovernanceDataSource() public {
  229. uint16 newEmitterChain = 2;
  230. bytes32 newEmitterAddress = 0x0000000000000000000000000000000000000000000000000000000000001111;
  231. // Create claim VAA from new governance
  232. bytes memory claimData = abi.encodePacked(
  233. MAGIC,
  234. uint8(GovernanceModule.Target),
  235. uint8(GovernanceAction.RequestGovernanceDataSourceTransfer),
  236. TARGET_CHAIN_ID, // Target chain ID
  237. uint32(1) // New governance index
  238. );
  239. bytes memory claimVaa = encodeAndSignMessage(
  240. claimData,
  241. newEmitterChain,
  242. newEmitterAddress,
  243. 1
  244. );
  245. // Create authorize VAA from current governance
  246. bytes memory authData = abi.encodePacked(
  247. MAGIC,
  248. uint8(GovernanceModule.Target),
  249. uint8(GovernanceAction.AuthorizeGovernanceDataSourceTransfer),
  250. TARGET_CHAIN_ID, // Target chain ID
  251. claimVaa
  252. );
  253. bytes memory authVaa = encodeAndSignMessage(
  254. authData,
  255. TEST_GOVERNANCE_CHAIN_ID,
  256. TEST_GOVERNANCE_EMITTER,
  257. 1
  258. );
  259. PythInternalStructs.DataSource
  260. memory oldDataSource = PythInternalStructs.DataSource(
  261. TEST_GOVERNANCE_CHAIN_ID,
  262. TEST_GOVERNANCE_EMITTER
  263. );
  264. PythInternalStructs.DataSource
  265. memory newDataSource = PythInternalStructs.DataSource(
  266. newEmitterChain,
  267. newEmitterAddress
  268. );
  269. vm.expectEmit(true, true, true, true);
  270. emit GovernanceDataSourceSet(oldDataSource, newDataSource, 1);
  271. PythGovernance(address(pyth)).executeGovernanceInstruction(authVaa);
  272. // Verify old governance can't execute instructions anymore
  273. bytes memory invalidData = abi.encodePacked(
  274. MAGIC,
  275. uint8(GovernanceModule.Target),
  276. uint8(GovernanceAction.SetValidPeriod),
  277. uint16(1), // Wrong chain ID for testing invalid target
  278. uint64(0)
  279. );
  280. bytes memory invalidVaa = encodeAndSignMessage(
  281. invalidData,
  282. TEST_GOVERNANCE_CHAIN_ID,
  283. TEST_GOVERNANCE_EMITTER,
  284. 2
  285. );
  286. vm.expectRevert(PythErrors.InvalidGovernanceDataSource.selector);
  287. PythGovernance(address(pyth)).executeGovernanceInstruction(invalidVaa);
  288. }
  289. function testSequentialGovernanceMessages() public {
  290. // First governance message
  291. bytes memory data1 = abi.encodePacked(
  292. MAGIC,
  293. uint8(GovernanceModule.Target),
  294. uint8(GovernanceAction.SetValidPeriod),
  295. TARGET_CHAIN_ID, // Target chain ID
  296. uint64(10)
  297. );
  298. bytes memory vaa1 = encodeAndSignMessage(
  299. data1,
  300. TEST_GOVERNANCE_CHAIN_ID,
  301. TEST_GOVERNANCE_EMITTER,
  302. 1
  303. );
  304. PythGovernance(address(pyth)).executeGovernanceInstruction(vaa1);
  305. // Second governance message
  306. bytes memory data2 = abi.encodePacked(
  307. MAGIC,
  308. uint8(GovernanceModule.Target),
  309. uint8(GovernanceAction.SetValidPeriod),
  310. TARGET_CHAIN_ID, // Target chain ID
  311. uint64(20)
  312. );
  313. bytes memory vaa2 = encodeAndSignMessage(
  314. data2,
  315. TEST_GOVERNANCE_CHAIN_ID,
  316. TEST_GOVERNANCE_EMITTER,
  317. 2
  318. );
  319. PythGovernance(address(pyth)).executeGovernanceInstruction(vaa2);
  320. // Try to replay first message
  321. vm.expectRevert(PythErrors.OldGovernanceMessage.selector);
  322. PythGovernance(address(pyth)).executeGovernanceInstruction(vaa1);
  323. // Try to replay second message
  324. vm.expectRevert(PythErrors.OldGovernanceMessage.selector);
  325. PythGovernance(address(pyth)).executeGovernanceInstruction(vaa2);
  326. }
  327. function testUpgradeContractWithChainIdZeroIsInvalid() public {
  328. // Deploy a new PythUpgradable contract
  329. PythUpgradable newImplementation = new PythUpgradable();
  330. // Create governance VAA with chain ID 0 (unset)
  331. bytes memory data = abi.encodePacked(
  332. MAGIC,
  333. uint8(GovernanceModule.Target),
  334. uint8(GovernanceAction.UpgradeContract),
  335. uint16(0), // Chain ID 0 (unset)
  336. address(newImplementation) // New implementation address
  337. );
  338. bytes memory vaa = encodeAndSignMessage(
  339. data,
  340. TEST_GOVERNANCE_CHAIN_ID,
  341. TEST_GOVERNANCE_EMITTER,
  342. 1
  343. );
  344. // Should revert with InvalidGovernanceTarget
  345. vm.expectRevert(PythErrors.InvalidGovernanceTarget.selector);
  346. PythGovernance(address(pyth)).executeGovernanceInstruction(vaa);
  347. }
  348. // Helper function to get the second address from event data
  349. function getSecondAddressFromEventData(
  350. bytes memory data
  351. ) internal pure returns (address) {
  352. (, address secondAddr) = abi.decode(data, (address, address));
  353. return secondAddr;
  354. }
  355. function testUpgradeContractShouldWork() public {
  356. // Deploy a new PythUpgradable contract
  357. PythUpgradable newImplementation = new PythUpgradable();
  358. // Create governance VAA to upgrade the contract
  359. bytes memory data = abi.encodePacked(
  360. MAGIC,
  361. uint8(GovernanceModule.Target),
  362. uint8(GovernanceAction.UpgradeContract),
  363. TARGET_CHAIN_ID, // Valid target chain ID
  364. address(newImplementation) // New implementation address
  365. );
  366. bytes memory vaa = encodeAndSignMessage(
  367. data,
  368. TEST_GOVERNANCE_CHAIN_ID,
  369. TEST_GOVERNANCE_EMITTER,
  370. 1
  371. );
  372. // Create a custom event checker for ContractUpgraded event
  373. // Since we only care about the newImplementation parameter
  374. vm.recordLogs();
  375. // Execute the governance instruction
  376. PythGovernance(address(pyth)).executeGovernanceInstruction(vaa);
  377. // Get emitted logs and check the event parameters
  378. Vm.Log[] memory entries = vm.getRecordedLogs();
  379. bool foundUpgradeEvent = false;
  380. for (uint i = 0; i < entries.length; i++) {
  381. // The event signature for ContractUpgraded
  382. bytes32 eventSignature = keccak256(
  383. "ContractUpgraded(address,address)"
  384. );
  385. if (entries[i].topics[0] == eventSignature) {
  386. // This is a ContractUpgraded event
  387. // Get just the new implementation address using our helper
  388. address recordedNewImplementation = getSecondAddressFromEventData(
  389. entries[i].data
  390. );
  391. // Check newImplementation
  392. assertEq(recordedNewImplementation, address(newImplementation));
  393. foundUpgradeEvent = true;
  394. break;
  395. }
  396. }
  397. // Make sure we found the event
  398. assertTrue(foundUpgradeEvent, "ContractUpgraded event not found");
  399. // Verify the upgrade worked by checking the magic number
  400. assertEq(
  401. PythUpgradable(address(pyth)).pythUpgradableMagic(),
  402. 0x97a6f304
  403. );
  404. // Verify the implementation was upgraded to our new implementation
  405. // Access implementation using the ERC1967 storage slot
  406. address implAddr = address(
  407. uint160(
  408. uint256(
  409. vm.load(
  410. address(pyth),
  411. 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc // ERC1967 implementation slot
  412. )
  413. )
  414. )
  415. );
  416. assertEq(implAddr, address(newImplementation));
  417. }
  418. function testUpgradeContractToNonPythContractWontWork() public {
  419. // Deploy a mock upgradeable proxy that isn't a proper Pyth implementation
  420. MockUpgradeableProxy newImplementation = new MockUpgradeableProxy();
  421. // Create governance VAA to upgrade to an invalid implementation
  422. bytes memory data = abi.encodePacked(
  423. MAGIC,
  424. uint8(GovernanceModule.Target),
  425. uint8(GovernanceAction.UpgradeContract),
  426. TARGET_CHAIN_ID, // Valid target chain ID
  427. address(newImplementation) // Invalid implementation address
  428. );
  429. bytes memory vaa = encodeAndSignMessage(
  430. data,
  431. TEST_GOVERNANCE_CHAIN_ID,
  432. TEST_GOVERNANCE_EMITTER,
  433. 1
  434. );
  435. // Should revert with no specific error message because the mock implementation
  436. // doesn't have the pythUpgradableMagic method
  437. vm.expectRevert();
  438. PythGovernance(address(pyth)).executeGovernanceInstruction(vaa);
  439. }
  440. function testSetTransactionFee() public {
  441. // Set transaction fee to 1000 (1000 = 1 * 10^3)
  442. bytes memory setTransactionFeeMessage = abi.encodePacked(
  443. MAGIC,
  444. uint8(GovernanceModule.Target),
  445. uint8(GovernanceAction.SetTransactionFee),
  446. TARGET_CHAIN_ID,
  447. uint64(1), // value
  448. uint64(3) // exponent
  449. );
  450. bytes memory vaa = encodeAndSignMessage(
  451. setTransactionFeeMessage,
  452. TEST_GOVERNANCE_CHAIN_ID,
  453. TEST_GOVERNANCE_EMITTER,
  454. 1
  455. );
  456. uint oldFee = PythGetters(address(pyth)).transactionFeeInWei();
  457. vm.expectEmit(true, true, true, true);
  458. emit TransactionFeeSet(oldFee, 1000);
  459. PythGovernance(address(pyth)).executeGovernanceInstruction(vaa);
  460. assertEq(PythGetters(address(pyth)).transactionFeeInWei(), 1000);
  461. // Test that update fee includes transaction fee
  462. bytes[] memory updateData = new bytes[](0);
  463. assertEq(pyth.getUpdateFee(updateData), 1000);
  464. // Test that insufficient fee reverts
  465. vm.expectRevert(PythErrors.InsufficientFee.selector);
  466. pyth.updatePriceFeeds{value: 999}(updateData);
  467. // Test that sufficient fee works
  468. pyth.updatePriceFeeds{value: 1000}(updateData);
  469. }
  470. function testWithdrawFee() public {
  471. // First send some ETH to the contract
  472. bytes[] memory updateData = new bytes[](0);
  473. pyth.updatePriceFeeds{value: 1 ether}(updateData);
  474. assertEq(address(pyth).balance, 1 ether);
  475. address recipient = makeAddr("recipient");
  476. // Create governance VAA to withdraw fee
  477. bytes memory withdrawMessage = abi.encodePacked(
  478. MAGIC,
  479. uint8(GovernanceModule.Target),
  480. uint8(GovernanceAction.WithdrawFee),
  481. TARGET_CHAIN_ID,
  482. recipient,
  483. uint64(5), // value = 5
  484. uint64(17) // exponent = 17 (5 * 10^17 = 0.5 ether)
  485. );
  486. bytes memory vaa = encodeAndSignMessage(
  487. withdrawMessage,
  488. TEST_GOVERNANCE_CHAIN_ID,
  489. TEST_GOVERNANCE_EMITTER,
  490. 1
  491. );
  492. vm.expectEmit(true, true, true, true);
  493. emit FeeWithdrawn(recipient, 0.5 ether);
  494. PythGovernance(address(pyth)).executeGovernanceInstruction(vaa);
  495. assertEq(address(pyth).balance, 0.5 ether);
  496. assertEq(recipient.balance, 0.5 ether);
  497. }
  498. function testWithdrawFeeInsufficientBalance() public {
  499. // First send some ETH to the contract
  500. bytes[] memory updateData = new bytes[](0);
  501. pyth.updatePriceFeeds{value: 1 ether}(updateData);
  502. assertEq(address(pyth).balance, 1 ether);
  503. address recipient = makeAddr("recipient");
  504. // Create governance VAA to withdraw fee
  505. bytes memory withdrawMessage = abi.encodePacked(
  506. MAGIC,
  507. uint8(GovernanceModule.Target),
  508. uint8(GovernanceAction.WithdrawFee),
  509. TARGET_CHAIN_ID,
  510. recipient,
  511. uint64(2), // value = 2
  512. uint64(18) // exponent = 18 (2 * 10^18 = 2 ether, more than balance)
  513. );
  514. bytes memory vaa = encodeAndSignMessage(
  515. withdrawMessage,
  516. TEST_GOVERNANCE_CHAIN_ID,
  517. TEST_GOVERNANCE_EMITTER,
  518. 1
  519. );
  520. vm.expectRevert(PythErrors.InsufficientFee.selector);
  521. PythGovernance(address(pyth)).executeGovernanceInstruction(vaa);
  522. // Balances should remain unchanged
  523. assertEq(address(pyth).balance, 1 ether);
  524. assertEq(recipient.balance, 0);
  525. }
  526. function testWithdrawFeeInvalidGovernance() public {
  527. address recipient = makeAddr("recipient");
  528. // Create governance VAA with wrong emitter
  529. bytes memory withdrawMessage = abi.encodePacked(
  530. MAGIC,
  531. uint8(GovernanceModule.Target),
  532. uint8(GovernanceAction.WithdrawFee),
  533. TARGET_CHAIN_ID,
  534. recipient,
  535. uint64(5), // value = 5
  536. uint64(17) // exponent = 17 (5 * 10^17 = 0.5 ether)
  537. );
  538. bytes memory vaa = encodeAndSignMessage(
  539. withdrawMessage,
  540. TEST_GOVERNANCE_CHAIN_ID,
  541. bytes32(uint256(0x1111)), // Wrong emitter
  542. 1
  543. );
  544. vm.expectRevert(PythErrors.InvalidGovernanceDataSource.selector);
  545. PythGovernance(address(pyth)).executeGovernanceInstruction(vaa);
  546. }
  547. function encodeAndSignWormholeMessage(
  548. bytes memory data,
  549. uint16 emitterChainId,
  550. bytes32 emitterAddress,
  551. uint64 sequence,
  552. uint8 numGuardians
  553. ) internal view returns (bytes memory) {
  554. return
  555. generateVaa(
  556. uint32(block.timestamp),
  557. emitterChainId,
  558. emitterAddress,
  559. sequence,
  560. data,
  561. numGuardians
  562. );
  563. }
  564. function encodeAndSignMessage(
  565. bytes memory data,
  566. uint16 emitterChainId,
  567. bytes32 emitterAddress,
  568. uint64 sequence
  569. ) internal view returns (bytes memory) {
  570. return
  571. encodeAndSignWormholeMessage(
  572. data,
  573. emitterChainId,
  574. emitterAddress,
  575. sequence,
  576. 1 // Number of guardians
  577. );
  578. }
  579. // Events
  580. event ContractUpgraded(
  581. address oldImplementation,
  582. address newImplementation
  583. );
  584. event GovernanceDataSourceSet(
  585. PythInternalStructs.DataSource oldDataSource,
  586. PythInternalStructs.DataSource newDataSource,
  587. uint64 initialSequence
  588. );
  589. event DataSourcesSet(
  590. PythInternalStructs.DataSource[] oldDataSources,
  591. PythInternalStructs.DataSource[] newDataSources
  592. );
  593. event FeeSet(uint oldFee, uint newFee);
  594. event ValidPeriodSet(uint oldValidPeriod, uint newValidPeriod);
  595. event WormholeAddressSet(
  596. address oldWormholeAddress,
  597. address newWormholeAddress
  598. );
  599. event TransactionFeeSet(uint oldFee, uint newFee);
  600. event FeeWithdrawn(address recipient, uint256 fee);
  601. }