module Hydra.Tx.Init where

import Hydra.Cardano.Api
import Hydra.Prelude hiding (toList)

import GHC.IsList (toList)
import Hydra.Contract.Head qualified as Head
import Hydra.Contract.HeadState qualified as Head
import Hydra.Contract.HeadTokens qualified as HeadTokens
import Hydra.Contract.Initial qualified as Initial
import Hydra.Contract.MintAction (MintAction (..))
import Hydra.Ledger.Cardano.Builder (addTxInsSpending, mintTokens, unsafeBuildTransaction)
import Hydra.Plutus (initialValidatorScript)
import Hydra.Tx.ContestationPeriod (ContestationPeriod, fromChain, toChain)
import Hydra.Tx.HeadId (HeadId, mkHeadId)
import Hydra.Tx.HeadParameters (HeadParameters (..))
import Hydra.Tx.OnChainId (OnChainId (..))
import Hydra.Tx.Party (Party, partyFromChain, partyToChain)
import Hydra.Tx.Utils (assetNameToOnChainId, findFirst, hydraHeadV1AssetName, mkHydraHeadV1TxName, onChainIdToAssetName)

-- * Construction

-- | Create the init transaction from some 'HeadParameters' and a single TxIn
-- which will be used as unique parameter for minting NFTs.
initTx ::
  NetworkId ->
  -- | Seed input.
  TxIn ->
  -- | Verification key hashes of all participants.
  [OnChainId] ->
  HeadParameters ->
  Tx
initTx :: NetworkId -> TxIn -> [OnChainId] -> HeadParameters -> Tx
initTx NetworkId
networkId TxIn
seedTxIn [OnChainId]
participants HeadParameters
parameters =
  HasCallStack => TxBodyContent BuildTx -> Tx
TxBodyContent BuildTx -> Tx
unsafeBuildTransaction (TxBodyContent BuildTx -> Tx) -> TxBodyContent BuildTx -> Tx
forall a b. (a -> b) -> a -> b
$
    TxBodyContent BuildTx
defaultTxBodyContent
      TxBodyContent BuildTx
-> (TxBodyContent BuildTx -> TxBodyContent BuildTx)
-> TxBodyContent BuildTx
forall a b. a -> (a -> b) -> b
& [TxIn] -> TxBodyContent BuildTx -> TxBodyContent BuildTx
addTxInsSpending [TxIn
seedTxIn]
      TxBodyContent BuildTx
-> (TxBodyContent BuildTx -> TxBodyContent BuildTx)
-> TxBodyContent BuildTx
forall a b. a -> (a -> b) -> b
& [TxOut CtxTx Era] -> TxBodyContent BuildTx -> TxBodyContent BuildTx
forall era build.
[TxOut CtxTx era]
-> TxBodyContent build era -> TxBodyContent build era
addTxOuts
        ( NetworkId -> TxIn -> HeadParameters -> TxOut CtxTx Era
mkHeadOutputInitial NetworkId
networkId TxIn
seedTxIn HeadParameters
parameters
            TxOut CtxTx Era -> [TxOut CtxTx Era] -> [TxOut CtxTx Era]
forall a. a -> [a] -> [a]
: (OnChainId -> TxOut CtxTx Era) -> [OnChainId] -> [TxOut CtxTx Era]
forall a b. (a -> b) -> [a] -> [b]
map (NetworkId -> TxIn -> OnChainId -> TxOut CtxTx Era
mkInitialOutput NetworkId
networkId TxIn
seedTxIn) [OnChainId]
participants
        )
      TxBodyContent BuildTx
