@gearbox-protocol/sdk 17.10.0 → 17.10.1-next.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.
Files changed (78) hide show
  1. package/dist/cjs/onchain/accounts/index.js +1 -0
  2. package/dist/cjs/onchain/accounts/intents/guards.js +4 -2
  3. package/dist/cjs/onchain/accounts/intents/index.js +41 -7
  4. package/dist/cjs/onchain/accounts/intents/math.js +3 -44
  5. package/dist/cjs/onchain/accounts/intents/maxBorrow.js +7 -1
  6. package/dist/cjs/onchain/accounts/intents/open-strategy.js +12 -7
  7. package/dist/cjs/onchain/accounts/intents/plan.js +4 -4
  8. package/dist/cjs/onchain/accounts/intents/realize.js +4 -3
  9. package/dist/cjs/onchain/accounts/intents/strategyLimits.js +159 -0
  10. package/dist/cjs/onchain/accounts/intents/tail.js +5 -1
  11. package/dist/cjs/onchain/accounts/intents/testing/sdk-mock.js +4 -0
  12. package/dist/cjs/onchain/accounts/intents/utils/index.js +1 -0
  13. package/dist/cjs/onchain/accounts/intents/utils/quotas-for-update.js +39 -0
  14. package/dist/cjs/onchain/accounts/quota-utils.js +9 -0
  15. package/dist/cjs/onchain/index.js +1 -0
  16. package/dist/cjs/onchain/market/oracle/PriceOracleBaseContract.js +8 -6
  17. package/dist/cjs/onchain/validation/checks/checkBorrowLimit.js +2 -3
  18. package/dist/cjs/onchain/validation/checks/checkDebtLimits.js +3 -4
  19. package/dist/cjs/onchain/validation/checks/checkQuotaLimit.js +2 -1
  20. package/dist/esm/dev/AccountOpener.js +1 -1
  21. package/dist/esm/dev/withdrawalUtils.js +1 -1
  22. package/dist/esm/history/trace/extractTransfers.js +1 -1
  23. package/dist/esm/onchain/accounts/CreditAccountsServiceV310.js +2 -2
  24. package/dist/esm/onchain/accounts/index.js +2 -2
  25. package/dist/esm/onchain/accounts/intents/guards.js +4 -2
  26. package/dist/esm/onchain/accounts/intents/index.js +41 -7
  27. package/dist/esm/onchain/accounts/intents/math.js +5 -45
  28. package/dist/esm/onchain/accounts/intents/maxBorrow.js +7 -1
  29. package/dist/esm/onchain/accounts/intents/open-strategy.js +13 -8
  30. package/dist/esm/onchain/accounts/intents/plan.js +4 -4
  31. package/dist/esm/onchain/accounts/intents/realize.js +4 -3
  32. package/dist/esm/onchain/accounts/intents/strategyLimits.js +158 -0
  33. package/dist/esm/onchain/accounts/intents/tail.js +5 -1
  34. package/dist/esm/onchain/accounts/intents/testing/sdk-mock.js +4 -0
  35. package/dist/esm/onchain/accounts/intents/utils/index.js +2 -2
  36. package/dist/esm/onchain/accounts/intents/utils/quotas-for-update.js +41 -3
  37. package/dist/esm/onchain/accounts/liquidations/LiquidationsService.js +1 -1
  38. package/dist/esm/onchain/accounts/quota-utils.js +9 -1
  39. package/dist/esm/onchain/accounts/withdrawal-compressor/RedemptionLoggerV310Contract.js +1 -1
  40. package/dist/esm/onchain/accounts/withdrawal-compressor/WithdrawalCompressorV310Contract.js +1 -1
  41. package/dist/esm/onchain/accounts/withdrawal-compressor/WithdrawalCompressorV311Contract.js +1 -1
  42. package/dist/esm/onchain/accounts/withdrawal-compressor/WithdrawalCompressorV313Contract.js +1 -1
  43. package/dist/esm/onchain/base/TokensMeta.js +3 -3
  44. package/dist/esm/onchain/core/createAddressProvider.js +1 -1
  45. package/dist/esm/onchain/index.js +2 -2
  46. package/dist/esm/onchain/market/adapters/contracts/AccountMigratorAdapterContract.js +1 -1
  47. package/dist/esm/onchain/market/adapters/contracts/ERC4626AdapterContract.js +1 -1
  48. package/dist/esm/onchain/market/credit/CreditFacadeV310BaseContract.js +1 -1
  49. package/dist/esm/onchain/market/oracle/PriceOracleBaseContract.js +8 -6
  50. package/dist/esm/onchain/market/pool/PoolV310Contract.js +1 -1
  51. package/dist/esm/onchain/market/rwa/createDegenNFT.js +1 -1
  52. package/dist/esm/onchain/market/zapper/IETHZapperContract.js +1 -1
  53. package/dist/esm/onchain/market/zapper/ZapperContract.js +1 -1
  54. package/dist/esm/onchain/preview/preview/previewPoolPositionOperation.js +2 -2
  55. package/dist/esm/onchain/utils/viem/simulateWithPriceUpdates.js +1 -1
  56. package/dist/esm/onchain/validation/checks/checkBorrowLimit.js +2 -3
  57. package/dist/esm/onchain/validation/checks/checkDebtLimits.js +3 -4
  58. package/dist/esm/onchain/validation/checks/checkQuotaLimit.js +2 -1
  59. package/dist/types/model/errors/index.d.ts +2 -2
  60. package/dist/types/model/errors/operation-errors.d.ts +28 -14
  61. package/dist/types/model/index.d.ts +2 -2
  62. package/dist/types/onchain/accounts/index.d.ts +2 -2
  63. package/dist/types/onchain/accounts/intents/guards.d.ts +12 -12
  64. package/dist/types/onchain/accounts/intents/math.d.ts +7 -16
  65. package/dist/types/onchain/accounts/intents/plan.d.ts +2 -0
  66. package/dist/types/onchain/accounts/intents/realize.d.ts +3 -0
  67. package/dist/types/onchain/accounts/intents/strategyLimits.d.ts +33 -0
  68. package/dist/types/onchain/accounts/intents/tail.d.ts +13 -9
  69. package/dist/types/onchain/accounts/intents/utils/index.d.ts +2 -2
  70. package/dist/types/onchain/accounts/intents/utils/quotas-for-update.d.ts +15 -1
  71. package/dist/types/onchain/accounts/quota-utils.d.ts +11 -1
  72. package/dist/types/onchain/index.d.ts +2 -2
  73. package/dist/types/onchain/market/oracle/PriceOracleBaseContract.d.ts +0 -3
  74. package/dist/types/onchain/market/oracle/types.d.ts +4 -3
  75. package/dist/types/onchain/validation/checks/checkBorrowLimit.d.ts +2 -3
  76. package/dist/types/onchain/validation/checks/checkDebtLimits.d.ts +2 -3
  77. package/dist/types/onchain/validation/checks/checkQuotaLimit.d.ts +2 -2
  78. package/package.json +1 -1
