morley-1.18.0: src/Morley/Tezos/Address/Alias.hs
-- SPDX-FileCopyrightText: 2022 Oxhead Alpha
-- SPDX-License-Identifier: LicenseRef-MIT-OA
module Morley.Tezos.Address.Alias
( AddressOrAlias(..)
, Alias(..)
, SomeAlias(..)
, SomeAddressOrAlias(..)
, ImplicitAlias
, ContractAlias
, ImplicitAddressOrAlias
, ContractAddressOrAlias
, unAlias
, mkAlias
, aliasKindSanity
)
where
import Data.Aeson (FromJSON(..), ToJSON(..))
import Data.Constraint (Dict(..), (\\))
import Data.Singletons (SingI(..), demote)
import Data.Type.Equality ((:~:)(..))
import Fmt (Buildable(..), nameF, pretty, (+|), (|+))
import Options.Applicative qualified as Opt
import Morley.Tezos.Address
import Morley.Tezos.Address.Kinds
import Morley.Util.CLI (HasCLReader(..))
import Morley.Util.Sing
-- | @tezos-client@ can associate addresses with textual aliases.
-- This type denotes such an alias.
data Alias (kind :: AddressKind) where
ImplicitAlias :: Text -> Alias 'AddressKindImplicit
ContractAlias :: Text -> Alias 'AddressKindContract
-- | A type only allowing v'ImplicitAlias' values.
type ImplicitAlias = Alias 'AddressKindImplicit
-- | A type only allowing v'ContractAlias' values.
type ContractAlias = Alias 'AddressKindContract
deriving stock instance Show (Alias kind)
deriving stock instance Eq (Alias kind)
deriving stock instance Ord (Alias kind)
-- | Get raw alias text from 'Alias'
unAlias :: Alias kind -> Text
unAlias = \case
ImplicitAlias x -> x
ContractAlias x -> x
-- | Construct an 'Alias' from alias 'Text'.
mkAlias :: forall kind. (SingI kind, L1AddressKind kind) => Text -> Alias kind
mkAlias = usingImplicitOrContractKind @kind $ case sing @kind of
SAddressKindImplicit -> ImplicitAlias
SAddressKindContract -> ContractAlias
instance Buildable (Alias kind) where
build = build . unAlias
instance ToJSON (Alias kind) where
toJSON = toJSON . unAlias
instance (SingI kind, L1AddressKind kind) => FromJSON (Alias kind) where
parseJSON = fmap mkAlias . parseJSON
-- | Existential wrapper over 'Alias'.
data SomeAlias = forall kind. SomeAlias (Alias kind)
deriving stock instance Show SomeAlias
instance Buildable SomeAlias where
build (SomeAlias x) = build x
-- | Given an 'Alias', prove it's @kind@ is well-defined (i.e. it has a 'SingI'
-- instance and satisfies 'L1AddressKind' constraint)
aliasKindSanity :: Alias kind -> Dict (L1AddressKind kind, SingI kind)
aliasKindSanity = \case
ImplicitAlias{} -> Dict
ContractAlias{} -> Dict
-- | Representation of an address that @tezos-client@ uses. It can be
-- an address itself or a textual alias.
data AddressOrAlias kind
= AddressResolved (KindedAddress kind)
-- ^ Address itself, can be used as is.
| AddressAlias (Alias kind)
-- ^ Address alias, should be resolved by @tezos-client@.
deriving stock (Show, Eq, Ord)
instance (SingI kind, L1AddressKind kind) => HasCLReader (AddressOrAlias kind) where
getReader =
Opt.str >>= \addrOrAlias ->
case parseAddress addrOrAlias of
Right (MkAddress (addr :: KindedAddress kind')) ->
case eqI @kind @kind' \\ addressKindSanity addr of
Just Refl -> pure $ AddressResolved addr
Nothing -> Opt.readerError $ pretty $ nameF "Unexpected address kind" $
"expected " +| demote @kind |+ " address, but got " +| addr |+ ""
Left _ -> pure $ AddressAlias (mkAlias addrOrAlias)
getMetavar = "ADDRESS OR ALIAS"
instance Buildable (AddressOrAlias kind) where
build = \case
AddressResolved addr -> build addr
AddressAlias alias -> build alias
-- | Convenience type synonym.
type ImplicitAddressOrAlias = AddressOrAlias 'AddressKindImplicit
-- | Convenience type synonym.
type ContractAddressOrAlias = AddressOrAlias 'AddressKindContract
-- | Existential over 'AddressOrAlias'.
data SomeAddressOrAlias = forall kind. SomeAddressOrAlias (AddressOrAlias kind)
instance Buildable SomeAddressOrAlias where
build (SomeAddressOrAlias x) = build x
deriving stock instance Show SomeAddressOrAlias