@pezkuwi/util-crypto 14.0.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.
Files changed (234) hide show
  1. package/README.md +17 -0
  2. package/package.json +45 -0
  3. package/src/address/addressToEvm.spec.ts +16 -0
  4. package/src/address/addressToEvm.ts +12 -0
  5. package/src/address/check.spec.ts +44 -0
  6. package/src/address/check.ts +34 -0
  7. package/src/address/checksum.spec.ts +45 -0
  8. package/src/address/checksum.ts +25 -0
  9. package/src/address/decode.spec.ts +138 -0
  10. package/src/address/decode.ts +41 -0
  11. package/src/address/defaults.ts +12 -0
  12. package/src/address/derive.spec.ts +26 -0
  13. package/src/address/derive.ts +36 -0
  14. package/src/address/encode.spec.ts +177 -0
  15. package/src/address/encode.ts +43 -0
  16. package/src/address/encodeDerived.spec.ts +14 -0
  17. package/src/address/encodeDerived.ts +19 -0
  18. package/src/address/encodeMulti.spec.ts +18 -0
  19. package/src/address/encodeMulti.ts +18 -0
  20. package/src/address/eq.spec.ts +45 -0
  21. package/src/address/eq.ts +24 -0
  22. package/src/address/evmToAddress.spec.ts +20 -0
  23. package/src/address/evmToAddress.ts +24 -0
  24. package/src/address/index.ts +21 -0
  25. package/src/address/is.spec.ts +113 -0
  26. package/src/address/is.ts +14 -0
  27. package/src/address/keyDerived.spec.ts +24 -0
  28. package/src/address/keyDerived.ts +22 -0
  29. package/src/address/keyMulti.spec.ts +20 -0
  30. package/src/address/keyMulti.ts +23 -0
  31. package/src/address/setSS58Format.spec.ts +21 -0
  32. package/src/address/setSS58Format.ts +20 -0
  33. package/src/address/sort.spec.ts +22 -0
  34. package/src/address/sort.ts +17 -0
  35. package/src/address/sshash.ts +12 -0
  36. package/src/address/types.ts +6 -0
  37. package/src/address/util.ts +8 -0
  38. package/src/address/validate.spec.ts +113 -0
  39. package/src/address/validate.ts +10 -0
  40. package/src/base32/bs32.ts +53 -0
  41. package/src/base32/decode.spec.ts +34 -0
  42. package/src/base32/encode.spec.ts +30 -0
  43. package/src/base32/helpers.ts +93 -0
  44. package/src/base32/index.ts +8 -0
  45. package/src/base32/is.spec.ts +32 -0
  46. package/src/base32/validate.spec.ts +44 -0
  47. package/src/base58/bs58.ts +43 -0
  48. package/src/base58/decode.spec.ts +31 -0
  49. package/src/base58/encode.spec.ts +26 -0
  50. package/src/base58/index.ts +8 -0
  51. package/src/base58/validate.spec.ts +20 -0
  52. package/src/base64/bs64.ts +43 -0
  53. package/src/base64/decode.spec.ts +42 -0
  54. package/src/base64/encode.spec.ts +14 -0
  55. package/src/base64/index.ts +10 -0
  56. package/src/base64/pad.spec.ts +14 -0
  57. package/src/base64/pad.ts +10 -0
  58. package/src/base64/trim.spec.ts +14 -0
  59. package/src/base64/trim.ts +14 -0
  60. package/src/base64/validate.spec.ts +32 -0
  61. package/src/blake2/asHex.spec.ts +57 -0
  62. package/src/blake2/asU8a.spec.ts +74 -0
  63. package/src/blake2/asU8a.ts +40 -0
  64. package/src/blake2/index.ts +8 -0
  65. package/src/bn.ts +15 -0
  66. package/src/bundle.ts +33 -0
  67. package/src/bundleInit.ts +11 -0
  68. package/src/crypto.spec.ts +18 -0
  69. package/src/crypto.ts +18 -0
  70. package/src/ed25519/deriveHard.ts +18 -0
  71. package/src/ed25519/index.ts +13 -0
  72. package/src/ed25519/pair/fromRandom.spec.ts +28 -0
  73. package/src/ed25519/pair/fromRandom.ts +25 -0
  74. package/src/ed25519/pair/fromSecret.spec.ts +33 -0
  75. package/src/ed25519/pair/fromSecret.ts +29 -0
  76. package/src/ed25519/pair/fromSeed.spec.ts +42 -0
  77. package/src/ed25519/pair/fromSeed.ts +41 -0
  78. package/src/ed25519/pair/fromString.spec.ts +17 -0
  79. package/src/ed25519/pair/fromString.ts +31 -0
  80. package/src/ed25519/sign.spec.ts +40 -0
  81. package/src/ed25519/sign.ts +38 -0
  82. package/src/ed25519/verify.spec.ts +84 -0
  83. package/src/ed25519/verify.ts +41 -0
  84. package/src/ethereum/encode.spec.ts +59 -0
  85. package/src/ethereum/encode.ts +39 -0
  86. package/src/ethereum/index.ts +6 -0
  87. package/src/ethereum/isAddress.spec.ts +34 -0
  88. package/src/ethereum/isAddress.ts +16 -0
  89. package/src/ethereum/isChecksum.ts +27 -0
  90. package/src/ethereum/isCheksum.spec.ts +30 -0
  91. package/src/hd/ethereum/index.spec.ts +54 -0
  92. package/src/hd/ethereum/index.ts +69 -0
  93. package/src/hd/index.ts +6 -0
  94. package/src/hd/ledger/derivePrivate.ts +34 -0
  95. package/src/hd/ledger/index.spec.ts +64 -0
  96. package/src/hd/ledger/index.ts +42 -0
  97. package/src/hd/ledger/master.spec.ts +19 -0
  98. package/src/hd/ledger/master.ts +26 -0
  99. package/src/hd/validatePath.spec.ts +30 -0
  100. package/src/hd/validatePath.ts +24 -0
  101. package/src/helpers.ts +38 -0
  102. package/src/hmac/index.ts +4 -0
  103. package/src/hmac/shaAsU8a.spec.ts +45 -0
  104. package/src/hmac/shaAsU8a.ts +48 -0
  105. package/src/index.ts +6 -0
  106. package/src/json/constants.ts +11 -0
  107. package/src/json/decrypt.ts +25 -0
  108. package/src/json/decryptData.ts +45 -0
  109. package/src/json/encrypt.ts +25 -0
  110. package/src/json/encryptFormat.ts +20 -0
  111. package/src/json/index.ts +7 -0
  112. package/src/json/types.ts +22 -0
  113. package/src/keccak/asHex.spec.ts +30 -0
  114. package/src/keccak/asU8a.spec.ts +56 -0
  115. package/src/keccak/asU8a.ts +45 -0
  116. package/src/keccak/index.ts +8 -0
  117. package/src/key/DeriveJunction.ts +79 -0
  118. package/src/key/extractPath.spec.ts +51 -0
  119. package/src/key/extractPath.ts +37 -0
  120. package/src/key/extractSuri.spec.ts +147 -0
  121. package/src/key/extractSuri.ts +40 -0
  122. package/src/key/fromPath.ts +28 -0
  123. package/src/key/hdkdDerive.ts +17 -0
  124. package/src/key/hdkdEcdsa.ts +8 -0
  125. package/src/key/hdkdEd25519.ts +7 -0
  126. package/src/key/hdkdSr25519.ts +14 -0
  127. package/src/key/index.ts +12 -0
  128. package/src/mnemonic/bip39.spec.ts +80 -0
  129. package/src/mnemonic/bip39.ts +127 -0
  130. package/src/mnemonic/generate.spec.ts +58 -0
  131. package/src/mnemonic/generate.ts +25 -0
  132. package/src/mnemonic/index.ts +11 -0
  133. package/src/mnemonic/toEntropy.spec.ts +36 -0
  134. package/src/mnemonic/toEntropy.ts +13 -0
  135. package/src/mnemonic/toLegacySeed.spec.ts +52 -0
  136. package/src/mnemonic/toLegacySeed.ts +39 -0
  137. package/src/mnemonic/toMiniSecret.spec.ts +67 -0
  138. package/src/mnemonic/toMiniSecret.ts +23 -0
  139. package/src/mnemonic/toMiniSecretCmp.spec.ts +64 -0
  140. package/src/mnemonic/validate.spec.ts +39 -0
  141. package/src/mnemonic/validate.ts +26 -0
  142. package/src/mnemonic/wordlists/en.ts +7 -0
  143. package/src/mnemonic/wordlists/es.ts +7 -0
  144. package/src/mnemonic/wordlists/fr.ts +7 -0
  145. package/src/mnemonic/wordlists/index.ts +11 -0
  146. package/src/mnemonic/wordlists/it.ts +7 -0
  147. package/src/mnemonic/wordlists/jp.ts +7 -0
  148. package/src/mnemonic/wordlists/ko.ts +7 -0
  149. package/src/mnemonic/wordlists/zh-s.ts +7 -0
  150. package/src/mnemonic/wordlists/zh-t.ts +7 -0
  151. package/src/mod.ts +4 -0
  152. package/src/nacl/decrypt.spec.ts +26 -0
  153. package/src/nacl/decrypt.ts +22 -0
  154. package/src/nacl/encrypt.spec.ts +20 -0
  155. package/src/nacl/encrypt.ts +31 -0
  156. package/src/nacl/index.ts +8 -0
  157. package/src/nacl/tweetnacl-secretbox-data.spec.ts +4629 -0
  158. package/src/nacl/tweetnacl-secretbox.spec.ts +161 -0
  159. package/src/nacl/tweetnacl.ts +1159 -0
  160. package/src/networks.ts +5 -0
  161. package/src/packageDetect.ts +14 -0
  162. package/src/packageInfo.ts +6 -0
  163. package/src/pbkdf2/encode.spec.ts +54 -0
  164. package/src/pbkdf2/encode.ts +29 -0
  165. package/src/pbkdf2/index.ts +4 -0
  166. package/src/random/asHex.spec.ts +38 -0
  167. package/src/random/asNumber.spec.ts +16 -0
  168. package/src/random/asNumber.ts +28 -0
  169. package/src/random/asU8a.spec.ts +36 -0
  170. package/src/random/asU8a.ts +30 -0
  171. package/src/random/index.ts +9 -0
  172. package/src/scrypt/defaults.ts +19 -0
  173. package/src/scrypt/encode.spec.ts +43 -0
  174. package/src/scrypt/encode.ts +30 -0
  175. package/src/scrypt/fromU8a.ts +44 -0
  176. package/src/scrypt/index.ts +6 -0
  177. package/src/scrypt/toU8a.ts +17 -0
  178. package/src/scrypt/types.ts +9 -0
  179. package/src/secp256k1/compress.spec.ts +47 -0
  180. package/src/secp256k1/compress.ts +21 -0
  181. package/src/secp256k1/deriveHard.ts +17 -0
  182. package/src/secp256k1/expand.spec.ts +47 -0
  183. package/src/secp256k1/expand.ts +30 -0
  184. package/src/secp256k1/hasher.spec.ts +24 -0
  185. package/src/secp256k1/hasher.ts +13 -0
  186. package/src/secp256k1/index.ts +10 -0
  187. package/src/secp256k1/pair/fromSeed.spec.ts +75 -0
  188. package/src/secp256k1/pair/fromSeed.ts +42 -0
  189. package/src/secp256k1/recover.spec.ts +35 -0
  190. package/src/secp256k1/recover.ts +36 -0
  191. package/src/secp256k1/sign.spec.ts +39 -0
  192. package/src/secp256k1/sign.ts +37 -0
  193. package/src/secp256k1/signVerify.spec.ts +94 -0
  194. package/src/secp256k1/tweakAdd.spec.ts +35 -0
  195. package/src/secp256k1/tweakAdd.ts +65 -0
  196. package/src/secp256k1/types.ts +4 -0
  197. package/src/secp256k1/verify.spec.ts +81 -0
  198. package/src/secp256k1/verify.ts +32 -0
  199. package/src/sha/asU8a.ts +30 -0
  200. package/src/sha/asU8a256.spec.ts +55 -0
  201. package/src/sha/asU8a512.spec.ts +33 -0
  202. package/src/sha/index.ts +8 -0
  203. package/src/signature/index.ts +8 -0
  204. package/src/signature/verify.spec.ts +230 -0
  205. package/src/signature/verify.ts +114 -0
  206. package/src/sr25519/agreement.spec.ts +31 -0
  207. package/src/sr25519/agreement.ts +23 -0
  208. package/src/sr25519/derive.ts +21 -0
  209. package/src/sr25519/deriveHard.ts +9 -0
  210. package/src/sr25519/derivePublic.ts +18 -0
  211. package/src/sr25519/deriveSoft.ts +9 -0
  212. package/src/sr25519/index.ts +12 -0
  213. package/src/sr25519/pair/fromSeed.spec.ts +35 -0
  214. package/src/sr25519/pair/fromSeed.ts +28 -0
  215. package/src/sr25519/pair/fromU8a.ts +23 -0
  216. package/src/sr25519/pair/testing.spec.ts +161 -0
  217. package/src/sr25519/pair/toU8a.ts +10 -0
  218. package/src/sr25519/sign.spec.ts +28 -0
  219. package/src/sr25519/sign.ts +22 -0
  220. package/src/sr25519/verify.spec.ts +42 -0
  221. package/src/sr25519/verify.ts +23 -0
  222. package/src/sr25519/vrfSign.ts +24 -0
  223. package/src/sr25519/vrfSignVerify.spec.ts +73 -0
  224. package/src/sr25519/vrfVerify.ts +25 -0
  225. package/src/test/index.ts +8 -0
  226. package/src/test/performance.ts +17 -0
  227. package/src/types.ts +33 -0
  228. package/src/xxhash/asHex.spec.ts +36 -0
  229. package/src/xxhash/asU8a.spec.ts +48 -0
  230. package/src/xxhash/asU8a.ts +45 -0
  231. package/src/xxhash/index.ts +8 -0
  232. package/src/xxhash/xxhash64.ts +155 -0
  233. package/tsconfig.build.json +18 -0
  234. package/tsconfig.spec.json +20 -0
