@@ -30,7 +30,7 @@ interface ImplementsPriceFeedType {
3030/// - it does not implement `getTokens`
3131/// - it only allows to fetch staleness period of a currently active price feed
3232interface IPriceOracleV3Legacy {
33- /// @dev Older signature for fetching main and reserve feeds
33+ /// @dev Older signature for fetching main and reserve feeds, reverts if price feed is not set
3434 function priceFeedsRaw (address token , bool reserve ) external view returns (address );
3535}
3636
@@ -90,9 +90,6 @@ contract PriceFeedCompressor is IVersion, Ownable {
9090 /// - `priceFeedTree` is a set of nodes in a tree-like structure that contains detailed info of both feeds
9191 /// from `priceFeedMap` and their underlying feeds, in case former are nested, which can help to determine
9292 /// what underlying feeds should be updated to query the nested one.
93- /// @dev `priceFeedTree` can have duplicate entries since a price feed can both be in `priceFeedMap` for one or
94- /// more (token, reserve) pairs, and serve as an underlying feed in one or more nested feeds.
95- /// If there are two identical nodes in the tree, then subtrees of these nodes are also identical.
9693 function getPriceFeeds (address priceOracle )
9794 external
9895 view
@@ -138,6 +135,10 @@ contract PriceFeedCompressor is IVersion, Ownable {
138135 for (uint256 i; i < priceFeedMapSize; ++ i) {
139136 offset = _loadPriceFeedTree (priceFeedMap[i].priceFeed, priceFeedTree, offset);
140137 }
138+ // trim array to its actual size in case there were duplicates
139+ assembly {
140+ mstore (priceFeedTree, offset)
141+ }
141142 }
142143
143144 // --------- //
@@ -155,9 +156,12 @@ contract PriceFeedCompressor is IVersion, Ownable {
155156 /// @dev Returns `token`'s price feed in the price oracle
156157 function _getPriceFeed (address priceOracle , address token , bool reserve ) internal view returns (address , uint32 ) {
157158 if (IPriceOracleV3 (priceOracle).version () < 3_10 ) {
158- address priceFeed = IPriceOracleV3Legacy (priceOracle).priceFeedsRaw (token, reserve);
159- // legacy oracle does not allow to fetch staleness period of a non-active feed
160- return (priceFeed, 0 );
159+ try IPriceOracleV3Legacy (priceOracle).priceFeedsRaw (token, reserve) returns (address priceFeed ) {
160+ // legacy oracle does not allow to fetch staleness period of a non-active feed
161+ return (priceFeed, 0 );
162+ } catch {
163+ return (address (0 ), 0 );
164+ }
161165 }
162166 PriceFeedParams memory params = reserve
163167 ? IPriceOracleV3 (priceOracle).reservePriceFeedParams (token)
@@ -180,6 +184,12 @@ contract PriceFeedCompressor is IVersion, Ownable {
180184 view
181185 returns (uint256 )
182186 {
187+ // duplicates are possible since price feed can be in `priceFeedMap` for more than one (token, reserve) pair
188+ // or serve as an underlying in more than one nested feed, and the whole subtree can be skipped in this case
189+ for (uint256 i; i < offset; ++ i) {
190+ if (priceFeedTree[i].priceFeed == priceFeed) return offset;
191+ }
192+
183193 PriceFeedTreeNode memory node = _getPriceFeedTreeNode (priceFeed);
184194 priceFeedTree[offset++ ] = node;
185195 for (uint256 i; i < node.underlyingFeeds.length ; ++ i) {
0 commit comments