@talismn/crypto 0.2.0 → 0.2.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.
@@ -0,0 +1,5 @@
1
+ type EncodeAddressOptions = {
2
+ ss58Format?: number | undefined;
3
+ };
4
+ export declare const encodeAnyAddress: (address: string, options?: EncodeAddressOptions) => string;
5
+ export {};
@@ -1,5 +1,5 @@
1
1
  export * from "./addressEncodingFromCurve";
2
- export * from "./base58";
2
+ export * from "./solana";
3
3
  export * from "./bitcoin";
4
4
  export * from "./detectAddressEncoding";
5
5
  export * from "./ethereum";
@@ -0,0 +1,2 @@
1
+ export declare const encodeAddressSolana: (publicKey: Uint8Array) => string;
2
+ export declare function isSolanaAddress(address: string): boolean;
@@ -2,4 +2,5 @@ export * from "./encoding";
2
2
  export * from "./addressFromPublicKey";
3
3
  export * from "./normalizeAddress";
4
4
  export * from "./isAddressEqual";
5
- export * from "./isValidAddress";
5
+ export * from "./encodeAnyAddress";
6
+ export * from "./isAddressValid";
@@ -0,0 +1 @@
1
+ export declare const isAddressValid: (address: string) => boolean;
@@ -1,16 +1,7 @@
1
- import { KeypairCurve } from "../types";
1
+ import { AccountPlatform, KeypairCurve } from "../types";
2
2
  export declare const deriveKeypair: (seed: Uint8Array, derivationPath: string, curve: KeypairCurve) => import("../types").Keypair;
3
3
  export declare const getPublicKeyFromSecret: (secretKey: Uint8Array, curve: KeypairCurve) => Uint8Array;
4
- export declare const addressFromSuri: (suri: string, type: KeypairCurve) => Promise<string>;
5
- /**
6
- * @dev we only expect suri to contain a mnemonic and derivation path.
7
- * for other cases see https://polkadot.js.org/docs/keyring/start/suri/
8
- */
9
- export declare const parseSuri: (suri: string) => {
10
- mnemonic: string;
11
- derivationPath: string;
12
- password: string | undefined;
13
- };
4
+ export declare const addressFromMnemonic: (mnemonic: string, derivationPath: string, curve: KeypairCurve) => Promise<string>;
14
5
  export declare const removeHexPrefix: (secretKey: string) => string;
15
- export declare const parseSecretKey: (secretKey: string, curve: KeypairCurve) => Uint8Array<ArrayBufferLike>;
6
+ export declare const parseSecretKey: (secretKey: string, platform: AccountPlatform) => Uint8Array<ArrayBufferLike>;
16
7
  export declare const isValidDerivationPath: (derivationPath: string, curve: KeypairCurve) => Promise<boolean>;
@@ -1,4 +1,4 @@
1
- import { AddressEncoding, KeypairCurve, Platform } from "../types";
2
- export declare const platformFromCurve: (curve: KeypairCurve) => Platform;
3
- export declare const platformFromEncoding: (encoding: AddressEncoding) => Platform;
4
- export declare const platformFromAddress: (address: string) => Platform;
1
+ import { AccountPlatform, AddressEncoding, KeypairCurve } from "../types";
2
+ export declare const getAccountPlatformFromCurve: (curve: KeypairCurve) => AccountPlatform;
3
+ export declare const getAccountPlatformFromEncoding: (encoding: AddressEncoding) => AccountPlatform;
4
+ export declare const getAccountPlatformFromAddress: (address: string) => AccountPlatform;
@@ -1,9 +1,9 @@
1
1
  export type KeypairCurve = "ecdsa" | "ed25519" | "sr25519" | "ethereum" | "bitcoin-ed25519" | "bitcoin-ecdsa" | "solana";
2
- export type AddressEncoding = "ss58" | "ethereum" | "bech32m" | "bech32" | "base58check" | "base58";
2
+ export type AddressEncoding = "ss58" | "ethereum" | "bech32m" | "bech32" | "base58check" | "base58solana";
3
3
  export type Keypair = {
4
4
  type: KeypairCurve;
5
5
  secretKey: Uint8Array;
6
6
  publicKey: Uint8Array;
7
7
  address: string;
8
8
  };
9
- export type Platform = "ethereum" | "polkadot" | "bitcoin" | "solana";
9
+ export type AccountPlatform = "ethereum" | "polkadot" | "bitcoin" | "solana";
@@ -1 +1,2 @@
1
- export { bytesToString, stringToBytes } from "@scure/base";
1
+ export { base58, base64, hex, utf8 } from "@scure/base";
2
+ export { ed25519 } from "@noble/curves/ed25519";
@@ -3,15 +3,15 @@
3
3
  var bip39 = require('@scure/bip39');
4
4
  var english = require('@scure/bip39/wordlists/english');
5
5
  var base = require('@scure/base');
6
+ var ed25519 = require('@noble/curves/ed25519');
7
+ var secp256k1 = require('@noble/curves/secp256k1');
6
8
  var bech32 = require('bech32');
7
9
  var bs58check = require('bs58check');
8
10
  var sha3 = require('@noble/hashes/sha3');
9
11
  var utils = require('@noble/hashes/utils');
10
12
  var blake2b = require('@noble/hashes/blake2b');
11
13
  var blake3$1 = require('@noble/hashes/blake3');
12
- var secp256k1 = require('@noble/curves/secp256k1');
13
14
  var scaleTs = require('scale-ts');
14
- var ed25519 = require('@noble/curves/ed25519');
15
15
  var bip32 = require('@scure/bip32');
16
16
  var hmac = require('@noble/hashes/hmac');
17
17
  var sha512 = require('@noble/hashes/sha512');
@@ -140,18 +140,18 @@ const addressEncodingFromCurve = curve => {
140
140
  case "ethereum":
141
141
  return "ethereum";
142
142
  case "solana":
143
- return "base58";
143
+ return "base58solana";
144
144
  }
145
145
  };
146
146
 
