@dolomite-exchange/zap-sdk 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/.circleci/config.yml +85 -0
  2. package/.eslintrc.js +67 -0
  3. package/LICENSE +176 -0
  4. package/README.md +22 -0
  5. package/__tests__/DolomiteZap.test.ts +552 -0
  6. package/__tests__/clients/DolomiteClient.test.ts +47 -0
  7. package/__tests__/clients/ParaswapAggregator.test.ts +34 -0
  8. package/__tests__/helpers/Setup.ts +4 -0
  9. package/__tests__/helpers/TestConstants.ts +119 -0
  10. package/__tests__/lib/LocalCache.test.ts +40 -0
  11. package/build/__tests__/DolomiteZap.test.d.ts +1 -0
  12. package/build/__tests__/DolomiteZap.test.js +526 -0
  13. package/build/__tests__/clients/DolomiteClient.test.d.ts +1 -0
  14. package/build/__tests__/clients/DolomiteClient.test.js +100 -0
  15. package/build/__tests__/clients/ParaswapAggregator.test.d.ts +1 -0
  16. package/build/__tests__/clients/ParaswapAggregator.test.js +76 -0
  17. package/build/__tests__/helpers/Setup.d.ts +0 -0
  18. package/build/__tests__/helpers/Setup.js +6 -0
  19. package/build/__tests__/helpers/TestConstants.d.ts +9 -0
  20. package/build/__tests__/helpers/TestConstants.js +109 -0
  21. package/build/__tests__/lib/LocalCache.test.d.ts +1 -0
  22. package/build/__tests__/lib/LocalCache.test.js +78 -0
  23. package/build/src/DolomiteZap.d.ts +26 -0
  24. package/build/src/DolomiteZap.js +259 -0
  25. package/build/src/abis/IDolomiteMarginExchangeWrapper.json +80 -0
  26. package/build/src/clients/AggregatorClient.d.ts +7 -0
  27. package/build/src/clients/AggregatorClient.js +10 -0
  28. package/build/src/clients/DolomiteClient.d.ts +11 -0
  29. package/build/src/clients/DolomiteClient.js +225 -0
  30. package/build/src/clients/IsolationModeClient.d.ts +16 -0
  31. package/build/src/clients/IsolationModeClient.js +47 -0
  32. package/build/src/clients/ParaswapAggregator.d.ts +7 -0
  33. package/build/src/clients/ParaswapAggregator.js +150 -0
  34. package/build/src/index.d.ts +5 -0
  35. package/build/src/index.js +51 -0
  36. package/build/src/lib/ApiTypes.d.ts +113 -0
  37. package/build/src/lib/ApiTypes.js +16 -0
  38. package/build/src/lib/Constants.d.ts +17 -0
  39. package/build/src/lib/Constants.js +118 -0
  40. package/build/src/lib/Environment.d.ts +0 -0
  41. package/build/src/lib/Environment.js +3 -0
  42. package/build/src/lib/GraphqlPageable.d.ts +4 -0
  43. package/build/src/lib/GraphqlPageable.js +73 -0
  44. package/build/src/lib/LocalCache.d.ts +9 -0
  45. package/build/src/lib/LocalCache.js +29 -0
  46. package/build/src/lib/Logger.d.ts +3 -0
  47. package/build/src/lib/Logger.js +78 -0
  48. package/build/src/lib/MathUtils.d.ts +4 -0
  49. package/build/src/lib/MathUtils.js +16 -0
  50. package/build/src/lib/Utils.d.ts +5 -0
  51. package/build/src/lib/Utils.js +84 -0
  52. package/build/src/lib/estimators/StandardEstimator.d.ts +9 -0
  53. package/build/src/lib/estimators/StandardEstimator.js +89 -0
  54. package/build/src/lib/graphql-types.d.ts +17 -0
  55. package/build/src/lib/graphql-types.js +3 -0
  56. package/jest.config.js +23 -0
  57. package/package.json +52 -0
  58. package/src/DolomiteZap.ts +235 -0
  59. package/src/abis/IDolomiteMarginExchangeWrapper.json +80 -0
  60. package/src/clients/AggregatorClient.ts +19 -0
  61. package/src/clients/DolomiteClient.ts +234 -0
  62. package/src/clients/IsolationModeClient.ts +55 -0
  63. package/src/clients/ParaswapAggregator.ts +92 -0
  64. package/src/index.ts +31 -0
  65. package/src/lib/ApiTypes.ts +130 -0
  66. package/src/lib/Constants.ts +106 -0
  67. package/src/lib/Environment.ts +1 -0
  68. package/src/lib/GraphqlPageable.ts +23 -0
  69. package/src/lib/LocalCache.ts +34 -0
  70. package/src/lib/Logger.ts +59 -0
  71. package/src/lib/MathUtils.ts +13 -0
  72. package/src/lib/Utils.ts +41 -0
  73. package/src/lib/estimators/StandardEstimator.ts +54 -0
  74. package/src/lib/graphql-types.ts +19 -0
  75. package/tsconfig.eslint.json +6 -0
  76. package/tsconfig.json +36 -0
