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 16 commits into
base: main
Choose a base branch
from
Open
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/hip-cows-serve.md
Copy link
Contributor

Choose a reason for hiding this comment

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

Just need 1 changeset for the PR, please mark it major instead of patch

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@chainlink/peregrine-fund-admin-adapter': patch
---

Peregrine Fund Admin Init
5 changes: 5 additions & 0 deletions .changeset/warm-vans-divide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@chainlink/peregrine-fund-admin-adapter': patch
---

Peregrine PR refactor and testing
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/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.3",
"tslib": "2.4.1"
}
}
23 changes: 23 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,23 @@
import { AdapterConfig } from '@chainlink/external-adapter-framework/config'
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,
},
API_BASE_URL: {
Copy link
Contributor

Choose a reason for hiding this comment

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

might be what @mxiao-cll is mentioning below, but should there be default URLs here as there were in the original commit?

description: 'Base URL to Fund Admin Server Endpoint',
type: 'string',
},
API_NAV_ENDPOINT: {
description:
'An API endpoint for the latest Net Asset Value (NAV) calculation for a given asset',
type: 'string',
},
API_RESERVE_ENDPOINT: {
description: 'API Endpoint to get the latest Proof of Reserves for a given asset',
type: 'string',
Copy link
Contributor

Choose a reason for hiding this comment

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

The values seems to be lost?

},
})
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'
36 changes: 36 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,36 @@
import { AdapterEndpoint } from '@chainlink/external-adapter-framework/adapter'
import { httpTransport } from '../transport/nav'
import { InputParameters } from '@chainlink/external-adapter-framework/validation'
import { SingleNumberResultResponse } from '@chainlink/external-adapter-framework/util'
import { config } from '../config'

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

// 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
Copy link
Contributor

Choose a reason for hiding this comment

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

you can get rid of the comments in this object

name: 'nav',
// Transport handles incoming requests, data processing and communication for this endpoint
transport: httpTransport,
//input params,
inputParameters,
})
35 changes: 35 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,35 @@
import { AdapterEndpoint } from '@chainlink/external-adapter-framework/adapter'
import { httpTransport } from '../transport/reserve'
import { InputParameters } from '@chainlink/external-adapter-framework/validation'
import { SingleNumberResultResponse } from '@chainlink/external-adapter-framework/util'
import { config } from '../config'

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

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

export const endpoint = new AdapterEndpoint({
// Endpoint name
Copy link
Contributor

Choose a reason for hiding this comment

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

here too please

name: 'reserve',
// Supported input parameters for this endpoint
inputParameters,
// Transport handles incoming requests, data processing and communication for this endpoint
transport: httpTransport,
})
24 changes: 24 additions & 0 deletions packages/sources/peregrine-fund-admin/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
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.
Copy link
Contributor

Choose a reason for hiding this comment

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

and here

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
rateLimiting: {
tiers: {
default: {
rateLimit1m: 30,
},
},
},
})

export const server = (): Promise<ServerInstance | undefined> => expose(adapter)
86 changes: 86 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,86 @@
import { HttpTransport } from '@chainlink/external-adapter-framework/transports'
import { BaseEndpointTypes } from '../endpoint/nav'

export interface ResponseSchema {
equityNav: number
seniorNAV: number
juniorNav: number
totalCollateral: number
totalAccounts: number
totalLiability: number
updateDateTime: string
assetId: string
}

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 httpTransport = 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.API_BASE_URL,
url: config.API_NAV_ENDPOINT,
Copy link
Contributor

Choose a reason for hiding this comment

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

this is still missing the assetId, isn't it?

headers: {
X_API_KEY: config.API_KEY,
},
},
}
})
},
// `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) => {
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 equityNav = response.data.equityNav
if (equityNav) {
return {
params: param,
response: {
result: Number(response.data.equityNav),
Copy link
Contributor

Choose a reason for hiding this comment

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

I didn't follow the earlier discussion very closely but I think what you might be looking for here is something like the following:

add to nav inputParameters

    trancheSelector: {
      required: true,
      type: 'string',
      options: ['equityNav', 'seniorNAV', 'juniorNav'],
      description: 'The tranche to use as result',
    },

then here

result: Number(response.data[param.trancheSelector])

plus necessary validation/verification where appropriate

data: {
result: Number(response.data.equityNav),
timestamps: {
providerIndicatedTimeUnixMs: Number(response.data.updateDateTime) * 1000,
},
},
},
}
} else {
return {
params: param,
response: {
errorMessage: `The data provider didn't return any value for asset id: ${param}`,
statusCode: 502,
},
}
}
})
},
})
85 changes: 85 additions & 0 deletions packages/sources/peregrine-fund-admin/src/transport/reserve.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
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 httpTransport = 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.API_BASE_URL,
url: config.API_RESERVE_ENDPOINT,
headers: {
X_API_KEY: config.API_KEY,
},
},
}
})
},
// `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) => {
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 totalValue = response.data.totalValue
if (totalValue) {
return {
params: param,
response: {
result: Number(response.data.totalValue),
data: {
result: Number(response.data.totalValue),
},
timestamps: {
providerIndicatedTimeUnixMs: Number(response.data.updateDateTime) * 1000,
},
},
}
} else {
return {
params: param,
response: {
errorMessage: `The data provider didn't return any value for asset id: ${param}`,
statusCode: 502,
},
}
}
})
},
})
Loading