diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,3 +1,37 @@
+## [1.0.1] - 2020-05-13
+
+### Improvements
+
+* Adjusted the Largest-First algorithm to pay for outputs collectively instead
+  of individually.
+
+  The updated algorithm should now be successful at paying for any set of
+  outputs of total value **_v_** provided that the total value **_u_** of
+  available inputs satisfies **_u_** ≥ **_v_**.
+
+  The cardinality restriction requiring the number of inputs to be greater than
+  the number of outputs has been removed.
+
+  See the following commits for more details:
+
+  * `aae26dddb727779f`
+    ([PR #73](https://github.com/input-output-hk/cardano-coin-selection/pull/73))
+  * `65d5108bac63251f`
+    ([PR #76](https://github.com/input-output-hk/cardano-coin-selection/pull/76))
+
+### Fixes
+
+* Fixed a small issue with the migration algorithm that caused it to
+  occasionally return more change than actually available.
+
+  This issue only occurred in extreme situations, where the total value of the
+  available UTxO set was less than the dust threshold value.
+
+  See the following commits for more details:
+
+  * `14ef17a9647974a8`
+    ([PR #77](https://github.com/input-output-hk/cardano-coin-selection/pull/77))
+
 ## [1.0.0] - 2020-04-29
 
 Initial release.
diff --git a/cardano-coin-selection.cabal b/cardano-coin-selection.cabal
--- a/cardano-coin-selection.cabal
+++ b/cardano-coin-selection.cabal
@@ -4,10 +4,10 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: fa91aa04303b2016d424d05014b688d5845de29176adad56b44a698e849d080a
+-- hash: 955257d9cc818ae774b8798f360815648a42e3808e53b8363c5195d1864c716a
 
 name:           cardano-coin-selection
-version:        1.0.0
+version:        1.0.1
 synopsis:       Algorithms for coin selection and fee balancing.
 description:    Please see the README on GitHub at <https://github.com/input-output-hk/cardano-coin-selection>
 category:       Cardano
@@ -58,7 +58,7 @@
     , deepseq
     , quiet
     , text
-    , transformers
+    , transformers >=0.5.6.0
   if flag(release)
     ghc-options: -Werror
   default-language: Haskell2010
@@ -96,7 +96,7 @@
     , quiet
     , random
     , text
-    , transformers
+    , transformers >=0.5.6.0
     , vector
   if flag(release)
     ghc-options: -Werror
diff --git a/src/library/Cardano/CoinSelection.hs b/src/library/Cardano/CoinSelection.hs
--- a/src/library/Cardano/CoinSelection.hs
+++ b/src/library/Cardano/CoinSelection.hs
@@ -233,7 +233,7 @@
         -- ^ The generated coin selection.
     , inputsRemaining :: CoinMap i
         -- ^ The set of inputs that were __not__ selected.
-    }
+    } deriving (Eq, Show)
 
 -- | A __coin selection__ is the basis for a /transaction/.
 --
diff --git a/src/library/Cardano/CoinSelection/Algorithm/LargestFirst.hs b/src/library/Cardano/CoinSelection/Algorithm/LargestFirst.hs
--- a/src/library/Cardano/CoinSelection/Algorithm/LargestFirst.hs
+++ b/src/library/Cardano/CoinSelection/Algorithm/LargestFirst.hs
@@ -1,4 +1,6 @@
+{-# LANGUAGE NamedFieldPuns #-}
 {-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeApplications #-}
 {-# LANGUAGE TypeFamilies #-}
 
 -- |
@@ -15,29 +17,28 @@
 import Prelude
 
 import Cardano.CoinSelection
-    ( CoinMapEntry (..)
+    ( CoinMap (..)
+    , CoinMapEntry (..)
     , CoinSelection (..)
     , CoinSelectionAlgorithm (..)
     , CoinSelectionError (..)
     , CoinSelectionLimit (..)
     , CoinSelectionParameters (..)
     , CoinSelectionResult (..)
-    , InputCountInsufficientError (..)
     , InputLimitExceededError (..)
     , InputValueInsufficientError (..)
-    , InputsExhaustedError (..)
     , coinMapFromList
     , coinMapToList
     , coinMapValue
     )
-import Control.Monad
-    ( foldM )
 import Control.Monad.Trans.Except
     ( ExceptT (..), throwE )
+import Data.Function
+    ( (&) )
 import Data.Ord
     ( Down (..) )
-import Internal.Coin
-    ( Coin )
+import Data.Word
+    ( Word16 )
 
 import qualified Data.Foldable as F
 import qualified Data.List as L
@@ -45,236 +46,102 @@
 
 -- | An implementation of the __Largest-First__ coin selection algorithm.
 --
--- = Overview
---
--- The __Largest-First__ algorithm processes outputs in /descending order of/
--- /value/, from /largest/ to /smallest/.
---
--- For each output, it repeatedly selects the /largest/ remaining unspent UTxO
--- entry until the value of selected entries is greater than or equal to the
--- value of that output.
---
--- = State Maintained by the Algorithm
---
--- At all stages of processing, the algorithm maintains:
---
---  1.  A __/remaining UTxO list/__
---
---      This is initially equal to the given /initial UTxO set/ parameter,
---      sorted into /descending order of coin value/.
---
---      The /head/ of the list is always the remaining UTxO entry with the
---      /largest coin value/.
---
---      Entries are incrementally removed from the /head/ of the list as the
---      algorithm proceeds, until the list is empty.
---
---  2.  An __/unpaid output list/__
---
---      This is initially equal to the given /output list/ parameter, sorted
---      into /descending order of coin value/.
---
---      The /head/ of the list is always the unpaid output with the
---      /largest coin value/.
---
---      Entries are incrementally removed from the /head/ of the list as the
---      algorithm proceeds, until the list is empty.
---
---  3.  An __/accumulated coin selection/__
---
---      This is initially /empty/.
---
---      Entries are incrementally added as each output is paid for, until the
---      /unpaid output list/ is empty.
---
--- = Cardinality Rules
---
--- The algorithm requires that:
---
---  1.  Each output from the given /output list/ is paid for by /one or more/
---      entries from the /initial UTxO set/.
---
---  2.  Each entry from the /initial UTxO set/ is used to pay for /at most one/
---      output from the given /output list/.
---
---      (A single UTxO entry __cannot__ be used to pay for multiple outputs.)
---
--- = Order of Processing
---
--- The algorithm proceeds according to the following sequence of steps:
---
---  *   /Step 1/
---
---      Remove a single /unpaid output/ from the head of the
---      /unpaid output list/.
---
---  *   /Step 2/
---
---      Repeatedly remove UTxO entries from the head of the
---      /remaining UTxO list/ until the total value of entries removed is
---      /greater than or equal to/ the value of the /removed output/.
---
---  *   /Step 3/
---
---      Use the /removed UTxO entries/ to pay for the /removed output/.
---
---      This is achieved by:
---
---      *  adding the /removed UTxO entries/ to the 'inputs' field of the
---         /accumulated coin selection/.
---      *  adding the /removed output/ to the 'outputs' field of the
---         /accumulated coin selection/.
---
---  *   /Step 4/
---
---      If the /total value/ of the /removed UTxO entries/ is greater than the
---      value of the /removed output/, generate a coin whose value is equal to
---      the exact difference, and add it to the 'change' field of the
---      /accumulated coin selection/.
---
---  *   /Step 5/
---
---      If the /unpaid output list/ is empty, __terminate__ here.
+-- The Largest-First coin selection algorithm considers available inputs in
+-- /descending/ order of value, from /largest/ to /smallest/.
 --
---      Otherwise, return to /Step 1/.
+-- When applied to a set of requested outputs, the algorithm repeatedly selects
+-- entries from the available inputs set until the total value of selected
+-- entries is greater than or equal to the total value of requested outputs.
 --
--- = Termination
+-- === Change Values
 --
--- The algorithm terminates __successfully__ if the /remaining UTxO list/ is
--- not depleted before the /unpaid output list/ can be fully depleted (i.e., if
--- all the outputs have been paid for).
+-- If the total value of selected inputs is /greater than/ the total value of
+-- all requested outputs, the 'change' set of the resulting selection will
+-- contain /a single coin/ with the excess value.
 --
--- The /accumulated coin selection/ and /remaining UTxO list/ are returned to
--- the caller.
+-- If the total value of selected inputs is /exactly equal to/ the total value
+-- of all requested outputs, the 'change' set of the resulting selection will
+-- be /empty/.
 --
 -- === Failure Modes
 --
 -- The algorithm terminates with an __error__ if:
 --
---  1.  The /total value/ of the initial UTxO set (the amount of money
---      /available/) is /less than/ the total value of the output list (the
+--  1.  The /total value/ of 'inputsAvailable' (the amount of money
+--      /available/) is /less than/ the total value of 'outputsRequested' (the
 --      amount of money /required/).
 --
 --      See: __'InputValueInsufficientError'__.
 --
---  2.  The /number/ of entries in the initial UTxO set is /smaller than/ the
---      number of requested outputs.
---
---      Due to the nature of the algorithm, /at least one/ UTxO entry is
---      required /for each/ output.
---
---      See: __'InputCountInsufficientError'__.
---
---  3.  Due to the particular /distribution/ of values within the initial UTxO
---      set, the algorithm depletes all entries from the UTxO set /before/ it
---      is able to pay for all requested outputs.
---
---      See: __'InputsExhaustedError'__.
---
---  4.  The /number/ of UTxO entries needed to pay for the requested outputs
---      would /exceed/ the upper limit specified by 'limit'.
+--  2.  It is not possible to cover the total value of 'outputsRequested'
+--      without selecting a number of inputs from 'inputsAvailable' that
+--      would exceed the maximum defined by 'limit'.
 --
 --      See: __'InputLimitExceededError'__.
 --
 -- @since 1.0.0
 largestFirst
-    :: (Ord i, Ord o, Monad m)
+    :: (Ord i, Monad m)
     => CoinSelectionAlgorithm i o m
 largestFirst = CoinSelectionAlgorithm payForOutputs
 
 payForOutputs
-    :: (Ord i, Ord o, Monad m)
+    :: forall i o m . (Ord i, Monad m)
     => CoinSelectionParameters i o
     -> ExceptT CoinSelectionError m (CoinSelectionResult i o)
-payForOutputs params =
-    case foldM payForOutput (utxoDescending, mempty) outputsDescending of
-        Just (utxoRemaining, selection) ->
-            pure $ CoinSelectionResult selection $ coinMapFromList utxoRemaining
-        Nothing ->
-            throwE errorCondition
+payForOutputs params
+    | amountAvailable < amountRequired =
+        throwE
+            $ InputValueInsufficient
+            $ InputValueInsufficientError amountAvailable amountRequired
+    | length inputsSelected > inputCountMax =
+        throwE
+            $ InputLimitExceeded
+            $ InputLimitExceededError
+            $ fromIntegral inputCountMax
+    | otherwise =
+        pure CoinSelectionResult {coinSelection, inputsRemaining}
   where
-    errorCondition
-      | amountAvailable < amountRequested =
-          InputValueInsufficient $
-              InputValueInsufficientError
-                  amountAvailable amountRequested
-      | utxoCount < outputCount =
-          InputCountInsufficient $
-              InputCountInsufficientError
-                  utxoCount outputCount
-      | utxoCount <= inputCountMax =
-          InputsExhausted
-              InputsExhaustedError
-      | otherwise =
-          InputLimitExceeded $
-              InputLimitExceededError $
-                  fromIntegral inputCountMax
     amountAvailable =
         coinMapValue $ inputsAvailable params
-    amountRequested =
+    amountRequired =
         coinMapValue $ outputsRequested params
-    inputCountMax = fromIntegral
-        $ calculateLimit (limit params)
-        $ fromIntegral outputCount
-    outputCount =
-        fromIntegral $ length $ coinMapToList $ outputsRequested params
-    outputsDescending =
-        L.sortOn (Down . entryValue) $ coinMapToList $ outputsRequested params
-    utxoCount =
-        fromIntegral $ L.length $ coinMapToList $ inputsAvailable params
-    utxoDescending =
-        take (fromIntegral inputCountMax)
-            $ L.sortOn (Down . entryValue)
-            $ coinMapToList
-            $ inputsAvailable params
+    coinSelection = CoinSelection
+        { inputs =
+            inputsSelected
+        , outputs =
+            outputsRequested params
+        , change = filter (> C.zero)
+            $ F.toList
+            $ coinMapValue inputsSelected `C.sub` amountRequired
+        }
+    inputsAvailableDescending :: [CoinMapEntry i]
+    inputsAvailableDescending = inputsAvailable params
+        & coinMapToList
+        & L.sortOn (Down . entryValue)
+    inputCountMax :: Int
+    inputCountMax = outputsRequested params
+        & coinMapToList
+        & length
+        & fromIntegral @Int @Word16
+        & calculateLimit (limit params)
+        & fromIntegral @Word16 @Int
+    inputsSelected :: CoinMap i
+    inputsSelected = inputsAvailableDescending
+        & fmap entryValue
+        & scanl1 (<>)
+        & takeUntil (>= amountRequired)
+        & zip inputsAvailableDescending
+        & fmap fst
+        & coinMapFromList
+    inputsRemaining :: CoinMap i
+    inputsRemaining = inputsAvailableDescending
+        & drop (length inputsSelected)
+        & coinMapFromList
 
--- | Attempts to pay for a /single transaction output/ by selecting the
---   /smallest possible/ number of entries from the /head/ of the given
---   UTxO list.
---
--- Returns a /reduced/ list of UTxO entries, and a coin selection that is
--- /updated/ to include the payment.
---
--- If the total value of entries in the given UTxO list is /less than/ the
--- required output amount, this function will return 'Nothing'.
---
-payForOutput
-    :: forall i o . (Ord i, Ord o)
-    => ([CoinMapEntry i], CoinSelection i o)
-    -> CoinMapEntry o
-    -> Maybe ([CoinMapEntry i], CoinSelection i o)
-payForOutput (utxoAvailable, currentSelection) out =
-    coverTarget utxoAvailable mempty
-  where
-    coverTarget
-        :: [CoinMapEntry i]
-        -> [CoinMapEntry i]
-        -> Maybe ([CoinMapEntry i], CoinSelection i o)
-    coverTarget utxoRemaining utxoSelected
-        | valueSelected >= valueTarget = Just
-            -- We've selected enough to cover the target, so stop here.
-            ( utxoRemaining
-            , currentSelection <> CoinSelection
-                { inputs  = coinMapFromList utxoSelected
-                , outputs = coinMapFromList [out]
-                , change  = filter (> C.zero)
-                    (F.toList $ valueSelected `C.sub` valueTarget)
-                }
-            )
-        | otherwise =
-            -- We haven't yet selected enough to cover the target, so attempt
-            -- to select a little more and then continue.
-            case utxoRemaining of
-                utxoEntry : utxoRemaining' ->
-                    coverTarget utxoRemaining' (utxoEntry : utxoSelected)
-                [] ->
-                    -- The UTxO has been exhausted, so stop here.
-                    Nothing
-      where
-        valueTarget
-            = entryValue out
-        valueSelected
-            = sumEntries utxoSelected
+--------------------------------------------------------------------------------
+-- Utilities
+--------------------------------------------------------------------------------
 
-sumEntries :: [CoinMapEntry a] -> Coin
-sumEntries entries = mconcat $ entryValue <$> entries
+takeUntil :: (a -> Bool) -> [a] -> [a]
+takeUntil p = foldr (\x ys -> x : if p x then [] else ys) []
diff --git a/src/library/Cardano/CoinSelection/Algorithm/Migration.hs b/src/library/Cardano/CoinSelection/Algorithm/Migration.hs
--- a/src/library/Cardano/CoinSelection/Algorithm/Migration.hs
+++ b/src/library/Cardano/CoinSelection/Algorithm/Migration.hs
@@ -33,6 +33,7 @@
     , CoinSelectionLimit (..)
     , coinMapFromList
     , coinMapToList
+    , coinMapValue
     , sumChange
     , sumInputs
     )
@@ -42,13 +43,14 @@
     , FeeBalancingPolicy (..)
     , FeeEstimator (..)
     , FeeOptions (..)
+    , isDust
     )
 import Control.Monad.Trans.State
     ( State, evalState, get, put )
 import Data.List.NonEmpty
     ( NonEmpty ((:|)) )
 import Data.Maybe
-    ( fromMaybe, mapMaybe )
+    ( fromMaybe )
 import Data.Word
     ( Word16 )
 import GHC.Generics
@@ -113,19 +115,20 @@
     -- Note that the selection may look a bit weird at first sight as it has
     -- no outputs (we are paying everything to ourselves!).
     mkCoinSelection :: [CoinMapEntry i] -> CoinSelection i o
-    mkCoinSelection inps = CoinSelection
-        { inputs = coinMapFromList inps
-        , outputs = mempty
-        , change =
-            let chgs = mapMaybe (noDust . entryValue) inps
-            in if null chgs then [threshold] else chgs
-        }
+    mkCoinSelection inputEntries = CoinSelection {inputs, outputs, change}
       where
-        threshold = unDustThreshold dustThreshold
-        noDust :: Coin -> Maybe Coin
-        noDust c
-            | c < threshold = Nothing
-            | otherwise = Just c
+        inputs = coinMapFromList inputEntries
+        outputs = mempty
+        change
+            | null nonDustInputCoins && totalInputValue >= smallestNonDustCoin =
+                [smallestNonDustCoin]
+            | otherwise =
+                nonDustInputCoins
+        nonDustInputCoins = filter
+            (not . isDust dustThreshold)
+            (entryValue <$> inputEntries)
+        smallestNonDustCoin = C.succ $ unDustThreshold dustThreshold
+        totalInputValue = coinMapValue inputs
 
     -- | Attempt to balance the coin selection by reducing or increasing the
     -- change values based on the computed fees.
diff --git a/src/library/Cardano/CoinSelection/Fee.hs b/src/library/Cardano/CoinSelection/Fee.hs
--- a/src/library/Cardano/CoinSelection/Fee.hs
+++ b/src/library/Cardano/CoinSelection/Fee.hs
@@ -31,6 +31,7 @@
 
       -- * Dust Processing
     , DustThreshold (..)
+    , isDust
     , coalesceDust
 
       -- # Internal Functions
@@ -119,6 +120,17 @@
     deriving stock (Eq, Generic, Ord)
     deriving Show via (Quiet DustThreshold)
 
+-- | Returns 'True' if and only if the given 'Coin' is a __dust coin__
+--   according to the given 'DustThreshold'.
+--
+-- A coin is considered to be a dust coin if it is /less than or equal to/
+-- the threshold.
+--
+-- See 'DustThreshold'.
+--
+isDust :: DustThreshold -> Coin -> Bool
+isDust (DustThreshold dt) c = c <= dt
+
 -- | Provides a function capable of __estimating__ the transaction fee required
 --   for a given coin selection, according to the rules of a particular
 --   blockchain.
@@ -618,10 +630,10 @@
 -- >>> all (/= Coin 0) (coalesceDust threshold coins)
 --
 coalesceDust :: DustThreshold -> NonEmpty Coin -> [Coin]
-coalesceDust (DustThreshold threshold) coins =
+coalesceDust threshold coins =
     splitCoin valueToDistribute coinsToKeep
   where
-    (coinsToKeep, coinsToRemove) = NE.partition (> threshold) coins
+    (coinsToRemove, coinsToKeep) = NE.partition (isDust threshold) coins
     valueToDistribute = F.fold coinsToRemove
 
 -- Splits up the given coin of value __@v@__, distributing its value over the
diff --git a/src/test/Cardano/CoinSelection/Algorithm/LargestFirstSpec.hs b/src/test/Cardano/CoinSelection/Algorithm/LargestFirstSpec.hs
--- a/src/test/Cardano/CoinSelection/Algorithm/LargestFirstSpec.hs
+++ b/src/test/Cardano/CoinSelection/Algorithm/LargestFirstSpec.hs
@@ -1,10 +1,13 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE NumericUnderscores #-}
 {-# LANGUAGE TypeApplications #-}
 {-# OPTIONS_GHC -fno-warn-orphans #-}
 
 module Cardano.CoinSelection.Algorithm.LargestFirstSpec
-    ( spec
+    ( isValidLargestFirstError
+    , spec
     ) where
 
 import Prelude
@@ -18,16 +21,16 @@
     , CoinSelectionLimit (..)
     , CoinSelectionParameters (..)
     , CoinSelectionResult (..)
-    , InputCountInsufficientError (..)
     , InputLimitExceededError (..)
     , InputValueInsufficientError (..)
-    , InputsExhaustedError (..)
+    , coinMapFromList
     , coinMapToList
+    , coinMapValue
     )
 import Cardano.CoinSelection.Algorithm.LargestFirst
     ( largestFirst )
 import Cardano.CoinSelectionSpec
-    ( CoinSelProp (..)
+    ( CoinSelectionData (..)
     , CoinSelectionFixture (..)
     , CoinSelectionTestResult (..)
     , coinSelectionUnitTest
@@ -40,22 +43,26 @@
     ( runExceptT )
 import Data.Either
     ( isRight )
+import Data.Function
+    ( (&) )
 import Data.Functor.Identity
     ( Identity (runIdentity) )
 import Test.Hspec
-    ( Spec, describe, it, shouldSatisfy )
+    ( Spec, describe, it, shouldBe, shouldSatisfy )
 import Test.QuickCheck
-    ( Property, property, (==>) )
+    ( Property, checkCoverage, cover, property, withMaxSuccess, (.&&.), (==>) )
 
 import qualified Data.List as L
 import qualified Data.Map.Strict as Map
 import qualified Data.Set as Set
+import qualified Internal.Coin as C
 
 spec :: Spec
 spec = do
     describe "Coin selection: largest-first algorithm: unit tests" $ do
 
-        coinSelectionUnitTest largestFirst ""
+        coinSelectionUnitTest largestFirst
+            "Expect success: case #1"
             (Right $ CoinSelectionTestResult
                 { rsInputs = [17]
                 , rsChange = []
@@ -67,7 +74,8 @@
                 , txOutputs = [17]
                 })
 
-        coinSelectionUnitTest largestFirst ""
+        coinSelectionUnitTest largestFirst
+            "Expect success: case #2"
             (Right $ CoinSelectionTestResult
                 { rsInputs = [17]
                 , rsChange = [16]
@@ -79,7 +87,8 @@
                 , txOutputs = [1]
                 })
 
-        coinSelectionUnitTest largestFirst ""
+        coinSelectionUnitTest largestFirst
+            "Expect success: case #3"
             (Right $ CoinSelectionTestResult
                 { rsInputs = [12, 17]
                 , rsChange = [11]
@@ -91,7 +100,8 @@
                 , txOutputs = [18]
                 })
 
-        coinSelectionUnitTest largestFirst ""
+        coinSelectionUnitTest largestFirst
+            "Expect success: case #4"
             (Right $ CoinSelectionTestResult
                 { rsInputs = [10, 12, 17]
                 , rsChange = [9]
@@ -103,10 +113,11 @@
                 , txOutputs = [30]
                 })
 
-        coinSelectionUnitTest largestFirst ""
+        coinSelectionUnitTest largestFirst
+            "Expect success: case #5"
             (Right $ CoinSelectionTestResult
-                { rsInputs = [6,10,5]
-                , rsChange = [5,4]
+                { rsInputs = [6,10]
+                , rsChange = [4]
                 , rsOutputs = [11,1]
                 })
             (CoinSelectionFixture
@@ -116,118 +127,165 @@
                 })
 
         coinSelectionUnitTest largestFirst
-            "UTxO balance not sufficient"
-            (Left $ InputValueInsufficient $ InputValueInsufficientError
-                (unsafeCoin @Int 39) (unsafeCoin @Int 40))
+            "Expect success: case #6"
+            (Right $ CoinSelectionTestResult
+                { rsInputs = [12,17,20]
+                , rsChange = [6]
+                , rsOutputs = [1,1,1,40]
+                })
             (CoinSelectionFixture
                 { maxNumOfInputs = 100
-                , utxoInputs = [12,10,17]
-                , txOutputs = [40]
+                , utxoInputs = [12,20,17]
+                , txOutputs = [40,1,1,1]
                 })
 
         coinSelectionUnitTest largestFirst
-            "UTxO balance not sufficient, and not fragmented enough"
-            (Left $ InputValueInsufficient $ InputValueInsufficientError
-                (unsafeCoin @Int 39) (unsafeCoin @Int 43))
+            "Expect success: case #7"
+            (Right $ CoinSelectionTestResult
+                { rsInputs = [12,17,20]
+                , rsChange = [8]
+                , rsOutputs = [1,40]
+                })
             (CoinSelectionFixture
                 { maxNumOfInputs = 100
-                , utxoInputs = [12,10,17]
-                , txOutputs = [40,1,1,1]
+                , utxoInputs = [12,20,17]
+                , txOutputs = [40, 1]
                 })
 
         coinSelectionUnitTest largestFirst
-            "UTxO balance sufficient, but not fragmented enough"
-            (Left $ InputCountInsufficient $ InputCountInsufficientError 3 4)
+            "Expect success: case #8"
+            (Right $ CoinSelectionTestResult
+                { rsInputs = [10,20,20]
+                , rsChange = [3]
+                , rsOutputs = [6,41]
+                })
             (CoinSelectionFixture
                 { maxNumOfInputs = 100
-                , utxoInputs = [12,20,17]
-                , txOutputs = [40,1,1,1]
+                , utxoInputs = [20,20,10,5]
+                , txOutputs = [41, 6]
                 })
 
         coinSelectionUnitTest largestFirst
-            "UTxO balance sufficient, fragmented enough, but single output \
-            \depletes all UTxO entries"
-            (Left (InputsExhausted InputsExhaustedError))
+            "Expect success: case #9"
+            (Right $ CoinSelectionTestResult
+                { rsInputs = [6,10]
+                , rsChange = [4]
+                , rsOutputs = [1,11]
+                })
             (CoinSelectionFixture
-                { maxNumOfInputs = 100
-                , utxoInputs = [12,20,17]
-                , txOutputs = [40, 1]
+                { maxNumOfInputs = 2
+                , utxoInputs = [1,2,10,6,5]
+                , txOutputs = [11, 1]
                 })
 
         coinSelectionUnitTest largestFirst
-            "UTxO balance sufficient, fragmented enough, but single output \
-            \depletes all UTxO entries"
-            (Left (InputsExhausted InputsExhaustedError))
+            "Expect success: fewer inputs than outputs: case #1"
+            (Right $ CoinSelectionTestResult
+                { rsInputs = [100]
+                , rsOutputs = [1,2,3,4]
+                , rsChange = [90]
+                })
             (CoinSelectionFixture
-                { maxNumOfInputs = 100
-                , utxoInputs = [20,20,10,5]
-                , txOutputs = [41, 6]
+                { maxNumOfInputs = 1000
+                , utxoInputs = [100,100]
+                , txOutputs = [1,2,3,4]
                 })
 
         coinSelectionUnitTest largestFirst
-            "UTxO balance sufficient, fragmented enough, but maximum input \
-            \count exceeded"
-            (Left $ InputLimitExceeded $ InputLimitExceededError 9)
+            "Expect success: fewer inputs than outputs: case #2"
+            (Right $ CoinSelectionTestResult
+                { rsInputs = [100]
+                , rsOutputs = [1,2,3,4]
+                , rsChange = [90]
+                })
             (CoinSelectionFixture
-                { maxNumOfInputs = 9
-                , utxoInputs = replicate 100 1
+                { maxNumOfInputs = 1000
+                , utxoInputs = [100,10]
+                , txOutputs = [1,2,3,4]
+                })
+
+        coinSelectionUnitTest largestFirst
+            "Expect success: fewer inputs than outputs: case #3"
+            (Right $ CoinSelectionTestResult
+                { rsInputs = [10]
+                , rsOutputs = [1,2,3,4]
+                , rsChange = []
+                })
+            (CoinSelectionFixture
+                { maxNumOfInputs = 1000
+                , utxoInputs = [10,10]
+                , txOutputs = [1,2,3,4]
+                })
+
+        coinSelectionUnitTest largestFirst
+            "Expect success: fewer inputs than outputs: case #4"
+            (Right $ CoinSelectionTestResult
+                { rsInputs = [100]
+                , rsOutputs = replicate 100 1
+                , rsChange = []
+                })
+            (CoinSelectionFixture
+                { maxNumOfInputs = 1
+                , utxoInputs = [100]
                 , txOutputs = replicate 100 1
                 })
 
         coinSelectionUnitTest largestFirst
-            "UTxO balance sufficient, fragmented enough, but maximum input \
-            \count exceeded"
-            (Left $ InputLimitExceeded $ InputLimitExceededError 9)
+            "UTxO balance not sufficient: case #1"
+            (Left $ InputValueInsufficient $ InputValueInsufficientError
+                (unsafeCoin @Int 39) (unsafeCoin @Int 40))
             (CoinSelectionFixture
-                { maxNumOfInputs = 9
-                , utxoInputs = replicate 100 1
-                , txOutputs = replicate 10 10
+                { maxNumOfInputs = 100
+                , utxoInputs = [12,10,17]
+                , txOutputs = [40]
                 })
 
         coinSelectionUnitTest largestFirst
-            "UTxO balance sufficient, fragmented enough, but maximum input \
-            \count exceeded"
-            (Left $ InputLimitExceeded $ InputLimitExceededError 2)
+            "UTxO balance not sufficient: case #2"
+            (Left $ InputValueInsufficient $ InputValueInsufficientError
+                (unsafeCoin @Int 39) (unsafeCoin @Int 43))
             (CoinSelectionFixture
-                { maxNumOfInputs = 2
-                , utxoInputs = [1,2,10,6,5]
-                , txOutputs = [11, 1]
+                { maxNumOfInputs = 100
+                , utxoInputs = [12,10,17]
+                , txOutputs = [40,1,1,1]
                 })
 
+        coinSelectionUnitTest largestFirst
+            "UTxO balance sufficient, but maximum input count exceeded"
+            (Left $ InputLimitExceeded $ InputLimitExceededError 9)
+            (CoinSelectionFixture
+                { maxNumOfInputs = 9
+                , utxoInputs = replicate 100 1
+                , txOutputs = replicate 100 1
+                })
+
     describe "Coin selection: largest-first algorithm: properties" $ do
 
-        it "forall (UTxO, NonEmpty TxOut), there's at least as many selected \
-            \inputs as there are requested outputs"
-            (property $ propAtLeast @TxIn @Address)
         it "forall (UTxO, NonEmpty TxOut), for all selected input, there's no \
             \bigger input in the UTxO that is not already in the selected \
             \inputs"
             (property $ propInputDecreasingOrder @TxIn @Address)
 
+        it "The algorithm selects just enough inputs and no more."
+            (property
+                $ withMaxSuccess 10_000
+                $ propSelectionMinimal @Int @Int)
+
+        it "The algorithm produces the correct set of change."
+            (checkCoverage
+                $ property
+                $ withMaxSuccess 10_000
+                $ propChangeCorrect @Int @Int)
+
 --------------------------------------------------------------------------------
 -- Properties
 --------------------------------------------------------------------------------
 
-propAtLeast
-    :: (Ord i, Ord o)
-    => CoinSelProp i o
-    -> Property
-propAtLeast (CoinSelProp utxo txOuts) =
-    isRight selection ==>
-        let Right (CoinSelectionResult s _) = selection in
-        prop s
-  where
-    prop (CoinSelection inps _ _) =
-        length inps `shouldSatisfy` (>= length txOuts)
-    selection = runIdentity $ runExceptT $ selectCoins largestFirst
-        $ CoinSelectionParameters utxo txOuts selectionLimit
-    selectionLimit = CoinSelectionLimit $ const 100
-
 propInputDecreasingOrder
-    :: (Ord i, Ord o)
-    => CoinSelProp i o
+    :: Ord i
+    => CoinSelectionData i o
     -> Property
-propInputDecreasingOrder (CoinSelProp utxo txOuts) =
+propInputDecreasingOrder (CoinSelectionData utxo txOuts) =
     isRight selection ==>
         let Right (CoinSelectionResult s _) = selection in
         prop s
@@ -245,3 +303,73 @@
         $ selectCoins largestFirst
         $ CoinSelectionParameters utxo txOuts selectionLimit
     selectionLimit = CoinSelectionLimit $ const 100
+
+-- Confirm that a selection is minimal by removing the smallest entry from the
+-- inputs and verifying that the reduced input total is no longer enough to pay
+-- for the total value of all outputs.
+propSelectionMinimal
+    :: Ord i => CoinSelectionData i o -> Property
+propSelectionMinimal (CoinSelectionData inpsAvailable outsRequested) =
+    isRight result ==>
+        let Right (CoinSelectionResult selection _) = result in
+        prop selection
+  where
+    prop (CoinSelection inputsSelected _ _) =
+        (coinMapValue inputsSelected
+            `shouldSatisfy` (>= coinMapValue outsRequested))
+        .&&.
+        (coinMapValue inputsReduced
+            `shouldSatisfy` (< coinMapValue outsRequested))
+      where
+        -- The set of selected inputs with the smallest entry removed.
+        inputsReduced = inputsSelected
+            & coinMapToList
+            & L.sortOn entryValue
+            & L.drop 1
+            & coinMapFromList
+    result = runIdentity
+        $ runExceptT
+        $ selectCoins largestFirst
+        $ CoinSelectionParameters inpsAvailable outsRequested
+        $ CoinSelectionLimit $ const 1000
+
+-- Verify that the algorithm generates the correct set of change.
+propChangeCorrect
+    :: Ord i => CoinSelectionData i o -> Property
+propChangeCorrect (CoinSelectionData inpsAvailable outsRequested) =
+    isRight result ==>
+        let Right (CoinSelectionResult selection _) = result in
+        prop selection
+  where
+    prop (CoinSelection inpsSelected _ changeGenerated) =
+        cover 8 (amountSelected > amountRequired)
+            "amountSelected > amountRequired" $
+        cover 1 (amountSelected == amountRequired)
+            "amountSelected = amountRequired" $
+        if amountSelected > amountRequired then
+            changeGenerated `shouldBe`
+                [amountSelected `C.distance` amountRequired]
+        else
+            changeGenerated `shouldSatisfy` null
+      where
+        amountSelected = coinMapValue inpsSelected
+        amountRequired = coinMapValue outsRequested
+    result = runIdentity
+        $ runExceptT
+        $ selectCoins largestFirst
+        $ CoinSelectionParameters inpsAvailable outsRequested
+        $ CoinSelectionLimit $ const 1000
+
+--------------------------------------------------------------------------------
+-- Utilities
+--------------------------------------------------------------------------------
+
+-- Returns true if (and only if) the given error value is one that can be
+-- thrown by the Largest-First algorithm.
+--
+isValidLargestFirstError :: CoinSelectionError -> Bool
+isValidLargestFirstError = \case
+    InputLimitExceeded     _ -> True
+    InputValueInsufficient _ -> True
+    InputCountInsufficient _ -> False
+    InputsExhausted        _ -> False
diff --git a/src/test/Cardano/CoinSelection/Algorithm/MigrationSpec.hs b/src/test/Cardano/CoinSelection/Algorithm/MigrationSpec.hs
--- a/src/test/Cardano/CoinSelection/Algorithm/MigrationSpec.hs
+++ b/src/test/Cardano/CoinSelection/Algorithm/MigrationSpec.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE NumericUnderscores #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TypeApplications #-}
@@ -117,27 +118,27 @@
 
     describe "selectCoins properties" $ do
         it "No coin selection has outputs" $
-            property $ withMaxSuccess 10000 $ prop_onlyChangeOutputs
+            property $ withMaxSuccess 10_000 $ prop_onlyChangeOutputs
                 @(Wrapped TxIn) @Address
 
-        it "Every coin in the selection change >= minimum threshold coin" $
-            property $ withMaxSuccess 10000 $ prop_noLessThanThreshold
+        it "Every coin in the selection change > dust threshold" $
+            property $ withMaxSuccess 10_000 $ prop_allAboveThreshold
                 @(Wrapped TxIn) @Address
 
         it "Total input UTxO value >= sum of selection change coins" $
-            property $ withMaxSuccess 10000 $ prop_inputsGreaterThanOutputs
+            property $ withMaxSuccess 10_000 $ prop_inputsGreaterThanOutputs
                 @(Wrapped TxIn) @Address
 
         it "Every selection input is unique" $
-            property $ withMaxSuccess 10000 $ prop_inputsAreUnique
+            property $ withMaxSuccess 10_000 $ prop_inputsAreUnique
                 @(Wrapped TxIn) @Address
 
         it "Every selection input is a member of the UTxO" $
-            property $ withMaxSuccess 10000 $ prop_inputsStillInUTxO
+            property $ withMaxSuccess 10_000 $ prop_inputsStillInUTxO
                 @(Wrapped TxIn) @Address
 
         it "Every coin selection is well-balanced" $
-            property $ withMaxSuccess 10000 $ prop_wellBalanced
+            property $ withMaxSuccess 10_000 $ prop_wellBalanced
                 @(Wrapped TxIn) @Address
 
     describe "selectCoins regressions" $ do
@@ -177,18 +178,18 @@
             coinMapToList . outputs =<< selectCoins feeOpts batchSize utxo
     property (allOutputs `shouldSatisfy` null)
 
--- | Every coin in the selection change >= minimum threshold coin
-prop_noLessThanThreshold
+-- | Every coin in the selection change > dust threshold
+prop_allAboveThreshold
     :: forall i o . (Ord i, Ord o)
     => FeeOptions i o
     -> BatchSize
     -> CoinMap i
     -> Property
-prop_noLessThanThreshold feeOpts batchSize utxo = do
+prop_allAboveThreshold feeOpts batchSize utxo = do
     let allChange = change
             =<< selectCoins feeOpts batchSize utxo
     let undersizedCoins =
-            filter (< threshold) allChange
+            filter (<= threshold) allChange
     property (undersizedCoins `shouldSatisfy` null)
   where
     threshold = unDustThreshold $ dustThreshold feeOpts
diff --git a/src/test/Cardano/CoinSelection/Algorithm/RandomImproveSpec.hs b/src/test/Cardano/CoinSelection/Algorithm/RandomImproveSpec.hs
--- a/src/test/Cardano/CoinSelection/Algorithm/RandomImproveSpec.hs
+++ b/src/test/Cardano/CoinSelection/Algorithm/RandomImproveSpec.hs
@@ -1,5 +1,6 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE TypeApplications #-}
 {-# OPTIONS_GHC -fno-warn-orphans #-}
 
@@ -23,10 +24,12 @@
     )
 import Cardano.CoinSelection.Algorithm.LargestFirst
     ( largestFirst )
+import Cardano.CoinSelection.Algorithm.LargestFirstSpec
+    ( isValidLargestFirstError )
 import Cardano.CoinSelection.Algorithm.RandomImprove
     ( randomImprove )
 import Cardano.CoinSelectionSpec
-    ( CoinSelProp (..)
+    ( CoinSelectionData (..)
     , CoinSelectionFixture (..)
     , CoinSelectionTestResult (..)
     , coinSelectionUnitTest
@@ -40,13 +43,13 @@
 import Crypto.Random.Types
     ( withDRG )
 import Data.Either
-    ( isLeft, isRight )
+    ( isRight )
 import Data.Functor.Identity
     ( Identity (..) )
 import Test.Hspec
-    ( Spec, before, describe, it, shouldSatisfy )
+    ( Spec, before, describe, it, shouldBe, shouldSatisfy )
 import Test.QuickCheck
-    ( Property, property, (===), (==>) )
+    ( Property, counterexample, property, (==>) )
 
 import qualified Data.List as L
 
@@ -227,9 +230,9 @@
 propFragmentation
     :: (Ord i, Ord o)
     => SystemDRG
-    -> CoinSelProp i o
+    -> CoinSelectionData i o
     -> Property
-propFragmentation drg (CoinSelProp utxo txOuts) = do
+propFragmentation drg (CoinSelectionData utxo txOuts) = do
     isRight selection1 && isRight selection2 ==>
         let Right (CoinSelectionResult s1 _) = selection1 in
         let Right (CoinSelectionResult s2 _) = selection2 in
@@ -242,23 +245,55 @@
     selection2 = runIdentity $ runExceptT $
         selectCoins largestFirst params
     selectionLimit = CoinSelectionLimit $ const 100
-    params = CoinSelectionParameters txOuts utxo selectionLimit
+    params = CoinSelectionParameters utxo txOuts selectionLimit
 
 propErrors
-    :: (Ord i, Ord o)
+    :: (Ord i, Ord o, Show i, Show o)
     => SystemDRG
-    -> CoinSelProp i o
+    -> CoinSelectionData i o
     -> Property
-propErrors drg (CoinSelProp utxo txOuts) = do
-    isLeft selection1 && isLeft selection2 ==>
-        let (Left s1, Left s2) = (selection1, selection2)
-        in prop (s1, s2)
+propErrors drg (CoinSelectionData utxo txOuts) =
+    case resultRandomImprove of
+        Right _ ->
+            -- Largest-First should always succeed if Random-Improve succeeds.
+            counterexample "case: Success"
+                $ property
+                $ resultLargestFirst `shouldSatisfy` isRight
+        Left (InputValueInsufficient _) ->
+            -- Largest-First should fail in exactly the same way when the total
+            -- value available is insufficient.
+            counterexample "case: InputValueInsufficient"
+                $ property
+                $ resultLargestFirst `shouldBe` resultRandomImprove
+        Left (InputCountInsufficient _) ->
+            -- Largest-First can still succeed in this case, so just check for
+            -- a valid result.
+            counterexample "case: InputCountInsufficient"
+                $ property
+                $ resultLargestFirst `shouldSatisfy` isValidLargestFirstResult
+        Left (InputsExhausted _) ->
+            -- Largest-First can still succeed in this case, so just check for
+            -- a valid result.
+            counterexample "case: InputsExhausted"
+                $ property
+                $ resultLargestFirst `shouldSatisfy` isValidLargestFirstResult
+        Left (InputLimitExceeded _) ->
+            -- Largest-First can still succeed in this case, so just check for
+            -- a valid result.
+            counterexample "case: InputLimitExceeded"
+                $ property
+                $ resultLargestFirst `shouldSatisfy` isValidLargestFirstResult
   where
-    prop (err1, err2) =
-        err1 === err2
-    (selection1,_) = withDRG drg $
+    isValidLargestFirstResult = \case
+        Right _ ->
+            -- We assume that this is a valid result, based on the assumption
+            -- that test coverage for Largest-First is sufficient.
+            True
+        Left x ->
+            isValidLargestFirstError x
+    resultRandomImprove = fst $ withDRG drg $
         runExceptT $ selectCoins randomImprove params
-    selection2 = runIdentity $ runExceptT $
+    resultLargestFirst = runIdentity $ runExceptT $
         selectCoins largestFirst params
     selectionLimit = CoinSelectionLimit $ const 1
-    params = CoinSelectionParameters txOuts utxo selectionLimit
+    params = CoinSelectionParameters utxo txOuts selectionLimit
diff --git a/src/test/Cardano/CoinSelectionSpec.hs b/src/test/Cardano/CoinSelectionSpec.hs
--- a/src/test/Cardano/CoinSelectionSpec.hs
+++ b/src/test/Cardano/CoinSelectionSpec.hs
@@ -1,6 +1,7 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE NumericUnderscores #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE StandaloneDeriving #-}
@@ -15,7 +16,7 @@
     -- * Export used to test various coin selection implementations
     , CoinSelectionFixture (..)
     , CoinSelectionTestResult (..)
-    , CoinSelProp (..)
+    , CoinSelectionData (..)
     , coinSelectionUnitTest
     ) where
 
@@ -46,6 +47,8 @@
     ( Address (..), Hash (..), ShowFmt (..), TxIn (..), unsafeCoin )
 import Control.Arrow
     ( (&&&) )
+import Control.Monad
+    ( replicateM )
 import Control.Monad.Trans.Except
     ( runExceptT )
 import Data.Function
@@ -63,12 +66,11 @@
 import Internal.Coin
     ( Coin, coinToIntegral )
 import Test.Hspec
-    ( Spec, SpecWith, describe, it, shouldBe )
+    ( Spec, SpecWith, describe, it, shouldBe, shouldSatisfy )
 import Test.QuickCheck
     ( Arbitrary (..)
     , Confidence (..)
     , Gen
-    , NonEmptyList (..)
     , Property
     , arbitraryBoundedIntegral
     , checkCoverage
@@ -82,6 +84,8 @@
     , property
     , scale
     , vectorOf
+    , withMaxSuccess
+    , (.&&.)
     )
 import Test.QuickCheck.Monadic
     ( monadicIO )
@@ -125,6 +129,12 @@
             checkCoverage $
                 prop_coinSelection_mappendPreservesTotalValue @Int @Int
 
+    describe "CoinSelectionData properties" $ do
+        it "CoinSelectionData coverage is adequate" $
+            checkCoverage
+                $ withMaxSuccess 10_000
+                $ prop_CoinSelectionData_coverage @Int @Int
+
   where
     lowerConfidence :: Confidence
     lowerConfidence = Confidence (10^(6 :: Integer)) 0.75
@@ -280,21 +290,35 @@
     sumChange  s1 <> sumChange  s2 `shouldBe` sumChange  (s1 <> s2)
     change     s1 <> change     s2 `shouldBe` change     (s1 <> s2)
 
+prop_CoinSelectionData_coverage
+    :: CoinSelectionData i o
+    -> Property
+prop_CoinSelectionData_coverage (CoinSelectionData inps outs) = property
+    $ cover 90 (amountAvailable >= amountRequested)
+        "amountAvailable ≥ amountRequested"
+    $
+    (amountAvailable `shouldSatisfy` (> C.zero))
+    .&&.
+    (amountRequested `shouldSatisfy` (> C.zero))
+  where
+    amountAvailable = coinMapValue inps
+    amountRequested = coinMapValue outs
+
 --------------------------------------------------------------------------------
 -- Coin Selection - Unit Tests
 --------------------------------------------------------------------------------
 
--- | Data for running
-data CoinSelProp i o = CoinSelProp
-    { csUtxO :: CoinMap i
-        -- ^ Available UTxO for the selection
-    , csOuts :: CoinMap o
-        -- ^ Requested outputs for the payment
+-- | Data for coin selection properties.
+data CoinSelectionData i o = CoinSelectionData
+    { csdInputsAvailable
+        :: CoinMap i
+    , csdOutputsRequested
+        :: CoinMap o
     } deriving Show
 
-instance (Buildable i, Buildable o) => Buildable (CoinSelProp i o) where
-    build (CoinSelProp utxo outs) = mempty
-        <> nameF "utxo" (blockListF $ coinMapToList utxo)
+instance (Buildable i, Buildable o) => Buildable (CoinSelectionData i o) where
+    build (CoinSelectionData inps outs) = mempty
+        <> nameF "inps" (blockListF $ coinMapToList inps)
         <> nameF "outs" (blockListF $ coinMapToList outs)
 
 -- | A fixture for testing the coin selection
@@ -396,14 +420,29 @@
         NE.fromList <$> vectorOf n arbitrary
 
 instance (Arbitrary i, Arbitrary o, Ord i, Ord o) =>
-    Arbitrary (CoinSelProp i o)
+    Arbitrary (CoinSelectionData i o)
   where
-    shrink (CoinSelProp utxo outs) = uncurry CoinSelProp <$> zip
-        (shrink utxo)
+    shrink (CoinSelectionData inps outs) = uncurry CoinSelectionData <$> zip
+        (shrink inps)
         (coinMapFromList <$> filter (not . null) (shrink (coinMapToList outs)))
-    arbitrary = CoinSelProp
-        <$> (coinMapFromList . getNonEmpty <$> arbitrary)
-        <*> (coinMapFromList . getNonEmpty <$> arbitrary)
+    arbitrary =
+        CoinSelectionData <$> genInps <*> genOuts
+      where
+        -- Incorporate a bias towards being able to pay for all outputs.
+        genInps = genCoinMap 256
+        genOuts = genCoinMap 4
+
+        genCoinMap :: forall k . (Arbitrary k, Ord k) => Int -> Gen (CoinMap k)
+        genCoinMap maxEntryCount = do
+            count <- choose (1, maxEntryCount)
+            coinMapFromList <$> replicateM count genCoinMapEntry
+
+        genCoinMapEntry :: forall k . Arbitrary k => Gen (CoinMapEntry k)
+        genCoinMapEntry = CoinMapEntry <$> arbitrary <*> genCoin
+
+        -- Generate coins with a reasonably high chance of size collisions.
+        genCoin :: Gen Coin
+        genCoin = unsafeCoin @Int <$> choose (1, 16)
 
 instance Arbitrary Address where
     shrink _ = []
