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

Commit 11d6bcf

Browse files
authored
Add PythErrors.sol (#36)
1 parent 202aa2e commit 11d6bcf

File tree

7 files changed

+105
-45
lines changed

7 files changed

+105
-45
lines changed

AbstractPyth.sol

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ pragma solidity ^0.8.0;
33

44
import "./PythStructs.sol";
55
import "./IPyth.sol";
6+
import "./PythErrors.sol";
67

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

53-
require(
54-
diff(block.timestamp, price.publishTime) <= age,
55-
"no price available which is recent enough"
56-
);
54+
if (diff(block.timestamp, price.publishTime) > age)
55+
revert PythErrors.StalePrice();
5756

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

74-
require(
75-
diff(block.timestamp, price.publishTime) <= age,
76-
"no ema price available which is recent enough"
77-
);
73+
if (diff(block.timestamp, price.publishTime) > age)
74+
revert PythErrors.StalePrice();
7875

7976
return price;
8077
}
@@ -97,28 +94,20 @@ abstract contract AbstractPyth is IPyth {
9794
bytes32[] calldata priceIds,
9895
uint64[] calldata publishTimes
9996
) external payable virtual override {
100-
require(
101-
priceIds.length == publishTimes.length,
102-
"priceIds and publishTimes arrays should have same length"
103-
);
97+
if (priceIds.length != publishTimes.length)
98+
revert PythErrors.InvalidArgument();
10499

105-
bool updateNeeded = false;
106100
for (uint i = 0; i < priceIds.length; i++) {
107101
if (
108102
!priceFeedExists(priceIds[i]) ||
109103
queryPriceFeed(priceIds[i]).price.publishTime < publishTimes[i]
110104
) {
111-
updateNeeded = true;
112-
break;
105+
updatePriceFeeds(updateData);
106+
return;
113107
}
114108
}
115109

116-
require(
117-
updateNeeded,
118-
"no prices in the submitted batch have fresh prices, so this update will have no effect"
119-
);
120-
121-
updatePriceFeeds(updateData);
110+
revert PythErrors.NoFreshUpdate();
122111
}
123112

124113
function parsePriceFeedUpdates(

MockPyth.sol

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ pragma solidity ^0.8.0;
33

44
import "./AbstractPyth.sol";
55
import "./PythStructs.sol";
6+
import "./PythErrors.sol";
67

78
contract MockPyth is AbstractPyth {
89
mapping(bytes32 => PythStructs.PriceFeed) priceFeeds;
@@ -19,10 +20,7 @@ contract MockPyth is AbstractPyth {
1920
function queryPriceFeed(
2021
bytes32 id
2122
) public view override returns (PythStructs.PriceFeed memory priceFeed) {
22-
require(
23-
priceFeeds[id].id != 0,
24-
"no price feed found for the given price id"
25-
);
23+
if (priceFeeds[id].id == 0) revert PythErrors.PriceFeedNotFound();
2624
return priceFeeds[id];
2725
}
2826

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

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

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

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

9896
if (feeds[i].id == priceIds[i]) {
99-
break;
97+
uint publishTime = feeds[i].price.publishTime;
98+
if (
99+
minPublishTime <= publishTime &&
100+
publishTime <= maxPublishTime
101+
) {
102+
break;
103+
} else {
104+
feeds[i].id = 0;
105+
}
100106
}
101107
}
102108

103-
require(
104-
feeds[i].id == priceIds[i],
105-
"price id does not exist in the updateData"
106-
);
107-
108-
uint publishTime = feeds[i].price.publishTime;
109-
require(
110-
minPublishTime <= publishTime,
111-
"price feed publish time out of range"
112-
);
113-
require(
114-
publishTime <= maxPublishTime,
115-
"price feed publish time out of range"
116-
);
109+
if (feeds[i].id != priceIds[i])
110+
revert PythErrors.PriceFeedNotFoundWithinRange();
117111
}
118112
}
119113

PythErrors.sol

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// SPDX-License-Identifier: Apache 2
2+
3+
pragma solidity ^0.8.0;
4+
5+
library PythErrors {
6+
// Function arguments are invalid (e.g., the arguments lengths mismatch)
7+
error InvalidArgument();
8+
// Update data is coming from an invalid data source.
9+
error InvalidUpdateDataSource();
10+
// Update data is invalid (e.g., deserialization error)
11+
error InvalidUpdateData();
12+
// Insufficient fee is paid to the method.
13+
error InsufficientFee();
14+
// There is no fresh update, whereas expected fresh updates.
15+
error NoFreshUpdate();
16+
// There is no price feed found within the given range or it does not exists.
17+
error PriceFeedNotFoundWithinRange();
18+
// Price feed not found or it is not pushed on-chain yet.
19+
error PriceFeedNotFound();
20+
// Requested price is stale.
21+
error StalePrice();
22+
// Given message is not a valid Wormhole VAA.
23+
error InvalidWormholeVaa();
24+
// Governance message is invalid (e.g., deserialization error).
25+
error InvalidGovernanceMessage();
26+
// Governance message is not for this contract.
27+
error InvalidGovernanceTarget();
28+
// Governance message is coming from an invalid data source.
29+
error InvalidGovernanceDataSource();
30+
// Governance message is old.
31+
error OldGovernanceMessage();
32+
}

abis/AbstractPyth.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,19 @@
11
[
2+
{
3+
"inputs": [],
4+
"name": "InvalidArgument",
5+
"type": "error"
6+
},
7+
{
8+
"inputs": [],
9+
"name": "NoFreshUpdate",
10+
"type": "error"
11+
},
12+
{
13+
"inputs": [],
14+
"name": "StalePrice",
15+
"type": "error"
16+
},
217
{
318
"anonymous": false,
419
"inputs": [

abis/MockPyth.json

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,36 @@
1515
"stateMutability": "nonpayable",
1616
"type": "constructor"
1717
},
18+
{
19+
"inputs": [],
20+
"name": "InsufficientFee",
21+
"type": "error"
22+
},
23+
{
24+
"inputs": [],
25+
"name": "InvalidArgument",
26+
"type": "error"
27+
},
28+
{
29+
"inputs": [],
30+
"name": "NoFreshUpdate",
31+
"type": "error"
32+
},
33+
{
34+
"inputs": [],
35+
"name": "PriceFeedNotFound",
36+
"type": "error"
37+
},
38+
{
39+
"inputs": [],
40+
"name": "PriceFeedNotFoundWithinRange",
41+
"type": "error"
42+
},
43+
{
44+
"inputs": [],
45+
"name": "StalePrice",
46+
"type": "error"
47+
},
1848
{
1949
"anonymous": false,
2050
"inputs": [

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@pythnetwork/pyth-sdk-solidity",
3-
"version": "2.1.0",
3+
"version": "2.2.0",
44
"description": "Read prices from the Pyth oracle",
55
"repository": {
66
"type": "git",

0 commit comments

Comments
 (0)