[eth] Use PythErrors everywhere (#404)
* Remove unnecessary check * Use PythErrors everywhere
This commit is contained in:
parent
b31f768d37
commit
c3461e5e1c
|
@ -7,6 +7,7 @@ import "../libraries/external/UnsafeBytesLib.sol";
|
||||||
import "@pythnetwork/pyth-sdk-solidity/AbstractPyth.sol";
|
import "@pythnetwork/pyth-sdk-solidity/AbstractPyth.sol";
|
||||||
import "@pythnetwork/pyth-sdk-solidity/PythStructs.sol";
|
import "@pythnetwork/pyth-sdk-solidity/PythStructs.sol";
|
||||||
|
|
||||||
|
import "@pythnetwork/pyth-sdk-solidity/PythErrors.sol";
|
||||||
import "./PythGetters.sol";
|
import "./PythGetters.sol";
|
||||||
import "./PythSetters.sol";
|
import "./PythSetters.sol";
|
||||||
import "./PythInternalStructs.sol";
|
import "./PythInternalStructs.sol";
|
||||||
|
@ -21,11 +22,10 @@ abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
|
||||||
) internal {
|
) internal {
|
||||||
setWormhole(wormhole);
|
setWormhole(wormhole);
|
||||||
|
|
||||||
require(
|
if (
|
||||||
dataSourceEmitterChainIds.length ==
|
dataSourceEmitterChainIds.length !=
|
||||||
dataSourceEmitterAddresses.length,
|
dataSourceEmitterAddresses.length
|
||||||
"data source arguments should have the same length"
|
) revert PythErrors.InvalidArgument();
|
||||||
);
|
|
||||||
|
|
||||||
for (uint i = 0; i < dataSourceEmitterChainIds.length; i++) {
|
for (uint i = 0; i < dataSourceEmitterChainIds.length; i++) {
|
||||||
PythInternalStructs.DataSource memory ds = PythInternalStructs
|
PythInternalStructs.DataSource memory ds = PythInternalStructs
|
||||||
|
@ -34,10 +34,8 @@ abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
|
||||||
dataSourceEmitterAddresses[i]
|
dataSourceEmitterAddresses[i]
|
||||||
);
|
);
|
||||||
|
|
||||||
require(
|
if (PythGetters.isValidDataSource(ds.chainId, ds.emitterAddress))
|
||||||
!PythGetters.isValidDataSource(ds.chainId, ds.emitterAddress),
|
revert PythErrors.InvalidArgument();
|
||||||
"Data source already added"
|
|
||||||
);
|
|
||||||
|
|
||||||
_state.isValidDataSource[hashDataSource(ds)] = true;
|
_state.isValidDataSource[hashDataSource(ds)] = true;
|
||||||
_state.validDataSources.push(ds);
|
_state.validDataSources.push(ds);
|
||||||
|
@ -57,7 +55,7 @@ abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
|
||||||
bytes[] calldata updateData
|
bytes[] calldata updateData
|
||||||
) public payable override {
|
) public payable override {
|
||||||
uint requiredFee = getUpdateFee(updateData);
|
uint requiredFee = getUpdateFee(updateData);
|
||||||
require(msg.value >= requiredFee, "insufficient paid fee amount");
|
if (msg.value < requiredFee) revert PythErrors.InsufficientFee();
|
||||||
|
|
||||||
for (uint i = 0; i < updateData.length; ) {
|
for (uint i = 0; i < updateData.length; ) {
|
||||||
updatePriceBatchFromVm(updateData[i]);
|
updatePriceBatchFromVm(updateData[i]);
|
||||||
|
@ -245,10 +243,8 @@ abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
require(
|
if (attestationIndex > attestationSize)
|
||||||
attestationIndex <= attestationSize,
|
revert PythErrors.InvalidUpdateData();
|
||||||
"INTERNAL: Consumed more than `attestationSize` bytes"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,10 +255,8 @@ abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
|
||||||
bytes32[] calldata priceIds,
|
bytes32[] calldata priceIds,
|
||||||
uint64[] calldata publishTimes
|
uint64[] calldata publishTimes
|
||||||
) external payable override {
|
) external payable override {
|
||||||
require(
|
if (priceIds.length != publishTimes.length)
|
||||||
priceIds.length == publishTimes.length,
|
revert PythErrors.InvalidArgument();
|
||||||
"priceIds and publishTimes arrays should have same length"
|
|
||||||
);
|
|
||||||
|
|
||||||
for (uint i = 0; i < priceIds.length; ) {
|
for (uint i = 0; i < priceIds.length; ) {
|
||||||
// If the price does not exist, then the publish time is zero and
|
// If the price does not exist, then the publish time is zero and
|
||||||
|
@ -277,9 +271,7 @@ abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
revert(
|
revert PythErrors.NoFreshUpdate();
|
||||||
"no prices in the submitted batch have fresh prices, so this update will have no effect"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is an overwrite of the same method in AbstractPyth.sol
|
// This is an overwrite of the same method in AbstractPyth.sol
|
||||||
|
@ -295,10 +287,7 @@ abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
|
||||||
price.price = info.price;
|
price.price = info.price;
|
||||||
price.conf = info.conf;
|
price.conf = info.conf;
|
||||||
|
|
||||||
require(
|
if (price.publishTime == 0) revert PythErrors.PriceFeedNotFound();
|
||||||
price.publishTime != 0,
|
|
||||||
"price feed for the given id is not pushed or does not exist"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is an overwrite of the same method in AbstractPyth.sol
|
// This is an overwrite of the same method in AbstractPyth.sol
|
||||||
|
@ -314,10 +303,7 @@ abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
|
||||||
price.price = info.emaPrice;
|
price.price = info.emaPrice;
|
||||||
price.conf = info.emaConf;
|
price.conf = info.emaConf;
|
||||||
|
|
||||||
require(
|
if (price.publishTime == 0) revert PythErrors.PriceFeedNotFound();
|
||||||
price.publishTime != 0,
|
|
||||||
"price feed for the given id is not pushed or does not exist"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseBatchAttestationHeader(
|
function parseBatchAttestationHeader(
|
||||||
|
@ -334,18 +320,20 @@ abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
|
||||||
{
|
{
|
||||||
uint32 magic = UnsafeBytesLib.toUint32(encoded, index);
|
uint32 magic = UnsafeBytesLib.toUint32(encoded, index);
|
||||||
index += 4;
|
index += 4;
|
||||||
require(magic == 0x50325748, "invalid magic value");
|
if (magic != 0x50325748) revert PythErrors.InvalidUpdateData();
|
||||||
|
|
||||||
uint16 versionMajor = UnsafeBytesLib.toUint16(encoded, index);
|
uint16 versionMajor = UnsafeBytesLib.toUint16(encoded, index);
|
||||||
index += 2;
|
index += 2;
|
||||||
require(versionMajor == 3, "invalid version major, expected 3");
|
if (versionMajor != 3) revert PythErrors.InvalidUpdateData();
|
||||||
|
|
||||||
uint16 versionMinor = UnsafeBytesLib.toUint16(encoded, index);
|
// This value is only used as the check below which currently
|
||||||
|
// never reverts
|
||||||
|
// uint16 versionMinor = UnsafeBytesLib.toUint16(encoded, index);
|
||||||
index += 2;
|
index += 2;
|
||||||
require(
|
|
||||||
versionMinor >= 0,
|
// This check is always false as versionMinor is 0, so it is commented.
|
||||||
"invalid version minor, expected 0 or more"
|
// in the future that the minor version increases this will have effect.
|
||||||
);
|
// if(versionMinor < 0) revert InvalidUpdateData();
|
||||||
|
|
||||||
uint16 hdrSize = UnsafeBytesLib.toUint16(encoded, index);
|
uint16 hdrSize = UnsafeBytesLib.toUint16(encoded, index);
|
||||||
index += 2;
|
index += 2;
|
||||||
|
@ -370,10 +358,7 @@ abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
|
||||||
index += hdrSize;
|
index += hdrSize;
|
||||||
|
|
||||||
// Payload ID of 2 required for batch headerBa
|
// Payload ID of 2 required for batch headerBa
|
||||||
require(
|
if (payloadId != 2) revert PythErrors.InvalidUpdateData();
|
||||||
payloadId == 2,
|
|
||||||
"invalid payload ID, expected 2 for BatchPriceAttestation"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the number of attestations
|
// Parse the number of attestations
|
||||||
|
@ -386,10 +371,8 @@ abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
|
||||||
|
|
||||||
// Given the message is valid the arithmetic below should not overflow, and
|
// Given the message is valid the arithmetic below should not overflow, and
|
||||||
// even if it overflows then the require would fail.
|
// even if it overflows then the require would fail.
|
||||||
require(
|
if (encoded.length != (index + (attestationSize * nAttestations)))
|
||||||
encoded.length == (index + (attestationSize * nAttestations)),
|
revert PythErrors.InvalidUpdateData();
|
||||||
"invalid BatchPriceAttestation size"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -398,12 +381,11 @@ abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
|
||||||
) internal view returns (IWormhole.VM memory vm) {
|
) internal view returns (IWormhole.VM memory vm) {
|
||||||
{
|
{
|
||||||
bool valid;
|
bool valid;
|
||||||
string memory reason;
|
(vm, valid, ) = wormhole().parseAndVerifyVM(encodedVm);
|
||||||
(vm, valid, reason) = wormhole().parseAndVerifyVM(encodedVm);
|
if (!valid) revert PythErrors.InvalidWormholeVaa();
|
||||||
require(valid, reason);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
require(verifyPythVM(vm), "invalid data source chain/emitter ID");
|
if (!verifyPythVM(vm)) revert PythErrors.InvalidUpdateDataSource();
|
||||||
}
|
}
|
||||||
|
|
||||||
function parsePriceFeedUpdates(
|
function parsePriceFeedUpdates(
|
||||||
|
@ -420,10 +402,8 @@ abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
|
||||||
unchecked {
|
unchecked {
|
||||||
{
|
{
|
||||||
uint requiredFee = getUpdateFee(updateData);
|
uint requiredFee = getUpdateFee(updateData);
|
||||||
require(
|
if (msg.value < requiredFee)
|
||||||
msg.value >= requiredFee,
|
revert PythErrors.InsufficientFee();
|
||||||
"insufficient paid fee amount"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
priceFeeds = new PythStructs.PriceFeed[](priceIds.length);
|
priceFeeds = new PythStructs.PriceFeed[](priceIds.length);
|
||||||
|
@ -482,10 +462,6 @@ abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
|
||||||
index,
|
index,
|
||||||
attestationSize
|
attestationSize
|
||||||
);
|
);
|
||||||
require(
|
|
||||||
info.publishTime != 0,
|
|
||||||
"price feed for the given id is not pushed or does not exist"
|
|
||||||
);
|
|
||||||
|
|
||||||
priceFeeds[k].id = priceId;
|
priceFeeds[k].id = priceId;
|
||||||
priceFeeds[k].price.price = info.price;
|
priceFeeds[k].price.price = info.price;
|
||||||
|
@ -513,10 +489,9 @@ abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint k = 0; k < priceIds.length; k++) {
|
for (uint k = 0; k < priceIds.length; k++) {
|
||||||
require(
|
if (priceFeeds[k].id == 0) {
|
||||||
priceFeeds[k].id != 0,
|
revert PythErrors.PriceFeedNotFoundWithinRange();
|
||||||
"1 or more price feeds are not found in the updateData or they are out of the given time range"
|
}
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -526,10 +501,7 @@ abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
|
||||||
) public view override returns (PythStructs.PriceFeed memory priceFeed) {
|
) public view override returns (PythStructs.PriceFeed memory priceFeed) {
|
||||||
// Look up the latest price info for the given ID
|
// Look up the latest price info for the given ID
|
||||||
PythInternalStructs.PriceInfo memory info = latestPriceInfo(id);
|
PythInternalStructs.PriceInfo memory info = latestPriceInfo(id);
|
||||||
require(
|
if (info.publishTime == 0) revert PythErrors.PriceFeedNotFound();
|
||||||
info.publishTime != 0,
|
|
||||||
"price feed for the given id is not pushed or does not exist"
|
|
||||||
);
|
|
||||||
|
|
||||||
priceFeed.id = id;
|
priceFeed.id = id;
|
||||||
priceFeed.price.price = info.price;
|
priceFeed.price.price = info.price;
|
||||||
|
|
|
@ -7,6 +7,7 @@ import "./PythGovernanceInstructions.sol";
|
||||||
import "./PythInternalStructs.sol";
|
import "./PythInternalStructs.sol";
|
||||||
import "./PythGetters.sol";
|
import "./PythGetters.sol";
|
||||||
import "./PythSetters.sol";
|
import "./PythSetters.sol";
|
||||||
|
import "@pythnetwork/pyth-sdk-solidity/PythErrors.sol";
|
||||||
|
|
||||||
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol";
|
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol";
|
||||||
|
|
||||||
|
@ -37,19 +38,17 @@ abstract contract PythGovernance is
|
||||||
function verifyGovernanceVM(
|
function verifyGovernanceVM(
|
||||||
bytes memory encodedVM
|
bytes memory encodedVM
|
||||||
) internal returns (IWormhole.VM memory parsedVM) {
|
) internal returns (IWormhole.VM memory parsedVM) {
|
||||||
(IWormhole.VM memory vm, bool valid, string memory reason) = wormhole()
|
(IWormhole.VM memory vm, bool valid, ) = wormhole().parseAndVerifyVM(
|
||||||
.parseAndVerifyVM(encodedVM);
|
encodedVM
|
||||||
|
|
||||||
require(valid, reason);
|
|
||||||
require(
|
|
||||||
isValidGovernanceDataSource(vm.emitterChainId, vm.emitterAddress),
|
|
||||||
"VAA is not coming from the governance data source"
|
|
||||||
);
|
);
|
||||||
|
|
||||||
require(
|
if (!valid) revert PythErrors.InvalidWormholeVaa();
|
||||||
vm.sequence > lastExecutedGovernanceSequence(),
|
|
||||||
"VAA is older than the last executed governance VAA"
|
if (!isValidGovernanceDataSource(vm.emitterChainId, vm.emitterAddress))
|
||||||
);
|
revert PythErrors.InvalidGovernanceDataSource();
|
||||||
|
|
||||||
|
if (vm.sequence <= lastExecutedGovernanceSequence())
|
||||||
|
revert PythErrors.OldGovernanceMessage();
|
||||||
|
|
||||||
setLastExecutedGovernanceSequence(vm.sequence);
|
setLastExecutedGovernanceSequence(vm.sequence);
|
||||||
|
|
||||||
|
@ -63,16 +62,12 @@ abstract contract PythGovernance is
|
||||||
vm.payload
|
vm.payload
|
||||||
);
|
);
|
||||||
|
|
||||||
require(
|
if (gi.targetChainId != chainId() && gi.targetChainId != 0)
|
||||||
gi.targetChainId == chainId() || gi.targetChainId == 0,
|
revert PythErrors.InvalidGovernanceTarget();
|
||||||
"invalid target chain for this governance instruction"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (gi.action == GovernanceAction.UpgradeContract) {
|
if (gi.action == GovernanceAction.UpgradeContract) {
|
||||||
require(
|
if (gi.targetChainId == 0)
|
||||||
gi.targetChainId != 0,
|
revert PythErrors.InvalidGovernanceTarget();
|
||||||
"upgrade with chain id 0 is not possible"
|
|
||||||
);
|
|
||||||
upgradeContract(parseUpgradeContractPayload(gi.payload));
|
upgradeContract(parseUpgradeContractPayload(gi.payload));
|
||||||
} else if (
|
} else if (
|
||||||
gi.action == GovernanceAction.AuthorizeGovernanceDataSourceTransfer
|
gi.action == GovernanceAction.AuthorizeGovernanceDataSourceTransfer
|
||||||
|
@ -89,11 +84,10 @@ abstract contract PythGovernance is
|
||||||
} else if (
|
} else if (
|
||||||
gi.action == GovernanceAction.RequestGovernanceDataSourceTransfer
|
gi.action == GovernanceAction.RequestGovernanceDataSourceTransfer
|
||||||
) {
|
) {
|
||||||
revert(
|
// RequestGovernanceDataSourceTransfer can be only part of AuthorizeGovernanceDataSourceTransfer message
|
||||||
"RequestGovernanceDataSourceTransfer can be only part of AuthorizeGovernanceDataSourceTransfer message"
|
revert PythErrors.InvalidGovernanceMessage();
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
revert("invalid governance action");
|
revert PythErrors.InvalidGovernanceMessage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,21 +113,19 @@ abstract contract PythGovernance is
|
||||||
// If it's valid then its emitter can take over the governance from the current emitter.
|
// If it's valid then its emitter can take over the governance from the current emitter.
|
||||||
// The VAA is checked here to ensure that the new governance data source is valid and can send message
|
// The VAA is checked here to ensure that the new governance data source is valid and can send message
|
||||||
// through wormhole.
|
// through wormhole.
|
||||||
(IWormhole.VM memory vm, bool valid, string memory reason) = wormhole()
|
(IWormhole.VM memory vm, bool valid, ) = wormhole().parseAndVerifyVM(
|
||||||
.parseAndVerifyVM(payload.claimVaa);
|
payload.claimVaa
|
||||||
require(valid, reason);
|
);
|
||||||
|
if (!valid) revert PythErrors.InvalidWormholeVaa();
|
||||||
|
|
||||||
GovernanceInstruction memory gi = parseGovernanceInstruction(
|
GovernanceInstruction memory gi = parseGovernanceInstruction(
|
||||||
vm.payload
|
vm.payload
|
||||||
);
|
);
|
||||||
require(
|
if (gi.targetChainId != chainId() && gi.targetChainId != 0)
|
||||||
gi.targetChainId == chainId() || gi.targetChainId == 0,
|
revert PythErrors.InvalidGovernanceTarget();
|
||||||
"invalid target chain for this governance instruction"
|
|
||||||
);
|
if (gi.action != GovernanceAction.RequestGovernanceDataSourceTransfer)
|
||||||
require(
|
revert PythErrors.InvalidGovernanceMessage();
|
||||||
gi.action == GovernanceAction.RequestGovernanceDataSourceTransfer,
|
|
||||||
"governance data source change inner vaa is not of claim action type"
|
|
||||||
);
|
|
||||||
|
|
||||||
RequestGovernanceDataSourceTransferPayload
|
RequestGovernanceDataSourceTransferPayload
|
||||||
memory claimPayload = parseRequestGovernanceDataSourceTransferPayload(
|
memory claimPayload = parseRequestGovernanceDataSourceTransferPayload(
|
||||||
|
@ -141,11 +133,10 @@ abstract contract PythGovernance is
|
||||||
);
|
);
|
||||||
|
|
||||||
// Governance data source index is used to prevent replay attacks, so a claimVaa cannot be used twice.
|
// Governance data source index is used to prevent replay attacks, so a claimVaa cannot be used twice.
|
||||||
require(
|
if (
|
||||||
governanceDataSourceIndex() <
|
governanceDataSourceIndex() >=
|
||||||
claimPayload.governanceDataSourceIndex,
|
claimPayload.governanceDataSourceIndex
|
||||||
"cannot upgrade to an older governance data source"
|
) revert PythErrors.OldGovernanceMessage();
|
||||||
);
|
|
||||||
|
|
||||||
setGovernanceDataSourceIndex(claimPayload.governanceDataSourceIndex);
|
setGovernanceDataSourceIndex(claimPayload.governanceDataSourceIndex);
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ pragma solidity ^0.8.0;
|
||||||
|
|
||||||
import "../libraries/external/BytesLib.sol";
|
import "../libraries/external/BytesLib.sol";
|
||||||
import "./PythInternalStructs.sol";
|
import "./PythInternalStructs.sol";
|
||||||
|
import "@pythnetwork/pyth-sdk-solidity/PythErrors.sol";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev `PythGovernanceInstructions` defines a set of structs and parsing functions
|
* @dev `PythGovernanceInstructions` defines a set of structs and parsing functions
|
||||||
|
@ -75,17 +76,16 @@ contract PythGovernanceInstructions {
|
||||||
uint index = 0;
|
uint index = 0;
|
||||||
|
|
||||||
uint32 magic = encodedInstruction.toUint32(index);
|
uint32 magic = encodedInstruction.toUint32(index);
|
||||||
require(magic == MAGIC, "invalid magic for GovernanceInstruction");
|
|
||||||
|
if (magic != MAGIC) revert PythErrors.InvalidGovernanceMessage();
|
||||||
|
|
||||||
index += 4;
|
index += 4;
|
||||||
|
|
||||||
uint8 modNumber = encodedInstruction.toUint8(index);
|
uint8 modNumber = encodedInstruction.toUint8(index);
|
||||||
gi.module = GovernanceModule(modNumber);
|
gi.module = GovernanceModule(modNumber);
|
||||||
index += 1;
|
index += 1;
|
||||||
|
|
||||||
require(
|
if (gi.module != MODULE) revert PythErrors.InvalidGovernanceTarget();
|
||||||
gi.module == MODULE,
|
|
||||||
"invalid module for GovernanceInstruction"
|
|
||||||
);
|
|
||||||
|
|
||||||
uint8 actionNumber = encodedInstruction.toUint8(index);
|
uint8 actionNumber = encodedInstruction.toUint8(index);
|
||||||
gi.action = GovernanceAction(actionNumber);
|
gi.action = GovernanceAction(actionNumber);
|
||||||
|
@ -112,10 +112,8 @@ contract PythGovernanceInstructions {
|
||||||
uc.newImplementation = address(encodedPayload.toAddress(index));
|
uc.newImplementation = address(encodedPayload.toAddress(index));
|
||||||
index += 20;
|
index += 20;
|
||||||
|
|
||||||
require(
|
if (encodedPayload.length != index)
|
||||||
encodedPayload.length == index,
|
revert PythErrors.InvalidGovernanceMessage();
|
||||||
"invalid length for UpgradeContractPayload"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Parse a AuthorizeGovernanceDataSourceTransferPayload (action 2) with minimal validation
|
/// @dev Parse a AuthorizeGovernanceDataSourceTransferPayload (action 2) with minimal validation
|
||||||
|
@ -142,10 +140,8 @@ contract PythGovernanceInstructions {
|
||||||
sgdsClaim.governanceDataSourceIndex = encodedPayload.toUint32(index);
|
sgdsClaim.governanceDataSourceIndex = encodedPayload.toUint32(index);
|
||||||
index += 4;
|
index += 4;
|
||||||
|
|
||||||
require(
|
if (encodedPayload.length != index)
|
||||||
encodedPayload.length == index,
|
revert PythErrors.InvalidGovernanceMessage();
|
||||||
"invalid length for RequestGovernanceDataSourceTransferPayload"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Parse a SetDataSourcesPayload (action 3) with minimal validation
|
/// @dev Parse a SetDataSourcesPayload (action 3) with minimal validation
|
||||||
|
@ -169,10 +165,8 @@ contract PythGovernanceInstructions {
|
||||||
index += 32;
|
index += 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
require(
|
if (encodedPayload.length != index)
|
||||||
encodedPayload.length == index,
|
revert PythErrors.InvalidGovernanceMessage();
|
||||||
"invalid length for SetDataSourcesPayload"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Parse a SetFeePayload (action 4) with minimal validation
|
/// @dev Parse a SetFeePayload (action 4) with minimal validation
|
||||||
|
@ -189,10 +183,8 @@ contract PythGovernanceInstructions {
|
||||||
|
|
||||||
sf.newFee = uint256(val) * uint256(10) ** uint256(expo);
|
sf.newFee = uint256(val) * uint256(10) ** uint256(expo);
|
||||||
|
|
||||||
require(
|
if (encodedPayload.length != index)
|
||||||
encodedPayload.length == index,
|
revert PythErrors.InvalidGovernanceMessage();
|
||||||
"invalid length for SetFeePayload"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Parse a SetValidPeriodPayload (action 5) with minimal validation
|
/// @dev Parse a SetValidPeriodPayload (action 5) with minimal validation
|
||||||
|
@ -204,9 +196,7 @@ contract PythGovernanceInstructions {
|
||||||
svp.newValidPeriod = uint256(encodedPayload.toUint64(index));
|
svp.newValidPeriod = uint256(encodedPayload.toUint64(index));
|
||||||
index += 8;
|
index += 8;
|
||||||
|
|
||||||
require(
|
if (encodedPayload.length != index)
|
||||||
encodedPayload.length == index,
|
revert PythErrors.InvalidGovernanceMessage();
|
||||||
"invalid length for SetValidPeriodPayload"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
|
||||||
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
|
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
|
||||||
import "./PythGovernance.sol";
|
import "./PythGovernance.sol";
|
||||||
import "./Pyth.sol";
|
import "./Pyth.sol";
|
||||||
|
import "@pythnetwork/pyth-sdk-solidity/PythErrors.sol";
|
||||||
|
|
||||||
contract PythUpgradable is
|
contract PythUpgradable is
|
||||||
Initializable,
|
Initializable,
|
||||||
|
@ -126,11 +127,10 @@ contract PythUpgradable is
|
||||||
_upgradeToAndCallUUPS(payload.newImplementation, new bytes(0), false);
|
_upgradeToAndCallUUPS(payload.newImplementation, new bytes(0), false);
|
||||||
|
|
||||||
// Calling a method using `this.<method>` will cause a contract call that will use
|
// Calling a method using `this.<method>` will cause a contract call that will use
|
||||||
// the new contract.
|
// the new contract. This call will fail if the method does not exists or the magic
|
||||||
require(
|
// is different.
|
||||||
this.pythUpgradableMagic() == 0x97a6f304,
|
if (this.pythUpgradableMagic() != 0x97a6f304)
|
||||||
"the new implementation is not a Pyth contract"
|
revert PythErrors.InvalidGovernanceMessage();
|
||||||
);
|
|
||||||
|
|
||||||
emit ContractUpgraded(oldImplementation, _getImplementation());
|
emit ContractUpgraded(oldImplementation, _getImplementation());
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
|
||||||
import "forge-std/Test.sol";
|
import "forge-std/Test.sol";
|
||||||
|
|
||||||
import "@pythnetwork/pyth-sdk-solidity/IPyth.sol";
|
import "@pythnetwork/pyth-sdk-solidity/IPyth.sol";
|
||||||
|
import "@pythnetwork/pyth-sdk-solidity/PythErrors.sol";
|
||||||
import "@pythnetwork/pyth-sdk-solidity/PythStructs.sol";
|
import "@pythnetwork/pyth-sdk-solidity/PythStructs.sol";
|
||||||
import "./utils/WormholeTestUtils.t.sol";
|
import "./utils/WormholeTestUtils.t.sol";
|
||||||
import "./utils/PythTestUtils.t.sol";
|
import "./utils/PythTestUtils.t.sol";
|
||||||
|
@ -139,11 +140,7 @@ contract GasBenchmark is Test, WormholeTestUtils, PythTestUtils {
|
||||||
function testBenchmarkUpdatePriceFeedsIfNecessaryNotFresh() public {
|
function testBenchmarkUpdatePriceFeedsIfNecessaryNotFresh() public {
|
||||||
// Since the price is not advanced, the publishTimes are the same as the
|
// Since the price is not advanced, the publishTimes are the same as the
|
||||||
// ones in the contract.
|
// ones in the contract.
|
||||||
vm.expectRevert(
|
vm.expectRevert(PythErrors.NoFreshUpdate.selector);
|
||||||
bytes(
|
|
||||||
"no prices in the submitted batch have fresh prices, so this update will have no effect"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
pyth.updatePriceFeedsIfNecessary{value: cachedPricesUpdateFee}(
|
pyth.updatePriceFeedsIfNecessary{value: cachedPricesUpdateFee}(
|
||||||
cachedPricesUpdateData,
|
cachedPricesUpdateData,
|
||||||
|
@ -183,11 +180,7 @@ contract GasBenchmark is Test, WormholeTestUtils, PythTestUtils {
|
||||||
bytes32[] memory ids = new bytes32[](1);
|
bytes32[] memory ids = new bytes32[](1);
|
||||||
ids[0] = priceIds[0];
|
ids[0] = priceIds[0];
|
||||||
|
|
||||||
vm.expectRevert(
|
vm.expectRevert(PythErrors.PriceFeedNotFoundWithinRange.selector);
|
||||||
bytes(
|
|
||||||
"1 or more price feeds are not found in the updateData or they are out of the given time range"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
pyth.parsePriceFeedUpdates{value: freshPricesUpdateFee}(
|
pyth.parsePriceFeedUpdates{value: freshPricesUpdateFee}(
|
||||||
freshPricesUpdateData,
|
freshPricesUpdateData,
|
||||||
ids,
|
ids,
|
||||||
|
|
|
@ -6,6 +6,7 @@ import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
|
||||||
import "forge-std/Test.sol";
|
import "forge-std/Test.sol";
|
||||||
|
|
||||||
import "@pythnetwork/pyth-sdk-solidity/IPyth.sol";
|
import "@pythnetwork/pyth-sdk-solidity/IPyth.sol";
|
||||||
|
import "@pythnetwork/pyth-sdk-solidity/PythErrors.sol";
|
||||||
import "@pythnetwork/pyth-sdk-solidity/PythStructs.sol";
|
import "@pythnetwork/pyth-sdk-solidity/PythStructs.sol";
|
||||||
import "./utils/WormholeTestUtils.t.sol";
|
import "./utils/WormholeTestUtils.t.sol";
|
||||||
import "./utils/PythTestUtils.t.sol";
|
import "./utils/PythTestUtils.t.sol";
|
||||||
|
@ -364,7 +365,7 @@ contract PythTest is Test, WormholeTestUtils, PythTestUtils, RandTestUtils {
|
||||||
// Since attestations are not empty the fee should be at least 1
|
// Since attestations are not empty the fee should be at least 1
|
||||||
assertGe(updateFee, 1);
|
assertGe(updateFee, 1);
|
||||||
|
|
||||||
vm.expectRevert(bytes("insufficient paid fee amount"));
|
vm.expectRevert(PythErrors.InsufficientFee.selector);
|
||||||
|
|
||||||
pyth.parsePriceFeedUpdates{value: updateFee - 1}(
|
pyth.parsePriceFeedUpdates{value: updateFee - 1}(
|
||||||
updateData,
|
updateData,
|
||||||
|
@ -425,7 +426,7 @@ contract PythTest is Test, WormholeTestUtils, PythTestUtils, RandTestUtils {
|
||||||
|
|
||||||
uint updateFee = pyth.getUpdateFee(updateData);
|
uint updateFee = pyth.getUpdateFee(updateData);
|
||||||
|
|
||||||
vm.expectRevert(bytes("invalid data source chain/emitter ID"));
|
vm.expectRevert(PythErrors.InvalidUpdateDataSource.selector);
|
||||||
pyth.parsePriceFeedUpdates{value: updateFee}(
|
pyth.parsePriceFeedUpdates{value: updateFee}(
|
||||||
updateData,
|
updateData,
|
||||||
priceIds,
|
priceIds,
|
||||||
|
@ -455,7 +456,7 @@ contract PythTest is Test, WormholeTestUtils, PythTestUtils, RandTestUtils {
|
||||||
|
|
||||||
uint updateFee = pyth.getUpdateFee(updateData);
|
uint updateFee = pyth.getUpdateFee(updateData);
|
||||||
|
|
||||||
vm.expectRevert(bytes("invalid data source chain/emitter ID"));
|
vm.expectRevert(PythErrors.InvalidUpdateDataSource.selector);
|
||||||
pyth.parsePriceFeedUpdates{value: updateFee}(
|
pyth.parsePriceFeedUpdates{value: updateFee}(
|
||||||
updateData,
|
updateData,
|
||||||
priceIds,
|
priceIds,
|
||||||
|
@ -480,11 +481,7 @@ contract PythTest is Test, WormholeTestUtils, PythTestUtils, RandTestUtils {
|
||||||
bytes32[] memory priceIds = new bytes32[](1);
|
bytes32[] memory priceIds = new bytes32[](1);
|
||||||
priceIds[0] = bytes32(uint(2));
|
priceIds[0] = bytes32(uint(2));
|
||||||
|
|
||||||
vm.expectRevert(
|
vm.expectRevert(PythErrors.PriceFeedNotFoundWithinRange.selector);
|
||||||
bytes(
|
|
||||||
"1 or more price feeds are not found in the updateData or they are out of the given time range"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
pyth.parsePriceFeedUpdates{value: updateFee}(
|
pyth.parsePriceFeedUpdates{value: updateFee}(
|
||||||
updateData,
|
updateData,
|
||||||
priceIds,
|
priceIds,
|
||||||
|
@ -520,11 +517,7 @@ contract PythTest is Test, WormholeTestUtils, PythTestUtils, RandTestUtils {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Request for parse after the time range should revert.
|
// Request for parse after the time range should revert.
|
||||||
vm.expectRevert(
|
vm.expectRevert(PythErrors.PriceFeedNotFoundWithinRange.selector);
|
||||||
bytes(
|
|
||||||
"1 or more price feeds are not found in the updateData or they are out of the given time range"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
pyth.parsePriceFeedUpdates{value: updateFee}(
|
pyth.parsePriceFeedUpdates{value: updateFee}(
|
||||||
updateData,
|
updateData,
|
||||||
priceIds,
|
priceIds,
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
"@certusone/wormhole-sdk-wasm": "^0.0.1",
|
"@certusone/wormhole-sdk-wasm": "^0.0.1",
|
||||||
"@openzeppelin/contracts": "^4.5.0",
|
"@openzeppelin/contracts": "^4.5.0",
|
||||||
"@openzeppelin/contracts-upgradeable": "^4.5.2",
|
"@openzeppelin/contracts-upgradeable": "^4.5.2",
|
||||||
"@pythnetwork/pyth-sdk-solidity": "^2.1.0",
|
"@pythnetwork/pyth-sdk-solidity": "^2.2.0",
|
||||||
"@pythnetwork/xc-governance-sdk": "file:../third_party/pyth/xc-governance-sdk-js",
|
"@pythnetwork/xc-governance-sdk": "file:../third_party/pyth/xc-governance-sdk-js",
|
||||||
"dotenv": "^10.0.0",
|
"dotenv": "^10.0.0",
|
||||||
"elliptic": "^6.5.2",
|
"elliptic": "^6.5.2",
|
||||||
|
@ -5642,9 +5642,9 @@
|
||||||
"integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA="
|
"integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA="
|
||||||
},
|
},
|
||||||
"node_modules/@pythnetwork/pyth-sdk-solidity": {
|
"node_modules/@pythnetwork/pyth-sdk-solidity": {
|
||||||
"version": "2.1.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@pythnetwork/pyth-sdk-solidity/-/pyth-sdk-solidity-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@pythnetwork/pyth-sdk-solidity/-/pyth-sdk-solidity-2.2.0.tgz",
|
||||||
"integrity": "sha512-jHzqw+BHaCOAYwRNCgAUhcbNZrB5f3Arly3PaYN3/Tg7/5RQ95a9FD15XvJB1DB3yymUPIkmLYMur7Sh+e1G4A=="
|
"integrity": "sha512-LsRMmaf9MTflGSymqOJMepFk/3R7DyxMOJfLDB5RDSieyiq+RJ5IYIYnXAFsMrqkjibOtVxARcortHtE9VWwhw=="
|
||||||
},
|
},
|
||||||
"node_modules/@pythnetwork/xc-governance-sdk": {
|
"node_modules/@pythnetwork/xc-governance-sdk": {
|
||||||
"resolved": "../third_party/pyth/xc-governance-sdk-js",
|
"resolved": "../third_party/pyth/xc-governance-sdk-js",
|
||||||
|
@ -45038,9 +45038,9 @@
|
||||||
"integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA="
|
"integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA="
|
||||||
},
|
},
|
||||||
"@pythnetwork/pyth-sdk-solidity": {
|
"@pythnetwork/pyth-sdk-solidity": {
|
||||||
"version": "2.1.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@pythnetwork/pyth-sdk-solidity/-/pyth-sdk-solidity-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@pythnetwork/pyth-sdk-solidity/-/pyth-sdk-solidity-2.2.0.tgz",
|
||||||
"integrity": "sha512-jHzqw+BHaCOAYwRNCgAUhcbNZrB5f3Arly3PaYN3/Tg7/5RQ95a9FD15XvJB1DB3yymUPIkmLYMur7Sh+e1G4A=="
|
"integrity": "sha512-LsRMmaf9MTflGSymqOJMepFk/3R7DyxMOJfLDB5RDSieyiq+RJ5IYIYnXAFsMrqkjibOtVxARcortHtE9VWwhw=="
|
||||||
},
|
},
|
||||||
"@pythnetwork/xc-governance-sdk": {
|
"@pythnetwork/xc-governance-sdk": {
|
||||||
"version": "file:../third_party/pyth/xc-governance-sdk-js",
|
"version": "file:../third_party/pyth/xc-governance-sdk-js",
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
"@certusone/wormhole-sdk-wasm": "^0.0.1",
|
"@certusone/wormhole-sdk-wasm": "^0.0.1",
|
||||||
"@openzeppelin/contracts": "^4.5.0",
|
"@openzeppelin/contracts": "^4.5.0",
|
||||||
"@openzeppelin/contracts-upgradeable": "^4.5.2",
|
"@openzeppelin/contracts-upgradeable": "^4.5.2",
|
||||||
"@pythnetwork/pyth-sdk-solidity": "^2.1.0",
|
"@pythnetwork/pyth-sdk-solidity": "^2.2.0",
|
||||||
"@pythnetwork/xc-governance-sdk": "file:../third_party/pyth/xc-governance-sdk-js",
|
"@pythnetwork/xc-governance-sdk": "file:../third_party/pyth/xc-governance-sdk-js",
|
||||||
"dotenv": "^10.0.0",
|
"dotenv": "^10.0.0",
|
||||||
"elliptic": "^6.5.2",
|
"elliptic": "^6.5.2",
|
||||||
|
|
|
@ -39,7 +39,6 @@ contract("Pyth", function () {
|
||||||
"0x71f8dcb863d176e2c420ad6610cf687359612b6fb392e0642b0ca6b1f186aa3b";
|
"0x71f8dcb863d176e2c420ad6610cf687359612b6fb392e0642b0ca6b1f186aa3b";
|
||||||
const notOwnerError =
|
const notOwnerError =
|
||||||
"Ownable: caller is not the owner -- Reason given: Ownable: caller is not the owner.";
|
"Ownable: caller is not the owner -- Reason given: Ownable: caller is not the owner.";
|
||||||
const insufficientFeeError = "insufficient paid fee amount";
|
|
||||||
|
|
||||||
// Place all atomic operations that are done within migrations here.
|
// Place all atomic operations that are done within migrations here.
|
||||||
beforeEach(async function () {
|
beforeEach(async function () {
|
||||||
|
@ -433,9 +432,9 @@ contract("Pyth", function () {
|
||||||
assert.equal(feeInWei, 20);
|
assert.equal(feeInWei, 20);
|
||||||
|
|
||||||
// When a smaller fee is payed it reverts
|
// When a smaller fee is payed it reverts
|
||||||
await expectRevert(
|
await expectRevertCustomError(
|
||||||
updatePriceFeeds(this.pythProxy, [rawBatch1, rawBatch2], feeInWei - 1),
|
updatePriceFeeds(this.pythProxy, [rawBatch1, rawBatch2], feeInWei - 1),
|
||||||
insufficientFeeError
|
"InsufficientFee"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -571,11 +570,11 @@ contract("Pyth", function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should fail transaction if a price is not found", async function () {
|
it("should fail transaction if a price is not found", async function () {
|
||||||
await expectRevert(
|
await expectRevertCustomError(
|
||||||
this.pythProxy.queryPriceFeed(
|
this.pythProxy.queryPriceFeed(
|
||||||
"0xdeadfeeddeadfeeddeadfeeddeadfeeddeadfeeddeadfeeddeadfeeddeadfeed"
|
"0xdeadfeeddeadfeeddeadfeeddeadfeeddeadfeeddeadfeeddeadfeeddeadfeed"
|
||||||
),
|
),
|
||||||
"price feed for the given id is not pushed or does not exist"
|
"PriceFeedNotFound"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -591,10 +590,7 @@ contract("Pyth", function () {
|
||||||
for (var i = 1; i <= RAW_BATCH_ATTESTATION_COUNT; i++) {
|
for (var i = 1; i <= RAW_BATCH_ATTESTATION_COUNT; i++) {
|
||||||
const price_id =
|
const price_id =
|
||||||
"0x" + (255 - (i % 256)).toString(16).padStart(2, "0").repeat(32);
|
"0x" + (255 - (i % 256)).toString(16).padStart(2, "0").repeat(32);
|
||||||
expectRevert(
|
expectRevertCustomError(this.pythProxy.getPrice(price_id), "StalePrice");
|
||||||
this.pythProxy.getPrice(price_id),
|
|
||||||
"no price available which is recent enough"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -610,10 +606,7 @@ contract("Pyth", function () {
|
||||||
for (var i = 1; i <= RAW_BATCH_ATTESTATION_COUNT; i++) {
|
for (var i = 1; i <= RAW_BATCH_ATTESTATION_COUNT; i++) {
|
||||||
const price_id =
|
const price_id =
|
||||||
"0x" + (255 - (i % 256)).toString(16).padStart(2, "0").repeat(32);
|
"0x" + (255 - (i % 256)).toString(16).padStart(2, "0").repeat(32);
|
||||||
expectRevert(
|
expectRevertCustomError(this.pythProxy.getPrice(price_id), "StalePrice");
|
||||||
this.pythProxy.getPrice(price_id),
|
|
||||||
"no price available which is recent enough"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -643,10 +636,7 @@ contract("Pyth", function () {
|
||||||
const price_id =
|
const price_id =
|
||||||
"0x" + (255 - (i % 256)).toString(16).padStart(2, "0").repeat(32);
|
"0x" + (255 - (i % 256)).toString(16).padStart(2, "0").repeat(32);
|
||||||
|
|
||||||
expectRevert(
|
expectRevertCustomError(this.pythProxy.getPrice(price_id), "StalePrice");
|
||||||
this.pythProxy.getPrice(price_id),
|
|
||||||
"no price available which is recent enough"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setting the validity time to 120 seconds
|
// Setting the validity time to 120 seconds
|
||||||
|
@ -753,9 +743,9 @@ contract("Pyth", function () {
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
|
|
||||||
await expectRevert(
|
await expectRevertCustomError(
|
||||||
this.pythProxy.updatePriceFeeds(["0x" + vm]),
|
this.pythProxy.updatePriceFeeds(["0x" + vm]),
|
||||||
"invalid data source chain/emitter ID"
|
"InvalidUpdateDataSource"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -779,9 +769,9 @@ contract("Pyth", function () {
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
await expectRevert(
|
await expectRevertCustomError(
|
||||||
this.pythProxy.executeGovernanceInstruction(vaaWrongMagic),
|
this.pythProxy.executeGovernanceInstruction(vaaWrongMagic),
|
||||||
"invalid magic for GovernanceInstruction"
|
"InvalidGovernanceMessage"
|
||||||
);
|
);
|
||||||
|
|
||||||
const wrongModule = Buffer.from(data);
|
const wrongModule = Buffer.from(data);
|
||||||
|
@ -794,9 +784,9 @@ contract("Pyth", function () {
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
await expectRevert(
|
await expectRevertCustomError(
|
||||||
this.pythProxy.executeGovernanceInstruction(vaaWrongModule),
|
this.pythProxy.executeGovernanceInstruction(vaaWrongModule),
|
||||||
"invalid module for GovernanceInstruction"
|
"InvalidGovernanceTarget"
|
||||||
);
|
);
|
||||||
|
|
||||||
const outOfBoundModule = Buffer.from(data);
|
const outOfBoundModule = Buffer.from(data);
|
||||||
|
@ -828,9 +818,9 @@ contract("Pyth", function () {
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
await expectRevert(
|
await expectRevertCustomError(
|
||||||
this.pythProxy.executeGovernanceInstruction(vaaWrongEmitter),
|
this.pythProxy.executeGovernanceInstruction(vaaWrongEmitter),
|
||||||
"VAA is not coming from the governance data source"
|
"InvalidGovernanceDataSource"
|
||||||
);
|
);
|
||||||
|
|
||||||
const vaaWrongChain = await createVAAFromUint8Array(
|
const vaaWrongChain = await createVAAFromUint8Array(
|
||||||
|
@ -840,9 +830,9 @@ contract("Pyth", function () {
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
await expectRevert(
|
await expectRevertCustomError(
|
||||||
this.pythProxy.executeGovernanceInstruction(vaaWrongChain),
|
this.pythProxy.executeGovernanceInstruction(vaaWrongChain),
|
||||||
"VAA is not coming from the governance data source"
|
"InvalidGovernanceDataSource"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -859,9 +849,9 @@ contract("Pyth", function () {
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
await expectRevert(
|
await expectRevertCustomError(
|
||||||
this.pythProxy.executeGovernanceInstruction(wrongChainVaa),
|
this.pythProxy.executeGovernanceInstruction(wrongChainVaa),
|
||||||
"invalid target chain for this governance instruction"
|
"InvalidGovernanceTarget"
|
||||||
);
|
);
|
||||||
|
|
||||||
const dataForAllChains = new governance.SetValidPeriodInstruction(
|
const dataForAllChains = new governance.SetValidPeriodInstruction(
|
||||||
|
@ -908,9 +898,9 @@ contract("Pyth", function () {
|
||||||
|
|
||||||
await this.pythProxy.executeGovernanceInstruction(vaaSeq1),
|
await this.pythProxy.executeGovernanceInstruction(vaaSeq1),
|
||||||
// Replaying shouldn't work
|
// Replaying shouldn't work
|
||||||
await expectRevert(
|
await expectRevertCustomError(
|
||||||
this.pythProxy.executeGovernanceInstruction(vaaSeq1),
|
this.pythProxy.executeGovernanceInstruction(vaaSeq1),
|
||||||
"VAA is older than the last executed governance VAA"
|
"OldGovernanceMessage"
|
||||||
);
|
);
|
||||||
|
|
||||||
const vaaSeq2 = await createVAAFromUint8Array(
|
const vaaSeq2 = await createVAAFromUint8Array(
|
||||||
|
@ -922,13 +912,13 @@ contract("Pyth", function () {
|
||||||
|
|
||||||
await this.pythProxy.executeGovernanceInstruction(vaaSeq2),
|
await this.pythProxy.executeGovernanceInstruction(vaaSeq2),
|
||||||
// Replaying shouldn't work
|
// Replaying shouldn't work
|
||||||
await expectRevert(
|
await expectRevertCustomError(
|
||||||
this.pythProxy.executeGovernanceInstruction(vaaSeq1),
|
this.pythProxy.executeGovernanceInstruction(vaaSeq1),
|
||||||
"VAA is older than the last executed governance VAA"
|
"OldGovernanceMessage"
|
||||||
);
|
);
|
||||||
await expectRevert(
|
await expectRevertCustomError(
|
||||||
this.pythProxy.executeGovernanceInstruction(vaaSeq2),
|
this.pythProxy.executeGovernanceInstruction(vaaSeq2),
|
||||||
"VAA is older than the last executed governance VAA"
|
"OldGovernanceMessage"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -948,9 +938,9 @@ contract("Pyth", function () {
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
await expectRevert(
|
await expectRevertCustomError(
|
||||||
this.pythProxy.executeGovernanceInstruction(vaa),
|
this.pythProxy.executeGovernanceInstruction(vaa),
|
||||||
"upgrade with chain id 0 is not possible"
|
"InvalidGovernanceTarget"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1020,9 +1010,9 @@ contract("Pyth", function () {
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
await expectRevert(
|
await expectRevertCustomError(
|
||||||
this.pythProxy.executeGovernanceInstruction(claimVaaHexString),
|
this.pythProxy.executeGovernanceInstruction(claimVaaHexString),
|
||||||
"VAA is not coming from the governance data source"
|
"InvalidGovernanceDataSource"
|
||||||
);
|
);
|
||||||
|
|
||||||
const claimVaa = Buffer.from(claimVaaHexString.substring(2), "hex");
|
const claimVaa = Buffer.from(claimVaaHexString.substring(2), "hex");
|
||||||
|
@ -1055,9 +1045,9 @@ contract("Pyth", function () {
|
||||||
expect(newGovernanceDataSource.emitterAddress).equal(newEmitterAddress);
|
expect(newGovernanceDataSource.emitterAddress).equal(newEmitterAddress);
|
||||||
|
|
||||||
// Verifies the data source has changed.
|
// Verifies the data source has changed.
|
||||||
await expectRevert(
|
await expectRevertCustomError(
|
||||||
this.pythProxy.executeGovernanceInstruction(vaa),
|
this.pythProxy.executeGovernanceInstruction(vaa),
|
||||||
"VAA is not coming from the governance data source"
|
"InvalidGovernanceDataSource"
|
||||||
);
|
);
|
||||||
|
|
||||||
// Make sure a claim vaa does not get executed
|
// Make sure a claim vaa does not get executed
|
||||||
|
@ -1075,9 +1065,9 @@ contract("Pyth", function () {
|
||||||
2
|
2
|
||||||
);
|
);
|
||||||
|
|
||||||
await expectRevert(
|
await expectRevertCustomError(
|
||||||
this.pythProxy.executeGovernanceInstruction(claimLonelyVaa),
|
this.pythProxy.executeGovernanceInstruction(claimLonelyVaa),
|
||||||
"RequestGovernanceDataSourceTransfer can be only part of AuthorizeGovernanceDataSourceTransfer message"
|
"InvalidGovernanceMessage"
|
||||||
);
|
);
|
||||||
|
|
||||||
// Transfer back the ownership to the old governance data source without increasing
|
// Transfer back the ownership to the old governance data source without increasing
|
||||||
|
@ -1115,9 +1105,15 @@ contract("Pyth", function () {
|
||||||
2
|
2
|
||||||
);
|
);
|
||||||
|
|
||||||
await expectRevert(
|
// This test fails without the hard coded gas limit.
|
||||||
this.pythProxy.executeGovernanceInstruction(transferBackVaaWrong),
|
// Without gas limit, it fails on a random place (in wormhole sig verification) which
|
||||||
"cannot upgrade to an older governance data source"
|
// is probably because truffle cannot estimate the gas usage correctly. So the gas is
|
||||||
|
// hard-coded to a high value of 6.7m gas (close to ganache limit).
|
||||||
|
await expectRevertCustomError(
|
||||||
|
this.pythProxy.executeGovernanceInstruction(transferBackVaaWrong, {
|
||||||
|
gas: 6700000,
|
||||||
|
}),
|
||||||
|
"OldGovernanceMessage"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1163,9 +1159,9 @@ contract("Pyth", function () {
|
||||||
);
|
);
|
||||||
|
|
||||||
let rawBatch = generateRawBatchAttestation(100, 100, 1337);
|
let rawBatch = generateRawBatchAttestation(100, 100, 1337);
|
||||||
await expectRevert(
|
await expectRevertCustomError(
|
||||||
updatePriceFeeds(this.pythProxy, [rawBatch]),
|
updatePriceFeeds(this.pythProxy, [rawBatch]),
|
||||||
"invalid data source chain/emitter ID"
|
"InvalidUpdateDataSource"
|
||||||
);
|
);
|
||||||
|
|
||||||
await updatePriceFeeds(
|
await updatePriceFeeds(
|
||||||
|
@ -1202,9 +1198,9 @@ contract("Pyth", function () {
|
||||||
assert.equal(await this.pythProxy.singleUpdateFeeInWei(), "5000");
|
assert.equal(await this.pythProxy.singleUpdateFeeInWei(), "5000");
|
||||||
|
|
||||||
let rawBatch = generateRawBatchAttestation(100, 100, 1337);
|
let rawBatch = generateRawBatchAttestation(100, 100, 1337);
|
||||||
await expectRevert(
|
await expectRevertCustomError(
|
||||||
updatePriceFeeds(this.pythProxy, [rawBatch], 0),
|
updatePriceFeeds(this.pythProxy, [rawBatch], 0),
|
||||||
insufficientFeeError
|
"InsufficientFee"
|
||||||
);
|
);
|
||||||
|
|
||||||
await updatePriceFeeds(this.pythProxy, [rawBatch], 5000);
|
await updatePriceFeeds(this.pythProxy, [rawBatch], 5000);
|
||||||
|
@ -1385,3 +1381,18 @@ function expectEventMultipleTimes(receipt, eventName, args, cnt) {
|
||||||
const matches = getNumMatchingEvents(receipt, eventName, args);
|
const matches = getNumMatchingEvents(receipt, eventName, args);
|
||||||
assert(matches === cnt, `Expected ${cnt} event matches, found ${matches}.`);
|
assert(matches === cnt, `Expected ${cnt} event matches, found ${matches}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function expectRevertCustomError(promise, reason) {
|
||||||
|
try {
|
||||||
|
await promise;
|
||||||
|
expect.fail("Expected promise to throw but it didn't");
|
||||||
|
} catch (revert) {
|
||||||
|
if (reason) {
|
||||||
|
const reasonId = web3.utils.keccak256(reason + "()").substr(0, 10);
|
||||||
|
expect(
|
||||||
|
JSON.stringify(revert),
|
||||||
|
`Expected custom error ${reason} (${reasonId})`
|
||||||
|
).to.include(reasonId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue