@gearbox-protocol/sdk 16.0.0-next.15 → 16.0.0-next.17

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 (43) hide show
  1. package/dist/cjs/onchain/accounts/intents/open-strategy.js +12 -3
  2. package/dist/cjs/onchain/accounts/intents/realize.js +11 -0
  3. package/dist/cjs/onchain/accounts/intents/testing/market.js +1 -0
  4. package/dist/cjs/onchain/accounts/intents/testing/sdk-mock.js +6 -4
  5. package/dist/cjs/onchain/accounts/intents/tests/deposit.fixtures.js +5 -2
  6. package/dist/cjs/onchain/accounts/intents/utils/index.js +4 -0
  7. package/dist/cjs/onchain/accounts/intents/utils/price-impact.js +99 -0
  8. package/dist/cjs/onchain/accounts/intents/utils/router-path.js +98 -40
  9. package/dist/cjs/rewards/index.js +1 -1
  10. package/dist/cjs/rewards/rewards/api.js +67 -57
  11. package/dist/cjs/rewards/rewards/index.js +1 -1
  12. package/dist/esm/onchain/accounts/intents/open-strategy.js +12 -3
  13. package/dist/esm/onchain/accounts/intents/realize.js +11 -0
  14. package/dist/esm/onchain/accounts/intents/testing/market.js +1 -0
  15. package/dist/esm/onchain/accounts/intents/testing/sdk-mock.js +6 -4
  16. package/dist/esm/onchain/accounts/intents/tests/deposit.fixtures.js +5 -2
  17. package/dist/esm/onchain/accounts/intents/utils/index.js +2 -1
  18. package/dist/esm/onchain/accounts/intents/utils/price-impact.js +96 -0
  19. package/dist/esm/onchain/accounts/intents/utils/router-path.js +98 -40
  20. package/dist/esm/rewards/index.js +2 -2
  21. package/dist/esm/rewards/rewards/api.js +68 -58
  22. package/dist/esm/rewards/rewards/index.js +2 -2
  23. package/dist/types/onchain/accounts/index.d.ts +4 -4
  24. package/dist/types/onchain/accounts/intents/index.d.ts +4 -4
  25. package/dist/types/onchain/accounts/intents/open-strategy.d.ts +3 -0
  26. package/dist/types/onchain/accounts/intents/realize.d.ts +1 -1
  27. package/dist/types/onchain/accounts/intents/tail.d.ts +1 -1
  28. package/dist/types/onchain/accounts/intents/testing/expect.d.ts +1 -1
  29. package/dist/types/onchain/accounts/intents/testing/market.d.ts +2 -0
  30. package/dist/types/onchain/accounts/intents/testing/sdk-mock.d.ts +7 -0
  31. package/dist/types/onchain/accounts/intents/tests/deposit.fixtures.d.ts +1 -1
  32. package/dist/types/onchain/accounts/intents/types.d.ts +18 -2
  33. package/dist/types/onchain/accounts/intents/utils/index.d.ts +2 -1
  34. package/dist/types/onchain/accounts/intents/utils/price-impact.d.ts +49 -0
  35. package/dist/types/onchain/accounts/intents/utils/router-path.d.ts +9 -0
  36. package/dist/types/onchain/index.d.ts +4 -4
  37. package/dist/types/rewards/index.d.ts +2 -2
  38. package/dist/types/rewards/rewards/api.d.ts +44 -29
  39. package/dist/types/rewards/rewards/index.d.ts +2 -2
  40. package/dist/types/sdk/index.d.ts +2 -2
  41. package/dist/types/sdk/prepare/index.d.ts +2 -2
  42. package/dist/types/sdk/prepare/types.d.ts +4 -4
  43. package/package.json +1 -1
@@ -5,6 +5,7 @@ import { convertAmount } from "./utils/convert-amount.js";
5
5
  import { isRedemptionPhantomToken } from "./utils/pick-token.js";
6
6
  import { assertCanBorrow, assertCollateralised, assertGrowthAllowed, assertQuotaHeadroom } from "./guards.js";
7
7
  import { OperationLedger } from "./utils/ledger.js";
8
+ import { collectPriceImpact } from "./utils/price-impact.js";
8
9
  import { clearedQuotas, getQuotasForUpdate, quotasAfterUpdate } from "./utils/quotas-for-update.js";
9
10
  import { createRouterPaths } from "./utils/router-path.js";
10
11
  import { buildAddCollateralOperation, buildClaimDelayedWithdrawalOperation, buildCloseSwapOperation, buildDecreaseDebtOperation, buildIncreaseDebtOperation, buildQuotaUpdateOperation, buildStartDelayedWithdrawalOperation, buildSwapOperation, buildUnwrapRwaCollateralOperation, buildWithdrawCollateralOperation, buildWrapRwaCollateralOperation, instantOutput } from "./operations.js";
@@ -42,6 +43,8 @@ async function realize(steps, props) {
42
43
  operations.push(op);
43
44
  ledger.apply(op);
44
45
  };
46
+ /** One per routed leg, each already awaiting its quote; folded after the guards. */
47
+ const probes = [];
45
48
  /** Output of the last convert or claim, for `RAISED` amounts. */
46
49
  let raised = 0n;
47
50
  /** The request, before the walk's end state can be attached to it. */
@@ -139,6 +142,7 @@ async function realize(steps, props) {
139
142
  amount,
140
143
  keep: held - amount
141
144
  });
