diff --git a/app/Main.hs b/app/Main.hs
--- a/app/Main.hs
+++ b/app/Main.hs
@@ -214,7 +214,7 @@
   where
     l _ lvl
         | deb = True
-        | otherwise = LevelWarn <= lvl
+        | otherwise = LevelInfo <= lvl
     clear =
         case cd of
             Nothing -> return ()
diff --git a/haskoin-store.cabal b/haskoin-store.cabal
--- a/haskoin-store.cabal
+++ b/haskoin-store.cabal
@@ -4,10 +4,10 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: 545948e0c4f4a34a7d6185ca86f13515e6f723853f2722c630202699fa2bf266
+-- hash: 2b80cfe38cf9c795efc07f40baf79adabae03baf6e15dcaa6c752ae6fdfad488
 
 name:           haskoin-store
-version:        0.16.5
+version:        0.16.6
 synopsis:       Storage and index for Bitcoin and Bitcoin Cash
 description:    Store blocks, transactions, and balances for Bitcoin or Bitcoin Cash, and make that information via REST API.
 category:       Bitcoin, Finance, Network
@@ -72,7 +72,7 @@
     , transformers
     , unliftio
     , unordered-containers
-    , uuid
+    , wai
   default-language: Haskell2010
 
 executable haskoin-store
@@ -112,7 +112,7 @@
     , transformers
     , unliftio
     , unordered-containers
-    , uuid
+    , wai
   default-language: Haskell2010
 
 test-suite haskoin-store-test
@@ -120,6 +120,7 @@
   main-is: Spec.hs
   other-modules:
       Haskoin.StoreSpec
+      Network.Haskoin.Store.DataSpec
       Paths_haskoin_store
   hs-source-dirs:
       test
@@ -153,6 +154,6 @@
     , transformers
     , unliftio
     , unordered-containers
-    , uuid
+    , wai
   default-language: Haskell2010
   build-tool-depends: hspec-discover:hspec-discover
