-
Notifications
You must be signed in to change notification settings - Fork 326
OPDATA 4790 #4440
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
base: main
Are you sure you want to change the base?
OPDATA 4790 #4440
Changes from all commits
065a794
15275a6
81fb682
6a3af7b
11611b1
4e739a5
d4ad5b6
baee734
59cf9c0
7ab8c9b
5005a98
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| '@chainlink/xusd-usd-exchange-rate-adapter': minor | ||
| --- | ||
|
|
||
| Add xusd-usd-exchange-rate adapter | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| # XUSD_USD_EXCHANGE_RATE | ||
wilson-cl marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
|   | ||
|
|
||
| This document was generated automatically. Please see [README Generator](../../scripts#readme-generator) for more info. | ||
|
|
||
| ## Environment Variables | ||
|
|
||
| | Required? | Name | Description | Type | Options | Default | | ||
| | :-------: | :-------------------: | :---------------------------------------------------------------------------------------: | :----: | :-----: | :-----: | | ||
| | ✅ | ETHEREUM_RPC_URL | Ethereum JSON-RPC endpoint URL provided by the node operator | string | | | | ||
| | | BACKGROUND_EXECUTE_MS | The amount of time the background execute should sleep before performing the next request | number | | `10000` | | ||
|
|
||
| --- | ||
|
|
||
| ## 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 | [round](#round-endpoint) | `round` | | ||
|
|
||
| ## Round Endpoint | ||
|
|
||
| `round` is the only supported name for this endpoint. | ||
|
|
||
| ### Input Params | ||
|
|
||
| There are no input parameters for this endpoint. | ||
|
|
||
| ### Example | ||
|
|
||
| Request: | ||
|
|
||
| ```json | ||
| { | ||
| "data": { | ||
| "endpoint": "round" | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| MIT License | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| { | ||
| "name": "@chainlink/xusd-usd-exchange-rate-adapter", | ||
| "version": "0.0.0", | ||
| "description": "Chainlink XUSD-USD Exchange Rate adapter for fetching the round value from the XUSD contract on Ethereum.", | ||
| "keywords": [ | ||
| "Chainlink", | ||
| "LINK", | ||
| "blockchain", | ||
| "oracle", | ||
| "xusd-usd-exchange-rate" | ||
| ], | ||
| "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.11.2", | ||
| "tslib": "2.4.1" | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| import { AdapterConfig } from '@chainlink/external-adapter-framework/config' | ||
|
|
||
| export const config = new AdapterConfig({ | ||
| ETHEREUM_RPC_URL: { | ||
| description: 'Ethereum JSON-RPC endpoint URL provided by the node operator', | ||
| type: 'string', | ||
| required: true, | ||
| }, | ||
| BACKGROUND_EXECUTE_MS: { | ||
| description: | ||
| 'The amount of time the background execute should sleep before performing the next request', | ||
| type: 'number', | ||
| default: 10_000, | ||
| }, | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| export { endpoint as round } from './round' |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| import { AdapterEndpoint } from '@chainlink/external-adapter-framework/adapter' | ||
| import { SingleNumberResultResponse } from '@chainlink/external-adapter-framework/util' | ||
| import { InputParameters } from '@chainlink/external-adapter-framework/validation' | ||
| import { config } from '../config' | ||
| import { httpTransport } from '../transport/round' | ||
|
|
||
| export const inputParameters = new InputParameters({}, [{}]) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is an EmptyInputParameters from framework |
||
|
|
||
| export type BaseEndpointTypes = { | ||
| Parameters: typeof inputParameters.definition | ||
| Response: SingleNumberResultResponse | ||
| Settings: typeof config.settings | ||
| } | ||
|
|
||
| export const endpoint = new AdapterEndpoint({ | ||
| name: 'round', | ||
| transport: httpTransport, | ||
| inputParameters, | ||
| }) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| import { expose, ServerInstance } from '@chainlink/external-adapter-framework' | ||
| import { Adapter } from '@chainlink/external-adapter-framework/adapter' | ||
| import { config } from './config' | ||
| import { round } from './endpoint' | ||
|
|
||
| export const adapter = new Adapter({ | ||
| defaultEndpoint: round.name, | ||
| name: 'XUSD_USD_EXCHANGE_RATE', | ||
| config, | ||
| endpoints: [round], | ||
| }) | ||
|
|
||
| export const server = (): Promise<ServerInstance | undefined> => expose(adapter) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| import { HttpTransport } from '@chainlink/external-adapter-framework/transports' | ||
| import { BaseEndpointTypes } from '../endpoint/round' | ||
|
|
||
| export const XUSD_CONTRACT_ADDRESS = '0xE2Fc85BfB48C4cF147921fBE110cf92Ef9f26F94' | ||
| export const ROUND_FUNCTION_SELECTOR = '0x146ca531' | ||
|
Comment on lines
+4
to
+5
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These should come from input |
||
|
|
||
| export interface JsonRpcRequest { | ||
| jsonrpc: string | ||
| method: string | ||
| params: [{ to: string; data: string }, string] | ||
| id: number | ||
| } | ||
|
|
||
| export interface JsonRpcResponse { | ||
| jsonrpc: string | ||
| id: number | ||
| result?: string | ||
| error?: { | ||
| code: number | ||
| message: string | ||
| } | ||
| } | ||
|
|
||
| export type HttpTransportTypes = BaseEndpointTypes & { | ||
| Provider: { | ||
| RequestBody: JsonRpcRequest | ||
| ResponseBody: JsonRpcResponse | ||
| } | ||
| } | ||
|
|
||
| export type RequestParams = Record<string, never> | ||
|
|
||
| export interface PreparedRequest { | ||
| params: RequestParams[] | ||
| request: { | ||
| baseURL: string | ||
| url: string | ||
| method: string | ||
| headers: Record<string, string> | ||
| data: JsonRpcRequest | ||
| } | ||
| } | ||
|
|
||
| export const buildEthCallRequest = (rpcUrl: string): PreparedRequest['request'] => ({ | ||
wilson-cl marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| baseURL: rpcUrl, | ||
| url: '', | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| }, | ||
| data: { | ||
| jsonrpc: '2.0', | ||
| method: 'eth_call', | ||
| params: [ | ||
| { | ||
| to: XUSD_CONTRACT_ADDRESS, | ||
| data: ROUND_FUNCTION_SELECTOR, | ||
| }, | ||
| 'latest', | ||
| ], | ||
| id: 1, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we hardcoding this to 1 everytime? |
||
| }, | ||
| }) | ||
|
|
||
| export const parseHexToInt = (hexValue: string): number => { | ||
wilson-cl marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return parseInt(hexValue, 16) | ||
| } | ||
|
|
||
| export const buildErrorResponse = (param: RequestParams, errorMessage: string) => ({ | ||
| params: param, | ||
| response: { | ||
| errorMessage, | ||
| statusCode: 502, | ||
| }, | ||
| }) | ||
|
|
||
| export const buildSuccessResponse = (param: RequestParams, result: number) => ({ | ||
wilson-cl marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| params: param, | ||
| response: { | ||
| result, | ||
| data: { | ||
| result, | ||
| }, | ||
| }, | ||
| }) | ||
|
|
||
| export const httpTransport = new HttpTransport<HttpTransportTypes>({ | ||
| prepareRequests: (params, config) => { | ||
| return params.map((param) => ({ | ||
| params: [param], | ||
| request: buildEthCallRequest(config.ETHEREUM_RPC_URL), | ||
| })) | ||
| }, | ||
|
Comment on lines
+88
to
+93
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is wrong, you are building the same request over and over again. Just need to return 1 request here |
||
| parseResponse: (params, response) => { | ||
| if (response.data.error) { | ||
| return params.map((param) => | ||
| buildErrorResponse(param, `RPC error: ${response.data.error?.message}`), | ||
| ) | ||
| } | ||
|
|
||
| if (!response.data.result) { | ||
| return params.map((param) => buildErrorResponse(param, 'No result returned from eth_call')) | ||
| } | ||
|
|
||
| const result = parseHexToInt(response.data.result) | ||
|
|
||
| return params.map((param) => buildSuccessResponse(param, result)) | ||
| }, | ||
| }) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| { | ||
| "requests": [ | ||
| { | ||
| "endpoint": "round" | ||
| } | ||
| ] | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| // Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
|
||
| exports[`execute round endpoint happy path should return success 1`] = ` | ||
| { | ||
| "data": { | ||
| "result": 289, | ||
| }, | ||
| "result": 289, | ||
| "statusCode": 200, | ||
| "timestamps": { | ||
| "providerDataReceivedUnixMs": 978347471111, | ||
| "providerDataRequestedUnixMs": 978347471111, | ||
| }, | ||
| } | ||
| `; | ||
|
|
||
| exports[`execute round endpoint happy path should return success with empty request body (default endpoint) 1`] = ` | ||
| { | ||
| "data": { | ||
| "result": 289, | ||
| }, | ||
| "result": 289, | ||
| "statusCode": 200, | ||
| "timestamps": { | ||
| "providerDataReceivedUnixMs": 978347471111, | ||
| "providerDataRequestedUnixMs": 978347471111, | ||
| }, | ||
| } | ||
| `; | ||
|
|
||
| exports[`execute round endpoint upstream failures should handle RPC error response 1`] = ` | ||
| { | ||
| "errorMessage": "RPC error: execution reverted", | ||
| "statusCode": 502, | ||
| "timestamps": { | ||
| "providerDataReceivedUnixMs": 978347471111, | ||
| "providerDataRequestedUnixMs": 978347471111, | ||
| }, | ||
| } | ||
| `; | ||
|
|
||
| exports[`execute round endpoint upstream failures should handle response with no result 1`] = ` | ||
| { | ||
| "errorMessage": "No result returned from eth_call", | ||
| "statusCode": 502, | ||
| "timestamps": { | ||
| "providerDataReceivedUnixMs": 978347471111, | ||
| "providerDataRequestedUnixMs": 978347471111, | ||
| }, | ||
| } | ||
| `; | ||
|
|
||
| exports[`execute round endpoint upstream failures should handle server error (5xx) 1`] = ` | ||
| { | ||
| "errorMessage": "Provider request failed with status 500: "Internal Server Error"", | ||
| "statusCode": 502, | ||
| "timestamps": { | ||
| "providerDataReceivedUnixMs": 978347471111, | ||
| "providerDataRequestedUnixMs": 978347471111, | ||
| }, | ||
| } | ||
| `; | ||
|
|
||
| exports[`execute round endpoint validation errors should return error for invalid endpoint 1`] = ` | ||
| { | ||
| "error": { | ||
| "message": "Adapter does not have a "invalid-endpoint" endpoint.", | ||
| "name": "AdapterError", | ||
| }, | ||
| "status": "errored", | ||
| "statusCode": 404, | ||
| } | ||
| `; |
Uh oh!
There was an error while loading. Please reload this page.