-> (TxBodyContent BuildTx -> TxBodyContent BuildTx)
-> TxBodyContent BuildTx
forall a b. a -> (a -> b) -> b
& PlutusScript
-> MintAction
-> PolicyAssets
-> TxBodyContent BuildTx
-> TxBodyContent BuildTx
forall redeemer.
ToScriptData redeemer =>
PlutusScript
-> redeemer
-> PolicyAssets
-> TxBodyContent BuildTx
-> TxBodyContent BuildTx
mintTokens (TxIn -> PlutusScript
HeadTokens.mkHeadTokenScript TxIn
seedTxIn) MintAction
Mint ([Item PolicyAssets] -> PolicyAssets
forall l. IsList l => [Item l] -> l
fromList ([Item PolicyAssets] -> PolicyAssets)
-> [Item PolicyAssets] -> PolicyAssets
forall a b. (a -> b) -> a -> b
$ (AssetName
hydraHeadV1AssetName, Quantity
1) (AssetName, Quantity)
-> [(AssetName, Quantity)] -> [(AssetName, Quantity)]
forall a. a -> [a] -> [a]
: [(AssetName, Quantity)]
participationTokens)
      TxBodyContent BuildTx
-> (TxBodyContent BuildTx -> TxBodyContent BuildTx)
-> TxBodyContent BuildTx
forall a b. a -> (a -> b) -> b
& TxMetadataInEra Era
-> TxBodyContent BuildTx -> TxBodyContent BuildTx
forall era build.
TxMetadataInEra era
-> TxBodyContent build era -> TxBodyContent build era
setTxMetadata (TxMetadata -> TxMetadataInEra Era
TxMetadataInEra (TxMetadata -> TxMetadataInEra Era)
-> TxMetadata -> TxMetadataInEra Era
forall a b. (a -> b) -> a -> b
$ Text -> TxMetadata
mkHydraHeadV1TxName Text
"InitTx")
 where
  participationTokens :: [(AssetName, Quantity)]
participationTokens =
    [(OnChainId -> AssetName
onChainIdToAssetName OnChainId
oid, Quantity
1) | OnChainId
oid <- [OnChainId]
participants]

mkHeadOutput :: NetworkId -> PolicyId -> TxOutDatum ctx -> TxOut ctx
mkHeadOutput :: forall ctx. NetworkId -> PolicyId -> TxOutDatum ctx -> TxOut ctx
mkHeadOutput NetworkId
networkId PolicyId
tokenPolicyId TxOutDatum ctx
datum =
  AddressInEra
-> Value -> TxOutDatum ctx -> ReferenceScript -> TxOut ctx
forall ctx.
AddressInEra
-> Value -> TxOutDatum ctx -> ReferenceScript -> TxOut ctx
TxOut
    (NetworkId -> PlutusScript -> AddressInEra
forall lang era.
(IsShelleyBasedEra era, IsPlutusScriptLanguage lang) =>
NetworkId -> PlutusScript lang -> AddressInEra era
mkScriptAddress NetworkId
networkId PlutusScript
Head.validatorScript)
    ([Item Value] -> Value
forall l. IsList l => [Item l] -> l
fromList [(PolicyId -> AssetName -> AssetId
AssetId PolicyId
tokenPolicyId AssetName
hydraHeadV1AssetName, Quantity
1)])
    TxOutDatum ctx
datum
    ReferenceScript
ReferenceScriptNone

mkHeadOutputInitial :: NetworkId -> TxIn -> HeadParameters -> TxOut CtxTx
mkHeadOutputInitial :: NetworkId -> TxIn -> HeadParameters -> TxOut CtxTx Era
mkHeadOutputInitial NetworkId
networkId TxIn
seedTxIn HeadParameters{ContestationPeriod
contestationPeriod :: ContestationPeriod
$sel:contestationPeriod:HeadParameters :: HeadParameters -> ContestationPeriod
contestationPeriod, [Party]
parties :: [Party]
$sel:parties:HeadParameters :: HeadParameters -> [Party]
parties} =
  NetworkId -> PolicyId -> TxOutDatum CtxTx -> TxOut CtxTx Era
forall ctx. NetworkId -> PolicyId -> TxOutDatum ctx -> TxOut ctx
mkHeadOutput NetworkId
networkId PolicyId
tokenPolicyId TxOutDatum CtxTx
headDatum
 where
  tokenPolicyId :: PolicyId
tokenPolicyId = TxIn -> PolicyId
HeadTokens.headPolicyId TxIn
seedTxIn
  headDatum :: TxOutDatum CtxTx
headDatum =
    State -> TxOutDatum CtxTx
