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

Peregrine Data External Adapter Submission #3590

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 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
Empty file.
3 changes: 3 additions & 0 deletions packages/sources/peregrine-fund-admin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Chainlink External Adapter for peregrine-fund-admin

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 peregrine-fund-admin`.
40 changes: 40 additions & 0 deletions packages/sources/peregrine-fund-admin/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "@chainlink/peregrine-fund-admin-adapter",
"version": "0.0.1",
"description": "Chainlink peregrine-fund-admin adapter.",
"keywords": [
"Chainlink",
"LINK",
"blockchain",
"oracle",
"peregrine-fund-admin"
],
"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.115",
"nock": "13.5.4",
"typescript": "5.6.3"
},
"dependencies": {
"@chainlink/external-adapter-framework": "1.7.1",
droconnel22 marked this conversation as resolved.
Show resolved Hide resolved
"tslib": "2.4.1"
}
}
47 changes: 47 additions & 0 deletions packages/sources/peregrine-fund-admin/src/config/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { AdapterConfig } from '@chainlink/external-adapter-framework/config'
droconnel22 marked this conversation as resolved.
Show resolved Hide resolved

export const NAME = 'PEREGRINE_FUND_ADMIN'
export const DEFAULT_API_VERSION = 'api/v1'
export const DEFAULT_NAV_ENDPOINT = 'nav'
export const DEFAULT_RESERVE_ENDPOINT = 'reserve'
export const DEFAULT_PARAM_ASSET_ID = 100
export const DEFAULT_BASE_URL_VALUE =
'https://fund-admin-data-adapter-v1-960005989691.europe-west2.run.app'
export const DEFAULT_NAV_URL_VALUE = '/api/v1/nav/'
export const DEFAULT_RESERVE_URL_VALUE = '/api/v1/reserve/'
droconnel22 marked this conversation as resolved.
Show resolved Hide resolved

export const config = new AdapterConfig({
API_KEY: {
description: 'An API key for Data Provider',
type: 'string',
required: true,
sensitive: true,
},
DEFAULT_BASE_URL: {
description: 'Base URL to Fund Admin Server Endpoint',
type: 'string',
default: DEFAULT_BASE_URL_VALUE,
},
DEFAULT_NAV_URL: {
description:
'An API endpoint for the latest Net Asset Value (NAV) calculation for a given asset',
type: 'string',
default: DEFAULT_NAV_URL_VALUE,
},
DEFAULT_RESERVE_URL: {
description: 'An API endpoint for the latest Proof of Reserve (PoR) for a given asset',
type: 'string',
default: DEFAULT_RESERVE_URL_VALUE,
},
API_NAV_ENDPOINT: {
description:
'An API endpoint for the latest Net Asset Value (NAV) calculation for a given asset',
type: 'string',
default: `${DEFAULT_BASE_URL_VALUE}/${DEFAULT_API_VERSION}/${DEFAULT_NAV_ENDPOINT}/${DEFAULT_PARAM_ASSET_ID}/`,
},
API_RESERVE_ENDPOINT: {
description: 'API Endpoint to get the latest Proof of Reserves for a given asset',
type: 'string',
droconnel22 marked this conversation as resolved.
Show resolved Hide resolved
default: `${DEFAULT_BASE_URL_VALUE}/${DEFAULT_API_VERSION}/${DEFAULT_RESERVE_ENDPOINT}/${DEFAULT_PARAM_ASSET_ID}/`,
},
})
2 changes: 2 additions & 0 deletions packages/sources/peregrine-fund-admin/src/endpoint/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { endpoint as nav } from './nav'
export { endpoint as reserve } from './reserve'
29 changes: 29 additions & 0 deletions packages/sources/peregrine-fund-admin/src/endpoint/nav.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { AdapterEndpoint } from '@chainlink/external-adapter-framework/adapter'
import { navHttpTransport } from '../transport/nav'
import { SingleNumberResultResponse } from '@chainlink/external-adapter-framework/util'
import { InputParameters } from '@chainlink/external-adapter-framework/validation'
import { config } from '../config'

export const inputParameters = new InputParameters({
assetId: {
required: true,
type: 'string',
description: 'The identifying number for the requested asset',
},
})

// Endpoints contain a type parameter that allows specifying relevant types of an endpoint, for example, request payload type, Adapter response type and Adapter configuration (environment variables) type
export type BaseEndpointTypes = {
Parameters: typeof inputParameters.definition
Response: SingleNumberResultResponse
Settings: typeof config.settings
}

export const endpoint = new AdapterEndpoint({
// Endpoint name
droconnel22 marked this conversation as resolved.
Show resolved Hide resolved
name: 'nav',
// Transport handles incoming requests, data processing and communication for this endpoint
transport: navHttpTransport,
// Supported input parameters for this endpoint
inputParameters,
})
29 changes: 29 additions & 0 deletions packages/sources/peregrine-fund-admin/src/endpoint/reserve.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { AdapterEndpoint } from '@chainlink/external-adapter-framework/adapter'
import { httpReserveTransport } from '../transport/reserve'
import { SingleNumberResultResponse } from '@chainlink/external-adapter-framework/util'
import { InputParameters } from '@chainlink/external-adapter-framework/validation'
import { config } from '../config'

export const inputParameters = new InputParameters({
assetId: {
required: true,
type: 'string',
description: 'The identifying number for the requested asset',
},
})

// Endpoints contain a type parameter that allows specifying relevant types of an endpoint, for example, request payload type, Adapter response type and Adapter configuration (environment variables) type
export type BaseEndpointTypes = {
Parameters: typeof inputParameters.definition
Response: SingleNumberResultResponse
Settings: typeof config.settings
}

export const endpoint = new AdapterEndpoint({
// Endpoint name
droconnel22 marked this conversation as resolved.
Show resolved Hide resolved
name: 'reserve',
// Supported input parameters for this endpoint
inputParameters,
// Transport handles incoming requests, data processing and communication for this endpoint
transport: httpReserveTransport,
})
17 changes: 17 additions & 0 deletions packages/sources/peregrine-fund-admin/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { expose, ServerInstance } from '@chainlink/external-adapter-framework'
import { Adapter } from '@chainlink/external-adapter-framework/adapter'
import { config } from './config'
import { nav, reserve } from './endpoint'

export const adapter = new Adapter({
//Requests will direct to this endpoint if the `endpoint` input parameter is not specified.
droconnel22 marked this conversation as resolved.
Show resolved Hide resolved
defaultEndpoint: nav.name,
// Adapter name
name: 'PEREGRINE_FUND_ADMIN',
// Adapter configuration (environment variables)
config,
// List of supported endpoints
endpoints: [nav, reserve],
droconnel22 marked this conversation as resolved.
Show resolved Hide resolved
})

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

export interface ResponseSchema {
Id: string | null
assetId: string
seniorNAV: number
juniorNav: number
equityNav: number
totalLiability: number
totalAccounts: string
totalCollateral: number
collateral: number[]
accounts: number[]
updateTimestamp: string
id: string | null
}

// export type HttpTransportTypes = BaseEndpointTypes & {
// Provider: {
// RequestBody: never
// ResponseBody: ResponseSchema
// }
// }

export type HttpTransportTypes = BaseEndpointTypes & {
Provider: {
RequestBody: never
ResponseBody: ResponseSchema
}
}

// HttpTransport is used to fetch and process data from a Provider using HTTP(S) protocol. It usually needs two methods
// `prepareRequests` and `parseResponse`
export const navHttpTransport = new HttpTransport<HttpTransportTypes>({
// `prepareRequests` method receives request payloads sent to associated endpoint alongside adapter config(environment variables)
// and should return 'request information' to the Data Provider. Use this method to construct one or many requests, and the framework
// will send them to Data Provider
prepareRequests: (params, config) => {
return params.map((param) => {
return {
// `params` are parameters associated to this single request and will also be available in the 'parseResponse' method.
params: [param],
// `request` contains any valid axios request configuration
request: {
baseURL: config.DEFAULT_BASE_URL,
url: config.DEFAULT_NAV_URL,
headers: {
X_API_KEY: config.API_KEY,
},
params: {
assetId: params,
},
},
}
})
},
// `parseResponse` takes the 'params' specified in the `prepareRequests` and the 'response' from Data Provider and should return
// an array of response objects to be stored in cache. Use this method to construct a list of response objects for every parameter in 'params'
// and the framework will save them in cache and return to user
parseResponse: (params, response) => {
return params.map((param) => {
const resData = response.data as ResponseSchema

if (!resData || !resData.equityNav) {
return {
params: param,
response: {
errorMessage: 'Missing equityNav in the response',
statusCode: 500,
},
}
}

return {
params: param,
response: {
data: {
result: resData.equityNav,
droconnel22 marked this conversation as resolved.
Show resolved Hide resolved
},
result: resData.equityNav,
statusCode: response.status,
},
}
})
},
})
75 changes: 75 additions & 0 deletions packages/sources/peregrine-fund-admin/src/transport/reserve.ts
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make the same changes as above nav.ts file in this file as well.

Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { HttpTransport } from '@chainlink/external-adapter-framework/transports'
import { BaseEndpointTypes } from '../endpoint/reserve'

export interface ResponseSchema {
assetId: string
totalValue: number
currencyBase: string
accountIds: number[]
updateDateTime: string
}

// HttpTransport extends base types from endpoint and adds additional, Provider-specific types like 'RequestBody', which is the type of
// request body (not the request to adapter, but the request that adapter sends to Data Provider), and 'ResponseBody' which is
// the type of raw response from Data Provider
export type HttpTransportTypes = BaseEndpointTypes & {
Provider: {
RequestBody: never
ResponseBody: ResponseSchema
}
}
// HttpTransport is used to fetch and process data from a Provider using HTTP(S) protocol. It usually needs two methods
// `prepareRequests` and `parseResponse`
export const httpReserveTransport = new HttpTransport<HttpTransportTypes>({
droconnel22 marked this conversation as resolved.
Show resolved Hide resolved
// `prepareRequests` method receives request payloads sent to associated endpoint alongside adapter config(environment variables)
// and should return 'request information' to the Data Provider. Use this method to construct one or many requests, and the framework
// will send them to Data Provider
prepareRequests: (params, config) => {
return params.map((param) => {
return {
// `params` are parameters associated to this single request and will also be available in the 'parseResponse' method.
params: [param],
// `request` contains any valid axios request configuration
request: {
baseURL: config.DEFAULT_BASE_URL,
url: config.DEFAULT_RESERVE_URL,
droconnel22 marked this conversation as resolved.
Show resolved Hide resolved
headers: {
X_API_KEY: config.API_KEY,
},
params: {
assetId: param.assetId,
},
},
}
})
},
// `parseResponse` takes the 'params' specified in the `prepareRequests` and the 'response' from Data Provider and should return
// an array of response objects to be stored in cache. Use this method to construct a list of response objects for every parameter in 'params'
// and the framework will save them in cache and return to user
parseResponse: (params, response) => {
return params.map((param) => {
const resData = response.data as ResponseSchema

if (!resData || !resData.totalValue) {
return {
params: param,
response: {
errorMessage: 'Missing totalValue in the response',
statusCode: 500,
droconnel22 marked this conversation as resolved.
Show resolved Hide resolved
},
}
}

return {
params: param,
response: {
data: {
result: resData.totalValue,
},
result: resData.totalValue,
statusCode: response.status,
},
}
})
},
})
Loading