@gearbox-protocol/sdk 16.3.2 → 16.4.0-next.10

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 (139) hide show
  1. package/dist/cjs/dev/index.js +3 -2
  2. package/dist/cjs/dev/kycUtils.js +18 -35
  3. package/dist/cjs/dev/midasUtils.js +40 -11
  4. package/dist/cjs/dev/securitizeUtils.js +14 -10
  5. package/dist/cjs/dev/withdrawalUtils.js +3 -6
  6. package/dist/cjs/model/errors/index.js +2 -0
  7. package/dist/cjs/model/errors/operation-errors.js +18 -0
  8. package/dist/cjs/model/index.js +2 -0
  9. package/dist/cjs/onchain/accounts/CreditAccountsServiceV310.js +0 -1
  10. package/dist/cjs/onchain/accounts/intents/borrow.js +139 -0
  11. package/dist/cjs/onchain/accounts/intents/collateral-valuation.js +41 -0
  12. package/dist/cjs/onchain/accounts/intents/guards.js +15 -3
  13. package/dist/cjs/onchain/accounts/intents/index.js +107 -20
  14. package/dist/cjs/onchain/accounts/intents/leverage-band.js +2 -12
  15. package/dist/cjs/onchain/accounts/intents/maxBorrow.js +89 -0
  16. package/dist/cjs/onchain/accounts/intents/maxWithdrawCollateral.js +15 -41
  17. package/dist/cjs/onchain/accounts/intents/open-strategy.js +6 -45
  18. package/dist/cjs/onchain/accounts/intents/realize.js +54 -2
  19. package/dist/cjs/onchain/accounts/intents/tail.js +1 -1
  20. package/dist/cjs/onchain/accounts/intents/testing/sdk-mock.js +1 -0
  21. package/dist/cjs/onchain/accounts/intents/utils/common.js +19 -0
  22. package/dist/cjs/onchain/accounts/intents/utils/credit-account-slice.js +21 -0
  23. package/dist/cjs/onchain/accounts/intents/utils/index.js +2 -0
  24. package/dist/cjs/onchain/accounts/intents/utils/price-impact.js +3 -3
  25. package/dist/cjs/onchain/accounts/intents/withdraw-limits.js +97 -0
  26. package/dist/cjs/onchain/core/createAddressProvider.js +2 -5
  27. package/dist/cjs/onchain/index.js +6 -0
  28. package/dist/cjs/onchain/market/adapters/abi/conctructorAbi.js +1 -1
  29. package/dist/cjs/onchain/market/adapters/contracts/MidasGatewayAdapterContract.js +11 -3
  30. package/dist/cjs/onchain/positions/calcHealthFactor.js +3 -3
  31. package/dist/cjs/onchain/preview/index.js +2 -0
  32. package/dist/cjs/onchain/preview/preview/index.js +2 -0
  33. package/dist/cjs/onchain/preview/preview/midasGreenlistsAccount.js +17 -0
  34. package/dist/cjs/onchain/preview/preview/previewOpenStrategyPosition.js +13 -16
  35. package/dist/cjs/onchain/preview/preview/previewOperation.js +19 -11
  36. package/dist/cjs/onchain/preview/preview/replayMulticall.js +4 -3
  37. package/dist/cjs/onchain/validation/bundles/checkCollateralFunding.js +3 -3
  38. package/dist/cjs/onchain/validation/bundles/checkCreditOperation.js +4 -3
  39. package/dist/cjs/onchain/validation/bundles/checkMidasAccountGreenlist.js +44 -0
  40. package/dist/cjs/onchain/validation/bundles/checkRWAOpening.js +16 -7
  41. package/dist/cjs/onchain/validation/bundles/index.js +2 -0
  42. package/dist/cjs/onchain/validation/checkOperation.js +0 -1
  43. package/dist/cjs/onchain/validation/checks/checkReservePriceLimited.js +30 -0
  44. package/dist/cjs/onchain/validation/checks/index.js +2 -0
  45. package/dist/cjs/onchain/validation/index.js +4 -0
  46. package/dist/cjs/sdk/execute/ExecuteApi.js +65 -5
  47. package/dist/cjs/sdk/prepare/PrepareApi.js +89 -14
  48. package/dist/esm/dev/index.js +3 -3
  49. package/dist/esm/dev/kycUtils.js +17 -33
  50. package/dist/esm/dev/midasUtils.js +39 -12
  51. package/dist/esm/dev/securitizeUtils.js +15 -11
  52. package/dist/esm/dev/withdrawalUtils.js +3 -6
  53. package/dist/esm/model/errors/index.js +2 -2
  54. package/dist/esm/model/errors/operation-errors.js +17 -1
  55. package/dist/esm/model/index.js +2 -2
  56. package/dist/esm/onchain/accounts/CreditAccountsServiceV310.js +0 -1
  57. package/dist/esm/onchain/accounts/intents/borrow.js +137 -0
  58. package/dist/esm/onchain/accounts/intents/collateral-valuation.js +40 -0
  59. package/dist/esm/onchain/accounts/intents/guards.js +15 -3
  60. package/dist/esm/onchain/accounts/intents/index.js +107 -20
  61. package/dist/esm/onchain/accounts/intents/leverage-band.js +2 -12
  62. package/dist/esm/onchain/accounts/intents/maxBorrow.js +87 -0
  63. package/dist/esm/onchain/accounts/intents/maxWithdrawCollateral.js +15 -41
  64. package/dist/esm/onchain/accounts/intents/open-strategy.js +6 -45
  65. package/dist/esm/onchain/accounts/intents/realize.js +54 -2
  66. package/dist/esm/onchain/accounts/intents/tail.js +1 -1
  67. package/dist/esm/onchain/accounts/intents/testing/sdk-mock.js +1 -0
  68. package/dist/esm/onchain/accounts/intents/utils/common.js +19 -1
  69. package/dist/esm/onchain/accounts/intents/utils/credit-account-slice.js +21 -1
  70. package/dist/esm/onchain/accounts/intents/utils/index.js +3 -3
  71. package/dist/esm/onchain/accounts/intents/utils/price-impact.js +3 -3
  72. package/dist/esm/onchain/accounts/intents/withdraw-limits.js +95 -0
  73. package/dist/esm/onchain/core/createAddressProvider.js +2 -5
  74. package/dist/esm/onchain/index.js +4 -1
  75. package/dist/esm/onchain/market/adapters/abi/conctructorAbi.js +1 -1
  76. package/dist/esm/onchain/market/adapters/contracts/MidasGatewayAdapterContract.js +11 -3
  77. package/dist/esm/onchain/positions/calcHealthFactor.js +3 -3
  78. package/dist/esm/onchain/preview/index.js +2 -1
  79. package/dist/esm/onchain/preview/preview/index.js +2 -1
  80. package/dist/esm/onchain/preview/preview/midasGreenlistsAccount.js +16 -0
  81. package/dist/esm/onchain/preview/preview/previewOpenStrategyPosition.js +14 -17
  82. package/dist/esm/onchain/preview/preview/previewOperation.js +19 -11
  83. package/dist/esm/onchain/preview/preview/replayMulticall.js +4 -3
  84. package/dist/esm/onchain/validation/bundles/checkCollateralFunding.js +3 -3
  85. package/dist/esm/onchain/validation/bundles/checkCreditOperation.js +4 -3
  86. package/dist/esm/onchain/validation/bundles/checkMidasAccountGreenlist.js +43 -0
  87. package/dist/esm/onchain/validation/bundles/checkRWAOpening.js +16 -7
  88. package/dist/esm/onchain/validation/bundles/index.js +2 -1
  89. package/dist/esm/onchain/validation/checkOperation.js +0 -1
  90. package/dist/esm/onchain/validation/checks/checkReservePriceLimited.js +29 -0
  91. package/dist/esm/onchain/validation/checks/index.js +2 -1
  92. package/dist/esm/onchain/validation/index.js +3 -1
  93. package/dist/esm/sdk/execute/ExecuteApi.js +65 -5
  94. package/dist/esm/sdk/prepare/PrepareApi.js +89 -14
  95. package/dist/types/dev/index.d.ts +3 -3
  96. package/dist/types/dev/kycUtils.d.ts +1 -5
  97. package/dist/types/dev/midasUtils.d.ts +11 -1
  98. package/dist/types/model/errors/index.d.ts +2 -2
  99. package/dist/types/model/errors/operation-errors.d.ts +54 -1
  100. package/dist/types/model/index.d.ts +3 -3
  101. package/dist/types/model/previews.d.ts +25 -19
  102. package/dist/types/onchain/accounts/index.d.ts +4 -3
  103. package/dist/types/onchain/accounts/intents/borrow.d.ts +141 -0
  104. package/dist/types/onchain/accounts/intents/collateral-valuation.d.ts +42 -0
  105. package/dist/types/onchain/accounts/intents/guards.d.ts +18 -2
  106. package/dist/types/onchain/accounts/intents/index.d.ts +102 -14
  107. package/dist/types/onchain/accounts/intents/maxBorrow.d.ts +54 -0
  108. package/dist/types/onchain/accounts/intents/maxWithdrawCollateral.d.ts +3 -8
  109. package/dist/types/onchain/accounts/intents/open-strategy.d.ts +3 -17
  110. package/dist/types/onchain/accounts/intents/testing/sdk-mock.d.ts +2 -0
  111. package/dist/types/onchain/accounts/intents/tests/open-strategy.fixtures.d.ts +2 -2
  112. package/dist/types/onchain/accounts/intents/types.d.ts +41 -5
  113. package/dist/types/onchain/accounts/intents/utils/common.d.ts +15 -1
  114. package/dist/types/onchain/accounts/intents/utils/credit-account-slice.d.ts +14 -1
  115. package/dist/types/onchain/accounts/intents/utils/index.d.ts +3 -3
  116. package/dist/types/onchain/accounts/intents/withdraw-limits.d.ts +80 -0
  117. package/dist/types/onchain/index.d.ts +8 -4
  118. package/dist/types/onchain/market/adapters/contracts/MidasGatewayAdapterContract.d.ts +8 -2
  119. package/dist/types/onchain/preview/index.d.ts +2 -1
  120. package/dist/types/onchain/preview/preview/index.d.ts +2 -1
  121. package/dist/types/onchain/preview/preview/midasGreenlistsAccount.d.ts +13 -0
  122. package/dist/types/onchain/preview/preview/previewOpenStrategyPosition.d.ts +5 -3
  123. package/dist/types/onchain/preview/preview/replayMulticall.d.ts +8 -7
  124. package/dist/types/onchain/validation/bundles/checkCreditOperation.d.ts +2 -2
  125. package/dist/types/onchain/validation/bundles/checkMidasAccountGreenlist.d.ts +22 -0
  126. package/dist/types/onchain/validation/bundles/checkRWAOpening.d.ts +6 -4
  127. package/dist/types/onchain/validation/bundles/index.d.ts +3 -2
  128. package/dist/types/onchain/validation/checks/checkDebtLimits.d.ts +2 -2
  129. package/dist/types/onchain/validation/checks/checkReservePriceLimited.d.ts +31 -0
  130. package/dist/types/onchain/validation/checks/index.d.ts +2 -1
  131. package/dist/types/onchain/validation/index.d.ts +4 -2
  132. package/dist/types/onchain/validation/raise.d.ts +2 -2
  133. package/dist/types/sdk/execute/index.d.ts +2 -2
  134. package/dist/types/sdk/execute/types.d.ts +48 -6
  135. package/dist/types/sdk/index.d.ts +5 -4
  136. package/dist/types/sdk/prepare/PrepareApi.d.ts +16 -4
  137. package/dist/types/sdk/prepare/index.d.ts +4 -3
  138. package/dist/types/sdk/prepare/types.d.ts +199 -65
  139. package/package.json +1 -1
