@varla/sdk 2.15.1 → 2.16.0

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # @varla-xyz/protocol
2
2
 
3
+ ## 2.16.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 42777b6: Add receipt retry support and post-borrow/withdraw hooks to leverage loop executors with resumable step execution.
8
+
9
+ - Added receipt retry helper utilities and exports for robust transaction receipt polling
10
+ - Extended leverage and deleverage loop params with retry options, post-step hooks, delays, and resume-from-step support
11
+
3
12
  ## 2.15.1
4
13
 
5
14
  ### Patch Changes
@@ -4,13 +4,14 @@
4
4
  * Drives a deleverage plan by delegating on-chain operations (withdraw/repay)
5
5
  * to SDK action helpers and off-chain operations (sell) to a consumer callback.
6
6
  */
7
- import type { Address, PublicClient, WalletClient } from "viem";
7
+ import type { Address, Hash, PublicClient, WalletClient } from "viem";
8
+ import type { ReceiptRetryOptions } from "./receipt.js";
8
9
  import type { DeleveragePlan, DeleverageStep, SellResult } from "./types.js";
9
10
  export type OnDeleverageProgress = (info: {
10
11
  completed: number;
11
12
  total: number;
12
13
  step: DeleverageStep;
13
- hash?: `0x${string}`;
14
+ hash?: Hash;
14
15
  sellResult?: SellResult;
15
16
  }) => void;
