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
111 changes: 111 additions & 0 deletions examples/js/eth/verify-wallet-address-sdk.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/**
* Verify that an address belongs to a wallet using the BitGo SDK.
*
* This example demonstrates using the SDK's isWalletAddress method which verifies:
* - Forwarder addresses (deposit addresses)
* - Base addresses (wallet contract addresses)
*
* Copyright 2024, BitGo, Inc. All Rights Reserved.
*/

const BitGoJS = require('bitgo');

const coin = 'hteth'; // change to 'eth' for production
const env = 'test'; // change to 'prod' for production

// TODO: set your access token here
const accessToken = '';

// TODO: set your wallet ID here
const walletId = '';

async function main() {
// Initialize BitGo SDK
const bitgo = new BitGoJS.BitGo({ env, accessToken });

console.log('Step 1: Getting wallet...');
const wallet = await bitgo.coin(coin).wallets().get({ id: walletId });

const coinSpecific = wallet.coinSpecific();
const baseAddress = coinSpecific.baseAddress;
const walletVersion = coinSpecific.walletVersion;
const feeAddress = coinSpecific.feeAddress;
const walletSalt = coinSpecific.salt;

console.log(' Base Address:', baseAddress);
console.log(' Wallet Version:', walletVersion);

console.log('Step 2: Fetching keychains...');
const keychainIds = wallet.keyIds();
const keychains = [];

for (const keychainId of keychainIds) {
const keychain = await bitgo.coin(coin).keychains().get({ id: keychainId });

// For TSS keychains, derive pub from commonKeychain (first 66 characters)
const pub = keychain.pub || (keychain.commonKeychain && keychain.commonKeychain.slice(0, 66));

if (!pub) {
throw new Error(`Unable to derive pub for keychain ${keychainId}`);
}

keychains.push({
pub: pub,
...(keychain.ethAddress && { ethAddress: keychain.ethAddress }),
...(keychain.commonKeychain && { commonKeychain: keychain.commonKeychain })
});
}
console.log(' Retrieved', keychains.length, 'keychains');

console.log('Step 3: Getting address details...');
const receiveAddress = wallet.receiveAddress();
const addressObj = await wallet.getAddress({ address: receiveAddress });

const addressIndex = addressObj.index;
const forwarderVersion = addressObj.coinSpecific?.forwarderVersion;
const forwarderSalt = addressObj.coinSpecific?.salt;

console.log(' Address:', receiveAddress);
console.log(' Index:', addressIndex);
console.log(' Forwarder Version:', forwarderVersion);

console.log('Step 4: Verifying forwarder address using SDK...');
const forwarderParams = {
address: receiveAddress,
keychains: keychains,
baseAddress: baseAddress,
walletVersion: walletVersion,
index: addressIndex,
coinSpecific: {
forwarderVersion: forwarderVersion,
salt: forwarderSalt,
feeAddress: feeAddress,
baseAddress: baseAddress
}
};

const forwarderResult = await wallet.baseCoin.isWalletAddress(forwarderParams);
console.log(' Result:', forwarderResult ? '✓ Valid' : '✗ Invalid');

console.log('Step 5: Verifying base address using SDK...');
const baseAddressParams = {
address: baseAddress,
keychains: keychains,
baseAddress: baseAddress,
walletVersion: walletVersion,
index: 0,
coinSpecific: {
salt: walletSalt,
feeAddress: feeAddress,
baseAddress: baseAddress
}
};

const baseResult = await wallet.baseCoin.isWalletAddress(baseAddressParams);
console.log(' Result:', baseResult ? '✓ Valid' : '✗ Invalid');

console.log('\n' + (forwarderResult && baseResult ? '✅ Success: Both addresses verified' : '❌ Failed: Verification failed'));
}

main().catch((e) => console.error(e));

