Skip to content

Commit c57dd55

Browse files
fix(sdk-coin-vet): invalid id for vet validation regn txn
Ticket: SC-4409
1 parent 8626186 commit c57dd55

File tree

3 files changed

+85
-2
lines changed

3 files changed

+85
-2
lines changed

modules/sdk-coin-vet/src/lib/transaction/transaction.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,8 @@ export class Transaction extends BaseTransaction {
393393
this.type === TransactionType.StakingDelegate ||
394394
this.type === TransactionType.StakingUnlock ||
395395
this.type === TransactionType.StakingWithdraw ||
396-
this.type === TransactionType.StakingClaim
396+
this.type === TransactionType.StakingClaim ||
397+
this.type === TransactionType.StakingLock
397398
) {
398399
transactionBody.reserved = {
399400
features: 1, // mark transaction as delegated i.e. will use gas payer

modules/sdk-coin-vet/test/resources/vet.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export const CLAIM_REWARDS_TRANSACTION =
3535
export const STAKING_LEVEL_ID = 8;
3636
export const STAKING_AUTORENEW = true;
3737
export const STAKING_CONTRACT_ADDRESS = '0x1e02b2953adefec225cf0ec49805b1146a4429c1';
38+
export const BUILT_IN_STAKER_CONTRACT_ADDRESS = '0x00000000000000000000000000005374616b6572';
3839

3940
export const VALID_TOKEN_SIGNABLE_PAYLOAD =
4041
'f8762788014ead140e77bbc140f85ef85c940000000000000000000000000000456e6572677980b844a9059cbb000000000000000000000000e59f1cea4e0fef511e3d0f4eec44adf19c4cbeec000000000000000000000000000000000000000000000000016345785d8a000081808252088082faf8c101';

modules/sdk-coin-vet/test/unit/stakingFlowE2E.ts

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import should from 'should';
22
import { coins } from '@bitgo/statics';
33
import { TransactionType } from '@bitgo/sdk-core';
4-
import { TransactionBuilderFactory, StakeClauseTransaction } from '../../src/lib';
4+
import { TransactionBuilderFactory, StakeClauseTransaction, ValidatorRegistrationTransaction } from '../../src/lib';
55
import * as testData from '../resources/vet';
66

77
describe('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

Comments
 (0)