forall era a ctx.
(ToScriptData a, IsBabbageBasedEra era) =>
a -> TxOutDatum ctx era
mkTxOutDatumInline (State -> TxOutDatum CtxTx) -> State -> TxOutDatum CtxTx
forall a b. (a -> b) -> a -> b
$
      Head.Initial
        { $sel:contestationPeriod:Initial :: ContestationPeriod
contestationPeriod = ContestationPeriod -> ContestationPeriod
toChain ContestationPeriod
contestationPeriod
        , $sel:parties:Initial :: [Party]
parties = (Party -> Party) -> [Party] -> [Party]
forall a b. (a -> b) -> [a] -> [b]
map Party -> Party
partyToChain [Party]
parties
        , $sel:headId:Initial :: CurrencySymbol
headId = PolicyId -> CurrencySymbol
toPlutusCurrencySymbol PolicyId
tokenPolicyId
        , $sel:seed:Initial :: TxOutRef
seed = TxIn -> TxOutRef
toPlutusTxOutRef TxIn
seedTxIn
        }

mkInitialOutput :: NetworkId -> TxIn -> OnChainId -> TxOut CtxTx
mkInitialOutput :: NetworkId -> TxIn -> OnChainId -> TxOut CtxTx Era
mkInitialOutput NetworkId
networkId TxIn
seedTxIn OnChainId
participant =
  AddressInEra
-> Value -> TxOutDatum CtxTx -> ReferenceScript -> TxOut CtxTx Era
forall ctx.
AddressInEra
-> Value -> TxOutDatum ctx -> ReferenceScript -> TxOut ctx
TxOut AddressInEra
initialAddress Value
initialValue TxOutDatum CtxTx
initialDatum ReferenceScript
ReferenceScriptNone
 where
  tokenPolicyId :: PolicyId
tokenPolicyId = TxIn -> PolicyId
HeadTokens.headPolicyId TxIn
seedTxIn
  initialValue :: Value
initialValue =
    [Item Value] -> Value
forall l. IsList l => [Item l] -> l
fromList [(PolicyId -> AssetName -> AssetId
AssetId PolicyId
tokenPolicyId (OnChainId -> AssetName
onChainIdToAssetName OnChainId
participant), Quantity
1)]
  initialAddress :: AddressInEra
initialAddress =
    NetworkId -> PlutusScript -> AddressInEra
forall lang era.
(IsShelleyBasedEra era, IsPlutusScriptLanguage lang) =>
NetworkId -> PlutusScript lang -> AddressInEra era
mkScriptAddress NetworkId
networkId PlutusScript
initialValidatorScript
  initialDatum :: TxOutDatum CtxTx
initialDatum =
    Datum -> TxOutDatum CtxTx
forall era a ctx.
(ToScriptData a, IsBabbageBasedEra era) =>
a -> TxOutDatum ctx era
mkTxOutDatumInline (Datum -> TxOutDatum CtxTx) -> Datum -> TxOutDatum CtxTx
forall a b. (a -> b) -> a -> b
$ CurrencySymbol -> Datum
Initial.datum (PolicyId -> CurrencySymbol
toPlutusCurrencySymbol PolicyId
tokenPolicyId)

-- * Observation

