171 lines
5.9 KiB
Solidity
171 lines
5.9 KiB
Solidity
// contracts/GovernanceStructs.sol
|
|
// SPDX-License-Identifier: Apache 2
|
|
|
|
pragma solidity ^0.8.0;
|
|
|
|
import "../libraries/external/BytesLib.sol";
|
|
import "./PythInternalStructs.sol";
|
|
|
|
/**
|
|
* @dev `PythGovernanceInstructions` defines a set of structs and parsing functions
|
|
* for Pyth governance instructions.
|
|
*/
|
|
contract PythGovernanceInstructions {
|
|
|
|
using BytesLib for bytes;
|
|
|
|
// Magic is `PTGM` encoded as a 4 byte data: Pyth Governance Message
|
|
uint32 constant MAGIC = 0x5054474d;
|
|
|
|
enum GovernanceModule {
|
|
Executor, // 0
|
|
Target // 1
|
|
}
|
|
|
|
GovernanceModule constant MODULE = GovernanceModule.Target;
|
|
|
|
enum GovernanceAction {
|
|
UpgradeContract, // 0
|
|
AuthorizeGovernanceDataSourceTransfer, // 1
|
|
SetDataSources, // 2
|
|
SetFee, // 3
|
|
SetValidPeriod, // 4
|
|
RequestGovernanceDataSourceTransfer // 5
|
|
}
|
|
|
|
struct GovernanceInstruction {
|
|
GovernanceModule module;
|
|
GovernanceAction action;
|
|
uint16 targetChainId;
|
|
bytes payload;
|
|
}
|
|
|
|
struct UpgradeContractPayload {
|
|
address newImplementation;
|
|
}
|
|
|
|
struct AuthorizeGovernanceDataSourceTransferPayload {
|
|
// Transfer governance control over this contract to another data source.
|
|
// The claimVaa field is a VAA created by the new data source; using a VAA prevents mistakes
|
|
// in the handoff by ensuring that the new data source can send VAAs (i.e., is not an invalid address).
|
|
bytes claimVaa;
|
|
}
|
|
|
|
struct RequestGovernanceDataSourceTransferPayload {
|
|
// Governance data source index is used to prevent replay attacks
|
|
// So a claimVaa cannot be used twice.
|
|
uint32 governanceDataSourceIndex;
|
|
}
|
|
|
|
struct SetDataSourcesPayload {
|
|
PythInternalStructs.DataSource[] dataSources;
|
|
}
|
|
|
|
struct SetFeePayload {
|
|
uint newFee;
|
|
}
|
|
|
|
struct SetValidPeriodPayload {
|
|
uint newValidPeriod;
|
|
}
|
|
|
|
/// @dev Parse a GovernanceInstruction
|
|
function parseGovernanceInstruction(bytes memory encodedInstruction) public pure returns (GovernanceInstruction memory gi) {
|
|
uint index = 0;
|
|
|
|
uint32 magic = encodedInstruction.toUint32(index);
|
|
require(magic == MAGIC, "invalid magic for GovernanceInstruction");
|
|
index += 4;
|
|
|
|
uint8 modNumber = encodedInstruction.toUint8(index);
|
|
gi.module = GovernanceModule(modNumber);
|
|
index += 1;
|
|
|
|
require(gi.module == MODULE, "invalid module for GovernanceInstruction");
|
|
|
|
uint8 actionNumber = encodedInstruction.toUint8(index);
|
|
gi.action = GovernanceAction(actionNumber);
|
|
index += 1;
|
|
|
|
gi.targetChainId = encodedInstruction.toUint16(index);
|
|
index += 2;
|
|
|
|
// As solidity performs math operations in a checked mode
|
|
// if the length of the encoded instruction be smaller than index
|
|
// it will revert. So we don't need any extra check.
|
|
gi.payload = encodedInstruction.slice(index, encodedInstruction.length - index);
|
|
}
|
|
|
|
/// @dev Parse a UpgradeContractPayload (action 1) with minimal validation
|
|
function parseUpgradeContractPayload(bytes memory encodedPayload) public pure returns (UpgradeContractPayload memory uc) {
|
|
uint index = 0;
|
|
|
|
uc.newImplementation = address(encodedPayload.toAddress(index));
|
|
index += 20;
|
|
|
|
require(encodedPayload.length == index, "invalid length for UpgradeContractPayload");
|
|
}
|
|
|
|
/// @dev Parse a AuthorizeGovernanceDataSourceTransferPayload (action 2) with minimal validation
|
|
function parseAuthorizeGovernanceDataSourceTransferPayload(bytes memory encodedPayload) public pure returns (AuthorizeGovernanceDataSourceTransferPayload memory sgds) {
|
|
sgds.claimVaa = encodedPayload;
|
|
}
|
|
|
|
/// @dev Parse a AuthorizeGovernanceDataSourceTransferPayload (action 2) with minimal validation
|
|
function parseRequestGovernanceDataSourceTransferPayload(bytes memory encodedPayload) public pure
|
|
returns (RequestGovernanceDataSourceTransferPayload memory sgdsClaim) {
|
|
|
|
uint index = 0;
|
|
|
|
sgdsClaim.governanceDataSourceIndex = encodedPayload.toUint32(index);
|
|
index += 4;
|
|
|
|
require(encodedPayload.length == index, "invalid length for RequestGovernanceDataSourceTransferPayload");
|
|
}
|
|
|
|
/// @dev Parse a SetDataSourcesPayload (action 3) with minimal validation
|
|
function parseSetDataSourcesPayload(bytes memory encodedPayload) public pure returns (SetDataSourcesPayload memory sds) {
|
|
uint index = 0;
|
|
|
|
uint8 dataSourcesLength = encodedPayload.toUint8(index);
|
|
index += 1;
|
|
|
|
sds.dataSources = new PythInternalStructs.DataSource[](dataSourcesLength);
|
|
|
|
for(uint i = 0; i < dataSourcesLength; i++) {
|
|
sds.dataSources[i].chainId = encodedPayload.toUint16(index);
|
|
index += 2;
|
|
|
|
sds.dataSources[i].emitterAddress = encodedPayload.toBytes32(index);
|
|
index += 32;
|
|
}
|
|
|
|
require(encodedPayload.length == index, "invalid length for SetDataSourcesPayload");
|
|
}
|
|
|
|
/// @dev Parse a SetFeePayload (action 4) with minimal validation
|
|
function parseSetFeePayload(bytes memory encodedPayload) public pure returns (SetFeePayload memory sf) {
|
|
uint index = 0;
|
|
|
|
uint64 val = encodedPayload.toUint64(index);
|
|
index += 8;
|
|
|
|
uint64 expo = encodedPayload.toUint64(index);
|
|
index += 8;
|
|
|
|
sf.newFee = uint256(val) * uint256(10)**uint256(expo);
|
|
|
|
require(encodedPayload.length == index, "invalid length for SetFeePayload");
|
|
}
|
|
|
|
/// @dev Parse a SetValidPeriodPayload (action 5) with minimal validation
|
|
function parseSetValidPeriodPayload(bytes memory encodedPayload) public pure returns (SetValidPeriodPayload memory svp) {
|
|
uint index = 0;
|
|
|
|
svp.newValidPeriod = uint256(encodedPayload.toUint64(index));
|
|
index += 8;
|
|
|
|
require(encodedPayload.length == index, "invalid length for SetValidPeriodPayload");
|
|
}
|
|
}
|