diff --git a/cabal.project b/cabal.project index 9817467..299edf2 100644 --- a/cabal.project +++ b/cabal.project @@ -14,9 +14,9 @@ repository cardano-haskell-packages -- Note: We repeat the hackage index-state because haskell.nix has trouble parsing the index-state with multiple repositories -- Cabal will ignore the first index-state anyway. -- cf. https://github.com/input-output-hk/cardano-ledger/pull/3265#issue-1548330220 -index-state: 2022-10-29T00:00:00Z +index-state: 2023-03-09T12:19:31Z index-state: - , hackage.haskell.org 2022-10-29T00:00:00Z + , hackage.haskell.org 2023-03-09T12:19:31Z , cardano-haskell-packages 2022-12-14T00:40:15Z profiling: False @@ -200,13 +200,9 @@ source-repository-package tag: 18a931648550246695c790578d4a55ee2f10463e --sha256: sha256-3Rnj/g3KLzOW5YSieqsUa9IF1Td22Eskk5KuVsOFgEQ= subdir: - lib/cli lib/core - lib/core-integration lib/dbvar - lib/launcher lib/numeric - lib/shelley lib/strict-non-empty-containers lib/test-utils lib/text-class @@ -282,3 +278,4 @@ allow-newer: *:aeson, *:servant, *:time, + playground-common:recursion-schemes, diff --git a/cabal.project.freeze b/cabal.project.freeze index 0fc68c7..5bfecba 100644 --- a/cabal.project.freeze +++ b/cabal.project.freeze @@ -74,7 +74,7 @@ constraints: any.Boolean ==0.2.4, bech32 -release -static, any.bech32-th ==1.1.1, bech32-th -release, - any.bifunctors ==5.5.7, + any.bifunctors ==5.5.15, bifunctors +semigroups +tagged, any.bimap ==0.4.0, any.bin ==0.1, @@ -85,11 +85,11 @@ constraints: any.Boolean ==0.2.4, any.blaze-markup ==0.8.2.8, any.blaze-textual ==0.2.2.1, blaze-textual -developer -integer-simple +native, - any.blockfrost-api ==0.4.0.0, + any.blockfrost-api ==0.7.1.0, blockfrost-api +buildfast -production, - any.blockfrost-client ==0.4.0.1, + any.blockfrost-client ==0.7.1.1, blockfrost-client +buildfast -examples -production, - any.blockfrost-client-core ==0.4.0.2, + any.blockfrost-client-core ==0.6.0.0, blockfrost-client-core +buildfast -production, any.boring ==0.2, boring +tagged, @@ -153,14 +153,8 @@ constraints: any.Boolean ==0.2.4, any.cardano-streaming ==1.1.0.0, any.cardano-wallet ==2022.7.1, cardano-wallet -release, - any.cardano-wallet-cli ==2022.7.1, - cardano-wallet-cli -release, any.cardano-wallet-core ==2022.7.1, cardano-wallet-core -release +scrypt, - any.cardano-wallet-core-integration ==2022.7.1, - cardano-wallet-core-integration -release, - any.cardano-wallet-launcher ==2022.7.1, - cardano-wallet-launcher -release, any.cardano-wallet-test-utils ==2022.7.1, cardano-wallet-test-utils -release, any.case-insensitive ==1.2.1.0, @@ -267,7 +261,7 @@ constraints: any.Boolean ==0.2.4, any.formatting ==6.3.7, any.foundation ==0.0.29, foundation -bench-all -bounds-check -doctest -experimental -linktest -minimal-deps, - any.free ==5.1.3, + any.free ==5.1.10, any.freer-extras ==1.1.0.0, any.freer-simple ==1.2.1.2, any.fusion-plugin-types ==0.1.0, @@ -358,7 +352,7 @@ constraints: any.Boolean ==0.2.4, any.lattices ==2.0.3, any.lazy-search ==0.1.3.0, any.lazysmallcheck ==0.6, - any.lens ==4.19.2, + any.lens ==5.2.1, lens -benchmark-uniplate -dump-splices +inlining -j -old-inline-pragmas -safe +test-doctests +test-hunit +test-properties +test-templates +trustworthy, any.lens-aeson ==1.1.3, any.libyaml ==0.1.2, @@ -384,7 +378,7 @@ constraints: any.Boolean ==0.2.4, mersenne-random-pure64 -small_base, any.microlens ==0.4.13.0, any.microlens-mtl ==0.2.0.2, - any.microlens-th ==0.4.3.6, + any.microlens-th ==0.4.3.11, any.microstache ==1.0.2.2, any.mime-types ==0.1.1.0, any.mmorph ==1.2.0, @@ -494,7 +488,7 @@ constraints: any.Boolean ==0.2.4, any.primitive-addr ==0.1.0.2, any.process ==1.6.13.2, any.process-extras ==0.7.4, - any.profunctors ==5.6, + any.profunctors ==5.6.2, any.protolude ==0.3.0, protolude -dev, any.psqueues ==0.2.7.3, @@ -514,7 +508,7 @@ constraints: any.Boolean ==0.2.4, any.random ==1.2.1.1, any.random-shuffle ==0.0.4, any.readable ==0.3.1, - any.recursion-schemes ==5.1.3, + any.recursion-schemes ==5.2.2.4, recursion-schemes +template-haskell, any.recv ==0.0.0, any.reducers ==3.12.4, @@ -539,7 +533,7 @@ constraints: any.Boolean ==0.2.4, any.selective ==0.5, any.semialign ==1.2.0.1, semialign +semigroupoids, - any.semigroupoids ==5.3.4, + any.semigroupoids ==5.3.7, semigroupoids +comonad +containers +contravariant +distributive +doctests +tagged +unordered-containers, any.semigroups ==0.20, semigroups +binary +bytestring -bytestring-builder +containers +deepseq +hashable +tagged +template-haskell +text +transformers +unordered-containers, @@ -637,7 +631,7 @@ constraints: any.Boolean ==0.2.4, any.text-short ==0.1.5, text-short -asserts, any.tf-random ==0.5, - any.th-abstraction ==0.3.2.0, + any.th-abstraction ==0.4.5.0, any.th-compat ==0.1.4, any.th-expand-syns ==0.4.7.0, any.th-extras ==0.0.0.4, @@ -732,4 +726,4 @@ constraints: any.Boolean ==0.2.4, any.zlib ==0.6.3.0, zlib -bundled-c-zlib -non-blocking-ffi -pkg-config, any.zlib-bindings ==0.1.1.5 -index-state: cardano-haskell-packages 2022-12-13T23:38:19Z, hackage.haskell.org 2022-10-28T22:57:46Z +index-state: cardano-haskell-packages 2022-12-13T23:38:19Z, hackage.haskell.org 2023-03-09T12:19:31Z diff --git a/src/Tokenomia/CLI.hs b/src/Tokenomia/CLI.hs index 62c5a13..cfb156a 100644 --- a/src/Tokenomia/CLI.hs +++ b/src/Tokenomia/CLI.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE DataKinds #-} {-# LANGUAGE ExtendedDefaultRules #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ImportQualifiedPost #-} @@ -22,16 +23,18 @@ import Data.List.NonEmpty as NonEmpty ( NonEmpty, fromList ) import Shh ( ExecReference(SearchPath), load ) import Streamly.Prelude qualified as S + import Tokenomia.Ada.Transfer qualified as Ada import Tokenomia.Common.Environment - ( Environment - , getMainnetEnvironmment - , getPreprodEnvironmment - , getTestnetEnvironmment + ( CustomNetworkArgs(..) + , Environment + , TokenomiaNetwork(..) + , getNetworkEnvironment + , readCustomNetworkArgsFile ) import Tokenomia.Common.Error ( TokenomiaError(..) ) import Tokenomia.Common.Shell.Console ( clearConsole, printLn, printOpt ) -import Tokenomia.Common.Shell.InteractiveMenu ( DisplayMenuItem(..), askMenu ) +import Tokenomia.Common.Shell.InteractiveMenu ( DisplayMenuItem(..), askMenu, askString ) import Tokenomia.Node.Status qualified as Node import Tokenomia.Token.CLAPStyle.Burn qualified as Token import Tokenomia.Token.CLAPStyle.Mint qualified as Token @@ -43,64 +46,87 @@ import Tokenomia.Wallet.Collateral.Write qualified as Wallet load SearchPath ["cardano-cli"] -main :: IO () +main :: IO () main = do clearConsole printLn "#############################" printLn "# Welcome to Tokenomia #" printLn "#############################" printLn "" - selectNetwork + network <- liftIO selectNetwork + environment <- getNetworkEnvironment network + clearConsole + runExceptT (runReaderT recursiveMenu environment) >>= \case + Left e -> printLn $ "An unexpected error occured :" <> show e + Right _ -> return () printLn "#############################" printLn "# End of Tokenomia #" printLn "#############################" -waitAndClear :: IO() -waitAndClear = do - _ <- printOpt "-n" "> press enter to continue..." >> getLine - clearConsole -selectNetwork :: IO() +selectNetwork :: IO TokenomiaNetwork selectNetwork = do - printLn "----------------------" - printLn " Select a network" - printLn "----------------------" - environment <- liftIO $ askMenu networks >>= \case - SelectMainnet -> getMainnetEnvironmment 764824073 - SelectPreprod -> getPreprodEnvironmment 1 - SelectTestnet -> getTestnetEnvironmment 1097911063 - clearConsole - result :: Either TokenomiaError () <- runExceptT $ runReaderT recursiveMenu environment - case result of - Left e -> printLn $ "An unexpected error occured :" <> show e - Right _ -> return () + printLn "----------------------" + printLn " Select a network" + printLn "----------------------" + askMenu networks >>= \case + SelectMainnet -> pure MainnetNetwork + SelectPreprod -> pure PreprodNetwork + SelectTestnet -> pure TestnetNetwork + SelectCustom -> CustomNetwork <$> inputCustomNetworkArgs + +inputCustomNetworkArgs :: IO CustomNetworkArgs +inputCustomNetworkArgs = do + clearConsole + printLn "--------------------------------------------" + printLn " Enter custom network arguments file path" + printLn "--------------------------------------------" + path <- askString "- File path : " + readCustomNetworkArgsFile path >>= \case + Right args -> pure args + Left e -> do + printLn "" + printLn $ "Invalid custom network arguments :" <> e + printLn "" + waitAndClear + inputCustomNetworkArgs networks :: NonEmpty SelectEnvironment -networks = NonEmpty.fromList [ - SelectMainnet, - SelectPreprod, - SelectTestnet +networks = NonEmpty.fromList + [ SelectMainnet + , SelectPreprod + , SelectTestnet + , SelectCustom ] data SelectEnvironment = SelectMainnet | SelectPreprod | SelectTestnet + | SelectCustom instance DisplayMenuItem SelectEnvironment where displayMenuItem item = case item of SelectMainnet -> "Mainnet (magicNumber 764824073)" SelectPreprod -> "Preprod (magicNumber 1)" SelectTestnet -> "`Old` Testnet (magicNumber 1097911063)" + SelectCustom -> "Use a custom network" -recursiveMenu - :: ( S.MonadAsync m - , MonadReader Environment m - , MonadError TokenomiaError m) => m () +waitAndClear :: IO () +waitAndClear = do + _ <- printOpt "-n" "> press enter to continue..." >> getLine + clearConsole + + +recursiveMenu :: + ( S.MonadAsync m + , MonadReader Environment m + , MonadError TokenomiaError m + ) => m () recursiveMenu = do printLn "----------------------" printLn " Select an action" @@ -109,6 +135,7 @@ recursiveMenu = do runAction action `catchError` (\case + NetworkNotSupported errorMsg -> printLn $ "Network not supported : " <> errorMsg NoWalletRegistered -> printLn "Register a Wallet First..." NoWalletWithoutCollateral -> printLn "All Wallets contain collateral..." NoWalletWithCollateral -> printLn "No Wallets with collateral..." diff --git a/src/Tokenomia/Common/Blockfrost.hs b/src/Tokenomia/Common/Blockfrost.hs index a1200ed..e2b738e 100644 --- a/src/Tokenomia/Common/Blockfrost.hs +++ b/src/Tokenomia/Common/Blockfrost.hs @@ -1,13 +1,7 @@ -{-# LANGUAGE DuplicateRecordFields #-} -{-# LANGUAGE ExtendedDefaultRules #-} {-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE ImportQualifiedPost #-} {-# LANGUAGE LambdaCase #-} -{-# LANGUAGE RankNTypes #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# OPTIONS_GHC -fno-warn-missing-signatures #-} -{-# OPTIONS_GHC -fno-warn-unused-top-binds #-} +{-# LANGUAGE RecordWildCards #-} module Tokenomia.Common.Blockfrost ( projectFromEnv'' @@ -17,15 +11,26 @@ module Tokenomia.Common.Blockfrost import Blockfrost.Client qualified as B import Prelude hiding ( head ) -import Tokenomia.Common.Environment ( Environment(Mainnet, Testnet) ) +import Tokenomia.Common.Environment ( Environment(..), TokenomiaNetwork(..), networkMagicNumber ) +import Control.Monad.Except ( MonadError(throwError) ) import Control.Monad.Reader ( MonadIO(..), MonadReader, asks ) +import Tokenomia.Common.Error ( TokenomiaError(NetworkNotSupported) ) projectFromEnv'' :: ( MonadIO m - , MonadReader Environment m) => m B.Project + , MonadReader Environment m, MonadError TokenomiaError m) => m B.Project projectFromEnv'' = do - environmentPath <- asks (\case - Testnet {} -> "BLOCKFROST_TOKEN_TESTNET_PATH" - Mainnet {} -> "BLOCKFROST_TOKEN_MAINNET_PATH") - liftIO $ B.projectFromEnv' environmentPath + environmentPath <- asks + (\case + Mainnet {} + -> Right "BLOCKFROST_TOKEN_MAINNET_PATH" + Testnet {..} | magicNumber == networkMagicNumber PreprodNetwork + -> Right "BLOCKFROST_TOKEN_PREPROD_PATH" + Testnet {..} | magicNumber == networkMagicNumber TestnetNetwork + -> Left "Blockfrost does not support the legacy Testnet anymore" + _ -> Left "Blockfrost only supports Mainnet, Preprod and Preview networks" + ) + case environmentPath of + Left err -> throwError (NetworkNotSupported err) + Right env -> liftIO $ B.projectFromEnv' env diff --git a/src/Tokenomia/Common/Environment.hs b/src/Tokenomia/Common/Environment.hs index fb2ee63..1681d76 100644 --- a/src/Tokenomia/Common/Environment.hs +++ b/src/Tokenomia/Common/Environment.hs @@ -1,3 +1,6 @@ +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ImportQualifiedPost #-} {-# LANGUAGE LambdaCase #-} @@ -5,24 +8,24 @@ {-# LANGUAGE ScopedTypeVariables #-} module Tokenomia.Common.Environment - ( Environment(..) + ( CustomNetworkArgs(..) + , Environment(..) + , TokenomiaNetwork(..) , convertToExternalPosix , convertToInternalPosix , formatISO8601 , getFirstShelleySlot , getFirstShelleySlotTime - , getMainnetEnvironmment - , getNetworkEnvironmment - , getPreprodEnvironmment - , getTestnetEnvironmment + , getNetworkEnvironment + , networkMagicNumber + , readCustomNetworkArgsFile , readNetworkMagic , toPosixTime , toSlot ) where import Control.Monad.Reader ( MonadIO(..), MonadReader(ask) ) - - +import Prelude hiding ( readFile ) import System.Environment ( getEnv ) @@ -40,41 +43,81 @@ import Cardano.Api ) import Cardano.Api qualified ( NetworkId(Mainnet, Testnet) ) - -import Cardano.Api.Shelley qualified as Shelley import Ledger ( POSIXTime(POSIXTime), Slot(Slot) ) import Ouroboros.Consensus.BlockchainTime.WallClock.Types ( SystemStart(..) ) +import Data.Aeson ( FromJSON, eitherDecode ) +import Data.ByteString.Lazy ( readFile ) import Data.Coerce ( coerce ) import Data.Time.Clock qualified as ExternalPosix import Data.Time.Clock.POSIX qualified as ExternalPosix import Data.Time.ISO8601 qualified as ExternalPosix - -data Environment = Testnet - { magicNumber :: Integer - , localNodeConnectInfo :: LocalNodeConnectInfo CardanoMode - , preShelleyEpochs :: Integer - , byronSlotsPerEpoch :: Integer - , byronSecondsPerSlot :: Integer - , systemStart' :: SystemStart - , systemStart :: ExternalPosix.POSIXTime } - | Mainnet - { magicNumber :: Integer - , localNodeConnectInfo :: LocalNodeConnectInfo CardanoMode - , preShelleyEpochs :: Integer - , byronSlotsPerEpoch :: Integer - , byronSecondsPerSlot :: Integer - , systemStart' :: SystemStart - , systemStart :: ExternalPosix.POSIXTime } - - -getMainnetEnvironmment :: MonadIO m => Integer -> m Environment -getMainnetEnvironmment magicNumber = do +import GHC.Generics ( Generic ) + +data TokenomiaNetwork = + MainnetNetwork + | TestnetNetwork + | PreprodNetwork + | CustomNetwork CustomNetworkArgs + deriving stock (Show, Eq) + +data Environment = + Testnet + { magicNumber :: Integer + , localNodeConnectInfo :: LocalNodeConnectInfo CardanoMode + , preShelleyEpochs :: Integer + , byronSlotsPerEpoch :: Integer + , byronSecondsPerSlot :: Integer + , systemStart' :: SystemStart + , systemStart :: ExternalPosix.POSIXTime } + | Mainnet + { magicNumber :: Integer + , localNodeConnectInfo :: LocalNodeConnectInfo CardanoMode + , preShelleyEpochs :: Integer + , byronSlotsPerEpoch :: Integer + , byronSecondsPerSlot :: Integer + , systemStart' :: SystemStart + , systemStart :: ExternalPosix.POSIXTime } + + +networkMagicNumber :: TokenomiaNetwork -> Integer +networkMagicNumber = \case + MainnetNetwork -> readNetworkMagic Cardano.Api.Mainnet + TestnetNetwork -> 1097911063 + PreprodNetwork -> 1 + CustomNetwork args -> _magicNumber args + +data CustomNetworkArgs = CustomNetworkArgs + { _magicNumber :: Integer + , _epochSlots :: Integer + , _preShelleyEpochs :: Integer + , _byronSlotsPerEpoch :: Integer + , _byronSecondsPerSlot :: Integer + } + deriving stock (Generic, Show, Eq) + deriving anyclass (FromJSON) + +readCustomNetworkArgsFile :: FilePath -> IO (Either String CustomNetworkArgs) +readCustomNetworkArgsFile path = eitherDecode <$> readFile path + + +getNetworkEnvironment :: MonadIO m => TokenomiaNetwork -> m Environment +getNetworkEnvironment = \case + MainnetNetwork -> getMainnetEnvironment + TestnetNetwork -> getTestnetEnvironment + PreprodNetwork -> getPreprodEnvironment + CustomNetwork args -> getCustomEnvironment args + + +getMainnetEnvironment :: MonadIO m => m Environment +getMainnetEnvironment = do socketPath <- liftIO $ getEnv "CARDANO_NODE_SOCKET_PATH" - let localNodeConnectInfo = LocalNodeConnectInfo { - localConsensusModeParams = CardanoModeParams (EpochSlots 21600), - localNodeNetworkId = Shelley.Mainnet, - localNodeSocketPath = socketPath} + let magicNumber = readNetworkMagic Cardano.Api.Mainnet + localNodeConnectInfo = LocalNodeConnectInfo + { localConsensusModeParams = CardanoModeParams (EpochSlots 21600) + , localNodeNetworkId = Cardano.Api.Mainnet + , localNodeSocketPath = socketPath + } preShelleyEpochs = 208 byronSlotsPerEpoch = 21600 byronSecondsPerSlot = 20 @@ -83,13 +126,16 @@ getMainnetEnvironmment magicNumber = do return $ Mainnet {..} -getPreprodEnvironmment :: MonadIO m => Integer -> m Environment -getPreprodEnvironmment magicNumber = do + +getPreprodEnvironment :: MonadIO m => m Environment +getPreprodEnvironment = do socketPath <- liftIO $ getEnv "CARDANO_NODE_SOCKET_PATH" - let localNodeConnectInfo = LocalNodeConnectInfo { - localConsensusModeParams = CardanoModeParams (EpochSlots 21600), - localNodeNetworkId = Shelley.Testnet (NetworkMagic (fromIntegral magicNumber)), - localNodeSocketPath = socketPath} + let magicNumber = networkMagicNumber PreprodNetwork + localNodeConnectInfo = LocalNodeConnectInfo + { localConsensusModeParams = CardanoModeParams (EpochSlots 21600) + , localNodeNetworkId = Cardano.Api.Testnet (NetworkMagic (fromIntegral magicNumber)) + , localNodeSocketPath = socketPath + } preShelleyEpochs = 4 byronSlotsPerEpoch = 21600 byronSecondsPerSlot = 20 @@ -98,13 +144,16 @@ getPreprodEnvironmment magicNumber = do return $ Testnet {..} -getTestnetEnvironmment :: MonadIO m => Integer -> m Environment -getTestnetEnvironmment magicNumber = do + +getTestnetEnvironment :: MonadIO m => m Environment +getTestnetEnvironment = do socketPath <- liftIO $ getEnv "CARDANO_NODE_SOCKET_PATH" - let localNodeConnectInfo = LocalNodeConnectInfo { - localConsensusModeParams = CardanoModeParams (EpochSlots 21600), - localNodeNetworkId = Shelley.Testnet (NetworkMagic (fromIntegral magicNumber)), - localNodeSocketPath = socketPath} + let magicNumber = networkMagicNumber TestnetNetwork + localNodeConnectInfo = LocalNodeConnectInfo + { localConsensusModeParams = CardanoModeParams (EpochSlots 21600) + , localNodeNetworkId = Cardano.Api.Testnet (NetworkMagic (fromIntegral magicNumber)) + , localNodeSocketPath = socketPath + } preShelleyEpochs = 74 byronSlotsPerEpoch = 21600 byronSecondsPerSlot = 20 @@ -113,17 +162,27 @@ getTestnetEnvironmment magicNumber = do return $ Testnet {..} + +getCustomEnvironment :: MonadIO m => CustomNetworkArgs -> m Environment +getCustomEnvironment customNetworkArgs = do + socketPath <- liftIO $ getEnv "CARDANO_NODE_SOCKET_PATH" + let magicNumber = _magicNumber customNetworkArgs + localNodeConnectInfo = LocalNodeConnectInfo { + localConsensusModeParams = CardanoModeParams (EpochSlots (fromIntegral $ _epochSlots customNetworkArgs)), + localNodeNetworkId = Cardano.Api.Testnet (NetworkMagic (fromIntegral $ _magicNumber customNetworkArgs)), + localNodeSocketPath = socketPath} + preShelleyEpochs = _preShelleyEpochs customNetworkArgs + byronSlotsPerEpoch = _byronSlotsPerEpoch customNetworkArgs + byronSecondsPerSlot = _byronSecondsPerSlot customNetworkArgs + systemStart <- ExternalPosix.utcTimeToPOSIXSeconds . coerce <$> getSystemStart' localNodeConnectInfo + systemStart' <- getSystemStart' localNodeConnectInfo + + return $ Testnet {..} + + readNetworkMagic :: NetworkId -> Integer readNetworkMagic = read . show . unNetworkMagic . toNetworkMagic -getNetworkEnvironmment :: MonadIO m => NetworkId -> m Environment -getNetworkEnvironmment networkId = - (getMainnetOrTestnetEnvironmment networkId . readNetworkMagic) networkId - where - getMainnetOrTestnetEnvironmment :: MonadIO m => NetworkId -> (Integer -> m Environment) - getMainnetOrTestnetEnvironmment = \case - Cardano.Api.Mainnet -> getMainnetEnvironmment - Cardano.Api.Testnet _ -> getTestnetEnvironmment -- N.H : This is not neccessary because the transactions are handling PosixTime directly and not Slot -- as I was thinking initially... I won't delete the code from now, but it will be eventually... diff --git a/src/Tokenomia/Common/Error.hs b/src/Tokenomia/Common/Error.hs index 00f57f3..28eb59b 100644 --- a/src/Tokenomia/Common/Error.hs +++ b/src/Tokenomia/Common/Error.hs @@ -42,6 +42,7 @@ data TokenomiaError | MalformedAddress | InvalidPrivateSale String | QueryFailure String + | NetworkNotSupported String deriving stock Show whenNullThrow :: MonadError e m => e -> [a] -> m (NonEmpty a) diff --git a/src/Tokenomia/TokenDistribution/CLI.hs b/src/Tokenomia/TokenDistribution/CLI.hs index b6b79f8..b39da38 100644 --- a/src/Tokenomia/TokenDistribution/CLI.hs +++ b/src/Tokenomia/TokenDistribution/CLI.hs @@ -23,7 +23,7 @@ runCommand = execParser $ parser :: Parser Parameters parser = Parameters - <$> Parser.networkId + <$> Parser.network <*> Parser.distributionFilePath <*> Parser.recipientPerTx <*> Parser.tokenWallet diff --git a/src/Tokenomia/TokenDistribution/CLI/Parameters.hs b/src/Tokenomia/TokenDistribution/CLI/Parameters.hs index c74fe5b..f594c7b 100644 --- a/src/Tokenomia/TokenDistribution/CLI/Parameters.hs +++ b/src/Tokenomia/TokenDistribution/CLI/Parameters.hs @@ -3,12 +3,12 @@ module Tokenomia.TokenDistribution.CLI.Parameters ( Parameters(..) ) where -import Cardano.Api ( NetworkId ) +import Tokenomia.Common.Environment ( TokenomiaNetwork ) import Tokenomia.Wallet.Type ( WalletName ) data Parameters = Parameters - { networkId :: NetworkId + { network :: Either TokenomiaNetwork FilePath , distributionFilePath :: FilePath , recipientPerTx :: Int , tokenWallet :: WalletName diff --git a/src/Tokenomia/TokenDistribution/CLI/Parser.hs b/src/Tokenomia/TokenDistribution/CLI/Parser.hs index 7c24b20..204c5b0 100644 --- a/src/Tokenomia/TokenDistribution/CLI/Parser.hs +++ b/src/Tokenomia/TokenDistribution/CLI/Parser.hs @@ -5,14 +5,12 @@ module Tokenomia.TokenDistribution.CLI.Parser , dryRun , metadataFilePath , minLovelaces - , networkId + , network , recipientPerTx , tokenWallet , verbose ) where -import Cardano.Api ( NetworkId(Mainnet, Testnet), NetworkMagic(..) ) - import Control.Applicative ( (<|>) ) import Tokenomia.Wallet.Type ( WalletName ) @@ -32,24 +30,34 @@ import Options.Applicative , switch , value ) +import Tokenomia.Common.Environment ( TokenomiaNetwork(..) ) -networkId :: Parser NetworkId -networkId = mainnet <|> testnet +network :: Parser (Either TokenomiaNetwork FilePath) +network = mainnet <|> testnet <|> preprod <|> custom where - mainnet :: Parser NetworkId - mainnet = flag' Mainnet $ + mainnet :: Parser (Either TokenomiaNetwork FilePath) + mainnet = flag' (Left MainnetNetwork) $ short 'm' <> long "mainnet" <> help "Use the mainnet network" - testnet :: Parser NetworkId - testnet = fmap (Testnet . NetworkMagic) $ option auto $ + testnet :: Parser (Either TokenomiaNetwork FilePath) + testnet = flag' (Left TestnetNetwork) $ short 't' <> long "testnet" - <> help "Use this testnet magic id" - <> showDefault - <> value 1 - <> metavar "MAGIC" + <> help "Use the legacy testnet network" + + preprod :: Parser (Either TokenomiaNetwork FilePath) + preprod = flag' (Left PreprodNetwork) $ + short 'p' + <> long "preprod" + <> help "Use the test preprod network" + + custom :: Parser (Either TokenomiaNetwork FilePath) + custom = fmap Right $ strOption $ + long "custom-network-parameters-file" + <> help "Use a custom network" + <> metavar "FILENAME" distributionFilePath :: Parser FilePath distributionFilePath = strOption $ diff --git a/src/Tokenomia/TokenDistribution/Main.hs b/src/Tokenomia/TokenDistribution/Main.hs index ac0cfdf..16dfb25 100644 --- a/src/Tokenomia/TokenDistribution/Main.hs +++ b/src/Tokenomia/TokenDistribution/Main.hs @@ -1,4 +1,5 @@ {-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE LambdaCase #-} module Tokenomia.TokenDistribution.Main ( main @@ -17,7 +18,14 @@ import Tokenomia.Common.Error , whenNothingThrow ) -import Tokenomia.Common.Environment ( Environment, getNetworkEnvironmment ) +import Tokenomia.Common.Environment + ( CustomNetworkArgs + , Environment + , TokenomiaNetwork(..) + , getNetworkEnvironment + , readCustomNetworkArgsFile + ) +import Tokenomia.Common.Shell.Console ( printLn ) import Tokenomia.Wallet.ChildAddress.ChildAddressRef ( ChildAddressRef(..) ) import Tokenomia.Wallet.CLI ( selectBiggestStrictlyADAsNotCollateral ) @@ -36,13 +44,15 @@ import Tokenomia.TokenDistribution.Transfer ( transferTokenInParallel import Tokenomia.TokenDistribution.Wallet.ChildAddress.LocalRepository ( deriveMissingChildAddresses ) +import System.Directory.Internal.Prelude ( exitFailure ) import Tokenomia.TokenDistribution.Wallet.ChildAddress.ChildAddressRef ( maxChildAddressIndexRequired ) main :: IO () main = do parameters <- runCommand - environment <- getNetworkEnvironmment (networkId parameters) + tokenomiaNetwork <- liftIO $ selectNetwork (network parameters) + environment <- getNetworkEnvironment tokenomiaNetwork result <- runExceptT $ runReaderT (run parameters) @@ -107,3 +117,21 @@ step title computation = do result <- computation liftIO . putStr $ "\r[*] " <> title <> " done\n" return result + + +selectNetwork :: Either TokenomiaNetwork FilePath -> IO TokenomiaNetwork +selectNetwork = \case + Left tn | tn == MainnetNetwork -> pure MainnetNetwork + Left tn | tn == PreprodNetwork -> pure PreprodNetwork + Left tn | tn == TestnetNetwork -> pure TestnetNetwork + Right path -> CustomNetwork <$> inputCustomNetworkArgs path + Left _ -> printLn "An unexpected error occured : Unknown network" >> exitFailure + + +inputCustomNetworkArgs :: FilePath -> IO CustomNetworkArgs +inputCustomNetworkArgs path = do + readCustomNetworkArgsFile path >>= \case + Right args -> pure args + Left e -> do + printLn $ "Invalid custom network arguments :" <> e + exitFailure diff --git a/src/Tokenomia/TokenDistribution/Transfer.hs b/src/Tokenomia/TokenDistribution/Transfer.hs index c5c4317..6d0b9e4 100644 --- a/src/Tokenomia/TokenDistribution/Transfer.hs +++ b/src/Tokenomia/TokenDistribution/Transfer.hs @@ -1,6 +1,5 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RecordWildCards #-} - module Tokenomia.TokenDistribution.Transfer ( distributionOutputs , transferTokenInParallel @@ -8,7 +7,7 @@ module Tokenomia.TokenDistribution.Transfer import Control.Monad ( void ) import Control.Monad.Except ( MonadError ) -import Control.Monad.Reader ( MonadIO, MonadReader, liftIO ) +import Control.Monad.Reader ( MonadIO, MonadReader, asks, liftIO ) import Data.Functor.Syntax ( (<$$>) ) import Data.List.NonEmpty ( NonEmpty((:|)), fromList, toList, (<|) ) @@ -23,7 +22,7 @@ import Streamly.Prelude ( MonadAsync, drain, from import Tokenomia.Common.Address ( Address(..) ) import Tokenomia.Common.AssetClass ( adaAssetClass ) -import Tokenomia.Common.Environment ( Environment ) +import Tokenomia.Common.Environment ( Environment(magicNumber) ) import Tokenomia.Common.Error ( TokenomiaError ) import Tokenomia.Common.Data.Convertible ( convert ) @@ -46,13 +45,11 @@ import Tokenomia.Common.Transacting import Tokenomia.Wallet.ChildAddress.ChildAddressRef ( ChildAddressIndex(..), ChildAddressRef(..) ) import Tokenomia.TokenDistribution.Parser.Address ( unsafeSerialiseCardanoAddress ) - -import Tokenomia.TokenDistribution.Wallet.ChildAddress.ChildAddressRef ( defaultCollateralAddressRef ) - import Tokenomia.TokenDistribution.Wallet.ChildAddress.ChainIndex ( fetchProvisionedUTxO ) - +import Tokenomia.TokenDistribution.Wallet.ChildAddress.ChildAddressRef ( defaultCollateralAddressRef ) import Tokenomia.TokenDistribution.Wallet.ChildAddress.LocalRepository ( fetchAddressByWalletAtIndex ) +import Cardano.Api ( NetworkMagic(NetworkMagic), fromNetworkMagic ) transferTokenInParallel :: ( MonadIO m @@ -129,7 +126,9 @@ distributionOutputs :: , MonadReader Environment m ) => Parameters -> Distribution -> m (NonEmpty TxOut) -distributionOutputs Parameters{..} distribution@Distribution{..} = +distributionOutputs Parameters{..} distribution@Distribution{..} = do + magicN <- asks magicNumber + let networkId = fromNetworkMagic $ NetworkMagic $ fromIntegral magicN withChangeTxOut . fromList $ zipWith3 ToWallet (Address . convert . unsafeSerialiseCardanoAddress networkId . address <$> recipients) (addε . assetClassValue assetClass . amount <$> recipients) diff --git a/src/Tokenomia/Wallet/ChildAddress/LocalRepository.hs b/src/Tokenomia/Wallet/ChildAddress/LocalRepository.hs index e4b318d..21974b3 100644 --- a/src/Tokenomia/Wallet/ChildAddress/LocalRepository.hs +++ b/src/Tokenomia/Wallet/ChildAddress/LocalRepository.hs @@ -12,6 +12,7 @@ {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeApplications #-} +{-# OPTIONS_GHC -Wno-incomplete-patterns #-} {-# OPTIONS_GHC -fno-warn-missing-signatures #-} {-# OPTIONS_GHC -fno-warn-unused-top-binds #-} @@ -57,7 +58,7 @@ import Shh.Internal import Ledger.Crypto ( PubKeyHash ) -import Tokenomia.Common.Environment ( Environment(Mainnet, Testnet) ) +import Tokenomia.Common.Environment ( Environment(..), TokenomiaNetwork(..), networkMagicNumber ) import Tokenomia.Common.Address ( Address(..) ) @@ -305,48 +306,53 @@ generateChildAddressFile , MonadReader Environment m ) => ChildAddressRef -> ChildAddressFile -> m () generateChildAddressFile childAddressRef@ChildAddressRef{name,index = (ChildAddressIndex indexInteger)} fileType = do - let getFilePath = getChildAddressFilePath childAddressRef - netWorkTag <- asks (\case - Testnet {} -> "testnet" - Mainnet {} -> "mainnet") - - case fileType of - AddressTxt -> getFilePath AddressTxt >>= \path -> do - extendedPublicKeyTxtPath <- getFilePath ExtendedPublicKeyTxt - stakePublicKey <- getWalletFilePath name StakePublicKeyTxt >>= \stakeKeyPath -> C.unpack <$> liftIO (cat stakeKeyPath |> capture) - liftIO $ (cat extendedPublicKeyTxtPath - |> cardano_address "address" "payment" "--network-tag" netWorkTag - |> cardano_address "address" "delegation" stakePublicKey) - &> (Truncate . fromString) path - ExtendedPublicKeyJSON -> getFilePath ExtendedPublicKeyJSON >>= \path -> do - extendedPrivateKeyJSONPath <- getFilePath ExtendedPrivateKeyJSON - liftIO $ cardano_cli - "key" "verification-key" - "--signing-key-file" extendedPrivateKeyJSONPath - "--verification-key-file" path - ExtendedPublicKeyTxt -> getFilePath ExtendedPublicKeyTxt >>= \path -> do - extendedPrivateKeyTxtPath <- getFilePath ExtendedPrivateKeyTxt - liftIO $ (cat extendedPrivateKeyTxtPath |> cardano_address "key" "public" "--with-chain-code") - &> (Truncate . fromString) path - ExtendedPrivateKeyJSON -> getFilePath ExtendedPrivateKeyJSON >>= \path -> do - extendedPrivateKeyTxtPath <- getFilePath ExtendedPrivateKeyTxt - liftIO $ cardano_cli - "key" - "convert-cardano-address-key" - "--shelley-payment-key" - "--signing-key-file" extendedPrivateKeyTxtPath - "--out-file" path - ExtendedPrivateKeyTxt -> getFilePath ExtendedPrivateKeyTxt >>= \path -> do - let derivationPath = "1852H/1815H/0H/0/" <> show indexInteger - rootPrivateKeyTxtPath <- getWalletFilePath name RootPrivateKeyTxt - liftIO $ (cat rootPrivateKeyTxtPath - |> cardano_address "key" "child" derivationPath) - &> (Truncate . fromString) path - PubKeyHashTxt -> getFilePath PubKeyHashTxt >>= \path -> do - extendedPublicKeyJSONpATH <- getFilePath ExtendedPublicKeyJSON - liftIO $ cardano_cli - "address" - "key-hash" - "--payment-verification-key-file" extendedPublicKeyJSONpATH + let getFilePath = getChildAddressFilePath childAddressRef + netWorkTag <- asks + (\case + Mainnet {} + -> "mainnet" + Testnet {..} | magicNumber == networkMagicNumber TestnetNetwork + -> "testnet" + Testnet {..} | magicNumber == networkMagicNumber PreprodNetwork + -> "preprod" + ) + case fileType of + AddressTxt -> getFilePath AddressTxt >>= \path -> do + extendedPublicKeyTxtPath <- getFilePath ExtendedPublicKeyTxt + stakePublicKey <- getWalletFilePath name StakePublicKeyTxt >>= \stakeKeyPath -> C.unpack <$> liftIO (cat stakeKeyPath |> capture) + liftIO $ (cat extendedPublicKeyTxtPath + |> cardano_address "address" "payment" "--network-tag" netWorkTag + |> cardano_address "address" "delegation" stakePublicKey) &> (Truncate . fromString) path + ExtendedPublicKeyJSON -> getFilePath ExtendedPublicKeyJSON >>= \path -> do + extendedPrivateKeyJSONPath <- getFilePath ExtendedPrivateKeyJSON + liftIO $ cardano_cli + "key" "verification-key" + "--signing-key-file" extendedPrivateKeyJSONPath + "--verification-key-file" path + ExtendedPublicKeyTxt -> getFilePath ExtendedPublicKeyTxt >>= \path -> do + extendedPrivateKeyTxtPath <- getFilePath ExtendedPrivateKeyTxt + liftIO $ (cat extendedPrivateKeyTxtPath |> cardano_address "key" "public" "--with-chain-code") + &> (Truncate . fromString) path + ExtendedPrivateKeyJSON -> getFilePath ExtendedPrivateKeyJSON >>= \path -> do + extendedPrivateKeyTxtPath <- getFilePath ExtendedPrivateKeyTxt + liftIO $ cardano_cli + "key" + "convert-cardano-address-key" + "--shelley-payment-key" + "--signing-key-file" extendedPrivateKeyTxtPath + "--out-file" path + ExtendedPrivateKeyTxt -> getFilePath ExtendedPrivateKeyTxt >>= \path -> do + let derivationPath = "1852H/1815H/0H/0/" <> show indexInteger + rootPrivateKeyTxtPath <- getWalletFilePath name RootPrivateKeyTxt + liftIO $ (cat rootPrivateKeyTxtPath + |> cardano_address "key" "child" derivationPath) + &> (Truncate . fromString) path + PubKeyHashTxt -> getFilePath PubKeyHashTxt >>= \path -> do + extendedPublicKeyJSONpATH <- getFilePath ExtendedPublicKeyJSON + liftIO $ cardano_cli + "address" + "key-hash" + "--payment-verification-key-file" extendedPublicKeyJSONpATH + &> (Truncate . fromString) path diff --git a/src/Tokenomia/Wallet/LocalRepository.hs b/src/Tokenomia/Wallet/LocalRepository.hs index c45c8dc..199dc22 100644 --- a/src/Tokenomia/Wallet/LocalRepository.hs +++ b/src/Tokenomia/Wallet/LocalRepository.hs @@ -11,6 +11,7 @@ {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeApplications #-} +{-# OPTIONS_GHC -Wno-incomplete-patterns #-} {-# OPTIONS_GHC -fno-warn-missing-signatures #-} {-# OPTIONS_GHC -fno-warn-unused-top-binds #-} @@ -39,7 +40,7 @@ import Shh.Internal import System.Directory ( doesDirectoryExist ) -import Tokenomia.Common.Environment ( Environment(Mainnet, Testnet) ) +import Tokenomia.Common.Environment ( Environment(..), TokenomiaNetwork(..), networkMagicNumber ) import Tokenomia.Common.Address ( Address(Address) ) import Tokenomia.Common.Folder ( Folder(..), getFolderPath ) @@ -120,30 +121,36 @@ generateWalletFile , MonadReader Environment m ) => WalletName -> WalletFile -> m () generateWalletFile name fileType = do - netWorkTag <- asks (\case - Testnet {} -> "testnet" - Mainnet {} -> "mainnet") - case fileType of - StakePublicKeyTxt -> do - rootPrivateKeyPath <- getWalletFilePath name RootPrivateKeyTxt - getWalletFilePath name StakePublicKeyTxt >>= \path -> - liftIO $ (cat rootPrivateKeyPath |> cardano_address "key" "child" "1852H/1815H/0H/2/0") - |> cardano_address "key" "public" "--with-chain-code" - &> (Truncate . fromString) path - StakeAddressTxt -> do - stakePublicKeyTxtPath <- getWalletFilePath name StakePublicKeyTxt - getWalletFilePath name StakeAddressTxt >>= \path -> - liftIO $ (cat stakePublicKeyTxtPath |> cardano_address "address" "stake" "--network-tag" netWorkTag) - &> (Truncate . fromString) path - RootPrivateKeyTxt -> do - mnemonicsPath <- getWalletFilePath name MnemonicsTxt - getWalletFilePath name RootPrivateKeyTxt >>= \path -> - liftIO $ (cat mnemonicsPath |> cardano_address "key" "from-recovery-phrase" "Shelley") - &> (Truncate . fromString) path - MnemonicsTxt -> - getWalletFilePath name MnemonicsTxt >>= \path -> - liftIO $ cardano_address "recovery-phrase" "generate" "--size" "24" - &> (Truncate . fromString) path + netWorkTag <- asks + (\case + Mainnet {} + -> "mainnet" + Testnet {..} | magicNumber == networkMagicNumber TestnetNetwork + -> "testnet" + Testnet {..} | magicNumber == networkMagicNumber PreprodNetwork + -> "preprod" + ) + case fileType of + StakePublicKeyTxt -> do + rootPrivateKeyPath <- getWalletFilePath name RootPrivateKeyTxt + getWalletFilePath name StakePublicKeyTxt >>= \path -> + liftIO $ (cat rootPrivateKeyPath |> cardano_address "key" "child" "1852H/1815H/0H/2/0") + |> cardano_address "key" "public" "--with-chain-code" + &> (Truncate . fromString) path + StakeAddressTxt -> do + stakePublicKeyTxtPath <- getWalletFilePath name StakePublicKeyTxt + getWalletFilePath name StakeAddressTxt >>= \path -> + liftIO $ (cat stakePublicKeyTxtPath |> cardano_address "address" "stake" "--network-tag" netWorkTag) + &> (Truncate . fromString) path + RootPrivateKeyTxt -> do + mnemonicsPath <- getWalletFilePath name MnemonicsTxt + getWalletFilePath name RootPrivateKeyTxt >>= \path -> + liftIO $ (cat mnemonicsPath |> cardano_address "key" "from-recovery-phrase" "Shelley") + &> (Truncate . fromString) path + MnemonicsTxt -> + getWalletFilePath name MnemonicsTxt >>= \path -> + liftIO $ cardano_address "recovery-phrase" "generate" "--size" "24" + &> (Truncate . fromString) path remove :: diff --git a/test/Spec/Tokenomia/Common/Time.hs b/test/Spec/Tokenomia/Common/Time.hs index 2b1ccd8..0b51701 100644 --- a/test/Spec/Tokenomia/Common/Time.hs +++ b/test/Spec/Tokenomia/Common/Time.hs @@ -21,7 +21,11 @@ import Tokenomia.CardanoApi.Arbitrary.Slot () import Tokenomia.CardanoApi.Arbitrary.Time () import Tokenomia.CardanoApi.Query ( querySlotToWallclock', queryWallclockToSlot' ) -import Tokenomia.Common.Environment ( Environment(..), getTestnetEnvironmment ) +import Tokenomia.Common.Environment + ( Environment + , TokenomiaNetwork(PreprodNetwork) + , getNetworkEnvironment + ) import Tokenomia.Common.Environment.Query ( evalQuery ) import Tokenomia.Common.Error ( TokenomiaError(QueryFailure) ) @@ -41,7 +45,7 @@ runTest :: runTest test = monadicIO $ do - env <- getTestnetEnvironmment 1 + env <- getNetworkEnvironment PreprodNetwork fromRight' <$> runExceptT (runReaderT test env) propertiesQueryWallclockToSlot' :: [TestTree] diff --git a/test/Spec/Tokenomia/Vesting/GenerateNative.hs b/test/Spec/Tokenomia/Vesting/GenerateNative.hs index 3ef2f97..d71d479 100644 --- a/test/Spec/Tokenomia/Vesting/GenerateNative.hs +++ b/test/Spec/Tokenomia/Vesting/GenerateNative.hs @@ -60,7 +60,11 @@ import Tokenomia.Common.Arbitrary.Wallet ( PaymentAddress(..), gen import Tokenomia.Common.Data.Convertible ( convert ) import Tokenomia.Common.Data.List.Extra ( transpose ) -import Tokenomia.Common.Environment ( Environment(..), getPreprodEnvironmment ) +import Tokenomia.Common.Environment + ( Environment + , TokenomiaNetwork(PreprodNetwork) + , getNetworkEnvironment + ) import Tokenomia.Common.Environment.Query ( evalQueryWithSystemStart ) import Tokenomia.Common.Error ( TokenomiaError ) import Tokenomia.Common.Time ( toNextBeginNominalDiffTime ) @@ -395,7 +399,7 @@ propertiesTrancheNativeScriptInfos = withMaxSuccess 1 $ mapSize (const 7) ( \(Restricted ps :: Restricted PrivateSale) -> monadicIO $ do - env <- getPreprodEnvironmment 1 + env <- getNetworkEnvironment PreprodNetwork validPrivateSale <- useValidAddresses ps and <$> traverse @@ -495,7 +499,7 @@ propertiesToDatabaseOutput = withMaxSuccess 1 $ mapSize (const 7) ( \(Restricted ps :: Restricted PrivateSale) -> monadicIO $ do - env <- getPreprodEnvironmment 1 + env <- getNetworkEnvironment PreprodNetwork validPrivateSale <- useValidAddresses ps let tranches = splitInTranches validPrivateSale runToDatabaseOutput env tranches @@ -524,7 +528,7 @@ propertiesToDistribution = withMaxSuccess 1 $ mapSize (const 7) ( \(Restricted ps :: Restricted PrivateSale) -> monadicIO $ do - env <- getPreprodEnvironmment 1 + env <- getNetworkEnvironment PreprodNetwork validPrivateSale <- useValidAddresses ps let tranches = splitInTranches validPrivateSale runToDistribution env tranches diff --git a/test/Spec/Tokenomia/Vesting/Sendings.hs b/test/Spec/Tokenomia/Vesting/Sendings.hs index 807f5bd..44b4c3a 100644 --- a/test/Spec/Tokenomia/Vesting/Sendings.hs +++ b/test/Spec/Tokenomia/Vesting/Sendings.hs @@ -144,6 +144,9 @@ invalidInputTests = [AdaAmount 10000000] Nothing 0 + False + Nothing + Nothing ] -- Missing Tx hash tests @@ -183,6 +186,9 @@ validTxHashTests = [AdaAmount 10000000] Nothing 0 + False + Nothing + Nothing ] -- Malformed address @@ -244,6 +250,9 @@ valueCheckTests = ] Nothing 0 + False + Nothing + Nothing ] wxyzOutUtxos = [ UtxoOutput @@ -251,6 +260,9 @@ valueCheckTests = [AdaAmount 10000000] Nothing 0 + False + Nothing + Nothing ] testAddress :: Text