Skip to content

Commit 7b3e1e8

Browse files
committed
fix(express): fixed type codec for prebuildSignTrans
Ticket: WP-5428
1 parent 6bf1d6f commit 7b3e1e8

File tree

2 files changed

+290
-114
lines changed

2 files changed

+290
-114
lines changed

modules/express/src/typedRoutes/api/v2/prebuildAndSignTransaction.ts

Lines changed: 117 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@ import * as t from 'io-ts';
22
import { httpRoute, httpRequest, optional } from '@api-ts/io-ts-http';
33
import { TransactionRequest as TxRequestResponse } from '@bitgo/public-types';
44
import { BitgoExpressError } from '../../schemas/error';
5+
import {
6+
FullySignedTransactionResponse,
7+
HalfSignedAccountTransactionResponse,
8+
HalfSignedUtxoTransactionResponse,
9+
SignedTransactionRequestResponse,
10+
EIP1559,
11+
} from './coinSignTx';
512

613
/**
714
* Request parameters for prebuild and sign transaction
@@ -13,57 +20,62 @@ export const PrebuildAndSignTransactionParams = {
1320
id: t.string,
1421
} as const;
1522

16-
/**
17-
* EIP1559 transaction parameters for Ethereum
18-
*/
19-
export const EIP1559 = t.partial({
20-
/** Maximum fee per gas */
21-
maxFeePerGas: t.union([t.string, t.number]),
22-
/** Maximum priority fee per gas */
23-
maxPriorityFeePerGas: t.union([t.string, t.number]),
24-
});
25-
2623
/**
2724
* Token enablement configuration
25+
* Reference: modules/sdk-core/src/bitgo/utils/tss/baseTypes.ts:35-38
2826
*/
29-
export const TokenEnablement = t.partial({
30-
/** Token name */
31-
name: t.string,
32-
/** Token address */
33-
address: t.string,
34-
});
27+
export const TokenEnablement = t.intersection([
28+
t.type({
29+
/** Token name (REQUIRED) */
30+
name: t.string,
31+
}),
32+
t.partial({
33+
/** Token address - Solana requires tokens to be enabled for specific address (OPTIONAL) */
34+
address: t.string,
35+
}),
36+
]);
3537

