Skip to content

Coin Selection API

The Coin Selection API provides strategies for selecting UTXOs when building transactions. It includes built-in algorithms and extension points for custom selection logic.

  • Multiple strategies: default, largest-first, random-improve
  • Configurable limits and datum handling
  • Inline datum and datum hash aware
  • Fallback chaining for robustness
  • UTXO discovery helpers via UtxoSelector
public interface UtxoSelectionStrategy {
Set<Utxo> select(AddressIterator addressIterator,
List<Amount> outputAmounts,
String datumHash,
PlutusData inlineDatum,
Set<Utxo> utxosToExclude,
int maxUtxoSelectionLimit);
UtxoSelectionStrategy fallback();
void setIgnoreUtxosWithDatumHash(boolean ignore);
}

Utility interface to query UTXOs by predicate:

Optional<Utxo> findFirst(String address, Predicate<Utxo> predicate);
List<Utxo> findAll(String address, Predicate<Utxo> predicate);
default void setSearchByAddressVkh(boolean flag) { }
UtxoSelectionStrategy strategy =
new DefaultUtxoSelectionStrategyImpl(utxoSupplier);
strategy.setIgnoreUtxosWithDatumHash(true); // default true
Set<Utxo> selected = strategy.select(
AddressIterators.of(senderAddress),
List.of(Amount.ada(10)),
null, // datumHash
null, // inline datum
Collections.emptySet(),
20);
UtxoSelectionStrategy strategy =
new LargestFirstUtxoSelectionStrategy(utxoSupplier);
Set<Utxo> selected = strategy.select(
AddressIterators.of(senderAddress),
List.of(Amount.ada(5)),
null, null,
Collections.emptySet(),
10);
UtxoSelectionStrategy strategy =
new RandomImproveUtxoSelectionStrategy(utxoSupplier);
strategy.setIgnoreUtxosWithDatumHash(false);
Set<Utxo> selected = strategy.select(
AddressIterators.of(senderAddress),
List.of(Amount.ada(15)),
null, null,
Collections.emptySet(),
15);
UtxoSelector utxoSelector = new DefaultUtxoSelector(utxoSupplier);
List<Utxo> adaUtxos = utxoSelector.findAll(
senderAddress,
utxo -> utxo.getAmount().stream().anyMatch(a -> "lovelace".equals(a.getUnit())));
Optional<Utxo> firstScriptUtxo = utxoSelector.findFirst(
scriptAddress,
utxo -> utxo.getReferenceScriptHash() != null);

If your backend supports lookup by address verification hash (addr_vkh), enable it:

utxoSelector.setSearchByAddressVkh(true);
utxoSupplier.setSearchByAddressVkh(true); // only if supported by your supplier/backend

select takes an AddressIterator and moves to the next address until the amount is covered. To search your own list of addresses, implement AddressIterator. For an HD wallet, use Tx.from(wallet), which scans the wallet’s addresses for you.

String requiredDatumHash = "...";
PlutusData requiredInlineDatum = BytesPlutusData.of("hello");
// UTXOs with a datum hash are skipped by default, so turn that off to select by hash
strategy.setIgnoreUtxosWithDatumHash(false);
Set<Utxo> withHash = strategy.select(
AddressIterators.of(contractAddress),
List.of(Amount.ada(5)),
requiredDatumHash,
null,
Collections.emptySet(),
10);
Set<Utxo> withInline = strategy.select(
AddressIterators.of(contractAddress),
List.of(Amount.ada(5)),
null,
requiredInlineDatum,
Collections.emptySet(),
10);
Set<Utxo> exclusions = Set.of(pendingUtxo1, pendingUtxo2);
Set<Utxo> selected = strategy.select(
AddressIterators.of(senderAddress),
List.of(Amount.ada(10)),
null, null,
exclusions,
20);
CoinselectionConfig.INSTANCE.setCoinSelectionLimit(50);
int current = CoinselectionConfig.INSTANCE.getCoinSelectionLimit();
public class CustomUtxoSelectionStrategy implements UtxoSelectionStrategy {
private final UtxoSupplier utxoSupplier;
private boolean ignoreUtxosWithDatumHash = true;
public CustomUtxoSelectionStrategy(UtxoSupplier utxoSupplier) {
this.utxoSupplier = utxoSupplier;
}
// implement select(...) using your own ordering/filtering
@Override
public UtxoSelectionStrategy fallback() {
return new DefaultUtxoSelectionStrategyImpl(utxoSupplier);
}
@Override
public void setIgnoreUtxosWithDatumHash(boolean ignore) {
this.ignoreUtxosWithDatumHash = ignore;
}
}

Provide a custom strategy per composition:

UtxoSelectionStrategy custom = new LargestFirstUtxoSelectionStrategy(utxoSupplier);
Tx tx = new Tx()
.payToAddress(receiverAddress, Amount.ada(10))
.from(senderAddress);
TxResult result = new QuickTxBuilder(backendService)
.compose(tx)
.withUtxoSelectionStrategy(custom)
.withSigner(SignerProviders.signerFrom(sender))
.complete();