@stll/stdnum 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/README.md +12 -13
  2. package/dist/_checksums/keccak.d.ts +5 -0
  3. package/dist/_checksums/keccak.js +118 -0
  4. package/dist/_checksums/keccak.js.map +1 -0
  5. package/dist/_checksums/mod112.js.map +1 -1
  6. package/dist/_checksums/sha256.d.ts +5 -0
  7. package/dist/_checksums/sha256.js +164 -0
  8. package/dist/_checksums/sha256.js.map +1 -0
  9. package/dist/_checksums/verhoeff.js.map +1 -1
  10. package/dist/_util/arabic.js.map +1 -1
  11. package/dist/_util/clean.js.map +1 -1
  12. package/dist/_util/date.js.map +1 -1
  13. package/dist/ad/nrt.js.map +1 -1
  14. package/dist/ae/eid.js.map +1 -1
  15. package/dist/ai/tin.js.map +1 -1
  16. package/dist/al/nipt.js.map +1 -1
  17. package/dist/am/tin.js.map +1 -1
  18. package/dist/ar/cbu.js.map +1 -1
  19. package/dist/ar/cuit.js.map +1 -1
  20. package/dist/ar/dni.js.map +1 -1
  21. package/dist/at/businessid.js.map +1 -1
  22. package/dist/at/vnr.js.map +1 -1
  23. package/dist/au/abn.js.map +1 -1
  24. package/dist/au/tfn.js.map +1 -1
  25. package/dist/az/voen.js.map +1 -1
  26. package/dist/ba/jmbg.js.map +1 -1
  27. package/dist/be/bis.js.map +1 -1
  28. package/dist/be/nn.js.map +1 -1
  29. package/dist/be/vat.js.map +1 -1
  30. package/dist/bg/egn.js.map +1 -1
  31. package/dist/bg/vat.js.map +1 -1
  32. package/dist/bh/cpr.js.map +1 -1
  33. package/dist/bic.js.map +1 -1
  34. package/dist/br/cnpj.js.map +1 -1
  35. package/dist/br/cpf.js.map +1 -1
  36. package/dist/by/unp.js.map +1 -1
  37. package/dist/bz/tin.js.map +1 -1
  38. package/dist/ch/uid.js.map +1 -1
  39. package/dist/ch/vat.js.map +1 -1
  40. package/dist/cl/rut.js.map +1 -1
  41. package/dist/cn/ric.js.map +1 -1
  42. package/dist/cn/uscc.js.map +1 -1
  43. package/dist/cr/cpf.js.map +1 -1
  44. package/dist/crypto/btcbase58.d.ts +11 -0
  45. package/dist/crypto/btcbase58.js +76 -0
  46. package/dist/crypto/btcbase58.js.map +1 -0
  47. package/dist/crypto/btcbech32.d.ts +19 -0
  48. package/dist/crypto/btcbech32.js +145 -0
  49. package/dist/crypto/btcbech32.js.map +1 -0
  50. package/dist/crypto/eth.d.ts +11 -0
  51. package/dist/crypto/eth.js +63 -0
  52. package/dist/crypto/eth.js.map +1 -0
  53. package/dist/crypto/mod.d.ts +12 -0
  54. package/dist/crypto/mod.js +16 -0
  55. package/dist/crypto/mod.js.map +1 -0
  56. package/dist/crypto/wallet.d.ts +10 -0
  57. package/dist/crypto/wallet.js +55 -0
  58. package/dist/crypto/wallet.js.map +1 -0
  59. package/dist/cu/ni.js.map +1 -1
  60. package/dist/cz/rc.js.map +1 -1
  61. package/dist/de/handelsreg.js.map +1 -1
  62. package/dist/de/idnr.js.map +1 -1
  63. package/dist/de/stnr.js.map +1 -1
  64. package/dist/de/svnr.js.map +1 -1
  65. package/dist/dk/cpr.js.map +1 -1
  66. package/dist/dk/cvr.js.map +1 -1
  67. package/dist/dk/vat.js.map +1 -1
  68. package/dist/do/rnc.js.map +1 -1
  69. package/dist/ec/ruc.js.map +1 -1
  70. package/dist/ee/ik.js.map +1 -1
  71. package/dist/ee/vat.js.map +1 -1
  72. package/dist/eg/tn.js.map +1 -1
  73. package/dist/es/dni.js.map +1 -1
  74. package/dist/es/nie.js.map +1 -1
  75. package/dist/es/nss.js.map +1 -1
  76. package/dist/eu/vat.js.map +1 -1
  77. package/dist/fi/hetu.js.map +1 -1
  78. package/dist/fi/vat.js.map +1 -1
  79. package/dist/fi/ytunnus.js.map +1 -1
  80. package/dist/fr/nif.js.map +1 -1
  81. package/dist/fr/nir.js.map +1 -1
  82. package/dist/fr/tva.js.map +1 -1
  83. package/dist/gb/mod.d.ts +3 -2
  84. package/dist/gb/mod.js +3 -1
  85. package/dist/gb/mod.js.map +1 -1
  86. package/dist/gb/nhs.d.ts +11 -0
  87. package/dist/gb/nhs.js +51 -0
  88. package/dist/gb/nhs.js.map +1 -0
  89. package/dist/gb/nino.js.map +1 -1
  90. package/dist/gb/sedol.js.map +1 -1
  91. package/dist/gb/utr.js.map +1 -1
  92. package/dist/gb/vat.js.map +1 -1
  93. package/dist/ge/pin.js.map +1 -1
  94. package/dist/gh/tin.js.map +1 -1
  95. package/dist/gr/amka.js.map +1 -1
  96. package/dist/gt/nit.js.map +1 -1
  97. package/dist/hk/hkid.js.map +1 -1
  98. package/dist/hu/vat.js.map +1 -1
  99. package/dist/iban.js.map +1 -1
  100. package/dist/id/npwp.js.map +1 -1
  101. package/dist/ie/pps.js.map +1 -1
  102. package/dist/ie/vat.js.map +1 -1
  103. package/dist/il/idnr.js.map +1 -1
  104. package/dist/in/aadhaar.js.map +1 -1
  105. package/dist/in/gstin.js.map +1 -1
  106. package/dist/in/pan.js.map +1 -1
  107. package/dist/index.d.ts +76 -75
  108. package/dist/index.js +2 -1
  109. package/dist/ir/nid.js.map +1 -1
  110. package/dist/is/kennitala.js.map +1 -1
  111. package/dist/isin.js.map +1 -1
  112. package/dist/it/codicefiscale.js.map +1 -1
  113. package/dist/it/iva.js.map +1 -1
  114. package/dist/kr/rrn.js.map +1 -1
  115. package/dist/kw/civil.js.map +1 -1
  116. package/dist/kz/iin.js.map +1 -1
  117. package/dist/lei.js.map +1 -1
  118. package/dist/li/peid.js.map +1 -1
  119. package/dist/lk/nic.js.map +1 -1
  120. package/dist/lt/asmens.js.map +1 -1
  121. package/dist/lt/vat.js.map +1 -1
  122. package/dist/lu/vat.js.map +1 -1
  123. package/dist/lv/vat.js.map +1 -1
  124. package/dist/ma/ice.js.map +1 -1
  125. package/dist/mc/tva.js.map +1 -1
  126. package/dist/mt/vat.js.map +1 -1
  127. package/dist/mu/brn.js.map +1 -1
  128. package/dist/mx/clabe.js.map +1 -1
  129. package/dist/mx/curp.js.map +1 -1
  130. package/dist/mx/rfc.js.map +1 -1
  131. package/dist/my/nric.js.map +1 -1
  132. package/dist/ni/ruc.js.map +1 -1
  133. package/dist/nl/bsn.js.map +1 -1
  134. package/dist/nl/vat.js.map +1 -1
  135. package/dist/no/fodselsnummer.js.map +1 -1
  136. package/dist/no/mva.js.map +1 -1
  137. package/dist/no/orgnr.js.map +1 -1
  138. package/dist/nz/ird.js.map +1 -1
  139. package/dist/pa/ruc.js.map +1 -1
  140. package/dist/patterns.js.map +1 -1
  141. package/dist/pe/ruc.js.map +1 -1
  142. package/dist/pl/pesel.js.map +1 -1
  143. package/dist/pl/regon.js.map +1 -1
  144. package/dist/pt/cc.js.map +1 -1
  145. package/dist/ro/cnp.js.map +1 -1
  146. package/dist/ro/vat.js.map +1 -1
  147. package/dist/rs/pib.js.map +1 -1
  148. package/dist/ru/inn.js.map +1 -1
  149. package/dist/se/personnummer.js.map +1 -1
  150. package/dist/sg/uen.js.map +1 -1
  151. package/dist/si/emso.js.map +1 -1
  152. package/dist/si/vat.js.map +1 -1
  153. package/dist/sk/dic.js.map +1 -1
  154. package/dist/tw/ubn.js.map +1 -1
  155. package/dist/ua/edrpou.js.map +1 -1
  156. package/dist/us/itin.js.map +1 -1
  157. package/dist/us/rtn.js.map +1 -1
  158. package/dist/us/ssn.js.map +1 -1
  159. package/dist/uy/rut.js.map +1 -1
  160. package/dist/ve/rif.js.map +1 -1
  161. package/dist/vn/mst.js.map +1 -1
  162. package/dist/za/idnr.js.map +1 -1
  163. package/package.json +31 -6
  164. package/src/_checksums/keccak.ts +145 -0
  165. package/src/_checksums/sha256.ts +125 -0
  166. package/src/crypto/btcbase58.ts +144 -0
  167. package/src/crypto/btcbech32.ts +210 -0
  168. package/src/crypto/eth.ts +114 -0
  169. package/src/crypto/mod.ts +4 -0
  170. package/src/crypto/wallet.ts +67 -0
  171. package/src/gb/mod.ts +1 -0
  172. package/src/gb/nhs.ts +92 -0
  173. package/src/index.ts +1 -0
