diff --git a/.changeset/red-experts-thank.md b/.changeset/red-experts-thank.md new file mode 100644 index 0000000000..05ee910bf1 --- /dev/null +++ b/.changeset/red-experts-thank.md @@ -0,0 +1,5 @@ +--- +'@chainlink/bitgo-reserves-adapter': major +--- + +update bitgo-reserves EA with new payload diff --git a/packages/sources/bitgo-reserves/src/config/index.ts b/packages/sources/bitgo-reserves/src/config/index.ts index fa6b9cfae4..ad754c7d47 100644 --- a/packages/sources/bitgo-reserves/src/config/index.ts +++ b/packages/sources/bitgo-reserves/src/config/index.ts @@ -6,4 +6,9 @@ export const config = new AdapterConfig({ type: 'string', default: 'https://reserves.usdstandard-test.com/por.json', }, + VERIFICATION_PUBKEY: { + description: 'Public key used for verifying data signature', + type: 'string', + required: true, + }, }) diff --git a/packages/sources/bitgo-reserves/src/transport/reserves.ts b/packages/sources/bitgo-reserves/src/transport/reserves.ts index 8db0d72678..305880190f 100644 --- a/packages/sources/bitgo-reserves/src/transport/reserves.ts +++ b/packages/sources/bitgo-reserves/src/transport/reserves.ts @@ -1,11 +1,19 @@ import { HttpTransport } from '@chainlink/external-adapter-framework/transports' import { BaseEndpointTypes } from '../endpoint/reserves' +import * as crypto from 'crypto' -export interface ResponseSchema { - totalReserve: number +export interface DataSchema { + totalReserve?: string + reserveAmount?: string + cashReserve: string + investedReserve: string lastUpdated: string - cashReserve: number - investedReserve: number +} + +export interface ResponseSchema { + data: string // formatted & escaped DataSchema + dataSignature: string + ripcord: boolean } export type HttpTransportTypes = BaseEndpointTypes & { @@ -27,26 +35,56 @@ export const httpTransport = new HttpTransport({ } }) }, - parseResponse: (params, response) => { - const timestamps = { - providerIndicatedTimeUnixMs: new Date(response.data.lastUpdated).getTime(), - } + parseResponse: (params, response, adapterSettings) => { + const payload = response.data - if (!response.data) { + if (!payload || !payload.data) { return params.map((param) => { return { params: param, response: { errorMessage: `The data provider didn't return any value`, statusCode: 502, - timestamps, }, } }) } + if (payload.ripcord) { + return [ + { + params: params[0], + response: { + errorMessage: 'Ripcord indicator true', + ripcord: response.data.ripcord, + statusCode: 502, + }, + }, + ] + } + + const publicKey = adapterSettings.VERIFICATION_PUBKEY + const verifier = crypto.createVerify('sha256') + verifier.update(payload.data) + if (!verifier.verify(publicKey, payload.dataSignature, 'base64')) { + return params.map((param) => { + return { + params: param, + response: { + errorMessage: `Data verification failed`, + statusCode: 502, + }, + } + }) + } + + const data = JSON.parse(payload.data) as DataSchema + const timestamps = { + providerIndicatedTimeUnixMs: new Date(data.lastUpdated).getTime(), + } + return params.map((param) => { - const result = response.data.totalReserve + const result = Number(data.totalReserve) || Number(data.reserveAmount) return { params: param, response: { diff --git a/packages/sources/bitgo-reserves/test/integration/__snapshots__/adapter.test.ts.snap b/packages/sources/bitgo-reserves/test/integration/__snapshots__/adapter.test.ts.snap index 0414f1ab7a..98b2066bb2 100644 --- a/packages/sources/bitgo-reserves/test/integration/__snapshots__/adapter.test.ts.snap +++ b/packages/sources/bitgo-reserves/test/integration/__snapshots__/adapter.test.ts.snap @@ -3,14 +3,14 @@ exports[`execute reserves endpoint should return success 1`] = ` { "data": { - "result": 1234567.89, + "result": 12345678.9, }, - "result": 1234567.89, + "result": 12345678.9, "statusCode": 200, "timestamps": { "providerDataReceivedUnixMs": 978347471111, "providerDataRequestedUnixMs": 978347471111, - "providerIndicatedTimeUnixMs": 1727745825000, + "providerIndicatedTimeUnixMs": 1733793825000, }, } -`; +`; \ No newline at end of file diff --git a/packages/sources/bitgo-reserves/test/integration/adapter.test.ts b/packages/sources/bitgo-reserves/test/integration/adapter.test.ts index ab9e3a848c..4941f5ddae 100644 --- a/packages/sources/bitgo-reserves/test/integration/adapter.test.ts +++ b/packages/sources/bitgo-reserves/test/integration/adapter.test.ts @@ -13,11 +13,18 @@ describe('execute', () => { beforeAll(async () => { oldEnv = JSON.parse(JSON.stringify(process.env)) process.env.API_ENDPOINT = 'http://test-endpoint.com' + process.env.VERIFICATION_PUBKEY = 'test' const mockDate = new Date('2001-01-01T11:11:11.111Z') spy = jest.spyOn(Date, 'now').mockReturnValue(mockDate.getTime()) - const adapter = (await import('./../../src')).adapter + jest.mock('crypto', () => ({ + createVerify: jest.fn().mockImplementation((_algo) => ({ + update: jest.fn().mockReturnThis(), + verify: jest.fn().mockImplementationOnce((a, b, c) => true), + })), + })) + const adapter = (await import('../../src')).adapter adapter.rateLimiting = undefined testAdapter = await TestAdapter.startWithMockedCache(adapter, { testAdapter: {} as TestAdapter, @@ -43,4 +50,5 @@ describe('execute', () => { expect(response.json()).toMatchSnapshot() }) }) + // Note: issues with mock verifier prevent further tests }) diff --git a/packages/sources/bitgo-reserves/test/integration/fixtures.ts b/packages/sources/bitgo-reserves/test/integration/fixtures.ts index 132cc3250a..f805bb5f11 100644 --- a/packages/sources/bitgo-reserves/test/integration/fixtures.ts +++ b/packages/sources/bitgo-reserves/test/integration/fixtures.ts @@ -8,7 +8,8 @@ export const mockResponseSuccess = (): nock.Scope => .reply( 200, () => ({ - totalReserve: 1234567.89, + data: '{"reserveAmount":"12345678.90","cashReserve":"2345678.90","investedReserve":"10000000.00","lastUpdated":"2024-12-10T01:23:45Z"}', + dataSignature: 'testsig', lastUpdated: '2024-10-01T01:23:45Z', }), [