@gearbox-protocol/sdk 16.0.0-next.47 → 16.0.0-next.49

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/model/positions.schema.js +7 -2
  2. package/dist/cjs/offchain/positions/OffchainPositions.js +9 -0
  3. package/dist/cjs/onchain/accounts/intents/testing/sdk-mock.js +2 -0
  4. package/dist/cjs/onchain/market/MarketSuite.js +28 -0
  5. package/dist/cjs/onchain/positions/PositionsService.js +4 -2
  6. package/dist/cjs/preview/index.js +2 -0
  7. package/dist/cjs/preview/preview/buildDelayedStrategyPositionOperationPreview.js +20 -20
  8. package/dist/cjs/preview/preview/errors.js +16 -0
  9. package/dist/cjs/preview/preview/index.js +2 -1
  10. package/dist/cjs/preview/preview/previewAdjustStrategyPosition.js +4 -12
  11. package/dist/cjs/preview/preview/previewExitOrRepayStrategyPosition.js +15 -5
  12. package/dist/cjs/sdk/positions/PositionsNamespace.js +6 -0
  13. package/dist/esm/model/positions.schema.js +7 -2
  14. package/dist/esm/offchain/positions/OffchainPositions.js +10 -1
  15. package/dist/esm/onchain/accounts/intents/testing/sdk-mock.js +2 -0
  16. package/dist/esm/onchain/market/MarketSuite.js +28 -0
  17. package/dist/esm/onchain/positions/PositionsService.js +4 -2
  18. package/dist/esm/preview/index.js +2 -1
  19. package/dist/esm/preview/preview/buildDelayedStrategyPositionOperationPreview.js +21 -21
  20. package/dist/esm/preview/preview/errors.js +16 -1
  21. package/dist/esm/preview/preview/index.js +2 -2
  22. package/dist/esm/preview/preview/previewAdjustStrategyPosition.js +5 -13
  23. package/dist/esm/preview/preview/previewExitOrRepayStrategyPosition.js +15 -5
  24. package/dist/esm/sdk/positions/PositionsNamespace.js +6 -0
  25. package/dist/types/model/positions.d.ts +36 -2
  26. package/dist/types/model/positions.schema.d.ts +26 -2
  27. package/dist/types/model/previews.d.ts +6 -11
  28. package/dist/types/offchain/positions/OffchainPositions.d.ts +5 -1
  29. package/dist/types/offchain/positions/types.d.ts +7 -1
  30. package/dist/types/onchain/index.d.ts +2 -2
  31. package/dist/types/onchain/market/MarketSuite.d.ts +29 -2
  32. package/dist/types/onchain/market/index.d.ts +2 -2
  33. package/dist/types/preview/index.d.ts +2 -2
  34. package/dist/types/preview/preview/buildDelayedStrategyPositionOperationPreview.d.ts +3 -2
  35. package/dist/types/preview/preview/errors.d.ts +9 -1
  36. package/dist/types/preview/preview/index.d.ts +2 -2
  37. package/dist/types/sdk/positions/PositionsNamespace.d.ts +5 -1
  38. package/dist/types/sdk/positions/types.d.ts +10 -1
  39. package/package.json +1 -1
@@ -208,7 +208,10 @@ const positionTransactionKindSchema = zod_v4.z.union([
208
208
  zod_v4.z.literal("adjustLeverage"),
209
209
  zod_v4.z.literal("addCollateral"),
210
210
  zod_v4.z.literal("withdrawCollateral"),
211
- zod_v4.z.literal("liquidation")
211
+ zod_v4.z.literal("liquidation"),
212
+ zod_v4.z.literal("repay"),
213
+ zod_v4.z.literal("rebalance"),
214
+ zod_v4.z.literal("other")
212
215
  ]);
213
216
  /**
214
217
  * {@link PositionTransaction}
@@ -217,7 +220,9 @@ const positionTransactionSchema = zod_v4.z.object({
217
220
  txHash: require_onchain_utils_zod.ZodHex(),
218
221
  timestamp: require_model_primitives_schema.timestampSchema,
219
222
  kind: positionTransactionKindSchema,
220
- assets: zod_v4.z.array(require_model_primitives_schema.tokenAmountSchema)
223
+ assets: zod_v4.z.array(require_model_primitives_schema.tokenAmountSchema),
224
+ balanceChanges: zod_v4.z.array(require_model_primitives_schema.tokenAmountSchema),
225
+ debtChange: require_model_primitives_schema.tokenAmountSchema
221
226
  });
222
227
  //#endregion
223
228
  exports.borrowRateBreakdownSchema = borrowRateBreakdownSchema;
@@ -39,6 +39,15 @@ var OffchainPositions = class extends require_offchain_AbstractOffchainNamespace
39
39
  async getCharts(key, metrics, range) {
40
40
  return this.readCharts(`${this.#chartRoot(key)}/charts`, metrics, range);
41
41
  }
42
+ /**
43
+ * {@inheritDoc IOffchainPositions.getTransactions}
44
+ **/
45
+ async getTransactions(key) {
46
+ return this.get({
47
+ path: `${this.#strategyPath(key)}/transactions`,
48
+ schema: zod_v4.z.array(require_model_positions_schema.positionTransactionSchema)
49
+ });
50
+ }
42
51
  #poolPath(key) {
43
52
  return `${this.#root}/pool/${key.chainId}/${key.pool}/${key.wallet}`;
44
53
  }
@@ -2,6 +2,7 @@ require("../../../constants/math.js");
2
2
  require("../../../constants/index.js");