@@ -0,0 +1,145 @@
1
+ const MASK_64 = (1n << 64n) - 1n;
2
+
3
+ const ROUND_CONSTANTS = [
4
+ 0x0000000000000001n,
5
+ 0x0000000000008082n,
6
+ 0x800000000000808an,
7
+ 0x8000000080008000n,
8
+ 0x000000000000808bn,
9
+ 0x0000000080000001n,
10
+ 0x8000000080008081n,
11
+ 0x8000000000008009n,
12
+ 0x000000000000008an,
13
+ 0x0000000000000088n,
14
+ 0x0000000080008009n,
15
+ 0x000000008000000an,
16
+ 0x000000008000808bn,
17
+ 0x800000000000008bn,
18
+ 0x8000000000008089n,
19
+ 0x8000000000008003n,
20
+ 0x8000000000008002n,
21
+ 0x8000000000000080n,
22
+ 0x000000000000800an,
23
+ 0x800000008000000an,
24
+ 0x8000000080008081n,
25
+ 0x8000000000008080n,
26
+ 0x0000000080000001n,
27
+ 0x8000000080008008n,
28
+ ] as const;
29
+
30
+ const ROTATION_OFFSETS = [
31
+ 0, 1, 62, 28, 27, 36, 44, 6, 55, 20, 3, 10, 43, 25, 39,
32
+ 41, 45, 15, 21, 8, 18, 2, 61, 56, 14,
33
+ ] as const;
34
+
35
+ const RATE_BYTES = 136;
36
+
37
+ const rotl64 = (value: bigint, shift: number): bigint => {
38
+ if (shift === 0) return value & MASK_64;
39
+ const bigShift = BigInt(shift);
40
+ return (
41
+ ((value << bigShift) | (value >> (64n - bigShift))) &
42
+ MASK_64
43
+ );
44
+ };
45
+
46
+ const keccakF1600 = (state: bigint[]): void => {
47
+ const c = Array.from({ length: 5 }, () => 0n);
48
+ const d = Array.from({ length: 5 }, () => 0n);
49
+ const b = Array.from({ length: 25 }, () => 0n);
50
+
51
+ for (const roundConstant of ROUND_CONSTANTS) {
52
+ for (let x = 0; x < 5; x += 1) {
53
+ c[x] =
54
+ (state[x] ?? 0n) ^
55
+ (state[x + 5] ?? 0n) ^
56
+ (state[x + 10] ?? 0n) ^
57
+ (state[x + 15] ?? 0n) ^
58
+ (state[x + 20] ?? 0n);
59
+ }
60
+
61
+ for (let x = 0; x < 5; x += 1) {
62
+ d[x] =
63
+ (c[(x + 4) % 5] ?? 0n) ^
64
+ rotl64(c[(x + 1) % 5] ?? 0n, 1);
65
+ }
66
+
67
+ for (let y = 0; y < 5; y += 1) {
68
+ for (let x = 0; x < 5; x += 1) {
69
+ const index = x + 5 * y;
70
+ state[index] =
71
+ ((state[index] ?? 0n) ^ (d[x] ?? 0n)) & MASK_64;
72
+ }
73
+ }
74
+
75
+ for (let y = 0; y < 5; y += 1) {
76
+ for (let x = 0; x < 5; x += 1) {
77
+ const sourceIndex = x + 5 * y;
78
+ const targetIndex = y + 5 * ((2 * x + 3 * y) % 5);
79
+ b[targetIndex] = rotl64(
80
+ state[sourceIndex] ?? 0n,
81
+ ROTATION_OFFSETS[sourceIndex] ?? 0,
82
+ );
83
+ }
84
+ }
85
+
86
+ for (let y = 0; y < 5; y += 1) {
87
+ for (let x = 0; x < 5; x += 1) {
88
+ const index = x + 5 * y;
89
+ state[index] =
90
+ ((b[index] ?? 0n) ^
91
+ (~(b[((x + 1) % 5) + 5 * y] ?? 0n) &
92
+ (b[((x + 2) % 5) + 5 * y] ?? 0n))) &
93
+ MASK_64;
94
+ }
95
+ }
96
+
97
+ state[0] = ((state[0] ?? 0n) ^ roundConstant) & MASK_64;
98
+ }
99
+ };
100
+
101
+ export const keccak256 = (
102
+ bytes: Uint8Array,
103
+ ): Uint8Array => {
104
+ const state = Array.from({ length: 25 }, () => 0n);
105
+ let offset = 0;
106
+
107
+ while (offset + RATE_BYTES <= bytes.length) {
108
+ for (let i = 0; i < RATE_BYTES; i += 1) {
109
+ const lane = Math.floor(i / 8);
110
+ const shift = BigInt((i % 8) * 8);
111
+ state[lane] =
112
+ ((state[lane] ?? 0n) ^
113
+ (BigInt(bytes[offset + i] ?? 0) << shift)) &
114
+ MASK_64;
115
+ }
116
+ keccakF1600(state);
117
+ offset += RATE_BYTES;
118
+ }
119
+
120
+ const block = new Uint8Array(RATE_BYTES);
121
+ block.set(bytes.slice(offset));
122
+ block[bytes.length - offset] = 0x01;
123
+ block[RATE_BYTES - 1] =
124
+ (block[RATE_BYTES - 1] ?? 0) ^ 0x80;
125
+
126
+ for (let i = 0; i < RATE_BYTES; i += 1) {
127
+ const lane = Math.floor(i / 8);
128
+ const shift = BigInt((i % 8) * 8);
129
+ state[lane] =
130
+ ((state[lane] ?? 0n) ^
131
+ (BigInt(block[i] ?? 0) << shift)) &
132
+ MASK_64;
133
+ }
134
+ keccakF1600(state);
135
+
136
+ const digest = new Uint8Array(32);
137
+ for (let i = 0; i < digest.length; i += 1) {
138
+ const lane = Math.floor(i / 8);
139
+ const shift = BigInt((i % 8) * 8);
140
+ digest[i] = Number(
141
+ ((state[lane] ?? 0n) >> shift) & 0xffn,
142
+ );
143
+ }
144
+ return digest;
145
+ };
@@ -0,0 +1,125 @@
1
+ const K = new Uint32Array([
2
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
3
+ 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
4
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
5
+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
6
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
7
+ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
8
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
9
+ 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
10
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
11
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
12
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
13
+ 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
14
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
15
+ 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
16
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
17
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
18
+ ]);
19
+
20
+ const rotr = (value: number, bits: number): number =>
21
+ (value >>> bits) | (value << (32 - bits));
22
+
23
+ export const sha256 = (bytes: Uint8Array): Uint8Array => {
24
+ const bitLength = bytes.length * 8;
25
+ const messageLength = bytes.length + 1;
26
+ const paddingLength =
27
+ (64 - ((messageLength + 8) % 64)) % 64;
28
+ const totalLength = messageLength + paddingLength + 8;
29
+ const message = new Uint8Array(totalLength);
30
+ message.set(bytes);
31
+ message[bytes.length] = 0x80;
32
+
33
+ const high = Math.floor(bitLength / 0x1_0000_0000);
34
+ const low = bitLength >>> 0;
35
+ message[totalLength - 8] = high >>> 24;
36
+ message[totalLength - 7] = high >>> 16;
37
+ message[totalLength - 6] = high >>> 8;
38
+ message[totalLength - 5] = high;
39
+ message[totalLength - 4] = low >>> 24;
40
+ message[totalLength - 3] = low >>> 16;
41
+ message[totalLength - 2] = low >>> 8;
42
+ message[totalLength - 1] = low;
43
+
44
+ let h0 = 0x6a09e667;
45
+ let h1 = 0xbb67ae85;
46
+ let h2 = 0x3c6ef372;
47
+ let h3 = 0xa54ff53a;
48
+ let h4 = 0x510e527f;
49
+ let h5 = 0x9b05688c;
50
+ let h6 = 0x1f83d9ab;
51
+ let h7 = 0x5be0cd19;
52
+
53
+ const words = new Uint32Array(64);
54
+
55
+ for (let offset = 0; offset < totalLength; offset += 64) {
56
+ for (let i = 0; i < 16; i += 1) {
57
+ const j = offset + i * 4;
58
+ words[i] =
59
+ ((message[j] ?? 0) << 24) |
60
+ ((message[j + 1] ?? 0) << 16) |
61
+ ((message[j + 2] ?? 0) << 8) |
62
+ (message[j + 3] ?? 0);
63
+ }
64
+
65
+ for (let i = 16; i < 64; i += 1) {
66
+ const w2 = words[i - 2] ?? 0;
67
+ const w7 = words[i - 7] ?? 0;
68
+ const w15 = words[i - 15] ?? 0;
69
+ const w16 = words[i - 16] ?? 0;
70
+ const s0 = rotr(w15, 7) ^ rotr(w15, 18) ^ (w15 >>> 3);
71
+ const s1 = rotr(w2, 17) ^ rotr(w2, 19) ^ (w2 >>> 10);
72
+ words[i] = (w16 + s0 + w7 + s1) >>> 0;
73
+ }
74
+
75
+ let a = h0;
76
+ let b = h1;
77
+ let c = h2;
78
+ let d = h3;
79
+ let e = h4;
80
+ let f = h5;
81
+ let g = h6;
82
+ let h = h7;
83
+
84
+ for (let i = 0; i < 64; i += 1) {
85
+ const s1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
86
+ const ch = (e & f) ^ (~e & g);
87
+ const temp1 =
88
+ (h + s1 + ch + (K[i] ?? 0) + (words[i] ?? 0)) >>> 0;
89
+ const s0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);
90
+ const maj = (a & b) ^ (a & c) ^ (b & c);
91
+ const temp2 = (s0 + maj) >>> 0;
92
+
93
+ h = g;
94
+ g = f;
95
+ f = e;
96
+ e = (d + temp1) >>> 0;
97
+ d = c;
98
+ c = b;
99
+ b = a;
100
+ a = (temp1 + temp2) >>> 0;
101
+ }
102
+
103
+ h0 = (h0 + a) >>> 0;
104
+ h1 = (h1 + b) >>> 0;
105
+ h2 = (h2 + c) >>> 0;
106
+ h3 = (h3 + d) >>> 0;
107
+ h4 = (h4 + e) >>> 0;
108
+ h5 = (h5 + f) >>> 0;
109
+ h6 = (h6 + g) >>> 0;
110
+ h7 = (h7 + h) >>> 0;
111
+ }
112
+
113
+ const digest = new Uint8Array(32);
114
+ const hash = [h0, h1, h2, h3, h4, h5, h6, h7];
115
+ for (let i = 0; i < hash.length; i += 1) {
116
+ const value = hash[i] ?? 0;
117
+ const j = i * 4;
118
+ digest[j] = value >>> 24;
119
+ digest[j + 1] = value >>> 16;
120
+ digest[j + 2] = value >>> 8;
121
+ digest[j + 3] = value;
122
+ }
123
+
124
+ return digest;
125
+ };
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Bitcoin Base58Check address.
3
+ *
4
+ * Validates legacy mainnet P2PKH and P2SH Bitcoin
5
+ * addresses, including the double-SHA-256 checksum.
6
+ */
7
+
8
+ import type {
9
+ GlobalValidator,
10
+ ValidateResult,
11
+ } from "../types";
12
+
13
+ import { sha256 } from "#checksums/sha256";
14
+ import { clean } from "#util/clean";
15
+ import { err } from "#util/result";
16
+
17
+ const BASE58_ALPHABET =
18
+ "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
19
+
20
+ const MIN_LENGTH = 26;
21
+ const MAX_LENGTH = 35;
22
+ const DECODED_ADDRESS_LENGTH = 25;
23
+ const CHECKSUM_LENGTH = 4;
24
+ const MAINNET_P2PKH_VERSION = 0x00;
25
+ const MAINNET_P2SH_VERSION = 0x05;
26
+
27
+ const compact = (value: string): string =>
28
+ clean(value, " ");
29
+
30
+ const decodeBase58 = (value: string): Uint8Array | null => {
31
+ let decoded = 0n;
32
+ for (const ch of value) {
33
+ const digit = BASE58_ALPHABET.indexOf(ch);
34
+ if (digit === -1) return null;
35
+ decoded = decoded * 58n + BigInt(digit);
36
+ }
37
+
38
+ const bytes: number[] = [];
39
+ while (decoded > 0n) {
40
+ bytes.push(Number(decoded & 0xffn));
41
+ decoded >>= 8n;
42
+ }
43
+ bytes.reverse();
44
+
45
+ let leadingZeros = 0;
46
+ while (value.charAt(leadingZeros) === "1") {
47
+ leadingZeros += 1;
48
+ }
49
+
50
+ const result = new Uint8Array(
51
+ leadingZeros + bytes.length,
52
+ );
53
+ result.set(bytes, leadingZeros);
54
+ return result;
55
+ };
56
+
57
+ const hasValidChecksum = (decoded: Uint8Array): boolean => {
58
+ if (decoded.length <= CHECKSUM_LENGTH) return false;
59
+
60
+ const payload = decoded.slice(0, -CHECKSUM_LENGTH);
61
+ const checksum = decoded.slice(-CHECKSUM_LENGTH);
62
+ const digest = sha256(sha256(payload));
63
+
64
+ for (let i = 0; i < CHECKSUM_LENGTH; i += 1) {
65
+ if (checksum[i] !== digest[i]) return false;
66
+ }
67
+ return true;
68
+ };
69
+
70
+ const validate = (value: string): ValidateResult => {
71
+ const v = compact(value);
72
+ if (v.length < MIN_LENGTH || v.length > MAX_LENGTH) {
73
+ return err(
74
+ "INVALID_LENGTH",
75
+ "Bitcoin Base58Check address must be 26-35 characters",
76
+ );
77
+ }
78
+ if (!/^[13]/.test(v)) {
79
+ return err(
80
+ "INVALID_COMPONENT",
81
+ "Bitcoin Base58Check address must start with 1 or 3",
82
+ );
83
+ }
84
+
85
+ const decoded = decodeBase58(v);
86
+ if (decoded === null) {
87
+ return err(
88
+ "INVALID_FORMAT",
89
+ "Bitcoin Base58Check address contains invalid characters",
90
+ );
91
+ }
92
+ if (decoded.length !== DECODED_ADDRESS_LENGTH) {
93
+ return err(
94
+ "INVALID_LENGTH",
95
+ "Bitcoin Base58Check address decoded payload must be 25 bytes",
96
+ );
97
+ }
98
+
99
+ const version = decoded[0];
100
+ if (
101
+ version !== MAINNET_P2PKH_VERSION &&
102
+ version !== MAINNET_P2SH_VERSION
103
+ ) {
104
+ return err(
105
+ "INVALID_COMPONENT",
106
+ "Bitcoin Base58Check address has an unsupported version",
107
+ );
108
+ }
109
+
110
+ if (!hasValidChecksum(decoded)) {
111
+ return err(
112
+ "INVALID_CHECKSUM",
113
+ "Bitcoin Base58Check address fails checksum validation",
114
+ );
115
+ }
116
+
117
+ return { valid: true, compact: v };
118
+ };
119
+
120
+ const format = (value: string): string => compact(value);
121
+
122
+ const btcBase58: GlobalValidator = {
123
+ scope: "global",
124
+ name: "Bitcoin Base58Check Address",
125
+ localName: "Bitcoin Base58Check Address",
126
+ abbreviation: "BTC",
127
+ aliases: [
128
+ "Bitcoin address",
129
+ "Bitcoin legacy address",
130
+ "Base58Check address",
131
+ ] as const,
132
+ candidatePattern: "[13][1-9A-HJ-NP-Za-km-z]{25,34}",
133
+ entityType: "any",
134
+ examples: [
135
+ "1BoatSLRHtKNngkdXEeobR76b53LETtpyT",
136
+ "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy",
137
+ ] as const,
138
+ compact,
139
+ format,
140
+ validate,
141
+ };
142
+
143
+ export default btcBase58;
144
+ export { compact, decodeBase58, format, validate };
@@ -0,0 +1,210 @@
1
+ /**
2
+ * Bitcoin Bech32/Bech32m address.
3
+ *
4
+ * Validates mainnet SegWit addresses with the
5
+ * Bech32 checksum for v0 and Bech32m for v1+.
6
+ */
7
+
8
+ import type {
9
+ GlobalValidator,
10
+ ValidateResult,
11
+ } from "../types";
12
+
13
+ import { clean } from "#util/clean";
14
+ import { err } from "#util/result";
15
+
16
+ const CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
17
+ const GENERATORS = [
18
+ 0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd,
19
+ 0x2a1462b3,
20
+ ] as const;
21
+ const BECH32_CONST = 1;
22
+ const BECH32M_CONST = 0x2bc830a3;
23
+ const CHECKSUM_LENGTH = 6;
24
+ const MIN_DATA_LENGTH = 11;
25
+ const MAX_DATA_LENGTH = 71;
26
+ const BC_HRP_EXPANSION = [3, 3, 0, 2, 3] as const;
27
+
28
+ const compact = (value: string): string =>
29
+ clean(value, " ").toLowerCase();
30
+
31
+ const polymod = (values: readonly number[]): number => {
32
+ let chk = 1;
33
+ for (const value of values) {
34
+ const top = chk >>> 25;
35
+ chk = (((chk & 0x1ffffff) << 5) ^ value) >>> 0;
36
+ for (let i = 0; i < GENERATORS.length; i += 1) {
37
+ const generator = GENERATORS[i] ?? 0;
38
+ if (((top >>> i) & 1) === 1) {
39
+ chk = (chk ^ generator) >>> 0;
40
+ }
41
+ }
42
+ }
43
+ return chk >>> 0;
44
+ };
45
+
46
+ const dataValues = (data: string): number[] | null => {
47
+ const values: number[] = [];
48
+ for (const ch of data) {
49
+ const value = CHARSET.indexOf(ch);
50
+ if (value === -1) return null;
51
+ values.push(value);
52
+ }
53
+ return values;
54
+ };
55
+
56
+ const convertBits = (
57
+ values: readonly number[],
58
+ fromBits: number,
59
+ toBits: number,
60
+ ): number[] | null => {
61
+ let accumulator = 0;
62
+ let bits = 0;
63
+ const maxValue = (1 << toBits) - 1;
64
+ const result: number[] = [];
65
+
66
+ for (const value of values) {
67
+ if (value < 0 || value >>> fromBits !== 0) return null;
68
+ accumulator = (accumulator << fromBits) | value;
69
+ bits += fromBits;
70
+ while (bits >= toBits) {
71
+ bits -= toBits;
72
+ result.push((accumulator >>> bits) & maxValue);
73
+ }
74
+ }
75
+
76
+ if (bits >= fromBits) return null;
77
+ if (((accumulator << (toBits - bits)) & maxValue) !== 0) {
78
+ return null;
79
+ }
80
+
81
+ return result;
82
+ };
83
+
84
+ type Bech32Validation =
85
+ | { valid: true }
86
+ | {
87
+ valid: false;
88
+ code: "format" | "checksum" | "component";
89
+ };
90
+
91
+ const validateBech32 = (
92
+ rawValue: string,
93
+ ): Bech32Validation => {
94
+ const hasLower = /[a-z]/.test(rawValue);
95
+ const hasUpper = /[A-Z]/.test(rawValue);
96
+ if (hasLower && hasUpper)
97
+ return { valid: false, code: "format" };
98
+
99
+ const v = rawValue.toLowerCase();
100
+ if (!v.startsWith("bc1"))
101
+ return { valid: false, code: "component" };
102
+
103
+ const data = v.slice(3);
104
+ if (
105
+ data.length < MIN_DATA_LENGTH ||
106
+ data.length > MAX_DATA_LENGTH
107
+ ) {
108
+ return { valid: false, code: "format" };
109
+ }
110
+
111
+ const values = dataValues(data);
112
+ if (values === null)
113
+ return { valid: false, code: "format" };
114
+
115
+ const expanded: number[] = Array.from(BC_HRP_EXPANSION);
116
+ for (const value of values) {
117
+ expanded.push(value);
118
+ }
119
+ const check = polymod(expanded);
120
+
121
+ const version = values[0];
122
+ if (version === undefined || version > 16) {
123
+ return { valid: false, code: "component" };
124
+ }
125
+
126
+ if (version === 0 && check !== BECH32_CONST) {
127
+ return { valid: false, code: "checksum" };
128
+ }
129
+ if (version > 0 && check !== BECH32M_CONST) {
130
+ return { valid: false, code: "checksum" };
131
+ }
132
+
133
+ const programValues = values.slice(1, -CHECKSUM_LENGTH);
134
+ const program = convertBits(programValues, 5, 8);
135
+ if (
136
+ program === null ||
137
+ program.length < 2 ||
138
+ program.length > 40
139
+ ) {
140
+ return { valid: false, code: "component" };
141
+ }
142
+
143
+ if (
144
+ version === 0 &&
145
+ program.length !== 20 &&
146
+ program.length !== 32
147
+ ) {
148
+ return { valid: false, code: "component" };
149
+ }
150
+
151
+ return { valid: true };
152
+ };
153
+
154
+ const validate = (value: string): ValidateResult => {
155
+ const v = compact(value);
156
+ const result = validateBech32(clean(value, " "));
157
+ if (result.valid) return { valid: true, compact: v };
158
+
159
+ if (result.code === "component") {
160
+ return err(
161
+ "INVALID_COMPONENT",
162
+ "Bitcoin Bech32 address has an unsupported component",
163
+ );
164
+ }
165
+ if (result.code === "checksum") {
166
+ return err(
167
+ "INVALID_CHECKSUM",
168
+ "Bitcoin Bech32 address fails checksum validation",
169
+ );
170
+ }
171
+ return err(
172
+ "INVALID_FORMAT",
173
+ "Bitcoin Bech32 address has an invalid format",
174
+ );
175
+ };
176
+
177
+ const format = (value: string): string => compact(value);
178
+
179
+ const btcBech32: GlobalValidator = {
180
+ scope: "global",
181
+ name: "Bitcoin Bech32 Address",
182
+ localName: "Bitcoin Bech32 Address",
183
+ abbreviation: "BTC",
184
+ aliases: [
185
+ "Bitcoin address",
186
+ "Bitcoin SegWit address",
187
+ "Bech32 address",
188
+ "Bech32m address",
189
+ ] as const,
190
+ candidatePattern:
191
+ "(?:bc1[ac-hj-np-z02-9]{11,71}|BC1[AC-HJ-NP-Z02-9]{11,71})",
192
+ entityType: "any",
193
+ examples: [
194
+ "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4",
195
+ "bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3",
196
+ ] as const,
197
+ compact,
198
+ format,
199
+ validate,
200
+ };
201
+
202
+ export default btcBech32;
203
+ export {
204
+ compact,
205
+ convertBits,
206
+ format,
207
+ polymod,
208
+ validate,
209
+ validateBech32,
210
+ };