@gearbox-protocol/sdk 17.1.1 → 17.2.0-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.
@@ -21,16 +21,18 @@ const require_onchain_accounts_intents_collateral_valuation = require("./collate
21
21
  * prices, under its liquidation threshold, capped by the quota the borrow
22
22
  * buys for it, all of which is {@link collateralValuation}'s business. The ceiling
23
23
  * is then held to what the market will actually lend: the pool's free
24
- * liquidity, the manager's own allowance and the facade's `maxDebt`, whichever
25
- * binds first.
24
+ * liquidity, the manager's own allowance, the facade's `maxDebt` and the
25
+ * remaining quota of the strategy target collateral, whichever binds first.
26
26
  *
27
- * The facade's `minDebt` is deliberately not applied. It is a floor, and a
28
- * ceiling answered as `0n` because the collateral is too small for this market
29
- * would tell a form nothing about what it is holding — the number a user needs
30
- * to see is the one they are short of. Collateral that carries something
31
- * therefore answers with it, whether or not the market would lend that little;
32
- * a loan under the floor is refused by `borrow` itself, with `debtOutOfRange`
33
- * naming both ends.
27
+ * The facade's `minDebt` is not applied to the collateral's own ceiling. It
28
+ * is a floor, and a ceiling answered as `0n` because the collateral is too
29
+ * small for this market would tell a form nothing about what it is holding —
30
+ * the number a user needs to see is the one they are short of. Collateral
31
+ * that carries something therefore answers with it, whether or not the market
32
+ * would lend that little; a loan under the floor is refused by `borrow`
33
+ * itself, with `debtOutOfRange` naming both ends. A market whose own capacity
34
+ * is under `minDebt` is different: `maxStrategyBorrowAmount` answers `0n`,
35
+ * because no loan of any size exists there.
34
36
  *
35
37
  * Nothing is fetched or simulated — the account does not exist yet and every
36
38
  * input is loaded market state, so a form can call this on each keystroke.
@@ -81,7 +83,7 @@ function maxBorrow(props) {
81
83
  const weighted = valuation.checkedUsd(holding) * valuation.lt(collateralToken);
82
84
  const backed = quotas.some((q) => require_onchain_accounts_intents_utils_common.eq(q.token, collateralToken)) ? require_onchain_utils_bigint_math.BigIntMath.min(valuation.quotaValue(holding), weighted) : weighted;
83
85
  if (backed <= 0n) return 0n;
84
- const ceiling = require_onchain_utils_bigint_math.BigIntMath.min(priceOracle.safeConvertFromUSD(underlying, backed / targetHF).value, suite.maxBorrowAmount().amount.value);
86
+ const ceiling = require_onchain_utils_bigint_math.BigIntMath.min(priceOracle.safeConvertFromUSD(underlying, backed / targetHF).value, suite.maxStrategyBorrowAmount().amount.value);
85
87
  const unwrapsPayout = !!rwaAsset && require_onchain_accounts_intents_utils_common.eq(borrowToken, rwaAsset);
86
88
  return require_onchain_accounts_intents_utils_common.eq(borrowToken, underlying) ? ceiling : unwrapsPayout ? require_onchain_accounts_intents_utils_common.toTargetDecimals(ceiling, underlying, borrowToken, sdk) : priceOracle.safeConvert(underlying, borrowToken, ceiling).value;
87
89
  }
@@ -198,6 +198,7 @@ function buildMockSdk(args) {
198
198
  creditOperationMarket: require_onchain_market_credit_CreditSuite.CreditSuite.prototype.creditOperationMarket,
199
199
  isForbidden: require_onchain_market_credit_CreditSuite.CreditSuite.prototype.isForbidden,
200
200
  maxBorrowAmount: require_onchain_market_credit_CreditSuite.CreditSuite.prototype.maxBorrowAmount,
201
+ maxStrategyBorrowAmount: require_onchain_market_credit_CreditSuite.CreditSuite.prototype.maxStrategyBorrowAmount,
201
202
  creditManager: {
202
203
  address: args.creditManager,
203
204
  liquidationThresholds,
@@ -19,7 +19,7 @@ let viem = require("viem");
19
19
  /**
20
20
  * Amount of underlying seeded into each pool at market creation to protect
21
21
  * from inflation attacks, in raw token units. A suite whose
22
- * {@link CreditSuite.maxBorrowAmount} is at or below this is treated as
22
+ * {@link CreditSuite.maxStrategyBorrowAmount} is at or below this is treated as
23
23
  * having nothing left to lend.
24
24
  **/
25
25
  const MIN_STRATEGY_BORROW_AMOUNT = 100000n;
@@ -282,7 +282,7 @@ var CreditSuite = class extends require_onchain_base_SDKConstruct.SDKConstruct {
282
282
  return this.forbiddenTokens.some((f) => (0, viem.isAddressEqual)(f, token));
283
283
  }
284
284
  /**
285
- * Largest debt one new position can take from this credit manager right now,
285
+ * Largest debt this credit manager will hand out on one operation right now,
286
286
  * and which limit set that number.
287
287
  *
288
288
  * Minimum of:
@@ -291,6 +291,10 @@ var CreditSuite = class extends require_onchain_base_SDKConstruct.SDKConstruct {
291
291
  * - the facade's per-account `maxDebt`.
292
292
  * While `maxDebtPerBlockMultiplier` is `0` the facade
293
293
  * takes no new debt at all, so the answer is `0`.
294
+ *
295
+ * These are the bounds every debt increase answers to, an existing account's
296
+ * included, which is what the guards hold a simulation to. Opening a position
297
+ * answers to two more — see {@link maxStrategyBorrowAmount}.
294
298
  */
295
299
  maxBorrowAmount() {
296
300
  const { pool } = this.market.pool;
@@ -320,6 +324,42 @@ var CreditSuite = class extends require_onchain_base_SDKConstruct.SDKConstruct {
320
324
  };
321
325
  }
322
326
  /**
327
+ * Largest debt one new position can take from this credit manager right now,
328
+ * and which limit set that number.
329
+ *
330
+ * {@link maxBorrowAmount} held to the two bounds only a position being opened
331
+ * answers to: the remaining quota of the strategy target collateral, which
332
+ * the position has to buy to be worth anything, and the facade's `minDebt`,
333
+ * which a first debt cannot sit under. `amount` is `0` whenever no position
334
+ * can be opened right now, and `limit` names why.
335
+ *
336
+ * An operation on an account that already exists is held to neither: its
337
+ * quota is weighed against the token its own plan buys, and its debt is
338
+ * already over the floor, so a top-up smaller than `minDebt` is legal.
339
+ */
340
+ maxStrategyBorrowAmount() {
341
+ const lends = this.maxBorrowAmount();
342
+ if (lends.limit === "debtPerBlockLimit") return lends;
343
+ const collateral = this.strategyTargetCollateral;
344
+ let value = lends.amount.value;
345
+ let limit = lends.limit;
346
+ if (collateral !== void 0) {
347
+ const quota = this.market.pool.pqk.quotaAvailable(collateral);
348
+ if (quota < value) {
349
+ value = quota;
350
+ limit = "quotaAvailable";
351
+ }
352
+ }
353
+ if (value < this.creditFacade.minDebt) return {
354
+ amount: this.market.toUnderlyingAmount(0n),
355
+ limit: "minDebt"
356
+ };
357
+ return {
358
+ amount: this.market.toUnderlyingAmount(value),
359
+ limit
360
+ };
361
+ }
362
+ /**
323
363
  * The single target collateral of this suite's strategy, or `undefined` when
324
364
  * none can be resolved.
325
365
  *
@@ -376,7 +416,7 @@ var CreditSuite = class extends require_onchain_base_SDKConstruct.SDKConstruct {
376
416
  * or `undefined` when credit suite does not offer a strategy opportunity.
377
417
  */
378
418
  strategyOpportunity() {
379
- const maxBorrowAmount = this.maxBorrowAmount().amount.value;
419
+ const maxBorrowAmount = this.maxStrategyBorrowAmount().amount.value;
380
420
  if (maxBorrowAmount <= MIN_STRATEGY_BORROW_AMOUNT) return;
381
421
  const collateral = this.strategyTargetCollateral;
382
422
  if (!collateral) return;
@@ -1,6 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_abi_310_generated = require("../../../abi/310/generated.js");
3
3
  const require_onchain_utils_AddressMap = require("../../utils/AddressMap.js");
4
+ const require_onchain_utils_bigint_math = require("../../utils/bigint-math.js");
4
5
  const require_onchain_utils_formatter = require("../../utils/formatter.js");
5
6
  require("../../utils/index.js");
6
7
  const require_onchain_base_BaseContract = require("../../base/BaseContract.js");
@@ -41,10 +42,12 @@ var PoolQuotaKeeperV310Contract = class extends require_onchain_base_BaseContrac
41
42
  /**
42
43
  * How much more quota the market will take for a token, in the underlying.
43
44
  * `0n` when the market has no quota entry; not the same as {@link hasActiveQuota}.
45
+ * Never negative: a limit lowered under what is already quoted leaves no
46
+ * room, not a debt.
44
47
  */
45
48
  quotaAvailable(token) {
46
49
  const quota = this.quotas.get(token);
47
- return quota ? quota.limit - quota.totalQuoted : 0n;
50
+ return quota ? require_onchain_utils_bigint_math.BigIntMath.max(0n, quota.limit - quota.totalQuoted) : 0n;
48
51
  }
49
52
  /**
50
53
  * Annual quota rate paid on a quoted token, in basis points, or `0` when the
@@ -20,16 +20,18 @@ import { collateralValuation } from "./collateral-valuation.js";
20
20
  * prices, under its liquidation threshold, capped by the quota the borrow
21
21
  * buys for it, all of which is {@link collateralValuation}'s business. The ceiling
22
22
  * is then held to what the market will actually lend: the pool's free
23
- * liquidity, the manager's own allowance and the facade's `maxDebt`, whichever
24
- * binds first.
23
+ * liquidity, the manager's own allowance, the facade's `maxDebt` and the
24
+ * remaining quota of the strategy target collateral, whichever binds first.
25
25
  *
26
- * The facade's `minDebt` is deliberately not applied. It is a floor, and a
27
- * ceiling answered as `0n` because the collateral is too small for this market
28
- * would tell a form nothing about what it is holding — the number a user needs
29
- * to see is the one they are short of. Collateral that carries something
30
- * therefore answers with it, whether or not the market would lend that little;
31
- * a loan under the floor is refused by `borrow` itself, with `debtOutOfRange`
32
- * naming both ends.
26
+ * The facade's `minDebt` is not applied to the collateral's own ceiling. It
27
+ * is a floor, and a ceiling answered as `0n` because the collateral is too
28
+ * small for this market would tell a form nothing about what it is holding —
29
+ * the number a user needs to see is the one they are short of. Collateral
30
+ * that carries something therefore answers with it, whether or not the market
31
+ * would lend that little; a loan under the floor is refused by `borrow`
32
+ * itself, with `debtOutOfRange` naming both ends. A market whose own capacity
33
+ * is under `minDebt` is different: `maxStrategyBorrowAmount` answers `0n`,
34
+ * because no loan of any size exists there.
33
35
  *
34
36
  * Nothing is fetched or simulated — the account does not exist yet and every
35
37
  * input is loaded market state, so a form can call this on each keystroke.
@@ -79,7 +81,7 @@ function maxBorrow(props) {
79
81
  const weighted = valuation.checkedUsd(holding) * valuation.lt(collateralToken);
80
82
  const backed = quotas.some((q) => eq(q.token, collateralToken)) ? BigIntMath.min(valuation.quotaValue(holding), weighted) : weighted;
81
83
  if (backed <= 0n) return 0n;
82
- const ceiling = BigIntMath.min(priceOracle.safeConvertFromUSD(underlying, backed / targetHF).value, suite.maxBorrowAmount().amount.value);
84
+ const ceiling = BigIntMath.min(priceOracle.safeConvertFromUSD(underlying, backed / targetHF).value, suite.maxStrategyBorrowAmount().amount.value);
83
85
  const unwrapsPayout = !!rwaAsset && eq(borrowToken, rwaAsset);
84
86
  return eq(borrowToken, underlying) ? ceiling : unwrapsPayout ? toTargetDecimals(ceiling, underlying, borrowToken, sdk) : priceOracle.safeConvert(underlying, borrowToken, ceiling).value;
85
87
  }
@@ -198,6 +198,7 @@ function buildMockSdk(args) {
198
198
  creditOperationMarket: CreditSuite.prototype.creditOperationMarket,
199
199
  isForbidden: CreditSuite.prototype.isForbidden,
200
200
  maxBorrowAmount: CreditSuite.prototype.maxBorrowAmount,
201
+ maxStrategyBorrowAmount: CreditSuite.prototype.maxStrategyBorrowAmount,
201
202
  creditManager: {
202
203
  address: args.creditManager,
203
204
  liquidationThresholds,
@@ -18,7 +18,7 @@ import { isAddressEqual } from "viem";
18
18
  /**
19
19
  * Amount of underlying seeded into each pool at market creation to protect
20
20
  * from inflation attacks, in raw token units. A suite whose
21
- * {@link CreditSuite.maxBorrowAmount} is at or below this is treated as
21
+ * {@link CreditSuite.maxStrategyBorrowAmount} is at or below this is treated as
22
22
  * having nothing left to lend.
23
23
  **/
24
24
  const MIN_STRATEGY_BORROW_AMOUNT = 100000n;
@@ -281,7 +281,7 @@ var CreditSuite = class extends SDKConstruct {
281
281
  return this.forbiddenTokens.some((f) => isAddressEqual(f, token));
282
282
  }
283
283
  /**
284
- * Largest debt one new position can take from this credit manager right now,
284
+ * Largest debt this credit manager will hand out on one operation right now,
285
285
  * and which limit set that number.
286
286
  *
287
287
  * Minimum of:
@@ -290,6 +290,10 @@ var CreditSuite = class extends SDKConstruct {
290
290
  * - the facade's per-account `maxDebt`.
291
291
  * While `maxDebtPerBlockMultiplier` is `0` the facade
292
292
  * takes no new debt at all, so the answer is `0`.
293
+ *
294
+ * These are the bounds every debt increase answers to, an existing account's
295
+ * included, which is what the guards hold a simulation to. Opening a position
296
+ * answers to two more — see {@link maxStrategyBorrowAmount}.
293
297
  */
294
298
  maxBorrowAmount() {
295
299
  const { pool } = this.market.pool;
@@ -319,6 +323,42 @@ var CreditSuite = class extends SDKConstruct {
319
323
  };
320
324
  }
321
325
  /**
326
+ * Largest debt one new position can take from this credit manager right now,
327
+ * and which limit set that number.
328
+ *
329
+ * {@link maxBorrowAmount} held to the two bounds only a position being opened
330
+ * answers to: the remaining quota of the strategy target collateral, which
331
+ * the position has to buy to be worth anything, and the facade's `minDebt`,
332
+ * which a first debt cannot sit under. `amount` is `0` whenever no position
333
+ * can be opened right now, and `limit` names why.
334
+ *
335
+ * An operation on an account that already exists is held to neither: its
336
+ * quota is weighed against the token its own plan buys, and its debt is
337
+ * already over the floor, so a top-up smaller than `minDebt` is legal.
338
+ */
339
+ maxStrategyBorrowAmount() {
340
+ const lends = this.maxBorrowAmount();
341
+ if (lends.limit === "debtPerBlockLimit") return lends;
342
+ const collateral = this.strategyTargetCollateral;
343
+ let value = lends.amount.value;
344
+ let limit = lends.limit;
345
+ if (collateral !== void 0) {
346
+ const quota = this.market.pool.pqk.quotaAvailable(collateral);
347
+ if (quota < value) {
348
+ value = quota;
349
+ limit = "quotaAvailable";
350
+ }
351
+ }
352
+ if (value < this.creditFacade.minDebt) return {
353
+ amount: this.market.toUnderlyingAmount(0n),
354
+ limit: "minDebt"
355
+ };
356
+ return {
357
+ amount: this.market.toUnderlyingAmount(value),
358
+ limit
359
+ };
360
+ }
361
+ /**
322
362
  * The single target collateral of this suite's strategy, or `undefined` when
323
363
  * none can be resolved.
324
364
  *
@@ -375,7 +415,7 @@ var CreditSuite = class extends SDKConstruct {
375
415
  * or `undefined` when credit suite does not offer a strategy opportunity.
376
416
  */
377
417
  strategyOpportunity() {
378
- const maxBorrowAmount = this.maxBorrowAmount().amount.value;
418
+ const maxBorrowAmount = this.maxStrategyBorrowAmount().amount.value;
379
419
  if (maxBorrowAmount <= MIN_STRATEGY_BORROW_AMOUNT) return;
380
420
  const collateral = this.strategyTargetCollateral;
381
421
  if (!collateral) return;
@@ -1,5 +1,6 @@
1
1
  import { iPoolQuotaKeeperV310Abi } from "../../../abi/310/generated.js";
2
2
  import { AddressMap } from "../../utils/AddressMap.js";
3
+ import { BigIntMath } from "../../utils/bigint-math.js";
3
4
  import { formatBNvalue, percentFmt } from "../../utils/formatter.js";
4
5
  import "../../utils/index.js";
5
6
  import { BaseContract } from "../../base/BaseContract.js";
@@ -40,10 +41,12 @@ var PoolQuotaKeeperV310Contract = class extends BaseContract {
40
41
  /**
41
42
  * How much more quota the market will take for a token, in the underlying.
42
43
  * `0n` when the market has no quota entry; not the same as {@link hasActiveQuota}.
44
+ * Never negative: a limit lowered under what is already quoted leaves no
45
+ * room, not a debt.
43
46
  */
44
47
  quotaAvailable(token) {
45
48
  const quota = this.quotas.get(token);
46
- return quota ? quota.limit - quota.totalQuoted : 0n;
49
+ return quota ? BigIntMath.max(0n, quota.limit - quota.totalQuoted) : 0n;
47
50
  }
48
51
  /**
49
52
  * Annual quota rate paid on a quoted token, in basis points, or `0` when the
@@ -11,11 +11,16 @@ import { Address } from "viem";
11
11
  * - `poolAvailableLiquidity` — the pool's available liquidity
12
12
  * - `managerDebtAvailable` — this credit manager's remaining debt allowance
13
13
  * - `maxDebt` — the facade's per-account `debtLimits.maxDebt`
14
+ * - `quotaAvailable` — remaining quota the market takes for the suite's
15
+ * strategy target collateral (`limit - totalQuoted`, floored at 0); only an
16
+ * account being opened is held to it
17
+ * - `minDebt` — every other limit left less than the facade's `minDebt`, so
18
+ * no account can be opened; the amount is 0
14
19
  * - `debtPerBlockLimit` — facade takes no new debt this block; in practice
15
20
  * `maxDebtPerBlockMultiplier == 0` after a with-loss liquidation
16
21
  * - `poolDebtLimit` — pool-wide debt cap; used on account-opening only
17
22
  **/
18
- type BorrowLimitCause = "poolAvailableLiquidity" | "managerDebtAvailable" | "maxDebt" | "debtPerBlockLimit" | "poolDebtLimit";
23
+ type BorrowLimitCause = "poolAvailableLiquidity" | "managerDebtAvailable" | "maxDebt" | "quotaAvailable" | "minDebt" | "debtPerBlockLimit" | "poolDebtLimit";
19
24
  /**
20
25
  * The credit manager is paused and takes no multicall at all.
21
26
  **/
@@ -30,16 +30,18 @@ interface MaxBorrowProps {
30
30
  * prices, under its liquidation threshold, capped by the quota the borrow
31
31
  * buys for it, all of which is {@link collateralValuation}'s business. The ceiling
32
32
  * is then held to what the market will actually lend: the pool's free
33
- * liquidity, the manager's own allowance and the facade's `maxDebt`, whichever
34
- * binds first.
33
+ * liquidity, the manager's own allowance, the facade's `maxDebt` and the
34
+ * remaining quota of the strategy target collateral, whichever binds first.
35
35
  *
36
- * The facade's `minDebt` is deliberately not applied. It is a floor, and a
37
- * ceiling answered as `0n` because the collateral is too small for this market
38
- * would tell a form nothing about what it is holding — the number a user needs
39
- * to see is the one they are short of. Collateral that carries something
40
- * therefore answers with it, whether or not the market would lend that little;
41
- * a loan under the floor is refused by `borrow` itself, with `debtOutOfRange`
42
- * naming both ends.
36
+ * The facade's `minDebt` is not applied to the collateral's own ceiling. It
37
+ * is a floor, and a ceiling answered as `0n` because the collateral is too
38
+ * small for this market would tell a form nothing about what it is holding —
39
+ * the number a user needs to see is the one they are short of. Collateral
40
+ * that carries something therefore answers with it, whether or not the market
41
+ * would lend that little; a loan under the floor is refused by `borrow`
42
+ * itself, with `debtOutOfRange` naming both ends. A market whose own capacity
43
+ * is under `minDebt` is different: `maxStrategyBorrowAmount` answers `0n`,
44
+ * because no loan of any size exists there.
43
45
  *
44
46
  * Nothing is fetched or simulated — the account does not exist yet and every
45
47
  * input is loaded market state, so a form can call this on each keystroke.
@@ -194,7 +194,7 @@ declare class CreditSuite extends SDKConstruct {
194
194
  */
195
195
  isForbidden(token: Address): boolean;
196
196
  /**
197
- * Largest debt one new position can take from this credit manager right now,
197
+ * Largest debt this credit manager will hand out on one operation right now,
198
198
  * and which limit set that number.
199
199
  *
200
200
  * Minimum of:
@@ -203,8 +203,27 @@ declare class CreditSuite extends SDKConstruct {
203
203
  * - the facade's per-account `maxDebt`.
204
204
  * While `maxDebtPerBlockMultiplier` is `0` the facade
205
205
  * takes no new debt at all, so the answer is `0`.
206
+ *
207
+ * These are the bounds every debt increase answers to, an existing account's
208
+ * included, which is what the guards hold a simulation to. Opening a position
209
+ * answers to two more — see {@link maxStrategyBorrowAmount}.
206
210
  */
207
211
  maxBorrowAmount(): MaxBorrowAmount;
212
+ /**
213
+ * Largest debt one new position can take from this credit manager right now,
214
+ * and which limit set that number.
215
+ *
216
+ * {@link maxBorrowAmount} held to the two bounds only a position being opened
217
+ * answers to: the remaining quota of the strategy target collateral, which
218
+ * the position has to buy to be worth anything, and the facade's `minDebt`,
219
+ * which a first debt cannot sit under. `amount` is `0` whenever no position
220
+ * can be opened right now, and `limit` names why.
221
+ *
222
+ * An operation on an account that already exists is held to neither: its
223
+ * quota is weighed against the token its own plan buys, and its debt is
224
+ * already over the floor, so a top-up smaller than `minDebt` is legal.
225
+ */
226
+ maxStrategyBorrowAmount(): MaxBorrowAmount;
208
227
  /**
209
228
  * The single target collateral of this suite's strategy, or `undefined` when
210
229
  * none can be resolved.
@@ -494,6 +494,8 @@ declare class PoolQuotaKeeperV310Contract extends BaseContract<abi> implements I
494
494
  /**
495
495
  * How much more quota the market will take for a token, in the underlying.
496
496
  * `0n` when the market has no quota entry; not the same as {@link hasActiveQuota}.
497
+ * Never negative: a limit lowered under what is already quoted leaves no
498
+ * room, not a debt.
497
499
  */
498
500
  quotaAvailable(token: Address): bigint;
499
501
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gearbox-protocol/sdk",
3
- "version": "17.1.1",
3
+ "version": "17.2.0-next.1",
4
4
  "description": "Gearbox SDK",
5
5
  "license": "MIT",
6
6
  "repository": {