Skip to content

Commit 42f1dd1

Browse files
fix(sdk-coin-vet): update stake and delegate builders for hayabusa
Ticket: SC-3989
1 parent c82d952 commit 42f1dd1

File tree

9 files changed

+87
-52
lines changed

9 files changed

+87
-52
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export const STARGATE_NFT_ADDRESS = '0x1856c533ac2d94340aaa8544d35a5c1d4a21dee7'
1717
export const STARGATE_DELEGATION_ADDRESS = '0x4cb1c9ef05b529c093371264fab2c93cc6cddb0e';
1818

1919
export const STARGATE_NFT_ADDRESS_TESTNET = '0x1ec1d168574603ec35b9d229843b7c2b44bcb770';
20-
export const STARGATE_DELEGATION_ADDRESS_TESTNET = '0x7240e3bc0d26431512d5b67dbd26d199205bffe8';
20+
export const STARGATE_CONTRACT_ADDRESS_TESTNET = '0x1E02B2953AdEfEC225cF0Ec49805b1146a4429C1';
2121

2222
export const AVG_GAS_UNITS = '21000';
2323
export const EXPIRATION = 400;

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,12 @@ export interface VetTransactionData {
2828
tokenId?: string; // Added for unstaking and burn NFT transactions
2929
stakingContractAddress?: string;
3030
amountToStake?: string;
31+
levelId?: number; // NFT tier level
3132
nftTokenId?: number; // Used as tier level (levelId) for stakeAndDelegate method (not the actual NFT token ID)
3233
autorenew?: boolean; // Autorenew flag for stakeAndDelegate method
3334
nftCollectionId?: string;
3435
claimRewardsData?: ClaimRewardsData;
36+
validatorAddress?: string;
3537
}
3638