145
+ if (leg.probe) probes.push(leg.probe);
142
146
  push(buildSwapOperation({
143
147
  tokenIn: step.from,
144
148
  amountIn: amount,
@@ -155,6 +159,7 @@ async function realize(steps, props) {
155
159
  if (pending) throw new IntentPreviewError("withdrawalInProgress", { inFlight: pending }, `closeAll: ${pending.token} is a pending withdrawal, claim it first`);
156
160
  if (balances.length > 0) {
157
161
  const leg = await paths.closeAll({ balances });
162
+ if (leg.probe) probes.push(leg.probe);
158
163
  if (leg.calls.length > 0 || leg.minAmount > 0n) push(buildCloseSwapOperation({
159
164
  from: balances,
160
165
  tokenOut: underlying,
@@ -283,12 +288,18 @@ async function realize(steps, props) {
283
288
  liquidationPrice: sdk.positions.liquidationPrice(snapshot)
284
289
  };
285
290
  assertCollateralised(paysOut ? sdk.positions.healthFactor(snapshot, { safePrices: true }) : metrics.healthFactor, paysOut);
291
+ const priceImpact = await collectPriceImpact(probes, {
292
+ totalValue,
293
+ netValue: totalValue - debt,
294
+ toUnderlying: (from, amount) => price(from, underlying, amount)
295
+ });
286
296
  const state = {
287
297
  totalValue,
288
298
  accountDebt: debt,
289
299
  leverage: calcPositionLeverage(totalValue, debt),
290
300
  assets: assets.map((a) => market.priceOracle.toTokenAmount(a.token, a.balance)),
291
301
  quotas: quotasAfter,
302
+ priceImpact,
292
303
  ...metrics
293
304
  };
294
305
  return {
@@ -95,6 +95,7 @@ function buildMarketSdk(extras) {
95
95
  creditManager: CREDIT_MANAGER,
96
96
  creditFacade: CREDIT_FACADE,
97
97
  underlying: UND,
98
+ routeQuote: extras?.routeQuote,
98
99
  rwaAssets: extras?.rwaAssets,
99
100
  phantoms: extras?.phantoms,
100
101
  creditAccounts: extras?.creditAccounts,
@@ -209,18 +209,20 @@ function buildMockSdk(args) {
209
209
  if (asset && from === underlying && to === asset) return [MOCK_RWA_UNWRAP_CALL];
210
210
  return [MOCK_ROUTER_CALL];
211
211
  };
212
+ /** Linear unless the case says otherwise — see `routeQuote`. */
213
+ const quote = args.routeQuote ?? ((amount) => amount);
212
214
  const router = {
213
215
  findOneTokenPath: vi.fn(async ({ amount, tokenIn, tokenOut }) => ({
214
- amount,
215
- minAmount: amount,
216
+ amount: quote(amount),
217
+ minAmount: quote(amount),
216
218
  calls: routeCalls(tokenIn, tokenOut)
217
219
  })),
218
220
  findManyToOnePath: vi.fn(async ({ expectedBalances, leftoverBalances, target }) => {
219
221
  const spent = expectedBalances.reduce((acc, a) => acc + a.balance, 0n) - leftoverBalances.reduce((acc, a) => acc + a.balance, 0n);
220
222
  const tokenIn = expectedBalances[0]?.token ?? target;
221
223
  return {
222
- amount: spent,
223
- minAmount: spent,
224
+ amount: quote(spent),
225
+ minAmount: quote(spent),
224
226
  calls: routeCalls(tokenIn, target)
225
227
  };
226
228
  }),
@@ -283,8 +283,11 @@ const case_native_coin = {
283
283
  }
284
284
  ]
285
285
  };
286
- function buildDepositSdk(c) {
287
- return buildMarketSdk({ rwaAssets: c.rwaAssets });
286
+ function buildDepositSdk(c, routeQuote) {
287
+ return buildMarketSdk({
288
+ rwaAssets: c.rwaAssets,
289
+ routeQuote
290
+ });
288
291
  }
289
292
  function buildDepositProps(c, sdk) {
290
293
  return {
@@ -6,6 +6,7 @@ import { assembleOperationCalls } from "./assemble-operation-calls.js";
6
6
  import { calcBorrowedAmountPlusInterestAndFees } from "./borrowed-amount-plus-interest-and-fees.js";
7
7
  import { fetchCreditAccountSlice, toCreditAccountSlice } from "./credit-account-slice.js";
8
8
  import { OperationLedger } from "./ledger.js";
9
+ import { collectPriceImpact, lossRate, startProbe } from "./price-impact.js";
9
10
  import { clearedQuotas, getQuotasForUpdate, quotasAfterUpdate } from "./quotas-for-update.js";
10
11
  import { createOraclePaths, createRouterPaths } from "./router-path.js";
11
- export { OperationLedger, adjustStateToSnapshot, assembleOperationCalls, calcBorrowedAmountPlusInterestAndFees, clearedQuotas, convertAmount, createOraclePaths, createRouterPaths, eq, fetchCreditAccountSlice, getQuotasForUpdate, isPhantomToken, isRedemptionPhantomToken, pickFattestNonPhantomToken, quotasAfterUpdate, rankAccountTokens, toCreditAccountSlice, toRouterCaSlice, toTargetDecimals };
12
+ export { OperationLedger, adjustStateToSnapshot, assembleOperationCalls, calcBorrowedAmountPlusInterestAndFees, clearedQuotas, collectPriceImpact, convertAmount, createOraclePaths, createRouterPaths, eq, fetchCreditAccountSlice, getQuotasForUpdate, isPhantomToken, isRedemptionPhantomToken, lossRate, pickFattestNonPhantomToken, quotasAfterUpdate, rankAccountTokens, startProbe, toCreditAccountSlice, toRouterCaSlice, toTargetDecimals };
@@ -0,0 +1,96 @@
1
+ import { PERCENTAGE_FACTOR_1KK, PRICE_DECIMALS, WAD } from "../../../constants/math.js";
2
+ //#region src/onchain/accounts/intents/utils/price-impact.ts
3
+ /** `b1 = b0 / V0`: a dollar of the basket, the reference implementation's anchor. */
4
+ const PROBE_UNIT_USD_WAD = WAD;
5
+ /**
6
+ * `V0 = Σ b0ᵢ·pᵢ`, then `b1 = b0 / V0`, proportions kept.
7
+ *
8
+ * Refuses only what the reference refuses — a basket worth nothing, or one that
9
+ * rounds away entirely. Stricter guards here would report nothing where the old
10
+ * client reported a number.
11
+ */
12
+ function probeBasket(balances, oracle) {
13
+ if (balances.length === 0) return;
14
+ let basketWad = 0n;
15
+ for (const asset of balances) {
16
+ if (asset.balance <= 0n) continue;
17
+ const usd = oracle.safeConvertToUSD(asset.token, asset.balance);
18
+ if (usd !== null && usd > 0n) basketWad += usd * WAD / PRICE_DECIMALS;
19
+ }
20
+ if (basketWad <= 0n) return;
21
+ const probeWad = PROBE_UNIT_USD_WAD;
22
+ const scaled = balances.map((asset) => ({
23
+ token: asset.token,
24
+ balance: asset.balance * probeWad / basketWad
25
+ }));
26
+ if (!scaled.some((a) => a.balance > 0n)) return;
27
+ return {
28
+ balances: scaled,
29
+ basketWad,
30
+ probeWad
31
+ };
32
+ }
33
+ /** Fires the marginal-price quote for one leg; `undefined` if it cannot be measured. */
34
+ function startProbe(args) {
35
+ const basket = probeBasket(args.basket, args.oracle);
36
+ if (!basket) return;
37
+ return {
38
+ tokenOut: args.tokenOut,
39
+ basketWad: basket.basketWad,
40
+ probeWad: basket.probeWad,
41
+ probe: args.route(basket.balances).catch(() => void 0)
42
+ };
43
+ }
44
+ /** `convert` answers `0` for a negative amount, so convert the magnitude and re-sign. */
45
+ function toUnderlyingSigned(convert, token, amount) {
46
+ if (amount === 0n) return 0n;
47
+ const converted = convert(token, amount < 0n ? -amount : amount);
48
+ if (converted <= 0n) return;
49
+ return amount < 0n ? -converted : converted;
50
+ }
51
+ /**
52
+ * In `PERCENTAGE_FACTOR_1KK` (1_000_000 = 100%), negative for a loss. A base
53
+ * that is not positive falls back to the routed output.
54
+ */
55
+ function lossRate(args) {
56
+ const { lossUnd, expectedUnd, totalValue, netValue } = args;
57
+ const against = (base) => -(PERCENTAGE_FACTOR_1KK * lossUnd / (base > 0n ? base : expectedUnd));
58
+ return {
59
+ pathPriceImpact: against(expectedUnd),
60
+ netValuePriceImpact: against(netValue),
61
+ totalValuePriceImpact: against(totalValue)
62
+ };
63
+ }
64
+ /**
65
+ * Folds every leg into one rate, in the underlying — the unit its bases are in.
66
+ *
67
+ * All or nothing: a partial sum would understate the loss and draw a better
68
+ * price than the route offers.
69
+ */
70
+ async function collectPriceImpact(probes, ctx) {
71
+ if (probes.length === 0) return;
72
+ const quotes = await Promise.all(probes.map((leg) => leg.probe));
73
+ let expectedUnd = 0n;
74
+ let lossUnd = 0n;
75
+ for (const [index, leg] of probes.entries()) {
76
+ const unit = quotes[index];
77
+ if (unit === void 0 || unit <= 0n) return;
78
+ const expected = unit * leg.basketWad / leg.probeWad;
79
+ if (expected <= 0n) return;
80
+ const expectedInUnd = ctx.toUnderlying(leg.tokenOut, expected);
81
+ if (expectedInUnd <= 0n) return;
82
+ const loss = toUnderlyingSigned(ctx.toUnderlying, leg.tokenOut, expected - leg.realAmount);
83
+ if (loss === void 0) return;
84
+ expectedUnd += expectedInUnd;
85
+ lossUnd += loss;
86
+ }
87
+ if (expectedUnd <= 0n) return;
88
+ return lossRate({
89
+ lossUnd,
90
+ expectedUnd,
91
+ totalValue: ctx.totalValue,
92
+ netValue: ctx.netValue
93
+ });
94
+ }
95
+ //#endregion
96
+ export { collectPriceImpact, lossRate, startProbe };
@@ -1,5 +1,6 @@
1
1
  import { toRouterCaSlice } from "./common.js";
2
2
  import { convertAmount } from "./convert-amount.js";
3
+ import { startProbe } from "./price-impact.js";
3
4
  //#region src/onchain/accounts/intents/utils/router-path.ts
4
5
  /**
5
6
  * The engine's only door to the pathfinder.
@@ -17,68 +18,124 @@ function createRouterPaths(args) {
17
18
  collateralTokens: suite.creditManager.collateralTokens.map((t) => t.toLowerCase())
18
19
  };
19
20
  const router = sdk.routerFor({ creditFacade: suite.creditFacade.address });
21
+ const { priceOracle } = sdk.marketRegister.findByCreditManager(creditAccount.creditManager);
22
+ const quoteSwap = (input) => {
23
+ const spending = [{
24
+ token: input.tokenIn,
25
+ balance: input.amount + input.keep
26
+ }];
27
+ return input.keep > 0n ? router.findManyToOnePath({
28
+ creditAccount: toRouterCaSlice(creditAccount, spending),
29
+ creditManager: cmSlice,
30
+ expectedBalances: spending,
31
+ leftoverBalances: [{
32
+ token: input.tokenIn,
33
+ balance: input.keep
34
+ }],
35
+ target: input.tokenOut,
36
+ slippage
37
+ }) : router.findOneTokenPath({
38
+ creditAccount: toRouterCaSlice(creditAccount, spending),
39
+ creditManager: cmSlice,
40
+ tokenIn: input.tokenIn,
41
+ tokenOut: input.tokenOut,
42
+ amount: input.amount,
43
+ slippage
44
+ });
45
+ };
46
+ const quoteClose = (balances) => router.findBestClosePath({
47
+ creditAccount: toRouterCaSlice(creditAccount, balances),
48
+ creditManager: cmSlice,
49
+ balances: {
50
+ expectedBalances: balances,
51
+ leftoverBalances: [],
52
+ tokensToClaim: []
53
+ },
54
+ slippage
55
+ });
56
+ const quoteOpen = (expectedBalances, leftoverBalances, target) => router.findOpenStrategyPath({
57
+ creditManager: cmSlice,
58
+ expectedBalances,
59
+ leftoverBalances,
60
+ target,
61
+ slippage
62
+ });
20
63
  return {
21
64
  async swap({ tokenIn, tokenOut, amount, keep = 0n }) {
22
65
  if (amount <= 0n) return {
23
66
  amount: 0n,
24
67
  minAmount: 0n,
25
- calls: []
68
+ calls: [],
69
+ probe: void 0
26
70
  };
27
71
  if (keep < 0n) throw new Error(`swap: spending ${amount} of ${tokenIn} exceeds its balance`);
28
- if (keep > 0n) {
29
- const expectedBalances = [{
30
- token: tokenIn,
31
- balance: amount + keep
32
- }];
33
- return router.findManyToOnePath({
34
- creditAccount: toRouterCaSlice(creditAccount, expectedBalances),
35
- creditManager: cmSlice,
36
- expectedBalances,
37
- leftoverBalances: [{
38
- token: tokenIn,
39
- balance: keep
40
- }],
41
- target: tokenOut,
42
- slippage
43
- });
44
- }
45
- return router.findOneTokenPath({
46
- creditAccount: toRouterCaSlice(creditAccount, [{
72
+ const probe = startProbe({
73
+ basket: [{
47
74
  token: tokenIn,
48
75
  balance: amount
49
- }]),
50
- creditManager: cmSlice,
76
+ }],
77
+ tokenOut,
78
+ oracle: priceOracle,
79
+ route: async ([only]) => {
80
+ if (!only) return 0n;
81
+ return (await quoteSwap({
82
+ tokenIn: only.token,
83
+ tokenOut,
84
+ amount: only.balance,
85
+ keep: 0n
86
+ })).amount;
87
+ }
88
+ });
89
+ const leg = await quoteSwap({
51
90
  tokenIn,
52
91
  tokenOut,
53
92
  amount,
54
- slippage
93
+ keep
55
94
  });
95
+ return {
96
+ ...leg,
97
+ probe: probe && {
98
+ ...probe,
99
+ realAmount: leg.amount
100
+ }
101
+ };
56
102
  },
57
103
  async closeAll({ balances }) {
58
- const { amount, minAmount, calls } = await router.findBestClosePath({
59
- creditAccount: toRouterCaSlice(creditAccount, balances),
60
- creditManager: cmSlice,
61
- balances: {
62
- expectedBalances: balances,
63
- leftoverBalances: [],
64
- tokensToClaim: []
65
- },
66
- slippage
104
+ const probe = startProbe({
105
+ basket: balances,
106
+ tokenOut: creditAccount.underlying,
107
+ oracle: priceOracle,
108
+ route: async (quoted) => (await quoteClose(quoted)).amount
67
109
  });
68
- return {
110
+ const { amount, minAmount, calls } = await quoteClose(balances);
111
+ const leg = {
69
112
  amount,
70
113
  minAmount,
71
114
  calls: [...calls]
72
115
  };
116
+ return {
117
+ ...leg,
118
+ probe: probe && {
119
+ ...probe,
120
+ realAmount: leg.amount
121
+ }
122
+ };
73
123
  },
74
124
  async openStrategy({ expectedBalances, leftoverBalances, target }) {
75
- return router.findOpenStrategyPath({
76
- creditManager: cmSlice,
77
- expectedBalances,
78
- leftoverBalances,
79
- target,
80
- slippage
125
+ const probe = startProbe({
126
+ basket: expectedBalances,
127
+ tokenOut: target,
128
+ oracle: priceOracle,
129
+ route: async (balances) => (await quoteOpen(balances, [], target)).amount
81
130
  });
131
+ const leg = await quoteOpen(expectedBalances, leftoverBalances, target);
132
+ return {
133
+ ...leg,
134
+ probe: probe && {
135
+ ...probe,
136
+ realAmount: leg.amount
137
+ }
138
+ };
82
139
  }
83
140
  };
84
141
  }
@@ -98,7 +155,8 @@ function createOraclePaths(args) {
98
155
  const estimate = (amount) => ({
99
156
  amount,
100
157
  minAmount: amount,
101
- calls: []
158
+ calls: [],
159
+ probe: void 0
102
160
  });
103
161
  return {
104
162
  async swap({ tokenIn, tokenOut, amount }) {
@@ -1,5 +1,5 @@
1
1
  import "./apy/index.js";
2
- import { RewardAmountAPI } from "./rewards/api.js";
2
+ import { getMerklRewards } from "./rewards/api.js";
3
3
  import { PoolPointsAPI, getKeyForPoolPointsInfo } from "./rewards/extra-apy.js";
4
4
  import "./rewards/index.js";
5
- export { PoolPointsAPI, RewardAmountAPI, getKeyForPoolPointsInfo };
5
+ export { PoolPointsAPI, getKeyForPoolPointsInfo, getMerklRewards };
@@ -1,69 +1,79 @@
1
+ import { AddressMap } from "../../onchain/utils/AddressMap.js";
1
2
  import { BigIntMath } from "../../onchain/utils/bigint-math.js";
2
- import { chains } from "../../onchain/chain/chains.js";
3
3
  import { toBigInt } from "../../onchain/utils/formatter.js";
4
4
  import "../../onchain/index.js";
5
5
  import "../../common-utils/index.js";
6
6
  import { MerkleXYZApi } from "./merkl-api.js";
7
- import { getAddress } from "viem";
7
+ import { getAddress, isAddress } from "viem";
8
8
  //#region src/rewards/rewards/api.ts
9
- var RewardAmountAPI = class RewardAmountAPI {
10
- constructor() {}
11
- static async getLmRewardsMerkle({ pools, account, network, reportError, apiKey }) {
12
- const [merkleXYZLMResponse] = await Promise.allSettled([MerkleXYZApi.fetchWithFallback(MerkleXYZApi.getUserRewardsUrl({ params: {
13
- chainId: chains[network].id,
14
- user: getAddress(account)
15
- } }), apiKey)]);
16
- const merkleXYZLm = RewardAmountAPI.extractFulfilled(merkleXYZLMResponse, reportError, "merkleXYZLm")?.data;
17
- const poolByItsToken = Object.values(pools).reduce((acc, p) => {
18
- p.stakedDieselToken.forEach((t) => {
19
- if (t) acc[t] = p.address;
20
- });
21
- p.stakedDieselToken_old.forEach((t) => {
22
- if (t) acc[t] = p.address;
23
- });
24
- acc[p.dieselToken] = p.address;
25
- return acc;
26
- }, {});
27
- const extraRewards = (merkleXYZLm || []).reduce((acc, chainRewards) => {
28
- chainRewards.rewards.forEach((reward) => {
29
- const rewardToken = reward.token.address.toLowerCase();
30
- reward.breakdowns.forEach((reason) => {
31
- const poolToken = ((reason.reason || "").split("_").find((part) => part.startsWith("0x")) || "").toLowerCase();
32
- const pool = poolByItsToken[poolToken];
33
- const total = toBigInt(reason.amount || 0);
34
- const claimed = toBigInt(reason.claimed || 0);
35
- const claimable = BigIntMath.max(total - claimed, 0n);
36
- const key = [
37
- pool,
38
- poolToken,
39
- rewardToken
40
- ].join("_");
41
- if (pool && claimable > 0n) {
42
- const prevAmount = acc[key]?.amount || 0n;
43
- acc[key] = {
44
- pool,
45
- poolToken,
46
- rewardToken,
47
- rewardTokenSymbol: reward.token.symbol,
48
- rewardTokenDecimals: reward.token.decimals || 18,
49
- amount: prevAmount + claimable,
50
- type: "extraMerkle"
51
- };
52
- }
9
+ /**
10
+ * The wallet's claimable Merkl rewards on one chain.
11
+ *
12
+ * Never rejects on a transport failure: the fetch is settled rather than
13
+ * awaited, and a failure goes to `reportError` and yields an empty list. A
14
+ * caller that must tell "this chain is down" from "this chain has no rewards"
15
+ * has to watch that callback.
16
+ */
17
+ async function getMerklRewards({ sdk, account, reportError, apiKey }) {
18
+ const [merkleXYZLMResponse] = await Promise.allSettled([MerkleXYZApi.fetchWithFallback(MerkleXYZApi.getUserRewardsUrl({ params: {
19
+ chainId: sdk.chainId,
20
+ user: getAddress(account)
21
+ } }), apiKey)]);
22
+ const merkleXYZLm = extractFulfilled(merkleXYZLMResponse, reportError, "merkleXYZLm")?.data;
23
+ const poolByItsToken = AddressMap.fromMappedArray(sdk.marketRegister.pools.map(({ pool }) => pool.address), (address) => address);
24
+ const claimable = /* @__PURE__ */ new Map();
25
+ for (const chainRewards of merkleXYZLm || []) for (const reward of chainRewards.rewards) {
26
+ if (!isAddress(reward.token.address, { strict: false })) continue;
27
+ const rewardTokenAddress = getAddress(reward.token.address);
28
+ for (const reason of reward.breakdowns) {
29
+ const poolTokenAddress = (reason.reason || "").split("_").find((part) => part.startsWith("0x")) ?? "";
30
+ if (!isAddress(poolTokenAddress, { strict: false })) continue;
31
+ const pool = poolByItsToken.get(poolTokenAddress);
32
+ if (!pool) continue;
33
+ const total = toBigInt(reason.amount || 0);
34
+ const claimed = toBigInt(reason.claimed || 0);
35
+ const amount = BigIntMath.max(total - claimed, 0n);
36
+ if (amount === 0n) continue;
37
+ const key = `${pool}_${rewardTokenAddress}`;
38
+ const seen = claimable.get(key);
39
+ if (seen) {
40
+ claimable.set(key, {
41
+ ...seen,
42
+ amount: seen.amount + amount
53
43
  });
44
+ continue;
45
+ }
46
+ const poolToken = sdk.tokensMeta.getToken(pool);
47
+ if (!poolToken) continue;
48
+ claimable.set(key, {
49
+ chainId: sdk.chainId,
50
+ pool,
51
+ poolToken,
52
+ rewardToken: toRewardToken(sdk, rewardTokenAddress, reward.token),
53
+ amount
54
54
  });
55
- return acc;
56
- }, {});
57
- return Object.values(extraRewards);
58
- }
59
- static extractFulfilled(r, reportError, description) {
60
- if (r.status === "fulfilled") return r.value;
61
- else {
62
- if (reportError) reportError(r.reason, description);
63
- else console.error(r.reason);
64
- return;
65
55
  }
66
56
  }
67
- };
57
+ return [...claimable.values()];
58
+ }
59
+ /**
60
+ * A campaign's incentive token is not protocol collateral, so the registry
61
+ * usually has no entry for it — and Merkl always names it. The one place the
62
+ * two sources are reconciled.
63
+ */
64
+ function toRewardToken(sdk, address, merkl) {
65
+ return sdk.tokensMeta.getToken(address) ?? {
66
+ chainId: sdk.chainId,
67
+ address,
68
+ symbol: merkl.symbol,
69
+ name: merkl.symbol,
70
+ decimals: merkl.decimals || 18
71
+ };
72
+ }
73
+ function extractFulfilled(r, reportError, description) {
74
+ if (r.status === "fulfilled") return r.value;
75
+ if (reportError) reportError(r.reason, description);
76
+ else console.error(r.reason);
77
+ }
68
78
  //#endregion
69
- export { RewardAmountAPI };
79
+ export { getMerklRewards };
@@ -1,3 +1,3 @@
1
- import { RewardAmountAPI } from "./api.js";
1
+ import { getMerklRewards } from "./api.js";
2
2
  import { PoolPointsAPI, getKeyForPoolPointsInfo } from "./extra-apy.js";
3
- export { PoolPointsAPI, RewardAmountAPI, getKeyForPoolPointsInfo };
3
+ export { PoolPointsAPI, getKeyForPoolPointsInfo, getMerklRewards };
@@ -21,10 +21,10 @@ import { PeripheryCompressorV310Contract } from "./bots/PeripheryCompressorV310C
21
21
  import "./bots/index.js";
22
22
  import { CreditAccountsServiceV310 } from "./CreditAccountsServiceV310.js";
23
23
  import { LeverageBand } from "./intents/leverage-band.js";
24
- import { OpenStrategyPreview, OpenStrategyProps } from "./intents/open-strategy.js";
25
- import { IntentPreviewError, PreviewErrorDetails, PreviewErrorReason, PreviewRefusal, refuse } from "./intents/refusal.js";
26
- import { AddCollateralIntent, AdjustLeverageIntent, CreditAccountSlice, DelayableIntent, DelayedRoute, DelayedStart, DelayedStartResult, DepositStrategyIntent, FinishIntentProps, InstantRoute, IntentPreviewResult, IntentRoutesResult, OperationState, RepayStrategyIntent, ResumableIntent, RouteRefusals, StartIntent, WithdrawAssetIntent, WithdrawStrategyIntent } from "./intents/types.js";
27
24
  import { AccountCalculatorOperation } from "./intents/operations.js";
25
+ import { IntentPreviewError, PreviewErrorDetails, PreviewErrorReason, PreviewRefusal, refuse } from "./intents/refusal.js";
26
+ import { AddCollateralIntent, AdjustLeverageIntent, CreditAccountSlice, DelayableIntent, DelayedRoute, DelayedStart, DelayedStartResult, DepositStrategyIntent, FinishIntentProps, InstantRoute, IntentPreviewResult, IntentRoutesResult, OperationState, PathLossRate, RepayStrategyIntent, ResumableIntent, RouteRefusals, StartIntent, WithdrawAssetIntent, WithdrawStrategyIntent } from "./intents/types.js";
27
+ import { OpenStrategyPreview, OpenStrategyProps } from "./intents/open-strategy.js";
28
28
  import { fetchCreditAccountSlice, toCreditAccountSlice } from "./intents/utils/credit-account-slice.js";
29
29
  import { CreditAccountOperationsService, OpenStrategyPreviewResult } from "./intents/index.js";
30
30
  import { LIQUIDATION_APPROVAL_BUFFER, LIQUIDATION_COMPRESSOR_V313_ADDRESS } from "./liquidations/constants.js";
@@ -32,4 +32,4 @@ import { BuildLiquidationTxProps, BuildLiquidationTxPropsBase, GetLiquidatableAc
32
32
  import { LiquidationsService } from "./liquidations/LiquidationsService.js";
33
33
  import { MultichainLiquidationsService } from "./liquidations/MultichainLiquidationsService.js";
34
34
  import "./liquidations/index.js";
35
- export { AbstractWithdrawalCompressorContract, AccountBotsService, type AccountCalculatorOperation, AccountToCheck, type AddCollateralIntent, type AdjustLeverageIntent, AssembleCaOperationsProps, AssembleClaimDelayedCallsProps, AssembleCloseCreditAccountCallsProps, AssembleRepayCreditAccountCallsProps, AssembleStartDelayedWithdrawalCallsProps, BotStatusCall, BotsDirectResponse, BuildLiquidationTxProps, BuildLiquidationTxPropsBase, CMSlice, ClaimFarmRewardsProps, ClaimableWithdrawal, CloseCreditAccountResult, ConnectedBotsCall, ConnectedBotsPerAccount, CreditAccountCompressor, CreditAccountCompressorV310Contract, CreditAccountDataCall, CreditAccountFilter, CreditAccountOperationResult, CreditAccountOperationsService, CreditAccountReadOptions, type CreditAccountSlice, CreditAccountsCall, CreditAccountsQuery, CreditAccountsReadOptions, CreditAccountsServiceV310, CreditAccountsTarget, CreditManagerFilter, CreditManagerOperationResult, CurrentWithdrawals, DELAYED_INTENT_TYPES, DELAYED_INTENT_VERSION, type DelayableIntent, DelayedIntentExtended, type DelayedRoute, type DelayedStart, type DelayedStartResult, type DepositStrategyIntent, EncodableCreditAccountOperation, type FinishIntentProps, FullyLiquidateProps, FullyLiquidateResult, GetApprovalAddressProps, GetConnectedBotsResponse, GetConnectedBotsResult, GetConnectedMigrationBotsResult, GetCreditAccountsArgs, GetCreditAccountsOptions, GetExternalAccountCurrentWithdrawalsProps, GetLiquidatableAccountsProps, GetLiquidationDetailsProps, GetLiquidationDetailsPropsBase, GetLiquidationPositionsProps, GetLiquidationPositionsPropsBase, GetPendingWithdrawalsProps, GetPendingWithdrawalsResult, GetWithdrawalRequestResultProps, ICreditAccountsService, IRedemptionLoggerContract, IWithdrawalCompressorContract, type InstantRoute, IntentPreviewError, type IntentPreviewResult, type IntentRoutesResult, InvalidDelayedIntentError, LIQUIDATION_APPROVAL_BUFFER, LIQUIDATION_COMPRESSOR_V313_ADDRESS, type LeverageBand, LiquidationsService, LoadRWALiquidatorsProps, MulticallWithFailure, MultichainLiquidationsService, OnchainLiquidationCall, OnchainLiquidationData, OnchainLiquidationOutput, OnchainRequestableWithdrawal, OpenCAProps, type OpenStrategyPreview, OpenStrategyPreviewResult, type OpenStrategyProps, type OperationState, PartiallyLiquidateProps, PendingWithdrawal, PeripheryCompressorV310Contract, PreviewDelayedWithdrawalProps, type PreviewErrorDetails, type PreviewErrorReason, type PreviewRefusal, RWALiquidatorInfo, RedemptionLog, RedemptionLoggerV310Contract, type RepayStrategyIntent, RequestableWithdrawal, type ResumableIntent, Rewards, type RouteRefusals, SetBotProps, SetBotResult, type StartIntent, type WithdrawAssetIntent, type WithdrawStrategyIntent, WithdrawableAsset, WithdrawalCompressorLocation, WithdrawalCompressorV310Contract, WithdrawalCompressorV311Contract, WithdrawalCompressorV313Contract, WithdrawalCompressorVersion, WithdrawalOutput, WithdrawalStatus, WithdrawalsState, createRedemptionLogger, createWithdrawalCompressor, decodeDelayedIntent, encodeDelayedIntent, fetchCreditAccountSlice, getWithdrawalCompressorAddress, iCreditAccountAbi, refuse, toClaimableWithdrawal, toCreditAccountSlice, toPendingWithdrawal, toRequestableWithdrawal, toWithdrawalStatus };
35
+ export { AbstractWithdrawalCompressorContract, AccountBotsService, type AccountCalculatorOperation, AccountToCheck, type AddCollateralIntent, type AdjustLeverageIntent, AssembleCaOperationsProps, AssembleClaimDelayedCallsProps, AssembleCloseCreditAccountCallsProps, AssembleRepayCreditAccountCallsProps, AssembleStartDelayedWithdrawalCallsProps, BotStatusCall, BotsDirectResponse, BuildLiquidationTxProps, BuildLiquidationTxPropsBase, CMSlice, ClaimFarmRewardsProps, ClaimableWithdrawal, CloseCreditAccountResult, ConnectedBotsCall, ConnectedBotsPerAccount, CreditAccountCompressor, CreditAccountCompressorV310Contract, CreditAccountDataCall, CreditAccountFilter, CreditAccountOperationResult, CreditAccountOperationsService, CreditAccountReadOptions, type CreditAccountSlice, CreditAccountsCall, CreditAccountsQuery, CreditAccountsReadOptions, CreditAccountsServiceV310, CreditAccountsTarget, CreditManagerFilter, CreditManagerOperationResult, CurrentWithdrawals, DELAYED_INTENT_TYPES, DELAYED_INTENT_VERSION, type DelayableIntent, DelayedIntentExtended, type DelayedRoute, type DelayedStart, type DelayedStartResult, type DepositStrategyIntent, EncodableCreditAccountOperation, type FinishIntentProps, FullyLiquidateProps, FullyLiquidateResult, GetApprovalAddressProps, GetConnectedBotsResponse, GetConnectedBotsResult, GetConnectedMigrationBotsResult, GetCreditAccountsArgs, GetCreditAccountsOptions, GetExternalAccountCurrentWithdrawalsProps, GetLiquidatableAccountsProps, GetLiquidationDetailsProps, GetLiquidationDetailsPropsBase, GetLiquidationPositionsProps, GetLiquidationPositionsPropsBase, GetPendingWithdrawalsProps, GetPendingWithdrawalsResult, GetWithdrawalRequestResultProps, ICreditAccountsService, IRedemptionLoggerContract, IWithdrawalCompressorContract, type InstantRoute, IntentPreviewError, type IntentPreviewResult, type IntentRoutesResult, InvalidDelayedIntentError, LIQUIDATION_APPROVAL_BUFFER, LIQUIDATION_COMPRESSOR_V313_ADDRESS, type LeverageBand, LiquidationsService, LoadRWALiquidatorsProps, MulticallWithFailure, MultichainLiquidationsService, OnchainLiquidationCall, OnchainLiquidationData, OnchainLiquidationOutput, OnchainRequestableWithdrawal, OpenCAProps, type OpenStrategyPreview, OpenStrategyPreviewResult, type OpenStrategyProps, type OperationState, PartiallyLiquidateProps, type PathLossRate, PendingWithdrawal, PeripheryCompressorV310Contract, PreviewDelayedWithdrawalProps, type PreviewErrorDetails, type PreviewErrorReason, type PreviewRefusal, RWALiquidatorInfo, RedemptionLog, RedemptionLoggerV310Contract, type RepayStrategyIntent, RequestableWithdrawal, type ResumableIntent, Rewards, type RouteRefusals, SetBotProps, SetBotResult, type StartIntent, type WithdrawAssetIntent, type WithdrawStrategyIntent, WithdrawableAsset, WithdrawalCompressorLocation, WithdrawalCompressorV310Contract, WithdrawalCompressorV311Contract, WithdrawalCompressorV313Contract, WithdrawalCompressorVersion, WithdrawalOutput, WithdrawalStatus, WithdrawalsState, createRedemptionLogger, createWithdrawalCompressor, decodeDelayedIntent, encodeDelayedIntent, fetchCreditAccountSlice, getWithdrawalCompressorAddress, iCreditAccountAbi, refuse, toClaimableWithdrawal, toCreditAccountSlice, toPendingWithdrawal, toRequestableWithdrawal, toWithdrawalStatus };
@@ -1,9 +1,9 @@
1
1
  import { SDKConstruct } from "../../base/SDKConstruct.js";
2
2
  import { LeverageBand, LeverageBandProps } from "./leverage-band.js";
3
- import { OpenStrategyPreview, OpenStrategyProps } from "./open-strategy.js";
4
- import { IntentPreviewError, PreviewErrorDetails, PreviewErrorReason, PreviewRefusal, refuse } from "./refusal.js";
5
- import { AddCollateralIntent, AdjustLeverageIntent, CreditAccountSlice, DelayableIntent, DelayedRoute, DelayedStart, DelayedStartResult, DepositStrategyIntent, FinishIntentProps, InstantRoute, IntentPreviewResult, IntentRoutesResult, OperationState, RepayStrategyIntent, ResumableIntent, RouteRefusals, StartIntent, StartIntentProps, WithdrawAssetIntent, WithdrawStrategyIntent } from "./types.js";
6
3
  import { AccountCalculatorOperation } from "./operations.js";
4
+ import { IntentPreviewError, PreviewErrorDetails, PreviewErrorReason, PreviewRefusal, refuse } from "./refusal.js";
5
+ import { AddCollateralIntent, AdjustLeverageIntent, CreditAccountSlice, DelayableIntent, DelayedRoute, DelayedStart, DelayedStartResult, DepositStrategyIntent, FinishIntentProps, InstantRoute, IntentPreviewResult, IntentRoutesResult, OperationState, PathLossRate, RepayStrategyIntent, ResumableIntent, RouteRefusals, StartIntent, StartIntentProps, WithdrawAssetIntent, WithdrawStrategyIntent } from "./types.js";
6
+ import { OpenStrategyPreview, OpenStrategyProps } from "./open-strategy.js";
7
7
  import { fetchCreditAccountSlice, toCreditAccountSlice } from "./utils/credit-account-slice.js";
8
8
  import { Address } from "viem";
9
9
  //#region src/onchain/accounts/intents/index.d.ts
@@ -172,4 +172,4 @@ declare class CreditAccountOperationsService extends SDKConstruct {
172
172
  openStrategyIntent(props: OpenStrategyProps): Promise<OpenStrategyPreviewResult>;
173
173
  }
174
174
  //#endregion
175
- export { type AccountCalculatorOperation, type AddCollateralIntent, type AdjustLeverageIntent, CreditAccountOperationsService, type CreditAccountSlice, type DelayableIntent, type DelayedRoute, type DelayedStart, type DelayedStartResult, type DepositStrategyIntent, type FinishIntentProps, type InstantRoute, IntentPreviewError, type IntentPreviewResult, type IntentRoutesResult, type LeverageBand, type OpenStrategyPreview, OpenStrategyPreviewResult, type OpenStrategyProps, type OperationState, type PreviewErrorDetails, type PreviewErrorReason, type PreviewRefusal, type RepayStrategyIntent, type ResumableIntent, type RouteRefusals, type StartIntent, type WithdrawAssetIntent, type WithdrawStrategyIntent, fetchCreditAccountSlice, refuse, toCreditAccountSlice };
175
+ export { type AccountCalculatorOperation, type AddCollateralIntent, type AdjustLeverageIntent, CreditAccountOperationsService, type CreditAccountSlice, type DelayableIntent, type DelayedRoute, type DelayedStart, type DelayedStartResult, type DepositStrategyIntent, type FinishIntentProps, type InstantRoute, IntentPreviewError, type IntentPreviewResult, type IntentRoutesResult, type LeverageBand, type OpenStrategyPreview, OpenStrategyPreviewResult, type OpenStrategyProps, type OperationState, type PathLossRate, type PreviewErrorDetails, type PreviewErrorReason, type PreviewRefusal, type RepayStrategyIntent, type ResumableIntent, type RouteRefusals, type StartIntent, type WithdrawAssetIntent, type WithdrawStrategyIntent, fetchCreditAccountSlice, refuse, toCreditAccountSlice };
@@ -4,6 +4,7 @@ import "../../../model/index.js";
4
4
  import { Asset } from "../../base/types.js";
5
5
  import { MultiCall } from "../../types/transactions.js";
6
6
  import { OnchainSDK } from "../../OnchainSDK.js";
7
+ import { PathLossRate } from "./types.js";
7
8
  import "../../index.js";
8
9
  import { Address } from "viem";
9
10
  //#region src/onchain/accounts/intents/open-strategy.d.ts
@@ -60,6 +61,8 @@ interface OpenStrategyPreview {
60
61
  collateral: bigint;
61
62
  /** Position size — collateral plus debt, in underlying. */
62
63
  totalValue: bigint;
64
+ /** What the routed leg lost to market depth; `undefined` if not measured. */
65
+ priceImpact: PathLossRate | undefined;
63
66
  /** Expected post-open balances. */
64
67
  averageAssets: TokenAmount[];
65
68
  /** Floor post-open balances after slippage. */
@@ -1,7 +1,7 @@
1
1
  import { MultiCall } from "../../types/transactions.js";
2
2
  import { OnchainSDK } from "../../OnchainSDK.js";
3
- import { CreditAccountSlice, DelayedStart, OperationState } from "./types.js";
4
3
  import { AccountCalculatorOperation } from "./operations.js";
4
+ import { CreditAccountSlice, DelayedStart, OperationState } from "./types.js";
5
5
  import "../../index.js";
6
6
  import { Step } from "./plan.js";
7
7
  import { RouterPaths } from "./utils/router-path.js";
@@ -1,7 +1,7 @@
1
1
  import { ClaimableWithdrawal } from "../withdrawal-compressor/types.js";
2
2
  import { OnchainSDK } from "../../OnchainSDK.js";
3
- import { CreditAccountSlice, DelayedStart, OperationState, ResumableIntent } from "./types.js";
4
3
  import { AccountCalculatorOperation, StartDelayedWithdrawalOperation } from "./operations.js";
4
+ import { CreditAccountSlice, DelayedStart, OperationState, ResumableIntent } from "./types.js";
5
5
  import "../../index.js";
6
6
  import { AccountView, Step } from "./plan.js";
7
7
  //#region src/onchain/accounts/intents/tail.d.ts
@@ -1,8 +1,8 @@
1
1
  import { TokenAmount } from "../../../../model/primitives.js";
2
2
  import "../../../../model/index.js";
3
3
  import { MultiCall } from "../../../types/transactions.js";
4
- import { DelayedStartResult, IntentPreviewResult, OperationState } from "../types.js";
5
4
  import { AccountCalculatorOperation } from "../operations.js";
5
+ import { DelayedStartResult, IntentPreviewResult, OperationState } from "../types.js";
6
6
  import "../../../index.js";
7
7
  import { Address } from "viem";
8
8
  //#region src/onchain/accounts/intents/testing/expect.d.ts