module Tendermint.SDK.BaseApp.Transaction.Effect ( TxEffs , TransactionContext(..) , newTransactionContext , eval ) where import Control.Lens ((&), (.~)) import Control.Monad.IO.Class (liftIO) import qualified Data.ByteArray.Base64String as Base64 import Data.Default.Class (def) import Data.IORef (IORef, newIORef, readIORef) import Polysemy (Embed, Member, Sem, raiseUnder) import Polysemy.Error (Error, runError) import Polysemy.Output (Output, runOutputMonoidAssocR) import Polysemy.State (State, runStateIORef) import Tendermint.SDK.BaseApp.Errors (AppError, txResultAppError) import qualified Tendermint.SDK.BaseApp.Events as E import qualified Tendermint.SDK.BaseApp.Gas as G import Tendermint.SDK.BaseApp.Transaction.Types (RoutingTx (..), Tx (..)) import Tendermint.SDK.Codec (HasCodec (encode)) import Tendermint.SDK.Types.Effects ((:&)) import Tendermint.SDK.Types.TxResult (TxResult, txResultData, txResultEvents, txResultGasUsed, txResultGasWanted) type TxEffs = [ Output E.Event , G.GasMeter , Error AppError ] data TransactionContext = TransactionContext { gas :: IORef G.GasAmount } newTransactionContext :: RoutingTx msg -> IO TransactionContext newTransactionContext (RoutingTx Tx{txGas}) = do initialGas <- newIORef $ G.GasAmount txGas pure TransactionContext { gas = initialGas } eval :: forall r a. HasCodec a => Member (Embed IO) r => TransactionContext -> Sem (TxEffs :& r) a -> Sem r TxResult eval TransactionContext{..} action = do initialGas <- liftIO $ readIORef gas eRes <- runError . runStateIORef gas . G.eval . raiseUnder @(State G.GasAmount) $ runOutputMonoidAssocR (pure @[]) action gasRemaining <- liftIO $ readIORef gas let gasUsed = initialGas - gasRemaining baseResponse = def & txResultGasWanted .~ G.unGasAmount initialGas & txResultGasUsed .~ G.unGasAmount gasUsed return $ case eRes of Left e -> baseResponse & txResultAppError .~ e Right (events, a) -> baseResponse & txResultEvents .~ events & txResultData .~ Base64.fromBytes (encode a)