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
22 changes: 9 additions & 13 deletions src/masterBitgoExpress/generateWallet.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {
GenerateWalletOptions,
promiseProps,
RequestTracer,
SupplementGenerateWalletOptions,
Expand All @@ -11,12 +10,14 @@ import {
} from '@bitgo/sdk-core';
import { createEnclavedExpressClient } from './enclavedExpressClient';
import _ from 'lodash';
import { BitGoRequest } from '../types/request';
import { MasterApiSpecRouteRequest } from './routers/masterApiSpec';

/**
* This route is used to generate a multisig wallet when enclaved express is enabled
*/
export async function handleGenerateWalletOnPrem(req: BitGoRequest) {
export async function handleGenerateWalletOnPrem(
req: MasterApiSpecRouteRequest<'v1.wallet.generate', 'post'>,
) {
const bitgo = req.bitgo;
const baseCoin = bitgo.coin(req.params.coin);

Expand All @@ -27,19 +28,14 @@ export async function handleGenerateWalletOnPrem(req: BitGoRequest) {
);
}

const params = req.body as GenerateWalletOptions;
const reqId = new RequestTracer();

// Assign the default multiSig type value based on the coin
if (!params.multisigType) {
params.multisigType = baseCoin.getDefaultMultisigType();
if (!req.decoded.multisigType) {
req.decoded.multisigType = baseCoin.getDefaultMultisigType();
}

if (typeof params.label !== 'string') {
throw new Error('missing required string parameter label');
}

const { label, enterprise } = params;
const { label, enterprise } = req.decoded;

// Create wallet parameters with type assertion to allow 'onprem' subtype
const walletParams = {
Expand Down Expand Up @@ -97,9 +93,9 @@ export async function handleGenerateWalletOnPrem(req: BitGoRequest) {
userKeychain: userKeychainPromise(),
backupKeychain: backupKeychainPromise(),
bitgoKeychain: baseCoin.keychains().createBitGo({
enterprise: params.enterprise,
enterprise: req.decoded.enterprise,
reqId,
isDistributedCustody: params.isDistributedCustody,
isDistributedCustody: req.decoded.isDistributedCustody,
}),
});

Expand Down
37 changes: 29 additions & 8 deletions src/masterBitgoExpress/routers/masterApiSpec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
import * as t from 'io-ts';
import { apiSpec, httpRoute, httpRequest, HttpResponse } from '@api-ts/io-ts-http';
import { createRouter, type WrappedRouter } from '@api-ts/typed-express-router';
import {
apiSpec,
httpRoute,
httpRequest,
HttpResponse,
Method as HttpMethod,
} from '@api-ts/io-ts-http';
import {
createRouter,
TypedRequestHandler,
type WrappedRouter,
} from '@api-ts/typed-express-router';
import { Response } from '@api-ts/response';
import express, { Request } from 'express';
import express from 'express';
import { BitGo } from 'bitgo';
import { BitGoRequest, isBitGoRequest } from '../../types/request';
import { BitGoRequest } from '../../types/request';
import { MasterExpressConfig } from '../../config';
import { handleGenerateWalletOnPrem } from '../generateWallet';
import { withResponseHandler } from '../../shared/responseHandler';
Expand Down Expand Up @@ -86,6 +96,20 @@ export const MasterApiSpec = apiSpec({
},
});

export type MasterApiSpec = typeof MasterApiSpec;

export type MasterApiSpecRouteHandler<
ApiName extends keyof MasterApiSpec,
Method extends keyof MasterApiSpec[ApiName] & HttpMethod,
> = TypedRequestHandler<MasterApiSpec, ApiName, Method>;

export type MasterApiSpecRouteRequest<
ApiName extends keyof MasterApiSpec,
Method extends keyof MasterApiSpec[ApiName] & HttpMethod,
> = BitGoRequest & Parameters<MasterApiSpecRouteHandler<ApiName, Method>>[0];

export type GenericMasterApiSpecRouteRequest = MasterApiSpecRouteRequest<any, any>;

// Create router with handlers
export function createMasterApiRouter(
cfg: MasterExpressConfig,
Expand All @@ -98,10 +122,7 @@ export function createMasterApiRouter(

// Generate wallet endpoint handler
router.post('v1.wallet.generate', [
withResponseHandler(async (req: BitGoRequest | Request) => {
if (!isBitGoRequest(req)) {
throw new Error('Invalid request type');
}
withResponseHandler(async (req: GenericMasterApiSpecRouteRequest) => {
const result = await handleGenerateWalletOnPrem(req);
return Response.ok(result);
}),
Expand Down
11 changes: 7 additions & 4 deletions src/shared/responseHandler.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { BitGoRequest } from '../types/request';
import { Request, Response as ExpressResponse, NextFunction } from 'express';
import {
GenericMasterApiSpecRouteRequest,
MasterApiSpecRouteRequest,
} from '../masterBitgoExpress/routers/masterApiSpec';

// Extend Express Response to include sendEncoded
interface EncodedResponse extends ExpressResponse {
Expand All @@ -13,7 +16,7 @@ interface ApiResponse {
}

type ServiceFunction = (
req: Request | BitGoRequest,
req: MasterApiSpecRouteRequest<any, any>,
res: EncodedResponse,
next: NextFunction,
) => Promise<ApiResponse> | ApiResponse;
Expand All @@ -24,9 +27,9 @@ type ServiceFunction = (
* @returns Express middleware function that handles the response encoding
*/
export function withResponseHandler(fn: ServiceFunction) {
return async (req: Request | BitGoRequest, res: EncodedResponse, next: NextFunction) => {
return async (req: Request, res: EncodedResponse, next: NextFunction) => {
try {
const result = await Promise.resolve(fn(req, res, next));
const result = await fn(req as unknown as GenericMasterApiSpecRouteRequest, res, next);
Comment on lines +30 to +32
Copy link

Copilot AI Jun 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Casting via unknown is a workaround; consider updating the middleware’s req type directly to GenericMasterApiSpecRouteRequest so you can call fn(req, ...) without an intermediate cast.

Copilot uses AI. Check for mistakes.
return res.sendEncoded(result.type, result.payload);
} catch (error) {
// Log the error
Expand Down