147
- const encodeAddressBase58 = publicKey => {
147
+ const encodeAddressSolana = publicKey => {
148
+ if (publicKey.length !== 32) throw new Error("Public key must be 32 bytes long for Solana base58 encoding");
148
149
  return base.base58.encode(publicKey);
149
150
  };
150
- /** Detect if address is base58 encoded (NOTE: also returns true for ss58 addresses) */
151
- function isBase58Address(address) {
151
+ function isSolanaAddress(address) {
152
152
  try {
153
- base.base58.decode(address);
154
- return true;
153
+ const bytes = base.base58.decode(address);
154
+ return bytes.length === 32;
155
155
  } catch (error) {
156
156
  return false;
157
157
  }
@@ -287,7 +287,7 @@ const blake2b512 = msg => blake2b.blake2b(msg, {
287
287
  const getSafeHash = bytes => {
288
288
  // cryptographically secure one way hash
289
289
  // outputs 44 characters without special characters
290
- return base.bytesToString("base58", blake3$1.blake3(bytes));
290
+ return base.base58.encode(blake3$1.blake3(bytes));
291
291
  };
292
292
 
293
293
  // Inspired from MIT licensed @polkadot-labs/hdkd-helpers
@@ -336,10 +336,10 @@ const CACHE$1 = new Map();
336
336
  const detectAddressEncodingInner = address => {
337
337
  if (isEthereumAddress(address)) return "ethereum";
338
338
  if (isSs58Address(address)) return "ss58";
339
+ if (isSolanaAddress(address)) return "base58solana";
339
340
  if (isBech32mAddress(address)) return "bech32m";
340
341
  if (isBech32Address(address)) return "bech32";
341
342
  if (isBase58CheckAddress(address)) return "base58check";
342
- if (isBase58Address(address)) return "base58";
343
343
  throw new Error(`Unknown address encoding`);
344
344
  };
345
345
  const detectAddressEncoding = address => {
@@ -353,12 +353,12 @@ const addressFromPublicKey = (publicKey, encoding, options) => {
353
353
  return encodeAddressSs58(publicKey, options?.ss58Prefix);
354
354
  case "ethereum":
355
355
  return encodeAddressEthereum(publicKey);
356
+ case "base58solana":
357
+ return encodeAddressSolana(publicKey);
356
358
  case "bech32m":
357
359
  case "bech32":
358
360
  case "base58check":
359
361
  throw new Error("addressFromPublicKey is not implemented for Bitcoin");
360
- case "base58":
361
- return encodeAddressBase58(publicKey);
362
362
  }
363
363
  };
364
364
 
@@ -382,7 +382,7 @@ const normalizeAnyAddress = address => {
382
382
  case "bech32m":
383
383
  case "bech32":
384
384
  case "base58check":
385
- case "base58":
385
+ case "base58solana":
386
386
  return address;
387
387
  case "ss58":
388
388
  {
@@ -401,9 +401,23 @@ const isAddressEqual = (address1, address2) => {
401
401
  }
402
402
  };
403
403
 
404
- const isValidAddress = address => {
404
+ const encodeAnyAddress = (address, options) => {
405
+ // this leverages cache
406
+ const encoding = detectAddressEncoding(address);
407
+
408
+ // this does NOT leverage cache
409
+ if (encoding === "ss58" && options?.ss58Format !== undefined) {
410
+ const [publicKey] = decodeSs58Address(address);
411
+ return encodeAddressSs58(publicKey, options?.ss58Format ?? 42);
412
+ }
413
+
414
+ // this leverages cache
415
+ return normalizeAddress(address);
416
+ };
417
+
418
+ const isAddressValid = address => {
405
419
  try {
406
- normalizeAddress(address);
420
+ detectAddressEncoding(address);
407
421
  return true;
408
422
  } catch {
409
423
  return false;
@@ -544,7 +558,7 @@ const deriveSolana = (seed, derivationPath) => {
544
558
  type: "solana",
545
559
  secretKey,
546
560
  publicKey,
547
- address: addressFromPublicKey(publicKey, "base58")
561
+ address: addressFromPublicKey(publicKey, "base58solana")
548
562
  };
549
563
  };
550
564
  const getPublicKeySolana = secretKey => {
@@ -602,60 +616,42 @@ const getPublicKeyFromSecret = (secretKey, curve) => {
602
616
  return getPublicKeySolana(secretKey);
603
617
  }
604
618
  };
605
- const addressFromSuri = async (suri, type) => {
606
- const {
607
- mnemonic,
608
- derivationPath,
609
- password
610
- } = parseSuri(suri);
619
+ const addressFromMnemonic = async (mnemonic, derivationPath, curve) => {
611
620
  const entropy = mnemonicToEntropy(mnemonic);
612
- const seed = await entropyToSeed(entropy, type, password); // ~80ms
621
+ const seed = await entropyToSeed(entropy, curve);
613
622
  const {
614
- secretKey
615
- } = deriveKeypair(seed, derivationPath, type);
616
- const publicKey = getPublicKeyFromSecret(secretKey, type);
617
- const encoding = addressEncodingFromCurve(type);
618
- return addressFromPublicKey(publicKey, encoding);
619
- };
620
-
621
- /**
622
- * @dev we only expect suri to contain a mnemonic and derivation path.
623
- * for other cases see https://polkadot.js.org/docs/keyring/start/suri/
624
- */
625
- const parseSuri = suri => {
626
- // extract password if any
627
- const indexOfPassword = suri.indexOf("///");
628
- const password = indexOfPassword === -1 ? undefined : suri.slice(indexOfPassword + 3);
629
- if (password) suri = suri.slice(0, indexOfPassword);
630
-
631
- // split mnemonic and derivation path
632
- const indexOfSlash = suri.indexOf("/");
633
- const mnemonic = indexOfSlash === -1 ? suri : suri.slice(0, indexOfSlash);
634
- let derivationPath = indexOfSlash === -1 ? "" : suri.slice(indexOfSlash);
635
-
636
- // if BIP44, leading slash must be removed
637
- if (derivationPath.startsWith("/m/")) derivationPath = derivationPath.slice(1);
638
- if (!isValidMnemonic(mnemonic)) throw new Error("Invalid mnemonic");
639
- return {
640
- mnemonic,
641
- derivationPath,
642
- password
643
- };
623
+ address
624
+ } = deriveKeypair(seed, derivationPath, curve);
625
+ return address;
644
626
  };
645
627
  const removeHexPrefix = secretKey => {
646
628
  if (secretKey.startsWith("0x")) return secretKey.slice(2);
647
629
  return secretKey;
648
630
  };
649
- const parseSecretKey = (secretKey, curve) => {
650
- switch (curve) {
631
+ const parseSecretKey = (secretKey, platform) => {
632
+ switch (platform) {
651
633
  case "ethereum":
652
- return base.stringToBytes("hex", removeHexPrefix(secretKey));
653
- case "ed25519":
654
- case "sr25519":
655
- case "ecdsa":
656
- case "bitcoin-ecdsa":
657
- case "bitcoin-ed25519":
634
+ {
635
+ const privateKey = removeHexPrefix(secretKey);
636
+ return base.hex.decode(privateKey);
637
+ }
658
638
  case "solana":
639
+ {
640
+ const bytes = secretKey.startsWith("[") ?
641
+ // JSON bytes array (ex: solflare)
642
+ Uint8Array.from(JSON.parse(secretKey)) :
643
+ // base58 encoded string (ex: phantom)
644
+ base.base58.decode(secretKey);
645
+ if (bytes.length === 64) {
646
+ const privateKey = bytes.slice(0, 32);
647
+ const publicKey = bytes.slice(32, 64);
648
+ const computedPublicKey = getPublicKeySolana(privateKey);
649
+ if (!publicKey.every((b, i) => b === computedPublicKey[i])) throw new Error("Invalid Solana secret key: public key does not match");
650
+ return privateKey;
651
+ } else if (bytes.length === 32) return bytes;
652
+ throw new Error("Invalid Solana secret key length");
653
+ }
654
+ default:
659
655
  throw new Error("Not implemented");
660
656
  }
661
657
  };
@@ -670,7 +666,7 @@ const isValidDerivationPath = async (derivationPath, curve) => {
670
666
  }
671
667
  };
672
668
 
673
- const platformFromCurve = curve => {
669
+ const getAccountPlatformFromCurve = curve => {
674
670
  switch (curve) {
675
671
  case "sr25519":
676
672
  case "ed25519":
@@ -685,7 +681,7 @@ const platformFromCurve = curve => {
685
681
  return "solana";
686
682
  }
687
683
  };
688
- const platformFromEncoding = encoding => {
684
+ const getAccountPlatformFromEncoding = encoding => {
689
685
  switch (encoding) {
690
686
  case "ss58":
691
687
  return "polkadot";
@@ -695,28 +691,40 @@ const platformFromEncoding = encoding => {
695
691
  case "bech32":
696
692
  case "base58check":
697
693
  return "bitcoin";
698
- case "base58":
694
+ case "base58solana":
699
695
  return "solana";
700
696
  }
701
697
  };
702
- const platformFromAddress = address => {
698
+ const getAccountPlatformFromAddress = address => {
703
699
  const encoding = detectAddressEncoding(address);
704
- return platformFromEncoding(encoding);
700
+ return getAccountPlatformFromEncoding(encoding);
705
701
  };
706
702
 
707
- Object.defineProperty(exports, "bytesToString", {
703
+ Object.defineProperty(exports, "base58", {
704
+ enumerable: true,
705
+ get: function () { return base.base58; }
706
+ });
707
+ Object.defineProperty(exports, "base64", {
708
+ enumerable: true,
709
+ get: function () { return base.base64; }
710
+ });
711
+ Object.defineProperty(exports, "hex", {
712
+ enumerable: true,
713
+ get: function () { return base.hex; }
714
+ });
715
+ Object.defineProperty(exports, "utf8", {
708
716
  enumerable: true,
709
- get: function () { return base.bytesToString; }
717
+ get: function () { return base.utf8; }
710
718
  });
711
- Object.defineProperty(exports, "stringToBytes", {
719
+ Object.defineProperty(exports, "ed25519", {
712
720
  enumerable: true,
713
- get: function () { return base.stringToBytes; }
721
+ get: function () { return ed25519.ed25519; }
714
722
  });
715
723
  exports.DEV_MNEMONIC_ETHEREUM = DEV_MNEMONIC_ETHEREUM;
716
724
  exports.DEV_MNEMONIC_POLKADOT = DEV_MNEMONIC_POLKADOT;
717
725
  exports.addressEncodingFromCurve = addressEncodingFromCurve;
726
+ exports.addressFromMnemonic = addressFromMnemonic;
718
727
  exports.addressFromPublicKey = addressFromPublicKey;
719
- exports.addressFromSuri = addressFromSuri;
720
728
  exports.blake2b256 = blake2b256;
721
729
  exports.blake2b512 = blake2b512;
722
730
  exports.blake3 = blake3;
@@ -724,35 +732,35 @@ exports.checksumEthereumAddress = checksumEthereumAddress;
724
732
  exports.decodeSs58Address = decodeSs58Address;
725
733
  exports.deriveKeypair = deriveKeypair;
726
734
  exports.detectAddressEncoding = detectAddressEncoding;
727
- exports.encodeAddressBase58 = encodeAddressBase58;
728
735
  exports.encodeAddressEthereum = encodeAddressEthereum;
736
+ exports.encodeAddressSolana = encodeAddressSolana;
729
737
  exports.encodeAddressSs58 = encodeAddressSs58;
738
+ exports.encodeAnyAddress = encodeAnyAddress;
730
739
  exports.entropyToMnemonic = entropyToMnemonic;
731
740
  exports.entropyToSeed = entropyToSeed;
732
741
  exports.fromBase58Check = fromBase58Check;
733
742
  exports.fromBech32 = fromBech32;
734
743
  exports.fromBech32m = fromBech32m;
735
744
  exports.generateMnemonic = generateMnemonic;
745
+ exports.getAccountPlatformFromAddress = getAccountPlatformFromAddress;
746
+ exports.getAccountPlatformFromCurve = getAccountPlatformFromCurve;
747
+ exports.getAccountPlatformFromEncoding = getAccountPlatformFromEncoding;
736
748
  exports.getDevSeed = getDevSeed;
737
749
  exports.getPublicKeyFromSecret = getPublicKeyFromSecret;
738
750
  exports.getSafeHash = getSafeHash;
739
751
  exports.isAddressEqual = isAddressEqual;
740
- exports.isBase58Address = isBase58Address;
752
+ exports.isAddressValid = isAddressValid;
741
753
  exports.isBase58CheckAddress = isBase58CheckAddress;
742
754
  exports.isBech32Address = isBech32Address;
743
755
  exports.isBech32mAddress = isBech32mAddress;
744
756
  exports.isBitcoinAddress = isBitcoinAddress;
745
757
  exports.isEthereumAddress = isEthereumAddress;
758
+ exports.isSolanaAddress = isSolanaAddress;
746
759
  exports.isSs58Address = isSs58Address;
747
- exports.isValidAddress = isValidAddress;
748
760
  exports.isValidDerivationPath = isValidDerivationPath;
749
761
  exports.isValidMnemonic = isValidMnemonic;
750
762
  exports.mnemonicToEntropy = mnemonicToEntropy;
751
763
  exports.normalizeAddress = normalizeAddress;
752
764
  exports.parseSecretKey = parseSecretKey;
753
- exports.parseSuri = parseSuri;
754
765
  exports.pbkdf2 = pbkdf2;
755
- exports.platformFromAddress = platformFromAddress;
756
- exports.platformFromCurve = platformFromCurve;
757
- exports.platformFromEncoding = platformFromEncoding;
758
766
  exports.removeHexPrefix = removeHexPrefix;
@@ -3,15 +3,15 @@
3
3
  var bip39 = require('@scure/bip39');
4
4
  var english = require('@scure/bip39/wordlists/english');
5
5
  var base = require('@scure/base');
6
+ var ed25519 = require('@noble/curves/ed25519');
7
+ var secp256k1 = require('@noble/curves/secp256k1');
6
8
  var bech32 = require('bech32');
7
9
  var bs58check = require('bs58check');
8
10
  var sha3 = require('@noble/hashes/sha3');
9
11
  var utils = require('@noble/hashes/utils');
10
12
  var blake2b = require('@noble/hashes/blake2b');
11
13
  var blake3$1 = require('@noble/hashes/blake3');
12
- var secp256k1 = require('@noble/curves/secp256k1');
13
14
  var scaleTs = require('scale-ts');
14
- var ed25519 = require('@noble/curves/ed25519');
15
15
  var bip32 = require('@scure/bip32');
16
16
  var hmac = require('@noble/hashes/hmac');
17
17
  var sha512 = require('@noble/hashes/sha512');
@@ -140,18 +140,18 @@ const addressEncodingFromCurve = curve => {
140
140
  case "ethereum":
141
141
  return "ethereum";
142
142
  case "solana":
143
- return "base58";
143
+ return "base58solana";
144
144
  }
145
145
  };
146
146
 
147
- const encodeAddressBase58 = publicKey => {
147
+ const encodeAddressSolana = publicKey => {
148
+ if (publicKey.length !== 32) throw new Error("Public key must be 32 bytes long for Solana base58 encoding");
148
149
  return base.base58.encode(publicKey);
149
150
  };
150
- /** Detect if address is base58 encoded (NOTE: also returns true for ss58 addresses) */
151
- function isBase58Address(address) {
151
+ function isSolanaAddress(address) {
152
152
  try {
153
- base.base58.decode(address);
154
- return true;
153
+ const bytes = base.base58.decode(address);
154
+ return bytes.length === 32;
155
155
  } catch (error) {
156
156
  return false;
157
157
  }
@@ -287,7 +287,7 @@ const blake2b512 = msg => blake2b.blake2b(msg, {
287
287
  const getSafeHash = bytes => {
288
288
  // cryptographically secure one way hash
289
289
  // outputs 44 characters without special characters
290
- return base.bytesToString("base58", blake3$1.blake3(bytes));
290
+ return base.base58.encode(blake3$1.blake3(bytes));
291
291
  };
292
292
 
293
293
  // Inspired from MIT licensed @polkadot-labs/hdkd-helpers
@@ -336,10 +336,10 @@ const CACHE$1 = new Map();
336
336
  const detectAddressEncodingInner = address => {
337
337
  if (isEthereumAddress(address)) return "ethereum";
338
338
  if (isSs58Address(address)) return "ss58";
339
+ if (isSolanaAddress(address)) return "base58solana";
339
340
  if (isBech32mAddress(address)) return "bech32m";
340
341
  if (isBech32Address(address)) return "bech32";
341
342
  if (isBase58CheckAddress(address)) return "base58check";
342
- if (isBase58Address(address)) return "base58";
343
343
  throw new Error(`Unknown address encoding`);
344
344
  };
345
345
  const detectAddressEncoding = address => {
@@ -353,12 +353,12 @@ const addressFromPublicKey = (publicKey, encoding, options) => {
353
353
  return encodeAddressSs58(publicKey, options?.ss58Prefix);
354
354
  case "ethereum":
355
355
  return encodeAddressEthereum(publicKey);
356
+ case "base58solana":
357
+ return encodeAddressSolana(publicKey);
356
358
  case "bech32m":
357
359
  case "bech32":
358
360
  case "base58check":
359
361
  throw new Error("addressFromPublicKey is not implemented for Bitcoin");
360
- case "base58":
361
- return encodeAddressBase58(publicKey);
362
362
  }
363
363
  };
364
364
 
@@ -382,7 +382,7 @@ const normalizeAnyAddress = address => {
382
382
  case "bech32m":
383
383
  case "bech32":
384
384
  case "base58check":
385
- case "base58":
385
+ case "base58solana":
386
386
  return address;
387
387
  case "ss58":
388
388
  {
@@ -401,9 +401,23 @@ const isAddressEqual = (address1, address2) => {
401
401
  }
402
402
  };
403
403
 
404
- const isValidAddress = address => {
404
+ const encodeAnyAddress = (address, options) => {
405
+ // this leverages cache
406
+ const encoding = detectAddressEncoding(address);
407
+
408
+ // this does NOT leverage cache
409
+ if (encoding === "ss58" && options?.ss58Format !== undefined) {
410
+ const [publicKey] = decodeSs58Address(address);
411
+ return encodeAddressSs58(publicKey, options?.ss58Format ?? 42);
412
+ }
413
+
414
+ // this leverages cache
415
+ return normalizeAddress(address);
416
+ };
417
+
418
+ const isAddressValid = address => {
405
419
  try {
406
- normalizeAddress(address);
420
+ detectAddressEncoding(address);
407
421
  return true;
408
422
  } catch {
409
423
  return false;
@@ -544,7 +558,7 @@ const deriveSolana = (seed, derivationPath) => {
544
558
  type: "solana",
545
559
  secretKey,
546
560
  publicKey,
547
- address: addressFromPublicKey(publicKey, "base58")
561
+ address: addressFromPublicKey(publicKey, "base58solana")
548
562
  };
549
563
  };
550
564
  const getPublicKeySolana = secretKey => {
@@ -602,60 +616,42 @@ const getPublicKeyFromSecret = (secretKey, curve) => {
602
616
  return getPublicKeySolana(secretKey);
603
617
  }
604
618
  };
605
- const addressFromSuri = async (suri, type) => {
606
- const {
607
- mnemonic,
608
- derivationPath,
609
- password
610
- } = parseSuri(suri);
619
+ const addressFromMnemonic = async (mnemonic, derivationPath, curve) => {
611
620
  const entropy = mnemonicToEntropy(mnemonic);
612
- const seed = await entropyToSeed(entropy, type, password); // ~80ms
621
+ const seed = await entropyToSeed(entropy, curve);
613
622
  const {
614
- secretKey
615
- } = deriveKeypair(seed, derivationPath, type);
616
- const publicKey = getPublicKeyFromSecret(secretKey, type);
617
- const encoding = addressEncodingFromCurve(type);
618
- return addressFromPublicKey(publicKey, encoding);
619
- };
620
-
621
- /**
622
- * @dev we only expect suri to contain a mnemonic and derivation path.
623
- * for other cases see https://polkadot.js.org/docs/keyring/start/suri/
624
- */
625
- const parseSuri = suri => {
626
- // extract password if any
627
- const indexOfPassword = suri.indexOf("///");
628
- const password = indexOfPassword === -1 ? undefined : suri.slice(indexOfPassword + 3);
629
- if (password) suri = suri.slice(0, indexOfPassword);
630
-
631
- // split mnemonic and derivation path
632
- const indexOfSlash = suri.indexOf("/");
633
- const mnemonic = indexOfSlash === -1 ? suri : suri.slice(0, indexOfSlash);
634
- let derivationPath = indexOfSlash === -1 ? "" : suri.slice(indexOfSlash);
635
-
636
- // if BIP44, leading slash must be removed
637
- if (derivationPath.startsWith("/m/")) derivationPath = derivationPath.slice(1);
638
- if (!isValidMnemonic(mnemonic)) throw new Error("Invalid mnemonic");
639
- return {
640
- mnemonic,
641
- derivationPath,
642
- password
643
- };
623
+ address
624
+ } = deriveKeypair(seed, derivationPath, curve);
625
+ return address;
644
626
  };
645
627
  const removeHexPrefix = secretKey => {
646
628
  if (secretKey.startsWith("0x")) return secretKey.slice(2);
647
629
  return secretKey;
648
630
  };
649
- const parseSecretKey = (secretKey, curve) => {
650
- switch (curve) {
631
+ const parseSecretKey = (secretKey, platform) => {
632
+ switch (platform) {
651
633
  case "ethereum":
652
- return base.stringToBytes("hex", removeHexPrefix(secretKey));
653
- case "ed25519":
654
- case "sr25519":
655
- case "ecdsa":
656
- case "bitcoin-ecdsa":
657
- case "bitcoin-ed25519":
634
+ {
635
+ const privateKey = removeHexPrefix(secretKey);
636
+ return base.hex.decode(privateKey);
637
+ }
658
638
  case "solana":
639
+ {
640
+ const bytes = secretKey.startsWith("[") ?
641
+ // JSON bytes array (ex: solflare)
642
+ Uint8Array.from(JSON.parse(secretKey)) :
643
+ // base58 encoded string (ex: phantom)
644
+ base.base58.decode(secretKey);
645
+ if (bytes.length === 64) {
646
+ const privateKey = bytes.slice(0, 32);
647
+ const publicKey = bytes.slice(32, 64);
648
+ const computedPublicKey = getPublicKeySolana(privateKey);
649
+ if (!publicKey.every((b, i) => b === computedPublicKey[i])) throw new Error("Invalid Solana secret key: public key does not match");
650
+ return privateKey;
651
+ } else if (bytes.length === 32) return bytes;
652
+ throw new Error("Invalid Solana secret key length");
653
+ }
654
+ default:
659
655
  throw new Error("Not implemented");
660
656
  }
661
657
  };
@@ -670,7 +666,7 @@ const isValidDerivationPath = async (derivationPath, curve) => {
670
666
  }
671
667
  };
672
668
 
673
- const platformFromCurve = curve => {
669
+ const getAccountPlatformFromCurve = curve => {
674
670
  switch (curve) {
675
671
  case "sr25519":
676
672
  case "ed25519":
@@ -685,7 +681,7 @@ const platformFromCurve = curve => {
685
681
  return "solana";
686
682
  }
687
683
  };
688
- const platformFromEncoding = encoding => {
684
+ const getAccountPlatformFromEncoding = encoding => {
689
685
  switch (encoding) {
690
686
  case "ss58":
691
687
  return "polkadot";
@@ -695,28 +691,40 @@ const platformFromEncoding = encoding => {
695
691
  case "bech32":
696
692
  case "base58check":
697
693
  return "bitcoin";
698
- case "base58":
694
+ case "base58solana":
699
695
  return "solana";
700
696
  }
701
697
  };
702
- const platformFromAddress = address => {
698
+ const getAccountPlatformFromAddress = address => {
703
699
  const encoding = detectAddressEncoding(address);
704
- return platformFromEncoding(encoding);
700
+ return getAccountPlatformFromEncoding(encoding);
705
701
  };
706
702
 
707
- Object.defineProperty(exports, "bytesToString", {
703
+ Object.defineProperty(exports, "base58", {
704
+ enumerable: true,
705
+ get: function () { return base.base58; }
706
+ });
707
+ Object.defineProperty(exports, "base64", {
708
+ enumerable: true,
709
+ get: function () { return base.base64; }
710
+ });
711
+ Object.defineProperty(exports, "hex", {
712
+ enumerable: true,
713
+ get: function () { return base.hex; }
714
+ });
715
+ Object.defineProperty(exports, "utf8", {
708
716
  enumerable: true,
709
- get: function () { return base.bytesToString; }
717
+ get: function () { return base.utf8; }
710
718
  });
711
- Object.defineProperty(exports, "stringToBytes", {
719
+ Object.defineProperty(exports, "ed25519", {
712
720
  enumerable: true,
713
- get: function () { return base.stringToBytes; }
721
+ get: function () { return ed25519.ed25519; }
714
722
  });
715
723
  exports.DEV_MNEMONIC_ETHEREUM = DEV_MNEMONIC_ETHEREUM;
716
724
  exports.DEV_MNEMONIC_POLKADOT = DEV_MNEMONIC_POLKADOT;
717
725
  exports.addressEncodingFromCurve = addressEncodingFromCurve;
726
+ exports.addressFromMnemonic = addressFromMnemonic;
718
727
  exports.addressFromPublicKey = addressFromPublicKey;
719
- exports.addressFromSuri = addressFromSuri;
720
728
  exports.blake2b256 = blake2b256;
721
729
  exports.blake2b512 = blake2b512;
722
730
  exports.blake3 = blake3;
@@ -724,35 +732,35 @@ exports.checksumEthereumAddress = checksumEthereumAddress;
724
732
  exports.decodeSs58Address = decodeSs58Address;
725
733
  exports.deriveKeypair = deriveKeypair;
726
734
  exports.detectAddressEncoding = detectAddressEncoding;
727
- exports.encodeAddressBase58 = encodeAddressBase58;
728
735
  exports.encodeAddressEthereum = encodeAddressEthereum;
736
+ exports.encodeAddressSolana = encodeAddressSolana;
729
737
  exports.encodeAddressSs58 = encodeAddressSs58;
738
+ exports.encodeAnyAddress = encodeAnyAddress;
730
739
  exports.entropyToMnemonic = entropyToMnemonic;
731
740
  exports.entropyToSeed = entropyToSeed;
732
741
  exports.fromBase58Check = fromBase58Check;
733
742
  exports.fromBech32 = fromBech32;
734
743
  exports.fromBech32m = fromBech32m;
735
744
  exports.generateMnemonic = generateMnemonic;
745
+ exports.getAccountPlatformFromAddress = getAccountPlatformFromAddress;
746
+ exports.getAccountPlatformFromCurve = getAccountPlatformFromCurve;
747
+ exports.getAccountPlatformFromEncoding = getAccountPlatformFromEncoding;
736
748
  exports.getDevSeed = getDevSeed;
737
749
  exports.getPublicKeyFromSecret = getPublicKeyFromSecret;
738
750
  exports.getSafeHash = getSafeHash;
739
751
  exports.isAddressEqual = isAddressEqual;
740
- exports.isBase58Address = isBase58Address;
752
+ exports.isAddressValid = isAddressValid;
741
753
  exports.isBase58CheckAddress = isBase58CheckAddress;
742
754
  exports.isBech32Address = isBech32Address;
743
755
  exports.isBech32mAddress = isBech32mAddress;
744
756
  exports.isBitcoinAddress = isBitcoinAddress;
745
757
  exports.isEthereumAddress = isEthereumAddress;
758
+ exports.isSolanaAddress = isSolanaAddress;
746
759
  exports.isSs58Address = isSs58Address;
747
- exports.isValidAddress = isValidAddress;
748
760
  exports.isValidDerivationPath = isValidDerivationPath;
749
761
  exports.isValidMnemonic = isValidMnemonic;
750
762
  exports.mnemonicToEntropy = mnemonicToEntropy;
751
763
  exports.normalizeAddress = normalizeAddress;
752
764
  exports.parseSecretKey = parseSecretKey;
753
- exports.parseSuri = parseSuri;
754
765
  exports.pbkdf2 = pbkdf2;
755
- exports.platformFromAddress = platformFromAddress;
756
- exports.platformFromCurve = platformFromCurve;
757
- exports.platformFromEncoding = platformFromEncoding;
758
766
  exports.removeHexPrefix = removeHexPrefix;
@@ -1,16 +1,17 @@
1
1
  import { mnemonicToEntropy as mnemonicToEntropy$1, entropyToMnemonic as entropyToMnemonic$1, validateMnemonic, generateMnemonic as generateMnemonic$1 } from '@scure/bip39';
2
2
  import { wordlist } from '@scure/bip39/wordlists/english';
3
- import { base58, bytesToString, stringToBytes } from '@scure/base';
4
- export { bytesToString, stringToBytes } from '@scure/base';
3
+ import { base58, hex } from '@scure/base';
4
+ export { base58, base64, hex, utf8 } from '@scure/base';
5
+ import { ed25519 } from '@noble/curves/ed25519';
6
+ export { ed25519 } from '@noble/curves/ed25519';
7
+ import { secp256k1 } from '@noble/curves/secp256k1';
5
8
  import { bech32m, bech32 } from 'bech32';
6
9
  import bs58check from 'bs58check';
7
10
  import { keccak_256 } from '@noble/hashes/sha3';
8
11
  import { bytesToHex, randomBytes } from '@noble/hashes/utils';
9
12
  import { blake2b } from '@noble/hashes/blake2b';
10
13
  import { blake3 as blake3$1 } from '@noble/hashes/blake3';
11
- import { secp256k1 } from '@noble/curves/secp256k1';
12
14
  import { Tuple, str, Bytes, u32 } from 'scale-ts';
13
- import { ed25519 } from '@noble/curves/ed25519';
14
15
  import { HDKey } from '@scure/bip32';
15
16
  import { hmac } from '@noble/hashes/hmac';
16
17
  import { sha512 } from '@noble/hashes/sha512';
@@ -135,18 +136,18 @@ const addressEncodingFromCurve = curve => {
135
136
  case "ethereum":
136
137
  return "ethereum";
137
138
  case "solana":
138
- return "base58";
139
+ return "base58solana";
139
140
  }
140
141
  };
141
142
 
142
- const encodeAddressBase58 = publicKey => {
143
+ const encodeAddressSolana = publicKey => {
144
+ if (publicKey.length !== 32) throw new Error("Public key must be 32 bytes long for Solana base58 encoding");
143
145
  return base58.encode(publicKey);
144
146
  };
145
- /** Detect if address is base58 encoded (NOTE: also returns true for ss58 addresses) */
146
- function isBase58Address(address) {
147
+ function isSolanaAddress(address) {
147
148
  try {
148
- base58.decode(address);
149
- return true;
149
+ const bytes = base58.decode(address);
150
+ return bytes.length === 32;
150
151
  } catch (error) {
151
152
  return false;
152
153
  }
@@ -282,7 +283,7 @@ const blake2b512 = msg => blake2b(msg, {
282
283
  const getSafeHash = bytes => {
283
284
  // cryptographically secure one way hash
284
285
  // outputs 44 characters without special characters
285
- return bytesToString("base58", blake3$1(bytes));
286
+ return base58.encode(blake3$1(bytes));
286
287
  };
287
288
 
288
289
  // Inspired from MIT licensed @polkadot-labs/hdkd-helpers
@@ -331,10 +332,10 @@ const CACHE$1 = new Map();
331
332
  const detectAddressEncodingInner = address => {
332
333
  if (isEthereumAddress(address)) return "ethereum";
333
334
  if (isSs58Address(address)) return "ss58";
335
+ if (isSolanaAddress(address)) return "base58solana";
334
336
  if (isBech32mAddress(address)) return "bech32m";
335
337
  if (isBech32Address(address)) return "bech32";
336
338
  if (isBase58CheckAddress(address)) return "base58check";
337
- if (isBase58Address(address)) return "base58";
338
339
  throw new Error(`Unknown address encoding`);
339
340
  };
340
341
  const detectAddressEncoding = address => {
@@ -348,12 +349,12 @@ const addressFromPublicKey = (publicKey, encoding, options) => {
348
349
  return encodeAddressSs58(publicKey, options?.ss58Prefix);
349
350
  case "ethereum":
350
351
  return encodeAddressEthereum(publicKey);
352
+ case "base58solana":
353
+ return encodeAddressSolana(publicKey);
351
354
  case "bech32m":
352
355
  case "bech32":
353
356
  case "base58check":
354
357
  throw new Error("addressFromPublicKey is not implemented for Bitcoin");
355
- case "base58":
356
- return encodeAddressBase58(publicKey);
357
358
  }
358
359
  };
359
360
 
@@ -377,7 +378,7 @@ const normalizeAnyAddress = address => {
377
378
  case "bech32m":
378
379
  case "bech32":
379
380
  case "base58check":
380
- case "base58":
381
+ case "base58solana":
381
382
  return address;
382
383
  case "ss58":
383
384
  {
@@ -396,9 +397,23 @@ const isAddressEqual = (address1, address2) => {
396
397
  }
397
398
  };
398
399
 
399
- const isValidAddress = address => {
400
+ const encodeAnyAddress = (address, options) => {
401
+ // this leverages cache
402
+ const encoding = detectAddressEncoding(address);
403
+
404
+ // this does NOT leverage cache
405
+ if (encoding === "ss58" && options?.ss58Format !== undefined) {
406
+ const [publicKey] = decodeSs58Address(address);
407
+ return encodeAddressSs58(publicKey, options?.ss58Format ?? 42);
408
+ }
409
+
410
+ // this leverages cache
411
+ return normalizeAddress(address);
412
+ };
413
+
414
+ const isAddressValid = address => {
400
415
  try {
401
- normalizeAddress(address);
416
+ detectAddressEncoding(address);
402
417
  return true;
403
418
  } catch {
404
419
  return false;
@@ -539,7 +554,7 @@ const deriveSolana = (seed, derivationPath) => {
539
554
  type: "solana",
540
555
  secretKey,
541
556
  publicKey,
542
- address: addressFromPublicKey(publicKey, "base58")
557
+ address: addressFromPublicKey(publicKey, "base58solana")
543
558
  };
544
559
  };
545
560
  const getPublicKeySolana = secretKey => {
@@ -597,60 +612,42 @@ const getPublicKeyFromSecret = (secretKey, curve) => {
597
612
  return getPublicKeySolana(secretKey);
598
613
  }
599
614
  };
600
- const addressFromSuri = async (suri, type) => {
601
- const {
602
- mnemonic,
603
- derivationPath,
604
- password
605
- } = parseSuri(suri);
615
+ const addressFromMnemonic = async (mnemonic, derivationPath, curve) => {
606
616
  const entropy = mnemonicToEntropy(mnemonic);
607
- const seed = await entropyToSeed(entropy, type, password); // ~80ms
617
+ const seed = await entropyToSeed(entropy, curve);
608
618
  const {
609
- secretKey
610
- } = deriveKeypair(seed, derivationPath, type);
611
- const publicKey = getPublicKeyFromSecret(secretKey, type);
612
- const encoding = addressEncodingFromCurve(type);
613
- return addressFromPublicKey(publicKey, encoding);
614
- };
615
-
616
- /**
617
- * @dev we only expect suri to contain a mnemonic and derivation path.
618
- * for other cases see https://polkadot.js.org/docs/keyring/start/suri/
619
- */
620
- const parseSuri = suri => {
621
- // extract password if any
622
- const indexOfPassword = suri.indexOf("///");
623
- const password = indexOfPassword === -1 ? undefined : suri.slice(indexOfPassword + 3);
624
- if (password) suri = suri.slice(0, indexOfPassword);
625
-
626
- // split mnemonic and derivation path
627
- const indexOfSlash = suri.indexOf("/");
628
- const mnemonic = indexOfSlash === -1 ? suri : suri.slice(0, indexOfSlash);
629
- let derivationPath = indexOfSlash === -1 ? "" : suri.slice(indexOfSlash);
630
-
631
- // if BIP44, leading slash must be removed
632
- if (derivationPath.startsWith("/m/")) derivationPath = derivationPath.slice(1);
633
- if (!isValidMnemonic(mnemonic)) throw new Error("Invalid mnemonic");
634
- return {
635
- mnemonic,
636
- derivationPath,
637
- password
638
- };
619
+ address
620
+ } = deriveKeypair(seed, derivationPath, curve);
621
+ return address;
639
622
  };
640
623
  const removeHexPrefix = secretKey => {
641
624
  if (secretKey.startsWith("0x")) return secretKey.slice(2);
642
625
  return secretKey;
643
626
  };
644
- const parseSecretKey = (secretKey, curve) => {
645
- switch (curve) {
627
+ const parseSecretKey = (secretKey, platform) => {
628
+ switch (platform) {
646
629
  case "ethereum":
647
- return stringToBytes("hex", removeHexPrefix(secretKey));
648
- case "ed25519":
649
- case "sr25519":
650
- case "ecdsa":
651
- case "bitcoin-ecdsa":
652
- case "bitcoin-ed25519":
630
+ {
631
+ const privateKey = removeHexPrefix(secretKey);
632
+ return hex.decode(privateKey);
633
+ }
653
634
  case "solana":
635
+ {
636
+ const bytes = secretKey.startsWith("[") ?
637
+ // JSON bytes array (ex: solflare)
638
+ Uint8Array.from(JSON.parse(secretKey)) :
639
+ // base58 encoded string (ex: phantom)
640
+ base58.decode(secretKey);
641
+ if (bytes.length === 64) {
642
+ const privateKey = bytes.slice(0, 32);
643
+ const publicKey = bytes.slice(32, 64);
644
+ const computedPublicKey = getPublicKeySolana(privateKey);
645
+ if (!publicKey.every((b, i) => b === computedPublicKey[i])) throw new Error("Invalid Solana secret key: public key does not match");
646
+ return privateKey;
647
+ } else if (bytes.length === 32) return bytes;
648
+ throw new Error("Invalid Solana secret key length");
649
+ }
650
+ default:
654
651
  throw new Error("Not implemented");
655
652
  }
656
653
  };
@@ -665,7 +662,7 @@ const isValidDerivationPath = async (derivationPath, curve) => {
665
662
  }
666
663
  };
667
664
 
668
- const platformFromCurve = curve => {
665
+ const getAccountPlatformFromCurve = curve => {
669
666
  switch (curve) {
670
667
  case "sr25519":
671
668
  case "ed25519":
@@ -680,7 +677,7 @@ const platformFromCurve = curve => {
680
677
  return "solana";
681
678
  }
682
679
  };
683
- const platformFromEncoding = encoding => {
680
+ const getAccountPlatformFromEncoding = encoding => {
684
681
  switch (encoding) {
685
682
  case "ss58":
686
683
  return "polkadot";
@@ -690,13 +687,13 @@ const platformFromEncoding = encoding => {
690
687
  case "bech32":
691
688
  case "base58check":
692
689
  return "bitcoin";
693
- case "base58":
690
+ case "base58solana":
694
691
  return "solana";
695
692
  }
696
693
  };
697
- const platformFromAddress = address => {
694
+ const getAccountPlatformFromAddress = address => {
698
695
  const encoding = detectAddressEncoding(address);
699
- return platformFromEncoding(encoding);
696
+ return getAccountPlatformFromEncoding(encoding);
700
697
  };
701
698
 
702
- export { DEV_MNEMONIC_ETHEREUM, DEV_MNEMONIC_POLKADOT, addressEncodingFromCurve, addressFromPublicKey, addressFromSuri, blake2b256, blake2b512, blake3, checksumEthereumAddress, decodeSs58Address, deriveKeypair, detectAddressEncoding, encodeAddressBase58, encodeAddressEthereum, encodeAddressSs58, entropyToMnemonic, entropyToSeed, fromBase58Check, fromBech32, fromBech32m, generateMnemonic, getDevSeed, getPublicKeyFromSecret, getSafeHash, isAddressEqual, isBase58Address, isBase58CheckAddress, isBech32Address, isBech32mAddress, isBitcoinAddress, isEthereumAddress, isSs58Address, isValidAddress, isValidDerivationPath, isValidMnemonic, mnemonicToEntropy, normalizeAddress, parseSecretKey, parseSuri, pbkdf2, platformFromAddress, platformFromCurve, platformFromEncoding, removeHexPrefix };
699
+ export { DEV_MNEMONIC_ETHEREUM, DEV_MNEMONIC_POLKADOT, addressEncodingFromCurve, addressFromMnemonic, addressFromPublicKey, blake2b256, blake2b512, blake3, checksumEthereumAddress, decodeSs58Address, deriveKeypair, detectAddressEncoding, encodeAddressEthereum, encodeAddressSolana, encodeAddressSs58, encodeAnyAddress, entropyToMnemonic, entropyToSeed, fromBase58Check, fromBech32, fromBech32m, generateMnemonic, getAccountPlatformFromAddress, getAccountPlatformFromCurve, getAccountPlatformFromEncoding, getDevSeed, getPublicKeyFromSecret, getSafeHash, isAddressEqual, isAddressValid, isBase58CheckAddress, isBech32Address, isBech32mAddress, isBitcoinAddress, isEthereumAddress, isSolanaAddress, isSs58Address, isValidDerivationPath, isValidMnemonic, mnemonicToEntropy, normalizeAddress, parseSecretKey, pbkdf2, removeHexPrefix };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@talismn/crypto",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "author": "Talisman",
5
5
  "homepage": "https://talisman.xyz",
6
6
  "license": "GPL-3.0-or-later",
@@ -15,8 +15,7 @@
15
15
  "main": "dist/talismn-crypto.cjs.js",
16
16
  "module": "dist/talismn-crypto.esm.js",
17
17
  "files": [
18
- "/dist",
19
- "/plugins"
18
+ "/dist"
20
19
  ],
21
20
  "engines": {
22
21
  "node": ">=18"
@@ -55,6 +54,6 @@
55
54
  "scripts": {
56
55
  "test": "jest --detectOpenHandles",
57
56
  "lint": "eslint src --max-warnings 0",
58
- "clean": "rm -rf dist plugins/dist .turbo node_modules"
57
+ "clean": "rm -rf dist .turbo node_modules"
59
58
  }
60
59
  }
@@ -1,3 +0,0 @@
1
- export declare const encodeAddressBase58: (publicKey: Uint8Array) => string;
2
- /** Detect if address is base58 encoded (NOTE: also returns true for ss58 addresses) */
3
- export declare function isBase58Address(address: string): boolean;
@@ -1 +0,0 @@
1
- export declare const isValidAddress: (address: string) => boolean;