From 791c320e66c3fb5121a4402e82cd5eb3910e1fc8 Mon Sep 17 00:00:00 2001 From: danielzhao122 Date: Tue, 2 Dec 2025 09:53:47 -0500 Subject: [PATCH 1/2] feat: address verification for vet TICKET: WP-7050 --- modules/sdk-coin-vet/src/vet.ts | 55 ++++++++++++++-- modules/sdk-coin-vet/test/unit/vet.ts | 95 +++++++++++++++++++++++++++ 2 files changed, 146 insertions(+), 4 deletions(-) diff --git a/modules/sdk-coin-vet/src/vet.ts b/modules/sdk-coin-vet/src/vet.ts index 2c57c644a2..d20fcfb7d6 100644 --- a/modules/sdk-coin-vet/src/vet.ts +++ b/modules/sdk-coin-vet/src/vet.ts @@ -20,14 +20,17 @@ import { SignedTransaction, SignTransactionOptions, TokenTransferRecipientParams, + TssVerifyAddressOptions, VerifyAddressOptions, VerifyTransactionOptions, + isTssVerifyAddressOptions, TokenType, Ecdsa, ECDSAUtils, Environments, BaseBroadcastTransactionOptions, BaseBroadcastTransactionResult, + verifyMPCWalletAddress, } from '@bitgo/sdk-core'; import * as mpc from '@bitgo/sdk-lib-mpc'; import { BaseCoin as StaticsBaseCoin, coins } from '@bitgo/statics'; @@ -53,6 +56,12 @@ interface FeeEstimateData { coefDivisor: string; } +export interface TssVerifyVetAddressOptions extends TssVerifyAddressOptions { + address: string; + baseAddress?: string; + walletVersion?: number; +} + /** * Full Name: Vechain * Docs: https://docs.vechain.org/ @@ -153,12 +162,50 @@ export class Vet extends BaseCoin { return true; } - async isWalletAddress(params: VerifyAddressOptions): Promise { - const { address: newAddress } = params; + /** + * Verify that an address belongs to this wallet. + * + * @param {VerifyAddressOptions | TssVerifyAddressOptions} params - Verification parameters + * @returns {Promise} True if address belongs to wallet + * @throws {InvalidAddressError} If address format is invalid + * @throws {Error} If invalid wallet version or missing parameters + */ + async isWalletAddress(params: VerifyAddressOptions | TssVerifyAddressOptions): Promise { + const { address, baseAddress, walletVersion } = params as TssVerifyVetAddressOptions; + + if (address && !this.isValidAddress(address)) { + throw new InvalidAddressError(`invalid address: ${address}`); + } + + if (walletVersion !== 6) { + throw new Error(`VET only supports wallet version 6, but got version ${walletVersion}`); + } + + if (!isTssVerifyAddressOptions(params)) { + throw new Error('VET requires TSS verification parameters (keychains with commonKeychain)'); + } - if (!this.isValidAddress(newAddress)) { - throw new InvalidAddressError(`invalid address: ${newAddress}`); + const isVerifyingBaseAddress = baseAddress && address.toLowerCase() === baseAddress.toLowerCase(); + if (isVerifyingBaseAddress) { + const index = typeof params.index === 'string' ? parseInt(params.index, 10) : params.index; + if (index !== 0) { + throw new Error(`Base address verification requires index 0, but got index ${params.index}.`); + } } + + const result = await verifyMPCWalletAddress( + { ...params, keyCurve: 'secp256k1' }, + this.isValidAddress.bind(this), + (pubKey) => { + const keyPair = new EthKeyPair({ pub: pubKey }); + return keyPair.getAddress(); + } + ); + + if (!result) { + throw new InvalidAddressError(`invalid address: ${address}`); + } + return true; } diff --git a/modules/sdk-coin-vet/test/unit/vet.ts b/modules/sdk-coin-vet/test/unit/vet.ts index 9385d7f449..6b804cb3f7 100644 --- a/modules/sdk-coin-vet/test/unit/vet.ts +++ b/modules/sdk-coin-vet/test/unit/vet.ts @@ -238,4 +238,99 @@ describe('Vechain', function () { basecoin.isValidAddress(address).should.equal(false); }); }); + + describe('address verification', () => { + const testData = { + walletVersion: 6, + commonKeychain: + '02fad451e7d1a536897ded5f803a73cc7309a403cf43bb25bda494b9800efe32999b6b71b6159eccf1244492556b733f68733a23c184bf40bc37c52da5ad29e575', + baseAddress: '0x8420429aa50b7f6ab196c4ce0dcf629fdbb821a1', + feeAddress: '0x33f9d3172de8a88e47b399562e51fa9cde0f2511', + depositAddress: '0x26b88c0a103d185792a9597580c2c5170d93f410', + depositIndex: 3, + forwarderVersion: 5, + }; + + it('should verify a valid TSS base address (wallet version 6)', async function () { + const params = { + address: testData.baseAddress, + baseAddress: testData.baseAddress, + keychains: [ + { commonKeychain: testData.commonKeychain }, + { commonKeychain: testData.commonKeychain }, + { commonKeychain: testData.commonKeychain }, + ], + index: 0, + walletVersion: testData.walletVersion, + coinSpecific: { + feeAddress: testData.feeAddress, + forwarderVersion: testData.forwarderVersion, + }, + }; + + const result = await basecoin.isWalletAddress(params); + result.should.equal(true); + }); + + it('should fail to verify a invalid TSS base address (wallet version 6)', async function () { + const params = { + address: '0x8420429aa50b7f6ab196c4ce0dcf629fdbb821a0', + baseAddress: testData.baseAddress, + keychains: [ + { commonKeychain: testData.commonKeychain }, + { commonKeychain: testData.commonKeychain }, + { commonKeychain: testData.commonKeychain }, + ], + index: 0, + walletVersion: testData.walletVersion, + coinSpecific: { + feeAddress: testData.feeAddress, + forwarderVersion: testData.forwarderVersion, + }, + }; + + await basecoin.isWalletAddress(params).should.be.rejected(); + }); + + it('should verify a valid TSS deposit address (wallet version 6)', async function () { + const params = { + address: testData.depositAddress, + baseAddress: testData.baseAddress, + keychains: [ + { commonKeychain: testData.commonKeychain }, + { commonKeychain: testData.commonKeychain }, + { commonKeychain: testData.commonKeychain }, + ], + index: testData.depositIndex, + walletVersion: testData.walletVersion, + coinSpecific: { + feeAddress: testData.feeAddress, + forwarderVersion: testData.forwarderVersion, + }, + }; + + const result = await basecoin.isWalletAddress(params); + result.should.equal(true); + }); + + it('should fail to verify a invalid TSS deposit address (wallet version 6)', async function () { + const params = { + address: '0x26b88c0a103d185792a9597580c2c5170d93f411', + baseAddress: testData.baseAddress, + keychains: [ + { commonKeychain: testData.commonKeychain }, + { commonKeychain: testData.commonKeychain }, + { commonKeychain: testData.commonKeychain }, + ], + index: testData.depositIndex, + walletVersion: testData.walletVersion, + coinSpecific: { + feeAddress: testData.feeAddress, + forwarderVersion: testData.forwarderVersion, + }, + }; + + await basecoin.isWalletAddress(params).should.be.rejected(); + }); + }); }); From 3638dd5345ce1fc715b5ff1a735d8c2bfd3b7a73 Mon Sep 17 00:00:00 2001 From: danielzhao122 Date: Tue, 2 Dec 2025 11:46:16 -0500 Subject: [PATCH 2/2] refactor: consolidate to TssVerifyVetAddressOptions TICKET: WP-7050 --- modules/sdk-coin-vet/src/vet.ts | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/modules/sdk-coin-vet/src/vet.ts b/modules/sdk-coin-vet/src/vet.ts index d20fcfb7d6..814e7edfb6 100644 --- a/modules/sdk-coin-vet/src/vet.ts +++ b/modules/sdk-coin-vet/src/vet.ts @@ -21,9 +21,7 @@ import { SignTransactionOptions, TokenTransferRecipientParams, TssVerifyAddressOptions, - VerifyAddressOptions, VerifyTransactionOptions, - isTssVerifyAddressOptions, TokenType, Ecdsa, ECDSAUtils, @@ -165,13 +163,13 @@ export class Vet extends BaseCoin { /** * Verify that an address belongs to this wallet. * - * @param {VerifyAddressOptions | TssVerifyAddressOptions} params - Verification parameters + * @param {TssVerifyVetAddressOptions} params - Verification parameters * @returns {Promise} True if address belongs to wallet * @throws {InvalidAddressError} If address format is invalid * @throws {Error} If invalid wallet version or missing parameters */ - async isWalletAddress(params: VerifyAddressOptions | TssVerifyAddressOptions): Promise { - const { address, baseAddress, walletVersion } = params as TssVerifyVetAddressOptions; + async isWalletAddress(params: TssVerifyVetAddressOptions): Promise { + const { address, baseAddress, walletVersion } = params; if (address && !this.isValidAddress(address)) { throw new InvalidAddressError(`invalid address: ${address}`); @@ -181,10 +179,6 @@ export class Vet extends BaseCoin { throw new Error(`VET only supports wallet version 6, but got version ${walletVersion}`); } - if (!isTssVerifyAddressOptions(params)) { - throw new Error('VET requires TSS verification parameters (keychains with commonKeychain)'); - } - const isVerifyingBaseAddress = baseAddress && address.toLowerCase() === baseAddress.toLowerCase(); if (isVerifyingBaseAddress) { const index = typeof params.index === 'string' ? parseInt(params.index, 10) : params.index;