@paraswap/dex-lib 2.26.2 → 2.26.3-curve-v1-wbeth-freth
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/dex/curve-v1/config.js +32 -0
- package/build/dex/curve-v1/config.js.map +1 -1
- package/build/dex/curve-v1/curve-v1.js +6 -0
- package/build/dex/curve-v1/curve-v1.js.map +1 -1
- package/build/dex/curve-v1/pools/curve-metapool.js +0 -1
- package/build/dex/curve-v1/pools/curve-metapool.js.map +1 -1
- package/build/dex/curve-v1/pools/curve-pool.d.ts +5 -0
- package/build/dex/curve-v1/pools/curve-pool.js +1 -0
- package/build/dex/curve-v1/pools/curve-pool.js.map +1 -1
- package/build/dex/curve-v1/pools/getstate-multicall.js +37 -1
- package/build/dex/curve-v1/pools/getstate-multicall.js.map +1 -1
- package/build/dex/curve-v1/pools/wbETHpool.js +2 -2
- package/build/dex/curve-v1/pools/wbETHpool.js.map +1 -1
- package/package.json +1 -1
- package/src/abi/curve-v1/StableSwapFRXETH.json +889 -0
- package/src/abi/curve-v1/StableSwapWBETH.json +1223 -0
- package/src/dex/curve-v1/config.ts +32 -0
- package/src/dex/curve-v1/curve-v1-e2e.test.ts +37 -4
- package/src/dex/curve-v1/curve-v1.ts +6 -0
- package/src/dex/curve-v1/pools/curve-metapool.ts +0 -1
- package/src/dex/curve-v1/pools/curve-pool.ts +6 -0
- package/src/dex/curve-v1/pools/frxETHpool.ts +38 -0
- package/src/dex/curve-v1/pools/getstate-multicall.ts +53 -2
- package/src/dex/curve-v1/pools/wbETHpool.ts +542 -0
- package/tests/constants-e2e.ts +11 -1
|
@@ -0,0 +1,542 @@
|
|
|
1
|
+
import { Address, Log } from '../../../types';
|
|
2
|
+
import { IDexHelper } from '../../../dex-helper';
|
|
3
|
+
import { bigNumberify, catchParseLogError, stringify } from '../../../utils';
|
|
4
|
+
import StableSwapWBETH from '../../../abi/curve-v1/StableSwapWBETH.json';
|
|
5
|
+
import BigNumber from 'bignumber.js';
|
|
6
|
+
import { BN_0, BN_1, BN_2 } from '../../../bignumber-constants';
|
|
7
|
+
import _ from 'lodash';
|
|
8
|
+
import { CurvePool, PoolState } from './curve-pool';
|
|
9
|
+
import { DeepReadonly } from 'ts-essentials';
|
|
10
|
+
|
|
11
|
+
const pool = 'wbETH';
|
|
12
|
+
export const address: Address =
|
|
13
|
+
'0xbfab6fa95e0091ed66058ad493189d2cb29385e6'.toLowerCase();
|
|
14
|
+
const tokenAddress: Address = '0xbfab6fa95e0091ed66058ad493189d2cb29385e6';
|
|
15
|
+
const N_COINS: number = 2;
|
|
16
|
+
const PRECISION_MUL = ['1', '1'].map(bigNumberify);
|
|
17
|
+
const A_PRECISION = bigNumberify(100);
|
|
18
|
+
const USE_LENDING = [false, false];
|
|
19
|
+
const ADMIN_FEE = bigNumberify(5000000000);
|
|
20
|
+
const COINS = [
|
|
21
|
+
'0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE',
|
|
22
|
+
'0xa2e3356610840701bdf5611a53974510ae27e2e1',
|
|
23
|
+
];
|
|
24
|
+
const trackCoins = true;
|
|
25
|
+
|
|
26
|
+
export class WBETHPool extends CurvePool {
|
|
27
|
+
public isAdminBalancesSupported: boolean = true;
|
|
28
|
+
|
|
29
|
+
lastTransferredCoin?: Address;
|
|
30
|
+
|
|
31
|
+
constructor(
|
|
32
|
+
parentName: string,
|
|
33
|
+
dexHelper: IDexHelper,
|
|
34
|
+
_pool = pool,
|
|
35
|
+
_address = address,
|
|
36
|
+
_tokenAddress = tokenAddress,
|
|
37
|
+
_trackCoins = trackCoins,
|
|
38
|
+
_abi: any = StableSwapWBETH,
|
|
39
|
+
_N_COINS = N_COINS,
|
|
40
|
+
_PRECISION_MUL = PRECISION_MUL,
|
|
41
|
+
_USE_LENDING = USE_LENDING,
|
|
42
|
+
_COINS = COINS,
|
|
43
|
+
prefix = 'WBETHPool',
|
|
44
|
+
) {
|
|
45
|
+
super(
|
|
46
|
+
parentName,
|
|
47
|
+
dexHelper,
|
|
48
|
+
dexHelper.getLogger(prefix),
|
|
49
|
+
_pool,
|
|
50
|
+
_address,
|
|
51
|
+
_tokenAddress,
|
|
52
|
+
_trackCoins,
|
|
53
|
+
_abi,
|
|
54
|
+
_N_COINS,
|
|
55
|
+
_PRECISION_MUL,
|
|
56
|
+
_USE_LENDING,
|
|
57
|
+
_COINS,
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
// Add pool specific handlers or overloaded handlers
|
|
61
|
+
this.handlers['RemoveLiquidityOne'] =
|
|
62
|
+
this.handleRemoveLiquidityOne.bind(this);
|
|
63
|
+
this.handlers['CommitNewFee'] = this.handleNewFee.bind(this);
|
|
64
|
+
this.handlers['Transfer'] = this.handleCoinTransfer.bind(this);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
public processLog(
|
|
68
|
+
state: DeepReadonly<PoolState>,
|
|
69
|
+
log: Readonly<Log>,
|
|
70
|
+
): DeepReadonly<PoolState> | null {
|
|
71
|
+
let _state: PoolState = {
|
|
72
|
+
A: bigNumberify(state.A),
|
|
73
|
+
fee: bigNumberify(state.fee),
|
|
74
|
+
admin_fee: bigNumberify(state.admin_fee),
|
|
75
|
+
supply: bigNumberify(state.supply),
|
|
76
|
+
balances: state.balances.map(bigNumberify),
|
|
77
|
+
eth_balance: bigNumberify(state.eth_balance),
|
|
78
|
+
token_balance: bigNumberify(state.token_balance),
|
|
79
|
+
stored_rates: state.stored_rates!.map(bigNumberify),
|
|
80
|
+
admin_balances: state.admin_balances!.map(bigNumberify),
|
|
81
|
+
};
|
|
82
|
+
try {
|
|
83
|
+
const event = this.decoder(log);
|
|
84
|
+
|
|
85
|
+
if (event.name in this.handlers) {
|
|
86
|
+
return this.handlers[event.name](event, _state, log);
|
|
87
|
+
}
|
|
88
|
+
return _state;
|
|
89
|
+
} catch (e) {
|
|
90
|
+
catchParseLogError(e, this.logger);
|
|
91
|
+
}
|
|
92
|
+
return _state;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
handleCoinTransfer(event: any, state: PoolState, log: Log): PoolState {
|
|
96
|
+
const from = event.args.src;
|
|
97
|
+
const coin = log.address;
|
|
98
|
+
|
|
99
|
+
if (from.toLowerCase() === this.address.toLowerCase())
|
|
100
|
+
this.lastTransferredCoin = coin.toLowerCase();
|
|
101
|
+
return state;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
handleRemoveLiquidityOne(event: any, state: PoolState, log: Log): PoolState {
|
|
105
|
+
const _token_amount = bigNumberify(stringify(event.args.token_amount));
|
|
106
|
+
const i = _.findIndex(
|
|
107
|
+
this.COINS,
|
|
108
|
+
c => c.toLowerCase() === this.lastTransferredCoin?.toLowerCase(),
|
|
109
|
+
);
|
|
110
|
+
if (i == -1) {
|
|
111
|
+
this.logger.error(
|
|
112
|
+
`Error: expected coin to have a transfer event before RemoveLiquidityOne event`,
|
|
113
|
+
);
|
|
114
|
+
return state;
|
|
115
|
+
}
|
|
116
|
+
this.remove_liquidity_one_coin(_token_amount, i, state);
|
|
117
|
+
return state;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
handleNewFee(event: any, state: PoolState, log: Log): PoolState {
|
|
121
|
+
const fee = bigNumberify(stringify(event.args.new_fee));
|
|
122
|
+
|
|
123
|
+
state.fee = fee;
|
|
124
|
+
return state;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
handleRemoveLiquidity(event: any, state: PoolState, log: Log): PoolState {
|
|
128
|
+
const token_supply = bigNumberify(stringify(event.args.token_supply));
|
|
129
|
+
const amounts = event.args.token_amounts
|
|
130
|
+
.map(stringify)
|
|
131
|
+
.map(bigNumberify);
|
|
132
|
+
|
|
133
|
+
for (let i = 0; i < this.N_COINS; i++) {
|
|
134
|
+
if(i === 0) {
|
|
135
|
+
state.eth_balance = state.eth_balance?.minus(amounts[i]);
|
|
136
|
+
} else {
|
|
137
|
+
state.token_balance = state.token_balance!.minus(amounts[i]);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
state.supply = token_supply;
|
|
142
|
+
return state;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
handleRemoveLiquidityImbalances(
|
|
146
|
+
event: any,
|
|
147
|
+
state: PoolState,
|
|
148
|
+
log: Log,
|
|
149
|
+
): PoolState {
|
|
150
|
+
const amounts = event.args.token_amounts.map(stringify).map(bigNumberify);
|
|
151
|
+
const { A, stored_rates: rates, balances, fee, supply: total_supply } = state;
|
|
152
|
+
const amp = A.times(A_PRECISION);
|
|
153
|
+
|
|
154
|
+
const old_balances = this._balances(state);
|
|
155
|
+
const D0 = this.get_D_mem(rates!, old_balances, amp);
|
|
156
|
+
let new_balances = _.clone(old_balances);
|
|
157
|
+
|
|
158
|
+
for (let i = 0; i < this.N_COINS; i++) {
|
|
159
|
+
new_balances[i] = new_balances[i].minus(amounts[i]);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const D1 = this.get_D_mem(rates!, new_balances, amp);
|
|
163
|
+
|
|
164
|
+
const fees: BigNumber[] = new Array<BigNumber>(this.N_COINS);
|
|
165
|
+
const base_fee = fee.times(this.N_COINS).idiv(4 * (this.N_COINS - 1));
|
|
166
|
+
|
|
167
|
+
for (let i = 0; i < this.N_COINS; i++) {
|
|
168
|
+
const ideal_balance: BigNumber = D1.times(old_balances[i]).idiv(D0);
|
|
169
|
+
let difference: BigNumber = BN_0;
|
|
170
|
+
const new_balance = new_balances[i];
|
|
171
|
+
if (ideal_balance.gt(new_balance)) {
|
|
172
|
+
difference = ideal_balance.minus(new_balances[i]);
|
|
173
|
+
} else {
|
|
174
|
+
difference = new_balances[i].minus(ideal_balance);
|
|
175
|
+
}
|
|
176
|
+
fees[i] = base_fee.times(difference).idiv(this.FEE_DENOMINATOR);
|
|
177
|
+
state.admin_balances![i] = state.admin_balances![i].plus(fees[i].times(ADMIN_FEE).idiv(this.FEE_DENOMINATOR));
|
|
178
|
+
new_balances[i] = new_balances[i].minus(fees[i]);
|
|
179
|
+
}
|
|
180
|
+
new_balances = this._xp_mem(rates!, new_balances);
|
|
181
|
+
const D2 = this.get_D(new_balances, amp);
|
|
182
|
+
|
|
183
|
+
const burn_amount = D0.minus(D2).times(total_supply).idiv(D0).plus(1);
|
|
184
|
+
state.supply = state.supply.minus(burn_amount);
|
|
185
|
+
|
|
186
|
+
if(! amounts[0].eq(BN_0)) {
|
|
187
|
+
state.eth_balance = state.eth_balance!.minus(amounts[0]);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if(! amounts[1].eq(BN_0)) {
|
|
191
|
+
state.token_balance = state.token_balance!.minus(amounts[1]);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return state;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
exchange(i: number, j: number, dx: BigNumber, state: PoolState): BigNumber {
|
|
198
|
+
const rates = state.stored_rates;
|
|
199
|
+
|
|
200
|
+
let msgValue;
|
|
201
|
+
if(i === 0) {
|
|
202
|
+
msgValue = bigNumberify(dx);
|
|
203
|
+
} else {
|
|
204
|
+
msgValue = BN_0;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const old_balances: BigNumber[] = this._balances(state, msgValue);
|
|
208
|
+
const xp: BigNumber[] = this._xp_mem(rates!, old_balances);
|
|
209
|
+
|
|
210
|
+
const x: BigNumber = xp[i].plus(dx.times(rates![i]).idiv(this.PRECISION));
|
|
211
|
+
const y = this._get_y(i, j, x, xp, state.A);
|
|
212
|
+
|
|
213
|
+
let dy = xp[j].minus(y).minus(1);
|
|
214
|
+
const dy_fee = dy.times(state.fee).idiv(this.FEE_DENOMINATOR);
|
|
215
|
+
|
|
216
|
+
dy = dy.minus(dy_fee).times(this.PRECISION).idiv(rates![j]);
|
|
217
|
+
|
|
218
|
+
xp[i] = x;
|
|
219
|
+
xp[j] = y;
|
|
220
|
+
|
|
221
|
+
const dy_admin_fee = dy_fee.times(ADMIN_FEE).idiv(this.FEE_DENOMINATOR).times(this.PRECISION).idiv(rates![j]);
|
|
222
|
+
if(!dy_admin_fee.eq(BN_0)) {
|
|
223
|
+
state.admin_balances![j] = state.admin_balances![j].plus(dy_admin_fee);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if(i === 0) {
|
|
227
|
+
state.eth_balance = state.eth_balance!.minus(dy);
|
|
228
|
+
} else {
|
|
229
|
+
state.token_balance = state.token_balance!.minus(dx);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return dy;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
private _balances(state: PoolState, value: BigNumber = BN_0): BigNumber[] {
|
|
236
|
+
const { admin_balances, eth_balance, token_balance } = state;
|
|
237
|
+
|
|
238
|
+
state.balances = [
|
|
239
|
+
eth_balance!.minus(admin_balances![0]).minus(value),
|
|
240
|
+
token_balance!.minus(admin_balances![1]),
|
|
241
|
+
];
|
|
242
|
+
|
|
243
|
+
return state.balances;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
public get_dy(
|
|
247
|
+
i: number,
|
|
248
|
+
j: number,
|
|
249
|
+
dx: BigNumber,
|
|
250
|
+
state: Readonly<PoolState>,
|
|
251
|
+
): BigNumber {
|
|
252
|
+
const { stored_rates: rates } = state;
|
|
253
|
+
this.logger.info(`CurveV1: wbETH pool get_dy state: ${JSON.stringify(state)}`)
|
|
254
|
+
return this._get_dy(
|
|
255
|
+
i,
|
|
256
|
+
j,
|
|
257
|
+
dx,
|
|
258
|
+
bigNumberify(state.A),
|
|
259
|
+
bigNumberify(state.fee),
|
|
260
|
+
this._balances(state),
|
|
261
|
+
rates!,
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
public _get_dy(
|
|
266
|
+
i: number,
|
|
267
|
+
j: number,
|
|
268
|
+
dx: BigNumber,
|
|
269
|
+
A: BigNumber,
|
|
270
|
+
fee: BigNumber,
|
|
271
|
+
balances: BigNumber[],
|
|
272
|
+
rates: BigNumber[],
|
|
273
|
+
usefee = true,
|
|
274
|
+
): BigNumber {
|
|
275
|
+
const xp = this._xp_mem(rates, balances);
|
|
276
|
+
const x = xp[i].plus(dx.times(rates[i]).idiv(this.PRECISION));
|
|
277
|
+
const y = this._get_y(i, j, x, xp, A);
|
|
278
|
+
const dy = xp[j].minus(y).minus(BN_1);
|
|
279
|
+
const _fee = fee.times(dy).idiv(this.FEE_DENOMINATOR);
|
|
280
|
+
|
|
281
|
+
return dy.minus(_fee).times(this.PRECISION).idiv(rates[j]);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
protected _xp_mem(_rates: BigNumber[], _balances: BigNumber[]): BigNumber[] {
|
|
285
|
+
const result = new Array(N_COINS).fill(0n);
|
|
286
|
+
for (const i of _.range(Number(N_COINS))) {
|
|
287
|
+
result[i] = _rates[i].times(_balances[i]).idiv(this.PRECISION);
|
|
288
|
+
}
|
|
289
|
+
return result;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
_get_y(
|
|
293
|
+
i: number,
|
|
294
|
+
j: number,
|
|
295
|
+
x: BigNumber,
|
|
296
|
+
xp: BigNumber[],
|
|
297
|
+
A: BigNumber,
|
|
298
|
+
): BigNumber {
|
|
299
|
+
let S_ = BN_0;
|
|
300
|
+
let _x = BN_0;
|
|
301
|
+
const amp = A.times(A_PRECISION);
|
|
302
|
+
const D = this._get_D(xp, amp);
|
|
303
|
+
let c = bigNumberify(D);
|
|
304
|
+
let y_prev = BN_0;
|
|
305
|
+
const Ann = amp.times(this.N_COINS);
|
|
306
|
+
|
|
307
|
+
for (const _i of _.range(N_COINS)) {
|
|
308
|
+
if (_i === i) {
|
|
309
|
+
_x = bigNumberify(x);
|
|
310
|
+
} else if (_i !== j) {
|
|
311
|
+
_x = bigNumberify(xp[_i]);
|
|
312
|
+
} else {
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
S_ = S_.plus(_x);
|
|
316
|
+
c = c.times(D).idiv(_x.times(this.N_COINS)); // c = c * D / (_x * N_COINS)
|
|
317
|
+
}
|
|
318
|
+
// c = c * D * A_PRECISION / (Ann * N_COINS)
|
|
319
|
+
c = c.times(D).times(A_PRECISION).idiv(Ann.times(this.N_COINS));
|
|
320
|
+
// b: uint256 = S_ + D * A_PRECISION / Ann # - D
|
|
321
|
+
const b = S_.plus(D.times(A_PRECISION).idiv(Ann));
|
|
322
|
+
let y = bigNumberify(D);
|
|
323
|
+
|
|
324
|
+
for (const _i of _.range(255)) {
|
|
325
|
+
y_prev = bigNumberify(y);
|
|
326
|
+
y = y.times(y).plus(c).idiv(BN_2.times(y).plus(b).minus(D)); // y = (y*y + c) / (2 * y + b - D)
|
|
327
|
+
|
|
328
|
+
// Equality with the precision of 1
|
|
329
|
+
if (y > y_prev) {
|
|
330
|
+
if (y.minus(y_prev).lte(1)) {
|
|
331
|
+
return y;
|
|
332
|
+
}
|
|
333
|
+
} else {
|
|
334
|
+
if (y_prev.minus(y).lte(1)) {
|
|
335
|
+
return y;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
throw Error('Error when calculate y');
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
add_liquidity(amounts: BigNumber[], state: PoolState): BigNumber {
|
|
344
|
+
const { stored_rates: rates, A, supply, fee } = state;
|
|
345
|
+
|
|
346
|
+
const msgValue = amounts[0];
|
|
347
|
+
|
|
348
|
+
const old_balances = this._balances(state, msgValue);
|
|
349
|
+
const amp = A.times(A_PRECISION);
|
|
350
|
+
|
|
351
|
+
const D0 = this.get_D_mem(rates!, old_balances, amp);
|
|
352
|
+
let total_supply = supply;
|
|
353
|
+
const new_balances = old_balances.slice(0);
|
|
354
|
+
|
|
355
|
+
for (let i = 0; i < this.N_COINS; i++) {
|
|
356
|
+
const amount: BigNumber = amounts[i];
|
|
357
|
+
new_balances[i] = new_balances[i].plus(amount);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const D1 = this.get_D_mem(rates!, new_balances, amp);
|
|
361
|
+
const fees = new Array(N_COINS).fill(0n);
|
|
362
|
+
let mint_amount = BN_0;
|
|
363
|
+
|
|
364
|
+
if (total_supply.gt(BN_0)) {
|
|
365
|
+
const base_fee = fee.times(this.N_COINS).idiv(4 * (this.N_COINS - 1));
|
|
366
|
+
for (let i = 0; i < this.N_COINS; i++) {
|
|
367
|
+
const ideal_balance: BigNumber = D1.times(old_balances[i]).idiv(D0);
|
|
368
|
+
let difference = BN_0;
|
|
369
|
+
const new_balance = new_balances[i];
|
|
370
|
+
if(ideal_balance.gt(new_balance)) {
|
|
371
|
+
difference = ideal_balance.minus(new_balance);
|
|
372
|
+
} else {
|
|
373
|
+
difference = new_balance.minus(ideal_balance);
|
|
374
|
+
}
|
|
375
|
+
fees[i] = base_fee.times(difference).idiv(this.FEE_DENOMINATOR);
|
|
376
|
+
state.admin_balances![i] = state.admin_balances![i].plus(fees[i].times(ADMIN_FEE).idiv(this.FEE_DENOMINATOR));
|
|
377
|
+
new_balances[i] = new_balances[i].minus(fees[i]);
|
|
378
|
+
}
|
|
379
|
+
const xp = this._xp_mem(rates!, new_balances);
|
|
380
|
+
const D2 = this.get_D(xp, amp);
|
|
381
|
+
mint_amount = total_supply.times(D2.minus(D0)).idiv(D0);
|
|
382
|
+
} else {
|
|
383
|
+
mint_amount = D1;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if(amounts[1].gt(0)) {
|
|
387
|
+
state.token_balance = state.token_balance!.plus(amounts[1]);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
total_supply = total_supply.plus(mint_amount);
|
|
391
|
+
state.supply = total_supply;
|
|
392
|
+
|
|
393
|
+
return mint_amount;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
remove_liquidity_one_coin(
|
|
397
|
+
token_amount: BigNumber,
|
|
398
|
+
i: number,
|
|
399
|
+
state: PoolState,
|
|
400
|
+
): BigNumber {
|
|
401
|
+
const { stored_rates: rates } = state;
|
|
402
|
+
|
|
403
|
+
let { dy } = this._calc_withdraw_one_coin(
|
|
404
|
+
token_amount,
|
|
405
|
+
i,
|
|
406
|
+
state,
|
|
407
|
+
);
|
|
408
|
+
|
|
409
|
+
state.admin_balances![i] = state.admin_balances![i].plus(dy[1].times(ADMIN_FEE).idiv(this.FEE_DENOMINATOR));
|
|
410
|
+
const total_supply = state.supply.minus(token_amount);
|
|
411
|
+
state.supply = total_supply;
|
|
412
|
+
|
|
413
|
+
if(i === 0) {
|
|
414
|
+
state.eth_balance = state.eth_balance!.minus(dy[0]);
|
|
415
|
+
} else {
|
|
416
|
+
state.token_balance = state.token_balance!.minus(dy[0]);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
return dy[0];
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
private _calc_withdraw_one_coin(
|
|
423
|
+
_token_amount: BigNumber,
|
|
424
|
+
i: number,
|
|
425
|
+
state: PoolState,
|
|
426
|
+
) {
|
|
427
|
+
const { A, fee, supply, stored_rates: rates } = state;
|
|
428
|
+
|
|
429
|
+
const amp = A.times(A_PRECISION);
|
|
430
|
+
const xp = this._xp_mem(rates!, this._balances(state));
|
|
431
|
+
const D0 = this._get_D(xp, amp);
|
|
432
|
+
|
|
433
|
+
const total_supply = supply;
|
|
434
|
+
const D1 = D0.minus(_token_amount).idiv(total_supply);
|
|
435
|
+
const new_y = this.get_y_D(amp, i, xp, D1);
|
|
436
|
+
|
|
437
|
+
const base_fee = fee.times(this.N_COINS).idiv(4 * (this.N_COINS - 1));
|
|
438
|
+
let xp_reduced = new Array(N_COINS).fill(0n);
|
|
439
|
+
|
|
440
|
+
for (const j of _.range(N_COINS)) {
|
|
441
|
+
let dx_expected = BN_0;
|
|
442
|
+
const xp_j = xp[j];
|
|
443
|
+
if(j == i) {
|
|
444
|
+
dx_expected = xp_j.times(D1).idiv(D0).minus(new_y);
|
|
445
|
+
} else {
|
|
446
|
+
dx_expected = xp_j.minus(xp_j.times(D1).idiv(D0));
|
|
447
|
+
}
|
|
448
|
+
xp_reduced[j] = xp_j.minus(base_fee.times(dx_expected).idiv(this.FEE_DENOMINATOR));
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
let dy = xp_reduced[i].minus(this.get_y_D(amp, i, xp_reduced, D1));
|
|
452
|
+
const dy_0 = xp[i].minus(new_y).times(this.PRECISION).idiv(rates![i]);
|
|
453
|
+
dy = dy.minus(1).times(this.PRECISION).idiv(rates![i]);
|
|
454
|
+
|
|
455
|
+
return { dy, dy_fee: dy_0.minus(dy) };
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
private get_y_D(
|
|
459
|
+
A_: BigNumber,
|
|
460
|
+
i: number,
|
|
461
|
+
xp: BigNumber[],
|
|
462
|
+
D: BigNumber,
|
|
463
|
+
): BigNumber {
|
|
464
|
+
let c: BigNumber = D;
|
|
465
|
+
let S_: BigNumber = BN_0;
|
|
466
|
+
const Ann: BigNumber = A_.times(this.N_COINS);
|
|
467
|
+
|
|
468
|
+
let _x: BigNumber = BN_0;
|
|
469
|
+
for (let _i = 0; _i < this.N_COINS; _i++) {
|
|
470
|
+
if (_i !== i) {
|
|
471
|
+
_x = xp[_i];
|
|
472
|
+
} else {
|
|
473
|
+
continue;
|
|
474
|
+
}
|
|
475
|
+
S_ = S_.plus(_x);
|
|
476
|
+
c = c.times(D).idiv(_x.times(this.N_COINS));
|
|
477
|
+
}
|
|
478
|
+
c = c.times(D).idiv(Ann.times(this.N_COINS));
|
|
479
|
+
const b: BigNumber = S_.plus(D.idiv(Ann));
|
|
480
|
+
let y_prev: BigNumber = BN_0;
|
|
481
|
+
let y: BigNumber = D;
|
|
482
|
+
for (let _i = 0; _i < 255; _i++) {
|
|
483
|
+
y_prev = y;
|
|
484
|
+
y = y.times(y).plus(c).idiv(y.times(2).plus(b).minus(D));
|
|
485
|
+
// Equality with the precision of 1
|
|
486
|
+
if (y.gt(y_prev)) {
|
|
487
|
+
if (y.minus(y_prev).lte(1)) {
|
|
488
|
+
break;
|
|
489
|
+
}
|
|
490
|
+
} else {
|
|
491
|
+
if (y_prev.minus(y).lte(1)) {
|
|
492
|
+
break;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
return y;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
_get_D(xp: BigNumber[], amp: BigNumber): BigNumber {
|
|
500
|
+
let S = BN_0;
|
|
501
|
+
for (const _x of xp) {
|
|
502
|
+
S = S.plus(_x);
|
|
503
|
+
}
|
|
504
|
+
if (S.eq(BN_0)) {
|
|
505
|
+
return BN_0;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
let D = bigNumberify(S);
|
|
509
|
+
const Ann = amp.times(this.N_COINS);
|
|
510
|
+
for (const _i of _.range(255)) {
|
|
511
|
+
let D_P = D.times(D)
|
|
512
|
+
.idiv(xp[0])
|
|
513
|
+
.times(D)
|
|
514
|
+
.idiv(xp[1])
|
|
515
|
+
.idiv(this.N_COINS ** 2); // D_P: uint256 = D * D / _xp[0] * D / _xp[1] / (N_COINS)**2
|
|
516
|
+
|
|
517
|
+
let Dprev = bigNumberify(D);
|
|
518
|
+
|
|
519
|
+
const dividend = Ann.times(S)
|
|
520
|
+
.idiv(A_PRECISION)
|
|
521
|
+
.plus(D_P.times(this.N_COINS))
|
|
522
|
+
.times(D);
|
|
523
|
+
const divisor = Ann.minus(A_PRECISION)
|
|
524
|
+
.times(D)
|
|
525
|
+
.idiv(A_PRECISION)
|
|
526
|
+
.plus(D_P.times(this.N_COINS + 1));
|
|
527
|
+
D = dividend.idiv(divisor); // D = (Ann * S / A_PRECISION + D_P * N_COINS) * D / ((Ann - A_PRECISION) * D / A_PRECISION + (N_COINS + 1) * D_P)
|
|
528
|
+
|
|
529
|
+
if (D.gt(Dprev)) {
|
|
530
|
+
if (D.minus(Dprev).lte(1)) {
|
|
531
|
+
return D;
|
|
532
|
+
}
|
|
533
|
+
} else {
|
|
534
|
+
if (Dprev.minus(D).lte(1)) {
|
|
535
|
+
return D;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
throw Error('Error when calcucate D');
|
|
541
|
+
}
|
|
542
|
+
}
|
package/tests/constants-e2e.ts
CHANGED
|
@@ -47,6 +47,14 @@ export const Tokens: {
|
|
|
47
47
|
address: '0xfe18be6b3bd88a2d2a7f928d00292e7a9963cfc6',
|
|
48
48
|
decimals: 18,
|
|
49
49
|
},
|
|
50
|
+
wBETH: {
|
|
51
|
+
address: '0xa2e3356610840701bdf5611a53974510ae27e2e1',
|
|
52
|
+
decimals: 18,
|
|
53
|
+
},
|
|
54
|
+
frxETH: {
|
|
55
|
+
address: '0x5e8422345238f34275888049021821e8e08caa1f',
|
|
56
|
+
decimals: 18,
|
|
57
|
+
},
|
|
50
58
|
BADGER: {
|
|
51
59
|
address: '0x3472A5A71965499acd81997a54BBA8D852C6E53d',
|
|
52
60
|
decimals: 18,
|
|
@@ -837,6 +845,8 @@ export const Holders: {
|
|
|
837
845
|
sBTC: '0xA2e3475D13776C6E42ff37B47286827d959B2195',
|
|
838
846
|
BADGER: '0x34e2741a3f8483dbe5231f61c005110ff4b9f50a',
|
|
839
847
|
STETH: '0x9bdb521a97e95177bf252c253e256a60c3e14447',
|
|
848
|
+
wBETH: '0xAAcd84176D37FbC52FBd5DFc2EdD84cf22Bc5FBF',
|
|
849
|
+
frxETH: '0x605d36F54546eaC8318deA61670e48db3A071A36',
|
|
840
850
|
wstETH: '0x6cE0F913F035ec6195bC3cE885aec4C66E485BC4',
|
|
841
851
|
WETH: '0x2fEb1512183545f48f6b9C5b4EbfCaF49CfCa6F3',
|
|
842
852
|
USDT: '0x8A446971dbB112f3be15bc38C14D44B94D9E94b9',
|
|
@@ -867,7 +877,7 @@ export const Holders: {
|
|
|
867
877
|
LINK: '0x98c63b7b319dfbdf3d811530f2ab9dfe4983af9d',
|
|
868
878
|
ADAIv1: '0x3021026e4ff227571a5a563ad19ea657c7027e59',
|
|
869
879
|
CETH: '0x712d0f306956a6a4b4f9319ad9b9de48c5345996',
|
|
870
|
-
CDAI: '
|
|
880
|
+
CDAI: '0xE0E484Dfa7F3aA36733A915D6f07EB5a57A74a11',
|
|
871
881
|
CUSDC: '0xC2F61a6eEEC48d686901D325CDE9233b81c793F3',
|
|
872
882
|
EURS: '0xC1056Adeb61a01964Ea265cA95EffB7016f9Ed78',
|
|
873
883
|
EURT: '0x6914FC70fAC4caB20a8922E900C4BA57fEECf8E1',
|