@gearbox-protocol/sdk 16.4.0-next.5 → 16.4.0-next.6

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 (55) hide show
  1. package/dist/cjs/onchain/accounts/intents/borrow.js +139 -0
  2. package/dist/cjs/onchain/accounts/intents/{collateral-money.js → collateral-valuation.js} +7 -7
  3. package/dist/cjs/onchain/accounts/intents/index.js +79 -1
  4. package/dist/cjs/onchain/accounts/intents/leverage-band.js +2 -12
  5. package/dist/cjs/onchain/accounts/intents/maxBorrow.js +85 -0
  6. package/dist/cjs/onchain/accounts/intents/maxWithdrawCollateral.js +14 -14
  7. package/dist/cjs/onchain/accounts/intents/open-strategy.js +6 -45
  8. package/dist/cjs/onchain/accounts/intents/testing/sdk-mock.js +1 -0
  9. package/dist/cjs/onchain/accounts/intents/utils/common.js +19 -0
  10. package/dist/cjs/onchain/accounts/intents/utils/credit-account-slice.js +21 -0
  11. package/dist/cjs/onchain/accounts/intents/utils/index.js +2 -0
  12. package/dist/cjs/onchain/accounts/intents/withdraw-limits.js +8 -8
  13. package/dist/cjs/onchain/positions/calcHealthFactor.js +3 -3
  14. package/dist/cjs/onchain/preview/preview/previewOpenStrategyPosition.js +2 -1
  15. package/dist/cjs/sdk/execute/ExecuteApi.js +65 -5
  16. package/dist/cjs/sdk/prepare/PrepareApi.js +86 -12
  17. package/dist/esm/onchain/accounts/intents/borrow.js +137 -0
  18. package/dist/esm/onchain/accounts/intents/{collateral-money.js → collateral-valuation.js} +7 -7
  19. package/dist/esm/onchain/accounts/intents/index.js +79 -1
  20. package/dist/esm/onchain/accounts/intents/leverage-band.js +2 -12
  21. package/dist/esm/onchain/accounts/intents/maxBorrow.js +83 -0
  22. package/dist/esm/onchain/accounts/intents/maxWithdrawCollateral.js +14 -14
  23. package/dist/esm/onchain/accounts/intents/open-strategy.js +6 -45
  24. package/dist/esm/onchain/accounts/intents/testing/sdk-mock.js +1 -0
  25. package/dist/esm/onchain/accounts/intents/utils/common.js +19 -1
  26. package/dist/esm/onchain/accounts/intents/utils/credit-account-slice.js +21 -1
  27. package/dist/esm/onchain/accounts/intents/utils/index.js +3 -3
  28. package/dist/esm/onchain/accounts/intents/withdraw-limits.js +8 -8
  29. package/dist/esm/onchain/positions/calcHealthFactor.js +3 -3
  30. package/dist/esm/onchain/preview/preview/previewOpenStrategyPosition.js +2 -1
  31. package/dist/esm/sdk/execute/ExecuteApi.js +65 -5
  32. package/dist/esm/sdk/prepare/PrepareApi.js +86 -12
  33. package/dist/types/model/previews.d.ts +11 -2
  34. package/dist/types/onchain/accounts/index.d.ts +4 -3
  35. package/dist/types/onchain/accounts/intents/borrow.d.ts +141 -0
  36. package/dist/types/onchain/accounts/intents/{collateral-money.d.ts → collateral-valuation.d.ts} +8 -8
  37. package/dist/types/onchain/accounts/intents/index.d.ts +78 -2
  38. package/dist/types/onchain/accounts/intents/maxBorrow.d.ts +47 -0
  39. package/dist/types/onchain/accounts/intents/maxWithdrawCollateral.d.ts +3 -3
  40. package/dist/types/onchain/accounts/intents/open-strategy.d.ts +3 -17
  41. package/dist/types/onchain/accounts/intents/testing/sdk-mock.d.ts +2 -0
  42. package/dist/types/onchain/accounts/intents/tests/open-strategy.fixtures.d.ts +2 -2
  43. package/dist/types/onchain/accounts/intents/types.d.ts +6 -1
  44. package/dist/types/onchain/accounts/intents/utils/common.d.ts +15 -1
  45. package/dist/types/onchain/accounts/intents/utils/credit-account-slice.d.ts +14 -1
  46. package/dist/types/onchain/accounts/intents/utils/index.d.ts +3 -3
  47. package/dist/types/onchain/accounts/intents/withdraw-limits.d.ts +1 -1
  48. package/dist/types/onchain/index.d.ts +4 -3
  49. package/dist/types/sdk/execute/index.d.ts +2 -2
  50. package/dist/types/sdk/execute/types.d.ts +48 -6
  51. package/dist/types/sdk/index.d.ts +5 -4
  52. package/dist/types/sdk/prepare/PrepareApi.d.ts +15 -3
  53. package/dist/types/sdk/prepare/index.d.ts +4 -3
  54. package/dist/types/sdk/prepare/types.d.ts +169 -49
  55. package/package.json +1 -1
