/ contracts / feeds / PythAggregatorV1.sol
PythAggregatorV1.sol
  1  pragma solidity >=0.6.0;
  2  pragma experimental ABIEncoderV2;
  3  
  4  import "../interfaces/AggregatorV3Interface.sol";
  5  import "../interfaces/external/pyth/IPyth.sol";
  6  import "../utils/MoreMath.sol";
  7  
  8  
  9  contract PythAggregatorV1 is AggregatorV3Interface {
 10  
 11      mapping(uint => uint) rounds;
 12  
 13      uint latestRound;
 14      int[] answers;
 15      uint[] updatedAts;
 16  
 17      bool private lockedRound = true;
 18      bool private lockedAnswers = true;
 19      bool private lockedUpdatedAts = true;
 20  
 21      address _pythOracleAddr;
 22      bytes32 _feedId;
 23  
 24  
 25      constructor(address pythOracleAddr, bytes32 feedId) public {
 26  
 27          /*
 28              oracle addrs: https://docs.pyth.network/pythnet-price-feeds/evm
 29              
 30              Goerli (Ethereum testnet) 0xff1a0f4744e8582DF1aE09D5611b887B6a12925C
 31              Fuji (Avalanche testnet) 0xff1a0f4744e8582DF1aE09D5611b887B6a12925C
 32              Fantom testnet 0xff1a0f4744e8582DF1aE09D5611b887B6a12925C
 33              Mumbai (Polygon testnet) 0xff1a0f4744e8582DF1aE09D5611b887B6a12925C
 34  
 35              feed ids: https://pyth.network/developers/price-feed-ids
 36  
 37              MAINET
 38  
 39              btc/usd: 0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43
 40              eth/usd: 0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace
 41              matic/usd: 0x5de33a9112c2b700b8d30b8a3402c103578ccfa2765696471cc672bd5cf6ac52
 42              avax/usd: 0x93da3352f9f1d105fdfe4971cfa80e9dd777bfc5d0f683ebb6e1294b92137bb7
 43  
 44              TESTNET
 45  
 46              btc/usd: 0xf9c0172ba10dfa4d19088d94f5bf61d3b54d5bd7483a322a982e1373ee8ea31b
 47              eth/usd: 0xca80ba6dc32e08d06f1aa886011eed1d77c77be9eb761cc10d72b7d0a2fd57a6
 48              matic/usd: 0xd2c2c1f2bba8e0964f9589e060c2ee97f5e19057267ac3284caef3bd50bd2cb5
 49              avax/usd: 0xd7566a3ba7f7286ed54f4ae7e983f4420ae0b1e0f3892e11f9c4ab107bbad7b9
 50  
 51          */
 52          _pythOracleAddr = pythOracleAddr;
 53          _feedId = feedId;
 54      }
 55  
 56      function decimals() override external view returns (uint8) {
 57          IPyth.Price memory p = IPyth(_pythOracleAddr).getPriceUnsafe(_feedId);
 58          return uint8(MoreMath.abs(p.expo));
 59      }
 60  
 61      function description() override external view returns (string memory) {
 62          return bytes32ToString(_feedId);
 63      }
 64  
 65      function version() override external view returns (uint256) {
 66  
 67      }
 68  
 69      /* SEEDING FOR INITIALIZATION BELOW */
 70  
 71      function setRoundIds(uint[] calldata rids) external {
 72          require(lockedRound == false && latestRound == 0, "already init round");
 73          for (uint i = 0; i < rids.length; i++) {
 74              rounds[rids[i]] = i;
 75          }
 76  
 77          latestRound = rids[ rids.length - 1];
 78          lockedRound = true;
 79      }
 80  
 81      function setAnswers(int[] calldata ans) external {
 82          require(lockedAnswers == false && answers.length == 0, "already init answers");
 83          answers = ans;
 84          lockedAnswers = true;
 85      }
 86  
 87      function setUpdatedAts(uint[] calldata uts) external {
 88          require(lockedUpdatedAts == false && updatedAts.length == 0, "already init answers");
 89          updatedAts = uts;
 90          lockedUpdatedAts = true;
 91      }
 92  
 93      /* SEEDING FOR INITIALIZATION ABOVE */
 94  
 95      function oracle() external view returns (address) {
 96          return _pythOracleAddr;
 97      }
 98  
 99      function incrementRound() external {
100          appendUpdatedAt();
101          appendAnswer();
102          appendRoundId();
103      }
104  
105      function appendRoundId() private {
106          if (answers.length > 1) {
107              rounds[latestRound++] = answers.length;
108          } else {
109              rounds[latestRound] = answers.length;
110          }
111      }
112  
113      function appendAnswer() private {
114          IPyth.Price memory p = IPyth(_pythOracleAddr).getPriceUnsafe(_feedId);
115          answers.push(p.price);
116      }
117  
118      function appendUpdatedAt() private {
119          IPyth.Price memory p = IPyth(_pythOracleAddr).getPriceUnsafe(_feedId);
120          updatedAts.push(uint(p.publishTime));
121      }
122  
123      function getRoundData(uint80 _roundId)
124          override
125          external
126          view
127          returns
128      (
129          uint80 roundId,
130          int256 answer,
131          uint256,
132          uint256 updatedAt,
133          uint80
134      )
135      {
136          roundId = _roundId;
137          answer = answers[rounds[_roundId]];
138          updatedAt = updatedAts[rounds[_roundId]];
139      }
140  
141      function latestRoundId() public view returns (uint) {
142          return latestRound;
143      }
144  
145      function latestRoundData()
146          override
147          external
148          view
149          returns
150      (
151          uint80 roundId,
152          int256 answer,
153          uint256,
154          uint256 updatedAt,
155          uint80
156      )
157      {
158          IPyth.Price memory p = IPyth(_pythOracleAddr).getPriceUnsafe(_feedId);
159  
160          roundId = uint80(latestRound);
161          answer = p.price;
162          updatedAt = uint(p.publishTime);
163      }
164  
165      function bytes32ToString(bytes32 x) private pure returns (string memory) {
166          bytes memory bytesString = new bytes(32);
167          uint charCount = 0;
168          for (uint j = 0; j < 32; j++) {
169              byte char = byte(bytes32(uint(x) * 2 ** (8 * j)));
170              if (char != 0) {
171                  bytesString[charCount] = char;
172                  charCount++;
173              }
174          }
175          bytes memory bytesStringTrimmed = new bytes(charCount);
176          for (uint j = 0; j < charCount; j++) {
177              bytesStringTrimmed[j] = bytesString[j];
178          }
179          return string(bytesStringTrimmed);
180      }
181  }