Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions modules/sdk-coin-vet/src/lib/transaction/burnNftTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import utils from '../utils';

export class BurnNftTransaction extends Transaction {
private _tokenId: string;
private _stakingContractAddress: string;

constructor(_coinConfig: Readonly<CoinConfig>) {
super(_coinConfig);
Expand All @@ -24,6 +25,14 @@ export class BurnNftTransaction extends Transaction {
this._tokenId = id;
}

get stakingContractAddress(): string {
return this._stakingContractAddress;
}

set stakingContractAddress(address: string) {
this._stakingContractAddress = address;
}

/** @inheritdoc */
async build(): Promise<void> {
this.buildClauses();
Expand All @@ -34,23 +43,23 @@ export class BurnNftTransaction extends Transaction {

/** @inheritdoc */
buildClauses(): void {
if (!this._contract || !this._tokenId) {
if (!this._stakingContractAddress || !this._tokenId) {
throw new InvalidTransactionError('Missing required burn NFT parameters');
}

utils.validateStakingContractAddress(this._contract, this._coinConfig);
utils.validateStakingContractAddress(this._stakingContractAddress, this._coinConfig);

this._clauses = [
{
to: this._contract,
to: this._stakingContractAddress,
value: '0x0',
data: this._transactionData || this.getBurnNftData(),
},
];

this._recipients = [
{
address: this._contract,
address: this._stakingContractAddress,
amount: '0',
},
];
Expand Down Expand Up @@ -87,7 +96,8 @@ export class BurnNftTransaction extends Transaction {
data: this.transactionData || this.getBurnNftData(),
value: '0',
sender: this.sender,
to: this.contract,
to: this.stakingContractAddress,
tokenId: this.tokenId,
};
return json;
}
Expand All @@ -114,7 +124,7 @@ export class BurnNftTransaction extends Transaction {
this.nonce = String(body.nonce);

// Set data from clauses
this.contract = body.clauses[0]?.to || '0x0';
this.stakingContractAddress = body.clauses[0]?.to || '0x0';
this.transactionData = body.clauses[0]?.data || '0x0';
this.type = TransactionType.StakingWithdraw;

Expand Down
22 changes: 16 additions & 6 deletions modules/sdk-coin-vet/src/lib/transaction/exitDelegation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import BigNumber from 'bignumber.js';

export class ExitDelegationTransaction extends Transaction {
private _tokenId: string;
private _stakingContractAddress: string;

constructor(_coinConfig: Readonly<CoinConfig>) {
super(_coinConfig);
Expand All @@ -25,6 +26,14 @@ export class ExitDelegationTransaction extends Transaction {
this._tokenId = id;
}

get stakingContractAddress(): string {
return this._stakingContractAddress;
}

set stakingContractAddress(address: string) {
this._stakingContractAddress = address;
}

/** @inheritdoc */
async build(): Promise<void> {
this.buildClauses();
Expand All @@ -35,23 +44,23 @@ export class ExitDelegationTransaction extends Transaction {

/** @inheritdoc */
buildClauses(): void {
if (!this._contract || !this._tokenId) {
if (!this._stakingContractAddress || !this._tokenId) {
throw new InvalidTransactionError('Missing required unstaking parameters');
}

utils.validateDelegationContractAddress(this._contract, this._coinConfig);
utils.validateStakingContractAddress(this._stakingContractAddress, this._coinConfig);

this._clauses = [
{
to: this._contract,
to: this._stakingContractAddress,
value: '0x0',
data: this._transactionData || this.getExitDelegationData(),
},
];

this._recipients = [
{
address: this._contract,
address: this._stakingContractAddress,
amount: '0',
},
];
Expand Down Expand Up @@ -88,7 +97,8 @@ export class ExitDelegationTransaction extends Transaction {
data: this.transactionData || this.getExitDelegationData(),
value: '0',
sender: this.sender,
to: this.contract,
to: this.stakingContractAddress,
tokenId: this.tokenId,
};
return json;
}
Expand All @@ -115,7 +125,7 @@ export class ExitDelegationTransaction extends Transaction {
this.nonce = String(body.nonce);

// Set data from clauses
this.contract = body.clauses[0]?.to || '0x0';
this.stakingContractAddress = body.clauses[0]?.to || '0x0';
this.transactionData = body.clauses[0]?.data || '0x0';
this.type = TransactionType.StakingUnlock;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,19 +92,19 @@ export class BurnNftBuilder extends TransactionBuilder {
}

/**
* Sets the NFT contract address for this burn NFT transaction.
* Sets the staking contract address for this staking tx.
* The address must be explicitly provided to ensure the correct contract is used.
*
* @param {string} address - The NFT contract address (required)
* @returns {BurnNftBuilder} This transaction builder
* @param {string} address - The staking contract address (required)
* @returns {StakingBuilder} This transaction builder
* @throws {Error} If no address is provided
*/
nftContract(address: string): this {
stakingContractAddress(address: string): this {
if (!address) {
throw new Error('NFT contract address is required and must be explicitly provided');
throw new Error('Staking contract address is required');
}
this.validateAddress({ address });
this.burnNftTransaction.contract = address;
this.burnNftTransaction.stakingContractAddress = address;
return this;
}

Expand All @@ -113,10 +113,10 @@ export class BurnNftBuilder extends TransactionBuilder {
if (!transaction) {
throw new Error('transaction not defined');
}
assert(transaction.contract, 'NFT contract address is required');
assert(transaction.stakingContractAddress, 'Staking contract address is required');
assert(transaction.tokenId, 'Token ID is required');

this.validateAddress({ address: transaction.contract });
this.validateAddress({ address: transaction.stakingContractAddress });
}

/** @inheritdoc */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export class ExitDelegationBuilder extends TransactionBuilder {
}

/**
* Validates the transaction clauses for unstaking.
* Validates the transaction clauses for exit delegation.
* @param {TransactionClause[]} clauses - The transaction clauses to validate.
* @returns {boolean} - Returns true if the clauses are valid, false otherwise.
*/
Expand Down Expand Up @@ -92,16 +92,19 @@ export class ExitDelegationBuilder extends TransactionBuilder {
}

/**
* Sets the delegation contract address for this unstaking transaction.
* If not provided, uses the network-appropriate default address.
* Sets the staking contract address for this staking tx.
* The address must be explicitly provided to ensure the correct contract is used.
*
* @param {string} address - The delegation contract address
* @returns {ExitDelegationBuilder} This transaction builder
* @param {string} address - The staking contract address (required)
* @returns {StakingBuilder} This transaction builder
* @throws {Error} If no address is provided
*/
delegationContract(address?: string): this {
const contractAddress = address || utils.getDefaultDelegationAddress(this._coinConfig);
this.validateAddress({ address: contractAddress });
this.exitDelegationTransaction.contract = contractAddress;
stakingContractAddress(address: string): this {
if (!address) {
throw new Error('Staking contract address is required');
}
this.validateAddress({ address });
this.exitDelegationTransaction.stakingContractAddress = address;
return this;
}

Expand All @@ -110,10 +113,10 @@ export class ExitDelegationBuilder extends TransactionBuilder {
if (!transaction) {
throw new Error('transaction not defined');
}
assert(transaction.contract, 'Delegation contract address is required');
assert(transaction.stakingContractAddress, 'Staking contract address is required');
assert(transaction.tokenId, 'Token ID is required');

this.validateAddress({ address: transaction.contract });
this.validateAddress({ address: transaction.stakingContractAddress });
}

/** @inheritdoc */
Expand Down
6 changes: 6 additions & 0 deletions modules/sdk-coin-vet/test/resources/vet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ export const STAKE_CLAUSE_TRANSACTION =
export const DELEGATION_TRANSACTION =
'0xf8fc278801640639091a26ce40f85ef85c941e02b2953adefec225cf0ec49805b1146a4429c180b84408bbb8240000000000000000000000000000000000000000000000000000000000003d2e00000000000000000000000000563ec3cafbbe7e60b04b3190e6eca66579706d8180830464b080830d8b05c101b8821a3cca8e8339456c6055ef796e5d716dda00de45f4cd9431bedf2119ae5de01b1f0a7268690784ba8f5c22b3043d0530ece5303a813ffdd9c0a5ae0ae85deee400b04543d6874f30eca88b3efb927c44934e9eb64a6f2327cce44a0a94faaca13615d153e804ba3fdd02bf5f8e1b6bc8e0f6149a1c7694803ed4fbb549bb79066101';

export const EXIT_DELEGATION_TRANSACTION =
'0xf8db278801640bf461bc7e1840f83df83b941e02b2953adefec225cf0ec49805b1146a4429c180a469e79b7d0000000000000000000000000000000000000000000000000000000000003d2d81808303525f808305f65ac101b8820cb393317793011b0a205973c77761f5c5c8652c21fe0115f527d2e2f2c1b5fc72a048107b263764312e9323f2ace9f30ce0beed873d7ef7f5432943330d2d5000a4a5f6439503f235ac6a5e17b47ac26c9e0c9e3be9dbd4cec3266fea324eb9bf5f806cedca59ff4144deb0ca18c41d9d6d600a86bf3d4e7b930bcec9b04c2e7301';

export const BURN_NFT_TRANSACTION =
'0xf8db278801640bfe6c1ee3e640f83df83b941e02b2953adefec225cf0ec49805b1146a4429c180a42e17de780000000000000000000000000000000000000000000000000000000000003d2d81808305548c808304fe38c101b882044933b92e0fc5517d58205b46211a5ad2403103c8c217ce9682ebe2457e374f655fc6be307c7dfd59f0f4eda2aab7e3a1ac9219923086cde52e6405c34de2d801d692df740f95dd4ac6dbae7eb6e91a712a1e456e1a80e3a2f501ea1e6ed12c4308e65a8a98b0142190812d4484f54121bbc95b6048ae09de5946304affbfba1400';

export const STAKING_LEVEL_ID = 8;
export const STAKING_AUTORENEW = true;
export const STAKING_CONTRACT_ADDRESS = '0x1e02b2953adefec225cf0ec49805b1146a4429c1';
Expand Down
53 changes: 23 additions & 30 deletions modules/sdk-coin-vet/test/transactionBuilder/burnNftBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { BURN_NFT_METHOD_ID, STARGATE_CONTRACT_ADDRESS_TESTNET } from '../../src

describe('Vet Burn NFT Transaction', () => {
const factory = new TransactionBuilderFactory(coins.get('tvet'));
const stakingContractAddress = STARGATE_CONTRACT_ADDRESS_TESTNET;

describe('Build and Sign', () => {
it('should build a burn NFT transaction', async function () {
Expand All @@ -16,7 +17,7 @@ describe('Vet Burn NFT Transaction', () => {

txBuilder.sender(testData.addresses.validAddresses[0]);
txBuilder.tokenId(tokenId);
txBuilder.nftContract(STARGATE_CONTRACT_ADDRESS_TESTNET);
txBuilder.stakingContractAddress(stakingContractAddress);
txBuilder.gas(21000);
txBuilder.nonce('64248');
txBuilder.blockRef('0x014ead140e77bbc1');
Expand All @@ -27,7 +28,7 @@ describe('Vet Burn NFT Transaction', () => {

should.equal(tx.sender, testData.addresses.validAddresses[0]);
should.equal(tx.tokenId, tokenId);
should.equal(tx.contract, STARGATE_CONTRACT_ADDRESS_TESTNET);
should.equal(tx.stakingContractAddress, STARGATE_CONTRACT_ADDRESS_TESTNET);
should.equal(tx.gas, 21000);
should.equal(tx.nonce, '64248');
should.equal(tx.expiration, 64);
Expand Down Expand Up @@ -56,36 +57,14 @@ describe('Vet Burn NFT Transaction', () => {
should.equal(tx.outputs[0].coin, 'tvet');
});

it('should build a burn NFT transaction with custom contract address', async function () {
const tokenId = '123456';
const customContractAddress = STARGATE_CONTRACT_ADDRESS_TESTNET; // Use the valid testnet NFT address
const txBuilder = factory.getBurnNftBuilder();

txBuilder.sender(testData.addresses.validAddresses[0]);
txBuilder.tokenId(tokenId);
txBuilder.nftContract(customContractAddress);
txBuilder.gas(21000);
txBuilder.nonce('64248');
txBuilder.blockRef('0x014ead140e77bbc1');
txBuilder.expiration(64);
txBuilder.gasPriceCoef(128);

const tx = (await txBuilder.build()) as BurnNftTransaction;

should.equal(tx.contract, customContractAddress);
should.exist(tx.clauses[0]);
should.exist(tx.clauses[0].to);
tx.clauses[0]?.to?.should.equal(customContractAddress);
});

it('should deserialize and reserialize a signed burn NFT transaction', async function () {
// Create a mock serialized transaction for burn NFT
const tokenId = '123456';
const txBuilder = factory.getBurnNftBuilder();

txBuilder.sender(testData.addresses.validAddresses[0]);
txBuilder.tokenId(tokenId);
txBuilder.nftContract(STARGATE_CONTRACT_ADDRESS_TESTNET);
txBuilder.stakingContractAddress(stakingContractAddress);
txBuilder.gas(21000);
txBuilder.nonce('64248');
txBuilder.blockRef('0x014ead140e77bbc1');
Expand All @@ -101,17 +80,17 @@ describe('Vet Burn NFT Transaction', () => {

should.equal(deserializedTx.type, TransactionType.StakingWithdraw);
should.equal(deserializedTx.tokenId, tokenId);
should.equal(deserializedTx.contract, STARGATE_CONTRACT_ADDRESS_TESTNET);
should.equal(deserializedTx.stakingContractAddress, STARGATE_CONTRACT_ADDRESS_TESTNET);
});

it('should validate the transaction data structure', async function () {
const txBuilder = factory.getBurnNftBuilder();

// Should throw error when building without required fields
await should(txBuilder.build()).be.rejectedWith('NFT contract address is required');
await should(txBuilder.build()).be.rejectedWith('Staking contract address is required');

txBuilder.sender(testData.addresses.validAddresses[0]);
txBuilder.nftContract(STARGATE_CONTRACT_ADDRESS_TESTNET);
txBuilder.stakingContractAddress(stakingContractAddress);
await should(txBuilder.build()).be.rejectedWith('Token ID is required');

// Now add the token ID and it should build successfully
Expand All @@ -123,18 +102,32 @@ describe('Vet Burn NFT Transaction', () => {
const tx = await txBuilder.build();
should.exist(tx);
});

it('should build from raw signed tx', async function () {
const txBuilder = factory.from(testData.BURN_NFT_TRANSACTION);
const tx = txBuilder.transaction as BurnNftTransaction;
const toJson = tx.toJson();
toJson.id.should.equal('0xf5e074f2d127fa3ef014873ec193b76823efab51891d43861092bd52b122563e');
toJson.stakingContractAddress?.should.equal('0x1e02b2953adefec225cf0ec49805b1146a4429c1');
toJson.nonce.should.equal('327224');
toJson.gas.should.equal(349324);
toJson.gasPriceCoef.should.equal(128);
toJson.expiration.should.equal(64);
toJson.chainTag.should.equal(39);
toJson.tokenId?.should.equal('15661');
});
});

describe('Validation', () => {
it('should fail with invalid contract address', function () {
const txBuilder = factory.getBurnNftBuilder();
should(() => txBuilder.nftContract('invalid-address')).throwError('Invalid address invalid-address');
should(() => txBuilder.stakingContractAddress('invalid-address')).throwError('Invalid address invalid-address');
});

it('should fail with invalid token ID', async function () {
const txBuilder = factory.getBurnNftBuilder();
txBuilder.sender(testData.addresses.validAddresses[0]);
txBuilder.nftContract(STARGATE_CONTRACT_ADDRESS_TESTNET);
txBuilder.stakingContractAddress(stakingContractAddress);
txBuilder.tokenId('');

await should(txBuilder.build()).be.rejectedWith('Token ID is required');
Expand Down
Loading