Release preparation #90
11 changed files with 495 additions and 66 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -1,3 +1,7 @@
|
|||
.stack-work/
|
||||
*~
|
||||
dist-newstyle/
|
||||
zenith.db
|
||||
zenith.log
|
||||
zenith.db-shm
|
||||
zenith.db-wal
|
||||
|
|
11
CHANGELOG.md
11
CHANGELOG.md
|
@ -29,8 +29,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
- Validation of input of amount for sending in TUI
|
||||
|
||||
## [0.5.3.1-beta]
|
||||
|
||||
### Added
|
||||
|
||||
- Docker image
|
||||
|
||||
## [0.5.3.0-beta]
|
||||
|
||||
### Added
|
||||
|
||||
- Address Book functionality. Allows users to store frequently used zcash addresses and
|
||||
generate transactions using them.
|
||||
|
||||
### Changed
|
||||
|
||||
- Improved formatting of sync progress
|
||||
|
|
|
@ -21,6 +21,7 @@ Zenith is a wallet for the [Zebra](https://zfnd.org/zebra/) Zcash node . It has
|
|||
- Listing transactions for specific addresses, decoding memos for easy reading.
|
||||
- Copying addresses to the clipboard.
|
||||
- Sending transactions with shielded memo support.
|
||||
- Address Book for storing frequently used zcash addresses
|
||||
|
||||
## Installation
|
||||
|
||||
|
|
|
@ -221,8 +221,8 @@ main = do
|
|||
" ______ _ _ _ \n |___ / (_) | | | \n / / ___ _ __ _| |_| |__ \n / / / _ \\ '_ \\| | __| '_ \\ \n / /_| __/ | | | | |_| | | |\n /_____\\___|_| |_|_|\\__|_| |_|\n Zcash Full Node CLI v0.4.0"
|
||||
}
|
||||
(root nodeUser nodePwd)
|
||||
"cli" -> runZenithCLI myConfig
|
||||
"gui" -> runZenithGUI myConfig
|
||||
"tui" -> runZenithTUI myConfig
|
||||
"rescan" -> clearSync myConfig
|
||||
_ -> printUsage
|
||||
else printUsage
|
||||
|
@ -232,5 +232,5 @@ printUsage = do
|
|||
putStrLn "zenith [command] [parameters]\n"
|
||||
putStrLn "Available commands:"
|
||||
putStrLn "legacy\tLegacy CLI for zcashd"
|
||||
putStrLn "cli\tCLI for zebrad"
|
||||
putStrLn "tui\tTUI for zebrad"
|
||||
putStrLn "rescan\tRescan the existing wallet(s)"
|
||||
|
|
|
@ -10,8 +10,10 @@ import qualified Brick.BChan as BC
|
|||
import qualified Brick.Focus as F
|
||||
import Brick.Forms
|
||||
( Form(..)
|
||||
, FormFieldState
|
||||
, (@@=)
|
||||
, allFieldsValid
|
||||
, editShowableField
|
||||
, editShowableFieldWithValidate
|
||||
, editTextField
|
||||
, focusedFormInputAttr
|
||||
|
@ -40,6 +42,9 @@ import Brick.Widgets.Core
|
|||
, joinBorders
|
||||
, padAll
|
||||
, padBottom
|
||||
, padLeft
|
||||
, padTop
|
||||
, setAvailableSize
|
||||
, str
|
||||
, strWrap
|
||||
, strWrapWith
|
||||
|
@ -49,6 +54,7 @@ import Brick.Widgets.Core
|
|||
, updateAttrMap
|
||||
, vBox
|
||||
, vLimit
|
||||
, viewport
|
||||
, withAttr
|
||||
, withBorderStyle
|
||||
)
|
||||
|
@ -116,6 +122,10 @@ data Name
|
|||
| RecField
|
||||
| AmtField
|
||||
| MemoField
|
||||
| ABViewport
|
||||
| ABList
|
||||
| DescripField
|
||||
| AddressField
|
||||
deriving (Eq, Show, Ord)
|
||||
|
||||
data DialogInput = DialogInput
|
||||
|
@ -132,6 +142,13 @@ data SendInput = SendInput
|
|||
|
||||
makeLenses ''SendInput
|
||||
|
||||
data AdrBookEntry = AdrBookEntry
|
||||
{ _descrip :: !T.Text
|
||||
, _address :: !T.Text
|
||||
} deriving (Show)
|
||||
|
||||
makeLenses ''AdrBookEntry
|
||||
|
||||
data DialogType
|
||||
= WName
|
||||
| AName
|
||||
|
@ -140,6 +157,10 @@ data DialogType
|
|||
| ASelect
|
||||
| SendTx
|
||||
| Blank
|
||||
| AdrBook
|
||||
| AdrBookForm
|
||||
| AdrBookUpdForm
|
||||
| AdrBookDelForm
|
||||
|
||||
data DisplayType
|
||||
= AddrDisplay
|
||||
|
@ -149,6 +170,7 @@ data DisplayType
|
|||
| TxIdDisplay
|
||||
| SyncDisplay
|
||||
| SendDisplay
|
||||
| AdrBookEntryDisplay
|
||||
| BlankDisplay
|
||||
|
||||
data Tick
|
||||
|
@ -179,6 +201,9 @@ data State = State
|
|||
, _eventDispatch :: !(BC.BChan Tick)
|
||||
, _timer :: !Int
|
||||
, _txForm :: !(Form SendInput () Name)
|
||||
, _abAddresses :: !(L.List Name (Entity AddressBook))
|
||||
, _abForm :: !(Form AdrBookEntry () Name)
|
||||
, _abCurAdrs :: !T.Text -- used for address book CRUD operations
|
||||
, _sentTx :: !(Maybe HexString)
|
||||
, _unconfBalance :: !Integer
|
||||
}
|
||||
|
@ -194,14 +219,15 @@ drawUI s = [splashDialog s, helpDialog s, displayDialog s, inputDialog s, ui s]
|
|||
withBorderStyle unicode $
|
||||
B.borderWithLabel
|
||||
(str
|
||||
("Zenith - " <>
|
||||
(" Zenith - " <>
|
||||
show (st ^. network) <>
|
||||
" - " <>
|
||||
T.unpack
|
||||
(T.unpack
|
||||
(maybe
|
||||
"(None)"
|
||||
(\(_, w) -> zcashWalletName $ entityVal w)
|
||||
(L.listSelectedElement (st ^. wallets)))))
|
||||
(L.listSelectedElement (st ^. wallets)))) ++
|
||||
" "))
|
||||
(C.hCenter
|
||||
(str
|
||||
("Account: " ++
|
||||
|
@ -224,15 +250,18 @@ drawUI s = [splashDialog s, helpDialog s, displayDialog s, inputDialog s, ui s]
|
|||
else displayTaz (st ^. unconfBalance))) <=>
|
||||
listAddressBox "Addresses" (st ^. addresses) <+>
|
||||
B.vBorder <+>
|
||||
(C.hCenter (str ("Last block seen: " ++ show (st ^. syncBlock))) <=>
|
||||
listTxBox "Transactions" (st ^. network) (st ^. transactions))) <=>
|
||||
(C.hCenter
|
||||
(str ("Last block seen: " ++ show (st ^. syncBlock) ++ "\n")) <=>
|
||||
listTxBox " Transactions " (st ^. network) (st ^. transactions))) <=>
|
||||
C.hCenter
|
||||
(hBox
|
||||
[ capCommand "W" "allets"
|
||||
, capCommand "A" "ccounts"
|
||||
, capCommand "V" "iew address"
|
||||
, capCommand "S" "end Tx"
|
||||
, capCommand2 "Address " "B" "ook"
|
||||
, capCommand "Q" "uit"
|
||||
, capCommand "?" " Help"
|
||||
, str $ show (st ^. timer)
|
||||
])
|
||||
listBox :: Show e => String -> L.List Name e -> Widget Name
|
||||
|
@ -271,7 +300,7 @@ drawUI s = [splashDialog s, helpDialog s, displayDialog s, inputDialog s, ui s]
|
|||
(hBox
|
||||
[ capCommand "↑↓ " "move"
|
||||
, capCommand "↲ " "select"
|
||||
, capCommand "Tab " "->"
|
||||
, capCommand3 "" "Tab" " ->"
|
||||
])
|
||||
]
|
||||
listTxBox ::
|
||||
|
@ -287,19 +316,20 @@ drawUI s = [splashDialog s, helpDialog s, displayDialog s, inputDialog s, ui s]
|
|||
(hBox
|
||||
[ capCommand "↑↓ " "move"
|
||||
, capCommand "T" "x Display"
|
||||
, capCommand "Tab " "<-"
|
||||
, capCommand3 "" "Tab" " <-"
|
||||
])
|
||||
]
|
||||
helpDialog :: State -> Widget Name
|
||||
helpDialog st =
|
||||
if st ^. helpBox
|
||||
then D.renderDialog
|
||||
(D.dialog (Just (str "Commands")) Nothing 55)
|
||||
(D.dialog (Just (str " Commands ")) Nothing 55)
|
||||
(vBox ([C.hCenter $ str "Key", B.hBorder] <> keyList) <+>
|
||||
vBox ([str "Actions", B.hBorder] <> actionList))
|
||||
else emptyWidget
|
||||
where
|
||||
keyList = map (C.hCenter . str) ["?", "Esc", "w", "a", "v", "q"]
|
||||
keyList =
|
||||
map (C.hCenter . str) ["?", "Esc", "w", "a", "v", "s", "b", "q"]
|
||||
actionList =
|
||||
map
|
||||
(hLimit 40 . str)
|
||||
|
@ -308,6 +338,8 @@ drawUI s = [splashDialog s, helpDialog s, displayDialog s, inputDialog s, ui s]
|
|||
, "Switch wallets"
|
||||
, "Switch accounts"
|
||||
, "View address"
|
||||
, "Send Tx"
|
||||
, "Address Book"
|
||||
, "Quit"
|
||||
]
|
||||
inputDialog :: State -> Widget Name
|
||||
|
@ -315,20 +347,20 @@ drawUI s = [splashDialog s, helpDialog s, displayDialog s, inputDialog s, ui s]
|
|||
case st ^. dialogBox of
|
||||
WName ->
|
||||
D.renderDialog
|
||||
(D.dialog (Just (str "Create Wallet")) Nothing 50)
|
||||
(D.dialog (Just (str " Create Wallet ")) Nothing 50)
|
||||
(renderForm $ st ^. inputForm)
|
||||
AName ->
|
||||
D.renderDialog
|
||||
(D.dialog (Just (str "Create Account")) Nothing 50)
|
||||
(D.dialog (Just (str " Create Account ")) Nothing 50)
|
||||
(renderForm $ st ^. inputForm)
|
||||
AdName ->
|
||||
D.renderDialog
|
||||
(D.dialog (Just (str "Create Address")) Nothing 50)
|
||||
(D.dialog (Just (str " Create Address ")) Nothing 50)
|
||||
(renderForm $ st ^. inputForm)
|
||||
WSelect ->
|
||||
D.renderDialog
|
||||
(D.dialog (Just (str "Select Wallet")) Nothing 50)
|
||||
(selectListBox "Wallets" (st ^. wallets) listDrawWallet <=>
|
||||
(D.dialog (Just (str " Select Wallet ")) Nothing 50)
|
||||
(selectListBox " Wallets " (st ^. wallets) listDrawWallet <=>
|
||||
C.hCenter
|
||||
(hBox
|
||||
[ capCommand "↑↓ " "move"
|
||||
|
@ -339,8 +371,8 @@ drawUI s = [splashDialog s, helpDialog s, displayDialog s, inputDialog s, ui s]
|
|||
]))
|
||||
ASelect ->
|
||||
D.renderDialog
|
||||
(D.dialog (Just (str "Select Account")) Nothing 50)
|
||||
(selectListBox "Accounts" (st ^. accounts) listDrawAccount <=>
|
||||
(D.dialog (Just (str " Select Account ")) Nothing 50)
|
||||
(selectListBox " Accounts " (st ^. accounts) listDrawAccount <=>
|
||||
C.hCenter
|
||||
(hBox
|
||||
[ capCommand "↑↓ " "move"
|
||||
|
@ -350,11 +382,63 @@ drawUI s = [splashDialog s, helpDialog s, displayDialog s, inputDialog s, ui s]
|
|||
]))
|
||||
SendTx ->
|
||||
D.renderDialog
|
||||
(D.dialog (Just (str "Send Transaction")) Nothing 50)
|
||||
(D.dialog (Just (str " Send Transaction ")) Nothing 50)
|
||||
(renderForm (st ^. txForm) <=>
|
||||
C.hCenter
|
||||
(hBox [capCommand "↲ " "Send", capCommand "<esc> " "Cancel"]))
|
||||
Blank -> emptyWidget
|
||||
-- Address Book List
|
||||
AdrBook ->
|
||||
D.renderDialog
|
||||
(D.dialog (Just $ str " Address Book ") Nothing 60)
|
||||
(withAttr abDefAttr $
|
||||
setAvailableSize (50, 20) $
|
||||
viewport ABViewport BT.Vertical $
|
||||
vLimit 20 $
|
||||
hLimit 50 $
|
||||
vBox
|
||||
[ vLimit 16 $
|
||||
hLimit 50 $
|
||||
vBox $ [L.renderList listDrawAB True (s ^. abAddresses)]
|
||||
, padTop Max $
|
||||
vLimit 4 $
|
||||
hLimit 50 $
|
||||
withAttr abMBarAttr $
|
||||
vBox $
|
||||
[ C.hCenter $
|
||||
(capCommand "N" "ew Address" <+>
|
||||
capCommand "E" "dit Address" <+>
|
||||
capCommand3 "" "C" "opy Address")
|
||||
, C.hCenter $
|
||||
(capCommand "D" "elete Address" <+>
|
||||
capCommand "S" "end Zcash" <+> capCommand3 "E" "x" "it")
|
||||
]
|
||||
])
|
||||
-- Address Book new entry form
|
||||
AdrBookForm ->
|
||||
D.renderDialog
|
||||
(D.dialog (Just $ str " New Address Book Entry ") Nothing 50)
|
||||
(renderForm (st ^. abForm) <=>
|
||||
C.hCenter
|
||||
(hBox [capCommand "↲" " Save", capCommand3 "" "<Esc>" " Cancel"]))
|
||||
-- Address Book edit/update entry form
|
||||
AdrBookUpdForm ->
|
||||
D.renderDialog
|
||||
(D.dialog (Just $ str " Edit Address Book Entry ") Nothing 50)
|
||||
(renderForm (st ^. abForm) <=>
|
||||
C.hCenter
|
||||
(hBox [capCommand "↲" " Save", capCommand3 "" "<Esc>" " Cancel"]))
|
||||
-- Address Book edit/update entry form
|
||||
AdrBookDelForm ->
|
||||
D.renderDialog
|
||||
(D.dialog (Just $ str " Delete Address Book Entry ") Nothing 50)
|
||||
(renderForm (st ^. abForm) <=>
|
||||
C.hCenter
|
||||
(hBox
|
||||
[ capCommand "C" "onfirm delete"
|
||||
, capCommand3 "" "<Esc>" " Cancel"
|
||||
]))
|
||||
--
|
||||
splashDialog :: State -> Widget Name
|
||||
splashDialog st =
|
||||
if st ^. splashBox
|
||||
|
@ -366,9 +450,14 @@ drawUI s = [splashDialog s, helpDialog s, displayDialog s, inputDialog s, ui s]
|
|||
(str
|
||||
" _____ _ _ _ \n|__ /___ _ __ (_) |_| |__\n / // _ \\ '_ \\| | __| '_ \\\n / /| __/ | | | | |_| | | |\n/____\\___|_| |_|_|\\__|_| |_|") <=>
|
||||
C.hCenter
|
||||
(withAttr titleAttr (str "Zcash Wallet v0.5.3.0-beta")) <=>
|
||||
(withAttr titleAttr (str "Zcash Wallet v0.5.3.1-beta")) <=>
|
||||
C.hCenter (withAttr blinkAttr $ str "Press any key..."))
|
||||
else emptyWidget
|
||||
capCommand3 :: String -> String -> String -> Widget Name
|
||||
capCommand3 l h e = hBox [str l, withAttr titleAttr (str h), str e]
|
||||
capCommand2 :: String -> String -> String -> Widget Name
|
||||
capCommand2 l h e =
|
||||
hBox [str l, withAttr titleAttr (str h), str e, str " | "]
|
||||
capCommand :: String -> String -> Widget Name
|
||||
capCommand k comm = hBox [withAttr titleAttr (str k), str comm, str " | "]
|
||||
xCommand :: Widget Name
|
||||
|
@ -419,7 +508,7 @@ drawUI s = [splashDialog s, helpDialog s, displayDialog s, inputDialog s, ui s]
|
|||
Just (_, w) ->
|
||||
withBorderStyle unicodeBold $
|
||||
D.renderDialog
|
||||
(D.dialog (Just $ txt "Seed Phrase") Nothing 50)
|
||||
(D.dialog (Just $ txt " Seed Phrase ") Nothing 50)
|
||||
(padAll 1 $
|
||||
txtWrap $
|
||||
E.decodeUtf8Lenient $
|
||||
|
@ -428,12 +517,12 @@ drawUI s = [splashDialog s, helpDialog s, displayDialog s, inputDialog s, ui s]
|
|||
MsgDisplay ->
|
||||
withBorderStyle unicodeBold $
|
||||
D.renderDialog
|
||||
(D.dialog (Just $ txt "Message") Nothing 50)
|
||||
(D.dialog (Just $ txt " Message ") Nothing 50)
|
||||
(padAll 1 $ strWrap $ st ^. msg)
|
||||
TxIdDisplay ->
|
||||
withBorderStyle unicodeBold $
|
||||
D.renderDialog
|
||||
(D.dialog (Just $ txt "Success") Nothing 50)
|
||||
(D.dialog (Just $ txt " Success ") Nothing 50)
|
||||
(padAll 1 $
|
||||
(txt "Tx ID: " <+>
|
||||
txtWrapWith
|
||||
|
@ -446,7 +535,7 @@ drawUI s = [splashDialog s, helpDialog s, displayDialog s, inputDialog s, ui s]
|
|||
Just (_, tx) ->
|
||||
withBorderStyle unicodeBold $
|
||||
D.renderDialog
|
||||
(D.dialog (Just $ txt "Transaction") Nothing 50)
|
||||
(D.dialog (Just $ txt " Transaction ") Nothing 50)
|
||||
(padAll
|
||||
1
|
||||
(str
|
||||
|
@ -472,7 +561,7 @@ drawUI s = [splashDialog s, helpDialog s, displayDialog s, inputDialog s, ui s]
|
|||
SyncDisplay ->
|
||||
withBorderStyle unicodeBold $
|
||||
D.renderDialog
|
||||
(D.dialog (Just $ txt "Sync") Nothing 50)
|
||||
(D.dialog (Just $ txt " Sync ") Nothing 50)
|
||||
(padAll
|
||||
1
|
||||
(updateAttrMap
|
||||
|
@ -486,12 +575,28 @@ drawUI s = [splashDialog s, helpDialog s, displayDialog s, inputDialog s, ui s]
|
|||
SendDisplay ->
|
||||
withBorderStyle unicodeBold $
|
||||
D.renderDialog
|
||||
(D.dialog (Just $ txt "Sending Transaction") Nothing 50)
|
||||
(D.dialog (Just $ txt " Sending Transaction ") Nothing 50)
|
||||
(padAll
|
||||
1
|
||||
(strWrapWith
|
||||
(WrapSettings False True NoFill FillAfterFirst)
|
||||
(st ^. msg)))
|
||||
AdrBookEntryDisplay -> do
|
||||
case L.listSelectedElement $ st ^. abAddresses of
|
||||
Just (_, a) -> do
|
||||
let abentry =
|
||||
T.pack $
|
||||
" Descr: " ++
|
||||
T.unpack (addressBookAbdescrip (entityVal a)) ++
|
||||
"\n Address: " ++
|
||||
T.unpack (addressBookAbaddress (entityVal a))
|
||||
withBorderStyle unicodeBold $
|
||||
D.renderDialog
|
||||
(D.dialog (Just $ txt " Address Book Entry ") Nothing 60)
|
||||
(padAll 1 $
|
||||
txtWrapWith (WrapSettings False True NoFill FillAfterFirst) $
|
||||
abentry)
|
||||
_ -> emptyWidget
|
||||
BlankDisplay -> emptyWidget
|
||||
|
||||
mkInputForm :: DialogInput -> Form DialogInput e Name
|
||||
|
@ -516,6 +621,29 @@ mkSendForm bal =
|
|||
label s w =
|
||||
padBottom (Pad 1) $ vLimit 1 (hLimit 15 $ str s <+> fill ' ') <+> w
|
||||
|
||||
mkNewABForm :: AdrBookEntry -> Form AdrBookEntry e Name
|
||||
mkNewABForm =
|
||||
newForm
|
||||
[ label "Descrip: " @@= editTextField descrip DescripField (Just 1)
|
||||
, label "Address: " @@= editTextField address AddressField (Just 1)
|
||||
]
|
||||
where
|
||||
label s w =
|
||||
padBottom (Pad 1) $ vLimit 1 (hLimit 10 $ str s <+> fill ' ') <+> w
|
||||
|
||||
isRecipientValid :: T.Text -> Bool
|
||||
isRecipientValid a =
|
||||
case isValidUnifiedAddress (E.encodeUtf8 a) of
|
||||
Just _a1 -> True
|
||||
Nothing ->
|
||||
isValidShieldedAddress (E.encodeUtf8 a) ||
|
||||
(case decodeTransparentAddress (E.encodeUtf8 a) of
|
||||
Just _a3 -> True
|
||||
Nothing ->
|
||||
case decodeExchangeAddress a of
|
||||
Just _a4 -> True
|
||||
Nothing -> False)
|
||||
|
||||
listDrawElement :: (Show a) => Bool -> a -> Widget Name
|
||||
listDrawElement sel a =
|
||||
let selStr s =
|
||||
|
@ -572,6 +700,14 @@ listDrawTx znet sel tx =
|
|||
then withAttr customAttr (txt $ "> " <> s)
|
||||
else txt $ " " <> s
|
||||
|
||||
listDrawAB :: Bool -> Entity AddressBook -> Widget Name
|
||||
listDrawAB sel ab =
|
||||
let selStr s =
|
||||
if sel
|
||||
then withAttr abSelAttr (txt $ " " <> s)
|
||||
else txt $ " " <> s
|
||||
in selStr $ addressBookAbdescrip (entityVal ab)
|
||||
|
||||
customAttr :: A.AttrName
|
||||
customAttr = L.listSelectedAttr <> A.attrName "custom"
|
||||
|
||||
|
@ -590,6 +726,18 @@ barDoneAttr = A.attrName "done"
|
|||
barToDoAttr :: A.AttrName
|
||||
barToDoAttr = A.attrName "remaining"
|
||||
|
||||
abDefAttr :: A.AttrName
|
||||
abDefAttr = A.attrName "abdefault"
|
||||
|
||||
abSelAttr :: A.AttrName
|
||||
abSelAttr = A.attrName "abselected"
|
||||
|
||||
abMBarAttr :: A.AttrName
|
||||
abMBarAttr = A.attrName "menubar"
|
||||
|
||||
validBarValue :: Float -> Float
|
||||
validBarValue = clamp 0 1
|
||||
|
||||
scanZebra :: T.Text -> T.Text -> Int -> Int -> BC.BChan Tick -> IO ()
|
||||
scanZebra dbP zHost zPort b eChan = do
|
||||
_ <- liftIO $ initDb dbP
|
||||
|
@ -627,8 +775,7 @@ scanZebra dbP zHost zPort b eChan = do
|
|||
"getblock"
|
||||
[Data.Aeson.String $ T.pack $ show bl, jsonNumber 1]
|
||||
case r of
|
||||
Left e1 -> do
|
||||
liftIO $ BC.writeBChan eChan $ TickMsg e1
|
||||
Left e1 -> liftIO $ BC.writeBChan eChan $ TickMsg e1
|
||||
Right blk -> do
|
||||
r2 <-
|
||||
liftIO $
|
||||
|
@ -638,8 +785,7 @@ scanZebra dbP zHost zPort b eChan = do
|
|||
"getblock"
|
||||
[Data.Aeson.String $ T.pack $ show bl, jsonNumber 0]
|
||||
case r2 of
|
||||
Left e2 -> do
|
||||
liftIO $ BC.writeBChan eChan $ TickMsg e2
|
||||
Left e2 -> liftIO $ BC.writeBChan eChan $ TickMsg e2
|
||||
Right hb -> do
|
||||
let blockTime = getBlockTime hb
|
||||
mapM_ (runNoLoggingT . processTx zHost zPort blockTime pool) $
|
||||
|
@ -666,8 +812,8 @@ appEvent (BT.AppEvent t) = do
|
|||
TxDisplay -> return ()
|
||||
TxIdDisplay -> return ()
|
||||
SyncDisplay -> return ()
|
||||
SendDisplay -> do
|
||||
BT.modify $ set msg m
|
||||
SendDisplay -> BT.modify $ set msg m
|
||||
AdrBookEntryDisplay -> return ()
|
||||
BlankDisplay -> return ()
|
||||
TickTx txid -> do
|
||||
BT.modify $ set sentTx (Just txid)
|
||||
|
@ -680,6 +826,7 @@ appEvent (BT.AppEvent t) = do
|
|||
TxDisplay -> return ()
|
||||
TxIdDisplay -> return ()
|
||||
SendDisplay -> return ()
|
||||
AdrBookEntryDisplay -> return ()
|
||||
SyncDisplay -> do
|
||||
if s ^. barValue == 1.0
|
||||
then do
|
||||
|
@ -712,6 +859,10 @@ appEvent (BT.AppEvent t) = do
|
|||
WSelect -> return ()
|
||||
ASelect -> return ()
|
||||
SendTx -> return ()
|
||||
AdrBook -> return ()
|
||||
AdrBookForm -> return ()
|
||||
AdrBookUpdForm -> return ()
|
||||
AdrBookDelForm -> return ()
|
||||
Blank -> do
|
||||
if s ^. timer == 90
|
||||
then do
|
||||
|
@ -729,8 +880,7 @@ appEvent (BT.AppEvent t) = do
|
|||
(s ^. eventDispatch)
|
||||
BT.modify $ set timer 0
|
||||
return ()
|
||||
else do
|
||||
BT.modify $ set timer $ 1 + s ^. timer
|
||||
else BT.modify $ set timer $ 1 + s ^. timer
|
||||
appEvent (BT.VtyEvent e) = do
|
||||
r <- F.focusGetCurrent <$> use focusRing
|
||||
s <- BT.get
|
||||
|
@ -739,8 +889,7 @@ appEvent (BT.VtyEvent e) = do
|
|||
else if s ^. helpBox
|
||||
then do
|
||||
case e of
|
||||
V.EvKey V.KEsc [] -> do
|
||||
BT.modify $ set helpBox False
|
||||
V.EvKey V.KEsc [] -> BT.modify $ set helpBox False
|
||||
_ev -> return ()
|
||||
else do
|
||||
case s ^. displayBox of
|
||||
|
@ -811,6 +960,7 @@ appEvent (BT.VtyEvent e) = do
|
|||
_ev -> return ()
|
||||
SendDisplay -> BT.modify $ set displayBox BlankDisplay
|
||||
SyncDisplay -> BT.modify $ set displayBox BlankDisplay
|
||||
AdrBookEntryDisplay -> BT.modify $ set displayBox BlankDisplay
|
||||
BlankDisplay -> do
|
||||
case s ^. dialogBox of
|
||||
WName -> do
|
||||
|
@ -873,7 +1023,7 @@ appEvent (BT.VtyEvent e) = do
|
|||
V.EvKey (V.KChar 'n') [] -> do
|
||||
BT.modify $
|
||||
set inputForm $
|
||||
updateFormState (DialogInput "New Wallet") $
|
||||
updateFormState (DialogInput " New Wallet ") $
|
||||
s ^. inputForm
|
||||
BT.modify $ set dialogBox WName
|
||||
V.EvKey (V.KChar 's') [] ->
|
||||
|
@ -890,7 +1040,7 @@ appEvent (BT.VtyEvent e) = do
|
|||
V.EvKey (V.KChar 'n') [] -> do
|
||||
BT.modify $
|
||||
set inputForm $
|
||||
updateFormState (DialogInput "New Account") $
|
||||
updateFormState (DialogInput " New Account ") $
|
||||
s ^. inputForm
|
||||
BT.modify $ set dialogBox AName
|
||||
ev -> BT.zoom accounts $ L.handleListEvent ev
|
||||
|
@ -951,7 +1101,7 @@ appEvent (BT.VtyEvent e) = do
|
|||
BT.modify $ set msg "Invalid inputs"
|
||||
BT.modify $ set displayBox MsgDisplay
|
||||
BT.modify $ set dialogBox Blank
|
||||
ev -> do
|
||||
ev ->
|
||||
BT.zoom txForm $ do
|
||||
handleFormEvent (BT.VtyEvent ev)
|
||||
fs <- BT.gets formState
|
||||
|
@ -959,6 +1109,189 @@ appEvent (BT.VtyEvent e) = do
|
|||
setFieldValid
|
||||
(isRecipientValid (fs ^. sendTo))
|
||||
RecField
|
||||
AdrBook -> do
|
||||
case e of
|
||||
V.EvKey (V.KChar 'x') [] ->
|
||||
BT.modify $ set dialogBox Blank
|
||||
V.EvKey (V.KChar 'c') []
|
||||
-- Copy Address to Clipboard
|
||||
-> do
|
||||
case L.listSelectedElement $ s ^. abAddresses of
|
||||
Just (_, a) -> do
|
||||
liftIO $
|
||||
setClipboard $
|
||||
T.unpack $ addressBookAbaddress (entityVal a)
|
||||
BT.modify $
|
||||
set msg $
|
||||
"Address copied to Clipboard from >>\n" ++
|
||||
T.unpack (addressBookAbdescrip (entityVal a))
|
||||
BT.modify $ set displayBox MsgDisplay
|
||||
_ -> do
|
||||
BT.modify $
|
||||
set msg "Error while copying the address!!"
|
||||
BT.modify $ set displayBox MsgDisplay
|
||||
-- Send Zcash transaction
|
||||
V.EvKey (V.KChar 's') [] -> do
|
||||
case L.listSelectedElement $ s ^. abAddresses of
|
||||
Just (_, a) -> do
|
||||
BT.modify $
|
||||
set txForm $
|
||||
mkSendForm
|
||||
(s ^. balance)
|
||||
(SendInput
|
||||
(addressBookAbaddress (entityVal a))
|
||||
0.0
|
||||
"")
|
||||
BT.modify $ set dialogBox SendTx
|
||||
_ -> do
|
||||
BT.modify $
|
||||
set msg "No receiver address available!!"
|
||||
BT.modify $ set displayBox MsgDisplay
|
||||
-- Edit an entry in Address Book
|
||||
V.EvKey (V.KChar 'e') [] -> do
|
||||
case L.listSelectedElement $ s ^. abAddresses of
|
||||
Just (_, a) -> do
|
||||
BT.modify $
|
||||
set
|
||||
abCurAdrs
|
||||
(addressBookAbaddress (entityVal a))
|
||||
BT.modify $
|
||||
set abForm $
|
||||
mkNewABForm
|
||||
(AdrBookEntry
|
||||
(addressBookAbdescrip (entityVal a))
|
||||
(addressBookAbaddress (entityVal a)))
|
||||
BT.modify $ set dialogBox AdrBookUpdForm
|
||||
_ -> do
|
||||
BT.modify $ set dialogBox Blank
|
||||
-- Delete an entry from Address Book
|
||||
V.EvKey (V.KChar 'd') [] -> do
|
||||
case L.listSelectedElement $ s ^. abAddresses of
|
||||
Just (_, a) -> do
|
||||
BT.modify $
|
||||
set
|
||||
abCurAdrs
|
||||
(addressBookAbaddress (entityVal a))
|
||||
BT.modify $
|
||||
set abForm $
|
||||
mkNewABForm
|
||||
(AdrBookEntry
|
||||
(addressBookAbdescrip (entityVal a))
|
||||
(addressBookAbaddress (entityVal a)))
|
||||
BT.modify $ set dialogBox AdrBookDelForm
|
||||
_ -> do
|
||||
BT.modify $ set dialogBox Blank
|
||||
-- Create a new entry in Address Book
|
||||
V.EvKey (V.KChar 'n') [] -> do
|
||||
BT.modify $
|
||||
set abForm $ mkNewABForm (AdrBookEntry "" "")
|
||||
BT.modify $ set dialogBox AdrBookForm
|
||||
-- Show AddressBook entry data
|
||||
V.EvKey V.KEnter [] -> do
|
||||
BT.modify $ set displayBox AdrBookEntryDisplay
|
||||
-- Process any other event
|
||||
ev -> BT.zoom abAddresses $ L.handleListEvent ev
|
||||
-- Process new address book entry
|
||||
AdrBookForm -> do
|
||||
case e of
|
||||
V.EvKey V.KEsc [] -> BT.modify $ set dialogBox AdrBook
|
||||
V.EvKey V.KEnter [] -> do
|
||||
pool <- liftIO $ runNoLoggingT $ initPool $ s ^. dbPath
|
||||
fs <- BT.zoom abForm $ BT.gets formState
|
||||
let idescr = T.unpack $ T.strip (fs ^. descrip)
|
||||
let iabadr = fs ^. address
|
||||
if not (null idescr) && isRecipientValid iabadr
|
||||
then do
|
||||
res <-
|
||||
liftIO $
|
||||
saveAdrsInAdrBook pool $
|
||||
AddressBook
|
||||
(ZcashNetDB (s ^. network))
|
||||
(fs ^. descrip)
|
||||
(fs ^. address)
|
||||
case res of
|
||||
Nothing -> do
|
||||
BT.modify $
|
||||
set
|
||||
msg
|
||||
("AddressBook Entry already exists: " ++
|
||||
T.unpack (fs ^. address))
|
||||
BT.modify $ set displayBox MsgDisplay
|
||||
Just _ -> do
|
||||
BT.modify $
|
||||
set
|
||||
msg
|
||||
("New AddressBook entry created!!\n" ++
|
||||
T.unpack (fs ^. address))
|
||||
BT.modify $ set displayBox MsgDisplay
|
||||
-- case end
|
||||
s' <- liftIO $ refreshAddressBook s
|
||||
BT.put s'
|
||||
BT.modify $ set dialogBox AdrBook
|
||||
else do
|
||||
BT.modify $ set msg "Invalid or missing data!!: "
|
||||
BT.modify $ set displayBox MsgDisplay
|
||||
BT.modify $ set dialogBox AdrBookForm
|
||||
ev ->
|
||||
BT.zoom abForm $ do
|
||||
handleFormEvent (BT.VtyEvent ev)
|
||||
fs <- BT.gets formState
|
||||
BT.modify $
|
||||
setFieldValid
|
||||
(isRecipientValid (fs ^. address))
|
||||
AddressField
|
||||
AdrBookUpdForm -> do
|
||||
case e of
|
||||
V.EvKey V.KEsc [] -> BT.modify $ set dialogBox AdrBook
|
||||
V.EvKey V.KEnter [] -> do
|
||||
pool <- liftIO $ runNoLoggingT $ initPool $ s ^. dbPath
|
||||
fs <- BT.zoom abForm $ BT.gets formState
|
||||
let idescr = T.unpack $ T.strip (fs ^. descrip)
|
||||
let iabadr = fs ^. address
|
||||
if not (null idescr) && isRecipientValid iabadr
|
||||
then do
|
||||
res <-
|
||||
liftIO $
|
||||
updateAdrsInAdrBook
|
||||
pool
|
||||
(fs ^. descrip)
|
||||
(fs ^. address)
|
||||
(s ^. abCurAdrs)
|
||||
BT.modify $
|
||||
set
|
||||
msg
|
||||
("AddressBook entry modified!!\n" ++
|
||||
T.unpack (fs ^. address))
|
||||
BT.modify $ set displayBox MsgDisplay
|
||||
-- case end
|
||||
s' <- liftIO $ refreshAddressBook s
|
||||
BT.put s'
|
||||
BT.modify $ set dialogBox AdrBook
|
||||
else do
|
||||
BT.modify $ set msg "Invalid or missing data!!: "
|
||||
BT.modify $ set displayBox MsgDisplay
|
||||
BT.modify $ set dialogBox AdrBookForm
|
||||
ev ->
|
||||
BT.zoom abForm $ do
|
||||
handleFormEvent (BT.VtyEvent ev)
|
||||
fs <- BT.gets formState
|
||||
BT.modify $
|
||||
setFieldValid
|
||||
(isRecipientValid (fs ^. address))
|
||||
AddressField
|
||||
-- Process delete AddresBook entry
|
||||
AdrBookDelForm -> do
|
||||
case e of
|
||||
V.EvKey V.KEsc [] -> BT.modify $ set dialogBox AdrBook
|
||||
V.EvKey (V.KChar 'c') [] -> do
|
||||
pool <- liftIO $ runNoLoggingT $ initPool $ s ^. dbPath
|
||||
fs <- BT.zoom abForm $ BT.gets formState
|
||||
res <- liftIO $ deleteAdrsFromAB pool (fs ^. address)
|
||||
s' <- liftIO $ refreshAddressBook s
|
||||
BT.put s'
|
||||
BT.modify $ set dialogBox AdrBook
|
||||
ev -> BT.modify $ set dialogBox AdrBookDelForm
|
||||
-- Process any other event
|
||||
Blank -> do
|
||||
case e of
|
||||
V.EvKey (V.KChar '\t') [] -> focusRing %= F.focusNext
|
||||
|
@ -982,12 +1315,16 @@ appEvent (BT.VtyEvent e) = do
|
|||
set txForm $
|
||||
mkSendForm (s ^. balance) (SendInput "" 0.0 "")
|
||||
BT.modify $ set dialogBox SendTx
|
||||
V.EvKey (V.KChar 'b') [] ->
|
||||
BT.modify $ set dialogBox AdrBook
|
||||
ev ->
|
||||
case r of
|
||||
Just AList ->
|
||||
BT.zoom addresses $ L.handleListEvent ev
|
||||
Just TList ->
|
||||
BT.zoom transactions $ L.handleListEvent ev
|
||||
Just ABList ->
|
||||
BT.zoom abAddresses $ L.handleListEvent ev
|
||||
_anyName -> return ()
|
||||
where
|
||||
printMsg :: String -> BT.EventM Name State ()
|
||||
|
@ -1007,11 +1344,14 @@ theMap =
|
|||
, (blinkAttr, style V.blink)
|
||||
, (focusedFormInputAttr, V.white `on` V.blue)
|
||||
, (invalidFormInputAttr, V.red `on` V.black)
|
||||
, (E.editAttr, V.white `on` V.blue)
|
||||
, (E.editFocusedAttr, V.blue `on` V.white)
|
||||
, (E.editAttr, V.white `on` V.black)
|
||||
, (E.editFocusedAttr, V.black `on` V.white)
|
||||
, (baseAttr, bg V.brightBlack)
|
||||
, (barDoneAttr, V.white `on` V.blue)
|
||||
, (barToDoAttr, V.white `on` V.black)
|
||||
, (abDefAttr, V.white `on` V.blue)
|
||||
, (abSelAttr, V.black `on` V.white)
|
||||
, (abMBarAttr, V.white `on` V.black)
|
||||
]
|
||||
|
||||
theApp :: M.App State Tick Name
|
||||
|
@ -1024,8 +1364,8 @@ theApp =
|
|||
, M.appAttrMap = const theMap
|
||||
}
|
||||
|
||||
runZenithCLI :: Config -> IO ()
|
||||
runZenithCLI config = do
|
||||
runZenithTUI :: Config -> IO ()
|
||||
runZenithTUI config = do
|
||||
let host = c_zebraHost config
|
||||
let port = c_zebraPort config
|
||||
let dbFilePath = c_dbPath config
|
||||
|
@ -1057,6 +1397,7 @@ runZenithCLI config = do
|
|||
if not (null walList)
|
||||
then zcashWalletLastSync $ entityVal $ head walList
|
||||
else 0
|
||||
abookList <- getAdrBook pool $ zgb_net chainInfo
|
||||
bal <-
|
||||
if not (null accList)
|
||||
then getBalance pool $ entityKey $ head accList
|
||||
|
@ -1101,6 +1442,9 @@ runZenithCLI config = do
|
|||
eventChan
|
||||
0
|
||||
(mkSendForm 0 $ SendInput "" 0.0 "")
|
||||
(L.list ABList (Vec.fromList abookList) 1)
|
||||
(mkNewABForm (AdrBookEntry "" ""))
|
||||
""
|
||||
Nothing
|
||||
uBal
|
||||
Left e -> do
|
||||
|
@ -1163,14 +1507,13 @@ addNewWallet n s = do
|
|||
let netName = s ^. network
|
||||
r <- saveWallet pool $ ZcashWallet n (ZcashNetDB netName) (PhraseDB sP) bH 0
|
||||
case r of
|
||||
Nothing -> do
|
||||
return $ s & msg .~ ("Wallet already exists: " ++ T.unpack n)
|
||||
Nothing -> return $ s & msg .~ ("Wallet already exists: " ++ T.unpack n)
|
||||
Just _ -> do
|
||||
wL <- getWallets pool netName
|
||||
let aL =
|
||||
L.listFindBy (\x -> zcashWalletName (entityVal x) == n) $
|
||||
L.listReplace (Vec.fromList wL) (Just 0) (s ^. wallets)
|
||||
return $ (s & wallets .~ aL) & msg .~ "Created new wallet: " ++ T.unpack n
|
||||
return $ s & wallets .~ aL & msg .~ "Created new wallet: " ++ T.unpack n
|
||||
|
||||
addNewAccount :: T.Text -> State -> IO State
|
||||
addNewAccount n s = do
|
||||
|
@ -1189,19 +1532,18 @@ addNewAccount n s = do
|
|||
try $ createZcashAccount n (aL' + 1) selWallet :: IO
|
||||
(Either IOError ZcashAccount)
|
||||
case zA of
|
||||
Left e -> return $ s & msg .~ ("Error: " ++ show e)
|
||||
Left e -> return $ s & msg .~ "Error: " ++ show e
|
||||
Right zA' -> do
|
||||
r <- saveAccount pool zA'
|
||||
case r of
|
||||
Nothing ->
|
||||
return $ s & msg .~ ("Account already exists: " ++ T.unpack n)
|
||||
Nothing -> return $ s & msg .~ "Account already exists: " ++ T.unpack n
|
||||
Just x -> do
|
||||
aL <- runNoLoggingT $ getAccounts pool (entityKey selWallet)
|
||||
let nL =
|
||||
L.listMoveToElement x $
|
||||
L.listReplace (Vec.fromList aL) (Just 0) (s ^. accounts)
|
||||
return $
|
||||
(s & accounts .~ nL) & msg .~ "Created new account: " ++ T.unpack n
|
||||
s & accounts .~ nL & msg .~ "Created new account: " ++ T.unpack n
|
||||
|
||||
refreshAccount :: State -> IO State
|
||||
refreshAccount s = do
|
||||
|
@ -1259,6 +1601,21 @@ refreshTxs s = do
|
|||
let tL' = L.listReplace (Vec.fromList tList) (Just 0) (s ^. transactions)
|
||||
return $ s & transactions .~ tL'
|
||||
|
||||
refreshAddressBook :: State -> IO State
|
||||
refreshAddressBook s = do
|
||||
pool <- runNoLoggingT $ initPool $ s ^. dbPath
|
||||
selAddress <-
|
||||
do case L.listSelectedElement $ s ^. abAddresses of
|
||||
Nothing -> do
|
||||
let fAdd =
|
||||
L.listSelectedElement $
|
||||
L.listMoveToBeginning $ s ^. abAddresses
|
||||
return fAdd
|
||||
Just a2 -> return $ Just a2
|
||||
abookList <- getAdrBook pool (s ^. network)
|
||||
let tL' = L.listReplace (Vec.fromList abookList) (Just 0) (s ^. abAddresses)
|
||||
return $ s & abAddresses .~ tL'
|
||||
|
||||
addNewAddress :: T.Text -> Scope -> State -> IO State
|
||||
addNewAddress n scope s = do
|
||||
pool <- runNoLoggingT $ initPool $ s ^. dbPath
|
||||
|
@ -1276,19 +1633,18 @@ addNewAddress n scope s = do
|
|||
try $ createWalletAddress n (maxAddr + 1) (s ^. network) scope selAccount :: IO
|
||||
(Either IOError WalletAddress)
|
||||
case uA of
|
||||
Left e -> return $ s & msg .~ ("Error: " ++ show e)
|
||||
Left e -> return $ s & msg .~ "Error: " ++ show e
|
||||
Right uA' -> do
|
||||
nAddr <- saveAddress pool uA'
|
||||
case nAddr of
|
||||
Nothing ->
|
||||
return $ s & msg .~ ("Address already exists: " ++ T.unpack n)
|
||||
Nothing -> return $ s & msg .~ "Address already exists: " ++ T.unpack n
|
||||
Just x -> do
|
||||
addrL <- runNoLoggingT $ getAddresses pool (entityKey selAccount)
|
||||
let nL =
|
||||
L.listMoveToElement x $
|
||||
L.listReplace (Vec.fromList addrL) (Just 0) (s ^. addresses)
|
||||
return $
|
||||
(s & addresses .~ nL) & msg .~ "Created new address: " ++
|
||||
s & addresses .~ nL & msg .~ "Created new address: " ++
|
||||
T.unpack n ++
|
||||
"(" ++
|
||||
T.unpack (showAddress $ walletAddressUAddress $ entityVal x) ++ ")"
|
||||
|
|
|
@ -24,7 +24,7 @@ import Data.Binary.Get hiding (getBytes)
|
|||
import qualified Data.ByteString as BS
|
||||
import qualified Data.ByteString.Lazy as LBS
|
||||
import Data.Digest.Pure.MD5
|
||||
import Data.HexString (HexString, hexString, toBytes)
|
||||
import Data.HexString (HexString, hexString, toBytes, toText)
|
||||
import Data.List
|
||||
import Data.Maybe (fromJust)
|
||||
import Data.Pool (Pool)
|
||||
|
@ -574,6 +574,7 @@ prepareTx pool zebraHost zebraPort zn za bh amt ua memo = do
|
|||
zn
|
||||
(bh + 3)
|
||||
True
|
||||
logDebugN $ T.pack $ show tx
|
||||
return tx
|
||||
where
|
||||
makeOutgoing ::
|
||||
|
|
|
@ -254,6 +254,12 @@ share
|
|||
name T.Text
|
||||
UniqueQr address version
|
||||
deriving Show Eq
|
||||
AddressBook
|
||||
network ZcashNetDB
|
||||
abdescrip T.Text
|
||||
abaddress T.Text
|
||||
UniqueABA abaddress
|
||||
deriving Show Eq
|
||||
|]
|
||||
|
||||
-- * Database functions
|
||||
|
@ -1666,5 +1672,55 @@ readUnifiedAddressDB :: WalletAddress -> Maybe UnifiedAddress
|
|||
readUnifiedAddressDB =
|
||||
isValidUnifiedAddress . TE.encodeUtf8 . getUA . walletAddressUAddress
|
||||
|
||||
-- | Get list of external zcash addresses from database
|
||||
getAdrBook :: ConnectionPool -> ZcashNet -> IO [Entity AddressBook]
|
||||
getAdrBook pool n =
|
||||
runNoLoggingT $
|
||||
PS.retryOnBusy $
|
||||
flip PS.runSqlPool pool $ do
|
||||
select $ do
|
||||
adrbook <- from $ table @AddressBook
|
||||
where_ (adrbook ^. AddressBookNetwork ==. val (ZcashNetDB n))
|
||||
pure adrbook
|
||||
|
||||
-- | Save a new address into AddressBook
|
||||
saveAdrsInAdrBook ::
|
||||
ConnectionPool -- ^ The database path to use
|
||||
-> AddressBook -- ^ The address to add to the database
|
||||
-> IO (Maybe (Entity AddressBook))
|
||||
saveAdrsInAdrBook pool a =
|
||||
runNoLoggingT $
|
||||
PS.retryOnBusy $ flip PS.runSqlPool pool $ insertUniqueEntity a
|
||||
|
||||
-- | Update an existing address into AddressBook
|
||||
updateAdrsInAdrBook :: ConnectionPool -> T.Text -> T.Text -> T.Text -> IO ()
|
||||
updateAdrsInAdrBook pool d a ia = do
|
||||
runNoLoggingT $
|
||||
PS.retryOnBusy $
|
||||
flip PS.runSqlPool pool $ do
|
||||
update $ \ab -> do
|
||||
set ab [AddressBookAbdescrip =. val d, AddressBookAbaddress =. val a]
|
||||
where_ $ ab ^. AddressBookAbaddress ==. val ia
|
||||
|
||||
-- | Get one AddrssBook record using the Address as a key
|
||||
-- getABookRec :: ConnectionPool -> T.Tex t -> IO (Maybe (Entity AddressBook))
|
||||
-- getABookRec pool a = do
|
||||
-- runNoLoggingT $
|
||||
-- PS.retryOnBusy $
|
||||
-- flip PS.runSqlPool pool $
|
||||
-- select $ do
|
||||
-- adrbook <- from $ table @AddressBook
|
||||
-- where_ ((adrbook ^. AddressBookAbaddress) ==. val a)
|
||||
-- return adrbook
|
||||
-- | delete an existing address from AddressBook
|
||||
deleteAdrsFromAB :: ConnectionPool -> T.Text -> IO ()
|
||||
deleteAdrsFromAB pool ia = do
|
||||
runNoLoggingT $
|
||||
PS.retryOnBusy $
|
||||
flip PS.runSqlPool pool $ do
|
||||
delete $ do
|
||||
ab <- from $ table @AddressBook
|
||||
where_ (ab ^. AddressBookAbaddress ==. val ia)
|
||||
|
||||
rmdups :: Ord a => [a] -> [a]
|
||||
rmdups = map head . group . sort
|
||||
|
|
|
@ -37,18 +37,18 @@ jsonNumber i = Number $ scientific (fromIntegral i) 0
|
|||
-- | Helper function to display small amounts of ZEC
|
||||
displayZec :: Integer -> String
|
||||
displayZec s
|
||||
| abs s < 100 = show s ++ " zats "
|
||||
| abs s < 100000 = show (fromIntegral s / 100) ++ " μZEC "
|
||||
| abs s < 100000000 = show (fromIntegral s / 100000) ++ " mZEC "
|
||||
| abs s < 100 = show s ++ " zats"
|
||||
| abs s < 100000 = show (fromIntegral s / 100) ++ " μZEC"
|
||||
| abs s < 100000000 = show (fromIntegral s / 100000) ++ " mZEC"
|
||||
| otherwise = show (fromIntegral s / 100000000) ++ " ZEC "
|
||||
|
||||
-- | Helper function to display small amounts of ZEC
|
||||
displayTaz :: Integer -> String
|
||||
displayTaz s
|
||||
| abs s < 100 = show s ++ " tazs "
|
||||
| abs s < 100000 = show (fromIntegral s / 100) ++ " μTAZ "
|
||||
| abs s < 100000000 = show (fromIntegral s / 100000) ++ " mTAZ "
|
||||
| otherwise = show (fromIntegral s / 100000000) ++ " TAZ "
|
||||
| abs s < 100 = show s ++ " tazs"
|
||||
| abs s < 100000 = show (fromIntegral s / 100) ++ " μTAZ"
|
||||
| abs s < 100000000 = show (fromIntegral s / 100000) ++ " mTAZ"
|
||||
| otherwise = show (fromIntegral s / 100000000) ++ " TAZ"
|
||||
|
||||
displayAmount :: ZcashNet -> Integer -> T.Text
|
||||
displayAmount n a =
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
cabal-version: 3.0
|
||||
name: zenith
|
||||
version: 0.5.3.0-beta
|
||||
version: 0.5.3.1-beta
|
||||
license: MIT
|
||||
license-file: LICENSE
|
||||
author: Rene Vergara
|
||||
|
|
BIN
zenith_er.bmp
Normal file
BIN
zenith_er.bmp
Normal file
Binary file not shown.
Before Width: | Height: | Size: 7.7 MiB After Width: | Height: | Size: 7.7 MiB |
BIN
zenith_er.png
Normal file
BIN
zenith_er.png
Normal file
Binary file not shown.
Before Width: | Height: | Size: 329 KiB After Width: | Height: | Size: 329 KiB |
Loading…
Reference in a new issue