-- SPDX-FileCopyrightText: 2021 Oxhead Alpha
-- SPDX-License-Identifier: LicenseRef-MIT-OA

-- | Primitives for running batched operations with a neat interface.
module Morley.Client.Action.Batched
  ( OperationsBatch (..)
  , originateContractM
  , runTransactionM
  , revealKeyM
  , runOperationsBatch
  ) where

import Control.Lens (Prism')
import Fmt (Buildable(..))

import Morley.Client.Action.Common
import Morley.Client.Action.Operation
import Morley.Client.Logging
import Morley.Client.RPC.Class
import Morley.Client.RPC.Types
import Morley.Client.TezosClient
import Morley.Client.Types
import Morley.Tezos.Address
import Morley.Tezos.Address.Alias
import Morley.Util.Batching

{- | Where the batched operations occur.

Example:

@
runOperationsBatch mySender $ do
  addr <- originateContractM ...
  runTransactionM ...
  return addr
@

Note that this is not a 'Monad', rather an 'Applicative' - use
@-XApplicativeDo@ extension for nicer experience.
-}
newtype OperationsBatch a = OperationsBatch
  { forall a.
OperationsBatch a
-> BatchingM
     (OperationInfo ClientInput)
     (OperationInfo Result)
     BatchedOperationError
     a
unOperationsBatch
      :: BatchingM
          (OperationInfo ClientInput)
          (OperationInfo Result)
          BatchedOperationError
          a
  } deriving newtype ((forall a b. (a -> b) -> OperationsBatch a -> OperationsBatch b)
-> (forall a b. a -> OperationsBatch b -> OperationsBatch a)
-> Functor OperationsBatch
forall a b. a -> OperationsBatch b -> OperationsBatch a
forall a b. (a -> b) -> OperationsBatch a -> OperationsBatch b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: forall a b. a -> OperationsBatch b -> OperationsBatch a
$c<$ :: forall a b. a -> OperationsBatch b -> OperationsBatch a
fmap :: forall a b. (a -> b) -> OperationsBatch a -> OperationsBatch b
$cfmap :: forall a b. (a -> b) -> OperationsBatch a -> OperationsBatch b
Functor, Functor OperationsBatch
Functor OperationsBatch
-> (forall a. a -> OperationsBatch a)
-> (forall a b.
    OperationsBatch (a -> b) -> OperationsBatch a -> OperationsBatch b)
-> (forall a b c.
    (a -> b -> c)
    -> OperationsBatch a -> OperationsBatch b -> OperationsBatch c)
-> (forall a b.
    OperationsBatch a -> OperationsBatch b -> OperationsBatch b)
-> (forall a b.
    OperationsBatch a -> OperationsBatch b -> OperationsBatch a)
-> Applicative OperationsBatch
forall a. a -> OperationsBatch a
forall a b.
OperationsBatch a -> OperationsBatch b -> OperationsBatch a
forall a b.
OperationsBatch a -> OperationsBatch b -> OperationsBatch b
forall a b.
OperationsBatch (a -> b) -> OperationsBatch a -> OperationsBatch b
forall a b c.
(a -> b -> c)
-> OperationsBatch a -> OperationsBatch b -> OperationsBatch c
forall (f :: * -> *).
Functor f
-> (forall a. a -> f a)
-> (forall a b. f (a -> b) -> f a -> f b)
-> (forall a b c. (a -> b -> c) -> f a -> f b -> f c)
-> (forall a b. f a -> f b -> f b)
-> (forall a b. f a -> f b -> f a)
-> Applicative f
<* :: forall a b.
OperationsBatch a -> OperationsBatch b -> OperationsBatch a
$c<* :: forall a b.
OperationsBatch a -> OperationsBatch b -> OperationsBatch a
*> :: forall a b.
OperationsBatch a -> OperationsBatch b -> OperationsBatch b
$c*> :: forall a b.
OperationsBatch a -> OperationsBatch b -> OperationsBatch b
liftA2 :: forall a b c.
(a -> b -> c)
-> OperationsBatch a -> OperationsBatch b -> OperationsBatch c
$cliftA2 :: forall a b c.
(a -> b -> c)
-> OperationsBatch a -> OperationsBatch b -> OperationsBatch c
<*> :: forall a b.
OperationsBatch (a -> b) -> OperationsBatch a -> OperationsBatch b
$c<*> :: forall a b.
OperationsBatch (a -> b) -> OperationsBatch a -> OperationsBatch b
pure :: forall a. a -> OperationsBatch a
$cpure :: forall a. a -> OperationsBatch a
Applicative)

data BatchedOperationError
  = UnexpectedOperationResult

instance Buildable BatchedOperationError where
  build :: BatchedOperationError -> Builder
build = \case
    BatchedOperationError
UnexpectedOperationResult ->
      Builder
"Got unexpected operation type within result of batched operation"

-- | A helper to run some operation.
--
-- It accepts
runOperationM
  :: (inp -> OperationInfo ClientInput)
  -> (Prism' (OperationInfo Result) out)
  -> inp
  -> OperationsBatch out
runOperationM :: forall inp out.
(inp -> OperationInfo ClientInput)
-> Prism' (OperationInfo Result) out -> inp -> OperationsBatch out
runOperationM inp -> OperationInfo ClientInput
wrapInp Prism' (OperationInfo Result) out
unwrapOut inp
inp = BatchingM
  (OperationInfo ClientInput)
  (OperationInfo Result)
  BatchedOperationError
  out
-> OperationsBatch out
forall a.
BatchingM
  (OperationInfo ClientInput)
  (OperationInfo Result)
  BatchedOperationError
  a
-> OperationsBatch a
OperationsBatch (BatchingM
   (OperationInfo ClientInput)
   (OperationInfo Result)
   BatchedOperationError
   out
 -> OperationsBatch out)
-> BatchingM
     (OperationInfo ClientInput)
     (OperationInfo Result)
     BatchedOperationError
     out
-> OperationsBatch out
forall a b. (a -> b) -> a -> b
$
  inp -> OperationInfo ClientInput
wrapInp inp
inp OperationInfo ClientInput
-> (OperationInfo Result -> Either BatchedOperationError out)
-> BatchingM
     (OperationInfo ClientInput)
     (OperationInfo Result)
     BatchedOperationError
     out
forall i o e a. i -> (o -> Either e a) -> BatchingM i o e a
`submitThenParse`
    BatchedOperationError
-> Maybe out -> Either BatchedOperationError out
forall l r. l -> Maybe r -> Either l r
maybeToRight BatchedOperationError
UnexpectedOperationResult (Maybe out -> Either BatchedOperationError out)
-> (OperationInfo Result -> Maybe out)
-> OperationInfo Result
-> Either BatchedOperationError out
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (OperationInfo Result
-> Getting (First out) (OperationInfo Result) out -> Maybe out
forall s a. s -> Getting (First a) s a -> Maybe a
^? Getting (First out) (OperationInfo Result) out
Prism' (OperationInfo Result) out
unwrapOut)

-- | Perform transaction within a batch.
runTransactionM :: TransactionData -> OperationsBatch [IntOpEvent]
runTransactionM :: TransactionData -> OperationsBatch [IntOpEvent]
runTransactionM = (TransactionData -> OperationInfo ClientInput)
-> Prism' (OperationInfo Result) [IntOpEvent]
-> TransactionData
-> OperationsBatch [IntOpEvent]
forall inp out.
(inp -> OperationInfo ClientInput)
-> Prism' (OperationInfo Result) out -> inp -> OperationsBatch out
runOperationM TransactionData -> OperationInfo ClientInput
forall i. TransferInfo i -> OperationInfo i
OpTransfer forall i. Prism' (OperationInfo i) (TransferInfo i)
Prism' (OperationInfo Result) [IntOpEvent]
_OpTransfer

-- | Perform origination within a batch.
originateContractM :: OriginationData -> OperationsBatch ContractAddress
originateContractM :: OriginationData -> OperationsBatch ContractAddress
originateContractM = (OriginationData -> OperationInfo ClientInput)
-> Prism' (OperationInfo Result) ContractAddress
-> OriginationData
-> OperationsBatch ContractAddress
forall inp out.
(inp -> OperationInfo ClientInput)
-> Prism' (OperationInfo Result) out -> inp -> OperationsBatch out
runOperationM OriginationData -> OperationInfo ClientInput
forall i. OriginationInfo i -> OperationInfo i
OpOriginate forall i. Prism' (OperationInfo i) (OriginationInfo i)
Prism' (OperationInfo Result) ContractAddress
_OpOriginate

-- | Perform key revealing within a batch.
revealKeyM :: RevealData -> OperationsBatch ()
revealKeyM :: RevealData -> OperationsBatch ()
revealKeyM = (RevealData -> OperationInfo ClientInput)
-> Prism' (OperationInfo Result) ()
-> RevealData
-> OperationsBatch ()
forall inp out.
(inp -> OperationInfo ClientInput)
-> Prism' (OperationInfo Result) out -> inp -> OperationsBatch out
runOperationM RevealData -> OperationInfo ClientInput
forall i. RevealInfo i -> OperationInfo i
OpReveal forall i. Prism' (OperationInfo i) (RevealInfo i)
Prism' (OperationInfo Result) ()
_OpReveal

-- | Execute a batch.
runOperationsBatch
  :: ( HasTezosRpc m
     , HasTezosClient m
     , WithClientLog env m
     )
  => ImplicitAddressOrAlias
  -> OperationsBatch a
  -> m (Maybe OperationHash, a)
runOperationsBatch :: forall (m :: * -> *) env a.
(HasTezosRpc m, HasTezosClient m, WithClientLog env m) =>
ImplicitAddressOrAlias
-> OperationsBatch a -> m (Maybe OperationHash, a)
runOperationsBatch ImplicitAddressOrAlias
sender (OperationsBatch BatchingM
  (OperationInfo ClientInput)
  (OperationInfo Result)
  BatchedOperationError
  a
batch) =
  ([OperationInfo ClientInput]
 -> m (Maybe OperationHash, [OperationInfo Result]))
-> BatchingM
     (OperationInfo ClientInput)
     (OperationInfo Result)
     BatchedOperationError
     a
-> m (Maybe OperationHash, a)
forall (m :: * -> *) e i r o a.
(Functor m, Buildable e) =>
([i] -> m (r, [o])) -> BatchingM i o e a -> m (r, a)
unsafeRunBatching (ImplicitAddressOrAlias
-> [OperationInfo ClientInput]
-> m (Maybe OperationHash, [OperationInfo Result])
forall (m :: * -> *) env.
(HasTezosRpc m, HasTezosClient m, WithClientLog env m) =>
ImplicitAddressOrAlias
-> [OperationInfo ClientInput]
-> m (Maybe OperationHash, [OperationInfo Result])
runOperations ImplicitAddressOrAlias
sender) BatchingM
  (OperationInfo ClientInput)
  (OperationInfo Result)
  BatchedOperationError
  a
batch