@buildonspark/spark-sdk 0.3.6 → 0.3.7
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 +8 -0
- package/dist/bare/index.cjs +9240 -9125
- package/dist/bare/index.d.cts +14 -11
- package/dist/bare/index.d.ts +14 -11
- package/dist/bare/index.js +3917 -3802
- package/dist/{chunk-XPHYQ2L6.js → chunk-KDEVNW7C.js} +4895 -4779
- package/dist/{chunk-LIZFXQWK.js → chunk-P4HYYSMU.js} +1 -1
- package/dist/{chunk-EHKP3Y65.js → chunk-SRPKOCG4.js} +1 -2
- package/dist/{chunk-FJ7LTA2O.js → chunk-UYTT3C6H.js} +1 -1
- package/dist/{client-AHn11NHe.d.cts → client-Bcb7TUIp.d.cts} +11 -9
- package/dist/{client-GOlkXliC.d.ts → client-D9T58OY8.d.ts} +11 -9
- package/dist/debug.cjs +1852 -1738
- package/dist/debug.d.cts +4 -4
- package/dist/debug.d.ts +4 -4
- package/dist/debug.js +2 -2
- package/dist/graphql/objects/index.d.cts +2 -2
- package/dist/graphql/objects/index.d.ts +2 -2
- package/dist/index.cjs +174 -58
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +3 -3
- package/dist/index.node.cjs +174 -57
- package/dist/index.node.d.cts +5 -5
- package/dist/index.node.d.ts +5 -5
- package/dist/index.node.js +2 -2
- package/dist/{logging-D7ukPwRA.d.ts → logging-JIaZZIbR.d.ts} +2 -2
- package/dist/{logging-CW3kwBaM.d.cts → logging-zkr4UlOi.d.cts} +2 -2
- package/dist/native/{index.cjs → index.react-native.cjs} +182 -62
- package/dist/native/{index.d.cts → index.react-native.d.cts} +19 -15
- package/dist/native/{index.d.ts → index.react-native.d.ts} +19 -15
- package/dist/native/{index.js → index.react-native.js} +179 -60
- package/dist/{spark-wallet-NxG55m7K.d.cts → spark-wallet-BuFrUWeE.d.cts} +4 -3
- package/dist/{spark-wallet-jwNvWvpK.d.ts → spark-wallet-CE5PYiIb.d.ts} +4 -3
- package/dist/{spark-wallet.browser-Cg4fB-Nm.d.ts → spark-wallet.browser-BwYkkOBU.d.ts} +1 -1
- package/dist/{spark-wallet.browser-Db7Y95Kt.d.cts → spark-wallet.browser-DC3jdQPW.d.cts} +1 -1
- package/dist/{spark-wallet.node-DB3ZqtJG.d.ts → spark-wallet.node-C9d2W-Nb.d.ts} +1 -1
- package/dist/{spark-wallet.node-HEG2ahNd.d.cts → spark-wallet.node-CR_zNxmy.d.cts} +1 -1
- package/dist/tests/test-utils.cjs +168 -51
- package/dist/tests/test-utils.d.cts +4 -4
- package/dist/tests/test-utils.d.ts +4 -4
- package/dist/tests/test-utils.js +4 -4
- package/dist/{token-transactions-B2-BO7Oz.d.ts → token-transactions-BZoJuvuE.d.ts} +1 -1
- package/dist/{token-transactions-BAN68xwg.d.cts → token-transactions-I_OFIoNH.d.cts} +1 -1
- package/dist/types/index.d.cts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/package.json +14 -4
- package/src/graphql/client.ts +6 -9
- package/src/graphql/mutations/CompleteCoopExit.ts +1 -1
- package/src/graphql/mutations/RequestCoopExit.ts +3 -1
- package/src/graphql/mutations/RequestLightningSend.ts +3 -1
- package/src/graphql/objects/CompleteCoopExitInput.ts +22 -33
- package/src/graphql/objects/RequestCoopExitInput.ts +39 -45
- package/src/graphql/objects/RequestLightningSendInput.ts +31 -39
- package/src/index.react-native.ts +21 -0
- package/src/services/config.ts +2 -2
- package/src/services/connection/connection.ts +10 -0
- package/src/services/coop-exit.ts +5 -1
- package/src/services/token-transactions.ts +8 -8
- package/src/spark-wallet/spark-wallet.browser.ts +0 -1
- package/src/spark-wallet/spark-wallet.react-native.ts +5 -3
- package/src/spark-wallet/spark-wallet.ts +56 -9
- package/src/tests/integration/coop-exit.test.ts +2 -0
- package/src/tests/integration/lightning.test.ts +5 -1
- package/src/tests/integration/ssp/coop-exit-validation.test.ts +5 -6
- package/src/tests/integration/ssp/static_deposit.test.ts +45 -35
- package/src/tests/optimize.test.ts +45 -0
- package/src/tests/token-outputs.test.ts +60 -1
- package/src/tests/utils/test-faucet.ts +12 -8
- package/src/utils/optimize.ts +226 -0
- package/src/native/index.ts +0 -21
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { InternalValidationError } from "../errors/types.js";
|
|
2
|
+
|
|
3
|
+
const DENOMINATIONS: number[] = Array.from({ length: 28 }, (_, i) => 2 ** i);
|
|
4
|
+
|
|
5
|
+
function assert(condition: boolean, message?: string): asserts condition {
|
|
6
|
+
if (!condition) {
|
|
7
|
+
throw new InternalValidationError(message || "Assertion failed");
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function sum(arr: number[]): number {
|
|
12
|
+
return arr.reduce((a, b) => a + b, 0);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function sorted(arr: number[]): number[] {
|
|
16
|
+
return [...arr].sort((a, b) => a - b);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function equals(a: number[], b: number[]): boolean {
|
|
20
|
+
return a.length === b.length && a.every((val, index) => val === b[index]);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function countOccurrences(arr: number[]): Map<number, number> {
|
|
24
|
+
const map = new Map<number, number>();
|
|
25
|
+
for (const x of arr) {
|
|
26
|
+
map.set(x, (map.get(x) ?? 0) + 1);
|
|
27
|
+
}
|
|
28
|
+
return map;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function subtractCounters(
|
|
32
|
+
a: Map<number, number>,
|
|
33
|
+
b: Map<number, number>,
|
|
34
|
+
): Map<number, number> {
|
|
35
|
+
const result = new Map<number, number>();
|
|
36
|
+
for (const [key, value] of a.entries()) {
|
|
37
|
+
const diff = value - (b.get(key) ?? 0);
|
|
38
|
+
if (diff > 0) {
|
|
39
|
+
result.set(key, diff);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function counterToFlatArray(counter: Map<number, number>): number[] {
|
|
46
|
+
const arr: number[] = [];
|
|
47
|
+
for (const [k, v] of Array.from(counter.entries()).sort(
|
|
48
|
+
(a, b) => a[0] - b[0],
|
|
49
|
+
)) {
|
|
50
|
+
for (let i = 0; i < v; i++) {
|
|
51
|
+
arr.push(k);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return arr;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function greedyLeaves(amount: number): number[] {
|
|
58
|
+
const leaves: number[] = [];
|
|
59
|
+
let remaining = amount;
|
|
60
|
+
for (let i = DENOMINATIONS.length - 1; i >= 0; i--) {
|
|
61
|
+
const leaf = DENOMINATIONS[i];
|
|
62
|
+
if (typeof leaf === "number" && leaf > 0) {
|
|
63
|
+
while (remaining >= leaf) {
|
|
64
|
+
remaining -= leaf;
|
|
65
|
+
leaves.push(leaf);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
assert(sum(leaves) === amount, "greedy_leaves: sum mismatch");
|
|
70
|
+
return sorted(leaves);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function swapMinimizingLeaves(
|
|
74
|
+
amount: number,
|
|
75
|
+
multiplicity: number = 1,
|
|
76
|
+
): number[] {
|
|
77
|
+
const leaves: number[] = [];
|
|
78
|
+
let remaining = amount;
|
|
79
|
+
assert(multiplicity > 0, "multiplicity must be > 0");
|
|
80
|
+
for (const leaf of DENOMINATIONS) {
|
|
81
|
+
if (typeof leaf === "number" && leaf > 0) {
|
|
82
|
+
for (let i = 0; i < multiplicity; i++) {
|
|
83
|
+
if (remaining >= leaf) {
|
|
84
|
+
remaining -= leaf;
|
|
85
|
+
leaves.push(leaf);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
leaves.push(...greedyLeaves(remaining));
|
|
91
|
+
assert(sum(leaves) === amount, "swap_minimizing_leaves: sum mismatch");
|
|
92
|
+
return sorted(leaves);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export class Swap {
|
|
96
|
+
inLeaves: number[];
|
|
97
|
+
outLeaves: number[];
|
|
98
|
+
|
|
99
|
+
constructor(inLeaves: number[], outLeaves: number[]) {
|
|
100
|
+
this.inLeaves = [...inLeaves];
|
|
101
|
+
this.outLeaves = [...outLeaves];
|
|
102
|
+
assert(
|
|
103
|
+
sum(this.inLeaves) === sum(this.outLeaves),
|
|
104
|
+
"Swap in/out leaves must sum to same value for swap: " + this.toString(),
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
toString(): string {
|
|
109
|
+
return `Swap(in=${JSON.stringify(this.inLeaves)}, out=${JSON.stringify(this.outLeaves)})`;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Generates swaps that will result in the unilateral exit maximizing set of leaves. Multiple iterations
|
|
115
|
+
* may be required to reach the optimal set.
|
|
116
|
+
*
|
|
117
|
+
* @param inputLeaves - the leaves to optimize.
|
|
118
|
+
* @param maxLeavesPerSwap - soft limit on the number of leaves per swap.
|
|
119
|
+
* @returns - the swaps that will result in the unilateral exit maximizing set of leaves.
|
|
120
|
+
*/
|
|
121
|
+
export function maximizeUnilateralExit(
|
|
122
|
+
inputLeaves: number[],
|
|
123
|
+
maxLeavesPerSwap: number = 64,
|
|
124
|
+
): Swap[] {
|
|
125
|
+
const swaps: Swap[] = [];
|
|
126
|
+
|
|
127
|
+
let batch: number[] = [];
|
|
128
|
+
let leaves: number[] = sorted(inputLeaves);
|
|
129
|
+
|
|
130
|
+
// Process leaves in batches of up to approximately maxLeavesPerSwap.
|
|
131
|
+
while (leaves.length > 0) {
|
|
132
|
+
batch.push(leaves.shift()!);
|
|
133
|
+
const target = greedyLeaves(sum(batch));
|
|
134
|
+
if (batch.length >= maxLeavesPerSwap || target.length >= maxLeavesPerSwap) {
|
|
135
|
+
if (!equals(target, batch)) {
|
|
136
|
+
swaps.push(new Swap([...batch], target));
|
|
137
|
+
}
|
|
138
|
+
batch = [];
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Process any remaining leaves.
|
|
143
|
+
if (batch.length > 0) {
|
|
144
|
+
const target = greedyLeaves(sum(batch));
|
|
145
|
+
if (!equals(target, batch)) {
|
|
146
|
+
swaps.push(new Swap([...batch], target));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return swaps;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Generates swaps that will minimize the probability of needing to swap during a transfer. Multiple iterations
|
|
155
|
+
* may be required to reach the optimal set.
|
|
156
|
+
*
|
|
157
|
+
* @param inputLeaves - the leaves to optimize.
|
|
158
|
+
* @param multiplicity - roughly speaking, the higher the multiplicity, the more transfers can be sent
|
|
159
|
+
* without needing to swap, but setting it too high will slow things down (recommended: 1 or 2)
|
|
160
|
+
* @param maxLeavesPerSwap - soft limit on the number of leaves per swap.
|
|
161
|
+
* @returns - the swaps that will minimize the probability of needing to swap during a transfer.
|
|
162
|
+
*/
|
|
163
|
+
export function minimizeTransferSwap(
|
|
164
|
+
inputLeaves: number[],
|
|
165
|
+
multiplicity: number = 1,
|
|
166
|
+
maxLeavesPerSwap: number = 64,
|
|
167
|
+
): Swap[] {
|
|
168
|
+
const balance = sum(inputLeaves);
|
|
169
|
+
const optimalLeaves = swapMinimizingLeaves(balance, multiplicity);
|
|
170
|
+
const walletCounter = countOccurrences(inputLeaves);
|
|
171
|
+
const optimalCounter = countOccurrences(optimalLeaves);
|
|
172
|
+
|
|
173
|
+
const leavesToGive = subtractCounters(walletCounter, optimalCounter);
|
|
174
|
+
const leavesToReceive = subtractCounters(optimalCounter, walletCounter);
|
|
175
|
+
|
|
176
|
+
const leavesToGiveFlat = counterToFlatArray(leavesToGive);
|
|
177
|
+
const leavesToReceiveFlat = counterToFlatArray(leavesToReceive);
|
|
178
|
+
|
|
179
|
+
const swaps: Swap[] = [];
|
|
180
|
+
let toGiveBatch: number[] = [];
|
|
181
|
+
let toReceiveBatch: number[] = [];
|
|
182
|
+
let give = [...leavesToGiveFlat];
|
|
183
|
+
let receive = [...leavesToReceiveFlat];
|
|
184
|
+
|
|
185
|
+
while (give.length > 0 || receive.length > 0) {
|
|
186
|
+
if (sum(toGiveBatch) > sum(toReceiveBatch)) {
|
|
187
|
+
if (receive.length === 0) break;
|
|
188
|
+
toReceiveBatch.push(receive.shift()!);
|
|
189
|
+
} else {
|
|
190
|
+
if (give.length === 0) break;
|
|
191
|
+
toGiveBatch.push(give.shift()!);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (
|
|
195
|
+
toGiveBatch.length > 0 &&
|
|
196
|
+
toReceiveBatch.length > 0 &&
|
|
197
|
+
sum(toGiveBatch) === sum(toReceiveBatch)
|
|
198
|
+
) {
|
|
199
|
+
if (toGiveBatch.length > maxLeavesPerSwap) {
|
|
200
|
+
for (let i = 0; i < toGiveBatch.length; i += maxLeavesPerSwap) {
|
|
201
|
+
const subset = toGiveBatch.slice(i, i + maxLeavesPerSwap);
|
|
202
|
+
swaps.push(new Swap(subset, greedyLeaves(sum(subset))));
|
|
203
|
+
}
|
|
204
|
+
} else if (toReceiveBatch.length > maxLeavesPerSwap) {
|
|
205
|
+
for (let cutoff = maxLeavesPerSwap; cutoff > 0; cutoff--) {
|
|
206
|
+
const sumCut = sum(toReceiveBatch.slice(0, cutoff));
|
|
207
|
+
const remainder = sum(toGiveBatch) - sumCut;
|
|
208
|
+
const alternateBatch = [
|
|
209
|
+
...toReceiveBatch.slice(0, cutoff),
|
|
210
|
+
...greedyLeaves(remainder),
|
|
211
|
+
];
|
|
212
|
+
if (alternateBatch.length <= maxLeavesPerSwap) {
|
|
213
|
+
swaps.push(new Swap([...toGiveBatch], alternateBatch));
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
} else {
|
|
218
|
+
swaps.push(new Swap([...toGiveBatch], [...toReceiveBatch]));
|
|
219
|
+
}
|
|
220
|
+
toGiveBatch = [];
|
|
221
|
+
toReceiveBatch = [];
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return swaps;
|
|
226
|
+
}
|
package/src/native/index.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/* Root React Native entrypoint */
|
|
2
|
-
|
|
3
|
-
import { setCrypto } from "../utils/crypto.js";
|
|
4
|
-
|
|
5
|
-
setCrypto(globalThis.crypto);
|
|
6
|
-
|
|
7
|
-
export * from "../errors/index.js";
|
|
8
|
-
export * from "../utils/index.js";
|
|
9
|
-
|
|
10
|
-
export { ReactNativeSparkSigner } from "../signer/signer.react-native.js";
|
|
11
|
-
/* Enable some consumers to use named import DefaultSparkSigner regardless of module, see LIG-7662 */
|
|
12
|
-
export { ReactNativeSparkSigner as DefaultSparkSigner } from "../signer/signer.react-native.js";
|
|
13
|
-
|
|
14
|
-
export { SparkWallet } from "../spark-wallet/spark-wallet.react-native.js";
|
|
15
|
-
export * from "../spark-wallet/types.js";
|
|
16
|
-
|
|
17
|
-
export { type WalletConfigService } from "../services/config.js";
|
|
18
|
-
export { ConnectionManagerBrowser as ConnectionManager } from "../services/connection/connection.browser.js";
|
|
19
|
-
export { type ConnectionManager as BaseConnectionManager } from "../services/connection/connection.js";
|
|
20
|
-
export { TokenTransactionService } from "../services/token-transactions.js";
|
|
21
|
-
export { WalletConfig, type ConfigOptions } from "../services/wallet-config.js";
|