3
3
  const require_onchain_market_math = require("../../../market/math.js");
4
4
  const require_onchain_market_credit_CreditSuite = require("../../../market/credit/CreditSuite.js");
5
+ const require_onchain_market_MarketSuite = require("../../../market/MarketSuite.js");
5
6
  const require_onchain_positions_PositionsService = require("../../../positions/PositionsService.js");
6
7
  let vitest = require("vitest");
7
8
  //#region src/onchain/accounts/intents/testing/sdk-mock.ts
@@ -221,6 +222,7 @@ function buildMockSdk(args) {
221
222
  underlying: args.underlying
222
223
  }
223
224
  };
225
+ Object.assign(market, { valueInUnderlying: require_onchain_market_MarketSuite.MarketSuite.prototype.valueInUnderlying });
224
226
  const collateralTokens = [args.underlying.toLowerCase(), ...Object.keys(args.liquidationThresholds).map((t) => t.toLowerCase()).filter((t) => t !== args.underlying.toLowerCase())];
225
227
  const forbidden = new Set((args.forbiddenTokens ?? []).map((t) => t.toLowerCase()));
226
228
  const forbiddenTokensMask = collateralTokens.reduce((mask, token, i) => forbidden.has(token) ? mask | 1n << BigInt(i) : mask, 0n);
@@ -1,6 +1,8 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_onchain_utils_AddressMap = require("../utils/AddressMap.js");
3
3
  const require_onchain_chain_chains = require("../chain/chains.js");
4
+ const require_onchain_constants_math = require("../constants/math.js");
5
+ require("../constants/index.js");
4
6
  require("../utils/index.js");
5
7
  const require_onchain_base_SDKConstruct = require("../base/SDKConstruct.js");
6
8
  require("../base/index.js");
@@ -151,6 +153,32 @@ var MarketSuite = class extends require_onchain_base_SDKConstruct.SDKConstruct {
151
153
  ...this.priceOracle.toAmount(this.underlying, value)
152
154
  });
153
155
  /**
156
+ * Sums `assets` in this market's underlying at latest oracle prices.
157
+ *
158
+ * Balances at or below `minBalance` are ignored. A token the oracle cannot
159
+ * price contributes nothing; the first such token is {@link ValueInUnderlying.unpriceable}.
160
+ *
161
+ * The counterpart of {@link toUnderlyingAmount}: that method labels a figure
162
+ * already in underlying, this one produces the figure from mixed holdings.
163
+ **/
164
+ valueInUnderlying(assets, minBalance = require_onchain_constants_math.DUST_THRESHOLD) {
165
+ let unpriceable;
166
+ let value = 0n;
167
+ for (const { token, balance } of assets) {
168
+ if (balance <= minBalance) continue;
169
+ const converted = this.priceOracle.safeConvert(token, this.underlying, balance);
170
+ if (converted === null) {
171
+ unpriceable ??= token;
172
+ continue;
173
+ }
174
+ value += converted;
175
+ }
176
+ return unpriceable === void 0 ? { value } : {
177
+ value,
178
+ unpriceable
179
+ };
180
+ }
181
+ /**
154
182
  * Display name of this market's pool, e.g. `"USDC Pool"`.
155
183
  */
156
184
  get poolName() {
@@ -294,12 +294,14 @@ var PositionsService = class extends require_onchain_base_SDKConstruct.SDKConstr
294
294
  withdrawals: withdrawals.get(t.token) ?? []
295
295
  });
296
296
  if (recomputeTotals) {
297
- const value = priceOracle.safeConvert(t.token, market.underlying, t.balance) || 0n;
298
- totalValue += value;
299
297
  const usd = priceOracle.safeConvertToUSD(t.token, t.balance) || 0n;
300
298
  totalValueUSD += usd;
301
299
  }
302
300
  }
