Skip to content

Commit b929af2

Browse files
authored
feat(express): moved wallet signtxtss to typed route
2 parents 0eebe64 + 8ca5c81 commit b929af2

File tree

4 files changed

+870
-7
lines changed

4 files changed

+870
-7
lines changed

modules/express/src/clientRoutes.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -495,7 +495,7 @@ export async function handleV2GenerateShareTSS(req: express.Request): Promise<an
495495
}
496496
}
497497

498-
export async function handleV2SignTSSWalletTx(req: express.Request) {
498+
export async function handleV2SignTSSWalletTx(req: ExpressApiRouteRequest<'express.v2.wallet.signtxtss', 'post'>) {
499499
const bitgo = req.bitgo;
500500
const coin = bitgo.coin(req.params.coin);
501501
const wallet = await coin.wallets().get({ id: req.params.id });
@@ -1634,12 +1634,7 @@ export function setupAPIRoutes(app: express.Application, config: Config): void {
16341634
// sign transaction
16351635
router.post('express.v2.coin.signtx', [prepareBitGo(config), typedPromiseWrapper(handleV2SignTx)]);
16361636
router.post('express.v2.wallet.signtx', [prepareBitGo(config), typedPromiseWrapper(handleV2SignTxWallet)]);
1637-
app.post(
1638-
'/api/v2/:coin/wallet/:id/signtxtss',
1639-
parseBody,
1640-
prepareBitGo(config),
1641-
promiseWrapper(handleV2SignTSSWalletTx)
1642-
);
1637+
router.post('express.v2.wallet.signtxtss', [prepareBitGo(config), typedPromiseWrapper(handleV2SignTSSWalletTx)]);
16431638
router.post('express.v2.wallet.recovertoken', [prepareBitGo(config), typedPromiseWrapper(handleV2RecoverToken)]);
16441639

16451640
// send transaction

modules/express/src/typedRoutes/api/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import { PostOfcSignPayload } from './v2/ofcSignPayload';
2828
import { PostWalletRecoverToken } from './v2/walletRecoverToken';
2929
import { PostCoinSignTx } from './v2/coinSignTx';
3030
import { PostWalletSignTx } from './v2/walletSignTx';
31+
import { PostWalletTxSignTSS } from './v2/walletTxSignTSS';
3132

3233
export const ExpressApi = apiSpec({
3334
'express.ping': {
@@ -108,6 +109,9 @@ export const ExpressApi = apiSpec({
108109
'express.v2.wallet.signtx': {
109110
post: PostWalletSignTx,
110111
},
112+
'express.v2.wallet.signtxtss': {
113+
post: PostWalletTxSignTSS,
114+
},
111115
});
112116

113117
export type ExpressApi = typeof ExpressApi;
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
import * as t from 'io-ts';
2+
import { httpRoute, httpRequest, optional } from '@api-ts/io-ts-http';
3+
import { TransactionRequest as TxRequestResponse, TransactionRequestApiVersion } from '@bitgo/public-types';
4+
import { BitgoExpressError } from '../../schemas/error';
5+
6+
/**
7+
* Request path parameters for signing a TSS wallet transaction
8+
*/
9+
export const WalletTxSignTSSParams = {
10+
/** The coin type */
11+
coin: t.string,
12+
/** The wallet ID */
13+
id: t.string,
14+
} as const;
15+
16+
/**
17+
* Transaction prebuild information for TSS wallet signing
18+
*/
19+
export const WalletTxSignTSSTransactionPrebuild = t.partial({
20+
/** Transaction in hex format */
21+
txHex: t.string,
22+
/** Transaction in base64 format (for some coins) */
23+
txBase64: t.string,
24+
/** Transaction in JSON format (for some coins) */
25+
txInfo: t.any,
26+
/** Wallet ID for the transaction */
27+
walletId: t.string,
28+
/** Transaction request ID for TSS wallets */
29+
txRequestId: t.string,
30+
/** Next contract sequence ID (for ETH) */
31+
nextContractSequenceId: t.number,
32+
/** Whether this is a batch transaction (for ETH) */
33+
isBatch: t.boolean,
34+
/** EIP1559 transaction parameters (for ETH) */
35+
eip1559: t.any,
36+
/** Hop transaction data (for ETH) */
37+
hopTransaction: t.any,
38+
/** Backup key nonce (for ETH) */
39+
backupKeyNonce: t.any,
40+
/** Recipients of the transaction */
41+
recipients: t.any,
42+
});
43+
44+
/**
45+
* Request body for signing a TSS wallet transaction
46+
*/
47+
export const WalletTxSignTSSBody = {
48+
/** Transaction prebuild data */
49+
txPrebuild: optional(WalletTxSignTSSTransactionPrebuild),
50+
/** Transaction request ID for TSS wallets */
51+
txRequestId: optional(t.string),
52+
/** Wallet passphrase for TSS wallets */
53+
walletPassphrase: optional(t.string),
54+
/** Public keys for multi-signature transactions */
55+
pubs: optional(t.array(t.string)),
56+
/** Private key for signing (for non-TSS wallets, rarely used with TSS) */
57+
prv: optional(t.string),
58+
/** Cosigner public key */
59+
cosignerPub: optional(t.string),
60+
/** Whether this is the last signature in a multi-sig tx */
61+
isLastSignature: optional(t.boolean),
62+
/** API version: 'lite' or 'full' */
63+
apiVersion: optional(TransactionRequestApiVersion),
64+
/** Multisig type version */
65+
multisigTypeVersion: optional(t.literal('MPCv2')),
66+
/** Gas limit for ETH transactions */
67+
gasLimit: optional(t.union([t.string, t.number])),
68+
/** Gas price for ETH transactions */
69+
gasPrice: optional(t.union([t.string, t.number])),
70+
/** Transaction expiration time */
71+
expireTime: optional(t.number),
72+
/** Sequence ID for transactions */
73+
sequenceId: optional(t.union([t.string, t.number])),
74+
/** Recipients of the transaction */
75+
recipients: optional(t.any),
76+
/** Custodian transaction ID */
77+
custodianTransactionId: optional(t.string),
78+
/** Signing step for MuSig2 */
79+
signingStep: optional(t.union([t.literal('signerNonce'), t.literal('signerSignature'), t.literal('cosignerNonce')])),
80+
/** Allow non-segwit signing without previous transaction */
81+
allowNonSegwitSigningWithoutPrevTx: optional(t.boolean),
82+
/** For EVM cross-chain recovery */
83+
isEvmBasedCrossChainRecovery: optional(t.boolean),
84+
/** Derivation seed for key derivation */
85+
derivationSeed: optional(t.string),
86+
} as const;
87+
88+
/**
89+
* Response for a fully signed transaction
90+
*/
91+
export const FullySignedTransactionResponse = t.type({
92+
/** Transaction in hex format */
93+
txHex: t.string,
94+
});
95+
96+
/**
97+
* Response for a half-signed account transaction
98+
*/
99+
export const HalfSignedAccountTransactionResponse = t.type({
100+
halfSigned: t.partial({
101+
txHex: optional(t.string),
102+
payload: optional(t.string),
103+
txBase64: optional(t.string),
104+
}),
105+
});
106+
107+
/**
108+
* Response for a half-signed UTXO transaction
109+
*/
110+
export const HalfSignedUtxoTransactionResponse = t.type({
111+
txHex: t.string,
112+
});
113+
114+
/**
115+
* Response for a transaction request
116+
*/
117+
export const SignedTransactionRequestResponse = t.type({
118+
txRequestId: t.string,
119+
});
120+
121+
/**
122+
* Response for signing a TSS wallet transaction
123+
*
124+
* Uses TxRequestResponse (TransactionRequest) from @bitgo/public-types for TSS transaction requests
125+
* (supports both Lite and Full versions)
126+
*/
127+
export const WalletTxSignTSSResponse = {
128+
/** Successfully signed transaction */
129+
200: t.union([
130+
FullySignedTransactionResponse,
131+
HalfSignedAccountTransactionResponse,
132+
HalfSignedUtxoTransactionResponse,
133+
SignedTransactionRequestResponse,
134+
TxRequestResponse,
135+
]),
136+
/** Error response */
137+
400: BitgoExpressError,
138+
};
139+
140+
/**
141+
* Sign a transaction for a specific TSS wallet
142+
*
143+
* This endpoint signs a transaction for a specific TSS wallet identified by coin type and wallet ID.
144+
* The request body is passed to wallet.signTransaction() and varies by coin and wallet type.
145+
*
146+
* Common fields for TSS wallets include:
147+
* - txPrebuild: Contains transaction data like txHex, txBase64, or txRequestId
148+
* - walletPassphrase: Passphrase for TSS wallets (required for TSS signing)
149+
* - txRequestId: Transaction request ID for TSS wallets (can be in body or txPrebuild)
150+
* - apiVersion: 'lite' or 'full' for TSS transaction requests
151+
* - multisigTypeVersion: 'MPCv2' for MPCv2 wallets
152+
* - isLastSignature: Whether this is the last signature in a multi-sig tx
153+
* - pubs: Public keys for multi-signature transactions
154+
* - gasLimit: Gas limit for ETH transactions
155+
* - gasPrice: Gas price for ETH transactions
156+
* - expireTime: Transaction expiration time
157+
* - sequenceId: Sequence ID for transactions
158+
* - isEvmBasedCrossChainRecovery: For EVM cross-chain recovery
159+
*
160+
* @operationId express.v2.wallet.signtxtss
161+
*/
162+
export const PostWalletTxSignTSS = httpRoute({
163+
path: '/api/v2/:coin/wallet/:id/signtxtss',
164+
method: 'POST',
165+
request: httpRequest({
166+
params: WalletTxSignTSSParams,
167+
body: WalletTxSignTSSBody,
168+
}),
169+
response: WalletTxSignTSSResponse,
170+
});

0 commit comments

Comments
 (0)