curvance 5.1.8 → 5.2.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 (114) hide show
  1. package/README.md +251 -70
  2. package/dist/abis/OptimizerReader.json +292 -1
  3. package/dist/chains/arbitrum.d.ts.map +1 -1
  4. package/dist/chains/arbitrum.js +14 -1
  5. package/dist/chains/arbitrum.js.map +1 -1
  6. package/dist/chains/index.d.ts +19 -0
  7. package/dist/chains/index.d.ts.map +1 -1
  8. package/dist/chains/index.js.map +1 -1
  9. package/dist/chains/monad.d.ts.map +1 -1
  10. package/dist/chains/monad.js +25 -2
  11. package/dist/chains/monad.js.map +1 -1
  12. package/dist/chains/services.d.ts +8 -0
  13. package/dist/chains/services.d.ts.map +1 -0
  14. package/dist/chains/services.js +9 -0
  15. package/dist/chains/services.js.map +1 -0
  16. package/dist/classes/Api.d.ts +7 -2
  17. package/dist/classes/Api.d.ts.map +1 -1
  18. package/dist/classes/Api.js +60 -24
  19. package/dist/classes/Api.js.map +1 -1
  20. package/dist/classes/BorrowableCToken.d.ts.map +1 -1
  21. package/dist/classes/BorrowableCToken.js +7 -2
  22. package/dist/classes/BorrowableCToken.js.map +1 -1
  23. package/dist/classes/CToken.d.ts +36 -29
  24. package/dist/classes/CToken.d.ts.map +1 -1
  25. package/dist/classes/CToken.js +225 -166
  26. package/dist/classes/CToken.js.map +1 -1
  27. package/dist/classes/DexAggregators/IDexAgg.d.ts +8 -0
  28. package/dist/classes/DexAggregators/IDexAgg.d.ts.map +1 -1
  29. package/dist/classes/DexAggregators/KyberSwap.d.ts +5 -2
  30. package/dist/classes/DexAggregators/KyberSwap.d.ts.map +1 -1
  31. package/dist/classes/DexAggregators/KyberSwap.js +41 -19
  32. package/dist/classes/DexAggregators/KyberSwap.js.map +1 -1
  33. package/dist/classes/DexAggregators/MultiDexAgg.d.ts +7 -4
  34. package/dist/classes/DexAggregators/MultiDexAgg.d.ts.map +1 -1
  35. package/dist/classes/DexAggregators/MultiDexAgg.js +62 -16
  36. package/dist/classes/DexAggregators/MultiDexAgg.js.map +1 -1
  37. package/dist/classes/DexAggregators/helpers.d.ts +1 -1
  38. package/dist/classes/DexAggregators/helpers.d.ts.map +1 -1
  39. package/dist/classes/DexAggregators/helpers.js +1 -1
  40. package/dist/classes/DexAggregators/helpers.js.map +1 -1
  41. package/dist/classes/DexAggregators/index.d.ts +0 -1
  42. package/dist/classes/DexAggregators/index.d.ts.map +1 -1
  43. package/dist/classes/DexAggregators/index.js +0 -1
  44. package/dist/classes/DexAggregators/index.js.map +1 -1
  45. package/dist/classes/LendingOptimizer.d.ts +7 -0
  46. package/dist/classes/LendingOptimizer.d.ts.map +1 -1
  47. package/dist/classes/LendingOptimizer.js +12 -2
  48. package/dist/classes/LendingOptimizer.js.map +1 -1
  49. package/dist/classes/Market.d.ts +5 -0
  50. package/dist/classes/Market.d.ts.map +1 -1
  51. package/dist/classes/Market.js +129 -30
  52. package/dist/classes/Market.js.map +1 -1
  53. package/dist/classes/NativeToken.d.ts +5 -2
  54. package/dist/classes/NativeToken.d.ts.map +1 -1
  55. package/dist/classes/NativeToken.js +5 -5
  56. package/dist/classes/NativeToken.js.map +1 -1
  57. package/dist/classes/OptimizerReader.d.ts +44 -4
  58. package/dist/classes/OptimizerReader.d.ts.map +1 -1
  59. package/dist/classes/OptimizerReader.js +133 -62
  60. package/dist/classes/OptimizerReader.js.map +1 -1
  61. package/dist/classes/PositionManager.d.ts +1 -0
  62. package/dist/classes/PositionManager.d.ts.map +1 -1
  63. package/dist/classes/PositionManager.js +25 -0
  64. package/dist/classes/PositionManager.js.map +1 -1
  65. package/dist/classes/ProtocolReader.d.ts +3 -0
  66. package/dist/classes/ProtocolReader.d.ts.map +1 -1
  67. package/dist/classes/ProtocolReader.js +34 -0
  68. package/dist/classes/ProtocolReader.js.map +1 -1
  69. package/dist/classes/Zapper.d.ts +4 -1
  70. package/dist/classes/Zapper.d.ts.map +1 -1
  71. package/dist/classes/Zapper.js +34 -9
  72. package/dist/classes/Zapper.js.map +1 -1
  73. package/dist/classes/index.d.ts +1 -1
  74. package/dist/classes/index.d.ts.map +1 -1
  75. package/dist/classes/index.js +1 -1
  76. package/dist/classes/index.js.map +1 -1
  77. package/dist/contracts/index.d.ts +249 -245
  78. package/dist/contracts/index.d.ts.map +1 -1
  79. package/dist/contracts/index.js +3 -2
  80. package/dist/contracts/index.js.map +1 -1
  81. package/dist/contracts/monad-mainnet.json +4 -0
  82. package/dist/feePolicy.d.ts +29 -26
  83. package/dist/feePolicy.d.ts.map +1 -1
  84. package/dist/feePolicy.js +43 -34
  85. package/dist/feePolicy.js.map +1 -1
  86. package/dist/format/index.d.ts +5 -0
  87. package/dist/format/index.d.ts.map +1 -1
  88. package/dist/format/index.js +28 -0
  89. package/dist/format/index.js.map +1 -1
  90. package/dist/helpers.d.ts +248 -245
  91. package/dist/helpers.d.ts.map +1 -1
  92. package/dist/helpers.js +5 -5
  93. package/dist/helpers.js.map +1 -1
  94. package/dist/immutability.d.ts +6 -0
  95. package/dist/immutability.d.ts.map +1 -0
  96. package/dist/immutability.js +25 -0
  97. package/dist/immutability.js.map +1 -0
  98. package/dist/integrations/merkl.d.ts +9 -2
  99. package/dist/integrations/merkl.d.ts.map +1 -1
  100. package/dist/integrations/merkl.js +219 -11
  101. package/dist/integrations/merkl.js.map +1 -1
  102. package/dist/integrations/snapshot.d.ts +3 -0
  103. package/dist/integrations/snapshot.d.ts.map +1 -1
  104. package/dist/integrations/snapshot.js +47 -3
  105. package/dist/integrations/snapshot.js.map +1 -1
  106. package/dist/setup.d.ts +13 -3
  107. package/dist/setup.d.ts.map +1 -1
  108. package/dist/setup.js +101 -7
  109. package/dist/setup.js.map +1 -1
  110. package/package.json +7 -5
  111. package/dist/classes/Redstone.d.ts +0 -15
  112. package/dist/classes/Redstone.d.ts.map +0 -1
  113. package/dist/classes/Redstone.js +0 -56
  114. package/dist/classes/Redstone.js.map +0 -1
