Skip to content

Backed-Fi EA #3929

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

Merged
merged 7 commits into from
Jul 15, 2025
Merged
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/purple-cobras-double.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@chainlink/backed-fi-adapter': major
---

Version 1.0.0 of Backed-Fi EA
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.
54 changes: 54 additions & 0 deletions packages/sources/backed-fi/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# BACKED_FI

![0.0.0](https://img.shields.io/github/package-json/v/smartcontractkit/external-adapters-js?filename=packages/sources/backed-fi/package.json) ![v3](https://img.shields.io/badge/framework%20version-v3-blueviolet)

This document was generated automatically. Please see [README Generator](../../scripts#readme-generator) for more info.

## Environment Variables

| Required? | Name | Description | Type | Options | Default |
| :-------: | :----------: | :---------------------------: | :----: | :-----: | :----------------------------------: |
| | API_ENDPOINT | An API endpoint for Backed-Fi | string | | `https://api.backed.fi/api/v1/token` |

---

## Data Provider Rate Limits

There are no rate limits for this adapter.

---

## Input Parameters

| Required? | Name | Description | Type | Options | Default |
| :-------: | :------: | :-----------------: | :----: | :--------------------------------: | :----------: |
| | endpoint | The endpoint to use | string | [multiplier](#multiplier-endpoint) | `multiplier` |

## Multiplier Endpoint

`multiplier` is the only supported name for this endpoint.

### Input Params

| Required? | Name | Aliases | Description | Type | Options | Default | Depends On | Not Valid With |
| :-------: | :---------: | :-----: | :--------------------------------: | :----: | :-----: | :-----: | :--------: | :------------: |
|| tokenSymbol | | The symbol of token to query | string | | | | |
|| network | | The symbol of the network to query | string | | | | |

### Example

Request:

```json
{
"data": {
"endpoint": "multiplier",
"tokenSymbol": "AMZNx",
"network": "Solana"
}
}
```

---

MIT License
40 changes: 40 additions & 0 deletions packages/sources/backed-fi/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "@chainlink/backed-fi-adapter",
"version": "0.0.0",
"description": "Chainlink backed-fi adapter.",
"keywords": [
"Chainlink",
"LINK",
"blockchain",
"oracle",
"backed-fi"
],
"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": "29.5.14",
"@types/node": "22.14.1",
"nock": "13.5.6",
"typescript": "5.8.3"
},
"dependencies": {
"@chainlink/external-adapter-framework": "2.6.0",
"tslib": "2.4.1"
}
}
9 changes: 9 additions & 0 deletions packages/sources/backed-fi/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 Backed-Fi',
type: 'string',
default: 'https://api.backed.fi/api/v1/token',
},
})
1 change: 1 addition & 0 deletions packages/sources/backed-fi/src/endpoint/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { endpoint as multiplier } from './multiplier'
48 changes: 48 additions & 0 deletions packages/sources/backed-fi/src/endpoint/multiplier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { AdapterEndpoint } from '@chainlink/external-adapter-framework/adapter'
import { InputParameters } from '@chainlink/external-adapter-framework/validation'
import { config } from '../config'
import { httpTransport } from '../transport/multiplier'

export const inputParameters = new InputParameters(
{
tokenSymbol: {
required: true,
type: 'string',
description: 'The symbol of token to query',
},
network: {
required: true,
type: 'string',
description: 'The symbol of the network to query',
},
},
[
{
tokenSymbol: 'AMZNx',
network: 'Solana',
},
],
)

export type GMCIResultResponse = {
Result: number
Data: {
currentMultiplier: number
newMultiplier: number
activationDateTime: number
reason: string | null
}
}

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

export const endpoint = new AdapterEndpoint({
name: 'multiplier',
aliases: [],
transport: httpTransport,
inputParameters,
})
21 changes: 21 additions & 0 deletions packages/sources/backed-fi/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 { multiplier } from './endpoint'

export const adapter = new Adapter({
defaultEndpoint: multiplier.name,
name: 'BACKED_FI',
config,
endpoints: [multiplier],
rateLimiting: {
tiers: {
default: {
rateLimit1m: 6,
note: 'Setting reasonable default limits',
},
},
},
})

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

export interface ResponseSchema {
activationDateTime: number
currentMultiplier: number
newMultiplier: number
reason: string | null
error: string
message: string
}

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,
url: `${param.tokenSymbol}/multiplier`,
params: {
network: param.network,
},
},
}
})
},
parseResponse: (params, response) => {
if (!response.data) {
return params.map((param) => {
return {
params: param,
response: {
errorMessage: `The data provider didn't return any value for ${param.tokenSymbol} on network ${param.network}`,
statusCode: 502,
},
}
})
}

if (response.data.error) {
return params.map((param) => {
return {
params: param,
response: {
errorMessage: response.data.message,
statusCode: 502,
},
}
})
}

return params.map((param) => {
const result = response.data.currentMultiplier
return {
params: param,
response: {
result: result,
data: response.data,
},
}
})
},
})
6 changes: 6 additions & 0 deletions packages/sources/backed-fi/test-payload.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"requests": [{
"tokenSymbol": "METAx",
"network": "Arbitrum"
}]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`execute multiplier endpoint should return failure - invalid network 1`] = `
{
"errorMessage": "Cannot read properties of undefined (reading 'multiplier')",
"statusCode": 502,
"timestamps": {
"providerDataReceivedUnixMs": 978347471111,
"providerDataRequestedUnixMs": 978347471111,
},
}
`;

exports[`execute multiplier endpoint should return success 1`] = `
{
"data": {
"activationDateTime": 0,
"currentMultiplier": 1,
"newMultiplier": 0,
"reason": null,
},
"result": 1,
"statusCode": 200,
"timestamps": {
"providerDataReceivedUnixMs": 978347471111,
"providerDataRequestedUnixMs": 978347471111,
},
}
`;
59 changes: 59 additions & 0 deletions packages/sources/backed-fi/test/integration/adapter.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {
TestAdapter,
setEnvVariables,
} from '@chainlink/external-adapter-framework/util/testing-utils'
import * as nock from 'nock'
import { mockResponseFailure, 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))

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('multiplier endpoint', () => {
it('should return success', async () => {
const data = {
tokenSymbol: 'METAx',
network: 'Arbitrum',
endpoint: 'multiplier',
}
mockResponseSuccess()
const response = await testAdapter.request(data)
expect(response.statusCode).toBe(200)
expect(response.json()).toMatchSnapshot()
})

it('should return failure - invalid network', async () => {
const data = {
tokenSymbol: 'METAx',
network: 'Ethereum',
endpoint: 'multiplier',
}
mockResponseFailure()
const response = await testAdapter.request(data)
expect(response.statusCode).toBe(502)
expect(response.json()).toMatchSnapshot()
})
})
})
Loading
Loading