@paraswap/dex-lib 4.0.16 → 4.0.17-usual-pp.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.
- package/build/abi/usual-pp/abi.json +750 -0
- package/build/dex/fluid-dex/fluid-dex.js +2 -0
- package/build/dex/fluid-dex/fluid-dex.js.map +1 -1
- package/build/dex/index.js +2 -0
- package/build/dex/index.js.map +1 -1
- package/build/dex/usual/usual.js +1 -1
- package/build/dex/usual/usual.js.map +1 -1
- package/build/dex/usual-pp/config.d.ts +3 -0
- package/build/dex/usual-pp/config.js +19 -0
- package/build/dex/usual-pp/config.js.map +1 -0
- package/build/dex/usual-pp/pool.d.ts +17 -0
- package/build/dex/usual-pp/pool.js +57 -0
- package/build/dex/usual-pp/pool.js.map +1 -0
- package/build/dex/usual-pp/types.d.ts +15 -0
- package/build/dex/usual-pp/types.js +3 -0
- package/build/dex/usual-pp/types.js.map +1 -0
- package/build/dex/usual-pp/usual-pp.d.ts +36 -0
- package/build/dex/usual-pp/usual-pp.js +167 -0
- package/build/dex/usual-pp/usual-pp.js.map +1 -0
- package/build/dex/usual-pp/utils.d.ts +4 -0
- package/build/dex/usual-pp/utils.js +18 -0
- package/build/dex/usual-pp/utils.js.map +1 -0
- package/package.json +1 -1
- package/src/abi/usual-pp/abi.json +750 -0
- package/src/dex/fluid-dex/fluid-dex-e2e.test.ts +0 -10
- package/src/dex/fluid-dex/fluid-dex.ts +6 -0
- package/src/dex/index.ts +2 -0
- package/src/dex/usual/usual.ts +1 -1
- package/src/dex/usual-pp/config.ts +18 -0
- package/src/dex/usual-pp/pool.ts +77 -0
- package/src/dex/usual-pp/types.ts +12 -0
- package/src/dex/usual-pp/usual-events.test.ts +70 -0
- package/src/dex/usual-pp/usual-integration.test.ts +143 -0
- package/src/dex/usual-pp/usual-pp.e2e.test.ts +76 -0
- package/src/dex/usual-pp/usual-pp.ts +208 -0
- package/src/dex/usual-pp/utils.ts +27 -0
|
@@ -19,16 +19,6 @@ import { FluidDex } from './fluid-dex';
|
|
|
19
19
|
README
|
|
20
20
|
======
|
|
21
21
|
|
|
22
|
-
This test script should add e2e tests for FluidDex. The tests
|
|
23
|
-
should cover as many cases as possible. Most of the DEXes follow
|
|
24
|
-
the following test structure:
|
|
25
|
-
- DexName
|
|
26
|
-
- ForkName + Network
|
|
27
|
-
- ContractMethod
|
|
28
|
-
- ETH -> Token swap
|
|
29
|
-
- Token -> ETH swap
|
|
30
|
-
- Token -> Token swap
|
|
31
|
-
|
|
32
22
|
The template already enumerates the basic structure which involves
|
|
33
23
|
testing simpleSwap, multiSwap, megaSwap contract methods for
|
|
34
24
|
ETH <> TOKEN and TOKEN <> TOKEN swaps. You should replace tokenA and
|
|
@@ -206,6 +206,12 @@ export class FluidDex extends SimpleExchange implements IDex<FluidDexData> {
|
|
|
206
206
|
pools = pools.filter(pool => limitPools.includes(pool.id));
|
|
207
207
|
}
|
|
208
208
|
|
|
209
|
+
// temporarily limit FLUID-ETH Dex Pool.
|
|
210
|
+
pools = pools.filter(
|
|
211
|
+
pool =>
|
|
212
|
+
pool.id != 'FluidDex_0xc800b0e15c40a1ff0539218100c86f4c1bac8d9c',
|
|
213
|
+
);
|
|
214
|
+
|
|
209
215
|
if (!pools.length) return null;
|
|
210
216
|
|
|
211
217
|
const liquidityProxyState = await this.liquidityProxy.getStateOrGenerate(
|
package/src/dex/index.ts
CHANGED
|
@@ -100,6 +100,7 @@ import { Stader } from './stader/stader';
|
|
|
100
100
|
import { UsualBond } from './usual/usual-bond';
|
|
101
101
|
import { UsualMWrappedM } from './usual/usual-m-wrapped-m';
|
|
102
102
|
import { UsualMUsd0 } from './usual/usual-m-usd0';
|
|
103
|
+
import { UsualPP } from './usual-pp/usual-pp';
|
|
103
104
|
|
|
104
105
|
const LegacyDexes = [
|
|
105
106
|
CurveV2,
|
|
@@ -194,6 +195,7 @@ const Dexes = [
|
|
|
194
195
|
FluidDex,
|
|
195
196
|
UsualMWrappedM,
|
|
196
197
|
UsualMUsd0,
|
|
198
|
+
UsualPP,
|
|
197
199
|
];
|
|
198
200
|
|
|
199
201
|
export type LegacyDexConstructor = new (dexHelper: IDexHelper) => IDexTxBuilder<
|
package/src/dex/usual/usual.ts
CHANGED
|
@@ -72,7 +72,7 @@ export class Usual extends SimpleExchange implements IDex<UsualBondData> {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
if (this.isValidTokens(srcTokenAddress, destTokenAddress)) {
|
|
75
|
-
return [`${this.dexKey}_${this.config.toToken}`];
|
|
75
|
+
return [`${this.dexKey}_${this.config.toToken.address}`];
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
return [];
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Network } from '../../constants';
|
|
2
|
+
import { DexConfigMap } from '../../types';
|
|
3
|
+
import { DexParams } from './types';
|
|
4
|
+
|
|
5
|
+
export const Config: DexConfigMap<DexParams> = {
|
|
6
|
+
UsualPP: {
|
|
7
|
+
[Network.MAINNET]: {
|
|
8
|
+
USD0: {
|
|
9
|
+
address: '0x73a15fed60bf67631dc6cd7bc5b6e8da8190acf5',
|
|
10
|
+
decimals: 18,
|
|
11
|
+
},
|
|
12
|
+
USD0PP: {
|
|
13
|
+
address: '0x35d8949372d46b7a3d5a56006ae77b215fc69bc0',
|
|
14
|
+
decimals: 18,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
import { Interface } from '@ethersproject/abi';
|
|
3
|
+
import { IDexHelper } from '../../dex-helper';
|
|
4
|
+
import { StatefulEventSubscriber } from '../../stateful-event-subscriber';
|
|
5
|
+
import { Address, Log, Logger } from '../../types';
|
|
6
|
+
import { AsyncOrSync, DeepReadonly } from 'ts-essentials';
|
|
7
|
+
import { PoolState } from './types';
|
|
8
|
+
import { getOnChainState } from './utils';
|
|
9
|
+
|
|
10
|
+
export class UsualPool extends StatefulEventSubscriber<PoolState> {
|
|
11
|
+
decoder = (log: Log) => this.poolInterface.parseLog(log);
|
|
12
|
+
|
|
13
|
+
constructor(
|
|
14
|
+
parentName: string,
|
|
15
|
+
protected dexHelper: IDexHelper,
|
|
16
|
+
private poolAddress: Address,
|
|
17
|
+
private poolInterface: Interface,
|
|
18
|
+
logger: Logger,
|
|
19
|
+
) {
|
|
20
|
+
super(parentName, 'UsualPool', dexHelper, logger);
|
|
21
|
+
this.addressesSubscribed = [poolAddress];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
protected processLog(
|
|
25
|
+
state: DeepReadonly<PoolState>,
|
|
26
|
+
log: Readonly<Log>,
|
|
27
|
+
): AsyncOrSync<DeepReadonly<PoolState> | null> {
|
|
28
|
+
const event = this.decoder(log);
|
|
29
|
+
const _state: PoolState = _.cloneDeep(state);
|
|
30
|
+
if (event.name === 'FloorPriceUpdated') {
|
|
31
|
+
_state.price = BigInt(event.args.newFloorPrice);
|
|
32
|
+
return _state;
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async generateState(
|
|
38
|
+
blockNumber: number | 'latest' = 'latest',
|
|
39
|
+
): Promise<DeepReadonly<PoolState>> {
|
|
40
|
+
const state = await getOnChainState(
|
|
41
|
+
this.dexHelper.multiContract,
|
|
42
|
+
this.poolAddress,
|
|
43
|
+
this.poolInterface,
|
|
44
|
+
blockNumber,
|
|
45
|
+
);
|
|
46
|
+
return state;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async getPrice(blockNumber: number): Promise<bigint> {
|
|
50
|
+
const state = await this.getOrGenerateState(blockNumber);
|
|
51
|
+
if (!state) throw new Error('Cannot compute state');
|
|
52
|
+
return state.price;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async getOrGenerateState(
|
|
56
|
+
blockNumber: number,
|
|
57
|
+
): Promise<DeepReadonly<PoolState> | null> {
|
|
58
|
+
const state = this.getState(blockNumber);
|
|
59
|
+
if (state) {
|
|
60
|
+
return state;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
this.logger.debug(
|
|
64
|
+
`No state found for ${this.addressesSubscribed[0]}, generating new one`,
|
|
65
|
+
);
|
|
66
|
+
const newState = await this.generateState(blockNumber);
|
|
67
|
+
|
|
68
|
+
if (!newState) {
|
|
69
|
+
this.logger.debug(
|
|
70
|
+
`Could not regenerate state for ${this.addressesSubscribed[0]}`,
|
|
71
|
+
);
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
this.setState(newState, blockNumber);
|
|
75
|
+
return newState;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Address } from '../../types';
|
|
2
|
+
|
|
3
|
+
export type PoolState = {
|
|
4
|
+
price: bigint;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export type UsualPPData = {};
|
|
8
|
+
|
|
9
|
+
export type DexParams = {
|
|
10
|
+
USD0: { address: Address; decimals: number };
|
|
11
|
+
USD0PP: { address: Address; decimals: number };
|
|
12
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import dotenv from 'dotenv';
|
|
2
|
+
dotenv.config();
|
|
3
|
+
|
|
4
|
+
import { Network } from '../../constants';
|
|
5
|
+
import { DummyDexHelper } from '../../dex-helper/index';
|
|
6
|
+
import { testEventSubscriber } from '../../../tests/utils-events';
|
|
7
|
+
import { PoolState } from './types';
|
|
8
|
+
import { Config } from './config';
|
|
9
|
+
import { UsualPool } from './pool';
|
|
10
|
+
import { PoolConfig } from '../curve-v1/types';
|
|
11
|
+
import UsualPoolAbi from '../../abi/usual-pp/abi.json';
|
|
12
|
+
import { Interface } from '@ethersproject/abi';
|
|
13
|
+
|
|
14
|
+
jest.setTimeout(50 * 1000);
|
|
15
|
+
const dexKey = 'UsualPP';
|
|
16
|
+
const network = Network.MAINNET;
|
|
17
|
+
const config = Config[dexKey][network];
|
|
18
|
+
const dexHelper = new DummyDexHelper(network);
|
|
19
|
+
const logger = dexHelper.getLogger(dexKey);
|
|
20
|
+
|
|
21
|
+
async function fetchPoolState(
|
|
22
|
+
usualPool: UsualPool,
|
|
23
|
+
blockNumber: number,
|
|
24
|
+
): Promise<PoolState> {
|
|
25
|
+
return usualPool.generateState(blockNumber);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function compareState(state: PoolState, expectedState: PoolState) {
|
|
29
|
+
expect(state).toEqual(expectedState);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
describe('UsualPP Event', function () {
|
|
33
|
+
const blockNumbers: { [eventName: string]: number[] } = {
|
|
34
|
+
FloorPriceUpdated: [21589702],
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
describe('UsualPool', function () {
|
|
38
|
+
let usualPool: UsualPool;
|
|
39
|
+
let blockNumber: number;
|
|
40
|
+
|
|
41
|
+
const usualPoolIface = new Interface(UsualPoolAbi);
|
|
42
|
+
|
|
43
|
+
beforeAll(async function () {
|
|
44
|
+
blockNumber = await dexHelper.web3Provider.eth.getBlockNumber();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
Object.keys(blockNumbers).forEach((event: string) => {
|
|
48
|
+
blockNumbers[event].forEach((blockNumber: number) => {
|
|
49
|
+
it(`Should return the correct state after the ${blockNumber}:${event}`, async function () {
|
|
50
|
+
usualPool = new UsualPool(
|
|
51
|
+
dexKey,
|
|
52
|
+
dexHelper,
|
|
53
|
+
config.USD0PP.address,
|
|
54
|
+
usualPoolIface,
|
|
55
|
+
logger,
|
|
56
|
+
);
|
|
57
|
+
await testEventSubscriber(
|
|
58
|
+
usualPool,
|
|
59
|
+
usualPool.addressesSubscribed,
|
|
60
|
+
(_blockNumber: number) => fetchPoolState(usualPool, _blockNumber),
|
|
61
|
+
blockNumber,
|
|
62
|
+
`${dexKey}_${config.USD0PP.address}`,
|
|
63
|
+
dexHelper.provider,
|
|
64
|
+
compareState,
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import dotenv from 'dotenv';
|
|
3
|
+
dotenv.config();
|
|
4
|
+
|
|
5
|
+
import { Interface } from '@ethersproject/abi';
|
|
6
|
+
import { DummyDexHelper } from '../../dex-helper/index';
|
|
7
|
+
import { Network, SwapSide } from '../../constants';
|
|
8
|
+
import { BI_POWS } from '../../bigint-constants';
|
|
9
|
+
import {
|
|
10
|
+
checkPoolPrices,
|
|
11
|
+
checkConstantPoolPrices,
|
|
12
|
+
checkPoolsLiquidity,
|
|
13
|
+
} from '../../../tests/utils';
|
|
14
|
+
import { Tokens } from '../../../tests/constants-e2e';
|
|
15
|
+
import { UsualPP } from './usual-pp';
|
|
16
|
+
|
|
17
|
+
async function testPricingOnNetwork(
|
|
18
|
+
usual: UsualPP,
|
|
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
|
+
console.log(amounts);
|
|
31
|
+
|
|
32
|
+
const pools = await usual.getPoolIdentifiers(
|
|
33
|
+
networkTokens[srcTokenSymbol],
|
|
34
|
+
networkTokens[destTokenSymbol],
|
|
35
|
+
side,
|
|
36
|
+
blockNumber,
|
|
37
|
+
);
|
|
38
|
+
console.log(
|
|
39
|
+
`${srcTokenSymbol} <> ${destTokenSymbol} Pool Identifiers: `,
|
|
40
|
+
pools,
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
expect(pools.length).toBeGreaterThan(0);
|
|
44
|
+
|
|
45
|
+
const poolPrices = await usual.getPricesVolume(
|
|
46
|
+
networkTokens[srcTokenSymbol],
|
|
47
|
+
networkTokens[destTokenSymbol],
|
|
48
|
+
amounts,
|
|
49
|
+
side,
|
|
50
|
+
blockNumber,
|
|
51
|
+
pools,
|
|
52
|
+
);
|
|
53
|
+
console.log(
|
|
54
|
+
`${srcTokenSymbol} <> ${destTokenSymbol} Pool Prices: `,
|
|
55
|
+
poolPrices,
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
expect(poolPrices).not.toBeNull();
|
|
59
|
+
// Check if onchain pricing equals to calculated ones
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
describe('UsualPP', function () {
|
|
63
|
+
const dexKey = 'UsualPP';
|
|
64
|
+
let blockNumber: number;
|
|
65
|
+
let usualPP: UsualPP;
|
|
66
|
+
|
|
67
|
+
describe('Mainnet', () => {
|
|
68
|
+
const network = Network.MAINNET;
|
|
69
|
+
const dexHelper = new DummyDexHelper(network);
|
|
70
|
+
|
|
71
|
+
// Don't forget to update relevant tokens in constant-e2e.ts
|
|
72
|
+
|
|
73
|
+
const amountsForSell = [
|
|
74
|
+
0n,
|
|
75
|
+
1n * BI_POWS[18],
|
|
76
|
+
2n * BI_POWS[18],
|
|
77
|
+
3n * BI_POWS[18],
|
|
78
|
+
4n * BI_POWS[18],
|
|
79
|
+
5n * BI_POWS[18],
|
|
80
|
+
6n * BI_POWS[18],
|
|
81
|
+
7n * BI_POWS[18],
|
|
82
|
+
8n * BI_POWS[18],
|
|
83
|
+
9n * BI_POWS[18],
|
|
84
|
+
10n * BI_POWS[18],
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
beforeAll(async () => {
|
|
88
|
+
blockNumber = await dexHelper.web3Provider.eth.getBlockNumber();
|
|
89
|
+
usualPP = new UsualPP(network, dexKey, dexHelper);
|
|
90
|
+
if (usualPP.initializePricing) {
|
|
91
|
+
await usualPP.initializePricing(blockNumber);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('getPoolIdentifiers and getPricesVolume SELL', async function () {
|
|
96
|
+
await testPricingOnNetwork(
|
|
97
|
+
usualPP,
|
|
98
|
+
network,
|
|
99
|
+
dexKey,
|
|
100
|
+
blockNumber,
|
|
101
|
+
'USD0++',
|
|
102
|
+
'USD0',
|
|
103
|
+
SwapSide.SELL,
|
|
104
|
+
amountsForSell,
|
|
105
|
+
'',
|
|
106
|
+
);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('getTopPoolsForToken: USD0', async function () {
|
|
110
|
+
const tokenA = Tokens[network]['USD0'];
|
|
111
|
+
const dexHelper = new DummyDexHelper(network);
|
|
112
|
+
const usualPP = new UsualPP(network, dexKey, dexHelper);
|
|
113
|
+
|
|
114
|
+
const poolLiquidity = await usualPP.getTopPoolsForToken(
|
|
115
|
+
tokenA.address,
|
|
116
|
+
10,
|
|
117
|
+
);
|
|
118
|
+
console.log(
|
|
119
|
+
`${tokenA.symbol} Top Pools:`,
|
|
120
|
+
JSON.stringify(poolLiquidity, null, 2),
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
checkPoolsLiquidity(poolLiquidity, tokenA.address, dexKey);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('getTopPoolsForToken: USD0++', async function () {
|
|
127
|
+
const tokenA = Tokens[network]['USD0++'];
|
|
128
|
+
const dexHelper = new DummyDexHelper(network);
|
|
129
|
+
const usualPP = new UsualPP(network, dexKey, dexHelper);
|
|
130
|
+
|
|
131
|
+
const poolLiquidity = await usualPP.getTopPoolsForToken(
|
|
132
|
+
tokenA.address,
|
|
133
|
+
10,
|
|
134
|
+
);
|
|
135
|
+
console.log(
|
|
136
|
+
`${tokenA.symbol} Top Pools:`,
|
|
137
|
+
JSON.stringify(poolLiquidity, null, 2),
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
checkPoolsLiquidity(poolLiquidity, tokenA.address, dexKey);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
});
|
|
@@ -0,0 +1,76 @@
|
|
|
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
|
+
]);
|
|
29
|
+
|
|
30
|
+
describe(`${network}`, () => {
|
|
31
|
+
sideToContractMethods.forEach((contractMethods, side) =>
|
|
32
|
+
describe(`${side}`, () => {
|
|
33
|
+
contractMethods.forEach((contractMethod: ContractMethod) => {
|
|
34
|
+
describe(`${contractMethod}`, () => {
|
|
35
|
+
it(`${tokenASymbol} -> ${tokenBSymbol}`, async () => {
|
|
36
|
+
await testE2E(
|
|
37
|
+
tokens[tokenASymbol],
|
|
38
|
+
tokens[tokenBSymbol],
|
|
39
|
+
holders[tokenASymbol],
|
|
40
|
+
side === SwapSide.SELL ? tokenAAmount : tokenBAmount,
|
|
41
|
+
side,
|
|
42
|
+
dexKey,
|
|
43
|
+
contractMethod,
|
|
44
|
+
network,
|
|
45
|
+
provider,
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
}),
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
describe('UsualPP E2E', () => {
|
|
56
|
+
const dexKey = 'UsualPP';
|
|
57
|
+
|
|
58
|
+
describe('Mainnet', () => {
|
|
59
|
+
const network = Network.MAINNET;
|
|
60
|
+
|
|
61
|
+
const tokenASymbol: string = 'USD0++';
|
|
62
|
+
const tokenBSymbol: string = 'USD0';
|
|
63
|
+
|
|
64
|
+
const tokenAAmount: string = '1000000000000000000';
|
|
65
|
+
const tokenBAmount: string = '1000000000000000000';
|
|
66
|
+
|
|
67
|
+
testForNetwork(
|
|
68
|
+
network,
|
|
69
|
+
dexKey,
|
|
70
|
+
tokenASymbol,
|
|
71
|
+
tokenBSymbol,
|
|
72
|
+
tokenAAmount,
|
|
73
|
+
tokenBAmount,
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Token,
|
|
3
|
+
Address,
|
|
4
|
+
ExchangePrices,
|
|
5
|
+
PoolPrices,
|
|
6
|
+
AdapterExchangeParam,
|
|
7
|
+
Logger,
|
|
8
|
+
PoolLiquidity,
|
|
9
|
+
DexExchangeParam,
|
|
10
|
+
NumberAsString,
|
|
11
|
+
} from '../../types';
|
|
12
|
+
import { SwapSide, Network } from '../../constants';
|
|
13
|
+
import * as CALLDATA_GAS_COST from '../../calldata-gas-cost';
|
|
14
|
+
import { IDex } from '../idex';
|
|
15
|
+
import { IDexHelper } from '../../dex-helper/idex-helper';
|
|
16
|
+
import { SimpleExchange } from '../simple-exchange';
|
|
17
|
+
import { BI_POWS } from '../../bigint-constants';
|
|
18
|
+
import { DexParams, UsualPPData } from './types';
|
|
19
|
+
import { UsualPool } from './pool';
|
|
20
|
+
import { Interface } from '@ethersproject/abi';
|
|
21
|
+
import UsualPoolAbi from '../../abi/usual-pp/abi.json';
|
|
22
|
+
import { Config } from './config';
|
|
23
|
+
import { getDexKeysWithNetwork } from '../../utils';
|
|
24
|
+
import { UsualBondData } from '../usual/types';
|
|
25
|
+
|
|
26
|
+
export class UsualPP extends SimpleExchange implements IDex<UsualPPData> {
|
|
27
|
+
readonly hasConstantPriceLargeAmounts = false;
|
|
28
|
+
readonly needWrapNative = false;
|
|
29
|
+
readonly isFeeOnTransferSupported = false;
|
|
30
|
+
|
|
31
|
+
private usualPool: UsualPool;
|
|
32
|
+
private usualPoolIface: Interface;
|
|
33
|
+
private config: DexParams;
|
|
34
|
+
|
|
35
|
+
logger: Logger;
|
|
36
|
+
|
|
37
|
+
public static dexKeysWithNetwork: { key: string; networks: Network[] }[] =
|
|
38
|
+
getDexKeysWithNetwork(Config);
|
|
39
|
+
|
|
40
|
+
constructor(
|
|
41
|
+
readonly network: Network,
|
|
42
|
+
readonly dexKey: string,
|
|
43
|
+
readonly dexHelper: IDexHelper,
|
|
44
|
+
protected adapters = {},
|
|
45
|
+
) {
|
|
46
|
+
super(dexHelper, dexKey);
|
|
47
|
+
this.config = Config[dexKey][network];
|
|
48
|
+
this.logger = dexHelper.getLogger(dexKey);
|
|
49
|
+
this.usualPoolIface = new Interface(UsualPoolAbi);
|
|
50
|
+
this.usualPool = new UsualPool(
|
|
51
|
+
this.dexKey,
|
|
52
|
+
dexHelper,
|
|
53
|
+
this.config.USD0PP.address,
|
|
54
|
+
this.usualPoolIface,
|
|
55
|
+
this.logger,
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async initializePricing(blockNumber: number) {
|
|
60
|
+
await this.usualPool.initialize(blockNumber);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
isUsd0PP(token: string) {
|
|
64
|
+
return token.toLowerCase() === this.config.USD0PP.address.toLowerCase();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
isUsd0(token: string) {
|
|
68
|
+
return token.toLowerCase() === this.config.USD0.address.toLowerCase();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
isValidTokens(srcToken: string, destToken: string) {
|
|
72
|
+
return this.isUsd0PP(srcToken) && this.isUsd0(destToken);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
getAdapters() {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async getPoolIdentifiers(
|
|
80
|
+
srcToken: Token,
|
|
81
|
+
destToken: Token,
|
|
82
|
+
side: SwapSide,
|
|
83
|
+
blockNumber: number,
|
|
84
|
+
): Promise<string[]> {
|
|
85
|
+
if (!srcToken || !destToken) {
|
|
86
|
+
this.logger.error('Source or destination token is undefined');
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const srcTokenAddress = srcToken.address?.toLowerCase();
|
|
91
|
+
const destTokenAddress = destToken.address?.toLowerCase();
|
|
92
|
+
|
|
93
|
+
if (!srcTokenAddress || !destTokenAddress) {
|
|
94
|
+
this.logger.error('Source or destination token address is undefined');
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (this.isValidTokens(srcTokenAddress, destTokenAddress)) {
|
|
99
|
+
return [`${this.dexKey}_${this.config.USD0.address}`];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async getPricesVolume(
|
|
106
|
+
srcToken: Token,
|
|
107
|
+
destToken: Token,
|
|
108
|
+
amounts: bigint[],
|
|
109
|
+
side: SwapSide,
|
|
110
|
+
blockNumber: number,
|
|
111
|
+
limitPools?: string[],
|
|
112
|
+
): Promise<null | ExchangePrices<UsualPPData>> {
|
|
113
|
+
if (side === SwapSide.BUY) {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const isValidSwap = this.isValidTokens(srcToken.address, destToken.address);
|
|
118
|
+
|
|
119
|
+
if (!isValidSwap) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const price = await this.usualPool.getPrice(blockNumber);
|
|
124
|
+
const unitOut = price;
|
|
125
|
+
|
|
126
|
+
const amountsOut = amounts.map(
|
|
127
|
+
amount => (amount * price) / BI_POWS[this.config.USD0PP.decimals],
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
return [
|
|
131
|
+
{
|
|
132
|
+
unit: unitOut,
|
|
133
|
+
prices: amountsOut,
|
|
134
|
+
data: {},
|
|
135
|
+
poolAddresses: [this.config.USD0.address],
|
|
136
|
+
exchange: this.dexKey,
|
|
137
|
+
gasCost: 70000,
|
|
138
|
+
poolIdentifier: this.dexKey,
|
|
139
|
+
},
|
|
140
|
+
];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
getCalldataGasCost(poolPrices: PoolPrices<UsualPPData>): number | number[] {
|
|
144
|
+
return CALLDATA_GAS_COST.DEX_NO_PAYLOAD;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
getAdapterParam(
|
|
148
|
+
srcToken: string,
|
|
149
|
+
destToken: string,
|
|
150
|
+
srcAmount: string,
|
|
151
|
+
destAmount: string,
|
|
152
|
+
data: UsualPPData,
|
|
153
|
+
side: SwapSide,
|
|
154
|
+
): AdapterExchangeParam {
|
|
155
|
+
const payload = '0x';
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
targetExchange: this.config.USD0.address,
|
|
159
|
+
payload,
|
|
160
|
+
networkFee: '0',
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async getTopPoolsForToken(
|
|
165
|
+
tokenAddress: Address,
|
|
166
|
+
limit: number,
|
|
167
|
+
): Promise<PoolLiquidity[]> {
|
|
168
|
+
const isUsd0PP = this.isUsd0PP(tokenAddress);
|
|
169
|
+
const isUsd0 = this.isUsd0(tokenAddress);
|
|
170
|
+
|
|
171
|
+
if (!(isUsd0PP || isUsd0)) return [];
|
|
172
|
+
|
|
173
|
+
return [
|
|
174
|
+
{
|
|
175
|
+
exchange: this.dexKey,
|
|
176
|
+
address: this.config.USD0PP.address,
|
|
177
|
+
connectorTokens: [isUsd0PP ? this.config.USD0 : this.config.USD0PP],
|
|
178
|
+
liquidityUSD: 1000000000, // Just returning a big number so this DEX will be preferred
|
|
179
|
+
},
|
|
180
|
+
];
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async getDexParam(
|
|
184
|
+
srcToken: Address,
|
|
185
|
+
destToken: Address,
|
|
186
|
+
srcAmount: NumberAsString,
|
|
187
|
+
destAmount: NumberAsString,
|
|
188
|
+
recipient: Address,
|
|
189
|
+
data: UsualBondData,
|
|
190
|
+
side: SwapSide,
|
|
191
|
+
): Promise<DexExchangeParam> {
|
|
192
|
+
if (this.isUsd0PP(srcToken) && this.isUsd0(destToken)) {
|
|
193
|
+
const exchangeData = this.usualPoolIface.encodeFunctionData(
|
|
194
|
+
'unlockUsd0ppFloorPrice',
|
|
195
|
+
[srcAmount],
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
needWrapNative: false,
|
|
200
|
+
dexFuncHasRecipient: false,
|
|
201
|
+
exchangeData,
|
|
202
|
+
targetExchange: this.config.USD0PP.address,
|
|
203
|
+
returnAmountPos: undefined,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
throw new Error('LOGIC ERROR');
|
|
207
|
+
}
|
|
208
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Contract } from 'web3-eth-contract';
|
|
2
|
+
import { Interface } from '@ethersproject/abi';
|
|
3
|
+
import { PoolState } from './types';
|
|
4
|
+
|
|
5
|
+
export async function getOnChainState(
|
|
6
|
+
multiContract: Contract,
|
|
7
|
+
poolAddress: string,
|
|
8
|
+
poolInterface: Interface,
|
|
9
|
+
blockNumber: number | 'latest',
|
|
10
|
+
): Promise<PoolState> {
|
|
11
|
+
const data: { returnData: any[] } = await multiContract.methods
|
|
12
|
+
.aggregate([
|
|
13
|
+
{
|
|
14
|
+
target: poolAddress,
|
|
15
|
+
callData: poolInterface.encodeFunctionData('getFloorPrice', []),
|
|
16
|
+
},
|
|
17
|
+
])
|
|
18
|
+
.call({}, blockNumber);
|
|
19
|
+
|
|
20
|
+
const price = BigInt(
|
|
21
|
+
poolInterface.decodeFunctionResult('getFloorPrice', data.returnData[0])[0],
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
price,
|
|
26
|
+
};
|
|
27
|
+
}
|