@talismn/crypto 0.1.2 → 0.1.4

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.
@@ -1,2 +1,3 @@
1
1
  import type { AddressEncoding, KeypairCurve } from "../../types";
2
+ /** NOTE: Try not to use this too much, it will need to change */
2
3
  export declare const addressEncodingFromCurve: (curve: KeypairCurve) => AddressEncoding;
@@ -0,0 +1,37 @@
1
+ export declare const isBitcoinAddress: (address: string) => boolean;
2
+ export declare function isBech32mAddress(address: string): boolean;
3
+ export declare function isBech32Address(address: string): boolean;
4
+ export declare function isBase58CheckAddress(address: string): boolean;
5
+ /**
6
+ * Converts a Bech32m encoded address to its corresponding data representation.
7
+ * @param address - The Bech32m encoded address.
8
+ * @returns An object containing the version, prefix, and data of the address.
9
+ * @throws {TypeError} If the address uses the wrong encoding.
10
+ */
11
+ export declare function fromBech32m(address: string): {
12
+ version: number;
13
+ prefix: string;
14
+ data: Uint8Array<ArrayBuffer>;
15
+ };
16
+ /**
17
+ * Converts a Bech32 encoded address to its corresponding data representation.
18
+ * @param address - The Bech32 encoded address.
19
+ * @returns An object containing the version, prefix, and data of the address.
20
+ * @throws {TypeError} If the address uses the wrong encoding.
21
+ */
22
+ export declare function fromBech32(address: string): {
23
+ version: number;
24
+ prefix: string;
25
+ data: Uint8Array<ArrayBuffer>;
26
+ };
27
+ /**
28
+ * Decodes a base58check encoded Bitcoin address and returns the version and hash.
29
+ *
30
+ * @param address - The base58check encoded Bitcoin address to decode.
31
+ * @returns An object containing the version and hash of the decoded address.
32
+ * @throws {TypeError} If the address is too short or too long.
33
+ */
34
+ export declare function fromBase58Check(address: string): {
35
+ version: number;
36
+ hash: Uint8Array<ArrayBuffer>;
37
+ };
@@ -1,5 +1,6 @@
1
+ export * from "./addressEncodingFromCurve";
1
2
  export * from "./base58";
3
+ export * from "./bitcoin";
4
+ export * from "./detectAddressEncoding";
2
5
  export * from "./ethereum";
3
6
  export * from "./ss58";
4
- export * from "./addressEncodingFromCurve";
5
- export * from "./detectAddressEncoding";
@@ -1,9 +1,9 @@
1
- export type KeypairCurve = "ecdsa" | "ed25519" | "sr25519" | "ethereum" | "solana";
2
- export type AddressEncoding = "ss58" | "ethereum" | "base58";
1
+ export type KeypairCurve = "ecdsa" | "ed25519" | "sr25519" | "ethereum" | "bitcoin-ed25519" | "bitcoin-ecdsa" | "solana";
2
+ export type AddressEncoding = "ss58" | "ethereum" | "bech32m" | "bech32" | "base58check" | "base58";
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" | "solana";
9
+ export type Platform = "ethereum" | "polkadot" | "bitcoin" | "solana";
@@ -3,6 +3,8 @@
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 bech32 = require('bech32');
7
+ var bs58check = require('bs58check');
6
8
  var sha3 = require('@noble/hashes/sha3');
7
9
  var utils = require('@noble/hashes/utils');
8
10
  var blake2b = require('@noble/hashes/blake2b');
@@ -15,6 +17,10 @@ var hmac = require('@noble/hashes/hmac');
15
17
  var sha512 = require('@noble/hashes/sha512');
16
18
  var microSr25519 = require('micro-sr25519');
17
19
 
20
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
21
+
22
+ var bs58check__default = /*#__PURE__*/_interopDefault(bs58check);
23
+
18
24
  const pbkdf2 = async (hash, entropy, salt, iterations, outputLenBytes) => {
19
25
  // NOTE: react-native-quick-crypto (our `global.crypto` polyfill on Talisman Mobile) doesn't support `crypto.subtle.deriveKey`.
20
26
  // But, we can work around this by using `crypto.subtle.deriveBits` and `crypto.subtle.importKey`, which when used together
@@ -56,6 +62,9 @@ const getSeedDerivationType = curve => {
56
62
  case "ethereum":
57
63
  case "solana":
58
64
  return "classic";
65
+ case "bitcoin-ecdsa":
66
+ case "bitcoin-ed25519":
67
+ throw new Error("seed derivation is not implemented for Bitcoin");
59
68
  }
60
69
  };
61
70
 
@@ -115,6 +124,26 @@ const getDevSeed = async curve => {
115
124
  return DEV_SEED_CACHE.get(type);
116
125
  };
117
126
 
