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