123 changes: 123 additions & 0 deletions examples/js/eth/verify-wallet-address.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/**
* Verify that an address belongs to a wallet using the BitGo Express API.
*
* This example demonstrates the isWalletAddress endpoint which verifies:
* - Forwarder addresses (deposit addresses)
* - Base addresses (wallet contract addresses)
*
* Copyright 2024, BitGo, Inc. All Rights Reserved.
*/

const fetch = require('node-fetch');

const coin = 'hteth'; // change to 'eth' for production

// TODO: set your access token here
const accessToken = '';

// TODO: set your wallet ID here
const walletId = '';

const expressUrl = '';

// Helper function to make API requests to Express
async function apiRequest(method, endpoint, body = null) {
const response = await fetch(`${expressUrl}${endpoint}`, {
method,
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
body: body ? JSON.stringify(body) : null
});

if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${await response.text()}`);
}

return await response.json();
}

async function main() {
console.log('Step 1: Fetching wallet data...');
const wallet = await apiRequest('GET', `/api/v2/${coin}/wallet/${walletId}`);

const keychainIds = wallet.keys;
const baseAddress = wallet.coinSpecific.baseAddress;
const walletVersion = wallet.coinSpecific.walletVersion;
const feeAddress = wallet.coinSpecific.feeAddress;
const walletSalt = wallet.coinSpecific.salt;
const addressToVerify = wallet.receiveAddress.address;

console.log(' Base Address:', baseAddress);
console.log(' Wallet Version:', walletVersion);

console.log('Step 2: Fetching keychains...');
const keychains = [];
for (const keychainId of keychainIds) {
const keychain = await apiRequest('GET', `/api/v2/${coin}/key/${keychainId}`);

// For TSS keychains, derive pub from commonKeychain (first 66 characters)
const pub = keychain.pub || (keychain.commonKeychain && keychain.commonKeychain.slice(0, 66));

if (!pub) {
throw new Error(`Unable to derive pub for keychain ${keychainId}`);
}

keychains.push({
pub: pub,
...(keychain.ethAddress && { ethAddress: keychain.ethAddress }),
...(keychain.commonKeychain && { commonKeychain: keychain.commonKeychain })
});
}
console.log(' Retrieved', keychains.length, 'keychains');

console.log('Step 3: Fetching address details...');
const addressData = await apiRequest('GET', `/api/v2/${coin}/wallet/${walletId}/address/${addressToVerify}`);

const addressIndex = addressData.index;
const forwarderVersion = addressData.coinSpecific?.forwarderVersion;
const forwarderSalt = addressData.coinSpecific?.salt;

console.log(' Address:', addressToVerify);
console.log(' Index:', addressIndex);
console.log(' Forwarder Version:', forwarderVersion);

console.log('Step 4: Verifying forwarder address...');
const forwarderParams = {
address: addressToVerify,
keychains: keychains,
baseAddress: baseAddress,
walletVersion: walletVersion,
index: addressIndex,
coinSpecific: {
forwarderVersion: forwarderVersion,
salt: forwarderSalt,
feeAddress: feeAddress,
baseAddress: baseAddress
}
};

const forwarderResult = await apiRequest('POST', `/api/v2/${coin}/wallet/${walletId}/iswalletaddress`, forwarderParams);
console.log(' Result:', forwarderResult ? '✓ Valid' : '✗ Invalid');

console.log('Step 5: Verifying base address...');
const baseAddressParams = {
address: baseAddress,
keychains: keychains,
baseAddress: baseAddress,
walletVersion: walletVersion,
index: 0,
coinSpecific: {
salt: walletSalt,
feeAddress: feeAddress,
baseAddress: baseAddress
}
};

const baseResult = await apiRequest('POST', `/api/v2/${coin}/wallet/${walletId}/iswalletaddress`, baseAddressParams);
console.log(' Result:', baseResult ? '✓ Valid' : '✗ Invalid');
}

main().catch((e) => console.error(e));

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);
}

/**
* 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
Loading