@gearbox-protocol/sdk 14.12.0-next.10 → 14.12.0-next.12

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.
@@ -45,7 +45,7 @@ function applyInnerOperations(sdk, multicall, state) {
45
45
  state.balances.inc(op.token, op.amount);
46
46
  break;
47
47
  case "WithdrawCollateral": {
48
- const running = state.balances.get(op.token) ?? 0n;
48
+ const running = state.balances.getOrZero(op.token);
49
49
  const amount = op.amount === import_sdk.MAX_UINT256 ? running > 0n ? running : 0n : op.amount;
50
50
  state.balances.dec(op.token, amount);
51
51
  state.collateralWithdrawn.inc(op.token, amount);
@@ -113,16 +113,14 @@ function applyQuotaChanges(initialQuotas, changes) {
113
113
  if (change === import_sdk.MIN_INT96) {
114
114
  final.upsert(token, 0n);
115
115
  } else {
116
- const next = (final.get(token) ?? 0n) + change;
116
+ const next = final.getOrZero(token) + change;
117
117
  final.upsert(token, next > 0n ? next : 0n);
118
118
  }
119
119
  }
120
- const quotas = final.entries().filter(([, balance]) => balance > 0n).map(([token, balance]) => ({ token, balance }));
121
- const quotasChange = final.entries().map(([token, balance]) => ({
122
- token,
123
- balance: balance - (initialQuotas.get(token) ?? 0n)
124
- })).filter(({ balance }) => balance !== 0n);
125
- return { quotas, quotasChange };
120
+ return {
121
+ quotas: final.toAssets(0n),
122
+ quotasChange: final.difference(initialQuotas).toAssets()
123
+ };
126
124
  }
127
125
  // Annotate the CommonJS export names for ESM import in node:
128
126
  0 && (module.exports = {
@@ -50,9 +50,7 @@ async function previewAdjustCreditAccount(input, operation, options) {
50
50
  }
51
51
  }
52
52
  const state = (0, import_applyInnerOperations.makeInnerOperationsState)();
53
- for (const [token, balance] of initialBalances.entries()) {
54
- state.balances.upsert(token, balance);
55
- }
53
+ state.balances = initialBalances.clone();
56
54
  state.debt = ca.debt;
57
55
  state.totalDebt = ca.debt + ca.accruedInterest + ca.accruedFees;
58
56
  (0, import_applyInnerOperations.applyInnerOperations)(sdk, operation.multicall, state);
@@ -65,11 +63,8 @@ async function previewAdjustCreditAccount(input, operation, options) {
65
63
  initialQuotas,
66
64
  state.quotaChanges
67
65
  );
68
- const assets = state.balances.toAssets(true);
69
- const assetsChange = state.balances.entries().map(([token, balance]) => ({
70
- token,
71
- balance: balance - (initialBalances.get(token) ?? 0n)
72
- })).filter(({ balance }) => balance !== 0n);
66
+ const assets = state.balances.toAssets(1n);
67
+ const assetsChange = state.balances.difference(initialBalances).toAssets(1n);
73
68
  const totalValue = assets.reduce(
74
69
  (acc, { token, balance }) => acc + market.priceOracle.convert(token, market.underlying, balance),
75
70
  0n
@@ -31,17 +31,15 @@ function previewOpenCreditAccount(input, operation) {
31
31
  );
32
32
  const state = (0, import_applyInnerOperations.makeInnerOperationsState)();
33
33
  (0, import_applyInnerOperations.applyInnerOperations)(sdk, operation.multicall, state);
34
- let collateral = state.collateralAdded.toAssets();
35
- const collateralValue = collateral.reduce(
36
- (acc, { token, balance }) => acc + market.priceOracle.convert(token, market.underlying, balance),
37
- 0n
34
+ const collateralValue = state.collateralAdded.sum(
35
+ (token, balance) => market.priceOracle.convert(token, market.underlying, balance)
38
36
  );
39
- collateral = (0, import_unwrapNativeCollateral.unwrapNativeCollateral)(
40
- collateral,
37
+ const collateral = (0, import_unwrapNativeCollateral.unwrapNativeCollateral)(
38
+ state.collateralAdded.toAssets(),
41
39
  value,
42
40
  sdk.addressProvider.getAddress(import_sdk.AP_WETH_TOKEN, import_sdk.NO_VERSION)
43
41
  );
44
- const assets = state.balances.entries().filter(([, balance]) => balance > 1n).map(([token, balance]) => ({ token, balance }));
42
+ const assets = state.balances.toAssets(1n);
45
43
  return {
46
44
  operation: operation.operation,
47
45
  creditManager: operation.creditManager,
@@ -620,14 +620,12 @@ class CreditAccountsServiceV310 extends import_base.SDKConstruct {
620
620
  creditManager: cm.creditManager,
621
621
  slippage
622
622
  });
623
- const operationCalls = [
624
- ...routerCloseResult.calls,
625
- ...this.#prepareDisableQuotas(ca),
626
- ...this.#prepareDecreaseDebt(ca),
627
- ...assetsToWithdraw.map(
628
- (t) => this.#prepareWithdrawToken(ca.creditFacade, t, import_constants.MAX_UINT256, to)
629
- )
630
- ];
623
+ const operationCalls = await this.assembleCloseCreditAccountCalls({
624
+ creditAccount: ca,
625
+ routerCalls: routerCloseResult.calls,
626
+ assetsToWithdraw,
627
+ to
628
+ });
631
629
  const calls = operation === "close" ? operationCalls : await this.#prependPriceUpdates(ca.creditManager, operationCalls, ca);
632
630
  const tx = await this.#closeCreditAccountTx(
633
631
  cm,
@@ -637,6 +635,32 @@ class CreditAccountsServiceV310 extends import_base.SDKConstruct {
637
635
  );
638
636
  return { tx, calls, routerCloseResult, creditFacade: cm.creditFacade };
639
637
  }
638
+ /**
639
+ * {@inheritDoc ICreditAccountsService.assembleCloseCreditAccountCalls}
640
+ */
641
+ async assembleCloseCreditAccountCalls({
642
+ creditAccount: ca,
643
+ routerCalls,
644
+ assetsToWithdraw,
645
+ to
646
+ }) {
647
+ const cm = this.sdk.marketRegister.findCreditManager(ca.creditManager);
648
+ await this.sdk.tokensMeta.loadTokenData(cm.underlying);
649
+ const underlying = this.sdk.tokensMeta.mustGet(cm.underlying);
650
+ if (this.sdk.tokensMeta.isRWAUnderlying(underlying)) {
651
+ throw new Error(
652
+ "closeCreditAccount is not supported for RWA underlying credit accounts"
653
+ );
654
+ }
655
+ return [
656
+ ...routerCalls,
657
+ ...this.#prepareDisableQuotas(ca),
658
+ ...this.#prepareDecreaseDebt(ca),
659
+ ...assetsToWithdraw.map(
660
+ (t) => this.#prepareWithdrawToken(ca.creditFacade, t, import_constants.MAX_UINT256, to)
661
+ )
662
+ ];
663
+ }
640
664
  /**
641
665
  * {@inheritDoc ICreditAccountsService.updateQuotas}
642
666
  **/
@@ -1306,8 +1330,23 @@ class CreditAccountsServiceV310 extends import_base.SDKConstruct {
1306
1330
  /**
1307
1331
  * {@inheritDoc ICreditAccountsService.repayCreditAccount}
1308
1332
  */
1309
- async repayCreditAccount({
1310
- operation,
1333
+ async repayCreditAccount(props) {
1334
+ const { operation, creditAccount: ca } = props;
1335
+ const cm = this.sdk.marketRegister.findCreditManager(ca.creditManager);
1336
+ const operationCalls = await this.assembleRepayCreditAccountCalls(props);
1337
+ const calls = operation === "close" ? operationCalls : await this.#prependPriceUpdates(ca.creditManager, operationCalls, ca);
1338
+ const tx = await this.#closeCreditAccountTx(
1339
+ cm,
1340
+ ca.creditAccount,
1341
+ calls,
1342
+ operation
1343
+ );
1344
+ return { tx, calls, creditFacade: cm.creditFacade };
1345
+ }
1346
+ /**
1347
+ * {@inheritDoc ICreditAccountsService.assembleRepayCreditAccountCalls}
1348
+ */
1349
+ async assembleRepayCreditAccountCalls({
1311
1350
  collateralAssets,
1312
1351
  assetsToWithdraw,
1313
1352
  creditAccount: ca,
@@ -1316,7 +1355,6 @@ class CreditAccountsServiceV310 extends import_base.SDKConstruct {
1316
1355
  tokensToClaim,
1317
1356
  calls: wrapCalls = []
1318
1357
  }) {
1319
- const cm = this.sdk.marketRegister.findCreditManager(ca.creditManager);
1320
1358
  const addCollateral = collateralAssets.filter((a) => a.balance > 0);
1321
1359
  const router = this.sdk.routerFor(ca);
1322
1360
  const unwrapCalls = await this.getRedeemDiffCalls(1n, ca.creditManager) ?? [];
@@ -1335,14 +1373,7 @@ class CreditAccountsServiceV310 extends import_base.SDKConstruct {
1335
1373
  (t) => this.#prepareWithdrawToken(ca.creditFacade, t.token, import_constants.MAX_UINT256, to)
1336
1374
  )
1337
1375
  ];
1338
- const calls = operation === "close" ? operationCalls : await this.#prependPriceUpdates(ca.creditManager, operationCalls, ca);
1339
- const tx = await this.#closeCreditAccountTx(
1340
- cm,
1341
- ca.creditAccount,
1342
- calls,
1343
- operation
1344
- );
1345
- return { tx, calls, creditFacade: cm.creditFacade };
1376
+ return operationCalls;
1346
1377
  }
1347
1378
  /**
1348
1379
  * {@inheritDoc ICreditAccountsService.repayAndLiquidateCreditAccount}
@@ -1563,19 +1594,14 @@ class CreditAccountsServiceV310 extends import_base.SDKConstruct {
1563
1594
  );
1564
1595
  }
1565
1596
  /**
1566
- * {@inheritDoc ICreditAccountsService.assembleCaUpdateCalls}
1597
+ * {@inheritDoc ICreditAccountsService.assembleCaOperations}
1567
1598
  */
1568
- assembleCaUpdateCalls({
1599
+ assembleCaOperations({
1569
1600
  operations,
1570
- routerCallGroups,
1571
1601
  creditFacade,
1572
- withdrawTo,
1573
- creditAccount,
1574
- underlyingToken
1602
+ withdrawTo
1575
1603
  }) {
1576
1604
  const calls = [];
1577
- let swapGroupIndex = 0;
1578
- let routerGroupsConsumed = 0;
1579
1605
  for (const op of operations) {
1580
1606
  switch (op.type) {
1581
1607
  case "increaseDebt":
@@ -1603,18 +1629,10 @@ class CreditAccountsServiceV310 extends import_base.SDKConstruct {
1603
1629
  )
1604
1630
  );
1605
1631
  break;
1606
- case "swap": {
1607
- const routerCalls = routerCallGroups[swapGroupIndex];
1608
- if (!routerCalls) {
1609
- throw new Error(
1610
- `assembleCaUpdateCalls: missing router calls for swap leg ${swapGroupIndex}`
1611
- );
1612
- }
1613
- calls.push(...routerCalls);
1614
- swapGroupIndex += 1;
1615
- routerGroupsConsumed += 1;
1632
+ case "swap":
1633
+ case "wrapRwaCollateral":
1634
+ calls.push(...op.calls);
1616
1635
  break;
1617
- }
1618
1636
  case "changeQuota": {
1619
1637
  const quotaAssets = [...op.quotaIncrease, ...op.quotaDecrease];
1620
1638
  if (quotaAssets.length === 0) {
@@ -1628,43 +1646,110 @@ class CreditAccountsServiceV310 extends import_base.SDKConstruct {
1628
1646
  );
1629
1647
  break;
1630
1648
  }
1631
- case "closeCreditAccount": {
1632
- for (const group of routerCallGroups) {
1633
- calls.push(...group);
1634
- routerGroupsConsumed += 1;
1635
- }
1649
+ default: {
1650
+ const _exhaustive = op;
1651
+ throw new Error(
1652
+ `assembleCaOperations: unsupported operation ${JSON.stringify(_exhaustive)}`
1653
+ );
1654
+ }
1655
+ }
1656
+ }
1657
+ return calls;
1658
+ }
1659
+ /**
1660
+ * {@inheritDoc ICreditAccountsService.assembleCaUpdateCalls}
1661
+ * @deprecated Prefer {@link CreditAccountsServiceV310.assembleCaOperations}.
1662
+ */
1663
+ assembleCaUpdateCalls({
1664
+ operations,
1665
+ routerCallGroups,
1666
+ creditFacade,
1667
+ withdrawTo,
1668
+ creditAccount,
1669
+ underlyingToken
1670
+ }) {
1671
+ const calls = [];
1672
+ let swapGroupIndex = 0;
1673
+ let routerGroupsConsumed = 0;
1674
+ const encodableOps = [];
1675
+ let pendingClose = null;
1676
+ const flushEncodable = () => {
1677
+ if (encodableOps.length === 0) {
1678
+ return;
1679
+ }
1680
+ calls.push(
1681
+ ...this.assembleCaOperations({
1682
+ operations: encodableOps.splice(0, encodableOps.length),
1683
+ creditFacade,
1684
+ withdrawTo
1685
+ })
1686
+ );
1687
+ };
1688
+ for (const op of operations) {
1689
+ if (op.type === "closeCreditAccount") {
1690
+ flushEncodable();
1691
+ pendingClose = { routerGroupsStart: routerGroupsConsumed };
1692
+ for (const group of routerCallGroups.slice(routerGroupsConsumed)) {
1693
+ calls.push(...group);
1694
+ routerGroupsConsumed += 1;
1695
+ }
1696
+ calls.push(
1697
+ ...this.#prepareDisableQuotas({
1698
+ creditFacade,
1699
+ tokens: Object.entries(creditAccount.initialQuotas).map(
1700
+ ([token, { quota }]) => ({
1701
+ token,
1702
+ quota
1703
+ })
1704
+ )
1705
+ })
1706
+ );
1707
+ if (creditAccount.debt > 0n) {
1636
1708
  calls.push(
1637
- ...this.#prepareDisableQuotas({
1709
+ ...this.#prepareDecreaseDebt({
1638
1710
  creditFacade,
1639
- tokens: Object.entries(creditAccount.initialQuotas).map(
1640
- ([token, { quota }]) => ({
1641
- token,
1642
- quota
1643
- })
1644
- )
1711
+ debt: creditAccount.debt
1645
1712
  })
1646
1713
  );
1647
- if (creditAccount.debt > 0n) {
1648
- calls.push(
1649
- ...this.#prepareDecreaseDebt({
1650
- creditFacade,
1651
- debt: creditAccount.debt
1652
- })
1653
- );
1654
- }
1655
- const hasAssets = creditAccount.assets.some(
1656
- (asset) => asset.balance > 0n
1714
+ }
1715
+ const hasAssets = creditAccount.assets.some(
1716
+ (asset) => asset.balance > 0n
1717
+ );
1718
+ if (hasAssets) {
1719
+ calls.push(
1720
+ this.#prepareWithdrawToken(
1721
+ creditFacade,
1722
+ underlyingToken,
1723
+ import_constants.MAX_UINT256,
1724
+ withdrawTo
1725
+ )
1657
1726
  );
1658
- if (hasAssets) {
1659
- calls.push(
1660
- this.#prepareWithdrawToken(
1661
- creditFacade,
1662
- underlyingToken,
1663
- import_constants.MAX_UINT256,
1664
- withdrawTo
1665
- )
1727
+ }
1728
+ continue;
1729
+ }
1730
+ if (pendingClose !== null) {
1731
+ throw new Error(
1732
+ "assembleCaUpdateCalls: operations after closeCreditAccount are not supported"
1733
+ );
1734
+ }
1735
+ switch (op.type) {
1736
+ case "increaseDebt":
1737
+ case "decreaseDebt":
1738
+ case "addCollateral":
1739
+ case "withdrawCollateral":
1740
+ case "changeQuota":
1741
+ encodableOps.push(op);
1742
+ break;
1743
+ case "swap": {
1744
+ const routerCalls = routerCallGroups[swapGroupIndex];
1745
+ if (!routerCalls) {
1746
+ throw new Error(
1747
+ `assembleCaUpdateCalls: missing router calls for swap leg ${swapGroupIndex}`
1666
1748
  );
1667
1749
  }
1750
+ encodableOps.push({ type: "swap", calls: routerCalls });
1751
+ swapGroupIndex += 1;
1752
+ routerGroupsConsumed += 1;
1668
1753
  break;
1669
1754
  }
1670
1755
  default: {
@@ -1675,6 +1760,7 @@ class CreditAccountsServiceV310 extends import_base.SDKConstruct {
1675
1760
  }
1676
1761
  }
1677
1762
  }
1763
+ flushEncodable();
1678
1764
  if (routerGroupsConsumed !== routerCallGroups.length) {
1679
1765
  throw new Error(
1680
1766
  `assembleCaUpdateCalls: router call group mismatch (consumed ${routerGroupsConsumed}, got ${routerCallGroups.length})`
@@ -33,11 +33,17 @@ class AssetsMap extends import_AddressMap.AddressMap {
33
33
  name
34
34
  );
35
35
  }
36
+ /**
37
+ * Looks up a token balance, treating a missing key as `0n`.
38
+ */
39
+ getOrZero(address) {
40
+ return this.get(address) ?? 0n;
41
+ }
36
42
  /**
37
43
  * Adds `amount` to the token balance, treating a missing key as `0n`.
38
44
  */
39
45
  inc(address, amount) {
40
- this.upsert(address, (this.get(address) ?? 0n) + amount);
46
+ this.upsert(address, this.getOrZero(address) + amount);
41
47
  }
42
48
  /**
43
49
  * Subtracts `amount` from the token balance, treating a missing key as `0n`.
@@ -45,12 +51,41 @@ class AssetsMap extends import_AddressMap.AddressMap {
45
51
  dec(address, amount) {
46
52
  this.inc(address, -amount);
47
53
  }
54
+ /**
55
+ * Per-token difference `this - before`, non-zero entries only.
56
+ *
57
+ * Assumes this map's keys are a superset of `before`'s keys; tokens present
58
+ * only in `before` are not reported.
59
+ */
60
+ difference(before) {
61
+ const result = new AssetsMap();
62
+ for (const [token, balance] of this.entries()) {
63
+ const change = balance - before.getOrZero(token);
64
+ if (change !== 0n) {
65
+ result.upsert(token, change);
66
+ }
67
+ }
68
+ return result;
69
+ }
70
+ /**
71
+ * Sums `fn` over all entries.
72
+ */
73
+ sum(fn) {
74
+ return this.entries().reduce(
75
+ (acc, [token, balance]) => acc + fn(token, balance),
76
+ 0n
77
+ );
78
+ }
48
79
  /**
49
80
  * Converts the map to an array of {@link Asset} objects.
50
- * @param filterDust - If true, filters out assets with a balance less than 1.
81
+ * @param minBalance - If provided, keeps only entries with a balance
82
+ * strictly greater than this threshold (e.g. `1n` to filter out dust).
51
83
  */
52
- toAssets(filterDust = false) {
53
- return this.entries().filter(filterDust ? ([, balance]) => balance > 1n : () => true).map(([token, balance]) => ({ token, balance }));
84
+ toAssets(minBalance) {
85
+ const abs = (balance) => balance > 0n ? balance : -balance;
86
+ return this.entries().filter(
87
+ ([, balance]) => minBalance === void 0 || abs(balance) > minBalance
88
+ ).map(([token, balance]) => ({ token, balance }));
54
89
  }
55
90
  clone() {
56
91
  return new AssetsMap(this.entries(), this.name);
@@ -24,7 +24,7 @@ function applyInnerOperations(sdk, multicall, state) {
24
24
  state.balances.inc(op.token, op.amount);
25
25
  break;
26
26
  case "WithdrawCollateral": {
27
- const running = state.balances.get(op.token) ?? 0n;
27
+ const running = state.balances.getOrZero(op.token);
28
28
  const amount = op.amount === MAX_UINT256 ? running > 0n ? running : 0n : op.amount;
29
29
  state.balances.dec(op.token, amount);
30
30
  state.collateralWithdrawn.inc(op.token, amount);
@@ -92,16 +92,14 @@ function applyQuotaChanges(initialQuotas, changes) {
92
92
  if (change === MIN_INT96) {
93
93
  final.upsert(token, 0n);
94
94
  } else {
95
- const next = (final.get(token) ?? 0n) + change;
95
+ const next = final.getOrZero(token) + change;
96
96
  final.upsert(token, next > 0n ? next : 0n);
97
97
  }
98
98
  }
99
- const quotas = final.entries().filter(([, balance]) => balance > 0n).map(([token, balance]) => ({ token, balance }));
100
- const quotasChange = final.entries().map(([token, balance]) => ({
101
- token,
102
- balance: balance - (initialQuotas.get(token) ?? 0n)
103
- })).filter(({ balance }) => balance !== 0n);
104
- return { quotas, quotasChange };
99
+ return {
100
+ quotas: final.toAssets(0n),
101
+ quotasChange: final.difference(initialQuotas).toAssets()
102
+ };
105
103
  }
106
104
  export {
107
105
  applyInnerOperations,
@@ -35,9 +35,7 @@ async function previewAdjustCreditAccount(input, operation, options) {
35
35
  }
36
36
  }
37
37
  const state = makeInnerOperationsState();
38
- for (const [token, balance] of initialBalances.entries()) {
39
- state.balances.upsert(token, balance);
40
- }
38
+ state.balances = initialBalances.clone();
41
39
  state.debt = ca.debt;
42
40
  state.totalDebt = ca.debt + ca.accruedInterest + ca.accruedFees;
43
41
  applyInnerOperations(sdk, operation.multicall, state);
@@ -50,11 +48,8 @@ async function previewAdjustCreditAccount(input, operation, options) {
50
48
  initialQuotas,
51
49
  state.quotaChanges
52
50
  );
53
- const assets = state.balances.toAssets(true);
54
- const assetsChange = state.balances.entries().map(([token, balance]) => ({
55
- token,
56
- balance: balance - (initialBalances.get(token) ?? 0n)
57
- })).filter(({ balance }) => balance !== 0n);
51
+ const assets = state.balances.toAssets(1n);
52
+ const assetsChange = state.balances.difference(initialBalances).toAssets(1n);
58
53
  const totalValue = assets.reduce(
59
54
  (acc, { token, balance }) => acc + market.priceOracle.convert(token, market.underlying, balance),
60
55
  0n
@@ -14,17 +14,15 @@ function previewOpenCreditAccount(input, operation) {
14
14
  );
15
15
  const state = makeInnerOperationsState();
16
16
  applyInnerOperations(sdk, operation.multicall, state);
17
- let collateral = state.collateralAdded.toAssets();
18
- const collateralValue = collateral.reduce(
19
- (acc, { token, balance }) => acc + market.priceOracle.convert(token, market.underlying, balance),
20
- 0n
17
+ const collateralValue = state.collateralAdded.sum(
18
+ (token, balance) => market.priceOracle.convert(token, market.underlying, balance)
21
19
  );
22
- collateral = unwrapNativeCollateral(
23
- collateral,
20
+ const collateral = unwrapNativeCollateral(
21
+ state.collateralAdded.toAssets(),
24
22
  value,
25
23
  sdk.addressProvider.getAddress(AP_WETH_TOKEN, NO_VERSION)
26
24
  );
27
- const assets = state.balances.entries().filter(([, balance]) => balance > 1n).map(([token, balance]) => ({ token, balance }));
25
+ const assets = state.balances.toAssets(1n);
28
26
  return {
29
27
  operation: operation.operation,
30
28
  creditManager: operation.creditManager,
@@ -615,14 +615,12 @@ class CreditAccountsServiceV310 extends SDKConstruct {
615
615
  creditManager: cm.creditManager,
616
616
  slippage
617
617
  });
618
- const operationCalls = [
619
- ...routerCloseResult.calls,
620
- ...this.#prepareDisableQuotas(ca),
621
- ...this.#prepareDecreaseDebt(ca),
622
- ...assetsToWithdraw.map(
623
- (t) => this.#prepareWithdrawToken(ca.creditFacade, t, MAX_UINT256, to)
624
- )
625
- ];
618
+ const operationCalls = await this.assembleCloseCreditAccountCalls({
619
+ creditAccount: ca,
620
+ routerCalls: routerCloseResult.calls,
621
+ assetsToWithdraw,
622
+ to
623
+ });
626
624
  const calls = operation === "close" ? operationCalls : await this.#prependPriceUpdates(ca.creditManager, operationCalls, ca);
627
625
  const tx = await this.#closeCreditAccountTx(
628
626
  cm,
@@ -632,6 +630,32 @@ class CreditAccountsServiceV310 extends SDKConstruct {
632
630
  );
633
631
  return { tx, calls, routerCloseResult, creditFacade: cm.creditFacade };
634
632
  }
633
+ /**
634
+ * {@inheritDoc ICreditAccountsService.assembleCloseCreditAccountCalls}
635
+ */
636
+ async assembleCloseCreditAccountCalls({
637
+ creditAccount: ca,
638
+ routerCalls,
639
+ assetsToWithdraw,
640
+ to
641
+ }) {
642
+ const cm = this.sdk.marketRegister.findCreditManager(ca.creditManager);
643
+ await this.sdk.tokensMeta.loadTokenData(cm.underlying);
644
+ const underlying = this.sdk.tokensMeta.mustGet(cm.underlying);
645
+ if (this.sdk.tokensMeta.isRWAUnderlying(underlying)) {
646
+ throw new Error(
647
+ "closeCreditAccount is not supported for RWA underlying credit accounts"
648
+ );
649
+ }
650
+ return [
651
+ ...routerCalls,
652
+ ...this.#prepareDisableQuotas(ca),
653
+ ...this.#prepareDecreaseDebt(ca),
654
+ ...assetsToWithdraw.map(
655
+ (t) => this.#prepareWithdrawToken(ca.creditFacade, t, MAX_UINT256, to)
656
+ )
657
+ ];
658
+ }
635
659
  /**
636
660
  * {@inheritDoc ICreditAccountsService.updateQuotas}
637
661
  **/
@@ -1301,8 +1325,23 @@ class CreditAccountsServiceV310 extends SDKConstruct {
1301
1325
  /**
1302
1326
  * {@inheritDoc ICreditAccountsService.repayCreditAccount}
1303
1327
  */
1304
- async repayCreditAccount({
1305
- operation,
1328
+ async repayCreditAccount(props) {
1329
+ const { operation, creditAccount: ca } = props;
1330
+ const cm = this.sdk.marketRegister.findCreditManager(ca.creditManager);
1331
+ const operationCalls = await this.assembleRepayCreditAccountCalls(props);
1332
+ const calls = operation === "close" ? operationCalls : await this.#prependPriceUpdates(ca.creditManager, operationCalls, ca);
1333
+ const tx = await this.#closeCreditAccountTx(
1334
+ cm,
1335
+ ca.creditAccount,
1336
+ calls,
1337
+ operation
1338
+ );
1339
+ return { tx, calls, creditFacade: cm.creditFacade };
1340
+ }
1341
+ /**
1342
+ * {@inheritDoc ICreditAccountsService.assembleRepayCreditAccountCalls}
1343
+ */
1344
+ async assembleRepayCreditAccountCalls({
1306
1345
  collateralAssets,
1307
1346
  assetsToWithdraw,
1308
1347
  creditAccount: ca,
@@ -1311,7 +1350,6 @@ class CreditAccountsServiceV310 extends SDKConstruct {
1311
1350
  tokensToClaim,
1312
1351
  calls: wrapCalls = []
1313
1352
  }) {
1314
- const cm = this.sdk.marketRegister.findCreditManager(ca.creditManager);
1315
1353
  const addCollateral = collateralAssets.filter((a) => a.balance > 0);
1316
1354
  const router = this.sdk.routerFor(ca);
1317
1355
  const unwrapCalls = await this.getRedeemDiffCalls(1n, ca.creditManager) ?? [];
@@ -1330,14 +1368,7 @@ class CreditAccountsServiceV310 extends SDKConstruct {
1330
1368
  (t) => this.#prepareWithdrawToken(ca.creditFacade, t.token, MAX_UINT256, to)
1331
1369
  )
1332
1370
  ];
1333
- const calls = operation === "close" ? operationCalls : await this.#prependPriceUpdates(ca.creditManager, operationCalls, ca);
1334
- const tx = await this.#closeCreditAccountTx(
1335
- cm,
1336
- ca.creditAccount,
1337
- calls,
1338
- operation
1339
- );
1340
- return { tx, calls, creditFacade: cm.creditFacade };
1371
+ return operationCalls;
1341
1372
  }
1342
1373
  /**
1343
1374
  * {@inheritDoc ICreditAccountsService.repayAndLiquidateCreditAccount}
@@ -1558,19 +1589,14 @@ class CreditAccountsServiceV310 extends SDKConstruct {
1558
1589
  );
1559
1590
  }
1560
1591
  /**
1561
- * {@inheritDoc ICreditAccountsService.assembleCaUpdateCalls}
1592
+ * {@inheritDoc ICreditAccountsService.assembleCaOperations}
1562
1593
  */
1563
- assembleCaUpdateCalls({
1594
+ assembleCaOperations({
1564
1595
  operations,
1565
- routerCallGroups,
1566
1596
  creditFacade,
1567
- withdrawTo,
1568
- creditAccount,
1569
- underlyingToken
1597
+ withdrawTo
1570
1598
  }) {
1571
1599
  const calls = [];
1572
- let swapGroupIndex = 0;
1573
- let routerGroupsConsumed = 0;
1574
1600
  for (const op of operations) {
1575
1601
  switch (op.type) {
1576
1602
  case "increaseDebt":
@@ -1598,18 +1624,10 @@ class CreditAccountsServiceV310 extends SDKConstruct {
1598
1624
  )
1599
1625
  );
1600
1626
  break;
1601
- case "swap": {
1602
- const routerCalls = routerCallGroups[swapGroupIndex];
1603
- if (!routerCalls) {
1604
- throw new Error(
1605
- `assembleCaUpdateCalls: missing router calls for swap leg ${swapGroupIndex}`
1606
- );
1607
- }
1608
- calls.push(...routerCalls);
1609
- swapGroupIndex += 1;
1610
- routerGroupsConsumed += 1;
1627
+ case "swap":
1628
+ case "wrapRwaCollateral":
1629
+ calls.push(...op.calls);
1611
1630
  break;
1612
- }
1613
1631
  case "changeQuota": {
1614
1632
  const quotaAssets = [...op.quotaIncrease, ...op.quotaDecrease];
1615
1633
  if (quotaAssets.length === 0) {
@@ -1623,43 +1641,110 @@ class CreditAccountsServiceV310 extends SDKConstruct {
1623
1641
  );
1624
1642
  break;
1625
1643
  }
1626
- case "closeCreditAccount": {
1627
- for (const group of routerCallGroups) {
1628
- calls.push(...group);
1629
- routerGroupsConsumed += 1;
1630
- }
1644
+ default: {
1645
+ const _exhaustive = op;
1646
+ throw new Error(
1647
+ `assembleCaOperations: unsupported operation ${JSON.stringify(_exhaustive)}`
1648
+ );
1649
+ }
1650
+ }
1651
+ }
1652
+ return calls;
1653
+ }
1654
+ /**
1655
+ * {@inheritDoc ICreditAccountsService.assembleCaUpdateCalls}
1656
+ * @deprecated Prefer {@link CreditAccountsServiceV310.assembleCaOperations}.
1657
+ */
1658
+ assembleCaUpdateCalls({
1659
+ operations,
1660
+ routerCallGroups,
1661
+ creditFacade,
1662
+ withdrawTo,
1663
+ creditAccount,
1664
+ underlyingToken
1665
+ }) {
1666
+ const calls = [];
1667
+ let swapGroupIndex = 0;
1668
+ let routerGroupsConsumed = 0;
1669
+ const encodableOps = [];
1670
+ let pendingClose = null;
1671
+ const flushEncodable = () => {
1672
+ if (encodableOps.length === 0) {
1673
+ return;
1674
+ }
1675
+ calls.push(
1676
+ ...this.assembleCaOperations({
1677
+ operations: encodableOps.splice(0, encodableOps.length),
1678
+ creditFacade,
1679
+ withdrawTo
1680
+ })
1681
+ );
1682
+ };
1683
+ for (const op of operations) {
1684
+ if (op.type === "closeCreditAccount") {
1685
+ flushEncodable();
1686
+ pendingClose = { routerGroupsStart: routerGroupsConsumed };
1687
+ for (const group of routerCallGroups.slice(routerGroupsConsumed)) {
1688
+ calls.push(...group);
1689
+ routerGroupsConsumed += 1;
1690
+ }
1691
+ calls.push(
1692
+ ...this.#prepareDisableQuotas({
1693
+ creditFacade,
1694
+ tokens: Object.entries(creditAccount.initialQuotas).map(
1695
+ ([token, { quota }]) => ({
1696
+ token,
1697
+ quota
1698
+ })
1699
+ )
1700
+ })
1701
+ );
1702
+ if (creditAccount.debt > 0n) {
1631
1703
  calls.push(
1632
- ...this.#prepareDisableQuotas({
1704
+ ...this.#prepareDecreaseDebt({
1633
1705
  creditFacade,
1634
- tokens: Object.entries(creditAccount.initialQuotas).map(
1635
- ([token, { quota }]) => ({
1636
- token,
1637
- quota
1638
- })
1639
- )
1706
+ debt: creditAccount.debt
1640
1707
  })
1641
1708
  );
1642
- if (creditAccount.debt > 0n) {
1643
- calls.push(
1644
- ...this.#prepareDecreaseDebt({
1645
- creditFacade,
1646
- debt: creditAccount.debt
1647
- })
1648
- );
1649
- }
1650
- const hasAssets = creditAccount.assets.some(
1651
- (asset) => asset.balance > 0n
1709
+ }
1710
+ const hasAssets = creditAccount.assets.some(
1711
+ (asset) => asset.balance > 0n
1712
+ );
1713
+ if (hasAssets) {
1714
+ calls.push(
1715
+ this.#prepareWithdrawToken(
1716
+ creditFacade,
1717
+ underlyingToken,
1718
+ MAX_UINT256,
1719
+ withdrawTo
1720
+ )
1652
1721
  );
1653
- if (hasAssets) {
1654
- calls.push(
1655
- this.#prepareWithdrawToken(
1656
- creditFacade,
1657
- underlyingToken,
1658
- MAX_UINT256,
1659
- withdrawTo
1660
- )
1722
+ }
1723
+ continue;
1724
+ }
1725
+ if (pendingClose !== null) {
1726
+ throw new Error(
1727
+ "assembleCaUpdateCalls: operations after closeCreditAccount are not supported"
1728
+ );
1729
+ }
1730
+ switch (op.type) {
1731
+ case "increaseDebt":
1732
+ case "decreaseDebt":
1733
+ case "addCollateral":
1734
+ case "withdrawCollateral":
1735
+ case "changeQuota":
1736
+ encodableOps.push(op);
1737
+ break;
1738
+ case "swap": {
1739
+ const routerCalls = routerCallGroups[swapGroupIndex];
1740
+ if (!routerCalls) {
1741
+ throw new Error(
1742
+ `assembleCaUpdateCalls: missing router calls for swap leg ${swapGroupIndex}`
1661
1743
  );
1662
1744
  }
1745
+ encodableOps.push({ type: "swap", calls: routerCalls });
1746
+ swapGroupIndex += 1;
1747
+ routerGroupsConsumed += 1;
1663
1748
  break;
1664
1749
  }
1665
1750
  default: {
@@ -1670,6 +1755,7 @@ class CreditAccountsServiceV310 extends SDKConstruct {
1670
1755
  }
1671
1756
  }
1672
1757
  }
1758
+ flushEncodable();
1673
1759
  if (routerGroupsConsumed !== routerCallGroups.length) {
1674
1760
  throw new Error(
1675
1761
  `assembleCaUpdateCalls: router call group mismatch (consumed ${routerGroupsConsumed}, got ${routerCallGroups.length})`
@@ -10,11 +10,17 @@ class AssetsMap extends AddressMap {
10
10
  name
11
11
  );
12
12
  }
13
+ /**
14
+ * Looks up a token balance, treating a missing key as `0n`.
15
+ */
16
+ getOrZero(address) {
17
+ return this.get(address) ?? 0n;
18
+ }
13
19
  /**
14
20
  * Adds `amount` to the token balance, treating a missing key as `0n`.
15
21
  */
16
22
  inc(address, amount) {
17
- this.upsert(address, (this.get(address) ?? 0n) + amount);
23
+ this.upsert(address, this.getOrZero(address) + amount);
18
24
  }
19
25
  /**
20
26
  * Subtracts `amount` from the token balance, treating a missing key as `0n`.
@@ -22,12 +28,41 @@ class AssetsMap extends AddressMap {
22
28
  dec(address, amount) {
23
29
  this.inc(address, -amount);
24
30
  }
31
+ /**
32
+ * Per-token difference `this - before`, non-zero entries only.
33
+ *
34
+ * Assumes this map's keys are a superset of `before`'s keys; tokens present
35
+ * only in `before` are not reported.
36
+ */
37
+ difference(before) {
38
+ const result = new AssetsMap();
39
+ for (const [token, balance] of this.entries()) {
40
+ const change = balance - before.getOrZero(token);
41
+ if (change !== 0n) {
42
+ result.upsert(token, change);
43
+ }
44
+ }
45
+ return result;
46
+ }
47
+ /**
48
+ * Sums `fn` over all entries.
49
+ */
50
+ sum(fn) {
51
+ return this.entries().reduce(
52
+ (acc, [token, balance]) => acc + fn(token, balance),
53
+ 0n
54
+ );
55
+ }
25
56
  /**
26
57
  * Converts the map to an array of {@link Asset} objects.
27
- * @param filterDust - If true, filters out assets with a balance less than 1.
58
+ * @param minBalance - If provided, keeps only entries with a balance
59
+ * strictly greater than this threshold (e.g. `1n` to filter out dust).
28
60
  */
29
- toAssets(filterDust = false) {
30
- return this.entries().filter(filterDust ? ([, balance]) => balance > 1n : () => true).map(([token, balance]) => ({ token, balance }));
61
+ toAssets(minBalance) {
62
+ const abs = (balance) => balance > 0n ? balance : -balance;
63
+ return this.entries().filter(
64
+ ([, balance]) => minBalance === void 0 || abs(balance) > minBalance
65
+ ).map(([token, balance]) => ({ token, balance }));
31
66
  }
32
67
  clone() {
33
68
  return new AssetsMap(this.entries(), this.name);
@@ -6,7 +6,7 @@ import type { RWAOpenAccountRequirements } from "../market/rwa/index.js";
6
6
  import type { OnchainSDK } from "../OnchainSDK.js";
7
7
  import type { RouterCASlice } from "../router/index.js";
8
8
  import type { MultiCall, RawTx } from "../types/index.js";
9
- import type { AccountToCheck, AddCollateralProps, AssembleCaUpdateCallsProps, ChangeDeptProps, ClaimDelayedProps, ClaimFarmRewardsProps, CloseCreditAccountProps, CloseCreditAccountResult, CreditAccountOperationResult, CreditAccountTokensSlice, CreditManagerOperationResult, DefaultPartialLiquidationParams, ExecuteSwapProps, FullyLiquidateProps, FullyLiquidateResult, GetApprovalAddressProps, GetConnectedBotsResult, GetConnectedMigrationBotsResult, GetCreditAccountsOptions, GetOpenAccountRequirementsProps, GetPendingWithdrawalsProps, GetPendingWithdrawalsResult, ICreditAccountsService, OpenCAProps, PartiallyLiquidateProps, PreviewDelayedWithdrawalProps, PreviewDelayedWithdrawalResult, RepayAndLiquidateCreditAccountProps, RepayCreditAccountProps, Rewards, SetBotProps, StartDelayedWithdrawalProps, UpdateQuotasProps, WithdrawCollateralProps } from "./types.js";
9
+ import type { AccountToCheck, AddCollateralProps, AssembleCaOperationsProps, AssembleCaUpdateCallsProps, AssembleCloseCreditAccountCallsProps, AssembleRepayCreditAccountCallsProps, ChangeDeptProps, ClaimDelayedProps, ClaimFarmRewardsProps, CloseCreditAccountProps, CloseCreditAccountResult, CreditAccountOperationResult, CreditAccountTokensSlice, CreditManagerOperationResult, DefaultPartialLiquidationParams, ExecuteSwapProps, FullyLiquidateProps, FullyLiquidateResult, GetApprovalAddressProps, GetConnectedBotsResult, GetConnectedMigrationBotsResult, GetCreditAccountsOptions, GetOpenAccountRequirementsProps, GetPendingWithdrawalsProps, GetPendingWithdrawalsResult, ICreditAccountsService, OpenCAProps, PartiallyLiquidateProps, PreviewDelayedWithdrawalProps, PreviewDelayedWithdrawalResult, RepayAndLiquidateCreditAccountProps, RepayCreditAccountProps, Rewards, SetBotProps, StartDelayedWithdrawalProps, UpdateQuotasProps, WithdrawCollateralProps } from "./types.js";
10
10
  /**
11
11
  * Options for configuring the credit account service.
12
12
  **/
@@ -79,6 +79,10 @@ export declare class CreditAccountsServiceV310 extends SDKConstruct implements I
79
79
  * {@inheritDoc ICreditAccountsService.closeCreditAccount}
80
80
  **/
81
81
  closeCreditAccount({ operation, assetsToWithdraw, creditAccount: ca, to, slippage, closePath, }: CloseCreditAccountProps): Promise<CloseCreditAccountResult>;
82
+ /**
83
+ * {@inheritDoc ICreditAccountsService.assembleCloseCreditAccountCalls}
84
+ */
85
+ assembleCloseCreditAccountCalls({ creditAccount: ca, routerCalls, assetsToWithdraw, to, }: AssembleCloseCreditAccountCallsProps): Promise<Array<MultiCall>>;
82
86
  /**
83
87
  * {@inheritDoc ICreditAccountsService.updateQuotas}
84
88
  **/
@@ -178,7 +182,11 @@ export declare class CreditAccountsServiceV310 extends SDKConstruct implements I
178
182
  /**
179
183
  * {@inheritDoc ICreditAccountsService.repayCreditAccount}
180
184
  */
181
- repayCreditAccount({ operation, collateralAssets, assetsToWithdraw, creditAccount: ca, permits, to, tokensToClaim, calls: wrapCalls, }: RepayCreditAccountProps): Promise<CreditAccountOperationResult>;
185
+ repayCreditAccount(props: RepayCreditAccountProps): Promise<CreditAccountOperationResult>;
186
+ /**
187
+ * {@inheritDoc ICreditAccountsService.assembleRepayCreditAccountCalls}
188
+ */
189
+ assembleRepayCreditAccountCalls({ collateralAssets, assetsToWithdraw, creditAccount: ca, permits, to, tokensToClaim, calls: wrapCalls, }: AssembleRepayCreditAccountCallsProps): Promise<Array<MultiCall>>;
182
190
  /**
183
191
  * {@inheritDoc ICreditAccountsService.repayAndLiquidateCreditAccount}
184
192
  */
@@ -210,8 +218,13 @@ export declare class CreditAccountsServiceV310 extends SDKConstruct implements I
210
218
  prependPriceUpdates(creditManager: Address, calls: MultiCall[], creditAccount?: RouterCASlice, options?: {
211
219
  ignoreReservePrices?: boolean;
212
220
  }): Promise<MultiCall[]>;
221
+ /**
222
+ * {@inheritDoc ICreditAccountsService.assembleCaOperations}
223
+ */
224
+ assembleCaOperations({ operations, creditFacade, withdrawTo, }: AssembleCaOperationsProps): MultiCall[];
213
225
  /**
214
226
  * {@inheritDoc ICreditAccountsService.assembleCaUpdateCalls}
227
+ * @deprecated Prefer {@link CreditAccountsServiceV310.assembleCaOperations}.
215
228
  */
216
229
  assembleCaUpdateCalls({ operations, routerCallGroups, creditFacade, withdrawTo, creditAccount, underlyingToken, }: AssembleCaUpdateCallsProps): MultiCall[];
217
230
  /**
@@ -182,16 +182,27 @@ export interface CloseCreditAccountProps {
182
182
  */
183
183
  closePath?: RouterCloseResult;
184
184
  }
185
- export interface RepayCreditAccountProps extends RepayAndLiquidateCreditAccountProps {
185
+ /**
186
+ * Input for {@link ICreditAccountsService.assembleCloseCreditAccountCalls}.
187
+ */
188
+ export type AssembleCloseCreditAccountCallsProps = {
186
189
  /**
187
- * Swap calls for repay
190
+ * Minimal credit account data on which operation is performed.
188
191
  */
189
- calls?: Array<MultiCall>;
192
+ creditAccount: RouterCASlice;
190
193
  /**
191
- * close or zeroDebt
194
+ * Pathfinder close router calls (`closePath.calls`).
192
195
  */
193
- operation: CloseOptions;
194
- }
196
+ routerCalls: Array<MultiCall>;
197
+ /**
198
+ * Tokens to withdraw from credit account after close path swaps.
199
+ */
200
+ assetsToWithdraw: Array<Address>;
201
+ /**
202
+ * Wallet address to withdraw tokens to.
203
+ */
204
+ to: Address;
205
+ };
195
206
  export interface RepayAndLiquidateCreditAccountProps {
196
207
  /**
197
208
  * Tokens to repay debt.
@@ -220,6 +231,31 @@ export interface RepayAndLiquidateCreditAccountProps {
220
231
  permits: Record<string, PermitResult>;
221
232
  tokensToClaim: Asset[];
222
233
  }
234
+ /**
235
+ * Input for {@link ICreditAccountsService.assembleRepayCreditAccountCalls}.
236
+ */
237
+ export type AssembleRepayCreditAccountCallsProps = {
238
+ collateralAssets: Array<Asset>;
239
+ assetsToWithdraw: Array<Asset>;
240
+ creditAccount: RouterCASlice;
241
+ to: Address;
242
+ permits: Record<string, PermitResult>;
243
+ tokensToClaim: Asset[];
244
+ /**
245
+ * RWA wrap multicall entries (from getRWAWrapCalls).
246
+ */
247
+ calls?: Array<MultiCall>;
248
+ };
249
+ export interface RepayCreditAccountProps extends RepayAndLiquidateCreditAccountProps {
250
+ /**
251
+ * RWA wrap multicall entries (from getRWAWrapCalls).
252
+ */
253
+ calls?: Array<MultiCall>;
254
+ /**
255
+ * close or zeroDebt
256
+ */
257
+ operation: CloseOptions;
258
+ }
223
259
  export interface PrepareUpdateQuotasProps {
224
260
  /**
225
261
  * average quota for desired token
@@ -677,6 +713,10 @@ export type GetApprovalAddressProps = {
677
713
  *
678
714
  * Used by {@link ICreditAccountsService.assembleCaUpdateCalls} to build the
679
715
  * underlying credit facade multicall calls.
716
+ *
717
+ * @deprecated Prefer {@link EncodableCreditAccountOperation} with
718
+ * {@link ICreditAccountsService.assembleCaOperations}. Close stays on dedicated
719
+ * assemblers / this deprecated path only.
680
720
  */
681
721
  export type CreditAccountUpdateOperation = {
682
722
  type: "increaseDebt";
@@ -703,6 +743,9 @@ export type CreditAccountUpdateOperation = {
703
743
  };
704
744
  /**
705
745
  * Input for {@link ICreditAccountsService.assembleCaUpdateCalls}.
746
+ *
747
+ * @deprecated Prefer {@link AssembleCaOperationsProps} /
748
+ * {@link ICreditAccountsService.assembleCaOperations}.
706
749
  */
707
750
  export type AssembleCaUpdateCallsProps = {
708
751
  operations: Array<CreditAccountUpdateOperation>;
@@ -719,6 +762,47 @@ export type AssembleCaUpdateCallsProps = {
719
762
  };
720
763
  underlyingToken: Address;
721
764
  };
765
+ /**
766
+ * An enriched credit-account operation ready for encoding.
767
+ * Each op carries everything needed to build facade / adapter multicalls —
768
+ * swap and wrap attach concrete `calls` (no external routerCallGroups).
769
+ *
770
+ * Used by {@link ICreditAccountsService.assembleCaOperations}. Does not include
771
+ * close / repay (use dedicated assemblers).
772
+ */
773
+ export type EncodableCreditAccountOperation = {
774
+ type: "increaseDebt";
775
+ amount: bigint;
776
+ } | {
777
+ type: "decreaseDebt";
778
+ amount: bigint;
779
+ } | {
780
+ type: "addCollateral";
781
+ token: Address;
782
+ amount: bigint;
783
+ } | {
784
+ type: "withdrawCollateral";
785
+ token: Address;
786
+ amount: bigint;
787
+ } | {
788
+ type: "swap";
789
+ calls: Array<MultiCall>;
790
+ } | {
791
+ type: "wrapRwaCollateral";
792
+ calls: Array<MultiCall>;
793
+ } | {
794
+ type: "changeQuota";
795
+ quotaIncrease: Array<Asset>;
796
+ quotaDecrease: Array<Asset>;
797
+ };
798
+ /**
799
+ * Input for {@link ICreditAccountsService.assembleCaOperations}.
800
+ */
801
+ export type AssembleCaOperationsProps = {
802
+ operations: Array<EncodableCreditAccountOperation>;
803
+ creditFacade: Address;
804
+ withdrawTo: Address;
805
+ };
722
806
  export interface ICreditAccountsService extends Construct {
723
807
  sdk: OnchainSDK;
724
808
  /**
@@ -798,6 +882,17 @@ export interface ICreditAccountsService extends Construct {
798
882
  * @returns Raw transaction ready to be signed and sent
799
883
  */
800
884
  partiallyLiquidate(props: PartiallyLiquidateProps): Promise<RawTx>;
885
+ /**
886
+ * Builds close multicall calls without price feed updates.
887
+ *
888
+ * Same operation sequence as {@link closeCreditAccount} (close path swaps,
889
+ * disable quotas, decrease debt, withdraw assets), but does not prepend
890
+ * price updates and does not build the facade transaction.
891
+ *
892
+ * @param props - {@link AssembleCloseCreditAccountCallsProps}
893
+ * @returns Raw facade multicall payload for close (before price feed updates)
894
+ */
895
+ assembleCloseCreditAccountCalls(props: AssembleCloseCreditAccountCallsProps): Promise<Array<MultiCall>>;
801
896
  /**
802
897
  * Closes credit account or closes credit account and keeps it open with zero debt.
803
898
  * - Ca is closed in the following order: price update -> close path to swap all tokens into underlying ->
@@ -942,11 +1037,26 @@ export interface ICreditAccountsService extends Construct {
942
1037
  prependPriceUpdates(creditManager: Address, calls: Array<MultiCall>, creditAccount?: RouterCASlice, options?: {
943
1038
  ignoreReservePrices?: boolean;
944
1039
  }): Promise<Array<MultiCall>>;
1040
+ /**
1041
+ * Builds credit facade multicall calls from an enriched operation list.
1042
+ * Each operation must already carry concrete encoding data (e.g. swap/wrap
1043
+ * `calls`). Unknown operation types throw.
1044
+ *
1045
+ * Does not handle close or repay — use
1046
+ * {@link ICreditAccountsService.assembleCloseCreditAccountCalls} /
1047
+ * {@link ICreditAccountsService.assembleRepayCreditAccountCalls}.
1048
+ *
1049
+ * @param props - Encodable operations and account context
1050
+ * @returns Array of facade / adapter multicall calls (without price feed updates)
1051
+ */
1052
+ assembleCaOperations(props: AssembleCaOperationsProps): Array<MultiCall>;
945
1053
  /**
946
1054
  * Builds the credit facade multicall calls for a chain of update operations.
947
1055
  *
948
1056
  * @param props - Operation chain, router call groups and account context
949
1057
  * @returns Array of facade multicall calls (without price feed updates)
1058
+ * @deprecated Prefer {@link ICreditAccountsService.assembleCaOperations}. Close
1059
+ * remains on this method / dedicated close assembler only.
950
1060
  */
951
1061
  assembleCaUpdateCalls(props: AssembleCaUpdateCallsProps): Array<MultiCall>;
952
1062
  /**
@@ -1009,6 +1119,17 @@ export interface ICreditAccountsService extends Construct {
1009
1119
  * @return All necessary data to execute the transaction (call, credit facade)
1010
1120
  */
1011
1121
  withdrawCollateral(props: WithdrawCollateralProps): Promise<CreditAccountOperationResult>;
1122
+ /**
1123
+ * Builds repay multicall calls without price feed updates.
1124
+ *
1125
+ * Same operation sequence as {@link repayCreditAccount} (add collateral, wrap calls,
1126
+ * disable quotas, decrease debt, redeem/unwrap, claim rewards, withdraw assets),
1127
+ * but does not prepend price updates and does not build the facade transaction.
1128
+ *
1129
+ * @param props - {@link AssembleRepayCreditAccountCallsProps}
1130
+ * @returns Raw facade multicall payload for repay (before price feed updates)
1131
+ */
1132
+ assembleRepayCreditAccountCalls(props: AssembleRepayCreditAccountCallsProps): Promise<Array<MultiCall>>;
1012
1133
  /**
1013
1134
  * Fully repays credit account or repays credit account and keeps it open with zero debt
1014
1135
  * - Repays in the following order: price update -> add collateral to cover the debt ->
@@ -1,3 +1,4 @@
1
+ import type { Address } from "viem";
1
2
  import type { Asset } from "../base/types.js";
2
3
  import { AddressMap } from "./AddressMap.js";
3
4
  /**
@@ -10,6 +11,10 @@ export declare class AssetsMap extends AddressMap<bigint> {
10
11
  * @param name - Optional label used in error messages when a lookup fails.
11
12
  */
12
13
  constructor(entries?: Array<[string, bigint] | Asset>, name?: string);
14
+ /**
15
+ * Looks up a token balance, treating a missing key as `0n`.
16
+ */
17
+ getOrZero(address: string): bigint;
13
18
  /**
14
19
  * Adds `amount` to the token balance, treating a missing key as `0n`.
15
20
  */
@@ -18,10 +23,22 @@ export declare class AssetsMap extends AddressMap<bigint> {
18
23
  * Subtracts `amount` from the token balance, treating a missing key as `0n`.
19
24
  */
20
25
  dec(address: string, amount: bigint): void;
26
+ /**
27
+ * Per-token difference `this - before`, non-zero entries only.
28
+ *
29
+ * Assumes this map's keys are a superset of `before`'s keys; tokens present
30
+ * only in `before` are not reported.
31
+ */
32
+ difference(before: AssetsMap): AssetsMap;
33
+ /**
34
+ * Sums `fn` over all entries.
35
+ */
36
+ sum(fn: (token: Address, balance: bigint) => bigint): bigint;
21
37
  /**
22
38
  * Converts the map to an array of {@link Asset} objects.
23
- * @param filterDust - If true, filters out assets with a balance less than 1.
39
+ * @param minBalance - If provided, keeps only entries with a balance
40
+ * strictly greater than this threshold (e.g. `1n` to filter out dust).
24
41
  */
25
- toAssets(filterDust?: boolean): Asset[];
42
+ toAssets(minBalance?: bigint): Asset[];
26
43
  clone(): AssetsMap;
27
44
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gearbox-protocol/sdk",
3
- "version": "14.12.0-next.10",
3
+ "version": "14.12.0-next.12",
4
4
  "description": "Gearbox SDK",
5
5
  "license": "MIT",
6
6
  "repository": {