@@ -34,8 +34,19 @@ import BotPlutusInterface.Types (
3434 Tip (block , slot ),
3535 TxFile (Signed ),
3636 )
37- import Cardano.Api (AsType (.. ), EraInMode (.. ), Tx (Tx ))
38- import Control.Lens (preview , (^.) )
37+ import Cardano.Api (
38+ AsType (.. ),
39+ EraInMode (.. ),
40+ ShelleyBasedEra (ShelleyBasedEraBabbage ),
41+ Tx (Tx ),
42+ toLedgerPParams ,
43+ )
44+ import Cardano.Api.Shelley (toShelleyTxOut )
45+ import Cardano.Ledger.Shelley.API.Wallet (
46+ CLI (evaluateMinLovelaceOutput ),
47+ )
48+ import Cardano.Prelude (maybeToEither )
49+ import Control.Lens (preview , (.~) , (^.) )
3950import Control.Monad (join , void , when )
4051import Control.Monad.Freer (Eff , Member , interpret , reinterpret , runM , subsume , type (~> ))
4152import Control.Monad.Freer.Error (runError )
@@ -46,7 +57,7 @@ import Control.Monad.Trans.Either (EitherT, eitherT, firstEitherT, newEitherT)
4657import Data.Aeson (ToJSON , Value (Array , Bool , Null , Number , Object , String ))
4758import Data.Aeson.Extras (encodeByteString )
4859import Data.Aeson.KeyMap qualified as KeyMap
49- import Data.Function (fix )
60+ import Data.Function (fix , (&) )
5061import Data.Kind (Type )
5162import Data.List.NonEmpty (NonEmpty ((:|) ))
5263import Data.Map qualified as Map
@@ -56,15 +67,16 @@ import Data.Text qualified as Text
5667import Data.Vector qualified as V
5768import Ledger (POSIXTime )
5869import Ledger qualified
70+ import Ledger.Ada qualified as Ada
5971import Ledger.Address (PaymentPubKeyHash (PaymentPubKeyHash ))
60- import Ledger.Constraints.OffChain (UnbalancedTx (.. ), adjustUnbalancedTx )
61- import Ledger.Params (Params (Params ))
72+ import Ledger.Constraints.OffChain (UnbalancedTx (.. ), tx )
6273import Ledger.Slot (Slot (Slot ))
63- import Ledger.TimeSlot (SlotConfig (.. ))
64- import Ledger.Tx (CardanoTx (CardanoApiTx , EmulatorTx ))
74+ import Ledger.Tx (CardanoTx (CardanoApiTx , EmulatorTx ), outputs )
6575import Ledger.Tx qualified as Tx
76+ import Ledger.Validation (Coin (Coin ))
6677import Plutus.ChainIndex.TxIdState (fromTx , transactionStatus )
6778import Plutus.ChainIndex.Types (RollbackState (.. ), TxIdState , TxStatus )
79+ import Plutus.Contract.CardanoAPI (toCardanoTxOutBabbage , toCardanoTxOutDatumHashBabbage )
6880import Plutus.Contract.Checkpoint (Checkpoint (.. ))
6981import Plutus.Contract.Effects (
7082 BalanceTxResponse (.. ),
@@ -76,6 +88,7 @@ import Plutus.Contract.Effects (
7688 )
7789import Plutus.Contract.Resumable (Resumable (.. ))
7890import Plutus.Contract.Types (Contract (.. ), ContractEffs )
91+ import Plutus.V1.Ledger.Tx (TxOut (txOutValue ))
7992import PlutusTx.Builtins (fromBuiltin )
8093import Prettyprinter (Pretty (pretty ), (<+>) )
8194import Prettyprinter qualified as PP
@@ -187,8 +200,8 @@ handlePABReq contractEnv req = do
187200 ChainIndexQueryResp <$> queryChainIndex @ w query
188201 BalanceTxReq unbalancedTx ->
189202 BalanceTxResp <$> balanceTx @ w contractEnv unbalancedTx
190- WriteBalancedTxReq tx ->
191- WriteBalancedTxResp <$> writeBalancedTx @ w contractEnv tx
203+ WriteBalancedTxReq tx' ->
204+ WriteBalancedTxResp <$> writeBalancedTx @ w contractEnv tx'
192205 AwaitSlotReq s -> AwaitSlotResp <$> awaitSlot @ w contractEnv s
193206 AwaitTimeReq t -> AwaitTimeResp <$> awaitTime @ w contractEnv t
194207 CurrentSlotReq -> CurrentSlotResp <$> currentSlot @ w contractEnv
@@ -210,18 +223,47 @@ handlePABReq contractEnv req = do
210223 printBpiLog @ w Debug $ pretty resp
211224 pure resp
212225
226+ -- do-not-remove yet, need fo comparison with "own" implementation below
227+ -- minAda calculated fo 1 Lovelace output for this version is 999978
228+ -- adjustUnbalancedTx' ::
229+ -- forall (w :: Type) (effs :: [Type -> Type]).
230+ -- ContractEnvironment w ->
231+ -- UnbalancedTx ->
232+ -- Eff effs (Either Tx.ToCardanoError UnbalancedTx)
233+ -- adjustUnbalancedTx' contractEnv unbalancedTx = do
234+ -- let slotConfig = SlotConfig 20000 1654524000
235+ -- networkId = contractEnv.cePABConfig.pcNetwork
236+ -- maybeParams = contractEnv.cePABConfig.pcProtocolParams >>= \pparams -> pure $ Params slotConfig pparams networkId
237+ -- case maybeParams of
238+ -- Just params -> pure $ snd <$> adjustUnbalancedTx params unbalancedTx
239+ -- _ -> pure . Left $ Tx.TxBodyError "no protocol params"
240+
241+ -- minAda calculated fo 1 Lovelace output for this version is 840450
242+ -- if switch all babbage related things to alonzo, it will calculate 999978 as ^ above
213243adjustUnbalancedTx' ::
214244 forall (w :: Type ) (effs :: [Type -> Type ]).
215245 ContractEnvironment w ->
216246 UnbalancedTx ->
217247 Eff effs (Either Tx. ToCardanoError UnbalancedTx )
218- adjustUnbalancedTx' contractEnv unbalancedTx = do
219- let slotConfig = SlotConfig 20000 1654524000
220- networkId = contractEnv. cePABConfig. pcNetwork
221- maybeParams = contractEnv. cePABConfig. pcProtocolParams >>= \ pparams -> pure $ Params slotConfig pparams networkId
222- case maybeParams of
223- Just params -> pure $ snd <$> adjustUnbalancedTx params unbalancedTx
224- _ -> pure . Left $ Tx. TxBodyError " no protocol params"
248+ adjustUnbalancedTx' contractEnv unbalancedTx = pure $ do
249+ pparams <- getPParams
250+ let networkId = contractEnv. cePABConfig. pcNetwork
251+
252+ updatedOuts <- traverse (adjustTxOut networkId pparams) (unbalancedTx ^. tx . outputs)
253+ return $ unbalancedTx & (tx . outputs .~ updatedOuts)
254+ where
255+ getPParams =
256+ maybeToEither (Tx. TxBodyError " No protocol params found in PAB config" ) $
257+ asBabbageBased toLedgerPParams
258+ <$> contractEnv. cePABConfig. pcProtocolParams
259+
260+ adjustTxOut networkId pparams txOut = do
261+ txOut' <- toCardanoTxOutBabbage networkId toCardanoTxOutDatumHashBabbage txOut
262+ let (Coin minTxOut) = evaluateMinLovelaceOutput pparams (asBabbageBased toShelleyTxOut txOut')
263+ missingLovelace = max 0 (Ada. lovelaceOf minTxOut - Ada. fromValue (txOutValue txOut))
264+ pure $ txOut {txOutValue = txOutValue txOut <> Ada. toValue missingLovelace}
265+
266+ asBabbageBased f = f ShelleyBasedEraBabbage
225267
226268{- | Await till transaction status change to something from `Unknown`.
227269 Uses `chain-index` to query transaction by id.
@@ -283,9 +325,9 @@ awaitTxStatusChange contractEnv txId = do
283325 queryChainIndexForTxState = do
284326 mTx <- join . preview _TxIdResponse <$> (queryChainIndex @ w $ TxFromTxId txId)
285327 case mTx of
286- Just tx -> do
328+ Just tx' -> do
287329 blk <- fromInteger <$> currentBlock contractEnv
288- pure . Just $ fromTx blk tx
330+ pure . Just $ fromTx blk tx'
289331 Nothing -> pure Nothing
290332
291333 logDebug = printBpiLog @ w Debug . pretty
@@ -310,8 +352,8 @@ balanceTx contractEnv unbalancedTx = do
310352
311353fromCardanoTx :: CardanoTx -> Tx. Tx
312354fromCardanoTx (CardanoApiTx _) = error " Cannot handle cardano api tx"
313- fromCardanoTx (EmulatorTx tx) = tx
314- fromCardanoTx (Tx. Both tx _) = tx
355+ fromCardanoTx (EmulatorTx tx' ) = tx'
356+ fromCardanoTx (Tx. Both tx' _) = tx'
315357
316358-- | This step would build tx files, write them to disk and submit them to the chain
317359writeBalancedTx ::
@@ -322,48 +364,48 @@ writeBalancedTx ::
322364 Eff effs WriteBalancedTxResponse
323365writeBalancedTx contractEnv cardanoTx = do
324366 let pabConf = contractEnv. cePABConfig
325- tx = fromCardanoTx cardanoTx
367+ tx' = fromCardanoTx cardanoTx
326368 uploadDir @ w pabConf. pcSigningKeyFileDir
327369 createDirectoryIfMissing @ w False (Text. unpack pabConf. pcScriptFileDir)
328370
329371 eitherT (pure . WriteBalancedTxFailed . OtherError ) (pure . WriteBalancedTxSuccess . CardanoApiTx ) $ do
330- void $ firstEitherT (Text. pack . show ) $ newEitherT $ Files. writeAll @ w pabConf tx
372+ void $ firstEitherT (Text. pack . show ) $ newEitherT $ Files. writeAll @ w pabConf tx'
331373 lift $ uploadDir @ w pabConf. pcScriptFileDir
332374
333375 privKeys <- newEitherT $ Files. readPrivateKeys @ w pabConf
334376
335- let requiredSigners = Map. keys $ tx ^. Tx. signatures
377+ let requiredSigners = Map. keys $ tx' ^. Tx. signatures
336378 skeys = Map. filter (\ case FromSKey _ -> True ; FromVKey _ -> False ) privKeys
337379 signable = all ((`Map.member` skeys) . Ledger. pubKeyHash) requiredSigners
338380
339- void $ newEitherT $ BodyBuilder. buildAndEstimateBudget @ w pabConf privKeys tx
381+ void $ newEitherT $ BodyBuilder. buildAndEstimateBudget @ w pabConf privKeys tx'
340382
341383 -- TODO: This whole part is hacky and we should remove it.
342- let path = Text. unpack $ Files. txFilePath pabConf " raw" (Tx. txId tx)
384+ let path = Text. unpack $ Files. txFilePath pabConf " raw" (Tx. txId tx' )
343385 -- We read back the tx from file as tx currently has the wrong id (but the one we create with cardano-cli is correct)
344386 alonzoBody <- firstEitherT (Text. pack . show ) $ newEitherT $ readFileTextEnvelope @ w (AsTxBody AsBabbageEra ) path
345387 let cardanoApiTx = Tx. SomeTx (Tx alonzoBody [] ) BabbageEraInCardanoMode
346388
347389 if signable
348- then newEitherT $ CardanoCLI. signTx @ w pabConf tx requiredSigners
390+ then newEitherT $ CardanoCLI. signTx @ w pabConf tx' requiredSigners
349391 else
350392 lift . printBpiLog @ w Warn . PP. vsep $
351393 [ " Not all required signatures have signing key files. Please sign and submit the tx manually:"
352- , " Tx file:" <+> pretty (Files. txFilePath pabConf " raw" (Tx. txId tx))
394+ , " Tx file:" <+> pretty (Files. txFilePath pabConf " raw" (Tx. txId tx' ))
353395 , " Signatories (pkh):" <+> pretty (Text. unwords (map pkhToText requiredSigners))
354396 ]
355397
356398 when (pabConf. pcCollectStats && signable) $
357- collectBudgetStats (Tx. txId tx) pabConf
399+ collectBudgetStats (Tx. txId tx' ) pabConf
358400
359401 when (not pabConf. pcDryRun && signable) $ do
360- newEitherT $ CardanoCLI. submitTx @ w pabConf tx
402+ newEitherT $ CardanoCLI. submitTx @ w pabConf tx'
361403
362404 -- We need to replace the outfile we created at the previous step, as it currently still has the old (incorrect) id
363405 let cardanoTxId = Ledger. getCardanoTxId $ Tx. CardanoApiTx cardanoApiTx
364- signedSrcPath = Files. txFilePath pabConf " signed" (Tx. txId tx)
406+ signedSrcPath = Files. txFilePath pabConf " signed" (Tx. txId tx' )
365407 signedDstPath = Files. txFilePath pabConf " signed" cardanoTxId
366- mvFiles (Files. txFilePath pabConf " raw" (Tx. txId tx)) (Files. txFilePath pabConf " raw" cardanoTxId)
408+ mvFiles (Files. txFilePath pabConf " raw" (Tx. txId tx' )) (Files. txFilePath pabConf " raw" cardanoTxId)
367409 when signable $ mvFiles signedSrcPath signedDstPath
368410
369411 pure cardanoApiTx
0 commit comments