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
6 changes: 3 additions & 3 deletions modules/express/src/clientRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -374,9 +374,9 @@ function handleV2VerifyAddress(req: ExpressApiRouteRequest<'express.verifycoinad
* handle address canonicalization
* @param req
*/
function handleCanonicalAddress(req: express.Request) {
function handleCanonicalAddress(req: ExpressApiRouteRequest<'express.v2.canonicaladdress', 'post'>) {
const bitgo = req.bitgo;
const coin = bitgo.coin(req.params.coin);
const coin = bitgo.coin(req.decoded.coin);
if (!['ltc', 'bch', 'bsv'].includes(coin.getFamily())) {
throw new Error('only Litecoin/Bitcoin Cash/Bitcoin SV address canonicalization is supported');
}
Expand Down Expand Up @@ -1684,7 +1684,7 @@ export function setupAPIRoutes(app: express.Application, config: Config): void {
]);

// Miscellaneous
app.post('/api/v2/:coin/canonicaladdress', parseBody, prepareBitGo(config), promiseWrapper(handleCanonicalAddress));
router.post('express.v2.canonicaladdress', [prepareBitGo(config), typedPromiseWrapper(handleCanonicalAddress)]);
router.post('express.verifycoinaddress', [prepareBitGo(config), typedPromiseWrapper(handleV2VerifyAddress)]);
router.put('express.v2.pendingapprovals', [prepareBitGo(config), typedPromiseWrapper(handleV2PendingApproval)]);

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 @@ -45,6 +45,7 @@ import { PostLightningWalletPayment } from './v2/lightningPayment';
import { PostLightningWalletWithdraw } from './v2/lightningWithdraw';
import { PutV2PendingApproval } from './v2/pendingApproval';
import { PostConsolidateAccount } from './v2/consolidateAccount';
import { PostCanonicalAddress } from './v2/canonicalAddress';

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

export const ExpressV2CanonicalAddressApiSpec = apiSpec({
'express.v2.canonicaladdress': {
post: PostCanonicalAddress,
},
});

export type ExpressApi = typeof ExpressPingApiSpec &
typeof ExpressPingExpressApiSpec &
typeof ExpressLoginApiSpec &
Expand Down Expand Up @@ -316,6 +323,7 @@ export type ExpressApi = typeof ExpressPingApiSpec &
typeof ExpressCoinSigningApiSpec &
typeof ExpressExternalSigningApiSpec &
typeof ExpressWalletSigningApiSpec &
typeof ExpressV2CanonicalAddressApiSpec &
typeof ExpressWalletManagementApiSpec;

export const ExpressApi: ExpressApi = {
Expand Down Expand Up @@ -352,6 +360,7 @@ export const ExpressApi: ExpressApi = {
...ExpressCoinSigningApiSpec,
...ExpressExternalSigningApiSpec,
...ExpressWalletSigningApiSpec,
...ExpressV2CanonicalAddressApiSpec,
...ExpressWalletManagementApiSpec,
};

Expand Down
67 changes: 67 additions & 0 deletions modules/express/src/typedRoutes/api/v2/canonicalAddress.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import * as t from 'io-ts';
import { httpRoute, httpRequest, optional } from '@api-ts/io-ts-http';
import { BitgoExpressError } from '../../schemas/error';

/**
* Request parameters for canonical address conversion
*/
export const CanonicalAddressRequestParams = {
/** Coin identifier - must be ltc, bch, bsv or their testnet equivalents (tltc, tbch, tbsv) */
coin: t.string,
} as const;

/**
* Request body for canonical address conversion
*/
export const CanonicalAddressRequestBody = {
/** Address to canonicalize - can be in any supported format (base58, cashaddr, etc.) */
address: t.string,
/** Desired address format: 'base58' or 'cashaddr' (BCH/BSV: defaults to 'base58', LTC: ignored as addresses are returned unchanged) */
version: optional(t.union([t.literal('base58'), t.literal('cashaddr')])),
/** @deprecated Use version instead. Fallback parameter for version. */
scriptHashVersion: optional(t.union([t.literal('base58'), t.literal('cashaddr')])),
} as const;

/**
* Canonicalize address format
*
* Converts cryptocurrency addresses between different formats. This endpoint is specifically
* designed for coins that support multiple address formats.
*
* **Supported Coins:**
* - **Bitcoin Cash (BCH/TBCH)**: Converts between base58 and cashaddr formats
* - **Bitcoin SV (BSV/TBSV)**: Converts between base58 and cashaddr formats
* - **Litecoin (LTC/TLTC)**: Returns address unchanged (included for API consistency)
*
* **Address Formats:**
* - **base58**: Traditional Bitcoin-style addresses (e.g., '1BpEi6DfDAUFd7GtittLSdBeYJvcoaVggu', '3CWFddi6m4ndiGyKqzYvsFYagqDLPVMTzC')
* - **cashaddr**: Bitcoin Cash address format with network prefix (e.g., 'bitcoincash:qpm2qsznhks23z7629mms6s4cwef74vcwvy22gdx6a')
*
* **BCH/BSV Behavior:**
* - version='base58': Converts any input format to base58 canonical format
* - version='cashaddr': Converts any input format to cashaddr format (adds network prefix if missing)
* - Default (no version): Converts to base58 format
*
* **LTC Behavior:**
* - Returns the input address unchanged regardless of version parameter
* - Included for API consistency but performs no transformation
*
* **Response:** Returns the canonical address string directly (as JSON string primitive).
*
* @operationId express.v2.canonicaladdress
* @tag express
*/
export const PostCanonicalAddress = httpRoute({
path: '/api/v2/{coin}/canonicaladdress',
method: 'POST',
request: httpRequest({
params: CanonicalAddressRequestParams,
body: CanonicalAddressRequestBody,
}),
response: {
/** Successfully converted address. Returns the canonical address string in the requested format. */
200: t.string,
/** Error response (e.g., unsupported coin, invalid address format, invalid version parameter) */
400: BitgoExpressError,
},
});
Loading