@paraswap/dex-lib 2.16.4 → 2.16.5-direct-swaps.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 (131) hide show
  1. package/build/abi/DirectSwap.json +970 -0
  2. package/build/abi/woo-fi/WooFeeManager.abi.json +318 -0
  3. package/build/abi/woo-fi/WooPP.abi.json +548 -0
  4. package/build/abi/woo-fi/Wooracle.abi.json +260 -0
  5. package/build/config.js +6 -6
  6. package/build/dex/aave-v3/tokens/arbitrum.json +50 -0
  7. package/build/dex/aave-v3/tokens/avalanche.json +74 -0
  8. package/build/dex/aave-v3/tokens/fantom.json +62 -0
  9. package/build/dex/aave-v3/tokens/optimism.json +50 -0
  10. package/build/dex/aave-v3/tokens/polygon.json +116 -0
  11. package/build/dex/balancer-v2/balancer-v2.d.ts +9 -3
  12. package/build/dex/balancer-v2/balancer-v2.js +81 -0
  13. package/build/dex/balancer-v2/balancer-v2.js.map +1 -1
  14. package/build/dex/balancer-v2/constants.d.ts +4 -0
  15. package/build/dex/balancer-v2/constants.js +6 -1
  16. package/build/dex/balancer-v2/constants.js.map +1 -1
  17. package/build/dex/balancer-v2/types.d.ts +18 -1
  18. package/build/dex/balancer-v2/types.js.map +1 -1
  19. package/build/dex/curve-v1/constants.d.ts +1 -0
  20. package/build/dex/curve-v1/constants.js +2 -1
  21. package/build/dex/curve-v1/constants.js.map +1 -1
  22. package/build/dex/curve-v1/curve-v1.d.ts +9 -3
  23. package/build/dex/curve-v1/curve-v1.js +90 -4
  24. package/build/dex/curve-v1/curve-v1.js.map +1 -1
  25. package/build/dex/curve-v1/types.d.ts +23 -0
  26. package/build/dex/curve-v1/types.js +6 -1
  27. package/build/dex/curve-v1/types.js.map +1 -1
  28. package/build/dex/curve-v1-factory/constants.d.ts +1 -0
  29. package/build/dex/curve-v1-factory/constants.js +2 -1
  30. package/build/dex/curve-v1-factory/constants.js.map +1 -1
  31. package/build/dex/curve-v1-factory/curve-v1-factory.d.ts +11 -4
  32. package/build/dex/curve-v1-factory/curve-v1-factory.js +85 -0
  33. package/build/dex/curve-v1-factory/curve-v1-factory.js.map +1 -1
  34. package/build/dex/curve-v1-factory/types.d.ts +23 -0
  35. package/build/dex/curve-v1-factory/types.js +6 -1
  36. package/build/dex/curve-v1-factory/types.js.map +1 -1
  37. package/build/dex/curve-v2.d.ts +28 -7
  38. package/build/dex/curve-v2.js +89 -1
  39. package/build/dex/curve-v2.js.map +1 -1
  40. package/build/dex/idex.d.ts +1 -1
  41. package/build/dex/quickswap-v3.d.ts +21 -0
  42. package/build/dex/quickswap-v3.js +40 -0
  43. package/build/dex/quickswap-v3.js.map +1 -0
  44. package/build/dex/simple-exchange.d.ts +2 -0
  45. package/build/dex/simple-exchange.js +1 -0
  46. package/build/dex/simple-exchange.js.map +1 -1
  47. package/build/dex/test-dex/config.d.ts +4 -0
  48. package/build/dex/test-dex/config.js +15 -0
  49. package/build/dex/test-dex/config.js.map +1 -0
  50. package/build/dex/test-dex/test-dex-pool.d.ts +39 -0
  51. package/build/dex/test-dex/test-dex-pool.js +63 -0
  52. package/build/dex/test-dex/test-dex-pool.js.map +1 -0
  53. package/build/dex/test-dex/test-dex.d.ts +37 -0
  54. package/build/dex/test-dex/test-dex.js +126 -0
  55. package/build/dex/test-dex/test-dex.js.map +1 -0
  56. package/build/dex/test-dex/types.d.ts +6 -0
  57. package/build/dex/test-dex/types.js +3 -0
  58. package/build/dex/test-dex/types.js.map +1 -0
  59. package/build/dex/uniswap-v2/uniswap-v2.d.ts +1 -1
  60. package/build/dex/uniswap-v2/uniswap-v2.js +1 -1
  61. package/build/dex/uniswap-v2/uniswap-v2.js.map +1 -1
  62. package/build/dex/uniswap-v3/constants.d.ts +4 -0
  63. package/build/dex/uniswap-v3/constants.js +6 -1
  64. package/build/dex/uniswap-v3/constants.js.map +1 -1
  65. package/build/dex/uniswap-v3/types.d.ts +20 -3
  66. package/build/dex/uniswap-v3/types.js.map +1 -1
  67. package/build/dex/uniswap-v3/uniswap-v3.d.ts +7 -1
  68. package/build/dex/uniswap-v3/uniswap-v3.js +85 -1
  69. package/build/dex/uniswap-v3/uniswap-v3.js.map +1 -1
  70. package/build/dex/woo-fi/config.d.ts +4 -0
  71. package/build/dex/woo-fi/config.js +141 -0
  72. package/build/dex/woo-fi/config.js.map +1 -0
  73. package/build/dex/woo-fi/constants.d.ts +5 -0
  74. package/build/dex/woo-fi/constants.js +20 -0
  75. package/build/dex/woo-fi/constants.js.map +1 -0
  76. package/build/dex/woo-fi/types.d.ts +48 -0
  77. package/build/dex/woo-fi/types.js +3 -0
  78. package/build/dex/woo-fi/types.js.map +1 -0
  79. package/build/dex/woo-fi/woo-fi-decimal-math.d.ts +14 -0
  80. package/build/dex/woo-fi/woo-fi-decimal-math.js +42 -0
  81. package/build/dex/woo-fi/woo-fi-decimal-math.js.map +1 -0
  82. package/build/dex/woo-fi/woo-fi-math.d.ts +31 -0
  83. package/build/dex/woo-fi/woo-fi-math.js +300 -0
  84. package/build/dex/woo-fi/woo-fi-math.js.map +1 -0
  85. package/build/dex/woo-fi/woo-fi.d.ts +65 -0
  86. package/build/dex/woo-fi/woo-fi.js +480 -0
  87. package/build/dex/woo-fi/woo-fi.js.map +1 -0
  88. package/build/dex/woo-fi-v2/woo-fi-math.d.ts +16 -0
  89. package/build/dex/woo-fi-v2/woo-fi-math.js +137 -0
  90. package/build/dex/woo-fi-v2/woo-fi-math.js.map +1 -0
  91. package/build/dex/zerox/index.d.ts +1 -1
  92. package/build/dex/zerox/index.js +1 -1
  93. package/build/dex/zerox/index.js.map +1 -1
  94. package/build/implementations/local-paraswap-sdk.js +5 -1
  95. package/build/implementations/local-paraswap-sdk.js.map +1 -1
  96. package/build/router/directswap.js +3 -1
  97. package/build/router/directswap.js.map +1 -1
  98. package/build/router/index.js.map +1 -1
  99. package/build/types.d.ts +1 -0
  100. package/package.json +5 -3
  101. package/src/abi/DirectSwap.json +970 -0
  102. package/src/config.ts +6 -6
  103. package/src/dex/balancer-v2/balancer-v2-e2e.test.ts +71 -0
  104. package/src/dex/balancer-v2/balancer-v2.ts +150 -3
  105. package/src/dex/balancer-v2/constants.ts +5 -0
  106. package/src/dex/balancer-v2/types.ts +19 -1
  107. package/src/dex/curve-v1/constants.ts +2 -0
  108. package/src/dex/curve-v1/curve-v1-e2e.test.ts +57 -0
  109. package/src/dex/curve-v1/curve-v1.ts +142 -5
  110. package/src/dex/curve-v1/types.ts +25 -0
  111. package/src/dex/curve-v1-factory/constants.ts +2 -0
  112. package/src/dex/curve-v1-factory/curve-v1-factory-e2e.test.ts +1 -0
  113. package/src/dex/curve-v1-factory/curve-v1-factory.ts +138 -4
  114. package/src/dex/curve-v1-factory/types.ts +25 -0
  115. package/src/dex/curve-v2.ts +166 -8
  116. package/src/dex/generic-rfq/generic-rfq-e2e.test.ts +2 -2
  117. package/src/dex/idex.ts +6 -0
  118. package/src/dex/simple-exchange.ts +6 -0
  119. package/src/dex/uniswap-v2/uniswap-v2.ts +6 -0
  120. package/src/dex/uniswap-v3/constants.ts +5 -0
  121. package/src/dex/uniswap-v3/types.ts +23 -3
  122. package/src/dex/uniswap-v3/uniswap-v3-e2e.test.ts +100 -2
  123. package/src/dex/uniswap-v3/uniswap-v3.ts +138 -3
  124. package/src/dex/zerox/index.ts +7 -0
  125. package/src/implementations/local-paraswap-sdk.ts +8 -1
  126. package/src/router/directswap.ts +9 -0
  127. package/src/router/index.ts +0 -2
  128. package/src/types.ts +1 -0
  129. package/tests/constants-e2e.ts +3 -3
  130. package/tests/utils-e2e.ts +135 -20
  131. package/tests/utils.ts +52 -0
