@varla/sdk 2.14.0 → 2.15.1

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 (40) hide show
  1. package/AGENTS.md +5 -5
  2. package/CHANGELOG.md +17 -0
  3. package/dist/abi/full/OracleUpdaterRouter.d.ts +35 -1
  4. package/dist/abi/full/OracleUpdaterRouter.d.ts.map +1 -1
  5. package/dist/abi/full/OracleUpdaterRouter.js +46 -1
  6. package/dist/abi/full/OracleUpdaterRouter.js.map +1 -1
  7. package/dist/actions/oracleUpdaterRouter.d.ts +24 -0
  8. package/dist/actions/oracleUpdaterRouter.d.ts.map +1 -1
  9. package/dist/actions/oracleUpdaterRouter.js +38 -0
  10. package/dist/actions/oracleUpdaterRouter.js.map +1 -1
  11. package/dist/generated.d.ts +35 -1
  12. package/dist/generated.d.ts.map +1 -1
  13. package/dist/leverage/deleverageExecute.d.ts +6 -0
  14. package/dist/leverage/deleverageExecute.d.ts.map +1 -1
  15. package/dist/leverage/deleverageExecute.js +14 -1
  16. package/dist/leverage/deleverageExecute.js.map +1 -1
  17. package/dist/leverage/execute.d.ts +6 -0
  18. package/dist/leverage/execute.d.ts.map +1 -1
  19. package/dist/leverage/execute.js +14 -1
  20. package/dist/leverage/execute.js.map +1 -1
  21. package/dist/leverage/index.d.ts +1 -1
  22. package/dist/leverage/index.d.ts.map +1 -1
  23. package/dist/leverage/index.js +1 -1
  24. package/dist/leverage/index.js.map +1 -1
  25. package/dist/leverage/math.d.ts +12 -0
  26. package/dist/leverage/math.d.ts.map +1 -1
  27. package/dist/leverage/math.js +66 -5
  28. package/dist/leverage/math.js.map +1 -1
  29. package/dist/leverage/types.d.ts +23 -0
  30. package/dist/leverage/types.d.ts.map +1 -1
  31. package/dist/leverage/types.js +21 -0
  32. package/dist/leverage/types.js.map +1 -1
  33. package/package.json +1 -1
  34. package/src/abi/full/OracleUpdaterRouter.ts +46 -1
  35. package/src/actions/oracleUpdaterRouter.ts +51 -0
  36. package/src/leverage/deleverageExecute.ts +22 -0
  37. package/src/leverage/execute.ts +22 -0
  38. package/src/leverage/index.ts +1 -0
  39. package/src/leverage/math.ts +92 -5
  40. package/src/leverage/types.ts +39 -0
@@ -41,6 +41,21 @@ function mulDivUp(a: bigint, b: bigint, c: bigint): bigint {
41
41
  return (a * b + c - 1n) / c;
42
42
  }
43
43
 
44
+ /** BPS base (100%). */
45
+ const BPS_BASE = 10_000n;
46
+
47
+ /**
48
+ * Adjust a price for slippage.
49
+ * - Buying: pay more per token → effective price goes UP.
50
+ * - Selling: receive less per token → effective price goes DOWN.
51
+ */
52
+ function applySlippageToBuyPrice(price: bigint, slippageBps: bigint): bigint {
53
+ return slippageBps > 0n ? mulDivUp(price, BPS_BASE + slippageBps, BPS_BASE) : price;
54
+ }
55
+ function applySlippageToSellPrice(price: bigint, slippageBps: bigint): bigint {
56
+ return slippageBps > 0n ? mulDiv(price, BPS_BASE - slippageBps, BPS_BASE) : price;
57
+ }
58
+
44
59
  // ---------------------------------------------------------------------------
45
60
  // Public helpers
46
61
  // ---------------------------------------------------------------------------
