web3 0.4.1.0 → 0.5.0.0
raw patch · 9 files changed
+236/−182 lines, 9 filesdep +http-client-tlsdep −data-default-classdep −mtldep ~aesondep ~attoparsecdep ~basePVP ok
version bump matches the API change (PVP)
Dependencies added: http-client-tls
Dependencies removed: data-default-class, mtl
Dependency ranges changed: aeson, attoparsec, base, base16-bytestring, bytestring, cryptonite, http-client, memory, template-haskell, text, transformers, vector, web3
API changes (from Hackage documentation)
- Network.Ethereum.Web3: Config :: String -> Config
- Network.Ethereum.Web3: [rpcUri] :: Config -> String
- Network.Ethereum.Web3: data Config
- Network.Ethereum.Web3: data Error
- Network.Ethereum.Web3: type Web3 = ReaderT Config (ExceptT Error IO)
- Network.Ethereum.Web3.Address: instance Data.Aeson.Types.FromJSON.FromJSON Network.Ethereum.Web3.Address.Address
- Network.Ethereum.Web3.Address: instance Data.Aeson.Types.ToJSON.ToJSON Network.Ethereum.Web3.Address.Address
- Network.Ethereum.Web3.JsonAbi: instance Data.Aeson.Types.FromJSON.FromJSON Network.Ethereum.Web3.JsonAbi.ContractABI
- Network.Ethereum.Web3.JsonAbi: instance Data.Aeson.Types.FromJSON.FromJSON Network.Ethereum.Web3.JsonAbi.Declaration
- Network.Ethereum.Web3.JsonAbi: instance Data.Aeson.Types.FromJSON.FromJSON Network.Ethereum.Web3.JsonAbi.EventArg
- Network.Ethereum.Web3.JsonAbi: instance Data.Aeson.Types.FromJSON.FromJSON Network.Ethereum.Web3.JsonAbi.FunctionArg
- Network.Ethereum.Web3.JsonAbi: instance Data.Aeson.Types.ToJSON.ToJSON Network.Ethereum.Web3.JsonAbi.ContractABI
- Network.Ethereum.Web3.JsonAbi: instance Data.Aeson.Types.ToJSON.ToJSON Network.Ethereum.Web3.JsonAbi.Declaration
- Network.Ethereum.Web3.JsonAbi: instance Data.Aeson.Types.ToJSON.ToJSON Network.Ethereum.Web3.JsonAbi.EventArg
- Network.Ethereum.Web3.JsonAbi: instance Data.Aeson.Types.ToJSON.ToJSON Network.Ethereum.Web3.JsonAbi.FunctionArg
- Network.Ethereum.Web3.Types: Config :: String -> Config
- Network.Ethereum.Web3.Types: [rpcUri] :: Config -> String
- Network.Ethereum.Web3.Types: data Config
- Network.Ethereum.Web3.Types: data Error
- Network.Ethereum.Web3.Types: instance Data.Aeson.Types.FromJSON.FromJSON Network.Ethereum.Web3.Types.Block
- Network.Ethereum.Web3.Types: instance Data.Aeson.Types.FromJSON.FromJSON Network.Ethereum.Web3.Types.Call
- Network.Ethereum.Web3.Types: instance Data.Aeson.Types.FromJSON.FromJSON Network.Ethereum.Web3.Types.Change
- Network.Ethereum.Web3.Types: instance Data.Aeson.Types.FromJSON.FromJSON Network.Ethereum.Web3.Types.Filter
- Network.Ethereum.Web3.Types: instance Data.Aeson.Types.FromJSON.FromJSON Network.Ethereum.Web3.Types.FilterId
- Network.Ethereum.Web3.Types: instance Data.Aeson.Types.FromJSON.FromJSON Network.Ethereum.Web3.Types.RpcError
- Network.Ethereum.Web3.Types: instance Data.Aeson.Types.FromJSON.FromJSON Network.Ethereum.Web3.Types.Transaction
- Network.Ethereum.Web3.Types: instance Data.Aeson.Types.ToJSON.ToJSON Network.Ethereum.Web3.Types.Block
- Network.Ethereum.Web3.Types: instance Data.Aeson.Types.ToJSON.ToJSON Network.Ethereum.Web3.Types.Call
- Network.Ethereum.Web3.Types: instance Data.Aeson.Types.ToJSON.ToJSON Network.Ethereum.Web3.Types.CallMode
- Network.Ethereum.Web3.Types: instance Data.Aeson.Types.ToJSON.ToJSON Network.Ethereum.Web3.Types.Change
- Network.Ethereum.Web3.Types: instance Data.Aeson.Types.ToJSON.ToJSON Network.Ethereum.Web3.Types.Filter
- Network.Ethereum.Web3.Types: instance Data.Aeson.Types.ToJSON.ToJSON Network.Ethereum.Web3.Types.FilterId
- Network.Ethereum.Web3.Types: instance Data.Aeson.Types.ToJSON.ToJSON Network.Ethereum.Web3.Types.RpcError
- Network.Ethereum.Web3.Types: instance Data.Aeson.Types.ToJSON.ToJSON Network.Ethereum.Web3.Types.Transaction
- Network.Ethereum.Web3.Types: instance Data.Default.Class.Default Network.Ethereum.Web3.Types.Config
- Network.Ethereum.Web3.Types: instance GHC.Classes.Eq Network.Ethereum.Web3.Types.Config
- Network.Ethereum.Web3.Types: instance GHC.Classes.Eq Network.Ethereum.Web3.Types.Error
- Network.Ethereum.Web3.Types: instance GHC.Show.Show Network.Ethereum.Web3.Types.Config
- Network.Ethereum.Web3.Types: instance GHC.Show.Show Network.Ethereum.Web3.Types.Error
- Network.Ethereum.Web3.Types: type Web3 = ReaderT Config (ExceptT Error IO)
+ Network.Ethereum.Web3: class Provider a where runWeb3' = liftIO . try . unWeb3 forkWeb3 = Web3 . forkIO . unWeb3
+ Network.Ethereum.Web3: data DefaultProvider
+ Network.Ethereum.Web3: data Web3 a b
+ Network.Ethereum.Web3: data Web3Error
+ Network.Ethereum.Web3: forkWeb3 :: Provider a => Web3 a () -> Web3 a ThreadId
+ Network.Ethereum.Web3: rpcUri :: Provider a => Web3 a String
+ Network.Ethereum.Web3.Address: instance Data.Aeson.Types.Class.FromJSON Network.Ethereum.Web3.Address.Address
+ Network.Ethereum.Web3.Address: instance Data.Aeson.Types.Class.ToJSON Network.Ethereum.Web3.Address.Address
+ Network.Ethereum.Web3.JsonAbi: instance Data.Aeson.Types.Class.FromJSON Network.Ethereum.Web3.JsonAbi.ContractABI
+ Network.Ethereum.Web3.JsonAbi: instance Data.Aeson.Types.Class.FromJSON Network.Ethereum.Web3.JsonAbi.Declaration
+ Network.Ethereum.Web3.JsonAbi: instance Data.Aeson.Types.Class.FromJSON Network.Ethereum.Web3.JsonAbi.EventArg
+ Network.Ethereum.Web3.JsonAbi: instance Data.Aeson.Types.Class.FromJSON Network.Ethereum.Web3.JsonAbi.FunctionArg
+ Network.Ethereum.Web3.JsonAbi: instance Data.Aeson.Types.Class.ToJSON Network.Ethereum.Web3.JsonAbi.ContractABI
+ Network.Ethereum.Web3.JsonAbi: instance Data.Aeson.Types.Class.ToJSON Network.Ethereum.Web3.JsonAbi.Declaration
+ Network.Ethereum.Web3.JsonAbi: instance Data.Aeson.Types.Class.ToJSON Network.Ethereum.Web3.JsonAbi.EventArg
+ Network.Ethereum.Web3.JsonAbi: instance Data.Aeson.Types.Class.ToJSON Network.Ethereum.Web3.JsonAbi.FunctionArg
+ Network.Ethereum.Web3.Types: Web3 :: IO b -> Web3 a b
+ Network.Ethereum.Web3.Types: [unWeb3] :: Web3 a b -> IO b
+ Network.Ethereum.Web3.Types: class Provider a where runWeb3' = liftIO . try . unWeb3 forkWeb3 = Web3 . forkIO . unWeb3
+ Network.Ethereum.Web3.Types: data DefaultProvider
+ Network.Ethereum.Web3.Types: data Web3Error
+ Network.Ethereum.Web3.Types: forkWeb3 :: Provider a => Web3 a () -> Web3 a ThreadId
+ Network.Ethereum.Web3.Types: instance Control.Monad.IO.Class.MonadIO (Network.Ethereum.Web3.Types.Web3 a)
+ Network.Ethereum.Web3.Types: instance Data.Aeson.Types.Class.FromJSON Network.Ethereum.Web3.Types.Block
+ Network.Ethereum.Web3.Types: instance Data.Aeson.Types.Class.FromJSON Network.Ethereum.Web3.Types.Call
+ Network.Ethereum.Web3.Types: instance Data.Aeson.Types.Class.FromJSON Network.Ethereum.Web3.Types.Change
+ Network.Ethereum.Web3.Types: instance Data.Aeson.Types.Class.FromJSON Network.Ethereum.Web3.Types.Filter
+ Network.Ethereum.Web3.Types: instance Data.Aeson.Types.Class.FromJSON Network.Ethereum.Web3.Types.FilterId
+ Network.Ethereum.Web3.Types: instance Data.Aeson.Types.Class.FromJSON Network.Ethereum.Web3.Types.RpcError
+ Network.Ethereum.Web3.Types: instance Data.Aeson.Types.Class.FromJSON Network.Ethereum.Web3.Types.Transaction
+ Network.Ethereum.Web3.Types: instance Data.Aeson.Types.Class.ToJSON Network.Ethereum.Web3.Types.Block
+ Network.Ethereum.Web3.Types: instance Data.Aeson.Types.Class.ToJSON Network.Ethereum.Web3.Types.Call
+ Network.Ethereum.Web3.Types: instance Data.Aeson.Types.Class.ToJSON Network.Ethereum.Web3.Types.CallMode
+ Network.Ethereum.Web3.Types: instance Data.Aeson.Types.Class.ToJSON Network.Ethereum.Web3.Types.Change
+ Network.Ethereum.Web3.Types: instance Data.Aeson.Types.Class.ToJSON Network.Ethereum.Web3.Types.Filter
+ Network.Ethereum.Web3.Types: instance Data.Aeson.Types.Class.ToJSON Network.Ethereum.Web3.Types.FilterId
+ Network.Ethereum.Web3.Types: instance Data.Aeson.Types.Class.ToJSON Network.Ethereum.Web3.Types.RpcError
+ Network.Ethereum.Web3.Types: instance Data.Aeson.Types.Class.ToJSON Network.Ethereum.Web3.Types.Transaction
+ Network.Ethereum.Web3.Types: instance GHC.Base.Applicative (Network.Ethereum.Web3.Types.Web3 a)
+ Network.Ethereum.Web3.Types: instance GHC.Base.Functor (Network.Ethereum.Web3.Types.Web3 a)
+ Network.Ethereum.Web3.Types: instance GHC.Base.Monad (Network.Ethereum.Web3.Types.Web3 a)
+ Network.Ethereum.Web3.Types: instance GHC.Classes.Eq Network.Ethereum.Web3.Types.Web3Error
+ Network.Ethereum.Web3.Types: instance GHC.Exception.Exception Network.Ethereum.Web3.Types.Web3Error
+ Network.Ethereum.Web3.Types: instance GHC.Show.Show Network.Ethereum.Web3.Types.Web3Error
+ Network.Ethereum.Web3.Types: instance Network.Ethereum.Web3.Types.Provider Network.Ethereum.Web3.Types.DefaultProvider
+ Network.Ethereum.Web3.Types: newtype Web3 a b
+ Network.Ethereum.Web3.Types: rpcUri :: Provider a => Web3 a String
- Network.Ethereum.Web3: JsonRpcFail :: RpcError -> Error
+ Network.Ethereum.Web3: JsonRpcFail :: RpcError -> Web3Error
- Network.Ethereum.Web3: ParserFail :: String -> Error
+ Network.Ethereum.Web3: ParserFail :: String -> Web3Error
- Network.Ethereum.Web3: UserFail :: String -> Error
+ Network.Ethereum.Web3: UserFail :: String -> Web3Error
- Network.Ethereum.Web3: call :: (Method a, ABIEncoding b) => Address -> CallMode -> a -> Web3 b
+ Network.Ethereum.Web3: call :: (Method a, Provider p, ABIEncoding b) => Address -> CallMode -> a -> Web3 p b
- Network.Ethereum.Web3: event :: Event a => Address -> (a -> IO EventAction) -> Web3 ThreadId
+ Network.Ethereum.Web3: event :: (Event a, Provider p) => Address -> (a -> IO EventAction) -> Web3 p ThreadId
- Network.Ethereum.Web3: runWeb3 :: MonadIO m => Web3 a -> m (Either Error a)
+ Network.Ethereum.Web3: runWeb3 :: MonadIO m => Web3 DefaultProvider b -> m (Either Web3Error b)
- Network.Ethereum.Web3: runWeb3' :: MonadIO m => Config -> Web3 a -> m (Either Error a)
+ Network.Ethereum.Web3: runWeb3' :: (Provider a, MonadIO m) => Web3 a b -> m (Either Web3Error b)
- Network.Ethereum.Web3: sendTx :: (Method a, Unit b) => Address -> b -> a -> Web3 TxHash
+ Network.Ethereum.Web3: sendTx :: (Method a, Provider p, Unit b) => Address -> b -> a -> Web3 p TxHash
- Network.Ethereum.Web3.Api: eth_accounts :: Web3 [Address]
+ Network.Ethereum.Web3.Api: eth_accounts :: Provider a => Web3 a [Address]
- Network.Ethereum.Web3.Api: eth_call :: Call -> CallMode -> Web3 Text
+ Network.Ethereum.Web3.Api: eth_call :: Provider a => Call -> CallMode -> Web3 a Text
- Network.Ethereum.Web3.Api: eth_getBalance :: Address -> CallMode -> Web3 Text
+ Network.Ethereum.Web3.Api: eth_getBalance :: Provider a => Address -> CallMode -> Web3 a Text
- Network.Ethereum.Web3.Api: eth_getBlockByHash :: Text -> Web3 Block
+ Network.Ethereum.Web3.Api: eth_getBlockByHash :: Provider a => Text -> Web3 a Block
- Network.Ethereum.Web3.Api: eth_getBlockFilterChanges :: Text -> Web3 [Text]
+ Network.Ethereum.Web3.Api: eth_getBlockFilterChanges :: Provider a => Text -> Web3 a [Text]
- Network.Ethereum.Web3.Api: eth_getFilterChanges :: FilterId -> Web3 [Change]
+ Network.Ethereum.Web3.Api: eth_getFilterChanges :: Provider a => FilterId -> Web3 a [Change]
- Network.Ethereum.Web3.Api: eth_newBlockFilter :: Web3 Text
+ Network.Ethereum.Web3.Api: eth_newBlockFilter :: Provider a => Web3 a Text
- Network.Ethereum.Web3.Api: eth_newFilter :: Filter -> Web3 FilterId
+ Network.Ethereum.Web3.Api: eth_newFilter :: Provider a => Filter -> Web3 a FilterId
- Network.Ethereum.Web3.Api: eth_sendTransaction :: Call -> Web3 Text
+ Network.Ethereum.Web3.Api: eth_sendTransaction :: Provider a => Call -> Web3 a Text
- Network.Ethereum.Web3.Api: eth_uninstallFilter :: FilterId -> Web3 Bool
+ Network.Ethereum.Web3.Api: eth_uninstallFilter :: Provider a => FilterId -> Web3 a Bool
- Network.Ethereum.Web3.Api: web3_clientVersion :: Web3 Text
+ Network.Ethereum.Web3.Api: web3_clientVersion :: Provider a => Web3 a Text
- Network.Ethereum.Web3.Api: web3_sha3 :: Text -> Web3 Text
+ Network.Ethereum.Web3.Api: web3_sha3 :: Provider a => Text -> Web3 a Text
- Network.Ethereum.Web3.Contract: call :: (Method a, ABIEncoding b) => Address -> CallMode -> a -> Web3 b
+ Network.Ethereum.Web3.Contract: call :: (Method a, Provider p, ABIEncoding b) => Address -> CallMode -> a -> Web3 p b
- Network.Ethereum.Web3.Contract: event :: Event a => Address -> (a -> IO EventAction) -> Web3 ThreadId
+ Network.Ethereum.Web3.Contract: event :: (Event a, Provider p) => Address -> (a -> IO EventAction) -> Web3 p ThreadId
- Network.Ethereum.Web3.Contract: sendTx :: (Method a, Unit b) => Address -> b -> a -> Web3 TxHash
+ Network.Ethereum.Web3.Contract: sendTx :: (Method a, Provider p, Unit b) => Address -> b -> a -> Web3 p TxHash
- Network.Ethereum.Web3.Types: JsonRpcFail :: RpcError -> Error
+ Network.Ethereum.Web3.Types: JsonRpcFail :: RpcError -> Web3Error
- Network.Ethereum.Web3.Types: ParserFail :: String -> Error
+ Network.Ethereum.Web3.Types: ParserFail :: String -> Web3Error
- Network.Ethereum.Web3.Types: UserFail :: String -> Error
+ Network.Ethereum.Web3.Types: UserFail :: String -> Web3Error
- Network.Ethereum.Web3.Types: runWeb3 :: MonadIO m => Web3 a -> m (Either Error a)
+ Network.Ethereum.Web3.Types: runWeb3 :: MonadIO m => Web3 DefaultProvider b -> m (Either Web3Error b)
- Network.Ethereum.Web3.Types: runWeb3' :: MonadIO m => Config -> Web3 a -> m (Either Error a)
+ Network.Ethereum.Web3.Types: runWeb3' :: (Provider a, MonadIO m) => Web3 a b -> m (Either Web3Error b)
Files
- README.md +19/−8
- src/Network/Ethereum/Web3.hs +4/−4
- src/Network/Ethereum/Web3/Address.hs +3/−1
- src/Network/Ethereum/Web3/Api.hs +12/−12
- src/Network/Ethereum/Web3/Contract.hs +44/−26
- src/Network/Ethereum/Web3/JsonRpc.hs +37/−36
- src/Network/Ethereum/Web3/TH.hs +10/−7
- src/Network/Ethereum/Web3/Types.hs +44/−30
- web3.cabal +63/−58
README.md view
@@ -3,7 +3,7 @@ This is the Ethereum compatible Haskell API which implements the [Generic JSON RPC](https://github.com/ethereum/wiki/wiki/JSON-RPC) spec. [](https://travis-ci.org/airalab/hs-web3)-[](https://ci.appveyor.com/project/akru/hs-web3)+[](https://ci.appveyor.com/project/akru/hs-web3) [](http://hackage.haskell.org/package/web3)  @@ -24,13 +24,19 @@ Any Ethereum node communication wrapped with `Web3` monadic type. > :t web3_clientVersion- web3_clientVersion :: Web3 Text+ web3_clientVersion :: Provider a => Web3 a Text To run this computation used `runWeb3'` or `runWeb3` functions. > runWeb3 web3_clientVersion Right "Parity//v1.4.5-beta-a028d04-20161126/x86_64-linux-gnu/rustc1.13.0" +Function `runWeb3` use default `Web3` provider at `localhost:8545`.++ > :t runWeb3+ runWeb3+ :: MonadIO m => Web3 DefaultProvider b -> m (Either Web3Error b)+ ### TemplateHaskell generator [Quasiquotation](https://wiki.haskell.org/Quasiquotation) is used to parse@@ -50,14 +56,19 @@ See example of usage. ```haskell-import Data.ByteArray (Bytes)-import Data.Text (Text)+import Data.Text (unpack)+import Text.Printf -[abiFrom|data/sample.json|]+[abiFrom|data/ERC20.json|] main :: IO () main = do- tx <- runWeb3 (runA2 addr nopay "Hello!" 42)- print tx- where addr = "0x19EE7966474b31225F71Ef8e36A71378a58a20E1"+ Right s <- runWeb3 $ do+ n <- name token+ s <- symbol token+ d <- decimals token+ return $ printf "Token %s with symbol %s and decimals %d"+ (unpack n) (unpack s) d+ putStrLn s+ where token = "0x237D60A8b41aFD2a335305ed458B609D7667D789" ```
src/Network/Ethereum/Web3.hs view
@@ -17,11 +17,11 @@ -- Web3 Haskell library currently use JSON-RPC over HTTP to access node functionality. -- module Network.Ethereum.Web3 (- -- ** Web3 monad & runners+ -- ** Web3 monad and service provider Web3- , Config(..)- , Error(..)- , runWeb3'+ , Provider(..)+ , DefaultProvider+ , Web3Error(..) , runWeb3 -- ** Contract actions , EventAction(..)
src/Network/Ethereum/Web3/Address.hs view
@@ -57,7 +57,9 @@ -- | Render 'Address' to text string toText :: Address -> Text-toText = toStrict . toLazyText . B.hexadecimal . unAddress+toText = wFix . toStrict . toLazyText . B.hexadecimal . unAddress+ where wFix x | T.length x < 40 = T.replicate (40 - T.length x) "0" <> x+ | otherwise = x -- | Null address zero :: Address
src/Network/Ethereum/Web3/Api.hs view
@@ -17,55 +17,55 @@ import Data.Text (Text) -- | Returns current node version string.-web3_clientVersion :: Web3 Text+web3_clientVersion :: Provider a => Web3 a Text web3_clientVersion = remote "web3_clientVersion" -- | Returns Keccak-256 (not the standardized SHA3-256) of the given data.-web3_sha3 :: Text -> Web3 Text+web3_sha3 :: Provider a => Text -> Web3 a Text web3_sha3 = remote "web3_sha3" -- | Returns the balance of the account of given address.-eth_getBalance :: Address -> CallMode -> Web3 Text+eth_getBalance :: Provider a => Address -> CallMode -> Web3 a Text eth_getBalance = remote "eth_getBalance" -- | Creates a filter object, based on filter options, to notify when the -- state changes (logs). To check if the state has changed, call -- 'getFilterChanges'.-eth_newFilter :: Filter -> Web3 FilterId+eth_newFilter :: Provider a => Filter -> Web3 a FilterId eth_newFilter = remote "eth_newFilter" -- | Polling method for a filter, which returns an array of logs which -- occurred since last poll.-eth_getFilterChanges :: FilterId -> Web3 [Change]+eth_getFilterChanges :: Provider a => FilterId -> Web3 a [Change] eth_getFilterChanges = remote "eth_getFilterChanges" -- | Uninstalls a filter with given id. -- Should always be called when watch is no longer needed.-eth_uninstallFilter :: FilterId -> Web3 Bool+eth_uninstallFilter :: Provider a => FilterId -> Web3 a Bool eth_uninstallFilter = remote "eth_uninstallFilter" -- | Executes a new message call immediately without creating a -- transaction on the block chain.-eth_call :: Call -> CallMode -> Web3 Text+eth_call :: Provider a => Call -> CallMode -> Web3 a Text eth_call = remote "eth_call" -- | Creates new message call transaction or a contract creation, -- if the data field contains code.-eth_sendTransaction :: Call -> Web3 Text+eth_sendTransaction :: Provider a => Call -> Web3 a Text eth_sendTransaction = remote "eth_sendTransaction" -- | Returns a list of addresses owned by client.-eth_accounts :: Web3 [Address]+eth_accounts :: Provider a => Web3 a [Address] eth_accounts = remote "eth_accounts" -eth_newBlockFilter :: Web3 Text+eth_newBlockFilter :: Provider a => Web3 a Text eth_newBlockFilter = remote "eth_newBlockFilter" -- | Polling method for a block filter, which returns an array of block hashes -- occurred since last poll.-eth_getBlockFilterChanges :: Text -> Web3 [Text]+eth_getBlockFilterChanges :: Provider a => Text -> Web3 a [Text] eth_getBlockFilterChanges = remote "eth_getFilterChanges" -- | Returns information about a block by hash.-eth_getBlockByHash :: Text -> Web3 Block+eth_getBlockByHash :: Provider a => Text -> Web3 a Block eth_getBlockByHash = flip (remote "eth_getBlockByHash") True
src/Network/Ethereum/Web3/Contract.hs view
@@ -34,8 +34,7 @@ import qualified Data.Text.Lazy.Builder.Int as B import qualified Data.Text.Lazy.Builder as B-import Control.Concurrent (ThreadId, threadDelay, forkIO)-import Control.Monad.Trans.Reader (ask)+import Control.Concurrent (ThreadId, threadDelay) import Control.Monad.IO.Class (liftIO) import Data.Text.Lazy (toStrict) import qualified Data.Text as T@@ -61,30 +60,33 @@ eventFilter :: a -> Address -> Filter -- | Start an event listener for given contract 'Address' and callback- event :: Address -> (a -> IO EventAction) -> Web3 ThreadId+ event :: Provider p+ => Address+ -- ^ Contract address+ -> (a -> IO EventAction)+ -- ^ 'Event' handler+ -> Web3 p ThreadId+ -- ^ 'Web3' wrapped event handler spawn ident event = _event -_event :: Event a => Address -> (a -> IO EventAction) -> Web3 ThreadId+_event :: (Provider p, Event a)+ => Address+ -> (a -> IO EventAction)+ -> Web3 p ThreadId _event a f = do fid <- let ftyp = snd $ let x = undefined :: Event a => a in (f x, x) in eth_newFilter (eventFilter ftyp a) - cfg <- ask- liftIO $ forkIO $- let loop = do threadDelay 1000000- res <- runWeb3' cfg (eth_getFilterChanges fid)- case res of- Left e -> print e- Right [] -> loop- Right changes -> do- acts <- mapM f $- catMaybes $ fmap parseChange changes- if any (== TerminateEvent) acts- then return ()- else loop+ forkWeb3 $+ let loop = do liftIO (threadDelay 1000000)+ changes <- fmap parseChange <$> eth_getFilterChanges fid+ acts <- mapM (liftIO . f) (catMaybes changes)+ if any (== TerminateEvent) acts+ then return ()+ else loop in do loop- runWeb3' cfg (eth_uninstallFilter fid)+ eth_uninstallFilter fid return () where prepareTopics = fmap (T.drop 2) . drop 1@@ -95,29 +97,45 @@ -- | Contract method caller class ABIEncoding a => Method a where -- | Send a transaction for given contract 'Address', value and input data- sendTx :: Unit b => Address -> b -> a -> Web3 TxHash+ sendTx :: (Provider p, Unit b)+ => Address+ -- ^ Contract address+ -> b+ -- ^ Payment value (set 'nopay' to empty value)+ -> a+ -- ^ Method data+ -> Web3 p TxHash+ -- ^ 'Web3' wrapped result sendTx = _sendTransaction -- | Constant call given contract 'Address' in mode and given input data- call :: ABIEncoding b => Address -> CallMode -> a -> Web3 b+ call :: (Provider p, ABIEncoding b)+ => Address+ -- ^ Contract address+ -> CallMode+ -- ^ State mode for constant call (latest or pending)+ -> a+ -- ^ Method data+ -> Web3 p b+ -- ^ 'Web3' wrapped result call = _call -_sendTransaction :: (Method a, Unit b)- => Address -> b -> a -> Web3 TxHash+_sendTransaction :: (Provider p, Method a, Unit b)+ => Address -> b -> a -> Web3 p TxHash _sendTransaction to value dat = do primeAddress <- head <$> eth_accounts eth_sendTransaction (txdata primeAddress $ Just $ toData dat) where txdata from = Call (Just from) to Nothing Nothing (Just $ toWeiText value) toWeiText = ("0x" <>) . toStrict . B.toLazyText . B.hexadecimal . toWei --- TODO: Correct dynamic type parsing-_call :: (Method a, ABIEncoding b)- => Address -> CallMode -> a -> Web3 b+_call :: (Provider p, Method a, ABIEncoding b)+ => Address -> CallMode -> a -> Web3 p b _call to mode dat = do res <- eth_call txdata mode case fromData (T.drop 2 res) of Nothing -> fail $- "Unable to parse result on `" ++ T.unpack res ++ "`"+ "Unable to parse result on `" ++ T.unpack res+ ++ "` from `" ++ show to ++ "`" Just x -> return x where txdata = Call Nothing to Nothing Nothing Nothing (Just (toData dat))
src/Network/Ethereum/Web3/JsonRpc.hs view
@@ -12,20 +12,21 @@ -- Functions for implementing the client side of JSON-RPC 2.0. -- See <http://www.jsonrpc.org/specification>. ---module Network.Ethereum.Web3.JsonRpc (remote, MethodName) where+module Network.Ethereum.Web3.JsonRpc (+ remote+ , MethodName+ , ServerUri+ ) where import Network.Ethereum.Web3.Types -import Network.HTTP.Client (httpLbs, newManager, defaultManagerSettings,- requestBody, responseBody, method,- requestHeaders, parseRequest,- RequestBody(RequestBodyLBS))-import Control.Monad.Error.Class (throwError)+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.Monad.Trans.Reader (ask)-import Control.Monad.IO.Class (liftIO) import Control.Applicative ((<|>))-import Control.Monad.Trans (lift)+import Control.Exception (throwIO) import Data.Vector (fromList) import Control.Monad ((>=>)) import Data.Text (Text)@@ -34,41 +35,41 @@ -- | Name of called method. type MethodName = Text +-- | JSON-RPC server URI+type ServerUri = String+ -- | Remote call of JSON-RPC method. -- Arguments of function are stored into @params@ request array. remote :: Remote a => MethodName -> a-remote n = remote_ (call . Array . fromList)- where connection body = do- conf <- ask- liftIO $ do- manager <- newManager defaultManagerSettings- request <- parseRequest (rpcUri conf)- let request' = request- { requestBody = RequestBodyLBS body- , requestHeaders = [("Content-Type", "application/json")]- , method = "POST" }- responseBody <$> httpLbs request' manager- call = connection . encode . Request n 1--class Remote a where- remote_ :: ([Value] -> Web3 ByteString) -> a--instance (ToJSON a, Remote b) => Remote (a -> b) where- remote_ f x = remote_ (\xs -> f (toJSON x : xs))+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 -> Web3 a+decodeResponse :: FromJSON a => ByteString -> IO a decodeResponse = tryParse . eitherDecode >=> tryJsonRpc . rsResult >=> tryParse . eitherDecode . encode- where tryJsonRpc :: Either RpcError a -> Web3 a- tryJsonRpc (Right a) = return a- tryJsonRpc (Left e) = lift $ throwError (JsonRpcFail e)- tryParse :: Either String a -> Web3 a- tryParse (Right a) = return a- tryParse (Left e) = lift $ throwError (ParserFail e)+ where tryJsonRpc :: Either RpcError a -> IO a+ tryJsonRpc = either (throwIO . JsonRpcFail) return+ tryParse :: Either String a -> IO a+ tryParse = either (throwIO . ParserFail) return -instance FromJSON a => Remote (Web3 a) where- remote_ f = decodeResponse =<< f []+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 -- | JSON-RPC request. data Request = Request { rqMethod :: Text
src/Network/Ethereum/Web3/TH.hs view
@@ -193,7 +193,9 @@ sequence $ case c of True ->- [ sigD name $ arrowing $ [t|Address|] : inputT ++ [outputT]+ [ sigD name $ [t|Provider $p =>+ $(arrowing $ [t|Address|] : inputT ++ [outputT])+ |] , funD' name (varP <$> a : vars) $ case result of Just [_] -> [|unSingleton <$> call $(varE a) Latest $(params)|]@@ -201,20 +203,21 @@ ] False ->- [ sigD name $ [t|Unit $(varT b) =>- $(arrowing $ [t|Address|] : varT b : inputT ++ [[t|Web3 TxHash|]])+ [ sigD name $ [t|(Provider $p, Unit $(varT b)) =>+ $(arrowing $ [t|Address|] : varT b : inputT ++ [[t|Web3 $p TxHash|]]) |] , funD' name (varP <$> a : b : vars) $ [|sendTx $(varE a) $(varE b) $(params)|] ] where- arrowing [x] = x+ p = varT (mkName "p")+ arrowing [x] = x arrowing (x : xs) = [t|$x -> $(arrowing xs)|] inputT = fmap (typeQ . funArgType) args outputT = case result of- Nothing -> [t|Web3 ()|]- Just [x] -> [t|Web3 $(typeQ $ funArgType x)|]+ Nothing -> [t|Web3 $p ()|]+ Just [x] -> [t|Web3 $p $(typeQ $ funArgType x)|] Just xs -> let outs = fmap (typeQ . funArgType) xs- in [t|Web3 $(foldl appT (tupleT (length xs)) outs)|]+ in [t|Web3 $p $(foldl appT (tupleT (length xs)) outs)|] -- | Event declarations maker mkEvent :: Declaration -> Q [Dec]
src/Network/Ethereum/Web3/Types.hs view
@@ -1,4 +1,7 @@-{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE TemplateHaskell #-} -- | -- Module : Network.Ethereum.Web3.Types -- Copyright : Alexander Krupenkin 2016@@ -13,48 +16,59 @@ module Network.Ethereum.Web3.Types where import Network.Ethereum.Web3.Internal (toLowerFirst)-import Control.Monad.Trans.Reader (ReaderT, runReaderT)-import Control.Monad.Trans.Except (ExceptT, runExceptT)-import Network.Ethereum.Web3.Address (Address)-import Control.Monad.IO.Class (MonadIO(..))-import Data.Default.Class (Default(..)) import qualified Data.Text.Lazy.Builder.Int as B import qualified Data.Text.Lazy.Builder as B-import qualified Data.Text.Read as R-import Data.Default.Class (def)+import qualified Data.Text.Read as R+import Network.Ethereum.Web3.Address (Address)+import Control.Monad.IO.Class (MonadIO(..))+import Control.Concurrent (forkIO, ThreadId)+import Control.Exception (Exception, try)+import Data.Typeable (Typeable) import Data.Monoid ((<>)) import Data.Text (Text) import Data.Aeson.TH import Data.Aeson -- | Any communication with Ethereum node wrapped with 'Web3' monad-type Web3 = ReaderT Config (ExceptT Error IO)+newtype Web3 a b = Web3 { unWeb3 :: IO b }+ deriving (Functor, Applicative, Monad, MonadIO) --- | Ethereum node params-data Config = Config- { rpcUri :: String- -- ^ JSON-RPC node URI- } deriving (Show, Eq)+-- | Ethereum node service provider+class Provider a where+ -- | JSON-RPC provider URI, default: localhost:8545+ rpcUri :: Web3 a String -instance Default Config where- def = Config "http://localhost:8545"+ -- | 'Web3' monad runner+ runWeb3' :: MonadIO m => Web3 a b -> m (Either Web3Error b)+ {-# INLINE runWeb3' #-}+ runWeb3' = liftIO . try . unWeb3 --- | Some peace of error response-data Error = JsonRpcFail RpcError- -- ^ JSON-RPC communication error- | ParserFail String- -- ^ Error in parser state- | UserFail String- -- ^ Common head for user errors- deriving (Show, Eq)+ -- | Fork 'Web3' with the same 'Provider'+ forkWeb3 :: Web3 a () -> Web3 a ThreadId+ {-# INLINE forkWeb3 #-}+ forkWeb3 = Web3 . forkIO . unWeb3 --- | Run 'Web3' monad with default config-runWeb3 :: MonadIO m => Web3 a -> m (Either Error a)-runWeb3 = runWeb3' def+-- | Default 'Web3' service provider+data DefaultProvider+instance Provider DefaultProvider where+ rpcUri = return "http://localhost:8545" --- | Run 'Web3' monad with given configuration-runWeb3' :: MonadIO m => Config -> Web3 a -> m (Either Error a)-runWeb3' c = liftIO . runExceptT . flip runReaderT c+-- | 'Web3' runner for default provider+runWeb3 :: MonadIO m => Web3 DefaultProvider b -> m (Either Web3Error b)+{-# INLINE runWeb3 #-}+runWeb3 = runWeb3'++-- | Some peace of error response+data Web3Error+ = JsonRpcFail RpcError+ -- ^ JSON-RPC communication error+ | ParserFail String+ -- ^ Error in parser state+ | UserFail String+ -- ^ Common head for user errors+ deriving (Typeable, Show, Eq)++instance Exception Web3Error -- | JSON-RPC error message data RpcError = RpcError
web3.cabal view
@@ -1,65 +1,70 @@-name: web3-version: 0.4.1.0-synopsis: Ethereum API for Haskell-description: Web3 is a Haskell client library for Ethereum-homepage: https://github.com/airalab/hs-web3#readme-license: BSD3-license-file: LICENSE-author: Alexander Krupenkin-maintainer: mail@akru.me-copyright: Alexander Krupenkin-category: Network-build-type: Simple-cabal-version: >=1.10-+name: web3+version: 0.5.0.0+cabal-version: >=1.10+build-type: Simple+license: BSD3+license-file: LICENSE+copyright: Alexander Krupenkin+maintainer: mail@akru.me+homepage: https://github.com/airalab/hs-web3#readme+synopsis: Ethereum API for Haskell+description:+ Web3 is a Haskell client library for Ethereum+category: Network+author: Alexander Krupenkin extra-source-files:- README.md- data/ERC20.json+ README.md+ data/ERC20.json source-repository head- type: git- location: https://github.com/airalab/hs-web3+ type: git+ location: https://github.com/airalab/hs-web3 library- hs-source-dirs: src- exposed-modules: Network.Ethereum.Web3- , Network.Ethereum.Unit- , Network.Ethereum.Web3.TH- , Network.Ethereum.Web3.Api- , Network.Ethereum.Web3.Types- , Network.Ethereum.Web3.Address- , Network.Ethereum.Web3.JsonAbi- , Network.Ethereum.Web3.Encoding- , Network.Ethereum.Web3.Contract- , Network.Ethereum.Web3.Encoding.Bytes- , Network.Ethereum.Web3.Encoding.Tuple- other-modules: Network.Ethereum.Web3.JsonRpc- , Network.Ethereum.Web3.Internal- , Network.Ethereum.Web3.Encoding.TupleTH- , Network.Ethereum.Web3.Encoding.Internal- build-depends: base >4.8 && <4.11- , data-default-class- , base16-bytestring- , template-haskell- , transformers- , http-client- , attoparsec- , bytestring- , cryptonite- , vector- , memory- , aeson- , text- , mtl- default-extensions: OverloadedStrings- default-language: Haskell2010+ exposed-modules:+ Network.Ethereum.Web3+ Network.Ethereum.Unit+ Network.Ethereum.Web3.TH+ Network.Ethereum.Web3.Api+ Network.Ethereum.Web3.Types+ Network.Ethereum.Web3.Address+ Network.Ethereum.Web3.JsonAbi+ Network.Ethereum.Web3.Encoding+ Network.Ethereum.Web3.Contract+ Network.Ethereum.Web3.Encoding.Bytes+ Network.Ethereum.Web3.Encoding.Tuple+ build-depends:+ base >4.8 && <4.11,+ base16-bytestring >=0.1.1.6 && <0.2,+ template-haskell >=2.11.0.0 && <2.12,+ http-client-tls >=0.2.4.1 && <0.3,+ transformers >=0.5.2.0 && <0.6,+ http-client >=0.4.31.2 && <0.5,+ attoparsec >=0.13.1.0 && <0.14,+ bytestring >=0.10.8.1 && <0.11,+ cryptonite ==0.19.*,+ vector >=0.11.0.0 && <0.12,+ memory ==0.13.*,+ aeson >=0.11.2.1 && <0.12,+ text >=1.2.2.1 && <1.3+ default-language: Haskell2010+ default-extensions: OverloadedStrings+ hs-source-dirs: src+ other-modules:+ Network.Ethereum.Web3.JsonRpc+ Network.Ethereum.Web3.Internal+ Network.Ethereum.Web3.Encoding.TupleTH+ Network.Ethereum.Web3.Encoding.Internal test-suite web3-test- type: exitcode-stdio-1.0- hs-source-dirs: test- main-is: Spec.hs- build-depends: base, memory, text, web3- ghc-options: -threaded -rtsopts -with-rtsopts=-N- --ghc-options: -ddump-splices -threaded -rtsopts -with-rtsopts=-N- default-extensions: OverloadedStrings- default-language: Haskell2010+ type: exitcode-stdio-1.0+ main-is: Spec.hs+ build-depends:+ base >=4.9.0.0 && <4.10,+ memory ==0.13.*,+ text >=1.2.2.1 && <1.3,+ web3 >=0.5.0.0 && <0.6+ default-language: Haskell2010+ default-extensions: OverloadedStrings+ hs-source-dirs: test+ ghc-options: -threaded -rtsopts -with-rtsopts=-N