Skip to content

Commit a604c23

Browse files
authored
fix(express): fixed type codec for coinSignTx
2 parents 465498d + 425d6ae commit a604c23

File tree

2 files changed

+261
-24
lines changed

2 files changed

+261
-24
lines changed

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

Lines changed: 154 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,42 +11,115 @@ export const CoinSignTxParams = {
1111
coin: t.string,
1212
} as const;
1313

14+
/**
15+
* EIP1559 transaction parameters for Ethereum
16+
* Reference: modules/abstract-eth/src/abstractEthLikeNewCoins.ts:1106
17+
*/
18+
export const EIP1559 = t.partial({
19+
/** Maximum fee per gas */
20+
maxFeePerGas: t.union([t.string, t.number]),
21+
/** Maximum priority fee per gas */
22+
maxPriorityFeePerGas: t.union([t.string, t.number]),
23+
});
24+
25+
/**
26+
* Recipient information for a transaction
27+
* Reference: modules/abstract-eth/src/abstractEthLikeNewCoins.ts:1100-1102
28+
*/
29+
export const Recipient = t.partial({
30+
/** Recipient address */
31+
address: t.string,
32+
/** Amount to send */
33+
amount: t.union([t.string, t.number]),
34+
/** Token name (for token transfers) */
35+
tokenName: t.string,
36+
/** Additional data */
37+
data: t.string,
38+
});
39+
40+
/**
41+
* Hop transaction data for Ethereum
42+
* Reference: modules/abstract-eth/src/abstractEthLikeNewCoins.ts:1110
43+
*/
44+
export const HopTransaction = t.partial({
45+
/** Transaction hex */
46+
txHex: t.string,
47+
/** User request signature */
48+
userReqSig: t.string,
49+
/** Maximum gas price */
50+
gasPriceMax: t.union([t.string, t.number]),
51+
/** Gas limit */
52+
gasLimit: t.union([t.string, t.number]),
53+
});
54+
55+
/**
56+
* Half-signed transaction data
57+
*/
58+
export const HalfSignedData = t.partial({
59+
/** Transaction hash */
60+
txHash: t.string,
61+
/** Transaction payload */
62+
payload: t.string,
63+
/** Transaction in base64 format */
64+
txBase64: t.string,
65+
});
66+
1467
/**
1568
* Transaction prebuild information
69+
* Reference: modules/abstract-utxo/src/abstractUtxoCoin.ts:336-346
70+
* Reference: modules/abstract-eth/src/abstractEthLikeNewCoins.ts:1088-1116
1671
*/
1772
export const TransactionPrebuild = t.partial({
1873
/** Transaction in hex format */
1974
txHex: t.string,
20-
/** Transaction in base64 format (for some coins) */
75+
/** Transaction in base64 format (for some coins like Solana) */
2176
txBase64: t.string,
22-
/** Transaction in JSON format (for some coins) */
77+
/** Transaction info with unspents (for UTXO coins) - coin-specific structure, varies by coin type */
2378
txInfo: t.any,
2479
/** Wallet ID for the transaction */
2580
walletId: t.string,
81+
/** Transaction request ID (for TSS transactions) */
82+
txRequestId: t.string,
83+
/** Consolidate ID */
84+
consolidateId: t.string,
2685
/** Next contract sequence ID (for ETH) */
2786
nextContractSequenceId: t.number,
2887
/** Whether this is a batch transaction (for ETH) */
2988
isBatch: t.boolean,
3089
/** EIP1559 transaction parameters (for ETH) */
31-
eip1559: t.any,
90+
eip1559: EIP1559,
3291
/** Hop transaction data (for ETH) */
33-
hopTransaction: t.any,
92+
hopTransaction: HopTransaction,
3493
/** Backup key nonce (for ETH) */
35-
backupKeyNonce: t.any,
94+
backupKeyNonce: t.union([t.number, t.string]),
3695
/** Recipients of the transaction */
37-
recipients: t.any,
96+
recipients: t.array(Recipient),
97+
/** Gas limit (for EVM chains) */
98+
gasLimit: t.union([t.string, t.number]),
99+
/** Gas price (for EVM chains) */
100+
gasPrice: t.union([t.string, t.number]),
101+
/** Transaction expiration time */
102+
expireTime: t.number,
103+
/** Half-signed transaction data */
104+
halfSigned: HalfSignedData,
105+
/** Payload string */
106+
payload: t.string,
38107
});
39108

