@@ -22,6 +22,7 @@ import Cardano.Types
2222 , Transaction
2323 , TransactionBody
2424 , TransactionOutput
25+ , TransactionUnspentOutput
2526 , UtxoMap
2627 , Value (Value )
2728 , _amount
@@ -41,9 +42,10 @@ import Cardano.Types.Address (Address)
4142import Cardano.Types.BigNum as BigNum
4243import Cardano.Types.Coin as Coin
4344import Cardano.Types.OutputDatum (OutputDatum (OutputDatum))
44- import Cardano.Types.TransactionBody (_votingProposals )
45+ import Cardano.Types.TransactionBody (_collateral , _votingProposals )
4546import Cardano.Types.TransactionInput (TransactionInput )
46- import Cardano.Types.TransactionUnspentOutput as TransactionUnspentOutputs
47+ import Cardano.Types.TransactionUnspentOutput (_output )
48+ import Cardano.Types.TransactionUnspentOutput as TransactionUnspentOutput
4749import Cardano.Types.TransactionWitnessSet (_redeemers )
4850import Cardano.Types.UtxoMap (pprintUtxoMap )
4951import Cardano.Types.Value (getMultiAsset , mkValue , pprintValue )
@@ -65,7 +67,10 @@ import Ctl.Internal.BalanceTx.Collateral
6567 ( addTxCollateral
6668 , addTxCollateralReturn
6769 )
68- import Ctl.Internal.BalanceTx.Collateral.Select (selectCollateral )
70+ import Ctl.Internal.BalanceTx.Collateral.Select
71+ ( minRequiredCollateral
72+ , selectCollateral
73+ ) as Collateral
6974import Ctl.Internal.BalanceTx.Constraints
7075 ( BalanceTxConstraintsBuilder
7176 , _collateralUtxos
@@ -137,7 +142,7 @@ import Data.Array.NonEmpty
137142import Data.Array.NonEmpty as NEA
138143import Data.Bitraversable (ltraverse )
139144import Data.Either (Either , hush , note )
140- import Data.Foldable (fold , foldMap , foldr , length , null , sum )
145+ import Data.Foldable (foldMap , foldr , length , null , sum )
141146import Data.Lens (view )
142147import Data.Lens.Getter ((^.))
143148import Data.Lens.Setter ((%~), (.~), (?~))
@@ -209,11 +214,6 @@ balanceTxWithConstraints transaction extraUtxos constraintsBuilder =
209214 <#> traverse (note CouldNotGetUtxos )
210215 >>> map (foldr Map .union Map .empty) -- merge all utxos into one map
211216
212- unbalancedCollTx <- transactionWithNetworkId >>=
213- if Array .null (transaction ^. _witnessSet <<< _redeemers)
214- -- Don't set collateral if tx doesn't contain phase-2 scripts:
215- then pure
216- else setTransactionCollateral changeAddress
217217 let
218218 allUtxos :: UtxoMap
219219 allUtxos =
@@ -223,6 +223,12 @@ balanceTxWithConstraints transaction extraUtxos constraintsBuilder =
223223
224224 availableUtxos <- liftContract $ filterLockedUtxos allUtxos
225225
226+ unbalancedCollTx <- transactionWithNetworkId >>=
227+ if Array .null (transaction ^. _witnessSet <<< _redeemers)
228+ -- Don't set collateral if tx doesn't contain phase-2 scripts:
229+ then pure
230+ else setTransactionCollateral changeAddress availableUtxos
231+
226232 Logger .info (pprintUtxoMap allUtxos) " balanceTxWithConstraints: all UTxOs"
227233 Logger .info (pprintUtxoMap availableUtxos)
228234 " balanceTxWithConstraints: available UTxOs"
@@ -253,8 +259,9 @@ balanceTxWithConstraints transaction extraUtxos constraintsBuilder =
253259 (transaction ^. _body <<< _networkId)
254260 pure (transaction # _body <<< _networkId ?~ networkId)
255261
256- setTransactionCollateral :: Address -> Transaction -> BalanceTxM Transaction
257- setTransactionCollateral changeAddr transaction = do
262+ setTransactionCollateral
263+ :: Address -> UtxoMap -> Transaction -> BalanceTxM Transaction
264+ setTransactionCollateral changeAddr availableUtxos transaction = do
258265 nonSpendableSet <- asksConstraints _nonSpendableInputs
259266 mbCollateralUtxos <- asksConstraints _collateralUtxos
260267 -- We must filter out UTxOs that are set as non-spendable in the balancer
@@ -272,21 +279,42 @@ setTransactionCollateral changeAddr transaction = do
272279 when (not $ Array .null filteredUtxos) do
273280 logWarn' $ pprintTagSet
274281 " Some of the collateral UTxOs returned by the wallet were marked as non-spendable and ignored"
275- (pprintUtxoMap (TransactionUnspentOutputs .toUtxoMap filteredUtxos))
276- pure spendableUtxos
282+ (pprintUtxoMap (TransactionUnspentOutput .toUtxoMap filteredUtxos))
283+ let
284+ collVal =
285+ foldMap (Val .fromValue <<< view (_output <<< _amount))
286+ spendableUtxos
287+ minRequiredCollateral =
288+ BigNum .toBigInt $
289+ unwrap Collateral .minRequiredCollateral
290+ if (Val .getCoin collVal < minRequiredCollateral) then do
291+ logWarn' $ pprintTagSet
292+ " Filtered collateral UTxOs do not cover the minimum required \
293+ \collateral, reselecting collateral using CTL algorithm."
294+ (pprintUtxoMap (TransactionUnspentOutput .toUtxoMap spendableUtxos))
295+ selectCollateral availableUtxos
296+ else pure spendableUtxos
277297 -- otherwise, get all the utxos, filter out unspendable, and select
278298 -- collateral using internal algo, that is also used in KeyWallet
279- Just utxoMap -> do
280- ProtocolParameters params <- liftContract getProtocolParameters
281- let
282- maxCollateralInputs = UInt .toInt $ params.maxCollateralInputs
283- mbCollateral =
284- Array .fromFoldable <$>
285- selectCollateral params.coinsPerUtxoByte maxCollateralInputs utxoMap
286- liftEither $ note (InsufficientCollateralUtxos utxoMap) mbCollateral
299+ Just utxoMap -> selectCollateral utxoMap
287300 addTxCollateralReturn collateral (addTxCollateral collateral transaction)
288301 changeAddr
289302
303+ -- | Select collateral from the provided utxos using internal CTL
304+ -- | collateral selection algorithm.
305+ selectCollateral :: UtxoMap -> BalanceTxM (Array TransactionUnspentOutput )
306+ selectCollateral utxos = do
307+ pparams <- unwrap <$> liftContract getProtocolParameters
308+ let
309+ maxCollateralInputs = UInt .toInt $ pparams.maxCollateralInputs
310+ mbCollateral =
311+ Array .fromFoldable <$> Collateral .selectCollateral
312+ pparams.coinsPerUtxoByte
313+ maxCollateralInputs
314+ utxos
315+ liftEither $ note (InsufficientCollateralUtxos utxos)
316+ mbCollateral
317+
290318-- ------------------------------------------------------------------------------
291319-- Balancing Algorithm
292320-- ------------------------------------------------------------------------------
@@ -346,11 +374,11 @@ runBalancer p = do
346374 isCip30 <- liftContract $ isCip30Wallet
347375 -- Get collateral inputs to mark them as unspendable.
348376 -- Some CIP-30 wallets don't allow to sign Txs that spend it.
349- nonSpendableCollateralInputs <-
350- if isCip30 then
351- liftContract $ Wallet .getWalletCollateral <#>
352- fold >>> map (unwrap >>> _.input) >>> Set .fromFoldable
353- else mempty
377+ let
378+ nonSpendableCollateralInputs =
379+ if isCip30 then
380+ Set .fromFoldable $ p.transaction ^. _body <<< _collateral
381+ else mempty
354382 asksConstraints Constraints ._nonSpendableInputs <#>
355383 append nonSpendableCollateralInputs >>>
356384 \nonSpendableInputs ->
0 commit comments