@@ -645,13 +645,16 @@ estimateBalancedTxBody
645645 balance =
646646 evaluateTransactionBalance sbe pparams poolids stakeDelegDeposits drepDelegDeposits fakeUTxO txbody2
647647 balanceTxOut = TxOut changeaddr balance TxOutDatumNone ReferenceScriptNone
648- -- check if the balance is positive or negative
649- -- in one case we can produce change, in the other the inputs are insufficient
650- first TxFeeEstimationBalanceError $ balanceCheck sbe pparams balanceTxOut
651648
652649 -- Step 6. Check all txouts have the min required UTxO value
653650 forM_ (txOuts txbodycontent1) $
654- \ txout -> first TxFeeEstimationBalanceError $ checkMinUTxOValue sbe txout pparams
651+ \ txout -> first TxFeeEstimationBalanceError $ checkMinUTxOValue sbe pparams txout
652+
653+ -- check if the balance is positive or negative
654+ -- in one case we can produce change, in the other the inputs are insufficient
655+ finalTxOuts <-
656+ first TxFeeEstimationBalanceError $
657+ checkAndIncludeChange sbe pparams balanceTxOut (txOuts txbodycontent1)
655658
656659 -- Step 7.
657660
@@ -663,10 +666,7 @@ estimateBalancedTxBody
663666 let finalTxBodyContent =
664667 txbodycontent1
665668 { txFee = TxFeeExplicit sbe fee
666- , txOuts =
667- accountForNoChange
668- balanceTxOut
669- (txOuts txbodycontent)
669+ , txOuts = finalTxOuts
670670 , txReturnCollateral = retColl
671671 , txTotalCollateral = reqCol
672672 }
@@ -1371,7 +1371,7 @@ makeTransactionBodyAutoBalance
13711371 TxOutDatumNone
13721372 ReferenceScriptNone
13731373
1374- balanceCheck sbe pp initialChangeTxOut
1374+ _ <- balanceCheck sbe pp initialChangeTxOut
13751375
13761376 -- Tx body used only for evaluating execution units. Because txout exact
13771377 -- values do not matter much here, we are using an initial change value,
@@ -1473,11 +1473,10 @@ makeTransactionBodyAutoBalance
14731473 }
14741474 let balance = evaluateTransactionBalance sbe pp poolids stakeDelegDeposits drepDelegDeposits utxo txbody2
14751475 balanceTxOut = TxOut changeaddr balance TxOutDatumNone ReferenceScriptNone
1476- forM_ (txOuts txbodycontent1) $ \ txout -> checkMinUTxOValue sbe txout pp
1476+ forM_ (txOuts txbodycontent1) $ \ txout -> checkMinUTxOValue sbe pp txout
14771477
1478- -- check if the balance is positive or negative
1479- -- in one case we can produce change, in the other the inputs are insufficient
1480- balanceCheck sbe pp balanceTxOut
1478+ -- check if change meets txout criteria, and include if non-zero
1479+ finalTxOuts <- checkAndIncludeChange sbe pp balanceTxOut (txOuts txbodycontent1)
14811480
14821481 -- TODO: we could add the extra fee for the CBOR encoding of the change,
14831482 -- now that we know the magnitude of the change: i.e. 1-8 bytes extra.
@@ -1489,10 +1488,7 @@ makeTransactionBodyAutoBalance
14891488 let finalTxBodyContent =
14901489 txbodycontent1
14911490 { txFee = TxFeeExplicit sbe fee
1492- , txOuts =
1493- accountForNoChange
1494- balanceTxOut
1495- (txOuts txbodycontent)
1491+ , txOuts = finalTxOuts
14961492 , txReturnCollateral = retColl
14971493 , txTotalCollateral = reqCol
14981494 }
@@ -1512,47 +1508,63 @@ makeTransactionBodyAutoBalance
15121508 era :: CardanoEra era
15131509 era = toCardanoEra sbe
15141510
1515- -- | In the event of spending the exact amount of lovelace in
1511+ -- | In the event of spending the exact amount of lovelace and non-ada assets in
15161512-- the specified input(s), this function excludes the change
15171513-- output. Note that this does not save any fees because by default
15181514-- the fee calculation includes a change address for simplicity and
15191515-- we make no attempt to recalculate the tx fee without a change address.
1520- accountForNoChange :: TxOut CtxTx era -> [TxOut CtxTx era ] -> [TxOut CtxTx era ]
1521- accountForNoChange change@ (TxOut _ balance _ _) rest =
1522- case txOutValueToLovelace balance of
1523- L. Coin 0 -> rest
1524- -- We append change at the end so a client can predict the indexes
1525- -- of the outputs
1526- _ -> rest ++ [change]
1516+ checkAndIncludeChange
1517+ :: ShelleyBasedEra era
1518+ -> Ledger. PParams (ShelleyLedgerEra era )
1519+ -> TxOut CtxTx era
1520+ -> [TxOut CtxTx era ]
1521+ -> Either (TxBodyErrorAutoBalance era ) [TxOut CtxTx era ]
1522+ checkAndIncludeChange sbe pp change rest = do
1523+ isChangeEmpty <- balanceCheck sbe pp change
1524+ if isChangeEmpty == Empty
1525+ then pure rest
1526+ else do
1527+ -- We append change at the end so a client can predict the indexes of the outputs.
1528+ -- Note that if this function will append change with 0 ADA, and non-ada assets in it.
1529+ pure $ rest <> [change]
15271530
15281531checkMinUTxOValue
15291532 :: ShelleyBasedEra era
1530- -> TxOut CtxTx era
15311533 -> Ledger. PParams (ShelleyLedgerEra era )
1534+ -> TxOut CtxTx era
15321535 -> Either (TxBodyErrorAutoBalance era ) ()
1533- checkMinUTxOValue sbe txout@ (TxOut _ v _ _) bpp = do
1534- let minUTxO = calculateMinimumUTxO sbe txout bpp
1536+ checkMinUTxOValue sbe bpp txout@ (TxOut _ v _ _) = do
1537+ let minUTxO = calculateMinimumUTxO sbe bpp txout
15351538 if txOutValueToLovelace v >= minUTxO
15361539 then Right ()
15371540 else Left $ TxBodyErrorMinUTxONotMet (txOutInAnyEra (toCardanoEra sbe) txout) minUTxO
15381541
1542+ data IsEmpty = Empty | NonEmpty
1543+ deriving (Eq , Show )
1544+
15391545balanceCheck
15401546 :: ShelleyBasedEra era
15411547 -> Ledger. PParams (ShelleyLedgerEra era )
15421548 -> TxOut CtxTx era
1543- -> Either (TxBodyErrorAutoBalance era ) ()
1549+ -> Either (TxBodyErrorAutoBalance era ) IsEmpty
15441550balanceCheck sbe bpparams txout@ (TxOut _ balance _ _) = do
15451551 let outValue@ (L. MaryValue coin multiAsset) = toMaryValue $ txOutValueToValue balance
15461552 isPositiveValue = L. pointwise (>) outValue mempty
15471553 if
1548- | L. isZero outValue -> pure () -- empty TxOut
1554+ | L. isZero outValue -> pure Empty -- empty TxOut - ok, it's removed at the end
1555+ | L. isZero coin -> -- no ADA, just non-ADA assets
1556+ Left $
1557+ TxBodyErrorAdaBalanceTooSmall
1558+ (TxOutInAnyEra (toCardanoEra sbe) txout)
1559+ (calculateMinimumUTxO sbe bpparams txout)
1560+ coin
15491561 | not isPositiveValue -> Left $ TxBodyErrorBalanceNegative coin multiAsset
15501562 | otherwise ->
1551- case checkMinUTxOValue sbe txout bpparams of
1563+ case checkMinUTxOValue sbe bpparams txout of
15521564 Left (TxBodyErrorMinUTxONotMet txOutAny minUTxO) ->
15531565 Left $ TxBodyErrorAdaBalanceTooSmall txOutAny minUTxO coin
15541566 Left err -> Left err
1555- Right _ -> Right ()
1567+ Right _ -> Right NonEmpty
15561568
15571569-- Calculation taken from validateInsufficientCollateral:
15581570-- https://github.com/input-output-hk/cardano-ledger/blob/389b266d6226dedf3d2aec7af640b3ca4984c5ea/eras/alonzo/impl/src/Cardano/Ledger/Alonzo/Rules/Utxo.hs#L335
@@ -1893,10 +1905,10 @@ traverseScriptWitnesses =
18931905calculateMinimumUTxO
18941906 :: HasCallStack
18951907 => ShelleyBasedEra era
1896- -> TxOut CtxTx era
18971908 -> Ledger. PParams (ShelleyLedgerEra era )
1909+ -> TxOut CtxTx era
18981910 -> L. Coin
1899- calculateMinimumUTxO sbe txout pp =
1911+ calculateMinimumUTxO sbe pp txout =
19001912 shelleyBasedEraConstraints sbe $
19011913 let txOutWithMinCoin = L. setMinCoinTxOut pp (toShelleyTxOutAny sbe txout)
19021914 in txOutWithMinCoin ^. L. coinTxOutL
0 commit comments