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

Abehjati/refactor-interface-release #27

Merged
merged 9 commits into from
Sep 21, 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
51 changes: 22 additions & 29 deletions AbstractPyth.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,58 +7,50 @@ import "./IPyth.sol";
abstract contract AbstractPyth is IPyth {
/// @notice Returns the price feed with given id.
/// @dev Reverts if the price does not exist.
/// @param id The Pyth Price Feed ID of which to fetch the current price and confidence interval.
/// @param id The Pyth Price Feed ID of which to fetch the PriceFeed.
function queryPriceFeed(bytes32 id) public view virtual returns (PythStructs.PriceFeed memory priceFeed);

/// @notice Returns true if a price feed with the given id exists.
/// @param id The Pyth Price Feed ID of which to check its existence.
function priceFeedExists(bytes32 id) public view virtual returns (bool exists);

/// @notice Returns the period (in seconds) that a price feed is considered valid since its publish time
function getValidTimePeriod() public view virtual returns (uint validTimePeriod);

function getCurrentPrice(bytes32 id) external view override returns (PythStructs.Price memory price) {
uint64 publishTime;
(price, publishTime) = getLatestAvailablePriceUnsafe(id);

require(diff(block.timestamp, publishTime) <= getValidTimePeriod(), "current price unavailable");

return price;
function getPrice(bytes32 id) external view override returns (PythStructs.Price memory price) {
return getPriceNoOlderThan(id, getValidTimePeriod());
}

function getEmaPrice(bytes32 id) external view override returns (PythStructs.Price memory price) {
return getEmaPriceNoOlderThan(id, getValidTimePeriod());
}

function getPriceUnsafe(bytes32 id) public view override returns (PythStructs.Price memory price) {
PythStructs.PriceFeed memory priceFeed = queryPriceFeed(id);
return priceFeed.price;
}

function getPriceNoOlderThan(bytes32 id, uint age) public view override returns (PythStructs.Price memory price) {
price = getPriceUnsafe(id);

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

price.price = priceFeed.emaPrice;
price.conf = priceFeed.emaConf;
price.expo = priceFeed.expo;
return price;
}

function getLatestAvailablePriceUnsafe(bytes32 id) public view override returns (PythStructs.Price memory price, uint64 publishTime) {
function getEmaPriceUnsafe(bytes32 id) public view override returns (PythStructs.Price memory price) {
PythStructs.PriceFeed memory priceFeed = queryPriceFeed(id);

price.expo = priceFeed.expo;
if (priceFeed.status == PythStructs.PriceStatus.TRADING) {
price.price = priceFeed.price;
price.conf = priceFeed.conf;
return (price, priceFeed.publishTime);
}

price.price = priceFeed.prevPrice;
price.conf = priceFeed.prevConf;
return (price, priceFeed.prevPublishTime);
return priceFeed.emaPrice;
}

function getLatestAvailablePriceWithinDuration(bytes32 id, uint64 duration) external view override returns (PythStructs.Price memory price) {
uint64 publishTime;
(price, publishTime) = getLatestAvailablePriceUnsafe(id);
function getEmaPriceNoOlderThan(bytes32 id, uint age) public view override returns (PythStructs.Price memory price) {
price = getEmaPriceUnsafe(id);

require(diff(block.timestamp, publishTime) <= duration, "No available price within given duration");
require(diff(block.timestamp, price.publishTime) <= age, "no ema price available which is recent enough");

return price;
}


function diff(uint x, uint y) internal pure returns (uint) {
if (x > y) {
return x - y;
Expand All @@ -75,8 +67,9 @@ abstract contract AbstractPyth is IPyth {

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

Expand Down
62 changes: 41 additions & 21 deletions IPyth.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ interface IPyth {
/// @param sequenceNumber Sequence number of the batch price update containing this price.
/// @param lastPublishTime Publish time of the previously stored price.
/// @param publishTime Publish time of the given price update.
/// @param price Current price of the given price update.
/// @param conf Current confidence interval of the given price update.
event PriceFeedUpdate(bytes32 indexed id, bool indexed fresh, uint16 chainId, uint64 sequenceNumber, uint64 lastPublishTime, uint64 publishTime, int64 price, uint64 conf);
/// @param price Price of the given price update.
/// @param conf Confidence interval of the given price update.
event PriceFeedUpdate(bytes32 indexed id, bool indexed fresh, uint16 chainId, uint64 sequenceNumber, uint lastPublishTime, uint publishTime, int64 price, uint64 conf);

/// @dev Emitted when a batch price update is processed successfully.
/// @param chainId ID of the source chain that the batch price update comes from.
Expand All @@ -32,38 +32,58 @@ interface IPyth {
/// @param fee Amount of paid fee for updating the prices.
event UpdatePriceFeeds(address indexed sender, uint batchCount, uint fee);

/// @notice Returns the current price and confidence interval.
/// @dev Reverts if the current price is not available.
/// @param id The Pyth Price Feed ID of which to fetch the current price and confidence interval.
/// @notice Returns the period (in seconds) that a price feed is considered valid since its publish time
function getValidTimePeriod() external view returns (uint validTimePeriod);

/// @notice Returns the price and confidence interval.
/// @dev Reverts if the price has not been updated within the last `getValidTimePeriod()` seconds.
/// @param id The Pyth Price Feed ID of which to fetch the price and confidence interval.
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
function getCurrentPrice(bytes32 id) external view returns (PythStructs.Price memory price);
function getPrice(bytes32 id) external view returns (PythStructs.Price memory price);

/// @notice Returns the exponential moving average price and confidence interval.
/// @dev Reverts if the current exponential moving average price is not available.
/// @param id The Pyth Price Feed ID of which to fetch the current price and confidence interval.
/// @notice Returns the exponentially-weighted moving average price and confidence interval.
/// @dev Reverts if the EMA price is not available.
/// @param id The Pyth Price Feed ID of which to fetch the EMA price and confidence interval.
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
function getEmaPrice(bytes32 id) external view returns (PythStructs.Price memory price);

/// @notice Returns the latest available price, along with the timestamp when it was generated.
/// @dev This function returns the same price as `getCurrentPrice` in the case where a price was available
/// at the time this `PriceFeed` was published (`publish_time`). However, if a price was not available
/// at that time, this function returns the price from the latest time at which the price was available.
/// @notice Returns the price of a price feed without any sanity checks.
/// @dev This function returns the most recent price update in this contract without any recency checks.
/// This function is unsafe as the returned price update may be arbitrarily far in the past.
///
/// Users of this function should check the `publishTime` in the price to ensure that the returned price is
/// sufficiently recent for their application. If you are considering using this function, it may be
/// safer / easier to use either `getPrice` or `getPriceNoOlderThan`.
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
function getPriceUnsafe(bytes32 id) external view returns (PythStructs.Price memory price);

/// @notice Returns the price that is no older than `age` seconds of the current time.
/// @dev This function is a sanity-checked version of `getPriceUnsafe` which is useful in
/// applications that require a sufficiently-recent price. Reverts if the price wasn't updated sufficiently
/// recently.
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
function getPriceNoOlderThan(bytes32 id, uint age) external view returns (PythStructs.Price memory price);

/// @notice Returns the exponentially-weighted moving average price of a price feed without any sanity checks.
/// @dev This function returns the same price as `getEmaPrice` in the case where the price is available.
/// However, if the price is not recent this function returns the latest available price.
///
/// The returned price can be from arbitrarily far in the past; this function makes no guarantees that
/// the returned price is recent or useful for any particular application.
///
/// Users of this function should check the returned timestamp to ensure that the returned price is
/// Users of this function should check the `publishTime` in the price to ensure that the returned price is
/// sufficiently recent for their application. If you are considering using this function, it may be
/// safer / easier to use either `getCurrentPrice` or `getLatestAvailablePriceWithinDuration`.
/// safer / easier to use either `getEmaPrice` or `getEmaPriceNoOlderThan`.
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
/// @return publishTime - the UNIX timestamp of when this price was computed.
function getLatestAvailablePriceUnsafe(bytes32 id) external view returns (PythStructs.Price memory price, uint64 publishTime);
function getEmaPriceUnsafe(bytes32 id) external view returns (PythStructs.Price memory price);

/// @notice Returns the latest price as long as it was updated within `duration` seconds of the current time.
/// @dev This function is a sanity-checked version of `getLatestAvailablePriceUnchecked` which is useful in
/// @notice Returns the exponentially-weighted moving average price that is no older than `age` seconds
/// of the current time.
/// @dev This function is a sanity-checked version of `getEmaPriceUnsafe` which is useful in
/// applications that require a sufficiently-recent price. Reverts if the price wasn't updated sufficiently
/// recently.
function getLatestAvailablePriceWithinDuration(bytes32 id, uint64 duration) external view returns (PythStructs.Price memory price);
/// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
function getEmaPriceNoOlderThan(bytes32 id, uint age) external view returns (PythStructs.Price memory price);

/// @notice Update price feeds with given update messages.
/// This method requires the caller to pay a fee in wei; the required fee can be computed by calling
Expand Down
37 changes: 15 additions & 22 deletions MockPyth.sol
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,17 @@ contract MockPyth is AbstractPyth {
PythStructs.PriceFeed memory priceFeed = abi.decode(updateData[i], (PythStructs.PriceFeed));

bool fresh = false;
uint64 lastPublishTime = priceFeeds[priceFeed.id].publishTime;
uint lastPublishTime = priceFeeds[priceFeed.id].price.publishTime;

if (lastPublishTime < priceFeed.publishTime) {
if (lastPublishTime < priceFeed.price.publishTime) {
// Price information is more recent than the existing price information.
fresh = true;
priceFeeds[priceFeed.id] = priceFeed;
freshPrices += 1;
}

emit PriceFeedUpdate(priceFeed.id, fresh, chainId, sequenceNumber, priceFeed.publishTime,
lastPublishTime, priceFeed.price, priceFeed.conf);
emit PriceFeedUpdate(priceFeed.id, fresh, chainId, sequenceNumber, priceFeed.price.publishTime,
lastPublishTime, priceFeed.price.price, priceFeed.price.conf);
}

// In the real contract, the input of this function contains multiple batches that each contain multiple prices.
Expand All @@ -83,30 +83,23 @@ contract MockPyth is AbstractPyth {
int64 price,
uint64 conf,
int32 expo,
uint8 status,
int64 emaPrice,
uint64 emaConf,
uint64 publishTime,
int64 prevPrice,
uint64 prevConf,
uint64 prevPublishTime
uint64 publishTime
) public pure returns (bytes memory priceFeedData) {
PythStructs.PriceFeed memory priceFeed;

priceFeed.id = id;
priceFeed.productId = id;
priceFeed.price = price;
priceFeed.conf = conf;
priceFeed.expo = expo;
priceFeed.status = PythStructs.PriceStatus(status);
priceFeed.maxNumPublishers = 10;
priceFeed.numPublishers = 10;
priceFeed.emaPrice = emaPrice;
priceFeed.emaConf = emaConf;
priceFeed.publishTime = publishTime;
priceFeed.prevPrice = prevPrice;
priceFeed.prevConf = prevConf;
priceFeed.prevPublishTime = prevPublishTime;

priceFeed.price.price = price;
priceFeed.price.conf = conf;
priceFeed.price.expo = expo;
priceFeed.price.publishTime = publishTime;

priceFeed.emaPrice.price = emaPrice;
priceFeed.emaPrice.conf = emaConf;
priceFeed.emaPrice.expo = expo;
priceFeed.emaPrice.publishTime = publishTime;

priceFeedData = abi.encode(priceFeed);
}
Expand Down
46 changes: 6 additions & 40 deletions PythStructs.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,51 +18,17 @@ contract PythStructs {
uint64 conf;
// Price exponent
int32 expo;
// Unix timestamp describing when the price was published
uint publishTime;
}

// PriceFeed represents a current aggregate price from pyth publisher feeds.
struct PriceFeed {
// The price ID.
bytes32 id;
// Product account key.
bytes32 productId;
// The current price.
int64 price;
// Confidence interval around the price.
uint64 conf;
// Price exponent.
int32 expo;
// Status of price.
PriceStatus status;
// Maximum number of allowed publishers that can contribute to a price.
uint32 maxNumPublishers;
// Number of publishers that made up current aggregate.
uint32 numPublishers;
// Exponentially moving average price.
int64 emaPrice;
// Exponentially moving average confidence interval.
uint64 emaConf;
// Unix timestamp describing when the price was published
uint64 publishTime;
// Price of previous price with TRADING status
int64 prevPrice;
// Confidence interval of previous price with TRADING status
uint64 prevConf;
// Unix timestamp describing when the previous price with TRADING status was published
uint64 prevPublishTime;
}

/* PriceStatus represents the availability status of a price feed.
UNKNOWN: The price feed is not currently updating for an unknown reason.
TRADING: The price feed is updating as expected.
HALTED: The price feed is not currently updating because trading in the product has been halted.
AUCTION: The price feed is not currently updating because an auction is setting the price.
*/
enum PriceStatus {
UNKNOWN,
TRADING,
HALTED,
AUCTION
// Latest available price
Price price;
// Latest available exponentially-weighted moving average price
Price emaPrice;
}

}
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ npm install @pythnetwork/pyth-sdk-solidity

To consume prices you should use the [`IPyth`](IPyth.sol) interface. Please make sure to read the documentation of this interface in order to use the prices safely.

For example, to read the latest price, call [`getCurrentPrice`](IPyth.sol) with the Price ID of the price feed you're interested in. The price feeds available on each chain are listed [below](#target-chains).
For example, to read the latest price, call [`getPrice`](IPyth.sol) with the Price ID of the price feed you're interested in. The price feeds available on each chain are listed [below](#target-chains).

```solidity
// SPDX-License-Identifier: MIT
Expand All @@ -38,7 +38,7 @@ contract ExampleContract {
pyth.updatePriceFeeds(priceUpdateData);

bytes32 priceID = 0xf9c0172ba10dfa4d19088d94f5bf61d3b54d5bd7483a322a982e1373ee8ea31b;
return pyth.getCurrentPrice(priceID);
return pyth.getPrice(priceID);
}
}
```
Expand Down
Loading