@@ -113,6 +113,9 @@ var PriceOracleBaseContract = class extends require_onchain_base_BaseContract.Ba
113
113
  * {@inheritDoc IPriceOracleContract.convert}
114
114
  **/
115
115
  convert(from, to, amount, reserve = false) {
116
+ return this.#convert(from, to, amount, reserve, false);
117
+ }
118
+ #convert(from, to, amount, reserve, roundUp) {
116
119
  if ((0, viem.isAddressEqual)(from, to)) return amount;
117
120
  const fromToken = this.#priceableToken(from);
118
121
  const toToken = this.#priceableToken(to);
@@ -120,7 +123,9 @@ var PriceOracleBaseContract = class extends require_onchain_base_BaseContract.Ba
120
123
  const fromScale = 10n ** BigInt(this.tokensMeta.decimals(fromToken));
121
124
  const toPrice = reserve ? this.reservePrice(toToken) : this.mainPrice(toToken);
122
125
  const toScale = 10n ** BigInt(this.tokensMeta.decimals(toToken));
123
- return amount * fromPrice * toScale / (toPrice * fromScale);
126
+ const numerator = amount * fromPrice * toScale;
127
+ const denominator = toPrice * fromScale;
128
+ return roundUp ? require_onchain_utils_bigint_math.BigIntMath.ceilDiv(numerator, denominator) : numerator / denominator;
124
129
  }
