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

-- | Types used for interaction with @octez-client@.

module Morley.Client.TezosClient.Types
  ( CmdArg (..)
  , CalcOriginationFeeData (..)
  , CalcTransferFeeData (..)
  , TezosClientConfig (..)
  , TezosClientEnv (..)
  , HasTezosClientEnv (..)
  , SecretKeyEncryption (..)

  -- * Lens
  , tceEndpointUrlL
  , tceTezosClientPathL
  , tceMbTezosClientDataDirL
  ) where

import Data.Aeson (FromJSON(..), KeyValue(..), ToJSON(..), object, withObject, (.:))
import Data.ByteArray (ScrubbedBytes)
import Data.Fixed (E6, Fixed(..))
import Fmt (Buildable(..), pretty)
import Morley.Util.Lens (makeLensesWith, postfixLFields)
import Servant.Client (BaseUrl(..), showBaseUrl)
import Text.Hex (encodeHex)

import Morley.Client.RPC.Types (OperationHash)
import Morley.Client.Util
import Morley.Micheline
import Morley.Michelson.Printer
import Morley.Michelson.Typed (Contract, EpName, Value)
import Morley.Michelson.Typed qualified as T
import Morley.Tezos.Address
import Morley.Tezos.Address.Alias (AddressOrAlias(..), Alias)
import Morley.Tezos.Core
import Morley.Tezos.Crypto

-- | An object that can be put as argument to a @octez-client@ command-line call.
class CmdArg a where
  -- | Render an object as a command-line argument.
  toCmdArg :: a -> String
  default toCmdArg :: Buildable a => a -> String
  toCmdArg = a -> String
forall a b. (Buildable a, FromBuilder b) => a -> b
pretty

instance CmdArg Text where

instance CmdArg LText where

instance CmdArg Word16 where

instance CmdArg SecretKey where
  toCmdArg :: SecretKey -> String
toCmdArg = Text -> String
forall a. CmdArg a => a -> String
toCmdArg (Text -> String) -> (SecretKey -> Text) -> SecretKey -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SecretKey -> Text
formatSecretKey

instance CmdArg (KindedAddress kind) where

instance CmdArg Address where

instance CmdArg ByteString where
  toCmdArg :: ByteString -> String
toCmdArg = Text -> String
forall a. CmdArg a => a -> String
toCmdArg (Text -> String) -> (ByteString -> Text) -> ByteString -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Text
"0x" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>) (Text -> Text) -> (ByteString -> Text) -> ByteString -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Text
encodeHex

instance CmdArg EpName where
  toCmdArg :: EpName -> String
toCmdArg = Text -> String
forall a. CmdArg a => a -> String
toCmdArg (Text -> String) -> (EpName -> Text) -> EpName -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. EpName -> Text
epNameToTezosEp

instance CmdArg Mutez where
  toCmdArg :: Mutez -> String
toCmdArg Mutez
m = Fixed E6 -> String
forall b a. (PrettyShow a, Show a, IsString b) => a -> b
show (Fixed E6 -> String) -> (Integer -> Fixed E6) -> Integer -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall k (a :: k). Integer -> Fixed a
MkFixed @_ @E6 (Integer -> String) -> Integer -> String
forall a b. (a -> b) -> a -> b
$ Word63 -> Integer
forall a b. (Integral a, Integral b, CheckIntSubType a b) => a -> b
fromIntegral (Mutez -> Word63
unMutez Mutez
m)

instance T.ProperUntypedValBetterErrors t => CmdArg (Value t) where
  toCmdArg :: Value t -> String
toCmdArg = LText -> String
forall a. CmdArg a => a -> String
toCmdArg (LText -> String) -> (Value t -> LText) -> Value t -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> Value t -> LText
forall (t :: T).
ProperUntypedValBetterErrors t =>
Bool -> Value t -> LText
printTypedValue Bool
True

instance CmdArg (Contract cp st) where
  toCmdArg :: Contract cp st -> String
toCmdArg = LText -> String
forall a. ToString a => a -> String
toString (LText -> String)
-> (Contract cp st -> LText) -> Contract cp st -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bool -> Contract cp st -> LText
forall (p :: T) (s :: T). Bool -> Contract p s -> LText
printTypedContract Bool
True

instance CmdArg BaseUrl where
  toCmdArg :: BaseUrl -> String
toCmdArg = BaseUrl -> String
showBaseUrl

instance CmdArg OperationHash

instance CmdArg (Alias kind) where

instance CmdArg (AddressOrAlias kind) where

-- | Representation of address secret key encryption type
data SecretKeyEncryption
  = UnencryptedKey
  | EncryptedKey
  | LedgerKey
  deriving stock (SecretKeyEncryption -> SecretKeyEncryption -> Bool
(SecretKeyEncryption -> SecretKeyEncryption -> Bool)
-> (SecretKeyEncryption -> SecretKeyEncryption -> Bool)
-> Eq SecretKeyEncryption
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: SecretKeyEncryption -> SecretKeyEncryption -> Bool
$c/= :: SecretKeyEncryption -> SecretKeyEncryption -> Bool
== :: SecretKeyEncryption -> SecretKeyEncryption -> Bool
$c== :: SecretKeyEncryption -> SecretKeyEncryption -> Bool
Eq, Int -> SecretKeyEncryption -> ShowS
[SecretKeyEncryption] -> ShowS
SecretKeyEncryption -> String
(Int -> SecretKeyEncryption -> ShowS)
-> (SecretKeyEncryption -> String)
-> ([SecretKeyEncryption] -> ShowS)
-> Show SecretKeyEncryption
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [SecretKeyEncryption] -> ShowS
$cshowList :: [SecretKeyEncryption] -> ShowS
show :: SecretKeyEncryption -> String
$cshow :: SecretKeyEncryption -> String
showsPrec :: Int -> SecretKeyEncryption -> ShowS
$cshowsPrec :: Int -> SecretKeyEncryption -> ShowS
Show)