-- | Data which can be observed from an `initTx`.
data InitObservation = InitObservation
  { InitObservation -> (TxIn, TxOut CtxUTxO)
initialThreadUTxO :: (TxIn, TxOut CtxUTxO)
  , InitObservation -> [(TxIn, TxOut CtxUTxO)]
initials :: [(TxIn, TxOut CtxUTxO)]
  , InitObservation -> HeadId
headId :: HeadId
  , -- XXX: This is cardano-specific, while headId, parties and
    -- contestationPeriod are already generic here. Check which is more
    -- convenient and consistent!
    InitObservation -> TxIn
seedTxIn :: TxIn
  , InitObservation -> ContestationPeriod
contestationPeriod :: ContestationPeriod
  , InitObservation -> [Party]
parties :: [Party]
  , -- XXX: Improve naming
    InitObservation -> [OnChainId]
participants :: [OnChainId]
  }
  deriving stock (Int -> InitObservation -> ShowS
[InitObservation] -> ShowS
InitObservation -> String
(Int -> InitObservation -> ShowS)
-> (InitObservation -> String)
-> ([InitObservation] -> ShowS)
-> Show InitObservation
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> InitObservation -> ShowS
showsPrec :: Int -> InitObservation -> ShowS
$cshow :: InitObservation -> String
show :: InitObservation -> String
$cshowList :: [InitObservation] -> ShowS
showList :: [InitObservation] -> ShowS
Show, InitObservation -> InitObservation -> Bool
(InitObservation -> InitObservation -> Bool)
-> (InitObservation -> InitObservation -> Bool)
-> Eq InitObservation
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: InitObservation -> InitObservation -> Bool
== :: InitObservation -> InitObservation -> Bool
$c/= :: InitObservation -> InitObservation -> Bool
/= :: InitObservation -> InitObservation -> Bool
Eq, (forall x. InitObservation -> Rep InitObservation x)
-> (forall x. Rep InitObservation x -> InitObservation)
-> Generic InitObservation
forall x. Rep InitObservation x -> InitObservation
forall x. InitObservation -> Rep InitObservation x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. InitObservation -> Rep InitObservation x
from :: forall x. InitObservation -> Rep InitObservation x
$cto :: forall x. Rep InitObservation x -> InitObservation
to :: forall x. Rep InitObservation x -> InitObservation
Generic)

data NotAnInitReason
  = NoHeadOutput
  | NotAHeadDatum
  | InvalidPartyInDatum
  | NoSTFound
  | NotAHeadPolicy
  deriving stock (Int -> NotAnInitReason -> ShowS
[NotAnInitReason] -> ShowS
NotAnInitReason -> String
(Int -> NotAnInitReason -> ShowS)
-> (NotAnInitReason -> String)
-> ([NotAnInitReason] -> ShowS)
-> Show NotAnInitReason
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> NotAnInitReason -> ShowS
showsPrec :: Int -> NotAnInitReason -> ShowS
$cshow :: NotAnInitReason -> String
show :: NotAnInitReason -> String
$cshowList :: [NotAnInitReason] -> ShowS
showList :: [NotAnInitReason] -> ShowS
Show, NotAnInitReason -> NotAnInitReason -> Bool
(NotAnInitReason -> NotAnInitReason -> Bool)
-> (NotAnInitReason -> NotAnInitReason -> Bool)
-> Eq NotAnInitReason
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: NotAnInitReason -> NotAnInitReason -> Bool
== :: NotAnInitReason -> NotAnInitReason -> Bool
$c/= :: NotAnInitReason -> NotAnInitReason -> Bool
/= :: NotAnInitReason -> NotAnInitReason -> Bool
Eq, (forall x. NotAnInitReason -> Rep NotAnInitReason x)
-> (forall x. Rep NotAnInitReason x -> NotAnInitReason)
-> Generic NotAnInitReason
forall x. Rep NotAnInitReason x -> NotAnInitReason
forall x. NotAnInitReason -> Rep NotAnInitReason x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. NotAnInitReason -> Rep NotAnInitReason x
from :: forall x. NotAnInitReason -> Rep NotAnInitReason x
$cto :: forall x. Rep NotAnInitReason x -> NotAnInitReason
to :: forall x. Rep NotAnInitReason x -> NotAnInitReason
Generic)

-- | Identify a init tx by checking the output value for holding tokens that are
-- valid head tokens (checked by seed + policy).
observeInitTx ::
  Tx ->
  Either NotAnInitReason InitObservation
observeInitTx :: Tx -> Either NotAnInitReason InitObservation
observeInitTx Tx
tx = do
  -- XXX: Lots of redundant information here
  (Word
ix, TxOut CtxTx Era
headOut, State
headState) <-
    NotAnInitReason
-> Maybe (Word, TxOut CtxTx Era, State)
-> Either NotAnInitReason (Word, TxOut CtxTx Era, State)
forall {a} {b}. a -> Maybe b -> Either a b
maybeLeft NotAnInitReason
NoHeadOutput (Maybe (Word, TxOut CtxTx Era, State)
 -> Either NotAnInitReason (Word, TxOut CtxTx Era, State))
