@@ -645,13 +645,16 @@ estimateBalancedTxBody
645
645
balance =
646
646
evaluateTransactionBalance sbe pparams poolids stakeDelegDeposits drepDelegDeposits fakeUTxO txbody2
647
647
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
651
648
652
649
-- Step 6. Check all txouts have the min required UTxO value
653
650
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)
655
658
656
659
-- Step 7.
657
660
@@ -663,10 +666,7 @@ estimateBalancedTxBody
663
666
let finalTxBodyContent =
664
667
txbodycontent1
665
668
{ txFee = TxFeeExplicit sbe fee
666
- , txOuts =
667
- accountForNoChange
668
- balanceTxOut
669
- (txOuts txbodycontent)
669
+ , txOuts = finalTxOuts
670
670
, txReturnCollateral = retColl
671
671
, txTotalCollateral = reqCol
672
672
}
@@ -1371,7 +1371,7 @@ makeTransactionBodyAutoBalance
1371
1371
TxOutDatumNone
1372
1372
ReferenceScriptNone
1373
1373
1374
- balanceCheck sbe pp initialChangeTxOut
1374
+ _ <- balanceCheck sbe pp initialChangeTxOut
1375
1375
1376
1376
-- Tx body used only for evaluating execution units. Because txout exact
1377
1377
-- values do not matter much here, we are using an initial change value,
@@ -1473,11 +1473,10 @@ makeTransactionBodyAutoBalance
1473
1473
}
1474
1474
let balance = evaluateTransactionBalance sbe pp poolids stakeDelegDeposits drepDelegDeposits utxo txbody2
1475
1475
balanceTxOut = TxOut changeaddr balance TxOutDatumNone ReferenceScriptNone
1476
- forM_ (txOuts txbodycontent1) $ \ txout -> checkMinUTxOValue sbe txout pp
1476
+ forM_ (txOuts txbodycontent1) $ \ txout -> checkMinUTxOValue sbe pp txout
1477
1477
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)
1481
1480
1482
1481
-- TODO: we could add the extra fee for the CBOR encoding of the change,
1483
1482
-- now that we know the magnitude of the change: i.e. 1-8 bytes extra.
@@ -1489,10 +1488,7 @@ makeTransactionBodyAutoBalance
1489
1488
let finalTxBodyContent =
1490
1489
txbodycontent1
1491
1490
{ txFee = TxFeeExplicit sbe fee
1492
- , txOuts =
1493
- accountForNoChange
1494
- balanceTxOut
1495
- (txOuts txbodycontent)
1491
+ , txOuts = finalTxOuts
1496
1492
, txReturnCollateral = retColl
1497
1493
, txTotalCollateral = reqCol
1498
1494
}
@@ -1512,47 +1508,63 @@ makeTransactionBodyAutoBalance
1512
1508
era :: CardanoEra era
1513
1509
era = toCardanoEra sbe
1514
1510
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
1516
1512
-- the specified input(s), this function excludes the change
1517
1513
-- output. Note that this does not save any fees because by default
1518
1514
-- the fee calculation includes a change address for simplicity and
1519
1515
-- 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]
1527
1530
1528
1531
checkMinUTxOValue
1529
1532
:: ShelleyBasedEra era
1530
- -> TxOut CtxTx era
1531
1533
-> Ledger. PParams (ShelleyLedgerEra era )
1534
+ -> TxOut CtxTx era
1532
1535
-> 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
1535
1538
if txOutValueToLovelace v >= minUTxO
1536
1539
then Right ()
1537
1540
else Left $ TxBodyErrorMinUTxONotMet (txOutInAnyEra (toCardanoEra sbe) txout) minUTxO
1538
1541
1542
+ data IsEmpty = Empty | NonEmpty
1543
+ deriving (Eq , Show )
1544
+
1539
1545
balanceCheck
1540
1546
:: ShelleyBasedEra era
1541
1547
-> Ledger. PParams (ShelleyLedgerEra era )
1542
1548
-> TxOut CtxTx era
1543
- -> Either (TxBodyErrorAutoBalance era ) ()
1549
+ -> Either (TxBodyErrorAutoBalance era ) IsEmpty
1544
1550
balanceCheck sbe bpparams txout@ (TxOut _ balance _ _) = do
1545
1551
let outValue@ (L. MaryValue coin multiAsset) = toMaryValue $ txOutValueToValue balance
1546
1552
isPositiveValue = L. pointwise (>) outValue mempty
1547
1553
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
1549
1561
| not isPositiveValue -> Left $ TxBodyErrorBalanceNegative coin multiAsset
1550
1562
| otherwise ->
1551
- case checkMinUTxOValue sbe txout bpparams of
1563
+ case checkMinUTxOValue sbe bpparams txout of
1552
1564
Left (TxBodyErrorMinUTxONotMet txOutAny minUTxO) ->
1553
1565
Left $ TxBodyErrorAdaBalanceTooSmall txOutAny minUTxO coin
1554
1566
Left err -> Left err
1555
- Right _ -> Right ()
1567
+ Right _ -> Right NonEmpty
1556
1568
1557
1569
-- Calculation taken from validateInsufficientCollateral:
1558
1570
-- 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 =
1893
1905
calculateMinimumUTxO
1894
1906
:: HasCallStack
1895
1907
=> ShelleyBasedEra era
1896
- -> TxOut CtxTx era
1897
1908
-> Ledger. PParams (ShelleyLedgerEra era )
1909
+ -> TxOut CtxTx era
1898
1910
-> L. Coin
1899
- calculateMinimumUTxO sbe txout pp =
1911
+ calculateMinimumUTxO sbe pp txout =
1900
1912
shelleyBasedEraConstraints sbe $
1901
1913
let txOutWithMinCoin = L. setMinCoinTxOut pp (toShelleyTxOutAny sbe txout)
1902
1914
in txOutWithMinCoin ^. L. coinTxOutL
0 commit comments