@kaleidorg/wallet-engine 1.0.0-beta.33 → 1.0.0-beta.35

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.
Files changed (41) hide show
  1. package/dist/adapters/SparkAdapter.d.ts +93 -16
  2. package/dist/adapters/SparkAdapter.d.ts.map +1 -1
  3. package/dist/adapters/SparkAdapter.js +831 -171
  4. package/dist/adapters/SparkAdapter.js.map +1 -1
  5. package/dist/adapters/spark.d.ts +11 -0
  6. package/dist/adapters/spark.d.ts.map +1 -0
  7. package/dist/adapters/spark.js +11 -0
  8. package/dist/adapters/spark.js.map +1 -0
  9. package/dist/index.d.ts +2 -1
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +4 -0
  12. package/dist/index.js.map +1 -1
  13. package/dist/lib/psbt-signer.d.ts +60 -0
  14. package/dist/lib/psbt-signer.d.ts.map +1 -0
  15. package/dist/lib/psbt-signer.js +161 -0
  16. package/dist/lib/psbt-signer.js.map +1 -0
  17. package/dist/lib/spark-activity.d.ts +5 -0
  18. package/dist/lib/spark-activity.d.ts.map +1 -0
  19. package/dist/lib/spark-activity.js +11 -0
  20. package/dist/lib/spark-activity.js.map +1 -0
  21. package/dist/lib/spark-balance-cache.d.ts +58 -0
  22. package/dist/lib/spark-balance-cache.d.ts.map +1 -0
  23. package/dist/lib/spark-balance-cache.js +86 -0
  24. package/dist/lib/spark-balance-cache.js.map +1 -0
  25. package/dist/lib/spark-client-manager.d.ts +54 -9
  26. package/dist/lib/spark-client-manager.d.ts.map +1 -1
  27. package/dist/lib/spark-client-manager.js +176 -35
  28. package/dist/lib/spark-client-manager.js.map +1 -1
  29. package/dist/lib/spark-converters.d.ts +64 -0
  30. package/dist/lib/spark-converters.d.ts.map +1 -0
  31. package/dist/lib/spark-converters.js +242 -0
  32. package/dist/lib/spark-converters.js.map +1 -0
  33. package/dist/lib/spark-helpers.d.ts +72 -0
  34. package/dist/lib/spark-helpers.d.ts.map +1 -0
  35. package/dist/lib/spark-helpers.js +151 -0
  36. package/dist/lib/spark-helpers.js.map +1 -0
  37. package/dist/lib/spark-sent-token-records.d.ts +43 -0
  38. package/dist/lib/spark-sent-token-records.d.ts.map +1 -0
  39. package/dist/lib/spark-sent-token-records.js +105 -0
  40. package/dist/lib/spark-sent-token-records.js.map +1 -0
  41. package/package.json +7 -2
