@paraswap/dex-lib 2.18.5 → 2.18.6-swaap-v2
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.
- package/.idea/aws.xml +17 -0
- package/.idea/codeStyles/Project.xml +19 -0
- package/.idea/codeStyles/codeStyleConfig.xml +5 -0
- package/.idea/misc.xml +6 -0
- package/.idea/modules.xml +8 -0
- package/.idea/paraswap-dex-lib.iml +9 -0
- package/.idea/vcs.xml +6 -0
- package/build/abi/swaap-v2/vault.json +918 -0
- package/build/config.js +4 -2
- package/build/config.js.map +1 -1
- package/build/dex/hashflow/hashflow.js +52 -68
- package/build/dex/hashflow/hashflow.js.map +1 -1
- package/build/dex/index.js +2 -0
- package/build/dex/index.js.map +1 -1
- package/build/dex/pancakeswap-v3/types.d.ts +14 -0
- package/build/dex/{test-dex → pancakeswap-v3}/types.js.map +1 -1
- package/build/dex/{test-dex → swaap-v2}/config.d.ts +1 -1
- package/build/dex/swaap-v2/config.js +25 -0
- package/build/dex/swaap-v2/config.js.map +1 -0
- package/build/dex/swaap-v2/constants.d.ts +9 -0
- package/build/dex/swaap-v2/constants.js +13 -0
- package/build/dex/swaap-v2/constants.js.map +1 -0
- package/build/dex/swaap-v2/rate-fetcher.d.ts +16 -0
- package/build/dex/swaap-v2/rate-fetcher.js +100 -0
- package/build/dex/swaap-v2/rate-fetcher.js.map +1 -0
- package/build/dex/swaap-v2/swaap-v2.d.ts +55 -0
- package/build/dex/swaap-v2/swaap-v2.js +465 -0
- package/build/dex/swaap-v2/swaap-v2.js.map +1 -0
- package/build/dex/swaap-v2/types.d.ts +64 -0
- package/build/dex/swaap-v2/types.js +7 -0
- package/build/dex/swaap-v2/types.js.map +1 -0
- package/build/dex/swaap-v2/utils.d.ts +7 -0
- package/build/dex/swaap-v2/utils.js +34 -0
- package/build/dex/swaap-v2/utils.js.map +1 -0
- package/build/dex/swaap-v2/validators.d.ts +5 -0
- package/build/dex/swaap-v2/validators.js +120 -0
- package/build/dex/swaap-v2/validators.js.map +1 -0
- package/build/types.d.ts +1 -0
- package/package.json +1 -1
- package/src/abi/swaap-v2/vault.json +918 -0
- package/src/config.ts +5 -2
- package/src/dex/hashflow/hashflow.ts +85 -110
- package/src/dex/index.ts +2 -0
- package/src/dex/swaap-v2/config.ts +25 -0
- package/src/dex/swaap-v2/constants.ts +14 -0
- package/src/dex/swaap-v2/rate-fetcher.ts +164 -0
- package/src/dex/swaap-v2/swaap-v2-e2e.test.ts +199 -0
- package/src/dex/swaap-v2/swaap-v2-integration.test.ts +142 -0
- package/src/dex/swaap-v2/swaap-v2.ts +714 -0
- package/src/dex/swaap-v2/types.ts +74 -0
- package/src/dex/swaap-v2/utils.ts +44 -0
- package/src/dex/swaap-v2/validators.ts +123 -0
- package/src/types.ts +1 -0
- package/tests/constants-e2e.ts +1 -1
- package/tests/utils-e2e.ts +5 -0
- package/build/abi/DirectSwap.json +0 -1018
- package/build/dex/test-dex/config.js +0 -15
- package/build/dex/test-dex/config.js.map +0 -1
- package/build/dex/test-dex/test-dex-pool.d.ts +0 -39
- package/build/dex/test-dex/test-dex-pool.js +0 -63
- package/build/dex/test-dex/test-dex-pool.js.map +0 -1
- package/build/dex/test-dex/test-dex.d.ts +0 -37
- package/build/dex/test-dex/test-dex.js +0 -126
- package/build/dex/test-dex/test-dex.js.map +0 -1
- package/build/dex/test-dex/types.d.ts +0 -6
- package/build/dex/woo-fi-v2/woo-fi-math.d.ts +0 -16
- package/build/dex/woo-fi-v2/woo-fi-math.js +0 -137
- package/build/dex/woo-fi-v2/woo-fi-math.js.map +0 -1
- /package/build/dex/{test-dex → pancakeswap-v3}/types.js +0 -0
package/src/config.ts
CHANGED
|
@@ -29,6 +29,7 @@ type BaseConfig = {
|
|
|
29
29
|
rpcPollingBlocksBackToTriggerUpdate: number;
|
|
30
30
|
hashFlowAuthToken?: string;
|
|
31
31
|
hashFlowDisabledMMs: string[];
|
|
32
|
+
swaapV2AuthToken?: string;
|
|
32
33
|
};
|
|
33
34
|
|
|
34
35
|
const baseConfigs: { [network: number]: BaseConfig } = {
|
|
@@ -185,8 +186,8 @@ const baseConfigs: { [network: number]: BaseConfig } = {
|
|
|
185
186
|
process.env[`HASHFLOW_DISABLED_MMS_137`]?.split(',') || [],
|
|
186
187
|
adapterAddresses: {
|
|
187
188
|
PolygonAdapter01: '0xE44769f42E1e9592f86B82f206407a8f7C84b4ed',
|
|
188
|
-
PolygonAdapter02: '
|
|
189
|
-
PolygonBuyAdapter: '
|
|
189
|
+
PolygonAdapter02: '0xa05d8C3F278fC7b20b39Ea7A3035E3aD8D808c78',
|
|
190
|
+
PolygonBuyAdapter: '0xB11bCA7B01b425afD0743A4D77B4f593883f94C0',
|
|
190
191
|
},
|
|
191
192
|
uniswapV2ExchangeRouterAddress:
|
|
192
193
|
'0xf3938337F7294fEf84e9B2c6D548A93F956Cc281',
|
|
@@ -194,6 +195,7 @@ const baseConfigs: { [network: number]: BaseConfig } = {
|
|
|
194
195
|
rfqConfigs: {},
|
|
195
196
|
rpcPollingMaxAllowedStateDelayInBlocks: 2,
|
|
196
197
|
rpcPollingBlocksBackToTriggerUpdate: 1,
|
|
198
|
+
swaapV2AuthToken: process.env.API_KEY_SWAAP_V2_AUTH_TOKEN || '',
|
|
197
199
|
},
|
|
198
200
|
[Network.AVALANCHE]: {
|
|
199
201
|
network: Network.AVALANCHE,
|
|
@@ -345,6 +347,7 @@ export function generateConfig(network: number): Config {
|
|
|
345
347
|
rpcPollingBlocksBackToTriggerUpdate:
|
|
346
348
|
baseConfig.rpcPollingBlocksBackToTriggerUpdate,
|
|
347
349
|
hashFlowAuthToken: baseConfig.hashFlowAuthToken,
|
|
350
|
+
swaapV2AuthToken: baseConfig.swaapV2AuthToken,
|
|
348
351
|
hashFlowDisabledMMs: baseConfig.hashFlowDisabledMMs,
|
|
349
352
|
};
|
|
350
353
|
}
|
|
@@ -279,14 +279,15 @@ export class Hashflow extends SimpleExchange implements IDex<HashflowData> {
|
|
|
279
279
|
destToken: Token,
|
|
280
280
|
side: SwapSide,
|
|
281
281
|
): bigint[] {
|
|
282
|
-
|
|
282
|
+
if (levels.length > 0) {
|
|
283
|
+
const firstLevel = levels[0];
|
|
284
|
+
if (new BigNumber(firstLevel.level).gt(0)) {
|
|
285
|
+
// Add zero level for price computation
|
|
286
|
+
levels.unshift({ level: '0', price: firstLevel.price });
|
|
287
|
+
}
|
|
288
|
+
}
|
|
283
289
|
|
|
284
290
|
const outputs = new Array<BigNumber>(amounts.length).fill(BN_0);
|
|
285
|
-
// FIXME: There is still case when last amount is fillable, but in between
|
|
286
|
-
// we may have splits that are less than min. amount. I assume that case is very
|
|
287
|
-
// and not addressing in current fix. If someone will look into that case, just be aware
|
|
288
|
-
// that it is not addressed
|
|
289
|
-
|
|
290
291
|
for (const [i, amount] of amounts.entries()) {
|
|
291
292
|
if (amount.isZero()) {
|
|
292
293
|
outputs[i] = BN_0;
|
|
@@ -418,124 +419,98 @@ export class Hashflow extends SimpleExchange implements IDex<HashflowData> {
|
|
|
418
419
|
blockNumber: number,
|
|
419
420
|
limitPools?: string[],
|
|
420
421
|
): Promise<null | ExchangePrices<HashflowData>> {
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
const normalizedDestToken = this.normalizeToken(destToken);
|
|
424
|
-
|
|
425
|
-
if (normalizedSrcToken.address === normalizedDestToken.address) {
|
|
426
|
-
return null;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
const prefix = this.getIdentifierPrefix(
|
|
430
|
-
normalizedSrcToken.address,
|
|
431
|
-
normalizedDestToken.address,
|
|
432
|
-
);
|
|
433
|
-
|
|
434
|
-
const pools =
|
|
435
|
-
limitPools ??
|
|
436
|
-
(await this.getPoolIdentifiers(srcToken, destToken, side, blockNumber));
|
|
437
|
-
|
|
438
|
-
const marketMakersToUse = pools.map(p => p.split(`${prefix}_`).pop());
|
|
422
|
+
const normalizedSrcToken = this.normalizeToken(srcToken);
|
|
423
|
+
const normalizedDestToken = this.normalizeToken(destToken);
|
|
439
424
|
|
|
440
|
-
|
|
425
|
+
if (normalizedSrcToken.address === normalizedDestToken.address) {
|
|
426
|
+
return null;
|
|
427
|
+
}
|
|
441
428
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
});
|
|
429
|
+
const prefix = this.getIdentifierPrefix(
|
|
430
|
+
normalizedSrcToken.address,
|
|
431
|
+
normalizedDestToken.address,
|
|
432
|
+
);
|
|
447
433
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
}[] = Object.keys(levelsMap)
|
|
452
|
-
.map(mm => {
|
|
453
|
-
const entry = levelsMap[mm]?.find(
|
|
454
|
-
e =>
|
|
455
|
-
`${e.pair.baseToken}_${e.pair.quoteToken}` ===
|
|
456
|
-
this.getPairName(
|
|
457
|
-
normalizedSrcToken.address,
|
|
458
|
-
normalizedDestToken.address,
|
|
459
|
-
),
|
|
460
|
-
);
|
|
461
|
-
if (entry === undefined) {
|
|
462
|
-
return undefined;
|
|
463
|
-
} else {
|
|
464
|
-
return { mm, levels: entry.levels };
|
|
465
|
-
}
|
|
466
|
-
})
|
|
467
|
-
.filter(o => o !== undefined)
|
|
468
|
-
.map(o => o!);
|
|
434
|
+
const pools =
|
|
435
|
+
limitPools ??
|
|
436
|
+
(await this.getPoolIdentifiers(srcToken, destToken, side, blockNumber));
|
|
469
437
|
|
|
470
|
-
|
|
471
|
-
const { mm, levels } = lEntry;
|
|
438
|
+
const marketMakersToUse = pools.map(p => p.split(`${prefix}_`).pop());
|
|
472
439
|
|
|
473
|
-
|
|
474
|
-
return null;
|
|
475
|
-
}
|
|
440
|
+
const levelsMap = (await this.getCachedLevels()) || {};
|
|
476
441
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
442
|
+
Object.keys(levelsMap).forEach(mmKey => {
|
|
443
|
+
if (!marketMakersToUse.includes(mmKey)) {
|
|
444
|
+
delete levelsMap[mmKey];
|
|
445
|
+
}
|
|
446
|
+
});
|
|
482
447
|
|
|
483
|
-
|
|
484
|
-
|
|
448
|
+
const levelEntries: {
|
|
449
|
+
mm: string;
|
|
450
|
+
levels: PriceLevel[];
|
|
451
|
+
}[] = Object.keys(levelsMap)
|
|
452
|
+
.map(mm => {
|
|
453
|
+
const entry = levelsMap[mm]?.find(
|
|
454
|
+
e =>
|
|
455
|
+
`${e.pair.baseToken}_${e.pair.quoteToken}` ===
|
|
456
|
+
this.getPairName(
|
|
457
|
+
normalizedSrcToken.address,
|
|
458
|
+
normalizedDestToken.address,
|
|
459
|
+
),
|
|
485
460
|
);
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
return null;
|
|
461
|
+
if (entry === undefined) {
|
|
462
|
+
return undefined;
|
|
463
|
+
} else {
|
|
464
|
+
return { mm, levels: entry.levels };
|
|
491
465
|
}
|
|
466
|
+
})
|
|
467
|
+
.filter(o => o !== undefined)
|
|
468
|
+
.map(o => o!);
|
|
492
469
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
levels.unshift({ level: '0', price: firstLevelRaw.price });
|
|
496
|
-
}
|
|
470
|
+
const prices = levelEntries.map(lEntry => {
|
|
471
|
+
const { mm, levels } = lEntry;
|
|
497
472
|
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
side,
|
|
504
|
-
)[0];
|
|
505
|
-
|
|
506
|
-
const prices = this.computePricesFromLevels(
|
|
507
|
-
amountsRaw,
|
|
508
|
-
levels,
|
|
509
|
-
normalizedSrcToken,
|
|
510
|
-
normalizedDestToken,
|
|
511
|
-
side,
|
|
512
|
-
);
|
|
473
|
+
const divider = getBigNumberPow(
|
|
474
|
+
side === SwapSide.SELL
|
|
475
|
+
? normalizedSrcToken.decimals
|
|
476
|
+
: normalizedDestToken.decimals,
|
|
477
|
+
);
|
|
513
478
|
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
data: { mm },
|
|
518
|
-
prices,
|
|
519
|
-
unit: unitPrice,
|
|
520
|
-
poolIdentifier: this.getPoolIdentifier(
|
|
521
|
-
normalizedSrcToken.address,
|
|
522
|
-
normalizedDestToken.address,
|
|
523
|
-
mm,
|
|
524
|
-
),
|
|
525
|
-
poolAddresses: [this.routerAddress],
|
|
526
|
-
} as PoolPrices<HashflowData>;
|
|
527
|
-
});
|
|
479
|
+
const amountsRaw = amounts.map(a =>
|
|
480
|
+
new BigNumber(a.toString()).dividedBy(divider),
|
|
481
|
+
);
|
|
528
482
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
483
|
+
const unitPrice = this.computePricesFromLevels(
|
|
484
|
+
[BN_1],
|
|
485
|
+
levels,
|
|
486
|
+
normalizedSrcToken,
|
|
487
|
+
normalizedDestToken,
|
|
488
|
+
side,
|
|
489
|
+
)[0];
|
|
490
|
+
const prices = this.computePricesFromLevels(
|
|
491
|
+
amountsRaw,
|
|
492
|
+
levels,
|
|
493
|
+
normalizedSrcToken,
|
|
494
|
+
normalizedDestToken,
|
|
495
|
+
side,
|
|
536
496
|
);
|
|
537
|
-
|
|
538
|
-
|
|
497
|
+
|
|
498
|
+
return {
|
|
499
|
+
gasCost: 100_000,
|
|
500
|
+
exchange: this.dexKey,
|
|
501
|
+
data: { mm },
|
|
502
|
+
prices,
|
|
503
|
+
unit: unitPrice,
|
|
504
|
+
poolIdentifier: this.getPoolIdentifier(
|
|
505
|
+
normalizedSrcToken.address,
|
|
506
|
+
normalizedDestToken.address,
|
|
507
|
+
mm,
|
|
508
|
+
),
|
|
509
|
+
poolAddresses: [this.routerAddress],
|
|
510
|
+
} as PoolPrices<HashflowData>;
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
return prices;
|
|
539
514
|
}
|
|
540
515
|
|
|
541
516
|
async preProcessTransaction(
|
package/src/dex/index.ts
CHANGED
|
@@ -63,6 +63,7 @@ import { MaverickV1 } from './maverick-v1/maverick-v1';
|
|
|
63
63
|
import { QuickSwapV3 } from './quickswap/quickswap-v3';
|
|
64
64
|
import { ZyberSwapV3 } from './quickswap/zyberswap-v3';
|
|
65
65
|
import { TraderJoeV2 } from './trader-joe-v2';
|
|
66
|
+
import { SwaapV2 } from './swaap-v2/swaap-v2';
|
|
66
67
|
import { SpiritSwapV3 } from './quickswap/spiritswap-v3';
|
|
67
68
|
import { TraderJoeV21 } from './trader-joe-v2.1';
|
|
68
69
|
import { PancakeswapV3 } from './pancakeswap-v3/pancakeswap-v3';
|
|
@@ -129,6 +130,7 @@ const Dexes = [
|
|
|
129
130
|
Hashflow,
|
|
130
131
|
MaverickV1,
|
|
131
132
|
Camelot,
|
|
133
|
+
SwaapV2,
|
|
132
134
|
];
|
|
133
135
|
|
|
134
136
|
export type LegacyDexConstructor = new (dexHelper: IDexHelper) => IDexTxBuilder<
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { DexParams } from './types';
|
|
2
|
+
import { DexConfigMap, AdapterMappings } from '../../types';
|
|
3
|
+
import { Network, SwapSide } from '../../constants';
|
|
4
|
+
|
|
5
|
+
export const SwaapV2Config: DexConfigMap<DexParams> = {
|
|
6
|
+
SwaapV2: {
|
|
7
|
+
// Mainnet and Arbitrum will be supported later
|
|
8
|
+
// [Network.MAINNET]: {
|
|
9
|
+
// routerAddress: '0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa',
|
|
10
|
+
// },
|
|
11
|
+
// [Network.ARBITRUM]: {
|
|
12
|
+
// routerAddress: '0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa',
|
|
13
|
+
// },
|
|
14
|
+
[Network.POLYGON]: {
|
|
15
|
+
routerAddress: '0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const Adapters: Record<number, AdapterMappings> = {
|
|
21
|
+
[Network.POLYGON]: {
|
|
22
|
+
[SwapSide.SELL]: [{ name: 'PolygonAdapter02', index: 8 }],
|
|
23
|
+
[SwapSide.BUY]: [{ name: 'PolygonBuyAdapter', index: 7 }],
|
|
24
|
+
},
|
|
25
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const SWAAP_RFQ_PRICES_CACHES_TTL_S = 3;
|
|
2
|
+
export const SWAAP_RFQ_QUOTE_TIMEOUT_MS = 2000;
|
|
3
|
+
|
|
4
|
+
export const SWAAP_RFQ_API_PRICES_POLLING_INTERVAL_MS = 1000;
|
|
5
|
+
|
|
6
|
+
export const SWAAP_RFQ_API_URL = 'https://api.swaap.finance/v1/rfq';
|
|
7
|
+
export const SWAAP_RFQ_PRICES_ENDPOINT = 'prices';
|
|
8
|
+
export const SWAAP_RFQ_QUOTE_ENDPOINT = 'quote';
|
|
9
|
+
|
|
10
|
+
export const GAS_COST_ESTIMATION = 142_000;
|
|
11
|
+
|
|
12
|
+
export const BATCH_SWAP_SELECTOR = '0x945bcec9';
|
|
13
|
+
|
|
14
|
+
export const CALLER_SLOT = 160;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { IDexHelper } from '../../dex-helper';
|
|
2
|
+
import { Fetcher } from '../../lib/fetcher/fetcher';
|
|
3
|
+
import { validateAndCast } from '../../lib/validators';
|
|
4
|
+
import { Logger, Token, Address } from '../../types';
|
|
5
|
+
import {
|
|
6
|
+
SwaapV2RateFetcherConfig,
|
|
7
|
+
SwaapV2PriceLevelsResponse,
|
|
8
|
+
SwaapV2QuoteResponse,
|
|
9
|
+
SwaapV2PriceLevels,
|
|
10
|
+
SwaapV2QuoteRequest,
|
|
11
|
+
SwaapV2OrderType,
|
|
12
|
+
} from './types';
|
|
13
|
+
import {
|
|
14
|
+
priceLevelsResponseValidator,
|
|
15
|
+
getQuoteResponseValidator,
|
|
16
|
+
} from './validators';
|
|
17
|
+
import { getPriceLevelsCacheKey, normalizeTokenAddress } from './utils';
|
|
18
|
+
import { SWAAP_RFQ_QUOTE_TIMEOUT_MS } from './constants';
|
|
19
|
+
import BigNumber from 'bignumber.js';
|
|
20
|
+
import { RequestConfig } from '../../dex-helper/irequest-wrapper';
|
|
21
|
+
|
|
22
|
+
export class RateFetcher {
|
|
23
|
+
private rateFetcher: Fetcher<SwaapV2PriceLevelsResponse>;
|
|
24
|
+
private pricesCacheTTL: number;
|
|
25
|
+
|
|
26
|
+
constructor(
|
|
27
|
+
private dexHelper: IDexHelper,
|
|
28
|
+
private dexKey: string,
|
|
29
|
+
private logger: Logger,
|
|
30
|
+
config: SwaapV2RateFetcherConfig,
|
|
31
|
+
) {
|
|
32
|
+
this.pricesCacheTTL = config.rateConfig.pricesCacheTTLSecs;
|
|
33
|
+
|
|
34
|
+
this.rateFetcher = new Fetcher<SwaapV2PriceLevelsResponse>(
|
|
35
|
+
dexHelper.httpRequest,
|
|
36
|
+
{
|
|
37
|
+
info: {
|
|
38
|
+
requestOptions: config.rateConfig.pricesReqParams,
|
|
39
|
+
caster: (data: unknown) => {
|
|
40
|
+
return validateAndCast<SwaapV2PriceLevelsResponse>(
|
|
41
|
+
data,
|
|
42
|
+
priceLevelsResponseValidator,
|
|
43
|
+
);
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
handler: this.handleRatesResponse.bind(this),
|
|
47
|
+
},
|
|
48
|
+
config.rateConfig.pricesIntervalMs,
|
|
49
|
+
logger,
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
start() {
|
|
54
|
+
this.rateFetcher.startPolling();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
stop() {
|
|
58
|
+
this.rateFetcher.stopPolling();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private handleRatesResponse(resp: SwaapV2PriceLevelsResponse): void {
|
|
62
|
+
if (resp.success) {
|
|
63
|
+
const levels = Object.keys(resp.levels)
|
|
64
|
+
.map(pairName => {
|
|
65
|
+
const pair = resp.levels[pairName];
|
|
66
|
+
if (!pair) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const levels = resp.levels[pairName];
|
|
70
|
+
|
|
71
|
+
if (!levels.asks || !levels.bids) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const pairSplit = pairName.split('/');
|
|
76
|
+
|
|
77
|
+
const baseAddress = pairSplit[0];
|
|
78
|
+
const quoteAddress = pairSplit[1];
|
|
79
|
+
pair.base = normalizeTokenAddress(baseAddress);
|
|
80
|
+
pair.quote = normalizeTokenAddress(quoteAddress);
|
|
81
|
+
return pair;
|
|
82
|
+
})
|
|
83
|
+
.filter((p: SwaapV2PriceLevels | undefined) => p != null);
|
|
84
|
+
|
|
85
|
+
this.dexHelper.cache.setex(
|
|
86
|
+
this.dexKey,
|
|
87
|
+
this.dexHelper.config.data.network,
|
|
88
|
+
`${getPriceLevelsCacheKey(this.dexKey)}`,
|
|
89
|
+
this.pricesCacheTTL,
|
|
90
|
+
JSON.stringify(levels),
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async getQuote(
|
|
96
|
+
networkId: number,
|
|
97
|
+
_srcToken: Token,
|
|
98
|
+
_destToken: Token,
|
|
99
|
+
srcAmount: string,
|
|
100
|
+
side: SwaapV2OrderType,
|
|
101
|
+
userAddress: Address,
|
|
102
|
+
aggregatorRecipient: Address,
|
|
103
|
+
tolerance: number,
|
|
104
|
+
requestParameters: RequestConfig,
|
|
105
|
+
): Promise<SwaapV2QuoteResponse> {
|
|
106
|
+
const srcToken = this.dexHelper.config.wrapETH(_srcToken);
|
|
107
|
+
const destToken = this.dexHelper.config.wrapETH(_destToken);
|
|
108
|
+
|
|
109
|
+
if (BigInt(srcAmount) === 0n) {
|
|
110
|
+
throw new Error('geQuote failed with srcAmount == 0');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const _payload: SwaapV2QuoteRequest = {
|
|
114
|
+
network_id: networkId,
|
|
115
|
+
origin: userAddress,
|
|
116
|
+
sender: aggregatorRecipient,
|
|
117
|
+
recipient: aggregatorRecipient,
|
|
118
|
+
timestamp: Math.round(Date.now() / 1000),
|
|
119
|
+
order_type: side,
|
|
120
|
+
token_in: srcToken.address,
|
|
121
|
+
token_out: destToken.address,
|
|
122
|
+
amount: srcAmount,
|
|
123
|
+
tolerance: tolerance,
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
let payload: RequestConfig = {
|
|
128
|
+
data: _payload,
|
|
129
|
+
...requestParameters,
|
|
130
|
+
timeout: SWAAP_RFQ_QUOTE_TIMEOUT_MS,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
this.logger.info(
|
|
134
|
+
'GetQuote Request:',
|
|
135
|
+
JSON.stringify(payload).replace(/(?:\r\n|\r|\n)/g, ' '),
|
|
136
|
+
);
|
|
137
|
+
const { data } = await this.dexHelper.httpRequest.request<unknown>(
|
|
138
|
+
payload,
|
|
139
|
+
);
|
|
140
|
+
this.logger.info(
|
|
141
|
+
'GetQuote Response: ',
|
|
142
|
+
JSON.stringify(data).replace(/(?:\r\n|\r|\n)/g, ' '),
|
|
143
|
+
);
|
|
144
|
+
const quoteResp = validateAndCast<SwaapV2QuoteResponse>(
|
|
145
|
+
data,
|
|
146
|
+
getQuoteResponseValidator,
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
id: quoteResp.id,
|
|
151
|
+
calldata: quoteResp.calldata,
|
|
152
|
+
router: quoteResp.router,
|
|
153
|
+
expiration: quoteResp.expiration,
|
|
154
|
+
amount: quoteResp.amount,
|
|
155
|
+
guaranteed_price: quoteResp.guaranteed_price,
|
|
156
|
+
success: quoteResp.success,
|
|
157
|
+
recipient: quoteResp.recipient,
|
|
158
|
+
};
|
|
159
|
+
} catch (e) {
|
|
160
|
+
this.logger.error(e);
|
|
161
|
+
throw e;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import dotenv from 'dotenv';
|
|
2
|
+
dotenv.config();
|
|
3
|
+
|
|
4
|
+
import { testE2E } from '../../../tests/utils-e2e';
|
|
5
|
+
import {
|
|
6
|
+
Tokens,
|
|
7
|
+
Holders,
|
|
8
|
+
NativeTokenSymbols,
|
|
9
|
+
} from '../../../tests/constants-e2e';
|
|
10
|
+
import { Network, ContractMethod, SwapSide } from '../../constants';
|
|
11
|
+
import { StaticJsonRpcProvider } from '@ethersproject/providers';
|
|
12
|
+
import { generateConfig } from '../../config';
|
|
13
|
+
|
|
14
|
+
const sleepMs: number = 5000;
|
|
15
|
+
|
|
16
|
+
function testForNetwork(
|
|
17
|
+
network: Network,
|
|
18
|
+
dexKey: string,
|
|
19
|
+
tokenASymbol: string,
|
|
20
|
+
tokenBSymbol: string,
|
|
21
|
+
tokenAAmount: string,
|
|
22
|
+
tokenBAmount: string,
|
|
23
|
+
nativeTokenAmount: string,
|
|
24
|
+
excludeNativeTokenTests: boolean = true,
|
|
25
|
+
) {
|
|
26
|
+
const provider = new StaticJsonRpcProvider(
|
|
27
|
+
generateConfig(network).privateHttpProvider,
|
|
28
|
+
network,
|
|
29
|
+
);
|
|
30
|
+
const tokens = Tokens[network];
|
|
31
|
+
const holders = Holders[network];
|
|
32
|
+
const nativeTokenSymbol = NativeTokenSymbols[network];
|
|
33
|
+
|
|
34
|
+
const sideToContractMethods = new Map([
|
|
35
|
+
[
|
|
36
|
+
SwapSide.SELL,
|
|
37
|
+
[
|
|
38
|
+
ContractMethod.simpleSwap,
|
|
39
|
+
ContractMethod.multiSwap,
|
|
40
|
+
ContractMethod.megaSwap,
|
|
41
|
+
],
|
|
42
|
+
],
|
|
43
|
+
[SwapSide.BUY, [ContractMethod.simpleBuy, ContractMethod.buy]],
|
|
44
|
+
]);
|
|
45
|
+
|
|
46
|
+
describe(`${network}`, () => {
|
|
47
|
+
sideToContractMethods.forEach((contractMethods, side) =>
|
|
48
|
+
describe(`${side}`, () => {
|
|
49
|
+
contractMethods.forEach((contractMethod: ContractMethod) => {
|
|
50
|
+
describe(`${contractMethod}`, () => {
|
|
51
|
+
if (excludeNativeTokenTests) {
|
|
52
|
+
it(`${tokenASymbol} -> ${tokenBSymbol}`, async () => {
|
|
53
|
+
await testE2E(
|
|
54
|
+
tokens[tokenASymbol],
|
|
55
|
+
tokens[tokenBSymbol],
|
|
56
|
+
holders[tokenASymbol],
|
|
57
|
+
side === SwapSide.SELL ? tokenAAmount : tokenBAmount,
|
|
58
|
+
side,
|
|
59
|
+
dexKey,
|
|
60
|
+
contractMethod,
|
|
61
|
+
network,
|
|
62
|
+
provider,
|
|
63
|
+
undefined,
|
|
64
|
+
undefined,
|
|
65
|
+
undefined,
|
|
66
|
+
undefined,
|
|
67
|
+
sleepMs,
|
|
68
|
+
);
|
|
69
|
+
});
|
|
70
|
+
it(`${tokenBSymbol} -> ${tokenASymbol}`, async () => {
|
|
71
|
+
await testE2E(
|
|
72
|
+
tokens[tokenBSymbol],
|
|
73
|
+
tokens[tokenASymbol],
|
|
74
|
+
holders[tokenBSymbol],
|
|
75
|
+
side === SwapSide.SELL ? tokenBAmount : tokenAAmount,
|
|
76
|
+
side,
|
|
77
|
+
dexKey,
|
|
78
|
+
contractMethod,
|
|
79
|
+
network,
|
|
80
|
+
provider,
|
|
81
|
+
undefined,
|
|
82
|
+
undefined,
|
|
83
|
+
undefined,
|
|
84
|
+
undefined,
|
|
85
|
+
sleepMs,
|
|
86
|
+
);
|
|
87
|
+
});
|
|
88
|
+
} else {
|
|
89
|
+
it(`${nativeTokenSymbol} -> ${tokenASymbol}`, async () => {
|
|
90
|
+
await testE2E(
|
|
91
|
+
tokens[nativeTokenSymbol],
|
|
92
|
+
tokens[tokenASymbol],
|
|
93
|
+
holders[nativeTokenSymbol],
|
|
94
|
+
side === SwapSide.SELL ? nativeTokenAmount : tokenAAmount,
|
|
95
|
+
side,
|
|
96
|
+
dexKey,
|
|
97
|
+
contractMethod,
|
|
98
|
+
network,
|
|
99
|
+
provider,
|
|
100
|
+
undefined,
|
|
101
|
+
undefined,
|
|
102
|
+
undefined,
|
|
103
|
+
undefined,
|
|
104
|
+
sleepMs,
|
|
105
|
+
);
|
|
106
|
+
});
|
|
107
|
+
it(`${tokenASymbol} -> ${nativeTokenSymbol}`, async () => {
|
|
108
|
+
await testE2E(
|
|
109
|
+
tokens[tokenASymbol],
|
|
110
|
+
tokens[nativeTokenSymbol],
|
|
111
|
+
holders[tokenASymbol],
|
|
112
|
+
side === SwapSide.SELL ? tokenAAmount : nativeTokenAmount,
|
|
113
|
+
side,
|
|
114
|
+
dexKey,
|
|
115
|
+
contractMethod,
|
|
116
|
+
network,
|
|
117
|
+
provider,
|
|
118
|
+
undefined,
|
|
119
|
+
undefined,
|
|
120
|
+
undefined,
|
|
121
|
+
undefined,
|
|
122
|
+
sleepMs,
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
}),
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
describe('SwaapV2 E2E', () => {
|
|
134
|
+
const dexKey = 'SwaapV2';
|
|
135
|
+
|
|
136
|
+
describe('Polygon', () => {
|
|
137
|
+
const network = Network.POLYGON;
|
|
138
|
+
|
|
139
|
+
const tokenASymbol: string = 'USDC';
|
|
140
|
+
const tokenBSymbol: string = 'WETH';
|
|
141
|
+
|
|
142
|
+
const tokenAAmount: string = '100000000';
|
|
143
|
+
const tokenBAmount: string = '10000000000000000';
|
|
144
|
+
const nativeTokenAmount = '1000000000000000000';
|
|
145
|
+
|
|
146
|
+
testForNetwork(
|
|
147
|
+
network,
|
|
148
|
+
dexKey,
|
|
149
|
+
tokenASymbol,
|
|
150
|
+
tokenBSymbol,
|
|
151
|
+
tokenAAmount,
|
|
152
|
+
tokenBAmount,
|
|
153
|
+
nativeTokenAmount,
|
|
154
|
+
);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// Arbitrum will be supported later
|
|
158
|
+
// describe('Arbitrum', () => {
|
|
159
|
+
// const network = Network.ARBITRUM;
|
|
160
|
+
//
|
|
161
|
+
// const tokenASymbol: string = 'ETH';
|
|
162
|
+
// const tokenBSymbol: string = 'DAI';
|
|
163
|
+
//
|
|
164
|
+
// const tokenAAmount: string = '100000000';
|
|
165
|
+
// const tokenBAmount: string = '1000000000000000000';
|
|
166
|
+
// const nativeTokenAmount = '1000000000000000000';
|
|
167
|
+
//
|
|
168
|
+
// testForNetwork(
|
|
169
|
+
// network,
|
|
170
|
+
// dexKey,
|
|
171
|
+
// tokenASymbol,
|
|
172
|
+
// tokenBSymbol,
|
|
173
|
+
// tokenAAmount,
|
|
174
|
+
// tokenBAmount,
|
|
175
|
+
// nativeTokenAmount,
|
|
176
|
+
// );
|
|
177
|
+
// });
|
|
178
|
+
|
|
179
|
+
// Mainnet will be supported later
|
|
180
|
+
// describe('Mainnet', () => {
|
|
181
|
+
// const network = Network.MAINNET;
|
|
182
|
+
// const tokenASymbol: string = 'USDC';
|
|
183
|
+
// const tokenBSymbol: string = 'USDT';
|
|
184
|
+
//
|
|
185
|
+
// const tokenAAmount: string = '100000000';
|
|
186
|
+
// const tokenBAmount: string = '1000000000000000000';
|
|
187
|
+
// const nativeTokenAmount = '1000000000000000000';
|
|
188
|
+
//
|
|
189
|
+
// testForNetwork(
|
|
190
|
+
// network,
|
|
191
|
+
// dexKey,
|
|
192
|
+
// tokenASymbol,
|
|
193
|
+
// tokenBSymbol,
|
|
194
|
+
// tokenAAmount,
|
|
195
|
+
// tokenBAmount,
|
|
196
|
+
// nativeTokenAmount,
|
|
197
|
+
// );
|
|
198
|
+
// });
|
|
199
|
+
});
|