@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/core/index.cjs
CHANGED
|
@@ -30,7 +30,6 @@ var __toCommonJS = (mod2) => __copyProps(__defProp({}, "__esModule", { value: tr
|
|
|
30
30
|
// core/index.ts
|
|
31
31
|
var core_exports = {};
|
|
32
32
|
__export(core_exports, {
|
|
33
|
-
CHARSET: () => CHARSET,
|
|
34
33
|
CurrencyUtils: () => CurrencyUtils,
|
|
35
34
|
DEFAULT_DERIVATION_PATH: () => DEFAULT_DERIVATION_PATH2,
|
|
36
35
|
DEFAULT_TOKEN_DECIMALS: () => DEFAULT_TOKEN_DECIMALS,
|
|
@@ -48,11 +47,8 @@ __export(core_exports, {
|
|
|
48
47
|
base58Encode: () => base58Encode,
|
|
49
48
|
bytesToHex: () => bytesToHex4,
|
|
50
49
|
checkNetworkHealth: () => checkNetworkHealth,
|
|
51
|
-
convertBits: () => convertBits,
|
|
52
|
-
createAddress: () => createAddress,
|
|
53
50
|
createKeyPair: () => createKeyPair,
|
|
54
51
|
createSphere: () => createSphere,
|
|
55
|
-
decodeBech32: () => decodeBech32,
|
|
56
52
|
decrypt: () => decrypt,
|
|
57
53
|
decryptDeliveryBundle: () => decryptDeliveryBundle,
|
|
58
54
|
decryptField: () => decryptField,
|
|
@@ -69,7 +65,6 @@ __export(core_exports, {
|
|
|
69
65
|
deserializeEncrypted: () => deserializeEncrypted,
|
|
70
66
|
discoverAddressesImpl: () => discoverAddressesImpl,
|
|
71
67
|
doubleSha256: () => doubleSha256,
|
|
72
|
-
encodeBech32: () => encodeBech32,
|
|
73
68
|
encrypt: () => encrypt,
|
|
74
69
|
encryptDeliveryBundle: () => encryptDeliveryBundle,
|
|
75
70
|
encryptField: () => encryptField,
|
|
@@ -85,7 +80,6 @@ __export(core_exports, {
|
|
|
85
80
|
generateMasterKey: () => generateMasterKey,
|
|
86
81
|
generateMnemonic: () => generateMnemonic2,
|
|
87
82
|
generateRandomKey: () => generateRandomKey,
|
|
88
|
-
getAddressHrp: () => getAddressHrp,
|
|
89
83
|
getPublicKey: () => getPublicKey,
|
|
90
84
|
getSphere: () => getSphere,
|
|
91
85
|
hash160: () => hash160,
|
|
@@ -98,7 +92,6 @@ __export(core_exports, {
|
|
|
98
92
|
isEncryptedData: () => isEncryptedData,
|
|
99
93
|
isPossiblyCommittedSendOutcome: () => isPossiblyCommittedSendOutcome,
|
|
100
94
|
isSphereError: () => isSphereError,
|
|
101
|
-
isValidBech32: () => isValidBech32,
|
|
102
95
|
isValidNametag: () => isValidNametag2,
|
|
103
96
|
isValidPrivateKey: () => isValidPrivateKey,
|
|
104
97
|
loadSphere: () => loadSphere,
|
|
@@ -8930,17 +8923,9 @@ function decryptDeliveryBundle(key, envelope) {
|
|
|
8930
8923
|
}
|
|
8931
8924
|
|
|
8932
8925
|
// types/txf.ts
|
|
8933
|
-
var ARCHIVED_PREFIX = "archived-";
|
|
8934
|
-
var FORKED_PREFIX = "_forked_";
|
|
8935
8926
|
var RESERVED_KEYS = ["_meta", "_nametag", "_nametags", "_tombstones", "_invalidatedNametags", "_outbox", "_mintOutbox", "_sent", "_invalid", "_integrity", "_history"];
|
|
8936
8927
|
function isTokenKey(key) {
|
|
8937
|
-
return key.startsWith("_") && !
|
|
8938
|
-
}
|
|
8939
|
-
function isArchivedKey(key) {
|
|
8940
|
-
return key.startsWith(ARCHIVED_PREFIX);
|
|
8941
|
-
}
|
|
8942
|
-
function isForkedKey(key) {
|
|
8943
|
-
return key.startsWith(FORKED_PREFIX);
|
|
8928
|
+
return key.startsWith("_") && !RESERVED_KEYS.includes(key);
|
|
8944
8929
|
}
|
|
8945
8930
|
function tokenIdFromKey(key) {
|
|
8946
8931
|
return key.startsWith("_") ? key.substring(1) : key;
|
|
@@ -8948,24 +8933,109 @@ function tokenIdFromKey(key) {
|
|
|
8948
8933
|
function keyFromTokenId(tokenId) {
|
|
8949
8934
|
return `_${tokenId}`;
|
|
8950
8935
|
}
|
|
8951
|
-
|
|
8952
|
-
|
|
8936
|
+
|
|
8937
|
+
// serialization/txf-serializer.ts
|
|
8938
|
+
function isV2TokenBlob(sdkData) {
|
|
8939
|
+
return typeof sdkData === "string" && sdkData.length >= 2 && sdkData.length % 2 === 0 && sdkData[0] !== "{" && /^[0-9a-f]+$/i.test(sdkData);
|
|
8953
8940
|
}
|
|
8954
|
-
function
|
|
8955
|
-
return
|
|
8941
|
+
function v2TokenId(token) {
|
|
8942
|
+
return token.id.startsWith("v2_") ? token.id.slice(3) : token.id;
|
|
8956
8943
|
}
|
|
8957
|
-
function
|
|
8958
|
-
return
|
|
8944
|
+
function isV2TokenEntry(entry) {
|
|
8945
|
+
return typeof entry === "object" && entry !== null && !("genesis" in entry) && isV2TokenBlob(entry.sdkData);
|
|
8959
8946
|
}
|
|
8960
|
-
function
|
|
8961
|
-
|
|
8962
|
-
|
|
8963
|
-
|
|
8964
|
-
|
|
8965
|
-
|
|
8966
|
-
|
|
8967
|
-
|
|
8947
|
+
async function buildTxfStorageData(tokens, meta, options) {
|
|
8948
|
+
const storageData = {
|
|
8949
|
+
_meta: {
|
|
8950
|
+
...meta,
|
|
8951
|
+
formatVersion: "2.0"
|
|
8952
|
+
}
|
|
8953
|
+
};
|
|
8954
|
+
if (options?.nametags && options.nametags.length > 0) {
|
|
8955
|
+
storageData._nametags = options.nametags;
|
|
8956
|
+
}
|
|
8957
|
+
if (options?.tombstones && options.tombstones.length > 0) {
|
|
8958
|
+
storageData._tombstones = options.tombstones;
|
|
8959
|
+
}
|
|
8960
|
+
if (options?.historyEntries && options.historyEntries.length > 0) {
|
|
8961
|
+
storageData._history = options.historyEntries;
|
|
8962
|
+
}
|
|
8963
|
+
for (const token of tokens) {
|
|
8964
|
+
if (isV2TokenBlob(token.sdkData)) {
|
|
8965
|
+
storageData[keyFromTokenId(v2TokenId(token))] = token;
|
|
8966
|
+
} else {
|
|
8967
|
+
logger.warn(
|
|
8968
|
+
"TXF",
|
|
8969
|
+
`Refusing to persist token ${token.id.slice(0, 16)}...: sdkData is not a v2 token blob \u2014 entry skipped`
|
|
8970
|
+
);
|
|
8971
|
+
}
|
|
8972
|
+
}
|
|
8973
|
+
return storageData;
|
|
8974
|
+
}
|
|
8975
|
+
function parseTxfStorageData(data) {
|
|
8976
|
+
const result = {
|
|
8977
|
+
tokens: [],
|
|
8978
|
+
meta: null,
|
|
8979
|
+
nametags: [],
|
|
8980
|
+
tombstones: [],
|
|
8981
|
+
historyEntries: [],
|
|
8982
|
+
validationErrors: []
|
|
8968
8983
|
};
|
|
8984
|
+
if (!data || typeof data !== "object") {
|
|
8985
|
+
result.validationErrors.push("Storage data is not an object");
|
|
8986
|
+
return result;
|
|
8987
|
+
}
|
|
8988
|
+
const storageData = data;
|
|
8989
|
+
if (storageData._meta && typeof storageData._meta === "object") {
|
|
8990
|
+
result.meta = storageData._meta;
|
|
8991
|
+
}
|
|
8992
|
+
const seenNames = /* @__PURE__ */ new Set();
|
|
8993
|
+
if (Array.isArray(storageData._nametags)) {
|
|
8994
|
+
for (const entry of storageData._nametags) {
|
|
8995
|
+
if (entry && typeof entry === "object" && typeof entry.name === "string") {
|
|
8996
|
+
result.nametags.push(entry);
|
|
8997
|
+
seenNames.add(entry.name);
|
|
8998
|
+
}
|
|
8999
|
+
}
|
|
9000
|
+
}
|
|
9001
|
+
if (storageData._nametag && typeof storageData._nametag === "object") {
|
|
9002
|
+
const legacy = storageData._nametag;
|
|
9003
|
+
if (typeof legacy.name === "string" && !seenNames.has(legacy.name)) {
|
|
9004
|
+
result.nametags.push(legacy);
|
|
9005
|
+
}
|
|
9006
|
+
}
|
|
9007
|
+
if (storageData._tombstones && Array.isArray(storageData._tombstones)) {
|
|
9008
|
+
for (const entry of storageData._tombstones) {
|
|
9009
|
+
if (typeof entry === "object" && entry !== null && typeof entry.tokenId === "string" && typeof entry.stateHash === "string" && typeof entry.timestamp === "number") {
|
|
9010
|
+
result.tombstones.push(entry);
|
|
9011
|
+
}
|
|
9012
|
+
}
|
|
9013
|
+
}
|
|
9014
|
+
if (Array.isArray(storageData._history)) {
|
|
9015
|
+
for (const entry of storageData._history) {
|
|
9016
|
+
if (typeof entry === "object" && entry !== null && typeof entry.dedupKey === "string" && typeof entry.type === "string") {
|
|
9017
|
+
result.historyEntries.push(entry);
|
|
9018
|
+
}
|
|
9019
|
+
}
|
|
9020
|
+
}
|
|
9021
|
+
for (const key of Object.keys(storageData)) {
|
|
9022
|
+
if (isTokenKey(key)) {
|
|
9023
|
+
const tokenId = tokenIdFromKey(key);
|
|
9024
|
+
const entry = storageData[key];
|
|
9025
|
+
if (isV2TokenEntry(entry)) {
|
|
9026
|
+
result.tokens.push(entry);
|
|
9027
|
+
} else {
|
|
9028
|
+
const msg = `Token ${tokenId}: unrecognized storage entry (not a v2 token blob) \u2014 entry skipped`;
|
|
9029
|
+
logger.warn("TXF", msg);
|
|
9030
|
+
result.validationErrors.push(msg);
|
|
9031
|
+
}
|
|
9032
|
+
} else if (key.startsWith("token-")) {
|
|
9033
|
+
const msg = `Token ${key}: sdkData is not a v2 token blob \u2014 entry skipped`;
|
|
9034
|
+
logger.warn("TXF", msg);
|
|
9035
|
+
result.validationErrors.push(msg);
|
|
9036
|
+
}
|
|
9037
|
+
}
|
|
9038
|
+
return result;
|
|
8969
9039
|
}
|
|
8970
9040
|
|
|
8971
9041
|
// registry/TokenRegistry.ts
|
|
@@ -9395,334 +9465,6 @@ function coinIdsMatch(a, b) {
|
|
|
9395
9465
|
return normalizeCoinId(a) === normalizeCoinId(b);
|
|
9396
9466
|
}
|
|
9397
9467
|
|
|
9398
|
-
// serialization/txf-serializer.ts
|
|
9399
|
-
function bytesToHex5(bytes) {
|
|
9400
|
-
const arr = Array.isArray(bytes) ? bytes : Array.from(bytes);
|
|
9401
|
-
return arr.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
9402
|
-
}
|
|
9403
|
-
function normalizeToHex(value) {
|
|
9404
|
-
if (typeof value === "string") {
|
|
9405
|
-
return value;
|
|
9406
|
-
}
|
|
9407
|
-
if (value && typeof value === "object") {
|
|
9408
|
-
const obj = value;
|
|
9409
|
-
if ("bytes" in obj && (Array.isArray(obj.bytes) || obj.bytes instanceof Uint8Array)) {
|
|
9410
|
-
return bytesToHex5(obj.bytes);
|
|
9411
|
-
}
|
|
9412
|
-
if (obj.type === "Buffer" && Array.isArray(obj.data)) {
|
|
9413
|
-
return bytesToHex5(obj.data);
|
|
9414
|
-
}
|
|
9415
|
-
}
|
|
9416
|
-
return String(value);
|
|
9417
|
-
}
|
|
9418
|
-
function normalizeSdkTokenToStorage(sdkTokenJson) {
|
|
9419
|
-
const txf = structuredClone(sdkTokenJson);
|
|
9420
|
-
if (txf.genesis?.data) {
|
|
9421
|
-
const data = txf.genesis.data;
|
|
9422
|
-
if (data.tokenId !== void 0) {
|
|
9423
|
-
data.tokenId = normalizeToHex(data.tokenId);
|
|
9424
|
-
}
|
|
9425
|
-
if (data.tokenType !== void 0) {
|
|
9426
|
-
data.tokenType = normalizeToHex(data.tokenType);
|
|
9427
|
-
}
|
|
9428
|
-
if (data.salt !== void 0) {
|
|
9429
|
-
data.salt = normalizeToHex(data.salt);
|
|
9430
|
-
}
|
|
9431
|
-
}
|
|
9432
|
-
if (txf.genesis?.inclusionProof?.authenticator) {
|
|
9433
|
-
const auth = txf.genesis.inclusionProof.authenticator;
|
|
9434
|
-
if (auth.publicKey !== void 0) {
|
|
9435
|
-
auth.publicKey = normalizeToHex(auth.publicKey);
|
|
9436
|
-
}
|
|
9437
|
-
if (auth.signature !== void 0) {
|
|
9438
|
-
auth.signature = normalizeToHex(auth.signature);
|
|
9439
|
-
}
|
|
9440
|
-
}
|
|
9441
|
-
if (Array.isArray(txf.transactions)) {
|
|
9442
|
-
for (const tx of txf.transactions) {
|
|
9443
|
-
if (tx.inclusionProof?.authenticator) {
|
|
9444
|
-
const auth = tx.inclusionProof.authenticator;
|
|
9445
|
-
if (auth.publicKey !== void 0) {
|
|
9446
|
-
auth.publicKey = normalizeToHex(auth.publicKey);
|
|
9447
|
-
}
|
|
9448
|
-
if (auth.signature !== void 0) {
|
|
9449
|
-
auth.signature = normalizeToHex(auth.signature);
|
|
9450
|
-
}
|
|
9451
|
-
}
|
|
9452
|
-
}
|
|
9453
|
-
}
|
|
9454
|
-
return txf;
|
|
9455
|
-
}
|
|
9456
|
-
function tokenToTxf(token) {
|
|
9457
|
-
const jsonData = token.sdkData;
|
|
9458
|
-
if (!jsonData) {
|
|
9459
|
-
return null;
|
|
9460
|
-
}
|
|
9461
|
-
try {
|
|
9462
|
-
const txfData = normalizeSdkTokenToStorage(JSON.parse(jsonData));
|
|
9463
|
-
if (!txfData.genesis || !txfData.state) {
|
|
9464
|
-
return null;
|
|
9465
|
-
}
|
|
9466
|
-
if (!txfData.version) {
|
|
9467
|
-
txfData.version = "2.0";
|
|
9468
|
-
}
|
|
9469
|
-
if (!txfData.transactions) {
|
|
9470
|
-
txfData.transactions = [];
|
|
9471
|
-
}
|
|
9472
|
-
if (!txfData.nametags) {
|
|
9473
|
-
txfData.nametags = [];
|
|
9474
|
-
}
|
|
9475
|
-
if (!txfData._integrity) {
|
|
9476
|
-
txfData._integrity = {
|
|
9477
|
-
genesisDataJSONHash: "0000" + "0".repeat(60)
|
|
9478
|
-
};
|
|
9479
|
-
}
|
|
9480
|
-
return txfData;
|
|
9481
|
-
} catch {
|
|
9482
|
-
return null;
|
|
9483
|
-
}
|
|
9484
|
-
}
|
|
9485
|
-
function determineTokenStatus(txf) {
|
|
9486
|
-
if (txf.transactions.length > 0) {
|
|
9487
|
-
const lastTx = txf.transactions[txf.transactions.length - 1];
|
|
9488
|
-
if (lastTx.inclusionProof === null) {
|
|
9489
|
-
return "pending";
|
|
9490
|
-
}
|
|
9491
|
-
}
|
|
9492
|
-
return "confirmed";
|
|
9493
|
-
}
|
|
9494
|
-
function txfToToken(tokenId, txf) {
|
|
9495
|
-
const coinData = txf.genesis.data.coinData;
|
|
9496
|
-
const totalAmount = coinData.reduce((sum, [, amt]) => {
|
|
9497
|
-
return sum + BigInt(amt || "0");
|
|
9498
|
-
}, BigInt(0));
|
|
9499
|
-
let coinId = coinData[0]?.[0] || "";
|
|
9500
|
-
for (const [cid, amt] of coinData) {
|
|
9501
|
-
if (BigInt(amt || "0") > 0) {
|
|
9502
|
-
coinId = cid;
|
|
9503
|
-
break;
|
|
9504
|
-
}
|
|
9505
|
-
}
|
|
9506
|
-
const tokenType = txf.genesis.data.tokenType;
|
|
9507
|
-
const isNft = tokenType === "455ad8720656b08e8dbd5bac1f3c73eeea5431565f6c1c3af742b1aa12d41d89";
|
|
9508
|
-
const now = Date.now();
|
|
9509
|
-
const registry = TokenRegistry.getInstance();
|
|
9510
|
-
const def = registry.getDefinition(coinId);
|
|
9511
|
-
return {
|
|
9512
|
-
id: tokenId,
|
|
9513
|
-
coinId,
|
|
9514
|
-
symbol: isNft ? "NFT" : def?.symbol || coinId.slice(0, 8),
|
|
9515
|
-
name: isNft ? "NFT" : def?.name ? def.name.charAt(0).toUpperCase() + def.name.slice(1) : "Token",
|
|
9516
|
-
decimals: isNft ? 0 : def?.decimals ?? 8,
|
|
9517
|
-
amount: totalAmount.toString(),
|
|
9518
|
-
status: determineTokenStatus(txf),
|
|
9519
|
-
createdAt: now,
|
|
9520
|
-
updatedAt: now,
|
|
9521
|
-
sdkData: JSON.stringify(txf)
|
|
9522
|
-
};
|
|
9523
|
-
}
|
|
9524
|
-
function isV2TokenBlob(sdkData) {
|
|
9525
|
-
return typeof sdkData === "string" && sdkData.length >= 2 && sdkData.length % 2 === 0 && sdkData[0] !== "{" && /^[0-9a-f]+$/i.test(sdkData);
|
|
9526
|
-
}
|
|
9527
|
-
function v2TokenId(token) {
|
|
9528
|
-
return token.id.startsWith("v2_") ? token.id.slice(3) : token.id;
|
|
9529
|
-
}
|
|
9530
|
-
function isV2TokenEntry(entry) {
|
|
9531
|
-
return typeof entry === "object" && entry !== null && !("genesis" in entry) && isV2TokenBlob(entry.sdkData);
|
|
9532
|
-
}
|
|
9533
|
-
async function buildTxfStorageData(tokens, meta, options) {
|
|
9534
|
-
const storageData = {
|
|
9535
|
-
_meta: {
|
|
9536
|
-
...meta,
|
|
9537
|
-
formatVersion: "2.0"
|
|
9538
|
-
}
|
|
9539
|
-
};
|
|
9540
|
-
if (options?.nametags && options.nametags.length > 0) {
|
|
9541
|
-
storageData._nametags = options.nametags;
|
|
9542
|
-
}
|
|
9543
|
-
if (options?.tombstones && options.tombstones.length > 0) {
|
|
9544
|
-
storageData._tombstones = options.tombstones;
|
|
9545
|
-
}
|
|
9546
|
-
if (options?.outboxEntries && options.outboxEntries.length > 0) {
|
|
9547
|
-
storageData._outbox = options.outboxEntries;
|
|
9548
|
-
}
|
|
9549
|
-
if (options?.mintOutboxEntries && options.mintOutboxEntries.length > 0) {
|
|
9550
|
-
storageData._mintOutbox = options.mintOutboxEntries;
|
|
9551
|
-
}
|
|
9552
|
-
if (options?.invalidatedNametags && options.invalidatedNametags.length > 0) {
|
|
9553
|
-
storageData._invalidatedNametags = options.invalidatedNametags;
|
|
9554
|
-
}
|
|
9555
|
-
if (options?.historyEntries && options.historyEntries.length > 0) {
|
|
9556
|
-
storageData._history = options.historyEntries;
|
|
9557
|
-
}
|
|
9558
|
-
for (const token of tokens) {
|
|
9559
|
-
const txf = tokenToTxf(token);
|
|
9560
|
-
if (txf) {
|
|
9561
|
-
const actualTokenId = txf.genesis.data.tokenId;
|
|
9562
|
-
storageData[keyFromTokenId(actualTokenId)] = txf;
|
|
9563
|
-
} else if (isV2TokenBlob(token.sdkData)) {
|
|
9564
|
-
storageData[keyFromTokenId(v2TokenId(token))] = token;
|
|
9565
|
-
}
|
|
9566
|
-
}
|
|
9567
|
-
if (options?.archivedTokens && options.archivedTokens.size > 0) {
|
|
9568
|
-
for (const [tokenId, txf] of options.archivedTokens) {
|
|
9569
|
-
storageData[archivedKeyFromTokenId(tokenId)] = txf;
|
|
9570
|
-
}
|
|
9571
|
-
}
|
|
9572
|
-
if (options?.forkedTokens && options.forkedTokens.size > 0) {
|
|
9573
|
-
for (const [key, txf] of options.forkedTokens) {
|
|
9574
|
-
const [tokenId, stateHash] = key.split("_");
|
|
9575
|
-
if (tokenId && stateHash) {
|
|
9576
|
-
storageData[forkedKeyFromTokenIdAndState(tokenId, stateHash)] = txf;
|
|
9577
|
-
}
|
|
9578
|
-
}
|
|
9579
|
-
}
|
|
9580
|
-
return storageData;
|
|
9581
|
-
}
|
|
9582
|
-
function parseTxfStorageData(data) {
|
|
9583
|
-
const result = {
|
|
9584
|
-
tokens: [],
|
|
9585
|
-
meta: null,
|
|
9586
|
-
nametags: [],
|
|
9587
|
-
tombstones: [],
|
|
9588
|
-
archivedTokens: /* @__PURE__ */ new Map(),
|
|
9589
|
-
forkedTokens: /* @__PURE__ */ new Map(),
|
|
9590
|
-
outboxEntries: [],
|
|
9591
|
-
mintOutboxEntries: [],
|
|
9592
|
-
invalidatedNametags: [],
|
|
9593
|
-
historyEntries: [],
|
|
9594
|
-
validationErrors: []
|
|
9595
|
-
};
|
|
9596
|
-
if (!data || typeof data !== "object") {
|
|
9597
|
-
result.validationErrors.push("Storage data is not an object");
|
|
9598
|
-
return result;
|
|
9599
|
-
}
|
|
9600
|
-
const storageData = data;
|
|
9601
|
-
if (storageData._meta && typeof storageData._meta === "object") {
|
|
9602
|
-
result.meta = storageData._meta;
|
|
9603
|
-
}
|
|
9604
|
-
const seenNames = /* @__PURE__ */ new Set();
|
|
9605
|
-
if (Array.isArray(storageData._nametags)) {
|
|
9606
|
-
for (const entry of storageData._nametags) {
|
|
9607
|
-
if (entry && typeof entry === "object" && typeof entry.name === "string") {
|
|
9608
|
-
result.nametags.push(entry);
|
|
9609
|
-
seenNames.add(entry.name);
|
|
9610
|
-
}
|
|
9611
|
-
}
|
|
9612
|
-
}
|
|
9613
|
-
if (storageData._nametag && typeof storageData._nametag === "object") {
|
|
9614
|
-
const legacy = storageData._nametag;
|
|
9615
|
-
if (typeof legacy.name === "string" && !seenNames.has(legacy.name)) {
|
|
9616
|
-
result.nametags.push(legacy);
|
|
9617
|
-
}
|
|
9618
|
-
}
|
|
9619
|
-
if (storageData._tombstones && Array.isArray(storageData._tombstones)) {
|
|
9620
|
-
for (const entry of storageData._tombstones) {
|
|
9621
|
-
if (typeof entry === "object" && entry !== null && typeof entry.tokenId === "string" && typeof entry.stateHash === "string" && typeof entry.timestamp === "number") {
|
|
9622
|
-
result.tombstones.push(entry);
|
|
9623
|
-
}
|
|
9624
|
-
}
|
|
9625
|
-
}
|
|
9626
|
-
if (storageData._outbox && Array.isArray(storageData._outbox)) {
|
|
9627
|
-
for (const entry of storageData._outbox) {
|
|
9628
|
-
if (typeof entry === "object" && entry !== null && typeof entry.id === "string" && typeof entry.status === "string") {
|
|
9629
|
-
result.outboxEntries.push(entry);
|
|
9630
|
-
}
|
|
9631
|
-
}
|
|
9632
|
-
}
|
|
9633
|
-
if (storageData._mintOutbox && Array.isArray(storageData._mintOutbox)) {
|
|
9634
|
-
for (const entry of storageData._mintOutbox) {
|
|
9635
|
-
if (typeof entry === "object" && entry !== null && typeof entry.id === "string" && typeof entry.status === "string") {
|
|
9636
|
-
result.mintOutboxEntries.push(entry);
|
|
9637
|
-
}
|
|
9638
|
-
}
|
|
9639
|
-
}
|
|
9640
|
-
if (storageData._invalidatedNametags && Array.isArray(storageData._invalidatedNametags)) {
|
|
9641
|
-
for (const entry of storageData._invalidatedNametags) {
|
|
9642
|
-
if (typeof entry === "object" && entry !== null && typeof entry.name === "string") {
|
|
9643
|
-
result.invalidatedNametags.push(entry);
|
|
9644
|
-
}
|
|
9645
|
-
}
|
|
9646
|
-
}
|
|
9647
|
-
if (Array.isArray(storageData._history)) {
|
|
9648
|
-
for (const entry of storageData._history) {
|
|
9649
|
-
if (typeof entry === "object" && entry !== null && typeof entry.dedupKey === "string" && typeof entry.type === "string") {
|
|
9650
|
-
result.historyEntries.push(entry);
|
|
9651
|
-
}
|
|
9652
|
-
}
|
|
9653
|
-
}
|
|
9654
|
-
for (const key of Object.keys(storageData)) {
|
|
9655
|
-
if (isTokenKey(key)) {
|
|
9656
|
-
const tokenId = tokenIdFromKey(key);
|
|
9657
|
-
try {
|
|
9658
|
-
const txfToken = storageData[key];
|
|
9659
|
-
if (txfToken?.genesis?.data?.tokenId) {
|
|
9660
|
-
const token = txfToToken(tokenId, txfToken);
|
|
9661
|
-
result.tokens.push(token);
|
|
9662
|
-
} else if (isV2TokenEntry(storageData[key])) {
|
|
9663
|
-
result.tokens.push(storageData[key]);
|
|
9664
|
-
}
|
|
9665
|
-
} catch (err) {
|
|
9666
|
-
result.validationErrors.push(`Token ${tokenId}: ${err}`);
|
|
9667
|
-
}
|
|
9668
|
-
} else if (isArchivedKey(key)) {
|
|
9669
|
-
const tokenId = tokenIdFromArchivedKey(key);
|
|
9670
|
-
try {
|
|
9671
|
-
const txfToken = storageData[key];
|
|
9672
|
-
if (txfToken?.genesis?.data?.tokenId) {
|
|
9673
|
-
result.archivedTokens.set(tokenId, txfToken);
|
|
9674
|
-
}
|
|
9675
|
-
} catch {
|
|
9676
|
-
result.validationErrors.push(`Archived token ${tokenId}: invalid structure`);
|
|
9677
|
-
}
|
|
9678
|
-
} else if (isForkedKey(key)) {
|
|
9679
|
-
const parsed = parseForkedKey(key);
|
|
9680
|
-
if (parsed) {
|
|
9681
|
-
try {
|
|
9682
|
-
const txfToken = storageData[key];
|
|
9683
|
-
if (txfToken?.genesis?.data?.tokenId) {
|
|
9684
|
-
const mapKey = `${parsed.tokenId}_${parsed.stateHash}`;
|
|
9685
|
-
result.forkedTokens.set(mapKey, txfToken);
|
|
9686
|
-
}
|
|
9687
|
-
} catch {
|
|
9688
|
-
result.validationErrors.push(`Forked token ${parsed.tokenId}: invalid structure`);
|
|
9689
|
-
}
|
|
9690
|
-
}
|
|
9691
|
-
} else if (key.startsWith("token-")) {
|
|
9692
|
-
try {
|
|
9693
|
-
const entry = storageData[key];
|
|
9694
|
-
const txfToken = entry?.token;
|
|
9695
|
-
if (txfToken?.genesis?.data?.tokenId) {
|
|
9696
|
-
const tokenId = txfToken.genesis.data.tokenId;
|
|
9697
|
-
const token = txfToToken(tokenId, txfToken);
|
|
9698
|
-
result.tokens.push(token);
|
|
9699
|
-
}
|
|
9700
|
-
} catch (err) {
|
|
9701
|
-
result.validationErrors.push(`Token ${key}: ${err}`);
|
|
9702
|
-
}
|
|
9703
|
-
}
|
|
9704
|
-
}
|
|
9705
|
-
return result;
|
|
9706
|
-
}
|
|
9707
|
-
function getCurrentStateHash(txf) {
|
|
9708
|
-
if (txf.transactions && txf.transactions.length > 0) {
|
|
9709
|
-
const lastTx = txf.transactions[txf.transactions.length - 1];
|
|
9710
|
-
if (lastTx?.newStateHash) {
|
|
9711
|
-
return lastTx.newStateHash;
|
|
9712
|
-
}
|
|
9713
|
-
if (lastTx?.inclusionProof?.authenticator?.stateHash) {
|
|
9714
|
-
return lastTx.inclusionProof.authenticator.stateHash;
|
|
9715
|
-
}
|
|
9716
|
-
}
|
|
9717
|
-
if (txf._integrity?.currentStateHash) {
|
|
9718
|
-
return txf._integrity.currentStateHash;
|
|
9719
|
-
}
|
|
9720
|
-
if (txf.genesis?.inclusionProof?.authenticator?.stateHash) {
|
|
9721
|
-
return txf.genesis.inclusionProof.authenticator.stateHash;
|
|
9722
|
-
}
|
|
9723
|
-
return void 0;
|
|
9724
|
-
}
|
|
9725
|
-
|
|
9726
9468
|
// core/utils.ts
|
|
9727
9469
|
var SECP256K1_ORDER = BigInt("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141");
|
|
9728
9470
|
var BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
@@ -9888,6 +9630,10 @@ function surfacedSendFailure(summary) {
|
|
|
9888
9630
|
}
|
|
9889
9631
|
return primaryError;
|
|
9890
9632
|
}
|
|
9633
|
+
function currentStateHashOfTxf(txf) {
|
|
9634
|
+
const last = txf.transactions?.[txf.transactions.length - 1];
|
|
9635
|
+
return last?.newStateHash ?? last?.inclusionProof?.authenticator?.stateHash ?? txf._integrity?.currentStateHash ?? txf.genesis?.inclusionProof?.authenticator?.stateHash ?? "";
|
|
9636
|
+
}
|
|
9891
9637
|
function genesisIdOf(token) {
|
|
9892
9638
|
return extractTokenIdFromSdkData(token.sdkData) ?? token.id.replace(/^v2_/, "");
|
|
9893
9639
|
}
|
|
@@ -9999,7 +9745,7 @@ function parseSdkDataCached(sdkData) {
|
|
|
9999
9745
|
try {
|
|
10000
9746
|
const txf = JSON.parse(sdkData);
|
|
10001
9747
|
tokenId = txf.genesis?.data?.tokenId || null;
|
|
10002
|
-
stateHash =
|
|
9748
|
+
stateHash = currentStateHashOfTxf(txf);
|
|
10003
9749
|
if (!stateHash) {
|
|
10004
9750
|
if (txf.state?.hash) {
|
|
10005
9751
|
stateHash = txf.state.hash;
|
|
@@ -10745,7 +10491,6 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
10745
10491
|
if (op.kind === "direct") {
|
|
10746
10492
|
finished = await ctx.engine.transfer({ token: op.sdkToken, recipientPubkey: ctx.recipientChainPubkey }, opts);
|
|
10747
10493
|
} else {
|
|
10748
|
-
const checkpointStore = this.getCheckpointStore();
|
|
10749
10494
|
const { outputs } = await ctx.engine.split(
|
|
10750
10495
|
{
|
|
10751
10496
|
token: op.sdkToken,
|
|
@@ -10754,7 +10499,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
10754
10499
|
{ recipientPubkey: ctx.selfChainPubkey, coinId: ctx.coinId, amount: op.remainderAmount }
|
|
10755
10500
|
]
|
|
10756
10501
|
},
|
|
10757
|
-
{ ...opts, ...checkpointStore ? { checkpointStore } : {} }
|
|
10502
|
+
{ ...opts, ...ctx.checkpointStore ? { checkpointStore: ctx.checkpointStore } : {} }
|
|
10758
10503
|
);
|
|
10759
10504
|
finished = outputs[0];
|
|
10760
10505
|
changeOutput = outputs[1];
|
|
@@ -10941,7 +10686,8 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
10941
10686
|
recipientChainPubkeyHex,
|
|
10942
10687
|
selfChainPubkey: hexToBytes4(this.deps.identity.chainPubkey),
|
|
10943
10688
|
coinId: request.coinId,
|
|
10944
|
-
...request.memo !== void 0 ? { memo: request.memo } : {}
|
|
10689
|
+
...request.memo !== void 0 ? { memo: request.memo } : {},
|
|
10690
|
+
...this.getCheckpointStore() ? { checkpointStore: this.getCheckpointStore() } : {}
|
|
10945
10691
|
})
|
|
10946
10692
|
);
|
|
10947
10693
|
attempt.summary = summary;
|
|
@@ -13458,8 +13204,11 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
13458
13204
|
try {
|
|
13459
13205
|
const plain = decryptField(this.getFieldEncryptionKey(), intent.payload);
|
|
13460
13206
|
payload = JSON.parse(plain);
|
|
13461
|
-
if (payload.v !==
|
|
13462
|
-
throw new SphereError(
|
|
13207
|
+
if (payload.v !== 2 || !Array.isArray(payload.direct)) {
|
|
13208
|
+
throw new SphereError(
|
|
13209
|
+
`Unsupported intent payload shape (v=${String(payload.v)}) \u2014 not resumable by this wallet version`,
|
|
13210
|
+
"VALIDATION_ERROR"
|
|
13211
|
+
);
|
|
13463
13212
|
}
|
|
13464
13213
|
} catch (err) {
|
|
13465
13214
|
logger.warn("Payments", `Intent ${intent.transferId.slice(0, 8)}\u2026 payload undecodable \u2014 skipped:`, err);
|
|
@@ -13598,31 +13347,22 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
13598
13347
|
async resumeIntent(transferId, payload) {
|
|
13599
13348
|
const engine = this.deps.tokenEngine;
|
|
13600
13349
|
const walletApi = this.deps.walletApi;
|
|
13601
|
-
const delivery = this.delivery;
|
|
13602
13350
|
const provider = this.getActiveTokenStorageProvider();
|
|
13603
13351
|
if (!provider) throw new SphereError("No token storage provider for intent resume", "STORAGE_ERROR");
|
|
13604
|
-
const serverApply = delivery.custody === "inventory";
|
|
13605
|
-
const
|
|
13606
|
-
const
|
|
13607
|
-
|
|
13608
|
-
|
|
13609
|
-
|
|
13610
|
-
|
|
13611
|
-
|
|
13612
|
-
|
|
13613
|
-
|
|
13614
|
-
}
|
|
13615
|
-
const nm = this.currentNametagName();
|
|
13616
|
-
await this.tryDeliver(
|
|
13617
|
-
() => delivery.deliver(payload.recipient, hexToBytes4(tokenBlobHex), {
|
|
13618
|
-
transferId,
|
|
13619
|
-
memo: payload.memo,
|
|
13620
|
-
...nm !== void 0 ? { senderNametag: nm } : {}
|
|
13621
|
-
}),
|
|
13622
|
-
tokenBlobHex
|
|
13623
|
-
);
|
|
13352
|
+
const serverApply = this.delivery.custody === "inventory";
|
|
13353
|
+
const checkpointStore = this.getCheckpointStore();
|
|
13354
|
+
const ctx = {
|
|
13355
|
+
engine,
|
|
13356
|
+
transferId,
|
|
13357
|
+
recipientChainPubkey: hexToBytes4(payload.recipient),
|
|
13358
|
+
recipientChainPubkeyHex: payload.recipient,
|
|
13359
|
+
selfChainPubkey: hexToBytes4(this.deps.identity.chainPubkey),
|
|
13360
|
+
coinId: payload.coinId,
|
|
13361
|
+
...payload.memo !== void 0 ? { memo: payload.memo } : {},
|
|
13362
|
+
...checkpointStore ? { checkpointStore } : {}
|
|
13624
13363
|
};
|
|
13625
13364
|
const journaled = await this.journaledByOp(transferId);
|
|
13365
|
+
const blobs = [];
|
|
13626
13366
|
const spent = [];
|
|
13627
13367
|
let conflicted = false;
|
|
13628
13368
|
let firstConflict;
|
|
@@ -13630,91 +13370,71 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
13630
13370
|
for (const [opIndex, genesisId] of payload.direct.entries()) {
|
|
13631
13371
|
const existing = journaled.get(opIndex);
|
|
13632
13372
|
if (existing) {
|
|
13633
|
-
|
|
13373
|
+
blobs.push(existing.tokenBlob);
|
|
13634
13374
|
spent.push(genesisId);
|
|
13635
|
-
|
|
13636
|
-
|
|
13637
|
-
|
|
13638
|
-
|
|
13639
|
-
|
|
13640
|
-
|
|
13641
|
-
|
|
13642
|
-
|
|
13643
|
-
|
|
13644
|
-
|
|
13645
|
-
|
|
13646
|
-
|
|
13647
|
-
|
|
13648
|
-
|
|
13649
|
-
|
|
13650
|
-
|
|
13651
|
-
|
|
13375
|
+
continue;
|
|
13376
|
+
}
|
|
13377
|
+
const source = await engine.decodeToken(await provider.getToken(genesisId));
|
|
13378
|
+
const outcome = await this.certifyOperation(
|
|
13379
|
+
{
|
|
13380
|
+
kind: "direct",
|
|
13381
|
+
opIndex,
|
|
13382
|
+
uiTokenId: `v2_${genesisId}`,
|
|
13383
|
+
genesisId,
|
|
13384
|
+
sourceSdkData: "",
|
|
13385
|
+
sdkToken: source,
|
|
13386
|
+
deliveredAmount: engine.balanceOf(source, payload.coinId)
|
|
13387
|
+
},
|
|
13388
|
+
ctx
|
|
13389
|
+
);
|
|
13390
|
+
if (outcome.error !== void 0) {
|
|
13391
|
+
if (!(outcome.error instanceof TransferConflictError)) throw outcome.error;
|
|
13392
|
+
conflicted = true;
|
|
13393
|
+
firstConflict ??= outcome.error;
|
|
13394
|
+
undeliveredAmount += engine.balanceOf(source, payload.coinId);
|
|
13395
|
+
logger.warn("Payments", `Resume op ${opIndex} lost its source to a concurrent transfer \u2014 not delivered (\xA78.1/#4):`, outcome.error);
|
|
13396
|
+
continue;
|
|
13652
13397
|
}
|
|
13398
|
+
blobs.push(outcome.tokenBlob);
|
|
13399
|
+
spent.push(genesisId);
|
|
13653
13400
|
}
|
|
13654
13401
|
let changeOutput = null;
|
|
13655
13402
|
if (payload.split) {
|
|
13656
13403
|
const splitOpIndex = payload.direct.length;
|
|
13657
|
-
const existingSplit = journaled.get(splitOpIndex);
|
|
13658
|
-
const checkpointStore = payload.v === 2 ? this.getCheckpointStore() : void 0;
|
|
13659
13404
|
let splitDelivered = false;
|
|
13660
|
-
|
|
13661
|
-
await
|
|
13662
|
-
|
|
13663
|
-
|
|
13664
|
-
|
|
13665
|
-
|
|
13666
|
-
|
|
13667
|
-
|
|
13668
|
-
|
|
13669
|
-
|
|
13670
|
-
|
|
13671
|
-
|
|
13672
|
-
|
|
13673
|
-
|
|
13674
|
-
|
|
13675
|
-
|
|
13676
|
-
|
|
13677
|
-
|
|
13678
|
-
|
|
13679
|
-
coinId: payload.coinId,
|
|
13680
|
-
amount: BigInt(payload.split.remainderAmount)
|
|
13681
|
-
}
|
|
13682
|
-
]
|
|
13683
|
-
},
|
|
13684
|
-
{
|
|
13685
|
-
signal: timeoutSignal(SEND_ENGINE_OP_TIMEOUT_MS),
|
|
13686
|
-
transferId,
|
|
13687
|
-
opIndex: splitOpIndex,
|
|
13688
|
-
...checkpointStore ? { checkpointStore } : {}
|
|
13689
|
-
}
|
|
13690
|
-
);
|
|
13691
|
-
changeOutput = outputs[1];
|
|
13692
|
-
if (!serverApply) await this.storeEngineToken(engine, changeOutput, { criticalSave: true });
|
|
13693
|
-
await deliverBlob(bytesToHex4(encodeTokenBlob(engine.encodeToken(outputs[0]))), splitOpIndex);
|
|
13694
|
-
splitDelivered = true;
|
|
13695
|
-
} catch (err) {
|
|
13696
|
-
if (err instanceof ProofUnconfirmedError) throw err;
|
|
13697
|
-
if (checkpointStore !== void 0 && (err instanceof CheckpointPersistFailedError || err instanceof SplitCheckpointLostError || err instanceof CheckpointTrustbaseMismatchError)) {
|
|
13698
|
-
throw err;
|
|
13405
|
+
{
|
|
13406
|
+
const source = await engine.decodeToken(await provider.getToken(payload.split.tokenId));
|
|
13407
|
+
const outcome = await this.certifyOperation(
|
|
13408
|
+
{
|
|
13409
|
+
kind: "split",
|
|
13410
|
+
opIndex: splitOpIndex,
|
|
13411
|
+
uiTokenId: `v2_${payload.split.tokenId}`,
|
|
13412
|
+
genesisId: payload.split.tokenId,
|
|
13413
|
+
sourceSdkData: "",
|
|
13414
|
+
sdkToken: source,
|
|
13415
|
+
deliveredAmount: BigInt(payload.split.splitAmount),
|
|
13416
|
+
remainderAmount: BigInt(payload.split.remainderAmount)
|
|
13417
|
+
},
|
|
13418
|
+
ctx
|
|
13419
|
+
);
|
|
13420
|
+
if (outcome.error === void 0) {
|
|
13421
|
+
changeOutput = outcome.changeOutput ?? null;
|
|
13422
|
+
if (!serverApply && changeOutput) {
|
|
13423
|
+
await this.storeEngineToken(engine, changeOutput, { criticalSave: true });
|
|
13699
13424
|
}
|
|
13700
|
-
|
|
13425
|
+
blobs.push(outcome.tokenBlob);
|
|
13426
|
+
splitDelivered = true;
|
|
13427
|
+
} else {
|
|
13428
|
+
splitDelivered = this.classifyResumeSplitFailure(outcome.error, (err) => {
|
|
13701
13429
|
conflicted = true;
|
|
13702
13430
|
firstConflict ??= err;
|
|
13703
13431
|
undeliveredAmount += BigInt(payload.split.splitAmount);
|
|
13704
|
-
|
|
13705
|
-
} else if (err instanceof SplitCheckpointLostError) {
|
|
13706
|
-
logger.warn("Payments", `Resume split (legacy no-checkpoint): recording the spend; change recovers on the next inventory resync (\xA73.1):`, err);
|
|
13707
|
-
this.deps.emitEvent("inventory:conflict", { transferId, coinId: payload.coinId, error: err.message });
|
|
13708
|
-
splitDelivered = true;
|
|
13709
|
-
} else {
|
|
13710
|
-
throw err;
|
|
13711
|
-
}
|
|
13432
|
+
});
|
|
13712
13433
|
}
|
|
13713
13434
|
}
|
|
13714
|
-
if (splitDelivered)
|
|
13715
|
-
spent.push(payload.split.tokenId);
|
|
13716
|
-
}
|
|
13435
|
+
if (splitDelivered) spent.push(payload.split.tokenId);
|
|
13717
13436
|
}
|
|
13437
|
+
await this.deliverCommittedBlobs(blobs, payload.recipient, transferId, payload.memo);
|
|
13718
13438
|
if (conflicted && spent.length === 0) throw firstConflict;
|
|
13719
13439
|
if (serverApply) {
|
|
13720
13440
|
await this.applyInventoryDelta(
|
|
@@ -13726,6 +13446,55 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
13726
13446
|
// the one the sources were read from, captured for the whole resume
|
|
13727
13447
|
);
|
|
13728
13448
|
}
|
|
13449
|
+
await this.dropResumedSources(spent, transferId, payload);
|
|
13450
|
+
if (conflicted) {
|
|
13451
|
+
const remaining = undeliveredAmount > 0n ? undeliveredAmount : 0n;
|
|
13452
|
+
const delivered = BigInt(payload.amount) - remaining;
|
|
13453
|
+
await walletApi.completeIntent(transferId);
|
|
13454
|
+
await this.recordResumedSentHistory(payload, transferId, delivered > 0n ? delivered.toString() : "0", spent[0]);
|
|
13455
|
+
this.deps.emitEvent("send:partial-remainder", {
|
|
13456
|
+
transferId,
|
|
13457
|
+
remainingAmount: remaining.toString(),
|
|
13458
|
+
coinId: payload.coinId,
|
|
13459
|
+
recipientPubkey: payload.recipient
|
|
13460
|
+
});
|
|
13461
|
+
throw new PartialSendConflictError(
|
|
13462
|
+
"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.",
|
|
13463
|
+
transferId,
|
|
13464
|
+
spent,
|
|
13465
|
+
// the DELIVERED genesisIds — never the conflicted leg
|
|
13466
|
+
remaining.toString(),
|
|
13467
|
+
firstConflict
|
|
13468
|
+
);
|
|
13469
|
+
}
|
|
13470
|
+
await walletApi.completeIntent(transferId);
|
|
13471
|
+
await this.recordResumedSentHistory(payload, transferId, payload.amount, payload.direct[0]);
|
|
13472
|
+
}
|
|
13473
|
+
/**
|
|
13474
|
+
* A resumed split that produced no output: a lost source means the leg delivered
|
|
13475
|
+
* nothing (returns false), and every checkpoint failure keeps the intent open by
|
|
13476
|
+
* throwing. There is no path here that records the spend.
|
|
13477
|
+
*/
|
|
13478
|
+
classifyResumeSplitFailure(error, onConflict) {
|
|
13479
|
+
if (error instanceof ProofUnconfirmedError) throw error;
|
|
13480
|
+
if (error instanceof CheckpointPersistFailedError || error instanceof SplitCheckpointLostError || error instanceof CheckpointTrustbaseMismatchError) {
|
|
13481
|
+
throw error;
|
|
13482
|
+
}
|
|
13483
|
+
if (error instanceof TransferConflictError) {
|
|
13484
|
+
onConflict(error);
|
|
13485
|
+
logger.warn("Payments", "Resume split: source lost to a concurrent transfer \u2014 not delivered (\xA78.1/#4):", error);
|
|
13486
|
+
return false;
|
|
13487
|
+
}
|
|
13488
|
+
throw error;
|
|
13489
|
+
}
|
|
13490
|
+
/**
|
|
13491
|
+
* Drop the local records of the sources a resume consumed (present when
|
|
13492
|
+
* resuming on the originating device). M7: each removal carries the LOCAL state
|
|
13493
|
+
* we spent, so a source a concurrent claim reactivated to a NEW state (a
|
|
13494
|
+
* self-send round-trip) is KEPT, not destroyed.
|
|
13495
|
+
*/
|
|
13496
|
+
async dropResumedSources(spent, transferId, payload) {
|
|
13497
|
+
const engine = this.deps.tokenEngine;
|
|
13729
13498
|
for (const genesisId of spent) {
|
|
13730
13499
|
const local = this.tokens.get(`v2_${genesisId}`);
|
|
13731
13500
|
if (!local) continue;
|
|
@@ -13737,8 +13506,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
13737
13506
|
let spentOnChain = false;
|
|
13738
13507
|
try {
|
|
13739
13508
|
if (local.sdkData) {
|
|
13740
|
-
|
|
13741
|
-
spentOnChain = await engine.isSpent(decoded);
|
|
13509
|
+
spentOnChain = await engine.isSpent(await engine.decodeToken(decodeTokenBlob(hexToBytes4(local.sdkData))));
|
|
13742
13510
|
}
|
|
13743
13511
|
} catch {
|
|
13744
13512
|
spentOnChain = false;
|
|
@@ -13752,47 +13520,19 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
13752
13520
|
);
|
|
13753
13521
|
}
|
|
13754
13522
|
}
|
|
13755
|
-
|
|
13756
|
-
|
|
13757
|
-
|
|
13758
|
-
await walletApi.completeIntent(transferId);
|
|
13759
|
-
await this.addToHistory({
|
|
13760
|
-
type: "SENT",
|
|
13761
|
-
amount: (delivered > 0n ? delivered : 0n).toString(),
|
|
13762
|
-
coinId: payload.coinId,
|
|
13763
|
-
symbol: this.getCoinSymbol(payload.coinId),
|
|
13764
|
-
timestamp: Date.now(),
|
|
13765
|
-
recipientPubkey: payload.recipient,
|
|
13766
|
-
memo: payload.memo,
|
|
13767
|
-
transferId,
|
|
13768
|
-
tokenId: spent[0] ? `v2_${spent[0]}` : void 0
|
|
13769
|
-
});
|
|
13770
|
-
this.deps.emitEvent("send:partial-remainder", {
|
|
13771
|
-
transferId,
|
|
13772
|
-
remainingAmount: (remaining > 0n ? remaining : 0n).toString(),
|
|
13773
|
-
coinId: payload.coinId,
|
|
13774
|
-
recipientPubkey: payload.recipient
|
|
13775
|
-
});
|
|
13776
|
-
throw new PartialSendConflictError(
|
|
13777
|
-
"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.",
|
|
13778
|
-
transferId,
|
|
13779
|
-
spent,
|
|
13780
|
-
// the DELIVERED genesisIds — never the conflicted leg
|
|
13781
|
-
(remaining > 0n ? remaining : 0n).toString(),
|
|
13782
|
-
firstConflict
|
|
13783
|
-
);
|
|
13784
|
-
}
|
|
13785
|
-
await walletApi.completeIntent(transferId);
|
|
13523
|
+
}
|
|
13524
|
+
/** §10: the SENT record — same dedupKey as the original attempt, so a resume after the history POST is a server-side no-op. */
|
|
13525
|
+
async recordResumedSentHistory(payload, transferId, amount, tokenGenesisId) {
|
|
13786
13526
|
await this.addToHistory({
|
|
13787
13527
|
type: "SENT",
|
|
13788
|
-
amount
|
|
13528
|
+
amount,
|
|
13789
13529
|
coinId: payload.coinId,
|
|
13790
13530
|
symbol: this.getCoinSymbol(payload.coinId),
|
|
13791
13531
|
timestamp: Date.now(),
|
|
13792
13532
|
recipientPubkey: payload.recipient,
|
|
13793
13533
|
memo: payload.memo,
|
|
13794
13534
|
transferId,
|
|
13795
|
-
tokenId:
|
|
13535
|
+
tokenId: tokenGenesisId ? `v2_${tokenGenesisId}` : void 0
|
|
13796
13536
|
});
|
|
13797
13537
|
}
|
|
13798
13538
|
// ===========================================================================
|
|
@@ -13814,11 +13554,7 @@ var PaymentsModule = class _PaymentsModule {
|
|
|
13814
13554
|
*/
|
|
13815
13555
|
async save(opts = {}) {
|
|
13816
13556
|
const providers = this.getTokenStorageProviders();
|
|
13817
|
-
|
|
13818
|
-
const txf = tokenToTxf(t);
|
|
13819
|
-
return `${t.id.slice(0, 12)}(${t.status},txf=${!!txf})`;
|
|
13820
|
-
});
|
|
13821
|
-
logger.debug("Payments", `save(): providers=${providers.size}, tokens=[${tokenStats.join(", ")}]`);
|
|
13557
|
+
logger.debug("Payments", `save(): providers=${providers.size}, tokens=${this.tokens.size}`);
|
|
13822
13558
|
if (providers.size === 0) {
|
|
13823
13559
|
logger.debug("Payments", "save(): No token storage providers - TXF not persisted");
|
|
13824
13560
|
return;
|
|
@@ -22488,7 +22224,6 @@ var import_canonicalize = __toESM(require("canonicalize"), 1);
|
|
|
22488
22224
|
|
|
22489
22225
|
// core/address.ts
|
|
22490
22226
|
var DIRECT_PREFIX = "DIRECT://";
|
|
22491
|
-
var PROXY_PREFIX = "PROXY://";
|
|
22492
22227
|
var NAMETAG_PREFIX = "@";
|
|
22493
22228
|
function parseAddress(address) {
|
|
22494
22229
|
if (!address || typeof address !== "string") return null;
|
|
@@ -22500,9 +22235,6 @@ function parseAddress(address) {
|
|
|
22500
22235
|
}
|
|
22501
22236
|
return null;
|
|
22502
22237
|
}
|
|
22503
|
-
if (trimmed.startsWith(PROXY_PREFIX) && trimmed.length > PROXY_PREFIX.length) {
|
|
22504
|
-
return { type: "PROXY", raw: trimmed, value: trimmed.slice(PROXY_PREFIX.length) };
|
|
22505
|
-
}
|
|
22506
22238
|
if (trimmed.startsWith(NAMETAG_PREFIX)) {
|
|
22507
22239
|
const value = trimmed.slice(NAMETAG_PREFIX.length);
|
|
22508
22240
|
if (value.length > 0) {
|
|
@@ -25749,447 +25481,97 @@ function parseWalletText(content) {
|
|
|
25749
25481
|
/ENCRYPTED MASTER KEY \(password protected\):\s*([^\n]+)/
|
|
25750
25482
|
);
|
|
25751
25483
|
if (!encryptedKey) {
|
|
25752
|
-
return {
|
|
25753
|
-
success: false,
|
|
25754
|
-
error: "Could not find encrypted master key in backup file"
|
|
25755
|
-
};
|
|
25756
|
-
}
|
|
25757
|
-
return {
|
|
25758
|
-
success: false,
|
|
25759
|
-
needsPassword: true,
|
|
25760
|
-
error: "Password required for encrypted wallet"
|
|
25761
|
-
};
|
|
25762
|
-
}
|
|
25763
|
-
const masterKey = extractFromText(
|
|
25764
|
-
content,
|
|
25765
|
-
/MASTER PRIVATE KEY \(keep secret!\):\s*([^\n]+)/
|
|
25766
|
-
);
|
|
25767
|
-
if (!masterKey) {
|
|
25768
|
-
return {
|
|
25769
|
-
success: false,
|
|
25770
|
-
error: "Could not find master private key in backup file"
|
|
25771
|
-
};
|
|
25772
|
-
}
|
|
25773
|
-
const chainCode = extractFromText(
|
|
25774
|
-
content,
|
|
25775
|
-
/MASTER CHAIN CODE \(for (?:BIP32 HD|Alpha) wallet compatibility\):\s*([^\n]+)/
|
|
25776
|
-
);
|
|
25777
|
-
const descriptorPath = extractFromText(content, /DESCRIPTOR PATH:\s*([^\n]+)/);
|
|
25778
|
-
const isBIP32 = content.includes("WALLET TYPE: BIP32 hierarchical deterministic wallet") || content.includes("WALLET TYPE: Alpha descriptor wallet") || !!chainCode;
|
|
25779
|
-
const effectiveDescriptorPath = descriptorPath ?? (isBIP32 ? "84'/1'/0'" : void 0);
|
|
25780
|
-
const data = {
|
|
25781
|
-
masterKey,
|
|
25782
|
-
chainCode: chainCode ?? void 0,
|
|
25783
|
-
descriptorPath: effectiveDescriptorPath,
|
|
25784
|
-
derivationMode: isBIP32 ? "bip32" : "wif_hmac"
|
|
25785
|
-
};
|
|
25786
|
-
return {
|
|
25787
|
-
success: true,
|
|
25788
|
-
data
|
|
25789
|
-
};
|
|
25790
|
-
} catch (e) {
|
|
25791
|
-
return {
|
|
25792
|
-
success: false,
|
|
25793
|
-
error: `Failed to parse wallet text: ${e instanceof Error ? e.message : String(e)}`
|
|
25794
|
-
};
|
|
25795
|
-
}
|
|
25796
|
-
}
|
|
25797
|
-
function parseAndDecryptWalletText(content, password) {
|
|
25798
|
-
try {
|
|
25799
|
-
const isEncrypted = isTextWalletEncrypted(content);
|
|
25800
|
-
if (!isEncrypted) {
|
|
25801
|
-
return parseWalletText(content);
|
|
25802
|
-
}
|
|
25803
|
-
const encryptedKey = extractFromText(
|
|
25804
|
-
content,
|
|
25805
|
-
/ENCRYPTED MASTER KEY \(password protected\):\s*([^\n]+)/
|
|
25806
|
-
);
|
|
25807
|
-
if (!encryptedKey) {
|
|
25808
|
-
return {
|
|
25809
|
-
success: false,
|
|
25810
|
-
error: "Could not find encrypted master key in backup file"
|
|
25811
|
-
};
|
|
25812
|
-
}
|
|
25813
|
-
const masterKey = decryptTextFormatKey(encryptedKey, password);
|
|
25814
|
-
if (!masterKey) {
|
|
25815
|
-
return {
|
|
25816
|
-
success: false,
|
|
25817
|
-
error: "Failed to decrypt - incorrect password?"
|
|
25818
|
-
};
|
|
25819
|
-
}
|
|
25820
|
-
const chainCode = extractFromText(
|
|
25821
|
-
content,
|
|
25822
|
-
/MASTER CHAIN CODE \(for (?:BIP32 HD|Alpha) wallet compatibility\):\s*([^\n]+)/
|
|
25823
|
-
);
|
|
25824
|
-
const descriptorPath = extractFromText(content, /DESCRIPTOR PATH:\s*([^\n]+)/);
|
|
25825
|
-
const isBIP32 = content.includes("WALLET TYPE: BIP32 hierarchical deterministic wallet") || content.includes("WALLET TYPE: Alpha descriptor wallet") || !!chainCode;
|
|
25826
|
-
const effectiveDescriptorPath = descriptorPath ?? (isBIP32 ? "84'/1'/0'" : void 0);
|
|
25827
|
-
const data = {
|
|
25828
|
-
masterKey,
|
|
25829
|
-
chainCode: chainCode ?? void 0,
|
|
25830
|
-
descriptorPath: effectiveDescriptorPath,
|
|
25831
|
-
derivationMode: isBIP32 ? "bip32" : "wif_hmac"
|
|
25832
|
-
};
|
|
25833
|
-
return {
|
|
25834
|
-
success: true,
|
|
25835
|
-
data
|
|
25836
|
-
};
|
|
25837
|
-
} catch (e) {
|
|
25838
|
-
return {
|
|
25839
|
-
success: false,
|
|
25840
|
-
error: `Failed to parse/decrypt wallet text: ${e instanceof Error ? e.message : String(e)}`
|
|
25841
|
-
};
|
|
25842
|
-
}
|
|
25843
|
-
}
|
|
25844
|
-
|
|
25845
|
-
// serialization/wallet-dat.ts
|
|
25846
|
-
var import_crypto_js4 = __toESM(require("crypto-js"), 1);
|
|
25847
|
-
function uint8ArrayToWordArray(u8arr) {
|
|
25848
|
-
const hex = bytesToHex4(u8arr);
|
|
25849
|
-
return import_crypto_js4.default.enc.Hex.parse(hex);
|
|
25850
|
-
}
|
|
25851
|
-
function isSQLiteDatabase(data) {
|
|
25852
|
-
if (data.length < 16) return false;
|
|
25853
|
-
const header = new TextDecoder().decode(data.slice(0, 16));
|
|
25854
|
-
return header.startsWith("SQLite format 3");
|
|
25855
|
-
}
|
|
25856
|
-
function isWalletDatEncrypted(data) {
|
|
25857
|
-
const mkeyPattern = new TextEncoder().encode("mkey");
|
|
25858
|
-
return findPattern(data, mkeyPattern, 0) !== -1;
|
|
25859
|
-
}
|
|
25860
|
-
function findAllCMasterKeys(data) {
|
|
25861
|
-
const results = [];
|
|
25862
|
-
for (let pos = 0; pos < data.length - 70; pos++) {
|
|
25863
|
-
if (data[pos] === 48) {
|
|
25864
|
-
const saltLenPos = pos + 1 + 48;
|
|
25865
|
-
if (saltLenPos < data.length && data[saltLenPos] === 8) {
|
|
25866
|
-
const iterPos = saltLenPos + 1 + 8 + 4;
|
|
25867
|
-
if (iterPos + 4 <= data.length) {
|
|
25868
|
-
const iterations = data[iterPos] | data[iterPos + 1] << 8 | data[iterPos + 2] << 16 | data[iterPos + 3] << 24;
|
|
25869
|
-
if (iterations >= 1e3 && iterations <= 1e7) {
|
|
25870
|
-
const encryptedKey = data.slice(pos + 1, pos + 1 + 48);
|
|
25871
|
-
const salt = data.slice(saltLenPos + 1, saltLenPos + 1 + 8);
|
|
25872
|
-
const derivationMethod = data[saltLenPos + 1 + 8] | data[saltLenPos + 1 + 8 + 1] << 8 | data[saltLenPos + 1 + 8 + 2] << 16 | data[saltLenPos + 1 + 8 + 3] << 24;
|
|
25873
|
-
results.push({ encryptedKey, salt, derivationMethod, iterations, position: pos });
|
|
25874
|
-
}
|
|
25875
|
-
}
|
|
25876
|
-
}
|
|
25877
|
-
}
|
|
25878
|
-
}
|
|
25879
|
-
return results;
|
|
25880
|
-
}
|
|
25881
|
-
function findWpkhDescriptor(data) {
|
|
25882
|
-
const descriptorPattern = new TextEncoder().encode("walletdescriptor");
|
|
25883
|
-
let descriptorIndex = 0;
|
|
25884
|
-
while ((descriptorIndex = findPattern(data, descriptorPattern, descriptorIndex)) !== -1) {
|
|
25885
|
-
let scanPos = descriptorIndex + descriptorPattern.length + 32;
|
|
25886
|
-
const descLen = data[scanPos];
|
|
25887
|
-
scanPos++;
|
|
25888
|
-
const descBytes = data.slice(scanPos, scanPos + Math.min(descLen, 200));
|
|
25889
|
-
let descStr = "";
|
|
25890
|
-
for (let i = 0; i < descBytes.length && descBytes[i] >= 32 && descBytes[i] <= 126; i++) {
|
|
25891
|
-
descStr += String.fromCharCode(descBytes[i]);
|
|
25892
|
-
}
|
|
25893
|
-
if (descStr.startsWith("wpkh(xpub") && descStr.includes("/0/*)")) {
|
|
25894
|
-
const xpubMatch = descStr.match(/xpub[1-9A-HJ-NP-Za-km-z]{100,}/);
|
|
25895
|
-
if (xpubMatch) {
|
|
25896
|
-
const descIdStart = descriptorIndex + descriptorPattern.length;
|
|
25897
|
-
const descriptorId = data.slice(descIdStart, descIdStart + 32);
|
|
25898
|
-
const pathMatch = descStr.match(/\[[\da-f]+\/(\d+'\/\d+'\/\d+')\]/);
|
|
25899
|
-
const descriptorPath = pathMatch ? pathMatch[1] : "84'/1'/0'";
|
|
25900
|
-
return {
|
|
25901
|
-
descriptorId,
|
|
25902
|
-
xpubString: xpubMatch[0],
|
|
25903
|
-
descriptorPath
|
|
25904
|
-
};
|
|
25905
|
-
}
|
|
25906
|
-
}
|
|
25907
|
-
descriptorIndex++;
|
|
25908
|
-
}
|
|
25909
|
-
return { descriptorId: null, xpubString: null, descriptorPath: null };
|
|
25910
|
-
}
|
|
25911
|
-
function extractChainCodeFromXpub(xpubString) {
|
|
25912
|
-
const decoded = base58Decode(xpubString);
|
|
25913
|
-
return bytesToHex4(decoded.slice(13, 45));
|
|
25914
|
-
}
|
|
25915
|
-
function findMasterChainCode(data) {
|
|
25916
|
-
const xpubPattern = new TextEncoder().encode("xpub");
|
|
25917
|
-
const base58Chars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
25918
|
-
let searchPos = 0;
|
|
25919
|
-
while (searchPos < data.length) {
|
|
25920
|
-
const xpubIndex = findPattern(data, xpubPattern, searchPos);
|
|
25921
|
-
if (xpubIndex === -1) break;
|
|
25922
|
-
let xpubStr = "xpub";
|
|
25923
|
-
let pos = xpubIndex + 4;
|
|
25924
|
-
while (pos < data.length && xpubStr.length < 120) {
|
|
25925
|
-
const char = String.fromCharCode(data[pos]);
|
|
25926
|
-
if (base58Chars.includes(char)) {
|
|
25927
|
-
xpubStr += char;
|
|
25928
|
-
pos++;
|
|
25929
|
-
} else {
|
|
25930
|
-
break;
|
|
25931
|
-
}
|
|
25932
|
-
}
|
|
25933
|
-
if (xpubStr.length > 100) {
|
|
25934
|
-
try {
|
|
25935
|
-
const decoded = base58Decode(xpubStr);
|
|
25936
|
-
const depth = decoded[4];
|
|
25937
|
-
if (depth === 0) {
|
|
25938
|
-
return bytesToHex4(decoded.slice(13, 45));
|
|
25939
|
-
}
|
|
25940
|
-
} catch {
|
|
25941
|
-
}
|
|
25942
|
-
}
|
|
25943
|
-
searchPos = xpubIndex + 4;
|
|
25944
|
-
}
|
|
25945
|
-
return null;
|
|
25946
|
-
}
|
|
25947
|
-
function extractDescriptorKeys(data) {
|
|
25948
|
-
const keys = [];
|
|
25949
|
-
const descriptorKeyPattern = new TextEncoder().encode("walletdescriptorkey");
|
|
25950
|
-
let index = 0;
|
|
25951
|
-
while ((index = findPattern(data, descriptorKeyPattern, index)) !== -1) {
|
|
25952
|
-
for (let checkPos = index + descriptorKeyPattern.length; checkPos < Math.min(index + descriptorKeyPattern.length + 200, data.length - 40); checkPos++) {
|
|
25953
|
-
if (data[checkPos] === 211 && data[checkPos + 1] === 2 && data[checkPos + 2] === 1 && data[checkPos + 3] === 1 && data[checkPos + 4] === 4 && data[checkPos + 5] === 32) {
|
|
25954
|
-
const privKey = data.slice(checkPos + 6, checkPos + 38);
|
|
25955
|
-
const privKeyHex = bytesToHex4(privKey);
|
|
25956
|
-
if (isValidPrivateKey(privKeyHex)) {
|
|
25957
|
-
keys.push(privKeyHex);
|
|
25958
|
-
break;
|
|
25959
|
-
}
|
|
25960
|
-
}
|
|
25961
|
-
}
|
|
25962
|
-
index++;
|
|
25963
|
-
}
|
|
25964
|
-
return keys;
|
|
25965
|
-
}
|
|
25966
|
-
function extractLegacyKeys(data) {
|
|
25967
|
-
const keys = [];
|
|
25968
|
-
const keyPattern = new TextEncoder().encode("key");
|
|
25969
|
-
let index = 0;
|
|
25970
|
-
while ((index = findPattern(data, keyPattern, index)) !== -1) {
|
|
25971
|
-
const searchPattern = new Uint8Array([4, 32]);
|
|
25972
|
-
for (let i = index; i < Math.min(index + 200, data.length - 34); i++) {
|
|
25973
|
-
if (data[i] === searchPattern[0] && data[i + 1] === searchPattern[1]) {
|
|
25974
|
-
const privKey = data.slice(i + 2, i + 34);
|
|
25975
|
-
const privKeyHex = bytesToHex4(privKey);
|
|
25976
|
-
if (isValidPrivateKey(privKeyHex)) {
|
|
25977
|
-
keys.push(privKeyHex);
|
|
25978
|
-
break;
|
|
25979
|
-
}
|
|
25980
|
-
}
|
|
25981
|
-
}
|
|
25982
|
-
index++;
|
|
25983
|
-
}
|
|
25984
|
-
return keys;
|
|
25985
|
-
}
|
|
25986
|
-
function findEncryptedKeyForDescriptor(data, descriptorId) {
|
|
25987
|
-
const ckeyPattern = new TextEncoder().encode("walletdescriptorckey");
|
|
25988
|
-
let ckeyIndex = findPattern(data, ckeyPattern, 0);
|
|
25989
|
-
while (ckeyIndex !== -1) {
|
|
25990
|
-
const recordDescId = data.slice(ckeyIndex + ckeyPattern.length, ckeyIndex + ckeyPattern.length + 32);
|
|
25991
|
-
if (Array.from(recordDescId).every((b, i) => b === descriptorId[i])) {
|
|
25992
|
-
let keyPos = ckeyIndex + ckeyPattern.length + 32;
|
|
25993
|
-
const pubkeyLen = data[keyPos];
|
|
25994
|
-
keyPos++;
|
|
25995
|
-
const pubkey = data.slice(keyPos, keyPos + pubkeyLen);
|
|
25996
|
-
for (let searchPos = keyPos + pubkeyLen; searchPos < Math.min(keyPos + pubkeyLen + 100, data.length - 50); searchPos++) {
|
|
25997
|
-
const valueLen = data[searchPos];
|
|
25998
|
-
if (valueLen >= 32 && valueLen <= 64) {
|
|
25999
|
-
const encryptedKey = data.slice(searchPos + 1, searchPos + 1 + valueLen);
|
|
26000
|
-
return { pubkey, encryptedKey };
|
|
26001
|
-
}
|
|
26002
|
-
}
|
|
26003
|
-
}
|
|
26004
|
-
ckeyIndex = findPattern(data, ckeyPattern, ckeyIndex + 1);
|
|
26005
|
-
}
|
|
26006
|
-
return null;
|
|
26007
|
-
}
|
|
26008
|
-
async function decryptCMasterKey(cmk, password, onProgress) {
|
|
26009
|
-
const { encryptedKey, salt, iterations } = cmk;
|
|
26010
|
-
const passwordHex = bytesToHex4(new TextEncoder().encode(password));
|
|
26011
|
-
const saltHex = bytesToHex4(salt);
|
|
26012
|
-
const inputHex = passwordHex + saltHex;
|
|
26013
|
-
let hash = import_crypto_js4.default.SHA512(import_crypto_js4.default.enc.Hex.parse(inputHex));
|
|
26014
|
-
const BATCH_SIZE = 1e3;
|
|
26015
|
-
for (let i = 0; i < iterations - 1; i++) {
|
|
26016
|
-
hash = import_crypto_js4.default.SHA512(hash);
|
|
26017
|
-
if (onProgress && i % BATCH_SIZE === 0) {
|
|
26018
|
-
await onProgress(i, iterations);
|
|
26019
|
-
}
|
|
26020
|
-
}
|
|
26021
|
-
const derivedKey = import_crypto_js4.default.lib.WordArray.create(hash.words.slice(0, 8));
|
|
26022
|
-
const derivedIv = import_crypto_js4.default.lib.WordArray.create(hash.words.slice(8, 12));
|
|
26023
|
-
const encryptedWords = uint8ArrayToWordArray(encryptedKey);
|
|
26024
|
-
const decrypted = import_crypto_js4.default.AES.decrypt(
|
|
26025
|
-
{ ciphertext: encryptedWords },
|
|
26026
|
-
derivedKey,
|
|
26027
|
-
{ iv: derivedIv, padding: import_crypto_js4.default.pad.Pkcs7, mode: import_crypto_js4.default.mode.CBC }
|
|
26028
|
-
);
|
|
26029
|
-
const result = import_crypto_js4.default.enc.Hex.stringify(decrypted);
|
|
26030
|
-
if (!result || result.length !== 64) {
|
|
26031
|
-
throw new SphereError("Master key decryption failed - incorrect password", "DECRYPTION_ERROR");
|
|
26032
|
-
}
|
|
26033
|
-
return result;
|
|
26034
|
-
}
|
|
26035
|
-
function decryptPrivateKey(encryptedKey, pubkey, masterKeyHex) {
|
|
26036
|
-
const pubkeyWords = uint8ArrayToWordArray(pubkey);
|
|
26037
|
-
const pubkeyHashWords = import_crypto_js4.default.SHA256(import_crypto_js4.default.SHA256(pubkeyWords));
|
|
26038
|
-
const ivWords = import_crypto_js4.default.lib.WordArray.create(pubkeyHashWords.words.slice(0, 4));
|
|
26039
|
-
const masterKeyWords = import_crypto_js4.default.enc.Hex.parse(masterKeyHex);
|
|
26040
|
-
const encryptedWords = uint8ArrayToWordArray(encryptedKey);
|
|
26041
|
-
const decrypted = import_crypto_js4.default.AES.decrypt(
|
|
26042
|
-
{ ciphertext: encryptedWords },
|
|
26043
|
-
masterKeyWords,
|
|
26044
|
-
{ iv: ivWords, padding: import_crypto_js4.default.pad.Pkcs7, mode: import_crypto_js4.default.mode.CBC }
|
|
26045
|
-
);
|
|
26046
|
-
return import_crypto_js4.default.enc.Hex.stringify(decrypted);
|
|
26047
|
-
}
|
|
26048
|
-
function parseWalletDat(data) {
|
|
26049
|
-
if (!isSQLiteDatabase(data)) {
|
|
26050
|
-
return {
|
|
26051
|
-
success: false,
|
|
26052
|
-
error: "Invalid wallet.dat file - not an SQLite database"
|
|
26053
|
-
};
|
|
26054
|
-
}
|
|
26055
|
-
const isEncrypted = isWalletDatEncrypted(data);
|
|
26056
|
-
const cmasterKeys = isEncrypted ? findAllCMasterKeys(data) : [];
|
|
26057
|
-
const descriptorKeys = isEncrypted ? [] : extractDescriptorKeys(data);
|
|
26058
|
-
const legacyKeys = isEncrypted ? [] : extractLegacyKeys(data);
|
|
26059
|
-
const { descriptorId: _descriptorId, xpubString, descriptorPath } = findWpkhDescriptor(data);
|
|
26060
|
-
void _descriptorId;
|
|
26061
|
-
let chainCode = null;
|
|
26062
|
-
if (xpubString) {
|
|
26063
|
-
try {
|
|
26064
|
-
chainCode = extractChainCodeFromXpub(xpubString);
|
|
26065
|
-
} catch {
|
|
26066
|
-
chainCode = findMasterChainCode(data);
|
|
26067
|
-
}
|
|
26068
|
-
} else {
|
|
26069
|
-
chainCode = findMasterChainCode(data);
|
|
26070
|
-
}
|
|
26071
|
-
if (!isEncrypted) {
|
|
26072
|
-
let masterKey = null;
|
|
26073
|
-
if (descriptorKeys.length > 0) {
|
|
26074
|
-
masterKey = descriptorKeys[0];
|
|
26075
|
-
} else if (legacyKeys.length > 0) {
|
|
26076
|
-
masterKey = legacyKeys[0];
|
|
26077
|
-
}
|
|
26078
|
-
if (masterKey) {
|
|
26079
|
-
const parsedData = {
|
|
26080
|
-
masterKey,
|
|
26081
|
-
chainCode: chainCode ?? void 0,
|
|
26082
|
-
descriptorPath: descriptorPath ?? "84'/1'/0'",
|
|
26083
|
-
derivationMode: chainCode ? "bip32" : "wif_hmac"
|
|
25484
|
+
return {
|
|
25485
|
+
success: false,
|
|
25486
|
+
error: "Could not find encrypted master key in backup file"
|
|
25487
|
+
};
|
|
25488
|
+
}
|
|
25489
|
+
return {
|
|
25490
|
+
success: false,
|
|
25491
|
+
needsPassword: true,
|
|
25492
|
+
error: "Password required for encrypted wallet"
|
|
26084
25493
|
};
|
|
25494
|
+
}
|
|
25495
|
+
const masterKey = extractFromText(
|
|
25496
|
+
content,
|
|
25497
|
+
/MASTER PRIVATE KEY \(keep secret!\):\s*([^\n]+)/
|
|
25498
|
+
);
|
|
25499
|
+
if (!masterKey) {
|
|
26085
25500
|
return {
|
|
26086
|
-
success:
|
|
26087
|
-
|
|
25501
|
+
success: false,
|
|
25502
|
+
error: "Could not find master private key in backup file"
|
|
26088
25503
|
};
|
|
26089
25504
|
}
|
|
26090
|
-
|
|
26091
|
-
|
|
26092
|
-
|
|
26093
|
-
|
|
26094
|
-
|
|
26095
|
-
|
|
26096
|
-
|
|
26097
|
-
|
|
26098
|
-
|
|
25505
|
+
const chainCode = extractFromText(
|
|
25506
|
+
content,
|
|
25507
|
+
/MASTER CHAIN CODE \(for (?:BIP32 HD|Alpha) wallet compatibility\):\s*([^\n]+)/
|
|
25508
|
+
);
|
|
25509
|
+
const descriptorPath = extractFromText(content, /DESCRIPTOR PATH:\s*([^\n]+)/);
|
|
25510
|
+
const isBIP32 = content.includes("WALLET TYPE: BIP32 hierarchical deterministic wallet") || content.includes("WALLET TYPE: Alpha descriptor wallet") || !!chainCode;
|
|
25511
|
+
const effectiveDescriptorPath = descriptorPath ?? (isBIP32 ? "84'/1'/0'" : void 0);
|
|
25512
|
+
const data = {
|
|
25513
|
+
masterKey,
|
|
25514
|
+
chainCode: chainCode ?? void 0,
|
|
25515
|
+
descriptorPath: effectiveDescriptorPath,
|
|
25516
|
+
derivationMode: isBIP32 ? "bip32" : "wif_hmac"
|
|
26099
25517
|
};
|
|
26100
|
-
}
|
|
26101
|
-
const firstCmk = cmasterKeys[0];
|
|
26102
|
-
return {
|
|
26103
|
-
success: false,
|
|
26104
|
-
needsPassword: true,
|
|
26105
|
-
encryptionInfo: {
|
|
26106
|
-
iterations: firstCmk.iterations,
|
|
26107
|
-
salt: firstCmk.salt,
|
|
26108
|
-
encryptedKey: firstCmk.encryptedKey
|
|
26109
|
-
},
|
|
26110
|
-
error: "Password required for encrypted wallet"
|
|
26111
|
-
};
|
|
26112
|
-
}
|
|
26113
|
-
async function parseAndDecryptWalletDat(data, password, onProgress) {
|
|
26114
|
-
if (!isSQLiteDatabase(data)) {
|
|
26115
25518
|
return {
|
|
26116
|
-
success:
|
|
26117
|
-
|
|
25519
|
+
success: true,
|
|
25520
|
+
data
|
|
26118
25521
|
};
|
|
26119
|
-
}
|
|
26120
|
-
const isEncrypted = isWalletDatEncrypted(data);
|
|
26121
|
-
if (!isEncrypted) {
|
|
26122
|
-
return parseWalletDat(data);
|
|
26123
|
-
}
|
|
26124
|
-
const cmasterKeys = findAllCMasterKeys(data);
|
|
26125
|
-
if (cmasterKeys.length === 0) {
|
|
25522
|
+
} catch (e) {
|
|
26126
25523
|
return {
|
|
26127
25524
|
success: false,
|
|
26128
|
-
error:
|
|
25525
|
+
error: `Failed to parse wallet text: ${e instanceof Error ? e.message : String(e)}`
|
|
26129
25526
|
};
|
|
26130
25527
|
}
|
|
26131
|
-
|
|
26132
|
-
|
|
26133
|
-
|
|
26134
|
-
|
|
26135
|
-
|
|
26136
|
-
|
|
26137
|
-
}
|
|
26138
|
-
} catch {
|
|
26139
|
-
continue;
|
|
25528
|
+
}
|
|
25529
|
+
function parseAndDecryptWalletText(content, password) {
|
|
25530
|
+
try {
|
|
25531
|
+
const isEncrypted = isTextWalletEncrypted(content);
|
|
25532
|
+
if (!isEncrypted) {
|
|
25533
|
+
return parseWalletText(content);
|
|
26140
25534
|
}
|
|
26141
|
-
|
|
26142
|
-
|
|
26143
|
-
|
|
26144
|
-
|
|
26145
|
-
|
|
26146
|
-
|
|
26147
|
-
|
|
26148
|
-
|
|
26149
|
-
|
|
26150
|
-
if (xpubString) {
|
|
26151
|
-
try {
|
|
26152
|
-
chainCode = extractChainCodeFromXpub(xpubString);
|
|
26153
|
-
} catch {
|
|
26154
|
-
chainCode = findMasterChainCode(data);
|
|
25535
|
+
const encryptedKey = extractFromText(
|
|
25536
|
+
content,
|
|
25537
|
+
/ENCRYPTED MASTER KEY \(password protected\):\s*([^\n]+)/
|
|
25538
|
+
);
|
|
25539
|
+
if (!encryptedKey) {
|
|
25540
|
+
return {
|
|
25541
|
+
success: false,
|
|
25542
|
+
error: "Could not find encrypted master key in backup file"
|
|
25543
|
+
};
|
|
26155
25544
|
}
|
|
26156
|
-
|
|
26157
|
-
|
|
26158
|
-
|
|
26159
|
-
|
|
26160
|
-
|
|
26161
|
-
|
|
26162
|
-
|
|
25545
|
+
const masterKey = decryptTextFormatKey(encryptedKey, password);
|
|
25546
|
+
if (!masterKey) {
|
|
25547
|
+
return {
|
|
25548
|
+
success: false,
|
|
25549
|
+
error: "Failed to decrypt - incorrect password?"
|
|
25550
|
+
};
|
|
25551
|
+
}
|
|
25552
|
+
const chainCode = extractFromText(
|
|
25553
|
+
content,
|
|
25554
|
+
/MASTER CHAIN CODE \(for (?:BIP32 HD|Alpha) wallet compatibility\):\s*([^\n]+)/
|
|
25555
|
+
);
|
|
25556
|
+
const descriptorPath = extractFromText(content, /DESCRIPTOR PATH:\s*([^\n]+)/);
|
|
25557
|
+
const isBIP32 = content.includes("WALLET TYPE: BIP32 hierarchical deterministic wallet") || content.includes("WALLET TYPE: Alpha descriptor wallet") || !!chainCode;
|
|
25558
|
+
const effectiveDescriptorPath = descriptorPath ?? (isBIP32 ? "84'/1'/0'" : void 0);
|
|
25559
|
+
const data = {
|
|
25560
|
+
masterKey,
|
|
25561
|
+
chainCode: chainCode ?? void 0,
|
|
25562
|
+
descriptorPath: effectiveDescriptorPath,
|
|
25563
|
+
derivationMode: isBIP32 ? "bip32" : "wif_hmac"
|
|
26163
25564
|
};
|
|
26164
|
-
}
|
|
26165
|
-
const encryptedKeyData = findEncryptedKeyForDescriptor(data, descriptorId);
|
|
26166
|
-
if (!encryptedKeyData) {
|
|
26167
25565
|
return {
|
|
26168
|
-
success:
|
|
26169
|
-
|
|
25566
|
+
success: true,
|
|
25567
|
+
data
|
|
26170
25568
|
};
|
|
26171
|
-
}
|
|
26172
|
-
const bip32MasterKey = decryptPrivateKey(
|
|
26173
|
-
encryptedKeyData.encryptedKey,
|
|
26174
|
-
encryptedKeyData.pubkey,
|
|
26175
|
-
masterKeyHex
|
|
26176
|
-
);
|
|
26177
|
-
if (!bip32MasterKey || bip32MasterKey.length !== 64) {
|
|
25569
|
+
} catch (e) {
|
|
26178
25570
|
return {
|
|
26179
25571
|
success: false,
|
|
26180
|
-
error:
|
|
25572
|
+
error: `Failed to parse/decrypt wallet text: ${e instanceof Error ? e.message : String(e)}`
|
|
26181
25573
|
};
|
|
26182
25574
|
}
|
|
26183
|
-
const parsedData = {
|
|
26184
|
-
masterKey: bip32MasterKey,
|
|
26185
|
-
chainCode: chainCode ?? void 0,
|
|
26186
|
-
descriptorPath: descriptorPath ?? "84'/1'/0'",
|
|
26187
|
-
derivationMode: "bip32"
|
|
26188
|
-
};
|
|
26189
|
-
return {
|
|
26190
|
-
success: true,
|
|
26191
|
-
data: parsedData
|
|
26192
|
-
};
|
|
26193
25575
|
}
|
|
26194
25576
|
|
|
26195
25577
|
// core/Sphere.ts
|
|
@@ -27022,32 +26404,196 @@ var Sphere = class _Sphere {
|
|
|
27022
26404
|
if (this._transport.isConnected() && this._transport.fetchPendingEvents) {
|
|
27023
26405
|
await this._transport.fetchPendingEvents();
|
|
27024
26406
|
}
|
|
27025
|
-
}
|
|
27026
|
-
getAggregator() {
|
|
27027
|
-
return this._oracle;
|
|
26407
|
+
}
|
|
26408
|
+
getAggregator() {
|
|
26409
|
+
return this._oracle;
|
|
26410
|
+
}
|
|
26411
|
+
/**
|
|
26412
|
+
* Apply a new gateway API key to the LIVE oracle + token engine WITHOUT a full
|
|
26413
|
+
* Sphere rebuild (transport / realtime socket / discovery stay up). Use this to
|
|
26414
|
+
* inject a per-wallet subscription key provisioned AFTER init, or a per-address
|
|
26415
|
+
* key on an address switch, so the next money operation authenticates with it.
|
|
26416
|
+
*
|
|
26417
|
+
* Money-safety: the running token engine is snapshotted per-operation (the
|
|
26418
|
+
* payments module reads `deps.tokenEngine` into a local at op start), so an
|
|
26419
|
+
* in-flight send/mint completes on the OLD key (submit + proof stay paired on
|
|
26420
|
+
* one client) and only operations started AFTER this call use the new key.
|
|
26421
|
+
* Only the ACTIVE address's engine is rebuilt — other tracked addresses re-key
|
|
26422
|
+
* on their next switch/build. No-op-safe if the engine can't be built (the
|
|
26423
|
+
* modules fall back to their legacy path, same as init).
|
|
26424
|
+
*/
|
|
26425
|
+
async setOracleApiKey(apiKey) {
|
|
26426
|
+
this._oracle.setApiKey?.(apiKey);
|
|
26427
|
+
const active = this._addressModules.get(this._currentAddressIndex);
|
|
26428
|
+
const replaced = active?.tokenEngine ?? this._tokenEngine;
|
|
26429
|
+
this._tokenEngine = await this.buildTokenEngine();
|
|
26430
|
+
if (active) active.tokenEngine = this._tokenEngine;
|
|
26431
|
+
this._payments.setTokenEngine(this._tokenEngine);
|
|
26432
|
+
if (replaced && replaced !== this._tokenEngine) replaced.dispose?.();
|
|
26433
|
+
}
|
|
26434
|
+
/**
|
|
26435
|
+
* Import a wallet from a backup file: a `UNICITY WALLET DETAILS` text backup
|
|
26436
|
+
* (the format `exportToTxt` writes, optionally password-encrypted), a legacy
|
|
26437
|
+
* flat-JSON webwallet export, or a bare mnemonic in a text file.
|
|
26438
|
+
*
|
|
26439
|
+
* @example
|
|
26440
|
+
* const result = await Sphere.importFromLegacyFile({
|
|
26441
|
+
* fileContent: await file.text(),
|
|
26442
|
+
* fileName: file.name,
|
|
26443
|
+
* password, // when the backup is encrypted
|
|
26444
|
+
* ...providers,
|
|
26445
|
+
* });
|
|
26446
|
+
*/
|
|
26447
|
+
static async importFromLegacyFile(options) {
|
|
26448
|
+
const { fileContent, fileName, password, onDecryptProgress, ...baseOptions } = options;
|
|
26449
|
+
const fileType = _Sphere.detectLegacyFileType(fileName, fileContent);
|
|
26450
|
+
if (fileType === "unknown") {
|
|
26451
|
+
return { success: false, error: "Unknown file format" };
|
|
26452
|
+
}
|
|
26453
|
+
if (fileType === "mnemonic") {
|
|
26454
|
+
const mnemonic = fileContent.trim().toLowerCase().split(/\s+/).join(" ");
|
|
26455
|
+
if (!_Sphere.validateMnemonic(mnemonic)) {
|
|
26456
|
+
return { success: false, error: "Invalid mnemonic phrase" };
|
|
26457
|
+
}
|
|
26458
|
+
const sphere = await _Sphere.import({ ...baseOptions, mnemonic });
|
|
26459
|
+
return { success: true, sphere, mnemonic };
|
|
26460
|
+
}
|
|
26461
|
+
if (fileType === "txt") {
|
|
26462
|
+
const content = fileContent;
|
|
26463
|
+
let parseResult;
|
|
26464
|
+
if (password) {
|
|
26465
|
+
parseResult = parseAndDecryptWalletText(content, password);
|
|
26466
|
+
} else if (isTextWalletEncrypted(content)) {
|
|
26467
|
+
return { success: false, needsPassword: true, error: "Password required for encrypted wallet" };
|
|
26468
|
+
} else {
|
|
26469
|
+
parseResult = parseWalletText(content);
|
|
26470
|
+
}
|
|
26471
|
+
if (parseResult.needsPassword && !password) {
|
|
26472
|
+
return { success: false, needsPassword: true, error: "Password required for encrypted wallet" };
|
|
26473
|
+
}
|
|
26474
|
+
if (!parseResult.success || !parseResult.data) {
|
|
26475
|
+
return { success: false, error: parseResult.error };
|
|
26476
|
+
}
|
|
26477
|
+
const { masterKey, chainCode, descriptorPath, derivationMode } = parseResult.data;
|
|
26478
|
+
const basePath = descriptorPath ? `m/${descriptorPath}` : DEFAULT_BASE_PATH;
|
|
26479
|
+
const sphere = await _Sphere.import({
|
|
26480
|
+
...baseOptions,
|
|
26481
|
+
masterKey,
|
|
26482
|
+
chainCode,
|
|
26483
|
+
basePath,
|
|
26484
|
+
derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac")
|
|
26485
|
+
});
|
|
26486
|
+
return { success: true, sphere };
|
|
26487
|
+
}
|
|
26488
|
+
if (fileType === "json") {
|
|
26489
|
+
const content = fileContent;
|
|
26490
|
+
let parsed;
|
|
26491
|
+
try {
|
|
26492
|
+
parsed = JSON.parse(content);
|
|
26493
|
+
} catch {
|
|
26494
|
+
return { success: false, error: "Invalid JSON file" };
|
|
26495
|
+
}
|
|
26496
|
+
if (parsed.type === "sphere-wallet") {
|
|
26497
|
+
const result = await _Sphere.importFromJSON({
|
|
26498
|
+
...baseOptions,
|
|
26499
|
+
jsonContent: content,
|
|
26500
|
+
password
|
|
26501
|
+
});
|
|
26502
|
+
if (result.success) {
|
|
26503
|
+
const sphere2 = _Sphere.getInstance();
|
|
26504
|
+
return { success: true, sphere: sphere2, mnemonic: result.mnemonic };
|
|
26505
|
+
}
|
|
26506
|
+
if (!password && result.error?.includes("Password required")) {
|
|
26507
|
+
return { success: false, needsPassword: true, error: result.error };
|
|
26508
|
+
}
|
|
26509
|
+
return { success: false, error: result.error };
|
|
26510
|
+
}
|
|
26511
|
+
let masterKey;
|
|
26512
|
+
let mnemonic;
|
|
26513
|
+
if (parsed.encrypted && typeof parsed.encrypted === "object") {
|
|
26514
|
+
if (!password) {
|
|
26515
|
+
return { success: false, needsPassword: true, error: "Password required for encrypted wallet" };
|
|
26516
|
+
}
|
|
26517
|
+
const enc = parsed.encrypted;
|
|
26518
|
+
if (!enc.salt || !enc.masterPrivateKey) {
|
|
26519
|
+
return { success: false, error: "Invalid encrypted wallet format" };
|
|
26520
|
+
}
|
|
26521
|
+
const decryptedKey = decryptWithSalt(enc.masterPrivateKey, password, enc.salt);
|
|
26522
|
+
if (!decryptedKey) {
|
|
26523
|
+
return { success: false, error: "Failed to decrypt - incorrect password?" };
|
|
26524
|
+
}
|
|
26525
|
+
masterKey = decryptedKey;
|
|
26526
|
+
if (enc.mnemonic) {
|
|
26527
|
+
mnemonic = decryptWithSalt(enc.mnemonic, password, enc.salt) ?? void 0;
|
|
26528
|
+
}
|
|
26529
|
+
} else {
|
|
26530
|
+
masterKey = parsed.masterPrivateKey;
|
|
26531
|
+
mnemonic = parsed.mnemonic;
|
|
26532
|
+
}
|
|
26533
|
+
if (!masterKey) {
|
|
26534
|
+
return { success: false, error: "No master key found in wallet JSON" };
|
|
26535
|
+
}
|
|
26536
|
+
const chainCode = parsed.chainCode;
|
|
26537
|
+
const descriptorPath = parsed.descriptorPath;
|
|
26538
|
+
const derivationMode = parsed.derivationMode;
|
|
26539
|
+
const isBIP32 = derivationMode === "bip32" || !!chainCode;
|
|
26540
|
+
const basePath = descriptorPath ? `m/${descriptorPath}` : isBIP32 ? "m/84'/1'/0'" : DEFAULT_BASE_PATH;
|
|
26541
|
+
if (mnemonic) {
|
|
26542
|
+
const sphere2 = await _Sphere.import({ ...baseOptions, mnemonic, basePath });
|
|
26543
|
+
return { success: true, sphere: sphere2, mnemonic };
|
|
26544
|
+
}
|
|
26545
|
+
const sphere = await _Sphere.import({
|
|
26546
|
+
...baseOptions,
|
|
26547
|
+
masterKey,
|
|
26548
|
+
chainCode,
|
|
26549
|
+
basePath,
|
|
26550
|
+
derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac")
|
|
26551
|
+
});
|
|
26552
|
+
return { success: true, sphere };
|
|
26553
|
+
}
|
|
26554
|
+
return { success: false, error: "Unsupported file type" };
|
|
26555
|
+
}
|
|
26556
|
+
/**
|
|
26557
|
+
* Detect legacy file type from filename and content
|
|
26558
|
+
*/
|
|
26559
|
+
static detectLegacyFileType(fileName, content) {
|
|
26560
|
+
if (fileName.endsWith(".json")) {
|
|
26561
|
+
return "json";
|
|
26562
|
+
}
|
|
26563
|
+
try {
|
|
26564
|
+
const trimmed = content.trim();
|
|
26565
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
26566
|
+
JSON.parse(trimmed);
|
|
26567
|
+
return "json";
|
|
26568
|
+
}
|
|
26569
|
+
} catch {
|
|
26570
|
+
}
|
|
26571
|
+
const words = content.trim().split(/\s+/);
|
|
26572
|
+
if ((words.length === 12 || words.length === 24) && words.every((w) => /^[a-z]+$/.test(w.toLowerCase()))) {
|
|
26573
|
+
return "mnemonic";
|
|
26574
|
+
}
|
|
26575
|
+
if (isWalletTextFormat(content)) {
|
|
26576
|
+
return "txt";
|
|
26577
|
+
}
|
|
26578
|
+
return "unknown";
|
|
27028
26579
|
}
|
|
27029
26580
|
/**
|
|
27030
|
-
*
|
|
27031
|
-
* Sphere rebuild (transport / realtime socket / discovery stay up). Use this to
|
|
27032
|
-
* inject a per-wallet subscription key provisioned AFTER init, or a per-address
|
|
27033
|
-
* key on an address switch, so the next money operation authenticates with it.
|
|
27034
|
-
*
|
|
27035
|
-
* Money-safety: the running token engine is snapshotted per-operation (the
|
|
27036
|
-
* payments module reads `deps.tokenEngine` into a local at op start), so an
|
|
27037
|
-
* in-flight send/mint completes on the OLD key (submit + proof stay paired on
|
|
27038
|
-
* one client) and only operations started AFTER this call use the new key.
|
|
27039
|
-
* Only the ACTIVE address's engine is rebuilt — other tracked addresses re-key
|
|
27040
|
-
* on their next switch/build. No-op-safe if the engine can't be built (the
|
|
27041
|
-
* modules fall back to their legacy path, same as init).
|
|
26581
|
+
* Check if a legacy file is encrypted
|
|
27042
26582
|
*/
|
|
27043
|
-
|
|
27044
|
-
|
|
27045
|
-
|
|
27046
|
-
|
|
27047
|
-
|
|
27048
|
-
if (
|
|
27049
|
-
|
|
27050
|
-
|
|
26583
|
+
static isLegacyFileEncrypted(fileName, content) {
|
|
26584
|
+
const fileType = _Sphere.detectLegacyFileType(fileName, content);
|
|
26585
|
+
if (fileType === "txt") {
|
|
26586
|
+
return isTextWalletEncrypted(content);
|
|
26587
|
+
}
|
|
26588
|
+
if (fileType === "json") {
|
|
26589
|
+
try {
|
|
26590
|
+
const data = JSON.parse(content);
|
|
26591
|
+
return !!data.encrypted;
|
|
26592
|
+
} catch {
|
|
26593
|
+
return false;
|
|
26594
|
+
}
|
|
26595
|
+
}
|
|
26596
|
+
return false;
|
|
27051
26597
|
}
|
|
27052
26598
|
/**
|
|
27053
26599
|
* Check if wallet has BIP32 master key for HD derivation
|
|
@@ -27319,224 +26865,6 @@ var Sphere = class _Sphere {
|
|
|
27319
26865
|
};
|
|
27320
26866
|
}
|
|
27321
26867
|
}
|
|
27322
|
-
/**
|
|
27323
|
-
* Import wallet from legacy file (.dat, .txt, or mnemonic text)
|
|
27324
|
-
*
|
|
27325
|
-
* Supports:
|
|
27326
|
-
* - Bitcoin Core wallet.dat files (SQLite format, encrypted or unencrypted)
|
|
27327
|
-
* - Text backup files (UNICITY WALLET DETAILS format)
|
|
27328
|
-
* - Plain mnemonic text (12 or 24 words)
|
|
27329
|
-
*
|
|
27330
|
-
* @returns Object with success status, created Sphere instance, and optionally recovered mnemonic
|
|
27331
|
-
*
|
|
27332
|
-
* @example
|
|
27333
|
-
* ```ts
|
|
27334
|
-
* // Import from .dat file
|
|
27335
|
-
* const fileBuffer = await file.arrayBuffer();
|
|
27336
|
-
* const result = await Sphere.importFromLegacyFile({
|
|
27337
|
-
* fileContent: new Uint8Array(fileBuffer),
|
|
27338
|
-
* fileName: 'wallet.dat',
|
|
27339
|
-
* password: 'wallet-password', // if encrypted
|
|
27340
|
-
* storage, transport, oracle,
|
|
27341
|
-
* });
|
|
27342
|
-
*
|
|
27343
|
-
* // Import from .txt file
|
|
27344
|
-
* const textContent = await file.text();
|
|
27345
|
-
* const result = await Sphere.importFromLegacyFile({
|
|
27346
|
-
* fileContent: textContent,
|
|
27347
|
-
* fileName: 'backup.txt',
|
|
27348
|
-
* storage, transport, oracle,
|
|
27349
|
-
* });
|
|
27350
|
-
* ```
|
|
27351
|
-
*/
|
|
27352
|
-
static async importFromLegacyFile(options) {
|
|
27353
|
-
const { fileContent, fileName, password, onDecryptProgress, ...baseOptions } = options;
|
|
27354
|
-
const fileType = _Sphere.detectLegacyFileType(fileName, fileContent);
|
|
27355
|
-
if (fileType === "unknown") {
|
|
27356
|
-
return { success: false, error: "Unknown file format" };
|
|
27357
|
-
}
|
|
27358
|
-
if (fileType === "mnemonic") {
|
|
27359
|
-
const mnemonic = fileContent.trim().toLowerCase().split(/\s+/).join(" ");
|
|
27360
|
-
if (!_Sphere.validateMnemonic(mnemonic)) {
|
|
27361
|
-
return { success: false, error: "Invalid mnemonic phrase" };
|
|
27362
|
-
}
|
|
27363
|
-
const sphere = await _Sphere.import({ ...baseOptions, mnemonic });
|
|
27364
|
-
return { success: true, sphere, mnemonic };
|
|
27365
|
-
}
|
|
27366
|
-
if (fileType === "dat") {
|
|
27367
|
-
const data = fileContent instanceof Uint8Array ? fileContent : new TextEncoder().encode(fileContent);
|
|
27368
|
-
let parseResult;
|
|
27369
|
-
if (password) {
|
|
27370
|
-
parseResult = await parseAndDecryptWalletDat(data, password, onDecryptProgress);
|
|
27371
|
-
} else {
|
|
27372
|
-
parseResult = parseWalletDat(data);
|
|
27373
|
-
}
|
|
27374
|
-
if (parseResult.needsPassword && !password) {
|
|
27375
|
-
return { success: false, needsPassword: true, error: "Password required for encrypted wallet" };
|
|
27376
|
-
}
|
|
27377
|
-
if (!parseResult.success || !parseResult.data) {
|
|
27378
|
-
return { success: false, error: parseResult.error };
|
|
27379
|
-
}
|
|
27380
|
-
const { masterKey, chainCode, descriptorPath, derivationMode } = parseResult.data;
|
|
27381
|
-
const basePath = descriptorPath ? `m/${descriptorPath}` : DEFAULT_BASE_PATH;
|
|
27382
|
-
const sphere = await _Sphere.import({
|
|
27383
|
-
...baseOptions,
|
|
27384
|
-
masterKey,
|
|
27385
|
-
chainCode,
|
|
27386
|
-
basePath,
|
|
27387
|
-
derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac")
|
|
27388
|
-
});
|
|
27389
|
-
return { success: true, sphere };
|
|
27390
|
-
}
|
|
27391
|
-
if (fileType === "txt") {
|
|
27392
|
-
const content = typeof fileContent === "string" ? fileContent : new TextDecoder().decode(fileContent);
|
|
27393
|
-
let parseResult;
|
|
27394
|
-
if (password) {
|
|
27395
|
-
parseResult = parseAndDecryptWalletText(content, password);
|
|
27396
|
-
} else if (isTextWalletEncrypted(content)) {
|
|
27397
|
-
return { success: false, needsPassword: true, error: "Password required for encrypted wallet" };
|
|
27398
|
-
} else {
|
|
27399
|
-
parseResult = parseWalletText(content);
|
|
27400
|
-
}
|
|
27401
|
-
if (parseResult.needsPassword && !password) {
|
|
27402
|
-
return { success: false, needsPassword: true, error: "Password required for encrypted wallet" };
|
|
27403
|
-
}
|
|
27404
|
-
if (!parseResult.success || !parseResult.data) {
|
|
27405
|
-
return { success: false, error: parseResult.error };
|
|
27406
|
-
}
|
|
27407
|
-
const { masterKey, chainCode, descriptorPath, derivationMode } = parseResult.data;
|
|
27408
|
-
const basePath = descriptorPath ? `m/${descriptorPath}` : DEFAULT_BASE_PATH;
|
|
27409
|
-
const sphere = await _Sphere.import({
|
|
27410
|
-
...baseOptions,
|
|
27411
|
-
masterKey,
|
|
27412
|
-
chainCode,
|
|
27413
|
-
basePath,
|
|
27414
|
-
derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac")
|
|
27415
|
-
});
|
|
27416
|
-
return { success: true, sphere };
|
|
27417
|
-
}
|
|
27418
|
-
if (fileType === "json") {
|
|
27419
|
-
const content = typeof fileContent === "string" ? fileContent : new TextDecoder().decode(fileContent);
|
|
27420
|
-
let parsed;
|
|
27421
|
-
try {
|
|
27422
|
-
parsed = JSON.parse(content);
|
|
27423
|
-
} catch {
|
|
27424
|
-
return { success: false, error: "Invalid JSON file" };
|
|
27425
|
-
}
|
|
27426
|
-
if (parsed.type === "sphere-wallet") {
|
|
27427
|
-
const result = await _Sphere.importFromJSON({
|
|
27428
|
-
...baseOptions,
|
|
27429
|
-
jsonContent: content,
|
|
27430
|
-
password
|
|
27431
|
-
});
|
|
27432
|
-
if (result.success) {
|
|
27433
|
-
const sphere2 = _Sphere.getInstance();
|
|
27434
|
-
return { success: true, sphere: sphere2, mnemonic: result.mnemonic };
|
|
27435
|
-
}
|
|
27436
|
-
if (!password && result.error?.includes("Password required")) {
|
|
27437
|
-
return { success: false, needsPassword: true, error: result.error };
|
|
27438
|
-
}
|
|
27439
|
-
return { success: false, error: result.error };
|
|
27440
|
-
}
|
|
27441
|
-
let masterKey;
|
|
27442
|
-
let mnemonic;
|
|
27443
|
-
if (parsed.encrypted && typeof parsed.encrypted === "object") {
|
|
27444
|
-
if (!password) {
|
|
27445
|
-
return { success: false, needsPassword: true, error: "Password required for encrypted wallet" };
|
|
27446
|
-
}
|
|
27447
|
-
const enc = parsed.encrypted;
|
|
27448
|
-
if (!enc.salt || !enc.masterPrivateKey) {
|
|
27449
|
-
return { success: false, error: "Invalid encrypted wallet format" };
|
|
27450
|
-
}
|
|
27451
|
-
const decryptedKey = decryptWithSalt(enc.masterPrivateKey, password, enc.salt);
|
|
27452
|
-
if (!decryptedKey) {
|
|
27453
|
-
return { success: false, error: "Failed to decrypt - incorrect password?" };
|
|
27454
|
-
}
|
|
27455
|
-
masterKey = decryptedKey;
|
|
27456
|
-
if (enc.mnemonic) {
|
|
27457
|
-
mnemonic = decryptWithSalt(enc.mnemonic, password, enc.salt) ?? void 0;
|
|
27458
|
-
}
|
|
27459
|
-
} else {
|
|
27460
|
-
masterKey = parsed.masterPrivateKey;
|
|
27461
|
-
mnemonic = parsed.mnemonic;
|
|
27462
|
-
}
|
|
27463
|
-
if (!masterKey) {
|
|
27464
|
-
return { success: false, error: "No master key found in wallet JSON" };
|
|
27465
|
-
}
|
|
27466
|
-
const chainCode = parsed.chainCode;
|
|
27467
|
-
const descriptorPath = parsed.descriptorPath;
|
|
27468
|
-
const derivationMode = parsed.derivationMode;
|
|
27469
|
-
const isBIP32 = derivationMode === "bip32" || !!chainCode;
|
|
27470
|
-
const basePath = descriptorPath ? `m/${descriptorPath}` : isBIP32 ? "m/84'/1'/0'" : DEFAULT_BASE_PATH;
|
|
27471
|
-
if (mnemonic) {
|
|
27472
|
-
const sphere2 = await _Sphere.import({ ...baseOptions, mnemonic, basePath });
|
|
27473
|
-
return { success: true, sphere: sphere2, mnemonic };
|
|
27474
|
-
}
|
|
27475
|
-
const sphere = await _Sphere.import({
|
|
27476
|
-
...baseOptions,
|
|
27477
|
-
masterKey,
|
|
27478
|
-
chainCode,
|
|
27479
|
-
basePath,
|
|
27480
|
-
derivationMode: derivationMode || (chainCode ? "bip32" : "wif_hmac")
|
|
27481
|
-
});
|
|
27482
|
-
return { success: true, sphere };
|
|
27483
|
-
}
|
|
27484
|
-
return { success: false, error: "Unsupported file type" };
|
|
27485
|
-
}
|
|
27486
|
-
/**
|
|
27487
|
-
* Detect legacy file type from filename and content
|
|
27488
|
-
*/
|
|
27489
|
-
static detectLegacyFileType(fileName, content) {
|
|
27490
|
-
if (fileName.endsWith(".dat")) {
|
|
27491
|
-
return "dat";
|
|
27492
|
-
}
|
|
27493
|
-
const textContent = typeof content === "string" ? content : content.length < 1e3 ? new TextDecoder().decode(content) : "";
|
|
27494
|
-
if (fileName.endsWith(".json")) {
|
|
27495
|
-
return "json";
|
|
27496
|
-
}
|
|
27497
|
-
try {
|
|
27498
|
-
const trimmed = textContent.trim();
|
|
27499
|
-
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
27500
|
-
JSON.parse(trimmed);
|
|
27501
|
-
return "json";
|
|
27502
|
-
}
|
|
27503
|
-
} catch {
|
|
27504
|
-
}
|
|
27505
|
-
const words = textContent.trim().split(/\s+/);
|
|
27506
|
-
if ((words.length === 12 || words.length === 24) && words.every((w) => /^[a-z]+$/.test(w.toLowerCase()))) {
|
|
27507
|
-
return "mnemonic";
|
|
27508
|
-
}
|
|
27509
|
-
if (isWalletTextFormat(textContent)) {
|
|
27510
|
-
return "txt";
|
|
27511
|
-
}
|
|
27512
|
-
if (content instanceof Uint8Array && isSQLiteDatabase(content)) {
|
|
27513
|
-
return "dat";
|
|
27514
|
-
}
|
|
27515
|
-
return "unknown";
|
|
27516
|
-
}
|
|
27517
|
-
/**
|
|
27518
|
-
* Check if a legacy file is encrypted
|
|
27519
|
-
*/
|
|
27520
|
-
static isLegacyFileEncrypted(fileName, content) {
|
|
27521
|
-
const fileType = _Sphere.detectLegacyFileType(fileName, content);
|
|
27522
|
-
if (fileType === "dat" && content instanceof Uint8Array) {
|
|
27523
|
-
return isWalletDatEncrypted(content);
|
|
27524
|
-
}
|
|
27525
|
-
if (fileType === "txt") {
|
|
27526
|
-
const textContent = typeof content === "string" ? content : new TextDecoder().decode(content);
|
|
27527
|
-
return isTextWalletEncrypted(textContent);
|
|
27528
|
-
}
|
|
27529
|
-
if (fileType === "json") {
|
|
27530
|
-
try {
|
|
27531
|
-
const textContent = typeof content === "string" ? content : new TextDecoder().decode(content);
|
|
27532
|
-
const data = JSON.parse(textContent);
|
|
27533
|
-
return !!data.encrypted;
|
|
27534
|
-
} catch {
|
|
27535
|
-
return false;
|
|
27536
|
-
}
|
|
27537
|
-
}
|
|
27538
|
-
return false;
|
|
27539
|
-
}
|
|
27540
26868
|
/**
|
|
27541
26869
|
* Get the current active address index
|
|
27542
26870
|
*
|
|
@@ -29447,116 +28775,6 @@ var CurrencyUtils = {
|
|
|
29447
28775
|
format: formatAmount
|
|
29448
28776
|
};
|
|
29449
28777
|
|
|
29450
|
-
// core/bech32.ts
|
|
29451
|
-
var CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
|
29452
|
-
var GENERATOR = [996825010, 642813549, 513874426, 1027748829, 705979059];
|
|
29453
|
-
function convertBits(data, fromBits, toBits, pad) {
|
|
29454
|
-
let acc = 0;
|
|
29455
|
-
let bits = 0;
|
|
29456
|
-
const ret = [];
|
|
29457
|
-
const maxv = (1 << toBits) - 1;
|
|
29458
|
-
for (let i = 0; i < data.length; i++) {
|
|
29459
|
-
const value = data[i];
|
|
29460
|
-
if (value < 0 || value >> fromBits !== 0) return null;
|
|
29461
|
-
acc = acc << fromBits | value;
|
|
29462
|
-
bits += fromBits;
|
|
29463
|
-
while (bits >= toBits) {
|
|
29464
|
-
bits -= toBits;
|
|
29465
|
-
ret.push(acc >> bits & maxv);
|
|
29466
|
-
}
|
|
29467
|
-
}
|
|
29468
|
-
if (pad) {
|
|
29469
|
-
if (bits > 0) {
|
|
29470
|
-
ret.push(acc << toBits - bits & maxv);
|
|
29471
|
-
}
|
|
29472
|
-
} else if (bits >= fromBits || acc << toBits - bits & maxv) {
|
|
29473
|
-
return null;
|
|
29474
|
-
}
|
|
29475
|
-
return ret;
|
|
29476
|
-
}
|
|
29477
|
-
function hrpExpand(hrp) {
|
|
29478
|
-
const ret = [];
|
|
29479
|
-
for (let i = 0; i < hrp.length; i++) ret.push(hrp.charCodeAt(i) >> 5);
|
|
29480
|
-
ret.push(0);
|
|
29481
|
-
for (let i = 0; i < hrp.length; i++) ret.push(hrp.charCodeAt(i) & 31);
|
|
29482
|
-
return ret;
|
|
29483
|
-
}
|
|
29484
|
-
function bech32Polymod(values) {
|
|
29485
|
-
let chk = 1;
|
|
29486
|
-
for (let p = 0; p < values.length; p++) {
|
|
29487
|
-
const top = chk >> 25;
|
|
29488
|
-
chk = (chk & 33554431) << 5 ^ values[p];
|
|
29489
|
-
for (let i = 0; i < 5; i++) {
|
|
29490
|
-
if (top >> i & 1) chk ^= GENERATOR[i];
|
|
29491
|
-
}
|
|
29492
|
-
}
|
|
29493
|
-
return chk;
|
|
29494
|
-
}
|
|
29495
|
-
function bech32Checksum(hrp, data) {
|
|
29496
|
-
const values = hrpExpand(hrp).concat(data).concat([0, 0, 0, 0, 0, 0]);
|
|
29497
|
-
const mod2 = bech32Polymod(values) ^ 1;
|
|
29498
|
-
const ret = [];
|
|
29499
|
-
for (let p = 0; p < 6; p++) {
|
|
29500
|
-
ret.push(mod2 >> 5 * (5 - p) & 31);
|
|
29501
|
-
}
|
|
29502
|
-
return ret;
|
|
29503
|
-
}
|
|
29504
|
-
function encodeBech32(hrp, version, program) {
|
|
29505
|
-
if (version < 0 || version > 16) {
|
|
29506
|
-
throw new SphereError("Invalid witness version", "VALIDATION_ERROR");
|
|
29507
|
-
}
|
|
29508
|
-
const converted = convertBits(Array.from(program), 8, 5, true);
|
|
29509
|
-
if (!converted) {
|
|
29510
|
-
throw new SphereError("Failed to convert bits", "VALIDATION_ERROR");
|
|
29511
|
-
}
|
|
29512
|
-
const data = [version].concat(converted);
|
|
29513
|
-
const checksum = bech32Checksum(hrp, data);
|
|
29514
|
-
const combined = data.concat(checksum);
|
|
29515
|
-
let out = hrp + "1";
|
|
29516
|
-
for (let i = 0; i < combined.length; i++) {
|
|
29517
|
-
out += CHARSET[combined[i]];
|
|
29518
|
-
}
|
|
29519
|
-
return out;
|
|
29520
|
-
}
|
|
29521
|
-
function decodeBech32(addr) {
|
|
29522
|
-
addr = addr.toLowerCase();
|
|
29523
|
-
const pos = addr.lastIndexOf("1");
|
|
29524
|
-
if (pos < 1) return null;
|
|
29525
|
-
const hrp = addr.substring(0, pos);
|
|
29526
|
-
const dataStr = addr.substring(pos + 1);
|
|
29527
|
-
const data = [];
|
|
29528
|
-
for (let i = 0; i < dataStr.length; i++) {
|
|
29529
|
-
const val = CHARSET.indexOf(dataStr[i]);
|
|
29530
|
-
if (val === -1) return null;
|
|
29531
|
-
data.push(val);
|
|
29532
|
-
}
|
|
29533
|
-
const checksum = bech32Checksum(hrp, data.slice(0, -6));
|
|
29534
|
-
for (let i = 0; i < 6; i++) {
|
|
29535
|
-
if (checksum[i] !== data[data.length - 6 + i]) {
|
|
29536
|
-
return null;
|
|
29537
|
-
}
|
|
29538
|
-
}
|
|
29539
|
-
const version = data[0];
|
|
29540
|
-
const program = convertBits(data.slice(1, -6), 5, 8, false);
|
|
29541
|
-
if (!program) return null;
|
|
29542
|
-
return {
|
|
29543
|
-
hrp,
|
|
29544
|
-
witnessVersion: version,
|
|
29545
|
-
data: Uint8Array.from(program)
|
|
29546
|
-
};
|
|
29547
|
-
}
|
|
29548
|
-
function createAddress(hrp, pubkeyHash) {
|
|
29549
|
-
const hashBytes = typeof pubkeyHash === "string" ? Uint8Array.from(Buffer.from(pubkeyHash, "hex")) : pubkeyHash;
|
|
29550
|
-
return encodeBech32(hrp, 1, hashBytes);
|
|
29551
|
-
}
|
|
29552
|
-
function isValidBech32(addr) {
|
|
29553
|
-
return decodeBech32(addr) !== null;
|
|
29554
|
-
}
|
|
29555
|
-
function getAddressHrp(addr) {
|
|
29556
|
-
const result = decodeBech32(addr);
|
|
29557
|
-
return result?.hrp ?? null;
|
|
29558
|
-
}
|
|
29559
|
-
|
|
29560
28778
|
// core/network-health.ts
|
|
29561
28779
|
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
29562
28780
|
async function checkNetworkHealth(network = "testnet", options) {
|
|
@@ -29740,7 +28958,6 @@ async function runCustomCheck(name, checkFn, timeoutMs) {
|
|
|
29740
28958
|
}
|
|
29741
28959
|
// Annotate the CommonJS export names for ESM import in node:
|
|
29742
28960
|
0 && (module.exports = {
|
|
29743
|
-
CHARSET,
|
|
29744
28961
|
CurrencyUtils,
|
|
29745
28962
|
DEFAULT_DERIVATION_PATH,
|
|
29746
28963
|
DEFAULT_TOKEN_DECIMALS,
|
|
@@ -29758,11 +28975,8 @@ async function runCustomCheck(name, checkFn, timeoutMs) {
|
|
|
29758
28975
|
base58Encode,
|
|
29759
28976
|
bytesToHex,
|
|
29760
28977
|
checkNetworkHealth,
|
|
29761
|
-
convertBits,
|
|
29762
|
-
createAddress,
|
|
29763
28978
|
createKeyPair,
|
|
29764
28979
|
createSphere,
|
|
29765
|
-
decodeBech32,
|
|
29766
28980
|
decrypt,
|
|
29767
28981
|
decryptDeliveryBundle,
|
|
29768
28982
|
decryptField,
|
|
@@ -29779,7 +28993,6 @@ async function runCustomCheck(name, checkFn, timeoutMs) {
|
|
|
29779
28993
|
deserializeEncrypted,
|
|
29780
28994
|
discoverAddressesImpl,
|
|
29781
28995
|
doubleSha256,
|
|
29782
|
-
encodeBech32,
|
|
29783
28996
|
encrypt,
|
|
29784
28997
|
encryptDeliveryBundle,
|
|
29785
28998
|
encryptField,
|
|
@@ -29795,7 +29008,6 @@ async function runCustomCheck(name, checkFn, timeoutMs) {
|
|
|
29795
29008
|
generateMasterKey,
|
|
29796
29009
|
generateMnemonic,
|
|
29797
29010
|
generateRandomKey,
|
|
29798
|
-
getAddressHrp,
|
|
29799
29011
|
getPublicKey,
|
|
29800
29012
|
getSphere,
|
|
29801
29013
|
hash160,
|
|
@@ -29808,7 +29020,6 @@ async function runCustomCheck(name, checkFn, timeoutMs) {
|
|
|
29808
29020
|
isEncryptedData,
|
|
29809
29021
|
isPossiblyCommittedSendOutcome,
|
|
29810
29022
|
isSphereError,
|
|
29811
|
-
isValidBech32,
|
|
29812
29023
|
isValidNametag,
|
|
29813
29024
|
isValidPrivateKey,
|
|
29814
29025
|
loadSphere,
|