125
130
  /**
126
131
  * {@inheritDoc IPriceOracleContract.convertToUSD}
@@ -141,15 +146,12 @@ var PriceOracleBaseContract = class extends require_onchain_base_BaseContract.Ba
141
146
  const price = reserve ? this.reservePrice(token) : this.mainPrice(token);
142
147
  return amount * 10n ** BigInt(this.tokensMeta.decimals(token)) / price;
143
148
  }
144
- /**
145
- * {@inheritDoc IPriceOracleContract.safeConvert}
146
- **/
147
149
  safeConvert(from, to, amount) {
148
150
  try {
149
- return { value: this.convert(from, to, amount) };
151
+ return { value: this.#convert(from, to, amount, false, true) };
150
152
  } catch {
151
153
  try {
152
- return { value: this.convert(from, to, amount, true) };
154
+ return { value: this.#convert(from, to, amount, true, true) };
153
155
  } catch {
154
156
  return require_model_result.safeValue(0n, require_model_errors_oracle_errors.unpriceableTokenError(from));
155
157
  }
@@ -12,13 +12,12 @@ require("../helpers/index.js");
12
12
  * takeable, left out when none is.
13
13
  */
14
14
  function checkBorrowLimit(args) {
15
- const { requested, available, limit, underlying, maxBorrowAmount } = args;
15
+ const { requested, available, underlying, maxBorrowAmount, ...rest } = args;
16
16
  if (requested <= available) return [];
17
17
  return [require_model_errors_operation_errors.insufficientPoolLiquidity({
18
- collateralLimits: args.getCollateralLimits?.(),
18
+ ...rest,
19
19
  requested: require_onchain_validation_helpers_amount.amountOf(underlying, requested),
20
20
  available: require_onchain_validation_helpers_amount.amountOf(underlying, available),
21
- limit,
22
21
  ...maxBorrowAmount === void 0 ? {} : { maxBorrowAmount: require_onchain_validation_helpers_amount.amountOf(underlying, maxBorrowAmount) }
23
22
  })];
24
23
  }
@@ -6,14 +6,13 @@ require("../helpers/index.js");
6
6
  //#region src/onchain/validation/checks/checkDebtLimits.ts
7
7
  /** A debt the facade would revert on. */
8
8
  function checkDebtLimits(args) {
9
- const { debt, minDebt, maxDebt, underlying, allowZero, maxBorrowAmount } = args;
9
+ const { debt, minDebt, maxDebt, underlying, allowZero, ...rest } = args;
10
10
  if (!(debt > maxDebt || debt < minDebt && !(allowZero && debt === 0n))) return [];
11
11
  return [require_model_errors_operation_errors.debtOutOfRange({
12
- collateralLimits: args.getCollateralLimits?.(),
12
+ ...rest,
13
13
  requested: require_onchain_validation_helpers_amount.amountOf(underlying, debt),
14
14
  minDebt: require_onchain_validation_helpers_amount.amountOf(underlying, minDebt),
15
- maxDebt: require_onchain_validation_helpers_amount.amountOf(underlying, maxDebt),
16
- maxBorrowAmount
15
+ maxDebt: require_onchain_validation_helpers_amount.amountOf(underlying, maxDebt)
17
16
  })];
18
17
  }
19
18
  //#endregion
@@ -6,9 +6,10 @@ require("../helpers/index.js");
6
6
  //#region src/onchain/validation/checks/checkQuotaLimit.ts
7
7
  /** The room the keeper still has for a token's quota, in the underlying. */
8
8
  function checkQuotaLimit(args) {
9
- const { token, requested, available, underlying } = args;
9
+ const { token, requested, available, underlying, ...rest } = args;
10
10
  if (requested !== void 0 && requested <= available) return [];
11
11
  return [require_model_errors_operation_errors.quotaLimitReached({
12
+ ...rest,
12
13
  token,
13
14
  requested: requested === void 0 ? void 0 : require_onchain_validation_helpers_amount.amountOf(underlying, requested),
14
15
  available: require_onchain_validation_helpers_amount.amountOf(underlying, available)
@@ -1,5 +1,5 @@
1
- import { iCreditFacadeV310Abi } from "../abi/310/generated.js";
2
1
  import { ierc20Abi } from "../abi/iERC20.js";
2
+ import { iCreditFacadeV310Abi } from "../abi/310/generated.js";
3
3
  import { AddressMap } from "../onchain/utils/AddressMap.js";
4
4
  import { AddressSet } from "../onchain/utils/AddressSet.js";
5
5
  import { AssetsMap } from "../onchain/utils/AssetsMap.js";
@@ -1,6 +1,6 @@
1
+ import { iWithdrawalCompressorV313Abi } from "../abi/IWithdrawalCompressorV313.js";
1
2
  import { getNetworkType } from "../onchain/chain/chains.js";
2
3
  import { getWithdrawalCompressorAddress } from "../onchain/accounts/withdrawal-compressor/addresses.js";
3
- import { iWithdrawalCompressorV313Abi } from "../abi/IWithdrawalCompressorV313.js";
4
4
  import "../onchain/index.js";
5
5
  import { iMidasDataFeedAbi, iMidasRedemptionVaultAbi, midasGatewayAbi, midasRedeemerAbi, midasRedemptionVaultPhantomTokenAbi, securitizeRedeemerAbi, securitizeRedemptionGatewayAbi, securitizeRedemptionPhantomTokenAbi } from "./withdrawalAbi.js";
6
6
  import { erc20Abi, hexToString, parseAbi, parseEther } from "viem";
@@ -1,5 +1,5 @@
1
- import { iCreditFacadeV310Abi } from "../../abi/310/generated.js";
2
1
  import { ierc20Abi } from "../../abi/iERC20.js";
2
+ import { iCreditFacadeV310Abi } from "../../abi/310/generated.js";
3
3
  import { AddressMap } from "../../onchain/utils/AddressMap.js";
4
4
  import "../../onchain/index.js";
5
5
  import { UnexpectedFacadeEventOrderError } from "./errors.js";
@@ -1,3 +1,5 @@
1
+ import { iBaseRewardPoolAbi } from "../../abi/iBaseRewardPool.js";
2
+ import { ierc4626AdapterAbi } from "../../abi/ierc4626Adapter.js";
1
3
  import { AP_REWARDS_COMPRESSOR } from "../constants/address-provider.js";
2
4
  import { ADDRESS_0X0 } from "../constants/addresses.js";
3
5
  import { MAX_UINT256 } from "../constants/math.js";
@@ -8,8 +10,6 @@ import "../base/index.js";
8
10
  import { AccountBotsService } from "./bots/AccountBotsService.js";
9
11
  import "./bots/index.js";
10
12
  import { rewardsCompressorAbi } from "../../abi/compressors/rewardsCompressor.js";
11
- import { iBaseRewardPoolAbi } from "../../abi/iBaseRewardPool.js";
12
- import { ierc4626AdapterAbi } from "../../abi/ierc4626Adapter.js";
13
13
  import { expectedBalanceDeltas } from "../market/credit/expectedBalanceDeltas.js";
14
14
  import "../market/index.js";
15
15
  import { CreditAccountCompressor } from "./credit-account-compressor/CreditAccountCompressor.js";
@@ -7,7 +7,7 @@ import "./credit-account-compressor/index.js";
7
7
  import { CreditAccountsServiceV310 } from "./CreditAccountsServiceV310.js";
8
8
  import { isPhantomToken } from "./intents/utils/pick-token.js";
9
9
  import { fetchCreditAccountSlice, toCreditAccountSlice } from "./intents/utils/credit-account-slice.js";
10
- import { DIRECT_TRANSFERS_QUOTA, calcDefaultQuota, calcQuotaUpdate, calcRecommendedQuota, roundUpQuota } from "./quota-utils.js";
10
+ import { DIRECT_TRANSFERS_QUOTA, calcDefaultQuota, calcQuotaUpdate, calcRecommendedQuota, maxAmountForQuota, roundUpQuota } from "./quota-utils.js";
11
11
  import { CreditAccountOperationsService } from "./intents/index.js";
12
12
  import { LIQUIDATION_APPROVAL_BUFFER, LIQUIDATION_COMPRESSOR_V313_ADDRESS } from "./liquidations/constants.js";
13
13
  import { LiquidationsService } from "./liquidations/LiquidationsService.js";
@@ -25,4 +25,4 @@ import { toWithdrawalStatus } from "./withdrawal-compressor/types.js";
25
25
  import { WithdrawalCompressorV313Contract } from "./withdrawal-compressor/WithdrawalCompressorV313Contract.js";
26
26
  import { createWithdrawalCompressor } from "./withdrawal-compressor/createWithdrawalCompressor.js";
27
27
  import "./withdrawal-compressor/index.js";
28
- export { AbstractWithdrawalCompressorContract, AccountBotsService, CreditAccountCompressor, CreditAccountCompressorV310Contract, CreditAccountOperationsService, CreditAccountsServiceV310, DELAYED_INTENT_TYPES, DELAYED_INTENT_VERSION, DIRECT_TRANSFERS_QUOTA, LIQUIDATION_APPROVAL_BUFFER, LIQUIDATION_COMPRESSOR_V313_ADDRESS, LiquidationsService, MultichainLiquidationsService, PeripheryCompressorV310Contract, RedemptionLoggerV310Contract, WithdrawalCompressorV310Contract, WithdrawalCompressorV311Contract, WithdrawalCompressorV313Contract, calcDefaultQuota, calcQuotaUpdate, calcRecommendedQuota, createRedemptionLogger, createWithdrawalCompressor, decodeDelayedIntent, encodeDelayedIntent, fetchCreditAccountSlice, getWithdrawalCompressorAddress, iCreditAccountAbi, isPhantomToken, roundUpQuota, toClaimableWithdrawal, toCreditAccountSlice, toPendingWithdrawal, toRequestableWithdrawal, toWithdrawalStatus };
28
+ export { AbstractWithdrawalCompressorContract, AccountBotsService, CreditAccountCompressor, CreditAccountCompressorV310Contract, CreditAccountOperationsService, CreditAccountsServiceV310, DELAYED_INTENT_TYPES, DELAYED_INTENT_VERSION, DIRECT_TRANSFERS_QUOTA, LIQUIDATION_APPROVAL_BUFFER, LIQUIDATION_COMPRESSOR_V313_ADDRESS, LiquidationsService, MultichainLiquidationsService, PeripheryCompressorV310Contract, RedemptionLoggerV310Contract, WithdrawalCompressorV310Contract, WithdrawalCompressorV311Contract, WithdrawalCompressorV313Contract, calcDefaultQuota, calcQuotaUpdate, calcRecommendedQuota, createRedemptionLogger, createWithdrawalCompressor, decodeDelayedIntent, encodeDelayedIntent, fetchCreditAccountSlice, getWithdrawalCompressorAddress, iCreditAccountAbi, isPhantomToken, maxAmountForQuota, roundUpQuota, toClaimableWithdrawal, toCreditAccountSlice, toPendingWithdrawal, toRequestableWithdrawal, toWithdrawalStatus };
@@ -52,7 +52,7 @@ function assertCanBorrow(sdk, suite, amount, options = {}) {
52
52
  available: maxBorrowAmount.amount.value,
53
53
  limit: maxBorrowAmount.limit,
54
54
  underlying: toToken(sdk, suite.market.pool.underlying),
55
- getCollateralLimits: options.getCollateralLimits
55
+ ...options
56
56
  }), `borrow: ${amount} exceeds what the pool can lend now (${maxBorrowAmount.amount.value})`);
57
57
  }
58
58
  /**
@@ -81,6 +81,7 @@ function assertGrowthAllowed(args) {
81
81
  token: toToken(sdk, token),
82
82
  requested: void 0,
83
83
  available: 0n,
84
+ ...args.limits,
84
85
  underlying: toToken(sdk, underlying)
85
86
  }), `${token} takes no quota in this market, so it counts as no collateral`);
86
87
  }
@@ -138,7 +139,7 @@ function assertCollateralised(healthFactorBps, safePrices, atSafePrices) {
138
139
  * A quota can only be raised as far as the market still has room for: past the
139
140
  * token's limit the keeper takes nothing more, whoever is asking.
140
141
  */
141
- function assertQuotaAvailable(sdk, market, increases) {
142
+ function assertQuotaAvailable(sdk, market, increases, options = {}) {
142
143
  const underlying = market.pool.underlying;
143
144
  const { pqk } = market.pool;
144
145
  for (const { token, balance } of increases) {
@@ -148,6 +149,7 @@ function assertQuotaAvailable(sdk, market, increases) {
148
149
  token: toToken(sdk, token),
149
150
  requested: balance,
150
151
  available: left,
152
+ ...options,
151
153
  underlying: toToken(sdk, underlying)
152
154
  }), `${token} has ${left} of quota left, the plan needs ${balance}`);
153
155
  }
@@ -1,3 +1,4 @@
1
+ import { LEVERAGE_DECIMALS } from "../../constants/math.js";
1
2
  import { SDKConstruct } from "../../base/SDKConstruct.js";
2
3
  import { unsupportedTokenPair } from "../../../model/errors/prepare-errors.js";
3
4
  import { sdkErr } from "../../../model/result.js";
@@ -12,6 +13,7 @@ import { buildBorrowState } from "./borrow.js";
12
13
  import { calcLeverageBand } from "./leverage-band.js";
13
14
  import { maxBorrow } from "./maxBorrow.js";
14
15
  import { maxWithdrawCollateral } from "./maxWithdrawCollateral.js";
16
+ import { strategyLimits } from "./strategyLimits.js";
15
17
  import { buildOpenStrategyState } from "./open-strategy.js";
16
18
  import { planAddCollateral, planAdjustLeverage, planAdjustLeverageDelayed, planDeposit, planRepay, planWithdraw, planWithdrawAsset, planWithdrawDelayed } from "./plan.js";
17
19
  import { accountView } from "./view.js";
@@ -36,9 +38,25 @@ var CreditAccountOperationsService = class extends SDKConstruct {
36
38
  * balance)
37
39
  */
38
40
  async startIntent(props) {
41
+ const { intent, sdk, creditAccount, quotaReserve } = props;
42
+ let current;
43
+ let limits = {};
44
+ if (intent.type === "DEPOSIT" || intent.type === "ADJUST_LEVERAGE") {
45
+ current = accountView(creditAccount, sdk);
46
+ limits = strategyLimits({
47
+ ...intent,
48
+ suite: sdk.marketRegister.findCreditManager(creditAccount.creditManager),
49
+ view: current,
50
+ initialQuotas: creditAccount.tokens,
51
+ quotaReserve
52
+ });
53
+ }
39
54
  return plain(await this.#preview(props, () => {
40
55
  const { intent } = props;
41
- const view = accountView(props.creditAccount, props.sdk);
56
+ const view = {
57
+ ...current ?? accountView(props.creditAccount, props.sdk),
58
+ limits
59
+ };
42
60
  switch (intent.type) {
43
61
  case "ADD_COLLATERAL": return planAddCollateral(intent);
44
62
  case "WITHDRAW_ASSET": return planWithdrawAsset(intent, view);
@@ -48,7 +66,7 @@ var CreditAccountOperationsService = class extends SDKConstruct {
48
66
  case "WITHDRAW": return planWithdraw(intent, view);
49
67
  default: throw new Error(`${intent.type} - not implemented`);
50
68
  }
51
- }));
69
+ }, limits));
52
70
  }
53
71
  /**
54
72
  * Both ends of what a `WITHDRAW` can take out, in underlying: the largest
@@ -190,14 +208,28 @@ var CreditAccountOperationsService = class extends SDKConstruct {
190
208
  */
191
209
  async startDelayedIntent(props) {
192
210
  const { intent } = props;
211
+ const current = accountView(props.creditAccount, props.sdk);
212
+ const repayFromSource = current.debt - current.balanceOf(current.underlying);
213
+ const maximum = intent.type === "ADJUST_LEVERAGE" && current.collateral > 0n && repayFromSource > 0n ? LEVERAGE_DECIMALS + (repayFromSource * LEVERAGE_DECIMALS - 1n) / current.collateral : void 0;
214
+ const limits = intent.type === "ADJUST_LEVERAGE" && maximum !== void 0 ? strategyLimits({
215
+ ...intent,
216
+ suite: props.sdk.marketRegister.findCreditManager(props.creditAccount.creditManager),
217
+ view: current,
218
+ initialQuotas: props.creditAccount.tokens,
219
+ quotaReserve: props.quotaReserve,
220
+ maxLeverage: maximum
221
+ }) : {};
193
222
  const result = await this.#preview(props, () => {
194
- const view = accountView(props.creditAccount, props.sdk);
223
+ const view = {
224
+ ...current,
225
+ limits
226
+ };
195
227
  switch (intent.type) {
196
228
  case "ADJUST_LEVERAGE": return planAdjustLeverageDelayed(intent, view);
197
229
  case "WITHDRAW": return planWithdrawDelayed(intent, view);
198
230
  default: throw new Error(`${intent.type} - cannot be delayed`);
199
231
  }
200
- });
232
+ }, limits);
201
233
  if (!result.ok) return result;
