@gearbox-protocol/sdk 14.12.0-next.29 → 14.12.0-next.30

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 (39) hide show
  1. package/dist/cjs/preview/preview/CreditAccountState.js +154 -0
  2. package/dist/cjs/preview/preview/buildDelayedPreview.js +111 -0
  3. package/dist/cjs/preview/preview/detectDelayedOperation.js +71 -0
  4. package/dist/cjs/preview/preview/errors.js +17 -0
  5. package/dist/cjs/preview/preview/index.js +10 -4
  6. package/dist/cjs/preview/preview/previewAdjustCreditAccount.js +16 -39
  7. package/dist/cjs/preview/preview/previewCloseOrRepayCreditAccount.js +11 -37
  8. package/dist/cjs/preview/preview/previewOpenCreditAccount.js +13 -8
  9. package/dist/cjs/preview/preview/previewOperation.js +46 -5
  10. package/dist/cjs/preview/preview/{applyInnerOperations.js → replayInnerOperations.js} +24 -35
  11. package/dist/cjs/preview/preview/{applyQuotaChanges.js → replayMulticall.js} +14 -20
  12. package/dist/cjs/preview/preview/types.js +5 -2
  13. package/dist/esm/preview/preview/CreditAccountState.js +133 -0
  14. package/dist/esm/preview/preview/buildDelayedPreview.js +90 -0
  15. package/dist/esm/preview/preview/detectDelayedOperation.js +51 -0
  16. package/dist/esm/preview/preview/errors.js +16 -0
  17. package/dist/esm/preview/preview/index.js +5 -2
  18. package/dist/esm/preview/preview/previewAdjustCreditAccount.js +19 -48
  19. package/dist/esm/preview/preview/previewCloseOrRepayCreditAccount.js +14 -44
  20. package/dist/esm/preview/preview/previewOpenCreditAccount.js +15 -10
  21. package/dist/esm/preview/preview/previewOperation.js +52 -6
  22. package/dist/esm/preview/preview/{applyInnerOperations.js → replayInnerOperations.js} +20 -34
  23. package/dist/esm/preview/preview/replayMulticall.js +16 -0
  24. package/dist/esm/preview/preview/types.js +3 -1
  25. package/dist/types/preview/preview/CreditAccountState.d.ts +83 -0
  26. package/dist/types/preview/preview/buildDelayedPreview.d.ts +41 -0
  27. package/dist/types/preview/preview/detectDelayedOperation.d.ts +29 -0
  28. package/dist/types/preview/preview/errors.d.ts +15 -0
  29. package/dist/types/preview/preview/index.d.ts +5 -2
  30. package/dist/types/preview/preview/previewAdjustCreditAccount.d.ts +3 -3
  31. package/dist/types/preview/preview/previewCloseOrRepayCreditAccount.d.ts +2 -2
  32. package/dist/types/preview/preview/replayInnerOperations.d.ts +44 -0
  33. package/dist/types/preview/preview/replayMulticall.d.ts +38 -0
  34. package/dist/types/preview/preview/types.d.ts +58 -3
  35. package/dist/types/preview/types.d.ts +17 -6
  36. package/package.json +1 -1
  37. package/dist/esm/preview/preview/applyQuotaChanges.js +0 -19
  38. package/dist/types/preview/preview/applyInnerOperations.d.ts +0 -59
  39. package/dist/types/preview/preview/applyQuotaChanges.d.ts +0 -24
@@ -22,12 +22,15 @@ __export(previewOperation_exports, {
22
22
  });
23
23
  module.exports = __toCommonJS(previewOperation_exports);
24
24
  var import_parse = require("../parse/index.js");
25
+ var import_buildDelayedPreview = require("./buildDelayedPreview.js");
25
26
  var import_detectCloseOrRepay = require("./detectCloseOrRepay.js");
27
+ var import_detectDelayedOperation = require("./detectDelayedOperation.js");
26
28
  var import_errors = require("./errors.js");
27
29
  var import_previewAdjustCreditAccount = require("./previewAdjustCreditAccount.js");
28
30
  var import_previewCloseOrRepayCreditAccount = require("./previewCloseOrRepayCreditAccount.js");
29
31
  var import_previewOpenCreditAccount = require("./previewOpenCreditAccount.js");
30
32
  var import_previewPoolOperation = require("./previewPoolOperation.js");
