88 Ecdsa ,
99 ECDSAUtils ,
1010 Environments ,
11+ InvalidAddressError ,
1112 KeyPair ,
1213 MPCAlgorithm ,
1314 MultisigType ,
@@ -17,8 +18,8 @@ import {
1718 SignedTransaction ,
1819 SigningError ,
1920 SignTransactionOptions ,
20- TssVerifyAddressOptions ,
2121 VerifyTransactionOptions ,
22+ verifyMPCWalletAddress ,
2223} from '@bitgo/sdk-core' ;
2324import { coins , NetworkType , BaseCoin as StaticsBaseCoin } from '@bitgo/statics' ;
2425import { Principal } from '@dfinity/principal' ;
@@ -41,6 +42,7 @@ import {
4142 SigningPayload ,
4243 IcpTransactionExplanation ,
4344 TransactionHexParams ,
45+ TssVerifyIcpAddressOptions ,
4446 UnsignedSweepRecoveryTransaction ,
4547} from './lib/iface' ;
4648import { TransactionBuilderFactory } from './lib/transactionBuilderFactory' ;
@@ -141,8 +143,81 @@ export class Icp extends BaseCoin {
141143 return true ;
142144 }
143145
144- async isWalletAddress ( params : TssVerifyAddressOptions ) : Promise < boolean > {
145- return this . isValidAddress ( params . address ) ;
146+ /**
147+ * Verify that an address belongs to this wallet.
148+ *
149+ * @param {TssVerifyIcpAddressOptions } params - Verification parameters
150+ * @returns {Promise<boolean> } True if address belongs to wallet
151+ * @throws {InvalidAddressError } If address format is invalid
152+ * @throws {Error } If invalid wallet version or missing parameters
153+ */
154+ async isWalletAddress ( params : TssVerifyIcpAddressOptions ) : Promise < boolean > {
155+ const { address, rootAddress, walletVersion } = params ;
156+
157+ if ( ! this . isValidAddress ( address ) ) {
158+ throw new InvalidAddressError ( `invalid address: ${ address } ` ) ;
159+ }
160+
161+ if ( walletVersion === 1 ) {
162+ return this . verifyMemoBasedAddress ( address , rootAddress ) ;
163+ }
164+
165+ return this . verifyKeyDerivedAddress ( params , address , rootAddress ) ;
166+ }
167+
168+ /**
169+ * Verifies a memo-based address for wallet version 1.
170+ *
171+ * @param {string } address - The full address to verify (must include memoId)
172+ * @param {string | undefined } rootAddress - The wallet's root address
173+ * @returns {boolean } True if the address is valid
174+ * @throws {Error } If rootAddress is missing or memoId is missing
175+ */
176+ private verifyMemoBasedAddress ( address : string , rootAddress : string | undefined ) : boolean {
177+ if ( ! rootAddress ) {
178+ throw new Error ( 'rootAddress is required for wallet version 1' ) ;
179+ }
180+ const extractedRootAddress = utils . validateMemoAndReturnRootAddress ( address ) ;
181+ if ( extractedRootAddress === address ) {
182+ throw new Error ( 'memoId is required for wallet version 1 addresses' ) ;
183+ }
184+
185+ return extractedRootAddress ?. toLowerCase ( ) === rootAddress . toLowerCase ( ) ;
186+ }
187+
188+ /**
189+ * Verifies a key-derived address using MPC wallet verification.
190+ *
191+ * @param {TssVerifyIcpAddressOptions } params - Verification parameters
192+ * @param {string } address - The full address to verify
193+ * @param {string | undefined } rootAddress - The wallet's root address
194+ * @returns {Promise<boolean> } True if the address matches the derived address
195+ * @throws {Error } If keychains are missing or address doesn't match
196+ */
197+ private async verifyKeyDerivedAddress (
198+ params : TssVerifyIcpAddressOptions ,
199+ address : string ,
200+ rootAddress : string | undefined
201+ ) : Promise < boolean > {
202+ const { index } = params ;
203+ const parsedIndex = typeof index === 'string' ? parseInt ( index , 10 ) : index ;
204+
205+ const isVerifyingRootAddress = rootAddress && address . toLowerCase ( ) === rootAddress . toLowerCase ( ) ;
206+ if ( isVerifyingRootAddress && parsedIndex !== 0 ) {
207+ throw new Error ( `Root address verification requires index 0, but got index ${ index } ` ) ;
208+ }
209+
210+ const result = await verifyMPCWalletAddress (
211+ { ...params , keyCurve : 'secp256k1' } ,
212+ this . isValidAddress . bind ( this ) ,
213+ ( pubKey ) => utils . getAddressFromPublicKey ( pubKey )
214+ ) ;
215+
216+ if ( ! result ) {
217+ throw new InvalidAddressError ( `invalid address: ${ address } ` ) ;
218+ }
219+
220+ return true ;
146221 }
147222
148223 async parseTransaction ( params : ParseTransactionOptions ) : Promise < ParsedTransaction > {
@@ -210,7 +285,7 @@ export class Icp extends BaseCoin {
210285 return createHash ( 'sha256' ) ;
211286 }
212287
213- private async getAddressFromPublicKey ( hexEncodedPublicKey : string ) {
288+ private getAddressFromPublicKey ( hexEncodedPublicKey : string ) : string {
214289 return utils . getAddressFromPublicKey ( hexEncodedPublicKey ) ;
215290 }
216291
@@ -388,7 +463,7 @@ export class Icp extends BaseCoin {
388463 throw new Error ( 'failed to derive public key' ) ;
389464 }
390465
391- const senderAddress = await this . getAddressFromPublicKey ( publicKey ) ;
466+ const senderAddress = this . getAddressFromPublicKey ( publicKey ) ;
392467 const balance = await this . getAccountBalance ( publicKey ) ;
393468 const feeData = await this . getFeeData ( ) ;
394469 const actualBalance = balance . minus ( feeData ) ;
0 commit comments