Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NYFED #3607

Merged
merged 2 commits into from
Dec 6, 2024
Merged

NYFED #3607

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/early-phones-smile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@chainlink/nyfed-adapter': major
---

NYFED
20 changes: 20 additions & 0 deletions .pnp.cjs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Empty file.
3 changes: 3 additions & 0 deletions packages/sources/nyfed/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Chainlink External Adapter for nyfed

This README will be generated automatically when code is merged to `main`. If you would like to generate a preview of the README, please run `yarn generate:readme nyfed`.
40 changes: 40 additions & 0 deletions packages/sources/nyfed/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "@chainlink/nyfed-adapter",
"version": "0.0.0",
"description": "Chainlink nyfed adapter.",
"keywords": [
"Chainlink",
"LINK",
"blockchain",
"oracle",
"nyfed"
],
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"repository": {
"url": "https://github.com/smartcontractkit/external-adapters-js",
"type": "git"
},
"license": "MIT",
"scripts": {
"clean": "rm -rf dist && rm -f tsconfig.tsbuildinfo",
"prepack": "yarn build",
"build": "tsc -b",
"server": "node -e 'require(\"./index.js\").server()'",
"server:dist": "node -e 'require(\"./dist/index.js\").server()'",
"start": "yarn server:dist"
},
"devDependencies": {
"@types/jest": "27.5.2",
"@types/node": "16.18.119",
"nock": "13.5.5",
"typescript": "5.6.3"
},
"dependencies": {
"@chainlink/external-adapter-framework": "1.7.4",
"tslib": "2.4.1"
}
}
9 changes: 9 additions & 0 deletions packages/sources/nyfed/src/config/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { AdapterConfig } from '@chainlink/external-adapter-framework/config'

export const config = new AdapterConfig({
API_ENDPOINT: {
description: 'An API endpoint for Data Provider',
type: 'string',
default: 'https://markets.newyorkfed.org/api/rates/secured/all/latest.json',
},
})
1 change: 1 addition & 0 deletions packages/sources/nyfed/src/endpoint/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { endpoint as rate } from './rate'
32 changes: 32 additions & 0 deletions packages/sources/nyfed/src/endpoint/rate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { AdapterEndpoint } from '@chainlink/external-adapter-framework/adapter'
import { InputParameters } from '@chainlink/external-adapter-framework/validation'
import { SingleNumberResultResponse } from '@chainlink/external-adapter-framework/util'
import { config } from '../config'
import { httpTransport } from '../transport/rate'

export const inputParameters = new InputParameters(
{
symbol: {
required: true,
type: 'string',
description: 'Symbol of the rate you are looking for',
},
},
[
{
symbol: 'SOFR',
},
],
)

export type BaseEndpointTypes = {
Parameters: typeof inputParameters.definition
Response: SingleNumberResultResponse
Settings: typeof config.settings
}

export const endpoint = new AdapterEndpoint({
name: 'rate',
transport: httpTransport,
inputParameters,
})
21 changes: 21 additions & 0 deletions packages/sources/nyfed/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { expose, ServerInstance } from '@chainlink/external-adapter-framework'
import { Adapter } from '@chainlink/external-adapter-framework/adapter'
import { config } from './config'
import { rate } from './endpoint'

export const adapter = new Adapter({
defaultEndpoint: rate.name,
name: 'NYFED',
config,
endpoints: [rate],
rateLimiting: {
tiers: {
default: {
rateLimit1m: 6,
note: 'Reasonable limits',
},
},
},
})

export const server = (): Promise<ServerInstance | undefined> => expose(adapter)
72 changes: 72 additions & 0 deletions packages/sources/nyfed/src/transport/rate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { HttpTransport } from '@chainlink/external-adapter-framework/transports'
import { BaseEndpointTypes } from '../endpoint/rate'

export interface ResponseSchema {
refRates: {
effectiveDate: string
type: string
percentRate?: number
}[]
}