-> Maybe (Word, TxOut CtxTx Era, State)
-> Either NotAnInitReason (Word, TxOut CtxTx Era, State)
forall a b. (a -> b) -> a -> b
$
      ((Word, TxOut CtxTx Era) -> Maybe (Word, TxOut CtxTx Era, State))
-> [(Word, TxOut CtxTx Era)]
-> Maybe (Word, TxOut CtxTx Era, State)
forall (t :: * -> *) a b.
Foldable t =>
(a -> Maybe b) -> t a -> Maybe b
findFirst (Word, TxOut CtxTx Era) -> Maybe (Word, TxOut CtxTx Era, State)
forall {a} {t} {era}.
FromData a =>
(t, TxOut CtxTx era) -> Maybe (t, TxOut CtxTx era, a)
matchHeadOutput [(Word, TxOut CtxTx Era)]
indexedOutputs

  -- check that we have a proper head
  (PolicyId
pid, ContestationPeriod
contestationPeriod, [Party]
onChainParties, TxIn
seedTxIn) <- case State
headState of
    (Head.Initial ContestationPeriod
cp [Party]
ps CurrencySymbol
cid TxOutRef
outRef) -> do
      PolicyId
pid <- CurrencySymbol -> Maybe PolicyId
forall (m :: * -> *). MonadFail m => CurrencySymbol -> m PolicyId
fromPlutusCurrencySymbol CurrencySymbol
cid Maybe PolicyId
-> NotAnInitReason -> Either NotAnInitReason PolicyId
forall a e. Maybe a -> e -> Either e a
?> NotAnInitReason
NotAHeadPolicy
      (PolicyId, ContestationPeriod, [Party], TxIn)
-> Either
     NotAnInitReason (PolicyId, ContestationPeriod, [Party], TxIn)
forall a. a -> Either NotAnInitReason a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (PolicyId
pid, ContestationPeriod -> ContestationPeriod
fromChain ContestationPeriod
cp, [Party]
ps, TxOutRef -> TxIn
fromPlutusTxOutRef TxOutRef
outRef)
    State
_ -> NotAnInitReason
-> Either
     NotAnInitReason (PolicyId, ContestationPeriod, [Party], TxIn)
forall a b. a -> Either a b
Left NotAnInitReason
NotAHeadDatum

  let stQuantity :: Quantity
stQuantity = Value -> AssetId -> Quantity
selectAsset (TxOut CtxTx Era -> Value
forall ctx. TxOut ctx -> Value
txOutValue TxOut CtxTx Era
headOut) (PolicyId -> AssetName -> AssetId
AssetId PolicyId
pid AssetName
hydraHeadV1AssetName)

  -- check that ST is present in the head output
  Bool -> Either NotAnInitReason () -> Either NotAnInitReason ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Quantity
stQuantity Quantity -> Quantity -> Bool
forall a. Eq a => a -> a -> Bool
== Quantity
1) (Either NotAnInitReason () -> Either NotAnInitReason ())
-> Either NotAnInitReason () -> Either NotAnInitReason ()
forall a b. (a -> b) -> a -> b
$
    NotAnInitReason -> Either NotAnInitReason ()
forall a b. a -> Either a b
Left NotAnInitReason
NoSTFound

  -- check that we are using the same seed and headId matches
  Bool -> Either NotAnInitReason () -> Either NotAnInitReason ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (PolicyId
pid PolicyId -> PolicyId -> Bool
forall a. Eq a => a -> a -> Bool
== TxIn -> PolicyId
HeadTokens.headPolicyId TxIn
seedTxIn) (Either NotAnInitReason () -> Either NotAnInitReason ())
-> Either NotAnInitReason () -> Either NotAnInitReason ()
forall a b. (a -> b) -> a -> b
$
    NotAnInitReason -> Either NotAnInitReason ()
forall a b. a -> Either a b
Left NotAnInitReason
NotAHeadPolicy

  [Party]
parties <-
    Either NotAnInitReason [Party]