202
234
  const { delayed } = result;
203
235
  if (!delayed) throw new Error("startDelayedIntent: plan started no withdrawal");
@@ -213,7 +245,8 @@ var CreditAccountOperationsService = class extends SDKConstruct {
213
245
  delayed,
214
246
  creditAccount: props.creditAccount,
215
247
  sdk: props.sdk,
216
- quotaReserve: props.quotaReserve
248
+ quotaReserve: props.quotaReserve,
249
+ limits
217
250
  });
218
251
  return {
219
252
  ...result,
@@ -399,7 +432,7 @@ var CreditAccountOperationsService = class extends SDKConstruct {
399
432
  }
400
433
  }
401
434
  /** Plan → realise → wrap. Unviable requests become `{ ok: false }`. */
402
- async #preview(props, plan) {
435
+ async #preview(props, plan, limits = {}) {
403
436
  const draft = props.draft ?? {};
404
437
  try {
405
438
  const suite = props.sdk.marketRegister.findCreditManager(props.creditAccount.creditManager);
@@ -410,7 +443,8 @@ var CreditAccountOperationsService = class extends SDKConstruct {
410
443
  sdk: props.sdk,
411
444
  slippage: props.slippage ?? 0,
412
445
  quotaReserve: props.quotaReserve,
413
- draft
446
+ draft,
447
+ limits
414
448
  });