@@ -0,0 +1,235 @@
1
+ import { ethers } from 'ethers';
2
+ import AggregatorClient from './clients/AggregatorClient';
3
+ import DolomiteClient from './clients/DolomiteClient';
4
+ import ParaswapAggregator from './clients/ParaswapAggregator';
5
+ import {
6
+ Address,
7
+ AggregatorOutput,
8
+ ApiMarket,
9
+ ApiMarketHelper,
10
+ ApiToken,
11
+ GenericTraderParam,
12
+ GenericTraderType,
13
+ Integer,
14
+ MarketId,
15
+ Network,
16
+ ZapOutputParam,
17
+ } from './lib/ApiTypes';
18
+ import { INTEGERS } from './lib/Constants';
19
+ import { LocalCache } from './lib/LocalCache';
20
+ import { removeDuplicates, toChecksumOpt, zapOutputParamToJson } from './lib/Utils';
21
+
22
+ const ONE_HOUR = 60 * 60;
23
+
24
+ export class DolomiteZap {
25
+ public readonly network: Network;
26
+ public readonly subgraphUrl: string;
27
+ public readonly web3Provider: ethers.providers.Provider;
28
+
29
+ private client: DolomiteClient;
30
+ private paraswapAggregator: ParaswapAggregator;
31
+ private marketsCache: LocalCache<Record<MarketId, ApiMarket>>;
32
+ private marketHelpersCache: LocalCache<Record<MarketId, ApiMarketHelper>>;
33
+ private validAggregators: AggregatorClient[];
34
+
35
+ public constructor(
36
+ network: Network,
37
+ subgraphUrl: string,
38
+ web3Provider: ethers.providers.Provider,
39
+ cacheSeconds: number = ONE_HOUR,
40
+ ) {
41
+ this.network = network;
42
+ this.subgraphUrl = subgraphUrl;
43
+ this.web3Provider = web3Provider;
44
+
45
+ this.client = new DolomiteClient(subgraphUrl, network, web3Provider);
46
+ this.paraswapAggregator = new ParaswapAggregator(network);
47
+ this.marketsCache = new LocalCache<Record<MarketId, ApiMarket>>(cacheSeconds);
48
+ this.marketHelpersCache = new LocalCache<Record<MarketId, ApiMarketHelper>>(cacheSeconds);
49
+ this.validAggregators = [this.paraswapAggregator].filter(aggregator => aggregator.isValidForNetwork());
50
+ }
51
+
52
+ /**
53
+ *
54
+ * @param tokenIn
55
+ * @param amountIn
56
+ * @param tokenOut
57
+ * @param amountOutMin The minimum amount out required for the swap to be considered valid
58
+ * @param txOrigin The address that will execute the transaction
59
+ * @return {Promise<ZapOutputParam[]>} A list of outputs that can be used to execute the trade. The outputs are
60
+ * sorted by execution, with the best ones being first.
61
+ */
62
+ public async getSwapExactTokensForTokensParams(
63
+ tokenIn: ApiToken,
64
+ amountIn: Integer,
65
+ tokenOut: ApiToken,
66
+ amountOutMin: Integer,
67
+ txOrigin: Address,
68
+ ): Promise<ZapOutputParam[]> {
69
+ const marketsMap = await this.getMarketsMap();
70
+ const marketHelpersMap = await this.getMarketHelpersMap(marketsMap);
71
+ const inputMarket = marketsMap[tokenIn.marketId];
72
+ const inputHelper = marketHelpersMap[tokenIn.marketId];
73
+ const outputMarket = marketsMap[tokenOut.marketId];
74
+ const outputHelper = marketHelpersMap[tokenOut.marketId];
75
+
76
+ if (!inputMarket) {
77
+ return Promise.reject(new Error(`Invalid tokenIn: ${tokenIn.symbol} / ${tokenIn.marketId}`));
78
+ } else if (!outputMarket) {
79
+ return Promise.reject(new Error(`Invalid tokenOut: ${tokenOut.symbol} / ${tokenOut.marketId}`));
80
+ } else if (amountIn.lte(INTEGERS.ZERO)) {
81
+ return Promise.reject(new Error('Invalid amountIn. Must be greater than 0'));
82
+ } else if (amountOutMin.lte(INTEGERS.ZERO)) {
83
+ return Promise.reject(new Error('Invalid amountOutMin. Must be greater than 0'));
84
+ } else if (!toChecksumOpt(txOrigin)) {
85
+ return Promise.reject(new Error('Invalid txOrigin'));
86
+ }
87
+
88
+ const marketIdsPath: number[] = [inputMarket.marketId];
89
+ const amountsPaths = new Array<Integer[]>(this.validAggregators.length).fill([amountIn]);
90
+ const traderParamsArrays = new Array<GenericTraderParam[]>(this.validAggregators.length).fill([]);
91
+ let effectiveInputMarketId = inputMarket.marketId;
92
+ let effectiveOutputMarketId = outputMarket.marketId;
93
+
94
+ const isIsolationModeUnwrapper = inputMarket.isolationModeUnwrapperInfo;
95
+ const unwrapperInfo = inputMarket.isolationModeUnwrapperInfo ?? inputMarket.liquidityTokenUnwrapperInfo;
96
+ const unwrapperHelper = inputHelper.isolationModeUnwrapperHelper ?? inputHelper.liquidityTokenUnwrapperHelper;
97
+ if (unwrapperInfo && unwrapperHelper) {
98
+ effectiveInputMarketId = unwrapperInfo.outputMarketId;
99
+ marketIdsPath.push(effectiveInputMarketId);
100
+
101
+ const { amountOut, tradeData } = await unwrapperHelper.estimateOutputFunction(
102
+ amountIn,
103
+ unwrapperInfo.outputMarketId,
104
+ );
105
+
106
+ amountsPaths.forEach(amountsPath => amountsPath.push(amountOut));
107
+ traderParamsArrays.forEach(traderParams => {
108
+ traderParams.push({
109
+ traderType: isIsolationModeUnwrapper
110
+ ? GenericTraderType.IsolationModeUnwrapper
111
+ : GenericTraderType.ExternalLiquidity,
112
+ makerAccountIndex: 0,
113
+ trader: unwrapperInfo.unwrapperAddress,
114
+ tradeData,
115
+ });
116
+ });
117
+ }
118
+
119
+ const isIsolationModeWrapper = outputMarket.isolationModeWrapperInfo;
120
+ const wrapperInfo = outputMarket.isolationModeWrapperInfo ?? outputMarket.liquidityTokenWrapperInfo;
121
+ const wrapperHelper = outputHelper.isolationModeWrapperHelper ?? outputHelper.liquidityTokenWrapperHelper;
122
+ if (wrapperInfo) {
123
+ // We can't get the amount yet until we know if we need to use an aggregator in the middle
124
+ effectiveOutputMarketId = wrapperInfo.inputMarketId;
125
+ if (effectiveInputMarketId !== effectiveOutputMarketId) {
126
+ marketIdsPath.push(wrapperInfo.inputMarketId);
127
+ }
128
+ }
129
+
130
+ if (effectiveInputMarketId !== effectiveOutputMarketId) {
131
+ if (!marketIdsPath.includes(effectiveOutputMarketId)) {
132
+ marketIdsPath.push(effectiveOutputMarketId);
133
+ }
134
+ const effectiveInputMarket = marketsMap[effectiveInputMarketId];
135
+ const effectiveOutputMarket = marketsMap[effectiveOutputMarketId];
136
+ const aggregatorOutputOrUndefinedList = await Promise.all(
137
+ this.validAggregators.map((aggregator, i) => {
138
+ return aggregator.getSwapExactTokensForTokensData(
139
+ effectiveInputMarket,
140
+ amountsPaths[i][amountsPaths[i].length - 1],
141
+ effectiveOutputMarket,
142
+ INTEGERS.ONE,
143
+ txOrigin,
144
+ )
145
+ }),
146
+ );
147
+
148
+ const aggregatorOutputs = aggregatorOutputOrUndefinedList.filter(trader => !!trader) as AggregatorOutput[];
149
+ if (aggregatorOutputs.length !== this.validAggregators.length) {
150
+ throw new Error('Invalid aggregator outputs length')
151
+ }
152
+
153
+ amountsPaths.forEach((amountsPath, i) => amountsPath.push(aggregatorOutputs[i].expectedAmountOut));
154
+ traderParamsArrays.forEach((traderParams, i) => {
155
+ traderParams.push({
156
+ traderType: GenericTraderType.ExternalLiquidity,
157
+ makerAccountIndex: 0,
158
+ trader: aggregatorOutputs[i].traderAddress,
159
+ tradeData: aggregatorOutputs[i].tradeData,
160
+ });
161
+ });
162
+ }
163
+
164
+ if (wrapperInfo && wrapperHelper) {
165
+ // Append the amounts and trader params for the wrapper
166
+ const amountsAndTraderParams = await Promise.all(
167
+ amountsPaths.map(async (amountsPath) => {
168
+ const amountInForEstimation = amountsPath[amountsPath.length - 1];
169
+ const outputEstimate = await wrapperHelper.estimateOutputFunction(
170
+ amountInForEstimation,
171
+ wrapperInfo.inputMarketId,
172
+ );
173
+ return {
174
+ amountOut: outputEstimate.amountOut,
175
+ traderParam: {
176
+ traderType: isIsolationModeWrapper
177
+ ? GenericTraderType.IsolationModeWrapper
178
+ : GenericTraderType.ExternalLiquidity,
179
+ makerAccountIndex: 0,
180
+ trader: wrapperInfo.wrapperAddress,
181
+ tradeData: outputEstimate.tradeData,
182
+ },
183
+ }
184
+ }),
185
+ );
186
+
187
+ amountsPaths.forEach((amountsPath, i) => {
188
+ amountsPath.push(amountsAndTraderParams[i].amountOut);
189
+ traderParamsArrays[i].push(amountsAndTraderParams[i].traderParam);
190
+ });
191
+ }
192
+
193
+ if (!marketIdsPath.includes(outputMarket.marketId)) {
194
+ marketIdsPath.push(outputMarket.marketId);
195
+ }
196
+
197
+ const result = this.validAggregators.map<ZapOutputParam>((_, i) => {
198
+ return {
199
+ marketIdsPath,
200
+ amountWeisPath: amountsPaths[i],
201
+ traderParams: traderParamsArrays[i],
202
+ makerAccounts: [],
203
+ amountOutMin,
204
+ };
205
+ });
206
+
207
+ return removeDuplicates(result, zapOutputParamToJson);
208
+ }
209
+
210
+ private async getMarketsMap(): Promise<Record<MarketId, ApiMarket>> {
211
+ const marketsKey = 'MARKETS';
212
+ const cachedMarkets = this.marketsCache.get(marketsKey);
213
+ if (cachedMarkets) {
214
+ return cachedMarkets;
215
+ }
216
+
217
+ const marketsMap = await this.client.getDolomiteMarketsMap();
218
+ this.marketsCache.set(marketsKey, marketsMap);
219
+ return marketsMap;
220
+ }
221
+
222
+ private async getMarketHelpersMap(
223
+ marketsMap: Record<MarketId, ApiMarket>,
224
+ ): Promise<Record<MarketId, ApiMarketHelper>> {
225
+ const marketHelpersKey = 'MARKET_HELPERS';
226
+ const cachedMarkets = this.marketHelpersCache.get(marketHelpersKey);
227
+ if (cachedMarkets) {
228
+ return cachedMarkets;
229
+ }
230
+
231
+ const marketHelpersMap = await this.client.getDolomiteMarketHelpers(marketsMap);
232
+ this.marketHelpersCache.set(marketHelpersKey, marketHelpersMap);
233
+ return marketHelpersMap;
234
+ }
235
+ }
@@ -0,0 +1,80 @@
1
+ [
2
+ {
3
+ "inputs": [
4
+ {
5
+ "internalType": "address",
6
+ "name": "_tradeOriginator",
7
+ "type": "address"
8
+ },
9
+ {
10
+ "internalType": "address",
11
+ "name": "_receiver",
12
+ "type": "address"
13
+ },
14
+ {
15
+ "internalType": "address",
16
+ "name": "_outputToken",
17
+ "type": "address"
18
+ },
19
+ {
20
+ "internalType": "address",
21
+ "name": "_inputToken",
22
+ "type": "address"
23
+ },
24
+ {
25
+ "internalType": "uint256",
26
+ "name": "_inputAmount",
27
+ "type": "uint256"
28
+ },
29
+ {
30
+ "internalType": "bytes",
31
+ "name": "_orderData",
32
+ "type": "bytes"
33
+ }
34
+ ],
35
+ "name": "exchange",
36
+ "outputs": [
37
+ {
38
+ "internalType": "uint256",
39
+ "name": "",
40
+ "type": "uint256"
41
+ }
42
+ ],
43
+ "stateMutability": "nonpayable",
44
+ "type": "function"
45
+ },
46
+ {
47
+ "inputs": [
48
+ {
49
+ "internalType": "address",
50
+ "name": "_inputToken",
51
+ "type": "address"
52
+ },
53
+ {
54
+ "internalType": "address",
55
+ "name": "_outputToken",
56
+ "type": "address"
57
+ },
58
+ {
59
+ "internalType": "uint256",
60
+ "name": "_desiredInputAmount",
61
+ "type": "uint256"
62
+ },
63
+ {
64
+ "internalType": "bytes",
65
+ "name": "_orderData",
66
+ "type": "bytes"
67
+ }
68
+ ],
69
+ "name": "getExchangeCost",
70
+ "outputs": [
71
+ {
72
+ "internalType": "uint256",
73
+ "name": "",
74
+ "type": "uint256"
75
+ }
76
+ ],
77
+ "stateMutability": "view",
78
+ "type": "function"
79
+ }
80
+ ]
@@ -0,0 +1,19 @@
1
+ import { Address, AggregatorOutput, ApiMarket, ApiToken, Integer, Network } from '../lib/ApiTypes';
2
+
3
+ export default abstract class AggregatorClient {
4
+ public readonly network: Network;
5
+
6
+ protected constructor(network: Network) {
7
+ this.network = network;
8
+ }
9
+
10
+ public abstract isValidForNetwork(): boolean;
11
+
12
+ public abstract getSwapExactTokensForTokensData(
13
+ inputMarket: ApiMarket | ApiToken,
14
+ inputAmountWei: Integer,
15
+ outputMarket: ApiMarket | ApiToken,
16
+ minOutputAmountWei: Integer,
17
+ txOrigin: Address,
18
+ ): Promise<AggregatorOutput | undefined>
19
+ }
@@ -0,0 +1,234 @@
1
+ import axios from 'axios';
2
+ import { ethers } from 'ethers';
3
+ import {
4
+ ApiMarket,
5
+ ApiMarketHelper,
6
+ ApiUnwrapperInfo,
7
+ ApiWrapperInfo,
8
+ BlockTag,
9
+ MarketId,
10
+ Network,
11
+ } from '../lib/ApiTypes';
12
+ import { StandardEstimator } from '../lib/estimators/StandardEstimator';
13
+ import { GraphqlMarketResult } from '../lib/graphql-types';
14
+ import GraphqlPageable from '../lib/GraphqlPageable';
15
+ import Logger from '../lib/Logger';
16
+ import IsolationModeClient from './IsolationModeClient';
17
+
18
+ const defaultAxiosConfig = {
19
+ headers: { 'Accept-Encoding': 'gzip,deflate,compress' },
20
+ };
21
+
22
+ export default class DolomiteClient {
23
+ private readonly subgraphUrl: string;
24
+ private readonly networkId: Network;
25
+ private readonly web3Provider: ethers.providers.Provider;
26
+
27
+ public constructor(
28
+ subgraphUrl: string,
29
+ networkId: Network,
30
+ web3Provider: ethers.providers.Provider,
31
+ ) {
32
+ this.subgraphUrl = subgraphUrl;
33
+ this.networkId = networkId;
34
+ this.web3Provider = web3Provider;
35
+ }
36
+
37
+ public async getDolomiteMarketsMap(
38
+ blockTag: BlockTag = 'latest',
39
+ ): Promise<Record<MarketId, ApiMarket>> {
40
+ const marketsList = await GraphqlPageable.getPageableValues((pageIndex) => this.getDolomiteMarketsWithPaging(
41
+ blockTag,
42
+ pageIndex,
43
+ ));
44
+ return marketsList.reduce<Record<MarketId, ApiMarket>>((acc, market) => {
45
+ acc[market.marketId] = market;
46
+ return acc;
47
+ }, {});
48
+ }
49
+
50
+ public async getDolomiteMarketHelpers(
51
+ marketsMap: Record<MarketId, ApiMarket>,
52
+ ): Promise<Record<MarketId, ApiMarketHelper>> {
53
+ const standardEstimator = new StandardEstimator(this.web3Provider, marketsMap);
54
+ return Object.values(marketsMap).reduce<Record<MarketId, ApiMarketHelper>>((acc, market) => {
55
+ const marketHelper: ApiMarketHelper = {
56
+ marketId: market.marketId,
57
+ isolationModeUnwrapperHelper: undefined,
58
+ liquidityTokenUnwrapperHelper: undefined,
59
+ isolationModeWrapperHelper: undefined,
60
+ liquidityTokenWrapperHelper: undefined,
61
+ }
62
+
63
+ const isolationModeUnwrapper = market.isolationModeUnwrapperInfo;
64
+ if (isolationModeUnwrapper) {
65
+ marketHelper.isolationModeUnwrapperHelper = {
66
+ estimateOutputFunction: async (
67
+ amountIn,
68
+ outputMarketId,
69
+ ) => standardEstimator.getUnwrappedAmount(
70
+ market.tokenAddress,
71
+ isolationModeUnwrapper.unwrapperAddress,
72
+ amountIn,
73
+ outputMarketId,
74
+ ),
75
+ }
76
+ }
77
+ const liquidityTokenUnwrapper = market.liquidityTokenUnwrapperInfo;
78
+ if (liquidityTokenUnwrapper) {
79
+ marketHelper.liquidityTokenUnwrapperHelper = {
80
+ estimateOutputFunction: async (
81
+ amountIn,
82
+ outputMarketId,
83
+ ) => standardEstimator.getUnwrappedAmount(
84
+ market.tokenAddress,
85
+ liquidityTokenUnwrapper.unwrapperAddress,
86
+ amountIn,
87
+ outputMarketId,
88
+ ),
89
+ }
90
+ }
91
+ const isolationModeWrapper = market.isolationModeWrapperInfo;
92
+ if (isolationModeWrapper) {
93
+ marketHelper.isolationModeWrapperHelper = {
94
+ estimateOutputFunction: async (
95
+ amountIn,
96
+ inputMarketId,
97
+ ) => standardEstimator.getWrappedAmount(
98
+ market.tokenAddress,
99
+ isolationModeWrapper.wrapperAddress,
100
+ amountIn,
101
+ inputMarketId,
102
+ ),
103
+ }
104
+ }
105
+ const liquidityTokenWrapper = market.liquidityTokenWrapperInfo;
106
+ if (liquidityTokenWrapper) {
107
+ marketHelper.liquidityTokenWrapperHelper = {
108
+ estimateOutputFunction: async (
109
+ amountIn,
110
+ inputMarketId,
111
+ ) => standardEstimator.getWrappedAmount(
112
+ market.tokenAddress,
113
+ liquidityTokenWrapper.wrapperAddress,
114
+ amountIn,
115
+ inputMarketId,
116
+ ),
117
+ }
118
+ }
119
+
120
+ acc[market.marketId] = marketHelper;
121
+ return acc;
122
+ }, {});
123
+ }
124
+
125
+ private async getDolomiteMarketsWithPaging(
126
+ blockTag: BlockTag = 'latest',
127
+ pageIndex: number = 0,
128
+ ): Promise<ApiMarket[]> {
129
+ let query: string;
130
+ if (blockTag === 'latest') {
131
+ // omit the time travel predicate
132
+ query = `query getMarketRiskInfos($skip: Int) {
133
+ marketRiskInfos(first: ${GraphqlPageable.MAX_PAGE_SIZE} skip: $skip) {
134
+ id
135
+ token {
136
+ id
137
+ marketId
138
+ name
139
+ symbol
140
+ decimals
141
+ }
142
+ }
143
+ }`
144
+ } else {
145
+ query = `query getMarketRiskInfos($blockNumber: Int, $skip: Int) {
146
+ marketRiskInfos(block: { number: $blockNumber } first: ${GraphqlPageable.MAX_PAGE_SIZE} skip: $skip) {
147
+ id
148
+ token {
149
+ id
150
+ marketId
151
+ name
152
+ symbol
153
+ decimals
154
+ }
155
+ }
156
+ }`;
157
+ }
158
+ const result: GraphqlMarketResult = await axios.post(
159
+ this.subgraphUrl,
160
+ {
161
+ query,
162
+ variables: {
163
+ blockNumber: blockTag === 'latest' ? undefined : blockTag,
164
+ skip: pageIndex * GraphqlPageable.MAX_PAGE_SIZE,
165
+ },
166
+ },
167
+ defaultAxiosConfig,
168
+ )
169
+ .then(response => response.data)
170
+ .then(json => json as GraphqlMarketResult);
171
+
172
+ if (result.errors && typeof result.errors === 'object') {
173
+ // noinspection JSPotentiallyInvalidTargetOfIndexedPropertyAccess
174
+ return Promise.reject(result.errors[0]);
175
+ }
176
+
177
+ const markets: Promise<ApiMarket | undefined>[] = result.data.marketRiskInfos.map(async (market) => {
178
+ const isolationModeClient = new IsolationModeClient(this.networkId);
179
+ let isolationModeUnwrapperInfo: ApiUnwrapperInfo | undefined;
180
+ let isolationModeWrapperInfo: ApiWrapperInfo | undefined;
181
+ if (isolationModeClient.isIsolationModeToken(market.token)) {
182
+ const unwrapperAddress = isolationModeClient.getIsolationModeUnwrapperByMarketId(market.token);
183
+ const outputMarketId = isolationModeClient.getIsolationModeUnwrapperMarketIdByMarketId(market.token);
184
+ const wrapperAddress = isolationModeClient.getIsolationModeWrapperByMarketId(market.token);
185
+ const inputMarketId = isolationModeClient.getIsolationModeWrapperMarketIdByMarketId(market.token);
186
+
187
+ if (!unwrapperAddress || typeof outputMarketId === 'undefined') {
188
+ Logger.warn({
189
+ message: 'Isolation Mode token cannot find unwrapper info!',
190
+ marketId: market.token.marketId,
191
+ });
192
+ return undefined;
193
+ } else if (!wrapperAddress || typeof inputMarketId === 'undefined') {
194
+ Logger.warn({
195
+ message: 'Isolation Mode token cannot find wrapper info!',
196
+ marketId: market.token.marketId,
197
+ });
198
+ return undefined;
199
+ }
200
+
201
+ isolationModeUnwrapperInfo = { unwrapperAddress, outputMarketId };
202
+ isolationModeWrapperInfo = { wrapperAddress, inputMarketId };
203
+ }
204
+
205
+ let liquidityTokenUnwrapperInfo: ApiUnwrapperInfo | undefined;
206
+ let liquidityTokenWrapperInfo: ApiWrapperInfo | undefined;
207
+ if (isolationModeClient.isLiquidityToken(market.token)) {
208
+ liquidityTokenUnwrapperInfo = {
209
+ unwrapperAddress: isolationModeClient.getLiquidityTokenUnwrapperByToken(market.token),
210
+ outputMarketId: isolationModeClient.getLiquidityTokenUnwrapperMarketIdByToken(market.token),
211
+ };
212
+ liquidityTokenWrapperInfo = {
213
+ wrapperAddress: isolationModeClient.getLiquidityTokenWrapperByToken(market.token),
214
+ inputMarketId: isolationModeClient.getLiquidityTokenWrapperMarketIdByToken(market.token),
215
+ };
216
+ }
217
+
218
+ const apiMarket: ApiMarket = {
219
+ marketId: Number(market.token.marketId),
220
+ symbol: market.token.symbol,
221
+ name: market.token.name,
222
+ tokenAddress: ethers.utils.getAddress(market.token.id),
223
+ decimals: Number(market.token.decimals),
224
+ isolationModeUnwrapperInfo,
225
+ isolationModeWrapperInfo,
226
+ liquidityTokenUnwrapperInfo,
227
+ liquidityTokenWrapperInfo,
228
+ };
229
+ return apiMarket;
230
+ });
231
+
232
+ return (await Promise.all(markets)).filter(market => !!market) as ApiMarket[];
233
+ }
234
+ }
@@ -0,0 +1,55 @@
1
+ import { Network } from '../lib/ApiTypes';
2
+ import { ISOLATION_MODE_CONVERSION_MARKET_ID_MAP, LIQUIDITY_TOKEN_CONVERSION_MARKET_ID_MAP } from '../lib/Constants';
3
+ import { GraphqlToken } from '../lib/graphql-types';
4
+
5
+ export default class IsolationModeClient {
6
+ public readonly network: Network;
7
+
8
+ constructor(network: Network) {
9
+ this.network = network;
10
+ }
11
+
12
+ // ==================== Isolation Mode Getters ====================
13
+
14
+ public isIsolationModeToken(token: GraphqlToken): boolean {
15
+ return token.name.includes('Dolomite Isolation:') || token.symbol === 'dfsGLP';
16
+ }
17
+
18
+ public getIsolationModeUnwrapperMarketIdByMarketId(token: GraphqlToken): number | undefined {
19
+ return ISOLATION_MODE_CONVERSION_MARKET_ID_MAP[this.network][token.marketId]?.unwrapperMarketId;
20
+ }
21
+
22
+ public getIsolationModeWrapperMarketIdByMarketId(token: GraphqlToken): number | undefined {
23
+ return ISOLATION_MODE_CONVERSION_MARKET_ID_MAP[this.network][token.marketId]?.wrapperMarketId;
24
+ }
25
+
26
+ public getIsolationModeUnwrapperByMarketId(token: GraphqlToken): string | undefined {
27
+ return ISOLATION_MODE_CONVERSION_MARKET_ID_MAP[this.network][token.marketId]?.unwrapper;
28
+ }
29
+
30
+ public getIsolationModeWrapperByMarketId(token: GraphqlToken): string | undefined {
31
+ return ISOLATION_MODE_CONVERSION_MARKET_ID_MAP[this.network][token.marketId]?.wrapper;
32
+ }
33
+
34
+ // ==================== Liquidity Token Getters ====================
35
+
36
+ public isLiquidityToken(token: GraphqlToken): boolean {
37
+ return LIQUIDITY_TOKEN_CONVERSION_MARKET_ID_MAP[this.network][token.marketId] !== undefined;
38
+ }
39
+
40
+ public getLiquidityTokenUnwrapperMarketIdByToken(token: GraphqlToken): number {
41
+ return LIQUIDITY_TOKEN_CONVERSION_MARKET_ID_MAP[this.network][token.marketId].unwrapperMarketId;
42
+ }
43
+
44
+ public getLiquidityTokenWrapperMarketIdByToken(token: GraphqlToken): number {
45
+ return LIQUIDITY_TOKEN_CONVERSION_MARKET_ID_MAP[this.network][token.marketId].wrapperMarketId;
46
+ }
47
+
48
+ public getLiquidityTokenUnwrapperByToken(token: GraphqlToken): string {
49
+ return LIQUIDITY_TOKEN_CONVERSION_MARKET_ID_MAP[this.network][token.marketId].unwrapper;
50
+ }
51
+
52
+ public getLiquidityTokenWrapperByToken(token: GraphqlToken): string {
53
+ return LIQUIDITY_TOKEN_CONVERSION_MARKET_ID_MAP[this.network][token.marketId].wrapper;
54
+ }
55
+ }