Cardano.CoinSelection

Module Cardano.CoinSelection provides functionality for performing coin selection based on a set of available UTXOs and a set of requested outputs. It exports functions for sorting UTXOs and performing the Largest-First coin selection algorithm as described in CIP2 (https://cips.cardano.org/cips/cip2/).

Types

type alias Context =
{ availableUtxos : List ( OutputReference, Output )
, alreadySelectedUtxos : List ( OutputReference, Output )
, targetAmount : Value
}

Holds the arguments necessary for performing coin selection.

type Error
= MaximumInputCountExceeded
| UTxOBalanceInsufficient
{ selectedUtxos : List ( OutputReference, Output )
, missingValue : Value
}

Enumerates the possible errors that can occur during coin selection.

errorToString : Error -> String

Converts an error to a human-readable string.

type alias Selection =
{ selectedUtxos : List ( OutputReference, Output )
, change : Maybe Value
}

Represents the result of a successful coin selection.

type alias Algorithm =
Int -> Context -> Result Error Selection

Alias for the function signature of a utxo selection algorithm.

Strategies

largestFirst : Int -> Context -> Result Error Selection

Implements the Largest-First coin selection algorithm as described in CIP2.

Takes a Context record containing the available UTXOs, initially selected UTXOs, requested outputs, and change address, along with an Int representing the maximum number of inputs allowed. Returns either a Error or a Selection. See https://cips.cardano.org/cips/cip2/#largestfirst

TODO: if possible, remove extraneous inputs. Indeed, when selecting later CNT, they might contain enough previous CNT too.

inOrderedList : Int -> Context -> Result Error Selection

Implements the simplest coin selection algorithm, which just adds UTxOs until the target amount is reached.

Takes a Context record containing the available UTXOs, initially selected UTXOs, requested outputs, and change address, along with an Int representing the maximum number of inputs allowed. Returns either a Error or a Selection.

Remark: the selected UTxOs are returned in reverse order for efficiency of list construction.

TODO: if possible, remove extraneous inputs. Indeed, when selecting later CNT, they might contain enough previous CNT too.

Per-address Selection

(Address -> PerAddressConfig)
-> Dict PerAddressContext
-> Result
(Dict
{ selectedUtxos : List ( OutputReference, Output )
, changeOutputs : List Output
}
)

Per-address coin selection algorithm.

This is intended to be used by the Tx building algorithm, but also available publicly. Contrary to the generic coin selection algorithm that has no notion of addresses, this algorithm performs coin selection per-address. It also provides a more fine-grained handling of the selection change outputs.

By making it per-address configurable, we enable easy customization of the selection algorithm, and the change algorithm depending on the type of address being handled. For example, the connected wallet address can pre-split the change, to keep around UTxOs for collaterals, while any other address could bundle everything into a single UTxO.

type alias PerAddressConfig =
{ selectionAlgo : Algorithm
, normalizationAlgo :
{ target : Value
, owed : Value
}
-> { normalizedTarget : Value
, normalizedOwed : Value
}
, changeAlgo : Value -> List Value
}

Configuration of the per-address section algorithm.

Since the per-address algorithm also merges the change with pre-existing owed values, you need to provide two additional algorithms:

  • normalizationAlgo: specifies how to simplify the target input and pre-owed value
  • changeAlgo: specifies how to split (or not) the returned change into multiple outputs

The normalization algorithm enables control over what to do in situations where the same address is both asked some value and returned some value. Let’s take the simplified example where intents are spending 10 ada, and returning 15 ada to the same address. If we normalize the asked and owed value, the result is just to return 5 ada, so we don’t even need to perform coin selection for this address, since the balance is strictly positive.

But sometimes you might want to force UTxO selection, even if the balance is null. For example if you want to collect all native tokens of a given policyId into a single output.

So by making both the normalization algorithm and the change algorithm configurable, we enable easily customizable behaviors, per-address. Because you might not want the same strategy for the wallet address and for other addresses.

type alias PerAddressContext =
{ availableUtxos : List ( OutputReference, Output )
, alreadySelectedUtxos : List ( OutputReference, Output )
, targetValue : Value
, alreadyOwed : Value
}

The per-address coin selection context.

In addition to all three fields of the usual selection Context, this one also contains an alreadyOwed value.

The reason is that per-address coin selection is intended to be run by the Tx building algorithm, and expects to generate actual Output returned values. These outputs will merge both the coin selection change, and value that was already destined to a given address. Knowledge of both enables better handling of change outputs and minAda for these.

Collateral Selection

type alias CollateralContext =
{ availableUtxos : List ( OutputReference, Output )
, allowedAddresses : Dict ()
, targetAmount : Natural
}

Holds the arguments necessary for performing collateral selection.

collateral : CollateralContext -> Result Error Selection

Perform collateral selection.

Only UTxOs at the provided whitelist of addresses are viable. UTxOs are picked following a prioritization list.

  • First, prioritize UTxOs with only Ada in them, and with >= ? Ada, but lowest amounts prioritized over higher amounts.
  • Second, prioritize UTxOs with >= ? Ada, and that would cost minimal fees to add, so basically no reference script, no datums, and minimal number of assets.
  • Third, everything else, prioritized with >= ? Ada first, and sorted by minimal fee cost associated.
  • Finally, all the rest, sorted by "available" ada amounts (without min Ada), with bigger available amounts prioritized over smaller amounts.