16
17
  export type RunDeleverageLoopParams = {
@@ -32,11 +33,62 @@ export type RunDeleverageLoopParams = {
32
33
  * the loop aborts with a `SlippageExceededError`.
33
34
  */
34
35
  maxSlippageBps?: bigint;
36
+ /**
37
+ * Receipt retry options for `waitForTransactionReceipt`.
38
+ *
39
+ * By default the executor retries up to 5 times with exponential backoff
40
+ * (2s → 4s → 8s → 16s → 32s). Set `{ maxRetries: 0 }` to disable retries.
41
+ */
42
+ receiptRetry?: ReceiptRetryOptions;
43
+ /**
44
+ * Hook called after each successful withdraw step (receipt confirmed),
45
+ * before the next sell step. Use this to refresh CLOB position caches, etc.
46
+ *
47
+ * @param info Withdraw step details and tx hash.
48
+ */
49
+ onPostWithdraw?: (info: {
50
+ step: Extract<DeleverageStep, {
51
+ kind: "withdraw";
52
+ }>;
53
+ hash: Hash;
54
+ }) => Promise<void>;
55
+ /**
56
+ * Milliseconds to sleep after each withdraw confirmation before proceeding
57
+ * to the next sell step. Gives the CLOB server time to re-index on-chain
58
+ * token balance changes.
59
+ *
60
+ * Applied AFTER `onPostWithdraw` (if provided). Default: 0 (no delay).
61
+ */
62
+ postWithdrawDelayMs?: number;
63
+ /**
64
+ * Resume a previously failed execution by skipping steps before this index.
65
+ *
66
+ * When a previous call returned `{ failedAtStep: N }`, pass `startFromStep: N`
67
+ * to resume from exactly where it left off.
68
+ *
69
+ * Default: 0 (run all steps from the beginning).
70
+ */
71
+ startFromStep?: number;
35
72
  };
36
73
  export type RunDeleverageLoopResult = {
37
74
  completed: boolean;
75
+ /** If execution stopped early, the index of the failed step (absolute). */
38
76
  failedAtStep?: number;
39
77
  error?: unknown;
40
78
  };
79
+ /**
80
+ * Execute a deleverage (unwind) loop plan step-by-step.
81
+ *
82
+ * For each step:
83
+ * - "withdraw": prepares + sends `VarlaCore.withdraw()` tx
84
+ * - "sell": delegates to `onSellStep` callback (off-chain CLOB trade)
85
+ * - "repay": prepares + sends `VarlaCore.repay()` tx
86
+ *
87
+ * **Reliability features (v2):**
88
+ * - `receiptRetry`: configurable exponential backoff for receipt polling
89
+ * - `onPostWithdraw`: hook for CLOB cache refresh after each withdraw
90
+ * - `postWithdrawDelayMs`: sleep after withdraw for CLOB indexer sync
91
+ * - `startFromStep`: resume from a previously failed step index
92
+ */
41
93
  export declare function runDeleverageLoop(params: RunDeleverageLoopParams): Promise<RunDeleverageLoopResult>;
42
94
  //# sourceMappingURL=deleverageExecute.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"deleverageExecute.d.ts","sourceRoot":"","sources":["../../src/leverage/deleverageExecute.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AAIhE,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAG7E,MAAM,MAAM,oBAAoB,GAAG,CAAC,IAAI,EAAE;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,cAAc,CAAC;IAErB,IAAI,CAAC,EAAE,KAAK,MAAM,EAAE,CAAC;IACrB,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB,KAAK,IAAI,CAAC;AAEX,MAAM,MAAM,uBAAuB,GAAG;IACpC,IAAI,EAAE,cAAc,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,kBAAkB,GAAG,2BAA2B,CAAC,CAAC;IACnF,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,eAAe,CAAC,CAAC;IAClD,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,+BAA+B;IAC/B,UAAU,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,cAAc,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IACrF,UAAU,CAAC,EAAE,oBAAoB,CAAC;IAClC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,uBAAuB,GAC9B,OAAO,CAAC,uBAAuB,CAAC,CAgFlC"}
1
+ {"version":3,"file":"deleverageExecute.d.ts","sourceRoot":"","sources":["../../src/leverage/deleverageExecute.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AAItE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAExD,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAG7E,MAAM,MAAM,oBAAoB,GAAG,CAAC,IAAI,EAAE;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,cAAc,CAAC;IAErB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB,KAAK,IAAI,CAAC;AAEX,MAAM,MAAM,uBAAuB,GAAG;IACpC,IAAI,EAAE,cAAc,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,kBAAkB,GAAG,2BAA2B,CAAC,CAAC;IACnF,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,eAAe,CAAC,CAAC;IAClD,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,+BAA+B;IAC/B,UAAU,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,cAAc,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IACrF,UAAU,CAAC,EAAE,oBAAoB,CAAC;IAClC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAIxB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,mBAAmB,CAAC;IAEnC;;;;;OAKG;IACH,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE;QACtB,IAAI,EAAE,OAAO,CAAC,cAAc,EAAE;YAAE,IAAI,EAAE,UAAU,CAAA;SAAE,CAAC,CAAC;QACpD,IAAI,EAAE,IAAI,CAAC;KACZ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpB;;;;;;OAMG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAE7B;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,SAAS,EAAE,OAAO,CAAC;IACnB,2EAA2E;IAC3E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAcF;;;;;;;;;;;;;GAaG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,uBAAuB,GAC9B,OAAO,CAAC,uBAAuB,CAAC,CAiGlC"}
@@ -1,13 +1,37 @@
1
1
  // Note: explicit .js extension is required for Node ESM resolution.
2
2
  import { prepareCoreRepay, prepareCoreWithdraw } from "../actions/core.js";
3
3
  import { sendTx } from "../actions/tx.js";
4
+ import { waitForReceiptWithRetry } from "./receipt.js";
4
5
  import { SlippageExceededError } from "./types.js";
6
+ // ---------------------------------------------------------------------------
7
+ // Helpers
8
+ // ---------------------------------------------------------------------------
9
+ function sleep(ms) {
10
+ return new Promise((resolve) => setTimeout(resolve, ms));
11
+ }
12
+ // ---------------------------------------------------------------------------
13
+ // Executor
14
+ // ---------------------------------------------------------------------------
15
+ /**
16
+ * Execute a deleverage (unwind) loop plan step-by-step.
17
+ *
18
+ * For each step:
19
+ * - "withdraw": prepares + sends `VarlaCore.withdraw()` tx
20
+ * - "sell": delegates to `onSellStep` callback (off-chain CLOB trade)
21
+ * - "repay": prepares + sends `VarlaCore.repay()` tx
22
+ *
23
+ * **Reliability features (v2):**
24
+ * - `receiptRetry`: configurable exponential backoff for receipt polling
25
+ * - `onPostWithdraw`: hook for CLOB cache refresh after each withdraw
26
+ * - `postWithdrawDelayMs`: sleep after withdraw for CLOB indexer sync
27
+ * - `startFromStep`: resume from a previously failed step index
28
+ */
5
29
  export async function runDeleverageLoop(params) {
6
- const { plan, positionId, publicClient, walletClient, coreAddress, account, onSellStep, onProgress, confirmations = 1, maxSlippageBps, } = params;
30
+ const { plan, positionId, publicClient, walletClient, coreAddress, account, onSellStep, onProgress, confirmations = 1, maxSlippageBps, receiptRetry, onPostWithdraw, postWithdrawDelayMs = 0, startFromStep = 0, } = params;
7
31
  const total = plan.steps.length;
8
32
  // Track actual collateral proceeds from the most recent sell step.
9
33
  let lastSellActualCollateral;
10
- for (let i = 0; i < total; i++) {
34
+ for (let i = startFromStep; i < total; i++) {
11
35
  const step = plan.steps[i];
12
36
  try {
13
37
  if (step.kind === "withdraw") {
@@ -19,8 +43,16 @@ export async function runDeleverageLoop(params) {
19
43
  amounts: [step.amount],
20
44
  });
21
45
  const hash = await sendTx({ walletClient: walletClient, request: sim.request });
22
- await publicClient.waitForTransactionReceipt({ hash, confirmations });
46
+ await waitForReceiptWithRetry({ publicClient, hash, confirmations, retry: receiptRetry });
23
47
  onProgress?.({ completed: i + 1, total, step, hash });
48
+ // Post-withdraw hook — let consumer refresh CLOB token balance cache, etc.
49
+ if (onPostWithdraw) {
50
+ await onPostWithdraw({ step, hash });
51
+ }
52
+ // Post-withdraw delay — give CLOB server time to re-index.
53
+ if (postWithdrawDelayMs > 0) {
54
+ await sleep(postWithdrawDelayMs);
55
+ }
24
56
  continue;
25
57
  }
26
58
  if (step.kind === "sell") {
@@ -51,7 +83,7 @@ export async function runDeleverageLoop(params) {
51
83
  amount: repayAmount,
52
84
  });
53
85
  const hash = await sendTx({ walletClient: walletClient, request: sim.request });
54
- await publicClient.waitForTransactionReceipt({ hash, confirmations });
86
+ await waitForReceiptWithRetry({ publicClient, hash, confirmations, retry: receiptRetry });
55
87
  onProgress?.({ completed: i + 1, total, step, hash });
56
88
  continue;
57
89
  }
@@ -1 +1 @@
1
- {"version":3,"file":"deleverageExecute.js","sourceRoot":"","sources":["../../src/leverage/deleverageExecute.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"deleverageExecute.js","sourceRoot":"","sources":["../../src/leverage/deleverageExecute.ts"],"names":[],"mappings":"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"}
@@ -5,7 +5,8 @@
5
5
  * to SDK action helpers and off-chain operations (buy) to a consumer-provided
6
6
  * callback.
7
7
  */
8
- import type { Address, PublicClient, WalletClient } from "viem";
8
+ import type { Address, Hash, PublicClient, WalletClient } from "viem";
9
+ import type { ReceiptRetryOptions } from "./receipt.js";
9
10
  import type { BuyResult, LeveragePlan, LeverageStep, LeverageStepResult, OnLeverageProgress } from "./types.js";
10
11
  export type RunLeverageLoopParams = {
11
12
  /** The plan to execute (from `planLeverageLoop` or `planLeverageFromChain`). */
@@ -39,13 +40,51 @@ export type RunLeverageLoopParams = {
39
40
  * the loop aborts with a `SlippageExceededError`.
40
41
  */
41
42
  maxSlippageBps?: bigint;
43
+ /**
44
+ * Receipt retry options for `waitForTransactionReceipt`.
45
+ *
46
+ * By default the executor retries up to 5 times with exponential backoff
47
+ * (2s → 4s → 8s → 16s → 32s). Set `{ maxRetries: 0 }` to disable retries.
48
+ */
49
+ receiptRetry?: ReceiptRetryOptions;
50
+ /**
51
+ * Hook called after each successful borrow step (receipt confirmed),
52
+ * before the next buy step. Use this to refresh CLOB balance caches,
53
+ * wait for off-chain indexers, etc.
54
+ *
55
+ * @param info Borrow step details and tx hash.
56
+ */
57
+ onPostBorrow?: (info: {
58
+ step: Extract<LeverageStep, {
59
+ kind: "borrow";
60
+ }>;
61
+ hash: Hash;
62
+ }) => Promise<void>;
63
+ /**
64
+ * Milliseconds to sleep after each borrow confirmation before proceeding
65
+ * to the next buy step. Gives the CLOB server time to re-index on-chain
66
+ * balance changes.
67
+ *
68
+ * Applied AFTER `onPostBorrow` (if provided). Default: 0 (no delay).
69
+ */
70
+ postBorrowDelayMs?: number;
71
+ /**
72
+ * Resume a previously failed execution by skipping steps before this index.
73
+ *
74
+ * When a previous call returned `{ failedAtStep: N }`, pass `startFromStep: N`
75
+ * to resume from exactly where it left off. Steps before `startFromStep` are
76
+ * skipped (their results are NOT included in the returned array).
77
+ *
78
+ * Default: 0 (run all steps from the beginning).
79
+ */
80
+ startFromStep?: number;
42
81
  };
43
82
  export type RunLeverageLoopResult = {
44
- /** Results for each step, aligned to `plan.steps`. */
83
+ /** Results for each step executed (starting from `startFromStep`). */
45
84
  results: LeverageStepResult[];
46
85
  /** Whether all steps completed successfully. */
47
86
  completed: boolean;
48
- /** If execution stopped early, the index of the failed step. */
87
+ /** If execution stopped early, the index of the failed step (absolute, relative to plan). */
49
88
  failedAtStep?: number;
50
89
  /** If execution stopped early, the error. */
51
90
  error?: unknown;
@@ -63,6 +102,12 @@ export type RunLeverageLoopResult = {
63
102
  * for slippage.
64
103
  *
65
104
  * If any step fails, execution stops and returns partial results.
105
+ *
106
+ * **Reliability features (v2):**
107
+ * - `receiptRetry`: configurable exponential backoff for receipt polling
108
+ * - `onPostBorrow`: hook for CLOB balance cache refresh after each borrow
109
+ * - `postBorrowDelayMs`: sleep after borrow for CLOB indexer sync
110
+ * - `startFromStep`: resume from a previously failed step index
66
111
  */
67
112
  export declare function runLeverageLoop(params: RunLeverageLoopParams): Promise<RunLeverageLoopResult>;
68
113
  //# sourceMappingURL=execute.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"execute.d.ts","sourceRoot":"","sources":["../../src/leverage/execute.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AAIhE,OAAO,KAAK,EACV,SAAS,EACT,YAAY,EACZ,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAOpB,MAAM,MAAM,qBAAqB,GAAG;IAClC,gFAAgF;IAChF,IAAI,EAAE,YAAY,CAAC;IACnB,sDAAsD;IACtD,UAAU,EAAE,MAAM,CAAC;IACnB,qCAAqC;IACrC,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,kBAAkB,GAAG,2BAA2B,CAAC,CAAC;IACnF,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,eAAe,CAAC,CAAC;IAClD,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB;;;;;;;;OAQG;IACH,SAAS,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,YAAY,EAAE;QAAE,IAAI,EAAE,KAAK,CAAA;KAAE,CAAC,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IAChF,kCAAkC;IAClC,UAAU,CAAC,EAAE,kBAAkB,CAAC;IAChC,iEAAiE;IACjE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,sDAAsD;IACtD,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,gDAAgD;IAChD,SAAS,EAAE,OAAO,CAAC;IACnB,gEAAgE;IAChE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6CAA6C;IAC7C,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAMF;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC,CAuGhC"}
1
+ {"version":3,"file":"execute.d.ts","sourceRoot":"","sources":["../../src/leverage/execute.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AAItE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAExD,OAAO,KAAK,EACV,SAAS,EACT,YAAY,EACZ,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAOpB,MAAM,MAAM,qBAAqB,GAAG;IAClC,gFAAgF;IAChF,IAAI,EAAE,YAAY,CAAC;IACnB,sDAAsD;IACtD,UAAU,EAAE,MAAM,CAAC;IACnB,qCAAqC;IACrC,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,kBAAkB,GAAG,2BAA2B,CAAC,CAAC;IACnF,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,eAAe,CAAC,CAAC;IAClD,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB;;;;;;;;OAQG;IACH,SAAS,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,YAAY,EAAE;QAAE,IAAI,EAAE,KAAK,CAAA;KAAE,CAAC,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;IAChF,kCAAkC;IAClC,UAAU,CAAC,EAAE,kBAAkB,CAAC;IAChC,iEAAiE;IACjE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAIxB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,mBAAmB,CAAC;IAEnC;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE;QACpB,IAAI,EAAE,OAAO,CAAC,YAAY,EAAE;YAAE,IAAI,EAAE,QAAQ,CAAA;SAAE,CAAC,CAAC;QAChD,IAAI,EAAE,IAAI,CAAC;KACZ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpB;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,sEAAsE;IACtE,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,gDAAgD;IAChD,SAAS,EAAE,OAAO,CAAC;IACnB,6FAA6F;IAC7F,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6CAA6C;IAC7C,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAcF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC,CAsHhC"}
@@ -1,8 +1,15 @@
1
1
  // Note: explicit .js extension is required for Node ESM resolution.
2
2
  import { prepareCoreBorrow, prepareCoreDeposit } from "../actions/core.js";
3
3
  import { sendTx } from "../actions/tx.js";
4
+ import { waitForReceiptWithRetry } from "./receipt.js";
4
5
  import { SlippageExceededError } from "./types.js";
5
6
  // ---------------------------------------------------------------------------
7
+ // Helpers
8
+ // ---------------------------------------------------------------------------
9
+ function sleep(ms) {
10
+ return new Promise((resolve) => setTimeout(resolve, ms));
11
+ }
12
+ // ---------------------------------------------------------------------------
6
13
  // Executor
7
14
  // ---------------------------------------------------------------------------
8
15
  /**
@@ -18,14 +25,20 @@ import { SlippageExceededError } from "./types.js";
18
25
  * for slippage.
19
26
  *
20
27
  * If any step fails, execution stops and returns partial results.
28
+ *
29
+ * **Reliability features (v2):**
30
+ * - `receiptRetry`: configurable exponential backoff for receipt polling
31
+ * - `onPostBorrow`: hook for CLOB balance cache refresh after each borrow
32
+ * - `postBorrowDelayMs`: sleep after borrow for CLOB indexer sync
33
+ * - `startFromStep`: resume from a previously failed step index
21
34
  */
22
35
  export async function runLeverageLoop(params) {
23
- const { plan, positionId, publicClient, walletClient, coreAddress, account, onBuyStep, onProgress, confirmations = 1, maxSlippageBps, } = params;
36
+ const { plan, positionId, publicClient, walletClient, coreAddress, account, onBuyStep, onProgress, confirmations = 1, maxSlippageBps, receiptRetry, onPostBorrow, postBorrowDelayMs = 0, startFromStep = 0, } = params;
24
37
  const results = [];
25
38
  const totalSteps = plan.steps.length;
26
39
  // Track actual tokens from the most recent buy (for slippage-aware deposits).
27
40
  let lastBuyActualTokens;
28
- for (let i = 0; i < totalSteps; i++) {
41
+ for (let i = startFromStep; i < totalSteps; i++) {
29
42
  const step = plan.steps[i];
30
43
  try {
31
44
  if (step.kind === "buy") {
@@ -60,7 +73,7 @@ export async function runLeverageLoop(params) {
60
73
  amounts: [depositAmount],
61
74
  });
62
75
  const hash = await sendTx({ walletClient: walletClient, request: sim.request });
63
- await publicClient.waitForTransactionReceipt({ hash, confirmations });
76
+ await waitForReceiptWithRetry({ publicClient, hash, confirmations, retry: receiptRetry });
64
77
  const result = { step, hash };
65
78
  results.push(result);
66
79
  onProgress?.({ completed: i + 1, total: totalSteps, step, result });
@@ -74,10 +87,18 @@ export async function runLeverageLoop(params) {
74
87
  amount: step.collateralAmount,
75
88
  });
76
89
  const hash = await sendTx({ walletClient: walletClient, request: sim.request });
77
- await publicClient.waitForTransactionReceipt({ hash, confirmations });
90
+ await waitForReceiptWithRetry({ publicClient, hash, confirmations, retry: receiptRetry });
78
91
  const result = { step, hash };
79
92
  results.push(result);
80
93
  onProgress?.({ completed: i + 1, total: totalSteps, step, result });
94
+ // Post-borrow hook — let consumer refresh CLOB balance cache, etc.
95
+ if (onPostBorrow) {
96
+ await onPostBorrow({ step, hash });
97
+ }
98
+ // Post-borrow delay — give CLOB server time to re-index.
99
+ if (postBorrowDelayMs > 0) {
100
+ await sleep(postBorrowDelayMs);
101
+ }
81
102
  continue;
82
103
  }
83
104
  // Exhaustive check.
@@ -1 +1 @@
1
- {"version":3,"file":"execute.js","sourceRoot":"","sources":["../../src/leverage/execute.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"execute.js","sourceRoot":"","sources":["../../src/leverage/execute.ts"],"names":[],"mappings":"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"}
@@ -27,6 +27,8 @@ export type { PlanDeleverageFromChainParams, PlanLeverageFromChainParams, PlanLe
27
27
  export { planDeleverageFromChain, planLeverageFromChain, planLeverageFromChainPrecise, resolveLeveragePositionId, } from "./plan.js";
28
28
  export type { ReadErc1155IsApprovedForAllParams } from "./preflight.js";
29
29
  export { prepareApprovePositionsTokenIfNeeded, readErc1155IsApprovedForAll, } from "./preflight.js";
30
+ export type { ReceiptRetryOptions } from "./receipt.js";
31
+ export { DEFAULT_RECEIPT_RETRY, waitForReceiptWithRetry } from "./receipt.js";
30
32
  export type { BuyResult, DeleveragePlan, DeleverageStep, LeverageDirection, LeveragePlan, LeverageStep, LeverageStepResult, LeverageSummary, OnLeverageProgress, SellResult, } from "./types.js";
31
33
  export { collateralForDollars, DEFAULT_MAX_ITERATIONS, DEFAULT_MIN_STEP_COLLATERAL, PRICE_E8, pow10, SlippageExceededError, WAD, } from "./types.js";
32
34
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/leverage/index.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,YAAY,EACV,oBAAoB,EACpB,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,YAAY,EACV,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,YAAY,EACV,gBAAgB,EAChB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,gBAAgB,EAChB,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,WAAW,CAAC;AACnB,YAAY,EACV,6BAA6B,EAC7B,2BAA2B,EAC3B,kCAAkC,GACnC,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACrB,4BAA4B,EAC5B,yBAAyB,GAC1B,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,iCAAiC,EAAE,MAAM,gBAAgB,CAAC;AAExE,OAAO,EACL,oCAAoC,EACpC,2BAA2B,GAC5B,MAAM,gBAAgB,CAAC;AAExB,YAAY,EACV,SAAS,EACT,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,kBAAkB,EAClB,UAAU,GACX,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,2BAA2B,EAC3B,QAAQ,EACR,KAAK,EACL,qBAAqB,EACrB,GAAG,GACJ,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/leverage/index.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,YAAY,EACV,oBAAoB,EACpB,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,YAAY,EACV,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,YAAY,EACV,gBAAgB,EAChB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,gBAAgB,EAChB,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,WAAW,CAAC;AACnB,YAAY,EACV,6BAA6B,EAC7B,2BAA2B,EAC3B,kCAAkC,GACnC,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACrB,4BAA4B,EAC5B,yBAAyB,GAC1B,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,iCAAiC,EAAE,MAAM,gBAAgB,CAAC;AAExE,OAAO,EACL,oCAAoC,EACpC,2BAA2B,GAC5B,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAExD,OAAO,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAE9E,YAAY,EACV,SAAS,EACT,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,kBAAkB,EAClB,UAAU,GACX,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,2BAA2B,EAC3B,QAAQ,EACR,KAAK,EACL,qBAAqB,EACrB,GAAG,GACJ,MAAM,YAAY,CAAC"}
@@ -8,5 +8,7 @@ export { collateralForTokens, estimateLiquidationPrice, leverageScenario, maxLev
8
8
  export { planDeleverageFromChain, planLeverageFromChain, planLeverageFromChainPrecise, resolveLeveragePositionId, } from "./plan.js";
9
9
  // Preflight
10
10
  export { prepareApprovePositionsTokenIfNeeded, readErc1155IsApprovedForAll, } from "./preflight.js";
11
+ // Receipt retry
12
+ export { DEFAULT_RECEIPT_RETRY, waitForReceiptWithRetry } from "./receipt.js";
11
13
  export { collateralForDollars, DEFAULT_MAX_ITERATIONS, DEFAULT_MIN_STEP_COLLATERAL, PRICE_E8, pow10, SlippageExceededError, WAD, } from "./types.js";
12
14
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/leverage/index.ts"],"names":[],"mappings":"AAAA,oEAAoE;AA2BpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAK3D,YAAY;AACZ,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAM/C,YAAY;AACZ,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,gBAAgB,EAChB,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,WAAW,CAAC;AAMnB,qBAAqB;AACrB,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACrB,4BAA4B,EAC5B,yBAAyB,GAC1B,MAAM,WAAW,CAAC;AAEnB,YAAY;AACZ,OAAO,EACL,oCAAoC,EACpC,2BAA2B,GAC5B,MAAM,gBAAgB,CAAC;AAcxB,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,2BAA2B,EAC3B,QAAQ,EACR,KAAK,EACL,qBAAqB,EACrB,GAAG,GACJ,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/leverage/index.ts"],"names":[],"mappings":"AAAA,oEAAoE;AA2BpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAK3D,YAAY;AACZ,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAM/C,YAAY;AACZ,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,gBAAgB,EAChB,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,WAAW,CAAC;AAMnB,qBAAqB;AACrB,OAAO,EACL,uBAAuB,EACvB,qBAAqB,EACrB,4BAA4B,EAC5B,yBAAyB,GAC1B,MAAM,WAAW,CAAC;AAEnB,YAAY;AACZ,OAAO,EACL,oCAAoC,EACpC,2BAA2B,GAC5B,MAAM,gBAAgB,CAAC;AAExB,gBAAgB;AAChB,OAAO,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAc9E,OAAO,EACL,oBAAoB,EACpB,sBAAsB,EACtB,2BAA2B,EAC3B,QAAQ,EACR,KAAK,EACL,qBAAqB,EACrB,GAAG,GACJ,MAAM,YAAY,CAAC"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Robust transaction receipt waiting with exponential backoff.
3
+ *
4
+ * Polygon (and other L2s) occasionally drop RPC connections or return
5
+ * "receipt not found" transiently — even for successfully broadcast txs.
6
+ * This helper retries `waitForTransactionReceipt` so a single flaky RPC
7
+ * call doesn't abort a multi-step leverage/deleverage loop.
8
+ */
9
+ import type { Hash, PublicClient, TransactionReceipt } from "viem";
10
+ export type ReceiptRetryOptions = {
11
+ /** Maximum number of retry attempts (default: 5). */
12
+ maxRetries?: number;
13
+ /** Base delay in ms for first retry (default: 2_000). */
14
+ baseDelayMs?: number;
15
+ /** Maximum delay cap in ms (default: 32_000). */
16
+ maxDelayMs?: number;
17
+ /** Multiplier applied each retry (default: 2). */
18
+ backoffMultiplier?: number;
19
+ };
20
+ export declare const DEFAULT_RECEIPT_RETRY: Required<ReceiptRetryOptions>;
21
+ /**
22
+ * Wait for a transaction receipt with automatic retry and exponential backoff.
23
+ *
24
+ * This wraps `publicClient.waitForTransactionReceipt` and handles the common
25
+ * failure modes seen on Polygon:
26
+ * - "Transaction receipt not found" (RPC hasn't indexed the block yet)
27
+ * - Timeouts and connection drops
28
+ * - 502/503/504 gateway errors
29
+ *
30
+ * @throws The original error after all retries are exhausted.
31
+ */
32
+ export declare function waitForReceiptWithRetry(params: {
33
+ publicClient: Pick<PublicClient, "waitForTransactionReceipt">;
34
+ hash: Hash;
35
+ confirmations?: number;
36
+ retry?: ReceiptRetryOptions;
37
+ }): Promise<TransactionReceipt>;
38
+ //# sourceMappingURL=receipt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"receipt.d.ts","sourceRoot":"","sources":["../../src/leverage/receipt.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,MAAM,CAAC;AAMnE,MAAM,MAAM,mBAAmB,GAAG;IAChC,qDAAqD;IACrD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,QAAQ,CAAC,mBAAmB,CAK/D,CAAC;AA+CF;;;;;;;;;;GAUG;AACH,wBAAsB,uBAAuB,CAAC,MAAM,EAAE;IACpD,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,2BAA2B,CAAC,CAAC;IAC9D,IAAI,EAAE,IAAI,CAAC;IACX,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,mBAAmB,CAAC;CAC7B,GAAG,OAAO,CAAC,kBAAkB,CAAC,CA8C9B"}
@@ -0,0 +1,95 @@
1
+ // Note: explicit .js extension is required for Node ESM resolution.
2
+ export const DEFAULT_RECEIPT_RETRY = {
3
+ maxRetries: 5,
4
+ baseDelayMs: 2_000,
5
+ maxDelayMs: 32_000,
6
+ backoffMultiplier: 2,
7
+ };
8
+ // ---------------------------------------------------------------------------
9
+ // Helper
10
+ // ---------------------------------------------------------------------------
11
+ function sleep(ms) {
12
+ return new Promise((resolve) => setTimeout(resolve, ms));
13
+ }
14
+ /**
15
+ * Classify whether an error from `waitForTransactionReceipt` is transient
16
+ * (worth retrying) vs permanent (should be thrown immediately).
17
+ */
18
+ function isTransientReceiptError(error) {
19
+ if (!(error instanceof Error))
20
+ return true; // unknown shape → retry
21
+ const msg = error.message.toLowerCase();
22
+ // Permanent: tx reverted on-chain → retrying won't help.
23
+ if (msg.includes("reverted") || msg.includes("execution reverted"))
24
+ return false;
25
+ // Transient: receipt not found, timeout, network issues.
26
+ if (msg.includes("not found") ||
27
+ msg.includes("could not be found") ||
28
+ msg.includes("timeout") ||
29
+ msg.includes("timed out") ||
30
+ msg.includes("econnrefused") ||
31
+ msg.includes("econnreset") ||
32
+ msg.includes("network") ||
33
+ msg.includes("fetch") ||
34
+ msg.includes("502") ||
35
+ msg.includes("503") ||
36
+ msg.includes("504") ||
37
+ msg.includes("rate limit")) {
38
+ return true;
39
+ }
40
+ // Default: retry (better to retry once too many than abort a valid tx).
41
+ return true;
42
+ }
43
+ // ---------------------------------------------------------------------------
44
+ // Main export
45
+ // ---------------------------------------------------------------------------
46
+ /**
47
+ * Wait for a transaction receipt with automatic retry and exponential backoff.
48
+ *
49
+ * This wraps `publicClient.waitForTransactionReceipt` and handles the common
50
+ * failure modes seen on Polygon:
51
+ * - "Transaction receipt not found" (RPC hasn't indexed the block yet)
52
+ * - Timeouts and connection drops
53
+ * - 502/503/504 gateway errors
54
+ *
55
+ * @throws The original error after all retries are exhausted.
56
+ */
57
+ export async function waitForReceiptWithRetry(params) {
58
+ const { publicClient, hash, confirmations = 1 } = params;
59
+ const opts = { ...DEFAULT_RECEIPT_RETRY, ...params.retry };
60
+ let lastError;
61
+ for (let attempt = 0; attempt <= opts.maxRetries; attempt++) {
62
+ try {
63
+ const receipt = await publicClient.waitForTransactionReceipt({
64
+ hash,
65
+ confirmations,
66
+ });
67
+ return receipt;
68
+ }
69
+ catch (error) {
70
+ lastError = error;
71
+ // Don't retry permanent errors (e.g. on-chain revert).
72
+ if (!isTransientReceiptError(error)) {
73
+ throw error;
74
+ }
75
+ // Don't retry if we've exhausted attempts.
76
+ if (attempt >= opts.maxRetries) {
77
+ break;
78
+ }
79
+ // Exponential backoff with cap.
80
+ const delay = Math.min(opts.baseDelayMs * opts.backoffMultiplier ** attempt, opts.maxDelayMs);
81
+ // No-op in production; useful for debugging.
82
+ // console.debug(`[receipt-retry] attempt ${attempt + 1}/${opts.maxRetries} for ${hash}, waiting ${delay}ms`);
83
+ await sleep(delay);
84
+ }
85
+ }
86
+ // Wrap with a descriptive message so the caller knows retries were exhausted.
87
+ const cause = lastError instanceof Error ? lastError : new Error(String(lastError));
88
+ const wrapped = new Error(`Transaction receipt not found for ${hash} after ${opts.maxRetries + 1} attempts (${opts.maxRetries} retries). ` +
89
+ `The transaction was broadcast successfully — it may still confirm. ` +
90
+ `Original error: ${cause.message}`);
91
+ wrapped.cause = cause;
92
+ wrapped.hash = hash;
93
+ throw wrapped;
94
+ }
95
+ //# sourceMappingURL=receipt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"receipt.js","sourceRoot":"","sources":["../../src/leverage/receipt.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@varla/sdk",
3
- "version": "2.15.1",
3
+ "version": "2.16.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -7,10 +7,12 @@
7
7
  * to SDK action helpers and off-chain operations (sell) to a consumer callback.
8
8
  */
9
9
 
10
- import type { Address, PublicClient, WalletClient } from "viem";
10
+ import type { Address, Hash, PublicClient, WalletClient } from "viem";
11
11
 
12
12
  import { prepareCoreRepay, prepareCoreWithdraw } from "../actions/core.js";
13
13
  import { sendTx } from "../actions/tx.js";
14
+ import type { ReceiptRetryOptions } from "./receipt.js";
15
+ import { waitForReceiptWithRetry } from "./receipt.js";
14
16
  import type { DeleveragePlan, DeleverageStep, SellResult } from "./types.js";
15
17
  import { SlippageExceededError } from "./types.js";
16
18
 
@@ -19,7 +21,7 @@ export type OnDeleverageProgress = (info: {
19
21
  total: number;
20
22
  step: DeleverageStep;
21
23
  // keep symmetry with leverage executor
22
- hash?: `0x${string}`;
24
+ hash?: Hash;
23
25
  sellResult?: SellResult;
24
26
  }) => void;
25
27
 
@@ -40,14 +42,81 @@ export type RunDeleverageLoopParams = {
40
42
  * the loop aborts with a `SlippageExceededError`.
41
43
  */
42
44
  maxSlippageBps?: bigint;
45
+
46
+ // --- NEW: reliability & hooks ------------------------------------------------
47
+
48
+ /**
49
+ * Receipt retry options for `waitForTransactionReceipt`.
50
+ *
51
+ * By default the executor retries up to 5 times with exponential backoff
52
+ * (2s → 4s → 8s → 16s → 32s). Set `{ maxRetries: 0 }` to disable retries.
53
+ */
54
+ receiptRetry?: ReceiptRetryOptions;
55
+
56
+ /**
57
+ * Hook called after each successful withdraw step (receipt confirmed),
58
+ * before the next sell step. Use this to refresh CLOB position caches, etc.
59
+ *
60
+ * @param info Withdraw step details and tx hash.
61
+ */
62
+ onPostWithdraw?: (info: {
63
+ step: Extract<DeleverageStep, { kind: "withdraw" }>;
64
+ hash: Hash;
65
+ }) => Promise<void>;
66
+
67
+ /**
68
+ * Milliseconds to sleep after each withdraw confirmation before proceeding
69
+ * to the next sell step. Gives the CLOB server time to re-index on-chain
70
+ * token balance changes.
71
+ *
72
+ * Applied AFTER `onPostWithdraw` (if provided). Default: 0 (no delay).
73
+ */
74
+ postWithdrawDelayMs?: number;
75
+
76
+ /**
77
+ * Resume a previously failed execution by skipping steps before this index.
78
+ *
79
+ * When a previous call returned `{ failedAtStep: N }`, pass `startFromStep: N`
80
+ * to resume from exactly where it left off.
81
+ *
82
+ * Default: 0 (run all steps from the beginning).
83
+ */
84
+ startFromStep?: number;
43
85
  };
44
86
 
45
87
  export type RunDeleverageLoopResult = {
46
88
  completed: boolean;
89
+ /** If execution stopped early, the index of the failed step (absolute). */
47
90
  failedAtStep?: number;
48
91
  error?: unknown;
49
92
  };
50
93
 
94
+ // ---------------------------------------------------------------------------
95
+ // Helpers
96
+ // ---------------------------------------------------------------------------
97
+
98
+ function sleep(ms: number): Promise<void> {
99
+ return new Promise((resolve) => setTimeout(resolve, ms));
100
+ }
101
+
102
+ // ---------------------------------------------------------------------------
103
+ // Executor
104
+ // ---------------------------------------------------------------------------
105
+
106
+ /**
107
+ * Execute a deleverage (unwind) loop plan step-by-step.
108
+ *
109
+ * For each step:
110
+ * - "withdraw": prepares + sends `VarlaCore.withdraw()` tx
111
+ * - "sell": delegates to `onSellStep` callback (off-chain CLOB trade)
112
+ * - "repay": prepares + sends `VarlaCore.repay()` tx
113
+ *
114
+ * **Reliability features (v2):**
115
+ * - `receiptRetry`: configurable exponential backoff for receipt polling
116
+ * - `onPostWithdraw`: hook for CLOB cache refresh after each withdraw
117
+ * - `postWithdrawDelayMs`: sleep after withdraw for CLOB indexer sync
118
+ * - `startFromStep`: resume from a previously failed step index
119
+ */
51
120
  export async function runDeleverageLoop(
52
121
  params: RunDeleverageLoopParams,
53
122
  ): Promise<RunDeleverageLoopResult> {
@@ -62,6 +131,10 @@ export async function runDeleverageLoop(
62
131
  onProgress,
63
132
  confirmations = 1,
64
133
  maxSlippageBps,
134
+ receiptRetry,
135
+ onPostWithdraw,
136
+ postWithdrawDelayMs = 0,
137
+ startFromStep = 0,
65
138
  } = params;
66
139
 
67
140
  const total = plan.steps.length;
@@ -69,7 +142,7 @@ export async function runDeleverageLoop(
69
142
  // Track actual collateral proceeds from the most recent sell step.
70
143
  let lastSellActualCollateral: bigint | undefined;
71
144
 
72
- for (let i = 0; i < total; i++) {
145
+ for (let i = startFromStep; i < total; i++) {
73
146
  const step = plan.steps[i]!;
74
147
  try {
75
148
  if (step.kind === "withdraw") {
@@ -81,8 +154,20 @@ export async function runDeleverageLoop(
81
154
  amounts: [step.amount],
82
155
  });
83
156
  const hash = await sendTx({ walletClient: walletClient as any, request: sim.request });
84
- await (publicClient as any).waitForTransactionReceipt({ hash, confirmations });
157
+ await waitForReceiptWithRetry({ publicClient, hash, confirmations, retry: receiptRetry });
158
+
85
159
  onProgress?.({ completed: i + 1, total, step, hash });
160
+
161
+ // Post-withdraw hook — let consumer refresh CLOB token balance cache, etc.
162
+ if (onPostWithdraw) {
163
+ await onPostWithdraw({ step, hash });
164
+ }
165
+
166
+ // Post-withdraw delay — give CLOB server time to re-index.
167
+ if (postWithdrawDelayMs > 0) {
168
+ await sleep(postWithdrawDelayMs);
169
+ }
170
+
86
171
  continue;
87
172
  }
88
173
 
@@ -118,7 +203,8 @@ export async function runDeleverageLoop(
118
203
  amount: repayAmount,
119
204
  });
120
205
  const hash = await sendTx({ walletClient: walletClient as any, request: sim.request });
121
- await (publicClient as any).waitForTransactionReceipt({ hash, confirmations });
206
+ await waitForReceiptWithRetry({ publicClient, hash, confirmations, retry: receiptRetry });
207
+
122
208
  onProgress?.({ completed: i + 1, total, step, hash });
123
209
  continue;
124
210
  }
@@ -8,10 +8,12 @@
8
8
  * callback.
9
9
  */
10
10
 
11
- import type { Address, PublicClient, WalletClient } from "viem";
11
+ import type { Address, Hash, PublicClient, WalletClient } from "viem";
12
12
 
13
13
  import { prepareCoreBorrow, prepareCoreDeposit } from "../actions/core.js";
14
14
  import { sendTx } from "../actions/tx.js";
15
+ import type { ReceiptRetryOptions } from "./receipt.js";
16
+ import { waitForReceiptWithRetry } from "./receipt.js";
15
17
  import type {
16
18
  BuyResult,
17
19
  LeveragePlan,
@@ -55,19 +57,69 @@ export type RunLeverageLoopParams = {
55
57
  * the loop aborts with a `SlippageExceededError`.
56
58
  */
57
59
  maxSlippageBps?: bigint;
60
+
61
+ // --- NEW: reliability & hooks ------------------------------------------------
62
+
63
+ /**
64
+ * Receipt retry options for `waitForTransactionReceipt`.
65
+ *
66
+ * By default the executor retries up to 5 times with exponential backoff
67
+ * (2s → 4s → 8s → 16s → 32s). Set `{ maxRetries: 0 }` to disable retries.
68
+ */
69
+ receiptRetry?: ReceiptRetryOptions;
70
+
71
+ /**
72
+ * Hook called after each successful borrow step (receipt confirmed),
73
+ * before the next buy step. Use this to refresh CLOB balance caches,
74
+ * wait for off-chain indexers, etc.
75
+ *
76
+ * @param info Borrow step details and tx hash.
77
+ */
78
+ onPostBorrow?: (info: {
79
+ step: Extract<LeverageStep, { kind: "borrow" }>;
80
+ hash: Hash;
81
+ }) => Promise<void>;
82
+
83
+ /**
84
+ * Milliseconds to sleep after each borrow confirmation before proceeding
85
+ * to the next buy step. Gives the CLOB server time to re-index on-chain
86
+ * balance changes.
87
+ *
88
+ * Applied AFTER `onPostBorrow` (if provided). Default: 0 (no delay).
89
+ */
90
+ postBorrowDelayMs?: number;
91
+
92
+ /**
93
+ * Resume a previously failed execution by skipping steps before this index.
94
+ *
95
+ * When a previous call returned `{ failedAtStep: N }`, pass `startFromStep: N`
96
+ * to resume from exactly where it left off. Steps before `startFromStep` are
97
+ * skipped (their results are NOT included in the returned array).
98
+ *
99
+ * Default: 0 (run all steps from the beginning).
100
+ */
101
+ startFromStep?: number;
58
102
  };
59
103
 
60
104
  export type RunLeverageLoopResult = {
61
- /** Results for each step, aligned to `plan.steps`. */
105
+ /** Results for each step executed (starting from `startFromStep`). */
62
106
  results: LeverageStepResult[];
63
107
  /** Whether all steps completed successfully. */
64
108
  completed: boolean;
65
- /** If execution stopped early, the index of the failed step. */
109
+ /** If execution stopped early, the index of the failed step (absolute, relative to plan). */
66
110
  failedAtStep?: number;
67
111
  /** If execution stopped early, the error. */
68
112
  error?: unknown;
69
113
  };
70
114
 
115
+ // ---------------------------------------------------------------------------
116
+ // Helpers
117
+ // ---------------------------------------------------------------------------
118
+
119
+ function sleep(ms: number): Promise<void> {
120
+ return new Promise((resolve) => setTimeout(resolve, ms));
121
+ }
122
+
71
123
  // ---------------------------------------------------------------------------
72
124
  // Executor
73
125
  // ---------------------------------------------------------------------------
@@ -85,6 +137,12 @@ export type RunLeverageLoopResult = {
85
137
  * for slippage.
86
138
  *
87
139
  * If any step fails, execution stops and returns partial results.
140
+ *
141
+ * **Reliability features (v2):**
142
+ * - `receiptRetry`: configurable exponential backoff for receipt polling
143
+ * - `onPostBorrow`: hook for CLOB balance cache refresh after each borrow
144
+ * - `postBorrowDelayMs`: sleep after borrow for CLOB indexer sync
145
+ * - `startFromStep`: resume from a previously failed step index
88
146
  */
89
147
  export async function runLeverageLoop(
90
148
  params: RunLeverageLoopParams,
@@ -100,6 +158,10 @@ export async function runLeverageLoop(
100
158
  onProgress,
101
159
  confirmations = 1,
102
160
  maxSlippageBps,
161
+ receiptRetry,
162
+ onPostBorrow,
163
+ postBorrowDelayMs = 0,
164
+ startFromStep = 0,
103
165
  } = params;
104
166
 
105
167
  const results: LeverageStepResult[] = [];
@@ -108,7 +170,7 @@ export async function runLeverageLoop(
108
170
  // Track actual tokens from the most recent buy (for slippage-aware deposits).
109
171
  let lastBuyActualTokens: bigint | undefined;
110
172
 
111
- for (let i = 0; i < totalSteps; i++) {
173
+ for (let i = startFromStep; i < totalSteps; i++) {
112
174
  const step = plan.steps[i]!;
113
175
 
114
176
  try {
@@ -151,7 +213,7 @@ export async function runLeverageLoop(
151
213
  });
152
214
 
153
215
  const hash = await sendTx({ walletClient: walletClient as any, request: sim.request });
154
- await (publicClient as any).waitForTransactionReceipt({ hash, confirmations });
216
+ await waitForReceiptWithRetry({ publicClient, hash, confirmations, retry: receiptRetry });
155
217
 
156
218
  const result: LeverageStepResult = { step, hash };
157
219
  results.push(result);
@@ -169,12 +231,23 @@ export async function runLeverageLoop(
169
231
  });
170
232
 
171
233
  const hash = await sendTx({ walletClient: walletClient as any, request: sim.request });
172
- await (publicClient as any).waitForTransactionReceipt({ hash, confirmations });
234
+ await waitForReceiptWithRetry({ publicClient, hash, confirmations, retry: receiptRetry });
173
235
 
174
236
  const result: LeverageStepResult = { step, hash };
175
237
  results.push(result);
176
238
 
177
239
  onProgress?.({ completed: i + 1, total: totalSteps, step, result });
240
+
241
+ // Post-borrow hook — let consumer refresh CLOB balance cache, etc.
242
+ if (onPostBorrow) {
243
+ await onPostBorrow({ step, hash });
244
+ }
245
+
246
+ // Post-borrow delay — give CLOB server time to re-index.
247
+ if (postBorrowDelayMs > 0) {
248
+ await sleep(postBorrowDelayMs);
249
+ }
250
+
178
251
  continue;
179
252
  }
180
253
 
@@ -65,6 +65,9 @@ export {
65
65
  prepareApprovePositionsTokenIfNeeded,
66
66
  readErc1155IsApprovedForAll,
67
67
  } from "./preflight.js";
68
+ export type { ReceiptRetryOptions } from "./receipt.js";
69
+ // Receipt retry
70
+ export { DEFAULT_RECEIPT_RETRY, waitForReceiptWithRetry } from "./receipt.js";
68
71
  // Types + constants
69
72
  export type {
70
73
  BuyResult,
@@ -0,0 +1,143 @@
1
+ // Note: explicit .js extension is required for Node ESM resolution.
2
+
3
+ /**
4
+ * Robust transaction receipt waiting with exponential backoff.
5
+ *
6
+ * Polygon (and other L2s) occasionally drop RPC connections or return
7
+ * "receipt not found" transiently — even for successfully broadcast txs.
8
+ * This helper retries `waitForTransactionReceipt` so a single flaky RPC
9
+ * call doesn't abort a multi-step leverage/deleverage loop.
10
+ */
11
+
12
+ import type { Hash, PublicClient, TransactionReceipt } from "viem";
13
+
14
+ // ---------------------------------------------------------------------------
15
+ // Types
16
+ // ---------------------------------------------------------------------------
17
+
18
+ export type ReceiptRetryOptions = {
19
+ /** Maximum number of retry attempts (default: 5). */
20
+ maxRetries?: number;
21
+ /** Base delay in ms for first retry (default: 2_000). */
22
+ baseDelayMs?: number;
23
+ /** Maximum delay cap in ms (default: 32_000). */
24
+ maxDelayMs?: number;
25
+ /** Multiplier applied each retry (default: 2). */
26
+ backoffMultiplier?: number;
27
+ };
28
+
29
+ export const DEFAULT_RECEIPT_RETRY: Required<ReceiptRetryOptions> = {
30
+ maxRetries: 5,
31
+ baseDelayMs: 2_000,
32
+ maxDelayMs: 32_000,
33
+ backoffMultiplier: 2,
34
+ };
35
+
36
+ // ---------------------------------------------------------------------------
37
+ // Helper
38
+ // ---------------------------------------------------------------------------
39
+
40
+ function sleep(ms: number): Promise<void> {
41
+ return new Promise((resolve) => setTimeout(resolve, ms));
42
+ }
43
+
44
+ /**
45
+ * Classify whether an error from `waitForTransactionReceipt` is transient
46
+ * (worth retrying) vs permanent (should be thrown immediately).
47
+ */
48
+ function isTransientReceiptError(error: unknown): boolean {
49
+ if (!(error instanceof Error)) return true; // unknown shape → retry
50
+ const msg = error.message.toLowerCase();
51
+
52
+ // Permanent: tx reverted on-chain → retrying won't help.
53
+ if (msg.includes("reverted") || msg.includes("execution reverted")) return false;
54
+
55
+ // Transient: receipt not found, timeout, network issues.
56
+ if (
57
+ msg.includes("not found") ||
58
+ msg.includes("could not be found") ||
59
+ msg.includes("timeout") ||
60
+ msg.includes("timed out") ||
61
+ msg.includes("econnrefused") ||
62
+ msg.includes("econnreset") ||
63
+ msg.includes("network") ||
64
+ msg.includes("fetch") ||
65
+ msg.includes("502") ||
66
+ msg.includes("503") ||
67
+ msg.includes("504") ||
68
+ msg.includes("rate limit")
69
+ ) {
70
+ return true;
71
+ }
72
+
73
+ // Default: retry (better to retry once too many than abort a valid tx).
74
+ return true;
75
+ }
76
+
77
+ // ---------------------------------------------------------------------------
78
+ // Main export
79
+ // ---------------------------------------------------------------------------
80
+
81
+ /**
82
+ * Wait for a transaction receipt with automatic retry and exponential backoff.
83
+ *
84
+ * This wraps `publicClient.waitForTransactionReceipt` and handles the common
85
+ * failure modes seen on Polygon:
86
+ * - "Transaction receipt not found" (RPC hasn't indexed the block yet)
87
+ * - Timeouts and connection drops
88
+ * - 502/503/504 gateway errors
89
+ *
90
+ * @throws The original error after all retries are exhausted.
91
+ */
92
+ export async function waitForReceiptWithRetry(params: {
93
+ publicClient: Pick<PublicClient, "waitForTransactionReceipt">;
94
+ hash: Hash;
95
+ confirmations?: number;
96
+ retry?: ReceiptRetryOptions;
97
+ }): Promise<TransactionReceipt> {
98
+ const { publicClient, hash, confirmations = 1 } = params;
99
+ const opts = { ...DEFAULT_RECEIPT_RETRY, ...params.retry };
100
+
101
+ let lastError: unknown;
102
+
103
+ for (let attempt = 0; attempt <= opts.maxRetries; attempt++) {
104
+ try {
105
+ const receipt = await (publicClient as any).waitForTransactionReceipt({
106
+ hash,
107
+ confirmations,
108
+ });
109
+ return receipt as TransactionReceipt;
110
+ } catch (error) {
111
+ lastError = error;
112
+
113
+ // Don't retry permanent errors (e.g. on-chain revert).
114
+ if (!isTransientReceiptError(error)) {
115
+ throw error;
116
+ }
117
+
118
+ // Don't retry if we've exhausted attempts.
119
+ if (attempt >= opts.maxRetries) {
120
+ break;
121
+ }
122
+
123
+ // Exponential backoff with cap.
124
+ const delay = Math.min(opts.baseDelayMs * opts.backoffMultiplier ** attempt, opts.maxDelayMs);
125
+
126
+ // No-op in production; useful for debugging.
127
+ // console.debug(`[receipt-retry] attempt ${attempt + 1}/${opts.maxRetries} for ${hash}, waiting ${delay}ms`);
128
+
129
+ await sleep(delay);
130
+ }
131
+ }
132
+
133
+ // Wrap with a descriptive message so the caller knows retries were exhausted.
134
+ const cause = lastError instanceof Error ? lastError : new Error(String(lastError));
135
+ const wrapped = new Error(
136
+ `Transaction receipt not found for ${hash} after ${opts.maxRetries + 1} attempts (${opts.maxRetries} retries). ` +
137
+ `The transaction was broadcast successfully — it may still confirm. ` +
138
+ `Original error: ${cause.message}`,
139
+ );
140
+ (wrapped as any).cause = cause;
141
+ (wrapped as any).hash = hash;
142
+ throw wrapped;
143
+ }