packages feed

web3-polkadot-1.0.1.0: src/Network/Polkadot/Storage.hs

{-# LANGUAGE RecordWildCards #-}

-- |
-- Module      :  Network.Polkadot.Storage
-- Copyright   :  Aleksandr Krupenkin 2016-2024
-- License     :  Apache-2.0
--
-- Maintainer  :  mail@akru.me
-- Stability   :  experimental
-- Portability :  portable
--
-- Substrate uses a simple key-value data store implemented as a database-backed,
-- modified Merkle tree.
--
-- Blockchains that are built with Substrate expose a remote procedure call (RPC)
-- server that can be used to query runtime storage.
--

module Network.Polkadot.Storage where

import           Cases                         (camelize)
import           Control.Arrow                 ((&&&))
import           Data.ByteArray                (convert)
import           Data.ByteArray.HexString      (HexString)
import           Data.Char                     (toLower)
import           Data.Map.Strict               (Map)
import qualified Data.Map.Strict               as Map (fromList, lookup)
import           Data.Maybe                    (mapMaybe)
import           Data.Text                     (Text)
import qualified Data.Text                     as T (cons, head, tail)

import           Network.Polkadot.Metadata.V13 (Metadata (modules),
                                                ModuleMetadata (..),
                                                StorageEntryMetadata (..),
                                                StorageMetadata (..))
import           Network.Polkadot.Storage.Key  (Argument, StorageEntry (..),
                                                newEntry)

-- | Runtime storage is a set of named modules.
type Storage = Map Text ModuleStorage

-- | Each module store data in a set of named entries.
type ModuleStorage = Map Text StorageEntry

-- | Create 'Storage' abstraction from runtime metadata.
fromMetadata :: Metadata
             -- ^ Runtime metadata (latest version).
             -> Storage
             -- ^ Storage entities.
fromMetadata = Map.fromList . mapMaybe go . modules
  where
    toLowerFirst = uncurry T.cons . (toLower . T.head &&& T.tail)
    go ModuleMetadata{..} = do
        StorageMetadata prefix items <- moduleStorage
        let section = camelize moduleName
            toEntry meta@StorageEntryMetadata{..} =
                (toLowerFirst entryName, newEntry prefix meta)
        return (toLowerFirst section, Map.fromList $ fmap toEntry items)

-- | Create storage key for given parameters.
storageKey :: Storage
           -- ^ Storage entities.
           -> Text
           -- ^ Module name.
           -> Text
           -- ^ Storage method name.
           -> [Argument]
           -- ^ Arguments (for mappings).
           -> Maybe HexString
           -- ^ Raw storage key. If module or method was not found
           -- or wrong number of arguments - returns 'Nothing'.
storageKey store section method args = convert <$> do
    entry <- Map.lookup method =<< Map.lookup section store
    case entry of
      PlainEntry x     -> Just x
      MapEntry f       -> case args of
                            [a] -> Just (f a)
                            _   -> Nothing
      DoubleMapEntry f -> case args of
                            [a, b] -> Just (f a b)
                            _      -> Nothing