22{-# LANGUAGE NamedFieldPuns #-}
33
44module BotPlutusInterface.Balance (
5- BalanceTxConstraint ( TxWithScript , TxWithoutScript , TxWithSeparateChange ),
5+ BalanceConfig ( BalanceConfig , bcHasScripts , bcSeparateChange ),
66 balanceTxStep ,
77 balanceTxIO ,
88 balanceTxIO' ,
@@ -21,7 +21,7 @@ import BotPlutusInterface.Effects (
2121import BotPlutusInterface.Files (DummyPrivKey , unDummyPrivateKey )
2222import BotPlutusInterface.Files qualified as Files
2323import BotPlutusInterface.Types (CollateralUtxo , LogLevel (Debug ), PABConfig , collateralTxOutRef )
24- import Cardano.Api (ExecutionUnitPrices (ExecutionUnitPrices ))
24+ import Cardano.Api (ExecutionUnitPrices (ExecutionUnitPrices ), UTxO ( UTxO ) )
2525import Cardano.Api.Shelley (ProtocolParameters (protocolParamPrices ))
2626import Control.Monad (foldM , void , zipWithM )
2727import Control.Monad.Freer (Eff , Member )
@@ -30,6 +30,7 @@ import Control.Monad.Trans.Either (EitherT, hoistEither, newEitherT, runEitherT)
3030import Control.Monad.Trans.Except (throwE )
3131import Data.Bifunctor (bimap )
3232import Data.Coerce (coerce )
33+ import Data.Default (Default (def ))
3334import Data.Either.Combinators (rightToMaybe )
3435import Data.Kind (Type )
3536import Data.List ((\\) )
@@ -77,67 +78,41 @@ import BotPlutusInterface.Collateral (removeCollateralFromMap)
7778import Prettyprinter (pretty , viaShow , (<+>) )
7879import Prelude
7980
80- data TxWithScript
81+ -- Config for balancing a `Tx`.
82+ data BalanceConfig = BalanceConfig
83+ { bcHasScripts :: Bool -- ^ This field represents whether the current `Tx` that needs to be balanced uses scripts.
84+ , bcSeparateChange :: Bool -- ^ This field represents whether the ada change should be in separate UTxO.
85+ }
86+ deriving stock (Show , Eq )
8187
82- data TxWithoutScript
83-
84- data BalanceTxConstraint (a :: Type ) where
85- TxWithoutScript :: BalanceTxConstraint TxWithoutScript
86- TxWithScript :: BalanceTxConstraint TxWithScript
87- TxWithSeparateChange :: BalanceTxConstraint a
88-
89- instance Eq (BalanceTxConstraint a ) where
90- TxWithScript == TxWithScript = True
91- TxWithoutScript == TxWithoutScript = True
92- TxWithSeparateChange == TxWithSeparateChange = True
93- _ == _ = False
94-
95- instance Ord (BalanceTxConstraint a ) where
96- _ <= TxWithSeparateChange = True
97- TxWithScript <= TxWithScript = True
98- TxWithSeparateChange <= TxWithScript = False
99- TxWithoutScript <= TxWithoutScript = True
100- _ <= TxWithoutScript = False
101-
102- class KnownBalanceConstraint (a :: Type ) where
103- knownConstraint :: BalanceTxConstraint a
104-
105- instance KnownBalanceConstraint TxWithScript where
106- knownConstraint = TxWithScript
107-
108- instance KnownBalanceConstraint TxWithoutScript where
109- knownConstraint = TxWithoutScript
88+ instance Default BalanceConfig where
89+ def = BalanceConfig {bcHasScripts = False , bcSeparateChange = False }
11090
11191{- | Collect necessary tx inputs and collaterals, add minimum lovelace values and balance non ada
112- assets. `balanceTxIO` assumes that the `Tx` we are balancing is of type `TxWithoutScript`.
113- If you want to add custom constraints while balancing the `Tx` like, saying that `Tx` contains
114- scripts refer to `balanceTxIO'`.
92+ assets. `balanceTxIO` calls `balanceTxIO' with default `BalanceConfig`.
11593-}
11694balanceTxIO ::
117- forall (w :: Type ) (effs :: [Type -> Type ]) (a :: Type ).
118- (Member (PABEffect w ) effs , KnownBalanceConstraint a ) =>
119- [BalanceTxConstraint a ] ->
95+ forall (w :: Type ) (effs :: [Type -> Type ]).
96+ (Member (PABEffect w ) effs ) =>
12097 PABConfig ->
12198 PubKeyHash ->
12299 UnbalancedTx ->
123100 Eff effs (Either Text Tx )
124- balanceTxIO = balanceTxIO' @ w . Set. fromList
101+ balanceTxIO = balanceTxIO' @ w def
125102
126- {- | This is just a more flexible version of `balanceTxIO` which let us specify the `BalanceTxConstraint`(s)
127- - for the `Tx` that we are balancing.
128- -}
103+ -- | `balanceTxIO'` is more flexible version of `balanceTxIO`, this let us specify custom `BalanceConfig`.
129104balanceTxIO' ::
130- forall (w :: Type ) (effs :: [Type -> Type ]) ( a :: Type ) .
131- (Member (PABEffect w ) effs , KnownBalanceConstraint a ) =>
132- Set ( BalanceTxConstraint a ) ->
105+ forall (w :: Type ) (effs :: [Type -> Type ]).
106+ (Member (PABEffect w ) effs ) =>
107+ BalanceConfig ->
133108 PABConfig ->
134109 PubKeyHash ->
135110 UnbalancedTx ->
136111 Eff effs (Either Text Tx )
137- balanceTxIO' balanceTxconstraints pabConf ownPkh unbalancedTx =
112+ balanceTxIO' balanceCfg pabConf ownPkh unbalancedTx =
138113 runEitherT $
139114 do
140- (utxos, mcollateral) <- newEitherT $ utxosAndCollateralAtAddress @ w balanceTxconstraints pabConf changeAddr
115+ (utxos, mcollateral) <- newEitherT $ utxosAndCollateralAtAddress @ w balanceCfg pabConf changeAddr
141116 privKeys <- newEitherT $ Files. readPrivateKeys @ w pabConf
142117
143118 let utxoIndex :: Map TxOutRef TxOut
@@ -146,9 +121,6 @@ balanceTxIO' balanceTxconstraints pabConf ownPkh unbalancedTx =
146121 requiredSigs :: [PubKeyHash ]
147122 requiredSigs = map Ledger. unPaymentPubKeyHash $ Map. keys (unBalancedTxRequiredSignatories unbalancedTx)
148123
149- txType :: BalanceTxConstraint a
150- txType = knownConstraint @ a
151-
152124 lift $ printBpiLog @ w Debug $ viaShow utxoIndex
153125
154126 -- We need this folder on the CLI machine, which may not be the local machine
@@ -160,15 +132,16 @@ balanceTxIO' balanceTxconstraints pabConf ownPkh unbalancedTx =
160132 (unBalancedTxValidityTimeRange unbalancedTx)
161133 (unBalancedTxTx unbalancedTx)
162134
163- -- Adds required collaterals in the `Tx`, if the `Tx` is of type `TxWithScript`.
164- -- Also adds signatures for fee calculation
165- preBalancedTx <- case txType of
166- TxWithScript ->
167- maybe
168- (throwE " Tx uses script but no collateral was provided." )
169- (hoistEither . addSignatories ownPkh privKeys requiredSigs . flip addTxCollaterals tx)
170- mcollateral
171- _ -> hoistEither $ addSignatories ownPkh privKeys requiredSigs tx
135+ -- Adds required collaterals in the `Tx`, if `bcHasScripts`
136+ -- is true. Also adds signatures for fee calculation
137+ preBalancedTx <-
138+ if bcHasScripts balanceCfg
139+ then
140+ maybe
141+ (throwE " Tx uses script but no collateral was provided." )
142+ (hoistEither . addSignatories ownPkh privKeys requiredSigs . flip addTxCollaterals tx)
143+ mcollateral
144+ else hoistEither $ addSignatories ownPkh privKeys requiredSigs tx
172145
173146 -- Balance the tx
174147 (balancedTx, minUtxos) <- balanceTxLoop utxoIndex privKeys [] preBalancedTx
@@ -177,15 +150,18 @@ balanceTxIO' balanceTxconstraints pabConf ownPkh unbalancedTx =
177150 let adaChange = getAdaChange utxoIndex balancedTx
178151 bTx = fst <$> balanceTxLoop utxoIndex privKeys minUtxos (addOutput changeAddr balancedTx)
179152
153+ -- Checks if there's ada change left, if there is then we check
154+ -- if `bcSeparateChange` is true, if this is the case then we create a new UTxO at
155+ -- the changeAddr.
180156 balancedTxWithChange <-
181157 case adaChange /= 0 of
182- True | TxWithSeparateChange `elem` balanceTxconstraints -> bTx
158+ True | bcSeparateChange balanceCfg -> bTx
183159 True | not (hasChangeUTxO changeAddr balancedTx) -> bTx
184160 _ -> pure balancedTx
185161
186162 -- Get the updated change, add it to the tx
187163 let finalAdaChange = getAdaChange utxoIndex balancedTxWithChange
188- fullyBalancedTx = addAdaChange balanceTxconstraints changeAddr finalAdaChange balancedTxWithChange
164+ fullyBalancedTx = addAdaChange balanceCfg changeAddr finalAdaChange balancedTxWithChange
189165
190166 -- finally, we must update the signatories
191167 hoistEither $ addSignatories ownPkh privKeys requiredSigs fullyBalancedTx
@@ -230,31 +206,30 @@ balanceTxIO' balanceTxconstraints pabConf ownPkh unbalancedTx =
230206
231207-- `utxosAndCollateralAtAddress` returns all the utxos that can be used as an input of a `Tx`,
232208-- i.e. we filter out `CollateralUtxo` present at the user's address, so it can't be used as input of a `Tx`.
233- -- This function throws error if `TxWithScript` constraint is present but there's no `CollateralUtxo`
234- -- in the environment.
235209utxosAndCollateralAtAddress ::
236- forall (w :: Type ) (effs :: [Type -> Type ]) ( a :: Type ) .
237- (Member (PABEffect w ) effs , KnownBalanceConstraint a ) =>
238- Set ( BalanceTxConstraint a ) ->
210+ forall (w :: Type ) (effs :: [Type -> Type ]).
211+ (Member (PABEffect w ) effs ) =>
212+ BalanceConfig ->
239213 PABConfig ->
240214 Address ->
241215 Eff effs (Either Text (Map TxOutRef Tx. ChainIndexTxOut , Maybe CollateralUtxo ))
242- utxosAndCollateralAtAddress _ pabConf changeAddr =
216+ utxosAndCollateralAtAddress balanceCfg pabConf changeAddr =
243217 runEitherT $ do
244218 utxos <- newEitherT $ CardanoCLI. utxosAt @ w pabConf changeAddr
245219 inMemCollateral <- lift $ getInMemCollateral @ w
246220
247- case knownConstraint @ a of
248- TxWithScript ->
221+ -- check if `bcHasScripts` is true, if this is the case then we search of
222+ -- collateral UTxO in the environment, if such collateral is not present we throw Error.
223+ if bcHasScripts balanceCfg
224+ then
249225 maybe
250226 ( throwE $
251227 " The given transaction uses script, but there's no collateral provided."
252228 <> " This usually means that, we failed to create Tx and update our ContractEnvironment."
253229 )
254230 (const $ pure (removeCollateralFromMap inMemCollateral utxos, inMemCollateral))
255231 inMemCollateral
256- TxWithoutScript -> pure (removeCollateralFromMap inMemCollateral utxos, Nothing )
257- _ -> pure (utxos, Nothing )
232+ else pure (removeCollateralFromMap inMemCollateral utxos, Nothing )
258233
259234hasChangeUTxO :: Address -> Tx -> Bool
260235hasChangeUTxO changeAddr tx =
@@ -421,14 +396,14 @@ handleNonAdaChange changeAddr utxos tx =
421396 then Right $ if Value. isZero nonAdaChange then tx else tx {txOutputs = outputs}
422397 else Left " Not enough inputs to balance tokens."
423398
424- {- | `addAdaChange` checks if `TxWithSeparateChange ` is present in the provided balancing
425- constraints, if it is then we add the ada change to seperate `TxOut`, else we add it to
426- any `TxOut` present at changeAddr.
399+ {- | `addAdaChange` checks if `bcSeparateChange ` is true,
400+ if it is then we add the ada change to seperate `TxOut` at changeAddr that contains only ada,
401+ else we add it to any `TxOut` present at changeAddr.
427402-}
428- addAdaChange :: forall ( a :: Type ) . Set ( BalanceTxConstraint a ) -> Address -> Integer -> Tx -> Tx
403+ addAdaChange :: BalanceConfig -> Address -> Integer -> Tx -> Tx
429404addAdaChange _ _ 0 tx = tx
430- addAdaChange balanceTxconstraints changeAddr change tx
431- | TxWithSeparateChange `elem` balanceTxconstraints =
405+ addAdaChange balanceCfg changeAddr change tx
406+ | bcSeparateChange balanceCfg =
432407 tx
433408 { txOutputs =
434409 List. reverse $
0 commit comments