diff --git a/src/Network/Haskoin/Store/Data.hs b/src/Network/Haskoin/Store/Data.hs
--- a/src/Network/Haskoin/Store/Data.hs
+++ b/src/Network/Haskoin/Store/Data.hs
@@ -1,8 +1,10 @@
 {-# LANGUAGE DeriveAnyClass        #-}
 {-# LANGUAGE DeriveGeneric         #-}
 {-# LANGUAGE FlexibleInstances     #-}
+{-# LANGUAGE LambdaCase            #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE OverloadedStrings     #-}
+
 module Network.Haskoin.Store.Data where
 
 import           Conduit
@@ -33,7 +35,7 @@
 import           Database.RocksDB          (DB, ReadOptions)
 import           GHC.Generics
 import           Haskoin                   as H
-import           Network.Socket            (SockAddr)
+import           Network.Socket            (SockAddr (SockAddrUnix))
 import           Paths_haskoin_store       as P
 import           UnliftIO
 import           UnliftIO.Exception
@@ -130,6 +132,7 @@
 
 instance BinSerial TxHash where
     binSerial _ = put
+    binDeserial _  = get
 
 instance BinSerial Address where
     binSerial net a =
@@ -137,11 +140,23 @@
             Nothing -> put B.empty
             Just x  -> put $ T.encodeUtf8 x
 
+    binDeserial net = do
+          bs <- get
+          guard (not (B.null bs))
+          t <- case T.decodeUtf8' bs of
+            Left _  -> mzero
+            Right v -> return v
+          case stringToAddr net t of
+            Nothing -> mzero
+            Just x  -> return x
+
 class BinSerial a where
     binSerial :: Network -> Putter a
+    binDeserial :: Network -> Get a
 
 instance BinSerial a => BinSerial [a] where
     binSerial net = putListOf (binSerial net)
+    binDeserial net = getListOf (binDeserial net)
 
 -- | Reference to a block where a transaction is stored.
 data BlockRef
@@ -182,6 +197,12 @@
         putWord8 0x01
         putWord64be t
 
+    binDeserial _ = getWord8 >>=
+        \case
+            0x00 -> BlockRef <$> getWord32be <*> getWord32be
+            0x01 -> MemRef <$> getUnixTime
+            _ -> fail "Expected fst byte to be 0x00 or 0x01"
+
 -- | JSON serialization for 'BlockRef'.
 blockRefPairs :: A.KeyValue kv => BlockRef -> [kv]
 blockRefPairs BlockRef {blockRefHeight = h, blockRefPos = p} =
@@ -220,10 +241,12 @@
     jsonValue _ = toJSON
 
 instance BinSerial BlockTx where
-    binSerial net BlockTx { blockTxBlock = b, blockTxHash = h }= do
+    binSerial net BlockTx { blockTxBlock = b, blockTxHash = h } = do
         binSerial net b
-        put h
+        binSerial net h
 
+    binDeserial net = BlockTx <$> binDeserial net <*> binDeserial net
+
 -- | Address balance information.
 data Balance = Balance
     { balanceAddress       :: !Address
@@ -276,6 +299,15 @@
         putWord64be c
         putWord64be t
 
+    binDeserial net =
+      Balance <$> binDeserial net
+        <*> getWord64be
+        <*> getWord64be
+        <*> getWord64be
+        <*> getWord64be
+        <*> getWord64be
+
+
 -- | Unspent output.
 data Unspent = Unspent
     { unspentBlock  :: !BlockRef
@@ -330,8 +362,15 @@
         binSerial net b
         put p
         putWord64be v
-        put $ B.Short.fromShort s
+        put s
 
+    binDeserial net =
+      Unspent
+      <$> binDeserial net
+      <*> get
+      <*> getWord64be
+      <*> get
+
 -- | Database value for a block entry.
 data BlockData = BlockData
     { blockDataHeight    :: !BlockHeight
@@ -388,6 +427,7 @@
 instance BinSerial BlockData where
     binSerial _ BlockData { blockDataHeight = e
                           , blockDataMainChain = m
+                          , blockDataWork = w
                           , blockDataHeader = h
                           , blockDataSize = z
                           , blockDataWeight = g
@@ -399,6 +439,7 @@
         put m
         putWord32be e
         put h
+        put w
         putWord32be z
         putWord32be g
         putWord64be o
@@ -406,6 +447,18 @@
         putWord64be y
         put t
 
+    binDeserial _ = do
+      m <- get
+      e <- getWord32be
+      h <- get
+      w <- get
+      z <- getWord32be
+      g <- getWord32be
+      o <- getWord64be
+      f <- getWord64be
+      y <- getWord64be
+      t <- get
+      return $ BlockData e m w h z g t o f y
 
 -- | Input information.
 data StoreInput
@@ -480,33 +533,6 @@
 inputToEncoding :: Network -> StoreInput -> Encoding
 inputToEncoding net = pairs . mconcat . inputPairs net
 
-instance BinSerial StoreInput where
-    binSerial net i = do
-        put $
-            case i of
-                StoreCoinbase {} -> True
-                StoreInput {}    -> False
-        put $ inputPoint i
-        putWord32be $ inputSequence i
-        put $ inputSigScript i
-        put $ inputWitness i
-        let a =
-                case i of
-                    StoreCoinbase {} -> Nothing
-                    StoreInput {inputPkScript = s} ->
-                        eitherToMaybe (scriptToAddressBS s)
-        case a of
-            Nothing -> put B.empty
-            Just x  -> binSerial net x
-        putWord64be $
-            case i of
-                StoreCoinbase {}             -> 0
-                StoreInput {inputAmount = v} -> v
-        put $
-            case i of
-                StoreCoinbase {}               -> B.empty
-                StoreInput {inputPkScript = s} -> s
-
 -- | Information about input spending output.
 data Spender = Spender
     { spenderHash  :: !TxHash
@@ -590,6 +616,33 @@
     , txDataTime    :: !Word64
     } deriving (Show, Eq, Ord, Generic, Serialize)
 
+instance BinSerial TxData where
+  binSerial _ TxData
+        { txDataBlock   = br
+        , txData        = tx
+        , txDataPrevs   = dp
+        , txDataDeleted = dd
+        , txDataRBF     = dr
+        , txDataTime    = t
+        } = do
+      put br
+      put tx
+      put dp
+      put dd
+      put dr
+      putWord64be t
+
+  binDeserial _ = do br <- get
+                     tx <- get
+                     dp <- get
+                     dd <- get
+                     dr <- get
+                     TxData br tx dp dd dr <$> getWord64be
+
+instance Serialize a => BinSerial (IntMap a) where
+  binSerial _ = put
+  binDeserial _ = get
+
 toTransaction :: TxData -> IntMap Spender -> Transaction
 toTransaction t sm =
     Transaction
@@ -704,24 +757,16 @@
     jsonValue = transactionToJSON
 
 instance BinSerial Transaction where
-    binSerial net Transaction { transactionBlock = b
-                              , transactionVersion = v
-                              , transactionLockTime = l
-                              , transactionInputs = is
-                              , transactionOutputs = os
-                              , transactionDeleted = d
-                              , transactionRBF = r
-                              , transactionTime = t
-                              } = do
-        binSerial net b
-        putWord32be v
-        putWord32be l
-        put d
-        put r
-        putWord64be t
-        put is
-        put os
+    binSerial net tx = do
+        let (txd, sp) = fromTransaction tx
+        binSerial net txd
+        binSerial net sp
 
+    binDeserial net = do
+      txd <- binDeserial net
+      sp <- binDeserial net
+      return $ toTransaction txd sp
+
 -- | Information about a connected peer.
 data PeerInformation
     = PeerInformation { peerUserAgent :: !ByteString
@@ -763,11 +808,17 @@
                                 , peerRelay = b
                                 } = do
         putWord32be v
-        putWord64be s
         put b
-        put $ show a
         put u
+        put $ NetworkAddress s a
 
+    binDeserial _ = do
+      v <- getWord32be
+      b <- get
+      u <- get
+      NetworkAddress { naServices = s, naAddress = a } <- get
+      return $ PeerInformation u a v s b
+
 -- | Address balances for an extended public key.
 data XPubBal = XPubBal
     { xPubBalPath :: ![KeyIndex]
@@ -795,6 +846,10 @@
     binSerial net XPubBal {xPubBalPath = p, xPubBal = b} = do
         put p
         binSerial net b
+    binDeserial net  = do
+      p <- get
+      b <- binDeserial net
+      return $ XPubBal p b
 
 -- | Unspent transaction for extended public key.
 data XPubUnspent = XPubUnspent
@@ -826,6 +881,11 @@
         put p
         binSerial net u
 
+    binDeserial net = do
+      p <- get
+      u <- binDeserial net
+      return $ XPubUnspent p u
+
 data XPubSummary =
     XPubSummary
         { xPubSummaryReceived  :: !Word64
@@ -880,13 +940,27 @@
         put z
         put ext
         put ch
-        putWord64be (fromIntegral $ M.size ps)
-        forM_ (M.toList ps) $ \(a, p) -> do
-            binSerial net a
-            put p
-        putWord64be (fromIntegral $ length ts)
-        forM_ ts $ binSerial net
+        put (map (first (runPut . binSerial net)) (M.toList ps))
+        put $ map (runPut . binSerial net) ts
 
+    binDeserial net = do
+      r <- get
+      c <- get
+      z <- get
+      ext <- get
+      ch <- get
+      ps <- get
+      let xs = map (first (runGet (binDeserial net))) ps
+      ys <- forM xs $ \(k, v) -> case k of
+        Right a -> return (a, v)
+        Left _  -> mzero
+      ts <- get
+      let txs = map (runGet (binDeserial net)) ts
+      tys <- forM txs $ \case
+        Right a -> return a
+        Left _ -> mzero
+      return $ XPubSummary r c z ext ch (M.fromList ys) tys
+
 data HealthCheck = HealthCheck
     { healthHeaderBest   :: !(Maybe BlockHash)
     , healthHeaderHeight :: !(Maybe BlockHeight)
@@ -938,6 +1012,8 @@
         put ok
         put synced
 
+    binDeserial _ = HealthCheck <$> get <*> get <*> get <*> get <*> get <*> get <*> get <*> get
+
 data Event
     = EventBlock BlockHash
     | EventTx TxHash
@@ -955,6 +1031,13 @@
     binSerial _ (EventBlock bh) = putWord8 0x00 >> put bh
     binSerial _ (EventTx th)    = putWord8 0x01 >> put th
 
+    binDeserial _ = getWord8 >>=
+            \case
+                0x00-> EventBlock <$> get
+                0x01 -> EventTx <$> get
+                _ -> fail "Expected fst byte to be 0x00 or 0x01"
+
+
 newtype TxAfterHeight = TxAfterHeight
     { txAfterHeight :: Maybe Bool
     } deriving (Show, Eq, Generic)
@@ -968,6 +1051,7 @@
 
 instance BinSerial TxAfterHeight where
     binSerial _ TxAfterHeight {txAfterHeight = a} = put a
+    binDeserial _ = TxAfterHeight <$> get
 
 newtype TxId = TxId TxHash deriving (Show, Eq, Generic)
 
@@ -980,6 +1064,7 @@
 
 instance BinSerial TxId where
     binSerial _ (TxId th) = put th
+    binDeserial _ = TxId <$> get
 
 data StartFrom
     = StartBlock !BlockHeight !BlockPos
diff --git a/src/Network/Haskoin/Store/Web.hs b/src/Network/Haskoin/Store/Web.hs
--- a/src/Network/Haskoin/Store/Web.hs
+++ b/src/Network/Haskoin/Store/Web.hs
@@ -5,6 +5,7 @@
 {-# LANGUAGE RankNTypes        #-}
 {-# LANGUAGE TemplateHaskell   #-}
 {-# LANGUAGE TupleSections     #-}
+
 module Network.Haskoin.Store.Web where
 import           Conduit                           hiding (runResourceT)
 import           Control.Applicative               ((<|>))
@@ -34,8 +35,7 @@
 import qualified Data.Text                         as T
 import qualified Data.Text.Encoding                as T
 import qualified Data.Text.Lazy                    as T.Lazy
-import           Data.UUID                         (UUID)
-import           Data.UUID.V4
+import           Data.Time.Clock
 import           Data.Version
 import           Data.Word                         (Word32)
 import           Database.RocksDB                  as R
@@ -45,6 +45,7 @@
 import           Network.Haskoin.Store.Data.Cached
 import           Network.Haskoin.Store.Messages
 import           Network.HTTP.Types
+import           Network.Wai
 import           NQE
 import qualified Paths_haskoin_store               as P
 import           Text.Read                         (readMaybe)
@@ -56,19 +57,19 @@
 type WebT m = ActionT Except (ReaderT LayeredDB m)
 
 data Except
-    = ThingNotFound UUID
-    | ServerError UUID
-    | BadRequest UUID
-    | UserError UUID String
+    = ThingNotFound
+    | ServerError
+    | BadRequest
+    | UserError String
     | StringError String
     deriving Eq
 
 instance Show Except where
-    show (ThingNotFound u) = "not found"
-    show (ServerError u)   = "you made me kill a unicorn"
-    show (BadRequest u)    = "bad request"
-    show (UserError u s)   = s
-    show (StringError s)   = "you killed the dragon with your bare hands"
+    show ThingNotFound   = "not found"
+    show ServerError     = "you made me kill a unicorn"
+    show BadRequest      = "bad request"
+    show (UserError s)   = s
+    show (StringError s) = "you killed the dragon with your bare hands"
 
 instance Exception Except
 
@@ -84,7 +85,20 @@
     jsonValue _ = toJSON
 
 instance BinSerial Except where
-    binSerial _ = Serialize.put . T.encodeUtf8 . T.pack . show
+    binSerial _ ex =
+        case ex of
+            ThingNotFound -> putWord8 0
+            ServerError   -> putWord8 1
+            BadRequest    -> putWord8 2
+            UserError s   -> putWord8 3 >> Serialize.put s
+            StringError s -> putWord8 4 >> Serialize.put s
+    binDeserial _ =
+        getWord8 >>= \case
+            0 -> return ThingNotFound
+            1 -> return ServerError
+            2 -> return BadRequest
+            3 -> UserError <$> Serialize.get
+            4 -> StringError <$> Serialize.get
 
 data WebConfig =
     WebConfig
@@ -131,22 +145,21 @@
 defHandler net e = do
     proto <- setupBin
     case e of
-        ThingNotFound _ -> status status404
-        BadRequest _    -> status status400
-        UserError _ _   -> status status400
-        StringError _   -> status status400
-        ServerError _   -> status status500
+        ThingNotFound -> status status404
+        BadRequest    -> status status400
+        UserError _   -> status status400
+        StringError _ -> status status400
+        ServerError   -> status status500
     protoSerial net proto e
 
 maybeSerial ::
        (Monad m, JsonSerial a, BinSerial a)
     => Network
-    -> UUID
     -> Bool -- ^ binary
     -> Maybe a
     -> WebT m ()
-maybeSerial _ u _ Nothing        = raise $ ThingNotFound u
-maybeSerial net _ proto (Just x) = S.raw $ serialAny net proto x
+maybeSerial _ _ Nothing        = raise ThingNotFound
+maybeSerial net proto (Just x) = S.raw $ serialAny net proto x
 
 protoSerial ::
        (Monad m, JsonSerial a, BinSerial a)
@@ -159,8 +172,6 @@
 scottyBestBlock :: MonadLoggerIO m => Network -> WebT m ()
 scottyBestBlock net = do
     cors
-    (i, u) <- uuid
-    $(logDebugS) i "Get best block"
     n <- parseNoTx
     proto <- setupBin
     res <-
@@ -168,32 +179,24 @@
             h <- MaybeT getBestBlock
             b <- MaybeT $ getBlock h
             return $ pruneTx n b
-    $(logDebugS) i $
-        "Response block hash: " <>
-        maybe "[none]" (blockHashToHex . headerHash . blockDataHeader) res
-    maybeSerial net u proto res
+    maybeSerial net proto res
 
 scottyBlock :: MonadLoggerIO m => Network -> WebT m ()
 scottyBlock net = do
     cors
     block <- param "block"
-    (i, u) <- uuid
-    $(logDebugS) i $ "Get block: " <> blockHashToHex block
     n <- parseNoTx
     proto <- setupBin
     res <-
         runMaybeT $ do
             b <- MaybeT $ getBlock block
             return $ pruneTx n b
-    $(logDebugS) i $ maybe "Block not found" (const "Block found") res
-    maybeSerial net u proto res
+    maybeSerial net proto res
 
 scottyBlockHeight :: MonadLoggerIO m => Network -> WebT m ()
 scottyBlockHeight net = do
     cors
     height <- param "height"
-    (i, u) <- uuid
-    $(logDebugS) i $ "Get blocks at height: " <> cs (show height)
     n <- parseNoTx
     proto <- setupBin
     res <-
@@ -203,15 +206,12 @@
                 runMaybeT $ do
                     b <- MaybeT $ getBlock h
                     return $ pruneTx n b
-    $(logDebugS) i $ "Blocks returned: " <> cs (show (length res))
     protoSerial net proto res
 
 scottyBlockHeights :: MonadLoggerIO m => Network -> WebT m ()
 scottyBlockHeights net = do
     cors
     heights <- param "heights"
-    (i, u) <- uuid
-    $(logDebugS) i $ "Get blocks at multiple heights: " <> cs (show heights)
     n <- parseNoTx
     proto <- setupBin
     bs <- concat <$> mapM getBlocksAtHeight (nub heights)
@@ -220,15 +220,12 @@
             runMaybeT $ do
                 b <- MaybeT $ getBlock bh
                 return $ pruneTx n b
-    $(logDebugS) i $ "Blocks returned: " <> cs (show (length res))
     protoSerial net proto res
 
 scottyBlocks :: MonadLoggerIO m => Network -> WebT m ()
 scottyBlocks net = do
     cors
     blocks <- param "blocks"
-    (i, u) <- uuid
-    $(logDebugS) i $ "Get multiple blocks: " <> cs (show blocks)
     n <- parseNoTx
     proto <- setupBin
     res <-
@@ -236,49 +233,35 @@
             runMaybeT $ do
                 b <- MaybeT $ getBlock bh
                 return $ pruneTx n b
-    $(logDebugS) i $ "Blocks returned: " <> cs (show (length res))
     protoSerial net proto res
 
 scottyMempool :: (MonadLoggerIO m, MonadUnliftIO m) => Network -> WebT m ()
 scottyMempool net = do
     cors
     (l, s) <- parseLimits
-    (i, u) <- uuid
     proto <- setupBin
     db <- askDB
-    $(logDebugS) i "Get mempool"
     stream $ \io flush' -> do
         runResourceT . withLayeredDB db $
             runConduit $ getMempoolLimit l s .| streamAny net proto io
         flush'
-    $(logDebugS) i "Mempool streaming complete"
 
 scottyTransaction :: MonadLoggerIO m => Network -> WebT m ()
 scottyTransaction net = do
     cors
     txid <- param "txid"
-    (i, u) <- uuid
-    $(logDebugS) i $ "Get transaction: " <> txHashToHex txid
     proto <- setupBin
     res <- getTransaction txid
-    case res of
-        Nothing -> $(logDebugS) i "Transaction not found"
-        Just _  -> $(logDebugS) i "Transaction found"
-    maybeSerial net u proto res
+    maybeSerial net proto res
 
 scottyRawTransaction :: MonadLoggerIO m => Bool -> WebT m ()
 scottyRawTransaction hex = do
     cors
     txid <- param "txid"
-    (i, u) <- uuid
-    $(logDebugS) i $ "Get raw transaction: " <> txHashToHex txid
     res <- getTransaction txid
     case res of
-        Nothing -> do
-            $(logDebugS) i "Transaction not found"
-            raise $ ThingNotFound u
+        Nothing -> raise ThingNotFound
         Just x -> do
-            $(logDebugS) i "Transaction found"
             if hex
                 then text . cs . encodeHex . Serialize.encode $
                      transactionData x
@@ -291,16 +274,8 @@
     cors
     txid <- param "txid"
     height <- param "height"
-    (i, _) <- uuid
-    $(logDebugS) i $
-        "Is transaction " <> txHashToHex txid <> "after height" <>
-        cs (show height) <> "?"
     proto <- setupBin
     res <- cbAfterHeight 10000 height txid
-    case txAfterHeight res of
-      Nothing    -> $(logDebugS) i $ "Could not find out"
-      Just False -> $(logDebugS) i "No"
-      Just True  -> $(logDebugS) i "Yes"
     protoSerial net proto res
 
 scottyTransactions :: MonadLoggerIO m => Network -> WebT m ()
@@ -308,20 +283,14 @@
     cors
     txids <- param "txids"
     proto <- setupBin
-    (i, _) <- uuid
-    $(logDebugS) i $ "Get transactions: " <> cs (show txids)
     res <- catMaybes <$> mapM getTransaction (nub txids)
-    $(logDebugS) i $ "Transactions returned: " <> cs (show (length res))
     protoSerial net proto res
 
 scottyRawTransactions :: MonadLoggerIO m => Bool -> WebT m ()
 scottyRawTransactions hex = do
     cors
     txids <- param "txids"
-    (i, _) <- uuid
-    $(logDebugS) i $ "Get raw transactions: " <> cs (show txids)
     res <- catMaybes <$> mapM getTransaction (nub txids)
-    $(logDebugS) i $ "Transactions returned: " <> cs (show (length res))
     if hex
         then S.json $ map (encodeHex . Serialize.encode . transactionData) res
         else do
@@ -335,14 +304,10 @@
     a <- parseAddress net
     (l, s) <- parseLimits
     proto <- setupBin
-    (i, _) <- uuid
-    $(logDebugS) i $
-        "Get transactions for address: " <> fromMaybe "???" (addrToString net a)
     db <- askDB
     stream $ \io flush' -> do
         runResourceT . withLayeredDB db . runConduit $ f proto l s a io
         flush'
-    $(logDebugS) i "Streamed transactions"
   where
     f proto l s a io
         | full = getAddressTxsFull l s a .| streamAny net proto io
@@ -355,15 +320,10 @@
     as <- parseAddresses net
     (l, s) <- parseLimits
     proto <- setupBin
-    (i, _) <- uuid
-    $(logDebugS) i $
-        "Get transactions for addresses: [" <>
-        T.intercalate "," (map (fromMaybe "???" . addrToString net) as) <> "]"
     db <- askDB
     stream $ \io flush' -> do
         runResourceT . withLayeredDB db . runConduit $ f proto l s as io
         flush'
-    $(logDebugS) i "Streamed transactions"
   where
     f proto l s as io
         | full = getAddressesTxsFull l s as .| streamAny net proto io
@@ -374,9 +334,6 @@
 scottyAddressUnspent net = do
     cors
     a <- parseAddress net
-    (i, _) <- uuid
-    $(logDebugS) i $
-        "Get UTXO for address: " <> fromMaybe "???" (addrToString net a)
     (l, s) <- parseLimits
     proto <- setupBin
     db <- askDB
@@ -384,7 +341,6 @@
         runResourceT . withLayeredDB db . runConduit $
             getAddressUnspentsLimit l s a .| streamAny net proto io
         flush'
-    $(logDebugS) i "Streamed UTXO"
 
 scottyAddressesUnspent ::
        (MonadLoggerIO m, MonadUnliftIO m) => Network -> WebT m ()
@@ -392,26 +348,18 @@
     cors
     as <- parseAddresses net
     (l, s) <- parseLimits
-    (i, _) <- uuid
-    $(logDebugS) i $
-        "Get UTXO for addresses: [" <>
-        T.intercalate "," (map (fromMaybe "???" . addrToString net) as) <> "]"
     proto <- setupBin
     db <- askDB
     stream $ \io flush' -> do
         runResourceT . withLayeredDB db . runConduit $
             getAddressesUnspentsLimit l s as .| streamAny net proto io
         flush'
-    $(logDebugS) i "Streamed UTXO"
 
 scottyAddressBalance :: MonadLoggerIO m => Network -> WebT m ()
 scottyAddressBalance net = do
     cors
     a <- parseAddress net
     proto <- setupBin
-    (i, _) <- uuid
-    $(logDebugS) i $
-        "Get balance for address: " <> fromMaybe "???" (addrToString net a)
     res <-
         getBalance a >>= \case
             Just b -> return b
@@ -425,7 +373,6 @@
                         , balanceTxCount = 0
                         , balanceTotalReceived = 0
                         }
-    $(logDebugS) i "Returned balance"
     protoSerial net proto res
 
 scottyAddressesBalances :: MonadLoggerIO m => Network -> WebT m ()
@@ -433,11 +380,6 @@
     cors
     as <- parseAddresses net
     proto <- setupBin
-    (i, _) <- uuid
-    $(logDebugS) i $
-        "Get UTXO for addresses: [" <>
-        T.intercalate "," (map (fromMaybe "???" . addrToString net) as) <>
-        "]"
     let f a Nothing =
             Balance
                 { balanceAddress = a
@@ -449,7 +391,6 @@
                 }
         f _ (Just b) = b
     res <- mapM (\a -> f a <$> getBalance a) as
-    $(logDebugS) i $ "Returned balances: " <> cs (show (length res))
     protoSerial net proto res
 
 scottyXpubBalances :: (MonadUnliftIO m, MonadLoggerIO m) => Network -> WebT m ()
@@ -457,11 +398,8 @@
     cors
     xpub <- parseXpub net
     proto <- setupBin
-    (i, _) <- uuid
-    $(logDebugS) i $ "Get balances for xpub: " <> xPubExport net xpub
     db <- askDB
     res <- liftIO . runResourceT . withLayeredDB db $ xpubBals xpub
-    $(logDebugS) i $ "Returned balances: " <> cs (show (length res))
     protoSerial net proto res
 
 scottyXpubTxs ::
@@ -471,14 +409,11 @@
     x <- parseXpub net
     (l, s) <- parseLimits
     proto <- setupBin
-    (i, _) <- uuid
-    $(logDebugS) i $ "Get transactions for xpub: " <> xPubExport net x
     db <- askDB
     bs <- liftIO . runResourceT . withLayeredDB db $ xpubBals x
     stream $ \io flush' -> do
         runResourceT . withLayeredDB db . runConduit $ f proto l s bs io
         flush'
-    $(logDebugS) i "Streamed balances"
   where
     f proto l s bs io
         | full =
@@ -494,26 +429,20 @@
     x <- parseXpub net
     proto <- setupBin
     (l, s) <- parseLimits
-    (i, _) <- uuid
-    $(logDebugS) i $ "Get UTXO for xpub: " <> xPubExport net x
     db <- askDB
     stream $ \io flush' -> do
         runResourceT . withLayeredDB db . runConduit $
             xpubUnspentLimit net l s x .| streamAny net proto io
         flush'
-    $(logDebugS) i "Streamed UTXO"
 
 scottyXpubSummary :: (MonadLoggerIO m, MonadUnliftIO m) => Network -> WebT m ()
 scottyXpubSummary net = do
     cors
     x <- parseXpub net
     (l, s) <- parseLimits
-    (i, _) <- uuid
-    $(logDebugS) i $ "Get summary for xpub: " <> xPubExport net x
     proto <- setupBin
     db <- askDB
     res <- liftIO . runResourceT . withLayeredDB db $ xpubSummary l s x
-    $(logDebugS) i "Returning summary"
     protoSerial net proto res
 
 scottyPostTx ::
@@ -526,22 +455,15 @@
     cors
     proto <- setupBin
     b <- body
-    (i, u) <- uuid
-    $(logDebugS) i "Received transaction to publish"
     let bin = eitherToMaybe . Serialize.decode
         hex = bin <=< decodeHex . cs . C.filter (not . isSpace)
     tx <-
         case hex b <|> bin (L.toStrict b) of
-            Nothing -> do
-                $(logDebugS) i "Decode tx fail"
-                raise $ UserError u "decode tx fail"
-            Just x -> do
-                $(logDebugS) i $ "Transaction id: " <> txHashToHex (txHash x)
-                return x
+            Nothing -> raise $ UserError "decode tx fail"
+            Just x  -> return x
     lift (publishTx net pub st tx) >>= \case
         Right () -> do
             protoSerial net proto (TxId (txHash tx))
-            $(logDebugS) i "Success publishing transaction"
         Left e -> do
             case e of
                 PubNoPeers          -> status status500
@@ -549,25 +471,18 @@
                 PubPeerDisconnected -> status status500
                 PubNotFound         -> status status500
                 PubReject _         -> status status400
-            protoSerial net proto (UserError u (show e))
-            $(logWarnS) i $
-                "Could not publish: " <> txHashToHex (txHash tx) <> ": " <>
-                cs (show e)
+            protoSerial net proto (UserError (show e))
             finish
 
 scottyDbStats :: MonadLoggerIO m => WebT m ()
 scottyDbStats = do
     cors
-    (i, _) <- uuid
-    $(logDebugS) i "Get DB stats"
     LayeredDB {layeredDB = BlockDB {blockDB = db}} <- askDB
     stats <- lift (getProperty db Stats)
     case stats of
       Nothing -> do
-          $(logWarnS) i "Could not get stats"
           text "Could not get stats"
       Just txt -> do
-          $(logDebugS) i "Returning stats"
           text $ cs txt
 
 scottyEvents ::
@@ -577,9 +492,7 @@
     -> WebT m ()
 scottyEvents net pub = do
     cors
-    (i, _) <- uuid
     proto <- setupBin
-    $(logDebugS) i "Streaming events..."
     stream $ \io flush' ->
         withSubscription pub $ \sub ->
             forever $
@@ -599,16 +512,12 @@
                                     then mempty
                                     else "\n"
                          in io (lazyByteString bs)
-    $(logDebugS) i "Finished streaming events"
 
 scottyPeers :: MonadLoggerIO m => Network -> Store -> WebT m ()
 scottyPeers net st = do
     cors
     proto <- setupBin
-    (i, _) <- uuid
-    $(logDebugS) i "Get peer information"
     ps <- getPeersInformation (storeManager st)
-    $(logDebugS) i $ "Returned peers: " <> cs (show (length ps))
     protoSerial net proto ps
 
 scottyHealth ::
@@ -616,14 +525,8 @@
 scottyHealth net st = do
     cors
     proto <- setupBin
-    (i, _) <- uuid
-    $(logDebugS) i "Get health information"
     h <- lift $ healthCheck net (storeManager st) (storeChain st)
-    if not (healthOK h) || not (healthSynced h)
-        then do
-            $(logDebugS) i "Not healthy"
-            status status503
-        else $(logDebugS) i "Healthy"
+    when (not (healthOK h) || not (healthSynced h)) $ status status503
     protoSerial net proto h
 
 runWeb :: (MonadLoggerIO m, MonadUnliftIO m) => WebConfig -> m ()
@@ -634,7 +537,9 @@
                  , webPublisher = pub
                  } = do
     runner <- askRunInIO
+    l <- logIt
     scottyT port (runner . withLayeredDB db) $ do
+        middleware l
         defaultHandler (defHandler net)
         S.get "/block/best" $ scottyBestBlock net
         S.get "/block/:block" $ scottyBlock net
@@ -667,10 +572,7 @@
         S.get "/events" $ scottyEvents net pub
         S.get "/peers" $ scottyPeers net st
         S.get "/health" $ scottyHealth net st
-        notFound $ do
-            (i, u) <- uuid
-            $(logDebugS) i "Requested resource not found"
-            raise (ThingNotFound u)
+        notFound $ raise ThingNotFound
 
 parseLimits :: (ScottyError e, Monad m) => ActionT e m (Maybe Word32, StartFrom)
 parseLimits = do
@@ -780,7 +682,7 @@
             h <- MaybeT getBestBlock
             MaybeT $ getBlock h
     p <- timeout (5 * 1000 * 1000) $ managerGetPeers mgr
-    let k = isNothing n || isNothing b || maybe False (not . null) p
+    let k = isNothing n || isNothing b || maybe False (not . Data.List.null) p
         s =
             isJust $ do
                 x <- n
@@ -1141,10 +1043,31 @@
                 | h' == txHash tx -> return $ Right ()
             _ -> g p s
 
-uuid :: MonadLoggerIO m => WebT m (Text, UUID)
-uuid = do
-    u <- liftIO nextRandom
-    r <- request
-    let t = "Web<" <> cs (show u) <> ">"
-    $(logDebugS) t $ cs (show r)
-    return (t, u)
+logIt :: (MonadLoggerIO m, MonadUnliftIO m) => m Middleware
+logIt = do
+    runner <- askRunInIO
+    return $ \app req respond -> do
+        t1 <- getCurrentTime
+        app req $ \res -> do
+            t2 <- getCurrentTime
+            let d = diffUTCTime t2 t1
+                s = responseStatus res
+            runner $
+                $(logInfoS) "Web" $
+                fmtReq req <> " [" <> fmtStatus s <> " / " <> fmtDiff d <> "]"
+            respond res
+
+fmtReq :: Request -> Text
+fmtReq req =
+    let method = requestMethod req
+        version = httpVersion req
+        path = rawPathInfo req
+        query = rawQueryString req
+     in T.decodeUtf8 $
+        method <> " " <> path <> query <> " " <> cs (show version)
+
+fmtDiff :: NominalDiffTime -> Text
+fmtDiff d = cs (show (round (d * 1000) :: Integer)) <> " ms"
+
+fmtStatus :: Status -> Text
+fmtStatus s = cs (show (statusCode s)) <> " " <> cs (statusMessage s)
diff --git a/test/Network/Haskoin/Store/DataSpec.hs b/test/Network/Haskoin/Store/DataSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Network/Haskoin/Store/DataSpec.hs
@@ -0,0 +1,48 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Network.Haskoin.Store.DataSpec
+    ( spec
+    ) where
+
+import           Control.Monad
+import           Control.Monad.Logger
+import           Control.Monad.Trans
+import           Data.Either
+import           Data.Maybe
+import           Data.Serialize
+import qualified Data.Text            as T
+import           Haskoin
+import           Haskoin.Node
+import           Haskoin.Store
+import           NQE
+import           Test.Hspec
+
+spec :: Spec
+spec = do
+  let net = btc
+
+  describe "Test TxHash serialization" $
+    it "serializes tx" $ do
+      let tx = TxHash "0666939fb16533c8e5ebaf6052bb8c90d27ee53fe6035bb763de5253e0b1cd44"
+      testSerial net tx
+
+  describe "Test Address serialization" $
+    it "serialize address" $ do
+      let addr = fromJust $ stringToAddr net "1DtDAYYTWRoiXvHRjARwVhjCUnNTk1XfXw"
+      testSerial net addr
+
+  describe "Test serialization of a list of addresses" $
+    it "serialize addresses" $ do
+      let addr1 = fromJust $ stringToAddr net "1DtDAYYTWRoiXvHRjARwVhjCUnNTk1XfXw"
+          addr2 = fromJust $ stringToAddr net "1GhnssnwRZwWKbHQXJRFpQfkfvE6hDG2KF"
+          expected = toAddrList net ["1DtDAYYTWRoiXvHRjARwVhjCUnNTk1XfXw", "1GhnssnwRZwWKbHQXJRFpQfkfvE6hDG2KF"]
+      testSerial net expected
+
+toAddrList :: Network -> [T.Text] -> [Address]
+toAddrList net = map (fromJust . stringToAddr net)
+
+testSerial :: (Eq a, Show a, BinSerial a) => Network -> a -> Expectation
+testSerial net input =
+  let raw = runPut $ binSerial net input
+      deser = runGet (binDeserial net) raw
+  in deser `shouldBe` Right input