@@ -126,6 +141,12 @@ export type PlanLeverageLoopParams = {
126
141
  startingDebt?: bigint;
127
142
  /** Optional absolute cap on total debt (collateral base units). */
128
143
  maxTotalDebt?: bigint;
144
+ /**
145
+ * Assumed slippage/spread for CLOB buys (bps, e.g. 50 = 0.50%).
146
+ * Buys use an effectively higher price: `price * (10_000 + slippageBps) / 10_000`.
147
+ * Default: 0 (ideal execution).
148
+ */
149
+ slippageBps?: bigint;
129
150
  };
130
151
 
131
152
  /**
@@ -151,6 +172,7 @@ export function planLeverageLoop(params: PlanLeverageLoopParams): LeveragePlan {
151
172
  startingTokens = 0n,
152
173
  startingDebt = 0n,
153
174
  maxTotalDebt,
175
+ slippageBps = 0n,
154
176
  } = params;
155
177
 
156
178
  if (initialCapital <= 0n) throw new Error("initialCapital must be > 0");
@@ -159,8 +181,11 @@ export function planLeverageLoop(params: PlanLeverageLoopParams): LeveragePlan {
159
181
  if (maxIterations < 1) throw new Error("maxIterations must be >= 1");
160
182
  if (startingTokens < 0n) throw new Error("startingTokens must be >= 0");
161
183
  if (startingDebt < 0n) throw new Error("startingDebt must be >= 0");
184
+ if (slippageBps < 0n) throw new Error("slippageBps must be >= 0");
185
+ if (slippageBps >= BPS_BASE) throw new Error("slippageBps must be < 10_000");
162
186
 
163
187
  const maxLev = maxLeverage(ltvWad);
188
+ const effectiveBuyPrice = applySlippageToBuyPrice(tokenPriceE8, slippageBps);
164
189
 
165
190
  // Target leverage semantics:
166
191
  // - undefined => run to max (bounded by maxIterations / minStepBorrow / caps)
@@ -198,7 +223,8 @@ export function planLeverageLoop(params: PlanLeverageLoopParams): LeveragePlan {
198
223
  // buy
199
224
  const spendCollateral = collateralAvailable;
200
225
 
201
- const tokensBought = tokensForCollateral(spendCollateral, tokenPriceE8);
226
+ // Use effective (slippage-adjusted) price for buy, but ideal price for expectedTokens display.
227
+ const tokensBought = tokensForCollateral(spendCollateral, effectiveBuyPrice);
202
228
  if (tokensBought <= 0n) break;
203
229
 
204
230
  // Spend the available collateral for this iteration.
@@ -262,6 +288,24 @@ export function planLeverageLoop(params: PlanLeverageLoopParams): LeveragePlan {
262
288
  estimatedLiquidationPriceE8,
263
289
  iterations,
264
290
  maxLeverageWad: maxLev,
291
+ assumedSlippageBps: slippageBps,
292
+ totalSlippageCost:
293
+ slippageBps > 0n
294
+ ? clampPositive(
295
+ // Ideal tokens - actual tokens, valued at mid price.
296
+ collateralForTokens(
297
+ steps
298
+ .filter((s): s is Extract<LeverageStep, { kind: "buy" }> => s.kind === "buy")
299
+ .reduce(
300
+ (acc, s) => acc + tokensForCollateral(s.collateralAmount, tokenPriceE8),
301
+ 0n,
302
+ ) -
303
+ totalTokens +
304
+ startingTokens,
305
+ tokenPriceE8,
306
+ ),
307
+ )
308
+ : 0n,
265
309
  };
266
310
 
267
311
  return {
@@ -300,6 +344,12 @@ export type PlanDeleverageParams = {
300
344
  maxIterations?: number;
301
345
  /** Minimum sell size (in collateral base units) to continue looping. */
302
346
  minStepSell?: bigint;
347
+ /**
348
+ * Assumed slippage/spread for CLOB sells (bps, e.g. 50 = 0.50%).
349
+ * Sells use an effectively lower price: `price * (10_000 - slippageBps) / 10_000`.
350
+ * Default: 0 (ideal execution).
351
+ */
352
+ slippageBps?: bigint;
303
353
  };