@@ -0,0 +1,139 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_model_errors_operation_errors = require("../../../model/errors/operation-errors.js");
3
+ const require_model_errors_prepare_errors = require("../../../model/errors/prepare-errors.js");
4
+ require("../../../model/index.js");
5
+ const require_onchain_validation_helpers_token = require("../../validation/helpers/token.js");
6
+ const require_onchain_validation_raise = require("../../validation/raise.js");
7
+ require("../../validation/index.js");
8
+ const require_onchain_accounts_intents_utils_common = require("./utils/common.js");
9
+ const require_onchain_accounts_intents_guards = require("./guards.js");
10
+ const require_onchain_accounts_intents_math = require("./math.js");
11
+ const require_onchain_accounts_intents_utils_credit_account_slice = require("./utils/credit-account-slice.js");
12
+ const require_onchain_accounts_intents_utils_price_impact = require("./utils/price-impact.js");
13
+ const require_onchain_accounts_intents_utils_quotas_for_update = require("./utils/quotas-for-update.js");
14
+ const require_onchain_accounts_intents_utils_router_path = require("./utils/router-path.js");
15
+ require("./utils/index.js");
16
+ //#region src/onchain/accounts/intents/borrow.ts
17
+ /**
18
+ * Builds the state a borrow would leave behind.
19
+ *
20
+ * The debt is named rather than derived: a borrow asks for an amount, where an
21
+ * opening asks for a leverage and lets the collateral decide. Everything the
22
+ * loan pays out leaves the account, so the collateral alone backs it — which
23
+ * is what makes the health factor here a straight function of the two amounts.
24
+ *
25
+ * Produces no operation list, as opening never does: the caller hands the
26
+ * numbers and the calls to `sdk.accounts.openCA`, with `withdrawToken` set to
27
+ * the payout.
28
+ */
29
+ async function buildBorrowState(props) {
30
+ const { sdk, creditManager, collateralAmount, borrowAmount, slippage = 0, quotaReserve, creditAccount: existing } = props;
31
+ const suite = sdk.marketRegister.findCreditManager(creditManager);
32
+ const market = sdk.marketRegister.findByCreditManager(creditManager);
33
+ require_onchain_accounts_intents_guards.assertMarketOperable(suite);
34
+ const underlying = market.pool.underlying.toLowerCase();
35
+ const collateralToken = props.collateralToken.toLowerCase();
36
+ const borrowToken = props.borrowToken.toLowerCase();
37
+ const convert = (from, to, amount) => market.priceOracle.safeConvert(from, to, amount).value;
38
+ if (require_onchain_accounts_intents_utils_common.eq(collateralToken, borrowToken)) throw new require_onchain_validation_raise.IntentPreviewError(require_model_errors_prepare_errors.unsupportedCollateralToken(require_onchain_validation_helpers_token.toToken(sdk, collateralToken)), "borrow: the payout token cannot also be the collateral");
39
+ const rwaAsset = sdk.tokensMeta.rwaUnderlyings.get(underlying)?.asset?.toLowerCase();
40
+ const unwrapsPayout = !!rwaAsset && require_onchain_accounts_intents_utils_common.eq(borrowToken, rwaAsset);
41
+ if (rwaAsset && require_onchain_accounts_intents_utils_common.eq(borrowToken, underlying)) throw new require_onchain_validation_raise.IntentPreviewError(require_model_errors_prepare_errors.unsupportedCollateralToken(require_onchain_validation_helpers_token.toToken(sdk, borrowToken)), `borrow: ${underlying} cannot leave the account, ask for the payout in ${rwaAsset}`);
42
+ if (collateralAmount <= 0n) throw new require_onchain_validation_raise.IntentPreviewError(require_model_errors_operation_errors.insufficientBalance(), "borrow: no collateral supplied");
43
+ if (borrowAmount <= 0n) throw new require_onchain_validation_raise.IntentPreviewError(require_model_errors_operation_errors.insufficientBalance(), "borrow: nothing to borrow");
44
+ const margin = convert(collateralToken, underlying, collateralAmount);
45
+ if (margin <= 0n) throw new require_onchain_validation_raise.IntentPreviewError(require_model_errors_operation_errors.insufficientBalance(), "borrow: collateral is worth nothing in underlying");
46
+ const debt = require_onchain_accounts_intents_utils_common.eq(borrowToken, underlying) ? borrowAmount : unwrapsPayout ? require_onchain_accounts_intents_utils_common.toTargetDecimals(borrowAmount, borrowToken, underlying, sdk) : convert(borrowToken, underlying, borrowAmount);
47
+ require_onchain_accounts_intents_math.assertDebtLimits(sdk, debt, suite.creditFacade, underlying);
48
+ require_onchain_accounts_intents_guards.assertCanBorrow(sdk, suite, debt);
49
+ const account = existing ?? require_onchain_accounts_intents_utils_credit_account_slice.unopenedAccountSlice({
50
+ creditManager,
51
+ creditFacade: suite.creditFacade.address,
52
+ underlying
53
+ });
54
+ const leg = require_onchain_accounts_intents_utils_common.eq(borrowToken, underlying) || unwrapsPayout ? void 0 : await require_onchain_accounts_intents_utils_router_path.createRouterPaths({
55
+ sdk,
56
+ creditAccount: account,
57
+ slippage
58
+ }).swap({
59
+ tokenIn: underlying,
60
+ tokenOut: borrowToken,
61
+ amount: debt,
62
+ keep: require_onchain_accounts_intents_utils_common.eq(collateralToken, underlying) ? collateralAmount : 0n
63
+ });
64
+ const unwrap = unwrapsPayout ? await sdk.accounts.assembleRWAUnwrapCalls(debt, creditManager) : void 0;
65
+ if (unwrapsPayout && !unwrap) throw new Error(`borrow: no unwrap calls found for ${borrowToken}`);
66
+ const assets = [{
67
+ token: collateralToken,
68
+ balance: collateralAmount
69
+ }];
70
+ const quotaIncrease = borrowCollateralQuota({
71
+ sdk,
72
+ creditManager,
73
+ assets,
74
+ quotaReserve
75
+ });
76
+ require_onchain_accounts_intents_guards.assertGrowthAllowed({
77
+ sdk,
78
+ suite,
79
+ market,
80
+ before: [],
81
+ after: assets
82
+ });
83
+ require_onchain_accounts_intents_guards.assertQuotaAvailable(sdk, market, quotaIncrease);
84
+ const snapshot = {
85
+ creditManager,
86
+ assets,
87
+ quotas: quotaIncrease,
88
+ totalDebt: debt,
89
+ totalValue: margin
90
+ };
91
+ const projection = sdk.positions.projection(snapshot, { availableLiquidityChange: -debt });
92
+ require_onchain_accounts_intents_guards.assertCollateralised(projection.safeHealthFactor, true);
93
+ const priced = (token, balance) => market.priceOracle.toTokenAmount(token, balance);
94
+ const payout = unwrapsPayout ? require_onchain_accounts_intents_utils_common.toTargetDecimals(debt, underlying, borrowToken, sdk) : leg ? leg.amount : debt;
95
+ return {
96
+ ...projection,
97
+ currentPrice: sdk.positions.currentPrice(snapshot),
98
+ executionCost: void 0,
99
+ priceImpact: await require_onchain_accounts_intents_utils_price_impact.collectPriceImpact(leg?.probe ? [leg.probe] : [], {
100
+ totalValue: margin,
101
+ netValue: margin - debt,
102
+ toUnderlying: (from, amount) => convert(from, underlying, amount)
103
+ }),
104
+ collateral: priced(collateralToken, collateralAmount),
105
+ borrowed: priced(borrowToken, payout),
106
+ minBorrowed: priced(borrowToken, leg ? leg.minAmount : payout),
107
+ slippage,
108
+ quotaIncrease,
109
+ calls: leg ? [...leg.calls] : unwrap ?? [],
110
+ creditAccount: existing?.creditAccount
111
+ };
112
+ }
113
+ /**
114
+ * Quota a borrow buys for its collateral on an account that holds none yet.
115
+ *
116
+ * Shared with `maxBorrow`, because a quota short of the collateral's weighted
117
+ * value is what caps the loan: the two have to read the same number or the
118
+ * ceiling one offers is one the other refuses.
119
+ *
120
+ * @param props - {@link BorrowCollateralQuotaProps}
121
+ **/
122
+ function borrowCollateralQuota({ sdk, creditManager, assets, quotaReserve }) {
123
+ const suite = sdk.marketRegister.findCreditManager(creditManager);
124
+ const market = sdk.marketRegister.findByCreditManager(creditManager);
125
+ return require_onchain_accounts_intents_utils_quotas_for_update.getQuotasForUpdate({
126
+ assetsBefore: [],
127
+ assetsAfter: assets,
128
+ initialQuotas: [],
129
+ quotaReserve,
130
+ underlyingToken: market.pool.underlying,
131
+ liquidationThresholds: suite.creditManager.liquidationThresholds,
132
+ quotas: market.pool.pqk.quotas,
133
+ maxDebt: suite.creditFacade.maxDebt,
134
+ convert: (from, to, amount) => market.priceOracle.safeConvert(from, to, amount).value
135
+ }).quotaIncrease;
136
+ }
137
+ //#endregion
138
+ exports.borrowCollateralQuota = borrowCollateralQuota;
139
+ exports.buildBorrowState = buildBorrowState;
@@ -3,9 +3,9 @@ const require_onchain_utils_bigint_math = require("../../utils/bigint-math.js");
3
3
  const require_onchain_constants_math = require("../../constants/math.js");