@@ -10,15 +10,11 @@ const ERC20_1 = require("./ERC20");
10
10
  const Calldata_1 = require("./Calldata");
11
11
  const decimal_js_1 = __importDefault(require("decimal.js"));
12
12
  const BaseCToken_json_1 = __importDefault(require("../abis/BaseCToken.json"));
13
- const Redstone_1 = require("./Redstone");
14
13
  const Zapper_1 = require("./Zapper");
15
14
  const PositionManager_1 = require("./PositionManager");
16
15
  const NativeToken_1 = require("./NativeToken");
17
16
  const ERC4626_1 = require("./ERC4626");
18
17
  const FormatConverter_1 = __importDefault(require("./FormatConverter"));
19
- const EXCLUDED_ZAP_SYMBOLS = new Set([
20
- 'eBTC', 'vUSD', 'ezETH', 'YZM', 'wsrUSD', 'sAUSD',
21
- ]);
22
18
  const EXECUTION_DEBT_BUFFER_TIME = 100n;
23
19
  function ceilDiv(numerator, denominator) {
24
20
  if (denominator <= 0n) {
@@ -33,9 +29,9 @@ function ceilDiv(numerator, denominator) {
33
29
  *
34
30
  * Single-oracle architecture (permanent design)
35
31
  * ---------------------------------------------
36
- * Curvance uses single-adaptor oracle configs only (Redstone Core/Classic
37
- * via BaseOracleAdaptor, which ignores the getLower flag — see line 78 of
38
- * BaseOracleAdaptor.sol). Dual-feed mode was deprecated in favor of the
32
+ * Curvance uses single-adaptor oracle configs only. The adaptor path ignores
33
+ * the getLower flag — see line 78 of BaseOracleAdaptor.sol. Dual-feed mode
34
+ * was deprecated in favor of the
39
35
  * price-guard system and orderflow MEV tech, and is not coming back.
40
36
  * This means MarketManager._statusOf returns symmetric prices for
41
37
  * collateral (queries with getLower=true) and debt (getLower=false), so
@@ -63,7 +59,7 @@ function ceilDiv(numerator, denominator) {
63
59
  * Leverage UP: under single-oracle, the contract sees zero forced loss
64
60
  * for a perfect swap. The only real sources of difference between
65
61
  * snapshot-time prices and execution-time prices are: (a) wei-level share
66
- * rounding, (b) Redstone update drift between the snapshot RPC and the
62
+ * rounding, (b) oracle price drift between the snapshot RPC and the
67
63
  * tx broadcast block. Both are small constants in absolute terms, NOT
68
64
  * leverage-scaled. A small flat buffer suffices.
69
65
  *
@@ -87,8 +83,7 @@ exports.LEVERAGE = {
87
83
  * for the same market)
88
84
  * - `CURVANCE_FEE_BPS` (deterministic, amplified by (L-1); at L=10
89
85
  * eats ~36bps of equity-fraction)
90
- * - Oracle drift between preview snapshot and Redstone payload at
91
- * tx inclusion
86
+ * - Oracle drift between preview snapshot and tx inclusion
92
87
  * - Share rounding (wei-level)
93
88
  *
94
89
  * History: 0.99 → 0.995 when caching improved precision (pre-fee era).
@@ -102,7 +97,7 @@ exports.LEVERAGE = {
102
97
  MAX_LEVERAGE_FACTOR: (0, decimal_js_1.default)(0.98),
103
98
  /** Flat BPS buffer added to leverage-up DEX/swapSafe slippage tolerance.
104
99
  * Under single-oracle, the only forced loss at the swap level comes from
105
- * wei-level share rounding plus possible Redstone price drift between
100
+ * wei-level share rounding plus possible oracle price drift between
106
101
  * snapshot RPC and tx broadcast block. Both are small constants.
107
102
  *
108
103
  * Fee handling: KyberSwap.quoteAction expands action.slippage by feeBps
@@ -184,27 +179,44 @@ class CToken extends Calldata_1.Calldata {
184
179
  this.contract = (0, helpers_1.contractSetup)(this.provider, address, BaseCToken_json_1.default);
185
180
  this.cache = cache;
186
181
  this.market = market;
187
- const chainSettings = this.currentChainConfig;
182
+ const chainSettings = this.currentChainAssets;
188
183
  const assetAddr = this.asset.address.toLowerCase();
189
184
  this.isNativeVault = chainSettings.native_vaults.some(vault => vault.contract.toLowerCase() == assetAddr);
190
185
  this.isVault = chainSettings.vaults.some(vault => vault.contract.toLowerCase() == assetAddr);
191
186
  this.isWrappedNative = chainSettings.wrapped_native.toLowerCase() == assetAddr;
192
- if (EXCLUDED_ZAP_SYMBOLS.has(this.asset.symbol)) {
187
+ this.refreshRouteCapabilities();
188
+ }
189
+ refreshRouteCapabilities() {
190
+ this.zapTypes = [];
191
+ this.leverageTypes = [];
192
+ if (this.isZapSymbolExcluded(this.asset.symbol)) {
193
193
  return;
194
194
  }
195
- if (this.isNativeVault)
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)
196
207
  this.zapTypes.push('native-vault');
197
208
  if ("nativeVaultPositionManager" in this.market.plugins && this.isNativeVault)
198
209
  this.leverageTypes.push('native-vault');
199
- if (this.isWrappedNative)
210
+ if (supportsSimpleZaps && this.isWrappedNative)
200
211
  this.zapTypes.push('native-simple');
201
- if (this.isVault)
212
+ if (supportsVaultZaps && this.isVault)
202
213
  this.zapTypes.push('vault');
203
214
  if ("vaultPositionManager" in this.market.plugins && this.isVault)
204
215
  this.leverageTypes.push('vault');
205
- if ("simplePositionManager" in this.market.plugins)
216
+ if (supportsSimpleZaps && "simplePositionManager" in this.market.plugins)
206
217
  this.leverageTypes.push('simple');
207
- this.zapTypes.push('simple');
218
+ if (supportsSimpleZaps)
219
+ this.zapTypes.push('simple');
208
220
  }
209
221
  getUserCacheFreshness() {
210
222
  if (this.userCacheFreshness == undefined) {
@@ -233,7 +245,28 @@ class CToken extends Calldata_1.Calldata {
233
245
  }
234
246
  get setup() { return this.market.setup; }
235
247
  get currentChain() { return this.setup.chain; }
236
- get currentChainConfig() { return (0, helpers_1.getChainConfig)(this.currentChain); }
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
+ }
237
270
  requireSigner() { return (0, helpers_1.requireSigner)(this.signer); }
238
271
  getAccountOrThrow(account = null) {
239
272
  return (0, helpers_1.requireAccount)(account ?? this.account, this.signer);
@@ -241,6 +274,31 @@ class CToken extends Calldata_1.Calldata {
241
274
  getWriteContract() {
242
275
  return (0, helpers_1.contractSetup)(this.requireSigner(), this.address, BaseCToken_json_1.default);
243
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
+ }
244
302
  getZapType(zap) {
245
303
  return typeof zap === "object" ? zap.type : zap;
246
304
  }
@@ -266,7 +324,7 @@ class CToken extends Calldata_1.Calldata {
266
324
  if (zap.inputToken.toLowerCase() === helpers_1.NATIVE_ADDRESS.toLowerCase()) {
267
325
  return 18n;
268
326
  }
269
- const inputErc20 = new ERC20_1.ERC20(this.provider, zap.inputToken, undefined, undefined, this.signer);
327
+ const inputErc20 = new ERC20_1.ERC20(this.provider, zap.inputToken, undefined, this.setup.contracts.OracleManager, this.signer);
270
328
  return inputErc20.decimals ?? await inputErc20.contract.decimals();
271
329
  }
272
330
  }
@@ -274,8 +332,9 @@ class CToken extends Calldata_1.Calldata {
274
332
  return FormatConverter_1.default.decimalToBigInt(amount, await this.getZapInputDecimals(zap));
275
333
  }
276
334
  async assertVaultLeverageBorrowAssetSupported(borrow, type) {
335
+ this.assertBorrowTokenBelongsToMarket(borrow);
277
336
  const expectedAsset = type === "native-vault"
278
- ? this.currentChainConfig.wrapped_native
337
+ ? this.currentChainAssets.wrapped_native
279
338
  : await this.getVaultAsset(false);
280
339
  const actualAsset = borrow.asset.address;
281
340
  if (actualAsset.toLowerCase() === expectedAsset.toLowerCase()) {
@@ -384,11 +443,15 @@ class CToken extends Calldata_1.Calldata {
384
443
  const shares = this.totalSupply === 0n || this.totalAssets === 0n
385
444
  ? assets
386
445
  : (assets * this.totalSupply) / this.totalAssets;
387
- return bufferBps > 0n ? shares * (10000n - bufferBps) / 10000n : shares;
388
- }
389
- getMarketLeverageState() {
390
- const currentCollateralInUsd = this.market.userCollateral;
391
- const currentDebt = this.market.userDebt;
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;
392
455
  const equity = currentCollateralInUsd.sub(currentDebt);
393
456
  if (currentCollateralInUsd.lte(0) || equity.lte(0)) {
394
457
  return {
@@ -525,7 +588,7 @@ class CToken extends Calldata_1.Calldata {
525
588
  // into Curvance shares. Buffer the inner preview so exchange-rate drift
526
589
  // between quote time and inclusion cannot trip the outer expectedShares
527
590
  // check on otherwise-valid deposits/leverage/zaps.
528
- const vaultShares = vaultSharesRaw * (10000n - exports.LEVERAGE.SHARES_BUFFER_BPS) / 10000n;
591
+ const vaultShares = vaultSharesRaw * (helpers_1.BPS - exports.LEVERAGE.SHARES_BUFFER_BPS) / helpers_1.BPS;
529
592
  return this.convertToShares(vaultShares);
530
593
  }
531
594
  getAsset(asErc20) {
@@ -588,7 +651,7 @@ class CToken extends Calldata_1.Calldata {
588
651
  if (zap_contract == null) {
589
652
  return null;
590
653
  }
591
- 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);
592
655
  }
593
656
  async isZapAssetApproved(instructions, amount) {
594
657
  if (instructions == 'none') {
@@ -655,7 +718,7 @@ class CToken extends Calldata_1.Calldata {
655
718
  }
656
719
  async getAllowance(check_contract, underlying = true) {
657
720
  const signer = this.requireSigner();
658
- const erc20 = new ERC20_1.ERC20(this.provider, underlying ? this.asset.address : this.address, undefined, undefined, this.signer);
721
+ const erc20 = new ERC20_1.ERC20(this.provider, underlying ? this.asset.address : this.address, undefined, this.setup.contracts.OracleManager, this.signer);
659
722
  const allowance = await erc20.allowance(signer.address, check_contract);
660
723
  return allowance;
661
724
  }
@@ -665,12 +728,12 @@ class CToken extends Calldata_1.Calldata {
665
728
  * @returns tx
666
729
  */
667
730
  async approveUnderlying(amount = null, target = null) {
668
- const erc20 = new ERC20_1.ERC20(this.provider, this.asset.address, undefined, undefined, this.signer);
731
+ const erc20 = new ERC20_1.ERC20(this.provider, this.asset.address, undefined, this.setup.contracts.OracleManager, this.signer);
669
732
  const tx = await erc20.approve(target ? target : this.address, amount);
670
733
  return tx;
671
734
  }
672
735
  async approve(amount = null, spender) {
673
- const erc20 = new ERC20_1.ERC20(this.provider, this.address, undefined, undefined, this.signer);
736
+ const erc20 = new ERC20_1.ERC20(this.provider, this.address, undefined, this.setup.contracts.OracleManager, this.signer);
674
737
  const tx = await erc20.approve(spender, amount);
675
738
  return tx;
676
739
  }
@@ -741,7 +804,8 @@ class CToken extends Calldata_1.Calldata {
741
804
  }
742
805
  async transfer(receiver, amount) {
743
806
  const shares = this.convertTokenInputToShares(amount);
744
- return this.getWriteContract().transfer(receiver, shares);
807
+ const calldata = this.getCallData("transfer", [receiver, shares]);
808
+ return this.oracleRoute(calldata);
745
809
  }
746
810
  async redeemCollateral(amount, receiver = null, owner = null) {
747
811
  const signer = this.requireSigner();
@@ -775,21 +839,47 @@ class CToken extends Calldata_1.Calldata {
775
839
  if (max_shares <= 0n) {
776
840
  throw new Error("No cToken shares available to post as collateral.");
777
841
  }
842
+ this.assertCollateralCapacity(max_shares);
778
843
  const calldata = this.getCallData("postCollateral", [max_shares]);
779
844
  const tx = await this.oracleRoute(calldata);
780
845
  // Reload collateral state after execution
781
846
  await this.fetchUserCollateral();
782
847
  return tx;
783
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
+ }
784
874
  async getZapBalance(zap) {
785
875
  const signer = this.requireSigner();
786
876
  let asset;
787
877
  if (typeof zap === 'object') {
788
878
  if (zap.type === 'native-vault' || zap.type === 'native-simple' || zap.inputToken.toLowerCase() === helpers_1.NATIVE_ADDRESS.toLowerCase()) {
789
- 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);
790
880
  }
791
881
  else {
792
- asset = new ERC20_1.ERC20(this.provider, zap.inputToken, undefined, undefined, this.signer);
882
+ asset = new ERC20_1.ERC20(this.provider, zap.inputToken, undefined, this.setup.contracts.OracleManager, this.signer);
793
883
  }
794
884
  }
795
885
  else {
@@ -801,10 +891,10 @@ class CToken extends Calldata_1.Calldata {
801
891
  asset = await this.getVaultAsset(true);
802
892
  break;
803
893
  case 'native-vault':
804
- 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);
805
895
  break;
806
896
  case 'native-simple':
807
- 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);
808
898
  break;
809
899
  default: throw new Error("Unsupported zap type for balance fetch");
810
900
  }
@@ -883,7 +973,7 @@ class CToken extends Calldata_1.Calldata {
883
973
  }
884
974
  async convertToShares(assets, bufferBps = exports.LEVERAGE.SHARES_BUFFER_BPS) {
885
975
  const shares = await this.contract.convertToShares(assets);
886
- return bufferBps > 0n ? shares * (10000n - bufferBps) / 10000n : shares;
976
+ return bufferBps > 0n ? shares * (helpers_1.BPS - bufferBps) / helpers_1.BPS : shares;
887
977
  }
888
978
  async maxRedemption(in_shares = false, bufferTime = 0n, breakdown = false) {
889
979
  const data = await this.market.reader.maxRedemptionOf(this.getAccountOrThrow(), this, bufferTime);
@@ -912,14 +1002,14 @@ class CToken extends Calldata_1.Calldata {
912
1002
  let tokens_exclude = [this.asset.address.toLocaleLowerCase()];
913
1003
  if (this.zapTypes.includes('native-vault')) {
914
1004
  tokens.push({
915
- 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),
916
1006
  type: 'native-vault'
917
1007
  });
918
1008
  tokens_exclude.push(helpers_1.EMPTY_ADDRESS.toLowerCase(), helpers_1.NATIVE_ADDRESS.toLowerCase());
919
1009
  }
920
1010
  if (this.zapTypes.includes('native-simple')) {
921
1011
  tokens.push({
922
- 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),
923
1013
  type: 'native-simple'
924
1014
  });
925
1015
  if (!this.zapTypes.includes('native-vault')) {
@@ -934,20 +1024,20 @@ class CToken extends Calldata_1.Calldata {
934
1024
  });
935
1025
  tokens_exclude.push(vault_asset.address.toLocaleLowerCase());
936
1026
  }
937
- if (this.zapTypes.includes('simple') && this.currentChainConfig.dexAgg.router !== helpers_1.EMPTY_ADDRESS) {
938
- let dexAggSearch = await this.currentChainConfig.dexAgg.getAvailableTokens(this.provider, search, this.account);
1027
+ if (this.zapTypes.includes('simple') && this.hasExecutableDexRoute) {
1028
+ let dexAggSearch = await this.currentDexAgg.getAvailableTokens(this.provider, search, this.account);
939
1029
  tokens = tokens.concat(dexAggSearch.filter(token => !tokens_exclude.includes(token.interface.address.toLocaleLowerCase())));
940
1030
  // Add native MON as a zap option for any token with a simple zapper
941
1031
  // (not just wrapped native). The simple zapper handles wrapping + swapping.
942
1032
  if (!tokens_exclude.includes(helpers_1.NATIVE_ADDRESS.toLowerCase()) && !this.isWrappedNative) {
943
1033
  tokens.push({
944
- 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),
945
1035
  type: 'simple'
946
1036
  });
947
1037
  tokens_exclude.push(helpers_1.NATIVE_ADDRESS.toLowerCase());
948
1038
  }
949
1039
  }
950
- tokens = tokens.filter(token => token.type === 'none' || !EXCLUDED_ZAP_SYMBOLS.has(token.interface.symbol ?? ''));
1040
+ tokens = tokens.filter(token => token.type === 'none' || !this.isZapSymbolExcluded(token.interface.symbol));
951
1041
  if (search) {
952
1042
  const lowerSearch = search.toLowerCase();
953
1043
  tokens = tokens.filter(token => (token.interface.name ?? '').toLowerCase().includes(lowerSearch) ||
@@ -963,30 +1053,29 @@ class CToken extends Calldata_1.Calldata {
963
1053
  * Single-RPC snapshot of fresh position state for leverage operations.
964
1054
  * Calls ProtocolReader.getLeverageSnapshot which internally uses
965
1055
  * hypotheticalLiquidityOf for aggregate position + fresh oracle prices
966
- * + projected debt balance. Updates the local cache so downstream
967
- * preview computations (previewLeverageUp/Down) read fresh values.
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.
968
1059
  *
969
1060
  * Returns the snapshot for direct use where needed (e.g. debtTokenBalance
970
1061
  * for full deleverage swap sizing).
971
1062
  */
972
1063
  async _getLeverageSnapshot(borrow) {
1064
+ this.assertBorrowTokenBelongsToMarket(borrow);
973
1065
  const snapshot = await this.market.reader.getLeverageSnapshot(this.getAccountOrThrow(), this.address, borrow.address, 120n);
974
1066
  if (snapshot.oracleError) {
975
1067
  throw new Error(`Oracle error fetching leverage snapshot for ${this.symbol}/${borrow.symbol}`);
976
1068
  }
977
- // Update cache so preview functions read fresh values
978
1069
  this.cache.assetPrice = snapshot.collateralAssetPrice;
979
1070
  this.cache.sharePrice = snapshot.sharePrice;
980
1071
  borrow.cache.assetPrice = snapshot.debtAssetPrice;
981
- this.market.cache.user.collateral = snapshot.collateralUsd;
982
- this.market.cache.user.debt = snapshot.debtUsd;
983
1072
  return snapshot;
984
1073
  }
985
1074
  /**
986
1075
  * Compute slippage BPS for the contract's checkSlippage modifier when
987
1076
  * leveraging up. Under Curvance's permanent single-oracle architecture
988
1077
  * with fresh state from _getLeverageSnapshot, the only forced equity
989
- * loss comes from wei-level share rounding plus possible Redstone price
1078
+ * loss comes from wei-level share rounding plus possible oracle price
990
1079
  * drift between snapshot RPC and tx broadcast — both small constants
991
1080
  * in absolute terms. We add a small flat buffer; the contract's
992
1081
  * equity-fraction denominator amplifies it by (L-1)x automatically.
@@ -994,7 +1083,7 @@ class CToken extends Calldata_1.Calldata {
994
1083
  * unaffected — that's the layer that bounds MEV extraction.
995
1084
  *
996
1085
  * Applied uniformly to simple AND vault/native-vault leverage-up paths.
997
- * Simple path uses the buffer for share-rounding + Redstone drift as
1086
+ * Simple path uses the buffer for share-rounding + oracle drift as
998
1087
  * described above. Vault paths inherit the flat 10 bps through the
999
1088
  * shared `slippage` variable before the per-branch
1000
1089
  * `amplifyContractSlippage(..., LEVERAGE_UP_VAULT_DRIFT_BPS)` expansion;
@@ -1010,11 +1099,13 @@ class CToken extends Calldata_1.Calldata {
1010
1099
  return slippage + exports.LEVERAGE.LEVERAGE_UP_BUFFER_BPS;
1011
1100
  }
1012
1101
  assertSimpleLeverageSwapAssetsDiffer(borrow) {
1102
+ this.assertBorrowTokenBelongsToMarket(borrow);
1013
1103
  if (borrow.asset.address.toLowerCase() === this.asset.address.toLowerCase()) {
1014
1104
  throw new Error("Simple leverage requires distinct collateral and borrow assets.");
1015
1105
  }
1016
1106
  }
1017
1107
  async assertLeverageBorrowCapacity(borrow, borrowAssets) {
1108
+ this.assertBorrowTokenBelongsToMarket(borrow);
1018
1109
  if (borrowAssets === 0n) {
1019
1110
  return;
1020
1111
  }
@@ -1031,6 +1122,7 @@ class CToken extends Calldata_1.Calldata {
1031
1122
  }
1032
1123
  }
1033
1124
  assertSelectedBorrowDebtCanDeleverage(borrow, selectedDebtAssets, requiredDebtReductionUsd) {
1125
+ this.assertBorrowTokenBelongsToMarket(borrow);
1034
1126
  if (requiredDebtReductionUsd.lte(0)) {
1035
1127
  return;
1036
1128
  }
@@ -1052,15 +1144,16 @@ class CToken extends Calldata_1.Calldata {
1052
1144
  return (0, decimal_js_1.default)(1);
1053
1145
  return collateralAfterDeposit.div(equityAfterDeposit);
1054
1146
  }
1055
- resolveLeverageUpPreview({ operation, targetLeverage, borrow, depositAssets = 0n, positionManagerType, }) {
1056
- const leverageState = this.getMarketLeverageState();
1057
- const currentLeverage = leverageState.currentLeverage ?? (0, decimal_js_1.default)(1);
1058
- const currentCollateralInUsd = leverageState.currentCollateralInUsd;
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;
1059
1152
  const depositInAssets = FormatConverter_1.default.bigIntToDecimal(depositAssets, this.asset.decimals);
1060
1153
  const depositInUsd = depositAssets > 0n
1061
1154
  ? this.convertTokensToUsd(depositAssets, true)
1062
1155
  : (0, decimal_js_1.default)(0);
1063
- const currentDebt = leverageState.currentDebt;
1156
+ const currentDebt = currentState.currentDebt;
1064
1157
  const effectiveCurrentLeverage = depositAssets > 0n
1065
1158
  ? this.computePostDepositNaturalLeverage(currentCollateralInUsd, currentDebt, depositInUsd)
1066
1159
  : currentLeverage;
@@ -1101,7 +1194,7 @@ class CToken extends Calldata_1.Calldata {
1101
1194
  targetLeverage: resolvedTargetLeverage,
1102
1195
  })
1103
1196
  : 0n;
1104
- const feeAssets = borrowAmount.mul((0, decimal_js_1.default)(Number(feeBps))).div((0, decimal_js_1.default)(10000));
1197
+ const feeAssets = borrowAmount.mul((0, decimal_js_1.default)(Number(feeBps))).div(helpers_1.BPS_DECIMAL);
1105
1198
  const feeUsd = feeAssets.mul(borrowPrice);
1106
1199
  const collateralIncreaseFromBorrow = decimal_js_1.default.max(debtIncrease.sub(feeUsd), (0, decimal_js_1.default)(0));
1107
1200
  const collateralIncrease = depositInUsd.add(collateralIncreaseFromBorrow);
@@ -1126,36 +1219,39 @@ class CToken extends Calldata_1.Calldata {
1126
1219
  feeUsd,
1127
1220
  };
1128
1221
  }
1129
- previewDepositAndLeverage(newLeverage, borrow, depositAmount, positionManagerType) {
1222
+ previewDepositAndLeverage(newLeverage, borrow, depositAmount, positionManagerType, leverageState) {
1130
1223
  return this.resolveLeverageUpPreview({
1131
1224
  operation: 'deposit-and-leverage',
1132
1225
  targetLeverage: newLeverage,
1133
1226
  borrow,
1134
1227
  depositAssets: depositAmount,
1135
1228
  positionManagerType,
1229
+ leverageState,
1136
1230
  });
1137
1231
  }
1138
- previewLeverageUp(newLeverage, borrow, depositAmount, positionManagerType) {
1232
+ previewLeverageUp(newLeverage, borrow, depositAmount, positionManagerType, leverageState) {
1139
1233
  if ((depositAmount ?? 0n) > 0n) {
1140
- return this.previewDepositAndLeverage(newLeverage, borrow, depositAmount, positionManagerType);
1234
+ return this.previewDepositAndLeverage(newLeverage, borrow, depositAmount, positionManagerType, leverageState);
1141
1235
  }
1142
1236
  return this.resolveLeverageUpPreview({
1143
1237
  operation: 'leverage-up',
1144
1238
  targetLeverage: newLeverage,
1145
1239
  borrow,
1146
1240
  positionManagerType,
1241
+ leverageState,
1147
1242
  });
1148
1243
  }
1149
- previewLeverageDown(newLeverage, currentLeverage, borrow) {
1244
+ previewLeverageDown(newLeverage, currentLeverage, borrow, leverageState) {
1245
+ this.assertBorrowTokenBelongsToMarket(borrow);
1150
1246
  if (newLeverage.gte(currentLeverage)) {
1151
1247
  throw new Error("New leverage must be less than current leverage");
1152
1248
  }
1153
1249
  if (newLeverage.lt((0, decimal_js_1.default)(1))) {
1154
1250
  throw new Error("New leverage must be at least 1");
1155
1251
  }
1156
- const leverageState = this.getMarketLeverageState();
1157
- const collateralInUsd = leverageState.currentCollateralInUsd;
1158
- const currentDebt = leverageState.currentDebt;
1252
+ const currentState = this.getMarketLeverageState(leverageState);
1253
+ const collateralInUsd = currentState.currentCollateralInUsd;
1254
+ const currentDebt = currentState.currentDebt;
1159
1255
  const equity = collateralInUsd.sub(currentDebt);
1160
1256
  if (equity.lte(0)) {
1161
1257
  throw new Error("Position has no positive equity to deleverage.");
@@ -1180,7 +1276,7 @@ class CToken extends Calldata_1.Calldata {
1180
1276
  currentLeverage,
1181
1277
  targetLeverage: newLeverage,
1182
1278
  }) : 0n;
1183
- const feeUsd = collateralAssetReductionUsd.mul((0, decimal_js_1.default)(Number(feeBps))).div((0, decimal_js_1.default)(10000));
1279
+ const feeUsd = collateralAssetReductionUsd.mul((0, decimal_js_1.default)(Number(feeBps))).div(helpers_1.BPS_DECIMAL);
1184
1280
  const feeAssets = this.getPrice(true).gt(0)
1185
1281
  ? feeUsd.div(this.getPrice(true))
1186
1282
  : (0, decimal_js_1.default)(0);
@@ -1199,6 +1295,7 @@ class CToken extends Calldata_1.Calldata {
1199
1295
  }
1200
1296
  async leverageUp(borrow, newLeverage, type, slippage_ = (0, decimal_js_1.default)(0.05), simulate = false) {
1201
1297
  try {
1298
+ this.assertBorrowTokenBelongsToMarket(borrow);
1202
1299
  this.requireSigner();
1203
1300
  const manager = this.getPositionManager(type);
1204
1301
  if (type === 'vault' || type === 'native-vault') {
@@ -1208,8 +1305,8 @@ class CToken extends Calldata_1.Calldata {
1208
1305
  this.assertSimpleLeverageSwapAssetsDiffer(borrow);
1209
1306
  }
1210
1307
  let calldata;
1211
- await this._getLeverageSnapshot(borrow);
1212
- 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);
1213
1310
  const slippage = this._leverageUpSlippage(FormatConverter_1.default.percentageToBps(slippage_), preview.targetLeverage);
1214
1311
  const { borrowAmount, borrowAssets, feeBps, targetLeverage } = preview;
1215
1312
  if (borrowAssets === 0n) {
@@ -1225,7 +1322,7 @@ class CToken extends Calldata_1.Calldata {
1225
1322
  switch (type) {
1226
1323
  case 'simple': {
1227
1324
  const feeReceiver = feeBps > 0n ? this.setup.feePolicy.feeReceiver : undefined;
1228
- const { action, quote } = await this.currentChainConfig.dexAgg.quoteAction(manager.address, borrow.asset.address, this.asset.address, borrowAssets, slippage, feeBps, feeReceiver);
1325
+ const { action, quote } = await this.currentDexAgg.quoteAction(manager.address, borrow.asset.address, this.asset.address, borrowAssets, slippage, feeBps, feeReceiver);
1229
1326
  // Fee-aware slippage expansion now lives inside KyberSwap.quoteAction
1230
1327
  // so any caller inherits correct behavior. See KyberSwap.ts for the
1231
1328
  // rationale. The fee still reduces swap output, which checkSlippage
@@ -1280,13 +1377,13 @@ class CToken extends Calldata_1.Calldata {
1280
1377
  }
1281
1378
  async leverageDown(borrowToken, currentLeverage, newLeverage, type, slippage_ = (0, decimal_js_1.default)(0.05), simulate = false) {
1282
1379
  try {
1380
+ this.assertBorrowTokenBelongsToMarket(borrowToken);
1283
1381
  if (newLeverage.gte(currentLeverage)) {
1284
1382
  if (simulate)
1285
1383
  return { success: false, error: "New leverage must be less than current leverage" };
1286
1384
  throw new Error("New leverage must be less than current leverage");
1287
1385
  }
1288
1386
  this.requireSigner();
1289
- const config = this.currentChainConfig;
1290
1387
  const slippage = (0, helpers_1.toBps)(slippage_);
1291
1388
  const manager = this.getPositionManager(type);
1292
1389
  if (type === 'simple') {
@@ -1294,7 +1391,7 @@ class CToken extends Calldata_1.Calldata {
1294
1391
  }
1295
1392
  let calldata;
1296
1393
  const snapshot = await this._getLeverageSnapshot(borrowToken);
1297
- const preview = this.previewLeverageDown(newLeverage, currentLeverage);
1394
+ const preview = this.previewLeverageDown(newLeverage, currentLeverage, undefined, snapshot);
1298
1395
  const { collateralAssetReduction } = preview;
1299
1396
  const isFullDeleverage = newLeverage.equals(1);
1300
1397
  const maxTokenCollateral = this.virtualConvertToAssets(this.readFreshUserCache("userCollateral", "executing leverage down"));
@@ -1339,7 +1436,7 @@ class CToken extends Calldata_1.Calldata {
1339
1436
  // Additive approximation is accurate to sub-bp at typical
1340
1437
  // fee+overhead magnitudes (< 100 bps combined).
1341
1438
  const overheadBps = exports.LEVERAGE.DELEVERAGE_OVERHEAD_BPS + feeBps;
1342
- swapCollateral = ceilDiv(debtInCollateral * (10000n + overheadBps), 10000n);
1439
+ swapCollateral = ceilDiv(debtInCollateral * (helpers_1.BPS + overheadBps), helpers_1.BPS);
1343
1440
  if (swapCollateral > maxTokenCollateral) {
1344
1441
  const error = "Selected collateral token does not have enough posted collateral to fully deleverage.";
1345
1442
  if (simulate) {
@@ -1356,7 +1453,7 @@ class CToken extends Calldata_1.Calldata {
1356
1453
  // from input before swapping, so without compensation
1357
1454
  // the swap underdelivers and actual leverage is slightly
1358
1455
  // higher than target.
1359
- swapCollateral = ceilDiv(swapCollateral * 10000n, 10000n - feeBps);
1456
+ swapCollateral = ceilDiv(swapCollateral * helpers_1.BPS, helpers_1.BPS - feeBps);
1360
1457
  }
1361
1458
  }
1362
1459
  if (!isFullDeleverage && swapCollateral > maxTokenCollateral) {
@@ -1366,7 +1463,7 @@ class CToken extends Calldata_1.Calldata {
1366
1463
  }
1367
1464
  throw new Error(error);
1368
1465
  }
1369
- const { action, quote } = await config.dexAgg.quoteAction(manager.address, this.asset.address, borrowToken.asset.address, swapCollateral, slippage, feeBps, feeReceiver);
1466
+ const { action, quote } = await this.currentDexAgg.quoteAction(manager.address, this.asset.address, borrowToken.asset.address, swapCollateral, slippage, feeBps, feeReceiver);
1370
1467
  // Fee-aware slippage expansion for `_swapSafe` is handled by
1371
1468
  // KyberSwap.quoteAction. See KyberSwap.ts for rationale.
1372
1469
  // In the current PositionManager, `repayAssets` is only a
@@ -1423,6 +1520,7 @@ class CToken extends Calldata_1.Calldata {
1423
1520
  }
1424
1521
  async depositAndLeverage(depositAmount, borrow, multiplier, type, slippage_ = (0, decimal_js_1.default)(0.05), simulate = false) {
1425
1522
  try {
1523
+ this.assertBorrowTokenBelongsToMarket(borrow);
1426
1524
  if (multiplier.lte((0, decimal_js_1.default)(1))) {
1427
1525
  if (simulate)
1428
1526
  return { success: false, error: "Multiplier must be greater than 1" };
@@ -1438,9 +1536,14 @@ class CToken extends Calldata_1.Calldata {
1438
1536
  }
1439
1537
  let calldata;
1440
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
+ }
1441
1544
  await this._checkTokenApproval(this.getPositionManagerDepositApprovalTarget(manager), depositAssets);
1442
- await this._getLeverageSnapshot(borrow);
1443
- 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);
1444
1547
  if (preview.borrowAssets === 0n) {
1445
1548
  if (simulate) {
1446
1549
  return {
@@ -1456,7 +1559,7 @@ class CToken extends Calldata_1.Calldata {
1456
1559
  switch (type) {
1457
1560
  case 'simple': {
1458
1561
  const feeReceiver = feeBps > 0n ? this.setup.feePolicy.feeReceiver : undefined;
1459
- const { action, quote } = await this.currentChainConfig.dexAgg.quoteAction(manager.address, borrow.asset.address, this.asset.address, borrowAssets, slippage, feeBps, feeReceiver);
1562
+ const { action, quote } = await this.currentDexAgg.quoteAction(manager.address, borrow.asset.address, this.asset.address, borrowAssets, slippage, feeBps, feeReceiver);
1460
1563
  // Fee-aware slippage expansion for `_swapSafe` is handled by
1461
1564
  // KyberSwap.quoteAction. See KyberSwap.ts for rationale.
1462
1565
  // Fee amplification: same pattern as leverageUp. See
@@ -1512,6 +1615,9 @@ class CToken extends Calldata_1.Calldata {
1512
1615
  receiver ??= signer.address;
1513
1616
  const depositAssets = FormatConverter_1.default.decimalToBigInt(amount, this.asset.decimals);
1514
1617
  const zapAssets = await this.getZapAssetAmount(amount, zap);
1618
+ if (zapAssets <= 0n) {
1619
+ throw new Error("Deposit amount must be greater than zero.");
1620
+ }
1515
1621
  const default_calldata = this.getCallData("deposit", [depositAssets, receiver]);
1516
1622
  const { calldata, calldata_overrides } = await this.zap(zapAssets, zap, false, default_calldata, receiver);
1517
1623
  return this.simulateOracleRoute(calldata, calldata_overrides);
@@ -1527,11 +1633,20 @@ class CToken extends Calldata_1.Calldata {
1527
1633
  receiver ??= signer.address;
1528
1634
  const depositAssets = FormatConverter_1.default.decimalToBigInt(amount, this.asset.decimals);
1529
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);
1530
1641
  const collateralMethod = receiver.toLowerCase() === signer.address.toLowerCase()
1531
1642
  ? "depositAsCollateral"
1532
1643
  : "depositAsCollateralFor";
1533
1644
  const default_calldata = this.getCallData(collateralMethod, [depositAssets, receiver]);
1534
- 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);
1535
1650
  return this.simulateOracleRoute(calldata, calldata_overrides);
1536
1651
  }
1537
1652
  catch (error) {
@@ -1582,7 +1697,12 @@ class CToken extends Calldata_1.Calldata {
1582
1697
  default:
1583
1698
  throw new Error("This zap type is not supported: " + type_of_zap);
1584
1699
  }
1585
- return { calldata, calldata_overrides, zapper };
1700
+ return {
1701
+ calldata,
1702
+ calldata_overrides,
1703
+ zapper,
1704
+ expectedShares: this.getZapperExpectedShares(zapper, calldata),
1705
+ };
1586
1706
  }
1587
1707
  async deposit(amount, zap = 'none', receiver = null) {
1588
1708
  amount = await this.ensureUnderlyingAmount(amount, zap);
@@ -1590,6 +1710,9 @@ class CToken extends Calldata_1.Calldata {
1590
1710
  receiver ??= signer.address;
1591
1711
  const depositAssets = FormatConverter_1.default.decimalToBigInt(amount, this.asset.decimals);
1592
1712
  const zapAssets = await this.getZapAssetAmount(amount, zap);
1713
+ if (zapAssets <= 0n) {
1714
+ throw new Error("Deposit amount must be greater than zero.");
1715
+ }
1593
1716
  await this._checkDepositApprovals(zap, depositAssets, zapAssets);
1594
1717
  const default_calldata = this.getCallData("deposit", [depositAssets, receiver]);
1595
1718
  const { calldata, calldata_overrides } = await this.zap(zapAssets, zap, false, default_calldata, receiver);
@@ -1601,43 +1724,49 @@ class CToken extends Calldata_1.Calldata {
1601
1724
  receiver ??= signer.address;
1602
1725
  const depositAssets = FormatConverter_1.default.decimalToBigInt(amount, this.asset.decimals);
1603
1726
  const zapAssets = await this.getZapAssetAmount(amount, zap);
1604
- if (!this.isZapInstruction(zap)) {
1605
- const collateralCapError = "There is not enough collateral left in this tokens collateral cap for this deposit.";
1606
- const remainingCollateral = this.getRemainingCollateral(false);
1607
- if (remainingCollateral <= 0n)
1608
- throw new Error(collateralCapError);
1609
- if (remainingCollateral > 0n) {
1610
- const shares = this.virtualConvertToShares(depositAssets);
1611
- if (shares > remainingCollateral) {
1612
- throw new Error(collateralCapError);
1613
- }
1614
- }
1727
+ if (zapAssets <= 0n) {
1728
+ throw new Error("Deposit amount must be greater than zero.");
1615
1729
  }
1730
+ const depositShares = this.isZapInstruction(zap) ? undefined : this.virtualConvertToShares(depositAssets);
1731
+ this.assertCollateralCapacity(depositShares);
1616
1732
  await this._checkDepositApprovals(zap, depositAssets, zapAssets, true, receiver);
1617
1733
  const collateralMethod = receiver.toLowerCase() === signer.address.toLowerCase()
1618
1734
  ? "depositAsCollateral"
1619
1735
  : "depositAsCollateralFor";
1620
1736
  const default_calldata = this.getCallData(collateralMethod, [depositAssets, receiver]);
1621
- 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);
1622
1742
  return this.oracleRoute(calldata, calldata_overrides, receiver);
1623
1743
  }
1624
1744
  async redeem(amount) {
1625
1745
  const signer = this.requireSigner();
1626
1746
  const receiver = signer.address;
1627
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
+ }
1628
1752
  const buffer = this.getExecutionDebtBufferTime();
1629
1753
  const balance_avail = await this.balanceOf(signer.address);
1630
1754
  const max_shares = await this.maxRedemption(true, buffer);
1631
- const converted_shares = this.convertTokenInputToShares(amount);
1632
1755
  const maxExecutableShares = max_shares < balance_avail ? max_shares : balance_avail;
1633
1756
  let shares = maxExecutableShares < converted_shares ? maxExecutableShares : converted_shares;
1634
1757
  if (maxExecutableShares === balance_avail && balance_avail - shares <= 10n) {
1635
1758
  shares = balance_avail;
1636
1759
  }
1760
+ if (shares <= 0n) {
1761
+ throw new Error("No redeemable cToken shares available.");
1762
+ }
1637
1763
  const calldata = this.getCallData("redeem", [shares, receiver, owner]);
1638
1764
  return this.oracleRoute(calldata);
1639
1765
  }
1640
1766
  async redeemShares(amount) {
1767
+ if (amount <= 0n) {
1768
+ throw new Error("Redeem amount must be greater than zero.");
1769
+ }
1641
1770
  const signer = this.requireSigner();
1642
1771
  const receiver = signer.address;
1643
1772
  const owner = signer.address;
@@ -1701,36 +1830,6 @@ class CToken extends Calldata_1.Calldata {
1701
1830
  async convertSharesToUsd(tokenAmount) {
1702
1831
  return this.convertSharesToUsdSync(tokenAmount);
1703
1832
  }
1704
- buildMultiCallAction(calldata, target = this.address) {
1705
- return {
1706
- target,
1707
- isPriceUpdate: false,
1708
- data: calldata
1709
- };
1710
- }
1711
- hasNonZeroNativeValueOverride(override) {
1712
- const value = override.value;
1713
- if (value == null) {
1714
- return false;
1715
- }
1716
- if (typeof value === "bigint") {
1717
- return value > 0n;
1718
- }
1719
- if (typeof value === "number") {
1720
- return value > 0;
1721
- }
1722
- if (typeof value === "string") {
1723
- return BigInt(value) > 0n;
1724
- }
1725
- return true;
1726
- }
1727
- assertOracleMulticallSupportsValue(priceUpdates, override) {
1728
- if (priceUpdates.length === 0 || !this.hasNonZeroNativeValueOverride(override)) {
1729
- return;
1730
- }
1731
- throw new Error("Native gas-token zaps cannot be combined with oracle price-update multicalls. " +
1732
- "Use the wrapped-native zap path or retry when no oracle update is required.");
1733
- }
1734
1833
  async _checkPositionManagerApproval(manager) {
1735
1834
  const isApproved = await this.isPluginApproved(manager.type, 'positionManager');
1736
1835
  if (!isApproved) {
@@ -1791,7 +1890,7 @@ class CToken extends Calldata_1.Calldata {
1791
1890
  return null;
1792
1891
  }
1793
1892
  return {
1794
- token: new ERC20_1.ERC20(this.provider, instructions.inputToken, undefined, undefined, this.signer),
1893
+ token: new ERC20_1.ERC20(this.provider, instructions.inputToken, undefined, this.setup.contracts.OracleManager, this.signer),
1795
1894
  spender,
1796
1895
  spenderLabel: `${zapType} Zapper`,
1797
1896
  };
@@ -1851,60 +1950,20 @@ class CToken extends Calldata_1.Calldata {
1851
1950
  }
1852
1951
  async oracleRoute(calldata, override = {}, reloadAccount = null) {
1853
1952
  const signer = this.requireSigner();
1854
- const price_updates = await this.getPriceUpdates();
1855
- this.assertOracleMulticallSupportsValue(price_updates, override);
1856
- if (price_updates.length > 0) {
1857
- const actionTarget = (override.to ?? this.address);
1858
- const token_action = this.buildMultiCallAction(calldata, actionTarget);
1859
- calldata = this.getCallData("multicall", [[...price_updates, token_action]]);
1860
- }
1861
1953
  const tx = await this.executeCallData(calldata, override);
1862
1954
  if (typeof tx.wait === "function") {
1863
1955
  await tx.wait();
1864
1956
  }
1865
- const refreshAccount = reloadAccount ?? signer.address;
1866
- await this.market.reloadUserData(refreshAccount, {
1867
- allowSignerMismatch: refreshAccount.toLowerCase() !== signer.address.toLowerCase(),
1868
- });
1957
+ const signerAddress = signer.address;
1958
+ const refreshAccount = reloadAccount?.toLowerCase() === signerAddress.toLowerCase()
1959
+ ? reloadAccount
1960
+ : signerAddress;
1961
+ await this.market.reloadUserData(refreshAccount);
1869
1962
  return tx;
1870
1963
  }
1871
1964
  async simulateOracleRoute(calldata, override = {}) {
1872
- const price_updates = await this.getPriceUpdates();
1873
- this.assertOracleMulticallSupportsValue(price_updates, override);
1874
- if (price_updates.length > 0) {
1875
- const actionTarget = (override.to ?? this.address);
1876
- const token_action = this.buildMultiCallAction(calldata, actionTarget);
1877
- calldata = this.getCallData("multicall", [[...price_updates, token_action]]);
1878
- }
1879
1965
  return this.simulateCallData(calldata, override);
1880
1966
  }
1881
- getRedstonePriceUpdateTokens() {
1882
- const candidates = (this.market?.tokens?.length ? this.market.tokens : [this]);
1883
- const seenAssets = new Set();
1884
- const tokens = [];
1885
- for (const token of candidates) {
1886
- if (!token.adapters?.includes(ProtocolReader_1.AdaptorTypes.REDSTONE_CORE)) {
1887
- continue;
1888
- }
1889
- let assetAddress;
1890
- try {
1891
- assetAddress = token.getAsset(false);
1892
- }
1893
- catch {
1894
- assetAddress = token.cache?.asset?.address ?? token.address;
1895
- }
1896
- const key = assetAddress.toLowerCase();
1897
- if (seenAssets.has(key)) {
1898
- continue;
1899
- }
1900
- seenAssets.add(key);
1901
- tokens.push(token);
1902
- }
1903
- return tokens;
1904
- }
1905
- async getPriceUpdates() {
1906
- return Promise.all(this.getRedstonePriceUpdateTokens().map((token) => Redstone_1.Redstone.buildMultiCallAction(token)));
1907
- }
1908
1967
  }
1909
1968
  exports.CToken = CToken;
1910
1969
  //# sourceMappingURL=CToken.js.map