88 Ecdsa ,
99 ECDSAUtils ,
1010 Environments ,
11+ InvalidAddressError ,
1112 KeyPair ,
1213 MPCAlgorithm ,
1314 MultisigType ,
@@ -17,8 +18,9 @@ import {
1718 SignedTransaction ,
1819 SigningError ,
1920 SignTransactionOptions ,
20- TssVerifyAddressOptions ,
2121 VerifyTransactionOptions ,
22+ verifyMPCWalletAddress ,
23+ UnexpectedAddressError ,
2224} from '@bitgo/sdk-core' ;
2325import { coins , NetworkType , BaseCoin as StaticsBaseCoin } from '@bitgo/statics' ;
2426import { Principal } from '@dfinity/principal' ;
@@ -41,6 +43,7 @@ import {
4143 SigningPayload ,
4244 IcpTransactionExplanation ,
4345 TransactionHexParams ,
46+ TssVerifyIcpAddressOptions ,
4447 UnsignedSweepRecoveryTransaction ,
4548} from './lib/iface' ;
4649import { TransactionBuilderFactory } from './lib/transactionBuilderFactory' ;
@@ -141,8 +144,52 @@ export class Icp extends BaseCoin {
141144 return true ;
142145 }
143146
144- async isWalletAddress ( params : TssVerifyAddressOptions ) : Promise < boolean > {
145- return this . isValidAddress ( params . address ) ;
147+ /**
148+ * Verify that an address belongs to this wallet.
149+ *
150+ * @param {TssVerifyIcpAddressOptions } params - Verification parameters
151+ * @returns {Promise<boolean> } True if address belongs to wallet
152+ * @throws {InvalidAddressError } If address format is invalid or doesn't match derived address
153+ * @throws {Error } If invalid wallet version or missing parameters
154+ */
155+ async isWalletAddress ( params : TssVerifyIcpAddressOptions ) : Promise < boolean > {
156+ const { address, rootAddress, walletVersion } = params ;
157+
158+ if ( ! this . isValidAddress ( address ) ) {
159+ throw new InvalidAddressError ( `invalid address: ${ address } ` ) ;
160+ }
161+
162+ let addressToVerify = address ;
163+ if ( walletVersion === 1 ) {
164+ if ( ! rootAddress ) {
165+ throw new Error ( 'rootAddress is required for wallet version 1' ) ;
166+ }
167+ const extractedRootAddress = utils . validateMemoAndReturnRootAddress ( address ) ;
168+ if ( ! extractedRootAddress || extractedRootAddress === address ) {
169+ throw new Error ( 'memoId is required for wallet version 1 addresses' ) ;
170+ }
171+ if ( extractedRootAddress . toLowerCase ( ) !== rootAddress . toLowerCase ( ) ) {
172+ throw new UnexpectedAddressError (
173+ `address validation failure: expected ${ rootAddress } but got ${ extractedRootAddress } `
174+ ) ;
175+ }
176+ addressToVerify = rootAddress ;
177+ }
178+
179+ const indexToVerify = walletVersion === 1 ? 0 : params . index ;
180+ const result = await verifyMPCWalletAddress (
181+ { ...params , address : addressToVerify , index : indexToVerify , keyCurve : 'secp256k1' } ,
182+ this . isValidAddress . bind ( this ) ,
183+ ( pubKey ) => utils . getAddressFromPublicKey ( pubKey )
184+ ) ;
185+
186+ if ( ! result ) {
187+ throw new UnexpectedAddressError (
188+ `address validation failure: address ${ addressToVerify } is not a wallet address`
189+ ) ;
190+ }
191+
192+ return true ;
146193 }
147194
148195 async parseTransaction ( params : ParseTransactionOptions ) : Promise < ParsedTransaction > {
@@ -210,7 +257,7 @@ export class Icp extends BaseCoin {
210257 return createHash ( 'sha256' ) ;
211258 }
212259
213- private async getAddressFromPublicKey ( hexEncodedPublicKey : string ) {
260+ private getAddressFromPublicKey ( hexEncodedPublicKey : string ) : string {
214261 return utils . getAddressFromPublicKey ( hexEncodedPublicKey ) ;
215262 }
216263
@@ -388,7 +435,7 @@ export class Icp extends BaseCoin {
388435 throw new Error ( 'failed to derive public key' ) ;
389436 }
390437
391- const senderAddress = await this . getAddressFromPublicKey ( publicKey ) ;
438+ const senderAddress = this . getAddressFromPublicKey ( publicKey ) ;
392439 const balance = await this . getAccountBalance ( publicKey ) ;
393440 const feeData = await this . getFeeData ( ) ;
394441 const actualBalance = balance . minus ( feeData ) ;
0 commit comments