Skip to content

Commit 5354a23

Browse files
committed
Handle the case when the data feed is not registered and log warning
1 parent f1d1701 commit 5354a23

File tree

6 files changed

+36
-18
lines changed

6 files changed

+36
-18
lines changed

contracts/contract-imports.sol

-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,3 @@
22
pragma solidity 0.8.18;
33

44
import "@api3/dapi-management/contracts/AirseekerRegistry.sol";
5-
import "@api3/airnode-protocol-v1/contracts/api3-server-v1/Api3ServerV1.sol";

local-test-configuration/monitoring/index.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -715,7 +715,7 @@ <h2>Active data feeds</h2>
715715
decodedDataFeed: decodeDataFeedDetails(dataFeedDetails),
716716
signedApiUrls,
717717
};
718-
console.log('Data feed', dataFeed); // For debugging purposes.
718+
console.info('Data feed', dataFeed); // For debugging purposes.
719719

720720
let signedDatas = [];
721721
for (let i = 0; i < signedApiUrls.length; i++) {
@@ -727,7 +727,7 @@ <h2>Active data feeds</h2>
727727
const signedData = signedApiResponse.data[dataFeed.decodedDataFeed.beacons[i].beaconId];
728728
signedDatas.push({ ...signedData, value: decodeBeaconValue(signedData.encodedValue).toString() });
729729
}
730-
console.log('Signed datas', signedDatas); // For debugging purposes.
730+
console.info('Signed datas', signedDatas); // For debugging purposes.
731731

