@paraswap/dex-lib 4.0.17 → 4.0.18
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/aavev3statav2/Factory.json +131 -0
- package/build/abi/aavev3statav2/Pool.json +1459 -0
- package/build/abi/aavev3statav2/Token.json +997 -0
- package/build/dex/aave-gsm/config.js +2 -2
- package/build/dex/aave-gsm/config.js.map +1 -1
- package/build/dex/aave-v2/tokenlist.json +904 -0
- package/build/dex/aave-v2/tokens.js +6 -4
- package/build/dex/aave-v2/tokens.js.map +1 -1
- package/build/dex/aave-v3/config.js +6 -6
- package/build/dex/aave-v3/config.js.map +1 -1
- package/build/dex/aave-v3-stata-v2/aave-v3-stata-v2.d.ts +49 -0
- package/build/dex/aave-v3-stata-v2/aave-v3-stata-v2.js +377 -0
- package/build/dex/aave-v3-stata-v2/aave-v3-stata-v2.js.map +1 -0
- package/build/dex/aave-v3-stata-v2/config.d.ts +3 -0
- package/build/dex/aave-v3-stata-v2/config.js +21 -0
- package/build/dex/aave-v3-stata-v2/config.js.map +1 -0
- package/build/dex/aave-v3-stata-v2/tokens.d.ts +10 -0
- package/build/dex/aave-v3-stata-v2/tokens.js +43 -0
- package/build/dex/aave-v3-stata-v2/tokens.js.map +1 -0
- package/build/dex/aave-v3-stata-v2/types.d.ts +35 -0
- package/build/dex/aave-v3-stata-v2/types.js +25 -0
- package/build/dex/aave-v3-stata-v2/types.js.map +1 -0
- package/build/dex/aave-v3-stata-v2/utils.d.ts +4 -0
- package/build/dex/aave-v3-stata-v2/utils.js +67 -0
- package/build/dex/aave-v3-stata-v2/utils.js.map +1 -0
- package/build/dex/index.js +2 -0
- package/build/dex/index.js.map +1 -1
- package/build/dex/stkgho/config.js +2 -2
- package/build/dex/stkgho/config.js.map +1 -1
- package/package.json +2 -2
- package/src/abi/aavev3statav2/Factory.json +131 -0
- package/src/abi/aavev3statav2/Pool.json +1459 -0
- package/src/abi/aavev3statav2/Token.json +997 -0
- package/src/dex/aave-gsm/config.ts +3 -3
- package/src/dex/aave-v2/tokenlist.json +904 -0
- package/src/dex/aave-v2/tokens.ts +3 -4
- package/src/dex/aave-v3/config.ts +6 -7
- package/src/dex/aave-v3-stata-v2/aave-v3-stata-v2-e2e.test.ts +397 -0
- package/src/dex/aave-v3-stata-v2/aave-v3-stata-v2-integration.test.ts +600 -0
- package/src/dex/aave-v3-stata-v2/aave-v3-stata-v2.ts +477 -0
- package/src/dex/aave-v3-stata-v2/config.ts +24 -0
- package/src/dex/aave-v3-stata-v2/tokens.ts +48 -0
- package/src/dex/aave-v3-stata-v2/types.ts +41 -0
- package/src/dex/aave-v3-stata-v2/utils.ts +88 -0
- package/src/dex/index.ts +2 -0
- package/src/dex/stkgho/config.ts +3 -3
- package/tests/constants-e2e.ts +11 -1
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Token,
|
|
3
|
+
Address,
|
|
4
|
+
ExchangePrices,
|
|
5
|
+
PoolPrices,
|
|
6
|
+
AdapterExchangeParam,
|
|
7
|
+
PoolLiquidity,
|
|
8
|
+
Logger,
|
|
9
|
+
NumberAsString,
|
|
10
|
+
DexExchangeParam,
|
|
11
|
+
} from '../../types';
|
|
12
|
+
import { SwapSide, Network } from '../../constants';
|
|
13
|
+
import * as CALLDATA_GAS_COST from '../../calldata-gas-cost';
|
|
14
|
+
import { Utils, getBigIntPow, getDexKeysWithNetwork } from '../../utils';
|
|
15
|
+
import { Context, IDex } from '../../dex/idex';
|
|
16
|
+
import { IDexHelper } from '../../dex-helper/idex-helper';
|
|
17
|
+
import {
|
|
18
|
+
AaveV3StataV2Data,
|
|
19
|
+
Rounding,
|
|
20
|
+
StataFunctions,
|
|
21
|
+
StataToken,
|
|
22
|
+
TokenType,
|
|
23
|
+
} from './types';
|
|
24
|
+
import { SimpleExchange } from '../simple-exchange';
|
|
25
|
+
import { AaveV3StataConfig } from './config';
|
|
26
|
+
import { Interface } from '@ethersproject/abi';
|
|
27
|
+
import { fetchTokenList } from './utils';
|
|
28
|
+
import {
|
|
29
|
+
Tokens,
|
|
30
|
+
getTokenFromAddress,
|
|
31
|
+
getTokenType,
|
|
32
|
+
setTokensOnNetwork,
|
|
33
|
+
} from './tokens';
|
|
34
|
+
import { uint256ToBigInt } from '../../lib/decoders';
|
|
35
|
+
import TokenABI from '../../abi/aavev3statav2/Token.json';
|
|
36
|
+
import PoolABI from '../../abi/aavev3statav2/Pool.json';
|
|
37
|
+
import { extractReturnAmountPosition } from '../../executor/utils';
|
|
38
|
+
import { RETURN_AMOUNT_POS_32 } from '../../executor/constants';
|
|
39
|
+
// import { IStaticATokenLM_ABI } from '@bgd-labs/aave-address-book';
|
|
40
|
+
// slimmed down version of @bgd-labs/aave-address-book
|
|
41
|
+
// required as version of web3-utils used is buggy
|
|
42
|
+
//import IStaticATokenFactory_ABI from '../../abi/aave-v3-stata/StaticATokenFactory.json';
|
|
43
|
+
|
|
44
|
+
export const TOKEN_LIST_CACHE_KEY = 'stata-v2-token-list';
|
|
45
|
+
const TOKEN_LIST_TTL_SECONDS = 24 * 60 * 60; // 1 day
|
|
46
|
+
const RAY = BigInt(`1${'0'.repeat(27)}`);
|
|
47
|
+
|
|
48
|
+
export class AaveV3StataV2
|
|
49
|
+
extends SimpleExchange
|
|
50
|
+
implements IDex<AaveV3StataV2Data>
|
|
51
|
+
{
|
|
52
|
+
readonly hasConstantPriceLargeAmounts = false;
|
|
53
|
+
|
|
54
|
+
readonly isFeeOnTransferSupported = false;
|
|
55
|
+
|
|
56
|
+
public static dexKeysWithNetwork: { key: string; networks: Network[] }[] =
|
|
57
|
+
getDexKeysWithNetwork(AaveV3StataConfig);
|
|
58
|
+
|
|
59
|
+
logger: Logger;
|
|
60
|
+
|
|
61
|
+
// static readonly stata = new Interface(IStaticATokenLM_ABI);
|
|
62
|
+
static readonly stata = new Interface(TokenABI);
|
|
63
|
+
static readonly pool = new Interface(PoolABI);
|
|
64
|
+
|
|
65
|
+
private state: Record<string, { blockNumber: number; rate: bigint }> = {};
|
|
66
|
+
|
|
67
|
+
constructor(
|
|
68
|
+
readonly network: Network,
|
|
69
|
+
readonly dexKey: string,
|
|
70
|
+
readonly dexHelper: IDexHelper,
|
|
71
|
+
protected config = AaveV3StataConfig[dexKey][network],
|
|
72
|
+
) {
|
|
73
|
+
super(dexHelper, dexKey);
|
|
74
|
+
this.logger = dexHelper.getLogger(dexKey);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async initializePricing(blockNumber: number): Promise<void> {
|
|
78
|
+
await this.initializeTokens(blockNumber);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async initializeTokens(blockNumber?: number) {
|
|
82
|
+
let cachedTokenList = await this.dexHelper.cache.getAndCacheLocally(
|
|
83
|
+
this.dexKey,
|
|
84
|
+
this.network,
|
|
85
|
+
TOKEN_LIST_CACHE_KEY,
|
|
86
|
+
TOKEN_LIST_TTL_SECONDS,
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
if (cachedTokenList !== null) {
|
|
90
|
+
if (Object.keys(Tokens[this.network] ?? {}).length !== 0) return;
|
|
91
|
+
|
|
92
|
+
const tokenListParsed = JSON.parse(cachedTokenList);
|
|
93
|
+
setTokensOnNetwork(this.network, tokenListParsed);
|
|
94
|
+
|
|
95
|
+
tokenListParsed.forEach((token: StataToken) => {
|
|
96
|
+
this.state[token.address] = {
|
|
97
|
+
blockNumber: 0,
|
|
98
|
+
rate: 0n,
|
|
99
|
+
};
|
|
100
|
+
});
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
let tokenList = await fetchTokenList(
|
|
105
|
+
this.dexHelper.web3Provider,
|
|
106
|
+
this.config.factoryAddresses,
|
|
107
|
+
this.dexHelper.multiWrapper,
|
|
108
|
+
blockNumber,
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
await this.dexHelper.cache.setexAndCacheLocally(
|
|
112
|
+
this.dexKey,
|
|
113
|
+
this.network,
|
|
114
|
+
TOKEN_LIST_CACHE_KEY,
|
|
115
|
+
TOKEN_LIST_TTL_SECONDS,
|
|
116
|
+
JSON.stringify(tokenList),
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
setTokensOnNetwork(this.network, tokenList);
|
|
120
|
+
|
|
121
|
+
// init state for all tokens as empty
|
|
122
|
+
tokenList.forEach(token => {
|
|
123
|
+
this.state[token.address] = {
|
|
124
|
+
blockNumber: 0,
|
|
125
|
+
rate: 0n,
|
|
126
|
+
};
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Returns the list of contract adapters (name and index)
|
|
131
|
+
// for a buy/sell. Return null if there are no adapters.
|
|
132
|
+
getAdapters(side: SwapSide): { name: string; index: number }[] | null {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private _getPoolIdentifier(srcToken: Token, destToken: Token): string {
|
|
137
|
+
return (
|
|
138
|
+
this.dexKey +
|
|
139
|
+
'-' +
|
|
140
|
+
[srcToken.address.toLowerCase(), destToken.address.toLowerCase()]
|
|
141
|
+
.sort((a, b) => (a > b ? 1 : -1))
|
|
142
|
+
.join('_')
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Returns list of pool identifiers that can be used
|
|
147
|
+
// for a given swap. poolIdentifiers must be unique
|
|
148
|
+
// across DEXes. It is recommended to use
|
|
149
|
+
// ${dexKey}_${poolAddress} as a poolIdentifier
|
|
150
|
+
async getPoolIdentifiers(
|
|
151
|
+
srcToken: Token,
|
|
152
|
+
destToken: Token,
|
|
153
|
+
side: SwapSide,
|
|
154
|
+
blockNumber: number,
|
|
155
|
+
): Promise<string[]> {
|
|
156
|
+
return [this._getPoolIdentifier(srcToken, destToken)];
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Returns pool prices for amounts.
|
|
160
|
+
// If limitPools is defined only pools in limitPools
|
|
161
|
+
// should be used. If limitPools is undefined then
|
|
162
|
+
// any pools can be used.
|
|
163
|
+
async getPricesVolume(
|
|
164
|
+
srcToken: Token,
|
|
165
|
+
destToken: Token,
|
|
166
|
+
amounts: bigint[],
|
|
167
|
+
side: SwapSide,
|
|
168
|
+
blockNumber: number,
|
|
169
|
+
limitPools?: string[],
|
|
170
|
+
): Promise<null | ExchangePrices<AaveV3StataV2Data>> {
|
|
171
|
+
const src = getTokenType(this.network, srcToken.address);
|
|
172
|
+
const dest = getTokenType(this.network, destToken.address);
|
|
173
|
+
|
|
174
|
+
// one of the tokens must be stata
|
|
175
|
+
if (![src, dest].includes(TokenType.STATA_TOKEN) || src === dest) {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const isSrcStata = src === TokenType.STATA_TOKEN;
|
|
180
|
+
|
|
181
|
+
const [stataToken, otherAddressLower] = isSrcStata
|
|
182
|
+
? [
|
|
183
|
+
getTokenFromAddress(this.network, srcToken.address),
|
|
184
|
+
destToken.address.toLowerCase(),
|
|
185
|
+
]
|
|
186
|
+
: [
|
|
187
|
+
getTokenFromAddress(this.network, destToken.address),
|
|
188
|
+
srcToken.address.toLowerCase(),
|
|
189
|
+
];
|
|
190
|
+
|
|
191
|
+
// the token itself can only swap from/to underlying and aToken, so
|
|
192
|
+
// - at least one must be stata
|
|
193
|
+
// - maximum one can be stata
|
|
194
|
+
// - second one must be underlying or aUnderlying
|
|
195
|
+
// on the buy side (mint, withdraw) we only support the underlying<->stata conversion, not the aUnderlying
|
|
196
|
+
if (side === SwapSide.SELL) {
|
|
197
|
+
if (
|
|
198
|
+
otherAddressLower !== stataToken.underlying.toLowerCase() &&
|
|
199
|
+
otherAddressLower !== stataToken.underlyingAToken.toLowerCase()
|
|
200
|
+
) {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
} else {
|
|
204
|
+
if (otherAddressLower !== stataToken.underlying.toLowerCase()) {
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const stataAddressLower = stataToken.address.toLowerCase();
|
|
210
|
+
|
|
211
|
+
if (
|
|
212
|
+
!this.state[stataAddressLower]?.blockNumber ||
|
|
213
|
+
blockNumber > this.state[stataAddressLower].blockNumber
|
|
214
|
+
) {
|
|
215
|
+
const results = await this.dexHelper.multiWrapper.tryAggregate<bigint>(
|
|
216
|
+
true,
|
|
217
|
+
[
|
|
218
|
+
{
|
|
219
|
+
target: this.config.pool,
|
|
220
|
+
callData: AaveV3StataV2.pool.encodeFunctionData(
|
|
221
|
+
'getReserveNormalizedIncome',
|
|
222
|
+
[stataToken.underlying],
|
|
223
|
+
),
|
|
224
|
+
decodeFunction: uint256ToBigInt,
|
|
225
|
+
},
|
|
226
|
+
],
|
|
227
|
+
blockNumber,
|
|
228
|
+
);
|
|
229
|
+
this.state[stataAddressLower] = {
|
|
230
|
+
blockNumber,
|
|
231
|
+
rate: results[0].returnData,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return [
|
|
236
|
+
{
|
|
237
|
+
prices: amounts.map(amount => {
|
|
238
|
+
const rate = this.state[stataAddressLower].rate;
|
|
239
|
+
if (side === SwapSide.SELL) {
|
|
240
|
+
if (isSrcStata) {
|
|
241
|
+
return this.previewRedeem(amount, rate);
|
|
242
|
+
} else {
|
|
243
|
+
return this.previewDeposit(amount, rate);
|
|
244
|
+
}
|
|
245
|
+
} else {
|
|
246
|
+
if (isSrcStata) {
|
|
247
|
+
return this.previewWithdraw(amount, rate);
|
|
248
|
+
} else {
|
|
249
|
+
return this.previewMint(amount, rate);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}),
|
|
253
|
+
unit: getBigIntPow(
|
|
254
|
+
(side === SwapSide.SELL ? destToken : srcToken).decimals,
|
|
255
|
+
),
|
|
256
|
+
gasCost: 270000, // 250_000 from underlying, far less from aToken
|
|
257
|
+
exchange: this.dexKey,
|
|
258
|
+
data: {
|
|
259
|
+
srcType: src,
|
|
260
|
+
destType: dest,
|
|
261
|
+
exchange: stataToken.address,
|
|
262
|
+
},
|
|
263
|
+
poolAddresses: [stataAddressLower],
|
|
264
|
+
},
|
|
265
|
+
];
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
getCalldataGasCost(
|
|
269
|
+
poolPrices: PoolPrices<AaveV3StataV2Data>,
|
|
270
|
+
): number | number[] {
|
|
271
|
+
return CALLDATA_GAS_COST.DEX_NO_PAYLOAD;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Not used for V6
|
|
275
|
+
getAdapterParam(): AdapterExchangeParam {
|
|
276
|
+
return {
|
|
277
|
+
targetExchange: '0x',
|
|
278
|
+
payload: '0x',
|
|
279
|
+
networkFee: '0',
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
getDexParam(
|
|
284
|
+
srcToken: Address,
|
|
285
|
+
destToken: Address,
|
|
286
|
+
srcAmount: NumberAsString,
|
|
287
|
+
destAmount: NumberAsString,
|
|
288
|
+
recipient: Address,
|
|
289
|
+
data: AaveV3StataV2Data,
|
|
290
|
+
side: SwapSide,
|
|
291
|
+
_: Context,
|
|
292
|
+
executorAddress: Address,
|
|
293
|
+
): DexExchangeParam {
|
|
294
|
+
const { exchange, srcType, destType } = data;
|
|
295
|
+
let swapData;
|
|
296
|
+
let returnAmountPos = undefined;
|
|
297
|
+
|
|
298
|
+
if (side === SwapSide.SELL) {
|
|
299
|
+
if (srcType === TokenType.STATA_TOKEN) {
|
|
300
|
+
// e.g. sell srcAmount 100 srcToken stataUSDC for destToken USDC
|
|
301
|
+
if (destType === TokenType.UNDERLYING) {
|
|
302
|
+
swapData = AaveV3StataV2.stata.encodeFunctionData(
|
|
303
|
+
StataFunctions.redeem,
|
|
304
|
+
[
|
|
305
|
+
srcAmount,
|
|
306
|
+
recipient, // receiver
|
|
307
|
+
executorAddress, // owner
|
|
308
|
+
],
|
|
309
|
+
);
|
|
310
|
+
} else {
|
|
311
|
+
swapData = AaveV3StataV2.stata.encodeFunctionData(
|
|
312
|
+
StataFunctions.redeemATokens,
|
|
313
|
+
[
|
|
314
|
+
srcAmount,
|
|
315
|
+
recipient, // receiver
|
|
316
|
+
executorAddress, // owner
|
|
317
|
+
],
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
returnAmountPos = RETURN_AMOUNT_POS_32;
|
|
322
|
+
} else {
|
|
323
|
+
if (srcType === TokenType.UNDERLYING) {
|
|
324
|
+
// sell srcAmount 100 srcToken USDC for destToken stataUSDC
|
|
325
|
+
swapData = AaveV3StataV2.stata.encodeFunctionData(
|
|
326
|
+
StataFunctions.deposit,
|
|
327
|
+
[
|
|
328
|
+
srcAmount,
|
|
329
|
+
recipient, // receiver
|
|
330
|
+
],
|
|
331
|
+
);
|
|
332
|
+
returnAmountPos = extractReturnAmountPosition(
|
|
333
|
+
AaveV3StataV2.stata,
|
|
334
|
+
StataFunctions.deposit,
|
|
335
|
+
);
|
|
336
|
+
} else {
|
|
337
|
+
// sell srcAmount 100 srcToken USDC for destToken stataUSDC
|
|
338
|
+
swapData = AaveV3StataV2.stata.encodeFunctionData(
|
|
339
|
+
StataFunctions.depositATokens,
|
|
340
|
+
[
|
|
341
|
+
srcAmount,
|
|
342
|
+
recipient, // receiver
|
|
343
|
+
],
|
|
344
|
+
);
|
|
345
|
+
returnAmountPos = extractReturnAmountPosition(
|
|
346
|
+
AaveV3StataV2.stata,
|
|
347
|
+
StataFunctions.depositATokens,
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
} else {
|
|
352
|
+
if (srcType === TokenType.STATA_TOKEN) {
|
|
353
|
+
// e.g. buy destAmount 100 destToken USDC for srcToken stataUSDC
|
|
354
|
+
swapData = AaveV3StataV2.stata.encodeFunctionData(
|
|
355
|
+
StataFunctions.withdraw,
|
|
356
|
+
[
|
|
357
|
+
destAmount,
|
|
358
|
+
recipient, // receiver
|
|
359
|
+
executorAddress, // owner
|
|
360
|
+
],
|
|
361
|
+
);
|
|
362
|
+
} else {
|
|
363
|
+
// e.g. buy destAmount 100 destToken stataUSDC for srcToken USDC
|
|
364
|
+
swapData = AaveV3StataV2.stata.encodeFunctionData(StataFunctions.mint, [
|
|
365
|
+
destAmount,
|
|
366
|
+
recipient,
|
|
367
|
+
]);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return {
|
|
372
|
+
needWrapNative: this.needWrapNative,
|
|
373
|
+
dexFuncHasRecipient: true,
|
|
374
|
+
exchangeData: swapData,
|
|
375
|
+
targetExchange: exchange,
|
|
376
|
+
returnAmountPos,
|
|
377
|
+
skipApproval: srcType === TokenType.STATA_TOKEN,
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
async updatePoolState(): Promise<void> {}
|
|
382
|
+
|
|
383
|
+
async getTopPoolsForToken(
|
|
384
|
+
tokenAddress: Address,
|
|
385
|
+
limit: number,
|
|
386
|
+
): Promise<PoolLiquidity[]> {
|
|
387
|
+
await this.initializeTokens();
|
|
388
|
+
|
|
389
|
+
const tokenType = getTokenType(this.network, tokenAddress);
|
|
390
|
+
|
|
391
|
+
if (tokenType === TokenType.UNKNOWN) {
|
|
392
|
+
return [];
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const stata = getTokenFromAddress(this.network, tokenAddress);
|
|
396
|
+
|
|
397
|
+
if (tokenType === TokenType.STATA_TOKEN) {
|
|
398
|
+
return [
|
|
399
|
+
{
|
|
400
|
+
liquidityUSD: 1e11,
|
|
401
|
+
exchange: this.dexKey,
|
|
402
|
+
address: stata.address,
|
|
403
|
+
connectorTokens: [
|
|
404
|
+
{ address: stata.underlying, decimals: stata.decimals },
|
|
405
|
+
{ address: stata.underlyingAToken, decimals: stata.decimals },
|
|
406
|
+
],
|
|
407
|
+
},
|
|
408
|
+
];
|
|
409
|
+
} else {
|
|
410
|
+
return [
|
|
411
|
+
{
|
|
412
|
+
liquidityUSD: 1e11,
|
|
413
|
+
exchange: this.dexKey,
|
|
414
|
+
address: stata.address,
|
|
415
|
+
connectorTokens: [
|
|
416
|
+
{ address: stata.address, decimals: stata.decimals },
|
|
417
|
+
{
|
|
418
|
+
address:
|
|
419
|
+
tokenType === TokenType.UNDERLYING
|
|
420
|
+
? stata.underlyingAToken
|
|
421
|
+
: stata.underlying,
|
|
422
|
+
decimals: stata.decimals,
|
|
423
|
+
},
|
|
424
|
+
],
|
|
425
|
+
},
|
|
426
|
+
];
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// TODO: Move to pool implementation when migrating to event based
|
|
431
|
+
previewRedeem(shares: bigint, rate: bigint) {
|
|
432
|
+
return this._convertToAssets(shares, rate, Rounding.DOWN);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
previewMint(shares: bigint, rate: bigint) {
|
|
436
|
+
return this._convertToAssets(shares, rate, Rounding.UP);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
previewWithdraw(assets: bigint, rate: bigint) {
|
|
440
|
+
return this._convertToShares(assets, rate, Rounding.UP);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
previewDeposit(assets: bigint, rate: bigint) {
|
|
444
|
+
return this._convertToShares(assets, rate, Rounding.DOWN);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
_convertToAssets(shares: bigint, rate: bigint, rounding: Rounding): bigint {
|
|
448
|
+
if (rounding == Rounding.UP) return this.rayMulRoundUp(shares, rate);
|
|
449
|
+
return this.rayMulRoundDown(shares, rate);
|
|
450
|
+
}
|
|
451
|
+
_convertToShares(assets: bigint, rate: bigint, rounding: Rounding): bigint {
|
|
452
|
+
if (rounding == Rounding.UP) return this.rayDivRoundUp(assets, rate);
|
|
453
|
+
return this.rayDivRoundDown(assets, rate);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
rayMulRoundDown(a: bigint, b: bigint): bigint {
|
|
457
|
+
if (a === 0n || b === 0n) {
|
|
458
|
+
return 0n;
|
|
459
|
+
}
|
|
460
|
+
return (a * b) / RAY;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
rayMulRoundUp(a: bigint, b: bigint) {
|
|
464
|
+
if (a === 0n || b === 0n) {
|
|
465
|
+
return 0n;
|
|
466
|
+
}
|
|
467
|
+
return (a * b + RAY - 1n) / RAY;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
rayDivRoundDown(a: bigint, b: bigint) {
|
|
471
|
+
return (a * RAY) / b;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
rayDivRoundUp(a: bigint, b: bigint) {
|
|
475
|
+
return (a * RAY + b - 1n) / b;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { DexParams } from './types';
|
|
2
|
+
import { DexConfigMap } from '../../types';
|
|
3
|
+
import { Network } from '../../constants';
|
|
4
|
+
import {
|
|
5
|
+
AaveV3Ethereum,
|
|
6
|
+
AaveV3EthereumLido,
|
|
7
|
+
AaveV3Gnosis,
|
|
8
|
+
} from '@bgd-labs/aave-address-book';
|
|
9
|
+
|
|
10
|
+
export const AaveV3StataConfig: DexConfigMap<DexParams> = {
|
|
11
|
+
AaveV3StataV2: {
|
|
12
|
+
[Network.MAINNET]: {
|
|
13
|
+
factoryAddresses: [
|
|
14
|
+
AaveV3Ethereum.STATA_FACTORY,
|
|
15
|
+
AaveV3EthereumLido.STATA_FACTORY,
|
|
16
|
+
],
|
|
17
|
+
pool: AaveV3Ethereum.POOL,
|
|
18
|
+
},
|
|
19
|
+
[Network.GNOSIS]: {
|
|
20
|
+
factoryAddresses: [AaveV3Gnosis.STATA_FACTORY],
|
|
21
|
+
pool: AaveV3Gnosis.POOL,
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Network } from '../../constants';
|
|
2
|
+
import { StataToken, TokenType } from './types';
|
|
3
|
+
|
|
4
|
+
export const Tokens: { [network: number]: { [symbol: string]: StataToken } } =
|
|
5
|
+
{};
|
|
6
|
+
|
|
7
|
+
const TokensByAddress: {
|
|
8
|
+
[network: number]: { [address: string]: StataToken };
|
|
9
|
+
} = {};
|
|
10
|
+
|
|
11
|
+
export function setTokensOnNetwork(network: Network, tokens: StataToken[]) {
|
|
12
|
+
if (Tokens[network] === undefined) {
|
|
13
|
+
Tokens[network] = {};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (TokensByAddress[network] === undefined) {
|
|
17
|
+
TokensByAddress[network] = {};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
for (let token of tokens) {
|
|
21
|
+
token.address = token.address.toLowerCase();
|
|
22
|
+
token.underlying = token.underlying.toLowerCase();
|
|
23
|
+
token.underlyingAToken = token.underlyingAToken.toLowerCase();
|
|
24
|
+
|
|
25
|
+
Tokens[network][token.stataSymbol] = token;
|
|
26
|
+
TokensByAddress[network][token.address] = token;
|
|
27
|
+
TokensByAddress[network][token.underlying] = token;
|
|
28
|
+
TokensByAddress[network][token.underlyingAToken] = token;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function getTokenType(network: Network, address: string): TokenType {
|
|
33
|
+
const addressLower = address.toLowerCase();
|
|
34
|
+
const token = TokensByAddress[network]?.[addressLower];
|
|
35
|
+
|
|
36
|
+
if (!token) return TokenType.UNKNOWN;
|
|
37
|
+
if (token.address === addressLower) return TokenType.STATA_TOKEN;
|
|
38
|
+
if (token.underlying === addressLower) return TokenType.UNDERLYING;
|
|
39
|
+
if (token.underlyingAToken === addressLower) return TokenType.A_TOKEN;
|
|
40
|
+
return TokenType.UNKNOWN;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function getTokenFromAddress(
|
|
44
|
+
network: Network,
|
|
45
|
+
address: string,
|
|
46
|
+
): StataToken {
|
|
47
|
+
return TokensByAddress[network]?.[address.toLowerCase()];
|
|
48
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Address } from '../../types';
|
|
2
|
+
|
|
3
|
+
export enum TokenType {
|
|
4
|
+
UNDERLYING,
|
|
5
|
+
A_TOKEN,
|
|
6
|
+
STATA_TOKEN,
|
|
7
|
+
UNKNOWN,
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type DexParams = {
|
|
11
|
+
factoryAddresses: string[];
|
|
12
|
+
pool: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type AaveV3StataV2Data = {
|
|
16
|
+
exchange: Address;
|
|
17
|
+
srcType: TokenType;
|
|
18
|
+
destType: TokenType;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type StataToken = {
|
|
22
|
+
address: string;
|
|
23
|
+
underlying: string;
|
|
24
|
+
underlyingAToken: string;
|
|
25
|
+
stataSymbol: string;
|
|
26
|
+
decimals: number;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export enum StataFunctions {
|
|
30
|
+
deposit = 'deposit',
|
|
31
|
+
depositATokens = 'depositATokens',
|
|
32
|
+
redeem = 'redeem',
|
|
33
|
+
redeemATokens = 'redeemATokens',
|
|
34
|
+
mint = 'mint',
|
|
35
|
+
withdraw = 'withdraw',
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export enum Rounding {
|
|
39
|
+
UP = 'UP',
|
|
40
|
+
DOWN = 'DOWN',
|
|
41
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// import {
|
|
2
|
+
// IStaticATokenFactory_ABI,
|
|
3
|
+
// IStaticATokenLM_ABI,
|
|
4
|
+
// } from '@bgd-labs/aave-address-book';
|
|
5
|
+
import { Interface } from '@ethersproject/abi';
|
|
6
|
+
import Web3 from 'web3';
|
|
7
|
+
import { MultiCallParams, MultiWrapper } from '../../lib/multi-wrapper';
|
|
8
|
+
import { stringDecode, uint8ToNumber, addressDecode } from '../../lib/decoders';
|
|
9
|
+
import { StataToken } from './types';
|
|
10
|
+
import FactoryABI from '../../abi/aavev3statav2/Factory.json';
|
|
11
|
+
import TokenABI from '../../abi/aavev3statav2/Token.json';
|
|
12
|
+
import { AbiItem } from 'web3-utils';
|
|
13
|
+
|
|
14
|
+
// const stataInterface = new Interface(IStaticATokenLM_ABI);
|
|
15
|
+
const statav2Interface = new Interface(TokenABI);
|
|
16
|
+
|
|
17
|
+
async function getTokenMetaData(
|
|
18
|
+
stataTokens: string[],
|
|
19
|
+
multiWrapper: MultiWrapper,
|
|
20
|
+
blockNumber?: number,
|
|
21
|
+
): Promise<StataToken[]> {
|
|
22
|
+
const calls: MultiCallParams<any>[] = stataTokens
|
|
23
|
+
.map(token => {
|
|
24
|
+
return [
|
|
25
|
+
{
|
|
26
|
+
target: token,
|
|
27
|
+
callData: statav2Interface.encodeFunctionData('symbol'),
|
|
28
|
+
decodeFunction: stringDecode,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
target: token,
|
|
32
|
+
callData: statav2Interface.encodeFunctionData('decimals'),
|
|
33
|
+
decodeFunction: uint8ToNumber,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
target: token,
|
|
37
|
+
callData: statav2Interface.encodeFunctionData('asset'),
|
|
38
|
+
decodeFunction: addressDecode,
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
target: token,
|
|
42
|
+
callData: statav2Interface.encodeFunctionData('aToken'),
|
|
43
|
+
decodeFunction: addressDecode,
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
})
|
|
47
|
+
.flat();
|
|
48
|
+
|
|
49
|
+
const results = await multiWrapper.aggregate<string | number>(
|
|
50
|
+
calls,
|
|
51
|
+
blockNumber,
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
let tokenList: StataToken[] = [];
|
|
55
|
+
for (let i = 0, x = 0; i < stataTokens.length; ++i, x += 4) {
|
|
56
|
+
tokenList.push({
|
|
57
|
+
address: stataTokens[i].toLowerCase(),
|
|
58
|
+
stataSymbol: results[x] as string,
|
|
59
|
+
decimals: results[x + 1] as number,
|
|
60
|
+
underlying: (results[x + 2] as string).toLowerCase(),
|
|
61
|
+
underlyingAToken: (results[x + 3] as string).toLowerCase(),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return tokenList;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const fetchTokenList = async (
|
|
68
|
+
web3Provider: Web3,
|
|
69
|
+
factoryAddresses: string[],
|
|
70
|
+
multiWrapper: MultiWrapper,
|
|
71
|
+
blockNumber?: number,
|
|
72
|
+
): Promise<StataToken[]> => {
|
|
73
|
+
let stataList: string[] = (
|
|
74
|
+
(await Promise.all(
|
|
75
|
+
factoryAddresses.map(async factoryAddress => {
|
|
76
|
+
let factoryContract = new web3Provider.eth.Contract(
|
|
77
|
+
FactoryABI as AbiItem[],
|
|
78
|
+
// IStaticATokenFactory_ABI as any,
|
|
79
|
+
factoryAddress,
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
return await factoryContract.methods.getStataTokens().call();
|
|
83
|
+
}),
|
|
84
|
+
)) as string[][]
|
|
85
|
+
).flat();
|
|
86
|
+
|
|
87
|
+
return getTokenMetaData(stataList, multiWrapper, blockNumber);
|
|
88
|
+
};
|
package/src/dex/index.ts
CHANGED
|
@@ -85,6 +85,7 @@ import { EtherFi } from './etherfi';
|
|
|
85
85
|
import { Spark } from './spark/spark';
|
|
86
86
|
import { VelodromeSlipstream } from './uniswap-v3/forks/velodrome-slipstream/velodrome-slipstream';
|
|
87
87
|
import { AaveV3Stata } from './aave-v3-stata/aave-v3-stata';
|
|
88
|
+
import { AaveV3StataV2 } from './aave-v3-stata-v2/aave-v3-stata-v2';
|
|
88
89
|
import { OSwap } from './oswap/oswap';
|
|
89
90
|
import { FluidDex } from './fluid-dex/fluid-dex';
|
|
90
91
|
import { ConcentratorArusd } from './concentrator-arusd/concentrator-arusd';
|
|
@@ -182,6 +183,7 @@ const Dexes = [
|
|
|
182
183
|
PharaohV1,
|
|
183
184
|
Spark,
|
|
184
185
|
AaveV3Stata,
|
|
186
|
+
AaveV3StataV2,
|
|
185
187
|
OSwap,
|
|
186
188
|
ConcentratorArusd,
|
|
187
189
|
FxProtocolRusd,
|