@@ -11,7 +11,8 @@ import SendTransaction from '../../../../src/new/sendTransaction';
1111import transactionUtils from '../../../../src/utils/transaction' ;
1212import { TransactionTemplateBuilder } from '../../../../src/template/transaction/builder' ;
1313import { WalletTxTemplateInterpreter } from '../../../../src/template/transaction/interpreter' ;
14- import { TOKEN_AUTHORITY_MASK , TOKEN_MELT_MASK , TOKEN_MINT_MASK } from '../../../../src/constants' ;
14+ import { NATIVE_TOKEN_UID , TOKEN_AUTHORITY_MASK , TOKEN_MELT_MASK , TOKEN_MINT_MASK } from '../../../../src/constants' ;
15+ import { TokenVersion } from '../../../../src/types' ;
1516
1617const DEBUG = true ;
1718
@@ -438,3 +439,178 @@ describe('Template execution', () => {
438439 ) ;
439440 } ) ;
440441} ) ;
442+
443+ describe ( 'Template execution with fee tokens' , ( ) => {
444+ let hWallet : HathorWallet ;
445+ let interpreter : WalletTxTemplateInterpreter ;
446+ let tokenUid : string ;
447+
448+ beforeAll ( async ( ) => {
449+ hWallet = await generateWalletHelper ( null ) ;
450+ interpreter = new WalletTxTemplateInterpreter ( hWallet ) ;
451+ const address = await hWallet . getAddressAtIndex ( 0 ) ;
452+ await GenesisWalletHelper . injectFunds ( hWallet , address , 10n , { } ) ;
453+ } ) ;
454+
455+ afterAll ( async ( ) => {
456+ await hWallet . stop ( ) ;
457+ await stopAllWallets ( ) ;
458+ await GenesisWalletHelper . clearListeners ( ) ;
459+ } ) ;
460+
461+ it ( 'should be not able to create a custom fee token without providing a fee header' , async ( ) => {
462+ const template = new TransactionTemplateBuilder ( )
463+ . addConfigAction ( {
464+ createToken : true ,
465+ tokenName : 'Tmpl Test Fee Token 01' ,
466+ tokenSymbol : 'TTT01' ,
467+ tokenVersion : TokenVersion . FEE ,
468+ } )
469+ . addSetVarAction ( { name : 'addr' , call : { method : 'get_wallet_address' } } )
470+ . addUtxoSelect ( { fill : 5 } )
471+ . addTokenOutput ( { address : '{addr}' , amount : 100 , useCreatedToken : true } )
472+ . addTokenOutput ( { address : '{addr}' , amount : 100 , useCreatedToken : true } )
473+ . addTokenOutput ( { address : '{addr}' , amount : 100 , useCreatedToken : true } )
474+ . addTokenOutput ( { address : '{addr}' , amount : 100 , useCreatedToken : true } )
475+ . build ( ) ;
476+
477+ const tx = await interpreter . build ( template , DEBUG ) ;
478+
479+ expect ( tx . outputs ) . toHaveLength ( 5 ) ;
480+
481+ // HTR change
482+ expect ( tx . outputs [ 0 ] . tokenData ) . toBe ( 0 ) ;
483+ expect ( tx . outputs [ 0 ] . value ) . toBe ( 5n ) ;
484+
485+ // Created token
486+ expect ( tx . outputs [ 1 ] . tokenData ) . toBe ( 1 ) ;
487+ expect ( tx . outputs [ 1 ] . value ) . toBe ( 100n ) ;
488+
489+ expect ( tx . outputs [ 2 ] . tokenData ) . toBe ( 1 ) ;
490+ expect ( tx . outputs [ 2 ] . value ) . toBe ( 100n ) ;
491+
492+ expect ( tx . outputs [ 3 ] . tokenData ) . toBe ( 1 ) ;
493+ expect ( tx . outputs [ 3 ] . value ) . toBe ( 100n ) ;
494+
495+ expect ( tx . outputs [ 4 ] . tokenData ) . toBe ( 1 ) ;
496+ expect ( tx . outputs [ 4 ] . value ) . toBe ( 100n ) ;
497+
498+ // Not have a fee header
499+ expect ( tx . headers ) . toHaveLength ( 0 ) ;
500+
501+ await transactionUtils . signTransaction ( tx , hWallet . storage , DEFAULT_PIN_CODE ) ;
502+ tx . prepareToSend ( ) ;
503+ const sendTx = new SendTransaction ( { storage : hWallet . storage , transaction : tx } ) ;
504+
505+ // Expecting the transaction to fail validation with negative HTR balance
506+ await expect ( sendTx . runFromMining ( ) ) . rejects . toThrow (
507+ 'full validation failed: HTR balance is different than expected. (amount=-5, expected=0)'
508+ ) ;
509+
510+ // Check the wallet balance to verify nothing was spent from the wallet
511+ const htrBalance = await hWallet . getBalance ( NATIVE_TOKEN_UID ) ;
512+ expect ( htrBalance [ 0 ] ) . toHaveProperty ( 'balance.unlocked' , 10n ) ;
513+ } ) ;
514+
515+ it ( 'should be able to create a custom fee token providing a fee header' , async ( ) => {
516+ const mintAmount = 8582n ;
517+ const template = new TransactionTemplateBuilder ( )
518+ . addConfigAction ( {
519+ createToken : true ,
520+ tokenName : 'Tmpl Test Fee Token 01' ,
521+ tokenSymbol : 'TTT01' ,
522+ tokenVersion : TokenVersion . FEE ,
523+ } )
524+ . addSetVarAction ( { name : 'addr' , call : { method : 'get_wallet_address' } } )
525+ . addUtxoSelect ( { fill : 1 } )
526+ . addTokenOutput ( { address : '{addr}' , amount : mintAmount , useCreatedToken : true } )
527+ . addFee ( { amount : 1 } )
528+ . build ( ) ;
529+
530+ const tx = await interpreter . build ( template , DEBUG ) ;
531+
532+ expect ( tx . outputs ) . toHaveLength ( 2 ) ;
533+
534+ // HTR change
535+ expect ( tx . outputs [ 0 ] . tokenData ) . toBe ( 0 ) ;
536+ expect ( tx . outputs [ 0 ] . value ) . toBe ( 9n ) ;
537+
538+ // Created token
539+ expect ( tx . outputs [ 1 ] . tokenData ) . toBe ( 1 ) ;
540+ expect ( tx . outputs [ 1 ] . value ) . toBe ( mintAmount ) ;
541+
542+ // Have a fee header
543+ expect ( tx . headers ) . toHaveLength ( 1 ) ;
544+ expect ( tx . getFeeHeader ( ) ) . not . toBeNull ( ) ;
545+ expect ( tx . getFeeHeader ( ) ! . entries ) . toHaveLength ( 1 ) ;
546+ expect ( tx . getFeeHeader ( ) ! . entries [ 0 ] . tokenIndex ) . toBe ( 0 ) ;
547+ expect ( tx . getFeeHeader ( ) ! . entries [ 0 ] . amount ) . toBe ( 1n ) ;
548+
549+ // after validate send the tx to the mining service
550+ await transactionUtils . signTransaction ( tx , hWallet . storage , DEFAULT_PIN_CODE ) ;
551+ tx . prepareToSend ( ) ;
552+ const sendTx = new SendTransaction ( { storage : hWallet . storage , transaction : tx } ) ;
553+ await sendTx . runFromMining ( ) ;
554+ expect ( tx . hash ) . not . toBeNull ( ) ;
555+ if ( tx . hash === null ) {
556+ throw new Error ( 'Transaction does not have a hash' ) ;
557+ }
558+ tokenUid = tx . hash ;
559+ await waitForTxReceived ( hWallet , tx . hash , undefined ) ;
560+
561+ const htrBalance = await hWallet . getBalance ( NATIVE_TOKEN_UID ) ;
562+ expect ( htrBalance [ 0 ] ) . toHaveProperty ( 'balance.unlocked' , 9n ) ;
563+ } ) ;
564+ it ( 'should be able to complete the fees of a transaction' , async ( ) => {
565+ const template = new TransactionTemplateBuilder ( )
566+ . addConfigAction ( {
567+ createToken : true ,
568+ tokenName : 'Tmpl Test Fee Token 01' ,
569+ tokenSymbol : 'TTT01' ,
570+ tokenVersion : TokenVersion . FEE ,
571+ } )
572+ . addSetVarAction ( { name : 'addr' , call : { method : 'get_wallet_address' } } )
573+ . addUtxoSelect ( { fill : 5 } )
574+ . addTokenOutput ( { address : '{addr}' , amount : 9999999 , useCreatedToken : true } )
575+ . addTokenOutput ( { address : '{addr}' , amount : 9999999 , useCreatedToken : true } )
576+ . addTokenOutput ( { address : '{addr}' , amount : 9999999 , useCreatedToken : true } )
577+ . addTokenOutput ( { address : '{addr}' , amount : 9999999 , useCreatedToken : true } )
578+ . addCompleteAction ( { calculateFee : true , token : '00' } )
579+ . build ( ) ;
580+
581+ const tx = await interpreter . build ( template , DEBUG ) ;
582+
583+ // check the outputs
584+ expect ( tx . outputs ) . toHaveLength ( 6 ) ;
585+
586+ // HTR change
587+ expect ( tx . outputs [ 0 ] . tokenData ) . toBe ( 0 ) ;
588+ expect ( tx . outputs [ 0 ] . value ) . toBe ( 4n ) ;
589+
590+ // Created token
591+ expect ( tx . outputs [ 1 ] . tokenData ) . toBe ( 1 ) ;
592+ expect ( tx . outputs [ 1 ] . value ) . toBe ( 9999999n ) ;
593+
594+ // Have a fee header
595+ expect ( tx . headers ) . toHaveLength ( 1 ) ;
596+ expect ( tx . getFeeHeader ( ) ) . not . toBeNull ( ) ;
597+ expect ( tx . getFeeHeader ( ) ! . entries ) . toHaveLength ( 1 ) ;
598+ expect ( tx . getFeeHeader ( ) ! . entries [ 0 ] . tokenIndex ) . toBe ( 0 ) ;
599+ expect ( tx . getFeeHeader ( ) ! . entries [ 0 ] . amount ) . toBe ( 4n ) ;
600+
601+ // after validate send the tx to the mining service
602+ await transactionUtils . signTransaction ( tx , hWallet . storage , DEFAULT_PIN_CODE ) ;
603+ tx . prepareToSend ( ) ;
604+ const sendTx = new SendTransaction ( { storage : hWallet . storage , transaction : tx } ) ;
605+ await sendTx . runFromMining ( ) ;
606+ expect ( tx . hash ) . not . toBeNull ( ) ;
607+ if ( tx . hash === null ) {
608+ throw new Error ( 'Transaction does not have a hash' ) ;
609+ }
610+
611+ await waitForTxReceived ( hWallet , tx . hash , undefined ) ;
612+
613+ const htrBalance = await hWallet . getBalance ( NATIVE_TOKEN_UID ) ;
614+ expect ( htrBalance [ 0 ] ) . toHaveProperty ( 'balance.unlocked' , 5n ) ;
615+ } ) ;
616+ } ) ;
0 commit comments