Skip to content
This repository was archived by the owner on May 6, 2025. It is now read-only.

Add PythErrors.sol #36

Merged
merged 2 commits into from
Nov 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 10 additions & 21 deletions AbstractPyth.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity ^0.8.0;

import "./PythStructs.sol";
import "./IPyth.sol";
import "./PythErrors.sol";

abstract contract AbstractPyth is IPyth {
/// @notice Returns the price feed with given id.
Expand Down Expand Up @@ -50,10 +51,8 @@ abstract contract AbstractPyth is IPyth {
) public view virtual override returns (PythStructs.Price memory price) {
price = getPriceUnsafe(id);

require(
diff(block.timestamp, price.publishTime) <= age,
"no price available which is recent enough"
);
if (diff(block.timestamp, price.publishTime) > age)
revert PythErrors.StalePrice();

return price;
}
Expand All @@ -71,10 +70,8 @@ abstract contract AbstractPyth is IPyth {
) public view virtual override returns (PythStructs.Price memory price) {
price = getEmaPriceUnsafe(id);

require(
diff(block.timestamp, price.publishTime) <= age,
"no ema price available which is recent enough"
);
if (diff(block.timestamp, price.publishTime) > age)
revert PythErrors.StalePrice();

return price;
}
Expand All @@ -97,28 +94,20 @@ abstract contract AbstractPyth is IPyth {
bytes32[] calldata priceIds,
uint64[] calldata publishTimes
) external payable virtual override {
require(
priceIds.length == publishTimes.length,
"priceIds and publishTimes arrays should have same length"
);
if (priceIds.length != publishTimes.length)
revert PythErrors.InvalidArgument();

bool updateNeeded = false;
for (uint i = 0; i < priceIds.length; i++) {
if (
!priceFeedExists(priceIds[i]) ||
queryPriceFeed(priceIds[i]).price.publishTime < publishTimes[i]
) {
updateNeeded = true;
break;
updatePriceFeeds(updateData);
return;
}
}

require(
updateNeeded,
"no prices in the submitted batch have fresh prices, so this update will have no effect"
);

updatePriceFeeds(updateData);
revert PythErrors.NoFreshUpdate();
}

function parsePriceFeedUpdates(
Expand Down
36 changes: 15 additions & 21 deletions MockPyth.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pragma solidity ^0.8.0;

import "./AbstractPyth.sol";
import "./PythStructs.sol";
import "./PythErrors.sol";

contract MockPyth is AbstractPyth {
mapping(bytes32 => PythStructs.PriceFeed) priceFeeds;
Expand All @@ -19,10 +20,7 @@ contract MockPyth is AbstractPyth {
function queryPriceFeed(
bytes32 id
) public view override returns (PythStructs.PriceFeed memory priceFeed) {
require(
priceFeeds[id].id != 0,
"no price feed found for the given price id"
);
if (priceFeeds[id].id == 0) revert PythErrors.PriceFeedNotFound();
return priceFeeds[id];
}

Expand All @@ -41,7 +39,7 @@ contract MockPyth is AbstractPyth {
bytes[] calldata updateData
) public payable override {
uint requiredFee = getUpdateFee(updateData);
require(msg.value >= requiredFee, "Insufficient paid fee amount");
if (msg.value < requiredFee) revert PythErrors.InsufficientFee();

// Chain ID is id of the source chain that the price update comes from. Since it is just a mock contract
// We set it to 1.
Expand Down Expand Up @@ -87,7 +85,7 @@ contract MockPyth is AbstractPyth {
uint64 maxPublishTime
) external payable override returns (PythStructs.PriceFeed[] memory feeds) {
uint requiredFee = getUpdateFee(updateData);
require(msg.value >= requiredFee, "Insufficient paid fee amount");
if (msg.value < requiredFee) revert PythErrors.InsufficientFee();

feeds = new PythStructs.PriceFeed[](priceIds.length);

Expand All @@ -96,24 +94,20 @@ contract MockPyth is AbstractPyth {
feeds[i] = abi.decode(updateData[j], (PythStructs.PriceFeed));

if (feeds[i].id == priceIds[i]) {
break;
uint publishTime = feeds[i].price.publishTime;
if (
minPublishTime <= publishTime &&
publishTime <= maxPublishTime
) {
break;
} else {
feeds[i].id = 0;
}
}
}

require(
feeds[i].id == priceIds[i],
"price id does not exist in the updateData"
);

uint publishTime = feeds[i].price.publishTime;
require(
minPublishTime <= publishTime,
"price feed publish time out of range"
);
require(
publishTime <= maxPublishTime,
"price feed publish time out of range"
);
if (feeds[i].id != priceIds[i])
revert PythErrors.PriceFeedNotFoundWithinRange();
}
}

Expand Down
32 changes: 32 additions & 0 deletions PythErrors.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: Apache 2

pragma solidity ^0.8.0;

library PythErrors {
// Function arguments are invalid (e.g., the arguments lengths mismatch)
error InvalidArgument();
// Update data is coming from an invalid data source.
error InvalidUpdateDataSource();
// Update data is invalid (e.g., deserialization error)
error InvalidUpdateData();
// Insufficient fee is paid to the method.
error InsufficientFee();
// There is no fresh update, whereas expected fresh updates.
error NoFreshUpdate();
// There is no price feed found within the given range or it does not exists.
error PriceFeedNotFoundWithinRange();
// Price feed not found or it is not pushed on-chain yet.
error PriceFeedNotFound();
// Requested price is stale.
error StalePrice();
// Given message is not a valid Wormhole VAA.
error InvalidWormholeVaa();
// Governance message is invalid (e.g., deserialization error).
error InvalidGovernanceMessage();
// Governance message is not for this contract.
error InvalidGovernanceTarget();
// Governance message is coming from an invalid data source.
error InvalidGovernanceDataSource();
// Governance message is old.
error OldGovernanceMessage();
}
15 changes: 15 additions & 0 deletions abis/AbstractPyth.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,19 @@
[
{
"inputs": [],
"name": "InvalidArgument",
"type": "error"
},
{
"inputs": [],
"name": "NoFreshUpdate",
"type": "error"
},
{
"inputs": [],
"name": "StalePrice",
"type": "error"
},
{
"anonymous": false,
"inputs": [
Expand Down
30 changes: 30 additions & 0 deletions abis/MockPyth.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,36 @@
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "InsufficientFee",
"type": "error"
},
{
"inputs": [],
"name": "InvalidArgument",
"type": "error"
},
{
"inputs": [],
"name": "NoFreshUpdate",
"type": "error"
},
{
"inputs": [],
"name": "PriceFeedNotFound",
"type": "error"
},
{
"inputs": [],
"name": "PriceFeedNotFoundWithinRange",
"type": "error"
},
{
"inputs": [],
"name": "StalePrice",
"type": "error"
},
{
"anonymous": false,
"inputs": [
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@pythnetwork/pyth-sdk-solidity",
"version": "2.1.0",
"version": "2.2.0",
"description": "Read prices from the Pyth oracle",
"repository": {
"type": "git",
Expand Down