@paraswap/dex-lib 2.34.0 → 2.35.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 (72) hide show
  1. package/build/abi/api3-proxy.json +46 -0
  2. package/build/abi/api3-server-v1.json +463 -0
  3. package/build/abi/quick-perps/fast-price-events.json +70 -0
  4. package/build/abi/quick-perps/fast-price-feed.json +741 -0
  5. package/build/abi/quick-perps/reader.json +313 -0
  6. package/build/abi/quick-perps/vault-price-feed.json +323 -0
  7. package/build/abi/quick-perps/vault.json +1953 -0
  8. package/build/composed-event-subscriber.js +8 -2
  9. package/build/composed-event-subscriber.js.map +1 -1
  10. package/build/config.js +2 -2
  11. package/build/dex/algebra/algebra.js +0 -12
  12. package/build/dex/algebra/algebra.js.map +1 -1
  13. package/build/dex/index.js +2 -0
  14. package/build/dex/index.js.map +1 -1
  15. package/build/dex/quick-perps/config.d.ts +11 -0
  16. package/build/dex/quick-perps/config.js +27 -0
  17. package/build/dex/quick-perps/config.js.map +1 -0
  18. package/build/dex/quick-perps/fast-price-feed.d.ts +26 -0
  19. package/build/dex/quick-perps/fast-price-feed.js +176 -0
  20. package/build/dex/quick-perps/fast-price-feed.js.map +1 -0
  21. package/build/dex/quick-perps/pool.d.ts +21 -0
  22. package/build/dex/quick-perps/pool.js +210 -0
  23. package/build/dex/quick-perps/pool.js.map +1 -0
  24. package/build/dex/quick-perps/quick-perps.d.ts +52 -0
  25. package/build/dex/quick-perps/quick-perps.js +205 -0
  26. package/build/dex/quick-perps/quick-perps.js.map +1 -0
  27. package/build/dex/quick-perps/types.d.ts +98 -0
  28. package/build/dex/quick-perps/types.js +3 -0
  29. package/build/dex/quick-perps/types.js.map +1 -0
  30. package/build/dex/quick-perps/usdq.d.ts +15 -0
  31. package/build/dex/quick-perps/usdq.js +61 -0
  32. package/build/dex/quick-perps/usdq.js.map +1 -0
  33. package/build/dex/quick-perps/vault-price-feed.d.ts +43 -0
  34. package/build/dex/quick-perps/vault-price-feed.js +194 -0
  35. package/build/dex/quick-perps/vault-price-feed.js.map +1 -0
  36. package/build/dex/quick-perps/vault-utils.d.ts +8 -0
  37. package/build/dex/quick-perps/vault-utils.js +41 -0
  38. package/build/dex/quick-perps/vault-utils.js.map +1 -0
  39. package/build/dex/quick-perps/vault.d.ts +46 -0
  40. package/build/dex/quick-perps/vault.js +166 -0
  41. package/build/dex/quick-perps/vault.js.map +1 -0
  42. package/build/lib/api3-feed.d.ts +26 -0
  43. package/build/lib/api3-feed.js +82 -0
  44. package/build/lib/api3-feed.js.map +1 -0
  45. package/package.json +1 -1
  46. package/src/abi/api3-proxy.json +46 -0
  47. package/src/abi/api3-server-v1.json +463 -0
  48. package/src/abi/quick-perps/fast-price-events.json +70 -0
  49. package/src/abi/quick-perps/fast-price-feed.json +741 -0
  50. package/src/abi/quick-perps/reader.json +313 -0
  51. package/src/abi/quick-perps/vault-price-feed.json +323 -0
  52. package/src/abi/quick-perps/vault.json +1953 -0
  53. package/src/composed-event-subscriber.ts +16 -11
  54. package/src/config.ts +2 -2
  55. package/src/dex/algebra/algebra.ts +0 -13
  56. package/src/dex/index.ts +2 -0
  57. package/src/dex/quick-perps/config.ts +31 -0
  58. package/src/dex/quick-perps/fast-price-feed.ts +279 -0
  59. package/src/dex/quick-perps/pool.ts +372 -0
  60. package/src/dex/quick-perps/quick-perps-e2e.test.ts +65 -0
  61. package/src/dex/quick-perps/quick-perps-events.test.ts +101 -0
  62. package/src/dex/quick-perps/quick-perps-integration.test.ts +153 -0
  63. package/src/dex/quick-perps/quick-perps.ts +292 -0
  64. package/src/dex/quick-perps/types.ts +97 -0
  65. package/src/dex/quick-perps/usdq.ts +83 -0
  66. package/src/dex/quick-perps/vault-price-feed.ts +322 -0
  67. package/src/dex/quick-perps/vault-utils.ts +53 -0
  68. package/src/dex/quick-perps/vault.ts +290 -0
  69. package/src/lib/api3-feed.ts +140 -0
  70. package/tests/constants-e2e.ts +29 -0
  71. package/tests/tenderly-simulation.ts +46 -2
  72. package/tests/utils-e2e.ts +18 -10
