{-# LANGUAGE RecordWildCards     #-}
{-# LANGUAGE ScopedTypeVariables #-}

-- |
-- Module      :  Network.Ethereum.Transaction
-- Copyright   :  Alexander Krupenkin 2018
--                Roy Blankman 2018
-- License     :  BSD3
--
-- Maintainer  :  mail@akru.me
-- Stability   :  experimental
-- Portability :  unportable
--
-- Transaction managing utils.
--

module Network.Ethereum.Transaction where

import           Data.ByteArray             (ByteArray, convert)
import           Data.ByteString            (ByteString, empty)
import           Data.Maybe                 (fromJust, fromMaybe)
import           Data.RLP                   (packRLP, rlpEncode)
import           Data.Word                  (Word8)

import           Data.ByteArray.HexString   (toBytes)
import           Data.Solidity.Prim.Address (toHexString)
import           Network.Ethereum.Api.Types (Call (..), Quantity (unQuantity))
import           Network.Ethereum.Unit      (Shannon, toWei)

-- | Ethereum transaction packer.
--
-- Two way RLP encoding of Ethereum transaction: for unsigned and signed.
-- Packing scheme described in https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md
encodeTransaction :: ByteArray ba
                  => Call
                  -- ^ Transaction call
                  -> Integer
                  -- ^ Chain ID
                  -> Maybe (Integer, Integer, Word8)
                  -- ^ Should contain signature when transaction signed
                  -> ba
                  -- ^ RLP encoded transaction
encodeTransaction Call{..} chain_id rsv =
    let (to       :: ByteString) = maybe mempty (toBytes . toHexString) callTo
        (value    :: Integer)    = unQuantity $ fromJust callValue
        (nonce    :: Integer)    = unQuantity $ fromJust callNonce
        (gasPrice :: Integer)    = maybe defaultGasPrice unQuantity callGasPrice
        (gasLimit :: Integer)    = unQuantity $ fromJust callGas
        (input    :: ByteString) = convert $ fromMaybe mempty callData

    in convert . packRLP $ case rsv of
        -- Unsigned transaction by EIP155
        Nothing        -> rlpEncode (nonce, gasPrice, gasLimit, to, value, input, chain_id, empty, empty)
        -- Signed transaction
        Just (r, s, v) ->
            let v' = fromIntegral v + 8 + 2 * chain_id  -- Improved 'v' according to EIP155
             in rlpEncode (nonce, gasPrice, gasLimit, to, value, input, v', r, s)
  where
    defaultGasPrice = toWei (10 :: Shannon)