From 14c29d31fb5ef5b068df06cd338c72470ae2850a Mon Sep 17 00:00:00 2001 From: Nicolas Henin Date: Mon, 8 Nov 2021 15:51:32 +0100 Subject: [PATCH 1/5] [ico.funds.reception] implemented fetchAddressState and plan --- app/Main.hs | 2 - cabal.project | 11 ++ nix/pkgs/haskell/haskell.nix | 1 + .../Adapter/Cardano/CLI/Transaction.hs | 2 +- src/Tokenomia/Adapter/Cardano/CLI/UTxO.hs | 1 + src/Tokenomia/Adapter/Cardano/Types.hs | 4 +- src/Tokenomia/CLI.hs | 8 +- .../ICO/{ => Funds/Emission}/HappyPath.hs | 4 +- .../ICO/Funds/{ => Emission}/Simulation.hs | 8 +- .../ICO/{ => Funds/Emission}/SpecialCases.hs | 8 +- .../Emission}/SpecialCases/MultiUTxOs.hs | 2 +- .../Emission}/SpecialCases/Multisig.hs | 5 +- .../Emission}/SpecialCases/NativeTokens.hs | 2 +- src/Tokenomia/ICO/Funds/Reception/Plan.hs | 77 ++++++++++ .../ICO/Funds/Reception/Simulation.hs | 74 +++++++++ src/Tokenomia/ICO/Funds/Reception/State.hs | 102 +++++++++++++ src/Tokenomia/ICO/Funds/Reception/Transact.hs | 28 ++++ src/Tokenomia/ICO/Funds/Reception/Types.hs | 144 ++++++++++++++++++ tokenomia.cabal | 28 +++- 19 files changed, 485 insertions(+), 26 deletions(-) rename src/Tokenomia/ICO/{ => Funds/Emission}/HappyPath.hs (97%) rename src/Tokenomia/ICO/Funds/{ => Emission}/Simulation.hs (92%) rename src/Tokenomia/ICO/{ => Funds/Emission}/SpecialCases.hs (97%) rename src/Tokenomia/ICO/{ => Funds/Emission}/SpecialCases/MultiUTxOs.hs (96%) rename src/Tokenomia/ICO/{ => Funds/Emission}/SpecialCases/Multisig.hs (97%) rename src/Tokenomia/ICO/{ => Funds/Emission}/SpecialCases/NativeTokens.hs (95%) create mode 100644 src/Tokenomia/ICO/Funds/Reception/Plan.hs create mode 100644 src/Tokenomia/ICO/Funds/Reception/Simulation.hs create mode 100644 src/Tokenomia/ICO/Funds/Reception/State.hs create mode 100644 src/Tokenomia/ICO/Funds/Reception/Transact.hs create mode 100644 src/Tokenomia/ICO/Funds/Reception/Types.hs diff --git a/app/Main.hs b/app/Main.hs index a0bbe151..9b41bf60 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -1,7 +1,5 @@ module Main (main) where - - import qualified Tokenomia.CLI as Tokenomia main :: IO () diff --git a/cabal.project b/cabal.project index 5d698051..c2797526 100644 --- a/cabal.project +++ b/cabal.project @@ -243,3 +243,14 @@ source-repository-package type: git location: https://github.com/input-output-hk/goblins tag: cde90a2b27f79187ca8310b6549331e59595e7ba + + +source-repository-package + type: git + location: https://github.com/smart-chain-fr/blockfrost-haskell + tag: 175cf5da3c173f89dbeec06c64d136be215d2439 + subdir: + blockfrost-client + blockfrost-api + blockfrost-client-core + blockfrost-pretty \ No newline at end of file diff --git a/nix/pkgs/haskell/haskell.nix b/nix/pkgs/haskell/haskell.nix index 09502dfa..fda6c8cd 100644 --- a/nix/pkgs/haskell/haskell.nix +++ b/nix/pkgs/haskell/haskell.nix @@ -36,6 +36,7 @@ let "https://github.com/input-output-hk/hedgehog-extras"."edf6945007177a638fbeb8802397f3a6f4e47c14" = "0wc7qzkc7j4ns2rz562h6qrx2f8xyq7yjcb7zidnj7f6j0pcd0i9"; "https://github.com/input-output-hk/cardano-wallet"."ae7569293e94241ef6829139ec02bd91abd069df" = "1mv1dhpkdj9ridm1fvq6jc85qs6zvbp172228rq72gyawjwrgvi6"; "https://github.com/input-output-hk/cardano-addresses"."d2f86caa085402a953920c6714a0de6a50b655ec" = "0p6jbnd7ky2yf7bwb1350k8880py8dgqg39k49q02a6ij4ld01ay"; + "https://github.com/smart-chain-fr/blockfrost-haskell"."175cf5da3c173f89dbeec06c64d136be215d2439" = "0x0529rl99jsvkcy28m7bg5lq2z7yjj8pbcc0yz1lkfccnmpv7g1"; }; modules = [ diff --git a/src/Tokenomia/Adapter/Cardano/CLI/Transaction.hs b/src/Tokenomia/Adapter/Cardano/CLI/Transaction.hs index 62f4b9bc..9f56fc2b 100644 --- a/src/Tokenomia/Adapter/Cardano/CLI/Transaction.hs +++ b/src/Tokenomia/Adapter/Cardano/CLI/Transaction.hs @@ -8,9 +8,9 @@ {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE RecordWildCards #-} {-# OPTIONS_GHC -fno-warn-missing-signatures #-} {-# OPTIONS_GHC -fno-warn-unused-top-binds #-} -{-# LANGUAGE RecordWildCards #-} module Tokenomia.Adapter.Cardano.CLI.Transaction diff --git a/src/Tokenomia/Adapter/Cardano/CLI/UTxO.hs b/src/Tokenomia/Adapter/Cardano/CLI/UTxO.hs index 0345709d..80a997e9 100644 --- a/src/Tokenomia/Adapter/Cardano/CLI/UTxO.hs +++ b/src/Tokenomia/Adapter/Cardano/CLI/UTxO.hs @@ -16,6 +16,7 @@ module Tokenomia.Adapter.Cardano.CLI.UTxO , containingStrictlyADAs , containsCollateral , showValue + , showTxOutRef ) where import Tokenomia.Common.Shell.InteractiveMenu diff --git a/src/Tokenomia/Adapter/Cardano/Types.hs b/src/Tokenomia/Adapter/Cardano/Types.hs index aa72c652..702807d5 100644 --- a/src/Tokenomia/Adapter/Cardano/Types.hs +++ b/src/Tokenomia/Adapter/Cardano/Types.hs @@ -3,7 +3,9 @@ {-# LANGUAGE GeneralizedNewtypeDeriving #-} module Tokenomia.Adapter.Cardano.Types (Address (..), Hash(..)) where -newtype Address = Address String deriving stock (Eq) +import Data.String (IsString) +newtype Address = Address String deriving stock (Eq,Ord) deriving newtype Show + deriving newtype IsString newtype Hash = Hash String deriving (Show,Eq) \ No newline at end of file diff --git a/src/Tokenomia/CLI.hs b/src/Tokenomia/CLI.hs index 868e4381..11d0c6b4 100644 --- a/src/Tokenomia/CLI.hs +++ b/src/Tokenomia/CLI.hs @@ -39,7 +39,7 @@ import qualified Tokenomia.Vesting.Vest as Vesting import qualified Tokenomia.Vesting.Retrieve as Vesting import qualified Tokenomia.Node.Status as Node - +import qualified Tokenomia.ICO.Funds.Reception.Simulation as ICO import Tokenomia.Adapter.Cardano.CLI.Transaction @@ -136,6 +136,7 @@ runAction = \case WalletCollateral -> Wallet.createCollateral WalletRestore -> Wallet.restore WalletRemove -> Wallet.remove + ICOReceptionPlanList -> ICO.simulatePlan TokenMint -> Token.mint TokenBurn -> Token.burn TokenTransfer -> Token.transfer @@ -157,7 +158,8 @@ actions = NonEmpty.fromList [ AdaTransfer, VestingVestFunds, VestingRetrieveFunds, - NodeStatus + NodeStatus, + ICOReceptionPlanList ] data Action @@ -173,6 +175,7 @@ data Action | VestingVestFunds | VestingRetrieveFunds | NodeStatus + | ICOReceptionPlanList instance DisplayMenuItem Action where displayMenuItem item = case item of @@ -188,5 +191,6 @@ instance DisplayMenuItem Action where VestingVestFunds -> "[Vesting] - Vest Funds" VestingRetrieveFunds -> "[Vesting] - Retrieve Funds" NodeStatus -> "[Node] - Status" + ICOReceptionPlanList -> "[ICO] - List Reception Funds Plans from a Stake Address" diff --git a/src/Tokenomia/ICO/HappyPath.hs b/src/Tokenomia/ICO/Funds/Emission/HappyPath.hs similarity index 97% rename from src/Tokenomia/ICO/HappyPath.hs rename to src/Tokenomia/ICO/Funds/Emission/HappyPath.hs index d870a3f3..c438ab74 100644 --- a/src/Tokenomia/ICO/HappyPath.hs +++ b/src/Tokenomia/ICO/Funds/Emission/HappyPath.hs @@ -4,7 +4,9 @@ {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} -module Tokenomia.ICO.HappyPath ( happyPath ) where +{-# OPTIONS_GHC -fno-warn-missing-signatures #-} +{-# OPTIONS_GHC -fno-warn-unused-top-binds #-} +module Tokenomia.ICO.Funds.Emission.HappyPath ( happyPath ) where import System.Random import Control.Concurrent.Lifted import Control.Monad.Reader diff --git a/src/Tokenomia/ICO/Funds/Simulation.hs b/src/Tokenomia/ICO/Funds/Emission/Simulation.hs similarity index 92% rename from src/Tokenomia/ICO/Funds/Simulation.hs rename to src/Tokenomia/ICO/Funds/Emission/Simulation.hs index 6e1eabdd..dc0319a2 100644 --- a/src/Tokenomia/ICO/Funds/Simulation.hs +++ b/src/Tokenomia/ICO/Funds/Emission/Simulation.hs @@ -5,7 +5,7 @@ {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeApplications #-} -module Tokenomia.ICO.Funds.Simulation ( simulation ) where +module Tokenomia.ICO.Funds.Emission.Simulation ( simulation ) where import Control.Monad.Reader import Control.Monad.Except import Data.Coerce ( coerce ) @@ -21,8 +21,8 @@ import Tokenomia.Adapter.Cardano.CLI.UTxO import Tokenomia.Adapter.Cardano.CLI.Transaction import Tokenomia.Adapter.Cardano.CLI.Environment -import Tokenomia.ICO.HappyPath ( happyPath ) -import Tokenomia.ICO.SpecialCases ( specialCases ) +import Tokenomia.ICO.Funds.Emission.HappyPath ( happyPath ) +import Tokenomia.ICO.Funds.Emission.SpecialCases ( specialCases ) simulation :: IO () @@ -37,7 +37,7 @@ simulation = do let a = runExceptT $ runReaderT setup environment b = runExceptT $ runReaderT (specialCases (coerce "addr_test1vzzj78m4jw2sudver47u3ad9v92n3kzeusqdk2ply32znysqx0835") 10000000) environment c = runExceptT $ runReaderT happyPath environment - result <- sequence [a,b,c] + sequence_ [a,b,c] printLn "#############################" diff --git a/src/Tokenomia/ICO/SpecialCases.hs b/src/Tokenomia/ICO/Funds/Emission/SpecialCases.hs similarity index 97% rename from src/Tokenomia/ICO/SpecialCases.hs rename to src/Tokenomia/ICO/Funds/Emission/SpecialCases.hs index 009fe45b..7ad02b5d 100644 --- a/src/Tokenomia/ICO/SpecialCases.hs +++ b/src/Tokenomia/ICO/Funds/Emission/SpecialCases.hs @@ -1,6 +1,6 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE FlexibleContexts #-} -module Tokenomia.ICO.SpecialCases where +module Tokenomia.ICO.Funds.Emission.SpecialCases where import Control.Concurrent import qualified Data.ByteString.UTF8 as BSU import qualified Ledger.Value as L @@ -18,9 +18,9 @@ import Tokenomia.Adapter.Cardano.CLI.Environment ( Environment ) import Tokenomia.Adapter.Cardano.CLI.UTxO.Query (queryUTxOsFilterBy) import Tokenomia.Adapter.Cardano.CLI.UTxO -import Tokenomia.ICO.SpecialCases.Multisig (multisigOneUTxO, multisigTwoUTxOs) -import Tokenomia.ICO.SpecialCases.MultiUTxOs ( multiUTxOsSameAddress ) -import Tokenomia.ICO.SpecialCases.NativeTokens ( sendTokensAndADAs ) +import Tokenomia.ICO.Funds.Emission.SpecialCases.Multisig (multisigOneUTxO, multisigTwoUTxOs) +import Tokenomia.ICO.Funds.Emission.SpecialCases.MultiUTxOs ( multiUTxOsSameAddress ) +import Tokenomia.ICO.Funds.Emission.SpecialCases.NativeTokens ( sendTokensAndADAs ) specialCases :: diff --git a/src/Tokenomia/ICO/SpecialCases/MultiUTxOs.hs b/src/Tokenomia/ICO/Funds/Emission/SpecialCases/MultiUTxOs.hs similarity index 96% rename from src/Tokenomia/ICO/SpecialCases/MultiUTxOs.hs rename to src/Tokenomia/ICO/Funds/Emission/SpecialCases/MultiUTxOs.hs index 3802978b..01a871ac 100644 --- a/src/Tokenomia/ICO/SpecialCases/MultiUTxOs.hs +++ b/src/Tokenomia/ICO/Funds/Emission/SpecialCases/MultiUTxOs.hs @@ -1,6 +1,6 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RecordWildCards #-} -module Tokenomia.ICO.SpecialCases.MultiUTxOs +module Tokenomia.ICO.Funds.Emission.SpecialCases.MultiUTxOs ( multiUTxOsSameAddress ) where import Data.List.NonEmpty ( NonEmpty((:|)) ) diff --git a/src/Tokenomia/ICO/SpecialCases/Multisig.hs b/src/Tokenomia/ICO/Funds/Emission/SpecialCases/Multisig.hs similarity index 97% rename from src/Tokenomia/ICO/SpecialCases/Multisig.hs rename to src/Tokenomia/ICO/Funds/Emission/SpecialCases/Multisig.hs index 255889c7..63ffa8ee 100644 --- a/src/Tokenomia/ICO/SpecialCases/Multisig.hs +++ b/src/Tokenomia/ICO/Funds/Emission/SpecialCases/Multisig.hs @@ -3,8 +3,9 @@ {-# LANGUAGE ExtendedDefaultRules #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RecordWildCards #-} - -module Tokenomia.ICO.SpecialCases.Multisig +{-# OPTIONS_GHC -fno-warn-missing-signatures #-} +{-# OPTIONS_GHC -fno-warn-unused-top-binds #-} +module Tokenomia.ICO.Funds.Emission.SpecialCases.Multisig ( multisigOneUTxO , multisigTwoUTxOs ) where diff --git a/src/Tokenomia/ICO/SpecialCases/NativeTokens.hs b/src/Tokenomia/ICO/Funds/Emission/SpecialCases/NativeTokens.hs similarity index 95% rename from src/Tokenomia/ICO/SpecialCases/NativeTokens.hs rename to src/Tokenomia/ICO/Funds/Emission/SpecialCases/NativeTokens.hs index c586a14d..6c7f5a58 100644 --- a/src/Tokenomia/ICO/SpecialCases/NativeTokens.hs +++ b/src/Tokenomia/ICO/Funds/Emission/SpecialCases/NativeTokens.hs @@ -1,6 +1,6 @@ {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE FlexibleContexts #-} -module Tokenomia.ICO.SpecialCases.NativeTokens ( sendTokensAndADAs ) where +module Tokenomia.ICO.Funds.Emission.SpecialCases.NativeTokens ( sendTokensAndADAs ) where import Prelude hiding ((+),(-)) import PlutusTx.Prelude (AdditiveSemigroup((+)),AdditiveGroup((-))) import Data.List.NonEmpty ( NonEmpty((:|)) ) diff --git a/src/Tokenomia/ICO/Funds/Reception/Plan.hs b/src/Tokenomia/ICO/Funds/Reception/Plan.hs new file mode 100644 index 00000000..e6f8dec0 --- /dev/null +++ b/src/Tokenomia/ICO/Funds/Reception/Plan.hs @@ -0,0 +1,77 @@ +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE TupleSections #-} +{-# LANGUAGE NumericUnderscores #-} +{-# LANGUAGE NamedFieldPuns #-} +module Tokenomia.ICO.Funds.Reception.Plan + (plan) where +import Prelude hiding (round,print) + +import Data.Set.Ordered + +import Plutus.V1.Ledger.Ada +import Ledger.Ada as Ada +import Plutus.V1.Ledger.Interval as I + +import Tokenomia.ICO.Funds.Reception.Types + + +plan :: RoundSettings -> AddressFundsState -> AddressFundsPlan +plan round AddressFundsState {..} + = let State {commands} = foldr + transition + State {commands = empty,.. } + transactions + in Plan + { references = references + , commands = commands} + +data State = State + { round :: RoundSettings + , volumes :: AddressVolumes + , commands :: OSet Command} + +transition :: FundsTransaction -> State -> State +transition i@FundsTransaction {funds = Left _} State {..} + = addCommand $ Reject i FundsWithNativeTokens + -- Here : only ADAs + where + addCommand command = State { commands = command |< commands , .. } +transition + i@FundsTransaction {funds = Right adas, ..} + State { round = round@RoundSettings {..} , volumes = volumes@AddressVolumes {..} ,..} + | notBelongToRoundTimeRange = addCommand $ Reject i TransactionOutofRoundTimeRange + -- Here : only ADAs | transaction slot ∈ time range + | fundsBelowMinimumRequired = addCommand $ Reject i InsufficientFundsReceived + -- Here : only ADAs | transaction slot ∈ time range | minimum < funds + | adressAlreadySaturated = addCommand $ Reject i AddressSaturated + -- Here : only ADAs | transaction slot ∈ time range | minimum < funds | in a non already saturated address + | adressSaturatedWithIncomingFund = addCommand $ AcceptWithPartialRefund i fundsOverSaturation + -- Here : only ADAs | transaction slot ∈ time range | minimum < funds | in a non saturated address with incoming fund + | notBelongToFundRange = addCommand $ AcceptWithPartialRefund i (fundsOverMaximumRequired fundRange) + -- Here : only ADAs | transaction slot ∈ time range | funds ∈ fundsRange | in a non saturated address with incoming fund + | otherwise = addCommand $ Accept i + where + + notBelongToRoundTimeRange = not $ I.member transactionSlot timeRange + notBelongToFundRange = not $ I.member (Ada.lovelaceOf (fromIntegral adas)) fundRange + fundsBelowMinimumRequired = before (Ada.lovelaceOf (fromIntegral adas)) fundRange + adressAlreadySaturated = maximumAdaPerAdress < (sent + accumulatedFundsToSent) + adressSaturatedWithIncomingFund = maximumAdaPerAdress < sent + accumulatedFundsToSent + sumAdaFunds [i] + accumulatedFundsToSent = sumAdaFunds (transaction <$> toAscList commands) + fundsOverSaturation = sent + accumulatedFundsToSent + sumAdaFunds [i] - maximumAdaPerAdress + fundsOverMaximumRequired (Interval _ (UpperBound (Finite maximumAdaPerTx) _) ) = Ada.lovelaceOf (fromIntegral adas) - maximumAdaPerTx + fundsOverMaximumRequired _ = Ada.lovelaceOf 0 + addCommand command = State { commands = command |< commands , .. } + + + +sumAdaFunds :: [FundsTransaction] -> Ada +sumAdaFunds [] = Ada.lovelaceOf 0 +sumAdaFunds (FundsTransaction {funds = Left _} : xs) = sumAdaFunds xs +sumAdaFunds (FundsTransaction {funds = Right x} : xs) = x + sumAdaFunds xs + diff --git a/src/Tokenomia/ICO/Funds/Reception/Simulation.hs b/src/Tokenomia/ICO/Funds/Reception/Simulation.hs new file mode 100644 index 00000000..e5d2b23f --- /dev/null +++ b/src/Tokenomia/ICO/Funds/Reception/Simulation.hs @@ -0,0 +1,74 @@ +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE TupleSections #-} +{-# LANGUAGE NumericUnderscores #-} +{-# LANGUAGE NamedFieldPuns #-} +module Tokenomia.ICO.Funds.Reception.Simulation + (simulatePlan) where + +import Prelude hiding (round,print) +import Control.Monad.Reader + + + +import Data.List (intersperse) +import Plutus.V1.Ledger.Ada + +import qualified Blockfrost.Client as B + +import Tokenomia.Adapter.Cardano.CLI.Transaction + +import Tokenomia.Common.Shell.Console (printLn) + +import Data.Foldable +import Plutus.V1.Ledger.Interval + +import Tokenomia.ICO.Funds.Reception.Types +import Tokenomia.ICO.Funds.Reception.State +import Tokenomia.ICO.Funds.Reception.Plan +import Tokenomia.Common.Shell.InteractiveMenu (askString) +import Data.Set.Ordered hiding (null) + +simulatePlan + :: ( MonadIO m) + => m () +simulatePlan = do + let round = RoundSettings + { maximumAdaPerAdress = adaOf 50 + , fundRange = interval (adaOf 2) (adaOf 1000) + , timeRange = interval 40354960 43002258 } + address <- Address <$> askString "- Plan Simulation on address : " + + prj <- liftIO B.projectFromEnv + res <- liftIO $ B.runBlockfrost prj $ fetchStateFromStakeAddress address + printLn "Round Context : " + printLn $ show round + printLn "--------------------------------------" + case res of + Right addressFundsStates-> do + let statesAndplans = (\a -> (a,plan round a)) <$> addressFundsStates + sequence_ $ uncurry displayStateAndPlan <$> statesAndplans + Left err -> + printLn $ show err + printLn "--------------------------------------" + +displayStateAndPlan :: ( MonadIO m) => AddressFundsState -> AddressFundsPlan -> m () +displayStateAndPlan AddressFundsState {transactions,volumes} Plan {references,commands} | not (null transactions) + = do + printLn $ show references + printLn $ "- Volumes :" <> show volumes + printLn $ "- Transactions not consumed : " <> (show . length) transactions + printLn $ fold (intersperse "\n" (show <$> toAscList transactions)) + printLn "- Plan :" + printLn $ fold (intersperse "\n" (show <$> toAscList commands)) + printLn "---------" +displayStateAndPlan _ Plan {..} + = do + printLn $ show references + printLn "> no transactions to consume" + printLn "---------" + \ No newline at end of file diff --git a/src/Tokenomia/ICO/Funds/Reception/State.hs b/src/Tokenomia/ICO/Funds/Reception/State.hs new file mode 100644 index 00000000..b2393a1d --- /dev/null +++ b/src/Tokenomia/ICO/Funds/Reception/State.hs @@ -0,0 +1,102 @@ +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE TupleSections #-} +{-# LANGUAGE NumericUnderscores #-} +module Tokenomia.ICO.Funds.Reception.State + (fetchState,fetchStateFromStakeAddress) where + +import Prelude hiding (round,print) + + +import Control.Monad.Reader +import Data.List (group,sort) +import Plutus.V1.Ledger.Ada + +import qualified Blockfrost.Client as B + +import Tokenomia.Adapter.Cardano.CLI.Transaction + +import Ledger.Ada as Ada +import qualified Data.Text as T + +import Blockfrost.Types.Shared.Amount +import Data.String +import Data.Foldable +import Ledger ( Slot(..), TxOutRef(..) ) +import Data.Coerce +import Data.Set.Ordered + +import Tokenomia.ICO.Funds.Reception.Types + + +fetchStateFromStakeAddress + :: Address + -> B.BlockfrostClient [AddressFundsState] +fetchStateFromStakeAddress (Address addr) = do + addresses <- B.getAccountAssociatedAddresses (fromString addr) + mapM fetchState (Address . T.unpack . coerce <$> addresses) + + + +fetchState + :: Address + -> B.BlockfrostClient AddressFundsState +fetchState address = do + references <- fetchAddressGenerationIndex address + volumes <- fetchAddressVolumes address + transactions <- fetchInvestorContexts address + pure AddressFundsState {..} + + +fetchAddressGenerationIndex :: MonadIO m => Address -> m AddressRef +fetchAddressGenerationIndex address = return AddressRef {generationIndex = AddressGenerationIndex 0,..} + +fetchInvestorContexts :: Address -> B.BlockfrostClient (OSet FundsTransaction) +fetchInvestorContexts addr = fromList <$> (fmap .fmap) (\(a,b,c) -> mkFundsTransaction a b c ) (fetchUTxOsDetails addr) + + +fetchUTxOsDetails :: Address -> B.BlockfrostClient [(B.AddressUtxo ,B.Transaction,B.TransactionUtxos)] +fetchUTxOsDetails (Address addr) = do + utxos <- B.getAddressUtxos (fromString addr) + mapM(\x@B.AddressUtxo {_addressUtxoTxHash = hash} -> do + transaction <- B.getTx hash + transactionUtxos <- B.getTxUtxos hash + return (x,transaction,transactionUtxos)) utxos + +mkFundsTransaction :: B.AddressUtxo -> B.Transaction -> B.TransactionUtxos -> FundsTransaction +mkFundsTransaction + B.AddressUtxo {..} + B.Transaction {..} + B.TransactionUtxos {..} = + FundsTransaction { txOutRef = TxOutRef { + txOutRefId = fromString . T.unpack . coerce $_addressUtxoTxHash, + txOutRefIdx = _addressUtxoOutputIndex } + , inputAddresses = rmdups $ Address . T.unpack . coerce . B._utxoInputAddress <$> _transactionUtxosInputs + , transactionSlot = (Slot . coerce) _transactionSlot + , funds = splitNativeTokensAndAdas _addressUtxoAmount} + +splitNativeTokensAndAdas :: [Amount] -> Either NativeTokens Ada +splitNativeTokensAndAdas [AdaAmount x] = Right $ Ada.lovelaceOf (fromIntegral x) +splitNativeTokensAndAdas x = Left x + +rmdups :: (Ord a) => [a] -> [a] +rmdups = Prelude.map Prelude.head . group . sort + + +fetchAddressVolumes :: Address -> B.BlockfrostClient AddressVolumes +fetchAddressVolumes (Address addr) = do + B.AddressDetails {..} <- B.getAddressDetails (fromString addr) + return AddressVolumes { received = fold (filterOnlyLovelaces <$> _addressDetailsReceivedSum) + , sent = fold (filterOnlyLovelaces <$> _addressDetailsSentSum)} + + + + +filterOnlyLovelaces :: Amount -> Ada +filterOnlyLovelaces (AdaAmount x) = Ada.lovelaceOf (fromIntegral x) +filterOnlyLovelaces (AssetAmount _) = Ada.lovelaceOf 0 + diff --git a/src/Tokenomia/ICO/Funds/Reception/Transact.hs b/src/Tokenomia/ICO/Funds/Reception/Transact.hs new file mode 100644 index 00000000..da9c1727 --- /dev/null +++ b/src/Tokenomia/ICO/Funds/Reception/Transact.hs @@ -0,0 +1,28 @@ +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE TupleSections #-} +{-# LANGUAGE NumericUnderscores #-} +{-# LANGUAGE NamedFieldPuns #-} +module Tokenomia.ICO.Funds.Reception.Transact + (transact) where + +import Prelude hiding (round,print) +import Control.Monad.Reader hiding (ask) +import Control.Monad.Except +import Tokenomia.Adapter.Cardano.CLI.Environment +import Tokenomia.Adapter.Cardano.CLI.Transaction + +import Tokenomia.ICO.Funds.Reception.Types + + +transact + :: ( MonadIO m + , MonadReader Environment m + , MonadError BuildingTxError m) + => AddressFundsPlan -> m () +transact _ + = return () -- Todo diff --git a/src/Tokenomia/ICO/Funds/Reception/Types.hs b/src/Tokenomia/ICO/Funds/Reception/Types.hs new file mode 100644 index 00000000..fb680272 --- /dev/null +++ b/src/Tokenomia/ICO/Funds/Reception/Types.hs @@ -0,0 +1,144 @@ +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE TupleSections #-} +{-# LANGUAGE NumericUnderscores #-} +module Tokenomia.ICO.Funds.Reception.Types + ( RoundSettings (..) + -- Address + , AddressRef (..) + , AddressGenerationIndex (..) + , AddressVolumes (..) + , AddressFundsState (..) + , FundsTransaction (..) + , AddressFundsPlan (..) + , RejectReason (..) + , Command (..) + , NativeTokens + , Funds + , ) where + +import Prelude hiding (round,print) + + + +import Data.List (intersperse) +import Data.Set.Ordered +import Plutus.V1.Ledger.Ada + +import Tokenomia.Adapter.Cardano.CLI.Transaction + +import Tokenomia.Adapter.Cardano.CLI.UTxO +import Ledger.Ada as Ada +import qualified Data.Text as T + +import Blockfrost.Types.Shared.Amount +import Data.Foldable +import Blockfrost.Pretty.Ada +import Ledger ( Slot(..), TxOutRef(..) ) +import Data.Coerce +import qualified Money +import Plutus.V1.Ledger.Interval + +import Data.Text.Prettyprint.Doc (pretty) + + +data RoundSettings + = RoundSettings + { timeRange :: Interval Slot + , maximumAdaPerAdress :: Ada + , fundRange :: Interval Ada} + +instance Show RoundSettings where + show RoundSettings { .. } + = "\n | time range = " <> (show . pretty) timeRange + <> "\n | fund range = " <> (show . pretty) fundRange + + +newtype AddressGenerationIndex = AddressGenerationIndex Integer + +data AddressVolumes = AddressVolumes + { received :: Ada + , sent :: Ada} + +data AddressRef = AddressRef {generationIndex :: AddressGenerationIndex, address :: Address} + +instance Show AddressRef where + show AddressRef {generationIndex = AddressGenerationIndex x,..} + = show x <> "#" <> coerce address + +instance Show AddressVolumes where + show AddressVolumes {received = Lovelace x, sent = Lovelace y } + = "\n | received = " <> T.unpack (prettyLovelaces (fromIntegral x)) + <> "\n | sent = " <> T.unpack (prettyLovelaces (fromIntegral y)) + <> "\n | balance = " <> T.unpack (prettyLovelaces (fromIntegral $ x-y)) + +data AddressFundsState = AddressFundsState {references :: AddressRef , volumes :: AddressVolumes, transactions :: OSet FundsTransaction} + +data AddressFundsPlan = Plan {references :: AddressRef , commands :: OSet Command} + +type Funds = Either NativeTokens Ada +type NativeTokens = [Amount] + +data FundsTransaction + = FundsTransaction + { txOutRef :: TxOutRef + , inputAddresses :: [Address] + , funds :: Funds + , transactionSlot :: Slot } deriving Eq + +instance Ord FundsTransaction where + compare x y = compare (transactionSlot x) (transactionSlot y) + +instance Show FundsTransaction where + show FundsTransaction {..} + = "\n | txOutRef = " <> showTxOutRef txOutRef + <> "\n | inputAddresses = " <> fold (intersperse "," (coerce <$> inputAddresses)) + <> "\n | funds = " <> fold (intersperse ("\n" <> replicate 20 ' ') (showFunds funds)) + <> "\n | TxSlot = " <> (show . getSlot) transactionSlot + + where + showFunds :: Funds -> [String] + showFunds (Right (Ada.Lovelace x)) = [T.unpack (prettyLovelaces $ fromIntegral x)] + showFunds (Left xs) = showAmount <$> xs + + showAmount :: Amount -> String + showAmount (AdaAmount x) = T.unpack (prettyLovelaces x) + showAmount (AssetAmount y) = ( show . Money.someDiscreteAmount) y <> " " <> (T.unpack . Money.someDiscreteCurrency) y + + +data RejectReason + = AddressSaturated + | FundsWithNativeTokens + | TransactionOutofRoundTimeRange + | InsufficientFundsReceived + deriving (Show,Eq) + + +data Command + = Reject {transaction :: FundsTransaction, reason :: RejectReason} + | AcceptWithPartialRefund {transaction :: FundsTransaction, refundAmount :: Ada} + | Accept {transaction :: FundsTransaction} deriving Eq + +instance Ord Command where + compare x y = compare (transaction x) (transaction y) + +instance Show Command where + show Accept {transaction = FundsTransaction {..}} + = show (getSlot transactionSlot) + <> " - " + <> showTxOutRef txOutRef <> " : Accept" + show Reject {transaction = FundsTransaction {..}, ..} + = show (getSlot transactionSlot) + <> " - " + <> showTxOutRef txOutRef + <> " : Reject (" <> show reason <> ")" + show AcceptWithPartialRefund {transaction = FundsTransaction {..}, ..} + = show (getSlot transactionSlot) + <> " - " + <> showTxOutRef txOutRef + <> " : Partially Accept ( refund of " <> show (prettyLovelaces $ fromIntegral refundAmount) <> ")" + diff --git a/tokenomia.cabal b/tokenomia.cabal index d99dbca6..0fc0a8cc 100644 --- a/tokenomia.cabal +++ b/tokenomia.cabal @@ -41,12 +41,12 @@ library exposed-modules: Tokenomia.CLI Tokenomia.ICO.AddressesGeneration - Tokenomia.ICO.Funds.Simulation - Tokenomia.ICO.HappyPath - Tokenomia.ICO.SpecialCases - Tokenomia.ICO.SpecialCases.Multisig - Tokenomia.ICO.SpecialCases.MultiUTxOs - Tokenomia.ICO.SpecialCases.NativeTokens + Tokenomia.ICO.Funds.Emission.Simulation + Tokenomia.ICO.Funds.Emission.HappyPath + Tokenomia.ICO.Funds.Emission.SpecialCases + Tokenomia.ICO.Funds.Emission.SpecialCases.Multisig + Tokenomia.ICO.Funds.Emission.SpecialCases.MultiUTxOs + Tokenomia.ICO.Funds.Emission.SpecialCases.NativeTokens Tokenomia.Node.Status Tokenomia.Token.CLAPStyle.Mint Tokenomia.Token.CLAPStyle.Burn @@ -76,6 +76,11 @@ library Tokenomia.Adapter.Cardano.CLI.Data Tokenomia.Adapter.Cardano.CLI.UTxO.Query Tokenomia.Adapter.Cardano.Types + Tokenomia.ICO.Funds.Reception.Types + Tokenomia.ICO.Funds.Reception.State + Tokenomia.ICO.Funds.Reception.Plan + Tokenomia.ICO.Funds.Reception.Transact + Tokenomia.ICO.Funds.Reception.Simulation build-depends: base >= 4.9 && < 5, lens, @@ -107,7 +112,16 @@ library plutus-contract, plutus-tx-plugin, plutus-tx, - plutus-ledger + plutus-ledger, + pretty-simple, + blockfrost-client, + blockfrost-api, + blockfrost-client-core, + blockfrost-pretty, + prettyprinter, + safe-money, + ordered-containers, + streamly hs-source-dirs: src test-suite tokenomia-tests From 3f97620fd624b48ab1ff9d3ef243614daa05734c Mon Sep 17 00:00:00 2001 From: NaadiQmmr Date: Wed, 10 Nov 2021 16:59:38 +0100 Subject: [PATCH 2/5] [ada] consolidation done --- src/Tokenomia/Ada/Consolidate.hs | 123 ++++++++++++++++++ src/Tokenomia/Ada/Transfer.hs | 1 - .../Adapter/Cardano/CLI/Transaction.hs | 35 ++++- src/Tokenomia/Adapter/Cardano/CLI/UTxO.hs | 2 +- .../Adapter/Cardano/CLI/UTxO/Query.hs | 7 +- src/Tokenomia/CLI.hs | 5 + src/Tokenomia/Token/Consolidate.hs | 0 tokenomia.cabal | 1 + 8 files changed, 167 insertions(+), 7 deletions(-) create mode 100644 src/Tokenomia/Ada/Consolidate.hs create mode 100644 src/Tokenomia/Token/Consolidate.hs diff --git a/src/Tokenomia/Ada/Consolidate.hs b/src/Tokenomia/Ada/Consolidate.hs new file mode 100644 index 00000000..5b2e1fad --- /dev/null +++ b/src/Tokenomia/Ada/Consolidate.hs @@ -0,0 +1,123 @@ +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE ExtendedDefaultRules #-} +{-# LANGUAGE FlexibleContexts #-} +{-# OPTIONS_GHC -fno-warn-missing-signatures #-} +{-# OPTIONS_GHC -fno-warn-unused-top-binds #-} +{-# LANGUAGE RecordWildCards #-} + +module Tokenomia.Ada.Consolidate where + + +import Data.List.NonEmpty +import qualified Data.ByteString.Lazy.Char8 as C +import Control.Monad.Reader hiding (ask) +import Control.Monad.Except +import Ledger.Ada ( lovelaceValueOf ) +import Data.Foldable +import Ledger (Slot(Slot)) + +import Tokenomia.Adapter.Cardano.CLI.Node + +import Tokenomia.Adapter.Cardano.CLI.Environment +import Tokenomia.Adapter.Cardano.CLI.UTxO +import Tokenomia.Adapter.Cardano.CLI.Transaction hiding (value) +import Shh.Internal + ( load, + (|>), + ExecArg(asArg), + ExecReference(SearchPath), + captureWords ) + +import Tokenomia.Adapter.Cardano.CLI.Wallet +import Tokenomia.Adapter.Cardano.CLI.UTxO.Query (queryUTxOsContainingStrictlyADAs) +import Tokenomia.Adapter.Cardano.CLI.Folder ( Folder(Transactions), getFolderPath ) +import Tokenomia.Common.Error +import Tokenomia.Wallet.Collateral.Read +import Tokenomia.Wallet.CLI + ( selectBiggestStrictlyADAsNotCollateral, + askToChooseAmongGivenWallets ) +import Tokenomia.Common.Shell.Console (printLn) +import PlutusTx.Prelude (AdditiveGroup((-))) + + +load SearchPath ["cardano-cli", "md5sum", "mv", "echo"] + +consolidate + :: ( MonadIO m + , MonadReader Environment m + , MonadError BuildingTxError m) + => m () +consolidate = do + wallet <- fetchWalletsWithCollateral >>= whenNullThrow NoWalletWithCollateral + >>= \wallets -> do + printLn "Select the minter wallet : " + askToChooseAmongGivenWallets wallets + utxo <- selectBiggestStrictlyADAsNotCollateral wallet >>= whenNothingThrow NoADAInWallet + + printLn $ "- Amount Available : " <> showValue (value utxo) + consolidate' wallet + +consolidate' + :: ( MonadIO m + , MonadReader Environment m + , MonadError BuildingTxError m) + => Wallet + -> m () +consolidate' wallet@Wallet {..} = do + ada <- queryUTxOsContainingStrictlyADAs wallet --TODO check if ada is NonEmpty before call fromList + let amount = fold (value <$> ada) + (txFolder, rawTx ) <- (\a-> (a,a <> "tx.raw")) <$> getFolderPath Transactions + aGivenTxOutRef <- txOutRef <$> (selectBiggestStrictlyADAsNotCollateral wallet >>= whenNothingThrow NoADAInWallet) + + buildRaw "0" (toCardanoCLIOptions TxBuild + { wallet = wallet + , txIns = fromList (fmap (FromWallet . txOutRef) ada) + , txOuts = ToWallet paymentAddress (lovelaceValueOf 1) :| [] + , metadataMaybe = Nothing + , validitySlotRangeMaybe = Nothing + , tokenSupplyChangesMaybe = Nothing + , ..}) + fees <- computeFees (Prelude.length ada) 1 + Slot synSlotAsInt <- getCurrentSlotSynced + buildRaw (show fees) (toCardanoCLIOptions TxBuild + { wallet = wallet + , txIns = fromList (fmap (FromWallet . txOutRef) ada) + , txOuts = ToWallet paymentAddress (amount PlutusTx.Prelude.- lovelaceValueOf fees) :| [] + , metadataMaybe = Nothing + , validitySlotRangeMaybe = Nothing + , tokenSupplyChangesMaybe = Nothing + , ..} + <> ["--invalid-hereafter", show (synSlotAsInt + 600)]) + -- Hashing the tx.raw and getting its hash x for renaming the tx.raw into x.raw + (rawHashTx,signedHashTx) <- (\txHash -> ( txFolder <> txHash <> ".raw" + , txFolder <> txHash <> ".signed" ) ) + . C.unpack . Prelude.head <$> liftIO (md5sum rawTx |> captureWords ) + liftIO $ mv rawTx rawHashTx + + printLn "Signing Tx" >> sign_tx rawHashTx signedHashTx paymentSigningKeyPath + printLn "Submitting Tx" >> submit_tx signedHashTx + printLn "Waiting for confirmation..." + awaitTxCommitted aGivenTxOutRef 0 + printLn "\nTx committed into ledger" + + +buildRaw + :: ( ExecArg a, MonadIO m, MonadReader Environment m) + => String + -> a + -> m () +buildRaw fee buildTxBody = do + (_, rawTx ) <- (\a-> (a,a <> "tx.raw")) <$> getFolderPath Transactions + + liftIO $ cardano_cli + "transaction" + "build-raw" + (asArg buildTxBody) + "--fee" fee + "--out-file" rawTx + liftIO $ echo "transaction" + "build-raw" + (asArg buildTxBody) + "--fee" fee + "--out-file" rawTx + \ No newline at end of file diff --git a/src/Tokenomia/Ada/Transfer.hs b/src/Tokenomia/Ada/Transfer.hs index c7c1cc44..9d471c2d 100644 --- a/src/Tokenomia/Ada/Transfer.hs +++ b/src/Tokenomia/Ada/Transfer.hs @@ -1,6 +1,5 @@ {-# LANGUAGE TypeApplications #-} {-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE LambdaCase #-} {-# LANGUAGE RecordWildCards #-} module Tokenomia.Ada.Transfer diff --git a/src/Tokenomia/Adapter/Cardano/CLI/Transaction.hs b/src/Tokenomia/Adapter/Cardano/CLI/Transaction.hs index 9f56fc2b..ba7cf12f 100644 --- a/src/Tokenomia/Adapter/Cardano/CLI/Transaction.hs +++ b/src/Tokenomia/Adapter/Cardano/CLI/Transaction.hs @@ -28,11 +28,13 @@ module Tokenomia.Adapter.Cardano.CLI.Transaction , submit' , createMetadataFile , register_protocol_parameters + , submit_tx , submitCollateral , sign_tx , doubleSign_tx , toCardanoCLIOptions + , computeFees ) where @@ -50,7 +52,15 @@ import Control.Monad.Except import Control.Concurrent import System.Random -import Shh.Internal +import Shh.Internal + ( (&>), + capture, + load, + (|>), + ExecArg(asArg), + ExecReference(SearchPath), + Stream(Truncate), + captureWords ) import Ledger ( TxOutRef (..),Value,Slot (..) ) @@ -66,6 +76,7 @@ import Tokenomia.Wallet.Collateral.Read import Tokenomia.Adapter.Cardano.CLI.Wallet import Tokenomia.Common.Error import Tokenomia.Wallet.CLI +import Plutus.V1.Ledger.Ada (lovelaceValueOf) {-# ANN module "HLINT: ignore Use camelCase" #-} @@ -129,7 +140,7 @@ instance ToCardanoCLIOptions TxOut where [ "--tx-out" , coerce address <> " " <> (T.unpack . toCLI) value , "--tx-out-datum-hash" , coerce datumHash] toCardanoCLIOptions ToWallet {..} = - [ "--tx-out" , coerce address <> " " <> (T.unpack . toCLI) value] + [ "--tx-out" , coerce address <> "+" <> (T.unpack . toCLI) value] instance ToCardanoCLIOptions ValiditySlotRange where toCardanoCLIOptions (ValiditySlotRange (Slot x) (Slot y)) = @@ -161,7 +172,7 @@ instance ToCardanoCLIOptions TxBuild where <> toCardanoCLIOptions tokenSupplyChangesMaybe <> toCardanoCLIOptions validitySlotRangeMaybe <> toCardanoCLIOptions metadataMaybe - + submit' :: ( MonadIO m , MonadReader Environment m @@ -318,3 +329,21 @@ createMetadataFile message = do liftIO (echo ("{\"" ++ show randomInt ++ "\":{\"message\":\"" ++ message ++ "\"}}") &> (Truncate . fromString) metadataJsonFilepath) return metadataJsonFilepath + +computeFees :: (MonadIO m, MonadReader Environment m) => Int -> Int -> m Integer +computeFees nbTxIn nbTxOut = do + protocolParametersPath <- register_protocol_parameters + magicN <- asks magicNumber + (_, rawTx ) <- (\a-> (a,a <> "tx.raw")) <$> getFolderPath Transactions + unparsedLovelaces <- liftIO (cardano_cli + "transaction" + "calculate-min-fee" + "--tx-body-file" rawTx + "--tx-in-count" (show nbTxIn) + "--tx-out-count" (show $ nbTxOut) + "--witness-count" "1" + "--byron-witness-count" "0" + "--testnet-magic" magicN + "--protocol-params-file" protocolParametersPath |> capture) + return $ parse unparsedLovelaces + where parse lovelaces = read @Integer (head (words (C.unpack lovelaces))) \ No newline at end of file diff --git a/src/Tokenomia/Adapter/Cardano/CLI/UTxO.hs b/src/Tokenomia/Adapter/Cardano/CLI/UTxO.hs index 80a997e9..e6294bfa 100644 --- a/src/Tokenomia/Adapter/Cardano/CLI/UTxO.hs +++ b/src/Tokenomia/Adapter/Cardano/CLI/UTxO.hs @@ -118,4 +118,4 @@ instance FromCLI [UTxO] where removeHeader = L.drop 2 filterEmptyLines :: [Text] -> [Text] - filterEmptyLines = L.filter (\a -> T.strip a /= mempty ) \ No newline at end of file + filterEmptyLines = L.filter (\a -> T.strip a /= mempty ) diff --git a/src/Tokenomia/Adapter/Cardano/CLI/UTxO/Query.hs b/src/Tokenomia/Adapter/Cardano/CLI/UTxO/Query.hs index adf85cf7..10ae028c 100644 --- a/src/Tokenomia/Adapter/Cardano/CLI/UTxO/Query.hs +++ b/src/Tokenomia/Adapter/Cardano/CLI/UTxO/Query.hs @@ -18,7 +18,11 @@ module Tokenomia.Adapter.Cardano.CLI.UTxO.Query ( query +<<<<<<< HEAD , queryUTxOsFilterBy +======= + , queryUTxOsContainingStrictlyADAs +>>>>>>> 12f4d10 ([ada] consolidation done) ) where @@ -28,7 +32,6 @@ import Data.Text.Lazy.Encoding as TLE ( decodeUtf8 ) import Control.Monad.Reader ( MonadReader, MonadIO(..), asks ) import Shh.Internal - import Tokenomia.Adapter.Cardano.CLI.Wallet import Tokenomia.Adapter.Cardano.CLI.Serialise import Tokenomia.Adapter.Cardano.CLI.Environment @@ -55,4 +58,4 @@ queryUTxOsFilterBy :: => Wallet -> (UTxO -> Bool) -> m [UTxO] -queryUTxOsFilterBy Wallet {..} f = filter f <$> query paymentAddress \ No newline at end of file +queryUTxOsFilterBy Wallet {..} f = filter f <$> query paymentAddress diff --git a/src/Tokenomia/CLI.hs b/src/Tokenomia/CLI.hs index 11d0c6b4..4823a99c 100644 --- a/src/Tokenomia/CLI.hs +++ b/src/Tokenomia/CLI.hs @@ -34,6 +34,7 @@ import qualified Tokenomia.Token.CLAPStyle.Burn as Token import qualified Tokenomia.Token.Transfer as Token import qualified Tokenomia.Ada.Transfer as Ada +import qualified Tokenomia.Ada.Consolidate as Ada import qualified Tokenomia.Vesting.Vest as Vesting import qualified Tokenomia.Vesting.Retrieve as Vesting @@ -141,6 +142,7 @@ runAction = \case TokenBurn -> Token.burn TokenTransfer -> Token.transfer AdaTransfer -> Ada.transfer + AdaConsolidate -> Ada.consolidate VestingVestFunds -> Vesting.vestFunds VestingRetrieveFunds -> Vesting.retrieveFunds NodeStatus -> Node.displayStatus @@ -156,6 +158,7 @@ actions = NonEmpty.fromList [ TokenBurn, TokenTransfer, AdaTransfer, + AdaConsolidate, VestingVestFunds, VestingRetrieveFunds, NodeStatus, @@ -172,6 +175,7 @@ data Action | TokenBurn | TokenTransfer | AdaTransfer + | AdaConsolidate | VestingVestFunds | VestingRetrieveFunds | NodeStatus @@ -188,6 +192,7 @@ instance DisplayMenuItem Action where TokenBurn -> " [Token] - Burn Tokens with CLAP type policy" TokenTransfer -> " [Token] - Transfer Tokens" AdaTransfer -> " [Ada] - Transfer ADAs" + AdaConsolidate -> " [Ada] - Consolidate ADAs" VestingVestFunds -> "[Vesting] - Vest Funds" VestingRetrieveFunds -> "[Vesting] - Retrieve Funds" NodeStatus -> "[Node] - Status" diff --git a/src/Tokenomia/Token/Consolidate.hs b/src/Tokenomia/Token/Consolidate.hs new file mode 100644 index 00000000..e69de29b diff --git a/tokenomia.cabal b/tokenomia.cabal index 0fc0a8cc..25370a18 100644 --- a/tokenomia.cabal +++ b/tokenomia.cabal @@ -55,6 +55,7 @@ library Tokenomia.Wallet.Collateral.Write Tokenomia.Wallet.Collateral.Read Tokenomia.Ada.Transfer + Tokenomia.Ada.Consolidate Tokenomia.Common.Error Tokenomia.Token.Transfer Tokenomia.Vesting.Contract From f5060623f2040bde3b926554d5bfb81ed9cb9f55 Mon Sep 17 00:00:00 2001 From: NaadiQmmr Date: Fri, 12 Nov 2021 16:11:49 +0100 Subject: [PATCH 3/5] [natives tokens] consolidation done --- src/Tokenomia/Ada/Consolidate.hs | 4 +- .../Adapter/Cardano/CLI/Transaction.hs | 4 +- src/Tokenomia/Adapter/Cardano/CLI/UTxO.hs | 4 +- .../Adapter/Cardano/CLI/UTxO/Query.hs | 43 +++++- src/Tokenomia/Adapter/Cardano/Types.hs | 1 - src/Tokenomia/Adapter/Data/List.hs | 13 ++ src/Tokenomia/CLI.hs | 6 + src/Tokenomia/Token/Consolidate.hs | 122 ++++++++++++++++++ tokenomia.cabal | 2 + 9 files changed, 186 insertions(+), 13 deletions(-) create mode 100644 src/Tokenomia/Adapter/Data/List.hs diff --git a/src/Tokenomia/Ada/Consolidate.hs b/src/Tokenomia/Ada/Consolidate.hs index 5b2e1fad..fb3f755d 100644 --- a/src/Tokenomia/Ada/Consolidate.hs +++ b/src/Tokenomia/Ada/Consolidate.hs @@ -29,7 +29,7 @@ import Shh.Internal captureWords ) import Tokenomia.Adapter.Cardano.CLI.Wallet -import Tokenomia.Adapter.Cardano.CLI.UTxO.Query (queryUTxOsContainingStrictlyADAs) +import Tokenomia.Adapter.Cardano.CLI.UTxO.Query (queryUTxOsFilterBy) import Tokenomia.Adapter.Cardano.CLI.Folder ( Folder(Transactions), getFolderPath ) import Tokenomia.Common.Error import Tokenomia.Wallet.Collateral.Read @@ -64,7 +64,7 @@ consolidate' => Wallet -> m () consolidate' wallet@Wallet {..} = do - ada <- queryUTxOsContainingStrictlyADAs wallet --TODO check if ada is NonEmpty before call fromList + ada <- queryUTxOsFilterBy wallet containingStrictlyADAs let amount = fold (value <$> ada) (txFolder, rawTx ) <- (\a-> (a,a <> "tx.raw")) <$> getFolderPath Transactions aGivenTxOutRef <- txOutRef <$> (selectBiggestStrictlyADAsNotCollateral wallet >>= whenNothingThrow NoADAInWallet) diff --git a/src/Tokenomia/Adapter/Cardano/CLI/Transaction.hs b/src/Tokenomia/Adapter/Cardano/CLI/Transaction.hs index ba7cf12f..c7d98ddc 100644 --- a/src/Tokenomia/Adapter/Cardano/CLI/Transaction.hs +++ b/src/Tokenomia/Adapter/Cardano/CLI/Transaction.hs @@ -62,7 +62,7 @@ import Shh.Internal Stream(Truncate), captureWords ) -import Ledger ( TxOutRef (..),Value,Slot (..) ) +import Ledger ( TxOutRef (..),Value,Slot (..), CurrencySymbol ) import Tokenomia.Common.Shell.Console (printLn) import Tokenomia.Adapter.Cardano.CLI.Environment @@ -76,7 +76,6 @@ import Tokenomia.Wallet.Collateral.Read import Tokenomia.Adapter.Cardano.CLI.Wallet import Tokenomia.Common.Error import Tokenomia.Wallet.CLI -import Plutus.V1.Ledger.Ada (lovelaceValueOf) {-# ANN module "HLINT: ignore Use camelCase" #-} @@ -91,6 +90,7 @@ data BuildingTxError | AlreadyACollateral UTxO | NoADAInWallet | NoUTxOWithOnlyOneToken + | NoUTxOWithSuchCurrency CurrencySymbol | TryingToBurnTokenWithoutScriptRegistered | NoVestingInProgress | NoFundsToBeRetrieved diff --git a/src/Tokenomia/Adapter/Cardano/CLI/UTxO.hs b/src/Tokenomia/Adapter/Cardano/CLI/UTxO.hs index e6294bfa..e15c25b4 100644 --- a/src/Tokenomia/Adapter/Cardano/CLI/UTxO.hs +++ b/src/Tokenomia/Adapter/Cardano/CLI/UTxO.hs @@ -34,7 +34,6 @@ import Data.Foldable ( Foldable(fold) ) import Plutus.V1.Ledger.Ada import Ledger.Value - getTokenFrom :: UTxO -> (CurrencySymbol,TokenName,Integer) getTokenFrom UTxO {..} = (head . filter (\(c,_,_) -> c /= adaSymbol ) .flattenValue) value -- should contains only one native token (filtering ADAs) @@ -71,6 +70,9 @@ instance Show UTxO where instance DisplayMenuItem UTxO where displayMenuItem UTxO {..} = showTxOutRef txOutRef <> " : " <> showValue value +instance DisplayMenuItem CurrencySymbol where + displayMenuItem (CurrencySymbol a) = show a + instance ToCLI TxOutRef where toCLI = T.pack . showTxOutRef diff --git a/src/Tokenomia/Adapter/Cardano/CLI/UTxO/Query.hs b/src/Tokenomia/Adapter/Cardano/CLI/UTxO/Query.hs index 10ae028c..b3a71adc 100644 --- a/src/Tokenomia/Adapter/Cardano/CLI/UTxO/Query.hs +++ b/src/Tokenomia/Adapter/Cardano/CLI/UTxO/Query.hs @@ -18,31 +18,34 @@ module Tokenomia.Adapter.Cardano.CLI.UTxO.Query ( query -<<<<<<< HEAD , queryUTxOsFilterBy -======= - , queryUTxOsContainingStrictlyADAs ->>>>>>> 12f4d10 ([ada] consolidation done) + , getCurrency + , askSelectUTxOByType ) where import qualified Data.Text.Lazy as TL import Data.Text.Lazy.Encoding as TLE ( decodeUtf8 ) + import Control.Monad.Reader ( MonadReader, MonadIO(..), asks ) import Shh.Internal +import Ledger.Value import Tokenomia.Adapter.Cardano.CLI.Wallet import Tokenomia.Adapter.Cardano.CLI.Serialise +import Tokenomia.Adapter.Data.List (short, rmdups) import Tokenomia.Adapter.Cardano.CLI.Environment import Tokenomia.Adapter.Cardano.CLI.Value () import Tokenomia.Adapter.Cardano.CLI.UTxO -import Tokenomia.Adapter.Cardano.Types +import Tokenomia.Adapter.Cardano.Types +import Data.List.NonEmpty (fromList) +import Tokenomia.Common.Shell.InteractiveMenu (askMenu, DisplayMenuItem, displayMenuItem) load SearchPath ["cardano-cli"] -query :: +query :: ( MonadIO m , MonadReader Environment m ) => Address @@ -58,4 +61,30 @@ queryUTxOsFilterBy :: => Wallet -> (UTxO -> Bool) -> m [UTxO] -queryUTxOsFilterBy Wallet {..} f = filter f <$> query paymentAddress +queryUTxOsFilterBy Wallet{..} f = filter f <$> query paymentAddress + +getCurrency :: UTxO -> (CurrencySymbol, TokenName) +getCurrency utxo = (currency, _tokenName) + where (currency, _tokenName, _) = getTokenFrom utxo + +queryTokensList :: (MonadIO m, MonadReader Environment m) => Wallet -> m [(CurrencySymbol, TokenName)] +queryTokensList wallet = do + utxos <- queryUTxOsFilterBy wallet (not . containingStrictlyADAs) + if null utxos then return [] else do + let currencyList = map getCurrency utxos + return $ rmdups currencyList + +askSelectUTxOByType :: (MonadIO m, MonadReader Environment m) => Wallet -> m (Maybe [UTxO]) +askSelectUTxOByType wallet = do + tokensList <- queryTokensList wallet + case tokensList of + [] -> return Nothing + tokenList -> do + (currency, _) <- askMenu $ fromList tokenList --displayMenuItem + queryUTxOsFilterBy wallet (containingGivenNativeToken currency) >>= + \case + [] -> return Nothing + utxos -> return $ Just utxos + +instance DisplayMenuItem (CurrencySymbol, TokenName) where + displayMenuItem (currency, _tokenName) = toString _tokenName <> " - " <> short (show currency) \ No newline at end of file diff --git a/src/Tokenomia/Adapter/Cardano/Types.hs b/src/Tokenomia/Adapter/Cardano/Types.hs index 702807d5..645bd8e8 100644 --- a/src/Tokenomia/Adapter/Cardano/Types.hs +++ b/src/Tokenomia/Adapter/Cardano/Types.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE DerivingVia #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} module Tokenomia.Adapter.Cardano.Types (Address (..), Hash(..)) where diff --git a/src/Tokenomia/Adapter/Data/List.hs b/src/Tokenomia/Adapter/Data/List.hs new file mode 100644 index 00000000..00fed67c --- /dev/null +++ b/src/Tokenomia/Adapter/Data/List.hs @@ -0,0 +1,13 @@ +module Tokenomia.Adapter.Data.List where +import qualified Data.Set as Set + +rmdups :: Ord a => [a] -> [a] +rmdups = rmdups' Set.empty where + rmdups' _ [] = [] + rmdups' a (b : c) = if Set.member b a + then rmdups' a c + else b : rmdups' (Set.insert b a) c + +short :: Ord a => a -> a +short [] = [] +short x = take (length x `div` 2) x \ No newline at end of file diff --git a/src/Tokenomia/CLI.hs b/src/Tokenomia/CLI.hs index 4823a99c..84ef1194 100644 --- a/src/Tokenomia/CLI.hs +++ b/src/Tokenomia/CLI.hs @@ -32,6 +32,7 @@ import qualified Tokenomia.Wallet.Collateral.Write as Wallet import qualified Tokenomia.Token.CLAPStyle.Mint as Token import qualified Tokenomia.Token.CLAPStyle.Burn as Token import qualified Tokenomia.Token.Transfer as Token +import qualified Tokenomia.Token.Consolidate as Token import qualified Tokenomia.Ada.Transfer as Ada import qualified Tokenomia.Ada.Consolidate as Ada @@ -115,6 +116,7 @@ recursiveMenu = do AlreadyACollateral utxo -> printLn ("Collateral Already Created..." <> show utxo) NoADAInWallet -> printLn "Please, add ADAs to your wallet..." NoUTxOWithOnlyOneToken -> printLn "Please, add tokens to your wallet..." + NoUTxOWithSuchCurrency c -> printLn ("No UTxO found with " <> show c <> " currency.") TryingToBurnTokenWithoutScriptRegistered -> printLn "You can't burn tokens without the monetary script registered in Tokenomia" NoVestingInProgress -> printLn "No vesting in progress" @@ -141,6 +143,7 @@ runAction = \case TokenMint -> Token.mint TokenBurn -> Token.burn TokenTransfer -> Token.transfer + TokenConsolidate -> Token.consolidate AdaTransfer -> Ada.transfer AdaConsolidate -> Ada.consolidate VestingVestFunds -> Vesting.vestFunds @@ -157,6 +160,7 @@ actions = NonEmpty.fromList [ TokenMint, TokenBurn, TokenTransfer, + TokenConsolidate, AdaTransfer, AdaConsolidate, VestingVestFunds, @@ -174,6 +178,7 @@ data Action | TokenMint | TokenBurn | TokenTransfer + | TokenConsolidate | AdaTransfer | AdaConsolidate | VestingVestFunds @@ -191,6 +196,7 @@ instance DisplayMenuItem Action where TokenMint -> " [Token] - Mint with CLAP type policy (Fix Total Supply | one-time Minting and open Burning Policy)" TokenBurn -> " [Token] - Burn Tokens with CLAP type policy" TokenTransfer -> " [Token] - Transfer Tokens" + TokenConsolidate -> "[Token] - Consolidate Tokens" AdaTransfer -> " [Ada] - Transfer ADAs" AdaConsolidate -> " [Ada] - Consolidate ADAs" VestingVestFunds -> "[Vesting] - Vest Funds" diff --git a/src/Tokenomia/Token/Consolidate.hs b/src/Tokenomia/Token/Consolidate.hs index e69de29b..ba551791 100644 --- a/src/Tokenomia/Token/Consolidate.hs +++ b/src/Tokenomia/Token/Consolidate.hs @@ -0,0 +1,122 @@ +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE ExtendedDefaultRules #-} +{-# LANGUAGE FlexibleContexts #-} +{-# OPTIONS_GHC -fno-warn-missing-signatures #-} +{-# OPTIONS_GHC -fno-warn-unused-top-binds #-} +{-# LANGUAGE RecordWildCards #-} + +module Tokenomia.Token.Consolidate where + + +import Data.List.NonEmpty +import qualified Data.ByteString.Lazy.Char8 as C +import Control.Monad.Reader hiding (ask) +import Control.Monad.Except +import Ledger.Ada ( lovelaceValueOf ) +import Data.Foldable +import Ledger (Slot(Slot)) + +import Tokenomia.Adapter.Cardano.CLI.Node + +import Tokenomia.Adapter.Cardano.CLI.Environment +import Tokenomia.Adapter.Cardano.CLI.UTxO +import Tokenomia.Adapter.Cardano.CLI.Transaction hiding (value) +import Shh.Internal + ( load, + (|>), + ExecArg(asArg), + ExecReference(SearchPath), + captureWords ) + +import Tokenomia.Adapter.Cardano.CLI.Wallet +import Tokenomia.Adapter.Cardano.CLI.UTxO.Query (askSelectUTxOByType) +import Tokenomia.Adapter.Cardano.CLI.Folder ( Folder(Transactions), getFolderPath ) +import Tokenomia.Common.Error +import Tokenomia.Wallet.Collateral.Read +import Tokenomia.Wallet.CLI + ( selectBiggestStrictlyADAsNotCollateral, + askToChooseAmongGivenWallets ) +import Tokenomia.Common.Shell.Console (printLn) +import PlutusTx.Prelude (AdditiveGroup((-))) + + +load SearchPath ["cardano-cli", "md5sum", "mv", "echo"] + + +consolidate + :: ( MonadIO m + , MonadReader Environment m + , MonadError BuildingTxError m) + => m () +consolidate = do + wallet@Wallet{..} <- fetchWalletsWithCollateral >>= whenNullThrow NoWalletWithCollateral + >>= \wallets -> do + printLn "Select the minter wallet : " + askToChooseAmongGivenWallets wallets + tokensChoosen <- askSelectUTxOByType wallet >>= whenNothingThrow NoFundsToBeRetrieved + consolidate' wallet tokensChoosen + +consolidate' + :: ( MonadIO m + , MonadReader Environment m + , MonadError BuildingTxError m) + => Wallet + -> [UTxO] + -> m () +consolidate' wallet@Wallet {..} utxos = do + let amount = fold (value <$> utxos) + (txFolder, rawTx ) <- (\a-> (a,a <> "tx.raw")) <$> getFolderPath Transactions + aGivenTxOutRef <- txOutRef <$> (selectBiggestStrictlyADAsNotCollateral wallet >>= whenNothingThrow NoADAInWallet) + + buildRaw "0" (toCardanoCLIOptions TxBuild + { wallet = wallet + , txIns = fromList (fmap (FromWallet . txOutRef) utxos) + , txOuts = ToWallet paymentAddress (lovelaceValueOf 1) :| [] + , metadataMaybe = Nothing + , validitySlotRangeMaybe = Nothing + , tokenSupplyChangesMaybe = Nothing + , ..}) + fees <- computeFees (Prelude.length utxos) 1 + Slot synSlotAsInt <- getCurrentSlotSynced + buildRaw (show fees) (toCardanoCLIOptions TxBuild + { wallet = wallet + , txIns = fromList (fmap (FromWallet . txOutRef) utxos) + , txOuts = ToWallet paymentAddress (amount PlutusTx.Prelude.- lovelaceValueOf fees) :| [] + , metadataMaybe = Nothing + , validitySlotRangeMaybe = Nothing + , tokenSupplyChangesMaybe = Nothing + , ..} + <> ["--invalid-hereafter", show (synSlotAsInt + 600)]) + -- Hashing the tx.raw and getting its hash x for renaming the tx.raw into x.raw + (rawHashTx,signedHashTx) <- (\txHash -> ( txFolder <> txHash <> ".raw" + , txFolder <> txHash <> ".signed" ) ) + . C.unpack . Prelude.head <$> liftIO (md5sum rawTx |> captureWords ) + liftIO $ mv rawTx rawHashTx + + printLn "Signing Tx" >> sign_tx rawHashTx signedHashTx paymentSigningKeyPath + printLn "Submitting Tx" >> submit_tx signedHashTx + printLn "Waiting for confirmation..." + awaitTxCommitted aGivenTxOutRef 0 + printLn "\nTx committed into ledger" + + +buildRaw + :: ( ExecArg a, MonadIO m, MonadReader Environment m) + => String + -> a + -> m () +buildRaw fee buildTxBody = do + (_, rawTx ) <- (\a-> (a,a <> "tx.raw")) <$> getFolderPath Transactions + + liftIO $ cardano_cli + "transaction" + "build-raw" + (asArg buildTxBody) + "--fee" fee + "--out-file" rawTx + liftIO $ echo "transaction" + "build-raw" + (asArg buildTxBody) + "--fee" fee + "--out-file" rawTx + \ No newline at end of file diff --git a/tokenomia.cabal b/tokenomia.cabal index 25370a18..b19d39f5 100644 --- a/tokenomia.cabal +++ b/tokenomia.cabal @@ -58,6 +58,7 @@ library Tokenomia.Ada.Consolidate Tokenomia.Common.Error Tokenomia.Token.Transfer + Tokenomia.Token.Consolidate Tokenomia.Vesting.Contract Tokenomia.Vesting.Repository Tokenomia.Vesting.Vest @@ -71,6 +72,7 @@ library Tokenomia.Adapter.Cardano.CLI.Folder Tokenomia.Adapter.Cardano.CLI.Scripts Tokenomia.Adapter.Cardano.CLI.Wallet + Tokenomia.Adapter.Data.List Tokenomia.Common.Shell.InteractiveMenu Tokenomia.Common.Shell.Console Tokenomia.Adapter.Cardano.CLI.Environment From ec8f9bbbbea12219f89637659d7606dd5922bedc Mon Sep 17 00:00:00 2001 From: NaadiQmmr Date: Fri, 19 Nov 2021 16:01:43 +0100 Subject: [PATCH 4/5] WIP --- src/Tokenomia/Ada/Consolidate.hs | 36 +++-------- .../Adapter/Cardano/CLI/Transaction.hs | 22 +++++-- src/Tokenomia/Adapter/Cardano/CLI/UTxO.hs | 1 - .../Adapter/Cardano/CLI/UTxO/Query.hs | 61 ++++++++++++------- src/Tokenomia/Adapter/Data/List.hs | 16 ++--- .../Adapter/Ledger/Value/CurrencySymbol.hs | 7 +++ src/Tokenomia/CLI.hs | 1 - src/Tokenomia/Token/Consolidate.hs | 36 +++-------- tokenomia.cabal | 1 + 9 files changed, 83 insertions(+), 98 deletions(-) create mode 100644 src/Tokenomia/Adapter/Ledger/Value/CurrencySymbol.hs diff --git a/src/Tokenomia/Ada/Consolidate.hs b/src/Tokenomia/Ada/Consolidate.hs index fb3f755d..6342eeb1 100644 --- a/src/Tokenomia/Ada/Consolidate.hs +++ b/src/Tokenomia/Ada/Consolidate.hs @@ -5,7 +5,8 @@ {-# OPTIONS_GHC -fno-warn-unused-top-binds #-} {-# LANGUAGE RecordWildCards #-} -module Tokenomia.Ada.Consolidate where +module Tokenomia.Ada.Consolidate + ( consolidate ) where import Data.List.NonEmpty @@ -24,7 +25,6 @@ import Tokenomia.Adapter.Cardano.CLI.Transaction hiding (value) import Shh.Internal ( load, (|>), - ExecArg(asArg), ExecReference(SearchPath), captureWords ) @@ -64,24 +64,24 @@ consolidate' => Wallet -> m () consolidate' wallet@Wallet {..} = do - ada <- queryUTxOsFilterBy wallet containingStrictlyADAs - let amount = fold (value <$> ada) + adas <- queryUTxOsFilterBy wallet containingStrictlyADAs + let amount = fold (value <$> adas ) (txFolder, rawTx ) <- (\a-> (a,a <> "tx.raw")) <$> getFolderPath Transactions aGivenTxOutRef <- txOutRef <$> (selectBiggestStrictlyADAsNotCollateral wallet >>= whenNothingThrow NoADAInWallet) buildRaw "0" (toCardanoCLIOptions TxBuild { wallet = wallet - , txIns = fromList (fmap (FromWallet . txOutRef) ada) + , txIns = fromList (fmap (FromWallet . txOutRef) adas ) , txOuts = ToWallet paymentAddress (lovelaceValueOf 1) :| [] , metadataMaybe = Nothing , validitySlotRangeMaybe = Nothing , tokenSupplyChangesMaybe = Nothing , ..}) - fees <- computeFees (Prelude.length ada) 1 + fees <- computeFees (Prelude.length adas ) 1 Slot synSlotAsInt <- getCurrentSlotSynced buildRaw (show fees) (toCardanoCLIOptions TxBuild { wallet = wallet - , txIns = fromList (fmap (FromWallet . txOutRef) ada) + , txIns = fromList (fmap (FromWallet . txOutRef) adas ) , txOuts = ToWallet paymentAddress (amount PlutusTx.Prelude.- lovelaceValueOf fees) :| [] , metadataMaybe = Nothing , validitySlotRangeMaybe = Nothing @@ -99,25 +99,3 @@ consolidate' wallet@Wallet {..} = do printLn "Waiting for confirmation..." awaitTxCommitted aGivenTxOutRef 0 printLn "\nTx committed into ledger" - - -buildRaw - :: ( ExecArg a, MonadIO m, MonadReader Environment m) - => String - -> a - -> m () -buildRaw fee buildTxBody = do - (_, rawTx ) <- (\a-> (a,a <> "tx.raw")) <$> getFolderPath Transactions - - liftIO $ cardano_cli - "transaction" - "build-raw" - (asArg buildTxBody) - "--fee" fee - "--out-file" rawTx - liftIO $ echo "transaction" - "build-raw" - (asArg buildTxBody) - "--fee" fee - "--out-file" rawTx - \ No newline at end of file diff --git a/src/Tokenomia/Adapter/Cardano/CLI/Transaction.hs b/src/Tokenomia/Adapter/Cardano/CLI/Transaction.hs index c7d98ddc..ecc04187 100644 --- a/src/Tokenomia/Adapter/Cardano/CLI/Transaction.hs +++ b/src/Tokenomia/Adapter/Cardano/CLI/Transaction.hs @@ -28,13 +28,13 @@ module Tokenomia.Adapter.Cardano.CLI.Transaction , submit' , createMetadataFile , register_protocol_parameters - , submit_tx , submitCollateral , sign_tx , doubleSign_tx , toCardanoCLIOptions , computeFees + , buildRaw ) where @@ -62,7 +62,7 @@ import Shh.Internal Stream(Truncate), captureWords ) -import Ledger ( TxOutRef (..),Value,Slot (..), CurrencySymbol ) +import Ledger ( TxOutRef (..),Value,Slot (..) ) import Tokenomia.Common.Shell.Console (printLn) import Tokenomia.Adapter.Cardano.CLI.Environment @@ -90,7 +90,6 @@ data BuildingTxError | AlreadyACollateral UTxO | NoADAInWallet | NoUTxOWithOnlyOneToken - | NoUTxOWithSuchCurrency CurrencySymbol | TryingToBurnTokenWithoutScriptRegistered | NoVestingInProgress | NoFundsToBeRetrieved @@ -346,4 +345,19 @@ computeFees nbTxIn nbTxOut = do "--testnet-magic" magicN "--protocol-params-file" protocolParametersPath |> capture) return $ parse unparsedLovelaces - where parse lovelaces = read @Integer (head (words (C.unpack lovelaces))) \ No newline at end of file + where parse lovelaces = read @Integer (head (words (C.unpack lovelaces))) + +buildRaw + :: ( ExecArg a, MonadIO m, MonadReader Environment m) + => String + -> a + -> m () +buildRaw fee buildTxBody = do + (_, rawTx ) <- (\a-> (a,a <> "tx.raw")) <$> getFolderPath Transactions + + liftIO $ cardano_cli + "transaction" + "build-raw" + (asArg buildTxBody) + "--fee" fee + "--out-file" rawTx \ No newline at end of file diff --git a/src/Tokenomia/Adapter/Cardano/CLI/UTxO.hs b/src/Tokenomia/Adapter/Cardano/CLI/UTxO.hs index e15c25b4..3e7f5ba9 100644 --- a/src/Tokenomia/Adapter/Cardano/CLI/UTxO.hs +++ b/src/Tokenomia/Adapter/Cardano/CLI/UTxO.hs @@ -40,7 +40,6 @@ getTokenFrom UTxO {..} = (head . filter (\(c,_,_) -> c /= adaSymbol ) .flattenVa getTokensFrom :: Value -> Value getTokensFrom = mkValue . filter (\(c,_,_) -> c /= adaSymbol ) .flattenValue -- should contains only one native token (filtering ADAs) - mkValue :: [(CurrencySymbol, TokenName, Integer)] -> Value mkValue = foldMap (\(a,b,c) -> singleton a b c) diff --git a/src/Tokenomia/Adapter/Cardano/CLI/UTxO/Query.hs b/src/Tokenomia/Adapter/Cardano/CLI/UTxO/Query.hs index b3a71adc..4b543bf6 100644 --- a/src/Tokenomia/Adapter/Cardano/CLI/UTxO/Query.hs +++ b/src/Tokenomia/Adapter/Cardano/CLI/UTxO/Query.hs @@ -19,14 +19,14 @@ module Tokenomia.Adapter.Cardano.CLI.UTxO.Query ( query , queryUTxOsFilterBy - , getCurrency , askSelectUTxOByType + , retrieveTotalAmountFromAsset ) where import qualified Data.Text.Lazy as TL import Data.Text.Lazy.Encoding as TLE ( decodeUtf8 ) - +import Data.List.NonEmpty import Control.Monad.Reader ( MonadReader, MonadIO(..), asks ) import Shh.Internal @@ -34,15 +34,13 @@ import Ledger.Value import Tokenomia.Adapter.Cardano.CLI.Wallet import Tokenomia.Adapter.Cardano.CLI.Serialise -import Tokenomia.Adapter.Data.List (short, rmdups) +import Tokenomia.Adapter.Data.List (removeDuplicates) import Tokenomia.Adapter.Cardano.CLI.Environment import Tokenomia.Adapter.Cardano.CLI.Value () import Tokenomia.Adapter.Cardano.CLI.UTxO import Tokenomia.Adapter.Cardano.Types -import Data.List.NonEmpty (fromList) import Tokenomia.Common.Shell.InteractiveMenu (askMenu, DisplayMenuItem, displayMenuItem) - load SearchPath ["cardano-cli"] query :: @@ -61,30 +59,47 @@ queryUTxOsFilterBy :: => Wallet -> (UTxO -> Bool) -> m [UTxO] -queryUTxOsFilterBy Wallet{..} f = filter f <$> query paymentAddress +queryUTxOsFilterBy Wallet{..} f = Prelude.filter f <$> query paymentAddress -getCurrency :: UTxO -> (CurrencySymbol, TokenName) -getCurrency utxo = (currency, _tokenName) - where (currency, _tokenName, _) = getTokenFrom utxo +getAssetClassList :: [UTxO] -> [AssetClass] +getAssetClassList [] = [] +getAssetClassList (UTxO{..} : xs) = + Prelude.map (\(c, t, _) -> assetClass c t) (flattenValue value) ++ getAssetClassList xs -queryTokensList :: (MonadIO m, MonadReader Environment m) => Wallet -> m [(CurrencySymbol, TokenName)] -queryTokensList wallet = do +queryAssetClassList :: (MonadIO m, MonadReader Environment m) => Wallet -> m [AssetClass] +queryAssetClassList wallet = do utxos <- queryUTxOsFilterBy wallet (not . containingStrictlyADAs) if null utxos then return [] else do - let currencyList = map getCurrency utxos - return $ rmdups currencyList + let currencyList = getAssetClassList utxos -- TODO change this + return $ removeDuplicates currencyList -askSelectUTxOByType :: (MonadIO m, MonadReader Environment m) => Wallet -> m (Maybe [UTxO]) +queryAssetClassFilterBy :: + ( MonadIO m + , MonadReader Environment m ) + => Wallet + -> (AssetClass -> Bool) + -> m [AssetClass] +queryAssetClassFilterBy wallet f = do + assetClasses <- queryAssetClassList wallet + return $ Prelude.filter f assetClasses + +askSelectUTxOByType :: (MonadIO m, MonadReader Environment m) => Wallet -> m (Maybe (NonEmpty AssetClass)) askSelectUTxOByType wallet = do - tokensList <- queryTokensList wallet + tokensList <- queryAssetClassList wallet case tokensList of [] -> return Nothing tokenList -> do - (currency, _) <- askMenu $ fromList tokenList --displayMenuItem - queryUTxOsFilterBy wallet (containingGivenNativeToken currency) >>= - \case - [] -> return Nothing - utxos -> return $ Just utxos - -instance DisplayMenuItem (CurrencySymbol, TokenName) where - displayMenuItem (currency, _tokenName) = toString _tokenName <> " - " <> short (show currency) \ No newline at end of file + _assetClass <- askMenu $ fromList tokenList + utxos <- queryAssetClassFilterBy wallet (== _assetClass) + if null utxos then return Nothing else return (Just $ fromList utxos) + +instance DisplayMenuItem AssetClass where + displayMenuItem _assetClass = show _tokenName <> " - " <> show _currencySymbol --TODO : showHashView for currencySymbol + where (_currencySymbol, _tokenName) = unAssetClass _assetClass + + +retrieveTotalAmountFromAsset :: AssetClass -> Wallet -> Integer +retrieveTotalAmountFromAsset asset Wallet{..} = do + allFlattenValues <- Prelude.map (\UTxO{..} -> flattenValue value) (query paymentAddress) + _flattenValues <- Prelude.filter (\(c, t, _) -> assetClass c t == asset) allFlattenValues + return $ sum (Prelude.map (\(_, _, a) -> a) _flattenValues) \ No newline at end of file diff --git a/src/Tokenomia/Adapter/Data/List.hs b/src/Tokenomia/Adapter/Data/List.hs index 00fed67c..e54438d8 100644 --- a/src/Tokenomia/Adapter/Data/List.hs +++ b/src/Tokenomia/Adapter/Data/List.hs @@ -1,13 +1,7 @@ -module Tokenomia.Adapter.Data.List where -import qualified Data.Set as Set +module Tokenomia.Adapter.Data.List + (removeDuplicates) where +import Data.List -rmdups :: Ord a => [a] -> [a] -rmdups = rmdups' Set.empty where - rmdups' _ [] = [] - rmdups' a (b : c) = if Set.member b a - then rmdups' a c - else b : rmdups' (Set.insert b a) c +removeDuplicates :: (Ord a) => [a] -> [a] +removeDuplicates = Prelude.map Prelude.head . group . sort -short :: Ord a => a -> a -short [] = [] -short x = take (length x `div` 2) x \ No newline at end of file diff --git a/src/Tokenomia/Adapter/Ledger/Value/CurrencySymbol.hs b/src/Tokenomia/Adapter/Ledger/Value/CurrencySymbol.hs new file mode 100644 index 00000000..2aa58140 --- /dev/null +++ b/src/Tokenomia/Adapter/Ledger/Value/CurrencySymbol.hs @@ -0,0 +1,7 @@ +module Tokenomia.Adapter.Ledger.Value.CurrencySymbol + ( shortHashView ) where + +import Ledger.Value + +shortHashView :: CurrencySymbol -> String +shortHashView x = take (length (show x) `div` 2) (show x) \ No newline at end of file diff --git a/src/Tokenomia/CLI.hs b/src/Tokenomia/CLI.hs index 84ef1194..a6564612 100644 --- a/src/Tokenomia/CLI.hs +++ b/src/Tokenomia/CLI.hs @@ -116,7 +116,6 @@ recursiveMenu = do AlreadyACollateral utxo -> printLn ("Collateral Already Created..." <> show utxo) NoADAInWallet -> printLn "Please, add ADAs to your wallet..." NoUTxOWithOnlyOneToken -> printLn "Please, add tokens to your wallet..." - NoUTxOWithSuchCurrency c -> printLn ("No UTxO found with " <> show c <> " currency.") TryingToBurnTokenWithoutScriptRegistered -> printLn "You can't burn tokens without the monetary script registered in Tokenomia" NoVestingInProgress -> printLn "No vesting in progress" diff --git a/src/Tokenomia/Token/Consolidate.hs b/src/Tokenomia/Token/Consolidate.hs index ba551791..aa5fc2e9 100644 --- a/src/Tokenomia/Token/Consolidate.hs +++ b/src/Tokenomia/Token/Consolidate.hs @@ -5,8 +5,8 @@ {-# OPTIONS_GHC -fno-warn-unused-top-binds #-} {-# LANGUAGE RecordWildCards #-} -module Tokenomia.Token.Consolidate where - +module Tokenomia.Token.Consolidate + ( consolidate ) where import Data.List.NonEmpty import qualified Data.ByteString.Lazy.Char8 as C @@ -15,6 +15,7 @@ import Control.Monad.Except import Ledger.Ada ( lovelaceValueOf ) import Data.Foldable import Ledger (Slot(Slot)) +import Ledger.Value import Tokenomia.Adapter.Cardano.CLI.Node @@ -24,12 +25,11 @@ import Tokenomia.Adapter.Cardano.CLI.Transaction hiding (value) import Shh.Internal ( load, (|>), - ExecArg(asArg), ExecReference(SearchPath), captureWords ) import Tokenomia.Adapter.Cardano.CLI.Wallet -import Tokenomia.Adapter.Cardano.CLI.UTxO.Query (askSelectUTxOByType) +import Tokenomia.Adapter.Cardano.CLI.UTxO.Query (askSelectUTxOByType, retrieveTotalAmountFromAsset) import Tokenomia.Adapter.Cardano.CLI.Folder ( Folder(Transactions), getFolderPath ) import Tokenomia.Common.Error import Tokenomia.Wallet.Collateral.Read @@ -49,7 +49,7 @@ consolidate , MonadError BuildingTxError m) => m () consolidate = do - wallet@Wallet{..} <- fetchWalletsWithCollateral >>= whenNullThrow NoWalletWithCollateral + wallet <- fetchWalletsWithCollateral >>= whenNullThrow NoWalletWithCollateral >>= \wallets -> do printLn "Select the minter wallet : " askToChooseAmongGivenWallets wallets @@ -61,10 +61,10 @@ consolidate' , MonadReader Environment m , MonadError BuildingTxError m) => Wallet - -> [UTxO] + -> NonEmpty AssetClass -> m () consolidate' wallet@Wallet {..} utxos = do - let amount = fold (value <$> utxos) + let amount = retrieveTotalAmountFromAsset (Data.List.NonEmpty.head . utxos) wallet (txFolder, rawTx ) <- (\a-> (a,a <> "tx.raw")) <$> getFolderPath Transactions aGivenTxOutRef <- txOutRef <$> (selectBiggestStrictlyADAsNotCollateral wallet >>= whenNothingThrow NoADAInWallet) @@ -98,25 +98,3 @@ consolidate' wallet@Wallet {..} utxos = do printLn "Waiting for confirmation..." awaitTxCommitted aGivenTxOutRef 0 printLn "\nTx committed into ledger" - - -buildRaw - :: ( ExecArg a, MonadIO m, MonadReader Environment m) - => String - -> a - -> m () -buildRaw fee buildTxBody = do - (_, rawTx ) <- (\a-> (a,a <> "tx.raw")) <$> getFolderPath Transactions - - liftIO $ cardano_cli - "transaction" - "build-raw" - (asArg buildTxBody) - "--fee" fee - "--out-file" rawTx - liftIO $ echo "transaction" - "build-raw" - (asArg buildTxBody) - "--fee" fee - "--out-file" rawTx - \ No newline at end of file diff --git a/tokenomia.cabal b/tokenomia.cabal index b19d39f5..dbf8d21e 100644 --- a/tokenomia.cabal +++ b/tokenomia.cabal @@ -73,6 +73,7 @@ library Tokenomia.Adapter.Cardano.CLI.Scripts Tokenomia.Adapter.Cardano.CLI.Wallet Tokenomia.Adapter.Data.List + Tokenomia.Adapter.Ledger.Value.CurrencySymbol Tokenomia.Common.Shell.InteractiveMenu Tokenomia.Common.Shell.Console Tokenomia.Adapter.Cardano.CLI.Environment From 64b7f7239b68e335f0579c33a4955d3111ecb59c Mon Sep 17 00:00:00 2001 From: NaadiQmmr Date: Fri, 26 Nov 2021 09:56:37 +0100 Subject: [PATCH 5/5] WIP --- src/Tokenomia/Adapter/Cardano/CLI/UTxO/Query.hs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Tokenomia/Adapter/Cardano/CLI/UTxO/Query.hs b/src/Tokenomia/Adapter/Cardano/CLI/UTxO/Query.hs index 4b543bf6..06f115ea 100644 --- a/src/Tokenomia/Adapter/Cardano/CLI/UTxO/Query.hs +++ b/src/Tokenomia/Adapter/Cardano/CLI/UTxO/Query.hs @@ -40,6 +40,7 @@ import Tokenomia.Adapter.Cardano.CLI.Value () import Tokenomia.Adapter.Cardano.CLI.UTxO import Tokenomia.Adapter.Cardano.Types import Tokenomia.Common.Shell.InteractiveMenu (askMenu, DisplayMenuItem, displayMenuItem) +import Ledger (Value) load SearchPath ["cardano-cli"] @@ -70,7 +71,7 @@ queryAssetClassList :: (MonadIO m, MonadReader Environment m) => Wallet -> m [As queryAssetClassList wallet = do utxos <- queryUTxOsFilterBy wallet (not . containingStrictlyADAs) if null utxos then return [] else do - let currencyList = getAssetClassList utxos -- TODO change this + let currencyList = getAssetClassList utxos return $ removeDuplicates currencyList queryAssetClassFilterBy :: @@ -97,9 +98,8 @@ instance DisplayMenuItem AssetClass where displayMenuItem _assetClass = show _tokenName <> " - " <> show _currencySymbol --TODO : showHashView for currencySymbol where (_currencySymbol, _tokenName) = unAssetClass _assetClass - -retrieveTotalAmountFromAsset :: AssetClass -> Wallet -> Integer -retrieveTotalAmountFromAsset asset Wallet{..} = do - allFlattenValues <- Prelude.map (\UTxO{..} -> flattenValue value) (query paymentAddress) - _flattenValues <- Prelude.filter (\(c, t, _) -> assetClass c t == asset) allFlattenValues +retrieveTotalAmountFromAsset :: CurrencySymbol -> NonEmpty UTxO -> Integer +retrieveTotalAmountFromAsset currency utxos = do + allFlattenValues <- fold (value <$> utxos) + _flattenValues <- Prelude.filter (\(c, _, _) -> c == currency) allFlattenValues return $ sum (Prelude.map (\(_, _, a) -> a) _flattenValues) \ No newline at end of file