PythGovernanceInstructions.sol 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. // contracts/GovernanceStructs.sol
  2. // SPDX-License-Identifier: Apache 2
  3. pragma solidity ^0.8.0;
  4. import "../libraries/external/BytesLib.sol";
  5. import "./PythInternalStructs.sol";
  6. import "@pythnetwork/pyth-sdk-solidity/PythErrors.sol";
  7. /**
  8. * @dev `PythGovernanceInstructions` defines a set of structs and parsing functions
  9. * for Pyth governance instructions.
  10. */
  11. contract PythGovernanceInstructions {
  12. using BytesLib for bytes;
  13. // Magic is `PTGM` encoded as a 4 byte data: Pyth Governance Message
  14. uint32 constant MAGIC = 0x5054474d;
  15. enum GovernanceModule {
  16. Executor, // 0
  17. Target, // 1
  18. EvmExecutor, // 2
  19. // The stacks target chain contract has custom governance instructions and needs its own module.
  20. StacksTarget // 3
  21. }
  22. GovernanceModule constant MODULE = GovernanceModule.Target;
  23. enum GovernanceAction {
  24. UpgradeContract, // 0
  25. AuthorizeGovernanceDataSourceTransfer, // 1
  26. SetDataSources, // 2
  27. SetFee, // 3
  28. SetValidPeriod, // 4
  29. RequestGovernanceDataSourceTransfer, // 5
  30. SetWormholeAddress, // 6
  31. SetFeeInToken, // 7 - No-op for EVM chains
  32. SetTransactionFee, // 8
  33. WithdrawFee // 9
  34. }
  35. struct GovernanceInstruction {
  36. GovernanceModule module;
  37. GovernanceAction action;
  38. uint16 targetChainId;
  39. bytes payload;
  40. }
  41. struct UpgradeContractPayload {
  42. address newImplementation;
  43. }
  44. struct AuthorizeGovernanceDataSourceTransferPayload {
  45. // Transfer governance control over this contract to another data source.
  46. // The claimVaa field is a VAA created by the new data source; using a VAA prevents mistakes
  47. // in the handoff by ensuring that the new data source can send VAAs (i.e., is not an invalid address).
  48. bytes claimVaa;
  49. }
  50. struct RequestGovernanceDataSourceTransferPayload {
  51. // Governance data source index is used to prevent replay attacks
  52. // So a claimVaa cannot be used twice.
  53. uint32 governanceDataSourceIndex;
  54. }
  55. struct SetDataSourcesPayload {
  56. PythInternalStructs.DataSource[] dataSources;
  57. }
  58. struct SetFeePayload {
  59. uint newFee;
  60. }
  61. struct SetValidPeriodPayload {
  62. uint newValidPeriod;
  63. }
  64. struct SetWormholeAddressPayload {
  65. address newWormholeAddress;
  66. }
  67. struct SetTransactionFeePayload {
  68. uint newFee;
  69. }
  70. struct WithdrawFeePayload {
  71. address targetAddress;
  72. // Fee in wei, matching the native uint256 type used for address.balance in EVM
  73. uint256 fee;
  74. }
  75. /// @dev Parse a GovernanceInstruction
  76. function parseGovernanceInstruction(
  77. bytes memory encodedInstruction
  78. ) public pure returns (GovernanceInstruction memory gi) {
  79. uint index = 0;
  80. uint32 magic = encodedInstruction.toUint32(index);
  81. if (magic != MAGIC) revert PythErrors.InvalidGovernanceMessage();
  82. index += 4;
  83. uint8 modNumber = encodedInstruction.toUint8(index);
  84. gi.module = GovernanceModule(modNumber);
  85. index += 1;
  86. if (gi.module != MODULE) revert PythErrors.InvalidGovernanceTarget();
  87. uint8 actionNumber = encodedInstruction.toUint8(index);
  88. gi.action = GovernanceAction(actionNumber);
  89. index += 1;
  90. gi.targetChainId = encodedInstruction.toUint16(index);
  91. index += 2;
  92. // As solidity performs math operations in a checked mode
  93. // if the length of the encoded instruction be smaller than index
  94. // it will revert. So we don't need any extra check.
  95. gi.payload = encodedInstruction.slice(
  96. index,
  97. encodedInstruction.length - index
  98. );
  99. }
  100. /// @dev Parse a UpgradeContractPayload (action 1) with minimal validation
  101. function parseUpgradeContractPayload(
  102. bytes memory encodedPayload
  103. ) public pure returns (UpgradeContractPayload memory uc) {
  104. uint index = 0;
  105. uc.newImplementation = address(encodedPayload.toAddress(index));
  106. index += 20;
  107. if (encodedPayload.length != index)
  108. revert PythErrors.InvalidGovernanceMessage();
  109. }
  110. /// @dev Parse a AuthorizeGovernanceDataSourceTransferPayload (action 2) with minimal validation
  111. function parseAuthorizeGovernanceDataSourceTransferPayload(
  112. bytes memory encodedPayload
  113. )
  114. public
  115. pure
  116. returns (AuthorizeGovernanceDataSourceTransferPayload memory sgds)
  117. {
  118. sgds.claimVaa = encodedPayload;
  119. }
  120. /// @dev Parse a AuthorizeGovernanceDataSourceTransferPayload (action 2) with minimal validation
  121. function parseRequestGovernanceDataSourceTransferPayload(
  122. bytes memory encodedPayload
  123. )
  124. public
  125. pure
  126. returns (RequestGovernanceDataSourceTransferPayload memory sgdsClaim)
  127. {
  128. uint index = 0;
  129. sgdsClaim.governanceDataSourceIndex = encodedPayload.toUint32(index);
  130. index += 4;
  131. if (encodedPayload.length != index)
  132. revert PythErrors.InvalidGovernanceMessage();
  133. }
  134. /// @dev Parse a SetDataSourcesPayload (action 3) with minimal validation
  135. function parseSetDataSourcesPayload(
  136. bytes memory encodedPayload
  137. ) public pure returns (SetDataSourcesPayload memory sds) {
  138. uint index = 0;
  139. uint8 dataSourcesLength = encodedPayload.toUint8(index);
  140. index += 1;
  141. sds.dataSources = new PythInternalStructs.DataSource[](
  142. dataSourcesLength
  143. );
  144. for (uint i = 0; i < dataSourcesLength; i++) {
  145. sds.dataSources[i].chainId = encodedPayload.toUint16(index);
  146. index += 2;
  147. sds.dataSources[i].emitterAddress = encodedPayload.toBytes32(index);
  148. index += 32;
  149. }
  150. if (encodedPayload.length != index)
  151. revert PythErrors.InvalidGovernanceMessage();
  152. }
  153. /// @dev Parse a SetFeePayload (action 4) with minimal validation
  154. function parseSetFeePayload(
  155. bytes memory encodedPayload
  156. ) public pure returns (SetFeePayload memory sf) {
  157. uint index = 0;
  158. uint64 val = encodedPayload.toUint64(index);
  159. index += 8;
  160. uint64 expo = encodedPayload.toUint64(index);
  161. index += 8;
  162. sf.newFee = uint256(val) * uint256(10) ** uint256(expo);
  163. if (encodedPayload.length != index)
  164. revert PythErrors.InvalidGovernanceMessage();
  165. }
  166. /// @dev Parse a SetValidPeriodPayload (action 5) with minimal validation
  167. function parseSetValidPeriodPayload(
  168. bytes memory encodedPayload
  169. ) public pure returns (SetValidPeriodPayload memory svp) {
  170. uint index = 0;
  171. svp.newValidPeriod = uint256(encodedPayload.toUint64(index));
  172. index += 8;
  173. if (encodedPayload.length != index)
  174. revert PythErrors.InvalidGovernanceMessage();
  175. }
  176. /// @dev Parse a UpdateWormholeAddressPayload (action 6) with minimal validation
  177. function parseSetWormholeAddressPayload(
  178. bytes memory encodedPayload
  179. ) public pure returns (SetWormholeAddressPayload memory sw) {
  180. uint index = 0;
  181. sw.newWormholeAddress = address(encodedPayload.toAddress(index));
  182. index += 20;
  183. if (encodedPayload.length != index)
  184. revert PythErrors.InvalidGovernanceMessage();
  185. }
  186. /// @dev Parse a SetTransactionFeePayload (action 8) with minimal validation
  187. function parseSetTransactionFeePayload(
  188. bytes memory encodedPayload
  189. ) public pure returns (SetTransactionFeePayload memory stf) {
  190. uint index = 0;
  191. uint64 val = encodedPayload.toUint64(index);
  192. index += 8;
  193. uint64 expo = encodedPayload.toUint64(index);
  194. index += 8;
  195. stf.newFee = uint256(val) * uint256(10) ** uint256(expo);
  196. if (encodedPayload.length != index)
  197. revert PythErrors.InvalidGovernanceMessage();
  198. }
  199. /// @dev Parse a WithdrawFeePayload (action 9) with minimal validation
  200. function parseWithdrawFeePayload(
  201. bytes memory encodedPayload
  202. ) public pure returns (WithdrawFeePayload memory wf) {
  203. uint index = 0;
  204. wf.targetAddress = address(encodedPayload.toAddress(index));
  205. index += 20;
  206. uint64 val = encodedPayload.toUint64(index);
  207. index += 8;
  208. uint64 expo = encodedPayload.toUint64(index);
  209. index += 8;
  210. wf.fee = uint256(val) * uint256(10) ** uint256(expo);
  211. if (encodedPayload.length != index)
  212. revert PythErrors.InvalidGovernanceMessage();
  213. }
  214. }