3739
export interface VetTransactionExplanation extends BaseTransactionExplanation {

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

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,27 @@ import { VetTransactionData } from '../iface';
66
import EthereumAbi from 'ethereumjs-abi';
77
import utils from '../utils';
88
import BigNumber from 'bignumber.js';
9-
import { addHexPrefix } from 'ethereumjs-util';
9+
import { addHexPrefix, BN } from 'ethereumjs-util';
1010
import { ZERO_VALUE_AMOUNT } from '../constants';
1111

1212
export class DelegateClauseTransaction extends Transaction {
1313
private _stakingContractAddress: string;
14-
private _tokenId: number;
15-
private _delegateForever = true;
14+
private _tokenId: string;
15+
private _validator: string;
1616

1717
constructor(_coinConfig: Readonly<CoinConfig>) {
1818
super(_coinConfig);
1919
this._type = TransactionType.StakingDelegate;
2020
}
2121

22+
get validator(): string {
23+
return this._validator;
24+
}
25+
26+
set validator(address: string) {
27+
this._validator = address;
28+
}
29+
2230
get stakingContractAddress(): string {
2331
return this._stakingContractAddress;
2432
}
@@ -27,20 +35,12 @@ export class DelegateClauseTransaction extends Transaction {
2735
this._stakingContractAddress = address;
2836
}
2937

30-
get tokenId(): number {
38+
get tokenId(): string {
3139
return this._tokenId;
3240
}
3341

34-
set tokenId(tokenId: number) {
35-
this._tokenId = tokenId;
36-
}
37-
38-
get delegateForever(): boolean {
39-
return this._delegateForever;
40-
}
41-
42-
set delegateForever(delegateForever: boolean) {
43-
this._delegateForever = delegateForever;
42+
set tokenId(tokenId: string) {
43+
this.tokenId = tokenId;
4444
}
4545

4646
buildClauses(): void {
@@ -54,7 +54,11 @@ export class DelegateClauseTransaction extends Transaction {
5454
throw new Error('Token ID is not set');
5555
}
5656

57-
const data = this.getDelegateData(this.tokenId, this.delegateForever);
57+
if (this.validator === undefined || this.validator === null) {
58+
throw new Error('Validator address is not set');
59+
}
60+
61+
const data = this.getDelegateData(this.tokenId, this.validator);
5862
this._transactionData = data;
5963

6064
// Create the clause for delegation
@@ -80,10 +84,10 @@ export class DelegateClauseTransaction extends Transaction {
8084
* @param {number} tokenId - The Token ID for delegation
8185
* @returns {string} - The encoded transaction data
8286
*/
83-
getDelegateData(levelId: number, delegateForever = true): string {
87+
getDelegateData(tokenId: string, validatorAddress: string): string {
8488
const methodName = 'delegate';
85-
const types = ['uint256', 'bool'];
86-
const params = [levelId, delegateForever];
89+
const types = ['uint256', 'address'];
90+
const params = [new BN(tokenId), validatorAddress];
8791

8892
const method = EthereumAbi.methodID(methodName, types);
8993
const args = EthereumAbi.rawEncode(types, params);
@@ -107,8 +111,8 @@ export class DelegateClauseTransaction extends Transaction {
107111
to: this.stakingContractAddress,
108112
stakingContractAddress: this.stakingContractAddress,
109113
amountToStake: ZERO_VALUE_AMOUNT,
110-
nftTokenId: this.tokenId,
111-
autorenew: this.delegateForever,
114+
tokenId: this.tokenId,
115+
validatorAddress: this.validator,
112116
};
113117

114118
return json;
@@ -144,7 +148,7 @@ export class DelegateClauseTransaction extends Transaction {
144148
this.transactionData = clause.data;
145149
const decoded = utils.decodeDelegateClauseData(clause.data);
146150
this.tokenId = decoded.tokenId;
147-
this.delegateForever = decoded.delegateForever;
151+
this.validator = decoded.validator;
148152
}
149153
}
150154

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ export class StakeClauseTransaction extends Transaction {
110110
to: this.stakingContractAddress,
111111
stakingContractAddress: this.stakingContractAddress,
112112
amountToStake: this.amountToStake,
113-
nftTokenId: this.levelId,
113+
levelId: this.levelId,
114114
};
115115

116116
return json;

modules/sdk-coin-vet/src/lib/transactionBuilder/delegateTxnBuilder.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,14 +88,28 @@ export class DelegateTxnBuilder extends TransactionBuilder {
8888
/**
8989
* Sets the token ID for this delegate tx.
9090
*
91-
* @param {number} levelId - The level ID for staking
91+
* @param {number} levelId - The NFT token ID
9292
* @returns {DelegateTxnBuilder} This transaction builder
9393
*/
94-
tokenId(tokenId: number): this {
94+
tokenId(tokenId: string): this {
9595
this.delegateTransaction.tokenId = tokenId;
9696
return this;
9797
}
9898

99+
/**
100+
* Sets the validator address for this delegate tx.
101+
* @param {string} address - The validator address
102+
* @returns {DelegateTxnBuilder} This transaction builder
103+
*/
104+
validator(address: string): this {
105+
if (!address) {
106+
throw new Error('Validator address is required');
107+
}
108+
this.validateAddress({ address });
109+
this.delegateTransaction.validator = address;
110+
return this;
111+
}
112+
99113
/**
100114
* Sets the transaction data for this delegate tx.
101115
*
@@ -114,8 +128,8 @@ export class DelegateTxnBuilder extends TransactionBuilder {
114128
}
115129
assert(transaction.stakingContractAddress, 'Staking contract address is required');
116130

117-
assert(transaction.tokenId, 'Token ID is required');
118-
assert(transaction.delegateForever, 'delegate forever flag is required');
131+
assert(transaction.levelId, 'Level ID is required');
132+
assert(transaction.validator, 'validator address is required');
119133
this.validateAddress({ address: transaction.stakingContractAddress });
120134
}
121135

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -179,21 +179,21 @@ export class Utils implements BaseUtils {
179179
}
180180

181181
/**
182-
* Decodes delegate transaction data to extract tokenId and delegateForever
182+
* Decodes delegate transaction data to extract tokenId and validatorAddress
183183
*
184184
* @param {string} data - The encoded transaction data
185-
* @returns {object} - Object containing tokenId and delegateForever
185+
* @returns {object} - Object containing levelId and validator address
186186
*/
187-
decodeDelegateClauseData(data: string): { tokenId: number; delegateForever: boolean } {
187+
decodeDelegateClauseData(data: string): { tokenId: string; validator: string } {
188188
try {
189189
const parameters = data.slice(10);
190190

191191
// Decode using ethereumjs-abi directly
192-
const decoded = EthereumAbi.rawDecode(['uint256', 'bool'], Buffer.from(parameters, 'hex'));
192+
const decoded = EthereumAbi.rawDecode(['uint256', 'address'], Buffer.from(parameters, 'hex'));
193193

194194
return {
195-
tokenId: Number(decoded[0]),
196-
delegateForever: Boolean(decoded[1]),
195+
tokenId: String(decoded[0]),
196+
validator: String(decoded[1]),
197197
};
198198
} catch (error) {
199199
throw new Error(`Failed to decode delegation data: ${error.message}`);

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export const STAKING_TRANSACTION =
1515
'0xf901032788015d55fcf2457e7c40f866f864941856c533ac2d94340aaa8544d35a5c1d4a21dee7880de0b6b3a7640000b844d8da3bbf0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000181808265848083094c53c101b882efcb9ea88e908d1a142db96c1b44dd056ea194f1ad45670c100a8c52348cc7b20387741260ebe7fe9b7594f96693c88662fa60edba5992332728222b0bdd8a30008535368bd901319eb4513d16bebc428dc8454d32a19eeb76372849a6134ebbba79f1eeceea1f6546574b945c05489222cb451f5b0e2901b0c687b750e833aeb800';
1616

1717
export const STAKE_CLAUSE_TRANSACTION =
18-
'0xf8e3278801618b7b1354c4ca40f845f843941ec1d168574603ec35b9d229843b7c2b44bcb770880de0b6b3a7640000a4604f2177000000000000000000000000000000000000000000000000000000000000000881808305fd5a808307b278c101b882b380970580d957b8e7989aa9aa9281e57245fa3835cb2aaae6475b4062bb4f1c2dd2ca694df6503d5dfd654579130b3484bee75420247cf8f6b6b4b76b7939f101db6e6cef5a27375274741f3c0aba4be13a9e086337c3290866afe049efcdaa2d3227c9e12b52627c4d71f5b667821f9d33adcc4c97fdc28b93c34013d32e242300';
18+
'0xf8e5278801638298c53767ac40f847f845941e02b2953adefec225cf0ec49805b1146a4429c18a021e19e0c9bab2400000a4604f2177000000000000000000000000000000000000000000000000000000000000000881808306338a80830f340bc101b882b4970b0c160552162de719b9ed1fa6268bbfe9b36fd4e4a5c13e956cf539e60478f69179e8db4a3106fdbe775e3d923510b16f48da56c66076d1f66ffd822abf01ebc040c795b1e17cb0ca5ba747e3a181b4eefea7db5378f64f82361bdb7745da45aba2ace3db9822b675474552f13849052987c431cd4867813c2cf635302b1101';
1919

2020
export const DELEGATION_TRANSACTION =
2121
'0xf8fb278801618aa3e0a55fc940f85ef85c947240e3bc0d26431512d5b67dbd26d199205bffe880b8443207555d00000000000000000000000000000000000000000000000000000000000187690000000000000000000000000000000000000000000000000000000000000001818082e43a808306af07c101b882fb8030f6e2ef6563ff3b0e7e2a2292c1db5fc41c7ab9f598bad370c5cfd3dc32286ae8d709e941c0312c8cd33a3505156b44d1639c73980ffa66bc72f37820f2001c0e6b6e76a6a4d806c377a0a279053eb6ea4356bd235f4396585bb071d70f992c639d45c53431a3c1493a52a136203905e42c671dd384ee5f5ead0a70cb607001';

modules/sdk-coin-vet/test/transactionBuilder/delegateClauseTxnBuilder.ts

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import { coins } from '@bitgo/statics';
22
import { TransactionBuilderFactory, Transaction, DelegateClauseTransaction } from '../../src/lib';
33
import should from 'should';
4-
import { DELEGATE_CLAUSE_METHOD_ID, STARGATE_DELEGATION_ADDRESS_TESTNET } from '../../src/lib/constants';
4+
import { DELEGATE_CLAUSE_METHOD_ID, STARGATE_CONTRACT_ADDRESS_TESTNET } from '../../src/lib/constants';
55
import EthereumAbi from 'ethereumjs-abi';
66
import * as testData from '../resources/vet';
7+
import { BN } from 'ethereumjs-util';
78

89
describe('VET Delegation Transaction', function () {
910
const factory = new TransactionBuilderFactory(coins.get('tvet'));
10-
const tokenId = 100201; // Test level ID
11-
const delegateForever = true; // Test delegateForever flag
11+
const tokenId = '100201'; // Test token ID
12+
const validatorAddress = '0x9a7aFCACc88c106f3bbD6B213CD0821D9224d945';
1213

1314
// Helper function to create a basic transaction builder with common properties
1415
const createBasicTxBuilder = () => {
@@ -20,12 +21,13 @@ describe('VET Delegation Transaction', function () {
2021
txBuilder.gas(100000);
2122
txBuilder.gasPriceCoef(0);
2223
txBuilder.nonce('12345');
24+
txBuilder.validator(validatorAddress);
2325
return txBuilder;
2426
};
2527

2628
it('should build a delegate transaction', async function () {
2729
const txBuilder = factory.getStakingDelegateBuilder();
28-
txBuilder.stakingContractAddress(STARGATE_DELEGATION_ADDRESS_TESTNET);
30+
txBuilder.stakingContractAddress(STARGATE_CONTRACT_ADDRESS_TESTNET);
2931
txBuilder.tokenId(tokenId);
3032
txBuilder.sender('0x9378c12BD7502A11F770a5C1F223c959B2805dA9');
3133
txBuilder.chainTag(0x27); // Testnet chain tag
@@ -34,21 +36,22 @@ describe('VET Delegation Transaction', function () {
3436
txBuilder.gas(100000);
3537
txBuilder.gasPriceCoef(0);
3638
txBuilder.nonce('12345');
39+
txBuilder.validator(validatorAddress);
3740

3841
const tx = await txBuilder.build();
3942
should.exist(tx);
4043
tx.should.be.instanceof(Transaction);
4144
tx.should.be.instanceof(DelegateClauseTransaction);
4245

4346
const delegationTx = tx as DelegateClauseTransaction;
44-
delegationTx.stakingContractAddress.should.equal(STARGATE_DELEGATION_ADDRESS_TESTNET);
47+
delegationTx.stakingContractAddress.should.equal(STARGATE_CONTRACT_ADDRESS_TESTNET);
4548
delegationTx.tokenId.should.equal(tokenId);
46-
delegationTx.delegateForever.should.equal(delegateForever);
49+
delegationTx.validator.should.equal(validatorAddress);
4750

4851
// Verify clauses
4952
delegationTx.clauses.length.should.equal(1);
5053
should.exist(delegationTx.clauses[0].to);
51-
delegationTx.clauses[0].to?.should.equal(STARGATE_DELEGATION_ADDRESS_TESTNET);
54+
delegationTx.clauses[0].to?.should.equal(STARGATE_CONTRACT_ADDRESS_TESTNET);
5255

5356
// Verify transaction data is correctly encoded using ethereumABI
5457
should.exist(delegationTx.clauses[0].data);
@@ -57,8 +60,8 @@ describe('VET Delegation Transaction', function () {
5760

5861
// Verify the encoded data matches what we expect from ethereumABI
5962
const methodName = 'delegate';
60-
const types = ['uint256', 'bool'];
61-
const params = [tokenId, delegateForever];
63+
const types = ['uint256', 'address'];
64+
const params = [new BN(tokenId), validatorAddress];
6265

6366
const method = EthereumAbi.methodID(methodName, types);
6467
const args = EthereumAbi.rawEncode(types, params);
@@ -68,24 +71,33 @@ describe('VET Delegation Transaction', function () {
6871

6972
// Verify recipients
7073
delegationTx.recipients.length.should.equal(1);
71-
delegationTx.recipients[0].address.should.equal(STARGATE_DELEGATION_ADDRESS_TESTNET);
74+
delegationTx.recipients[0].address.should.equal(STARGATE_CONTRACT_ADDRESS_TESTNET);
7275
});
7376

7477
describe('Failure scenarios', function () {
7578
it('should throw error when stakingContractAddress is missing', async function () {
7679
const txBuilder = createBasicTxBuilder();
7780
txBuilder.tokenId(tokenId);
81+
txBuilder.validator(validatorAddress);
7882

7983
await txBuilder.build().should.be.rejectedWith('Staking contract address is required');
8084
});
8185

8286
it('should throw error when tokenId is missing', async function () {
8387
const txBuilder = createBasicTxBuilder();
84-
txBuilder.stakingContractAddress(STARGATE_DELEGATION_ADDRESS_TESTNET);
88+
txBuilder.stakingContractAddress(STARGATE_CONTRACT_ADDRESS_TESTNET);
8589

8690
await txBuilder.build().should.be.rejectedWith('Token ID is required');
8791
});
8892

93+
it('should throw error when validator address is missing', async function () {
94+
const txBuilder = createBasicTxBuilder();
95+
txBuilder.stakingContractAddress(STARGATE_CONTRACT_ADDRESS_TESTNET);
96+
txBuilder.tokenId(tokenId);
97+
98+
await txBuilder.build().should.be.rejectedWith('Validator address is required');
99+
});
100+
89101
it('should throw error when stakingContractAddress is invalid', async function () {
90102
const txBuilder = createBasicTxBuilder();
91103

@@ -97,14 +109,15 @@ describe('VET Delegation Transaction', function () {
97109

98110
it('should build transaction with undefined sender but include it in inputs', async function () {
99111
const txBuilder = factory.getStakingDelegateBuilder();
100-
txBuilder.stakingContractAddress(STARGATE_DELEGATION_ADDRESS_TESTNET);
112+
txBuilder.stakingContractAddress(STARGATE_CONTRACT_ADDRESS_TESTNET);
101113
txBuilder.tokenId(tokenId);
102114
txBuilder.chainTag(0x27);
103115
txBuilder.blockRef('0x0000000000000000');
104116
txBuilder.expiration(64);
105117
txBuilder.gas(100000);
106118
txBuilder.gasPriceCoef(0);
107119
txBuilder.nonce('12345');
120+
txBuilder.validator(validatorAddress);
108121
// Not setting sender
109122

110123
const tx = await txBuilder.build();
@@ -117,12 +130,12 @@ describe('VET Delegation Transaction', function () {
117130

118131
// Verify the transaction has the correct output
119132
delegationTx.outputs.length.should.equal(1);
120-
delegationTx.outputs[0].address.should.equal(STARGATE_DELEGATION_ADDRESS_TESTNET);
133+
delegationTx.outputs[0].address.should.equal(STARGATE_CONTRACT_ADDRESS_TESTNET);
121134
});
122135

123136
it('should use network default chainTag when not explicitly set', async function () {
124137
const txBuilder = factory.getStakingDelegateBuilder();
125-
txBuilder.stakingContractAddress(STARGATE_DELEGATION_ADDRESS_TESTNET);
138+
txBuilder.stakingContractAddress(STARGATE_CONTRACT_ADDRESS_TESTNET);
126139
txBuilder.tokenId(tokenId);
127140
// Not setting chainTag
128141
txBuilder.blockRef('0x0000000000000000');
@@ -131,6 +144,7 @@ describe('VET Delegation Transaction', function () {
131144
txBuilder.gasPriceCoef(0);
132145
txBuilder.nonce('12345');
133146
txBuilder.sender('0x9378c12BD7502A11F770a5C1F223c959B2805dA9');
147+
txBuilder.validator(validatorAddress);
134148

135149
const tx = await txBuilder.build();
136150
tx.should.be.instanceof(DelegateClauseTransaction);
@@ -140,6 +154,7 @@ describe('VET Delegation Transaction', function () {
140154
delegationTx.chainTag.should.equal(39);
141155
});
142156

157+
// TODO(SC-3926): this test needs to be updated with delegate txn data once its done in coins sandbox
143158
it('should build a signed tx and validate its toJson', async function () {
144159
const txBuilder = factory.from(testData.DELEGATION_TRANSACTION);
145160
const tx = txBuilder.transaction as DelegateClauseTransaction;
@@ -153,7 +168,7 @@ describe('VET Delegation Transaction', function () {
153168
toJson.chainTag.should.equal(39);
154169
// in delegate txn, nftTokenId indicates the tokenId
155170
toJson.nftTokenId?.should.equal(tokenId);
156-
toJson.autorenew?.should.equal(true);
171+
toJson.validatorAddress?.should.equal(validatorAddress);
157172
});
158173
});
159174
});

modules/sdk-coin-vet/test/transactionBuilder/stakeClauseTransactionBuilder.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import * as testData from '../resources/vet';
88
describe('VET Staking Transaction', function () {
99
const factory = new TransactionBuilderFactory(coins.get('tvet'));
1010
const stakingContractAddress = STARGATE_NFT_ADDRESS_TESTNET;
11-
const amountToStake = '1000000000000000000'; // 1 VET in wei
11+
const amountToStake = '10000000000000000000000'; // 10000 VET in wei
1212
const levelId = 8; // Test level ID
1313

1414
// Helper function to create a basic transaction builder with common properties
@@ -240,8 +240,8 @@ describe('VET Staking Transaction', function () {
240240
const txBuilder = factory.from(testData.STAKE_CLAUSE_TRANSACTION);
241241
const tx = txBuilder.transaction as StakeClauseTransaction;
242242
const toJson = tx.toJson();
243-
toJson.id.should.equal('0x2f96e4c16d70bd3e2dabec29a07eb3d6066691ba5b812d6e897676f6ebc0a798');
244-
toJson.stakingContractAddress?.should.equal('0x1ec1d168574603ec35b9d229843b7c2b44bcb770');
243+
toJson.id.should.equal('0x7148f62b42ecfdcb7ee62ac0654514e4b5f65f2fe5fdee79d4d29f56ab1722eb');
244+
toJson.stakingContractAddress?.should.equal('0x1E02B2953AdEfEC225cF0Ec49805b1146a4429C1');
245245
toJson.amountToStake?.should.equal('0xde0b6b3a7640000');
246246
toJson.nonce.should.equal('504440');
247247
toJson.gas.should.equal(392538);

0 commit comments

Comments
 (0)