ecash-lib 1.2.2-rc9 → 1.4.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 (125) hide show
  1. package/README.md +2 -1
  2. package/dist/ecc.d.ts +12 -0
  3. package/dist/ecc.d.ts.map +1 -1
  4. package/dist/ecc.js +9 -47
  5. package/dist/ecc.js.map +1 -1
  6. package/dist/ffi/ecash_lib_wasm_bg_browser.wasm +0 -0
  7. package/dist/ffi/ecash_lib_wasm_bg_browser.wasm.d.ts +18 -4
  8. package/dist/ffi/ecash_lib_wasm_bg_nodejs.wasm +0 -0
  9. package/dist/ffi/ecash_lib_wasm_bg_nodejs.wasm.d.ts +18 -4
  10. package/dist/ffi/ecash_lib_wasm_browser.d.ts +95 -4
  11. package/dist/ffi/ecash_lib_wasm_browser.js +245 -14
  12. package/dist/ffi/ecash_lib_wasm_nodejs.d.ts +77 -0
  13. package/dist/ffi/ecash_lib_wasm_nodejs.js +247 -14
  14. package/dist/hash.d.ts +21 -1
  15. package/dist/hash.d.ts.map +1 -1
  16. package/dist/hash.js +16 -10
  17. package/dist/hash.js.map +1 -1
  18. package/dist/hdwallet.d.ts +33 -0
  19. package/dist/hdwallet.d.ts.map +1 -0
  20. package/dist/hdwallet.js +148 -0
  21. package/dist/hdwallet.js.map +1 -0
  22. package/dist/hmac.d.ts +13 -0
  23. package/dist/hmac.d.ts.map +1 -0
  24. package/dist/hmac.js +63 -0
  25. package/dist/hmac.js.map +1 -0
  26. package/dist/index.d.ts +3 -1
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +3 -1
  29. package/dist/index.js.map +1 -1
  30. package/dist/initBrowser.d.ts.map +1 -1
  31. package/dist/initBrowser.js +3 -0
  32. package/dist/initBrowser.js.map +1 -1
  33. package/dist/initNodeJs.d.ts.map +1 -1
  34. package/dist/initNodeJs.js +3 -0
  35. package/dist/initNodeJs.js.map +1 -1
  36. package/dist/io/bytes.d.ts +5 -3
  37. package/dist/io/bytes.d.ts.map +1 -1
  38. package/dist/io/bytes.js +15 -7
  39. package/dist/io/bytes.js.map +1 -1
  40. package/dist/io/writer.d.ts +4 -3
  41. package/dist/io/writer.d.ts.map +1 -1
  42. package/dist/io/writerbytes.d.ts +4 -3
  43. package/dist/io/writerbytes.d.ts.map +1 -1
  44. package/dist/io/writerbytes.js +7 -6
  45. package/dist/io/writerbytes.js.map +1 -1
  46. package/dist/io/writerlength.d.ts +4 -3
  47. package/dist/io/writerlength.d.ts.map +1 -1
  48. package/dist/io/writerlength.js +3 -3
  49. package/dist/io/writerlength.js.map +1 -1
  50. package/dist/mnemonic.d.ts +14 -0
  51. package/dist/mnemonic.d.ts.map +1 -0
  52. package/dist/mnemonic.js +123 -0
  53. package/dist/mnemonic.js.map +1 -0
  54. package/dist/pbkdf2.d.ts +11 -0
  55. package/dist/pbkdf2.d.ts.map +1 -0
  56. package/dist/pbkdf2.js +36 -0
  57. package/dist/pbkdf2.js.map +1 -0
  58. package/dist/script.d.ts +4 -0
  59. package/dist/script.d.ts.map +1 -1
  60. package/dist/script.js +6 -0
  61. package/dist/script.js.map +1 -1
  62. package/package.json +1 -1
  63. package/src/address/address.ts +346 -0
  64. package/src/address/legacyaddr.ts +129 -0
  65. package/src/consts.ts +8 -0
  66. package/src/ecc.ts +61 -0
  67. package/src/ffi/ecash_lib_wasm_bg_browser.wasm +0 -0
  68. package/src/ffi/ecash_lib_wasm_bg_browser.wasm.d.ts +32 -0
  69. package/src/ffi/ecash_lib_wasm_bg_nodejs.wasm +0 -0
  70. package/src/ffi/ecash_lib_wasm_bg_nodejs.wasm.d.ts +32 -0
  71. package/src/ffi/ecash_lib_wasm_browser.d.ts +183 -0
  72. package/src/ffi/ecash_lib_wasm_browser.js +571 -0
  73. package/src/ffi/ecash_lib_wasm_nodejs.d.ts +127 -0
  74. package/src/ffi/ecash_lib_wasm_nodejs.js +498 -0
  75. package/src/hash.ts +46 -0
  76. package/src/hdwallet.ts +181 -0
  77. package/src/hmac.ts +74 -0
  78. package/src/index.ts +29 -0
  79. package/src/indexBrowser.ts +6 -0
  80. package/src/indexNodeJs.ts +6 -0
  81. package/src/initBrowser.ts +21 -0
  82. package/src/initNodeJs.ts +20 -0
  83. package/src/io/bytes.ts +80 -0
  84. package/src/io/hex.ts +69 -0
  85. package/src/io/int.ts +6 -0
  86. package/src/io/str.ts +16 -0
  87. package/src/io/varsize.ts +49 -0
  88. package/src/io/writer.ts +20 -0
  89. package/src/io/writerbytes.ts +87 -0
  90. package/src/io/writerlength.ts +44 -0
  91. package/src/mnemonic.ts +153 -0
  92. package/src/op.ts +162 -0
  93. package/src/opcode.ts +154 -0
  94. package/src/pbkdf2.ts +52 -0
  95. package/src/script.ts +195 -0
  96. package/src/sigHashType.ts +190 -0
  97. package/src/test/testRunner.ts +209 -0
  98. package/src/token/alp.ts +146 -0
  99. package/src/token/common.ts +32 -0
  100. package/src/token/empp.ts +29 -0
  101. package/src/token/slp.ts +212 -0
  102. package/src/tx.ts +180 -0
  103. package/src/txBuilder.ts +262 -0
  104. package/src/unsignedTx.ts +359 -0
  105. package/tsconfig.json +2 -1
  106. package/wordlists/english.json +2053 -0
  107. package/.nyc_output/0fc40ca6-d52c-45eb-b31b-2601ce70b887.json +0 -1
  108. package/.nyc_output/ac5be6db-4e40-41f8-8b84-7598d4747e57.json +0 -1
  109. package/.nyc_output/b316d46f-5ea0-4e98-884a-bfbf9cc1d0f8.json +0 -1
  110. package/.nyc_output/f965566b-9422-4874-b45e-9eefda9c769c.json +0 -1
  111. package/.nyc_output/processinfo/0fc40ca6-d52c-45eb-b31b-2601ce70b887.json +0 -1
  112. package/.nyc_output/processinfo/ac5be6db-4e40-41f8-8b84-7598d4747e57.json +0 -1
  113. package/.nyc_output/processinfo/b316d46f-5ea0-4e98-884a-bfbf9cc1d0f8.json +0 -1
  114. package/.nyc_output/processinfo/f965566b-9422-4874-b45e-9eefda9c769c.json +0 -1
  115. package/.nyc_output/processinfo/index.json +0 -1
  116. package/dist/address/cashaddr.d.ts +0 -78
  117. package/dist/address/cashaddr.d.ts.map +0 -1
  118. package/dist/address/cashaddr.js +0 -543
  119. package/dist/address/cashaddr.js.map +0 -1
  120. package/dist/cashaddr/cashaddr.d.ts +0 -23
  121. package/dist/cashaddr/cashaddr.d.ts.map +0 -1
  122. package/dist/cashaddr/cashaddr.js +0 -325
  123. package/dist/cashaddr/cashaddr.js.map +0 -1
  124. package/global.d.ts +0 -64
  125. package/test.log +0 -82