301
+ if (recomputeTotals) totalValue = market.valueInUnderlying(ca.tokens.map((t) => ({
302
+ token: t.token,
303
+ balance: t.balance
304
+ }))).value;
303
305
  const snapshot = {
304
306
  ...require_onchain_positions_types.accountSnapshotFromCreditAccountData(ca),
305
307
  totalValue
@@ -18,6 +18,7 @@ const require_preview_prerequisites_BalancePrerequisite = require("./prerequisit
18
18
  const require_preview_prerequisites_RWAOpenRequirementsPrerequisite = require("./prerequisites/RWAOpenRequirementsPrerequisite.js");
19
19
  const require_preview_prerequisites_checkPrerequisites = require("./prerequisites/checkPrerequisites.js");
20
20
  require("./prerequisites/index.js");
21
+ const require_preview_preview_errors = require("./preview/errors.js");
21
22
  const require_preview_preview_buildDelayedStrategyPositionOperationPreview = require("./preview/buildDelayedStrategyPositionOperationPreview.js");
22
23
  const require_preview_preview_CreditAccountState = require("./preview/CreditAccountState.js");
23
24
  const require_preview_preview_detectCloseOrRepay = require("./preview/detectCloseOrRepay.js");
@@ -77,3 +78,4 @@ exports.refuse = require_onchain_validation_refusal.refuse;
77
78
  exports.replayInnerOperations = require_preview_preview_replayInnerOperations.replayInnerOperations;
78
79
  exports.replayMulticall = require_preview_preview_replayMulticall.replayMulticall;
79
80
  exports.resolveDelayedClaimIntent = require_preview_preview_detectDelayedClaim.resolveDelayedClaimIntent;
81
+ exports.unpriceableTokenError = require_preview_preview_errors.unpriceableTokenError;
@@ -5,6 +5,7 @@ const require_onchain_constants_math = require("../../onchain/constants/math.js"
5
5
  const require_model_previews = require("../../model/previews.js");
6
6
  require("../../model/index.js");
7
7
  require("../../onchain/index.js");
8
+ const require_preview_preview_errors = require("./errors.js");
8
9
  let viem = require("viem");
9
10
  //#region src/preview/preview/buildDelayedStrategyPositionOperationPreview.ts
10
11
  /**
@@ -13,8 +14,9 @@ let viem = require("viem");
13
14
  * the claim itself followed by the intent-specific tail
14
15
  *
15
16
  * Pure function: the input states are never mutated and no network access is performed.
16
- * Swaps are estimated with the injected conversion; tokens it cannot price contribute
17
- * nothing and set a non-fatal `ERROR_UNPRICEABLE_TOKEN` error on the
17
+ * Swaps are estimated with the injected conversion; remaining holdings are
18
+ * priced by `MarketSuite.valueInUnderlying`. Tokens that cannot be priced
19
+ * contribute nothing and set a non-fatal `ERROR_UNPRICEABLE_TOKEN` error on the
18
20
  * preview.
19
21
  *
20
22
  * The changes (e.g. `totalDebtChange`) are reported relative to the account
@@ -51,10 +53,7 @@ function makeSafeConverter(convert) {
51
53
  try {
52
54
  return convert(token, to, amount);
53
55
  } catch {
54
- error ??= {
55
- code: require_model_previews.ERROR_UNPRICEABLE_TOKEN,
56
- message: `cannot price token ${token}`
57
- };
56
+ error ??= require_preview_preview_errors.unpriceableTokenError(token);
58
57
  return 0n;
59
58
  }
60
59
  },
@@ -149,35 +148,36 @@ function repayFromClaim(post, claimToken, convert, amount) {
149
148
  post.repay(received);
150
149
  }
151
150
  /**
152
- * Oracle estimate of the account's total value in the underlying, ignoring
153
- * balances at or below `dust`.
154
- */
155
- function totalValueInUnderlying(post, convert, dust) {
156
- return post.balances.sum((token, balance) => balance > dust ? convert(token, post.underlying, balance) : 0n);
157
- }
158
- /**
159
151
  * `CLOSE_ACCOUNT` operation tail: everything is swapped into the
160
152
  * underlying, the debt is repaid in full and the remainder is withdrawn to
161
153
  * the user as `receivedToken`.
162
154
  */
163
155
  function buildClosePreview(post, converter, receivedToken, sdk) {
164
- const totalValue = totalValueInUnderlying(post, converter.convert, 0n);
165
- const oracle = sdk.marketRegister.findByCreditManager(post.creditManager).priceOracle;
156
+ const market = sdk.marketRegister.findByCreditManager(post.creditManager);
157
+ const priced = market.valueInUnderlying(post.balances.toAssets(), 0n);
158
+ const oracle = market.priceOracle;
166
159
  const suite = sdk.marketRegister.findCreditManager(post.creditManager);
167
160
  return {
168
161
  operation: "CloseCreditAccount",
169
162
  permanent: false,
170
- ...suite.creditOperationMarket(),
163
+ ...require_model_previews.asEstimated(sdk.positions.projection({
164
+ creditManager: post.creditManager,
165
+ assets: [],
166
+ quotas: [],
167
+ totalDebt: 0n,
168
+ totalValue: 0n
169
+ }, { availableLiquidityChange: post.totalDebt })),
171
170
  creditAccount: post.creditAccount,
172
171
  name: suite.accountStrategyName(post.creditAccount),
173
172
  targetCollateral: suite.accountTargetCollateral(post.creditAccount),
174
- receivedAmount: oracle.toTokenAmount(receivedToken, require_onchain_utils_bigint_math.BigIntMath.max(totalValue - post.totalDebt, 0n)),
175
- error: converter.error
173
+ receivedAmount: oracle.toTokenAmount(receivedToken, require_onchain_utils_bigint_math.BigIntMath.max(priced.value - post.totalDebt, 0n)),
174
+ error: converter.error ?? (priced.unpriceable ? require_preview_preview_errors.unpriceableTokenError(priced.unpriceable) : void 0)
176
175
  };
177
176
  }
178
177
  function buildAdjustPreview(post, before, collateralWithdrawn, converter, sdk) {
179
- const snap = post.toSnapshot(totalValueInUnderlying(post, converter.convert, require_onchain_constants_math.DUST_THRESHOLD));
180
178
  const market = sdk.marketRegister.findByCreditManager(post.creditManager);
179
+ const priced = market.valueInUnderlying(post.balances.toAssets());
180
+ const snap = post.toSnapshot(priced.value);
181
181
  const suite = sdk.marketRegister.findCreditManager(post.creditManager);
182
182
  const oracle = market.priceOracle;
183
183
  return {
@@ -194,7 +194,7 @@ function buildAdjustPreview(post, before, collateralWithdrawn, converter, sdk) {
194
194
  ...oracle.toAmount(market.underlying, q.balance)
195
195
  })),
196
196
  assetsChange: post.balances.difference(before.balances).toAssets(require_onchain_constants_math.DUST_THRESHOLD).map((a) => oracle.toTokenAmount(a.token, a.balance)),
197
- error: converter.error
197
+ error: converter.error ?? (priced.unpriceable ? require_preview_preview_errors.unpriceableTokenError(priced.unpriceable) : void 0)
198
198
  };
199
199
  }
200
200
  //#endregion
@@ -1 +1,17 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_model_previews = require("../../model/previews.js");
3
+ require("../../model/index.js");
4
+ //#region src/preview/preview/errors.ts
5
+ /**
6
+ * Preview limitation (2xxx): the oracle could not price `token`. Callers
7
+ * attach this with `error ??=` so a malformed-transaction (1xxx) error
8
+ * already recorded keeps precedence.
9
+ **/
10
+ function unpriceableTokenError(token) {
11
+ return {
12
+ code: require_model_previews.ERROR_UNPRICEABLE_TOKEN,
13
+ message: `cannot price token ${token}`
14
+ };
15
+ }
16
+ //#endregion
17
+ exports.unpriceableTokenError = unpriceableTokenError;
@@ -1,10 +1,10 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_preview_preview_errors = require("./errors.js");
2
3
  const require_preview_preview_buildDelayedStrategyPositionOperationPreview = require("./buildDelayedStrategyPositionOperationPreview.js");
3
4
  const require_preview_preview_CreditAccountState = require("./CreditAccountState.js");
4
5
  const require_preview_preview_detectCloseOrRepay = require("./detectCloseOrRepay.js");
5
6
  const require_preview_preview_detectDelayedClaim = require("./detectDelayedClaim.js");
6
7
  const require_preview_preview_detectDelayedOperation = require("./detectDelayedOperation.js");
7
- require("./errors.js");
8
8
  const require_preview_preview_estimateClaimableAt = require("./estimateClaimableAt.js");
9
9
  const require_preview_preview_replayInnerOperations = require("./replayInnerOperations.js");
10
10
  const require_preview_preview_replayMulticall = require("./replayMulticall.js");
@@ -25,3 +25,4 @@ exports.previewOperation = require_preview_preview_previewOperation.previewOpera
25
25
  exports.replayInnerOperations = require_preview_preview_replayInnerOperations.replayInnerOperations;
26
26
  exports.replayMulticall = require_preview_preview_replayMulticall.replayMulticall;
27
27
  exports.resolveDelayedClaimIntent = require_preview_preview_detectDelayedClaim.resolveDelayedClaimIntent;
28
+ exports.unpriceableTokenError = require_preview_preview_errors.unpriceableTokenError;
@@ -4,6 +4,7 @@ const require_onchain_constants_math = require("../../onchain/constants/math.js"
4
4
  const require_model_previews = require("../../model/previews.js");
5
5
  require("../../model/index.js");
6
6
  require("../../onchain/index.js");
7
+ const require_preview_preview_errors = require("./errors.js");
7
8
  const require_preview_preview_replayMulticall = require("./replayMulticall.js");
8
9
  const require_preview_preview_unwrapNativeCollateral = require("./unwrapNativeCollateral.js");
9
10
  //#region src/preview/preview/previewAdjustStrategyPosition.ts
@@ -25,18 +26,9 @@ function previewAdjustStrategyPosition(input, operation, options) {
25
26
  const { assets: collateralAdded, error: unwrapError } = require_preview_preview_unwrapNativeCollateral.unwrapNativeCollateral(after.collateralAdded.toAssets(), value, sdk.addressProvider.getAddress(require_onchain_constants_address_provider.AP_WETH_TOKEN, 0));
26
27
  error ??= unwrapError;
27
28
  const assetsChange = account.balances.difference(before.balances).toAssets(require_onchain_constants_math.DUST_THRESHOLD);
28
- const totalValue = account.balances.toAssets(require_onchain_constants_math.DUST_THRESHOLD).reduce((acc, { token, balance }) => {
29
- try {
30
- return acc + oracle.convert(token, market.underlying, balance);
31
- } catch {
32
- error ??= {
33
- code: require_model_previews.ERROR_UNPRICEABLE_TOKEN,
34
- message: `cannot price token ${token}`
35
- };
36
- return acc;
37
- }
38
- }, 0n);
39
- const snap = account.toSnapshot(totalValue);
29
+ const priced = market.valueInUnderlying(account.balances.toAssets());
30
+ if (priced.unpriceable) error ??= require_preview_preview_errors.unpriceableTokenError(priced.unpriceable);
31
+ const snap = account.toSnapshot(priced.value);
40
32
  return {
41
33
  operation: "AdjustCreditAccount",
42
34
  ...require_model_previews.asEstimated(sdk.positions.projection(snap, { availableLiquidityChange: before.totalDebt - account.totalDebt })),
@@ -1,7 +1,10 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_onchain_constants_address_provider = require("../../onchain/constants/address-provider.js");
3
3
  require("../../onchain/constants/math.js");
4
+ const require_model_previews = require("../../model/previews.js");
5
+ require("../../model/index.js");
4
6
  require("../../onchain/index.js");
7
+ const require_preview_preview_errors = require("./errors.js");
5
8
  const require_preview_preview_detectCloseOrRepay = require("./detectCloseOrRepay.js");
6
9
  const require_preview_preview_replayMulticall = require("./replayMulticall.js");
7
10
  const require_preview_preview_unwrapNativeCollateral = require("./unwrapNativeCollateral.js");
@@ -23,7 +26,11 @@ function previewExitOrRepayStrategyPosition(input, operation, permanent, options
23
26
  function previewCloseCreditAccount(input, operation, permanent, replay) {
24
27
  const { sdk } = input;
25
28
  const market = sdk.marketRegister.findByCreditManager(operation.creditManager);
26
- const { after, error } = replay;
29
+ const { before, after, error: replayError } = replay;
30
+ const account = after.account;
31
+ let error = replayError;
32
+ const priced = market.valueInUnderlying(account.balances.toAssets());
33
+ if (priced.unpriceable) error ??= require_preview_preview_errors.unpriceableTokenError(priced.unpriceable);
27
34
  const suite = sdk.marketRegister.findCreditManager(operation.creditManager);
28
35
  let receivedToken = market.underlying;
29
36
  for (const m of operation.multicall) if (m.operation === "WithdrawCollateral" && m.amount === 115792089237316195423570985008687907853269984665640564039457584007913129639935n) {
@@ -33,7 +40,7 @@ function previewCloseCreditAccount(input, operation, permanent, replay) {
33
40
  return {
34
41
  operation: "CloseCreditAccount",
35
42
  permanent,
36
- ...suite.creditOperationMarket(),
43
+ ...require_model_previews.asEstimated(sdk.positions.projection(account.toSnapshot(priced.value), { availableLiquidityChange: before.totalDebt - account.totalDebt })),
37
44
  creditAccount: operation.creditAccount,
38
45
  name: suite.accountStrategyName(operation.creditAccount),
39
46
  targetCollateral: suite.accountTargetCollateral(operation.creditAccount),
@@ -50,18 +57,21 @@ function previewRepayCreditAccount(input, operation, permanent, replay) {
50
57
  const { sdk, value = 0n } = input;
51
58
  const market = sdk.marketRegister.findByCreditManager(operation.creditManager);
52
59
  const { before, after, error: replayError } = replay;
60
+ const account = after.account;
53
61
  const { assets: collateralAdded, error: unwrapError } = require_preview_preview_unwrapNativeCollateral.unwrapNativeCollateral(after.collateralAdded.toAssets(), value, sdk.addressProvider.getAddress(require_onchain_constants_address_provider.AP_WETH_TOKEN, 0));
54
- const error = replayError ?? unwrapError;
62
+ let error = replayError ?? unwrapError;
63
+ const priced = market.valueInUnderlying(account.balances.toAssets());
64
+ if (priced.unpriceable) error ??= require_preview_preview_errors.unpriceableTokenError(priced.unpriceable);
55
65
  const suite = sdk.marketRegister.findCreditManager(operation.creditManager);
56
66
  return {
57
67
  operation: "RepayCreditAccount",
58
68
  permanent,
59
- ...suite.creditOperationMarket(),
69
+ ...require_model_previews.asEstimated(sdk.positions.projection(account.toSnapshot(priced.value), { availableLiquidityChange: before.totalDebt - account.totalDebt })),
60
70
  creditAccount: operation.creditAccount,
61
71
  name: suite.accountStrategyName(operation.creditAccount),
62
72
  targetCollateral: suite.accountTargetCollateral(operation.creditAccount),
63
73
  collateralAdded: collateralAdded.map((a) => market.priceOracle.toTokenAmount(a.token, a.balance)),
64
- debtRepaid: market.toUnderlyingAmount(before.totalDebt - after.account.totalDebt),
74
+ debtRepaid: market.toUnderlyingAmount(before.totalDebt - account.totalDebt),
65
75
  collateralWithdrawn: after.collateralWithdrawn.toAssets().map((a) => market.priceOracle.toTokenAmount(a.token, a.balance)),
66
76
  error
67
77
  };
@@ -51,6 +51,12 @@ var PositionsNamespace = class extends require_sdk_AbstractNamespace.AbstractNam
51
51
  return this.offchain.getCharts(key, metrics, range);
52
52
  }
53
53
  /**
54
+ * {@inheritDoc IPositionsOffchainOnly.transactions}
55
+ **/
56
+ async transactions(key) {
57
+ return this.offchain.getTransactions(key);
58
+ }
59
+ /**
54
60
  * {@inheritDoc IPositionsOnchainOnly.getCurrentWithdrawals}
55
61
  **/
56
62
  async getCurrentWithdrawals(props) {
@@ -207,7 +207,10 @@ const positionTransactionKindSchema = z.union([
207
207
  z.literal("adjustLeverage"),
208
208
  z.literal("addCollateral"),
209
209
  z.literal("withdrawCollateral"),
210
- z.literal("liquidation")
210
+ z.literal("liquidation"),
211
+ z.literal("repay"),
212
+ z.literal("rebalance"),
213
+ z.literal("other")
211
214
  ]);
212
215
  /**
213
216
  * {@link PositionTransaction}
@@ -216,7 +219,9 @@ const positionTransactionSchema = z.object({
216
219
  txHash: ZodHex(),
217
220
  timestamp: timestampSchema,
218
221
  kind: positionTransactionKindSchema,
219
- assets: z.array(tokenAmountSchema)
222
+ assets: z.array(tokenAmountSchema),
223
+ balanceChanges: z.array(tokenAmountSchema),
224
+ debtChange: tokenAmountSchema
220
225
  });
221
226
  //#endregion
222
227
  export { borrowRateBreakdownSchema, pnlBreakdownSchema, pointsProgramPnLSchema, pointsRewardsPnLSchema, poolPositionKeySchema, poolPositionSchema, positionCollateralSchema, positionFilterQueryParamsSchema, positionFilterQuerySchema, positionFilterSchema, positionKeySchema, positionKindSchema, positionSchema, positionTransactionKindSchema, positionTransactionSchema, positionsTotalsSchema, rewardsPnLSchema, strategyPositionKeySchema, strategyPositionSchema, tokenQuotaRateSchema, tokenRewardsPnLSchema };
@@ -1,4 +1,4 @@
1
- import { positionFilterQuerySchema, positionSchema, positionsTotalsSchema } from "../../model/positions.schema.js";
1
+ import { positionFilterQuerySchema, positionSchema, positionTransactionSchema, positionsTotalsSchema } from "../../model/positions.schema.js";
2
2
  import { AbstractOffchainNamespace } from "../AbstractOffchainNamespace.js";
3
3
  import { z } from "zod/v4";
4
4
  //#region src/offchain/positions/OffchainPositions.ts
@@ -38,6 +38,15 @@ var OffchainPositions = class extends AbstractOffchainNamespace {
38
38
  async getCharts(key, metrics, range) {
39
39
  return this.readCharts(`${this.#chartRoot(key)}/charts`, metrics, range);
40
40
  }
41
+ /**
42
+ * {@inheritDoc IOffchainPositions.getTransactions}
43
+ **/
44
+ async getTransactions(key) {
45
+ return this.get({
46
+ path: `${this.#strategyPath(key)}/transactions`,
47
+ schema: z.array(positionTransactionSchema)
48
+ });
49
+ }
41
50
  #poolPath(key) {
42
51
  return `${this.#root}/pool/${key.chainId}/${key.pool}/${key.wallet}`;
43
52
  }
@@ -2,6 +2,7 @@ import "../../../constants/math.js";
2
2
  import "../../../constants/index.js";
3
3
  import { calcMaxLeverage, usdToNumber } from "../../../market/math.js";
4
4
  import { CreditSuite } from "../../../market/credit/CreditSuite.js";
5
+ import { MarketSuite } from "../../../market/MarketSuite.js";
5
6
  import { PositionsService } from "../../../positions/PositionsService.js";
6
7
  import { vi } from "vitest";
7
8
  //#region src/onchain/accounts/intents/testing/sdk-mock.ts
@@ -221,6 +222,7 @@ function buildMockSdk(args) {
221
222
  underlying: args.underlying
222
223
  }
223
224
  };
225
+ Object.assign(market, { valueInUnderlying: MarketSuite.prototype.valueInUnderlying });
224
226
  const collateralTokens = [args.underlying.toLowerCase(), ...Object.keys(args.liquidationThresholds).map((t) => t.toLowerCase()).filter((t) => t !== args.underlying.toLowerCase())];
225
227
  const forbidden = new Set((args.forbiddenTokens ?? []).map((t) => t.toLowerCase()));
226
228
  const forbiddenTokensMask = collateralTokens.reduce((mask, token, i) => forbidden.has(token) ? mask | 1n << BigInt(i) : mask, 0n);
@@ -1,5 +1,7 @@
1
1
  import { AddressMap } from "../utils/AddressMap.js";
2
2
  import { isRWAToken, isSunsetPool } from "../chain/chains.js";
3
+ import { DUST_THRESHOLD } from "../constants/math.js";
4
+ import "../constants/index.js";
3
5
  import "../utils/index.js";
4
6
  import { SDKConstruct } from "../base/SDKConstruct.js";
5
7
  import "../base/index.js";
@@ -150,6 +152,32 @@ var MarketSuite = class extends SDKConstruct {
150
152
  ...this.priceOracle.toAmount(this.underlying, value)
151
153
  });
152
154
  /**
155
+ * Sums `assets` in this market's underlying at latest oracle prices.
156
+ *
157
+ * Balances at or below `minBalance` are ignored. A token the oracle cannot
158
+ * price contributes nothing; the first such token is {@link ValueInUnderlying.unpriceable}.
159
+ *
160
+ * The counterpart of {@link toUnderlyingAmount}: that method labels a figure
161
+ * already in underlying, this one produces the figure from mixed holdings.
162
+ **/
163
+ valueInUnderlying(assets, minBalance = DUST_THRESHOLD) {
164
+ let unpriceable;
165
+ let value = 0n;
166
+ for (const { token, balance } of assets) {
167
+ if (balance <= minBalance) continue;
168
+ const converted = this.priceOracle.safeConvert(token, this.underlying, balance);
169
+ if (converted === null) {
170
+ unpriceable ??= token;
171
+ continue;
172
+ }
173
+ value += converted;
174
+ }
175
+ return unpriceable === void 0 ? { value } : {
176
+ value,
177
+ unpriceable
178
+ };
179
+ }
180
+ /**
153
181
  * Display name of this market's pool, e.g. `"USDC Pool"`.
154
182
  */
155
183
  get poolName() {
@@ -293,12 +293,14 @@ var PositionsService = class extends SDKConstruct {
293
293
  withdrawals: withdrawals.get(t.token) ?? []
294
294
  });
295
295
  if (recomputeTotals) {
296
- const value = priceOracle.safeConvert(t.token, market.underlying, t.balance) || 0n;
297
- totalValue += value;
298
296
  const usd = priceOracle.safeConvertToUSD(t.token, t.balance) || 0n;
299
297
  totalValueUSD += usd;
300
298
  }
301
299
  }
300
+ if (recomputeTotals) totalValue = market.valueInUnderlying(ca.tokens.map((t) => ({
301
+ token: t.token,
302
+ balance: t.balance
303
+ }))).value;
302
304
  const snapshot = {
303
305
  ...accountSnapshotFromCreditAccountData(ca),
304
306
  totalValue
@@ -17,6 +17,7 @@ import { BalancePrerequisite } from "./prerequisites/BalancePrerequisite.js";
17
17
  import { RWAOpenRequirementsPrerequisite } from "./prerequisites/RWAOpenRequirementsPrerequisite.js";
18
18
  import { checkPrerequisites } from "./prerequisites/checkPrerequisites.js";
19
19
  import "./prerequisites/index.js";
20
+ import { unpriceableTokenError } from "./preview/errors.js";
20
21
  import { buildDelayedStrategyPositionOperationPreview } from "./preview/buildDelayedStrategyPositionOperationPreview.js";
21
22
  import { CreditAccountState } from "./preview/CreditAccountState.js";
22
23
  import { classifyCloseOrRepay, isCloseOrRepay } from "./preview/detectCloseOrRepay.js";
@@ -34,4 +35,4 @@ import "./types.js";
34
35
  import { checkOperation, collateralIssue, marketIssues, quotaCountIssue } from "./validate/checkOperation.js";
35
36
  import { checkSimulation } from "./validate/checkSimulation.js";
36
37
  import "./validate/index.js";
37
- export { AllowancePrerequisite, BalancePrerequisite, CreditAccountState, Prerequisite, RWAOpenRequirementsPrerequisite, TransferAlignmentError, UnexpectedFacadeEventOrderError, UnknownAdapterError, UnknownFacadeCallError, WithdrawCollateralAlignmentError, asPreviewSimulationError, buildDelayedStrategyPositionOperationPreview, checkOperation, checkPrerequisites, checkSimulation, classifyCloseOrRepay, classifyInnerOperations, collateralIssue, detectDelayedClaim, detectDelayedOperation, estimateClaimableAt, extractAdapterCallTraces, extractTransfers, findFacadeCalls, isCloseOrRepay, isPoolOperation, isRWAOperation, makeReplayState, marketIssues, parseFacadeOperationCalldata, parseOperationCalldata, parsePoolOperationCalldata, parseRWAFactoryOperationCalldata, previewAdjustStrategyPosition, previewExitOrRepayStrategyPosition, previewOperation, quotaCountIssue, raise, refuse, replayInnerOperations, replayMulticall, resolveDelayedClaimIntent };
38
+ export { AllowancePrerequisite, BalancePrerequisite, CreditAccountState, Prerequisite, RWAOpenRequirementsPrerequisite, TransferAlignmentError, UnexpectedFacadeEventOrderError, UnknownAdapterError, UnknownFacadeCallError, WithdrawCollateralAlignmentError, asPreviewSimulationError, buildDelayedStrategyPositionOperationPreview, checkOperation, checkPrerequisites, checkSimulation, classifyCloseOrRepay, classifyInnerOperations, collateralIssue, detectDelayedClaim, detectDelayedOperation, estimateClaimableAt, extractAdapterCallTraces, extractTransfers, findFacadeCalls, isCloseOrRepay, isPoolOperation, isRWAOperation, makeReplayState, marketIssues, parseFacadeOperationCalldata, parseOperationCalldata, parsePoolOperationCalldata, parseRWAFactoryOperationCalldata, previewAdjustStrategyPosition, previewExitOrRepayStrategyPosition, previewOperation, quotaCountIssue, raise, refuse, replayInnerOperations, replayMulticall, resolveDelayedClaimIntent, unpriceableTokenError };
@@ -1,9 +1,10 @@
1
1
  import { AssetsMap } from "../../onchain/utils/AssetsMap.js";
2
2
  import { BigIntMath } from "../../onchain/utils/bigint-math.js";
3
3
  import { DUST_THRESHOLD } from "../../onchain/constants/math.js";
4
- import { ERROR_UNPRICEABLE_TOKEN, asEstimated } from "../../model/previews.js";
4
+ import { asEstimated } from "../../model/previews.js";
5
5
  import "../../model/index.js";
6
6
  import "../../onchain/index.js";
7
+ import { unpriceableTokenError } from "./errors.js";
7
8
  import { isAddressEqual } from "viem";
8
9
  //#region src/preview/preview/buildDelayedStrategyPositionOperationPreview.ts
9
10
  /**
@@ -12,8 +13,9 @@ import { isAddressEqual } from "viem";
12
13
  * the claim itself followed by the intent-specific tail
13
14
  *
14
15
  * Pure function: the input states are never mutated and no network access is performed.
15
- * Swaps are estimated with the injected conversion; tokens it cannot price contribute
16
- * nothing and set a non-fatal `ERROR_UNPRICEABLE_TOKEN` error on the
16
+ * Swaps are estimated with the injected conversion; remaining holdings are
17
+ * priced by `MarketSuite.valueInUnderlying`. Tokens that cannot be priced
18
+ * contribute nothing and set a non-fatal `ERROR_UNPRICEABLE_TOKEN` error on the
17
19
  * preview.
18
20
  *
19
21
  * The changes (e.g. `totalDebtChange`) are reported relative to the account
@@ -50,10 +52,7 @@ function makeSafeConverter(convert) {
50
52
  try {
51
53
  return convert(token, to, amount);
52
54
  } catch {
53
- error ??= {
54
- code: ERROR_UNPRICEABLE_TOKEN,
55
- message: `cannot price token ${token}`
56
- };
55
+ error ??= unpriceableTokenError(token);
57
56
  return 0n;
58
57
  }
59
58
  },
@@ -148,35 +147,36 @@ function repayFromClaim(post, claimToken, convert, amount) {
148
147
  post.repay(received);
149
148
  }
150
149
  /**
151
- * Oracle estimate of the account's total value in the underlying, ignoring
152
- * balances at or below `dust`.
153
- */
154
- function totalValueInUnderlying(post, convert, dust) {
155
- return post.balances.sum((token, balance) => balance > dust ? convert(token, post.underlying, balance) : 0n);
156
- }
157
- /**
158
150
  * `CLOSE_ACCOUNT` operation tail: everything is swapped into the
159
151
  * underlying, the debt is repaid in full and the remainder is withdrawn to
160
152
  * the user as `receivedToken`.
161
153
  */
162
154
  function buildClosePreview(post, converter, receivedToken, sdk) {
163
- const totalValue = totalValueInUnderlying(post, converter.convert, 0n);
164
- const oracle = sdk.marketRegister.findByCreditManager(post.creditManager).priceOracle;
155
+ const market = sdk.marketRegister.findByCreditManager(post.creditManager);
156
+ const priced = market.valueInUnderlying(post.balances.toAssets(), 0n);
157
+ const oracle = market.priceOracle;
165
158
  const suite = sdk.marketRegister.findCreditManager(post.creditManager);
166
159
  return {
167
160
  operation: "CloseCreditAccount",
168
161
  permanent: false,
169
- ...suite.creditOperationMarket(),
162
+ ...asEstimated(sdk.positions.projection({
163
+ creditManager: post.creditManager,
164
+ assets: [],
165
+ quotas: [],
166
+ totalDebt: 0n,
167
+ totalValue: 0n
168
+ }, { availableLiquidityChange: post.totalDebt })),
170
169
  creditAccount: post.creditAccount,
171
170
  name: suite.accountStrategyName(post.creditAccount),
172
171
  targetCollateral: suite.accountTargetCollateral(post.creditAccount),
173
- receivedAmount: oracle.toTokenAmount(receivedToken, BigIntMath.max(totalValue - post.totalDebt, 0n)),
174
- error: converter.error
172
+ receivedAmount: oracle.toTokenAmount(receivedToken, BigIntMath.max(priced.value - post.totalDebt, 0n)),
173
+ error: converter.error ?? (priced.unpriceable ? unpriceableTokenError(priced.unpriceable) : void 0)
175
174
  };
176
175
  }
177
176
  function buildAdjustPreview(post, before, collateralWithdrawn, converter, sdk) {
178
- const snap = post.toSnapshot(totalValueInUnderlying(post, converter.convert, DUST_THRESHOLD));
179
177
  const market = sdk.marketRegister.findByCreditManager(post.creditManager);
178
+ const priced = market.valueInUnderlying(post.balances.toAssets());
179
+ const snap = post.toSnapshot(priced.value);
180
180
  const suite = sdk.marketRegister.findCreditManager(post.creditManager);
181
181
  const oracle = market.priceOracle;
182
182
  return {
@@ -193,7 +193,7 @@ function buildAdjustPreview(post, before, collateralWithdrawn, converter, sdk) {
193
193
  ...oracle.toAmount(market.underlying, q.balance)
194
194
  })),
195
195
  assetsChange: post.balances.difference(before.balances).toAssets(DUST_THRESHOLD).map((a) => oracle.toTokenAmount(a.token, a.balance)),
196
- error: converter.error
196
+ error: converter.error ?? (priced.unpriceable ? unpriceableTokenError(priced.unpriceable) : void 0)
197
197
  };
198
198
  }
199
199
  //#endregion
@@ -1 +1,16 @@
1
- export {};
1
+ import { ERROR_UNPRICEABLE_TOKEN } from "../../model/previews.js";
2
+ import "../../model/index.js";
3
+ //#region src/preview/preview/errors.ts
4
+ /**
5
+ * Preview limitation (2xxx): the oracle could not price `token`. Callers
6
+ * attach this with `error ??=` so a malformed-transaction (1xxx) error
7
+ * already recorded keeps precedence.
8
+ **/
9
+ function unpriceableTokenError(token) {
10
+ return {
11
+ code: ERROR_UNPRICEABLE_TOKEN,
12
+ message: `cannot price token ${token}`
13
+ };
14
+ }
15
+ //#endregion
16
+ export { unpriceableTokenError };
@@ -1,13 +1,13 @@
1
+ import { unpriceableTokenError } from "./errors.js";
1
2
  import { buildDelayedStrategyPositionOperationPreview } from "./buildDelayedStrategyPositionOperationPreview.js";
2
3
  import { CreditAccountState } from "./CreditAccountState.js";
3
4
  import { classifyCloseOrRepay, isCloseOrRepay } from "./detectCloseOrRepay.js";
4
5
  import { detectDelayedClaim, resolveDelayedClaimIntent } from "./detectDelayedClaim.js";
5
6
  import { detectDelayedOperation } from "./detectDelayedOperation.js";
6
- import "./errors.js";
7
7
  import { estimateClaimableAt } from "./estimateClaimableAt.js";
8
8
  import { makeReplayState, replayInnerOperations } from "./replayInnerOperations.js";
9
9
  import { replayMulticall } from "./replayMulticall.js";
10
10
  import { previewAdjustStrategyPosition } from "./previewAdjustStrategyPosition.js";
11
11
  import { previewExitOrRepayStrategyPosition } from "./previewExitOrRepayStrategyPosition.js";
12
12
  import { previewOperation } from "./previewOperation.js";
13
- 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, previewAdjustStrategyPosition, previewExitOrRepayStrategyPosition, previewOperation, replayInnerOperations, replayMulticall, resolveDelayedClaimIntent, unpriceableTokenError };