@@ -29,6 +29,9 @@ import { WalletStore } from '../src/lib/WalletStore';
2929// BitcoinJS is defined as a global variable in BitcoinJS.min.js loaded by demos/index.html
3030declare global {
3131 const BitcoinJS : typeof import ( 'bitcoinjs-lib' ) ;
32+ interface Window {
33+ loadAlbatross ( ) : Promise < typeof import ( '@nimiq/core' ) > ;
34+ }
3235}
3336
3437class Demo {
@@ -199,6 +202,41 @@ class Demo {
199202 }
200203 } ) ;
201204
205+ document . querySelector ( 'button#sign-multi-transaction' ) ! . addEventListener ( 'click' , async ( ) => {
206+ const txRequest = generateSignMultiTransactionRequest ( ) ;
207+ try {
208+ const result = await demo . client . signTransaction ( txRequest , demo . _defaultBehavior ) ;
209+ if ( demo . isRedirectResult ( result ) ) return ;
210+ console . log ( 'Result' , result ) ;
211+ if ( Array . isArray ( result ) ) {
212+ document . querySelector ( '#result' ) ! . textContent = `${ result . length } transactions signed` ;
213+ } else {
214+ document . querySelector ( '#result' ) ! . textContent = 'TX signed' ;
215+ }
216+ } catch ( e ) {
217+ console . error ( e ) ;
218+ document . querySelector ( '#result' ) ! . textContent = `Error: ${ e . message || e } ` ;
219+ }
220+ } ) ;
221+
222+ document . querySelector ( 'button#sign-staking-multi-transaction' ) ! . addEventListener ( 'click' , async ( ) => {
223+ const txRequest = await generateStakingMultiTransactionRequest ( ) ;
224+ try {
225+ const result = await demo . client . signTransaction ( txRequest , demo . _defaultBehavior ) ;
226+ if ( demo . isRedirectResult ( result ) ) return ;
227+ console . log ( 'Result' , result ) ;
228+ if ( Array . isArray ( result ) ) {
229+ document . querySelector ( '#result' ) ! . textContent =
230+ `${ result . length } staking transactions signed (deactivate, retire, unstake)` ;
231+ } else {
232+ document . querySelector ( '#result' ) ! . textContent = 'Staking TX signed' ;
233+ }
234+ } catch ( e ) {
235+ console . error ( e ) ;
236+ document . querySelector ( '#result' ) ! . textContent = `Error: ${ e . message || e } ` ;
237+ }
238+ } ) ;
239+
202240 document . querySelector ( 'button#onboard' ) ! . addEventListener ( 'click' , async ( ) => {
203241 try {
204242 const result = await demo . client . onboard ( { appName : 'Hub Demos' } , demo . _defaultBehavior ) ;
@@ -259,6 +297,118 @@ class Demo {
259297 } ;
260298 }
261299
300+ function generateSignMultiTransactionRequest ( ) : SignTransactionRequest {
301+ const $radio = document . querySelector ( 'input[name="address"]:checked' ) ;
302+ if ( ! $radio ) {
303+ alert ( 'You have no account to send a tx from, create an account first (signup)' ) ;
304+ throw new Error ( 'No account found' ) ;
305+ }
306+ const sender = ( $radio as HTMLElement ) . dataset . address ! ;
307+ const baseFee = parseInt ( ( document . querySelector ( '#fee' ) as HTMLInputElement ) . value , 10 ) || 100 ;
308+ const validityStartHeight = parseInt ( ( document . querySelector ( '#validitystartheight' ) as HTMLInputElement ) . value || '1234' , 10 ) ;
309+
310+ // Sample recipients for multi-transaction demo
311+ const recipients = [
312+ 'NQ63 U7XG 1YYE D6FA SXGG 3F5H X403 NBKN JLDU' ,
313+ 'NQ46 2RM7 QE4T 82KR 61Q9 9B7E R38G LBVM N6KY' ,
314+ 'NQ70 APBA 9GCC FL44 D82R UJCD DS4B Y824 3LYJ' ,
315+ ] ;
316+
317+ return {
318+ appName : 'Hub Demos' ,
319+ sender,
320+ recipientLabel : 'Alice' ,
321+ transactions : recipients . map ( ( recipient , index ) => ( {
322+ recipient,
323+ value : 100000000 + ( index * 10000000 ) , // 1 NIM + increments
324+ fee : baseFee + ( index * 10 ) ,
325+ validityStartHeight : validityStartHeight + index ,
326+ extraData : Utf8Tools . stringToUtf8ByteArray ( `Multi-tx demo ${ index + 1 } ` ) ,
327+ } ) ) ,
328+ } ;
329+ }
330+
331+ async function generateStakingMultiTransactionRequest ( ) : Promise < SignTransactionRequest > {
332+ const $radio = document . querySelector ( 'input[name="address"]:checked' ) ;
333+ if ( ! $radio ) {
334+ alert ( 'You have no account to send a tx from, create an account first (signup)' ) ;
335+ throw new Error ( 'No account found' ) ;
336+ }
337+ const sender = ( $radio as HTMLElement ) . dataset . address ! ;
338+ const validityStartHeight = parseInt ( ( document . querySelector ( '#validitystartheight' ) as HTMLInputElement ) . value || '1234' , 10 ) ;
339+
340+ // Load Nimiq library
341+ const Nimiq = await window . loadAlbatross ( ) ;
342+
343+ // Demo recipients for simulated multi-transaction staking flow
344+ // Using the same recipients as in the regular multi-transaction demo (known valid addresses)
345+ const recipients = [
346+ 'NQ63 U7XG 1YYE D6FA SXGG 3F5H X403 NBKN JLDU' ,
347+ 'NQ46 2RM7 QE4T 82KR 61Q9 9B7E R38G LBVM N6KY' ,
348+ 'NQ70 APBA 9GCC FL44 D82R UJCD DS4B Y824 3LYJ' ,
349+ ] ;
350+
351+ // Parse sender address
352+ const senderAddress = Nimiq . Address . fromString ( sender ) ;
353+
354+ // Network ID for testnet (5 for test-albatross, 1 for mainnet)
355+ const networkId = 5 ;
356+
357+ // Create actual serialized transactions using Nimiq library
358+ const serializedTransactions = [
359+ // Transaction 1: Deactivate stake (demo)
360+ new Nimiq . Transaction (
361+ senderAddress , // sender
362+ Nimiq . AccountType . Basic , // senderType
363+ new Uint8Array ( 0 ) , // senderData
364+ Nimiq . Address . fromString ( recipients [ 0 ] ) , // recipient
365+ Nimiq . AccountType . Basic , // recipientType
366+ Utf8Tools . stringToUtf8ByteArray ( 'Deactivate stake (demo)' ) , // recipientData (extraData)
367+ 50000000n , // value (0.5 NIM)
368+ 100n , // fee
369+ Nimiq . TransactionFlag . None , // flag
370+ validityStartHeight , // validityStartHeight
371+ networkId , // networkId
372+ ) . serialize ( ) ,
373+ // Transaction 2: Retire stake (demo)
374+ new Nimiq . Transaction (
375+ senderAddress ,
376+ Nimiq . AccountType . Basic ,
377+ new Uint8Array ( 0 ) ,
378+ Nimiq . Address . fromString ( recipients [ 1 ] ) ,
379+ Nimiq . AccountType . Basic ,
380+ Utf8Tools . stringToUtf8ByteArray ( 'Retire stake (demo)' ) ,
381+ 50000000n , // 0.5 NIM
382+ 100n , // fee
383+ Nimiq . TransactionFlag . None ,
384+ validityStartHeight + 10 , // Slightly later
385+ networkId ,
386+ ) . serialize ( ) ,
387+ // Transaction 3: Unstake complete (demo)
388+ new Nimiq . Transaction (
389+ senderAddress ,
390+ Nimiq . AccountType . Basic ,
391+ new Uint8Array ( 0 ) ,
392+ Nimiq . Address . fromString ( recipients [ 2 ] ) ,
393+ Nimiq . AccountType . Basic ,
394+ Utf8Tools . stringToUtf8ByteArray ( 'Unstake complete (demo)' ) ,
395+ 50000000n , // 0.5 NIM
396+ 100n , // fee
397+ Nimiq . TransactionFlag . None ,
398+ validityStartHeight + 20 ,
399+ networkId ,
400+ ) . serialize ( ) ,
401+ ] ;
402+
403+ // Return request with serialized transactions
404+ return {
405+ appName : 'Hub Demos' ,
406+ sender,
407+ recipientLabel : 'Staking Demo Recipients' ,
408+ transactions : serializedTransactions ,
409+ } ;
410+ }
411+
262412 async function generateCheckoutRequest ( multiCheckout ?: boolean ) : Promise < CheckoutRequest > {
263413 const nimTxValue = parseInt ( ( document . querySelector ( '#value' ) as HTMLInputElement ) . value , 10 ) || 1337 ;
264414 const nimTxFee = parseInt ( ( document . querySelector ( '#fee' ) as HTMLInputElement ) . value , 10 ) || 0 ;
0 commit comments