@paraswap/dex-lib 2.33.4-balancer-v2-multihop.2 → 2.35.0

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 (148) hide show
  1. package/build/abi/algebra/AlgebraPool.abi.json +727 -0
  2. package/build/composed-event-subscriber.js +8 -2
  3. package/build/composed-event-subscriber.js.map +1 -1
  4. package/build/config.js +27 -0
  5. package/build/config.js.map +1 -1
  6. package/build/dex/algebra/algebra-pool-v1_1.d.ts +16 -2
  7. package/build/dex/algebra/algebra-pool-v1_1.js +166 -4
  8. package/build/dex/algebra/algebra-pool-v1_1.js.map +1 -1
  9. package/build/dex/algebra/algebra-pool.d.ts +46 -0
  10. package/build/dex/algebra/algebra-pool.js +336 -0
  11. package/build/dex/algebra/algebra-pool.js.map +1 -0
  12. package/build/dex/algebra/algebra.js +9 -7
  13. package/build/dex/algebra/algebra.js.map +1 -1
  14. package/build/dex/algebra/config.js +1 -0
  15. package/build/dex/algebra/config.js.map +1 -1
  16. package/build/dex/algebra/types.d.ts +3 -2
  17. package/build/dex/algebra/utils.d.ts +3 -1
  18. package/build/dex/algebra/utils.js +59 -1
  19. package/build/dex/algebra/utils.js.map +1 -1
  20. package/build/dex/balancer-v2/optimizer.d.ts +0 -1
  21. package/build/dex/balancer-v2/optimizer.js +21 -29
  22. package/build/dex/balancer-v2/optimizer.js.map +1 -1
  23. package/build/dex/index.js +2 -0
  24. package/build/dex/index.js.map +1 -1
  25. package/build/dex/solidly/forks-override/velodromeV2.js +4 -1
  26. package/build/dex/solidly/forks-override/velodromeV2.js.map +1 -1
  27. package/build/dex/uniswap-v2/nomiswap-v2.js.map +1 -1
  28. package/build/dex/uniswap-v3/uniswap-v3.js +7 -1
  29. package/build/dex/uniswap-v3/uniswap-v3.js.map +1 -1
  30. package/build/lib/decoders.d.ts +3 -0
  31. package/build/lib/decoders.js +9 -1
  32. package/build/lib/decoders.js.map +1 -1
  33. package/build/stateful-event-subscriber.d.ts +1 -0
  34. package/build/stateful-event-subscriber.js +6 -0
  35. package/build/stateful-event-subscriber.js.map +1 -1
  36. package/package.json +1 -1
  37. package/src/abi/algebra/AlgebraFactory-v1_1.abi.json +250 -0
  38. package/src/abi/api3-proxy.json +46 -0
  39. package/src/abi/api3-server-v1.json +463 -0
  40. package/src/abi/quick-perps/fast-price-events.json +70 -0
  41. package/src/abi/quick-perps/fast-price-feed.json +741 -0
  42. package/src/abi/quick-perps/reader.json +313 -0
  43. package/src/abi/quick-perps/vault-price-feed.json +323 -0
  44. package/src/abi/quick-perps/vault.json +1953 -0
  45. package/src/composed-event-subscriber.ts +16 -11
  46. package/src/config.ts +30 -0
  47. package/src/dex/algebra/algebra-integration.test.ts +122 -0
  48. package/src/dex/algebra/algebra-pool-v1_1.ts +259 -8
  49. package/src/dex/algebra/algebra.ts +9 -6
  50. package/src/dex/algebra/config.ts +1 -0
  51. package/src/dex/algebra/types.ts +3 -2
  52. package/src/dex/algebra/utils.ts +83 -2
  53. package/src/dex/balancer-v2/optimizer.ts +23 -38
  54. package/src/dex/index.ts +2 -0
  55. package/src/dex/quick-perps/config.ts +31 -0
  56. package/src/dex/quick-perps/fast-price-feed.ts +279 -0
  57. package/src/dex/quick-perps/pool.ts +372 -0
  58. package/src/dex/quick-perps/quick-perps-e2e.test.ts +65 -0
  59. package/src/dex/quick-perps/quick-perps-events.test.ts +101 -0
  60. package/src/dex/quick-perps/quick-perps-integration.test.ts +153 -0
  61. package/src/dex/quick-perps/quick-perps.ts +292 -0
  62. package/src/dex/quick-perps/types.ts +97 -0
  63. package/src/dex/quick-perps/usdq.ts +83 -0
  64. package/src/dex/quick-perps/vault-price-feed.ts +322 -0
  65. package/src/dex/quick-perps/vault-utils.ts +53 -0
  66. package/src/dex/quick-perps/vault.ts +290 -0
  67. package/src/dex/solidly/forks-override/velodromeV2.ts +4 -2
  68. package/src/dex/uniswap-v2/nomiswap-v2.ts +2 -1
  69. package/src/dex/uniswap-v2/uniswap-v2-e2e-bsc.test.ts +6 -8
  70. package/src/dex/uniswap-v3/uniswap-v3-e2e.test.ts +52 -47
  71. package/src/dex/uniswap-v3/uniswap-v3-integration.test.ts +1 -9
  72. package/src/dex/uniswap-v3/uniswap-v3.ts +7 -1
  73. package/src/lib/api3-feed.ts +140 -0
  74. package/src/lib/decoders.ts +13 -0
  75. package/src/stateful-event-subscriber.ts +8 -0
  76. package/tests/constants-e2e.ts +29 -0
  77. package/tests/tenderly-simulation.ts +46 -2
  78. package/tests/utils-e2e.ts +18 -10
  79. package/build/abi/quickPerps/fast-price-events.json +0 -70
  80. package/build/abi/quickPerps/fast-price-feed.json +0 -748
  81. package/build/abi/quickPerps/reader.json +0 -313
  82. package/build/abi/quickPerps/vault-price-feed.json +0 -477
  83. package/build/abi/quickPerps/vault.json +0 -1953
  84. package/build/abi/uniswap-v3/pancakeswap-v3/PancakeswapV3QuoterV2.abi.json +0 -285
  85. package/build/abi/uniswap-v3/pancakeswap-v3/PancakeswapV3Router.abi.json +0 -583
  86. package/build/abi/woo-fi/WooFeeManager.abi.json +0 -318
  87. package/build/abi/woo-fi/WooPP.abi.json +0 -548
  88. package/build/abi/woo-fi/Wooracle.abi.json +0 -260
  89. package/build/dex/aave-v3/tokens/arbitrum.json +0 -50
  90. package/build/dex/aave-v3/tokens/avalanche.json +0 -74
  91. package/build/dex/aave-v3/tokens/fantom.json +0 -62
  92. package/build/dex/aave-v3/tokens/optimism.json +0 -50
  93. package/build/dex/aave-v3/tokens/polygon.json +0 -116
  94. package/build/dex/balancer-v2/LinearMath.d.ts +0 -19
  95. package/build/dex/balancer-v2/LinearMath.js +0 -179
  96. package/build/dex/balancer-v2/LinearMath.js.map +0 -1
  97. package/build/dex/balancer-v2/LinearPool.d.ts +0 -47
  98. package/build/dex/balancer-v2/LinearPool.js +0 -324
  99. package/build/dex/balancer-v2/LinearPool.js.map +0 -1
  100. package/build/dex/balancer-v2/PhantomStablePool.d.ts +0 -47
  101. package/build/dex/balancer-v2/PhantomStablePool.js +0 -294
  102. package/build/dex/balancer-v2/PhantomStablePool.js.map +0 -1
  103. package/build/dex/balancer-v2/StableMath.d.ts +0 -9
  104. package/build/dex/balancer-v2/StableMath.js +0 -272
  105. package/build/dex/balancer-v2/StableMath.js.map +0 -1
  106. package/build/dex/balancer-v2/balancer-v2-pool.d.ts +0 -75
  107. package/build/dex/balancer-v2/balancer-v2-pool.js +0 -417
  108. package/build/dex/balancer-v2/balancer-v2-pool.js.map +0 -1
  109. package/build/dex/quickswap-v3.d.ts +0 -21
  110. package/build/dex/quickswap-v3.js +0 -40
  111. package/build/dex/quickswap-v3.js.map +0 -1
  112. package/build/dex/test-dex/config.d.ts +0 -4
  113. package/build/dex/test-dex/config.js +0 -15
  114. package/build/dex/test-dex/config.js.map +0 -1
  115. package/build/dex/test-dex/test-dex-pool.d.ts +0 -39
  116. package/build/dex/test-dex/test-dex-pool.js +0 -63
  117. package/build/dex/test-dex/test-dex-pool.js.map +0 -1
  118. package/build/dex/test-dex/test-dex.d.ts +0 -37
  119. package/build/dex/test-dex/test-dex.js +0 -126
  120. package/build/dex/test-dex/test-dex.js.map +0 -1
  121. package/build/dex/test-dex/types.d.ts +0 -6
  122. package/build/dex/test-dex/types.js +0 -3
  123. package/build/dex/test-dex/types.js.map +0 -1
  124. package/build/dex/uniswap-v3/forks/pancakeswap-v3/pancakeswap-v3.d.ts +0 -12
  125. package/build/dex/uniswap-v3/forks/pancakeswap-v3/pancakeswap-v3.js +0 -23
  126. package/build/dex/uniswap-v3/forks/pancakeswap-v3/pancakeswap-v3.js.map +0 -1
  127. package/build/dex/woo-fi/config.d.ts +0 -4
  128. package/build/dex/woo-fi/config.js +0 -141
  129. package/build/dex/woo-fi/config.js.map +0 -1
  130. package/build/dex/woo-fi/constants.d.ts +0 -5
  131. package/build/dex/woo-fi/constants.js +0 -20
  132. package/build/dex/woo-fi/constants.js.map +0 -1
  133. package/build/dex/woo-fi/types.d.ts +0 -48
  134. package/build/dex/woo-fi/types.js +0 -3
  135. package/build/dex/woo-fi/types.js.map +0 -1
  136. package/build/dex/woo-fi/woo-fi-decimal-math.d.ts +0 -14
  137. package/build/dex/woo-fi/woo-fi-decimal-math.js +0 -42
  138. package/build/dex/woo-fi/woo-fi-decimal-math.js.map +0 -1
  139. package/build/dex/woo-fi/woo-fi-math.d.ts +0 -31
  140. package/build/dex/woo-fi/woo-fi-math.js +0 -300
  141. package/build/dex/woo-fi/woo-fi-math.js.map +0 -1
  142. package/build/dex/woo-fi/woo-fi.d.ts +0 -65
  143. package/build/dex/woo-fi/woo-fi.js +0 -480
  144. package/build/dex/woo-fi/woo-fi.js.map +0 -1
  145. package/build/dex/woo-fi-v2/woo-fi-math.d.ts +0 -16
  146. package/build/dex/woo-fi-v2/woo-fi-math.js +0 -137
  147. package/build/dex/woo-fi-v2/woo-fi-math.js.map +0 -1
  148. package/src/dex/balancer-v2/balancer-v2-optimizer.test.ts +0 -226
