Skip to content

Commit 0b01272

Browse files
authored
Update Superstate EA to allow for response selector (#3448)
1 parent f012ff4 commit 0b01272

File tree

6 files changed

+81
-25
lines changed

6 files changed

+81
-25
lines changed

.changeset/metal-knives-move.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@chainlink/superstate-adapter': minor
3+
---
4+
5+
Report assets_under_management

packages/sources/superstate/src/endpoint/nav.ts

+21-6
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,29 @@ import { SingleNumberResultResponse } from '@chainlink/external-adapter-framewor
33
import { config } from '../config'
44
import { NavTransport } from '../transport/nav'
55
import { InputParameters } from '@chainlink/external-adapter-framework/validation'
6+
import { NetAssetValue, AssetsUnderManagement } from '../transport/utils'
67

7-
export const inputParameters = new InputParameters({
8-
fundId: {
9-
description: 'Fund id',
10-
type: 'number',
11-
required: true,
8+
export const inputParameters = new InputParameters(
9+
{
10+
fundId: {
11+
description: 'Fund id',
12+
type: 'number',
13+
required: true,
14+
},
15+
reportValue: {
16+
description: 'Which value to report on',
17+
type: 'string',
18+
default: NetAssetValue,
19+
options: [NetAssetValue, AssetsUnderManagement],
20+
},
1221
},
13-
})
22+
[
23+
{
24+
fundId: 1,
25+
reportValue: NetAssetValue,
26+
},
27+
],
28+
)
1429

1530
export type BaseEndpointTypes = {
1631
Parameters: typeof inputParameters.definition

packages/sources/superstate/src/transport/nav.ts

+25-17
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
isBeforeTime,
1717
isInTimeRange,
1818
toTimezoneDate,
19+
AssetsUnderManagement,
1920
} from './utils'
2021

2122
const logger = makeLogger('Superstate')
@@ -31,6 +32,8 @@ export interface ResponseSchema {
3132

3233
const TZ = 'America/New_York'
3334

35+
type ReportValueType = typeof inputParameters.validated.reportValue
36+
3437
// Custom transport implementation that takes incoming requests, adds them into a SET, and makes requests to DP
3538
// on a specific time every day, after receiving a signal from scheduler.
3639
export class NavTransport implements Transport<BaseEndpointTypes> {
@@ -39,7 +42,7 @@ export class NavTransport implements Transport<BaseEndpointTypes> {
3942
requester!: Requester
4043
settings!: BaseEndpointTypes['Settings']
4144
endpointName!: string
42-
fundIdsSet!: Set<number>
45+
fundsMap!: Map<string, [number, ReportValueType]>
4346

4447
async initialize(
4548
dependencies: TransportDependencies<BaseEndpointTypes>,
@@ -52,26 +55,27 @@ export class NavTransport implements Transport<BaseEndpointTypes> {
5255
this.requester = dependencies.requester
5356
this.settings = settings
5457
this.endpointName = endpointName
55-
this.fundIdsSet = new Set()
58+
this.fundsMap = new Map()
5659
this.runScheduler()
5760
}
5861

5962
// registerRequest is invoked on every valid request to EA
6063
// Adds fundId in the request to a SET
6164
async registerRequest(req: AdapterRequest<typeof inputParameters.validated>) {
62-
const { fundId } = req.requestContext.data
63-
if (!this.fundIdsSet.has(fundId)) {
64-
this.fundIdsSet.add(fundId)
65-
logger.info(`Added new fund id - ${fundId}`)
65+
const { fundId, reportValue } = req.requestContext.data
66+
const mapKey = `${fundId}+${reportValue}`
67+
if (!this.fundsMap.has(mapKey)) {
68+
this.fundsMap.set(mapKey, [fundId, reportValue])
69+
logger.info(`Added new fund id - ${fundId} - reportValue ${reportValue}`)
6670
}
6771
}
6872

6973
// foregroundExecute is executed when there is a new request/fundId that is not in the cache
7074
async foregroundExecute(
7175
req: AdapterRequest<typeof inputParameters.validated>,
7276
): Promise<AdapterResponse<BaseEndpointTypes['Response']> | void> {
73-
const { fundId } = req.requestContext.data
74-
return this.execute(fundId)
77+
const { fundId, reportValue } = req.requestContext.data
78+
return this.execute(fundId, reportValue)
7579
}
7680

7781
// Runs 'execute' function every day at 9:09 AM ET (if fundIdsSet is not empty)
@@ -83,19 +87,17 @@ export class NavTransport implements Transport<BaseEndpointTypes> {
8387

8488
schedule.scheduleJob(rule, () => {
8589
logger.info(
86-
`Scheduled execution started at ${Date.now()}. FundIdSet - ${[...this.fundIdsSet].join(
87-
',',
88-
)}`,
90+
`Scheduled execution started at ${Date.now()}. FundsMap - ${[...this.fundsMap].join(',')}`,
8991
)
90-
;[...this.fundIdsSet].map(async (fundId) => this.execute(fundId))
92+
;[...this.fundsMap].map(async (entry) => this.execute(entry[1][0], entry[1][1]))
9193
})
9294
}
9395

9496
// execute is either called by scheduler or foregroundExecute.
9597
// Makes a request to DP and saves the response in the cache.
9698
// In case the DP returns stale data the function will be executed again several times
9799
// before finalizing and saving the last returned data to a cache.
98-
async execute(fundId: number, retryCount = 0) {
100+
async execute(fundId: number, reportValue: ReportValueType, retryCount = 0) {
99101
const providerDataRequestedUnixMs = Date.now()
100102
const apiResponse = await this.makeRequest(fundId)
101103
const providerDataReceivedUnixMs = Date.now()
@@ -110,12 +112,15 @@ export class NavTransport implements Transport<BaseEndpointTypes> {
110112
providerIndicatedTimeUnixMs: undefined,
111113
},
112114
}
113-
await this.responseCache.write(this.name, [{ params: { fundId }, response }])
115+
await this.responseCache.write(this.name, [{ params: { fundId, reportValue }, response }])
114116
return
115117
}
116118

117119
const data = apiResponse.data[0]
118-
const result = Number(data.net_asset_value)
120+
let result = Number(data.net_asset_value)
121+
if (reportValue == AssetsUnderManagement) {
122+
result = Number(data.assets_under_management)
123+
}
119124

120125
// DP updates previous working day's price on the next working day at 9:09 AM ET
121126
// If there is no fresh price data, we try to re-fetch the API until 10:30 AM ET
@@ -135,7 +140,10 @@ export class NavTransport implements Transport<BaseEndpointTypes> {
135140
} ms`,
136141
)
137142
retryCount++
138-
setTimeout(() => this.execute(fundId, retryCount), this.settings.RETRY_INTERVAL_MS)
143+
setTimeout(
144+
() => this.execute(fundId, reportValue, retryCount),
145+
this.settings.RETRY_INTERVAL_MS,
146+
)
139147
// We don't `return` here and let the value be stored in cache on purpose.
140148
// This way the EA will respond with the latest value from DP (even though it's not the value that the EA expects),
141149
// while it tries to get a fresh update.
@@ -159,7 +167,7 @@ export class NavTransport implements Transport<BaseEndpointTypes> {
159167
providerIndicatedTimeUnixMs: undefined,
160168
},
161169
}
162-
await this.responseCache.write(this.name, [{ params: { fundId }, response }])
170+
await this.responseCache.write(this.name, [{ params: { fundId, reportValue }, response }])
163171
return response
164172
}
165173

packages/sources/superstate/src/transport/utils.ts

+3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { formatInTimeZone } from 'date-fns-tz'
22
import { format, isAfter, isBefore, isSaturday, isSunday, subDays } from 'date-fns'
33

4+
export const NetAssetValue = 'net_asset_value'
5+
export const AssetsUnderManagement = 'assets_under_management'
6+
47
// Converts a Date to a formatted string in the given timezone
58
export const toTimezoneDate = (date: string | Date, timezone: string): string => {
69
return formatInTimeZone(date, timezone, 'yyyy-MM-dd HH:mm:ss')

packages/sources/superstate/test/integration/__snapshots__/adapter.test.ts.snap

+15-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`execute price endpoint should return success 1`] = `
3+
exports[`execute price endpoint should return success - aum 1`] = `
4+
{
5+
"data": {
6+
"result": 88412710.73007037,
7+
},
8+
"result": 88412710.73007037,
9+
"statusCode": 200,
10+
"timestamps": {
11+
"providerDataReceivedUnixMs": 978347471111,
12+
"providerDataRequestedUnixMs": 978347471111,
13+
},
14+
}
15+
`;
16+
17+
exports[`execute price endpoint should return success - nav 1`] = `
418
{
519
"data": {
620
"result": 10.170643,

packages/sources/superstate/test/integration/adapter.test.ts

+12-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ describe('execute', () => {
4747
})
4848

4949
describe('price endpoint', () => {
50-
it('should return success', async () => {
50+
it('should return success - nav', async () => {
5151
const data = {
5252
endpoint: 'reserves',
5353
fundId: 1,
@@ -57,5 +57,16 @@ describe('execute', () => {
5757
expect(response.statusCode).toBe(200)
5858
expect(response.json()).toMatchSnapshot()
5959
})
60+
it('should return success - aum', async () => {
61+
const data = {
62+
endpoint: 'reserves',
63+
fundId: 1,
64+
reportValue: 'assets_under_management',
65+
}
66+
mockResponseSuccess()
67+
const response = await testAdapter.request(data)
68+
expect(response.statusCode).toBe(200)
69+
expect(response.json()).toMatchSnapshot()
70+
})
6071
})
6172
})

0 commit comments

Comments
 (0)