@@ -3,7 +3,7 @@ import should from 'should';
33import { assert as SinonAssert , spy } from 'sinon' ;
44import { MoveStakeBuilder } from '../../../src/lib/moveStakeBuilder' ;
55import utils from '../../../src/lib/utils' ;
6- import { accounts , mockTssSignature , genesisHash , chainName } from '../../resources' ;
6+ import { accounts , mockTssSignature , genesisHash , chainName , rawTx } from '../../resources' ;
77import { buildTestConfig } from './base' ;
88import { testnetMaterial } from '../../../src/resources' ;
99import { InvalidTransactionError } from '@bitgo/sdk-core' ;
@@ -498,4 +498,146 @@ describe('Tao Move Stake Builder', function () {
498498 explanation . outputAmount . should . equal ( '0' ) ;
499499 } ) ;
500500 } ) ;
501+
502+ describe ( 'fromImplementation stages validation' , function ( ) {
503+ it ( 'should call super.fromImplementation before validation to populate _method' , async function ( ) {
504+ const config = buildTestConfig ( ) ;
505+ const material = utils . getMaterial ( config . network . type ) ;
506+ const validBuilder = new MoveStakeBuilder ( config ) . material ( material ) ;
507+ validBuilder
508+ . amount ( '1000000000000' )
509+ . originHotkey ( { address : '5FCPTnjevGqAuTttetBy4a24Ej3pH9fiQ8fmvP1ZkrVsLUoT' } )
510+ . destinationHotkey ( { address : '5Ffp1wJCPu4hzVDTo7XaMLqZSvSadyUQmxWPDw74CBjECSoq' } )
511+ . originNetuid ( '1' )
512+ . destinationNetuid ( '2' )
513+ . sender ( { address : sender . address } )
514+ . validity ( { firstValid : 3933 , maxDuration : 64 } )
515+ . referenceBlock ( referenceBlock )
516+ . sequenceId ( { name : 'Nonce' , keyword : 'nonce' , value : 200 } )
517+ . fee ( { amount : 0 , type : 'tip' } ) ;
518+
519+ const validTx = await validBuilder . build ( ) ;
520+ const rawTxHex = validTx . toBroadcastFormat ( ) ;
521+
522+ const newBuilder = new MoveStakeBuilder ( config ) . material ( material ) ;
523+
524+ should . doesNotThrow ( ( ) => {
525+ newBuilder . from ( rawTxHex ) ;
526+ } ) ;
527+
528+ const builderMethod = ( newBuilder as any ) . _method ;
529+ builderMethod . should . not . be . undefined ( ) ;
530+ builderMethod . name . should . equal ( 'moveStake' ) ;
531+ builderMethod . args . should . have . properties ( [
532+ 'originHotkey' ,
533+ 'destinationHotkey' ,
534+ 'originNetuid' ,
535+ 'destinationNetuid' ,
536+ 'alphaAmount' ,
537+ ] ) ;
538+ builderMethod . args . alphaAmount . should . equal ( '1000000000000' ) ;
539+ builderMethod . args . originHotkey . should . equal ( '5FCPTnjevGqAuTttetBy4a24Ej3pH9fiQ8fmvP1ZkrVsLUoT' ) ;
540+ builderMethod . args . destinationHotkey . should . equal ( '5Ffp1wJCPu4hzVDTo7XaMLqZSvSadyUQmxWPDw74CBjECSoq' ) ;
541+ builderMethod . args . originNetuid . should . equal ( '1' ) ;
542+ builderMethod . args . destinationNetuid . should . equal ( '2' ) ;
543+ } ) ;
544+
545+ it ( 'should throw error if _method is not populated before validation' , function ( ) {
546+ const config = buildTestConfig ( ) ;
547+ const material = utils . getMaterial ( config . network . type ) ;
548+ const mockBuilder = new TestMoveStakeBuilder ( config ) . material ( material ) ;
549+
550+ assert . throws (
551+ ( ) => {
552+ if ( mockBuilder [ '_method' ] ?. name !== 'moveStake' ) {
553+ throw new InvalidTransactionError (
554+ `Invalid Transaction Type: ${ mockBuilder [ '_method' ] ?. name } . Expected moveStake`
555+ ) ;
556+ }
557+ } ,
558+ ( e : Error ) => e . message . includes ( 'Invalid Transaction Type: undefined. Expected moveStake' )
559+ ) ;
560+ } ) ;
561+
562+ it ( 'should properly validate transaction type after super.fromImplementation' , function ( ) {
563+ const config = buildTestConfig ( ) ;
564+ const material = utils . getMaterial ( config . network . type ) ;
565+ const mockBuilder = new TestMoveStakeBuilder ( config ) . material ( material ) ;
566+
567+ mockBuilder . setMethodForTesting ( {
568+ name : 'transferKeepAlive' ,
569+ args : { dest : { id : 'test' } , value : '1000' } ,
570+ pallet : 'balances' ,
571+ } ) ;
572+
573+ assert . throws (
574+ ( ) => {
575+ if ( mockBuilder [ '_method' ] ?. name !== 'moveStake' ) {
576+ throw new InvalidTransactionError (
577+ `Invalid Transaction Type: ${ mockBuilder [ '_method' ] ?. name } . Expected moveStake`
578+ ) ;
579+ }
580+ } ,
581+ ( e : Error ) => e . message . includes ( 'Invalid Transaction Type: transferKeepAlive. Expected moveStake' )
582+ ) ;
583+ } ) ;
584+
585+ it ( 'should successfully parse and validate correct moveStake transaction' , function ( ) {
586+ const config = buildTestConfig ( ) ;
587+ const material = utils . getMaterial ( config . network . type ) ;
588+ const mockBuilder = new TestMoveStakeBuilder ( config ) . material ( material ) ;
589+
590+ mockBuilder . setMethodForTesting ( {
591+ name : 'moveStake' ,
592+ args : {
593+ originHotkey : '5FCPTnjevGqAuTttetBy4a24Ej3pH9fiQ8fmvP1ZkrVsLUoT' ,
594+ destinationHotkey : '5Ffp1wJCPu4hzVDTo7XaMLqZSvSadyUQmxWPDw74CBjECSoq' ,
595+ originNetuid : '1' ,
596+ destinationNetuid : '2' ,
597+ alphaAmount : '1000000000000' ,
598+ } ,
599+ pallet : 'subtensorModule' ,
600+ } ) ;
601+
602+ should . doesNotThrow ( ( ) => {
603+ if ( mockBuilder [ '_method' ] ?. name !== 'moveStake' ) {
604+ throw new InvalidTransactionError (
605+ `Invalid Transaction Type: ${ mockBuilder [ '_method' ] ?. name } . Expected moveStake`
606+ ) ;
607+ }
608+ } ) ;
609+ } ) ;
610+
611+ it ( 'should fail validation when parsing wrong transaction type (transferStake instead of moveStake)' , function ( ) {
612+ const config = buildTestConfig ( ) ;
613+ const material = utils . getMaterial ( config . network . type ) ;
614+ const moveStakeBuilder = new MoveStakeBuilder ( config ) . material ( material ) ;
615+
616+ assert . throws (
617+ ( ) => {
618+ moveStakeBuilder . from ( rawTx . transferStake . signed ) ;
619+ } ,
620+ ( e : Error ) => e . message . includes ( 'Invalid Transaction Type: transferStake. Expected moveStake' )
621+ ) ;
622+ } ) ;
623+
624+ it ( 'should verify _method is properly populated after super.fromImplementation with wrong transaction type' , function ( ) {
625+ const config = buildTestConfig ( ) ;
626+ const material = utils . getMaterial ( config . network . type ) ;
627+
628+ const testBuilder = new TestMoveStakeBuilder ( config ) . material ( material ) ;
629+
630+ try {
631+ testBuilder . from ( rawTx . transferStake . signed ) ;
632+ } catch ( error ) {
633+ const method = ( testBuilder as any ) . _method ;
634+ method . should . not . be . undefined ( ) ;
635+ method . name . should . equal ( 'transferStake' ) ; // This proves super.fromImplementation was called
636+ method . should . have . property ( 'args' ) ;
637+ method . should . have . property ( 'pallet' ) ;
638+
639+ ( error as Error ) . message . should . containEql ( 'Invalid Transaction Type: transferStake. Expected moveStake' ) ;
640+ }
641+ } ) ;
642+ } ) ;
501643} ) ;
0 commit comments