3638
/**
3739
* Memo information for transactions (e.g., Stellar, EOS)
40+
* Reference: modules/sdk-core/src/bitgo/wallet/iWallet.ts:57-60
41+
* Both fields are REQUIRED when memo object is present
3842
*/
39-
export const Memo = t.partial({
40-
/** Memo value */
43+
export const Memo = t.type({
44+
/** Memo value (REQUIRED) */
4145
value: t.string,
42-
/** Memo type */
46+
/** Memo type (REQUIRED) */
4347
type: t.string,
4448
});
4549

4650
/**
4751
* Recipient information for transactions
52+
* Reference: modules/sdk-core/src/bitgo/wallet/iWallet.ts:92-97
53+
*
4854
*/
49-
export const Recipient = t.partial({
50-
/** Recipient address */
51-
address: t.string,
52-
/** Amount to send */
53-
amount: t.union([t.string, t.number]),
54-
/** Token name for token transfers */
55-
tokenName: t.string,
56-
/** Token-specific data */
57-
tokenData: t.any,
58-
});
55+
export const Recipient = t.intersection([
56+
t.type({
57+
/** Recipient address (REQUIRED) */
58+
address: t.string,
59+
/** Amount to send (REQUIRED) */
60+
amount: t.union([t.string, t.number]),
61+
}),
62+
t.partial({
63+
/** Token name for token transfers (OPTIONAL) */
64+
tokenName: t.string,
65+
/** Token-specific data (OPTIONAL) */
66+
tokenData: t.any,
67+
}),
68+
]);
5969

6070
/**
61-
* Message to sign
71+
* Message to sign for transaction
72+
* Reference: modules/sdk-core/src/bitgo/wallet/iWallet.ts:125
73+
* Both fields are REQUIRED when message object is present
6274
*/
63-
export const MessageToSign = t.partial({
64-
/** Address */
75+
export const MessageToSign = t.type({
76+
/** Address (REQUIRED) */
6577
address: t.string,
66-
/** Message to sign */
78+
/** Message to sign (REQUIRED) */
6779
message: t.string,
6880
});
6981

@@ -214,40 +226,47 @@ export const FeeInfo = t.partial({
214226
});
215227

216228
/**
217-
* Consolidation details
229+
* Consolidation details for sweep/consolidation transactions
230+
* Reference: modules/sdk-core/src/bitgo/wallet/iWallet.ts:230
231+
* Note: senderAddressIndex is REQUIRED when consolidationDetails exists
218232
*/
219-
export const ConsolidationDetails = t.partial({
220-
/** Sender address index */
233+
export const ConsolidationDetails = t.type({
234+
/** Sender address index (REQUIRED) */
221235
senderAddressIndex: t.number,
222236
});
223237

224238
/**
225239
* Transaction prebuild result (for when transaction is already prebuilt)
226240
* Extends TransactionPrebuild with additional fields
241+
* Reference: modules/sdk-core/src/bitgo/wallet/iWallet.ts:227-242
227242
*/
228-
export const TransactionPrebuildResult = t.partial({
229-
// From TransactionPrebuild
230-
/** Transaction hex */
231-
txHex: t.string,
232-
/** Transaction base64 */
233-
txBase64: t.string,
234-
/** Transaction info */
235-
txInfo: t.any,
236-
/** Transaction request ID */
237-
txRequestId: t.string,
238-
/** Wallet ID */
239-
walletId: t.string,
240-
/** Consolidate ID */
241-
consolidateId: t.string,
242-
/** Consolidation details */
243-
consolidationDetails: ConsolidationDetails,
244-
/** Fee information */
245-
feeInfo: FeeInfo,
246-
/** Pending approval ID */
247-
pendingApprovalId: t.string,
248-
/** Payload string */
249-
payload: t.string,
250-
});
243+
export const TransactionPrebuildResult = t.intersection([
244+
t.type({
245+
/** Wallet ID (REQUIRED) */
246+
walletId: t.string,
247+
}),
248+
t.partial({
249+
// From TransactionPrebuild
250+
/** Transaction hex */
251+
txHex: t.string,
252+
/** Transaction base64 */
253+
txBase64: t.string,
254+
/** Transaction info */
255+
txInfo: t.any,
256+
/** Transaction request ID */
257+
txRequestId: t.string,
258+
/** Consolidate ID */
259+
consolidateId: t.string,
260+
/** Consolidation details */
261+
consolidationDetails: ConsolidationDetails,
262+
/** Fee information */
263+
feeInfo: FeeInfo,
264+
/** Pending approval ID */
265+
pendingApprovalId: t.string,
266+
/** Payload string */
267+
payload: t.string,
268+
}),
269+
]);
251270

252271
/**
253272
* Verification options for transaction verification
@@ -393,29 +412,38 @@ export const PrebuildAndSignTransactionBody = {
393412
multisigTypeVersion: optional(t.literal('MPCv2')),
394413
/** Transaction prebuild data */
395414
txPrebuild: optional(TransactionPrebuildResult),
415+
/** Private key for signing */
416+
prv: optional(t.string),
396417
/** Public keys for signing */
397418
pubs: optional(t.array(t.string)),
398419
/** Cosigner public key */
399420
cosignerPub: optional(t.string),
400-
/** Transaction verification parameters */
421+
/**
422+
* Transaction verification parameters
423+
* Reference: modules/sdk-core/src/bitgo/wallet/iWallet.ts:283-286
424+
*/
401425
verifyTxParams: optional(
402-
t.partial({
403-
/** Transaction parameters */
404-
txParams: t.partial({
405-
/** Recipients */
406-
recipients: t.array(Recipient),
407-
/** Wallet passphrase */
408-
walletPassphrase: t.string,
409-
/** Transaction type */
410-
type: t.string,
411-
/** Memo */
412-
memo: Memo,
413-
/** Tokens to enable */
414-
enableTokens: t.array(TokenEnablement),
426+
t.intersection([
427+
t.type({
428+
/** Transaction parameters (REQUIRED when verifyTxParams exists) */
429+
txParams: t.partial({
430+
/** Recipients */
431+
recipients: t.array(Recipient),
432+
/** Wallet passphrase */
433+
walletPassphrase: t.string,
434+
/** Transaction type */
435+
type: t.string,
436+
/** Memo */
437+
memo: Memo,
438+
/** Tokens to enable */
439+
enableTokens: t.array(TokenEnablement),
440+
}),
415441
}),
416-
/** Verification options */
417-
verification: VerificationOptions,
418-
})
442+
t.partial({
443+
/** Verification options (OPTIONAL) */
444+
verification: VerificationOptions,
445+
}),
446+
])
419447
),
420448
/** Pre-built transaction (string or object) - alternative to txPrebuild */
421449
prebuildTx: optional(t.union([t.string, TransactionPrebuildResult])),
@@ -424,43 +452,20 @@ export const PrebuildAndSignTransactionBody = {
424452
} as const;
425453

426454
/**
427-
* Response for a fully signed transaction
428-
*/
429-
export const FullySignedTransactionResponse = t.type({
430-
/** Transaction in hex format */
431-
txHex: t.string,
432-
});
433-
434-
/**
435-
* Response for a half-signed account transaction
436-
*/
437-
export const HalfSignedAccountTransactionResponse = t.partial({
438-
/** Half signed data */
439-
halfSigned: t.partial({
440-
/** Transaction hex */
441-
txHex: t.string,
442-
/** Payload */
443-
payload: t.string,
444-
/** Transaction base64 */
445-
txBase64: t.string,
446-
}),
447-
});
448-
449-
/**
450-
* Response for a half-signed UTXO transaction
451-
*/
452-
export const HalfSignedUtxoTransactionResponse = t.type({
453-
/** Transaction in hex format */
454-
txHex: t.string,
455-
});
456-
457-
/**
458-
* Response for a transaction request
455+
* Response codecs imported from coinSignTx for consistency.
456+
* Both endpoints call wallet.prebuildAndSignTransaction() or similar signing methods
457+
* and return the same SignedTransaction union type.
458+
*
459+
* Possible response types:
460+
* - FullySignedTransactionResponse: For hot wallets (all signatures collected)
461+
* - HalfSignedAccountTransactionResponse: For cold wallets, account-based coins (needs more signatures)
462+
* - HalfSignedUtxoTransactionResponse: For cold wallets, UTXO coins (needs more signatures)
463+
* - SignedTransactionRequestResponse: For transaction requests
464+
* - TxRequestResponse: For TSS wallets (returns transaction request ID)
465+
*
466+
* Reference: modules/express/src/typedRoutes/api/v2/coinSignTx.ts:267-418
467+
* Note: Response types are imported and re-exported at the top of this file
459468
*/
460-
export const SignedTransactionRequestResponse = t.type({
461-
/** Transaction request ID */
462-
txRequestId: t.string,
463-
});
464469

465470
/**
466471
* Combined response type for prebuild and sign transaction

0 commit comments

Comments
 (0)