@paraswap/dex-lib 4.1.6 → 4.1.7-mwrappedm.1

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/src/dex/index.ts CHANGED
@@ -102,6 +102,7 @@ import { UsualBond } from './usual/usual-bond';
102
102
  import { UsualMWrappedM } from './usual/usual-m-wrapped-m';
103
103
  import { UsualMUsd0 } from './usual/usual-m-usd0';
104
104
  import { UsualPP } from './usual-pp/usual-pp';
105
+ import { MWrappedM } from './m-token/m-wrapped-m';
105
106
 
106
107
  const LegacyDexes = [
107
108
  CurveV2,
@@ -198,6 +199,7 @@ const Dexes = [
198
199
  UsualMWrappedM,
199
200
  UsualMUsd0,
200
201
  UsualPP,
202
+ MWrappedM,
201
203
  ];
202
204
 
203
205
  export type LegacyDexConstructor = new (dexHelper: IDexHelper) => IDexTxBuilder<
@@ -0,0 +1,18 @@
1
+ import { DexConfigMap } from '../../types';
2
+ import { DexParams } from './types';
3
+ import { Network } from '../../constants';
4
+
5
+ export const Config: DexConfigMap<DexParams> = {
6
+ MWrappedM: {
7
+ [Network.MAINNET]: {
8
+ MToken: {
9
+ address: '0x866A2BF4E572CbcF37D5071A7a58503Bfb36be1b',
10
+ decimals: 6,
11
+ },
12
+ WrappedM: {
13
+ address: '0x437cc33344a0B27A429f795ff6B469C72698B291',
14
+ decimals: 6,
15
+ },
16
+ },
17
+ },
18
+ };
@@ -0,0 +1,93 @@
1
+ /* eslint-disable no-console */
2
+ import dotenv from 'dotenv';
3
+ dotenv.config();
4
+
5
+ import { testE2E } from '../../../tests/utils-e2e';
6
+ import { Tokens, Holders } from '../../../tests/constants-e2e';
7
+ import { Network, ContractMethod, SwapSide } from '../../constants';
8
+ import { StaticJsonRpcProvider } from '@ethersproject/providers';
9
+ import { generateConfig } from '../../config';
10
+
11
+ function testForNetwork(
12
+ network: Network,
13
+ dexKey: string,
14
+ tokenASymbol: string,
15
+ tokenBSymbol: string,
16
+ tokenAAmount: string,
17
+ tokenBAmount: string,
18
+ ) {
19
+ const provider = new StaticJsonRpcProvider(
20
+ generateConfig(network).privateHttpProvider,
21
+ network,
22
+ );
23
+ const tokens = Tokens[network];
24
+ const holders = Holders[network];
25
+
26
+ const sideToContractMethods = new Map([
27
+ [SwapSide.SELL, [ContractMethod.swapExactAmountIn]],
28
+ [SwapSide.BUY, [ContractMethod.swapExactAmountOut]],
29
+ ]);
30
+
31
+ describe(`${network}`, () => {
32
+ sideToContractMethods.forEach((contractMethods, side) =>
33
+ describe(`${side}`, () => {
34
+ const isSell = side === SwapSide.SELL;
35
+
36
+ contractMethods.forEach((contractMethod: ContractMethod) => {
37
+ describe(`${contractMethod}`, () => {
38
+ it(`${tokenASymbol} -> ${tokenBSymbol}`, async () => {
39
+ await testE2E(
40
+ tokens[tokenASymbol],
41
+ tokens[tokenBSymbol],
42
+ holders[tokenASymbol],
43
+ isSell ? tokenAAmount : tokenBAmount,
44
+ side,
45
+ dexKey,
46
+ contractMethod,
47
+ network,
48
+ provider,
49
+ );
50
+ });
51
+
52
+ it(`${tokenBSymbol} -> ${tokenASymbol}`, async () => {
53
+ await testE2E(
54
+ tokens[tokenBSymbol],
55
+ tokens[tokenASymbol],
56
+ holders[tokenBSymbol],
57
+ isSell ? tokenBAmount : tokenAAmount,
58
+ side,
59
+ dexKey,
60
+ contractMethod,
61
+ network,
62
+ provider,
63
+ );
64
+ });
65
+ });
66
+ });
67
+ }),
68
+ );
69
+ });
70
+ }
71
+
72
+ describe('MWrappedM E2E', () => {
73
+ const dexKey = 'MWrappedM';
74
+
75
+ describe('Mainnet', () => {
76
+ const network = Network.MAINNET;
77
+
78
+ const tokenASymbol: string = 'M';
79
+ const tokenBSymbol: string = 'WrappedM';
80
+
81
+ const tokenAAmount: string = '1000000000';
82
+ const tokenBAmount: string = '1000000000';
83
+
84
+ testForNetwork(
85
+ network,
86
+ dexKey,
87
+ tokenASymbol,
88
+ tokenBSymbol,
89
+ tokenAAmount,
90
+ tokenBAmount,
91
+ );
92
+ });
93
+ });
@@ -0,0 +1,199 @@
1
+ /* eslint-disable no-console */
2
+ import dotenv from 'dotenv';
3
+ dotenv.config();
4
+
5
+ import { DummyDexHelper } from '../../dex-helper/index';
6
+ import { Network, SwapSide } from '../../constants';
7
+ import { BI_POWS } from '../../bigint-constants';
8
+ import { MToken } from './m-token';
9
+ import { MWrappedM } from './m-wrapped-m';
10
+ import {
11
+ checkPoolPrices,
12
+ checkPoolsLiquidity,
13
+ checkConstantPoolPrices,
14
+ } from '../../../tests/utils';
15
+ import { Tokens } from '../../../tests/constants-e2e';
16
+
17
+ async function testPricingOnNetwork(
18
+ mWrappedM: MToken,
19
+ network: Network,
20
+ dexKey: string,
21
+ blockNumber: number,
22
+ srcTokenSymbol: string,
23
+ destTokenSymbol: string,
24
+ side: SwapSide,
25
+ amounts: bigint[],
26
+ funcNameToCheck: string,
27
+ ) {
28
+ const networkTokens = Tokens[network];
29
+
30
+ const pools = await mWrappedM.getPoolIdentifiers(
31
+ networkTokens[srcTokenSymbol],
32
+ networkTokens[destTokenSymbol],
33
+ side,
34
+ blockNumber,
35
+ );
36
+ console.log(
37
+ `${srcTokenSymbol} <> ${destTokenSymbol} Pool Identifiers: `,
38
+ pools,
39
+ );
40
+
41
+ expect(pools.length).toBeGreaterThan(0);
42
+
43
+ const poolPrices = await mWrappedM.getPricesVolume(
44
+ networkTokens[srcTokenSymbol],
45
+ networkTokens[destTokenSymbol],
46
+ amounts,
47
+ side,
48
+ blockNumber,
49
+ pools,
50
+ );
51
+ console.log(
52
+ `${srcTokenSymbol} <> ${destTokenSymbol} Pool Prices: `,
53
+ poolPrices,
54
+ );
55
+
56
+ expect(poolPrices).not.toBeNull();
57
+ if (mWrappedM.hasConstantPriceLargeAmounts) {
58
+ checkConstantPoolPrices(poolPrices!, amounts, dexKey);
59
+ } else {
60
+ checkPoolPrices(poolPrices!, amounts, side, dexKey);
61
+ }
62
+
63
+ // Check if onchain pricing equals to calculated ones
64
+ checkPoolPrices(poolPrices!, amounts, side, dexKey);
65
+ }
66
+
67
+ describe('MWrappedM', function () {
68
+ const dexKey = 'MWrappedM';
69
+ let blockNumber: number;
70
+ let mWrappedM: MWrappedM;
71
+
72
+ describe('Mainnet', () => {
73
+ const network = Network.MAINNET;
74
+ const dexHelper = new DummyDexHelper(network);
75
+
76
+ const tokens = Tokens[network];
77
+
78
+ const srcTokenSymbol = 'M';
79
+ const destTokenSymbol = 'WrappedM';
80
+
81
+ // Don't forget to update relevant tokens in constant-e2e.ts
82
+
83
+ const amounts = [
84
+ 0n,
85
+ 1n * BI_POWS[tokens[srcTokenSymbol].decimals],
86
+ 2n * BI_POWS[tokens[srcTokenSymbol].decimals],
87
+ 3n * BI_POWS[tokens[srcTokenSymbol].decimals],
88
+ 4n * BI_POWS[tokens[srcTokenSymbol].decimals],
89
+ 5n * BI_POWS[tokens[srcTokenSymbol].decimals],
90
+ 6n * BI_POWS[tokens[srcTokenSymbol].decimals],
91
+ 7n * BI_POWS[tokens[srcTokenSymbol].decimals],
92
+ 8n * BI_POWS[tokens[srcTokenSymbol].decimals],
93
+ 9n * BI_POWS[tokens[srcTokenSymbol].decimals],
94
+ 10n * BI_POWS[tokens[srcTokenSymbol].decimals],
95
+ ];
96
+
97
+ beforeAll(async () => {
98
+ blockNumber = await dexHelper.web3Provider.eth.getBlockNumber();
99
+ mWrappedM = new MWrappedM(network, dexKey, dexHelper);
100
+ if (mWrappedM.initializePricing) {
101
+ await mWrappedM.initializePricing();
102
+ }
103
+ });
104
+
105
+ it('getPoolIdentifiers and getPricesVolume SELL M -> WrappedM', async function () {
106
+ await testPricingOnNetwork(
107
+ mWrappedM,
108
+ network,
109
+ dexKey,
110
+ blockNumber,
111
+ srcTokenSymbol,
112
+ destTokenSymbol,
113
+ SwapSide.SELL,
114
+ amounts,
115
+ '',
116
+ );
117
+ });
118
+
119
+ it('getPoolIdentifiers and getPricesVolume SELL WrappedM -> M', async function () {
120
+ await testPricingOnNetwork(
121
+ mWrappedM,
122
+ network,
123
+ dexKey,
124
+ blockNumber,
125
+ destTokenSymbol,
126
+ srcTokenSymbol,
127
+ SwapSide.SELL,
128
+ amounts,
129
+ '',
130
+ );
131
+ });
132
+
133
+ it('getPoolIdentifiers and getPricesVolume BUY M -> WrappedM', async function () {
134
+ await testPricingOnNetwork(
135
+ mWrappedM,
136
+ network,
137
+ dexKey,
138
+ blockNumber,
139
+ srcTokenSymbol,
140
+ destTokenSymbol,
141
+ SwapSide.BUY,
142
+ amounts,
143
+ '',
144
+ );
145
+ });
146
+
147
+ it('getPoolIdentifiers and getPricesVolume BUY WrappedM -> M', async function () {
148
+ await testPricingOnNetwork(
149
+ mWrappedM,
150
+ network,
151
+ dexKey,
152
+ blockNumber,
153
+ destTokenSymbol,
154
+ srcTokenSymbol,
155
+ SwapSide.BUY,
156
+ amounts,
157
+ '',
158
+ );
159
+ });
160
+
161
+ it('getTopPoolsForToken: M', async function () {
162
+ const symbol = srcTokenSymbol;
163
+
164
+ const poolLiquidity = await mWrappedM.getTopPoolsForToken(
165
+ tokens[symbol].address,
166
+ 10,
167
+ );
168
+ console.log(
169
+ `${symbol} Top Pools:`,
170
+ JSON.stringify(poolLiquidity, null, 2),
171
+ );
172
+
173
+ checkPoolsLiquidity(
174
+ poolLiquidity,
175
+ Tokens[network][symbol].address,
176
+ dexKey,
177
+ );
178
+ });
179
+
180
+ it('getTopPoolsForToken: WrappedM', async function () {
181
+ const symbol = destTokenSymbol;
182
+
183
+ const poolLiquidity = await mWrappedM.getTopPoolsForToken(
184
+ tokens[symbol].address,
185
+ 10,
186
+ );
187
+ console.log(
188
+ `${symbol} Top Pools:`,
189
+ JSON.stringify(poolLiquidity, null, 2),
190
+ );
191
+
192
+ checkPoolsLiquidity(
193
+ poolLiquidity,
194
+ Tokens[network][symbol].address,
195
+ dexKey,
196
+ );
197
+ });
198
+ });
199
+ });
@@ -0,0 +1,167 @@
1
+ import { AsyncOrSync } from 'ts-essentials';
2
+ import type {
3
+ Token,
4
+ Address,
5
+ ExchangePrices,
6
+ PoolPrices,
7
+ AdapterExchangeParam,
8
+ Logger,
9
+ PoolLiquidity,
10
+ } from '../../types';
11
+ import { SwapSide, Network } from '../../constants';
12
+ import * as CALLDATA_GAS_COST from '../../calldata-gas-cost';
13
+ import { IDex } from '../idex';
14
+ import { IDexHelper } from '../../dex-helper/idex-helper';
15
+ import type { MTokenData, DexParams } from './types';
16
+ import { SimpleExchange } from '../simple-exchange';
17
+ import { BI_POWS } from '../../bigint-constants';
18
+
19
+ export class MToken extends SimpleExchange implements IDex<MTokenData> {
20
+ readonly hasConstantPriceLargeAmounts = true;
21
+
22
+ readonly needWrapNative = false;
23
+
24
+ readonly isFeeOnTransferSupported = false;
25
+
26
+ logger: Logger;
27
+
28
+ constructor(
29
+ readonly network: Network,
30
+ readonly dexKey: string,
31
+ readonly dexHelper: IDexHelper,
32
+ readonly config: DexParams,
33
+ ) {
34
+ super(dexHelper, dexKey);
35
+ this.logger = dexHelper.getLogger(dexKey);
36
+ }
37
+
38
+ // No initialization needed for constant price
39
+ async initializePricing() {}
40
+
41
+ // Legacy: was only used for V5
42
+ // Returns the list of contract adapters (name and index)
43
+ // for a buy/sell. Return null if there are no adapters.
44
+ getAdapters() {
45
+ return null;
46
+ }
47
+
48
+ // Returns list of pool identifiers that can be used
49
+ // for a given swap. poolIdentifiers must be unique
50
+ // across DEXes. It is recommended to use
51
+ // ${dexKey}_${poolAddress} as a poolIdentifier
52
+ async getPoolIdentifiers(
53
+ from: Token,
54
+ to: Token,
55
+ side: SwapSide,
56
+ blockNumber: number,
57
+ ): Promise<string[]> {
58
+ if (!this.ensureOrigin({ from, to })) {
59
+ return [];
60
+ }
61
+
62
+ return [`${this.dexKey}_${this.config.WrappedM.address}`];
63
+ }
64
+
65
+ // Returns pool prices for amounts.
66
+ // If limitPools is defined only pools in limitPools
67
+ // should be used. If limitPools is undefined then
68
+ // any pools can be used.
69
+ async getPricesVolume(
70
+ from: Token,
71
+ to: Token,
72
+ amounts: bigint[],
73
+ side: SwapSide,
74
+ blockNumber: number,
75
+ limitPools?: string[],
76
+ ): Promise<null | ExchangePrices<MTokenData>> {
77
+ if (!this.ensureOrigin({ from, to })) {
78
+ return null;
79
+ }
80
+
81
+ // 1:1 swap
82
+ // Amounts need no adjustment
83
+ const unitOut = BI_POWS[to.decimals];
84
+
85
+ return [
86
+ {
87
+ unit: unitOut,
88
+ prices: amounts,
89
+ data: {},
90
+ poolAddresses: [this.config.WrappedM.address],
91
+ exchange: this.dexKey,
92
+ gasCost: 70000,
93
+ poolIdentifier: this.dexKey,
94
+ },
95
+ ];
96
+ }
97
+
98
+ // Returns estimated gas cost of calldata for this DEX in multiSwap
99
+ getCalldataGasCost(poolPrices: PoolPrices<MTokenData>): number | number[] {
100
+ return CALLDATA_GAS_COST.DEX_NO_PAYLOAD;
101
+ }
102
+
103
+ // Encode params required by the exchange adapter
104
+ // V5: Used for multiSwap, buy & megaSwap
105
+ // V6: Not used, can be left blank
106
+ // Hint: abiCoder.encodeParameter() could be useful
107
+ getAdapterParam(
108
+ srcToken: string,
109
+ destToken: string,
110
+ srcAmount: string,
111
+ destAmount: string,
112
+ data: MTokenData,
113
+ side: SwapSide,
114
+ ): AdapterExchangeParam {
115
+ const exchange = this.config.WrappedM.address;
116
+ const payload = '0x';
117
+
118
+ return {
119
+ targetExchange: exchange,
120
+ payload,
121
+ networkFee: '0',
122
+ };
123
+ }
124
+
125
+ // Returns list of top pools based on liquidity. Max
126
+ // limit number pools should be returned.
127
+ async getTopPoolsForToken(
128
+ tokenAddress: Address,
129
+ limit: number,
130
+ ): Promise<PoolLiquidity[]> {
131
+ if (!this.isMOrWrappedM(tokenAddress)) {
132
+ return [];
133
+ }
134
+
135
+ const isM =
136
+ tokenAddress.toLowerCase() === this.config.MToken.address.toLowerCase();
137
+
138
+ return [
139
+ {
140
+ exchange: this.dexKey,
141
+ address: this.config.WrappedM.address,
142
+ connectorTokens: [isM ? this.config.WrappedM : this.config.MToken],
143
+ liquidityUSD: 1000000000, // Returning a big number to prefer this DEX
144
+ },
145
+ ];
146
+ }
147
+
148
+ // Checks if the token addresses is M or WrappedM
149
+ isMOrWrappedM(tokenAddress: Address): boolean {
150
+ return (
151
+ tokenAddress.toLowerCase() === this.config.MToken.address.toLowerCase() ||
152
+ tokenAddress.toLowerCase() === this.config.WrappedM.address.toLowerCase()
153
+ );
154
+ }
155
+
156
+ // Ensures that the token addresses are correct
157
+ ensureOrigin(args: Partial<{ from: Token; to: Token }>): boolean {
158
+ const areDifferentAddresses =
159
+ args?.from?.address?.toLowerCase() !== args?.to?.address?.toLowerCase();
160
+
161
+ return (
162
+ areDifferentAddresses &&
163
+ this.isMOrWrappedM(args?.from?.address || '') &&
164
+ this.isMOrWrappedM(args?.to?.address || '')
165
+ );
166
+ }
167
+ }
@@ -0,0 +1,68 @@
1
+ import { Network, SwapSide } from '../../constants';
2
+ import { getDexKeysWithNetwork } from '../../utils';
3
+ import { IDexHelper } from '../../dex-helper/idex-helper';
4
+ import { MToken } from './m-token';
5
+ import type {
6
+ Address,
7
+ DexExchangeParam,
8
+ NumberAsString,
9
+ Token,
10
+ } from '../../types';
11
+ import { Interface } from '@ethersproject/abi';
12
+ import WRAPPED_M_ABI from '../../abi/m-token/WrappedM.abi.json';
13
+ import { Config } from './config';
14
+
15
+ export class MWrappedM extends MToken {
16
+ public static dexKeysWithNetwork: { key: string; networks: Network[] }[] =
17
+ getDexKeysWithNetwork(Config);
18
+
19
+ wrappedMInterface: Interface;
20
+
21
+ constructor(
22
+ readonly network: Network,
23
+ readonly dexKey: string,
24
+ readonly dexHelper: IDexHelper,
25
+ ) {
26
+ const config = Config[dexKey][network];
27
+ super(network, dexKey, dexHelper, config);
28
+
29
+ this.wrappedMInterface = new Interface(WRAPPED_M_ABI);
30
+ }
31
+
32
+ async getDexParam(
33
+ from: Address,
34
+ to: Address,
35
+ srcAmount: NumberAsString,
36
+ destAmount: NumberAsString,
37
+ recipient: Address,
38
+ data: {},
39
+ side: SwapSide,
40
+ ): Promise<DexExchangeParam> {
41
+ if (
42
+ !this.ensureOrigin({
43
+ from: { address: from } as Token,
44
+ to: { address: to } as Token,
45
+ })
46
+ ) {
47
+ throw new Error('Unexpected token addresses');
48
+ }
49
+
50
+ const fn =
51
+ from.toLowerCase() === this.config.MToken.address.toLowerCase()
52
+ ? 'wrap(address, uint256)'
53
+ : 'unwrap(address, uint256)';
54
+
55
+ const exchangeData = this.wrappedMInterface.encodeFunctionData(fn, [
56
+ recipient,
57
+ srcAmount,
58
+ ]);
59
+
60
+ return {
61
+ needWrapNative: false,
62
+ dexFuncHasRecipient: true,
63
+ exchangeData,
64
+ targetExchange: this.config.WrappedM.address,
65
+ returnAmountPos: undefined,
66
+ };
67
+ }
68
+ }
@@ -0,0 +1,10 @@
1
+ import { Address } from '../../types';
2
+
3
+ // Data that returned by the API that can be used for
4
+ // tx building.
5
+ export type MTokenData = {};
6
+
7
+ export type DexParams = {
8
+ MToken: { address: Address; decimals: number };
9
+ WrappedM: { address: Address; decimals: number };
10
+ };
@@ -576,6 +576,11 @@ export const Tokens: {
576
576
  decimals: 18,
577
577
  symbol: 'USDL',
578
578
  },
579
+ M: {
580
+ address: '0x866A2BF4E572CbcF37D5071A7a58503Bfb36be1b',
581
+ decimals: 6,
582
+ symbol: 'M',
583
+ },
579
584
  WrappedM: {
580
585
  address: '0x437cc33344a0B27A429f795ff6B469C72698B291',
581
586
  decimals: 6,
@@ -1879,7 +1884,8 @@ export const Holders: {
1879
1884
  rUSD: '0xEC2eda1C4F981E468ABF62424a10B69B738b498E',
1880
1885
  arUSD: '0xeFc24206053a452e2299BF3b8f964512b041Db4C',
1881
1886
  USD0: '0x224762e69169E425239EeEE0012d1B0e041C123D',
1882
- WrappedM: '0xE0663f2372cAa1459b7ade90812Dc737CE587FA6',
1887
+ M: '0xA370275bb06b7416184A967C5B8593942784CDFe',
1888
+ WrappedM: '0xfAc0973a7D0B847367F6DD46FD470Ee069aAB845',
1883
1889
  'USD0++': '0x2227b6806339906707b43F36a1f07B52FF7Fa776',
1884
1890
  USDM: '0x57F5E098CaD7A3D1Eed53991D4d66C45C9AF7812',
1885
1891
  wUSDM: '0x3B95bC951EE0f553ba487327278cAc44f29715E5',