-> ([Party] -> Either NotAnInitReason [Party])
-> Maybe [Party]
-> Either NotAnInitReason [Party]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (NotAnInitReason -> Either NotAnInitReason [Party]
forall a b. a -> Either a b
Left NotAnInitReason
InvalidPartyInDatum) [Party] -> Either NotAnInitReason [Party]
forall a b. b -> Either a b
Right (Maybe [Party] -> Either NotAnInitReason [Party])
-> Maybe [Party] -> Either NotAnInitReason [Party]
forall a b. (a -> b) -> a -> b
$
      (Party -> Maybe Party) -> [Party] -> Maybe [Party]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> [a] -> f [b]
traverse Party -> Maybe Party
forall (m :: * -> *). MonadFail m => Party -> m Party
partyFromChain [Party]
onChainParties

  InitObservation -> Either NotAnInitReason InitObservation
forall a. a -> Either NotAnInitReason a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (InitObservation -> Either NotAnInitReason InitObservation)
-> InitObservation -> Either NotAnInitReason InitObservation
forall a b. (a -> b) -> a -> b
$
    InitObservation
      { $sel:headId:InitObservation :: HeadId
headId = PolicyId -> HeadId
mkHeadId PolicyId
pid
      , TxIn
$sel:seedTxIn:InitObservation :: TxIn
seedTxIn :: TxIn
seedTxIn
      , $sel:initialThreadUTxO:InitObservation :: (TxIn, TxOut CtxUTxO)
initialThreadUTxO = (Tx -> Word -> TxIn
forall era. Tx era -> Word -> TxIn
mkTxIn Tx
tx Word
ix, TxOut CtxTx Era -> TxOut CtxUTxO
forall era. TxOut CtxTx era -> TxOut CtxUTxO era
toCtxUTxOTxOut TxOut CtxTx Era
headOut)
      , [(TxIn, TxOut CtxUTxO)]
$sel:initials:InitObservation :: [(TxIn, TxOut CtxUTxO)]
initials :: [(TxIn, TxOut CtxUTxO)]
initials
      , ContestationPeriod
$sel:contestationPeriod:InitObservation :: ContestationPeriod
contestationPeriod :: ContestationPeriod
contestationPeriod
      , [Party]
$sel:parties:InitObservation :: [Party]
parties :: [Party]
parties
      , $sel:participants:InitObservation :: [OnChainId]
participants = AssetName -> OnChainId
assetNameToOnChainId (AssetName -> OnChainId) -> [AssetName] -> [OnChainId]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> PolicyId -> [AssetName]
mintedTokenNames PolicyId
pid
      }
 where
  maybeLeft :: a -> Maybe b -> Either a b
maybeLeft a
e = Either a b -> (b -> Either a b) -> Maybe b -> Either a b
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (a -> Either a b
forall a b. a -> Either a b
Left a
e) b -> Either a b
forall a b. b -> Either a b
Right

  matchHeadOutput :: (t, TxOut CtxTx era) -> Maybe (t, TxOut CtxTx era, a)
matchHeadOutput (t
ix, TxOut CtxTx era
out) = do
    Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ PlutusScript -> TxOut CtxTx era -> Bool
forall lang ctx era.
IsPlutusScriptLanguage lang =>
PlutusScript lang -> TxOut ctx era -> Bool
isScriptTxOut PlutusScript
Head.validatorScript TxOut CtxTx era
out
    (t
ix,TxOut CtxTx era
out,) (a -> (t, TxOut CtxTx era, a))
-> Maybe a -> Maybe (t, TxOut CtxTx era, a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (HashableScriptData -> Maybe a
forall a. FromScriptData a => HashableScriptData -> Maybe a
fromScriptData (HashableScriptData -> Maybe a)
-> Maybe HashableScriptData -> Maybe a
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< TxOut CtxTx era -> Maybe HashableScriptData
forall era. TxOut CtxTx era -> Maybe HashableScriptData
txOutScriptData TxOut CtxTx era
out)

  indexedOutputs :: [(Word, TxOut CtxTx Era)]
indexedOutputs = [Word] -> [TxOut CtxTx Era] -> [(Word, TxOut CtxTx Era)]
forall a b. [a] -> [b] -> [(a, b)]
zip [Word
0 ..] (Tx -> [TxOut CtxTx Era]
forall era. Tx era -> [TxOut CtxTx era]
txOuts' Tx
tx)

  initialOutputs :: [(Word, TxOut CtxTx Era)]
initialOutputs = ((Word, TxOut CtxTx Era) -> Bool)
-> [(Word, TxOut CtxTx Era)] -> [(Word, TxOut CtxTx Era)]
forall a. (a -> Bool) -> [a] -> [a]
filter (TxOut CtxTx Era -> Bool
forall {ctx} {era}. TxOut ctx era -> Bool
isInitial (TxOut CtxTx Era -> Bool)
-> ((Word, TxOut CtxTx Era) -> TxOut CtxTx Era)
-> (Word, TxOut CtxTx Era)
-> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Word, TxOut CtxTx Era) -> TxOut CtxTx Era
forall a b. (a, b) -> b
snd) [(Word, TxOut CtxTx Era)]
indexedOutputs

  initials :: [(TxIn, TxOut CtxUTxO)]
