-
Notifications
You must be signed in to change notification settings - Fork 309
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feat/DF-20368 combine tp and icap (#3449)
Co-authored-by: Michael Xiao <[email protected]>
- Loading branch information
1 parent
d6988cc
commit 2e7c23b
Showing
25 changed files
with
811 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@chainlink/icap-adapter': minor | ||
--- | ||
|
||
Seperate ICAP from TP |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@chainlink/tp-adapter': minor | ||
--- | ||
|
||
Combined TP and ICAP EAs into a single EA and removed ICAP.URL must have query param appended as selector in bridge URL, eg: https://<tp-ea>:8080?streamName=icapThis change will save subscription costs as all data for both DPs is sent on 1 WS connection and each additional connection requires additional subscriptions (and cost).Should be backwards compatible for TP ONLY |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Binary file added
BIN
+4.68 MB
.yarn/cache/@nomicfoundation-edr-darwin-arm64-npm-0.3.5-1d9a58a391-10.zip
Binary file not shown.
Binary file removed
BIN
-6.9 MB
.yarn/cache/@nomicfoundation-edr-linux-x64-gnu-npm-0.3.5-9de0955a33-10.zip
Binary file not shown.
Binary file added
BIN
+163 KB
.yarn/cache/@nomicfoundation-solidity-analyzer-darwin-arm64-npm-0.1.1-269bd960f5-10.zip
Binary file not shown.
Binary file removed
BIN
-178 KB
.yarn/cache/@nomicfoundation-solidity-analyzer-linux-x64-gnu-npm-0.1.1-d68b54567f-10.zip
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,210 @@ | ||
import Decimal from 'decimal.js' | ||
import { WebSocketTransport } from '@chainlink/external-adapter-framework/transports/websocket' | ||
import { makeLogger, ProviderResult } from '@chainlink/external-adapter-framework/util' | ||
import { BaseEndpointTypes, GeneratePriceOptions } from '@chainlink/tp-adapter' | ||
|
||
const logger = makeLogger('TpIcapPrice') | ||
|
||
type WsMessage = { | ||
msg: 'auth' | 'sub' | ||
pro?: string | ||
rec: string // example: FXSPTEURUSDSPT:GBL.BIL.QTE.RTM!IC | ||
sta: number | ||
img?: number | ||
fvs?: { | ||
CCY1?: string // example: "EUR" | ||
CCY2?: string // example: "USD" | ||
ACTIV_DATE?: string // example: "2023-03-06" | ||
TIMACT?: string // example: "15:00:00" | ||
BID?: number | ||
ASK?: number | ||
MID_PRICE?: number | ||
} | ||
} | ||
|
||
export type WsTransportTypes = BaseEndpointTypes & { | ||
Provider: { | ||
WsMessage: WsMessage | ||
} | ||
} | ||
|
||
const isNum = (i: number | undefined) => typeof i === 'number' | ||
|
||
let providerDataStreamEstablishedUnixMs: number | ||
|
||
/* | ||
TP and ICAP EAs currently do not receive asset prices during off-market hours. When a heartbeat message is received during these hours, | ||
we update the TTL of cache entries that EA is requested to provide a price during off-market hours. | ||
*/ | ||
const updateTTL = async (transport: WebSocketTransport<WsTransportTypes>, ttl: number) => { | ||
const params = await transport.subscriptionSet.getAll() | ||
transport.responseCache.writeTTL(transport.name, params, ttl) | ||
} | ||
|
||
export const generateTransport = (generatePriceOptions: GeneratePriceOptions) => { | ||
const tpTransport = new WebSocketTransport<WsTransportTypes>({ | ||
url: ({ adapterSettings: { WS_API_ENDPOINT } }) => WS_API_ENDPOINT, | ||
handlers: { | ||
open: (connection, { adapterSettings: { WS_API_USERNAME, WS_API_PASSWORD } }) => { | ||
logger.debug('Opening WS connection') | ||
|
||
return new Promise((resolve) => { | ||
connection.addEventListener('message', (event: MessageEvent) => { | ||
const { msg, sta } = JSON.parse(event.data.toString()) | ||
if (msg === 'auth' && sta === 1) { | ||
logger.info('Got logged in response, connection is ready') | ||
providerDataStreamEstablishedUnixMs = Date.now() | ||
resolve() | ||
} | ||
}) | ||
const options = { | ||
msg: 'auth', | ||
user: WS_API_USERNAME, | ||
pass: WS_API_PASSWORD, | ||
mode: 'broadcast', | ||
} | ||
connection.send(JSON.stringify(options)) | ||
}) | ||
}, | ||
message: (message, context) => { | ||
logger.debug({ msg: 'Received message from WS', message }) | ||
|
||
const providerDataReceivedUnixMs = Date.now() | ||
|
||
if (!('msg' in message) || message.msg === 'auth') return [] | ||
|
||
const { fvs, rec, sta } = message | ||
|
||
if (!fvs || !rec || sta !== 1) { | ||
logger.debug({ msg: 'Missing expected field `fvs` or `rec` from `sub` message', message }) | ||
return [] | ||
} | ||
|
||
// Check for a heartbeat message, refresh the TTLs of all requested entries in the cache | ||
if (rec.includes('HBHHH')) { | ||
logger.debug({ | ||
msg: 'Received heartbeat message from WS, updating TTLs of active entries', | ||
message, | ||
}) | ||
updateTTL(tpTransport, context.adapterSettings.CACHE_MAX_AGE) | ||
return [] | ||
} | ||
|
||
const ticker = parseRec(rec) | ||
if (!ticker) { | ||
logger.debug({ msg: `Invalid symbol: ${rec}`, message }) | ||
return [] | ||
} | ||
|
||
if (ticker.stream !== generatePriceOptions.streamName) { | ||
logger.debug({ | ||
msg: `Only ${generatePriceOptions.streamName} forex prices accepted on this adapter. Filtering out this message.`, | ||
message, | ||
}) | ||
return [] | ||
} | ||
|
||
const { ASK, BID, MID_PRICE } = fvs | ||
|
||
if (!isNum(MID_PRICE) && !(isNum(BID) && isNum(ASK))) { | ||
const errorMessage = '`sub` message did not include required price fields' | ||
logger.debug({ errorMessage, message }) | ||
return [] | ||
} | ||
|
||
const result = | ||
MID_PRICE || | ||
new Decimal(ASK as number) | ||
.add(BID as number) | ||
.div(2) | ||
.toNumber() | ||
|
||
const response = { | ||
result, | ||
data: { | ||
result, | ||
}, | ||
timestamps: { | ||
providerDataReceivedUnixMs, | ||
providerDataStreamEstablishedUnixMs, | ||
providerIndicatedTimeUnixMs: undefined, | ||
}, | ||
} | ||
|
||
// Cache both the base and the full ticker string. The full ticker is to | ||
// accomodate cases where there are multiple instruments for a single base | ||
// (e.g. forwards like CEFWDXAUUSDSPT06M:LDN.BIL.QTE.RTM!TP, CEFWDXAUUSDSPT02Y:LDN.BIL.QTE.RTM!TP, CEFWDXAUUSDSPT03M:LDN.BIL.QTE.RTM!TP, etc). | ||
// It is expected that for such cases, the exact ticker will be provided as | ||
// an override. | ||
// e.g. request body = {"data":{"endpoint":"forex","from":"CHF","to":"USD","overrides":{"tp":{"CHF":"FXSPTUSDAEDSPT:GBL.BIL.QTE.RTM!TP"}}}} | ||
return [ | ||
{ | ||
params: { | ||
base: ticker.base, | ||
quote: ticker.quote, | ||
[generatePriceOptions.sourceName]: ticker.source, | ||
}, | ||
response, | ||
}, | ||
{ | ||
params: { | ||
base: rec, | ||
quote: ticker.quote, | ||
[generatePriceOptions.sourceName]: ticker.source, | ||
}, | ||
response, | ||
}, | ||
] as unknown as ProviderResult<WsTransportTypes>[] | ||
}, | ||
}, | ||
}) | ||
return tpTransport | ||
} | ||
|
||
// mapping OTRWTS to WTIUSD specifically for caching with quote = USD | ||
const marketBaseQuoteOverrides: Record<string, string> = { | ||
CEOILOTRWTS: 'CEOILWTIUSD', | ||
} | ||
|
||
type Ticker = { | ||
market: string | ||
base: string | ||
quote: string | ||
source: string | ||
stream: string | ||
} | ||
|
||
/* | ||
For example, if rec = 'FXSPTCHFSEKSPT:GBL.BIL.QTE.RTM!IC', then the parsed output is | ||
{ | ||
market: 'FXSPT', | ||
base: 'CHF', | ||
quote: 'SEK', | ||
source: 'GBL', | ||
stream: 'IC' | ||
} | ||
*/ | ||
export const parseRec = (rec: string): Ticker | null => { | ||
const [symbol, rec1] = rec.split(':') | ||
if (!rec1) { | ||
return null | ||
} | ||
|
||
const [sources, stream] = rec1.split('!') | ||
if (!stream) { | ||
return null | ||
} | ||
|
||
let marketBaseQuote = symbol.slice(0, 11) | ||
if (marketBaseQuote in marketBaseQuoteOverrides) { | ||
marketBaseQuote = marketBaseQuoteOverrides[marketBaseQuote] | ||
} | ||
|
||
return { | ||
market: marketBaseQuote.slice(0, 5), | ||
base: marketBaseQuote.slice(5, 8), | ||
quote: marketBaseQuote.slice(8, 11), | ||
source: sources.split('.')[0], | ||
stream, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,6 @@ | ||
export { generateInputParams, priceEndpoint, GeneratePriceOptions } from './price' | ||
export { | ||
generateInputParams, | ||
priceEndpoint, | ||
GeneratePriceOptions, | ||
BaseEndpointTypes, | ||
} from './price' |
Oops, something went wrong.