Skip to content

Commit 02f28da

Browse files
committed
feat: #490 Add AddressIterator support in balance, change outputs adjustment and collateral builder
1 parent e49655a commit 02f28da

File tree

3 files changed

+101
-13
lines changed

3 files changed

+101
-13
lines changed

function/src/main/java/com/bloxbean/cardano/client/function/helper/BalanceTxBuilders.java

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.bloxbean.cardano.client.function.helper;
22

3+
import com.bloxbean.cardano.client.api.AddressIterator;
4+
import com.bloxbean.cardano.client.api.common.AddressIterators;
35
import com.bloxbean.cardano.client.function.TxBuilder;
46
import com.bloxbean.cardano.client.api.util.UtxoUtil;
57
import lombok.extern.slf4j.Slf4j;
@@ -20,11 +22,31 @@ public class BalanceTxBuilders {
2022
* @return TxBuilder
2123
*/
2224
public static TxBuilder balanceTx(String changeAddress, int nSigners) {
25+
return balanceTx(AddressIterators.of(changeAddress), nSigners);
26+
}
27+
28+
/**
29+
* Function to balance an unbalanced transaction.
30+
* This is a wrapper function which invokes the following functions to create a balanced transaction
31+
* <ul>
32+
* <li>{@link FeeCalculators#feeCalculator(String, int)} </li>
33+
* <li>{@link ChangeOutputAdjustments#adjustChangeOutput(String, int)} </li>
34+
* <li>{@link CollateralBuilders#balanceCollateralOutputs()} (For transaction with collateral return)</li>
35+
* </ul>
36+
*
37+
* @param changeAddressIter An iterator for change addresses to be used for adjusting outputs.
38+
* The first address from the iterator is used as the change address
39+
* @param nSigners No of signers. This is required for accurate fee calculation.
40+
* @return A {@link TxBuilder} instance that balances the transaction during its building phase.
41+
*/
42+
public static TxBuilder balanceTx(AddressIterator changeAddressIter, int nSigners) {
2343
return (context, txn) -> {
44+
String changeAddress = changeAddressIter.getFirst().getAddress();
45+
2446
FeeCalculators.feeCalculator(changeAddress, nSigners).apply(context, txn);
2547

2648
//Incase change output goes below min ada after fee deduction
27-
ChangeOutputAdjustments.adjustChangeOutput(changeAddress, nSigners).apply(context, txn);
49+
ChangeOutputAdjustments.adjustChangeOutput(changeAddressIter.clone(), changeAddress, nSigners).apply(context, txn);
2850

2951
//If collateral return found, balance collateral outputs
3052
if (txn.getBody().getCollateralReturn() != null)
@@ -55,16 +77,40 @@ public static TxBuilder balanceTx(String changeAddress) {
5577
* <li>{@link ChangeOutputAdjustments#adjustChangeOutput(String, int)} </li>
5678
* <li>{@link CollateralBuilders#balanceCollateralOutputs()} (For transaction with collateral return)</li>
5779
* </ul>
80+
*
5881
* @param changeAddress Change output address
5982
* @param additionalSigners No of Additional signers. This is required for accurate fee calculation.
6083
* @return TxBuilder
6184
*/
6285
public static TxBuilder balanceTxWithAdditionalSigners(String changeAddress, int additionalSigners) {
86+
return balanceTxWithAdditionalSigners(AddressIterators.of(changeAddress), additionalSigners);
87+
}
88+
89+
/**
90+
* Function to balance an unbalanced transaction using Automatic Utxo Discovery with Additional Signers.
91+
* This is a wrapper function which invokes the following functions to create a balanced transaction
92+
* <ul>
93+
* <li>{@link FeeCalculators#feeCalculator(String, int)} </li>
94+
* <li>{@link ChangeOutputAdjustments#adjustChangeOutput(String, int)} </li>
95+
* <li>{@link CollateralBuilders#balanceCollateralOutputs()} (For transaction with collateral return)</li>
96+
* </ul>
97+
*
98+
* @param changeAddressIter An iterator to provide addresses for any change output.
99+
* The first address from the iterator is used as the change address
100+
* @param additionalSigners The number of additional required signers to include in the
101+
* transaction balancing process.
102+
* @return A {@link TxBuilder} function that applies the necessary adjustments to balance
103+
* the transaction, including fee calculation, change output adjustments, and
104+
* collateral balancing if applicable.
105+
*/
106+
public static TxBuilder balanceTxWithAdditionalSigners(AddressIterator changeAddressIter, int additionalSigners) {
63107
return (context, txn) -> {
64-
FeeCalculators.feeCalculator(changeAddress, UtxoUtil.getNoOfRequiredSigners(context.getUtxos()) + additionalSigners).apply(context, txn);
108+
String changeAddress = changeAddressIter.getFirst().getAddress();
109+
110+
FeeCalculators.feeCalculator(changeAddress, UtxoUtil.getNoOfRequiredSigners(context.getAllUtxos()) + additionalSigners).apply(context, txn);
65111

66112
//Incase change output goes below min ada after fee deduction
67-
ChangeOutputAdjustments.adjustChangeOutput(changeAddress, UtxoUtil.getNoOfRequiredSigners(context.getUtxos()) + additionalSigners).apply(context, txn);
113+
ChangeOutputAdjustments.adjustChangeOutput(changeAddressIter.clone(), UtxoUtil.getNoOfRequiredSigners(context.getAllUtxos()) + additionalSigners).apply(context, txn);
68114

69115
//If collateral return found, balance collateral outputs
70116
if (txn.getBody().getCollateralReturn() != null)

function/src/main/java/com/bloxbean/cardano/client/function/helper/ChangeOutputAdjustments.java

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
package com.bloxbean.cardano.client.function.helper;
22

3+
import com.bloxbean.cardano.client.address.Address;
4+
import com.bloxbean.cardano.client.api.AddressIterator;
5+
import com.bloxbean.cardano.client.api.common.AddressIterators;
36
import com.bloxbean.cardano.client.api.exception.ApiException;
47
import com.bloxbean.cardano.client.api.exception.ApiRuntimeException;
58
import com.bloxbean.cardano.client.api.model.Utxo;
9+
import com.bloxbean.cardano.client.api.model.WalletUtxo;
610
import com.bloxbean.cardano.client.coinselection.UtxoSelector;
711
import com.bloxbean.cardano.client.function.MinAdaChecker;
812
import com.bloxbean.cardano.client.function.TxBuilder;
@@ -52,7 +56,7 @@ public class ChangeOutputAdjustments {
5256
* @throws ApiRuntimeException If api call error
5357
*/
5458
public static TxBuilder adjustChangeOutput(String changeAddress) {
55-
return adjustChangeOutput(changeAddress, changeAddress, 1);
59+
return adjustChangeOutput(AddressIterators.of(changeAddress), changeAddress, 1);
5660
}
5761

5862
/**
@@ -73,7 +77,29 @@ public static TxBuilder adjustChangeOutput(String changeAddress) {
7377
* @throws ApiRuntimeException If api call error
7478
*/
7579
public static TxBuilder adjustChangeOutput(String changeAddress, int noOfSigners) {
76-
return adjustChangeOutput(changeAddress, changeAddress, noOfSigners);
80+
return adjustChangeOutput(AddressIterators.of(changeAddress), changeAddress, noOfSigners);
81+
}
82+
83+
/**
84+
* Function to adjust change output in a <code>Transaction</code> to meet min ada requirement.
85+
* Finds a change output specific to given change address.
86+
* If multiple change outputs with less than min required ada are found for the change address, it throws <code>{@link TxBuildException}</code>
87+
* Get additional utxos from change address and update the change output.
88+
* Re-calculates fee and checks min ada in change output.
89+
* Retry if required, upto 3 times.
90+
* <p>
91+
* <br>Default values:
92+
* <br> Change Output Address = First address in senderAddressIter
93+
* </p>
94+
*
95+
* @param senderAddressIter Addresses to select additional utxos. The first address in the iterator is selected as change output address.
96+
* @param noOfSigners The number of signers required for the transaction.
97+
* @return A TxBuilder instance with the modified change output configuration.
98+
*/
99+
public static TxBuilder adjustChangeOutput(AddressIterator senderAddressIter, int noOfSigners) {
100+
var changeAddress = senderAddressIter.getFirst().getAddress();
101+
102+
return adjustChangeOutput(senderAddressIter.clone(), changeAddress, noOfSigners);
77103
}
78104

79105
/**
@@ -84,14 +110,14 @@ public static TxBuilder adjustChangeOutput(String changeAddress, int noOfSigners
84110
* Re-calculates fee and checks min ada in change output.
85111
* Retry if required, upto 3 times.
86112
*
87-
* @param senderAddress Address to select additional utxos
113+
* @param senderAddressIter Addresses to select additional utxos
88114
* @param changeAddress Address for change output selection
89115
* @param noOfSigners No of required signers. Required for fee calculation after adjustment
90116
* @return <code>TxBuilder</code> function
91117
* @throws TxBuildException If multiple change outputs with less than min required ada are found for the change address.
92118
* @throws ApiRuntimeException If api call error
93119
*/
94-
public static TxBuilder adjustChangeOutput(String senderAddress, String changeAddress, int noOfSigners) {
120+
public static TxBuilder adjustChangeOutput(AddressIterator senderAddressIter, String changeAddress, int noOfSigners) {
95121
return (context, transaction) -> {
96122
int counter = 0;
97123
while (true) {
@@ -124,7 +150,7 @@ else if (outputsWithLessAda.size() > 1) {
124150

125151
//check outputs for minAda and balance if required
126152
try {
127-
adjust(context, transaction, outputsWithLessAda.get(0)._1, outputsWithLessAda.get(0)._2, senderAddress, changeAddress);
153+
adjust(context, transaction, outputsWithLessAda.get(0)._1, outputsWithLessAda.get(0)._2, senderAddressIter.clone());
128154
} catch (ApiException apiException) {
129155
throw new ApiRuntimeException("Error in api call", apiException);
130156
}
@@ -137,7 +163,7 @@ else if (outputsWithLessAda.size() > 1) {
137163
}
138164

139165
private static void adjust(TxBuilderContext context, Transaction transaction, TransactionOutput outputToAdjust, BigInteger additionalRequiredAmt,
140-
String senderAddress, String changeAddress) throws ApiException {
166+
AddressIterator senderAddressItr) throws ApiException {
141167
Objects.requireNonNull(context);
142168
Objects.requireNonNull(transaction);
143169

@@ -171,22 +197,32 @@ private static void adjust(TxBuilderContext context, Transaction transaction, Tr
171197

172198
List<Utxo> newUtxos = new ArrayList<>();
173199
UtxoSelector utxoSelector = context.getUtxoSelector();
200+
201+
Address sender = senderAddressItr.getFirst();
202+
String senderAddr = sender.getAddress();
203+
174204
//Try to find ada only utxo
175-
Optional<Utxo> utxoOptional = utxoSelector.findFirst(senderAddress, utxo ->
205+
Optional<Utxo> utxoOptional = utxoSelector.findFirst(senderAddr, utxo ->
176206
!existingUtxos.contains(utxo) && utxo.getAmount().size() == 1
177207
&& utxo.getAmount().get(0).getQuantity().compareTo(totalRequiredWithBuffer) == 1);
178208

179209
if (utxoOptional.isPresent()) {
180-
newUtxos.add(utxoOptional.get());
210+
if (sender.getDerivationPath().isPresent()) {
211+
var walletUtxo = WalletUtxo.from(utxoOptional.get());
212+
walletUtxo.setDerivationPath(sender.getDerivationPath().get());
213+
newUtxos.add(walletUtxo);
214+
} else {
215+
newUtxos.add(utxoOptional.get());
216+
}
181217
} else { //Not Found
182218
//Use utxo selection strategy
183219
List<Utxo> utxosFound = null;
184220

185221
try {
186-
utxosFound = context.getUtxoSelectionStrategy().selectUtxos(senderAddress, LOVELACE, totalRequiredWithBuffer, existingUtxos);
222+
utxosFound = context.getUtxoSelectionStrategy().selectUtxos(senderAddressItr.clone(), LOVELACE, totalRequiredWithBuffer, existingUtxos);
187223
} catch (ApiException ex) {
188224
//Not found... check without Buffer
189-
utxosFound = context.getUtxoSelectionStrategy().selectUtxos(senderAddress, LOVELACE, additionalRequiredAmt, existingUtxos);
225+
utxosFound = context.getUtxoSelectionStrategy().selectUtxos(senderAddressItr.clone(), LOVELACE, additionalRequiredAmt, existingUtxos);
190226
}
191227

192228
if (utxosFound != null && utxosFound.size() > 0)

function/src/main/java/com/bloxbean/cardano/client/function/helper/CollateralBuilders.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ public static TxBuilder collateralFrom(List<Utxo> utxos) {
3434
.index(utxo.getOutputIndex())
3535
.build();
3636
transaction.getBody().getCollateral().add(input);
37+
38+
context.addCollateralUtxo(utxo);
3739
});
3840
};
3941
}
@@ -54,6 +56,8 @@ public static TxBuilder collateralFrom(Supplier<List<Utxo>> supplier) {
5456
.index(utxo.getOutputIndex())
5557
.build();
5658
transaction.getBody().getCollateral().add(input);
59+
60+
context.addCollateralUtxo(utxo);
5761
});
5862
};
5963
}
@@ -102,6 +106,8 @@ public static TxBuilder collateralOutputs(String collateralReturnAddress, List<U
102106

103107
//Create collateral output
104108
UtxoUtil.copyUtxoValuesToOutput(collateralOutput, utxo);
109+
110+
context.addCollateralUtxo(utxo);
105111
});
106112

107113
transaction.getBody().setCollateralReturn(collateralOutput);

0 commit comments

Comments
 (0)