23
23
import com .bloxbean .cardano .client .transaction .spec .TransactionInput ;
24
24
import com .bloxbean .cardano .client .util .JsonUtil ;
25
25
import com .bloxbean .cardano .client .util .Tuple ;
26
+ import com .bloxbean .cardano .hdwallet .Wallet ;
27
+ import com .bloxbean .cardano .hdwallet .util .HDWalletAddressIterator ;
26
28
import lombok .NonNull ;
27
29
import lombok .extern .slf4j .Slf4j ;
28
30
@@ -147,7 +149,9 @@ public TxContext compose(AbstractTx... txs) {
147
149
public class TxContext {
148
150
private AbstractTx [] txList ;
149
151
private String feePayer ;
152
+ private Wallet feePayerWallet ;
150
153
private String collateralPayer ;
154
+ private Wallet collateralPayerWallet ;
151
155
private Set <byte []> requiredSigners ;
152
156
private Set <TransactionInput > collateralInputs ;
153
157
@@ -183,18 +187,70 @@ public class TxContext {
183
187
* When there are more than one txs, fee payer address is mandatory.
184
188
*
185
189
* @param address fee payer address
186
- * @return TxContext
190
+ * @return TxContext - the updated transaction context with the fee payer set
191
+ * @throws TxBuildException if the fee payer has already been set
187
192
*/
188
193
public TxContext feePayer (String address ) {
194
+ if (feePayerWallet != null || feePayer != null )
195
+ throw new TxBuildException ("The fee payer has already been set. It can only be set once." );
196
+
189
197
this .feePayer = address ;
190
198
return this ;
191
199
}
192
200
201
+ /**
202
+ * Sets the fee payer wallet for the transaction. When there is only one tx, sender's address is used as fee payer address.
203
+ * When there are more than one txs, fee payer address/wallet is mandatory.
204
+ *
205
+ * @param feePayerWallet the wallet that will act as the fee payer for the transaction
206
+ * @return TxContext - the updated transaction context with the fee payer set
207
+ * @throws TxBuildException if the fee payer has already been set
208
+ */
209
+ public TxContext feePayer (Wallet feePayerWallet ) {
210
+ if (feePayerWallet != null || feePayer != null )
211
+ throw new TxBuildException ("The fee payer has already been set. It can only be set once." );
212
+
213
+ this .feePayerWallet = feePayerWallet ;
214
+ // TODO feePayer is not used in this scenarios, but it must be set to avoid breaking other things.
215
+ this .feePayer = this .feePayerWallet .getBaseAddress (0 ).getAddress ();
216
+
217
+ return this ;
218
+ }
219
+
220
+ /**
221
+ * Sets the provided collateral payer address. This method ensures that the collateral payer can only be set once.
222
+ *
223
+ * @param address the address of the collateral payer to be set
224
+ * @return TxContext
225
+ * @throws TxBuildException if the collateral payer has already been set
226
+ */
193
227
public TxContext collateralPayer (String address ) {
228
+ if (collateralPayerWallet != null || collateralPayer != null )
229
+ throw new TxBuildException ("The collateral payer has already been set. It can only be set once." );
230
+
194
231
this .collateralPayer = address ;
195
232
return this ;
196
233
}
197
234
235
+ /**
236
+ * Sets the collateral payer using the provided wallet. This method ensures that the collateral payer
237
+ * is set only once.
238
+ *
239
+ * @param wallet the wallet from which the collateral payer address will be derived
240
+ * @return TxContext
241
+ * @throws TxBuildException if the collateral payer has already been set
242
+ */
243
+ public TxContext collateralPayer (Wallet wallet ) {
244
+ if (collateralPayerWallet != null || collateralPayer != null )
245
+ throw new TxBuildException ("The collateral payer has already been set. It can only be set once." );
246
+
247
+ this .collateralPayerWallet = wallet ;
248
+ // TODO collateralPayer is not used in this scenarios, but it must be set to avoid breaking other things.
249
+ this .collateralPayer = this .collateralPayerWallet .getBaseAddress (0 ).getAddress ();
250
+
251
+ return this ;
252
+ }
253
+
198
254
/**
199
255
* Set a TxBuilder function to transform the transaction before balance calculation.
200
256
* This is useful when additional transformation logic is required before balance calculation.
@@ -281,7 +337,10 @@ private Tuple<TxBuilderContext, TxBuilder> _build() {
281
337
((ScriptTx ) tx ).withChangeAddress (feePayer );
282
338
}
283
339
if (tx .getFromAddress () == null && tx instanceof ScriptTx ) {
284
- ((ScriptTx ) tx ).from (feePayer );
340
+ if (feePayerWallet != null )
341
+ ((ScriptTx ) tx ).from (feePayerWallet );
342
+ else
343
+ ((ScriptTx ) tx ).from (feePayer );
285
344
}
286
345
287
346
txBuilder = txBuilder .andThen (tx .complete ());
@@ -338,7 +397,7 @@ private Tuple<TxBuilderContext, TxBuilder> _build() {
338
397
if (preBalanceTrasformer != null )
339
398
txBuilder = txBuilder .andThen (preBalanceTrasformer );
340
399
341
- if (feePayer == null ) {
400
+ if (feePayer == null && feePayerWallet == null ) {
342
401
if (txList .length == 1 ) {
343
402
feePayer = txList [0 ].getFeePayer ();
344
403
if (feePayer == null )
@@ -352,9 +411,18 @@ private Tuple<TxBuilderContext, TxBuilder> _build() {
352
411
txBuilder = buildValidityIntervalTxBuilder (txBuilder );
353
412
354
413
if (containsScriptTx ) {
355
- if (collateralPayer == null )
356
- collateralPayer = feePayer ;
357
- txBuilder = txBuilder .andThen (buildCollateralOutput (collateralPayer ));
414
+ if (collateralPayer == null && collateralPayerWallet == null ) {
415
+ if (feePayerWallet != null )
416
+ collateralPayerWallet = feePayerWallet ;
417
+ else
418
+ collateralPayer = feePayer ;
419
+ }
420
+
421
+ if (collateralPayerWallet != null ) {
422
+ txBuilder = txBuilder .andThen (buildCollateralOutput (collateralPayerWallet ));
423
+ } else {
424
+ txBuilder = txBuilder .andThen (buildCollateralOutput (collateralPayer ));
425
+ }
358
426
}
359
427
360
428
if (containsScriptTx ) {
@@ -393,7 +461,11 @@ private Tuple<TxBuilderContext, TxBuilder> _build() {
393
461
}
394
462
395
463
//Balance outputs
396
- txBuilder = txBuilder .andThen (ScriptBalanceTxProviders .balanceTx (feePayer , totalSigners , containsScriptTx ));
464
+ if (feePayerWallet != null ) {
465
+ var walletAddrIterator = new HDWalletAddressIterator (feePayerWallet , utxoSupplier );
466
+ txBuilder = txBuilder .andThen (ScriptBalanceTxProviders .balanceTx (walletAddrIterator , totalSigners , containsScriptTx ));
467
+ } else
468
+ txBuilder = txBuilder .andThen (ScriptBalanceTxProviders .balanceTx (feePayer , totalSigners , containsScriptTx ));
397
469
398
470
if ((containsScriptTx || hasMultiAssetMint ) && removeDuplicateScriptWitnesses ) {
399
471
txBuilder = txBuilder .andThen (DuplicateScriptWitnessChecker .removeDuplicateScriptWitnesses ());
@@ -420,22 +492,45 @@ private int getTotalSigners() {
420
492
return totalSigners ;
421
493
}
422
494
423
- private TxBuilder buildCollateralOutput (String feePayer ) {
495
+ private TxBuilder buildCollateralOutput (String payingAddress ) {
496
+ if (collateralInputs != null && !collateralInputs .isEmpty ()) {
497
+ List <Utxo > collateralUtxos = collateralInputs .stream ()
498
+ .map (input -> utxoSupplier .getTxOutput (input .getTransactionId (), input .getIndex ()))
499
+ .map (optionalUtxo -> optionalUtxo .get ())
500
+ .collect (Collectors .toList ());
501
+ return CollateralBuilders .collateralOutputs (payingAddress , List .copyOf (collateralUtxos ));
502
+ } else {
503
+ UtxoSelectionStrategy utxoSelectionStrategy = new DefaultUtxoSelectionStrategyImpl (utxoSupplier );
504
+ Set <Utxo > collateralUtxos = utxoSelectionStrategy .select (payingAddress , DEFAULT_COLLATERAL_AMT , null );
505
+ if (collateralUtxos .size () > MAX_COLLATERAL_INPUTS ) {
506
+ utxoSelectionStrategy = new LargestFirstUtxoSelectionStrategy (utxoSupplier );
507
+ collateralUtxos = utxoSelectionStrategy .select (payingAddress , DEFAULT_COLLATERAL_AMT , null );
508
+ }
509
+
510
+ return CollateralBuilders .collateralOutputs (payingAddress , List .copyOf (collateralUtxos ));
511
+ }
512
+ }
513
+
514
+ private TxBuilder buildCollateralOutput (Wallet payingWallet ) {
515
+ String collateralPayerAddress = payingWallet .getBaseAddressString (0 ); //TODO: first addr as collateral output addr
516
+
424
517
if (collateralInputs != null && !collateralInputs .isEmpty ()) {
425
518
List <Utxo > collateralUtxos = collateralInputs .stream ()
426
519
.map (input -> utxoSupplier .getTxOutput (input .getTransactionId (), input .getIndex ()))
427
520
.map (optionalUtxo -> optionalUtxo .get ())
428
521
.collect (Collectors .toList ());
429
- return CollateralBuilders .collateralOutputs (feePayer , List .copyOf (collateralUtxos ));
522
+ return CollateralBuilders .collateralOutputs (collateralPayerAddress , List .copyOf (collateralUtxos ));
430
523
} else {
431
524
UtxoSelectionStrategy utxoSelectionStrategy = new DefaultUtxoSelectionStrategyImpl (utxoSupplier );
432
- Set <Utxo > collateralUtxos = utxoSelectionStrategy .select (feePayer , DEFAULT_COLLATERAL_AMT , null );
525
+ var hdWalletAddressIterator = new HDWalletAddressIterator (payingWallet , utxoSupplier );
526
+
527
+ List <Utxo > collateralUtxos = utxoSelectionStrategy .selectUtxos (hdWalletAddressIterator , List .of (DEFAULT_COLLATERAL_AMT ), null );
433
528
if (collateralUtxos .size () > MAX_COLLATERAL_INPUTS ) {
434
529
utxoSelectionStrategy = new LargestFirstUtxoSelectionStrategy (utxoSupplier );
435
- collateralUtxos = utxoSelectionStrategy .select ( feePayer , DEFAULT_COLLATERAL_AMT , null );
530
+ collateralUtxos = utxoSelectionStrategy .selectUtxos ( hdWalletAddressIterator , List . of ( DEFAULT_COLLATERAL_AMT ) , null );
436
531
}
437
532
438
- return CollateralBuilders .collateralOutputs (feePayer , List .copyOf (collateralUtxos ));
533
+ return CollateralBuilders .collateralOutputs (collateralPayerAddress , List .copyOf (collateralUtxos ));
439
534
}
440
535
}
441
536
0 commit comments