curvance 5.1.9 → 5.2.1
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/README.md +264 -67
- package/dist/abis/OptimizerReader.json +292 -1
- package/dist/abis/OptimizerZapper.json +177 -0
- package/dist/chains/arbitrum.d.ts.map +1 -1
- package/dist/chains/arbitrum.js +14 -1
- package/dist/chains/arbitrum.js.map +1 -1
- package/dist/chains/index.d.ts +19 -0
- package/dist/chains/index.d.ts.map +1 -1
- package/dist/chains/index.js.map +1 -1
- package/dist/chains/monad.d.ts.map +1 -1
- package/dist/chains/monad.js +25 -2
- package/dist/chains/monad.js.map +1 -1
- package/dist/chains/services.d.ts +8 -0
- package/dist/chains/services.d.ts.map +1 -0
- package/dist/chains/services.js +9 -0
- package/dist/chains/services.js.map +1 -0
- package/dist/classes/Api.d.ts +7 -2
- package/dist/classes/Api.d.ts.map +1 -1
- package/dist/classes/Api.js +60 -24
- package/dist/classes/Api.js.map +1 -1
- package/dist/classes/BorrowableCToken.d.ts.map +1 -1
- package/dist/classes/BorrowableCToken.js +7 -2
- package/dist/classes/BorrowableCToken.js.map +1 -1
- package/dist/classes/CToken.d.ts +28 -15
- package/dist/classes/CToken.d.ts.map +1 -1
- package/dist/classes/CToken.js +217 -85
- package/dist/classes/CToken.js.map +1 -1
- package/dist/classes/DexAggregators/IDexAgg.d.ts +8 -0
- package/dist/classes/DexAggregators/IDexAgg.d.ts.map +1 -1
- package/dist/classes/DexAggregators/KyberSwap.d.ts +5 -2
- package/dist/classes/DexAggregators/KyberSwap.d.ts.map +1 -1
- package/dist/classes/DexAggregators/KyberSwap.js +41 -19
- package/dist/classes/DexAggregators/KyberSwap.js.map +1 -1
- package/dist/classes/DexAggregators/MultiDexAgg.d.ts +7 -4
- package/dist/classes/DexAggregators/MultiDexAgg.d.ts.map +1 -1
- package/dist/classes/DexAggregators/MultiDexAgg.js +62 -16
- package/dist/classes/DexAggregators/MultiDexAgg.js.map +1 -1
- package/dist/classes/DexAggregators/helpers.d.ts +1 -1
- package/dist/classes/DexAggregators/helpers.d.ts.map +1 -1
- package/dist/classes/DexAggregators/helpers.js +1 -1
- package/dist/classes/DexAggregators/helpers.js.map +1 -1
- package/dist/classes/DexAggregators/index.d.ts +0 -1
- package/dist/classes/DexAggregators/index.d.ts.map +1 -1
- package/dist/classes/DexAggregators/index.js +0 -1
- package/dist/classes/DexAggregators/index.js.map +1 -1
- package/dist/classes/LendingOptimizer.d.ts +62 -2
- package/dist/classes/LendingOptimizer.d.ts.map +1 -1
- package/dist/classes/LendingOptimizer.js +251 -4
- package/dist/classes/LendingOptimizer.js.map +1 -1
- package/dist/classes/Market.d.ts +5 -0
- package/dist/classes/Market.d.ts.map +1 -1
- package/dist/classes/Market.js +129 -30
- package/dist/classes/Market.js.map +1 -1
- package/dist/classes/NativeToken.d.ts +5 -2
- package/dist/classes/NativeToken.d.ts.map +1 -1
- package/dist/classes/NativeToken.js +5 -5
- package/dist/classes/NativeToken.js.map +1 -1
- package/dist/classes/OptimizerReader.d.ts +44 -4
- package/dist/classes/OptimizerReader.d.ts.map +1 -1
- package/dist/classes/OptimizerReader.js +133 -62
- package/dist/classes/OptimizerReader.js.map +1 -1
- package/dist/classes/OptimizerZapper.d.ts +24 -0
- package/dist/classes/OptimizerZapper.d.ts.map +1 -0
- package/dist/classes/OptimizerZapper.js +117 -0
- package/dist/classes/OptimizerZapper.js.map +1 -0
- package/dist/classes/PositionManager.d.ts +1 -0
- package/dist/classes/PositionManager.d.ts.map +1 -1
- package/dist/classes/PositionManager.js +25 -0
- package/dist/classes/PositionManager.js.map +1 -1
- package/dist/classes/ProtocolReader.d.ts +3 -0
- package/dist/classes/ProtocolReader.d.ts.map +1 -1
- package/dist/classes/ProtocolReader.js +34 -0
- package/dist/classes/ProtocolReader.js.map +1 -1
- package/dist/classes/Zapper.d.ts +4 -1
- package/dist/classes/Zapper.d.ts.map +1 -1
- package/dist/classes/Zapper.js +34 -9
- package/dist/classes/Zapper.js.map +1 -1
- package/dist/classes/index.d.ts +2 -0
- package/dist/classes/index.d.ts.map +1 -1
- package/dist/classes/index.js +2 -0
- package/dist/classes/index.js.map +1 -1
- package/dist/contracts/index.d.ts +249 -248
- package/dist/contracts/index.d.ts.map +1 -1
- package/dist/contracts/index.js +3 -2
- package/dist/contracts/index.js.map +1 -1
- package/dist/contracts/monad-mainnet.json +3 -2
- package/dist/feePolicy.d.ts +29 -26
- package/dist/feePolicy.d.ts.map +1 -1
- package/dist/feePolicy.js +43 -34
- package/dist/feePolicy.js.map +1 -1
- package/dist/format/index.d.ts +5 -0
- package/dist/format/index.d.ts.map +1 -1
- package/dist/format/index.js +28 -0
- package/dist/format/index.js.map +1 -1
- package/dist/helpers.d.ts +248 -248
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +5 -5
- package/dist/helpers.js.map +1 -1
- package/dist/immutability.d.ts +6 -0
- package/dist/immutability.d.ts.map +1 -0
- package/dist/immutability.js +25 -0
- package/dist/immutability.js.map +1 -0
- package/dist/integrations/merkl.d.ts +9 -2
- package/dist/integrations/merkl.d.ts.map +1 -1
- package/dist/integrations/merkl.js +219 -11
- package/dist/integrations/merkl.js.map +1 -1
- package/dist/integrations/snapshot.d.ts +3 -0
- package/dist/integrations/snapshot.d.ts.map +1 -1
- package/dist/integrations/snapshot.js +47 -3
- package/dist/integrations/snapshot.js.map +1 -1
- package/dist/setup.d.ts +13 -3
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +101 -7
- package/dist/setup.js.map +1 -1
- package/package.json +7 -4
package/dist/classes/CToken.js
CHANGED
|
@@ -15,9 +15,6 @@ const PositionManager_1 = require("./PositionManager");
|
|
|
15
15
|
const NativeToken_1 = require("./NativeToken");
|
|
16
16
|
const ERC4626_1 = require("./ERC4626");
|
|
17
17
|
const FormatConverter_1 = __importDefault(require("./FormatConverter"));
|
|
18
|
-
const EXCLUDED_ZAP_SYMBOLS = new Set([
|
|
19
|
-
'eBTC', 'vUSD', 'ezETH', 'YZM', 'wsrUSD', 'sAUSD',
|
|
20
|
-
]);
|
|
21
18
|
const EXECUTION_DEBT_BUFFER_TIME = 100n;
|
|
22
19
|
function ceilDiv(numerator, denominator) {
|
|
23
20
|
if (denominator <= 0n) {
|
|
@@ -182,27 +179,44 @@ class CToken extends Calldata_1.Calldata {
|
|
|
182
179
|
this.contract = (0, helpers_1.contractSetup)(this.provider, address, BaseCToken_json_1.default);
|
|
183
180
|
this.cache = cache;
|
|
184
181
|
this.market = market;
|
|
185
|
-
const chainSettings = this.
|
|
182
|
+
const chainSettings = this.currentChainAssets;
|
|
186
183
|
const assetAddr = this.asset.address.toLowerCase();
|
|
187
184
|
this.isNativeVault = chainSettings.native_vaults.some(vault => vault.contract.toLowerCase() == assetAddr);
|
|
188
185
|
this.isVault = chainSettings.vaults.some(vault => vault.contract.toLowerCase() == assetAddr);
|
|
189
186
|
this.isWrappedNative = chainSettings.wrapped_native.toLowerCase() == assetAddr;
|
|
190
|
-
|
|
187
|
+
this.refreshRouteCapabilities();
|
|
188
|
+
}
|
|
189
|
+
refreshRouteCapabilities() {
|
|
190
|
+
this.zapTypes = [];
|
|
191
|
+
this.leverageTypes = [];
|
|
192
|
+
if (this.isZapSymbolExcluded(this.asset.symbol)) {
|
|
191
193
|
return;
|
|
192
194
|
}
|
|
193
|
-
|
|
195
|
+
const zappers = this.setup.contracts.zappers;
|
|
196
|
+
const nativeVaultZapper = zappers?.nativeVaultZapper;
|
|
197
|
+
const vaultZapper = zappers?.vaultZapper;
|
|
198
|
+
const simpleZapper = zappers?.simpleZapper;
|
|
199
|
+
const supportsNativeVaultZaps = typeof nativeVaultZapper === 'string'
|
|
200
|
+
&& nativeVaultZapper.toLowerCase() !== helpers_1.EMPTY_ADDRESS.toLowerCase();
|
|
201
|
+
const supportsVaultZaps = typeof vaultZapper === 'string'
|
|
202
|
+
&& vaultZapper.toLowerCase() !== helpers_1.EMPTY_ADDRESS.toLowerCase();
|
|
203
|
+
const supportsSimpleZaps = typeof simpleZapper === 'string'
|
|
204
|
+
&& simpleZapper.toLowerCase() !== helpers_1.EMPTY_ADDRESS.toLowerCase()
|
|
205
|
+
&& this.hasExecutableDexRoute;
|
|
206
|
+
if (supportsNativeVaultZaps && this.isNativeVault)
|
|
194
207
|
this.zapTypes.push('native-vault');
|
|
195
208
|
if ("nativeVaultPositionManager" in this.market.plugins && this.isNativeVault)
|
|
196
209
|
this.leverageTypes.push('native-vault');
|
|
197
|
-
if (this.isWrappedNative)
|
|
210
|
+
if (supportsSimpleZaps && this.isWrappedNative)
|
|
198
211
|
this.zapTypes.push('native-simple');
|
|
199
|
-
if (this.isVault)
|
|
212
|
+
if (supportsVaultZaps && this.isVault)
|
|
200
213
|
this.zapTypes.push('vault');
|
|
201
214
|
if ("vaultPositionManager" in this.market.plugins && this.isVault)
|
|
202
215
|
this.leverageTypes.push('vault');
|
|
203
|
-
if ("simplePositionManager" in this.market.plugins)
|
|
216
|
+
if (supportsSimpleZaps && "simplePositionManager" in this.market.plugins)
|
|
204
217
|
this.leverageTypes.push('simple');
|
|
205
|
-
|
|
218
|
+
if (supportsSimpleZaps)
|
|
219
|
+
this.zapTypes.push('simple');
|
|
206
220
|
}
|
|
207
221
|
getUserCacheFreshness() {
|
|
208
222
|
if (this.userCacheFreshness == undefined) {
|
|
@@ -231,7 +245,28 @@ class CToken extends Calldata_1.Calldata {
|
|
|
231
245
|
}
|
|
232
246
|
get setup() { return this.market.setup; }
|
|
233
247
|
get currentChain() { return this.setup.chain; }
|
|
234
|
-
get
|
|
248
|
+
get currentChainAssets() { return this.setup.assets; }
|
|
249
|
+
isZapSymbolExcluded(symbol) {
|
|
250
|
+
if (symbol == null) {
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
const excludedSymbols = this.setup.assets
|
|
254
|
+
?.excluded_zap_symbols ?? [];
|
|
255
|
+
return excludedSymbols.some((excluded) => excluded.toLowerCase() === symbol.toLowerCase());
|
|
256
|
+
}
|
|
257
|
+
get boundDexAgg() { return this.market.dexAgg ?? null; }
|
|
258
|
+
get currentDexAgg() {
|
|
259
|
+
const dexAgg = this.boundDexAgg;
|
|
260
|
+
if (dexAgg == null) {
|
|
261
|
+
throw new Error(`DEX aggregator is not bound for token ${this.address} on ${this.currentChain}. ` +
|
|
262
|
+
`Use setupChain(...) result markets or attach a setup-bound dexAgg before route discovery/execution.`);
|
|
263
|
+
}
|
|
264
|
+
return dexAgg;
|
|
265
|
+
}
|
|
266
|
+
get hasExecutableDexRoute() {
|
|
267
|
+
const router = this.boundDexAgg?.router;
|
|
268
|
+
return typeof router === "string" && router.toLowerCase() !== helpers_1.EMPTY_ADDRESS.toLowerCase();
|
|
269
|
+
}
|
|
235
270
|
requireSigner() { return (0, helpers_1.requireSigner)(this.signer); }
|
|
236
271
|
getAccountOrThrow(account = null) {
|
|
237
272
|
return (0, helpers_1.requireAccount)(account ?? this.account, this.signer);
|
|
@@ -239,6 +274,31 @@ class CToken extends Calldata_1.Calldata {
|
|
|
239
274
|
getWriteContract() {
|
|
240
275
|
return (0, helpers_1.contractSetup)(this.requireSigner(), this.address, BaseCToken_json_1.default);
|
|
241
276
|
}
|
|
277
|
+
assertBorrowTokenBelongsToMarket(borrow, label = "Borrow") {
|
|
278
|
+
if (borrow == null) {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
const borrowMarket = borrow.market;
|
|
282
|
+
if (borrowMarket == undefined) {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
if (borrowMarket === this.market) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
const borrowChain = borrowMarket.setup?.chain;
|
|
289
|
+
const collateralChain = this.market.setup?.chain;
|
|
290
|
+
const sameMarket = borrowMarket.address?.toLowerCase() === this.market.address.toLowerCase();
|
|
291
|
+
const sameChain = borrowChain != null && collateralChain != null && borrowChain === collateralChain;
|
|
292
|
+
const borrowReaderKey = borrowMarket.reader?.batchKey ?? null;
|
|
293
|
+
const collateralReaderKey = this.market.reader?.batchKey ?? null;
|
|
294
|
+
const sameReaderDeployment = borrowMarket.reader === this.market.reader ||
|
|
295
|
+
(borrowReaderKey != null && borrowReaderKey === collateralReaderKey);
|
|
296
|
+
if (!sameMarket || !sameChain || !sameReaderDeployment) {
|
|
297
|
+
throw new Error(`${label} token ${borrow.address} belongs to market ${borrowMarket.address} ` +
|
|
298
|
+
`on ${borrowChain ?? "unknown"}, not market ${this.market.address} on ${collateralChain ?? "unknown"} ` +
|
|
299
|
+
`with the same reader deployment.`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
242
302
|
getZapType(zap) {
|
|
243
303
|
return typeof zap === "object" ? zap.type : zap;
|
|
244
304
|
}
|
|
@@ -264,7 +324,7 @@ class CToken extends Calldata_1.Calldata {
|
|
|
264
324
|
if (zap.inputToken.toLowerCase() === helpers_1.NATIVE_ADDRESS.toLowerCase()) {
|
|
265
325
|
return 18n;
|
|
266
326
|
}
|
|
267
|
-
const inputErc20 = new ERC20_1.ERC20(this.provider, zap.inputToken, undefined,
|
|
327
|
+
const inputErc20 = new ERC20_1.ERC20(this.provider, zap.inputToken, undefined, this.setup.contracts.OracleManager, this.signer);
|
|
268
328
|
return inputErc20.decimals ?? await inputErc20.contract.decimals();
|
|
269
329
|
}
|
|
270
330
|
}
|
|
@@ -272,8 +332,9 @@ class CToken extends Calldata_1.Calldata {
|
|
|
272
332
|
return FormatConverter_1.default.decimalToBigInt(amount, await this.getZapInputDecimals(zap));
|
|
273
333
|
}
|
|
274
334
|
async assertVaultLeverageBorrowAssetSupported(borrow, type) {
|
|
335
|
+
this.assertBorrowTokenBelongsToMarket(borrow);
|
|
275
336
|
const expectedAsset = type === "native-vault"
|
|
276
|
-
? this.
|
|
337
|
+
? this.currentChainAssets.wrapped_native
|
|
277
338
|
: await this.getVaultAsset(false);
|
|
278
339
|
const actualAsset = borrow.asset.address;
|
|
279
340
|
if (actualAsset.toLowerCase() === expectedAsset.toLowerCase()) {
|
|
@@ -382,11 +443,15 @@ class CToken extends Calldata_1.Calldata {
|
|
|
382
443
|
const shares = this.totalSupply === 0n || this.totalAssets === 0n
|
|
383
444
|
? assets
|
|
384
445
|
: (assets * this.totalSupply) / this.totalAssets;
|
|
385
|
-
return bufferBps > 0n ? shares * (
|
|
386
|
-
}
|
|
387
|
-
getMarketLeverageState() {
|
|
388
|
-
const currentCollateralInUsd =
|
|
389
|
-
|
|
446
|
+
return bufferBps > 0n ? shares * (helpers_1.BPS - bufferBps) / helpers_1.BPS : shares;
|
|
447
|
+
}
|
|
448
|
+
getMarketLeverageState(leverageState) {
|
|
449
|
+
const currentCollateralInUsd = leverageState?.collateralUsd != null
|
|
450
|
+
? (0, helpers_1.toDecimal)(leverageState.collateralUsd, 18n)
|
|
451
|
+
: this.market.userCollateral;
|
|
452
|
+
const currentDebt = leverageState?.debtUsd != null
|
|
453
|
+
? (0, helpers_1.toDecimal)(leverageState.debtUsd, 18n)
|
|
454
|
+
: this.market.userDebt;
|
|
390
455
|
const equity = currentCollateralInUsd.sub(currentDebt);
|
|
391
456
|
if (currentCollateralInUsd.lte(0) || equity.lte(0)) {
|
|
392
457
|
return {
|
|
@@ -523,7 +588,7 @@ class CToken extends Calldata_1.Calldata {
|
|
|
523
588
|
// into Curvance shares. Buffer the inner preview so exchange-rate drift
|
|
524
589
|
// between quote time and inclusion cannot trip the outer expectedShares
|
|
525
590
|
// check on otherwise-valid deposits/leverage/zaps.
|
|
526
|
-
const vaultShares = vaultSharesRaw * (
|
|
591
|
+
const vaultShares = vaultSharesRaw * (helpers_1.BPS - exports.LEVERAGE.SHARES_BUFFER_BPS) / helpers_1.BPS;
|
|
527
592
|
return this.convertToShares(vaultShares);
|
|
528
593
|
}
|
|
529
594
|
getAsset(asErc20) {
|
|
@@ -586,7 +651,7 @@ class CToken extends Calldata_1.Calldata {
|
|
|
586
651
|
if (zap_contract == null) {
|
|
587
652
|
return null;
|
|
588
653
|
}
|
|
589
|
-
return new Zapper_1.Zapper(zap_contract, signer, type, this.setup);
|
|
654
|
+
return new Zapper_1.Zapper(zap_contract, signer, type, this.setup, this.currentDexAgg);
|
|
590
655
|
}
|
|
591
656
|
async isZapAssetApproved(instructions, amount) {
|
|
592
657
|
if (instructions == 'none') {
|
|
@@ -653,7 +718,7 @@ class CToken extends Calldata_1.Calldata {
|
|
|
653
718
|
}
|
|
654
719
|
async getAllowance(check_contract, underlying = true) {
|
|
655
720
|
const signer = this.requireSigner();
|
|
656
|
-
const erc20 = new ERC20_1.ERC20(this.provider, underlying ? this.asset.address : this.address, undefined,
|
|
721
|
+
const erc20 = new ERC20_1.ERC20(this.provider, underlying ? this.asset.address : this.address, undefined, this.setup.contracts.OracleManager, this.signer);
|
|
657
722
|
const allowance = await erc20.allowance(signer.address, check_contract);
|
|
658
723
|
return allowance;
|
|
659
724
|
}
|
|
@@ -663,12 +728,12 @@ class CToken extends Calldata_1.Calldata {
|
|
|
663
728
|
* @returns tx
|
|
664
729
|
*/
|
|
665
730
|
async approveUnderlying(amount = null, target = null) {
|
|
666
|
-
const erc20 = new ERC20_1.ERC20(this.provider, this.asset.address, undefined,
|
|
731
|
+
const erc20 = new ERC20_1.ERC20(this.provider, this.asset.address, undefined, this.setup.contracts.OracleManager, this.signer);
|
|
667
732
|
const tx = await erc20.approve(target ? target : this.address, amount);
|
|
668
733
|
return tx;
|
|
669
734
|
}
|
|
670
735
|
async approve(amount = null, spender) {
|
|
671
|
-
const erc20 = new ERC20_1.ERC20(this.provider, this.address, undefined,
|
|
736
|
+
const erc20 = new ERC20_1.ERC20(this.provider, this.address, undefined, this.setup.contracts.OracleManager, this.signer);
|
|
672
737
|
const tx = await erc20.approve(spender, amount);
|
|
673
738
|
return tx;
|
|
674
739
|
}
|
|
@@ -739,7 +804,8 @@ class CToken extends Calldata_1.Calldata {
|
|
|
739
804
|
}
|
|
740
805
|
async transfer(receiver, amount) {
|
|
741
806
|
const shares = this.convertTokenInputToShares(amount);
|
|
742
|
-
|
|
807
|
+
const calldata = this.getCallData("transfer", [receiver, shares]);
|
|
808
|
+
return this.oracleRoute(calldata);
|
|
743
809
|
}
|
|
744
810
|
async redeemCollateral(amount, receiver = null, owner = null) {
|
|
745
811
|
const signer = this.requireSigner();
|
|
@@ -773,21 +839,47 @@ class CToken extends Calldata_1.Calldata {
|
|
|
773
839
|
if (max_shares <= 0n) {
|
|
774
840
|
throw new Error("No cToken shares available to post as collateral.");
|
|
775
841
|
}
|
|
842
|
+
this.assertCollateralCapacity(max_shares);
|
|
776
843
|
const calldata = this.getCallData("postCollateral", [max_shares]);
|
|
777
844
|
const tx = await this.oracleRoute(calldata);
|
|
778
845
|
// Reload collateral state after execution
|
|
779
846
|
await this.fetchUserCollateral();
|
|
780
847
|
return tx;
|
|
781
848
|
}
|
|
849
|
+
assertCollateralCapacity(shares) {
|
|
850
|
+
const collateralCapError = "There is not enough collateral left in this tokens collateral cap for this deposit.";
|
|
851
|
+
const remainingCollateral = this.getRemainingCollateral(false);
|
|
852
|
+
if (remainingCollateral <= 0n)
|
|
853
|
+
throw new Error(collateralCapError);
|
|
854
|
+
if (shares != undefined && shares > remainingCollateral) {
|
|
855
|
+
throw new Error(collateralCapError);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
getZapperExpectedShares(zapper, calldata) {
|
|
859
|
+
if (zapper == null)
|
|
860
|
+
return undefined;
|
|
861
|
+
let expectedShares;
|
|
862
|
+
try {
|
|
863
|
+
const decoded = zapper.contract.interface.decodeFunctionData("swapAndDeposit", calldata);
|
|
864
|
+
expectedShares = BigInt(decoded[3]);
|
|
865
|
+
}
|
|
866
|
+
catch {
|
|
867
|
+
return undefined;
|
|
868
|
+
}
|
|
869
|
+
if (expectedShares <= 0n) {
|
|
870
|
+
throw new Error("Zap expected shares must be greater than zero.");
|
|
871
|
+
}
|
|
872
|
+
return expectedShares;
|
|
873
|
+
}
|
|
782
874
|
async getZapBalance(zap) {
|
|
783
875
|
const signer = this.requireSigner();
|
|
784
876
|
let asset;
|
|
785
877
|
if (typeof zap === 'object') {
|
|
786
878
|
if (zap.type === 'native-vault' || zap.type === 'native-simple' || zap.inputToken.toLowerCase() === helpers_1.NATIVE_ADDRESS.toLowerCase()) {
|
|
787
|
-
asset = new NativeToken_1.NativeToken(this.currentChain, this.provider, this.setup.contracts.OracleManager, this.signer, this.account);
|
|
879
|
+
asset = new NativeToken_1.NativeToken(this.currentChain, this.provider, this.setup.contracts.OracleManager, this.signer, this.account, this.currentChainAssets);
|
|
788
880
|
}
|
|
789
881
|
else {
|
|
790
|
-
asset = new ERC20_1.ERC20(this.provider, zap.inputToken, undefined,
|
|
882
|
+
asset = new ERC20_1.ERC20(this.provider, zap.inputToken, undefined, this.setup.contracts.OracleManager, this.signer);
|
|
791
883
|
}
|
|
792
884
|
}
|
|
793
885
|
else {
|
|
@@ -799,10 +891,10 @@ class CToken extends Calldata_1.Calldata {
|
|
|
799
891
|
asset = await this.getVaultAsset(true);
|
|
800
892
|
break;
|
|
801
893
|
case 'native-vault':
|
|
802
|
-
asset = new NativeToken_1.NativeToken(this.currentChain, this.provider, this.setup.contracts.OracleManager, this.signer, this.account);
|
|
894
|
+
asset = new NativeToken_1.NativeToken(this.currentChain, this.provider, this.setup.contracts.OracleManager, this.signer, this.account, this.currentChainAssets);
|
|
803
895
|
break;
|
|
804
896
|
case 'native-simple':
|
|
805
|
-
asset = new NativeToken_1.NativeToken(this.currentChain, this.provider, this.setup.contracts.OracleManager, this.signer, this.account);
|
|
897
|
+
asset = new NativeToken_1.NativeToken(this.currentChain, this.provider, this.setup.contracts.OracleManager, this.signer, this.account, this.currentChainAssets);
|
|
806
898
|
break;
|
|
807
899
|
default: throw new Error("Unsupported zap type for balance fetch");
|
|
808
900
|
}
|
|
@@ -881,7 +973,7 @@ class CToken extends Calldata_1.Calldata {
|
|
|
881
973
|
}
|
|
882
974
|
async convertToShares(assets, bufferBps = exports.LEVERAGE.SHARES_BUFFER_BPS) {
|
|
883
975
|
const shares = await this.contract.convertToShares(assets);
|
|
884
|
-
return bufferBps > 0n ? shares * (
|
|
976
|
+
return bufferBps > 0n ? shares * (helpers_1.BPS - bufferBps) / helpers_1.BPS : shares;
|
|
885
977
|
}
|
|
886
978
|
async maxRedemption(in_shares = false, bufferTime = 0n, breakdown = false) {
|
|
887
979
|
const data = await this.market.reader.maxRedemptionOf(this.getAccountOrThrow(), this, bufferTime);
|
|
@@ -910,14 +1002,14 @@ class CToken extends Calldata_1.Calldata {
|
|
|
910
1002
|
let tokens_exclude = [this.asset.address.toLocaleLowerCase()];
|
|
911
1003
|
if (this.zapTypes.includes('native-vault')) {
|
|
912
1004
|
tokens.push({
|
|
913
|
-
interface: new NativeToken_1.NativeToken(this.currentChain, this.provider, this.setup.contracts.OracleManager, this.signer, this.account),
|
|
1005
|
+
interface: new NativeToken_1.NativeToken(this.currentChain, this.provider, this.setup.contracts.OracleManager, this.signer, this.account, this.currentChainAssets),
|
|
914
1006
|
type: 'native-vault'
|
|
915
1007
|
});
|
|
916
1008
|
tokens_exclude.push(helpers_1.EMPTY_ADDRESS.toLowerCase(), helpers_1.NATIVE_ADDRESS.toLowerCase());
|
|
917
1009
|
}
|
|
918
1010
|
if (this.zapTypes.includes('native-simple')) {
|
|
919
1011
|
tokens.push({
|
|
920
|
-
interface: new NativeToken_1.NativeToken(this.currentChain, this.provider, this.setup.contracts.OracleManager, this.signer, this.account),
|
|
1012
|
+
interface: new NativeToken_1.NativeToken(this.currentChain, this.provider, this.setup.contracts.OracleManager, this.signer, this.account, this.currentChainAssets),
|
|
921
1013
|
type: 'native-simple'
|
|
922
1014
|
});
|
|
923
1015
|
if (!this.zapTypes.includes('native-vault')) {
|
|
@@ -932,20 +1024,20 @@ class CToken extends Calldata_1.Calldata {
|
|
|
932
1024
|
});
|
|
933
1025
|
tokens_exclude.push(vault_asset.address.toLocaleLowerCase());
|
|
934
1026
|
}
|
|
935
|
-
if (this.zapTypes.includes('simple') && this.
|
|
936
|
-
let dexAggSearch = await this.
|
|
1027
|
+
if (this.zapTypes.includes('simple') && this.hasExecutableDexRoute) {
|
|
1028
|
+
let dexAggSearch = await this.currentDexAgg.getAvailableTokens(this.provider, search, this.account);
|
|
937
1029
|
tokens = tokens.concat(dexAggSearch.filter(token => !tokens_exclude.includes(token.interface.address.toLocaleLowerCase())));
|
|
938
1030
|
// Add native MON as a zap option for any token with a simple zapper
|
|
939
1031
|
// (not just wrapped native). The simple zapper handles wrapping + swapping.
|
|
940
1032
|
if (!tokens_exclude.includes(helpers_1.NATIVE_ADDRESS.toLowerCase()) && !this.isWrappedNative) {
|
|
941
1033
|
tokens.push({
|
|
942
|
-
interface: new NativeToken_1.NativeToken(this.currentChain, this.provider, this.setup.contracts.OracleManager, this.signer, this.account),
|
|
1034
|
+
interface: new NativeToken_1.NativeToken(this.currentChain, this.provider, this.setup.contracts.OracleManager, this.signer, this.account, this.currentChainAssets),
|
|
943
1035
|
type: 'simple'
|
|
944
1036
|
});
|
|
945
1037
|
tokens_exclude.push(helpers_1.NATIVE_ADDRESS.toLowerCase());
|
|
946
1038
|
}
|
|
947
1039
|
}
|
|
948
|
-
tokens = tokens.filter(token => token.type === 'none' || !
|
|
1040
|
+
tokens = tokens.filter(token => token.type === 'none' || !this.isZapSymbolExcluded(token.interface.symbol));
|
|
949
1041
|
if (search) {
|
|
950
1042
|
const lowerSearch = search.toLowerCase();
|
|
951
1043
|
tokens = tokens.filter(token => (token.interface.name ?? '').toLowerCase().includes(lowerSearch) ||
|
|
@@ -961,23 +1053,22 @@ class CToken extends Calldata_1.Calldata {
|
|
|
961
1053
|
* Single-RPC snapshot of fresh position state for leverage operations.
|
|
962
1054
|
* Calls ProtocolReader.getLeverageSnapshot which internally uses
|
|
963
1055
|
* hypotheticalLiquidityOf for aggregate position + fresh oracle prices
|
|
964
|
-
* + projected debt balance. Updates
|
|
965
|
-
*
|
|
1056
|
+
* + projected debt balance. Updates only token price/share caches; leverage
|
|
1057
|
+
* previews consume the returned aggregate values explicitly so failed or
|
|
1058
|
+
* simulated plans cannot leave market user totals ahead of token user rows.
|
|
966
1059
|
*
|
|
967
1060
|
* Returns the snapshot for direct use where needed (e.g. debtTokenBalance
|
|
968
1061
|
* for full deleverage swap sizing).
|
|
969
1062
|
*/
|
|
970
1063
|
async _getLeverageSnapshot(borrow) {
|
|
1064
|
+
this.assertBorrowTokenBelongsToMarket(borrow);
|
|
971
1065
|
const snapshot = await this.market.reader.getLeverageSnapshot(this.getAccountOrThrow(), this.address, borrow.address, 120n);
|
|
972
1066
|
if (snapshot.oracleError) {
|
|
973
1067
|
throw new Error(`Oracle error fetching leverage snapshot for ${this.symbol}/${borrow.symbol}`);
|
|
974
1068
|
}
|
|
975
|
-
// Update cache so preview functions read fresh values
|
|
976
1069
|
this.cache.assetPrice = snapshot.collateralAssetPrice;
|
|
977
1070
|
this.cache.sharePrice = snapshot.sharePrice;
|
|
978
1071
|
borrow.cache.assetPrice = snapshot.debtAssetPrice;
|
|
979
|
-
this.market.cache.user.collateral = snapshot.collateralUsd;
|
|
980
|
-
this.market.cache.user.debt = snapshot.debtUsd;
|
|
981
1072
|
return snapshot;
|
|
982
1073
|
}
|
|
983
1074
|
/**
|
|
@@ -1008,11 +1099,13 @@ class CToken extends Calldata_1.Calldata {
|
|
|
1008
1099
|
return slippage + exports.LEVERAGE.LEVERAGE_UP_BUFFER_BPS;
|
|
1009
1100
|
}
|
|
1010
1101
|
assertSimpleLeverageSwapAssetsDiffer(borrow) {
|
|
1102
|
+
this.assertBorrowTokenBelongsToMarket(borrow);
|
|
1011
1103
|
if (borrow.asset.address.toLowerCase() === this.asset.address.toLowerCase()) {
|
|
1012
1104
|
throw new Error("Simple leverage requires distinct collateral and borrow assets.");
|
|
1013
1105
|
}
|
|
1014
1106
|
}
|
|
1015
1107
|
async assertLeverageBorrowCapacity(borrow, borrowAssets) {
|
|
1108
|
+
this.assertBorrowTokenBelongsToMarket(borrow);
|
|
1016
1109
|
if (borrowAssets === 0n) {
|
|
1017
1110
|
return;
|
|
1018
1111
|
}
|
|
@@ -1029,6 +1122,7 @@ class CToken extends Calldata_1.Calldata {
|
|
|
1029
1122
|
}
|
|
1030
1123
|
}
|
|
1031
1124
|
assertSelectedBorrowDebtCanDeleverage(borrow, selectedDebtAssets, requiredDebtReductionUsd) {
|
|
1125
|
+
this.assertBorrowTokenBelongsToMarket(borrow);
|
|
1032
1126
|
if (requiredDebtReductionUsd.lte(0)) {
|
|
1033
1127
|
return;
|
|
1034
1128
|
}
|
|
@@ -1050,15 +1144,16 @@ class CToken extends Calldata_1.Calldata {
|
|
|
1050
1144
|
return (0, decimal_js_1.default)(1);
|
|
1051
1145
|
return collateralAfterDeposit.div(equityAfterDeposit);
|
|
1052
1146
|
}
|
|
1053
|
-
resolveLeverageUpPreview({ operation, targetLeverage, borrow, depositAssets = 0n, positionManagerType, }) {
|
|
1054
|
-
|
|
1055
|
-
const
|
|
1056
|
-
const
|
|
1147
|
+
resolveLeverageUpPreview({ operation, targetLeverage, borrow, depositAssets = 0n, positionManagerType, leverageState, }) {
|
|
1148
|
+
this.assertBorrowTokenBelongsToMarket(borrow);
|
|
1149
|
+
const currentState = this.getMarketLeverageState(leverageState);
|
|
1150
|
+
const currentLeverage = currentState.currentLeverage ?? (0, decimal_js_1.default)(1);
|
|
1151
|
+
const currentCollateralInUsd = currentState.currentCollateralInUsd;
|
|
1057
1152
|
const depositInAssets = FormatConverter_1.default.bigIntToDecimal(depositAssets, this.asset.decimals);
|
|
1058
1153
|
const depositInUsd = depositAssets > 0n
|
|
1059
1154
|
? this.convertTokensToUsd(depositAssets, true)
|
|
1060
1155
|
: (0, decimal_js_1.default)(0);
|
|
1061
|
-
const currentDebt =
|
|
1156
|
+
const currentDebt = currentState.currentDebt;
|
|
1062
1157
|
const effectiveCurrentLeverage = depositAssets > 0n
|
|
1063
1158
|
? this.computePostDepositNaturalLeverage(currentCollateralInUsd, currentDebt, depositInUsd)
|
|
1064
1159
|
: currentLeverage;
|
|
@@ -1099,7 +1194,7 @@ class CToken extends Calldata_1.Calldata {
|
|
|
1099
1194
|
targetLeverage: resolvedTargetLeverage,
|
|
1100
1195
|
})
|
|
1101
1196
|
: 0n;
|
|
1102
|
-
const feeAssets = borrowAmount.mul((0, decimal_js_1.default)(Number(feeBps))).div(
|
|
1197
|
+
const feeAssets = borrowAmount.mul((0, decimal_js_1.default)(Number(feeBps))).div(helpers_1.BPS_DECIMAL);
|
|
1103
1198
|
const feeUsd = feeAssets.mul(borrowPrice);
|
|
1104
1199
|
const collateralIncreaseFromBorrow = decimal_js_1.default.max(debtIncrease.sub(feeUsd), (0, decimal_js_1.default)(0));
|
|
1105
1200
|
const collateralIncrease = depositInUsd.add(collateralIncreaseFromBorrow);
|
|
@@ -1124,36 +1219,39 @@ class CToken extends Calldata_1.Calldata {
|
|
|
1124
1219
|
feeUsd,
|
|
1125
1220
|
};
|
|
1126
1221
|
}
|
|
1127
|
-
previewDepositAndLeverage(newLeverage, borrow, depositAmount, positionManagerType) {
|
|
1222
|
+
previewDepositAndLeverage(newLeverage, borrow, depositAmount, positionManagerType, leverageState) {
|
|
1128
1223
|
return this.resolveLeverageUpPreview({
|
|
1129
1224
|
operation: 'deposit-and-leverage',
|
|
1130
1225
|
targetLeverage: newLeverage,
|
|
1131
1226
|
borrow,
|
|
1132
1227
|
depositAssets: depositAmount,
|
|
1133
1228
|
positionManagerType,
|
|
1229
|
+
leverageState,
|
|
1134
1230
|
});
|
|
1135
1231
|
}
|
|
1136
|
-
previewLeverageUp(newLeverage, borrow, depositAmount, positionManagerType) {
|
|
1232
|
+
previewLeverageUp(newLeverage, borrow, depositAmount, positionManagerType, leverageState) {
|
|
1137
1233
|
if ((depositAmount ?? 0n) > 0n) {
|
|
1138
|
-
return this.previewDepositAndLeverage(newLeverage, borrow, depositAmount, positionManagerType);
|
|
1234
|
+
return this.previewDepositAndLeverage(newLeverage, borrow, depositAmount, positionManagerType, leverageState);
|
|
1139
1235
|
}
|
|
1140
1236
|
return this.resolveLeverageUpPreview({
|
|
1141
1237
|
operation: 'leverage-up',
|
|
1142
1238
|
targetLeverage: newLeverage,
|
|
1143
1239
|
borrow,
|
|
1144
1240
|
positionManagerType,
|
|
1241
|
+
leverageState,
|
|
1145
1242
|
});
|
|
1146
1243
|
}
|
|
1147
|
-
previewLeverageDown(newLeverage, currentLeverage, borrow) {
|
|
1244
|
+
previewLeverageDown(newLeverage, currentLeverage, borrow, leverageState) {
|
|
1245
|
+
this.assertBorrowTokenBelongsToMarket(borrow);
|
|
1148
1246
|
if (newLeverage.gte(currentLeverage)) {
|
|
1149
1247
|
throw new Error("New leverage must be less than current leverage");
|
|
1150
1248
|
}
|
|
1151
1249
|
if (newLeverage.lt((0, decimal_js_1.default)(1))) {
|
|
1152
1250
|
throw new Error("New leverage must be at least 1");
|
|
1153
1251
|
}
|
|
1154
|
-
const
|
|
1155
|
-
const collateralInUsd =
|
|
1156
|
-
const currentDebt =
|
|
1252
|
+
const currentState = this.getMarketLeverageState(leverageState);
|
|
1253
|
+
const collateralInUsd = currentState.currentCollateralInUsd;
|
|
1254
|
+
const currentDebt = currentState.currentDebt;
|
|
1157
1255
|
const equity = collateralInUsd.sub(currentDebt);
|
|
1158
1256
|
if (equity.lte(0)) {
|
|
1159
1257
|
throw new Error("Position has no positive equity to deleverage.");
|
|
@@ -1178,7 +1276,7 @@ class CToken extends Calldata_1.Calldata {
|
|
|
1178
1276
|
currentLeverage,
|
|
1179
1277
|
targetLeverage: newLeverage,
|
|
1180
1278
|
}) : 0n;
|
|
1181
|
-
const feeUsd = collateralAssetReductionUsd.mul((0, decimal_js_1.default)(Number(feeBps))).div(
|
|
1279
|
+
const feeUsd = collateralAssetReductionUsd.mul((0, decimal_js_1.default)(Number(feeBps))).div(helpers_1.BPS_DECIMAL);
|
|
1182
1280
|
const feeAssets = this.getPrice(true).gt(0)
|
|
1183
1281
|
? feeUsd.div(this.getPrice(true))
|
|
1184
1282
|
: (0, decimal_js_1.default)(0);
|
|
@@ -1197,6 +1295,7 @@ class CToken extends Calldata_1.Calldata {
|
|
|
1197
1295
|
}
|
|
1198
1296
|
async leverageUp(borrow, newLeverage, type, slippage_ = (0, decimal_js_1.default)(0.05), simulate = false) {
|
|
1199
1297
|
try {
|
|
1298
|
+
this.assertBorrowTokenBelongsToMarket(borrow);
|
|
1200
1299
|
this.requireSigner();
|
|
1201
1300
|
const manager = this.getPositionManager(type);
|
|
1202
1301
|
if (type === 'vault' || type === 'native-vault') {
|
|
@@ -1206,8 +1305,8 @@ class CToken extends Calldata_1.Calldata {
|
|
|
1206
1305
|
this.assertSimpleLeverageSwapAssetsDiffer(borrow);
|
|
1207
1306
|
}
|
|
1208
1307
|
let calldata;
|
|
1209
|
-
await this._getLeverageSnapshot(borrow);
|
|
1210
|
-
const preview = this.previewLeverageUp(newLeverage, borrow, undefined, type);
|
|
1308
|
+
const snapshot = await this._getLeverageSnapshot(borrow);
|
|
1309
|
+
const preview = this.previewLeverageUp(newLeverage, borrow, undefined, type, snapshot);
|
|
1211
1310
|
const slippage = this._leverageUpSlippage(FormatConverter_1.default.percentageToBps(slippage_), preview.targetLeverage);
|
|
1212
1311
|
const { borrowAmount, borrowAssets, feeBps, targetLeverage } = preview;
|
|
1213
1312
|
if (borrowAssets === 0n) {
|
|
@@ -1223,7 +1322,7 @@ class CToken extends Calldata_1.Calldata {
|
|
|
1223
1322
|
switch (type) {
|
|
1224
1323
|
case 'simple': {
|
|
1225
1324
|
const feeReceiver = feeBps > 0n ? this.setup.feePolicy.feeReceiver : undefined;
|
|
1226
|
-
const { action, quote } = await this.
|
|
1325
|
+
const { action, quote } = await this.currentDexAgg.quoteAction(manager.address, borrow.asset.address, this.asset.address, borrowAssets, slippage, feeBps, feeReceiver);
|
|
1227
1326
|
// Fee-aware slippage expansion now lives inside KyberSwap.quoteAction
|
|
1228
1327
|
// so any caller inherits correct behavior. See KyberSwap.ts for the
|
|
1229
1328
|
// rationale. The fee still reduces swap output, which checkSlippage
|
|
@@ -1278,13 +1377,13 @@ class CToken extends Calldata_1.Calldata {
|
|
|
1278
1377
|
}
|
|
1279
1378
|
async leverageDown(borrowToken, currentLeverage, newLeverage, type, slippage_ = (0, decimal_js_1.default)(0.05), simulate = false) {
|
|
1280
1379
|
try {
|
|
1380
|
+
this.assertBorrowTokenBelongsToMarket(borrowToken);
|
|
1281
1381
|
if (newLeverage.gte(currentLeverage)) {
|
|
1282
1382
|
if (simulate)
|
|
1283
1383
|
return { success: false, error: "New leverage must be less than current leverage" };
|
|
1284
1384
|
throw new Error("New leverage must be less than current leverage");
|
|
1285
1385
|
}
|
|
1286
1386
|
this.requireSigner();
|
|
1287
|
-
const config = this.currentChainConfig;
|
|
1288
1387
|
const slippage = (0, helpers_1.toBps)(slippage_);
|
|
1289
1388
|
const manager = this.getPositionManager(type);
|
|
1290
1389
|
if (type === 'simple') {
|
|
@@ -1292,7 +1391,7 @@ class CToken extends Calldata_1.Calldata {
|
|
|
1292
1391
|
}
|
|
1293
1392
|
let calldata;
|
|
1294
1393
|
const snapshot = await this._getLeverageSnapshot(borrowToken);
|
|
1295
|
-
const preview = this.previewLeverageDown(newLeverage, currentLeverage);
|
|
1394
|
+
const preview = this.previewLeverageDown(newLeverage, currentLeverage, undefined, snapshot);
|
|
1296
1395
|
const { collateralAssetReduction } = preview;
|
|
1297
1396
|
const isFullDeleverage = newLeverage.equals(1);
|
|
1298
1397
|
const maxTokenCollateral = this.virtualConvertToAssets(this.readFreshUserCache("userCollateral", "executing leverage down"));
|
|
@@ -1337,7 +1436,7 @@ class CToken extends Calldata_1.Calldata {
|
|
|
1337
1436
|
// Additive approximation is accurate to sub-bp at typical
|
|
1338
1437
|
// fee+overhead magnitudes (< 100 bps combined).
|
|
1339
1438
|
const overheadBps = exports.LEVERAGE.DELEVERAGE_OVERHEAD_BPS + feeBps;
|
|
1340
|
-
swapCollateral = ceilDiv(debtInCollateral * (
|
|
1439
|
+
swapCollateral = ceilDiv(debtInCollateral * (helpers_1.BPS + overheadBps), helpers_1.BPS);
|
|
1341
1440
|
if (swapCollateral > maxTokenCollateral) {
|
|
1342
1441
|
const error = "Selected collateral token does not have enough posted collateral to fully deleverage.";
|
|
1343
1442
|
if (simulate) {
|
|
@@ -1354,7 +1453,7 @@ class CToken extends Calldata_1.Calldata {
|
|
|
1354
1453
|
// from input before swapping, so without compensation
|
|
1355
1454
|
// the swap underdelivers and actual leverage is slightly
|
|
1356
1455
|
// higher than target.
|
|
1357
|
-
swapCollateral = ceilDiv(swapCollateral *
|
|
1456
|
+
swapCollateral = ceilDiv(swapCollateral * helpers_1.BPS, helpers_1.BPS - feeBps);
|
|
1358
1457
|
}
|
|
1359
1458
|
}
|
|
1360
1459
|
if (!isFullDeleverage && swapCollateral > maxTokenCollateral) {
|
|
@@ -1364,7 +1463,7 @@ class CToken extends Calldata_1.Calldata {
|
|
|
1364
1463
|
}
|
|
1365
1464
|
throw new Error(error);
|
|
1366
1465
|
}
|
|
1367
|
-
const { action, quote } = await
|
|
1466
|
+
const { action, quote } = await this.currentDexAgg.quoteAction(manager.address, this.asset.address, borrowToken.asset.address, swapCollateral, slippage, feeBps, feeReceiver);
|
|
1368
1467
|
// Fee-aware slippage expansion for `_swapSafe` is handled by
|
|
1369
1468
|
// KyberSwap.quoteAction. See KyberSwap.ts for rationale.
|
|
1370
1469
|
// In the current PositionManager, `repayAssets` is only a
|
|
@@ -1421,6 +1520,7 @@ class CToken extends Calldata_1.Calldata {
|
|
|
1421
1520
|
}
|
|
1422
1521
|
async depositAndLeverage(depositAmount, borrow, multiplier, type, slippage_ = (0, decimal_js_1.default)(0.05), simulate = false) {
|
|
1423
1522
|
try {
|
|
1523
|
+
this.assertBorrowTokenBelongsToMarket(borrow);
|
|
1424
1524
|
if (multiplier.lte((0, decimal_js_1.default)(1))) {
|
|
1425
1525
|
if (simulate)
|
|
1426
1526
|
return { success: false, error: "Multiplier must be greater than 1" };
|
|
@@ -1436,9 +1536,14 @@ class CToken extends Calldata_1.Calldata {
|
|
|
1436
1536
|
}
|
|
1437
1537
|
let calldata;
|
|
1438
1538
|
const depositAssets = FormatConverter_1.default.decimalToBigInt(depositAmount, this.asset.decimals);
|
|
1539
|
+
if (depositAssets <= 0n) {
|
|
1540
|
+
if (simulate)
|
|
1541
|
+
return { success: false, error: "Deposit amount must be greater than zero." };
|
|
1542
|
+
throw new Error("Deposit amount must be greater than zero.");
|
|
1543
|
+
}
|
|
1439
1544
|
await this._checkTokenApproval(this.getPositionManagerDepositApprovalTarget(manager), depositAssets);
|
|
1440
|
-
await this._getLeverageSnapshot(borrow);
|
|
1441
|
-
const preview = this.previewDepositAndLeverage(multiplier, borrow, depositAssets, type);
|
|
1545
|
+
const snapshot = await this._getLeverageSnapshot(borrow);
|
|
1546
|
+
const preview = this.previewDepositAndLeverage(multiplier, borrow, depositAssets, type, snapshot);
|
|
1442
1547
|
if (preview.borrowAssets === 0n) {
|
|
1443
1548
|
if (simulate) {
|
|
1444
1549
|
return {
|
|
@@ -1454,7 +1559,7 @@ class CToken extends Calldata_1.Calldata {
|
|
|
1454
1559
|
switch (type) {
|
|
1455
1560
|
case 'simple': {
|
|
1456
1561
|
const feeReceiver = feeBps > 0n ? this.setup.feePolicy.feeReceiver : undefined;
|
|
1457
|
-
const { action, quote } = await this.
|
|
1562
|
+
const { action, quote } = await this.currentDexAgg.quoteAction(manager.address, borrow.asset.address, this.asset.address, borrowAssets, slippage, feeBps, feeReceiver);
|
|
1458
1563
|
// Fee-aware slippage expansion for `_swapSafe` is handled by
|
|
1459
1564
|
// KyberSwap.quoteAction. See KyberSwap.ts for rationale.
|
|
1460
1565
|
// Fee amplification: same pattern as leverageUp. See
|
|
@@ -1510,6 +1615,9 @@ class CToken extends Calldata_1.Calldata {
|
|
|
1510
1615
|
receiver ??= signer.address;
|
|
1511
1616
|
const depositAssets = FormatConverter_1.default.decimalToBigInt(amount, this.asset.decimals);
|
|
1512
1617
|
const zapAssets = await this.getZapAssetAmount(amount, zap);
|
|
1618
|
+
if (zapAssets <= 0n) {
|
|
1619
|
+
throw new Error("Deposit amount must be greater than zero.");
|
|
1620
|
+
}
|
|
1513
1621
|
const default_calldata = this.getCallData("deposit", [depositAssets, receiver]);
|
|
1514
1622
|
const { calldata, calldata_overrides } = await this.zap(zapAssets, zap, false, default_calldata, receiver);
|
|
1515
1623
|
return this.simulateOracleRoute(calldata, calldata_overrides);
|
|
@@ -1525,11 +1633,20 @@ class CToken extends Calldata_1.Calldata {
|
|
|
1525
1633
|
receiver ??= signer.address;
|
|
1526
1634
|
const depositAssets = FormatConverter_1.default.decimalToBigInt(amount, this.asset.decimals);
|
|
1527
1635
|
const zapAssets = await this.getZapAssetAmount(amount, zap);
|
|
1636
|
+
if (zapAssets <= 0n) {
|
|
1637
|
+
throw new Error("Deposit amount must be greater than zero.");
|
|
1638
|
+
}
|
|
1639
|
+
const depositShares = this.isZapInstruction(zap) ? undefined : this.virtualConvertToShares(depositAssets);
|
|
1640
|
+
this.assertCollateralCapacity(depositShares);
|
|
1528
1641
|
const collateralMethod = receiver.toLowerCase() === signer.address.toLowerCase()
|
|
1529
1642
|
? "depositAsCollateral"
|
|
1530
1643
|
: "depositAsCollateralFor";
|
|
1531
1644
|
const default_calldata = this.getCallData(collateralMethod, [depositAssets, receiver]);
|
|
1532
|
-
const { calldata, calldata_overrides } = await this.zap(zapAssets, zap, true, default_calldata, receiver);
|
|
1645
|
+
const { calldata, calldata_overrides, expectedShares } = await this.zap(zapAssets, zap, true, default_calldata, receiver);
|
|
1646
|
+
if (expectedShares !== undefined && expectedShares <= 0n) {
|
|
1647
|
+
throw new Error("Zap expected shares must be greater than zero.");
|
|
1648
|
+
}
|
|
1649
|
+
this.assertCollateralCapacity(expectedShares ?? depositShares);
|
|
1533
1650
|
return this.simulateOracleRoute(calldata, calldata_overrides);
|
|
1534
1651
|
}
|
|
1535
1652
|
catch (error) {
|
|
@@ -1580,7 +1697,12 @@ class CToken extends Calldata_1.Calldata {
|
|
|
1580
1697
|
default:
|
|
1581
1698
|
throw new Error("This zap type is not supported: " + type_of_zap);
|
|
1582
1699
|
}
|
|
1583
|
-
return {
|
|
1700
|
+
return {
|
|
1701
|
+
calldata,
|
|
1702
|
+
calldata_overrides,
|
|
1703
|
+
zapper,
|
|
1704
|
+
expectedShares: this.getZapperExpectedShares(zapper, calldata),
|
|
1705
|
+
};
|
|
1584
1706
|
}
|
|
1585
1707
|
async deposit(amount, zap = 'none', receiver = null) {
|
|
1586
1708
|
amount = await this.ensureUnderlyingAmount(amount, zap);
|
|
@@ -1588,6 +1710,9 @@ class CToken extends Calldata_1.Calldata {
|
|
|
1588
1710
|
receiver ??= signer.address;
|
|
1589
1711
|
const depositAssets = FormatConverter_1.default.decimalToBigInt(amount, this.asset.decimals);
|
|
1590
1712
|
const zapAssets = await this.getZapAssetAmount(amount, zap);
|
|
1713
|
+
if (zapAssets <= 0n) {
|
|
1714
|
+
throw new Error("Deposit amount must be greater than zero.");
|
|
1715
|
+
}
|
|
1591
1716
|
await this._checkDepositApprovals(zap, depositAssets, zapAssets);
|
|
1592
1717
|
const default_calldata = this.getCallData("deposit", [depositAssets, receiver]);
|
|
1593
1718
|
const { calldata, calldata_overrides } = await this.zap(zapAssets, zap, false, default_calldata, receiver);
|
|
@@ -1599,43 +1724,49 @@ class CToken extends Calldata_1.Calldata {
|
|
|
1599
1724
|
receiver ??= signer.address;
|
|
1600
1725
|
const depositAssets = FormatConverter_1.default.decimalToBigInt(amount, this.asset.decimals);
|
|
1601
1726
|
const zapAssets = await this.getZapAssetAmount(amount, zap);
|
|
1602
|
-
if (
|
|
1603
|
-
|
|
1604
|
-
const remainingCollateral = this.getRemainingCollateral(false);
|
|
1605
|
-
if (remainingCollateral <= 0n)
|
|
1606
|
-
throw new Error(collateralCapError);
|
|
1607
|
-
if (remainingCollateral > 0n) {
|
|
1608
|
-
const shares = this.virtualConvertToShares(depositAssets);
|
|
1609
|
-
if (shares > remainingCollateral) {
|
|
1610
|
-
throw new Error(collateralCapError);
|
|
1611
|
-
}
|
|
1612
|
-
}
|
|
1727
|
+
if (zapAssets <= 0n) {
|
|
1728
|
+
throw new Error("Deposit amount must be greater than zero.");
|
|
1613
1729
|
}
|
|
1730
|
+
const depositShares = this.isZapInstruction(zap) ? undefined : this.virtualConvertToShares(depositAssets);
|
|
1731
|
+
this.assertCollateralCapacity(depositShares);
|
|
1614
1732
|
await this._checkDepositApprovals(zap, depositAssets, zapAssets, true, receiver);
|
|
1615
1733
|
const collateralMethod = receiver.toLowerCase() === signer.address.toLowerCase()
|
|
1616
1734
|
? "depositAsCollateral"
|
|
1617
1735
|
: "depositAsCollateralFor";
|
|
1618
1736
|
const default_calldata = this.getCallData(collateralMethod, [depositAssets, receiver]);
|
|
1619
|
-
const { calldata, calldata_overrides } = await this.zap(zapAssets, zap, true, default_calldata, receiver);
|
|
1737
|
+
const { calldata, calldata_overrides, expectedShares } = await this.zap(zapAssets, zap, true, default_calldata, receiver);
|
|
1738
|
+
if (expectedShares !== undefined && expectedShares <= 0n) {
|
|
1739
|
+
throw new Error("Zap expected shares must be greater than zero.");
|
|
1740
|
+
}
|
|
1741
|
+
this.assertCollateralCapacity(expectedShares ?? depositShares);
|
|
1620
1742
|
return this.oracleRoute(calldata, calldata_overrides, receiver);
|
|
1621
1743
|
}
|
|
1622
1744
|
async redeem(amount) {
|
|
1623
1745
|
const signer = this.requireSigner();
|
|
1624
1746
|
const receiver = signer.address;
|
|
1625
1747
|
const owner = signer.address;
|
|
1748
|
+
const converted_shares = this.convertTokenInputToShares(amount);
|
|
1749
|
+
if (converted_shares <= 0n) {
|
|
1750
|
+
throw new Error("Redeem amount must be greater than zero.");
|
|
1751
|
+
}
|
|
1626
1752
|
const buffer = this.getExecutionDebtBufferTime();
|
|
1627
1753
|
const balance_avail = await this.balanceOf(signer.address);
|
|
1628
1754
|
const max_shares = await this.maxRedemption(true, buffer);
|
|
1629
|
-
const converted_shares = this.convertTokenInputToShares(amount);
|
|
1630
1755
|
const maxExecutableShares = max_shares < balance_avail ? max_shares : balance_avail;
|
|
1631
1756
|
let shares = maxExecutableShares < converted_shares ? maxExecutableShares : converted_shares;
|
|
1632
1757
|
if (maxExecutableShares === balance_avail && balance_avail - shares <= 10n) {
|
|
1633
1758
|
shares = balance_avail;
|
|
1634
1759
|
}
|
|
1760
|
+
if (shares <= 0n) {
|
|
1761
|
+
throw new Error("No redeemable cToken shares available.");
|
|
1762
|
+
}
|
|
1635
1763
|
const calldata = this.getCallData("redeem", [shares, receiver, owner]);
|
|
1636
1764
|
return this.oracleRoute(calldata);
|
|
1637
1765
|
}
|
|
1638
1766
|
async redeemShares(amount) {
|
|
1767
|
+
if (amount <= 0n) {
|
|
1768
|
+
throw new Error("Redeem amount must be greater than zero.");
|
|
1769
|
+
}
|
|
1639
1770
|
const signer = this.requireSigner();
|
|
1640
1771
|
const receiver = signer.address;
|
|
1641
1772
|
const owner = signer.address;
|
|
@@ -1759,7 +1890,7 @@ class CToken extends Calldata_1.Calldata {
|
|
|
1759
1890
|
return null;
|
|
1760
1891
|
}
|
|
1761
1892
|
return {
|
|
1762
|
-
token: new ERC20_1.ERC20(this.provider, instructions.inputToken, undefined,
|
|
1893
|
+
token: new ERC20_1.ERC20(this.provider, instructions.inputToken, undefined, this.setup.contracts.OracleManager, this.signer),
|
|
1763
1894
|
spender,
|
|
1764
1895
|
spenderLabel: `${zapType} Zapper`,
|
|
1765
1896
|
};
|
|
@@ -1823,10 +1954,11 @@ class CToken extends Calldata_1.Calldata {
|
|
|
1823
1954
|
if (typeof tx.wait === "function") {
|
|
1824
1955
|
await tx.wait();
|
|
1825
1956
|
}
|
|
1826
|
-
const
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1957
|
+
const signerAddress = signer.address;
|
|
1958
|
+
const refreshAccount = reloadAccount?.toLowerCase() === signerAddress.toLowerCase()
|
|
1959
|
+
? reloadAccount
|
|
1960
|
+
: signerAddress;
|
|
1961
|
+
await this.market.reloadUserData(refreshAccount);
|
|
1830
1962
|
return tx;
|
|
1831
1963
|
}
|
|
1832
1964
|
async simulateOracleRoute(calldata, override = {}) {
|