{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# OPTIONS_HADDOCK prune #-}
module Cardano.CoinSelection.Fee
(
Fee (..)
, FeeEstimator (..)
, adjustForFee
, FeeOptions (..)
, FeeBalancingPolicy (..)
, FeeAdjustmentError (..)
, DustThreshold (..)
, isDust
, coalesceDust
, calculateFee
, distributeFee
, reduceChangeOutputs
, splitCoin
) where
import Prelude hiding ( round )
import Cardano.CoinSelection
( CoinMap (..)
, CoinMapEntry (..)
, CoinSelection (..)
, coinMapFromList
, coinMapRandomEntry
, sumChange
, sumInputs
, sumOutputs
)
import Control.Monad.Trans.Class ( lift )
import Control.Monad.Trans.Except ( ExceptT (..), except, throwE )
import Control.Monad.Trans.State ( StateT (..), evalStateT )
import Crypto.Random.Types ( MonadRandom )
import Data.Bifunctor ( first )
import Data.Function ( (&) )
import Data.List.NonEmpty ( NonEmpty ((:|)) )
import Data.Maybe ( fromMaybe )
import Data.Ord ( Down (..), comparing )
import Data.Ratio ( (%) )
import GHC.Generics ( Generic )
import Internal.Coin ( Coin )
import Internal.Invariant ( invariant )
import Internal.Rounding ( RoundingDirection (..), round )
import Quiet ( Quiet (Quiet) )
import qualified Data.Foldable as F
import qualified Data.List.NonEmpty as NE
import qualified Internal.Coin as C
newtype Fee = Fee { Fee -> Coin
unFee :: Coin }
deriving newtype (Semigroup Fee
Fee
Semigroup Fee =>
Fee -> (Fee -> Fee -> Fee) -> ([Fee] -> Fee) -> Monoid Fee
[Fee] -> Fee
Fee -> Fee -> Fee
forall a.
Semigroup a =>
a -> (a -> a -> a) -> ([a] -> a) -> Monoid a
$cmempty :: Fee
mempty :: Fee
$cmappend :: Fee -> Fee -> Fee
mappend :: Fee -> Fee -> Fee
$cmconcat :: [Fee] -> Fee
mconcat :: [Fee] -> Fee
Monoid, NonEmpty Fee -> Fee
Fee -> Fee -> Fee
(Fee -> Fee -> Fee)
-> (NonEmpty Fee -> Fee)
-> (forall b. Integral b => b -> Fee -> Fee)
-> Semigroup Fee
forall b. Integral b => b -> Fee -> Fee
forall a.
(a -> a -> a)
-> (NonEmpty a -> a)
-> (forall b. Integral b => b -> a -> a)
-> Semigroup a
$c<> :: Fee -> Fee -> Fee
<> :: Fee -> Fee -> Fee
$csconcat :: NonEmpty Fee -> Fee
sconcat :: NonEmpty Fee -> Fee
$cstimes :: forall b. Integral b => b -> Fee -> Fee
stimes :: forall b. Integral b => b -> Fee -> Fee
Semigroup)
deriving stock (Fee -> Fee -> Bool
(Fee -> Fee -> Bool) -> (Fee -> Fee -> Bool) -> Eq Fee
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Fee -> Fee -> Bool
== :: Fee -> Fee -> Bool
$c/= :: Fee -> Fee -> Bool
/= :: Fee -> Fee -> Bool
Eq, (forall x. Fee -> Rep Fee x)
-> (forall x. Rep Fee x -> Fee) -> Generic Fee
forall x. Rep Fee x -> Fee
forall x. Fee -> Rep Fee x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. Fee -> Rep Fee x
from :: forall x. Fee -> Rep Fee x
$cto :: forall x. Rep Fee x -> Fee
to :: forall x. Rep Fee x -> Fee
Generic, Eq Fee
Eq Fee =>
(Fee -> Fee -> Ordering)
-> (Fee -> Fee -> Bool)
-> (Fee -> Fee -> Bool)
-> (Fee -> Fee -> Bool)
-> (Fee -> Fee -> Bool)
-> (Fee -> Fee -> Fee)
-> (Fee -> Fee -> Fee)
-> Ord Fee
Fee -> Fee -> Bool
Fee -> Fee -> Ordering
Fee -> Fee -> Fee
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: Fee -> Fee -> Ordering
compare :: Fee -> Fee -> Ordering
$c< :: Fee -> Fee -> Bool
< :: Fee -> Fee -> Bool
$c<= :: Fee -> Fee -> Bool
<= :: Fee -> Fee -> Bool
$c> :: Fee -> Fee -> Bool
> :: Fee -> Fee -> Bool
$c>= :: Fee -> Fee -> Bool
>= :: Fee -> Fee -> Bool
$cmax :: Fee -> Fee -> Fee
max :: Fee -> Fee -> Fee
$cmin :: Fee -> Fee -> Fee
min :: Fee -> Fee -> Fee
Ord)
deriving Int -> Fee -> ShowS
[Fee] -> ShowS
Fee -> String
(Int -> Fee -> ShowS)
-> (Fee -> String) -> ([Fee] -> ShowS) -> Show Fee
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Fee -> ShowS
showsPrec :: Int -> Fee -> ShowS
$cshow :: Fee -> String
show :: Fee -> String
$cshowList :: [Fee] -> ShowS
showList :: [Fee] -> ShowS
Show via (Quiet Fee)
newtype DustThreshold = DustThreshold { DustThreshold -> Coin
unDustThreshold :: Coin }
deriving stock (DustThreshold -> DustThreshold -> Bool
(DustThreshold -> DustThreshold -> Bool)
-> (DustThreshold -> DustThreshold -> Bool) -> Eq DustThreshold
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: DustThreshold -> DustThreshold -> Bool
== :: DustThreshold -> DustThreshold -> Bool
$c/= :: DustThreshold -> DustThreshold -> Bool
/= :: DustThreshold -> DustThreshold -> Bool
Eq, (forall x. DustThreshold -> Rep DustThreshold x)
-> (forall x. Rep DustThreshold x -> DustThreshold)
-> Generic DustThreshold
forall x. Rep DustThreshold x -> DustThreshold
forall x. DustThreshold -> Rep DustThreshold x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. DustThreshold -> Rep DustThreshold x
from :: forall x. DustThreshold -> Rep DustThreshold x
$cto :: forall x. Rep DustThreshold x -> DustThreshold
to :: forall x. Rep DustThreshold x -> DustThreshold
Generic, Eq DustThreshold
Eq DustThreshold =>
(DustThreshold -> DustThreshold -> Ordering)
-> (DustThreshold -> DustThreshold -> Bool)
-> (DustThreshold -> DustThreshold -> Bool)
-> (DustThreshold -> DustThreshold -> Bool)
-> (DustThreshold -> DustThreshold -> Bool)
-> (DustThreshold -> DustThreshold -> DustThreshold)
-> (DustThreshold -> DustThreshold -> DustThreshold)
-> Ord DustThreshold
DustThreshold -> DustThreshold -> Bool
DustThreshold -> DustThreshold -> Ordering
DustThreshold -> DustThreshold -> DustThreshold
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: DustThreshold -> DustThreshold -> Ordering
compare :: DustThreshold -> DustThreshold -> Ordering
$c< :: DustThreshold -> DustThreshold -> Bool
< :: DustThreshold -> DustThreshold -> Bool
$c<= :: DustThreshold -> DustThreshold -> Bool
<= :: DustThreshold -> DustThreshold -> Bool
$c> :: DustThreshold -> DustThreshold -> Bool
> :: DustThreshold -> DustThreshold -> Bool
$c>= :: DustThreshold -> DustThreshold -> Bool
>= :: DustThreshold -> DustThreshold -> Bool
$cmax :: DustThreshold -> DustThreshold -> DustThreshold
max :: DustThreshold -> DustThreshold -> DustThreshold
$cmin :: DustThreshold -> DustThreshold -> DustThreshold
min :: DustThreshold -> DustThreshold -> DustThreshold
Ord)
deriving Int -> DustThreshold -> ShowS
[DustThreshold] -> ShowS
DustThreshold -> String
(Int -> DustThreshold -> ShowS)
-> (DustThreshold -> String)
-> ([DustThreshold] -> ShowS)
-> Show DustThreshold
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> DustThreshold -> ShowS
showsPrec :: Int -> DustThreshold -> ShowS
$cshow :: DustThreshold -> String
show :: DustThreshold -> String
$cshowList :: [DustThreshold] -> ShowS
showList :: [DustThreshold] -> ShowS
Show via (Quiet DustThreshold)
isDust :: DustThreshold -> Coin -> Bool
isDust :: DustThreshold -> Coin -> Bool
isDust (DustThreshold Coin
dt) Coin
c = Coin
c Coin -> Coin -> Bool
forall a. Ord a => a -> a -> Bool
<= Coin
dt
newtype FeeEstimator i o = FeeEstimator
{ forall i o. FeeEstimator i o -> CoinSelection i o -> Fee
estimateFee :: CoinSelection i o -> Fee
} deriving (forall x. FeeEstimator i o -> Rep (FeeEstimator i o) x)
-> (forall x. Rep (FeeEstimator i o) x -> FeeEstimator i o)
-> Generic (FeeEstimator i o)
forall x. Rep (FeeEstimator i o) x -> FeeEstimator i o
forall x. FeeEstimator i o -> Rep (FeeEstimator i o) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall i o x. Rep (FeeEstimator i o) x -> FeeEstimator i o
forall i o x. FeeEstimator i o -> Rep (FeeEstimator i o) x
$cfrom :: forall i o x. FeeEstimator i o -> Rep (FeeEstimator i o) x
from :: forall x. FeeEstimator i o -> Rep (FeeEstimator i o) x
$cto :: forall i o x. Rep (FeeEstimator i o) x -> FeeEstimator i o
to :: forall x. Rep (FeeEstimator i o) x -> FeeEstimator i o
Generic
data FeeOptions i o = FeeOptions
{ forall i o. FeeOptions i o -> FeeEstimator i o
feeEstimator
:: FeeEstimator i o
, forall i o. FeeOptions i o -> DustThreshold
dustThreshold
:: DustThreshold
, forall i o. FeeOptions i o -> FeeBalancingPolicy
feeBalancingPolicy
:: FeeBalancingPolicy
} deriving (forall x. FeeOptions i o -> Rep (FeeOptions i o) x)
-> (forall x. Rep (FeeOptions i o) x -> FeeOptions i o)
-> Generic (FeeOptions i o)
forall x. Rep (FeeOptions i o) x -> FeeOptions i o
forall x. FeeOptions i o -> Rep (FeeOptions i o) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall i o x. Rep (FeeOptions i o) x -> FeeOptions i o
forall i o x. FeeOptions i o -> Rep (FeeOptions i o) x
$cfrom :: forall i o x. FeeOptions i o -> Rep (FeeOptions i o) x
from :: forall x. FeeOptions i o -> Rep (FeeOptions i o) x
$cto :: forall i o x. Rep (FeeOptions i o) x -> FeeOptions i o
to :: forall x. Rep (FeeOptions i o) x -> FeeOptions i o
Generic
data FeeBalancingPolicy
= RequireBalancedFee
| RequireMinimalFee
deriving ((forall x. FeeBalancingPolicy -> Rep FeeBalancingPolicy x)
-> (forall x. Rep FeeBalancingPolicy x -> FeeBalancingPolicy)
-> Generic FeeBalancingPolicy
forall x. Rep FeeBalancingPolicy x -> FeeBalancingPolicy
forall x. FeeBalancingPolicy -> Rep FeeBalancingPolicy x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. FeeBalancingPolicy -> Rep FeeBalancingPolicy x
from :: forall x. FeeBalancingPolicy -> Rep FeeBalancingPolicy x
$cto :: forall x. Rep FeeBalancingPolicy x -> FeeBalancingPolicy
to :: forall x. Rep FeeBalancingPolicy x -> FeeBalancingPolicy
Generic, Int -> FeeBalancingPolicy -> ShowS
[FeeBalancingPolicy] -> ShowS
FeeBalancingPolicy -> String
(Int -> FeeBalancingPolicy -> ShowS)
-> (FeeBalancingPolicy -> String)
-> ([FeeBalancingPolicy] -> ShowS)
-> Show FeeBalancingPolicy
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> FeeBalancingPolicy -> ShowS
showsPrec :: Int -> FeeBalancingPolicy -> ShowS
$cshow :: FeeBalancingPolicy -> String
show :: FeeBalancingPolicy -> String
$cshowList :: [FeeBalancingPolicy] -> ShowS
showList :: [FeeBalancingPolicy] -> ShowS
Show, FeeBalancingPolicy -> FeeBalancingPolicy -> Bool
(FeeBalancingPolicy -> FeeBalancingPolicy -> Bool)
-> (FeeBalancingPolicy -> FeeBalancingPolicy -> Bool)
-> Eq FeeBalancingPolicy
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: FeeBalancingPolicy -> FeeBalancingPolicy -> Bool
== :: FeeBalancingPolicy -> FeeBalancingPolicy -> Bool
$c/= :: FeeBalancingPolicy -> FeeBalancingPolicy -> Bool
/= :: FeeBalancingPolicy -> FeeBalancingPolicy -> Bool
Eq)
data FeeAdjustmentError i o
= CannotCoverFee Fee
| CoinSelectionUnderfunded (CoinSelection i o)
deriving (Int -> FeeAdjustmentError i o -> ShowS
[FeeAdjustmentError i o] -> ShowS
FeeAdjustmentError i o -> String
(Int -> FeeAdjustmentError i o -> ShowS)
-> (FeeAdjustmentError i o -> String)
-> ([FeeAdjustmentError i o] -> ShowS)
-> Show (FeeAdjustmentError i o)
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
forall i o.
(Show i, Show o) =>
Int -> FeeAdjustmentError i o -> ShowS
forall i o. (Show i, Show o) => [FeeAdjustmentError i o] -> ShowS
forall i o. (Show i, Show o) => FeeAdjustmentError i o -> String
$cshowsPrec :: forall i o.
(Show i, Show o) =>
Int -> FeeAdjustmentError i o -> ShowS
showsPrec :: Int -> FeeAdjustmentError i o -> ShowS
$cshow :: forall i o. (Show i, Show o) => FeeAdjustmentError i o -> String
show :: FeeAdjustmentError i o -> String
$cshowList :: forall i o. (Show i, Show o) => [FeeAdjustmentError i o] -> ShowS
showList :: [FeeAdjustmentError i o] -> ShowS
Show, FeeAdjustmentError i o -> FeeAdjustmentError i o -> Bool
(FeeAdjustmentError i o -> FeeAdjustmentError i o -> Bool)
-> (FeeAdjustmentError i o -> FeeAdjustmentError i o -> Bool)
-> Eq (FeeAdjustmentError i o)
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
forall i o.
(Eq i, Eq o) =>
FeeAdjustmentError i o -> FeeAdjustmentError i o -> Bool
$c== :: forall i o.
(Eq i, Eq o) =>
FeeAdjustmentError i o -> FeeAdjustmentError i o -> Bool
== :: FeeAdjustmentError i o -> FeeAdjustmentError i o -> Bool
$c/= :: forall i o.
(Eq i, Eq o) =>
FeeAdjustmentError i o -> FeeAdjustmentError i o -> Bool
/= :: FeeAdjustmentError i o -> FeeAdjustmentError i o -> Bool
Eq)
adjustForFee
:: (Ord i, MonadRandom m)
=> FeeOptions i o
-> CoinMap i
-> CoinSelection i o
-> ExceptT (FeeAdjustmentError i o) m (CoinSelection i o)
adjustForFee :: forall i (m :: * -> *) o.
(Ord i, MonadRandom m) =>
FeeOptions i o
-> CoinMap i
-> CoinSelection i o
-> ExceptT (FeeAdjustmentError i o) m (CoinSelection i o)
adjustForFee FeeOptions i o
unsafeOpt CoinMap i
utxo CoinSelection i o
coinSel = do
let opt :: FeeOptions i o
opt = String
-> FeeOptions i o -> (FeeOptions i o -> Bool) -> FeeOptions i o
forall a. String -> a -> (a -> Bool) -> a
invariant
String
"adjustForFee: fee must be non-null" FeeOptions i o
unsafeOpt (Bool -> Bool
not (Bool -> Bool)
-> (FeeOptions i o -> Bool) -> FeeOptions i o -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FeeOptions i o -> Bool
nullFee)
FeeOptions i o
-> CoinMap i
-> CoinSelection i o
-> ExceptT (FeeAdjustmentError i o) m (CoinSelection i o)
forall i o (m :: * -> *).
(Ord i, MonadRandom m) =>
FeeOptions i o
-> CoinMap i
-> CoinSelection i o
-> ExceptT (FeeAdjustmentError i o) m (CoinSelection i o)
senderPaysFee FeeOptions i o
opt CoinMap i
utxo CoinSelection i o
coinSel
where
nullFee :: FeeOptions i o -> Bool
nullFee FeeOptions i o
opt = FeeEstimator i o -> CoinSelection i o -> Fee
forall i o. FeeEstimator i o -> CoinSelection i o -> Fee
estimateFee (FeeOptions i o -> FeeEstimator i o
forall i o. FeeOptions i o -> FeeEstimator i o
feeEstimator FeeOptions i o
opt) CoinSelection i o
coinSel Fee -> Fee -> Bool
forall a. Eq a => a -> a -> Bool
== Coin -> Fee
Fee Coin
C.zero
calculateFee :: CoinSelection i o -> Maybe Fee
calculateFee :: forall i o. CoinSelection i o -> Maybe Fee
calculateFee CoinSelection i o
s = Coin -> Fee
Fee (Coin -> Fee) -> Maybe Coin -> Maybe Fee
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> CoinSelection i o -> Coin
forall i o. CoinSelection i o -> Coin
sumInputs CoinSelection i o
s Coin -> Coin -> Maybe Coin
`C.sub` (CoinSelection i o -> Coin
forall i o. CoinSelection i o -> Coin
sumOutputs CoinSelection i o
s Coin -> Coin -> Coin
`C.add` CoinSelection i o -> Coin
forall i o. CoinSelection i o -> Coin
sumChange CoinSelection i o
s)
senderPaysFee
:: forall i o m . (Ord i, MonadRandom m)
=> FeeOptions i o
-> CoinMap i
-> CoinSelection i o
-> ExceptT (FeeAdjustmentError i o) m (CoinSelection i o)
senderPaysFee :: forall i o (m :: * -> *).
(Ord i, MonadRandom m) =>
FeeOptions i o
-> CoinMap i
-> CoinSelection i o
-> ExceptT (FeeAdjustmentError i o) m (CoinSelection i o)
senderPaysFee FeeOptions i o
opts CoinMap i
utxo CoinSelection i o
sel =
StateT
(CoinMap i)
(ExceptT (FeeAdjustmentError i o) m)
(CoinSelection i o)
-> CoinMap i
-> ExceptT (FeeAdjustmentError i o) m (CoinSelection i o)
forall (m :: * -> *) s a. Monad m => StateT s m a -> s -> m a
evalStateT (CoinSelection i o
-> StateT
(CoinMap i)
(ExceptT (FeeAdjustmentError i o) m)
(CoinSelection i o)
go CoinSelection i o
sel) CoinMap i
utxo
where
go
:: CoinSelection i o
-> StateT
(CoinMap i)
(ExceptT (FeeAdjustmentError i o) m)
(CoinSelection i o)
go :: CoinSelection i o
-> StateT
(CoinMap i)
(ExceptT (FeeAdjustmentError i o) m)
(CoinSelection i o)
go coinSel :: CoinSelection i o
coinSel@(CoinSelection CoinMap i
inps CoinMap o
outs [Coin]
chgs) = do
(CoinSelection i o
coinSel', Fee
remFee) <- ExceptT (FeeAdjustmentError i o) m (CoinSelection i o, Fee)
-> StateT
(CoinMap i)
(ExceptT (FeeAdjustmentError i o) m)
(CoinSelection i o, Fee)
forall (m :: * -> *) a. Monad m => m a -> StateT (CoinMap i) m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (ExceptT (FeeAdjustmentError i o) m (CoinSelection i o, Fee)
-> StateT
(CoinMap i)
(ExceptT (FeeAdjustmentError i o) m)
(CoinSelection i o, Fee))
-> ExceptT (FeeAdjustmentError i o) m (CoinSelection i o, Fee)
-> StateT
(CoinMap i)
(ExceptT (FeeAdjustmentError i o) m)
(CoinSelection i o, Fee)
forall a b. (a -> b) -> a -> b
$ Either (FeeAdjustmentError i o) (CoinSelection i o, Fee)
-> ExceptT (FeeAdjustmentError i o) m (CoinSelection i o, Fee)
forall (m :: * -> *) e a. Monad m => Either e a -> ExceptT e m a
except (Either (FeeAdjustmentError i o) (CoinSelection i o, Fee)
-> ExceptT (FeeAdjustmentError i o) m (CoinSelection i o, Fee))
-> Either (FeeAdjustmentError i o) (CoinSelection i o, Fee)
-> ExceptT (FeeAdjustmentError i o) m (CoinSelection i o, Fee)
forall a b. (a -> b) -> a -> b
$ FeeOptions i o
-> CoinSelection i o
-> Either (FeeAdjustmentError i o) (CoinSelection i o, Fee)
forall i o.
FeeOptions i o
-> CoinSelection i o
-> Either (FeeAdjustmentError i o) (CoinSelection i o, Fee)
reduceChangeOutputs FeeOptions i o
opts CoinSelection i o
coinSel
if Fee
remFee Fee -> Fee -> Bool
forall a. Eq a => a -> a -> Bool
== Coin -> Fee
Fee Coin
C.zero
then CoinSelection i o
-> StateT
(CoinMap i)
(ExceptT (FeeAdjustmentError i o) m)
(CoinSelection i o)
forall a.
a -> StateT (CoinMap i) (ExceptT (FeeAdjustmentError i o) m) a
forall (f :: * -> *) a. Applicative f => a -> f a
pure CoinSelection i o
coinSel'
else do
[CoinMapEntry i]
inps' <- Fee
-> StateT
(CoinMap i) (ExceptT (FeeAdjustmentError i o) m) [CoinMapEntry i]
forall (m :: * -> *) i o.
MonadRandom m =>
Fee
-> StateT
(CoinMap i) (ExceptT (FeeAdjustmentError i o) m) [CoinMapEntry i]
coverRemainingFee Fee
remFee
let extraChange :: [Coin]
extraChange = Coin -> [Coin] -> [Coin]
splitCoin ([CoinMapEntry i] -> Coin
forall i. [CoinMapEntry i] -> Coin
sumEntries [CoinMapEntry i]
inps') [Coin]
chgs
CoinSelection i o
-> StateT
(CoinMap i)
(ExceptT (FeeAdjustmentError i o) m)
(CoinSelection i o)
go (CoinSelection i o
-> StateT
(CoinMap i)
(ExceptT (FeeAdjustmentError i o) m)
(CoinSelection i o))
-> CoinSelection i o
-> StateT
(CoinMap i)
(ExceptT (FeeAdjustmentError i o) m)
(CoinSelection i o)
forall a b. (a -> b) -> a -> b
$ CoinMap i -> CoinMap o -> [Coin] -> CoinSelection i o
forall i o. CoinMap i -> CoinMap o -> [Coin] -> CoinSelection i o
CoinSelection (CoinMap i
inps CoinMap i -> CoinMap i -> CoinMap i
forall a. Semigroup a => a -> a -> a
<> [CoinMapEntry i] -> CoinMap i
forall a. Ord a => [CoinMapEntry a] -> CoinMap a
coinMapFromList [CoinMapEntry i]
inps') CoinMap o
outs [Coin]
extraChange
coverRemainingFee
:: MonadRandom m
=> Fee
-> StateT (CoinMap i) (ExceptT (FeeAdjustmentError i o) m) [CoinMapEntry i]
coverRemainingFee :: forall (m :: * -> *) i o.
MonadRandom m =>
Fee
-> StateT
(CoinMap i) (ExceptT (FeeAdjustmentError i o) m) [CoinMapEntry i]
coverRemainingFee (Fee Coin
fee) = [CoinMapEntry i]
-> StateT
(CoinMap i) (ExceptT (FeeAdjustmentError i o) m) [CoinMapEntry i]
go [] where
go :: [CoinMapEntry i]
-> StateT
(CoinMap i) (ExceptT (FeeAdjustmentError i o) m) [CoinMapEntry i]
go [CoinMapEntry i]
acc
| [CoinMapEntry i] -> Coin
forall i. [CoinMapEntry i] -> Coin
sumEntries [CoinMapEntry i]
acc Coin -> Coin -> Bool
forall a. Ord a => a -> a -> Bool
>= Coin
fee =
[CoinMapEntry i]
-> StateT
(CoinMap i) (ExceptT (FeeAdjustmentError i o) m) [CoinMapEntry i]
forall a.
a -> StateT (CoinMap i) (ExceptT (FeeAdjustmentError i o) m) a
forall (m :: * -> *) a. Monad m => a -> m a
return [CoinMapEntry i]
acc
| Bool
otherwise = do
(CoinMap i
-> ExceptT
(FeeAdjustmentError i o) m (Maybe (CoinMapEntry i), CoinMap i))
-> StateT
(CoinMap i)
(ExceptT (FeeAdjustmentError i o) m)
(Maybe (CoinMapEntry i))
forall s (m :: * -> *) a. (s -> m (a, s)) -> StateT s m a
StateT (m (Maybe (CoinMapEntry i), CoinMap i)
-> ExceptT
(FeeAdjustmentError i o) m (Maybe (CoinMapEntry i), CoinMap i)
forall (m :: * -> *) a.
Monad m =>
m a -> ExceptT (FeeAdjustmentError i o) m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m (Maybe (CoinMapEntry i), CoinMap i)
-> ExceptT
(FeeAdjustmentError i o) m (Maybe (CoinMapEntry i), CoinMap i))
-> (CoinMap i -> m (Maybe (CoinMapEntry i), CoinMap i))
-> CoinMap i
-> ExceptT
(FeeAdjustmentError i o) m (Maybe (CoinMapEntry i), CoinMap i)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CoinMap i -> m (Maybe (CoinMapEntry i), CoinMap i)
forall {f :: * -> *} {a}.
MonadRandom f =>
CoinMap a -> f (Maybe (CoinMapEntry a), CoinMap a)
selectRandomEntry) StateT
(CoinMap i)
(ExceptT (FeeAdjustmentError i o) m)
(Maybe (CoinMapEntry i))
-> (Maybe (CoinMapEntry i)
-> StateT
(CoinMap i) (ExceptT (FeeAdjustmentError i o) m) [CoinMapEntry i])
-> StateT
(CoinMap i) (ExceptT (FeeAdjustmentError i o) m) [CoinMapEntry i]
forall a b.
StateT (CoinMap i) (ExceptT (FeeAdjustmentError i o) m) a
-> (a -> StateT (CoinMap i) (ExceptT (FeeAdjustmentError i o) m) b)
-> StateT (CoinMap i) (ExceptT (FeeAdjustmentError i o) m) b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Just CoinMapEntry i
entry ->
[CoinMapEntry i]
-> StateT
(CoinMap i) (ExceptT (FeeAdjustmentError i o) m) [CoinMapEntry i]
go (CoinMapEntry i
entry CoinMapEntry i -> [CoinMapEntry i] -> [CoinMapEntry i]
forall a. a -> [a] -> [a]
: [CoinMapEntry i]
acc)
Maybe (CoinMapEntry i)
Nothing ->
ExceptT (FeeAdjustmentError i o) m [CoinMapEntry i]
-> StateT
(CoinMap i) (ExceptT (FeeAdjustmentError i o) m) [CoinMapEntry i]
forall (m :: * -> *) a. Monad m => m a -> StateT (CoinMap i) m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (ExceptT (FeeAdjustmentError i o) m [CoinMapEntry i]
-> StateT
(CoinMap i) (ExceptT (FeeAdjustmentError i o) m) [CoinMapEntry i])
-> ExceptT (FeeAdjustmentError i o) m [CoinMapEntry i]
-> StateT
(CoinMap i) (ExceptT (FeeAdjustmentError i o) m) [CoinMapEntry i]
forall a b. (a -> b) -> a -> b
$ FeeAdjustmentError i o
-> ExceptT (FeeAdjustmentError i o) m [CoinMapEntry i]
forall (m :: * -> *) e a. Monad m => e -> ExceptT e m a
throwE (FeeAdjustmentError i o
-> ExceptT (FeeAdjustmentError i o) m [CoinMapEntry i])
-> FeeAdjustmentError i o
-> ExceptT (FeeAdjustmentError i o) m [CoinMapEntry i]
forall a b. (a -> b) -> a -> b
$ Fee -> FeeAdjustmentError i o
forall i o. Fee -> FeeAdjustmentError i o
CannotCoverFee (Fee -> FeeAdjustmentError i o) -> Fee -> FeeAdjustmentError i o
forall a b. (a -> b) -> a -> b
$ Coin -> Fee
Fee (Coin -> Fee) -> Coin -> Fee
forall a b. (a -> b) -> a -> b
$
Coin
fee Coin -> Coin -> Coin
`C.distance` ([CoinMapEntry i] -> Coin
forall i. [CoinMapEntry i] -> Coin
sumEntries [CoinMapEntry i]
acc)
selectRandomEntry :: CoinMap a -> f (Maybe (CoinMapEntry a), CoinMap a)
selectRandomEntry CoinMap a
m =
(Maybe (CoinMapEntry a), CoinMap a)
-> ((CoinMapEntry a, CoinMap a)
-> (Maybe (CoinMapEntry a), CoinMap a))
-> Maybe (CoinMapEntry a, CoinMap a)
-> (Maybe (CoinMapEntry a), CoinMap a)
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (Maybe (CoinMapEntry a)
forall a. Maybe a
Nothing, CoinMap a
m) ((CoinMapEntry a -> Maybe (CoinMapEntry a))
-> (CoinMapEntry a, CoinMap a)
-> (Maybe (CoinMapEntry a), CoinMap a)
forall a b c. (a -> b) -> (a, c) -> (b, c)
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first CoinMapEntry a -> Maybe (CoinMapEntry a)
forall a. a -> Maybe a
Just) (Maybe (CoinMapEntry a, CoinMap a)
-> (Maybe (CoinMapEntry a), CoinMap a))
-> f (Maybe (CoinMapEntry a, CoinMap a))
-> f (Maybe (CoinMapEntry a), CoinMap a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> CoinMap a -> f (Maybe (CoinMapEntry a, CoinMap a))
forall (m :: * -> *) a.
MonadRandom m =>
CoinMap a -> m (Maybe (CoinMapEntry a, CoinMap a))
coinMapRandomEntry CoinMap a
m
reduceChangeOutputs
:: FeeOptions i o
-> CoinSelection i o
-> Either (FeeAdjustmentError i o) (CoinSelection i o, Fee)
reduceChangeOutputs :: forall i o.
FeeOptions i o
-> CoinSelection i o
-> Either (FeeAdjustmentError i o) (CoinSelection i o, Fee)
reduceChangeOutputs FeeOptions i o
opts CoinSelection i o
s = do
let Fee Coin
phi_original = FeeEstimator i o -> CoinSelection i o -> Fee
forall i o. FeeEstimator i o -> CoinSelection i o -> Fee
estimateFee (FeeOptions i o -> FeeEstimator i o
forall i o. FeeOptions i o -> FeeEstimator i o
feeEstimator FeeOptions i o
opts) CoinSelection i o
s
let m_delta_original :: Maybe Coin
m_delta_original = CoinSelection i o -> Coin
forall i o. CoinSelection i o -> Coin
sumInputs CoinSelection i o
s Coin -> Coin -> Maybe Coin
`C.sub` (CoinSelection i o -> Coin
forall i o. CoinSelection i o -> Coin
sumOutputs CoinSelection i o
s Coin -> Coin -> Coin
`C.add` CoinSelection i o -> Coin
forall i o. CoinSelection i o -> Coin
sumChange CoinSelection i o
s)
case Maybe Coin
m_delta_original of
Just Coin
delta_original | Coin
phi_original Coin -> Coin -> Bool
forall a. Eq a => a -> a -> Bool
== Coin
delta_original -> do
(CoinSelection i o, Fee)
-> Either (FeeAdjustmentError i o) (CoinSelection i o, Fee)
forall a. a -> Either (FeeAdjustmentError i o) a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (CoinSelection i o
s, Coin -> Fee
Fee Coin
C.zero)
Just Coin
delta_original | Coin
phi_original Coin -> Coin -> Bool
forall a. Ord a => a -> a -> Bool
> Coin
delta_original Bool -> Bool -> Bool
&& [Coin] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null (CoinSelection i o -> [Coin]
forall i o. CoinSelection i o -> [Coin]
change CoinSelection i o
s) -> do
let remainder :: Coin
remainder = Coin
phi_original Coin -> Coin -> Coin
`C.distance` Coin
delta_original
(CoinSelection i o, Fee)
-> Either (FeeAdjustmentError i o) (CoinSelection i o, Fee)
forall a. a -> Either (FeeAdjustmentError i o) a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (CoinSelection i o
s, Coin -> Fee
Fee Coin
remainder)
Just Coin
delta_original | Coin
phi_original Coin -> Coin -> Bool
forall a. Ord a => a -> a -> Bool
> Coin
delta_original Bool -> Bool -> Bool
&& Bool -> Bool
not ([Coin] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null (CoinSelection i o -> [Coin]
forall i o. CoinSelection i o -> [Coin]
change CoinSelection i o
s)) -> do
let remainder :: Coin
remainder = Coin
phi_original Coin -> Coin -> Coin
`C.distance` Coin
delta_original
let chgs' :: [Coin]
chgs' = Fee -> NonEmpty Coin -> NonEmpty (Fee, Coin)
distributeFee (Coin -> Fee
Fee Coin
remainder) ([Coin] -> NonEmpty Coin
forall a. HasCallStack => [a] -> NonEmpty a
NE.fromList (CoinSelection i o -> [Coin]
forall i o. CoinSelection i o -> [Coin]
change CoinSelection i o
s))
NonEmpty (Fee, Coin)
-> (NonEmpty (Fee, Coin) -> NonEmpty Coin) -> NonEmpty Coin
forall a b. a -> (a -> b) -> b
& ((Fee, Coin) -> Coin) -> NonEmpty (Fee, Coin) -> NonEmpty Coin
forall a b. (a -> b) -> NonEmpty a -> NonEmpty b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Fee, Coin) -> Coin
payFee
NonEmpty Coin -> (NonEmpty Coin -> [Coin]) -> [Coin]
forall a b. a -> (a -> b) -> b
& DustThreshold -> NonEmpty Coin -> [Coin]
coalesceDust (FeeOptions i o -> DustThreshold
forall i o. FeeOptions i o -> DustThreshold
dustThreshold FeeOptions i o
opts)
FeeOptions i o
-> CoinSelection i o
-> Either (FeeAdjustmentError i o) (CoinSelection i o, Fee)
forall i o.
FeeOptions i o
-> CoinSelection i o
-> Either (FeeAdjustmentError i o) (CoinSelection i o, Fee)
reduceChangeOutputs FeeOptions i o
opts (CoinSelection i o
s { change = chgs' })
Just Coin
delta_original | Coin
delta_original Coin -> Coin -> Bool
forall a. Ord a => a -> a -> Bool
> Coin
phi_original -> do
let extraChg :: Coin
extraChg = Coin
delta_original Coin -> Coin -> Coin
`C.distance` Coin
phi_original
let sDangling :: CoinSelection i o
sDangling = CoinSelection i o
s { change = splitCoin extraChg (change s) }
let Fee Coin
phi_dangling = FeeEstimator i o -> CoinSelection i o -> Fee
forall i o. FeeEstimator i o -> CoinSelection i o -> Fee
estimateFee (FeeOptions i o -> FeeEstimator i o
forall i o. FeeOptions i o -> FeeEstimator i o
feeEstimator FeeOptions i o
opts) CoinSelection i o
sDangling
let delta_dangling :: Coin
delta_dangling = Coin
phi_original
case Coin
phi_dangling Coin -> Coin -> Maybe Coin
`C.sub` Coin
delta_dangling of
Just Coin
remainder | Coin
phi_dangling Coin -> Coin -> Bool
forall a. Ord a => a -> a -> Bool
>= Coin
delta_original ->
case FeeOptions i o -> FeeBalancingPolicy
forall i o. FeeOptions i o -> FeeBalancingPolicy
feeBalancingPolicy FeeOptions i o
opts of
FeeBalancingPolicy
RequireMinimalFee ->
(CoinSelection i o, Fee)
-> Either (FeeAdjustmentError i o) (CoinSelection i o, Fee)
forall a. a -> Either (FeeAdjustmentError i o) a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (CoinSelection i o
s, Coin -> Fee
Fee Coin
C.zero)
FeeBalancingPolicy
RequireBalancedFee ->
(CoinSelection i o, Fee)
-> Either (FeeAdjustmentError i o) (CoinSelection i o, Fee)
forall a. a -> Either (FeeAdjustmentError i o) a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (CoinSelection i o
sDangling, Coin -> Fee
Fee Coin
remainder)
Maybe Coin
_otherwise ->
FeeOptions i o
-> CoinSelection i o
-> Either (FeeAdjustmentError i o) (CoinSelection i o, Fee)
forall i o.
FeeOptions i o
-> CoinSelection i o
-> Either (FeeAdjustmentError i o) (CoinSelection i o, Fee)
reduceChangeOutputs FeeOptions i o
opts CoinSelection i o
sDangling
Maybe Coin
_Nothing ->
FeeAdjustmentError i o
-> Either (FeeAdjustmentError i o) (CoinSelection i o, Fee)
forall a b. a -> Either a b
Left (CoinSelection i o -> FeeAdjustmentError i o
forall i o. CoinSelection i o -> FeeAdjustmentError i o
CoinSelectionUnderfunded CoinSelection i o
s)
distributeFee :: Fee -> NonEmpty Coin -> NonEmpty (Fee, Coin)
distributeFee :: Fee -> NonEmpty Coin -> NonEmpty (Fee, Coin)
distributeFee (Fee Coin
feeTotal) NonEmpty Coin
coinsUnsafe =
NonEmpty Fee -> NonEmpty Coin -> NonEmpty (Fee, Coin)
forall a b. NonEmpty a -> NonEmpty b -> NonEmpty (a, b)
NE.zip NonEmpty Fee
feesRounded NonEmpty Coin
coins
where
coins :: NonEmpty Coin
coins :: NonEmpty Coin
coins =
String -> NonEmpty Coin -> (NonEmpty Coin -> Bool) -> NonEmpty Coin
forall a. String -> a -> (a -> Bool) -> a
invariant String
"distributeFee: all coins must be non-zero in value."
NonEmpty Coin
coinsUnsafe (Coin
C.zero Coin -> NonEmpty Coin -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`F.notElem`)
feesRounded :: NonEmpty Fee
feesRounded :: NonEmpty Fee
feesRounded
= NonEmpty Rational
feesUnrounded
NonEmpty Rational
-> (NonEmpty Rational -> NonEmpty (Int, Rational))
-> NonEmpty (Int, Rational)
forall a b. a -> (a -> b) -> b
& NonEmpty Int -> NonEmpty Rational -> NonEmpty (Int, Rational)
forall a b. NonEmpty a -> NonEmpty b -> NonEmpty (a, b)
NE.zip NonEmpty Int
indices
NonEmpty (Int, Rational)
-> (NonEmpty (Int, Rational) -> NonEmpty (Int, Rational))
-> NonEmpty (Int, Rational)
forall a b. a -> (a -> b) -> b
& ((Int, Rational) -> (Int, Rational) -> Ordering)
-> NonEmpty (Int, Rational) -> NonEmpty (Int, Rational)
forall a. (a -> a -> Ordering) -> NonEmpty a -> NonEmpty a
NE.sortBy (((Int, Rational) -> Down Rational)
-> (Int, Rational) -> (Int, Rational) -> Ordering
forall a b. Ord a => (b -> a) -> b -> b -> Ordering
comparing (Rational -> Down Rational
forall a. a -> Down a
Down (Rational -> Down Rational)
-> ((Int, Rational) -> Rational)
-> (Int, Rational)
-> Down Rational
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Rational -> Rational
fractionalPart (Rational -> Rational)
-> ((Int, Rational) -> Rational) -> (Int, Rational) -> Rational
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Int, Rational) -> Rational
forall a b. (a, b) -> b
snd))
NonEmpty (Int, Rational)
-> (NonEmpty (Int, Rational) -> NonEmpty (Int, Integer))
-> NonEmpty (Int, Integer)
forall a b. a -> (a -> b) -> b
& (RoundingDirection -> (Int, Rational) -> (Int, Integer))
-> NonEmpty RoundingDirection
-> NonEmpty (Int, Rational)
-> NonEmpty (Int, Integer)
forall a b c.
(a -> b -> c) -> NonEmpty a -> NonEmpty b -> NonEmpty c
NE.zipWith (\RoundingDirection
roundDir (Int
i, Rational
f) -> (Int
i, RoundingDirection -> Rational -> Integer
forall a b. (RealFrac a, Integral b) => RoundingDirection -> a -> b
round RoundingDirection
roundDir Rational
f)) NonEmpty RoundingDirection
feeRoundings
NonEmpty (Int, Integer)
-> (NonEmpty (Int, Integer) -> NonEmpty (Int, Integer))
-> NonEmpty (Int, Integer)
forall a b. a -> (a -> b) -> b
& ((Int, Integer) -> (Int, Integer) -> Ordering)
-> NonEmpty (Int, Integer) -> NonEmpty (Int, Integer)
forall a. (a -> a -> Ordering) -> NonEmpty a -> NonEmpty a
NE.sortBy (((Int, Integer) -> Int)
-> (Int, Integer) -> (Int, Integer) -> Ordering
forall a b. Ord a => (b -> a) -> b -> b -> Ordering
comparing (Int, Integer) -> Int
forall a b. (a, b) -> a
fst)
NonEmpty (Int, Integer)
-> (NonEmpty (Int, Integer) -> NonEmpty Integer)
-> NonEmpty Integer
forall a b. a -> (a -> b) -> b
& ((Int, Integer) -> Integer)
-> NonEmpty (Int, Integer) -> NonEmpty Integer
forall a b. (a -> b) -> NonEmpty a -> NonEmpty b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Int, Integer) -> Integer
forall a b. (a, b) -> b
snd
NonEmpty Integer
-> (NonEmpty Integer -> NonEmpty Fee) -> NonEmpty Fee
forall a b. a -> (a -> b) -> b
& (Integer -> Fee) -> NonEmpty Integer -> NonEmpty Fee
forall a b. (a -> b) -> NonEmpty a -> NonEmpty b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Coin -> Fee
Fee (Coin -> Fee) -> (Integer -> Coin) -> Integer -> Fee
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Coin -> Maybe Coin -> Coin
forall a. a -> Maybe a -> a
fromMaybe Coin
C.zero (Maybe Coin -> Coin) -> (Integer -> Maybe Coin) -> Integer -> Coin
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall i. Integral i => i -> Maybe Coin
C.coinFromIntegral @Integer)
where
indices :: NonEmpty Int
indices :: NonEmpty Int
indices = Int
0 Int -> [Int] -> NonEmpty Int
forall a. a -> [a] -> NonEmpty a
:| [Int
1 ..]
feeRoundings :: NonEmpty RoundingDirection
feeRoundings :: NonEmpty RoundingDirection
feeRoundings =
Int
-> (NonEmpty RoundingDirection -> NonEmpty RoundingDirection)
-> NonEmpty RoundingDirection
-> NonEmpty RoundingDirection
forall a. Int -> (a -> a) -> a -> a
applyN Int
feeShortfall (RoundingDirection
-> NonEmpty RoundingDirection -> NonEmpty RoundingDirection
forall a. a -> NonEmpty a -> NonEmpty a
NE.cons RoundingDirection
RoundUp) (RoundingDirection -> NonEmpty RoundingDirection
forall a. a -> NonEmpty a
NE.repeat RoundingDirection
RoundDown)
where
feeShortfall :: Int
feeShortfall
= Coin -> Int
forall i. Integral i => Coin -> i
C.coinToIntegral Coin
feeTotal
Int -> Int -> Int
forall a. Num a => a -> a -> a
- forall a b. (Integral a, Num b) => a -> b
fromIntegral @Integer (NonEmpty Integer -> Integer
forall a. Num a => NonEmpty a -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
F.sum (NonEmpty Integer -> Integer) -> NonEmpty Integer -> Integer
forall a b. (a -> b) -> a -> b
$ RoundingDirection -> Rational -> Integer
forall a b. (RealFrac a, Integral b) => RoundingDirection -> a -> b
round RoundingDirection
RoundDown (Rational -> Integer) -> NonEmpty Rational -> NonEmpty Integer
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> NonEmpty Rational
feesUnrounded)
feesUnrounded :: NonEmpty Rational
feesUnrounded :: NonEmpty Rational
feesUnrounded = Coin -> Rational
calculateIdealFee (Coin -> Rational) -> NonEmpty Coin -> NonEmpty Rational
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> NonEmpty Coin
coins
where
calculateIdealFee :: Coin -> Rational
calculateIdealFee Coin
c
= Coin -> Integer
forall i. Integral i => Coin -> i
C.coinToIntegral Coin
c
Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Coin -> Integer
forall i. Integral i => Coin -> i
C.coinToIntegral Coin
feeTotal
Integer -> Integer -> Rational
forall a. Integral a => a -> a -> Ratio a
% Coin -> Integer
forall i. Integral i => Coin -> i
C.coinToIntegral Coin
totalCoinValue
totalCoinValue :: Coin
totalCoinValue :: Coin
totalCoinValue = NonEmpty Coin -> Coin
forall m. Monoid m => NonEmpty m -> m
forall (t :: * -> *) m. (Foldable t, Monoid m) => t m -> m
F.fold NonEmpty Coin
coins
coalesceDust :: DustThreshold -> NonEmpty Coin -> [Coin]
coalesceDust :: DustThreshold -> NonEmpty Coin -> [Coin]
coalesceDust DustThreshold
threshold NonEmpty Coin
coins =
Coin -> [Coin] -> [Coin]
splitCoin Coin
valueToDistribute [Coin]
coinsToKeep
where
([Coin]
coinsToRemove, [Coin]
coinsToKeep) = (Coin -> Bool) -> NonEmpty Coin -> ([Coin], [Coin])
forall a. (a -> Bool) -> NonEmpty a -> ([a], [a])
NE.partition (DustThreshold -> Coin -> Bool
isDust DustThreshold
threshold) NonEmpty Coin
coins
valueToDistribute :: Coin
valueToDistribute = [Coin] -> Coin
forall m. Monoid m => [m] -> m
forall (t :: * -> *) m. (Foldable t, Monoid m) => t m -> m
F.fold [Coin]
coinsToRemove
splitCoin :: Coin -> [Coin] -> [Coin]
splitCoin :: Coin -> [Coin] -> [Coin]
splitCoin Coin
coinToSplit [Coin]
coinsToIncrease =
case (Maybe Coin
mIncrement, Maybe Coin
mShortfall) of
(Just Coin
increment, Just Coin
shortfall) ->
(Coin -> Coin -> Coin) -> [Coin] -> [Coin] -> [Coin]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith Coin -> Coin -> Coin
C.add [Coin]
coinsToIncrease [Coin]
increments
where
increments :: [Coin]
increments = (Coin -> Coin -> Coin) -> [Coin] -> [Coin] -> [Coin]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith Coin -> Coin -> Coin
C.add [Coin]
majorIncrements [Coin]
minorIncrements
majorIncrements :: [Coin]
majorIncrements = Coin -> [Coin]
forall a. a -> [a]
repeat Coin
increment
minorIncrements :: [Coin]
minorIncrements = Int -> Coin -> [Coin]
forall a. Int -> a -> [a]
replicate (Coin -> Int
forall i. Integral i => Coin -> i
C.coinToIntegral Coin
shortfall) Coin
C.one
[Coin] -> [Coin] -> [Coin]
forall a. Semigroup a => a -> a -> a
<> Coin -> [Coin]
forall a. a -> [a]
repeat Coin
C.zero
(Maybe Coin, Maybe Coin)
_ | Coin
coinToSplit Coin -> Coin -> Bool
forall a. Ord a => a -> a -> Bool
> Coin
C.zero ->
[Coin
coinToSplit]
(Maybe Coin, Maybe Coin)
_ ->
[]
where
mCoinCount :: Int
mCoinCount = [Coin] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Coin]
coinsToIncrease
mIncrement :: Maybe Coin
mIncrement = Coin
coinToSplit Coin -> Int -> Maybe Coin
forall i. Integral i => Coin -> i -> Maybe Coin
`C.div` Int
mCoinCount
mShortfall :: Maybe Coin
mShortfall = Coin
coinToSplit Coin -> Int -> Maybe Coin
forall i. Integral i => Coin -> i -> Maybe Coin
`C.mod` Int
mCoinCount
fractionalPart :: Rational -> Rational
fractionalPart :: Rational -> Rational
fractionalPart = (Integer, Rational) -> Rational
forall a b. (a, b) -> b
snd ((Integer, Rational) -> Rational)
-> (Rational -> (Integer, Rational)) -> Rational -> Rational
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (RealFrac a, Integral b) => a -> (b, a)
properFraction @_ @Integer
applyN :: Int -> (a -> a) -> a -> a
applyN :: forall a. Int -> (a -> a) -> a -> a
applyN Int
n a -> a
f = ((a -> a) -> (a -> a) -> a -> a) -> (a -> a) -> [a -> a] -> a -> a
forall a b. (a -> b -> b) -> b -> [a] -> b
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
F.foldr (a -> a) -> (a -> a) -> a -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
(.) a -> a
forall a. a -> a
id (Int -> (a -> a) -> [a -> a]
forall a. Int -> a -> [a]
replicate Int
n a -> a
f)
sumEntries :: [CoinMapEntry i] -> Coin
sumEntries :: forall i. [CoinMapEntry i] -> Coin
sumEntries = [Coin] -> Coin
forall m. Monoid m => [m] -> m
forall (t :: * -> *) m. (Foldable t, Monoid m) => t m -> m
F.fold ([Coin] -> Coin)
-> ([CoinMapEntry i] -> [Coin]) -> [CoinMapEntry i] -> Coin
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (CoinMapEntry i -> Coin) -> [CoinMapEntry i] -> [Coin]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap CoinMapEntry i -> Coin
forall a. CoinMapEntry a -> Coin
entryValue
payFee :: (Fee, Coin) -> Coin
payFee :: (Fee, Coin) -> Coin
payFee (Fee Coin
f, Coin
c) = Coin -> Maybe Coin -> Coin
forall a. a -> Maybe a -> a
fromMaybe Coin
C.zero (Coin
c Coin -> Coin -> Maybe Coin
`C.sub` Coin
f)