@@ -110,17 +110,22 @@ export abstract class ComposedEventSubscriber<
110
110
  .call({}, blockNumber)
111
111
  ).returnData;
112
112
  }
113
- const stateParts = await Promise.all(
114
- this.parts.map((p, i) =>
115
- p.generateState(
116
- returnData.slice(...this.multiCallSlices[i]),
117
- blockNumber,
113
+ try {
114
+ const stateParts = await Promise.all(
115
+ this.parts.map((p, i) =>
116
+ p.generateState(
117
+ returnData.slice(...this.multiCallSlices[i]),
118
+ blockNumber,
119
+ ),
118
120
  ),
119
- ),
120
- );
121
- return this.parts.reduce(
122
- (acc, p, i) => p.lens.set(stateParts[i])(acc),
123
- this.blankState,
124
- );
121
+ );
122
+ return this.parts.reduce(
123
+ (acc, p, i) => p.lens.set(stateParts[i])(acc),
124
+ this.blankState,
125
+ );
126
+ } catch (e) {
127
+ this.logger.error(`Error generating state: ${e}`);
128
+ throw e;
129
+ }
125
130
  }
126
131
  }
package/src/config.ts CHANGED
@@ -332,8 +332,8 @@ const baseConfigs: { [network: number]: BaseConfig } = {
332
332
  multicallV2Address: '0x6cA478C852DfA8941FC819fDf248606eA04780B6',
333
333
  privateHttpProvider: process.env.HTTP_PROVIDER_1101,
334
334
  adapterAddresses: {
335
- PolygonZkEvmAdapter01: '0x760870be538e7b4E2110e2890473CC17ADB1fdC1',
336
- PolygonZkEvmBuyAdapter: '0x61bFA60A992472869F8d7b4705609A6E0BCf383f',
335
+ PolygonZkEvmAdapter01: '0xd63B7691dD98fa89A2ea5e1604700489c585aa7B',
336
+ PolygonZkEvmBuyAdapter: '0xe2137168CdA486a2555E16c597905854C84F9127',
337
337
  },
338
338
 
339
339
  rpcPollingMaxAllowedStateDelayInBlocks: 0,
@@ -817,20 +817,7 @@ export class Algebra extends SimpleExchange implements IDex<AlgebraData> {
817
817
  return null;
818
818
  }
819
819
 
820
- // TODO buy
821
- let lastNonZeroOutput = 0n;
822
- let lastNonZeroTickCountsOutputs = 0;
823
-
824
820
  for (let i = 0; i < outputsResult.outputs.length; i++) {
825
- // local pricing algo may output 0s at the tail for some out of range amounts, prefer to propagating last amount to appease top algo
826
- if (outputsResult.outputs[i] > 0n) {
827
- lastNonZeroOutput = outputsResult.outputs[i];
828
- lastNonZeroTickCountsOutputs = outputsResult.tickCounts[i];
829
- } else {
830
- outputsResult.outputs[i] = lastNonZeroOutput;
831
- outputsResult.tickCounts[i] = lastNonZeroTickCountsOutputs;
832
- }
833
-
834
821
  if (outputsResult.outputs[i] > destTokenBalance) {
835
822
  outputsResult.outputs[i] = 0n;
836
823
  outputsResult.tickCounts[i] = 0;
package/src/dex/index.ts CHANGED
@@ -73,6 +73,7 @@ import { SpiritSwapV3 } from './quickswap/spiritswap-v3';
73
73
  import { TraderJoeV21 } from './trader-joe-v2.1';
74
74
  import { PancakeswapV3 } from './pancakeswap-v3/pancakeswap-v3';
75
75
  import { Algebra } from './algebra/algebra';
76
+ import { QuickPerps } from './quick-perps/quick-perps';
76
77
  import { NomiswapV2 } from './uniswap-v2/nomiswap-v2';
77
78
 
78
79
  const LegacyDexes = [
@@ -144,6 +145,7 @@ const Dexes = [
144
145
  MaverickV1,
145
146
  Camelot,
146
147
  SwaapV2,
148
+ QuickPerps,
147
149
  NomiswapV2,
148
150
  ];
149
151
 
@@ -0,0 +1,31 @@
1
+ import { DexParams } from './types';
2
+ import { DexConfigMap } from '../../types';
3
+ import { Network, SwapSide } from '../../constants';
4
+
5
+ export const QuickPerpsConfig: DexConfigMap<DexParams> = {
6
+ QuickPerps: {
7
+ [Network.ZKEVM]: {
8
+ vault: '0x99B31498B0a1Dae01fc3433e3Cb60F095340935C',
9
+ reader: '0xf1CFB75854DE535475B88Bb6FBad317eea98c0F9',
10
+ priceFeed: '0x5b1F500134bdD7f4359F5B2adC65f839737290f4',
11
+ fastPriceFeed: '0x73903fEc691a80Ec47bc830bf3F0baD127A06e30',
12
+ fastPriceEvents: '0x08bC8ef0b71238055f9Ee6BBc90869D8d0DBdCCa',
13
+ usdq: '0x48aC594dd00c4aAcF40f83337fc6dA31F9F439A7',
14
+ },
15
+ },
16
+ };
17
+
18
+ export const Adapters: {
19
+ [chainId: number]: {
20
+ [side: string]: { name: string; index: number }[] | null;
21
+ };
22
+ } = {
23
+ [Network.ZKEVM]: {
24
+ [SwapSide.SELL]: [
25
+ {
26
+ name: 'PolygonZkEvmAdapter01',
27
+ index: 2,
28
+ },
29
+ ],
30
+ },
31
+ };
@@ -0,0 +1,279 @@
1
+ import _ from 'lodash';
2
+ import { Interface } from '@ethersproject/abi';
3
+ import { DeepReadonly } from 'ts-essentials';
4
+ import { PartialEventSubscriber } from '../../composed-event-subscriber';
5
+ import {
6
+ Address,
7
+ MultiCallInput,
8
+ MultiCallOutput,
9
+ Logger,
10
+ Log,
11
+ BlockHeader,
12
+ } from '../../types';
13
+ import { FastPriceFeedConfig, FastPriceFeedState } from './types';
14
+ import FastPriceFeedAbi from '../../abi/quick-perps/fast-price-feed.json';
15
+ import FastPriceEventsAbi from '../../abi/quick-perps/fast-price-events.json';
16
+ import { Lens } from '../../lens';
17
+
18
+ export class FastPriceFeed<State> extends PartialEventSubscriber<
19
+ State,
20
+ FastPriceFeedState
21
+ > {
22
+ static readonly interface = new Interface(FastPriceFeedAbi);
23
+ static readonly fastPriceEventsInterface = new Interface(FastPriceEventsAbi);
24
+
25
+ BASIS_POINTS_DIVISOR = 10000n;
26
+ protected priceDuration: number;
27
+ protected maxDeviationBasisPoints: bigint;
28
+ protected favorFastPrice: Record<string, boolean>;
29
+ private spreadBasisPointsIfInactive: bigint;
30
+ private spreadBasisPointsIfChainError: bigint;
31
+ private maxPriceUpdateDelay: number;
32
+
33
+ constructor(
34
+ private fastPriceFeedAddress: Address,
35
+ fastPriceEventsAddress: Address,
36
+ private tokenAddresses: Address[],
37
+ config: FastPriceFeedConfig,
38
+ lens: Lens<DeepReadonly<State>, DeepReadonly<FastPriceFeedState>>,
39
+ logger: Logger,
40
+ ) {
41
+ super([fastPriceEventsAddress], lens, logger);
42
+ this.priceDuration = config.priceDuration;
43
+ this.maxDeviationBasisPoints = config.maxDeviationBasisPoints;
44
+ this.favorFastPrice = config.favorFastPrice;
45
+ this.spreadBasisPointsIfInactive = config.spreadBasisPointsIfInactive;
46
+ this.spreadBasisPointsIfChainError = config.spreadBasisPointsIfChainError;
47
+ this.maxPriceUpdateDelay = config.maxPriceUpdateDelay;
48
+ }
49
+
50
+ public processLog(
51
+ state: DeepReadonly<FastPriceFeedState>,
52
+ log: Readonly<Log>,
53
+ blockHeader: Readonly<BlockHeader>,
54
+ ): DeepReadonly<FastPriceFeedState> | null {
55
+ try {
56
+ const parsed = FastPriceFeed.fastPriceEventsInterface.parseLog(log);
57
+ switch (parsed.name) {
58
+ case 'PriceUpdate': {
59
+ const _state: FastPriceFeedState = _.cloneDeep(state);
60
+ _state.lastUpdatedAt =
61
+ typeof blockHeader.timestamp === 'string'
62
+ ? parseInt(blockHeader.timestamp)
63
+ : blockHeader.timestamp;
64
+ const tokenAddress = parsed.args.token.toLowerCase();
65
+ if (tokenAddress in state.prices)
66
+ _state.prices[tokenAddress] = BigInt(parsed.args.price.toString());
67
+ return _state;
68
+ }
69
+ default:
70
+ return null;
71
+ }
72
+ } catch (e) {
73
+ this.logger.error('Failed to parse log', e);
74
+ return null;
75
+ }
76
+ }
77
+
78
+ getPrice(
79
+ _state: DeepReadonly<State>,
80
+ _token: Address,
81
+ _refPrice: bigint,
82
+ _maximize: boolean,
83
+ ) {
84
+ const state = this.lens.get()(_state);
85
+
86
+ const timestamp = Math.floor(Date.now() / 1000);
87
+
88
+ if (timestamp > state.lastUpdatedAt + this.maxPriceUpdateDelay) {
89
+ if (_maximize) {
90
+ return (
91
+ (_refPrice *
92
+ (this.BASIS_POINTS_DIVISOR + this.spreadBasisPointsIfChainError)) /
93
+ this.BASIS_POINTS_DIVISOR
94
+ );
95
+ }
96
+
97
+ return (
98
+ (_refPrice *
99
+ (this.BASIS_POINTS_DIVISOR - this.spreadBasisPointsIfChainError)) /
100
+ this.BASIS_POINTS_DIVISOR
101
+ );
102
+ }
103
+
104
+ if (timestamp > state.lastUpdatedAt + this.priceDuration) {
105
+ if (_maximize) {
106
+ return (
107
+ (_refPrice *
108
+ (this.BASIS_POINTS_DIVISOR + this.spreadBasisPointsIfInactive)) /
109
+ this.BASIS_POINTS_DIVISOR
110
+ );
111
+ }
112
+
113
+ return (
114
+ (_refPrice *
115
+ (this.BASIS_POINTS_DIVISOR - this.spreadBasisPointsIfInactive)) /
116
+ this.BASIS_POINTS_DIVISOR
117
+ );
118
+ }
119
+
120
+ const fastPrice = state.prices[_token];
121
+ if (fastPrice === 0n) return _refPrice;
122
+
123
+ let diffBasisPoints =
124
+ _refPrice > fastPrice ? _refPrice - fastPrice : fastPrice - _refPrice;
125
+ diffBasisPoints = (diffBasisPoints * this.BASIS_POINTS_DIVISOR) / _refPrice;
126
+
127
+ // create a spread between the _refPrice and the fastPrice if the maxDeviationBasisPoints is exceeded
128
+ // or if watchers have flagged an issue with the fast price
129
+ const hasSpread =
130
+ !this.favorFastPrice[_token] ||
131
+ diffBasisPoints > this.maxDeviationBasisPoints;
132
+
133
+ if (hasSpread) {
134
+ // return the higher of the two prices
135
+ if (_maximize) {
136
+ return _refPrice > fastPrice ? _refPrice : fastPrice;
137
+ }
138
+
139
+ // return the lower of the two prices
140
+ return _refPrice < fastPrice ? _refPrice : fastPrice;
141
+ }
142
+
143
+ return fastPrice;
144
+ }
145
+
146
+ static getConfigMulticallInputs(
147
+ fastPriceFeedAddress: Address,
148
+ tokenAddresses: Address[],
149
+ ): MultiCallInput[] {
150
+ return [
151
+ {
152
+ target: fastPriceFeedAddress,
153
+ callData: FastPriceFeed.interface.encodeFunctionData('priceDuration'),
154
+ },
155
+ {
156
+ target: fastPriceFeedAddress,
157
+ callData: FastPriceFeed.interface.encodeFunctionData(
158
+ 'maxDeviationBasisPoints',
159
+ ),
160
+ },
161
+ {
162
+ target: fastPriceFeedAddress,
163
+ callData: FastPriceFeed.interface.encodeFunctionData(
164
+ 'spreadBasisPointsIfInactive',
165
+ ),
166
+ },
167
+ {
168
+ target: fastPriceFeedAddress,
169
+ callData: FastPriceFeed.interface.encodeFunctionData(
170
+ 'spreadBasisPointsIfChainError',
171
+ ),
172
+ },
173
+ {
174
+ target: fastPriceFeedAddress,
175
+ callData: FastPriceFeed.interface.encodeFunctionData(
176
+ 'maxPriceUpdateDelay',
177
+ ),
178
+ },
179
+ ...tokenAddresses.map(t => ({
180
+ target: fastPriceFeedAddress,
181
+ callData: FastPriceFeed.interface.encodeFunctionData('favorFastPrice', [
182
+ t,
183
+ ]),
184
+ })),
185
+ ];
186
+ }
187
+
188
+ static getConfig(
189
+ multicallOutputs: MultiCallOutput[],
190
+ tokenAddresses: Address[],
191
+ ): FastPriceFeedConfig {
192
+ return {
193
+ priceDuration: parseInt(
194
+ FastPriceFeed.interface
195
+ .decodeFunctionResult('priceDuration', multicallOutputs[0])[0]
196
+ .toString(),
197
+ ),
198
+ maxDeviationBasisPoints: BigInt(
199
+ FastPriceFeed.interface
200
+ .decodeFunctionResult(
201
+ 'maxDeviationBasisPoints',
202
+ multicallOutputs[1],
203
+ )[0]
204
+ .toString(),
205
+ ),
206
+ spreadBasisPointsIfInactive: BigInt(
207
+ FastPriceFeed.interface
208
+ .decodeFunctionResult(
209
+ 'spreadBasisPointsIfInactive',
210
+ multicallOutputs[2],
211
+ )[0]
212
+ .toString(),
213
+ ),
214
+ spreadBasisPointsIfChainError: BigInt(
215
+ FastPriceFeed.interface
216
+ .decodeFunctionResult(
217
+ 'spreadBasisPointsIfChainError',
218
+ multicallOutputs[3],
219
+ )[0]
220
+ .toString(),
221
+ ),
222
+ maxPriceUpdateDelay: parseInt(
223
+ FastPriceFeed.interface
224
+ .decodeFunctionResult('maxPriceUpdateDelay', multicallOutputs[4])[0]
225
+ .toString(),
226
+ ),
227
+ favorFastPrice: multicallOutputs
228
+ .slice(5)
229
+ .reduce<Record<string, boolean>>((acc, curr, i) => {
230
+ acc[tokenAddresses[i]] = FastPriceFeed.interface.decodeFunctionResult(
231
+ 'favorFastPrice',
232
+ curr,
233
+ )[0];
234
+ return acc;
235
+ }, {}),
236
+ };
237
+ }
238
+
239
+ public getGenerateStateMultiCallInputs(): MultiCallInput[] {
240
+ const pricesEntries = this.tokenAddresses.map((t: Address) => ({
241
+ target: this.fastPriceFeedAddress,
242
+ callData: FastPriceFeed.interface.encodeFunctionData('prices', [t]),
243
+ }));
244
+ return [
245
+ ...pricesEntries,
246
+ {
247
+ target: this.fastPriceFeedAddress,
248
+ callData: FastPriceFeed.interface.encodeFunctionData('lastUpdatedAt'),
249
+ },
250
+ ];
251
+ }
252
+
253
+ public generateState(
254
+ multicallOutputs: MultiCallOutput[],
255
+ blockNumber?: number | 'latest',
256
+ ): DeepReadonly<FastPriceFeedState> {
257
+ let fastPriceFeedState: FastPriceFeedState = {
258
+ prices: {},
259
+ lastUpdatedAt: 0,
260
+ };
261
+ this.tokenAddresses.forEach(
262
+ (t: Address, i: number) =>
263
+ (fastPriceFeedState.prices[t] = BigInt(
264
+ FastPriceFeed.interface
265
+ .decodeFunctionResult('prices', multicallOutputs[i])[0]
266
+ .toString(),
267
+ )),
268
+ );
269
+ fastPriceFeedState.lastUpdatedAt = parseInt(
270
+ FastPriceFeed.interface
271
+ .decodeFunctionResult(
272
+ 'lastUpdatedAt',
273
+ multicallOutputs[this.tokenAddresses.length],
274
+ )[0]
275
+ .toString(),
276
+ );
277
+ return fastPriceFeedState;
278
+ }
279
+ }