@@ -0,0 +1,242 @@
1
+ /**
2
+ * SDK ↔ unified-shape converters for the Spark adapter.
3
+ *
4
+ * The three converters here cover the three sources of Spark activity:
5
+ *
6
+ * - `convertTransferToTransaction` — native Spark transfer
7
+ * - `convertTokenTransactionToUnified` — RGB-Spark token transaction
8
+ * (with direction-inference from output ownership)
9
+ * - `buildSentRecordTransaction` — offline fallback from the
10
+ * locally-stored send-token outbox
11
+ */
12
+ import { encodeBech32mTokenIdentifier } from '@buildonspark/spark-sdk';
13
+ import { normalizeTxHash } from './spark-sent-token-records.js';
14
+ import { formatAmount, mapTransferStatus, rawTokenIdFromBytes, tokenRefsMatch, txHashFromBytes, u8aToBigInt, u8aToHex, } from './spark-helpers.js';
15
+ const BTC_ASSET = {
16
+ id: 'BTC',
17
+ name: 'Bitcoin',
18
+ ticker: 'BTC',
19
+ precision: 8,
20
+ protocol: 'SPARK',
21
+ layer: 'SPARK_SPARK',
22
+ };
23
+ /**
24
+ * Project a native Spark transfer into the unified shape. BTC is the only
25
+ * native Spark asset, so the asset is hard-coded — this branch never
26
+ * surfaces RGB-Spark token transfers (see `convertTokenTransactionToUnified`).
27
+ */
28
+ export function convertTransferToTransaction(transfer) {
29
+ const isIncoming = transfer.transferDirection === 'INCOMING';
30
+ return {
31
+ id: transfer.id,
32
+ type: isIncoming ? 'receive' : 'send',
33
+ status: mapTransferStatus(transfer.status),
34
+ timestamp: transfer.createdTime?.getTime() ?? Date.now(),
35
+ amount: transfer.totalValue,
36
+ amountDisplay: formatAmount(transfer.totalValue, 8),
37
+ fee: 0,
38
+ feeDisplay: '0.00000000',
39
+ asset: BTC_ASSET,
40
+ protocolData: {
41
+ sparkInvoice: transfer.sparkInvoice,
42
+ type: transfer.type,
43
+ },
44
+ };
45
+ }
46
+ /**
47
+ * Convert a TokenTransactionWithStatus from the Spark SDK into a
48
+ * UnifiedTransaction.
49
+ *
50
+ * Direction: mints/creates are always receives. For transfers the protocol
51
+ * exposes no direction field, so we derive it from output ownership — the
52
+ * SDK orders token outputs recipients-first, change-last, so a wallet-owned
53
+ * first output (`tokenOutputs[0]`) means the wallet was the recipient
54
+ * (receive); anything else means it sent. A hash in `sentHashSet` (our
55
+ * local outbox) overrides this as an authoritative "send" — it covers the
56
+ * rare batch transfer where the wallet is a non-first recipient.
57
+ *
58
+ * Amount: receives sum the wallet-owned outputs; sends sum the outputs that
59
+ * left the wallet (everything not wallet-owned), falling back to the
60
+ * recorded amount when the gateway returns no figure.
61
+ */
62
+ export function convertTokenTransactionToUnified(txWithStatus, walletIdentityPubKey, tokenMetaMap, rawTokenMetaMap, sentHashSet, storedRecordMap, storedAmountMap, networkType, requestedAsset, requestedTokenRawId) {
63
+ const tx = txWithStatus.tokenTransaction;
64
+ if (!tx)
65
+ return null;
66
+ const outputs = tx.tokenOutputs ?? [];
67
+ if (outputs.length === 0)
68
+ return null;
69
+ const txHash = txHashFromBytes(txWithStatus.tokenTransactionHash);
70
+ const walletPubKeyLower = walletIdentityPubKey.toLowerCase();
71
+ const ownsOutput = (o) => !!o.ownerPublicKey && u8aToHex(o.ownerPublicKey).toLowerCase() === walletPubKeyLower;
72
+ // Direction: mints/creates are receives; an outbox hit is an authoritative
73
+ // send; otherwise the first output's owner decides (see method doc).
74
+ const inputCase = tx.tokenInputs?.$case;
75
+ let type;
76
+ if (inputCase === 'mintInput' || inputCase === 'createInput') {
77
+ type = 'receive';
78
+ }
79
+ else if (sentHashSet.has(txHash)) {
80
+ type = 'send';
81
+ }
82
+ else {
83
+ type = ownsOutput(outputs[0]) ? 'receive' : 'send';
84
+ }
85
+ const storedRecord = type === 'send' ? storedRecordMap.get(txHash) : undefined;
86
+ // Resolve token identity from the strongest local source available.
87
+ const firstOutput = outputs[0];
88
+ const tokenIdBytes = firstOutput.tokenIdentifier;
89
+ let tokenId = '';
90
+ let meta = { name: 'Unknown Token', ticker: 'TOKEN', decimals: 0 };
91
+ if (storedRecord?.assetId) {
92
+ tokenId =
93
+ requestedAsset && tokenRefsMatch(storedRecord.assetId, requestedAsset)
94
+ ? requestedAsset
95
+ : storedRecord.assetId;
96
+ meta = {
97
+ name: storedRecord.name || storedRecord.assetId,
98
+ ticker: storedRecord.ticker || 'TOKEN',
99
+ decimals: storedRecord.decimals,
100
+ };
101
+ }
102
+ else if (tokenIdBytes && tokenIdBytes.length > 0) {
103
+ const rawTokenId = rawTokenIdFromBytes(tokenIdBytes);
104
+ const rawFound = rawTokenMetaMap.get(rawTokenId);
105
+ if (requestedAsset && requestedTokenRawId && rawTokenId === requestedTokenRawId) {
106
+ tokenId = requestedAsset;
107
+ meta = rawFound?.meta ?? tokenMetaMap.get(requestedAsset) ?? meta;
108
+ }
109
+ if (rawFound) {
110
+ tokenId || (tokenId = rawFound.id);
111
+ meta = rawFound.meta;
112
+ }
113
+ try {
114
+ if (!tokenId) {
115
+ tokenId = encodeBech32mTokenIdentifier({
116
+ tokenIdentifier: tokenIdBytes,
117
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- SDK network enum varies by version
118
+ network: networkType,
119
+ });
120
+ }
121
+ const found = tokenMetaMap.get(tokenId);
122
+ if (found)
123
+ meta = found;
124
+ }
125
+ catch {
126
+ // Fallback: take first entry from map when encoding fails
127
+ for (const [id, m] of tokenMetaMap) {
128
+ tokenId = id;
129
+ meta = m;
130
+ break;
131
+ }
132
+ }
133
+ }
134
+ if (requestedAsset && !tokenId) {
135
+ tokenId = requestedAsset;
136
+ const found = tokenMetaMap.get(requestedAsset);
137
+ if (found)
138
+ meta = found;
139
+ }
140
+ // Amount
141
+ let totalAmount = 0n;
142
+ if (type === 'receive') {
143
+ // Sum the outputs the wallet owns — its received amount — ignoring any
144
+ // change the sender kept for itself.
145
+ for (const o of outputs) {
146
+ if (ownsOutput(o))
147
+ totalAmount += u8aToBigInt(o.tokenAmount);
148
+ }
149
+ // Fallback for mints / unexpected shapes where no output resolves as owned.
150
+ if (totalAmount === 0n) {
151
+ for (const o of outputs)
152
+ totalAmount += u8aToBigInt(o.tokenAmount);
153
+ }
154
+ }
155
+ else {
156
+ // Send: the amount that left the wallet is the sum of outputs the wallet
157
+ // does NOT own (recipients), excluding its own change output.
158
+ for (const o of outputs) {
159
+ if (!ownsOutput(o))
160
+ totalAmount += u8aToBigInt(o.tokenAmount);
161
+ }
162
+ // Fall back to the amount recorded at send time if the gateway returned
163
+ // nothing usable (e.g. a redacted or change-less response).
164
+ if (totalAmount === 0n) {
165
+ totalAmount = storedAmountMap.get(txHash) ?? 0n;
166
+ }
167
+ }
168
+ const amount = Number(totalAmount);
169
+ // Map token transaction status
170
+ // TokenTransactionStatus: FINALIZED=2 → confirmed, *CANCELLED=3,4 → failed
171
+ const statusNum = txWithStatus.status;
172
+ let status = 'pending';
173
+ if (statusNum === 2) {
174
+ status = 'confirmed'; // TOKEN_TRANSACTION_FINALIZED
175
+ }
176
+ else if (statusNum === 3 || statusNum === 4) {
177
+ status = 'failed'; // *_CANCELLED
178
+ }
179
+ const rawTs = tx.clientCreatedTimestamp;
180
+ const timestamp = rawTs instanceof Date ? rawTs.getTime() : typeof rawTs === 'number' ? rawTs : Date.now();
181
+ return {
182
+ id: `token-${txHash.slice(0, 16)}`,
183
+ type,
184
+ status,
185
+ timestamp,
186
+ amount,
187
+ amountDisplay: formatAmount(amount, meta.decimals),
188
+ fee: 0,
189
+ feeDisplay: '0',
190
+ asset: {
191
+ id: tokenId,
192
+ name: meta.name,
193
+ ticker: meta.ticker,
194
+ precision: meta.decimals,
195
+ protocol: 'SPARK',
196
+ layer: 'SPARK_SPARK',
197
+ },
198
+ protocolData: {
199
+ type: inputCase ?? 'unknown',
200
+ },
201
+ };
202
+ }
203
+ /**
204
+ * Build a UnifiedTransaction directly from a locally-stored send record,
205
+ * with no Spark RPC. Used as the offline / failed-fetch fallback in
206
+ * `listTransactions` so a completed token withdrawal always appears in
207
+ * history even when the Spark gateway is unreachable.
208
+ *
209
+ * Marks the transaction as `confirmed` on the rationale that the record is
210
+ * only written after the SDK returns a tx id — i.e. the transfer was
211
+ * signed and broadcast.
212
+ */
213
+ export function buildSentRecordTransaction(record, requestedAsset) {
214
+ const decimals = record.decimals || 0;
215
+ const amount = record.amount || 0;
216
+ const tokenId = requestedAsset && tokenRefsMatch(record.assetId, requestedAsset)
217
+ ? requestedAsset
218
+ : record.assetId;
219
+ return {
220
+ id: `token-${normalizeTxHash(record.hash).slice(0, 16)}`,
221
+ type: 'send',
222
+ status: 'confirmed',
223
+ timestamp: record.timestamp || Date.now(),
224
+ amount,
225
+ amountDisplay: formatAmount(amount, decimals),
226
+ fee: 0,
227
+ feeDisplay: '0',
228
+ asset: {
229
+ id: tokenId,
230
+ name: record.name || record.assetId,
231
+ ticker: record.ticker || 'TOKEN',
232
+ precision: decimals,
233
+ protocol: 'SPARK',
234
+ layer: 'SPARK_SPARK',
235
+ },
236
+ protocolData: {
237
+ type: 'transferInput',
238
+ source: 'local-record',
239
+ },
240
+ };
241
+ }
242
+ //# sourceMappingURL=spark-converters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spark-converters.js","sourceRoot":"","sources":["../../src/lib/spark-converters.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Pure helpers for the Spark adapter.
3
+ *
4
+ * Extracted from the extension's spark adapter so the adapter file can stay
5
+ * focused on the IProtocolAdapter surface + RPC orchestration. Everything in
6
+ * this module is side-effect free.
7
+ */
8
+ import type { TransactionStatus } from '../types/base.js';
9
+ /**
10
+ * Render a raw integer amount in the asset's display precision.
11
+ * Always emits exactly `precision` decimal digits (no trailing-zero trim) —
12
+ * callers that want a tighter rendering should post-process.
13
+ */
14
+ export declare function formatAmount(amount: number, precision: number): string;
15
+ /**
16
+ * Map a Spark transfer status string to our unified TransactionStatus.
17
+ *
18
+ * The Spark SDK ships two related but distinct status vocabularies:
19
+ * 1. Native transfers use the `TRANSFER_STATUS_*` enum from the SDK
20
+ * (COMPLETED / EXPIRED / RETURNED / SENDER_INITIATED /
21
+ * RECEIVER_KEY_TWEAKED).
22
+ * 2. Lightning send requests use a looser lowercase vocabulary
23
+ * (`completed` / `complete` / `succeeded` / `success` / `failed` /
24
+ * `error`) which has drifted across SDK versions.
25
+ *
26
+ * Both are mapped here so callers don't need to know which vocabulary a
27
+ * given record came from.
28
+ */
29
+ export declare function mapTransferStatus(status?: string): TransactionStatus;
30
+ /**
31
+ * Wrap a promise with a rejection timeout. Used to fail fast on slow Spark
32
+ * RPC calls; the SDK's own 30 s ceiling is too long for popup UI.
33
+ */
34
+ export declare function withTimeout<T>(promise: Promise<T>, timeoutMs: number, label: string): Promise<T>;
35
+ /**
36
+ * True when a Spark balance snapshot represents a fresh / still-syncing
37
+ * wallet — zero sats AND no token balances. The adapter applies a shorter
38
+ * TTL to empty snapshots so the UI doesn't get stuck on "0 sats" while the
39
+ * Spark wallet syncs.
40
+ */
41
+ export declare function isEmptyBalance(value: {
42
+ balance?: bigint | number | string;
43
+ tokenBalances?: Map<unknown, unknown> | unknown;
44
+ }): boolean;
45
+ /** Convert a Uint8Array to lowercase hex string. */
46
+ export declare function u8aToHex(bytes: Uint8Array): string;
47
+ /** Convert a big-endian Uint8Array to bigint (uint128 max). */
48
+ export declare function u8aToBigInt(bytes: Uint8Array): bigint;
49
+ /** Hex tx hash from raw bytes, run through the project's normalizer. */
50
+ export declare function txHashFromBytes(bytes: Uint8Array): string;
51
+ /** Normalized raw token id from raw bytes; empty string when bytes are missing. */
52
+ export declare function rawTokenIdFromBytes(bytes: Uint8Array | undefined): string;
53
+ /**
54
+ * Decode a bech32m-encoded Spark token id (e.g. `btkn1…`) back to its
55
+ * normalized raw hex form. Returns `""` for falsy input or decode failures —
56
+ * tokens that aren't bech32m-encoded simply round-trip through the empty
57
+ * string and fall back to the caller's other matchers.
58
+ */
59
+ export declare function rawTokenIdFromBech32mTokenId(tokenId: string | undefined): string;
60
+ /**
61
+ * Cross-format token id comparison: matches identical strings directly,
62
+ * otherwise decodes both via bech32m / normalizeTxHash and compares the
63
+ * raw forms. Returns false when either side is empty.
64
+ */
65
+ export declare function tokenRefsMatch(left: string | undefined, right: string | undefined): boolean;
66
+ /**
67
+ * Parse one of the SDK's polymorphic expiry shapes (Date | number | ISO
68
+ * string) into a finite millisecond timestamp. Returns undefined for
69
+ * unparseable / falsy / Infinity values so callers can branch on absence.
70
+ */
71
+ export declare function parseSdkExpiryMs(expiry: unknown): number | undefined;
72
+ //# sourceMappingURL=spark-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spark-helpers.d.ts","sourceRoot":"","sources":["../../src/lib/spark-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAGtD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEtE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,iBAAiB,CAuBpE;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAOhG;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE;IACpC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAA;IAClC,aAAa,CAAC,EAAE,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,GAAG,OAAO,CAAA;CAChD,GAAG,OAAO,CAKV;AAED,oDAAoD;AACpD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAIlD;AAED,+DAA+D;AAC/D,wBAAgB,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAIrD;AAED,wEAAwE;AACxE,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAEzD;AAED,mFAAmF;AACnF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,GAAG,MAAM,CAEzE;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAQhF;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAU3F;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAcpE"}
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Pure helpers for the Spark adapter.
3
+ *
4
+ * Extracted from the extension's spark adapter so the adapter file can stay
5
+ * focused on the IProtocolAdapter surface + RPC orchestration. Everything in
6
+ * this module is side-effect free.
7
+ */
8
+ import { bech32m } from '@scure/base';
9
+ import { normalizeTxHash } from './spark-sent-token-records.js';
10
+ /**
11
+ * Render a raw integer amount in the asset's display precision.
12
+ * Always emits exactly `precision` decimal digits (no trailing-zero trim) —
13
+ * callers that want a tighter rendering should post-process.
14
+ */
15
+ export function formatAmount(amount, precision) {
16
+ return (amount / Math.pow(10, precision)).toFixed(precision);
17
+ }
18
+ /**
19
+ * Map a Spark transfer status string to our unified TransactionStatus.
20
+ *
21
+ * The Spark SDK ships two related but distinct status vocabularies:
22
+ * 1. Native transfers use the `TRANSFER_STATUS_*` enum from the SDK
23
+ * (COMPLETED / EXPIRED / RETURNED / SENDER_INITIATED /
24
+ * RECEIVER_KEY_TWEAKED).
25
+ * 2. Lightning send requests use a looser lowercase vocabulary
26
+ * (`completed` / `complete` / `succeeded` / `success` / `failed` /
27
+ * `error`) which has drifted across SDK versions.
28
+ *
29
+ * Both are mapped here so callers don't need to know which vocabulary a
30
+ * given record came from.
31
+ */
32
+ export function mapTransferStatus(status) {
33
+ if (!status)
34
+ return 'pending';
35
+ // SDK TransferStatus enum keys.
36
+ if (status === 'TRANSFER_STATUS_COMPLETED')
37
+ return 'confirmed';
38
+ if (status === 'TRANSFER_STATUS_EXPIRED' || status === 'TRANSFER_STATUS_RETURNED') {
39
+ return 'failed';
40
+ }
41
+ if (status === 'TRANSFER_STATUS_SENDER_INITIATED' ||
42
+ status === 'TRANSFER_STATUS_RECEIVER_KEY_TWEAKED') {
43
+ return 'pending';
44
+ }
45
+ // LightningSendRequest status vocabulary — case-insensitive.
46
+ const s = status.toLowerCase();
47
+ if (s === 'completed' || s === 'complete' || s === 'succeeded' || s === 'success') {
48
+ return 'confirmed';
49
+ }
50
+ if (s === 'failed' || s === 'error')
51
+ return 'failed';
52
+ return 'pending';
53
+ }
54
+ /**
55
+ * Wrap a promise with a rejection timeout. Used to fail fast on slow Spark
56
+ * RPC calls; the SDK's own 30 s ceiling is too long for popup UI.
57
+ */
58
+ export function withTimeout(promise, timeoutMs, label) {
59
+ return Promise.race([
60
+ promise,
61
+ new Promise((_, reject) => setTimeout(() => reject(new Error(`${label} timed out after ${timeoutMs}ms`)), timeoutMs)),
62
+ ]);
63
+ }
64
+ /**
65
+ * True when a Spark balance snapshot represents a fresh / still-syncing
66
+ * wallet — zero sats AND no token balances. The adapter applies a shorter
67
+ * TTL to empty snapshots so the UI doesn't get stuck on "0 sats" while the
68
+ * Spark wallet syncs.
69
+ */
70
+ export function isEmptyBalance(value) {
71
+ const raw = value?.balance;
72
+ const sats = typeof raw === 'bigint' ? raw : BigInt(raw ?? 0);
73
+ const tokenCount = value?.tokenBalances instanceof Map ? value.tokenBalances.size : 0;
74
+ return sats === 0n && tokenCount === 0;
75
+ }
76
+ /** Convert a Uint8Array to lowercase hex string. */
77
+ export function u8aToHex(bytes) {
78
+ return Array.from(bytes)
79
+ .map((b) => b.toString(16).padStart(2, '0'))
80
+ .join('');
81
+ }
82
+ /** Convert a big-endian Uint8Array to bigint (uint128 max). */
83
+ export function u8aToBigInt(bytes) {
84
+ let result = 0n;
85
+ for (const b of bytes)
86
+ result = (result << 8n) | BigInt(b);
87
+ return result;
88
+ }
89
+ /** Hex tx hash from raw bytes, run through the project's normalizer. */
90
+ export function txHashFromBytes(bytes) {
91
+ return normalizeTxHash(u8aToHex(bytes));
92
+ }
93
+ /** Normalized raw token id from raw bytes; empty string when bytes are missing. */
94
+ export function rawTokenIdFromBytes(bytes) {
95
+ return bytes ? normalizeTxHash(u8aToHex(bytes)) : '';
96
+ }
97
+ /**
98
+ * Decode a bech32m-encoded Spark token id (e.g. `btkn1…`) back to its
99
+ * normalized raw hex form. Returns `""` for falsy input or decode failures —
100
+ * tokens that aren't bech32m-encoded simply round-trip through the empty
101
+ * string and fall back to the caller's other matchers.
102
+ */
103
+ export function rawTokenIdFromBech32mTokenId(tokenId) {
104
+ if (!tokenId)
105
+ return '';
106
+ try {
107
+ const decoded = bech32m.decode(tokenId, 500);
108
+ return rawTokenIdFromBytes(new Uint8Array(bech32m.fromWords(decoded.words)));
109
+ }
110
+ catch {
111
+ return '';
112
+ }
113
+ }
114
+ /**
115
+ * Cross-format token id comparison: matches identical strings directly,
116
+ * otherwise decodes both via bech32m / normalizeTxHash and compares the
117
+ * raw forms. Returns false when either side is empty.
118
+ */
119
+ export function tokenRefsMatch(left, right) {
120
+ const normalizedLeft = left?.trim();
121
+ const normalizedRight = right?.trim();
122
+ if (!normalizedLeft || !normalizedRight)
123
+ return false;
124
+ if (normalizedLeft === normalizedRight)
125
+ return true;
126
+ const leftRaw = rawTokenIdFromBech32mTokenId(normalizedLeft) || normalizeTxHash(normalizedLeft);
127
+ const rightRaw = rawTokenIdFromBech32mTokenId(normalizedRight) || normalizeTxHash(normalizedRight);
128
+ return !!leftRaw && leftRaw === rightRaw;
129
+ }
130
+ /**
131
+ * Parse one of the SDK's polymorphic expiry shapes (Date | number | ISO
132
+ * string) into a finite millisecond timestamp. Returns undefined for
133
+ * unparseable / falsy / Infinity values so callers can branch on absence.
134
+ */
135
+ export function parseSdkExpiryMs(expiry) {
136
+ if (!expiry)
137
+ return undefined;
138
+ if (expiry instanceof Date) {
139
+ const time = expiry.getTime();
140
+ return Number.isFinite(time) ? time : undefined;
141
+ }
142
+ if (typeof expiry === 'number') {
143
+ return Number.isFinite(expiry) ? expiry : undefined;
144
+ }
145
+ if (typeof expiry === 'string') {
146
+ const time = new Date(expiry).getTime();
147
+ return Number.isFinite(time) ? time : undefined;
148
+ }
149
+ return undefined;
150
+ }
151
+ //# sourceMappingURL=spark-helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spark-helpers.js","sourceRoot":"","sources":["../../src/lib/spark-helpers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Spark sent-token-transaction outbox.
3
+ *
4
+ * The Spark SDK exposes no direction for token transactions — a
5
+ * `queryTokenTransactions*` response cannot tell a send apart from a receive
6
+ * (both leave the wallet owning an output: the received amount, or the change
7
+ * of a send). A send with no change output is not returned at all.
8
+ *
9
+ * To make outgoing token transfers visible in history we persist a record of
10
+ * every send the wallet performs. This module is the single source of truth
11
+ * for that outbox; it is intentionally dependency-light so it can be used both
12
+ * by the SparkAdapter and by the low-level `transferTokens` wrapper in
13
+ * `spark-client-manager.ts` without import cycles.
14
+ *
15
+ * Storage is platform-agnostic: it uses the engine's ports storage
16
+ * (`getPlatform()?.storage`, an `IStorageProvider`), persisting the whole
17
+ * record array as a JSON string under a single key. When no platform is set it
18
+ * degrades to an in-module in-memory Map so callers never crash.
19
+ */
20
+ /** Cap on retained records — newest first, oldest dropped. */
21
+ export declare const MAX_SENT_TOKEN_TX_HISTORY = 200;
22
+ export interface SentTokenTxRecord {
23
+ hash: string;
24
+ /** Spark address of the wallet that created the send. Prevents cross-wallet misclassification. */
25
+ senderSparkAddress?: string;
26
+ /** Raw token amount (integer, before decimal division). */
27
+ amount: number;
28
+ assetId: string;
29
+ ticker: string;
30
+ name: string;
31
+ decimals: number;
32
+ timestamp: number;
33
+ }
34
+ /** Normalize transaction hashes across SDK/storage shapes. */
35
+ export declare function normalizeTxHash(hash: string): string;
36
+ export declare function loadSentTokenRecords(): Promise<SentTokenTxRecord[]>;
37
+ /**
38
+ * Persist a send record. Records are keyed by hash — re-saving the same hash
39
+ * replaces the earlier entry, so a richer record (with token metadata) written
40
+ * after a minimal one supersedes it.
41
+ */
42
+ export declare function saveSentTokenRecord(record: SentTokenTxRecord): Promise<void>;
43
+ //# sourceMappingURL=spark-sent-token-records.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spark-sent-token-records.d.ts","sourceRoot":"","sources":["../../src/lib/spark-sent-token-records.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAQH,8DAA8D;AAC9D,eAAO,MAAM,yBAAyB,MAAM,CAAA;AAE5C,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,kGAAkG;IAClG,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,2DAA2D;IAC3D,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;CAClB;AAsBD,8DAA8D;AAC9D,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAmCzE;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAalF"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Spark sent-token-transaction outbox.
3
+ *
4
+ * The Spark SDK exposes no direction for token transactions — a
5
+ * `queryTokenTransactions*` response cannot tell a send apart from a receive
6
+ * (both leave the wallet owning an output: the received amount, or the change
7
+ * of a send). A send with no change output is not returned at all.
8
+ *
9
+ * To make outgoing token transfers visible in history we persist a record of
10
+ * every send the wallet performs. This module is the single source of truth
11
+ * for that outbox; it is intentionally dependency-light so it can be used both
12
+ * by the SparkAdapter and by the low-level `transferTokens` wrapper in
13
+ * `spark-client-manager.ts` without import cycles.
14
+ *
15
+ * Storage is platform-agnostic: it uses the engine's ports storage
16
+ * (`getPlatform()?.storage`, an `IStorageProvider`), persisting the whole
17
+ * record array as a JSON string under a single key. When no platform is set it
18
+ * degrades to an in-module in-memory Map so callers never crash.
19
+ */
20
+ import { log } from './log.js';
21
+ import { getPlatform } from '../ports/index.js';
22
+ /** Single storage key holding the JSON-serialized record array. */
23
+ const STORAGE_KEY = 'sparkSentTokenTxHashes';
24
+ /** Cap on retained records — newest first, oldest dropped. */
25
+ export const MAX_SENT_TOKEN_TX_HISTORY = 200;
26
+ /** In-memory fallback used when no platform storage is injected. */
27
+ const memoryStore = new Map();
28
+ async function readRaw() {
29
+ const storage = getPlatform()?.storage;
30
+ if (storage) {
31
+ return storage.get(STORAGE_KEY);
32
+ }
33
+ return memoryStore.get(STORAGE_KEY) ?? null;
34
+ }
35
+ async function writeRaw(value) {
36
+ const storage = getPlatform()?.storage;
37
+ if (storage) {
38
+ await storage.set(STORAGE_KEY, value);
39
+ return;
40
+ }
41
+ memoryStore.set(STORAGE_KEY, value);
42
+ }
43
+ /** Normalize transaction hashes across SDK/storage shapes. */
44
+ export function normalizeTxHash(hash) {
45
+ return hash.trim().toLowerCase().replace(/^0x/, '');
46
+ }
47
+ export async function loadSentTokenRecords() {
48
+ try {
49
+ const raw = await readRaw();
50
+ if (!raw)
51
+ return [];
52
+ const stored = JSON.parse(raw);
53
+ if (!Array.isArray(stored))
54
+ return [];
55
+ return stored
56
+ .map((r) => {
57
+ if (typeof r === 'string') {
58
+ // Legacy format: plain hash string, no amount info — migrate in-place with amount 0.
59
+ return {
60
+ hash: normalizeTxHash(r),
61
+ senderSparkAddress: undefined,
62
+ amount: 0,
63
+ assetId: '',
64
+ ticker: 'TOKEN',
65
+ name: '',
66
+ decimals: 0,
67
+ timestamp: 0,
68
+ };
69
+ }
70
+ if (typeof r === 'object' &&
71
+ r !== null &&
72
+ typeof r.hash === 'string') {
73
+ const rec = r;
74
+ return { ...rec, hash: normalizeTxHash(rec.hash) };
75
+ }
76
+ return null;
77
+ })
78
+ .filter((r) => r !== null);
79
+ }
80
+ catch {
81
+ return [];
82
+ }
83
+ }
84
+ /**
85
+ * Persist a send record. Records are keyed by hash — re-saving the same hash
86
+ * replaces the earlier entry, so a richer record (with token metadata) written
87
+ * after a minimal one supersedes it.
88
+ */
89
+ export async function saveSentTokenRecord(record) {
90
+ try {
91
+ const normalizedRecord = { ...record, hash: normalizeTxHash(record.hash) };
92
+ if (!normalizedRecord.hash)
93
+ return;
94
+ const existing = await loadSentTokenRecords();
95
+ const updated = [
96
+ normalizedRecord,
97
+ ...existing.filter((r) => normalizeTxHash(r.hash) !== normalizedRecord.hash),
98
+ ].slice(0, MAX_SENT_TOKEN_TX_HISTORY);
99
+ await writeRaw(JSON.stringify(updated));
100
+ }
101
+ catch (err) {
102
+ log.error('[sent-token-records] Failed to save sent token transaction record:', err);
103
+ }
104
+ }
105
+ //# sourceMappingURL=spark-sent-token-records.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spark-sent-token-records.js","sourceRoot":"","sources":["../../src/lib/spark-sent-token-records.ts"],"names":[],"mappings":"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"}