@paraswap/dex-lib 2.42.26 → 2.42.27
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/abi/smardex/all/smardex-router.json +648 -0
- package/build/abi/smardex/layer-1/smardex-factory.json +242 -0
- package/build/abi/smardex/layer-1/smardex-pool.json +506 -0
- package/build/abi/smardex/layer-2/smardex-factory.json +185 -0
- package/build/abi/smardex/layer-2/smardex-pool.json +578 -0
- package/build/config.js +20 -10
- package/build/config.js.map +1 -1
- package/build/dex/index.js +2 -0
- package/build/dex/index.js.map +1 -1
- package/build/dex/smardex/config.d.ts +4 -0
- package/build/dex/smardex/config.js +64 -0
- package/build/dex/smardex/config.js.map +1 -0
- package/build/dex/smardex/constants.d.ts +18 -0
- package/build/dex/smardex/constants.js +33 -0
- package/build/dex/smardex/constants.js.map +1 -0
- package/build/dex/smardex/sdk/constants.d.ts +6 -0
- package/build/dex/smardex/sdk/constants.js +13 -0
- package/build/dex/smardex/sdk/constants.js.map +1 -0
- package/build/dex/smardex/sdk/core.d.ts +113 -0
- package/build/dex/smardex/sdk/core.js +499 -0
- package/build/dex/smardex/sdk/core.js.map +1 -0
- package/build/dex/smardex/sdk/errors.d.ts +15 -0
- package/build/dex/smardex/sdk/errors.js +22 -0
- package/build/dex/smardex/sdk/errors.js.map +1 -0
- package/build/dex/smardex/sdk/types.d.ts +62 -0
- package/build/dex/smardex/sdk/types.js +3 -0
- package/build/dex/smardex/sdk/types.js.map +1 -0
- package/build/dex/smardex/sdk/utils.d.ts +44 -0
- package/build/dex/smardex/sdk/utils.js +133 -0
- package/build/dex/smardex/sdk/utils.js.map +1 -0
- package/build/dex/smardex/smardex-event-pool.d.ts +28 -0
- package/build/dex/smardex/smardex-event-pool.js +119 -0
- package/build/dex/smardex/smardex-event-pool.js.map +1 -0
- package/build/dex/smardex/smardex.d.ts +65 -0
- package/build/dex/smardex/smardex.js +519 -0
- package/build/dex/smardex/smardex.js.map +1 -0
- package/build/dex/smardex/types.d.ts +55 -0
- package/build/dex/smardex/types.js +3 -0
- package/build/dex/smardex/types.js.map +1 -0
- package/build/types.d.ts +1 -0
- package/package.json +3 -2
- package/src/abi/smardex/all/smardex-router.json +648 -0
- package/src/abi/smardex/layer-1/smardex-factory.json +242 -0
- package/src/abi/smardex/layer-1/smardex-pool.json +506 -0
- package/src/abi/smardex/layer-2/smardex-factory.json +185 -0
- package/src/abi/smardex/layer-2/smardex-pool.json +578 -0
- package/src/config.ts +21 -10
- package/src/dex/index.ts +2 -0
- package/src/dex/smardex/config.ts +67 -0
- package/src/dex/smardex/constants.ts +35 -0
- package/src/dex/smardex/sdk/constants.ts +10 -0
- package/src/dex/smardex/sdk/core.ts +832 -0
- package/src/dex/smardex/sdk/errors.ts +18 -0
- package/src/dex/smardex/sdk/types.ts +68 -0
- package/src/dex/smardex/sdk/utils.ts +156 -0
- package/src/dex/smardex/smardex-e2e.test.ts +275 -0
- package/src/dex/smardex/smardex-event-pool.ts +164 -0
- package/src/dex/smardex/smardex-events.test.ts +297 -0
- package/src/dex/smardex/smardex-integration.test.ts +258 -0
- package/src/dex/smardex/smardex.ts +770 -0
- package/src/dex/smardex/types.ts +69 -0
- package/src/types.ts +1 -0
- package/tests/constants-e2e.ts +26 -1
- package/.vscode/launch.json +0 -54
- package/.vscode/settings.json +0 -2
- package/.vscode/tasks.json +0 -15
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SmardexError class
|
|
3
|
+
*
|
|
4
|
+
* @class SmardexError extends Error
|
|
5
|
+
*/
|
|
6
|
+
export default class SmardexError extends Error {
|
|
7
|
+
/**
|
|
8
|
+
* Create SmardexError object
|
|
9
|
+
*
|
|
10
|
+
* @param {string} message - error message
|
|
11
|
+
* @param {string} [errorName=SmarDexSDK] - identifier for the error
|
|
12
|
+
* @returns {SmardexError} SmarDex Error object
|
|
13
|
+
*/
|
|
14
|
+
constructor(message: string, errorName = 'SmarDexSDK') {
|
|
15
|
+
super(message);
|
|
16
|
+
this.name = errorName;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { TradeType } from './constants';
|
|
2
|
+
|
|
3
|
+
export interface Pair {
|
|
4
|
+
address?: string;
|
|
5
|
+
token0: string;
|
|
6
|
+
token1: string;
|
|
7
|
+
reserve0: bigint;
|
|
8
|
+
reserve1: bigint;
|
|
9
|
+
reserve0LastFictive: bigint;
|
|
10
|
+
reserve1LastFictive: bigint;
|
|
11
|
+
priceAverageLastTimestamp: number;
|
|
12
|
+
priceAverage0: bigint;
|
|
13
|
+
priceAverage1: bigint;
|
|
14
|
+
forcedPriceAverageTimestamp?: number;
|
|
15
|
+
prevReserveFic0?: bigint;
|
|
16
|
+
prevReserveFic1?: bigint;
|
|
17
|
+
feesLP: bigint;
|
|
18
|
+
feesPool: bigint;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface CurrencyAmount {
|
|
22
|
+
currency: string;
|
|
23
|
+
amount: bigint;
|
|
24
|
+
amountMax?: bigint;
|
|
25
|
+
newRes0?: bigint;
|
|
26
|
+
newRes1?: bigint;
|
|
27
|
+
newRes0Fic?: bigint;
|
|
28
|
+
newRes1Fic?: bigint;
|
|
29
|
+
newPriceAverage0?: bigint;
|
|
30
|
+
newPriceAverage1?: bigint;
|
|
31
|
+
forcedPriceAverageTimestamp?: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface BestTradeOptions {
|
|
35
|
+
maxNumResults?: number; // how many results to return
|
|
36
|
+
maxHops?: number; // the maximum number of hops for the swap
|
|
37
|
+
arbitrage?: boolean; // consider arbitrage loops or not
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface Route {
|
|
41
|
+
pairs: Pair[];
|
|
42
|
+
path: string[];
|
|
43
|
+
input: string;
|
|
44
|
+
output: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface Trade {
|
|
48
|
+
route: Route;
|
|
49
|
+
amountIn: CurrencyAmount;
|
|
50
|
+
amountOut: CurrencyAmount;
|
|
51
|
+
tradeType: TradeType;
|
|
52
|
+
priceImpact?: bigint;
|
|
53
|
+
gasFeesUSD?: bigint;
|
|
54
|
+
amountInUSD?: bigint;
|
|
55
|
+
amountOutUSD?: bigint;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface GasEstimateData {
|
|
59
|
+
gasPrice: bigint;
|
|
60
|
+
gasQuantitiesFirstHop: number;
|
|
61
|
+
gasQuantitiesAdditionalHop: number;
|
|
62
|
+
nativeTokenPrice: bigint;
|
|
63
|
+
nativeTokenDecimals: number;
|
|
64
|
+
inputTokenPrice: bigint;
|
|
65
|
+
inputTokenDecimals: number;
|
|
66
|
+
outputTokenPrice: bigint;
|
|
67
|
+
outputTokenDecimals: number;
|
|
68
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { parseUnits } from 'ethers/lib/utils';
|
|
2
|
+
|
|
3
|
+
import { CurrencyAmount, Trade } from './types';
|
|
4
|
+
|
|
5
|
+
// Constants to compute approximate equality
|
|
6
|
+
const APPROX_EQ_PRECISION = 1n;
|
|
7
|
+
const APPROX_EQ_BASE_PRECISION = 1000000n;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Computes logarithm in base 2 for a given positive number.
|
|
11
|
+
* Result is rounded down.
|
|
12
|
+
*
|
|
13
|
+
* @param {bigint} value - value to compute the log2
|
|
14
|
+
* @returns {bigint} the log in base 2 of the value, 0 if given 0.
|
|
15
|
+
*/
|
|
16
|
+
/* eslint-disable no-param-reassign */
|
|
17
|
+
export function log2(value: bigint): bigint {
|
|
18
|
+
let result = 0n;
|
|
19
|
+
|
|
20
|
+
if (value >> 128n > 0n) {
|
|
21
|
+
value >>= 128n;
|
|
22
|
+
result += 128n;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (value >> 64n > 0n) {
|
|
26
|
+
value >>= 64n;
|
|
27
|
+
result += 64n;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (value >> 32n > 0n) {
|
|
31
|
+
value >>= 32n;
|
|
32
|
+
result += 32n;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (value >> 16n > 0n) {
|
|
36
|
+
value >>= 16n;
|
|
37
|
+
result += 16n;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (value >> 8n > 0n) {
|
|
41
|
+
value >>= 8n;
|
|
42
|
+
result += 8n;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (value >> 4n > 0n) {
|
|
46
|
+
value >>= 4n;
|
|
47
|
+
result += 4n;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (value >> 2n > 0n) {
|
|
51
|
+
value >>= 2n;
|
|
52
|
+
result += 2n;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (value >> 1n > 0n) {
|
|
56
|
+
result += 1n;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return result;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Computes the square root of a number. If the number is not a perfect square, the value is rounded down.
|
|
64
|
+
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
|
|
65
|
+
*
|
|
66
|
+
* @param {bigint} value - value to compute the square root
|
|
67
|
+
* @returns {bigint} the square root of the value
|
|
68
|
+
*/
|
|
69
|
+
export function sqrt(value: bigint): bigint {
|
|
70
|
+
if (value === 0n) {
|
|
71
|
+
return 0n;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
|
|
75
|
+
//
|
|
76
|
+
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
|
|
77
|
+
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
|
|
78
|
+
//
|
|
79
|
+
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
|
|
80
|
+
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
|
|
81
|
+
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
|
|
82
|
+
//
|
|
83
|
+
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
|
|
84
|
+
let result = 1n << (log2(value) / 2n);
|
|
85
|
+
|
|
86
|
+
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
|
|
87
|
+
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
|
|
88
|
+
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
|
|
89
|
+
// into the expected uint128 result.
|
|
90
|
+
result = (result + value / result) >> 1n;
|
|
91
|
+
result = (result + value / result) >> 1n;
|
|
92
|
+
result = (result + value / result) >> 1n;
|
|
93
|
+
result = (result + value / result) >> 1n;
|
|
94
|
+
result = (result + value / result) >> 1n;
|
|
95
|
+
result = (result + value / result) >> 1n;
|
|
96
|
+
result = (result + value / result) >> 1n;
|
|
97
|
+
|
|
98
|
+
return result < value / result ? result : value / result;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Evaluates the equality of two numbers at a precision of 1/1_000_000
|
|
103
|
+
*
|
|
104
|
+
* @param {bigint} x - value to compare
|
|
105
|
+
* @param {bigint} y - value to compare
|
|
106
|
+
* @returns {boolean} true if numbers are approximatively equal at 1/1_000_000, false otherwise
|
|
107
|
+
*/
|
|
108
|
+
export function approxEq(x: bigint, y: bigint): true | false {
|
|
109
|
+
return x > y
|
|
110
|
+
? x < y + (y * APPROX_EQ_PRECISION) / APPROX_EQ_BASE_PRECISION
|
|
111
|
+
: y < x + (x * APPROX_EQ_PRECISION) / APPROX_EQ_BASE_PRECISION;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Evaluates the equality of two ratio numbers at a precision of 1/1_000_000. xNum / xDen ~= yNum / yDen
|
|
116
|
+
*
|
|
117
|
+
* @param {bigint} _xNum - first number numerator
|
|
118
|
+
* @param {bigint} _xDen - first number denominator
|
|
119
|
+
* @param {bigint} _yNum - second number numerator
|
|
120
|
+
* @param {bigint} _yDen - second number denominator
|
|
121
|
+
* @returns {boolean} true if the two ratios are approximatively equal at 1/1_000_000, false otherwise
|
|
122
|
+
*/
|
|
123
|
+
export function ratioApproxEq(
|
|
124
|
+
_xNum: bigint,
|
|
125
|
+
_xDen: bigint,
|
|
126
|
+
_yNum: bigint,
|
|
127
|
+
_yDen: bigint,
|
|
128
|
+
): true | false {
|
|
129
|
+
return approxEq(_xNum * _yDen, _xDen * _yNum);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Computes the ratio of two numbers
|
|
134
|
+
*
|
|
135
|
+
* @param {bigint} numerator - numerator number
|
|
136
|
+
* @param {bigint} denominator - denominator number
|
|
137
|
+
* @param {number} decimals - decimals
|
|
138
|
+
* @returns {bigint} ratio of the two numbers. returns 0 if denominator is 0
|
|
139
|
+
*/
|
|
140
|
+
export function priceRatio(
|
|
141
|
+
numerator: bigint,
|
|
142
|
+
denominator: bigint,
|
|
143
|
+
decimals = 18,
|
|
144
|
+
) {
|
|
145
|
+
if (denominator === 0n) {
|
|
146
|
+
return 0n;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return (
|
|
150
|
+
BigInt(parseUnits(numerator.toString(), decimals).toString()) / denominator
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function abs(value: bigint) {
|
|
155
|
+
return value === -0n || value < 0n ? -value : value;
|
|
156
|
+
}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import dotenv from 'dotenv';
|
|
2
|
+
dotenv.config();
|
|
3
|
+
|
|
4
|
+
import { testE2E } from '../../../tests/utils-e2e';
|
|
5
|
+
import { Tokens, Holders } from '../../../tests/constants-e2e';
|
|
6
|
+
import { Network, ContractMethod, SwapSide } from '../../constants';
|
|
7
|
+
import { StaticJsonRpcProvider } from '@ethersproject/providers';
|
|
8
|
+
import { generateConfig } from '../../config';
|
|
9
|
+
|
|
10
|
+
function testForNetwork(
|
|
11
|
+
network: Network,
|
|
12
|
+
dexKey: string,
|
|
13
|
+
pairs: { name: string; sellAmount: string; buyAmount: string }[][],
|
|
14
|
+
) {
|
|
15
|
+
const provider = new StaticJsonRpcProvider(
|
|
16
|
+
generateConfig(network).privateHttpProvider,
|
|
17
|
+
network,
|
|
18
|
+
);
|
|
19
|
+
const tokens = Tokens[network];
|
|
20
|
+
const holders = Holders[network];
|
|
21
|
+
|
|
22
|
+
const sideToContractMethods = new Map([
|
|
23
|
+
[
|
|
24
|
+
SwapSide.SELL,
|
|
25
|
+
[
|
|
26
|
+
ContractMethod.simpleSwap,
|
|
27
|
+
ContractMethod.multiSwap,
|
|
28
|
+
ContractMethod.megaSwap,
|
|
29
|
+
],
|
|
30
|
+
],
|
|
31
|
+
[SwapSide.BUY, [ContractMethod.simpleBuy, ContractMethod.buy]],
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
sideToContractMethods.forEach((contractMethods, side) =>
|
|
35
|
+
describe(`${side}`, () => {
|
|
36
|
+
contractMethods.forEach((contractMethod: ContractMethod) => {
|
|
37
|
+
pairs.forEach(pair => {
|
|
38
|
+
describe(`${contractMethod}`, () => {
|
|
39
|
+
it(`${pair[0].name} -> ${pair[1].name}`, async () => {
|
|
40
|
+
await testE2E(
|
|
41
|
+
tokens[pair[0].name],
|
|
42
|
+
tokens[pair[1].name],
|
|
43
|
+
holders[pair[0].name],
|
|
44
|
+
side === SwapSide.SELL ? pair[0].sellAmount : pair[0].buyAmount,
|
|
45
|
+
side,
|
|
46
|
+
dexKey,
|
|
47
|
+
contractMethod,
|
|
48
|
+
network,
|
|
49
|
+
provider,
|
|
50
|
+
);
|
|
51
|
+
});
|
|
52
|
+
it(`${pair[1].name} -> ${pair[0].name}`, async () => {
|
|
53
|
+
await testE2E(
|
|
54
|
+
tokens[pair[1].name],
|
|
55
|
+
tokens[pair[0].name],
|
|
56
|
+
holders[pair[1].name],
|
|
57
|
+
side === SwapSide.SELL ? pair[1].sellAmount : pair[1].buyAmount,
|
|
58
|
+
side,
|
|
59
|
+
dexKey,
|
|
60
|
+
contractMethod,
|
|
61
|
+
network,
|
|
62
|
+
provider,
|
|
63
|
+
);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
}),
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
describe('SmarDex E2E', () => {
|
|
73
|
+
const dexKey = 'SmarDex';
|
|
74
|
+
|
|
75
|
+
describe('MAINNET', () => {
|
|
76
|
+
const network = Network.MAINNET;
|
|
77
|
+
|
|
78
|
+
const pairs = [
|
|
79
|
+
[
|
|
80
|
+
{
|
|
81
|
+
name: 'USDT',
|
|
82
|
+
sellAmount: '1200000000',
|
|
83
|
+
buyAmount: '120000',
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: 'SDEX',
|
|
87
|
+
sellAmount: '300000000000000000000000',
|
|
88
|
+
buyAmount: '30000000000',
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
[
|
|
92
|
+
{
|
|
93
|
+
name: 'SDEX',
|
|
94
|
+
sellAmount: '300000000000000000000000',
|
|
95
|
+
buyAmount: '30000000000',
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: 'ETH',
|
|
99
|
+
sellAmount: '11000000000000000',
|
|
100
|
+
buyAmount: '1100000000',
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
testForNetwork(network, dexKey, pairs);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe('ARBITRUM', () => {
|
|
109
|
+
const network = Network.ARBITRUM;
|
|
110
|
+
|
|
111
|
+
const pairs = [
|
|
112
|
+
[
|
|
113
|
+
{
|
|
114
|
+
name: 'USDC',
|
|
115
|
+
sellAmount: '1200000000',
|
|
116
|
+
buyAmount: '120000',
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
name: 'SDEX',
|
|
120
|
+
sellAmount: '300000000000000000000000',
|
|
121
|
+
buyAmount: '30000000000',
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
[
|
|
125
|
+
{
|
|
126
|
+
name: 'SDEX',
|
|
127
|
+
sellAmount: '300000000000000000000000',
|
|
128
|
+
buyAmount: '11000000000000000',
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
name: 'ETH',
|
|
132
|
+
sellAmount: '11000000000000000',
|
|
133
|
+
buyAmount: '11000000',
|
|
134
|
+
},
|
|
135
|
+
],
|
|
136
|
+
];
|
|
137
|
+
|
|
138
|
+
testForNetwork(network, dexKey, pairs);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
describe('BSC', () => {
|
|
142
|
+
const network = Network.BSC;
|
|
143
|
+
|
|
144
|
+
const pairs = [
|
|
145
|
+
[
|
|
146
|
+
{
|
|
147
|
+
name: 'USDT',
|
|
148
|
+
sellAmount: '1200000000000000000000',
|
|
149
|
+
buyAmount: '300000000000000000000000',
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
name: 'SDEX',
|
|
153
|
+
sellAmount: '300000000000000000000000',
|
|
154
|
+
buyAmount: '120000000000000000000',
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
[
|
|
158
|
+
{
|
|
159
|
+
name: 'SDEX',
|
|
160
|
+
sellAmount: '300000000000000000000000',
|
|
161
|
+
buyAmount: '50000000000000000',
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
name: 'bBTC',
|
|
165
|
+
sellAmount: '50000000000000000',
|
|
166
|
+
buyAmount: '300000000000000000000000',
|
|
167
|
+
},
|
|
168
|
+
],
|
|
169
|
+
[
|
|
170
|
+
{
|
|
171
|
+
name: 'USDT',
|
|
172
|
+
sellAmount: '1500000000000000000000',
|
|
173
|
+
buyAmount: '20000000000000000000',
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
name: 'BNB',
|
|
177
|
+
sellAmount: '20000000000000000000',
|
|
178
|
+
buyAmount: '1500000000000000000000',
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
];
|
|
182
|
+
|
|
183
|
+
testForNetwork(network, dexKey, pairs);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
describe('POLYGON', () => {
|
|
187
|
+
const network = Network.POLYGON;
|
|
188
|
+
|
|
189
|
+
const pairs = [
|
|
190
|
+
[
|
|
191
|
+
{
|
|
192
|
+
name: 'WETH',
|
|
193
|
+
sellAmount: '1500000000000000000',
|
|
194
|
+
buyAmount: '250000000000000000000000',
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
name: 'SDEX',
|
|
198
|
+
sellAmount: '250000000000000000000000',
|
|
199
|
+
buyAmount: '1500000000000000000',
|
|
200
|
+
},
|
|
201
|
+
],
|
|
202
|
+
[
|
|
203
|
+
{
|
|
204
|
+
name: 'USDC',
|
|
205
|
+
sellAmount: '2500000000',
|
|
206
|
+
buyAmount: '300000000000000000000000',
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
name: 'SDEX',
|
|
210
|
+
sellAmount: '300000000000000000000000',
|
|
211
|
+
buyAmount: '2500000000',
|
|
212
|
+
},
|
|
213
|
+
],
|
|
214
|
+
[
|
|
215
|
+
{
|
|
216
|
+
name: 'USDC',
|
|
217
|
+
sellAmount: '2500000000',
|
|
218
|
+
buyAmount: '8000000000000000000000',
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
name: 'MATIC',
|
|
222
|
+
sellAmount: '8000000000000000000000',
|
|
223
|
+
buyAmount: '2500000000',
|
|
224
|
+
},
|
|
225
|
+
],
|
|
226
|
+
[
|
|
227
|
+
{
|
|
228
|
+
name: 'SDEX',
|
|
229
|
+
sellAmount: '190000000000000000000000',
|
|
230
|
+
buyAmount: '8000000',
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
name: 'WBTC',
|
|
234
|
+
sellAmount: '8000000',
|
|
235
|
+
buyAmount: '190000000000000000000000',
|
|
236
|
+
},
|
|
237
|
+
],
|
|
238
|
+
];
|
|
239
|
+
|
|
240
|
+
testForNetwork(network, dexKey, pairs);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
describe('BASE', () => {
|
|
244
|
+
const network = Network.BASE;
|
|
245
|
+
|
|
246
|
+
const pairs = [
|
|
247
|
+
[
|
|
248
|
+
{
|
|
249
|
+
name: 'WETH',
|
|
250
|
+
sellAmount: '1500000000000000000',
|
|
251
|
+
buyAmount: '2500000000',
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
name: 'USDbC',
|
|
255
|
+
sellAmount: '2500000000',
|
|
256
|
+
buyAmount: '1500000000000000000',
|
|
257
|
+
},
|
|
258
|
+
],
|
|
259
|
+
[
|
|
260
|
+
{
|
|
261
|
+
name: 'WETH',
|
|
262
|
+
sellAmount: '1500000000000000000',
|
|
263
|
+
buyAmount: '250000000000000000000000',
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
name: 'SDEX',
|
|
267
|
+
sellAmount: '250000000000000000000000',
|
|
268
|
+
buyAmount: '1500000000000000000',
|
|
269
|
+
},
|
|
270
|
+
],
|
|
271
|
+
];
|
|
272
|
+
|
|
273
|
+
testForNetwork(network, dexKey, pairs);
|
|
274
|
+
});
|
|
275
|
+
});
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { StatefulEventSubscriber } from '../../stateful-event-subscriber';
|
|
2
|
+
import { SmardexFees, SmardexPoolState } from './types';
|
|
3
|
+
import { AbiCoder, Interface } from '@ethersproject/abi';
|
|
4
|
+
import { IDexHelper } from '../../dex-helper';
|
|
5
|
+
import { Address, Log, Logger, Token } from '../../types';
|
|
6
|
+
import { DeepReadonly } from 'ts-essentials';
|
|
7
|
+
import { FEES_LAYER_ONE, TOPICS } from './constants';
|
|
8
|
+
|
|
9
|
+
const coder = new AbiCoder();
|
|
10
|
+
|
|
11
|
+
export class SmardexEventPool extends StatefulEventSubscriber<SmardexPoolState> {
|
|
12
|
+
constructor(
|
|
13
|
+
protected poolInterface: Interface,
|
|
14
|
+
protected dexHelper: IDexHelper,
|
|
15
|
+
public poolAddress: Address,
|
|
16
|
+
token0: Token,
|
|
17
|
+
token1: Token,
|
|
18
|
+
logger: Logger,
|
|
19
|
+
protected smardexFeesMultiCallEntry?: {
|
|
20
|
+
target: string;
|
|
21
|
+
callData: string;
|
|
22
|
+
},
|
|
23
|
+
protected smardexFeesMulticallDecoder?: (values: any[]) => SmardexFees,
|
|
24
|
+
private isLayerOne = true,
|
|
25
|
+
) {
|
|
26
|
+
super('Smardex', `${token0.address}_${token1.address}`, dexHelper, logger);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async fetchPairFeesAndLastTimestamp(blockNumber: number): Promise<{
|
|
30
|
+
feesLP: bigint;
|
|
31
|
+
feesPool: bigint;
|
|
32
|
+
priceAverageLastTimestamp: number;
|
|
33
|
+
}> {
|
|
34
|
+
let calldata = [
|
|
35
|
+
{
|
|
36
|
+
target: this.poolAddress,
|
|
37
|
+
callData: this.poolInterface.encodeFunctionData('getPriceAverage', []),
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
if (!this.isLayerOne) {
|
|
41
|
+
calldata.push(this.smardexFeesMultiCallEntry!);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const data: { returnData: any[] } =
|
|
45
|
+
await this.dexHelper.multiContract.methods
|
|
46
|
+
.aggregate(calldata)
|
|
47
|
+
.call({}, blockNumber);
|
|
48
|
+
|
|
49
|
+
const priceAverageLastTimestamp = coder.decode(
|
|
50
|
+
['uint256', 'uint256', 'uint256'],
|
|
51
|
+
data.returnData[0],
|
|
52
|
+
)[2];
|
|
53
|
+
|
|
54
|
+
const fees = this.isLayerOne
|
|
55
|
+
? FEES_LAYER_ONE
|
|
56
|
+
: this.smardexFeesMulticallDecoder!(data.returnData[1]);
|
|
57
|
+
return {
|
|
58
|
+
feesLP: fees.feesLP,
|
|
59
|
+
feesPool: fees.feesPool,
|
|
60
|
+
priceAverageLastTimestamp: priceAverageLastTimestamp.toNumber(),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// This methode overrides previous state with new state.
|
|
65
|
+
// Problem: Smardex Pair state is updated partially on different events
|
|
66
|
+
// This is why we must fetch pair's missing state in Events
|
|
67
|
+
protected async processLog(
|
|
68
|
+
state: DeepReadonly<SmardexPoolState>,
|
|
69
|
+
log: Readonly<Log>,
|
|
70
|
+
): Promise<DeepReadonly<SmardexPoolState> | null> {
|
|
71
|
+
if (!Object.values(TOPICS).includes(log.topics[0] as TOPICS)) return null;
|
|
72
|
+
const event = this.poolInterface.parseLog(log);
|
|
73
|
+
switch (event.name) {
|
|
74
|
+
case 'Sync':
|
|
75
|
+
const fetchedSync = await this.fetchPairFeesAndLastTimestamp(
|
|
76
|
+
log.blockNumber,
|
|
77
|
+
);
|
|
78
|
+
return {
|
|
79
|
+
reserves0: event.args.reserve0.toString(),
|
|
80
|
+
reserves1: event.args.reserve1.toString(),
|
|
81
|
+
fictiveReserves0: event.args.fictiveReserve0.toString(),
|
|
82
|
+
fictiveReserves1: event.args.fictiveReserve1.toString(),
|
|
83
|
+
priceAverage0: event.args.priceAverage0.toString(),
|
|
84
|
+
priceAverage1: event.args.priceAverage1.toString(),
|
|
85
|
+
priceAverageLastTimestamp: fetchedSync.priceAverageLastTimestamp,
|
|
86
|
+
feesLP: fetchedSync.feesLP,
|
|
87
|
+
feesPool: fetchedSync.feesPool,
|
|
88
|
+
};
|
|
89
|
+
case 'FeesChanged': // only triggerd on L2
|
|
90
|
+
return {
|
|
91
|
+
reserves0: state.reserves0,
|
|
92
|
+
reserves1: state.reserves1,
|
|
93
|
+
fictiveReserves0: state.fictiveReserves0,
|
|
94
|
+
fictiveReserves1: state.fictiveReserves1,
|
|
95
|
+
priceAverage0: state.priceAverage0,
|
|
96
|
+
priceAverage1: state.priceAverage1,
|
|
97
|
+
priceAverageLastTimestamp: state.priceAverageLastTimestamp,
|
|
98
|
+
feesLP: BigInt(event.args.feesLP),
|
|
99
|
+
feesPool: BigInt(event.args.feesPool),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async generateState(
|
|
106
|
+
blockNumber: number | 'latest' = 'latest',
|
|
107
|
+
): Promise<DeepReadonly<SmardexPoolState>> {
|
|
108
|
+
const coder = new AbiCoder();
|
|
109
|
+
let calldata = [
|
|
110
|
+
{
|
|
111
|
+
target: this.poolAddress,
|
|
112
|
+
callData: this.poolInterface.encodeFunctionData('getReserves', []),
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
target: this.poolAddress,
|
|
116
|
+
callData: this.poolInterface.encodeFunctionData(
|
|
117
|
+
'getFictiveReserves',
|
|
118
|
+
[],
|
|
119
|
+
),
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
target: this.poolAddress,
|
|
123
|
+
callData: this.poolInterface.encodeFunctionData('getPriceAverage', []),
|
|
124
|
+
},
|
|
125
|
+
];
|
|
126
|
+
if (!this.isLayerOne) {
|
|
127
|
+
calldata.push(this.smardexFeesMultiCallEntry!);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const data: { returnData: any[] } =
|
|
131
|
+
await this.dexHelper.multiContract.methods
|
|
132
|
+
.aggregate(calldata)
|
|
133
|
+
.call({}, blockNumber);
|
|
134
|
+
|
|
135
|
+
const [reserves0, reserves1] = coder.decode(
|
|
136
|
+
['uint256', 'uint256'],
|
|
137
|
+
data.returnData[0],
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
const [fictiveReserve0, fictiveReserve1] = coder.decode(
|
|
141
|
+
['uint256', 'uint256'],
|
|
142
|
+
data.returnData[1],
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
const [priceAverage0, priceAverage1, priceAverageLastTimestamp] =
|
|
146
|
+
coder.decode(['uint256', 'uint256', 'uint256'], data.returnData[2]);
|
|
147
|
+
|
|
148
|
+
const fees = this.isLayerOne
|
|
149
|
+
? FEES_LAYER_ONE
|
|
150
|
+
: this.smardexFeesMulticallDecoder!(data.returnData[3]);
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
reserves0: reserves0.toString(),
|
|
154
|
+
reserves1: reserves1.toString(),
|
|
155
|
+
fictiveReserves0: fictiveReserve0.toString(),
|
|
156
|
+
fictiveReserves1: fictiveReserve1.toString(),
|
|
157
|
+
priceAverage0: priceAverage0.toString(),
|
|
158
|
+
priceAverage1: priceAverage1.toString(),
|
|
159
|
+
priceAverageLastTimestamp: priceAverageLastTimestamp.toNumber(),
|
|
160
|
+
feesLP: fees.feesLP,
|
|
161
|
+
feesPool: fees.feesPool,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
}
|