@@ -35,7 +35,7 @@ function calcHealthFactor(props) {
35
35
  const scale = 10n ** BigInt(decimalsByToken.get(token) ?? 18);
36
36
  return amount * price / scale;
37
37
  };
38
- const assetMoney = snapshot.assets.reduce((acc, { token, balance }) => {
38
+ const assetValue = snapshot.assets.reduce((acc, { token, balance }) => {
39
39
  if (balance <= 10n) return acc;
40
40
  const lt = BigInt(lts.get(token) ?? 0);
41
41
  const tokenLtWeighted = (convertToUSD(token, balance, true) ?? 0n) * lt;
@@ -44,8 +44,8 @@ function calcHealthFactor(props) {
44
44
  const quotaWeighted = (convertToUSD(underlying, quotaBalance) ?? 0n) * PERCENTAGE_FACTOR;
45
45
  return acc + (quota ? BigIntMath.min(quotaWeighted, tokenLtWeighted) : tokenLtWeighted);
46
46
  }, 0n);
47
- const borrowedMoney = convertToUSD(underlying, snapshot.totalDebt) ?? 0n;
48
- const hf = borrowedMoney > 0n ? assetMoney / borrowedMoney : 0n;
47
+ const borrowedValue = convertToUSD(underlying, snapshot.totalDebt) ?? 0n;
48
+ const hf = borrowedValue > 0n ? assetValue / borrowedValue : 0n;
49
49
  return Number(hf);
50
50
  }
51
51
  //#endregion
@@ -11,10 +11,11 @@ import { classifyCloseOrRepay, isCloseOrRepay } from "./preview/detectCloseOrRep
11
11
  import { detectDelayedClaim, resolveDelayedClaimIntent } from "./preview/detectDelayedClaim.js";
12
12
  import { detectDelayedOperation } from "./preview/detectDelayedOperation.js";
13
13
  import { estimateClaimableAt } from "./preview/estimateClaimableAt.js";
14
+ import { midasGreenlistsAccount } from "./preview/midasGreenlistsAccount.js";
14
15
  import { previewAdjustStrategyPosition } from "./preview/previewAdjustStrategyPosition.js";
15
16
  import { previewExitOrRepayStrategyPosition } from "./preview/previewExitOrRepayStrategyPosition.js";
16
17
  import { makeReplayState, replayInnerOperations } from "./preview/replayInnerOperations.js";
17
18
  import { replayMulticall } from "./preview/replayMulticall.js";
18
19
  import { previewOperation } from "./preview/previewOperation.js";
19
20
  import "./preview/index.js";
20
- export { CreditAccountState, buildDelayedStrategyPositionOperationPreview, classifyCloseOrRepay, classifyInnerOperations, detectDelayedClaim, detectDelayedOperation, estimateClaimableAt, isCloseOrRepay, isPoolOperation, isRWAOperation, makeReplayState, parseFacadeOperationCalldata, parseOperationCalldata, parsePoolOperationCalldata, parseRWAFactoryOperationCalldata, previewAdjustStrategyPosition, previewExitOrRepayStrategyPosition, previewOperation, replayInnerOperations, replayMulticall, resolveDelayedClaimIntent };
21
+ export { CreditAccountState, buildDelayedStrategyPositionOperationPreview, classifyCloseOrRepay, classifyInnerOperations, detectDelayedClaim, detectDelayedOperation, estimateClaimableAt, isCloseOrRepay, isPoolOperation, isRWAOperation, makeReplayState, midasGreenlistsAccount, parseFacadeOperationCalldata, parseOperationCalldata, parsePoolOperationCalldata, parseRWAFactoryOperationCalldata, previewAdjustStrategyPosition, previewExitOrRepayStrategyPosition, previewOperation, replayInnerOperations, replayMulticall, resolveDelayedClaimIntent };
@@ -4,9 +4,10 @@ import { classifyCloseOrRepay, isCloseOrRepay } from "./detectCloseOrRepay.js";
4
4
  import { detectDelayedClaim, resolveDelayedClaimIntent } from "./detectDelayedClaim.js";
5
5
  import { detectDelayedOperation } from "./detectDelayedOperation.js";
6
6
  import { estimateClaimableAt } from "./estimateClaimableAt.js";
7
+ import { midasGreenlistsAccount } from "./midasGreenlistsAccount.js";
7
8
  import { previewAdjustStrategyPosition } from "./previewAdjustStrategyPosition.js";
8
9
  import { previewExitOrRepayStrategyPosition } from "./previewExitOrRepayStrategyPosition.js";
9
10
  import { makeReplayState, replayInnerOperations } from "./replayInnerOperations.js";
10
11
  import { replayMulticall } from "./replayMulticall.js";
11
12
  import { previewOperation } from "./previewOperation.js";
12
- export { CreditAccountState, buildDelayedStrategyPositionOperationPreview, classifyCloseOrRepay, detectDelayedClaim, detectDelayedOperation, estimateClaimableAt, isCloseOrRepay, makeReplayState, previewAdjustStrategyPosition, previewExitOrRepayStrategyPosition, previewOperation, replayInnerOperations, replayMulticall, resolveDelayedClaimIntent };
13
+ export { CreditAccountState, buildDelayedStrategyPositionOperationPreview, classifyCloseOrRepay, detectDelayedClaim, detectDelayedOperation, estimateClaimableAt, isCloseOrRepay, makeReplayState, midasGreenlistsAccount, previewAdjustStrategyPosition, previewExitOrRepayStrategyPosition, previewOperation, replayInnerOperations, replayMulticall, resolveDelayedClaimIntent };
@@ -0,0 +1,16 @@
1
+ import { MidasGatewayAdapterContract } from "../../market/adapters/contracts/MidasGatewayAdapterContract.js";
2
+ //#region src/onchain/preview/preview/midasGreenlistsAccount.ts
3
+ /**
4
+ * Whether the multicall grants the Midas greenlisted role to the credit
5
+ * account via `receiveGreenlist()`.
6
+ */
7
+ function midasGreenlistsAccount(sdk, multicall) {
8
+ for (const op of multicall) {
9
+ if (op.operation !== "Execute") continue;
10
+ const adapter = sdk.getContract(op.adapter);
11
+ if (adapter instanceof MidasGatewayAdapterContract && adapter.isReceiveGreenlist(op.calldata)) return true;
12
+ }
13
+ return false;
14
+ }
15
+ //#endregion
16
+ export { midasGreenlistsAccount };
@@ -1,26 +1,24 @@
1
1
  import { AP_WETH_TOKEN } from "../../constants/address-provider.js";
2
2
  import { asEstimated } from "../../../model/previews.js";
3
- import { sdkErr, sdkOk } from "../../../model/result.js";
3
+ import { sdkOk } from "../../../model/result.js";
4
4
  import "../../../model/index.js";
5
- import { CreditAccountState } from "./CreditAccountState.js";
5
+ import { midasGreenlistsAccount } from "./midasGreenlistsAccount.js";
6
6
  import { unwrapNativeCollateral } from "./unwrapNativeCollateral.js";
7
- import { makeReplayState, replayInnerOperations } from "./replayInnerOperations.js";
8
7
  //#region src/onchain/preview/preview/previewOpenStrategyPosition.ts
9
- function previewOpenStrategyPosition(sdk, input, operation) {
8
+ function previewOpenStrategyPosition(sdk, input, operation, replay) {
10
9
  const { value = 0n } = input;
11
10
  const market = sdk.marketRegister.findByCreditManager(operation.creditManager);
12
11
  const oracle = market.priceOracle;
13
- const state = makeReplayState(CreditAccountState.beforeOpen(operation.creditManager, market.underlying));
14
- const replayError = replayInnerOperations(sdk, operation.multicall, state);
15
- if (replayError) return sdkErr(replayError);
16
- const account = state.account;
12
+ const { before, after } = replay;
13
+ const account = after.account;
17
14
  let warning;
18
- const netValue = state.collateralAdded.sum((token, balance) => {
15
+ const price = (token, balance) => {
19
16
  const priced = oracle.safeConvert(token, market.underlying, balance);
20
17
  warning ??= priced.error;
21
18
  return priced.value;
22
- });
23
- const unwrapped = unwrapNativeCollateral(state.collateralAdded.toAssets(), value, sdk.addressProvider.getAddress(AP_WETH_TOKEN, 0));
19
+ };
20
+ const netValue = before.balances.sum(price) + after.collateralAdded.sum(price) - after.collateralWithdrawn.sum(price);
21
+ const unwrapped = unwrapNativeCollateral(after.collateralAdded.toAssets(), value, sdk.addressProvider.getAddress(AP_WETH_TOKEN, 0));
24
22
  if (!unwrapped.ok) return unwrapped;
25
23
  const collateral = unwrapped.data;
26
24
  const snap = account.toSnapshot(netValue + account.totalDebt);
@@ -29,16 +27,15 @@ function previewOpenStrategyPosition(sdk, input, operation) {
29
27
  ...asEstimated(sdk.positions.projection(snap, { availableLiquidityChange: -account.totalDebt })),
30
28
  targetCollateral: targetAsset ? oracle.toTokenAmount(targetAsset.token, targetAsset.balance) : void 0,
31
29
  collateralAdded: collateral.map((a) => oracle.toTokenAmount(a.token, a.balance)),
30
+ collateralWithdrawn: after.collateralWithdrawn.toAssets().map((a) => oracle.toTokenAmount(a.token, a.balance)),
32
31
  warning
33
32
  };
34
- if (operation.operation === "RWAOpenCreditAccount") return sdkOk({
35
- ...projection,
36
- operation: "RWAOpenCreditAccount",
37
- rwaArgs: operation.args
38
- });
39
33
  return sdkOk({
40
34
  ...projection,
41
- operation: "OpenCreditAccount"
35
+ operation: "OpenCreditAccount",
36
+ ...operation.operation === "MultiCall" || operation.operation === "BotMulticall" || operation.operation === "RWAMulticall" ? { creditAccount: operation.creditAccount } : {},
37
+ ..."args" in operation ? { rwaArgs: operation.args } : {},
38
+ midasGreenlistsAccount: midasGreenlistsAccount(sdk, operation.multicall)
42
39
  });
43
40
  }
44
41
  /**
@@ -33,9 +33,13 @@ async function previewOperation(sdk, input, options, creditAccount) {
33
33
  if (!parsed.ok) return parsed;
34
34
  const operation = parsed.data;
35
35
  if (isPoolOperation(operation)) return previewPoolPositionOperation(sdk, operation, options);
36
- if (operation.operation === "OpenCreditAccount" || operation.operation === "RWAOpenCreditAccount") return previewOpenStrategyPosition(sdk, input, operation);
36
+ if (operation.operation === "OpenCreditAccount" || operation.operation === "RWAOpenCreditAccount") {
37
+ const replayed = replayMulticall(sdk, operation);
38
+ if (!replayed.ok) return replayed;
39
+ return previewOpenStrategyPosition(sdk, input, operation, replayed.data);
40
+ }
37
41
  if (operation.operation === "CloseCreditAccount") {
38
- const resolved = await resolveCreditAccount(sdk, operation, options, creditAccount);
42
+ const resolved = await resolveCreditAccount(sdk, operation.creditAccount, options, creditAccount);
39
43
  if (!resolved.ok) return resolved;
40
44
  const replayed = replayMulticall(sdk, operation, resolved.data);
41
45
  if (!replayed.ok) return replayed;
@@ -47,7 +51,7 @@ async function previewOperation(sdk, input, options, creditAccount) {
47
51
  return preview;
48
52
  }
49
53
  if (operation.operation === "MultiCall" || operation.operation === "BotMulticall" || operation.operation === "RWAMulticall") {
50
- const resolved = await resolveCreditAccount(sdk, operation, options, creditAccount);
54
+ const resolved = await resolveCreditAccount(sdk, operation.creditAccount, options, creditAccount);
51
55
  if (!resolved.ok) return resolved;
52
56
  const replayed = replayMulticall(sdk, operation, resolved.data);
53
57
  if (!replayed.ok) return replayed;
@@ -64,20 +68,24 @@ async function previewOperation(sdk, input, options, creditAccount) {
64
68
  * injected state when present, otherwise fetches it from the credit account
65
69
  * compressor.
66
70
  */
67
- async function resolveCreditAccount(sdk, operation, options, creditAccount) {
68
- let resolved = creditAccount;
69
- if (!resolved) resolved = await sdk.accounts.getCreditAccountData(operation.creditAccount, options?.blockNumber);
70
- if (!resolved) return sdkErr(creditAccountNotFound(operation.creditAccount));
71
+ async function resolveCreditAccount(sdk, address, options, data) {
72
+ let resolved = data;
73
+ if (!resolved) resolved = await sdk.accounts.getCreditAccountData(address, options?.blockNumber);
74
+ if (!resolved) return sdkErr(creditAccountNotFound(address));
71
75
  return sdkOk(resolved);
72
76
  }
73
77
  /**
74
78
  * Previews a plain/bot/RWA multicall: classifies the instant preview
75
- * (zero-debt closure/repay vs adjustment) and, when the multicall requests a
76
- * delayed withdrawal, wraps the instant preview into a
77
- * `DelayedCreditAccountOperation` together with the best-effort preview of
78
- * the state after the withdrawal is claimed.
79
+ * (reopening a zero-debt account, zero-debt closure/repay, or adjustment)
80
+ * and, when the multicall requests a delayed withdrawal, wraps the instant
81
+ * preview into a `DelayedCreditAccountOperation` together with the
82
+ * best-effort preview of the state after the withdrawal is claimed.
83
+ *
84
+ * A reopening is reported as `OpenCreditAccount` and never as a delayed
85
+ * withdrawal: an opening requests no redemption.
79
86
  */
80
87
  async function previewMulticallOperation(sdk, input, operation, replay, blockNumber) {
88
+ if (replay.before.totalDebt === 0n && replay.before.quotas.toAssets(0n).length === 0 && replay.after.account.totalDebt > 0n) return previewOpenStrategyPosition(sdk, input, operation, replay);
81
89
  let instantPreview;
82
90
  if (isCloseOrRepay(operation.multicall)) {
83
91
  const instant = previewExitOrRepayStrategyPosition(sdk, input, operation, false, replay);
@@ -4,11 +4,12 @@ import { CreditAccountState } from "./CreditAccountState.js";
4
4
  import { makeReplayState, replayInnerOperations } from "./replayInnerOperations.js";
5
5
  //#region src/onchain/preview/preview/replayMulticall.ts
6
6
  /**
7
- * Replays the operation's multicall over the account's pre-resolved
8
- * pre-state via {@link replayInnerOperations}.
7
+ * Replays the operation's multicall via {@link replayInnerOperations}.
8
+ * When `creditAccount` is omitted, the seed is
9
+ * {@link CreditAccountState.beforeOpen} (a fresh opening).
9
10
  */
10
11
  function replayMulticall(sdk, operation, creditAccount) {
11
- const before = CreditAccountState.fromCreditAccountData(creditAccount);
12
+ const before = creditAccount ? CreditAccountState.fromCreditAccountData(creditAccount) : CreditAccountState.beforeOpen(operation.creditManager, sdk.marketRegister.findByCreditManager(operation.creditManager).underlying);
12
13
  const after = makeReplayState(before.clone());
13
14
  const error = replayInnerOperations(sdk, operation.multicall, after);
14
15
  if (error) return sdkErr(error);
@@ -22,13 +22,13 @@ async function checkCollateralFunding(input) {
22
22
  if (held.size === 0) return [];
23
23
  let spender;
24
24
  try {
25
- if (preview.operation === "OpenCreditAccount" || preview.operation === "RWAOpenCreditAccount") spender = await sdk.accounts.getApprovalAddress({
25
+ if (preview.creditAccount) spender = await sdk.accounts.getApprovalAddress({
26
26
  creditManager: preview.creditManager,
27
- borrower: sender
27
+ creditAccount: preview.creditAccount
28
28
  });
29
29
  else spender = await sdk.accounts.getApprovalAddress({
30
30
  creditManager: preview.creditManager,
31
- creditAccount: preview.creditAccount
31
+ borrower: sender
32
32
  });
33
33
  } catch (cause) {
34
34
  return [unexpectedFailure(cause, "resolve the approval address")];
@@ -25,7 +25,8 @@ async function checkCreditOperation(args) {
25
25
  const suite = sdk.marketRegister.findCreditManager(preview.creditManager);
26
26
  const market = suite.market;
27
27
  const underlying = toToken(sdk, market.pool.underlying);
28
- const isOpening = preview.operation === "OpenCreditAccount" || preview.operation === "RWAOpenCreditAccount";
28
+ const isOpening = preview.operation === "OpenCreditAccount";
29
+ const isEmptyOpening = preview.operation === "OpenCreditAccount" && preview.totalDebt.value === 0n && preview.collateralAdded.length === 0 && preview.quotas.length === 0;
29
30
  const protocol = [
30
31
  ...checkMarket(suite),
31
32
  ...checkDebtLimits({
@@ -33,7 +34,7 @@ async function checkCreditOperation(args) {
33
34
  minDebt: suite.creditFacade.minDebt,
34
35
  maxDebt: suite.creditFacade.maxDebt,
35
36
  underlying,
36
- allowZero: !isOpening,
37
+ allowZero: !isOpening || isEmptyOpening,
37
38
  maxBorrowAmount: suite.maxBorrowAmount()
38
39
  }),
39
40
  ...checkIncreaseDebt(suite, preview, underlying),
@@ -51,7 +52,7 @@ async function checkCreditOperation(args) {
51
52
  preview,
52
53
  sender,
53
54
  blockNumber
54
- }), preview.operation === "OpenCreditAccount" || preview.operation === "RWAOpenCreditAccount" ? checkRWAOpening({
55
+ }), isOpening ? checkRWAOpening({
55
56
  sdk,
56
57
  preview,
57
58
  sender
@@ -0,0 +1,43 @@
1
+ import { accountNotMidasGreenlisted } from "../../../model/errors/operation-errors.js";
2
+ import { unexpectedFailure } from "../../../model/errors/prepare-errors.js";
3
+ import "../../../model/index.js";
4
+ import { toToken } from "../helpers/token.js";
5
+ import "../helpers/index.js";
6
+ //#region src/onchain/validation/bundles/checkMidasAccountGreenlist.ts
7
+ /**
8
+ * Whether the credit account already holds the Midas greenlisted role
9
+ * this permissioned mToken requires of its holder.
10
+ */
11
+ async function checkMidasAccountGreenlist(input) {
12
+ const { sdk, preview, nft, tokens } = input;
13
+ if (nft.protocol !== "midas") return [];
14
+ if (tokens.length === 0) return [];
15
+ const account = preview.creditAccount;
16
+ if (!account) return tokens.map((token) => accountNotMidasGreenlisted({
17
+ token: toToken(sdk, token),
18
+ creditManager: preview.creditManager
19
+ }));
20
+ return (await Promise.all(tokens.map((token) => checkAccountRole({
21
+ sdk,
22
+ nft,
23
+ account,
24
+ token,
25
+ preview
26
+ })))).flat();
27
+ }
28
+ async function checkAccountRole(input) {
29
+ const { sdk, nft, account, token, preview } = input;
30
+ try {
31
+ const requirements = await nft.getOpenAccountRequirements(account, { tokenOutAddress: token });
32
+ if (nft.isRegistered(requirements)) return [];
33
+ return [accountNotMidasGreenlisted({
34
+ token: toToken(sdk, token),
35
+ creditManager: preview.creditManager,
36
+ creditAccount: account
37
+ })];
38
+ } catch (cause) {
39
+ return [unexpectedFailure(cause, "read the Midas account greenlist")];
40
+ }
41
+ }
42
+ //#endregion
43
+ export { checkMidasAccountGreenlist };
@@ -1,24 +1,33 @@
1
1
  import { AddressSet } from "../../utils/AddressSet.js";
2
+ import { checkMidasAccountGreenlist } from "./checkMidasAccountGreenlist.js";
2
3
  import { checkRWAOpenRequirements } from "./checkRWAOpenRequirements.js";
3
4
  //#region src/onchain/validation/bundles/checkRWAOpening.ts
4
5
  /**
5
6
  * Per KYC-gated token among `collateralAdded ∪ quotas`, whether the
6
- * borrower still has to register or sign before this opening can land.
7
+ * borrower still has to register or sign before this opening can land,
8
+ * and whether a Midas credit account may hold the mToken.
7
9
  */
8
10
  async function checkRWAOpening(input) {
9
11
  const { sdk, preview, sender } = input;
10
12
  const nft = await sdk.marketRegister.findCreditManager(preview.creditManager).degenNFT();
11
13
  if (!nft) return [];
12
- const gated = new AddressSet(await nft.getTokens());
13
- const candidates = new AddressSet([...preview.collateralAdded.map((a) => a.token.address), ...preview.quotas.map((q) => q.token.address)]);
14
- const providedArgs = preview.operation === "RWAOpenCreditAccount" ? preview.rwaArgs : void 0;
15
- return (await Promise.all([...candidates].filter((token) => gated.has(token)).map((token) => checkRWAOpenRequirements({
14
+ const nftTokens = new AddressSet(await nft.getTokens());
15
+ let candidates = new AddressSet([...preview.collateralAdded.map((a) => a.token.address), ...preview.quotas.map((q) => q.token.address)]).asArray();
16
+ candidates = candidates.filter((token) => nftTokens.has(token));
17
+ let tokensToCheck = candidates;
18
+ if (tokensToCheck.length === 0 && !preview.creditAccount && nft.protocol === "midas") tokensToCheck = nftTokens.asArray();
19
+ return (await Promise.all([...tokensToCheck.map((token) => checkRWAOpenRequirements({
16
20
  sdk,
17
21
  wallet: sender,
18
22
  creditManager: preview.creditManager,
19
23
  token,
20
- providedArgs
21
- })))).flat();
24
+ providedArgs: preview.rwaArgs
25
+ })), checkMidasAccountGreenlist({
26
+ sdk,
27
+ preview,
28
+ nft,
29
+ tokens: candidates
30
+ })])).flat();
22
31
  }
23
32
  //#endregion
24
33
  export { checkRWAOpening };
@@ -7,6 +7,7 @@ import { checkIncreaseDebt } from "./checkIncreaseDebt.js";
7
7
  import { checkIncreaseQuota } from "./checkIncreaseQuota.js";
8
8
  import { checkMarket } from "./checkMarket.js";
9
9
  import { checkObtained } from "./checkObtained.js";
10
+ import { checkMidasAccountGreenlist } from "./checkMidasAccountGreenlist.js";
10
11
  import { checkRWAOpenRequirements } from "./checkRWAOpenRequirements.js";
11
12
  import { checkRWAOpening } from "./checkRWAOpening.js";
12
13
  import { checkCreditOperation } from "./checkCreditOperation.js";
@@ -15,4 +16,4 @@ import { checkLiquidationFunding } from "./checkLiquidationFunding.js";
15
16
  import { checkWallet } from "./checkWallet.js";
16
17
  import { checkPoolFunding } from "./checkPoolFunding.js";
17
18
  import { checkPoolOperation } from "./checkPoolOperation.js";
18
- export { checkAccountQuotas, checkCollateralFunding, checkCreditOperation, checkHealthFactors, checkIncreaseDebt, checkIncreaseQuota, checkLiquidationEligibility, checkLiquidationFunding, checkMarket, checkObtained, checkPoolFunding, checkPoolOperation, checkRWAOpenRequirements, checkRWAOpening, checkWallet, checkWalletAllowance, checkWalletBalance };
19
+ export { checkAccountQuotas, checkCollateralFunding, checkCreditOperation, checkHealthFactors, checkIncreaseDebt, checkIncreaseQuota, checkLiquidationEligibility, checkLiquidationFunding, checkMarket, checkMidasAccountGreenlist, checkObtained, checkPoolFunding, checkPoolOperation, checkRWAOpenRequirements, checkRWAOpening, checkWallet, checkWalletAllowance, checkWalletBalance };
@@ -42,7 +42,6 @@ async function checkOperation(input, options = {}) {
42
42
  sender
43
43
  }, options);
44
44
  case "OpenCreditAccount":
45
- case "RWAOpenCreditAccount":
46
45
  case "AdjustCreditAccount": return checkCreditOperation({
47
46
  sdk,
48
47
  preview,
@@ -0,0 +1,29 @@
1
+ import { reservePriceLimited } from "../../../model/errors/operation-errors.js";
2
+ import "../../../model/index.js";
3
+ //#region src/onchain/validation/checks/checkReservePriceLimited.ts
4
+ /**
5
+ * Whether a failed collateral check is the reserve price feed's doing.
6
+ *
7
+ * A call that hands funds over is weighed at safe prices — `min` of a token's
8
+ * two feeds, and nothing at all where governance registered no reserve feed —
9
+ * so an account that covers its debt at the main feed can still be refused.
10
+ * The two are worth telling apart: a position that is genuinely too small is
11
+ * fixed by adding collateral or requesting less, while this one is a valuation
12
+ * the account does not control, and requesting less only helps as far as
13
+ * `withdrawable` says it does.
14
+ *
15
+ * Runs after `checkCollateralised` and answers only when that one refused, so
16
+ * the caller keeps its own threshold rather than restating it here.
17
+ */
18
+ function checkReservePriceLimited(args) {
19
+ const { healthFactor, atMainPrices, healthFactorThreshold, withdrawable } = args;
20
+ if (healthFactor >= healthFactorThreshold || atMainPrices < healthFactorThreshold) return [];
21
+ return [reservePriceLimited({
22
+ healthFactor,
23
+ atMainPrices,
24
+ healthFactorThreshold,
25
+ withdrawable
26
+ })];
27
+ }
28
+ //#endregion
29
+ export { checkReservePriceLimited };
@@ -13,4 +13,5 @@ import { checkPoolPaused } from "./checkPoolPaused.js";
13
13
  import { checkPoolSunset } from "./checkPoolSunset.js";
14
14
  import { checkQuotaCount } from "./checkQuotaCount.js";
15
15
  import { checkQuotaLimit } from "./checkQuotaLimit.js";
16
- export { checkBorrowLimit, checkCollateralised, checkCreditAccountFrozen, checkCreditManagerPaused, checkDebtLimits, checkEmergencyLiquidator, checkForbiddenToken, checkLeverage, checkLiquidatorEligible, checkMarketExpired, checkPoolLiquidity, checkPoolPaused, checkPoolSunset, checkQuotaCount, checkQuotaLimit };
16
+ import { checkReservePriceLimited } from "./checkReservePriceLimited.js";
17
+ export { checkBorrowLimit, checkCollateralised, checkCreditAccountFrozen, checkCreditManagerPaused, checkDebtLimits, checkEmergencyLiquidator, checkForbiddenToken, checkLeverage, checkLiquidatorEligible, checkMarketExpired, checkPoolLiquidity, checkPoolPaused, checkPoolSunset, checkQuotaCount, checkQuotaLimit, checkReservePriceLimited };
@@ -17,6 +17,7 @@ import { checkPoolPaused } from "./checks/checkPoolPaused.js";
17
17
  import { checkPoolSunset } from "./checks/checkPoolSunset.js";
18
18
  import { checkQuotaCount } from "./checks/checkQuotaCount.js";
19
19
  import { checkQuotaLimit } from "./checks/checkQuotaLimit.js";
20
+ import { checkReservePriceLimited } from "./checks/checkReservePriceLimited.js";
20
21
  import "./checks/index.js";
21
22
  import { checkAccountQuotas } from "./bundles/checkAccountQuotas.js";
22
23
  import { checkWalletAllowance } from "./bundles/checkWalletAllowance.js";
@@ -27,6 +28,7 @@ import { checkIncreaseDebt } from "./bundles/checkIncreaseDebt.js";
27
28
  import { checkIncreaseQuota } from "./bundles/checkIncreaseQuota.js";
28
29
  import { checkMarket } from "./bundles/checkMarket.js";
29
30
  import { checkObtained } from "./bundles/checkObtained.js";
31
+ import { checkMidasAccountGreenlist } from "./bundles/checkMidasAccountGreenlist.js";
30
32
  import { checkRWAOpenRequirements } from "./bundles/checkRWAOpenRequirements.js";
31
33
  import { checkRWAOpening } from "./bundles/checkRWAOpening.js";
32
34
  import { checkCreditOperation } from "./bundles/checkCreditOperation.js";
@@ -40,4 +42,4 @@ import { checkLiquidation } from "./checkLiquidation.js";
40
42
  import { checkOperation } from "./checkOperation.js";
41
43
  import { checkSimulation } from "./checkSimulation.js";
42
44
  import { raise } from "./raise.js";
43
- export { MIN_HEALTH_FACTOR_FACADE, MIN_HEALTH_FACTOR_FORM, MIN_HF_LIMITED, MIN_SAFE_HEALTH_FACTOR_FORM, amountOf, checkAccountQuotas, checkBorrowLimit, checkCollateralFunding, checkCollateralised, checkCreditAccountFrozen, checkCreditManagerPaused, checkCreditOperation, checkDebtLimits, checkEmergencyLiquidator, checkForbiddenToken, checkHealthFactors, checkIncreaseDebt, checkIncreaseQuota, checkLeverage, checkLiquidation, checkLiquidationEligibility, checkLiquidationFunding, checkLiquidatorEligible, checkMarket, checkMarketExpired, checkObtained, checkOperation, checkPoolFunding, checkPoolLiquidity, checkPoolOperation, checkPoolPaused, checkPoolSunset, checkQuotaCount, checkQuotaLimit, checkRWAOpenRequirements, checkRWAOpening, checkSimulation, checkWallet, checkWalletAllowance, checkWalletBalance, raise, toToken, toTokenAmount };
45
+ export { MIN_HEALTH_FACTOR_FACADE, MIN_HEALTH_FACTOR_FORM, MIN_HF_LIMITED, MIN_SAFE_HEALTH_FACTOR_FORM, amountOf, checkAccountQuotas, checkBorrowLimit, checkCollateralFunding, checkCollateralised, checkCreditAccountFrozen, checkCreditManagerPaused, checkCreditOperation, checkDebtLimits, checkEmergencyLiquidator, checkForbiddenToken, checkHealthFactors, checkIncreaseDebt, checkIncreaseQuota, checkLeverage, checkLiquidation, checkLiquidationEligibility, checkLiquidationFunding, checkLiquidatorEligible, checkMarket, checkMarketExpired, checkMidasAccountGreenlist, checkObtained, checkOperation, checkPoolFunding, checkPoolLiquidity, checkPoolOperation, checkPoolPaused, checkPoolSunset, checkQuotaCount, checkQuotaLimit, checkRWAOpenRequirements, checkRWAOpening, checkReservePriceLimited, checkSimulation, checkWallet, checkWalletAllowance, checkWalletBalance, raise, toToken, toTokenAmount };
@@ -16,6 +16,8 @@ var ExecuteApi = class {
16
16
  switch (request.kind) {
17
17
  case "pool": return poolTx(sdk, request);
18
18
  case "open": return openTx(sdk, request);
19
+ case "openEmpty": return openEmptyTx(sdk, request);
20
+ case "borrow": return borrowTx(sdk, request);
19
21
  case "account": return accountTx(sdk, request);
20
22
  }
21
23
  }
@@ -62,17 +64,75 @@ async function openTx(sdk, request) {
62
64
  reopenCreditAccount: state.creditAccount,
63
65
  permits: {},
64
66
  referralCode: 0n,
65
- rwaOptions: await openRwaOptions(sdk, request)
67
+ rwaOptions: await openRwaOptions(sdk, request, request.targetToken)
68
+ });
69
+ }
70
+ /**
71
+ * The opening with nothing in it: no debt to draw, no collateral to take, no
72
+ * path to run and no quota to buy, so `openCA` is left with the price updates
73
+ * the market demands and the facade's own `openCreditAccount`.
74
+ *
75
+ * Nothing is read off the preparation because there is nothing on it. The
76
+ * account holds no token, so an RWA market has none to gate on either.
77
+ **/
78
+ function openEmptyTx(sdk, request) {
79
+ return sdk.accounts.openCA({
80
+ creditManager: request.creditManager,
81
+ to: request.wallet,
82
+ collateral: [],
83
+ ethAmount: 0n,
84
+ debt: 0n,
85
+ calls: [],
86
+ averageQuota: [],
87
+ minQuota: [],
88
+ permits: {},
89
+ referralCode: 0n
90
+ });
91
+ }
92
+ /**
93
+ * A loan is an opening whose debt leaves again, so it is the same `openCA`
94
+ * with `withdrawToken` set: the facade draws the debt, takes the collateral,
95
+ * runs whatever path buys the payout, and sweeps that payout to the wallet.
96
+ *
97
+ * Both quota branches are the one the state carries. An opening has two
98
+ * because the balances it lands on are a router quote; here the account is
99
+ * left holding the collateral the caller named, and a named amount has no
100
+ * floor to differ from.
101
+ **/
102
+ async function borrowTx(sdk, request) {
103
+ const { creditManager, wallet, ethAmount, sim } = request;
104
+ const { state } = sim.data;
105
+ const collateral = {
106
+ token: state.collateral.token.address,
107
+ balance: state.collateral.value
108
+ };
109
+ return sdk.accounts.openCA({
110
+ creditManager,
111
+ to: wallet,
112
+ collateral: [collateral],
113
+ ethAmount,
114
+ debt: state.totalDebt.value,
115
+ calls: state.calls,
116
+ withdrawToken: state.borrowed.token.address,
117
+ averageQuota: state.quotaIncrease,
118
+ minQuota: state.quotaIncrease,
119
+ reopenCreditAccount: state.creditAccount,
120
+ permits: {},
121
+ referralCode: 0n,
122
+ rwaOptions: await openRwaOptions(sdk, request, collateral.token)
66
123
  });
67
124
  }
68
125
  /**
69
126
  * The documented `openCA` contract: ask the market for its open requirements
70
127
  * and hand them back as operation args, with the caller's cached signatures
71
- * attached. `undefined` on non-RWA markets and when no target token is named.
128
+ * attached. `undefined` on non-RWA markets and when no token is named.
129
+ *
130
+ * `token` is the one the account ends up holding, which is what an RWA market
131
+ * gates on: the opening's target, and the borrow's collateral.
72
132
  **/
73
- async function openRwaOptions(sdk, request) {
74
- if (!request.targetToken) return;
75
- const requirements = await sdk.accounts.getOpenAccountRequirements(request.wallet, request.creditManager, { tokenOutAddress: request.targetToken });
133
+ async function openRwaOptions(sdk, request, token) {
134
+ if (!token) return;
135
+ const requirements = await sdk.accounts.getOpenAccountRequirements(request.wallet, request.creditManager, { tokenOutAddress: token });
76
136
  if (requirements?.protocol !== "securitize") return;
77
137
  return {
78
138
  protocol: "securitize",