@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
@@ -1,11 +1,12 @@
1
- import { BytesLike, ethers } from 'ethers';
1
+ import { BigNumber, BytesLike, ethers } from 'ethers';
2
2
  import { assert } from 'ts-essentials';
3
3
  import { extractSuccessAndValue } from '../../lib/decoders';
4
4
  import { MultiResult } from '../../lib/multi-wrapper';
5
5
  import {
6
- DecodedGlobalStateV1_9,
6
+ DecodedGlobalStateV1_1,
7
7
  DecodedStateMultiCallResultWithRelativeBitmapsV1_1,
8
8
  DecodedStateMultiCallResultWithRelativeBitmapsV1_9,
9
+ TickInfoWithBigNumber,
9
10
  } from './types';
10
11
 
11
12
  export function decodeStateMultiCallResultWithRelativeBitmapsV1_1(
@@ -134,3 +135,83 @@ export function decodeStateMultiCallResultWithRelativeBitmapsV1_9(
134
135
  // But I typed only the ones that are used later
135
136
  return decoded as DecodedStateMultiCallResultWithRelativeBitmapsV1_9;
136
137
  }
138
+
139
+ export function decodeGlobalStateV1_1(
140
+ result: MultiResult<BytesLike> | BytesLike,
141
+ ): DecodedGlobalStateV1_1 {
142
+ const [isSuccess, toDecode] = extractSuccessAndValue(result);
143
+
144
+ assert(
145
+ isSuccess && toDecode !== '0x',
146
+ `decodeGlobalStateV1_1 failed to get decodable result: ${result}`,
147
+ );
148
+
149
+ const results: DecodedGlobalStateV1_1 = {
150
+ price: BigNumber.from(0),
151
+ tick: 0,
152
+ fee: 0,
153
+ communityFeeToken0: 0,
154
+ communityFeeToken1: 0,
155
+ };
156
+
157
+ [
158
+ results.price,
159
+ results.tick,
160
+ results.fee,
161
+ ,
162
+ results.communityFeeToken0,
163
+ results.communityFeeToken1,
164
+ ] = ethers.utils.defaultAbiCoder.decode(
165
+ [`uint160`, `int24`, `uint16`, `uint16`, `uint8`, `uint8`, `bool`],
166
+ toDecode,
167
+ );
168
+ // This conversion is not precise, because when we decode, we have more values
169
+ // But used later
170
+ return results;
171
+ }
172
+
173
+ export function decodeTicksV1_1(
174
+ result: MultiResult<BytesLike> | BytesLike,
175
+ ): TickInfoWithBigNumber {
176
+ const [isSuccess, toDecode] = extractSuccessAndValue(result);
177
+
178
+ assert(
179
+ isSuccess && toDecode !== '0x',
180
+ `decodeGlobalStateV1_1 failed to get decodable result: ${result}`,
181
+ );
182
+
183
+ const results: TickInfoWithBigNumber = {
184
+ liquidityNet: BigNumber.from(0),
185
+ liquidityGross: BigNumber.from(0),
186
+ secondsOutside: 0,
187
+ secondsPerLiquidityOutsideX128: BigNumber.from(0),
188
+ tickCumulativeOutside: BigNumber.from(0),
189
+ initialized: false,
190
+ };
191
+
192
+ [
193
+ results.liquidityGross,
194
+ results.liquidityNet,
195
+ ,
196
+ ,
197
+ results.tickCumulativeOutside,
198
+ results.secondsPerLiquidityOutsideX128,
199
+ results.secondsOutside,
200
+ results.initialized,
201
+ ] = ethers.utils.defaultAbiCoder.decode(
202
+ [
203
+ `uint128`,
204
+ `int128`,
205
+ `uint256`,
206
+ `uint256`,
207
+ `int256`,
208
+ `uint160`,
209
+ `uint32`,
210
+ `bool`,
211
+ ],
212
+ toDecode,
213
+ );
214
+ // This conversion is not precise, because when we decode, we have more values
215
+ // But used later
216
+ return results;
217
+ }
@@ -3,20 +3,22 @@ import { BalancerSwapV2 } from './types';
3
3
  import { SwapSide } from '../../constants';
4
4
  import { BalancerConfig } from './config';
5
5
 
6
- export const AllBalancerV2Forks = Object.keys(BalancerConfig);
6
+ const MAX_UINT256 =
7
+ '115792089237316195423570985008687907853269984665640564039457584007913129639935';
8
+
9
+ const AllBalancerV2Forks = Object.keys(BalancerConfig);
7
10
 
8
11
  export function balancerV2Merge(or: UnoptimizedRate): UnoptimizedRate {
9
12
  const fixSwap = (
10
13
  rawSwap: OptimalSwapExchange<any>[],
11
- accumulatedBalancers: { [key: string]: OptimalSwapExchange<any> },
12
14
  side: SwapSide,
13
15
  ): OptimalSwapExchange<any>[] => {
14
- let optimizedSwap = new Array<OptimalSwapExchange<any>>();
15
16
  const newBalancers: { [key: string]: OptimalSwapExchange<any> } = {};
17
+ let optimizedSwap = new Array<OptimalSwapExchange<any>>();
16
18
  rawSwap.forEach((s: OptimalSwapExchange<any>) => {
17
19
  const exchangeKey = s.exchange.toLowerCase();
18
20
  if (AllBalancerV2Forks.some(d => d.toLowerCase() === exchangeKey)) {
19
- if (!(exchangeKey in accumulatedBalancers)) {
21
+ if (!(exchangeKey in newBalancers)) {
20
22
  newBalancers[exchangeKey] = {
21
23
  exchange: s.exchange,
22
24
  srcAmount: '0',
@@ -28,33 +30,27 @@ export function balancerV2Merge(or: UnoptimizedRate): UnoptimizedRate {
28
30
  gasUSD: '0',
29
31
  },
30
32
  };
31
- accumulatedBalancers[exchangeKey] = newBalancers[exchangeKey];
32
33
  }
33
- accumulatedBalancers[exchangeKey].srcAmount = (
34
- BigInt(accumulatedBalancers[exchangeKey].srcAmount) +
35
- BigInt(s.srcAmount)
34
+ newBalancers[exchangeKey].srcAmount = (
35
+ BigInt(newBalancers[exchangeKey].srcAmount) + BigInt(s.srcAmount)
36
36
  ).toString();
37
37
 
38
- accumulatedBalancers[exchangeKey].destAmount = (
39
- BigInt(accumulatedBalancers[exchangeKey].destAmount) +
40
- BigInt(s.destAmount)
38
+ newBalancers[exchangeKey].destAmount = (
39
+ BigInt(newBalancers[exchangeKey].destAmount) + BigInt(s.destAmount)
41
40
  ).toString();
42
41
 
43
- accumulatedBalancers[exchangeKey].percent += s.percent;
44
- accumulatedBalancers[exchangeKey].data.exchangeProxy =
45
- s.data.exchangeProxy;
46
- accumulatedBalancers[exchangeKey].data.gasUSD = (
47
- parseFloat(accumulatedBalancers[exchangeKey].data.gasUSD) +
42
+ newBalancers[exchangeKey].percent += s.percent;
43
+ newBalancers[exchangeKey].data.exchangeProxy = s.data.exchangeProxy;
44
+ newBalancers[exchangeKey].data.gasUSD = (
45
+ parseFloat(newBalancers[exchangeKey].data.gasUSD) +
48
46
  parseFloat(s.data.gasUSD)
49
47
  ).toFixed(6);
50
48
 
51
- accumulatedBalancers[exchangeKey].data.swaps.push({
49
+ newBalancers[exchangeKey].data.swaps.push({
52
50
  poolId: s.data.poolId,
53
51
  amount: side === SwapSide.SELL ? s.srcAmount : s.destAmount,
54
52
  });
55
- accumulatedBalancers[exchangeKey].poolAddresses!.push(
56
- s.poolAddresses![0],
57
- );
53
+ newBalancers[exchangeKey].poolAddresses!.push(s.poolAddresses![0]);
58
54
  } else {
59
55
  optimizedSwap.push(s);
60
56
  }
@@ -63,23 +59,12 @@ export function balancerV2Merge(or: UnoptimizedRate): UnoptimizedRate {
63
59
  return optimizedSwap;
64
60
  };
65
61
 
66
- or.bestRoute = or.bestRoute.map(r => {
67
- const accumulatedBalancers: {
68
- [key: string]: OptimalSwapExchange<any>;
69
- } = {};
70
- return {
71
- ...r,
72
- swaps: r.swaps
73
- .map(s => ({
74
- ...s,
75
- swapExchanges: fixSwap(
76
- s.swapExchanges,
77
- accumulatedBalancers,
78
- or.side,
79
- ),
80
- }))
81
- .filter(s => s.swapExchanges.length > 0),
82
- };
83
- });
62
+ or.bestRoute = or.bestRoute.map(r => ({
63
+ ...r,
64
+ swaps: r.swaps.map(s => ({
65
+ ...s,
66
+ swapExchanges: fixSwap(s.swapExchanges, or.side),
67
+ })),
68
+ }));
84
69
  return or;
85
70
  }
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
+ }