304
354
 
305
355
  /**
@@ -316,12 +366,17 @@ export function planDeleverage(params: PlanDeleverageParams): DeleveragePlan {
316
366
  targetDebtReduction,
317
367
  maxIterations = DEFAULT_MAX_ITERATIONS,
318
368
  minStepSell = DEFAULT_MIN_STEP_COLLATERAL,
369
+ slippageBps = 0n,
319
370
  } = params;
320
371
 
321
372
  if (tokenPriceE8 <= 0n) throw new Error("tokenPriceE8 must be > 0");
322
373
  if (ltvWad <= 0n || ltvWad >= WAD) throw new Error("ltvWad must be in (0, 1e18)");
323
374
  if (depositedTokens < 0n) throw new Error("depositedTokens must be >= 0");
324
375
  if (currentDebt < 0n) throw new Error("currentDebt must be >= 0");
376
+ if (slippageBps < 0n) throw new Error("slippageBps must be >= 0");
377
+ if (slippageBps >= BPS_BASE) throw new Error("slippageBps must be < 10_000");
378
+
379
+ const effectiveSellPrice = applySlippageToSellPrice(tokenPriceE8, slippageBps);
325
380
 
326
381
  const targetRepay = targetDebtReduction === "full" ? currentDebt : targetDebtReduction;
327
382
  if (targetRepay <= 0n) {
@@ -333,6 +388,8 @@ export function planDeleverage(params: PlanDeleverageParams): DeleveragePlan {
333
388
  tokensRemaining: depositedTokens,
334
389
  collateralRemaining: 0n,
335
390
  iterations: 0,
391
+ assumedSlippageBps: slippageBps,
392
+ totalSlippageCost: 0n,
336
393
  },
337
394
  };
338
395
  }
@@ -342,6 +399,7 @@ export function planDeleverage(params: PlanDeleverageParams): DeleveragePlan {
342
399
  let remainingDebt = currentDebt;
343
400
  let totalSold = 0n;
344
401
  let totalRepaid = 0n;
402
+ let excessCollateral = 0n; // sell proceeds not used for repayment (returned to user)
345
403
 
346
404
  for (let i = 0; i < maxIterations; i++) {
347
405
  if (remainingDebt <= 0n) break;
@@ -364,7 +422,7 @@ export function planDeleverage(params: PlanDeleverageParams): DeleveragePlan {
364
422
  withdrawable < tokensNeededForTarget ? withdrawable : tokensNeededForTarget;
365
423
  if (tokensToWithdraw <= 0n) break;
366
424
 
367
- const collateralProceeds = collateralForTokens(tokensToWithdraw, tokenPriceE8);
425
+ const collateralProceeds = collateralForTokens(tokensToWithdraw, effectiveSellPrice);
368
426
  if (collateralProceeds < minStepSell) break;
369
427
 
370
428
  // withdraw
@@ -385,11 +443,32 @@ export function planDeleverage(params: PlanDeleverageParams): DeleveragePlan {
385
443
  steps.push({ kind: "repay", collateralAmount: repayAmount, iteration: i });
386
444
  remainingDebt -= repayAmount;
387
445
  totalRepaid += repayAmount;
446
+ excessCollateral += collateralProceeds - repayAmount;
447
+ }
448
+
449
+ // Phase 2: After all debt is repaid, withdraw and sell remaining tokens.
450
+ // This ensures "full" close returns all collateral to the user, not just repays debt.
451
+ // Only fires when debt is fully repaid — if debt remains (underwater/stuck), skip.
452
+ if (targetDebtReduction === "full" && remainingDebt <= 0n && remainingTokens > 0n) {
453
+ const collateralProceeds = collateralForTokens(remainingTokens, tokenPriceE8);
454
+ const iteration = steps.length > 0 ? steps[steps.length - 1]!.iteration + 1 : 0;
455
+
456
+ steps.push({ kind: "withdraw", amount: remainingTokens, iteration });
457
+ steps.push({
458
+ kind: "sell",
459
+ tokenAmount: remainingTokens,
460
+ expectedCollateral: collateralProceeds,
461
+ iteration,
462
+ });
463
+
464
+ excessCollateral += collateralProceeds;
465
+ totalSold += remainingTokens;
466
+ remainingTokens = 0n;
388
467
  }
389
468
 
390
- const collateralRemaining = clampPositive(
391
- collateralForTokens(remainingTokens, tokenPriceE8) - remainingDebt,
392
- );
469
+ const collateralRemaining =
470
+ excessCollateral +
471
+ clampPositive(collateralForTokens(remainingTokens, tokenPriceE8) - remainingDebt);
393
472
  const iterations = steps.length > 0 ? steps[steps.length - 1]!.iteration + 1 : 0;
394
473
 
395
474
  return {
@@ -400,6 +479,14 @@ export function planDeleverage(params: PlanDeleverageParams): DeleveragePlan {
400
479
  tokensRemaining: remainingTokens,
401
480
  collateralRemaining,
402
481
  iterations,
482
+ assumedSlippageBps: slippageBps,
483
+ totalSlippageCost:
484
+ slippageBps > 0n
485
+ ? clampPositive(
486
+ collateralForTokens(totalSold, tokenPriceE8) -
487
+ collateralForTokens(totalSold, effectiveSellPrice),
488
+ )
489
+ : 0n,
403
490
  },
404
491
  };
405
492
  }
@@ -109,6 +109,10 @@ export type LeverageSummary = {
109
109
  iterations: number;
110
110
  /** Maximum theoretical leverage = 1 / (1 - LTV) as WAD. */
