@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,87 @@
|
|
|
1
|
+
import { Interface } from '@ethersproject/abi';
|
|
2
|
+
import { uint256ToBigInt } from '../../lib/decoders';
|
|
3
|
+
import CDO_ABI from '../../abi/idle-dao/idle-cdo.json';
|
|
4
|
+
import { IDexHelper } from '../../dex-helper/idex-helper';
|
|
5
|
+
import { PoolState, IdleToken, PoolsState } from './types';
|
|
6
|
+
import { MultiCallParams, MultiResult } from '../../lib/multi-wrapper';
|
|
7
|
+
import { pollingManagerCbExtractor } from '../../lib/stateful-rpc-poller/utils';
|
|
8
|
+
import { StatefulRpcPoller } from '../../lib/stateful-rpc-poller/stateful-rpc-poller';
|
|
9
|
+
import { StatePollingManager } from '../../lib/stateful-rpc-poller/state-polling-manager';
|
|
10
|
+
import { Logger } from '../../types';
|
|
11
|
+
import { ObjWithUpdateInfo } from '../../lib/stateful-rpc-poller/types';
|
|
12
|
+
|
|
13
|
+
type MulticallResultOutputs = bigint;
|
|
14
|
+
|
|
15
|
+
export class IdleDaoPollingPool extends StatefulRpcPoller<
|
|
16
|
+
PoolsState,
|
|
17
|
+
MulticallResultOutputs
|
|
18
|
+
> {
|
|
19
|
+
constructor(
|
|
20
|
+
dexKey: string,
|
|
21
|
+
poolIdentifier: string,
|
|
22
|
+
dexHelper: IDexHelper,
|
|
23
|
+
protected idleTokens: IdleToken[],
|
|
24
|
+
protected logger: Logger,
|
|
25
|
+
) {
|
|
26
|
+
const callbacks = pollingManagerCbExtractor(
|
|
27
|
+
StatePollingManager.getInstance(dexHelper),
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
super(dexKey, poolIdentifier, dexHelper, 0, 0, false, callbacks);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async getAggregatedBlockData(
|
|
34
|
+
blockNumber: number,
|
|
35
|
+
): Promise<ObjWithUpdateInfo<PoolsState> | null> {
|
|
36
|
+
const multiCalls = this.getFetchStateWithBlockInfoMultiCalls();
|
|
37
|
+
try {
|
|
38
|
+
const lastUpdatedAtMs = Date.now();
|
|
39
|
+
const aggregatedResults = (await this.dexHelper.multiWrapper.tryAggregate<
|
|
40
|
+
number | MulticallResultOutputs
|
|
41
|
+
>(
|
|
42
|
+
true,
|
|
43
|
+
multiCalls as MultiCallParams<MulticallResultOutputs | number>[],
|
|
44
|
+
blockNumber,
|
|
45
|
+
)) as [MultiResult<number>, ...MultiResult<MulticallResultOutputs>[]];
|
|
46
|
+
|
|
47
|
+
return this.parseStateFromMultiResultsWithBlockInfo(
|
|
48
|
+
aggregatedResults,
|
|
49
|
+
lastUpdatedAtMs,
|
|
50
|
+
);
|
|
51
|
+
} catch (e) {
|
|
52
|
+
// this.logger.debug('ERROR fetching multicall data', blockNumber, e)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
protected _getFetchStateMultiCalls(): MultiCallParams<MulticallResultOutputs>[] {
|
|
59
|
+
const cdoInterface = new Interface(CDO_ABI);
|
|
60
|
+
return this.idleTokens.map(idleToken => ({
|
|
61
|
+
target: idleToken.cdoAddress,
|
|
62
|
+
callData: cdoInterface.encodeFunctionData('virtualPrice', [
|
|
63
|
+
idleToken.idleAddress,
|
|
64
|
+
]),
|
|
65
|
+
decodeFunction: uint256ToBigInt,
|
|
66
|
+
})) as MultiCallParams<MulticallResultOutputs>[];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
protected _parseStateFromMultiResults(
|
|
70
|
+
multiOutputs: MulticallResultOutputs[],
|
|
71
|
+
): PoolsState {
|
|
72
|
+
const output: PoolsState = multiOutputs.reduce(
|
|
73
|
+
(acc: PoolsState, output: MulticallResultOutputs, index: number) => {
|
|
74
|
+
const tokenPrice = output as bigint;
|
|
75
|
+
const idleToken = this.idleTokens[index];
|
|
76
|
+
return {
|
|
77
|
+
...acc,
|
|
78
|
+
[idleToken.idleAddress]: {
|
|
79
|
+
tokenPrice,
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
},
|
|
83
|
+
{},
|
|
84
|
+
);
|
|
85
|
+
return output;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
import { assert } from 'ts-essentials';
|
|
2
|
+
import { Interface } from '@ethersproject/abi';
|
|
3
|
+
import {
|
|
4
|
+
Token,
|
|
5
|
+
Address,
|
|
6
|
+
ExchangePrices,
|
|
7
|
+
PoolPrices,
|
|
8
|
+
AdapterExchangeParam,
|
|
9
|
+
SimpleExchangeParam,
|
|
10
|
+
PoolLiquidity,
|
|
11
|
+
Logger,
|
|
12
|
+
NumberAsString,
|
|
13
|
+
DexExchangeParam,
|
|
14
|
+
} from '../../types';
|
|
15
|
+
import { SwapSide, Network, NULL_ADDRESS } from '../../constants';
|
|
16
|
+
import * as CALLDATA_GAS_COST from '../../calldata-gas-cost';
|
|
17
|
+
import { getDexKeysWithNetwork, isETHAddress, getBigIntPow } from '../../utils';
|
|
18
|
+
import { Context, IDex } from '../../dex/idex';
|
|
19
|
+
import { IDexHelper } from '../../dex-helper/idex-helper';
|
|
20
|
+
import {
|
|
21
|
+
IdleDaoData,
|
|
22
|
+
Param,
|
|
23
|
+
PoolFunctions,
|
|
24
|
+
IdleToken,
|
|
25
|
+
PoolState,
|
|
26
|
+
} from './types';
|
|
27
|
+
import { SimpleExchange } from '../simple-exchange';
|
|
28
|
+
import { Config, Adapters } from './config';
|
|
29
|
+
import { IdleDaoEventPool } from './idle-dao-pool';
|
|
30
|
+
import { fetchTokenList_api } from './utils';
|
|
31
|
+
import {
|
|
32
|
+
getIdleTokenIfIdleDaoPair,
|
|
33
|
+
setTokensOnNetwork,
|
|
34
|
+
getPoolsByTokenAddress,
|
|
35
|
+
getTokenFromIdleToken,
|
|
36
|
+
getTokensByNetwork,
|
|
37
|
+
} from './tokens';
|
|
38
|
+
import FACTORY_ABI from '../../abi/idle-dao/idle-cdo-factory.json';
|
|
39
|
+
import CDO_ABI from '../../abi/idle-dao/idle-cdo.json';
|
|
40
|
+
import { IdleDaoPollingPool } from './idle-dao-pooling-pool';
|
|
41
|
+
import { ObjWithUpdateInfo } from '../../lib/stateful-rpc-poller/types';
|
|
42
|
+
import { StatePollingManager } from '../../lib/stateful-rpc-poller/state-polling-manager';
|
|
43
|
+
|
|
44
|
+
export const TOKEN_LIST_CACHE_KEY = 'token-list';
|
|
45
|
+
const TOKEN_LIST_TTL_SECONDS = 24 * 60 * 60;
|
|
46
|
+
const TOKEN_LIST_LOCAL_TTL_SECONDS = 3 * 60 * 60;
|
|
47
|
+
|
|
48
|
+
export class IdleDao extends SimpleExchange implements IDex<IdleDaoData> {
|
|
49
|
+
protected eventPools: Record<string, IdleDaoEventPool> = {};
|
|
50
|
+
|
|
51
|
+
readonly hasConstantPriceLargeAmounts = false;
|
|
52
|
+
// TODO: set true here if protocols works only with wrapped asset
|
|
53
|
+
readonly needWrapNative = true;
|
|
54
|
+
|
|
55
|
+
readonly isFeeOnTransferSupported = false;
|
|
56
|
+
|
|
57
|
+
public static dexKeysWithNetwork: { key: string; networks: Network[] }[] =
|
|
58
|
+
getDexKeysWithNetwork(Config);
|
|
59
|
+
|
|
60
|
+
logger: Logger;
|
|
61
|
+
|
|
62
|
+
private cdo: Interface;
|
|
63
|
+
private factory: Interface;
|
|
64
|
+
|
|
65
|
+
private tokenList: IdleToken[] = [];
|
|
66
|
+
private idleDaoAuthToken: string;
|
|
67
|
+
|
|
68
|
+
protected pollingPool?: IdleDaoPollingPool;
|
|
69
|
+
|
|
70
|
+
protected pollingManager: StatePollingManager;
|
|
71
|
+
|
|
72
|
+
constructor(
|
|
73
|
+
readonly network: Network,
|
|
74
|
+
readonly dexKey: string,
|
|
75
|
+
readonly dexHelper: IDexHelper,
|
|
76
|
+
protected config = Config[dexKey][network],
|
|
77
|
+
protected adapters = Adapters[network] || {}, // TODO: add any additional optional params to support other fork DEXes
|
|
78
|
+
) {
|
|
79
|
+
super(dexHelper, dexKey);
|
|
80
|
+
this.logger = dexHelper.getLogger(dexKey);
|
|
81
|
+
this.cdo = new Interface(CDO_ABI);
|
|
82
|
+
this.factory = new Interface(FACTORY_ABI);
|
|
83
|
+
this.pollingManager = StatePollingManager.getInstance(dexHelper);
|
|
84
|
+
|
|
85
|
+
const idleDaoAuthToken = dexHelper.config.data.idleDaoAuthToken;
|
|
86
|
+
assert(
|
|
87
|
+
idleDaoAuthToken !== undefined,
|
|
88
|
+
'idleDaoAuthToken auth token is not specified with env variable',
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
this.idleDaoAuthToken = idleDaoAuthToken;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
getEventPool(idleAddress: string): IdleDaoEventPool | null {
|
|
95
|
+
return this.eventPools[idleAddress] || null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
setEventPoolStateBlock(
|
|
99
|
+
idleAddress: string,
|
|
100
|
+
blockNumber: number,
|
|
101
|
+
state: PoolState,
|
|
102
|
+
): void {
|
|
103
|
+
const eventPool = this.getEventPool(idleAddress);
|
|
104
|
+
if (!eventPool) return;
|
|
105
|
+
return eventPool.setBlockState(blockNumber, state);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async setupEventPool(
|
|
109
|
+
idleToken: IdleToken,
|
|
110
|
+
blockNumber: number,
|
|
111
|
+
): Promise<IdleDaoEventPool> {
|
|
112
|
+
const idleDaoEventPool = new IdleDaoEventPool(
|
|
113
|
+
this.dexKey,
|
|
114
|
+
this.network,
|
|
115
|
+
this.dexHelper,
|
|
116
|
+
this.logger,
|
|
117
|
+
idleToken,
|
|
118
|
+
this,
|
|
119
|
+
);
|
|
120
|
+
await idleDaoEventPool.initialize(blockNumber);
|
|
121
|
+
this.eventPools[idleToken.idleAddress] = idleDaoEventPool;
|
|
122
|
+
return idleDaoEventPool;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async getTokensList(blockNumber: number): Promise<Record<string, IdleToken>> {
|
|
126
|
+
if (!this.tokenList) {
|
|
127
|
+
this.tokenList = await fetchTokenList_api(
|
|
128
|
+
this.network,
|
|
129
|
+
this.dexHelper,
|
|
130
|
+
blockNumber,
|
|
131
|
+
this.cdo,
|
|
132
|
+
this.erc20Interface,
|
|
133
|
+
this.dexHelper.multiWrapper,
|
|
134
|
+
this.idleDaoAuthToken,
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return this.tokenList.reduce(
|
|
139
|
+
(acc: Record<string, IdleToken>, idleToken: IdleToken) => {
|
|
140
|
+
return {
|
|
141
|
+
...acc,
|
|
142
|
+
[idleToken.idleAddress]: idleToken,
|
|
143
|
+
};
|
|
144
|
+
},
|
|
145
|
+
{},
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Initialize pricing is called once in the start of
|
|
150
|
+
// pricing service. It is intended to setup the integration
|
|
151
|
+
// for pricing requests. It is optional for a DEX to
|
|
152
|
+
// implement this function
|
|
153
|
+
async initializePricing(blockNumber: number) {
|
|
154
|
+
let cachedTokenList = await this.dexHelper.cache.getAndCacheLocally(
|
|
155
|
+
this.dexKey,
|
|
156
|
+
this.network,
|
|
157
|
+
TOKEN_LIST_CACHE_KEY,
|
|
158
|
+
TOKEN_LIST_LOCAL_TTL_SECONDS,
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
if (cachedTokenList !== null) {
|
|
162
|
+
const tokens: IdleToken[] = JSON.parse(cachedTokenList);
|
|
163
|
+
setTokensOnNetwork(this.network, tokens);
|
|
164
|
+
|
|
165
|
+
await Promise.all(
|
|
166
|
+
tokens.map((idleToken: IdleToken) =>
|
|
167
|
+
this.setupEventPool(idleToken, blockNumber),
|
|
168
|
+
),
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
this.tokenList = tokens;
|
|
172
|
+
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
this.tokenList = await fetchTokenList_api(
|
|
177
|
+
this.network,
|
|
178
|
+
this.dexHelper,
|
|
179
|
+
blockNumber,
|
|
180
|
+
this.cdo,
|
|
181
|
+
this.erc20Interface,
|
|
182
|
+
this.dexHelper.multiWrapper,
|
|
183
|
+
this.idleDaoAuthToken,
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
// console.log('api_tokenList', this.tokenList);
|
|
187
|
+
|
|
188
|
+
await this.dexHelper.cache.setex(
|
|
189
|
+
this.dexKey,
|
|
190
|
+
this.network,
|
|
191
|
+
TOKEN_LIST_CACHE_KEY,
|
|
192
|
+
TOKEN_LIST_TTL_SECONDS,
|
|
193
|
+
JSON.stringify(this.tokenList),
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
setTokensOnNetwork(this.network, this.tokenList);
|
|
197
|
+
|
|
198
|
+
await Promise.all(
|
|
199
|
+
this.tokenList.map((idleToken: IdleToken) =>
|
|
200
|
+
this.setupEventPool(idleToken, blockNumber),
|
|
201
|
+
),
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
// Initialize polling pool
|
|
205
|
+
if (this.pollingPool === undefined) {
|
|
206
|
+
this._initializePollingPool(this.tokenList);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Returns the list of contract adapters (name and index)
|
|
211
|
+
// for a buy/sell. Return null if there are no adapters.
|
|
212
|
+
getAdapters(side: SwapSide): { name: string; index: number }[] | null {
|
|
213
|
+
return this.adapters[side] ? this.adapters[side] : null;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
getIdentifier() {
|
|
217
|
+
return `${this.dexKey}_idledaov1`;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
private _initializePollingPool(idleTokens: IdleToken[]) {
|
|
221
|
+
this.pollingPool = new IdleDaoPollingPool(
|
|
222
|
+
this.dexKey,
|
|
223
|
+
this.getIdentifier(),
|
|
224
|
+
this.dexHelper,
|
|
225
|
+
idleTokens,
|
|
226
|
+
this.logger,
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
this.pollingManager.initializeAllPendingPools();
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
private _getPoolIdentifier(srcToken: Token, destToken: Token): string {
|
|
233
|
+
return (
|
|
234
|
+
this.dexKey +
|
|
235
|
+
[srcToken.address.toLowerCase(), destToken.address.toLowerCase()]
|
|
236
|
+
.sort((a, b) => (a > b ? 1 : -1))
|
|
237
|
+
.join('_')
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Returns list of pool identifiers that can be used
|
|
242
|
+
// for a given swap. poolIdentifiers must be unique
|
|
243
|
+
// across DEXes. It is recommended to use
|
|
244
|
+
// ${dexKey}_${poolAddress} as a poolIdentifier
|
|
245
|
+
async getPoolIdentifiers(
|
|
246
|
+
srcToken: Token,
|
|
247
|
+
destToken: Token,
|
|
248
|
+
side: SwapSide,
|
|
249
|
+
blockNumber: number,
|
|
250
|
+
): Promise<string[]> {
|
|
251
|
+
const idleToken = getIdleTokenIfIdleDaoPair(
|
|
252
|
+
this.network,
|
|
253
|
+
this.dexHelper.config.wrapETH(srcToken),
|
|
254
|
+
this.dexHelper.config.wrapETH(destToken),
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
if (idleToken === null) return [];
|
|
258
|
+
|
|
259
|
+
return [this._getPoolIdentifier(srcToken, destToken)];
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Returns pool prices for amounts.
|
|
263
|
+
// If limitPools is defined only pools in limitPools
|
|
264
|
+
// should be used. If limitPools is undefined then
|
|
265
|
+
// any pools can be used.
|
|
266
|
+
|
|
267
|
+
async getPricesVolume(
|
|
268
|
+
srcToken: Token,
|
|
269
|
+
destToken: Token,
|
|
270
|
+
amounts: bigint[],
|
|
271
|
+
side: SwapSide,
|
|
272
|
+
blockNumber: number,
|
|
273
|
+
limitPools?: string[],
|
|
274
|
+
): Promise<null | ExchangePrices<IdleDaoData>> {
|
|
275
|
+
try {
|
|
276
|
+
const _src = this.dexHelper.config.wrapETH(srcToken);
|
|
277
|
+
const _dst = this.dexHelper.config.wrapETH(destToken);
|
|
278
|
+
|
|
279
|
+
// Look for idleToken
|
|
280
|
+
const idleToken = getIdleTokenIfIdleDaoPair(this.network, _src, _dst);
|
|
281
|
+
if (!idleToken) {
|
|
282
|
+
// console.log(
|
|
283
|
+
// 'IdleToken NOT FOUND -',
|
|
284
|
+
// blockNumber,
|
|
285
|
+
// this.network,
|
|
286
|
+
// _src,
|
|
287
|
+
// _dst,
|
|
288
|
+
// );
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const fromIdleToken =
|
|
293
|
+
idleToken.idleAddress.toLowerCase() == _src.address.toLowerCase();
|
|
294
|
+
|
|
295
|
+
// Cannot buy with IdleTokens
|
|
296
|
+
// if (fromIdleToken && side === SwapSide.BUY) return null
|
|
297
|
+
|
|
298
|
+
const eventPool: IdleDaoEventPool =
|
|
299
|
+
this.eventPools[idleToken.idleAddress];
|
|
300
|
+
const eventPoolState: PoolState | null = eventPool.getState(blockNumber);
|
|
301
|
+
|
|
302
|
+
// this.logger.debug(
|
|
303
|
+
// 'eventPoolState',
|
|
304
|
+
// blockNumber,
|
|
305
|
+
// idleToken.idleAddress,
|
|
306
|
+
// eventPoolState,
|
|
307
|
+
// );
|
|
308
|
+
|
|
309
|
+
if (!eventPoolState) {
|
|
310
|
+
// console.log('eventPoolState is NULL -', blockNumber, idleToken.idleAddress, eventPoolState)
|
|
311
|
+
return null;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const tokenPrice = eventPoolState.tokenPrice;
|
|
315
|
+
|
|
316
|
+
const unitVolume = getBigIntPow(
|
|
317
|
+
(side === SwapSide.SELL ? srcToken : destToken).decimals,
|
|
318
|
+
);
|
|
319
|
+
|
|
320
|
+
const prices = [unitVolume, ...amounts].map((amount: bigint) => {
|
|
321
|
+
let output = 0;
|
|
322
|
+
if (side === SwapSide.SELL) {
|
|
323
|
+
// SELL idleToken (amount = 1000000000000000000 AA_idle_cpPOR-USDC, output = 1000000000000000000*tokenPrice/1e18)
|
|
324
|
+
if (fromIdleToken) {
|
|
325
|
+
output = Math.round(
|
|
326
|
+
(parseFloat('' + tokenPrice) * parseFloat('' + amount)) /
|
|
327
|
+
parseFloat(`1e${idleToken.decimals}`),
|
|
328
|
+
);
|
|
329
|
+
// SELL underlyingToken (amount = 1000000 USDC), output = 1000000/tokenPrice*1e18
|
|
330
|
+
} else {
|
|
331
|
+
output = Math.round(
|
|
332
|
+
(parseFloat('' + amount) / parseFloat('' + tokenPrice)) *
|
|
333
|
+
parseFloat(`1e${idleToken.decimals}`),
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
} else {
|
|
337
|
+
// BUY idleToken (amount = 1000000 USDC, output = 1000000/tokenPrice*1e18)
|
|
338
|
+
if (fromIdleToken) {
|
|
339
|
+
output = Math.round(
|
|
340
|
+
(parseFloat('' + amount) / parseFloat('' + tokenPrice)) *
|
|
341
|
+
parseFloat(`1e${idleToken.decimals}`),
|
|
342
|
+
);
|
|
343
|
+
// BUY underlyingToken (amount = 1000000000000000000 AA_idle_cpPOR-USDC, output = 1000000000000000000*tokenPrice/1e18)
|
|
344
|
+
} else {
|
|
345
|
+
output = Math.round(
|
|
346
|
+
(parseFloat('' + tokenPrice) * parseFloat('' + amount)) /
|
|
347
|
+
parseFloat(`1e${idleToken.decimals}`),
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return BigInt(output);
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
// console.log('getPricesVolume', srcToken.address, destToken.address, fromIdleToken, side, unitVolume, amounts, prices)
|
|
355
|
+
|
|
356
|
+
// const unit = side === SwapSide.SELL ? prices[0] : unitVolume;
|
|
357
|
+
|
|
358
|
+
return [
|
|
359
|
+
{
|
|
360
|
+
unit: prices[0],
|
|
361
|
+
prices: prices.slice(1),
|
|
362
|
+
gasCost: this.config.lendingGasCost,
|
|
363
|
+
exchange: this.dexKey,
|
|
364
|
+
data: {
|
|
365
|
+
idleToken,
|
|
366
|
+
fromIdleToken,
|
|
367
|
+
},
|
|
368
|
+
poolAddresses: [fromIdleToken ? srcToken.address : destToken.address],
|
|
369
|
+
},
|
|
370
|
+
];
|
|
371
|
+
} catch (e) {
|
|
372
|
+
if (blockNumber === 0)
|
|
373
|
+
this.logger.error(
|
|
374
|
+
`Error_getPricesVolume: Aurelius block manager not yet instantiated`,
|
|
375
|
+
);
|
|
376
|
+
this.logger.error(`Error_getPrices:`, e);
|
|
377
|
+
return null;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Returns estimated gas cost of calldata for this DEX in multiSwap
|
|
382
|
+
getCalldataGasCost(poolPrices: PoolPrices<IdleDaoData>): number | number[] {
|
|
383
|
+
// TODO: update if there is any payload in getAdapterParam
|
|
384
|
+
return CALLDATA_GAS_COST.DEX_NO_PAYLOAD;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Encode params required by the exchange adapter
|
|
388
|
+
// Used for multiSwap, buy & megaSwap
|
|
389
|
+
// Hint: abiCoder.encodeParameter() could be useful
|
|
390
|
+
getAdapterParam(
|
|
391
|
+
srcToken: string,
|
|
392
|
+
destToken: string,
|
|
393
|
+
srcAmount: string,
|
|
394
|
+
destAmount: string,
|
|
395
|
+
data: IdleDaoData,
|
|
396
|
+
side: SwapSide,
|
|
397
|
+
): AdapterExchangeParam {
|
|
398
|
+
const idleToken = data.fromIdleToken ? srcToken : destToken; // Warning
|
|
399
|
+
const payload = this.abiCoder.encodeParameter(
|
|
400
|
+
{
|
|
401
|
+
ParentStruct: {
|
|
402
|
+
idleToken: 'address',
|
|
403
|
+
},
|
|
404
|
+
},
|
|
405
|
+
{ idleToken },
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
return {
|
|
409
|
+
// target exchange is not used by the contract
|
|
410
|
+
targetExchange: NULL_ADDRESS,
|
|
411
|
+
payload,
|
|
412
|
+
networkFee: '0',
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
getDexParam(
|
|
417
|
+
srcToken: Address,
|
|
418
|
+
destToken: Address,
|
|
419
|
+
srcAmount: NumberAsString,
|
|
420
|
+
destAmount: NumberAsString,
|
|
421
|
+
recipient: Address,
|
|
422
|
+
data: IdleDaoData,
|
|
423
|
+
side: SwapSide,
|
|
424
|
+
_: Context,
|
|
425
|
+
executorAddress: Address,
|
|
426
|
+
): DexExchangeParam {
|
|
427
|
+
const [Interface, swapCallee, swapFunction, swapFunctionParams] = ((): [
|
|
428
|
+
Interface,
|
|
429
|
+
Address,
|
|
430
|
+
PoolFunctions,
|
|
431
|
+
Param,
|
|
432
|
+
] => {
|
|
433
|
+
if (data.fromIdleToken) {
|
|
434
|
+
return [
|
|
435
|
+
this.cdo,
|
|
436
|
+
data.idleToken.cdoAddress,
|
|
437
|
+
PoolFunctions[`withdraw${data.idleToken.tokenType}`],
|
|
438
|
+
[srcAmount],
|
|
439
|
+
];
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
return [
|
|
443
|
+
this.cdo,
|
|
444
|
+
data.idleToken.cdoAddress,
|
|
445
|
+
PoolFunctions[`deposit${data.idleToken.tokenType}`],
|
|
446
|
+
[srcAmount, recipient],
|
|
447
|
+
];
|
|
448
|
+
})();
|
|
449
|
+
|
|
450
|
+
const exchangeData = Interface.encodeFunctionData(
|
|
451
|
+
swapFunction,
|
|
452
|
+
swapFunctionParams,
|
|
453
|
+
);
|
|
454
|
+
|
|
455
|
+
return {
|
|
456
|
+
needWrapNative: this.needWrapNative,
|
|
457
|
+
dexFuncHasRecipient: false,
|
|
458
|
+
exchangeData,
|
|
459
|
+
targetExchange: swapCallee,
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
async updatePoolState(): Promise<void> {
|
|
464
|
+
const blockNumber = await this.dexHelper.web3Provider.eth.getBlockNumber();
|
|
465
|
+
|
|
466
|
+
let idleTokens = getTokensByNetwork(this.network);
|
|
467
|
+
if (!idleTokens) {
|
|
468
|
+
await this.initializePricing(blockNumber);
|
|
469
|
+
}
|
|
470
|
+
idleTokens = getTokensByNetwork(this.network);
|
|
471
|
+
|
|
472
|
+
const state = await this.pollingPool!.getState();
|
|
473
|
+
|
|
474
|
+
// this.logger.debug('updatePoolState', blockNumber, state)
|
|
475
|
+
|
|
476
|
+
if (!state) {
|
|
477
|
+
this.logger.error(`Failed to updatePoolState. State is null.`);
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
Object.entries(state).forEach((state: any) => {
|
|
482
|
+
const idleAddress = state[0];
|
|
483
|
+
const poolState: ObjWithUpdateInfo<PoolState> = state[1];
|
|
484
|
+
const eventPool: IdleDaoEventPool = this.eventPools[idleAddress];
|
|
485
|
+
// this.logger.debug('Get eventPool', state, idleAddress, eventPool)
|
|
486
|
+
if (eventPool) {
|
|
487
|
+
// this.logger.debug('Set pool state', idleAddress, poolState.blockNumber, poolState.value)
|
|
488
|
+
eventPool.setBlockState(poolState.blockNumber, poolState.value);
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
async getTopPoolsForToken(
|
|
494
|
+
tokenAddress: Address,
|
|
495
|
+
limit: number,
|
|
496
|
+
): Promise<PoolLiquidity[]> {
|
|
497
|
+
const idleTokens: IdleToken[] = getPoolsByTokenAddress(tokenAddress);
|
|
498
|
+
|
|
499
|
+
return idleTokens
|
|
500
|
+
.map((idleToken: IdleToken, i) => ({
|
|
501
|
+
liquidityUSD: 0,
|
|
502
|
+
exchange: this.dexKey,
|
|
503
|
+
address: idleToken.idleAddress,
|
|
504
|
+
connectorTokens: [getTokenFromIdleToken(idleToken)],
|
|
505
|
+
}))
|
|
506
|
+
.slice(0, limit);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { Token } from '../../types';
|
|
2
|
+
import { Network } from '../../constants';
|
|
3
|
+
|
|
4
|
+
import { IdleToken } from './types';
|
|
5
|
+
|
|
6
|
+
export const Tokens: { [network: number]: { [symbol: string]: IdleToken } } =
|
|
7
|
+
{};
|
|
8
|
+
|
|
9
|
+
const poolsByTokenAddress: { [address: string]: IdleToken[] } = {};
|
|
10
|
+
|
|
11
|
+
const TokensByAddress: { [network: number]: { [address: string]: IdleToken } } =
|
|
12
|
+
{};
|
|
13
|
+
|
|
14
|
+
const TokensByCDO: { [address: string]: IdleToken[] } = {};
|
|
15
|
+
|
|
16
|
+
export const getPoolsByTokenAddress = (address: string): IdleToken[] => {
|
|
17
|
+
return poolsByTokenAddress[address] || [];
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// return null if the pair does not exists otherwise return the idleToken
|
|
21
|
+
export function getIdleTokenIfIdleDaoPair(
|
|
22
|
+
network: number,
|
|
23
|
+
src: Token,
|
|
24
|
+
dst: Token,
|
|
25
|
+
): IdleToken | null {
|
|
26
|
+
const srcAddr = src.address.toLowerCase();
|
|
27
|
+
const dstAddr = dst.address.toLowerCase();
|
|
28
|
+
|
|
29
|
+
if (srcAddr === dstAddr) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const _src = TokensByAddress[network][srcAddr];
|
|
34
|
+
const _dst = TokensByAddress[network][dstAddr];
|
|
35
|
+
|
|
36
|
+
if (_src && _src.address.toLowerCase() == dstAddr) {
|
|
37
|
+
return _src;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (_dst && _dst.address.toLowerCase() == srcAddr) {
|
|
41
|
+
return _dst;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function getTokenFromIdleToken(idleToken: IdleToken): Token {
|
|
48
|
+
return {
|
|
49
|
+
address: idleToken.idleAddress,
|
|
50
|
+
decimals: idleToken.decimals,
|
|
51
|
+
symbol: idleToken.idleSymbol,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function getTokenFromIdleSymbol(
|
|
56
|
+
network: number,
|
|
57
|
+
symbol: string,
|
|
58
|
+
): Token | null {
|
|
59
|
+
const idleToken = Tokens[network][symbol];
|
|
60
|
+
|
|
61
|
+
// console.log('idleToken', network, symbol, idleToken)
|
|
62
|
+
|
|
63
|
+
if (!idleToken) return null;
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
address: idleToken.idleAddress,
|
|
67
|
+
decimals: idleToken.decimals,
|
|
68
|
+
symbol: idleToken.idleSymbol,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function getIdleTokenByAddress(
|
|
73
|
+
network: Network,
|
|
74
|
+
address: string,
|
|
75
|
+
): IdleToken | undefined {
|
|
76
|
+
return TokensByAddress[network]?.[address];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function getTokensByNetwork(network: Network): IdleToken[] | undefined {
|
|
80
|
+
return Object.values(Tokens[network]);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function getTokensByCdoAddress(
|
|
84
|
+
cdoAddress: string,
|
|
85
|
+
): IdleToken[] | undefined {
|
|
86
|
+
return TokensByCDO[cdoAddress];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function setTokensOnNetwork(network: Network, tokens: IdleToken[]): any {
|
|
90
|
+
for (let token of tokens) {
|
|
91
|
+
token.address = token.address.toLowerCase();
|
|
92
|
+
token.idleAddress = token.idleAddress.toLowerCase();
|
|
93
|
+
|
|
94
|
+
if (Tokens[network] === undefined) {
|
|
95
|
+
Tokens[network] = {};
|
|
96
|
+
}
|
|
97
|
+
if (TokensByAddress[network] === undefined) {
|
|
98
|
+
TokensByAddress[network] = {};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (poolsByTokenAddress[token.address] === undefined) {
|
|
102
|
+
poolsByTokenAddress[token.address] = [];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (TokensByCDO[token.cdoAddress] === undefined) {
|
|
106
|
+
TokensByCDO[token.cdoAddress] = [];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
Tokens[network][token.idleSymbol] = token;
|
|
110
|
+
TokensByCDO[token.cdoAddress].push(token);
|
|
111
|
+
TokensByAddress[network][token.idleAddress] = token;
|
|
112
|
+
TokensByAddress[network][token.address] = token;
|
|
113
|
+
poolsByTokenAddress[token.address].push(token);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return TokensByAddress[network];
|
|
117
|
+
}
|