Modify PriceInfo struct for gas optimization (#373)

This PR removes unneeded PriceInfo fields and rearranges the struct in a way that it takes less storage.
This commit is contained in:
Ali Behjati 2022-11-04 11:41:01 +01:00 committed by GitHub
parent 61b84c84ac
commit 61651a10f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 136 additions and 30 deletions

View File

@ -41,14 +41,14 @@ abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
PythInternalStructs.PriceInfo memory latestPrice = latestPriceInfo(attestation.priceId);
bool fresh = false;
if(newPriceInfo.priceFeed.price.publishTime > latestPrice.priceFeed.price.publishTime) {
if(newPriceInfo.price.publishTime > latestPrice.price.publishTime) {
freshPrices += 1;
fresh = true;
setLatestPriceInfo(attestation.priceId, newPriceInfo);
}
emit PriceFeedUpdate(attestation.priceId, fresh, vm.emitterChainId, vm.sequence, latestPrice.priceFeed.price.publishTime,
newPriceInfo.priceFeed.price.publishTime, newPriceInfo.priceFeed.price.price, newPriceInfo.priceFeed.price.conf);
emit PriceFeedUpdate(attestation.priceId, fresh, vm.emitterChainId, vm.sequence, latestPrice.price.publishTime,
newPriceInfo.price.publishTime, newPriceInfo.price.price, newPriceInfo.price.conf);
}
emit BatchPriceFeedUpdate(vm.emitterChainId, vm.sequence, batch.attestations.length, freshPrices);
@ -76,32 +76,27 @@ abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
return singleUpdateFeeInWei() * updateData.length;
}
function createNewPriceInfo(PythInternalStructs.PriceAttestation memory pa) private view returns (PythInternalStructs.PriceInfo memory info) {
info.attestationTime = pa.attestationTime;
info.arrivalTime = block.timestamp;
info.arrivalBlock = block.number;
info.priceFeed.id = pa.priceId;
function createNewPriceInfo(PythInternalStructs.PriceAttestation memory pa) private pure returns (PythInternalStructs.PriceInfo memory info) {
PythInternalStructs.PriceAttestationStatus status = PythInternalStructs.PriceAttestationStatus(pa.status);
if (status == PythInternalStructs.PriceAttestationStatus.TRADING) {
info.priceFeed.price.price = pa.price;
info.priceFeed.price.conf = pa.conf;
info.priceFeed.price.publishTime = pa.publishTime;
info.priceFeed.emaPrice.publishTime = pa.publishTime;
info.price.price = pa.price;
info.price.conf = pa.conf;
info.price.publishTime = pa.publishTime;
info.emaPrice.publishTime = pa.publishTime;
} else {
info.priceFeed.price.price = pa.prevPrice;
info.priceFeed.price.conf = pa.prevConf;
info.priceFeed.price.publishTime = pa.prevPublishTime;
info.price.price = pa.prevPrice;
info.price.conf = pa.prevConf;
info.price.publishTime = pa.prevPublishTime;
// The EMA is last updated when the aggregate had trading status,
// so, we use prev_publish_time (the time when the aggregate last had trading status).
info.priceFeed.emaPrice.publishTime = pa.prevPublishTime;
info.emaPrice.publishTime = pa.prevPublishTime;
}
info.priceFeed.price.expo = pa.expo;
info.priceFeed.emaPrice.price = pa.emaPrice;
info.priceFeed.emaPrice.conf = pa.emaConf;
info.priceFeed.emaPrice.expo = pa.expo;
info.price.expo = pa.expo;
info.emaPrice.price = pa.emaPrice;
info.emaPrice.conf = pa.emaConf;
info.emaPrice.expo = pa.expo;
return info;
}
@ -226,14 +221,23 @@ abstract contract Pyth is PythGetters, PythSetters, AbstractPyth {
function queryPriceFeed(bytes32 id) public view override returns (PythStructs.PriceFeed memory priceFeed){
// Look up the latest price info for the given ID
PythInternalStructs.PriceInfo memory info = latestPriceInfo(id);
require(info.priceFeed.id != 0, "price feed for the given id is not pushed or does not exist");
require(info.price.publishTime != 0, "price feed for the given id is not pushed or does not exist");
return info.priceFeed;
priceFeed.id = id;
priceFeed.price.price = info.price.price;
priceFeed.price.conf = info.price.conf;
priceFeed.price.expo = info.price.expo;
priceFeed.price.publishTime = uint(info.price.publishTime);
priceFeed.emaPrice.price = info.emaPrice.price;
priceFeed.emaPrice.conf = info.emaPrice.conf;
priceFeed.emaPrice.expo = info.emaPrice.expo;
priceFeed.emaPrice.publishTime = uint(info.emaPrice.publishTime);
}
function priceFeedExists(bytes32 id) public override view returns (bool) {
PythInternalStructs.PriceInfo memory info = latestPriceInfo(id);
return (info.priceFeed.id != 0);
return (info.price.publishTime != 0);
}
function getValidTimePeriod() public override view returns (uint) {

View File

@ -0,0 +1,89 @@
// contracts/PythDeprecatedStructs.sol
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
// This contract contains self contained structs of all our deprecated structs.
// When deprecating the structs, make sure that there be no dependency to
// the sdk as the sdk might change.
//
// By storing these structs, we keep deprecated fields definitions correctly. Then,
// in the future, we can use them to cleanup their storage and redeem some gas back.
contract PythDeprecatedStructs {
// Structs related to the _deprecatedLatestPriceInfoV1
enum DeprecatedPriceStatusV1 {
UNKNOWN,
TRADING,
HALTED,
AUCTION
}
struct DeprecatedPriceFeedV1 {
// 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.
DeprecatedPriceStatusV1 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;
}
struct DeprecatedPriceInfoV1 {
uint256 attestationTime;
uint256 arrivalTime;
uint256 arrivalBlock;
DeprecatedPriceFeedV1 priceFeed;
}
// Structs related to the _deprecatedLatestPriceInfoV2
struct DeprecatedPriceV2 {
// Price
int64 price;
// Confidence interval around the price
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 DeprecatedPriceFeedV2 {
// The price ID.
bytes32 id;
// Latest available price
DeprecatedPriceV2 price;
// Latest available exponentially-weighted moving average price
DeprecatedPriceV2 emaPrice;
}
struct DeprecatedPriceInfoV2 {
uint256 attestationTime;
uint256 arrivalTime;
uint256 arrivalBlock;
DeprecatedPriceFeedV2 priceFeed;
}
}

View File

@ -43,11 +43,19 @@ contract PythInternalStructs {
uint64 prevConf;
}
struct InternalPrice {
int64 price;
uint64 conf;
uint64 publishTime;
int32 expo;
}
struct PriceInfo {
uint256 attestationTime;
uint256 arrivalTime;
uint256 arrivalBlock;
PythStructs.PriceFeed priceFeed;
// slot 1
InternalPrice price;
// slot 2
InternalPrice emaPrice;
}
struct DataSource {

View File

@ -4,6 +4,7 @@
pragma solidity ^0.8.0;
import "./PythInternalStructs.sol";
import "./PythDeprecatedStructs.sol";
contract PythStorage {
struct State {
@ -12,7 +13,7 @@ contract PythStorage {
bytes32 _deprecatedPyth2WormholeEmitter; // Ditto
// After a backward-incompatible change in PriceFeed this mapping got deprecated.
mapping(bytes32 => PythInternalStructs.PriceInfo) _deprecatedLatestPriceInfo;
mapping(bytes32 => PythDeprecatedStructs.DeprecatedPriceInfoV1) _deprecatedLatestPriceInfoV1;
// For tracking all active emitter/chain ID pairs
PythInternalStructs.DataSource[] validDataSources;
@ -38,11 +39,15 @@ contract PythStorage {
// Mapping of cached price information
// priceId => PriceInfo
mapping(bytes32 => PythInternalStructs.PriceInfo) latestPriceInfo;
mapping(bytes32 => PythDeprecatedStructs.DeprecatedPriceInfoV2) _deprecatedLatestPriceInfoV2;
// Index of the governance data source, increased each time the governance data source
// changes.
uint32 governanceDataSourceIndex;
// Mapping of cached price information
// priceId => PriceInfo
mapping(bytes32 => PythInternalStructs.PriceInfo) latestPriceInfo;
}
}