@mybucks.online/core 2.0.0 → 2.1.0

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 CHANGED
@@ -99,7 +99,7 @@ To make the wallet more secure and resilient against attacks, and to meet standa
99
99
 
100
100
  **Token generation (default)**
101
101
  - Legacy encoded the transfer-link token by **concatenating** passphrase, pin and network with a delimiter, which is ambiguous for some inputs.
102
- - The default uses **ABI encoding** for the payload so there is no concatenation ambiguity.
102
+ - The default uses a **compact length-prefixed** payload (version byte + lengths + UTF-8 bytes) so there is no concatenation ambiguity and the URL fragment stays short.
103
103
  - `parseToken` accepts both legacy and default token formats automatically and returns `[passphrase, pin, network, legacy]`, where `legacy` is `true` if the token was in legacy format.
104
104
 
105
105
  Use `generateHash(passphrase, pin, cb, true)` or `generateToken(passphrase, pin, network, true)` only when you need to match existing legacy wallets or tokens.
@@ -119,6 +119,6 @@ Find the docs [here](https://docs.mybucks.online).
119
119
  - https://app.mybucks.online
120
120
  passphrase: **DemoAccount5&**
121
121
  PIN: **112324**
122
- - https://app.mybucks.online/#wallet=VWnsSGRGVtb0FjY291bnQ1JgIxMTIzMjQCb3B0aW1pc20=_wNovT
123
- - https://app.mybucks.online/#wallet=1jpFD8RGVtb0FjY291bnQ1JgIxMTIzMjQCYmFzZQ==fhk-GL
122
+ - https://app.mybucks.online/#wallet=Db1zfXAg1EZW1vQWNjb3VudDUmBjExMjMyNAhvcHRpbWlzbQ==mlUEbO (default)
123
+ - https://app.mybucks.online/#wallet=VWnsSGRGVtb0FjY291bnQ1JgIxMTIzMjQCb3B0aW1pc20=_wNovT (legacy)
124
124
  - https://codesandbox.io/p/sandbox/mybucks-online-key-generation-sandbox-lt53c3
package/index.js CHANGED
@@ -135,7 +135,9 @@ export function getTronWalletAddress(hash) {
135
135
  }
136
136
 
137
137
  const URL_DELIMITER = "\u0002";
138
- const TOKEN_VERSION_ABI = 0x02;
138
+ // Version byte for the default (non-legacy) token format.
139
+ // Uses a compact length-prefixed encoding for passphrase, pin and network.
140
+ const TOKEN_VERSION_COMPACT = 0x02;
139
141
 
140
142
  const NETWORKS = [
141
143
  "ethereum",
@@ -151,12 +153,12 @@ const NETWORKS = [
151
153
  * This function generates a transfer-link token by encoding passphrase, pin and network, and adding random padding.
152
154
  * The transfer-link enables users to send their full ownership of a wallet account to another user for gifting or airdropping.
153
155
  * Passphrase and PIN are validated by length (see PASSPHRASE_MIN/MAX_LENGTH, PIN_MIN/MAX_LENGTH) and zxcvbn; invalid or weak values are rejected (returns null).
154
- * When legacy is false, payload is ABI-encoded to avoid concatenation ambiguity; when true, uses URL_DELIMITER concatenation.
156
+ * When legacy is false, payload is compact length-prefixed (version 0x02) to avoid concatenation ambiguity and keep the URL fragment short; when true, uses URL_DELIMITER concatenation.
155
157
  *
156
158
  * @param {string} passphrase - Length in [PASSPHRASE_MIN_LENGTH, PASSPHRASE_MAX_LENGTH], zxcvbn score >= 3
157
159
  * @param {string} pin - Length in [PIN_MIN_LENGTH, PIN_MAX_LENGTH], zxcvbn score >= 1
158
160
  * @param {string} network - ethereum | polygon | arbitrum | optimism | bsc | avalanche | base | tron
159
- * @param {boolean} legacy - when true, use URL_DELIMITER concatenation; when false, use ABI encoding for the payload
161
+ * @param {boolean} legacy - when true, use URL_DELIMITER concatenation; when false, use compact length-prefixed encoding for the payload
160
162
  * @returns A string formatted as a transfer-link token, which can be appended to `https://app.mybucks.online#wallet=`, or null if invalid/weak
161
163
  */
162
164
  export function generateToken(passphrase, pin, network, legacy = false) {
@@ -194,14 +196,19 @@ export function generateToken(passphrase, pin, network, legacy = false) {
194
196
  "utf-8",
195
197
  );
196
198
  } else {
197
- const encoded = abi.encode(
198
- ["string", "string", "string"],
199
- [passphrase, pin, network],
200
- );
201
- const encodedBuffer = Buffer.from(encoded.slice(2), "hex");
199
+ // Default format: compact length-prefixed encoding.
200
+ const passphraseBytes = Buffer.from(passphrase, "utf-8");
201
+ const pinBytes = Buffer.from(pin, "utf-8");
202
+ const networkBytes = Buffer.from(network, "utf-8");
203
+
202
204
  payloadBuffer = Buffer.concat([
203
- Buffer.from([TOKEN_VERSION_ABI]),
204
- encodedBuffer,
205
+ Buffer.from([TOKEN_VERSION_COMPACT]),
206
+ Buffer.from([passphraseBytes.length]),
207
+ passphraseBytes,
208
+ Buffer.from([pinBytes.length]),
209
+ pinBytes,
210
+ Buffer.from([networkBytes.length]),
211
+ networkBytes,
205
212
  ]);
206
213
  }
207
214
 
@@ -212,20 +219,24 @@ export function generateToken(passphrase, pin, network, legacy = false) {
212
219
 
213
220
  /**
214
221
  * This function parses a transfer-link token generated by generateToken().
215
- * Tokens with version byte 0x02 are ABI-decoded; otherwise payload is treated as legacy (UTF-8 + URL_DELIMITER).
222
+ * Tokens whose payload starts with 0x02 are decoded as compact length-prefixed; otherwise payload is treated as legacy (UTF-8 + URL_DELIMITER).
216
223
  * @param {string} token - token string returned by generateToken()
217
- * @returns {[string, string, string, boolean]} [passphrase, pin, network, legacy] - legacy is true if token was in legacy format, false if ABI-encoded
224
+ * @returns {[string, string, string, boolean]} [passphrase, pin, network, legacy] - legacy is true if token was in legacy format, false if compact-encoded
218
225
  */
219
226
  export function parseToken(token) {
220
227
  const payload = token.slice(6, token.length - 6);
221
228
  const decoded = Buffer.from(payload, "base64");
222
229
 
223
- if (decoded[0] === TOKEN_VERSION_ABI) {
224
- const hex = "0x" + decoded.subarray(1).toString("hex");
225
- const [passphrase, pin, network] = abi.decode(
226
- ["string", "string", "string"],
227
- hex,
228
- );
230
+ if (decoded[0] === TOKEN_VERSION_COMPACT) {
231
+ let i = 1;
232
+ const lenP = decoded[i++];
233
+ const passphrase = decoded.subarray(i, i + lenP).toString("utf-8");
234
+ i += lenP;
235
+ const lenI = decoded[i++];
236
+ const pin = decoded.subarray(i, i + lenI).toString("utf-8");
237
+ i += lenI;
238
+ const lenN = decoded[i++];
239
+ const network = decoded.subarray(i, i + lenN).toString("utf-8");
229
240
  return [passphrase, pin, network, false];
230
241
  }
231
242
  const str = decoded.toString("utf-8");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mybucks.online/core",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "Core module of Mybucks.online Crypto Wallet",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -38,7 +38,7 @@
38
38
  "ethers": "^6.13.5",
39
39
  "nanoid": "^5.0.9",
40
40
  "scrypt-js": "^3.0.1",
41
- "tronweb": "^6.0.1",
41
+ "tronweb": "^6.2.2",
42
42
  "zxcvbn": "^4.4.2"
43
43
  }
44
44
  }
@@ -21,6 +21,7 @@ const DEMO_WALLET_EVM_ADDRESS = "0xC3Bb18Ed137577e5482fA7C6AEaca8b3F68Dafba";
21
21
  const DEMO_WALLET_TRON_ADDRESS = "TTp8uDeig42XdefAoTGWTj61uNMYvEVnXR";
22
22
 
23
23
  const DEMO_LEGACY_TOKEN = "VWnsSGRGVtb0FjY291bnQ1JgIxMTIzMjQCb3B0aW1pc20=_wNovT";
24
+ const DEMO_DEFAULT_TOKEN = "Db1zfXAg1EZW1vQWNjb3VudDUmBjExMjMyNAhvcHRpbWlzbQ==mlUEbO";
24
25
 
25
26
  describe("generateHash (default)", () => {
26
27
  test("should return empty string if passphrase or pin is blank", async () => {
@@ -282,6 +283,13 @@ describe("generateToken", () => {
282
283
  );
283
284
  }
284
285
  });
286
+
287
+ test("should handle passphrase containing URL_DELIMITER when legacy=false", () => {
288
+ const delimiter = String.fromCharCode(2);
289
+ const trickyPassphrase = `Demo${delimiter}Account5&`;
290
+ const token = generateToken(trickyPassphrase, DEMO_PIN, DEMO_NETWORK, false);
291
+ assert.ok(token !== null);
292
+ });
285
293
  });
286
294
 
287
295
  describe("parseToken", () => {
@@ -310,6 +318,14 @@ describe("parseToken", () => {
310
318
  assert.strictEqual(network, DEMO_NETWORK);
311
319
  assert.strictEqual(legacy, true);
312
320
  });
321
+
322
+ test("should parse DEMO_DEFAULT_TOKEN and return DEMO_PASSPHRASE, DEMO_PIN, DEMO_NETWORK and legacy=false", () => {
323
+ const [passphrase, pin, network, legacy] = parseToken(DEMO_DEFAULT_TOKEN);
324
+ assert.strictEqual(passphrase, DEMO_PASSPHRASE);
325
+ assert.strictEqual(pin, DEMO_PIN);
326
+ assert.strictEqual(network, DEMO_NETWORK);
327
+ assert.strictEqual(legacy, false);
328
+ });
313
329
  });
314
330
 
315
331
  describe("generateToken and parseToken", () => {
@@ -325,6 +341,20 @@ describe("generateToken and parseToken", () => {
325
341
  assert.strictEqual(network, testNetwork);
326
342
  });
327
343
 
344
+ test("should round-trip passphrase containing URL_DELIMITER when legacy=false", () => {
345
+ const delimiter = String.fromCharCode(2);
346
+ const testPassphrase = `My${delimiter}-1st-car-was-a-red-Ford-2005!`;
347
+ const testPin = "909011";
348
+ const testNetwork = "polygon";
349
+ const token = generateToken(testPassphrase, testPin, testNetwork, false);
350
+
351
+ const [passphrase, pin, network, legacy] = parseToken(token);
352
+ assert.strictEqual(passphrase, testPassphrase);
353
+ assert.strictEqual(pin, testPin);
354
+ assert.strictEqual(network, testNetwork);
355
+ assert.strictEqual(legacy, false);
356
+ });
357
+
328
358
  test("should round-trip when legacy=true", () => {
329
359
  const testPassphrase = "My-1st-car-was-a-red-Ford-2005!";
330
360
  const testPin = "909011";
@@ -336,4 +366,20 @@ describe("generateToken and parseToken", () => {
336
366
  assert.strictEqual(pin, testPin);
337
367
  assert.strictEqual(network, testNetwork);
338
368
  });
369
+
370
+ test("should not safely round-trip passphrase containing URL_DELIMITER when legacy=true", () => {
371
+ const delimiter = String.fromCharCode(2);
372
+ const testPassphrase = `My${delimiter}-1st-car-was-a-red-Ford-2005!`;
373
+ const testPin = "909011";
374
+ const testNetwork = "polygon";
375
+ const token = generateToken(testPassphrase, testPin, testNetwork, true);
376
+
377
+ const [passphrase, pin, network, legacy] = parseToken(token);
378
+ assert.notStrictEqual(
379
+ passphrase,
380
+ testPassphrase,
381
+ "legacy format cannot safely encode passphrase containing URL_DELIMITER",
382
+ );
383
+ assert.strictEqual(legacy, true);
384
+ });
339
385
  });