1+ {-# LANGUAGE AllowAmbiguousTypes #-}
2+
13module BotPlutusInterface.CoinSelection (valueToVec , valuesToVecs , selectTxIns ) where
24
35import Control.Lens (Cons , cons , ix , uncons , (^?) )
6+ import Control.Monad.Freer (Eff , Member )
47
5- import Data.Either.Combinators (maybeToRight )
8+ import Control.Monad.Trans.Class (lift )
9+ import Control.Monad.Trans.Either (hoistEither , runEitherT )
10+ import Data.Either.Combinators (isRight , maybeToRight )
611import Data.Kind (Type )
712import Data.Map (Map )
813import Data.Map qualified as Map
@@ -20,44 +25,70 @@ import Plutus.V1.Ledger.Api (
2025 Credential (PubKeyCredential , ScriptCredential ),
2126 )
2227
28+ import BotPlutusInterface.Effects (PABEffect , printBpiLog )
29+ import BotPlutusInterface.Types (LogLevel (Notice ))
30+
31+ import Prettyprinter (pretty , (<+>) )
2332import Prelude
2433
2534data Search = Greedy
2635 deriving stock (Show )
2736
28- selectTxIns :: Set TxIn -> Map TxOutRef TxOut -> Value -> Either Text (Set TxIn )
29- selectTxIns originalTxIns utxosIndex outValue = do
30- let txInsValue :: Value
31- txInsValue =
32- mconcat $ map txOutValue $ mapMaybe ((`Map.lookup` utxosIndex) . txInRef) $ Set. toList originalTxIns
37+ selectTxIns ::
38+ forall (w :: Type ) (effs :: [Type -> Type ]).
39+ Member (PABEffect w ) effs =>
40+ Set TxIn ->
41+ Map TxOutRef TxOut ->
42+ Value ->
43+ Eff effs (Either Text (Set TxIn ))
44+ selectTxIns originalTxIns utxosIndex outValue =
45+ runEitherT $ do
46+ lift $ printBpiLog @ w Notice $ pretty (Map. toList utxosIndex)
47+
48+ let txInsValue :: Value
49+ txInsValue =
50+ mconcat $ map txOutValue $ mapMaybe ((`Map.lookup` utxosIndex) . txInRef) $ Set. toList originalTxIns
51+
52+ allAssetClasses :: Set AssetClass
53+ allAssetClasses =
54+ uniqueAssetClasses $ txInsValue : outValue : map (txOutValue . snd ) (Map. toList utxosIndex)
3355
34- allAssetClasses :: Set AssetClass
35- allAssetClasses =
36- uniqueAssetClasses $ txInsValue : outValue : map (txOutValue . snd ) (Map. toList utxosIndex)
56+ txInRefs :: [TxOutRef ]
57+ txInRefs = map txInRef $ Set. toList originalTxIns
3758
38- txInRefs :: [TxOutRef ]
39- txInRefs = map txInRef $ Set. toList originalTxIns
59+ remainingUtxos :: [(TxOutRef , TxOut )]
60+ remainingUtxos =
61+ Map. toList $
62+ Map. filterWithKey
63+ (\ k v -> k `notElem` txInRefs && isRight (txOutToTxIn (k, v)))
64+ utxosIndex
4065
41- remainingUtxos :: [(TxOutRef , TxOut )]
42- remainingUtxos = Map. toList $ Map. filterWithKey (\ k _ -> k `notElem` txInRefs) utxosIndex
66+ lift $ printBpiLog @ w Notice $ " \n\n Remaining UTxOs: " <+> pretty remainingUtxos <+> " \n\n "
4367
44- txInsVec <-
45- if Value. isZero txInsValue
46- then Right $ zeroVec (toInteger $ length allAssetClasses)
47- else valueToVec allAssetClasses txInsValue
68+ txInsVec <-
69+ hoistEither $
70+ if Value. isZero txInsValue
71+ then Right $ zeroVec (toInteger $ length allAssetClasses)
72+ else valueToVec allAssetClasses txInsValue
4873
49- outVec <- valueToVec allAssetClasses outValue
74+ outVec <- hoistEither $ valueToVec allAssetClasses outValue
5075
51- remainingUtxosVec <- mapM (valueToVec allAssetClasses . txOutValue . snd ) remainingUtxos
76+ lift $ printBpiLog @ w Notice $ " IsSufficient: " <+> pretty (isSufficient outVec txInsVec) <+> " \n\n "
5277
53- selectedUtxosIdxs <- selectTxIns' Greedy (isSufficient outVec) outVec txInsVec remainingUtxosVec
78+ remainingUtxosVec <- hoistEither $ mapM (valueToVec allAssetClasses . txOutValue . snd ) remainingUtxos
5479
55- let selectedUtxos :: [(TxOutRef , TxOut )]
56- selectedUtxos = mapMaybe (\ idx -> remainingUtxos ^? ix (fromInteger idx)) selectedUtxosIdxs
80+ selectedUtxosIdxs <- hoistEither $ selectTxIns' Greedy (isSufficient outVec) outVec txInsVec remainingUtxosVec
5781
58- selectedTxIns <- mapM txOutToTxIn selectedUtxos
82+ lift $ printBpiLog @ w Notice $ " \n\n " <+> " Selected UTxOs Index: " <+> pretty selectedUtxosIdxs <+> " \n\n "
5983
60- return $ originalTxIns <> Set. fromList selectedTxIns
84+ let selectedUtxos :: [(TxOutRef , TxOut )]
85+ selectedUtxos = mapMaybe (\ idx -> remainingUtxos ^? ix (fromInteger idx)) selectedUtxosIdxs
86+
87+ selectedTxIns <- hoistEither $ mapM txOutToTxIn selectedUtxos
88+
89+ lift $ printBpiLog @ w Notice $ " Selected TxIns: " <+> pretty selectedTxIns <+> " \n\n "
90+
91+ return $ originalTxIns <> Set. fromList selectedTxIns
6192 where
6293 isSufficient :: Vector Integer -> Vector Integer -> Bool
6394 isSufficient outVec = Vec. all (== True ) . Vec. zipWith (<=) outVec
@@ -69,20 +100,20 @@ selectTxIns' ::
69100 Vector Integer ->
70101 [Vector Integer ] ->
71102 Either Text [Integer ]
72- selectTxIns' Greedy stopSearch outVec txInsVec utxosVec =
73- if null utxosVec
74- then Right mempty
75- else do
103+ selectTxIns' Greedy stopSearch outVec txInsVec utxosVec
104+ | null utxosVec || stopSearch txInsVec = Right mempty
105+ | otherwise =
106+ do
76107 utxosDist <- Vec. fromList . map (l2norm outVec) <$> mapM (addVec txInsVec) utxosVec
77108 let minIndex = toInteger $ Vec. minIndex utxosDist
78109
79- (selectedUtxoVec, remainingUtxosVec) <- popN utxosVec minIndex
110+ (selectedUtxoVec, remainingUtxosVec) <- pop utxosVec minIndex
80111
81112 newTxInsVec <- addVec txInsVec selectedUtxoVec
82113
83- case stopSearch newTxInsVec of
84- True -> return [minIndex]
85- False -> (minIndex : ) <$> selectTxIns' Greedy stopSearch outVec newTxInsVec remainingUtxosVec
114+ if stopSearch newTxInsVec
115+ then return [minIndex]
116+ else (minIndex : ) <$> selectTxIns' Greedy stopSearch outVec newTxInsVec remainingUtxosVec
86117
87118l2norm :: Vector Integer -> Vector Integer -> Either Text Float
88119l2norm v1 v2
@@ -144,15 +175,15 @@ txOutToTxIn (txOutRef, txOut) =
144175 PubKeyCredential _ -> Right $ pubKeyTxIn txOutRef
145176 ScriptCredential _ -> Left " Cannot covert a script output to TxIn"
146177
147- popN ::
178+ pop ::
148179 forall (v :: Type -> Type ) a .
149180 (Cons (v a ) (v a ) a a ) =>
150181 v a ->
151182 Integer ->
152183 Either Text (a , v a )
153- popN va idx = do
184+ pop va idx = do
154185 (a, va') <- maybeToRight " Error: Not able to uncons from empty structure." $ uncons va
155186
156187 if idx == 0
157188 then return (a, va')
158- else popN va' (idx - 1 ) >>= (\ (a', va'') -> return (a', cons a va''))
189+ else pop va' (idx - 1 ) >>= (\ (a', va'') -> return (a', cons a va''))
0 commit comments