{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}

-- | This module is used for building and inspecting transaction outputs.
--
-- You'll find some examples below.
--
-- Let's start by defining the GHC extensions and imports.
--
-- >>> :set -XTypeApplications
-- >>> import Cardano.Ledger.Api.Era (Babbage)
-- >>> import Lens.Micro
-- >>> import Test.Cardano.Ledger.Babbage.Serialisation.Generators () -- Neded for doctests only
-- >>> import Test.QuickCheck -- Needed for doctests only
--
-- Here's an example on how to build a very basic Babbage era transaction output with a random
-- address and value, and without any datum or reference script.
--
-- >>> :{
-- quickCheck $ \addr val ->
--     let
--         -- Defining a Babbage era transaction output with some random address and value.
--         txOut = mkBasicTxOut @Babbage addr val
--      in
--         -- We verify that the transaction output contains our random address and value.
--         txOut ^. addrTxOutL == addr && txOut ^. valueTxOutL == val
-- :}
-- +++ OK, passed 100 tests.
module Cardano.Ledger.Api.Tx.Out (
  module Cardano.Ledger.Api.Tx.Address,
  EraTxOut (TxOut),
  mkBasicTxOut,
  upgradeTxOut,

  -- ** Value
  valueTxOutL,
  coinTxOutL,
  isAdaOnlyTxOutF,

  -- ** Address
  addrTxOutL,
  bootAddrTxOutF,

  -- ** Size
  getMinCoinTxOut,
  setMinCoinTxOut,
  getMinCoinSizedTxOut,
  setMinCoinSizedTxOut,
  ensureMinCoinTxOut,
  ensureMinCoinSizedTxOut,

  -- * Shelley, Allegra and Mary Era

  -- * Alonzo Era
  AlonzoEraTxOut,
  dataHashTxOutL,
  DataHash,
  datumTxOutF,

  -- * Babbage Era
  BabbageEraTxOut,
  dataTxOutL,
  Data (..),
  datumTxOutL,
  Datum (..),
  referenceScriptTxOutL,
)
where

import Cardano.Ledger.Alonzo.Core (AlonzoEraTxOut (..))
import Cardano.Ledger.Api.Era ()
import Cardano.Ledger.Api.Scripts.Data (Data (..), DataHash, Datum (..))
import Cardano.Ledger.Api.Tx.Address
import Cardano.Ledger.Babbage.Core (BabbageEraTxOut (..))
import Cardano.Ledger.Binary
import Cardano.Ledger.Coin
import Cardano.Ledger.Core (
  EraTxOut (..),
  PParams,
  bootAddrTxOutF,
  coinTxOutL,
  eraProtVerLow,
  isAdaOnlyTxOutF,
 )
import Cardano.Ledger.Tools (ensureMinCoinTxOut, setMinCoinTxOut)
import Lens.Micro

setMinCoinSizedTxOutInternal ::
  forall era.
  EraTxOut era =>
  (Coin -> Coin -> Bool) ->
  PParams era ->
  Sized (TxOut era) ->
  Sized (TxOut era)
setMinCoinSizedTxOutInternal :: forall era.
EraTxOut era =>
(Coin -> Coin -> Bool)
-> PParams era -> Sized (TxOut era) -> Sized (TxOut era)
setMinCoinSizedTxOutInternal Coin -> Coin -> Bool
f PParams era
pp = Sized (TxOut era) -> Sized (TxOut era)
go
  where
    version :: Version
version = forall era. Era era => Version
eraProtVerLow @era
    go :: Sized (TxOut era) -> Sized (TxOut era)
go !Sized (TxOut era)
txOut =
      let curMinCoin :: Coin
curMinCoin = forall era.
EraTxOut era =>
PParams era -> Sized (TxOut era) -> Coin
getMinCoinSizedTxOut PParams era
pp Sized (TxOut era)
txOut
          curCoin :: Coin
curCoin = Sized (TxOut era)
txOut forall s a. s -> Getting a s a -> a
^. forall s a. EncCBOR s => Version -> Lens' s a -> Lens' (Sized s) a
toSizedL Version
version forall era. (HasCallStack, EraTxOut era) => Lens' (TxOut era) Coin
coinTxOutL
       in if Coin
curCoin Coin -> Coin -> Bool
`f` Coin
curMinCoin
            then Sized (TxOut era)
txOut
            else Sized (TxOut era) -> Sized (TxOut era)
go (Sized (TxOut era)
txOut forall a b. a -> (a -> b) -> b
& forall s a. EncCBOR s => Version -> Lens' s a -> Lens' (Sized s) a
toSizedL Version
version forall era. (HasCallStack, EraTxOut era) => Lens' (TxOut era) Coin
coinTxOutL forall s t a b. ASetter s t a b -> b -> s -> t
.~ Coin
curMinCoin)

-- | This function will adjust the output's `Coin` value to the smallest amount
-- allowed by the UTXO rule. Initial amount is not important.
setMinCoinSizedTxOut ::
  forall era.
  EraTxOut era =>
  PParams era ->
  Sized (TxOut era) ->
  Sized (TxOut era)
setMinCoinSizedTxOut :: forall era.
EraTxOut era =>
PParams era -> Sized (TxOut era) -> Sized (TxOut era)
setMinCoinSizedTxOut = forall era.
EraTxOut era =>
(Coin -> Coin -> Bool)
-> PParams era -> Sized (TxOut era) -> Sized (TxOut era)
setMinCoinSizedTxOutInternal forall a. Eq a => a -> a -> Bool
(==)

-- | Similar to `setMinCoinSizedTxOut` it will guarantee that the minimum requirement for the
-- output amount is satisified, however it makes it possible to set a higher amount than
-- the minimaly required.
--
-- `ensureMinCoinSizedTxOut` relates to `setMinCoinSizedTxOut` in the same way that
-- `ensureMinCoinTxOut` relates to `setMinCoinTxOut`.
ensureMinCoinSizedTxOut ::
  forall era.
  EraTxOut era =>
  PParams era ->
  Sized (TxOut era) ->
  Sized (TxOut era)
ensureMinCoinSizedTxOut :: forall era.
EraTxOut era =>
PParams era -> Sized (TxOut era) -> Sized (TxOut era)
ensureMinCoinSizedTxOut = forall era.
EraTxOut era =>
(Coin -> Coin -> Bool)
-> PParams era -> Sized (TxOut era) -> Sized (TxOut era)
setMinCoinSizedTxOutInternal forall a. Ord a => a -> a -> Bool
(>=)