1
- import * as bitcoin from "bitcoinjs-lib" ;
1
+ import * as bitcoin from 'bitcoinjs-lib' ;
2
+ import { ECPairInterface , ECPairFactory } from 'ecpair' ;
3
+ import * as tinysecp from '@bitcoin-js/tiny-secp256k1-asmjs' ;
2
4
5
+ // Initialize ECPair with tinysecp256k1-asmjs
6
+ const ECPair = ECPairFactory ( tinysecp ) ;
7
+
8
+ // Define interfaces
3
9
interface UTXO {
4
10
txid : string ;
5
- index : number ;
6
- amount : number ;
7
- txout : bitcoin . TxOutput ;
8
- address : bitcoin . Address ;
11
+ vout : number ;
12
+ value : number ;
13
+ scriptPubKey : Buffer ;
14
+ address : string ;
9
15
}
10
16
11
17
interface Wallet {
12
18
utxos : UTXO [ ] ;
13
- privateKeys : Map < string , bitcoin . ECPairInterface > ;
19
+ privateKeys : Map < string , ECPairInterface > ;
14
20
}
15
21
22
+ // Create a new wallet
16
23
function createWallet ( ) : Wallet {
17
24
return {
18
25
utxos : [ ] ,
19
26
privateKeys : new Map ( ) ,
20
27
} ;
21
28
}
22
29
23
- function addUTXO ( wallet : Wallet , txid : string , index : number , txout : bitcoin . TxOutput , address : bitcoin . Address ) {
24
- wallet . utxos . push ( { txid, index, txout, address } ) ;
25
- }
26
-
27
- function removeUTXO ( wallet : Wallet , txid : string ) {
28
- wallet . utxos = wallet . utxos . filter ( utxo => utxo . txid !== txid ) ;
30
+ // Add a UTXO to the wallet
31
+ function addUTXO (
32
+ wallet : Wallet ,
33
+ txid : string ,
34
+ vout : number ,
35
+ value : number ,
36
+ scriptPubKey : Buffer ,
37
+ address : string
38
+ ) {
39
+ wallet . utxos . push ( { txid, vout, value, scriptPubKey, address } ) ;
29
40
}
30
41
42
+ // Select UTXOs sufficient to cover the amount
31
43
function selectUTXOs ( wallet : Wallet , amount : number ) : UTXO [ ] {
32
- let selectedUTXOs : UTXO [ ] = [ ] ;
33
- let totalAmount = 0 ;
44
+ const selectedUTXOs : UTXO [ ] = [ ] ;
45
+ let totalValue = 0 ;
34
46
35
47
for ( const utxo of wallet . utxos ) {
36
- totalAmount += utxo . txout . value ;
37
-
48
+ totalValue += utxo . value ;
38
49
selectedUTXOs . push ( utxo ) ;
39
50
40
- if ( totalAmount >= amount ) {
51
+ if ( totalValue >= amount ) {
41
52
break ;
42
53
}
43
54
}
44
55
56
+ if ( totalValue < amount ) {
57
+ throw new Error ( 'Insufficient funds' ) ;
58
+ }
59
+
45
60
return selectedUTXOs ;
46
61
}
47
62
48
- function createTransaction ( selectedUTXOs : UTXO [ ] , outputs : { address : bitcoin . Address ; amount : number } [ ] ) : bitcoin . Transaction {
49
- const txb = new bitcoin . TransactionBuilder ( ) ;
63
+ // Create a transaction
64
+ function createTransaction (
65
+ selectedUTXOs : UTXO [ ] ,
66
+ outputs : { address : string ; value : number } [ ] ,
67
+ network : bitcoin . Network = bitcoin . networks . bitcoin
68
+ ) : bitcoin . Psbt {
69
+ const psbt = new bitcoin . Psbt ( { network } ) ;
70
+
71
+ // Add inputs
50
72
for ( const utxo of selectedUTXOs ) {
51
- txb . addInput ( utxo . txid , utxo . index ) ;
73
+ psbt . addInput ( {
74
+ hash : utxo . txid ,
75
+ index : utxo . vout ,
76
+ nonWitnessUtxo : Buffer . from ( utxo . scriptPubKey ) , // Simplified for example
77
+ } ) ;
52
78
}
53
79
54
- for ( const { address, amount } of outputs ) {
55
- txb . addOutput ( address , amount ) ;
80
+ // Add outputs
81
+ for ( const output of outputs ) {
82
+ psbt . addOutput ( {
83
+ address : output . address ,
84
+ value : output . value ,
85
+ } ) ;
56
86
}
57
87
58
- return txb . buildIncomplete ( ) ;
88
+ return psbt ;
59
89
}
60
90
61
- function signTransaction ( wallet : Wallet , transaction : bitcoin . Transaction , selectedUTXOs : UTXO [ ] ) {
62
- for ( let i = 0 ; i < selectedUTXOs . length ; i ++ ) {
63
- const utxo = selectedUTXOs [ i ] ;
64
- const key = wallet . privateKeys . get ( utxo . address . toString ( ) ) ;
65
- if ( key ) {
66
- transaction . sign ( i , key ) ;
91
+ // Sign the transaction
92
+ function signTransaction (
93
+ wallet : Wallet ,
94
+ psbt : bitcoin . Psbt ,
95
+ selectedUTXOs : UTXO [ ]
96
+ ) {
97
+ selectedUTXOs . forEach ( ( utxo , index ) => {
98
+ const keyPair = wallet . privateKeys . get ( utxo . address ) ;
99
+ if ( ! keyPair ) {
100
+ throw new Error ( `No private key found for address: ${ utxo . address } ` ) ;
67
101
}
68
- }
102
+
103
+ psbt . signInput ( index , {
104
+ publicKey : Buffer . from ( keyPair . publicKey ) ,
105
+ sign : ( hash : Buffer ) => Buffer . from ( keyPair . sign ( hash ) )
106
+ } ) ;
107
+ } ) ;
108
+ psbt . finalizeAllInputs ( ) ;
109
+ return psbt . extractTransaction ( ) ;
69
110
}
70
111
71
112
// Example usage
72
113
const wallet : Wallet = createWallet ( ) ;
73
114
74
- // Add UTXOs to the wallet
75
- // addUTXO(wallet, ...);
76
-
77
- // Specify the amount and recipient address
78
- const amount = 10000000 ; // satoshis
79
- const recipientAddress = "recipient_address" ; // should be a valid bitcoin.Address
80
-
81
- // Select UTXOs for the transaction
82
- const selectedUTXOs = selectUTXOs ( wallet , amount ) ;
83
-
84
- // Create transaction outputs
85
- const outputs = [ { address : recipientAddress , amount } ] ;
86
-
87
- // Create the transaction
88
- const transaction = createTransaction ( selectedUTXOs , outputs ) ;
89
-
90
- // Sign the transaction
91
- signTransaction ( wallet , transaction , selectedUTXOs ) ;
92
-
93
- // Broadcast the transaction
94
- // broadcastTransaction(transaction);
115
+ // Generate a key pair for testing
116
+ const keyPair = ECPair . makeRandom ( ) ;
117
+ const { address } = bitcoin . payments . p2pkh ( { pubkey : Buffer . from ( keyPair . publicKey ) } ) ;
118
+ // Add a test private key
119
+ wallet . privateKeys . set ( address ! , keyPair ) ;
120
+
121
+ // Add a test UTXO
122
+ addUTXO (
123
+ wallet ,
124
+ '0000000000000000000000000000000000000000000000000000000000000000' ,
125
+ 0 ,
126
+ 15000000 , // 0.15 BTC in satoshis
127
+ Buffer . from ( '76a914' + '0' . repeat ( 40 ) + '88ac' , 'hex' ) , // Dummy scriptPubKey
128
+ address !
129
+ ) ;
130
+
131
+ const amount = 10000000 ; // 0.1 BTC in satoshis
132
+ const recipientAddress = '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa' ; // Example recipient (Satoshi's address)
133
+
134
+ try {
135
+ // Select UTXOs
136
+ const selectedUTXOs = selectUTXOs ( wallet , amount ) ;
137
+
138
+ // Create transaction outputs
139
+ const outputs = [ { address : recipientAddress , value : amount } ] ;
140
+
141
+ // Create and sign transaction
142
+ const psbt = createTransaction ( selectedUTXOs , outputs ) ;
143
+ const signedTx = signTransaction ( wallet , psbt , selectedUTXOs ) ;
144
+
145
+ // Get the transaction hex
146
+ const txHex = signedTx . toHex ( ) ;
147
+ console . log ( 'Transaction hex:' , txHex ) ;
148
+
149
+ // Note: To broadcast, you'd need to use a Bitcoin node API or service
150
+ // await broadcastTransaction(txHex);
151
+ } catch ( error ) {
152
+ console . error ( 'Transaction failed:' , error ) ;
153
+ }
0 commit comments