@paraswap/dex-lib 3.3.0 → 3.3.1-idle
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/angle-transmuter/TransmuterSidechain.json +1798 -0
- package/build/config.js +2 -0
- package/build/config.js.map +1 -1
- package/build/dex/angle-staked-stable/angle-staked-stable.js +0 -4
- package/build/dex/angle-staked-stable/angle-staked-stable.js.map +1 -1
- package/build/dex/angle-transmuter/angle-transmuter-pool.d.ts +1 -1
- package/build/dex/angle-transmuter/angle-transmuter-pool.js +19 -10
- package/build/dex/angle-transmuter/angle-transmuter-pool.js.map +1 -1
- package/build/dex/angle-transmuter/angle-transmuter.d.ts +3 -2
- package/build/dex/angle-transmuter/angle-transmuter.js +23 -4
- package/build/dex/angle-transmuter/angle-transmuter.js.map +1 -1
- package/build/dex/angle-transmuter/config.js +20 -0
- package/build/dex/angle-transmuter/config.js.map +1 -1
- package/build/dex/angle-transmuter/constants.d.ts +5 -72
- package/build/dex/angle-transmuter/constants.js +44 -1
- package/build/dex/angle-transmuter/constants.js.map +1 -1
- package/build/dex/angle-transmuter/morphoVault.d.ts +1 -0
- package/build/dex/angle-transmuter/morphoVault.js +6 -0
- package/build/dex/angle-transmuter/morphoVault.js.map +1 -1
- package/build/dex/angle-transmuter/transmuter.d.ts +5 -2
- package/build/dex/angle-transmuter/transmuter.js +61 -23
- package/build/dex/angle-transmuter/transmuter.js.map +1 -1
- package/build/dex/angle-transmuter/types.d.ts +3 -2
- package/build/dex/angle-transmuter/types.js.map +1 -1
- package/build/dex/angle-transmuter/utils.d.ts +2 -2
- package/build/dex/angle-transmuter/utils.js +9 -4
- package/build/dex/angle-transmuter/utils.js.map +1 -1
- package/build/dex/idex.d.ts +1 -1
- package/build/dex/idle-dao/config.d.ts +2 -1
- package/build/dex/idle-dao/config.js +543 -1
- package/build/dex/idle-dao/config.js.map +1 -1
- package/build/dex/idle-dao/idle-dao.d.ts +3 -3
- package/build/dex/idle-dao/idle-dao.js +18 -8
- package/build/dex/idle-dao/idle-dao.js.map +1 -1
- package/build/dex/idle-dao/utils.js +5 -3
- package/build/dex/idle-dao/utils.js.map +1 -1
- package/build/dex/index.js +2 -0
- package/build/dex/index.js.map +1 -1
- package/build/router/simpleswap.js +6 -3
- package/build/router/simpleswap.js.map +1 -1
- package/build/types.d.ts +1 -0
- package/dex-template/__DexName__.ts.template +2 -29
- package/package.json +1 -1
- package/src/abi/idle-dao/idle-cdo-factory.json +38 -0
- package/src/abi/idle-dao/idle-cdo.json +1245 -0
- package/src/config.ts +3 -0
- package/src/dex/angle-staked-stable/angle-staked-stable.ts +0 -4
- package/src/dex/idex.ts +5 -3
- package/src/dex/idle-dao/config.ts +574 -0
- package/src/dex/idle-dao/idle-dao-e2e.test.ts +113 -0
- package/src/dex/idle-dao/idle-dao-events.test.ts +133 -0
- package/src/dex/idle-dao/idle-dao-integration.test.ts +166 -0
- package/src/dex/idle-dao/idle-dao-pool.ts +304 -0
- package/src/dex/idle-dao/idle-dao-pooling-pool.ts +87 -0
- package/src/dex/idle-dao/idle-dao.ts +508 -0
- package/src/dex/idle-dao/tokens.ts +117 -0
- package/src/dex/idle-dao/types.ts +48 -0
- package/src/dex/idle-dao/utils.ts +216 -0
- package/src/dex/index.ts +2 -0
- package/src/router/simpleswap.ts +7 -3
- package/src/types.ts +1 -0
- package/tests/constants-e2e.ts +70 -4
|
@@ -0,0 +1,113 @@
|
|
|
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
|
+
contractMethods.forEach((contractMethod: ContractMethod) => {
|
|
35
|
+
describe(`${contractMethod}`, () => {
|
|
36
|
+
it(`${tokenASymbol} -> ${tokenBSymbol} - ${tokenAAmount}`, async () => {
|
|
37
|
+
await testE2E(
|
|
38
|
+
tokens[tokenASymbol],
|
|
39
|
+
tokens[tokenBSymbol],
|
|
40
|
+
holders[tokenASymbol],
|
|
41
|
+
side === SwapSide.SELL ? tokenAAmount : tokenBAmount,
|
|
42
|
+
side,
|
|
43
|
+
dexKey,
|
|
44
|
+
contractMethod,
|
|
45
|
+
network,
|
|
46
|
+
provider,
|
|
47
|
+
);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it(`${tokenBSymbol} -> ${tokenASymbol} - ${tokenBAmount}`, async () => {
|
|
51
|
+
await testE2E(
|
|
52
|
+
tokens[tokenBSymbol],
|
|
53
|
+
tokens[tokenASymbol],
|
|
54
|
+
holders[tokenBSymbol],
|
|
55
|
+
side === SwapSide.SELL ? tokenBAmount : tokenAAmount,
|
|
56
|
+
side,
|
|
57
|
+
dexKey,
|
|
58
|
+
contractMethod,
|
|
59
|
+
network,
|
|
60
|
+
provider,
|
|
61
|
+
);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
}),
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
describe('IdleDao E2E', () => {
|
|
71
|
+
const dexKey = 'IdleDao';
|
|
72
|
+
|
|
73
|
+
describe('Mainnet', () => {
|
|
74
|
+
const network = Network.MAINNET;
|
|
75
|
+
const tests = [
|
|
76
|
+
{
|
|
77
|
+
tokenASymbol: 'STETH',
|
|
78
|
+
tokenBSymbol: 'AA_wstETH',
|
|
79
|
+
tokenAAmount: '1000000000000000000',
|
|
80
|
+
tokenBAmount: '1000000000000000000',
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
tokenASymbol: 'USDC',
|
|
84
|
+
tokenBSymbol: 'AA_idle_cpPOR-USDC',
|
|
85
|
+
tokenAAmount: '1000000',
|
|
86
|
+
tokenBAmount: '1000000000000000000',
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
tokenASymbol: 'USDT',
|
|
90
|
+
tokenBSymbol: 'BB_idle_cpFAS-USDT',
|
|
91
|
+
tokenAAmount: '1000000',
|
|
92
|
+
tokenBAmount: '1000000000000000000',
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
tokenASymbol: 'WETH',
|
|
96
|
+
tokenBSymbol: 'BB_Re7WETH',
|
|
97
|
+
tokenAAmount: '100000000000000',
|
|
98
|
+
tokenBAmount: '100000000000000',
|
|
99
|
+
},
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
tests.forEach(testData => {
|
|
103
|
+
testForNetwork(
|
|
104
|
+
network,
|
|
105
|
+
dexKey,
|
|
106
|
+
testData.tokenASymbol,
|
|
107
|
+
testData.tokenBSymbol,
|
|
108
|
+
testData.tokenAAmount,
|
|
109
|
+
testData.tokenBAmount,
|
|
110
|
+
);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
});
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import dotenv from 'dotenv';
|
|
3
|
+
dotenv.config();
|
|
4
|
+
|
|
5
|
+
import { IdleDao } from './idle-dao';
|
|
6
|
+
import { IdleDaoEventPool } from './idle-dao-pool';
|
|
7
|
+
import { Network } from '../../constants';
|
|
8
|
+
import { Address, Logger } from '../../types';
|
|
9
|
+
import { DummyDexHelper } from '../../dex-helper/index';
|
|
10
|
+
import { testEventSubscriber } from '../../../tests/utils-events';
|
|
11
|
+
import { PoolState, IdleToken } from './types';
|
|
12
|
+
|
|
13
|
+
jest.setTimeout(50 * 1000);
|
|
14
|
+
|
|
15
|
+
async function fetchPoolState(
|
|
16
|
+
idleDaoPool: IdleDaoEventPool,
|
|
17
|
+
blockNumber: number,
|
|
18
|
+
poolAddress: string,
|
|
19
|
+
logger: Logger,
|
|
20
|
+
): Promise<PoolState> {
|
|
21
|
+
const state = await idleDaoPool.generateState(blockNumber);
|
|
22
|
+
return state;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type EventMappings = Record<string, number[]>;
|
|
26
|
+
|
|
27
|
+
describe('IdleDao EventPool Mainnet', function () {
|
|
28
|
+
const dexKey = 'IdleDao';
|
|
29
|
+
let latestBlockNumber: number;
|
|
30
|
+
const network = Network.MAINNET;
|
|
31
|
+
let tokensList: Record<string, IdleToken>;
|
|
32
|
+
const dexHelper = new DummyDexHelper(network);
|
|
33
|
+
const logger = dexHelper.getLogger(dexKey);
|
|
34
|
+
const idleDao: IdleDao = new IdleDao(network, dexKey, dexHelper);
|
|
35
|
+
|
|
36
|
+
beforeAll(async () => {
|
|
37
|
+
latestBlockNumber = await dexHelper.web3Provider.eth.getBlockNumber();
|
|
38
|
+
await idleDao.initializePricing(latestBlockNumber);
|
|
39
|
+
tokensList = await idleDao.getTokensList(latestBlockNumber);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// poolAddress -> EventMappings
|
|
43
|
+
const eventsToTest: Record<
|
|
44
|
+
Address,
|
|
45
|
+
{ blockNumber: number; events: EventMappings }
|
|
46
|
+
> = {
|
|
47
|
+
// BB_dUSDCV3
|
|
48
|
+
'0x2a84a042db06222c486bcb815e961f26599d0df6': {
|
|
49
|
+
blockNumber: 19676846,
|
|
50
|
+
events: {
|
|
51
|
+
Transfer: [
|
|
52
|
+
19676846, // DepositAA
|
|
53
|
+
19676851, // DepositBB
|
|
54
|
+
19681374, // Harvest
|
|
55
|
+
19681547, // WithdrawBB
|
|
56
|
+
20031918, // Harvest
|
|
57
|
+
20036646,
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
// BB_clearpool_fasanara_USDT
|
|
62
|
+
'0x3eb6318b8d9f362a0e1d99f6032edb1c4c602500': {
|
|
63
|
+
blockNumber: 18341913,
|
|
64
|
+
events: {
|
|
65
|
+
Transfer: [
|
|
66
|
+
18341913, // DepositBB
|
|
67
|
+
18357425, // WithdrawBB
|
|
68
|
+
18646128, // DepositBB
|
|
69
|
+
18804244, // WithdrawAA
|
|
70
|
+
18908679, // Harvest
|
|
71
|
+
20036646,
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
'0x7b713b1cb6eafd4061064581579ffccf7df21545': {
|
|
76
|
+
blockNumber: 18985108,
|
|
77
|
+
events: {
|
|
78
|
+
Transfer: [
|
|
79
|
+
18985108, // DepositBB
|
|
80
|
+
18997082, // WithdrawBB
|
|
81
|
+
19013537, // Harvest
|
|
82
|
+
19346397, // DepositAA
|
|
83
|
+
20036646,
|
|
84
|
+
],
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
Object.entries(eventsToTest).forEach(
|
|
90
|
+
([poolAddress, { blockNumber, events }]: [
|
|
91
|
+
string,
|
|
92
|
+
{ blockNumber: number; events: EventMappings },
|
|
93
|
+
]) => {
|
|
94
|
+
describe(`Events for ${poolAddress}`, () => {
|
|
95
|
+
let idleDaoPool: IdleDaoEventPool | null;
|
|
96
|
+
|
|
97
|
+
it(`idleDaoPool for ${network} - ${poolAddress} - ${blockNumber}`, async function () {
|
|
98
|
+
idleDaoPool = await idleDao.setupEventPool(
|
|
99
|
+
tokensList[poolAddress],
|
|
100
|
+
blockNumber,
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
expect(idleDaoPool).not.toBeNull();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
Object.entries(events).forEach(
|
|
107
|
+
([eventName, blockNumbers]: [string, number[]]) => {
|
|
108
|
+
describe(`${eventName}`, () => {
|
|
109
|
+
blockNumbers.forEach((blockNumber: number) => {
|
|
110
|
+
it(`State after ${blockNumber}`, async function () {
|
|
111
|
+
await testEventSubscriber(
|
|
112
|
+
idleDaoPool!,
|
|
113
|
+
idleDaoPool!.addressesSubscribed,
|
|
114
|
+
(_blockNumber: number) =>
|
|
115
|
+
fetchPoolState(
|
|
116
|
+
idleDaoPool!,
|
|
117
|
+
_blockNumber,
|
|
118
|
+
poolAddress,
|
|
119
|
+
logger,
|
|
120
|
+
),
|
|
121
|
+
blockNumber,
|
|
122
|
+
`${dexKey}_${poolAddress}`,
|
|
123
|
+
dexHelper.provider,
|
|
124
|
+
);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
},
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
},
|
|
132
|
+
);
|
|
133
|
+
});
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import dotenv from 'dotenv';
|
|
2
|
+
dotenv.config();
|
|
3
|
+
|
|
4
|
+
import { DummyDexHelper } from '../../dex-helper/index';
|
|
5
|
+
import { Network, SwapSide } from '../../constants';
|
|
6
|
+
import { IdleDao } from './idle-dao';
|
|
7
|
+
import { checkPoolPrices, checkPoolsLiquidity } from '../../../tests/utils';
|
|
8
|
+
import { Tokens } from '../../../tests/constants-e2e';
|
|
9
|
+
import { BI_POWS } from '../../bigint-constants';
|
|
10
|
+
|
|
11
|
+
async function testForIntegration(
|
|
12
|
+
network: Network,
|
|
13
|
+
dexKey: string,
|
|
14
|
+
tokenASymbol: string,
|
|
15
|
+
tokenBSymbol: string,
|
|
16
|
+
tokenAAmount: string,
|
|
17
|
+
tokenBAmount: string,
|
|
18
|
+
) {
|
|
19
|
+
const TokenA = Tokens[network][tokenASymbol];
|
|
20
|
+
const TokenB = Tokens[network][tokenBSymbol];
|
|
21
|
+
const tokenAAmounts = [
|
|
22
|
+
0n,
|
|
23
|
+
BigInt(tokenAAmount),
|
|
24
|
+
BigInt(tokenAAmount) * BigInt(2),
|
|
25
|
+
];
|
|
26
|
+
const tokenBAmounts = [
|
|
27
|
+
0n,
|
|
28
|
+
BigInt(tokenBAmount),
|
|
29
|
+
BigInt(tokenBAmount) * BigInt(2),
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
describe('IdleDao', function () {
|
|
33
|
+
it('getPoolIdentifiers and getPricesVolume SELL', async function () {
|
|
34
|
+
const dexHelper = new DummyDexHelper(network);
|
|
35
|
+
const blocknumber = await dexHelper.web3Provider.eth.getBlockNumber();
|
|
36
|
+
const idleDao = new IdleDao(network, dexKey, dexHelper);
|
|
37
|
+
|
|
38
|
+
await idleDao.initializePricing(blocknumber);
|
|
39
|
+
|
|
40
|
+
const pools = await idleDao.getPoolIdentifiers(
|
|
41
|
+
TokenA,
|
|
42
|
+
TokenB,
|
|
43
|
+
SwapSide.SELL,
|
|
44
|
+
blocknumber,
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
expect(pools.length).toBeGreaterThan(0);
|
|
48
|
+
|
|
49
|
+
const poolPrices = await idleDao.getPricesVolume(
|
|
50
|
+
TokenA,
|
|
51
|
+
TokenB,
|
|
52
|
+
tokenAAmounts,
|
|
53
|
+
SwapSide.SELL,
|
|
54
|
+
blocknumber,
|
|
55
|
+
pools,
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
expect(poolPrices).not.toBeNull();
|
|
59
|
+
checkPoolPrices(poolPrices!, tokenAAmounts, SwapSide.SELL, dexKey);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('getPoolIdentifiers and getPricesVolume BUY', async function () {
|
|
63
|
+
const dexHelper = new DummyDexHelper(network);
|
|
64
|
+
const blocknumber = await dexHelper.web3Provider.eth.getBlockNumber();
|
|
65
|
+
const idleDao = new IdleDao(network, dexKey, dexHelper);
|
|
66
|
+
|
|
67
|
+
await idleDao.initializePricing(blocknumber);
|
|
68
|
+
|
|
69
|
+
const pools = await idleDao.getPoolIdentifiers(
|
|
70
|
+
TokenA,
|
|
71
|
+
TokenB,
|
|
72
|
+
SwapSide.BUY,
|
|
73
|
+
blocknumber,
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
expect(pools.length).toBeGreaterThan(0);
|
|
77
|
+
|
|
78
|
+
const poolPrices = await idleDao.getPricesVolume(
|
|
79
|
+
TokenA,
|
|
80
|
+
TokenB,
|
|
81
|
+
tokenBAmounts,
|
|
82
|
+
SwapSide.BUY,
|
|
83
|
+
blocknumber,
|
|
84
|
+
pools,
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
expect(poolPrices).not.toBeNull();
|
|
88
|
+
checkPoolPrices(poolPrices!, tokenBAmounts, SwapSide.BUY, dexKey);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('getTopPoolsForToken', async function () {
|
|
92
|
+
const dexHelper = new DummyDexHelper(network);
|
|
93
|
+
const idleDao = new IdleDao(network, dexKey, dexHelper);
|
|
94
|
+
|
|
95
|
+
const poolLiquidity = await idleDao.getTopPoolsForToken(
|
|
96
|
+
TokenA.address,
|
|
97
|
+
10,
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
checkPoolsLiquidity(poolLiquidity, TokenA.address, dexKey);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
describe('IdleDao E2E', () => {
|
|
106
|
+
const dexKey = 'IdleDao';
|
|
107
|
+
|
|
108
|
+
describe('Mainnet', () => {
|
|
109
|
+
const network = Network.MAINNET;
|
|
110
|
+
const tests = [
|
|
111
|
+
{
|
|
112
|
+
tokenASymbol: 'STETH',
|
|
113
|
+
tokenBSymbol: 'AA_wstETH',
|
|
114
|
+
tokenAAmount: '1000000000000000000',
|
|
115
|
+
tokenBAmount: '1000000000000000000',
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
tokenASymbol: 'USDC',
|
|
119
|
+
tokenBSymbol: 'AA_idle_cpPOR-USDC',
|
|
120
|
+
tokenAAmount: '1000000',
|
|
121
|
+
tokenBAmount: '1000000000000000000',
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
tokenASymbol: 'USDT',
|
|
125
|
+
tokenBSymbol: 'BB_idle_cpFAS-USDT',
|
|
126
|
+
tokenAAmount: '1000000',
|
|
127
|
+
tokenBAmount: '1000000000000000000',
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
tokenASymbol: 'WETH',
|
|
131
|
+
tokenBSymbol: 'BB_Re7WETH',
|
|
132
|
+
tokenAAmount: '100000000000000',
|
|
133
|
+
tokenBAmount: '100000000000000',
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
tokenASymbol: 'WETH',
|
|
137
|
+
tokenBSymbol: 'AA_Re7WETH',
|
|
138
|
+
tokenAAmount: '1000000000000000',
|
|
139
|
+
tokenBAmount: '1000000000000000',
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
tokenASymbol: 'STETH',
|
|
143
|
+
tokenBSymbol: 'AA_iETHv2',
|
|
144
|
+
tokenAAmount: '1000000000000000',
|
|
145
|
+
tokenBAmount: '1000000000000000',
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
tokenASymbol: 'STETH',
|
|
149
|
+
tokenBSymbol: 'BB_iETHv2',
|
|
150
|
+
tokenAAmount: '1000000000000000',
|
|
151
|
+
tokenBAmount: '1000000000000000',
|
|
152
|
+
},
|
|
153
|
+
];
|
|
154
|
+
|
|
155
|
+
tests.forEach(testData => {
|
|
156
|
+
testForIntegration(
|
|
157
|
+
network,
|
|
158
|
+
dexKey,
|
|
159
|
+
testData.tokenASymbol,
|
|
160
|
+
testData.tokenBSymbol,
|
|
161
|
+
testData.tokenAAmount,
|
|
162
|
+
testData.tokenBAmount,
|
|
163
|
+
);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
});
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import { AbiItem } from 'web3-utils';
|
|
2
|
+
import BigNumber from 'bignumber.js';
|
|
3
|
+
import { Contract } from 'web3-eth-contract';
|
|
4
|
+
import { Interface } from '@ethersproject/abi';
|
|
5
|
+
import { DeepReadonly } from 'ts-essentials';
|
|
6
|
+
import { Log, Logger } from '../../types';
|
|
7
|
+
import { catchParseLogError } from '../../utils';
|
|
8
|
+
import ERC20_ABI from '../../abi/erc20.json';
|
|
9
|
+
import CDO_ABI from '../../abi/idle-dao/idle-cdo.json';
|
|
10
|
+
import { StatefulEventSubscriber } from '../../stateful-event-subscriber';
|
|
11
|
+
import { IDexHelper } from '../../dex-helper/idex-helper';
|
|
12
|
+
import { PoolState, IdleToken, PoolsState } from './types';
|
|
13
|
+
import { IdleDaoPollingPool } from './idle-dao-pooling-pool';
|
|
14
|
+
import { getTokensByCdoAddress } from './tokens';
|
|
15
|
+
import { ObjWithUpdateInfo } from '../../lib/stateful-rpc-poller/types';
|
|
16
|
+
import { IdleDao } from './idle-dao';
|
|
17
|
+
|
|
18
|
+
type BlocksTransfers = Record<
|
|
19
|
+
number,
|
|
20
|
+
{
|
|
21
|
+
underlyingAmount: BigNumber;
|
|
22
|
+
idleAmount: BigNumber;
|
|
23
|
+
processed: boolean;
|
|
24
|
+
}
|
|
25
|
+
>;
|
|
26
|
+
|
|
27
|
+
export class IdleDaoEventPool extends StatefulEventSubscriber<PoolState> {
|
|
28
|
+
handlers: {
|
|
29
|
+
[event: string]: (
|
|
30
|
+
event: any,
|
|
31
|
+
state: DeepReadonly<PoolState>,
|
|
32
|
+
log: Readonly<Log>,
|
|
33
|
+
) => Promise<DeepReadonly<PoolState> | null>;
|
|
34
|
+
} = {};
|
|
35
|
+
|
|
36
|
+
logDecoder: (log: Log) => any;
|
|
37
|
+
|
|
38
|
+
cdoContract: Contract;
|
|
39
|
+
poolInterface: Interface;
|
|
40
|
+
addressesSubscribed: string[];
|
|
41
|
+
blocksTransfers: BlocksTransfers = {};
|
|
42
|
+
|
|
43
|
+
constructor(
|
|
44
|
+
readonly parentName: string,
|
|
45
|
+
protected network: number,
|
|
46
|
+
protected dexHelper: IDexHelper,
|
|
47
|
+
protected logger: Logger,
|
|
48
|
+
protected idleToken: IdleToken,
|
|
49
|
+
protected IdleDao: IdleDao,
|
|
50
|
+
) {
|
|
51
|
+
// TODO: Add pool name
|
|
52
|
+
super(parentName, idleToken.idleSymbol, dexHelper, logger);
|
|
53
|
+
|
|
54
|
+
this.poolInterface = new Interface(ERC20_ABI);
|
|
55
|
+
|
|
56
|
+
// TODO: make logDecoder decode logs that
|
|
57
|
+
this.logDecoder = (log: Log) => this.poolInterface.parseLog(log);
|
|
58
|
+
|
|
59
|
+
this.addressesSubscribed = [idleToken.address.toLowerCase()];
|
|
60
|
+
|
|
61
|
+
const cdoTokens = getTokensByCdoAddress(this.idleToken.cdoAddress);
|
|
62
|
+
if (cdoTokens) {
|
|
63
|
+
cdoTokens.forEach((idleToken: IdleToken) => {
|
|
64
|
+
if (
|
|
65
|
+
!this.addressesSubscribed.includes(
|
|
66
|
+
idleToken.idleAddress.toLowerCase(),
|
|
67
|
+
)
|
|
68
|
+
) {
|
|
69
|
+
this.addressesSubscribed.push(idleToken.idleAddress.toLowerCase());
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// this.logger.debug('addressesSubscribed', this.idleToken.idleAddress, this.addressesSubscribed)
|
|
75
|
+
|
|
76
|
+
// Add handlers
|
|
77
|
+
this.handlers['Transfer'] = this.handleCoinTransfer.bind(this);
|
|
78
|
+
|
|
79
|
+
this.idleToken = idleToken;
|
|
80
|
+
this.cdoContract = new dexHelper.web3Provider.eth.Contract(
|
|
81
|
+
CDO_ABI as AbiItem[],
|
|
82
|
+
this.idleToken.cdoAddress,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* The function is called every time any of the subscribed
|
|
88
|
+
* addresses release log. The function accepts the current
|
|
89
|
+
* state, updates the state according to the log, and returns
|
|
90
|
+
* the updated state.
|
|
91
|
+
* @param state - Current state of event subscriber
|
|
92
|
+
* @param log - Log released by one of the subscribed addresses
|
|
93
|
+
* @returns Updates state of the event subscriber after the log
|
|
94
|
+
*/
|
|
95
|
+
protected async processLog(
|
|
96
|
+
state: DeepReadonly<PoolState>,
|
|
97
|
+
log: Readonly<Log>,
|
|
98
|
+
): Promise<DeepReadonly<PoolState> | null> {
|
|
99
|
+
try {
|
|
100
|
+
const event = this.logDecoder(log);
|
|
101
|
+
if (event.name in this.handlers) {
|
|
102
|
+
const result = await this.handlers[event.name](event, state, log);
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
} catch (e) {
|
|
106
|
+
catchParseLogError(e, this.logger);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* The function generates state using on-chain calls. This
|
|
114
|
+
* function is called to regenerate state if the event based
|
|
115
|
+
* system fails to fetch events and the local state is no
|
|
116
|
+
* more correct.
|
|
117
|
+
* @param blockNumber - Blocknumber for which the state should
|
|
118
|
+
* should be generated
|
|
119
|
+
* @returns state of the event subscriber at blocknumber
|
|
120
|
+
*/
|
|
121
|
+
async generateState(blockNumber: number): Promise<DeepReadonly<PoolState>> {
|
|
122
|
+
const tranchePrice = await this.cdoContract.methods['virtualPrice'](
|
|
123
|
+
this.idleToken.idleAddress,
|
|
124
|
+
).call({}, blockNumber);
|
|
125
|
+
return {
|
|
126
|
+
tokenPrice: BigInt(tranchePrice),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
setBlockState(blockNumber: number, state: PoolState): void {
|
|
131
|
+
this.logger.debug(
|
|
132
|
+
'setBlockState',
|
|
133
|
+
blockNumber,
|
|
134
|
+
this.idleToken.idleAddress,
|
|
135
|
+
state,
|
|
136
|
+
);
|
|
137
|
+
return this._setState(state, blockNumber);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async handleCoinTransfer(
|
|
141
|
+
event: any,
|
|
142
|
+
state: PoolState,
|
|
143
|
+
log: Log,
|
|
144
|
+
): Promise<DeepReadonly<PoolState> | null> {
|
|
145
|
+
const idleTokens = getTokensByCdoAddress(this.idleToken.cdoAddress);
|
|
146
|
+
if (!idleTokens) return null;
|
|
147
|
+
|
|
148
|
+
const pollingPool = new IdleDaoPollingPool(
|
|
149
|
+
this.parentName,
|
|
150
|
+
this.parentName,
|
|
151
|
+
this.dexHelper,
|
|
152
|
+
idleTokens,
|
|
153
|
+
this.logger,
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
const blockNumber = log.blockNumber;
|
|
157
|
+
let poolsState: ObjWithUpdateInfo<PoolsState> | null = null;
|
|
158
|
+
|
|
159
|
+
if (log.address.toLowerCase() === this.idleToken.address.toLowerCase()) {
|
|
160
|
+
if (
|
|
161
|
+
event.args.dst.toLowerCase() ===
|
|
162
|
+
this.idleToken.cdoAddress.toLowerCase() ||
|
|
163
|
+
event.args.src.toLowerCase() === this.idleToken.cdoAddress.toLowerCase()
|
|
164
|
+
) {
|
|
165
|
+
poolsState = await pollingPool.getAggregatedBlockData(blockNumber);
|
|
166
|
+
}
|
|
167
|
+
} else if (this.addressesSubscribed.includes(log.address.toLowerCase())) {
|
|
168
|
+
poolsState = await pollingPool.getAggregatedBlockData(blockNumber);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Update pools with the new state
|
|
172
|
+
if (poolsState) {
|
|
173
|
+
Object.keys(poolsState.value as PoolsState).forEach(
|
|
174
|
+
(idleAddress: string) => {
|
|
175
|
+
const poolState = poolsState?.value?.[idleAddress];
|
|
176
|
+
if (poolState) {
|
|
177
|
+
this.IdleDao.setEventPoolStateBlock(
|
|
178
|
+
idleAddress,
|
|
179
|
+
poolsState!.blockNumber,
|
|
180
|
+
poolState,
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
// Overwrite state
|
|
184
|
+
if (idleAddress === this.idleToken.idleAddress) {
|
|
185
|
+
state = poolState;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return state;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
processBlockTransfer(blockNumber: number, state: PoolState): PoolState {
|
|
196
|
+
if (
|
|
197
|
+
!this.blocksTransfers[blockNumber] ||
|
|
198
|
+
this.blocksTransfers[blockNumber].processed
|
|
199
|
+
)
|
|
200
|
+
return state;
|
|
201
|
+
const idleAmount = this.blocksTransfers[blockNumber].idleAmount.div(1e18);
|
|
202
|
+
const underlyingAmount = this.blocksTransfers[
|
|
203
|
+
blockNumber
|
|
204
|
+
].underlyingAmount.div(`1e${this.idleToken.decimals}`);
|
|
205
|
+
if (idleAmount.gt(0) && underlyingAmount.gt(0)) {
|
|
206
|
+
const tokenPrice = BigInt(
|
|
207
|
+
BigNumber.maximum(idleAmount, underlyingAmount)
|
|
208
|
+
.div(BigNumber.minimum(idleAmount, underlyingAmount))
|
|
209
|
+
.times(`1e${this.idleToken.decimals}`)
|
|
210
|
+
.toFixed(0),
|
|
211
|
+
);
|
|
212
|
+
this.logger.debug(
|
|
213
|
+
'tokenPrice',
|
|
214
|
+
blockNumber,
|
|
215
|
+
this.idleToken.idleSymbol,
|
|
216
|
+
this.idleToken.decimals,
|
|
217
|
+
idleAmount.toString(),
|
|
218
|
+
underlyingAmount.toString(),
|
|
219
|
+
tokenPrice.toString(),
|
|
220
|
+
);
|
|
221
|
+
this.blocksTransfers[blockNumber].processed = true;
|
|
222
|
+
return {
|
|
223
|
+
tokenPrice,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
return state;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async handleCoinTransfer_old(
|
|
230
|
+
event: any,
|
|
231
|
+
state: PoolState,
|
|
232
|
+
log: Log,
|
|
233
|
+
): Promise<DeepReadonly<PoolState> | null> {
|
|
234
|
+
// Handle underlying token transfer
|
|
235
|
+
if (log.address.toLowerCase() === this.idleToken.address.toLowerCase()) {
|
|
236
|
+
if (
|
|
237
|
+
event.args.dst.toLowerCase() ===
|
|
238
|
+
this.idleToken.cdoAddress.toLowerCase() ||
|
|
239
|
+
event.args.src.toLowerCase() === this.idleToken.cdoAddress.toLowerCase()
|
|
240
|
+
) {
|
|
241
|
+
if (!this.blocksTransfers[log.blockNumber]) {
|
|
242
|
+
this.blocksTransfers[log.blockNumber] = {
|
|
243
|
+
processed: false,
|
|
244
|
+
idleAmount: BigNumber(0),
|
|
245
|
+
underlyingAmount: BigNumber(0),
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
this.logger.debug(
|
|
249
|
+
'UnderlyingAmount',
|
|
250
|
+
log.blockNumber,
|
|
251
|
+
'event',
|
|
252
|
+
event,
|
|
253
|
+
'log',
|
|
254
|
+
log,
|
|
255
|
+
BigNumber(BigInt(event.args.wad).toString()).toFixed(),
|
|
256
|
+
);
|
|
257
|
+
this.blocksTransfers[log.blockNumber].underlyingAmount = BigNumber(
|
|
258
|
+
BigInt(event.args.wad).toString(),
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
// Handle idle token transfer
|
|
263
|
+
else if (
|
|
264
|
+
log.address.toLowerCase() === this.idleToken.idleAddress.toLowerCase()
|
|
265
|
+
) {
|
|
266
|
+
this.logger.debug(
|
|
267
|
+
'IdleToken Transfer',
|
|
268
|
+
log.blockNumber,
|
|
269
|
+
'event',
|
|
270
|
+
event,
|
|
271
|
+
'log',
|
|
272
|
+
log,
|
|
273
|
+
);
|
|
274
|
+
if (
|
|
275
|
+
event.args.src.toLowerCase() ===
|
|
276
|
+
'0x0000000000000000000000000000000000000000' ||
|
|
277
|
+
event.args.dst.toLowerCase() ===
|
|
278
|
+
'0x0000000000000000000000000000000000000000'
|
|
279
|
+
) {
|
|
280
|
+
if (!this.blocksTransfers[log.blockNumber]) {
|
|
281
|
+
this.blocksTransfers[log.blockNumber] = {
|
|
282
|
+
processed: false,
|
|
283
|
+
idleAmount: BigNumber(0),
|
|
284
|
+
underlyingAmount: BigNumber(0),
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
this.logger.debug(
|
|
288
|
+
'IdleAmount',
|
|
289
|
+
log.blockNumber,
|
|
290
|
+
'event',
|
|
291
|
+
event,
|
|
292
|
+
'log',
|
|
293
|
+
log,
|
|
294
|
+
BigNumber(BigInt(event.args.wad).toString()).toFixed(),
|
|
295
|
+
);
|
|
296
|
+
this.blocksTransfers[log.blockNumber].idleAmount = BigNumber(
|
|
297
|
+
BigInt(event.args.wad).toString(),
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return this.processBlockTransfer(log.blockNumber, state);
|
|
303
|
+
}
|
|
304
|
+
}
|