@paraswap/dex-lib 2.42.16 → 2.42.17-wombat.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/.vscode/launch.json +39 -0
  2. package/.vscode/settings.json +2 -0
  3. package/.vscode/tasks.json +15 -0
  4. package/build/abi/algebra/SwapRouter.json +281 -0
  5. package/build/abi/wombat/asset.json +769 -0
  6. package/build/abi/wombat/bmw.json +1247 -0
  7. package/build/abi/wombat/pool.json +1364 -0
  8. package/build/config.js +7 -6
  9. package/build/config.js.map +1 -1
  10. package/build/dex/index.js +2 -0
  11. package/build/dex/index.js.map +1 -1
  12. package/build/dex/swaap-v2/stable-coins.d.ts +5 -0
  13. package/build/dex/swaap-v2/stable-coins.js +26 -0
  14. package/build/dex/swaap-v2/stable-coins.js.map +1 -0
  15. package/build/dex/wombat/config.d.ts +4 -0
  16. package/build/dex/wombat/config.js +77 -0
  17. package/build/dex/wombat/config.js.map +1 -0
  18. package/build/dex/wombat/types.d.ts +33 -0
  19. package/build/dex/wombat/types.js +3 -0
  20. package/build/dex/wombat/types.js.map +1 -0
  21. package/build/dex/wombat/utils.d.ts +9 -0
  22. package/build/dex/wombat/utils.js +66 -0
  23. package/build/dex/wombat/utils.js.map +1 -0
  24. package/build/dex/wombat/wombat-bmw.d.ts +40 -0
  25. package/build/dex/wombat/wombat-bmw.js +147 -0
  26. package/build/dex/wombat/wombat-bmw.js.map +1 -0
  27. package/build/dex/wombat/wombat-pool.d.ts +22 -0
  28. package/build/dex/wombat/wombat-pool.js +161 -0
  29. package/build/dex/wombat/wombat-pool.js.map +1 -0
  30. package/build/dex/wombat/wombat-quoter.d.ts +16 -0
  31. package/build/dex/wombat/wombat-quoter.js +176 -0
  32. package/build/dex/wombat/wombat-quoter.js.map +1 -0
  33. package/build/dex/wombat/wombat.d.ts +55 -0
  34. package/build/dex/wombat/wombat.js +290 -0
  35. package/build/dex/wombat/wombat.js.map +1 -0
  36. package/build/rebase-tokens.json +17 -0
  37. package/package.json +1 -1
  38. package/src/abi/wombat/asset.json +769 -0
  39. package/src/abi/wombat/bmw.json +1247 -0
  40. package/src/abi/wombat/pool.json +1364 -0
  41. package/src/config.ts +7 -7
  42. package/src/dex/index.ts +2 -0
  43. package/src/dex/wombat/config.ts +77 -0
  44. package/src/dex/wombat/types.ts +55 -0
  45. package/src/dex/wombat/utils.ts +62 -0
  46. package/src/dex/wombat/wombat-bmw.ts +209 -0
  47. package/src/dex/wombat/wombat-e2e.test.ts +264 -0
  48. package/src/dex/wombat/wombat-integration.test.ts +258 -0
  49. package/src/dex/wombat/wombat-pool.ts +231 -0
  50. package/src/dex/wombat/wombat-quoter.ts +269 -0
  51. package/src/dex/wombat/wombat.ts +426 -0
  52. package/tests/constants-e2e.ts +20 -4
  53. package/tests/utils-e2e.ts +2 -1
  54. package/build/abi/algebra/AlgebraPool.abi.json +0 -727
  55. package/build/abi/uniswap-v3/UniswapV3Quoter.abi.json +0 -193
  56. package/build/dex/algebra/algebra-pool.d.ts +0 -46
  57. package/build/dex/algebra/algebra-pool.js +0 -336
  58. package/build/dex/algebra/algebra-pool.js.map +0 -1
  59. package/build/dex/quickswap/camelot-v3.d.ts +0 -6
  60. package/build/dex/quickswap/camelot-v3.js +0 -19
  61. package/build/dex/quickswap/camelot-v3.js.map +0 -1
  62. package/build/dex/quickswap/zyberswap-v3.d.ts +0 -6
  63. package/build/dex/quickswap/zyberswap-v3.js +0 -19
  64. package/build/dex/quickswap/zyberswap-v3.js.map +0 -1
