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,10 @@ 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 ;
16+ const validatorAddress = '0x9a7afcacc88c106f3bbd6b213cd0821d9224d945' ;
1417 const levelId = testData . STAKING_LEVEL_ID ;
1518 const senderAddress = '0x9378c12BD7502A11F770a5C1F223c959B2805dA9' ;
1619 const feePayerAddress = '0xdc9fef0b84a0ccf3f1bd4b84e41743e3e051a083' ;
@@ -99,6 +102,84 @@ describe('VET Staking Flow - End-to-End Test', function () {
99102 jsonOutput . should . have . property ( 'levelId' , levelId ) ;
100103 } ) ;
101104
105+ it ( 'should build, sign, and serialize a complete validator registration transaction with fee delegation' , async function ( ) {
106+ // Step 1: Build the staking transaction
107+ const validatorRegistrationBuilder = factory . getValidatorRegistrationBuilder ( ) ;
108+
109+ validatorRegistrationBuilder
110+ . stakingContractAddress ( builtInStakerContractAddress )
111+ . stakingPeriod ( 60480 )
112+ . validator ( validatorAddress )
113+ . sender ( senderAddress )
114+ . chainTag ( 0x27 ) // Testnet chain tag
115+ . blockRef ( '0x014ead140e77bbc1' )
116+ . expiration ( 64 )
117+ . gas ( 100000 )
118+ . gasPriceCoef ( 128 )
119+ . nonce ( '12345' ) ;
120+
121+ validatorRegistrationBuilder . addFeePayerAddress ( feePayerAddress ) ;
122+
123+ const unsignedTx = await validatorRegistrationBuilder . build ( ) ;
124+ should . exist ( unsignedTx ) ;
125+ unsignedTx . should . be . instanceof ( ValidatorRegistrationTransaction ) ;
126+
127+ const validatorRegistrationTx = unsignedTx as ValidatorRegistrationTransaction ;
128+
129+ // Verify transaction structure
130+ validatorRegistrationTx . type . should . equal ( TransactionType . StakingLock ) ;
131+ validatorRegistrationTx . stakingContractAddress . should . equal ( builtInStakerContractAddress ) ;
132+ validatorRegistrationTx . stakingPeriod . should . equal ( stakingPeriod ) ;
133+ validatorRegistrationTx . validator . should . equal ( validatorAddress ) ;
134+
135+ should . exist ( validatorRegistrationTx . rawTransaction ) ;
136+ should . exist ( validatorRegistrationTx . rawTransaction . body ) ;
137+
138+ // This is the critical test - ensure reserved.features = 1
139+ should . exist ( validatorRegistrationTx . rawTransaction . body . reserved ) ;
140+ validatorRegistrationTx . rawTransaction . body . reserved ! . should . have . property ( 'features' , 1 ) ;
141+
142+ // Step 3: Add sender signature
143+ validatorRegistrationTx . addSenderSignature ( mockSenderSignature ) ;
144+ should . exist ( validatorRegistrationTx . senderSignature ) ;
145+ Buffer . from ( validatorRegistrationTx . senderSignature ! ) . should . eql ( mockSenderSignature ) ;
146+
147+ // Step 4: Add fee payer signature
148+ validatorRegistrationTx . addFeePayerSignature ( mockFeePayerSignature ) ;
149+ should . exist ( validatorRegistrationTx . feePayerSignature ) ;
150+ Buffer . from ( validatorRegistrationTx . feePayerSignature ! ) . should . eql ( mockFeePayerSignature ) ;
151+
152+ // Step 5: Generate transaction ID
153+
154+ // This should NOT throw "not signed transaction: id unavailable" error anymore
155+ const transactionId = validatorRegistrationTx . id ;
156+ should . exist ( transactionId ) ;
157+ transactionId . should . not . equal ( 'UNAVAILABLE' ) ;
158+
159+ // Step 6: Serialize the fully signed transaction
160+ const serializedTx = validatorRegistrationTx . toBroadcastFormat ( ) ;
161+ should . exist ( serializedTx ) ;
162+ serializedTx . should . be . type ( 'string' ) ;
163+ serializedTx . should . startWith ( '0x' ) ;
164+
165+ // Step 7: Verify transaction can be deserialized
166+ const deserializedBuilder = factory . from ( serializedTx ) ;
167+ const deserializedTx = deserializedBuilder . transaction as ValidatorRegistrationTransaction ;
168+
169+ deserializedTx . should . be . instanceof ( ValidatorRegistrationTransaction ) ;
170+ deserializedTx . stakingContractAddress . should . equal ( builtInStakerContractAddress ) ;
171+ deserializedTx . stakingPeriod . should . equal ( stakingPeriod ) ;
172+ deserializedTx . validator . should . equal ( validatorAddress ) ;
173+
174+ // Step 8: Verify toJson output
175+ const jsonOutput = validatorRegistrationTx . toJson ( ) ;
176+ should . exist ( jsonOutput ) ;
177+ jsonOutput . should . have . property ( 'id' , transactionId ) ;
178+ jsonOutput . should . have . property ( 'stakingContractAddress' , builtInStakerContractAddress ) ;
179+ jsonOutput . should . have . property ( 'stakingPeriod' , stakingPeriod ) ;
180+ jsonOutput . should . have . property ( 'validatorAddress' , validatorAddress ) ;
181+ } ) ;
182+
102183 it ( 'should handle signature combination in the correct order' , async function ( ) {
103184 // This test specifically validates the signature combination flow that was failing
104185 const stakingBuilder = factory . getStakingActivateBuilder ( ) ;
0 commit comments