11import should from 'should' ;
22import { coins } from '@bitgo/statics' ;
33import { TransactionType } from '@bitgo/sdk-core' ;
4- import { TransactionBuilderFactory , StakeClauseTransaction } from '../../src/lib' ;
4+ import { TransactionBuilderFactory , StakeClauseTransaction , ValidatorRegistrationTransaction } from '../../src/lib' ;
55import * as testData from '../resources/vet' ;
66
77describe ( 'VET Staking Flow - End-to-End Test' , function ( ) {
@@ -10,7 +10,9 @@ describe('VET Staking Flow - End-to-End Test', function () {
1010
1111 // Test data
1212 const stakingContractAddress = testData . STAKING_CONTRACT_ADDRESS ;
13+ const builtInStakerContractAddress = testData . BUILT_IN_STAKER_CONTRACT_ADDRESS ;
1314 const amountToStake = '1000000000000000000' ; // 1 VET in wei
15+ const stakingPeriod = 60480 ;
1416 const levelId = testData . STAKING_LEVEL_ID ;
1517 const senderAddress = '0x9378c12BD7502A11F770a5C1F223c959B2805dA9' ;
1618 const feePayerAddress = '0xdc9fef0b84a0ccf3f1bd4b84e41743e3e051a083' ;
@@ -99,6 +101,80 @@ describe('VET Staking Flow - End-to-End Test', function () {
99101 jsonOutput . should . have . property ( 'levelId' , levelId ) ;
100102 } ) ;
101103
104+ it ( 'should build, sign, and serialize a complete validator registration transaction with fee delegation' , async function ( ) {
105+ // Step 1: Build the staking transaction
106+ const validatorRegistrationBuilder = factory . getValidatorRegistrationBuilder ( ) ;
107+
108+ validatorRegistrationBuilder
109+ . stakingContractAddress ( builtInStakerContractAddress )
110+ . stakingPeriod ( 60480 )
111+ . sender ( senderAddress )
112+ . chainTag ( 0x27 ) // Testnet chain tag
113+ . blockRef ( '0x014ead140e77bbc1' )
114+ . expiration ( 64 )
115+ . gas ( 100000 )
116+ . gasPriceCoef ( 128 )
117+ . nonce ( '12345' ) ;
118+
119+ validatorRegistrationBuilder . addFeePayerAddress ( feePayerAddress ) ;
120+
121+ const unsignedTx = await validatorRegistrationBuilder . build ( ) ;
122+ should . exist ( unsignedTx ) ;
123+ unsignedTx . should . be . instanceof ( ValidatorRegistrationTransaction ) ;
124+
125+ const validatorRegistrationTx = unsignedTx as ValidatorRegistrationTransaction ;
126+
127+ // Verify transaction structure
128+ validatorRegistrationTx . type . should . equal ( TransactionType . StakingLock ) ;
129+ validatorRegistrationTx . stakingContractAddress . should . equal ( builtInStakerContractAddress ) ;
130+ validatorRegistrationTx . stakingPeriod . should . equal ( stakingPeriod ) ;
131+
132+ should . exist ( validatorRegistrationTx . rawTransaction ) ;
133+ should . exist ( validatorRegistrationTx . rawTransaction . body ) ;
134+
135+ // This is the critical test - ensure reserved.features = 1
136+ should . exist ( validatorRegistrationTx . rawTransaction . body . reserved ) ;
137+ validatorRegistrationTx . rawTransaction . body . reserved ! . should . have . property ( 'features' , 1 ) ;
138+
139+ // Step 3: Add sender signature
140+ validatorRegistrationTx . addSenderSignature ( mockSenderSignature ) ;
141+ should . exist ( validatorRegistrationTx . senderSignature ) ;
142+ Buffer . from ( validatorRegistrationTx . senderSignature ! ) . should . eql ( mockSenderSignature ) ;
143+
144+ // Step 4: Add fee payer signature
145+ validatorRegistrationTx . addFeePayerSignature ( mockFeePayerSignature ) ;
146+ should . exist ( validatorRegistrationTx . feePayerSignature ) ;
147+ Buffer . from ( validatorRegistrationTx . feePayerSignature ! ) . should . eql ( mockFeePayerSignature ) ;
148+
149+ // Step 5: Generate transaction ID
150+
151+ // This should NOT throw "not signed transaction: id unavailable" error anymore
152+ const transactionId = validatorRegistrationTx . id ;
153+ should . exist ( transactionId ) ;
154+ transactionId . should . not . equal ( 'UNAVAILABLE' ) ;
155+
156+ // Step 6: Serialize the fully signed transaction
157+ const serializedTx = validatorRegistrationTx . toBroadcastFormat ( ) ;
158+ should . exist ( serializedTx ) ;
159+ serializedTx . should . be . type ( 'string' ) ;
160+ serializedTx . should . startWith ( '0x' ) ;
161+
162+ // Step 7: Verify transaction can be deserialized
163+ const deserializedBuilder = factory . from ( serializedTx ) ;
164+ const deserializedTx = deserializedBuilder . transaction as ValidatorRegistrationTransaction ;
165+
166+ deserializedTx . should . be . instanceof ( ValidatorRegistrationTransaction ) ;
167+ deserializedTx . stakingContractAddress . should . equal ( builtInStakerContractAddress ) ;
168+ deserializedTx . stakingPeriod . should . equal ( stakingPeriod ) ;
169+
170+ // Step 8: Verify toJson output
171+ const jsonOutput = validatorRegistrationTx . toJson ( ) ;
172+ should . exist ( jsonOutput ) ;
173+ jsonOutput . should . have . property ( 'id' , transactionId ) ;
174+ jsonOutput . should . have . property ( 'stakingContractAddress' , builtInStakerContractAddress ) ;
175+ jsonOutput . should . have . property ( 'stakingPeriod' , stakingPeriod ) ;
176+ } ) ;
177+
102178 it ( 'should handle signature combination in the correct order' , async function ( ) {
103179 // This test specifically validates the signature combination flow that was failing
104180 const stakingBuilder = factory . getStakingActivateBuilder ( ) ;
0 commit comments