Skip to content
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/early-shoes-move.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@chainlink/static-market-hours-adapter': major
---

Initial version
22 changes: 22 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/static-market-hours/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Chainlink External Adapter for static-market-hours

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 static-market-hours`.
42 changes: 42 additions & 0 deletions packages/sources/static-market-hours/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"name": "@chainlink/static-market-hours-adapter",
"version": "0.0.0",
"description": "Chainlink static-market-hours adapter.",
"keywords": [
"Chainlink",
"LINK",
"blockchain",
"oracle",
"static-market-hours"
],
"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.18.0",
"@date-fns/tz": "1.4.1",
"date-fns": "^4.1.0",
"tslib": "2.4.1"
}
}
33 changes: 33 additions & 0 deletions packages/sources/static-market-hours/src/config/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {
MarketStatus,
TwentyfourFiveMarketStatus,
} from '@chainlink/external-adapter-framework/adapter'
import { AdapterConfig } from '@chainlink/external-adapter-framework/config'
import { getScheduleValidationError } from '../util/schedule'

export const config = new AdapterConfig({
Comment thread
mxiao-cll marked this conversation as resolved.
MARKET_REGULAR_SCHEDULE: {
description:
'JSON encoded schedule data for ${MARKET} which is specified in the input parameter',
type: 'string',
required: true,
sensitive: true,
variablePlaceholder: 'MARKET',
validate: {
meta: {},
fn: (value) => getScheduleValidationError(value!, MarketStatus),
},
},
MARKET_24_5_SCHEDULE: {
description:
'JSON encoded schedule data for ${MARKET} which is specified in the input parameter, when type = "24/5"',
type: 'string',
required: true,
sensitive: true,
variablePlaceholder: 'MARKET',
validate: {
meta: {},
fn: (value) => getScheduleValidationError(value!, TwentyfourFiveMarketStatus),
},
},
})
1 change: 1 addition & 0 deletions packages/sources/static-market-hours/src/endpoint/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { endpoint as marketStatus } from './market-status'
39 changes: 39 additions & 0 deletions packages/sources/static-market-hours/src/endpoint/market-status.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {
MarketStatusEndpoint,
marketStatusEndpointInputParametersDefinition,
MarketStatusResultResponse,
} from '@chainlink/external-adapter-framework/adapter'
import { InputParameters } from '@chainlink/external-adapter-framework/validation'
import { AdapterInputError } from '@chainlink/external-adapter-framework/validation/error'
import { config } from '../config'
import { customTransport } from '../transport/market-status'

export const inputParameters = new InputParameters(marketStatusEndpointInputParametersDefinition)

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

export const endpoint = new MarketStatusEndpoint({
name: 'market-status',
transport: customTransport,
inputParameters,
customInputValidation: (request, settings): undefined => {
const params = request.requestContext.data

switch (params.type) {
case 'regular':
settings.MARKET_REGULAR_SCHEDULE.get(params.market)
return
case '24/5':
settings.MARKET_24_5_SCHEDULE.get(params.market)
return
}
throw new AdapterInputError({
statusCode: 400,
message: `Invalid market type: ${params.type}. Must be one of 'regular' or '24/5'`,
})
},
})
13 changes: 13 additions & 0 deletions packages/sources/static-market-hours/src/index.ts
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 { marketStatus } from './endpoint'

export const adapter = new Adapter({
defaultEndpoint: marketStatus.name,
name: 'STATIC_MARKET_HOURS',
config,
endpoints: [marketStatus],
})

export const server = (): Promise<ServerInstance | undefined> => expose(adapter)
128 changes: 128 additions & 0 deletions packages/sources/static-market-hours/src/transport/market-status.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import {
MarketStatus,
TwentyfourFiveMarketStatus,
} from '@chainlink/external-adapter-framework/adapter'
import { ResponseCache } from '@chainlink/external-adapter-framework/cache/response'
import { Transport, TransportDependencies } from '@chainlink/external-adapter-framework/transports'
import { AdapterRequest, AdapterResponse } from '@chainlink/external-adapter-framework/util'
import { Requester } from '@chainlink/external-adapter-framework/util/requester'
import { config } from '../config'
import { BaseEndpointTypes, inputParameters } from '../endpoint/market-status'
import {
getMarketStatusFromSchedule,
MarketStatusResult,
MarketStatusType,
Schedule,
} from '../util/schedule'

export type CustomTransportTypes = BaseEndpointTypes & {
Provider: {
RequestBody: never
ResponseBody: any
}
}

export class CustomTransport implements Transport<CustomTransportTypes> {
name!: string
responseCache!: ResponseCache<CustomTransportTypes>
requester!: Requester
regularSchedules = new Map<string, Schedule<typeof MarketStatus>>()
twentyfourFiveSchedules = new Map<string, Schedule<typeof TwentyfourFiveMarketStatus>>()

async initialize(
_dependencies: TransportDependencies<CustomTransportTypes>,
_adapterSettings: CustomTransportTypes['Settings'],
_endpointName: string,
transportName: string,
): Promise<void> {
this.name = transportName
}

async foregroundExecute(
request: AdapterRequest<typeof inputParameters.validated>,
settings: typeof config.settings,
): Promise<AdapterResponse<CustomTransportTypes['Response']>> {
const params = request.requestContext.data

const statusResult = this.getStatusResult(
params.market,
settings,
this.getMarketStatusType(params.type),
)

const result = statusResult.result
return {
data: statusResult,
statusCode: 200,
result,
timestamps: {
providerDataRequestedUnixMs: Date.now(),
providerDataReceivedUnixMs: Date.now(),
providerIndicatedTimeUnixMs: undefined,
},
}
}

getMarketStatusType(type: string): MarketStatusType {
switch (type) {
case 'regular':
return MarketStatus
case '24/5':
return TwentyfourFiveMarketStatus
}
throw new Error(`Invalid market status type: ${type}`)
}

getStatusResult<StatusType extends MarketStatusType>(
market: string,
settings: typeof config.settings,
usedMarketStatusType: StatusType,
): MarketStatusResult<StatusType> {
const scheduleData = this.getScheduleData(market, settings, usedMarketStatusType)
return getMarketStatusFromSchedule(Date.now(), scheduleData, usedMarketStatusType)
}

getScheduleData<StatusType extends MarketStatusType>(
market: string,
settings: typeof config.settings,
usedMarketStatusType: StatusType,
): Schedule<StatusType> {
const schedulesMap = this.getSchedulesMap(usedMarketStatusType)

let scheduleData = schedulesMap.get(market)
if (!scheduleData) {
const scheduleString = this.getScheduleSettings(market, usedMarketStatusType, settings)
scheduleData = JSON.parse(scheduleString) as Schedule<StatusType>
schedulesMap.set(market, scheduleData)
}
return scheduleData
}

getSchedulesMap<StatusType extends MarketStatusType>(
usedMarketStatusType: StatusType,
): Map<string, Schedule<StatusType>> {
switch (usedMarketStatusType) {
case MarketStatus:
return this.regularSchedules as Map<string, Schedule<StatusType>>
case TwentyfourFiveMarketStatus:
return this.twentyfourFiveSchedules as Map<string, Schedule<StatusType>>
}
throw new Error(`Invalid market status type: ${JSON.stringify(usedMarketStatusType)}`)
}

getScheduleSettings(
market: string,
usedMarketStatusType: MarketStatusType,
settings: typeof config.settings,
): string {
switch (usedMarketStatusType) {
case MarketStatus:
return settings.MARKET_REGULAR_SCHEDULE.get(market)
case TwentyfourFiveMarketStatus:
return settings.MARKET_24_5_SCHEDULE.get(market)
}
throw new Error(`Invalid market status type: ${JSON.stringify(usedMarketStatusType)}`)
}
}

export const customTransport = new CustomTransport()
Loading
Loading