415
449
  return {
416
450
  ok: true,
@@ -1,6 +1,5 @@
1
1
  import { BigIntMath } from "../../utils/bigint-math.js";
2
- import { LEVERAGE_DECIMALS, MAX_UINT256, PERCENTAGE_FACTOR } from "../../constants/math.js";
3
- import "../../constants/index.js";
2
+ import { LEVERAGE_DECIMALS, PERCENTAGE_FACTOR } from "../../constants/math.js";
4
3
  import { insufficientBalance } from "../../../model/errors/operation-errors.js";
5
4
  import "../../../model/index.js";
6
5
  import { toToken } from "../../validation/helpers/token.js";
@@ -58,53 +57,14 @@ function assertLeverageAtLeastOne(leverage) {
58
57
  * may end owing nothing, an opening may not.
59
58
  */
60
59
  function assertDebtLimits(sdk, debt, debtLimits, underlying, options = {}) {
61
- const { allowZero = true, getCollateralLimits, maxBorrowAmount } = options;
60
+ const { allowZero = true, ...rest } = options;
62
61
  raise(checkDebtLimits({
63
62
  debt,
64
- minDebt: debtLimits.minDebt,
65
- maxDebt: debtLimits.maxDebt,
63
+ ...debtLimits,
66
64
  underlying: toToken(sdk, underlying),
67
65
  allowZero,
68
- getCollateralLimits,
69
- maxBorrowAmount
66
+ ...rest
70
67
  }), debt > debtLimits.maxDebt ? `debt ${debt} exceeds maxDebt ${debtLimits.maxDebt}` : `debt ${debt} is below minDebt ${debtLimits.minDebt}`);
71
68
  }
72
- /** Finds single-token Open amounts whose projected debt fits the market limits. */
73
- function openCollateralForDebtLimits({ suite, collateral, leverage }) {
74
- if (collateral.length !== 1 || leverage <= 100n) return void 0;
75
- const [{ token }] = collateral;
76
- const { market } = suite;
77
- const limits = {
78
- minDebt: suite.creditFacade.minDebt,
79
- maxDebt: suite.maxBorrowAmount().amount.value
80
- };
81
- if (limits.maxDebt < limits.minDebt) return void 0;
82
- const debtAt = (amount) => {
83
- const converted = market.priceOracle.safeConvert(token, market.pool.underlying, amount);
84
- return converted.error ? void 0 : debtForLeverage(converted.value, leverage);
85
- };
86
- const lastDebt = debtAt(MAX_UINT256);
87
- if (lastDebt === void 0 || lastDebt < limits.minDebt) return void 0;
88
- const firstAtLeast = (threshold) => {
89
- let low = 0n;
90
- let high = MAX_UINT256;
91
- while (low < high) {
92
- const middle = (low + high) / 2n;
93
- const debt = debtAt(middle);
94
- if (debt === void 0) return void 0;
95
- if (debt < threshold) low = middle + 1n;
96
- else high = middle;
97
- }
98
- return low;
99
- };
100
- const min = firstAtLeast(limits.minDebt > 0n ? limits.minDebt : 1n);
101
- const above = lastDebt > limits.maxDebt ? firstAtLeast(limits.maxDebt + 1n) : void 0;
102
- if (min === void 0 || lastDebt > limits.maxDebt && above === void 0) return void 0;
103
- const max = above === void 0 ? MAX_UINT256 : above - 1n;
104
- return min <= max ? {
105
- min: market.priceOracle.toTokenAmount(token, min),
106
- max: market.priceOracle.toTokenAmount(token, max)
107
- } : void 0;
108
- }
109
69
  //#endregion
110
- export { assertDebtLimits, assertLeverageAtLeastOne, debtForLeverage, maxProportionalWithdrawal, openCollateralForDebtLimits, proportionalDebt };
70
+ export { assertDebtLimits, assertLeverageAtLeastOne, debtForLeverage, maxProportionalWithdrawal, proportionalDebt };
@@ -85,7 +85,13 @@ function maxBorrow(props) {
85
85
  if (lends < suite.creditFacade.minDebt) return 0n;
86
86
  const ceiling = BigIntMath.min(priceOracle.safeConvertFromUSD(underlying, backed / targetHF).value, lends);
87
87
  const unwrapsPayout = !!rwaAsset && eq(borrowToken, rwaAsset);
88
- return eq(borrowToken, underlying) ? ceiling : unwrapsPayout ? toTargetDecimals(ceiling, underlying, borrowToken, sdk) : priceOracle.safeConvert(underlying, borrowToken, ceiling).value;
88
+ if (eq(borrowToken, underlying)) return ceiling;
89
+ if (unwrapsPayout) return toTargetDecimals(ceiling, underlying, borrowToken, sdk);
90
+ const payout = priceOracle.safeConvert(underlying, borrowToken, ceiling);
91
+ if (payout.error) return 0n;
92
+ const debt = priceOracle.safeConvert(borrowToken, underlying, payout.value);
93
+ if (debt.error) return 0n;
94
+ return BigIntMath.max(payout.value - (debt.value > ceiling ? 1n : 0n), 0n);
89
95
  }
90
96
  //#endregion
91
97
  export { maxBorrow };
@@ -2,12 +2,13 @@ import { insufficientBalance } from "../../../model/errors/operation-errors.js";
2
2
  import "../../../model/index.js";
3
3
  import { IntentPreviewError } from "../../validation/raise.js";
4
4
  import { assertCanBorrow, assertCollateralised, assertGrowthAllowed, assertMarketOperable, assertQuotaAvailable } from "./guards.js";
5
- import { assertDebtLimits, assertLeverageAtLeastOne, debtForLeverage, openCollateralForDebtLimits } from "./math.js";
5
+ import { assertDebtLimits, assertLeverageAtLeastOne, debtForLeverage } from "./math.js";
6
6
  import { unopenedAccountSlice } from "./utils/credit-account-slice.js";
7
7
  import { collectPriceImpact } from "./utils/price-impact.js";
8
8
  import { getQuotasForUpdate } from "./utils/quotas-for-update.js";
9
9
  import { createRouterPaths } from "./utils/router-path.js";
10
10
  import "./utils/index.js";
11
+ import { strategyLimits } from "./strategyLimits.js";
11
12
  //#region src/onchain/accounts/intents/open-strategy.ts
12
13
  /**
13
14
  * Builds the state opening a leveraged position out of wallet collateral would
@@ -44,17 +45,20 @@ async function buildOpenStrategyState(props) {
44
45
  creditFacade: suite.creditFacade.address,
45
46
  underlying
46
47
  });
47
- const getCollateralLimits = () => openCollateralForDebtLimits({
48
+ const options = leftoverBalances.length || existing?.tokens.some((t) => t.balance > 0n) ? void 0 : strategyLimits({
49
+ type: "OPEN",
48
50
  suite,
49
51
  collateral,
50
- leverage
52
+ leverage,
53
+ targetToken,
54
+ quotaReserve
51
55
  });
52
56
  assertDebtLimits(sdk, debt, suite.creditFacade, underlying, {
57
+ ...options,
53
58
  allowZero: false,
54
- maxBorrowAmount: suite.maxBorrowAmount(),
55
- getCollateralLimits
59
+ maxBorrowAmount: suite.maxBorrowAmount()
56
60
  });
57
- assertCanBorrow(sdk, suite, debt, { getCollateralLimits });
61
+ assertCanBorrow(sdk, suite, debt, options);
58
62
  const paths = createRouterPaths({
59
63
  sdk,
60
64
  creditAccount: account,
@@ -123,9 +127,10 @@ async function buildOpenStrategyState(props) {
123
127
  suite,
124
128
  market,
125
129
  before: [],
126
- after: averageAssets
130
+ after: averageAssets,
131
+ limits: options
127
132
  });
128
- assertQuotaAvailable(sdk, market, averageQuota);
133
+ assertQuotaAvailable(sdk, market, averageQuota, options);
129
134
  const projection = reached ?? settle();
130
135
  assertCollateralised(projection.healthFactor, false);
131
136
  const priceImpact = await collectPriceImpact(leg.probe ? [leg.probe] : [], {
@@ -42,7 +42,7 @@ function planDeposit(intent, view) {
42
42
  const aU = view.price(intent.token, U, intent.amount);
43
43
  const debtDelta = intent.targetLeverage === void 0 ? proportionalDebt(view, aU) : debtForLeverage(view.collateral + aU, intent.targetLeverage) - view.debt;
44
44
  if (debtDelta < 0n) throw new IntentPreviewError(leverageOutOfRange(), `deposit: target leverage ${intent.targetLeverage} would require repaying debt`);
45
- assertDebtLimits(view.sdk, view.debt + debtDelta, view.debtLimits, U);
45
+ assertDebtLimits(view.sdk, view.debt + debtDelta, view.debtLimits, U, view.limits);
46
46
  const T = intent.positionToken ?? positionToken(view, "deposit");
47
47
  const depositStays = eq(intent.token, T);
48
48
  return [
@@ -77,7 +77,7 @@ function planRepay(intent, view) {
77
77
  }), "repay: the account owes nothing");
78
78
  const funding = everything(intent.amount) ? view.price(U, intent.token, withMargin(view.debt)) : intent.amount;
79
79
  const repaid = min(view.price(intent.token, U, funding), view.debt);
80
- assertDebtLimits(view.sdk, view.debt - repaid, view.debtLimits, view.underlying);
80
+ assertDebtLimits(view.sdk, view.debt - repaid, view.debtLimits, view.underlying, view.limits);
81
81
  return [
82
82
  add(intent.token, funding, intent.value),
83
83
  ...fundsInU ? [] : [convert(intent.token, U, funding)],
@@ -306,7 +306,7 @@ function withdrawShape(intent, view) {
306
306
  };
307
307
  }
308
308
  const dD = proportionalDebt(view, WU);
309
- assertDebtLimits(view.sdk, view.debt - dD, view.debtLimits, view.underlying);
309
+ assertDebtLimits(view.sdk, view.debt - dD, view.debtLimits, view.underlying, view.limits);
310
310
  return {
311
311
  U,
312
312
  T,
@@ -325,7 +325,7 @@ function leverageShape(intent, view) {
325
325
  assertLeverageAtLeastOne(intent.targetLeverage);
326
326
  if (view.collateral <= 0n) throw new IntentPreviewError(insufficientBalance(), "adjustLeverage: account has no collateral to lever");
327
327
  const target = debtForLeverage(view.collateral, intent.targetLeverage);
328
- assertDebtLimits(view.sdk, target, view.debtLimits, view.underlying);
328
+ assertDebtLimits(view.sdk, target, view.debtLimits, view.underlying, view.limits);
329
329
  return {
330
330
  U: view.underlying,
331
331
  delta: target - view.debt
@@ -130,7 +130,7 @@ async function realize(steps, props) {
130
130
  }));
131
131
  break;
132
132
  case "borrow":
133
- assertCanBorrow(sdk, suite, step.amount);
133
+ assertCanBorrow(sdk, suite, step.amount, props.limits);
134
134
  push(buildIncreaseDebtOperation({
135
135
  amount: step.amount,
136
136
  creditAccount,
@@ -394,10 +394,11 @@ async function realize(steps, props) {
394
394
  suite,
395
395
  market,
396
396
  before: creditAccount.tokens,
397
- after: projected.assets
397
+ after: projected.assets,
398
+ limits: props.limits
398
399
  });
399
400
  if (!cleared && quotas.quotaIncrease.length + quotas.quotaDecrease.length > 0) {
400
- assertQuotaAvailable(sdk, market, quotas.quotaIncrease);
401
+ assertQuotaAvailable(sdk, market, quotas.quotaIncrease, props.limits);
401
402
  push(buildQuotaUpdateOperation({
402
403
  update: quotas,
403
404
  creditAccount,
@@ -0,0 +1,158 @@
1
+ import { BigIntMath } from "../../utils/bigint-math.js";
2
+ import { LEVERAGE_DECIMALS, MAX_UINT256 } from "../../constants/math.js";
3
+ import "../../constants/index.js";
4
+ import { quotaIncreaseLimit } from "./utils/quotas-for-update.js";
5
+ //#region src/onchain/accounts/intents/strategyLimits.ts
6
+ /** Independent input bounds supplied to debt, liquidity and quota errors. */
7
+ function strategyLimits(props) {
8
+ const { suite, quotaReserve } = props;
9
+ const { market } = suite;
10
+ const { priceOracle: oracle } = market;
11
+ switch (props.type) {
12
+ case "OPEN": {
13
+ const { collateral, leverage, targetToken } = props;
14
+ if (collateral.length !== 1 || leverage <= 100n) return {};
15
+ const [{ token }] = collateral;
16
+ const minDebt = suite.creditFacade.minDebt;
17
+ const maxDebt = suite.maxBorrowAmount().amount.value;
18
+ if (maxDebt < minDebt) return {};
19
+ const minCollateralForDebt = (debt) => {
20
+ const collateralUnderlying = BigIntMath.ceilDiv(debt * LEVERAGE_DECIMALS, leverage - LEVERAGE_DECIMALS);
21
+ const result = oracle.safeConvert(market.pool.underlying, token, collateralUnderlying - 1n);
22
+ if (result.error) return void 0;
23
+ const valuation = oracle.safeConvert(token, market.pool.underlying, result.value);
24
+ if (valuation.error) return void 0;
25
+ const collateral = result.value + (valuation.value < collateralUnderlying ? 1n : 0n);
26
+ const checked = oracle.safeConvert(token, market.pool.underlying, collateral);
27
+ return checked.error || checked.value < collateralUnderlying ? void 0 : collateral;
28
+ };
29
+ if (oracle.safeConvert(token, market.pool.underlying, 1n).error) return {};
30
+ const minCollateral = minCollateralForDebt(BigIntMath.max(minDebt, 1n));
31
+ const excessCollateral = minCollateralForDebt(maxDebt + 1n);
32
+ if (minCollateral === void 0 || excessCollateral === void 0 || minCollateral > 115792089237316195423570985008687907853269984665640564039457584007913129639935n) return {};
33
+ const maxCollateral = BigIntMath.min(MAX_UINT256, excessCollateral - 1n);
34
+ let maxCollateralByQuota;
35
+ if (!market.isUnderlyingLike(targetToken) && !market.pool.pqk.hasActiveQuota(targetToken)) maxCollateralByQuota = 0n;
36
+ else {
37
+ const quotaResult = quotaIncreaseLimit({
38
+ suite,
39
+ token: targetToken,
40
+ balance: 0n,
41
+ initialQuotas: [],
42
+ quotaReserve
43
+ });
44
+ if (!quotaResult.error && quotaResult.value !== void 0) {
45
+ const excessUnderlying = BigIntMath.ceilDiv((BigIntMath.max(quotaResult.value, 0n) + 1n) * LEVERAGE_DECIMALS, leverage);
46
+ const result = oracle.safeConvert(market.pool.underlying, token, excessUnderlying - 1n);
47
+ if (!result.error) {
48
+ const valuation = oracle.safeConvert(token, market.pool.underlying, result.value);
49
+ if (!valuation.error) {
50
+ const collateral = result.value - (valuation.value >= excessUnderlying ? 1n : 0n);
51
+ maxCollateralByQuota = BigIntMath.min(MAX_UINT256, BigIntMath.max(collateral, 0n));
52
+ }
53
+ }
54
+ }
55
+ }
56
+ return {
57
+ collateralLimits: minCollateral <= maxCollateral ? {
58
+ min: oracle.toTokenAmount(token, minCollateral),
59
+ max: oracle.toTokenAmount(token, maxCollateral)
60
+ } : void 0,
61
+ quotaLimits: maxCollateralByQuota === void 0 ? void 0 : { collateralMax: oracle.toTokenAmount(token, maxCollateralByQuota) }
62
+ };
63
+ }
64
+ case "DEPOSIT": {
65
+ const { view, token, positionToken, initialQuotas, targetLeverage } = props;
66
+ if (targetLeverage !== void 0 || view.collateral <= 0n) return {};
67
+ const minDebt = BigIntMath.max(view.debt, suite.creditFacade.minDebt);
68
+ const maxDebt = BigIntMath.min(suite.creditFacade.maxDebt, view.debt + suite.maxBorrowAmount().amount.value);
69
+ const minCollateralForDebt = (debt) => {
70
+ if (debt <= view.debt) return 0n;
71
+ if (view.debt === 0n) return void 0;
72
+ const collateralUnderlying = BigIntMath.ceilDiv((debt - view.debt) * view.collateral, view.debt);
73
+ const result = oracle.safeConvert(view.underlying, token, collateralUnderlying - 1n);
74
+ if (result.error) return void 0;
75
+ const valuation = oracle.safeConvert(token, view.underlying, result.value);
76
+ if (valuation.error) return void 0;
77
+ const collateral = result.value + (valuation.value < collateralUnderlying ? 1n : 0n);
78
+ const checked = oracle.safeConvert(token, view.underlying, collateral);
79
+ return checked.error || checked.value < collateralUnderlying ? void 0 : collateral;
80
+ };
81
+ const debtFloorCollateral = minCollateralForDebt(minDebt);
82
+ const minCollateral = debtFloorCollateral === void 0 ? void 0 : BigIntMath.max(1n, debtFloorCollateral);
83
+ const excessCollateral = debtFloorCollateral === void 0 ? void 0 : minCollateralForDebt(maxDebt + 1n);
84
+ const maxCollateral = excessCollateral === void 0 ? MAX_UINT256 : BigIntMath.min(MAX_UINT256, excessCollateral - 1n);
85
+ const position = positionToken ?? view.fattest([view.underlying]);
86
+ let maxCollateralByQuota;
87
+ if (position) {
88
+ if (!market.isUnderlyingLike(position) && !market.pool.pqk.hasActiveQuota(position)) maxCollateralByQuota = 0n;
89
+ else {
90
+ const quotaResult = quotaIncreaseLimit({
91
+ suite,
92
+ token: position,
93
+ balance: view.balanceOf(position),
94
+ initialQuotas,
95
+ quotaReserve,
96
+ rwaAsset: view.rwaAsset
97
+ });
98
+ maxCollateralByQuota = MAX_UINT256;
99
+ if (!quotaResult.error && quotaResult.value !== void 0) {
100
+ const excessUnderlying = BigIntMath.ceilDiv((quotaResult.value + 1n) * view.collateral, view.collateral + view.debt);
101
+ const result = oracle.safeConvert(view.underlying, token, BigIntMath.max(excessUnderlying - 1n, 0n));
102
+ maxCollateralByQuota = void 0;
103
+ if (!result.error) {
104
+ const valuation = oracle.safeConvert(token, view.underlying, result.value);
105
+ if (!valuation.error) {
106
+ const collateral = result.value - (valuation.value >= excessUnderlying ? 1n : 0n);
107
+ maxCollateralByQuota = BigIntMath.min(MAX_UINT256, BigIntMath.max(collateral, 0n));
108
+ }
109
+ }
110
+ }
111
+ }
112
+ }
113
+ return {
114
+ collateralLimits: minCollateral !== void 0 && minCollateral <= maxCollateral ? {
115
+ min: oracle.toTokenAmount(token, minCollateral),
116
+ max: oracle.toTokenAmount(token, maxCollateral)
117
+ } : void 0,
118
+ quotaLimits: maxCollateralByQuota === void 0 ? void 0 : { collateralMax: oracle.toTokenAmount(token, maxCollateralByQuota) }
119
+ };
120
+ }
121
+ case "ADJUST_LEVERAGE": {
122
+ const { view, token, initialQuotas, targetLeverage, maxLeverage = MAX_UINT256 } = props;
123
+ if (view.collateral <= 0n || targetLeverage < 100n) return {};
124
+ const maxDebt = BigIntMath.min(suite.creditFacade.maxDebt, view.debt + suite.maxBorrowAmount().amount.value);
125
+ const minLeverage = BigIntMath.max(LEVERAGE_DECIMALS + 1n, LEVERAGE_DECIMALS + BigIntMath.ceilDiv(suite.creditFacade.minDebt * LEVERAGE_DECIMALS, view.collateral));
126
+ const debtMaxLeverage = BigIntMath.min(maxLeverage, LEVERAGE_DECIMALS + BigIntMath.ceilDiv((maxDebt + 1n) * LEVERAGE_DECIMALS, view.collateral) - 1n);
127
+ const position = token ?? view.fattest([view.underlying]);
128
+ let maxLeverageByQuota;
129
+ if (position) {
130
+ if (!market.isUnderlyingLike(position) && !market.pool.pqk.hasActiveQuota(position)) maxLeverageByQuota = LEVERAGE_DECIMALS;
131
+ else {
132
+ const quotaResult = quotaIncreaseLimit({
133
+ suite,
134
+ token: position,
135
+ balance: view.balanceOf(position),
136
+ initialQuotas,
137
+ quotaReserve,
138
+ rwaAsset: view.rwaAsset
139
+ });
140
+ maxLeverageByQuota = maxLeverage;
141
+ if (!quotaResult.error && quotaResult.value !== void 0) {
142
+ const quotaMaxDebt = view.debt + quotaResult.value;
143
+ maxLeverageByQuota = BigIntMath.min(maxLeverage, LEVERAGE_DECIMALS + BigIntMath.ceilDiv((quotaMaxDebt + 1n) * LEVERAGE_DECIMALS, view.collateral) - 1n);
144
+ }
145
+ }
146
+ }
147
+ return {
148
+ leverageLimits: minLeverage <= debtMaxLeverage ? {
149
+ min: minLeverage,
150
+ max: debtMaxLeverage
151
+ } : void 0,
152
+ quotaLimits: maxLeverageByQuota === void 0 ? void 0 : { leverageMax: maxLeverageByQuota }
153
+ };
154
+ }
155
+ }
156
+ }
157
+ //#endregion
158
+ export { strategyLimits };