diff --git a/licenses/zerolend-pyth-oracles.md b/licenses/zerolend-pyth-oracles.md new file mode 100644 index 00000000..d8d1dbb3 --- /dev/null +++ b/licenses/zerolend-pyth-oracles.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Antonio + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/package-lock.json b/package-lock.json index e8fa3fcb..3e3ecab4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -59449,7 +59449,7 @@ }, "target_chains/ethereum/sdk/solidity": { "name": "@pythnetwork/pyth-sdk-solidity", - "version": "3.0.0", + "version": "3.1.0", "license": "Apache-2.0", "devDependencies": { "abi_generator": "*", @@ -59499,7 +59499,7 @@ }, "target_chains/solana/sdk/js/pyth_solana_receiver": { "name": "@pythnetwork/pyth-solana-receiver", - "version": "0.4.0", + "version": "0.5.0", "license": "Apache-2.0", "dependencies": { "@coral-xyz/anchor": "^0.29.0", diff --git a/target_chains/ethereum/sdk/solidity/PythAggregatorV3.sol b/target_chains/ethereum/sdk/solidity/PythAggregatorV3.sol new file mode 100644 index 00000000..1d8ba450 --- /dev/null +++ b/target_chains/ethereum/sdk/solidity/PythAggregatorV3.sol @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: Apache 2 +pragma solidity ^0.8.0; + +import {PythStructs} from "./PythStructs.sol"; +import {IPyth} from "./IPyth.sol"; + +// This interface is forked from the Zerolend Adapter found here: +// https://github.com/zerolend/pyth-oracles/blob/master/contracts/PythAggregatorV3.sol +// Original license found under licenses/zerolend-pyth-oracles.md + +/** + * @title A port of the ChainlinkAggregatorV3 interface that supports Pyth price feeds + * @notice This does not store any roundId information on-chain. Please review the code before using this implementation. + * Users should deploy an instance of this contract to wrap every price feed id that they need to use. + */ +contract PythAggregatorV3 { + bytes32 public priceId; + IPyth public pyth; + + constructor(address _pyth, bytes32 _priceId) { + priceId = _priceId; + pyth = IPyth(_pyth); + } + + // Wrapper function to update the underlying Pyth price feeds. Not part of the AggregatorV3 interface but useful. + function updateFeeds(bytes[] calldata priceUpdateData) public payable { + // Update the prices to the latest available values and pay the required fee for it. The `priceUpdateData` data + // should be retrieved from our off-chain Price Service API using the `pyth-evm-js` package. + // See section "How Pyth Works on EVM Chains" below for more information. + uint fee = pyth.getUpdateFee(priceUpdateData); + pyth.updatePriceFeeds{value: fee}(priceUpdateData); + + // refund remaining eth + payable(msg.sender).call{value: address(this).balance}(""); + } + + function decimals() public view virtual returns (uint8) { + PythStructs.Price memory price = pyth.getPriceUnsafe(priceId); + return uint8(-1 * int8(price.expo)); + } + + function description() public pure returns (string memory) { + return "A port of a chainlink aggregator powered by pyth network feeds"; + } + + function version() public pure returns (uint256) { + return 1; + } + + function latestAnswer() public view virtual returns (int256) { + PythStructs.Price memory price = pyth.getPriceUnsafe(priceId); + return int256(price.price); + } + + function latestTimestamp() public view returns (uint256) { + PythStructs.Price memory price = pyth.getPriceUnsafe(priceId); + return price.publishTime; + } + + function latestRound() public view returns (uint256) { + // use timestamp as the round id + return latestTimestamp(); + } + + function getAnswer(uint256) public view returns (int256) { + return latestAnswer(); + } + + function getTimestamp(uint256) external view returns (uint256) { + return latestTimestamp(); + } + + function getRoundData( + uint80 _roundId + ) + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + PythStructs.Price memory price = pyth.getPriceUnsafe(priceId); + return ( + _roundId, + int256(price.price), + price.publishTime, + price.publishTime, + _roundId + ); + } + + function latestRoundData() + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) + { + PythStructs.Price memory price = pyth.getPriceUnsafe(priceId); + roundId = uint80(price.publishTime); + return ( + roundId, + int256(price.price), + price.publishTime, + price.publishTime, + roundId + ); + } +} diff --git a/target_chains/ethereum/sdk/solidity/package.json b/target_chains/ethereum/sdk/solidity/package.json index 4a9d3e72..ffd46e96 100644 --- a/target_chains/ethereum/sdk/solidity/package.json +++ b/target_chains/ethereum/sdk/solidity/package.json @@ -1,6 +1,6 @@ { "name": "@pythnetwork/pyth-sdk-solidity", - "version": "3.0.0", + "version": "3.1.0", "description": "Read prices from the Pyth oracle", "repository": { "type": "git",