Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Wallet] consolidating #46

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions app/Main.hs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
module Main (main) where



import qualified Tokenomia.CLI as Tokenomia

main :: IO ()
Expand Down
11 changes: 11 additions & 0 deletions cabal.project
Original file line number Diff line number Diff line change
Expand Up @@ -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
1 change: 1 addition & 0 deletions nix/pkgs/haskell/haskell.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand Down
101 changes: 101 additions & 0 deletions src/Tokenomia/Ada/Consolidate.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
{-# 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
( 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,
(|>),
ExecReference(SearchPath),
captureWords )

import Tokenomia.Adapter.Cardano.CLI.Wallet
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
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
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) adas )
, txOuts = ToWallet paymentAddress (lovelaceValueOf 1) :| []
, metadataMaybe = Nothing
, validitySlotRangeMaybe = Nothing
, tokenSupplyChangesMaybe = Nothing
, ..})
fees <- computeFees (Prelude.length adas ) 1
Slot synSlotAsInt <- getCurrentSlotSynced
buildRaw (show fees) (toCardanoCLIOptions TxBuild
{ wallet = wallet
, txIns = fromList (fmap (FromWallet . txOutRef) adas )
, txOuts = ToWallet paymentAddress (amount PlutusTx.Prelude.- lovelaceValueOf fees) :| []
, metadataMaybe = Nothing
, validitySlotRangeMaybe = Nothing
, tokenSupplyChangesMaybe = Nothing
, ..}
<> ["--invalid-hereafter", show (synSlotAsInt + 600)])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are you adding ["--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"
1 change: 0 additions & 1 deletion src/Tokenomia/Ada/Transfer.hs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE RecordWildCards #-}

module Tokenomia.Ada.Transfer
Expand Down
51 changes: 47 additions & 4 deletions src/Tokenomia/Adapter/Cardano/CLI/Transaction.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -33,6 +33,8 @@ module Tokenomia.Adapter.Cardano.CLI.Transaction
, sign_tx
, doubleSign_tx
, toCardanoCLIOptions
, computeFees
, buildRaw
) where


Expand All @@ -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 (..) )

Expand Down Expand Up @@ -129,7 +139,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]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should not add a "+" here, the format is "address x lovelace + y policyhash.tokenName" , something like that


instance ToCardanoCLIOptions ValiditySlotRange where
toCardanoCLIOptions (ValiditySlotRange (Slot x) (Slot y)) =
Expand Down Expand Up @@ -161,7 +171,7 @@ instance ToCardanoCLIOptions TxBuild where
<> toCardanoCLIOptions tokenSupplyChangesMaybe
<> toCardanoCLIOptions validitySlotRangeMaybe
<> toCardanoCLIOptions metadataMaybe

submit'
:: ( MonadIO m
, MonadReader Environment m
Expand Down Expand Up @@ -318,3 +328,36 @@ createMetadataFile message = do
liftIO (echo ("{\"" ++ show randomInt ++ "\":{\"message\":\"" ++ message ++ "\"}}")
&> (Truncate . fromString) metadataJsonFilepath)
return metadataJsonFilepath

computeFees :: (MonadIO m, MonadReader Environment m) => Int -> Int -> m Integer
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

computeFees :: (MonadIO m, MonadReader Environment m) => TxBuild -> m Ada

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)))

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
8 changes: 5 additions & 3 deletions src/Tokenomia/Adapter/Cardano/CLI/UTxO.hs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ module Tokenomia.Adapter.Cardano.CLI.UTxO
, containingStrictlyADAs
, containsCollateral
, showValue
, showTxOutRef
) where

import Tokenomia.Common.Shell.InteractiveMenu
Expand All @@ -33,14 +34,12 @@ 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)

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)

Expand Down Expand Up @@ -70,6 +69,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

Expand Down Expand Up @@ -117,4 +119,4 @@ instance FromCLI [UTxO] where
removeHeader = L.drop 2

filterEmptyLines :: [Text] -> [Text]
filterEmptyLines = L.filter (\a -> T.strip a /= mempty )
filterEmptyLines = L.filter (\a -> T.strip a /= mempty )
57 changes: 52 additions & 5 deletions src/Tokenomia/Adapter/Cardano/CLI/UTxO/Query.hs
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,32 @@
module Tokenomia.Adapter.Cardano.CLI.UTxO.Query
( query
, queryUTxOsFilterBy
, 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

import Ledger.Value

import Tokenomia.Adapter.Cardano.CLI.Wallet
import Tokenomia.Adapter.Cardano.CLI.Serialise
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 Tokenomia.Adapter.Cardano.Types
import Tokenomia.Common.Shell.InteractiveMenu (askMenu, DisplayMenuItem, displayMenuItem)
import Ledger (Value)

load SearchPath ["cardano-cli"]

query ::
query ::
( MonadIO m
, MonadReader Environment m )
=> Address
Expand All @@ -55,4 +60,46 @@ queryUTxOsFilterBy ::
=> Wallet
-> (UTxO -> Bool)
-> m [UTxO]
queryUTxOsFilterBy Wallet {..} f = filter f <$> query paymentAddress
queryUTxOsFilterBy Wallet{..} f = Prelude.filter f <$> query paymentAddress

getAssetClassList :: [UTxO] -> [AssetClass]
getAssetClassList [] = []
getAssetClassList (UTxO{..} : xs) =
Prelude.map (\(c, t, _) -> assetClass c t) (flattenValue value) ++ getAssetClassList xs

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 = getAssetClassList utxos
return $ removeDuplicates currencyList

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 <- queryAssetClassList wallet
case tokensList of
[] -> return Nothing
tokenList -> do
_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 :: 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)
5 changes: 3 additions & 2 deletions src/Tokenomia/Adapter/Cardano/Types.hs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE DerivingVia #-}
{-# 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)
7 changes: 7 additions & 0 deletions src/Tokenomia/Adapter/Data/List.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Tokenomia.Adapter.Data.List
(removeDuplicates) where
import Data.List

removeDuplicates :: (Ord a) => [a] -> [a]
removeDuplicates = Prelude.map Prelude.head . group . sort

7 changes: 7 additions & 0 deletions src/Tokenomia/Adapter/Ledger/Value/CurrencySymbol.hs
Original file line number Diff line number Diff line change
@@ -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)
Loading