module Network.Ethereum.Web3.JsonRpc (
remote
, MethodName
, ServerUri
) where
import Network.Ethereum.Web3.Provider
import Network.Ethereum.Web3.Types
import Network.HTTP.Client (httpLbs, newManager, requestBody,
responseBody, method, requestHeaders,
parseRequest, RequestBody(RequestBodyLBS))
import Network.HTTP.Client.TLS (tlsManagerSettings)
import Data.ByteString.Lazy (ByteString)
import Control.Applicative ((<|>))
import Control.Exception (throwIO)
import Data.Vector (fromList)
import Control.Monad ((>=>))
import Data.Text (Text)
import Data.Aeson
type MethodName = Text
type ServerUri = String
remote :: Remote a => MethodName -> a
remote n = remote_ (\uri -> call uri . Array . fromList)
where
call uri = connection uri . encode . Request n 1
connection uri body = do
manager <- newManager tlsManagerSettings
request <- parseRequest uri
let request' = request
{ requestBody = RequestBodyLBS body
, requestHeaders = [("Content-Type", "application/json")]
, method = "POST" }
responseBody <$> httpLbs request' manager
decodeResponse :: FromJSON a => ByteString -> IO a
decodeResponse = tryParse . eitherDecode
>=> tryJsonRpc . rsResult
>=> tryParse . eitherDecode . encode
where tryJsonRpc :: Either RpcError a -> IO a
tryJsonRpc = either (throwIO . JsonRpcFail) return
tryParse :: Either String a -> IO a
tryParse = either (throwIO . ParserFail) return
class Remote a where
remote_ :: (ServerUri -> [Value] -> IO ByteString) -> a
instance (ToJSON a, Remote b) => Remote (a -> b) where
remote_ f x = remote_ (\u xs -> f u (toJSON x : xs))
instance (Provider p, FromJSON a) => Remote (Web3 p a) where
remote_ f = (\u -> Web3 (decodeResponse =<< f u [])) =<< rpcUri
data Request = Request { rqMethod :: Text
, rqId :: Int
, rqParams :: Value }
instance ToJSON Request where
toJSON rq = object $ [ "jsonrpc" .= String "2.0"
, "method" .= rqMethod rq
, "params" .= rqParams rq
, "id" .= rqId rq ]
data Response = Response
{ rsResult :: Either RpcError Value
} deriving (Show)
instance FromJSON Response where
parseJSON = withObject "JSON-RPC response object" $
\v -> Response <$>
(Right <$> v .: "result" <|> Left <$> v .: "error")