@@ -0,0 +1,322 @@
1
+ import { Interface } from '@ethersproject/abi';
2
+ import { Address, MultiCallInput, MultiCallOutput } from '../../types';
3
+ import { VaultPriceFeedConfig } from './types';
4
+ import { FastPriceFeed } from './fast-price-feed';
5
+ import VaultPriceFeedAbi from '../../abi/quick-perps/vault-price-feed.json';
6
+ import { DeepReadonly } from 'ts-essentials';
7
+ import { Api3FeedSubscriber } from '../../lib/api3-feed';
8
+
9
+ export class VaultPriceFeed<State> {
10
+ BASIS_POINTS_DIVISOR = 10000n;
11
+ PRICE_PRECISION = 10n ** 30n;
12
+ ONE_USD = this.PRICE_PRECISION;
13
+
14
+ static interface = new Interface(VaultPriceFeedAbi);
15
+
16
+ protected isSecondaryPriceEnabled: boolean;
17
+ protected strictStableTokens: { [address: string]: boolean };
18
+ protected spreadBasisPoints: { [address: string]: bigint };
19
+ protected adjustmentBasisPoints: { [address: string]: bigint };
20
+ protected isAdjustmentAdditive: { [address: string]: boolean };
21
+ protected priceDecimals: { [address: string]: number };
22
+ protected maxStrictPriceDeviation: bigint;
23
+
24
+ constructor(
25
+ config: VaultPriceFeedConfig,
26
+ protected primaryPrices: { [token: string]: Api3FeedSubscriber<State> },
27
+ protected secondaryPrice: FastPriceFeed<State>,
28
+ ) {
29
+ this.isSecondaryPriceEnabled = config.isSecondaryPriceEnabled;
30
+ this.strictStableTokens = config.strictStableTokens;
31
+ this.spreadBasisPoints = config.spreadBasisPoints;
32
+ this.adjustmentBasisPoints = config.adjustmentBasisPoints;
33
+ this.isAdjustmentAdditive = config.isAdjustmentAdditive;
34
+ this.priceDecimals = config.priceDecimals;
35
+ this.maxStrictPriceDeviation = config.maxStrictPriceDeviation;
36
+ }
37
+
38
+ getPrice(
39
+ state: DeepReadonly<State>,
40
+ _token: Address,
41
+ _maximize: boolean,
42
+ _includeAmmPrice: boolean,
43
+ _useSwapPricing: boolean,
44
+ ): bigint {
45
+ let price = this.getPriceV1(state, _token, _maximize, _includeAmmPrice);
46
+
47
+ const adjustmentBps = this.adjustmentBasisPoints[_token];
48
+ if (adjustmentBps > 0n) {
49
+ const isAdditive = this.isAdjustmentAdditive[_token];
50
+ if (isAdditive) {
51
+ price =
52
+ (price * (this.BASIS_POINTS_DIVISOR + adjustmentBps)) /
53
+ this.BASIS_POINTS_DIVISOR;
54
+ } else {
55
+ price =
56
+ (price * (this.BASIS_POINTS_DIVISOR - adjustmentBps)) /
57
+ this.BASIS_POINTS_DIVISOR;
58
+ }
59
+ }
60
+
61
+ return price;
62
+ }
63
+
64
+ getPriceV1(
65
+ state: DeepReadonly<State>,
66
+ _token: Address,
67
+ _maximize: boolean,
68
+ _includeAmmPrice: boolean,
69
+ ): bigint {
70
+ let price = this.getPrimaryPrice(state, _token, _maximize);
71
+
72
+ if (this.isSecondaryPriceEnabled) {
73
+ price = this.getSecondaryPrice(state, _token, price, _maximize);
74
+ }
75
+
76
+ if (this.strictStableTokens[_token]) {
77
+ const delta =
78
+ price > this.ONE_USD ? price - this.ONE_USD : this.ONE_USD - price;
79
+ if (delta <= this.maxStrictPriceDeviation) {
80
+ return this.ONE_USD;
81
+ }
82
+
83
+ // if _maximize and price is e.g. 1.02, return 1.02
84
+ if (_maximize && price > this.ONE_USD) {
85
+ return price;
86
+ }
87
+
88
+ // if !_maximize and price is e.g. 0.98, return 0.98
89
+ if (!_maximize && price < this.ONE_USD) {
90
+ return price;
91
+ }
92
+
93
+ return this.ONE_USD;
94
+ }
95
+
96
+ const _spreadBasisPoints = this.spreadBasisPoints[_token];
97
+
98
+ if (_maximize) {
99
+ return (
100
+ (price * (this.BASIS_POINTS_DIVISOR + _spreadBasisPoints)) /
101
+ this.BASIS_POINTS_DIVISOR
102
+ );
103
+ }
104
+
105
+ return (
106
+ (price * (this.BASIS_POINTS_DIVISOR - _spreadBasisPoints)) /
107
+ this.BASIS_POINTS_DIVISOR
108
+ );
109
+ }
110
+
111
+ getAmmPrice(state: DeepReadonly<State>, token: Address): bigint {
112
+ throw new Error(
113
+ 'getAmmPrice implementation is not complete, developers should disable the dex or complete the implementation',
114
+ );
115
+ }
116
+
117
+ getPrimaryPrice(
118
+ state: DeepReadonly<State>,
119
+ _token: Address,
120
+ _maximize: boolean,
121
+ ): bigint {
122
+ // const priceFeedAddress = this.priceFeeds[_token];
123
+ // require(priceFeedAddress != address(0), "VaultPriceFeed: invalid price feed");
124
+
125
+ // if (chainlinkFlags != address(0)) {
126
+ // bool isRaised = IChainlinkFlags(chainlinkFlags).getFlag(FLAG_ARBITRUM_SEQ_OFFLINE);
127
+ // if (isRaised) {
128
+ // // If flag is raised we shouldn't perform any critical operations
129
+ // revert("Chainlink feeds are not being updated");
130
+ // }
131
+ // }
132
+
133
+ // IPriceFeed priceFeed = IPriceFeed(priceFeedAddress);
134
+ let price = 0n;
135
+ // const roundId = priceFeed.latestRound;
136
+
137
+ // for (let i = 0; i < this.priceSampleSpace; i++) {
138
+ // if (roundId <= i) {
139
+ // break;
140
+ // }
141
+
142
+ // if (i == 0) {
143
+ // const _p = priceFeed.latestAnswer();
144
+ // require(_p > 0, "VaultPriceFeed: invalid price");
145
+ // p = uint256(_p);
146
+ // } else {
147
+ // (, int256 _p, , ,) = priceFeed.getRoundData(roundId - i);
148
+ // require(_p > 0, "VaultPriceFeed: invalid price");
149
+ // p = uint256(_p);
150
+ // }
151
+
152
+ // if (price == 0n) {
153
+ // price = p;
154
+ // continue;
155
+ // }
156
+
157
+ // if (_maximize && p > price) {
158
+ // price = p;
159
+ // continue;
160
+ // }
161
+
162
+ // if (!_maximize && p < price) {
163
+ // price = p;
164
+ // }
165
+ // }
166
+ price = this.primaryPrices[_token].getLatestData(state);
167
+
168
+ // require(price > 0n, "VaultPriceFeed: could not fetch price");
169
+ if (price <= 0n) throw new Error('VaultPriceFeed: could not fetch price');
170
+ // normalize price precision
171
+ const _priceDecimals = this.priceDecimals[_token];
172
+ return (price * this.PRICE_PRECISION) / BigInt(10 ** _priceDecimals);
173
+ }
174
+
175
+ getSecondaryPrice(
176
+ state: DeepReadonly<State>,
177
+ _token: Address,
178
+ _referencePrice: bigint,
179
+ _maximize: boolean,
180
+ ): bigint {
181
+ return this.secondaryPrice.getPrice(
182
+ state,
183
+ _token,
184
+ _referencePrice,
185
+ _maximize,
186
+ );
187
+ }
188
+
189
+ static getConfigMulticallInputs(
190
+ vaultPriceFeedAddress: Address,
191
+ tokenAddresses: Address[],
192
+ ): MultiCallInput[] {
193
+ return [
194
+ {
195
+ target: vaultPriceFeedAddress,
196
+ callData: VaultPriceFeed.interface.encodeFunctionData(
197
+ 'isSecondaryPriceEnabled',
198
+ ),
199
+ },
200
+ ...tokenAddresses.map(t => ({
201
+ target: vaultPriceFeedAddress,
202
+ callData: VaultPriceFeed.interface.encodeFunctionData(
203
+ 'strictStableTokens',
204
+ [t],
205
+ ),
206
+ })),
207
+ ...tokenAddresses.map(t => ({
208
+ target: vaultPriceFeedAddress,
209
+ callData: VaultPriceFeed.interface.encodeFunctionData(
210
+ 'spreadBasisPoints',
211
+ [t],
212
+ ),
213
+ })),
214
+ ...tokenAddresses.map(t => ({
215
+ target: vaultPriceFeedAddress,
216
+ callData: VaultPriceFeed.interface.encodeFunctionData(
217
+ 'isAdjustmentAdditive',
218
+ [t],
219
+ ),
220
+ })),
221
+ ...tokenAddresses.map(t => ({
222
+ target: vaultPriceFeedAddress,
223
+ callData: VaultPriceFeed.interface.encodeFunctionData(
224
+ 'adjustmentBasisPoints',
225
+ [t],
226
+ ),
227
+ })),
228
+ ...tokenAddresses.map(t => ({
229
+ target: vaultPriceFeedAddress,
230
+ callData: VaultPriceFeed.interface.encodeFunctionData('priceDecimals', [
231
+ t,
232
+ ]),
233
+ })),
234
+ {
235
+ target: vaultPriceFeedAddress,
236
+ callData: VaultPriceFeed.interface.encodeFunctionData(
237
+ 'maxStrictPriceDeviation',
238
+ ),
239
+ },
240
+ ];
241
+ }
242
+
243
+ static getConfig(
244
+ multicallOutputs: MultiCallOutput[],
245
+ tokenAddress: Address[],
246
+ ): VaultPriceFeedConfig {
247
+ let i = 0;
248
+ return {
249
+ isSecondaryPriceEnabled: VaultPriceFeed.interface.decodeFunctionResult(
250
+ 'isSecondaryPriceEnabled',
251
+ multicallOutputs[i++],
252
+ )[0],
253
+ strictStableTokens: tokenAddress.reduce(
254
+ (acc: { [address: string]: boolean }, t: Address) => {
255
+ acc[t] = VaultPriceFeed.interface.decodeFunctionResult(
256
+ 'strictStableTokens',
257
+ multicallOutputs[i++],
258
+ )[0];
259
+ return acc;
260
+ },
261
+ {},
262
+ ),
263
+ spreadBasisPoints: tokenAddress.reduce(
264
+ (acc: { [address: string]: bigint }, t: Address) => {
265
+ acc[t] = BigInt(
266
+ VaultPriceFeed.interface
267
+ .decodeFunctionResult(
268
+ 'spreadBasisPoints',
269
+ multicallOutputs[i++],
270
+ )[0]
271
+ .toString(),
272
+ );
273
+ return acc;
274
+ },
275
+ {},
276
+ ),
277
+ isAdjustmentAdditive: tokenAddress.reduce(
278
+ (acc: { [address: string]: boolean }, t: Address) => {
279
+ acc[t] = VaultPriceFeed.interface.decodeFunctionResult(
280
+ 'isAdjustmentAdditive',
281
+ multicallOutputs[i++],
282
+ )[0];
283
+ return acc;
284
+ },
285
+ {},
286
+ ),
287
+ adjustmentBasisPoints: tokenAddress.reduce(
288
+ (acc: { [address: string]: bigint }, t: Address) => {
289
+ acc[t] = BigInt(
290
+ VaultPriceFeed.interface
291
+ .decodeFunctionResult(
292
+ 'adjustmentBasisPoints',
293
+ multicallOutputs[i++],
294
+ )[0]
295
+ .toString(),
296
+ );
297
+ return acc;
298
+ },
299
+ {},
300
+ ),
301
+ priceDecimals: tokenAddress.reduce(
302
+ (acc: { [address: string]: number }, t: Address) => {
303
+ acc[t] = parseInt(
304
+ VaultPriceFeed.interface
305
+ .decodeFunctionResult('priceDecimals', multicallOutputs[i++])[0]
306
+ .toString(),
307
+ );
308
+ return acc;
309
+ },
310
+ {},
311
+ ),
312
+ maxStrictPriceDeviation: BigInt(
313
+ VaultPriceFeed.interface
314
+ .decodeFunctionResult(
315
+ 'maxStrictPriceDeviation',
316
+ multicallOutputs[i++],
317
+ )[0]
318
+ .toString(),
319
+ ),
320
+ };
321
+ }
322
+ }
@@ -0,0 +1,53 @@
1
+ import { Vault } from './vault';
2
+ import { Address } from '../../types';
3
+ import { DeepReadonly } from 'ts-essentials';
4
+
5
+ export class VaultUtils<State> {
6
+ constructor(protected vault: Vault<State>) {}
7
+
8
+ getFeeBasisPoints(
9
+ state: DeepReadonly<State>,
10
+ _token: Address,
11
+ _usdqDelta: bigint,
12
+ _feeBasisPoints: bigint,
13
+ _taxBasisPoints: bigint,
14
+ _increment: boolean,
15
+ ) {
16
+ if (!this.vault.hasDynamicFees) {
17
+ return _feeBasisPoints;
18
+ }
19
+
20
+ const initialAmount = this.vault.getUSDQAmount(state, _token);
21
+ let nextAmount = initialAmount + _usdqDelta;
22
+ if (!_increment) {
23
+ nextAmount = _usdqDelta > initialAmount ? 0n : initialAmount - _usdqDelta;
24
+ }
25
+
26
+ const targetAmount = this.vault.getTargetUsdqAmount(state, _token);
27
+ if (targetAmount == 0n) {
28
+ return _feeBasisPoints;
29
+ }
30
+
31
+ const initialDiff =
32
+ initialAmount > targetAmount
33
+ ? initialAmount - targetAmount
34
+ : targetAmount - initialAmount;
35
+ const nextDiff =
36
+ nextAmount > targetAmount
37
+ ? nextAmount - targetAmount
38
+ : targetAmount - nextAmount;
39
+
40
+ // action improves relative asset balance
41
+ if (nextDiff < initialDiff) {
42
+ const rebateBps = (_taxBasisPoints * initialDiff) / targetAmount;
43
+ return rebateBps > _feeBasisPoints ? 0n : _feeBasisPoints - rebateBps;
44
+ }
45
+
46
+ let averageDiff = (initialDiff + nextDiff) / 2n;
47
+ if (averageDiff > targetAmount) {
48
+ averageDiff = targetAmount;
49
+ }
50
+ const taxBps = (_taxBasisPoints * averageDiff) / targetAmount;
51
+ return _feeBasisPoints + taxBps;
52
+ }
53
+ }
@@ -0,0 +1,290 @@
1
+ import _ from 'lodash';
2
+ import { Interface } from '@ethersproject/abi';
3
+ import { AsyncOrSync, DeepReadonly } from 'ts-essentials';
4
+ import { PartialEventSubscriber } from '../../composed-event-subscriber';
5
+ import { Lens } from '../../lens';
6
+ import VaultABI from '../../abi/quick-perps/vault.json';
7
+ import { VaultUtils } from './vault-utils';
8
+ import {
9
+ VaultConfig,
10
+ VaultState,
11
+ FastPriceFeedConfig,
12
+ PoolState,
13
+ } from './types';
14
+ import { VaultPriceFeed } from './vault-price-feed';
15
+ import { USDQ } from './usdq';
16
+ import {
17
+ MultiCallInput,
18
+ MultiCallOutput,
19
+ Address,
20
+ Logger,
21
+ Log,
22
+ } from '../../types';
23
+ import { BlockHeader } from 'web3-eth';
24
+
25
+ export class Vault<State> extends PartialEventSubscriber<State, VaultState> {
26
+ static readonly interface: Interface = new Interface(VaultABI);
27
+
28
+ protected vaultUtils: VaultUtils<State>;
29
+
30
+ public tokenDecimals: { [address: string]: number };
31
+ public stableTokens: { [address: string]: boolean };
32
+ protected tokenWeights: { [address: string]: bigint };
33
+ public stableSwapFeeBasisPoints: bigint;
34
+ public swapFeeBasisPoints: bigint;
35
+ public stableTaxBasisPoints: bigint;
36
+ public taxBasisPoints: bigint;
37
+ public hasDynamicFees: bigint;
38
+ protected includeAmmPrice: boolean;
39
+ protected useSwapPricing: boolean;
40
+ protected totalTokenWeights: bigint;
41
+
42
+ constructor(
43
+ public readonly vaultAddress: Address,
44
+ protected tokenAddresses: Address[],
45
+ config: VaultConfig,
46
+ protected vaultPriceFeed: VaultPriceFeed<State>,
47
+ protected usdq: USDQ<State>,
48
+ lens: Lens<DeepReadonly<State>, DeepReadonly<VaultState>>,
49
+ logger: Logger,
50
+ ) {
51
+ super([vaultAddress], lens, logger);
52
+ this.vaultUtils = new VaultUtils(this);
53
+ this.tokenDecimals = config.tokenDecimals;
54
+ this.stableTokens = config.stableTokens;
55
+ this.tokenWeights = config.tokenWeights;
56
+ this.stableSwapFeeBasisPoints = config.stableSwapFeeBasisPoints;
57
+ this.swapFeeBasisPoints = config.swapFeeBasisPoints;
58
+ this.stableTaxBasisPoints = config.stableTaxBasisPoints;
59
+ this.taxBasisPoints = config.taxBasisPoints;
60
+ this.hasDynamicFees = config.hasDynamicFees;
61
+ this.includeAmmPrice = config.includeAmmPrice;
62
+ this.useSwapPricing = config.useSwapPricing;
63
+ this.totalTokenWeights = config.totalTokenWeights;
64
+ }
65
+
66
+ getMinPrice(state: DeepReadonly<State>, _token: Address): bigint {
67
+ return this.vaultPriceFeed.getPrice(
68
+ state,
69
+ _token,
70
+ false,
71
+ this.includeAmmPrice,
72
+ this.useSwapPricing,
73
+ );
74
+ }
75
+
76
+ getMaxPrice(state: DeepReadonly<State>, _token: Address): bigint {
77
+ return this.vaultPriceFeed.getPrice(
78
+ state,
79
+ _token,
80
+ true,
81
+ this.includeAmmPrice,
82
+ this.useSwapPricing,
83
+ );
84
+ }
85
+
86
+ getFeeBasisPoints(
87
+ state: DeepReadonly<State>,
88
+ _token: Address,
89
+ _usdqDelta: bigint,
90
+ _feeBasisPoints: bigint,
91
+ _taxBasisPoints: bigint,
92
+ _increment: boolean,
93
+ ): bigint {
94
+ return this.vaultUtils.getFeeBasisPoints(
95
+ state,
96
+ _token,
97
+ _usdqDelta,
98
+ _feeBasisPoints,
99
+ _taxBasisPoints,
100
+ _increment,
101
+ );
102
+ }
103
+
104
+ getTargetUsdqAmount(state: DeepReadonly<State>, _token: Address): bigint {
105
+ const supply = this.usdq.getTotalSupply(state);
106
+ if (supply == 0n) {
107
+ return 0n;
108
+ }
109
+ const weight = this.tokenWeights[_token];
110
+ return (weight * supply) / this.totalTokenWeights;
111
+ }
112
+
113
+ static getConfigMulticallInputs(
114
+ vaultAddress: Address,
115
+ tokenAddresses: Address[],
116
+ ): MultiCallInput[] {
117
+ return [
118
+ ...tokenAddresses.map(t => ({
119
+ target: vaultAddress,
120
+ callData: Vault.interface.encodeFunctionData('tokenDecimals', [t]),
121
+ })),
122
+ ...tokenAddresses.map(t => ({
123
+ target: vaultAddress,
124
+ callData: Vault.interface.encodeFunctionData('stableTokens', [t]),
125
+ })),
126
+ ...tokenAddresses.map(t => ({
127
+ target: vaultAddress,
128
+ callData: Vault.interface.encodeFunctionData('tokenWeights', [t]),
129
+ })),
130
+ ...[
131
+ 'stableSwapFeeBasisPoints',
132
+ 'swapFeeBasisPoints',
133
+ 'stableTaxBasisPoints',
134
+ 'taxBasisPoints',
135
+ 'hasDynamicFees',
136
+ 'includeAmmPrice',
137
+ 'useSwapPricing',
138
+ 'totalTokenWeights',
139
+ ].map(fn => ({
140
+ target: vaultAddress,
141
+ callData: Vault.interface.encodeFunctionData(fn),
142
+ })),
143
+ ];
144
+ }
145
+
146
+ static getConfig(
147
+ multicallOutputs: MultiCallOutput[],
148
+ tokenAddresses: Address[],
149
+ ): VaultConfig {
150
+ let i = 0;
151
+ return {
152
+ tokenDecimals: tokenAddresses.reduce(
153
+ (acc: { [address: string]: number }, t: Address) => {
154
+ acc[t] = parseInt(
155
+ Vault.interface
156
+ .decodeFunctionResult('tokenDecimals', multicallOutputs[i++])[0]
157
+ .toString(),
158
+ );
159
+ return acc;
160
+ },
161
+ {},
162
+ ),
163
+ stableTokens: tokenAddresses.reduce(
164
+ (acc: { [address: string]: boolean }, t: Address) => {
165
+ acc[t] = Vault.interface.decodeFunctionResult(
166
+ 'stableTokens',
167
+ multicallOutputs[i++],
168
+ )[0];
169
+ return acc;
170
+ },
171
+ {},
172
+ ),
173
+ tokenWeights: tokenAddresses.reduce(
174
+ (acc: { [address: string]: bigint }, t: Address) => {
175
+ acc[t] = BigInt(
176
+ Vault.interface
177
+ .decodeFunctionResult('tokenWeights', multicallOutputs[i++])[0]
178
+ .toString(),
179
+ );
180
+ return acc;
181
+ },
182
+ {},
183
+ ),
184
+ stableSwapFeeBasisPoints: BigInt(
185
+ Vault.interface
186
+ .decodeFunctionResult(
187
+ 'stableSwapFeeBasisPoints',
188
+ multicallOutputs[i++],
189
+ )[0]
190
+ .toString(),
191
+ ),
192
+ swapFeeBasisPoints: BigInt(
193
+ Vault.interface
194
+ .decodeFunctionResult('swapFeeBasisPoints', multicallOutputs[i++])[0]
195
+ .toString(),
196
+ ),
197
+ stableTaxBasisPoints: BigInt(
198
+ Vault.interface
199
+ .decodeFunctionResult(
200
+ 'stableTaxBasisPoints',
201
+ multicallOutputs[i++],
202
+ )[0]
203
+ .toString(),
204
+ ),
205
+ taxBasisPoints: BigInt(
206
+ Vault.interface
207
+ .decodeFunctionResult('taxBasisPoints', multicallOutputs[i++])[0]
208
+ .toString(),
209
+ ),
210
+ hasDynamicFees: Vault.interface.decodeFunctionResult(
211
+ 'hasDynamicFees',
212
+ multicallOutputs[i++],
213
+ )[0],
214
+ includeAmmPrice: Vault.interface.decodeFunctionResult(
215
+ 'includeAmmPrice',
216
+ multicallOutputs[i++],
217
+ )[0],
218
+ useSwapPricing: Vault.interface.decodeFunctionResult(
219
+ 'useSwapPricing',
220
+ multicallOutputs[i++],
221
+ )[0],
222
+ totalTokenWeights: BigInt(
223
+ Vault.interface
224
+ .decodeFunctionResult('totalTokenWeights', multicallOutputs[i++])[0]
225
+ .toString(),
226
+ ),
227
+ };
228
+ }
229
+
230
+ public getGenerateStateMultiCallInputs(): MultiCallInput[] {
231
+ return this.tokenAddresses.map((t: Address) => ({
232
+ target: this.vaultAddress,
233
+ callData: Vault.interface.encodeFunctionData('usdqAmounts', [t]),
234
+ }));
235
+ }
236
+
237
+ public generateState(
238
+ multicallOutputs: MultiCallOutput[],
239
+ blockNumber?: number | 'latest',
240
+ ): DeepReadonly<VaultState> {
241
+ let vaultState: VaultState = {
242
+ usdqAmounts: {},
243
+ };
244
+ this.tokenAddresses.forEach(
245
+ (t: Address, i: number) =>
246
+ (vaultState.usdqAmounts[t] = BigInt(
247
+ Vault.interface
248
+ .decodeFunctionResult('usdqAmounts', multicallOutputs[i])[0]
249
+ .toString(),
250
+ )),
251
+ );
252
+ return vaultState;
253
+ }
254
+
255
+ public getUSDQAmount(state: DeepReadonly<State>, token: Address): bigint {
256
+ return this.lens.get()(state).usdqAmounts[token];
257
+ }
258
+
259
+ public processLog(
260
+ state: DeepReadonly<VaultState>,
261
+ log: Readonly<Log>,
262
+ blockHeader: Readonly<BlockHeader>,
263
+ ): AsyncOrSync<VaultState | null> {
264
+ try {
265
+ const parsed = Vault.interface.parseLog(log);
266
+ const _state: VaultState = _.cloneDeep(state);
267
+ switch (parsed.name) {
268
+ case 'IncreaseUsdqAmount': {
269
+ const tokenAddress = parsed.args.token.toLowerCase();
270
+ const amount = BigInt(parsed.args.amount.toString());
271
+ if (tokenAddress in state.usdqAmounts)
272
+ _state.usdqAmounts[tokenAddress] += amount;
273
+ return _state;
274
+ }
275
+ case 'DecreaseUsdqAmount': {
276
+ const tokenAddress = parsed.args.token.toLowerCase();
277
+ const amount = BigInt(parsed.args.amount.toString());
278
+ if (tokenAddress in state.usdqAmounts)
279
+ _state.usdqAmounts[tokenAddress] -= amount;
280
+ return _state;
281
+ }
282
+ default:
283
+ return null;
284
+ }
285
+ } catch (e) {
286
+ this.logger.error('Failed to parse log', e);
287
+ return null;
288
+ }
289
+ }
290
+ }
@@ -24,7 +24,6 @@ const VelodromeV2FactoryABI = [
24
24
  const velodromeV2FactoryIface = new Interface(VelodromeV2FactoryABI);
25
25
 
26
26
  export class VelodromeV2 extends Solidly {
27
-
28
27
  public static dexKeysWithNetwork: { key: string; networks: Network[] }[] =
29
28
  getDexKeysWithNetwork(_.pick(SolidlyConfig, ['VelodromeV2']));
30
29
 
@@ -44,7 +43,10 @@ export class VelodromeV2 extends Solidly {
44
43
  protected getFeesMultiCallData(pair: SolidlyPair) {
45
44
  const callEntry = {
46
45
  target: this.factoryAddress,
47
- callData: velodromeV2FactoryIface.encodeFunctionData('getFee', [pair.exchange, pair.stable]),
46
+ callData: velodromeV2FactoryIface.encodeFunctionData('getFee', [
47
+ pair.exchange,
48
+ pair.stable,
49
+ ]),
48
50
  };
49
51
  const callDecoder = (values: any[]) =>
50
52
  parseInt(
@@ -15,7 +15,8 @@ export const NomiswapV2Config: DexConfigMap<DexParams> = {
15
15
  '0x83eb759f5ea0525124f03d4ac741bb4af0bb1c703d5f694bd42a8bd72e495a01',
16
16
  poolGasCost: 120 * 1000,
17
17
  feeCode: 0, // this is ignored as Nomiswap uses dynamic fees,
18
- subgraphURL: 'https://api.thegraph.com/subgraphs/name/nominex/nomiswap-exchange-subgraph',
18
+ subgraphURL:
19
+ 'https://api.thegraph.com/subgraphs/name/nominex/nomiswap-exchange-subgraph',
19
20
  },
20
21
  },
21
22
  };