Skip to content
Closed
Show file tree
Hide file tree
Changes from 6 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/orange-cats-rhyme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@chainlink/accountable-adapter': major
---

Accountable 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.

1 change: 1 addition & 0 deletions packages/sources/accountable/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# @chainlink/accountable-adapter
54 changes: 54 additions & 0 deletions packages/sources/accountable/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# ACCOUNTABLE

![1.5.3](https://img.shields.io/github/package-json/v/smartcontractkit/external-adapters-js?filename=packages/sources/accountable/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 | API Endpoint to use | string | | |

---

## Data Provider Rate Limits

| Name | Requests/credits per second | Requests/credits per minute | Requests/credits per hour | Note |
| :-----: | :-------------------------: | :-------------------------: | :-----------------------: | :--: |
| default | | 30 | | |

---

## Input Parameters

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

## Reserve Endpoint

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

### Input Params

| Required? | Name | Aliases | Description | Type | Options | Default | Depends On | Not Valid With |
| :-------: | :----: | :-----: | :------------------------------------------------: | :----: | :-----: | :-----: | :--------: | :------------: |
| ✅ | client | | The name of the Accountable client to consume from | string | | | | |

### Example

Request:

```json
{
"data": {
"endpoint": "reserve",
"client": "axis"
}
}
```

---

MIT License
40 changes: 40 additions & 0 deletions packages/sources/accountable/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "@chainlink/accountable-adapter",
"version": "1.5.3",
"description": "Chainlink's Accountable adapter.",
"keywords": [
"Chainlink",
"LINK",
"blockchain",
"oracle",
"accountable"
],
"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.8.0",
"tslib": "2.4.1"
}
}
9 changes: 9 additions & 0 deletions packages/sources/accountable/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: 'API Endpoint to use',
type: 'string',
required: true,
},
})
1 change: 1 addition & 0 deletions packages/sources/accountable/src/endpoint/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { endpoint as reserve } from './reserve'
41 changes: 41 additions & 0 deletions packages/sources/accountable/src/endpoint/reserve.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {
PoRProviderEndpoint,
PoRProviderResponse,
} from '@chainlink/external-adapter-framework/adapter/por'
import { InputParameters } from '@chainlink/external-adapter-framework/validation'
import { AdapterError } from '@chainlink/external-adapter-framework/validation/error'
import { config } from '../config'
import { getApiKey, httpTransport } from '../transport/reserve'

export const inputParameters = new InputParameters(
{
client: {
type: 'string',
required: true,
description: 'The name of the Accountable client to consume from',
},
},
[
{
client: 'axis',
},
],
)

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

export const endpoint = new PoRProviderEndpoint({
name: 'reserve',
transport: httpTransport,
inputParameters,
customInputValidation: (request): AdapterError | undefined => {
if (request.requestContext.data.client) {
getApiKey(request.requestContext.data.client)
}
return
},
})
20 changes: 20 additions & 0 deletions packages/sources/accountable/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { expose, ServerInstance } from '@chainlink/external-adapter-framework'
import { PoRAdapter } from '@chainlink/external-adapter-framework/adapter/por'
import { config } from './config'
import { reserve } from './endpoint'

export const adapter = new PoRAdapter({
defaultEndpoint: reserve.name,
name: 'ACCOUNTABLE',
config,
endpoints: [reserve],
rateLimiting: {
tiers: {
default: {
rateLimit1m: 30,
},
},
},
})

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

export interface ResponseSchema {
client: string
totalReserve: number
totalSupply: number
underlyingAssets: {
name: string
value: number
}[]
collateralization: number
}

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

export const getApiKey = (client: string) => {
const apiKeyName = `${client.replace(/-/g, '_').toUpperCase()}_API_KEY`
const apiKeyValue = process.env[apiKeyName]

if (!apiKeyValue) {
throw new AdapterInputError({
statusCode: 400,
message: `Missing '${apiKeyName}' environment variable.`,
})
}

return apiKeyValue
}

export const httpTransport = new HttpTransport<HttpTransportTypes>({
prepareRequests: (params, config) => {
return params.map((param) => {
const client = param.client
return {
params: [param],
request: {
baseURL: config.API_ENDPOINT,
params: {
client: client,
},
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${getApiKey(client)}`,
},
},
}
})
},
parseResponse: (params, response) => {
return params.map((param) => {
const result = response.data.totalReserve
const totalReserve = Number(result)
return {
params: param,
response: {
result,
data: {
result,
totalReserve,
ripcord: false,
},
},
}
})
},
})
8 changes: 8 additions & 0 deletions packages/sources/accountable/test-payload.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"requests": [
{
"endpoint": "reserve",
"client": "axis"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`execute reserve endpoint should fail if client name is not present or wrong 1`] = `
{
"error": {
"message": "[Param: client] param is required but no value was provided",
"name": "AdapterError",
},
"status": "errored",
"statusCode": 400,
}
`;

exports[`execute reserve endpoint should return success for axis 1`] = `
{
"data": {
"result": "40438382.35",
"ripcord": false,
"totalReserve": 40438382.35,
},
"result": "40438382.35",
"statusCode": 200,
"timestamps": {
"providerDataReceivedUnixMs": 978347471111,
"providerDataRequestedUnixMs": 978347471111,
},
}
`;

exports[`execute reserve endpoint should return success for unitas 1`] = `
{
"data": {
"result": "25559832.85",
"ripcord": false,
"totalReserve": 25559832.85,
},
"result": "25559832.85",
"statusCode": 200,
"timestamps": {
"providerDataReceivedUnixMs": 978347471111,
"providerDataRequestedUnixMs": 978347471111,
},
}
`;
41 changes: 41 additions & 0 deletions packages/sources/accountable/test/integration/fixtures.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import nock from 'nock'

export const mockReserveAxisResponseSuccess = (): nock.Scope =>
nock('http://test-endpoint-new?client=axis', {
encodedQueryParams: true,
})
.get('/')
.query({ client: 'axis' })
.reply(200, {
client: 'axis',
totalReserve: '40438382.35',
totalSupply: '40355393.07',
underlyingAssets: [
{ name: 'Copper', value: 20000000 },
{ name: 'Fireblocks', value: 20438382.35 },
{ name: 'Insurance Fund', value: 630502.74 },
{ name: 'Ethereum Chain', value: 76414.71688973988 },
{ name: 'Binance', value: 3095.5639951004273 },
{ name: 'BNB Smart Chain', value: 7.3854242495045 },
],
collateralization: 1.002056,
})
.persist()

export const mockReserveUnitasResponseSuccess = (): nock.Scope =>
nock('http://test-endpoint-new?client=unitas', {
encodedQueryParams: true,
})
.get('/')
.query({ client: 'unitas' })
.reply(200, {
client: 'unitas',
totalReserve: '25559832.85',
totalSupply: '25559832.85',
underlyingAssets: [
{ name: 'Solana', value: 18440848.73 },
{ name: 'Binance', value: 7118984.11 },
],
collateralization: 1.00416,
})
.persist()
Loading
Loading