@paraswap/dex-lib 2.42.16 → 2.42.17-wombat.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 (64) hide show
  1. package/.vscode/launch.json +39 -0
  2. package/.vscode/settings.json +2 -0
  3. package/.vscode/tasks.json +15 -0
  4. package/build/abi/algebra/SwapRouter.json +281 -0
  5. package/build/abi/wombat/asset.json +769 -0
  6. package/build/abi/wombat/bmw.json +1247 -0
  7. package/build/abi/wombat/pool.json +1364 -0
  8. package/build/config.js +7 -6
  9. package/build/config.js.map +1 -1
  10. package/build/dex/index.js +2 -0
  11. package/build/dex/index.js.map +1 -1
  12. package/build/dex/swaap-v2/stable-coins.d.ts +5 -0
  13. package/build/dex/swaap-v2/stable-coins.js +26 -0
  14. package/build/dex/swaap-v2/stable-coins.js.map +1 -0
  15. package/build/dex/wombat/config.d.ts +4 -0
  16. package/build/dex/wombat/config.js +77 -0
  17. package/build/dex/wombat/config.js.map +1 -0
  18. package/build/dex/wombat/types.d.ts +33 -0
  19. package/build/dex/wombat/types.js +3 -0
  20. package/build/dex/wombat/types.js.map +1 -0
  21. package/build/dex/wombat/utils.d.ts +9 -0
  22. package/build/dex/wombat/utils.js +66 -0
  23. package/build/dex/wombat/utils.js.map +1 -0
  24. package/build/dex/wombat/wombat-bmw.d.ts +40 -0
  25. package/build/dex/wombat/wombat-bmw.js +147 -0
  26. package/build/dex/wombat/wombat-bmw.js.map +1 -0
  27. package/build/dex/wombat/wombat-pool.d.ts +22 -0
  28. package/build/dex/wombat/wombat-pool.js +161 -0
  29. package/build/dex/wombat/wombat-pool.js.map +1 -0
  30. package/build/dex/wombat/wombat-quoter.d.ts +16 -0
  31. package/build/dex/wombat/wombat-quoter.js +176 -0
  32. package/build/dex/wombat/wombat-quoter.js.map +1 -0
  33. package/build/dex/wombat/wombat.d.ts +55 -0
  34. package/build/dex/wombat/wombat.js +290 -0
  35. package/build/dex/wombat/wombat.js.map +1 -0
  36. package/build/rebase-tokens.json +17 -0
  37. package/package.json +1 -1
  38. package/src/abi/wombat/asset.json +769 -0
  39. package/src/abi/wombat/bmw.json +1247 -0
  40. package/src/abi/wombat/pool.json +1364 -0
  41. package/src/config.ts +7 -7
  42. package/src/dex/index.ts +2 -0
  43. package/src/dex/wombat/config.ts +77 -0
  44. package/src/dex/wombat/types.ts +55 -0
  45. package/src/dex/wombat/utils.ts +62 -0
  46. package/src/dex/wombat/wombat-bmw.ts +209 -0
  47. package/src/dex/wombat/wombat-e2e.test.ts +264 -0
  48. package/src/dex/wombat/wombat-integration.test.ts +258 -0
  49. package/src/dex/wombat/wombat-pool.ts +231 -0
  50. package/src/dex/wombat/wombat-quoter.ts +269 -0
  51. package/src/dex/wombat/wombat.ts +426 -0
  52. package/tests/constants-e2e.ts +20 -4
  53. package/tests/utils-e2e.ts +2 -1
  54. package/build/abi/algebra/AlgebraPool.abi.json +0 -727
  55. package/build/abi/uniswap-v3/UniswapV3Quoter.abi.json +0 -193
  56. package/build/dex/algebra/algebra-pool.d.ts +0 -46
  57. package/build/dex/algebra/algebra-pool.js +0 -336
  58. package/build/dex/algebra/algebra-pool.js.map +0 -1
  59. package/build/dex/quickswap/camelot-v3.d.ts +0 -6
  60. package/build/dex/quickswap/camelot-v3.js +0 -19
  61. package/build/dex/quickswap/camelot-v3.js.map +0 -1
  62. package/build/dex/quickswap/zyberswap-v3.d.ts +0 -6
  63. package/build/dex/quickswap/zyberswap-v3.js +0 -19
  64. package/build/dex/quickswap/zyberswap-v3.js.map +0 -1