@@ -0,0 +1,146 @@
1
+ // Copyright (c) 2024 The Bitcoin developers
2
+ // Distributed under the MIT software license, see the accompanying
3
+ // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
+
5
+ import { fromHex, fromHexRev } from '../io/hex.js';
6
+ import { strToBytes } from '../io/str.js';
7
+ import { Writer } from '../io/writer.js';
8
+ import { WriterBytes } from '../io/writerbytes.js';
9
+ import { WriterLength } from '../io/writerlength.js';
10
+ import { Amount, BURN, GENESIS, GenesisInfo, MINT, SEND } from './common.js';
11
+
12
+ /** LOKAD ID for ALP */
13
+ export const ALP_LOKAD_ID = strToBytes('SLP2');
14
+
15
+ /** ALP standard token type number */
16
+ export const ALP_STANDARD = 0;
17
+
18
+ /** Mint data specifying mint amounts and batons of a GENESIS/MINT tx */
19
+ export interface MintData {
20
+ /**
21
+ * List of amounts to be minted by this tx, each having their own tx output.
22
+ */
23
+ amounts: Amount[];
24
+ /** Number of mint batons to create, each having their own tx output. */
25
+ numBatons: number;
26
+ }
27
+
28
+ /** Build an ALP GENESIS pushdata section, creating a new ALP token */
29
+ export function alpGenesis(
30
+ tokenType: number,
31
+ genesisInfo: GenesisInfo,
32
+ mintData: MintData,
33
+ ): Uint8Array {
34
+ const writeSection = (writer: Writer) => {
35
+ writer.putBytes(ALP_LOKAD_ID);
36
+ writer.putU8(tokenType);
37
+ putVarBytes(GENESIS, writer);
38
+ putVarBytes(strToBytes(genesisInfo.tokenTicker ?? ''), writer);
39
+ putVarBytes(strToBytes(genesisInfo.tokenName ?? ''), writer);
40
+ putVarBytes(strToBytes(genesisInfo.url ?? ''), writer);
41
+ putVarBytes(fromHex(genesisInfo.data ?? ''), writer);
42
+ putVarBytes(fromHex(genesisInfo.authPubkey ?? ''), writer);
43
+ writer.putU8(genesisInfo.decimals ?? 0);
44
+ putMintData(mintData, writer);
45
+ };
46
+ const writerLength = new WriterLength();
47
+ writeSection(writerLength);
48
+ const writerBytes = new WriterBytes(writerLength.length);
49
+ writeSection(writerBytes);
50
+ return writerBytes.data;
51
+ }
52
+
53
+ /**
54
+ * Build an ALP MINT pushdata section, creating new ALP tokens and mint batons
55
+ * of the given token ID.
56
+ **/
57
+ export function alpMint(
58
+ tokenId: string,
59
+ tokenType: number,
60
+ mintData: MintData,
61
+ ): Uint8Array {
62
+ const tokenIdBytes = fromHexRev(tokenId);
63
+ const writeSection = (writer: Writer) => {
64
+ writer.putBytes(ALP_LOKAD_ID);
65
+ writer.putU8(tokenType);
66
+ putVarBytes(MINT, writer);
67
+ writer.putBytes(tokenIdBytes);
68
+ putMintData(mintData, writer);
69
+ };
70
+ const writerLength = new WriterLength();
71
+ writeSection(writerLength);
72
+ const writerBytes = new WriterBytes(writerLength.length);
73
+ writeSection(writerBytes);
74
+ return writerBytes.data;
75
+ }
76
+
77
+ /**
78
+ * Build an ALP SEND pushdata section, moving ALP tokens to different outputs
79
+ **/
80
+ export function alpSend(
81
+ tokenId: string,
82
+ tokenType: number,
83
+ sendAmounts: Amount[],
84
+ ): Uint8Array {
85
+ const tokenIdBytes = fromHexRev(tokenId);
86
+ const writeSection = (writer: Writer) => {
87
+ writer.putBytes(ALP_LOKAD_ID);
88
+ writer.putU8(tokenType);
89
+ writer.putU8(SEND.length);
90
+ writer.putBytes(SEND);
91
+ writer.putBytes(tokenIdBytes);
92
+ writer.putU8(sendAmounts.length);
93
+ for (const amount of sendAmounts) {
94
+ putAlpAmount(amount, writer);
95
+ }
96
+ };
97
+ const writerLength = new WriterLength();
98
+ writeSection(writerLength);
99
+ const writerBytes = new WriterBytes(writerLength.length);
100
+ writeSection(writerBytes);
101
+ return writerBytes.data;
102
+ }
103
+
104
+ /** Build an ALP BURN pushdata section, intentionally burning ALP tokens. */
105
+ export function alpBurn(
106
+ tokenId: string,
107
+ tokenType: number,
108
+ burnAmount: Amount,
109
+ ): Uint8Array {
110
+ const tokenIdBytes = fromHexRev(tokenId);
111
+ const writeSection = (writer: Writer) => {
112
+ writer.putBytes(ALP_LOKAD_ID);
113
+ writer.putU8(tokenType);
114
+ writer.putU8(BURN.length);
115
+ writer.putBytes(BURN);
116
+ writer.putBytes(tokenIdBytes);
117
+ putAlpAmount(burnAmount, writer);
118
+ };
119
+ const writerLength = new WriterLength();
120
+ writeSection(writerLength);
121
+ const writerBytes = new WriterBytes(writerLength.length);
122
+ writeSection(writerBytes);
123
+ return writerBytes.data;
124
+ }
125
+
126
+ function putMintData(mintData: MintData, writer: Writer) {
127
+ writer.putU8(mintData.amounts.length);
128
+ for (const amount of mintData.amounts) {
129
+ putAlpAmount(amount, writer);
130
+ }
131
+ writer.putU8(mintData.numBatons);
132
+ }
133
+
134
+ function putAlpAmount(amount: Amount, writer: Writer) {
135
+ const amountN = BigInt(amount);
136
+ writer.putU32(amountN & 0xffffffffn);
137
+ writer.putU16(amountN >> 32n);
138
+ }
139
+
140
+ function putVarBytes(bytes: Uint8Array, writer: Writer) {
141
+ if (bytes.length > 127) {
142
+ throw new Error('Length of bytes must be between 0 and 127');
143
+ }
144
+ writer.putU8(bytes.length);
145
+ writer.putBytes(bytes);
146
+ }
@@ -0,0 +1,32 @@
1
+ // Copyright (c) 2024 The Bitcoin developers
2
+ // Distributed under the MIT software license, see the accompanying
3
+ // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
+
5
+ import { strToBytes } from '../io/str.js';
6
+
7
+ export type Amount = bigint | number;
8
+
9
+ export const GENESIS = strToBytes('GENESIS');
10
+ export const MINT = strToBytes('MINT');
11
+ export const SEND = strToBytes('SEND');
12
+ export const BURN = strToBytes('BURN');
13
+
14
+ /** Genesis info found in GENESIS txs of tokens */
15
+ export interface GenesisInfo {
16
+ /** token_ticker of the token */
17
+ tokenTicker?: string;
18
+ /** token_name of the token */
19
+ tokenName?: string;
20
+ /** URL of the token */
21
+ url?: string;
22
+ /** token_document_hash of the token (only on SLP) */
23
+ hash?: string;
24
+ /** mint_vault_scripthash (only on SLP V2 Mint Vault) */
25
+ mintVaultScripthash?: string;
26
+ /** Arbitray payload data of the token (only on ALP) */
27
+ data?: string;
28
+ /** auth_pubkey of the token (only on ALP) */
29
+ authPubkey?: string;
30
+ /** decimals of the token, i.e. how many decimal places the token should be displayed with. */
31
+ decimals?: number;
32
+ }
@@ -0,0 +1,29 @@
1
+ // Copyright (c) 2024 The Bitcoin developers
2
+ // Distributed under the MIT software license, see the accompanying
3
+ // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
+
5
+ import { Op, pushBytesOp } from '../op.js';
6
+ import { OP_PUSHDATA1, OP_RESERVED, OP_RETURN } from '../opcode.js';
7
+ import { Script } from '../script.js';
8
+
9
+ /** Build an eMPP OP_RETURN script with the given pushdata */
10
+ export function emppScript(pushdata: Uint8Array[]): Script {
11
+ if (pushdata.find(pushdata => pushdata.length == 0) !== undefined) {
12
+ throw new Error('Pushdata cannot be empty');
13
+ }
14
+ return Script.fromOps([
15
+ OP_RETURN,
16
+ OP_RESERVED,
17
+ ...pushdata.map(pushdataOpEmpp),
18
+ ]);
19
+ }
20
+
21
+ function pushdataOpEmpp(pushdata: Uint8Array): Op {
22
+ if (pushdata.length < OP_PUSHDATA1) {
23
+ return {
24
+ opcode: pushdata.length,
25
+ data: pushdata,
26
+ };
27
+ }
28
+ return pushBytesOp(pushdata);
29
+ }
@@ -0,0 +1,212 @@
1
+ // Copyright (c) 2024 The Bitcoin developers
2
+ // Distributed under the MIT software license, see the accompanying
3
+ // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
+
5
+ import { fromHex } from '../io/hex.js';
6
+ import { strToBytes } from '../io/str.js';
7
+ import { Op, pushBytesOp } from '../op.js';
8
+ import { OP_PUSHDATA1, OP_RETURN } from '../opcode.js';
9
+ import { Script } from '../script.js';
10
+ import { Amount, BURN, GENESIS, GenesisInfo, MINT, SEND } from './common.js';
11
+
12
+ /** LOKAD ID for SLP */
13
+ export const SLP_LOKAD_ID = strToBytes('SLP\0');
14
+
15
+ /** SLP fungible token type number */
16
+ export const SLP_FUNGIBLE = 1;
17
+ /** SLP MINT Vault token type number */
18
+ export const SLP_MINT_VAULT = 2;
19
+ /** SLP NFT1 Child token type number */
20
+ export const SLP_NFT1_CHILD = 0x41;
21
+ /** SLP NFT1 Group token type number */
22
+ export const SLP_NFT1_GROUP = 0x81;
23
+
24
+ /** Build an SLP GENESIS OP_RETURN, creating a new SLP token */
25
+ export function slpGenesis(
26
+ tokenType: number,
27
+ genesisInfo: GenesisInfo,
28
+ initialQuantity: Amount,
29
+ mintBatonOutIdx?: number,
30
+ ): Script {
31
+ verifyTokenType(tokenType);
32
+ const data: Uint8Array[] = [];
33
+ data.push(SLP_LOKAD_ID);
34
+ data.push(new Uint8Array([tokenType]));
35
+ data.push(GENESIS);
36
+ data.push(strToBytes(genesisInfo.tokenTicker ?? ''));
37
+ data.push(strToBytes(genesisInfo.tokenName ?? ''));
38
+ data.push(strToBytes(genesisInfo.url ?? ''));
39
+ data.push(genesisInfo.hash ? fromHex(genesisInfo.hash) : new Uint8Array());
40
+ data.push(new Uint8Array([genesisInfo.decimals ?? 0]));
41
+ if (tokenType == SLP_MINT_VAULT) {
42
+ if (genesisInfo.mintVaultScripthash === undefined) {
43
+ throw new Error('Must set mintVaultScripthash for MINT VAULT');
44
+ }
45
+ data.push(fromHex(genesisInfo.mintVaultScripthash));
46
+ } else {
47
+ if (mintBatonOutIdx !== undefined) {
48
+ if (mintBatonOutIdx < 2) {
49
+ throw new Error('mintBatonOutIdx must be >= 2');
50
+ }
51
+ data.push(new Uint8Array([mintBatonOutIdx]));
52
+ } else {
53
+ data.push(new Uint8Array());
54
+ }
55
+ }
56
+ data.push(slpAmount(initialQuantity));
57
+ return Script.fromOps([OP_RETURN as Op].concat(data.map(pushdataOpSlp)));
58
+ }
59
+
60
+ /**
61
+ * Build an SLP MINT pushdata section, creating new SLP tokens and mint batons
62
+ * of the given token ID.
63
+ **/
64
+ export function slpMint(
65
+ tokenId: string,
66
+ tokenType: number,
67
+ additionalQuantity: Amount,
68
+ mintBatonOutIdx?: number,
69
+ ): Script {
70
+ verifyTokenType(tokenType);
71
+ verifyTokenId(tokenId);
72
+ return Script.fromOps([
73
+ OP_RETURN,
74
+ pushdataOpSlp(SLP_LOKAD_ID),
75
+ pushdataOpSlp(new Uint8Array([tokenType])),
76
+ pushdataOpSlp(MINT),
77
+ pushdataOpSlp(fromHex(tokenId)),
78
+ pushdataOpSlp(
79
+ new Uint8Array(
80
+ mintBatonOutIdx !== undefined ? [mintBatonOutIdx] : [],
81
+ ),
82
+ ),
83
+ pushdataOpSlp(slpAmount(additionalQuantity)),
84
+ ]);
85
+ }
86
+
87
+ /**
88
+ * Build an SLP MINT VAULT pushdata section, creating new SLP tokens and mint batons
89
+ * of the given token ID.
90
+ **/
91
+ export function slpMintVault(
92
+ tokenId: string,
93
+ additionalQuantities: Amount[],
94
+ ): Script {
95
+ verifyTokenId(tokenId);
96
+ verifySendAmounts(additionalQuantities);
97
+ return Script.fromOps(
98
+ [
99
+ OP_RETURN,
100
+ pushdataOpSlp(SLP_LOKAD_ID),
101
+ pushdataOpSlp(new Uint8Array([SLP_MINT_VAULT])),
102
+ pushdataOpSlp(MINT),
103
+ pushdataOpSlp(fromHex(tokenId)),
104
+ ].concat(
105
+ additionalQuantities.map(qty => pushdataOpSlp(slpAmount(qty))),
106
+ ),
107
+ );
108
+ }
109
+
110
+ /**
111
+ * Build an SLP SEND pushdata section, moving SLP tokens to different outputs
112
+ **/
113
+ export function slpSend(
114
+ tokenId: string,
115
+ tokenType: number,
116
+ sendAmounts: Amount[],
117
+ ): Script {
118
+ verifyTokenType(tokenType);
119
+ verifyTokenId(tokenId);
120
+ verifySendAmounts(sendAmounts);
121
+ return Script.fromOps(
122
+ [
123
+ OP_RETURN,
124
+ pushdataOpSlp(SLP_LOKAD_ID),
125
+ pushdataOpSlp(new Uint8Array([tokenType])),
126
+ pushdataOpSlp(SEND),
127
+ pushdataOpSlp(fromHex(tokenId)),
128
+ ].concat(sendAmounts.map(qty => pushdataOpSlp(slpAmount(qty)))),
129
+ );
130
+ }
131
+
132
+ /**
133
+ * Build an SLP BURN pushdata section, intentionally burning SLP tokens.
134
+ * See https://github.com/badger-cash/slp-self-mint-protocol/blob/master/token-type1-burn.md
135
+ **/
136
+ export function slpBurn(
137
+ tokenId: string,
138
+ tokenType: number,
139
+ burnAmount: Amount,
140
+ ): Script {
141
+ verifyTokenType(tokenType);
142
+ verifyTokenId(tokenId);
143
+ return Script.fromOps([
144
+ OP_RETURN,
145
+ pushdataOpSlp(SLP_LOKAD_ID),
146
+ pushdataOpSlp(new Uint8Array([tokenType])),
147
+ pushdataOpSlp(BURN),
148
+ pushdataOpSlp(fromHex(tokenId)),
149
+ pushdataOpSlp(slpAmount(burnAmount)),
150
+ ]);
151
+ }
152
+
153
+ function verifyTokenType(tokenType: number) {
154
+ switch (tokenType) {
155
+ case SLP_FUNGIBLE:
156
+ case SLP_MINT_VAULT:
157
+ case SLP_NFT1_GROUP:
158
+ case SLP_NFT1_CHILD:
159
+ return;
160
+ default:
161
+ throw new Error(`Unknown token type ${tokenType}`);
162
+ }
163
+ }
164
+
165
+ function verifyTokenId(tokenId: string) {
166
+ if (tokenId.length != 64) {
167
+ throw new Error(
168
+ `Token ID must be 64 hex characters in length, but got ${tokenId.length}`,
169
+ );
170
+ }
171
+ }
172
+
173
+ function verifySendAmounts(sendAmounts: Amount[]) {
174
+ if (sendAmounts.length == 0) {
175
+ throw new Error('Send amount cannot be empty');
176
+ }
177
+ if (sendAmounts.length > 19) {
178
+ throw new Error(
179
+ `Cannot use more than 19 amounts, but got ${sendAmounts.length}`,
180
+ );
181
+ }
182
+ }
183
+
184
+ function pushdataOpSlp(pushdata: Uint8Array): Op {
185
+ if (pushdata.length == 0) {
186
+ return {
187
+ opcode: OP_PUSHDATA1,
188
+ data: pushdata,
189
+ };
190
+ }
191
+ if (pushdata.length < OP_PUSHDATA1) {
192
+ return {
193
+ opcode: pushdata.length,
194
+ data: pushdata,
195
+ };
196
+ }
197
+ return pushBytesOp(pushdata);
198
+ }
199
+
200
+ export function slpAmount(amount: Amount): Uint8Array {
201
+ if (amount < 0 || BigInt(amount) > 0xffffffffffffffffn) {
202
+ throw new Error(`Amount out of range: ${amount}`);
203
+ }
204
+ const amountBytes = new Uint8Array(8);
205
+ const view = new DataView(
206
+ amountBytes.buffer,
207
+ amountBytes.byteOffset,
208
+ amountBytes.byteLength,
209
+ );
210
+ view.setBigUint64(0, BigInt(amount), /*little endian=*/ false);
211
+ return amountBytes;
212
+ }
package/src/tx.ts ADDED
@@ -0,0 +1,180 @@
1
+ // Copyright (c) 2024 The Bitcoin developers
2
+ // Distributed under the MIT software license, see the accompanying
3
+ // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
+
5
+ import { Bytes } from './io/bytes.js';
6
+ import { fromHexRev } from './io/hex.js';
7
+ import { writeVarSize } from './io/varsize.js';
8
+ import { Writer } from './io/writer.js';
9
+ import { WriterBytes } from './io/writerbytes.js';
10
+ import { WriterLength } from './io/writerlength.js';
11
+ import { Script } from './script.js';
12
+
13
+ /**
14
+ * Default value for nSequence of inputs if left undefined; this opts out of
15
+ * BIP68 relative lock-time, and if all inputs have this value, nLockTime is
16
+ * disabled, too.
17
+ *
18
+ * This is chosen as the default as it's the default in the node too,
19
+ * see CTxIn in /src/primitives/transaction.h.
20
+ **/
21
+ export const DEFAULT_SEQUENCE = 0xffffffff;
22
+
23
+ /** Current tx version, see CTransaction in /stc/primitives/transaction.h */
24
+ export const DEFAULT_TX_VERSION = 2;
25
+
26
+ /** COutPoint, pointing to a coin being spent. */
27
+ export interface OutPoint {
28
+ /**
29
+ * TxId of the output of the coin, either a big-endian hex encoded TxId
30
+ * or a little-endian bytearray.
31
+ **/
32
+ txid: string | Uint8Array;
33
+ /** Index in the outputs of the tx of the coin. */
34
+ outIdx: number;
35
+ }
36
+
37
+ /** CTxIn, spending an unspent output. */
38
+ export interface TxInput {
39
+ /** Points to an output being spent. */
40
+ prevOut: OutPoint;
41
+ /** scriptSig unlocking the output, defaults to the empty Script. */
42
+ script?: Script;
43
+ /** nSequence, defaults to 0xffffffff if unspecified. */
44
+ sequence?: number;
45
+ /** Sign data required to sign an input */
46
+ signData?: SignData;
47
+ }
48
+
49
+ /** CTxOut, creating a new output. */
50
+ export interface TxOutput {
51
+ /** Value in satoshis of the output (1 XEC = 100 satoshis) */
52
+ value: number | bigint;
53
+ /** Script locking the output */
54
+ script: Script;
55
+ }
56
+
57
+ /** All the data required to sign an input (using BIP143). */
58
+ export interface SignData {
59
+ /** Value of the output being spent */
60
+ value: number | bigint;
61
+ /** Script of the output being spent (not for P2SH) */
62
+ outputScript?: Script;
63
+ /**
64
+ * For P2SH inputs, the preimage of the script hash locking the output being
65
+ * spent.
66
+ **/
67
+ redeemScript?: Script;
68
+ }
69
+
70
+ /** CTransaction, a Bitcoin transaction. */
71
+ export class Tx {
72
+ /** nVersion of the tx */
73
+ public version: number;
74
+ /** vin, tx inputs spending outputs of other txs */
75
+ public inputs: TxInput[];
76
+ /** vout, tx outputs created in this tx */
77
+ public outputs: TxOutput[];
78
+ /** nLockTime of the tx, specifies when the tx can be mined earliest */
79
+ public locktime: number;
80
+
81
+ public constructor(params?: {
82
+ version?: number;
83
+ inputs?: TxInput[];
84
+ outputs?: TxOutput[];
85
+ locktime?: number;
86
+ }) {
87
+ this.version = params?.version ?? DEFAULT_TX_VERSION;
88
+ this.inputs = params?.inputs ?? [];
89
+ this.outputs = params?.outputs ?? [];
90
+ this.locktime = params?.locktime ?? 0;
91
+ }
92
+
93
+ /** Serialize the tx to a byte array */
94
+ public ser(): Uint8Array {
95
+ const writerBytes = new WriterBytes(this.serSize());
96
+ this.write(writerBytes);
97
+ return writerBytes.data;
98
+ }
99
+
100
+ /** Calculate the serialized size of the tx */
101
+ public serSize(): number {
102
+ const writerLength = new WriterLength();
103
+ this.write(writerLength);
104
+ return writerLength.length;
105
+ }
106
+
107
+ /** Write the tx to the given writer */
108
+ public write(writer: Writer) {
109
+ writer.putU32(this.version);
110
+ writeVarSize(this.inputs.length, writer);
111
+ for (const input of this.inputs) {
112
+ writeTxInput(input, writer);
113
+ }
114
+ writeVarSize(this.outputs.length, writer);
115
+ for (const output of this.outputs) {
116
+ writeTxOutput(output, writer);
117
+ }
118
+ writer.putU32(this.locktime);
119
+ }
120
+ }
121
+
122
+ export function readTxOutput(bytes: Bytes): TxOutput {
123
+ const value = bytes.readU64();
124
+ const script = Script.readWithSize(bytes);
125
+ return {
126
+ value,
127
+ script,
128
+ };
129
+ }
130
+
131
+ /** Write an outpoint to a Writer */
132
+ export function writeOutPoint(outpoint: OutPoint, writer: Writer): void {
133
+ const txid =
134
+ typeof outpoint.txid === 'string'
135
+ ? fromHexRev(outpoint.txid)
136
+ : outpoint.txid;
137
+ writer.putBytes(txid);
138
+ writer.putU32(outpoint.outIdx);
139
+ }
140
+
141
+ /** Write a TxInput to a Writer */
142
+ export function writeTxInput(input: TxInput, writer: Writer): void {
143
+ writeOutPoint(input.prevOut, writer);
144
+ (input.script ?? new Script()).writeWithSize(writer);
145
+ writer.putU32(input.sequence ?? DEFAULT_SEQUENCE);
146
+ }
147
+
148
+ /** Write a TxOutput to a Writer */
149
+ export function writeTxOutput(output: TxOutput, writer: Writer): void {
150
+ writer.putU64(output.value);
151
+ output.script.writeWithSize(writer);
152
+ }
153
+
154
+ /** Create a deep copy of the TxInput */
155
+ export function copyTxInput(input: TxInput): TxInput {
156
+ return {
157
+ prevOut: {
158
+ txid:
159
+ typeof input.prevOut.txid === 'string'
160
+ ? input.prevOut.txid
161
+ : new Uint8Array(input.prevOut.txid),
162
+ outIdx: input.prevOut.outIdx,
163
+ },
164
+ script: input.script?.copy(),
165
+ sequence: input.sequence,
166
+ signData: input.signData && {
167
+ value: input.signData.value,
168
+ outputScript: input.signData.outputScript?.copy(),
169
+ redeemScript: input.signData.redeemScript?.copy(),
170
+ },
171
+ };
172
+ }
173
+
174
+ /** Create a deep copy of the TxOutput */
175
+ export function copyTxOutput(output: TxOutput): TxOutput {
176
+ return {
177
+ value: output.value,
178
+ script: output.script.copy(),
179
+ };
180
+ }