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 }