127
+ /** NOTE: Try not to use this too much, it will need to change */
128
+ const addressEncodingFromCurve = curve => {
129
+ switch (curve) {
130
+ case "sr25519":
131
+ case "ed25519":
132
+ case "ecdsa":
133
+ return "ss58";
134
+ case "bitcoin-ecdsa":
135
+ case "bitcoin-ed25519":
136
+ // NOTE: Bitcoin has multiple address formats, so this isn't necessarily correct
137
+ // The format MAY be bech32m, but it might also be bech32 or base58check.
138
+ // bech32m is the most recent format.
139
+ return "bech32m";
140
+ case "ethereum":
141
+ return "ethereum";
142
+ case "solana":
143
+ return "base58";
144
+ }
145
+ };
146
+
118
147
  const encodeAddressBase58 = publicKey => {
119
148
  return base.base58.encode(publicKey);
120
149
  };
@@ -128,6 +157,94 @@ function isBase58Address(address) {
128
157
  }
129
158
  }
130
159
 
160
+ const isBitcoinAddress = address => isBech32mAddress(address) || isBech32Address(address) || isBase58CheckAddress(address);
161
+ function isBech32mAddress(address) {
162
+ try {
163
+ fromBech32m(address);
164
+ } catch {
165
+ return false;
166
+ }
167
+ return true;
168
+ }
169
+ function isBech32Address(address) {
170
+ try {
171
+ fromBech32(address);
172
+ } catch {
173
+ return false;
174
+ }
175
+ return true;
176
+ }
177
+ function isBase58CheckAddress(address) {
178
+ try {
179
+ fromBase58Check(address);
180
+ } catch {
181
+ return false;
182
+ }
183
+ return true;
184
+ }
185
+
186
+ /**
187
+ * Converts a Bech32m encoded address to its corresponding data representation.
188
+ * @param address - The Bech32m encoded address.
189
+ * @returns An object containing the version, prefix, and data of the address.
190
+ * @throws {TypeError} If the address uses the wrong encoding.
191
+ */
192
+ function fromBech32m(address) {
193
+ const result = bech32.bech32m.decode(address);
194
+ const version = result.words[0];
195
+ if (version === 0) throw new TypeError(address + " uses wrong encoding");
196
+ const data = bech32.bech32m.fromWords(result.words.slice(1));
197
+ return {
198
+ version,
199
+ prefix: result.prefix,
200
+ data: Uint8Array.from(data)
201
+ };
202
+ }
203
+
204
+ /**
205
+ * Converts a Bech32 encoded address to its corresponding data representation.
206
+ * @param address - The Bech32 encoded address.
207
+ * @returns An object containing the version, prefix, and data of the address.
208
+ * @throws {TypeError} If the address uses the wrong encoding.
209
+ */
210
+ function fromBech32(address) {
211
+ const result = bech32.bech32.decode(address);
212
+ const version = result.words[0];
213
+ if (version !== 0) throw new TypeError(address + " uses wrong encoding");
214
+ const data = bech32.bech32.fromWords(result.words.slice(1));
215
+ return {
216
+ version,
217
+ prefix: result.prefix,
218
+ data: Uint8Array.from(data)
219
+ };
220
+ }
221
+
222
+ /**
223
+ * Decodes a base58check encoded Bitcoin address and returns the version and hash.
224
+ *
225
+ * @param address - The base58check encoded Bitcoin address to decode.
226
+ * @returns An object containing the version and hash of the decoded address.
227
+ * @throws {TypeError} If the address is too short or too long.
228
+ */
229
+ function fromBase58Check(address) {
230
+ const payload = bs58check__default.default.decode(address);
231
+ if (payload.length < 21) throw new TypeError(address + " is too short");
232
+ if (payload.length > 21) throw new TypeError(address + " is too long");
233
+ function readUInt8(buffer, offset) {
234
+ if (offset + 1 > buffer.length) {
235
+ throw new Error("Offset is outside the bounds of Uint8Array");
236
+ }
237
+ const buf = Buffer.from(buffer);
238
+ return buf.readUInt8(offset);
239
+ }
240
+ const version = readUInt8(payload, 0);
241
+ const hash = payload.slice(1);
242
+ return {
243
+ version,
244
+ hash
245
+ };
246
+ }
247
+
131
248
  /**
132
249
  * Encodes a public key using H160 encoding with Ethereum checksum.
133
250
  */
@@ -215,23 +332,13 @@ function isSs58Address(address) {
215
332
  }
216
333
  }
217
334
 
218
- const addressEncodingFromCurve = curve => {
219
- switch (curve) {
220
- case "sr25519":
221
- case "ed25519":
222
- case "ecdsa":
223
- return "ss58";
224
- case "ethereum":
225
- return "ethereum";
226
- case "solana":
227
- return "base58";
228
- }
229
- };
230
-
231
335
  const CACHE$1 = new Map();
