@unicitylabs/sphere-sdk 0.13.2 → 0.13.3-dev.1

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/dist/index.cjs CHANGED
@@ -75,7 +75,6 @@ __export(index_exports, {
75
75
  WalletApiError: () => WalletApiError,
76
76
  WholeBlobInventoryAdapter: () => WholeBlobInventoryAdapter,
77
77
  addressesMatch: () => addressesMatch,
78
- archivedKeyFromTokenId: () => archivedKeyFromTokenId,
79
78
  areSameNametag: () => import_nostr_js_sdk5.areSameNametag,
80
79
  assertFieldEnvelopeShape: () => assertFieldEnvelopeShape,
81
80
  base58Decode: () => base58Decode,
@@ -88,8 +87,6 @@ __export(index_exports, {
88
87
  composeDeliveryKeys: () => composeDeliveryKeys,
89
88
  computeDeliveryId: () => computeDeliveryId,
90
89
  computeSwapId: () => computeSwapId,
91
- countCommittedTransactions: () => countCommittedTransactions,
92
- createAddress: () => createAddress,
93
90
  createCommunicationsModule: () => createCommunicationsModule,
94
91
  createGroupChatModule: () => createGroupChatModule,
95
92
  createKeyPair: () => createKeyPair,
@@ -100,15 +97,12 @@ __export(index_exports, {
100
97
  createSphere: () => createSphere,
101
98
  createSwapModule: () => createSwapModule,
102
99
  createTokenValidator: () => createTokenValidator,
103
- decodeBech32: () => decodeBech32,
104
100
  decrypt: () => decrypt,
105
- decryptCMasterKey: () => decryptCMasterKey,
106
101
  decryptField: () => decryptField,
107
102
  decryptFieldBytes: () => decryptFieldBytes,
108
103
  decryptJson: () => decryptJson,
109
104
  decryptMnemonic: () => decryptMnemonic,
110
105
  decryptNametag: () => import_nostr_js_sdk5.decryptNametag,
111
- decryptPrivateKey: () => decryptPrivateKey,
112
106
  decryptSimple: () => decryptSimple,
113
107
  decryptTextFormatKey: () => decryptTextFormatKey,
114
108
  decryptWithSalt: () => decryptWithSalt,
@@ -117,7 +111,6 @@ __export(index_exports, {
117
111
  deriveFieldEncryptionKey: () => deriveFieldEncryptionKey,
118
112
  deriveKeyAtPath: () => deriveKeyAtPath,
119
113
  doubleSha256: () => doubleSha256,
120
- encodeBech32: () => encodeBech32,
121
114
  encrypt: () => encrypt,
122
115
  encryptField: () => encryptField,
123
116
  encryptFieldBytes: () => encryptFieldBytes,
@@ -126,28 +119,21 @@ __export(index_exports, {
126
119
  encryptSimple: () => encryptSimple,
127
120
  extractFromText: () => extractFromText,
128
121
  findPattern: () => findPattern,
129
- forkedKeyFromTokenIdAndState: () => forkedKeyFromTokenIdAndState,
130
122
  formatAmount: () => formatAmount,
131
123
  generateAddressFromMasterKey: () => generateAddressFromMasterKey,
132
124
  generateMasterKey: () => generateMasterKey,
133
125
  generateMnemonic: () => generateMnemonic2,
134
- getAddressHrp: () => getAddressHrp,
135
126
  getAddressId: () => getAddressId,
136
127
  getAddressStorageKey: () => getAddressStorageKey,
137
128
  getCoinIdByName: () => getCoinIdByName,
138
129
  getCoinIdBySymbol: () => getCoinIdBySymbol,
139
- getCurrentStateHash: () => getCurrentStateHash,
140
130
  getPublicKey: () => getPublicKey,
141
131
  getSphere: () => getSphere,
142
132
  getTokenDecimals: () => getTokenDecimals,
143
133
  getTokenDefinition: () => getTokenDefinition,
144
134
  getTokenIconUrl: () => getTokenIconUrl,
145
- getTokenId: () => getTokenId,
146
135
  getTokenName: () => getTokenName,
147
136
  getTokenSymbol: () => getTokenSymbol,
148
- hasMissingNewStateHash: () => hasMissingNewStateHash,
149
- hasUncommittedTransactions: () => hasUncommittedTransactions,
150
- hasValidTxfData: () => hasValidTxfData,
151
137
  hash160: () => hash160,
152
138
  hashAddressForTag: () => import_nostr_js_sdk5.hashAddressForTag,
153
139
  hashNametag: () => import_nostr_js_sdk5.hashNametag,
@@ -155,22 +141,17 @@ __export(index_exports, {
155
141
  hexToBytes: () => hexToBytes4,
156
142
  identityFromMnemonicSync: () => identityFromMnemonicSync,
157
143
  initSphere: () => initSphere,
158
- isArchivedKey: () => isArchivedKey,
159
- isForkedKey: () => isForkedKey,
160
144
  isKnownToken: () => isKnownToken,
161
145
  isPhoneNumber: () => import_nostr_js_sdk5.isPhoneNumber,
162
146
  isPossiblyCommittedSendOutcome: () => isPossiblyCommittedSendOutcome,
163
- isSQLiteDatabase: () => isSQLiteDatabase,
164
147
  isSphereError: () => isSphereError,
165
148
  isTextWalletEncrypted: () => isTextWalletEncrypted,
166
149
  isTokenKey: () => isTokenKey,
167
150
  isValidAddress: () => isValidAddress,
168
- isValidBech32: () => isValidBech32,
169
151
  isValidDirectAddress: () => isValidDirectAddress,
170
152
  isValidNametag: () => isValidNametag2,
171
153
  isValidPrivateKey: () => isValidPrivateKey,
172
154
  isValidTokenId: () => isValidTokenId,
173
- isWalletDatEncrypted: () => isWalletDatEncrypted,
174
155
  isWalletTextFormat: () => isWalletTextFormat,
175
156
  keyFromTokenId: () => keyFromTokenId,
176
157
  loadSphere: () => loadSphere,
@@ -179,15 +160,10 @@ __export(index_exports, {
179
160
  normalizeAddress: () => normalizeAddress,
180
161
  normalizeCoinId: () => normalizeCoinId,
181
162
  normalizeNametag: () => import_nostr_js_sdk5.normalizeNametag,
182
- normalizeSdkTokenToStorage: () => normalizeSdkTokenToStorage,
183
- objectToTxf: () => objectToTxf,
184
163
  parseAddress: () => parseAddress,
185
- parseAndDecryptWalletDat: () => parseAndDecryptWalletDat,
186
164
  parseAndDecryptWalletText: () => parseAndDecryptWalletText,
187
- parseForkedKey: () => parseForkedKey,
188
165
  parseTokenAmount: () => parseTokenAmount,
189
166
  parseTxfStorageData: () => parseTxfStorageData,
190
- parseWalletDat: () => parseWalletDat,
191
167
  parseWalletText: () => parseWalletText,
192
168
  randomBytes: () => randomBytes4,
193
169
  randomHex: () => randomHex,
@@ -201,10 +177,7 @@ __export(index_exports, {
201
177
  sleep: () => sleep,
202
178
  sphereExists: () => sphereExists,
203
179
  toHumanReadable: () => toHumanReadable,
204
- tokenIdFromArchivedKey: () => tokenIdFromArchivedKey,
205
180
  tokenIdFromKey: () => tokenIdFromKey,
206
- tokenToTxf: () => tokenToTxf,
207
- txfToToken: () => txfToToken,
208
181
  validateManifest: () => validateManifest,
209
182
  validateMnemonic: () => validateMnemonic2,
210
183
  verifyChallengeTemplate: () => verifyChallengeTemplate,
@@ -10539,17 +10512,9 @@ function decryptDeliveryBundle(key, envelope) {
10539
10512
  }
10540
10513
 
10541
10514
  // types/txf.ts
10542
- var ARCHIVED_PREFIX = "archived-";
10543
- var FORKED_PREFIX = "_forked_";
10544
10515
  var RESERVED_KEYS = ["_meta", "_nametag", "_nametags", "_tombstones", "_invalidatedNametags", "_outbox", "_mintOutbox", "_sent", "_invalid", "_integrity", "_history"];
10545
10516
  function isTokenKey(key) {
10546
- return key.startsWith("_") && !key.startsWith(ARCHIVED_PREFIX) && !key.startsWith(FORKED_PREFIX) && !RESERVED_KEYS.includes(key);
10547
- }
10548
- function isArchivedKey(key) {
10549
- return key.startsWith(ARCHIVED_PREFIX);
10550
- }
10551
- function isForkedKey(key) {
10552
- return key.startsWith(FORKED_PREFIX);
10517
+ return key.startsWith("_") && !RESERVED_KEYS.includes(key);
10553
10518
  }
10554
10519
  function tokenIdFromKey(key) {
10555
10520
  return key.startsWith("_") ? key.substring(1) : key;
@@ -10557,27 +10522,112 @@ function tokenIdFromKey(key) {
10557
10522
  function keyFromTokenId(tokenId) {
10558
10523
  return `_${tokenId}`;
10559
10524
  }
10560
- function tokenIdFromArchivedKey(key) {
10561
- return key.startsWith(ARCHIVED_PREFIX) ? key.substring(ARCHIVED_PREFIX.length) : key;
10525
+ function isValidTokenId(tokenId) {
10526
+ return /^[0-9a-fA-F]{64}$/.test(tokenId);
10562
10527
  }
10563
- function archivedKeyFromTokenId(tokenId) {
10564
- return `${ARCHIVED_PREFIX}${tokenId}`;
10528
+
10529
+ // serialization/txf-serializer.ts
10530
+ function isV2TokenBlob(sdkData) {
10531
+ return typeof sdkData === "string" && sdkData.length >= 2 && sdkData.length % 2 === 0 && sdkData[0] !== "{" && /^[0-9a-f]+$/i.test(sdkData);
10565
10532
  }
10566
- function forkedKeyFromTokenIdAndState(tokenId, stateHash) {
10567
- return `${FORKED_PREFIX}${tokenId}_${stateHash}`;
10533
+ function v2TokenId(token) {
10534
+ return token.id.startsWith("v2_") ? token.id.slice(3) : token.id;
10568
10535
  }
10569
- function parseForkedKey(key) {
10570
- if (!key.startsWith(FORKED_PREFIX)) return null;
10571
- const remainder = key.substring(FORKED_PREFIX.length);
10572
- const underscoreIndex = remainder.indexOf("_");
10573
- if (underscoreIndex === -1 || underscoreIndex < 64) return null;
10574
- return {
10575
- tokenId: remainder.substring(0, underscoreIndex),
10576
- stateHash: remainder.substring(underscoreIndex + 1)
10536
+ function isV2TokenEntry(entry) {
10537
+ return typeof entry === "object" && entry !== null && !("genesis" in entry) && isV2TokenBlob(entry.sdkData);
10538
+ }
10539
+ async function buildTxfStorageData(tokens, meta, options) {
10540
+ const storageData = {
10541
+ _meta: {
10542
+ ...meta,
10543
+ formatVersion: "2.0"
10544
+ }
10577
10545
  };
10546
+ if (options?.nametags && options.nametags.length > 0) {
10547
+ storageData._nametags = options.nametags;
10548
+ }
10549
+ if (options?.tombstones && options.tombstones.length > 0) {
10550
+ storageData._tombstones = options.tombstones;
10551
+ }
10552
+ if (options?.historyEntries && options.historyEntries.length > 0) {
10553
+ storageData._history = options.historyEntries;
10554
+ }
10555
+ for (const token of tokens) {
10556
+ if (isV2TokenBlob(token.sdkData)) {
10557
+ storageData[keyFromTokenId(v2TokenId(token))] = token;
10558
+ } else {
10559
+ logger.warn(
10560
+ "TXF",
10561
+ `Refusing to persist token ${token.id.slice(0, 16)}...: sdkData is not a v2 token blob \u2014 entry skipped`
10562
+ );
10563
+ }
10564
+ }
10565
+ return storageData;
10578
10566
  }
10579
- function isValidTokenId(tokenId) {
10580
- return /^[0-9a-fA-F]{64}$/.test(tokenId);
10567
+ function parseTxfStorageData(data) {
10568
+ const result = {
10569
+ tokens: [],
10570
+ meta: null,
10571
+ nametags: [],
10572
+ tombstones: [],
10573
+ historyEntries: [],
10574
+ validationErrors: []
10575
+ };
10576
+ if (!data || typeof data !== "object") {
10577
+ result.validationErrors.push("Storage data is not an object");
10578
+ return result;
10579
+ }
10580
+ const storageData = data;
10581
+ if (storageData._meta && typeof storageData._meta === "object") {
10582
+ result.meta = storageData._meta;
10583
+ }
10584
+ const seenNames = /* @__PURE__ */ new Set();
10585
+ if (Array.isArray(storageData._nametags)) {
10586
+ for (const entry of storageData._nametags) {
10587
+ if (entry && typeof entry === "object" && typeof entry.name === "string") {
10588
+ result.nametags.push(entry);
10589
+ seenNames.add(entry.name);
10590
+ }
10591
+ }
10592
+ }
10593
+ if (storageData._nametag && typeof storageData._nametag === "object") {
10594
+ const legacy = storageData._nametag;
10595
+ if (typeof legacy.name === "string" && !seenNames.has(legacy.name)) {
10596
+ result.nametags.push(legacy);
10597
+ }
10598
+ }
10599
+ if (storageData._tombstones && Array.isArray(storageData._tombstones)) {
10600
+ for (const entry of storageData._tombstones) {
10601
+ if (typeof entry === "object" && entry !== null && typeof entry.tokenId === "string" && typeof entry.stateHash === "string" && typeof entry.timestamp === "number") {
10602
+ result.tombstones.push(entry);
10603
+ }
10604
+ }
10605
+ }
10606
+ if (Array.isArray(storageData._history)) {
10607
+ for (const entry of storageData._history) {
10608
+ if (typeof entry === "object" && entry !== null && typeof entry.dedupKey === "string" && typeof entry.type === "string") {
10609
+ result.historyEntries.push(entry);
10610
+ }
10611
+ }
10612
+ }
10613
+ for (const key of Object.keys(storageData)) {
10614
+ if (isTokenKey(key)) {
10615
+ const tokenId = tokenIdFromKey(key);
10616
+ const entry = storageData[key];
10617
+ if (isV2TokenEntry(entry)) {
10618
+ result.tokens.push(entry);
10619
+ } else {
10620
+ const msg = `Token ${tokenId}: unrecognized storage entry (not a v2 token blob) \u2014 entry skipped`;
10621
+ logger.warn("TXF", msg);
10622
+ result.validationErrors.push(msg);
10623
+ }
10624
+ } else if (key.startsWith("token-")) {
10625
+ const msg = `Token ${key}: sdkData is not a v2 token blob \u2014 entry skipped`;
10626
+ logger.warn("TXF", msg);
10627
+ result.validationErrors.push(msg);
10628
+ }
10629
+ }
10630
+ return result;
10581
10631
  }
10582
10632
 
10583
10633
  // registry/TokenRegistry.ts
@@ -11018,403 +11068,14 @@ function getCoinIdByName(name) {
11018
11068
  }
11019
11069
  function normalizeCoinId(coinId) {
11020
11070
  if (coinId.length <= 20 && /^[A-Za-z0-9]+$/.test(coinId)) {
11021
- const resolved = getCoinIdBySymbol(coinId);
11022
- if (resolved) return resolved;
11023
- }
11024
- return coinId;
11025
- }
11026
- function coinIdsMatch(a, b) {
11027
- if (a === b) return true;
11028
- return normalizeCoinId(a) === normalizeCoinId(b);
11029
- }
11030
-
11031
- // serialization/txf-serializer.ts
11032
- function bytesToHex5(bytes) {
11033
- const arr = Array.isArray(bytes) ? bytes : Array.from(bytes);
11034
- return arr.map((b) => b.toString(16).padStart(2, "0")).join("");
11035
- }
11036
- function normalizeToHex(value) {
11037
- if (typeof value === "string") {
11038
- return value;
11039
- }
11040
- if (value && typeof value === "object") {
11041
- const obj = value;
11042
- if ("bytes" in obj && (Array.isArray(obj.bytes) || obj.bytes instanceof Uint8Array)) {
11043
- return bytesToHex5(obj.bytes);
11044
- }
11045
- if (obj.type === "Buffer" && Array.isArray(obj.data)) {
11046
- return bytesToHex5(obj.data);
11047
- }
11048
- }
11049
- return String(value);
11050
- }
11051
- function normalizeSdkTokenToStorage(sdkTokenJson) {
11052
- const txf = structuredClone(sdkTokenJson);
11053
- if (txf.genesis?.data) {
11054
- const data = txf.genesis.data;
11055
- if (data.tokenId !== void 0) {
11056
- data.tokenId = normalizeToHex(data.tokenId);
11057
- }
11058
- if (data.tokenType !== void 0) {
11059
- data.tokenType = normalizeToHex(data.tokenType);
11060
- }
11061
- if (data.salt !== void 0) {
11062
- data.salt = normalizeToHex(data.salt);
11063
- }
11064
- }
11065
- if (txf.genesis?.inclusionProof?.authenticator) {
11066
- const auth = txf.genesis.inclusionProof.authenticator;
11067
- if (auth.publicKey !== void 0) {
11068
- auth.publicKey = normalizeToHex(auth.publicKey);
11069
- }
11070
- if (auth.signature !== void 0) {
11071
- auth.signature = normalizeToHex(auth.signature);
11072
- }
11073
- }
11074
- if (Array.isArray(txf.transactions)) {
11075
- for (const tx of txf.transactions) {
11076
- if (tx.inclusionProof?.authenticator) {
11077
- const auth = tx.inclusionProof.authenticator;
11078
- if (auth.publicKey !== void 0) {
11079
- auth.publicKey = normalizeToHex(auth.publicKey);
11080
- }
11081
- if (auth.signature !== void 0) {
11082
- auth.signature = normalizeToHex(auth.signature);
11083
- }
11084
- }
11085
- }
11086
- }
11087
- return txf;
11088
- }
11089
- function tokenToTxf(token) {
11090
- const jsonData = token.sdkData;
11091
- if (!jsonData) {
11092
- return null;
11093
- }
11094
- try {
11095
- const txfData = normalizeSdkTokenToStorage(JSON.parse(jsonData));
11096
- if (!txfData.genesis || !txfData.state) {
11097
- return null;
11098
- }
11099
- if (!txfData.version) {
11100
- txfData.version = "2.0";
11101
- }
11102
- if (!txfData.transactions) {
11103
- txfData.transactions = [];
11104
- }
11105
- if (!txfData.nametags) {
11106
- txfData.nametags = [];
11107
- }
11108
- if (!txfData._integrity) {
11109
- txfData._integrity = {
11110
- genesisDataJSONHash: "0000" + "0".repeat(60)
11111
- };
11112
- }
11113
- return txfData;
11114
- } catch {
11115
- return null;
11116
- }
11117
- }
11118
- function objectToTxf(obj) {
11119
- if (!obj.sdkData) return null;
11120
- try {
11121
- const txfData = normalizeSdkTokenToStorage(JSON.parse(obj.sdkData));
11122
- if (!txfData.genesis || !txfData.state) return null;
11123
- return txfData;
11124
- } catch {
11125
- return null;
11126
- }
11127
- }
11128
- function determineTokenStatus(txf) {
11129
- if (txf.transactions.length > 0) {
11130
- const lastTx = txf.transactions[txf.transactions.length - 1];
11131
- if (lastTx.inclusionProof === null) {
11132
- return "pending";
11133
- }
11134
- }
11135
- return "confirmed";
11136
- }
11137
- function txfToToken(tokenId, txf) {
11138
- const coinData = txf.genesis.data.coinData;
11139
- const totalAmount = coinData.reduce((sum, [, amt]) => {
11140
- return sum + BigInt(amt || "0");
11141
- }, BigInt(0));
11142
- let coinId = coinData[0]?.[0] || "";
11143
- for (const [cid, amt] of coinData) {
11144
- if (BigInt(amt || "0") > 0) {
11145
- coinId = cid;
11146
- break;
11147
- }
11148
- }
11149
- const tokenType = txf.genesis.data.tokenType;
11150
- const isNft = tokenType === "455ad8720656b08e8dbd5bac1f3c73eeea5431565f6c1c3af742b1aa12d41d89";
11151
- const now = Date.now();
11152
- const registry = TokenRegistry.getInstance();
11153
- const def = registry.getDefinition(coinId);
11154
- return {
11155
- id: tokenId,
11156
- coinId,
11157
- symbol: isNft ? "NFT" : def?.symbol || coinId.slice(0, 8),
11158
- name: isNft ? "NFT" : def?.name ? def.name.charAt(0).toUpperCase() + def.name.slice(1) : "Token",
11159
- decimals: isNft ? 0 : def?.decimals ?? 8,
11160
- amount: totalAmount.toString(),
11161
- status: determineTokenStatus(txf),
11162
- createdAt: now,
11163
- updatedAt: now,
11164
- sdkData: JSON.stringify(txf)
11165
- };
11166
- }
11167
- function isV2TokenBlob(sdkData) {
11168
- return typeof sdkData === "string" && sdkData.length >= 2 && sdkData.length % 2 === 0 && sdkData[0] !== "{" && /^[0-9a-f]+$/i.test(sdkData);
11169
- }
11170
- function v2TokenId(token) {
11171
- return token.id.startsWith("v2_") ? token.id.slice(3) : token.id;
11172
- }
11173
- function isV2TokenEntry(entry) {
11174
- return typeof entry === "object" && entry !== null && !("genesis" in entry) && isV2TokenBlob(entry.sdkData);
11175
- }
11176
- async function buildTxfStorageData(tokens, meta, options) {
11177
- const storageData = {
11178
- _meta: {
11179
- ...meta,
11180
- formatVersion: "2.0"
11181
- }
11182
- };
11183
- if (options?.nametags && options.nametags.length > 0) {
11184
- storageData._nametags = options.nametags;
11185
- }
11186
- if (options?.tombstones && options.tombstones.length > 0) {
11187
- storageData._tombstones = options.tombstones;
11188
- }
11189
- if (options?.outboxEntries && options.outboxEntries.length > 0) {
11190
- storageData._outbox = options.outboxEntries;
11191
- }
11192
- if (options?.mintOutboxEntries && options.mintOutboxEntries.length > 0) {
11193
- storageData._mintOutbox = options.mintOutboxEntries;
11194
- }
11195
- if (options?.invalidatedNametags && options.invalidatedNametags.length > 0) {
11196
- storageData._invalidatedNametags = options.invalidatedNametags;
11197
- }
11198
- if (options?.historyEntries && options.historyEntries.length > 0) {
11199
- storageData._history = options.historyEntries;
11200
- }
11201
- for (const token of tokens) {
11202
- const txf = tokenToTxf(token);
11203
- if (txf) {
11204
- const actualTokenId = txf.genesis.data.tokenId;
11205
- storageData[keyFromTokenId(actualTokenId)] = txf;
11206
- } else if (isV2TokenBlob(token.sdkData)) {
11207
- storageData[keyFromTokenId(v2TokenId(token))] = token;
11208
- }
11209
- }
11210
- if (options?.archivedTokens && options.archivedTokens.size > 0) {
11211
- for (const [tokenId, txf] of options.archivedTokens) {
11212
- storageData[archivedKeyFromTokenId(tokenId)] = txf;
11213
- }
11214
- }
11215
- if (options?.forkedTokens && options.forkedTokens.size > 0) {
11216
- for (const [key, txf] of options.forkedTokens) {
11217
- const [tokenId, stateHash] = key.split("_");
11218
- if (tokenId && stateHash) {
11219
- storageData[forkedKeyFromTokenIdAndState(tokenId, stateHash)] = txf;
11220
- }
11221
- }
11222
- }
11223
- return storageData;
11224
- }
11225
- function parseTxfStorageData(data) {
11226
- const result = {
11227
- tokens: [],
11228
- meta: null,
11229
- nametags: [],
11230
- tombstones: [],
11231
- archivedTokens: /* @__PURE__ */ new Map(),
11232
- forkedTokens: /* @__PURE__ */ new Map(),
11233
- outboxEntries: [],
11234
- mintOutboxEntries: [],
11235
- invalidatedNametags: [],
11236
- historyEntries: [],
11237
- validationErrors: []
11238
- };
11239
- if (!data || typeof data !== "object") {
11240
- result.validationErrors.push("Storage data is not an object");
11241
- return result;
11242
- }
11243
- const storageData = data;
11244
- if (storageData._meta && typeof storageData._meta === "object") {
11245
- result.meta = storageData._meta;
11246
- }
11247
- const seenNames = /* @__PURE__ */ new Set();
11248
- if (Array.isArray(storageData._nametags)) {
11249
- for (const entry of storageData._nametags) {
11250
- if (entry && typeof entry === "object" && typeof entry.name === "string") {
11251
- result.nametags.push(entry);
11252
- seenNames.add(entry.name);
11253
- }
11254
- }
11255
- }
11256
- if (storageData._nametag && typeof storageData._nametag === "object") {
11257
- const legacy = storageData._nametag;
11258
- if (typeof legacy.name === "string" && !seenNames.has(legacy.name)) {
11259
- result.nametags.push(legacy);
11260
- }
11261
- }
11262
- if (storageData._tombstones && Array.isArray(storageData._tombstones)) {
11263
- for (const entry of storageData._tombstones) {
11264
- if (typeof entry === "object" && entry !== null && typeof entry.tokenId === "string" && typeof entry.stateHash === "string" && typeof entry.timestamp === "number") {
11265
- result.tombstones.push(entry);
11266
- }
11267
- }
11268
- }
11269
- if (storageData._outbox && Array.isArray(storageData._outbox)) {
11270
- for (const entry of storageData._outbox) {
11271
- if (typeof entry === "object" && entry !== null && typeof entry.id === "string" && typeof entry.status === "string") {
11272
- result.outboxEntries.push(entry);
11273
- }
11274
- }
11275
- }
11276
- if (storageData._mintOutbox && Array.isArray(storageData._mintOutbox)) {
11277
- for (const entry of storageData._mintOutbox) {
11278
- if (typeof entry === "object" && entry !== null && typeof entry.id === "string" && typeof entry.status === "string") {
11279
- result.mintOutboxEntries.push(entry);
11280
- }
11281
- }
11282
- }
11283
- if (storageData._invalidatedNametags && Array.isArray(storageData._invalidatedNametags)) {
11284
- for (const entry of storageData._invalidatedNametags) {
11285
- if (typeof entry === "object" && entry !== null && typeof entry.name === "string") {
11286
- result.invalidatedNametags.push(entry);
11287
- }
11288
- }
11289
- }
11290
- if (Array.isArray(storageData._history)) {
11291
- for (const entry of storageData._history) {
11292
- if (typeof entry === "object" && entry !== null && typeof entry.dedupKey === "string" && typeof entry.type === "string") {
11293
- result.historyEntries.push(entry);
11294
- }
11295
- }
11296
- }
11297
- for (const key of Object.keys(storageData)) {
11298
- if (isTokenKey(key)) {
11299
- const tokenId = tokenIdFromKey(key);
11300
- try {
11301
- const txfToken = storageData[key];
11302
- if (txfToken?.genesis?.data?.tokenId) {
11303
- const token = txfToToken(tokenId, txfToken);
11304
- result.tokens.push(token);
11305
- } else if (isV2TokenEntry(storageData[key])) {
11306
- result.tokens.push(storageData[key]);
11307
- }
11308
- } catch (err) {
11309
- result.validationErrors.push(`Token ${tokenId}: ${err}`);
11310
- }
11311
- } else if (isArchivedKey(key)) {
11312
- const tokenId = tokenIdFromArchivedKey(key);
11313
- try {
11314
- const txfToken = storageData[key];
11315
- if (txfToken?.genesis?.data?.tokenId) {
11316
- result.archivedTokens.set(tokenId, txfToken);
11317
- }
11318
- } catch {
11319
- result.validationErrors.push(`Archived token ${tokenId}: invalid structure`);
11320
- }
11321
- } else if (isForkedKey(key)) {
11322
- const parsed = parseForkedKey(key);
11323
- if (parsed) {
11324
- try {
11325
- const txfToken = storageData[key];
11326
- if (txfToken?.genesis?.data?.tokenId) {
11327
- const mapKey = `${parsed.tokenId}_${parsed.stateHash}`;
11328
- result.forkedTokens.set(mapKey, txfToken);
11329
- }
11330
- } catch {
11331
- result.validationErrors.push(`Forked token ${parsed.tokenId}: invalid structure`);
11332
- }
11333
- }
11334
- } else if (key.startsWith("token-")) {
11335
- try {
11336
- const entry = storageData[key];
11337
- const txfToken = entry?.token;
11338
- if (txfToken?.genesis?.data?.tokenId) {
11339
- const tokenId = txfToken.genesis.data.tokenId;
11340
- const token = txfToToken(tokenId, txfToken);
11341
- result.tokens.push(token);
11342
- }
11343
- } catch (err) {
11344
- result.validationErrors.push(`Token ${key}: ${err}`);
11345
- }
11346
- }
11347
- }
11348
- return result;
11349
- }
11350
- function getTokenId(token) {
11351
- if (token.sdkData) {
11352
- try {
11353
- const txf = JSON.parse(token.sdkData);
11354
- if (txf.genesis?.data?.tokenId) {
11355
- return txf.genesis.data.tokenId;
11356
- }
11357
- } catch {
11358
- }
11359
- }
11360
- return token.id;
11361
- }
11362
- function getCurrentStateHash(txf) {
11363
- if (txf.transactions && txf.transactions.length > 0) {
11364
- const lastTx = txf.transactions[txf.transactions.length - 1];
11365
- if (lastTx?.newStateHash) {
11366
- return lastTx.newStateHash;
11367
- }
11368
- if (lastTx?.inclusionProof?.authenticator?.stateHash) {
11369
- return lastTx.inclusionProof.authenticator.stateHash;
11370
- }
11371
- }
11372
- if (txf._integrity?.currentStateHash) {
11373
- return txf._integrity.currentStateHash;
11374
- }
11375
- if (txf.genesis?.inclusionProof?.authenticator?.stateHash) {
11376
- return txf.genesis.inclusionProof.authenticator.stateHash;
11377
- }
11378
- return void 0;
11379
- }
11380
- function hasValidTxfData(token) {
11381
- if (!token.sdkData) return false;
11382
- try {
11383
- const txf = JSON.parse(token.sdkData);
11384
- return !!(txf.genesis && txf.genesis.data && txf.genesis.data.tokenId && txf.state && txf.genesis.inclusionProof);
11385
- } catch {
11386
- return false;
11387
- }
11388
- }
11389
- function hasUncommittedTransactions(token) {
11390
- if (!token.sdkData) return false;
11391
- try {
11392
- const txf = JSON.parse(token.sdkData);
11393
- if (!txf.transactions || txf.transactions.length === 0) return false;
11394
- return txf.transactions.some(
11395
- (tx) => tx.inclusionProof === null
11396
- );
11397
- } catch {
11398
- return false;
11399
- }
11400
- }
11401
- function hasMissingNewStateHash(txf) {
11402
- if (!txf.transactions || txf.transactions.length === 0) {
11403
- return false;
11071
+ const resolved = getCoinIdBySymbol(coinId);
11072
+ if (resolved) return resolved;
11404
11073
  }
11405
- return txf.transactions.some((tx) => !tx.newStateHash);
11074
+ return coinId;
11406
11075
  }
11407
- function countCommittedTransactions(token) {
11408
- if (!token.sdkData) return 0;
11409
- try {
11410
- const txf = JSON.parse(token.sdkData);
11411
- if (!txf.transactions) return 0;
11412
- return txf.transactions.filter(
11413
- (tx) => tx.inclusionProof !== null
11414
- ).length;
11415
- } catch {
11416
- return 0;
11417
- }
11076
+ function coinIdsMatch(a, b) {
11077
+ if (a === b) return true;
11078
+ return normalizeCoinId(a) === normalizeCoinId(b);
11418
11079
  }
11419
11080
 
11420
11081
  // core/utils.ts
@@ -11582,6 +11243,10 @@ function surfacedSendFailure(summary) {
11582
11243
  }
11583
11244
  return primaryError;
11584
11245
  }
11246
+ function currentStateHashOfTxf(txf) {
11247
+ const last = txf.transactions?.[txf.transactions.length - 1];
11248
+ return last?.newStateHash ?? last?.inclusionProof?.authenticator?.stateHash ?? txf._integrity?.currentStateHash ?? txf.genesis?.inclusionProof?.authenticator?.stateHash ?? "";
11249
+ }
11585
11250
  function genesisIdOf(token) {
11586
11251
  return extractTokenIdFromSdkData(token.sdkData) ?? token.id.replace(/^v2_/, "");
11587
11252
  }
@@ -11693,7 +11358,7 @@ function parseSdkDataCached(sdkData) {
11693
11358
  try {
11694
11359
  const txf = JSON.parse(sdkData);
11695
11360
  tokenId = txf.genesis?.data?.tokenId || null;
11696
- stateHash = getCurrentStateHash(txf) || "";
11361
+ stateHash = currentStateHashOfTxf(txf);
11697
11362
  if (!stateHash) {
11698
11363
  if (txf.state?.hash) {
11699
11364
  stateHash = txf.state.hash;
@@ -12439,7 +12104,6 @@ var PaymentsModule = class _PaymentsModule {
12439
12104
  if (op.kind === "direct") {
12440
12105
  finished = await ctx.engine.transfer({ token: op.sdkToken, recipientPubkey: ctx.recipientChainPubkey }, opts);
12441
12106
  } else {
12442
- const checkpointStore = this.getCheckpointStore();
12443
12107
  const { outputs } = await ctx.engine.split(
12444
12108
  {
12445
12109
  token: op.sdkToken,
@@ -12448,7 +12112,7 @@ var PaymentsModule = class _PaymentsModule {
12448
12112
  { recipientPubkey: ctx.selfChainPubkey, coinId: ctx.coinId, amount: op.remainderAmount }
12449
12113
  ]
12450
12114
  },
12451
- { ...opts, ...checkpointStore ? { checkpointStore } : {} }
12115
+ { ...opts, ...ctx.checkpointStore ? { checkpointStore: ctx.checkpointStore } : {} }
12452
12116
  );
12453
12117
  finished = outputs[0];
12454
12118
  changeOutput = outputs[1];
@@ -12635,7 +12299,8 @@ var PaymentsModule = class _PaymentsModule {
12635
12299
  recipientChainPubkeyHex,
12636
12300
  selfChainPubkey: hexToBytes4(this.deps.identity.chainPubkey),
12637
12301
  coinId: request.coinId,
12638
- ...request.memo !== void 0 ? { memo: request.memo } : {}
12302
+ ...request.memo !== void 0 ? { memo: request.memo } : {},
12303
+ ...this.getCheckpointStore() ? { checkpointStore: this.getCheckpointStore() } : {}
12639
12304
  })
12640
12305
  );
12641
12306
  attempt.summary = summary;
@@ -15152,8 +14817,11 @@ var PaymentsModule = class _PaymentsModule {
15152
14817
  try {
15153
14818
  const plain = decryptField(this.getFieldEncryptionKey(), intent.payload);
15154
14819
  payload = JSON.parse(plain);
15155
- if (payload.v !== 1 && payload.v !== 2 || !Array.isArray(payload.direct)) {
15156
- throw new SphereError("Unknown intent payload shape", "VALIDATION_ERROR");
14820
+ if (payload.v !== 2 || !Array.isArray(payload.direct)) {
14821
+ throw new SphereError(
14822
+ `Unsupported intent payload shape (v=${String(payload.v)}) \u2014 not resumable by this wallet version`,
14823
+ "VALIDATION_ERROR"
14824
+ );
15157
14825
  }
15158
14826
  } catch (err) {
15159
14827
  logger.warn("Payments", `Intent ${intent.transferId.slice(0, 8)}\u2026 payload undecodable \u2014 skipped:`, err);
@@ -15292,31 +14960,22 @@ var PaymentsModule = class _PaymentsModule {
15292
14960
  async resumeIntent(transferId, payload) {
15293
14961
  const engine = this.deps.tokenEngine;
15294
14962
  const walletApi = this.deps.walletApi;
15295
- const delivery = this.delivery;
15296
14963
  const provider = this.getActiveTokenStorageProvider();
15297
14964
  if (!provider) throw new SphereError("No token storage provider for intent resume", "STORAGE_ERROR");
15298
- const serverApply = delivery.custody === "inventory";
15299
- const recipientChainPubkey = hexToBytes4(payload.recipient);
15300
- const deliverBlob = async (tokenBlobHex, opIndex) => {
15301
- await this.savePendingV2Delivery({
15302
- transferId,
15303
- recipientPubkey: payload.recipient,
15304
- tokenBlob: tokenBlobHex,
15305
- memo: payload.memo,
15306
- opIndex,
15307
- createdAt: Date.now()
15308
- });
15309
- const nm = this.currentNametagName();
15310
- await this.tryDeliver(
15311
- () => delivery.deliver(payload.recipient, hexToBytes4(tokenBlobHex), {
15312
- transferId,
15313
- memo: payload.memo,
15314
- ...nm !== void 0 ? { senderNametag: nm } : {}
15315
- }),
15316
- tokenBlobHex
15317
- );
14965
+ const serverApply = this.delivery.custody === "inventory";
14966
+ const checkpointStore = this.getCheckpointStore();
14967
+ const ctx = {
14968
+ engine,
14969
+ transferId,
14970
+ recipientChainPubkey: hexToBytes4(payload.recipient),
14971
+ recipientChainPubkeyHex: payload.recipient,
14972
+ selfChainPubkey: hexToBytes4(this.deps.identity.chainPubkey),
14973
+ coinId: payload.coinId,
14974
+ ...payload.memo !== void 0 ? { memo: payload.memo } : {},
14975
+ ...checkpointStore ? { checkpointStore } : {}
15318
14976
  };
15319
14977
  const journaled = await this.journaledByOp(transferId);
14978
+ const blobs = [];
15320
14979
  const spent = [];
15321
14980
  let conflicted = false;
15322
14981
  let firstConflict;
@@ -15324,91 +14983,71 @@ var PaymentsModule = class _PaymentsModule {
15324
14983
  for (const [opIndex, genesisId] of payload.direct.entries()) {
15325
14984
  const existing = journaled.get(opIndex);
15326
14985
  if (existing) {
15327
- await deliverBlob(existing.tokenBlob, opIndex);
14986
+ blobs.push(existing.tokenBlob);
15328
14987
  spent.push(genesisId);
15329
- } else {
15330
- const source = await engine.decodeToken(await provider.getToken(genesisId));
15331
- try {
15332
- const finished = await engine.transfer(
15333
- { token: source, recipientPubkey: recipientChainPubkey },
15334
- // (transferId, opIndex) pairing replayed from the intent's persisted order (§8.1)
15335
- { signal: timeoutSignal(SEND_ENGINE_OP_TIMEOUT_MS), transferId, opIndex }
15336
- );
15337
- await deliverBlob(bytesToHex4(encodeTokenBlob(engine.encodeToken(finished))), opIndex);
15338
- spent.push(genesisId);
15339
- } catch (err) {
15340
- if (!(err instanceof TransferConflictError)) throw err;
15341
- conflicted = true;
15342
- firstConflict ??= err;
15343
- undeliveredAmount += engine.balanceOf(source, payload.coinId);
15344
- logger.warn("Payments", `Resume op ${opIndex} lost its source to a concurrent transfer \u2014 not delivered (\xA78.1/#4):`, err);
15345
- }
14988
+ continue;
14989
+ }
14990
+ const source = await engine.decodeToken(await provider.getToken(genesisId));
14991
+ const outcome = await this.certifyOperation(
14992
+ {
14993
+ kind: "direct",
14994
+ opIndex,
14995
+ uiTokenId: `v2_${genesisId}`,
14996
+ genesisId,
14997
+ sourceSdkData: "",
14998
+ sdkToken: source,
14999
+ deliveredAmount: engine.balanceOf(source, payload.coinId)
15000
+ },
15001
+ ctx
15002
+ );
15003
+ if (outcome.error !== void 0) {
15004
+ if (!(outcome.error instanceof TransferConflictError)) throw outcome.error;
15005
+ conflicted = true;
15006
+ firstConflict ??= outcome.error;
15007
+ undeliveredAmount += engine.balanceOf(source, payload.coinId);
15008
+ logger.warn("Payments", `Resume op ${opIndex} lost its source to a concurrent transfer \u2014 not delivered (\xA78.1/#4):`, outcome.error);
15009
+ continue;
15346
15010
  }
15011
+ blobs.push(outcome.tokenBlob);
15012
+ spent.push(genesisId);
15347
15013
  }
15348
15014
  let changeOutput = null;
15349
15015
  if (payload.split) {
15350
15016
  const splitOpIndex = payload.direct.length;
15351
- const existingSplit = journaled.get(splitOpIndex);
15352
- const checkpointStore = payload.v === 2 ? this.getCheckpointStore() : void 0;
15353
15017
  let splitDelivered = false;
15354
- if (checkpointStore === void 0 && existingSplit) {
15355
- await deliverBlob(existingSplit.tokenBlob, splitOpIndex);
15356
- splitDelivered = true;
15357
- } else {
15358
- try {
15359
- const blob = await provider.getToken(payload.split.tokenId);
15360
- const source = await engine.decodeToken(blob);
15361
- const selfChainPubkey = hexToBytes4(this.deps.identity.chainPubkey);
15362
- const { outputs } = await engine.split(
15363
- {
15364
- token: source,
15365
- outputs: [
15366
- {
15367
- recipientPubkey: recipientChainPubkey,
15368
- coinId: payload.coinId,
15369
- amount: BigInt(payload.split.splitAmount)
15370
- },
15371
- {
15372
- recipientPubkey: selfChainPubkey,
15373
- coinId: payload.coinId,
15374
- amount: BigInt(payload.split.remainderAmount)
15375
- }
15376
- ]
15377
- },
15378
- {
15379
- signal: timeoutSignal(SEND_ENGINE_OP_TIMEOUT_MS),
15380
- transferId,
15381
- opIndex: splitOpIndex,
15382
- ...checkpointStore ? { checkpointStore } : {}
15383
- }
15384
- );
15385
- changeOutput = outputs[1];
15386
- if (!serverApply) await this.storeEngineToken(engine, changeOutput, { criticalSave: true });
15387
- await deliverBlob(bytesToHex4(encodeTokenBlob(engine.encodeToken(outputs[0]))), splitOpIndex);
15388
- splitDelivered = true;
15389
- } catch (err) {
15390
- if (err instanceof ProofUnconfirmedError) throw err;
15391
- if (checkpointStore !== void 0 && (err instanceof CheckpointPersistFailedError || err instanceof SplitCheckpointLostError || err instanceof CheckpointTrustbaseMismatchError)) {
15392
- throw err;
15018
+ {
15019
+ const source = await engine.decodeToken(await provider.getToken(payload.split.tokenId));
15020
+ const outcome = await this.certifyOperation(
15021
+ {
15022
+ kind: "split",
15023
+ opIndex: splitOpIndex,
15024
+ uiTokenId: `v2_${payload.split.tokenId}`,
15025
+ genesisId: payload.split.tokenId,
15026
+ sourceSdkData: "",
15027
+ sdkToken: source,
15028
+ deliveredAmount: BigInt(payload.split.splitAmount),
15029
+ remainderAmount: BigInt(payload.split.remainderAmount)
15030
+ },
15031
+ ctx
15032
+ );
15033
+ if (outcome.error === void 0) {
15034
+ changeOutput = outcome.changeOutput ?? null;
15035
+ if (!serverApply && changeOutput) {
15036
+ await this.storeEngineToken(engine, changeOutput, { criticalSave: true });
15393
15037
  }
15394
- if (err instanceof TransferConflictError) {
15038
+ blobs.push(outcome.tokenBlob);
15039
+ splitDelivered = true;
15040
+ } else {
15041
+ splitDelivered = this.classifyResumeSplitFailure(outcome.error, (err) => {
15395
15042
  conflicted = true;
15396
15043
  firstConflict ??= err;
15397
15044
  undeliveredAmount += BigInt(payload.split.splitAmount);
15398
- logger.warn("Payments", `Resume split: source lost to a concurrent transfer \u2014 not delivered (\xA78.1/#4):`, err);
15399
- } else if (err instanceof SplitCheckpointLostError) {
15400
- logger.warn("Payments", `Resume split (legacy no-checkpoint): recording the spend; change recovers on the next inventory resync (\xA73.1):`, err);
15401
- this.deps.emitEvent("inventory:conflict", { transferId, coinId: payload.coinId, error: err.message });
15402
- splitDelivered = true;
15403
- } else {
15404
- throw err;
15405
- }
15045
+ });
15406
15046
  }
15407
15047
  }
15408
- if (splitDelivered) {
15409
- spent.push(payload.split.tokenId);
15410
- }
15048
+ if (splitDelivered) spent.push(payload.split.tokenId);
15411
15049
  }
15050
+ await this.deliverCommittedBlobs(blobs, payload.recipient, transferId, payload.memo);
15412
15051
  if (conflicted && spent.length === 0) throw firstConflict;
15413
15052
  if (serverApply) {
15414
15053
  await this.applyInventoryDelta(
@@ -15420,6 +15059,55 @@ var PaymentsModule = class _PaymentsModule {
15420
15059
  // the one the sources were read from, captured for the whole resume
15421
15060
  );
15422
15061
  }
15062
+ await this.dropResumedSources(spent, transferId, payload);
15063
+ if (conflicted) {
15064
+ const remaining = undeliveredAmount > 0n ? undeliveredAmount : 0n;
15065
+ const delivered = BigInt(payload.amount) - remaining;
15066
+ await walletApi.completeIntent(transferId);
15067
+ await this.recordResumedSentHistory(payload, transferId, delivered > 0n ? delivered.toString() : "0", spent[0]);
15068
+ this.deps.emitEvent("send:partial-remainder", {
15069
+ transferId,
15070
+ remainingAmount: remaining.toString(),
15071
+ coinId: payload.coinId,
15072
+ recipientPubkey: payload.recipient
15073
+ });
15074
+ throw new PartialSendConflictError(
15075
+ "Resume: part of the payment was delivered before a source was consumed by a concurrent transfer \u2014 the delivered legs are final; re-plan ONLY the remainder.",
15076
+ transferId,
15077
+ spent,
15078
+ // the DELIVERED genesisIds — never the conflicted leg
15079
+ remaining.toString(),
15080
+ firstConflict
15081
+ );
15082
+ }
15083
+ await walletApi.completeIntent(transferId);
15084
+ await this.recordResumedSentHistory(payload, transferId, payload.amount, payload.direct[0]);
15085
+ }
15086
+ /**
15087
+ * A resumed split that produced no output: a lost source means the leg delivered
15088
+ * nothing (returns false), and every checkpoint failure keeps the intent open by
15089
+ * throwing. There is no path here that records the spend.
15090
+ */
15091
+ classifyResumeSplitFailure(error, onConflict) {
15092
+ if (error instanceof ProofUnconfirmedError) throw error;
15093
+ if (error instanceof CheckpointPersistFailedError || error instanceof SplitCheckpointLostError || error instanceof CheckpointTrustbaseMismatchError) {
15094
+ throw error;
15095
+ }
15096
+ if (error instanceof TransferConflictError) {
15097
+ onConflict(error);
15098
+ logger.warn("Payments", "Resume split: source lost to a concurrent transfer \u2014 not delivered (\xA78.1/#4):", error);
15099
+ return false;
15100
+ }
15101
+ throw error;
15102
+ }
15103
+ /**
15104
+ * Drop the local records of the sources a resume consumed (present when
15105
+ * resuming on the originating device). M7: each removal carries the LOCAL state
15106
+ * we spent, so a source a concurrent claim reactivated to a NEW state (a
15107
+ * self-send round-trip) is KEPT, not destroyed.
15108
+ */
15109
+ async dropResumedSources(spent, transferId, payload) {
15110
+ const engine = this.deps.tokenEngine;
15423
15111
  for (const genesisId of spent) {
15424
15112
  const local = this.tokens.get(`v2_${genesisId}`);
15425
15113
  if (!local) continue;
@@ -15431,8 +15119,7 @@ var PaymentsModule = class _PaymentsModule {
15431
15119
  let spentOnChain = false;
15432
15120
  try {
15433
15121
  if (local.sdkData) {
15434
- const decoded = await engine.decodeToken(decodeTokenBlob(hexToBytes4(local.sdkData)));
15435
- spentOnChain = await engine.isSpent(decoded);
15122
+ spentOnChain = await engine.isSpent(await engine.decodeToken(decodeTokenBlob(hexToBytes4(local.sdkData))));
15436
15123
  }
15437
15124
  } catch {
15438
15125
  spentOnChain = false;
@@ -15446,47 +15133,19 @@ var PaymentsModule = class _PaymentsModule {
15446
15133
  );
15447
15134
  }
15448
15135
  }
15449
- if (conflicted) {
15450
- const remaining = undeliveredAmount;
15451
- const delivered = BigInt(payload.amount) - remaining;
15452
- await walletApi.completeIntent(transferId);
15453
- await this.addToHistory({
15454
- type: "SENT",
15455
- amount: (delivered > 0n ? delivered : 0n).toString(),
15456
- coinId: payload.coinId,
15457
- symbol: this.getCoinSymbol(payload.coinId),
15458
- timestamp: Date.now(),
15459
- recipientPubkey: payload.recipient,
15460
- memo: payload.memo,
15461
- transferId,
15462
- tokenId: spent[0] ? `v2_${spent[0]}` : void 0
15463
- });
15464
- this.deps.emitEvent("send:partial-remainder", {
15465
- transferId,
15466
- remainingAmount: (remaining > 0n ? remaining : 0n).toString(),
15467
- coinId: payload.coinId,
15468
- recipientPubkey: payload.recipient
15469
- });
15470
- throw new PartialSendConflictError(
15471
- "Resume: part of the payment was delivered before a source was consumed by a concurrent transfer \u2014 the delivered legs are final; re-plan ONLY the remainder.",
15472
- transferId,
15473
- spent,
15474
- // the DELIVERED genesisIds — never the conflicted leg
15475
- (remaining > 0n ? remaining : 0n).toString(),
15476
- firstConflict
15477
- );
15478
- }
15479
- await walletApi.completeIntent(transferId);
15136
+ }
15137
+ /** §10: the SENT record — same dedupKey as the original attempt, so a resume after the history POST is a server-side no-op. */
15138
+ async recordResumedSentHistory(payload, transferId, amount, tokenGenesisId) {
15480
15139
  await this.addToHistory({
15481
15140
  type: "SENT",
15482
- amount: payload.amount,
15141
+ amount,
15483
15142
  coinId: payload.coinId,
15484
15143
  symbol: this.getCoinSymbol(payload.coinId),
15485
15144
  timestamp: Date.now(),
15486
15145
  recipientPubkey: payload.recipient,
15487
15146
  memo: payload.memo,
15488
15147
  transferId,
15489
- tokenId: payload.direct[0] ? `v2_${payload.direct[0]}` : void 0
15148
+ tokenId: tokenGenesisId ? `v2_${tokenGenesisId}` : void 0
15490
15149
  });
15491
15150
  }
15492
15151
  // ===========================================================================
@@ -15508,11 +15167,7 @@ var PaymentsModule = class _PaymentsModule {
15508
15167
  */
15509
15168
  async save(opts = {}) {
15510
15169
  const providers = this.getTokenStorageProviders();
15511
- const tokenStats = Array.from(this.tokens.values()).map((t) => {
15512
- const txf = tokenToTxf(t);
15513
- return `${t.id.slice(0, 12)}(${t.status},txf=${!!txf})`;
15514
- });
15515
- logger.debug("Payments", `save(): providers=${providers.size}, tokens=[${tokenStats.join(", ")}]`);
15170
+ logger.debug("Payments", `save(): providers=${providers.size}, tokens=${this.tokens.size}`);
15516
15171
  if (providers.size === 0) {
15517
15172
  logger.debug("Payments", "save(): No token storage providers - TXF not persisted");
15518
15173
  return;
@@ -24182,7 +23837,6 @@ var import_canonicalize = __toESM(require("canonicalize"), 1);
24182
23837
 
24183
23838
  // core/address.ts
24184
23839
  var DIRECT_PREFIX = "DIRECT://";
24185
- var PROXY_PREFIX = "PROXY://";
24186
23840
  var NAMETAG_PREFIX = "@";
24187
23841
  function parseAddress(address) {
24188
23842
  if (!address || typeof address !== "string") return null;
@@ -24194,9 +23848,6 @@ function parseAddress(address) {
24194
23848
  }
24195
23849
  return null;
24196
23850
  }
24197
- if (trimmed.startsWith(PROXY_PREFIX) && trimmed.length > PROXY_PREFIX.length) {
24198
- return { type: "PROXY", raw: trimmed, value: trimmed.slice(PROXY_PREFIX.length) };
24199
- }
24200
23851
  if (trimmed.startsWith(NAMETAG_PREFIX)) {
24201
23852
  const value = trimmed.slice(NAMETAG_PREFIX.length);
24202
23853
  if (value.length > 0) {
@@ -24219,8 +23870,6 @@ function normalizeAddress(address) {
24219
23870
  switch (parsed.type) {
24220
23871
  case "DIRECT":
24221
23872
  return `DIRECT://${parsed.value.toLowerCase()}`;
24222
- case "PROXY":
24223
- return `PROXY://${parsed.value.toLowerCase()}`;
24224
23873
  case "NAMETAG":
24225
23874
  return `@${parsed.value.toLowerCase()}`;
24226
23875
  }
@@ -27441,444 +27090,94 @@ function parseWalletText(content) {
27441
27090
  return {
27442
27091
  success: false,
27443
27092
  error: "Could not find encrypted master key in backup file"
27444
- };
27445
- }
27446
- return {
27447
- success: false,
27448
- needsPassword: true,
27449
- error: "Password required for encrypted wallet"
27450
- };
27451
- }
27452
- const masterKey = extractFromText(
27453
- content,
27454
- /MASTER PRIVATE KEY \(keep secret!\):\s*([^\n]+)/
27455
- );
27456
- if (!masterKey) {
27457
- return {
27458
- success: false,
27459
- error: "Could not find master private key in backup file"
27460
- };
27461
- }
27462
- const chainCode = extractFromText(
27463
- content,
27464
- /MASTER CHAIN CODE \(for (?:BIP32 HD|Alpha) wallet compatibility\):\s*([^\n]+)/
27465
- );
27466
- const descriptorPath = extractFromText(content, /DESCRIPTOR PATH:\s*([^\n]+)/);
27467
- const isBIP32 = content.includes("WALLET TYPE: BIP32 hierarchical deterministic wallet") || content.includes("WALLET TYPE: Alpha descriptor wallet") || !!chainCode;
27468
- const effectiveDescriptorPath = descriptorPath ?? (isBIP32 ? "84'/1'/0'" : void 0);
27469
- const data = {
27470
- masterKey,
27471
- chainCode: chainCode ?? void 0,
27472
- descriptorPath: effectiveDescriptorPath,
27473
- derivationMode: isBIP32 ? "bip32" : "wif_hmac"
27474
- };
27475
- return {
27476
- success: true,
27477
- data
27478
- };
27479
- } catch (e) {
27480
- return {
27481
- success: false,
27482
- error: `Failed to parse wallet text: ${e instanceof Error ? e.message : String(e)}`
27483
- };
27484
- }
27485
- }
27486
- function parseAndDecryptWalletText(content, password) {
27487
- try {
27488
- const isEncrypted = isTextWalletEncrypted(content);
27489
- if (!isEncrypted) {
27490
- return parseWalletText(content);
27491
- }
27492
- const encryptedKey = extractFromText(
27493
- content,
27494
- /ENCRYPTED MASTER KEY \(password protected\):\s*([^\n]+)/
27495
- );
27496
- if (!encryptedKey) {
27497
- return {
27498
- success: false,
27499
- error: "Could not find encrypted master key in backup file"
27500
- };
27501
- }
27502
- const masterKey = decryptTextFormatKey(encryptedKey, password);
27503
- if (!masterKey) {
27504
- return {
27505
- success: false,
27506
- error: "Failed to decrypt - incorrect password?"
27507
- };
27508
- }
27509
- const chainCode = extractFromText(
27510
- content,
27511
- /MASTER CHAIN CODE \(for (?:BIP32 HD|Alpha) wallet compatibility\):\s*([^\n]+)/
27512
- );
27513
- const descriptorPath = extractFromText(content, /DESCRIPTOR PATH:\s*([^\n]+)/);
27514
- const isBIP32 = content.includes("WALLET TYPE: BIP32 hierarchical deterministic wallet") || content.includes("WALLET TYPE: Alpha descriptor wallet") || !!chainCode;
27515
- const effectiveDescriptorPath = descriptorPath ?? (isBIP32 ? "84'/1'/0'" : void 0);
27516
- const data = {
27517
- masterKey,
27518
- chainCode: chainCode ?? void 0,
27519
- descriptorPath: effectiveDescriptorPath,
27520
- derivationMode: isBIP32 ? "bip32" : "wif_hmac"
27521
- };
27522
- return {
27523
- success: true,
27524
- data
27525
- };
27526
- } catch (e) {
27527
- return {
27528
- success: false,
27529
- error: `Failed to parse/decrypt wallet text: ${e instanceof Error ? e.message : String(e)}`
27530
- };
27531
- }
27532
- }
27533
-
27534
- // serialization/wallet-dat.ts
27535
- var import_crypto_js4 = __toESM(require("crypto-js"), 1);
27536
- function uint8ArrayToWordArray(u8arr) {
27537
- const hex = bytesToHex4(u8arr);
27538
- return import_crypto_js4.default.enc.Hex.parse(hex);
27539
- }
27540
- function isSQLiteDatabase(data) {
27541
- if (data.length < 16) return false;
27542
- const header = new TextDecoder().decode(data.slice(0, 16));
27543
- return header.startsWith("SQLite format 3");
27544
- }
27545
- function isWalletDatEncrypted(data) {
27546
- const mkeyPattern = new TextEncoder().encode("mkey");
27547
- return findPattern(data, mkeyPattern, 0) !== -1;
27548
- }
27549
- function findAllCMasterKeys(data) {
27550
- const results = [];
27551
- for (let pos = 0; pos < data.length - 70; pos++) {
27552
- if (data[pos] === 48) {
27553
- const saltLenPos = pos + 1 + 48;
27554
- if (saltLenPos < data.length && data[saltLenPos] === 8) {
27555
- const iterPos = saltLenPos + 1 + 8 + 4;
27556
- if (iterPos + 4 <= data.length) {
27557
- const iterations = data[iterPos] | data[iterPos + 1] << 8 | data[iterPos + 2] << 16 | data[iterPos + 3] << 24;
27558
- if (iterations >= 1e3 && iterations <= 1e7) {
27559
- const encryptedKey = data.slice(pos + 1, pos + 1 + 48);
27560
- const salt = data.slice(saltLenPos + 1, saltLenPos + 1 + 8);
27561
- const derivationMethod = data[saltLenPos + 1 + 8] | data[saltLenPos + 1 + 8 + 1] << 8 | data[saltLenPos + 1 + 8 + 2] << 16 | data[saltLenPos + 1 + 8 + 3] << 24;
27562
- results.push({ encryptedKey, salt, derivationMethod, iterations, position: pos });
27563
- }
27564
- }
27565
- }
27566
- }
27567
- }
27568
- return results;
27569
- }
27570
- function findWpkhDescriptor(data) {
27571
- const descriptorPattern = new TextEncoder().encode("walletdescriptor");
27572
- let descriptorIndex = 0;
27573
- while ((descriptorIndex = findPattern(data, descriptorPattern, descriptorIndex)) !== -1) {
27574
- let scanPos = descriptorIndex + descriptorPattern.length + 32;
27575
- const descLen = data[scanPos];
27576
- scanPos++;
27577
- const descBytes = data.slice(scanPos, scanPos + Math.min(descLen, 200));
27578
- let descStr = "";
27579
- for (let i = 0; i < descBytes.length && descBytes[i] >= 32 && descBytes[i] <= 126; i++) {
27580
- descStr += String.fromCharCode(descBytes[i]);
27581
- }
27582
- if (descStr.startsWith("wpkh(xpub") && descStr.includes("/0/*)")) {
27583
- const xpubMatch = descStr.match(/xpub[1-9A-HJ-NP-Za-km-z]{100,}/);
27584
- if (xpubMatch) {
27585
- const descIdStart = descriptorIndex + descriptorPattern.length;
27586
- const descriptorId = data.slice(descIdStart, descIdStart + 32);
27587
- const pathMatch = descStr.match(/\[[\da-f]+\/(\d+'\/\d+'\/\d+')\]/);
27588
- const descriptorPath = pathMatch ? pathMatch[1] : "84'/1'/0'";
27589
- return {
27590
- descriptorId,
27591
- xpubString: xpubMatch[0],
27592
- descriptorPath
27593
- };
27594
- }
27595
- }
27596
- descriptorIndex++;
27597
- }
27598
- return { descriptorId: null, xpubString: null, descriptorPath: null };
27599
- }
27600
- function extractChainCodeFromXpub(xpubString) {
27601
- const decoded = base58Decode(xpubString);
27602
- return bytesToHex4(decoded.slice(13, 45));
27603
- }
27604
- function findMasterChainCode(data) {
27605
- const xpubPattern = new TextEncoder().encode("xpub");
27606
- const base58Chars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
27607
- let searchPos = 0;
27608
- while (searchPos < data.length) {
27609
- const xpubIndex = findPattern(data, xpubPattern, searchPos);
27610
- if (xpubIndex === -1) break;
27611
- let xpubStr = "xpub";
27612
- let pos = xpubIndex + 4;
27613
- while (pos < data.length && xpubStr.length < 120) {
27614
- const char = String.fromCharCode(data[pos]);
27615
- if (base58Chars.includes(char)) {
27616
- xpubStr += char;
27617
- pos++;
27618
- } else {
27619
- break;
27620
- }
27621
- }
27622
- if (xpubStr.length > 100) {
27623
- try {
27624
- const decoded = base58Decode(xpubStr);
27625
- const depth = decoded[4];
27626
- if (depth === 0) {
27627
- return bytesToHex4(decoded.slice(13, 45));
27628
- }
27629
- } catch {
27630
- }
27631
- }
27632
- searchPos = xpubIndex + 4;
27633
- }
27634
- return null;
27635
- }
27636
- function extractDescriptorKeys(data) {
27637
- const keys = [];
27638
- const descriptorKeyPattern = new TextEncoder().encode("walletdescriptorkey");
27639
- let index = 0;
27640
- while ((index = findPattern(data, descriptorKeyPattern, index)) !== -1) {
27641
- for (let checkPos = index + descriptorKeyPattern.length; checkPos < Math.min(index + descriptorKeyPattern.length + 200, data.length - 40); checkPos++) {
27642
- if (data[checkPos] === 211 && data[checkPos + 1] === 2 && data[checkPos + 2] === 1 && data[checkPos + 3] === 1 && data[checkPos + 4] === 4 && data[checkPos + 5] === 32) {
27643
- const privKey = data.slice(checkPos + 6, checkPos + 38);
27644
- const privKeyHex = bytesToHex4(privKey);
27645
- if (isValidPrivateKey(privKeyHex)) {
27646
- keys.push(privKeyHex);
27647
- break;
27648
- }
27649
- }
27650
- }
27651
- index++;
27652
- }
27653
- return keys;
27654
- }
27655
- function extractLegacyKeys(data) {
27656
- const keys = [];
27657
- const keyPattern = new TextEncoder().encode("key");
27658
- let index = 0;
27659
- while ((index = findPattern(data, keyPattern, index)) !== -1) {
27660
- const searchPattern = new Uint8Array([4, 32]);
27661
- for (let i = index; i < Math.min(index + 200, data.length - 34); i++) {
27662
- if (data[i] === searchPattern[0] && data[i + 1] === searchPattern[1]) {
27663
- const privKey = data.slice(i + 2, i + 34);
27664
- const privKeyHex = bytesToHex4(privKey);
27665
- if (isValidPrivateKey(privKeyHex)) {
27666
- keys.push(privKeyHex);
27667
- break;
27668
- }
27669
- }
27670
- }
27671
- index++;
27672
- }
27673
- return keys;
27674
- }
27675
- function findEncryptedKeyForDescriptor(data, descriptorId) {
27676
- const ckeyPattern = new TextEncoder().encode("walletdescriptorckey");
27677
- let ckeyIndex = findPattern(data, ckeyPattern, 0);
27678
- while (ckeyIndex !== -1) {
27679
- const recordDescId = data.slice(ckeyIndex + ckeyPattern.length, ckeyIndex + ckeyPattern.length + 32);
27680
- if (Array.from(recordDescId).every((b, i) => b === descriptorId[i])) {
27681
- let keyPos = ckeyIndex + ckeyPattern.length + 32;
27682
- const pubkeyLen = data[keyPos];
27683
- keyPos++;
27684
- const pubkey = data.slice(keyPos, keyPos + pubkeyLen);
27685
- for (let searchPos = keyPos + pubkeyLen; searchPos < Math.min(keyPos + pubkeyLen + 100, data.length - 50); searchPos++) {
27686
- const valueLen = data[searchPos];
27687
- if (valueLen >= 32 && valueLen <= 64) {
27688
- const encryptedKey = data.slice(searchPos + 1, searchPos + 1 + valueLen);
27689
- return { pubkey, encryptedKey };
27690
- }
27691
- }
27692
- }
27693
- ckeyIndex = findPattern(data, ckeyPattern, ckeyIndex + 1);
27694
- }
27695
- return null;
27696
- }
27697
- async function decryptCMasterKey(cmk, password, onProgress) {
27698
- const { encryptedKey, salt, iterations } = cmk;
27699
- const passwordHex = bytesToHex4(new TextEncoder().encode(password));
27700
- const saltHex = bytesToHex4(salt);
27701
- const inputHex = passwordHex + saltHex;
27702
- let hash = import_crypto_js4.default.SHA512(import_crypto_js4.default.enc.Hex.parse(inputHex));
27703
- const BATCH_SIZE = 1e3;
27704
- for (let i = 0; i < iterations - 1; i++) {
27705
- hash = import_crypto_js4.default.SHA512(hash);
27706
- if (onProgress && i % BATCH_SIZE === 0) {
27707
- await onProgress(i, iterations);
27708
- }
27709
- }
27710
- const derivedKey = import_crypto_js4.default.lib.WordArray.create(hash.words.slice(0, 8));
27711
- const derivedIv = import_crypto_js4.default.lib.WordArray.create(hash.words.slice(8, 12));
27712
- const encryptedWords = uint8ArrayToWordArray(encryptedKey);
27713
- const decrypted = import_crypto_js4.default.AES.decrypt(
27714
- { ciphertext: encryptedWords },
27715
- derivedKey,
27716
- { iv: derivedIv, padding: import_crypto_js4.default.pad.Pkcs7, mode: import_crypto_js4.default.mode.CBC }
27717
- );
27718
- const result = import_crypto_js4.default.enc.Hex.stringify(decrypted);
27719
- if (!result || result.length !== 64) {
27720
- throw new SphereError("Master key decryption failed - incorrect password", "DECRYPTION_ERROR");
27721
- }
27722
- return result;
27723
- }
27724
- function decryptPrivateKey(encryptedKey, pubkey, masterKeyHex) {
27725
- const pubkeyWords = uint8ArrayToWordArray(pubkey);
27726
- const pubkeyHashWords = import_crypto_js4.default.SHA256(import_crypto_js4.default.SHA256(pubkeyWords));
27727
- const ivWords = import_crypto_js4.default.lib.WordArray.create(pubkeyHashWords.words.slice(0, 4));
27728
- const masterKeyWords = import_crypto_js4.default.enc.Hex.parse(masterKeyHex);
27729
- const encryptedWords = uint8ArrayToWordArray(encryptedKey);
27730
- const decrypted = import_crypto_js4.default.AES.decrypt(
27731
- { ciphertext: encryptedWords },
27732
- masterKeyWords,
27733
- { iv: ivWords, padding: import_crypto_js4.default.pad.Pkcs7, mode: import_crypto_js4.default.mode.CBC }
27734
- );
27735
- return import_crypto_js4.default.enc.Hex.stringify(decrypted);
27736
- }
27737
- function parseWalletDat(data) {
27738
- if (!isSQLiteDatabase(data)) {
27739
- return {
27740
- success: false,
27741
- error: "Invalid wallet.dat file - not an SQLite database"
27742
- };
27743
- }
27744
- const isEncrypted = isWalletDatEncrypted(data);
27745
- const cmasterKeys = isEncrypted ? findAllCMasterKeys(data) : [];
27746
- const descriptorKeys = isEncrypted ? [] : extractDescriptorKeys(data);
27747
- const legacyKeys = isEncrypted ? [] : extractLegacyKeys(data);
27748
- const { descriptorId: _descriptorId, xpubString, descriptorPath } = findWpkhDescriptor(data);
27749
- void _descriptorId;
27750
- let chainCode = null;
27751
- if (xpubString) {
27752
- try {
27753
- chainCode = extractChainCodeFromXpub(xpubString);
27754
- } catch {
27755
- chainCode = findMasterChainCode(data);
27756
- }
27757
- } else {
27758
- chainCode = findMasterChainCode(data);
27759
- }
27760
- if (!isEncrypted) {
27761
- let masterKey = null;
27762
- if (descriptorKeys.length > 0) {
27763
- masterKey = descriptorKeys[0];
27764
- } else if (legacyKeys.length > 0) {
27765
- masterKey = legacyKeys[0];
27766
- }
27767
- if (masterKey) {
27768
- const parsedData = {
27769
- masterKey,
27770
- chainCode: chainCode ?? void 0,
27771
- descriptorPath: descriptorPath ?? "84'/1'/0'",
27772
- derivationMode: chainCode ? "bip32" : "wif_hmac"
27093
+ };
27094
+ }
27095
+ return {
27096
+ success: false,
27097
+ needsPassword: true,
27098
+ error: "Password required for encrypted wallet"
27773
27099
  };
27100
+ }
27101
+ const masterKey = extractFromText(
27102
+ content,
27103
+ /MASTER PRIVATE KEY \(keep secret!\):\s*([^\n]+)/
27104
+ );
27105
+ if (!masterKey) {
27774
27106
  return {
27775
- success: true,
27776
- data: parsedData
27107
+ success: false,
27108
+ error: "Could not find master private key in backup file"
27777
27109
  };
27778
27110
  }
27779
- return {
27780
- success: false,
27781
- error: "No valid private keys found in wallet.dat file"
27782
- };
27783
- }
27784
- if (cmasterKeys.length === 0) {
27785
- return {
27786
- success: false,
27787
- error: "Encrypted wallet but no CMasterKey structures found"
27111
+ const chainCode = extractFromText(
27112
+ content,
27113
+ /MASTER CHAIN CODE \(for (?:BIP32 HD|Alpha) wallet compatibility\):\s*([^\n]+)/
27114
+ );
27115
+ const descriptorPath = extractFromText(content, /DESCRIPTOR PATH:\s*([^\n]+)/);
27116
+ const isBIP32 = content.includes("WALLET TYPE: BIP32 hierarchical deterministic wallet") || content.includes("WALLET TYPE: Alpha descriptor wallet") || !!chainCode;
27117
+ const effectiveDescriptorPath = descriptorPath ?? (isBIP32 ? "84'/1'/0'" : void 0);
27118
+ const data = {
27119
+ masterKey,
27120
+ chainCode: chainCode ?? void 0,
27121
+ descriptorPath: effectiveDescriptorPath,
27122
+ derivationMode: isBIP32 ? "bip32" : "wif_hmac"
27788
27123
  };
27789
- }
27790
- const firstCmk = cmasterKeys[0];
27791
- return {
27792
- success: false,
27793
- needsPassword: true,
27794
- encryptionInfo: {
27795
- iterations: firstCmk.iterations,
27796
- salt: firstCmk.salt,
27797
- encryptedKey: firstCmk.encryptedKey
27798
- },
27799
- error: "Password required for encrypted wallet"
27800
- };
27801
- }
27802
- async function parseAndDecryptWalletDat(data, password, onProgress) {
27803
- if (!isSQLiteDatabase(data)) {
27804
27124
  return {
27805
- success: false,
27806
- error: "Invalid wallet.dat file - not an SQLite database"
27125
+ success: true,
27126
+ data
27807
27127
  };
27808
- }
27809
- const isEncrypted = isWalletDatEncrypted(data);
27810
- if (!isEncrypted) {
27811
- return parseWalletDat(data);
27812
- }
27813
- const cmasterKeys = findAllCMasterKeys(data);
27814
- if (cmasterKeys.length === 0) {
27128
+ } catch (e) {
27815
27129
  return {
27816
27130
  success: false,
27817
- error: "Encrypted wallet but no CMasterKey structures found"
27131
+ error: `Failed to parse wallet text: ${e instanceof Error ? e.message : String(e)}`
27818
27132
  };
27819
27133
  }
27820
- let masterKeyHex = null;
27821
- for (const cmk of cmasterKeys) {
27822
- try {
27823
- masterKeyHex = await decryptCMasterKey(cmk, password, onProgress);
27824
- if (masterKeyHex && masterKeyHex.length === 64) {
27825
- break;
27826
- }
27827
- } catch {
27828
- continue;
27134
+ }
27135
+ function parseAndDecryptWalletText(content, password) {
27136
+ try {
27137
+ const isEncrypted = isTextWalletEncrypted(content);
27138
+ if (!isEncrypted) {
27139
+ return parseWalletText(content);
27829
27140
  }
27830
- }
27831
- if (!masterKeyHex || masterKeyHex.length !== 64) {
27832
- return {
27833
- success: false,
27834
- error: "Master key decryption failed - incorrect password"
27835
- };
27836
- }
27837
- const { descriptorId, xpubString, descriptorPath } = findWpkhDescriptor(data);
27838
- let chainCode = null;
27839
- if (xpubString) {
27840
- try {
27841
- chainCode = extractChainCodeFromXpub(xpubString);
27842
- } catch {
27843
- chainCode = findMasterChainCode(data);
27141
+ const encryptedKey = extractFromText(
27142
+ content,
27143
+ /ENCRYPTED MASTER KEY \(password protected\):\s*([^\n]+)/
27144
+ );
27145
+ if (!encryptedKey) {
27146
+ return {
27147
+ success: false,
27148
+ error: "Could not find encrypted master key in backup file"
27149
+ };
27844
27150
  }
27845
- } else {
27846
- chainCode = findMasterChainCode(data);
27847
- }
27848
- if (!descriptorId) {
27849
- return {
27850
- success: false,
27851
- error: "Could not find native SegWit receive descriptor"
27151
+ const masterKey = decryptTextFormatKey(encryptedKey, password);
27152
+ if (!masterKey) {
27153
+ return {
27154
+ success: false,
27155
+ error: "Failed to decrypt - incorrect password?"
27156
+ };
27157
+ }
27158
+ const chainCode = extractFromText(
27159
+ content,
27160
+ /MASTER CHAIN CODE \(for (?:BIP32 HD|Alpha) wallet compatibility\):\s*([^\n]+)/
27161
+ );
27162
+ const descriptorPath = extractFromText(content, /DESCRIPTOR PATH:\s*([^\n]+)/);
27163
+ const isBIP32 = content.includes("WALLET TYPE: BIP32 hierarchical deterministic wallet") || content.includes("WALLET TYPE: Alpha descriptor wallet") || !!chainCode;
27164
+ const effectiveDescriptorPath = descriptorPath ?? (isBIP32 ? "84'/1'/0'" : void 0);
27165
+ const data = {
27166
+ masterKey,
27167
+ chainCode: chainCode ?? void 0,
27168
+ descriptorPath: effectiveDescriptorPath,
27169
+ derivationMode: isBIP32 ? "bip32" : "wif_hmac"
27852
27170
  };
27853
- }
27854
- const encryptedKeyData = findEncryptedKeyForDescriptor(data, descriptorId);
27855
- if (!encryptedKeyData) {
27856
27171
  return {
27857
- success: false,
27858
- error: "Could not find encrypted private key for descriptor"
27172
+ success: true,
27173
+ data
27859
27174
  };
27860
- }
27861
- const bip32MasterKey = decryptPrivateKey(
27862
- encryptedKeyData.encryptedKey,
27863
- encryptedKeyData.pubkey,
27864
- masterKeyHex
27865
- );
27866
- if (!bip32MasterKey || bip32MasterKey.length !== 64) {
27175
+ } catch (e) {
27867
27176
  return {
27868
27177
  success: false,
27869
- error: "Could not decrypt BIP32 master private key"
27178
+ error: `Failed to parse/decrypt wallet text: ${e instanceof Error ? e.message : String(e)}`
27870
27179
  };
27871
27180
  }
27872
- const parsedData = {
27873
- masterKey: bip32MasterKey,
27874
- chainCode: chainCode ?? void 0,
27875
- descriptorPath: descriptorPath ?? "84'/1'/0'",
27876
- derivationMode: "bip32"
27877
- };
27878
- return {
27879
- success: true,
27880
- data: parsedData
27881
- };
27882
27181
  }
27883
27182
 
27884
27183
  // core/Sphere.ts
@@ -28711,32 +28010,196 @@ var Sphere = class _Sphere {
28711
28010
  if (this._transport.isConnected() && this._transport.fetchPendingEvents) {
28712
28011
  await this._transport.fetchPendingEvents();
28713
28012
  }
28714
- }
28715
- getAggregator() {
28716
- return this._oracle;
28013
+ }
28014
+ getAggregator() {
28015
+ return this._oracle;
28016
+ }
28017
+ /**
28018
+ * Apply a new gateway API key to the LIVE oracle + token engine WITHOUT a full
28019
+ * Sphere rebuild (transport / realtime socket / discovery stay up). Use this to
28020
+ * inject a per-wallet subscription key provisioned AFTER init, or a per-address
28021
+ * key on an address switch, so the next money operation authenticates with it.
28022
+ *
28023
+ * Money-safety: the running token engine is snapshotted per-operation (the
28024
+ * payments module reads `deps.tokenEngine` into a local at op start), so an
28025
+ * in-flight send/mint completes on the OLD key (submit + proof stay paired on
28026
+ * one client) and only operations started AFTER this call use the new key.
28027
+ * Only the ACTIVE address's engine is rebuilt — other tracked addresses re-key
28028
+ * on their next switch/build. No-op-safe if the engine can't be built (the
28029
+ * modules fall back to their legacy path, same as init).
28030
+ */
28031
+ async setOracleApiKey(apiKey) {
28032
+ this._oracle.setApiKey?.(apiKey);
28033
+ const active = this._addressModules.get(this._currentAddressIndex);
28034
+ const replaced = active?.tokenEngine ?? this._tokenEngine;
28035
+ this._tokenEngine = await this.buildTokenEngine();
28036
+ if (active) active.tokenEngine = this._tokenEngine;
28037
+ this._payments.setTokenEngine(this._tokenEngine);
28038
+ if (replaced && replaced !== this._tokenEngine) replaced.dispose?.();
28039
+ }
28040
+ /**
28041
+ * Import a wallet from a backup file: a `UNICITY WALLET DETAILS` text backup
28042
+ * (the format `exportToTxt` writes, optionally password-encrypted), a legacy
28043
+ * flat-JSON webwallet export, or a bare mnemonic in a text file.
28044
+ *
28045
+ * @example
28046
+ * const result = await Sphere.importFromLegacyFile({
28047
+ * fileContent: await file.text(),
28048
+ * fileName: file.name,
28049
+ * password, // when the backup is encrypted
28050
+ * ...providers,
28051
+ * });
28052
+ */
28053
+ static async importFromLegacyFile(options) {
28054
+ const { fileContent, fileName, password, onDecryptProgress, ...baseOptions } = options;
28055
+ const fileType = _Sphere.detectLegacyFileType(fileName, fileContent);
28056
+ if (fileType === "unknown") {
28057
+ return { success: false, error: "Unknown file format" };
28058
+ }
28059
+ if (fileType === "mnemonic") {
28060
+ const mnemonic = fileContent.trim().toLowerCase().split(/\s+/).join(" ");
28061
+ if (!_Sphere.validateMnemonic(mnemonic)) {
28062
+ return { success: false, error: "Invalid mnemonic phrase" };
28063
+ }
28064
+ const sphere = await _Sphere.import({ ...baseOptions, mnemonic });
28065
+ return { success: true, sphere, mnemonic };
28066
+ }
28067
+ if (fileType === "txt") {
28068
+ const content = fileContent;
28069
+ let parseResult;
28070
+ if (password) {
28071
+ parseResult = parseAndDecryptWalletText(content, password);
28072
+ } else if (isTextWalletEncrypted(content)) {
28073
+ return { success: false, needsPassword: true, error: "Password required for encrypted wallet" };
28074
+ } else {
28075
+ parseResult = parseWalletText(content);
28076
+ }
28077
+ if (parseResult.needsPassword && !password) {
28078
+ return { success: false, needsPassword: true, error: "Password required for encrypted wallet" };
28079
+ }
28080
+ if (!parseResult.success || !parseResult.data) {
28081
+ return { success: false, error: parseResult.error };
28082
+ }
28083
+ const { masterKey, chainCode, descriptorPath, derivationMode } = parseResult.data;
28084
+ const basePath = descriptorPath ? `m/${descriptorPath}` : DEFAULT_BASE_PATH;
28085
+ const sphere = await _Sphere.import({
28086
+ ...baseOptions,
28087
+ masterKey,
28088
+ chainCode,
28089
+ basePath,
28090
+ derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac")
28091
+ });
28092
+ return { success: true, sphere };
28093
+ }
28094
+ if (fileType === "json") {
28095
+ const content = fileContent;
28096
+ let parsed;
28097
+ try {
28098
+ parsed = JSON.parse(content);
28099
+ } catch {
28100
+ return { success: false, error: "Invalid JSON file" };
28101
+ }
28102
+ if (parsed.type === "sphere-wallet") {
28103
+ const result = await _Sphere.importFromJSON({
28104
+ ...baseOptions,
28105
+ jsonContent: content,
28106
+ password
28107
+ });
28108
+ if (result.success) {
28109
+ const sphere2 = _Sphere.getInstance();
28110
+ return { success: true, sphere: sphere2, mnemonic: result.mnemonic };
28111
+ }
28112
+ if (!password && result.error?.includes("Password required")) {
28113
+ return { success: false, needsPassword: true, error: result.error };
28114
+ }
28115
+ return { success: false, error: result.error };
28116
+ }
28117
+ let masterKey;
28118
+ let mnemonic;
28119
+ if (parsed.encrypted && typeof parsed.encrypted === "object") {
28120
+ if (!password) {
28121
+ return { success: false, needsPassword: true, error: "Password required for encrypted wallet" };
28122
+ }
28123
+ const enc = parsed.encrypted;
28124
+ if (!enc.salt || !enc.masterPrivateKey) {
28125
+ return { success: false, error: "Invalid encrypted wallet format" };
28126
+ }
28127
+ const decryptedKey = decryptWithSalt(enc.masterPrivateKey, password, enc.salt);
28128
+ if (!decryptedKey) {
28129
+ return { success: false, error: "Failed to decrypt - incorrect password?" };
28130
+ }
28131
+ masterKey = decryptedKey;
28132
+ if (enc.mnemonic) {
28133
+ mnemonic = decryptWithSalt(enc.mnemonic, password, enc.salt) ?? void 0;
28134
+ }
28135
+ } else {
28136
+ masterKey = parsed.masterPrivateKey;
28137
+ mnemonic = parsed.mnemonic;
28138
+ }
28139
+ if (!masterKey) {
28140
+ return { success: false, error: "No master key found in wallet JSON" };
28141
+ }
28142
+ const chainCode = parsed.chainCode;
28143
+ const descriptorPath = parsed.descriptorPath;
28144
+ const derivationMode = parsed.derivationMode;
28145
+ const isBIP32 = derivationMode === "bip32" || !!chainCode;
28146
+ const basePath = descriptorPath ? `m/${descriptorPath}` : isBIP32 ? "m/84'/1'/0'" : DEFAULT_BASE_PATH;
28147
+ if (mnemonic) {
28148
+ const sphere2 = await _Sphere.import({ ...baseOptions, mnemonic, basePath });
28149
+ return { success: true, sphere: sphere2, mnemonic };
28150
+ }
28151
+ const sphere = await _Sphere.import({
28152
+ ...baseOptions,
28153
+ masterKey,
28154
+ chainCode,
28155
+ basePath,
28156
+ derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac")
28157
+ });
28158
+ return { success: true, sphere };
28159
+ }
28160
+ return { success: false, error: "Unsupported file type" };
28161
+ }
28162
+ /**
28163
+ * Detect legacy file type from filename and content
28164
+ */
28165
+ static detectLegacyFileType(fileName, content) {
28166
+ if (fileName.endsWith(".json")) {
28167
+ return "json";
28168
+ }
28169
+ try {
28170
+ const trimmed = content.trim();
28171
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
28172
+ JSON.parse(trimmed);
28173
+ return "json";
28174
+ }
28175
+ } catch {
28176
+ }
28177
+ const words = content.trim().split(/\s+/);
28178
+ if ((words.length === 12 || words.length === 24) && words.every((w) => /^[a-z]+$/.test(w.toLowerCase()))) {
28179
+ return "mnemonic";
28180
+ }
28181
+ if (isWalletTextFormat(content)) {
28182
+ return "txt";
28183
+ }
28184
+ return "unknown";
28717
28185
  }
28718
28186
  /**
28719
- * Apply a new gateway API key to the LIVE oracle + token engine WITHOUT a full
28720
- * Sphere rebuild (transport / realtime socket / discovery stay up). Use this to
28721
- * inject a per-wallet subscription key provisioned AFTER init, or a per-address
28722
- * key on an address switch, so the next money operation authenticates with it.
28723
- *
28724
- * Money-safety: the running token engine is snapshotted per-operation (the
28725
- * payments module reads `deps.tokenEngine` into a local at op start), so an
28726
- * in-flight send/mint completes on the OLD key (submit + proof stay paired on
28727
- * one client) and only operations started AFTER this call use the new key.
28728
- * Only the ACTIVE address's engine is rebuilt — other tracked addresses re-key
28729
- * on their next switch/build. No-op-safe if the engine can't be built (the
28730
- * modules fall back to their legacy path, same as init).
28187
+ * Check if a legacy file is encrypted
28731
28188
  */
28732
- async setOracleApiKey(apiKey) {
28733
- this._oracle.setApiKey?.(apiKey);
28734
- const active = this._addressModules.get(this._currentAddressIndex);
28735
- const replaced = active?.tokenEngine ?? this._tokenEngine;
28736
- this._tokenEngine = await this.buildTokenEngine();
28737
- if (active) active.tokenEngine = this._tokenEngine;
28738
- this._payments.setTokenEngine(this._tokenEngine);
28739
- if (replaced && replaced !== this._tokenEngine) replaced.dispose?.();
28189
+ static isLegacyFileEncrypted(fileName, content) {
28190
+ const fileType = _Sphere.detectLegacyFileType(fileName, content);
28191
+ if (fileType === "txt") {
28192
+ return isTextWalletEncrypted(content);
28193
+ }
28194
+ if (fileType === "json") {
28195
+ try {
28196
+ const data = JSON.parse(content);
28197
+ return !!data.encrypted;
28198
+ } catch {
28199
+ return false;
28200
+ }
28201
+ }
28202
+ return false;
28740
28203
  }
28741
28204
  /**
28742
28205
  * Check if wallet has BIP32 master key for HD derivation
@@ -29008,224 +28471,6 @@ var Sphere = class _Sphere {
29008
28471
  };
29009
28472
  }
29010
28473
  }
29011
- /**
29012
- * Import wallet from legacy file (.dat, .txt, or mnemonic text)
29013
- *
29014
- * Supports:
29015
- * - Bitcoin Core wallet.dat files (SQLite format, encrypted or unencrypted)
29016
- * - Text backup files (UNICITY WALLET DETAILS format)
29017
- * - Plain mnemonic text (12 or 24 words)
29018
- *
29019
- * @returns Object with success status, created Sphere instance, and optionally recovered mnemonic
29020
- *
29021
- * @example
29022
- * ```ts
29023
- * // Import from .dat file
29024
- * const fileBuffer = await file.arrayBuffer();
29025
- * const result = await Sphere.importFromLegacyFile({
29026
- * fileContent: new Uint8Array(fileBuffer),
29027
- * fileName: 'wallet.dat',
29028
- * password: 'wallet-password', // if encrypted
29029
- * storage, transport, oracle,
29030
- * });
29031
- *
29032
- * // Import from .txt file
29033
- * const textContent = await file.text();
29034
- * const result = await Sphere.importFromLegacyFile({
29035
- * fileContent: textContent,
29036
- * fileName: 'backup.txt',
29037
- * storage, transport, oracle,
29038
- * });
29039
- * ```
29040
- */
29041
- static async importFromLegacyFile(options) {
29042
- const { fileContent, fileName, password, onDecryptProgress, ...baseOptions } = options;
29043
- const fileType = _Sphere.detectLegacyFileType(fileName, fileContent);
29044
- if (fileType === "unknown") {
29045
- return { success: false, error: "Unknown file format" };
29046
- }
29047
- if (fileType === "mnemonic") {
29048
- const mnemonic = fileContent.trim().toLowerCase().split(/\s+/).join(" ");
29049
- if (!_Sphere.validateMnemonic(mnemonic)) {
29050
- return { success: false, error: "Invalid mnemonic phrase" };
29051
- }
29052
- const sphere = await _Sphere.import({ ...baseOptions, mnemonic });
29053
- return { success: true, sphere, mnemonic };
29054
- }
29055
- if (fileType === "dat") {
29056
- const data = fileContent instanceof Uint8Array ? fileContent : new TextEncoder().encode(fileContent);
29057
- let parseResult;
29058
- if (password) {
29059
- parseResult = await parseAndDecryptWalletDat(data, password, onDecryptProgress);
29060
- } else {
29061
- parseResult = parseWalletDat(data);
29062
- }
29063
- if (parseResult.needsPassword && !password) {
29064
- return { success: false, needsPassword: true, error: "Password required for encrypted wallet" };
29065
- }
29066
- if (!parseResult.success || !parseResult.data) {
29067
- return { success: false, error: parseResult.error };
29068
- }
29069
- const { masterKey, chainCode, descriptorPath, derivationMode } = parseResult.data;
29070
- const basePath = descriptorPath ? `m/${descriptorPath}` : DEFAULT_BASE_PATH;
29071
- const sphere = await _Sphere.import({
29072
- ...baseOptions,
29073
- masterKey,
29074
- chainCode,
29075
- basePath,
29076
- derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac")
29077
- });
29078
- return { success: true, sphere };
29079
- }
29080
- if (fileType === "txt") {
29081
- const content = typeof fileContent === "string" ? fileContent : new TextDecoder().decode(fileContent);
29082
- let parseResult;
29083
- if (password) {
29084
- parseResult = parseAndDecryptWalletText(content, password);
29085
- } else if (isTextWalletEncrypted(content)) {
29086
- return { success: false, needsPassword: true, error: "Password required for encrypted wallet" };
29087
- } else {
29088
- parseResult = parseWalletText(content);
29089
- }
29090
- if (parseResult.needsPassword && !password) {
29091
- return { success: false, needsPassword: true, error: "Password required for encrypted wallet" };
29092
- }
29093
- if (!parseResult.success || !parseResult.data) {
29094
- return { success: false, error: parseResult.error };
29095
- }
29096
- const { masterKey, chainCode, descriptorPath, derivationMode } = parseResult.data;
29097
- const basePath = descriptorPath ? `m/${descriptorPath}` : DEFAULT_BASE_PATH;
29098
- const sphere = await _Sphere.import({
29099
- ...baseOptions,
29100
- masterKey,
29101
- chainCode,
29102
- basePath,
29103
- derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac")
29104
- });
29105
- return { success: true, sphere };
29106
- }
29107
- if (fileType === "json") {
29108
- const content = typeof fileContent === "string" ? fileContent : new TextDecoder().decode(fileContent);
29109
- let parsed;
29110
- try {
29111
- parsed = JSON.parse(content);
29112
- } catch {
29113
- return { success: false, error: "Invalid JSON file" };
29114
- }
29115
- if (parsed.type === "sphere-wallet") {
29116
- const result = await _Sphere.importFromJSON({
29117
- ...baseOptions,
29118
- jsonContent: content,
29119
- password
29120
- });
29121
- if (result.success) {
29122
- const sphere2 = _Sphere.getInstance();
29123
- return { success: true, sphere: sphere2, mnemonic: result.mnemonic };
29124
- }
29125
- if (!password && result.error?.includes("Password required")) {
29126
- return { success: false, needsPassword: true, error: result.error };
29127
- }
29128
- return { success: false, error: result.error };
29129
- }
29130
- let masterKey;
29131
- let mnemonic;
29132
- if (parsed.encrypted && typeof parsed.encrypted === "object") {
29133
- if (!password) {
29134
- return { success: false, needsPassword: true, error: "Password required for encrypted wallet" };
29135
- }
29136
- const enc = parsed.encrypted;
29137
- if (!enc.salt || !enc.masterPrivateKey) {
29138
- return { success: false, error: "Invalid encrypted wallet format" };
29139
- }
29140
- const decryptedKey = decryptWithSalt(enc.masterPrivateKey, password, enc.salt);
29141
- if (!decryptedKey) {
29142
- return { success: false, error: "Failed to decrypt - incorrect password?" };
29143
- }
29144
- masterKey = decryptedKey;
29145
- if (enc.mnemonic) {
29146
- mnemonic = decryptWithSalt(enc.mnemonic, password, enc.salt) ?? void 0;
29147
- }
29148
- } else {
29149
- masterKey = parsed.masterPrivateKey;
29150
- mnemonic = parsed.mnemonic;
29151
- }
29152
- if (!masterKey) {
29153
- return { success: false, error: "No master key found in wallet JSON" };
29154
- }
29155
- const chainCode = parsed.chainCode;
29156
- const descriptorPath = parsed.descriptorPath;
29157
- const derivationMode = parsed.derivationMode;
29158
- const isBIP32 = derivationMode === "bip32" || !!chainCode;
29159
- const basePath = descriptorPath ? `m/${descriptorPath}` : isBIP32 ? "m/84'/1'/0'" : DEFAULT_BASE_PATH;
29160
- if (mnemonic) {
29161
- const sphere2 = await _Sphere.import({ ...baseOptions, mnemonic, basePath });
29162
- return { success: true, sphere: sphere2, mnemonic };
29163
- }
29164
- const sphere = await _Sphere.import({
29165
- ...baseOptions,
29166
- masterKey,
29167
- chainCode,
29168
- basePath,
29169
- derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac")
29170
- });
29171
- return { success: true, sphere };
29172
- }
29173
- return { success: false, error: "Unsupported file type" };
29174
- }
29175
- /**
29176
- * Detect legacy file type from filename and content
29177
- */
29178
- static detectLegacyFileType(fileName, content) {
29179
- if (fileName.endsWith(".dat")) {
29180
- return "dat";
29181
- }
29182
- const textContent = typeof content === "string" ? content : content.length < 1e3 ? new TextDecoder().decode(content) : "";
29183
- if (fileName.endsWith(".json")) {
29184
- return "json";
29185
- }
29186
- try {
29187
- const trimmed = textContent.trim();
29188
- if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
29189
- JSON.parse(trimmed);
29190
- return "json";
29191
- }
29192
- } catch {
29193
- }
29194
- const words = textContent.trim().split(/\s+/);
29195
- if ((words.length === 12 || words.length === 24) && words.every((w) => /^[a-z]+$/.test(w.toLowerCase()))) {
29196
- return "mnemonic";
29197
- }
29198
- if (isWalletTextFormat(textContent)) {
29199
- return "txt";
29200
- }
29201
- if (content instanceof Uint8Array && isSQLiteDatabase(content)) {
29202
- return "dat";
29203
- }
29204
- return "unknown";
29205
- }
29206
- /**
29207
- * Check if a legacy file is encrypted
29208
- */
29209
- static isLegacyFileEncrypted(fileName, content) {
29210
- const fileType = _Sphere.detectLegacyFileType(fileName, content);
29211
- if (fileType === "dat" && content instanceof Uint8Array) {
29212
- return isWalletDatEncrypted(content);
29213
- }
29214
- if (fileType === "txt") {
29215
- const textContent = typeof content === "string" ? content : new TextDecoder().decode(content);
29216
- return isTextWalletEncrypted(textContent);
29217
- }
29218
- if (fileType === "json") {
29219
- try {
29220
- const textContent = typeof content === "string" ? content : new TextDecoder().decode(content);
29221
- const data = JSON.parse(textContent);
29222
- return !!data.encrypted;
29223
- } catch {
29224
- return false;
29225
- }
29226
- }
29227
- return false;
29228
- }
29229
28474
  /**
29230
28475
  * Get the current active address index
29231
28476
  *
@@ -31130,116 +30375,6 @@ function formatAmount(amount, options = {}) {
31130
30375
  return symbol ? `${readable} ${symbol}` : readable;
31131
30376
  }
31132
30377
 
31133
- // core/bech32.ts
31134
- var CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
31135
- var GENERATOR = [996825010, 642813549, 513874426, 1027748829, 705979059];
31136
- function convertBits(data, fromBits, toBits, pad) {
31137
- let acc = 0;
31138
- let bits = 0;
31139
- const ret = [];
31140
- const maxv = (1 << toBits) - 1;
31141
- for (let i = 0; i < data.length; i++) {
31142
- const value = data[i];
31143
- if (value < 0 || value >> fromBits !== 0) return null;
31144
- acc = acc << fromBits | value;
31145
- bits += fromBits;
31146
- while (bits >= toBits) {
31147
- bits -= toBits;
31148
- ret.push(acc >> bits & maxv);
31149
- }
31150
- }
31151
- if (pad) {
31152
- if (bits > 0) {
31153
- ret.push(acc << toBits - bits & maxv);
31154
- }
31155
- } else if (bits >= fromBits || acc << toBits - bits & maxv) {
31156
- return null;
31157
- }
31158
- return ret;
31159
- }
31160
- function hrpExpand(hrp) {
31161
- const ret = [];
31162
- for (let i = 0; i < hrp.length; i++) ret.push(hrp.charCodeAt(i) >> 5);
31163
- ret.push(0);
31164
- for (let i = 0; i < hrp.length; i++) ret.push(hrp.charCodeAt(i) & 31);
31165
- return ret;
31166
- }
31167
- function bech32Polymod(values) {
31168
- let chk = 1;
31169
- for (let p = 0; p < values.length; p++) {
31170
- const top = chk >> 25;
31171
- chk = (chk & 33554431) << 5 ^ values[p];
31172
- for (let i = 0; i < 5; i++) {
31173
- if (top >> i & 1) chk ^= GENERATOR[i];
31174
- }
31175
- }
31176
- return chk;
31177
- }
31178
- function bech32Checksum(hrp, data) {
31179
- const values = hrpExpand(hrp).concat(data).concat([0, 0, 0, 0, 0, 0]);
31180
- const mod2 = bech32Polymod(values) ^ 1;
31181
- const ret = [];
31182
- for (let p = 0; p < 6; p++) {
31183
- ret.push(mod2 >> 5 * (5 - p) & 31);
31184
- }
31185
- return ret;
31186
- }
31187
- function encodeBech32(hrp, version, program) {
31188
- if (version < 0 || version > 16) {
31189
- throw new SphereError("Invalid witness version", "VALIDATION_ERROR");
31190
- }
31191
- const converted = convertBits(Array.from(program), 8, 5, true);
31192
- if (!converted) {
31193
- throw new SphereError("Failed to convert bits", "VALIDATION_ERROR");
31194
- }
31195
- const data = [version].concat(converted);
31196
- const checksum = bech32Checksum(hrp, data);
31197
- const combined = data.concat(checksum);
31198
- let out = hrp + "1";
31199
- for (let i = 0; i < combined.length; i++) {
31200
- out += CHARSET[combined[i]];
31201
- }
31202
- return out;
31203
- }
31204
- function decodeBech32(addr) {
31205
- addr = addr.toLowerCase();
31206
- const pos = addr.lastIndexOf("1");
31207
- if (pos < 1) return null;
31208
- const hrp = addr.substring(0, pos);
31209
- const dataStr = addr.substring(pos + 1);
31210
- const data = [];
31211
- for (let i = 0; i < dataStr.length; i++) {
31212
- const val = CHARSET.indexOf(dataStr[i]);
31213
- if (val === -1) return null;
31214
- data.push(val);
31215
- }
31216
- const checksum = bech32Checksum(hrp, data.slice(0, -6));
31217
- for (let i = 0; i < 6; i++) {
31218
- if (checksum[i] !== data[data.length - 6 + i]) {
31219
- return null;
31220
- }
31221
- }
31222
- const version = data[0];
31223
- const program = convertBits(data.slice(1, -6), 5, 8, false);
31224
- if (!program) return null;
31225
- return {
31226
- hrp,
31227
- witnessVersion: version,
31228
- data: Uint8Array.from(program)
31229
- };
31230
- }
31231
- function createAddress(hrp, pubkeyHash) {
31232
- const hashBytes = typeof pubkeyHash === "string" ? Uint8Array.from(Buffer.from(pubkeyHash, "hex")) : pubkeyHash;
31233
- return encodeBech32(hrp, 1, hashBytes);
31234
- }
31235
- function isValidBech32(addr) {
31236
- return decodeBech32(addr) !== null;
31237
- }
31238
- function getAddressHrp(addr) {
31239
- const result = decodeBech32(addr);
31240
- return result?.hrp ?? null;
31241
- }
31242
-
31243
30378
  // core/network-health.ts
31244
30379
  var DEFAULT_TIMEOUT_MS = 5e3;
31245
30380
  async function checkNetworkHealth(network = "testnet", options) {
@@ -31424,7 +30559,7 @@ async function runCustomCheck(name, checkFn, timeoutMs) {
31424
30559
 
31425
30560
  // storage/whole-blob-inventory-adapter.ts
31426
30561
  var TOKEN_KEY_RE = /^_[0-9a-f]{4,}$/i;
31427
- function bytesToHex6(bytes) {
30562
+ function bytesToHex5(bytes) {
31428
30563
  return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
31429
30564
  }
31430
30565
  function hexToBytes6(hex) {
@@ -31534,7 +30669,7 @@ var WholeBlobInventoryAdapter = class {
31534
30669
  if (isV2TokenEntry2(entry)) {
31535
30670
  try {
31536
30671
  const blob = decodeTokenBlob(hexToBytes6(entry.sdkData));
31537
- stateHash = bytesToHex6(sha256(blob.token));
30672
+ stateHash = bytesToHex5(sha256(blob.token));
31538
30673
  } catch {
31539
30674
  }
31540
30675
  }
@@ -31559,7 +30694,7 @@ function hexToBytes7(hex) {
31559
30694
  for (let i = 0; i < out.length; i++) out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
31560
30695
  return out;
31561
30696
  }
31562
- function bytesToHex7(bytes) {
30697
+ function bytesToHex6(bytes) {
31563
30698
  return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
31564
30699
  }
31565
30700
  function computeDeliveryId(tokenIdHex, stateHashHex) {
@@ -31568,7 +30703,7 @@ function computeDeliveryId(tokenIdHex, stateHashHex) {
31568
30703
  const joined = new Uint8Array(tokenId.length + stateHash.length);
31569
30704
  joined.set(tokenId, 0);
31570
30705
  joined.set(stateHash, tokenId.length);
31571
- return bytesToHex7(sha256(joined));
30706
+ return bytesToHex6(sha256(joined));
31572
30707
  }
31573
30708
  function composeDeliveryKeys(keys) {
31574
30709
  return { ...keys, deliveryId: computeDeliveryId(keys.tokenId, keys.stateHash) };
@@ -31971,7 +31106,6 @@ function createPriceProvider(config) {
31971
31106
  WalletApiError,
31972
31107
  WholeBlobInventoryAdapter,
31973
31108
  addressesMatch,
31974
- archivedKeyFromTokenId,
31975
31109
  areSameNametag,
31976
31110
  assertFieldEnvelopeShape,
31977
31111
  base58Decode,
@@ -31984,8 +31118,6 @@ function createPriceProvider(config) {
31984
31118
  composeDeliveryKeys,
31985
31119
  computeDeliveryId,
31986
31120
  computeSwapId,
31987
- countCommittedTransactions,
31988
- createAddress,
31989
31121
  createCommunicationsModule,
31990
31122
  createGroupChatModule,
31991
31123
  createKeyPair,
@@ -31996,15 +31128,12 @@ function createPriceProvider(config) {
31996
31128
  createSphere,
31997
31129
  createSwapModule,
31998
31130
  createTokenValidator,
31999
- decodeBech32,
32000
31131
  decrypt,
32001
- decryptCMasterKey,
32002
31132
  decryptField,
32003
31133
  decryptFieldBytes,
32004
31134
  decryptJson,
32005
31135
  decryptMnemonic,
32006
31136
  decryptNametag,
32007
- decryptPrivateKey,
32008
31137
  decryptSimple,
32009
31138
  decryptTextFormatKey,
32010
31139
  decryptWithSalt,
@@ -32013,7 +31142,6 @@ function createPriceProvider(config) {
32013
31142
  deriveFieldEncryptionKey,
32014
31143
  deriveKeyAtPath,
32015
31144
  doubleSha256,
32016
- encodeBech32,
32017
31145
  encrypt,
32018
31146
  encryptField,
32019
31147
  encryptFieldBytes,
@@ -32022,28 +31150,21 @@ function createPriceProvider(config) {
32022
31150
  encryptSimple,
32023
31151
  extractFromText,
32024
31152
  findPattern,
32025
- forkedKeyFromTokenIdAndState,
32026
31153
  formatAmount,
32027
31154
  generateAddressFromMasterKey,
32028
31155
  generateMasterKey,
32029
31156
  generateMnemonic,
32030
- getAddressHrp,
32031
31157
  getAddressId,
32032
31158
  getAddressStorageKey,
32033
31159
  getCoinIdByName,
32034
31160
  getCoinIdBySymbol,
32035
- getCurrentStateHash,
32036
31161
  getPublicKey,
32037
31162
  getSphere,
32038
31163
  getTokenDecimals,
32039
31164
  getTokenDefinition,
32040
31165
  getTokenIconUrl,
32041
- getTokenId,
32042
31166
  getTokenName,
32043
31167
  getTokenSymbol,
32044
- hasMissingNewStateHash,
32045
- hasUncommittedTransactions,
32046
- hasValidTxfData,
32047
31168
  hash160,
32048
31169
  hashAddressForTag,
32049
31170
  hashNametag,
@@ -32051,22 +31172,17 @@ function createPriceProvider(config) {
32051
31172
  hexToBytes,
32052
31173
  identityFromMnemonicSync,
32053
31174
  initSphere,
32054
- isArchivedKey,
32055
- isForkedKey,
32056
31175
  isKnownToken,
32057
31176
  isPhoneNumber,
32058
31177
  isPossiblyCommittedSendOutcome,
32059
- isSQLiteDatabase,
32060
31178
  isSphereError,
32061
31179
  isTextWalletEncrypted,
32062
31180
  isTokenKey,
32063
31181
  isValidAddress,
32064
- isValidBech32,
32065
31182
  isValidDirectAddress,
32066
31183
  isValidNametag,
32067
31184
  isValidPrivateKey,
32068
31185
  isValidTokenId,
32069
- isWalletDatEncrypted,
32070
31186
  isWalletTextFormat,
32071
31187
  keyFromTokenId,
32072
31188
  loadSphere,
@@ -32075,15 +31191,10 @@ function createPriceProvider(config) {
32075
31191
  normalizeAddress,
32076
31192
  normalizeCoinId,
32077
31193
  normalizeNametag,
32078
- normalizeSdkTokenToStorage,
32079
- objectToTxf,
32080
31194
  parseAddress,
32081
- parseAndDecryptWalletDat,
32082
31195
  parseAndDecryptWalletText,
32083
- parseForkedKey,
32084
31196
  parseTokenAmount,
32085
31197
  parseTxfStorageData,
32086
- parseWalletDat,
32087
31198
  parseWalletText,
32088
31199
  randomBytes,
32089
31200
  randomHex,
@@ -32097,10 +31208,7 @@ function createPriceProvider(config) {
32097
31208
  sleep,
32098
31209
  sphereExists,
32099
31210
  toHumanReadable,
32100
- tokenIdFromArchivedKey,
32101
31211
  tokenIdFromKey,
32102
- tokenToTxf,
32103
- txfToToken,
32104
31212
  validateManifest,
32105
31213
  validateMnemonic,
32106
31214
  verifyChallengeTemplate,