2323import com .bloxbean .cardano .client .transaction .spec .TransactionInput ;
2424import com .bloxbean .cardano .client .util .JsonUtil ;
2525import com .bloxbean .cardano .client .util .Tuple ;
26+ import com .bloxbean .cardano .hdwallet .Wallet ;
27+ import com .bloxbean .cardano .hdwallet .util .HDWalletAddressIterator ;
2628import lombok .NonNull ;
2729import lombok .extern .slf4j .Slf4j ;
2830
@@ -147,7 +149,9 @@ public TxContext compose(AbstractTx... txs) {
147149 public class TxContext {
148150 private AbstractTx [] txList ;
149151 private String feePayer ;
152+ private Wallet feePayerWallet ;
150153 private String collateralPayer ;
154+ private Wallet collateralPayerWallet ;
151155 private Set <byte []> requiredSigners ;
152156 private Set <TransactionInput > collateralInputs ;
153157
@@ -183,18 +187,70 @@ public class TxContext {
183187 * When there are more than one txs, fee payer address is mandatory.
184188 *
185189 * @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
187192 */
188193 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+
189197 this .feePayer = address ;
190198 return this ;
191199 }
192200
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+ */
193227 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+
194231 this .collateralPayer = address ;
195232 return this ;
196233 }
197234
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+
198254 /**
199255 * Set a TxBuilder function to transform the transaction before balance calculation.
200256 * This is useful when additional transformation logic is required before balance calculation.
@@ -281,7 +337,10 @@ private Tuple<TxBuilderContext, TxBuilder> _build() {
281337 ((ScriptTx ) tx ).withChangeAddress (feePayer );
282338 }
283339 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 );
285344 }
286345
287346 txBuilder = txBuilder .andThen (tx .complete ());
@@ -338,7 +397,7 @@ private Tuple<TxBuilderContext, TxBuilder> _build() {
338397 if (preBalanceTrasformer != null )
339398 txBuilder = txBuilder .andThen (preBalanceTrasformer );
340399
341- if (feePayer == null ) {
400+ if (feePayer == null && feePayerWallet == null ) {
342401 if (txList .length == 1 ) {
343402 feePayer = txList [0 ].getFeePayer ();
344403 if (feePayer == null )
@@ -352,9 +411,18 @@ private Tuple<TxBuilderContext, TxBuilder> _build() {
352411 txBuilder = buildValidityIntervalTxBuilder (txBuilder );
353412
354413 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+ }
358426 }
359427
360428 if (containsScriptTx ) {
@@ -393,7 +461,11 @@ private Tuple<TxBuilderContext, TxBuilder> _build() {
393461 }
394462
395463 //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 ));
397469
398470 if ((containsScriptTx || hasMultiAssetMint ) && removeDuplicateScriptWitnesses ) {
399471 txBuilder = txBuilder .andThen (DuplicateScriptWitnessChecker .removeDuplicateScriptWitnesses ());
@@ -420,22 +492,45 @@ private int getTotalSigners() {
420492 return totalSigners ;
421493 }
422494
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+
424517 if (collateralInputs != null && !collateralInputs .isEmpty ()) {
425518 List <Utxo > collateralUtxos = collateralInputs .stream ()
426519 .map (input -> utxoSupplier .getTxOutput (input .getTransactionId (), input .getIndex ()))
427520 .map (optionalUtxo -> optionalUtxo .get ())
428521 .collect (Collectors .toList ());
429- return CollateralBuilders .collateralOutputs (feePayer , List .copyOf (collateralUtxos ));
522+ return CollateralBuilders .collateralOutputs (collateralPayerAddress , List .copyOf (collateralUtxos ));
430523 } else {
431524 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 );
433528 if (collateralUtxos .size () > MAX_COLLATERAL_INPUTS ) {
434529 utxoSelectionStrategy = new LargestFirstUtxoSelectionStrategy (utxoSupplier );
435- collateralUtxos = utxoSelectionStrategy .select ( feePayer , DEFAULT_COLLATERAL_AMT , null );
530+ collateralUtxos = utxoSelectionStrategy .selectUtxos ( hdWalletAddressIterator , List . of ( DEFAULT_COLLATERAL_AMT ) , null );
436531 }
437532
438- return CollateralBuilders .collateralOutputs (feePayer , List .copyOf (collateralUtxos ));
533+ return CollateralBuilders .collateralOutputs (collateralPayerAddress , List .copyOf (collateralUtxos ));
439534 }
440535 }
441536
0 commit comments