@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/README.md +7 -65
- package/dist/connect/index.cjs +1 -1
- package/dist/connect/index.cjs.map +1 -1
- package/dist/connect/index.js +1 -1
- package/dist/connect/index.js.map +1 -1
- package/dist/core/index.cjs +490 -1279
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +58 -134
- package/dist/core/index.d.ts +58 -134
- package/dist/core/index.js +490 -1272
- package/dist/core/index.js.map +1 -1
- package/dist/impl/browser/connect/index.cjs +1 -1
- package/dist/impl/browser/connect/index.cjs.map +1 -1
- package/dist/impl/browser/connect/index.js +1 -1
- package/dist/impl/browser/connect/index.js.map +1 -1
- package/dist/impl/nodejs/index.d.cts +0 -22
- package/dist/impl/nodejs/index.d.ts +0 -22
- package/dist/impl/shared/wallet-api/index.d.cts +0 -22
- package/dist/impl/shared/wallet-api/index.d.ts +0 -22
- package/dist/index.cjs +499 -1391
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +79 -357
- package/dist/index.d.ts +79 -357
- package/dist/index.js +499 -1364
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
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("_") && !
|
|
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
|
|
10561
|
-
return
|
|
10525
|
+
function isValidTokenId(tokenId) {
|
|
10526
|
+
return /^[0-9a-fA-F]{64}$/.test(tokenId);
|
|
10562
10527
|
}
|
|
10563
|
-
|
|
10564
|
-
|
|
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
|
|
10567
|
-
return
|
|
10533
|
+
function v2TokenId(token) {
|
|
10534
|
+
return token.id.startsWith("v2_") ? token.id.slice(3) : token.id;
|
|
10568
10535
|
}
|
|
10569
|
-
function
|
|
10570
|
-
|
|
10571
|
-
|
|
10572
|
-
|
|
10573
|
-
|
|
10574
|
-
|
|
10575
|
-
|
|
10576
|
-
|
|
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
|
|
10580
|
-
|
|
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
|
|
11074
|
+
return coinId;
|
|
11406
11075
|
}
|
|
11407
|
-
function
|
|
11408
|
-
if (
|
|
11409
|
-
|
|
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 =
|
|
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 !==
|
|
15156
|
-
throw new SphereError(
|
|
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
|
|
15300
|
-
const
|
|
15301
|
-
|
|
15302
|
-
|
|
15303
|
-
|
|
15304
|
-
|
|
15305
|
-
|
|
15306
|
-
|
|
15307
|
-
|
|
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
|
-
|
|
14986
|
+
blobs.push(existing.tokenBlob);
|
|
15328
14987
|
spent.push(genesisId);
|
|
15329
|
-
|
|
15330
|
-
|
|
15331
|
-
|
|
15332
|
-
|
|
15333
|
-
|
|
15334
|
-
|
|
15335
|
-
|
|
15336
|
-
|
|
15337
|
-
|
|
15338
|
-
|
|
15339
|
-
|
|
15340
|
-
|
|
15341
|
-
|
|
15342
|
-
|
|
15343
|
-
|
|
15344
|
-
|
|
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
|
-
|
|
15355
|
-
await
|
|
15356
|
-
|
|
15357
|
-
|
|
15358
|
-
|
|
15359
|
-
|
|
15360
|
-
|
|
15361
|
-
|
|
15362
|
-
|
|
15363
|
-
|
|
15364
|
-
|
|
15365
|
-
|
|
15366
|
-
|
|
15367
|
-
|
|
15368
|
-
|
|
15369
|
-
|
|
15370
|
-
|
|
15371
|
-
|
|
15372
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
15450
|
-
|
|
15451
|
-
|
|
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
|
|
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:
|
|
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
|
-
|
|
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:
|
|
27776
|
-
|
|
27107
|
+
success: false,
|
|
27108
|
+
error: "Could not find master private key in backup file"
|
|
27777
27109
|
};
|
|
27778
27110
|
}
|
|
27779
|
-
|
|
27780
|
-
|
|
27781
|
-
|
|
27782
|
-
|
|
27783
|
-
|
|
27784
|
-
|
|
27785
|
-
|
|
27786
|
-
|
|
27787
|
-
|
|
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:
|
|
27806
|
-
|
|
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:
|
|
27131
|
+
error: `Failed to parse wallet text: ${e instanceof Error ? e.message : String(e)}`
|
|
27818
27132
|
};
|
|
27819
27133
|
}
|
|
27820
|
-
|
|
27821
|
-
|
|
27822
|
-
|
|
27823
|
-
|
|
27824
|
-
|
|
27825
|
-
|
|
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
|
-
|
|
27832
|
-
|
|
27833
|
-
|
|
27834
|
-
|
|
27835
|
-
|
|
27836
|
-
|
|
27837
|
-
|
|
27838
|
-
|
|
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
|
-
|
|
27846
|
-
|
|
27847
|
-
|
|
27848
|
-
|
|
27849
|
-
|
|
27850
|
-
|
|
27851
|
-
|
|
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:
|
|
27858
|
-
|
|
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:
|
|
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
|
-
*
|
|
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
|
-
|
|
28733
|
-
|
|
28734
|
-
|
|
28735
|
-
|
|
28736
|
-
|
|
28737
|
-
if (
|
|
28738
|
-
|
|
28739
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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,
|