initials =
    ((Word, TxOut CtxTx Era) -> (TxIn, TxOut CtxUTxO))
-> [(Word, TxOut CtxTx Era)] -> [(TxIn, TxOut CtxUTxO)]
forall a b. (a -> b) -> [a] -> [b]
map
      ((Word -> TxIn)
-> (TxOut CtxTx Era -> TxOut CtxUTxO)
-> (Word, TxOut CtxTx Era)
-> (TxIn, TxOut CtxUTxO)
forall a b c d. (a -> b) -> (c -> d) -> (a, c) -> (b, d)
forall (p :: * -> * -> *) a b c d.
Bifunctor p =>
(a -> b) -> (c -> d) -> p a c -> p b d
bimap (Tx -> Word -> TxIn
forall era. Tx era -> Word -> TxIn
mkTxIn Tx
tx) TxOut CtxTx Era -> TxOut CtxUTxO
forall era. TxOut CtxTx era -> TxOut CtxUTxO era
toCtxUTxOTxOut)
      [(Word, TxOut CtxTx Era)]
initialOutputs

  isInitial :: TxOut ctx era -> Bool
isInitial = PlutusScript -> TxOut ctx era -> Bool
forall lang ctx era.
IsPlutusScriptLanguage lang =>
PlutusScript lang -> TxOut ctx era -> Bool
isScriptTxOut PlutusScript
initialValidatorScript

  mintedTokenNames :: PolicyId -> [AssetName]
mintedTokenNames PolicyId
pid =
    [ AssetName
assetName
    | (AssetId PolicyId
policyId AssetName
assetName, Quantity
q) <- Value -> [Item Value]
forall l. IsList l => l -> [Item l]
toList (Value -> [Item Value]) -> Value -> [Item Value]
forall a b. (a -> b) -> a -> b
$ TxMintValue ViewTx Era -> Value
forall build era. TxMintValue build era -> Value
txMintValueToValue (TxMintValue ViewTx Era -> Value)
-> TxMintValue ViewTx Era -> Value
forall a b. (a -> b) -> a -> b
$ TxBodyContent ViewTx -> TxMintValue ViewTx Era
forall buidl. TxBodyContent buidl -> TxMintValue buidl
txMintValue (TxBodyContent ViewTx -> TxMintValue ViewTx Era)
-> TxBodyContent ViewTx -> TxMintValue ViewTx Era
forall a b. (a -> b) -> a -> b
$ TxBody Era -> TxBodyContent ViewTx
forall era. TxBody era -> TxBodyContent ViewTx era
getTxBodyContent (TxBody Era -> TxBodyContent ViewTx)
-> TxBody Era -> TxBodyContent ViewTx
forall a b. (a -> b) -> a -> b
$ Tx -> TxBody Era
forall era. Tx era -> TxBody era
getTxBody Tx
tx
    , Quantity
q Quantity -> Quantity -> Bool
forall a. Eq a => a -> a -> Bool
== Quantity
1 -- NOTE: Only consider unique tokens
    , PolicyId
policyId PolicyId -> PolicyId -> Bool
forall a. Eq a => a -> a -> Bool
== PolicyId
pid
    , AssetName
assetName AssetName -> AssetName -> Bool
forall a. Eq a => a -> a -> Bool
/= AssetName
hydraHeadV1AssetName
    ]