111
111
  maxLeverageWad: bigint;
112
+ /** Assumed slippage/spread used in planning (bps). 0 if not specified. */
113
+ assumedSlippageBps: bigint;
114
+ /** Total collateral lost to slippage across all buy steps (base units). */
115
+ totalSlippageCost: bigint;
112
116
  };
113
117
 
114
118
  /**
@@ -164,6 +168,10 @@ export type DeleveragePlan = {
164
168
  tokensRemaining: bigint;
165
169
  collateralRemaining: bigint;
166
170
  iterations: number;
171
+ /** Assumed slippage/spread used in planning (bps). 0 if not specified. */
172
+ assumedSlippageBps: bigint;
173
+ /** Total collateral lost to slippage across all sell steps (base units). */
174
+ totalSlippageCost: bigint;
167
175
  };
168
176
  };
169
177
 
@@ -202,3 +210,34 @@ export type OnLeverageProgress = (info: {
202
210
  step: LeverageStep;
203
211
  result: LeverageStepResult;
204
212
  }) => void;
213
+
214
+ // ---------------------------------------------------------------------------
215
+ // Slippage error
216
+ // ---------------------------------------------------------------------------
217
+
218
+ /**
219
+ * Thrown by executors when actual fill deviates beyond `maxSlippageBps`.
220
+ */
221
+ export class SlippageExceededError extends Error {
222
+ readonly expected: bigint;
223
+ readonly actual: bigint;
224
+ readonly maxSlippageBps: bigint;
225
+ readonly side: "buy" | "sell";
226
+
227
+ constructor(params: {
228
+ expected: bigint;
229
+ actual: bigint;
230
+ maxSlippageBps: bigint;
231
+ side: "buy" | "sell";
232
+ }) {
233
+ const pct = (Number(params.maxSlippageBps) / 100).toFixed(2);
234
+ super(
235
+ `Slippage exceeded on ${params.side}: expected ${params.expected}, got ${params.actual} (max ${pct}%)`,
236
+ );
237
+ this.name = "SlippageExceededError";
238
+ this.expected = params.expected;
239
+ this.actual = params.actual;
240
+ this.maxSlippageBps = params.maxSlippageBps;
241
+ this.side = params.side;
242
+ }
243
+ }