-
Notifications
You must be signed in to change notification settings - Fork 317
Merc6933 GMCI EA - Data Link Feed #3919
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?
Conversation
…js into MERC6933-GMCI-EA-Data_Link_Feed Merge changes from main
🦋 Changeset detectedLatest commit: 81abf28 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
… including field from both messages
.changeset/itchy-pianos-wave.md
Outdated
@@ -0,0 +1,5 @@ | |||
--- | |||
'@chainlink/gmci-adapter': minor |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
major?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FIXED
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just in case you don't know. You can just edit these changeset files. You don't need to run the command again. Because now you have 2 changeset files which seems unnecessary for a single PR.
Can you remove one of them and make sure the other one is correct?
}, | ||
}, | ||
} | ||
transport.price_cache.set(item.symbol, result, 90000) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is async, there is no guarantee that this result is in the cache by the time you call processMessage
.
You should probably just move the entire body here inside processMessage
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
rebalance_status_cache = new LocalCache<CachedRebalanceResponse>(10) | ||
} | ||
|
||
export async function processMessage(symbols: Array<string>, transport: GMCIWebsocketTransport) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you make this a method on GMCIWebsocketTransport
.
It's a bit unfortunate that this function is using transport
as a global variable.
If we move it in as a message, it has a cleaner access to everything inside the transport.
Then we still refer to the global transport
where the method is called, but at least then it's a single isolated reference.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
expect(message.args).toContain('rebalance_status.gmci30') | ||
expect(message.args.length).toBe(2) | ||
}) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also add unit tests for the transport itself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you remove it again?
…ing logic to processMessage
status: 'open', | ||
} | ||
|
||
transport.price_cache.set( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you mock the websocket connection and get this into the cache by sending the corresponding message over the mock websocket?
{ | ||
params: { index: 'GMCI30', type: 'price' }, | ||
response: { | ||
result: 184, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
declare variables for the values (price, symbol, timestamps, etc.) so that it's clear that the values you start with are the values you end up with.
|
||
expect(mockWrite).toHaveBeenCalledTimes(2) | ||
const [callArg] = mockWrite.mock.calls[0] | ||
expect(callArg).toBe(transport.name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you know expect(mockWrite).toHaveBeenNthCalledWith(...)
?
Then you don't need to reach into the inner workings of the mock.
expect(result.response.data.status).toBe('open') | ||
expect(result.response.data.result).toBe(184) | ||
expect(result.response.timestamps.providerIndicatedTimeUnixMs).toBe(1752494400000) | ||
expect(result.response.timestamps.providerDataReceivedUnixMs).toBe(now) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's better to expect the value of the entire argument, rather than expect specific individual fields.
That way you know you're not missing something you didn't think of or which was added later.
expect(result.response.timestamps.providerDataReceivedUnixMs).toBe(now) | ||
}) | ||
|
||
it('skips writing to cache if any data is missing', async () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also add a test where rebalance status is set but price is not.
providerDataStreamEstablishedUnixMs: transport.providerDataStreamEstablished, | ||
providerDataReceivedUnixMs: now, | ||
providerIndicatedTimeUnixMs: | ||
price_data.response.timestamps.providerIndicatedTimeUnixMs, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is conceptually the difference between providerDataReceivedUnixMs
and providerIndicatedTimeUnixMs
?
And why do we use the price_data timestamp as opposed to the rebalance_data timestamp for providerIndicatedTimeUnixMs
?
Would it make sense to use the maximum of both timestamps for providerDataReceivedUnixMs
, rather than creating a new now
timestamp?
transport.rebalance_status_cache.get(symbol), | ||
]) | ||
|
||
if (cached_price_data != undefined && cached_rebalance_data != undefined) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If either of them is undefined
, return early.
Then you avoid the extra indentation in the rest of the method.
|
||
export interface WsResponse { | ||
success: boolean | ||
data: Array<PriceMessage | RebalanceMessage> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We never mix types of message in a single array, right?
If so data: Array<PriceMessage> | Array<RebalanceMessage>
would be more accurate.
Or with the following, the compiler could even deduce the type of array from the value of topic
:
interface WsPriceResponse {
success: boolean
data: Array<PriceMessage>
topic: 'price'
}
interface WsRebalanceResponse {
success: boolean
data: Array<RebalanceMessage>
topic: 'rebalance_status'
}
export type WsResponse = WsPriceResponse | WsPriceResponse
} | ||
|
||
export interface CachedPriceResponse { | ||
params: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see where these params
are used.
response: { | ||
result: number | ||
data: { | ||
result: number |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is just an intermediate data object. We don't need to put the same information multiple times in it, right?
…balance_status message - Wintermute does not support rebalance_status
…tractkit/external-adapters-js into MERC6933-GMCI-EA-Data_Link_Feed Merge remote branch changes
@dskloetc I have a question. |
description: 'WS endpoint for GMCI Data Provider', | ||
type: 'string', | ||
// default: 'wss://api.gmci.co/private', | ||
default: 'wss://generic-alb-ty.api.wintermute-rfq.xyz:15494/private', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this for testing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thats my question. We should remove the defaultaccording to me. Forces us to set endpoint for GMCI and wintermute separately.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there is no obvious single default then it makes sense not to have a default at all.
if (message.topic === 'price') { | ||
for (const item of message.data as PriceMessage[]) { | ||
results.push({ | ||
params: { index: item.symbol }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we call it index
if it's the same as symbol
? Would it make sense to call it symbol
everywhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup. Makes sense.
@@ -121,44 +160,7 @@ export const options: WebSocketTransportConfig<WsTransportTypes> = { | |||
return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see a log.
symbol: item.symbol, | ||
}, | ||
timestamps: { | ||
providerIndicatedTimeUnixMs: convertTimetoUnixMs(item.last_updated), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Weren't there multiple different timestamps before? How do we decide which timestamps to provide?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The others get written here.
Since we had write to the cache ourselves, we have to this bit of work in the previous version.
const results = [] | ||
|
||
if (message.topic === 'price') { | ||
for (const item of message.data as PriceMessage[]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think if we do this, we can avoid a cast here.
expect(message.args).toContain('rebalance_status.gmci30') | ||
expect(message.args.length).toBe(2) | ||
}) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you remove it again?
… Rebalance response
…tractkit/external-adapters-js into MERC6933-GMCI-EA-Data_Link_Feed Merge main branch
packages/sources/gmci/README.md
Outdated
|
||
| Required? | Name | Aliases | Description | Type | Options | Default | Depends On | Not Valid With | | ||
| :-------: | :---: | :-----: | :---------: | :----: | :-----: | :-----: | :--------: | :------------: | | ||
| ✅ | index | | Index name | string | | | | | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you recreate the README now that the name of the parameter is changed?
expect(results?.[0].params.symbol).toBe('GMCI30') | ||
expect(results?.[1].params.symbol).toBe('GML2') | ||
}) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a test that rebalance messages don't cause problems?
Closes #MERC6933
Description
GMCI EA
(https://smartcontract-it.atlassian.net/browse/MERC-6933)
Changes
Steps to Test
yarn test packeages/source/gmci
Quality Assurance
infra-k8s
configuration file.adapter-secrets
configuration file or update the soak testing blacklist.test-payload.json
file with relevant requests.feature/x
,chore/x
,release/x
,hotfix/x
,fix/x
) or is created from Jira.