@@ -0,0 +1,231 @@
1
+ import { Interface } from '@ethersproject/abi';
2
+ import { Address } from '@paraswap/core';
3
+ import _ from 'lodash';
4
+
5
+ import { MultiCallParams, MultiResult } from '../../lib/multi-wrapper';
6
+ import { IDexHelper } from '../../dex-helper';
7
+ import { StatefulRpcPoller } from '../../lib/stateful-rpc-poller/stateful-rpc-poller';
8
+ import { StatePollingManager } from '../../lib/stateful-rpc-poller/state-polling-manager';
9
+ import { ObjWithUpdateInfo } from '../../lib/stateful-rpc-poller/types';
10
+ import { pollingManagerCbExtractor } from '../../lib/stateful-rpc-poller/utils';
11
+ import {
12
+ addressArrayDecode,
13
+ booleanDecode,
14
+ uint256ToBigInt,
15
+ uint8ToNumber,
16
+ } from '../../lib/decoders';
17
+ import PoolABI from '../../abi/wombat/pool.json';
18
+ import AssetABI from '../../abi/wombat/asset.json';
19
+ import { uint120ToBigInt } from './utils';
20
+ import { AssetState, MulticallResultOutputs, PoolState } from './types';
21
+
22
+ export class WombatPool extends StatefulRpcPoller<
23
+ PoolState,
24
+ MulticallResultOutputs
25
+ > {
26
+ static readonly poolInterface = new Interface(PoolABI);
27
+ static readonly assetInterface = new Interface(AssetABI);
28
+
29
+ constructor(
30
+ dexKey: string,
31
+ poolIdentifier: string,
32
+ dexHelper: IDexHelper,
33
+ protected poolAddress: Address,
34
+ protected asset2TokenMap: Map<Address, Address>,
35
+ ) {
36
+ const callbacks = pollingManagerCbExtractor(
37
+ StatePollingManager.getInstance(dexHelper),
38
+ );
39
+
40
+ super(dexKey, poolIdentifier, dexHelper, 0, 0, false, callbacks);
41
+ }
42
+
43
+ protected _getFetchStateMultiCalls(): MultiCallParams<MulticallResultOutputs>[] {
44
+ const params: MultiCallParams<MulticallResultOutputs>[] = [];
45
+
46
+ params.push({
47
+ target: this.poolAddress,
48
+ callData: WombatPool.poolInterface.encodeFunctionData('paused'),
49
+ decodeFunction: booleanDecode,
50
+ });
51
+ params.push({
52
+ target: this.poolAddress,
53
+ callData: WombatPool.poolInterface.encodeFunctionData('ampFactor'),
54
+ decodeFunction: uint256ToBigInt,
55
+ });
56
+ params.push({
57
+ target: this.poolAddress,
58
+ callData: WombatPool.poolInterface.encodeFunctionData('haircutRate'),
59
+ decodeFunction: uint256ToBigInt,
60
+ });
61
+ params.push({
62
+ target: this.poolAddress,
63
+ callData: WombatPool.poolInterface.encodeFunctionData('startCovRatio'),
64
+ decodeFunction: uint256ToBigInt,
65
+ });
66
+ params.push({
67
+ target: this.poolAddress,
68
+ callData: WombatPool.poolInterface.encodeFunctionData('endCovRatio'),
69
+ decodeFunction: uint256ToBigInt,
70
+ });
71
+ params.push({
72
+ target: this.poolAddress,
73
+ callData: WombatPool.poolInterface.encodeFunctionData('getTokens'),
74
+ decodeFunction: addressArrayDecode,
75
+ });
76
+
77
+ this.asset2TokenMap.forEach((token, asset) => {
78
+ params.push({
79
+ target: this.poolAddress,
80
+ callData: WombatPool.poolInterface.encodeFunctionData('isPaused', [
81
+ token,
82
+ ]),
83
+ decodeFunction: booleanDecode,
84
+ });
85
+ params.push({
86
+ target: asset,
87
+ callData: WombatPool.assetInterface.encodeFunctionData('cash'),
88
+ decodeFunction: uint120ToBigInt,
89
+ });
90
+ params.push({
91
+ target: asset,
92
+ callData: WombatPool.assetInterface.encodeFunctionData('liability'),
93
+ decodeFunction: uint120ToBigInt,
94
+ });
95
+ params.push({
96
+ target: asset,
97
+ callData: WombatPool.assetInterface.encodeFunctionData(
98
+ 'underlyingTokenDecimals',
99
+ ),
100
+ decodeFunction: uint8ToNumber,
101
+ });
102
+ params.push({
103
+ target: asset,
104
+ callData:
105
+ WombatPool.assetInterface.encodeFunctionData('getRelativePrice'),
106
+ decodeFunction: uint256ToBigInt,
107
+ });
108
+ });
109
+
110
+ return params;
111
+ }
112
+
113
+ protected _parseStateFromMultiResults(
114
+ multiOutputs: MulticallResultOutputs[],
115
+ ): PoolState {
116
+ const [
117
+ paused,
118
+ ampFactor,
119
+ haircutRate,
120
+ startCovRatio,
121
+ endCovRatio,
122
+ tokens,
123
+ ...remained
124
+ ] = multiOutputs as [
125
+ boolean,
126
+ bigint,
127
+ bigint,
128
+ bigint,
129
+ bigint,
130
+ Address[],
131
+ ...MulticallResultOutputs[],
132
+ ];
133
+
134
+ const assetTokenArray = Array.from(this.asset2TokenMap.entries());
135
+ const token2AssetStates = new Map<Address, AssetState>();
136
+ _.chunk(remained, 5).forEach((chunk, i) => {
137
+ const [paused, cash, liability, underlyingTokenDecimals, relativePrice] =
138
+ chunk as [boolean, bigint, bigint, number, bigint | undefined];
139
+
140
+ const [asset, token] = assetTokenArray[i];
141
+ token2AssetStates.set(token, {
142
+ address: asset,
143
+ paused,
144
+ cash,
145
+ liability,
146
+ underlyingTokenDecimals,
147
+ relativePrice,
148
+ });
149
+ });
150
+
151
+ const poolState: PoolState = {
152
+ params: {
153
+ paused,
154
+ ampFactor,
155
+ haircutRate,
156
+ startCovRatio,
157
+ endCovRatio,
158
+ },
159
+ underlyingAddresses: tokens,
160
+ asset: {},
161
+ };
162
+
163
+ const isMainPool =
164
+ Array.from(token2AssetStates.values()).filter(
165
+ assetState => assetState.relativePrice === undefined,
166
+ ).length > 0;
167
+ for (const token of poolState.underlyingAddresses) {
168
+ if (!token2AssetStates.has(token)) {
169
+ // this happens when asset has been added to pool but is not added to BMW yet
170
+ continue;
171
+ }
172
+ poolState.asset[token] = token2AssetStates.get(token)!;
173
+ if (isMainPool) {
174
+ poolState.asset[token].relativePrice = undefined;
175
+ }
176
+ }
177
+
178
+ return poolState;
179
+ }
180
+
181
+ public async fetchLatestStateFromRpc(): Promise<ObjWithUpdateInfo<PoolState> | null> {
182
+ const multiCalls = this.getFetchStateWithBlockInfoMultiCalls();
183
+ try {
184
+ const lastUpdatedAtMs = Date.now();
185
+ const aggregatedResults = (await this.dexHelper.multiWrapper.tryAggregate<
186
+ number | MulticallResultOutputs
187
+ >(
188
+ false,
189
+ multiCalls as MultiCallParams<MulticallResultOutputs | number>[],
190
+ )) as [MultiResult<number>, ...MultiResult<MulticallResultOutputs>[]];
191
+
192
+ return this.parseStateFromMultiResultsWithBlockInfo(
193
+ aggregatedResults,
194
+ lastUpdatedAtMs,
195
+ );
196
+ } catch (e) {
197
+ this._logMessageWithSuppression('ERROR_FETCHING_STATE_FROM_RPC', e);
198
+ }
199
+
200
+ return null;
201
+ }
202
+
203
+ public parseStateFromMultiResultsWithBlockInfo(
204
+ multiOutputs: [
205
+ MultiResult<number>,
206
+ ...MultiResult<MulticallResultOutputs>[],
207
+ ],
208
+ lastUpdatedAtMs: number,
209
+ ): ObjWithUpdateInfo<PoolState> {
210
+ const [blockNumber, ...outputsForAbstract] = multiOutputs.map((m, i) => {
211
+ return m.returnData;
212
+ }) as [number, ...MulticallResultOutputs[]];
213
+
214
+ return {
215
+ value: this._parseStateFromMultiResults(outputsForAbstract),
216
+ blockNumber,
217
+ lastUpdatedAtMs,
218
+ };
219
+ }
220
+
221
+ public addAssets(asset2TokenMap: Map<Address, Address>) {
222
+ for (const [asset, token] of asset2TokenMap) {
223
+ if (this.asset2TokenMap.has(asset)) {
224
+ continue;
225
+ }
226
+
227
+ this.asset2TokenMap.set(asset, token);
228
+ this._cachedMultiCallData = undefined;
229
+ }
230
+ }
231
+ }
@@ -0,0 +1,269 @@
1
+ import { AssetState, PoolParams } from './types';
2
+ import { fromWad, sqrt, toWad, WAD, wdiv, wmul } from './utils';
3
+
4
+ export class WombatQuoter {
5
+ private readonly ampFactor: bigint;
6
+ private readonly haircutRate: bigint;
7
+ private readonly startCovRatio: bigint;
8
+ private readonly endCovRatio: bigint;
9
+
10
+ constructor(poolParams: PoolParams) {
11
+ this.ampFactor = poolParams.ampFactor;
12
+ this.haircutRate = poolParams.haircutRate;
13
+ this.startCovRatio = poolParams.startCovRatio;
14
+ this.endCovRatio = poolParams.endCovRatio;
15
+ }
16
+
17
+ public getQuote(
18
+ fromAsset: AssetState,
19
+ toAsset: AssetState,
20
+ fromAmount: bigint,
21
+ ): bigint {
22
+ if (fromAmount === 0n || fromAsset.paused) {
23
+ return 0n;
24
+ }
25
+
26
+ fromAmount = toWad(fromAmount, BigInt(fromAsset.underlyingTokenDecimals));
27
+
28
+ try {
29
+ const { actualToAmount, haircut } = this._highCovRatioPoolQuoteFrom(
30
+ fromAsset,
31
+ toAsset,
32
+ fromAmount,
33
+ );
34
+ const toCash = toAsset.cash - actualToAmount - haircut;
35
+
36
+ if (wdiv(toCash, toAsset.liability) < WAD / 100n) {
37
+ return 0n;
38
+ }
39
+
40
+ return fromWad(actualToAmount, BigInt(toAsset.underlyingTokenDecimals));
41
+ } catch (e) {
42
+ return 0n;
43
+ }
44
+ }
45
+
46
+ private _highCovRatioPoolQuoteFrom(
47
+ fromAsset: AssetState,
48
+ toAsset: AssetState,
49
+ fromAmount: bigint,
50
+ ): { actualToAmount: bigint; haircut: bigint } {
51
+ let { actualToAmount, haircut } = this._quoteFrom(
52
+ fromAsset,
53
+ toAsset,
54
+ fromAmount,
55
+ );
56
+
57
+ if (fromAmount > 0n) {
58
+ const fromCash = fromAsset.cash;
59
+ const fromLiability = fromAsset.liability;
60
+
61
+ const finalFromAssetCovRatio = wdiv(fromCash + fromAmount, fromLiability);
62
+ if (finalFromAssetCovRatio > this.startCovRatio) {
63
+ // charge high cov ratio fee
64
+ const highCovRatioFee = wmul(
65
+ this._highCovRatioFee(
66
+ wdiv(fromCash, fromLiability),
67
+ finalFromAssetCovRatio,
68
+ ),
69
+ actualToAmount,
70
+ );
71
+
72
+ actualToAmount -= highCovRatioFee;
73
+ haircut += highCovRatioFee;
74
+ }
75
+ } else {
76
+ // reverse quote
77
+ const toCash = toAsset.cash;
78
+ const toLiability = toAsset.liability;
79
+
80
+ const finalToAssetCovRatio = wdiv(toCash + actualToAmount, toLiability);
81
+ if (finalToAssetCovRatio <= this.startCovRatio) {
82
+ // happy path: no high cov ratio fee is charged
83
+ return { actualToAmount, haircut };
84
+ } else if (wdiv(toCash, toLiability) >= this.endCovRatio) {
85
+ // the to-asset exceeds it's cov ratio limit, further swap to increase cov ratio is impossible
86
+ throw new Error('WOMBAT_COV_RATIO_LIMIT_EXCEEDED');
87
+ }
88
+
89
+ // reverse quote: cov ratio of the to-asset exceed endCovRatio. direct reverse quote is not supported
90
+ // we binary search for a upper bound
91
+ actualToAmount = this._findUpperBound(toAsset, fromAsset, -fromAmount);
92
+ const result = this._highCovRatioPoolQuoteFrom(
93
+ toAsset,
94
+ fromAsset,
95
+ actualToAmount,
96
+ );
97
+ haircut = result.haircut;
98
+ }
99
+
100
+ return { actualToAmount, haircut };
101
+ }
102
+
103
+ private _highCovRatioFee(
104
+ initCovRatio: bigint,
105
+ finalCovRatio: bigint,
106
+ ): bigint {
107
+ const startCovRatio = this.startCovRatio!;
108
+ const endCovRatio = this.endCovRatio!;
109
+ if (finalCovRatio > endCovRatio) {
110
+ // invalid swap
111
+ throw new Error('WOMBAT_COV_RATIO_LIMIT_EXCEEDED');
112
+ } else if (
113
+ finalCovRatio <= startCovRatio ||
114
+ finalCovRatio <= initCovRatio
115
+ ) {
116
+ return 0n;
117
+ }
118
+
119
+ // 1. Calculate the area of fee(r) = (r - startCovRatio) / (endCovRatio - startCovRatio)
120
+ // when r increase from initCovRatio to finalCovRatio
121
+ // 2. Then multiply it by (endCovRatio - startCovRatio) / (finalCovRatio - initCovRatio)
122
+ // to get the average fee over the range
123
+ const a =
124
+ initCovRatio <= startCovRatio
125
+ ? 0n
126
+ : (initCovRatio - startCovRatio) * (initCovRatio - startCovRatio);
127
+ const b = (finalCovRatio - startCovRatio) * (finalCovRatio - startCovRatio);
128
+ return wdiv(
129
+ (b - a) / (finalCovRatio - initCovRatio) / BigInt(2),
130
+ endCovRatio - startCovRatio,
131
+ );
132
+ }
133
+
134
+ private _quoteFrom(
135
+ fromAsset: AssetState,
136
+ toAsset: AssetState,
137
+ fromAmount: bigint,
138
+ ): { actualToAmount: bigint; haircut: bigint } {
139
+ if (fromAmount < 0n) {
140
+ fromAmount = wdiv(fromAmount, WAD - this.haircutRate);
141
+ }
142
+
143
+ let fromCash = fromAsset.cash;
144
+ const toCash = toAsset.cash;
145
+ let fromLiability = fromAsset.liability;
146
+ const toLiability = toAsset.liability;
147
+
148
+ const scaleFactor = this._quoteFactor(fromAsset, toAsset);
149
+ if (scaleFactor !== WAD) {
150
+ fromCash = (fromCash * scaleFactor) / WAD;
151
+ fromLiability = (fromLiability * scaleFactor) / WAD;
152
+ fromAmount = (fromAmount * scaleFactor) / WAD;
153
+ }
154
+
155
+ const idealToAmount = WombatQuoter.swapQuoteFunc(
156
+ fromCash,
157
+ toCash,
158
+ fromLiability,
159
+ toLiability,
160
+ fromAmount,
161
+ this.ampFactor,
162
+ );
163
+ if (
164
+ (fromAmount > 0n && toCash < idealToAmount) ||
165
+ (fromAmount < 0n && fromCash < -fromAmount)
166
+ ) {
167
+ throw new Error('WOMBAT_CASH_NOT_ENOUGH');
168
+ }
169
+
170
+ let actualToAmount, haircut;
171
+ if (fromAmount > 0) {
172
+ haircut = wmul(idealToAmount, this.haircutRate);
173
+ actualToAmount = idealToAmount - haircut;
174
+ } else {
175
+ actualToAmount = idealToAmount;
176
+ haircut = wmul(-fromAmount, this.haircutRate);
177
+ }
178
+
179
+ return { actualToAmount, haircut };
180
+ }
181
+
182
+ private _findUpperBound(
183
+ fromAsset: AssetState,
184
+ toAsset: AssetState,
185
+ toAmount: bigint,
186
+ ): bigint {
187
+ const decimals = BigInt(fromAsset.underlyingTokenDecimals);
188
+ const toWadFactor = toWad(1n, decimals);
189
+ // the search value uses the same number of digits as the token
190
+ let high = fromWad(
191
+ wmul(fromAsset.liability, this.endCovRatio!) - fromAsset.cash,
192
+ decimals,
193
+ );
194
+ let low = 1n;
195
+
196
+ // verify `high` is a valid upper bound
197
+ const { actualToAmount: quote } = this._highCovRatioPoolQuoteFrom(
198
+ fromAsset,
199
+ toAsset,
200
+ high * toWadFactor,
201
+ );
202
+ if (quote < toAmount) {
203
+ throw new Error('WOMBAT_COV_RATIO_LIMIT_EXCEEDED');
204
+ }
205
+
206
+ // Note: we might limit the maximum number of rounds if the request is always rejected by the RPC server
207
+ while (low < high) {
208
+ const mid = (low + high) / BigInt(2);
209
+ const { actualToAmount: quote } = this._highCovRatioPoolQuoteFrom(
210
+ fromAsset,
211
+ toAsset,
212
+ mid * toWadFactor,
213
+ );
214
+ if (quote >= toAmount) {
215
+ high = mid;
216
+ } else {
217
+ low = mid + 1n;
218
+ }
219
+ }
220
+ return high * toWadFactor;
221
+ }
222
+
223
+ private _quoteFactor(fromAsset: AssetState, toAsset: AssetState): bigint {
224
+ if (!fromAsset.relativePrice || !toAsset.relativePrice) {
225
+ return WAD;
226
+ }
227
+ return (WAD * fromAsset.relativePrice) / toAsset.relativePrice;
228
+ }
229
+
230
+ static solveQuad(b: bigint, c: bigint): bigint {
231
+ return (sqrt(b * b + c * 4n * WAD, b) - b) / 2n;
232
+ }
233
+
234
+ static swapQuoteFunc(
235
+ aX: bigint,
236
+ aY: bigint,
237
+ lX: bigint,
238
+ lY: bigint,
239
+ dX: bigint,
240
+ a: bigint,
241
+ ): bigint {
242
+ if (lX == 0n || lY == 0n) {
243
+ // in case div of 0, CORE_UNDERFLOW
244
+ return 0n;
245
+ }
246
+
247
+ // int256 D = Ax + Ay - A.wmul((Lx * Lx) / Ax + (Ly * Ly) / Ay); // flattened _invariantFunc
248
+ const d = aX + aY - wmul(a, (lX * lX) / aX + (lY * lY) / aY);
249
+ // int256 rx_ = (Ax + Dx).wdiv(Lx);
250
+ const rX = wdiv(aX + dX, lX);
251
+ // int256 b = (Lx * (rx_ - A.wdiv(rx_))) / Ly - D.wdiv(Ly); // flattened _coefficientFunc
252
+ const b = (lX * (rX - wdiv(a, rX))) / lY - wdiv(d, lY);
253
+ // int256 ry_ = _solveQuad(b, A);
254
+ const rY = WombatQuoter.solveQuad(b, a);
255
+ // int256 Dy = Ly.wmul(ry_) - Ay;
256
+ const dY = wmul(lY, rY) - aY;
257
+
258
+ // if (Dy < 0) {
259
+ // quote = uint256(-Dy);
260
+ // } else {
261
+ // quote = uint256(Dy);
262
+ // }
263
+ if (dY < 0n) {
264
+ return -dY;
265
+ } else {
266
+ return dY;
267
+ }
268
+ }
269
+ }