40109
/**
41110
* Request body for signing a transaction
111+
* Reference: modules/abstract-utxo/src/abstractUtxoCoin.ts:335-362 (UTXO fields)
112+
* Reference: modules/abstract-eth/src/abstractEthLikeNewCoins.ts:168-177 (EVM fields)
42113
*/
43114
export const CoinSignTxBody = {
44-
/** Private key for signing */
115+
/** Private key for signing (universal field) */
45116
prv: optional(t.string),
46-
/** Transaction prebuild data */
117+
/** Transaction prebuild data (universal field) */
47118
txPrebuild: optional(TransactionPrebuild),
48119
/** Whether this is the last signature in a multi-sig tx */
49120
isLastSignature: optional(t.boolean),
121+
122+
// EVM-specific fields
50123
/** Gas limit for ETH transactions */
51124
gasLimit: optional(t.union([t.string, t.number])),
52125
/** Gas price for ETH transactions */
@@ -55,17 +128,23 @@ export const CoinSignTxBody = {
55128
expireTime: optional(t.number),
56129
/** Sequence ID for transactions */
57130
sequenceId: optional(t.number),
58-
/** Public keys for multi-signature transactions */
59-
pubKeys: optional(t.array(t.string)),
60-
/** For EVM cross-chain recovery */
61-
isEvmBasedCrossChainRecovery: optional(t.boolean),
62131
/** Recipients of the transaction */
63-
recipients: optional(t.any),
132+
recipients: optional(t.array(Recipient)),
64133
/** Custodian transaction ID */
65134
custodianTransactionId: optional(t.string),
135+
/** For EVM cross-chain recovery */
136+
isEvmBasedCrossChainRecovery: optional(t.boolean),
137+
/** Wallet version (for EVM) */
138+
walletVersion: optional(t.number),
139+
140+
// UTXO-specific fields
141+
/** Public keys for multi-signature transactions (xpub triple: user, backup, bitgo) */
142+
pubs: optional(t.array(t.string)),
143+
/** Cosigner public key (defaults to bitgo) */
144+
cosignerPub: optional(t.string),
66145
/** Signing step for MuSig2 */
67146
signingStep: optional(t.union([t.literal('signerNonce'), t.literal('signerSignature'), t.literal('cosignerNonce')])),
68-
/** Allow non-segwit signing without previous transaction */
147+
/** Allow non-segwit signing without previous transaction (deprecated) */
69148
allowNonSegwitSigningWithoutPrevTx: optional(t.boolean),
70149
} as const;
71150

@@ -79,17 +158,53 @@ export const FullySignedTransactionResponse = t.type({
79158

80159
/**
81160
* Response for a half-signed account transaction
161+
*
162+
* Used by all account-based coins including:
163+
* - Generic account coins: Tron, Algorand, Solana, etc. (use txHex/payload/txBase64)
164+
* - EVM coins: Ethereum, Polygon, etc. (use txHex + EVM-specific fields)
165+
*
166+
* This type includes all possible fields as EVM responses are a superset of the base
167+
* HalfSignedAccountTransaction interface. TypeScript's structural typing allows this
168+
* since a superset is assignable to the base type.
169+
*
170+
* Reference: modules/sdk-core/src/bitgo/baseCoin/iBaseCoin.ts:408-414 (base)
171+
* Reference: modules/abstract-eth/src/abstractEthLikeNewCoins.ts:1105-1118 (EVM superset)
82172
*/
83173
export const HalfSignedAccountTransactionResponse = t.type({
84174
halfSigned: t.partial({
175+
// Generic account-based coin fields
176+
/** Transaction in hex format (used by most account coins) */
85177
txHex: optional(t.string),
178+
/** Transaction payload (used by some account coins) */
86179
payload: optional(t.string),
180+
/** Transaction in base64 format (used by some account coins) */
87181
txBase64: optional(t.string),
182+
183+
// Additional EVM-specific fields (superset)
184+
/** Transaction recipients (EVM) */
185+
recipients: t.array(Recipient),
186+
/** Expiration timestamp (EVM) */
187+
expiration: t.number,
188+
/** Expire time timestamp (EVM) */
189+
expireTime: t.number,
190+
/** Contract sequence ID (EVM) */
191+
contractSequenceId: t.number,
192+
/** Sequence ID for replay protection (EVM) */
193+
sequenceId: t.number,
194+
/** EIP1559 parameters (EVM) */
195+
eip1559: EIP1559,
196+
/** Hop transaction data (EVM) */
197+
hopTransaction: HopTransaction,
198+
/** Custodian transaction ID (EVM) */
199+
custodianTransactionId: t.string,
200+
/** Whether this is a batch transaction (EVM) */
201+
isBatch: t.boolean,
88202
}),
89203
});
90204

91205
/**
92-
* Response for a half-signed UTXO transaction
206+
* Response for a half-signed UTXO transaction (Bitcoin, Litecoin, etc.)
207+
* Reference: modules/sdk-core/src/bitgo/baseCoin/iBaseCoin.ts:404-406
93208
*/
94209
export const HalfSignedUtxoTransactionResponse = t.type({
95210
txHex: t.string,
@@ -107,6 +222,13 @@ export const SignedTransactionRequestResponse = t.type({
107222
*
108223
* Uses TxRequestResponse (TransactionRequest) from @bitgo/public-types for TSS transaction requests
109224
* (supports both Lite and Full versions)
225+
*
226+
* Response types match the SDK's SignedTransaction union:
227+
* - FullySignedTransactionResponse: Fully signed transaction with txHex
228+
* - HalfSignedAccountTransactionResponse: Half-signed account-based coins (includes EVM, Algorand, Solana, Tron, etc.)
229+
* - HalfSignedUtxoTransactionResponse: Half-signed UTXO coins (Bitcoin, Litecoin, etc.)
230+
* - SignedTransactionRequestResponse: TSS transaction request with txRequestId
231+
* - TxRequestResponse: Full TSS transaction request (Lite/Full versions)
110232
*/
111233
export const CoinSignTxResponse = {
112234
/** Successfully signed transaction */
@@ -126,16 +248,27 @@ export const CoinSignTxResponse = {
126248
*
127249
* This endpoint signs a transaction for a specific coin type.
128250
* The request body is passed directly to coin.signTransaction() and varies by coin.
129-
* Common fields include:
251+
*
252+
* Universal fields:
130253
* - txPrebuild: Contains transaction data like txHex or txBase64
131254
* - prv: Private key for signing
132255
* - isLastSignature: Whether this is the last signature in a multi-sig tx
133-
* - gasLimit: Gas limit for ETH transactions
134-
* - gasPrice: Gas price for ETH transactions
256+
*
257+
* EVM-specific fields (Ethereum, Polygon, etc.):
258+
* - gasLimit: Gas limit for transactions
259+
* - gasPrice: Gas price for transactions
135260
* - expireTime: Transaction expiration time
136-
* - sequenceId: Sequence ID for transactions
137-
* - pubKeys: Public keys for multi-signature transactions
138-
* - isEvmBasedCrossChainRecovery: For EVM cross-chain recovery
261+
* - sequenceId: Sequence ID for replay protection
262+
* - recipients: Transaction recipients
263+
* - custodianTransactionId: Custodian transaction ID
264+
* - walletVersion: Wallet version
265+
* - isEvmBasedCrossChainRecovery: For cross-chain recovery
266+
*
267+
* UTXO-specific fields (Bitcoin, Litecoin, etc.):
268+
* - pubs: Public keys array (xpub triple: user, backup, bitgo)
269+
* - cosignerPub: Cosigner's public key (defaults to bitgo)
270+
* - signingStep: MuSig2 signing step
271+
* - allowNonSegwitSigningWithoutPrevTx: Legacy parameter
139272
*
140273
* @tag express
141274
* @operationId express.v2.coin.signtx

0 commit comments

Comments
 (0)