@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.js
CHANGED
|
@@ -10351,17 +10351,9 @@ function decryptDeliveryBundle(key, envelope) {
|
|
|
10351
10351
|
}
|
|
10352
10352
|
|
|
10353
10353
|
// types/txf.ts
|
|
10354
|
-
var ARCHIVED_PREFIX = "archived-";
|
|
10355
|
-
var FORKED_PREFIX = "_forked_";
|
|
10356
10354
|
var RESERVED_KEYS = ["_meta", "_nametag", "_nametags", "_tombstones", "_invalidatedNametags", "_outbox", "_mintOutbox", "_sent", "_invalid", "_integrity", "_history"];
|
|
10357
10355
|
function isTokenKey(key) {
|
|
10358
|
-
return key.startsWith("_") && !
|
|
10359
|
-
}
|
|
10360
|
-
function isArchivedKey(key) {
|
|
10361
|
-
return key.startsWith(ARCHIVED_PREFIX);
|
|
10362
|
-
}
|
|
10363
|
-
function isForkedKey(key) {
|
|
10364
|
-
return key.startsWith(FORKED_PREFIX);
|
|
10356
|
+
return key.startsWith("_") && !RESERVED_KEYS.includes(key);
|
|
10365
10357
|
}
|
|
10366
10358
|
function tokenIdFromKey(key) {
|
|
10367
10359
|
return key.startsWith("_") ? key.substring(1) : key;
|
|
@@ -10369,27 +10361,112 @@ function tokenIdFromKey(key) {
|
|
|
10369
10361
|
function keyFromTokenId(tokenId) {
|
|
10370
10362
|
return `_${tokenId}`;
|
|
10371
10363
|
}
|
|
10372
|
-
function
|
|
10373
|
-
return
|
|
10364
|
+
function isValidTokenId(tokenId) {
|
|
10365
|
+
return /^[0-9a-fA-F]{64}$/.test(tokenId);
|
|
10374
10366
|
}
|
|
10375
|
-
|
|
10376
|
-
|
|
10367
|
+
|
|
10368
|
+
// serialization/txf-serializer.ts
|
|
10369
|
+
function isV2TokenBlob(sdkData) {
|
|
10370
|
+
return typeof sdkData === "string" && sdkData.length >= 2 && sdkData.length % 2 === 0 && sdkData[0] !== "{" && /^[0-9a-f]+$/i.test(sdkData);
|
|
10377
10371
|
}
|
|
10378
|
-
function
|
|
10379
|
-
return
|
|
10372
|
+
function v2TokenId(token) {
|
|
10373
|
+
return token.id.startsWith("v2_") ? token.id.slice(3) : token.id;
|
|
10380
10374
|
}
|
|
10381
|
-
function
|
|
10382
|
-
|
|
10383
|
-
|
|
10384
|
-
|
|
10385
|
-
|
|
10386
|
-
|
|
10387
|
-
|
|
10388
|
-
|
|
10375
|
+
function isV2TokenEntry(entry) {
|
|
10376
|
+
return typeof entry === "object" && entry !== null && !("genesis" in entry) && isV2TokenBlob(entry.sdkData);
|
|
10377
|
+
}
|
|
10378
|
+
async function buildTxfStorageData(tokens, meta, options) {
|
|
10379
|
+
const storageData = {
|
|
10380
|
+
_meta: {
|
|
10381
|
+
...meta,
|
|
10382
|
+
formatVersion: "2.0"
|
|
10383
|
+
}
|
|
10389
10384
|
};
|
|
10385
|
+
if (options?.nametags && options.nametags.length > 0) {
|
|
10386
|
+
storageData._nametags = options.nametags;
|
|
10387
|
+
}
|
|
10388
|
+
if (options?.tombstones && options.tombstones.length > 0) {
|
|
10389
|
+
storageData._tombstones = options.tombstones;
|
|
10390
|
+
}
|
|
10391
|
+
if (options?.historyEntries && options.historyEntries.length > 0) {
|
|
10392
|
+
storageData._history = options.historyEntries;
|
|
10393
|
+
}
|
|
10394
|
+
for (const token of tokens) {
|
|
10395
|
+
if (isV2TokenBlob(token.sdkData)) {
|
|
10396
|
+
storageData[keyFromTokenId(v2TokenId(token))] = token;
|
|
10397
|
+
} else {
|
|
10398
|
+
logger.warn(
|
|
10399
|
+
"TXF",
|
|
10400
|
+
`Refusing to persist token ${token.id.slice(0, 16)}...: sdkData is not a v2 token blob \u2014 entry skipped`
|
|
10401
|
+
);
|
|
10402
|
+
}
|
|
10403
|
+
}
|
|
10404
|
+
return storageData;
|
|
10390
10405
|
}
|
|
10391
|
-
function
|
|
10392
|
-
|
|
10406
|
+
function parseTxfStorageData(data) {
|
|
10407
|
+
const result = {
|
|
10408
|
+
tokens: [],
|
|
10409
|
+
meta: null,
|
|
10410
|
+
nametags: [],
|
|
10411
|
+
tombstones: [],
|
|
10412
|
+
historyEntries: [],
|
|
10413
|
+
validationErrors: []
|
|
10414
|
+
};
|
|
10415
|
+
if (!data || typeof data !== "object") {
|
|
10416
|
+
result.validationErrors.push("Storage data is not an object");
|
|
10417
|
+
return result;
|
|
10418
|
+
}
|
|
10419
|
+
const storageData = data;
|
|
10420
|
+
if (storageData._meta && typeof storageData._meta === "object") {
|
|
10421
|
+
result.meta = storageData._meta;
|
|
10422
|
+
}
|
|
10423
|
+
const seenNames = /* @__PURE__ */ new Set();
|
|
10424
|
+
if (Array.isArray(storageData._nametags)) {
|
|
10425
|
+
for (const entry of storageData._nametags) {
|
|
10426
|
+
if (entry && typeof entry === "object" && typeof entry.name === "string") {
|
|
10427
|
+
result.nametags.push(entry);
|
|
10428
|
+
seenNames.add(entry.name);
|
|
10429
|
+
}
|
|
10430
|
+
}
|
|
10431
|
+
}
|
|
10432
|
+
if (storageData._nametag && typeof storageData._nametag === "object") {
|
|
10433
|
+
const legacy = storageData._nametag;
|
|
10434
|
+
if (typeof legacy.name === "string" && !seenNames.has(legacy.name)) {
|
|
10435
|
+
result.nametags.push(legacy);
|
|
10436
|
+
}
|
|
10437
|
+
}
|
|
10438
|
+
if (storageData._tombstones && Array.isArray(storageData._tombstones)) {
|
|
10439
|
+
for (const entry of storageData._tombstones) {
|
|
10440
|
+
if (typeof entry === "object" && entry !== null && typeof entry.tokenId === "string" && typeof entry.stateHash === "string" && typeof entry.timestamp === "number") {
|
|
10441
|
+
result.tombstones.push(entry);
|
|
10442
|
+
}
|
|
10443
|
+
}
|
|
10444
|
+
}
|
|
10445
|
+
if (Array.isArray(storageData._history)) {
|
|
10446
|
+
for (const entry of storageData._history) {
|
|
10447
|
+
if (typeof entry === "object" && entry !== null && typeof entry.dedupKey === "string" && typeof entry.type === "string") {
|
|
10448
|
+
result.historyEntries.push(entry);
|
|
10449
|
+
}
|
|
10450
|
+
}
|
|
10451
|
+
}
|
|
10452
|
+
for (const key of Object.keys(storageData)) {
|
|
10453
|
+
if (isTokenKey(key)) {
|
|
10454
|
+
const tokenId = tokenIdFromKey(key);
|
|
10455
|
+
const entry = storageData[key];
|
|
10456
|
+
if (isV2TokenEntry(entry)) {
|
|
10457
|
+
result.tokens.push(entry);
|
|
10458
|
+
} else {
|
|
10459
|
+
const msg = `Token ${tokenId}: unrecognized storage entry (not a v2 token blob) \u2014 entry skipped`;
|
|
10460
|
+
logger.warn("TXF", msg);
|
|
10461
|
+
result.validationErrors.push(msg);
|
|
10462
|
+
}
|
|
10463
|
+
} else if (key.startsWith("token-")) {
|
|
10464
|
+
const msg = `Token ${key}: sdkData is not a v2 token blob \u2014 entry skipped`;
|
|
10465
|
+
logger.warn("TXF", msg);
|
|
10466
|
+
result.validationErrors.push(msg);
|
|
10467
|
+
}
|
|
10468
|
+
}
|
|
10469
|
+
return result;
|
|
10393
10470
|
}
|
|
10394
10471
|
|
|
10395
10472
|
// registry/TokenRegistry.ts
|
|
@@ -10830,403 +10907,14 @@ function getCoinIdByName(name) {
|
|
|
10830
10907
|
}
|
|
10831
10908
|
function normalizeCoinId(coinId) {
|
|
10832
10909
|
if (coinId.length <= 20 && /^[A-Za-z0-9]+$/.test(coinId)) {
|
|
10833
|
-
const resolved = getCoinIdBySymbol(coinId);
|
|
10834
|
-
if (resolved) return resolved;
|
|
10835
|
-
}
|
|
10836
|
-
return coinId;
|
|
10837
|
-
}
|
|
10838
|
-
function coinIdsMatch(a, b) {
|
|
10839
|
-
if (a === b) return true;
|
|
10840
|
-
return normalizeCoinId(a) === normalizeCoinId(b);
|
|
10841
|
-
}
|
|
10842
|
-
|
|
10843
|
-
// serialization/txf-serializer.ts
|
|
10844
|
-
function bytesToHex5(bytes) {
|
|
10845
|
-
const arr = Array.isArray(bytes) ? bytes : Array.from(bytes);
|
|
10846
|
-
return arr.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
10847
|
-
}
|
|
10848
|
-
function normalizeToHex(value) {
|
|
10849
|
-
if (typeof value === "string") {
|
|
10850
|
-
return value;
|
|
10851
|
-
}
|
|
10852
|
-
if (value && typeof value === "object") {
|
|
10853
|
-
const obj = value;
|
|
10854
|
-
if ("bytes" in obj && (Array.isArray(obj.bytes) || obj.bytes instanceof Uint8Array)) {
|
|
10855
|
-
return bytesToHex5(obj.bytes);
|
|
10856
|
-
}
|
|
10857
|
-
if (obj.type === "Buffer" && Array.isArray(obj.data)) {
|
|
10858
|
-
return bytesToHex5(obj.data);
|
|
10859
|
-
}
|
|
10860
|
-
}
|
|
10861
|
-
return String(value);
|
|
10862
|
-
}
|
|
10863
|
-
function normalizeSdkTokenToStorage(sdkTokenJson) {
|
|
10864
|
-
const txf = structuredClone(sdkTokenJson);
|
|
10865
|
-
if (txf.genesis?.data) {
|
|
10866
|
-
const data = txf.genesis.data;
|
|
10867
|
-
if (data.tokenId !== void 0) {
|
|
10868
|
-
data.tokenId = normalizeToHex(data.tokenId);
|
|
10869
|
-
}
|
|
10870
|
-
if (data.tokenType !== void 0) {
|
|
10871
|
-
data.tokenType = normalizeToHex(data.tokenType);
|
|
10872
|
-
}
|
|
10873
|
-
if (data.salt !== void 0) {
|
|
10874
|
-
data.salt = normalizeToHex(data.salt);
|
|
10875
|
-
}
|
|
10876
|
-
}
|
|
10877
|
-
if (txf.genesis?.inclusionProof?.authenticator) {
|
|
10878
|
-
const auth = txf.genesis.inclusionProof.authenticator;
|
|
10879
|
-
if (auth.publicKey !== void 0) {
|
|
10880
|
-
auth.publicKey = normalizeToHex(auth.publicKey);
|
|
10881
|
-
}
|
|
10882
|
-
if (auth.signature !== void 0) {
|
|
10883
|
-
auth.signature = normalizeToHex(auth.signature);
|
|
10884
|
-
}
|
|
10885
|
-
}
|
|
10886
|
-
if (Array.isArray(txf.transactions)) {
|
|
10887
|
-
for (const tx of txf.transactions) {
|
|
10888
|
-
if (tx.inclusionProof?.authenticator) {
|
|
10889
|
-
const auth = tx.inclusionProof.authenticator;
|
|
10890
|
-
if (auth.publicKey !== void 0) {
|
|
10891
|
-
auth.publicKey = normalizeToHex(auth.publicKey);
|
|
10892
|
-
}
|
|
10893
|
-
if (auth.signature !== void 0) {
|
|
10894
|
-
auth.signature = normalizeToHex(auth.signature);
|
|
10895
|
-
}
|
|
10896
|
-
}
|
|
10897
|
-
}
|
|
10898
|
-
}
|
|
10899
|
-
return txf;
|
|
10900
|
-
}
|
|
10901
|
-
function tokenToTxf(token) {
|
|
10902
|
-
const jsonData = token.sdkData;
|
|
10903
|
-
if (!jsonData) {
|
|
10904
|
-
return null;
|
|
10905
|
-
}
|
|
10906
|
-
try {
|
|
10907
|
-
const txfData = normalizeSdkTokenToStorage(JSON.parse(jsonData));
|
|
10908
|
-
if (!txfData.genesis || !txfData.state) {
|
|
10909
|
-
return null;
|
|
10910
|
-
}
|
|
10911
|
-
if (!txfData.version) {
|
|
10912
|
-
txfData.version = "2.0";
|
|
10913
|
-
}
|
|
10914
|
-
if (!txfData.transactions) {
|
|
10915
|
-
txfData.transactions = [];
|
|
10916
|
-
}
|
|
10917
|
-
if (!txfData.nametags) {
|
|
10918
|
-
txfData.nametags = [];
|
|
10919
|
-
}
|
|
10920
|
-
if (!txfData._integrity) {
|
|
10921
|
-
txfData._integrity = {
|
|
10922
|
-
genesisDataJSONHash: "0000" + "0".repeat(60)
|
|
10923
|
-
};
|
|
10924
|
-
}
|
|
10925
|
-
return txfData;
|
|
10926
|
-
} catch {
|
|
10927
|
-
return null;
|
|
10928
|
-
}
|
|
10929
|
-
}
|
|
10930
|
-
function objectToTxf(obj) {
|
|
10931
|
-
if (!obj.sdkData) return null;
|
|
10932
|
-
try {
|
|
10933
|
-
const txfData = normalizeSdkTokenToStorage(JSON.parse(obj.sdkData));
|
|
10934
|
-
if (!txfData.genesis || !txfData.state) return null;
|
|
10935
|
-
return txfData;
|
|
10936
|
-
} catch {
|
|
10937
|
-
return null;
|
|
10938
|
-
}
|
|
10939
|
-
}
|
|
10940
|
-
function determineTokenStatus(txf) {
|
|
10941
|
-
if (txf.transactions.length > 0) {
|
|
10942
|
-
const lastTx = txf.transactions[txf.transactions.length - 1];
|
|
10943
|
-
if (lastTx.inclusionProof === null) {
|
|
10944
|
-
return "pending";
|
|
10945
|
-
}
|
|
10946
|
-
}
|
|
10947
|
-
return "confirmed";
|
|
10948
|
-
}
|
|
10949
|
-
function txfToToken(tokenId, txf) {
|
|
10950
|
-
const coinData = txf.genesis.data.coinData;
|
|
10951
|
-
const totalAmount = coinData.reduce((sum, [, amt]) => {
|
|
10952
|
-
return sum + BigInt(amt || "0");
|
|
10953
|
-
}, BigInt(0));
|
|
10954
|
-
let coinId = coinData[0]?.[0] || "";
|
|
10955
|
-
for (const [cid, amt] of coinData) {
|
|
10956
|
-
if (BigInt(amt || "0") > 0) {
|
|
10957
|
-
coinId = cid;
|
|
10958
|
-
break;
|
|
10959
|
-
}
|
|
10960
|
-
}
|
|
10961
|
-
const tokenType = txf.genesis.data.tokenType;
|
|
10962
|
-
const isNft = tokenType === "455ad8720656b08e8dbd5bac1f3c73eeea5431565f6c1c3af742b1aa12d41d89";
|
|
10963
|
-
const now = Date.now();
|
|
10964
|
-
const registry = TokenRegistry.getInstance();
|
|
10965
|
-
const def = registry.getDefinition(coinId);
|
|
10966
|
-
return {
|
|
10967
|
-
id: tokenId,
|
|
10968
|
-
coinId,
|
|
10969
|
-
symbol: isNft ? "NFT" : def?.symbol || coinId.slice(0, 8),
|
|
10970
|
-
name: isNft ? "NFT" : def?.name ? def.name.charAt(0).toUpperCase() + def.name.slice(1) : "Token",
|
|
10971
|
-
decimals: isNft ? 0 : def?.decimals ?? 8,
|
|
10972
|
-
amount: totalAmount.toString(),
|
|
10973
|
-
status: determineTokenStatus(txf),
|
|
10974
|
-
createdAt: now,
|
|
10975
|
-
updatedAt: now,
|
|
10976
|
-
sdkData: JSON.stringify(txf)
|
|
10977
|
-
};
|
|
10978
|
-
}
|
|
10979
|
-
function isV2TokenBlob(sdkData) {
|
|
10980
|
-
return typeof sdkData === "string" && sdkData.length >= 2 && sdkData.length % 2 === 0 && sdkData[0] !== "{" && /^[0-9a-f]+$/i.test(sdkData);
|
|
10981
|
-
}
|
|
10982
|
-
function v2TokenId(token) {
|
|
10983
|
-
return token.id.startsWith("v2_") ? token.id.slice(3) : token.id;
|
|
10984
|
-
}
|
|
10985
|
-
function isV2TokenEntry(entry) {
|
|
10986
|
-
return typeof entry === "object" && entry !== null && !("genesis" in entry) && isV2TokenBlob(entry.sdkData);
|
|
10987
|
-
}
|
|
10988
|
-
async function buildTxfStorageData(tokens, meta, options) {
|
|
10989
|
-
const storageData = {
|
|
10990
|
-
_meta: {
|
|
10991
|
-
...meta,
|
|
10992
|
-
formatVersion: "2.0"
|
|
10993
|
-
}
|
|
10994
|
-
};
|
|
10995
|
-
if (options?.nametags && options.nametags.length > 0) {
|
|
10996
|
-
storageData._nametags = options.nametags;
|
|
10997
|
-
}
|
|
10998
|
-
if (options?.tombstones && options.tombstones.length > 0) {
|
|
10999
|
-
storageData._tombstones = options.tombstones;
|
|
11000
|
-
}
|
|
11001
|
-
if (options?.outboxEntries && options.outboxEntries.length > 0) {
|
|
11002
|
-
storageData._outbox = options.outboxEntries;
|
|
11003
|
-
}
|
|
11004
|
-
if (options?.mintOutboxEntries && options.mintOutboxEntries.length > 0) {
|
|
11005
|
-
storageData._mintOutbox = options.mintOutboxEntries;
|
|
11006
|
-
}
|
|
11007
|
-
if (options?.invalidatedNametags && options.invalidatedNametags.length > 0) {
|
|
11008
|
-
storageData._invalidatedNametags = options.invalidatedNametags;
|
|
11009
|
-
}
|
|
11010
|
-
if (options?.historyEntries && options.historyEntries.length > 0) {
|
|
11011
|
-
storageData._history = options.historyEntries;
|
|
11012
|
-
}
|
|
11013
|
-
for (const token of tokens) {
|
|
11014
|
-
const txf = tokenToTxf(token);
|
|
11015
|
-
if (txf) {
|
|
11016
|
-
const actualTokenId = txf.genesis.data.tokenId;
|
|
11017
|
-
storageData[keyFromTokenId(actualTokenId)] = txf;
|
|
11018
|
-
} else if (isV2TokenBlob(token.sdkData)) {
|
|
11019
|
-
storageData[keyFromTokenId(v2TokenId(token))] = token;
|
|
11020
|
-
}
|
|
11021
|
-
}
|
|
11022
|
-
if (options?.archivedTokens && options.archivedTokens.size > 0) {
|
|
11023
|
-
for (const [tokenId, txf] of options.archivedTokens) {
|
|
11024
|
-
storageData[archivedKeyFromTokenId(tokenId)] = txf;
|
|
11025
|
-
}
|
|
11026
|
-
}
|
|
11027
|
-
if (options?.forkedTokens && options.forkedTokens.size > 0) {
|
|
11028
|
-
for (const [key, txf] of options.forkedTokens) {
|
|
11029
|
-
const [tokenId, stateHash] = key.split("_");
|
|
11030
|
-
if (tokenId && stateHash) {
|
|
11031
|
-
storageData[forkedKeyFromTokenIdAndState(tokenId, stateHash)] = txf;
|
|
11032
|
-
}
|
|
11033
|
-
}
|
|
11034
|
-
}
|
|
11035
|
-
return storageData;
|
|
11036
|
-
}
|
|
11037
|
-
function parseTxfStorageData(data) {
|
|
11038
|
-
const result = {
|
|
11039
|
-
tokens: [],
|
|
11040
|
-
meta: null,
|
|
11041
|
-
nametags: [],
|
|
11042
|
-
tombstones: [],
|
|
11043
|
-
archivedTokens: /* @__PURE__ */ new Map(),
|
|
11044
|
-
forkedTokens: /* @__PURE__ */ new Map(),
|
|
11045
|
-
outboxEntries: [],
|
|
11046
|
-
mintOutboxEntries: [],
|
|
11047
|
-
invalidatedNametags: [],
|
|
11048
|
-
historyEntries: [],
|
|
11049
|
-
validationErrors: []
|
|
11050
|
-
};
|
|
11051
|
-
if (!data || typeof data !== "object") {
|
|
11052
|
-
result.validationErrors.push("Storage data is not an object");
|
|
11053
|
-
return result;
|
|
11054
|
-
}
|
|
11055
|
-
const storageData = data;
|
|
11056
|
-
if (storageData._meta && typeof storageData._meta === "object") {
|
|
11057
|
-
result.meta = storageData._meta;
|
|
11058
|
-
}
|
|
11059
|
-
const seenNames = /* @__PURE__ */ new Set();
|
|
11060
|
-
if (Array.isArray(storageData._nametags)) {
|
|
11061
|
-
for (const entry of storageData._nametags) {
|
|
11062
|
-
if (entry && typeof entry === "object" && typeof entry.name === "string") {
|
|
11063
|
-
result.nametags.push(entry);
|
|
11064
|
-
seenNames.add(entry.name);
|
|
11065
|
-
}
|
|
11066
|
-
}
|
|
11067
|
-
}
|
|
11068
|
-
if (storageData._nametag && typeof storageData._nametag === "object") {
|
|
11069
|
-
const legacy = storageData._nametag;
|
|
11070
|
-
if (typeof legacy.name === "string" && !seenNames.has(legacy.name)) {
|
|
11071
|
-
result.nametags.push(legacy);
|
|
11072
|
-
}
|
|
11073
|
-
}
|
|
11074
|
-
if (storageData._tombstones && Array.isArray(storageData._tombstones)) {
|
|
11075
|
-
for (const entry of storageData._tombstones) {
|
|
11076
|
-
if (typeof entry === "object" && entry !== null && typeof entry.tokenId === "string" && typeof entry.stateHash === "string" && typeof entry.timestamp === "number") {
|
|
11077
|
-
result.tombstones.push(entry);
|
|
11078
|
-
}
|
|
11079
|
-
}
|
|
11080
|
-
}
|
|
11081
|
-
if (storageData._outbox && Array.isArray(storageData._outbox)) {
|
|
11082
|
-
for (const entry of storageData._outbox) {
|
|
11083
|
-
if (typeof entry === "object" && entry !== null && typeof entry.id === "string" && typeof entry.status === "string") {
|
|
11084
|
-
result.outboxEntries.push(entry);
|
|
11085
|
-
}
|
|
11086
|
-
}
|
|
11087
|
-
}
|
|
11088
|
-
if (storageData._mintOutbox && Array.isArray(storageData._mintOutbox)) {
|
|
11089
|
-
for (const entry of storageData._mintOutbox) {
|
|
11090
|
-
if (typeof entry === "object" && entry !== null && typeof entry.id === "string" && typeof entry.status === "string") {
|
|
11091
|
-
result.mintOutboxEntries.push(entry);
|
|
11092
|
-
}
|
|
11093
|
-
}
|
|
11094
|
-
}
|
|
11095
|
-
if (storageData._invalidatedNametags && Array.isArray(storageData._invalidatedNametags)) {
|
|
11096
|
-
for (const entry of storageData._invalidatedNametags) {
|
|
11097
|
-
if (typeof entry === "object" && entry !== null && typeof entry.name === "string") {
|
|
11098
|
-
result.invalidatedNametags.push(entry);
|
|
11099
|
-
}
|
|
11100
|
-
}
|
|
11101
|
-
}
|
|
11102
|
-
if (Array.isArray(storageData._history)) {
|
|
11103
|
-
for (const entry of storageData._history) {
|
|
11104
|
-
if (typeof entry === "object" && entry !== null && typeof entry.dedupKey === "string" && typeof entry.type === "string") {
|
|
11105
|
-
result.historyEntries.push(entry);
|
|
11106
|
-
}
|
|
11107
|
-
}
|
|
11108
|
-
}
|
|
11109
|
-
for (const key of Object.keys(storageData)) {
|
|
11110
|
-
if (isTokenKey(key)) {
|
|
11111
|
-
const tokenId = tokenIdFromKey(key);
|
|
11112
|
-
try {
|
|
11113
|
-
const txfToken = storageData[key];
|
|
11114
|
-
if (txfToken?.genesis?.data?.tokenId) {
|
|
11115
|
-
const token = txfToToken(tokenId, txfToken);
|
|
11116
|
-
result.tokens.push(token);
|
|
11117
|
-
} else if (isV2TokenEntry(storageData[key])) {
|
|
11118
|
-
result.tokens.push(storageData[key]);
|
|
11119
|
-
}
|
|
11120
|
-
} catch (err) {
|
|
11121
|
-
result.validationErrors.push(`Token ${tokenId}: ${err}`);
|
|
11122
|
-
}
|
|
11123
|
-
} else if (isArchivedKey(key)) {
|
|
11124
|
-
const tokenId = tokenIdFromArchivedKey(key);
|
|
11125
|
-
try {
|
|
11126
|
-
const txfToken = storageData[key];
|
|
11127
|
-
if (txfToken?.genesis?.data?.tokenId) {
|
|
11128
|
-
result.archivedTokens.set(tokenId, txfToken);
|
|
11129
|
-
}
|
|
11130
|
-
} catch {
|
|
11131
|
-
result.validationErrors.push(`Archived token ${tokenId}: invalid structure`);
|
|
11132
|
-
}
|
|
11133
|
-
} else if (isForkedKey(key)) {
|
|
11134
|
-
const parsed = parseForkedKey(key);
|
|
11135
|
-
if (parsed) {
|
|
11136
|
-
try {
|
|
11137
|
-
const txfToken = storageData[key];
|
|
11138
|
-
if (txfToken?.genesis?.data?.tokenId) {
|
|
11139
|
-
const mapKey = `${parsed.tokenId}_${parsed.stateHash}`;
|
|
11140
|
-
result.forkedTokens.set(mapKey, txfToken);
|
|
11141
|
-
}
|
|
11142
|
-
} catch {
|
|
11143
|
-
result.validationErrors.push(`Forked token ${parsed.tokenId}: invalid structure`);
|
|
11144
|
-
}
|
|
11145
|
-
}
|
|
11146
|
-
} else if (key.startsWith("token-")) {
|
|
11147
|
-
try {
|
|
11148
|
-
const entry = storageData[key];
|
|
11149
|
-
const txfToken = entry?.token;
|
|
11150
|
-
if (txfToken?.genesis?.data?.tokenId) {
|
|
11151
|
-
const tokenId = txfToken.genesis.data.tokenId;
|
|
11152
|
-
const token = txfToToken(tokenId, txfToken);
|
|
11153
|
-
result.tokens.push(token);
|
|
11154
|
-
}
|
|
11155
|
-
} catch (err) {
|
|
11156
|
-
result.validationErrors.push(`Token ${key}: ${err}`);
|
|
11157
|
-
}
|
|
11158
|
-
}
|
|
11159
|
-
}
|
|
11160
|
-
return result;
|
|
11161
|
-
}
|
|
11162
|
-
function getTokenId(token) {
|
|
11163
|
-
if (token.sdkData) {
|
|
11164
|
-
try {
|
|
11165
|
-
const txf = JSON.parse(token.sdkData);
|
|
11166
|
-
if (txf.genesis?.data?.tokenId) {
|
|
11167
|
-
return txf.genesis.data.tokenId;
|
|
11168
|
-
}
|
|
11169
|
-
} catch {
|
|
11170
|
-
}
|
|
11171
|
-
}
|
|
11172
|
-
return token.id;
|
|
11173
|
-
}
|
|
11174
|
-
function getCurrentStateHash(txf) {
|
|
11175
|
-
if (txf.transactions && txf.transactions.length > 0) {
|
|
11176
|
-
const lastTx = txf.transactions[txf.transactions.length - 1];
|
|
11177
|
-
if (lastTx?.newStateHash) {
|
|
11178
|
-
return lastTx.newStateHash;
|
|
11179
|
-
}
|
|
11180
|
-
if (lastTx?.inclusionProof?.authenticator?.stateHash) {
|
|
11181
|
-
return lastTx.inclusionProof.authenticator.stateHash;
|
|
11182
|
-
}
|
|
11183
|
-
}
|
|
11184
|
-
if (txf._integrity?.currentStateHash) {
|
|
11185
|
-
return txf._integrity.currentStateHash;
|
|
11186
|
-
}
|
|
11187
|
-
if (txf.genesis?.inclusionProof?.authenticator?.stateHash) {
|
|
11188
|
-
return txf.genesis.inclusionProof.authenticator.stateHash;
|
|
11189
|
-
}
|
|
11190
|
-
return void 0;
|
|
11191
|
-
}
|
|
11192
|
-
function hasValidTxfData(token) {
|
|
11193
|
-
if (!token.sdkData) return false;
|
|
11194
|
-
try {
|
|
11195
|
-
const txf = JSON.parse(token.sdkData);
|
|
11196
|
-
return !!(txf.genesis && txf.genesis.data && txf.genesis.data.tokenId && txf.state && txf.genesis.inclusionProof);
|
|
11197
|
-
} catch {
|
|
11198
|
-
return false;
|
|
11199
|
-
}
|
|
11200
|
-
}
|
|
11201
|
-
function hasUncommittedTransactions(token) {
|
|
11202
|
-
if (!token.sdkData) return false;
|
|
11203
|
-
try {
|
|
11204
|
-
const txf = JSON.parse(token.sdkData);
|
|
11205
|
-
if (!txf.transactions || txf.transactions.length === 0) return false;
|
|
11206
|
-
return txf.transactions.some(
|
|
11207
|
-
(tx) => tx.inclusionProof === null
|
|
11208
|
-
);
|
|
11209
|
-
} catch {
|
|
11210
|
-
return false;
|
|
11211
|
-
}
|
|
11212
|
-
}
|
|
11213
|
-
function hasMissingNewStateHash(txf) {
|
|
11214
|
-
if (!txf.transactions || txf.transactions.length === 0) {
|
|
11215
|
-
return false;
|
|
10910
|
+
const resolved = getCoinIdBySymbol(coinId);
|
|
10911
|
+
if (resolved) return resolved;
|
|
11216
10912
|
}
|
|
11217
|
-
return
|
|
10913
|
+
return coinId;
|
|
11218
10914
|
}
|
|
11219
|
-
function
|
|
11220
|
-
if (
|
|
11221
|
-
|
|
11222
|
-
const txf = JSON.parse(token.sdkData);
|
|
11223
|
-
if (!txf.transactions) return 0;
|
|
11224
|
-
return txf.transactions.filter(
|
|
11225
|
-
(tx) => tx.inclusionProof !== null
|
|
11226
|
-
).length;
|
|
11227
|
-
} catch {
|
|
11228
|
-
return 0;
|
|
11229
|
-
}
|
|
10915
|
+
function coinIdsMatch(a, b) {
|
|
10916
|
+
if (a === b) return true;
|
|
10917
|
+
return normalizeCoinId(a) === normalizeCoinId(b);
|
|
11230
10918
|
}
|
|
11231
10919
|
|
|
11232
10920
|
// core/utils.ts
|
|
@@ -11394,6 +11082,10 @@ function surfacedSendFailure(summary) {
|
|
|
11394
11082
|
}
|
|
11395
11083
|
return primaryError;
|
|
11396
11084
|
}
|
|
11085
|
+
function currentStateHashOfTxf(txf) {
|
|
11086
|
+
const last = txf.transactions?.[txf.transactions.length - 1];
|
|
11087
|
+
return last?.newStateHash ?? last?.inclusionProof?.authenticator?.stateHash ?? txf._integrity?.currentStateHash ?? txf.genesis?.inclusionProof?.authenticator?.stateHash ?? "";
|
|
11088
|
+
}
|
|
11397
11089
|
function genesisIdOf(token) {
|
|
11398
11090
|
return extractTokenIdFromSdkData(token.sdkData) ?? token.id.replace(/^v2_/, "");
|
|
11399
11091
|
}
|
|
@@ -11505,7 +11197,7 @@ function parseSdkDataCached(sdkData) {
|
|
|
11505
11197
|
try {
|
|
11506
11198
|
const txf = JSON.parse(sdkData);
|
|
11507
11199
|
tokenId = txf.genesis?.data?.tokenId || null;
|
|
11508
|
-
stateHash =
|
|
11200
|
+
stateHash = currentStateHashOfTxf(txf);
|
|
11509
11201
|
if (!stateHash) {
|
|
11510
11202
|
if (txf.state?.hash) {
|
|
11511
11203
|
stateHash = txf.state.hash;
|
|
@@ -12251,7 +11943,6 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12251
11943
|
if (op.kind === "direct") {
|
|
12252
11944
|
finished = await ctx.engine.transfer({ token: op.sdkToken, recipientPubkey: ctx.recipientChainPubkey }, opts);
|
|
12253
11945
|
} else {
|
|
12254
|
-
const checkpointStore = this.getCheckpointStore();
|
|
12255
11946
|
const { outputs } = await ctx.engine.split(
|
|
12256
11947
|
{
|
|
12257
11948
|
token: op.sdkToken,
|
|
@@ -12260,7 +11951,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12260
11951
|
{ recipientPubkey: ctx.selfChainPubkey, coinId: ctx.coinId, amount: op.remainderAmount }
|
|
12261
11952
|
]
|
|
12262
11953
|
},
|
|
12263
|
-
{ ...opts, ...checkpointStore ? { checkpointStore } : {} }
|
|
11954
|
+
{ ...opts, ...ctx.checkpointStore ? { checkpointStore: ctx.checkpointStore } : {} }
|
|
12264
11955
|
);
|
|
12265
11956
|
finished = outputs[0];
|
|
12266
11957
|
changeOutput = outputs[1];
|
|
@@ -12447,7 +12138,8 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
12447
12138
|
recipientChainPubkeyHex,
|
|
12448
12139
|
selfChainPubkey: hexToBytes4(this.deps.identity.chainPubkey),
|
|
12449
12140
|
coinId: request.coinId,
|
|
12450
|
-
...request.memo !== void 0 ? { memo: request.memo } : {}
|
|
12141
|
+
...request.memo !== void 0 ? { memo: request.memo } : {},
|
|
12142
|
+
...this.getCheckpointStore() ? { checkpointStore: this.getCheckpointStore() } : {}
|
|
12451
12143
|
})
|
|
12452
12144
|
);
|
|
12453
12145
|
attempt.summary = summary;
|
|
@@ -14964,8 +14656,11 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
14964
14656
|
try {
|
|
14965
14657
|
const plain = decryptField(this.getFieldEncryptionKey(), intent.payload);
|
|
14966
14658
|
payload = JSON.parse(plain);
|
|
14967
|
-
if (payload.v !==
|
|
14968
|
-
throw new SphereError(
|
|
14659
|
+
if (payload.v !== 2 || !Array.isArray(payload.direct)) {
|
|
14660
|
+
throw new SphereError(
|
|
14661
|
+
`Unsupported intent payload shape (v=${String(payload.v)}) \u2014 not resumable by this wallet version`,
|
|
14662
|
+
"VALIDATION_ERROR"
|
|
14663
|
+
);
|
|
14969
14664
|
}
|
|
14970
14665
|
} catch (err) {
|
|
14971
14666
|
logger.warn("Payments", `Intent ${intent.transferId.slice(0, 8)}\u2026 payload undecodable \u2014 skipped:`, err);
|
|
@@ -15104,31 +14799,22 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
15104
14799
|
async resumeIntent(transferId, payload) {
|
|
15105
14800
|
const engine = this.deps.tokenEngine;
|
|
15106
14801
|
const walletApi = this.deps.walletApi;
|
|
15107
|
-
const delivery = this.delivery;
|
|
15108
14802
|
const provider = this.getActiveTokenStorageProvider();
|
|
15109
14803
|
if (!provider) throw new SphereError("No token storage provider for intent resume", "STORAGE_ERROR");
|
|
15110
|
-
const serverApply = delivery.custody === "inventory";
|
|
15111
|
-
const
|
|
15112
|
-
const
|
|
15113
|
-
|
|
15114
|
-
|
|
15115
|
-
|
|
15116
|
-
|
|
15117
|
-
|
|
15118
|
-
|
|
15119
|
-
|
|
15120
|
-
}
|
|
15121
|
-
const nm = this.currentNametagName();
|
|
15122
|
-
await this.tryDeliver(
|
|
15123
|
-
() => delivery.deliver(payload.recipient, hexToBytes4(tokenBlobHex), {
|
|
15124
|
-
transferId,
|
|
15125
|
-
memo: payload.memo,
|
|
15126
|
-
...nm !== void 0 ? { senderNametag: nm } : {}
|
|
15127
|
-
}),
|
|
15128
|
-
tokenBlobHex
|
|
15129
|
-
);
|
|
14804
|
+
const serverApply = this.delivery.custody === "inventory";
|
|
14805
|
+
const checkpointStore = this.getCheckpointStore();
|
|
14806
|
+
const ctx = {
|
|
14807
|
+
engine,
|
|
14808
|
+
transferId,
|
|
14809
|
+
recipientChainPubkey: hexToBytes4(payload.recipient),
|
|
14810
|
+
recipientChainPubkeyHex: payload.recipient,
|
|
14811
|
+
selfChainPubkey: hexToBytes4(this.deps.identity.chainPubkey),
|
|
14812
|
+
coinId: payload.coinId,
|
|
14813
|
+
...payload.memo !== void 0 ? { memo: payload.memo } : {},
|
|
14814
|
+
...checkpointStore ? { checkpointStore } : {}
|
|
15130
14815
|
};
|
|
15131
14816
|
const journaled = await this.journaledByOp(transferId);
|
|
14817
|
+
const blobs = [];
|
|
15132
14818
|
const spent = [];
|
|
15133
14819
|
let conflicted = false;
|
|
15134
14820
|
let firstConflict;
|
|
@@ -15136,91 +14822,71 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
15136
14822
|
for (const [opIndex, genesisId] of payload.direct.entries()) {
|
|
15137
14823
|
const existing = journaled.get(opIndex);
|
|
15138
14824
|
if (existing) {
|
|
15139
|
-
|
|
14825
|
+
blobs.push(existing.tokenBlob);
|
|
15140
14826
|
spent.push(genesisId);
|
|
15141
|
-
|
|
15142
|
-
|
|
15143
|
-
|
|
15144
|
-
|
|
15145
|
-
|
|
15146
|
-
|
|
15147
|
-
|
|
15148
|
-
|
|
15149
|
-
|
|
15150
|
-
|
|
15151
|
-
|
|
15152
|
-
|
|
15153
|
-
|
|
15154
|
-
|
|
15155
|
-
|
|
15156
|
-
|
|
15157
|
-
|
|
14827
|
+
continue;
|
|
14828
|
+
}
|
|
14829
|
+
const source = await engine.decodeToken(await provider.getToken(genesisId));
|
|
14830
|
+
const outcome = await this.certifyOperation(
|
|
14831
|
+
{
|
|
14832
|
+
kind: "direct",
|
|
14833
|
+
opIndex,
|
|
14834
|
+
uiTokenId: `v2_${genesisId}`,
|
|
14835
|
+
genesisId,
|
|
14836
|
+
sourceSdkData: "",
|
|
14837
|
+
sdkToken: source,
|
|
14838
|
+
deliveredAmount: engine.balanceOf(source, payload.coinId)
|
|
14839
|
+
},
|
|
14840
|
+
ctx
|
|
14841
|
+
);
|
|
14842
|
+
if (outcome.error !== void 0) {
|
|
14843
|
+
if (!(outcome.error instanceof TransferConflictError)) throw outcome.error;
|
|
14844
|
+
conflicted = true;
|
|
14845
|
+
firstConflict ??= outcome.error;
|
|
14846
|
+
undeliveredAmount += engine.balanceOf(source, payload.coinId);
|
|
14847
|
+
logger.warn("Payments", `Resume op ${opIndex} lost its source to a concurrent transfer \u2014 not delivered (\xA78.1/#4):`, outcome.error);
|
|
14848
|
+
continue;
|
|
15158
14849
|
}
|
|
14850
|
+
blobs.push(outcome.tokenBlob);
|
|
14851
|
+
spent.push(genesisId);
|
|
15159
14852
|
}
|
|
15160
14853
|
let changeOutput = null;
|
|
15161
14854
|
if (payload.split) {
|
|
15162
14855
|
const splitOpIndex = payload.direct.length;
|
|
15163
|
-
const existingSplit = journaled.get(splitOpIndex);
|
|
15164
|
-
const checkpointStore = payload.v === 2 ? this.getCheckpointStore() : void 0;
|
|
15165
14856
|
let splitDelivered = false;
|
|
15166
|
-
|
|
15167
|
-
await
|
|
15168
|
-
|
|
15169
|
-
|
|
15170
|
-
|
|
15171
|
-
|
|
15172
|
-
|
|
15173
|
-
|
|
15174
|
-
|
|
15175
|
-
|
|
15176
|
-
|
|
15177
|
-
|
|
15178
|
-
|
|
15179
|
-
|
|
15180
|
-
|
|
15181
|
-
|
|
15182
|
-
|
|
15183
|
-
|
|
15184
|
-
|
|
15185
|
-
coinId: payload.coinId,
|
|
15186
|
-
amount: BigInt(payload.split.remainderAmount)
|
|
15187
|
-
}
|
|
15188
|
-
]
|
|
15189
|
-
},
|
|
15190
|
-
{
|
|
15191
|
-
signal: timeoutSignal(SEND_ENGINE_OP_TIMEOUT_MS),
|
|
15192
|
-
transferId,
|
|
15193
|
-
opIndex: splitOpIndex,
|
|
15194
|
-
...checkpointStore ? { checkpointStore } : {}
|
|
15195
|
-
}
|
|
15196
|
-
);
|
|
15197
|
-
changeOutput = outputs[1];
|
|
15198
|
-
if (!serverApply) await this.storeEngineToken(engine, changeOutput, { criticalSave: true });
|
|
15199
|
-
await deliverBlob(bytesToHex4(encodeTokenBlob(engine.encodeToken(outputs[0]))), splitOpIndex);
|
|
15200
|
-
splitDelivered = true;
|
|
15201
|
-
} catch (err) {
|
|
15202
|
-
if (err instanceof ProofUnconfirmedError) throw err;
|
|
15203
|
-
if (checkpointStore !== void 0 && (err instanceof CheckpointPersistFailedError || err instanceof SplitCheckpointLostError || err instanceof CheckpointTrustbaseMismatchError)) {
|
|
15204
|
-
throw err;
|
|
14857
|
+
{
|
|
14858
|
+
const source = await engine.decodeToken(await provider.getToken(payload.split.tokenId));
|
|
14859
|
+
const outcome = await this.certifyOperation(
|
|
14860
|
+
{
|
|
14861
|
+
kind: "split",
|
|
14862
|
+
opIndex: splitOpIndex,
|
|
14863
|
+
uiTokenId: `v2_${payload.split.tokenId}`,
|
|
14864
|
+
genesisId: payload.split.tokenId,
|
|
14865
|
+
sourceSdkData: "",
|
|
14866
|
+
sdkToken: source,
|
|
14867
|
+
deliveredAmount: BigInt(payload.split.splitAmount),
|
|
14868
|
+
remainderAmount: BigInt(payload.split.remainderAmount)
|
|
14869
|
+
},
|
|
14870
|
+
ctx
|
|
14871
|
+
);
|
|
14872
|
+
if (outcome.error === void 0) {
|
|
14873
|
+
changeOutput = outcome.changeOutput ?? null;
|
|
14874
|
+
if (!serverApply && changeOutput) {
|
|
14875
|
+
await this.storeEngineToken(engine, changeOutput, { criticalSave: true });
|
|
15205
14876
|
}
|
|
15206
|
-
|
|
14877
|
+
blobs.push(outcome.tokenBlob);
|
|
14878
|
+
splitDelivered = true;
|
|
14879
|
+
} else {
|
|
14880
|
+
splitDelivered = this.classifyResumeSplitFailure(outcome.error, (err) => {
|
|
15207
14881
|
conflicted = true;
|
|
15208
14882
|
firstConflict ??= err;
|
|
15209
14883
|
undeliveredAmount += BigInt(payload.split.splitAmount);
|
|
15210
|
-
|
|
15211
|
-
} else if (err instanceof SplitCheckpointLostError) {
|
|
15212
|
-
logger.warn("Payments", `Resume split (legacy no-checkpoint): recording the spend; change recovers on the next inventory resync (\xA73.1):`, err);
|
|
15213
|
-
this.deps.emitEvent("inventory:conflict", { transferId, coinId: payload.coinId, error: err.message });
|
|
15214
|
-
splitDelivered = true;
|
|
15215
|
-
} else {
|
|
15216
|
-
throw err;
|
|
15217
|
-
}
|
|
14884
|
+
});
|
|
15218
14885
|
}
|
|
15219
14886
|
}
|
|
15220
|
-
if (splitDelivered)
|
|
15221
|
-
spent.push(payload.split.tokenId);
|
|
15222
|
-
}
|
|
14887
|
+
if (splitDelivered) spent.push(payload.split.tokenId);
|
|
15223
14888
|
}
|
|
14889
|
+
await this.deliverCommittedBlobs(blobs, payload.recipient, transferId, payload.memo);
|
|
15224
14890
|
if (conflicted && spent.length === 0) throw firstConflict;
|
|
15225
14891
|
if (serverApply) {
|
|
15226
14892
|
await this.applyInventoryDelta(
|
|
@@ -15232,6 +14898,55 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
15232
14898
|
// the one the sources were read from, captured for the whole resume
|
|
15233
14899
|
);
|
|
15234
14900
|
}
|
|
14901
|
+
await this.dropResumedSources(spent, transferId, payload);
|
|
14902
|
+
if (conflicted) {
|
|
14903
|
+
const remaining = undeliveredAmount > 0n ? undeliveredAmount : 0n;
|
|
14904
|
+
const delivered = BigInt(payload.amount) - remaining;
|
|
14905
|
+
await walletApi.completeIntent(transferId);
|
|
14906
|
+
await this.recordResumedSentHistory(payload, transferId, delivered > 0n ? delivered.toString() : "0", spent[0]);
|
|
14907
|
+
this.deps.emitEvent("send:partial-remainder", {
|
|
14908
|
+
transferId,
|
|
14909
|
+
remainingAmount: remaining.toString(),
|
|
14910
|
+
coinId: payload.coinId,
|
|
14911
|
+
recipientPubkey: payload.recipient
|
|
14912
|
+
});
|
|
14913
|
+
throw new PartialSendConflictError(
|
|
14914
|
+
"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.",
|
|
14915
|
+
transferId,
|
|
14916
|
+
spent,
|
|
14917
|
+
// the DELIVERED genesisIds — never the conflicted leg
|
|
14918
|
+
remaining.toString(),
|
|
14919
|
+
firstConflict
|
|
14920
|
+
);
|
|
14921
|
+
}
|
|
14922
|
+
await walletApi.completeIntent(transferId);
|
|
14923
|
+
await this.recordResumedSentHistory(payload, transferId, payload.amount, payload.direct[0]);
|
|
14924
|
+
}
|
|
14925
|
+
/**
|
|
14926
|
+
* A resumed split that produced no output: a lost source means the leg delivered
|
|
14927
|
+
* nothing (returns false), and every checkpoint failure keeps the intent open by
|
|
14928
|
+
* throwing. There is no path here that records the spend.
|
|
14929
|
+
*/
|
|
14930
|
+
classifyResumeSplitFailure(error, onConflict) {
|
|
14931
|
+
if (error instanceof ProofUnconfirmedError) throw error;
|
|
14932
|
+
if (error instanceof CheckpointPersistFailedError || error instanceof SplitCheckpointLostError || error instanceof CheckpointTrustbaseMismatchError) {
|
|
14933
|
+
throw error;
|
|
14934
|
+
}
|
|
14935
|
+
if (error instanceof TransferConflictError) {
|
|
14936
|
+
onConflict(error);
|
|
14937
|
+
logger.warn("Payments", "Resume split: source lost to a concurrent transfer \u2014 not delivered (\xA78.1/#4):", error);
|
|
14938
|
+
return false;
|
|
14939
|
+
}
|
|
14940
|
+
throw error;
|
|
14941
|
+
}
|
|
14942
|
+
/**
|
|
14943
|
+
* Drop the local records of the sources a resume consumed (present when
|
|
14944
|
+
* resuming on the originating device). M7: each removal carries the LOCAL state
|
|
14945
|
+
* we spent, so a source a concurrent claim reactivated to a NEW state (a
|
|
14946
|
+
* self-send round-trip) is KEPT, not destroyed.
|
|
14947
|
+
*/
|
|
14948
|
+
async dropResumedSources(spent, transferId, payload) {
|
|
14949
|
+
const engine = this.deps.tokenEngine;
|
|
15235
14950
|
for (const genesisId of spent) {
|
|
15236
14951
|
const local = this.tokens.get(`v2_${genesisId}`);
|
|
15237
14952
|
if (!local) continue;
|
|
@@ -15243,8 +14958,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
15243
14958
|
let spentOnChain = false;
|
|
15244
14959
|
try {
|
|
15245
14960
|
if (local.sdkData) {
|
|
15246
|
-
|
|
15247
|
-
spentOnChain = await engine.isSpent(decoded);
|
|
14961
|
+
spentOnChain = await engine.isSpent(await engine.decodeToken(decodeTokenBlob(hexToBytes4(local.sdkData))));
|
|
15248
14962
|
}
|
|
15249
14963
|
} catch {
|
|
15250
14964
|
spentOnChain = false;
|
|
@@ -15258,47 +14972,19 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
15258
14972
|
);
|
|
15259
14973
|
}
|
|
15260
14974
|
}
|
|
15261
|
-
|
|
15262
|
-
|
|
15263
|
-
|
|
15264
|
-
await walletApi.completeIntent(transferId);
|
|
15265
|
-
await this.addToHistory({
|
|
15266
|
-
type: "SENT",
|
|
15267
|
-
amount: (delivered > 0n ? delivered : 0n).toString(),
|
|
15268
|
-
coinId: payload.coinId,
|
|
15269
|
-
symbol: this.getCoinSymbol(payload.coinId),
|
|
15270
|
-
timestamp: Date.now(),
|
|
15271
|
-
recipientPubkey: payload.recipient,
|
|
15272
|
-
memo: payload.memo,
|
|
15273
|
-
transferId,
|
|
15274
|
-
tokenId: spent[0] ? `v2_${spent[0]}` : void 0
|
|
15275
|
-
});
|
|
15276
|
-
this.deps.emitEvent("send:partial-remainder", {
|
|
15277
|
-
transferId,
|
|
15278
|
-
remainingAmount: (remaining > 0n ? remaining : 0n).toString(),
|
|
15279
|
-
coinId: payload.coinId,
|
|
15280
|
-
recipientPubkey: payload.recipient
|
|
15281
|
-
});
|
|
15282
|
-
throw new PartialSendConflictError(
|
|
15283
|
-
"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.",
|
|
15284
|
-
transferId,
|
|
15285
|
-
spent,
|
|
15286
|
-
// the DELIVERED genesisIds — never the conflicted leg
|
|
15287
|
-
(remaining > 0n ? remaining : 0n).toString(),
|
|
15288
|
-
firstConflict
|
|
15289
|
-
);
|
|
15290
|
-
}
|
|
15291
|
-
await walletApi.completeIntent(transferId);
|
|
14975
|
+
}
|
|
14976
|
+
/** §10: the SENT record — same dedupKey as the original attempt, so a resume after the history POST is a server-side no-op. */
|
|
14977
|
+
async recordResumedSentHistory(payload, transferId, amount, tokenGenesisId) {
|
|
15292
14978
|
await this.addToHistory({
|
|
15293
14979
|
type: "SENT",
|
|
15294
|
-
amount
|
|
14980
|
+
amount,
|
|
15295
14981
|
coinId: payload.coinId,
|
|
15296
14982
|
symbol: this.getCoinSymbol(payload.coinId),
|
|
15297
14983
|
timestamp: Date.now(),
|
|
15298
14984
|
recipientPubkey: payload.recipient,
|
|
15299
14985
|
memo: payload.memo,
|
|
15300
14986
|
transferId,
|
|
15301
|
-
tokenId:
|
|
14987
|
+
tokenId: tokenGenesisId ? `v2_${tokenGenesisId}` : void 0
|
|
15302
14988
|
});
|
|
15303
14989
|
}
|
|
15304
14990
|
// ===========================================================================
|
|
@@ -15320,11 +15006,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
15320
15006
|
*/
|
|
15321
15007
|
async save(opts = {}) {
|
|
15322
15008
|
const providers = this.getTokenStorageProviders();
|
|
15323
|
-
|
|
15324
|
-
const txf = tokenToTxf(t);
|
|
15325
|
-
return `${t.id.slice(0, 12)}(${t.status},txf=${!!txf})`;
|
|
15326
|
-
});
|
|
15327
|
-
logger.debug("Payments", `save(): providers=${providers.size}, tokens=[${tokenStats.join(", ")}]`);
|
|
15009
|
+
logger.debug("Payments", `save(): providers=${providers.size}, tokens=${this.tokens.size}`);
|
|
15328
15010
|
if (providers.size === 0) {
|
|
15329
15011
|
logger.debug("Payments", "save(): No token storage providers - TXF not persisted");
|
|
15330
15012
|
return;
|
|
@@ -23998,7 +23680,6 @@ import canonicalize from "canonicalize";
|
|
|
23998
23680
|
|
|
23999
23681
|
// core/address.ts
|
|
24000
23682
|
var DIRECT_PREFIX = "DIRECT://";
|
|
24001
|
-
var PROXY_PREFIX = "PROXY://";
|
|
24002
23683
|
var NAMETAG_PREFIX = "@";
|
|
24003
23684
|
function parseAddress(address) {
|
|
24004
23685
|
if (!address || typeof address !== "string") return null;
|
|
@@ -24010,9 +23691,6 @@ function parseAddress(address) {
|
|
|
24010
23691
|
}
|
|
24011
23692
|
return null;
|
|
24012
23693
|
}
|
|
24013
|
-
if (trimmed.startsWith(PROXY_PREFIX) && trimmed.length > PROXY_PREFIX.length) {
|
|
24014
|
-
return { type: "PROXY", raw: trimmed, value: trimmed.slice(PROXY_PREFIX.length) };
|
|
24015
|
-
}
|
|
24016
23694
|
if (trimmed.startsWith(NAMETAG_PREFIX)) {
|
|
24017
23695
|
const value = trimmed.slice(NAMETAG_PREFIX.length);
|
|
24018
23696
|
if (value.length > 0) {
|
|
@@ -24035,8 +23713,6 @@ function normalizeAddress(address) {
|
|
|
24035
23713
|
switch (parsed.type) {
|
|
24036
23714
|
case "DIRECT":
|
|
24037
23715
|
return `DIRECT://${parsed.value.toLowerCase()}`;
|
|
24038
|
-
case "PROXY":
|
|
24039
|
-
return `PROXY://${parsed.value.toLowerCase()}`;
|
|
24040
23716
|
case "NAMETAG":
|
|
24041
23717
|
return `@${parsed.value.toLowerCase()}`;
|
|
24042
23718
|
}
|
|
@@ -27257,444 +26933,94 @@ function parseWalletText(content) {
|
|
|
27257
26933
|
return {
|
|
27258
26934
|
success: false,
|
|
27259
26935
|
error: "Could not find encrypted master key in backup file"
|
|
27260
|
-
};
|
|
27261
|
-
}
|
|
27262
|
-
return {
|
|
27263
|
-
success: false,
|
|
27264
|
-
needsPassword: true,
|
|
27265
|
-
error: "Password required for encrypted wallet"
|
|
27266
|
-
};
|
|
27267
|
-
}
|
|
27268
|
-
const masterKey = extractFromText(
|
|
27269
|
-
content,
|
|
27270
|
-
/MASTER PRIVATE KEY \(keep secret!\):\s*([^\n]+)/
|
|
27271
|
-
);
|
|
27272
|
-
if (!masterKey) {
|
|
27273
|
-
return {
|
|
27274
|
-
success: false,
|
|
27275
|
-
error: "Could not find master private key in backup file"
|
|
27276
|
-
};
|
|
27277
|
-
}
|
|
27278
|
-
const chainCode = extractFromText(
|
|
27279
|
-
content,
|
|
27280
|
-
/MASTER CHAIN CODE \(for (?:BIP32 HD|Alpha) wallet compatibility\):\s*([^\n]+)/
|
|
27281
|
-
);
|
|
27282
|
-
const descriptorPath = extractFromText(content, /DESCRIPTOR PATH:\s*([^\n]+)/);
|
|
27283
|
-
const isBIP32 = content.includes("WALLET TYPE: BIP32 hierarchical deterministic wallet") || content.includes("WALLET TYPE: Alpha descriptor wallet") || !!chainCode;
|
|
27284
|
-
const effectiveDescriptorPath = descriptorPath ?? (isBIP32 ? "84'/1'/0'" : void 0);
|
|
27285
|
-
const data = {
|
|
27286
|
-
masterKey,
|
|
27287
|
-
chainCode: chainCode ?? void 0,
|
|
27288
|
-
descriptorPath: effectiveDescriptorPath,
|
|
27289
|
-
derivationMode: isBIP32 ? "bip32" : "wif_hmac"
|
|
27290
|
-
};
|
|
27291
|
-
return {
|
|
27292
|
-
success: true,
|
|
27293
|
-
data
|
|
27294
|
-
};
|
|
27295
|
-
} catch (e) {
|
|
27296
|
-
return {
|
|
27297
|
-
success: false,
|
|
27298
|
-
error: `Failed to parse wallet text: ${e instanceof Error ? e.message : String(e)}`
|
|
27299
|
-
};
|
|
27300
|
-
}
|
|
27301
|
-
}
|
|
27302
|
-
function parseAndDecryptWalletText(content, password) {
|
|
27303
|
-
try {
|
|
27304
|
-
const isEncrypted = isTextWalletEncrypted(content);
|
|
27305
|
-
if (!isEncrypted) {
|
|
27306
|
-
return parseWalletText(content);
|
|
27307
|
-
}
|
|
27308
|
-
const encryptedKey = extractFromText(
|
|
27309
|
-
content,
|
|
27310
|
-
/ENCRYPTED MASTER KEY \(password protected\):\s*([^\n]+)/
|
|
27311
|
-
);
|
|
27312
|
-
if (!encryptedKey) {
|
|
27313
|
-
return {
|
|
27314
|
-
success: false,
|
|
27315
|
-
error: "Could not find encrypted master key in backup file"
|
|
27316
|
-
};
|
|
27317
|
-
}
|
|
27318
|
-
const masterKey = decryptTextFormatKey(encryptedKey, password);
|
|
27319
|
-
if (!masterKey) {
|
|
27320
|
-
return {
|
|
27321
|
-
success: false,
|
|
27322
|
-
error: "Failed to decrypt - incorrect password?"
|
|
27323
|
-
};
|
|
27324
|
-
}
|
|
27325
|
-
const chainCode = extractFromText(
|
|
27326
|
-
content,
|
|
27327
|
-
/MASTER CHAIN CODE \(for (?:BIP32 HD|Alpha) wallet compatibility\):\s*([^\n]+)/
|
|
27328
|
-
);
|
|
27329
|
-
const descriptorPath = extractFromText(content, /DESCRIPTOR PATH:\s*([^\n]+)/);
|
|
27330
|
-
const isBIP32 = content.includes("WALLET TYPE: BIP32 hierarchical deterministic wallet") || content.includes("WALLET TYPE: Alpha descriptor wallet") || !!chainCode;
|
|
27331
|
-
const effectiveDescriptorPath = descriptorPath ?? (isBIP32 ? "84'/1'/0'" : void 0);
|
|
27332
|
-
const data = {
|
|
27333
|
-
masterKey,
|
|
27334
|
-
chainCode: chainCode ?? void 0,
|
|
27335
|
-
descriptorPath: effectiveDescriptorPath,
|
|
27336
|
-
derivationMode: isBIP32 ? "bip32" : "wif_hmac"
|
|
27337
|
-
};
|
|
27338
|
-
return {
|
|
27339
|
-
success: true,
|
|
27340
|
-
data
|
|
27341
|
-
};
|
|
27342
|
-
} catch (e) {
|
|
27343
|
-
return {
|
|
27344
|
-
success: false,
|
|
27345
|
-
error: `Failed to parse/decrypt wallet text: ${e instanceof Error ? e.message : String(e)}`
|
|
27346
|
-
};
|
|
27347
|
-
}
|
|
27348
|
-
}
|
|
27349
|
-
|
|
27350
|
-
// serialization/wallet-dat.ts
|
|
27351
|
-
import CryptoJS4 from "crypto-js";
|
|
27352
|
-
function uint8ArrayToWordArray(u8arr) {
|
|
27353
|
-
const hex = bytesToHex4(u8arr);
|
|
27354
|
-
return CryptoJS4.enc.Hex.parse(hex);
|
|
27355
|
-
}
|
|
27356
|
-
function isSQLiteDatabase(data) {
|
|
27357
|
-
if (data.length < 16) return false;
|
|
27358
|
-
const header = new TextDecoder().decode(data.slice(0, 16));
|
|
27359
|
-
return header.startsWith("SQLite format 3");
|
|
27360
|
-
}
|
|
27361
|
-
function isWalletDatEncrypted(data) {
|
|
27362
|
-
const mkeyPattern = new TextEncoder().encode("mkey");
|
|
27363
|
-
return findPattern(data, mkeyPattern, 0) !== -1;
|
|
27364
|
-
}
|
|
27365
|
-
function findAllCMasterKeys(data) {
|
|
27366
|
-
const results = [];
|
|
27367
|
-
for (let pos = 0; pos < data.length - 70; pos++) {
|
|
27368
|
-
if (data[pos] === 48) {
|
|
27369
|
-
const saltLenPos = pos + 1 + 48;
|
|
27370
|
-
if (saltLenPos < data.length && data[saltLenPos] === 8) {
|
|
27371
|
-
const iterPos = saltLenPos + 1 + 8 + 4;
|
|
27372
|
-
if (iterPos + 4 <= data.length) {
|
|
27373
|
-
const iterations = data[iterPos] | data[iterPos + 1] << 8 | data[iterPos + 2] << 16 | data[iterPos + 3] << 24;
|
|
27374
|
-
if (iterations >= 1e3 && iterations <= 1e7) {
|
|
27375
|
-
const encryptedKey = data.slice(pos + 1, pos + 1 + 48);
|
|
27376
|
-
const salt = data.slice(saltLenPos + 1, saltLenPos + 1 + 8);
|
|
27377
|
-
const derivationMethod = data[saltLenPos + 1 + 8] | data[saltLenPos + 1 + 8 + 1] << 8 | data[saltLenPos + 1 + 8 + 2] << 16 | data[saltLenPos + 1 + 8 + 3] << 24;
|
|
27378
|
-
results.push({ encryptedKey, salt, derivationMethod, iterations, position: pos });
|
|
27379
|
-
}
|
|
27380
|
-
}
|
|
27381
|
-
}
|
|
27382
|
-
}
|
|
27383
|
-
}
|
|
27384
|
-
return results;
|
|
27385
|
-
}
|
|
27386
|
-
function findWpkhDescriptor(data) {
|
|
27387
|
-
const descriptorPattern = new TextEncoder().encode("walletdescriptor");
|
|
27388
|
-
let descriptorIndex = 0;
|
|
27389
|
-
while ((descriptorIndex = findPattern(data, descriptorPattern, descriptorIndex)) !== -1) {
|
|
27390
|
-
let scanPos = descriptorIndex + descriptorPattern.length + 32;
|
|
27391
|
-
const descLen = data[scanPos];
|
|
27392
|
-
scanPos++;
|
|
27393
|
-
const descBytes = data.slice(scanPos, scanPos + Math.min(descLen, 200));
|
|
27394
|
-
let descStr = "";
|
|
27395
|
-
for (let i = 0; i < descBytes.length && descBytes[i] >= 32 && descBytes[i] <= 126; i++) {
|
|
27396
|
-
descStr += String.fromCharCode(descBytes[i]);
|
|
27397
|
-
}
|
|
27398
|
-
if (descStr.startsWith("wpkh(xpub") && descStr.includes("/0/*)")) {
|
|
27399
|
-
const xpubMatch = descStr.match(/xpub[1-9A-HJ-NP-Za-km-z]{100,}/);
|
|
27400
|
-
if (xpubMatch) {
|
|
27401
|
-
const descIdStart = descriptorIndex + descriptorPattern.length;
|
|
27402
|
-
const descriptorId = data.slice(descIdStart, descIdStart + 32);
|
|
27403
|
-
const pathMatch = descStr.match(/\[[\da-f]+\/(\d+'\/\d+'\/\d+')\]/);
|
|
27404
|
-
const descriptorPath = pathMatch ? pathMatch[1] : "84'/1'/0'";
|
|
27405
|
-
return {
|
|
27406
|
-
descriptorId,
|
|
27407
|
-
xpubString: xpubMatch[0],
|
|
27408
|
-
descriptorPath
|
|
27409
|
-
};
|
|
27410
|
-
}
|
|
27411
|
-
}
|
|
27412
|
-
descriptorIndex++;
|
|
27413
|
-
}
|
|
27414
|
-
return { descriptorId: null, xpubString: null, descriptorPath: null };
|
|
27415
|
-
}
|
|
27416
|
-
function extractChainCodeFromXpub(xpubString) {
|
|
27417
|
-
const decoded = base58Decode(xpubString);
|
|
27418
|
-
return bytesToHex4(decoded.slice(13, 45));
|
|
27419
|
-
}
|
|
27420
|
-
function findMasterChainCode(data) {
|
|
27421
|
-
const xpubPattern = new TextEncoder().encode("xpub");
|
|
27422
|
-
const base58Chars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
27423
|
-
let searchPos = 0;
|
|
27424
|
-
while (searchPos < data.length) {
|
|
27425
|
-
const xpubIndex = findPattern(data, xpubPattern, searchPos);
|
|
27426
|
-
if (xpubIndex === -1) break;
|
|
27427
|
-
let xpubStr = "xpub";
|
|
27428
|
-
let pos = xpubIndex + 4;
|
|
27429
|
-
while (pos < data.length && xpubStr.length < 120) {
|
|
27430
|
-
const char = String.fromCharCode(data[pos]);
|
|
27431
|
-
if (base58Chars.includes(char)) {
|
|
27432
|
-
xpubStr += char;
|
|
27433
|
-
pos++;
|
|
27434
|
-
} else {
|
|
27435
|
-
break;
|
|
27436
|
-
}
|
|
27437
|
-
}
|
|
27438
|
-
if (xpubStr.length > 100) {
|
|
27439
|
-
try {
|
|
27440
|
-
const decoded = base58Decode(xpubStr);
|
|
27441
|
-
const depth = decoded[4];
|
|
27442
|
-
if (depth === 0) {
|
|
27443
|
-
return bytesToHex4(decoded.slice(13, 45));
|
|
27444
|
-
}
|
|
27445
|
-
} catch {
|
|
27446
|
-
}
|
|
27447
|
-
}
|
|
27448
|
-
searchPos = xpubIndex + 4;
|
|
27449
|
-
}
|
|
27450
|
-
return null;
|
|
27451
|
-
}
|
|
27452
|
-
function extractDescriptorKeys(data) {
|
|
27453
|
-
const keys = [];
|
|
27454
|
-
const descriptorKeyPattern = new TextEncoder().encode("walletdescriptorkey");
|
|
27455
|
-
let index = 0;
|
|
27456
|
-
while ((index = findPattern(data, descriptorKeyPattern, index)) !== -1) {
|
|
27457
|
-
for (let checkPos = index + descriptorKeyPattern.length; checkPos < Math.min(index + descriptorKeyPattern.length + 200, data.length - 40); checkPos++) {
|
|
27458
|
-
if (data[checkPos] === 211 && data[checkPos + 1] === 2 && data[checkPos + 2] === 1 && data[checkPos + 3] === 1 && data[checkPos + 4] === 4 && data[checkPos + 5] === 32) {
|
|
27459
|
-
const privKey = data.slice(checkPos + 6, checkPos + 38);
|
|
27460
|
-
const privKeyHex = bytesToHex4(privKey);
|
|
27461
|
-
if (isValidPrivateKey(privKeyHex)) {
|
|
27462
|
-
keys.push(privKeyHex);
|
|
27463
|
-
break;
|
|
27464
|
-
}
|
|
27465
|
-
}
|
|
27466
|
-
}
|
|
27467
|
-
index++;
|
|
27468
|
-
}
|
|
27469
|
-
return keys;
|
|
27470
|
-
}
|
|
27471
|
-
function extractLegacyKeys(data) {
|
|
27472
|
-
const keys = [];
|
|
27473
|
-
const keyPattern = new TextEncoder().encode("key");
|
|
27474
|
-
let index = 0;
|
|
27475
|
-
while ((index = findPattern(data, keyPattern, index)) !== -1) {
|
|
27476
|
-
const searchPattern = new Uint8Array([4, 32]);
|
|
27477
|
-
for (let i = index; i < Math.min(index + 200, data.length - 34); i++) {
|
|
27478
|
-
if (data[i] === searchPattern[0] && data[i + 1] === searchPattern[1]) {
|
|
27479
|
-
const privKey = data.slice(i + 2, i + 34);
|
|
27480
|
-
const privKeyHex = bytesToHex4(privKey);
|
|
27481
|
-
if (isValidPrivateKey(privKeyHex)) {
|
|
27482
|
-
keys.push(privKeyHex);
|
|
27483
|
-
break;
|
|
27484
|
-
}
|
|
27485
|
-
}
|
|
27486
|
-
}
|
|
27487
|
-
index++;
|
|
27488
|
-
}
|
|
27489
|
-
return keys;
|
|
27490
|
-
}
|
|
27491
|
-
function findEncryptedKeyForDescriptor(data, descriptorId) {
|
|
27492
|
-
const ckeyPattern = new TextEncoder().encode("walletdescriptorckey");
|
|
27493
|
-
let ckeyIndex = findPattern(data, ckeyPattern, 0);
|
|
27494
|
-
while (ckeyIndex !== -1) {
|
|
27495
|
-
const recordDescId = data.slice(ckeyIndex + ckeyPattern.length, ckeyIndex + ckeyPattern.length + 32);
|
|
27496
|
-
if (Array.from(recordDescId).every((b, i) => b === descriptorId[i])) {
|
|
27497
|
-
let keyPos = ckeyIndex + ckeyPattern.length + 32;
|
|
27498
|
-
const pubkeyLen = data[keyPos];
|
|
27499
|
-
keyPos++;
|
|
27500
|
-
const pubkey = data.slice(keyPos, keyPos + pubkeyLen);
|
|
27501
|
-
for (let searchPos = keyPos + pubkeyLen; searchPos < Math.min(keyPos + pubkeyLen + 100, data.length - 50); searchPos++) {
|
|
27502
|
-
const valueLen = data[searchPos];
|
|
27503
|
-
if (valueLen >= 32 && valueLen <= 64) {
|
|
27504
|
-
const encryptedKey = data.slice(searchPos + 1, searchPos + 1 + valueLen);
|
|
27505
|
-
return { pubkey, encryptedKey };
|
|
27506
|
-
}
|
|
27507
|
-
}
|
|
27508
|
-
}
|
|
27509
|
-
ckeyIndex = findPattern(data, ckeyPattern, ckeyIndex + 1);
|
|
27510
|
-
}
|
|
27511
|
-
return null;
|
|
27512
|
-
}
|
|
27513
|
-
async function decryptCMasterKey(cmk, password, onProgress) {
|
|
27514
|
-
const { encryptedKey, salt, iterations } = cmk;
|
|
27515
|
-
const passwordHex = bytesToHex4(new TextEncoder().encode(password));
|
|
27516
|
-
const saltHex = bytesToHex4(salt);
|
|
27517
|
-
const inputHex = passwordHex + saltHex;
|
|
27518
|
-
let hash = CryptoJS4.SHA512(CryptoJS4.enc.Hex.parse(inputHex));
|
|
27519
|
-
const BATCH_SIZE = 1e3;
|
|
27520
|
-
for (let i = 0; i < iterations - 1; i++) {
|
|
27521
|
-
hash = CryptoJS4.SHA512(hash);
|
|
27522
|
-
if (onProgress && i % BATCH_SIZE === 0) {
|
|
27523
|
-
await onProgress(i, iterations);
|
|
27524
|
-
}
|
|
27525
|
-
}
|
|
27526
|
-
const derivedKey = CryptoJS4.lib.WordArray.create(hash.words.slice(0, 8));
|
|
27527
|
-
const derivedIv = CryptoJS4.lib.WordArray.create(hash.words.slice(8, 12));
|
|
27528
|
-
const encryptedWords = uint8ArrayToWordArray(encryptedKey);
|
|
27529
|
-
const decrypted = CryptoJS4.AES.decrypt(
|
|
27530
|
-
{ ciphertext: encryptedWords },
|
|
27531
|
-
derivedKey,
|
|
27532
|
-
{ iv: derivedIv, padding: CryptoJS4.pad.Pkcs7, mode: CryptoJS4.mode.CBC }
|
|
27533
|
-
);
|
|
27534
|
-
const result = CryptoJS4.enc.Hex.stringify(decrypted);
|
|
27535
|
-
if (!result || result.length !== 64) {
|
|
27536
|
-
throw new SphereError("Master key decryption failed - incorrect password", "DECRYPTION_ERROR");
|
|
27537
|
-
}
|
|
27538
|
-
return result;
|
|
27539
|
-
}
|
|
27540
|
-
function decryptPrivateKey(encryptedKey, pubkey, masterKeyHex) {
|
|
27541
|
-
const pubkeyWords = uint8ArrayToWordArray(pubkey);
|
|
27542
|
-
const pubkeyHashWords = CryptoJS4.SHA256(CryptoJS4.SHA256(pubkeyWords));
|
|
27543
|
-
const ivWords = CryptoJS4.lib.WordArray.create(pubkeyHashWords.words.slice(0, 4));
|
|
27544
|
-
const masterKeyWords = CryptoJS4.enc.Hex.parse(masterKeyHex);
|
|
27545
|
-
const encryptedWords = uint8ArrayToWordArray(encryptedKey);
|
|
27546
|
-
const decrypted = CryptoJS4.AES.decrypt(
|
|
27547
|
-
{ ciphertext: encryptedWords },
|
|
27548
|
-
masterKeyWords,
|
|
27549
|
-
{ iv: ivWords, padding: CryptoJS4.pad.Pkcs7, mode: CryptoJS4.mode.CBC }
|
|
27550
|
-
);
|
|
27551
|
-
return CryptoJS4.enc.Hex.stringify(decrypted);
|
|
27552
|
-
}
|
|
27553
|
-
function parseWalletDat(data) {
|
|
27554
|
-
if (!isSQLiteDatabase(data)) {
|
|
27555
|
-
return {
|
|
27556
|
-
success: false,
|
|
27557
|
-
error: "Invalid wallet.dat file - not an SQLite database"
|
|
27558
|
-
};
|
|
27559
|
-
}
|
|
27560
|
-
const isEncrypted = isWalletDatEncrypted(data);
|
|
27561
|
-
const cmasterKeys = isEncrypted ? findAllCMasterKeys(data) : [];
|
|
27562
|
-
const descriptorKeys = isEncrypted ? [] : extractDescriptorKeys(data);
|
|
27563
|
-
const legacyKeys = isEncrypted ? [] : extractLegacyKeys(data);
|
|
27564
|
-
const { descriptorId: _descriptorId, xpubString, descriptorPath } = findWpkhDescriptor(data);
|
|
27565
|
-
void _descriptorId;
|
|
27566
|
-
let chainCode = null;
|
|
27567
|
-
if (xpubString) {
|
|
27568
|
-
try {
|
|
27569
|
-
chainCode = extractChainCodeFromXpub(xpubString);
|
|
27570
|
-
} catch {
|
|
27571
|
-
chainCode = findMasterChainCode(data);
|
|
27572
|
-
}
|
|
27573
|
-
} else {
|
|
27574
|
-
chainCode = findMasterChainCode(data);
|
|
27575
|
-
}
|
|
27576
|
-
if (!isEncrypted) {
|
|
27577
|
-
let masterKey = null;
|
|
27578
|
-
if (descriptorKeys.length > 0) {
|
|
27579
|
-
masterKey = descriptorKeys[0];
|
|
27580
|
-
} else if (legacyKeys.length > 0) {
|
|
27581
|
-
masterKey = legacyKeys[0];
|
|
27582
|
-
}
|
|
27583
|
-
if (masterKey) {
|
|
27584
|
-
const parsedData = {
|
|
27585
|
-
masterKey,
|
|
27586
|
-
chainCode: chainCode ?? void 0,
|
|
27587
|
-
descriptorPath: descriptorPath ?? "84'/1'/0'",
|
|
27588
|
-
derivationMode: chainCode ? "bip32" : "wif_hmac"
|
|
26936
|
+
};
|
|
26937
|
+
}
|
|
26938
|
+
return {
|
|
26939
|
+
success: false,
|
|
26940
|
+
needsPassword: true,
|
|
26941
|
+
error: "Password required for encrypted wallet"
|
|
27589
26942
|
};
|
|
26943
|
+
}
|
|
26944
|
+
const masterKey = extractFromText(
|
|
26945
|
+
content,
|
|
26946
|
+
/MASTER PRIVATE KEY \(keep secret!\):\s*([^\n]+)/
|
|
26947
|
+
);
|
|
26948
|
+
if (!masterKey) {
|
|
27590
26949
|
return {
|
|
27591
|
-
success:
|
|
27592
|
-
|
|
26950
|
+
success: false,
|
|
26951
|
+
error: "Could not find master private key in backup file"
|
|
27593
26952
|
};
|
|
27594
26953
|
}
|
|
27595
|
-
|
|
27596
|
-
|
|
27597
|
-
|
|
27598
|
-
|
|
27599
|
-
|
|
27600
|
-
|
|
27601
|
-
|
|
27602
|
-
|
|
27603
|
-
|
|
26954
|
+
const chainCode = extractFromText(
|
|
26955
|
+
content,
|
|
26956
|
+
/MASTER CHAIN CODE \(for (?:BIP32 HD|Alpha) wallet compatibility\):\s*([^\n]+)/
|
|
26957
|
+
);
|
|
26958
|
+
const descriptorPath = extractFromText(content, /DESCRIPTOR PATH:\s*([^\n]+)/);
|
|
26959
|
+
const isBIP32 = content.includes("WALLET TYPE: BIP32 hierarchical deterministic wallet") || content.includes("WALLET TYPE: Alpha descriptor wallet") || !!chainCode;
|
|
26960
|
+
const effectiveDescriptorPath = descriptorPath ?? (isBIP32 ? "84'/1'/0'" : void 0);
|
|
26961
|
+
const data = {
|
|
26962
|
+
masterKey,
|
|
26963
|
+
chainCode: chainCode ?? void 0,
|
|
26964
|
+
descriptorPath: effectiveDescriptorPath,
|
|
26965
|
+
derivationMode: isBIP32 ? "bip32" : "wif_hmac"
|
|
27604
26966
|
};
|
|
27605
|
-
}
|
|
27606
|
-
const firstCmk = cmasterKeys[0];
|
|
27607
|
-
return {
|
|
27608
|
-
success: false,
|
|
27609
|
-
needsPassword: true,
|
|
27610
|
-
encryptionInfo: {
|
|
27611
|
-
iterations: firstCmk.iterations,
|
|
27612
|
-
salt: firstCmk.salt,
|
|
27613
|
-
encryptedKey: firstCmk.encryptedKey
|
|
27614
|
-
},
|
|
27615
|
-
error: "Password required for encrypted wallet"
|
|
27616
|
-
};
|
|
27617
|
-
}
|
|
27618
|
-
async function parseAndDecryptWalletDat(data, password, onProgress) {
|
|
27619
|
-
if (!isSQLiteDatabase(data)) {
|
|
27620
26967
|
return {
|
|
27621
|
-
success:
|
|
27622
|
-
|
|
26968
|
+
success: true,
|
|
26969
|
+
data
|
|
27623
26970
|
};
|
|
27624
|
-
}
|
|
27625
|
-
const isEncrypted = isWalletDatEncrypted(data);
|
|
27626
|
-
if (!isEncrypted) {
|
|
27627
|
-
return parseWalletDat(data);
|
|
27628
|
-
}
|
|
27629
|
-
const cmasterKeys = findAllCMasterKeys(data);
|
|
27630
|
-
if (cmasterKeys.length === 0) {
|
|
26971
|
+
} catch (e) {
|
|
27631
26972
|
return {
|
|
27632
26973
|
success: false,
|
|
27633
|
-
error:
|
|
26974
|
+
error: `Failed to parse wallet text: ${e instanceof Error ? e.message : String(e)}`
|
|
27634
26975
|
};
|
|
27635
26976
|
}
|
|
27636
|
-
|
|
27637
|
-
|
|
27638
|
-
|
|
27639
|
-
|
|
27640
|
-
|
|
27641
|
-
|
|
27642
|
-
}
|
|
27643
|
-
} catch {
|
|
27644
|
-
continue;
|
|
26977
|
+
}
|
|
26978
|
+
function parseAndDecryptWalletText(content, password) {
|
|
26979
|
+
try {
|
|
26980
|
+
const isEncrypted = isTextWalletEncrypted(content);
|
|
26981
|
+
if (!isEncrypted) {
|
|
26982
|
+
return parseWalletText(content);
|
|
27645
26983
|
}
|
|
27646
|
-
|
|
27647
|
-
|
|
27648
|
-
|
|
27649
|
-
|
|
27650
|
-
|
|
27651
|
-
|
|
27652
|
-
|
|
27653
|
-
|
|
27654
|
-
|
|
27655
|
-
if (xpubString) {
|
|
27656
|
-
try {
|
|
27657
|
-
chainCode = extractChainCodeFromXpub(xpubString);
|
|
27658
|
-
} catch {
|
|
27659
|
-
chainCode = findMasterChainCode(data);
|
|
26984
|
+
const encryptedKey = extractFromText(
|
|
26985
|
+
content,
|
|
26986
|
+
/ENCRYPTED MASTER KEY \(password protected\):\s*([^\n]+)/
|
|
26987
|
+
);
|
|
26988
|
+
if (!encryptedKey) {
|
|
26989
|
+
return {
|
|
26990
|
+
success: false,
|
|
26991
|
+
error: "Could not find encrypted master key in backup file"
|
|
26992
|
+
};
|
|
27660
26993
|
}
|
|
27661
|
-
|
|
27662
|
-
|
|
27663
|
-
|
|
27664
|
-
|
|
27665
|
-
|
|
27666
|
-
|
|
27667
|
-
|
|
26994
|
+
const masterKey = decryptTextFormatKey(encryptedKey, password);
|
|
26995
|
+
if (!masterKey) {
|
|
26996
|
+
return {
|
|
26997
|
+
success: false,
|
|
26998
|
+
error: "Failed to decrypt - incorrect password?"
|
|
26999
|
+
};
|
|
27000
|
+
}
|
|
27001
|
+
const chainCode = extractFromText(
|
|
27002
|
+
content,
|
|
27003
|
+
/MASTER CHAIN CODE \(for (?:BIP32 HD|Alpha) wallet compatibility\):\s*([^\n]+)/
|
|
27004
|
+
);
|
|
27005
|
+
const descriptorPath = extractFromText(content, /DESCRIPTOR PATH:\s*([^\n]+)/);
|
|
27006
|
+
const isBIP32 = content.includes("WALLET TYPE: BIP32 hierarchical deterministic wallet") || content.includes("WALLET TYPE: Alpha descriptor wallet") || !!chainCode;
|
|
27007
|
+
const effectiveDescriptorPath = descriptorPath ?? (isBIP32 ? "84'/1'/0'" : void 0);
|
|
27008
|
+
const data = {
|
|
27009
|
+
masterKey,
|
|
27010
|
+
chainCode: chainCode ?? void 0,
|
|
27011
|
+
descriptorPath: effectiveDescriptorPath,
|
|
27012
|
+
derivationMode: isBIP32 ? "bip32" : "wif_hmac"
|
|
27668
27013
|
};
|
|
27669
|
-
}
|
|
27670
|
-
const encryptedKeyData = findEncryptedKeyForDescriptor(data, descriptorId);
|
|
27671
|
-
if (!encryptedKeyData) {
|
|
27672
27014
|
return {
|
|
27673
|
-
success:
|
|
27674
|
-
|
|
27015
|
+
success: true,
|
|
27016
|
+
data
|
|
27675
27017
|
};
|
|
27676
|
-
}
|
|
27677
|
-
const bip32MasterKey = decryptPrivateKey(
|
|
27678
|
-
encryptedKeyData.encryptedKey,
|
|
27679
|
-
encryptedKeyData.pubkey,
|
|
27680
|
-
masterKeyHex
|
|
27681
|
-
);
|
|
27682
|
-
if (!bip32MasterKey || bip32MasterKey.length !== 64) {
|
|
27018
|
+
} catch (e) {
|
|
27683
27019
|
return {
|
|
27684
27020
|
success: false,
|
|
27685
|
-
error:
|
|
27021
|
+
error: `Failed to parse/decrypt wallet text: ${e instanceof Error ? e.message : String(e)}`
|
|
27686
27022
|
};
|
|
27687
27023
|
}
|
|
27688
|
-
const parsedData = {
|
|
27689
|
-
masterKey: bip32MasterKey,
|
|
27690
|
-
chainCode: chainCode ?? void 0,
|
|
27691
|
-
descriptorPath: descriptorPath ?? "84'/1'/0'",
|
|
27692
|
-
derivationMode: "bip32"
|
|
27693
|
-
};
|
|
27694
|
-
return {
|
|
27695
|
-
success: true,
|
|
27696
|
-
data: parsedData
|
|
27697
|
-
};
|
|
27698
27024
|
}
|
|
27699
27025
|
|
|
27700
27026
|
// core/Sphere.ts
|
|
@@ -28527,32 +27853,196 @@ var Sphere = class _Sphere {
|
|
|
28527
27853
|
if (this._transport.isConnected() && this._transport.fetchPendingEvents) {
|
|
28528
27854
|
await this._transport.fetchPendingEvents();
|
|
28529
27855
|
}
|
|
28530
|
-
}
|
|
28531
|
-
getAggregator() {
|
|
28532
|
-
return this._oracle;
|
|
27856
|
+
}
|
|
27857
|
+
getAggregator() {
|
|
27858
|
+
return this._oracle;
|
|
27859
|
+
}
|
|
27860
|
+
/**
|
|
27861
|
+
* Apply a new gateway API key to the LIVE oracle + token engine WITHOUT a full
|
|
27862
|
+
* Sphere rebuild (transport / realtime socket / discovery stay up). Use this to
|
|
27863
|
+
* inject a per-wallet subscription key provisioned AFTER init, or a per-address
|
|
27864
|
+
* key on an address switch, so the next money operation authenticates with it.
|
|
27865
|
+
*
|
|
27866
|
+
* Money-safety: the running token engine is snapshotted per-operation (the
|
|
27867
|
+
* payments module reads `deps.tokenEngine` into a local at op start), so an
|
|
27868
|
+
* in-flight send/mint completes on the OLD key (submit + proof stay paired on
|
|
27869
|
+
* one client) and only operations started AFTER this call use the new key.
|
|
27870
|
+
* Only the ACTIVE address's engine is rebuilt — other tracked addresses re-key
|
|
27871
|
+
* on their next switch/build. No-op-safe if the engine can't be built (the
|
|
27872
|
+
* modules fall back to their legacy path, same as init).
|
|
27873
|
+
*/
|
|
27874
|
+
async setOracleApiKey(apiKey) {
|
|
27875
|
+
this._oracle.setApiKey?.(apiKey);
|
|
27876
|
+
const active = this._addressModules.get(this._currentAddressIndex);
|
|
27877
|
+
const replaced = active?.tokenEngine ?? this._tokenEngine;
|
|
27878
|
+
this._tokenEngine = await this.buildTokenEngine();
|
|
27879
|
+
if (active) active.tokenEngine = this._tokenEngine;
|
|
27880
|
+
this._payments.setTokenEngine(this._tokenEngine);
|
|
27881
|
+
if (replaced && replaced !== this._tokenEngine) replaced.dispose?.();
|
|
27882
|
+
}
|
|
27883
|
+
/**
|
|
27884
|
+
* Import a wallet from a backup file: a `UNICITY WALLET DETAILS` text backup
|
|
27885
|
+
* (the format `exportToTxt` writes, optionally password-encrypted), a legacy
|
|
27886
|
+
* flat-JSON webwallet export, or a bare mnemonic in a text file.
|
|
27887
|
+
*
|
|
27888
|
+
* @example
|
|
27889
|
+
* const result = await Sphere.importFromLegacyFile({
|
|
27890
|
+
* fileContent: await file.text(),
|
|
27891
|
+
* fileName: file.name,
|
|
27892
|
+
* password, // when the backup is encrypted
|
|
27893
|
+
* ...providers,
|
|
27894
|
+
* });
|
|
27895
|
+
*/
|
|
27896
|
+
static async importFromLegacyFile(options) {
|
|
27897
|
+
const { fileContent, fileName, password, onDecryptProgress, ...baseOptions } = options;
|
|
27898
|
+
const fileType = _Sphere.detectLegacyFileType(fileName, fileContent);
|
|
27899
|
+
if (fileType === "unknown") {
|
|
27900
|
+
return { success: false, error: "Unknown file format" };
|
|
27901
|
+
}
|
|
27902
|
+
if (fileType === "mnemonic") {
|
|
27903
|
+
const mnemonic = fileContent.trim().toLowerCase().split(/\s+/).join(" ");
|
|
27904
|
+
if (!_Sphere.validateMnemonic(mnemonic)) {
|
|
27905
|
+
return { success: false, error: "Invalid mnemonic phrase" };
|
|
27906
|
+
}
|
|
27907
|
+
const sphere = await _Sphere.import({ ...baseOptions, mnemonic });
|
|
27908
|
+
return { success: true, sphere, mnemonic };
|
|
27909
|
+
}
|
|
27910
|
+
if (fileType === "txt") {
|
|
27911
|
+
const content = fileContent;
|
|
27912
|
+
let parseResult;
|
|
27913
|
+
if (password) {
|
|
27914
|
+
parseResult = parseAndDecryptWalletText(content, password);
|
|
27915
|
+
} else if (isTextWalletEncrypted(content)) {
|
|
27916
|
+
return { success: false, needsPassword: true, error: "Password required for encrypted wallet" };
|
|
27917
|
+
} else {
|
|
27918
|
+
parseResult = parseWalletText(content);
|
|
27919
|
+
}
|
|
27920
|
+
if (parseResult.needsPassword && !password) {
|
|
27921
|
+
return { success: false, needsPassword: true, error: "Password required for encrypted wallet" };
|
|
27922
|
+
}
|
|
27923
|
+
if (!parseResult.success || !parseResult.data) {
|
|
27924
|
+
return { success: false, error: parseResult.error };
|
|
27925
|
+
}
|
|
27926
|
+
const { masterKey, chainCode, descriptorPath, derivationMode } = parseResult.data;
|
|
27927
|
+
const basePath = descriptorPath ? `m/${descriptorPath}` : DEFAULT_BASE_PATH;
|
|
27928
|
+
const sphere = await _Sphere.import({
|
|
27929
|
+
...baseOptions,
|
|
27930
|
+
masterKey,
|
|
27931
|
+
chainCode,
|
|
27932
|
+
basePath,
|
|
27933
|
+
derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac")
|
|
27934
|
+
});
|
|
27935
|
+
return { success: true, sphere };
|
|
27936
|
+
}
|
|
27937
|
+
if (fileType === "json") {
|
|
27938
|
+
const content = fileContent;
|
|
27939
|
+
let parsed;
|
|
27940
|
+
try {
|
|
27941
|
+
parsed = JSON.parse(content);
|
|
27942
|
+
} catch {
|
|
27943
|
+
return { success: false, error: "Invalid JSON file" };
|
|
27944
|
+
}
|
|
27945
|
+
if (parsed.type === "sphere-wallet") {
|
|
27946
|
+
const result = await _Sphere.importFromJSON({
|
|
27947
|
+
...baseOptions,
|
|
27948
|
+
jsonContent: content,
|
|
27949
|
+
password
|
|
27950
|
+
});
|
|
27951
|
+
if (result.success) {
|
|
27952
|
+
const sphere2 = _Sphere.getInstance();
|
|
27953
|
+
return { success: true, sphere: sphere2, mnemonic: result.mnemonic };
|
|
27954
|
+
}
|
|
27955
|
+
if (!password && result.error?.includes("Password required")) {
|
|
27956
|
+
return { success: false, needsPassword: true, error: result.error };
|
|
27957
|
+
}
|
|
27958
|
+
return { success: false, error: result.error };
|
|
27959
|
+
}
|
|
27960
|
+
let masterKey;
|
|
27961
|
+
let mnemonic;
|
|
27962
|
+
if (parsed.encrypted && typeof parsed.encrypted === "object") {
|
|
27963
|
+
if (!password) {
|
|
27964
|
+
return { success: false, needsPassword: true, error: "Password required for encrypted wallet" };
|
|
27965
|
+
}
|
|
27966
|
+
const enc = parsed.encrypted;
|
|
27967
|
+
if (!enc.salt || !enc.masterPrivateKey) {
|
|
27968
|
+
return { success: false, error: "Invalid encrypted wallet format" };
|
|
27969
|
+
}
|
|
27970
|
+
const decryptedKey = decryptWithSalt(enc.masterPrivateKey, password, enc.salt);
|
|
27971
|
+
if (!decryptedKey) {
|
|
27972
|
+
return { success: false, error: "Failed to decrypt - incorrect password?" };
|
|
27973
|
+
}
|
|
27974
|
+
masterKey = decryptedKey;
|
|
27975
|
+
if (enc.mnemonic) {
|
|
27976
|
+
mnemonic = decryptWithSalt(enc.mnemonic, password, enc.salt) ?? void 0;
|
|
27977
|
+
}
|
|
27978
|
+
} else {
|
|
27979
|
+
masterKey = parsed.masterPrivateKey;
|
|
27980
|
+
mnemonic = parsed.mnemonic;
|
|
27981
|
+
}
|
|
27982
|
+
if (!masterKey) {
|
|
27983
|
+
return { success: false, error: "No master key found in wallet JSON" };
|
|
27984
|
+
}
|
|
27985
|
+
const chainCode = parsed.chainCode;
|
|
27986
|
+
const descriptorPath = parsed.descriptorPath;
|
|
27987
|
+
const derivationMode = parsed.derivationMode;
|
|
27988
|
+
const isBIP32 = derivationMode === "bip32" || !!chainCode;
|
|
27989
|
+
const basePath = descriptorPath ? `m/${descriptorPath}` : isBIP32 ? "m/84'/1'/0'" : DEFAULT_BASE_PATH;
|
|
27990
|
+
if (mnemonic) {
|
|
27991
|
+
const sphere2 = await _Sphere.import({ ...baseOptions, mnemonic, basePath });
|
|
27992
|
+
return { success: true, sphere: sphere2, mnemonic };
|
|
27993
|
+
}
|
|
27994
|
+
const sphere = await _Sphere.import({
|
|
27995
|
+
...baseOptions,
|
|
27996
|
+
masterKey,
|
|
27997
|
+
chainCode,
|
|
27998
|
+
basePath,
|
|
27999
|
+
derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac")
|
|
28000
|
+
});
|
|
28001
|
+
return { success: true, sphere };
|
|
28002
|
+
}
|
|
28003
|
+
return { success: false, error: "Unsupported file type" };
|
|
28004
|
+
}
|
|
28005
|
+
/**
|
|
28006
|
+
* Detect legacy file type from filename and content
|
|
28007
|
+
*/
|
|
28008
|
+
static detectLegacyFileType(fileName, content) {
|
|
28009
|
+
if (fileName.endsWith(".json")) {
|
|
28010
|
+
return "json";
|
|
28011
|
+
}
|
|
28012
|
+
try {
|
|
28013
|
+
const trimmed = content.trim();
|
|
28014
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
28015
|
+
JSON.parse(trimmed);
|
|
28016
|
+
return "json";
|
|
28017
|
+
}
|
|
28018
|
+
} catch {
|
|
28019
|
+
}
|
|
28020
|
+
const words = content.trim().split(/\s+/);
|
|
28021
|
+
if ((words.length === 12 || words.length === 24) && words.every((w) => /^[a-z]+$/.test(w.toLowerCase()))) {
|
|
28022
|
+
return "mnemonic";
|
|
28023
|
+
}
|
|
28024
|
+
if (isWalletTextFormat(content)) {
|
|
28025
|
+
return "txt";
|
|
28026
|
+
}
|
|
28027
|
+
return "unknown";
|
|
28533
28028
|
}
|
|
28534
28029
|
/**
|
|
28535
|
-
*
|
|
28536
|
-
* Sphere rebuild (transport / realtime socket / discovery stay up). Use this to
|
|
28537
|
-
* inject a per-wallet subscription key provisioned AFTER init, or a per-address
|
|
28538
|
-
* key on an address switch, so the next money operation authenticates with it.
|
|
28539
|
-
*
|
|
28540
|
-
* Money-safety: the running token engine is snapshotted per-operation (the
|
|
28541
|
-
* payments module reads `deps.tokenEngine` into a local at op start), so an
|
|
28542
|
-
* in-flight send/mint completes on the OLD key (submit + proof stay paired on
|
|
28543
|
-
* one client) and only operations started AFTER this call use the new key.
|
|
28544
|
-
* Only the ACTIVE address's engine is rebuilt — other tracked addresses re-key
|
|
28545
|
-
* on their next switch/build. No-op-safe if the engine can't be built (the
|
|
28546
|
-
* modules fall back to their legacy path, same as init).
|
|
28030
|
+
* Check if a legacy file is encrypted
|
|
28547
28031
|
*/
|
|
28548
|
-
|
|
28549
|
-
|
|
28550
|
-
|
|
28551
|
-
|
|
28552
|
-
|
|
28553
|
-
if (
|
|
28554
|
-
|
|
28555
|
-
|
|
28032
|
+
static isLegacyFileEncrypted(fileName, content) {
|
|
28033
|
+
const fileType = _Sphere.detectLegacyFileType(fileName, content);
|
|
28034
|
+
if (fileType === "txt") {
|
|
28035
|
+
return isTextWalletEncrypted(content);
|
|
28036
|
+
}
|
|
28037
|
+
if (fileType === "json") {
|
|
28038
|
+
try {
|
|
28039
|
+
const data = JSON.parse(content);
|
|
28040
|
+
return !!data.encrypted;
|
|
28041
|
+
} catch {
|
|
28042
|
+
return false;
|
|
28043
|
+
}
|
|
28044
|
+
}
|
|
28045
|
+
return false;
|
|
28556
28046
|
}
|
|
28557
28047
|
/**
|
|
28558
28048
|
* Check if wallet has BIP32 master key for HD derivation
|
|
@@ -28824,224 +28314,6 @@ var Sphere = class _Sphere {
|
|
|
28824
28314
|
};
|
|
28825
28315
|
}
|
|
28826
28316
|
}
|
|
28827
|
-
/**
|
|
28828
|
-
* Import wallet from legacy file (.dat, .txt, or mnemonic text)
|
|
28829
|
-
*
|
|
28830
|
-
* Supports:
|
|
28831
|
-
* - Bitcoin Core wallet.dat files (SQLite format, encrypted or unencrypted)
|
|
28832
|
-
* - Text backup files (UNICITY WALLET DETAILS format)
|
|
28833
|
-
* - Plain mnemonic text (12 or 24 words)
|
|
28834
|
-
*
|
|
28835
|
-
* @returns Object with success status, created Sphere instance, and optionally recovered mnemonic
|
|
28836
|
-
*
|
|
28837
|
-
* @example
|
|
28838
|
-
* ```ts
|
|
28839
|
-
* // Import from .dat file
|
|
28840
|
-
* const fileBuffer = await file.arrayBuffer();
|
|
28841
|
-
* const result = await Sphere.importFromLegacyFile({
|
|
28842
|
-
* fileContent: new Uint8Array(fileBuffer),
|
|
28843
|
-
* fileName: 'wallet.dat',
|
|
28844
|
-
* password: 'wallet-password', // if encrypted
|
|
28845
|
-
* storage, transport, oracle,
|
|
28846
|
-
* });
|
|
28847
|
-
*
|
|
28848
|
-
* // Import from .txt file
|
|
28849
|
-
* const textContent = await file.text();
|
|
28850
|
-
* const result = await Sphere.importFromLegacyFile({
|
|
28851
|
-
* fileContent: textContent,
|
|
28852
|
-
* fileName: 'backup.txt',
|
|
28853
|
-
* storage, transport, oracle,
|
|
28854
|
-
* });
|
|
28855
|
-
* ```
|
|
28856
|
-
*/
|
|
28857
|
-
static async importFromLegacyFile(options) {
|
|
28858
|
-
const { fileContent, fileName, password, onDecryptProgress, ...baseOptions } = options;
|
|
28859
|
-
const fileType = _Sphere.detectLegacyFileType(fileName, fileContent);
|
|
28860
|
-
if (fileType === "unknown") {
|
|
28861
|
-
return { success: false, error: "Unknown file format" };
|
|
28862
|
-
}
|
|
28863
|
-
if (fileType === "mnemonic") {
|
|
28864
|
-
const mnemonic = fileContent.trim().toLowerCase().split(/\s+/).join(" ");
|
|
28865
|
-
if (!_Sphere.validateMnemonic(mnemonic)) {
|
|
28866
|
-
return { success: false, error: "Invalid mnemonic phrase" };
|
|
28867
|
-
}
|
|
28868
|
-
const sphere = await _Sphere.import({ ...baseOptions, mnemonic });
|
|
28869
|
-
return { success: true, sphere, mnemonic };
|
|
28870
|
-
}
|
|
28871
|
-
if (fileType === "dat") {
|
|
28872
|
-
const data = fileContent instanceof Uint8Array ? fileContent : new TextEncoder().encode(fileContent);
|
|
28873
|
-
let parseResult;
|
|
28874
|
-
if (password) {
|
|
28875
|
-
parseResult = await parseAndDecryptWalletDat(data, password, onDecryptProgress);
|
|
28876
|
-
} else {
|
|
28877
|
-
parseResult = parseWalletDat(data);
|
|
28878
|
-
}
|
|
28879
|
-
if (parseResult.needsPassword && !password) {
|
|
28880
|
-
return { success: false, needsPassword: true, error: "Password required for encrypted wallet" };
|
|
28881
|
-
}
|
|
28882
|
-
if (!parseResult.success || !parseResult.data) {
|
|
28883
|
-
return { success: false, error: parseResult.error };
|
|
28884
|
-
}
|
|
28885
|
-
const { masterKey, chainCode, descriptorPath, derivationMode } = parseResult.data;
|
|
28886
|
-
const basePath = descriptorPath ? `m/${descriptorPath}` : DEFAULT_BASE_PATH;
|
|
28887
|
-
const sphere = await _Sphere.import({
|
|
28888
|
-
...baseOptions,
|
|
28889
|
-
masterKey,
|
|
28890
|
-
chainCode,
|
|
28891
|
-
basePath,
|
|
28892
|
-
derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac")
|
|
28893
|
-
});
|
|
28894
|
-
return { success: true, sphere };
|
|
28895
|
-
}
|
|
28896
|
-
if (fileType === "txt") {
|
|
28897
|
-
const content = typeof fileContent === "string" ? fileContent : new TextDecoder().decode(fileContent);
|
|
28898
|
-
let parseResult;
|
|
28899
|
-
if (password) {
|
|
28900
|
-
parseResult = parseAndDecryptWalletText(content, password);
|
|
28901
|
-
} else if (isTextWalletEncrypted(content)) {
|
|
28902
|
-
return { success: false, needsPassword: true, error: "Password required for encrypted wallet" };
|
|
28903
|
-
} else {
|
|
28904
|
-
parseResult = parseWalletText(content);
|
|
28905
|
-
}
|
|
28906
|
-
if (parseResult.needsPassword && !password) {
|
|
28907
|
-
return { success: false, needsPassword: true, error: "Password required for encrypted wallet" };
|
|
28908
|
-
}
|
|
28909
|
-
if (!parseResult.success || !parseResult.data) {
|
|
28910
|
-
return { success: false, error: parseResult.error };
|
|
28911
|
-
}
|
|
28912
|
-
const { masterKey, chainCode, descriptorPath, derivationMode } = parseResult.data;
|
|
28913
|
-
const basePath = descriptorPath ? `m/${descriptorPath}` : DEFAULT_BASE_PATH;
|
|
28914
|
-
const sphere = await _Sphere.import({
|
|
28915
|
-
...baseOptions,
|
|
28916
|
-
masterKey,
|
|
28917
|
-
chainCode,
|
|
28918
|
-
basePath,
|
|
28919
|
-
derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac")
|
|
28920
|
-
});
|
|
28921
|
-
return { success: true, sphere };
|
|
28922
|
-
}
|
|
28923
|
-
if (fileType === "json") {
|
|
28924
|
-
const content = typeof fileContent === "string" ? fileContent : new TextDecoder().decode(fileContent);
|
|
28925
|
-
let parsed;
|
|
28926
|
-
try {
|
|
28927
|
-
parsed = JSON.parse(content);
|
|
28928
|
-
} catch {
|
|
28929
|
-
return { success: false, error: "Invalid JSON file" };
|
|
28930
|
-
}
|
|
28931
|
-
if (parsed.type === "sphere-wallet") {
|
|
28932
|
-
const result = await _Sphere.importFromJSON({
|
|
28933
|
-
...baseOptions,
|
|
28934
|
-
jsonContent: content,
|
|
28935
|
-
password
|
|
28936
|
-
});
|
|
28937
|
-
if (result.success) {
|
|
28938
|
-
const sphere2 = _Sphere.getInstance();
|
|
28939
|
-
return { success: true, sphere: sphere2, mnemonic: result.mnemonic };
|
|
28940
|
-
}
|
|
28941
|
-
if (!password && result.error?.includes("Password required")) {
|
|
28942
|
-
return { success: false, needsPassword: true, error: result.error };
|
|
28943
|
-
}
|
|
28944
|
-
return { success: false, error: result.error };
|
|
28945
|
-
}
|
|
28946
|
-
let masterKey;
|
|
28947
|
-
let mnemonic;
|
|
28948
|
-
if (parsed.encrypted && typeof parsed.encrypted === "object") {
|
|
28949
|
-
if (!password) {
|
|
28950
|
-
return { success: false, needsPassword: true, error: "Password required for encrypted wallet" };
|
|
28951
|
-
}
|
|
28952
|
-
const enc = parsed.encrypted;
|
|
28953
|
-
if (!enc.salt || !enc.masterPrivateKey) {
|
|
28954
|
-
return { success: false, error: "Invalid encrypted wallet format" };
|
|
28955
|
-
}
|
|
28956
|
-
const decryptedKey = decryptWithSalt(enc.masterPrivateKey, password, enc.salt);
|
|
28957
|
-
if (!decryptedKey) {
|
|
28958
|
-
return { success: false, error: "Failed to decrypt - incorrect password?" };
|
|
28959
|
-
}
|
|
28960
|
-
masterKey = decryptedKey;
|
|
28961
|
-
if (enc.mnemonic) {
|
|
28962
|
-
mnemonic = decryptWithSalt(enc.mnemonic, password, enc.salt) ?? void 0;
|
|
28963
|
-
}
|
|
28964
|
-
} else {
|
|
28965
|
-
masterKey = parsed.masterPrivateKey;
|
|
28966
|
-
mnemonic = parsed.mnemonic;
|
|
28967
|
-
}
|
|
28968
|
-
if (!masterKey) {
|
|
28969
|
-
return { success: false, error: "No master key found in wallet JSON" };
|
|
28970
|
-
}
|
|
28971
|
-
const chainCode = parsed.chainCode;
|
|
28972
|
-
const descriptorPath = parsed.descriptorPath;
|
|
28973
|
-
const derivationMode = parsed.derivationMode;
|
|
28974
|
-
const isBIP32 = derivationMode === "bip32" || !!chainCode;
|
|
28975
|
-
const basePath = descriptorPath ? `m/${descriptorPath}` : isBIP32 ? "m/84'/1'/0'" : DEFAULT_BASE_PATH;
|
|
28976
|
-
if (mnemonic) {
|
|
28977
|
-
const sphere2 = await _Sphere.import({ ...baseOptions, mnemonic, basePath });
|
|
28978
|
-
return { success: true, sphere: sphere2, mnemonic };
|
|
28979
|
-
}
|
|
28980
|
-
const sphere = await _Sphere.import({
|
|
28981
|
-
...baseOptions,
|
|
28982
|
-
masterKey,
|
|
28983
|
-
chainCode,
|
|
28984
|
-
basePath,
|
|
28985
|
-
derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac")
|
|
28986
|
-
});
|
|
28987
|
-
return { success: true, sphere };
|
|
28988
|
-
}
|
|
28989
|
-
return { success: false, error: "Unsupported file type" };
|
|
28990
|
-
}
|
|
28991
|
-
/**
|
|
28992
|
-
* Detect legacy file type from filename and content
|
|
28993
|
-
*/
|
|
28994
|
-
static detectLegacyFileType(fileName, content) {
|
|
28995
|
-
if (fileName.endsWith(".dat")) {
|
|
28996
|
-
return "dat";
|
|
28997
|
-
}
|
|
28998
|
-
const textContent = typeof content === "string" ? content : content.length < 1e3 ? new TextDecoder().decode(content) : "";
|
|
28999
|
-
if (fileName.endsWith(".json")) {
|
|
29000
|
-
return "json";
|
|
29001
|
-
}
|
|
29002
|
-
try {
|
|
29003
|
-
const trimmed = textContent.trim();
|
|
29004
|
-
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
29005
|
-
JSON.parse(trimmed);
|
|
29006
|
-
return "json";
|
|
29007
|
-
}
|
|
29008
|
-
} catch {
|
|
29009
|
-
}
|
|
29010
|
-
const words = textContent.trim().split(/\s+/);
|
|
29011
|
-
if ((words.length === 12 || words.length === 24) && words.every((w) => /^[a-z]+$/.test(w.toLowerCase()))) {
|
|
29012
|
-
return "mnemonic";
|
|
29013
|
-
}
|
|
29014
|
-
if (isWalletTextFormat(textContent)) {
|
|
29015
|
-
return "txt";
|
|
29016
|
-
}
|
|
29017
|
-
if (content instanceof Uint8Array && isSQLiteDatabase(content)) {
|
|
29018
|
-
return "dat";
|
|
29019
|
-
}
|
|
29020
|
-
return "unknown";
|
|
29021
|
-
}
|
|
29022
|
-
/**
|
|
29023
|
-
* Check if a legacy file is encrypted
|
|
29024
|
-
*/
|
|
29025
|
-
static isLegacyFileEncrypted(fileName, content) {
|
|
29026
|
-
const fileType = _Sphere.detectLegacyFileType(fileName, content);
|
|
29027
|
-
if (fileType === "dat" && content instanceof Uint8Array) {
|
|
29028
|
-
return isWalletDatEncrypted(content);
|
|
29029
|
-
}
|
|
29030
|
-
if (fileType === "txt") {
|
|
29031
|
-
const textContent = typeof content === "string" ? content : new TextDecoder().decode(content);
|
|
29032
|
-
return isTextWalletEncrypted(textContent);
|
|
29033
|
-
}
|
|
29034
|
-
if (fileType === "json") {
|
|
29035
|
-
try {
|
|
29036
|
-
const textContent = typeof content === "string" ? content : new TextDecoder().decode(content);
|
|
29037
|
-
const data = JSON.parse(textContent);
|
|
29038
|
-
return !!data.encrypted;
|
|
29039
|
-
} catch {
|
|
29040
|
-
return false;
|
|
29041
|
-
}
|
|
29042
|
-
}
|
|
29043
|
-
return false;
|
|
29044
|
-
}
|
|
29045
28317
|
/**
|
|
29046
28318
|
* Get the current active address index
|
|
29047
28319
|
*
|
|
@@ -30946,116 +30218,6 @@ function formatAmount(amount, options = {}) {
|
|
|
30946
30218
|
return symbol ? `${readable} ${symbol}` : readable;
|
|
30947
30219
|
}
|
|
30948
30220
|
|
|
30949
|
-
// core/bech32.ts
|
|
30950
|
-
var CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
|
30951
|
-
var GENERATOR = [996825010, 642813549, 513874426, 1027748829, 705979059];
|
|
30952
|
-
function convertBits(data, fromBits, toBits, pad) {
|
|
30953
|
-
let acc = 0;
|
|
30954
|
-
let bits = 0;
|
|
30955
|
-
const ret = [];
|
|
30956
|
-
const maxv = (1 << toBits) - 1;
|
|
30957
|
-
for (let i = 0; i < data.length; i++) {
|
|
30958
|
-
const value = data[i];
|
|
30959
|
-
if (value < 0 || value >> fromBits !== 0) return null;
|
|
30960
|
-
acc = acc << fromBits | value;
|
|
30961
|
-
bits += fromBits;
|
|
30962
|
-
while (bits >= toBits) {
|
|
30963
|
-
bits -= toBits;
|
|
30964
|
-
ret.push(acc >> bits & maxv);
|
|
30965
|
-
}
|
|
30966
|
-
}
|
|
30967
|
-
if (pad) {
|
|
30968
|
-
if (bits > 0) {
|
|
30969
|
-
ret.push(acc << toBits - bits & maxv);
|
|
30970
|
-
}
|
|
30971
|
-
} else if (bits >= fromBits || acc << toBits - bits & maxv) {
|
|
30972
|
-
return null;
|
|
30973
|
-
}
|
|
30974
|
-
return ret;
|
|
30975
|
-
}
|
|
30976
|
-
function hrpExpand(hrp) {
|
|
30977
|
-
const ret = [];
|
|
30978
|
-
for (let i = 0; i < hrp.length; i++) ret.push(hrp.charCodeAt(i) >> 5);
|
|
30979
|
-
ret.push(0);
|
|
30980
|
-
for (let i = 0; i < hrp.length; i++) ret.push(hrp.charCodeAt(i) & 31);
|
|
30981
|
-
return ret;
|
|
30982
|
-
}
|
|
30983
|
-
function bech32Polymod(values) {
|
|
30984
|
-
let chk = 1;
|
|
30985
|
-
for (let p = 0; p < values.length; p++) {
|
|
30986
|
-
const top = chk >> 25;
|
|
30987
|
-
chk = (chk & 33554431) << 5 ^ values[p];
|
|
30988
|
-
for (let i = 0; i < 5; i++) {
|
|
30989
|
-
if (top >> i & 1) chk ^= GENERATOR[i];
|
|
30990
|
-
}
|
|
30991
|
-
}
|
|
30992
|
-
return chk;
|
|
30993
|
-
}
|
|
30994
|
-
function bech32Checksum(hrp, data) {
|
|
30995
|
-
const values = hrpExpand(hrp).concat(data).concat([0, 0, 0, 0, 0, 0]);
|
|
30996
|
-
const mod2 = bech32Polymod(values) ^ 1;
|
|
30997
|
-
const ret = [];
|
|
30998
|
-
for (let p = 0; p < 6; p++) {
|
|
30999
|
-
ret.push(mod2 >> 5 * (5 - p) & 31);
|
|
31000
|
-
}
|
|
31001
|
-
return ret;
|
|
31002
|
-
}
|
|
31003
|
-
function encodeBech32(hrp, version, program) {
|
|
31004
|
-
if (version < 0 || version > 16) {
|
|
31005
|
-
throw new SphereError("Invalid witness version", "VALIDATION_ERROR");
|
|
31006
|
-
}
|
|
31007
|
-
const converted = convertBits(Array.from(program), 8, 5, true);
|
|
31008
|
-
if (!converted) {
|
|
31009
|
-
throw new SphereError("Failed to convert bits", "VALIDATION_ERROR");
|
|
31010
|
-
}
|
|
31011
|
-
const data = [version].concat(converted);
|
|
31012
|
-
const checksum = bech32Checksum(hrp, data);
|
|
31013
|
-
const combined = data.concat(checksum);
|
|
31014
|
-
let out = hrp + "1";
|
|
31015
|
-
for (let i = 0; i < combined.length; i++) {
|
|
31016
|
-
out += CHARSET[combined[i]];
|
|
31017
|
-
}
|
|
31018
|
-
return out;
|
|
31019
|
-
}
|
|
31020
|
-
function decodeBech32(addr) {
|
|
31021
|
-
addr = addr.toLowerCase();
|
|
31022
|
-
const pos = addr.lastIndexOf("1");
|
|
31023
|
-
if (pos < 1) return null;
|
|
31024
|
-
const hrp = addr.substring(0, pos);
|
|
31025
|
-
const dataStr = addr.substring(pos + 1);
|
|
31026
|
-
const data = [];
|
|
31027
|
-
for (let i = 0; i < dataStr.length; i++) {
|
|
31028
|
-
const val = CHARSET.indexOf(dataStr[i]);
|
|
31029
|
-
if (val === -1) return null;
|
|
31030
|
-
data.push(val);
|
|
31031
|
-
}
|
|
31032
|
-
const checksum = bech32Checksum(hrp, data.slice(0, -6));
|
|
31033
|
-
for (let i = 0; i < 6; i++) {
|
|
31034
|
-
if (checksum[i] !== data[data.length - 6 + i]) {
|
|
31035
|
-
return null;
|
|
31036
|
-
}
|
|
31037
|
-
}
|
|
31038
|
-
const version = data[0];
|
|
31039
|
-
const program = convertBits(data.slice(1, -6), 5, 8, false);
|
|
31040
|
-
if (!program) return null;
|
|
31041
|
-
return {
|
|
31042
|
-
hrp,
|
|
31043
|
-
witnessVersion: version,
|
|
31044
|
-
data: Uint8Array.from(program)
|
|
31045
|
-
};
|
|
31046
|
-
}
|
|
31047
|
-
function createAddress(hrp, pubkeyHash) {
|
|
31048
|
-
const hashBytes = typeof pubkeyHash === "string" ? Uint8Array.from(Buffer.from(pubkeyHash, "hex")) : pubkeyHash;
|
|
31049
|
-
return encodeBech32(hrp, 1, hashBytes);
|
|
31050
|
-
}
|
|
31051
|
-
function isValidBech32(addr) {
|
|
31052
|
-
return decodeBech32(addr) !== null;
|
|
31053
|
-
}
|
|
31054
|
-
function getAddressHrp(addr) {
|
|
31055
|
-
const result = decodeBech32(addr);
|
|
31056
|
-
return result?.hrp ?? null;
|
|
31057
|
-
}
|
|
31058
|
-
|
|
31059
30221
|
// core/network-health.ts
|
|
31060
30222
|
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
31061
30223
|
async function checkNetworkHealth(network = "testnet", options) {
|
|
@@ -31240,7 +30402,7 @@ async function runCustomCheck(name, checkFn, timeoutMs) {
|
|
|
31240
30402
|
|
|
31241
30403
|
// storage/whole-blob-inventory-adapter.ts
|
|
31242
30404
|
var TOKEN_KEY_RE = /^_[0-9a-f]{4,}$/i;
|
|
31243
|
-
function
|
|
30405
|
+
function bytesToHex5(bytes) {
|
|
31244
30406
|
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
31245
30407
|
}
|
|
31246
30408
|
function hexToBytes6(hex) {
|
|
@@ -31350,7 +30512,7 @@ var WholeBlobInventoryAdapter = class {
|
|
|
31350
30512
|
if (isV2TokenEntry2(entry)) {
|
|
31351
30513
|
try {
|
|
31352
30514
|
const blob = decodeTokenBlob(hexToBytes6(entry.sdkData));
|
|
31353
|
-
stateHash =
|
|
30515
|
+
stateHash = bytesToHex5(sha256(blob.token));
|
|
31354
30516
|
} catch {
|
|
31355
30517
|
}
|
|
31356
30518
|
}
|
|
@@ -31375,7 +30537,7 @@ function hexToBytes7(hex) {
|
|
|
31375
30537
|
for (let i = 0; i < out.length; i++) out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
31376
30538
|
return out;
|
|
31377
30539
|
}
|
|
31378
|
-
function
|
|
30540
|
+
function bytesToHex6(bytes) {
|
|
31379
30541
|
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
31380
30542
|
}
|
|
31381
30543
|
function computeDeliveryId(tokenIdHex, stateHashHex) {
|
|
@@ -31384,7 +30546,7 @@ function computeDeliveryId(tokenIdHex, stateHashHex) {
|
|
|
31384
30546
|
const joined = new Uint8Array(tokenId.length + stateHash.length);
|
|
31385
30547
|
joined.set(tokenId, 0);
|
|
31386
30548
|
joined.set(stateHash, tokenId.length);
|
|
31387
|
-
return
|
|
30549
|
+
return bytesToHex6(sha256(joined));
|
|
31388
30550
|
}
|
|
31389
30551
|
function composeDeliveryKeys(keys) {
|
|
31390
30552
|
return { ...keys, deliveryId: computeDeliveryId(keys.tokenId, keys.stateHash) };
|
|
@@ -31794,7 +30956,6 @@ export {
|
|
|
31794
30956
|
WalletApiError,
|
|
31795
30957
|
WholeBlobInventoryAdapter,
|
|
31796
30958
|
addressesMatch,
|
|
31797
|
-
archivedKeyFromTokenId,
|
|
31798
30959
|
areSameNametag,
|
|
31799
30960
|
assertFieldEnvelopeShape,
|
|
31800
30961
|
base58Decode,
|
|
@@ -31807,8 +30968,6 @@ export {
|
|
|
31807
30968
|
composeDeliveryKeys,
|
|
31808
30969
|
computeDeliveryId,
|
|
31809
30970
|
computeSwapId,
|
|
31810
|
-
countCommittedTransactions,
|
|
31811
|
-
createAddress,
|
|
31812
30971
|
createCommunicationsModule,
|
|
31813
30972
|
createGroupChatModule,
|
|
31814
30973
|
createKeyPair,
|
|
@@ -31819,15 +30978,12 @@ export {
|
|
|
31819
30978
|
createSphere,
|
|
31820
30979
|
createSwapModule,
|
|
31821
30980
|
createTokenValidator,
|
|
31822
|
-
decodeBech32,
|
|
31823
30981
|
decrypt,
|
|
31824
|
-
decryptCMasterKey,
|
|
31825
30982
|
decryptField,
|
|
31826
30983
|
decryptFieldBytes,
|
|
31827
30984
|
decryptJson,
|
|
31828
30985
|
decryptMnemonic,
|
|
31829
30986
|
decryptNametag2 as decryptNametag,
|
|
31830
|
-
decryptPrivateKey,
|
|
31831
30987
|
decryptSimple,
|
|
31832
30988
|
decryptTextFormatKey,
|
|
31833
30989
|
decryptWithSalt,
|
|
@@ -31836,7 +30992,6 @@ export {
|
|
|
31836
30992
|
deriveFieldEncryptionKey,
|
|
31837
30993
|
deriveKeyAtPath,
|
|
31838
30994
|
doubleSha256,
|
|
31839
|
-
encodeBech32,
|
|
31840
30995
|
encrypt,
|
|
31841
30996
|
encryptField,
|
|
31842
30997
|
encryptFieldBytes,
|
|
@@ -31845,28 +31000,21 @@ export {
|
|
|
31845
31000
|
encryptSimple,
|
|
31846
31001
|
extractFromText,
|
|
31847
31002
|
findPattern,
|
|
31848
|
-
forkedKeyFromTokenIdAndState,
|
|
31849
31003
|
formatAmount,
|
|
31850
31004
|
generateAddressFromMasterKey,
|
|
31851
31005
|
generateMasterKey,
|
|
31852
31006
|
generateMnemonic2 as generateMnemonic,
|
|
31853
|
-
getAddressHrp,
|
|
31854
31007
|
getAddressId,
|
|
31855
31008
|
getAddressStorageKey,
|
|
31856
31009
|
getCoinIdByName,
|
|
31857
31010
|
getCoinIdBySymbol,
|
|
31858
|
-
getCurrentStateHash,
|
|
31859
31011
|
getPublicKey,
|
|
31860
31012
|
getSphere,
|
|
31861
31013
|
getTokenDecimals,
|
|
31862
31014
|
getTokenDefinition,
|
|
31863
31015
|
getTokenIconUrl,
|
|
31864
|
-
getTokenId,
|
|
31865
31016
|
getTokenName,
|
|
31866
31017
|
getTokenSymbol,
|
|
31867
|
-
hasMissingNewStateHash,
|
|
31868
|
-
hasUncommittedTransactions,
|
|
31869
|
-
hasValidTxfData,
|
|
31870
31018
|
hash160,
|
|
31871
31019
|
hashAddressForTag,
|
|
31872
31020
|
hashNametag,
|
|
@@ -31874,22 +31022,17 @@ export {
|
|
|
31874
31022
|
hexToBytes4 as hexToBytes,
|
|
31875
31023
|
identityFromMnemonicSync,
|
|
31876
31024
|
initSphere,
|
|
31877
|
-
isArchivedKey,
|
|
31878
|
-
isForkedKey,
|
|
31879
31025
|
isKnownToken,
|
|
31880
31026
|
isPhoneNumber2 as isPhoneNumber,
|
|
31881
31027
|
isPossiblyCommittedSendOutcome,
|
|
31882
|
-
isSQLiteDatabase,
|
|
31883
31028
|
isSphereError,
|
|
31884
31029
|
isTextWalletEncrypted,
|
|
31885
31030
|
isTokenKey,
|
|
31886
31031
|
isValidAddress,
|
|
31887
|
-
isValidBech32,
|
|
31888
31032
|
isValidDirectAddress,
|
|
31889
31033
|
isValidNametag2 as isValidNametag,
|
|
31890
31034
|
isValidPrivateKey,
|
|
31891
31035
|
isValidTokenId,
|
|
31892
|
-
isWalletDatEncrypted,
|
|
31893
31036
|
isWalletTextFormat,
|
|
31894
31037
|
keyFromTokenId,
|
|
31895
31038
|
loadSphere,
|
|
@@ -31898,15 +31041,10 @@ export {
|
|
|
31898
31041
|
normalizeAddress,
|
|
31899
31042
|
normalizeCoinId,
|
|
31900
31043
|
normalizeNametag2 as normalizeNametag,
|
|
31901
|
-
normalizeSdkTokenToStorage,
|
|
31902
|
-
objectToTxf,
|
|
31903
31044
|
parseAddress,
|
|
31904
|
-
parseAndDecryptWalletDat,
|
|
31905
31045
|
parseAndDecryptWalletText,
|
|
31906
|
-
parseForkedKey,
|
|
31907
31046
|
parseTokenAmount,
|
|
31908
31047
|
parseTxfStorageData,
|
|
31909
|
-
parseWalletDat,
|
|
31910
31048
|
parseWalletText,
|
|
31911
31049
|
randomBytes4 as randomBytes,
|
|
31912
31050
|
randomHex,
|
|
@@ -31920,10 +31058,7 @@ export {
|
|
|
31920
31058
|
sleep,
|
|
31921
31059
|
sphereExists,
|
|
31922
31060
|
toHumanReadable,
|
|
31923
|
-
tokenIdFromArchivedKey,
|
|
31924
31061
|
tokenIdFromKey,
|
|
31925
|
-
tokenToTxf,
|
|
31926
|
-
txfToToken,
|
|
31927
31062
|
validateManifest,
|
|
31928
31063
|
validateMnemonic2 as validateMnemonic,
|
|
31929
31064
|
verifyChallengeTemplate,
|