Skip to content

Commit 7de2ee6

Browse files
Revert "Refactors GM-token to support Botanix and dynamic fetching of tokens (#4077)" (#4089)
This reverts commit 11c143f. Co-authored-by: app-token-issuer-data-feeds[bot] <134377064+app-token-issuer-data-feeds[bot]@users.noreply.github.com>
1 parent 95fc9e3 commit 7de2ee6

File tree

10 files changed

+123
-462
lines changed

10 files changed

+123
-462
lines changed

.changeset/popular-otters-brush.md

Lines changed: 0 additions & 5 deletions
This file was deleted.

packages/composites/gm-token/src/config/index.ts

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -14,57 +14,18 @@ export const config = new AdapterConfig(
1414
required: true,
1515
default: 42161,
1616
},
17-
BOTANIX_RPC_URL: {
18-
description: 'RPC url of Botanix node',
19-
type: 'string',
20-
},
21-
BOTANIX_CHAIN_ID: {
22-
description: 'The chain id to connect to',
23-
type: 'number',
24-
default: 3637,
25-
},
2617
DATASTORE_CONTRACT_ADDRESS: {
2718
description: 'Address of Data Store contract',
2819
type: 'string',
2920
required: true,
3021
default: '0xFD70de6b91282D8017aA4E741e9Ae325CAb992d8',
3122
},
32-
BOTANIX_DATASTORE_CONTRACT_ADDRESS: {
33-
description: 'Address of Data Store contract',
34-
type: 'string',
35-
required: true,
36-
default: '0xA23B81a89Ab9D7D89fF8fc1b5d8508fB75Cc094d',
37-
},
3823
READER_CONTRACT_ADDRESS: {
3924
description: 'Address of Reader contract',
4025
type: 'string',
4126
required: true,
4227
default: '0xf60becbba223EEA9495Da3f606753867eC10d139',
4328
},
44-
BOTANIX_READER_CONTRACT_ADDRESS: {
45-
description: 'Address of Reader contract',
46-
type: 'string',
47-
required: true,
48-
default: '0xa254B60cbB85a92F6151B10E1233639F601f2F0F',
49-
},
50-
ARBITRUM_TOKENS_INFO_URL: {
51-
description: 'URL to token meta data supported by GMX on Arbitrum',
52-
type: 'string',
53-
required: true,
54-
default: 'https://arbitrum-api.gmxinfra.io/tokens',
55-
},
56-
BOTANIX_TOKENS_INFO_URL: {
57-
description: 'URL to token meta data supported by GMX on Botanix',
58-
type: 'string',
59-
required: true,
60-
default: 'https://botanix-api.gmxinfra.io/tokens',
61-
},
62-
GMX_TOKENS_CACHE_MS: {
63-
description: 'TTL in milliseconds for GMX tokens cache',
64-
type: 'number',
65-
required: true,
66-
default: 300_000,
67-
},
6829
PNL_FACTOR_TYPE: {
6930
description:
7031
'PnL factor type. See https://github.com/gmx-io/gmx-synthetics#market-token-price',

packages/composites/gm-token/src/endpoint/price.ts

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import { AdapterEndpoint } from '@chainlink/external-adapter-framework/adapter'
22
import { InputParameters } from '@chainlink/external-adapter-framework/validation'
33
import { config } from '../config'
44
import { gmTokenTransport } from '../transport/price'
5-
6-
export const CHAIN_OPTIONS = ['arbitrum', 'botanix']
5+
import { tokenAddresses } from '../transport/utils'
6+
import { AdapterInputError } from '@chainlink/external-adapter-framework/validation/error'
77

88
export const inputParameters = new InputParameters(
99
{
@@ -28,26 +28,17 @@ export const inputParameters = new InputParameters(
2828
type: 'string',
2929
description: 'Market address of the market pool.',
3030
},
31-
chain: {
32-
description: 'Target chain for GM market',
33-
type: 'string',
34-
options: [...CHAIN_OPTIONS],
35-
default: 'arbitrum',
36-
},
3731
},
3832
[
3933
{
4034
index: 'LINK',
4135
long: 'LINK',
4236
short: 'USDC',
4337
market: '0x7f1fa204bb700853D36994DA19F830b6Ad18455C',
44-
chain: 'arbitrum',
4538
},
4639
],
4740
)
4841

49-
export type ChainKey = (typeof inputParameters.validated)['chain']
50-
5142
export type BaseEndpointTypes = {
5243
Parameters: typeof inputParameters.definition
5344
Response: {
@@ -64,4 +55,26 @@ export const endpoint = new AdapterEndpoint({
6455
name: 'price',
6556
transport: gmTokenTransport,
6657
inputParameters,
58+
customInputValidation: (req): AdapterInputError | undefined => {
59+
const { index, long, short } = req.requestContext.data
60+
const indexToken = tokenAddresses.arbitrum[index as keyof typeof tokenAddresses.arbitrum]
61+
const longToken = tokenAddresses.arbitrum[long as keyof typeof tokenAddresses.arbitrum]
62+
const shortToken = tokenAddresses.arbitrum[short as keyof typeof tokenAddresses.arbitrum]
63+
let invalidTokens = ''
64+
if (!indexToken) {
65+
invalidTokens += 'indexToken,'
66+
}
67+
if (!longToken) {
68+
invalidTokens += 'longToken,'
69+
}
70+
if (!shortToken) {
71+
invalidTokens += 'shortToken,'
72+
}
73+
if (invalidTokens.length) {
74+
throw new AdapterInputError({
75+
message: `Invalid ${invalidTokens} Must be one of ${Object.keys(tokenAddresses.arbitrum)}`,
76+
})
77+
}
78+
return
79+
},
6780
})

packages/composites/gm-token/src/transport/price.ts

Lines changed: 54 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,26 @@
1-
import { ResponseCache } from '@chainlink/external-adapter-framework/cache/response'
21
import { TransportDependencies } from '@chainlink/external-adapter-framework/transports'
2+
import { ResponseCache } from '@chainlink/external-adapter-framework/cache/response'
33
import { Requester } from '@chainlink/external-adapter-framework/util/requester'
44

5+
import { BaseEndpointTypes, inputParameters } from '../endpoint/price'
6+
import { ethers, utils } from 'ethers'
7+
import { SubscriptionTransport } from '@chainlink/external-adapter-framework/transports/abstract/subscription'
58
import {
69
EndpointContext,
710
LwbaResponseDataFields,
811
} from '@chainlink/external-adapter-framework/adapter'
9-
import { SubscriptionTransport } from '@chainlink/external-adapter-framework/transports/abstract/subscription'
1012
import { AdapterResponse, makeLogger, sleep } from '@chainlink/external-adapter-framework/util'
11-
import { AdapterDataProviderError } from '@chainlink/external-adapter-framework/validation/error'
12-
import { ethers, utils } from 'ethers'
13-
import { BaseEndpointTypes, ChainKey, inputParameters } from '../endpoint/price'
13+
import {
14+
decimals,
15+
toFixed,
16+
median,
17+
PriceData,
18+
SIGNED_PRICE_DECIMALS,
19+
tokenAddresses,
20+
Source,
21+
} from './utils'
1422
import abi from './../config/readerAbi.json'
15-
import { TokenResolver } from './token-resolver'
16-
import { median, PriceData, SIGNED_PRICE_DECIMALS, Source, toFixed, unwrapAsset } from './utils'
23+
import { AdapterDataProviderError } from '@chainlink/external-adapter-framework/validation/error'
1724

1825
const logger = makeLogger('GMToken')
1926

@@ -25,11 +32,10 @@ export class GmTokenTransport extends SubscriptionTransport<GmTokenTransportType
2532
name!: string
2633
responseCache!: ResponseCache<GmTokenTransportTypes>
2734
requester!: Requester
35+
provider!: ethers.providers.JsonRpcProvider
36+
readerContract!: ethers.Contract
2837
abiEncoder!: utils.AbiCoder
2938
settings!: GmTokenTransportTypes['Settings']
30-
tokenResolver!: TokenResolver
31-
private providers: Partial<Record<ChainKey, ethers.providers.JsonRpcProvider>> = {}
32-
private readers: Partial<Record<ChainKey, ethers.Contract>> = {}
3339

3440
async initialize(
3541
dependencies: TransportDependencies<GmTokenTransportTypes>,
@@ -39,8 +45,16 @@ export class GmTokenTransport extends SubscriptionTransport<GmTokenTransportType
3945
): Promise<void> {
4046
await super.initialize(dependencies, adapterSettings, endpointName, transportName)
4147
this.settings = adapterSettings
48+
this.provider = new ethers.providers.JsonRpcProvider(
49+
adapterSettings.ARBITRUM_RPC_URL,
50+
adapterSettings.ARBITRUM_CHAIN_ID,
51+
)
52+
this.readerContract = new ethers.Contract(
53+
adapterSettings.READER_CONTRACT_ADDRESS,
54+
abi,
55+
this.provider,
56+
)
4257
this.requester = dependencies.requester
43-
this.tokenResolver = new TokenResolver(this.requester, this.settings)
4458
}
4559

4660
async backgroundHandler(context: EndpointContext<BaseEndpointTypes>, entries: RequestParams[]) {
@@ -71,40 +85,35 @@ export class GmTokenTransport extends SubscriptionTransport<GmTokenTransportType
7185
async _handleRequest(
7286
param: RequestParams,
7387
): Promise<AdapterResponse<GmTokenTransportTypes['Response']>> {
74-
const { index, long, short, market, chain } = param
88+
const { index, long, short, market } = param
7589

7690
const assets = [index, long, short]
7791

7892
const providerDataRequestedUnixMs = Date.now()
7993

80-
const [indexToken, longToken, shortToken] = await Promise.all([
81-
this.tokenResolver.getToken(chain, index),
82-
this.tokenResolver.getToken(chain, long),
83-
this.tokenResolver.getToken(chain, short),
84-
])
85-
const decimalsMap = new Map(
86-
[indexToken, longToken, shortToken].map((t) => [t.symbol, t.decimals]),
87-
)
88-
8994
const {
9095
prices: [indexPrices, longPrices, shortPrices],
9196
sources,
92-
} = await this.fetchPrices(assets, providerDataRequestedUnixMs, decimalsMap)
97+
} = await this.fetchPrices(assets, providerDataRequestedUnixMs)
98+
99+
const indexToken = tokenAddresses.arbitrum[index as keyof typeof tokenAddresses.arbitrum]
100+
const longToken = tokenAddresses.arbitrum[long as keyof typeof tokenAddresses.arbitrum]
101+
const shortToken = tokenAddresses.arbitrum[short as keyof typeof tokenAddresses.arbitrum]
93102

94103
const tokenPriceContractParams = [
95-
this.getDatastoreContractAddress(chain as ChainKey),
96-
[market, indexToken.address, longToken.address, shortToken.address],
104+
this.settings.DATASTORE_CONTRACT_ADDRESS,
105+
[market, indexToken, longToken, shortToken],
97106
[indexPrices.ask, indexPrices.bid],
98107
[longPrices.ask, longPrices.bid],
99108
[shortPrices.ask, shortPrices.bid],
100109
utils.keccak256(utils.defaultAbiCoder.encode(['string'], [this.settings.PNL_FACTOR_TYPE])),
101110
]
102-
const readerContract = this.getReaderContract(chain)
111+
103112
// Prices have a spread from min to max. The last param (maximize-true/false) decides whether to maximize the market token price
104113
// or not. We get both values and return the median.
105114
const [[maximizedValue], [minimizedValue]] = await Promise.all([
106-
readerContract.getMarketTokenPrice(...tokenPriceContractParams, true),
107-
readerContract.getMarketTokenPrice(...tokenPriceContractParams, false),
115+
this.readerContract.getMarketTokenPrice(...tokenPriceContractParams, true),
116+
this.readerContract.getMarketTokenPrice(...tokenPriceContractParams, false),
108117
])
109118

110119
const maximizedPrice = Number(utils.formatUnits(maximizedValue, SIGNED_PRICE_DECIMALS))
@@ -127,11 +136,7 @@ export class GmTokenTransport extends SubscriptionTransport<GmTokenTransportType
127136
}
128137

129138
// Fetches the lwba price info from multiple source EAs, calculates the median for bids and asks per asset and fixes the price precision
130-
private async fetchPrices(
131-
assets: string[],
132-
dataRequestedTimestamp: number,
133-
decimals: Map<string, number>,
134-
) {
139+
private async fetchPrices(assets: string[], dataRequestedTimestamp: number) {
135140
// priceData holds raw bid/ask values per asset from source EAs response
136141
const priceData = {} as PriceData
137142

@@ -150,7 +155,7 @@ export class GmTokenTransport extends SubscriptionTransport<GmTokenTransportType
150155
const source = sources[i]
151156

152157
const assetPromises = assets.map(async (asset) => {
153-
const base = unwrapAsset(asset)
158+
const base = this.unwrapAsset(asset)
154159
const requestConfig = {
155160
url: source.url,
156161
method: 'POST',
@@ -197,17 +202,11 @@ export class GmTokenTransport extends SubscriptionTransport<GmTokenTransportType
197202

198203
const medianValues = this.calculateMedian(assets, priceData)
199204

200-
const prices = medianValues.map((v) => {
201-
const decimal = decimals.get(v.asset)
202-
if (!decimal) {
203-
throw new Error(`Missing token decimals for '${v.asset}'`)
204-
}
205-
return {
206-
...v,
207-
ask: toFixed(v.ask, decimal),
208-
bid: toFixed(v.bid, decimal),
209-
}
210-
})
205+
const prices = medianValues.map((v) => ({
206+
...v,
207+
ask: toFixed(v.ask, decimals[v.asset as keyof typeof decimals]),
208+
bid: toFixed(v.bid, decimals[v.asset as keyof typeof decimals]),
209+
}))
211210

212211
return {
213212
prices,
@@ -224,6 +223,16 @@ export class GmTokenTransport extends SubscriptionTransport<GmTokenTransportType
224223
})
225224
}
226225

226+
private unwrapAsset(asset: string) {
227+
if (asset === 'WBTC.b') {
228+
return 'BTC'
229+
}
230+
if (asset === 'WETH') {
231+
return 'ETH'
232+
}
233+
return asset
234+
}
235+
227236
/*
228237
For every asset check that we received responses from the required number of source EAs to accurately calculate the median price of the asset.
229238
*/
@@ -249,7 +258,7 @@ export class GmTokenTransport extends SubscriptionTransport<GmTokenTransportType
249258
}
250259

251260
assets.forEach((asset) => {
252-
const base = unwrapAsset(asset)
261+
const base = this.unwrapAsset(asset)
253262
const respondedSources = priceProviders[base]
254263

255264
if (respondedSources.length < this.settings.MIN_REQUIRED_SOURCE_SUCCESS) {
@@ -273,39 +282,6 @@ export class GmTokenTransport extends SubscriptionTransport<GmTokenTransportType
273282
})
274283
}
275284

276-
private getProvider(chain: ChainKey): ethers.providers.JsonRpcProvider {
277-
if (this.providers[chain]) return this.providers[chain]!
278-
const p =
279-
chain === 'botanix'
280-
? new ethers.providers.JsonRpcProvider(
281-
this.settings.BOTANIX_RPC_URL,
282-
this.settings.BOTANIX_CHAIN_ID,
283-
)
284-
: new ethers.providers.JsonRpcProvider(
285-
this.settings.ARBITRUM_RPC_URL,
286-
this.settings.ARBITRUM_CHAIN_ID,
287-
)
288-
this.providers[chain] = p
289-
return p
290-
}
291-
292-
private getReaderContract(chain: ChainKey): ethers.Contract {
293-
if (this.readers[chain]) return this.readers[chain]!
294-
const addr =
295-
chain === 'botanix'
296-
? this.settings.BOTANIX_READER_CONTRACT_ADDRESS
297-
: this.settings.READER_CONTRACT_ADDRESS
298-
const reader = new ethers.Contract(addr, abi, this.getProvider(chain))
299-
this.readers[chain] = reader
300-
return reader
301-
}
302-
303-
private getDatastoreContractAddress(chain: ChainKey): string {
304-
return chain === 'botanix'
305-
? this.settings.BOTANIX_DATASTORE_CONTRACT_ADDRESS
306-
: this.settings.DATASTORE_CONTRACT_ADDRESS
307-
}
308-
309285
getSubscriptionTtlFromConfig(adapterSettings: BaseEndpointTypes['Settings']): number {
310286
return adapterSettings.WARMUP_SUBSCRIPTION_TTL
311287
}

packages/composites/gm-token/src/transport/token-resolver.ts

Lines changed: 0 additions & 31 deletions
This file was deleted.

0 commit comments

Comments
 (0)