4
4
  require("../../utils/index.js");
5
5
  const require_onchain_accounts_intents_utils_common = require("./utils/common.js");
6
- //#region src/onchain/accounts/intents/collateral-money.ts
7
- /** {@inheritDoc CollateralMoney} */
8
- function collateralMoney(creditAccount, sdk) {
6
+ //#region src/onchain/accounts/intents/collateral-valuation.ts
7
+ /** {@inheritDoc CollateralValuation} */
8
+ function collateralValuation(creditAccount, sdk) {
9
9
  const { market, creditManager } = sdk.marketRegister.findCreditManager(creditAccount.creditManager);
10
10
  const { priceOracle } = market;
11
11
  const { pqk } = market.pool;
@@ -21,21 +21,21 @@ function collateralMoney(creditAccount, sdk) {
21
21
  const lt = (token) => BigInt(creditManager.liquidationThresholds.get(token) ?? 0);
22
22
  const checkedUsd = (holding) => require_onchain_accounts_intents_utils_common.eq(holding.token, underlying) ? mainUsd(holding.token, holding.balance) ?? 0n : priceOracle.safeConvertMinUSD(holding.token, holding.balance).value;
23
23
  /** A quota is underlying-denominated, and a closed market backs nothing. */
24
- const quotaMoney = (holding) => pqk.hasActiveQuota(holding.token) ? (mainUsd(underlying, holding.quota) ?? 0n) * require_onchain_constants_math.PERCENTAGE_FACTOR : 0n;
24
+ const quotaValue = (holding) => pqk.hasActiveQuota(holding.token) ? (mainUsd(underlying, holding.quota) ?? 0n) * require_onchain_constants_math.PERCENTAGE_FACTOR : 0n;
25
25
  const weigh = (holding) => {
26
26
  const weighted = checkedUsd(holding) * lt(holding.token);
27
27
  if (holding.quota === 0n) return weighted;
28
- return require_onchain_utils_bigint_math.BigIntMath.min(quotaMoney(holding), weighted);
28
+ return require_onchain_utils_bigint_math.BigIntMath.min(quotaValue(holding), weighted);
29
29
  };
30
30
  return {
31
31
  underlying,
32
32
  counts: (holding) => holding.balance > 10n && (!masked || (holding.mask & creditAccount.enabledTokensMask) !== 0n),
33
33
  weigh,
34
- quotaMoney,
34
+ quotaValue,
35
35
  mainUsd,
36
36
  checkedUsd,
37
37
  lt
38
38
  };
39
39
  }
40
40
  //#endregion
41
- exports.collateralMoney = collateralMoney;
41
+ exports.collateralValuation = collateralValuation;
@@ -8,9 +8,11 @@ const require_onchain_validation_raise = require("../../validation/raise.js");
8
8
  require("../../validation/index.js");
9
9
  const require_onchain_accounts_intents_utils_pick_token = require("./utils/pick-token.js");
10
10
  const require_onchain_accounts_intents_guards = require("./guards.js");
11
+ const require_onchain_accounts_intents_utils_credit_account_slice = require("./utils/credit-account-slice.js");
12
+ const require_onchain_accounts_intents_borrow = require("./borrow.js");
11
13
  const require_onchain_accounts_intents_leverage_band = require("./leverage-band.js");
14
+ const require_onchain_accounts_intents_maxBorrow = require("./maxBorrow.js");
12
15
  const require_onchain_accounts_intents_maxWithdrawCollateral = require("./maxWithdrawCollateral.js");
13
- const require_onchain_accounts_intents_utils_credit_account_slice = require("./utils/credit-account-slice.js");
14
16
  const require_onchain_accounts_intents_open_strategy = require("./open-strategy.js");
15
17
  const require_onchain_accounts_intents_plan = require("./plan.js");
16
18
  const require_onchain_accounts_intents_view = require("./view.js");
@@ -133,6 +135,31 @@ var CreditAccountOperationsService = class extends require_onchain_base_SDKConst
133
135
  });
