@varla/sdk 2.15.0 → 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 +17 -0
- package/dist/leverage/deleverageExecute.d.ts +60 -2
- package/dist/leverage/deleverageExecute.d.ts.map +1 -1
- package/dist/leverage/deleverageExecute.js +49 -4
- package/dist/leverage/deleverageExecute.js.map +1 -1
- package/dist/leverage/execute.d.ts +54 -3
- package/dist/leverage/execute.d.ts.map +1 -1
- package/dist/leverage/execute.js +38 -4
- package/dist/leverage/execute.js.map +1 -1
- package/dist/leverage/index.d.ts +3 -1
- package/dist/leverage/index.d.ts.map +1 -1
- package/dist/leverage/index.js +3 -1
- package/dist/leverage/index.js.map +1 -1
- package/dist/leverage/math.d.ts +12 -0
- package/dist/leverage/math.d.ts.map +1 -1
- package/dist/leverage/math.js +66 -5
- package/dist/leverage/math.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/dist/leverage/types.d.ts +23 -0
- package/dist/leverage/types.d.ts.map +1 -1
- package/dist/leverage/types.js +21 -0
- package/dist/leverage/types.js.map +1 -1
- package/package.json +1 -1
- package/src/leverage/deleverageExecute.ts +113 -5
- package/src/leverage/execute.ts +101 -6
- package/src/leverage/index.ts +4 -0
- package/src/leverage/math.ts +92 -5
- package/src/leverage/receipt.ts +143 -0
- package/src/leverage/types.ts +39 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
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
|
+
|
|
12
|
+
## 2.15.1
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- a943668: Applied internal formatting adjustments in leverage execution and math utilities with no functional changes.
|
|
17
|
+
|
|
18
|
+
- Reorganized internal code formatting in leverage execution loops and planning calculations.
|
|
19
|
+
|
|
3
20
|
## 2.15.0
|
|
4
21
|
|
|
5
22
|
### Minor 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?:
|
|
14
|
+
hash?: Hash;
|
|
14
15
|
sellResult?: SellResult;
|
|
15
16
|
}) => void;
|
|
16
17
|
export type RunDeleverageLoopParams = {
|
|
@@ -26,11 +27,68 @@ export type RunDeleverageLoopParams = {
|
|
|
26
27
|
}>) => Promise<SellResult>;
|
|
27
28
|
onProgress?: OnDeleverageProgress;
|
|
28
29
|
confirmations?: number;
|
|
30
|
+
/**
|
|
31
|
+
* Maximum acceptable slippage for sell fills (bps).
|
|
32
|
+
* If `actualCollateral < expectedCollateral * (10_000 - maxSlippageBps) / 10_000`,
|
|
33
|
+
* the loop aborts with a `SlippageExceededError`.
|
|
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;
|
|
29
72
|
};
|
|
30
73
|
export type RunDeleverageLoopResult = {
|
|
31
74
|
completed: boolean;
|
|
75
|
+
/** If execution stopped early, the index of the failed step (absolute). */
|
|
32
76
|
failedAtStep?: number;
|
|
33
77
|
error?: unknown;
|
|
34
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
|
+
*/
|
|
35
93
|
export declare function runDeleverageLoop(params: RunDeleverageLoopParams): Promise<RunDeleverageLoopResult>;
|
|
36
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,12 +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";
|
|
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
|
+
*/
|
|
4
29
|
export async function runDeleverageLoop(params) {
|
|
5
|
-
const { plan, positionId, publicClient, walletClient, coreAddress, account, onSellStep, onProgress, confirmations = 1, } = params;
|
|
30
|
+
const { plan, positionId, publicClient, walletClient, coreAddress, account, onSellStep, onProgress, confirmations = 1, maxSlippageBps, receiptRetry, onPostWithdraw, postWithdrawDelayMs = 0, startFromStep = 0, } = params;
|
|
6
31
|
const total = plan.steps.length;
|
|
7
32
|
// Track actual collateral proceeds from the most recent sell step.
|
|
8
33
|
let lastSellActualCollateral;
|
|
9
|
-
for (let i =
|
|
34
|
+
for (let i = startFromStep; i < total; i++) {
|
|
10
35
|
const step = plan.steps[i];
|
|
11
36
|
try {
|
|
12
37
|
if (step.kind === "withdraw") {
|
|
@@ -18,12 +43,32 @@ export async function runDeleverageLoop(params) {
|
|
|
18
43
|
amounts: [step.amount],
|
|
19
44
|
});
|
|
20
45
|
const hash = await sendTx({ walletClient: walletClient, request: sim.request });
|
|
21
|
-
await
|
|
46
|
+
await waitForReceiptWithRetry({ publicClient, hash, confirmations, retry: receiptRetry });
|
|
22
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
|
+
}
|
|
23
56
|
continue;
|
|
24
57
|
}
|
|
25
58
|
if (step.kind === "sell") {
|
|
26
59
|
const res = await onSellStep(step);
|
|
60
|
+
// Slippage guard: reject fills that deviate too far from expected.
|
|
61
|
+
if (maxSlippageBps !== undefined && step.expectedCollateral > 0n) {
|
|
62
|
+
const minAcceptable = (step.expectedCollateral * (10000n - maxSlippageBps)) / 10000n;
|
|
63
|
+
if (res.actualCollateral < minAcceptable) {
|
|
64
|
+
throw new SlippageExceededError({
|
|
65
|
+
expected: step.expectedCollateral,
|
|
66
|
+
actual: res.actualCollateral,
|
|
67
|
+
maxSlippageBps,
|
|
68
|
+
side: "sell",
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
27
72
|
lastSellActualCollateral = res.actualCollateral;
|
|
28
73
|
onProgress?.({ completed: i + 1, total, step, sellResult: res });
|
|
29
74
|
continue;
|
|
@@ -38,7 +83,7 @@ export async function runDeleverageLoop(params) {
|
|
|
38
83
|
amount: repayAmount,
|
|
39
84
|
});
|
|
40
85
|
const hash = await sendTx({ walletClient: walletClient, request: sim.request });
|
|
41
|
-
await
|
|
86
|
+
await waitForReceiptWithRetry({ publicClient, hash, confirmations, retry: receiptRetry });
|
|
42
87
|
onProgress?.({ completed: i + 1, total, step, hash });
|
|
43
88
|
continue;
|
|
44
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;
|
|
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,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAEvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AA6EnD,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,8EAA8E;AAC9E,WAAW;AACX,8EAA8E;AAE9E;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,MAA+B;IAE/B,MAAM,EACJ,IAAI,EACJ,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,OAAO,EACP,UAAU,EACV,UAAU,EACV,aAAa,GAAG,CAAC,EACjB,cAAc,EACd,YAAY,EACZ,cAAc,EACd,mBAAmB,GAAG,CAAC,EACvB,aAAa,GAAG,CAAC,GAClB,GAAG,MAAM,CAAC;IAEX,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;IAEhC,mEAAmE;IACnE,IAAI,wBAA4C,CAAC;IAEjD,KAAK,IAAI,CAAC,GAAG,aAAa,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC;QAC5B,IAAI,CAAC;YACH,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;gBAC7B,MAAM,GAAG,GAAG,MAAM,mBAAmB,CAAC;oBACpC,YAAY,EAAE,YAAmB;oBACjC,WAAW;oBACX,OAAO;oBACP,WAAW,EAAE,CAAC,UAAU,CAAC;oBACzB,OAAO,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC;iBACvB,CAAC,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,EAAE,YAAY,EAAE,YAAmB,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;gBACvF,MAAM,uBAAuB,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;gBAE1F,UAAU,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;gBAEtD,2EAA2E;gBAC3E,IAAI,cAAc,EAAE,CAAC;oBACnB,MAAM,cAAc,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;gBACvC,CAAC;gBAED,2DAA2D;gBAC3D,IAAI,mBAAmB,GAAG,CAAC,EAAE,CAAC;oBAC5B,MAAM,KAAK,CAAC,mBAAmB,CAAC,CAAC;gBACnC,CAAC;gBAED,SAAS;YACX,CAAC;YAED,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBACzB,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;gBAEnC,mEAAmE;gBACnE,IAAI,cAAc,KAAK,SAAS,IAAI,IAAI,CAAC,kBAAkB,GAAG,EAAE,EAAE,CAAC;oBACjE,MAAM,aAAa,GAAG,CAAC,IAAI,CAAC,kBAAkB,GAAG,CAAC,MAAO,GAAG,cAAc,CAAC,CAAC,GAAG,MAAO,CAAC;oBACvF,IAAI,GAAG,CAAC,gBAAgB,GAAG,aAAa,EAAE,CAAC;wBACzC,MAAM,IAAI,qBAAqB,CAAC;4BAC9B,QAAQ,EAAE,IAAI,CAAC,kBAAkB;4BACjC,MAAM,EAAE,GAAG,CAAC,gBAAgB;4BAC5B,cAAc;4BACd,IAAI,EAAE,MAAM;yBACb,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;gBAED,wBAAwB,GAAG,GAAG,CAAC,gBAAgB,CAAC;gBAChD,UAAU,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC;gBACjE,SAAS;YACX,CAAC;YAED,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;gBAC1B,MAAM,WAAW,GAAG,wBAAwB,IAAI,IAAI,CAAC,gBAAgB,CAAC;gBACtE,wBAAwB,GAAG,SAAS,CAAC;gBAErC,MAAM,GAAG,GAAG,MAAM,gBAAgB,CAAC;oBACjC,YAAY,EAAE,YAAmB;oBACjC,WAAW;oBACX,OAAO;oBACP,MAAM,EAAE,WAAW;iBACpB,CAAC,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,EAAE,YAAY,EAAE,YAAmB,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;gBACvF,MAAM,uBAAuB,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;gBAE1F,UAAU,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;gBACtD,SAAS;YACX,CAAC;YAED,MAAM,IAAI,KAAK,CAAC,sBAAuB,IAAY,CAAC,IAAI,EAAE,CAAC,CAAC;QAC9D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC;QACtD,CAAC;IACH,CAAC;IAED,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;AAC7B,CAAC"}
|
|
@@ -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`). */
|
|
@@ -33,13 +34,57 @@ export type RunLeverageLoopParams = {
|
|
|
33
34
|
onProgress?: OnLeverageProgress;
|
|
34
35
|
/** Number of block confirmations to wait per tx (default: 1). */
|
|
35
36
|
confirmations?: number;
|
|
37
|
+
/**
|
|
38
|
+
* Maximum acceptable slippage for buy fills (bps).
|
|
39
|
+
* If `actualTokens < expectedTokens * (10_000 - maxSlippageBps) / 10_000`,
|
|
40
|
+
* the loop aborts with a `SlippageExceededError`.
|
|
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;
|
|
36
81
|
};
|
|
37
82
|
export type RunLeverageLoopResult = {
|
|
38
|
-
/** Results for each step
|
|
83
|
+
/** Results for each step executed (starting from `startFromStep`). */
|
|
39
84
|
results: LeverageStepResult[];
|
|
40
85
|
/** Whether all steps completed successfully. */
|
|
41
86
|
completed: boolean;
|
|
42
|
-
/** 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). */
|
|
43
88
|
failedAtStep?: number;
|
|
44
89
|
/** If execution stopped early, the error. */
|
|
45
90
|
error?: unknown;
|
|
@@ -57,6 +102,12 @@ export type RunLeverageLoopResult = {
|
|
|
57
102
|
* for slippage.
|
|
58
103
|
*
|
|
59
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
|
|
60
111
|
*/
|
|
61
112
|
export declare function runLeverageLoop(params: RunLeverageLoopParams): Promise<RunLeverageLoopResult>;
|
|
62
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,6 +1,14 @@
|
|
|
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";
|
|
5
|
+
import { SlippageExceededError } from "./types.js";
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
// Helpers
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
function sleep(ms) {
|
|
10
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
11
|
+
}
|
|
4
12
|
// ---------------------------------------------------------------------------
|
|
5
13
|
// Executor
|
|
6
14
|
// ---------------------------------------------------------------------------
|
|
@@ -17,18 +25,36 @@ import { sendTx } from "../actions/tx.js";
|
|
|
17
25
|
* for slippage.
|
|
18
26
|
*
|
|
19
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
|
|
20
34
|
*/
|
|
21
35
|
export async function runLeverageLoop(params) {
|
|
22
|
-
const { plan, positionId, publicClient, walletClient, coreAddress, account, onBuyStep, onProgress, confirmations = 1, } = params;
|
|
36
|
+
const { plan, positionId, publicClient, walletClient, coreAddress, account, onBuyStep, onProgress, confirmations = 1, maxSlippageBps, receiptRetry, onPostBorrow, postBorrowDelayMs = 0, startFromStep = 0, } = params;
|
|
23
37
|
const results = [];
|
|
24
38
|
const totalSteps = plan.steps.length;
|
|
25
39
|
// Track actual tokens from the most recent buy (for slippage-aware deposits).
|
|
26
40
|
let lastBuyActualTokens;
|
|
27
|
-
for (let i =
|
|
41
|
+
for (let i = startFromStep; i < totalSteps; i++) {
|
|
28
42
|
const step = plan.steps[i];
|
|
29
43
|
try {
|
|
30
44
|
if (step.kind === "buy") {
|
|
31
45
|
const buyResult = await onBuyStep(step);
|
|
46
|
+
// Slippage guard: reject fills that deviate too far from expected.
|
|
47
|
+
if (maxSlippageBps !== undefined && step.expectedTokens > 0n) {
|
|
48
|
+
const minAcceptable = (step.expectedTokens * (10000n - maxSlippageBps)) / 10000n;
|
|
49
|
+
if (buyResult.actualTokens < minAcceptable) {
|
|
50
|
+
throw new SlippageExceededError({
|
|
51
|
+
expected: step.expectedTokens,
|
|
52
|
+
actual: buyResult.actualTokens,
|
|
53
|
+
maxSlippageBps,
|
|
54
|
+
side: "buy",
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
32
58
|
lastBuyActualTokens = buyResult.actualTokens;
|
|
33
59
|
const result = { step, buyResult };
|
|
34
60
|
results.push(result);
|
|
@@ -47,7 +73,7 @@ export async function runLeverageLoop(params) {
|
|
|
47
73
|
amounts: [depositAmount],
|
|
48
74
|
});
|
|
49
75
|
const hash = await sendTx({ walletClient: walletClient, request: sim.request });
|
|
50
|
-
await
|
|
76
|
+
await waitForReceiptWithRetry({ publicClient, hash, confirmations, retry: receiptRetry });
|
|
51
77
|
const result = { step, hash };
|
|
52
78
|
results.push(result);
|
|
53
79
|
onProgress?.({ completed: i + 1, total: totalSteps, step, result });
|
|
@@ -61,10 +87,18 @@ export async function runLeverageLoop(params) {
|
|
|
61
87
|
amount: step.collateralAmount,
|
|
62
88
|
});
|
|
63
89
|
const hash = await sendTx({ walletClient: walletClient, request: sim.request });
|
|
64
|
-
await
|
|
90
|
+
await waitForReceiptWithRetry({ publicClient, hash, confirmations, retry: receiptRetry });
|
|
65
91
|
const result = { step, hash };
|
|
66
92
|
results.push(result);
|
|
67
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
|
+
}
|
|
68
102
|
continue;
|
|
69
103
|
}
|
|
70
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
|
-
export { collateralForDollars, DEFAULT_MAX_ITERATIONS, DEFAULT_MIN_STEP_COLLATERAL, PRICE_E8, pow10, WAD, } from "./types.js";
|
|
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
|
-
|
|
11
|
+
// Receipt retry
|
|
12
|
+
export { DEFAULT_RECEIPT_RETRY, waitForReceiptWithRetry } from "./receipt.js";
|
|
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"}
|
package/dist/leverage/math.d.ts
CHANGED
|
@@ -55,6 +55,12 @@ export type PlanLeverageLoopParams = {
|
|
|
55
55
|
startingDebt?: bigint;
|
|
56
56
|
/** Optional absolute cap on total debt (collateral base units). */
|
|
57
57
|
maxTotalDebt?: bigint;
|
|
58
|
+
/**
|
|
59
|
+
* Assumed slippage/spread for CLOB buys (bps, e.g. 50 = 0.50%).
|
|
60
|
+
* Buys use an effectively higher price: `price * (10_000 + slippageBps) / 10_000`.
|
|
61
|
+
* Default: 0 (ideal execution).
|
|
62
|
+
*/
|
|
63
|
+
slippageBps?: bigint;
|
|
58
64
|
};
|
|
59
65
|
/**
|
|
60
66
|
* Plan a leverage loop (pure math, zero RPC).
|
|
@@ -87,6 +93,12 @@ export type PlanDeleverageParams = {
|
|
|
87
93
|
maxIterations?: number;
|
|
88
94
|
/** Minimum sell size (in collateral base units) to continue looping. */
|
|
89
95
|
minStepSell?: bigint;
|
|
96
|
+
/**
|
|
97
|
+
* Assumed slippage/spread for CLOB sells (bps, e.g. 50 = 0.50%).
|
|
98
|
+
* Sells use an effectively lower price: `price * (10_000 - slippageBps) / 10_000`.
|
|
99
|
+
* Default: 0 (ideal execution).
|
|
100
|
+
*/
|
|
101
|
+
slippageBps?: bigint;
|
|
90
102
|
};
|
|
91
103
|
/**
|
|
92
104
|
* Plan a deleverage (unwind) loop (pure math, zero RPC).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"math.d.ts","sourceRoot":"","sources":["../../src/leverage/math.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,cAAc,EAEd,YAAY,EAGb,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"math.d.ts","sourceRoot":"","sources":["../../src/leverage/math.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,cAAc,EAEd,YAAY,EAGb,MAAM,YAAY,CAAC;AA+CpB;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAIlD;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,MAAM,CAUR;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,gBAAgB,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAG1F;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAErF;AAMD,MAAM,MAAM,sBAAsB,GAAG;IACnC,iEAAiE;IACjE,cAAc,EAAE,MAAM,CAAC;IACvB,wCAAwC;IACxC,YAAY,EAAE,MAAM,CAAC;IACrB,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,oCAAoC;IACpC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,0FAA0F;IAC1F,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kEAAkE;IAClE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,0DAA0D;IAC1D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mEAAmE;IACnE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,sBAAsB,GAAG,YAAY,CA+J7E;AAMD,MAAM,MAAM,oBAAoB,GAAG;IACjC,oDAAoD;IACpD,eAAe,EAAE,MAAM,CAAC;IACxB,uDAAuD;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,wCAAwC;IACxC,YAAY,EAAE,MAAM,CAAC;IACrB,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,mBAAmB,EAAE,MAAM,GAAG,MAAM,CAAC;IACrC,oCAAoC;IACpC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,wEAAwE;IACxE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,oBAAoB,GAAG,cAAc,CAoI3E;AAMD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,+BAA+B;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,oCAAoC;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,wDAAwD;IACxD,GAAG,EAAE,MAAM,CAAC;IACZ,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB,0DAA0D;IAC1D,YAAY,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,gBAAgB,CAanB"}
|
package/dist/leverage/math.js
CHANGED
|
@@ -23,6 +23,19 @@ function mulDivUp(a, b, c) {
|
|
|
23
23
|
throw new Error("mulDivUp: division by zero");
|
|
24
24
|
return (a * b + c - 1n) / c;
|
|
25
25
|
}
|
|
26
|
+
/** BPS base (100%). */
|
|
27
|
+
const BPS_BASE = 10000n;
|
|
28
|
+
/**
|
|
29
|
+
* Adjust a price for slippage.
|
|
30
|
+
* - Buying: pay more per token → effective price goes UP.
|
|
31
|
+
* - Selling: receive less per token → effective price goes DOWN.
|
|
32
|
+
*/
|
|
33
|
+
function applySlippageToBuyPrice(price, slippageBps) {
|
|
34
|
+
return slippageBps > 0n ? mulDivUp(price, BPS_BASE + slippageBps, BPS_BASE) : price;
|
|
35
|
+
}
|
|
36
|
+
function applySlippageToSellPrice(price, slippageBps) {
|
|
37
|
+
return slippageBps > 0n ? mulDiv(price, BPS_BASE - slippageBps, BPS_BASE) : price;
|
|
38
|
+
}
|
|
26
39
|
// ---------------------------------------------------------------------------
|
|
27
40
|
// Public helpers
|
|
28
41
|
// ---------------------------------------------------------------------------
|
|
@@ -89,7 +102,7 @@ export function collateralForTokens(tokenAmount, tokenPriceE8) {
|
|
|
89
102
|
* If `maxIterations` is hit, the final iteration ends at `deposit`.
|
|
90
103
|
*/
|
|
91
104
|
export function planLeverageLoop(params) {
|
|
92
|
-
const { initialCapital, tokenPriceE8, ltvWad, targetLeverageWad, maxIterations = DEFAULT_MAX_ITERATIONS, minStepBorrow = DEFAULT_MIN_STEP_COLLATERAL, startingTokens = 0n, startingDebt = 0n, maxTotalDebt, } = params;
|
|
105
|
+
const { initialCapital, tokenPriceE8, ltvWad, targetLeverageWad, maxIterations = DEFAULT_MAX_ITERATIONS, minStepBorrow = DEFAULT_MIN_STEP_COLLATERAL, startingTokens = 0n, startingDebt = 0n, maxTotalDebt, slippageBps = 0n, } = params;
|
|
93
106
|
if (initialCapital <= 0n)
|
|
94
107
|
throw new Error("initialCapital must be > 0");
|
|
95
108
|
if (tokenPriceE8 <= 0n)
|
|
@@ -102,7 +115,12 @@ export function planLeverageLoop(params) {
|
|
|
102
115
|
throw new Error("startingTokens must be >= 0");
|
|
103
116
|
if (startingDebt < 0n)
|
|
104
117
|
throw new Error("startingDebt must be >= 0");
|
|
118
|
+
if (slippageBps < 0n)
|
|
119
|
+
throw new Error("slippageBps must be >= 0");
|
|
120
|
+
if (slippageBps >= BPS_BASE)
|
|
121
|
+
throw new Error("slippageBps must be < 10_000");
|
|
105
122
|
const maxLev = maxLeverage(ltvWad);
|
|
123
|
+
const effectiveBuyPrice = applySlippageToBuyPrice(tokenPriceE8, slippageBps);
|
|
106
124
|
// Target leverage semantics:
|
|
107
125
|
// - undefined => run to max (bounded by maxIterations / minStepBorrow / caps)
|
|
108
126
|
// - <= 1× => treat as 1× (no borrowing)
|
|
@@ -132,7 +150,8 @@ export function planLeverageLoop(params) {
|
|
|
132
150
|
break;
|
|
133
151
|
// buy
|
|
134
152
|
const spendCollateral = collateralAvailable;
|
|
135
|
-
|
|
153
|
+
// Use effective (slippage-adjusted) price for buy, but ideal price for expectedTokens display.
|
|
154
|
+
const tokensBought = tokensForCollateral(spendCollateral, effectiveBuyPrice);
|
|
136
155
|
if (tokensBought <= 0n)
|
|
137
156
|
break;
|
|
138
157
|
// Spend the available collateral for this iteration.
|
|
@@ -188,6 +207,16 @@ export function planLeverageLoop(params) {
|
|
|
188
207
|
estimatedLiquidationPriceE8,
|
|
189
208
|
iterations,
|
|
190
209
|
maxLeverageWad: maxLev,
|
|
210
|
+
assumedSlippageBps: slippageBps,
|
|
211
|
+
totalSlippageCost: slippageBps > 0n
|
|
212
|
+
? clampPositive(
|
|
213
|
+
// Ideal tokens - actual tokens, valued at mid price.
|
|
214
|
+
collateralForTokens(steps
|
|
215
|
+
.filter((s) => s.kind === "buy")
|
|
216
|
+
.reduce((acc, s) => acc + tokensForCollateral(s.collateralAmount, tokenPriceE8), 0n) -
|
|
217
|
+
totalTokens +
|
|
218
|
+
startingTokens, tokenPriceE8))
|
|
219
|
+
: 0n,
|
|
191
220
|
};
|
|
192
221
|
return {
|
|
193
222
|
steps,
|
|
@@ -208,7 +237,7 @@ export function planLeverageLoop(params) {
|
|
|
208
237
|
* Steps: withdraw → sell → repay → ...
|
|
209
238
|
*/
|
|
210
239
|
export function planDeleverage(params) {
|
|
211
|
-
const { depositedTokens, currentDebt, tokenPriceE8, ltvWad, targetDebtReduction, maxIterations = DEFAULT_MAX_ITERATIONS, minStepSell = DEFAULT_MIN_STEP_COLLATERAL, } = params;
|
|
240
|
+
const { depositedTokens, currentDebt, tokenPriceE8, ltvWad, targetDebtReduction, maxIterations = DEFAULT_MAX_ITERATIONS, minStepSell = DEFAULT_MIN_STEP_COLLATERAL, slippageBps = 0n, } = params;
|
|
212
241
|
if (tokenPriceE8 <= 0n)
|
|
213
242
|
throw new Error("tokenPriceE8 must be > 0");
|
|
214
243
|
if (ltvWad <= 0n || ltvWad >= WAD)
|
|
@@ -217,6 +246,11 @@ export function planDeleverage(params) {
|
|
|
217
246
|
throw new Error("depositedTokens must be >= 0");
|
|
218
247
|
if (currentDebt < 0n)
|
|
219
248
|
throw new Error("currentDebt must be >= 0");
|
|
249
|
+
if (slippageBps < 0n)
|
|
250
|
+
throw new Error("slippageBps must be >= 0");
|
|
251
|
+
if (slippageBps >= BPS_BASE)
|
|
252
|
+
throw new Error("slippageBps must be < 10_000");
|
|
253
|
+
const effectiveSellPrice = applySlippageToSellPrice(tokenPriceE8, slippageBps);
|
|
220
254
|
const targetRepay = targetDebtReduction === "full" ? currentDebt : targetDebtReduction;
|
|
221
255
|
if (targetRepay <= 0n) {
|
|
222
256
|
return {
|
|
@@ -227,6 +261,8 @@ export function planDeleverage(params) {
|
|
|
227
261
|
tokensRemaining: depositedTokens,
|
|
228
262
|
collateralRemaining: 0n,
|
|
229
263
|
iterations: 0,
|
|
264
|
+
assumedSlippageBps: slippageBps,
|
|
265
|
+
totalSlippageCost: 0n,
|
|
230
266
|
},
|
|
231
267
|
};
|
|
232
268
|
}
|
|
@@ -235,6 +271,7 @@ export function planDeleverage(params) {
|
|
|
235
271
|
let remainingDebt = currentDebt;
|
|
236
272
|
let totalSold = 0n;
|
|
237
273
|
let totalRepaid = 0n;
|
|
274
|
+
let excessCollateral = 0n; // sell proceeds not used for repayment (returned to user)
|
|
238
275
|
for (let i = 0; i < maxIterations; i++) {
|
|
239
276
|
if (remainingDebt <= 0n)
|
|
240
277
|
break;
|
|
@@ -255,7 +292,7 @@ export function planDeleverage(params) {
|
|
|
255
292
|
const tokensToWithdraw = withdrawable < tokensNeededForTarget ? withdrawable : tokensNeededForTarget;
|
|
256
293
|
if (tokensToWithdraw <= 0n)
|
|
257
294
|
break;
|
|
258
|
-
const collateralProceeds = collateralForTokens(tokensToWithdraw,
|
|
295
|
+
const collateralProceeds = collateralForTokens(tokensToWithdraw, effectiveSellPrice);
|
|
259
296
|
if (collateralProceeds < minStepSell)
|
|
260
297
|
break;
|
|
261
298
|
// withdraw
|
|
@@ -274,8 +311,27 @@ export function planDeleverage(params) {
|
|
|
274
311
|
steps.push({ kind: "repay", collateralAmount: repayAmount, iteration: i });
|
|
275
312
|
remainingDebt -= repayAmount;
|
|
276
313
|
totalRepaid += repayAmount;
|
|
314
|
+
excessCollateral += collateralProceeds - repayAmount;
|
|
315
|
+
}
|
|
316
|
+
// Phase 2: After all debt is repaid, withdraw and sell remaining tokens.
|
|
317
|
+
// This ensures "full" close returns all collateral to the user, not just repays debt.
|
|
318
|
+
// Only fires when debt is fully repaid — if debt remains (underwater/stuck), skip.
|
|
319
|
+
if (targetDebtReduction === "full" && remainingDebt <= 0n && remainingTokens > 0n) {
|
|
320
|
+
const collateralProceeds = collateralForTokens(remainingTokens, tokenPriceE8);
|
|
321
|
+
const iteration = steps.length > 0 ? steps[steps.length - 1].iteration + 1 : 0;
|
|
322
|
+
steps.push({ kind: "withdraw", amount: remainingTokens, iteration });
|
|
323
|
+
steps.push({
|
|
324
|
+
kind: "sell",
|
|
325
|
+
tokenAmount: remainingTokens,
|
|
326
|
+
expectedCollateral: collateralProceeds,
|
|
327
|
+
iteration,
|
|
328
|
+
});
|
|
329
|
+
excessCollateral += collateralProceeds;
|
|
330
|
+
totalSold += remainingTokens;
|
|
331
|
+
remainingTokens = 0n;
|
|
277
332
|
}
|
|
278
|
-
const collateralRemaining =
|
|
333
|
+
const collateralRemaining = excessCollateral +
|
|
334
|
+
clampPositive(collateralForTokens(remainingTokens, tokenPriceE8) - remainingDebt);
|
|
279
335
|
const iterations = steps.length > 0 ? steps[steps.length - 1].iteration + 1 : 0;
|
|
280
336
|
return {
|
|
281
337
|
steps,
|
|
@@ -285,6 +341,11 @@ export function planDeleverage(params) {
|
|
|
285
341
|
tokensRemaining: remainingTokens,
|
|
286
342
|
collateralRemaining,
|
|
287
343
|
iterations,
|
|
344
|
+
assumedSlippageBps: slippageBps,
|
|
345
|
+
totalSlippageCost: slippageBps > 0n
|
|
346
|
+
? clampPositive(collateralForTokens(totalSold, tokenPriceE8) -
|
|
347
|
+
collateralForTokens(totalSold, effectiveSellPrice))
|
|
348
|
+
: 0n,
|
|
288
349
|
},
|
|
289
350
|
};
|
|
290
351
|
}
|