Skip to content

Commit d8e66ac

Browse files
committed
add BalanceTxConstraints to the balancing algorithm
1 parent a263d2f commit d8e66ac

File tree

2 files changed

+70
-66
lines changed

2 files changed

+70
-66
lines changed

src/BotPlutusInterface/Balance.hs

Lines changed: 61 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
{-# LANGUAGE NamedFieldPuns #-}
33

44
module BotPlutusInterface.Balance (
5+
BalanceTxConstraint(..),
56
balanceTxStep,
67
balanceTxIO,
8+
balanceTxIO',
79
txUsesScripts,
810
withFee,
911
) where
@@ -18,7 +20,7 @@ import BotPlutusInterface.Effects (
1820
)
1921
import BotPlutusInterface.Files (DummyPrivKey, unDummyPrivateKey)
2022
import BotPlutusInterface.Files qualified as Files
21-
import BotPlutusInterface.Types (CollateralUtxo, LogLevel (Debug), PABConfig, collateralTxOutRef, collateralValue)
23+
import BotPlutusInterface.Types (CollateralUtxo, LogLevel (Debug), PABConfig, collateralTxOutRef)
2224
import Cardano.Api (ExecutionUnitPrices (ExecutionUnitPrices))
2325
import Cardano.Api.Shelley (ProtocolParameters (protocolParamPrices))
2426
import Control.Monad (foldM, void, zipWithM)
@@ -34,6 +36,7 @@ import Data.List (uncons, (\\))
3436
import Data.Map (Map)
3537
import Data.Map qualified as Map
3638
import Data.Maybe (fromMaybe, mapMaybe)
39+
import Data.Proxy (Proxy(Proxy))
3740
import Data.Set (Set)
3841
import Data.Set qualified as Set
3942
import Data.Text (Text)
@@ -74,57 +77,53 @@ import BotPlutusInterface.Collateral (removeCollateralFromMap)
7477
import Prettyprinter (pretty, viaShow, (<+>))
7578
import Prelude
7679

77-
-- `BalanceTx` is for checking which type of `Tx` we are balancing.
78-
data BalanceTx
79-
= -- | `BalanceTxCollateral` denotes that we are balancing the `Tx` that creates collateral.
80-
BalanceTxCollateral
81-
| -- | `BalanceTxWithScripts` denotes that we are balancing the `Tx` that uses scripts.
82-
BalanceTxWithScripts
83-
| -- | `BalanceTxWithoutScripts` denotes that we are balancing the `Tx` that doesn't uses any scripts.
84-
BalanceTxWithoutScripts
85-
deriving stock (Eq)
86-
87-
-- `balanceTxIO` checks the type of the current `UnbalancedTx` and then calls `balanceTxIO'`.
80+
data TxWithScript
81+
data TxWithoutScript
82+
83+
data BalanceTxConstraint a where
84+
TxWithScript :: BalanceTxConstraint TxWithScript
85+
TxWithoutScript :: BalanceTxConstraint TxWithoutScript
86+
TxWithSeparateChange :: BalanceTxConstraint a
87+
88+
instance Eq (BalanceTxConstraint a) where
89+
TxWithScript == TxWithScript = True
90+
TxWithoutScript == TxWithoutScript = True
91+
TxWithSeparateChange == TxWithSeparateChange = True
92+
_ == _ = False
93+
94+
class KnowBalanceConstraint (a :: Type) where
95+
constraint :: proxy a -> BalanceTxConstraint a
96+
97+
instance KnowBalanceConstraint TxWithScript where
98+
constraint _ = TxWithScript
99+
100+
instance KnowBalanceConstraint TxWithoutScript where
101+
constraint _ = TxWithoutScript
102+
88103
balanceTxIO ::
89104
forall (w :: Type) (effs :: [Type -> Type]).
90105
(Member (PABEffect w) effs) =>
91106
PABConfig ->
92107
PubKeyHash ->
93108
UnbalancedTx ->
94109
Eff effs (Either Text Tx)
95-
balanceTxIO pabConf ownPkh unbalancedTx@(UnbalancedTx tx' _ _ _)
96-
| validCollateralTx tx' = balanceTxIO' @w pabConf ownPkh unbalancedTx BalanceTxCollateral
97-
| txUsesScripts tx' = balanceTxIO' @w pabConf ownPkh unbalancedTx BalanceTxWithScripts
98-
| otherwise = balanceTxIO' @w pabConf ownPkh unbalancedTx BalanceTxWithoutScripts
99-
where
100-
validCollateralTx :: Tx -> Bool
101-
validCollateralTx tx
102-
| [out] <- txOutputs tx
103-
, Map.size (txData tx) == 0
104-
, txMint tx == mempty
105-
, txOutValue out == collateralValue pabConf
106-
, txMintScripts tx == mempty
107-
, txCollateral tx == mempty
108-
, not (txUsesScripts tx) =
109-
True
110-
| otherwise =
111-
False
110+
balanceTxIO = balanceTxIO' @w [TxWithScript]
112111

113112
{- | Collect necessary tx inputs and collaterals, add minimum lovelace values and balance non ada
114113
assets
115114
-}
116115
balanceTxIO' ::
117-
forall (w :: Type) (effs :: [Type -> Type]).
118-
(Member (PABEffect w) effs) =>
116+
forall (w :: Type) (effs :: [Type -> Type]) (a :: Type) .
117+
(Member (PABEffect w) effs, KnowBalanceConstraint a) =>
118+
[BalanceTxConstraint a] ->
119119
PABConfig ->
120120
PubKeyHash ->
121121
UnbalancedTx ->
122-
BalanceTx ->
123122
Eff effs (Either Text Tx)
124-
balanceTxIO' pabConf ownPkh unbalancedTx balanceTxType =
123+
balanceTxIO' balanceTxconstraints pabConf ownPkh unbalancedTx =
125124
runEitherT $
126125
do
127-
(utxos, mcollateral) <- newEitherT $ utxosAndCollateralAtAddress @w balanceTxType pabConf changeAddr
126+
(utxos, mcollateral) <- newEitherT $ utxosAndCollateralAtAddress @w balanceTxconstraints pabConf changeAddr
128127
privKeys <- newEitherT $ Files.readPrivateKeys @w pabConf
129128

130129
let utxoIndex = fmap Tx.toTxOut utxos <> unBalancedTxUtxoIndex unbalancedTx
@@ -143,14 +142,13 @@ balanceTxIO' pabConf ownPkh unbalancedTx balanceTxType =
143142

144143
-- Adds required collaterals in the `Tx`, if the `Tx` is of type `BalanceTxWithScripts`.
145144
-- Also adds signatures for fee calculation
146-
preBalancedTx <-
147-
case balanceTxType of
148-
BalanceTxWithScripts ->
149-
maybe
150-
(throwE "Tx uses script but no collateral was provided.")
151-
(hoistEither . addSignatories ownPkh privKeys requiredSigs . flip addTxCollaterals tx)
152-
mcollateral
153-
_ -> hoistEither $ addSignatories ownPkh privKeys requiredSigs tx
145+
146+
preBalancedTx <- case constraint @a Proxy of
147+
TxWithScript -> maybe
148+
(throwE "Tx uses script but no collateral was provided.")
149+
(hoistEither . addSignatories ownPkh privKeys requiredSigs . flip addTxCollaterals tx)
150+
mcollateral
151+
_ -> hoistEither $ addSignatories ownPkh privKeys requiredSigs tx
154152

155153
-- Balance the tx
156154
(balancedTx, minUtxos) <- balanceTxLoop utxoIndex privKeys [] preBalancedTx
@@ -159,30 +157,30 @@ balanceTxIO' pabConf ownPkh unbalancedTx balanceTxType =
159157
let adaChange = getAdaChange utxoIndex balancedTx
160158
-- This represents the collateral TxOut, in cases of `BalanceTxWithoutScripts` & `BalanceTxWithScripts`
161159
-- we don't create any collateral TxOut, hence the result is Nothing.
162-
collateralTxOut = case balanceTxType of
163-
BalanceTxCollateral -> fmap fst . uncons . txOutputs $ unBalancedTxTx unbalancedTx
164-
BalanceTxWithoutScripts -> Nothing
165-
BalanceTxWithScripts -> Nothing
160+
ownTxOut = if TxWithSeparateChange `elem` balanceTxconstraints
161+
then fmap fst . uncons $ filter ((== changeAddr) . txOutAddress) $ txOutputs $ unBalancedTxTx unbalancedTx
162+
else Nothing
166163
bTx = fst <$> balanceTxLoop utxoIndex privKeys minUtxos (addOutput changeAddr balancedTx)
167164

168165
-- If we have change but no change UTxO, we need to add an output for it
169166
-- We'll add a minimal output, run the loop again so it gets minUTxO, then update change
170167

171168
balancedTxWithChange <-
172-
case balanceTxType of
173-
BalanceTxCollateral | adaChange /= 0 -> bTx
174-
_ | adaChange /= 0 && not (hasChangeUTxO changeAddr balancedTx) -> bTx
175-
_ -> pure balancedTx
176-
169+
case adaChange /= 0 of
170+
True | TxWithSeparateChange `elem` balanceTxconstraints -> bTx
171+
True | not (hasChangeUTxO changeAddr balancedTx) -> bTx
172+
True -> pure balancedTx
173+
False -> pure balancedTx
174+
177175
-- Get the updated change, add it to the tx
178176
let finalAdaChange = getAdaChange utxoIndex balancedTxWithChange
179-
fullyBalancedTx = addAdaChange changeAddr finalAdaChange balancedTxWithChange collateralTxOut
177+
fullyBalancedTx = addAdaChange changeAddr finalAdaChange balancedTxWithChange ownTxOut
180178

181179
-- finally, we must update the signatories
182180
hoistEither $ addSignatories ownPkh privKeys requiredSigs fullyBalancedTx
183181
where
184182
changeAddr :: Address
185-
changeAddr = Ledger.pubKeyHashAddress (Ledger.PaymentPubKeyHash ownPkh) (pabConf.pcOwnStakePubKeyHash)
183+
changeAddr = Ledger.pubKeyHashAddress (Ledger.PaymentPubKeyHash ownPkh) pabConf.pcOwnStakePubKeyHash
186184

187185
balanceTxLoop ::
188186
Map TxOutRef TxOut ->
@@ -219,29 +217,27 @@ balanceTxIO' pabConf ownPkh unbalancedTx balanceTxType =
219217
then pure (balancedTx, minUtxos)
220218
else balanceTxLoop utxoIndex privKeys minUtxos balancedTx
221219

222-
-- `utxosAndCollateralAtAddress` returns all the utxos that can be used as input of a `Tx`,
223-
-- i.e. we filter out `CollateralUtxo` present at the user's address, so it can't be used as input of a `Tx`.
224-
-- This function throws error if the `Tx` type is of `BalanceTxWithScripts` but there's not `CollateralUtxo`
225-
-- in the environment.
226220
utxosAndCollateralAtAddress ::
227-
forall (w :: Type) (effs :: [Type -> Type]).
228-
Member (PABEffect w) effs =>
229-
BalanceTx ->
221+
forall (w :: Type) (effs :: [Type -> Type]) (a :: Type).
222+
(Member (PABEffect w) effs, KnowBalanceConstraint a) =>
223+
[BalanceTxConstraint a] ->
230224
PABConfig ->
231225
Address ->
232226
Eff effs (Either Text (Map TxOutRef Tx.ChainIndexTxOut, Maybe CollateralUtxo))
233-
utxosAndCollateralAtAddress balanceTxType pabConf changeAddr =
227+
utxosAndCollateralAtAddress _ pabConf changeAddr =
234228
runEitherT $ do
235229
utxos <- newEitherT $ CardanoCLI.utxosAt @w pabConf changeAddr
236230
inMemCollateral <- lift $ getInMemCollateral @w
237231

238-
case balanceTxType of
239-
BalanceTxCollateral -> pure (utxos, Nothing)
240-
BalanceTxWithoutScripts -> pure (removeCollateralFromMap inMemCollateral utxos, Nothing)
241-
BalanceTxWithScripts ->
232+
case constraint @a Proxy of
233+
TxWithScript ->
242234
case inMemCollateral of
243235
Nothing -> throwE "The given transaction uses script, but there's no collateral provided. This usually means that, we failed to create Tx and update our ContractEnvironment."
244236
Just _ -> pure (removeCollateralFromMap inMemCollateral utxos, inMemCollateral)
237+
238+
TxWithoutScript -> pure (removeCollateralFromMap inMemCollateral utxos, Nothing)
239+
240+
_ -> pure (utxos, Nothing)
245241

246242
hasChangeUTxO :: Address -> Tx -> Bool
247243
hasChangeUTxO changeAddr tx =

src/BotPlutusInterface/Contract.hs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,11 @@ balanceTx ::
287287
Eff effs BalanceTxResponse
288288
balanceTx contractEnv unbalancedTx = do
289289
let pabConf = contractEnv.cePABConfig
290+
-- balanceTxconstaints =
291+
-- if PreBalance.txUsesScripts (unBalancedTxTx unbalancedTx)
292+
-- then [PreBalance.TxWithScript]
293+
-- else [PreBalance.TxWithoutScript]
294+
290295

291296
result <- handleCollateral @w contractEnv
292297

@@ -497,7 +502,10 @@ makeCollateral cEnv = runEitherT $ do
497502
firstEitherT (T.pack . show) $
498503
hoistEither $ Collateral.mkCollateralTx pabConf
499504

500-
balancedTx <- newEitherT $ PreBalance.balanceTxIO @w pabConf pabConf.pcOwnPubKeyHash unbalancedTx
505+
balancedTx <- newEitherT
506+
$ PreBalance.balanceTxIO' @w
507+
[PreBalance.TxWithoutScript, PreBalance.TxWithSeparateChange]
508+
pabConf pabConf.pcOwnPubKeyHash unbalancedTx
501509

502510
wbr <- lift $ writeBalancedTx cEnv (Right balancedTx)
503511
case wbr of

0 commit comments

Comments
 (0)