134
136
  }
135
137
  /**
138
+ * Largest loan a given collateral supports at `targetHF`, in the payout
139
+ * token's units — the ceiling a borrow form should offer, and the amount
140
+ * {@link borrowIntent} will accept at the top of its range.
141
+ *
142
+ * Reads no account, like {@link leverageBand}: the borrow opens one. The
143
+ * collateral is valued the way the transaction will be judged, at safe
144
+ * prices and under the quota the borrow buys, and the answer is then held to
145
+ * what the market will lend.
146
+ *
147
+ * The default is {@link MIN_HF_LIMITED}, the threshold a form holds an
148
+ * account to.
149
+ *
150
+ * @param props - The manager, the SDK holding its market, the collateral put
151
+ * up, the token to be paid in, and optionally the health factor to land at
152
+ * @returns Amount in the payout token's units; `0n` where no loan of this
153
+ * shape can be funded
154
+ */
155
+ maxBorrow(props) {
156
+ const { targetHF = require_onchain_validation_helpers_health_factor_limits.MIN_HF_LIMITED, ...rest } = props;
157
+ return require_onchain_accounts_intents_maxBorrow.maxBorrow({
158
+ ...rest,
159
+ targetHF: targetHF + 2n
160
+ });
161
+ }
162
+ /**
136
163
  * Previews the same operation when its source only redeems through its
137
164
  * issuer: a Securitize dsToken, a Mellow share.
138
165
  *
@@ -278,6 +305,26 @@ var CreditAccountOperationsService = class extends require_onchain_base_SDKConst
278
305
  };
279
306
  }
280
307
  /**
308
+ * Previews opening an account that holds nothing.
309
+ *
310
+ * Nothing is put up, drawn or routed, so there is no state to build and no
311
+ * guard to run beyond the market's own: a paused or expired facade takes no
312
+ * multicall, and an opening is a multicall like any other. Answers the same
313
+ * envelope its two neighbours do so a caller branches on `ok` throughout.
314
+ *
315
+ * @param props - The SDK holding the market, and the manager to open in
316
+ * @returns `{ ok: true }`, or `{ ok: false, error }` when the market takes
317
+ * no transaction right now
318
+ */
319
+ async openEmptyAccountIntent(props) {
320
+ try {
321
+ require_onchain_accounts_intents_guards.assertMarketOperable(props.sdk.marketRegister.findCreditManager(props.creditManager));
322
+ return { ok: true };
323
+ } catch (e) {
324
+ return asSDKError(e);
325
+ }
326
+ }
327
+ /**
281
328
  * Previews opening a brand-new leveraged position.
282
329
  *
283
330
  * Sits apart from {@link startIntent} because there is no account yet: nothing
@@ -299,6 +346,37 @@ var CreditAccountOperationsService = class extends require_onchain_base_SDKConst
299
346
  return asSDKError(e);
300
347
  }
301
348
  }
349
+ /**
350
+ * Previews taking a loan against collateral, on an account this same
351
+ * transaction opens.
352
+ *
353
+ * Sits beside {@link openStrategyIntent} rather than under
354
+ * {@link startIntent} for the same reason: there is no account yet, and the
355
+ * output feeds `sdk.accounts.openCA`. What sets it apart from an opening is
356
+ * where the loan goes — out to the wallet rather than into a position — so
357
+ * the debt is named outright instead of following from a leverage, and the
358
+ * collateral is the only thing the account is left holding.
359
+ *
360
+ * `creditAccount` draws the loan on one the wallet already holds instead of
361
+ * opening another, as an opening takes one.
362
+ *
363
+ * @param props - Credit manager, the collateral the wallet puts up and the
364
+ * payout it asks for
365
+ * @returns Debt, the payout's two branches and the projection the account
366
+ * lands in, or `{ ok: false, error }` when the loan is not viable — a debt
367
+ * outside the facade's limits, collateral that cannot carry it, a payout the
368
+ * router has no path to
369
+ */
370
+ async borrowIntent(props) {
371
+ try {
372
+ return {
373
+ ok: true,
374
+ state: await require_onchain_accounts_intents_borrow.buildBorrowState(props)
375
+ };
376
+ } catch (e) {
377
+ return asSDKError(e);
378
+ }
379
+ }
302
380
  /** Plan → realise → wrap. Unviable requests become `{ ok: false }`. */
