@varla/sdk 2.15.1 → 2.17.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 +21 -0
- package/dist/addresses/polygon.js +12 -12
- package/dist/addresses/polygonProxy.js +12 -12
- package/dist/leverage/deleverageExecute.d.ts +54 -2
- package/dist/leverage/deleverageExecute.d.ts.map +1 -1
- package/dist/leverage/deleverageExecute.js +36 -4
- package/dist/leverage/deleverageExecute.js.map +1 -1
- package/dist/leverage/execute.d.ts +48 -3
- package/dist/leverage/execute.d.ts.map +1 -1
- package/dist/leverage/execute.js +25 -4
- package/dist/leverage/execute.js.map +1 -1
- package/dist/leverage/index.d.ts +2 -0
- package/dist/leverage/index.d.ts.map +1 -1
- package/dist/leverage/index.js +2 -0
- package/dist/leverage/index.js.map +1 -1
- package/dist/leverage/receipt.d.ts +38 -0
- package/dist/leverage/receipt.d.ts.map +1 -0
- package/dist/leverage/receipt.js +95 -0
- package/dist/leverage/receipt.js.map +1 -0
- package/package.json +1 -1
- package/src/addresses/polygon.json +10 -10
- package/src/addresses/polygon.ts +12 -12
- package/src/addresses/polygonProxy.ts +12 -12
- package/src/leverage/deleverageExecute.ts +91 -5
- package/src/leverage/execute.ts +79 -6
- package/src/leverage/index.ts +3 -0
- package/src/leverage/receipt.ts +143 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# @varla-xyz/protocol
|
|
2
2
|
|
|
3
|
+
## 2.17.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Updated Polygon deployment records for Varla protocol core, pool, oracle, liquidator, and related proxy/admin contracts.
|
|
8
|
+
- Refreshed Polygon adapter deployments, including Polymarket CTF and NegRisk merge adapters, and OracleUpdaterRouter.
|
|
9
|
+
- Added a new solc input artifact for Polygon deployments.
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 5887995: Updated Polygon address books with newly deployed contract addresses.
|
|
14
|
+
- Refreshed Polygon and Polygon proxy contract address mappings.
|
|
15
|
+
## 2.16.0
|
|
16
|
+
|
|
17
|
+
### Minor Changes
|
|
18
|
+
|
|
19
|
+
- 42777b6: Add receipt retry support and post-borrow/withdraw hooks to leverage loop executors with resumable step execution.
|
|
20
|
+
|
|
21
|
+
- Added receipt retry helper utilities and exports for robust transaction receipt polling
|
|
22
|
+
- Extended leverage and deleverage loop params with retry options, post-step hooks, delays, and resume-from-step support
|
|
23
|
+
|
|
3
24
|
## 2.15.1
|
|
4
25
|
|
|
5
26
|
### Patch Changes
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
export const polygon = {
|
|
2
|
-
accessManager: "
|
|
3
|
-
oracle: "
|
|
4
|
-
core: "
|
|
5
|
-
pool: "
|
|
6
|
-
oracleUpdaterRouter: "
|
|
7
|
-
interestRateStrategy: "
|
|
8
|
-
proxyAdmin: "
|
|
9
|
-
liquidator: "
|
|
10
|
-
mergeLiquidator: "
|
|
11
|
-
convertLiquidator: "
|
|
12
|
-
polymarketCtfAdapter: "
|
|
13
|
-
negRiskMergeAdapter: "
|
|
2
|
+
accessManager: "0xF5Acd8496248bb911fd1dE37f12F27Ae2D4EE973",
|
|
3
|
+
oracle: "0x93275d07182EF45E79Ac976Ab3b8b8eD8E50966e",
|
|
4
|
+
core: "0xBCF248890AC7BE6A54b8E94775C3fD59Cf689340",
|
|
5
|
+
pool: "0xB1820B7F807853a03a6a76a5694622F499F71428",
|
|
6
|
+
oracleUpdaterRouter: "0x67c30c998d549be0efcaAE01623720BB081fC702",
|
|
7
|
+
interestRateStrategy: "0x4c728684f72A232a125ABCC726A5b94768815efE",
|
|
8
|
+
proxyAdmin: "0xEB21e1513F0b522A78d8b8285094e89563e164C6",
|
|
9
|
+
liquidator: "0xa1d30AEC3106bfe10311D290228F342727Cae5cF",
|
|
10
|
+
mergeLiquidator: "0xDa929503D720653433eb9DB273E5a42d8C14aF90",
|
|
11
|
+
convertLiquidator: "0xA59e346f4C663AF4cF9B3F302B3eAd460101d709",
|
|
12
|
+
polymarketCtfAdapter: "0x1715CC1c9797098F77993d6aa0D190F323aa9421",
|
|
13
|
+
negRiskMergeAdapter: "0x3611E0AA36ebEFe3b555Db9b5BDcB071Fb64a477",
|
|
14
14
|
};
|
|
15
15
|
//# sourceMappingURL=polygon.js.map
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
export const polygonProxy = {
|
|
2
|
-
accessManager: "
|
|
3
|
-
oracle: "
|
|
4
|
-
core: "
|
|
5
|
-
pool: "
|
|
6
|
-
oracleUpdaterRouter: "
|
|
7
|
-
interestRateStrategy: "
|
|
8
|
-
proxyAdmin: "
|
|
9
|
-
liquidator: "
|
|
10
|
-
mergeLiquidator: "
|
|
11
|
-
convertLiquidator: "
|
|
12
|
-
polymarketCtfAdapter: "
|
|
13
|
-
negRiskMergeAdapter: "
|
|
2
|
+
accessManager: "0xF5Acd8496248bb911fd1dE37f12F27Ae2D4EE973",
|
|
3
|
+
oracle: "0xf507345D044D29DE9696c9fce02E9202cB902107",
|
|
4
|
+
core: "0x760453D0380222AaFB959145c20C9B32EBb1B636",
|
|
5
|
+
pool: "0x0fe32e729aE12600Ec8CbE6950456171C98120AE",
|
|
6
|
+
oracleUpdaterRouter: "0x67c30c998d549be0efcaAE01623720BB081fC702",
|
|
7
|
+
interestRateStrategy: "0xf3aac20247fd73ae5C8ACFA0Cf25cfC463332E82",
|
|
8
|
+
proxyAdmin: "0xEB21e1513F0b522A78d8b8285094e89563e164C6",
|
|
9
|
+
liquidator: "0x76B21eFF6F232CB9b647007A5E505a5392C7417F",
|
|
10
|
+
mergeLiquidator: "0xd0017264cd4Ee870F6869F1ab47f6ff579c31ba5",
|
|
11
|
+
convertLiquidator: "0x68421cDD230e550ac467Ae980454032bf8CE3651",
|
|
12
|
+
polymarketCtfAdapter: "0x1715CC1c9797098F77993d6aa0D190F323aa9421",
|
|
13
|
+
negRiskMergeAdapter: "0x3611E0AA36ebEFe3b555Db9b5BDcB071Fb64a477",
|
|
14
14
|
};
|
|
15
15
|
//# sourceMappingURL=polygonProxy.js.map
|
|
@@ -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?:
|
|
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;
|
|
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 =
|
|
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
|
|
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
|
|
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":"AAAA,oEAAoE;AAWpE,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC3E,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;
|
|
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
|
|
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;
|
|
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"}
|
package/dist/leverage/execute.js
CHANGED
|
@@ -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 =
|
|
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
|
|
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
|
|
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":"AAAA,oEAAoE;AAYpE,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC3E,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"execute.js","sourceRoot":"","sources":["../../src/leverage/execute.ts"],"names":[],"mappings":"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"}
|
package/dist/leverage/index.d.ts
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/leverage/index.js
CHANGED
|
@@ -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;
|
|
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
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
"generatedAt": "",
|
|
4
4
|
"deploymentsDir": "polygon",
|
|
5
5
|
"contracts": {
|
|
6
|
-
"VarlaAccessManager": "
|
|
7
|
-
"VarlaOracle": "
|
|
8
|
-
"VarlaCore": "
|
|
9
|
-
"VarlaPool": "
|
|
10
|
-
"OracleUpdaterRouter": "
|
|
11
|
-
"VarlaLiquidator": "
|
|
12
|
-
"VarlaMergeLiquidator": "
|
|
13
|
-
"VarlaConvertLiquidator": "
|
|
14
|
-
"PolymarketCtfAdapter": "
|
|
15
|
-
"PolymarketNegRiskMergeAdapter": "
|
|
6
|
+
"VarlaAccessManager": "0xF5Acd8496248bb911fd1dE37f12F27Ae2D4EE973",
|
|
7
|
+
"VarlaOracle": "0x93275d07182EF45E79Ac976Ab3b8b8eD8E50966e",
|
|
8
|
+
"VarlaCore": "0xBCF248890AC7BE6A54b8E94775C3fD59Cf689340",
|
|
9
|
+
"VarlaPool": "0xB1820B7F807853a03a6a76a5694622F499F71428",
|
|
10
|
+
"OracleUpdaterRouter": "0x67c30c998d549be0efcaAE01623720BB081fC702",
|
|
11
|
+
"VarlaLiquidator": "0xa1d30AEC3106bfe10311D290228F342727Cae5cF",
|
|
12
|
+
"VarlaMergeLiquidator": "0xDa929503D720653433eb9DB273E5a42d8C14aF90",
|
|
13
|
+
"VarlaConvertLiquidator": "0xA59e346f4C663AF4cF9B3F302B3eAd460101d709",
|
|
14
|
+
"PolymarketCtfAdapter": "0x1715CC1c9797098F77993d6aa0D190F323aa9421",
|
|
15
|
+
"PolymarketNegRiskMergeAdapter": "0x3611E0AA36ebEFe3b555Db9b5BDcB071Fb64a477"
|
|
16
16
|
}
|
|
17
17
|
}
|
package/src/addresses/polygon.ts
CHANGED
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
import type { AddressBook } from "../types";
|
|
3
3
|
|
|
4
4
|
export const polygon: AddressBook = {
|
|
5
|
-
accessManager: "
|
|
6
|
-
oracle: "
|
|
7
|
-
core: "
|
|
8
|
-
pool: "
|
|
9
|
-
oracleUpdaterRouter: "
|
|
10
|
-
interestRateStrategy: "
|
|
11
|
-
proxyAdmin: "
|
|
12
|
-
liquidator: "
|
|
13
|
-
mergeLiquidator: "
|
|
14
|
-
convertLiquidator: "
|
|
15
|
-
polymarketCtfAdapter: "
|
|
16
|
-
negRiskMergeAdapter: "
|
|
5
|
+
accessManager: "0xF5Acd8496248bb911fd1dE37f12F27Ae2D4EE973",
|
|
6
|
+
oracle: "0x93275d07182EF45E79Ac976Ab3b8b8eD8E50966e",
|
|
7
|
+
core: "0xBCF248890AC7BE6A54b8E94775C3fD59Cf689340",
|
|
8
|
+
pool: "0xB1820B7F807853a03a6a76a5694622F499F71428",
|
|
9
|
+
oracleUpdaterRouter: "0x67c30c998d549be0efcaAE01623720BB081fC702",
|
|
10
|
+
interestRateStrategy: "0x4c728684f72A232a125ABCC726A5b94768815efE",
|
|
11
|
+
proxyAdmin: "0xEB21e1513F0b522A78d8b8285094e89563e164C6",
|
|
12
|
+
liquidator: "0xa1d30AEC3106bfe10311D290228F342727Cae5cF",
|
|
13
|
+
mergeLiquidator: "0xDa929503D720653433eb9DB273E5a42d8C14aF90",
|
|
14
|
+
convertLiquidator: "0xA59e346f4C663AF4cF9B3F302B3eAd460101d709",
|
|
15
|
+
polymarketCtfAdapter: "0x1715CC1c9797098F77993d6aa0D190F323aa9421",
|
|
16
|
+
negRiskMergeAdapter: "0x3611E0AA36ebEFe3b555Db9b5BDcB071Fb64a477",
|
|
17
17
|
};
|
|
18
18
|
|
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
import type { AddressBook } from "../types";
|
|
3
3
|
|
|
4
4
|
export const polygonProxy: AddressBook = {
|
|
5
|
-
accessManager: "
|
|
6
|
-
oracle: "
|
|
7
|
-
core: "
|
|
8
|
-
pool: "
|
|
9
|
-
oracleUpdaterRouter: "
|
|
10
|
-
interestRateStrategy: "
|
|
11
|
-
proxyAdmin: "
|
|
12
|
-
liquidator: "
|
|
13
|
-
mergeLiquidator: "
|
|
14
|
-
convertLiquidator: "
|
|
15
|
-
polymarketCtfAdapter: "
|
|
16
|
-
negRiskMergeAdapter: "
|
|
5
|
+
accessManager: "0xF5Acd8496248bb911fd1dE37f12F27Ae2D4EE973",
|
|
6
|
+
oracle: "0xf507345D044D29DE9696c9fce02E9202cB902107",
|
|
7
|
+
core: "0x760453D0380222AaFB959145c20C9B32EBb1B636",
|
|
8
|
+
pool: "0x0fe32e729aE12600Ec8CbE6950456171C98120AE",
|
|
9
|
+
oracleUpdaterRouter: "0x67c30c998d549be0efcaAE01623720BB081fC702",
|
|
10
|
+
interestRateStrategy: "0xf3aac20247fd73ae5C8ACFA0Cf25cfC463332E82",
|
|
11
|
+
proxyAdmin: "0xEB21e1513F0b522A78d8b8285094e89563e164C6",
|
|
12
|
+
liquidator: "0x76B21eFF6F232CB9b647007A5E505a5392C7417F",
|
|
13
|
+
mergeLiquidator: "0xd0017264cd4Ee870F6869F1ab47f6ff579c31ba5",
|
|
14
|
+
convertLiquidator: "0x68421cDD230e550ac467Ae980454032bf8CE3651",
|
|
15
|
+
polymarketCtfAdapter: "0x1715CC1c9797098F77993d6aa0D190F323aa9421",
|
|
16
|
+
negRiskMergeAdapter: "0x3611E0AA36ebEFe3b555Db9b5BDcB071Fb64a477",
|
|
17
17
|
};
|
|
18
18
|
|
|
@@ -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?:
|
|
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 =
|
|
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
|
|
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
|
|
206
|
+
await waitForReceiptWithRetry({ publicClient, hash, confirmations, retry: receiptRetry });
|
|
207
|
+
|
|
122
208
|
onProgress?.({ completed: i + 1, total, step, hash });
|
|
123
209
|
continue;
|
|
124
210
|
}
|
package/src/leverage/execute.ts
CHANGED
|
@@ -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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
package/src/leverage/index.ts
CHANGED
|
@@ -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
|
+
}
|