1
+ import {
2
+ Transaction ,
3
+ script ,
4
+ Psbt ,
5
+ initEccLib ,
6
+ networks ,
7
+ Signer as BTCSigner ,
8
+ crypto ,
9
+ payments ,
10
+ opcodes ,
11
+ address as Address
12
+ } from "bitcoinjs-lib" ;
13
+
14
+ import { Taptree } from "bitcoinjs-lib/src/types" ;
15
+ import { ECPairFactory , ECPairAPI } from "ecpair" ;
16
+ import ecc from "@bitcoinerlab/secp256k1" ;
17
+ import axios , { AxiosResponse } from "axios" ;
18
+ import networkConfig from "config/network.config" ;
19
+ import { WIFWallet } from 'utils/WIFWallet'
20
+ import { SeedWallet } from "utils/SeedWallet" ;
21
+ import cbor from 'cbor'
22
+ //test
23
+ const network = networks . testnet ;
24
+ // const network = networks.bitcoin;
25
+
26
+ initEccLib ( ecc as any ) ;
27
+ const ECPair : ECPairAPI = ECPairFactory ( ecc ) ;
28
+
29
+ // const seed: string = process.env.MNEMONIC as string;
30
+ // const networkType: string = networkConfig.networkType;
31
+ // const wallet = new SeedWallet({ networkType: networkType, seed: seed });
32
+
33
+ const privateKey : string = process . env . PRIVATE_KEY as string ;
34
+ const networkType : string = networkConfig . networkType ;
35
+ const wallet = new WIFWallet ( { networkType : networkType , privateKey : privateKey } ) ;
36
+
37
+ const txhash : string = 'dd476bdd2039161c50196d9a8f8412d56be3710de8bda5de4674429e5f8e3649' ;
38
+ const txidBuffer = Buffer . from ( txhash , 'hex' ) ;
39
+ const delegateBuffer = txidBuffer . reverse ( ) ;
40
+
41
+ const receiveAddress : string = "tb1ppx220ln489s5wqu8mqgezm7twwpj0avcvle3vclpdkpqvdg3mwqsvydajn" ;
42
+ const transaction_fee = 28000 ;
43
+
44
+
45
+ export function createdelegateInscriptionTapScript ( ) : Array < Buffer > {
46
+
47
+ const keyPair = wallet . ecPair ;
48
+ const delegateOrdinalStacks : any = [
49
+ toXOnly ( keyPair . publicKey ) ,
50
+ opcodes . OP_CHECKSIG ,
51
+ opcodes . OP_FALSE ,
52
+ opcodes . OP_IF ,
53
+ Buffer . from ( "ord" , "utf8" ) ,
54
+ 1 ,
55
+ 11 ,
56
+ delegateBuffer ,
57
+ opcodes . OP_ENDIF ,
58
+ ] ;
59
+ return delegateOrdinalStacks ;
60
+ }
61
+
62
+ async function delegateInscribe ( ) {
63
+ const keyPair = wallet . ecPair ;
64
+ const delegateOrdinalStack = createdelegateInscriptionTapScript ( ) ;
65
+
66
+ const ordinal_script = script . compile ( delegateOrdinalStack ) ;
67
+
68
+ const scriptTree : Taptree = {
69
+ output : ordinal_script ,
70
+ } ;
71
+
72
+ const redeem = {
73
+ output : ordinal_script ,
74
+ redeemVersion : 192 ,
75
+ } ;
76
+
77
+ const ordinal_p2tr = payments . p2tr ( {
78
+ internalPubkey : toXOnly ( keyPair . publicKey ) ,
79
+ network,
80
+ scriptTree,
81
+ redeem,
82
+ } ) ;
83
+
84
+ const address = ordinal_p2tr . address ?? "" ;
85
+ console . log ( "send coin to address" , address ) ;
86
+
87
+ const utxos = await waitUntilUTXO ( address as string ) ;
88
+ console . log ( `Using UTXO ${ utxos [ 0 ] . txid } :${ utxos [ 0 ] . vout } ` ) ;
89
+
90
+ const psbt = new Psbt ( { network } ) ;
91
+
92
+ psbt . addInput ( {
93
+ hash : utxos [ 0 ] . txid ,
94
+ index : utxos [ 0 ] . vout ,
95
+ tapInternalKey : toXOnly ( keyPair . publicKey ) ,
96
+ witnessUtxo : { value : utxos [ 0 ] . value , script : ordinal_p2tr . output ! } ,
97
+ tapLeafScript : [
98
+ {
99
+ leafVersion : redeem . redeemVersion ,
100
+ script : redeem . output ,
101
+ controlBlock : ordinal_p2tr . witness ! [ ordinal_p2tr . witness ! . length - 1 ] ,
102
+ } ,
103
+ ] ,
104
+ } ) ;
105
+
106
+
107
+ const change = utxos [ 0 ] . value - 546 - transaction_fee ;
108
+
109
+ psbt . addOutput ( {
110
+ address : receiveAddress , //Destination Address
111
+ value : 546 ,
112
+ } ) ;
113
+
114
+ psbt . addOutput ( {
115
+ address : receiveAddress , // Change address
116
+ value : change ,
117
+ } ) ;
118
+
119
+ await signAndSend ( keyPair , psbt ) ;
120
+ }
121
+
122
+ delegateInscribe ( )
123
+
124
+ export async function signAndSend (
125
+ keypair : BTCSigner ,
126
+ psbt : Psbt ,
127
+ ) {
128
+ psbt . signInput ( 0 , keypair ) ;
129
+ psbt . finalizeAllInputs ( )
130
+ const tx = psbt . extractTransaction ( ) ;
131
+
132
+ console . log ( tx . virtualSize ( ) )
133
+ console . log ( tx . toHex ( ) )
134
+
135
+ const txid = await broadcast ( tx . toHex ( ) ) ;
136
+ console . log ( `Success! Txid is ${ txid } ` ) ;
137
+ }
138
+
139
+ export async function waitUntilUTXO ( address : string ) {
140
+ return new Promise < IUTXO [ ] > ( ( resolve , reject ) => {
141
+ let intervalId : any ;
142
+ const checkForUtxo = async ( ) => {
143
+ try {
144
+ const response : AxiosResponse < string > = await blockstream . get (
145
+ `/address/${ address } /utxo`
146
+ ) ;
147
+ const data : IUTXO [ ] = response . data
148
+ ? JSON . parse ( response . data )
149
+ : undefined ;
150
+ console . log ( data ) ;
151
+ if ( data . length > 0 ) {
152
+ resolve ( data ) ;
153
+ clearInterval ( intervalId ) ;
154
+ }
155
+ } catch ( error ) {
156
+ reject ( error ) ;
157
+ clearInterval ( intervalId ) ;
158
+ }
159
+ } ;
160
+ intervalId = setInterval ( checkForUtxo , 4000 ) ;
161
+ } ) ;
162
+ }
163
+ export async function getTx ( id : string ) : Promise < string > {
164
+ const response : AxiosResponse < string > = await blockstream . get (
165
+ `/tx/${ id } /hex`
166
+ ) ;
167
+ return response . data ;
168
+ }
169
+ const blockstream = new axios . Axios ( {
170
+ baseURL : `https://mempool.space/testnet/api` ,
171
+ // baseURL: `https://mempool.space/api`,
172
+ } ) ;
173
+ export async function broadcast ( txHex : string ) {
174
+ const response : AxiosResponse < string > = await blockstream . post ( "/tx" , txHex ) ;
175
+ return response . data ;
176
+ }
177
+ function tapTweakHash ( pubKey : Buffer , h : Buffer | undefined ) : Buffer {
178
+ return crypto . taggedHash (
179
+ "TapTweak" ,
180
+ Buffer . concat ( h ? [ pubKey , h ] : [ pubKey ] )
181
+ ) ;
182
+ }
183
+ function toXOnly ( pubkey : Buffer ) : Buffer {
184
+ return pubkey . subarray ( 1 , 33 ) ;
185
+ }
186
+ function tweakSigner ( signer : any , opts : any = { } ) {
187
+ let privateKey = signer . privateKey ;
188
+ if ( ! privateKey ) {
189
+ throw new Error ( 'Private key is required for tweaking signer!' ) ;
190
+ }
191
+ if ( signer . publicKey [ 0 ] === 3 ) {
192
+ privateKey = ecc . privateNegate ( privateKey ) ;
193
+ }
194
+ const tweakedPrivateKey = ecc . privateAdd ( privateKey , tapTweakHash ( toXOnly ( signer . publicKey ) , opts . tweakHash ) ) ;
195
+ if ( ! tweakedPrivateKey ) {
196
+ throw new Error ( 'Invalid tweaked private key!' ) ;
197
+ }
198
+ return ECPair . fromPrivateKey ( Buffer . from ( tweakedPrivateKey ) , {
199
+ network : opts . network ,
200
+ } ) ;
201
+ }
202
+ interface IUTXO {
203
+ txid : string ;
204
+ vout : number ;
205
+ status : {
206
+ confirmed : boolean ;
207
+ block_height : number ;
208
+ block_hash : string ;
209
+ block_time : number ;
210
+ } ;
211
+ value : number ;
212
+ }
0 commit comments