package/src/config.ts CHANGED
@@ -51,7 +51,7 @@ const baseConfigs: { [network: number]: BaseConfig } = {
51
51
  adapterAddresses: {
52
52
  Adapter01: '0x9bE264469eF954c139Da4A45Cf76CbCC5e3A6A73',
53
53
  Adapter02: '0xFC2Ba6E830a04C25e207B8214b26d8C713F6881F',
54
- Adapter03: '0xfb2a3de6c7B8c77b520E3da16021f3D8A4E93168',
54
+ Adapter03: '0xBAEeb4540f59d30E567a5B563CC0c4587eDd9366',
55
55
  Adapter04: '0x654dE10890f8B2C5bF54E50Af169a7E93165C416',
56
56
  BuyAdapter: '0x1310dE2C69e9753bee19B5522bad39c5f788efd9',
57
57
  BuyAdapter02: '0xA10c9a84E72d9DfF424Fe2284B6460784bed407E',
@@ -165,7 +165,8 @@ const baseConfigs: { [network: number]: BaseConfig } = {
165
165
  process.env[`HASHFLOW_DISABLED_MMS_56`]?.split(',') || [],
166
166
  adapterAddresses: {
167
167
  BscAdapter01: '0xA31d9C571DF00e0F428B0bD24c34D103E8112222',
168
- BscAdapter02: '0xEECA9223063bD13e8ca77ed9e39a07f2BD1923E6',
168
+ // BscAdapter02: '0xEECA9223063bD13e8ca77ed9e39a07f2BD1923E6',
169
+ BscAdapter02: '0x84bEF12C9931cE12662cc9F2366b6a5029E4BD29',
169
170
  BscBuyAdapter: '0x301c2813e3ceb43A448a12f21551EDBcdC37F157',
170
171
  },
171
172
  rpcPollingMaxAllowedStateDelayInBlocks: 1,
@@ -224,7 +225,7 @@ const baseConfigs: { [network: number]: BaseConfig } = {
224
225
  dexalotAuthToken: process.env.API_KEY_DEXALOT_AUTH_TOKEN || '',
225
226
  adapterAddresses: {
226
227
  AvalancheAdapter01: '0x745Ec73855CeC7249E5fF4c9DD81cc65b4D297a9',
227
- AvalancheAdapter02: '0x2cdB0cDc2a9321ac2ED5b741828a5216C265Be80',
228
+ AvalancheAdapter02: '0xA10c9a84E72d9DfF424Fe2284B6460784bed407E',
228
229
  AvalancheBuyAdapter: '0x9Aa41A24A10af2a965A6D406b913a7Cd9C6886ea',
229
230
  },
230
231
  uniswapV2ExchangeRouterAddress:
@@ -281,7 +282,7 @@ const baseConfigs: { [network: number]: BaseConfig } = {
281
282
  process.env[`HASHFLOW_DISABLED_MMS_42161`]?.split(',') || [],
282
283
  adapterAddresses: {
283
284
  ArbitrumAdapter01: '0xD8134ACfc9c71Ab51452b5bA23A31354F4739032',
284
- ArbitrumAdapter02: '0x248009f6F78b5AcD5EA2fc86d56b77A7AeEe2eFc',
285
+ ArbitrumAdapter02: '0xe9166234DFB6d3ec05C82404109C02Ca82b16c22',
285
286
  ArbitrumBuyAdapter: '0xEECA9223063bD13e8ca77ed9e39a07f2BD1923E6',
286
287
  },
287
288
  uniswapV2ExchangeRouterAddress:
@@ -308,9 +309,8 @@ const baseConfigs: { [network: number]: BaseConfig } = {
308
309
  hashFlowAuthToken: process.env.API_KEY_HASHFLOW_AUTH_TOKEN || '',
309
310
  hashFlowDisabledMMs:
310
311
  process.env[`HASHFLOW_DISABLED_MMS_10`]?.split(',') || [],
311
-
312
312
  adapterAddresses: {
313
- OptimismAdapter01: '0x3ad7f275E27AC579cA88e0b4765828242A9E8C49',
313
+ OptimismAdapter01: '0x5dcf544b0c9689fa67dcb713fd2656d217e25a59',
314
314
  OptimismBuyAdapter: '0xfdDD975FE4c1af20c24A3Ad2b33e8609a62DDC73',
315
315
  },
316
316
  uniswapV2ExchangeRouterAddress:
@@ -366,7 +366,7 @@ const baseConfigs: { [network: number]: BaseConfig } = {
366
366
  hashFlowAuthToken: process.env.API_KEY_HASHFLOW_AUTH_TOKEN || '',
367
367
  hashFlowDisabledMMs: [],
368
368
  adapterAddresses: {
369
- BaseAdapter01: '0x30F6B9b6485ff0B67E881f5ac80D3F1c70A4B23d',
369
+ BaseAdapter01: '0x654dE10890f8B2C5bF54E50Af169a7E93165C416',
370
370
  BaseBuyAdapter: '0xB11bCA7B01b425afD0743A4D77B4f593883f94C0',
371
371
  },
372
372
  uniswapV2ExchangeRouterAddress:
package/src/dex/index.ts CHANGED
@@ -38,6 +38,7 @@ import { Excalibur } from './uniswap-v2/excalibur';
38
38
  import { MakerPsm } from './maker-psm/maker-psm';
39
39
  import { KyberDmm } from './kyberdmm/kyberdmm';
40
40
  import { Platypus } from './platypus/platypus';
41
+ import { Wombat } from './wombat/wombat';
41
42
  import { GMX } from './gmx/gmx';
42
43
  import { WooFiV2 } from './woo-fi-v2/woo-fi-v2';
43
44
  import { ParaSwapLimitOrders } from './paraswap-limit-orders/paraswap-limit-orders';
@@ -157,6 +158,7 @@ const Dexes = [
157
158
  SwaapV2,
158
159
  QuickPerps,
159
160
  NomiswapV2,
161
+ Wombat,
160
162
  ];
161
163
 
162
164
  export type LegacyDexConstructor = new (dexHelper: IDexHelper) => IDexTxBuilder<
@@ -0,0 +1,77 @@
1
+ import { DexParams } from './types';
2
+ import { DexConfigMap, AdapterMappings } from '../../types';
3
+ import { Network, SwapSide } from '../../constants';
4
+
5
+ export const WombatConfig: DexConfigMap<DexParams> = {
6
+ Wombat: {
7
+ [Network.BSC]: {
8
+ bmwAddress: '0x489833311676B566f888119c29bd997Dc6C95830',
9
+ },
10
+ [Network.ARBITRUM]: {
11
+ bmwAddress: '0x62A83C6791A3d7950D823BB71a38e47252b6b6F4',
12
+ },
13
+ [Network.MAINNET]: {
14
+ bmwAddress: '0xC9bFC3eFeFe4CF96877009F75a61F5c1937e5d1a',
15
+ },
16
+ [Network.AVALANCHE]: {
17
+ bmwAddress: '0x6521a549834F5E6d253CD2e5F4fbe4048f86cd7b',
18
+ },
19
+ [Network.BASE]: {
20
+ bmwAddress: '0x6521a549834F5E6d253CD2e5F4fbe4048f86cd7b',
21
+ },
22
+ [Network.OPTIMISM]: {
23
+ bmwAddress: '0x25C9dd8a3774EF7C918cd28ff59cF9e29504C914',
24
+ },
25
+ },
26
+ };
27
+
28
+ export const Adapters: Record<number, AdapterMappings> = {
29
+ [Network.BSC]: {
30
+ [SwapSide.SELL]: [
31
+ {
32
+ name: 'BscAdapter02',
33
+ index: 7,
34
+ },
35
+ ],
36
+ },
37
+ [Network.ARBITRUM]: {
38
+ [SwapSide.SELL]: [
39
+ {
40
+ name: 'ArbitrumAdapter02',
41
+ index: 8,
42
+ },
43
+ ],
44
+ },
45
+ [Network.MAINNET]: {
46
+ [SwapSide.SELL]: [
47
+ {
48
+ name: 'Adapter03',
49
+ index: 15,
50
+ },
51
+ ],
52
+ },
53
+ [Network.AVALANCHE]: {
54
+ [SwapSide.SELL]: [
55
+ {
56
+ name: 'AvalancheAdapter02',
57
+ index: 7,
58
+ },
59
+ ],
60
+ },
61
+ [Network.BASE]: {
62
+ [SwapSide.SELL]: [
63
+ {
64
+ name: 'BaseAdapter01',
65
+ index: 7,
66
+ },
67
+ ],
68
+ },
69
+ [Network.OPTIMISM]: {
70
+ [SwapSide.SELL]: [
71
+ {
72
+ name: 'OptimismAdapter01',
73
+ index: 12,
74
+ },
75
+ ],
76
+ },
77
+ };
@@ -0,0 +1,55 @@
1
+ import { Address } from '../../types';
2
+
3
+ // State-related types
4
+
5
+ export type BmwState = {
6
+ pools: Address[];
7
+ };
8
+
9
+ export type PoolState = {
10
+ // poolState is the state of event
11
+ // subscriber. This should be the minimum
12
+ // set of parameters required to compute
13
+ // pool prices.
14
+ params: PoolParams;
15
+ underlyingAddresses: Address[];
16
+ asset: { [underlyingAddress: string]: AssetState };
17
+ };
18
+
19
+ export type AssetState = {
20
+ address: Address;
21
+ paused: boolean;
22
+ cash: bigint;
23
+ liability: bigint;
24
+ underlyingTokenDecimals: number;
25
+ relativePrice?: bigint;
26
+ };
27
+
28
+ export type PoolParams = {
29
+ paused: boolean;
30
+ ampFactor: bigint;
31
+ haircutRate: bigint;
32
+ startCovRatio: bigint;
33
+ endCovRatio: bigint;
34
+ };
35
+
36
+ export type WombatData = {
37
+ // TODO: WombatData is the dex data that is
38
+ // returned by the API that can be used for
39
+ // tx building. The data structure should be minimal.
40
+ // Complete me!
41
+ exchange: Address;
42
+ };
43
+
44
+ export type DexParams = {
45
+ // DexParams is set of parameters that can be used to initiate a DEX fork.
46
+ bmwAddress: Address;
47
+ // routerAddress?: Address;
48
+ };
49
+
50
+ export type MulticallResultOutputs =
51
+ | boolean
52
+ | bigint
53
+ | Address
54
+ | number
55
+ | undefined;
@@ -0,0 +1,62 @@
1
+ import { BI_POWS } from '../../bigint-constants';
2
+ import { MultiResult } from '../../lib/multi-wrapper';
3
+ import { BytesLike } from 'ethers/lib/utils';
4
+ import { generalDecoder } from '../../lib/decoders';
5
+
6
+ export const WAD = BI_POWS[18];
7
+
8
+ export function wmul(x: bigint, y: bigint): bigint {
9
+ return (x * y + WAD / 2n) / WAD;
10
+ }
11
+
12
+ export function wdiv(x: bigint, y: bigint): bigint {
13
+ return (x * WAD + y / 2n) / y;
14
+ }
15
+
16
+ // Convert x to WAD (18 decimals) from d decimals.
17
+ export function toWad(x: bigint, d: bigint): bigint {
18
+ if (d < 18n) {
19
+ return x * 10n ** (18n - d);
20
+ } else if (d > 18n) {
21
+ return x / 10n ** (d - 18n);
22
+ }
23
+ return x;
24
+ }
25
+ // Convert x from WAD (18 decimals) to d decimals.
26
+ export function fromWad(x: bigint, d: bigint): bigint {
27
+ if (d < 18n) {
28
+ return x / 10n ** (18n - d);
29
+ } else if (d > 18n) {
30
+ return x * 10n ** (d - 18n);
31
+ }
32
+ return x;
33
+ }
34
+
35
+ // Babylonian Method with initial guess (typecast as int)
36
+ export function sqrt(y: bigint, guess: bigint): bigint {
37
+ let z = 0n;
38
+ if (y > 3) {
39
+ if (guess > 0 && guess <= y) {
40
+ z = guess;
41
+ } else if (guess < 0 && -guess <= y) {
42
+ z = -guess;
43
+ } else {
44
+ z = y;
45
+ }
46
+ let x = (y / z + z) / 2n;
47
+ while (x != z) {
48
+ z = x;
49
+ x = (y / x + x) / 2n;
50
+ }
51
+ } else if (y != 0n) {
52
+ z = 1n;
53
+ }
54
+
55
+ return z;
56
+ }
57
+
58
+ export const uint120ToBigInt = (
59
+ result: MultiResult<BytesLike> | BytesLike,
60
+ ): bigint => {
61
+ return generalDecoder(result, ['uint120'], 0n, value => value[0].toBigInt());
62
+ };
@@ -0,0 +1,209 @@
1
+ import { Interface } from '@ethersproject/abi';
2
+ import { BytesLike } from 'ethers';
3
+ import { LogDescription } from 'ethers/lib/utils';
4
+ import { Contract } from 'web3-eth-contract';
5
+ import { DeepReadonly } from 'ts-essentials';
6
+ import { Address, Log, Logger, MultiCallInput } from '../../types';
7
+ import { catchParseLogError } from '../../utils';
8
+ import { StatefulEventSubscriber } from '../../stateful-event-subscriber';
9
+ import { IDexHelper } from '../../dex-helper';
10
+ import { BmwState } from './types';
11
+ import BmwABI from '../../abi/wombat/bmw.json';
12
+ import AssetABI from '../../abi/wombat/asset.json';
13
+ import PoolABI from '../../abi/wombat/pool.json';
14
+
15
+ export class WombatBmw extends StatefulEventSubscriber<BmwState> {
16
+ static readonly bmwInterface = new Interface(BmwABI);
17
+ static readonly assetInterface = new Interface(AssetABI);
18
+ static readonly poolInterface = new Interface(PoolABI);
19
+
20
+ private readonly logDecoder: (log: Log) => any;
21
+ private bmwContract: Contract;
22
+ private handlers: {
23
+ [event: string]: (
24
+ event: any,
25
+ state: DeepReadonly<BmwState>,
26
+ log: Readonly<Log>,
27
+ ) => Promise<DeepReadonly<BmwState> | null>;
28
+ } = {};
29
+
30
+ constructor(
31
+ dexKey: string,
32
+ name: string,
33
+ protected network: number,
34
+ protected dexHelper: IDexHelper,
35
+ logger: Logger,
36
+ protected bmwAddress: Address,
37
+ protected onAssetAdded: (
38
+ pool: Address,
39
+ asset2TokenMap: Map<Address, Address>,
40
+ blockNumber: number,
41
+ ) => Promise<void>,
42
+ ) {
43
+ super(
44
+ `${dexKey} ${name}`,
45
+ `${dexKey}-${network} ${name}`,
46
+ dexHelper,
47
+ logger,
48
+ );
49
+
50
+ this.logDecoder = (log: Log) => WombatBmw.bmwInterface.parseLog(log);
51
+ this.addressesSubscribed = [this.bmwAddress];
52
+ this.bmwContract = new this.dexHelper.web3Provider.eth.Contract(
53
+ BmwABI as any,
54
+ this.bmwAddress,
55
+ );
56
+
57
+ // users-actions handlers
58
+ this.handlers['Add'] = this.handleAdd.bind(this);
59
+ }
60
+
61
+ /**
62
+ * The function is called every time any of the subscribed
63
+ * addresses release log. The function accepts the current
64
+ * state, updates the state according to the log, and returns
65
+ * the updated state.
66
+ * @param state - Current state of event subscriber
67
+ * @param log - Log released by one of the subscribed addresses
68
+ * @returns Updates state of the event subscriber after the log
69
+ */
70
+ protected async processLog(
71
+ state: DeepReadonly<BmwState>,
72
+ log: Readonly<Log>,
73
+ ): Promise<DeepReadonly<BmwState> | null> {
74
+ try {
75
+ const event = this.logDecoder(log);
76
+ if (event.name in this.handlers) {
77
+ return await this.handlers[event.name](event, state, log);
78
+ }
79
+ } catch (e) {
80
+ catchParseLogError(e, this.logger);
81
+ }
82
+
83
+ return null;
84
+ }
85
+
86
+ /**
87
+ * The function generates state using on-chain calls. This
88
+ * function is called to regenerate state if the event based
89
+ * system fails to fetch events and the local state is no
90
+ * more correct.
91
+ * should be generated
92
+ * @returns state of the event subscriber at blocknumber
93
+ */
94
+ async generateState(blockNumber: number): Promise<DeepReadonly<BmwState>> {
95
+ const bmwState: BmwState = { pools: [] };
96
+ let inputs: MultiCallInput[] = [];
97
+
98
+ const poolLength = await this.bmwContract.methods
99
+ .poolLength()
100
+ .call({}, blockNumber);
101
+ for (let i = 0; i < poolLength; i++) {
102
+ inputs.push({
103
+ target: this.bmwAddress,
104
+ callData: WombatBmw.bmwInterface.encodeFunctionData('poolInfo', [i]),
105
+ });
106
+ }
107
+ let returnData = (
108
+ await this.dexHelper.multiContract.methods
109
+ .aggregate(inputs)
110
+ .call({}, blockNumber)
111
+ ).returnData;
112
+
113
+ const lpTokens = returnData.map((data: BytesLike) =>
114
+ WombatBmw.bmwInterface
115
+ .decodeFunctionResult('poolInfo', data)[0]
116
+ .toLowerCase(),
117
+ );
118
+
119
+ inputs = [];
120
+ for (const lpToken of lpTokens) {
121
+ inputs.push({
122
+ target: lpToken,
123
+ callData:
124
+ WombatBmw.assetInterface.encodeFunctionData('underlyingToken'),
125
+ });
126
+ inputs.push({
127
+ target: lpToken,
128
+ callData: WombatBmw.assetInterface.encodeFunctionData('pool'),
129
+ });
130
+ }
131
+
132
+ returnData = (
133
+ await this.dexHelper.multiContract.methods
134
+ .aggregate(inputs)
135
+ .call({}, blockNumber)
136
+ ).returnData;
137
+
138
+ const pool2AssetInfo = new Map<Address, Map<Address, Address>>();
139
+ let i = 0;
140
+ for (const lpToken of lpTokens) {
141
+ const underlyingToken = WombatBmw.assetInterface
142
+ .decodeFunctionResult('underlyingToken', returnData[i++])[0]
143
+ .toLowerCase();
144
+ const pool = WombatBmw.assetInterface
145
+ .decodeFunctionResult('pool', returnData[i++])[0]
146
+ .toLowerCase();
147
+
148
+ if (!pool2AssetInfo.has(pool)) {
149
+ pool2AssetInfo.set(pool, new Map<Address, Address>());
150
+ }
151
+ pool2AssetInfo.get(pool)!.set(lpToken, underlyingToken);
152
+ }
153
+
154
+ const promises: Promise<void>[] = [];
155
+ pool2AssetInfo.forEach((asset2TokenMap, pool) => {
156
+ bmwState.pools.push(pool);
157
+ promises.push(this.onAssetAdded(pool, asset2TokenMap, blockNumber));
158
+ });
159
+ await Promise.all(promises);
160
+
161
+ return bmwState;
162
+ }
163
+
164
+ async handleAdd(
165
+ event: LogDescription,
166
+ state: DeepReadonly<BmwState>,
167
+ log: Readonly<Log>,
168
+ ): Promise<DeepReadonly<BmwState> | null> {
169
+ const lpToken = event.args.lpToken.toString().toLowerCase();
170
+ let multiCallInputs: MultiCallInput[] = [];
171
+
172
+ multiCallInputs.push({
173
+ target: lpToken,
174
+ callData: WombatBmw.assetInterface.encodeFunctionData('pool'),
175
+ });
176
+ multiCallInputs.push({
177
+ target: lpToken,
178
+ callData: WombatBmw.assetInterface.encodeFunctionData('underlyingToken'),
179
+ });
180
+
181
+ const returnData = (
182
+ await this.dexHelper.multiContract.methods
183
+ .aggregate(multiCallInputs)
184
+ .call({}, log.blockNumber)
185
+ ).returnData;
186
+
187
+ const pool = WombatBmw.assetInterface
188
+ .decodeFunctionResult('pool', returnData[0])[0]
189
+ .toLowerCase();
190
+ const underlyingToken = WombatBmw.assetInterface
191
+ .decodeFunctionResult('underlyingToken', returnData[1])[0]
192
+ .toLowerCase();
193
+
194
+ await this.onAssetAdded(
195
+ pool,
196
+ new Map<Address, Address>().set(lpToken, underlyingToken),
197
+ log.blockNumber,
198
+ );
199
+ if (!state.pools.includes(pool)) {
200
+ return {
201
+ pools: [...state.pools, pool],
202
+ };
203
+ } else {
204
+ return {
205
+ pools: state.pools,
206
+ };
207
+ }
208
+ }
209
+ }