@beclab/olaresid 0.1.4 → 0.1.6
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/business/index.d.ts +3 -3
- package/dist/business/index.d.ts.map +1 -1
- package/dist/business/index.js +49 -64
- package/dist/business/index.js.map +1 -1
- package/dist/cli.js +3 -3
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/utils/crypto-utils.d.ts +32 -4
- package/dist/utils/crypto-utils.d.ts.map +1 -1
- package/dist/utils/crypto-utils.js +117 -30
- package/dist/utils/crypto-utils.js.map +1 -1
- package/examples/crypto-utilities.ts +3 -3
- package/examples/ed25519-jwk.ts +1 -1
- package/examples/encoding-utils.ts +96 -0
- package/examples/frontend-demo/.dockerignore +40 -0
- package/examples/frontend-demo/index.html +13 -0
- package/examples/frontend-demo/package-lock.json +5304 -0
- package/examples/frontend-demo/package.json +32 -0
- package/examples/frontend-demo/src/App.vue +1156 -0
- package/examples/frontend-demo/src/main.ts +5 -0
- package/examples/frontend-demo/src/style.css +323 -0
- package/examples/frontend-demo/tsconfig.json +24 -0
- package/examples/frontend-demo/webpack.config.js +86 -0
- package/examples/generate-mnemonic.ts +3 -3
- package/examples/register-subdomain.ts +4 -3
- package/examples/transfer-domain.ts +1 -1
- package/examples/wallet-management.ts +8 -8
- package/package.json +1 -3
- package/src/business/index.ts +46 -58
- package/src/cli.ts +3 -3
- package/src/index.ts +6 -1
- package/src/utils/crypto-utils.ts +134 -32
- package/examples/quasar-demo/.eslintrc.js +0 -23
- package/examples/quasar-demo/.quasar/app.js +0 -43
- package/examples/quasar-demo/.quasar/client-entry.js +0 -38
- package/examples/quasar-demo/.quasar/client-prefetch.js +0 -130
- package/examples/quasar-demo/.quasar/quasar-user-options.js +0 -16
- package/examples/quasar-demo/README.md +0 -49
- package/examples/quasar-demo/index.html +0 -11
- package/examples/quasar-demo/package-lock.json +0 -6407
- package/examples/quasar-demo/package.json +0 -36
- package/examples/quasar-demo/quasar.config.js +0 -73
- package/examples/quasar-demo/src/App.vue +0 -13
- package/examples/quasar-demo/src/css/app.scss +0 -1
- package/examples/quasar-demo/src/layouts/MainLayout.vue +0 -21
- package/examples/quasar-demo/src/pages/IndexPage.vue +0 -905
- package/examples/quasar-demo/src/router/index.ts +0 -25
- package/examples/quasar-demo/src/router/routes.ts +0 -11
- package/examples/quasar-demo/tsconfig.json +0 -28
package/src/business/index.ts
CHANGED
|
@@ -4,6 +4,12 @@ import { parseContractError } from '../utils/error-parser';
|
|
|
4
4
|
import { TagContext } from './tag-context';
|
|
5
5
|
import { TagTypeBuilder } from '../utils/tag-type-builder';
|
|
6
6
|
import { normalizeToDomain } from '../utils/olares-id';
|
|
7
|
+
import {
|
|
8
|
+
base64ToUint8Array,
|
|
9
|
+
uint8ArrayToHex,
|
|
10
|
+
hexToUint8Array,
|
|
11
|
+
uint8ArrayToBase64
|
|
12
|
+
} from '../utils/crypto-utils';
|
|
7
13
|
|
|
8
14
|
export interface TransactionResult<T = any> {
|
|
9
15
|
// Basic transaction information
|
|
@@ -48,7 +54,11 @@ export {
|
|
|
48
54
|
getDIDFromMnemonic,
|
|
49
55
|
generateDIDKeyData,
|
|
50
56
|
deriveDIDFromMnemonic,
|
|
51
|
-
getEd25519JwkFromMnemonic
|
|
57
|
+
getEd25519JwkFromMnemonic,
|
|
58
|
+
base64ToUint8Array,
|
|
59
|
+
uint8ArrayToHex,
|
|
60
|
+
hexToUint8Array,
|
|
61
|
+
uint8ArrayToBase64
|
|
52
62
|
} from '../utils/crypto-utils';
|
|
53
63
|
export type { RSAPublicKeyData, DIDKeyData } from '../utils/crypto-utils';
|
|
54
64
|
|
|
@@ -180,7 +190,7 @@ export class DomainContext {
|
|
|
180
190
|
* ```typescript
|
|
181
191
|
* // For parent domain "parent.com", register subdomain "child"
|
|
182
192
|
* const parentDomain = olaresId.domain('parent.com');
|
|
183
|
-
* const mnemonic = generateMnemonic(12);
|
|
193
|
+
* const mnemonic = await generateMnemonic(12);
|
|
184
194
|
*
|
|
185
195
|
* const result = await parentDomain.registerSubdomain('child', mnemonic);
|
|
186
196
|
* // This will register "child.parent.com"
|
|
@@ -266,7 +276,7 @@ export class DomainContext {
|
|
|
266
276
|
*
|
|
267
277
|
* @example
|
|
268
278
|
* ```typescript
|
|
269
|
-
* const mnemonic = generateMnemonic(12);
|
|
279
|
+
* const mnemonic = await generateMnemonic(12);
|
|
270
280
|
* const result = await domain.transfer(mnemonic);
|
|
271
281
|
* if (result.success) {
|
|
272
282
|
* console.log('Domain transferred!');
|
|
@@ -846,18 +856,22 @@ export class DomainContext {
|
|
|
846
856
|
const { Keypair } = await import('@solana/web3.js');
|
|
847
857
|
const nacl = await import('tweetnacl');
|
|
848
858
|
const { decodeUTF8 } = await import('tweetnacl-util');
|
|
859
|
+
const { base58btc } = await import('multiformats/bases/base58');
|
|
849
860
|
|
|
850
861
|
// Parse Solana private key (support both base58 and base64)
|
|
851
862
|
let solanaWallet: any;
|
|
852
863
|
try {
|
|
853
864
|
// Try base58 first (most common format for Phantom)
|
|
854
|
-
|
|
855
|
-
const
|
|
865
|
+
// Solana uses plain base58, but multiformats requires 'z' prefix for multibase
|
|
866
|
+
const base58Key = solanaPrivateKey.startsWith('z')
|
|
867
|
+
? solanaPrivateKey
|
|
868
|
+
: 'z' + solanaPrivateKey;
|
|
869
|
+
const secretKey = base58btc.decode(base58Key);
|
|
856
870
|
solanaWallet = Keypair.fromSecretKey(secretKey);
|
|
857
871
|
} catch {
|
|
858
872
|
// Try base64
|
|
859
873
|
try {
|
|
860
|
-
const secretKey =
|
|
874
|
+
const secretKey = base64ToUint8Array(solanaPrivateKey);
|
|
861
875
|
solanaWallet = Keypair.fromSecretKey(secretKey);
|
|
862
876
|
} catch {
|
|
863
877
|
// Try as JSON array
|
|
@@ -868,9 +882,9 @@ export class DomainContext {
|
|
|
868
882
|
}
|
|
869
883
|
}
|
|
870
884
|
|
|
871
|
-
// Get Solana address as bytes32
|
|
885
|
+
// Get Solana address as bytes32 (cross-platform)
|
|
872
886
|
const solanaAddressBytes =
|
|
873
|
-
'0x' + solanaWallet.publicKey.
|
|
887
|
+
'0x' + uint8ArrayToHex(solanaWallet.publicKey.toBytes());
|
|
874
888
|
|
|
875
889
|
// Get current timestamp
|
|
876
890
|
const signAt = Math.floor(Date.now() / 1000) - 30 * 60; // 30 minutes ago
|
|
@@ -930,8 +944,8 @@ export class DomainContext {
|
|
|
930
944
|
decodeUTF8(solanaMsg),
|
|
931
945
|
solanaWallet.secretKey
|
|
932
946
|
);
|
|
933
|
-
|
|
934
|
-
|
|
947
|
+
// Convert signature to hex (cross-platform)
|
|
948
|
+
const sigFromAuthAddrHex = '0x' + uint8ArrayToHex(sigFromAuthAddr);
|
|
935
949
|
|
|
936
950
|
// Call contract
|
|
937
951
|
const tx = await rootTagger.updateSolanaWallet(
|
|
@@ -979,18 +993,22 @@ export class DomainContext {
|
|
|
979
993
|
try {
|
|
980
994
|
// Import Solana libraries dynamically
|
|
981
995
|
const { Keypair } = await import('@solana/web3.js');
|
|
996
|
+
const { base58btc } = await import('multiformats/bases/base58');
|
|
982
997
|
|
|
983
998
|
// Parse Solana private key
|
|
984
999
|
let solanaWallet: any;
|
|
985
1000
|
try {
|
|
986
1001
|
// Try base58 first
|
|
987
|
-
|
|
988
|
-
const
|
|
1002
|
+
// Solana uses plain base58, but multiformats requires 'z' prefix for multibase
|
|
1003
|
+
const base58Key = solanaPrivateKey.startsWith('z')
|
|
1004
|
+
? solanaPrivateKey
|
|
1005
|
+
: 'z' + solanaPrivateKey;
|
|
1006
|
+
const secretKey = base58btc.decode(base58Key);
|
|
989
1007
|
solanaWallet = Keypair.fromSecretKey(secretKey);
|
|
990
1008
|
} catch {
|
|
991
1009
|
// Try base64
|
|
992
1010
|
try {
|
|
993
|
-
const secretKey =
|
|
1011
|
+
const secretKey = base64ToUint8Array(solanaPrivateKey);
|
|
994
1012
|
solanaWallet = Keypair.fromSecretKey(secretKey);
|
|
995
1013
|
} catch {
|
|
996
1014
|
// Try as JSON array
|
|
@@ -1001,9 +1019,9 @@ export class DomainContext {
|
|
|
1001
1019
|
}
|
|
1002
1020
|
}
|
|
1003
1021
|
|
|
1004
|
-
// Get Solana address as bytes32
|
|
1022
|
+
// Get Solana address as bytes32 (cross-platform)
|
|
1005
1023
|
const solanaAddressBytes =
|
|
1006
|
-
'0x' + solanaWallet.publicKey.
|
|
1024
|
+
'0x' + uint8ArrayToHex(solanaWallet.publicKey.toBytes());
|
|
1007
1025
|
|
|
1008
1026
|
// Get current timestamp
|
|
1009
1027
|
const signAt = Math.floor(Date.now() / 1000) - 30 * 60; // 30 minutes ago
|
|
@@ -1104,11 +1122,8 @@ export class DomainContext {
|
|
|
1104
1122
|
// Extract addresses from the result and convert to base58
|
|
1105
1123
|
// Result is array of { algorithm, addr } where addr is bytes32
|
|
1106
1124
|
return result.map((item: any) => {
|
|
1107
|
-
//
|
|
1108
|
-
const
|
|
1109
|
-
? item.addr.slice(2)
|
|
1110
|
-
: item.addr;
|
|
1111
|
-
const buffer = Buffer.from(hexStr, 'hex');
|
|
1125
|
+
// Convert hex to Uint8Array (cross-platform)
|
|
1126
|
+
const buffer = hexToUint8Array(item.addr);
|
|
1112
1127
|
// Convert to Solana public key and then to base58
|
|
1113
1128
|
return new PublicKey(buffer).toBase58();
|
|
1114
1129
|
});
|
|
@@ -1423,26 +1438,16 @@ export function bytes4ToIpv4(bytes4Hex: string): string {
|
|
|
1423
1438
|
*/
|
|
1424
1439
|
export function pemToDer(pem: string): string {
|
|
1425
1440
|
// Remove PEM headers, footers, and whitespace
|
|
1426
|
-
const
|
|
1441
|
+
const base64Str = pem
|
|
1427
1442
|
.replace(/-----BEGIN.*?-----/g, '')
|
|
1428
1443
|
.replace(/-----END.*?-----/g, '')
|
|
1429
1444
|
.replace(/\s/g, '');
|
|
1430
1445
|
|
|
1431
|
-
// Convert base64 to
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
//
|
|
1435
|
-
|
|
1436
|
-
// Node.js environment
|
|
1437
|
-
const derBuffer = Buffer.from(base64, 'base64');
|
|
1438
|
-
hexString = derBuffer.toString('hex');
|
|
1439
|
-
} else {
|
|
1440
|
-
// Browser environment
|
|
1441
|
-
const binaryString = atob(base64);
|
|
1442
|
-
hexString = Array.from(binaryString, (char) =>
|
|
1443
|
-
char.charCodeAt(0).toString(16).padStart(2, '0')
|
|
1444
|
-
).join('');
|
|
1445
|
-
}
|
|
1446
|
+
// Convert base64 to Uint8Array using cross-platform method
|
|
1447
|
+
const derBuffer = base64ToUint8Array(base64Str);
|
|
1448
|
+
|
|
1449
|
+
// Convert to hex string
|
|
1450
|
+
const hexString = uint8ArrayToHex(derBuffer);
|
|
1446
1451
|
|
|
1447
1452
|
// Convert to hex string with 0x prefix
|
|
1448
1453
|
return '0x' + hexString;
|
|
@@ -1457,31 +1462,14 @@ export function derToPem(derHex: string): string {
|
|
|
1457
1462
|
// Remove '0x' prefix if present
|
|
1458
1463
|
const hexString = derHex.startsWith('0x') ? derHex.slice(2) : derHex;
|
|
1459
1464
|
|
|
1460
|
-
// Convert hex to base64
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
// Check if running in Node.js or browser
|
|
1464
|
-
if (typeof Buffer !== 'undefined') {
|
|
1465
|
-
// Node.js environment
|
|
1466
|
-
const derBuffer = Buffer.from(hexString, 'hex');
|
|
1467
|
-
base64 = derBuffer.toString('base64');
|
|
1468
|
-
} else {
|
|
1469
|
-
// Browser environment
|
|
1470
|
-
// Convert hex string to byte array
|
|
1471
|
-
const bytes = new Uint8Array(hexString.length / 2);
|
|
1472
|
-
for (let i = 0; i < hexString.length; i += 2) {
|
|
1473
|
-
bytes[i / 2] = parseInt(hexString.substr(i, 2), 16);
|
|
1474
|
-
}
|
|
1475
|
-
// Convert byte array to binary string
|
|
1476
|
-
const binaryString = String.fromCharCode(...bytes);
|
|
1477
|
-
// Convert to base64
|
|
1478
|
-
base64 = btoa(binaryString);
|
|
1479
|
-
}
|
|
1465
|
+
// Convert hex to base64 using cross-platform methods
|
|
1466
|
+
const bytes = hexToUint8Array(hexString);
|
|
1467
|
+
const base64Str = uint8ArrayToBase64(bytes);
|
|
1480
1468
|
|
|
1481
1469
|
// Split base64 into 64-character lines for PEM format
|
|
1482
1470
|
const lines: string[] = [];
|
|
1483
|
-
for (let i = 0; i <
|
|
1484
|
-
lines.push(
|
|
1471
|
+
for (let i = 0; i < base64Str.length; i += 64) {
|
|
1472
|
+
lines.push(base64Str.slice(i, i + 64));
|
|
1485
1473
|
}
|
|
1486
1474
|
|
|
1487
1475
|
// Construct PEM format with headers and footers
|
package/src/cli.ts
CHANGED
|
@@ -1014,7 +1014,7 @@ async function registerSubdomain(
|
|
|
1014
1014
|
);
|
|
1015
1015
|
process.exit(1);
|
|
1016
1016
|
}
|
|
1017
|
-
mnemonic = generateMnemonic(wordCount);
|
|
1017
|
+
mnemonic = await generateMnemonic(wordCount);
|
|
1018
1018
|
|
|
1019
1019
|
// Save mnemonic to file
|
|
1020
1020
|
const mnemonicFile = './subdomain-mnemonic.txt';
|
|
@@ -1141,7 +1141,7 @@ async function transferDomain(
|
|
|
1141
1141
|
);
|
|
1142
1142
|
process.exit(1);
|
|
1143
1143
|
}
|
|
1144
|
-
mnemonic = generateMnemonic(wordCount);
|
|
1144
|
+
mnemonic = await generateMnemonic(wordCount);
|
|
1145
1145
|
|
|
1146
1146
|
// Save mnemonic to file
|
|
1147
1147
|
const mnemonicFile = './transfer-new-owner-mnemonic.txt';
|
|
@@ -1228,7 +1228,7 @@ async function cryptoGenerate(options: CliOptions): Promise<void> {
|
|
|
1228
1228
|
process.exit(1);
|
|
1229
1229
|
}
|
|
1230
1230
|
|
|
1231
|
-
const mnemonic = generateMnemonic(wordCount);
|
|
1231
|
+
const mnemonic = await generateMnemonic(wordCount);
|
|
1232
1232
|
|
|
1233
1233
|
// Save mnemonic to file
|
|
1234
1234
|
const outputFile = options.output || './mnemonic.txt';
|
package/src/index.ts
CHANGED
|
@@ -38,7 +38,12 @@ export {
|
|
|
38
38
|
getDIDFromMnemonic,
|
|
39
39
|
generateDIDKeyData,
|
|
40
40
|
deriveDIDFromMnemonic,
|
|
41
|
-
getEd25519JwkFromMnemonic
|
|
41
|
+
getEd25519JwkFromMnemonic,
|
|
42
|
+
// Cross-platform encoding utilities
|
|
43
|
+
base64ToUint8Array,
|
|
44
|
+
uint8ArrayToHex,
|
|
45
|
+
hexToUint8Array,
|
|
46
|
+
uint8ArrayToBase64
|
|
42
47
|
} from './business';
|
|
43
48
|
|
|
44
49
|
type Domain = PackageDomain.Domain;
|
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
* compatibility across the entire Olares ecosystem.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import * as bip39 from 'bip39';
|
|
10
9
|
import * as varint from 'varint';
|
|
11
10
|
import { base58btc } from 'multiformats/bases/base58';
|
|
12
11
|
import { base64url } from 'multiformats/bases/base64';
|
|
13
12
|
|
|
14
13
|
// Browser globals type declaration
|
|
15
14
|
declare const window: any;
|
|
15
|
+
declare const atob: (input: string) => string;
|
|
16
16
|
declare const btoa: (input: string) => string;
|
|
17
17
|
|
|
18
18
|
export interface RSAPublicKeyData {
|
|
@@ -26,6 +26,61 @@ export interface DIDKeyData {
|
|
|
26
26
|
mnemonic: string;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
// ============================================================================
|
|
30
|
+
// Cross-platform encoding utilities (Browser + Node.js compatible)
|
|
31
|
+
// ============================================================================
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Convert base64 string to Uint8Array (cross-platform)
|
|
35
|
+
* Works in both browser and Node.js environments
|
|
36
|
+
* @param base64 base64 encoded string
|
|
37
|
+
* @returns Uint8Array
|
|
38
|
+
*/
|
|
39
|
+
export function base64ToUint8Array(base64: string): Uint8Array {
|
|
40
|
+
const binaryString = atob(base64);
|
|
41
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
42
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
43
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
44
|
+
}
|
|
45
|
+
return bytes;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Convert Uint8Array to hex string (cross-platform)
|
|
50
|
+
* @param bytes Uint8Array to convert
|
|
51
|
+
* @returns hex string without '0x' prefix
|
|
52
|
+
*/
|
|
53
|
+
export function uint8ArrayToHex(bytes: Uint8Array): string {
|
|
54
|
+
return Array.from(bytes)
|
|
55
|
+
.map((b) => b.toString(16).padStart(2, '0'))
|
|
56
|
+
.join('');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Convert hex string to Uint8Array (cross-platform)
|
|
61
|
+
* @param hex hex string (with or without '0x' prefix)
|
|
62
|
+
* @returns Uint8Array
|
|
63
|
+
*/
|
|
64
|
+
export function hexToUint8Array(hex: string): Uint8Array {
|
|
65
|
+
const cleanHex = hex.startsWith('0x') ? hex.slice(2) : hex;
|
|
66
|
+
const bytes = new Uint8Array(cleanHex.length / 2);
|
|
67
|
+
for (let i = 0; i < cleanHex.length; i += 2) {
|
|
68
|
+
bytes[i / 2] = parseInt(cleanHex.slice(i, i + 2), 16);
|
|
69
|
+
}
|
|
70
|
+
return bytes;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Convert Uint8Array to base64 string (cross-platform)
|
|
75
|
+
* Works in both browser and Node.js environments
|
|
76
|
+
* @param bytes Uint8Array to convert
|
|
77
|
+
* @returns base64 encoded string
|
|
78
|
+
*/
|
|
79
|
+
export function uint8ArrayToBase64(bytes: Uint8Array): string {
|
|
80
|
+
const binaryString = String.fromCharCode(...bytes);
|
|
81
|
+
return btoa(binaryString);
|
|
82
|
+
}
|
|
83
|
+
|
|
29
84
|
// ============================================================================
|
|
30
85
|
// Trust Wallet Core Management
|
|
31
86
|
// ============================================================================
|
|
@@ -37,6 +92,7 @@ let loadingPromise: Promise<any> | null = null;
|
|
|
37
92
|
/**
|
|
38
93
|
* Load Trust Wallet Core (lazy loading)
|
|
39
94
|
* Works in both Node.js and browser environments
|
|
95
|
+
* Handles different module export formats (CommonJS vs ESM)
|
|
40
96
|
*/
|
|
41
97
|
async function loadWalletCore(): Promise<any> {
|
|
42
98
|
// Return cached instance if already loaded
|
|
@@ -52,18 +108,33 @@ async function loadWalletCore(): Promise<any> {
|
|
|
52
108
|
// Start loading
|
|
53
109
|
loadingPromise = (async () => {
|
|
54
110
|
try {
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
111
|
+
// Dynamic import works in both Node.js ESM and browser ESM
|
|
112
|
+
const WalletCoreModule = await import('@trustwallet/wallet-core');
|
|
113
|
+
|
|
114
|
+
// Handle different export formats (CommonJS vs ESM)
|
|
115
|
+
let initWasmFunc: any;
|
|
116
|
+
if (WalletCoreModule.initWasm) {
|
|
117
|
+
// ESM named export
|
|
118
|
+
initWasmFunc = WalletCoreModule.initWasm;
|
|
119
|
+
} else if (
|
|
120
|
+
WalletCoreModule.default &&
|
|
121
|
+
(WalletCoreModule.default as any).initWasm
|
|
59
122
|
) {
|
|
60
|
-
//
|
|
61
|
-
|
|
62
|
-
|
|
123
|
+
// ESM default export with initWasm
|
|
124
|
+
initWasmFunc = (WalletCoreModule.default as any).initWasm;
|
|
125
|
+
} else if (WalletCoreModule.default) {
|
|
126
|
+
// Default export is the function itself
|
|
127
|
+
initWasmFunc = WalletCoreModule.default;
|
|
128
|
+
} else {
|
|
129
|
+
// Module itself is the function
|
|
130
|
+
initWasmFunc = WalletCoreModule as any;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Call the init function
|
|
134
|
+
if (typeof initWasmFunc === 'function') {
|
|
135
|
+
walletCore = await initWasmFunc();
|
|
63
136
|
} else {
|
|
64
|
-
|
|
65
|
-
const { initWasm } = require('@trustwallet/wallet-core');
|
|
66
|
-
walletCore = await initWasm();
|
|
137
|
+
throw new Error('initWasm is not a function');
|
|
67
138
|
}
|
|
68
139
|
|
|
69
140
|
walletCoreLoaded = true;
|
|
@@ -84,23 +155,38 @@ async function loadWalletCore(): Promise<any> {
|
|
|
84
155
|
// multicodec code for Ed25519 keys (0xed)
|
|
85
156
|
const ED25519_CODEC_ID = varint.encode(parseInt('0xed', 16));
|
|
86
157
|
|
|
158
|
+
/**
|
|
159
|
+
* Simple mnemonic validation (checks word count)
|
|
160
|
+
* @param mnemonic BIP39 mnemonic phrase
|
|
161
|
+
* @returns true if mnemonic has valid word count (12, 15, 18, 21, or 24 words)
|
|
162
|
+
*/
|
|
163
|
+
function validateMnemonic(mnemonic: string): boolean {
|
|
164
|
+
const words = mnemonic.trim().split(/\s+/);
|
|
165
|
+
const validWordCounts = [12, 15, 18, 21, 24];
|
|
166
|
+
return validWordCounts.includes(words.length);
|
|
167
|
+
}
|
|
168
|
+
|
|
87
169
|
// ============================================================================
|
|
88
170
|
// Mnemonic and Key Derivation Functions
|
|
89
171
|
// ============================================================================
|
|
90
172
|
|
|
91
173
|
/**
|
|
92
|
-
* Generate a random BIP39 mnemonic phrase
|
|
174
|
+
* Generate a random BIP39 mnemonic phrase using Trust Wallet Core
|
|
175
|
+
* Works in both Node.js and browser environments
|
|
176
|
+
*
|
|
93
177
|
* @param wordCount Number of words (12, 15, 18, 21, or 24), default is 12
|
|
94
|
-
* @returns A mnemonic phrase string
|
|
178
|
+
* @returns A promise that resolves to a mnemonic phrase string
|
|
95
179
|
*
|
|
96
180
|
* @example
|
|
97
181
|
* ```typescript
|
|
98
|
-
* const mnemonic = generateMnemonic(12);
|
|
182
|
+
* const mnemonic = await generateMnemonic(12);
|
|
99
183
|
* console.log(mnemonic);
|
|
100
184
|
* // Output: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
|
|
101
185
|
* ```
|
|
102
186
|
*/
|
|
103
|
-
export function generateMnemonic(
|
|
187
|
+
export async function generateMnemonic(
|
|
188
|
+
wordCount: number = 12
|
|
189
|
+
): Promise<string> {
|
|
104
190
|
// Convert word count to entropy bits
|
|
105
191
|
// 12 words = 128 bits, 15 words = 160 bits, etc.
|
|
106
192
|
const strengthMap: Record<number, number> = {
|
|
@@ -118,7 +204,14 @@ export function generateMnemonic(wordCount: number = 12): string {
|
|
|
118
204
|
);
|
|
119
205
|
}
|
|
120
206
|
|
|
121
|
-
|
|
207
|
+
// Ensure Wallet Core is loaded
|
|
208
|
+
const core = await loadWalletCore();
|
|
209
|
+
const { HDWallet } = core;
|
|
210
|
+
|
|
211
|
+
const wallet = HDWallet.create(strength, '');
|
|
212
|
+
const mnemonic = wallet.mnemonic();
|
|
213
|
+
|
|
214
|
+
return mnemonic;
|
|
122
215
|
}
|
|
123
216
|
|
|
124
217
|
/**
|
|
@@ -138,8 +231,10 @@ export async function getEthereumAddressFromMnemonic(
|
|
|
138
231
|
mnemonic: string
|
|
139
232
|
): Promise<string> {
|
|
140
233
|
// Validate mnemonic
|
|
141
|
-
if (!
|
|
142
|
-
throw new Error(
|
|
234
|
+
if (!validateMnemonic(mnemonic)) {
|
|
235
|
+
throw new Error(
|
|
236
|
+
'Invalid mnemonic phrase: must have 12, 15, 18, 21, or 24 words'
|
|
237
|
+
);
|
|
143
238
|
}
|
|
144
239
|
|
|
145
240
|
const core = await loadWalletCore();
|
|
@@ -167,8 +262,10 @@ export async function getEVMPrivateKeyFromMnemonic(
|
|
|
167
262
|
mnemonic: string
|
|
168
263
|
): Promise<string> {
|
|
169
264
|
// Validate mnemonic
|
|
170
|
-
if (!
|
|
171
|
-
throw new Error(
|
|
265
|
+
if (!validateMnemonic(mnemonic)) {
|
|
266
|
+
throw new Error(
|
|
267
|
+
'Invalid mnemonic phrase: must have 12, 15, 18, 21, or 24 words'
|
|
268
|
+
);
|
|
172
269
|
}
|
|
173
270
|
|
|
174
271
|
const core = await loadWalletCore();
|
|
@@ -179,9 +276,9 @@ export async function getEVMPrivateKeyFromMnemonic(
|
|
|
179
276
|
// Get private key for Ethereum
|
|
180
277
|
const privateKeyData = wallet.getKeyForCoin(CoinType.ethereum);
|
|
181
278
|
|
|
182
|
-
// Convert to hex string with 0x prefix
|
|
183
|
-
const
|
|
184
|
-
|
|
279
|
+
// Convert to hex string with 0x prefix (cross-platform)
|
|
280
|
+
const privateKeyBytes = new Uint8Array(privateKeyData.data());
|
|
281
|
+
const privateKeyHex = '0x' + uint8ArrayToHex(privateKeyBytes);
|
|
185
282
|
|
|
186
283
|
return privateKeyHex;
|
|
187
284
|
}
|
|
@@ -230,8 +327,10 @@ async function getID(mnemonic: string): Promise<string> {
|
|
|
230
327
|
*/
|
|
231
328
|
export async function getDIDFromMnemonic(mnemonic: string): Promise<string> {
|
|
232
329
|
// Validate mnemonic
|
|
233
|
-
if (!
|
|
234
|
-
throw new Error(
|
|
330
|
+
if (!validateMnemonic(mnemonic)) {
|
|
331
|
+
throw new Error(
|
|
332
|
+
'Invalid mnemonic phrase: must have 12, 15, 18, 21, or 24 words'
|
|
333
|
+
);
|
|
235
334
|
}
|
|
236
335
|
|
|
237
336
|
const id = await getID(mnemonic);
|
|
@@ -278,8 +377,10 @@ export async function getEd25519JwkFromMnemonic(mnemonic: string): Promise<{
|
|
|
278
377
|
privateJwk: any;
|
|
279
378
|
}> {
|
|
280
379
|
// Validate mnemonic
|
|
281
|
-
if (!
|
|
282
|
-
throw new Error(
|
|
380
|
+
if (!validateMnemonic(mnemonic)) {
|
|
381
|
+
throw new Error(
|
|
382
|
+
'Invalid mnemonic phrase: must have 12, 15, 18, 21, or 24 words'
|
|
383
|
+
);
|
|
283
384
|
}
|
|
284
385
|
|
|
285
386
|
const core = await loadWalletCore();
|
|
@@ -300,7 +401,6 @@ export async function getEd25519JwkFromMnemonic(mnemonic: string): Promise<{
|
|
|
300
401
|
idBytes.set(publicKeyBytes, ED25519_CODEC_ID.length);
|
|
301
402
|
const id = base58btc.encode(idBytes);
|
|
302
403
|
const did = `did:key:${id}`;
|
|
303
|
-
const keyId = `${did}#${id}`;
|
|
304
404
|
|
|
305
405
|
// Base64url encode the keys
|
|
306
406
|
const x = base64url.baseEncode(publicKeyBytes);
|
|
@@ -312,7 +412,7 @@ export async function getEd25519JwkFromMnemonic(mnemonic: string): Promise<{
|
|
|
312
412
|
crv: 'Ed25519', // Curve: Ed25519
|
|
313
413
|
alg: 'EdDSA', // Algorithm: EdDSA
|
|
314
414
|
use: 'sig', // Use: signature
|
|
315
|
-
kid:
|
|
415
|
+
kid: did, // DID
|
|
316
416
|
x: x // Public key parameter
|
|
317
417
|
};
|
|
318
418
|
|
|
@@ -343,8 +443,10 @@ export async function deriveDIDFromMnemonic(mnemonic: string): Promise<{
|
|
|
343
443
|
did: string;
|
|
344
444
|
}> {
|
|
345
445
|
// Validate mnemonic once upfront
|
|
346
|
-
if (!
|
|
347
|
-
throw new Error(
|
|
446
|
+
if (!validateMnemonic(mnemonic)) {
|
|
447
|
+
throw new Error(
|
|
448
|
+
'Invalid mnemonic phrase: must have 12, 15, 18, 21, or 24 words'
|
|
449
|
+
);
|
|
348
450
|
}
|
|
349
451
|
|
|
350
452
|
// Derive both in parallel for better performance
|
|
@@ -373,7 +475,7 @@ export async function deriveDIDFromMnemonic(mnemonic: string): Promise<{
|
|
|
373
475
|
export async function generateDIDKeyData(
|
|
374
476
|
wordCount: number = 12
|
|
375
477
|
): Promise<DIDKeyData> {
|
|
376
|
-
const mnemonic = generateMnemonic(wordCount);
|
|
478
|
+
const mnemonic = await generateMnemonic(wordCount);
|
|
377
479
|
const { owner, did } = await deriveDIDFromMnemonic(mnemonic);
|
|
378
480
|
|
|
379
481
|
return {
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
root: true,
|
|
3
|
-
parserOptions: {
|
|
4
|
-
parser: '@typescript-eslint/parser',
|
|
5
|
-
ecmaVersion: 2021
|
|
6
|
-
},
|
|
7
|
-
env: {
|
|
8
|
-
browser: true,
|
|
9
|
-
es2021: true,
|
|
10
|
-
node: true
|
|
11
|
-
},
|
|
12
|
-
extends: [
|
|
13
|
-
'plugin:vue/vue3-essential',
|
|
14
|
-
'eslint:recommended',
|
|
15
|
-
'@vue/typescript/recommended',
|
|
16
|
-
'prettier'
|
|
17
|
-
],
|
|
18
|
-
plugins: ['vue', '@typescript-eslint'],
|
|
19
|
-
rules: {
|
|
20
|
-
'@typescript-eslint/no-explicit-any': 'off',
|
|
21
|
-
'@typescript-eslint/no-unused-vars': 'warn'
|
|
22
|
-
}
|
|
23
|
-
};
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
/* eslint-disable */
|
|
2
|
-
/**
|
|
3
|
-
* THIS FILE IS GENERATED AUTOMATICALLY.
|
|
4
|
-
* DO NOT EDIT.
|
|
5
|
-
*
|
|
6
|
-
* You are probably looking on adding startup/initialization code.
|
|
7
|
-
* Use "quasar new boot <name>" and add it there.
|
|
8
|
-
* One boot file per concern. Then reference the file(s) in quasar.config.js > boot:
|
|
9
|
-
* boot: ['file', ...] // do not add ".js" extension to it.
|
|
10
|
-
*
|
|
11
|
-
* Boot files are your "main.js"
|
|
12
|
-
**/
|
|
13
|
-
|
|
14
|
-
import { Quasar } from 'quasar';
|
|
15
|
-
import { markRaw } from 'vue';
|
|
16
|
-
import RootComponent from 'app/src/App.vue';
|
|
17
|
-
|
|
18
|
-
import createRouter from 'app/src/router/index';
|
|
19
|
-
|
|
20
|
-
export default async function (createAppFn, quasarUserOptions) {
|
|
21
|
-
// Create the app instance.
|
|
22
|
-
// Here we inject into it the Quasar UI, the router & possibly the store.
|
|
23
|
-
const app = createAppFn(RootComponent);
|
|
24
|
-
|
|
25
|
-
app.config.performance = true;
|
|
26
|
-
|
|
27
|
-
app.use(Quasar, quasarUserOptions);
|
|
28
|
-
|
|
29
|
-
const router = markRaw(
|
|
30
|
-
typeof createRouter === 'function'
|
|
31
|
-
? await createRouter({})
|
|
32
|
-
: createRouter
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
// Expose the app, the router and the store.
|
|
36
|
-
// Note that we are not mounting the app here, since bootstrapping will be
|
|
37
|
-
// different depending on whether we are in a browser or on the server.
|
|
38
|
-
return {
|
|
39
|
-
app,
|
|
40
|
-
|
|
41
|
-
router
|
|
42
|
-
};
|
|
43
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/* eslint-disable */
|
|
2
|
-
/**
|
|
3
|
-
* THIS FILE IS GENERATED AUTOMATICALLY.
|
|
4
|
-
* DO NOT EDIT.
|
|
5
|
-
*
|
|
6
|
-
* You are probably looking on adding startup/initialization code.
|
|
7
|
-
* Use "quasar new boot <name>" and add it there.
|
|
8
|
-
* One boot file per concern. Then reference the file(s) in quasar.config.js > boot:
|
|
9
|
-
* boot: ['file', ...] // do not add ".js" extension to it.
|
|
10
|
-
*
|
|
11
|
-
* Boot files are your "main.js"
|
|
12
|
-
**/
|
|
13
|
-
|
|
14
|
-
import { createApp } from 'vue';
|
|
15
|
-
|
|
16
|
-
import '@quasar/extras/roboto-font/roboto-font.css';
|
|
17
|
-
|
|
18
|
-
import '@quasar/extras/material-icons/material-icons.css';
|
|
19
|
-
|
|
20
|
-
// We load Quasar stylesheet file
|
|
21
|
-
import 'quasar/dist/quasar.css';
|
|
22
|
-
|
|
23
|
-
import 'src/css/app.scss';
|
|
24
|
-
|
|
25
|
-
import createQuasarApp from './app.js';
|
|
26
|
-
import quasarUserOptions from './quasar-user-options.js';
|
|
27
|
-
|
|
28
|
-
console.info('[Quasar] Running SPA.');
|
|
29
|
-
|
|
30
|
-
const publicPath = `/`;
|
|
31
|
-
|
|
32
|
-
async function start({ app, router }) {
|
|
33
|
-
app.use(router);
|
|
34
|
-
|
|
35
|
-
app.mount('#q-app');
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
createQuasarApp(createApp, quasarUserOptions).then(start);
|