@@ -193,6 +193,7 @@ describe('signTxRequest:', function () {
193193 txRequest,
194194 prv : userPrvBase64 ,
195195 reqId,
196+ txParams : { recipients : [ { address : '0xrecipient' , amount : '1000' } ] } ,
196197 } ) ;
197198 nockPromises [ 0 ] . isDone ( ) . should . be . true ( ) ;
198199 nockPromises [ 1 ] . isDone ( ) . should . be . true ( ) ;
@@ -215,6 +216,7 @@ describe('signTxRequest:', function () {
215216 prv : backupPrvBase64 ,
216217 mpcv2PartyId : 1 ,
217218 reqId,
219+ txParams : { recipients : [ { address : '0xrecipient' , amount : '1000' } ] } ,
218220 } ) ;
219221 nockPromises [ 0 ] . isDone ( ) . should . be . true ( ) ;
220222 nockPromises [ 1 ] . isDone ( ) . should . be . true ( ) ;
@@ -236,6 +238,7 @@ describe('signTxRequest:', function () {
236238 txRequest,
237239 prv : userPrvBase64 ,
238240 reqId,
241+ txParams : { recipients : [ { address : '0xrecipient' , amount : '1000' } ] } ,
239242 } ) ;
240243 nockPromises [ 0 ] . isDone ( ) . should . be . true ( ) ;
241244 nockPromises [ 1 ] . isDone ( ) . should . be . true ( ) ;
@@ -257,6 +260,7 @@ describe('signTxRequest:', function () {
257260 txRequest,
258261 prv : userPrvBase64 ,
259262 reqId,
263+ txParams : { recipients : [ { address : '0xrecipient' , amount : '1000' } ] } ,
260264 } ) ;
261265 nockPromises [ 0 ] . isDone ( ) . should . be . true ( ) ;
262266 nockPromises [ 1 ] . isDone ( ) . should . be . true ( ) ;
@@ -277,11 +281,197 @@ describe('signTxRequest:', function () {
277281 txRequest,
278282 prv : userPrvBase64 ,
279283 reqId,
284+ txParams : { recipients : [ { address : '0xrecipient' , amount : '1000' } ] } ,
280285 } )
281286 . should . be . rejectedWith ( 'Too many requests, slow down!' ) ;
282287 nockPromises [ 0 ] . isDone ( ) . should . be . true ( ) ;
283288 nockPromises [ 1 ] . isDone ( ) . should . be . false ( ) ;
284289 } ) ;
290+
291+ it ( 'rejects signTxRequest when txParams is missing' , async function ( ) {
292+ const userShare = fs . readFileSync ( shareFiles [ vector . party1 ] ) ;
293+ const userPrvBase64 = Buffer . from ( userShare ) . toString ( 'base64' ) ;
294+ await tssUtils
295+ . signTxRequest ( {
296+ txRequest,
297+ prv : userPrvBase64 ,
298+ reqId,
299+ } )
300+ . should . be . rejectedWith (
301+ 'Recipient details are required to verify this transaction before signing. Pass txParams with at least one recipient.'
302+ ) ;
303+ } ) ;
304+
305+ it ( 'rejects signTxRequest when txParams has empty recipients' , async function ( ) {
306+ const userShare = fs . readFileSync ( shareFiles [ vector . party1 ] ) ;
307+ const userPrvBase64 = Buffer . from ( userShare ) . toString ( 'base64' ) ;
308+ await tssUtils
309+ . signTxRequest ( {
310+ txRequest,
311+ prv : userPrvBase64 ,
312+ reqId,
313+ txParams : { recipients : [ ] } ,
314+ } )
315+ . should . be . rejectedWith (
316+ 'Recipient details are required to verify this transaction before signing. Pass txParams with at least one recipient.'
317+ ) ;
318+ } ) ;
319+
320+ it ( 'accepts signTxRequest when recipients are only in intent (smart contract interaction)' , async function ( ) {
321+ const txRequestWithIntentRecipients = {
322+ ...txRequest ,
323+ intent : {
324+ intentType : 'contractCall' ,
325+ recipients : [
326+ {
327+ address : { address : '0xrecipient' } ,
328+ amount : { value : '1000' , symbol : 'hteth' } ,
329+ } ,
330+ ] ,
331+ } ,
332+ } ;
333+ const nockPromises = [
334+ await nockTxRequestResponseSignatureShareRoundOne ( bitgoParty , txRequestWithIntentRecipients , bitgoGpgKey ) ,
335+ await nockTxRequestResponseSignatureShareRoundTwo ( bitgoParty , txRequestWithIntentRecipients , bitgoGpgKey ) ,
336+ await nockTxRequestResponseSignatureShareRoundThree ( txRequestWithIntentRecipients ) ,
337+ await nockSendTxRequest ( txRequestWithIntentRecipients ) ,
338+ ] ;
339+ await Promise . all ( nockPromises ) ;
340+
341+ const userShare = fs . readFileSync ( shareFiles [ vector . party1 ] ) ;
342+ const userPrvBase64 = Buffer . from ( userShare ) . toString ( 'base64' ) ;
343+ // Falls back to intent.recipients — guard should pass and signing should complete
344+ await tssUtils . signTxRequest ( {
345+ txRequest : txRequestWithIntentRecipients ,
346+ prv : userPrvBase64 ,
347+ reqId,
348+ // no txParams.recipients
349+ } ) ;
350+ nockPromises [ 0 ] . isDone ( ) . should . be . true ( ) ;
351+ nockPromises [ 1 ] . isDone ( ) . should . be . true ( ) ;
352+ nockPromises [ 2 ] . isDone ( ) . should . be . true ( ) ;
353+ } ) ;
354+
355+ it ( 'accepts signTxRequest for no-recipient tx types (tokenApproval)' , async function ( ) {
356+ const nockPromises = [
357+ await nockTxRequestResponseSignatureShareRoundOne ( bitgoParty , txRequest , bitgoGpgKey ) ,
358+ await nockTxRequestResponseSignatureShareRoundTwo ( bitgoParty , txRequest , bitgoGpgKey ) ,
359+ await nockTxRequestResponseSignatureShareRoundThree ( txRequest ) ,
360+ await nockSendTxRequest ( txRequest ) ,
361+ ] ;
362+ await Promise . all ( nockPromises ) ;
363+
364+ const userShare = fs . readFileSync ( shareFiles [ vector . party1 ] ) ;
365+ const userPrvBase64 = Buffer . from ( userShare ) . toString ( 'base64' ) ;
366+ // Type exemption applies — guard passes without recipients
367+ await tssUtils . signTxRequest ( {
368+ txRequest,
369+ prv : userPrvBase64 ,
370+ reqId,
371+ txParams : { type : 'tokenApproval' } ,
372+ } ) ;
373+ nockPromises [ 0 ] . isDone ( ) . should . be . true ( ) ;
374+ nockPromises [ 1 ] . isDone ( ) . should . be . true ( ) ;
375+ nockPromises [ 2 ] . isDone ( ) . should . be . true ( ) ;
376+ } ) ;
377+
378+ it ( 'accepts signTxRequest for no-recipient tx types (acceleration)' , async function ( ) {
379+ const nockPromises = [
380+ await nockTxRequestResponseSignatureShareRoundOne ( bitgoParty , txRequest , bitgoGpgKey ) ,
381+ await nockTxRequestResponseSignatureShareRoundTwo ( bitgoParty , txRequest , bitgoGpgKey ) ,
382+ await nockTxRequestResponseSignatureShareRoundThree ( txRequest ) ,
383+ await nockSendTxRequest ( txRequest ) ,
384+ ] ;
385+ await Promise . all ( nockPromises ) ;
386+
387+ const userShare = fs . readFileSync ( shareFiles [ vector . party1 ] ) ;
388+ const userPrvBase64 = Buffer . from ( userShare ) . toString ( 'base64' ) ;
389+ await tssUtils . signTxRequest ( {
390+ txRequest,
391+ prv : userPrvBase64 ,
392+ reqId,
393+ txParams : { type : 'acceleration' } ,
394+ } ) ;
395+ nockPromises [ 0 ] . isDone ( ) . should . be . true ( ) ;
396+ nockPromises [ 1 ] . isDone ( ) . should . be . true ( ) ;
397+ nockPromises [ 2 ] . isDone ( ) . should . be . true ( ) ;
398+ } ) ;
399+
400+ it ( 'accepts signTxRequest for no-recipient tx types (customTx)' , async function ( ) {
401+ const nockPromises = [
402+ await nockTxRequestResponseSignatureShareRoundOne ( bitgoParty , txRequest , bitgoGpgKey ) ,
403+ await nockTxRequestResponseSignatureShareRoundTwo ( bitgoParty , txRequest , bitgoGpgKey ) ,
404+ await nockTxRequestResponseSignatureShareRoundThree ( txRequest ) ,
405+ await nockSendTxRequest ( txRequest ) ,
406+ ] ;
407+ await Promise . all ( nockPromises ) ;
408+
409+ const userShare = fs . readFileSync ( shareFiles [ vector . party1 ] ) ;
410+ const userPrvBase64 = Buffer . from ( userShare ) . toString ( 'base64' ) ;
411+ // DeFi/WalletConnect smart contract interactions have no traditional recipients
412+ await tssUtils . signTxRequest ( {
413+ txRequest,
414+ prv : userPrvBase64 ,
415+ reqId,
416+ txParams : { type : 'customTx' } ,
417+ } ) ;
418+ nockPromises [ 0 ] . isDone ( ) . should . be . true ( ) ;
419+ nockPromises [ 1 ] . isDone ( ) . should . be . true ( ) ;
420+ nockPromises [ 2 ] . isDone ( ) . should . be . true ( ) ;
421+ } ) ;
422+
423+ it ( 'accepts signTxRequest for no-recipient tx types (enableToken)' , async function ( ) {
424+ const nockPromises = [
425+ await nockTxRequestResponseSignatureShareRoundOne ( bitgoParty , txRequest , bitgoGpgKey ) ,
426+ await nockTxRequestResponseSignatureShareRoundTwo ( bitgoParty , txRequest , bitgoGpgKey ) ,
427+ await nockTxRequestResponseSignatureShareRoundThree ( txRequest ) ,
428+ await nockSendTxRequest ( txRequest ) ,
429+ ] ;
430+ await Promise . all ( nockPromises ) ;
431+
432+ const userShare = fs . readFileSync ( shareFiles [ vector . party1 ] ) ;
433+ const userPrvBase64 = Buffer . from ( userShare ) . toString ( 'base64' ) ;
434+ // TSS wallets do not populate recipients for token enablement — exemption must apply
435+ await tssUtils . signTxRequest ( {
436+ txRequest,
437+ prv : userPrvBase64 ,
438+ reqId,
439+ txParams : { type : 'enableToken' } ,
440+ } ) ;
441+ nockPromises [ 0 ] . isDone ( ) . should . be . true ( ) ;
442+ nockPromises [ 1 ] . isDone ( ) . should . be . true ( ) ;
443+ nockPromises [ 2 ] . isDone ( ) . should . be . true ( ) ;
444+ } ) ;
445+
446+ it ( 'accepts signTxRequest when txParams.recipients takes priority over intent.recipients' , async function ( ) {
447+ const txRequestWithBothRecipients = {
448+ ...txRequest ,
449+ intent : {
450+ intentType : 'contractCall' ,
451+ recipients : [ { address : { address : '0xintentRecipient' } , amount : { value : '9999' , symbol : 'hteth' } } ] ,
452+ } ,
453+ } ;
454+ const nockPromises = [
455+ await nockTxRequestResponseSignatureShareRoundOne ( bitgoParty , txRequestWithBothRecipients , bitgoGpgKey ) ,
456+ await nockTxRequestResponseSignatureShareRoundTwo ( bitgoParty , txRequestWithBothRecipients , bitgoGpgKey ) ,
457+ await nockTxRequestResponseSignatureShareRoundThree ( txRequestWithBothRecipients ) ,
458+ await nockSendTxRequest ( txRequestWithBothRecipients ) ,
459+ ] ;
460+ await Promise . all ( nockPromises ) ;
461+
462+ const userShare = fs . readFileSync ( shareFiles [ vector . party1 ] ) ;
463+ const userPrvBase64 = Buffer . from ( userShare ) . toString ( 'base64' ) ;
464+ // txParams.recipients takes priority — guard passes and signing completes
465+ await tssUtils . signTxRequest ( {
466+ txRequest : txRequestWithBothRecipients ,
467+ prv : userPrvBase64 ,
468+ reqId,
469+ txParams : { recipients : [ { address : '0xrecipient' , amount : '1000' } ] } ,
470+ } ) ;
471+ nockPromises [ 0 ] . isDone ( ) . should . be . true ( ) ;
472+ nockPromises [ 1 ] . isDone ( ) . should . be . true ( ) ;
473+ nockPromises [ 2 ] . isDone ( ) . should . be . true ( ) ;
474+ } ) ;
285475} ) ;
286476
287477export function getBitGoPartyGpgKeyPrv ( key : openpgp . SerializedKeyPair < string > ) : DklsTypes . PartyGpgKey {
0 commit comments