-- | Configuration maintained by @octez-client@, see its @config@ subcommands
-- (e. g. @octez-client config show@).
-- Only the field we are interested in is present here.
newtype TezosClientConfig = TezosClientConfig { TezosClientConfig -> BaseUrl
tcEndpointUrl :: BaseUrl }
  deriving stock Int -> TezosClientConfig -> ShowS
[TezosClientConfig] -> ShowS
TezosClientConfig -> String
(Int -> TezosClientConfig -> ShowS)
-> (TezosClientConfig -> String)
-> ([TezosClientConfig] -> ShowS)
-> Show TezosClientConfig
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [TezosClientConfig] -> ShowS
$cshowList :: [TezosClientConfig] -> ShowS
show :: TezosClientConfig -> String
$cshow :: TezosClientConfig -> String
showsPrec :: Int -> TezosClientConfig -> ShowS
$cshowsPrec :: Int -> TezosClientConfig -> ShowS
Show

-- | For reading @octez-client@ config.
instance FromJSON TezosClientConfig where
  parseJSON :: Value -> Parser TezosClientConfig
parseJSON = String
-> (Object -> Parser TezosClientConfig)
-> Value
-> Parser TezosClientConfig
forall a. String -> (Object -> Parser a) -> Value -> Parser a
withObject String
"node info" ((Object -> Parser TezosClientConfig)
 -> Value -> Parser TezosClientConfig)
-> (Object -> Parser TezosClientConfig)
-> Value
-> Parser TezosClientConfig
forall a b. (a -> b) -> a -> b
$ \Object
o -> BaseUrl -> TezosClientConfig
TezosClientConfig (BaseUrl -> TezosClientConfig)
-> Parser BaseUrl -> Parser TezosClientConfig
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
o Object -> Key -> Parser BaseUrl
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"endpoint"

-- | Runtime environment for @octez-client@ bindings.
data TezosClientEnv = TezosClientEnv
  { TezosClientEnv -> BaseUrl
tceEndpointUrl :: BaseUrl
  -- ^ URL of tezos node on which operations are performed.
  , TezosClientEnv -> String
tceTezosClientPath :: FilePath
  -- ^ Path to tezos client binary through which operations are
  -- performed.
  , TezosClientEnv -> Maybe String
tceMbTezosClientDataDir :: Maybe FilePath
  -- ^ Path to tezos client data directory.
  }

makeLensesWith postfixLFields ''TezosClientEnv

-- | Using this type class one can require 'MonadReader' constraint
-- that holds any type with 'TezosClientEnv' inside.
class HasTezosClientEnv env where
  tezosClientEnvL :: Lens' env TezosClientEnv

-- | Data required for calculating fee for transfer operation.
data CalcTransferFeeData = forall t kind. T.UntypedValScope t => CalcTransferFeeData
  { ()
ctfdTo :: AddressOrAlias kind
  , ()
ctfdParam :: Value t
  , CalcTransferFeeData -> EpName
ctfdEp :: EpName
  , CalcTransferFeeData -> TezosMutez
ctfdAmount :: TezosMutez
  }

instance ToJSON CalcTransferFeeData where
  toJSON :: CalcTransferFeeData -> Value
toJSON CalcTransferFeeData{EpName
AddressOrAlias kind
Value t
TezosMutez
ctfdAmount :: TezosMutez
ctfdEp :: EpName
ctfdParam :: Value t
ctfdTo :: AddressOrAlias kind
ctfdAmount :: CalcTransferFeeData -> TezosMutez
ctfdEp :: CalcTransferFeeData -> EpName
ctfdParam :: ()
ctfdTo :: ()
..} = [Pair] -> Value
object
    [ Key
"destination" Key -> Text -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
.= forall a b. (Buildable a, FromBuilder b) => a -> b
pretty @_ @Text AddressOrAlias kind
ctfdTo
    , Key
"amount" Key -> Text -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
.= (forall a. IsString a => String -> a
fromString @Text (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ Mutez -> String
forall a. CmdArg a => a -> String
toCmdArg (Mutez -> String) -> Mutez -> String
forall a b. (a -> b) -> a -> b
$ TezosMutez -> Mutez
unTezosMutez TezosMutez
ctfdAmount)
    , Key
"arg" Key -> Text -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
.= (forall a. IsString a => String -> a
fromString @Text (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ Value t -> String
forall a. CmdArg a => a -> String
toCmdArg Value t
ctfdParam)
    , Key
"entrypoint" Key -> Text -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
.= (forall a. IsString a => String -> a
fromString @Text (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ EpName -> String
forall a. CmdArg a => a -> String
toCmdArg EpName
ctfdEp)
    ]

-- | Data required for calculating fee for origination operation.
data CalcOriginationFeeData cp st = forall kind. CalcOriginationFeeData
  { ()
cofdFrom :: AddressOrAlias kind
  , forall (cp :: T) (st :: T).
CalcOriginationFeeData cp st -> TezosMutez
cofdBalance :: TezosMutez
  , forall (cp :: T) (st :: T).
CalcOriginationFeeData cp st -> Maybe ScrubbedBytes
cofdMbFromPassword :: Maybe ScrubbedBytes
  , forall (cp :: T) (st :: T).
CalcOriginationFeeData cp st -> Contract cp st
cofdContract :: Contract cp st
  , forall (cp :: T) (st :: T).
CalcOriginationFeeData cp st -> Value st
cofdStorage :: Value st
  , forall (cp :: T) (st :: T).
CalcOriginationFeeData cp st -> TezosInt64
cofdBurnCap :: TezosInt64
  }