@paraswap/dex-lib 3.8.21 → 3.8.22-infusion
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/build/dex/index.js +2 -0
- package/build/dex/index.js.map +1 -1
- package/build/dex/lite-psm/lite-psm.js +1 -1
- package/build/dex/lite-psm/lite-psm.js.map +1 -1
- package/build/dex/maker-psm/maker-psm.js +1 -1
- package/build/dex/trader-joe-v2.1/base.js +5 -16
- package/build/dex/trader-joe-v2.1/base.js.map +1 -1
- package/build/dex/trader-joe-v2.1/config.js +2 -2
- package/build/dex/trader-joe-v2.1/types.d.ts +3 -3
- package/package.json +1 -1
- package/src/abi/infusion/InfusionFactory.json +147 -0
- package/src/abi/infusion/InfusionPair.json +658 -0
- package/src/abi/infusion/InfusionRouter.json +442 -0
- package/src/dex/index.ts +2 -0
- package/src/dex/infusion/config.ts +27 -0
- package/src/dex/infusion/infusion-e2e.test.ts +110 -0
- package/src/dex/infusion/infusion-integration.test.ts +232 -0
- package/src/dex/infusion/infusion-stable-pool.ts +91 -0
- package/src/dex/infusion/infusion.ts +749 -0
- package/src/dex/infusion/types.ts +60 -0
- package/src/dex/infusion/utils/isStablePair.ts +18 -0
- package/src/dex/lite-psm/lite-psm.ts +1 -1
- package/src/dex/maker-psm/maker-psm.ts +1 -1
- package/src/dex/trader-joe-v2.1/base.ts +5 -16
- package/src/dex/trader-joe-v2.1/config.ts +2 -2
- package/src/dex/trader-joe-v2.1/types.ts +3 -3
- package/tests/constants-e2e.ts +1 -1
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import dotenv from 'dotenv';
|
|
3
|
+
dotenv.config();
|
|
4
|
+
|
|
5
|
+
import { DummyDexHelper } from '../../dex-helper';
|
|
6
|
+
import { Network, SwapSide } from '../../constants';
|
|
7
|
+
import { checkPoolPrices, checkPoolsLiquidity } from '../../../tests/utils';
|
|
8
|
+
import { BI_POWS } from '../../bigint-constants';
|
|
9
|
+
import { Infusion } from './infusion';
|
|
10
|
+
import { Tokens } from '../../../tests/constants-e2e';
|
|
11
|
+
import { Interface, Result } from '@ethersproject/abi';
|
|
12
|
+
import infusionPairABI from '../../abi/infusion/InfusionPair.json';
|
|
13
|
+
|
|
14
|
+
const amounts18 = [0n, BI_POWS[18], 2000000000000000000n];
|
|
15
|
+
const amounts6 = [0n, BI_POWS[6], 2000000n];
|
|
16
|
+
|
|
17
|
+
function getReaderCalldata(
|
|
18
|
+
exchangeAddress: string,
|
|
19
|
+
readerIface: Interface,
|
|
20
|
+
amounts: bigint[],
|
|
21
|
+
funcName: string,
|
|
22
|
+
tokenIn: string,
|
|
23
|
+
) {
|
|
24
|
+
return amounts.map(amount => ({
|
|
25
|
+
target: exchangeAddress,
|
|
26
|
+
callData: readerIface.encodeFunctionData(funcName, [amount, tokenIn]),
|
|
27
|
+
}));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function decodeReaderResult(
|
|
31
|
+
results: Result,
|
|
32
|
+
readerIface: Interface,
|
|
33
|
+
funcName: string,
|
|
34
|
+
) {
|
|
35
|
+
return results.map(result => {
|
|
36
|
+
const parsed = readerIface.decodeFunctionResult(funcName, result);
|
|
37
|
+
return BigInt(parsed[0]._hex);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const constructCheckOnChainPricing =
|
|
42
|
+
(dexHelper: DummyDexHelper) =>
|
|
43
|
+
async (
|
|
44
|
+
infusion: Infusion,
|
|
45
|
+
funcName: string,
|
|
46
|
+
blockNumber: number,
|
|
47
|
+
prices: bigint[],
|
|
48
|
+
exchangeAddress: string,
|
|
49
|
+
tokenIn: string,
|
|
50
|
+
amounts: bigint[],
|
|
51
|
+
) => {
|
|
52
|
+
const readerIface = new Interface(infusionPairABI as any);
|
|
53
|
+
|
|
54
|
+
const readerCallData = getReaderCalldata(
|
|
55
|
+
exchangeAddress,
|
|
56
|
+
readerIface,
|
|
57
|
+
amounts.slice(1),
|
|
58
|
+
funcName,
|
|
59
|
+
tokenIn,
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const readerResult = (
|
|
63
|
+
await dexHelper.multiContract.methods
|
|
64
|
+
.aggregate(readerCallData)
|
|
65
|
+
.call({}, blockNumber)
|
|
66
|
+
).returnData;
|
|
67
|
+
const expectedPrices = [0n].concat(
|
|
68
|
+
decodeReaderResult(readerResult, readerIface, funcName),
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
console.log('ON-CHAIN PRICES: ', expectedPrices);
|
|
72
|
+
|
|
73
|
+
expect(prices.map(p => p.toString())).toEqual(
|
|
74
|
+
expectedPrices.map(p => p.toString()),
|
|
75
|
+
);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
describe('Infusion integration tests', () => {
|
|
79
|
+
describe('Base', () => {
|
|
80
|
+
const network = Network.BASE;
|
|
81
|
+
const dexHelper = new DummyDexHelper(network);
|
|
82
|
+
const checkOnChainPricing = constructCheckOnChainPricing(dexHelper);
|
|
83
|
+
|
|
84
|
+
describe('Infusion', () => {
|
|
85
|
+
const dexKey = 'Infusion';
|
|
86
|
+
const infusion = new Infusion(network, dexKey, dexHelper);
|
|
87
|
+
|
|
88
|
+
describe('UniswapV2 like pool', function () {
|
|
89
|
+
const TokenASymbol = 'USDC';
|
|
90
|
+
const tokenA = Tokens[network][TokenASymbol];
|
|
91
|
+
const TokenBSymbol = 'WETH';
|
|
92
|
+
const tokenB = Tokens[network][TokenBSymbol];
|
|
93
|
+
|
|
94
|
+
const amounts = amounts18;
|
|
95
|
+
|
|
96
|
+
it('getPoolIdentifiers and getPricesVolume SELL', async function () {
|
|
97
|
+
const blocknumber = await dexHelper.web3Provider.eth.getBlockNumber();
|
|
98
|
+
const pools = await infusion.getPoolIdentifiers(
|
|
99
|
+
tokenA,
|
|
100
|
+
tokenB,
|
|
101
|
+
SwapSide.SELL,
|
|
102
|
+
blocknumber,
|
|
103
|
+
);
|
|
104
|
+
console.log(
|
|
105
|
+
`${TokenASymbol} <> ${TokenBSymbol} Pool Identifiers: `,
|
|
106
|
+
pools,
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
expect(pools.length).toBeGreaterThan(0);
|
|
110
|
+
|
|
111
|
+
const poolPrices = await infusion.getPricesVolume(
|
|
112
|
+
tokenA,
|
|
113
|
+
tokenB,
|
|
114
|
+
amounts,
|
|
115
|
+
SwapSide.SELL,
|
|
116
|
+
blocknumber,
|
|
117
|
+
pools,
|
|
118
|
+
);
|
|
119
|
+
console.log(
|
|
120
|
+
`${TokenASymbol} <> ${TokenBSymbol} Pool Prices: `,
|
|
121
|
+
poolPrices,
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
expect(poolPrices).not.toBeNull();
|
|
125
|
+
checkPoolPrices(poolPrices!, amounts, SwapSide.SELL, dexKey);
|
|
126
|
+
|
|
127
|
+
// Check if onchain pricing equals to calculated ones
|
|
128
|
+
|
|
129
|
+
for (const poolPrice of poolPrices || []) {
|
|
130
|
+
await checkOnChainPricing(
|
|
131
|
+
infusion,
|
|
132
|
+
'getAmountOut',
|
|
133
|
+
blocknumber,
|
|
134
|
+
poolPrice.prices,
|
|
135
|
+
poolPrice.poolAddresses![0],
|
|
136
|
+
tokenA.address,
|
|
137
|
+
amounts,
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('getTopPoolsForToken', async function () {
|
|
143
|
+
const dexHelper = new DummyDexHelper(Network.BASE);
|
|
144
|
+
const infusion = new Infusion(Network.BASE, dexKey, dexHelper);
|
|
145
|
+
|
|
146
|
+
const poolLiquidity = await infusion.getTopPoolsForToken(
|
|
147
|
+
'0x4200000000000000000000000000000000000006',
|
|
148
|
+
10,
|
|
149
|
+
);
|
|
150
|
+
console.log('WETH Top Pools:', poolLiquidity);
|
|
151
|
+
|
|
152
|
+
checkPoolsLiquidity(
|
|
153
|
+
poolLiquidity,
|
|
154
|
+
'0x4200000000000000000000000000000000000006',
|
|
155
|
+
dexKey,
|
|
156
|
+
);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
describe('Curve like stable pool', function () {
|
|
161
|
+
const TokenASymbol = 'USDbC';
|
|
162
|
+
const tokenA = Tokens[network][TokenASymbol];
|
|
163
|
+
const TokenBSymbol = 'USDC';
|
|
164
|
+
const tokenB = Tokens[network][TokenBSymbol];
|
|
165
|
+
|
|
166
|
+
const amounts = amounts6;
|
|
167
|
+
|
|
168
|
+
it('getPoolIdentifiers and getPricesVolume SELL', async function () {
|
|
169
|
+
const blocknumber = await dexHelper.web3Provider.eth.getBlockNumber();
|
|
170
|
+
const pools = await infusion.getPoolIdentifiers(
|
|
171
|
+
tokenA,
|
|
172
|
+
tokenB,
|
|
173
|
+
SwapSide.SELL,
|
|
174
|
+
blocknumber,
|
|
175
|
+
);
|
|
176
|
+
console.log(
|
|
177
|
+
`${TokenASymbol} <> ${TokenBSymbol} Pool Identifiers: `,
|
|
178
|
+
pools,
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
expect(pools.length).toBeGreaterThan(0);
|
|
182
|
+
|
|
183
|
+
const poolPrices = await infusion.getPricesVolume(
|
|
184
|
+
tokenA,
|
|
185
|
+
tokenB,
|
|
186
|
+
amounts,
|
|
187
|
+
SwapSide.SELL,
|
|
188
|
+
blocknumber,
|
|
189
|
+
pools,
|
|
190
|
+
);
|
|
191
|
+
console.log(
|
|
192
|
+
`${TokenASymbol} <> ${TokenBSymbol} Pool Prices: `,
|
|
193
|
+
poolPrices,
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
expect(poolPrices).not.toBeNull();
|
|
197
|
+
checkPoolPrices(poolPrices!, amounts, SwapSide.SELL, dexKey);
|
|
198
|
+
|
|
199
|
+
// Check if onchain pricing equals to calculated ones
|
|
200
|
+
for (const poolPrice of poolPrices || []) {
|
|
201
|
+
await checkOnChainPricing(
|
|
202
|
+
infusion,
|
|
203
|
+
'getAmountOut',
|
|
204
|
+
blocknumber,
|
|
205
|
+
poolPrice.prices,
|
|
206
|
+
poolPrice.poolAddresses![0],
|
|
207
|
+
tokenA.address,
|
|
208
|
+
amounts,
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('getTopPoolsForToken', async function () {
|
|
214
|
+
const dexHelper = new DummyDexHelper(Network.BASE);
|
|
215
|
+
const infusion = new Infusion(Network.BASE, dexKey, dexHelper);
|
|
216
|
+
|
|
217
|
+
const poolLiquidity = await infusion.getTopPoolsForToken(
|
|
218
|
+
'0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
|
|
219
|
+
10,
|
|
220
|
+
);
|
|
221
|
+
console.log('WETH Top Pools:', poolLiquidity);
|
|
222
|
+
|
|
223
|
+
checkPoolsLiquidity(
|
|
224
|
+
poolLiquidity,
|
|
225
|
+
'0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
|
|
226
|
+
dexKey,
|
|
227
|
+
);
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
});
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { BI_POWS } from '../../bigint-constants';
|
|
2
|
+
import { RESERVE_LIMIT } from '../uniswap-v2/uniswap-v2';
|
|
3
|
+
import { InfusionPoolOrderedParams } from './types';
|
|
4
|
+
|
|
5
|
+
const e18 = BI_POWS[18];
|
|
6
|
+
|
|
7
|
+
function _k(
|
|
8
|
+
x: bigint,
|
|
9
|
+
y: bigint,
|
|
10
|
+
decimals0: bigint,
|
|
11
|
+
decimals1: bigint,
|
|
12
|
+
): bigint {
|
|
13
|
+
const _x = (x * e18) / decimals0;
|
|
14
|
+
const _y = (y * e18) / decimals1;
|
|
15
|
+
const _a = (_x * _y) / e18;
|
|
16
|
+
const _b = (_x * _x) / e18 + (_y * _y) / e18;
|
|
17
|
+
// x3y+y3x >= k
|
|
18
|
+
return (_a * _b) / e18;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function _f(x0: bigint, y: bigint): bigint {
|
|
22
|
+
return (
|
|
23
|
+
(x0 * ((((y * y) / e18) * y) / e18)) / e18 +
|
|
24
|
+
(((((x0 * x0) / e18) * x0) / e18) * y) / e18
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function _d(x0: bigint, y: bigint): bigint {
|
|
29
|
+
return (3n * x0 * ((y * y) / e18)) / e18 + (((x0 * x0) / e18) * x0) / e18;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function _getY(x0: bigint, xy: bigint, y: bigint): bigint {
|
|
33
|
+
for (let i = 0; i < 255; i++) {
|
|
34
|
+
const yPrev = y;
|
|
35
|
+
const k = _f(x0, y);
|
|
36
|
+
if (k < xy) {
|
|
37
|
+
const dy = ((xy - k) * e18) / _d(x0, y);
|
|
38
|
+
y = y + dy;
|
|
39
|
+
} else {
|
|
40
|
+
const dy = ((k - xy) * e18) / _d(x0, y);
|
|
41
|
+
y = y - dy;
|
|
42
|
+
}
|
|
43
|
+
if (_closeTo(y, yPrev, 1n)) {
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return y;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function _closeTo(a: bigint, b: bigint, target: bigint) {
|
|
51
|
+
if (a > b) {
|
|
52
|
+
if (a - b <= target) {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
} else {
|
|
56
|
+
if (b - a <= target) {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export class InfusionStablePool {
|
|
64
|
+
static async getSellPrice(
|
|
65
|
+
priceParams: InfusionPoolOrderedParams,
|
|
66
|
+
srcAmount: bigint,
|
|
67
|
+
feeFactor: number,
|
|
68
|
+
): Promise<bigint> {
|
|
69
|
+
const { reservesIn, reservesOut, decimalsIn, decimalsOut, fee } =
|
|
70
|
+
priceParams;
|
|
71
|
+
|
|
72
|
+
// FIXME: some dexes use uint112 others use uint256
|
|
73
|
+
if (BigInt(reservesIn) + srcAmount > RESERVE_LIMIT) {
|
|
74
|
+
return 0n;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const amountIn = srcAmount - (srcAmount * BigInt(fee)) / BigInt(feeFactor);
|
|
78
|
+
|
|
79
|
+
const reservesInN = BigInt(reservesIn);
|
|
80
|
+
const reservesOutN = BigInt(reservesOut);
|
|
81
|
+
const decimalsInN = 10n ** BigInt(decimalsIn);
|
|
82
|
+
const decimalsOutN = 10n ** BigInt(decimalsOut);
|
|
83
|
+
|
|
84
|
+
const xy = _k(reservesInN, reservesOutN, decimalsInN, decimalsOutN);
|
|
85
|
+
const reserveA = (reservesInN * e18) / decimalsInN;
|
|
86
|
+
const reserveB = (reservesOutN * e18) / decimalsOutN;
|
|
87
|
+
const amountInNorm = (amountIn * e18) / decimalsInN;
|
|
88
|
+
const y = reserveB - _getY(amountInNorm + reserveA, xy, reserveB);
|
|
89
|
+
return (y * decimalsOutN) / e18;
|
|
90
|
+
}
|
|
91
|
+
}
|