blockfrost-api (empty) → 0.1.0.0
raw patch · 73 files changed
+6383/−0 lines, 73 filesdep +QuickCheckdep +aesondep +basesetup-changed
Dependencies added: QuickCheck, aeson, base, blockfrost-api, bytestring, data-default, data-default-class, deriving-aeson, hspec, lens, quickcheck-instances, raw-strings-qq, safe-money, servant, servant-docs, servant-multipart-api, tasty, tasty-hspec, tasty-hunit, template-haskell, text, time, vector
Files
- CHANGELOG.md +10/−0
- LICENSE +13/−0
- README.md +44/−0
- Setup.hs +2/−0
- blockfrost-api.cabal +167/−0
- src/Blockfrost/API.hs +136/−0
- src/Blockfrost/API/Cardano.hs +27/−0
- src/Blockfrost/API/Cardano/Accounts.hs +79/−0
- src/Blockfrost/API/Cardano/Addresses.hs +52/−0
- src/Blockfrost/API/Cardano/Assets.hs +68/−0
- src/Blockfrost/API/Cardano/Blocks.hs +82/−0
- src/Blockfrost/API/Cardano/Epochs.hs +97/−0
- src/Blockfrost/API/Cardano/Ledger.hs +21/−0
- src/Blockfrost/API/Cardano/Metadata.hs +53/−0
- src/Blockfrost/API/Cardano/Network.hs +21/−0
- src/Blockfrost/API/Cardano/Pools.hs +98/−0
- src/Blockfrost/API/Cardano/Transactions.hs +87/−0
- src/Blockfrost/API/Common.hs +56/−0
- src/Blockfrost/API/IPFS.hs +84/−0
- src/Blockfrost/API/NutLink.hs +56/−0
- src/Blockfrost/Auth.hs +74/−0
- src/Blockfrost/Env.hs +32/−0
- src/Blockfrost/Lens.hs +86/−0
- src/Blockfrost/Types.hs +17/−0
- src/Blockfrost/Types/ApiError.hs +35/−0
- src/Blockfrost/Types/Cardano.hs +25/−0
- src/Blockfrost/Types/Cardano/Accounts.hs +204/−0
- src/Blockfrost/Types/Cardano/Addresses.hs +143/−0
- src/Blockfrost/Types/Cardano/Assets.hs +221/−0
- src/Blockfrost/Types/Cardano/Blocks.hs +52/−0
- src/Blockfrost/Types/Cardano/Epochs.hs +133/−0
- src/Blockfrost/Types/Cardano/Genesis.hs +44/−0
- src/Blockfrost/Types/Cardano/Metadata.hs +83/−0
- src/Blockfrost/Types/Cardano/Network.hs +65/−0
- src/Blockfrost/Types/Cardano/Pools.hs +254/−0
- src/Blockfrost/Types/Cardano/Transactions.hs +359/−0
- src/Blockfrost/Types/Common.hs +103/−0
- src/Blockfrost/Types/IPFS.hs +102/−0
- src/Blockfrost/Types/NutLink.hs +95/−0
- src/Blockfrost/Types/Shared.hs +35/−0
- src/Blockfrost/Types/Shared/Ada.hs +55/−0
- src/Blockfrost/Types/Shared/Address.hs +37/−0
- src/Blockfrost/Types/Shared/Amount.hs +79/−0
- src/Blockfrost/Types/Shared/AssetId.hs +36/−0
- src/Blockfrost/Types/Shared/BlockHash.hs +50/−0
- src/Blockfrost/Types/Shared/BlockIndex.hs +60/−0
- src/Blockfrost/Types/Shared/CBOR.hs +27/−0
- src/Blockfrost/Types/Shared/Epoch.hs +22/−0
- src/Blockfrost/Types/Shared/Opts.hs +25/−0
- src/Blockfrost/Types/Shared/POSIXMillis.hs +48/−0
- src/Blockfrost/Types/Shared/PolicyId.hs +34/−0
- src/Blockfrost/Types/Shared/PoolId.hs +33/−0
- src/Blockfrost/Types/Shared/Quantity.hs +31/−0
- src/Blockfrost/Types/Shared/Slot.hs +22/−0
- src/Blockfrost/Types/Shared/TxHash.hs +37/−0
- src/Blockfrost/Util/LensRules.hs +35/−0
- src/Blockfrost/Util/Pagination.hs +54/−0
- src/Blockfrost/Util/Sorting.hs +45/−0
- src/Blockfrost/Util/Tag.hs +13/−0
- test/APISpec.hs +135/−0
- test/AmountSpec.hs +76/−0
- test/Cardano/Accounts.hs +266/−0
- test/Cardano/Addresses.hs +231/−0
- test/Cardano/Assets.hs +233/−0
- test/Cardano/Epochs.hs +146/−0
- test/Cardano/Ledger.hs +54/−0
- test/Cardano/Metadata.hs +143/−0
- test/Cardano/Network.hs +47/−0
- test/Cardano/Pools.hs +277/−0
- test/Cardano/Transactions.hs +387/−0
- test/IPFS.hs +77/−0
- test/NutLink.hs +152/−0
- test/Spec.hs +1/−0
+ CHANGELOG.md view
@@ -0,0 +1,10 @@+# Version [0.1.0.0](https://github.com/blockfrost/blockfrost-haskell/compare/initial...0.1.0.0) (2021-09-14)++* Initial release++---++`blockfrost-api` uses [PVP Versioning][1].++[1]: https://pvp.haskell.org+
+ LICENSE view
@@ -0,0 +1,13 @@+Copyright 2021 blockfrost.io++Licensed under the Apache License, Version 2.0 (the "License");+you may not use this file except in compliance with the License.+You may obtain a copy of the License at++ http://www.apache.org/licenses/LICENSE-2.0++Unless required by applicable law or agreed to in writing, software+distributed under the License is distributed on an "AS IS" BASIS,+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.+See the License for the specific language governing permissions and+limitations under the License.
+ README.md view
@@ -0,0 +1,44 @@+# blockfrost-api++Core types and Servant API definitions.++## Exploring data types++All data types have a `ToSample` instance+for [servant-docs](https://hackage.haskell.org/package/servant-docs)+which can be used to get a sample response in `cabal repl`:++``` haskell+λ: import Data.Proxy+λ: import Servant.Docs+λ: Just block = toSample (Proxy :: Proxy Block)+λ: _blockHash block+BlockHash "4ea1ba291e8eef538635a53e59fddba7810d1679631cc3aed7c8e6c4091a516a"+```++## Lenses++Instead of using long record names, it is recommended+to use provided lenses and a [lens](https://hackage.haskell.org/package/lens) or similar package.++``` haskell+λ: import Control.Lens (^.)+λ: import Blockfrost.Lens+λ: block ^. epoch+Just (Epoch 425)+```++## Monetary values++Ada values and values of assets are represented using [`Discrete`](https://hackage.haskell.org/package/safe-money/docs/Money.html#t:Discrete) type+from [safe-money](https://hackage.haskell.org/package/safe-money) library.++We use a type alias `type Lovelaces = Money.Discrete "ADA" "lovelace"`+for Ada values and [`SomeDiscrete`](https://hackage.haskell.org/package/safe-money/docs/Money.html#t:SomeDiscrete) for asset values. This should allow working+with monetary values safely, preventing summing different currencies.+See the [blog post](https://ren.zone/articles/safe-money) by the library author.++``` haskell+λ: block ^. fees+Just (Discrete "ADA" 1000000%1 592661)+```
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ blockfrost-api.cabal view
@@ -0,0 +1,167 @@+cabal-version: 2.2+name: blockfrost-api+version: 0.1.0.0+synopsis: API definitions for blockfrost.io+description: Core types and Servant API description+homepage: https://github.com/blockfrost/blockfrost-haskell+license: Apache-2.0+license-file: LICENSE+author: blockfrost.io+maintainer: srk@48.io+copyright: 2021 blockfrost.io+category: Web+build-type: Simple++extra-source-files:+ CHANGELOG.md+ LICENSE+ README.md++flag BuildFast+ Default: True+ Description: Turn off optimizations++flag Production+ Default: False+ Manual: True+ Description: Production build++common libstuff+ default-language: Haskell2010+ default-extensions:+ DataKinds+ DeriveAnyClass+ DeriveGeneric+ DerivingVia+ GADTs+ GeneralizedNewtypeDeriving+ FlexibleContexts+ FlexibleInstances+ InstanceSigs+ LambdaCase+ MultiParamTypeClasses+ QuasiQuotes+ RankNTypes+ ScopedTypeVariables+ TemplateHaskell+ TypeApplications+ TypeFamilies+ TypeOperators+ ViewPatterns+ OverloadedStrings+ ghc-options: -Wall -Wunused-packages -fno-warn-orphans+ if flag(BuildFast)+ ghc-options: -O0+ if flag(Production)+ ghc-options: -Werror++library+ import: libstuff+ hs-source-dirs: src+ exposed-modules: Blockfrost.API+ , Blockfrost.API.Common+ , Blockfrost.API.Cardano+ , Blockfrost.API.Cardano.Accounts+ , Blockfrost.API.Cardano.Addresses+ , Blockfrost.API.Cardano.Assets+ , Blockfrost.API.Cardano.Blocks+ , Blockfrost.API.Cardano.Epochs+ , Blockfrost.API.Cardano.Ledger+ , Blockfrost.API.Cardano.Metadata+ , Blockfrost.API.Cardano.Network+ , Blockfrost.API.Cardano.Pools+ , Blockfrost.API.Cardano.Transactions+ , Blockfrost.API.IPFS+ , Blockfrost.API.NutLink+ , Blockfrost.Auth+ , Blockfrost.Env+ , Blockfrost.Lens+ , Blockfrost.Types+ , Blockfrost.Types.ApiError+ , Blockfrost.Types.Common+ , Blockfrost.Types.Cardano+ , Blockfrost.Types.Cardano.Accounts+ , Blockfrost.Types.Cardano.Addresses+ , Blockfrost.Types.Cardano.Assets+ , Blockfrost.Types.Cardano.Blocks+ , Blockfrost.Types.Cardano.Epochs+ , Blockfrost.Types.Cardano.Genesis+ , Blockfrost.Types.Cardano.Metadata+ , Blockfrost.Types.Cardano.Network+ , Blockfrost.Types.Cardano.Pools+ , Blockfrost.Types.Cardano.Transactions+ , Blockfrost.Types.IPFS+ , Blockfrost.Types.NutLink+ , Blockfrost.Types.Shared+ , Blockfrost.Types.Shared.Ada+ , Blockfrost.Types.Shared.Address+ , Blockfrost.Types.Shared.BlockHash+ , Blockfrost.Types.Shared.BlockIndex+ , Blockfrost.Types.Shared.Epoch+ , Blockfrost.Types.Shared.CBOR+ , Blockfrost.Types.Shared.Opts+ , Blockfrost.Types.Shared.POSIXMillis+ , Blockfrost.Types.Shared.Slot+ , Blockfrost.Types.Shared.TxHash+ , Blockfrost.Types.Shared.Amount+ , Blockfrost.Types.Shared.AssetId+ , Blockfrost.Types.Shared.PolicyId+ , Blockfrost.Types.Shared.PoolId+ , Blockfrost.Types.Shared.Quantity+ , Blockfrost.Util.LensRules+ , Blockfrost.Util.Pagination+ , Blockfrost.Util.Sorting+ , Blockfrost.Util.Tag+ build-depends: base >= 4.7 && < 5+ , bytestring+ , data-default-class+ , text+ , time+ , aeson+ , deriving-aeson+ , lens+ , template-haskell+ , servant ^>= 0.18+ , servant-docs+ , servant-multipart-api+ , safe-money+ , quickcheck-instances+ , QuickCheck++test-suite blockfrost-api-tests+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: Spec.hs+ other-modules: AmountSpec+ , APISpec+ , Cardano.Accounts+ , Cardano.Addresses+ , Cardano.Assets+ , Cardano.Epochs+ , Cardano.Ledger+ , Cardano.Metadata+ , Cardano.Network+ , Cardano.Pools+ , Cardano.Transactions+ , IPFS+ , NutLink+ build-depends: base >= 4.7 && < 5+ , blockfrost-api+ , aeson+ , bytestring+ , text+ , data-default+ , safe-money+ , raw-strings-qq+ , vector+ , hspec+ , tasty+ , tasty-hspec+ , tasty-hunit+ build-tool-depends:+ tasty-discover:tasty-discover+ default-language: Haskell2010++source-repository head+ type: git+ location: https://github.com/blockfrost/blockfrost-haskell
+ src/Blockfrost/API.hs view
@@ -0,0 +1,136 @@+-- | Blockfrost API definitions++{-# OPTIONS_HADDOCK hide #-}++module Blockfrost.API+ ( BlockfrostAPI (..)+ , BlockfrostV0API (..)+ , CommonAPI (..)+ , CardanoAPI (..)+ , IPFSAPI (..)+ , NutLinkAPI (..)+ , module Blockfrost.API.Cardano+ , api+ , api0+ , ServantBlockfrostAPI+ , BlockfrostAuth+ , Project (..)+ , Form (..)+ ) where++import Data.Proxy (Proxy (..))+import Servant.API+import Servant.API.Generic++import Blockfrost.API.Cardano+import Blockfrost.API.Common+import Blockfrost.API.IPFS+import Blockfrost.API.NutLink+import Blockfrost.Auth+import Blockfrost.Types+import Blockfrost.Util.Tag (Tag)++-- * API++-- ** Our custom auth++type BlockfrostAuth = ProjectAuth '[APIKeyInHeader "project_id"] Project++-- ** Generic API types++-- *** Toplevel++newtype BlockfrostAPI route =+ BlockfrostAPI+ {+ _apiV0 :: route+ :- "api"+ :> "v0"+ :> BlockfrostAuth+ :> ToServantApi BlockfrostV0API+ } deriving (Generic)++-- *** V0, requiring auth++data BlockfrostV0API route =+ BlockfrostV0API+ {+ _common :: route :- ToServantApi CommonAPI+ , _cardano :: route :- ToServantApi CardanoAPI+ , _ipfs :: route :- "ipfs" :> ToServantApi IPFSAPI+ , _nutLink :: route :- "nutlink" :> Tag "Nut.link" :> ToServantApi NutLinkAPI+ } deriving (Generic)++-- *** Services++data CardanoAPI route =+ CardanoAPI+ {+ _accounts+ :: route+ :- "accounts"+ :> Tag "Cardano » Accounts"+ :> ToServantApi AccountsAPI+ , _addresses+ :: route+ :- "addresses"+ :> Tag "Cardano » Addresses"+ :> ToServantApi AddressesAPI+ , _assets+ :: route+ :- "assets"+ :> Tag "Cardano » Assets"+ :> ToServantApi AssetsAPI+ , _blocks+ :: route+ :- "blocks"+ :> Tag "Cardano » Blocks"+ :> ToServantApi BlocksAPI+ , _epochs+ :: route+ :- "epochs"+ :> Tag "Cardano » Epochs"+ :> ToServantApi EpochsAPI+ , _ledger+ :: route+ :- "genesis"+ :> Tag "Cardano » Ledger"+ :> ToServantApi LedgerAPI+ , _metadata+ :: route+ :- "metadata"+ :> Tag "Cardano » Metadata"+ :> ToServantApi MetadataAPI+ , _network+ :: route+ :- "network"+ :> Tag "Cardano » Network"+ :> ToServantApi NetworkAPI+ , _pools+ :: route+ :- "pools"+ :> Tag "Cardano » Pools"+ :> ToServantApi PoolsAPI+ , _transactions+ :: route+ :- "txs"+ :> Tag "Cardano » Transactions"+ :> ToServantApi TransactionsAPI+ , _txSubmit+ :: route+ :- Summary "Submit a transaction"+ :> Description "Submit an already serialized transaction to the network."+ :> Tag "Cardano » Transactions"+ :> "tx"+ :> "submit"+ :> ReqBody '[CBOR] CBORString+ :> Post '[JSON] TxHash+ } deriving (Generic)++type ServantBlockfrostAPI = ToServantApi BlockfrostAPI++api :: Proxy (ToServantApi BlockfrostAPI)+api = genericApi (Proxy :: Proxy BlockfrostAPI)++api0 :: Proxy (ToServantApi BlockfrostV0API)+api0 = genericApi (Proxy :: Proxy BlockfrostV0API)
+ src/Blockfrost/API/Cardano.hs view
@@ -0,0 +1,27 @@+-- | Cardano services++{-# OPTIONS_HADDOCK hide #-}++module Blockfrost.API.Cardano+ ( module Blockfrost.API.Cardano.Accounts+ , module Blockfrost.API.Cardano.Addresses+ , module Blockfrost.API.Cardano.Assets+ , module Blockfrost.API.Cardano.Blocks+ , module Blockfrost.API.Cardano.Epochs+ , module Blockfrost.API.Cardano.Ledger+ , module Blockfrost.API.Cardano.Metadata+ , module Blockfrost.API.Cardano.Network+ , module Blockfrost.API.Cardano.Pools+ , module Blockfrost.API.Cardano.Transactions+ ) where++import Blockfrost.API.Cardano.Accounts+import Blockfrost.API.Cardano.Addresses+import Blockfrost.API.Cardano.Assets+import Blockfrost.API.Cardano.Blocks+import Blockfrost.API.Cardano.Epochs+import Blockfrost.API.Cardano.Ledger+import Blockfrost.API.Cardano.Metadata+import Blockfrost.API.Cardano.Network+import Blockfrost.API.Cardano.Pools+import Blockfrost.API.Cardano.Transactions
+ src/Blockfrost/API/Cardano/Accounts.hs view
@@ -0,0 +1,79 @@+-- | Accounts API endpoints++{-# OPTIONS_HADDOCK hide #-}++module Blockfrost.API.Cardano.Accounts+ where++import Servant.API+import Servant.API.Generic++import Blockfrost.Types.Cardano.Accounts+import Blockfrost.Types.Shared+import Blockfrost.Util.Pagination+import Blockfrost.Util.Sorting++data AccountsAPI route =+ AccountsAPI+ {+ _account+ :: route+ :- Summary "Specific account address"+ :> Description "Obtain information about a specific stake account."+ :> Capture "stake_address" Address+ :> Get '[JSON] AccountInfo+ , _accountRewards+ :: route+ :- Summary "Specific reward history"+ :> Description "Obtain information about the reward history of a specific account."+ :> Capture "stake_address" Address+ :> "rewards"+ :> Pagination+ :> Sorting+ :> Get '[JSON] [AccountReward]+ , _accountHistory+ :: route+ :- Summary "Account history"+ :> Description "Obtain information about the history of a specific account."+ :> Capture "stake_address" Address+ :> "history"+ :> Pagination+ :> Sorting+ :> Get '[JSON] [AccountHistory]+ , _accountDelegations+ :: route+ :- Summary "Account delegation history"+ :> Description "Obtain information about the delegation of a specific account."+ :> Capture "stake_address" Address+ :> "delegations"+ :> Pagination+ :> Sorting+ :> Get '[JSON] [AccountDelegation]+ , _accountRegistrations+ :: route+ :- Summary "Account registration history"+ :> Description "Obtain information about the registrations and deregistrations of a specific account."+ :> Capture "stake_address" Address+ :> "registrations"+ :> Pagination+ :> Sorting+ :> Get '[JSON] [AccountRegistration]+ , _accountWithdrawals+ :: route+ :- Summary "Account withdrawal history"+ :> Description "Obtain information about the withdrawals of a specific account."+ :> Capture "stake_address" Address+ :> "withdrawals"+ :> Pagination+ :> Sorting+ :> Get '[JSON] [AccountWithdrawal]+ , _accountMirs+ :: route+ :- Summary "Account MIR history"+ :> Description "Obtain information about the MIRs of a specific account."+ :> Capture "stake_address" Address+ :> "mirs"+ :> Pagination+ :> Sorting+ :> Get '[JSON] [AccountMir]+ } deriving (Generic)
+ src/Blockfrost/API/Cardano/Addresses.hs view
@@ -0,0 +1,52 @@+-- | Addresses API endpoints++{-# OPTIONS_HADDOCK hide #-}++module Blockfrost.API.Cardano.Addresses+ where++import Servant.API+import Servant.API.Generic++import Blockfrost.Types.Cardano.Addresses+import Blockfrost.Types.Shared+import Blockfrost.Util.Pagination+import Blockfrost.Util.Sorting++data AddressesAPI route =+ AddressesAPI+ {+ _addressInfo+ :: route+ :- Summary "Specific address"+ :> Description "Obtain information about a specific address."+ :> Capture "address" Address+ :> Get '[JSON] AddressInfo+ , _addressDetails+ :: route+ :- Summary "Address details"+ :> Description "Obtain details about an address."+ :> Capture "address" Address+ :> "total"+ :> Get '[JSON] AddressDetails+ , _addressUtxos+ :: route+ :- Summary "Address UTXOs"+ :> Description "UTXOs of the address."+ :> Capture "address" Address+ :> "utxos"+ :> Pagination+ :> Sorting+ :> Get '[JSON] [AddressUTXO]+ , _addressTransactions+ :: route+ :- Summary "Address transactions"+ :> Description "Transactions on the address."+ :> Capture "address" Address+ :> "transactions"+ :> Pagination+ :> Sorting+ :> QueryParam "from" BlockIndex+ :> QueryParam "to" BlockIndex+ :> Get '[JSON] [AddressTransaction]+ } deriving (Generic)
+ src/Blockfrost/API/Cardano/Assets.hs view
@@ -0,0 +1,68 @@+-- | Assets API endpoints++{-# OPTIONS_HADDOCK hide #-}++module Blockfrost.API.Cardano.Assets+ where++import Servant.API+import Servant.API.Generic++import Blockfrost.Types.Cardano.Assets+import Blockfrost.Types.Shared+import Blockfrost.Util.Pagination+import Blockfrost.Util.Sorting++data AssetsAPI route =+ AssetsAPI+ {+ _listAssets+ :: route+ :- Summary "Assets"+ :> Description "List of assets."+ :> Pagination+ :> Sorting+ :> Get '[JSON] [AssetInfo]+ , _assetDetails+ :: route+ :- Summary "Specific asset"+ :> Description "Information about a specific asset."+ :> Capture "asset" AssetId+ :> Get '[JSON] AssetDetails+ , _assetHistory+ :: route+ :- Summary "Asset history"+ :> Description "History of a specific asset."+ :> Capture "asset" AssetId+ :> "history"+ :> Pagination+ :> Sorting+ :> Get '[JSON] [AssetHistory]+ , _assetTransactions+ :: route+ :- Summary "Asset transactions"+ :> Description "List of a specific asset transactions"+ :> Capture "asset" AssetId+ :> "transactions"+ :> Pagination+ :> Sorting+ :> Get '[JSON] [AssetTransaction]+ , _assetAddresses+ :: route+ :- Summary "Asset addresses"+ :> Description "List of a addresses containing a specific asset"+ :> Capture "asset" AssetId+ :> "addresses"+ :> Pagination+ :> Sorting+ :> Get '[JSON] [AssetAddress]+ , _listAssetsPolicy+ :: route+ :- Summary "Assets of a specific policy"+ :> Description "List of asset minted under a specific policy."+ :> "policy"+ :> Capture "policy_id" PolicyId+ :> Pagination+ :> Sorting+ :> Get '[JSON] [AssetInfo]+ } deriving (Generic)
+ src/Blockfrost/API/Cardano/Blocks.hs view
@@ -0,0 +1,82 @@+-- | Blocks API endpoints++{-# OPTIONS_HADDOCK hide #-}++module Blockfrost.API.Cardano.Blocks+ where++import Servant.API+import Servant.API.Generic++import Blockfrost.Types.Cardano.Blocks+import Blockfrost.Types.Shared+import Blockfrost.Util.Pagination+import Blockfrost.Util.Sorting++data BlocksAPI route =+ BlocksAPI+ {+ _latest+ :: route+ :- Summary "Latest block"+ :> Description "Return the latest block available to the backends, \+ \also known as the tip of the blockchain."+ :> "latest"+ :> Get '[JSON] Block+ , _latestTxs+ :: route+ :- Summary "Latest block transactions"+ :> Description "Return the transactions within the latest block."+ :> "latest"+ :> "txs"+ :> Pagination+ :> Sorting+ :> Get '[JSON] [TxHash]+ , _block+ :: route+ :- Summary "Latest block transactions"+ :> Description "Return the transactions within the latest block."+ :> Capture "hash_or_number" (Either Integer BlockHash)+ :> Get '[JSON] Block+ , __blockSlot+ :: route+ :- Summary "Specific block in a slot"+ :> Description "Return the content of a requested block for a specific slot."+ :> "slot"+ :> Capture "slot_number" Slot+ :> Get '[JSON] Block+ , __blockEpochSlot+ :: route+ :- Summary "Specific block in a slot in an epoch"+ :> Description "Return the content of a requested block for a specific slot in an epoch."+ :> "epoch"+ :> Capture "epoch_number" Epoch+ :> "slot"+ :> Capture "slot_number" Slot+ :> Get '[JSON] Block+ , _blockNext+ :: route+ :- Summary "Listing of next blocks"+ :> Description "Return the list of blocks following a specific block."+ :> Capture "hash_or_number" (Either Integer BlockHash)+ :> "next"+ :> Pagination+ :> Get '[JSON] [Block]+ , _blockPrevious+ :: route+ :- Summary "Listing of preious blocks"+ :> Description "Return the list of blocks preceeding a specific block."+ :> Capture "hash_or_number" (Either Integer BlockHash)+ :> "previous"+ :> Pagination+ :> Get '[JSON] [Block]+ , _blockTxs+ :: route+ :- Summary "Block transactions"+ :> Description "Return the transactions within the block."+ :> Capture "hash_or_number" (Either Integer BlockHash)+ :> "txs"+ :> Pagination+ :> Sorting+ :> Get '[JSON] [TxHash]+ } deriving (Generic)
+ src/Blockfrost/API/Cardano/Epochs.hs view
@@ -0,0 +1,97 @@+-- | Epochs API endpoints++{-# OPTIONS_HADDOCK hide #-}++module Blockfrost.API.Cardano.Epochs+ where++import Servant.API+import Servant.API.Generic++import Blockfrost.Types.Cardano.Epochs+import Blockfrost.Types.Shared+import Blockfrost.Util.Pagination+import Blockfrost.Util.Sorting++data EpochsAPI route =+ EpochsAPI+ {+ _latestEpoch+ :: route+ :- Summary "Latest epoch"+ :> Description "Return the information about the latest, therefore current, epoch."+ :> "latest"+ :> Get '[JSON] EpochInfo+ , _latestEpochProtocolParams+ :: route+ :- Summary "Latest epoch protocol parameters"+ :> Description "Return the protocol parameters for the latest epoch."+ :> "latest"+ :> "parameters"+ :> Get '[JSON] ProtocolParams+ , _getEpoch+ :: route+ :- Summary "Specific epoch"+ :> Description "Return the content of the requested epoch."+ :> Capture "epoch_number" Epoch+ :> Get '[JSON] EpochInfo+ , _getNextEpochs+ :: route+ :- Summary "List of next epochs"+ :> Description "Return the list of epochs following a specific epoch."+ :> Capture "epoch_number" Epoch+ :> "next"+ :> Pagination+ :> Get '[JSON] [EpochInfo]+ , _getPreviousEpochs+ :: route+ :- Summary "List of previous epochs"+ :> Description "Return the list of epochs preceding a specific epoch."+ :> Capture "epoch_number" Epoch+ :> "previous"+ :> Pagination+ :> Get '[JSON] [EpochInfo]+ , _getEpochStake+ :: route+ :- Summary "Stake distribution"+ :> Description "Return the active stake distribution for the specified epoch."+ :> Capture "epoch_number" Epoch+ :> "stakes"+ :> Pagination+ :> Get '[JSON] [StakeDistribution]+ , _getEpochStakeByPool+ :: route+ :- Summary "Stake distribution by pool"+ :> Description "Return the active stake distribution for the epoch specified by stake pool."+ :> Capture "epoch_number" Epoch+ :> "stakes"+ :> Capture "pool_id" PoolId+ :> Pagination+ :> Get '[JSON] [StakeDistribution]+ , _getEpochBlocks+ :: route+ :- Summary "Block distribution"+ :> Description "Return the blocks minted for the epoch specified."+ :> Capture "epoch_number" Epoch+ :> "blocks"+ :> Pagination+ :> Sorting+ :> Get '[JSON] [BlockHash]+ , _getEpochBlocksByPool+ :: route+ :- Summary "Block distribution"+ :> Description "Return the block minted for the epoch specified by stake pool."+ :> Capture "epoch_number" Epoch+ :> "blocks"+ :> Capture "pool_id" PoolId+ :> Pagination+ :> Sorting+ :> Get '[JSON] [BlockHash]+ , _getEpochProtocolParams+ :: route+ :- Summary "Protocol parameters"+ :> Description "Return the protocol parameters for the specified epoch."+ :> Capture "epoch_number" Epoch+ :> "parameters"+ :> Get '[JSON] ProtocolParams+ } deriving (Generic)
+ src/Blockfrost/API/Cardano/Ledger.hs view
@@ -0,0 +1,21 @@+-- | Cardano Ledger endpoints++{-# OPTIONS_HADDOCK hide #-}++module Blockfrost.API.Cardano.Ledger+ where++import Servant.API+import Servant.API.Generic++import Blockfrost.Types.Cardano.Genesis++data LedgerAPI route =+ LedgerAPI+ {+ _genesis+ :: route+ :- Summary "Blockchain genesis"+ :> Description "Return the information about blockchain genesis."+ :> Get '[JSON] Genesis+ } deriving (Generic)
+ src/Blockfrost/API/Cardano/Metadata.hs view
@@ -0,0 +1,53 @@+-- | Cardano Metadata endpoints++{-# OPTIONS_HADDOCK hide #-}++module Blockfrost.API.Cardano.Metadata+ where++import Data.Text (Text)+import Servant.API+import Servant.API.Generic+import Servant.Docs (DocCapture (..), ToCapture (..))++import Blockfrost.Types.Cardano.Metadata+import Blockfrost.Util.Pagination+import Blockfrost.Util.Sorting++data MetadataAPI route =+ MetadataAPI+ {+ _txMetadataLabels+ :: route+ :- Summary "Transaction metadata labels"+ :> Description "List of all used transaction metadata labels."+ :> "txs"+ :> "labels"+ :> Pagination+ :> Sorting+ :> Get '[JSON] [TxMeta]+ , _txMetadataByLabelJSON+ :: route+ :- Summary "Transaction metadata content in JSON"+ :> Description "Transaction metadata per label."+ :> "txs"+ :> "labels"+ :> Capture "label" Text+ :> Pagination+ :> Sorting+ :> Get '[JSON] [TxMetaJSON]+ , _txMetadataByLabelCBOR+ :: route+ :- Summary "Transaction metadata content in CBOR"+ :> Description "Transaction metadata per label."+ :> "txs"+ :> "labels"+ :> Capture "label" Text+ :> "cbor"+ :> Pagination+ :> Sorting+ :> Get '[JSON] [TxMetaCBOR]+ } deriving (Generic)++instance ToCapture (Capture "label" Text) where+ toCapture _ = DocCapture "label" "Metadata label"
+ src/Blockfrost/API/Cardano/Network.hs view
@@ -0,0 +1,21 @@+-- | Cardano Network endpoints++{-# OPTIONS_HADDOCK hide #-}++module Blockfrost.API.Cardano.Network+ where++import Servant.API+import Servant.API.Generic++import Blockfrost.Types.Cardano.Network++data NetworkAPI route =+ NetworkAPI+ {+ _networkInfo+ :: route+ :- Summary "Network information"+ :> Description "Return detailed network information."+ :> Get '[JSON] Network+ } deriving (Generic)
+ src/Blockfrost/API/Cardano/Pools.hs view
@@ -0,0 +1,98 @@+-- | Cardano Pools endpoints++{-# OPTIONS_HADDOCK hide #-}++module Blockfrost.API.Cardano.Pools+ where++import Servant.API+import Servant.API.Generic++import Blockfrost.Types.Cardano.Pools+import Blockfrost.Types.Shared+import Blockfrost.Util.Pagination+import Blockfrost.Util.Sorting++data PoolsAPI route =+ PoolsAPI+ {+ _listPools+ :: route+ :- Summary "List of stake pools"+ :> Description "List of registered stake pools."+ :> Pagination+ :> Sorting+ :> Get '[JSON] [PoolId]+ , _listRetiredPools+ :: route+ :- Summary "List of retired stake pools"+ :> Description "List of already retired stake pools."+ :> "retired"+ :> Pagination+ :> Sorting+ :> Get '[JSON] [PoolEpoch]+ , _listRetiringPools+ :: route+ :- Summary "List of retiring stake pools"+ :> Description "List of stake pools retiring in the upcoming epochs"+ :> "retiring"+ :> Pagination+ :> Sorting+ :> Get '[JSON] [PoolEpoch]+ , _getPool+ :: route+ :- Summary "Specific stake pool"+ :> Description "Pool information."+ :> Capture "pool_id" PoolId+ :> Get '[JSON] PoolInfo+ , _getPoolHistory+ :: route+ :- Summary "Stake pool history"+ :> Description "History of stake pool parameters over epochs."+ :> Capture "pool_id" PoolId+ :> "history"+ :> Pagination+ :> Sorting+ :> Get '[JSON] [PoolHistory]+ , _getPoolMetadata+ :: route+ :- Summary "Stake pool metadata"+ :> Description "Stake pool registration metadata."+ :> Capture "pool_id" PoolId+ :> "metadata"+ :> Get '[JSON] (Maybe PoolMetadata)+ , _getPoolRelays+ :: route+ :- Summary "Stake pool relays"+ :> Description "Relays of a stake pool."+ :> Capture "pool_id" PoolId+ :> "relays"+ :> Get '[JSON] [PoolRelay]+ , _getPoolDelegators+ :: route+ :- Summary "Stake pool delegators"+ :> Description "List of current stake pools delegators."+ :> Capture "pool_id" PoolId+ :> "delegators"+ :> Pagination+ :> Sorting+ :> Get '[JSON] [PoolDelegator]+ , _getPoolBlocks+ :: route+ :- Summary "Stake pool blocks"+ :> Description "List of stake pool blocks."+ :> Capture "pool_id" PoolId+ :> "blocks"+ :> Pagination+ :> Sorting+ :> Get '[JSON] [BlockHash]+ , _getPoolUpdates+ :: route+ :- Summary "Stake pool updates"+ :> Description "List of certificate updates to the stake pool."+ :> Capture "pool_id" PoolId+ :> "updates"+ :> Pagination+ :> Sorting+ :> Get '[JSON] [PoolUpdate]+ } deriving (Generic)
+ src/Blockfrost/API/Cardano/Transactions.hs view
@@ -0,0 +1,87 @@+-- | Cardano Pools endpoints++{-# OPTIONS_HADDOCK hide #-}++module Blockfrost.API.Cardano.Transactions+ where++import Servant.API+import Servant.API.Generic++import Blockfrost.Types.Cardano.Transactions+import Blockfrost.Types.Shared++data TransactionsAPI route =+ TransactionsAPI+ {+ _tx+ :: route+ :- Summary "Specific transaction"+ :> Description "Return content of the requested transaction."+ :> Capture "hash" TxHash+ :> Get '[JSON] Transaction+ , _txUtxos+ :: route+ :- Summary "Transaction UTXOs"+ :> Description "Return the inputs and UTXOs of the specific transaction."+ :> Capture "hash" TxHash+ :> "utxos"+ :> Get '[JSON] TransactionUtxos+ , _txStakes+ :: route+ :- Summary "Transaction stake addresses certificates "+ :> Description "Obtain information about (de)registration of stake addresses within a transaction."+ :> Capture "hash" TxHash+ :> "stakes"+ :> Get '[JSON] [TransactionStake]+ , _txDelegations+ :: route+ :- Summary "Transaction delegation certificates"+ :> Description "Obtain information about delegation certificates of a specific transaction."+ :> Capture "hash" TxHash+ :> "delegations"+ :> Get '[JSON] [TransactionDelegation]+ , _txWithdrawals+ :: route+ :- Summary "Transaction withdrawal"+ :> Description "Obtain information about withdrawals of a specific transaction."+ :> Capture "hash" TxHash+ :> "withdrawals"+ :> Get '[JSON] [TransactionWithdrawal]+ , _txMirs+ :: route+ :- Summary "Transaction MIRs"+ :> Description "Obtain information about Move Instantaneous Rewards (MIRs) of a specific transaction."+ :> Capture "hash" TxHash+ :> "mirs"+ :> Get '[JSON] [TransactionMir]+ , _txPoolUpdates+ :: route+ :- Summary "Transaction stake pool registration and update certificates"+ :> Description "Obtain information about stake pool registration and update certificates of a specific transaction."+ :> Capture "hash" TxHash+ :> "pool_updates"+ :> Get '[JSON] [TransactionPoolUpdate]+ , _txPoolRetiring+ :: route+ :- Summary "Transaction stake pool retirement certificates"+ :> Description "Obtain information about stake pool retirements within a specific transaction."+ :> Capture "hash" TxHash+ :> "pool_retires"+ :> Get '[JSON] [TransactionPoolRetiring]+ , _txMetadataJSON+ :: route+ :- Summary "Transaction metadata"+ :> Description "Obtain the transaction metadata."+ :> Capture "hash" TxHash+ :> "metadata"+ :> Get '[JSON] [TransactionMetaJSON]+ , _txMetadataCBOR+ :: route+ :- Summary "Transaction metadata in CBOR"+ :> Description "Obtain the transaction metadata in CBOR."+ :> Capture "hash" TxHash+ :> "metadata"+ :> "cbor"+ :> Get '[JSON] [TransactionMetaCBOR]+ } deriving (Generic)
+ src/Blockfrost/API/Common.hs view
@@ -0,0 +1,56 @@+-- | Common services++{-# OPTIONS_HADDOCK hide #-}++module Blockfrost.API.Common+ where++import Blockfrost.Types+import Blockfrost.Util.Tag (Tag)+import Data.Text (Text)+import Servant.API+import Servant.API.Generic++data CommonAPI route =+ CommonAPI+ {+ _getRoot+ :: route+ :- Summary "Root endpoint"+ :> Description "Root endpoint has no other function than to point end users to documentation."+ :> Tag "Health"+ :> Get '[JSON] URLVersion+ , _getHealth+ :: route+ :- Summary "Backend health status"+ :> Description "Return backend status as a boolean. \+ \Your application should handle situations when backend for the given chain is unavailable."+ :> Tag "Health"+ :> "health"+ :> Get '[JSON] Healthy+ , _getClock+ :: route+ :- Summary "Current backend time"+ :> Description "This endpoint provides the current UNIX time. \+ \Your application might use this to verify if \+ \the client clock is not out of sync."+ :> Tag "Health"+ :> "health"+ :> "clock"+ :> Get '[JSON] ServerTime+ , _metrics+ :: route+ :- Summary "Blockfrost usage metrics"+ :> Description "History of your Blockfrost usage metrics in the past 30 days."+ :> Tag "Metrics"+ :> "metrics"+ :> Get '[JSON] [Metric]+ , _metricsEndpoints+ :: route+ :- Summary "Blockfrost endpoint usage metrics"+ :> Description "History of your Blockfrost usage metrics per endpoint in the past 30 days."+ :> Tag "Metrics"+ :> "metrics"+ :> "endpoints"+ :> Get '[JSON] [(Text, Metric)]+ } deriving (Generic)
+ src/Blockfrost/API/IPFS.hs view
@@ -0,0 +1,84 @@+-- | IPFS services++{-# OPTIONS_HADDOCK hide #-}++module Blockfrost.API.IPFS+ where++import Blockfrost.Types+import Blockfrost.Util.Pagination+import Blockfrost.Util.Sorting+import Blockfrost.Util.Tag (Tag)+import Data.Text (Text)+import Servant.API+import Servant.API.Generic+import Servant.Docs (DocCapture (..), ToCapture (..))+import Servant.Multipart.API++data IPFSAPI route =+ IPFSAPI+ {+ _add+ :: route+ :- Summary "Add a file or directory to IPFS"+ :> Description "You need to `/ipfs/pin/add` an object to avoid it being garbage collected. \+ \This usage is being counted in your user account quota."+ :> Tag "IPFS » Add"+ :> "add"+ :> MultipartForm Tmp Form+ :> Post '[JSON] IPFSAdd+ , _gateway+ :: route+ :- Summary "Relay to an IPFS gateway"+ :> Description "Retrieve an object from the IFPS gateway. \+ \(Useful if you do not want to rely on a public gateway, such as ``ipfs.blockfrost.dev`)."+ :> Tag "IPFS » Gateway"+ :> "gateway"+ :> Capture "IPFS_path" Text+ :> Get '[PlainText, OctetStream] IPFSData+ , _pin+ :: route+ :- Summary "Pin an object"+ :> Description "Pinned objects are counted in your user storage quota."+ :> Tag "IPFS » Pins"+ :> "pin"+ :> "add"+ :> Capture "IPFS_path" Text+ :> Post '[JSON] IPFSPinChange+ , _listPins+ :: route+ :- Summary "List pinned objects"+ :> Description "List objects pinned to local storage."+ :> Tag "IPFS » Pins"+ :> "pin"+ :> "list"+ :> Pagination+ :> Sorting+ :> Get '[JSON] [IPFSPin]+ , _getPin+ :: route+ :- Summary "Get pinned object details"+ :> Description "Obtain inormation about specific pinned object."+ :> Tag "IPFS » Pins"+ :> "pin"+ :> "list"+ :> Capture "IPFS_path" Text+ :> Get '[JSON] IPFSPin+ , _removePin+ :: route+ :- Summary "Remove pinned object from local storage"+ :> Description "Remove pinned object from local storage"+ :> Tag "IPFS » Pins"+ :> "pin"+ :> "remove"+ :> Capture "IPFS_path" Text+ :> Post '[JSON] IPFSPinChange+ } deriving (Generic)++instance ToCapture (Capture "IPFS_path" Text) where+ toCapture _ = DocCapture "IPFS_path" "Path to the IPFS object"++data Form = Form {+ formFileName :: Text+ , formFilePath :: FilePath+ }
+ src/Blockfrost/API/NutLink.hs view
@@ -0,0 +1,56 @@+-- | Nut.link services++{-# OPTIONS_HADDOCK hide #-}++module Blockfrost.API.NutLink+ where++import Blockfrost.Types+import Blockfrost.Util.Pagination+import Blockfrost.Util.Sorting+import Data.Text (Text)+import Servant.API+import Servant.API.Generic+import Servant.Docs (DocCapture (..), ToCapture (..))++data NutLinkAPI route =+ NutLinkAPI+ {+ _address+ :: route+ :- Summary "List metadata about specific address"+ :> Description "List metadata about specific address"+ :> Capture "address" Address+ :> Get '[JSON] NutlinkAddress+ , _listAddressTickers+ :: route+ :- Summary "List tickers for a specific metadata oracle"+ :> Description "List tickers for a specific metadata oracle"+ :> Capture "address" Address+ :> "tickers"+ :> Pagination+ :> Sorting+ :> Get '[JSON] [NutlinkAddressTicker]+ , _addressTickers+ :: route+ :- Summary "List of records of a specific ticker"+ :> Description "List of records of a specific ticker"+ :> Capture "address" Address+ :> "tickers"+ :> Capture "ticker" Text+ :> Pagination+ :> Sorting+ :> Get '[JSON] [NutlinkTicker]+ , _tickers+ :: route+ :- Summary "List of records of a specific ticker"+ :> Description "List of records of a specific ticker"+ :> "tickers"+ :> Capture "ticker" Text+ :> Pagination+ :> Sorting+ :> Get '[JSON] [(Address, NutlinkTicker)]+ } deriving (Generic)++instance ToCapture (Capture "ticker" Text) where+ toCapture _ = DocCapture "ticker" "Ticker for the pool record"
+ src/Blockfrost/Auth.hs view
@@ -0,0 +1,74 @@+-- | Blockfrost authentication schemes+{-# LANGUAGE PolyKinds #-}++module Blockfrost.Auth+ ( APIKeyInHeader+ , APIKeyInHeaderSettings (..)+ , Env (..)+ , Project (..)+ , ProjectAuth+ , mkProject+ , mkProjectEnv+ ) where++import Data.Kind (Type)+import Data.Proxy (Proxy (..))+import Data.Text as Text+import GHC.Generics (Generic)+import GHC.TypeLits+import Servant.API (HasLink (..), (:>))++import qualified Data.Text++import Data.String (IsString (..))++import Blockfrost.Env++-- * Authentication result and clients token++data Project = Project {+ projectEnv :: Env+ , projectId :: Text+ }+ deriving (Eq, Show, Generic)++-- | Parse @Project@ from @Text@+mkProject' :: Text -> Either Text Project+mkProject' t =+ let st = Data.Text.strip t+ tEnv = Data.Text.dropEnd 32 st+ token = Data.Text.drop (Data.Text.length tEnv) st+ in Project <$> parseEnv tEnv <*> pure token++-- | Parse @Project@ from @Text@ or fail with error+mkProject :: Text -> Project+mkProject = either (error . Data.Text.unpack) id . mkProject'++-- | @Project@ constructor+mkProjectEnv :: Env -> Text -> Project+mkProjectEnv = Project++instance IsString Project where+ fromString = mkProject . Data.Text.pack++-- * Common++-- | The type of Auth scheme.+data APIKeyInHeader (headerName :: Symbol)++-- | Auth scheme settings+-- Needs IO action to verify passed in token and maybe return Project+newtype APIKeyInHeaderSettings =+ APIKeyInHeaderSettings+ { apiKeySettingsQueryProject :: Text -> IO (Maybe Project)+ }++-- * Custom Auth+-- we use custom ProjectAuth instead of Auth from servant-auth+-- to get rid of superfluous Contexts like JWTSettings and CookiesSettings++data ProjectAuth (auths :: [Type]) val++instance HasLink sub => HasLink (ProjectAuth (tag :: [Type]) value :> sub) where+ type MkLink (ProjectAuth (tag :: [Type]) value :> sub) r = MkLink sub r+ toLink toA _ = toLink toA (Proxy @sub)
+ src/Blockfrost/Env.hs view
@@ -0,0 +1,32 @@+-- | Blockfrost environments++module Blockfrost.Env+ ( Env (..)+ , parseEnv+ ) where++import Data.Text (Text)+import qualified Data.Text+import GHC.Generics (Generic)+import qualified Text.Read++-- | Blockfrost environments+--+-- Corresponds to Network when creating a Blockfrost project.+-- Each environment has separate token.+data Env =+ Alonzo+ | Ipfs+ | Mainnet+ | Testnet+ | Localhost+ deriving (Eq, Read, Show, Ord, Generic)++-- | Try parsing @Env@ from @Text@+parseEnv :: Text -> Either Text Env+parseEnv tEnv = case Text.Read.readMaybe (Data.Text.unpack $ Data.Text.toTitle tEnv) of+ Just env -> pure env+ Nothing ->+ Left+ $ "Unknown environment: `" <> tEnv <> "`"+ <> " expecting one of `alonzo`, `ipfs`, `mainnet`, `testnet`, `localhost`"
+ src/Blockfrost/Lens.hs view
@@ -0,0 +1,86 @@+-- | Lenses for Blockfrost types++{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE UndecidableInstances #-}++module Blockfrost.Lens+ where++import Blockfrost.Types+import Blockfrost.Util.LensRules+import Control.Lens++makeLensesWith blockfrostFieldRules ''URLVersion++makeFields ''AccountInfo+makeFields ''AccountReward+makeFields ''AccountHistory+makeFields ''AccountDelegation+makeFields ''AccountRegistration+makeFields ''AccountWithdrawal+makeFields ''AccountMir++makeLensesWith blockfrostFieldRules ''AddressInfo+makeFields ''AddressDetails+makeFields ''AddressUTXO+makeFields ''AddressTransaction++makeFields ''AssetInfo+makeFields ''AssetDetails+makeFields ''AssetOnChainMetadata+makeFields ''AssetMetadata+makeFields ''AssetHistory+makeFields ''AssetTransaction+makeFields ''AssetAddress++makeFields ''Block++makeFields ''EpochInfo+makeFields ''ProtocolParams+makeFields ''StakeDistribution+makeFields ''PoolStakeDistribution++makeFields ''Genesis++makeFields ''TxMeta+makeFields ''TxMetaJSON+makeFields ''TxMetaCBOR++makeFields ''Network+makeFields ''NetworkSupply+makeFields ''NetworkStake++makeFields ''PoolEpoch+makeFields ''PoolInfo+makeFields ''PoolHistory+makeFields ''PoolMetadata+makeFields ''PoolRelay+makeFields ''PoolDelegator+makeFields ''PoolUpdate+makeFields ''PoolRegistrationAction++makeFields ''Transaction+makeFields ''TransactionUtxos+makeFields ''UtxoInput+makeFields ''UtxoOutput+makeFields ''TransactionStake+makeFields ''TransactionDelegation+makeFields ''TransactionWithdrawal+makeFields ''TransactionMir+makeFields ''TransactionPoolUpdate+makeFields ''PoolUpdateMetadata+makeFields ''TransactionPoolRetiring+makeFields ''TransactionMetaJSON+makeFields ''TransactionMetaCBOR++makeLensesWith blockfrostFieldRules ''IPFSAdd+makeLensesWith blockfrostFieldRules ''IPFSPinChange+makeLensesWith blockfrostFieldRules ''IPFSPin++makeFields ''NutlinkAddress+makeFields ''NutlinkAddressTicker+makeFields ''NutlinkTicker++-- * Shared+makeFields ''BlockIndex+makePrisms ''Amount
+ src/Blockfrost/Types.hs view
@@ -0,0 +1,17 @@+-- | Blockfrost types++module Blockfrost.Types+ ( module Blockfrost.Types.ApiError+ , module Blockfrost.Types.Cardano+ , module Blockfrost.Types.Common+ , module Blockfrost.Types.IPFS+ , module Blockfrost.Types.NutLink+ , module Blockfrost.Types.Shared+ ) where++import Blockfrost.Types.ApiError+import Blockfrost.Types.Cardano+import Blockfrost.Types.Common+import Blockfrost.Types.IPFS+import Blockfrost.Types.NutLink+import Blockfrost.Types.Shared
+ src/Blockfrost/Types/ApiError.hs view
@@ -0,0 +1,35 @@+-- | Internal API error type++{-# LANGUAGE RecordWildCards #-}++{-# OPTIONS_HADDOCK hide #-}++module Blockfrost.Types.ApiError+ ( ApiError (..)+ ) where++import Data.Aeson+import Data.Text (Text)+import GHC.Generics++-- | Fancy JSON error returned+-- by the server+data ApiError = ApiError+ { apiError :: Text+ , apiErrorMessage :: Text+ , apiErrorCode :: Int+ } deriving (Eq, Show, Generic)++instance ToJSON ApiError where+ toJSON ApiError{..} =+ object [ "error" .= toJSON apiError+ , "message" .= toJSON apiErrorMessage+ , "status_code" .= toJSON apiErrorCode+ ]++instance FromJSON ApiError where+ parseJSON = withObject "error" $ \o -> do+ apiError <- o .: "error"+ apiErrorMessage <- o .: "message"+ apiErrorCode <- o .: "status_code"+ pure $ ApiError {..}
+ src/Blockfrost/Types/Cardano.hs view
@@ -0,0 +1,25 @@+-- | Types of Cardano++module Blockfrost.Types.Cardano+ ( module Blockfrost.Types.Cardano.Accounts+ , module Blockfrost.Types.Cardano.Addresses+ , module Blockfrost.Types.Cardano.Assets+ , module Blockfrost.Types.Cardano.Blocks+ , module Blockfrost.Types.Cardano.Epochs+ , module Blockfrost.Types.Cardano.Genesis+ , module Blockfrost.Types.Cardano.Metadata+ , module Blockfrost.Types.Cardano.Network+ , module Blockfrost.Types.Cardano.Pools+ , module Blockfrost.Types.Cardano.Transactions+ ) where++import Blockfrost.Types.Cardano.Accounts+import Blockfrost.Types.Cardano.Addresses+import Blockfrost.Types.Cardano.Assets+import Blockfrost.Types.Cardano.Blocks+import Blockfrost.Types.Cardano.Epochs+import Blockfrost.Types.Cardano.Genesis+import Blockfrost.Types.Cardano.Metadata+import Blockfrost.Types.Cardano.Network+import Blockfrost.Types.Cardano.Pools+import Blockfrost.Types.Cardano.Transactions
+ src/Blockfrost/Types/Cardano/Accounts.hs view
@@ -0,0 +1,204 @@+-- | Responses for Cardano accounts queries++module Blockfrost.Types.Cardano.Accounts+ ( AccountInfo (..)+ , AccountReward (..)+ , AccountHistory (..)+ , AccountDelegation (..)+ , AccountRegistration (..)+ , AccountRegistrationAction (..)+ , AccountWithdrawal (..)+ , AccountMir (..)+ ) where++import Blockfrost.Types.Shared+import Deriving.Aeson+import Servant.Docs (ToSample (..), samples, singleSample)++-- | Information about an account, identified by its stake address+data AccountInfo = AccountInfo+ { _accountInfoStakeAddress :: Address -- ^ Bech32 stake address+ , _accountInfoActive :: Bool -- ^ Registration state of an account+ , _accountInfoActiveEpoch :: Integer -- ^ Epoch of the most recent action - registration or deregistration+ , _accountInfoControlledAmount :: Lovelaces -- ^ Balance of the account in Lovelaces+ , _accountInfoRewardsSum :: Lovelaces -- ^ Sum of all funds rewards for the account in the Lovelaces+ , _accountInfoWithdrawalsSum :: Lovelaces -- ^ Sum of all the withdrawals for the account in the Lovelaces+ , _accountInfoReservesSum :: Lovelaces -- ^ Sum of all funds from reserves for the account in the Lovelaces+ , _accountInfoTreasurySum :: Lovelaces -- ^ Sum of all funds from treasury for the account in the Lovelaces+ , _accountInfoWithdrawableAmount :: Lovelaces -- ^ Sum of available rewards that haven't been withdrawn yet for the account in the Lovelaces+ , _accountInfoPoolId :: Maybe PoolId -- ^ Bech32 pool ID that owns the account+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_accountInfo", CamelToSnake]] AccountInfo++instance ToSample AccountInfo where+ toSamples = pure $ singleSample $ AccountInfo+ { _accountInfoStakeAddress = "stake1ux3g2c9dx2nhhehyrezyxpkstartcqmu9hk63qgfkccw5rqttygt7"+ , _accountInfoActive = True+ , _accountInfoActiveEpoch = 412+ , _accountInfoControlledAmount = 619154618165+ , _accountInfoRewardsSum = 319154618165+ , _accountInfoWithdrawalsSum = 12125369253+ , _accountInfoReservesSum = 319154618165+ , _accountInfoTreasurySum = 12000000+ , _accountInfoWithdrawableAmount = 319154618165+ , _accountInfoPoolId = pure "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ }++-- | Reward received by an account+data AccountReward = AccountReward+ { _accountRewardEpoch :: Epoch -- ^ Epoch of the associated reward+ , _accountRewardAmount :: Lovelaces -- ^ Rewards for given epoch in Lovelaces+ , _accountRewardPoolId :: PoolId -- ^ Bech32 pool ID being delegated to+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_accountReward", CamelToSnake]] AccountReward++instance ToSample AccountReward where+ toSamples = pure $ samples+ [ AccountReward+ { _accountRewardEpoch = 214+ , _accountRewardAmount = 1395265+ , _accountRewardPoolId = "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ }+ , AccountReward+ { _accountRewardEpoch = 215+ , _accountRewardAmount = 58632+ , _accountRewardPoolId = "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ }+ , AccountReward+ { _accountRewardEpoch = 216+ , _accountRewardAmount = 0+ , _accountRewardPoolId = "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ }+ , AccountReward+ { _accountRewardEpoch = 217+ , _accountRewardAmount = 1395265+ , _accountRewardPoolId = "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ }+ ]++-- | History of accounts stake delegation+data AccountHistory = AccountHistory+ { _accountHistoryActiveEpoch :: Integer -- ^ Epoch in which the stake was active+ , _accountHistoryAmount :: Lovelaces -- ^ Stake amount in Lovelaces+ , _accountHistoryPoolId :: PoolId -- ^ Bech32 ID of pool being delegated to+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_accountHistory", CamelToSnake]] AccountHistory++instance ToSample AccountHistory where+ toSamples = pure $ samples+ [ AccountHistory+ { _accountHistoryActiveEpoch = 260+ , _accountHistoryAmount = 1395265+ , _accountHistoryPoolId = "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ }+ , AccountHistory+ { _accountHistoryActiveEpoch = 211+ , _accountHistoryAmount = 22695385+ , _accountHistoryPoolId = "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ }+ ]++-- | Account delegations and associated transaction IDs+data AccountDelegation = AccountDelegation+ { _accountDelegationActiveEpoch :: Epoch -- ^ Epoch in which the delegation becomes active+ , _accountDelegationTxHash :: TxHash -- ^ Hash of the transaction containing the delegation+ , _accountDelegationAmount :: Lovelaces -- ^ Rewards for given epoch in Lovelaces+ , _accountDelegationPoolId :: PoolId -- ^ Bech32 ID of pool being delegated to+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_accountDelegation", CamelToSnake]] AccountDelegation++instance ToSample AccountDelegation where+ toSamples = pure $ samples+ [ AccountDelegation+ { _accountDelegationActiveEpoch = 210+ , _accountDelegationTxHash = "2dd15e0ef6e6a17841cb9541c27724072ce4d4b79b91e58432fbaa32d9572531"+ , _accountDelegationAmount = 12695385+ , _accountDelegationPoolId = "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ }+ , AccountDelegation+ { _accountDelegationActiveEpoch = 242+ , _accountDelegationTxHash = "1a0570af966fb355a7160e4f82d5a80b8681b7955f5d44bec0dde628516157f0"+ , _accountDelegationAmount = 12691385+ , _accountDelegationPoolId = "pool1kchver88u3kygsak8wgll7htr8uxn5v35lfrsyy842nkscrzyvj"+ }+ ]++-- | Registration action+data AccountRegistrationAction = Registered | Deregistered+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[ConstructorTagModifier '[ToLower]] AccountRegistrationAction++instance ToSample AccountRegistrationAction where+ toSamples = pure $ samples [ Registered, Deregistered ]++-- | Account (de)registration+data AccountRegistration = AccountRegistration+ { _accountRegistrationAction :: AccountRegistrationAction -- ^ Action in the certificate+ , _accountRegistrationTxHash :: TxHash -- ^ Hash of the transaction containing the (de)registration certificate+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_accountRegistration", CamelToSnake]] AccountRegistration++instance ToSample AccountRegistration where+ toSamples = pure $ samples+ [ AccountRegistration+ { _accountRegistrationAction = Registered+ , _accountRegistrationTxHash = "2dd15e0ef6e6a17841cb9541c27724072ce4d4b79b91e58432fbaa32d9572531"+ }+ , AccountRegistration+ { _accountRegistrationAction = Deregistered+ , _accountRegistrationTxHash = "1a0570af966fb355a7160e4f82d5a80b8681b7955f5d44bec0dde628516157f0"+ }+ ]++-- | Withdrawal from an account+data AccountWithdrawal = AccountWithdrawal+ { _accountWithdrawalAmount :: Lovelaces -- ^ Withdrawal amount in Lovelaces+ , _accountWithdrawalTxHash :: TxHash -- ^ Hash of the transaction containing the withdrawal+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_accountWithdrawal", CamelToSnake]] AccountWithdrawal++instance ToSample AccountWithdrawal where+ toSamples = pure $ samples+ [ AccountWithdrawal+ { _accountWithdrawalAmount = 454541212442+ , _accountWithdrawalTxHash = "48a9625c841eea0dd2bb6cf551eabe6523b7290c9ce34be74eedef2dd8f7ecc5"+ }+ , AccountWithdrawal+ { _accountWithdrawalAmount = 97846969+ , _accountWithdrawalTxHash = "4230b0cbccf6f449f0847d8ad1d634a7a49df60d8c142bb8cc2dbc8ca03d9e34"+ }+ ]++-- | Account MIR (Move Instantaneous Reward)+data AccountMir = AccountMir+ { _accountMirAmount :: Lovelaces -- ^ MIR amount in Lovelaces+ , _accountMirTxHash :: TxHash -- ^ Hash of the transaction containing the MIR+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_accountMir", CamelToSnake]] AccountMir++instance ToSample AccountMir where+ toSamples = pure $ samples+ [ AccountMir+ { _accountMirAmount = 6202170+ , _accountMirTxHash = "2dd15e0ef6e6a17841cb9541c27724072ce4d4b79b91e58432fbaa32d9572531"+ }+ , AccountMir+ { _accountMirAmount = 1202170+ , _accountMirTxHash = "1dd15e0ef6e6a17841cb9541c27724072ce4d4b79b91e58432fbaa32d9572531"+ }+ ]
+ src/Blockfrost/Types/Cardano/Addresses.hs view
@@ -0,0 +1,143 @@+-- | Responses for Cardano address queries++module Blockfrost.Types.Cardano.Addresses+ ( AddressInfo (..)+ , AddressType (..)+ , AddressDetails (..)+ , AddressUTXO (..)+ , AddressTransaction (..)+ ) where++import Blockfrost.Types.Shared+import Deriving.Aeson+import qualified Money+import Servant.Docs (ToSample (..), samples, singleSample)++-- | Information about Cardano address+data AddressInfo = AddressInfo+ { _addressInfoAddress :: Address -- ^ Bech32 encoded addresses+ , _addressInfoAmount :: [Amount] -- ^ Lovelaces or tokens stored on this address+ , _addressInfoStakeAddress :: Maybe Address -- ^ Stake address that controls the key+ , _addressInfoType :: AddressType -- ^ Address era+ } deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_addressInfo", CamelToSnake]] AddressInfo++instance ToSample AddressInfo where+ toSamples = pure $ singleSample+ AddressInfo+ { _addressInfoAddress = "addr1qxqs59lphg8g6qndelq8xwqn60ag3aeyfcp33c2kdp46a09re5df3pzwwmyq946axfcejy5n4x0y99wqpgtp2gd0k09qsgy6pz"+ , _addressInfoAmount =+ [ AdaAmount 42000000+ , AssetAmount+ $ Money.mkSomeDiscrete+ "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e"+ unitScale+ 12+ ]+ , _addressInfoStakeAddress = pure "stake1ux3g2c9dx2nhhehyrezyxpkstartcqmu9hk63qgfkccw5rqttygt7"+ , _addressInfoType = Shelley+ }++-- | Type (era) of an address+data AddressType = Byron | Shelley+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[ConstructorTagModifier '[ToLower]] AddressType++instance ToSample AddressType where+ toSamples = pure $ samples [ Byron, Shelley ]++-- | Details about Cardano address+data AddressDetails = AddressDetails+ { _addressDetailsAddress :: Address -- ^ Bech32 encoded address+ , _addressDetailsReceivedSum :: [Amount] -- ^ Total Lovelaces or tokens received by this address+ , _addressDetailsSentSum :: [Amount] -- ^ Total Lovelaces or tokens sent by this address+ , _addressDetailsTxCount :: Integer -- ^ Count of all transactions on the address+ } deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_addressDetails", CamelToSnake]] AddressDetails++instance ToSample AddressDetails where+ toSamples = pure $ singleSample+ AddressDetails+ { _addressDetailsAddress = "addr1qxqs59lphg8g6qndelq8xwqn60ag3aeyfcp33c2kdp46a09re5df3pzwwmyq946axfcejy5n4x0y99wqpgtp2gd0k09qsgy6pz"+ , _addressDetailsReceivedSum = amounts+ , _addressDetailsSentSum = amounts+ , _addressDetailsTxCount = 12+ }+ where amounts =+ [ AdaAmount 42000000+ , AssetAmount+ $ Money.mkSomeDiscrete+ "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e"+ unitScale+ 12+ ]++-- | UTxOs of the address+data AddressUTXO = AddressUTXO+ { _addressUTXOTxHash :: TxHash -- ^ Transaction hash of the UTXO+ , _addressUTXOOutputIndex :: Integer -- ^ UTXO index in the transaction+ , _addressUTXOAmount :: [Amount] -- ^ Amounts of Lovelaces or tokens+ , _addressUTXOBlock :: BlockHash -- ^ Block hash of the UTXO+ } deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_addressUTXO", CamelToSnake]] AddressUTXO++instance ToSample AddressUTXO where+ toSamples = pure $ samples+ [ AddressUTXO+ { _addressUTXOTxHash = "39a7a284c2a0948189dc45dec670211cd4d72f7b66c5726c08d9b3df11e44d58"+ , _addressUTXOOutputIndex = 0+ , _addressUTXOAmount = [ AdaAmount 42000000 ]+ , _addressUTXOBlock = "7eb8e27d18686c7db9a18f8bbcfe34e3fed6e047afaa2d969904d15e934847e6"+ }+ , AddressUTXO+ { _addressUTXOTxHash = "4c4e67bafa15e742c13c592b65c8f74c769cd7d9af04c848099672d1ba391b49"+ , _addressUTXOOutputIndex = 0+ , _addressUTXOAmount = [ AdaAmount 729235000 ]+ , _addressUTXOBlock = "953f1b80eb7c11a7ffcd67cbd4fde66e824a451aca5a4065725e5174b81685b7"+ }+ , AddressUTXO+ { _addressUTXOTxHash = "768c63e27a1c816a83dc7b07e78af673b2400de8849ea7e7b734ae1333d100d2"+ , _addressUTXOOutputIndex = 1+ , _addressUTXOAmount =+ [ AdaAmount 42000000+ , AssetAmount+ $ Money.mkSomeDiscrete+ "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e"+ unitScale+ 12+ ]+ , _addressUTXOBlock = "5c571f83fe6c784d3fbc223792627ccf0eea96773100f9aedecf8b1eda4544d7"+ }+ ]++-- | Transactions on the address+data AddressTransaction = AddressTransaction {+ _addressTransactionTxHash :: TxHash -- ^ Hash of the transaction+ , _addressTransactionTxIndex :: Integer -- ^ Transaction index within the block+ , _addressTransactionBlockHeight :: Integer -- ^ Block height+ } deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_addressTransaction", CamelToSnake]] AddressTransaction++instance ToSample AddressTransaction where+ toSamples = pure $ samples+ [ AddressTransaction+ { _addressTransactionTxHash = "8788591983aa73981fc92d6cddbbe643959f5a784e84b8bee0db15823f575a5b"+ , _addressTransactionTxIndex = 6+ , _addressTransactionBlockHeight = 69+ }+ , AddressTransaction+ { _addressTransactionTxHash = "52e748c4dec58b687b90b0b40d383b9fe1f24c1a833b7395cdf07dd67859f46f"+ , _addressTransactionTxIndex = 9+ , _addressTransactionBlockHeight = 4547+ }+ , AddressTransaction+ { _addressTransactionTxHash = "e8073fd5318ff43eca18a852527166aa8008bee9ee9e891f585612b7e4ba700b"+ , _addressTransactionTxIndex = 0+ , _addressTransactionBlockHeight = 564654+ }+ ]
+ src/Blockfrost/Types/Cardano/Assets.hs view
@@ -0,0 +1,221 @@+-- | Responses for Cardano asset queries++module Blockfrost.Types.Cardano.Assets+ ( AssetInfo (..)+ , AssetDetails (..)+ , AssetOnChainMetadata (..)+ , AssetMetadata (..)+ , AssetHistory (..)+ , AssetAction (..)+ , AssetTransaction (..)+ , AssetAddress (..)+ ) where++import Blockfrost.Types.Shared+import Data.Text (Text)+import Deriving.Aeson+import Servant.Docs (ToSample (..), samples, singleSample)++-- | Asset information, result of listing assets+data AssetInfo = AssetInfo+ { _assetInfoAsset :: Text+ , _assetInfoQuantity :: Quantity+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_assetInfo", CamelToSnake]] AssetInfo++instance ToSample AssetInfo where+ toSamples = pure $ samples+ [ AssetInfo+ { _assetInfoAsset = "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e"+ , _assetInfoQuantity = 1+ }+ , AssetInfo+ { _assetInfoAsset = "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e75d"+ , _assetInfoQuantity = 100000+ }+ , AssetInfo+ { _assetInfoAsset = "6804edf9712d2b619edb6ac86861fe93a730693183a262b165fcc1ba1bc99cad"+ , _assetInfoQuantity = 18605647+ }+ ]++-- | On-chain metadata stored in the minting transaction under label 721,+-- community discussion around the standard ongoing at https://github.com/cardano-foundation/CIPs/pull/85+data AssetOnChainMetadata = AssetOnChainMetadata+ { _assetOnChainMetadataName :: Text -- ^ Name of the asset+ , _assetOnChainMetadataImage :: Text -- ^ URI of the associated asset+ -- TODO: in schema has `additionalProperties: true`+ -- so it can carry more arbitrary properties, keep as Value?+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_assetOnChainMetadata", CamelToSnake]] AssetOnChainMetadata++instance ToSample AssetOnChainMetadata where+ toSamples = pure $ singleSample+ AssetOnChainMetadata+ { _assetOnChainMetadataName = "My NFT token"+ , _assetOnChainMetadataImage = "ipfs://ipfs/QmfKyJ4tuvHowwKQCbCHj4L5T3fSj8cjs7Aau8V7BWv226"+ }++-- | Asset metadata obtained from Cardano token registry+-- https://github.com/cardano-foundation/cardano-token-registry+data AssetMetadata = AssetMetadata+ { _assetMetadataName :: Text -- ^ Asset name+ , _assetMetadataDescription :: Text -- ^ Asset description+ , _assetMetadataTicker :: Maybe Text+ , _assetMetadataUrl :: Maybe Text -- ^ Asset website+ , _assetMetadataLogo :: Maybe Text -- ^ Base64 encoded logo of the asset+ , _assetMetadataDecimals :: Maybe Int -- ^ Number of decimal places of the asset unit+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_assetMetadata", CamelToSnake]] AssetMetadata++instance ToSample AssetMetadata where+ toSamples = pure $ singleSample+ AssetMetadata+ { _assetMetadataName = "nutcoin"+ , _assetMetadataDescription = "The Nut Coin"+ , _assetMetadataTicker = pure "nutc"+ , _assetMetadataUrl = pure "https://www.stakenuts.com/"+ , _assetMetadataLogo = pure "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"+ , _assetMetadataDecimals = pure 6+ }++-- | Details of an asset+data AssetDetails = AssetDetails+ { _assetDetailsAsset :: Text -- ^ Hex-encoded asset full name+ , _assetDetailsPolicyId :: PolicyId -- ^ Policy ID of the asset+ , _assetDetailsAssetName :: Maybe Text -- ^ Hex-encoded asset name of the asset+ , _assetDetailsFingerprint :: Text -- ^ CIP14 based user-facing fingerprint+ , _assetDetailsQuantity :: Quantity -- ^ Current asset quantity+ , _assetDetailsInitialMintTxHash :: TxHash -- ^ ID of the initial minting transaction+ , _assetDetailsMintOrBurnCount :: Integer -- ^ Count of mint and burn transactions+ , _assetDetailsOnchainMetadata :: Maybe AssetOnChainMetadata+ -- ^ On-chain metadata stored in the minting transaction under label 721,+ -- community discussion around the standard ongoing at https://github.com/cardano-foundation/CIPs/pull/85+ , _assetDetailsMetadata :: Maybe AssetMetadata+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_assetDetails", CamelToSnake]] AssetDetails++instance ToSample AssetDetails where+ toSamples = pure $ singleSample+ AssetDetails+ { _assetDetailsAsset = "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e"+ , _assetDetailsPolicyId = "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a7"+ , _assetDetailsAssetName = pure "6e7574636f696e"+ , _assetDetailsFingerprint = "asset1pkpwyknlvul7az0xx8czhl60pyel45rpje4z8w"+ , _assetDetailsQuantity = 12000+ , _assetDetailsInitialMintTxHash = "6804edf9712d2b619edb6ac86861fe93a730693183a262b165fcc1ba1bc99cad"+ , _assetDetailsMintOrBurnCount = 1+ , _assetDetailsOnchainMetadata = pure $+ AssetOnChainMetadata+ { _assetOnChainMetadataName = "My NFT token"+ , _assetOnChainMetadataImage = "ipfs://ipfs/QmfKyJ4tuvHowwKQCbCHj4L5T3fSj8cjs7Aau8V7BWv226"+ }+ , _assetDetailsMetadata = pure $+ AssetMetadata+ { _assetMetadataName = "nutcoin"+ , _assetMetadataDescription = "The Nut Coin"+ , _assetMetadataTicker = pure "nutc"+ , _assetMetadataUrl = pure "https://www.stakenuts.com/"+ , _assetMetadataLogo = pure "iVBORw0KGgoAAAANSUhEUgAAADAAAAAoCAYAAAC4h3lxAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAABmJLR0QA/wD/AP+gvaeTAAAAB3RJTUUH5QITCDUPjqwFHwAAB9xJREFUWMPVWXtsU9cZ/8499/r6dZ3E9rUdO7ZDEgglFWO8KaOsJW0pCLRKrN1AqqYVkqoqrYo0ja7bpElru1WairStFKY9WzaE1E1tx+jokKqwtqFNyhKahEJJyJNgJ37E9r1+3HvO/sFR4vhx7SBtfH/F3/l93/f7ne/4PBxEKYU72dj/ZfH772v1TU+HtqbTaX8wOO01GPQpRVH7JEm+vGHDuq6z7/8jUSoHKtaBKkEUFUXdajDy1hUrmrs6zn/wWS7m7pZVjMUirKGUTnzc+e9xLcTrPPVfZzDz06Sc2lyQGEIyAPzT7Xa+dvE/3e+XLaCxoflHsVj8MAAYs74aa/WHoenwvpkZKeFy2Z5NJlOPUkqXZccFwSSrKjlyffjLH+TL6XTUGTGL/6hklD3ldIrj2M5MRmkLBMcvaRLQ1Nj88sxM/HCBfMP+eu/OYGDqe6l0WmpoqJ/88upgrU7HrQNA/cFg6MlkKiLlBtVUO40cx54BgHvLIT/HJLvdeqh/4NKxogKWN7fsCoUi7xTLxLJ4vLq6ak//wKVOrdXtttrTDMPsqJA8AAAwDErdu3VL3alTf5ma9eWCpoKhn5dKpCiqJxicPucQPVu0FHaInn35yHMcKwPAa4SQ3QCwFgDWUko3qSr5vqqSgTypuEg4Mo/zvA74/Y0rZSnZU8akSHV17k2fXfy0txjI5224kEym1s/1EUI7LBbztweHrkzkizn49LP6U6feepFSeggAQK/n04SQZ8bGrxdeQjZrbRvGzLH5hcibRqOhPplMfS1fIY5jz4xPDBdcGggho2h3z9sOLRazdG3wqp9SMgUlzGZ17SSEPsRx7J8CwfGu3PF57WhqqjfN/VxVJUxKUrIdITAXKpDJKFscosdfaFy0u+/K9aXTmXe0kAcAmA5Nng5Hbj6Tj/wCAYFAcN7uEY3GXGazMSHLqVVFapgBoMPna9yqhRAAgCTJMa3YUjZPgNFkSlWYx5eUkx+0tKx83V3rF+cVYJjruWCe133DIXqMmrNrFSDabRcWkywYmG5XFOW6aHcfb9324CoAgMmbo9MIoXkneCajiAihV/c/8eSiBSw4BxyiZxQA6m7H7FBKT2CMn2MY5jFFUX6ZO+5w2j8aHZ7YH40FByrJD5DnHGAY5uTtIA8AgBDaR4F2Yxb3WizCgmtA4ObUPSazodduqz3Suu0hf0U1cjvgdNSJ1dWWveFwdDUAtAiC2Uopdcdi8c9Zlh3GmDGl05mtAKAvo47EcdwThJCjqqpWFxALlNITomg73tff21GRAJez7iVK4WGGYfoJIQduBsbm7UrLm1ueCoUiv65kpiilw1ZbzcFoZOYoIcRTAn6eYZgXJm+Oni+Vd3YJbdyweSch9HlK6SpVVfcyDDq7Yf3m2XPBIXraKyV/a4b9UkLawbLsZgB4rwR8CyGkw13r+5fX27BckwBAEJ47oKpk8+DgUIdod7fV1vqOAMDrlZLPmqKoB+rrvXIgOP6w0WjYy3Ls5RL4bUk52bVm9fqnCk7M3CXU2ND8+MxM7BcIIftiyRYyntcdHh0bmr0wfmXl6p2SJB2KRmP3l4j7zejYUFtRAQAAgslm1Bv4nyGEDpYiIwjmjw0G/RjP866JiclNqqqWfKLq9fyZkdHBBXcnl9O71GDgD8bj0ncRQqZ8sRgzL9yYHH2pqICsOUTPLgA4CXNeZFmzWIS/YhYfjUZmvqPjuceSckrz25pS2h2cmlhbaBwhzr6kfsnL8Xhif55YYFl23Y3Jkdl7EVMoUSA4/q6qqNsBIPd11e52u45FwtG3CSH7yiEPAGC1Vt9dXGBmanDoygFLlbAjtzZCCMyC6VeaOpA1l9N7l1kwtauKaozHE28YTQaQpeR7+TqjxXheR0fHhhgt2CX1S3clEtKC16HL5djYe+niBU0CcmYA2W21/Qih5ZqDcoxlMZ24MaJJAABA87IVJ8Lh6N65Pr1B/+LIyLUfAhRZQvnM6ah7ZDHkAQB0vK6/HHxNTc2ruT5Zkldn/y5LACFk+2LIAwAwCGl6yGSt88KHXbmrBCHkqEgAz+vWLFZALJb4qNwYhFDhCSknkSwnQ4sVgDFeWg7+gQe2r1tAmkGTFQlACHWVg89nhJA9ot3dphV/eeCLp/Pw6K5IQP0S39uLFXCLwDG7zf1cKZxD9LSlUunHc/12u/2t2Vzl/rzu8zb8PZlM7bwdQgDgPK/nX2nddt+53//ht3LW2dS0fF0iLj2vquojuQFmwXRucPBKa8UCmpe1iOFwpAsAfLdJBFBKwVIlXJ2JxqKCxbwyHkvoCkAlv9/71U+7Oq+UJWDZ0hViJBL1cRynbNq0sSeeiPl6ei4NqIqq6TSmlB7X6bjuTEY5pgWfzwxGPZhMpt39/b3vzvWXFGCzulZjjM/DrauDwcAr8bjcgzGjZUuVBMH8k2uDX7wCAFDr8n2LEPI7SqmhTP6SzVbz6MDlz0/nDpT8EmOM22HOvUeWU2wp8iyLgRL6hk7Hrc2SBwC4MTlykmXZRozxn00mbVcphNA5jJmV+chr6oDd5l6jN/A/TqfSuwEAGITGMIsvGo3GTwTB3Dc2NjGSxdZYq4VIOOoNBANnKE0XPXE3brjHOTQ08k2MmVZOxzVJCbkFIQSCYEphzPaFQuGzTpfjb319PZ8UFXin/5OvrHPg/9HueAH/BSUqOuNZm4fyAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDIxLTAyLTE5VDA4OjUyOjI1KzAwOjAwCmFGlgAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyMS0wMi0xOVQwODo1MjoyMyswMDowMBjsyxAAAAAASUVORK5CYII="+ , _assetMetadataDecimals = pure 6+ }+ }++-- | Action of the asset.+-- Created (`Minted`) or destroyed (`Burned`).+data AssetAction = Minted | Burned+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[ConstructorTagModifier '[ToLower]] AssetAction++instance ToSample AssetAction where+ toSamples = pure $ samples [ Minted, Burned ]++-- | History of an asset+data AssetHistory = AssetHistory+ { _assetHistoryTxHash :: TxHash -- ^ Hash of the transaction containing the asset action+ , _assetHistoryAmount :: Quantity -- ^ Asset amount of the specific action+ , _assetHistoryAction :: AssetAction -- ^ Action executed upon the asset policy+ } deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_assetHistory", CamelToSnake]] AssetHistory++instance ToSample AssetHistory where+ toSamples = pure $ samples+ [ AssetHistory+ { _assetHistoryTxHash = "2dd15e0ef6e6a17841cb9541c27724072ce4d4b79b91e58432fbaa32d9572531"+ , _assetHistoryAmount = 10+ , _assetHistoryAction = Minted+ }+ , AssetHistory+ { _assetHistoryTxHash = "9c190bc1ac88b2ab0c05a82d7de8b71b67a9316377e865748a89d4426c0d3005"+ , _assetHistoryAmount = 5+ , _assetHistoryAction = Burned+ }+ , AssetHistory+ { _assetHistoryTxHash = "1a0570af966fb355a7160e4f82d5a80b8681b7955f5d44bec0dde628516157f0"+ , _assetHistoryAmount = 5+ , _assetHistoryAction = Burned+ }+ ]++-- | Transaction of an asset+data AssetTransaction = AssetTransaction+ { _assetTransactionTxHash :: TxHash -- ^ Hash of the transaction+ , _assetTransactionTxIndex :: Integer -- ^ Transaction index within the block+ , _assetTransactionBlockHeight :: Integer -- ^ Block height+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_assetTransaction", CamelToSnake]] AssetTransaction++instance ToSample AssetTransaction where+ toSamples = pure $ samples+ [ AssetTransaction+ { _assetTransactionTxHash = "8788591983aa73981fc92d6cddbbe643959f5a784e84b8bee0db15823f575a5b"+ , _assetTransactionTxIndex = 6+ , _assetTransactionBlockHeight = 69+ }+ , AssetTransaction+ { _assetTransactionTxHash = "52e748c4dec58b687b90b0b40d383b9fe1f24c1a833b7395cdf07dd67859f46f"+ , _assetTransactionTxIndex = 9+ , _assetTransactionBlockHeight = 4547+ }+ , AssetTransaction+ { _assetTransactionTxHash = "e8073fd5318ff43eca18a852527166aa8008bee9ee9e891f585612b7e4ba700b"+ , _assetTransactionTxIndex = 0+ , _assetTransactionBlockHeight = 564654+ }+ ]++-- | An address containing specific asset+data AssetAddress = AssetAddress+ { _assetAddressAddress :: Address -- ^ Address containing the specific asset+ , _assetAddressQuantity :: Quantity -- ^ Asset quantity on the specific address+ } deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_assetAddress", CamelToSnake]] AssetAddress++instance ToSample AssetAddress where+ toSamples = pure $ samples+ [ AssetAddress+ { _assetAddressAddress = "addr1qxqs59lphg8g6qndelq8xwqn60ag3aeyfcp33c2kdp46a09re5df3pzwwmyq946axfcejy5n4x0y99wqpgtp2gd0k09qsgy6pz"+ , _assetAddressQuantity = 1+ }+ , AssetAddress+ { _assetAddressAddress = "addr1qyhr4exrgavdcn3qhfcc9f939fzsch2re5ry9cwvcdyh4x4re5df3pzwwmyq946axfcejy5n4x0y99wqpgtp2gd0k09qdpvhza"+ , _assetAddressQuantity = 100000+ }+ , AssetAddress+ { _assetAddressAddress = "addr1q8zup8m9ue3p98kxlxl9q8rnyan8hw3ul282tsl9s326dfj088lvedv4zckcj24arcpasr0gua4c5gq4zw2rpcpjk2lq8cmd9l"+ , _assetAddressQuantity = 18605647+ }+ ]
+ src/Blockfrost/Types/Cardano/Blocks.hs view
@@ -0,0 +1,52 @@+-- | Information about produced blocks++module Blockfrost.Types.Cardano.Blocks+ ( Block (..)+ ) where++import Data.Text (Text)+import Deriving.Aeson+import Servant.Docs (ToSample (..), singleSample)++import Blockfrost.Types.Shared++-- | Information about a block+data Block = Block+ { _blockTime :: POSIXTime -- ^ Block creation time in UNIX time+ , _blockHeight :: Maybe Integer -- ^ Block number+ , _blockHash :: BlockHash -- ^ Hash of the block+ , _blockSlot :: Maybe Slot -- ^ Slot number+ , _blockEpoch :: Maybe Epoch -- ^ Epoch number+ , _blockEpochSlot :: Maybe Integer -- ^ Slot within the epoch+ , _blockSlotLeader :: Text -- ^ Bech32 ID of the slot leader or specific block description in case there is no slot leader+ , _blockSize :: Integer -- ^ Block size in Bytes+ , _blockTxCount :: Integer -- ^ Number of transactions in the block+ , _blockOutput :: Maybe Lovelaces -- ^ Total output within the block in Lovelaces+ , _blockFees :: Maybe Lovelaces -- ^ Total fees within the block in Lovelaces+ , _blockBlockVrf :: Maybe Text -- ^ VRF key of the block+ , _blockPreviousBlock :: Maybe BlockHash -- ^ Hash of the previous block+ , _blockNextBlock :: Maybe BlockHash -- ^ Hash of the next block+ , _blockConfirmations :: Integer -- ^ Number of block confirmations+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_block", CamelToSnake]] Block++instance ToSample Block where+ toSamples _ = singleSample $ Block+ { _blockTime = 1641338934+ , _blockHeight = pure 15243593+ , _blockHash = "4ea1ba291e8eef538635a53e59fddba7810d1679631cc3aed7c8e6c4091a516a"+ , _blockSlot = pure 412162133+ , _blockEpoch = pure 425+ , _blockEpochSlot = pure 12+ , _blockSlotLeader = "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2qnikdy"+ , _blockSize = 3+ , _blockTxCount = 1+ , _blockOutput = pure 128314491794+ , _blockFees = pure 592661+ , _blockBlockVrf = pure "vrf_vk1wf2k6lhujezqcfe00l6zetxpnmh9n6mwhpmhm0dvfh3fxgmdnrfqkms8ty"+ , _blockPreviousBlock = pure "43ebccb3ac72c7cebd0d9b755a4b08412c9f5dcb81b8a0ad1e3c197d29d47b05"+ , _blockNextBlock = pure "8367f026cf4b03e116ff8ee5daf149b55ba5a6ec6dec04803b8dc317721d15fa"+ , _blockConfirmations = 4698+ }
+ src/Blockfrost/Types/Cardano/Epochs.hs view
@@ -0,0 +1,133 @@+-- | Responses for Cardano epoch quries++module Blockfrost.Types.Cardano.Epochs+ ( EpochInfo (..)+ , PoolStakeDistribution (..)+ , ProtocolParams (..)+ , StakeDistribution (..)+ ) where++import Blockfrost.Types.Shared+import Data.Text (Text)+import Deriving.Aeson+import Servant.Docs (ToSample (..), singleSample)++-- | Information about an epoch+data EpochInfo = EpochInfo+ { _epochInfoEpoch :: Epoch -- ^ Epoch number+ , _epochInfoStartTime :: POSIXTime -- ^ Unix time of the start of the epoch+ , _epochInfoEndTime :: POSIXTime -- ^ Unix time of the end of the epoch+ , _epochInfoFirstBlockTime :: POSIXTime -- ^ Unix time of the first block of the epoch+ , _epochInfoLastBlockTime :: POSIXTime -- ^ Unix time of the last block of the epoch+ , _epochInfoBlockCount :: Integer -- ^ Number of blocks within the epoch+ , _epochInfoTxCount :: Integer -- ^ Number of transactions within the epoch+ , _epochInfoOutput :: Lovelaces -- ^ Sum of all the transactions within the epoch in Lovelaces+ , _epochInfoFees :: Lovelaces -- ^ Sum of all the fees within the epoch in Lovelaces+ , _epochInfoActiveStake :: Maybe Lovelaces -- ^ Sum of all the active stakes within the epoch in Lovelaces+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_epochInfo", CamelToSnake]] EpochInfo++instance ToSample EpochInfo where+ toSamples = pure $ singleSample+ EpochInfo+ { _epochInfoEpoch = 225+ , _epochInfoStartTime = 1603403091+ , _epochInfoEndTime = 1603835086+ , _epochInfoFirstBlockTime = 1603403092+ , _epochInfoLastBlockTime = 1603835084+ , _epochInfoBlockCount = 21298+ , _epochInfoTxCount = 17856+ , _epochInfoOutput = 7849943934049314+ , _epochInfoFees = 4203312194+ , _epochInfoActiveStake = pure 784953934049314+ }+++-- | Protocol parameters+data ProtocolParams = ProtocolParams+ { _protocolParamsEpoch :: Epoch -- ^ Epoch number+ , _protocolParamsMinFeeA :: Integer -- ^ The linear factor for the minimum fee calculation for given epoch+ , _protocolParamsMinFeeB :: Integer -- ^ The constant factor for the minimum fee calculation+ , _protocolParamsMaxBlockSize :: Integer -- ^ Maximum block body size in Bytes+ , _protocolParamsMaxTxSize :: Integer -- ^ Maximum transaction size+ , _protocolParamsMaxBlockHeaderSize :: Integer -- ^ Maximum block header size+ , _protocolParamsKeyDeposit :: Lovelaces -- ^ The amount of a key registration deposit in Lovelaces+ , _protocolParamsPoolDeposit :: Lovelaces -- ^ The amount of a pool registration deposit in Lovelaces+ , _protocolParamsEMax :: Integer -- ^ Epoch bound on pool retirement+ , _protocolParamsNOpt :: Integer -- ^ Desired number of pools+ , _protocolParamsA0 :: Double -- ^ Pool pledge influence+ , _protocolParamsRho :: Double -- ^ Monetary expansion+ , _protocolParamsTau :: Double -- ^ Treasury expansion+ , _protocolParamsDecentralisationParam :: Double -- ^ Percentage of blocks produced by federated nodes+-- ?? TODO: object Nullable+-- , protocolParamsExtraEntropy :: Maybe Value+ , _protocolParamsProtocolMajorVer :: Integer -- ^ Accepted protocol major version+ , _protocolParamsProtocolMinorVer :: Integer -- ^ Accepted protocol minor version+ , _protocolParamsMinUtxo :: Lovelaces -- ^ Minimum UTXO value+ , _protocolParamsMinPoolCost :: Lovelaces -- ^ Minimum stake cost forced on the pool+ , _protocolParamsNonce :: Text -- ^ Epoch number only used once+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_protocolParams", CamelToSnake]] ProtocolParams++instance ToSample ProtocolParams where+ toSamples = pure $ singleSample+ ProtocolParams+ { _protocolParamsEpoch = 225+ , _protocolParamsMinFeeA = 44+ , _protocolParamsMinFeeB = 155381+ , _protocolParamsMaxBlockSize = 65536+ , _protocolParamsMaxTxSize = 16384+ , _protocolParamsMaxBlockHeaderSize = 1100+ , _protocolParamsKeyDeposit = 2000000+ , _protocolParamsPoolDeposit = 500000000+ , _protocolParamsEMax = 18+ , _protocolParamsNOpt = 150+ , _protocolParamsA0 = 0.3+ , _protocolParamsRho = 0.003+ , _protocolParamsTau = 0.2+ , _protocolParamsDecentralisationParam = 0.5+-- , _protocolParamsExtraEntropy = Nothing+ , _protocolParamsProtocolMajorVer = 2+ , _protocolParamsProtocolMinorVer = 0+ , _protocolParamsMinUtxo = 1000000+ , _protocolParamsMinPoolCost = 340000000+ , _protocolParamsNonce = "1a3be38bcbb7911969283716ad7aa550250226b76a61fc51cc9a9a35d9276d81"+ }++-- | Active stake distribution for an epoch+data StakeDistribution = StakeDistribution+ { _stakeDistributionStakeAddress :: Address -- ^ Stake address+ , _stakeDistributionPoolId :: PoolId -- ^ Bech32 prefix of the pool delegated to+ , _stakeDistributionAmount :: Lovelaces -- ^ Amount of active delegated stake in Lovelaces+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_stakeDistribution", CamelToSnake]] StakeDistribution++instance ToSample StakeDistribution where+ toSamples = pure $ singleSample+ StakeDistribution+ { _stakeDistributionStakeAddress = "stake1u9l5q5jwgelgagzyt6nuaasefgmn8pd25c8e9qpeprq0tdcp0e3uk"+ , _stakeDistributionPoolId = "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ , _stakeDistributionAmount = 4440295078+ }++-- | Stake distribution for an epoch for specific pool+data PoolStakeDistribution = PoolStakeDistribution+ { _poolStakeDistributionStakeAddress :: Address -- ^ Stake address+ , _poolStakeDistributionAmount :: Lovelaces -- ^ Amount of active delegated stake in Lovelaces+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_poolStakeDistribution", CamelToSnake]] PoolStakeDistribution++instance ToSample PoolStakeDistribution where+ toSamples = pure $ singleSample+ PoolStakeDistribution+ { _poolStakeDistributionStakeAddress = "stake1u9l5q5jwgelgagzyt6nuaasefgmn8pd25c8e9qpeprq0tdcp0e3uk"+ , _poolStakeDistributionAmount = 4440295078+ }
+ src/Blockfrost/Types/Cardano/Genesis.hs view
@@ -0,0 +1,44 @@+-- | Blockchain genesis++{-# LANGUAGE NumericUnderscores #-}++module Blockfrost.Types.Cardano.Genesis+ ( Genesis (..)+ ) where++import Deriving.Aeson+import Servant.Docs (ToSample (..), singleSample)++import Blockfrost.Types.Shared++-- | Information about blockchain genesis+data Genesis = Genesis+ { _genesisActiveSlotsCoefficient :: Double -- ^ The proportion of slots in which blocks should be issued+ , _genesisUpdateQuorum :: Integer -- ^ Determines the quorum needed for votes on the protocol parameter updates+ , _genesisMaxLovelaceSupply :: Lovelaces -- ^ The total number of lovelace in the system+ , _genesisNetworkMagic :: Integer -- ^ Network identifier+ , _genesisEpochLength :: Integer -- ^ Number of slots in an epoch+ , _genesisSystemStart :: POSIXTime -- ^ Time of slot 0 in UNIX time+ , _genesisSlotsPerKesPeriod :: Integer -- ^ Number of slots in an KES period+ , _genesisSlotLength :: Integer -- ^ Duration of one slot in seconds+ , _genesisMaxKesEvolutions :: Integer -- ^ The maximum number of time a KES key can be evolved before a pool operator must create a new operational certificate+ , _genesisSecurityParam :: Integer -- ^ Security parameter @k@+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_genesis", CamelToSnake]] Genesis++instance ToSample Genesis where+ toSamples _ = singleSample $+ Genesis+ { _genesisActiveSlotsCoefficient = 0.05+ , _genesisUpdateQuorum = 5+ , _genesisMaxLovelaceSupply = 45_000_000_000_000_000+ , _genesisNetworkMagic = 764824073+ , _genesisEpochLength = 432_000+ , _genesisSystemStart = 1506203091+ , _genesisSlotsPerKesPeriod = 129600+ , _genesisSlotLength = 1+ , _genesisMaxKesEvolutions = 62+ , _genesisSecurityParam = 2160+ }
+ src/Blockfrost/Types/Cardano/Metadata.hs view
@@ -0,0 +1,83 @@+-- | Transaction metadata++module Blockfrost.Types.Cardano.Metadata+ ( TxMeta (..)+ , TxMetaJSON (..)+ , TxMetaCBOR (..)+ ) where++import Data.Aeson (Value, object, (.=))+import Data.Text (Text)+import Deriving.Aeson+import Servant.Docs (ToSample (..), samples)++import Blockfrost.Types.Shared++-- | Transaction metadata label in use+data TxMeta = TxMeta+ { _txMetaLabel :: Text -- ^ Metadata label+ , _txMetaCip10 :: Maybe Text -- ^ CIP10 defined description+ , _txMetaCount :: Quantity -- ^ The count of metadata entries with a specific label+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_txMeta", CamelToSnake]] TxMeta++instance ToSample TxMeta where+ toSamples = pure $ samples+ [ TxMeta "1990" Nothing 1+ , TxMeta "1967" (Just "nut.link metadata oracles registry") 3+ , TxMeta "1968" (Just "nut.link metadata oracles data points") 16321+ ]++-- | Transaction metadata content in JSON+data TxMetaJSON = TxMetaJSON+ { _txMetaJSONTxHash :: Text -- ^ Transaction hash that contains the specific metadata+ , _txMetaJSONJSONMetadata :: Maybe Value -- ^ Content of the JSON metadata+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_txMetaJSON", CamelToSnake]] TxMetaJSON++instance ToSample TxMetaJSON where+ toSamples =+ let oracleMeta val =+ object [+ "ADAUSD" .=+ [ object [ "value" .= (val :: Text)+ , "source" .= ("ergoOracles" :: Text) ]+ ]+ ]+ in pure $ samples+ [ TxMetaJSON+ "257d75c8ddb0434e9b63e29ebb6241add2b835a307aa33aedba2effe09ed4ec8"+ (Just $ oracleMeta "0.10409800535729975")+ , TxMetaJSON+ "e865f2cc01ca7381cf98dcdc4de07a5e8674b8ea16e6a18e3ed60c186fde2b9c"+ (Just $ oracleMeta "0.15409850555139935")+ , TxMetaJSON+ "4237501da3cfdd53ade91e8911e764bd0699d88fd43b12f44a1f459b89bc91be"+ Nothing+ ]++-- | Transaction metadata content in CBOR+data TxMetaCBOR = TxMetaCBOR+ { _txMetaCBORTxHash :: Text -- ^ Transaction hash that contains the specific metadata+ , _txMetaCBORCBORMetadata :: Maybe Text -- ^ Content of the CBOR metadata+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_txMetaCBOR", CamelToSnake]] TxMetaCBOR++instance ToSample TxMetaCBOR where+ toSamples = pure $ samples+ [ TxMetaCBOR+ "257d75c8ddb0434e9b63e29ebb6241add2b835a307aa33aedba2effe09ed4ec8"+ Nothing+ , TxMetaCBOR+ "e865f2cc01ca7381cf98dcdc4de07a5e8674b8ea16e6a18e3ed60c186fde2b9c"+ Nothing+ , TxMetaCBOR+ "4237501da3cfdd53ade91e8911e764bd0699d88fd43b12f44a1f459b89bc91be"+ (Just "\\xa100a16b436f6d62696e6174696f6e8601010101010c")+ ]
+ src/Blockfrost/Types/Cardano/Network.hs view
@@ -0,0 +1,65 @@+-- | Cardano Network reponses++{-# LANGUAGE NumericUnderscores #-}++module Blockfrost.Types.Cardano.Network+ ( Network (..)+ , NetworkStake (..)+ , NetworkSupply (..)+ ) where++import Deriving.Aeson+import Servant.Docs (ToSample (..), singleSample)++import Blockfrost.Types.Shared++-- | Lovelace supply data+data NetworkSupply = NetworkSupply+ { _supplyMax :: Lovelaces -- ^ Maximum supply in Lovelaces+ , _supplyTotal :: Lovelaces -- ^ Current total (max supply - reserves) supply in Lovelaces+ , _supplyCirculating :: Lovelaces -- ^ Current circulating (UTXOs + withdrawables) supply in Lovelaces+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_supply", CamelToSnake]] NetworkSupply++netSupplySample :: NetworkSupply+netSupplySample =+ NetworkSupply+ 45_000_000_000_000_000+ 32_890_715_183_299_160+ 32_412_601_976_210_393++instance ToSample NetworkSupply where+ toSamples = pure $ singleSample netSupplySample++-- | Live and active stake of the whole network+data NetworkStake = NetworkStake+ { _stakeLive :: Lovelaces -- ^ Current live stake in Lovelaces+ , _stakeActive :: Lovelaces -- ^ Current active stake in Lovelaces+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_stake", CamelToSnake]] NetworkStake++netStakeSample :: NetworkStake+netStakeSample =+ NetworkStake+ 23_204_950_463_991_654+ 22_210_233_523_456_321++instance ToSample NetworkStake where+ toSamples = pure $ singleSample $ netStakeSample++-- | Detailed network information+data Network = Network+ { _networkSupply :: NetworkSupply -- ^ Supply data+ , _networkStake :: NetworkStake -- ^ Stake data+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_network", CamelToSnake]] Network++instance ToSample Network where+ toSamples = pure $ singleSample $+ Network netSupplySample netStakeSample
+ src/Blockfrost/Types/Cardano/Pools.hs view
@@ -0,0 +1,254 @@+-- | Cardano Pools reponses++module Blockfrost.Types.Cardano.Pools+ ( PoolEpoch (..)+ , PoolInfo (..)+ , PoolHistory (..)+ , PoolMetadata (..)+ , PoolRelay (..)+ , PoolDelegator (..)+ , PoolUpdate (..)+ , PoolRegistrationAction (..)+ , samplePoolRelay+ ) where++import Data.Aeson (FromJSON (..), ToJSON (..), object, pairs, withText)+import Data.Text (Text)+import Deriving.Aeson+import Servant.Docs (ToSample (..), samples, singleSample)++import Blockfrost.Types.Shared++-- | Retirement epoch for pool+data PoolEpoch = PoolEpoch+ { _poolEpochPoolId :: PoolId -- ^ Bech32 encoded pool ID+ , _poolEpochEpoch :: Epoch -- ^ Retirement epoch number+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_poolEpoch", CamelToSnake]] PoolEpoch++instance ToSample PoolEpoch where+ toSamples = pure $ samples+ [ PoolEpoch "pool19u64770wqp6s95gkajc8udheske5e6ljmpq33awxk326zjaza0q" 225+ , PoolEpoch "pool1dvla4zq98hpvacv20snndupjrqhuc79zl6gjap565nku6et5zdx" 215+ , PoolEpoch "pool1wvccajt4eugjtf3k0ja3exjqdj7t8egsujwhcw4tzj4rzsxzw5w" 231+ ]++-- | Detailed pool information+data PoolInfo = PoolInfo+ { _poolInfoPoolId :: PoolId -- ^ Bech32 encoded pool ID+ , _poolInfoHex :: Text -- ^ Hexadecimal pool ID.+ , _poolInfoVrfKey :: Text -- ^ VRF key hash+ , _poolInfoBlocksMinted :: Integer -- ^ Total minted blocks+ , _poolInfoLiveStake :: Lovelaces+ , _poolInfoLiveSize :: Double+ , _poolInfoLiveSaturation :: Double+ , _poolInfoActiveStake :: Lovelaces+ , _poolInfoActiveSize :: Double+ , _poolInfoDeclaredPledge :: Lovelaces -- ^ Stake pool certificate pledge+ , _poolInfoLivePledge :: Lovelaces -- ^ Stake pool current pledge+ , _poolInfoMarginCost :: Double -- ^ Margin tax cost of the stake pool+ , _poolInfoFixedCost :: Lovelaces -- ^ Fixed tax cost of the stake pool+ , _poolInfoRewardAccount :: Address -- ^ Bech32 reward account of the stake pool+ , _poolInfoOwners :: [Address]+ , _poolInfoRegistration :: [Text]+ , _poolInfoRetirement :: [Text]+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_poolInfo", CamelToSnake]] PoolInfo++instance ToSample PoolInfo where+ toSamples = pure $ singleSample+ PoolInfo+ { _poolInfoPoolId = "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ , _poolInfoHex = "0f292fcaa02b8b2f9b3c8f9fd8e0bb21abedb692a6d5058df3ef2735"+ , _poolInfoVrfKey = "0b5245f9934ec2151116fb8ec00f35fd00e0aa3b075c4ed12cce440f999d8233"+ , _poolInfoBlocksMinted = 69+ , _poolInfoLiveStake = 6900000000+ , _poolInfoLiveSize = 0.42+ , _poolInfoLiveSaturation = 0.93+ , _poolInfoActiveStake = 4200000000+ , _poolInfoActiveSize = 0.43+ , _poolInfoDeclaredPledge = 5000000000+ , _poolInfoLivePledge = 5000000001+ , _poolInfoMarginCost = 0.05+ , _poolInfoFixedCost = 340000000+ , _poolInfoRewardAccount = "stake1uxkptsa4lkr55jleztw43t37vgdn88l6ghclfwuxld2eykgpgvg3f"+ , _poolInfoOwners = [ "stake1u98nnlkvkk23vtvf9273uq7cph5ww6u2yq2389psuqet90sv4xv9v" ]+ , _poolInfoRegistration =+ [ "9f83e5484f543e05b52e99988272a31da373f3aab4c064c76db96643a355d9dc"+ , "7ce3b8c433bf401a190d58c8c483d8e3564dfd29ae8633c8b1b3e6c814403e95"+ , "3e6e1200ce92977c3fe5996bd4d7d7e192bcb7e231bc762f9f240c76766535b9"+ ]+ , _poolInfoRetirement = [ "252f622976d39e646815db75a77289cf16df4ad2b287dd8e3a889ce14c13d1a8" ]+ }++-- | History of a stake pool parameters over epochs+data PoolHistory = PoolHistory+ { _poolHistoryEpoch :: Epoch -- ^ Epoch number+ , _poolHistoryBlocks :: Integer -- ^ Number of blocks created by pool+ , _poolHistoryActiveStake :: Lovelaces -- ^ Active (Snapshot of live stake 2 epochs ago) stake in Lovelaces+ , _poolHistoryActiveSize :: Double -- ^ Pool size (percentage) of overall active stake at that epoch+ , _poolHistoryDelegatorsCount :: Integer -- ^ Number of delegators for epoch+ , _poolHistoryRewards :: Lovelaces -- ^ Total rewards received before distribution to delegators+ , _poolHistoryFees :: Lovelaces -- ^ Pool operator rewards+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_poolHistory", CamelToSnake]] PoolHistory++instance ToSample PoolHistory where+ toSamples = pure $ singleSample+ PoolHistory+ { _poolHistoryEpoch = 233+ , _poolHistoryBlocks = 22+ , _poolHistoryActiveStake = 20485965693569+ , _poolHistoryActiveSize = 1.2345+ , _poolHistoryDelegatorsCount = 115+ , _poolHistoryRewards = 206936253674159+ , _poolHistoryFees = 1290968354+ }++-- | Stake pool registration metadata+data PoolMetadata = PoolMetadata+ { _poolMetadataPoolId :: PoolId -- ^ Bech32 pool ID+ , _poolMetadataHex :: Text -- ^ Hexadecimal pool ID+ , _poolMetadataUrl :: Maybe Text -- ^ URL to the stake pool metadata+ , _poolMetadataHash :: Maybe Text -- ^ Hash of the metadata file+ , _poolMetadataTicker :: Maybe Text -- ^ Ticker of the stake pool+ , _poolMetadataName :: Maybe Text -- ^ Name of the stake pool+ , _poolMetadataDescription :: Maybe Text -- ^ Description of the stake pool+ , _poolMetadataHomepage :: Maybe Text -- ^ Home page of the stake pool+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_poolMetadata", CamelToSnake]] PoolMetadata++-- We need this more specific+-- instance since API returns+-- empty object if there's no metadata+instance {-# OVERLAPS #-} ToJSON (Maybe PoolMetadata) where+ toJSON Nothing = object mempty+ toJSON (Just pm) = toJSON pm+ toEncoding Nothing = pairs mempty+ toEncoding (Just pm) = toEncoding pm+instance {-# OVERLAPS #-} FromJSON (Maybe PoolMetadata) where+ parseJSON x | x == object [] = pure Nothing+ parseJSON x = Just <$> parseJSON x++instance ToSample PoolMetadata where+ toSamples = pure $ singleSample samplePoolMetadata++samplePoolMetadata :: PoolMetadata+samplePoolMetadata =+ PoolMetadata+ { _poolMetadataPoolId = "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ , _poolMetadataHex = "0f292fcaa02b8b2f9b3c8f9fd8e0bb21abedb692a6d5058df3ef2735"+ , _poolMetadataUrl = Just "https://stakenuts.com/mainnet.json"+ , _poolMetadataHash = Just "47c0c68cb57f4a5b4a87bad896fc274678e7aea98e200fa14a1cb40c0cab1d8c"+ , _poolMetadataTicker = Just "NUTS"+ , _poolMetadataName = Just "Stake Nuts"+ , _poolMetadataDescription = Just "The best pool ever"+ , _poolMetadataHomepage = Just "https://stakentus.com/"+ }++-- | Relays of a stake pool+data PoolRelay = PoolRelay+ { _poolRelayIpv4 :: Maybe Text -- ^ IPv4 address of the relay+ , _poolRelayIpv6 :: Maybe Text -- ^ IPv6 address of the relay+ , _poolRelayDns :: Maybe Text -- ^ DNS name of the relay+ , _poolRelayDnsSrv :: Maybe Text -- ^ DNS SRV entry of the relay+ , _poolRelayPort :: Integer -- ^ Network port of the relay+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_poolRelay", CamelToSnake]] PoolRelay++instance ToSample PoolRelay where+ toSamples = pure $ singleSample samplePoolRelay++-- | Example of `PoolRelay`+samplePoolRelay :: PoolRelay+samplePoolRelay =+ PoolRelay+ { _poolRelayIpv4 = Just "4.4.4.4"+ , _poolRelayIpv6 = Just "https://stakenuts.com/mainnet.json"+ , _poolRelayDns = Just "relay1.stakenuts.com"+ , _poolRelayDnsSrv = Just "_relays._tcp.relays.stakenuts.com"+ , _poolRelayPort = 3001+ }++-- | Stake pool delegator+data PoolDelegator = PoolDelegator+ { _poolDelegatorAddress :: Text -- ^ Bech32 encoded stake addresses+ , _poolDelegatorLiveStake :: Lovelaces -- ^ Currently delegated amount+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_poolDelegator", CamelToSnake]] PoolDelegator++instance ToSample PoolDelegator where+ toSamples = pure $ samples+ [ PoolDelegator+ { _poolDelegatorAddress = "stake1ux4vspfvwuus9uwyp5p3f0ky7a30jq5j80jxse0fr7pa56sgn8kha"+ , _poolDelegatorLiveStake = 1137959159981411+ }+ , PoolDelegator+ { _poolDelegatorAddress = "stake1uylayej7esmarzd4mk4aru37zh9yz0luj3g9fsvgpfaxulq564r5u"+ , _poolDelegatorLiveStake = 16958865648+ }+ , PoolDelegator+ { _poolDelegatorAddress = "stake1u8lr2pnrgf8f7vrs9lt79hc3sxm8s2w4rwvgpncks3axx6q93d4ck"+ , _poolDelegatorLiveStake = 18605647+ }+ ]++-- | Registration action of a pool+data PoolRegistrationAction = PoolRegistered | PoolDeregistered+ deriving stock (Show, Eq, Generic)++instance ToJSON PoolRegistrationAction where+ toJSON PoolRegistered = toJSON ("registered" :: Text)+ toJSON PoolDeregistered = toJSON ("deregistered" :: Text)+ toEncoding PoolRegistered = toEncoding ("registered" :: Text)+ toEncoding PoolDeregistered = toEncoding ("deregistered" :: Text)++instance FromJSON PoolRegistrationAction where+ parseJSON = withText "action" $ \case+ "registered" -> pure PoolRegistered+ "deregistered" -> pure PoolDeregistered+ x -> fail ("Expected registration action got " ++ show x)++instance ToSample PoolRegistrationAction where+ toSamples = pure $ samples [ PoolRegistered, PoolDeregistered ]++-- | Certificate update to the stake pool+data PoolUpdate = PoolUpdate+ { _poolUpdateTxHash :: TxHash -- ^ Transaction ID+ , _poolUpdateCertIndex :: Integer -- ^ Certificate within the transaction+ , _poolUpdateAction :: PoolRegistrationAction -- ^ Action in the certificate+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_poolUpdate", CamelToSnake]] PoolUpdate++instance ToSample PoolUpdate where+ toSamples = pure $ samples+ [ PoolUpdate+ { _poolUpdateTxHash = "6804edf9712d2b619edb6ac86861fe93a730693183a262b165fcc1ba1bc99cad"+ , _poolUpdateCertIndex = 0+ , _poolUpdateAction = PoolRegistered+ }+ , PoolUpdate+ { _poolUpdateTxHash = "9c190bc1ac88b2ab0c05a82d7de8b71b67a9316377e865748a89d4426c0d3005"+ , _poolUpdateCertIndex = 0+ , _poolUpdateAction = PoolDeregistered+ }+ , PoolUpdate+ { _poolUpdateTxHash = "e14a75b0eb2625de7055f1f580d70426311b78e0d36dd695a6bdc96c7b3d80e0"+ , _poolUpdateCertIndex = 1+ , _poolUpdateAction = PoolRegistered+ }+ ]
+ src/Blockfrost/Types/Cardano/Transactions.hs view
@@ -0,0 +1,359 @@+-- | Cardano Transactions responses++module Blockfrost.Types.Cardano.Transactions+ ( Transaction (..)+ , TransactionUtxos (..)+ , UtxoInput (..)+ , UtxoOutput (..)+ , TransactionStake (..)+ , TransactionDelegation (..)+ , TransactionWithdrawal (..)+ , Pot (..)+ , TransactionMir (..)+ , TransactionPoolUpdate (..)+ , PoolUpdateMetadata (..)+ , TransactionPoolRetiring (..)+ , TransactionMetaJSON (..)+ , TransactionMetaCBOR (..)+ ) where++import Data.Aeson (Value, object, (.=))+import Data.Text (Text)+import Deriving.Aeson+import qualified Money+import Servant.Docs (ToSample (..), samples, singleSample)++import Blockfrost.Types.Cardano.Pools+import Blockfrost.Types.Shared++-- | Information about a transaction+data Transaction = Transaction+ { _transactionHash :: Text -- ^ Transaction hash+ , _transactionBlock :: BlockHash -- ^ Block hash+ , _transactionBlockHeight :: Integer -- ^ Block number+ , _transactionSlot :: Slot -- ^ Slot number+ , _transactionIndex :: Integer -- ^ Transaction index within the block+ , _transactionOutputAmount :: [Amount] -- ^ Transaction outputs+ , _transactionFees :: Lovelaces -- ^ Fees of the transaction in Lovelaces+ , _transactionDeposit :: Lovelaces -- ^ Deposit within the transaction in Lovelaces+ , _transactionSize :: Integer -- ^ Size of the transaction in Bytes+ --_ TODO: Text Slots?+ , _transactionInvalidBefore :: Maybe Text -- ^ Left (included) endpoint of the timelock validity intervals+ , _transactionInvalidHereafter :: Maybe Text -- ^ Right (excluded) endpoint of the timelock validity intervals+ , _transactionUtxoCount :: Integer -- ^ Count of UTXOs within the transaction+ , _transactionWithdrawalCount :: Integer -- ^ Count of the withdrawals within the transaction+ , _transactionMirCertCount :: Integer -- ^ Count of the MIR certificates within the transaction+ , _transactionDelegationCount :: Integer -- ^ Count of the delegations within the transaction+ , _transactionStakeCertCount :: Integer -- ^ Count of the stake keys (de)registration and delegation certificates within the transaction+ , _transactionPoolUpdateCount :: Integer -- ^ Count of the stake pool registration and update certificates within the transaction+ , _transactionPoolRetireCount :: Integer -- ^ Count of the stake pool retirement certificates within the transaction+ , _transactionAssetMintOrBurnCount :: Integer -- ^ Count of asset mints and burns within the transaction+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_transaction", CamelToSnake]] Transaction++instance ToSample Transaction where+ toSamples = pure $ singleSample+ Transaction+ { _transactionHash = "1e043f100dce12d107f679685acd2fc0610e10f72a92d412794c9773d11d8477"+ , _transactionBlock = "356b7d7dbb696ccd12775c016941057a9dc70898d87a63fc752271bb46856940"+ , _transactionBlockHeight = 123456+ , _transactionSlot = 42000000+ , _transactionIndex = 1+ , _transactionOutputAmount = sampleAmounts+ , _transactionFees = 182485+ , _transactionDeposit = 0+ , _transactionSize = 433+ , _transactionInvalidBefore = Nothing+ , _transactionInvalidHereafter = Just "13885913"+ , _transactionUtxoCount = 4+ , _transactionWithdrawalCount = 0+ , _transactionMirCertCount = 0+ , _transactionDelegationCount = 0+ , _transactionStakeCertCount = 0+ , _transactionPoolUpdateCount = 0+ , _transactionPoolRetireCount = 0+ , _transactionAssetMintOrBurnCount = 0+ }++-- | Transaction input UTxO+data UtxoInput = UtxoInput+ { _utxoInputAddress :: Address -- ^ Input address+ , _utxoInputAmount :: [Amount]+ , _utxoInputTxHash :: Text -- ^ Hash of the UTXO transaction+ , _utxoInputOutputIndex :: Integer -- ^ UTXO index in the transaction+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_utxoInput", CamelToSnake]] UtxoInput++instance ToSample UtxoInput where+ toSamples = pure $ singleSample utxoInSample++utxoInSample :: UtxoInput+utxoInSample =+ UtxoInput+ { _utxoInputAddress = "addr1q9ld26v2lv8wvrxxmvg90pn8n8n5k6tdst06q2s856rwmvnueldzuuqmnsye359fqrk8hwvenjnqultn7djtrlft7jnq7dy7wv"+ , _utxoInputAmount = sampleAmounts+ , _utxoInputTxHash = "1a0570af966fb355a7160e4f82d5a80b8681b7955f5d44bec0dce628516157f0"+ , _utxoInputOutputIndex = 0+ }++-- | Transaction output UTxO+data UtxoOutput = UtxoOutput+ { _utxoOutputAddress :: Address -- ^ Output address+ , _utxoOutputAmount :: [Amount]+ } deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_utxoOutput", CamelToSnake]] UtxoOutput++instance ToSample UtxoOutput where+ toSamples = pure $ singleSample utxoOutSample++utxoOutSample :: UtxoOutput+utxoOutSample =+ UtxoOutput+ { _utxoOutputAddress = "addr1q9ld26v2lv8wvrxxmvg90pn8n8n5k6tdst06q2s856rwmvnueldzuuqmnsye359fqrk8hwvenjnqultn7djtrlft7jnq7dy7wv"+ , _utxoOutputAmount = sampleAmounts+ }++-- | Transaction UTxOs+data TransactionUtxos = TransactionUtxos+ { _transactionUtxosHash :: TxHash -- ^ Transaction hash+ , _transactionUtxosInputs :: [UtxoInput]+ , _transactionUtxosOutputs :: [UtxoOutput]+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_transactionUtxos", CamelToSnake]] TransactionUtxos++instance ToSample TransactionUtxos where+ toSamples = pure $ singleSample+ TransactionUtxos+ { _transactionUtxosHash = "1e043f100dce12d107f679685acd2fc0610e10f72a92d412794c9773d11d8477"+ , _transactionUtxosInputs = pure utxoInSample+ , _transactionUtxosOutputs = pure utxoOutSample+ }++sampleAmounts :: [Amount]+sampleAmounts =+ [ AdaAmount 42000000+ , AssetAmount+ $ Money.mkSomeDiscrete+ "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e"+ unitScale+ 12+ ]++-- | Information about (de-)registration of a stake address+-- within a transaction+data TransactionStake = TransactionStake+ { _transactionStakeCertIndex :: Integer -- ^ Index of the certificate within the transaction+ , _transactionStakeAddress :: Address -- ^ Delegation stake address+ , _transactionStakeRegistration :: Bool -- ^ Registration boolean, false if deregistration+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_transactionStake", CamelToSnake]] TransactionStake++instance ToSample TransactionStake where+ toSamples = pure $ singleSample+ TransactionStake+ { _transactionStakeCertIndex = 0+ , _transactionStakeAddress = "stake1u9t3a0tcwune5xrnfjg4q7cpvjlgx9lcv0cuqf5mhfjwrvcwrulda"+ , _transactionStakeRegistration = True+ }++-- | Information about delegation certificates of a specific transaction+data TransactionDelegation = TransactionDelegation+ { _transactionDelegationCertIndex :: Integer -- ^ Index of the certificate within the transaction+ , _transactionDelegationAddress :: Address -- ^ Delegation stake address+ , _transactionDelegationPoolId :: PoolId -- ^ Bech32 ID of delegated stake pool+ , _transactionDelegationActiveEpoch :: Epoch -- ^ Epoch in which the delegation becomes active+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_transactionDelegation", CamelToSnake]] TransactionDelegation++instance ToSample TransactionDelegation where+ toSamples = pure $ singleSample+ TransactionDelegation+ { _transactionDelegationCertIndex = 0+ , _transactionDelegationAddress = "stake1u9t3a0tcwune5xrnfjg4q7cpvjlgx9lcv0cuqf5mhfjwrvcwrulda"+ , _transactionDelegationPoolId = "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ , _transactionDelegationActiveEpoch = 210+ }++-- | Information about withdrawals of a specific transaction+data TransactionWithdrawal = TransactionWithdrawal+ { _transactionWithdrawalAddress :: Address -- ^ Bech32 withdrawal address+ , _transactionWithdrawalAmount :: Lovelaces -- ^ Withdrawal amount in Lovelaces+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_transactionWithdrawal", CamelToSnake]] TransactionWithdrawal++instance ToSample TransactionWithdrawal where+ toSamples = pure $ singleSample+ TransactionWithdrawal+ { _transactionWithdrawalAddress = "stake1u9r76ypf5fskppa0cmttas05cgcswrttn6jrq4yd7jpdnvc7gt0yc"+ , _transactionWithdrawalAmount = 431833601+ }++-- | Pot from which MIRs are transferred+data Pot = Reserve | Treasury+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[ConstructorTagModifier '[ToLower]] Pot++instance ToSample Pot where+ toSamples = pure $ samples [ Reserve, Treasury ]++-- | Information about Move Instantaneous Rewards (MIRs) of a specific transaction+data TransactionMir = TransactionMir+ { _transactionMirPot :: Pot -- ^ Source of MIR funds+ , _transactionMirCertIndex :: Integer -- ^ Index of the certificate within the transaction+ , _transactionMirAddress :: Address -- ^ Bech32 stake address+ , _transactionMirAmount :: Lovelaces -- ^ MIR amount in Lovelaces+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_transactionMir", CamelToSnake]] TransactionMir++instance ToSample TransactionMir where+ toSamples = pure $ singleSample+ TransactionMir+ { _transactionMirPot = Reserve+ , _transactionMirCertIndex = 0+ , _transactionMirAddress = "stake1u9r76ypf5fskppa0cmttas05cgcswrttn6jrq4yd7jpdnvc7gt0yc"+ , _transactionMirAmount = 431833601+ }++-- | Information about stake pool registration and update certificates+-- of a specific transaction+data TransactionPoolUpdate = TransactionPoolUpdate+ { _transactionPoolUpdateCertIndex :: Integer -- ^ Index of the certificate within the transaction+ , _transactionPoolUpdatePoolId :: PoolId -- ^ Bech32 encoded pool ID+ , _transactionPoolUpdateVrfKey :: Text -- ^ VRF key hash+ , _transactionPoolUpdatePledge :: Lovelaces -- ^ Stake pool certificate pledge in Lovelaces+ , _transactionPoolUpdateMarginCost :: Double -- ^ Margin tax cost of the stake pool+ , _transactionPoolUpdateFixedCost :: Lovelaces -- ^ Fixed tax cost of the stake pool in Lovelaces+ , _transactionPoolUpdateRewardAccount :: Address -- ^ Bech32 reward account of the stake pool+ , _transactionPoolUpdateOwners :: [Address]+ , _transactionPoolUpdateMetadata :: Maybe PoolUpdateMetadata+ , _transactionPoolUpdateRelays :: [PoolRelay]+ , _transactionPoolUpdateActiveEpoch :: Epoch -- ^ Epoch that the delegation becomes active+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_transactionPoolUpdate", CamelToSnake]] TransactionPoolUpdate++instance ToSample TransactionPoolUpdate where+ toSamples = pure $ singleSample+ TransactionPoolUpdate+ { _transactionPoolUpdateCertIndex = 0+ , _transactionPoolUpdatePoolId = "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ , _transactionPoolUpdateVrfKey = "0b5245f9934ec2151116fb8ec00f35fd00e0aa3b075c4ed12cce440f999d8233"+ , _transactionPoolUpdatePledge = 5000000000+ , _transactionPoolUpdateMarginCost = 0.05+ , _transactionPoolUpdateFixedCost = 340000000+ , _transactionPoolUpdateRewardAccount = "stake1uxkptsa4lkr55jleztw43t37vgdn88l6ghclfwuxld2eykgpgvg3f"+ , _transactionPoolUpdateOwners = [ "stake1u98nnlkvkk23vtvf9273uq7cph5ww6u2yq2389psuqet90sv4xv9v" ]+ , _transactionPoolUpdateMetadata = Just samplePoolUpdateMetadata+ , _transactionPoolUpdateRelays = [ samplePoolRelay ]+ , _transactionPoolUpdateActiveEpoch = 210+ }++-- | Information about stake pool retirements+-- within a specific transaction+data TransactionPoolRetiring = TransactionPoolRetiring+ { _transactionPoolRetiringCertIndex :: Integer -- ^ Index of the certificate within the transaction+ , _transactionPoolRetiringPoolId :: PoolId -- ^ Bech32 stake pool ID+ , _transactionPoolRetiringRetiringEpoch :: Epoch -- ^ Retiring epoch+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_transactionPoolRetiring", CamelToSnake]] TransactionPoolRetiring++instance ToSample TransactionPoolRetiring where+ toSamples = pure $ singleSample+ TransactionPoolRetiring+ { _transactionPoolRetiringCertIndex = 0+ , _transactionPoolRetiringPoolId = "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ , _transactionPoolRetiringRetiringEpoch = 216+ }++-- | Transaction metadata in JSON+data TransactionMetaJSON = TransactionMetaJSON+ { _transactionMetaJSONLabel :: Text -- ^ Metadata label+ , _transactionMetaJSONJSONMetadata :: Maybe Value -- ^ Content of the JSON metadata+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_transactionMetaJSON", CamelToSnake]] TransactionMetaJSON++instance ToSample TransactionMetaJSON where+ toSamples =+ let oracleMeta val =+ object [+ "ADAUSD" .=+ [ object [ "value" .= (val :: Text)+ , "source" .= ("ergoOracles" :: Text) ]+ ]+ ]+ in pure $ samples+ [ TransactionMetaJSON+ "1967"+ (Just $ object+ [ "metadata" .= ("https://nut.link/metadata.json" :: Text)+ , "hash" .= ("6bf124f217d0e5a0a8adb1dbd8540e1334280d49ab861127868339f43b3948af" :: Text)+ ])+ , TransactionMetaJSON+ "1968"+ (Just $ oracleMeta "0.15409850555139935")+ ]++-- | Transaction metadata in CBOR+data TransactionMetaCBOR = TransactionMetaCBOR+ { _transactionMetaCBORLabel :: Text -- ^ Metadata label+ , _transactionMetaCBORCBORMetadata :: Maybe Text -- ^ Content of the CBOR metadata+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_transactionMetaCBOR", CamelToSnake]] TransactionMetaCBOR++instance ToSample TransactionMetaCBOR where+ toSamples = pure $ singleSample $+ TransactionMetaCBOR+ "1968"+ (Just "\\xa100a16b436f6d62696e6174696f6e8601010101010c")++-- | Update of a pool metadata+data PoolUpdateMetadata = PoolUpdateMetadata+ { _poolUpdateMetadataUrl :: Maybe Text -- ^ URL to the stake pool metadata+ , _poolUpdateMetadataHash :: Maybe Text -- ^ Hash of the metadata file+ , _poolUpdateMetadataTicker :: Maybe Text -- ^ Ticker of the stake pool+ , _poolUpdateMetadataName :: Maybe Text -- ^ Name of the stake pool+ , _poolUpdateMetadataDescription :: Maybe Text -- ^ Description of the stake pool+ , _poolUpdateMetadataHomepage :: Maybe Text -- ^ Home page of the stake pool+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_poolUpdateMetadata", CamelToSnake]] PoolUpdateMetadata++-- Note: Similar to PoolMetadata but w/o PoolId and Hex fields++instance ToSample PoolUpdateMetadata where+ toSamples = pure $ singleSample samplePoolUpdateMetadata++samplePoolUpdateMetadata :: PoolUpdateMetadata+samplePoolUpdateMetadata =+ PoolUpdateMetadata+ { _poolUpdateMetadataUrl = Just "https://stakenuts.com/mainnet.json"+ , _poolUpdateMetadataHash = Just "47c0c68cb57f4a5b4a87bad896fc274678e7aea98e200fa14a1cb40c0cab1d8c"+ , _poolUpdateMetadataTicker = Just "NUTS"+ , _poolUpdateMetadataName = Just "Stake Nuts"+ , _poolUpdateMetadataDescription = Just "The best pool ever"+ , _poolUpdateMetadataHomepage = Just "https://stakentus.com/"+ }
+ src/Blockfrost/Types/Common.hs view
@@ -0,0 +1,103 @@+-- | Types for common servics++module Blockfrost.Types.Common+ ( URLVersion (..)+ , Healthy (..)+ , ServerTime (..)+ , Metric (..)+ ) where++import Blockfrost.Types.Shared+import Data.Aeson+import Data.Text (Text)+import Deriving.Aeson+import Servant.Docs (ToSample (..), samples, singleSample)+import Test.QuickCheck.Arbitrary (Arbitrary (..))+import Test.QuickCheck.Instances ()+import Test.QuickCheck.Modifiers++-- | Root endpoint reply+data URLVersion = URLVersion+ { _urlVersionUrl :: Text+ , _urlVersionVersion :: Text+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_urlVersion", CamelToSnake]] URLVersion++instance ToSample URLVersion where+ toSamples = pure $ singleSample $+ URLVersion "http://blockfrost.io" "0.0.0"++-- | Health endpoint reply+newtype Healthy = Healthy+ { isHealthy :: Bool+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[CamelToSnake]] Healthy++instance ToSample Healthy where+ toSamples = pure $ singleSample $ Healthy False++-- | Health clock endpoint reply+newtype ServerTime = ServerTime+ { serverTime :: POSIXTime+ }+ deriving stock (Show, Eq, Generic)++instance FromJSON ServerTime where+ parseJSON = withObject "ServerTime"+ $ \v -> v .: "server_time"+ >>= \t -> ServerTime . millisecondsToPosix <$> parseJSON @Integer t++instance ToJSON ServerTime where+ toJSON (ServerTime t) =+ object [+ "server_time" .= posixToMilliseconds t+ ]++instance ToSample ServerTime where+ toSamples _ = singleSample $ ServerTime $ millisecondsToPosix 1603400958947++instance Arbitrary ServerTime where+ arbitrary = do+ Positive (n :: Integer) <- arbitrary+ pure $ ServerTime (fromInteger n / 1000)++-- | Metrics response+data Metric = Metric {+ _metricTime :: POSIXTime+ , _metricCalls :: Integer+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_metric", CamelToSnake]] Metric++instance ToSample Metric where+ toSamples _ = samples $+ [ Metric 1612543884 42+ , Metric 1614523884 6942+ ]++-- Re-use @Metric@ for response with endpoint field+instance {-# OVERLAPS #-} ToJSON (Text, Metric) where+ toJSON (enp, m) = case toJSON m of+ (Object o) -> Object (o <> ("endpoint" .= (toJSON enp)))+ _ -> error "Absurd"++instance {-# OVERLAPS #-} FromJSON (Text, Metric) where+ parseJSON v@(Object o) = do+ enp <- o .: "endpoint"+ ticker <- parseJSON v+ return (enp, ticker)+ parseJSON _ = fail "Unexpected type for (Text, Metric)"++instance {-# OVERLAPS #-} ToSample (Text, Metric) where+ toSamples _ = samples $+ [ ("block", Metric 1612543814 182)+ , ("epoch", Metric 1612543814 42)+ , ("block", Metric 1612543812 775)+ , ("epoch", Metric 1612523884 4)+ , ("block", Metric 1612553884 89794)+ ]
+ src/Blockfrost/Types/IPFS.hs view
@@ -0,0 +1,102 @@+-- | Types for IPFS servics++module Blockfrost.Types.IPFS+ ( IPFSAdd (..)+ , IPFSPinChange (..)+ , IPFSPin (..)+ , PinState (..)+ , IPFSData (..)+ ) where++import Blockfrost.Types.Shared+import Data.Aeson+import Data.ByteString.Lazy (ByteString)+import Data.Text (Text)+import Deriving.Aeson+import Servant.API+import Servant.Docs (ToSample (..), singleSample)++-- | IPFS Add response+data IPFSAdd = IPFSAdd+ { _ipfsAddName :: Text -- ^ Name of the file+ , _ipfsAddIpfsHash :: Text -- ^ IPFS hash of the file+ , _ipfsAddSize :: Quantity -- ^ Size of the IPFS node+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_ipfsAdd", CamelToSnake]] IPFSAdd++instance ToSample IPFSAdd where+ toSamples = pure $ singleSample $+ IPFSAdd+ "README.md"+ "QmZbHqiCxKEVX7QfijzJTkZiSi3WEVTcvANgNAWzDYgZDr"+ 125297++-- | State of the pinned object,+-- which is @Queued@ when we are retriving object.+--+-- If this is successful the state is changed to @Pinned@ or @Failed@ if not.+-- The state @Gc@ means the pinned item has been garbage collected+-- due to account being over storage quota or after it has been+-- moved to @Unpinned@ state by removing the object pin.+data PinState = Queued | Pinned | Unpinned | Failed | Gc+ deriving (Eq, Show, Ord, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[ConstructorTagModifier '[ToLower]] PinState+++-- | IPFS Pin Add response+data IPFSPinChange = IPFSPinChange+ { _ipfsPinChangeIpfsHash :: Text -- ^ IPFS hash of pinned object+ , _ipfsPinChangeState :: PinState -- ^ State of the pin action+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_ipfsPinChange", CamelToSnake]] IPFSPinChange++instance ToSample IPFSPinChange where+ toSamples = pure $ singleSample $+ IPFSPinChange+ "QmPojRfAXYAXV92Dof7gtSgaVuxEk64xx9CKvprqu9VwA8"+ Queued++-- | IPFS Pin information+data IPFSPin = IPFSPin+ { _ipfsPinTimeCreated :: POSIXTime -- ^ Creation time of the IPFS object on our backends+ , _ipfsPinTimePinned :: POSIXTime -- ^ Pin time of the IPFS object on our backends+ , _ipfsPinIpfsHash :: Text -- ^ IPFS hash of the pinned object+ , _ipfsPinSize :: Quantity -- ^ Size of the IPFS node+ , _ipfsPinState :: PinState -- ^ State of the pinned object+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_ipfsPin", CamelToSnake]] IPFSPin++instance ToSample IPFSPin where+ toSamples = pure $ singleSample $+ IPFSPin+ { _ipfsPinTimeCreated = 1615551024+ , _ipfsPinTimePinned = 1615551024+ , _ipfsPinIpfsHash = "QmdVMnULrY95mth2XkwjxDtMHvzuzmvUPTotKE1tgqKbCx"+ , _ipfsPinSize = 1615551024+ , _ipfsPinState = Pinned+ }++newtype IPFSData = IPFSData ByteString+ deriving (Show, Eq, Generic)++instance ToSample IPFSData where+ toSamples = pure $ singleSample $ IPFSData "sample ipfs bytestring"++instance MimeRender OctetStream IPFSData where+ mimeRender _ (IPFSData cs) = cs++instance MimeUnrender OctetStream IPFSData where+ mimeUnrender _ lbs = pure $ IPFSData lbs++instance MimeRender PlainText IPFSData where+ mimeRender _ (IPFSData cs) = cs++instance MimeUnrender PlainText IPFSData where+ mimeUnrender _ lbs = pure $ IPFSData lbs
+ src/Blockfrost/Types/NutLink.hs view
@@ -0,0 +1,95 @@+-- | Types for Nut.link servics++module Blockfrost.Types.NutLink+ ( NutlinkAddress (..)+ , NutlinkAddressTicker (..)+ , NutlinkTicker (..)+ ) where++import Blockfrost.Types.Shared+import Data.Aeson+import Data.Text (Text)+import Deriving.Aeson+import Servant.Docs (ToSample (..), samples, singleSample)++-- | Specific address metadata+data NutlinkAddress = NutlinkAddress+ { _nutlinkAddressAddress :: Address -- ^ Bech32 encoded address+ , _nutlinkAddressMetadataUrl :: Text -- ^ URL of the specific metadata file+ , _nutlinkAddressMetadataHash :: Text -- ^ Hash of the metadata file+ , _nutlinkAddressMetadata :: Maybe Value -- ^ The cached metadata of the metadata_url file.+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_nutlinkAddress", CamelToSnake]] NutlinkAddress++instance ToSample NutlinkAddress where+ toSamples = pure $ singleSample $+ NutlinkAddress+ { _nutlinkAddressAddress = "addr1qxqs59lphg8g6qndelq8xwqn60ag3aeyfcp33c2kdp46a09re5df3pzwwmyq946axfcejy5n4x0y99wqpgtp2gd0k09qsgy6pz"+ , _nutlinkAddressMetadataUrl = "https://nut.link/metadata.json"+ , _nutlinkAddressMetadataHash = "6bf124f217d0e5a0a8adb1dbd8540e1334280d49ab861127868339f43b3948af"+ , _nutlinkAddressMetadata = pure $ object []+ }++-- | Ticker for specific metadata oracle+data NutlinkAddressTicker = NutlinkAddressTicker+ { _nutlinkAddressTickerName :: Text -- ^ Name of the ticker+ , _nutlinkAddressTickerCount :: Integer -- ^ Number of ticker records+ , _nutlinkAddressTickerLatestBlock :: Integer -- ^ Block height of the latest record+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_nutlinkAddressTicker", CamelToSnake]] NutlinkAddressTicker++instance ToSample NutlinkAddressTicker where+ toSamples = pure $ samples $+ [ NutlinkAddressTicker+ { _nutlinkAddressTickerName = "ADAUSD"+ , _nutlinkAddressTickerCount = 1980038+ , _nutlinkAddressTickerLatestBlock = 2657092+ }+ , NutlinkAddressTicker+ { _nutlinkAddressTickerName = "ADAEUR"+ , _nutlinkAddressTickerCount = 1980038+ , _nutlinkAddressTickerLatestBlock = 2657092+ }+ , NutlinkAddressTicker+ { _nutlinkAddressTickerName = "ADABTC"+ , _nutlinkAddressTickerCount = 1980038+ , _nutlinkAddressTickerLatestBlock = 2657092+ }+ ]++-- | Specific ticker record+data NutlinkTicker = NutlinkTicker+ { _nutlinkTickerTxHash :: TxHash -- ^ Hash of the transaction+ , _nutlinkTickerBlockHeight :: Integer -- ^ Block height of the record+ , _nutlinkTickerTxIndex :: Integer -- ^ Transaction index within the block+ , _nutlinkTickerPayload :: Value -- ^ Content of the ticker+ }+ deriving stock (Show, Eq, Generic)+ deriving (FromJSON, ToJSON)+ via CustomJSON '[FieldLabelModifier '[StripPrefix "_nutlinkTicker", CamelToSnake]] NutlinkTicker++instance ToSample NutlinkTicker where+ toSamples = pure $ singleSample $+ NutlinkTicker+ { _nutlinkTickerTxHash = "e8073fd5318ff43eca18a852527166aa8008bee9ee9e891f585612b7e4ba700b"+ , _nutlinkTickerBlockHeight = 2657092+ , _nutlinkTickerTxIndex = 8+ , _nutlinkTickerPayload = object []+ }++-- Re-use @NutlinkTicker@ for response with address field+instance {-# OVERLAPS #-} ToJSON (Address, NutlinkTicker) where+ toJSON (addr, nt) = case toJSON nt of+ (Object o) -> Object (o <> ("address" .= (toJSON addr)))+ _ -> error "Absurd"++instance {-# OVERLAPS #-} FromJSON (Address, NutlinkTicker) where+ parseJSON v@(Object o) = do+ addr <- o .: "address"+ ticker <- parseJSON v+ return (addr, ticker)+ parseJSON _ = fail "Unexpected type for (Address, NutlinkTicker)"
@@ -0,0 +1,35 @@+-- | Shared types and utilities++module Blockfrost.Types.Shared+ ( module Blockfrost.Types.Shared.Ada+ , module Blockfrost.Types.Shared.Address+ , module Blockfrost.Types.Shared.Amount+ , module Blockfrost.Types.Shared.AssetId+ , module Blockfrost.Types.Shared.BlockHash+ , module Blockfrost.Types.Shared.BlockIndex+ , module Blockfrost.Types.Shared.CBOR+ , module Blockfrost.Types.Shared.Epoch+ , module Blockfrost.Types.Shared.Opts+ , module Blockfrost.Types.Shared.POSIXMillis+ , module Blockfrost.Types.Shared.PolicyId+ , module Blockfrost.Types.Shared.PoolId+ , module Blockfrost.Types.Shared.Quantity+ , module Blockfrost.Types.Shared.Slot+ , module Blockfrost.Types.Shared.TxHash+ ) where++import Blockfrost.Types.Shared.Ada+import Blockfrost.Types.Shared.Address+import Blockfrost.Types.Shared.Amount+import Blockfrost.Types.Shared.AssetId+import Blockfrost.Types.Shared.BlockHash+import Blockfrost.Types.Shared.BlockIndex+import Blockfrost.Types.Shared.CBOR+import Blockfrost.Types.Shared.Epoch+import Blockfrost.Types.Shared.Opts+import Blockfrost.Types.Shared.POSIXMillis+import Blockfrost.Types.Shared.PolicyId+import Blockfrost.Types.Shared.PoolId+import Blockfrost.Types.Shared.Quantity+import Blockfrost.Types.Shared.Slot+import Blockfrost.Types.Shared.TxHash
@@ -0,0 +1,55 @@+-- | Lovelaces++{-# LANGUAGE NumericUnderscores #-}+{-# LANGUAGE UndecidableInstances #-}++module Blockfrost.Types.Shared.Ada+ ( Lovelaces+ ) where++import Data.Aeson (Encoding, FromJSON (..), ToJSON (..), Value)+import Data.Proxy (Proxy (..))+import Data.Text (Text)+import GHC.TypeLits (KnownSymbol)+import qualified Money+import Servant.Docs (ToSample (..), samples)++type Lovelaces = Money.Discrete "ADA" "lovelace"++instance (KnownSymbol cur, Money.GoodScale sc) => ToSample (Money.Discrete' cur sc) where+ toSamples _ = samples $ map Money.discrete [+ 592661+ , 3592661+ , 1337+ , 42+ , 0+ , 3000100+ ]++lovelaceDecimalConfig :: Money.DecimalConf+lovelaceDecimalConfig =+ Money.defaultDecimalConf+ { Money.decimalConf_digits = 0+ , Money.decimalConf_scale =+ Money.scale (Proxy @(Money.UnitScale "ADA" "lovelace"))+ }++instance ToJSON (Money.Discrete' "ADA" '(1_000_000, 1)) where+ toJSON =+ (toJSON :: Text -> Value)+ . Money.discreteToDecimal+ lovelaceDecimalConfig+ Money.Round+ toEncoding =+ (toEncoding :: Text -> Encoding)+ . Money.discreteToDecimal+ lovelaceDecimalConfig+ Money.Round++instance FromJSON (Money.Discrete' "ADA" '(1000000, 1)) where+ parseJSON v = do+ t <- parseJSON v+ maybe+ (fail "Can't parse Discrete ADA value")+ pure+ (Money.discreteFromDecimal lovelaceDecimalConfig t)
@@ -0,0 +1,37 @@+-- | Address newtype++module Blockfrost.Types.Shared.Address+ where++import Data.Aeson (FromJSON, ToJSON)+import Data.String (IsString (..))+import Data.Text (Text)+import qualified Data.Text+import GHC.Generics+import Servant.API (Capture, FromHttpApiData (..), ToHttpApiData (..))+import Servant.Docs (DocCapture (..), ToCapture (..), ToSample (..), samples)++newtype Address = Address Text+ deriving stock (Eq, Show, Generic)+ deriving newtype (FromHttpApiData, ToHttpApiData, FromJSON, ToJSON)++mkAddress :: Text -> Address+mkAddress = Address++unAddress :: Address -> Text+unAddress (Address a) = a++instance IsString Address where+ fromString = mkAddress . Data.Text.pack++instance ToCapture (Capture "address" Address) where+ toCapture _ = DocCapture "address" "Bech32 encoded address"++instance ToCapture (Capture "stake_address" Address) where+ toCapture _ = DocCapture "stake_address" "Bech32 stake address"++instance ToSample Address where+ toSamples = pure $ samples [+ "addr1qxqs59lphg8g6qndelq8xwqn60ag3aeyfcp33c2kdp46a09re5df3pzwwmyq946axfcejy5n4x0y99wqpgtp2gd0k09qsgy6pz"+ , "stake1ux3g2c9dx2nhhehyrezyxpkstartcqmu9hk63qgfkccw5rqttygt7"+ ]
@@ -0,0 +1,79 @@+-- | Amount sum type++{-# LANGUAGE NumericUnderscores #-}++module Blockfrost.Types.Shared.Amount+ where++import Blockfrost.Types.Shared.Ada (Lovelaces)+import Data.Aeson+ ( FromJSON (..)+ , ToJSON (..)+ , Value (Object)+ , object+ , withObject+ , (.:)+ , (.=)+ )+import GHC.Generics+import qualified Money+import Servant.Docs (ToSample (..), samples)+import qualified Text.Read++-- | Amount, which is either `AdaAmount Lovelaces` representing+-- amount of lovelaces or `AssetAmount SomeDiscrete` for asset amounts,+-- identified by concatenation of asset policy ID+-- and hex-encoded asset_name+data Amount =+ AdaAmount Lovelaces+ | AssetAmount Money.SomeDiscrete+ deriving (Eq, Show, Ord, Generic)++-- | SomeDiscrete values always use scale of 1+unitScale :: Money.Scale+unitScale = let (Just s) = Money.scaleFromRational 1 in s++instance ToJSON Money.SomeDiscrete where+ toJSON sd =+ object [ "unit" .= Money.someDiscreteCurrency sd+ , "quantity" .= show (Money.someDiscreteAmount sd)+ ]++instance FromJSON Money.SomeDiscrete where+ parseJSON = withObject "amount" $ \o -> do+ u <- o .: "unit"+ (strQuant :: String) <- o .: "quantity"+ case Text.Read.readMaybe strQuant of+ Nothing -> fail "Unable to read quantity as Integer"+ Just quant -> pure $ Money.mkSomeDiscrete u unitScale quant++instance ToJSON Amount where+ toJSON (AdaAmount lovelaces) =+ object [ "unit" .= ("lovelace" :: String)+ , "quantity" .= toJSON lovelaces+ ]+ toJSON (AssetAmount av) = toJSON av++instance FromJSON Amount where+ parseJSON x@(Object o) = do+ (u :: String) <- o .: "unit"+ v <- o .: "quantity"+ case u of+ "lovelace" -> AdaAmount <$> parseJSON v+ _ -> AssetAmount <$> parseJSON x+ parseJSON other = fail $ "Amount expecting object, got" ++ show other++instance ToSample Amount where+ toSamples = pure $ samples+ [ AdaAmount 42000000+ , AssetAmount+ $ Money.toSomeDiscrete+ (12 :: Money.Discrete'+ "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e"+ '(1,1))+ , AssetAmount+ $ Money.toSomeDiscrete+ (18605647 :: Money.Discrete'+ "6804edf9712d2b619edb6ac86861fe93a730693183a262b165fcc1ba1bc99cad"+ '(1,1))+ ]
@@ -0,0 +1,36 @@+-- | AssetIds++module Blockfrost.Types.Shared.AssetId+ where++import Data.Aeson (FromJSON, ToJSON)+import Data.String (IsString (..))+import Data.Text (Text)+import qualified Data.Text+import GHC.Generics+import Servant.API (Capture, FromHttpApiData (..), ToHttpApiData (..))+import Servant.Docs (DocCapture (..), ToCapture (..), ToSample (..), samples)++-- | Concatenation of asset policy ID+-- and hex-encoded asset name+newtype AssetId = AssetId Text+ deriving stock (Eq, Show, Generic)+ deriving newtype (FromHttpApiData, ToHttpApiData, FromJSON, ToJSON)++mkAssetId :: Text -> AssetId+mkAssetId = AssetId++unAssetId :: AssetId -> Text+unAssetId (AssetId a) = a++instance IsString AssetId where+ fromString = mkAssetId . Data.Text.pack++instance ToCapture (Capture "asset" AssetId) where+ toCapture _ = DocCapture "asset" "Concatenation of the policy_id and hex-encoded asset_name"++instance ToSample AssetId where+ toSamples = pure $ samples [+ "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e"+ , "6804edf9712d2b619edb6ac86861fe93a730693183a262b165fcc1ba1bc99cad"+ ]
@@ -0,0 +1,50 @@+-- | Hash of the block++module Blockfrost.Types.Shared.BlockHash+ where++import Data.Aeson (FromJSON, ToJSON)+import Data.Char (isDigit)+import Data.String (IsString (..))+import Data.Text (Text)+import qualified Data.Text+import GHC.Generics+import Servant.API (Capture, FromHttpApiData (..), ToHttpApiData (..))+import Servant.Docs (DocCapture (..), ToCapture (..), ToSample (..), samples)+import qualified Text.Read++newtype BlockHash = BlockHash Text+ deriving stock (Eq, Show, Generic)+ deriving newtype (FromHttpApiData, ToHttpApiData, FromJSON, ToJSON)++mkBlockHash :: Text -> BlockHash+mkBlockHash = BlockHash++unBlockHash :: BlockHash -> Text+unBlockHash (BlockHash a) = a++instance IsString BlockHash where+ fromString = mkBlockHash . Data.Text.pack++instance ToCapture (Capture "block_hash" BlockHash) where+ toCapture _ = DocCapture "block_hash" "Specific block hash"++instance ToSample BlockHash where+ toSamples = pure $ samples+ [ "d0fa315687e99ccdc96b14cc2ea74a767405d64427b648c470731a9b69e4606e"+ , "38bc6efb92a830a0ed22a64f979d120d26483fd3c811f6622a8c62175f530878"+ , "f3258fcd8b975c061b4fcdcfcbb438807134d6961ec278c200151274893b6b7d"+ ]++instance ToCapture (Capture "hash_or_number" (Either Integer BlockHash)) where+ toCapture _ = DocCapture "hash_or_number" "Hash or number of the requested block."++instance {-# OVERLAPS #-} ToHttpApiData (Either Integer BlockHash) where+ toUrlPiece = either (Data.Text.pack . show) unBlockHash++instance {-# OVERLAPS #-} FromHttpApiData (Either Integer BlockHash) where+ parseUrlPiece x | Data.Text.all isDigit x =+ case Text.Read.readMaybe (Data.Text.unpack x) of+ Nothing -> Left "Unable to read block id"+ Just blockId -> pure (Left blockId)+ parseUrlPiece x | otherwise = pure (Right (BlockHash x))
@@ -0,0 +1,60 @@+-- | BlockIndex query parameter++module Blockfrost.Types.Shared.BlockIndex+ where++import qualified Data.Text+import GHC.Generics+import Servant.API (FromHttpApiData (..), QueryParam, ToHttpApiData (..))+import Servant.Docs+ ( DocQueryParam (..)+ , ParamKind (..)+ , ToParam (..)+ , ToSample (..)+ , samples+ )++-- | Block height (number) and optional index+data BlockIndex = BlockIndex {+ blockIndexHeight :: Integer+ , blockIndexIndex :: Maybe Integer+ }+ deriving stock (Eq, Show, Generic)++instance ToHttpApiData BlockIndex where+ toUrlPiece bi = Data.Text.pack $+ show (blockIndexHeight bi)+ <> maybe mempty ((':':) .show) (blockIndexIndex bi)++instance FromHttpApiData BlockIndex where+ parseUrlPiece x = case Data.Text.splitOn ":" x of+ [""] -> Left "Empty block index"+ [bh] -> (`BlockIndex` Nothing) <$> parseUrlPiece bh+ [bh, idx] -> BlockIndex <$> parseUrlPiece bh <*> (Just <$> parseUrlPiece idx)+ _ -> Left "Invalid block index"++instance ToParam (QueryParam "from" BlockIndex) where+ toParam _ =+ DocQueryParam+ "blockIndex"+ []+ "The block number and optionally also index from which (inclusive)\+ \ to start search for results, concatenated using colon.\+ \ Has to be lower than or equal to `to` parameter."+ Normal++instance ToParam (QueryParam "to" BlockIndex) where+ toParam _ =+ DocQueryParam+ "blockIndex"+ []+ "The block number and optionally also index from which (inclusive)\+ \ to end the search for results, concatenated using colon.\+ \ Has to be higher than or equal to `from` parameter."+ Normal++instance ToSample BlockIndex where+ toSamples = pure $ samples [+ BlockIndex 892961 Nothing+ , BlockIndex 9999269 (Just 10)+ ]
@@ -0,0 +1,27 @@+-- | CBOR Servant support and wrapper type++module Blockfrost.Types.Shared.CBOR+ where++import Data.ByteString.Lazy (ByteString)+import Servant.API (Accept (..), MimeRender (..), MimeUnrender (..))+import Servant.Docs (ToSample (..), singleSample)++data CBOR++-- | Wrapper for CBOR encoded `ByteString`s+-- used for submitting a transaction+newtype CBORString = CBORString ByteString+ deriving stock (Eq, Show)++instance Accept CBOR where+ contentType = pure "application/cbor"++instance MimeRender CBOR CBORString where+ mimeRender _ (CBORString cs) = cs++instance MimeUnrender CBOR CBORString where+ mimeUnrender _ lbs = pure $ CBORString lbs++instance ToSample CBORString where+ toSamples = pure $ singleSample $ CBORString "adef"
@@ -0,0 +1,22 @@+-- | Epoch++module Blockfrost.Types.Shared.Epoch+ where++import Data.Aeson (FromJSON, ToJSON)+import GHC.Generics+import Servant.API (Capture, FromHttpApiData (..), ToHttpApiData (..))+import Servant.Docs (DocCapture (..), ToCapture (..), ToSample (..), samples)++newtype Epoch = Epoch Integer+ deriving stock (Eq, Show, Generic)+ deriving newtype (Num, FromHttpApiData, ToHttpApiData, FromJSON, ToJSON)++unEpoch :: Epoch -> Integer+unEpoch (Epoch i) = i++instance ToCapture (Capture "epoch_number" Epoch) where+ toCapture _ = DocCapture "epoch_number" "Epoch for specific epoch slot."++instance ToSample Epoch where+ toSamples = pure $ samples [425, 500, 1200]
@@ -0,0 +1,25 @@+-- | Aeson options and helpers++{-# OPTIONS_HADDOCK hide #-}++module Blockfrost.Types.Shared.Opts+ ( ToLower+ , aesonOptions+ ) where++import Data.Aeson (Options (..), camelTo2, defaultOptions)+import Data.Char (toLower)+import Deriving.Aeson (StringModifier (..))++data ToLower++instance StringModifier ToLower where+ getStringModifier "" = ""+ getStringModifier (c : xs) = toLower c : xs++aesonOptions :: Maybe String -> Options+aesonOptions mPrefix = defaultOptions {+ fieldLabelModifier = camelTo2 '_' . dropIfPrefixed+ , constructorTagModifier = map toLower+ }+ where dropIfPrefixed = maybe id (drop . length) mPrefix
@@ -0,0 +1,48 @@+-- | POSIX Milliseconds wrapper++module Blockfrost.Types.Shared.POSIXMillis+ ( POSIXMillis+ , POSIXTime+ , millisecondsToPosix+ , posixToMilliseconds+ , seconds+ ) where++import Data.Aeson (FromJSON (..), ToJSON (..))+import Data.Time.Clock.POSIX+import GHC.Generics+import Servant.Docs (ToSample (..), singleSample)+import Test.QuickCheck.Arbitrary+import Test.QuickCheck.Instances ()+import Test.QuickCheck.Modifiers++-- | Convert `Integer` milliseconds to `POSIXTime`+millisecondsToPosix :: Integer -> POSIXTime+millisecondsToPosix n = fromInteger n / 1000++-- | Convert `POSIXTime` to `Integer` milliseconds+posixToMilliseconds :: POSIXTime -> Integer+posixToMilliseconds t = round (t * 1000)++newtype POSIXMillis = POSIXMillis {+ unPOSIXMillis :: POSIXTime+ }+ deriving stock (Show, Eq, Generic)++seconds :: POSIXTime -> POSIXMillis+seconds = POSIXMillis++instance FromJSON POSIXMillis where+ parseJSON t = POSIXMillis . millisecondsToPosix <$> parseJSON @Integer t++instance ToJSON POSIXMillis where+ toJSON (POSIXMillis t) = toJSON $ posixToMilliseconds t+ toEncoding (POSIXMillis t) = toEncoding $ posixToMilliseconds t++instance ToSample POSIXMillis where+ toSamples _ = singleSample $ POSIXMillis $ millisecondsToPosix 1603400958947++instance Arbitrary POSIXMillis where+ arbitrary = do+ Positive (n :: Integer) <- arbitrary+ pure $ POSIXMillis (fromInteger n / 1000)
@@ -0,0 +1,34 @@+-- | PolicyId++module Blockfrost.Types.Shared.PolicyId+ where++import Data.Aeson (FromJSON, ToJSON)+import Data.String (IsString (..))+import Data.Text (Text)+import qualified Data.Text+import GHC.Generics+import Servant.API (Capture, FromHttpApiData (..), ToHttpApiData (..))+import Servant.Docs (DocCapture (..), ToCapture (..), ToSample (..), samples)++-- | Minting policy Id+newtype PolicyId = PolicyId Text+ deriving stock (Eq, Show, Generic)+ deriving newtype (FromHttpApiData, ToHttpApiData, FromJSON, ToJSON)++mkPolicyId :: Text -> PolicyId+mkPolicyId = PolicyId++unPolicyId :: PolicyId -> Text+unPolicyId (PolicyId a) = a++instance IsString PolicyId where+ fromString = mkPolicyId . Data.Text.pack++instance ToCapture (Capture "policy_id" PolicyId) where+ toCapture _ = DocCapture "policy_id" "Specific policy_id"++instance ToSample PolicyId where+ toSamples = pure $ samples [+ "476039a0949cf0b22f6a800f56780184c44533887ca6e821007840c3"+ ]
@@ -0,0 +1,33 @@+-- | Pool identifier++module Blockfrost.Types.Shared.PoolId+ where++import Data.Aeson (FromJSON, ToJSON)+import Data.String (IsString (..))+import Data.Text (Text)+import qualified Data.Text+import GHC.Generics+import Servant.API (Capture, FromHttpApiData (..), ToHttpApiData (..))+import Servant.Docs (DocCapture (..), ToCapture (..), ToSample (..), samples)++newtype PoolId = PoolId Text+ deriving stock (Eq, Show, Generic)+ deriving newtype (FromHttpApiData, ToHttpApiData, FromJSON, ToJSON)++mkPoolId :: Text -> PoolId+mkPoolId = PoolId++unPoolId :: PoolId -> Text+unPoolId (PoolId a) = a++instance IsString PoolId where+ fromString = mkPoolId . Data.Text.pack++instance ToCapture (Capture "pool_id" PoolId) where+ toCapture _ = DocCapture "pool_id" "Specific pool_id"++instance ToSample PoolId where+ toSamples = pure $ samples [+ "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ ]
@@ -0,0 +1,31 @@+-- | Quantity wrapper++module Blockfrost.Types.Shared.Quantity+ where++import Data.Aeson (FromJSON (..), ToJSON (..), withText)+import qualified Data.Text+import GHC.Generics+import Servant.API (FromHttpApiData (..), ToHttpApiData (..))+import Servant.Docs (ToSample (..), samples)+import qualified Text.Read++newtype Quantity = Quantity Integer+ deriving stock (Eq, Show, Generic)+ deriving newtype (Num, Read, FromHttpApiData, ToHttpApiData)++unQuantity :: Quantity -> Integer+unQuantity (Quantity i) = i++instance ToJSON Quantity where+ toJSON = toJSON . show . unQuantity+ toEncoding = toEncoding . show . unQuantity++instance FromJSON Quantity where+ parseJSON = withText "quantity" $ \q -> do+ case Text.Read.readMaybe (Data.Text.unpack q) of+ Nothing -> fail "Unable to read quantity as Integer"+ Just quant -> pure quant++instance ToSample Quantity where+ toSamples = pure $ samples [37040682, 412162133]
@@ -0,0 +1,22 @@+-- | Slot wrapper++module Blockfrost.Types.Shared.Slot+ where++import Data.Aeson (FromJSON, ToJSON)+import GHC.Generics+import Servant.API (Capture, FromHttpApiData (..), ToHttpApiData (..))+import Servant.Docs (DocCapture (..), ToCapture (..), ToSample (..), samples)++newtype Slot = Slot Integer+ deriving stock (Eq, Show, Generic)+ deriving newtype (Num, FromHttpApiData, ToHttpApiData, FromJSON, ToJSON)++unSlot :: Slot -> Integer+unSlot (Slot i) = i++instance ToCapture (Capture "slot_number" Slot) where+ toCapture _ = DocCapture "slot_number" "Slot position for requested block."++instance ToSample Slot where+ toSamples = pure $ samples [37040682, 412162133]
@@ -0,0 +1,37 @@+-- | Transaction Id++module Blockfrost.Types.Shared.TxHash+ ( TxHash (..)+ ) where++import Data.Aeson (FromJSON (..), ToJSON (..))+import Data.String (IsString (..))+import Data.Text (Text)+import qualified Data.Text+import GHC.Generics+import Servant.API (Capture, FromHttpApiData (..), ToHttpApiData (..))+import Servant.Docs (DocCapture (..), ToCapture (..), ToSample (..), samples)++-- | Id (hash) of the transaction+newtype TxHash = TxHash { unTxHash :: Text }+ deriving stock (Show, Eq, Generic)+ deriving newtype (FromHttpApiData, ToHttpApiData)++instance IsString TxHash where+ fromString = TxHash . Data.Text.pack++instance ToJSON TxHash where+ toJSON = toJSON . unTxHash+ toEncoding = toEncoding . unTxHash+instance FromJSON TxHash where+ parseJSON = fmap TxHash <$> parseJSON++instance ToSample TxHash where+ toSamples _ = samples $ map TxHash+ [ "8788591983aa73981fc92d6cddbbe643959f5a784e84b8bee0db15823f575a5b"+ , "52e748c4dec58b687b90b0b40d383b9fe1f24c1a833b7395cdf07dd67859f46f"+ , "e8073fd5318ff43eca18a852527166aa8008bee9ee9e891f585612b7e4ba700b"+ ]++instance ToCapture (Capture "hash" TxHash) where+ toCapture _ = DocCapture "hash" "Hash of the requested transaction."
+ src/Blockfrost/Util/LensRules.hs view
@@ -0,0 +1,35 @@+-- | Rules for lens generation++{-# OPTIONS_HADDOCK hide #-}++module Blockfrost.Util.LensRules+ where++import Control.Lens+import Language.Haskell.TH (mkName, nameBase)++-- Bit of an overkill, since we only alter+-- one field name which would end up called `type`+blockfrostFieldRules :: LensRules+blockfrostFieldRules = defaultFieldRules+ & lensField %~ modNamer+ where+ modNamer namer dname fnames fname =+ map fixDefName (namer (fixTypeName dname) fnames fname)++ fixDefName (MethodName cname mname)=MethodName cname (fixName mname)+ fixDefName (TopName name) = TopName (fixName name)++ fixTypeName = mkName . fixTypeName' . nameBase+ -- we coerce IPFS (and similar) to Ipfs so+ -- camelCase namer is happy+ -- and we don't have to rename our type or fields+ fixTypeName' "IPFSAdd" = "IpfsAdd"+ fixTypeName' "IPFSPinChange" = "IpfsPinChange"+ fixTypeName' "IPFSPin" = "IpfsPin"+ fixTypeName' "URLVersion" = "UrlVersion"+ fixTypeName' x = x++ fixName = mkName . fixName' . nameBase+ fixName' "type" = "type_"+ fixName' n = n
+ src/Blockfrost/Util/Pagination.hs view
@@ -0,0 +1,54 @@+-- | Pagination utilities++{-# OPTIONS_HADDOCK hide #-}++module Blockfrost.Util.Pagination+ ( Paged (..)+ , Pagination+ , PaginationExpanded+ , page+ , paged+ , nextPage+ , maxPageSize+ ) where++import Data.Default.Class+import Servant.API (QueryParam, (:>))++-- | Pagination parameters+data Paged = Paged+ { countPerPage :: Int -- ^ Count of results per page+ , pageNumber :: Int -- ^ Page number+ } deriving (Eq, Ord, Show)++-- | Maximum number of items per page+maxPageSize :: Int+maxPageSize = 100++instance Default Paged where+ def = Paged maxPageSize 1++-- | Default `Paged` at specific page number+page :: Int -> Paged+page n | n >= 1 = def { pageNumber = n }+page _ = error "Page number not in range [1..]"++-- | Construct `Paged` from page size and page number.+--+-- Throws error on invalid values.+paged :: Int -> Int -> Paged+paged size _ | size > maxPageSize = error "Page size exceeds 100"+paged _ n | n < 1 = error "Page number not in range [1..]"+paged size n = Paged size n++-- | Increment page number+nextPage :: Paged -> Paged+nextPage p = p { pageNumber = 1 + pageNumber p }++-- | Adds Pagination to an API+data Pagination++type PaginationExpanded subApi =+ QueryParam "count" Int+ :> QueryParam "page" Int+ :> subApi
+ src/Blockfrost/Util/Sorting.hs view
@@ -0,0 +1,45 @@+-- | Sorting utilities++{-# OPTIONS_HADDOCK hide #-}++module Blockfrost.Util.Sorting+ ( SortOrder (..)+ , Sorting+ , SortingExpanded+ , asc+ , desc+ ) where++import Data.Default.Class+import Servant.API (FromHttpApiData (..), QueryParam, ToHttpApiData (..), (:>))++data SortOrder = Ascending | Descending+ deriving (Eq, Show, Ord)++-- | @Ascending@ shortcut+asc :: SortOrder+asc = Ascending++-- | @Descending@ shortcut+desc :: SortOrder+desc = Descending++instance Default SortOrder where+ def = Ascending++instance ToHttpApiData SortOrder where+ toUrlPiece Ascending = "asc"+ toUrlPiece Descending = "desc"++instance FromHttpApiData SortOrder where+ parseUrlPiece "asc" = Right Ascending+ parseUrlPiece "desc" = Right Descending+ parseUrlPiece z = Left ("unknown SortOrder: " <> z)++-- | Adds sorting to an API+data Sorting++-- | Adds sorting to an API+type SortingExpanded a =+ QueryParam "order" SortOrder+ :> a
+ src/Blockfrost/Util/Tag.hs view
@@ -0,0 +1,13 @@+-- | Tag for OpenAPI 3 docs++{-# OPTIONS_HADDOCK hide #-}++module Blockfrost.Util.Tag+ ( Tag+ ) where++import GHC.TypeLits (Symbol)++-- | Attaches a tag to documentation.+-- Server / client implementations remains intact.+data Tag (name :: Symbol)
+ test/APISpec.hs view
@@ -0,0 +1,135 @@+{-# LANGUAGE NumericUnderscores #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-}++module APISpec+ where++import Data.Aeson (decode, eitherDecode, encode)+import Data.Text (Text)+import qualified Money+import Test.Hspec+import Test.Tasty.Hspec+import Text.RawString.QQ++import Blockfrost.Types++spec_sample :: Spec+spec_sample = do+ it "parses server time sample" $ do+ decode serverTimeSample+ `shouldBe`+ Just (ServerTime 1603400958.947)++ it "parses block sample" $ do+ eitherDecode blockSample+ `shouldBe`+ Right blockExpected++ it "parses metrics sample" $ do+ eitherDecode metricsSample+ `shouldBe`+ Right metricsExpected++ it "parses metrics endpoints sample" $ do+ eitherDecode metricsEndpointsSample+ `shouldBe`+ Right metricsEndpointsExpected++serverTimeSample = [r| { "server_time": 1603400958947 } |]++blockSample = [r|+{+ "time": 1641338934,+ "height": 15243593,+ "hash": "4ea1ba291e8eef538635a53e59fddba7810d1679631cc3aed7c8e6c4091a516a",+ "slot": 412162133,+ "epoch": 425,+ "epoch_slot": 12,+ "slot_leader": "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2qnikdy",+ "size": 3,+ "tx_count": 1,+ "output": "128314491794",+ "fees": "592661",+ "block_vrf": "vrf_vk1wf2k6lhujezqcfe00l6zetxpnmh9n6mwhpmhm0dvfh3fxgmdnrfqkms8ty",+ "previous_block": "43ebccb3ac72c7cebd0d9b755a4b08412c9f5dcb81b8a0ad1e3c197d29d47b05",+ "next_block": "8367f026cf4b03e116ff8ee5daf149b55ba5a6ec6dec04803b8dc317721d15fa",+ "confirmations": 4698+}+|]++blockExpected = Block+ { _blockTime = 1641338934+ , _blockHeight = pure 15243593+ , _blockHash = "4ea1ba291e8eef538635a53e59fddba7810d1679631cc3aed7c8e6c4091a516a"+ , _blockSlot = pure 412162133+ , _blockEpoch = pure 425+ , _blockEpochSlot = pure 12+ , _blockSlotLeader = "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2qnikdy"+ , _blockSize = 3+ , _blockTxCount = 1+ , _blockOutput = pure $ 128314491794+ , _blockFees = pure $ 592661+ , _blockBlockVrf = pure "vrf_vk1wf2k6lhujezqcfe00l6zetxpnmh9n6mwhpmhm0dvfh3fxgmdnrfqkms8ty"+ , _blockPreviousBlock = pure "43ebccb3ac72c7cebd0d9b755a4b08412c9f5dcb81b8a0ad1e3c197d29d47b05"+ , _blockNextBlock = pure "8367f026cf4b03e116ff8ee5daf149b55ba5a6ec6dec04803b8dc317721d15fa"+ , _blockConfirmations = 4698+ }++metricsSample = [r|+[+ {+ "time": 1612543884,+ "calls": 42+ },+ {+ "time": 1614523884,+ "calls": 6942+ }+]+|]++metricsExpected =+ [ Metric 1612543884 42+ , Metric 1614523884 6942+ ]++metricsEndpointsSample = [r|+[+ {+ "time": 1612543814,+ "calls": 182,+ "endpoint": "block"+ },+ {+ "time": 1612543814,+ "calls": 42,+ "endpoint": "epoch"+ },+ {+ "time": 1612543812,+ "calls": 775,+ "endpoint": "block"+ },+ {+ "time": 1612523884,+ "calls": 4,+ "endpoint": "epoch"+ },+ {+ "time": 1612553884,+ "calls": 89794,+ "endpoint": "block"+ }+]+|]++metricsEndpointsExpected :: [(Text, Metric)]+metricsEndpointsExpected =+ [ ("block", Metric 1612543814 182)+ , ("epoch", Metric 1612543814 42)+ , ("block", Metric 1612543812 775)+ , ("epoch", Metric 1612523884 4)+ , ("block", Metric 1612553884 89794)+ ]
+ test/AmountSpec.hs view
@@ -0,0 +1,76 @@+-- | Amount (de)serialization+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE NumericUnderscores #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-}++module AmountSpec+ where++import Blockfrost.Types.Shared.Amount+import Data.Aeson (decode, encode)+import Data.Default+import qualified Money+import Test.Hspec+import Test.Tasty.Hspec+import Text.RawString.QQ++spec_sample :: Spec+spec_sample = do+ it "my samples decodes to expected value" $ do+ decode mySamples `shouldBe` Just mySamplesExpected+ it "doc samples decodes to expected value" $ do+ decode docSamples `shouldBe` Just docSamplesExpected++mySamples = [r|+[+ { "unit":"lovelace"+ , "quantity":"42000000"+ }++ , { "unit":"b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e"+ , "quantity":"12"+ }++ , { "unit":"6804edf9712d2b619edb6ac86861fe93a730693183a262b165fcc1ba1bc99cad"+ , "quantity":"18605647"+ }+]+|]++mySamplesExpected =+ [ AdaAmount 42000000+ , AssetAmount+ $ Money.toSomeDiscrete+ (12 :: Money.Discrete'+ "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e"+ '(1,1))+ , AssetAmount+ $ Money.toSomeDiscrete+ (18605647 :: Money.Discrete'+ "6804edf9712d2b619edb6ac86861fe93a730693183a262b165fcc1ba1bc99cad"+ '(1,1))+ ]++docSamples = [r|+[+ {+ "unit": "lovelace",+ "quantity": "42000000"+ },+ {+ "unit": "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e",+ "quantity": "12"+ }+]+|]++docSamplesExpected =+ [ AdaAmount 42000000+ , AssetAmount+ $ Money.toSomeDiscrete+ (12 :: Money.Discrete'+ "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e"+ '(1,1))+ ]
+ test/Cardano/Accounts.hs view
@@ -0,0 +1,266 @@+{-# LANGUAGE NumericUnderscores #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-}++module Cardano.Accounts+ where++import Data.Aeson (decode, eitherDecode, encode)+import Data.Text (Text)+import qualified Money+import Test.Hspec+import Test.Tasty.Hspec+import Text.RawString.QQ++import Blockfrost.Types++spec_sample :: Spec+spec_sample = do+ it "parses account sample" $ do+ eitherDecode accountSample+ `shouldBe`+ Right accountExpected++ it "parses account rewards sample" $ do+ eitherDecode accountRewardsSample+ `shouldBe`+ Right accountRewardsExpected++ it "parses account history sample" $ do+ eitherDecode accountHistorySample+ `shouldBe`+ Right accountHistoryExpected++ it "parses account delegation history sample" $ do+ eitherDecode accountDelegationHistorySample+ `shouldBe`+ Right accountDelegationHistoryExpected++ it "parses account withdrawal history sample" $ do+ eitherDecode accountWithdrawalsSample+ `shouldBe`+ Right accountWithdrawalsExpected++ it "parses account mirs sample" $ do+ eitherDecode accountMirsSample+ `shouldBe`+ Right accountMirsExpected++accountSample = [r|+{+ "stake_address": "stake1ux3g2c9dx2nhhehyrezyxpkstartcqmu9hk63qgfkccw5rqttygt7",+ "active": true,+ "active_epoch": 412,+ "controlled_amount": "619154618165",+ "rewards_sum": "319154618165",+ "withdrawals_sum": "12125369253",+ "reserves_sum": "319154618165",+ "treasury_sum": "12000000",+ "withdrawable_amount": "319154618165",+ "pool_id": "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+}+|]++accountExpected = AccountInfo+ { _accountInfoStakeAddress = "stake1ux3g2c9dx2nhhehyrezyxpkstartcqmu9hk63qgfkccw5rqttygt7"+ , _accountInfoActive = True+ , _accountInfoActiveEpoch = 412+ , _accountInfoControlledAmount = 619154618165+ , _accountInfoRewardsSum = 319154618165+ , _accountInfoWithdrawalsSum = 12125369253+ , _accountInfoReservesSum = 319154618165+ , _accountInfoTreasurySum = 12000000+ , _accountInfoWithdrawableAmount = 319154618165+ , _accountInfoPoolId = pure "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ }++accountRewardsSample = [r|+[+ {+ "epoch": 215,+ "amount": "12695385",+ "pool_id": "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ },+ {+ "epoch": 216,+ "amount": "12695385",+ "pool_id": "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ },+ {+ "epoch": 216,+ "amount": "3586329",+ "pool_id": "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ },+ {+ "epoch": 217,+ "amount": "0",+ "pool_id": "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ },+ {+ "epoch": 218,+ "amount": "1395265",+ "pool_id": "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ }+]+|]++accountRewardsExpected =+ [ AccountReward+ { _accountRewardEpoch = 215+ , _accountRewardAmount = 12695385+ , _accountRewardPoolId = "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ }+ , AccountReward+ { _accountRewardEpoch = 216+ , _accountRewardAmount = 12695385+ , _accountRewardPoolId = "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ }+ , AccountReward+ { _accountRewardEpoch = 216+ , _accountRewardAmount = 3586329+ , _accountRewardPoolId = "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ }+ , AccountReward+ { _accountRewardEpoch = 217+ , _accountRewardAmount = 0+ , _accountRewardPoolId = "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ }+ , AccountReward+ { _accountRewardEpoch = 218+ , _accountRewardAmount = 1395265+ , _accountRewardPoolId = "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ }+ ]++accountHistorySample = [r|+[+ {+ "active_epoch": 210,+ "amount": "12695385",+ "pool_id": "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ },+ {+ "active_epoch": 211,+ "amount": "22695385",+ "pool_id": "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ }+]+|]++accountHistoryExpected =+ [ AccountHistory+ { _accountHistoryActiveEpoch = 210+ , _accountHistoryAmount = 12695385+ , _accountHistoryPoolId = "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ }+ , AccountHistory+ { _accountHistoryActiveEpoch = 211+ , _accountHistoryAmount = 22695385+ , _accountHistoryPoolId = "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ }+ ]++accountDelegationHistorySample = [r|+[+ {+ "active_epoch": 210,+ "tx_hash": "2dd15e0ef6e6a17841cb9541c27724072ce4d4b79b91e58432fbaa32d9572531",+ "amount": "12695385",+ "pool_id": "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ },+ {+ "active_epoch": 242,+ "tx_hash": "1a0570af966fb355a7160e4f82d5a80b8681b7955f5d44bec0dde628516157f0",+ "amount": "12691385",+ "pool_id": "pool1kchver88u3kygsak8wgll7htr8uxn5v35lfrsyy842nkscrzyvj"+ }+]+|]++accountDelegationHistoryExpected =+ [ AccountDelegation+ { _accountDelegationActiveEpoch = 210+ , _accountDelegationTxHash = "2dd15e0ef6e6a17841cb9541c27724072ce4d4b79b91e58432fbaa32d9572531"+ , _accountDelegationAmount = 12695385+ , _accountDelegationPoolId = "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ }+ , AccountDelegation+ { _accountDelegationActiveEpoch = 242+ , _accountDelegationTxHash = "1a0570af966fb355a7160e4f82d5a80b8681b7955f5d44bec0dde628516157f0"+ , _accountDelegationAmount = 12691385+ , _accountDelegationPoolId = "pool1kchver88u3kygsak8wgll7htr8uxn5v35lfrsyy842nkscrzyvj"+ }+ ]++accountRegistrationSample = [r|+[+ {+ "tx_hash": "2dd15e0ef6e6a17841cb9541c27724072ce4d4b79b91e58432fbaa32d9572531",+ "action": "registered"+ },+ {+ "tx_hash": "1a0570af966fb355a7160e4f82d5a80b8681b7955f5d44bec0dde628516157f0",+ "action": "deregistered"+ }+]+|]++accountRegistrationExpected =+ [ AccountRegistration+ { _accountRegistrationAction = Registered+ , _accountRegistrationTxHash = "2dd15e0ef6e6a17841cb9541c27724072ce4d4b79b91e58432fbaa32d9572531"+ }+ , AccountRegistration+ { _accountRegistrationAction = Deregistered+ , _accountRegistrationTxHash = "1a0570af966fb355a7160e4f82d5a80b8681b7955f5d44bec0dde628516157f0"+ }+ ]++accountWithdrawalsSample = [r|+[+ {+ "tx_hash": "48a9625c841eea0dd2bb6cf551eabe6523b7290c9ce34be74eedef2dd8f7ecc5",+ "amount": "454541212442"+ },+ {+ "tx_hash": "4230b0cbccf6f449f0847d8ad1d634a7a49df60d8c142bb8cc2dbc8ca03d9e34",+ "amount": "97846969"+ }+]+|]++accountWithdrawalsExpected =+ [ AccountWithdrawal+ { _accountWithdrawalAmount = 454541212442+ , _accountWithdrawalTxHash = "48a9625c841eea0dd2bb6cf551eabe6523b7290c9ce34be74eedef2dd8f7ecc5"+ }+ , AccountWithdrawal+ { _accountWithdrawalAmount = 97846969+ , _accountWithdrawalTxHash = "4230b0cbccf6f449f0847d8ad1d634a7a49df60d8c142bb8cc2dbc8ca03d9e34"+ }+ ]++accountMirsSample = [r|+[+ {+ "tx_hash": "69705bba1d687a816ff5a04ec0c358a1f1ef075ab7f9c6cc2763e792581cec6d",+ "amount": "2193707473"+ },+ {+ "tx_hash": "baaa77b63d4d7d2bb3ab02c9b85978c2092c336dede7f59e31ad65452d510c13",+ "amount": "14520198574"+ }+]+|]++accountMirsExpected =+ [ AccountMir+ { _accountMirAmount = 2193707473+ , _accountMirTxHash = "69705bba1d687a816ff5a04ec0c358a1f1ef075ab7f9c6cc2763e792581cec6d"+ }+ , AccountMir+ { _accountMirAmount = 14520198574+ , _accountMirTxHash = "baaa77b63d4d7d2bb3ab02c9b85978c2092c336dede7f59e31ad65452d510c13"+ }+ ]
+ test/Cardano/Addresses.hs view
@@ -0,0 +1,231 @@+{-# LANGUAGE NumericUnderscores #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-}++module Cardano.Addresses+ where++import Data.Aeson (decode, eitherDecode, encode, object, (.=))+import Data.Text (Text)+import qualified Money+import Test.Hspec+import Test.Tasty.Hspec+import Text.RawString.QQ++import Blockfrost.Types++spec_sample :: Spec+spec_sample = do+ it "parses address info sample" $ do+ eitherDecode addressInfoSample+ `shouldBe`+ Right addressInfoExpected++ it "parses address info sample" $ do+ eitherDecode addressInfoSample+ `shouldBe`+ Right addressInfoExpected++ it "parses address details sample" $ do+ eitherDecode addressDetailsSample+ `shouldBe`+ Right addressDetailsExpected++ it "parses address utxos sample" $ do+ eitherDecode addressUTXOsSample+ `shouldBe`+ Right addressUTXOsExpected++ it "parses address transactions sample" $ do+ eitherDecode addressTransactionsSample+ `shouldBe`+ Right addressTransactionsExpected++addressInfoSample = [r|+{+ "address": "addr1qxqs59lphg8g6qndelq8xwqn60ag3aeyfcp33c2kdp46a09re5df3pzwwmyq946axfcejy5n4x0y99wqpgtp2gd0k09qsgy6pz",+ "amount": [+ {+ "unit": "lovelace",+ "quantity": "42000000"+ },+ {+ "unit": "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e",+ "quantity": "12"+ }+ ],+ "stake_address": "stake1ux3g2c9dx2nhhehyrezyxpkstartcqmu9hk63qgfkccw5rqttygt7",+ "type": "shelley"+}+|]++addressInfoExpected =+ AddressInfo+ { _addressInfoAddress = "addr1qxqs59lphg8g6qndelq8xwqn60ag3aeyfcp33c2kdp46a09re5df3pzwwmyq946axfcejy5n4x0y99wqpgtp2gd0k09qsgy6pz"+ , _addressInfoAmount =+ [ AdaAmount 42000000+ , AssetAmount+ $ Money.mkSomeDiscrete+ "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e"+ unitScale+ 12+ ]+ , _addressInfoStakeAddress = pure "stake1ux3g2c9dx2nhhehyrezyxpkstartcqmu9hk63qgfkccw5rqttygt7"+ , _addressInfoType = Shelley+ }++addressDetailsSample = [r|+{+ "address": "addr1qxqs59lphg8g6qndelq8xwqn60ag3aeyfcp33c2kdp46a09re5df3pzwwmyq946axfcejy5n4x0y99wqpgtp2gd0k09qsgy6pz",+ "received_sum": [+ {+ "unit": "lovelace",+ "quantity": "42000000"+ },+ {+ "unit": "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e",+ "quantity": "12"+ }+ ],+ "sent_sum": [+ {+ "unit": "lovelace",+ "quantity": "42000000"+ },+ {+ "unit": "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e",+ "quantity": "12"+ }+ ],+ "tx_count": 12+}+|]++addressDetailsExpected =+ AddressDetails+ { _addressDetailsAddress = "addr1qxqs59lphg8g6qndelq8xwqn60ag3aeyfcp33c2kdp46a09re5df3pzwwmyq946axfcejy5n4x0y99wqpgtp2gd0k09qsgy6pz"+ , _addressDetailsReceivedSum = amounts+ , _addressDetailsSentSum = amounts+ , _addressDetailsTxCount = 12+ }+ where amounts =+ [ AdaAmount 42000000+ , AssetAmount+ $ Money.mkSomeDiscrete+ "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e"+ unitScale+ 12+ ]+++addressUTXOsSample = [r|+[+ {+ "tx_hash": "39a7a284c2a0948189dc45dec670211cd4d72f7b66c5726c08d9b3df11e44d58",+ "output_index": 0,+ "amount":+ [+ {+ "unit": "lovelace",+ "quantity": "42000000"+ }+ ],+ "block": "7eb8e27d18686c7db9a18f8bbcfe34e3fed6e047afaa2d969904d15e934847e6"+ },+ {+ "tx_hash": "4c4e67bafa15e742c13c592b65c8f74c769cd7d9af04c848099672d1ba391b49",+ "output_index": 0,+ "amount":+ [+ {+ "unit": "lovelace",+ "quantity": "729235000"+ }+ ],+ "block": "953f1b80eb7c11a7ffcd67cbd4fde66e824a451aca5a4065725e5174b81685b7"+ },+ {+ "tx_hash": "768c63e27a1c816a83dc7b07e78af673b2400de8849ea7e7b734ae1333d100d2",+ "output_index": 1,+ "amount":+ [+ {+ "unit": "lovelace",+ "quantity": "42000000"+ },+ {+ "unit": "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e",+ "quantity": "12"+ }+ ],+ "block": "5c571f83fe6c784d3fbc223792627ccf0eea96773100f9aedecf8b1eda4544d7"+ }+]+|]++addressUTXOsExpected =+ [ AddressUTXO+ { _addressUTXOTxHash = "39a7a284c2a0948189dc45dec670211cd4d72f7b66c5726c08d9b3df11e44d58"+ , _addressUTXOOutputIndex = 0+ , _addressUTXOAmount = [ AdaAmount 42000000 ]+ , _addressUTXOBlock = "7eb8e27d18686c7db9a18f8bbcfe34e3fed6e047afaa2d969904d15e934847e6"+ }+ , AddressUTXO+ { _addressUTXOTxHash = "4c4e67bafa15e742c13c592b65c8f74c769cd7d9af04c848099672d1ba391b49"+ , _addressUTXOOutputIndex = 0+ , _addressUTXOAmount = [ AdaAmount 729235000 ]+ , _addressUTXOBlock = "953f1b80eb7c11a7ffcd67cbd4fde66e824a451aca5a4065725e5174b81685b7"+ }+ , AddressUTXO+ { _addressUTXOTxHash = "768c63e27a1c816a83dc7b07e78af673b2400de8849ea7e7b734ae1333d100d2"+ , _addressUTXOOutputIndex = 1+ , _addressUTXOAmount =+ [ AdaAmount 42000000+ , AssetAmount+ $ Money.mkSomeDiscrete+ "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e"+ unitScale+ 12+ ]+ , _addressUTXOBlock = "5c571f83fe6c784d3fbc223792627ccf0eea96773100f9aedecf8b1eda4544d7"+ }+ ]++addressTransactionsSample = [r|+[+ {+ "tx_hash": "8788591983aa73981fc92d6cddbbe643959f5a784e84b8bee0db15823f575a5b",+ "tx_index": 6,+ "block_height": 69+ },+ {+ "tx_hash": "52e748c4dec58b687b90b0b40d383b9fe1f24c1a833b7395cdf07dd67859f46f",+ "tx_index": 9,+ "block_height": 4547+ },+ {+ "tx_hash": "e8073fd5318ff43eca18a852527166aa8008bee9ee9e891f585612b7e4ba700b",+ "tx_index": 0,+ "block_height": 564654+ }+]+|]++addressTransactionsExpected =+ [ AddressTransaction+ { _addressTransactionTxHash = "8788591983aa73981fc92d6cddbbe643959f5a784e84b8bee0db15823f575a5b"+ , _addressTransactionTxIndex = 6+ , _addressTransactionBlockHeight = 69+ }+ , AddressTransaction+ { _addressTransactionTxHash = "52e748c4dec58b687b90b0b40d383b9fe1f24c1a833b7395cdf07dd67859f46f"+ , _addressTransactionTxIndex = 9+ , _addressTransactionBlockHeight = 4547+ }+ , AddressTransaction+ { _addressTransactionTxHash = "e8073fd5318ff43eca18a852527166aa8008bee9ee9e891f585612b7e4ba700b"+ , _addressTransactionTxIndex = 0+ , _addressTransactionBlockHeight = 564654+ }+ ]
+ test/Cardano/Assets.hs view
@@ -0,0 +1,233 @@+{-# LANGUAGE NumericUnderscores #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-}++module Cardano.Assets+ where++import Data.Aeson (decode, eitherDecode, encode)+import Data.Text (Text)+import qualified Money+import Test.Hspec+import Test.Tasty.Hspec+import Text.RawString.QQ++import Blockfrost.Types++spec_assets :: Spec+spec_assets = do+ it "parses assets sample" $ do+ eitherDecode assetInfoSample+ `shouldBe`+ Right assetInfoExpected++ it "parses assets details sample" $ do+ eitherDecode assetDetailsSample+ `shouldBe`+ Right assetDetailsExpected++ it "parses assets history sample" $ do+ eitherDecode assetHistorySample+ `shouldBe`+ Right assetHistoryExpected++ it "parses assets transaction sample" $ do+ eitherDecode assetTransactionsSample+ `shouldBe`+ Right assetTransactionsExpected++ it "parses assets adresses sample" $ do+ eitherDecode assetAddressesSample+ `shouldBe`+ Right assetAddressesExpected++assetInfoSample = [r|+[+ {+ "asset": "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e",+ "quantity": "1"+ },+ {+ "asset": "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e75d",+ "quantity": "100000"+ },+ {+ "asset": "6804edf9712d2b619edb6ac86861fe93a730693183a262b165fcc1ba1bc99cad",+ "quantity": "18605647"+ }+]+|]++assetInfoExpected =+ [ AssetInfo+ { _assetInfoAsset = "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e"+ , _assetInfoQuantity = 1+ }+ , AssetInfo+ { _assetInfoAsset = "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e75d"+ , _assetInfoQuantity = 100000+ }+ , AssetInfo+ { _assetInfoAsset = "6804edf9712d2b619edb6ac86861fe93a730693183a262b165fcc1ba1bc99cad"+ , _assetInfoQuantity = 18605647+ }+ ]+++assetDetailsSample = [r|+{+ "asset": "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e",+ "policy_id": "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a7",+ "asset_name": "6e7574636f696e",+ "fingerprint": "asset1pkpwyknlvul7az0xx8czhl60pyel45rpje4z8w",+ "quantity": "12000",+ "initial_mint_tx_hash": "6804edf9712d2b619edb6ac86861fe93a730693183a262b165fcc1ba1bc99cad",+ "mint_or_burn_count": 1,+ "onchain_metadata": {+ "name": "My NFT token",+ "image": "ipfs://ipfs/QmfKyJ4tuvHowwKQCbCHj4L5T3fSj8cjs7Aau8V7BWv226"+ },+ "metadata": {+ "name": "nutcoin",+ "description": "The Nut Coin",+ "ticker": "nutc",+ "url": "https://www.stakenuts.com/",+ "logo": "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",+ "decimals": 6+ }+}+|]++assetDetailsExpected =+ AssetDetails+ { _assetDetailsAsset = "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e"+ , _assetDetailsPolicyId = "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a7"+ , _assetDetailsAssetName = pure "6e7574636f696e"+ , _assetDetailsFingerprint = "asset1pkpwyknlvul7az0xx8czhl60pyel45rpje4z8w"+ , _assetDetailsQuantity = 12000+ , _assetDetailsInitialMintTxHash = "6804edf9712d2b619edb6ac86861fe93a730693183a262b165fcc1ba1bc99cad"+ , _assetDetailsMintOrBurnCount = 1+ , _assetDetailsOnchainMetadata = pure $+ AssetOnChainMetadata+ { _assetOnChainMetadataName = "My NFT token"+ , _assetOnChainMetadataImage = "ipfs://ipfs/QmfKyJ4tuvHowwKQCbCHj4L5T3fSj8cjs7Aau8V7BWv226"+ }+ , _assetDetailsMetadata = pure $+ AssetMetadata+ { _assetMetadataName = "nutcoin"+ , _assetMetadataDescription = "The Nut Coin"+ , _assetMetadataTicker = pure "nutc"+ , _assetMetadataUrl = pure "https://www.stakenuts.com/"+ , _assetMetadataLogo = pure "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"+ , _assetMetadataDecimals = pure 6+ }+ }++assetHistorySample = [r|+[+ {+ "tx_hash": "2dd15e0ef6e6a17841cb9541c27724072ce4d4b79b91e58432fbaa32d9572531",+ "amount": "10",+ "action": "minted"+ },+ {+ "tx_hash": "9c190bc1ac88b2ab0c05a82d7de8b71b67a9316377e865748a89d4426c0d3005",+ "amount": "5",+ "action": "burned"+ },+ {+ "tx_hash": "1a0570af966fb355a7160e4f82d5a80b8681b7955f5d44bec0dde628516157f0",+ "amount": "5",+ "action": "burned"+ }+]+|]++assetHistoryExpected =+ [ AssetHistory+ { _assetHistoryTxHash = "2dd15e0ef6e6a17841cb9541c27724072ce4d4b79b91e58432fbaa32d9572531"+ , _assetHistoryAmount = 10+ , _assetHistoryAction = Minted+ }+ , AssetHistory+ { _assetHistoryTxHash = "9c190bc1ac88b2ab0c05a82d7de8b71b67a9316377e865748a89d4426c0d3005"+ , _assetHistoryAmount = 5+ , _assetHistoryAction = Burned+ }+ , AssetHistory+ { _assetHistoryTxHash = "1a0570af966fb355a7160e4f82d5a80b8681b7955f5d44bec0dde628516157f0"+ , _assetHistoryAmount = 5+ , _assetHistoryAction = Burned+ }+ ]++assetTransactionsSample = [r|+[+ {+ "tx_hash": "8788591983aa73981fc92d6cddbbe643959f5a784e84b8bee0db15823f575a5b",+ "tx_index": 6,+ "block_height": 69+ },+ {+ "tx_hash": "52e748c4dec58b687b90b0b40d383b9fe1f24c1a833b7395cdf07dd67859f46f",+ "tx_index": 9,+ "block_height": 4547+ },+ {+ "tx_hash": "e8073fd5318ff43eca18a852527166aa8008bee9ee9e891f585612b7e4ba700b",+ "tx_index": 0,+ "block_height": 564654+ }+]+|]++assetTransactionsExpected =+ [ AssetTransaction+ { _assetTransactionTxHash = "8788591983aa73981fc92d6cddbbe643959f5a784e84b8bee0db15823f575a5b"+ , _assetTransactionTxIndex = 6+ , _assetTransactionBlockHeight = 69+ }+ , AssetTransaction+ { _assetTransactionTxHash = "52e748c4dec58b687b90b0b40d383b9fe1f24c1a833b7395cdf07dd67859f46f"+ , _assetTransactionTxIndex = 9+ , _assetTransactionBlockHeight = 4547+ }+ , AssetTransaction+ { _assetTransactionTxHash = "e8073fd5318ff43eca18a852527166aa8008bee9ee9e891f585612b7e4ba700b"+ , _assetTransactionTxIndex = 0+ , _assetTransactionBlockHeight = 564654+ }+ ]++assetAddressesSample = [r|+[+ {+ "address": "addr1qxqs59lphg8g6qndelq8xwqn60ag3aeyfcp33c2kdp46a09re5df3pzwwmyq946axfcejy5n4x0y99wqpgtp2gd0k09qsgy6pz",+ "quantity": "1"+ },+ {+ "address": "addr1qyhr4exrgavdcn3qhfcc9f939fzsch2re5ry9cwvcdyh4x4re5df3pzwwmyq946axfcejy5n4x0y99wqpgtp2gd0k09qdpvhza",+ "quantity": "100000"+ },+ {+ "address": "addr1q8zup8m9ue3p98kxlxl9q8rnyan8hw3ul282tsl9s326dfj088lvedv4zckcj24arcpasr0gua4c5gq4zw2rpcpjk2lq8cmd9l",+ "quantity": "18605647"+ }+]+|]++assetAddressesExpected =+ [ AssetAddress+ { _assetAddressAddress = "addr1qxqs59lphg8g6qndelq8xwqn60ag3aeyfcp33c2kdp46a09re5df3pzwwmyq946axfcejy5n4x0y99wqpgtp2gd0k09qsgy6pz"+ , _assetAddressQuantity = 1+ }+ , AssetAddress+ { _assetAddressAddress = "addr1qyhr4exrgavdcn3qhfcc9f939fzsch2re5ry9cwvcdyh4x4re5df3pzwwmyq946axfcejy5n4x0y99wqpgtp2gd0k09qdpvhza"+ , _assetAddressQuantity = 100000+ }+ , AssetAddress+ { _assetAddressAddress = "addr1q8zup8m9ue3p98kxlxl9q8rnyan8hw3ul282tsl9s326dfj088lvedv4zckcj24arcpasr0gua4c5gq4zw2rpcpjk2lq8cmd9l"+ , _assetAddressQuantity = 18605647+ }+ ]
+ test/Cardano/Epochs.hs view
@@ -0,0 +1,146 @@+{-# LANGUAGE NumericUnderscores #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-}++module Cardano.Epochs+ where++import Data.Aeson (decode, eitherDecode, encode)+import Data.Text (Text)+import qualified Money+import Test.Hspec+import Test.Tasty.Hspec+import Text.RawString.QQ++import Blockfrost.Types++spec_epochs :: Spec+spec_epochs = do+ it "parses epoch info sample" $ do+ eitherDecode epochInfoSample+ `shouldBe`+ Right epochInfoExpected++ it "parses protocol params sample" $ do+ eitherDecode protocolParamsSample+ `shouldBe`+ Right protocolParamsExpected++ it "parses stake distribution sample" $ do+ eitherDecode stakeDistributionSample+ `shouldBe`+ Right stakeDistributionExpected++ it "parses pool stake distribution sample" $ do+ eitherDecode poolStakeDistributionSample+ `shouldBe`+ Right poolStakeDistributionExpected++epochInfoSample = [r|+{+ "epoch": 225,+ "start_time": 1603403091,+ "end_time": 1603835086,+ "first_block_time": 1603403092,+ "last_block_time": 1603835084,+ "block_count": 21298,+ "tx_count": 17856,+ "output": "7849943934049314",+ "fees": "4203312194",+ "active_stake": "784953934049314"+}+|]++epochInfoExpected =+ EpochInfo+ { _epochInfoEpoch = 225+ , _epochInfoStartTime = 1603403091+ , _epochInfoEndTime = 1603835086+ , _epochInfoFirstBlockTime = 1603403092+ , _epochInfoLastBlockTime = 1603835084+ , _epochInfoBlockCount = 21298+ , _epochInfoTxCount = 17856+ , _epochInfoOutput = 7849943934049314+ , _epochInfoFees = 4203312194+ , _epochInfoActiveStake = pure 784953934049314+ }++protocolParamsSample = [r|+{+ "epoch": 225,+ "min_fee_a": 44,+ "min_fee_b": 155381,+ "max_block_size": 65536,+ "max_tx_size": 16384,+ "max_block_header_size": 1100,+ "key_deposit": "2000000",+ "pool_deposit": "500000000",+ "e_max": 18,+ "n_opt": 150,+ "a0": 0.3,+ "rho": 0.003,+ "tau": 0.2,+ "decentralisation_param": 0.5,+ "extra_entropy": null,+ "protocol_major_ver": 2,+ "protocol_minor_ver": 0,+ "min_utxo": "1000000",+ "min_pool_cost": "340000000",+ "nonce": "1a3be38bcbb7911969283716ad7aa550250226b76a61fc51cc9a9a35d9276d81"+}+|]++protocolParamsExpected =+ ProtocolParams+ { _protocolParamsEpoch = 225+ , _protocolParamsMinFeeA = 44+ , _protocolParamsMinFeeB = 155381+ , _protocolParamsMaxBlockSize = 65536+ , _protocolParamsMaxTxSize = 16384+ , _protocolParamsMaxBlockHeaderSize = 1100+ , _protocolParamsKeyDeposit = 2000000+ , _protocolParamsPoolDeposit = 500000000+ , _protocolParamsEMax = 18+ , _protocolParamsNOpt = 150+ , _protocolParamsA0 = 0.3+ , _protocolParamsRho = 0.003+ , _protocolParamsTau = 0.2+ , _protocolParamsDecentralisationParam = 0.5+-- , _protocolParamsExtraEntropy = Nothing+ , _protocolParamsProtocolMajorVer = 2+ , _protocolParamsProtocolMinorVer = 0+ , _protocolParamsMinUtxo = 1000000+ , _protocolParamsMinPoolCost = 340000000+ , _protocolParamsNonce = "1a3be38bcbb7911969283716ad7aa550250226b76a61fc51cc9a9a35d9276d81"+ }++stakeDistributionSample = [r|+{+ "stake_address": "stake1u9l5q5jwgelgagzyt6nuaasefgmn8pd25c8e9qpeprq0tdcp0e3uk",+ "pool_id": "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy",+ "amount": "4440295078"+}+|]++stakeDistributionExpected =+ StakeDistribution+ { _stakeDistributionStakeAddress = "stake1u9l5q5jwgelgagzyt6nuaasefgmn8pd25c8e9qpeprq0tdcp0e3uk"+ , _stakeDistributionPoolId = "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ , _stakeDistributionAmount = 4440295078+ }++poolStakeDistributionSample = [r|+{+ "stake_address": "stake1u9l5q5jwgelgagzyt6nuaasefgmn8pd25c8e9qpeprq0tdcp0e3uk",+ "amount": "4440295078"+}+|]++poolStakeDistributionExpected =+ PoolStakeDistribution+ { _poolStakeDistributionStakeAddress = "stake1u9l5q5jwgelgagzyt6nuaasefgmn8pd25c8e9qpeprq0tdcp0e3uk"+ , _poolStakeDistributionAmount = 4440295078+ }++
+ test/Cardano/Ledger.hs view
@@ -0,0 +1,54 @@+{-# LANGUAGE NumericUnderscores #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-}++module Cardano.Ledger+ where++import Data.Aeson (decode, eitherDecode, encode)+import Data.Text (Text)+import qualified Money+import Test.Hspec+import Test.Tasty.Hspec+import Text.RawString.QQ++import Blockfrost.Types++spec_ledger :: Spec+spec_ledger = do+ it "parses ledger genesis sample" $ do+ eitherDecode ledgerGenesisSample+ `shouldBe`+ Right ledgerGenesisExpected++ledgerGenesisSample = [r|+{+ "active_slots_coefficient": 0.05,+ "update_quorum": 5,+ "max_lovelace_supply": "45000000000000000",+ "network_magic": 764824073,+ "epoch_length": 432000,+ "system_start": 1506203091,+ "slots_per_kes_period": 129600,+ "slot_length": 1,+ "max_kes_evolutions": 62,+ "security_param": 2160+}+|]++ledgerGenesisExpected =+ Genesis+ { _genesisActiveSlotsCoefficient = 0.05+ , _genesisUpdateQuorum = 5+ , _genesisMaxLovelaceSupply = 45_000_000_000_000_000+ , _genesisNetworkMagic = 764824073+ , _genesisEpochLength = 432_000+ , _genesisSystemStart = 1506203091+ , _genesisSlotsPerKesPeriod = 129600+ , _genesisSlotLength = 1+ , _genesisMaxKesEvolutions = 62+ , _genesisSecurityParam = 2160+ }++
+ test/Cardano/Metadata.hs view
@@ -0,0 +1,143 @@+{-# LANGUAGE NumericUnderscores #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-}++module Cardano.Metadata+ where++import Data.Aeson (decode, eitherDecode, encode, object, (.=))+import Data.Text (Text)+import qualified Money+import Test.Hspec+import Test.Tasty.Hspec+import Text.RawString.QQ++import Blockfrost.Types++spec_metadata :: Spec+spec_metadata = do+ it "parses tx meta sample" $ do+ eitherDecode txMetaSample+ `shouldBe`+ Right txMetaExpected++ it "parses tx meta JSON sample" $ do+ eitherDecode txMetaJSONSample+ `shouldBe`+ Right txMetaJSONExpected++ it "parses tx meta CBOR sample" $ do+ eitherDecode txMetaCBORSample+ `shouldBe`+ Right txMetaCBORExpected++txMetaSample = [r|+[+ {+ "label": "1990",+ "cip10": null,+ "count": "1"+ },+ {+ "label": "1967",+ "cip10": "nut.link metadata oracles registry",+ "count": "3"+ },+ {+ "label": "1968",+ "cip10": "nut.link metadata oracles data points",+ "count": "16321"+ }+]+|]++txMetaExpected =+ [ TxMeta "1990" Nothing 1+ , TxMeta "1967" (Just "nut.link metadata oracles registry") 3+ , TxMeta "1968" (Just "nut.link metadata oracles data points") 16321+ ]++txMetaJSONSample = [r|+[+ {+ "tx_hash": "257d75c8ddb0434e9b63e29ebb6241add2b835a307aa33aedba2effe09ed4ec8",+ "json_metadata": {+ "ADAUSD": [+ {+ "value": "0.10409800535729975",+ "source": "ergoOracles"+ }+ ]+ }+ },+ {+ "tx_hash": "e865f2cc01ca7381cf98dcdc4de07a5e8674b8ea16e6a18e3ed60c186fde2b9c",+ "json_metadata": {+ "ADAUSD": [+ {+ "value": "0.15409850555139935",+ "source": "ergoOracles"+ }+ ]+ }+ },+ {+ "tx_hash": "4237501da3cfdd53ade91e8911e764bd0699d88fd43b12f44a1f459b89bc91be",+ "json_metadata": null+ }+]+|]++txMetaJSONExpected =+ let oracleMeta val =+ object [+ "ADAUSD" .=+ [ object [+ "value" .= (val :: Text)+ , "source" .= ("ergoOracles" :: Text)+ ]+ ]+ ]+ in+ [ TxMetaJSON+ "257d75c8ddb0434e9b63e29ebb6241add2b835a307aa33aedba2effe09ed4ec8"+ (Just $ oracleMeta "0.10409800535729975")+ , TxMetaJSON+ "e865f2cc01ca7381cf98dcdc4de07a5e8674b8ea16e6a18e3ed60c186fde2b9c"+ (Just $ oracleMeta "0.15409850555139935")+ , TxMetaJSON+ "4237501da3cfdd53ade91e8911e764bd0699d88fd43b12f44a1f459b89bc91be"+ Nothing+ ]++txMetaCBORSample = [r|+[+ {+ "tx_hash": "257d75c8ddb0434e9b63e29ebb6241add2b835a307aa33aedba2effe09ed4ec8",+ "cbor_metadata": null+ },+ {+ "tx_hash": "e865f2cc01ca7381cf98dcdc4de07a5e8674b8ea16e6a18e3ed60c186fde2b9c",+ "cbor_metadata": null+ },+ {+ "tx_hash": "4237501da3cfdd53ade91e8911e764bd0699d88fd43b12f44a1f459b89bc91be",+ "cbor_metadata": "\\xa100a16b436f6d62696e6174696f6e8601010101010c"+ }+]+|]++txMetaCBORExpected =+ [ TxMetaCBOR+ "257d75c8ddb0434e9b63e29ebb6241add2b835a307aa33aedba2effe09ed4ec8"+ Nothing+ , TxMetaCBOR+ "e865f2cc01ca7381cf98dcdc4de07a5e8674b8ea16e6a18e3ed60c186fde2b9c"+ Nothing+ , TxMetaCBOR+ "4237501da3cfdd53ade91e8911e764bd0699d88fd43b12f44a1f459b89bc91be"+ (Just "\\xa100a16b436f6d62696e6174696f6e8601010101010c")+ ]++
+ test/Cardano/Network.hs view
@@ -0,0 +1,47 @@+{-# LANGUAGE NumericUnderscores #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-}++module Cardano.Network+ where++import Data.Aeson (decode, eitherDecode, encode)+import Data.Text (Text)+import qualified Money+import Test.Hspec+import Test.Tasty.Hspec+import Text.RawString.QQ++import Blockfrost.Types++spec_network :: Spec+spec_network = do+ it "parses network sample" $ do+ eitherDecode networkSample+ `shouldBe`+ Right networkExpected++networkSample = [r|+{+ "supply": {+ "max": "45000000000000000",+ "total": "32890715183299160",+ "circulating": "32412601976210393"+ },+ "stake": {+ "live": "23204950463991654",+ "active": "22210233523456321"+ }+}+|]++networkExpected =+ Network+ (NetworkSupply+ 45_000_000_000_000_000+ 32_890_715_183_299_160+ 32_412_601_976_210_393)+ (NetworkStake+ 23_204_950_463_991_654+ 22_210_233_523_456_321)
+ test/Cardano/Pools.hs view
@@ -0,0 +1,277 @@+{-# LANGUAGE NumericUnderscores #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-}++module Cardano.Pools+ where++import Data.Aeson (decode, eitherDecode, encode)+import Data.Text (Text)+import qualified Money+import Test.Hspec+import Test.Tasty.Hspec+import Text.RawString.QQ++import Blockfrost.Types++spec_pools :: Spec+spec_pools = do+ it "parses pool epoch sample" $ do+ eitherDecode poolEpochSample+ `shouldBe`+ Right poolEpochExpected++ it "parses pool info sample" $ do+ eitherDecode poolInfoSample+ `shouldBe`+ Right poolInfoExpected++ it "parses pool history sample" $ do+ eitherDecode poolHistorySample+ `shouldBe`+ Right poolHistoryExpected++ it "parses pool metadata sample" $ do+ eitherDecode poolMetadataSample+ `shouldBe`+ Right poolMetadataExpected++ it "parses empty object pool metadata sample" $ do+ eitherDecode "{}"+ `shouldBe`+ Right (Nothing :: Maybe PoolMetadata)++ it "parses pool relay sample" $ do+ eitherDecode poolRelaySample+ `shouldBe`+ Right poolRelayExpected++ it "parses pool delegators sample" $ do+ eitherDecode poolDelegatorsSample+ `shouldBe`+ Right poolDelegatorsExpected++ it "parses pool updates sample" $ do+ eitherDecode poolUpdatesSample+ `shouldBe`+ Right poolUpdatesExpected++poolEpochSample = [r|+[+ {+ "pool_id": "pool19u64770wqp6s95gkajc8udheske5e6ljmpq33awxk326zjaza0q",+ "epoch": 225+ },+ {+ "pool_id": "pool1dvla4zq98hpvacv20snndupjrqhuc79zl6gjap565nku6et5zdx",+ "epoch": 215+ },+ {+ "pool_id": "pool1wvccajt4eugjtf3k0ja3exjqdj7t8egsujwhcw4tzj4rzsxzw5w",+ "epoch": 231+ }+]+|]++poolEpochExpected =+ [ PoolEpoch "pool19u64770wqp6s95gkajc8udheske5e6ljmpq33awxk326zjaza0q" 225+ , PoolEpoch "pool1dvla4zq98hpvacv20snndupjrqhuc79zl6gjap565nku6et5zdx" 215+ , PoolEpoch "pool1wvccajt4eugjtf3k0ja3exjqdj7t8egsujwhcw4tzj4rzsxzw5w" 231+ ]++poolInfoSample = [r|+{+ "pool_id": "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy",+ "hex": "0f292fcaa02b8b2f9b3c8f9fd8e0bb21abedb692a6d5058df3ef2735",+ "vrf_key": "0b5245f9934ec2151116fb8ec00f35fd00e0aa3b075c4ed12cce440f999d8233",+ "blocks_minted": 69,+ "live_stake": "6900000000",+ "live_size": 0.42,+ "live_saturation": 0.93,+ "live_delegators": 127,+ "active_stake": "4200000000",+ "active_size": 0.43,+ "declared_pledge": "5000000000",+ "live_pledge": "5000000001",+ "margin_cost": 0.05,+ "fixed_cost": "340000000",+ "reward_account": "stake1uxkptsa4lkr55jleztw43t37vgdn88l6ghclfwuxld2eykgpgvg3f",+ "owners": [+ "stake1u98nnlkvkk23vtvf9273uq7cph5ww6u2yq2389psuqet90sv4xv9v"+ ],+ "registration": [+ "9f83e5484f543e05b52e99988272a31da373f3aab4c064c76db96643a355d9dc",+ "7ce3b8c433bf401a190d58c8c483d8e3564dfd29ae8633c8b1b3e6c814403e95",+ "3e6e1200ce92977c3fe5996bd4d7d7e192bcb7e231bc762f9f240c76766535b9"+ ],+ "retirement": [+ "252f622976d39e646815db75a77289cf16df4ad2b287dd8e3a889ce14c13d1a8"+ ]+}+|]++poolInfoExpected =+ PoolInfo+ { _poolInfoPoolId = "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ , _poolInfoHex = "0f292fcaa02b8b2f9b3c8f9fd8e0bb21abedb692a6d5058df3ef2735"+ , _poolInfoVrfKey = "0b5245f9934ec2151116fb8ec00f35fd00e0aa3b075c4ed12cce440f999d8233"+ , _poolInfoBlocksMinted = 69+ , _poolInfoLiveStake = 6900000000+ , _poolInfoLiveSize = 0.42+ , _poolInfoLiveSaturation = 0.93+ , _poolInfoActiveStake = 4200000000+ , _poolInfoActiveSize = 0.43+ , _poolInfoDeclaredPledge = 5000000000+ , _poolInfoLivePledge = 5000000001+ , _poolInfoMarginCost = 0.05+ , _poolInfoFixedCost = 340000000+ , _poolInfoRewardAccount = "stake1uxkptsa4lkr55jleztw43t37vgdn88l6ghclfwuxld2eykgpgvg3f"+ , _poolInfoOwners = [ "stake1u98nnlkvkk23vtvf9273uq7cph5ww6u2yq2389psuqet90sv4xv9v" ]+ , _poolInfoRegistration =+ [ "9f83e5484f543e05b52e99988272a31da373f3aab4c064c76db96643a355d9dc"+ , "7ce3b8c433bf401a190d58c8c483d8e3564dfd29ae8633c8b1b3e6c814403e95"+ , "3e6e1200ce92977c3fe5996bd4d7d7e192bcb7e231bc762f9f240c76766535b9"+ ]+ , _poolInfoRetirement = [ "252f622976d39e646815db75a77289cf16df4ad2b287dd8e3a889ce14c13d1a8" ]+ }+++poolHistorySample = [r|+{+ "epoch": 233,+ "blocks": 22,+ "active_stake": "20485965693569",+ "active_size": 1.2345,+ "delegators_count": 115,+ "rewards": "206936253674159",+ "fees": "1290968354"+}+|]++poolHistoryExpected =+ PoolHistory+ { _poolHistoryEpoch = 233+ , _poolHistoryBlocks = 22+ , _poolHistoryActiveStake = 20485965693569+ , _poolHistoryActiveSize = 1.2345+ , _poolHistoryDelegatorsCount = 115+ , _poolHistoryRewards = 206936253674159+ , _poolHistoryFees = 1290968354+ }++poolMetadataSample = [r|+{+ "pool_id": "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy",+ "hex": "0f292fcaa02b8b2f9b3c8f9fd8e0bb21abedb692a6d5058df3ef2735",+ "url": "https://stakenuts.com/mainnet.json",+ "hash": "47c0c68cb57f4a5b4a87bad896fc274678e7aea98e200fa14a1cb40c0cab1d8c",+ "ticker": "NUTS",+ "name": "Stake Nuts",+ "description": "The best pool ever",+ "homepage": "https://stakentus.com/"+}+|]++poolMetadataExpected =+ PoolMetadata+ { _poolMetadataPoolId = "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ , _poolMetadataHex = "0f292fcaa02b8b2f9b3c8f9fd8e0bb21abedb692a6d5058df3ef2735"+ , _poolMetadataUrl = Just "https://stakenuts.com/mainnet.json"+ , _poolMetadataHash = Just "47c0c68cb57f4a5b4a87bad896fc274678e7aea98e200fa14a1cb40c0cab1d8c"+ , _poolMetadataTicker = Just "NUTS"+ , _poolMetadataName = Just "Stake Nuts"+ , _poolMetadataDescription = Just "The best pool ever"+ , _poolMetadataHomepage = Just "https://stakentus.com/"+ }++poolRelaySample = [r|+{+ "ipv4": "4.4.4.4",+ "ipv6": "https://stakenuts.com/mainnet.json",+ "dns": "relay1.stakenuts.com",+ "dns_srv": "_relays._tcp.relays.stakenuts.com",+ "port": 3001+}+|]++poolRelayExpected =+ PoolRelay+ { _poolRelayIpv4 = Just "4.4.4.4"+ , _poolRelayIpv6 = Just "https://stakenuts.com/mainnet.json"+ , _poolRelayDns = Just "relay1.stakenuts.com"+ , _poolRelayDnsSrv = Just "_relays._tcp.relays.stakenuts.com"+ , _poolRelayPort = 3001+ }++poolDelegatorsSample = [r|+[+ {+ "address": "stake1ux4vspfvwuus9uwyp5p3f0ky7a30jq5j80jxse0fr7pa56sgn8kha",+ "live_stake": "1137959159981411"+ },+ {+ "address": "stake1uylayej7esmarzd4mk4aru37zh9yz0luj3g9fsvgpfaxulq564r5u",+ "live_stake": "16958865648"+ },+ {+ "address": "stake1u8lr2pnrgf8f7vrs9lt79hc3sxm8s2w4rwvgpncks3axx6q93d4ck",+ "live_stake": "18605647"+ }+]+|]++poolDelegatorsExpected =+ [ PoolDelegator+ { _poolDelegatorAddress = "stake1ux4vspfvwuus9uwyp5p3f0ky7a30jq5j80jxse0fr7pa56sgn8kha"+ , _poolDelegatorLiveStake = 1137959159981411+ }+ , PoolDelegator+ { _poolDelegatorAddress = "stake1uylayej7esmarzd4mk4aru37zh9yz0luj3g9fsvgpfaxulq564r5u"+ , _poolDelegatorLiveStake = 16958865648+ }+ , PoolDelegator+ { _poolDelegatorAddress = "stake1u8lr2pnrgf8f7vrs9lt79hc3sxm8s2w4rwvgpncks3axx6q93d4ck"+ , _poolDelegatorLiveStake = 18605647+ }+ ]++poolUpdatesSample = [r|+[+ {+ "tx_hash": "6804edf9712d2b619edb6ac86861fe93a730693183a262b165fcc1ba1bc99cad",+ "cert_index": 0,+ "action": "registered"+ },+ {+ "tx_hash": "9c190bc1ac88b2ab0c05a82d7de8b71b67a9316377e865748a89d4426c0d3005",+ "cert_index": 0,+ "action": "deregistered"+ },+ {+ "tx_hash": "e14a75b0eb2625de7055f1f580d70426311b78e0d36dd695a6bdc96c7b3d80e0",+ "cert_index": 1,+ "action": "registered"+ }+]+|]++poolUpdatesExpected =+ [ PoolUpdate+ { _poolUpdateTxHash = "6804edf9712d2b619edb6ac86861fe93a730693183a262b165fcc1ba1bc99cad"+ , _poolUpdateCertIndex = 0+ , _poolUpdateAction = PoolRegistered+ }+ , PoolUpdate+ { _poolUpdateTxHash = "9c190bc1ac88b2ab0c05a82d7de8b71b67a9316377e865748a89d4426c0d3005"+ , _poolUpdateCertIndex = 0+ , _poolUpdateAction = PoolDeregistered+ }+ , PoolUpdate+ { _poolUpdateTxHash = "e14a75b0eb2625de7055f1f580d70426311b78e0d36dd695a6bdc96c7b3d80e0"+ , _poolUpdateCertIndex = 1+ , _poolUpdateAction = PoolRegistered+ }+ ]++
+ test/Cardano/Transactions.hs view
@@ -0,0 +1,387 @@+{-# LANGUAGE NumericUnderscores #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-}++module Cardano.Transactions+ where++import Data.Aeson (decode, eitherDecode, encode, object, (.=))+import Data.Text (Text)+import qualified Money+import Test.Hspec+import Test.Tasty.Hspec+import Text.RawString.QQ++import Blockfrost.Types++spec_txs :: Spec+spec_txs = do+ it "parses transaction sample" $ do+ eitherDecode transactionSample+ `shouldBe`+ Right transactionExpected++ it "parses transaction utxos sample" $ do+ eitherDecode transactionUtxosSample+ `shouldBe`+ Right transactionUtxosExpected++ it "parses transaction stake sample" $ do+ eitherDecode transactionStakeSample+ `shouldBe`+ Right transactionStakeExpected++ it "parses transaction delegation sample" $ do+ eitherDecode transactionDelegationSample+ `shouldBe`+ Right transactionDelegationExpected++ it "parses transaction withdrawal sample" $ do+ eitherDecode transactionWithdrawalSample+ `shouldBe`+ Right transactionWithdrawalExpected++ it "parses transaction mir sample" $ do+ eitherDecode transactionMirSample+ `shouldBe`+ Right transactionMirExpected++ it "parses transaction pool update sample" $ do+ eitherDecode transactionPoolUpdateSample+ `shouldBe`+ Right transactionPoolUpdateExpected++ it "parses transaction pool retiring sample" $ do+ eitherDecode transactionPoolRetiringSample+ `shouldBe`+ Right transactionPoolRetiringExpected++ it "parses transaction meta (JSON) sample" $ do+ eitherDecode transactionMetaJSONSample+ `shouldBe`+ Right transactionMetaJSONExpected++ it "parses transaction meta (CBOR) sample" $ do+ eitherDecode transactionMetaCBORSample+ `shouldBe`+ Right transactionMetaCBORExpected++transactionSample = [r|+{+ "hash": "1e043f100dce12d107f679685acd2fc0610e10f72a92d412794c9773d11d8477",+ "block": "356b7d7dbb696ccd12775c016941057a9dc70898d87a63fc752271bb46856940",+ "block_height": 123456,+ "slot": 42000000,+ "index": 1,+ "output_amount": [+ {+ "unit": "lovelace",+ "quantity": "42000000"+ },+ {+ "unit": "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e",+ "quantity": "12"+ }+ ],+ "fees": "182485",+ "deposit": "0",+ "size": 433,+ "invalid_before": null,+ "invalid_hereafter": "13885913",+ "utxo_count": 4,+ "withdrawal_count": 0,+ "mir_cert_count": 0,+ "delegation_count": 0,+ "stake_cert_count": 0,+ "pool_update_count": 0,+ "pool_retire_count": 0,+ "asset_mint_or_burn_count": 0+}+|]++sampleAmounts :: [Amount]+sampleAmounts =+ [ AdaAmount 42000000+ , AssetAmount+ $ Money.mkSomeDiscrete+ "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e"+ unitScale+ 12+ ]++transactionExpected =+ Transaction+ { _transactionHash = "1e043f100dce12d107f679685acd2fc0610e10f72a92d412794c9773d11d8477"+ , _transactionBlock = "356b7d7dbb696ccd12775c016941057a9dc70898d87a63fc752271bb46856940"+ , _transactionBlockHeight = 123456+ , _transactionSlot = 42000000+ , _transactionIndex = 1+ , _transactionOutputAmount = sampleAmounts+ , _transactionFees = 182485+ , _transactionDeposit = 0+ , _transactionSize = 433+ , _transactionInvalidBefore = Nothing+ , _transactionInvalidHereafter = Just "13885913"+ , _transactionUtxoCount = 4+ , _transactionWithdrawalCount = 0+ , _transactionMirCertCount = 0+ , _transactionDelegationCount = 0+ , _transactionStakeCertCount = 0+ , _transactionPoolUpdateCount = 0+ , _transactionPoolRetireCount = 0+ , _transactionAssetMintOrBurnCount = 0+ }++transactionUtxosSample = [r|+{+ "hash": "1e043f100dce12d107f679685acd2fc0610e10f72a92d412794c9773d11d8477",+ "inputs": [+ {+ "address": "addr1q9ld26v2lv8wvrxxmvg90pn8n8n5k6tdst06q2s856rwmvnueldzuuqmnsye359fqrk8hwvenjnqultn7djtrlft7jnq7dy7wv",+ "amount": [+ {+ "unit": "lovelace",+ "quantity": "42000000"+ },+ {+ "unit": "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e",+ "quantity": "12"+ }+ ],+ "tx_hash": "1a0570af966fb355a7160e4f82d5a80b8681b7955f5d44bec0dce628516157f0",+ "output_index": 0+ }+ ],+ "outputs": [+ {+ "address": "addr1q9ld26v2lv8wvrxxmvg90pn8n8n5k6tdst06q2s856rwmvnueldzuuqmnsye359fqrk8hwvenjnqultn7djtrlft7jnq7dy7wv",+ "amount": [+ {+ "unit": "lovelace",+ "quantity": "42000000"+ },+ {+ "unit": "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e",+ "quantity": "12"+ }+ ]+ }+ ]+}+|]++utxoInSample :: UtxoInput+utxoInSample =+ UtxoInput+ { _utxoInputAddress = "addr1q9ld26v2lv8wvrxxmvg90pn8n8n5k6tdst06q2s856rwmvnueldzuuqmnsye359fqrk8hwvenjnqultn7djtrlft7jnq7dy7wv"+ , _utxoInputAmount = sampleAmounts+ , _utxoInputTxHash = "1a0570af966fb355a7160e4f82d5a80b8681b7955f5d44bec0dce628516157f0"+ , _utxoInputOutputIndex = 0+ }++utxoOutSample :: UtxoOutput+utxoOutSample =+ UtxoOutput+ { _utxoOutputAddress = "addr1q9ld26v2lv8wvrxxmvg90pn8n8n5k6tdst06q2s856rwmvnueldzuuqmnsye359fqrk8hwvenjnqultn7djtrlft7jnq7dy7wv"+ , _utxoOutputAmount = sampleAmounts+ }++transactionUtxosExpected =+ TransactionUtxos+ { _transactionUtxosHash = "1e043f100dce12d107f679685acd2fc0610e10f72a92d412794c9773d11d8477"+ , _transactionUtxosInputs = pure utxoInSample+ , _transactionUtxosOutputs = pure utxoOutSample+ }++transactionStakeSample = [r|+{+ "cert_index": 0,+ "address": "stake1u9t3a0tcwune5xrnfjg4q7cpvjlgx9lcv0cuqf5mhfjwrvcwrulda",+ "registration": true+}+|]++transactionStakeExpected =+ TransactionStake+ { _transactionStakeCertIndex = 0+ , _transactionStakeAddress = "stake1u9t3a0tcwune5xrnfjg4q7cpvjlgx9lcv0cuqf5mhfjwrvcwrulda"+ , _transactionStakeRegistration = True+ }++transactionDelegationSample = [r|+{+ "index": 0,+ "cert_index": 0,+ "address": "stake1u9r76ypf5fskppa0cmttas05cgcswrttn6jrq4yd7jpdnvc7gt0yc",+ "pool_id": "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy",+ "active_epoch": 210+}+|]++transactionDelegationExpected =+ TransactionDelegation+ { _transactionDelegationCertIndex = 0+ , _transactionDelegationAddress = "stake1u9r76ypf5fskppa0cmttas05cgcswrttn6jrq4yd7jpdnvc7gt0yc"+ , _transactionDelegationPoolId = "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ , _transactionDelegationActiveEpoch = 210+ }++transactionWithdrawalSample = [r|+{+ "address": "stake1u9r76ypf5fskppa0cmttas05cgcswrttn6jrq4yd7jpdnvc7gt0yc",+ "amount": "431833601"+}+|]++transactionWithdrawalExpected =+ TransactionWithdrawal+ { _transactionWithdrawalAddress = "stake1u9r76ypf5fskppa0cmttas05cgcswrttn6jrq4yd7jpdnvc7gt0yc"+ , _transactionWithdrawalAmount = 431833601+ }++transactionMirSample = [r|+{+ "pot": "reserve",+ "cert_index": 0,+ "address": "stake1u9r76ypf5fskppa0cmttas05cgcswrttn6jrq4yd7jpdnvc7gt0yc",+ "amount": "431833601"+}+|]++transactionMirExpected =+ TransactionMir+ { _transactionMirPot = Reserve+ , _transactionMirCertIndex = 0+ , _transactionMirAddress = "stake1u9r76ypf5fskppa0cmttas05cgcswrttn6jrq4yd7jpdnvc7gt0yc"+ , _transactionMirAmount = 431833601+ }++transactionPoolUpdateSample = [r|+{+ "cert_index": 0,+ "pool_id": "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy",+ "vrf_key": "0b5245f9934ec2151116fb8ec00f35fd00e0aa3b075c4ed12cce440f999d8233",+ "pledge": "5000000000",+ "margin_cost": 0.05,+ "fixed_cost": "340000000",+ "reward_account": "stake1uxkptsa4lkr55jleztw43t37vgdn88l6ghclfwuxld2eykgpgvg3f",+ "owners": [+ "stake1u98nnlkvkk23vtvf9273uq7cph5ww6u2yq2389psuqet90sv4xv9v"+ ],+ "metadata": {+ "url": "https://stakenuts.com/mainnet.json",+ "hash": "47c0c68cb57f4a5b4a87bad896fc274678e7aea98e200fa14a1cb40c0cab1d8c",+ "ticker": "NUTS",+ "name": "Stake Nuts",+ "description": "The best pool ever",+ "homepage": "https://stakentus.com/"+ },+ "relays": [+ {+ "ipv4": "4.4.4.4",+ "ipv6": "https://stakenuts.com/mainnet.json",+ "dns": "relay1.stakenuts.com",+ "dns_srv": "_relays._tcp.relays.stakenuts.com",+ "port": 3001+ }+ ],+ "active_epoch": 210+}+|]++transactionPoolUpdateExpected =+ TransactionPoolUpdate+ { _transactionPoolUpdateCertIndex = 0+ , _transactionPoolUpdatePoolId = "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ , _transactionPoolUpdateVrfKey = "0b5245f9934ec2151116fb8ec00f35fd00e0aa3b075c4ed12cce440f999d8233"+ , _transactionPoolUpdatePledge = 5000000000+ , _transactionPoolUpdateMarginCost = 0.05+ , _transactionPoolUpdateFixedCost = 340000000+ , _transactionPoolUpdateRewardAccount = "stake1uxkptsa4lkr55jleztw43t37vgdn88l6ghclfwuxld2eykgpgvg3f"+ , _transactionPoolUpdateOwners = [ "stake1u98nnlkvkk23vtvf9273uq7cph5ww6u2yq2389psuqet90sv4xv9v" ]+ , _transactionPoolUpdateMetadata = Just samplePoolUpdateMetadata+ , _transactionPoolUpdateRelays = [ samplePoolRelay ]+ , _transactionPoolUpdateActiveEpoch = 210+ }++samplePoolUpdateMetadata :: PoolUpdateMetadata+samplePoolUpdateMetadata =+ PoolUpdateMetadata+ { _poolUpdateMetadataUrl = Just "https://stakenuts.com/mainnet.json"+ , _poolUpdateMetadataHash = Just "47c0c68cb57f4a5b4a87bad896fc274678e7aea98e200fa14a1cb40c0cab1d8c"+ , _poolUpdateMetadataTicker = Just "NUTS"+ , _poolUpdateMetadataName = Just "Stake Nuts"+ , _poolUpdateMetadataDescription = Just "The best pool ever"+ , _poolUpdateMetadataHomepage = Just "https://stakentus.com/"+ }++transactionPoolRetiringSample = [r|+{+ "cert_index": 0,+ "pool_id": "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy",+ "retiring_epoch": 216+}+|]++transactionPoolRetiringExpected =+ TransactionPoolRetiring+ { _transactionPoolRetiringCertIndex = 0+ , _transactionPoolRetiringPoolId = "pool1pu5jlj4q9w9jlxeu370a3c9myx47md5j5m2str0naunn2q3lkdy"+ , _transactionPoolRetiringRetiringEpoch = 216+ }++transactionMetaJSONSample = [r|+[+ {+ "label": "1967",+ "json_metadata": {+ "metadata": "https://nut.link/metadata.json",+ "hash": "6bf124f217d0e5a0a8adb1dbd8540e1334280d49ab861127868339f43b3948af"+ }+ },+ {+ "label": "1968",+ "json_metadata": {+ "ADAUSD": [+ {+ "value": "0.15409850555139935",+ "source": "ergoOracles"+ }+ ]+ }+ }+]+|]++transactionMetaJSONExpected =+ let oracleMeta val =+ object [+ "ADAUSD" .=+ [ object [ "value" .= (val :: Text)+ , "source" .= ("ergoOracles" :: Text) ]+ ]+ ]+ in+ [ TransactionMetaJSON+ "1967"+ (Just $ object+ [ "metadata" .= ("https://nut.link/metadata.json" :: Text)+ , "hash" .= ("6bf124f217d0e5a0a8adb1dbd8540e1334280d49ab861127868339f43b3948af" :: Text)+ ])+ , TransactionMetaJSON+ "1968"+ (Just $ oracleMeta "0.15409850555139935")+ ]++transactionMetaCBORSample = [r|+{+ "label": "1968",+ "cbor_metadata": "\\xa100a16b436f6d62696e6174696f6e8601010101010c"+}+|]++transactionMetaCBORExpected =+ TransactionMetaCBOR+ "1968"+ (Just "\\xa100a16b436f6d62696e6174696f6e8601010101010c")
+ test/IPFS.hs view
@@ -0,0 +1,77 @@+{-# LANGUAGE NumericUnderscores #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-}++module IPFS+ where++import Data.Aeson (eitherDecode)+import Data.Text (Text)+import Test.Hspec+import Test.Tasty.Hspec+import Text.RawString.QQ++import Blockfrost.Types++spec_ipfs :: Spec+spec_ipfs = do+ it "parses add sample" $ do+ eitherDecode pinAddSample+ `shouldBe`+ Right pinAddExpected++ it "parses change sample" $ do+ eitherDecode pinChangeSample+ `shouldBe`+ Right pinChangeExpected++ it "parses pin sample" $ do+ eitherDecode pinSample+ `shouldBe`+ Right pinExpected++pinAddSample = [r|+{+ "name": "README.md",+ "ipfs_hash": "QmZbHqiCxKEVX7QfijzJTkZiSi3WEVTcvANgNAWzDYgZDr",+ "size": "125297"+}+|]++pinAddExpected =+ IPFSAdd+ "README.md"+ "QmZbHqiCxKEVX7QfijzJTkZiSi3WEVTcvANgNAWzDYgZDr"+ 125297++pinChangeSample = [r|+{+ "ipfs_hash": "QmPojRfAXYAXV92Dof7gtSgaVuxEk64xx9CKvprqu9VwA8",+ "state": "queued"+}+|]++pinChangeExpected =+ IPFSPinChange+ "QmPojRfAXYAXV92Dof7gtSgaVuxEk64xx9CKvprqu9VwA8"+ Queued++pinSample = [r|+{+ "time_created": 1615551024,+ "time_pinned": 1615551024,+ "ipfs_hash": "QmdVMnULrY95mth2XkwjxDtMHvzuzmvUPTotKE1tgqKbCx",+ "size": "1615551024",+ "state": "pinned"+}+|]++pinExpected =+ IPFSPin+ { _ipfsPinTimeCreated = 1615551024+ , _ipfsPinTimePinned = 1615551024+ , _ipfsPinIpfsHash = "QmdVMnULrY95mth2XkwjxDtMHvzuzmvUPTotKE1tgqKbCx"+ , _ipfsPinSize = 1615551024+ , _ipfsPinState = Pinned+ }
+ test/NutLink.hs view
@@ -0,0 +1,152 @@+{-# LANGUAGE NumericUnderscores #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-}++module NutLink+ where++import Data.Aeson (Value (..), eitherDecode, object, (.=))+import Data.Text (Text)+import qualified Data.Vector+import Test.Hspec+import Test.Tasty.Hspec+import Text.RawString.QQ++import Blockfrost.Types++spec_nutlink :: Spec+spec_nutlink = do+ it "parses nutlink address sample" $ do+ eitherDecode nutlinkAddressSample+ `shouldBe`+ Right nutlinkAddressExpected++ it "parses nutlink address tickers sample" $ do+ eitherDecode nutlinkAddressTickersSample+ `shouldBe`+ Right nutlinkAddressTickersExpected++ it "parses nutlink ticker sample" $ do+ eitherDecode nutlinkTickerSample+ `shouldBe`+ Right nutlinkTickerExpected++ it "parses nutlink ticker with address sample" $ do+ eitherDecode nutlinkTickerWithAddressSample+ `shouldBe`+ Right nutlinkTickerWithAddressExpected++nutlinkAddressSample = [r|+{+ "address": "addr1qxqs59lphg8g6qndelq8xwqn60ag3aeyfcp33c2kdp46a09re5df3pzwwmyq946axfcejy5n4x0y99wqpgtp2gd0k09qsgy6pz",+ "metadata_url": "https://nut.link/metadata.json",+ "metadata_hash": "6bf124f217d0e5a0a8adb1dbd8540e1334280d49ab861127868339f43b3948af",+ "metadata": {}+}+|]++nutlinkAddressExpected =+ NutlinkAddress+ { _nutlinkAddressAddress = "addr1qxqs59lphg8g6qndelq8xwqn60ag3aeyfcp33c2kdp46a09re5df3pzwwmyq946axfcejy5n4x0y99wqpgtp2gd0k09qsgy6pz"+ , _nutlinkAddressMetadataUrl = "https://nut.link/metadata.json"+ , _nutlinkAddressMetadataHash = "6bf124f217d0e5a0a8adb1dbd8540e1334280d49ab861127868339f43b3948af"+ , _nutlinkAddressMetadata = pure $ object []+ }++nutlinkAddressTickersSample = [r|+[+ {+ "name": "ADAUSD",+ "count": 1980038,+ "latest_block": 2657092+ },+ {+ "name": "ADAEUR",+ "count": 1980038,+ "latest_block": 2657092+ },+ {+ "name": "ADABTC",+ "count": 1980038,+ "latest_block": 2657092+ }+]+|]++nutlinkAddressTickersExpected =+ [ NutlinkAddressTicker+ { _nutlinkAddressTickerName = "ADAUSD"+ , _nutlinkAddressTickerCount = 1980038+ , _nutlinkAddressTickerLatestBlock = 2657092+ }+ , NutlinkAddressTicker+ { _nutlinkAddressTickerName = "ADAEUR"+ , _nutlinkAddressTickerCount = 1980038+ , _nutlinkAddressTickerLatestBlock = 2657092+ }+ , NutlinkAddressTicker+ { _nutlinkAddressTickerName = "ADABTC"+ , _nutlinkAddressTickerCount = 1980038+ , _nutlinkAddressTickerLatestBlock = 2657092+ }+ ]+++nutlinkTickerSample = [r|+{+ "tx_hash": "e8073fd5318ff43eca18a852527166aa8008bee9ee9e891f585612b7e4ba700b",+ "block_height": 2657092,+ "tx_index": 8,+ "payload": [+ {+ "source": "coinGecko",+ "value": "1.29"+ },+ {+ "source": "cryptoCompare",+ "value": "1.283"+ }+ ]+}+|]++sampleArray = Array+ $ Data.Vector.fromList+ [ object [ "source" .= ("coinGecko" :: Text)+ , "value" .= ("1.29" :: Text) ]+ , object [ "source" .= ("cryptoCompare" :: Text),+ "value" .= ("1.283" :: Text) ]+ ]++nutlinkTickerExpected =+ NutlinkTicker+ { _nutlinkTickerTxHash = "e8073fd5318ff43eca18a852527166aa8008bee9ee9e891f585612b7e4ba700b"+ , _nutlinkTickerBlockHeight = 2657092+ , _nutlinkTickerTxIndex = 8+ , _nutlinkTickerPayload = sampleArray+ }++nutlinkTickerWithAddressSample = [r|+{+ "address": "addr_test1qpmtp5t0t5y6cqkaz7rfsyrx7mld77kpvksgkwm0p7en7qum7a589n30e80tclzrrnj8qr4qvzj6al0vpgtnmrkkksnqd8upj0",+ "tx_hash": "e8073fd5318ff43eca18a852527166aa8008bee9ee9e891f585612b7e4ba700b",+ "block_height": 2657092,+ "tx_index": 8,+ "payload": [+ {+ "source": "coinGecko",+ "value": "1.29"+ },+ {+ "source": "cryptoCompare",+ "value": "1.283"+ }+ ]+}+|]++nutlinkTickerWithAddressExpected :: (Address, NutlinkTicker)+nutlinkTickerWithAddressExpected =+ ("addr_test1qpmtp5t0t5y6cqkaz7rfsyrx7mld77kpvksgkwm0p7en7qum7a589n30e80tclzrrnj8qr4qvzj6al0vpgtnmrkkksnqd8upj0"+ , nutlinkTickerExpected)
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF tasty-discover #-}