232
336
  const detectAddressEncodingInner = address => {
233
337
  if (isEthereumAddress(address)) return "ethereum";
234
338
  if (isSs58Address(address)) return "ss58";
339
+ if (isBech32mAddress(address)) return "bech32m";
340
+ if (isBech32Address(address)) return "bech32";
341
+ if (isBase58CheckAddress(address)) return "base58check";
235
342
  if (isBase58Address(address)) return "base58";
236
343
  throw new Error(`Unknown address encoding`);
237
344
  };
@@ -246,6 +353,10 @@ const addressFromPublicKey = (publicKey, encoding, options) => {
246
353
  return encodeAddressSs58(publicKey, options?.ss58Prefix);
247
354
  case "ethereum":
248
355
  return encodeAddressEthereum(publicKey);
356
+ case "bech32m":
357
+ case "bech32":
358
+ case "base58check":
359
+ throw new Error("addressFromPublicKey is not implemented for Bitcoin");
249
360
  case "base58":
250
361
  return encodeAddressBase58(publicKey);
251
362
  }
@@ -268,6 +379,9 @@ const normalizeAnyAddress = address => {
268
379
  switch (detectAddressEncoding(address)) {
269
380
  case "ethereum":
270
381
  return checksumEthereumAddress(address);
382
+ case "bech32m":
383
+ case "bech32":
384
+ case "base58check":
271
385
  case "base58":
272
386
  return address;
273
387
  case "ss58":
@@ -279,7 +393,12 @@ const normalizeAnyAddress = address => {
279
393
  };
280
394
 
281
395
  const isAddressEqual = (address1, address2) => {
282
- return normalizeAddress(address1) === normalizeAddress(address2);
396
+ try {
397
+ return normalizeAddress(address1) === normalizeAddress(address2);
398
+ } catch (err) {
399
+ // if normalization fails, assume the addresses are not equal
400
+ return false;
401
+ }
283
402
  };
284
403
 
285
404
  // Inspired from MIT licensed @polkadot-labs/hdkd helpers
@@ -448,6 +567,9 @@ const deriveKeypair = (seed, derivationPath, curve) => {
448
567
  return deriveEd25519(seed, derivationPath);
449
568
  case "ecdsa":
450
569
  return deriveEcdsa(seed, derivationPath);
570
+ case "bitcoin-ecdsa":
571
+ case "bitcoin-ed25519":
572
+ throw new Error("deriveKeypair is not implemented for Bitcoin");
451
573
  case "ethereum":
452
574
  return deriveEthereum(seed, derivationPath);
453
575
  case "solana":
@@ -464,6 +586,9 @@ const getPublicKeyFromSecret = (secretKey, curve) => {
464
586
  return getPublicKeySr25519(secretKey);
465
587
  case "ed25519":
466
588
  return getPublicKeyEd25519(secretKey);
589
+ case "bitcoin-ecdsa":
590
+ case "bitcoin-ed25519":
591
+ throw new Error("getPublicKeyFromSecret is not implemented for Bitcoin");
467
592
  case "solana":
468
593
  return getPublicKeySolana(secretKey);
469
594
  }
@@ -519,6 +644,8 @@ const parseSecretKey = (secretKey, curve) => {
519
644
  case "ed25519":
520
645
  case "sr25519":
521
646
  case "ecdsa":
647
+ case "bitcoin-ecdsa":
648
+ case "bitcoin-ed25519":
522
649
  case "solana":
523
650
  throw new Error("Not implemented");
524
651
  }
@@ -542,6 +669,9 @@ const platformFromCurve = curve => {
542
669
  return "polkadot";
543
670
  case "ethereum":
544
671
  return "ethereum";
672
+ case "bitcoin-ecdsa":
673
+ case "bitcoin-ed25519":
674
+ return "bitcoin";
545
675
  case "solana":
546
676
  return "solana";
547
677
  }
@@ -552,6 +682,10 @@ const platformFromEncoding = encoding => {
552
682
  return "polkadot";
553
683
  case "ethereum":
554
684
  return "ethereum";
685
+ case "bech32m":
686
+ case "bech32":
687
+ case "base58check":
688
+ return "bitcoin";
555
689
  case "base58":
556
690
  return "solana";
557
691
  }
@@ -586,12 +720,19 @@ exports.encodeAddressEthereum = encodeAddressEthereum;
586
720
  exports.encodeAddressSs58 = encodeAddressSs58;
587
721
  exports.entropyToMnemonic = entropyToMnemonic;
588
722
  exports.entropyToSeed = entropyToSeed;
723
+ exports.fromBase58Check = fromBase58Check;
724
+ exports.fromBech32 = fromBech32;
725
+ exports.fromBech32m = fromBech32m;
589
726
  exports.generateMnemonic = generateMnemonic;
590
727
  exports.getDevSeed = getDevSeed;
591
728
  exports.getPublicKeyFromSecret = getPublicKeyFromSecret;
592
729
  exports.getSafeHash = getSafeHash;
593
730
  exports.isAddressEqual = isAddressEqual;
594
731
  exports.isBase58Address = isBase58Address;
732
+ exports.isBase58CheckAddress = isBase58CheckAddress;
733
+ exports.isBech32Address = isBech32Address;
734
+ exports.isBech32mAddress = isBech32mAddress;
735
+ exports.isBitcoinAddress = isBitcoinAddress;
595
736
  exports.isEthereumAddress = isEthereumAddress;
596
737
  exports.isSs58Address = isSs58Address;
597
738
  exports.isValidDerivationPath = isValidDerivationPath;
@@ -3,6 +3,8 @@
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 bech32 = require('bech32');
7
+ var bs58check = require('bs58check');
6
8
  var sha3 = require('@noble/hashes/sha3');
7
9
  var utils = require('@noble/hashes/utils');
8
10
  var blake2b = require('@noble/hashes/blake2b');
@@ -15,6 +17,10 @@ var hmac = require('@noble/hashes/hmac');
15
17
  var sha512 = require('@noble/hashes/sha512');
16
18
  var microSr25519 = require('micro-sr25519');
17
19
 
20
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
21
+
22
+ var bs58check__default = /*#__PURE__*/_interopDefault(bs58check);
23
+
18
24
  const pbkdf2 = async (hash, entropy, salt, iterations, outputLenBytes) => {
19
25
  // NOTE: react-native-quick-crypto (our `global.crypto` polyfill on Talisman Mobile) doesn't support `crypto.subtle.deriveKey`.
20
26
  // But, we can work around this by using `crypto.subtle.deriveBits` and `crypto.subtle.importKey`, which when used together
@@ -56,6 +62,9 @@ const getSeedDerivationType = curve => {
56
62
  case "ethereum":
57
63
  case "solana":
58
64
  return "classic";
65
+ case "bitcoin-ecdsa":
66
+ case "bitcoin-ed25519":
67
+ throw new Error("seed derivation is not implemented for Bitcoin");
59
68
  }
60
69
  };
61
70
 
@@ -115,6 +124,26 @@ const getDevSeed = async curve => {
115
124
  return DEV_SEED_CACHE.get(type);
116
125
  };
117
126
 
127
+ /** NOTE: Try not to use this too much, it will need to change */
128
+ const addressEncodingFromCurve = curve => {
129
+ switch (curve) {
130
+ case "sr25519":
131
+ case "ed25519":
132
+ case "ecdsa":
133
+ return "ss58";
134
+ case "bitcoin-ecdsa":
135
+ case "bitcoin-ed25519":
136
+ // NOTE: Bitcoin has multiple address formats, so this isn't necessarily correct
137
+ // The format MAY be bech32m, but it might also be bech32 or base58check.
138
+ // bech32m is the most recent format.
139
+ return "bech32m";
140
+ case "ethereum":
141
+ return "ethereum";
142
+ case "solana":
143
+ return "base58";
144
+ }
145
+ };
146
+
118
147
  const encodeAddressBase58 = publicKey => {
119
148
  return base.base58.encode(publicKey);
120
149
  };
@@ -128,6 +157,94 @@ function isBase58Address(address) {
128
157
  }
129
158
  }
130
159
 
160
+ const isBitcoinAddress = address => isBech32mAddress(address) || isBech32Address(address) || isBase58CheckAddress(address);
161
+ function isBech32mAddress(address) {
162
+ try {
163
+ fromBech32m(address);
164
+ } catch {
165
+ return false;
166
+ }
167
+ return true;
168
+ }
169
+ function isBech32Address(address) {
170
+ try {
171
+ fromBech32(address);
172
+ } catch {
173
+ return false;
174
+ }
175
+ return true;
176
+ }
177
+ function isBase58CheckAddress(address) {
178
+ try {
179
+ fromBase58Check(address);
180
+ } catch {
181
+ return false;
182
+ }
183
+ return true;
184
+ }
185
+
186
+ /**
187
+ * Converts a Bech32m encoded address to its corresponding data representation.
188
+ * @param address - The Bech32m encoded address.
189
+ * @returns An object containing the version, prefix, and data of the address.
190
+ * @throws {TypeError} If the address uses the wrong encoding.
191
+ */
192
+ function fromBech32m(address) {
193
+ const result = bech32.bech32m.decode(address);
194
+ const version = result.words[0];
195
+ if (version === 0) throw new TypeError(address + " uses wrong encoding");
196
+ const data = bech32.bech32m.fromWords(result.words.slice(1));
197
+ return {
198
+ version,
199
+ prefix: result.prefix,
200
+ data: Uint8Array.from(data)
201
+ };
202
+ }
203
+
204
+ /**
205
+ * Converts a Bech32 encoded address to its corresponding data representation.
206
+ * @param address - The Bech32 encoded address.
207
+ * @returns An object containing the version, prefix, and data of the address.
208
+ * @throws {TypeError} If the address uses the wrong encoding.
209
+ */
210
+ function fromBech32(address) {
211
+ const result = bech32.bech32.decode(address);
212
+ const version = result.words[0];
213
+ if (version !== 0) throw new TypeError(address + " uses wrong encoding");
214
+ const data = bech32.bech32.fromWords(result.words.slice(1));
215
+ return {
216
+ version,
217
+ prefix: result.prefix,
218
+ data: Uint8Array.from(data)
219
+ };
220
+ }
221
+
222
+ /**
223
+ * Decodes a base58check encoded Bitcoin address and returns the version and hash.
224
+ *
225
+ * @param address - The base58check encoded Bitcoin address to decode.
226
+ * @returns An object containing the version and hash of the decoded address.
227
+ * @throws {TypeError} If the address is too short or too long.
228
+ */
229
+ function fromBase58Check(address) {
230
+ const payload = bs58check__default.default.decode(address);
231
+ if (payload.length < 21) throw new TypeError(address + " is too short");
232
+ if (payload.length > 21) throw new TypeError(address + " is too long");
233
+ function readUInt8(buffer, offset) {
234
+ if (offset + 1 > buffer.length) {
235
+ throw new Error("Offset is outside the bounds of Uint8Array");
236
+ }
237
+ const buf = Buffer.from(buffer);
238
+ return buf.readUInt8(offset);
239
+ }
240
+ const version = readUInt8(payload, 0);
241
+ const hash = payload.slice(1);
242
+ return {
243
+ version,
244
+ hash
245
+ };
246
+ }
247
+
131
248
  /**
132
249
  * Encodes a public key using H160 encoding with Ethereum checksum.
133
250
  */
@@ -215,23 +332,13 @@ function isSs58Address(address) {
215
332
  }
216
333
  }
217
334
 
218
- const addressEncodingFromCurve = curve => {
219
- switch (curve) {
220
- case "sr25519":
221
- case "ed25519":
222
- case "ecdsa":
223
- return "ss58";
224
- case "ethereum":
225
- return "ethereum";
226
- case "solana":
227
- return "base58";
228
- }
229
- };
230
-
231
335
  const CACHE$1 = new Map();
232
336
  const detectAddressEncodingInner = address => {
233
337
  if (isEthereumAddress(address)) return "ethereum";
234
338
  if (isSs58Address(address)) return "ss58";
339
+ if (isBech32mAddress(address)) return "bech32m";
340
+ if (isBech32Address(address)) return "bech32";
341
+ if (isBase58CheckAddress(address)) return "base58check";
235
342
  if (isBase58Address(address)) return "base58";
236
343
  throw new Error(`Unknown address encoding`);
237
344
  };
@@ -246,6 +353,10 @@ const addressFromPublicKey = (publicKey, encoding, options) => {
246
353
  return encodeAddressSs58(publicKey, options?.ss58Prefix);
247
354
  case "ethereum":
248
355
  return encodeAddressEthereum(publicKey);
356
+ case "bech32m":
357
+ case "bech32":
358
+ case "base58check":
359
+ throw new Error("addressFromPublicKey is not implemented for Bitcoin");
249
360
  case "base58":
250
361
  return encodeAddressBase58(publicKey);
251
362
  }
@@ -268,6 +379,9 @@ const normalizeAnyAddress = address => {
268
379
  switch (detectAddressEncoding(address)) {
269
380
  case "ethereum":
270
381
  return checksumEthereumAddress(address);
382
+ case "bech32m":
383
+ case "bech32":
384
+ case "base58check":
271
385
  case "base58":
272
386
  return address;
273
387
  case "ss58":
@@ -279,7 +393,12 @@ const normalizeAnyAddress = address => {
279
393
  };
280
394
 
281
395
  const isAddressEqual = (address1, address2) => {
282
- return normalizeAddress(address1) === normalizeAddress(address2);
396
+ try {
397
+ return normalizeAddress(address1) === normalizeAddress(address2);
398
+ } catch (err) {
399
+ // if normalization fails, assume the addresses are not equal
400
+ return false;
401
+ }
283
402
  };
284
403
 
285
404
  // Inspired from MIT licensed @polkadot-labs/hdkd helpers
@@ -448,6 +567,9 @@ const deriveKeypair = (seed, derivationPath, curve) => {
448
567
  return deriveEd25519(seed, derivationPath);
449
568
  case "ecdsa":
450
569
  return deriveEcdsa(seed, derivationPath);
570
+ case "bitcoin-ecdsa":
571
+ case "bitcoin-ed25519":
572
+ throw new Error("deriveKeypair is not implemented for Bitcoin");
451
573
  case "ethereum":
452
574
  return deriveEthereum(seed, derivationPath);
453
575
  case "solana":
@@ -464,6 +586,9 @@ const getPublicKeyFromSecret = (secretKey, curve) => {
464
586
  return getPublicKeySr25519(secretKey);
465
587
  case "ed25519":
466
588
  return getPublicKeyEd25519(secretKey);
589
+ case "bitcoin-ecdsa":
590
+ case "bitcoin-ed25519":
591
+ throw new Error("getPublicKeyFromSecret is not implemented for Bitcoin");
467
592
  case "solana":
468
593
  return getPublicKeySolana(secretKey);
469
594
  }
@@ -519,6 +644,8 @@ const parseSecretKey = (secretKey, curve) => {
519
644
  case "ed25519":
520
645
  case "sr25519":
521
646
  case "ecdsa":
647
+ case "bitcoin-ecdsa":
648
+ case "bitcoin-ed25519":
522
649
  case "solana":
523
650
  throw new Error("Not implemented");
524
651
  }
@@ -542,6 +669,9 @@ const platformFromCurve = curve => {
542
669
  return "polkadot";
543
670
  case "ethereum":
544
671
  return "ethereum";
672
+ case "bitcoin-ecdsa":
673
+ case "bitcoin-ed25519":
674
+ return "bitcoin";
545
675
  case "solana":
546
676
  return "solana";
547
677
  }
@@ -552,6 +682,10 @@ const platformFromEncoding = encoding => {
552
682
  return "polkadot";
553
683
  case "ethereum":
554
684
  return "ethereum";
685
+ case "bech32m":
686
+ case "bech32":
687
+ case "base58check":
688
+ return "bitcoin";
555
689
  case "base58":
556
690
  return "solana";
557
691
  }
@@ -586,12 +720,19 @@ exports.encodeAddressEthereum = encodeAddressEthereum;
586
720
  exports.encodeAddressSs58 = encodeAddressSs58;
587
721
  exports.entropyToMnemonic = entropyToMnemonic;
588
722
  exports.entropyToSeed = entropyToSeed;
723
+ exports.fromBase58Check = fromBase58Check;
724
+ exports.fromBech32 = fromBech32;
725
+ exports.fromBech32m = fromBech32m;
589
726
  exports.generateMnemonic = generateMnemonic;
590
727
  exports.getDevSeed = getDevSeed;
591
728
  exports.getPublicKeyFromSecret = getPublicKeyFromSecret;
592
729
  exports.getSafeHash = getSafeHash;
593
730
  exports.isAddressEqual = isAddressEqual;
594
731
  exports.isBase58Address = isBase58Address;
732
+ exports.isBase58CheckAddress = isBase58CheckAddress;
733
+ exports.isBech32Address = isBech32Address;
734
+ exports.isBech32mAddress = isBech32mAddress;
735
+ exports.isBitcoinAddress = isBitcoinAddress;
595
736
  exports.isEthereumAddress = isEthereumAddress;
596
737
  exports.isSs58Address = isSs58Address;
597
738
  exports.isValidDerivationPath = isValidDerivationPath;
@@ -2,6 +2,8 @@ import { mnemonicToEntropy as mnemonicToEntropy$1, entropyToMnemonic as entropyT
2
2
  import { wordlist } from '@scure/bip39/wordlists/english';
3
3
  import { base58, bytesToString, stringToBytes } from '@scure/base';
4
4
  export { bytesToString, stringToBytes } from '@scure/base';
5
+ import { bech32m, bech32 } from 'bech32';
6
+ import bs58check from 'bs58check';
5
7
  import { keccak_256 } from '@noble/hashes/sha3';
6
8
  import { bytesToHex, randomBytes } from '@noble/hashes/utils';
7
9
  import { blake2b } from '@noble/hashes/blake2b';
@@ -55,6 +57,9 @@ const getSeedDerivationType = curve => {
55
57
  case "ethereum":
56
58
  case "solana":
57
59
  return "classic";
60
+ case "bitcoin-ecdsa":
61
+ case "bitcoin-ed25519":
62
+ throw new Error("seed derivation is not implemented for Bitcoin");
58
63
  }
59
64
  };
60
65
 
@@ -114,6 +119,26 @@ const getDevSeed = async curve => {
114
119
  return DEV_SEED_CACHE.get(type);
115
120
  };
116
121
 
122
+ /** NOTE: Try not to use this too much, it will need to change */
123
+ const addressEncodingFromCurve = curve => {
124
+ switch (curve) {
125
+ case "sr25519":
126
+ case "ed25519":
127
+ case "ecdsa":
128
+ return "ss58";
129
+ case "bitcoin-ecdsa":
130
+ case "bitcoin-ed25519":
131
+ // NOTE: Bitcoin has multiple address formats, so this isn't necessarily correct
132
+ // The format MAY be bech32m, but it might also be bech32 or base58check.
133
+ // bech32m is the most recent format.
134
+ return "bech32m";
135
+ case "ethereum":
136
+ return "ethereum";
137
+ case "solana":
138
+ return "base58";
139
+ }
140
+ };
141
+
117
142
  const encodeAddressBase58 = publicKey => {
118
143
  return base58.encode(publicKey);
119
144
  };
@@ -127,6 +152,94 @@ function isBase58Address(address) {
127
152
  }
128
153
  }
129
154
 
155
+ const isBitcoinAddress = address => isBech32mAddress(address) || isBech32Address(address) || isBase58CheckAddress(address);
156
+ function isBech32mAddress(address) {
157
+ try {
158
+ fromBech32m(address);
159
+ } catch {
160
+ return false;
161
+ }
162
+ return true;
163
+ }
164
+ function isBech32Address(address) {
165
+ try {
166
+ fromBech32(address);
167
+ } catch {
168
+ return false;
169
+ }
170
+ return true;
171
+ }
172
+ function isBase58CheckAddress(address) {
173
+ try {
174
+ fromBase58Check(address);
175
+ } catch {
176
+ return false;
177
+ }
178
+ return true;
179
+ }
180
+
181
+ /**
182
+ * Converts a Bech32m encoded address to its corresponding data representation.
183
+ * @param address - The Bech32m encoded address.
184
+ * @returns An object containing the version, prefix, and data of the address.
185
+ * @throws {TypeError} If the address uses the wrong encoding.
186
+ */
187
+ function fromBech32m(address) {
188
+ const result = bech32m.decode(address);
189
+ const version = result.words[0];
190
+ if (version === 0) throw new TypeError(address + " uses wrong encoding");
191
+ const data = bech32m.fromWords(result.words.slice(1));
192
+ return {
193
+ version,
194
+ prefix: result.prefix,
195
+ data: Uint8Array.from(data)
196
+ };
197
+ }
198
+
199
+ /**
200
+ * Converts a Bech32 encoded address to its corresponding data representation.
201
+ * @param address - The Bech32 encoded address.
202
+ * @returns An object containing the version, prefix, and data of the address.
203
+ * @throws {TypeError} If the address uses the wrong encoding.
204
+ */
205
+ function fromBech32(address) {
206
+ const result = bech32.decode(address);
207
+ const version = result.words[0];
208
+ if (version !== 0) throw new TypeError(address + " uses wrong encoding");
209
+ const data = bech32.fromWords(result.words.slice(1));
210
+ return {
211
+ version,
212
+ prefix: result.prefix,
213
+ data: Uint8Array.from(data)
214
+ };
215
+ }
216
+
217
+ /**
218
+ * Decodes a base58check encoded Bitcoin address and returns the version and hash.
219
+ *
220
+ * @param address - The base58check encoded Bitcoin address to decode.
221
+ * @returns An object containing the version and hash of the decoded address.
222
+ * @throws {TypeError} If the address is too short or too long.
223
+ */
224
+ function fromBase58Check(address) {
225
+ const payload = bs58check.decode(address);
226
+ if (payload.length < 21) throw new TypeError(address + " is too short");
227
+ if (payload.length > 21) throw new TypeError(address + " is too long");
228
+ function readUInt8(buffer, offset) {
229
+ if (offset + 1 > buffer.length) {
230
+ throw new Error("Offset is outside the bounds of Uint8Array");
231
+ }
232
+ const buf = Buffer.from(buffer);
233
+ return buf.readUInt8(offset);
234
+ }
235
+ const version = readUInt8(payload, 0);
236
+ const hash = payload.slice(1);
237
+ return {
238
+ version,
239
+ hash
240
+ };
241
+ }
242
+
130
243
  /**
131
244
  * Encodes a public key using H160 encoding with Ethereum checksum.
132
245
  */
@@ -214,23 +327,13 @@ function isSs58Address(address) {
214
327
  }
215
328
  }
216
329
 
217
- const addressEncodingFromCurve = curve => {
218
- switch (curve) {
219
- case "sr25519":
220
- case "ed25519":
221
- case "ecdsa":
222
- return "ss58";
223
- case "ethereum":
224
- return "ethereum";
225
- case "solana":
226
- return "base58";
227
- }
228
- };
229
-
230
330
  const CACHE$1 = new Map();
231
331
  const detectAddressEncodingInner = address => {
232
332
  if (isEthereumAddress(address)) return "ethereum";
233
333
  if (isSs58Address(address)) return "ss58";
334
+ if (isBech32mAddress(address)) return "bech32m";
335
+ if (isBech32Address(address)) return "bech32";
336
+ if (isBase58CheckAddress(address)) return "base58check";
234
337
  if (isBase58Address(address)) return "base58";
235
338
  throw new Error(`Unknown address encoding`);
236
339
  };
@@ -245,6 +348,10 @@ const addressFromPublicKey = (publicKey, encoding, options) => {
245
348
  return encodeAddressSs58(publicKey, options?.ss58Prefix);
246
349
  case "ethereum":
247
350
  return encodeAddressEthereum(publicKey);
351
+ case "bech32m":
352
+ case "bech32":
353
+ case "base58check":
354
+ throw new Error("addressFromPublicKey is not implemented for Bitcoin");
248
355
  case "base58":
249
356
  return encodeAddressBase58(publicKey);
250
357
  }
@@ -267,6 +374,9 @@ const normalizeAnyAddress = address => {
267
374
  switch (detectAddressEncoding(address)) {
268
375
  case "ethereum":
269
376
  return checksumEthereumAddress(address);
377
+ case "bech32m":
378
+ case "bech32":
379
+ case "base58check":
270
380
  case "base58":
271
381
  return address;
272
382
  case "ss58":
@@ -278,7 +388,12 @@ const normalizeAnyAddress = address => {
278
388
  };
279
389
 
280
390
  const isAddressEqual = (address1, address2) => {
281
- return normalizeAddress(address1) === normalizeAddress(address2);
391
+ try {
392
+ return normalizeAddress(address1) === normalizeAddress(address2);
393
+ } catch (err) {
394
+ // if normalization fails, assume the addresses are not equal
395
+ return false;
396
+ }
282
397
  };
283
398
 
284
399
  // Inspired from MIT licensed @polkadot-labs/hdkd helpers
@@ -447,6 +562,9 @@ const deriveKeypair = (seed, derivationPath, curve) => {
447
562
  return deriveEd25519(seed, derivationPath);
448
563
  case "ecdsa":
449
564
  return deriveEcdsa(seed, derivationPath);
565
+ case "bitcoin-ecdsa":
566
+ case "bitcoin-ed25519":
567
+ throw new Error("deriveKeypair is not implemented for Bitcoin");
450
568
  case "ethereum":
451
569
  return deriveEthereum(seed, derivationPath);
452
570
  case "solana":
@@ -463,6 +581,9 @@ const getPublicKeyFromSecret = (secretKey, curve) => {
463
581
  return getPublicKeySr25519(secretKey);
464
582
  case "ed25519":
465
583
  return getPublicKeyEd25519(secretKey);
584
+ case "bitcoin-ecdsa":
585
+ case "bitcoin-ed25519":
586
+ throw new Error("getPublicKeyFromSecret is not implemented for Bitcoin");
466
587
  case "solana":
467
588
  return getPublicKeySolana(secretKey);
468
589
  }
@@ -518,6 +639,8 @@ const parseSecretKey = (secretKey, curve) => {
518
639
  case "ed25519":
519
640
  case "sr25519":
520
641
  case "ecdsa":
642
+ case "bitcoin-ecdsa":
643
+ case "bitcoin-ed25519":
521
644
  case "solana":
522
645
  throw new Error("Not implemented");
523
646
  }
@@ -541,6 +664,9 @@ const platformFromCurve = curve => {
541
664
  return "polkadot";
542
665
  case "ethereum":
543
666
  return "ethereum";
667
+ case "bitcoin-ecdsa":
668
+ case "bitcoin-ed25519":
669
+ return "bitcoin";
544
670
  case "solana":
545
671
  return "solana";
546
672
  }
@@ -551,6 +677,10 @@ const platformFromEncoding = encoding => {
551
677
  return "polkadot";
552
678
  case "ethereum":
553
679
  return "ethereum";
680
+ case "bech32m":
681
+ case "bech32":
682
+ case "base58check":
683
+ return "bitcoin";
554
684
  case "base58":
555
685
  return "solana";
556
686
  }
@@ -560,4 +690,4 @@ const platformFromAddress = address => {
560
690
  return platformFromEncoding(encoding);
561
691
  };
562
692
 
563
- export { DEV_MNEMONIC_ETHEREUM, DEV_MNEMONIC_POLKADOT, addressEncodingFromCurve, addressFromPublicKey, addressFromSuri, blake2b256, blake2b512, blake3, checksumEthereumAddress, decodeSs58Address, deriveKeypair, detectAddressEncoding, encodeAddressBase58, encodeAddressEthereum, encodeAddressSs58, entropyToMnemonic, entropyToSeed, generateMnemonic, getDevSeed, getPublicKeyFromSecret, getSafeHash, isAddressEqual, isBase58Address, isEthereumAddress, isSs58Address, isValidDerivationPath, isValidMnemonic, mnemonicToEntropy, normalizeAddress, parseSecretKey, parseSuri, pbkdf2, platformFromAddress, platformFromCurve, platformFromEncoding, removeHexPrefix };
693
+ 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, isValidDerivationPath, isValidMnemonic, mnemonicToEntropy, normalizeAddress, parseSecretKey, parseSuri, pbkdf2, platformFromAddress, platformFromCurve, platformFromEncoding, removeHexPrefix };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@talismn/crypto",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "author": "Talisman",
5
5
  "homepage": "https://talisman.xyz",
6
6
  "license": "GPL-3.0-or-later",
@@ -27,6 +27,8 @@
27
27
  "@scure/base": "1.2.4",
28
28
  "@scure/bip32": "1.6.2",
29
29
  "@scure/bip39": "1.5.4",
30
+ "bech32": "2.0.0",
31
+ "bs58check": "4.0.0",
30
32
  "micro-sr25519": "0.1.3",
31
33
  "scale-ts": "1.6.1"
32
34
  },