@@ -229,19 +229,20 @@ export class DelegatorTxBuilder extends TransactionBuilder {
229229   * @param  inputs 
230230   * @protected  
231231   */ 
232-   protected  recoverUtxos ( inputs : TransferableInput [ ] ) : DecodedUtxoObj [ ]  { 
233-     return  inputs . map ( ( input )  =>  { 
234-       const  secpInput : SECPTransferInput  =  input . getInput ( )  as  SECPTransferInput ; 
235-       // Order Addresses as output was defined. 
232+   protected  recoverUtxos ( utxos : TransferableInput [ ] ) : DecodedUtxoObj [ ]  { 
233+     return  utxos . map ( ( utxo )  =>  { 
234+       const  secpInput : SECPTransferInput  =  utxo . getInput ( )  as  SECPTransferInput ; 
235+ 
236+       // use the same addressesIndex as existing ones in the inputs 
236237      const  addressesIndex : number [ ]  =  secpInput . getSigIdxs ( ) . map ( ( s )  =>  s . toBuffer ( ) . readUInt32BE ( 0 ) ) ; 
237238
238239      return  { 
239240        outputID : 7 , 
240-         outputidx : utils . cb58Encode ( input . getOutputIdx ( ) ) , 
241-         txid : utils . cb58Encode ( input . getTxID ( ) ) , 
241+         outputidx : utils . cb58Encode ( utxo . getOutputIdx ( ) ) , 
242+         txid : utils . cb58Encode ( utxo . getTxID ( ) ) , 
242243        amount : secpInput . getAmount ( ) . toString ( ) , 
243244        threshold : this . transaction . _threshold , 
244-         addresses : [ ] , 
245+         addresses : [ ] ,   // this is empty since the inputs from deserialized transaction don't contain addresses 
245246        addressesIndex, 
246247      } ; 
247248    } ) ; 
@@ -250,7 +251,7 @@ export class DelegatorTxBuilder extends TransactionBuilder {
250251  /** 
251252   * Threshold must be 2 and since output always get reordered we want to make sure we can always add signatures in the correct location 
252253   * To find the correct location for the signature, we use the ouput's addresses to create the signatureIdx in the order that we desire 
253-    * 0: user key, 1: recovery  key, 2: hsm  key 
254+    * 0: user key, 1: hsm  key, 2: recovery  key 
254255   * @protected  
255256   */ 
256257  protected  createInputOutput ( ) : { 
@@ -260,90 +261,132 @@ export class DelegatorTxBuilder extends TransactionBuilder {
260261  }  { 
261262    const  inputs : TransferableInput [ ]  =  [ ] ; 
262263    const  outputs : TransferableOutput [ ]  =  [ ] ; 
263-     const  addresses  =  this . transaction . _fromAddresses . map ( ( b )  => 
264+ 
265+     // amount spent so far 
266+     let  currentTotal : BN  =  new  BN ( 0 ) ; 
267+ 
268+     // delegating and validating have no fees 
269+     const  totalTarget  =  this . _stakeAmount . clone ( ) ; 
270+ 
271+     const  credentials : Credential [ ]  =  [ ] ; 
272+ 
273+     // convert fromAddresses to string 
274+     // fromAddresses = bitgo order if we are in WP 
275+     // fromAddresses = onchain order if we are in from 
276+     const  bitgoAddresses  =  this . transaction . _fromAddresses . map ( ( b )  => 
264277      utils . addressToString ( this . transaction . _network . hrp ,  this . transaction . _network . alias ,  b ) 
265278    ) ; 
266-     let  total : BN  =  new  BN ( 0 ) ; 
267-     const  totalTarget  =  this . _stakeAmount . clone ( ) . add ( this . transaction . _txFee ) ; 
268-     const  credentials : Credential [ ]  =  [ ] ; 
269279
270-     this . transaction . _utxos . forEach ( ( outputs )  =>  { 
271-       if  ( ! outputs . addressesIndex  ||  outputs . addressesIndex . length  ==  0 )  { 
272-         outputs . addressesIndex  =  addresses . map ( ( a )  =>  outputs . addresses . indexOf ( a ) ) ; 
280+     /*  
281+     A = user key 
282+     B = hsm key 
283+     C = backup key 
284+     bitgoAddresses = bitgo addresses [ A, B, C ] 
285+     utxo.addresses = IMS addresses [ B, C, A ] 
286+     utxo.addressesIndex = [ 2, 0, 1 ] 
287+     we pick 0, 1 for non-recovery 
288+     we pick 1, 2 for recovery 
289+     */ 
290+     this . transaction . _utxos . forEach ( ( utxo )  =>  { 
291+       // in WP, output.addressesIndex is empty, so fill it 
292+       if  ( ! utxo . addressesIndex  ||  utxo . addressesIndex . length  ===  0 )  { 
293+         utxo . addressesIndex  =  bitgoAddresses . map ( ( a )  =>  utxo . addresses . indexOf ( a ) ) ; 
273294      } 
295+       // in OVC, output.addressesIndex is defined correctly from the previous iteration 
274296    } ) ; 
275297
276-     this . transaction . _utxos 
277-       . filter ( 
278-         ( output )  => 
279-           output . threshold  ===  this . transaction . _threshold  && 
280-           ( output . addressesIndex ?. length  ==  3  ||  ! output . addressesIndex ?. includes ( - 1 ) ) 
281-       ) 
282-       . forEach ( ( output ,  i )  =>  { 
283-         if  ( output . outputID  ===  7  &&  total . lte ( totalTarget ) )  { 
284-           const  txidBuf  =  utils . cb58Decode ( output . txid ) ; 
285-           const  amt : BN  =  new  BN ( output . amount ) ; 
286-           const  outputidx  =  utils . cb58Decode ( output . outputidx ) ; 
287-           const  addressesIndex  =  output . addressesIndex  ??  [ ] ; 
288-           const  isRawUtxos  =  output . addresses . length  ==  0 ; 
289-           const  firstIndex  =  this . recoverSigner  ? 1  : 0 ; 
290-           total  =  total . add ( amt ) ; 
291- 
292-           const  secpTransferInput  =  new  SECPTransferInput ( amt ) ; 
293- 
294-           if  ( isRawUtxos )  { 
295-             addressesIndex . forEach ( ( i )  =>  secpTransferInput . addSignatureIdx ( i ,  this . transaction . _fromAddresses [ i ] ) ) ; 
298+     // validate the utxos 
299+     this . transaction . _utxos . forEach ( ( utxo )  =>  { 
300+       if  ( ! utxo )  { 
301+         throw  new  BuildTransactionError ( 'Utxo is undefined' ) ; 
302+       } 
303+       // addressesIndex should neve have a mismatch 
304+       if  ( utxo . addressesIndex ?. includes ( - 1 ) )  { 
305+         throw  new  BuildTransactionError ( 'Addresses are inconsistent' ) ; 
306+       } 
307+       if  ( utxo . threshold  !==  this . transaction . _threshold )  { 
308+         throw  new  BuildTransactionError ( 'Threshold is inconsistent' ) ; 
309+       } 
310+     } ) ; 
311+ 
312+     // if we are in OVC, none of the utxos will have addresses since they come from 
313+     // deserialized inputs (which don't have addresses), not the IMS 
314+     const  buildOutputs  =  this . transaction . _utxos [ 0 ] . addresses . length  !==  0 ; 
315+ 
316+     this . transaction . _utxos . forEach ( ( utxo ,  i )  =>  { 
317+       if  ( utxo . outputID  ===  7  &&  currentTotal . lte ( totalTarget ) )  { 
318+         const  txidBuf  =  utils . cb58Decode ( utxo . txid ) ; 
319+         const  amt : BN  =  new  BN ( utxo . amount ) ; 
320+         const  outputidx  =  utils . cb58Decode ( utxo . outputidx ) ; 
321+         const  addressesIndex  =  utxo . addressesIndex  ??  [ ] ; 
322+ 
323+         // either user (0) or recovery (2) 
324+         const  firstIndex  =  this . recoverSigner  ? 2  : 0 ;  // 0 
325+         const  bitgoIndex  =  1 ; 
326+         currentTotal  =  currentTotal . add ( amt ) ; 
327+ 
328+         const  secpTransferInput  =  new  SECPTransferInput ( amt ) ; 
329+ 
330+         if  ( ! buildOutputs )  { 
331+           addressesIndex . forEach ( ( i )  =>  secpTransferInput . addSignatureIdx ( i ,  this . transaction . _fromAddresses [ i ] ) ) ; 
332+         }  else  { 
333+           // if user/backup > bitgo 
334+           if  ( addressesIndex [ bitgoIndex ]  <  addressesIndex [ firstIndex ] )  { 
335+             // console.log('bitgo < user', addressesIndex[bitgoIndex], addressesIndex[firstIndex]); 
336+             secpTransferInput . addSignatureIdx ( addressesIndex [ bitgoIndex ] ,  this . transaction . _fromAddresses [ bitgoIndex ] ) ; 
337+             secpTransferInput . addSignatureIdx ( addressesIndex [ firstIndex ] ,  this . transaction . _fromAddresses [ firstIndex ] ) ; 
338+             credentials . push ( 
339+               SelectCredentialClass ( 
340+                 secpTransferInput . getCredentialID ( ) ,  // 9 
341+                 [ '' ,  this . transaction . _fromAddresses [ firstIndex ] . toString ( 'hex' ) ] . map ( utils . createSig ) 
342+               ) 
343+             ) ; 
296344          }  else  { 
297-             if  ( addressesIndex [ firstIndex ]  >  addressesIndex [ 2 ] )  { 
298-               secpTransferInput . addSignatureIdx ( addressesIndex [ 2 ] ,  this . transaction . _fromAddresses [ 2 ] ) ; 
299-               secpTransferInput . addSignatureIdx ( 
300-                 addressesIndex [ firstIndex ] , 
301-                 this . transaction . _fromAddresses [ firstIndex ] 
302-               ) ; 
303-               credentials . push ( 
304-                 SelectCredentialClass ( 
305-                   secpTransferInput . getCredentialID ( ) , 
306-                   [ '' ,  this . transaction . _fromAddresses [ firstIndex ] . toString ( 'hex' ) ] . map ( utils . createSig ) 
307-                 ) 
308-               ) ; 
309-             }  else  { 
310-               secpTransferInput . addSignatureIdx ( 
311-                 addressesIndex [ firstIndex ] , 
312-                 this . transaction . _fromAddresses [ firstIndex ] 
313-               ) ; 
314-               secpTransferInput . addSignatureIdx ( addressesIndex [ 2 ] ,  this . transaction . _fromAddresses [ 2 ] ) ; 
315-               credentials . push ( 
316-                 SelectCredentialClass ( 
317-                   secpTransferInput . getCredentialID ( ) , 
318-                   [ this . transaction . _fromAddresses [ firstIndex ] . toString ( 'hex' ) ,  '' ] . map ( utils . createSig ) 
319-                 ) 
320-               ) ; 
321-             } 
345+             // console.log('user < bitgo', addressesIndex[firstIndex], addressesIndex[bitgoIndex]); 
346+             secpTransferInput . addSignatureIdx ( addressesIndex [ firstIndex ] ,  this . transaction . _fromAddresses [ firstIndex ] ) ; 
347+             secpTransferInput . addSignatureIdx ( addressesIndex [ bitgoIndex ] ,  this . transaction . _fromAddresses [ bitgoIndex ] ) ; 
348+             credentials . push ( 
349+               SelectCredentialClass ( 
350+                 secpTransferInput . getCredentialID ( ) , 
351+                 [ this . transaction . _fromAddresses [ firstIndex ] . toString ( 'hex' ) ,  '' ] . map ( utils . createSig ) 
352+               ) 
353+             ) ; 
322354          } 
323- 
324-           const  input : TransferableInput  =  new  TransferableInput ( 
325-             txidBuf , 
326-             outputidx , 
327-             this . transaction . _assetId , 
328-             secpTransferInput 
329-           ) ; 
330-           inputs . push ( input ) ; 
331355        } 
332-       } ) ; 
333-     if  ( total . lt ( totalTarget ) )  { 
334-       throw  new  BuildTransactionError ( `Utxo outputs get ${ total . toString ( ) }   and ${ totalTarget . toString ( ) }   is required` ) ; 
335-     } 
336-     outputs . push ( 
337-       new  TransferableOutput ( 
338-         this . transaction . _assetId , 
339-         new  SECPTransferOutput ( 
340-           total . sub ( totalTarget ) , 
341-           this . transaction . _fromAddresses , 
342-           this . transaction . _locktime , 
343-           this . transaction . _threshold 
356+ 
357+         const  input : TransferableInput  =  new  TransferableInput ( 
358+           txidBuf , 
359+           outputidx , 
360+           this . transaction . _assetId , 
361+           secpTransferInput 
362+         ) ; 
363+         inputs . push ( input ) ; 
364+       } 
365+     } ) ; 
366+ 
367+     if  ( buildOutputs )  { 
368+       if  ( currentTotal . lt ( totalTarget ) )  { 
369+         throw  new  BuildTransactionError ( 
370+           `Utxo outputs get ${ currentTotal . toString ( ) }   and ${ totalTarget . toString ( ) }   is required` 
371+         ) ; 
372+       } 
373+       outputs . push ( 
374+         new  TransferableOutput ( 
375+           this . transaction . _assetId , 
376+           new  SECPTransferOutput ( 
377+             currentTotal . sub ( totalTarget ) , 
378+             this . transaction . _fromAddresses , 
379+             this . transaction . _locktime , 
380+             this . transaction . _threshold 
381+           ) 
344382        ) 
345-       ) 
346-     ) ; 
347-     return  {  inputs,  outputs,  credentials : credentials . length  ==  0  ? this . transaction . credentials  : credentials  } ; 
383+       ) ; 
384+     } 
385+     // get outputs and credentials from the deserialized transaction if we are in OVC 
386+     return  { 
387+       inputs, 
388+       outputs : outputs . length  ===  0  ? this . transaction . avaxPTransaction . getOuts ( )  : outputs , 
389+       credentials : credentials . length  ===  0  ? this . transaction . credentials  : credentials , 
390+     } ; 
348391  } 
349392} 
0 commit comments