otx-btc-wallet-core 0.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.
- package/README.md +312 -0
- package/dist/index.d.mts +320 -0
- package/dist/index.d.ts +320 -0
- package/dist/index.js +342 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +331 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +41 -0
- package/src/createConfig.ts +80 -0
- package/src/index.ts +40 -0
- package/src/store/index.ts +296 -0
- package/src/types/account.ts +26 -0
- package/src/types/config.ts +28 -0
- package/src/types/connector.ts +109 -0
- package/src/types/index.ts +12 -0
- package/src/types/network.ts +4 -0
- package/src/types/psbt.ts +27 -0
- package/src/types/state.ts +35 -0
- package/src/utils/index.ts +1 -0
- package/src/utils/psbt.ts +185 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PSBT (Partially Signed Bitcoin Transaction) utilities
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Convert PSBT hex to base64
|
|
7
|
+
*/
|
|
8
|
+
export function psbtHexToBase64(hex: string): string {
|
|
9
|
+
// Remove any whitespace
|
|
10
|
+
const cleanHex = hex.replace(/\s/g, '');
|
|
11
|
+
|
|
12
|
+
// Validate hex string
|
|
13
|
+
if (!/^[0-9a-fA-F]*$/.test(cleanHex)) {
|
|
14
|
+
throw new Error('Invalid hex string');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (cleanHex.length % 2 !== 0) {
|
|
18
|
+
throw new Error('Hex string must have even length');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Convert hex to bytes then to base64
|
|
22
|
+
const bytes = new Uint8Array(cleanHex.length / 2);
|
|
23
|
+
for (let i = 0; i < cleanHex.length; i += 2) {
|
|
24
|
+
bytes[i / 2] = parseInt(cleanHex.substring(i, i + 2), 16);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Use Buffer in Node.js or btoa in browser
|
|
28
|
+
if (typeof Buffer !== 'undefined') {
|
|
29
|
+
return Buffer.from(bytes).toString('base64');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Browser fallback
|
|
33
|
+
let binary = '';
|
|
34
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
35
|
+
binary += String.fromCharCode(bytes[i]!);
|
|
36
|
+
}
|
|
37
|
+
return btoa(binary);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Convert PSBT base64 to hex
|
|
42
|
+
*/
|
|
43
|
+
export function psbtBase64ToHex(base64: string): string {
|
|
44
|
+
// Decode base64 to bytes
|
|
45
|
+
let bytes: Uint8Array;
|
|
46
|
+
|
|
47
|
+
if (typeof Buffer !== 'undefined') {
|
|
48
|
+
bytes = Buffer.from(base64, 'base64');
|
|
49
|
+
} else {
|
|
50
|
+
// Browser fallback
|
|
51
|
+
const binary = atob(base64);
|
|
52
|
+
bytes = new Uint8Array(binary.length);
|
|
53
|
+
for (let i = 0; i < binary.length; i++) {
|
|
54
|
+
bytes[i] = binary.charCodeAt(i);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Convert bytes to hex
|
|
59
|
+
return Array.from(bytes)
|
|
60
|
+
.map((b) => b.toString(16).padStart(2, '0'))
|
|
61
|
+
.join('');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Validate PSBT hex format
|
|
66
|
+
* PSBT magic bytes: 0x70736274ff (psbt\xff)
|
|
67
|
+
*/
|
|
68
|
+
export function isValidPsbtHex(hex: string): boolean {
|
|
69
|
+
const cleanHex = hex.replace(/\s/g, '').toLowerCase();
|
|
70
|
+
|
|
71
|
+
// Check minimum length (magic bytes = 5 bytes = 10 hex chars)
|
|
72
|
+
if (cleanHex.length < 10) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Check magic bytes
|
|
77
|
+
return cleanHex.startsWith('70736274ff');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Validate PSBT base64 format
|
|
82
|
+
*/
|
|
83
|
+
export function isValidPsbtBase64(base64: string): boolean {
|
|
84
|
+
try {
|
|
85
|
+
const hex = psbtBase64ToHex(base64);
|
|
86
|
+
return isValidPsbtHex(hex);
|
|
87
|
+
} catch {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Extract basic info from PSBT hex (without full parsing)
|
|
94
|
+
* This is a lightweight check - for full parsing use a library like bitcoinjs-lib
|
|
95
|
+
*/
|
|
96
|
+
export function getPsbtInfo(psbtHex: string): {
|
|
97
|
+
isValid: boolean;
|
|
98
|
+
version: number | null;
|
|
99
|
+
inputCount: number | null;
|
|
100
|
+
outputCount: number | null;
|
|
101
|
+
} {
|
|
102
|
+
if (!isValidPsbtHex(psbtHex)) {
|
|
103
|
+
return {
|
|
104
|
+
isValid: false,
|
|
105
|
+
version: null,
|
|
106
|
+
inputCount: null,
|
|
107
|
+
outputCount: null,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// PSBT structure is complex, return basic validation only
|
|
112
|
+
// For full parsing, use bitcoinjs-lib or similar
|
|
113
|
+
return {
|
|
114
|
+
isValid: true,
|
|
115
|
+
version: null, // Would need full parsing
|
|
116
|
+
inputCount: null, // Would need full parsing
|
|
117
|
+
outputCount: null, // Would need full parsing
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Combine multiple signed PSBTs into one
|
|
123
|
+
* Note: This is a basic implementation. For complex cases, use bitcoinjs-lib.
|
|
124
|
+
*
|
|
125
|
+
* @param psbts - Array of PSBT hex strings to combine
|
|
126
|
+
* @returns Combined PSBT hex
|
|
127
|
+
*/
|
|
128
|
+
export function combinePsbts(psbts: string[]): string {
|
|
129
|
+
if (psbts.length === 0) {
|
|
130
|
+
throw new Error('No PSBTs provided');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (psbts.length === 1) {
|
|
134
|
+
return psbts[0]!;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// For proper PSBT combination, you need bitcoinjs-lib
|
|
138
|
+
// This is just a placeholder that returns the first PSBT
|
|
139
|
+
// In production, use: bitcoin.Psbt.combine([psbt1, psbt2, ...])
|
|
140
|
+
console.warn(
|
|
141
|
+
'combinePsbts: For proper PSBT combination, use bitcoinjs-lib. ' +
|
|
142
|
+
'This function returns the first PSBT as a fallback.'
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
return psbts[0]!;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Sighash types for Bitcoin transactions
|
|
150
|
+
*/
|
|
151
|
+
export const SighashType = {
|
|
152
|
+
ALL: 0x01,
|
|
153
|
+
NONE: 0x02,
|
|
154
|
+
SINGLE: 0x03,
|
|
155
|
+
ANYONECANPAY: 0x80,
|
|
156
|
+
ALL_ANYONECANPAY: 0x81,
|
|
157
|
+
NONE_ANYONECANPAY: 0x82,
|
|
158
|
+
SINGLE_ANYONECANPAY: 0x83,
|
|
159
|
+
} as const;
|
|
160
|
+
|
|
161
|
+
export type SighashType = (typeof SighashType)[keyof typeof SighashType];
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Get human-readable name for sighash type
|
|
165
|
+
*/
|
|
166
|
+
export function getSighashTypeName(sighash: number): string {
|
|
167
|
+
switch (sighash) {
|
|
168
|
+
case SighashType.ALL:
|
|
169
|
+
return 'SIGHASH_ALL';
|
|
170
|
+
case SighashType.NONE:
|
|
171
|
+
return 'SIGHASH_NONE';
|
|
172
|
+
case SighashType.SINGLE:
|
|
173
|
+
return 'SIGHASH_SINGLE';
|
|
174
|
+
case SighashType.ANYONECANPAY:
|
|
175
|
+
return 'SIGHASH_ANYONECANPAY';
|
|
176
|
+
case SighashType.ALL_ANYONECANPAY:
|
|
177
|
+
return 'SIGHASH_ALL|ANYONECANPAY';
|
|
178
|
+
case SighashType.NONE_ANYONECANPAY:
|
|
179
|
+
return 'SIGHASH_NONE|ANYONECANPAY';
|
|
180
|
+
case SighashType.SINGLE_ANYONECANPAY:
|
|
181
|
+
return 'SIGHASH_SINGLE|ANYONECANPAY';
|
|
182
|
+
default:
|
|
183
|
+
return `UNKNOWN(${sighash})`;
|
|
184
|
+
}
|
|
185
|
+
}
|