@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.d.cts
CHANGED
|
@@ -28,7 +28,8 @@ declare enum SDKErrorCode {
|
|
|
28
28
|
WALLET_RETRIEVAL_FAILED = "WALLET_RETRIEVAL_FAILED",
|
|
29
29
|
NOT_LOGGED_IN = "NOT_LOGGED_IN",
|
|
30
30
|
NETWORK_SWITCH_FAILED = "NETWORK_SWITCH_FAILED",
|
|
31
|
-
TRANSACTION_FAILED = "TRANSACTION_FAILED"
|
|
31
|
+
TRANSACTION_FAILED = "TRANSACTION_FAILED",
|
|
32
|
+
INVALID_PIN = "INVALID_PIN"
|
|
32
33
|
}
|
|
33
34
|
declare class SDKError extends Error {
|
|
34
35
|
readonly code: SDKErrorCode;
|
package/dist/index.d.ts
CHANGED
|
@@ -28,7 +28,8 @@ declare enum SDKErrorCode {
|
|
|
28
28
|
WALLET_RETRIEVAL_FAILED = "WALLET_RETRIEVAL_FAILED",
|
|
29
29
|
NOT_LOGGED_IN = "NOT_LOGGED_IN",
|
|
30
30
|
NETWORK_SWITCH_FAILED = "NETWORK_SWITCH_FAILED",
|
|
31
|
-
TRANSACTION_FAILED = "TRANSACTION_FAILED"
|
|
31
|
+
TRANSACTION_FAILED = "TRANSACTION_FAILED",
|
|
32
|
+
INVALID_PIN = "INVALID_PIN"
|
|
32
33
|
}
|
|
33
34
|
declare class SDKError extends Error {
|
|
34
35
|
readonly code: SDKErrorCode;
|
package/dist/index.js
CHANGED
|
@@ -104064,6 +104064,7 @@ var SDKErrorCode = /* @__PURE__ */ ((SDKErrorCode2) => {
|
|
|
104064
104064
|
SDKErrorCode2["NOT_LOGGED_IN"] = "NOT_LOGGED_IN";
|
|
104065
104065
|
SDKErrorCode2["NETWORK_SWITCH_FAILED"] = "NETWORK_SWITCH_FAILED";
|
|
104066
104066
|
SDKErrorCode2["TRANSACTION_FAILED"] = "TRANSACTION_FAILED";
|
|
104067
|
+
SDKErrorCode2["INVALID_PIN"] = "INVALID_PIN";
|
|
104067
104068
|
return SDKErrorCode2;
|
|
104068
104069
|
})(SDKErrorCode || {});
|
|
104069
104070
|
var SDKError = class extends Error {
|
|
@@ -104110,8 +104111,14 @@ var STORAGE_KEYS = {
|
|
|
104110
104111
|
walletAddress: (orgHost) => `${orgHost}:walletAddress`,
|
|
104111
104112
|
share2: (orgHost) => `${orgHost}:share2`,
|
|
104112
104113
|
encryptedShare2: (orgHost) => `${orgHost}:encryptedShare2`,
|
|
104113
|
-
|
|
104114
|
-
|
|
104114
|
+
// User-scoped keys to prevent cross-account mixing on the same device.
|
|
104115
|
+
deviceId: (orgHost, firebaseId) => `${orgHost}:${firebaseId}:deviceId`,
|
|
104116
|
+
encryptedShare2Device: (orgHost, firebaseId) => `${orgHost}:${firebaseId}:encryptedShare2_device`,
|
|
104117
|
+
deviceSecret: (orgHost, firebaseId) => `${orgHost}:${firebaseId}:deviceSecret`,
|
|
104118
|
+
// Legacy keys (kept for migration / cleanup)
|
|
104119
|
+
encryptedShare2DeviceLegacy: (orgHost) => `${orgHost}:encryptedShare2_device`,
|
|
104120
|
+
deviceSecretLegacy: (orgHost) => `${orgHost}:deviceSecret`,
|
|
104121
|
+
deviceIdLegacy: (orgHost) => `${orgHost}:deviceId`,
|
|
104115
104122
|
firebaseId: (orgHost) => `${orgHost}:firebaseId`,
|
|
104116
104123
|
accessToken: (orgHost) => `${orgHost}:accessToken`,
|
|
104117
104124
|
isNewUser: (orgHost) => `${orgHost}:isNewUser`
|
|
@@ -104143,28 +104150,76 @@ var WalletService = class {
|
|
|
104143
104150
|
isSixDigitPin(pin) {
|
|
104144
104151
|
return /^[0-9]{6}$/.test(pin);
|
|
104145
104152
|
}
|
|
104153
|
+
normalizeAddr(v5) {
|
|
104154
|
+
const s5 = String(v5 ?? "").trim();
|
|
104155
|
+
return s5 ? s5.toLowerCase() : "";
|
|
104156
|
+
}
|
|
104157
|
+
addressesMismatch(a5, b4) {
|
|
104158
|
+
return !!a5 && !!b4 && a5.toLowerCase() !== b4.toLowerCase();
|
|
104159
|
+
}
|
|
104160
|
+
/**
|
|
104161
|
+
* deviceId: stable identifier for the current device/browser profile.
|
|
104162
|
+
* Used to store/fetch server-side device recovery backup.
|
|
104163
|
+
*/
|
|
104164
|
+
async getOrCreateDeviceId(firebaseId) {
|
|
104165
|
+
const scopedKey = STORAGE_KEYS.deviceId(this.orgHost, firebaseId);
|
|
104166
|
+
const existing = await LocalForage.get(scopedKey);
|
|
104167
|
+
if (existing) return existing;
|
|
104168
|
+
const legacy = await LocalForage.get(
|
|
104169
|
+
STORAGE_KEYS.deviceIdLegacy(this.orgHost)
|
|
104170
|
+
);
|
|
104171
|
+
if (legacy) {
|
|
104172
|
+
await LocalForage.save(scopedKey, legacy);
|
|
104173
|
+
return legacy;
|
|
104174
|
+
}
|
|
104175
|
+
const id = randomBytes(16).toString("hex");
|
|
104176
|
+
await LocalForage.save(scopedKey, id);
|
|
104177
|
+
return id;
|
|
104178
|
+
}
|
|
104146
104179
|
/**
|
|
104147
|
-
* deviceSecret:
|
|
104148
|
-
*
|
|
104180
|
+
* deviceSecret: device recovery secret. In the original SDK this was local-only.
|
|
104181
|
+
* To enable PIN reset after local storage wipe, we back it up to the server
|
|
104182
|
+
* (server should encrypt-at-rest; transport is TLS).
|
|
104149
104183
|
*/
|
|
104150
|
-
async getOrCreateDeviceSecret() {
|
|
104151
|
-
const
|
|
104152
|
-
const existing = await LocalForage.get(
|
|
104184
|
+
async getOrCreateDeviceSecret(firebaseId) {
|
|
104185
|
+
const scopedKey = STORAGE_KEYS.deviceSecret(this.orgHost, firebaseId);
|
|
104186
|
+
const existing = await LocalForage.get(scopedKey);
|
|
104153
104187
|
if (existing) return existing;
|
|
104188
|
+
const legacy = await LocalForage.get(
|
|
104189
|
+
STORAGE_KEYS.deviceSecretLegacy(this.orgHost)
|
|
104190
|
+
);
|
|
104191
|
+
if (legacy) {
|
|
104192
|
+
await LocalForage.save(scopedKey, legacy);
|
|
104193
|
+
return legacy;
|
|
104194
|
+
}
|
|
104154
104195
|
const secret = randomBytes(32).toString("hex");
|
|
104155
|
-
await LocalForage.save(
|
|
104196
|
+
await LocalForage.save(scopedKey, secret);
|
|
104156
104197
|
return secret;
|
|
104157
104198
|
}
|
|
104158
104199
|
/**
|
|
104159
|
-
* PATCH APPLIED:
|
|
104160
104200
|
* Always overwrite encryptedShare2_device when we have a fresh share2.
|
|
104161
|
-
*
|
|
104201
|
+
* Also upsert to server so that PIN reset can work after local storage wipe.
|
|
104202
|
+
*
|
|
104203
|
+
* Note: Server backup is best-effort; we do not fail the main flow if backup fails.
|
|
104162
104204
|
*/
|
|
104163
104205
|
async ensureDeviceEncryptedShare2(share2Plain, firebaseId) {
|
|
104164
|
-
const
|
|
104165
|
-
const deviceSecret = await this.getOrCreateDeviceSecret();
|
|
104206
|
+
const deviceId = await this.getOrCreateDeviceId(firebaseId);
|
|
104207
|
+
const deviceSecret = await this.getOrCreateDeviceSecret(firebaseId);
|
|
104208
|
+
const encryptedKey = STORAGE_KEYS.encryptedShare2Device(
|
|
104209
|
+
this.orgHost,
|
|
104210
|
+
firebaseId
|
|
104211
|
+
);
|
|
104166
104212
|
const encrypted = Crypto.encryptShare(share2Plain, deviceSecret, firebaseId);
|
|
104167
104213
|
await LocalForage.save(encryptedKey, encrypted);
|
|
104214
|
+
try {
|
|
104215
|
+
await this.walletClient.upsertDeviceRecovery({
|
|
104216
|
+
deviceId,
|
|
104217
|
+
encryptedShare2Device: encrypted,
|
|
104218
|
+
deviceSecret
|
|
104219
|
+
});
|
|
104220
|
+
} catch (e7) {
|
|
104221
|
+
console.warn("[WalletService] device recovery backup upsert failed", e7);
|
|
104222
|
+
}
|
|
104168
104223
|
}
|
|
104169
104224
|
async getAddress() {
|
|
104170
104225
|
try {
|
|
@@ -104288,6 +104343,7 @@ var WalletService = class {
|
|
|
104288
104343
|
}
|
|
104289
104344
|
};
|
|
104290
104345
|
const walletInfo = await this.walletClient.getWallet();
|
|
104346
|
+
const serverAddr = this.normalizeAddr(walletInfo?.address);
|
|
104291
104347
|
let share2 = await LocalForage.get(
|
|
104292
104348
|
STORAGE_KEYS.share2(this.orgHost)
|
|
104293
104349
|
);
|
|
@@ -104307,6 +104363,13 @@ var WalletService = class {
|
|
|
104307
104363
|
share3
|
|
104308
104364
|
]);
|
|
104309
104365
|
const wallet2 = new Wallet(privateKey2);
|
|
104366
|
+
const derivedAddr2 = this.normalizeAddr(wallet2.address);
|
|
104367
|
+
if (this.addressesMismatch(serverAddr, derivedAddr2)) {
|
|
104368
|
+
throw new SDKError(
|
|
104369
|
+
`Recovered wallet address mismatch. server=${serverAddr} derived=${derivedAddr2}`,
|
|
104370
|
+
"WALLET_RECOVERY_FAILED" /* WALLET_RECOVERY_FAILED */
|
|
104371
|
+
);
|
|
104372
|
+
}
|
|
104310
104373
|
const newShares = await Secrets.split(wallet2.privateKey, 3, 2);
|
|
104311
104374
|
const [newShare1, newShare2, newShare3] = newShares;
|
|
104312
104375
|
await this.walletClient.updateWalletKey({
|
|
@@ -104337,6 +104400,13 @@ var WalletService = class {
|
|
|
104337
104400
|
share2
|
|
104338
104401
|
]);
|
|
104339
104402
|
const wallet = new Wallet(privateKey);
|
|
104403
|
+
const derivedAddr = this.normalizeAddr(wallet.address);
|
|
104404
|
+
if (this.addressesMismatch(serverAddr, derivedAddr)) {
|
|
104405
|
+
throw new SDKError(
|
|
104406
|
+
`Recovered wallet address mismatch. server=${serverAddr} derived=${derivedAddr}`,
|
|
104407
|
+
"WALLET_RECOVERY_FAILED" /* WALLET_RECOVERY_FAILED */
|
|
104408
|
+
);
|
|
104409
|
+
}
|
|
104340
104410
|
await this.ensureDeviceEncryptedShare2(share2, firebaseId);
|
|
104341
104411
|
this.walletAddress = wallet.address;
|
|
104342
104412
|
await LocalForage.save(
|
|
@@ -104357,7 +104427,8 @@ var WalletService = class {
|
|
|
104357
104427
|
}
|
|
104358
104428
|
}
|
|
104359
104429
|
/**
|
|
104360
|
-
* PIN reset (same private key/address) using
|
|
104430
|
+
* PIN reset (same private key/address) using device recovery material.
|
|
104431
|
+
* If local recovery material is missing, it attempts to restore it from server backup.
|
|
104361
104432
|
*/
|
|
104362
104433
|
async resetPin(newPassword) {
|
|
104363
104434
|
try {
|
|
@@ -104379,34 +104450,74 @@ var WalletService = class {
|
|
|
104379
104450
|
"INVALID_PARAMS" /* INVALID_PARAMS */
|
|
104380
104451
|
);
|
|
104381
104452
|
}
|
|
104382
|
-
|
|
104383
|
-
STORAGE_KEYS.encryptedShare2Device(this.orgHost)
|
|
104453
|
+
let encryptedDevice = await LocalForage.get(
|
|
104454
|
+
STORAGE_KEYS.encryptedShare2Device(this.orgHost, firebaseId)
|
|
104384
104455
|
);
|
|
104385
|
-
|
|
104386
|
-
STORAGE_KEYS.deviceSecret(this.orgHost)
|
|
104456
|
+
let deviceSecret = await LocalForage.get(
|
|
104457
|
+
STORAGE_KEYS.deviceSecret(this.orgHost, firebaseId)
|
|
104387
104458
|
);
|
|
104388
104459
|
if (!encryptedDevice || !deviceSecret) {
|
|
104389
|
-
|
|
104390
|
-
|
|
104391
|
-
|
|
104460
|
+
let backup;
|
|
104461
|
+
try {
|
|
104462
|
+
backup = await this.walletClient.getDeviceRecovery(void 0);
|
|
104463
|
+
} catch (e7) {
|
|
104464
|
+
throw new SDKError(
|
|
104465
|
+
"PIN reset is not available on this device",
|
|
104466
|
+
"RECOVERY_NOT_AVAILABLE" /* RECOVERY_NOT_AVAILABLE */,
|
|
104467
|
+
e7
|
|
104468
|
+
);
|
|
104469
|
+
}
|
|
104470
|
+
if (!backup?.found || !backup?.encryptedShare2Device || !backup?.deviceSecret) {
|
|
104471
|
+
throw new SDKError(
|
|
104472
|
+
"PIN reset is not available. No device recovery backup found for this user.",
|
|
104473
|
+
"RECOVERY_NOT_AVAILABLE" /* RECOVERY_NOT_AVAILABLE */
|
|
104474
|
+
);
|
|
104475
|
+
}
|
|
104476
|
+
encryptedDevice = backup.encryptedShare2Device;
|
|
104477
|
+
deviceSecret = backup.deviceSecret;
|
|
104478
|
+
await LocalForage.save(
|
|
104479
|
+
STORAGE_KEYS.encryptedShare2Device(this.orgHost, firebaseId),
|
|
104480
|
+
encryptedDevice
|
|
104481
|
+
);
|
|
104482
|
+
await LocalForage.save(
|
|
104483
|
+
STORAGE_KEYS.deviceSecret(this.orgHost, firebaseId),
|
|
104484
|
+
deviceSecret
|
|
104392
104485
|
);
|
|
104486
|
+
if (backup?.deviceId) {
|
|
104487
|
+
await LocalForage.save(
|
|
104488
|
+
STORAGE_KEYS.deviceId(this.orgHost, firebaseId),
|
|
104489
|
+
backup.deviceId
|
|
104490
|
+
);
|
|
104491
|
+
}
|
|
104393
104492
|
}
|
|
104394
104493
|
let share2;
|
|
104395
104494
|
try {
|
|
104396
|
-
share2 = Crypto.decryptShare(
|
|
104495
|
+
share2 = Crypto.decryptShare(
|
|
104496
|
+
encryptedDevice,
|
|
104497
|
+
deviceSecret,
|
|
104498
|
+
firebaseId
|
|
104499
|
+
);
|
|
104397
104500
|
} catch (e7) {
|
|
104398
104501
|
throw new SDKError(
|
|
104399
|
-
"PIN reset is not available
|
|
104502
|
+
"PIN reset is not available. Recovery material cannot be decrypted.",
|
|
104400
104503
|
"RECOVERY_NOT_AVAILABLE" /* RECOVERY_NOT_AVAILABLE */,
|
|
104401
104504
|
e7
|
|
104402
104505
|
);
|
|
104403
104506
|
}
|
|
104404
104507
|
const walletInfo = await this.walletClient.getWallet();
|
|
104508
|
+
const serverAddr = this.normalizeAddr(walletInfo?.address);
|
|
104405
104509
|
const privateKey = await Secrets.combine([
|
|
104406
104510
|
walletInfo.share1,
|
|
104407
104511
|
share2
|
|
104408
104512
|
]);
|
|
104409
104513
|
const wallet = new Wallet(privateKey);
|
|
104514
|
+
const derivedAddr = this.normalizeAddr(wallet.address);
|
|
104515
|
+
if (this.addressesMismatch(serverAddr, derivedAddr)) {
|
|
104516
|
+
throw new SDKError(
|
|
104517
|
+
`Device recovery does not match server wallet. server=${serverAddr} derived=${derivedAddr}`,
|
|
104518
|
+
"RECOVERY_NOT_AVAILABLE" /* RECOVERY_NOT_AVAILABLE */
|
|
104519
|
+
);
|
|
104520
|
+
}
|
|
104410
104521
|
const newShares = await Secrets.split(wallet.privateKey, 3, 2);
|
|
104411
104522
|
const [newShare1, newShare2, newShare3] = newShares;
|
|
104412
104523
|
await this.walletClient.updateWalletKey({
|
|
@@ -104443,8 +104554,23 @@ var WalletService = class {
|
|
|
104443
104554
|
await LocalForage.delete(STORAGE_KEYS.walletAddress(this.orgHost));
|
|
104444
104555
|
await LocalForage.delete(STORAGE_KEYS.share2(this.orgHost));
|
|
104445
104556
|
await LocalForage.delete(STORAGE_KEYS.encryptedShare2(this.orgHost));
|
|
104446
|
-
await LocalForage.
|
|
104447
|
-
|
|
104557
|
+
const firebaseId = await LocalForage.get(
|
|
104558
|
+
STORAGE_KEYS.firebaseId(this.orgHost)
|
|
104559
|
+
);
|
|
104560
|
+
if (firebaseId) {
|
|
104561
|
+
await LocalForage.delete(
|
|
104562
|
+
STORAGE_KEYS.encryptedShare2Device(this.orgHost, firebaseId)
|
|
104563
|
+
);
|
|
104564
|
+
await LocalForage.delete(
|
|
104565
|
+
STORAGE_KEYS.deviceSecret(this.orgHost, firebaseId)
|
|
104566
|
+
);
|
|
104567
|
+
await LocalForage.delete(STORAGE_KEYS.deviceId(this.orgHost, firebaseId));
|
|
104568
|
+
}
|
|
104569
|
+
await LocalForage.delete(
|
|
104570
|
+
STORAGE_KEYS.encryptedShare2DeviceLegacy(this.orgHost)
|
|
104571
|
+
);
|
|
104572
|
+
await LocalForage.delete(STORAGE_KEYS.deviceSecretLegacy(this.orgHost));
|
|
104573
|
+
await LocalForage.delete(STORAGE_KEYS.deviceIdLegacy(this.orgHost));
|
|
104448
104574
|
}
|
|
104449
104575
|
async getBalance(address, chainId) {
|
|
104450
104576
|
const response = await this.rpcClient.sendRpc({
|
|
@@ -104581,7 +104707,7 @@ var WalletService = class {
|
|
|
104581
104707
|
}
|
|
104582
104708
|
}
|
|
104583
104709
|
/**
|
|
104584
|
-
*
|
|
104710
|
+
* Critical safety:
|
|
104585
104711
|
* - Never overwrite cached walletAddress with derived signer address.
|
|
104586
104712
|
* - If server/cached address != derived address => throw WALLET_RECOVERY_FAILED.
|
|
104587
104713
|
*/
|
|
@@ -104609,19 +104735,21 @@ var WalletService = class {
|
|
|
104609
104735
|
const firebaseId = await LocalForage.get(
|
|
104610
104736
|
STORAGE_KEYS.firebaseId(this.orgHost)
|
|
104611
104737
|
);
|
|
104612
|
-
|
|
104613
|
-
|
|
104614
|
-
|
|
104615
|
-
const deviceSecret = await LocalForage.get(
|
|
104616
|
-
STORAGE_KEYS.deviceSecret(this.orgHost)
|
|
104617
|
-
);
|
|
104618
|
-
if (firebaseId && encryptedDevice && deviceSecret) {
|
|
104619
|
-
share2 = Crypto.decryptShare(
|
|
104620
|
-
encryptedDevice,
|
|
104621
|
-
deviceSecret,
|
|
104622
|
-
firebaseId
|
|
104738
|
+
if (firebaseId) {
|
|
104739
|
+
const encryptedDevice = await LocalForage.get(
|
|
104740
|
+
STORAGE_KEYS.encryptedShare2Device(this.orgHost, firebaseId)
|
|
104623
104741
|
);
|
|
104624
|
-
await LocalForage.
|
|
104742
|
+
const deviceSecret = await LocalForage.get(
|
|
104743
|
+
STORAGE_KEYS.deviceSecret(this.orgHost, firebaseId)
|
|
104744
|
+
);
|
|
104745
|
+
if (encryptedDevice && deviceSecret) {
|
|
104746
|
+
share2 = Crypto.decryptShare(
|
|
104747
|
+
encryptedDevice,
|
|
104748
|
+
deviceSecret,
|
|
104749
|
+
firebaseId
|
|
104750
|
+
);
|
|
104751
|
+
await LocalForage.save(STORAGE_KEYS.share2(this.orgHost), share2);
|
|
104752
|
+
}
|
|
104625
104753
|
}
|
|
104626
104754
|
} catch {
|
|
104627
104755
|
}
|
|
@@ -105214,6 +105342,38 @@ var WalletClient = class {
|
|
|
105214
105342
|
needsAccessToken: true
|
|
105215
105343
|
});
|
|
105216
105344
|
}
|
|
105345
|
+
async getDeviceShare2Backup() {
|
|
105346
|
+
try {
|
|
105347
|
+
return await this.client.get(
|
|
105348
|
+
"/api/v1/wallet/device-share2",
|
|
105349
|
+
{ needsAccessToken: true }
|
|
105350
|
+
);
|
|
105351
|
+
} catch (e7) {
|
|
105352
|
+
if (e7?.details?.status === 404 || String(e7?.message || "").includes("404"))
|
|
105353
|
+
return null;
|
|
105354
|
+
throw e7;
|
|
105355
|
+
}
|
|
105356
|
+
}
|
|
105357
|
+
async upsertDeviceShare2Backup(body) {
|
|
105358
|
+
await this.client.put("/api/v1/wallet/device-share2", body, {
|
|
105359
|
+
needsAccessToken: true
|
|
105360
|
+
});
|
|
105361
|
+
}
|
|
105362
|
+
async upsertDeviceRecovery(req) {
|
|
105363
|
+
await this.client.put(`${this.baseUrl}/device-recovery`, req, {
|
|
105364
|
+
needsAccessToken: true
|
|
105365
|
+
});
|
|
105366
|
+
}
|
|
105367
|
+
/**
|
|
105368
|
+
* If deviceId is omitted, server returns latest.
|
|
105369
|
+
* Returns { found:false } if no backup exists.
|
|
105370
|
+
*/
|
|
105371
|
+
async getDeviceRecovery(deviceId) {
|
|
105372
|
+
const qs = deviceId ? `?deviceId=${encodeURIComponent(deviceId)}` : "";
|
|
105373
|
+
return this.client.get(`${this.baseUrl}/device-recovery${qs}`, {
|
|
105374
|
+
needsAccessToken: true
|
|
105375
|
+
});
|
|
105376
|
+
}
|
|
105217
105377
|
};
|
|
105218
105378
|
|
|
105219
105379
|
// src/clients/api/rpcs.ts
|