@vbyte/btc-dev 1.0.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 (205) hide show
  1. package/LICENSE +121 -0
  2. package/README.md +5 -0
  3. package/dist/class/index.d.ts +5 -0
  4. package/dist/class/index.js +5 -0
  5. package/dist/class/signer.d.ts +18 -0
  6. package/dist/class/signer.js +32 -0
  7. package/dist/class/tx.d.ts +38 -0
  8. package/dist/class/tx.js +73 -0
  9. package/dist/class/txin.d.ts +29 -0
  10. package/dist/class/txin.js +68 -0
  11. package/dist/class/txout.d.ts +18 -0
  12. package/dist/class/txout.js +38 -0
  13. package/dist/class/witness.d.ts +20 -0
  14. package/dist/class/witness.js +57 -0
  15. package/dist/const.d.ts +22 -0
  16. package/dist/const.js +36 -0
  17. package/dist/index.d.ts +11 -0
  18. package/dist/index.js +10 -0
  19. package/dist/lib/address/encode.d.ts +3 -0
  20. package/dist/lib/address/encode.js +79 -0
  21. package/dist/lib/address/index.d.ts +20 -0
  22. package/dist/lib/address/index.js +21 -0
  23. package/dist/lib/address/p2pkh.d.ts +10 -0
  24. package/dist/lib/address/p2pkh.js +33 -0
  25. package/dist/lib/address/p2sh.d.ts +10 -0
  26. package/dist/lib/address/p2sh.js +33 -0
  27. package/dist/lib/address/p2tr.d.ts +8 -0
  28. package/dist/lib/address/p2tr.js +26 -0
  29. package/dist/lib/address/p2wpkh.d.ts +10 -0
  30. package/dist/lib/address/p2wpkh.js +34 -0
  31. package/dist/lib/address/p2wsh.d.ts +10 -0
  32. package/dist/lib/address/p2wsh.js +33 -0
  33. package/dist/lib/address/script.d.ts +5 -0
  34. package/dist/lib/address/script.js +46 -0
  35. package/dist/lib/address/util.d.ts +4 -0
  36. package/dist/lib/address/util.js +57 -0
  37. package/dist/lib/meta/index.d.ts +2 -0
  38. package/dist/lib/meta/index.js +2 -0
  39. package/dist/lib/meta/pointer.d.ts +42 -0
  40. package/dist/lib/meta/pointer.js +69 -0
  41. package/dist/lib/meta/scribe.d.ts +7 -0
  42. package/dist/lib/meta/scribe.js +192 -0
  43. package/dist/lib/psbt/encoder.d.ts +5 -0
  44. package/dist/lib/psbt/encoder.js +21 -0
  45. package/dist/lib/psbt/index.d.ts +4 -0
  46. package/dist/lib/psbt/index.js +4 -0
  47. package/dist/lib/psbt/meta.d.ts +3 -0
  48. package/dist/lib/psbt/meta.js +11 -0
  49. package/dist/lib/psbt/util.d.ts +5 -0
  50. package/dist/lib/psbt/util.js +44 -0
  51. package/dist/lib/psbt/validate.d.ts +2 -0
  52. package/dist/lib/psbt/validate.js +11 -0
  53. package/dist/lib/script/decode.d.ts +2 -0
  54. package/dist/lib/script/decode.js +55 -0
  55. package/dist/lib/script/encode.d.ts +6 -0
  56. package/dist/lib/script/encode.js +80 -0
  57. package/dist/lib/script/index.d.ts +126 -0
  58. package/dist/lib/script/index.js +17 -0
  59. package/dist/lib/script/util.d.ts +2 -0
  60. package/dist/lib/script/util.js +10 -0
  61. package/dist/lib/script/words.d.ts +116 -0
  62. package/dist/lib/script/words.js +164 -0
  63. package/dist/lib/sighash/index.d.ts +5 -0
  64. package/dist/lib/sighash/index.js +5 -0
  65. package/dist/lib/sighash/segwit.d.ts +3 -0
  66. package/dist/lib/sighash/segwit.js +89 -0
  67. package/dist/lib/sighash/sign.d.ts +3 -0
  68. package/dist/lib/sighash/sign.js +20 -0
  69. package/dist/lib/sighash/taproot.d.ts +9 -0
  70. package/dist/lib/sighash/taproot.js +124 -0
  71. package/dist/lib/sighash/util.d.ts +3 -0
  72. package/dist/lib/sighash/util.js +16 -0
  73. package/dist/lib/sighash/verify.d.ts +1 -0
  74. package/dist/lib/sighash/verify.js +1 -0
  75. package/dist/lib/taproot/cblock.d.ts +3 -0
  76. package/dist/lib/taproot/cblock.js +55 -0
  77. package/dist/lib/taproot/encode.d.ts +6 -0
  78. package/dist/lib/taproot/encode.js +26 -0
  79. package/dist/lib/taproot/index.d.ts +4 -0
  80. package/dist/lib/taproot/index.js +4 -0
  81. package/dist/lib/taproot/parse.d.ts +11 -0
  82. package/dist/lib/taproot/parse.js +47 -0
  83. package/dist/lib/taproot/tree.d.ts +3 -0
  84. package/dist/lib/taproot/tree.js +49 -0
  85. package/dist/lib/tx/create.d.ts +6 -0
  86. package/dist/lib/tx/create.js +54 -0
  87. package/dist/lib/tx/decode.d.ts +9 -0
  88. package/dist/lib/tx/decode.js +109 -0
  89. package/dist/lib/tx/encode.d.ts +15 -0
  90. package/dist/lib/tx/encode.js +94 -0
  91. package/dist/lib/tx/index.d.ts +10 -0
  92. package/dist/lib/tx/index.js +10 -0
  93. package/dist/lib/tx/locktime.d.ts +7 -0
  94. package/dist/lib/tx/locktime.js +37 -0
  95. package/dist/lib/tx/meta.d.ts +8 -0
  96. package/dist/lib/tx/meta.js +48 -0
  97. package/dist/lib/tx/parse.d.ts +2 -0
  98. package/dist/lib/tx/parse.js +12 -0
  99. package/dist/lib/tx/sequence.d.ts +7 -0
  100. package/dist/lib/tx/sequence.js +65 -0
  101. package/dist/lib/tx/size.d.ts +10 -0
  102. package/dist/lib/tx/size.js +48 -0
  103. package/dist/lib/tx/validate.d.ts +7 -0
  104. package/dist/lib/tx/validate.js +21 -0
  105. package/dist/lib/tx/witness.d.ts +3 -0
  106. package/dist/lib/tx/witness.js +85 -0
  107. package/dist/main.cjs +14625 -0
  108. package/dist/main.cjs.map +1 -0
  109. package/dist/module.mjs +14610 -0
  110. package/dist/module.mjs.map +1 -0
  111. package/dist/package.json +106 -0
  112. package/dist/schema/index.d.ts +2 -0
  113. package/dist/schema/index.js +2 -0
  114. package/dist/schema/taproot.d.ts +18 -0
  115. package/dist/schema/taproot.js +9 -0
  116. package/dist/schema/tx.d.ts +278 -0
  117. package/dist/schema/tx.js +35 -0
  118. package/dist/script.js +15 -0
  119. package/dist/script.js.map +1 -0
  120. package/dist/types/address.d.ts +34 -0
  121. package/dist/types/address.js +1 -0
  122. package/dist/types/index.d.ts +9 -0
  123. package/dist/types/index.js +9 -0
  124. package/dist/types/meta.d.ts +10 -0
  125. package/dist/types/meta.js +1 -0
  126. package/dist/types/psbt.d.ts +9 -0
  127. package/dist/types/psbt.js +1 -0
  128. package/dist/types/sighash.d.ts +14 -0
  129. package/dist/types/sighash.js +1 -0
  130. package/dist/types/taproot.d.ts +35 -0
  131. package/dist/types/taproot.js +1 -0
  132. package/dist/types/transaction.d.ts +81 -0
  133. package/dist/types/transaction.js +1 -0
  134. package/dist/types/txdata.d.ts +45 -0
  135. package/dist/types/txdata.js +1 -0
  136. package/dist/types/txmeta.d.ts +19 -0
  137. package/dist/types/txmeta.js +1 -0
  138. package/dist/types/witness.d.ts +30 -0
  139. package/dist/types/witness.js +1 -0
  140. package/package.json +106 -0
  141. package/src/class/index.ts +5 -0
  142. package/src/class/signer.ts +47 -0
  143. package/src/class/tx.ts +118 -0
  144. package/src/class/txin.ts +95 -0
  145. package/src/class/txout.ts +57 -0
  146. package/src/class/witness.ts +85 -0
  147. package/src/const.ts +43 -0
  148. package/src/index.ts +14 -0
  149. package/src/lib/address/encode.ts +183 -0
  150. package/src/lib/address/index.ts +24 -0
  151. package/src/lib/address/p2pkh.ts +65 -0
  152. package/src/lib/address/p2sh.ts +65 -0
  153. package/src/lib/address/p2tr.ts +51 -0
  154. package/src/lib/address/p2wpkh.ts +67 -0
  155. package/src/lib/address/p2wsh.ts +65 -0
  156. package/src/lib/address/script.ts +63 -0
  157. package/src/lib/address/util.ts +102 -0
  158. package/src/lib/meta/index.ts +2 -0
  159. package/src/lib/meta/pointer.ts +107 -0
  160. package/src/lib/meta/scribe.ts +251 -0
  161. package/src/lib/psbt/encoder.ts +24 -0
  162. package/src/lib/psbt/index.ts +4 -0
  163. package/src/lib/psbt/meta.ts +15 -0
  164. package/src/lib/psbt/util.ts +62 -0
  165. package/src/lib/psbt/validate.ts +18 -0
  166. package/src/lib/script/decode.ts +75 -0
  167. package/src/lib/script/encode.ts +130 -0
  168. package/src/lib/script/index.ts +26 -0
  169. package/src/lib/script/util.ts +78 -0
  170. package/src/lib/script/words.ts +182 -0
  171. package/src/lib/sighash/index.ts +5 -0
  172. package/src/lib/sighash/segwit.ts +152 -0
  173. package/src/lib/sighash/sign.ts +35 -0
  174. package/src/lib/sighash/taproot.ts +236 -0
  175. package/src/lib/sighash/util.ts +29 -0
  176. package/src/lib/sighash/verify.ts +83 -0
  177. package/src/lib/taproot/cblock.ts +95 -0
  178. package/src/lib/taproot/encode.ts +49 -0
  179. package/src/lib/taproot/index.ts +4 -0
  180. package/src/lib/taproot/parse.ts +65 -0
  181. package/src/lib/taproot/tree.ts +94 -0
  182. package/src/lib/tx/create.ts +82 -0
  183. package/src/lib/tx/decode.ts +145 -0
  184. package/src/lib/tx/encode.ts +154 -0
  185. package/src/lib/tx/index.ts +10 -0
  186. package/src/lib/tx/locktime.ts +57 -0
  187. package/src/lib/tx/meta.ts +73 -0
  188. package/src/lib/tx/parse.ts +16 -0
  189. package/src/lib/tx/sequence.ts +146 -0
  190. package/src/lib/tx/size.ts +77 -0
  191. package/src/lib/tx/validate.ts +36 -0
  192. package/src/lib/tx/witness.ts +122 -0
  193. package/src/schema/index.ts +2 -0
  194. package/src/schema/taproot.ts +12 -0
  195. package/src/schema/tx.ts +42 -0
  196. package/src/types/address.ts +39 -0
  197. package/src/types/index.ts +9 -0
  198. package/src/types/meta.ts +10 -0
  199. package/src/types/psbt.ts +15 -0
  200. package/src/types/sighash.ts +16 -0
  201. package/src/types/taproot.ts +40 -0
  202. package/src/types/transaction.ts +98 -0
  203. package/src/types/txdata.ts +53 -0
  204. package/src/types/txmeta.ts +25 -0
  205. package/src/types/witness.ts +36 -0
