From a55e087d0ff3a0c50e19fb7e287cac8bc0d3c97b Mon Sep 17 00:00:00 2001 From: sorki Date: Fri, 1 Dec 2023 07:44:57 +0100 Subject: [PATCH 01/16] Trim whitespace in LensRules --- blockfrost-api/src/Blockfrost/Util/LensRules.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockfrost-api/src/Blockfrost/Util/LensRules.hs b/blockfrost-api/src/Blockfrost/Util/LensRules.hs index a7874c3..305a03a 100644 --- a/blockfrost-api/src/Blockfrost/Util/LensRules.hs +++ b/blockfrost-api/src/Blockfrost/Util/LensRules.hs @@ -17,7 +17,7 @@ blockfrostFieldRules = defaultFieldRules modNamer namer dname fnames fname = map fixDefName (namer (fixTypeName dname) fnames fname) - fixDefName (MethodName cname mname)=MethodName cname (fixName mname) + fixDefName (MethodName cname mname) = MethodName cname (fixName mname) fixDefName (TopName name) = TopName (fixName name) fixTypeName = mkName . fixTypeName' . nameBase From 37ae32de36757b636fb7dc9cb35498a08f289315 Mon Sep 17 00:00:00 2001 From: sorki Date: Fri, 1 Dec 2023 08:10:46 +0100 Subject: [PATCH 02/16] api: extend Unknown environment message with preprod, preview --- blockfrost-api/src/Blockfrost/Env.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockfrost-api/src/Blockfrost/Env.hs b/blockfrost-api/src/Blockfrost/Env.hs index bd06bed..3fc011f 100644 --- a/blockfrost-api/src/Blockfrost/Env.hs +++ b/blockfrost-api/src/Blockfrost/Env.hs @@ -30,4 +30,4 @@ parseEnv tEnv = case Text.Read.readMaybe (Data.Text.unpack $ Data.Text.toTitle t Nothing -> Left $ "Unknown environment: `" <> tEnv <> "`" - <> " expecting one of `ipfs`, `mainnet`, `testnet`, `localhost`" + <> " expecting one of `ipfs`, `mainnet`, `testnet`, `preprod`, `preview`, `localhost`" From dbc91b39b1eb4fc3f196b84f2e0e9e18bbe3652e Mon Sep 17 00:00:00 2001 From: sorki Date: Fri, 1 Dec 2023 08:11:12 +0100 Subject: [PATCH 03/16] api: add Sanchonet environment --- blockfrost-api/src/Blockfrost/Env.hs | 3 ++- blockfrost-client-core/src/Blockfrost/Client/Core.hs | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/blockfrost-api/src/Blockfrost/Env.hs b/blockfrost-api/src/Blockfrost/Env.hs index 3fc011f..4b360a2 100644 --- a/blockfrost-api/src/Blockfrost/Env.hs +++ b/blockfrost-api/src/Blockfrost/Env.hs @@ -20,6 +20,7 @@ data Env = | Testnet | Preprod | Preview + | Sanchonet | Localhost deriving (Eq, Read, Show, Ord, Generic) @@ -30,4 +31,4 @@ parseEnv tEnv = case Text.Read.readMaybe (Data.Text.unpack $ Data.Text.toTitle t Nothing -> Left $ "Unknown environment: `" <> tEnv <> "`" - <> " expecting one of `ipfs`, `mainnet`, `testnet`, `preprod`, `preview`, `localhost`" + <> " expecting one of `ipfs`, `mainnet`, `testnet`, `preprod`, `preview`, `sanchonet`, `localhost`" diff --git a/blockfrost-client-core/src/Blockfrost/Client/Core.hs b/blockfrost-client-core/src/Blockfrost/Client/Core.hs index de1e5a3..1042435 100644 --- a/blockfrost-client-core/src/Blockfrost/Client/Core.hs +++ b/blockfrost-client-core/src/Blockfrost/Client/Core.hs @@ -69,6 +69,7 @@ subdomainByEnv Mainnet = pure "cardano-mainnet" subdomainByEnv Testnet = pure "cardano-testnet" subdomainByEnv Preprod = pure "cardano-preprod" subdomainByEnv Preview = pure "cardano-preview" +subdomainByEnv Sanchonet = pure "cardano-sanchonet" subdomainByEnv Localhost = Nothing -- | Read file according to BLOCKFROST_TOKEN_PATH environment variable name. From e4ccd47173144d953fc5d17167bd9139c2b196eb Mon Sep 17 00:00:00 2001 From: sorki Date: Fri, 8 Dec 2023 18:54:50 +0100 Subject: [PATCH 04/16] api: neaten Types --- .../src/Blockfrost/Client/Types.hs | 44 +++++++++++++++---- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/blockfrost-client/src/Blockfrost/Client/Types.hs b/blockfrost-client/src/Blockfrost/Client/Types.hs index c0125b0..0d3d805 100644 --- a/blockfrost-client/src/Blockfrost/Client/Types.hs +++ b/blockfrost-client/src/Blockfrost/Client/Types.hs @@ -64,7 +64,9 @@ instance MonadIO m => MonadBlockfrost (BlockfrostClientT m) where liftBlockfrostClient act = BlockfrostClientT $ do (env, _proj) <- ask liftIO (runClientM act env) - >>= either (throwError . fromServantClientError) pure + >>= either + (throwError . fromServantClientError) + pure getConf = BlockfrostClientT ask instance MonadBlockfrost ClientM where @@ -72,13 +74,24 @@ instance MonadBlockfrost ClientM where getConf = newClientConfig instance MonadBlockfrost IO where - liftBlockfrostClient act = getConf >>= \(env, _prj) -> runClientM act env >>= either (error . show) pure + liftBlockfrostClient act = + getConf + >>= \(env, _prj) -> + runClientM act env + >>= either + (error . show) + pure getConf = newClientConfig -apiClient :: MonadBlockfrost m => BlockfrostAPI (AsClientT m) +apiClient + :: MonadBlockfrost m + => BlockfrostAPI (AsClientT m) apiClient = genericClientHoist liftBlockfrostClient -api0Client :: MonadBlockfrost m => Project -> BlockfrostV0API (AsClientT m) +api0Client + :: MonadBlockfrost m + => Project + -> BlockfrostV0API (AsClientT m) api0Client = fromServant . _apiV0 apiClient -- ** Client runner @@ -92,7 +105,8 @@ runBlockfrost = runBlockfrostClientT -- | Run @BlockfrostClientT@, using provided @Project@ runBlockfrostClientT - :: MonadIO m => Project + :: MonadIO m + => Project -> BlockfrostClientT m a -> m (Either BlockfrostError a) runBlockfrostClientT proj act = do @@ -125,14 +139,26 @@ tryError action = (Right <$> action) `catchError` (pure . Left) -- ** Service clients -commonClient :: MonadBlockfrost m => Project -> CommonAPI (AsClientT m) +commonClient + :: MonadBlockfrost m + => Project + -> CommonAPI (AsClientT m) commonClient = fromServant . _common . api0Client -cardanoClient :: MonadBlockfrost m => Project -> CardanoAPI (AsClientT m) +cardanoClient + :: MonadBlockfrost m + => Project + -> CardanoAPI (AsClientT m) cardanoClient = fromServant . _cardano . api0Client -ipfsClient :: MonadBlockfrost m => Project -> IPFSAPI (AsClientT m) +ipfsClient + :: MonadBlockfrost m + => Project + -> IPFSAPI (AsClientT m) ipfsClient = fromServant . _ipfs . api0Client -nutLinkClient :: MonadBlockfrost m => Project -> NutLinkAPI (AsClientT m) +nutLinkClient + :: MonadBlockfrost m + => Project + -> NutLinkAPI (AsClientT m) nutLinkClient = fromServant . _nutLink . api0Client From f202a5b9d6888d4afffa0f5de3d9b2112b8e5bf6 Mon Sep 17 00:00:00 2001 From: sorki Date: Mon, 18 Dec 2023 06:49:19 +0100 Subject: [PATCH 05/16] api: add /accounts/:stake_address/addresses/total endpoint, AddressAssociatedTotal type --- blockfrost-api/CHANGELOG.md | 2 + .../src/Blockfrost/API/Cardano/Accounts.hs | 11 ++++ .../src/Blockfrost/Types/Cardano/Accounts.hs | 35 ++++++++++++ blockfrost-api/test/Cardano/Accounts.hs | 54 +++++++++++++++++++ 4 files changed, 102 insertions(+) diff --git a/blockfrost-api/CHANGELOG.md b/blockfrost-api/CHANGELOG.md index 7af89ee..6d82f32 100644 --- a/blockfrost-api/CHANGELOG.md +++ b/blockfrost-api/CHANGELOG.md @@ -1,6 +1,8 @@ # Version [next](https://github.com/blockfrost/blockfrost-haskell/compare/api-0.8.1.0-master) (2023-mm-dd) * Allow servant `0.20` +* Additions + * `/accounts/:stake_address/addresses/total` endpoint and `AddressAssociatedTotal` type # Version [0.8.1.0](https://github.com/blockfrost/blockfrost-haskell/compare/api-0.8.0.0...api-0.8.1.0) (2023-09-18) diff --git a/blockfrost-api/src/Blockfrost/API/Cardano/Accounts.hs b/blockfrost-api/src/Blockfrost/API/Cardano/Accounts.hs index c7febaf..40b24a7 100644 --- a/blockfrost-api/src/Blockfrost/API/Cardano/Accounts.hs +++ b/blockfrost-api/src/Blockfrost/API/Cardano/Accounts.hs @@ -95,4 +95,15 @@ data AccountsAPI route = :> Pagination :> Sorting :> Get '[JSON] [Amount] + , _accountAssociatedTotal + :: route + :- Summary "Detailed information about account associated addresses" + :> Description "Obtain summed details about all addresses associated with a given account. \ + \Be careful, as an account could be part of a mangled address and does not \ + \necessarily mean the addresses are owned by user as the account." + :> Capture "stake_address" Address + :> "addresses" + :> "total" + :> Get '[JSON] AddressAssociatedTotal + } deriving (Generic) diff --git a/blockfrost-api/src/Blockfrost/Types/Cardano/Accounts.hs b/blockfrost-api/src/Blockfrost/Types/Cardano/Accounts.hs index ea63acf..aa9ac7f 100644 --- a/blockfrost-api/src/Blockfrost/Types/Cardano/Accounts.hs +++ b/blockfrost-api/src/Blockfrost/Types/Cardano/Accounts.hs @@ -11,10 +11,12 @@ module Blockfrost.Types.Cardano.Accounts , AccountWithdrawal (..) , AccountMir (..) , AddressAssociated (..) + , AddressAssociatedTotal (..) ) where import Blockfrost.Types.Shared import Deriving.Aeson +import qualified Money import Servant.Docs (ToSample (..), samples, singleSample) -- | Information about an account, identified by its stake address @@ -230,3 +232,36 @@ instance ToSample AddressAssociated where [ AddressAssociated "addr1qx2kd28nq8ac5prwg32hhvudlwggpgfp8utlyqxu6wqgz62f79qsdmm5dsknt9ecr5w468r9ey0fxwkdrwh08ly3tu9sy0f4qd" , AddressAssociated "addr1q8j55h253zcvl326sk5qdt2n8z7eghzspe0ekxgncr796s2f79qsdmm5dsknt9ecr5w468r9ey0fxwkdrwh08ly3tu9sjmd35m" ] + +-- | Detailed information about account associated addresses +data AddressAssociatedTotal = AddressAssociatedTotal { + _addressAssociatedTotalStakeAddress :: Address -- ^ Bech32 encoded address + , _addressAssociatedTotalReceivedSum :: [Amount] + , _addressAssociatedTotalSentSum :: [Amount] + , _addressAssociatedTotalTxCount :: Integer -- ^ Count of all transactions for all addresses associated with the account + } deriving stock (Show, Eq, Generic) + deriving (FromJSON, ToJSON) + via CustomJSON '[FieldLabelModifier '[StripPrefix "_addressAssociatedTotal", CamelToSnake]] AddressAssociatedTotal + +instance ToSample AddressAssociatedTotal where + toSamples = pure $ singleSample + AddressAssociatedTotal + { _addressAssociatedTotalStakeAddress = "stake1u9l5q5jwgelgagzyt6nuaasefgmn8pd25c8e9qpeprq0tdcp0e3uk" + , _addressAssociatedTotalReceivedSum = + [ AdaAmount 42000000 + , AssetAmount + $ Money.mkSomeDiscrete + "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e" + unitScale + 12 + ] + , _addressAssociatedTotalSentSum = + [ AdaAmount 123 + , AssetAmount + $ Money.mkSomeDiscrete + "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e" + unitScale + 1 + ] + , _addressAssociatedTotalTxCount = 2 + } diff --git a/blockfrost-api/test/Cardano/Accounts.hs b/blockfrost-api/test/Cardano/Accounts.hs index faeba79..c8ebe7d 100644 --- a/blockfrost-api/test/Cardano/Accounts.hs +++ b/blockfrost-api/test/Cardano/Accounts.hs @@ -57,6 +57,11 @@ spec_sample = do `shouldBe` Right accountAssociatedAddressesExpected + it "parses account associated addresses total sample" $ do + eitherDecode accountAssociatedAddressesTotalSample + `shouldBe` + Right accountAssociatedAddressesTotalExpected + accountSample = [r| { "stake_address": "stake1ux3g2c9dx2nhhehyrezyxpkstartcqmu9hk63qgfkccw5rqttygt7", @@ -300,3 +305,52 @@ accountAssociatedAddressesExpected = [ AddressAssociated "addr1qx2kd28nq8ac5prwg32hhvudlwggpgfp8utlyqxu6wqgz62f79qsdmm5dsknt9ecr5w468r9ey0fxwkdrwh08ly3tu9sy0f4qd" , AddressAssociated "addr1q8j55h253zcvl326sk5qdt2n8z7eghzspe0ekxgncr796s2f79qsdmm5dsknt9ecr5w468r9ey0fxwkdrwh08ly3tu9sjmd35m" ] + +accountAssociatedAddressesTotalSample = [r| +{ + "stake_address": "stake1u9l5q5jwgelgagzyt6nuaasefgmn8pd25c8e9qpeprq0tdcp0e3uk", + "received_sum": [ + { + "unit": "lovelace", + "quantity": "42000000" + }, + { + "unit": "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e", + "quantity": "12" + } + ], + "sent_sum": [ + { + "unit": "lovelace", + "quantity": "42000000" + }, + { + "unit": "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e", + "quantity": "12" + } + ], + "tx_count": 12 +} +|] + +accountAssociatedAddressesTotalExpected = + AddressAssociatedTotal + { _addressAssociatedTotalStakeAddress = "stake1u9l5q5jwgelgagzyt6nuaasefgmn8pd25c8e9qpeprq0tdcp0e3uk" + , _addressAssociatedTotalReceivedSum = + [ AdaAmount 42000000 + , AssetAmount + $ Money.mkSomeDiscrete + "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e" + unitScale + 12 + ] + , _addressAssociatedTotalSentSum = + [ AdaAmount 42000000 + , AssetAmount + $ Money.mkSomeDiscrete + "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e" + unitScale + 12 + ] + , _addressAssociatedTotalTxCount = 12 + } From 4183b5059174fa165eb8cd20660c281b86ecc2aa Mon Sep 17 00:00:00 2001 From: sorki Date: Mon, 18 Dec 2023 07:00:13 +0100 Subject: [PATCH 06/16] client: add getAccountAssociatedAddressesTotal for /accounts/:stake_address/addresses/total --- blockfrost-client/CHANGELOG.md | 2 ++ blockfrost-client/src/Blockfrost/Client.hs | 1 + .../src/Blockfrost/Client/Cardano/Accounts.hs | 7 +++++++ 3 files changed, 10 insertions(+) diff --git a/blockfrost-client/CHANGELOG.md b/blockfrost-client/CHANGELOG.md index f18a011..38b936c 100644 --- a/blockfrost-client/CHANGELOG.md +++ b/blockfrost-client/CHANGELOG.md @@ -1,6 +1,8 @@ # Version [next](https://github.com/blockfrost/blockfrost-haskell/compare/client-0.7.1.1...master) (2023-mm-dd) * Allow servant `0.20` +* Additions + * `getAccountAssociatedAddressesTotal` for `/accounts/:stake_address/addresses/total` # Version [0.7.1.1](https://github.com/blockfrost/blockfrost-haskell/compare/v0.7.1.0...client-0.7.1.1) (2023-01-10) diff --git a/blockfrost-client/src/Blockfrost/Client.hs b/blockfrost-client/src/Blockfrost/Client.hs index c3b964f..704554e 100644 --- a/blockfrost-client/src/Blockfrost/Client.hs +++ b/blockfrost-client/src/Blockfrost/Client.hs @@ -31,6 +31,7 @@ module Blockfrost.Client , getAccountMirs' , getAccountAssociatedAddresses , getAccountAssociatedAddresses' + , getAccountAssociatedAddressesTotal , getAccountAssociatedAssets , getAccountAssociatedAssets' -- Cardano - Addresses diff --git a/blockfrost-client/src/Blockfrost/Client/Cardano/Accounts.hs b/blockfrost-client/src/Blockfrost/Client/Cardano/Accounts.hs index f5c5838..360e0e2 100644 --- a/blockfrost-client/src/Blockfrost/Client/Cardano/Accounts.hs +++ b/blockfrost-client/src/Blockfrost/Client/Cardano/Accounts.hs @@ -16,6 +16,7 @@ module Blockfrost.Client.Cardano.Accounts , getAccountMirs' , getAccountAssociatedAddresses , getAccountAssociatedAddresses' + , getAccountAssociatedAddressesTotal , getAccountAssociatedAssets , getAccountAssociatedAssets' ) where @@ -146,6 +147,12 @@ getAccountAssociatedAddresses' a pg s = go (\p -> getAccountAssociatedAddresses_ getAccountAssociatedAddresses :: MonadBlockfrost m => Address -> m [AddressAssociated] getAccountAssociatedAddresses a = getAccountAssociatedAddresses' a def def +getAccountAssociatedAddressesTotal_ :: MonadBlockfrost m => Project -> Address -> m AddressAssociatedTotal +getAccountAssociatedAddressesTotal_ = _accountAssociatedTotal . accountsClient + +getAccountAssociatedAddressesTotal :: MonadBlockfrost m => Address -> m AddressAssociatedTotal +getAccountAssociatedAddressesTotal a = go (`getAccountAssociatedAddressesTotal_` a) + getAccountAssociatedAssets_ :: MonadBlockfrost m => Project -> Address -> Paged -> SortOrder -> m [Amount] getAccountAssociatedAssets_ = _accountAssociatedAssets . accountsClient From 10e42b98d8cfd278152ed2ba299c7e56ce6d2ac4 Mon Sep 17 00:00:00 2001 From: sorki Date: Mon, 18 Dec 2023 07:22:05 +0100 Subject: [PATCH 07/16] api: add /pools/extended endpoint, Pool type --- blockfrost-api/CHANGELOG.md | 1 + .../src/Blockfrost/API/Cardano/Pools.hs | 8 +++ .../src/Blockfrost/Types/Cardano/Pools.hs | 36 ++++++++++++- blockfrost-api/test/Cardano/Pools.hs | 50 +++++++++++++++++++ 4 files changed, 94 insertions(+), 1 deletion(-) diff --git a/blockfrost-api/CHANGELOG.md b/blockfrost-api/CHANGELOG.md index 6d82f32..ffe6b08 100644 --- a/blockfrost-api/CHANGELOG.md +++ b/blockfrost-api/CHANGELOG.md @@ -3,6 +3,7 @@ * Allow servant `0.20` * Additions * `/accounts/:stake_address/addresses/total` endpoint and `AddressAssociatedTotal` type + * `/pools/extended` endpoint and `Pool` type # Version [0.8.1.0](https://github.com/blockfrost/blockfrost-haskell/compare/api-0.8.0.0...api-0.8.1.0) (2023-09-18) diff --git a/blockfrost-api/src/Blockfrost/API/Cardano/Pools.hs b/blockfrost-api/src/Blockfrost/API/Cardano/Pools.hs index 08a0aec..81e33a9 100644 --- a/blockfrost-api/src/Blockfrost/API/Cardano/Pools.hs +++ b/blockfrost-api/src/Blockfrost/API/Cardano/Pools.hs @@ -23,6 +23,14 @@ data PoolsAPI route = :> Pagination :> Sorting :> Get '[JSON] [PoolId] + , _listPoolsExtended + :: route + :- Summary "List of stake pools with additional information" + :> Description "List of registered stake pools with additional information." + :> "extended" + :> Pagination + :> Sorting + :> Get '[JSON] [Pool] , _listRetiredPools :: route :- Summary "List of retired stake pools" diff --git a/blockfrost-api/src/Blockfrost/Types/Cardano/Pools.hs b/blockfrost-api/src/Blockfrost/Types/Cardano/Pools.hs index cfc8c60..ed20f35 100644 --- a/blockfrost-api/src/Blockfrost/Types/Cardano/Pools.hs +++ b/blockfrost-api/src/Blockfrost/Types/Cardano/Pools.hs @@ -1,7 +1,8 @@ -- | Cardano Pools reponses module Blockfrost.Types.Cardano.Pools - ( PoolEpoch (..) + ( Pool (..) + , PoolEpoch (..) , PoolInfo (..) , PoolHistory (..) , PoolMetadata (..) @@ -19,6 +20,39 @@ import Servant.Docs (ToSample (..), samples, singleSample) import Blockfrost.Types.Shared +-- | Extended pool info +data Pool = Pool + { _poolPoolId :: PoolId -- ^ Bech32 encoded pool ID + , _poolHex :: Text -- ^ Hexadecimal pool ID. + , _poolActiveStake :: Lovelaces -- ^ Active delegated amount + , _poolLiveStake :: Lovelaces -- ^ Currently delegated amount + } + deriving stock (Show, Eq, Generic) + deriving (FromJSON, ToJSON) + via CustomJSON '[FieldLabelModifier '[StripPrefix "_pool", CamelToSnake]] Pool + +instance ToSample Pool where + toSamples = pure $ samples + [ Pool + { _poolPoolId = "pool19u64770wqp6s95gkajc8udheske5e6ljmpq33awxk326zjaza0q" + , _poolHex = "2f355f79ee007502d116ecb07e36f985b34cebf2d84118f5c6b455a1" + , _poolActiveStake = 1541200000 + , _poolLiveStake = 1541400000 + } + , Pool + { _poolPoolId = "pool1dvla4zq98hpvacv20snndupjrqhuc79zl6gjap565nku6et5zdx" + , _poolHex = "6b3fda88053dc2cee18a7c2736f032182fcc78a2fe912e869aa4edcd" + , _poolActiveStake = 22200000 + , _poolLiveStake = 48955550 + } + , Pool + { _poolPoolId = "pool1wvccajt4eugjtf3k0ja3exjqdj7t8egsujwhcw4tzj4rzsxzw5w" + , _poolHex = "73318ec975cf1125a6367cbb1c9a406cbcb3e510e49d7c3aab14aa31" + , _poolActiveStake = 9989541215 + , _poolLiveStake = 168445464878 + } + ] + -- | Retirement epoch for pool data PoolEpoch = PoolEpoch { _poolEpochPoolId :: PoolId -- ^ Bech32 encoded pool ID diff --git a/blockfrost-api/test/Cardano/Pools.hs b/blockfrost-api/test/Cardano/Pools.hs index 964dba7..c9d261c 100644 --- a/blockfrost-api/test/Cardano/Pools.hs +++ b/blockfrost-api/test/Cardano/Pools.hs @@ -17,6 +17,11 @@ import Blockfrost.Types spec_pools :: Spec spec_pools = do + it "parses pools sample" $ do + eitherDecode poolsSample + `shouldBe` + Right poolsExpected + it "parses pool epoch sample" $ do eitherDecode poolEpochSample `shouldBe` @@ -57,6 +62,51 @@ spec_pools = do `shouldBe` Right poolUpdatesExpected +poolsSample = [r| +[ + { + "pool_id": "pool19u64770wqp6s95gkajc8udheske5e6ljmpq33awxk326zjaza0q", + "hex": "2f355f79ee007502d116ecb07e36f985b34cebf2d84118f5c6b455a1", + "active_stake": "1541200000", + "live_stake": "1541400000" + }, + { + "pool_id": "pool1dvla4zq98hpvacv20snndupjrqhuc79zl6gjap565nku6et5zdx", + "hex": "6b3fda88053dc2cee18a7c2736f032182fcc78a2fe912e869aa4edcd", + "active_stake": "22200000", + "live_stake": "48955550" + }, + { + "pool_id": "pool1wvccajt4eugjtf3k0ja3exjqdj7t8egsujwhcw4tzj4rzsxzw5w", + "hex": "73318ec975cf1125a6367cbb1c9a406cbcb3e510e49d7c3aab14aa31", + "active_stake": "9989541215", + "live_stake": "168445464878" + } +] +|] + +poolsExpected = + [ Pool + { _poolPoolId = "pool19u64770wqp6s95gkajc8udheske5e6ljmpq33awxk326zjaza0q" + , _poolHex = "2f355f79ee007502d116ecb07e36f985b34cebf2d84118f5c6b455a1" + , _poolActiveStake = 1541200000 + , _poolLiveStake = 1541400000 + } + , Pool + { _poolPoolId = "pool1dvla4zq98hpvacv20snndupjrqhuc79zl6gjap565nku6et5zdx" + , _poolHex = "6b3fda88053dc2cee18a7c2736f032182fcc78a2fe912e869aa4edcd" + , _poolActiveStake = 22200000 + , _poolLiveStake = 48955550 + } + , Pool + { _poolPoolId = "pool1wvccajt4eugjtf3k0ja3exjqdj7t8egsujwhcw4tzj4rzsxzw5w" + , _poolHex = "73318ec975cf1125a6367cbb1c9a406cbcb3e510e49d7c3aab14aa31" + , _poolActiveStake = 9989541215 + , _poolLiveStake = 168445464878 + } + ] + + poolEpochSample = [r| [ { From 0bc7f0b95426e6e122ca3a03c8f3f79ff5abf470 Mon Sep 17 00:00:00 2001 From: sorki Date: Mon, 18 Dec 2023 07:25:49 +0100 Subject: [PATCH 08/16] client: add listPoolsExtended for /pools/extended endpoint --- blockfrost-client/CHANGELOG.md | 1 + blockfrost-client/src/Blockfrost/Client.hs | 2 ++ .../src/Blockfrost/Client/Cardano/Pools.hs | 18 ++++++++++++++++++ 3 files changed, 21 insertions(+) diff --git a/blockfrost-client/CHANGELOG.md b/blockfrost-client/CHANGELOG.md index 38b936c..6e3d749 100644 --- a/blockfrost-client/CHANGELOG.md +++ b/blockfrost-client/CHANGELOG.md @@ -3,6 +3,7 @@ * Allow servant `0.20` * Additions * `getAccountAssociatedAddressesTotal` for `/accounts/:stake_address/addresses/total` + * `listPoolsExtended` for `/pools/extended` # Version [0.7.1.1](https://github.com/blockfrost/blockfrost-haskell/compare/v0.7.1.0...client-0.7.1.1) (2023-01-10) diff --git a/blockfrost-client/src/Blockfrost/Client.hs b/blockfrost-client/src/Blockfrost/Client.hs index 704554e..4623917 100644 --- a/blockfrost-client/src/Blockfrost/Client.hs +++ b/blockfrost-client/src/Blockfrost/Client.hs @@ -102,6 +102,8 @@ module Blockfrost.Client -- Cardano - Pools , listPools , listPools' + , listPoolsExtended + , listPoolsExtended' , listRetiredPools , listRetiredPools' , listRetiringPools diff --git a/blockfrost-client/src/Blockfrost/Client/Cardano/Pools.hs b/blockfrost-client/src/Blockfrost/Client/Cardano/Pools.hs index 002127c..46857f3 100644 --- a/blockfrost-client/src/Blockfrost/Client/Cardano/Pools.hs +++ b/blockfrost-client/src/Blockfrost/Client/Cardano/Pools.hs @@ -3,6 +3,8 @@ module Blockfrost.Client.Cardano.Pools ( listPools , listPools' + , listPoolsExtended + , listPoolsExtended' , listRetiredPools , listRetiredPools' , listRetiringPools @@ -43,6 +45,22 @@ listPools' pg s = go (\p -> listPools_ p pg s) listPools :: MonadBlockfrost m => m [PoolId] listPools = listPools' def def +listPoolsExtended_ :: MonadBlockfrost m => Project -> Paged -> SortOrder -> m [Pool] +listPoolsExtended_ = _listPoolsExtended . poolsClient + +-- | List registered stake pools with additional info +-- Allows custom paging and ordering using 'Paged' and 'SortOrder'. +listPoolsExtended' :: MonadBlockfrost m => Paged -> SortOrder -> m [Pool] +listPoolsExtended' pg s = go (\p -> listPoolsExtended_ p pg s) + +-- | List registered stake pools with additional info. +-- +-- Queries 100 entries. To query all entries use 'Blockfrost.Client.Core.allPages' +-- with principled variant of this function (suffixed with @'@) +-- that accepts 'Paged' argument. +listPoolsExtended :: MonadBlockfrost m => m [Pool] +listPoolsExtended = listPoolsExtended' def def + listRetiredPools_ :: MonadBlockfrost m => Project -> Paged -> SortOrder -> m [PoolEpoch] listRetiredPools_ = _listRetiredPools . poolsClient From ab7d54b6f8902ba102e357b34a650f5d69f3d6ed Mon Sep 17 00:00:00 2001 From: sorki Date: Mon, 18 Dec 2023 07:46:48 +0100 Subject: [PATCH 09/16] api: add /utils/addresses/xpub/:xpub/:role/:index endpoint, DerivedAddress type --- blockfrost-api/CHANGELOG.md | 2 + blockfrost-api/blockfrost-api.cabal | 3 ++ blockfrost-api/src/Blockfrost/API.hs | 5 +++ blockfrost-api/src/Blockfrost/API/Cardano.hs | 2 + .../src/Blockfrost/API/Cardano/Utils.hs | 27 +++++++++++++ .../src/Blockfrost/Types/Cardano.hs | 2 + .../src/Blockfrost/Types/Cardano/Utils.hs | 29 ++++++++++++++ blockfrost-api/test/Cardano/Utils.hs | 40 +++++++++++++++++++ 8 files changed, 110 insertions(+) create mode 100644 blockfrost-api/src/Blockfrost/API/Cardano/Utils.hs create mode 100644 blockfrost-api/src/Blockfrost/Types/Cardano/Utils.hs create mode 100644 blockfrost-api/test/Cardano/Utils.hs diff --git a/blockfrost-api/CHANGELOG.md b/blockfrost-api/CHANGELOG.md index ffe6b08..71dc3d8 100644 --- a/blockfrost-api/CHANGELOG.md +++ b/blockfrost-api/CHANGELOG.md @@ -4,6 +4,8 @@ * Additions * `/accounts/:stake_address/addresses/total` endpoint and `AddressAssociatedTotal` type * `/pools/extended` endpoint and `Pool` type + * `/utils` API + * `/utils/addresses/xpub/:xpub/:role/:index` endpoint and `DerivedAddress` type # Version [0.8.1.0](https://github.com/blockfrost/blockfrost-haskell/compare/api-0.8.0.0...api-0.8.1.0) (2023-09-18) diff --git a/blockfrost-api/blockfrost-api.cabal b/blockfrost-api/blockfrost-api.cabal index 7bda3b5..014aeb7 100644 --- a/blockfrost-api/blockfrost-api.cabal +++ b/blockfrost-api/blockfrost-api.cabal @@ -72,6 +72,7 @@ library , Blockfrost.API.Cardano.Pools , Blockfrost.API.Cardano.Scripts , Blockfrost.API.Cardano.Transactions + , Blockfrost.API.Cardano.Utils , Blockfrost.API.IPFS , Blockfrost.API.NutLink , Blockfrost.Auth @@ -92,6 +93,7 @@ library , Blockfrost.Types.Cardano.Pools , Blockfrost.Types.Cardano.Scripts , Blockfrost.Types.Cardano.Transactions + , Blockfrost.Types.Cardano.Utils , Blockfrost.Types.IPFS , Blockfrost.Types.NutLink , Blockfrost.Types.Shared @@ -153,6 +155,7 @@ test-suite blockfrost-api-tests , Cardano.Pools , Cardano.Scripts , Cardano.Transactions + , Cardano.Utils , IPFS , NutLink build-depends: base >= 4.7 && < 5 diff --git a/blockfrost-api/src/Blockfrost/API.hs b/blockfrost-api/src/Blockfrost/API.hs index 5106c17..04f741a 100644 --- a/blockfrost-api/src/Blockfrost/API.hs +++ b/blockfrost-api/src/Blockfrost/API.hs @@ -132,6 +132,11 @@ data CardanoAPI route = :> "submit" :> ReqBody '[CBOR] CBORString :> Post '[JSON] TxHash + , _utils + :: route + :- "utils" + :> Tag "Cardano ยป Utilities" + :> ToServantApi UtilsAPI } deriving (Generic) type ServantBlockfrostAPI = ToServantApi BlockfrostAPI diff --git a/blockfrost-api/src/Blockfrost/API/Cardano.hs b/blockfrost-api/src/Blockfrost/API/Cardano.hs index 6265398..972f6f6 100644 --- a/blockfrost-api/src/Blockfrost/API/Cardano.hs +++ b/blockfrost-api/src/Blockfrost/API/Cardano.hs @@ -14,6 +14,7 @@ module Blockfrost.API.Cardano , module Blockfrost.API.Cardano.Pools , module Blockfrost.API.Cardano.Scripts , module Blockfrost.API.Cardano.Transactions + , module Blockfrost.API.Cardano.Utils ) where import Blockfrost.API.Cardano.Accounts @@ -27,3 +28,4 @@ import Blockfrost.API.Cardano.Network import Blockfrost.API.Cardano.Pools import Blockfrost.API.Cardano.Scripts import Blockfrost.API.Cardano.Transactions +import Blockfrost.API.Cardano.Utils diff --git a/blockfrost-api/src/Blockfrost/API/Cardano/Utils.hs b/blockfrost-api/src/Blockfrost/API/Cardano/Utils.hs new file mode 100644 index 0000000..18b93ce --- /dev/null +++ b/blockfrost-api/src/Blockfrost/API/Cardano/Utils.hs @@ -0,0 +1,27 @@ +-- | Cardano utility endpoints + +{-# OPTIONS_HADDOCK hide #-} + +module Blockfrost.API.Cardano.Utils + where + +import Data.Text +import Servant.API +import Servant.API.Generic + +import Blockfrost.Types.Cardano.Utils + +data UtilsAPI route = + UtilsAPI + { + _deriveAddr + :: route + :- Summary "Derive an address" + :> Description "Derive Shelley address from an xpub." + :> "addresses" + :> "xpub" + :> Capture "xpub" Text + :> Capture "role" Integer + :> Capture "Index" Integer + :> Get '[JSON] DerivedAddress + } deriving (Generic) diff --git a/blockfrost-api/src/Blockfrost/Types/Cardano.hs b/blockfrost-api/src/Blockfrost/Types/Cardano.hs index 5e3f64b..84a6808 100644 --- a/blockfrost-api/src/Blockfrost/Types/Cardano.hs +++ b/blockfrost-api/src/Blockfrost/Types/Cardano.hs @@ -12,6 +12,7 @@ module Blockfrost.Types.Cardano , module Blockfrost.Types.Cardano.Pools , module Blockfrost.Types.Cardano.Scripts , module Blockfrost.Types.Cardano.Transactions + , module Blockfrost.Types.Cardano.Utils ) where import Blockfrost.Types.Cardano.Accounts @@ -25,3 +26,4 @@ import Blockfrost.Types.Cardano.Network import Blockfrost.Types.Cardano.Pools import Blockfrost.Types.Cardano.Scripts import Blockfrost.Types.Cardano.Transactions +import Blockfrost.Types.Cardano.Utils diff --git a/blockfrost-api/src/Blockfrost/Types/Cardano/Utils.hs b/blockfrost-api/src/Blockfrost/Types/Cardano/Utils.hs new file mode 100644 index 0000000..3e464f4 --- /dev/null +++ b/blockfrost-api/src/Blockfrost/Types/Cardano/Utils.hs @@ -0,0 +1,29 @@ +-- | Cardano Utils responses + +module Blockfrost.Types.Cardano.Utils + ( DerivedAddress (..) + ) where + +import Data.Text (Text) +import Deriving.Aeson +import Servant.Docs (ToSample (..), singleSample) + +-- | Derived Shelley address +data DerivedAddress = DerivedAddress + { _derivedAddressXpub :: Text -- ^ Hexadecimal xpub + , _derivedAddressRole :: Integer -- ^ Account role + , _derivedAddressIndex :: Integer -- ^ Address index + , _derivedAddressAddress :: Text -- ^ Derived address + } + deriving stock (Show, Eq, Generic) + deriving (FromJSON, ToJSON) + via CustomJSON '[FieldLabelModifier '[StripPrefix "_derivedAddress", CamelToSnake]] DerivedAddress + +instance ToSample DerivedAddress where + toSamples = pure $ singleSample + DerivedAddress + { _derivedAddressXpub = "d507c8f866691bd96e131334c355188b1a1d0b2fa0ab11545075aab332d77d9eb19657ad13ee581b56b0f8d744d66ca356b93d42fe176b3de007d53e9c4c4e7a" + , _derivedAddressRole = 0 + , _derivedAddressIndex = 0 + , _derivedAddressAddress = "addr1q90sqnljxky88s0jsnps48jd872p7znzwym0jpzqnax6qs5nfrlkaatu28n0qzmqh7f2cpksxhpc9jefx3wrl0a2wu8q5amen7" + } diff --git a/blockfrost-api/test/Cardano/Utils.hs b/blockfrost-api/test/Cardano/Utils.hs new file mode 100644 index 0000000..0b97832 --- /dev/null +++ b/blockfrost-api/test/Cardano/Utils.hs @@ -0,0 +1,40 @@ +{-# LANGUAGE NumericUnderscores #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE TemplateHaskell #-} + +module Cardano.Utils + 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_scripts :: Spec +spec_scripts = do + it "parses derived address sample" $ do + eitherDecode derivedAddressSample + `shouldBe` + Right derivedAddressExpected + +derivedAddressSample = [r| +{ + "xpub": "d507c8f866691bd96e131334c355188b1a1d0b2fa0ab11545075aab332d77d9eb19657ad13ee581b56b0f8d744d66ca356b93d42fe176b3de007d53e9c4c4e7a", + "role": 0, + "index": 0, + "address": "addr1q90sqnljxky88s0jsnps48jd872p7znzwym0jpzqnax6qs5nfrlkaatu28n0qzmqh7f2cpksxhpc9jefx3wrl0a2wu8q5amen7" +} +|] + +derivedAddressExpected = + DerivedAddress + { _derivedAddressXpub = "d507c8f866691bd96e131334c355188b1a1d0b2fa0ab11545075aab332d77d9eb19657ad13ee581b56b0f8d744d66ca356b93d42fe176b3de007d53e9c4c4e7a" + , _derivedAddressRole = 0 + , _derivedAddressIndex = 0 + , _derivedAddressAddress = "addr1q90sqnljxky88s0jsnps48jd872p7znzwym0jpzqnax6qs5nfrlkaatu28n0qzmqh7f2cpksxhpc9jefx3wrl0a2wu8q5amen7" + } From 2f743940bb9c28561b4ca73bbf18071a979ad69e Mon Sep 17 00:00:00 2001 From: sorki Date: Mon, 18 Dec 2023 07:56:58 +0100 Subject: [PATCH 10/16] client: add deriveShelleyAddress for /utils/addresses/xpub/:xpub/:role/:index endpoint --- blockfrost-client/CHANGELOG.md | 2 ++ blockfrost-client/blockfrost-client.cabal | 1 + blockfrost-client/src/Blockfrost/Client.hs | 3 ++ .../src/Blockfrost/Client/Cardano/Utils.hs | 33 +++++++++++++++++++ 4 files changed, 39 insertions(+) create mode 100644 blockfrost-client/src/Blockfrost/Client/Cardano/Utils.hs diff --git a/blockfrost-client/CHANGELOG.md b/blockfrost-client/CHANGELOG.md index 6e3d749..f1e3407 100644 --- a/blockfrost-client/CHANGELOG.md +++ b/blockfrost-client/CHANGELOG.md @@ -4,6 +4,8 @@ * Additions * `getAccountAssociatedAddressesTotal` for `/accounts/:stake_address/addresses/total` * `listPoolsExtended` for `/pools/extended` + * `/utils` API + * `deriveShelleyAddress` for `/utils/addresses/xpub/:xpub/:role/:index` # Version [0.7.1.1](https://github.com/blockfrost/blockfrost-haskell/compare/v0.7.1.0...client-0.7.1.1) (2023-01-10) diff --git a/blockfrost-client/blockfrost-client.cabal b/blockfrost-client/blockfrost-client.cabal index 70a168a..05b1f9c 100644 --- a/blockfrost-client/blockfrost-client.cabal +++ b/blockfrost-client/blockfrost-client.cabal @@ -61,6 +61,7 @@ library , Blockfrost.Client.Cardano.Pools , Blockfrost.Client.Cardano.Scripts , Blockfrost.Client.Cardano.Transactions + , Blockfrost.Client.Cardano.Utils , Blockfrost.Client.IPFS , Blockfrost.Client.NutLink build-depends: base >= 4.7 && < 5 diff --git a/blockfrost-client/src/Blockfrost/Client.hs b/blockfrost-client/src/Blockfrost/Client.hs index 4623917..619e3fa 100644 --- a/blockfrost-client/src/Blockfrost/Client.hs +++ b/blockfrost-client/src/Blockfrost/Client.hs @@ -142,6 +142,8 @@ module Blockfrost.Client , getTxMetadataCBOR , getTxRedeemers , submitTx + -- Cardano - Utils + , deriveShelleyAddress -- IPFS , ipfsAdd , ipfsGateway @@ -177,6 +179,7 @@ import Blockfrost.Client.Cardano.Network import Blockfrost.Client.Cardano.Pools import Blockfrost.Client.Cardano.Scripts import Blockfrost.Client.Cardano.Transactions +import Blockfrost.Client.Cardano.Utils import Blockfrost.Client.IPFS import Blockfrost.Client.NutLink import Blockfrost.Client.Types diff --git a/blockfrost-client/src/Blockfrost/Client/Cardano/Utils.hs b/blockfrost-client/src/Blockfrost/Client/Cardano/Utils.hs new file mode 100644 index 0000000..4f881d6 --- /dev/null +++ b/blockfrost-client/src/Blockfrost/Client/Cardano/Utils.hs @@ -0,0 +1,33 @@ +-- | Utility queries + +module Blockfrost.Client.Cardano.Utils + ( deriveShelleyAddress + ) where + +import Data.Text (Text) +import Blockfrost.API +import Blockfrost.Client.Types +import Blockfrost.Types + +utilsClient :: MonadBlockfrost m => Project -> UtilsAPI (AsClientT m) +utilsClient = fromServant . _utils . cardanoClient + +-- | Derive Shelley address from xpub key +deriveShelleyAddress_ + :: MonadBlockfrost m + => Project + -> Text -- ^ Hexadecimal xpub + -> Integer -- ^ Account role + -> Integer -- ^ Address index + -> m DerivedAddress +deriveShelleyAddress_ = _deriveAddr . utilsClient + +-- | Derive Shelley address from xpub key +deriveShelleyAddress + :: MonadBlockfrost m + => Text -- ^ Hexadecimal xpub + -> Integer -- ^ Account role + -> Integer -- ^ Address index + -> m DerivedAddress +deriveShelleyAddress xpub role index = go (\p -> deriveShelleyAddress_ p xpub role index) + From ee666351ed3f0241e977aa7130223447e9f80491 Mon Sep 17 00:00:00 2001 From: sorki Date: Mon, 18 Dec 2023 09:04:52 +0100 Subject: [PATCH 11/16] api: add /utils/tx/evalue, /utils/tx/evalute/utxos and related types --- blockfrost-api/CHANGELOG.md | 3 + .../src/Blockfrost/API/Cardano/Utils.hs | 23 +++ .../src/Blockfrost/Types/Cardano/Utils.hs | 107 +++++++++++++ .../src/Blockfrost/Types/Shared/CBOR.hs | 20 +++ blockfrost-api/test/Cardano/Utils.hs | 141 +++++++++++++++++- 5 files changed, 293 insertions(+), 1 deletion(-) diff --git a/blockfrost-api/CHANGELOG.md b/blockfrost-api/CHANGELOG.md index 71dc3d8..c215537 100644 --- a/blockfrost-api/CHANGELOG.md +++ b/blockfrost-api/CHANGELOG.md @@ -6,6 +6,9 @@ * `/pools/extended` endpoint and `Pool` type * `/utils` API * `/utils/addresses/xpub/:xpub/:role/:index` endpoint and `DerivedAddress` type + * `/utils/txs/evaluate` endpoint + * `/utils/txs/evaluate/utxos` endpoint + * `TxEval`, `TxEvalResult`, `TxEvalBudget`, `TxEvalInput` types for the two above # Version [0.8.1.0](https://github.com/blockfrost/blockfrost-haskell/compare/api-0.8.0.0...api-0.8.1.0) (2023-09-18) diff --git a/blockfrost-api/src/Blockfrost/API/Cardano/Utils.hs b/blockfrost-api/src/Blockfrost/API/Cardano/Utils.hs index 18b93ce..0bff384 100644 --- a/blockfrost-api/src/Blockfrost/API/Cardano/Utils.hs +++ b/blockfrost-api/src/Blockfrost/API/Cardano/Utils.hs @@ -10,6 +10,7 @@ import Servant.API import Servant.API.Generic import Blockfrost.Types.Cardano.Utils +import Blockfrost.Types.Shared.CBOR data UtilsAPI route = UtilsAPI @@ -24,4 +25,26 @@ data UtilsAPI route = :> Capture "role" Integer :> Capture "Index" Integer :> Get '[JSON] DerivedAddress + + , _txEvaluate + :: route + :- Summary "Submit a transaction for execution units evaluation" + :> Description "Submit an already serialized transaction to evaluate \ + \how much execution units it requires." + :> "txs" + :> "evaluate" + :> ReqBody '[CBOR] CBORString + :> Post '[JSON] TxEval + + , _txEvaluateUTXOs + :: route + :- Summary "Submit a transaction for execution units evaluation (additional UTXO set)" + :> Description "Submit a JSON payload with transaction CBOR and additional UTXO set \ + \to evaluate how much execution units it requires." + :> "txs" + :> "evaluate" + :> "utxos" + :> ReqBody '[JSON] TxEvalInput + :> Post '[JSON] TxEval + } deriving (Generic) diff --git a/blockfrost-api/src/Blockfrost/Types/Cardano/Utils.hs b/blockfrost-api/src/Blockfrost/Types/Cardano/Utils.hs index 3e464f4..2ad8e20 100644 --- a/blockfrost-api/src/Blockfrost/Types/Cardano/Utils.hs +++ b/blockfrost-api/src/Blockfrost/Types/Cardano/Utils.hs @@ -1,12 +1,32 @@ +{-# LANGUAGE RecordWildCards #-} -- | Cardano Utils responses module Blockfrost.Types.Cardano.Utils ( DerivedAddress (..) + , TxEval (..) + , TxEvalBudget (..) + , TxEvalResult (..) + , evalSample + , resultSample + , TxEvalInput (..) ) where +import Data.Aeson + ( FromJSON (..) + , ToJSON (..) + , Value (Array) + , object + , withObject + , (.:) + , (.:?) + , (.=) + ) + +import Blockfrost.Types.Shared.CBOR (CBORString(..)) import Data.Text (Text) import Deriving.Aeson import Servant.Docs (ToSample (..), singleSample) +import qualified Data.Char -- | Derived Shelley address data DerivedAddress = DerivedAddress @@ -27,3 +47,90 @@ instance ToSample DerivedAddress where , _derivedAddressIndex = 0 , _derivedAddressAddress = "addr1q90sqnljxky88s0jsnps48jd872p7znzwym0jpzqnax6qs5nfrlkaatu28n0qzmqh7f2cpksxhpc9jefx3wrl0a2wu8q5amen7" } + +data TxEvalBudget = TxEvalBudget + { _txEvalBudgetMemory :: Integer -- ^ Memory budget + , _txEvalBudgetCPU :: Integer -- ^ CPU budget + } + deriving stock (Show, Eq, Generic) + deriving (FromJSON, ToJSON) + via CustomJSON '[FieldLabelModifier '[StripPrefix "_txEvalBudget", CamelToSnake]] TxEvalBudget + +instance ToSample TxEvalBudget where + toSamples = pure $ singleSample + TxEvalBudget + { _txEvalBudgetMemory = 1700 + , _txEvalBudgetCPU = 476468 + } + +-- | Transaction evaluation result +data TxEvalResult = TxEvalResult + { _txEvalResultValidator :: Text -- ^ Redeemer pointer + , _txEvalResultBudget :: TxEvalBudget -- ^ Budget + } + deriving stock (Show, Eq, Generic) + deriving (FromJSON, ToJSON) + via CustomJSON '[FieldLabelModifier '[StripPrefix "_txEvalResult", CamelToSnake]] TxEvalResult + +resultSample :: TxEvalResult +resultSample = + TxEvalResult + { _txEvalResultValidator = "spend:0" + , _txEvalResultBudget = + TxEvalBudget + { _txEvalBudgetMemory = 1700 + , _txEvalBudgetCPU = 476468 + } + } + +instance ToSample TxEvalResult where + toSamples = pure $ singleSample resultSample + +-- | Transaction evaluation result wrapper +newtype TxEval = TxEval { _txEvalResult :: [TxEvalResult] } + deriving stock (Show, Eq, Generic) + +instance ToJSON TxEval where + toJSON TxEval{..} = + object + [ "jsonrpc" .= ("2.0" :: Text) + , "method" .= ("evaluateTransaction" :: Text) + , "result" .= toJSON _txEvalResult + ] + +instance FromJSON TxEval where + parseJSON = withObject "txEval" $ \o -> do + (mErr :: Maybe Value) <- o .:? "error" + case mErr of + Just err -> fail $ show err + Nothing -> pure () + + r <- o .: "result" + TxEval <$> parseJSON r + +evalSample :: TxEval +evalSample = TxEval (pure resultSample) + +instance ToSample TxEval where + toSamples = pure $ singleSample evalSample + +data LowerLeading +instance StringModifier LowerLeading where + getStringModifier "" = "" + getStringModifier (c:xs) = Data.Char.toLower c : xs + +-- | Transaction evaluation input for UTXO variant +data TxEvalInput = TxEvalInput + { _txEvalInputCbor :: CBORString -- ^ CBOR encoded transaction + , _txEvalInputAdditionalUtxoSet :: Value -- ^ Additional UTXO set as JSON @Value@ + } + deriving stock (Show, Eq, Generic) + deriving (FromJSON, ToJSON) + via CustomJSON '[FieldLabelModifier '[StripPrefix "_txEvalInput", LowerLeading]] TxEvalInput + +instance ToSample TxEvalInput where + toSamples = pure $ singleSample + TxEvalInput + { _txEvalInputCbor = CBORString "83a40081825820daa9" + , _txEvalInputAdditionalUtxoSet = Array mempty + } diff --git a/blockfrost-api/src/Blockfrost/Types/Shared/CBOR.hs b/blockfrost-api/src/Blockfrost/Types/Shared/CBOR.hs index 3abaee7..726da19 100644 --- a/blockfrost-api/src/Blockfrost/Types/Shared/CBOR.hs +++ b/blockfrost-api/src/Blockfrost/Types/Shared/CBOR.hs @@ -3,10 +3,15 @@ module Blockfrost.Types.Shared.CBOR where +import Data.Aeson (FromJSON (..), ToJSON (..), withText) import Data.ByteString.Lazy (ByteString) import Servant.API (Accept (..), MimeRender (..), MimeUnrender (..)) import Servant.Docs (ToSample (..), singleSample) +import qualified Data.ByteString.Char8 +import qualified Data.ByteString.Lazy +import qualified Data.Text + data CBOR -- | Wrapper for CBOR encoded `ByteString`s @@ -14,6 +19,21 @@ data CBOR newtype CBORString = CBORString ByteString deriving stock (Eq, Show) +instance ToJSON CBORString where + toJSON (CBORString bs) = + toJSON + . Data.Text.pack + $ Data.ByteString.Char8.unpack + $ Data.ByteString.Lazy.toStrict bs + +instance FromJSON CBORString where + parseJSON = withText "CBORString" $ \t -> + pure + $ CBORString + <$> Data.ByteString.Lazy.fromStrict + . Data.ByteString.Char8.pack + $ Data.Text.unpack t + instance Accept CBOR where contentType = pure "application/cbor" diff --git a/blockfrost-api/test/Cardano/Utils.hs b/blockfrost-api/test/Cardano/Utils.hs index 0b97832..0905073 100644 --- a/blockfrost-api/test/Cardano/Utils.hs +++ b/blockfrost-api/test/Cardano/Utils.hs @@ -6,7 +6,8 @@ module Cardano.Utils where -import Data.Aeson (decode, eitherDecode, encode, object, (.=)) +import Data.Aeson (decode, eitherDecode, encode, object, (.=), Value (Array)) +import qualified Data.Either import Data.Text (Text) import qualified Money import Test.Hspec @@ -22,6 +23,21 @@ spec_scripts = do `shouldBe` Right derivedAddressExpected + it "parses tx eval sample" $ do + eitherDecode txEvalSample + `shouldBe` + Right txEvalExpected + + it "fails to parse tx eval error" $ do + eitherDecode txEvalErrorSample + `shouldSatisfy` + (Data.Either.isLeft :: Either String TxEval -> Bool) + + it "parses tx eval input sample" $ do + eitherDecode txEvalInputSample + `shouldBe` + Right txEvalInputExpected + derivedAddressSample = [r| { "xpub": "d507c8f866691bd96e131334c355188b1a1d0b2fa0ab11545075aab332d77d9eb19657ad13ee581b56b0f8d744d66ca356b93d42fe176b3de007d53e9c4c4e7a", @@ -38,3 +54,126 @@ derivedAddressExpected = , _derivedAddressIndex = 0 , _derivedAddressAddress = "addr1q90sqnljxky88s0jsnps48jd872p7znzwym0jpzqnax6qs5nfrlkaatu28n0qzmqh7f2cpksxhpc9jefx3wrl0a2wu8q5amen7" } + +txEvalSample = [r| +{ + "jsonrpc": "2.0", + "method": "evaluateTransaction", + "result": [{ + "validator": "spend:0", + "budget": { + "memory": 1700, + "cpu": 476468 + } + }] +} +|] + +txEvalExpected = evalSample + +-- Stolen from +-- https://github.com/CardanoSolutions/ogmios/blob/master/server/test/vectors/EvaluateTransactionResponse/000.json +-- Mozilla Public License 2.0 +txEvalErrorSample = [r| +{ + "jsonrpc": "2.0", + "method": "evaluateTransaction", + "error": { + "code": 3010, + "message": "Some scripts of the transactions terminated with error(s).", + "data": [ + { + "validator": "spend:4", + "error": { + "code": 3011, + "message": "An associated script witness is missing. Indeed, any script used in a transaction (when spending, minting, withdrawing or publishing certificates) must be provided in full with the transaction. Scripts must therefore be added either to the witness set or provided as a reference inputs should you use Plutus V2+ and a format from Babbage and beyond.", + "data": { + "missingScripts": [ + "certificate:3" + ] + } + } + }, + { + "validator": "mint:0", + "error": { + "code": 3011, + "message": "An associated script witness is missing. Indeed, any script used in a transaction (when spending, minting, withdrawing or publishing certificates) must be provided in full with the transaction. Scripts must therefore be added either to the witness set or provided as a reference inputs should you use Plutus V2+ and a format from Babbage and beyond.", + "data": { + "missingScripts": [ + "certificate:11" + ] + } + } + }, + { + "validator": "mint:3", + "error": { + "code": 3011, + "message": "An associated script witness is missing. Indeed, any script used in a transaction (when spending, minting, withdrawing or publishing certificates) must be provided in full with the transaction. Scripts must therefore be added either to the witness set or provided as a reference inputs should you use Plutus V2+ and a format from Babbage and beyond.", + "data": { + "missingScripts": [ + "withdrawal:7" + ] + } + } + }, + { + "validator": "mint:7", + "error": { + "code": 3011, + "message": "An associated script witness is missing. Indeed, any script used in a transaction (when spending, minting, withdrawing or publishing certificates) must be provided in full with the transaction. Scripts must therefore be added either to the witness set or provided as a reference inputs should you use Plutus V2+ and a format from Babbage and beyond.", + "data": { + "missingScripts": [ + "mint:4" + ] + } + } + }, + { + "validator": "mint:11", + "error": { + "code": 3117, + "message": "The transaction contains unknown UTxO references as inputs. This can happen if the inputs you're trying to spend have already been spent, or if you've simply referred to non-existing UTxO altogether. The field 'data.unknownOutputReferences' indicates all unknown inputs.", + "data": { + "unknownOutputReferences": [ + { + "transaction": { + "id": "a10897006ca78f6ce87fc6e2b139d92a896de01d62fe01f4fd0eccc6a10075c1" + }, + "index": 14 + } + ] + } + } + }, + { + "validator": "certificate:2", + "error": { + "code": 3011, + "message": "An associated script witness is missing. Indeed, any script used in a transaction (when spending, minting, withdrawing or publishing certificates) must be provided in full with the transaction. Scripts must therefore be added either to the witness set or provided as a reference inputs should you use Plutus V2+ and a format from Babbage and beyond.", + "data": { + "missingScripts": [ + "withdrawal:2" + ] + } + } + } + ] + }, + "id": null +} +|] + +txEvalInputSample = [r| +{ + "cbor": "sample", + "additionalUtxoSet": [] +} +|] + +txEvalInputExpected = + TxEvalInput + { _txEvalInputCbor = CBORString "sample" + , _txEvalInputAdditionalUtxoSet = Array mempty + } From e2671137f39a54f39c924067bf36ed8f6311b1b7 Mon Sep 17 00:00:00 2001 From: sorki Date: Mon, 18 Dec 2023 09:05:13 +0100 Subject: [PATCH 12/16] api: neaten changelog, add Env.Sanchonet entry --- blockfrost-api/CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/blockfrost-api/CHANGELOG.md b/blockfrost-api/CHANGELOG.md index c215537..74e06d1 100644 --- a/blockfrost-api/CHANGELOG.md +++ b/blockfrost-api/CHANGELOG.md @@ -1,7 +1,8 @@ # Version [next](https://github.com/blockfrost/blockfrost-haskell/compare/api-0.8.1.0-master) (2023-mm-dd) -* Allow servant `0.20` -* Additions +* Allow servant `0.20` [#41](https://github.com/blockfrost/blockfrost-haskell/pull/41) +* Additions [#43](https://github.com/blockfrost/blockfrost-haskell/pull/43) + * `Env` enum extended with `Sanchonet` for `cardano-sanchonet` network * `/accounts/:stake_address/addresses/total` endpoint and `AddressAssociatedTotal` type * `/pools/extended` endpoint and `Pool` type * `/utils` API From 7b4d5141733937538bd746a5fbac362792397316 Mon Sep 17 00:00:00 2001 From: sorki Date: Mon, 18 Dec 2023 09:18:43 +0100 Subject: [PATCH 13/16] client: add txEvaluate, txEvaluateUTXOs for /utils/txs/evaluate, /utils/txs/evaluate/utxos --- blockfrost-client/CHANGELOG.md | 2 ++ blockfrost-client/src/Blockfrost/Client.hs | 2 ++ .../src/Blockfrost/Client/Cardano/Utils.hs | 29 +++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/blockfrost-client/CHANGELOG.md b/blockfrost-client/CHANGELOG.md index f1e3407..9e28030 100644 --- a/blockfrost-client/CHANGELOG.md +++ b/blockfrost-client/CHANGELOG.md @@ -6,6 +6,8 @@ * `listPoolsExtended` for `/pools/extended` * `/utils` API * `deriveShelleyAddress` for `/utils/addresses/xpub/:xpub/:role/:index` + * `evaluateTx` for `/utils/txs/evaluate` endpoint + * `evaluateTxUTXOs` for `/utils/txs/evaluate/utxos` endpoint # Version [0.7.1.1](https://github.com/blockfrost/blockfrost-haskell/compare/v0.7.1.0...client-0.7.1.1) (2023-01-10) diff --git a/blockfrost-client/src/Blockfrost/Client.hs b/blockfrost-client/src/Blockfrost/Client.hs index 619e3fa..1e51bd8 100644 --- a/blockfrost-client/src/Blockfrost/Client.hs +++ b/blockfrost-client/src/Blockfrost/Client.hs @@ -144,6 +144,8 @@ module Blockfrost.Client , submitTx -- Cardano - Utils , deriveShelleyAddress + , txEvaluate + , txEvaluateUTXOs -- IPFS , ipfsAdd , ipfsGateway diff --git a/blockfrost-client/src/Blockfrost/Client/Cardano/Utils.hs b/blockfrost-client/src/Blockfrost/Client/Cardano/Utils.hs index 4f881d6..0dcac58 100644 --- a/blockfrost-client/src/Blockfrost/Client/Cardano/Utils.hs +++ b/blockfrost-client/src/Blockfrost/Client/Cardano/Utils.hs @@ -2,6 +2,8 @@ module Blockfrost.Client.Cardano.Utils ( deriveShelleyAddress + , txEvaluate + , txEvaluateUTXOs ) where import Data.Text (Text) @@ -31,3 +33,30 @@ deriveShelleyAddress -> m DerivedAddress deriveShelleyAddress xpub role index = go (\p -> deriveShelleyAddress_ p xpub role index) +txEvaluate_ + :: MonadBlockfrost m + => Project + -> CBORString + -> m TxEval +txEvaluate_ = _txEvaluate . utilsClient + +-- | Submit a transaction for execution units evaluation +txEvaluate + :: MonadBlockfrost m + => CBORString + -> m TxEval +txEvaluate txCbor = go (`txEvaluate_` txCbor) + +txEvaluateUTXOs_ + :: MonadBlockfrost m + => Project + -> TxEvalInput + -> m TxEval +txEvaluateUTXOs_ = _txEvaluateUTXOs . utilsClient + +-- | Submit a transaction for execution units evaluation (additional UTXO set) +txEvaluateUTXOs + :: MonadBlockfrost m + => TxEvalInput + -> m TxEval +txEvaluateUTXOs txei = go (`txEvaluateUTXOs_` txei) From ef5053fe41e4e4cd2a38e8dd5f18a30a6be563a5 Mon Sep 17 00:00:00 2001 From: sorki Date: Mon, 18 Dec 2023 09:20:38 +0100 Subject: [PATCH 14/16] client: add PR links to changelog --- blockfrost-client/CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blockfrost-client/CHANGELOG.md b/blockfrost-client/CHANGELOG.md index 9e28030..8a57b38 100644 --- a/blockfrost-client/CHANGELOG.md +++ b/blockfrost-client/CHANGELOG.md @@ -1,7 +1,7 @@ # Version [next](https://github.com/blockfrost/blockfrost-haskell/compare/client-0.7.1.1...master) (2023-mm-dd) -* Allow servant `0.20` -* Additions +* Allow servant `0.20` [#41](https://github.com/blockfrost/blockfrost-haskell/pull/41) +* Additions [#43](https://github.com/blockfrost/blockfrost-haskell/pull/43) * `getAccountAssociatedAddressesTotal` for `/accounts/:stake_address/addresses/total` * `listPoolsExtended` for `/pools/extended` * `/utils` API From 1233e39bd0cefc32a3992a6de3aa3807e06d216d Mon Sep 17 00:00:00 2001 From: sorki Date: Mon, 18 Dec 2023 10:11:08 +0100 Subject: [PATCH 15/16] api: add /addresses/:address/extended endpoint, AddressInfoExtended type --- blockfrost-api/CHANGELOG.md | 1 + .../src/Blockfrost/API/Cardano/Addresses.hs | 7 +++ .../src/Blockfrost/Types/Cardano/Addresses.hs | 33 +++++++++++ .../src/Blockfrost/Types/Shared/Amount.hs | 57 +++++++++++++++++++ blockfrost-api/test/Cardano/Addresses.hs | 48 ++++++++++++++++ 5 files changed, 146 insertions(+) diff --git a/blockfrost-api/CHANGELOG.md b/blockfrost-api/CHANGELOG.md index 74e06d1..a4f8d40 100644 --- a/blockfrost-api/CHANGELOG.md +++ b/blockfrost-api/CHANGELOG.md @@ -4,6 +4,7 @@ * Additions [#43](https://github.com/blockfrost/blockfrost-haskell/pull/43) * `Env` enum extended with `Sanchonet` for `cardano-sanchonet` network * `/accounts/:stake_address/addresses/total` endpoint and `AddressAssociatedTotal` type + * `/addresses/:address/extended` endpoint and `AddressInfoExtended` type (uses `AmountExtended` type) * `/pools/extended` endpoint and `Pool` type * `/utils` API * `/utils/addresses/xpub/:xpub/:role/:index` endpoint and `DerivedAddress` type diff --git a/blockfrost-api/src/Blockfrost/API/Cardano/Addresses.hs b/blockfrost-api/src/Blockfrost/API/Cardano/Addresses.hs index ae0327e..e591785 100644 --- a/blockfrost-api/src/Blockfrost/API/Cardano/Addresses.hs +++ b/blockfrost-api/src/Blockfrost/API/Cardano/Addresses.hs @@ -22,6 +22,13 @@ data AddressesAPI route = :> Description "Obtain information about a specific address." :> Capture "address" Address :> Get '[JSON] AddressInfo + , _addressInfoExtended + :: route + :- Summary "Specific address - extended" + :> Description "Obtain extended information about a specific address." + :> Capture "address" Address + :> "extended" + :> Get '[JSON] AddressInfoExtended , _addressDetails :: route :- Summary "Address details" diff --git a/blockfrost-api/src/Blockfrost/Types/Cardano/Addresses.hs b/blockfrost-api/src/Blockfrost/Types/Cardano/Addresses.hs index a7f5081..881b0d0 100644 --- a/blockfrost-api/src/Blockfrost/Types/Cardano/Addresses.hs +++ b/blockfrost-api/src/Blockfrost/Types/Cardano/Addresses.hs @@ -2,6 +2,7 @@ module Blockfrost.Types.Cardano.Addresses ( AddressInfo (..) + , AddressInfoExtended (..) , AddressType (..) , AddressDetails (..) , AddressUtxo (..) @@ -43,6 +44,38 @@ instance ToSample AddressInfo where , _addressInfoScript = False } +-- | Information about Cardano address +data AddressInfoExtended = AddressInfoExtended + { _addressInfoExtendedAddress :: Address -- ^ Bech32 encoded addresses + , _addressInfoExtendedAmount :: [AmountExtended] -- ^ Lovelaces or tokens stored on this address + , _addressInfoExtendedStakeAddress :: Maybe Address -- ^ Stake address that controls the key + , _addressInfoExtendedType :: AddressType -- ^ Address era + , _addressInfoExtendedScript :: Bool -- ^ True if this is a script address + } deriving stock (Show, Eq, Generic) + deriving (FromJSON, ToJSON) + via CustomJSON '[FieldLabelModifier '[StripPrefix "_addressInfoExtended", CamelToSnake]] AddressInfoExtended + +instance ToSample AddressInfoExtended where + toSamples = pure $ singleSample + AddressInfoExtended + { _addressInfoExtendedAddress = "addr1qxqs59lphg8g6qndelq8xwqn60ag3aeyfcp33c2kdp46a09re5df3pzwwmyq946axfcejy5n4x0y99wqpgtp2gd0k09qsgy6pz" + , _addressInfoExtendedAmount = + [ AdaAmountExtended 42000000 + , AssetAmountExtended + { assetAmountExtendedDecimals = Nothing + , assetAmountExtendedHasNftOnchainMetadata = True + , assetAmountExtendedValue = + Money.toSomeDiscrete + (12 :: Money.Discrete' + "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e" + '(1,1)) + } + ] + , _addressInfoExtendedStakeAddress = pure "stake1ux3g2c9dx2nhhehyrezyxpkstartcqmu9hk63qgfkccw5rqttygt7" + , _addressInfoExtendedType = Shelley + , _addressInfoExtendedScript = False + } + -- | Type (era) of an address data AddressType = Byron | Shelley deriving stock (Show, Eq, Generic) diff --git a/blockfrost-api/src/Blockfrost/Types/Shared/Amount.hs b/blockfrost-api/src/Blockfrost/Types/Shared/Amount.hs index d3ace3a..8ad1658 100644 --- a/blockfrost-api/src/Blockfrost/Types/Shared/Amount.hs +++ b/blockfrost-api/src/Blockfrost/Types/Shared/Amount.hs @@ -1,6 +1,7 @@ -- | Amount sum type {-# LANGUAGE NumericUnderscores #-} +{-# LANGUAGE RecordWildCards #-} module Blockfrost.Types.Shared.Amount where @@ -79,3 +80,59 @@ instance ToSample Amount where "6804edf9712d2b619edb6ac86861fe93a730693183a262b165fcc1ba1bc99cad" '(1,1)) ] + +-- | Like @Amount@, extended with @decimals` and `has_nft_onchain_metadata` +data AmountExtended = + AdaAmountExtended Lovelaces + | AssetAmountExtended + { assetAmountExtendedDecimals :: Maybe Int + , assetAmountExtendedHasNftOnchainMetadata :: Bool + , assetAmountExtendedValue :: Money.SomeDiscrete + } + deriving (Eq, Show, Ord, Generic) + +instance ToJSON AmountExtended where + toJSON (AdaAmountExtended lovelaces) = + object [ "unit" .= ("lovelace" :: String) + , "quantity" .= toJSON lovelaces + , "decimals" .= (6 :: Int) + , "has_nft_onchain_metadata" .= False + ] + toJSON (AssetAmountExtended {..}) = + object [ "unit" .= Money.someDiscreteCurrency assetAmountExtendedValue + , "quantity" .= show (Money.someDiscreteAmount assetAmountExtendedValue) + , "decimals" .= assetAmountExtendedDecimals + , "has_nft_onchain_metadata" .= assetAmountExtendedHasNftOnchainMetadata + ] + +instance FromJSON AmountExtended where + parseJSON x@(Object o) = do + (u :: String) <- o .: "unit" + v <- o .: "quantity" + assetAmountExtendedDecimals + <- o .: "decimals" + assetAmountExtendedHasNftOnchainMetadata + <- o .: "has_nft_onchain_metadata" + case u of + "lovelace" -> AdaAmountExtended <$> parseJSON v + _ -> do + assetAmountExtendedValue <- parseJSON x + pure AssetAmountExtended{..} + + parseJSON other = fail $ "Amount expecting object, got" ++ show other + +instance ToSample AmountExtended where + toSamples = pure $ samples + [ AdaAmountExtended 42000000 + , AssetAmountExtended + { assetAmountExtendedDecimals = Nothing + , assetAmountExtendedHasNftOnchainMetadata = True + , assetAmountExtendedValue = + Money.toSomeDiscrete + (12 :: Money.Discrete' + "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e" + '(1,1)) + } + ] + + diff --git a/blockfrost-api/test/Cardano/Addresses.hs b/blockfrost-api/test/Cardano/Addresses.hs index 1e066da..fce8730 100644 --- a/blockfrost-api/test/Cardano/Addresses.hs +++ b/blockfrost-api/test/Cardano/Addresses.hs @@ -22,6 +22,11 @@ spec_sample = do `shouldBe` Right addressInfoExpected + it "parses address info extended sample" $ do + eitherDecode addressInfoExtendedSample + `shouldBe` + Right addressInfoExtendedExpected + it "parses address info sample" $ do eitherDecode addressInfoSample `shouldBe` @@ -77,6 +82,49 @@ addressInfoExpected = , _addressInfoScript = False } +addressInfoExtendedSample = [r| +{ + "address": "addr1qxqs59lphg8g6qndelq8xwqn60ag3aeyfcp33c2kdp46a09re5df3pzwwmyq946axfcejy5n4x0y99wqpgtp2gd0k09qsgy6pz", + "amount": [ + { + "unit": "lovelace", + "quantity": "42000000", + "decimals": 6, + "has_nft_onchain_metadata": false + }, + { + "unit": "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e", + "quantity": "12", + "decimals": null, + "has_nft_onchain_metadata": true + } + ], + "stake_address": "stake1ux3g2c9dx2nhhehyrezyxpkstartcqmu9hk63qgfkccw5rqttygt7", + "type": "shelley", + "script": false +} +|] + +addressInfoExtendedExpected = + AddressInfoExtended + { _addressInfoExtendedAddress = "addr1qxqs59lphg8g6qndelq8xwqn60ag3aeyfcp33c2kdp46a09re5df3pzwwmyq946axfcejy5n4x0y99wqpgtp2gd0k09qsgy6pz" + , _addressInfoExtendedAmount = + [ AdaAmountExtended 42000000 + , AssetAmountExtended + { assetAmountExtendedDecimals = Nothing + , assetAmountExtendedHasNftOnchainMetadata = True + , assetAmountExtendedValue = + Money.mkSomeDiscrete + "b0d07d45fe9514f80213f4020e5a61241458be626841cde717cb38a76e7574636f696e" + unitScale + 12 + } + ] + , _addressInfoExtendedStakeAddress = pure "stake1ux3g2c9dx2nhhehyrezyxpkstartcqmu9hk63qgfkccw5rqttygt7" + , _addressInfoExtendedType = Shelley + , _addressInfoExtendedScript = False + } + addressDetailsSample = [r| { "address": "addr1qxqs59lphg8g6qndelq8xwqn60ag3aeyfcp33c2kdp46a09re5df3pzwwmyq946axfcejy5n4x0y99wqpgtp2gd0k09qsgy6pz", From 4c5a26cb06c6799a2b494b45e0da04786ae7e623 Mon Sep 17 00:00:00 2001 From: sorki Date: Mon, 18 Dec 2023 10:16:05 +0100 Subject: [PATCH 16/16] api: add getAddressInfoExtended for /addresses/:address/extended endpoint --- blockfrost-client/CHANGELOG.md | 1 + blockfrost-client/src/Blockfrost/Client.hs | 1 + .../src/Blockfrost/Client/Cardano/Addresses.hs | 8 ++++++++ 3 files changed, 10 insertions(+) diff --git a/blockfrost-client/CHANGELOG.md b/blockfrost-client/CHANGELOG.md index 8a57b38..40f8d08 100644 --- a/blockfrost-client/CHANGELOG.md +++ b/blockfrost-client/CHANGELOG.md @@ -3,6 +3,7 @@ * Allow servant `0.20` [#41](https://github.com/blockfrost/blockfrost-haskell/pull/41) * Additions [#43](https://github.com/blockfrost/blockfrost-haskell/pull/43) * `getAccountAssociatedAddressesTotal` for `/accounts/:stake_address/addresses/total` + * `getAddressInfoExtended` for `/addresses/:address/extended` * `listPoolsExtended` for `/pools/extended` * `/utils` API * `deriveShelleyAddress` for `/utils/addresses/xpub/:xpub/:role/:index` diff --git a/blockfrost-client/src/Blockfrost/Client.hs b/blockfrost-client/src/Blockfrost/Client.hs index 1e51bd8..2cb89c0 100644 --- a/blockfrost-client/src/Blockfrost/Client.hs +++ b/blockfrost-client/src/Blockfrost/Client.hs @@ -36,6 +36,7 @@ module Blockfrost.Client , getAccountAssociatedAssets' -- Cardano - Addresses , getAddressInfo + , getAddressInfoExtended , getAddressDetails , getAddressUtxos , getAddressUtxos' diff --git a/blockfrost-client/src/Blockfrost/Client/Cardano/Addresses.hs b/blockfrost-client/src/Blockfrost/Client/Cardano/Addresses.hs index 19bbef3..e5d0535 100644 --- a/blockfrost-client/src/Blockfrost/Client/Cardano/Addresses.hs +++ b/blockfrost-client/src/Blockfrost/Client/Cardano/Addresses.hs @@ -2,6 +2,7 @@ module Blockfrost.Client.Cardano.Addresses ( getAddressInfo + , getAddressInfoExtended , getAddressDetails , getAddressUtxos , getAddressUtxos' @@ -25,6 +26,13 @@ getAddressInfo_ = _addressInfo . addressesClient getAddressInfo :: MonadBlockfrost m => Address -> m AddressInfo getAddressInfo a = go (`getAddressInfo_` a) +getAddressInfoExtended_ :: MonadBlockfrost m => Project -> Address -> m AddressInfoExtended +getAddressInfoExtended_ = _addressInfoExtended . addressesClient + +-- | Obtain extended information about a specific address. +getAddressInfoExtended :: MonadBlockfrost m => Address -> m AddressInfoExtended +getAddressInfoExtended a = go (`getAddressInfoExtended_` a) + getAddressDetails_ :: MonadBlockfrost m => Project -> Address -> m AddressDetails getAddressDetails_ = _addressDetails . addressesClient