export type HttpTransportTypes = BaseEndpointTypes & {
Provider: {
RequestBody: never
ResponseBody: ResponseSchema
}
}
export const httpTransport = new HttpTransport<HttpTransportTypes>({
prepareRequests: (params, config) => {
return params.map((param) => {
return {
params: [param],
request: {
baseURL: config.API_ENDPOINT,
},
}
})
},
parseResponse: (params, response) => {
if (!response.data) {
return params.map((param) => {
return {
params: param,
response: {
errorMessage: `The data provider didn't return any value`,
statusCode: 502,
},
}
})
}

return params.map((param) => {
const result = response.data.refRates.find(
(d) => d.type.toUpperCase() == param.symbol.toUpperCase(),
)
if (result?.percentRate == undefined) {
return {
params: param,
response: {
errorMessage: `The data provider didn't return any value for ${param.symbol}`,
statusCode: 502,
},
}
} else {
return {
params: param,
response: {
result: result.percentRate,
data: {
result: result.percentRate,
},
timestamps: {
providerIndicatedTimeUnixMs: result.effectiveDate
? new Date(result.effectiveDate).getTime()
: undefined,
},
},
}
}
})
},
})
5 changes: 5 additions & 0 deletions packages/sources/nyfed/test-payload.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"requests": [{
"symbol": "SOFR"
}]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`execute rate endpoint should return success 1`] = `
{
"data": {
"result": 4.59,
},
"result": 4.59,
"statusCode": 200,
"timestamps": {
"providerDataReceivedUnixMs": 978347471111,
"providerDataRequestedUnixMs": 978347471111,
"providerIndicatedTimeUnixMs": 1733356800000,
},
}
`;
45 changes: 45 additions & 0 deletions packages/sources/nyfed/test/integration/adapter.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {
TestAdapter,
setEnvVariables,
} from '@chainlink/external-adapter-framework/util/testing-utils'
import * as nock from 'nock'
import { mockResponseSuccess } from './fixtures'

describe('execute', () => {
let spy: jest.SpyInstance
let testAdapter: TestAdapter
let oldEnv: NodeJS.ProcessEnv

beforeAll(async () => {
oldEnv = JSON.parse(JSON.stringify(process.env))
process.env.API_ENDPOINT = 'http://fake-url'

const mockDate = new Date('2001-01-01T11:11:11.111Z')
spy = jest.spyOn(Date, 'now').mockReturnValue(mockDate.getTime())

const adapter = (await import('./../../src')).adapter
adapter.rateLimiting = undefined
testAdapter = await TestAdapter.startWithMockedCache(adapter, {
testAdapter: {} as TestAdapter<never>,
})
})

afterAll(async () => {
setEnvVariables(oldEnv)
await testAdapter.api.close()
nock.restore()
nock.cleanAll()
spy.mockRestore()
})

describe('rate endpoint', () => {
it('should return success', async () => {
mockResponseSuccess()
const response = await testAdapter.request({
symbol: 'SOFR',
})
expect(response.statusCode).toBe(200)
expect(response.json()).toMatchSnapshot()
})
})
})
36 changes: 36 additions & 0 deletions packages/sources/nyfed/test/integration/fixtures.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import nock from 'nock'

export const mockResponseSuccess = (): nock.Scope =>
nock('http://fake-url', {
encodedQueryParams: true,
})
.get('/')
.reply(
200,
() => ({
refRates: [
{
effectiveDate: '2024-12-05',
type: 'SOFR',
percentRate: 4.59,
percentPercentile1: 4.55,
percentPercentile25: 4.57,
percentPercentile75: 4.65,
percentPercentile99: 4.68,
volumeInBillions: 2325,
revisionIndicator: '',
},
],
}),
[
'Content-Type',
'application/json',
'Connection',
'close',
'Vary',
'Accept-Encoding',
'Vary',
'Origin',
],
)
.persist()
9 changes: 9 additions & 0 deletions packages/sources/nyfed/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
},
"include": ["src/**/*", "src/**/*.json"],
"exclude": ["dist", "**/*.spec.ts", "**/*.test.ts"]
}
7 changes: 7 additions & 0 deletions packages/sources/nyfed/tsconfig.test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": "../../tsconfig.base.json",
"include": ["src/**/*", "**/test", "src/**/*.json"],
"compilerOptions": {
"noEmit": true
}
}
3 changes: 3 additions & 0 deletions packages/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,9 @@
{
"path": "./sources/nikkei"
},
{
"path": "./sources/nyfed"
},
{
"path": "./sources/oanda"
},
Expand Down
3 changes: 3 additions & 0 deletions packages/tsconfig.test.json
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,9 @@
{
"path": "./sources/nikkei/tsconfig.test.json"
},
{
"path": "./sources/nyfed/tsconfig.test.json"
},
{
"path": "./sources/oanda/tsconfig.test.json"
},
Expand Down
13 changes: 13 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4922,6 +4922,19 @@ __metadata:
languageName: unknown
linkType: soft

"@chainlink/nyfed-adapter@workspace:packages/sources/nyfed":
version: 0.0.0-use.local
resolution: "@chainlink/nyfed-adapter@workspace:packages/sources/nyfed"
dependencies:
"@chainlink/external-adapter-framework": "npm:1.7.4"
"@types/jest": "npm:27.5.2"
"@types/node": "npm:16.18.119"
nock: "npm:13.5.5"
tslib: "npm:2.4.1"
typescript: "npm:5.6.3"
languageName: unknown
linkType: soft

"@chainlink/oanda-adapter@workspace:packages/sources/oanda":
version: 0.0.0-use.local
resolution: "@chainlink/oanda-adapter@workspace:packages/sources/oanda"
Expand Down
Loading