Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
17 changes: 17 additions & 0 deletions modules/express/src/clientRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,19 @@ export async function handleV2CreateAddress(req: ExpressApiRouteRequest<'express
return wallet.createAddress(req.decoded);
}

/**
* handle v2 isWalletAddress - verify if an address belongs to a wallet
* @param req
*/
export async function handleV2IsWalletAddress(
req: ExpressApiRouteRequest<'express.v2.wallet.isWalletAddress', 'post'>
) {
const bitgo = req.bitgo;
const coin = bitgo.coin(req.decoded.coin);
const wallet = await coin.wallets().get({ id: req.decoded.id });
return await wallet.baseCoin.isWalletAddress(req.decoded as any);
Comment thread
danielzhao122 marked this conversation as resolved.
}

/**
* handle v2 approve transaction
* @param req
Expand Down Expand Up @@ -1626,6 +1639,10 @@ export function setupAPIRoutes(app: express.Application, config: Config): void {
]);

router.post('express.v2.wallet.createAddress', [prepareBitGo(config), typedPromiseWrapper(handleV2CreateAddress)]);
router.post('express.v2.wallet.isWalletAddress', [
prepareBitGo(config),
typedPromiseWrapper(handleV2IsWalletAddress),
]);

router.post('express.v2.wallet.share', [prepareBitGo(config), typedPromiseWrapper(handleV2ShareWallet)]);
app.post(
Expand Down
9 changes: 9 additions & 0 deletions modules/express/src/typedRoutes/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import { PutV2PendingApproval } from './v2/pendingApproval';
import { PostConsolidateAccount } from './v2/consolidateAccount';
import { PostCanonicalAddress } from './v2/canonicalAddress';
import { PostWalletSweep } from './v2/walletSweep';
import { PostIsWalletAddress } from './v2/isWalletAddress';

// Too large types can cause the following error
//
Expand Down Expand Up @@ -180,6 +181,12 @@ export const ExpressV2WalletCreateAddressApiSpec = apiSpec({
},
});

export const ExpressV2WalletIsWalletAddressApiSpec = apiSpec({
'express.v2.wallet.isWalletAddress': {
post: PostIsWalletAddress,
},
});

export const ExpressV2WalletSendManyApiSpec = apiSpec({
'express.v2.wallet.sendmany': {
post: PostSendMany,
Expand Down Expand Up @@ -316,6 +323,7 @@ export type ExpressApi = typeof ExpressPingApiSpec &
typeof ExpressV2WalletConsolidateAccountApiSpec &
typeof ExpressWalletFanoutUnspentsApiSpec &
typeof ExpressV2WalletCreateAddressApiSpec &
typeof ExpressV2WalletIsWalletAddressApiSpec &
typeof ExpressKeychainLocalApiSpec &
typeof ExpressKeychainChangePasswordApiSpec &
typeof ExpressLightningWalletPaymentApiSpec &
Expand Down Expand Up @@ -354,6 +362,7 @@ export const ExpressApi: ExpressApi = {
...ExpressWalletFanoutUnspentsApiSpec,
...ExpressV2WalletCreateAddressApiSpec,
...ExpressV2WalletConsolidateAccountApiSpec,
...ExpressV2WalletIsWalletAddressApiSpec,
...ExpressKeychainLocalApiSpec,
...ExpressKeychainChangePasswordApiSpec,
...ExpressLightningWalletPaymentApiSpec,
Expand Down
102 changes: 102 additions & 0 deletions modules/express/src/typedRoutes/api/v2/isWalletAddress.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import * as t from 'io-ts';
import { httpRoute, httpRequest, optional } from '@api-ts/io-ts-http';
import { BitgoExpressError } from '../../schemas/error';

/**
* Path parameters for verifying if an address belongs to a wallet
*/
export const IsWalletAddressParams = {
/** Coin ticker / chain identifier */
coin: t.string,
/** The ID of the wallet */
id: t.string,
} as const;

/**
* Keychain codec for address verification
* Supports both BIP32 wallets (need ethAddress) and TSS/MPC wallets (need commonKeychain)
*/
export const KeychainCodec = t.intersection([
// Required field
t.type({
pub: t.string,
}),
// Optional fields for different wallet types
t.partial({
/** Ethereum address (required for BIP32 wallet base address verification: V1, V2, V4) */
ethAddress: t.string,
/** Common keychain (required for TSS/MPC wallets: V3, V5, V6) */
commonKeychain: t.string,
}),
]);

/**
* Request body for verifying if an address belongs to a wallet
*/
export const IsWalletAddressBody = {
/** The address to verify */
address: t.string,
/** Keychains for verification */
keychains: t.array(KeychainCodec),
/** Base address of the wallet */
baseAddress: optional(t.string),
/** Wallet version */
walletVersion: optional(t.number),
/** Address index for TSS/MPC wallet derivation */
index: optional(t.union([t.number, t.string])),
/** Coin-specific address data */
coinSpecific: optional(
t.partial({
/** Forwarder version */
forwarderVersion: t.number,
/** Salt for CREATE2 address derivation */
salt: t.string,
/** Fee address for v4 forwarders */
feeAddress: t.string,
/** Base address (alternative to top-level baseAddress) */
baseAddress: t.string,
})
),
/** Implied forwarder version */
impliedForwarderVersion: optional(t.number),
/** Format for the address */
format: optional(t.string),
/** Root address for coins that use root address */
rootAddress: optional(t.string),
Comment thread
danielzhao122 marked this conversation as resolved.
Comment thread
danielzhao122 marked this conversation as resolved.
} as const;

/**
* Response for verifying if an address belongs to a wallet
*/
export const IsWalletAddressResponse = {
200: t.boolean,
400: BitgoExpressError,
} as const;

/**
* Verify if an address belongs to a wallet
*
* This endpoint verifies whether a given address belongs to the specified wallet.
* It performs cryptographic verification, checking address derivation
* against wallet keychains and configuration.
*
* Returns `true` if the address belongs to the wallet, `false` otherwise.
* Throws an error if verification fails or parameters are invalid.
*
* To verify a baseAddress, set the `baseAddress` and `address` to the base address of the wallet.
*
* Due to architecture limitations, forwarder version 0 addresses cannot be verified and will return `true` without verification.
* Verifying custodial wallet addresses is not supported.
*
* @operationId express.v2.wallet.isWalletAddress
* @tag Express
*/
export const PostIsWalletAddress = httpRoute({
path: '/api/v2/{coin}/wallet/{id}/iswalletaddress',
method: 'POST',
request: httpRequest({
params: IsWalletAddressParams,
body: IsWalletAddressBody,
}),
response: IsWalletAddressResponse,
});
Loading