From a1a5b26177bef9656f3f838385a2130f175bbd7a Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 6 Feb 2025 10:50:15 -0700 Subject: [PATCH] Glv adapter lwba (#3670) * Refactor adapter to have price/lwba endoints * Add LWBA response verification in test --- .changeset/happy-moles-change.md | 5 +++ packages/composites/glv-token/README.md | 23 ++++++++++ .../glv-token/src/endpoint/index.ts | 1 + .../composites/glv-token/src/endpoint/lwba.ts | 38 ++++++++++++++++ packages/composites/glv-token/src/index.ts | 4 +- .../glv-token/src/transport/base.ts | 3 +- .../glv-token/src/transport/lwba.ts | 44 +++++++++++++++++++ .../composites/glv-token/test-payload.json | 16 +++++-- .../__snapshots__/adapter.test.ts.snap | 28 ++++++++++++ .../test/integration/adapter.test.ts | 22 ++++++++++ 10 files changed, 178 insertions(+), 6 deletions(-) create mode 100644 .changeset/happy-moles-change.md create mode 100644 packages/composites/glv-token/src/endpoint/lwba.ts create mode 100644 packages/composites/glv-token/src/transport/lwba.ts diff --git a/.changeset/happy-moles-change.md b/.changeset/happy-moles-change.md new file mode 100644 index 0000000000..12103ebfe5 --- /dev/null +++ b/.changeset/happy-moles-change.md @@ -0,0 +1,5 @@ +--- +'@chainlink/glv-token-adapter': minor +--- + +Add LWBA endpoint diff --git a/packages/composites/glv-token/README.md b/packages/composites/glv-token/README.md index 73d80aad9b..9c36be1dd8 100644 --- a/packages/composites/glv-token/README.md +++ b/packages/composites/glv-token/README.md @@ -59,6 +59,29 @@ Request: } ``` +## Crypto-lwba Endpoint + +Supported names for this endpoint are: `cryptolwba`. + +### Input Params + +| Required? | Name | Aliases | Description | Type | Options | Default | Depends On | Not Valid With | +| :-------: | :--: | :-----: | :---------: | :----: | :-----: | :-----: | :--------: | :------------: | +| ✅ | glv | | Glv address | string | | | | | + +### Example + +Request: + +```json +{ + "data": { + "endpoint": "crypto-lwba", + "glv": "0x528A5bac7E746C9A509A1f4F6dF58A03d44279F9" + } +} +``` + --- MIT License diff --git a/packages/composites/glv-token/src/endpoint/index.ts b/packages/composites/glv-token/src/endpoint/index.ts index 11a44912b4..ac9492a769 100644 --- a/packages/composites/glv-token/src/endpoint/index.ts +++ b/packages/composites/glv-token/src/endpoint/index.ts @@ -1 +1,2 @@ export { endpoint as price } from './price' +export { endpoint as lwba } from './lwba' diff --git a/packages/composites/glv-token/src/endpoint/lwba.ts b/packages/composites/glv-token/src/endpoint/lwba.ts new file mode 100644 index 0000000000..7d5e3067df --- /dev/null +++ b/packages/composites/glv-token/src/endpoint/lwba.ts @@ -0,0 +1,38 @@ +import { + AdapterEndpoint, + LwbaResponseDataFields, +} from '@chainlink/external-adapter-framework/adapter' +import { InputParameters } from '@chainlink/external-adapter-framework/validation' +import { config } from '../config' +import { glvLwbaTransport } from '../transport/lwba' + +export const inputParameters = new InputParameters( + { + glv: { + required: true, + type: 'string', + description: 'Glv address', + }, + }, + [ + { + glv: '0x528A5bac7E746C9A509A1f4F6dF58A03d44279F9', + }, + ], +) + +export type BaseEndpointTypesLwba = { + Parameters: typeof inputParameters.definition + Response: LwbaResponseDataFields & { + Data: { + sources: Record + } + } + Settings: typeof config.settings +} + +export const endpoint = new AdapterEndpoint({ + name: 'crypto-lwba', + transport: glvLwbaTransport, + inputParameters, +}) diff --git a/packages/composites/glv-token/src/index.ts b/packages/composites/glv-token/src/index.ts index e0d17dd88c..ecd1242480 100644 --- a/packages/composites/glv-token/src/index.ts +++ b/packages/composites/glv-token/src/index.ts @@ -1,13 +1,13 @@ import { expose, ServerInstance } from '@chainlink/external-adapter-framework' import { Adapter } from '@chainlink/external-adapter-framework/adapter' import { config } from './config' -import { price } from './endpoint' +import { lwba, price } from './endpoint' export const adapter = new Adapter({ defaultEndpoint: price.name, name: 'GLV_TOKEN', config, - endpoints: [price], + endpoints: [price, lwba], }) export const server = (): Promise => expose(adapter) diff --git a/packages/composites/glv-token/src/transport/base.ts b/packages/composites/glv-token/src/transport/base.ts index db307ba92a..e79a7ea28b 100644 --- a/packages/composites/glv-token/src/transport/base.ts +++ b/packages/composites/glv-token/src/transport/base.ts @@ -11,6 +11,7 @@ import { AdapterResponse, makeLogger } from '@chainlink/external-adapter-framewo import { AdapterDataProviderError } from '@chainlink/external-adapter-framework/validation/error' import glvAbi from '../config/glvReaderAbi.json' import { BaseEndpointTypes, inputParameters } from '../endpoint/price' +import { BaseEndpointTypesLwba } from '../endpoint/lwba' import { mapParameter, mapSymbol, @@ -41,7 +42,7 @@ type RequestParams = typeof inputParameters.validated * `formatResponse()` to produce different output shapes. */ export abstract class BaseGlvTransport< - T extends BaseEndpointTypes, + T extends BaseEndpointTypes | BaseEndpointTypesLwba, > extends SubscriptionTransport { abstract backgroundHandler( context: EndpointContext, diff --git a/packages/composites/glv-token/src/transport/lwba.ts b/packages/composites/glv-token/src/transport/lwba.ts new file mode 100644 index 0000000000..ea602c220c --- /dev/null +++ b/packages/composites/glv-token/src/transport/lwba.ts @@ -0,0 +1,44 @@ +import { BaseGlvTransport } from './base' +import { EndpointContext } from '@chainlink/external-adapter-framework/adapter' +import { TypeFromDefinition } from '@chainlink/external-adapter-framework/validation/input-params' +import { AdapterResponse, sleep } from '@chainlink/external-adapter-framework/util' +import { BaseEndpointTypesLwba } from '../endpoint/lwba' + +export class GlvLwbaTransport extends BaseGlvTransport { + async backgroundHandler( + context: EndpointContext, + entries: TypeFromDefinition[], + ): Promise { + await Promise.all(entries.map(async (param) => this.handleRequest(param))) + await sleep(context.adapterSettings.BACKGROUND_EXECUTE_MS) + } + + async handleRequest( + param: TypeFromDefinition, + ): Promise { + const response = await this._handleRequest(param).catch((e) => this.handleError(e)) + await this.responseCache.write(this.name, [{ params: param, response }]) + } + + protected formatResponse( + result: number, + minimizedValue: number, + maximizedValue: number, + sources: Record, + timestamps: any, + ): AdapterResponse { + return { + data: { + mid: result, + bid: minimizedValue, + ask: maximizedValue, + sources, + }, + statusCode: 200, + result: null, + timestamps, + } + } +} + +export const glvLwbaTransport = new GlvLwbaTransport() diff --git a/packages/composites/glv-token/test-payload.json b/packages/composites/glv-token/test-payload.json index e09b5a6de9..fb2e37e836 100644 --- a/packages/composites/glv-token/test-payload.json +++ b/packages/composites/glv-token/test-payload.json @@ -1,5 +1,15 @@ { - "requests": [{ - "glv": "0x528A5bac7E746C9A509A1f4F6dF58A03d44279F9" - }] + "requests": [ + { + "glv": "0x528A5bac7E746C9A509A1f4F6dF58A03d44279F9" + }, + { + "endpoint": "price", + "glv": "0x528A5bac7E746C9A509A1f4F6dF58A03d44279F9" + }, + { + "endpoint": "crypto-lwba", + "glv": "0x528A5bac7E746C9A509A1f4F6dF58A03d44279F9" + } + ] } \ No newline at end of file diff --git a/packages/composites/glv-token/test/integration/__snapshots__/adapter.test.ts.snap b/packages/composites/glv-token/test/integration/__snapshots__/adapter.test.ts.snap index f4068dd3f8..31f1231305 100644 --- a/packages/composites/glv-token/test/integration/__snapshots__/adapter.test.ts.snap +++ b/packages/composites/glv-token/test/integration/__snapshots__/adapter.test.ts.snap @@ -1,5 +1,33 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`execute lwba endpoint should return success 1`] = ` +{ + "data": { + "ask": 1.1473068612168396, + "bid": 1.1470467994160611, + "mid": 1.1471768303164502, + "sources": { + "ETH": [ + "tiingo", + "coinmetrics", + "ncfx", + ], + "USDC": [ + "tiingo", + "coinmetrics", + "ncfx", + ], + }, + }, + "result": null, + "statusCode": 200, + "timestamps": { + "providerDataReceivedUnixMs": 978347471111, + "providerDataRequestedUnixMs": 978347471111, + }, +} +`; + exports[`execute price endpoint should return success 1`] = ` { "data": { diff --git a/packages/composites/glv-token/test/integration/adapter.test.ts b/packages/composites/glv-token/test/integration/adapter.test.ts index 6c665080d6..5140f9247e 100644 --- a/packages/composites/glv-token/test/integration/adapter.test.ts +++ b/packages/composites/glv-token/test/integration/adapter.test.ts @@ -9,6 +9,10 @@ import { mockTiingoEAResponseSuccess, } from './fixtures' import { ethers } from 'ethers' +import { + LwbaResponseDataFields, + validateLwbaResponse, +} from '@chainlink/external-adapter-framework/adapter' jest.mock('ethers', () => ({ ...jest.requireActual('ethers'), @@ -116,4 +120,22 @@ describe('execute', () => { expect(response.json()).toMatchSnapshot() }) }) + + describe('lwba endpoint', () => { + it('should return success', async () => { + const data = { + endpoint: 'crypto-lwba', + glv: '0x528A5bac7E746C9A509A1f4F6dF58A03d44279F9', + } + mockTiingoEAResponseSuccess('ETH') + mockNCFXEAResponseSuccess('ETH') + mockCoinmetricsEAResponseSuccess('ETH') + const response = await testAdapter.request(data) + expect(response.statusCode).toBe(200) + expect(response.json()).toMatchSnapshot() + const resJson = response.json() + const resData = resJson.data as LwbaResponseDataFields['Data'] + validateLwbaResponse(resData.bid, resData.mid, resData.ask) + }) + }) })