diff --git a/contracts/src/v0.8/dev/automation/upkeeps/MercuryRegistry.sol b/contracts/src/v0.8/dev/automation/upkeeps/MercuryRegistry.sol index 0b665e72c79..de7353e5cb1 100644 --- a/contracts/src/v0.8/dev/automation/upkeeps/MercuryRegistry.sol +++ b/contracts/src/v0.8/dev/automation/upkeeps/MercuryRegistry.sol @@ -28,6 +28,9 @@ import "../../../ChainSpecificUtil.sol"; | - Optimize gas consumption. | -+---------------------------------------------------------------------------------------------------------------------*/ contract MercuryRegistry is AutomationCompatibleInterface, FeedLookupCompatibleInterface { + error DuplicateFeed(string feedId); + error FeedNotActive(string feedId); + // Feed object used for storing feed data. // not included but contained in reports: // - blocknumberUpperBound @@ -41,6 +44,7 @@ contract MercuryRegistry is AutomationCompatibleInterface, FeedLookupCompatibleI int192 ask; // the current ask price of the feed string feedName; // the name of the feed string feedId; // the id of the feed (hex encoded) + bool active; // true if the feed is being actively updated, otherwise false } // Report object obtained from off-chain Mercury server. @@ -78,24 +82,49 @@ contract MercuryRegistry is AutomationCompatibleInterface, FeedLookupCompatibleI ) { i_verifier = IVerifierProxy(verifier); - // Ensure correctly formatted constructor arguments. - require(feedIds.length == feedNames.length, "incorrect constructor args"); - // Store desired deviation threshold and staleness seconds. s_deviationPercentagePPM = deviationPercentagePPM; s_stalenessSeconds = stalenessSeconds; // Store desired feeds. + setFeeds(feedIds, feedNames); + } + + function setFeeds(string[] memory feedIds, string[] memory feedNames) public { + // Ensure correctly formatted constructor arguments. + require(feedIds.length == feedNames.length, "incorrectly formatted feeds"); + + // Clear prior feeds. + for (uint256 i = 0; i < s_feeds.length; i++) { + s_feedMapping[s_feeds[i]].active = false; + } + + // Assign new feeds. + for (uint256 i = 0; i < feedIds.length; i++) { + string memory feedId = feedIds[i]; + if (s_feedMapping[feedId].active) { + revert DuplicateFeed(feedId); + } + + s_feedMapping[feedId].feedName = feedNames[i]; + s_feedMapping[feedId].feedId = feedId; + s_feedMapping[feedId].active = true; + } s_feeds = feedIds; + } + + function addFeeds(string[] memory feedIds, string[] memory feedNames) external { for (uint256 i = 0; i < feedIds.length; i++) { - s_feedMapping[s_feeds[i]] = Feed({ - feedName: feedNames[i], - feedId: feedIds[i], - price: 0, - bid: 0, - ask: 0, - observationsTimestamp: 0 - }); + string memory feedId = feedIds[i]; + if (s_feedMapping[feedId].active) { + revert DuplicateFeed(feedId); + } + + s_feedMapping[feedId].feedName = feedNames[i]; + s_feedMapping[feedId].feedId = feedId; + s_feedMapping[feedId].active = true; + + s_feeds.push(feedId); } } diff --git a/contracts/test/v0.8/foundry/automation/MercuryRegistry.t.sol b/contracts/test/v0.8/foundry/automation/MercuryRegistry.t.sol index 5d6d4a8ac0d..3ef311b22bc 100644 --- a/contracts/test/v0.8/foundry/automation/MercuryRegistry.t.sol +++ b/contracts/test/v0.8/foundry/automation/MercuryRegistry.t.sol @@ -48,14 +48,30 @@ contract MercuryRegistryTest is Test { vm.selectFork(vm.createFork("https://arbitrum-goerli.publicnode.com")); vm.rollFork(BLOCK_NUMBER); - // Create Mercury Registry. + // Use a BTC feed and ETH feed. feedIds = new string[](2); feedIds[0] = s_BTCUSDFeedId; feedIds[1] = s_ETHUSDFeedId; - string[] memory feedNames = new string[](2); - feedNames[0] = "BTC/USD"; - feedNames[1] = "ETH/USD"; - s_testRegistry = new MercuryRegistry(feedIds, feedNames, VERIFIER, DEVIATION_THRESHOLD, STALENESS_SECONDS); + + // Initialize with BTC feed. + string[] memory initialFeedIds = new string[](1); + initialFeedIds[0] = feedIds[0]; + string[] memory initialFeedNames = new string[](1); + initialFeedNames[0] = "BTC/USD"; + s_testRegistry = new MercuryRegistry( + initialFeedIds, + initialFeedNames, + VERIFIER, + DEVIATION_THRESHOLD, + STALENESS_SECONDS + ); + + // Add ETH feed. + string[] memory addedFeedIds = new string[](1); + addedFeedIds[0] = feedIds[1]; + string[] memory addedFeedNames = new string[](1); + addedFeedNames[0] = "ETH/USD"; + s_testRegistry.addFeeds(addedFeedIds, addedFeedNames); } function testMercuryRegistry() public { @@ -92,7 +108,8 @@ contract MercuryRegistryTest is Test { int192 bid, int192 ask, string memory feedName, - string memory localFeedId + string memory localFeedId, + bool active ) = s_testRegistry.s_feedMapping(s_BTCUSDFeedId); assertEq(observationsTimestamp, 1692732568); // Tuesday, August 22, 2023 7:29:28 PM assertEq(bid, 2585674416498); // $25,856.74416498 @@ -100,6 +117,7 @@ contract MercuryRegistryTest is Test { assertEq(ask, 2585747836943); // $25,857.47836943 assertEq(feedName, "BTC/USD"); assertEq(localFeedId, s_BTCUSDFeedId); + assertEq(active, true); // Obtain mercury report off-chain (for August 23 BTC/USD price & ETH/USD price) values = new bytes[](2); @@ -185,7 +203,8 @@ contract MercuryRegistryTest is Test { int192 bid, int192 ask, string memory feedName, - string memory localFeedId + string memory localFeedId, + bool active ) = s_testRegistry.s_feedMapping(s_BTCUSDFeedId); assertEq(observationsTimestamp, 1692732568); // Tuesday, August 22, 2023 7:29:28 PM assertEq(bid, 2585674416498); // $25,856.74416498 @@ -193,6 +212,7 @@ contract MercuryRegistryTest is Test { assertEq(ask, 2585747836943); // $25,857.47836943 assertEq(feedName, "BTC/USD"); assertEq(localFeedId, s_BTCUSDFeedId); + assertEq(active, true); // Obtain mercury report off-chain (for August 23 BTC/USD price & ETH/USD price) values = new bytes[](2);