package/src/config.ts CHANGED
@@ -158,8 +158,8 @@ const baseConfigs: { [network: number]: BaseConfig } = {
158
158
  process.env[`HASHFLOW_DISABLED_MMS_56`]?.split(',') || [],
159
159
  adapterAddresses: {
160
160
  BscAdapter01: '0xA31d9C571DF00e0F428B0bD24c34D103E8112222',
161
- BscAdapter02: '0xe102429Ef7CAaBaAA3bc2C017aC784c5F665EE82',
162
- BscBuyAdapter: '0xA7f4fBCEB76a1B1c05C106a4289C28B3b91C17ca',
161
+ BscAdapter02: '0x1d2Fd92a1942A92a51198168eFCd626ed441CEC0',
162
+ BscBuyAdapter: '0x64C856fafE4C83a818514cBDfD661a3563a71B98',
163
163
  },
164
164
  rpcPollingMaxAllowedStateDelayInBlocks: 1,
165
165
  rpcPollingBlocksBackToTriggerUpdate: 1,
@@ -213,8 +213,8 @@ const baseConfigs: { [network: number]: BaseConfig } = {
213
213
  process.env[`HASHFLOW_DISABLED_MMS_43114`]?.split(',') || [],
214
214
  adapterAddresses: {
215
215
  AvalancheAdapter01: '0x745Ec73855CeC7249E5fF4c9DD81cc65b4D297a9',
216
- AvalancheAdapter02: '0x68847D34a84459Fe3654d3b69c6741a0c336D410',
217
- AvalancheBuyAdapter: '0xad2f7039B0F4c410654886D049ae8406F5aDe709',
216
+ AvalancheAdapter02: '0xa003dFBA51C9e1e56C67ae445b852bdEd7aC5EEd',
217
+ AvalancheBuyAdapter: '0xfa072107a2CCfE5664794282EF36400735ABB70F',
218
218
  },
219
219
  uniswapV2ExchangeRouterAddress:
220
220
  '0x53e693c6C7FFC4446c53B205Cf513105Bf140D7b',
@@ -267,8 +267,8 @@ const baseConfigs: { [network: number]: BaseConfig } = {
267
267
  process.env[`HASHFLOW_DISABLED_MMS_42161`]?.split(',') || [],
268
268
  adapterAddresses: {
269
269
  ArbitrumAdapter01: '0x745Ec73855CeC7249E5fF4c9DD81cc65b4D297a9',
270
- ArbitrumAdapter02: '0xCBaeB06C2dF373c07A2Dc205266EC3bCd525DfB6',
271
- ArbitrumBuyAdapter: '0xfa072107a2CCfE5664794282EF36400735ABB70F',
270
+ ArbitrumAdapter02: '0x3ad7f275E27AC579cA88e0b4765828242A9E8C49',
271
+ ArbitrumBuyAdapter: '0x6c33C7f6CBB4a428fe9ee31ca500a787c9f1525b',
272
272
  },
273
273
  uniswapV2ExchangeRouterAddress:
274
274
  '0xB41dD984730dAf82f5C41489E21ac79D5e3B61bC',
@@ -405,6 +405,77 @@ describe('BalancerV2 E2E', () => {
405
405
  //});
406
406
  });
407
407
 
408
+ describe('BalancerV2 Polygon', () => {
409
+ const dexKey = 'BalancerV2';
410
+ const network = Network.POLYGON;
411
+ const tokens = Tokens[Network.POLYGON];
412
+ const holders = Holders[Network.POLYGON];
413
+ const provider = new StaticJsonRpcProvider(
414
+ generateConfig(network).privateHttpProvider,
415
+ network,
416
+ );
417
+
418
+ const sideToContractMethods = new Map([
419
+ [
420
+ SwapSide.SELL,
421
+ [
422
+ ContractMethod.simpleSwap,
423
+ ContractMethod.multiSwap,
424
+ ContractMethod.megaSwap,
425
+ ContractMethod.directBalancerV2GivenInSwap,
426
+ ],
427
+ ],
428
+ ]);
429
+
430
+ const pairs: { name: string; sellAmount: string }[][] = [
431
+ [
432
+ { name: 'WMATIC', sellAmount: '1000000000000000000' },
433
+ { name: 'stMATIC', sellAmount: '1000000000000000000' },
434
+ ],
435
+ [
436
+ { name: 'MATIC', sellAmount: '1000000000000000000' },
437
+ { name: 'stMATIC', sellAmount: '1000000000000000000' },
438
+ ],
439
+ ];
440
+
441
+ sideToContractMethods.forEach((contractMethods, side) =>
442
+ describe(`${side}`, () => {
443
+ contractMethods.forEach((contractMethod: ContractMethod) => {
444
+ pairs.forEach(pair => {
445
+ describe(`${contractMethod}`, () => {
446
+ it(`${pair[0].name} -> ${pair[1].name}`, async () => {
447
+ await testE2E(
448
+ tokens[pair[0].name],
449
+ tokens[pair[1].name],
450
+ holders[pair[0].name],
451
+ pair[0].sellAmount,
452
+ side,
453
+ dexKey,
454
+ contractMethod,
455
+ network,
456
+ provider,
457
+ );
458
+ });
459
+ it(`${pair[1].name} -> ${pair[0].name}`, async () => {
460
+ await testE2E(
461
+ tokens[pair[1].name],
462
+ tokens[pair[0].name],
463
+ holders[pair[1].name],
464
+ pair[1].sellAmount,
465
+ side,
466
+ dexKey,
467
+ contractMethod,
468
+ network,
469
+ provider,
470
+ );
471
+ });
472
+ });
473
+ });
474
+ });
475
+ }),
476
+ );
477
+ });
478
+
408
479
  describe('BalancerV2 ARBITRUM', () => {
409
480
  const dexKey = 'BalancerV2';
410
481
  const network = Network.ARBITRUM;
@@ -11,6 +11,9 @@ import {
11
11
  SimpleExchangeParam,
12
12
  PoolLiquidity,
13
13
  Logger,
14
+ TxInfo,
15
+ PreprocessTransactionOptions,
16
+ ExchangeTxInfo,
14
17
  } from '../../types';
15
18
  import {
16
19
  SwapSide,
@@ -26,8 +29,13 @@ import { StablePool, WeightedPool } from './balancer-v2-pool';
26
29
  import { PhantomStablePool } from './PhantomStablePool';
27
30
  import { LinearPool } from './LinearPool';
28
31
  import VaultABI from '../../abi/balancer-v2/vault.json';
32
+ import DirectSwapABI from '../../abi/DirectSwap.json';
29
33
  import { StatefulEventSubscriber } from '../../stateful-event-subscriber';
30
- import { getDexKeysWithNetwork, getBigIntPow } from '../../utils';
34
+ import {
35
+ getDexKeysWithNetwork,
36
+ getBigIntPow,
37
+ uuidToBytes16,
38
+ } from '../../utils';
31
39
  import { IDex } from '../../dex/idex';
32
40
  import { IDexHelper } from '../../dex-helper';
33
41
  import {
@@ -42,8 +50,12 @@ import {
42
50
  PoolStateCache,
43
51
  BalancerPoolTypes,
44
52
  SubgraphPoolAddressDictionary,
53
+ BalancerV2DirectParam,
45
54
  } from './types';
46
- import { SimpleExchange } from '../simple-exchange';
55
+ import {
56
+ getLocalDeadlineAsFriendlyPlaceholder,
57
+ SimpleExchange,
58
+ } from '../simple-exchange';
47
59
  import { BalancerConfig, Adapters } from './config';
48
60
  import {
49
61
  getAllPoolsUsedInPaths,
@@ -52,10 +64,12 @@ import {
52
64
  poolGetPathForTokenInOut,
53
65
  } from './utils';
54
66
  import {
67
+ DirectMethods,
55
68
  MIN_USD_LIQUIDITY_TO_FETCH,
56
69
  STABLE_GAS_COST,
57
70
  VARIABLE_GAS_COST_PER_CYCLE,
58
71
  } from './constants';
72
+ import { NumberAsString, OptimalSwapExchange } from '@paraswap/core';
59
73
 
60
74
  const fetchAllPools = `query ($count: Int) {
61
75
  pools: pools(
@@ -474,13 +488,16 @@ export class BalancerV2EventPool extends StatefulEventSubscriber<PoolStateMap> {
474
488
 
475
489
  export class BalancerV2
476
490
  extends SimpleExchange
477
- implements IDex<BalancerV2Data, BalancerParam, OptimizedBalancerV2Data>
491
+ implements
492
+ IDex<BalancerV2Data, BalancerV2DirectParam, OptimizedBalancerV2Data>
478
493
  {
479
494
  protected eventPools: BalancerV2EventPool;
480
495
 
481
496
  readonly hasConstantPriceLargeAmounts = false;
482
497
  readonly isFeeOnTransferSupported = false;
483
498
 
499
+ readonly directSwapIface = new Interface(DirectSwapABI);
500
+
484
501
  public static dexKeysWithNetwork: { key: string; networks: Network[] }[] =
485
502
  getDexKeysWithNetwork(BalancerConfig);
486
503
 
@@ -993,6 +1010,136 @@ export class BalancerV2
993
1010
  return params;
994
1011
  }
995
1012
 
1013
+ static getDirectFunctionName(): string[] {
1014
+ return [DirectMethods.directSell, DirectMethods.directBuy];
1015
+ }
1016
+
1017
+ getTokenFromAddress(address: Address): Token {
1018
+ // In this Dex decimals are not used
1019
+ return { address, decimals: 0 };
1020
+ }
1021
+
1022
+ async preProcessTransaction(
1023
+ optimalSwapExchange: OptimalSwapExchange<OptimizedBalancerV2Data>,
1024
+ srcToken: Token,
1025
+ _0: Token,
1026
+ _1: SwapSide,
1027
+ options: PreprocessTransactionOptions,
1028
+ ): Promise<[OptimalSwapExchange<OptimizedBalancerV2Data>, ExchangeTxInfo]> {
1029
+ if (!options.isDirectMethod) {
1030
+ return [
1031
+ optimalSwapExchange,
1032
+ {
1033
+ deadline: BigInt(getLocalDeadlineAsFriendlyPlaceholder()),
1034
+ },
1035
+ ];
1036
+ }
1037
+
1038
+ assert(
1039
+ optimalSwapExchange.data !== undefined,
1040
+ `preProcessTransaction: data field is missing`,
1041
+ );
1042
+
1043
+ let isApproved: boolean | undefined;
1044
+
1045
+ try {
1046
+ this.erc20Contract.options.address =
1047
+ this.dexHelper.config.wrapETH(srcToken).address;
1048
+ const allowance = await this.erc20Contract.methods
1049
+ .allowance(this.augustusAddress, this.vaultAddress)
1050
+ .call(undefined, 'latest');
1051
+ isApproved =
1052
+ BigInt(allowance.toString()) >= BigInt(optimalSwapExchange.srcAmount);
1053
+ } catch (e) {
1054
+ this.logger.error(
1055
+ `preProcessTransaction failed to retrieve allowance info: `,
1056
+ e,
1057
+ );
1058
+ }
1059
+
1060
+ return [
1061
+ {
1062
+ ...optimalSwapExchange,
1063
+ data: {
1064
+ ...optimalSwapExchange.data,
1065
+ isApproved,
1066
+ },
1067
+ },
1068
+ {
1069
+ deadline: BigInt(getLocalDeadlineAsFriendlyPlaceholder()),
1070
+ },
1071
+ ];
1072
+ }
1073
+
1074
+ getDirectParam(
1075
+ srcToken: Address,
1076
+ destToken: Address,
1077
+ srcAmount: NumberAsString,
1078
+ destAmount: NumberAsString,
1079
+ expectedAmount: NumberAsString,
1080
+ data: OptimizedBalancerV2Data,
1081
+ side: SwapSide,
1082
+ permit: string,
1083
+ uuid: string,
1084
+ feePercent: NumberAsString,
1085
+ deadline: NumberAsString,
1086
+ partner: string,
1087
+ beneficiary: string,
1088
+ contractMethod?: string,
1089
+ ): TxInfo<BalancerV2DirectParam> {
1090
+ if (
1091
+ contractMethod !== DirectMethods.directSell &&
1092
+ contractMethod !== DirectMethods.directBuy
1093
+ ) {
1094
+ throw new Error(`Invalid contract method ${contractMethod}`);
1095
+ }
1096
+
1097
+ let isApproved: boolean = !!data.isApproved;
1098
+ if (data.isApproved === undefined) {
1099
+ this.logger.warn(`isApproved is undefined, defaulting to false`);
1100
+ }
1101
+
1102
+ const [, swaps, assets, funds, limits, _deadline] = this.getBalancerParam(
1103
+ srcToken,
1104
+ destToken,
1105
+ srcAmount,
1106
+ destAmount,
1107
+ data,
1108
+ side,
1109
+ );
1110
+
1111
+ const swapParams: BalancerV2DirectParam = [
1112
+ swaps,
1113
+ assets,
1114
+ funds,
1115
+ limits,
1116
+ expectedAmount,
1117
+ _deadline,
1118
+ feePercent,
1119
+ this.vaultAddress,
1120
+ partner,
1121
+ isApproved,
1122
+ beneficiary,
1123
+ permit,
1124
+ uuidToBytes16(uuid),
1125
+ ];
1126
+
1127
+ const encoder = (...params: BalancerV2DirectParam) => {
1128
+ return this.directSwapIface.encodeFunctionData(
1129
+ side === SwapSide.SELL
1130
+ ? DirectMethods.directSell
1131
+ : DirectMethods.directBuy,
1132
+ [params],
1133
+ );
1134
+ };
1135
+
1136
+ return {
1137
+ params: swapParams,
1138
+ encoder,
1139
+ networkFee: '0',
1140
+ };
1141
+ }
1142
+
996
1143
  async getSimpleParam(
997
1144
  srcToken: string,
998
1145
  destToken: string,
@@ -8,3 +8,8 @@ export const STABLE_GAS_COST = 84_000;
8
8
 
9
9
  // I see three pools used in trade: (57856 + 79098 + 51041) / 3 = 62665 ~ 63k
10
10
  export const VARIABLE_GAS_COST_PER_CYCLE = 63_000;
11
+
12
+ export enum DirectMethods {
13
+ directSell = 'directBalancerV2GivenInSwap',
14
+ directBuy = 'directBalancerV2GivenOutSwap',
15
+ }
@@ -1,3 +1,4 @@
1
+ import { NumberAsString } from '@paraswap/core';
1
2
  import { Address } from '../../types';
2
3
 
3
4
  // These should match the Balancer Pool types available on Subgraph
@@ -84,12 +85,13 @@ export type BalancerSwapV2 = {
84
85
 
85
86
  export type OptimizedBalancerV2Data = {
86
87
  swaps: BalancerSwapV2[];
88
+ isApproved?: boolean;
87
89
  };
88
90
 
89
91
  export type BalancerFunds = {
90
92
  sender: string;
91
- recipient: string;
92
93
  fromInternalBalance: boolean;
94
+ recipient: string;
93
95
  toInternalBalance: boolean;
94
96
  };
95
97
 
@@ -116,6 +118,22 @@ export type BalancerParam = [
116
118
  deadline: string,
117
119
  ];
118
120
 
121
+ export type BalancerV2DirectParam = [
122
+ swaps: BalancerSwap[],
123
+ assets: Address[],
124
+ funds: BalancerFunds,
125
+ limits: NumberAsString[],
126
+ expectedAmount: NumberAsString,
127
+ deadline: NumberAsString,
128
+ feePercent: NumberAsString,
129
+ vault: Address,
130
+ partner: Address,
131
+ isApproved: boolean,
132
+ beneficiary: Address,
133
+ permit: string,
134
+ uuid: string,
135
+ ];
136
+
119
137
  export type BalancerV2Data = {
120
138
  poolId: string;
121
139
  };
@@ -2,6 +2,8 @@ import { CurveV1Config } from './config';
2
2
  import { SwerveConfig } from './forks/swerve/config';
3
3
  import { CurveForksConfig } from './forks/curve-forks/config';
4
4
 
5
+ export const DIRECT_METHOD_NAME = 'directCurveV1Swap';
6
+
5
7
  export const AllCurveForks = [
6
8
  ...Object.keys(CurveV1Config),
7
9
  ...Object.keys(CurveForksConfig),
@@ -133,6 +133,63 @@ describe('CurveV1 E2E', () => {
133
133
  });
134
134
  });
135
135
 
136
+ describe('CurveV1 POLYGON', () => {
137
+ const network = Network.POLYGON;
138
+ const tokens = Tokens[network];
139
+ const holders = Holders[network];
140
+ const provider = new StaticJsonRpcProvider(
141
+ generateConfig(network).privateHttpProvider,
142
+ network,
143
+ );
144
+
145
+ const tokensToTest = [
146
+ [
147
+ {
148
+ symbol: 'USDT',
149
+ amount: (10 ** 8).toString(),
150
+ },
151
+ {
152
+ symbol: 'DAI',
153
+ amount: (10 ** 8).toString(),
154
+ },
155
+ ],
156
+ ];
157
+
158
+ const sideToContractMethods = new Map([
159
+ [
160
+ SwapSide.SELL,
161
+ [
162
+ ContractMethod.simpleSwap,
163
+ ContractMethod.multiSwap,
164
+ ContractMethod.megaSwap,
165
+ ContractMethod.directCurveV1Swap,
166
+ ],
167
+ ],
168
+ ]);
169
+
170
+ sideToContractMethods.forEach((contractMethods, side) =>
171
+ contractMethods.forEach((contractMethod: ContractMethod) => {
172
+ tokensToTest.forEach(pair => {
173
+ describe(`${contractMethod}`, () => {
174
+ it(`${pair[0].symbol} -> ${pair[1].symbol}`, async () => {
175
+ await testE2E(
176
+ tokens[pair[0].symbol],
177
+ tokens[pair[1].symbol],
178
+ holders[pair[0].symbol],
179
+ side === SwapSide.SELL ? pair[0].amount : pair[1].amount,
180
+ side,
181
+ dexKey,
182
+ contractMethod,
183
+ network,
184
+ provider,
185
+ );
186
+ });
187
+ });
188
+ });
189
+ }),
190
+ );
191
+ });
192
+
136
193
  describe('FANTOM', () => {
137
194
  const network = Network.FANTOM;
138
195
  const tokens = Tokens[network];
@@ -1,17 +1,24 @@
1
1
  import { Interface, AbiCoder, JsonFragment } from '@ethersproject/abi';
2
+ import _ from 'lodash';
3
+ import { NumberAsString, OptimalSwapExchange } from '@paraswap/core';
4
+ import { assert } from 'ts-essentials';
2
5
  import BigNumber from 'bignumber.js';
3
6
  import CurveABI from '../../abi/Curve.json';
7
+ import DirectSwapABI from '../../abi/DirectSwap.json';
4
8
  import { Adapters, CurveV1Config } from './config';
5
9
 
6
10
  import {
7
11
  AdapterExchangeParam,
8
12
  Address,
9
13
  ExchangePrices,
14
+ ExchangeTxInfo,
10
15
  PoolLiquidity,
11
16
  PoolPrices,
17
+ PreprocessTransactionOptions,
12
18
  SimpleExchangeParam,
13
19
  Token,
14
20
  TransferFeeParams,
21
+ TxInfo,
15
22
  } from '../../types';
16
23
  import {
17
24
  Network,
@@ -48,8 +55,6 @@ import { TBTCPool, address as TBTCPoolAddress } from './pools/tBTCpool';
48
55
  import { USDKPool, address as USDKPoolAddress } from './pools/usdkpool';
49
56
  import { USTPool, address as USTPoolAddress } from './pools/ustpool';
50
57
  import { SLINKPool, address as SLINKPoolAddress } from './pools/sLinkpool';
51
-
52
- import * as _ from 'lodash';
53
58
  import {
54
59
  getBigIntPow,
55
60
  getDexKeysWithNetwork,
@@ -57,9 +62,13 @@ import {
57
62
  isDestTokenTransferFeeToBeExchanged,
58
63
  isSrcTokenTransferFeeToBeExchanged,
59
64
  Utils,
65
+ uuidToBytes16,
60
66
  } from '../../utils';
61
67
  import { BN_0 } from '../../bignumber-constants';
62
- import { SimpleExchange } from '../simple-exchange';
68
+ import {
69
+ getLocalDeadlineAsFriendlyPlaceholder,
70
+ SimpleExchange,
71
+ } from '../simple-exchange';
63
72
  import { IDex } from '../idex';
64
73
  import { IDexHelper } from '../../dex-helper';
65
74
  import { Logger } from 'log4js';
@@ -71,9 +80,12 @@ import {
71
80
  CurveV1Data,
72
81
  TokenWithReasonableVolume,
73
82
  CurveSwapFunctions,
83
+ CurveV1SwapType,
84
+ DirectCurveV1Param,
74
85
  } from './types';
75
86
  import { erc20Iface } from '../../lib/utils-interfaces';
76
87
  import { applyTransferFee } from '../../lib/token-transfer-fee';
88
+ import { DIRECT_METHOD_NAME } from './constants';
77
89
 
78
90
  const CURVE_DEFAULT_CHUNKS = 10;
79
91
 
@@ -82,7 +94,10 @@ const CURVE_POOL_GAS = 200 * 1000;
82
94
 
83
95
  const coder = new AbiCoder();
84
96
 
85
- export class CurveV1 extends SimpleExchange implements IDex<CurveV1Data> {
97
+ export class CurveV1
98
+ extends SimpleExchange
99
+ implements IDex<CurveV1Data, DirectCurveV1Param>
100
+ {
86
101
  exchangeRouterInterface: Interface;
87
102
  minConversionRate = '1';
88
103
 
@@ -102,6 +117,8 @@ export class CurveV1 extends SimpleExchange implements IDex<CurveV1Data> {
102
117
 
103
118
  protected disableFeeOnTransferTokenAddresses: Set<string>;
104
119
 
120
+ readonly directSwapIface = new Interface(DirectSwapABI);
121
+
105
122
  readonly SRC_TOKEN_DEX_TRANSFERS = 1;
106
123
  readonly DEST_TOKEN_DEX_TRANSFERS = 1;
107
124
 
@@ -840,6 +857,126 @@ export class CurveV1 extends SimpleExchange implements IDex<CurveV1Data> {
840
857
  return this.adapters[side];
841
858
  }
842
859
 
860
+ static getDirectFunctionName(): string[] {
861
+ return [DIRECT_METHOD_NAME];
862
+ }
863
+
864
+ getTokenFromAddress(address: Address): Token {
865
+ // In this Dex decimals are not used
866
+ return { address, decimals: 0 };
867
+ }
868
+
869
+ async preProcessTransaction(
870
+ optimalSwapExchange: OptimalSwapExchange<CurveV1Data>,
871
+ srcToken: Token,
872
+ _0: Token,
873
+ _1: SwapSide,
874
+ options: PreprocessTransactionOptions,
875
+ ): Promise<[OptimalSwapExchange<CurveV1Data>, ExchangeTxInfo]> {
876
+ if (!options.isDirectMethod) {
877
+ return [
878
+ optimalSwapExchange,
879
+ {
880
+ deadline: BigInt(getLocalDeadlineAsFriendlyPlaceholder()),
881
+ },
882
+ ];
883
+ }
884
+
885
+ assert(
886
+ optimalSwapExchange.data !== undefined,
887
+ `preProcessTransaction: data field is missing`,
888
+ );
889
+
890
+ let isApproved: boolean | undefined;
891
+
892
+ try {
893
+ this.erc20Contract.options.address =
894
+ this.dexHelper.config.wrapETH(srcToken).address;
895
+ const allowance = await this.erc20Contract.methods
896
+ .allowance(this.augustusAddress, optimalSwapExchange.data.exchange)
897
+ .call(undefined, 'latest');
898
+ isApproved =
899
+ BigInt(allowance.toString()) >= BigInt(optimalSwapExchange.srcAmount);
900
+ } catch (e) {
901
+ this.logger.error(
902
+ `preProcessTransaction failed to retrieve allowance info: `,
903
+ e,
904
+ );
905
+ }
906
+
907
+ return [
908
+ {
909
+ ...optimalSwapExchange,
910
+ data: {
911
+ ...optimalSwapExchange.data,
912
+ isApproved,
913
+ },
914
+ },
915
+ {
916
+ deadline: BigInt(getLocalDeadlineAsFriendlyPlaceholder()),
917
+ },
918
+ ];
919
+ }
920
+
921
+ getDirectParam(
922
+ srcToken: Address,
923
+ destToken: Address,
924
+ srcAmount: NumberAsString,
925
+ destAmount: NumberAsString,
926
+ expectedAmount: NumberAsString,
927
+ data: CurveV1Data,
928
+ side: SwapSide,
929
+ permit: string,
930
+ uuid: string,
931
+ feePercent: NumberAsString,
932
+ deadline: NumberAsString,
933
+ partner: string,
934
+ beneficiary: string,
935
+ contractMethod?: string,
936
+ ): TxInfo<DirectCurveV1Param> {
937
+ if (contractMethod !== DIRECT_METHOD_NAME) {
938
+ throw new Error(`Invalid contract method ${contractMethod}`);
939
+ }
940
+ assert(side === SwapSide.SELL, 'Buy not supported');
941
+
942
+ let isApproved: boolean = !!data.isApproved;
943
+ if (data.isApproved === undefined) {
944
+ this.logger.warn(`isApproved is undefined, defaulting to false`);
945
+ }
946
+
947
+ const swapParams: DirectCurveV1Param = [
948
+ srcToken,
949
+ destToken,
950
+ data.exchange,
951
+ srcAmount,
952
+ destAmount,
953
+ expectedAmount,
954
+ feePercent,
955
+ data.i.toString(),
956
+ data.j.toString(),
957
+ partner,
958
+ isApproved,
959
+ data.underlyingSwap
960
+ ? CurveV1SwapType.EXCHANGE_UNDERLYING
961
+ : CurveV1SwapType.EXCHANGE,
962
+ beneficiary,
963
+ permit,
964
+ uuidToBytes16(uuid),
965
+ ];
966
+
967
+ const encoder = (...params: DirectCurveV1Param) => {
968
+ return this.directSwapIface.encodeFunctionData(DIRECT_METHOD_NAME, [
969
+ params,
970
+ ]);
971
+ };
972
+
973
+ return {
974
+ params: swapParams,
975
+ encoder,
976
+ networkFee: '0',
977
+ };
978
+ }
979
+
843
980
  async getSimpleParam(
844
981
  srcToken: string,
845
982
  destToken: string,
@@ -943,7 +1080,7 @@ export class CurveV1 extends SimpleExchange implements IDex<CurveV1Data> {
943
1080
  // the balances array in the pool as it was a mess to have each curve abi
944
1081
  // Some have balances[uint128] some have balances[uint256]
945
1082
  // One of the consequence for such a hack is below for ETH Pools
946
- // TODO: below can be highly optimised
1083
+ // TODO: below can be highly optimized
947
1084
  // * we don't need to query token decimals every iteration
948
1085
  // * we can use the decimals from the tokens list using the api
949
1086
 
@@ -1,3 +1,4 @@
1
+ import { NumberAsString } from '@paraswap/core';
1
2
  import { Address, Token } from '../../types';
2
3
 
3
4
  export type CurveV1Data = {
@@ -6,6 +7,7 @@ export type CurveV1Data = {
6
7
  j: number;
7
8
  underlyingSwap: boolean;
8
9
  deadline: number;
10
+ isApproved?: boolean;
9
11
  };
10
12
 
11
13
  export type PoolConfig = {
@@ -42,3 +44,26 @@ export enum CurveSwapFunctions {
42
44
  exchange = 'exchange',
43
45
  exchange_underlying = 'exchange_underlying',
44
46
  }
47
+
48
+ export enum CurveV1SwapType {
49
+ EXCHANGE,
50
+ EXCHANGE_UNDERLYING,
51
+ }
52
+
53
+ export type DirectCurveV1Param = [
54
+ fromToken: Address,
55
+ toToken: Address,
56
+ exchange: Address,
57
+ fromAmount: NumberAsString,
58
+ toAmount: NumberAsString,
59
+ expectedAmount: NumberAsString,
60
+ feePercent: NumberAsString,
61
+ i: NumberAsString,
62
+ j: NumberAsString,
63
+ partner: Address,
64
+ isApproved: boolean,
65
+ swapType: CurveV1SwapType,
66
+ beneficiary: Address,
67
+ permit: string,
68
+ uuid: string,
69
+ ];
@@ -34,3 +34,5 @@ export const NETWORK_ID_TO_NAME: Record<number, string> = {
34
34
  // revisited
35
35
  export const FACTORY_MAX_PLAIN_COINS = 4;
36
36
  export const FACTORY_MAX_PLAIN_IMPLEMENTATIONS_FOR_COIN = 10;
37
+
38
+ export const DIRECT_METHOD_NAME = 'directCurveV1Swap';