morley-client-0.1.0: src/Morley/Client/Util.hs
-- SPDX-FileCopyrightText: 2020 Tocqueville Group
--
-- SPDX-License-Identifier: LicenseRef-MIT-TQ
module Morley.Client.Util
( epNameToTezosEp
, extractAddressesFromValue
, disableAlphanetWarning
, runContract
, runContractSimple
, RunContractParameters(..)
-- tezos-client password-related helpers
, scrubbedBytesToString
, readScrubbedBytes
) where
import Data.ByteArray (ScrubbedBytes, convert)
import qualified Data.ByteString as BS (getLine)
import Data.Constraint ((\\))
import Generics.SYB (everything, mkQ)
import System.Environment (setEnv)
import Morley.Client.RPC.AsRPC (AsRPC, rpcHasNoBigMapEvi, rpcStorageScopeEvi)
import Morley.Client.RPC.Class
import Morley.Client.RPC.Getters
import Morley.Client.RPC.Types
import Morley.Micheline
import Morley.Michelson.Text
import qualified Morley.Michelson.Typed as T (Contract, ParameterScope, StorageScope, Value)
import Morley.Michelson.Typed.Entrypoints (EpAddress(..), parseEpAddress)
import Morley.Michelson.Untyped (InternalByteString(..), Value, Value'(..))
import Morley.Michelson.Untyped.Entrypoints (EpName(..), pattern DefEpName)
import Morley.Tezos.Address
import Morley.Tezos.Core (Mutez, zeroMutez)
import Morley.Util.Exception as E (throwLeft)
-- | Sets the environment variable for disabling tezos-client
-- "not a mainnet" warning
disableAlphanetWarning :: IO ()
disableAlphanetWarning = setEnv "TEZOS_CLIENT_UNSAFE_DISABLE_DISCLAIMER" "YES"
-- | Convert 'EpName' to the textual representation used by RPC and tezos-client.
epNameToTezosEp :: EpName -> Text
epNameToTezosEp = \case
DefEpName -> "default"
epName -> unEpName epName
-- | Extract all addresses value from given untyped 'Value'.
--
-- Note that it returns all values that can be used as an address.
-- However, some of fetched values can never be used as an address.
extractAddressesFromValue :: Value -> [Address]
extractAddressesFromValue val =
everything (<>) (mkQ [] fetchAddress) val
where
fetchAddress :: Value -> [Address]
fetchAddress = \case
ValueString s -> case parseEpAddress (unMText s) of
Right addr -> [eaAddress addr]
Left _ -> []
ValueBytes (InternalByteString b) -> case parseAddressRaw b of
Right addr -> [addr]
Left _ -> []
_ -> []
-- | Simplified version of 'runContract' for convenience
--
-- Sets transfer amount to 0 and sender and source are both unspecified.
runContractSimple
:: forall cp st m. (HasTezosRpc m, T.ParameterScope cp, T.StorageScope st)
=> T.Contract cp st -- ^ Contract
-> T.Value cp -- ^ Parameter passed to the contract
-> T.Value st -- ^ Initial storage
-> Mutez -- ^ Initial balance
-> m (AsRPC (T.Value st))
runContractSimple rcpContract rcpParameter rcpStorage rcpBalance =
let rcpSender = Nothing
rcpSource = Nothing
rcpAmount = zeroMutez
in runContract RunContractParameters {..}
-- | A structure with all the parameters for 'runContract'
data RunContractParameters cp st = RunContractParameters
{ rcpContract :: T.Contract cp st
, rcpParameter :: T.Value cp
, rcpStorage :: T.Value st
, rcpBalance :: Mutez
, rcpAmount :: Mutez
, rcpSender :: Maybe Address
, rcpSource :: Maybe Address
}
-- | Run contract with given parameter and storage and get new storage without
-- injecting anything to the chain.
--
-- Storage type is limited to not have any bigmaps because their updates are treated differently
-- in node RPC and its quite nontrivial to properly support storage update when storage type
-- contains bigmaps.
runContract
:: forall cp st m. (HasTezosRpc m, T.ParameterScope cp, T.StorageScope st)
=> RunContractParameters cp st -> m (AsRPC (T.Value st))
runContract RunContractParameters{..} = do
headConstants <- getBlockConstants HeadId
let args = RunCode
{ rcScript = toExpression rcpContract
, rcStorage = toExpression rcpStorage
, rcInput = toExpression rcpParameter
, rcAmount = TezosMutez rcpAmount
, rcBalance = TezosMutez rcpBalance
, rcChainId = bcChainId headConstants
, rcSource = rcpSender
, rcPayer = rcpSource
}
res <- runCode args
throwLeft @_ @FromExpressionError $ pure $
fromExpression @(AsRPC (T.Value st)) (rcrStorage res)
\\ rpcStorageScopeEvi @st
\\ rpcHasNoBigMapEvi @st
-- | Function for relatively safe getting password from stdin.
-- After reading bytes are converted to @ScrubbedBytes@, thus it's harder
-- to accidentally leak them.
readScrubbedBytes :: MonadIO m => m ScrubbedBytes
readScrubbedBytes = convert <$> liftIO BS.getLine
-- | Convert @ScrubbedBytes@ to @String@, so that it can be passed to @tezos-client@
-- as a stdin
scrubbedBytesToString :: ScrubbedBytes -> String
scrubbedBytesToString = decodeUtf8 . convert @ScrubbedBytes @ByteString