732732
const newBeaconSetValue = calculateMedian(
733733
signedDatas.map((signedData) => ethers.BigNumber.from(signedData.value))

src/update-feeds-loops/contracts.ts

+19-6
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,11 @@ export const decodeActiveDataFeedCountResponse = (
3232
return activeDataFeedCount.toNumber();
3333
};
3434

35-
export const decodeDataFeedDetails = (dataFeed: string): DecodedDataFeed => {
35+
export const decodeDataFeedDetails = (dataFeed: string): DecodedDataFeed | null => {
36+
// The contract returns empty bytes if the data feed is not registered. See:
37+
// https://github.com/api3dao/dapi-management/blob/f3d39e4707c33c075a8f07aa8f8369f8dc07736f/contracts/AirseekerRegistry.sol#L209
38+
if (dataFeed === '0x') return null;
39+
3640
if (dataFeed.length === 130) {
3741
// (64 [actual bytes] * 2[hex encoding] ) + 2 [for the '0x' preamble]
3842
// This is a hex encoded string, the contract works with bytes directly
@@ -63,7 +67,7 @@ export interface DecodedUpdateParameters {
6367
heartbeatInterval: ethers.BigNumber;
6468
}
6569

66-
export const decodeUpdateParameters = (updateParameters: string) => {
70+
export const decodeUpdateParameters = (updateParameters: string): DecodedUpdateParameters => {
6771
// https://github.com/api3dao/airnode-protocol-v1/blob/5f861715749e182e334c273d6a52c4f2560c7994/contracts/api3-server-v1/extensions/BeaconSetUpdatesWithPsp.sol#L122
6872
const [deviationThresholdInPercentage, deviationReference, heartbeatInterval] = ethers.utils.defaultAbiCoder.decode(
6973
['uint256', 'int224', 'uint256'],
@@ -77,17 +81,28 @@ export const decodeUpdateParameters = (updateParameters: string) => {
7781
};
7882
};
7983

84+
export interface DecodedActiveDataFeedResponse {
85+
dapiName: string | null;
86+
decodedDapiName: string | null;
87+
decodedUpdateParameters: DecodedUpdateParameters;
88+
dataFeedValue: ethers.BigNumber;
89+
dataFeedTimestamp: number;
90+
decodedDataFeed: DecodedDataFeed;
91+
signedApiUrls: string[];
92+
}
93+
8094
export const decodeActiveDataFeedResponse = (
8195
airseekerRegistry: AirseekerRegistry,
8296
activeDataFeedReturndata: string
83-
) => {
97+
): DecodedActiveDataFeedResponse | null => {
8498
const { dapiName, updateParameters, dataFeedValue, dataFeedTimestamp, dataFeedDetails, signedApiUrls } =
8599
airseekerRegistry.interface.decodeFunctionResult('activeDataFeed', activeDataFeedReturndata) as Awaited<
86100
ReturnType<AirseekerRegistry['activeDataFeed']>
87101
>;
88102

89-
// https://github.com/api3dao/dapi-management/pull/3/files#diff-b6941851ebc92dc9691bbf0cb701fe9c4595cb78488c3bb92ad6e4b917719f4fR346
103+
// https://github.com/api3dao/dapi-management/blob/f3d39e4707c33c075a8f07aa8f8369f8dc07736f/contracts/AirseekerRegistry.sol#L162
90104
const decodedDataFeed = decodeDataFeedDetails(dataFeedDetails);
105+
if (!decodedDataFeed) return null;
91106

92107
// The dAPI name will be set to zero (in bytes32) in case the data feed is not a dAPI and is identified by a data feed
93108
// ID.
@@ -103,5 +118,3 @@ export const decodeActiveDataFeedResponse = (
103118
signedApiUrls,
104119
};
105120
};
106-
107-
export type DecodedActiveDataFeedResponse = ReturnType<typeof decodeActiveDataFeedResponse>;

src/update-feeds-loops/update-feeds-loops.test.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -181,12 +181,12 @@ describe(updateFeedsLoopsModule.runUpdateFeeds.name, () => {
181181
const decodedFirstDataFeed = {
182182
...omit(firstDataFeed, ['dataFeedDetails', 'updateParameters']),
183183
decodedUpdateParameters: contractsModule.decodeUpdateParameters(firstDataFeed.updateParameters),
184-
decodedDataFeed: contractsModule.decodeDataFeedDetails(firstDataFeed.dataFeedDetails),
184+
decodedDataFeed: contractsModule.decodeDataFeedDetails(firstDataFeed.dataFeedDetails)!,
185185
};
186186
const decodedThirdDataFeed = {
187187
...omit(thirdDataFeed, ['dataFeedDetails', 'updateParameters']),
188188
decodedUpdateParameters: contractsModule.decodeUpdateParameters(thirdDataFeed.updateParameters),
189-
decodedDataFeed: contractsModule.decodeDataFeedDetails(thirdDataFeed.dataFeedDetails),
189+
decodedDataFeed: contractsModule.decodeDataFeedDetails(thirdDataFeed.dataFeedDetails)!,
190190
};
191191
const airseekerRegistry = generateMockAirseekerRegistry();
192192
jest
@@ -343,7 +343,7 @@ describe(updateFeedsLoopsModule.runUpdateFeeds.name, () => {
343343
describe(updateFeedsLoopsModule.processBatch.name, () => {
344344
it('applies deviationThresholdCoefficient from config', async () => {
345345
const dataFeed = generateActiveDataFeedResponse();
346-
const decodedDataFeed = contractsModule.decodeDataFeedDetails(dataFeed.dataFeedDetails);
346+
const decodedDataFeed = contractsModule.decodeDataFeedDetails(dataFeed.dataFeedDetails)!;
347347
const decodedUpdateParameters = contractsModule.decodeUpdateParameters(dataFeed.updateParameters);
348348
const activeDataFeed = {
349349
...omit(dataFeed, ['dataFeedDetails', 'updateParameters']),

src/update-feeds-loops/update-feeds-loops.ts

+11-5
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,11 @@ export const runUpdateFeeds = async (providerName: ProviderName, chain: Chain, c
112112
const firstBatch = activeDataFeedCallsReturndata
113113
// Because the activeDataFeedCount is not known during the multicall, we may ask for non-existent data feeds. These should be filtered out.
114114
.slice(0, activeDataFeedCount)
115-
.map((dataFeedReturndata) => ({
116-
...decodeActiveDataFeedResponse(airseekerRegistry, dataFeedReturndata),
117-
chainId,
118-
}));
115+
.map((dataFeedReturndata) => decodeActiveDataFeedResponse(airseekerRegistry, dataFeedReturndata))
116+
.filter((dataFeed, dataFeedIndex): dataFeed is DecodedActiveDataFeedResponse => {
117+
logger.warn(`Data feed not registered.`, { dataFeedIndex });
118+
return dataFeed !== null;
119+
});
119120
return {
120121
firstBatch,
121122
activeDataFeedCount,
@@ -169,7 +170,12 @@ export const runUpdateFeeds = async (providerName: ProviderName, chain: Chain, c
169170
await airseekerRegistry.callStatic.tryMulticall(activeDataFeedCalldatas)
170171
);
171172

172-
return returndata.map((returndata) => decodeActiveDataFeedResponse(airseekerRegistry, returndata));
173+
return returndata
174+
.map((returndata) => decodeActiveDataFeedResponse(airseekerRegistry, returndata))
175+
.filter((dataFeed, index): dataFeed is DecodedActiveDataFeedResponse => {
176+
logger.warn(`Data feed not registered.`, { dataFeedIndex: dataFeedBatchIndexStart + index });
177+
return dataFeed !== null;
178+
});
173179
});
174180
if (!goBatch.success) {
175181
logger.error(`Failed to get active data feeds batch.`, goBatch.error);

test/e2e/update-feeds.feature.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ it('updates blockchain data', async () => {
4949
initializeGasState(chainId, providerName);
5050
const btcDataFeed = await airseekerRegistry.activeDataFeed(0);
5151

52-
const decodedDataFeed = decodeDataFeedDetails(btcDataFeed.dataFeedDetails);
52+
const decodedDataFeed = decodeDataFeedDetails(btcDataFeed.dataFeedDetails)!;
5353
const activeBtcDataFeed = {
5454
...omit(btcDataFeed, ['dataFeedDetails', 'updateParameters']),
5555
decodedUpdateParameters: decodeUpdateParameters(btcDataFeed.updateParameters),

0 commit comments

Comments
 (0)