303
381
  async #preview(props, plan) {
304
382
  try {
@@ -1,6 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_onchain_utils_bigint_math = require("../../utils/bigint-math.js");
3
3
  const require_onchain_constants_math = require("../../constants/math.js");
4
+ const require_onchain_accounts_intents_utils_common = require("./utils/common.js");
4
5
  //#region src/onchain/accounts/intents/leverage-band.ts
5
6
  /**
6
7
  * The leverages this market will actually fund for a position of this size.
@@ -30,7 +31,7 @@ const require_onchain_constants_math = require("../../constants/math.js");
30
31
  * ```
31
32
  **/
32
33
  function calcLeverageBand({ sdk, creditManager, collateral, targetHF }) {
33
- const found = resolve(sdk, creditManager);
34
+ const found = require_onchain_accounts_intents_utils_common.resolveCreditManager(sdk, creditManager);
34
35
  if (!found) return;
35
36
  const { suite, market } = found;
36
37
  const target = suite.strategyTargetCollateral;
@@ -55,16 +56,5 @@ function calcLeverageBand({ sdk, creditManager, collateral, targetHF }) {
55
56
  max
56
57
  };
57
58
  }
58
- /** The manager's suite and market, or nothing while they cannot be resolved. */
59
- function resolve(sdk, creditManager) {
60
- try {
61
- return {
62
- suite: sdk.marketRegister.findCreditManager(creditManager),
63
- market: sdk.marketRegister.findByCreditManager(creditManager)
64
- };
65
- } catch {
66
- return;
67
- }
68
- }
69
59
  //#endregion
70
60
  exports.calcLeverageBand = calcLeverageBand;
@@ -0,0 +1,85 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_onchain_utils_bigint_math = require("../../utils/bigint-math.js");
3
+ require("../../constants/math.js");
4
+ require("../../utils/index.js");
5
+ const require_onchain_accounts_intents_utils_common = require("./utils/common.js");
6
+ const require_onchain_accounts_intents_utils_credit_account_slice = require("./utils/credit-account-slice.js");
7
+ require("./utils/index.js");
8
+ const require_onchain_accounts_intents_borrow = require("./borrow.js");
9
+ const require_onchain_accounts_intents_collateral_valuation = require("./collateral-valuation.js");
10
+ //#region src/onchain/accounts/intents/maxBorrow.ts
11
+ /**
12
+ * Largest loan this collateral supports at `targetHF` — the ceiling a borrow
13
+ * form should offer, in the payout token's units.
14
+ *
15
+ * The inverse of a borrow rather than a search for one: the loan leaves the
16
+ * account entirely, so the collateral is the whole of what backs the debt, and
17
+ * the health factor is one division away from the amount. Solving it the other
18
+ * way round costs a division too, and no iteration.
19
+ *
20
+ * Collateral is valued the way the transaction will be judged — at safe
21
+ * prices, under its liquidation threshold, capped by the quota the borrow
22
+ * buys for it, all of which is {@link collateralValuation}'s business. The ceiling
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.
26
+ *
27
+ * Nothing is fetched or simulated — the account does not exist yet and every
28
+ * input is loaded market state, so a form can call this on each keystroke.
29
+ *
30
+ * @param props - {@link MaxBorrowProps}
31
+ * @returns Amount in the payout token's units; `0n` when this market will fund
32
+ * no loan of this shape — including a debt that would land under `minDebt`, a
33
+ * collateral that backs nothing at safe prices, and a manager the SDK does not
34
+ * hold yet
35
+ **/
36
+ function maxBorrow(props) {
37
+ const { sdk, creditManager, collateralAmount, targetHF, quotaReserve } = props;
38
+ const found = require_onchain_accounts_intents_utils_common.resolveCreditManager(sdk, creditManager);
39
+ if (!found) return 0n;
40
+ const { suite, market } = found;
41
+ const { priceOracle } = market;
42
+ const underlying = market.pool.underlying.toLowerCase();
43
+ const collateralToken = props.collateralToken.toLowerCase();
44
+ const borrowToken = props.borrowToken.toLowerCase();
45
+ const rwaAsset = sdk.tokensMeta.rwaUnderlyings.get(underlying)?.asset?.toLowerCase();
46
+ if (require_onchain_accounts_intents_utils_common.eq(collateralToken, borrowToken)) return 0n;
47
+ if (rwaAsset && require_onchain_accounts_intents_utils_common.eq(borrowToken, underlying)) return 0n;
48
+ if (collateralAmount <= 10n || targetHF <= 0n) return 0n;
49
+ const assets = [{
50
+ token: collateralToken,
51
+ balance: collateralAmount
52
+ }];
53
+ const quotas = require_onchain_accounts_intents_borrow.borrowCollateralQuota({
54
+ sdk,
55
+ creditManager,
56
+ assets,
57
+ quotaReserve
58
+ });
59
+ const holding = {
60
+ token: collateralToken,
61
+ balance: collateralAmount,
62
+ quota: quotas.find((q) => require_onchain_accounts_intents_utils_common.eq(q.token, collateralToken))?.balance ?? 0n,
63
+ mask: 0n,
64
+ success: true
65
+ };
66
+ const valuation = require_onchain_accounts_intents_collateral_valuation.collateralValuation({
67
+ ...require_onchain_accounts_intents_utils_credit_account_slice.unopenedAccountSlice({
68
+ creditManager,
69
+ creditFacade: suite.creditFacade.address,
70
+ underlying
71
+ }),
72
+ tokens: [holding]
73
+ }, sdk);
74
+ const weighted = valuation.checkedUsd(holding) * valuation.lt(collateralToken);
75
+ 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;
76
+ if (backed <= 0n) return 0n;
77
+ const ceiling = require_onchain_utils_bigint_math.BigIntMath.min(priceOracle.safeConvertFromUSD(underlying, backed / targetHF).value, suite.maxBorrowAmount().amount.value);
78
+ const unwrapsPayout = !!rwaAsset && require_onchain_accounts_intents_utils_common.eq(borrowToken, rwaAsset);
79
+ const intoPayout = (value) => unwrapsPayout ? require_onchain_accounts_intents_utils_common.toTargetDecimals(value, underlying, borrowToken, sdk) : priceOracle.safeConvert(underlying, borrowToken, value).value;
80
+ const intoUnderlying = (value) => unwrapsPayout ? require_onchain_accounts_intents_utils_common.toTargetDecimals(value, borrowToken, underlying, sdk) : priceOracle.safeConvert(borrowToken, underlying, value).value;
81
+ const amount = require_onchain_accounts_intents_utils_common.eq(borrowToken, underlying) ? ceiling : intoPayout(ceiling);
82
+ return (require_onchain_accounts_intents_utils_common.eq(borrowToken, underlying) ? amount : intoUnderlying(amount)) < suite.creditFacade.minDebt ? 0n : amount;
83
+ }
84
+ //#endregion
85
+ exports.maxBorrow = maxBorrow;
@@ -3,16 +3,16 @@ const require_onchain_utils_bigint_math = require("../../utils/bigint-math.js");
3
3
  require("../../constants/math.js");
4
4
  require("../../utils/index.js");
5
5
  const require_onchain_accounts_intents_utils_common = require("./utils/common.js");
6
- const require_onchain_accounts_intents_collateral_money = require("./collateral-money.js");
6
+ const require_onchain_accounts_intents_collateral_valuation = require("./collateral-valuation.js");
7
7
  //#region src/onchain/accounts/intents/maxWithdrawCollateral.ts
8
8
  /**
9
9
  * Largest amount of one collateral the account can withdraw while its health
10
10
  * factor stays at or above `targetHF`.
11
11
  *
12
12
  * This is the collateral check solved for one balance, and it counts what that
13
- * check counts — see {@link collateralMoney} for the valuation, safe prices
14
- * included. The debt is valued at the main feed, as the check does. Zero debt
15
- * frees the whole balance.
13
+ * check counts — see {@link collateralValuation} for it, safe prices included.
14
+ * The debt is valued at the main feed, as the check does. Zero debt frees the
15
+ * whole balance.
16
16
  *
17
17
  * Rounding always favours the account, so the answer clears the check rather
18
18
  * than landing a wei short of it.
@@ -26,20 +26,20 @@ function maxWithdrawCollateral(props) {
26
26
  const target = creditAccount.tokens.find((t) => require_onchain_accounts_intents_utils_common.eq(t.token, token));
27
27
  if (!target || target.balance <= 10n) return 0n;
28
28
  if (creditAccount.totalDebt === 0n) return target.balance;
29
- const money = require_onchain_accounts_intents_collateral_money.collateralMoney(creditAccount, sdk);
30
- let otherMoney = 0n;
29
+ const valuation = require_onchain_accounts_intents_collateral_valuation.collateralValuation(creditAccount, sdk);
30
+ let otherValue = 0n;
31
31
  for (const t of creditAccount.tokens) {
32
- if (require_onchain_accounts_intents_utils_common.eq(t.token, token) || !money.counts(t)) continue;
33
- otherMoney += money.weigh(t);
32
+ if (require_onchain_accounts_intents_utils_common.eq(t.token, token) || !valuation.counts(t)) continue;
33
+ otherValue += valuation.weigh(t);
34
34
  }
35
- const borrowed = money.mainUsd(money.underlying, creditAccount.totalDebt);
35
+ const borrowed = valuation.mainUsd(valuation.underlying, creditAccount.totalDebt);
36
36
  if (borrowed === void 0 || borrowed <= 0n) return 0n;
37
37
  const required = borrowed * targetHF;
38
- if (required <= otherMoney) return target.balance;
39
- const shortfall = required - otherMoney;
40
- if (target.quota > 0n && money.quotaMoney(target) < shortfall) return 0n;
41
- const targetLt = money.lt(target.token);
42
- const targetUsd = money.checkedUsd(target);
38
+ if (required <= otherValue) return target.balance;
39
+ const shortfall = required - otherValue;
40
+ if (target.quota > 0n && valuation.quotaValue(target) < shortfall) return 0n;
41
+ const targetLt = valuation.lt(target.token);
42
+ const targetUsd = valuation.checkedUsd(target);
43
43
  if (targetLt === 0n || targetUsd === 0n) return 0n;
44
44
  const keptUsd = require_onchain_utils_bigint_math.BigIntMath.ceilDiv(shortfall, targetLt);
45
45
  const kept = require_onchain_utils_bigint_math.BigIntMath.ceilDiv(target.balance * keptUsd, targetUsd);
@@ -4,17 +4,13 @@ require("../../../model/index.js");
4
4
  const require_onchain_validation_raise = require("../../validation/raise.js");
5
5
  const require_onchain_accounts_intents_guards = require("./guards.js");
6
6
  const require_onchain_accounts_intents_math = require("./math.js");
7
+ const require_onchain_accounts_intents_utils_credit_account_slice = require("./utils/credit-account-slice.js");
7
8
  const require_onchain_accounts_intents_utils_price_impact = require("./utils/price-impact.js");
8
9
  const require_onchain_accounts_intents_utils_quotas_for_update = require("./utils/quotas-for-update.js");
9
10
  const require_onchain_accounts_intents_utils_router_path = require("./utils/router-path.js");
10
11
  require("./utils/index.js");
11
12
  //#region src/onchain/accounts/intents/open-strategy.ts
12
13
  /**
13
- * Stand-in account address, used when the opening creates its own account:
14
- * nothing exists on chain until the tx lands.
15
- */
16
- const NO_ACCOUNT = "0x0000000000000000000000000000000000000000";
17
- /**
18
14
  * Builds the state opening a leveraged position out of wallet collateral would
19
15
  * reach.
20
16
  *
@@ -27,7 +23,6 @@ const NO_ACCOUNT = "0x0000000000000000000000000000000000000000";
27
23
  * `sdk.accounts.openCA`.
28
24
  */
29
25
  async function buildOpenStrategyState(props) {
30
- if (props.empty) return emptyOpenState(props);
31
26
  const { sdk, creditManager, collateral, targetToken, leverage, slippage = 0, quotaReserve, leftoverBalances = [], creditAccount: existing } = props;
32
27
  require_onchain_accounts_intents_math.assertLeverageAtLeastOne(leverage);
33
28
  const suite = sdk.marketRegister.findCreditManager(creditManager);
@@ -38,16 +33,11 @@ async function buildOpenStrategyState(props) {
38
33
  const margin = collateral.reduce((acc, a) => acc + convert(a.token, underlying, a.balance), 0n);
39
34
  if (margin <= 0n) throw new require_onchain_validation_raise.IntentPreviewError(require_model_errors_operation_errors.insufficientBalance(), "openStrategy: collateral is worth nothing in underlying");
40
35
  const debt = require_onchain_accounts_intents_math.debtForLeverage(margin, leverage);
41
- const account = existing ?? {
42
- creditAccount: NO_ACCOUNT,
43
- creditManager: creditManager.toLowerCase(),
44
- creditFacade: suite.creditFacade.address.toLowerCase(),
45
- underlying,
46
- enabledTokensMask: 0n,
47
- totalDebtUSD: 0n,
48
- totalDebt: 0n,
49
- tokens: []
50
- };
36
+ const account = existing ?? require_onchain_accounts_intents_utils_credit_account_slice.unopenedAccountSlice({
37
+ creditManager,
38
+ creditFacade: suite.creditFacade.address,
39
+ underlying
40
+ });
51
41
  require_onchain_accounts_intents_math.assertDebtLimits(sdk, debt, suite.creditFacade, underlying);
52
42
  require_onchain_accounts_intents_guards.assertCanBorrow(sdk, suite, debt);
53
43
  const paths = require_onchain_accounts_intents_utils_router_path.createRouterPaths({
@@ -111,35 +101,6 @@ async function buildOpenStrategyState(props) {
111
101
  creditAccount: existing?.creditAccount
112
102
  };
113
103
  }
114
- /**
115
- * The opening that holds an account and nothing else.
116
- *
117
- * Taken before the walk rather than threaded through it: the router has no
118
- * guard for an empty basket and would still make its `eth_call`, and every
119
- * assertion below reads amounts that are not there.
120
- */
121
- async function emptyOpenState(props) {
122
- const { sdk, creditManager } = props;
123
- require_onchain_accounts_intents_guards.assertMarketOperable(sdk.marketRegister.findCreditManager(creditManager));
124
- const snapshot = {
125
- creditManager,
126
- assets: [],
127
- quotas: [],
128
- totalDebt: 0n,
129
- totalValue: 0n
130
- };
131
- const { assets: _assets, quotas: _quotas, ...projection } = sdk.positions.projection(snapshot, { availableLiquidityChange: 0n });
132
- return {
133
- ...projection,
134
- currentPrice: sdk.positions.currentPrice(snapshot),
135
- priceImpact: void 0,
136
- averageAssets: [],
137
- minAssets: [],
138
- averageQuota: [],
139
- minQuota: [],
140
- calls: []
141
- };
142
- }
143
104
  /** Collateral plus the borrowed underlying, folded into one balance per token. */
144
105
  function mergeExpectedBalances(collateral, underlying, debt) {
145
106
  const merged = /* @__PURE__ */ new Map();
@@ -203,6 +203,7 @@ function buildMockSdk(args) {
203
203
  liquidationThresholds,
204
204
  collateralTokens,
205
205
  feeInterest: args.feeInterest ?? 0,
206
+ maxEnabledTokens: args.maxEnabledTokens ?? 4,
206
207
  maxLeverage: (collateral, targetHF) => require_onchain_market_math.calcMaxLeverage(liquidationThresholds.get(collateral) ?? 0, targetHF)
207
208
  },
208
209
  creditFacade: {
@@ -3,6 +3,24 @@ const require_onchain_utils_AddressMap = require("../../../utils/AddressMap.js")
3
3
  //#region src/onchain/accounts/intents/utils/common.ts
4
4
  /** Case-insensitive address equality. */
5
5
  const eq = (a, b) => a.toLowerCase() === b.toLowerCase();
6
+ /**
7
+ * The suite and market behind a credit manager, or nothing where the register
8
+ * has no entry for it.
9
+ *
10
+ * For the reads a form calls on every keystroke, including before the SDK has
11
+ * finished attaching: a question the register cannot answer yet is not an
12
+ * error. Everything that prepares a transaction wants the throw instead.
13
+ */
14
+ function resolveCreditManager(sdk, creditManager) {
15
+ try {
16
+ return {
17
+ suite: sdk.marketRegister.findCreditManager(creditManager),
18
+ market: sdk.marketRegister.findByCreditManager(creditManager)
19
+ };
20
+ } catch {
21
+ return;
22
+ }
23
+ }
6
24
  function toTargetDecimals(fromAmount, fromToken, toToken, sdk) {
7
25
  const fromDecimals = sdk.tokensMeta.get(fromToken)?.decimals ?? 18;
8
26
  const toDecimals = sdk.tokensMeta.get(toToken)?.decimals ?? 18;
@@ -39,5 +57,6 @@ function toRouterCaSlice(creditAccount, expectedBalances = []) {
39
57
  }
40
58
  //#endregion
41
59
  exports.eq = eq;
60
+ exports.resolveCreditManager = resolveCreditManager;
42
61
  exports.toRouterCaSlice = toRouterCaSlice;
43
62
  exports.toTargetDecimals = toTargetDecimals;
@@ -27,6 +27,26 @@ function toCreditAccountSlice(ca) {
27
27
  };
28
28
  }
29
29
  /**
30
+ * The slice a flow that has no account yet quotes against.
31
+ *
32
+ * Nothing of it exists on chain until the transaction lands, and nothing has
33
+ * to: the pathfinder is asked about the credit manager, and every balance the
34
+ * flow reasons about is one the transaction itself puts there. The zero
35
+ * address stands in for the account so the shape is complete.
36
+ */
37
+ function unopenedAccountSlice(args) {
38
+ return {
39
+ creditAccount: "0x0000000000000000000000000000000000000000",
40
+ creditManager: args.creditManager.toLowerCase(),
41
+ creditFacade: args.creditFacade.toLowerCase(),
42
+ underlying: args.underlying.toLowerCase(),
43
+ enabledTokensMask: 0n,
44
+ totalDebtUSD: 0n,
45
+ totalDebt: 0n,
46
+ tokens: []
47
+ };
48
+ }
49
+ /**
30
50
  * Reads an account by address and narrows it to {@link CreditAccountSlice}.
31
51
  *
32
52
  * The shared read model's `StrategyPosition` carries neither `tokens` nor
@@ -43,3 +63,4 @@ async function fetchCreditAccountSlice(sdk, creditAccount) {
43
63
  //#endregion
44
64
  exports.fetchCreditAccountSlice = fetchCreditAccountSlice;
45
65
  exports.toCreditAccountSlice = toCreditAccountSlice;
66
+ exports.unopenedAccountSlice = unopenedAccountSlice;
@@ -26,7 +26,9 @@ exports.lossRate = require_onchain_accounts_intents_utils_price_impact.lossRate;
26
26
  exports.pickFattestNonPhantomToken = require_onchain_accounts_intents_utils_pick_token.pickFattestNonPhantomToken;
27
27
  exports.quotasAfterUpdate = require_onchain_accounts_intents_utils_quotas_for_update.quotasAfterUpdate;
28
28
  exports.rankAccountTokens = require_onchain_accounts_intents_utils_pick_token.rankAccountTokens;
29
+ exports.resolveCreditManager = require_onchain_accounts_intents_utils_common.resolveCreditManager;
29
30
  exports.startProbe = require_onchain_accounts_intents_utils_price_impact.startProbe;
30
31
  exports.toCreditAccountSlice = require_onchain_accounts_intents_utils_credit_account_slice.toCreditAccountSlice;
31
32
  exports.toRouterCaSlice = require_onchain_accounts_intents_utils_common.toRouterCaSlice;
32
33
  exports.toTargetDecimals = require_onchain_accounts_intents_utils_common.toTargetDecimals;
34
+ exports.unopenedAccountSlice = require_onchain_accounts_intents_utils_credit_account_slice.unopenedAccountSlice;