@@ -698,27 +698,24 @@ instance Error FeeCalculationError where
698698-- (i.e. @inputs + mint + withdrawals + refunds = outputs + fee + deposits@
699699-- for all value components: ADA and every native token).
700700--
701+ -- Before entering the iterative loop the multi-asset balance is checked.
702+ -- Because fee adjustments only affect ADA, a negative multi-asset balance
703+ -- is unrecoverable and the function returns 'NonAdaAssetsUnbalanced'
704+ -- immediately.
705+ --
701706-- On each iteration the balance is computed via 'evaluateTransactionBalance'
702707-- and the minimum fee via @calcMinFeeTx@. The function then proceeds based
703708-- on the following cases, evaluated in order:
704709--
705- -- * __Case 1 – Negative multi-asset balance__: The outputs demand more of a
706- -- native token than is available from inputs and minting. This is
707- -- unrecoverable because fee adjustments only affect ADA — they cannot
708- -- change the multi-asset balance. Remedy: provide additional inputs
709- -- containing the deficit tokens, mint the missing amount, or reduce the
710- -- token quantities in the outputs.
711- -- Returns 'NonAdaAssetsUnbalanced'.
712- --
713- -- * __Case 2 – Fee converged, balance is zero__: The transaction is fully
710+ -- * __Case 1 – Fee converged, balance is zero__: The transaction is fully
714711-- balanced. Before returning, all outputs are checked against the minimum
715712-- UTxO requirement ('MinUTxONotMet'). Note: a 'MinUTxONotMet' error at
716- -- this point typically means that Case 3 distributed surplus multi-assets
713+ -- this point typically means that Case 2 distributed surplus multi-assets
717714-- to an output on a prior iteration but there was not enough ADA surplus
718715-- to satisfy the increased @coinPerUTxOByte@ requirement for that output.
719716-- The remedy is to provide additional ADA inputs.
720717--
721- -- * __Case 3 – Fee converged, non-zero balance__: There is surplus or
718+ -- * __Case 2 – Fee converged, non-zero balance__: There is surplus or
722719-- deficit ADA, excess multi-assets (e.g. from minting), or both. A new
723720-- change output is created at the provided change address with the
724721-- balance and appended to the end of the existing outputs; if a change
@@ -729,7 +726,7 @@ instance Error FeeCalculationError where
729726-- required fee, and must also satisfy the minimum UTxO
730727-- (@coinPerUTxOByte@) constraint.
731728--
732- -- * __Case 4 – Fee has not converged__: The fee field is set to the newly
729+ -- * __Case 3 – Fee has not converged__: The fee field is set to the newly
733730-- computed minimum fee and the function recurses.
734731--
735732-- A maximum iteration limit (currently 50) guards against non-termination.
@@ -757,8 +754,29 @@ calcMinFeeRecursive
757754 -> Int
758755 -- ^ Number of extra key hashes for native scripts
759756 -> Either FeeCalculationError (UnsignedTx (LedgerEra era ))
760- calcMinFeeRecursive changeAddr = go maxIterations
757+ calcMinFeeRecursive changeAddr unsignedTx utxo pparams poolids stakeDelegDeposits drepDelegDeposits nExtraWitnesses
758+ -- If multi-assets are non-negative initially, they stay non-negative across
759+ -- iterations (only ADA and fee change), so check once upfront.
760+ | multiAssetIsNegative =
761+ Left $ NonAdaAssetsUnbalanced multiAssets
762+ | otherwise =
763+ go
764+ maxIterations
765+ unsignedTx
766+ utxo
767+ pparams
768+ poolids
769+ stakeDelegDeposits
770+ drepDelegDeposits
771+ nExtraWitnesses
761772 where
773+ initialBalance = evaluateTransactionBalance pparams poolids stakeDelegDeposits drepDelegDeposits utxo unsignedTx
774+ multiAssets = getMultiAssets (useEra @ era ) initialBalance
775+ -- Check whether any native token quantity is negative.
776+ -- ADA is zeroed out so it doesn't influence the check.
777+ multiAssetIsNegative =
778+ obtainCommonConstraints (useEra @ era ) $
779+ not (L. pointwise (>=) (L. MaryValue (L. Coin 0 ) multiAssets) mempty )
762780 maxIterations :: Int
763781 maxIterations = 50
764782
@@ -773,32 +791,26 @@ calcMinFeeRecursive changeAddr = go maxIterations
773791 -> Int
774792 -> Either FeeCalculationError (UnsignedTx (LedgerEra era ))
775793 go 0 _ _ _ _ _ _ _ = Left FeeCalculationDidNotConverge
776- go n unSignTx@ (UnsignedTx ledgerTx) utxo pparams poolids stakeDelegDeposits drepDelegDeposits nExtraWitnesses
777- | multiAssetIsNegative =
778- -- Case 1
779- Left $ NonAdaAssetsUnbalanced (getMultiAssets (useEra @ era ) txBalanceValue)
794+ go n unSignTx@ (UnsignedTx ledgerTx) utxo' pparams' poolids' stakeDelegDeposits' drepDelegDeposits' nExtraWitnesses'
780795 | minFee == txBodyFee && L. isZero txBalanceValue = do
781- -- Case 2
796+ -- Case 1
782797 let outs = toList $ ledgerTx ^. L. bodyTxL . L. outputsTxBodyL
783- mapM_ (checkOutputMinUTxO pparams) outs
798+ mapM_ (checkOutputMinUTxO pparams' ) outs
784799 return unSignTx
785800 | minFee == txBodyFee = do
786- -- Case 3
801+ -- Case 2
787802 balancedOuts <- balanceTxOuts @ era changeAddr txBalanceValue unSignTx
788803 let updatedTx = UnsignedTx (ledgerTx & L. bodyTxL . L. outputsTxBodyL .~ balancedOuts)
789- go (n - 1 ) updatedTx utxo pparams poolids stakeDelegDeposits drepDelegDeposits nExtraWitnesses
804+ go (n - 1 ) updatedTx utxo' pparams' poolids' stakeDelegDeposits' drepDelegDeposits' nExtraWitnesses'
790805 | otherwise =
791- -- Case 4
806+ -- Case 3
792807 let newTx = UnsignedTx (ledgerTx & L. bodyTxL . L. feeTxBodyL .~ minFee)
793- in go (n - 1 ) newTx utxo pparams poolids stakeDelegDeposits drepDelegDeposits nExtraWitnesses
808+ in go (n - 1 ) newTx utxo' pparams' poolids' stakeDelegDeposits' drepDelegDeposits' nExtraWitnesses'
794809 where
795- minFee = obtainCommonConstraints (useEra @ era ) $ L. calcMinFeeTx utxo pparams ledgerTx nExtraWitnesses
810+ minFee = obtainCommonConstraints (useEra @ era ) $ L. calcMinFeeTx utxo' pparams' ledgerTx nExtraWitnesses'
796811 txBodyFee = ledgerTx ^. L. bodyTxL . L. feeTxBodyL
797- txBalanceValue = evaluateTransactionBalance pparams poolids stakeDelegDeposits drepDelegDeposits utxo unSignTx
798- txBalanceCoin = L. coin txBalanceValue
799- multiAssetIsNegative =
800- obtainCommonConstraints (useEra @ era ) $
801- not (L. pointwise (>=) txBalanceValue (L. inject txBalanceCoin))
812+ txBalanceValue =
813+ evaluateTransactionBalance pparams' poolids' stakeDelegDeposits' drepDelegDeposits' utxo' unSignTx
802814
803815checkOutputMinUTxO
804816 :: forall era
0 commit comments