{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} -- | -- Module : Network.Ethereum.Account.LocalKey -- Copyright : Alexander Krupenkin 2018 -- Roy Blankman 2018 -- License : BSD3 -- -- Maintainer : mail@akru.me -- Stability : experimental -- Portability : unportable -- -- Using ECC for singing transactions locally, e.g. out of Ethereum node. -- Transaction will send using 'eth_sendRawTransacion' JSON-RPC method. -- module Network.Ethereum.Account.LocalKey where import Control.Exception (TypeError (..)) import Control.Monad.Catch (throwM) import Control.Monad.State.Strict (get, runStateT) import Control.Monad.Trans (lift) import Crypto.PubKey.ECC.ECDSA (PrivateKey) import Data.ByteArray (convert) import Data.ByteString (empty) import Data.Default (Default (..)) import Data.Proxy (Proxy (..)) import Crypto.Ethereum (derivePubKey, importKey) import Crypto.Ethereum.Signature (signTransaction) import Data.Solidity.Abi.Codec (decode, encode) import Data.Solidity.Prim.Address (fromPubKey) import Network.Ethereum.Account.Class (Account (..)) import Network.Ethereum.Account.Internal (AccountT (..), CallParam (..), defaultCallParam, getCall, getReceipt) import qualified Network.Ethereum.Api.Eth as Eth (call, estimateGas, getTransactionCount, sendRawTransaction) import Network.Ethereum.Api.Types (Call (..)) import Network.Ethereum.Chain (foundation) import Network.Ethereum.Contract.Method (selector) import Network.Ethereum.Transaction (encodeTransaction) -- | Local EOA params data LocalKey = LocalKey { localKeyPrivate :: !PrivateKey , localKeyChainId :: !Integer } deriving (Eq, Show) instance Default LocalKey where def = LocalKey (importKey empty) foundation type LocalKeyAccount = AccountT LocalKey instance Account LocalKey LocalKeyAccount where withAccount a = fmap fst . flip runStateT (defaultCallParam a) . runAccountT send (args :: a) = do CallParam{..} <- get c <- getCall let dat = selector (Proxy :: Proxy a) <> encode args address = fromPubKey (derivePubKey $ localKeyPrivate _account) nonce <- lift $ Eth.getTransactionCount address _block let params = c { callFrom = Just address , callNonce = Just nonce , callData = Just $ convert dat } params' <- case callGas params of Just _ -> return params Nothing -> do gasLimit <- lift $ Eth.estimateGas params return $ params { callGas = Just gasLimit } let packer = encodeTransaction params' (localKeyChainId _account) signed = signTransaction packer (localKeyPrivate _account) lift $ getReceipt =<< Eth.sendRawTransaction signed call (args :: a) = do CallParam{..} <- get c <- getCall let dat = selector (Proxy :: Proxy a) <> encode args address = fromPubKey (derivePubKey $ localKeyPrivate _account) params = c { callFrom = Just address, callData = Just $ convert dat } res <- lift $ Eth.call params _block case decode res of Right r -> return r Left e -> lift (throwM $ TypeError e)