33
+ var import_replayMulticall = require("./replayMulticall.js");
31
34
  async function previewOperation(input, options) {
32
35
  const operation = (0, import_parse.parseOperationCalldata)(input);
33
36
  if ((0, import_parse.isPoolOperation)(operation)) {
@@ -37,16 +40,54 @@ async function previewOperation(input, options) {
37
40
  return (0, import_previewOpenCreditAccount.previewOpenCreditAccount)(input, operation);
38
41
  }
39
42
  if (operation.operation === "CloseCreditAccount") {
40
- return (0, import_previewCloseOrRepayCreditAccount.previewCloseOrRepayCreditAccount)(input, operation, true, options);
43
+ const resolved = await resolveCreditAccount(input, operation, options);
44
+ return (0, import_previewCloseOrRepayCreditAccount.previewCloseOrRepayCreditAccount)(input, operation, true, resolved);
41
45
  }
42
46
  if (operation.operation === "MultiCall" || operation.operation === "BotMulticall" || operation.operation === "RWAMulticall") {
43
- if ((0, import_detectCloseOrRepay.isCloseOrRepay)(operation.multicall)) {
44
- return (0, import_previewCloseOrRepayCreditAccount.previewCloseOrRepayCreditAccount)(input, operation, false, options);
45
- }
46
- return (0, import_previewAdjustCreditAccount.previewAdjustCreditAccount)(input, operation, options);
47
+ const resolved = await resolveCreditAccount(input, operation, options);
48
+ return previewMulticallOperation(input, operation, resolved);
47
49
  }
48
50
  throw new import_errors.UnsupportedOperationError(operation.operation);
49
51
  }
52
+ async function resolveCreditAccount(input, operation, options) {
53
+ let creditAccount = options?.creditAccount;
54
+ if (!creditAccount) {
55
+ creditAccount = await input.sdk.accounts.getCreditAccountData(
56
+ operation.creditAccount,
57
+ options?.blockNumber
58
+ );
59
+ }
60
+ if (!creditAccount) {
61
+ throw new Error(`credit account ${operation.creditAccount} not found`);
62
+ }
63
+ return { ...options, creditAccount };
64
+ }
65
+ async function previewMulticallOperation(input, operation, options) {
66
+ const { sdk } = input;
67
+ const instantPreview = (0, import_detectCloseOrRepay.isCloseOrRepay)(operation.multicall) ? await (0, import_previewCloseOrRepayCreditAccount.previewCloseOrRepayCreditAccount)(input, operation, false, options) : await (0, import_previewAdjustCreditAccount.previewAdjustCreditAccount)(input, operation, options);
68
+ const delayed = (0, import_detectDelayedOperation.detectDelayedOperation)(sdk, operation.multicall);
69
+ if (!delayed) {
70
+ return instantPreview;
71
+ }
72
+ const { before, after } = await (0, import_replayMulticall.replayMulticall)(sdk, operation, options);
73
+ const market = sdk.marketRegister.findByCreditManager(
74
+ operation.creditManager
75
+ );
76
+ const convert = (token, to, amount) => market.priceOracle.convert(token, to, amount);
77
+ return {
78
+ operation: "DelayedCreditAccountOperation",
79
+ creditAccount: operation.creditAccount,
80
+ creditManager: operation.creditManager,
81
+ intent: delayed.intent,
82
+ instantPreview,
83
+ delayedPreview: (0, import_buildDelayedPreview.buildDelayedPreview)(
84
+ after.account,
85
+ before,
86
+ delayed,
87
+ convert
88
+ )
89
+ };
90
+ }
50
91
  // Annotate the CommonJS export names for ESM import in node:
51
92
  0 && (module.exports = {
52
93
  previewOperation
@@ -16,27 +16,24 @@ var __copyProps = (to, from, except, desc) => {
16
16
  return to;
17
17
  };
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
- var applyInnerOperations_exports = {};
20
- __export(applyInnerOperations_exports, {
21
- applyInnerOperations: () => applyInnerOperations,
22
- makeInnerOperationsState: () => makeInnerOperationsState
19
+ var replayInnerOperations_exports = {};
20
+ __export(replayInnerOperations_exports, {
21
+ makeReplayState: () => makeReplayState,
22
+ replayInnerOperations: () => replayInnerOperations
23
23
  });
24
- module.exports = __toCommonJS(applyInnerOperations_exports);
24
+ module.exports = __toCommonJS(replayInnerOperations_exports);
25
25
  var import_adapters = require("../../plugins/adapters/index.js");
26
26
  var import_sdk = require("../../sdk/index.js");
27
27
  var import_applyRWAWrapUnwrap = require("./applyRWAWrapUnwrap.js");
28
28
  var import_types = require("./types.js");
29
- function makeInnerOperationsState() {
29
+ function makeReplayState(account) {
30
30
  return {
31
- balances: new import_sdk.AssetsMap(),
32
- debt: 0n,
33
- totalDebt: 0n,
31
+ account,
34
32
  collateralAdded: new import_sdk.AssetsMap(),
35
- collateralWithdrawn: new import_sdk.AssetsMap(),
36
- quotaChanges: []
33
+ collateralWithdrawn: new import_sdk.AssetsMap()
37
34
  };
38
35
  }
39
- async function applyInnerOperations(sdk, multicall, state) {
36
+ async function replayInnerOperations(sdk, multicall, state) {
40
37
  let inBracket = false;
41
38
  let error;
42
39
  for (const op of multicall) {
@@ -49,13 +46,13 @@ async function applyInnerOperations(sdk, multicall, state) {
49
46
  applyWithdrawCollateral(state, op);
50
47
  break;
51
48
  case "IncreaseBorrowedAmount":
52
- applyIncreaseDebt(state, op);
49
+ state.account.increaseDebt(op.amount);
53
50
  break;
54
51
  case "DecreaseBorrowedAmount":
55
- applyDecreaseDebt(state, op);
52
+ state.account.repay(op.amount);
56
53
  break;
57
54
  case "UpdateQuota":
58
- state.quotaChanges.push({ token: op.token, balance: op.change });
55
+ state.account.updateQuota(op.token, op.change);
59
56
  break;
60
57
  case "StoreExpectedBalances":
61
58
  if (inBracket) {
@@ -66,7 +63,7 @@ async function applyInnerOperations(sdk, multicall, state) {
66
63
  }
67
64
  inBracket = true;
68
65
  for (const { token, balance } of op.deltas) {
69
- state.balances.inc(token, balance);
66
+ state.account.balances.inc(token, balance);
70
67
  }
71
68
  break;
72
69
  case "CompareBalances":
@@ -79,7 +76,12 @@ async function applyInnerOperations(sdk, multicall, state) {
79
76
  inBracket = false;
80
77
  break;
81
78
  case "Execute":
82
- opError = await applyExecute(sdk, op, inBracket, state.balances);
79
+ opError = await applyExecute(
80
+ sdk,
81
+ op,
82
+ inBracket,
83
+ state.account.balances
84
+ );
83
85
  break;
84
86
  }
85
87
  error ??= opError;
@@ -94,27 +96,14 @@ async function applyInnerOperations(sdk, multicall, state) {
94
96
  }
95
97
  function applyAddCollateral(state, op) {
96
98
  state.collateralAdded.inc(op.token, op.amount);
97
- state.balances.inc(op.token, op.amount);
99
+ state.account.balances.inc(op.token, op.amount);
98
100
  }
99
101
  function applyWithdrawCollateral(state, op) {
100
- const running = state.balances.getOrZero(op.token);
102
+ const running = state.account.balances.getOrZero(op.token);
101
103
  const amount = op.amount === import_sdk.MAX_UINT256 ? running > 0n ? running : 0n : op.amount;
102
- state.balances.dec(op.token, amount);
104
+ state.account.balances.dec(op.token, amount);
103
105
  state.collateralWithdrawn.inc(op.token, amount);
104
106
  }
105
- function applyIncreaseDebt(state, op) {
106
- state.debt += op.amount;
107
- state.totalDebt += op.amount;
108
- state.balances.inc(op.token, op.amount);
109
- }
110
- function applyDecreaseDebt(state, op) {
111
- const repaid = op.amount > state.totalDebt ? state.totalDebt : op.amount;
112
- state.balances.dec(op.token, repaid);
113
- state.totalDebt -= repaid;
114
- if (state.debt > state.totalDebt) {
115
- state.debt = state.totalDebt;
116
- }
117
- }
118
107
  async function applyExecute(sdk, op, inBracket, balances) {
119
108
  const adapter = sdk.getContract(op.adapter);
120
109
  if (!inBracket) {
@@ -159,6 +148,6 @@ function isRWAShare(sdk, adapter) {
159
148
  }
160
149
  // Annotate the CommonJS export names for ESM import in node:
161
150
  0 && (module.exports = {
162
- applyInnerOperations,
163
- makeInnerOperationsState
151
+ makeReplayState,
152
+ replayInnerOperations
164
153
  });
@@ -16,28 +16,22 @@ var __copyProps = (to, from, except, desc) => {
16
16
  return to;
17
17
  };
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
- var applyQuotaChanges_exports = {};
20
- __export(applyQuotaChanges_exports, {
21
- applyQuotaChanges: () => applyQuotaChanges
19
+ var replayMulticall_exports = {};
20
+ __export(replayMulticall_exports, {
21
+ replayMulticall: () => replayMulticall
22
22
  });
23
- module.exports = __toCommonJS(applyQuotaChanges_exports);
24
- var import_sdk = require("../../sdk/index.js");
25
- function applyQuotaChanges(initialQuotas, changes) {
26
- const final = initialQuotas.clone();
27
- for (const { token, balance: change } of changes) {
28
- if (change === import_sdk.MIN_INT96) {
29
- final.upsert(token, 0n);
30
- } else {
31
- const next = final.getOrZero(token) + change;
32
- final.upsert(token, next > 0n ? next : 0n);
33
- }
34
- }
35
- return {
36
- quotas: final.toAssets(0n),
37
- quotasChange: final.difference(initialQuotas).toAssets()
38
- };
23
+ module.exports = __toCommonJS(replayMulticall_exports);
24
+ var import_CreditAccountState = require("./CreditAccountState.js");
25
+ var import_replayInnerOperations = require("./replayInnerOperations.js");
26
+ async function replayMulticall(sdk, operation, options) {
27
+ const before = import_CreditAccountState.CreditAccountState.fromCreditAccountData(
28
+ options.creditAccount
29
+ );
30
+ const after = (0, import_replayInnerOperations.makeReplayState)(before.clone());
31
+ const error = await (0, import_replayInnerOperations.replayInnerOperations)(sdk, operation.multicall, after);
32
+ return { before, after, error };
39
33
  }
40
34
  // Annotate the CommonJS export names for ESM import in node:
41
35
  0 && (module.exports = {
42
- applyQuotaChanges
36
+ replayMulticall
43
37
  });
@@ -24,7 +24,8 @@ __export(types_exports, {
24
24
  ERROR_NON_ADAPTER_CALL_IN_BRACKET: () => ERROR_NON_ADAPTER_CALL_IN_BRACKET,
25
25
  ERROR_UNPREVIEWABLE_ADAPTER_CALL: () => ERROR_UNPREVIEWABLE_ADAPTER_CALL,
26
26
  ERROR_UNPREVIEWABLE_RWA_WRAP_UNWRAP: () => ERROR_UNPREVIEWABLE_RWA_WRAP_UNWRAP,
27
- ERROR_UNPRICEABLE_TOKEN: () => ERROR_UNPRICEABLE_TOKEN
27
+ ERROR_UNPRICEABLE_TOKEN: () => ERROR_UNPRICEABLE_TOKEN,
28
+ PREVIEW_DUST: () => PREVIEW_DUST
28
29
  });
29
30
  module.exports = __toCommonJS(types_exports);
30
31
  const ERROR_MALFORMED_BRACKET = 1001;
@@ -34,6 +35,7 @@ const ERROR_UNPREVIEWABLE_ADAPTER_CALL = 1004;
34
35
  const ERROR_UNPREVIEWABLE_RWA_WRAP_UNWRAP = 1005;
35
36
  const ERROR_INVALID_TRANSACTION_VALUE = 1006;
36
37
  const ERROR_UNPRICEABLE_TOKEN = 2001;
38
+ const PREVIEW_DUST = 10n;
37
39
  // Annotate the CommonJS export names for ESM import in node:
38
40
  0 && (module.exports = {
39
41
  ERROR_ADAPTER_CALL_OUTSIDE_BRACKET,
@@ -42,5 +44,6 @@ const ERROR_UNPRICEABLE_TOKEN = 2001;
42
44
  ERROR_NON_ADAPTER_CALL_IN_BRACKET,
43
45
  ERROR_UNPREVIEWABLE_ADAPTER_CALL,
44
46
  ERROR_UNPREVIEWABLE_RWA_WRAP_UNWRAP,
45
- ERROR_UNPRICEABLE_TOKEN
47
+ ERROR_UNPRICEABLE_TOKEN,
48
+ PREVIEW_DUST
46
49
  });
@@ -0,0 +1,133 @@
1
+ import { zeroAddress } from "viem";
2
+ import {
3
+ AssetsMap,
4
+ MIN_INT96
5
+ } from "../../sdk/index.js";
6
+ class CreditAccountState {
7
+ creditAccount;
8
+ creditManager;
9
+ /**
10
+ * Credit manager underlying
11
+ */
12
+ underlying;
13
+ /**
14
+ * Per-token balances
15
+ */
16
+ balances;
17
+ /**
18
+ * Per-token quotas
19
+ */
20
+ quotas;
21
+ /**
22
+ * Debt principal
23
+ */
24
+ debt;
25
+ /**
26
+ * Total debt: principal + accrued interest + accrued fees.
27
+ * `decreaseDebt` amounts operate on total debt (interest and fees are
28
+ * repaid before principal), so principal alone is not enough to resolve
29
+ * either the underlying balance decrement or the final principal.
30
+ */
31
+ totalDebt;
32
+ constructor(props) {
33
+ this.creditAccount = props.creditAccount;
34
+ this.creditManager = props.creditManager;
35
+ this.underlying = props.underlying;
36
+ this.balances = props.balances ?? new AssetsMap();
37
+ this.quotas = props.quotas ?? new AssetsMap();
38
+ this.debt = props.debt ?? 0n;
39
+ this.totalDebt = props.totalDebt ?? 0n;
40
+ }
41
+ /**
42
+ * The account-opening seed: everything zero/empty. The account doesn't
43
+ * exist yet at preview time, so `creditAccount` is the zero address;
44
+ * opening previews take identity from the parsed operation instead.
45
+ */
46
+ static beforeOpen(creditManager, underlying) {
47
+ return new CreditAccountState({
48
+ creditAccount: zeroAddress,
49
+ creditManager,
50
+ underlying
51
+ });
52
+ }
53
+ /**
54
+ * Pre-state of an existing account from its on-chain data, with dust
55
+ * balances and quotas (≤ 1 wei) filtered out.
56
+ */
57
+ static fromCreditAccountData(ca) {
58
+ const balances = new AssetsMap();
59
+ const quotas = new AssetsMap();
60
+ for (const t of ca.tokens) {
61
+ if (t.balance > 1n) {
62
+ balances.upsert(t.token, t.balance);
63
+ }
64
+ if (t.quota > 1n) {
65
+ quotas.upsert(t.token, t.quota);
66
+ }
67
+ }
68
+ return new CreditAccountState({
69
+ creditAccount: ca.creditAccount,
70
+ creditManager: ca.creditManager,
71
+ underlying: ca.underlying,
72
+ balances,
73
+ quotas,
74
+ debt: ca.debt,
75
+ totalDebt: ca.debt + ca.accruedInterest + ca.accruedFees
76
+ });
77
+ }
78
+ clone() {
79
+ return new CreditAccountState({
80
+ creditAccount: this.creditAccount,
81
+ creditManager: this.creditManager,
82
+ underlying: this.underlying,
83
+ balances: this.balances.clone(),
84
+ quotas: this.quotas.clone(),
85
+ debt: this.debt,
86
+ totalDebt: this.totalDebt
87
+ });
88
+ }
89
+ /**
90
+ * Borrows `amount` of underlying: debt, total debt and the underlying
91
+ * balance all increase by it.
92
+ */
93
+ increaseDebt(amount) {
94
+ this.debt += amount;
95
+ this.totalDebt += amount;
96
+ this.balances.inc(this.underlying, amount);
97
+ }
98
+ /**
99
+ * Repays up to `amount` of underlying against the debt, clamped to the
100
+ * total debt (the facade never repays more than is owed). Interest and
101
+ * fees are repaid before principal (CreditLogic.calcDecrease), so
102
+ * principal only decreases once total debt drops below it. The underlying
103
+ * balance decreases by the repaid amount.
104
+ *
105
+ * @returns The actually repaid (post-clamp) amount.
106
+ */
107
+ repay(amount) {
108
+ const repaid = amount > this.totalDebt ? this.totalDebt : amount;
109
+ this.balances.dec(this.underlying, repaid);
110
+ this.totalDebt -= repaid;
111
+ if (this.debt > this.totalDebt) {
112
+ this.debt = this.totalDebt;
113
+ }
114
+ return repaid;
115
+ }
116
+ /**
117
+ * Applies a relative `updateQuota` change to the token's quota:
118
+ * `MIN_INT96` is the facade "disable quota" sentinel and zeroes it, other
119
+ * changes are added with the result clamped at zero (quotas cannot go
120
+ * negative on-chain).
121
+ */
122
+ updateQuota(token, change) {
123
+ if (change === MIN_INT96) {
124
+ this.quotas.upsert(token, 0n);
125
+ return;
126
+ }
127
+ const next = this.quotas.getOrZero(token) + change;
128
+ this.quotas.upsert(token, next > 0n ? next : 0n);
129
+ }
130
+ }
131
+ export {
132
+ CreditAccountState
133
+ };
@@ -0,0 +1,90 @@
1
+ import { isAddressEqual } from "viem";
2
+ import { AssetsMap } from "../../sdk/index.js";
3
+ import {
4
+ ERROR_UNPRICEABLE_TOKEN,
5
+ PREVIEW_DUST
6
+ } from "./types.js";
7
+ function buildDelayedPreview(state, before, detected, convert) {
8
+ const { request, intent } = detected;
9
+ const post = state.clone();
10
+ let error;
11
+ const safeConvert = (token, to, amount) => {
12
+ try {
13
+ return convert(token, to, amount);
14
+ } catch {
15
+ error ??= {
16
+ code: ERROR_UNPRICEABLE_TOKEN,
17
+ message: `cannot price token ${token}`
18
+ };
19
+ return 0n;
20
+ }
21
+ };
22
+ const claimed = post.balances.getOrZero(request.phantomToken);
23
+ post.balances.upsert(request.phantomToken, 0n);
24
+ post.quotas.upsert(request.phantomToken, 0n);
25
+ post.balances.inc(request.claimToken, claimed);
26
+ if (intent?.type === "CLOSE_ACCOUNT") {
27
+ const totalValue2 = post.balances.sum(
28
+ (token, balance) => balance > 0n ? safeConvert(token, post.underlying, balance) : 0n
29
+ );
30
+ const receivedAmount = totalValue2 > post.totalDebt ? totalValue2 - post.totalDebt : 0n;
31
+ const preview2 = {
32
+ operation: "CloseCreditAccount",
33
+ permanent: true,
34
+ creditManager: post.creditManager,
35
+ creditAccount: post.creditAccount,
36
+ receivedAmount,
37
+ error
38
+ };
39
+ return preview2;
40
+ }
41
+ const collateralWithdrawn = new AssetsMap();
42
+ const repayFromClaim = (amount) => {
43
+ const available = post.balances.getOrZero(request.claimToken);
44
+ const spent = amount > available ? available : amount;
45
+ if (spent <= 0n) {
46
+ return;
47
+ }
48
+ const received = safeConvert(request.claimToken, post.underlying, spent);
49
+ post.balances.dec(request.claimToken, spent);
50
+ post.balances.inc(post.underlying, received);
51
+ post.repay(received);
52
+ };
53
+ if (intent?.type === "DECREASE_LEVERAGE") {
54
+ repayFromClaim(claimed);
55
+ } else if (intent?.type === "WITHDRAW_COLLATERAL") {
56
+ const running = post.balances.getOrZero(intent.withdrawToken);
57
+ const withdrawn = intent.withdrawAmount > running ? running : intent.withdrawAmount;
58
+ if (withdrawn > 0n) {
59
+ post.balances.dec(intent.withdrawToken, withdrawn);
60
+ collateralWithdrawn.upsert(intent.withdrawToken, withdrawn);
61
+ }
62
+ const remainingClaim = isAddressEqual(intent.withdrawToken, request.claimToken) && claimed > withdrawn ? claimed - withdrawn : claimed;
63
+ repayFromClaim(remainingClaim);
64
+ }
65
+ const totalValue = post.balances.sum(
66
+ (token, balance) => balance > PREVIEW_DUST ? safeConvert(token, post.underlying, balance) : 0n
67
+ );
68
+ const preview = {
69
+ operation: "AdjustCreditAccount",
70
+ creditManager: post.creditManager,
71
+ creditAccount: post.creditAccount,
72
+ // the resume flow adds nothing from the wallet
73
+ collateralAdded: [],
74
+ collateralWithdrawn: collateralWithdrawn.toAssets(),
75
+ totalValue,
76
+ debt: post.debt,
77
+ // relative to the pre-transaction state: where the account will end up
78
+ // compared to now, once the withdrawal is claimed and the intent resumed
79
+ debtChange: post.debt - before.debt,
80
+ quotas: post.quotas.toAssets(0n),
81
+ quotasChange: post.quotas.difference(before.quotas).toAssets(),
82
+ assets: post.balances.toAssets(PREVIEW_DUST),
83
+ assetsChange: post.balances.difference(before.balances).toAssets(PREVIEW_DUST),
84
+ error
85
+ };
86
+ return preview;
87
+ }
88
+ export {
89
+ buildDelayedPreview
90
+ };
@@ -0,0 +1,51 @@
1
+ import { isAddressEqual } from "viem";
2
+ import {
3
+ AbstractAdapterContract
4
+ } from "../../plugins/adapters/index.js";
5
+ import {
6
+ decodeDelayedIntent
7
+ } from "../../sdk/index.js";
8
+ import { InvalidDelayedIntentError } from "./errors.js";
9
+ function detectDelayedOperation(sdk, multicall) {
10
+ let bracketDeltas = [];
11
+ for (const op of multicall) {
12
+ if (op.operation === "StoreExpectedBalances") {
13
+ bracketDeltas = op.deltas;
14
+ continue;
15
+ }
16
+ if (op.operation === "CompareBalances") {
17
+ bracketDeltas = [];
18
+ continue;
19
+ }
20
+ if (op.operation !== "Execute") {
21
+ continue;
22
+ }
23
+ const adapter = sdk.getContract(op.adapter);
24
+ if (!(adapter instanceof AbstractAdapterContract)) {
25
+ continue;
26
+ }
27
+ const request = adapter.parseDelayedWithdrawalRequest(op.calldata);
28
+ if (!request) {
29
+ continue;
30
+ }
31
+ const mintsPhantom = bracketDeltas.some(
32
+ ({ token, balance }) => balance > 0n && isAddressEqual(token, request.phantomToken)
33
+ );
34
+ if (!mintsPhantom) {
35
+ continue;
36
+ }
37
+ let intent;
38
+ if (request.extraData && request.extraData !== "0x") {
39
+ try {
40
+ intent = decodeDelayedIntent(request.extraData);
41
+ } catch (e) {
42
+ throw new InvalidDelayedIntentError(op.adapter, request.extraData, e);
43
+ }
44
+ }
45
+ return { request, intent };
46
+ }
47
+ return void 0;
48
+ }
49
+ export {
50
+ detectDelayedOperation
51
+ };
@@ -7,6 +7,22 @@ class UnsupportedOperationError extends Error {
7
7
  this.operation = operation;
8
8
  }
9
9
  }
10
+ class InvalidDelayedIntentError extends Error {
11
+ /** Adapter the withdrawal request was addressed to. */
12
+ adapter;
13
+ /** Raw `extraData` that failed to decode. */
14
+ extraData;
15
+ constructor(adapter, extraData, cause) {
16
+ super(
17
+ `cannot decode delayed intent from extraData ${extraData} of withdrawal request to adapter ${adapter}`,
18
+ { cause }
19
+ );
20
+ this.name = "InvalidDelayedIntentError";
21
+ this.adapter = adapter;
22
+ this.extraData = extraData;
23
+ }
24
+ }
10
25
  export {
26
+ InvalidDelayedIntentError,
11
27
  UnsupportedOperationError
12
28
  };
@@ -1,8 +1,11 @@
1
- export * from "./applyInnerOperations.js";
2
- export * from "./applyQuotaChanges.js";
1
+ export * from "./buildDelayedPreview.js";
2
+ export * from "./CreditAccountState.js";
3
3
  export * from "./detectCloseOrRepay.js";
4
+ export * from "./detectDelayedOperation.js";
4
5
  export * from "./errors.js";
5
6
  export * from "./previewAdjustCreditAccount.js";
6
7
  export * from "./previewCloseOrRepayCreditAccount.js";
7
8
  export * from "./previewOperation.js";
9
+ export * from "./replayInnerOperations.js";
10
+ export * from "./replayMulticall.js";
8
11
  export * from "./types.js";
@@ -1,15 +1,8 @@
1
+ import { AP_WETH_TOKEN, NO_VERSION } from "../../sdk/index.js";
2
+ import { replayMulticall } from "./replayMulticall.js";
1
3
  import {
2
- AP_WETH_TOKEN,
3
- AssetsMap,
4
- NO_VERSION
5
- } from "../../sdk/index.js";
6
- import {
7
- applyInnerOperations,
8
- makeInnerOperationsState
9
- } from "./applyInnerOperations.js";
10
- import { applyQuotaChanges } from "./applyQuotaChanges.js";
11
- import {
12
- ERROR_UNPRICEABLE_TOKEN
4
+ ERROR_UNPRICEABLE_TOKEN,
5
+ PREVIEW_DUST
13
6
  } from "./types.js";
14
7
  import { unwrapNativeCollateral } from "./unwrapNativeCollateral.js";
15
8
  async function previewAdjustCreditAccount(input, operation, options) {
@@ -17,43 +10,21 @@ async function previewAdjustCreditAccount(input, operation, options) {
17
10
  const market = sdk.marketRegister.findByCreditManager(
18
11
  operation.creditManager
19
12
  );
20
- let ca = options?.creditAccount;
21
- if (!ca) {
22
- ca = await sdk.accounts.getCreditAccountData(
23
- operation.creditAccount,
24
- options?.blockNumber
25
- );
26
- }
27
- if (!ca) {
28
- throw new Error(`credit account ${operation.creditAccount} not found`);
29
- }
30
- const initialBalances = new AssetsMap();
31
- const initialQuotas = new AssetsMap();
32
- for (const t of ca.tokens) {
33
- if (t.balance > 1n) {
34
- initialBalances.upsert(t.token, t.balance);
35
- }
36
- if (t.quota > 1n) {
37
- initialQuotas.upsert(t.token, t.quota);
38
- }
39
- }
40
- const state = makeInnerOperationsState();
41
- state.balances = initialBalances.clone();
42
- state.debt = ca.debt;
43
- state.totalDebt = ca.debt + ca.accruedInterest + ca.accruedFees;
44
- let error = await applyInnerOperations(sdk, operation.multicall, state);
13
+ const {
14
+ before,
15
+ after,
16
+ error: replayError
17
+ } = await replayMulticall(sdk, operation, options);
18
+ const account = after.account;
19
+ let error = replayError;
45
20
  const { assets: collateralAdded, error: unwrapError } = unwrapNativeCollateral(
46
- state.collateralAdded.toAssets(),
21
+ after.collateralAdded.toAssets(),
47
22
  value,
48
23
  sdk.addressProvider.getAddress(AP_WETH_TOKEN, NO_VERSION)
49
24
  );
50
25
  error ??= unwrapError;
51
- const { quotas, quotasChange } = applyQuotaChanges(
52
- initialQuotas,
53
- state.quotaChanges
54
- );
55
- const assets = state.balances.toAssets(1n);
56
- const assetsChange = state.balances.difference(initialBalances).toAssets(1n);
26
+ const assets = account.balances.toAssets(PREVIEW_DUST);
27
+ const assetsChange = account.balances.difference(before.balances).toAssets(PREVIEW_DUST);
57
28
  const totalValue = assets.reduce((acc, { token, balance }) => {
58
29
  try {
59
30
  return acc + market.priceOracle.convert(token, market.underlying, balance);
@@ -70,12 +41,12 @@ async function previewAdjustCreditAccount(input, operation, options) {
70
41
  creditManager: operation.creditManager,
71
42
  creditAccount: operation.creditAccount,
72
43
  collateralAdded,
73
- collateralWithdrawn: state.collateralWithdrawn.toAssets(),
44
+ collateralWithdrawn: after.collateralWithdrawn.toAssets(),
74
45
  totalValue,
75
- debt: state.debt,
76
- debtChange: state.debt - ca.debt,
77
- quotas,
78
- quotasChange,
46
+ debt: account.debt,
47
+ debtChange: account.debt - before.debt,
48
+ quotas: account.quotas.toAssets(0n),
49
+ quotasChange: account.quotas.difference(before.quotas).toAssets(),
79
50
  assets,
80
51
  assetsChange,
81
52
  error