1- import { TransportDependencies } from '@chainlink/external-adapter-framework/transports'
21import { ResponseCache } from '@chainlink/external-adapter-framework/cache/response'
2+ import { TransportDependencies } from '@chainlink/external-adapter-framework/transports'
33import { 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'
85import {
96 EndpointContext ,
107 LwbaResponseDataFields ,
118} from '@chainlink/external-adapter-framework/adapter'
9+ import { SubscriptionTransport } from '@chainlink/external-adapter-framework/transports/abstract/subscription'
1210import { AdapterResponse , makeLogger , sleep } from '@chainlink/external-adapter-framework/util'
13- import {
14- decimals ,
15- toFixed ,
16- median ,
17- PriceData ,
18- SIGNED_PRICE_DECIMALS ,
19- tokenAddresses ,
20- Source ,
21- } from './utils'
22- import abi from './../config/readerAbi.json'
2311import { AdapterDataProviderError } from '@chainlink/external-adapter-framework/validation/error'
12+ import { ethers , utils } from 'ethers'
13+ import { BaseEndpointTypes , ChainKey , inputParameters } from '../endpoint/price'
14+ import abi from './../config/readerAbi.json'
15+ import { TokenResolver } from './token-resolver'
16+ import { median , PriceData , SIGNED_PRICE_DECIMALS , Source , toFixed , unwrapAsset } from './utils'
2417
2518const logger = makeLogger ( 'GMToken' )
2619
@@ -32,10 +25,11 @@ export class GmTokenTransport extends SubscriptionTransport<GmTokenTransportType
3225 name ! : string
3326 responseCache ! : ResponseCache < GmTokenTransportTypes >
3427 requester ! : Requester
35- provider ! : ethers . providers . JsonRpcProvider
36- readerContract ! : ethers . Contract
3728 abiEncoder ! : utils . AbiCoder
3829 settings ! : GmTokenTransportTypes [ 'Settings' ]
30+ tokenResolver ! : TokenResolver
31+ private providers : Partial < Record < ChainKey , ethers . providers . JsonRpcProvider > > = { }
32+ private readers : Partial < Record < ChainKey , ethers . Contract > > = { }
3933
4034 async initialize (
4135 dependencies : TransportDependencies < GmTokenTransportTypes > ,
@@ -45,16 +39,8 @@ export class GmTokenTransport extends SubscriptionTransport<GmTokenTransportType
4539 ) : Promise < void > {
4640 await super . initialize ( dependencies , adapterSettings , endpointName , transportName )
4741 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- )
5742 this . requester = dependencies . requester
43+ this . tokenResolver = new TokenResolver ( this . requester , this . settings )
5844 }
5945
6046 async backgroundHandler ( context : EndpointContext < BaseEndpointTypes > , entries : RequestParams [ ] ) {
@@ -85,35 +71,40 @@ export class GmTokenTransport extends SubscriptionTransport<GmTokenTransportType
8571 async _handleRequest (
8672 param : RequestParams ,
8773 ) : Promise < AdapterResponse < GmTokenTransportTypes [ 'Response' ] > > {
88- const { index, long, short, market } = param
74+ const { index, long, short, market, chain } = param
8975
9076 const assets = [ index , long , short ]
9177
9278 const providerDataRequestedUnixMs = Date . now ( )
9379
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+
9489 const {
9590 prices : [ indexPrices , longPrices , shortPrices ] ,
9691 sources,
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 ]
92+ } = await this . fetchPrices ( assets , providerDataRequestedUnixMs , decimalsMap )
10293
10394 const tokenPriceContractParams = [
104- this . settings . DATASTORE_CONTRACT_ADDRESS ,
105- [ market , indexToken , longToken , shortToken ] ,
95+ this . getDatastoreContractAddress ( chain as ChainKey ) ,
96+ [ market , indexToken . address , longToken . address , shortToken . address ] ,
10697 [ indexPrices . ask , indexPrices . bid ] ,
10798 [ longPrices . ask , longPrices . bid ] ,
10899 [ shortPrices . ask , shortPrices . bid ] ,
109100 utils . keccak256 ( utils . defaultAbiCoder . encode ( [ 'string' ] , [ this . settings . PNL_FACTOR_TYPE ] ) ) ,
110101 ]
111-
102+ const readerContract = this . getReaderContract ( chain )
112103 // Prices have a spread from min to max. The last param (maximize-true/false) decides whether to maximize the market token price
113104 // or not. We get both values and return the median.
114105 const [ [ maximizedValue ] , [ minimizedValue ] ] = await Promise . all ( [
115- this . readerContract . getMarketTokenPrice ( ...tokenPriceContractParams , true ) ,
116- this . readerContract . getMarketTokenPrice ( ...tokenPriceContractParams , false ) ,
106+ readerContract . getMarketTokenPrice ( ...tokenPriceContractParams , true ) ,
107+ readerContract . getMarketTokenPrice ( ...tokenPriceContractParams , false ) ,
117108 ] )
118109
119110 const maximizedPrice = Number ( utils . formatUnits ( maximizedValue , SIGNED_PRICE_DECIMALS ) )
@@ -136,7 +127,11 @@ export class GmTokenTransport extends SubscriptionTransport<GmTokenTransportType
136127 }
137128
138129 // Fetches the lwba price info from multiple source EAs, calculates the median for bids and asks per asset and fixes the price precision
139- private async fetchPrices ( assets : string [ ] , dataRequestedTimestamp : number ) {
130+ private async fetchPrices (
131+ assets : string [ ] ,
132+ dataRequestedTimestamp : number ,
133+ decimals : Map < string , number > ,
134+ ) {
140135 // priceData holds raw bid/ask values per asset from source EAs response
141136 const priceData = { } as PriceData
142137
@@ -155,7 +150,7 @@ export class GmTokenTransport extends SubscriptionTransport<GmTokenTransportType
155150 const source = sources [ i ]
156151
157152 const assetPromises = assets . map ( async ( asset ) => {
158- const base = this . unwrapAsset ( asset )
153+ const base = unwrapAsset ( asset )
159154 const requestConfig = {
160155 url : source . url ,
161156 method : 'POST' ,
@@ -202,11 +197,17 @@ export class GmTokenTransport extends SubscriptionTransport<GmTokenTransportType
202197
203198 const medianValues = this . calculateMedian ( assets , priceData )
204199
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- } ) )
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+ } )
210211
211212 return {
212213 prices,
@@ -223,16 +224,6 @@ export class GmTokenTransport extends SubscriptionTransport<GmTokenTransportType
223224 } )
224225 }
225226
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-
236227 /*
237228 For every asset check that we received responses from the required number of source EAs to accurately calculate the median price of the asset.
238229 */
@@ -258,7 +249,7 @@ export class GmTokenTransport extends SubscriptionTransport<GmTokenTransportType
258249 }
259250
260251 assets . forEach ( ( asset ) => {
261- const base = this . unwrapAsset ( asset )
252+ const base = unwrapAsset ( asset )
262253 const respondedSources = priceProviders [ base ]
263254
264255 if ( respondedSources . length < this . settings . MIN_REQUIRED_SOURCE_SUCCESS ) {
@@ -282,6 +273,39 @@ export class GmTokenTransport extends SubscriptionTransport<GmTokenTransportType
282273 } )
283274 }
284275
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+
285309 getSubscriptionTtlFromConfig ( adapterSettings : BaseEndpointTypes [ 'Settings' ] ) : number {
286310 return adapterSettings . WARMUP_SUBSCRIPTION_TTL
287311 }
0 commit comments