@weblock-wallet/sdk 0.1.69 → 0.1.71
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +197 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +197 -37
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -104109,6 +104109,7 @@ var SDKErrorCode = /* @__PURE__ */ ((SDKErrorCode2) => {
|
|
|
104109
104109
|
SDKErrorCode2["NOT_LOGGED_IN"] = "NOT_LOGGED_IN";
|
|
104110
104110
|
SDKErrorCode2["NETWORK_SWITCH_FAILED"] = "NETWORK_SWITCH_FAILED";
|
|
104111
104111
|
SDKErrorCode2["TRANSACTION_FAILED"] = "TRANSACTION_FAILED";
|
|
104112
|
+
SDKErrorCode2["INVALID_PIN"] = "INVALID_PIN";
|
|
104112
104113
|
return SDKErrorCode2;
|
|
104113
104114
|
})(SDKErrorCode || {});
|
|
104114
104115
|
var SDKError = class extends Error {
|
|
@@ -104155,8 +104156,14 @@ var STORAGE_KEYS = {
|
|
|
104155
104156
|
walletAddress: (orgHost) => `${orgHost}:walletAddress`,
|
|
104156
104157
|
share2: (orgHost) => `${orgHost}:share2`,
|
|
104157
104158
|
encryptedShare2: (orgHost) => `${orgHost}:encryptedShare2`,
|
|
104158
|
-
|
|
104159
|
-
|
|
104159
|
+
// User-scoped keys to prevent cross-account mixing on the same device.
|
|
104160
|
+
deviceId: (orgHost, firebaseId) => `${orgHost}:${firebaseId}:deviceId`,
|
|
104161
|
+
encryptedShare2Device: (orgHost, firebaseId) => `${orgHost}:${firebaseId}:encryptedShare2_device`,
|
|
104162
|
+
deviceSecret: (orgHost, firebaseId) => `${orgHost}:${firebaseId}:deviceSecret`,
|
|
104163
|
+
// Legacy keys (kept for migration / cleanup)
|
|
104164
|
+
encryptedShare2DeviceLegacy: (orgHost) => `${orgHost}:encryptedShare2_device`,
|
|
104165
|
+
deviceSecretLegacy: (orgHost) => `${orgHost}:deviceSecret`,
|
|
104166
|
+
deviceIdLegacy: (orgHost) => `${orgHost}:deviceId`,
|
|
104160
104167
|
firebaseId: (orgHost) => `${orgHost}:firebaseId`,
|
|
104161
104168
|
accessToken: (orgHost) => `${orgHost}:accessToken`,
|
|
104162
104169
|
isNewUser: (orgHost) => `${orgHost}:isNewUser`
|
|
@@ -104188,28 +104195,76 @@ var WalletService = class {
|
|
|
104188
104195
|
isSixDigitPin(pin) {
|
|
104189
104196
|
return /^[0-9]{6}$/.test(pin);
|
|
104190
104197
|
}
|
|
104198
|
+
normalizeAddr(v5) {
|
|
104199
|
+
const s5 = String(v5 ?? "").trim();
|
|
104200
|
+
return s5 ? s5.toLowerCase() : "";
|
|
104201
|
+
}
|
|
104202
|
+
addressesMismatch(a5, b4) {
|
|
104203
|
+
return !!a5 && !!b4 && a5.toLowerCase() !== b4.toLowerCase();
|
|
104204
|
+
}
|
|
104205
|
+
/**
|
|
104206
|
+
* deviceId: stable identifier for the current device/browser profile.
|
|
104207
|
+
* Used to store/fetch server-side device recovery backup.
|
|
104208
|
+
*/
|
|
104209
|
+
async getOrCreateDeviceId(firebaseId) {
|
|
104210
|
+
const scopedKey = STORAGE_KEYS.deviceId(this.orgHost, firebaseId);
|
|
104211
|
+
const existing = await LocalForage.get(scopedKey);
|
|
104212
|
+
if (existing) return existing;
|
|
104213
|
+
const legacy = await LocalForage.get(
|
|
104214
|
+
STORAGE_KEYS.deviceIdLegacy(this.orgHost)
|
|
104215
|
+
);
|
|
104216
|
+
if (legacy) {
|
|
104217
|
+
await LocalForage.save(scopedKey, legacy);
|
|
104218
|
+
return legacy;
|
|
104219
|
+
}
|
|
104220
|
+
const id = randomBytes(16).toString("hex");
|
|
104221
|
+
await LocalForage.save(scopedKey, id);
|
|
104222
|
+
return id;
|
|
104223
|
+
}
|
|
104191
104224
|
/**
|
|
104192
|
-
* deviceSecret:
|
|
104193
|
-
*
|
|
104225
|
+
* deviceSecret: device recovery secret. In the original SDK this was local-only.
|
|
104226
|
+
* To enable PIN reset after local storage wipe, we back it up to the server
|
|
104227
|
+
* (server should encrypt-at-rest; transport is TLS).
|
|
104194
104228
|
*/
|
|
104195
|
-
async getOrCreateDeviceSecret() {
|
|
104196
|
-
const
|
|
104197
|
-
const existing = await LocalForage.get(
|
|
104229
|
+
async getOrCreateDeviceSecret(firebaseId) {
|
|
104230
|
+
const scopedKey = STORAGE_KEYS.deviceSecret(this.orgHost, firebaseId);
|
|
104231
|
+
const existing = await LocalForage.get(scopedKey);
|
|
104198
104232
|
if (existing) return existing;
|
|
104233
|
+
const legacy = await LocalForage.get(
|
|
104234
|
+
STORAGE_KEYS.deviceSecretLegacy(this.orgHost)
|
|
104235
|
+
);
|
|
104236
|
+
if (legacy) {
|
|
104237
|
+
await LocalForage.save(scopedKey, legacy);
|
|
104238
|
+
return legacy;
|
|
104239
|
+
}
|
|
104199
104240
|
const secret = randomBytes(32).toString("hex");
|
|
104200
|
-
await LocalForage.save(
|
|
104241
|
+
await LocalForage.save(scopedKey, secret);
|
|
104201
104242
|
return secret;
|
|
104202
104243
|
}
|
|
104203
104244
|
/**
|
|
104204
|
-
* PATCH APPLIED:
|
|
104205
104245
|
* Always overwrite encryptedShare2_device when we have a fresh share2.
|
|
104206
|
-
*
|
|
104246
|
+
* Also upsert to server so that PIN reset can work after local storage wipe.
|
|
104247
|
+
*
|
|
104248
|
+
* Note: Server backup is best-effort; we do not fail the main flow if backup fails.
|
|
104207
104249
|
*/
|
|
104208
104250
|
async ensureDeviceEncryptedShare2(share2Plain, firebaseId) {
|
|
104209
|
-
const
|
|
104210
|
-
const deviceSecret = await this.getOrCreateDeviceSecret();
|
|
104251
|
+
const deviceId = await this.getOrCreateDeviceId(firebaseId);
|
|
104252
|
+
const deviceSecret = await this.getOrCreateDeviceSecret(firebaseId);
|
|
104253
|
+
const encryptedKey = STORAGE_KEYS.encryptedShare2Device(
|
|
104254
|
+
this.orgHost,
|
|
104255
|
+
firebaseId
|
|
104256
|
+
);
|
|
104211
104257
|
const encrypted = Crypto.encryptShare(share2Plain, deviceSecret, firebaseId);
|
|
104212
104258
|
await LocalForage.save(encryptedKey, encrypted);
|
|
104259
|
+
try {
|
|
104260
|
+
await this.walletClient.upsertDeviceRecovery({
|
|
104261
|
+
deviceId,
|
|
104262
|
+
encryptedShare2Device: encrypted,
|
|
104263
|
+
deviceSecret
|
|
104264
|
+
});
|
|
104265
|
+
} catch (e7) {
|
|
104266
|
+
console.warn("[WalletService] device recovery backup upsert failed", e7);
|
|
104267
|
+
}
|
|
104213
104268
|
}
|
|
104214
104269
|
async getAddress() {
|
|
104215
104270
|
try {
|
|
@@ -104333,6 +104388,7 @@ var WalletService = class {
|
|
|
104333
104388
|
}
|
|
104334
104389
|
};
|
|
104335
104390
|
const walletInfo = await this.walletClient.getWallet();
|
|
104391
|
+
const serverAddr = this.normalizeAddr(walletInfo?.address);
|
|
104336
104392
|
let share2 = await LocalForage.get(
|
|
104337
104393
|
STORAGE_KEYS.share2(this.orgHost)
|
|
104338
104394
|
);
|
|
@@ -104352,6 +104408,13 @@ var WalletService = class {
|
|
|
104352
104408
|
share3
|
|
104353
104409
|
]);
|
|
104354
104410
|
const wallet2 = new import_ethers2.Wallet(privateKey2);
|
|
104411
|
+
const derivedAddr2 = this.normalizeAddr(wallet2.address);
|
|
104412
|
+
if (this.addressesMismatch(serverAddr, derivedAddr2)) {
|
|
104413
|
+
throw new SDKError(
|
|
104414
|
+
`Recovered wallet address mismatch. server=${serverAddr} derived=${derivedAddr2}`,
|
|
104415
|
+
"WALLET_RECOVERY_FAILED" /* WALLET_RECOVERY_FAILED */
|
|
104416
|
+
);
|
|
104417
|
+
}
|
|
104355
104418
|
const newShares = await Secrets.split(wallet2.privateKey, 3, 2);
|
|
104356
104419
|
const [newShare1, newShare2, newShare3] = newShares;
|
|
104357
104420
|
await this.walletClient.updateWalletKey({
|
|
@@ -104382,6 +104445,13 @@ var WalletService = class {
|
|
|
104382
104445
|
share2
|
|
104383
104446
|
]);
|
|
104384
104447
|
const wallet = new import_ethers2.Wallet(privateKey);
|
|
104448
|
+
const derivedAddr = this.normalizeAddr(wallet.address);
|
|
104449
|
+
if (this.addressesMismatch(serverAddr, derivedAddr)) {
|
|
104450
|
+
throw new SDKError(
|
|
104451
|
+
`Recovered wallet address mismatch. server=${serverAddr} derived=${derivedAddr}`,
|
|
104452
|
+
"WALLET_RECOVERY_FAILED" /* WALLET_RECOVERY_FAILED */
|
|
104453
|
+
);
|
|
104454
|
+
}
|
|
104385
104455
|
await this.ensureDeviceEncryptedShare2(share2, firebaseId);
|
|
104386
104456
|
this.walletAddress = wallet.address;
|
|
104387
104457
|
await LocalForage.save(
|
|
@@ -104402,7 +104472,8 @@ var WalletService = class {
|
|
|
104402
104472
|
}
|
|
104403
104473
|
}
|
|
104404
104474
|
/**
|
|
104405
|
-
* PIN reset (same private key/address) using
|
|
104475
|
+
* PIN reset (same private key/address) using device recovery material.
|
|
104476
|
+
* If local recovery material is missing, it attempts to restore it from server backup.
|
|
104406
104477
|
*/
|
|
104407
104478
|
async resetPin(newPassword) {
|
|
104408
104479
|
try {
|
|
@@ -104424,34 +104495,74 @@ var WalletService = class {
|
|
|
104424
104495
|
"INVALID_PARAMS" /* INVALID_PARAMS */
|
|
104425
104496
|
);
|
|
104426
104497
|
}
|
|
104427
|
-
|
|
104428
|
-
STORAGE_KEYS.encryptedShare2Device(this.orgHost)
|
|
104498
|
+
let encryptedDevice = await LocalForage.get(
|
|
104499
|
+
STORAGE_KEYS.encryptedShare2Device(this.orgHost, firebaseId)
|
|
104429
104500
|
);
|
|
104430
|
-
|
|
104431
|
-
STORAGE_KEYS.deviceSecret(this.orgHost)
|
|
104501
|
+
let deviceSecret = await LocalForage.get(
|
|
104502
|
+
STORAGE_KEYS.deviceSecret(this.orgHost, firebaseId)
|
|
104432
104503
|
);
|
|
104433
104504
|
if (!encryptedDevice || !deviceSecret) {
|
|
104434
|
-
|
|
104435
|
-
|
|
104436
|
-
|
|
104505
|
+
let backup;
|
|
104506
|
+
try {
|
|
104507
|
+
backup = await this.walletClient.getDeviceRecovery(void 0);
|
|
104508
|
+
} catch (e7) {
|
|
104509
|
+
throw new SDKError(
|
|
104510
|
+
"PIN reset is not available on this device",
|
|
104511
|
+
"RECOVERY_NOT_AVAILABLE" /* RECOVERY_NOT_AVAILABLE */,
|
|
104512
|
+
e7
|
|
104513
|
+
);
|
|
104514
|
+
}
|
|
104515
|
+
if (!backup?.found || !backup?.encryptedShare2Device || !backup?.deviceSecret) {
|
|
104516
|
+
throw new SDKError(
|
|
104517
|
+
"PIN reset is not available. No device recovery backup found for this user.",
|
|
104518
|
+
"RECOVERY_NOT_AVAILABLE" /* RECOVERY_NOT_AVAILABLE */
|
|
104519
|
+
);
|
|
104520
|
+
}
|
|
104521
|
+
encryptedDevice = backup.encryptedShare2Device;
|
|
104522
|
+
deviceSecret = backup.deviceSecret;
|
|
104523
|
+
await LocalForage.save(
|
|
104524
|
+
STORAGE_KEYS.encryptedShare2Device(this.orgHost, firebaseId),
|
|
104525
|
+
encryptedDevice
|
|
104526
|
+
);
|
|
104527
|
+
await LocalForage.save(
|
|
104528
|
+
STORAGE_KEYS.deviceSecret(this.orgHost, firebaseId),
|
|
104529
|
+
deviceSecret
|
|
104437
104530
|
);
|
|
104531
|
+
if (backup?.deviceId) {
|
|
104532
|
+
await LocalForage.save(
|
|
104533
|
+
STORAGE_KEYS.deviceId(this.orgHost, firebaseId),
|
|
104534
|
+
backup.deviceId
|
|
104535
|
+
);
|
|
104536
|
+
}
|
|
104438
104537
|
}
|
|
104439
104538
|
let share2;
|
|
104440
104539
|
try {
|
|
104441
|
-
share2 = Crypto.decryptShare(
|
|
104540
|
+
share2 = Crypto.decryptShare(
|
|
104541
|
+
encryptedDevice,
|
|
104542
|
+
deviceSecret,
|
|
104543
|
+
firebaseId
|
|
104544
|
+
);
|
|
104442
104545
|
} catch (e7) {
|
|
104443
104546
|
throw new SDKError(
|
|
104444
|
-
"PIN reset is not available
|
|
104547
|
+
"PIN reset is not available. Recovery material cannot be decrypted.",
|
|
104445
104548
|
"RECOVERY_NOT_AVAILABLE" /* RECOVERY_NOT_AVAILABLE */,
|
|
104446
104549
|
e7
|
|
104447
104550
|
);
|
|
104448
104551
|
}
|
|
104449
104552
|
const walletInfo = await this.walletClient.getWallet();
|
|
104553
|
+
const serverAddr = this.normalizeAddr(walletInfo?.address);
|
|
104450
104554
|
const privateKey = await Secrets.combine([
|
|
104451
104555
|
walletInfo.share1,
|
|
104452
104556
|
share2
|
|
104453
104557
|
]);
|
|
104454
104558
|
const wallet = new import_ethers2.Wallet(privateKey);
|
|
104559
|
+
const derivedAddr = this.normalizeAddr(wallet.address);
|
|
104560
|
+
if (this.addressesMismatch(serverAddr, derivedAddr)) {
|
|
104561
|
+
throw new SDKError(
|
|
104562
|
+
`Device recovery does not match server wallet. server=${serverAddr} derived=${derivedAddr}`,
|
|
104563
|
+
"RECOVERY_NOT_AVAILABLE" /* RECOVERY_NOT_AVAILABLE */
|
|
104564
|
+
);
|
|
104565
|
+
}
|
|
104455
104566
|
const newShares = await Secrets.split(wallet.privateKey, 3, 2);
|
|
104456
104567
|
const [newShare1, newShare2, newShare3] = newShares;
|
|
104457
104568
|
await this.walletClient.updateWalletKey({
|
|
@@ -104488,8 +104599,23 @@ var WalletService = class {
|
|
|
104488
104599
|
await LocalForage.delete(STORAGE_KEYS.walletAddress(this.orgHost));
|
|
104489
104600
|
await LocalForage.delete(STORAGE_KEYS.share2(this.orgHost));
|
|
104490
104601
|
await LocalForage.delete(STORAGE_KEYS.encryptedShare2(this.orgHost));
|
|
104491
|
-
await LocalForage.
|
|
104492
|
-
|
|
104602
|
+
const firebaseId = await LocalForage.get(
|
|
104603
|
+
STORAGE_KEYS.firebaseId(this.orgHost)
|
|
104604
|
+
);
|
|
104605
|
+
if (firebaseId) {
|
|
104606
|
+
await LocalForage.delete(
|
|
104607
|
+
STORAGE_KEYS.encryptedShare2Device(this.orgHost, firebaseId)
|
|
104608
|
+
);
|
|
104609
|
+
await LocalForage.delete(
|
|
104610
|
+
STORAGE_KEYS.deviceSecret(this.orgHost, firebaseId)
|
|
104611
|
+
);
|
|
104612
|
+
await LocalForage.delete(STORAGE_KEYS.deviceId(this.orgHost, firebaseId));
|
|
104613
|
+
}
|
|
104614
|
+
await LocalForage.delete(
|
|
104615
|
+
STORAGE_KEYS.encryptedShare2DeviceLegacy(this.orgHost)
|
|
104616
|
+
);
|
|
104617
|
+
await LocalForage.delete(STORAGE_KEYS.deviceSecretLegacy(this.orgHost));
|
|
104618
|
+
await LocalForage.delete(STORAGE_KEYS.deviceIdLegacy(this.orgHost));
|
|
104493
104619
|
}
|
|
104494
104620
|
async getBalance(address, chainId) {
|
|
104495
104621
|
const response = await this.rpcClient.sendRpc({
|
|
@@ -104626,7 +104752,7 @@ var WalletService = class {
|
|
|
104626
104752
|
}
|
|
104627
104753
|
}
|
|
104628
104754
|
/**
|
|
104629
|
-
*
|
|
104755
|
+
* Critical safety:
|
|
104630
104756
|
* - Never overwrite cached walletAddress with derived signer address.
|
|
104631
104757
|
* - If server/cached address != derived address => throw WALLET_RECOVERY_FAILED.
|
|
104632
104758
|
*/
|
|
@@ -104654,19 +104780,21 @@ var WalletService = class {
|
|
|
104654
104780
|
const firebaseId = await LocalForage.get(
|
|
104655
104781
|
STORAGE_KEYS.firebaseId(this.orgHost)
|
|
104656
104782
|
);
|
|
104657
|
-
|
|
104658
|
-
|
|
104659
|
-
|
|
104660
|
-
const deviceSecret = await LocalForage.get(
|
|
104661
|
-
STORAGE_KEYS.deviceSecret(this.orgHost)
|
|
104662
|
-
);
|
|
104663
|
-
if (firebaseId && encryptedDevice && deviceSecret) {
|
|
104664
|
-
share2 = Crypto.decryptShare(
|
|
104665
|
-
encryptedDevice,
|
|
104666
|
-
deviceSecret,
|
|
104667
|
-
firebaseId
|
|
104783
|
+
if (firebaseId) {
|
|
104784
|
+
const encryptedDevice = await LocalForage.get(
|
|
104785
|
+
STORAGE_KEYS.encryptedShare2Device(this.orgHost, firebaseId)
|
|
104668
104786
|
);
|
|
104669
|
-
await LocalForage.
|
|
104787
|
+
const deviceSecret = await LocalForage.get(
|
|
104788
|
+
STORAGE_KEYS.deviceSecret(this.orgHost, firebaseId)
|
|
104789
|
+
);
|
|
104790
|
+
if (encryptedDevice && deviceSecret) {
|
|
104791
|
+
share2 = Crypto.decryptShare(
|
|
104792
|
+
encryptedDevice,
|
|
104793
|
+
deviceSecret,
|
|
104794
|
+
firebaseId
|
|
104795
|
+
);
|
|
104796
|
+
await LocalForage.save(STORAGE_KEYS.share2(this.orgHost), share2);
|
|
104797
|
+
}
|
|
104670
104798
|
}
|
|
104671
104799
|
} catch {
|
|
104672
104800
|
}
|
|
@@ -105255,6 +105383,38 @@ var WalletClient = class {
|
|
|
105255
105383
|
needsAccessToken: true
|
|
105256
105384
|
});
|
|
105257
105385
|
}
|
|
105386
|
+
async getDeviceShare2Backup() {
|
|
105387
|
+
try {
|
|
105388
|
+
return await this.client.get(
|
|
105389
|
+
"/api/v1/wallet/device-share2",
|
|
105390
|
+
{ needsAccessToken: true }
|
|
105391
|
+
);
|
|
105392
|
+
} catch (e7) {
|
|
105393
|
+
if (e7?.details?.status === 404 || String(e7?.message || "").includes("404"))
|
|
105394
|
+
return null;
|
|
105395
|
+
throw e7;
|
|
105396
|
+
}
|
|
105397
|
+
}
|
|
105398
|
+
async upsertDeviceShare2Backup(body) {
|
|
105399
|
+
await this.client.put("/api/v1/wallet/device-share2", body, {
|
|
105400
|
+
needsAccessToken: true
|
|
105401
|
+
});
|
|
105402
|
+
}
|
|
105403
|
+
async upsertDeviceRecovery(req) {
|
|
105404
|
+
await this.client.put(`${this.baseUrl}/device-recovery`, req, {
|
|
105405
|
+
needsAccessToken: true
|
|
105406
|
+
});
|
|
105407
|
+
}
|
|
105408
|
+
/**
|
|
105409
|
+
* If deviceId is omitted, server returns latest.
|
|
105410
|
+
* Returns { found:false } if no backup exists.
|
|
105411
|
+
*/
|
|
105412
|
+
async getDeviceRecovery(deviceId) {
|
|
105413
|
+
const qs = deviceId ? `?deviceId=${encodeURIComponent(deviceId)}` : "";
|
|
105414
|
+
return this.client.get(`${this.baseUrl}/device-recovery${qs}`, {
|
|
105415
|
+
needsAccessToken: true
|
|
105416
|
+
});
|
|
105417
|
+
}
|
|
105258
105418
|
};
|
|
105259
105419
|
|
|
105260
105420
|
// src/clients/api/rpcs.ts
|