package/README.md ADDED
@@ -0,0 +1,17 @@
1
+ # @pezkuwi/util-crypto
2
+
3
+ Various useful cyrpto utility functions that are used across all projects in the [@pezkuwi](https://pezkuwi.js.org) namespace. It provides utility functions with additional safety checks, allowing not only for consistent coding, but also reducing the general boilerplate.
4
+
5
+ ## Usage
6
+
7
+ Installation -
8
+
9
+ ```
10
+ yarn add @pezkuwi/util-crypto
11
+ ```
12
+
13
+ Functions can be imported as follows:
14
+
15
+ ```js
16
+ import { mnemonicGenerate } from '@pezkuwi/util-crypto';
17
+ ```
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "author": "Jaco Greeff <jacogr@gmail.com>",
3
+ "bugs": "https://github.com/pezkuwichain/pezkuwi-common/issues",
4
+ "description": "A collection of useful crypto utilities for @pezkuwi",
5
+ "engines": {
6
+ "node": ">=18"
7
+ },
8
+ "homepage": "https://github.com/pezkuwichain/pezkuwi-common/tree/master/packages/util-crypto#readme",
9
+ "license": "Apache-2.0",
10
+ "name": "@pezkuwi/util-crypto",
11
+ "repository": {
12
+ "directory": "packages/util-crypto",
13
+ "type": "git",
14
+ "url": "https://github.com/pezkuwichain/pezkuwi-common.git"
15
+ },
16
+ "sideEffects": [
17
+ "./bundleInit.js",
18
+ "./bundleInit.cjs",
19
+ "./packageDetect.js",
20
+ "./packageDetect.cjs"
21
+ ],
22
+ "type": "module",
23
+ "version": "14.0.1",
24
+ "browser": {
25
+ "crypto": false,
26
+ "stream": false
27
+ },
28
+ "main": "index.js",
29
+ "dependencies": {
30
+ "@noble/curves": "^1.3.0",
31
+ "@noble/hashes": "^1.3.3",
32
+ "@pezkuwi/networks": "14.0.1",
33
+ "@pezkuwi/util": "14.0.1",
34
+ "@pezkuwi/wasm-crypto": "^7.5.3",
35
+ "@pezkuwi/wasm-util": "^7.5.3",
36
+ "@pezkuwi/x-bigint": "14.0.1",
37
+ "@pezkuwi/x-randomvalues": "14.0.1",
38
+ "@scure/base": "^1.1.7",
39
+ "@scure/sr25519": "^0.2.0",
40
+ "tslib": "^2.8.0"
41
+ },
42
+ "peerDependencies": {
43
+ "@pezkuwi/util": "14.0.1"
44
+ }
45
+ }
@@ -0,0 +1,16 @@
1
+ // Copyright 2017-2025 @polkadot/util-crypto authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /// <reference types="@polkadot/dev-test/globals.d.ts" />
5
+
6
+ import { hexToU8a } from '@pezkuwi/util';
7
+
8
+ import { addressToEvm } from './addressToEvm.js';
9
+
10
+ describe('addressToEvm', (): void => {
11
+ it('creates a valid known EVM address', (): void => {
12
+ expect(
13
+ addressToEvm('KWCv1L3QX9LDPwY4VzvLmarEmXjVJidUzZcinvVnmxAJJCBou')
14
+ ).toEqual(hexToU8a('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee9'));
15
+ });
16
+ });
@@ -0,0 +1,12 @@
1
+ // Copyright 2017-2025 @polkadot/util-crypto authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { decodeAddress } from './decode.js';
5
+
6
+ /**
7
+ * @name addressToEvm
8
+ * @summary Converts an SS58 address to its corresponding EVM address.
9
+ */
10
+ export function addressToEvm (address: string | Uint8Array, ignoreChecksum?: boolean): Uint8Array {
11
+ return decodeAddress(address, ignoreChecksum).subarray(0, 20);
12
+ }
@@ -0,0 +1,44 @@
1
+ // Copyright 2017-2025 @polkadot/util-crypto authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /// <reference types="@polkadot/dev-test/globals.d.ts" />
5
+
6
+ import { checkAddress } from './index.js';
7
+
8
+ describe('checkAddress', (): void => {
9
+ it('returns [true, null] for Kusama', (): void => {
10
+ expect(
11
+ checkAddress('FJaco77EJ99VtBmVFibuBJR3x5Qq9KQrgQJvWjqScCcCCae', 2)
12
+ ).toEqual([true, null]);
13
+ });
14
+
15
+ it('returns [true, null] for Substrate', (): void => {
16
+ expect(
17
+ checkAddress('5EnxxUmEbw8DkENKiYuZ1DwQuMoB2UWEQJZZXrTsxoz7SpgG', 42)
18
+ ).toEqual([true, null]);
19
+ });
20
+
21
+ it('fails when an invalid base58 character is supplied', (): void => {
22
+ expect(
23
+ checkAddress('5EnxIUmEbw8DkENKiYuZ1DwQuMoB2UWEQJZZXrTsxoz7SpgG', 2)
24
+ ).toEqual([false, 'Invalid base58 character "I" (0x49) at index 4']);
25
+ });
26
+
27
+ it('fails with invalid prefix when checking Substrate against Kusama prefix', (): void => {
28
+ expect(
29
+ checkAddress('5EnxxUmEbw8DkENKiYuZ1DwQuMoB2UWEQJZZXrTsxoz7SpgG', 2)
30
+ ).toEqual([false, 'Prefix mismatch, expected 2, found 42']);
31
+ });
32
+
33
+ it('fails with invalid length when some bytes are missing', (): void => {
34
+ expect(
35
+ checkAddress('y9EMHt34JJo4rWLSaxoLGdYXvjgSXEd4zHUnQgfNzwES8b', 42)
36
+ ).toEqual([false, 'Invalid decoded address length']);
37
+ });
38
+
39
+ it('fails with invalid length on checksum mismatch', (): void => {
40
+ expect(
41
+ checkAddress('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaDwU', 42)
42
+ ).toEqual([false, 'Invalid decoded address checksum']);
43
+ });
44
+ });
@@ -0,0 +1,34 @@
1
+ // Copyright 2017-2025 @polkadot/util-crypto authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import type { Prefix } from './types.js';
5
+
6
+ import { base58Decode } from '../base58/index.js';
7
+ import { checkAddressChecksum } from './checksum.js';
8
+ import { defaults } from './defaults.js';
9
+
10
+ /**
11
+ * @name checkAddress
12
+ * @summary Validates an ss58 address.
13
+ * @description
14
+ * From the provided input, validate that the address is a valid input.
15
+ */
16
+ export function checkAddress (address: string, prefix: Prefix): [boolean, string | null] {
17
+ let decoded;
18
+
19
+ try {
20
+ decoded = base58Decode(address);
21
+ } catch (error) {
22
+ return [false, (error as Error).message];
23
+ }
24
+
25
+ const [isValid,,, ss58Decoded] = checkAddressChecksum(decoded);
26
+
27
+ if (ss58Decoded !== prefix) {
28
+ return [false, `Prefix mismatch, expected ${prefix}, found ${ss58Decoded}`];
29
+ } else if (!defaults.allowedEncodedLengths.includes(decoded.length)) {
30
+ return [false, 'Invalid decoded address length'];
31
+ }
32
+
33
+ return [isValid, isValid ? null : 'Invalid decoded address checksum'];
34
+ }
@@ -0,0 +1,45 @@
1
+ // Copyright 2017-2025 @polkadot/util-crypto authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /// <reference types="@polkadot/dev-test/globals.d.ts" />
5
+
6
+ import { base58Decode } from '../base58/index.js';
7
+ import { checkAddressChecksum } from './checksum.js';
8
+
9
+ describe('checkAddressChecksum', (): void => {
10
+ it('correctly extracts the info from a 1-byte-prefix address', (): void => {
11
+ expect(
12
+ checkAddressChecksum(base58Decode('F3opxRbN5ZbjJNU511Kj2TLuzFcDq9BGduA9TgiECafpg29'))
13
+ ).toEqual([true, 33, 1, 2]);
14
+ });
15
+
16
+ it('correctly extracts the info from a 2-byte-prefix address (66)', (): void => {
17
+ expect(
18
+ checkAddressChecksum(base58Decode('cTGShekJ1L1UKFZR9xmv9UTJod7vqjFAPo4sDhXih2c3y1yLS'))
19
+ ).toEqual([true, 34, 2, 66]);
20
+ });
21
+
22
+ it('correctly extracts the info from a 2-byte-prefix address (69)', (): void => {
23
+ expect(
24
+ checkAddressChecksum(base58Decode('cnVvyMzRdqjwejTFuByQQ4w2yu78V2hpFixjHQz5zr6NSYsxA'))
25
+ ).toEqual([true, 34, 2, 69]);
26
+ });
27
+
28
+ it('correctly extracts the info from a 2-byte-prefix address (252)', (): void => {
29
+ expect(
30
+ checkAddressChecksum(base58Decode('xw8Ffc2SZtDqUJKd9Ky4vc7PRz2D2asuVkEEzf3WGAbw9cnfq'))
31
+ ).toEqual([true, 34, 2, 252]);
32
+ });
33
+
34
+ it('correctly extracts the info from a 2-byte-prefix address (255)', (): void => {
35
+ expect(
36
+ checkAddressChecksum(base58Decode('yGHU8YKprxHbHdEv7oUK4rzMZXtsdhcXVG2CAMyC9WhzhjH2k'))
37
+ ).toEqual([true, 34, 2, 255]);
38
+ });
39
+
40
+ it('correctly extracts the info from a 2-byte-prefix address (ecdsa, from Substrate)', (): void => {
41
+ expect(
42
+ checkAddressChecksum(base58Decode('4pbsSkWcBaYoFHrKJZp5fDVUKbqSYD9dhZZGvpp3vQ5ysVs5ybV'))
43
+ ).toEqual([true, 35, 2, 200]);
44
+ });
45
+ });
@@ -0,0 +1,25 @@
1
+ // Copyright 2017-2025 @polkadot/util-crypto authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { sshash } from './sshash.js';
5
+
6
+ export function checkAddressChecksum (decoded: Uint8Array): [boolean, number, number, number] {
7
+ const ss58Length = (decoded[0] & 0b0100_0000) ? 2 : 1;
8
+ const ss58Decoded = ss58Length === 1
9
+ ? decoded[0]
10
+ : ((decoded[0] & 0b0011_1111) << 2) | (decoded[1] >> 6) | ((decoded[1] & 0b0011_1111) << 8);
11
+
12
+ // 32/33 bytes public + 2 bytes checksum + prefix
13
+ const isPublicKey = [34 + ss58Length, 35 + ss58Length].includes(decoded.length);
14
+ const length = decoded.length - (isPublicKey ? 2 : 1);
15
+
16
+ // calculate the hash and do the checksum byte checks
17
+ const hash = sshash(decoded.subarray(0, length));
18
+ const isValid = (decoded[0] & 0b1000_0000) === 0 && ![46, 47].includes(decoded[0]) && (
19
+ isPublicKey
20
+ ? decoded[decoded.length - 2] === hash[0] && decoded[decoded.length - 1] === hash[1]
21
+ : decoded[decoded.length - 1] === hash[0]
22
+ );
23
+
24
+ return [isValid, length, ss58Length, ss58Decoded];
25
+ }
@@ -0,0 +1,138 @@
1
+ // Copyright 2017-2025 @polkadot/util-crypto authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /// <reference types="@polkadot/dev-test/globals.d.ts" />
5
+
6
+ import { hexToU8a, stringToU8a, u8aToHex } from '@pezkuwi/util';
7
+
8
+ import { ALICE_PUBLIC_SR } from './encode.spec.js';
9
+ import { decodeAddress } from './index.js';
10
+
11
+ describe('decodeAddress', (): void => {
12
+ it('decodes an address', (): void => {
13
+ expect(
14
+ decodeAddress('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY')
15
+ ).toEqual(
16
+ ALICE_PUBLIC_SR
17
+ );
18
+ });
19
+
20
+ it('decodes the council address', (): void => {
21
+ expect(
22
+ u8aToHex(decodeAddress('F3opxRbN5ZbjJNU511Kj2TLuzFcDq9BGduA9TgiECafpg29'))
23
+ ).toEqual(u8aToHex(stringToU8a('modlpy/trsry'.padEnd(32, '\0'))));
24
+ });
25
+
26
+ it('converts a publicKey (u8a) as-is', (): void => {
27
+ expect(
28
+ decodeAddress(new Uint8Array([1, 2, 3]))
29
+ ).toEqual(
30
+ new Uint8Array([1, 2, 3])
31
+ );
32
+ });
33
+
34
+ it('converts a publicKey (hex) as-is', (): void => {
35
+ expect(
36
+ decodeAddress('0x01020304')
37
+ ).toEqual(
38
+ new Uint8Array([1, 2, 3, 4])
39
+ );
40
+ });
41
+
42
+ it('decodes a short address', (): void => {
43
+ expect(
44
+ decodeAddress('F7NZ')
45
+ ).toEqual(new Uint8Array([1]));
46
+ });
47
+
48
+ it('decodes a 1-byte accountId (with prefix)', (): void => {
49
+ expect(
50
+ decodeAddress('g4b', false, 2)
51
+ ).toEqual(new Uint8Array([1]));
52
+ });
53
+
54
+ it('decodes a 2-byte accountId', (): void => {
55
+ expect(
56
+ decodeAddress('3xygo', false, 2)
57
+ ).toEqual(new Uint8Array([0, 1]));
58
+ });
59
+
60
+ it('encodes a 4-byte address', (): void => {
61
+ expect(
62
+ decodeAddress('zswfoZa', false, 2)
63
+ ).toEqual(new Uint8Array([1, 2, 3, 4]));
64
+ });
65
+
66
+ it('decodes a 8-byte address', (): void => {
67
+ expect(
68
+ decodeAddress('848Gh2GcGaZia', false, 2)
69
+ ).toEqual(new Uint8Array([42, 44, 10, 0, 0, 0, 0, 0]));
70
+ });
71
+
72
+ it('decodes a 33-byte address', (): void => {
73
+ expect(
74
+ decodeAddress('KWCv1L3QX9LDPwY4VzvLmarEmXjVJidUzZcinvVnmxAJJCBou')
75
+ ).toEqual(
76
+ hexToU8a('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077')
77
+ );
78
+ });
79
+
80
+ it('decodes a 2-byte prefix (65)', (): void => {
81
+ expect(
82
+ decodeAddress('cLtA6nCDyvwKcEHH4QkZDSHMhS9s78BvUJUsKUbUAn1Jc2SCF')
83
+ ).toEqual(
84
+ hexToU8a('0x08e8969768fc14399930d4b8d693f68a2ff6c6a597325d6946095e5e9d9d1b0e')
85
+ );
86
+ });
87
+
88
+ it('decodes a 2-byte prefix (69)', (): void => {
89
+ expect(
90
+ decodeAddress('cnUaoo5wodnTVA4bnr4woSweto8hWZADUvLFXkR9Q6U7BRsbF')
91
+ ).toEqual(
92
+ hexToU8a('0x88eafe0305d460d1695cf34c2f786050df8e40d215e488790cc70929c9e8316d')
93
+ );
94
+ });
95
+
96
+ it('decodes a 2-byte prefix (252)', (): void => {
97
+ expect(
98
+ decodeAddress('xw9Hca4RJTmBRgzJT4ieJBh7XCK9gE3NXBDSEmgGHd4TCrbnG')
99
+ ).toEqual(
100
+ hexToU8a('0xfc422da6c3bc6dfa2a436a506428072941662f816987baaa8914e02ff5947f4b')
101
+ );
102
+ });
103
+
104
+ it('decodes a 2-byte prefix (255)', (): void => {
105
+ expect(
106
+ decodeAddress('yGHU8YKprxHbHdEv7oUK4rzMZXtsdhcXVG2CAMyC9WhzhjH2k')
107
+ ).toEqual(
108
+ decodeAddress('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQua')
109
+ );
110
+ });
111
+
112
+ it('decodes a 2-byte prefix (ecdsa, from Substrate)', (): void => {
113
+ expect(
114
+ u8aToHex(decodeAddress('4pbsSkWcBaYoFHrKJZp5fDVUKbqSYD9dhZZGvpp3vQ5ysVs5ybV'))
115
+ ).toEqual('0x035676109c54b9a16d271abeb4954316a40a32bcce023ac14c8e26e958aa68fba9');
116
+ });
117
+
118
+ it('fails when length is invalid', (): void => {
119
+ expect(
120
+ (): Uint8Array => decodeAddress('y9EMHt34JJo4rWLSaxoLGdYXvjgSXEd4zHUnQgfNzwES8b')
121
+ ).toThrow(/address length/);
122
+ });
123
+
124
+ it('fails when the checksum does not match', (): void => {
125
+ expect(
126
+ (): Uint8Array => decodeAddress('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMa9cj')
127
+ ).toThrow(/address checksum/);
128
+ expect(
129
+ (): Uint8Array => decodeAddress('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaDwU')
130
+ ).toThrow(/address checksum/);
131
+ });
132
+
133
+ it('fails when invalid base58 encoded address is found', (): void => {
134
+ expect(
135
+ () => u8aToHex(decodeAddress('F3opIRbN5ZbjJNU511Kj2TLuzFcDq9BGduA9TgiECafpg29'))
136
+ ).toThrow(/Decoding F3opIRbN5ZbjJNU511Kj2TLuzFcDq9BGduA9TgiECafpg29: Invalid base58 character "I" \(0x49\) at index 4/);
137
+ });
138
+ });
@@ -0,0 +1,41 @@
1
+ // Copyright 2017-2025 @polkadot/util-crypto authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import type { Prefix } from './types.js';
5
+
6
+ // Original implementation: https://github.com/paritytech/polka-ui/blob/4858c094684769080f5811f32b081dd7780b0880/src/polkadot.js#L6
7
+ import { isHex, isU8a, u8aToU8a } from '@pezkuwi/util';
8
+
9
+ import { base58Decode } from '../base58/index.js';
10
+ import { checkAddressChecksum } from './checksum.js';
11
+ import { defaults } from './defaults.js';
12
+
13
+ export function decodeAddress (encoded?: string | Uint8Array | null, ignoreChecksum?: boolean, ss58Format: Prefix = -1): Uint8Array {
14
+ if (!encoded) {
15
+ throw new Error('Invalid empty address passed');
16
+ }
17
+
18
+ if (isU8a(encoded) || isHex(encoded)) {
19
+ return u8aToU8a(encoded);
20
+ }
21
+
22
+ try {
23
+ const decoded = base58Decode(encoded);
24
+
25
+ if (!defaults.allowedEncodedLengths.includes(decoded.length)) {
26
+ throw new Error('Invalid decoded address length');
27
+ }
28
+
29
+ const [isValid, endPos, ss58Length, ss58Decoded] = checkAddressChecksum(decoded);
30
+
31
+ if (!isValid && !ignoreChecksum) {
32
+ throw new Error('Invalid decoded address checksum');
33
+ } else if (ss58Format !== -1 && ss58Format !== ss58Decoded) {
34
+ throw new Error(`Expected ss58Format ${ss58Format}, received ${ss58Decoded}`);
35
+ }
36
+
37
+ return decoded.slice(ss58Length, endPos);
38
+ } catch (error) {
39
+ throw new Error(`Decoding ${encoded}: ${(error as Error).message}`);
40
+ }
41
+ }
@@ -0,0 +1,12 @@
1
+ // Copyright 2017-2025 @polkadot/util-crypto authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { availableNetworks } from '../networks.js';
5
+
6
+ export const defaults = {
7
+ allowedDecodedLengths: [1, 2, 4, 8, 32, 33],
8
+ // publicKey has prefix + 2 checksum bytes, short only prefix + 1 checksum byte
9
+ allowedEncodedLengths: [3, 4, 6, 10, 35, 36, 37, 38],
10
+ allowedPrefix: availableNetworks.map(({ prefix }) => prefix),
11
+ prefix: 42
12
+ };
@@ -0,0 +1,26 @@
1
+ // Copyright 2017-2025 @polkadot/util-crypto authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /// <reference types="@polkadot/dev-test/globals.d.ts" />
5
+
6
+ import { waitReady } from '@pezkuwi/wasm-crypto';
7
+
8
+ import { deriveAddress } from './index.js';
9
+
10
+ describe('deriveAddress', (): void => {
11
+ beforeEach(async (): Promise<void> => {
12
+ await waitReady();
13
+ });
14
+
15
+ it('derives a known path', (): void => {
16
+ expect(
17
+ deriveAddress('5CZtJLXtVzrBJq1fMWfywDa6XuRwXekGdShPR4b8i9GWSbzB', '/joe/polkadot/0')
18
+ ).toEqual('5GZ4srnepXvdsuNVoxCGyVZd8ScDm4gkGLTKuaGARy9akjTa');
19
+ });
20
+
21
+ it('fails on hard paths', (): void => {
22
+ expect(
23
+ () => deriveAddress('5CZtJLXtVzrBJq1fMWfywDa6XuRwXekGdShPR4b8i9GWSbzB', '//bob')
24
+ ).toThrow(/Expected suri to contain a combination of non-hard paths/);
25
+ });
26
+ });
@@ -0,0 +1,36 @@
1
+ // Copyright 2017-2025 @polkadot/util-crypto authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import type { DeriveJunction } from '../key/DeriveJunction.js';
5
+ import type { Prefix } from './types.js';
6
+
7
+ import { keyExtractPath } from '../key/index.js';
8
+ import { sr25519DerivePublic } from '../sr25519/index.js';
9
+ import { decodeAddress } from './decode.js';
10
+ import { encodeAddress } from './encode.js';
11
+
12
+ function filterHard ({ isHard }: DeriveJunction): boolean {
13
+ return isHard;
14
+ }
15
+
16
+ /**
17
+ * @name deriveAddress
18
+ * @summary Creates a sr25519 derived address from the supplied and path.
19
+ * @description
20
+ * Creates a sr25519 derived address based on the input address/publicKey and the uri supplied.
21
+ */
22
+ export function deriveAddress (who: string | Uint8Array, suri: string, ss58Format?: Prefix): string {
23
+ const { path } = keyExtractPath(suri);
24
+
25
+ if (!path.length || path.every(filterHard)) {
26
+ throw new Error('Expected suri to contain a combination of non-hard paths');
27
+ }
28
+
29
+ let publicKey = decodeAddress(who);
30
+
31
+ for (const { chainCode } of path) {
32
+ publicKey = sr25519DerivePublic(publicKey, chainCode);
33
+ }
34
+
35
+ return encodeAddress(publicKey, ss58Format);
36
+ }
@@ -0,0 +1,177 @@
1
+ // Copyright 2017-2025 @polkadot/util-crypto authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /// <reference types="@polkadot/dev-test/globals.d.ts" />
5
+
6
+ import { encodeAddress } from './index.js';
7
+
8
+ // eslint-disable-next-line jest/no-export
9
+ export const ALICE_PUBLIC_SR = new Uint8Array([212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125]);
10
+
11
+ // eslint-disable-next-line jest/no-export
12
+ export const ALICE_PUBLIC_ED = new Uint8Array([209, 114, 167, 76, 218, 76, 134, 89, 18, 195, 43, 160, 168, 10, 87, 174, 105, 171, 174, 65, 14, 92, 203, 89, 222, 232, 78, 47, 68, 50, 219, 79]);
13
+
14
+ const SUBKEY = [
15
+ {
16
+ // substrate default
17
+ address: '5DA4D4GL5iakrn22h5uKoevgvo18Pqj5BcdEUv8etEDPdijA',
18
+ publicKey: '0x3050f8456519829fe03302da802d22d3233a5f4037b9a3e2bcc403ccfcb2d735',
19
+ ss58Format: 42
20
+ },
21
+ {
22
+ // aventus
23
+ address: 'cLtA6nCDyvwKcEHH4QkZDSHMhS9s78BvUJUsKUbUAn1Jc2SCF',
24
+ publicKey: '0x08e8969768fc14399930d4b8d693f68a2ff6c6a597325d6946095e5e9d9d1b0e',
25
+ ss58Format: 65
26
+ },
27
+ {
28
+ // crust
29
+ address: 'cTGShekJ1L1UKFZR9xmv9UTJod7vqjFAPo4sDhXih2c3y1yLS',
30
+ publicKey: '0x04a047d52fe542484c69bc528990cfeaf3a663dded0638ee1b51cf78bacd1072',
31
+ ss58Format: 66
32
+ },
33
+ {
34
+ // sora
35
+ address: 'cnVRwXfAnz3RSVQyBUC8f8McrK3YBX2QYd4WoctpeSC6VTJYm',
36
+ publicKey: '0xae640d53cfa815f4a6a50ae70235cd7d9d134d0f1c3a4ccd118e321dfb6ab51f',
37
+ ss58Format: 69
38
+ },
39
+ {
40
+ // ecdsa
41
+ address: '4pbsSkWcBaYoFHrKJZp5fDVUKbqSYD9dhZZGvpp3vQ5ysVs5ybV',
42
+ publicKey: '0x035676109c54b9a16d271abeb4954316a40a32bcce023ac14c8e26e958aa68fba9',
43
+ ss58Format: 200
44
+ },
45
+ {
46
+ // social-network
47
+ address: 'xw5g1Eec8LT99pZLZMaTWwrwvNtfM6vrSuZeVbtEszCDUwByg',
48
+ publicKey: '0x5c64f1151f0ce4358c27238fb20c88e7c899825436f565410724c8c2c5add869',
49
+ ss58Format: 252
50
+ },
51
+ {
52
+ address: 'yGF4JP7q5AK46d1FPCEm9sYQ4KooSjHMpyVAjLnsCSWVafPnf',
53
+ publicKey: '0x66cd6cf085627d6c85af1aaf2bd10cf843033e929b4e3b1c2ba8e4aa46fe111b',
54
+ ss58Format: 255
55
+ },
56
+ {
57
+ address: 'yGDYxQatQwuxqT39Zs4LtcTnpzE12vXb7ZJ6xpdiHv6gTu1hF',
58
+ publicKey: '0x242fd5a078ac6b7c3c2531e9bcf1314343782aeb58e7bc6880794589e701db55',
59
+ ss58Format: 255
60
+ },
61
+ {
62
+ address: 'mHm8k9Emsvyfp3piCauSH684iA6NakctF8dySQcX94GDdrJrE',
63
+ publicKey: '0x44d5a3ac156335ea99d33a83c57c7146c40c8e2260a8a4adf4e7a86256454651',
64
+ ss58Format: 4242
65
+ },
66
+ {
67
+ address: 'r6Gr4gaMP8TsjhFbqvZhv3YvnasugLiRJpzpRHifsqqG18UXa',
68
+ publicKey: '0x88f01441682a17b52d6ae12d1a5670cf675fd254897efabaa5069eb3a701ab73',
69
+ ss58Format: 14269
70
+ }
71
+ ];
72
+
73
+ describe('encode', (): void => {
74
+ it('encodes an address to a valid value', (): void => {
75
+ expect(
76
+ encodeAddress(ALICE_PUBLIC_ED)
77
+ ).toEqual('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaQua');
78
+ });
79
+
80
+ it('can re-encode an address', (): void => {
81
+ expect(
82
+ encodeAddress('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', 2)
83
+ ).toEqual('HNZata7iMYWmk5RvZRTiAsSDhV8366zq2YGb3tLH5Upf74F');
84
+ });
85
+
86
+ it('can re-encode an address to Polkadot live', (): void => {
87
+ expect(
88
+ encodeAddress('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', 0)
89
+ ).toEqual('15oF4uVJwmo4TdGW7VfQxNLavjCXviqxT9S1MgbjMNHr6Sp5');
90
+ });
91
+
92
+ it('fails when non-valid publicKey provided', (): void => {
93
+ expect(
94
+ (): string => encodeAddress(
95
+ ALICE_PUBLIC_ED.slice(0, 30)
96
+ )
97
+ ).toThrow(/Expected a valid key/);
98
+ });
99
+
100
+ it('encodes a 1-byte address', (): void => {
101
+ expect(
102
+ encodeAddress(
103
+ new Uint8Array([1])
104
+ )
105
+ ).toEqual('F7NZ');
106
+ });
107
+
108
+ it('encodes a 1-byte address (with prefix)', (): void => {
109
+ expect(
110
+ encodeAddress(
111
+ new Uint8Array([1]), 2
112
+ )
113
+ ).toEqual('g4b');
114
+ });
115
+
116
+ it('encodes a 2-byte address', (): void => {
117
+ expect(
118
+ encodeAddress(
119
+ new Uint8Array([0, 1]), 2
120
+ )
121
+ ).toEqual('3xygo');
122
+ });
123
+
124
+ it('encodes a 4-byte address', (): void => {
125
+ expect(
126
+ encodeAddress(
127
+ new Uint8Array([1, 2, 3, 4]), 2
128
+ )
129
+ ).toEqual('zswfoZa');
130
+ });
131
+
132
+ it('encodes a 8-byte address', (): void => {
133
+ expect(
134
+ encodeAddress(
135
+ new Uint8Array([42, 44, 10, 0, 0, 0, 0, 0]), 2
136
+ )
137
+ ).toEqual('848Gh2GcGaZia');
138
+ });
139
+
140
+ it('encodes an 33-byte address', (): void => {
141
+ expect(
142
+ encodeAddress('0x03b9dc646dd71118e5f7fda681ad9eca36eb3ee96f344f582fbe7b5bcdebb13077')
143
+ ).toEqual('KWCv1L3QX9LDPwY4VzvLmarEmXjVJidUzZcinvVnmxAJJCBou');
144
+ });
145
+
146
+ it('encodes with 2 byte prefix', (): void => {
147
+ expect(
148
+ encodeAddress(ALICE_PUBLIC_ED, 255)
149
+ ).toEqual('yGHU8YKprxHbHdEv7oUK4rzMZXtsdhcXVG2CAMyC9WhzhjH2k');
150
+ });
151
+
152
+ SUBKEY.forEach(({ address, publicKey, ss58Format }, index): void => {
153
+ it(`encodes with Subkey equality (${index} - ${ss58Format})`, (): void => {
154
+ expect(
155
+ encodeAddress(publicKey, ss58Format)
156
+ ).toEqual(address);
157
+ });
158
+ });
159
+
160
+ it('does not encode for > 16,383, < 0', (): void => {
161
+ expect(
162
+ () => encodeAddress(ALICE_PUBLIC_ED, -1)
163
+ ).toThrow(/range ss58Format specified/);
164
+ expect(
165
+ () => encodeAddress(ALICE_PUBLIC_ED, 16384)
166
+ ).toThrow(/range ss58Format specified/);
167
+ });
168
+
169
+ it('does not encode reserved', (): void => {
170
+ expect(
171
+ () => encodeAddress(ALICE_PUBLIC_ED, 46)
172
+ ).toThrow(/range ss58Format specified/);
173
+ expect(
174
+ () => encodeAddress(ALICE_PUBLIC_ED, 47)
175
+ ).toThrow(/range ss58Format specified/);
176
+ });
177
+ });