@strkfarm/sdk 1.1.39 → 1.1.41

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.
@@ -0,0 +1,150 @@
1
+ import { logger } from "@/utils";
2
+
3
+ export async function binarySearch(
4
+ lowWei: bigint,
5
+ highWei: bigint,
6
+ callback: (mid: bigint) => Promise<'go_low' | 'go_high' | 'retry' | 'found'>
7
+ ): Promise<bigint | null> {
8
+ while (lowWei <= highWei) {
9
+ const diff = highWei - lowWei;
10
+ const mid = lowWei + (diff / 2n);
11
+ const result = await callback(mid);
12
+
13
+ if (result === 'found') {
14
+ return mid;
15
+ } else if (result == 'retry') {
16
+ // do nothing.
17
+ } else if (result === 'go_low') {
18
+ highWei = mid - BigInt(1);
19
+ } else {
20
+ lowWei = mid + BigInt(1);
21
+ }
22
+ }
23
+
24
+ return null; // not found
25
+ }
26
+
27
+ interface SwapResult {
28
+ optimalInput: number;
29
+ actualOutput: number;
30
+ actualSlippage: number;
31
+ apiCallsUsed: number;
32
+ }
33
+
34
+ interface FindMaxInputOptions {
35
+ /** Function that calls your API: input amount -> output amount */
36
+ apiGetOutput: (inputAmount: number) => Promise<number>;
37
+ /** Maximum input amount you're willing to swap */
38
+ maxInput: number;
39
+ /** Maximum acceptable slippage in percent (e.g., 1.0 for 1%) */
40
+ maxSlippagePercent: number;
41
+ /** Convergence tolerance as fraction of maxInput */
42
+ tolerance: number;
43
+ /** Minimum input amount (default: 0) */
44
+ minInput?: number;
45
+ /** Reference amount multiplier for price calculation (default: 0.001) */
46
+ referenceAmountMultiplier?: number;
47
+ /** Reference rate for price calculation */
48
+ referenceRate?: number;
49
+ }
50
+
51
+ /**
52
+ * Find maximum input amount within slippage constraints using binary search
53
+ *
54
+ * @returns Promise with optimal input, output, slippage, and API calls used
55
+ */
56
+ export async function findMaxInputWithSlippage(
57
+ options: FindMaxInputOptions
58
+ ): Promise<SwapResult> {
59
+ const {
60
+ apiGetOutput,
61
+ maxInput,
62
+ maxSlippagePercent,
63
+ tolerance,
64
+ minInput = 0,
65
+ referenceAmountMultiplier = 0.001,
66
+ referenceRate = 0,
67
+ } = options;
68
+
69
+ let apiCalls = 0;
70
+
71
+ // one of referenceRate or referenceAmountMultiplier must be provided
72
+ if (!referenceRate && !referenceAmountMultiplier) {
73
+ throw new Error('One of referenceRate or referenceAmountMultiplier must be provided');
74
+ }
75
+
76
+ let _referenceRate = referenceRate;
77
+ if (!_referenceRate) {
78
+ // Get reference rate at minimal amount for price calculation
79
+ const smallAmount = maxInput * referenceAmountMultiplier;
80
+ const referenceOutput = await apiGetOutput(smallAmount);
81
+ apiCalls++;
82
+ _referenceRate = referenceOutput / smallAmount;
83
+ }
84
+
85
+ /**
86
+ * Check if slippage is acceptable for given input amount
87
+ * Returns { acceptable: boolean, slippage: number, output: number }
88
+ */
89
+ async function checkSlippage(inputAmount: number) {
90
+ const actualOutput = await apiGetOutput(inputAmount);
91
+ apiCalls++;
92
+
93
+ const expectedOutput = inputAmount * referenceRate;
94
+ const slippage = ((expectedOutput - actualOutput) / expectedOutput);
95
+ logger.verbose(`findMaxInputWithSlippage::checkSlippage inputAmount: ${inputAmount}, actualOutput: ${actualOutput}, slippage: ${slippage}, maxSlippagePercent: ${maxSlippagePercent}`);
96
+
97
+ return {
98
+ acceptable: slippage <= maxSlippagePercent,
99
+ slippage,
100
+ output: actualOutput,
101
+ };
102
+ }
103
+
104
+ // Start from the top - try maxInput first
105
+ const maxCheck = await checkSlippage(maxInput);
106
+ if (maxCheck.acceptable) {
107
+ // Best case: maxInput is within slippage, return immediately
108
+ return {
109
+ optimalInput: maxInput,
110
+ actualOutput: maxCheck.output,
111
+ actualSlippage: maxCheck.slippage,
112
+ apiCallsUsed: apiCalls,
113
+ };
114
+ }
115
+
116
+ // Binary search between minInput and maxInput
117
+ let left = minInput;
118
+ let right = maxInput;
119
+ let bestInput = minInput;
120
+ let bestOutput = 0;
121
+ let bestSlippage = 0;
122
+
123
+ const convergenceThreshold = tolerance * maxInput;
124
+
125
+ while (right - left > convergenceThreshold) {
126
+ const mid = (left + right) / 2;
127
+ const midCheck = await checkSlippage(mid);
128
+
129
+ if (midCheck.acceptable) {
130
+ // Good slippage at mid, save it and try higher
131
+ bestInput = mid;
132
+ bestOutput = midCheck.output;
133
+ bestSlippage = midCheck.slippage;
134
+ left = mid; // Search in upper half [mid, right]
135
+ } else {
136
+ // Bad slippage at mid, try lower
137
+ right = mid; // Search in lower half [left, mid]
138
+ }
139
+ }
140
+
141
+ return {
142
+ optimalInput: bestInput,
143
+ actualOutput: bestOutput,
144
+ actualSlippage: bestSlippage,
145
+ apiCallsUsed: apiCalls,
146
+ };
147
+ }
148
+
149
+
150
+