1313{-# LANGUAGE TypeApplications #-}
1414
1515module Cardano.Api.Experimental.Tx.Internal.Fee
16- ( TxBodyErrorAutoBalance (.. )
16+ ( RecursiveFeeCalculationError (.. )
17+ , TxBodyErrorAutoBalance (.. )
1718 , TxFeeEstimationError (.. )
1819 , calculateMinimumUTxO
20+ , calcMinFeeRecursive
1921 , collectTxBodyScriptWitnesses
2022 , estimateBalancedTxBody
2123 , evaluateTransactionExecutionUnits
@@ -83,11 +85,12 @@ import Data.Maybe
8385import Data.OSet.Strict qualified as OSet
8486import Data.Ord (Down (Down ), comparing )
8587import Data.Ratio
88+ import Data.Sequence.Strict qualified as Seq
8689import Data.Set (Set )
8790import Data.Set qualified as Set
8891import GHC.Exts (IsList (.. ))
8992import GHC.Stack
90- import Lens.Micro ((.~) , (^.) )
93+ import Lens.Micro ((%~) , ( .~) , (^.) )
9194import Prettyprinter (punctuate )
9295
9396data TxBodyErrorAutoBalance era
@@ -657,6 +660,16 @@ evaluateTransactionFee
657660evaluateTransactionFee pp (UnsignedTx tx) keywitcount byronwitcount refScriptsSize =
658661 L. estimateMinFeeTx pp tx (fromIntegral keywitcount) (fromIntegral byronwitcount) refScriptsSize
659662
663+ data RecursiveFeeCalculationError = NotEnoughAda Coin deriving (Show , Eq )
664+
665+ instance Error RecursiveFeeCalculationError where
666+ prettyError (NotEnoughAda balance) =
667+ mconcat
668+ [ " The transaction balance is negative: "
669+ , pretty balance
670+ , " \n This means that the transaction does not have enough ada to cover the fees. The usual solution is to provide more inputs, or inputs with more ada."
671+ ]
672+
660673-- The assumption here is that we are spending a single UTxO
661674-- When the fee changes we must also adjust the outputs.
662675-- We will make two outputs, one for the change (if any)
@@ -671,46 +684,36 @@ calcMinFeeRecursive
671684 -> L. PParams (LedgerEra era )
672685 -> Int
673686 -- ^ Number of extra key hashes for native scripts
674- -> UnsignedTx (LedgerEra era )
675- calcMinFeeRecursive unSignTx@ (UnsignedTx ledgerTx) utxo pparams nExtraWitnesses =
676- let sbe = convert useEra
677- minFee = obtainCommonConstraints (useEra @ era ) $ L. calcMinFeeTx utxo pparams ledgerTx nExtraWitnesses
678-
679- txBodyFee = ledgerTx ^. L. bodyTxL . L. feeTxBodyL
680- in if minFee == txBodyFee
681- then
682- let txBalance = evaluateTransactionBalance pparams mempty mempty mempty utxo unSignTx
683- in -- We have reached the minimum fee but there isn't a guarantee that
684- -- the inputs/outputs are balanced
685- if txBalance == 0
686- then unSignTx
687- else
688- let balancedOuts = balanceTxOuts sbe txBalance txBody
689- updatedTx = ledgerTx & L. bodyTxL . L. outputsTxBodyL .~ Seq. fromList balancedOuts
690- in updatedTx
691- -- TODO: We currently just add the balance (positive or negative) to the
692- -- first output. This is not ideal because we could make the transaction invalid
693- -- if we have a negative balance and the resulting output has less then the
694- -- required minimum UTxO value.
695- else
696- let newTx = ledgerTx & L. bodyTxL . L. feeTxBodyL .~ minFee
697- in calcMinFeeRecursive newTx utxo pparams nExtraWitnesses
687+ -> Either RecursiveFeeCalculationError (UnsignedTx (LedgerEra era ))
688+ calcMinFeeRecursive unSignTx@ (UnsignedTx ledgerTx) utxo pparams nExtraWitnesses
689+ | minFee == txBodyFee && L. isZero txBalanceCoin =
690+ -- We have reached the minimum fee but there isn't a guarantee that
691+ -- the inputs/outputs are balanced
692+ return unSignTx
693+ | minFee == txBodyFee && txBalanceCoin > 0 =
694+ -- We have a surplus balance so we modify the outputs to include it.
695+ let balancedOuts = balanceTxOuts txBalanceValue unSignTx
696+ updatedTx = UnsignedTx (ledgerTx & L. bodyTxL . L. outputsTxBodyL .~ Seq. fromList balancedOuts)
697+ in return updatedTx
698+ | txBalanceCoin < 0 = Left $ NotEnoughAda txBalanceCoin
699+ | otherwise =
700+ let newTx = UnsignedTx (ledgerTx & L. bodyTxL . L. feeTxBodyL .~ minFee)
701+ in calcMinFeeRecursive newTx utxo pparams nExtraWitnesses
702+ where
703+ minFee = obtainCommonConstraints (useEra @ era ) $ L. calcMinFeeTx utxo pparams ledgerTx nExtraWitnesses
704+ txBodyFee = ledgerTx ^. L. bodyTxL . L. feeTxBodyL
705+ txBalanceValue = evaluateTransactionBalance pparams mempty mempty mempty utxo unSignTx
706+ txBalanceCoin = L. coin txBalanceValue
698707
699708balanceTxOuts
700- :: IsEra era
701- => TxOutValue era
702- -> L. TxBody (ShelleyLedgerEra era )
703- -> [L. TxOut (ShelleyLedgerEra era )]
704- balanceTxOuts sbe txBalance txBody =
705- let outs = toList $ txBody ^. L. outputsTxBodyL
709+ :: L. Value (LedgerEra era )
710+ -> UnsignedTx (LedgerEra era )
711+ -> [L. TxOut (LedgerEra era )]
712+ balanceTxOuts txBalance (UnsignedTx tx) =
713+ let outs = toList $ tx ^. L. bodyTxL . L. outputsTxBodyL
706714 split = List. uncons outs
707- (h, rest) = maybe (Unsafe. error " calcMinFeeRecursive: No outs!" ) id split
708- balance =
709- forShelleyBasedEraInEon
710- sbe
711- (L. inject $ txOutValueToLovelace txBalance)
712- (\ w -> toLedgerValue w $ txOutValueToValue txBalance)
713- updatedout = h & L. valueTxOutL %~ (<> balance)
715+ (h, rest) = maybe (error " calcMinFeeRecursive: No outs!" ) id split
716+ updatedout = h & L. valueTxOutL %~ (<> txBalance)
714717 in updatedout : rest
715718
716719-- Essentially we check for the existence of collateral inputs. If they exist we
0 commit comments