@@ -0,0 +1,67 @@
1
+ import { Buff } from '@vbyte/buff'
2
+ import { Assert } from '@vbyte/micro-lib'
3
+ import { hash160 } from '@vbyte/micro-lib/hash'
4
+ import { encode_address } from './encode.js'
5
+
6
+ import {
7
+ get_address_config,
8
+ parse_address
9
+ } from './util.js'
10
+
11
+ import type {
12
+ AddressData,
13
+ ChainNetwork
14
+ } from '@/types/index.js'
15
+
16
+ const ADDR_TYPE = 'p2w-pkh'
17
+
18
+ export namespace P2WPKH {
19
+ export const create = create_p2wpkh_address
20
+ export const encode = encode_p2wpkh_address
21
+ export const decode = decode_p2wpkh_address
22
+ }
23
+
24
+ function create_p2wpkh_address (
25
+ pubkey : string | Uint8Array,
26
+ network : ChainNetwork = 'main',
27
+ ) : string {
28
+ // Convert the public key into bytes.
29
+ const bytes = Buff.bytes(pubkey)
30
+ // Assert the payload size is correct.
31
+ Assert.size(bytes, 33, `invalid payload size: ${bytes.length} !== 33` )
32
+ // Convert the bytes into a hash.
33
+ const hash = hash160(bytes)
34
+ // Encode the address.
35
+ return encode_p2wpkh_address(hash, network)
36
+ }
37
+
38
+ function encode_p2wpkh_address (
39
+ pk_hash : string | Uint8Array,
40
+ network : ChainNetwork = 'main',
41
+ ) : string {
42
+ // Convert the public key hash into bytes.
43
+ const bytes = Buff.bytes(pk_hash)
44
+ // Get the address configuration.
45
+ const config = get_address_config(network, ADDR_TYPE)
46
+ // Assert the configuration exists.
47
+ Assert.exists(config, `unrecognized address config: ${ADDR_TYPE} on ${network}` )
48
+ // Assert the payload size is correct.
49
+ Assert.size(bytes, config.size, `invalid payload size: ${bytes.length} !== ${config.size}` )
50
+ // Encode the address.
51
+ return encode_address({
52
+ data : bytes,
53
+ format : 'bech32',
54
+ prefix : config.prefix
55
+ })
56
+ }
57
+
58
+ function decode_p2wpkh_address (
59
+ address : string
60
+ ) : AddressData {
61
+ // Parse the address.
62
+ const parsed = parse_address(address)
63
+ // Assert the address type is correct.
64
+ Assert.ok(parsed.type === 'p2w-pkh', `address type mismatch: ${parsed.type} !== ${ADDR_TYPE}`)
65
+ // Return the parsed address.
66
+ return parsed
67
+ }
@@ -0,0 +1,65 @@
1
+ import { Buff } from '@vbyte/buff'
2
+ import { Assert } from '@vbyte/micro-lib'
3
+ import { sha256 } from '@vbyte/micro-lib/hash'
4
+ import { encode_address } from './encode.js'
5
+
6
+ import {
7
+ get_address_config,
8
+ parse_address
9
+ } from './util.js'
10
+
11
+ import type {
12
+ ChainNetwork,
13
+ AddressData
14
+ } from '@/types/index.js'
15
+
16
+ const ADDR_TYPE = 'p2w-sh'
17
+
18
+ export namespace P2WSH {
19
+ export const create = create_p2wsh_address
20
+ export const encode = encode_p2wsh_address
21
+ export const decode = decode_p2wsh_address
22
+ }
23
+
24
+ function create_p2wsh_address (
25
+ script : string | Uint8Array,
26
+ network : ChainNetwork = 'main',
27
+ ) : string {
28
+ // Convert the script into bytes.
29
+ const bytes = Buff.bytes(script)
30
+ // Convert the bytes into a hash.
31
+ const hash = sha256(bytes)
32
+ // Encode the address.
33
+ return encode_p2wsh_address(hash, network)
34
+ }
35
+
36
+ function encode_p2wsh_address (
37
+ script_hash : string | Uint8Array,
38
+ network : ChainNetwork = 'main',
39
+ ) : string {
40
+ // Convert the script hash into bytes.
41
+ const bytes = Buff.bytes(script_hash)
42
+ // Get the address configuration.
43
+ const config = get_address_config(network, ADDR_TYPE)
44
+ // Assert the configuration exists.
45
+ Assert.exists(config, `unrecognized address config: ${ADDR_TYPE} on ${network}` )
46
+ // Assert the payload size is correct.
47
+ Assert.size(bytes, config.size, `invalid payload size: ${bytes.length} !== ${config.size}` )
48
+ // Encode the address.
49
+ return encode_address({
50
+ data : bytes,
51
+ format : 'bech32',
52
+ prefix : config.prefix
53
+ })
54
+ }
55
+
56
+ function decode_p2wsh_address (
57
+ address : string
58
+ ) : AddressData {
59
+ // Parse the address.
60
+ const parsed = parse_address(address)
61
+ // Assert the address type is correct.
62
+ Assert.ok(parsed.type === 'p2w-sh', `address type mismatch: ${parsed.type} !== ${ADDR_TYPE}`)
63
+ // Return the parsed address.
64
+ return parsed
65
+ }
@@ -0,0 +1,63 @@
1
+ import type { AddressType } from '@/types/index.js'
2
+
3
+ /**
4
+ * Get the address script.
5
+ *
6
+ * @param script_key - The script key.
7
+ * @param script_type - The script type.
8
+ * @returns The address script.
9
+ */
10
+ export function get_address_script (
11
+ script_key : string,
12
+ script_type : AddressType
13
+ ) {
14
+ switch (script_type) {
15
+ case 'p2pkh':
16
+ return get_p2pkh_script(script_key)
17
+ case 'p2sh':
18
+ return get_p2sh_script(script_key)
19
+ case 'p2w-pkh':
20
+ return get_p2w_pkh_script(script_key)
21
+ case 'p2w-sh':
22
+ return get_p2w_sh_script(script_key)
23
+ case 'p2tr':
24
+ return get_p2tr_script(script_key)
25
+ default:
26
+ throw new Error('unrecognized script type: ' + script_type)
27
+ }
28
+ }
29
+
30
+ function get_p2pkh_script (script_key : string) {
31
+ return {
32
+ script_hex : '76a914' + script_key + '88ac',
33
+ script_asm : [ 'OP_DUP', 'OP_HASH160', script_key, 'OP_EQUALVERIFY', 'OP_CHECKSIG' ]
34
+ }
35
+ }
36
+
37
+ function get_p2sh_script (script_key : string) {
38
+ return {
39
+ script_hex : 'a914' + script_key + '87',
40
+ script_asm : [ 'OP_HASH160', script_key, 'OP_EQUAL' ]
41
+ }
42
+ }
43
+
44
+ function get_p2w_pkh_script (script_key : string) {
45
+ return {
46
+ script_hex : '0014' + script_key,
47
+ script_asm : [ 'OP_0', script_key ]
48
+ }
49
+ }
50
+
51
+ function get_p2w_sh_script (script_key : string) {
52
+ return {
53
+ script_hex : '0020' + script_key,
54
+ script_asm : [ 'OP_0', script_key ]
55
+ }
56
+ }
57
+
58
+ function get_p2tr_script (script_key : string) {
59
+ return {
60
+ script_hex : '5120' + script_key,
61
+ script_asm : [ 'OP_1', script_key ]
62
+ }
63
+ }
@@ -0,0 +1,102 @@
1
+ import { Buff } from '@vbyte/buff'
2
+ import { decode_address } from './encode.js'
3
+ import { get_address_script } from './script.js'
4
+
5
+ import type {
6
+ AddressConfig,
7
+ AddressConfigEntry,
8
+ AddressType,
9
+ ChainNetwork,
10
+ AddressContext,
11
+ AddressData
12
+ } from '@/types/index.js'
13
+
14
+ const CONFIG_TABLE : AddressConfigEntry[] = [
15
+ [ '1', 'p2pkh', 'main', 20, 'base58', 0x00 ],
16
+ [ '3', 'p2sh', 'main', 20, 'base58', 0x05 ],
17
+ [ 'm', 'p2pkh', 'testnet', 20, 'base58', 0x6F ],
18
+ [ 'n', 'p2pkh', 'testnet', 20, 'base58', 0x6F ],
19
+ [ '2', 'p2sh', 'testnet', 20, 'base58', 0xC4 ],
20
+ [ 'm', 'p2pkh', 'regtest', 20, 'base58', 0x6F ],
21
+ [ 'n', 'p2pkh', 'regtest', 20, 'base58', 0x6F ],
22
+ [ '2', 'p2sh', 'regtest', 20, 'base58', 0xC4 ],
23
+ [ 'bc', 'p2w-pkh', 'main', 20, 'bech32', 0 ],
24
+ [ 'tb', 'p2w-pkh', 'testnet', 20, 'bech32', 0 ],
25
+ [ 'bcrt', 'p2w-pkh', 'regtest', 20, 'bech32', 0 ],
26
+ [ 'bc', 'p2w-sh', 'main', 32, 'bech32', 0 ],
27
+ [ 'tb', 'p2w-sh', 'testnet', 32, 'bech32', 0 ],
28
+ [ 'bcrt', 'p2w-sh', 'regtest', 32, 'bech32', 0 ],
29
+ [ 'bc', 'p2tr', 'main', 32, 'bech32m', 1 ],
30
+ [ 'tb', 'p2tr', 'testnet', 32, 'bech32m', 1 ],
31
+ [ 'bcrt', 'p2tr', 'regtest', 32, 'bech32m', 1 ]
32
+ ]
33
+
34
+ /**
35
+ * Lookup an address configuration by its type and network.
36
+ *
37
+ * @param address_network - The network of the address.
38
+ * @param address_type - The type of the address.
39
+ * @returns The address information, or null if the address is not recognized.
40
+ */
41
+ export function get_address_config (
42
+ address_network : ChainNetwork,
43
+ address_type : AddressType
44
+ ) : AddressConfig | null {
45
+ // For each configuration in the table,
46
+ for (const [ prefix, type, network, size, format, version ] of CONFIG_TABLE) {
47
+ // Check if the address matches the configuration
48
+ if (type === address_type && network === address_network) {
49
+ // Return the address configuration.
50
+ return { type, prefix, network, size, format, version }
51
+ }
52
+ }
53
+ // If no configuration matches the address, return null.
54
+ return null
55
+ }
56
+
57
+ /**
58
+ * Get the address context.
59
+ *
60
+ * @param address - The address to get the context for.
61
+ * @returns The address context.
62
+ */
63
+ export function get_address_ctx (address : string) : AddressContext {
64
+ // Decode the address.
65
+ const dec = decode_address(address)
66
+ // For each configuration in the table,
67
+ for (const [ prefix, type, network, size, format, version ] of CONFIG_TABLE) {
68
+
69
+ // Check if the address matches the configuration
70
+ if (format !== dec.format) continue
71
+ if (size !== dec.data.length) continue
72
+ if (version !== dec.version) continue
73
+
74
+ if (dec.prefix) {
75
+ if (prefix !== dec.prefix) continue
76
+ } else {
77
+ if (!address.startsWith(prefix)) continue
78
+ }
79
+
80
+ // Convert the decoded data into a hex string.
81
+ const hex = Buff.uint(dec.data).hex
82
+ // Return the address configuration and data.
83
+ return { data: dec.data, hex, type, prefix, network, size, format, version }
84
+ }
85
+ // Otherwise, throw an error
86
+ throw new Error('address configuration is invalid')
87
+ }
88
+
89
+ /**
90
+ * Parse an address into its data and script.
91
+ *
92
+ * @param address - The address to parse.
93
+ * @returns The address data and script.
94
+ */
95
+ export function parse_address (address : string) : AddressData {
96
+ // Get the address context.
97
+ const ctx = get_address_ctx(address)
98
+ // Get the address script.
99
+ const script = get_address_script(ctx.hex, ctx.type)
100
+ // Return the address data.
101
+ return { ...ctx, ...script }
102
+ }
@@ -0,0 +1,2 @@
1
+ export * from './pointer.js'
2
+ export * from './scribe.js'
@@ -0,0 +1,107 @@
1
+ export namespace TxPointer {
2
+ export const outpoint = {
3
+ encode : encode_outpoint,
4
+ decode : decode_outpoint,
5
+ verify : verify_outpoint,
6
+ assert : assert_outpoint,
7
+ }
8
+ export const record_id = {
9
+ encode : encode_inscription_id,
10
+ decode : decode_inscription_id,
11
+ verify : verify_inscription_id,
12
+ assert : assert_inscription_id,
13
+ }
14
+ export const rune_id = {
15
+ encode : encode_rune_id,
16
+ decode : decode_rune_id,
17
+ verify : verify_rune_id,
18
+ assert : assert_rune_id,
19
+ }
20
+ }
21
+
22
+ function encode_inscription_id (
23
+ txid : string,
24
+ order : number = 0
25
+ ) : string {
26
+ return `${txid}i${order}`
27
+ }
28
+
29
+ function decode_inscription_id (
30
+ inscription_id : string
31
+ ) : { txid : string, order : number } {
32
+ assert_inscription_id(inscription_id)
33
+ const [ txid, order ] = inscription_id.split('i')
34
+ return { txid, order : parseInt(order) }
35
+ }
36
+
37
+ function verify_inscription_id (
38
+ inscription_id : string
39
+ ) : boolean {
40
+ return inscription_id.match(/^[a-f0-9]{64}i\d+$/) !== null
41
+ }
42
+
43
+ function assert_inscription_id (
44
+ inscription_id : string
45
+ ) : void {
46
+ if (!verify_inscription_id(inscription_id)) {
47
+ throw new Error(`invalid inscription id: ${inscription_id}`)
48
+ }
49
+ }
50
+
51
+ function encode_rune_id (
52
+ block_height : number,
53
+ block_index : number
54
+ ) : string {
55
+ return `${block_height}:${block_index}`
56
+ }
57
+
58
+ function decode_rune_id (
59
+ rune_id : string
60
+ ) : { block_height : number, block_index : number } {
61
+ assert_rune_id(rune_id)
62
+ const [ block_height, block_index ] = rune_id.split(':')
63
+ return { block_height : parseInt(block_height), block_index : parseInt(block_index) }
64
+ }
65
+
66
+ function verify_rune_id (
67
+ rune_id : string
68
+ ) : boolean {
69
+ return rune_id.match(/^\d+:\d+$/) !== null
70
+ }
71
+
72
+ function assert_rune_id (
73
+ rune_id : string
74
+ ) : void {
75
+ if (!verify_rune_id(rune_id)) {
76
+ throw new Error(`invalid rune id: ${rune_id}`)
77
+ }
78
+ }
79
+
80
+ function encode_outpoint (
81
+ txid : string,
82
+ vout : number
83
+ ) : string {
84
+ return `${txid}:${vout}`
85
+ }
86
+
87
+ function decode_outpoint (
88
+ outpoint : string
89
+ ) : { txid : string, vout : number } {
90
+ assert_outpoint(outpoint)
91
+ const [ txid, vout ] = outpoint.split(':')
92
+ return { txid, vout : parseInt(vout) }
93
+ }
94
+
95
+ function verify_outpoint (
96
+ outpoint : string
97
+ ) : boolean {
98
+ return outpoint.match(/^[a-f0-9]{64}:[0-9]+$/) !== null
99
+ }
100
+
101
+ function assert_outpoint (
102
+ outpoint : string
103
+ ) : void {
104
+ if (!verify_outpoint(outpoint)) {
105
+ throw new Error(`invalid outpoint: ${outpoint}`)
106
+ }
107
+ }
@@ -0,0 +1,251 @@
1
+ import { Buff, Stream } from '@vbyte/buff'
2
+ import { Assert } from '@vbyte/micro-lib'
3
+ import { encode_script } from '@/lib/script/encode.js'
4
+ import { decode_script } from '@/lib/script/decode.js'
5
+
6
+ import type { InscriptionData } from '@/types/index.js'
7
+
8
+ const _0n = BigInt(0)
9
+ const _1n = BigInt(1)
10
+ const _26n = BigInt(26)
11
+
12
+ export namespace Inscription {
13
+ export const encode = encode_inscription
14
+ export const decode = decode_inscription
15
+ }
16
+
17
+ export function decode_inscription (
18
+ script : string
19
+ ) : InscriptionData[] {
20
+ const envelopes = parse_envelopes(script)
21
+ return envelopes.map(parse_record)
22
+ }
23
+
24
+ export function encode_inscription (data : InscriptionData[]) : string {
25
+ return data.map(create_envelope).join('')
26
+ }
27
+
28
+ function create_envelope (data : InscriptionData) : string {
29
+ let asm : string[] = [ 'OP_0', 'OP_IF', '6f7264' ]
30
+
31
+ if (typeof data.delegate === 'string') {
32
+ const id = encode_id(data.delegate)
33
+ asm.push('OP_11', id)
34
+ }
35
+
36
+ if (typeof data.ref === 'string') {
37
+ asm.push('OP_WITHIN', data.ref)
38
+ }
39
+
40
+ if (typeof data.parent === 'string') {
41
+ const id = encode_id(data.parent)
42
+ asm.push('OP_3', id)
43
+ }
44
+
45
+ if (typeof data.opcode === 'number') {
46
+ const code = encode_pointer(data.opcode)
47
+ asm.push('OP_NOP', code)
48
+ }
49
+
50
+ if (typeof data.pointer === 'number') {
51
+ const ptr = encode_pointer(data.pointer)
52
+ asm.push('OP_2', ptr)
53
+ }
54
+
55
+ if (typeof data.rune === 'string') {
56
+ const label = encode_rune_label(data.rune)
57
+ asm.push('OP_13', label)
58
+ }
59
+
60
+ if (typeof data.mimetype === 'string') {
61
+ const label = encode_label(data.mimetype)
62
+ asm.push('OP_1', label)
63
+ }
64
+
65
+ if (typeof data.content === 'string') {
66
+ const chunks = encode_content(data.content)
67
+ asm.push('OP_0', ...chunks)
68
+ }
69
+
70
+ asm.push('OP_ENDIF')
71
+
72
+ return encode_script(asm)
73
+ }
74
+
75
+ function parse_envelopes (
76
+ script : string
77
+ ) : string[][] {
78
+
79
+ const words = decode_script(script)
80
+ const start_idx = words.findIndex(e => e === 'OP_0')
81
+
82
+ Assert.ok(start_idx !== -1, 'inscription envelope not found')
83
+
84
+ const envelopes = []
85
+
86
+ for (let idx = start_idx; idx < words.length; idx++) {
87
+ Assert.ok(words[idx + 1] === 'OP_IF', 'OP_IF missing from envelope')
88
+ Assert.ok(words[idx + 2] === '6f7264', 'magic bytes missing from envelope')
89
+
90
+ const stop_idx = words.findIndex(e => e === 'OP_ENDIF')
91
+ Assert.ok(stop_idx !== -1, 'inscription envelope missing END_IF statement')
92
+
93
+ const env = words.slice(idx + 3, stop_idx)
94
+ envelopes.push(env)
95
+ idx += stop_idx
96
+ }
97
+
98
+ return envelopes
99
+ }
100
+
101
+ function parse_record (envelope : string[]) {
102
+ const record : InscriptionData = {}
103
+
104
+ for (let i = 0; i < envelope.length; i++) {
105
+ switch (envelope[i]) {
106
+ case 'OP_1':
107
+ record.mimetype = decode_label(envelope[i+1])
108
+ i += 1
109
+ break
110
+ case 'OP_2':
111
+ record.pointer = decode_pointer(envelope[i+1])
112
+ i += 1
113
+ break
114
+ case 'OP_3':
115
+ record.parent = decode_id(envelope[i+1])
116
+ i += 1
117
+ break
118
+ case 'OP_11':
119
+ record.delegate = decode_id(envelope[i+1])
120
+ i += 1
121
+ break
122
+ case 'OP_13':
123
+ record.rune = decode_rune_label(envelope[i+1])
124
+ i += 1
125
+ break
126
+ case 'OP_WITHIN':
127
+ record.ref = envelope[i+1]
128
+ i += 1
129
+ break;
130
+ case 'OP_NOP':
131
+ record.opcode = decode_pointer(envelope[i+1])
132
+ i += 1
133
+ break;
134
+ case 'OP_0':
135
+ record.content = decode_content(envelope.slice(i+1))
136
+ return record
137
+ }
138
+ }
139
+ return record
140
+ }
141
+
142
+ function encode_id (
143
+ identifier : string
144
+ ) : string {
145
+ Assert.ok(identifier.includes('i'), 'identifier must include an index')
146
+ const parts = identifier.split('i')
147
+ const bytes = Buff.hex(parts[0])
148
+ const idx = Number(parts[1])
149
+ const txid = bytes.reverse().hex
150
+ return (idx !== 0) ? txid + Buff.num(idx).hex : txid
151
+ }
152
+
153
+ function decode_id (
154
+ hexstr : string
155
+ ) : string {
156
+ const bytes = Buff.hex(hexstr)
157
+ const idx = bytes.at(-1) ?? 0
158
+ const txid = bytes.slice(0, -1).reverse().hex
159
+ return txid + 'i' + String(idx)
160
+ }
161
+
162
+ function encode_pointer (
163
+ pointer : number
164
+ ) : string {
165
+ return Buff.num(pointer).reverse().hex
166
+ }
167
+
168
+ function decode_pointer (
169
+ hexstr : string
170
+ ) : number {
171
+ return Buff.hex(hexstr).reverse().num
172
+ }
173
+
174
+ function encode_label (
175
+ label : string
176
+ ) : string {
177
+ return Buff.str(label).hex
178
+ }
179
+
180
+ function decode_label (
181
+ hexstr : string
182
+ ) : string {
183
+ return Buff.hex(hexstr).str
184
+ }
185
+
186
+ function encode_content (
187
+ content : string
188
+ ) : string[] {
189
+ const bytes = Buff.is_hex(content)
190
+ ? Buff.hex(content)
191
+ : Buff.str(content)
192
+ const stream = new Stream(bytes)
193
+ const chunks : string[]= []
194
+ while (stream.size > 0) {
195
+ if (stream.size > 520) {
196
+ const chunk = stream.read(520)
197
+ chunks.push(chunk.hex)
198
+ } else {
199
+ const chunk = stream.read(stream.size)
200
+ chunks.push(chunk.hex)
201
+ }
202
+ }
203
+ return chunks
204
+ }
205
+
206
+ function decode_content (
207
+ hexstrs : string[],
208
+ type : 'hex' | 'utf8' = 'hex'
209
+ ) : string {
210
+ const data = Buff.join(hexstrs)
211
+ return (type === 'hex')
212
+ ? data.hex
213
+ : data.str
214
+ }
215
+
216
+ function encode_rune_label (label : string) : string {
217
+ const str = label.toUpperCase()
218
+ let big = _0n
219
+ for (const char of str) {
220
+ if (char >= 'A' && char <= 'Z') {
221
+ big = big * _26n + BigInt(char.charCodeAt(0) - ('A'.charCodeAt(0) - 1))
222
+ } else { continue }
223
+ }
224
+ big = big - _1n
225
+ return Buff.big(big).reverse().hex
226
+ }
227
+
228
+ function decode_rune_label (hex: string): string {
229
+ // Convert hex to BigInt, with byte order reversed
230
+ let big = Buff.hex(hex).reverse().big
231
+ // Add 1 as per the encoding algorithm
232
+ big = big + _1n
233
+ // Initialize result string
234
+ let result = ''
235
+ // Convert the BigInt back to a string of alphabet characters
236
+ while (big > _0n) {
237
+ // Get remainder after division by 26
238
+ const mod = big % _26n
239
+ // Convert remainder to character (0 maps to 'Z', 1 to 'A', 2 to 'B', etc.)
240
+ if (mod === _0n) {
241
+ result = 'Z' + result
242
+ big = big / _26n - _1n // Adjust for special case of 'Z'
243
+ } else {
244
+ // Map 1 to 'A', 2 to 'B', etc.
245
+ const charCode = Number(mod) + 'A'.charCodeAt(0) - 1
246
+ result = String.fromCharCode(charCode) + result
247
+ big = big / _26n
248
+ }
249
+ }
250
+ return result
251
+ }
@@ -0,0 +1,24 @@
1
+ import { Base64 } from '@vbyte/micro-lib'
2
+ import { Transaction } from '@scure/btc-signer'
3
+
4
+ import type { PSBTData } from '@/types/index.js'
5
+
6
+ export function decode_psbt (b64str : string) : Transaction {
7
+ const psbt = Base64.decode(b64str)
8
+ return Transaction.fromPSBT(psbt, { allowUnknownOutputs: true })
9
+ }
10
+
11
+ export function encode_psbt (psbt : PSBTData) : string {
12
+ const psbt_bytes = psbt.toPSBT(0)
13
+ return Base64.encode(psbt_bytes)
14
+ }
15
+
16
+ export function parse_psbt (psbt : string | PSBTData) : Transaction {
17
+ if (psbt instanceof Transaction) {
18
+ return psbt
19
+ } else if (typeof psbt === 'string') {
20
+ return decode_psbt(psbt)
21
+ } else {
22
+ throw new Error('invalid psbt input: ' + psbt)
23
+ }
24
+ }
@@ -0,0 +1,4 @@
1
+ export * from './encoder.js'
2
+ export * from './meta.js'
3
+ export * from './util.js'
4
+ export * from './validate.js'
@@ -0,0 +1,15 @@
1
+ import { parse_psbt } from './encoder.js'
2
+ import { finalize_legacy_inputs } from './util.js'
3
+
4
+ import { PSBTData } from '@/types/index.js'
5
+
6
+ export function get_vsize (psbt : string | PSBTData) : number {
7
+ const pdata = parse_psbt(psbt)
8
+ return pdata.vsize
9
+ }
10
+
11
+ export function get_txhex (psbt : PSBTData) : string {
12
+ let pdata = parse_psbt(psbt)
13
+ pdata = finalize_legacy_inputs(pdata)
14
+ return pdata.hex
15
+ }