From 240f1912d84a45090085c30248a11eb4c4d68bc5 Mon Sep 17 00:00:00 2001 From: karen-stepanyan <91897037+karen-stepanyan@users.noreply.github.com> Date: Fri, 8 Mar 2024 16:27:08 +0400 Subject: [PATCH] Added new view-function-multi-chain EA (#3217) * add view-function-multi-chain EA * update test-payload --- .changeset/dry-laws-hang.md | 5 + .pnp.cjs | 21 +++ .../src/generate-readme/readmeBlacklist.json | 1 + .../view-function-multi-chain/CHANGELOG.md | 0 .../view-function-multi-chain/README.md | 57 ++++++++ .../view-function-multi-chain/package.json | 41 ++++++ .../src/config/index.ts | 10 ++ .../src/endpoint/function.ts | 47 +++++++ .../src/endpoint/index.ts | 1 + .../view-function-multi-chain/src/index.ts | 13 ++ .../src/transport/function.ts | 110 +++++++++++++++ .../test-payload.json | 7 + .../__snapshots__/adapter.test.ts.snap | 54 +++++++ .../test/integration/adapter.test.ts | 113 +++++++++++++++ .../test/integration/fixtures.ts | 133 ++++++++++++++++++ .../view-function-multi-chain/tsconfig.json | 9 ++ .../tsconfig.test.json | 7 + packages/tsconfig.json | 3 + packages/tsconfig.test.json | 3 + yarn.lock | 14 ++ 20 files changed, 649 insertions(+) create mode 100644 .changeset/dry-laws-hang.md create mode 100644 packages/sources/view-function-multi-chain/CHANGELOG.md create mode 100644 packages/sources/view-function-multi-chain/README.md create mode 100644 packages/sources/view-function-multi-chain/package.json create mode 100644 packages/sources/view-function-multi-chain/src/config/index.ts create mode 100644 packages/sources/view-function-multi-chain/src/endpoint/function.ts create mode 100644 packages/sources/view-function-multi-chain/src/endpoint/index.ts create mode 100644 packages/sources/view-function-multi-chain/src/index.ts create mode 100644 packages/sources/view-function-multi-chain/src/transport/function.ts create mode 100644 packages/sources/view-function-multi-chain/test-payload.json create mode 100644 packages/sources/view-function-multi-chain/test/integration/__snapshots__/adapter.test.ts.snap create mode 100644 packages/sources/view-function-multi-chain/test/integration/adapter.test.ts create mode 100644 packages/sources/view-function-multi-chain/test/integration/fixtures.ts create mode 100644 packages/sources/view-function-multi-chain/tsconfig.json create mode 100644 packages/sources/view-function-multi-chain/tsconfig.test.json diff --git a/.changeset/dry-laws-hang.md b/.changeset/dry-laws-hang.md new file mode 100644 index 0000000000..95de4fce20 --- /dev/null +++ b/.changeset/dry-laws-hang.md @@ -0,0 +1,5 @@ +--- +'@chainlink/view-function-multi-chain-adapter': major +--- + +Initial version of the EA diff --git a/.pnp.cjs b/.pnp.cjs index 6bc2dd8cef..1a7d4552d9 100644 --- a/.pnp.cjs +++ b/.pnp.cjs @@ -799,6 +799,10 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "name": "@chainlink/view-function-adapter",\ "reference": "workspace:packages/sources/view-function"\ },\ + {\ + "name": "@chainlink/view-function-multi-chain-adapter",\ + "reference": "workspace:packages/sources/view-function-multi-chain"\ + },\ {\ "name": "@chainlink/wbtc-address-set-adapter",\ "reference": "workspace:packages/sources/wbtc-address-set"\ @@ -1039,6 +1043,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@chainlink/uscpi-one-adapter", ["workspace:packages/sources/uscpi-one"]],\ ["@chainlink/vesper-adapter", ["workspace:packages/composites/vesper"]],\ ["@chainlink/view-function-adapter", ["workspace:packages/sources/view-function"]],\ + ["@chainlink/view-function-multi-chain-adapter", ["workspace:packages/sources/view-function-multi-chain"]],\ ["@chainlink/wbtc-address-set-adapter", ["workspace:packages/sources/wbtc-address-set"]],\ ["@chainlink/wootrade-adapter", ["workspace:packages/sources/wootrade"]],\ ["@chainlink/wrapped-adapter", ["workspace:packages/sources/wrapped"]],\ @@ -8453,6 +8458,22 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "SOFT"\ }]\ ]],\ + ["@chainlink/view-function-multi-chain-adapter", [\ + ["workspace:packages/sources/view-function-multi-chain", {\ + "packageLocation": "./packages/sources/view-function-multi-chain/",\ + "packageDependencies": [\ + ["@chainlink/view-function-multi-chain-adapter", "workspace:packages/sources/view-function-multi-chain"],\ + ["@chainlink/external-adapter-framework", "npm:0.33.4"],\ + ["@types/jest", "npm:27.5.2"],\ + ["@types/node", "npm:16.11.68"],\ + ["ethers", "npm:5.7.2"],\ + ["nock", "npm:13.2.9"],\ + ["tslib", "npm:2.4.1"],\ + ["typescript", "patch:typescript@npm%3A5.0.4#~builtin::version=5.0.4&hash=b5f058"]\ + ],\ + "linkType": "SOFT"\ + }]\ + ]],\ ["@chainlink/wbtc-address-set-adapter", [\ ["workspace:packages/sources/wbtc-address-set", {\ "packageLocation": "./packages/sources/wbtc-address-set/",\ diff --git a/packages/scripts/src/generate-readme/readmeBlacklist.json b/packages/scripts/src/generate-readme/readmeBlacklist.json index 66d0b265db..1355e4db09 100644 --- a/packages/scripts/src/generate-readme/readmeBlacklist.json +++ b/packages/scripts/src/generate-readme/readmeBlacklist.json @@ -57,6 +57,7 @@ "the-graph", "token-allocation", "vesper", + "view-function-multi-chain", "xsushi-price" ] } diff --git a/packages/sources/view-function-multi-chain/CHANGELOG.md b/packages/sources/view-function-multi-chain/CHANGELOG.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/sources/view-function-multi-chain/README.md b/packages/sources/view-function-multi-chain/README.md new file mode 100644 index 0000000000..5b4a13e810 --- /dev/null +++ b/packages/sources/view-function-multi-chain/README.md @@ -0,0 +1,57 @@ +# VIEW_FUNCTION_MULTI_CHAIN + +![v3](https://img.shields.io/badge/framework%20version-v3-blueviolet) + +## Environment Variables + +| Required? | Name | Description | Type | Options | Default | +| :-------: | :-------------------: | :---------------------------------------------------------------------------------------: | :----: | :-----: | :-----: | +| ✅ | {NETWORK}\_RPC_URL | RPC url for a NETWORK. NETWORK is the value of `network` input param | string | | | +| ✅ | {NETWORK}\_CHAIN_ID | Chain id for a NETWORK. NETWORK is the value of `network` input param | number | | | +| | 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 | [function](#function-endpoint) | `function` | + +## Function Endpoint + +`function` is the only supported name for this endpoint. + +### Input Params + +| Required? | Name | Aliases | Description | Type | Options | Default | Depends On | Not Valid With | +| :-------: | :---------: | :--------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------: | :------: | :-----: | :-----: | :--------: | :------------: | +| ✅ | signature | `function` | Function signature. Should be formatted as [human readable ABI](https://docs.ethers.io/v5/single-page/#/v5/getting-started/-%23-getting-started--contracts) | string | | | | | +| ✅ | address | `contract` | Address of the contract | string | | | | | +| | inputParams | | Array of function parameters in order | string[] | | | | | +| ✅ | network | | RPC network name | string | | | | | + +### Example + +Request: + +```json +{ + "data": { + "endpoint": "function", + "signature": "function latestAnswer() view returns (int256)", + "address": "0x779877A7B0D9E8603169DdbD7836e478b4624789", + "network": "ETHEREUM_GOERLI" + } +} +``` + +--- + +MIT License diff --git a/packages/sources/view-function-multi-chain/package.json b/packages/sources/view-function-multi-chain/package.json new file mode 100644 index 0000000000..7a5cd0fd9c --- /dev/null +++ b/packages/sources/view-function-multi-chain/package.json @@ -0,0 +1,41 @@ +{ + "name": "@chainlink/view-function-multi-chain-adapter", + "version": "0.0.0", + "description": "Chainlink view-function-multi-chain adapter.", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle", + "view-function-multi-chain" + ], + "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.11.68", + "nock": "13.2.9", + "typescript": "5.0.4" + }, + "dependencies": { + "@chainlink/external-adapter-framework": "0.33.4", + "ethers": "^5.4.6", + "tslib": "2.4.1" + } +} diff --git a/packages/sources/view-function-multi-chain/src/config/index.ts b/packages/sources/view-function-multi-chain/src/config/index.ts new file mode 100644 index 0000000000..3e6d3817b1 --- /dev/null +++ b/packages/sources/view-function-multi-chain/src/config/index.ts @@ -0,0 +1,10 @@ +import { AdapterConfig } from '@chainlink/external-adapter-framework/config' + +export const config = new AdapterConfig({ + BACKGROUND_EXECUTE_MS: { + description: + 'The amount of time the background execute should sleep before performing the next request', + type: 'number', + default: 10_000, + }, +}) diff --git a/packages/sources/view-function-multi-chain/src/endpoint/function.ts b/packages/sources/view-function-multi-chain/src/endpoint/function.ts new file mode 100644 index 0000000000..42fcc98ec3 --- /dev/null +++ b/packages/sources/view-function-multi-chain/src/endpoint/function.ts @@ -0,0 +1,47 @@ +import { AdapterEndpoint } from '@chainlink/external-adapter-framework/adapter' +import { InputParameters } from '@chainlink/external-adapter-framework/validation' +import { config } from '../config' +import { multiChainFunctionTransport } from '../transport/function' + +export const inputParameters = new InputParameters({ + signature: { + type: 'string', + aliases: ['function'], + required: true, + description: + 'Function signature. Should be formatted as [human readable ABI](https://docs.ethers.io/v5/single-page/#/v5/getting-started/-%23-getting-started--contracts)', + }, + address: { + aliases: ['contract'], + required: true, + description: 'Address of the contract', + type: 'string', + }, + inputParams: { + array: true, + description: 'Array of function parameters in order', + type: 'string', + }, + network: { + required: true, + description: 'RPC network name', + type: 'string', + }, +}) + +export type BaseEndpointTypes = { + Parameters: typeof inputParameters.definition + Response: { + Data: { + result: string + } + Result: string + } + Settings: typeof config.settings +} + +export const endpoint = new AdapterEndpoint({ + name: 'function', + transport: multiChainFunctionTransport, + inputParameters, +}) diff --git a/packages/sources/view-function-multi-chain/src/endpoint/index.ts b/packages/sources/view-function-multi-chain/src/endpoint/index.ts new file mode 100644 index 0000000000..71f642ff27 --- /dev/null +++ b/packages/sources/view-function-multi-chain/src/endpoint/index.ts @@ -0,0 +1 @@ +export { endpoint as functionEndpoint } from './function' diff --git a/packages/sources/view-function-multi-chain/src/index.ts b/packages/sources/view-function-multi-chain/src/index.ts new file mode 100644 index 0000000000..9067c8b35e --- /dev/null +++ b/packages/sources/view-function-multi-chain/src/index.ts @@ -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 { functionEndpoint } from './endpoint' + +export const adapter = new Adapter({ + defaultEndpoint: functionEndpoint.name, + name: 'VIEW_FUNCTION_MULTI_CHAIN', + config, + endpoints: [functionEndpoint], +}) + +export const server = (): Promise => expose(adapter) diff --git a/packages/sources/view-function-multi-chain/src/transport/function.ts b/packages/sources/view-function-multi-chain/src/transport/function.ts new file mode 100644 index 0000000000..85a3152c2d --- /dev/null +++ b/packages/sources/view-function-multi-chain/src/transport/function.ts @@ -0,0 +1,110 @@ +import { TransportDependencies } from '@chainlink/external-adapter-framework/transports' +import { AdapterResponse, makeLogger, sleep } from '@chainlink/external-adapter-framework/util' +import { SubscriptionTransport } from '@chainlink/external-adapter-framework/transports/abstract/subscription' +import { EndpointContext } from '@chainlink/external-adapter-framework/adapter' +import { ethers, utils } from 'ethers' +import { BaseEndpointTypes, inputParameters } from '../endpoint/function' +import { AdapterInputError } from '@chainlink/external-adapter-framework/validation/error' + +const logger = makeLogger('View Function Multi Chain') + +export type MultiChainFunctionTransportTypes = BaseEndpointTypes + +type RequestParams = typeof inputParameters.validated + +export class MultiChainFunctionTransport extends SubscriptionTransport { + providers: Record = {} + + async initialize( + dependencies: TransportDependencies, + adapterSettings: MultiChainFunctionTransportTypes['Settings'], + endpointName: string, + transportName: string, + ): Promise { + await super.initialize(dependencies, adapterSettings, endpointName, transportName) + } + + async backgroundHandler( + context: EndpointContext, + entries: RequestParams[], + ) { + await Promise.all(entries.map(async (param) => this.handleRequest(param))) + await sleep(context.adapterSettings.BACKGROUND_EXECUTE_MS) + } + + async handleRequest(param: RequestParams) { + let response: AdapterResponse + try { + response = await this._handleRequest(param) + } catch (e: unknown) { + const errorMessage = e instanceof Error ? e.message : 'Unknown error occurred' + logger.error(e, errorMessage) + response = { + statusCode: (e as AdapterInputError)?.statusCode || 502, + errorMessage, + timestamps: { + providerDataRequestedUnixMs: 0, + providerDataReceivedUnixMs: 0, + providerIndicatedTimeUnixMs: undefined, + }, + } + } + await this.responseCache.write(this.name, [{ params: param, response }]) + } + + async _handleRequest( + param: RequestParams, + ): Promise> { + const { address, signature, inputParams, network } = param + + const networkName = network.toUpperCase() + const networkEnvName = `${networkName}_RPC_URL` + const chainIdEnvName = `${networkName}_CHAIN_ID` + + const rpcUrl = process.env[networkEnvName] + const chainId = Number(process.env[chainIdEnvName]) + + if (!rpcUrl || isNaN(chainId)) { + throw new AdapterInputError({ + statusCode: 400, + message: `Missing '${networkEnvName}' or '${chainIdEnvName}' environment variables.`, + }) + } + + if (!this.providers[networkName]) { + this.providers[networkName] = new ethers.providers.JsonRpcProvider(rpcUrl, chainId) + } + + const iface = new utils.Interface([signature]) + const fnName = iface.functions[Object.keys(iface.functions)[0]].name + + const encoded = iface.encodeFunctionData(fnName, [...(inputParams || [])]) + + const providerDataRequestedUnixMs = Date.now() + const result = await this.providers[networkName].call({ + to: address, + data: encoded, + }) + + return { + data: { + result, + }, + statusCode: 200, + result, + timestamps: { + providerDataRequestedUnixMs, + providerDataReceivedUnixMs: Date.now(), + providerIndicatedTimeUnixMs: undefined, + }, + } + } + + getSubscriptionTtlFromConfig( + adapterSettings: MultiChainFunctionTransportTypes['Settings'], + ): number { + return adapterSettings.WARMUP_SUBSCRIPTION_TTL + } +} + +export const multiChainFunctionTransport = new MultiChainFunctionTransport() diff --git a/packages/sources/view-function-multi-chain/test-payload.json b/packages/sources/view-function-multi-chain/test-payload.json new file mode 100644 index 0000000000..e0348e5505 --- /dev/null +++ b/packages/sources/view-function-multi-chain/test-payload.json @@ -0,0 +1,7 @@ +{ + "requests": [{ + "contract": "0x2c1d072e956AFFC0D435Cb7AC38EF18d24d9127c", + "function": "function latestAnswer() view returns (int256)", + "network": "ETHEREUM_MAINNET" + }] +} diff --git a/packages/sources/view-function-multi-chain/test/integration/__snapshots__/adapter.test.ts.snap b/packages/sources/view-function-multi-chain/test/integration/__snapshots__/adapter.test.ts.snap new file mode 100644 index 0000000000..63b653f512 --- /dev/null +++ b/packages/sources/view-function-multi-chain/test/integration/__snapshots__/adapter.test.ts.snap @@ -0,0 +1,54 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`execute function endpoint should return error for invalid input 1`] = ` +{ + "errorMessage": "unsupported fragment (argument="value", value="symbol () view returns (string)", code=INVALID_ARGUMENT, version=abi/5.7.0)", + "statusCode": 502, + "timestamps": { + "providerDataReceivedUnixMs": 0, + "providerDataRequestedUnixMs": 0, + }, +} +`; + +exports[`execute function endpoint should return success 1`] = ` +{ + "data": { + "result": "0x000000000000000000000000000000000000000000000000000000005ad789f8", + }, + "result": "0x000000000000000000000000000000000000000000000000000000005ad789f8", + "statusCode": 200, + "timestamps": { + "providerDataReceivedUnixMs": 978347471111, + "providerDataRequestedUnixMs": 978347471111, + }, +} +`; + +exports[`execute function endpoint should return success for different network 1`] = ` +{ + "data": { + "result": "0x000000000000000000000000000000000000000000000000eead809f678d30f0", + }, + "result": "0x000000000000000000000000000000000000000000000000eead809f678d30f0", + "statusCode": 200, + "timestamps": { + "providerDataReceivedUnixMs": 978347471111, + "providerDataRequestedUnixMs": 978347471111, + }, +} +`; + +exports[`execute function endpoint should return success with parameters 1`] = ` +{ + "data": { + "result": "0x000000000000000000000000000000000000000000000000000000005cf7ff3b", + }, + "result": "0x000000000000000000000000000000000000000000000000000000005cf7ff3b", + "statusCode": 200, + "timestamps": { + "providerDataReceivedUnixMs": 978347471111, + "providerDataRequestedUnixMs": 978347471111, + }, +} +`; diff --git a/packages/sources/view-function-multi-chain/test/integration/adapter.test.ts b/packages/sources/view-function-multi-chain/test/integration/adapter.test.ts new file mode 100644 index 0000000000..85a3bf8ae7 --- /dev/null +++ b/packages/sources/view-function-multi-chain/test/integration/adapter.test.ts @@ -0,0 +1,113 @@ +import { + TestAdapter, + setEnvVariables, +} from '@chainlink/external-adapter-framework/util/testing-utils' +import * as nock from 'nock' +import { + mockETHGoerliContractCallResponseSuccess, + mockETHMainnetContractCallResponseSuccess, +} from './fixtures' +import * as process from 'process' + +describe('execute', () => { + let spy: jest.SpyInstance + let testAdapter: TestAdapter + let oldEnv: NodeJS.ProcessEnv + + beforeAll(async () => { + oldEnv = JSON.parse(JSON.stringify(process.env)) + process.env.ETHEREUM_MAINNET_RPC_URL = + process.env.ETHEREUM_MAINNET_RPC_URL ?? 'http://localhost:8545' + process.env.ETHEREUM_MAINNET_CHAIN_ID = process.env.ETHEREUM_MAINNET_CHAIN_ID ?? '1' + process.env.ETHEREUM_GOERLI_RPC_URL = + process.env.ETHEREUM_GOERLI_RPC_URL ?? 'http://localhost:8554' + process.env.ETHEREUM_GOERLI_CHAIN_ID = process.env.ETHEREUM_GOERLI_CHAIN_ID ?? '5' + process.env.BACKGROUND_EXECUTE_MS = '0' + const mockDate = new Date('2001-01-01T11:11:11.111Z') + spy = jest.spyOn(Date, 'now').mockReturnValue(mockDate.getTime()) + + const adapter = (await import('./../../src')).adapter + testAdapter = await TestAdapter.startWithMockedCache(adapter, { + testAdapter: {} as TestAdapter, + }) + }) + + afterAll(async () => { + setEnvVariables(oldEnv) + await testAdapter.api.close() + nock.restore() + nock.cleanAll() + spy.mockRestore() + }) + + describe('function endpoint', () => { + it('should return success', async () => { + const data = { + contract: '0x2c1d072e956AFFC0D435Cb7AC38EF18d24d9127c', + function: 'function latestAnswer() external view returns (int256)', + network: 'ethereum_mainnet', + } + mockETHMainnetContractCallResponseSuccess() + const response = await testAdapter.request(data) + expect(response.statusCode).toBe(200) + expect(response.json()).toMatchSnapshot() + }) + + it('should return success for different network', async () => { + const data = { + contract: '0x779877a7b0d9e8603169ddbd7836e478b4624789', + function: 'function latestAnswer() external view returns (int256)', + network: 'ETHEREUM_GOERLI', + } + mockETHGoerliContractCallResponseSuccess() + const response = await testAdapter.request(data) + expect(response.statusCode).toBe(200) + expect(response.json()).toMatchSnapshot() + }) + + it('should return success with parameters', async () => { + const data = { + contract: '0x2c1d072e956AFFC0D435Cb7AC38EF18d24d9127c', + function: 'function getAnswer(uint256 roundId) external view returns (int256)', + inputParams: ['110680464442257317364'], + network: 'ethereum_mainnet', + } + mockETHMainnetContractCallResponseSuccess() + const response = await testAdapter.request(data) + expect(response.statusCode).toBe(200) + expect(response.json()).toMatchSnapshot() + }) + + it('should return error for missing RPC url env var', async () => { + const data = { + contract: '0xdAC17F958D2ee523a2206206994597C13D831ec7', + function: 'function getAnswer(uint256 roundId) external view returns (int256)', + network: 'arbitrum_mainnet', // ARBITRUM_MAINNET_RPC_URL is not provided + } + const response = await testAdapter.request(data) + expect(response.statusCode).toBe(400) + }) + + it('should return error for missing chain id env var', async () => { + process.env.ARBITRUM_MAINNET_RPC_URL = 'http://localhost:8546' + const data = { + contract: '0xdAC17F958D2ee523a2206206994597C13D831ec7', + function: 'function getAnswer(uint256 roundId) external view returns (int256)', + network: 'arbitrum_mainnet', // ARBITRUM_MAINNET_CHAIN_ID is not provided + } + const response = await testAdapter.request(data) + expect(response.statusCode).toBe(400) + }) + + it('should return error for invalid input', async () => { + const data = { + contract: '0xdAC17F958D2ee523a2206206994597C13D831ec7', + function: 'symbol() view returns (string)', // missing 'function' keyword + network: 'ethereum_mainnet', + } + const response = await testAdapter.request(data) + expect(response.statusCode).toBe(502) + expect(response.json()).toMatchSnapshot() + }) + }) +}) diff --git a/packages/sources/view-function-multi-chain/test/integration/fixtures.ts b/packages/sources/view-function-multi-chain/test/integration/fixtures.ts new file mode 100644 index 0000000000..cbd2ed5738 --- /dev/null +++ b/packages/sources/view-function-multi-chain/test/integration/fixtures.ts @@ -0,0 +1,133 @@ +import nock from 'nock' + +type JsonRpcPayload = { + id: number + method: string + // eslint-disable-next-line @typescript-eslint/no-explicit-any + params: Array | Record + jsonrpc: '2.0' +} + +export const mockETHMainnetContractCallResponseSuccess = (): nock.Scope => + nock('http://localhost:8545', { + encodedQueryParams: true, + }) + .persist() + .post('/', { method: 'eth_chainId', params: [], id: /^\d+$/, jsonrpc: '2.0' }) + .reply( + 200, + (_, request: JsonRpcPayload) => ({ jsonrpc: '2.0', id: request['id'], result: '0x1' }), + [ + 'Content-Type', + 'application/json', + 'Connection', + 'close', + 'Vary', + 'Accept-Encoding', + 'Vary', + 'Origin', + ], + ) + .persist() + .post('/', { + method: 'eth_call', + params: [{ to: '0x2c1d072e956affc0d435cb7ac38ef18d24d9127c', data: '0x50d25bcd' }, 'latest'], + id: /^\d+$/, + jsonrpc: '2.0', + }) + .reply( + 200, + (_, request: JsonRpcPayload) => ({ + jsonrpc: '2.0', + id: request['id'], + result: '0x000000000000000000000000000000000000000000000000000000005ad789f8', + }), + [ + 'Content-Type', + 'application/json', + 'Connection', + 'close', + 'Vary', + 'Accept-Encoding', + 'Vary', + 'Origin', + ], + ) + .persist() + .post('/', { + method: 'eth_call', + params: [ + { + to: '0x2c1d072e956affc0d435cb7ac38ef18d24d9127c', + data: '0xb5ab58dc0000000000000000000000000000000000000000000000060000000000001df4', + }, + 'latest', + ], + id: /^\d+$/, + jsonrpc: '2.0', + }) + .reply( + 200, + (_, request: JsonRpcPayload) => ({ + jsonrpc: '2.0', + id: request['id'], + result: '0x000000000000000000000000000000000000000000000000000000005cf7ff3b', + }), + [ + 'Content-Type', + 'application/json', + 'Connection', + 'close', + 'Vary', + 'Accept-Encoding', + 'Vary', + 'Origin', + ], + ) + .persist() + +export const mockETHGoerliContractCallResponseSuccess = (): nock.Scope => + nock('http://localhost:8554', { + encodedQueryParams: true, + }) + .persist() + .post('/', { method: 'eth_chainId', params: [], id: /^\d+$/, jsonrpc: '2.0' }) + .reply( + 200, + (_, request: JsonRpcPayload) => ({ jsonrpc: '2.0', id: request['id'], result: '0x5' }), + [ + 'Content-Type', + 'application/json', + 'Connection', + 'close', + 'Vary', + 'Accept-Encoding', + 'Vary', + 'Origin', + ], + ) + .persist() + .post('/', { + method: 'eth_call', + params: [{ to: '0x779877a7b0d9e8603169ddbd7836e478b4624789', data: '0x50d25bcd' }, 'latest'], + id: /^\d+$/, + jsonrpc: '2.0', + }) + .reply( + 200, + (_, request: JsonRpcPayload) => ({ + jsonrpc: '2.0', + id: request['id'], + result: '0x000000000000000000000000000000000000000000000000eead809f678d30f0', + }), + [ + 'Content-Type', + 'application/json', + 'Connection', + 'close', + 'Vary', + 'Accept-Encoding', + 'Vary', + 'Origin', + ], + ) diff --git a/packages/sources/view-function-multi-chain/tsconfig.json b/packages/sources/view-function-multi-chain/tsconfig.json new file mode 100644 index 0000000000..f59363fd76 --- /dev/null +++ b/packages/sources/view-function-multi-chain/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src" + }, + "include": ["src/**/*", "src/**/*.json"], + "exclude": ["dist", "**/*.spec.ts", "**/*.test.ts"] +} diff --git a/packages/sources/view-function-multi-chain/tsconfig.test.json b/packages/sources/view-function-multi-chain/tsconfig.test.json new file mode 100644 index 0000000000..e3de28cb5c --- /dev/null +++ b/packages/sources/view-function-multi-chain/tsconfig.test.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src/**/*", "**/test", "src/**/*.json"], + "compilerOptions": { + "noEmit": true + } +} diff --git a/packages/tsconfig.json b/packages/tsconfig.json index 0a405c7d23..c06fc79ff4 100644 --- a/packages/tsconfig.json +++ b/packages/tsconfig.json @@ -578,6 +578,9 @@ { "path": "./sources/view-function" }, + { + "path": "./sources/view-function-multi-chain" + }, { "path": "./sources/wbtc-address-set" }, diff --git a/packages/tsconfig.test.json b/packages/tsconfig.test.json index bac3d148eb..03031bcc5b 100644 --- a/packages/tsconfig.test.json +++ b/packages/tsconfig.test.json @@ -578,6 +578,9 @@ { "path": "./sources/view-function/tsconfig.test.json" }, + { + "path": "./sources/view-function-multi-chain/tsconfig.test.json" + }, { "path": "./sources/wbtc-address-set/tsconfig.test.json" }, diff --git a/yarn.lock b/yarn.lock index 16cd912cfb..89972714cc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5494,6 +5494,20 @@ __metadata: languageName: unknown linkType: soft +"@chainlink/view-function-multi-chain-adapter@workspace:packages/sources/view-function-multi-chain": + version: 0.0.0-use.local + resolution: "@chainlink/view-function-multi-chain-adapter@workspace:packages/sources/view-function-multi-chain" + dependencies: + "@chainlink/external-adapter-framework": 0.33.4 + "@types/jest": 27.5.2 + "@types/node": 16.11.68 + ethers: ^5.4.6 + nock: 13.2.9 + tslib: 2.4.1 + typescript: 5.0.4 + languageName: unknown + linkType: soft + "@chainlink/wbtc-address-set-adapter@workspace:*, @chainlink/wbtc-address-set-adapter@workspace:packages/sources/wbtc-address-set": version: 0.0.0-use.local resolution: "@chainlink/wbtc-address-set-adapter@workspace:packages/sources/wbtc-address-set"