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
24 changes: 23 additions & 1 deletion .pnp.cjs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,5 +77,10 @@
"resolutions": {
"ethereum-cryptography@^1.1.2": "patch:ethereum-cryptography@npm%3A1.1.2#./.yarn/patches/ethereum-cryptography-npm-1.1.2-c16cfd7e8a.patch",
"ethereum-cryptography@^1.0.3": "patch:ethereum-cryptography@npm%3A1.1.2#./.yarn/patches/ethereum-cryptography-npm-1.1.2-c16cfd7e8a.patch"
},
"dependenciesMeta": {
"@chainlink/external-adapter-framework@2.9.0": {
"unplugged": true
}
}
}
Empty file.
3 changes: 3 additions & 0 deletions packages/sources/kalshi-binary-markets/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Chainlink External Adapter for example-adapter

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 example-adapter`.
43 changes: 43 additions & 0 deletions packages/sources/kalshi-binary-markets/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"name": "@chainlink/kalshi-binary-markets-adapter",
"version": "0.0.0",
"description": "Chainlink External Adapter for fetching Kalshi Binary Market data (prices, status, settlement)",
"keywords": [
"Chainlink",
"LINK",
"blockchain",
"oracle",
"kalshi",
"binary-markets"
],
"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": {
"@sinonjs/fake-timers": "9.1.2",
"@types/jest": "^29.5.14",
"@types/node": "22.14.1",
"@types/sinonjs__fake-timers": "8.1.5",
"nock": "13.5.6",
"typescript": "5.8.3"
},
"dependencies": {
"@chainlink/external-adapter-framework": "2.11.4",
"tslib": "2.4.1"
}
}
15 changes: 15 additions & 0 deletions packages/sources/kalshi-binary-markets/src/config/index.ts
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({
API_KEY: {
description: 'API key for Kalshi',
type: 'string',
required: true,
sensitive: true,
},
API_ENDPOINT: {
description: 'API endpoint for Kalshi',
type: 'string',
default: 'https://api.kalshi.com/v1',
},
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"kalshi-binary-markets": {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { endpoint as market } from './market'
53 changes: 53 additions & 0 deletions packages/sources/kalshi-binary-markets/src/endpoint/market.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { AdapterEndpoint } from '@chainlink/external-adapter-framework/adapter'
import { InputParameters } from '@chainlink/external-adapter-framework/validation'
import { config } from '../config'
import { httpTransport } from '../transport/market'

export const inputParameters = new InputParameters(
{
market_ticker: {
required: true,
type: 'string',
description: 'The market ticker symbol',
},
},
[
{
market_ticker: 'KXUSIRATECUTS25MAR',
},
],
)

export interface MarketResult {
market_ticker: string
event_ticker: string
market_status: number
settlement_flag: number
yes_bid_price: number
yes_ask_price: number
no_bid_price: number
no_ask_price: number
yes_mid_price: number
no_mid_price: number
open_interest: number
category: string
close_timestamp: number
updated_at: number
}

export type BaseEndpointTypes = {
Parameters: typeof inputParameters.definition
Response: {
Result: null
Data: {
result: MarketResult
}
}
Settings: typeof config.settings
}

export const endpoint = new AdapterEndpoint({
name: 'market',
transport: httpTransport,
inputParameters,
})
21 changes: 21 additions & 0 deletions packages/sources/kalshi-binary-markets/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 { market } from './endpoint'

export const adapter = new Adapter({
defaultEndpoint: market.name,
name: 'KALSHI_BINARY_MARKETS',
config,
endpoints: [market],
rateLimiting: {
tiers: {
default: {
rateLimit1s: 10,
note: 'Kalshi API rate limit',
},
},
},
})

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

export interface MarketData {
can_close_early: boolean
category: string
close_time: string
event_ticker: string
expiration_time: string
last_price: number
liquidity: number
market_type: string
no_ask: number
no_bid: number
open_interest: number
status: string
ticker: string
yes_ask: number
yes_bid: number
result: string | null
}

export interface ResponseSchema {
market: MarketData
}

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

export const convertStatusToCode = (status: string): number => {
switch (status) {
case 'active':
return 0
case 'closed':
return 1
case 'settled':
return 2
default:
return 0
}
}

export const convertResultToFlag = (result: string | null): number => {
if (result === null) return 0
if (result === 'yes') return 1
if (result === 'no') return 2
return 0
}

export const parseTimestamp = (isoString: string): number => {
return Math.floor(new Date(isoString).getTime() / 1000)
}

export const calculateMidPrice = (bid: number, ask: number): number => {
return (bid + ask) / 2
}

export const buildMarketResult = (
market: MarketData,
updatedAt: number,
): {
market_ticker: string
event_ticker: string
market_status: number
settlement_flag: number
yes_bid_price: number
yes_ask_price: number
no_bid_price: number
no_ask_price: number
yes_mid_price: number
no_mid_price: number
open_interest: number
category: string
close_timestamp: number
updated_at: number
} => {
return {
market_ticker: market.ticker,
event_ticker: market.event_ticker,
market_status: convertStatusToCode(market.status),
settlement_flag: convertResultToFlag(market.result),
yes_bid_price: market.yes_bid,
yes_ask_price: market.yes_ask,
no_bid_price: market.no_bid,
no_ask_price: market.no_ask,
yes_mid_price: calculateMidPrice(market.yes_bid, market.yes_ask),
no_mid_price: calculateMidPrice(market.no_bid, market.no_ask),
open_interest: market.open_interest,
category: market.category,
close_timestamp: parseTimestamp(market.close_time),
updated_at: updatedAt,
}
}

export const httpTransport = new HttpTransport<HttpTransportTypes>({
prepareRequests: (params, config) => {
return params.map((param) => ({
params: [param],
request: {
baseURL: config.API_ENDPOINT,
url: `/markets/${param.market_ticker}`,
headers: {
accept: 'application/json',
Authorization: `Bearer ${config.API_KEY}`,
},
},
}))
},
parseResponse: (params, response) => {
if (!response.data?.market) {
return params.map((param) => ({
params: param,
response: {
errorMessage: `No market data returned for ${param.market_ticker}`,
statusCode: 502,
},
}))
}

const market = response.data.market
const updatedAt = Math.floor(Date.now() / 1000)

return params.map((param) => {
return {
params: param,
response: {
result: null,
data: {
result: buildMarketResult(market, updatedAt),
},
},
}
})
},
})
7 changes: 7 additions & 0 deletions packages/sources/kalshi-binary-markets/test-payload.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"requests": [
{
"market_ticker": "KXUSIRATECUTS25MAR"
}
]
}
Loading
Loading