diff --git a/src/Internal/Contract/QueryHandle.purs b/src/Internal/Contract/QueryHandle.purs index 360c6bf914..c678c78548 100644 --- a/src/Internal/Contract/QueryHandle.purs +++ b/src/Internal/Contract/QueryHandle.purs @@ -10,7 +10,6 @@ import Cardano.AsCbor (encodeCbor) import Cardano.Types.Transaction (hash) as Transaction import Contract.Log (logDebug') import Control.Monad.Error.Class (throwError) -import Control.Monad.Except (ExceptT(ExceptT), runExceptT) import Ctl.Internal.Contract.LogParams (LogParams) import Ctl.Internal.Contract.QueryBackend (BlockfrostBackend, CtlBackend) import Ctl.Internal.Contract.QueryHandle.Type (QueryHandle) @@ -41,7 +40,7 @@ import Ctl.Internal.Service.Blockfrost import Ctl.Internal.Service.Blockfrost as Blockfrost import Ctl.Internal.Service.Error (ClientError(ClientOtherError)) import Data.Either (Either(Left, Right)) -import Data.Maybe (Maybe(Just, Nothing), fromMaybe, isJust) +import Data.Maybe (fromMaybe, isJust) import Data.Newtype (wrap) import Effect.Aff (Aff) import Effect.Exception (error) @@ -101,15 +100,7 @@ queryHandleForBlockfrostBackend logParams backend = , getOutputAddressesByTxHash: runBlockfrostServiceM' <<< Blockfrost.getOutputAddressesByTxHash , doesTxExist: runBlockfrostServiceM' <<< Blockfrost.doesTxExist - , getTxAuxiliaryData: \txHash -> runExceptT do - -- FIXME: check if Blockfrost also returns full aux data - metadata <- ExceptT $ runBlockfrostServiceM' $ Blockfrost.getTxMetadata - txHash - pure $ wrap - { metadata: Just metadata - , nativeScripts: Nothing - , plutusScripts: Nothing - } + , getTxAuxiliaryData: runBlockfrostServiceM' <<< Blockfrost.getTxAuxiliaryData , utxosAt: runBlockfrostServiceM' <<< Blockfrost.utxosAt , getChainTip: runBlockfrostServiceM' Blockfrost.getChainTip , getCurrentEpoch: diff --git a/src/Internal/QueryM/Kupo.purs b/src/Internal/QueryM/Kupo.purs index 5ae6ef14b6..3a5eb589b0 100644 --- a/src/Internal/QueryM/Kupo.purs +++ b/src/Internal/QueryM/Kupo.purs @@ -457,7 +457,7 @@ unwrapKupoUtxoSlot :: KupoUtxoSlot -> Slot unwrapKupoUtxoSlot (KupoUtxoSlot slot) = slot -------------------------------------------------------------------------------- --- `getTxAuxiliaryData` reponse parsing +-- `getTxAuxiliaryData` response parsing -------------------------------------------------------------------------------- newtype KupoAuxiliaryData = KupoAuxiliaryData (Maybe AuxiliaryData) diff --git a/src/Internal/Service/Blockfrost.purs b/src/Internal/Service/Blockfrost.purs index 1e9708af21..e833f083a7 100644 --- a/src/Internal/Service/Blockfrost.purs +++ b/src/Internal/Service/Blockfrost.purs @@ -49,6 +49,7 @@ module Ctl.Internal.Service.Blockfrost , getScriptByHash , getScriptInfo , getSystemStart + , getTxAuxiliaryData , getTxMetadata , getUtxoByOref , getValidatorHashDelegationsAndRewards @@ -85,9 +86,10 @@ import Cardano.AsCbor (decodeCbor, encodeCbor) import Cardano.Serialization.Lib (toBytes) import Cardano.Types ( AssetClass(AssetClass) + , AuxiliaryData , DataHash , GeneralTransactionMetadata(GeneralTransactionMetadata) - , Language(PlutusV1, PlutusV2, PlutusV3) + , Language(PlutusV3, PlutusV2, PlutusV1) , PlutusData , PoolPubKeyHash , RawBytes @@ -128,6 +130,7 @@ import Cardano.Types.NativeScript ) import Cardano.Types.NetworkId (NetworkId) import Cardano.Types.OutputDatum (OutputDatum(OutputDatum, OutputDatumHash)) +import Cardano.Types.PlutusScript (PlutusScript) import Cardano.Types.PlutusScript as PlutusScript import Cardano.Types.PoolPubKeyHash as PoolPubKeyHash import Cardano.Types.RedeemerTag (RedeemerTag(Spend, Mint, Cert, Reward)) as RedeemerTag @@ -201,6 +204,7 @@ import Ctl.Internal.Types.ProtocolParameters import Ctl.Internal.Types.Rational (Rational, reduce) import Ctl.Internal.Types.StakeValidatorHash (StakeValidatorHash) import Ctl.Internal.Types.SystemStart (SystemStart(SystemStart)) +import Data.Array (catMaybes) import Data.Array (find, length) as Array import Data.Bifunctor (lmap) import Data.BigNumber (BigNumber, toFraction) @@ -236,6 +240,7 @@ import Effect.Exception (error) import Foreign.Object (Object) import Foreign.Object as ForeignObject import JS.BigInt (fromString, toNumber) as BigInt +import Prim.TypeError (class Warn, Text) -------------------------------------------------------------------------------- -- BlockfrostServiceM @@ -673,23 +678,79 @@ doesTxExist txHash = do Left e -> Left e -------------------------------------------------------------------------------- --- Get transaction metadata --------------------------------------------------------------------------------- +-- Get transaction auxiliary data +getTxAuxiliaryData + :: TransactionHash + -> BlockfrostServiceM (Either GetTxMetadataError AuxiliaryData) +getTxAuxiliaryData txHash = runExceptT do + metadata <- ExceptT $ getMetadata + scriptRefs <- ExceptT $ getTxScripts txHash + pure $ wrap + { metadata: Just metadata + , nativeScripts: arrayToMaybe $ getNativeScripts scriptRefs + , plutusScripts: arrayToMaybe $ getPlutusScripts scriptRefs + } + + where + + getMetadata = do + response <- blockfrostGetRequest (TransactionMetadata txHash) + pure case unwrapBlockfrostMetadata <$> handleBlockfrostResponse response of + Left (ClientHttpResponseError (Affjax.StatusCode 404) _) -> + Left GetTxMetadataTxNotFoundError + Left e -> Left (GetTxMetadataClientError e) + Right metadata + | Map.isEmpty (unwrap metadata) -> + Left GetTxMetadataMetadataEmptyOrMissingError + | otherwise -> Right metadata + + getNativeScripts :: Array ScriptRef -> Array NativeScript + getNativeScripts = catMaybes <<< map isNativeScript + where + isNativeScript (NativeScriptRef script) = Just script + isNativeScript (PlutusScriptRef _) = Nothing + + getPlutusScripts :: Array ScriptRef -> Array PlutusScript + getPlutusScripts = catMaybes <<< map isPlutusScript + where + isPlutusScript (PlutusScriptRef script) = Just script + isPlutusScript (NativeScriptRef _) = Nothing + + arrayToMaybe :: forall a. Array a -> Maybe (Array a) + arrayToMaybe [] = Nothing + arrayToMaybe xs = Just xs getTxMetadata - :: TransactionHash + :: Warn + ( Text + "deprecated: getTxMetadata. use Ctl.Internal.Service.Blockfrost.getTxAuxiliaryData" + ) + => TransactionHash -> BlockfrostServiceM (Either GetTxMetadataError GeneralTransactionMetadata) getTxMetadata txHash = do - response <- blockfrostGetRequest (TransactionMetadata txHash) - pure case unwrapBlockfrostMetadata <$> handleBlockfrostResponse response of - Left (ClientHttpResponseError (Affjax.StatusCode 404) _) -> - Left GetTxMetadataTxNotFoundError - Left e -> - Left (GetTxMetadataClientError e) - Right metadata - | Map.isEmpty (unwrap metadata) -> - Left GetTxMetadataMetadataEmptyOrMissingError - | otherwise -> Right metadata + eAuxData <- getTxAuxiliaryData txHash + pure $ case eAuxData of + Left err -> Left err + Right auxiliaryData -> case (unwrap auxiliaryData).metadata of + Nothing -> Left GetTxMetadataMetadataEmptyOrMissingError + Just metadata -> Right metadata + +getTxScripts + :: TransactionHash + -> BlockfrostServiceM (Either GetTxMetadataError (Array ScriptRef)) +getTxScripts txHash = runExceptT do + (blockfrostUtxoMap :: BlockfrostUtxosOfTransaction) <- ExceptT $ + blockfrostGetRequest (UtxosOfTransaction txHash) + <#> lmap GetTxMetadataClientError <<< handle404AsMempty <<< + handleBlockfrostResponse + let + (scriptHashes :: Array ScriptHash) = catMaybes + $ map (_.scriptHash <<< unwrap <<< snd) + $ unwrap blockfrostUtxoMap + catMaybes <$> traverse (ExceptT <<< scriptByHash) scriptHashes + + where + scriptByHash t = (lmap GetTxMetadataClientError) <$> getScriptByHash t -------------------------------------------------------------------------------- -- Get current epoch information