11import { Hash } from 'fast-sha256' ;
2+ import { Psbt } from '@bitgo/utxo-lib' ;
23
34export const BIP322_TAG = 'BIP0322-signed-message' ;
45
@@ -22,3 +23,42 @@ export function hashMessageWithTag(message: string | Buffer, tag = BIP322_TAG):
2223 const messageHash = messageHasher . digest ( ) ;
2324 return Buffer . from ( messageHash ) ;
2425}
26+
27+ /**
28+ * Build a BIP322 "to spend" transaction
29+ * Source: https://github.com/bitcoin/bips/blob/master/bip-0322.mediawiki#full
30+ *
31+ * @param {Buffer } scriptPubKey - The scriptPubKey to use for the output
32+ * @param {string | Buffer } message - The message to include in the transaction
33+ * @param {Buffer } [tag=BIP322_TAG] - The tag to use for hashing, defaults to BIP322_TAG.
34+ * @returns {string } - The hex representation of the constructed transaction
35+ */
36+ export function buildToSpendTransaction ( scriptPubKey : Buffer , message : string | Buffer , tag = BIP322_TAG ) : string {
37+ // Create PSBT object for constructing the transaction
38+ const psbt = new Psbt ( ) ;
39+ // Set default value for nVersion and nLockTime
40+ psbt . setVersion ( 0 ) ; // nVersion = 0
41+ psbt . setLocktime ( 0 ) ; // nLockTime = 0
42+ // Compute the message hash - SHA256(SHA256(tag) || SHA256(tag) || message)
43+ const messageHash = hashMessageWithTag ( message , tag ) ;
44+ // Construct the scriptSig - OP_0 PUSH32[ message_hash ]
45+ const scriptSigPartOne = new Uint8Array ( [ 0x00 , 0x20 ] ) ; // OP_0 PUSH32
46+ const scriptSig = new Uint8Array ( scriptSigPartOne . length + messageHash . length ) ;
47+ scriptSig . set ( scriptSigPartOne ) ;
48+ scriptSig . set ( messageHash , scriptSigPartOne . length ) ;
49+ // Set the input
50+ psbt . addInput ( {
51+ hash : '0' . repeat ( 64 ) , // vin[0].prevout.hash = 0000...000
52+ index : 0xffffffff , // vin[0].prevout.n = 0xFFFFFFFF
53+ sequence : 0 , // vin[0].nSequence = 0
54+ finalScriptSig : Buffer . from ( scriptSig ) , // vin[0].scriptSig = OP_0 PUSH32[ message_hash ]
55+ witnessScript : Buffer . from ( [ ] ) , // vin[0].scriptWitness = []
56+ } ) ;
57+ // Set the output
58+ psbt . addOutput ( {
59+ value : BigInt ( 0 ) , // vout[0].nValue = 0
60+ script : scriptPubKey , // vout[0].scriptPubKey = message_challenge
61+ } ) ;
62+ // Return transaction
63+ return psbt . extractTransaction ( ) . toHex ( ) ;
64+ }
0 commit comments