pyth-crosschain/target_chains/ethereum/sdk/solidity/MockPyth.sol

140 lines
4.8 KiB
Solidity
Raw Normal View History

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
import "./AbstractPyth.sol";
import "./PythStructs.sol";
import "./PythErrors.sol";
contract MockPyth is AbstractPyth {
mapping(bytes32 => PythStructs.PriceFeed) priceFeeds;
uint64 sequenceNumber;
uint singleUpdateFeeInWei;
uint validTimePeriod;
constructor(uint _validTimePeriod, uint _singleUpdateFeeInWei) {
singleUpdateFeeInWei = _singleUpdateFeeInWei;
validTimePeriod = _validTimePeriod;
}
function queryPriceFeed(
bytes32 id
) public view override returns (PythStructs.PriceFeed memory priceFeed) {
if (priceFeeds[id].id == 0) revert PythErrors.PriceFeedNotFound();
return priceFeeds[id];
}
function priceFeedExists(bytes32 id) public view override returns (bool) {
return (priceFeeds[id].id != 0);
}
function getValidTimePeriod() public view override returns (uint) {
return validTimePeriod;
}
// Takes an array of encoded price feeds and stores them.
// You can create this data either by calling createPriceFeedData or
// by using web3.js or ethers abi utilities.
function updatePriceFeeds(
bytes[] calldata updateData
) public payable override {
uint requiredFee = getUpdateFee(updateData);
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.
uint16 chainId = 1;
for (uint i = 0; i < updateData.length; i++) {
PythStructs.PriceFeed memory priceFeed = abi.decode(
updateData[i],
(PythStructs.PriceFeed)
);
uint lastPublishTime = priceFeeds[priceFeed.id].price.publishTime;
if (lastPublishTime < priceFeed.price.publishTime) {
// Price information is more recent than the existing price information.
priceFeeds[priceFeed.id] = priceFeed;
emit PriceFeedUpdate(
priceFeed.id,
uint64(lastPublishTime),
priceFeed.price.price,
priceFeed.price.conf
);
}
}
// In the real contract, the input of this function contains multiple batches that each contain multiple prices.
// This event is emitted when a batch is processed. In this mock contract we consider there is only one batch of prices.
// Each batch has (chainId, sequenceNumber) as it's unique identifier. Here chainId is set to 1 and an increasing sequence number is used.
emit BatchPriceFeedUpdate(chainId, sequenceNumber);
sequenceNumber += 1;
}
function getUpdateFee(
bytes[] calldata updateData
) public view override returns (uint feeAmount) {
return singleUpdateFeeInWei * updateData.length;
}
function parsePriceFeedUpdates(
bytes[] calldata updateData,
bytes32[] calldata priceIds,
uint64 minPublishTime,
uint64 maxPublishTime
) external payable override returns (PythStructs.PriceFeed[] memory feeds) {
uint requiredFee = getUpdateFee(updateData);
if (msg.value < requiredFee) revert PythErrors.InsufficientFee();
feeds = new PythStructs.PriceFeed[](priceIds.length);
for (uint i = 0; i < priceIds.length; i++) {
for (uint j = 0; j < updateData.length; j++) {
feeds[i] = abi.decode(updateData[j], (PythStructs.PriceFeed));
if (feeds[i].id == priceIds[i]) {
uint publishTime = feeds[i].price.publishTime;
if (
minPublishTime <= publishTime &&
publishTime <= maxPublishTime
) {
break;
} else {
feeds[i].id = 0;
}
}
}
if (feeds[i].id != priceIds[i])
revert PythErrors.PriceFeedNotFoundWithinRange();
}
}
function createPriceFeedUpdateData(
bytes32 id,
int64 price,
uint64 conf,
int32 expo,
int64 emaPrice,
uint64 emaConf,
uint64 publishTime
) public pure returns (bytes memory priceFeedData) {
PythStructs.PriceFeed memory priceFeed;
priceFeed.id = id;
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);
}
}