@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.
- package/.vscode/launch.json +39 -0
- package/.vscode/settings.json +2 -0
- package/.vscode/tasks.json +15 -0
- package/build/abi/algebra/SwapRouter.json +281 -0
- package/build/abi/wombat/asset.json +769 -0
- package/build/abi/wombat/bmw.json +1247 -0
- package/build/abi/wombat/pool.json +1364 -0
- package/build/config.js +7 -6
- package/build/config.js.map +1 -1
- package/build/dex/index.js +2 -0
- package/build/dex/index.js.map +1 -1
- package/build/dex/swaap-v2/stable-coins.d.ts +5 -0
- package/build/dex/swaap-v2/stable-coins.js +26 -0
- package/build/dex/swaap-v2/stable-coins.js.map +1 -0
- package/build/dex/wombat/config.d.ts +4 -0
- package/build/dex/wombat/config.js +77 -0
- package/build/dex/wombat/config.js.map +1 -0
- package/build/dex/wombat/types.d.ts +33 -0
- package/build/dex/wombat/types.js +3 -0
- package/build/dex/wombat/types.js.map +1 -0
- package/build/dex/wombat/utils.d.ts +9 -0
- package/build/dex/wombat/utils.js +66 -0
- package/build/dex/wombat/utils.js.map +1 -0
- package/build/dex/wombat/wombat-bmw.d.ts +40 -0
- package/build/dex/wombat/wombat-bmw.js +147 -0
- package/build/dex/wombat/wombat-bmw.js.map +1 -0
- package/build/dex/wombat/wombat-pool.d.ts +22 -0
- package/build/dex/wombat/wombat-pool.js +161 -0
- package/build/dex/wombat/wombat-pool.js.map +1 -0
- package/build/dex/wombat/wombat-quoter.d.ts +16 -0
- package/build/dex/wombat/wombat-quoter.js +176 -0
- package/build/dex/wombat/wombat-quoter.js.map +1 -0
- package/build/dex/wombat/wombat.d.ts +55 -0
- package/build/dex/wombat/wombat.js +290 -0
- package/build/dex/wombat/wombat.js.map +1 -0
- package/build/rebase-tokens.json +17 -0
- package/package.json +1 -1
- package/src/abi/wombat/asset.json +769 -0
- package/src/abi/wombat/bmw.json +1247 -0
- package/src/abi/wombat/pool.json +1364 -0
- package/src/config.ts +7 -7
- package/src/dex/index.ts +2 -0
- package/src/dex/wombat/config.ts +77 -0
- package/src/dex/wombat/types.ts +55 -0
- package/src/dex/wombat/utils.ts +62 -0
- package/src/dex/wombat/wombat-bmw.ts +209 -0
- package/src/dex/wombat/wombat-e2e.test.ts +264 -0
- package/src/dex/wombat/wombat-integration.test.ts +258 -0
- package/src/dex/wombat/wombat-pool.ts +231 -0
- package/src/dex/wombat/wombat-quoter.ts +269 -0
- package/src/dex/wombat/wombat.ts +426 -0
- package/tests/constants-e2e.ts +20 -4
- package/tests/utils-e2e.ts +2 -1
- package/build/abi/algebra/AlgebraPool.abi.json +0 -727
- package/build/abi/uniswap-v3/UniswapV3Quoter.abi.json +0 -193
- package/build/dex/algebra/algebra-pool.d.ts +0 -46
- package/build/dex/algebra/algebra-pool.js +0 -336
- package/build/dex/algebra/algebra-pool.js.map +0 -1
- package/build/dex/quickswap/camelot-v3.d.ts +0 -6
- package/build/dex/quickswap/camelot-v3.js +0 -19
- package/build/dex/quickswap/camelot-v3.js.map +0 -1
- package/build/dex/quickswap/zyberswap-v3.d.ts +0 -6
- package/build/dex/quickswap/zyberswap-v3.js +0 -19
- 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
|
+
}
|