diff --git a/README.md b/README.md index fa668c3ee..a8f8bbde8 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ cryptopower [options] ``` - Run `./cryptopower --network=testnet` to run cryptopower on the testnet network. +- Run `./cryptopower --network=dextest` to run cryptopower with the decred dex simnet harness. - Run `cryptopower -h` or `cryptopower help` to get general information of commands and options that can be issued on the cli. - Use `cryptopower -h` or `cryptopower help ` to get detailed information about a command. diff --git a/config.go b/config.go index 374737e30..2e4a73e25 100644 --- a/config.go +++ b/config.go @@ -25,7 +25,7 @@ const ( ) type config struct { - Network string `long:"network" description:"Network to use (mainnet, testnet or simnet). Cannot be changed from the app once set."` + Network string `long:"network" description:"Network to use (mainnet, simnet, testnet, dextest). Default is mainnet. Cannot be changed from the app once set."` HomeDir string `long:"appdata" description:"Directory where the app configuration file and wallet data is stored"` ConfigFile string `long:"configfile" description:"Filename of the config file in the app directory"` ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"` @@ -35,6 +35,9 @@ type config struct { Quiet bool `short:"q" long:"quiet" description:"Easy way to set debuglevel to error"` SpendUnconfirmed bool `long:"spendunconfirmed" description:"Allow the assetsManager to use transactions that have not been confirmed"` Profile int `long:"profile" description:"Runs local web server for profiling"` + DEXTestAddr string `long:"dextestaddr" description:"If using the dextest network, set an address for the dex harness to be used as a persistant peer for all new wallets."` + + net libutils.NetworkType } func defaultConfig(defaultHomeDir string) config { diff --git a/dexc/core.go b/dexc/core.go index 433848e1e..ea29d2b0a 100644 --- a/dexc/core.go +++ b/dexc/core.go @@ -256,7 +256,7 @@ func parseDEXNet(net libutils.NetworkType) (dex.Network, error) { return dex.Mainnet, nil case libutils.Testnet: return dex.Testnet, nil - case libutils.Regression, libutils.Simulation: + case libutils.Regression, libutils.Simulation, libutils.DEXTest: return dex.Simnet, nil default: return 0, fmt.Errorf("unknown network %s", net) diff --git a/go.mod b/go.mod index 7f0c0031f..5f0d6affb 100644 --- a/go.mod +++ b/go.mod @@ -94,7 +94,8 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/dchest/blake2b v1.0.0 // indirect github.com/dchest/siphash v1.2.3 // indirect - github.com/dcrlabs/neutrino-bch v0.0.0-20221031001408-f296bfa9bd1c // indirect + github.com/dcrlabs/bchwallet v0.0.0-20240114124852-0e95005810be // indirect + github.com/dcrlabs/neutrino-bch v0.0.0-20240114121828-d656bce11095 // indirect github.com/deckarep/golang-set/v2 v2.1.0 // indirect github.com/decred/base58 v1.0.5 // indirect github.com/decred/dcrd/blockchain/stake/v3 v3.0.0 // indirect @@ -130,9 +131,6 @@ require ( github.com/gcash/bchd v0.19.0 // indirect github.com/gcash/bchlog v0.0.0-20180913005452-b4f036f92fa6 // indirect github.com/gcash/bchutil v0.0.0-20210113190856-6ea28dff4000 // indirect - github.com/gcash/bchwallet v0.10.0 // indirect - github.com/gcash/bchwallet/walletdb v0.0.0-20210524114850-4837f9798568 // indirect - github.com/gcash/neutrino v0.0.0-20210524114821-3b1878290cf9 // indirect github.com/getsentry/sentry-go v0.18.0 // indirect github.com/go-chi/chi/v5 v5.0.1 // indirect github.com/go-ole/go-ole v1.2.6 // indirect @@ -144,6 +142,7 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.3.0 // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect + github.com/golang/glog v1.0.0 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/trillian v1.4.1 // indirect @@ -229,9 +228,4 @@ replace github.com/lib/pq => github.com/lib/pq v1.10.4 // github.com/btcsuite/btcwallet. replace github.com/btcsuite/btcwallet v0.16.10-0.20230706223227-037580c66b74 => github.com/btcsuite/btcwallet v0.16.9 -// github.com/ukane-philemon/dcrdex v0.0.0-20240126135833-b75c189e80a9 is the -// latest dcrdex master plus commits that adds methods required by -// cryptopower. This should be updated when these PRs get merged. -// 1. core: add ActiveOrders method to Core: https://github.com/decred/dcrdex/pull/2680 -// 1. core: Add ExpiredBonds method to Core: https://github.com/decred/dcrdex/pull/2679 -replace decred.org/dcrdex v0.6.3 => github.com/ukane-philemon/dcrdex v0.0.0-20240126135833-b75c189e80a9 +replace decred.org/dcrdex v0.6.3 => github.com/decred/dcrdex v0.0.0-20240207234259-462dcb6e0736 diff --git a/go.sum b/go.sum index 64190564d..2fa97466a 100644 --- a/go.sum +++ b/go.sum @@ -276,8 +276,6 @@ github.com/campoy/unique v0.0.0-20180121183637-88950e537e7e/go.mod h1:9IOqJGCPMS github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e/go.mod h1:oDpT4efm8tSYHXV5tHSdRvBet/b/QzxZ+XyyPehvm3A= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -365,8 +363,11 @@ github.com/dchest/siphash v1.2.1/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBl github.com/dchest/siphash v1.2.2/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4= github.com/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA= github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc= -github.com/dcrlabs/neutrino-bch v0.0.0-20221031001408-f296bfa9bd1c h1:cBNmU5A0Qo+AQMfytPzY2LEKQITtAQPtbt+aB8d5Zr0= -github.com/dcrlabs/neutrino-bch v0.0.0-20221031001408-f296bfa9bd1c/go.mod h1:Iq+pOckoLj0Hj7C0W3/bEDpVAj7moSxP5W1v3R0Mhl4= +github.com/dcrlabs/bchwallet v0.0.0-20240114115928-2a995d024eed/go.mod h1:dX7SZgs+dmGL56e6KHn+MktfriF/aM2qI4yYK56ySEQ= +github.com/dcrlabs/bchwallet v0.0.0-20240114124852-0e95005810be h1:F61HTz77YatFuMgleG4Hp4QpXWJgjSG+S72YlXD4jPA= +github.com/dcrlabs/bchwallet v0.0.0-20240114124852-0e95005810be/go.mod h1:Km7vMslEkg88jIFE2TA/XX7kAvXvnsoGTXN9ktYSi98= +github.com/dcrlabs/neutrino-bch v0.0.0-20240114121828-d656bce11095 h1:oGXgq+wZ2FvBzBTqXnOJ+ZP6f79zhfJNd46nDkJaUdU= +github.com/dcrlabs/neutrino-bch v0.0.0-20240114121828-d656bce11095/go.mod h1:aymH7e5PUsdsLagAIIMbpyJIff0Kv8/BJg7nolIVJbU= github.com/dcrlabs/neutrino-ltc v0.0.0-20221031001456-55ef06cefead h1:pblpm/kKhsgJTDdJmolLYkQO2jsRRr9bcIoKaHV7cpE= github.com/dcrlabs/neutrino-ltc v0.0.0-20221031001456-55ef06cefead/go.mod h1:dWndR5wa89ZDMGbiizpTvVDsck+GWOVhDgGo0GZmMxI= github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI= @@ -524,6 +525,8 @@ github.com/decred/dcrdata/semver v1.0.0/go.mod h1:z+nQqiAd9fYkHhBLbejysZ2FPHtgkr github.com/decred/dcrdata/txhelpers/v3 v3.0.4/go.mod h1:tKEDhoO+TbYrFrx+5qKZDxcla8ELQFYs4f5+8gL4cuY= github.com/decred/dcrdata/v8 v8.0.0-20230617164141-fa4d8e1b4e8e h1:BETBkx4F+ozHPju0Fwwdblv0t0odBhvzlkIk9ejGNk8= github.com/decred/dcrdata/v8 v8.0.0-20230617164141-fa4d8e1b4e8e/go.mod h1:u7+CQr/8aGJpmq8Wrgff4XejwyxwQp2MWTwndKc9dSQ= +github.com/decred/dcrdex v0.0.0-20240207234259-462dcb6e0736 h1:PIpsAQi30llqffPV44yr8qjNurSzfGyc3MRcTl/6DPg= +github.com/decred/dcrdex v0.0.0-20240207234259-462dcb6e0736/go.mod h1:rvIXDCS3B3gbu8b0tKDvNU664nC+/zVl3ivs6AeA2V8= github.com/decred/dcrtime v0.0.0-20191018193024-8d8b4ef0458e h1:sNDR7vx6gaA3WD+WoEofTvtdjfwHAiogtjB3kt8iFco= github.com/decred/dcrtime v0.0.0-20191018193024-8d8b4ef0458e/go.mod h1:IyZnyBE3E6RBFsEjwEs21FrO/UsrLrL15hUnpZZQxpU= github.com/decred/dcrwallet v1.2.2/go.mod h1:BrSus0F+Rx8UhvPNBfuRMIjRJBNrW2sLspN9iQR5hm8= @@ -642,9 +645,6 @@ github.com/gcash/bchd v0.14.7/go.mod h1:Gk/O1ktRVW5Kao0RsnVXp3bWxeYQadqawZ1Im9HE github.com/gcash/bchd v0.15.2/go.mod h1:k9wIjgwnhbrAw+ruIPZ2tHZMzfFNdyUnORZZX7lqXGY= github.com/gcash/bchd v0.17.1/go.mod h1:qwEZ/wr6LyUo5IBgAPcAbYHzXrjnr5gc4tj03n1TwKc= github.com/gcash/bchd v0.17.2-0.20201218180520-5708823e0e99/go.mod h1:qwEZ/wr6LyUo5IBgAPcAbYHzXrjnr5gc4tj03n1TwKc= -github.com/gcash/bchd v0.18.1-0.20210522092846-10614544cf00/go.mod h1:M8ps+ZMPvdUoqCMoI/aKWKXg8GOE4zTKQpvGK9D940Y= -github.com/gcash/bchd v0.18.1-0.20210524104807-34d7fe5c34b2/go.mod h1:Ev71ERU8MTkTRNFPeNox+9Vz/ZWX0ejpkwgZW/ophgw= -github.com/gcash/bchd v0.18.1/go.mod h1:Ev71ERU8MTkTRNFPeNox+9Vz/ZWX0ejpkwgZW/ophgw= github.com/gcash/bchd v0.19.0 h1:qM1wHgb9FlO95f1tj3q2deHVW7c7gB7GRnz2xVpn8EY= github.com/gcash/bchd v0.19.0/go.mod h1:Ev71ERU8MTkTRNFPeNox+9Vz/ZWX0ejpkwgZW/ophgw= github.com/gcash/bchlog v0.0.0-20180913005452-b4f036f92fa6 h1:3pZvWJ8MSfWstGrb8Hfh4ZpLyZNcXypcGx2Ju4ZibVM= @@ -654,21 +654,6 @@ github.com/gcash/bchutil v0.0.0-20191012211144-98e73ec336ba/go.mod h1:nUIrcbbtEQ github.com/gcash/bchutil v0.0.0-20200506001747-c2894cd54b33/go.mod h1:wB++2ZcHUvGLN1OgO9swBmJK1vmyshJLW9SNS+apXwc= github.com/gcash/bchutil v0.0.0-20210113190856-6ea28dff4000 h1:vVi7Ym3I9T4ZKhQy0/XLKzS3xAqX4K+/cSAmnvMR+HM= github.com/gcash/bchutil v0.0.0-20210113190856-6ea28dff4000/go.mod h1:H2USFGwtiu6CNMxiVQPqZkDzsoVSt9BLNqTfBBqGXRo= -github.com/gcash/bchwallet v0.8.3-0.20210524032547-09a7e9ef6126/go.mod h1:w+CpcX7VBjAiLMq+pkubcbEU6IqJWg/VNu3skgm6RGc= -github.com/gcash/bchwallet v0.8.3-0.20210524084514-53ddbcfd3712/go.mod h1:C7YNuYMdesznEyUVU305/9jtWDkzVbcNU9/ZgwD1QAU= -github.com/gcash/bchwallet v0.8.3-0.20210524112536-14ca25bc6549/go.mod h1:4hGEkghrAx5yIOdvakkRP5ZT8K/ZMtJhrXXr1Mc6lz4= -github.com/gcash/bchwallet v0.10.0 h1:kAd5q585B/QpjZxv6JAvDYVn1KJpCK8IePzjfUvlYl0= -github.com/gcash/bchwallet v0.10.0/go.mod h1:7Uftb3ZU9+iM5nEpLT2502aYi2x+OuUOo7A7rl13kvw= -github.com/gcash/bchwallet/walletdb v0.0.0-20210524032111-4a6e424b2c66/go.mod h1:KNPI56t8uMlItrGyu3tS4NCd3jGfA6KjdUg4bsk+QJg= -github.com/gcash/bchwallet/walletdb v0.0.0-20210524032946-5175b789def6/go.mod h1:KNPI56t8uMlItrGyu3tS4NCd3jGfA6KjdUg4bsk+QJg= -github.com/gcash/bchwallet/walletdb v0.0.0-20210524044131-61bcca2ae6f9/go.mod h1:KNPI56t8uMlItrGyu3tS4NCd3jGfA6KjdUg4bsk+QJg= -github.com/gcash/bchwallet/walletdb v0.0.0-20210524114850-4837f9798568 h1:lUckiJp1DwpCHFi8IuvtssDS3i3VHqkyluksH0IhL64= -github.com/gcash/bchwallet/walletdb v0.0.0-20210524114850-4837f9798568/go.mod h1:KNPI56t8uMlItrGyu3tS4NCd3jGfA6KjdUg4bsk+QJg= -github.com/gcash/neutrino v0.0.0-20210524024247-f7bf0435d155/go.mod h1:91zIrel7tOt35+0hAN8Rrr7O07LZrR9b98Nk4AcC/Lg= -github.com/gcash/neutrino v0.0.0-20210524083541-2900eef48821/go.mod h1:ud/wsgR459Qy+HmEAoQZF9XPTYaOYep5df8ynowCnLs= -github.com/gcash/neutrino v0.0.0-20210524105223-4cec86bbd8a4/go.mod h1:YBR6T+ZT02eR1S7JGqJ2gVPxZlfjWswTCXB4HZafp/U= -github.com/gcash/neutrino v0.0.0-20210524114821-3b1878290cf9 h1:V5UNzi/5pZxE5s6kfCe59VjJRmfkyI+npZizMcAvEdI= -github.com/gcash/neutrino v0.0.0-20210524114821-3b1878290cf9/go.mod h1:MshBO/Xf8SCndZFetZ8yg79db/JghnOiMmPiY1Eatlw= github.com/gen2brain/beeep v0.0.0-20220402123239-6a3042f4b71a h1:fwNLHrP5Rbg/mGSXCjtPdpbqv2GucVTA/KMi8wEm6mE= github.com/gen2brain/beeep v0.0.0-20220402123239-6a3042f4b71a/go.mod h1:/WeFVhhxMOGypVKS0w8DUJxUBbHypnWkUVnW7p5c9Pw= github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= @@ -677,9 +662,7 @@ github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkN github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnRJRLTXZr51aKQ= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= -github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-chi/chi/v5 v5.0.1 h1:ALxjCrTf1aflOlkhMnCUP86MubbWFrzB3gkRPReLpTo= @@ -705,10 +688,6 @@ github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AE github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/go-redis/redis v6.15.8+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= @@ -949,9 +928,7 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t github.com/grpc-ecosystem/grpc-gateway v1.12.1/go.mod h1:8XEsbTttt/W+VvjtQhLACqCisSPWTxCZ7sBRjU6iH9c= github.com/grpc-ecosystem/grpc-gateway v1.14.6/go.mod h1:zdiPV4Yse/1gnckTHtghG4GkDEdKCRJduHpTxT3/jcw= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.1.0/go.mod h1:ly5QWKtiqC7tGfzgXYtpoZYmEWx5Z82/b18ASEL+yGc= github.com/grpc-ecosystem/grpc-gateway/v2 v2.4.0/go.mod h1:IOyTYjcIO0rkmnGBfJTL0NJ11exy/Tc2QEuv7hCXp24= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.10.0/go.mod h1:XnLCLFp3tjoZJszVKjfpyAK6J8sYIcQXWQxmqLWF21I= github.com/h2non/go-is-svg v0.0.0-20160927212452-35e8c4b0612c h1:fEE5/5VNnYUoBOj2I9TP8Jc+a7lge3QWn9DKE7NCwfc= github.com/h2non/go-is-svg v0.0.0-20160927212452-35e8c4b0612c/go.mod h1:ObS/W+h8RYb1Y7fYivughjxojTmIu5iAIjSrSLCLeqE= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= @@ -1005,8 +982,6 @@ github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= github.com/improbable-eng/grpc-web v0.9.1/go.mod h1:6hRR09jOEG81ADP5wCQju1z71g6OL4eEvELdran/3cs= github.com/improbable-eng/grpc-web v0.13.0/go.mod h1:6hRR09jOEG81ADP5wCQju1z71g6OL4eEvELdran/3cs= -github.com/improbable-eng/grpc-web v0.14.0/go.mod h1:6hRR09jOEG81ADP5wCQju1z71g6OL4eEvELdran/3cs= -github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPtwNr0l57L4f878wP8= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= @@ -1089,12 +1064,9 @@ github.com/kkdai/bstream v1.0.0 h1:Se5gHwgp2VT2uHfDrkbbgbgEvV9cimLELwrPJctSjg8= github.com/kkdai/bstream v1.0.0/go.mod h1:FDnDOHt5Yx4p3FaHcioFT0QjDOtgUpvjeZqAs+NVZZA= github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.0/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.15.2/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= @@ -1128,7 +1100,6 @@ github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awS github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/ldez/gomoddirectives v0.2.1/go.mod h1:sGicqkRgBOg//JfpXwkB9Hj0X5RyJ7mlACM5B9f6Me4= github.com/ldez/tagliatelle v0.2.0/go.mod h1:8s6WJQwEYHbKZDsp/LjArytKOG8qaMrKQQ3mFukHs88= -github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/letsencrypt/pkcs11key/v4 v4.0.0/go.mod h1:EFUvBDay26dErnNb70Nd0/VW3tJiIbETBPTl9ATXQag= github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= @@ -1233,7 +1204,6 @@ github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyex github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= -github.com/miekg/dns v1.1.42/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= github.com/miekg/dns v1.1.48/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= @@ -1269,7 +1239,6 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007/go.mod h1:m2XC9Qq0AlmmVksL6FktJCdTYyLk7V3fKyp0sl1yWQo= github.com/mwitkow/go-proto-validators v0.2.0/go.mod h1:ZfA1hW+UH/2ZHOWvQ3HnQaU0DtnpXu850MZiy+YUgcc= -github.com/mwitkow/grpc-proxy v0.0.0-20181017164139-0f1106ef9c76/go.mod h1:x5OoJHDHqxHS801UIuhqGl6QdSAEJvtausosHSdazIo= github.com/nakabonne/nestif v0.3.0/go.mod h1:dI314BppzXjJ4HsCnbo7XzrJHPszZsjnk5wEBSYHI2c= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= @@ -1368,7 +1337,6 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU= github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= @@ -1390,7 +1358,6 @@ github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= github.com/prometheus/common v0.25.0/go.mod h1:H6QK/N6XVT42whUeIdI3dp36w49c+/iMDk7UAI2qm7Q= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= @@ -1405,7 +1372,6 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= @@ -1476,7 +1442,6 @@ github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxr github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/simpleledgerinc/goslp v0.0.0-20210310142058-5920ead5c7a0/go.mod h1:H95uvOA9ea+6KkX647VlNLbV+UFBV/i+vPktgnXiQJw= -github.com/simpleledgerinc/goslp v0.0.0-20210423125905-3c2e5f2ef33f/go.mod h1:H95uvOA9ea+6KkX647VlNLbV+UFBV/i+vPktgnXiQJw= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= @@ -1578,8 +1543,6 @@ github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGr github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/ukane-philemon/dcrdex v0.0.0-20240126135833-b75c189e80a9 h1:9k0cyp9qflL5PfHCTu8KMtjpBtpUEFu6vwAjZ8cd8mQ= -github.com/ukane-philemon/dcrdex v0.0.0-20240126135833-b75c189e80a9/go.mod h1:2U+b4uDx4ZbAkk/pLaa2rv57G23XTyEe0Sw7Um+AdNE= github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= github.com/ulikunitz/xz v0.5.7/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ultraware/funlen v0.0.3/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA= @@ -1865,7 +1828,6 @@ golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210521195947-fe42d452be8f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -1987,7 +1949,6 @@ golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201024232916-9f70ab9862d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1996,7 +1957,6 @@ golang.org/x/sys v0.0.0-20210217105451-b926d437f341/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210228012217-479acdf4ea46/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2017,7 +1977,6 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -2044,7 +2003,6 @@ golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220411215600-e5f449aeb171/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c= @@ -2300,9 +2258,7 @@ google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210106152847-07624b53cd92/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210207032614-bba0dbe2a9ea/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= @@ -2351,7 +2307,6 @@ google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2 google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= @@ -2486,14 +2441,11 @@ mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIa mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= mvdan.cc/unparam v0.0.0-20210104141923-aac4ce9116a7/go.mod h1:hBpJkZE8H/sb+VRFvw2+rBpHNsTBcvSpk61hr8mzXZE= mvdan.cc/unparam v0.0.0-20210520122750-2ac67f130a88/go.mod h1:hBpJkZE8H/sb+VRFvw2+rBpHNsTBcvSpk61hr8mzXZE= -nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= -nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= pack.ag/amqp v0.11.2/go.mod h1:4/cbmt4EJXSKlG6LCfWHoqmN0uFdy5i/+YFz+fTfhV4= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/libwallet/assets/dcr/dex_wallet.go b/libwallet/assets/dcr/dex_wallet.go index 5503fdafd..f01cd73c4 100644 --- a/libwallet/assets/dcr/dex_wallet.go +++ b/libwallet/assets/dcr/dex_wallet.go @@ -33,17 +33,26 @@ import ( // DEXWallet wraps *Asset and implements dexdcr.Wallet. type DEXWallet struct { - *Asset + w *dcrwallet.Wallet + helper WalletHelper tradingAccountNumber int32 syncData *SyncData } +type WalletHelper interface { + IsAccountMixerActive() bool + UnmixedAccountNumber() int32 + MixedAccountNumber() int32 +} + var _ dexdcr.Wallet = (*DEXWallet)(nil) +var _ WalletHelper = (*Asset)(nil) // NewDEXWallet returns a new *DEXWallet. -func NewDEXWallet(asset *Asset, tradingAccountNumber int32, syncData *SyncData) *DEXWallet { +func NewDEXWallet(w *dcrwallet.Wallet, helper WalletHelper, tradingAccountNumber int32, syncData *SyncData) *DEXWallet { return &DEXWallet{ - Asset: asset, + w: w, + helper: helper, tradingAccountNumber: tradingAccountNumber, syncData: syncData, } @@ -69,26 +78,26 @@ func (dw *DEXWallet) SpvMode() bool { // Accounts returns the names of the accounts for use by the exchange wallet. func (dw *DEXWallet) Accounts() dexdcr.XCWalletAccounts { var accts dexdcr.XCWalletAccounts - accountName, err := dw.AccountName(dw.tradingAccountNumber) + accountName, err := dw.w.AccountName(context.Background(), uint32(dw.tradingAccountNumber)) if err == nil { accts.PrimaryAccount = accountName } else { log.Errorf("error checking selected DEX account name: %v", err) } - if !dw.IsAccountMixerActive() { + if !dw.helper.IsAccountMixerActive() { return accts } - unMixedAcctNum := dw.UnmixedAccountNumber() - mixedAcctNum := dw.MixedAccountNumber() - unMixedAcctName, err := dw.AccountName(unMixedAcctNum) + unMixedAcctNum := dw.helper.UnmixedAccountNumber() + mixedAcctNum := dw.helper.MixedAccountNumber() + unMixedAcctName, err := dw.w.AccountName(context.Background(), uint32(unMixedAcctNum)) if err != nil { log.Errorf("error retrieving unmixed account name: %v", err) return accts } - mixedAcctName, err := dw.AccountName(mixedAcctNum) + mixedAcctName, err := dw.w.AccountName(context.Background(), uint32(mixedAcctNum)) if err != nil { log.Errorf("error retrieving mixed account name: %v", err) return accts @@ -119,11 +128,11 @@ func (dw *DEXWallet) NotifyOnTipChange(_ context.Context, _ dexdcr.TipChangeCall // if the address is not owned by the wallet. // Part of the Wallet interface. func (dw *DEXWallet) AddressInfo(ctx context.Context, address string) (*dexdcr.AddressInfo, error) { - addr, err := stdaddr.DecodeAddress(address, dw.Internal().DCR.ChainParams()) + addr, err := stdaddr.DecodeAddress(address, dw.w.ChainParams()) if err != nil { return nil, err } - ka, err := dw.Internal().DCR.KnownAddress(ctx, addr) + ka, err := dw.w.KnownAddress(ctx, addr) if err != nil { return nil, err } @@ -139,7 +148,7 @@ func (dw *DEXWallet) AddressInfo(ctx context.Context, address string) (*dexdcr.A // specified account. // Part of the Wallet interface. func (dw *DEXWallet) AccountOwnsAddress(ctx context.Context, addr stdaddr.Address, acctName string) (bool, error) { - ka, err := dw.Internal().DCR.KnownAddress(ctx, addr) + ka, err := dw.w.KnownAddress(ctx, addr) if err != nil { if errors.Is(err, walleterrors.NotExist) { return false, nil @@ -158,11 +167,11 @@ func (dw *DEXWallet) AccountOwnsAddress(ctx context.Context, addr stdaddr.Addres // AccountBalance returns the balance breakdown for the specified account. // Part of the Wallet interface. func (dw *DEXWallet) AccountBalance(ctx context.Context, confirms int32, accountName string) (*walletjson.GetAccountBalanceResult, error) { - accountNumber, err := dw.Internal().DCR.AccountNumber(ctx, accountName) + accountNumber, err := dw.w.AccountNumber(ctx, accountName) if err != nil { return nil, err } - bal, err := dw.Internal().DCR.AccountBalance(ctx, accountNumber, confirms) + bal, err := dw.w.AccountBalance(ctx, accountNumber, confirms) if err != nil { return nil, err } @@ -182,21 +191,21 @@ func (dw *DEXWallet) AccountBalance(ctx context.Context, confirms int32, account // LockedOutputs fetches locked outputs for the Wallet. // Part of the Wallet interface. func (dw *DEXWallet) LockedOutputs(ctx context.Context, accountName string) ([]chainjson.TransactionInput, error) { - return dw.Internal().DCR.LockedOutpoints(ctx, accountName) + return dw.w.LockedOutpoints(ctx, accountName) } // Unspents fetches unspent outputs for the Wallet. // Part of the Wallet interface. func (dw *DEXWallet) Unspents(ctx context.Context, accountName string) ([]*walletjson.ListUnspentResult, error) { - return dw.Internal().DCR.ListUnspent(ctx, 0, math.MaxInt32, nil, accountName) + return dw.w.ListUnspent(ctx, 0, math.MaxInt32, nil, accountName) } // LockUnspent locks or unlocks the specified outpoint. // Part of the Wallet interface. func (dw *DEXWallet) LockUnspent(_ context.Context, unlock bool, ops []*wire.OutPoint) error { - fun := dw.Internal().DCR.LockOutpoint + fun := dw.w.LockOutpoint if unlock { - fun = dw.Internal().DCR.UnlockOutpoint + fun = dw.w.UnlockOutpoint } for _, op := range ops { fun(&op.Hash, op.Index) @@ -213,14 +222,14 @@ func (dw *DEXWallet) LockUnspent(_ context.Context, unlock bool, ops []*wire.Out // output cannot be located. // Part of the Wallet interface. func (dw *DEXWallet) UnspentOutput(ctx context.Context, txHash *chainhash.Hash, index uint32, _ int8) (*dexdcr.TxOutput, error) { - txd, err := dcrwallet.UnstableAPI(dw.Internal().DCR).TxDetails(ctx, txHash) + txd, err := dcrwallet.UnstableAPI(dw.w).TxDetails(ctx, txHash) if errors.Is(err, walleterrors.NotExist) { return nil, dexasset.CoinNotFoundError } else if err != nil { return nil, err } - details, err := dw.Internal().DCR.ListTransactionDetails(ctx, txHash) + details, err := dw.w.ListTransactionDetails(ctx, txHash) if err != nil { return nil, err } @@ -244,7 +253,7 @@ func (dw *DEXWallet) UnspentOutput(ctx context.Context, txHash *chainhash.Hash, return nil, fmt.Errorf("not enough outputs") } - _, tipHeight := dw.Internal().DCR.MainChainTip(ctx) + _, tipHeight := dw.w.MainChainTip(ctx) var ours bool for _, credit := range txd.Credits { @@ -274,21 +283,21 @@ func (dw *DEXWallet) UnspentOutput(ctx context.Context, txHash *chainhash.Hash, // Using GapPolicyWrap here, introducing a relatively small risk of address // reuse, but improving wallet recoverability. func (dw *DEXWallet) ExternalAddress(ctx context.Context, accountName string) (stdaddr.Address, error) { - acctNum, err := dw.Internal().DCR.AccountNumber(ctx, accountName) + acctNum, err := dw.w.AccountNumber(ctx, accountName) if err != nil { return nil, err } - return dw.Internal().DCR.NewExternalAddress(ctx, acctNum, dcrwallet.WithGapPolicyWrap()) + return dw.w.NewExternalAddress(ctx, acctNum, dcrwallet.WithGapPolicyWrap()) } // InternalAddress returns an internal address using GapPolicyIgnore. // Part of the Wallet interface. func (dw *DEXWallet) InternalAddress(ctx context.Context, accountName string) (stdaddr.Address, error) { - acctNum, err := dw.Internal().DCR.AccountNumber(ctx, accountName) + acctNum, err := dw.w.AccountNumber(ctx, accountName) if err != nil { return nil, err } - return dw.Internal().DCR.NewInternalAddress(ctx, acctNum, dcrwallet.WithGapPolicyWrap()) + return dw.w.NewInternalAddress(ctx, acctNum, dcrwallet.WithGapPolicyWrap()) } // SignRawTransaction signs the provided transaction. SignRawTransaction is not @@ -297,7 +306,7 @@ func (dw *DEXWallet) InternalAddress(ctx context.Context, accountName string) (s // Part of the Wallet interface. func (dw *DEXWallet) SignRawTransaction(ctx context.Context, baseTx *wire.MsgTx) (*wire.MsgTx, error) { tx := baseTx.Copy() - sigErrs, err := dw.Internal().DCR.SignTransaction(ctx, tx, txscript.SigHashAll, nil, nil, nil) + sigErrs, err := dw.w.SignTransaction(ctx, tx, txscript.SigHashAll, nil, nil, nil) if err != nil { return nil, err } @@ -314,11 +323,11 @@ func (dw *DEXWallet) SignRawTransaction(ctx context.Context, baseTx *wire.MsgTx) // network. // Part of the Wallet interface. func (dw *DEXWallet) SendRawTransaction(ctx context.Context, tx *wire.MsgTx, _ bool) (*chainhash.Hash, error) { - syncer, err := dw.Internal().DCR.NetworkBackend() + syncer, err := dw.w.NetworkBackend() if err != nil { return nil, err } - return dw.Internal().DCR.PublishTransaction(ctx, tx, syncer) + return dw.w.PublishTransaction(ctx, tx, syncer) } // GetBestBlock returns the hash and height of the wallet's best block. @@ -326,11 +335,10 @@ func (dw *DEXWallet) SendRawTransaction(ctx context.Context, tx *wire.MsgTx, _ b func (dw *DEXWallet) GetBestBlock(ctx context.Context) (*chainhash.Hash, int64, error) { // Handle a scenario where DEX dcr exchange wallet accesses this method from // a goroutine. - w := dw.Internal().DCR - if w == nil { + if dw.w == nil { return nil, 0, errors.New("dcr wallet does not exit") } - blockHash, blockHeight := w.MainChainTip(ctx) + blockHash, blockHeight := dw.w.MainChainTip(ctx) return &blockHash, int64(blockHeight), nil } @@ -339,7 +347,7 @@ func (dw *DEXWallet) GetBestBlock(ctx context.Context) (*chainhash.Hash, int64, // block's median time. // Part of the Wallet interface. func (dw *DEXWallet) GetBlockHeader(ctx context.Context, blockHash *chainhash.Hash) (*dexdcr.BlockHeader, error) { - hdr, err := dw.Internal().DCR.BlockHeader(ctx, blockHash) + hdr, err := dw.w.BlockHeader(ctx, blockHash) if err != nil { return nil, err } @@ -352,12 +360,12 @@ func (dw *DEXWallet) GetBlockHeader(ctx context.Context, blockHash *chainhash.Ha // Get next block hash unless there are none. var nextHash *chainhash.Hash confirmations := int64(-1) - mainChainHasBlock, _, err := dw.Internal().DCR.BlockInMainChain(ctx, blockHash) + mainChainHasBlock, _, err := dw.w.BlockInMainChain(ctx, blockHash) if err != nil { return nil, fmt.Errorf("error checking if block is in mainchain: %w", err) } if mainChainHasBlock { - _, tipHeight := dw.Internal().DCR.MainChainTip(ctx) + _, tipHeight := dw.w.MainChainTip(ctx) if int32(hdr.Height) < tipHeight { nextHash, err = dw.GetBlockHash(ctx, int64(hdr.Height)+1) if err != nil { @@ -392,7 +400,7 @@ func (dw *DEXWallet) medianTime(ctx context.Context, iBlkHeader *wire.BlockHeade break } var err error - iBlkHeader, err = dw.Internal().DCR.BlockHeader(ctx, &iBlkHeader.PrevBlock) + iBlkHeader, err = dw.w.BlockHeader(ctx, &iBlkHeader.PrevBlock) if err != nil { return 0, fmt.Errorf("info not found for previous block: %v", err) } @@ -422,14 +430,14 @@ func (dw *DEXWallet) GetBlock(ctx context.Context, blockHash *chainhash.Hash) (* // found in the wallet. func (dw *DEXWallet) GetTransaction(ctx context.Context, txHash *chainhash.Hash) (*dexdcr.WalletTransaction, error) { // copy-pasted from dcrwallet/internal/rpc/jsonrpc/methods.go - txd, err := dcrwallet.UnstableAPI(dw.Internal().DCR).TxDetails(ctx, txHash) + txd, err := dcrwallet.UnstableAPI(dw.w).TxDetails(ctx, txHash) if errors.Is(err, walleterrors.NotExist) { return nil, dexasset.CoinNotFoundError } else if err != nil { return nil, err } - _, tipHeight := dw.Internal().DCR.MainChainTip(ctx) + _, tipHeight := dw.w.MainChainTip(ctx) var b strings.Builder b.Grow(2 * txd.MsgTx.SerializeSize()) @@ -447,7 +455,7 @@ func (dw *DEXWallet) GetTransaction(ctx context.Context, txHash *chainhash.Hash) ret.Confirmations = int64(tipHeight - txd.Block.Height + 1) } - details, err := dw.Internal().DCR.ListTransactionDetails(ctx, txHash) + details, err := dw.w.ListTransactionDetails(ctx, txHash) if err != nil { return nil, err } @@ -471,7 +479,7 @@ func (dw *DEXWallet) GetTransaction(ctx context.Context, txHash *chainhash.Hash) // Returns dw.wallet. CoinNotFoundError if the tx is not found. // Part of the Wallet interface. func (dw *DEXWallet) GetRawTransaction(ctx context.Context, txHash *chainhash.Hash) (*wire.MsgTx, error) { - txs, _, err := dw.Internal().DCR.GetTransactionsByHashes(ctx, []*chainhash.Hash{txHash}) + txs, _, err := dw.w.GetTransactionsByHashes(ctx, []*chainhash.Hash{txHash}) if err != nil { return nil, err } @@ -484,7 +492,7 @@ func (dw *DEXWallet) GetRawTransaction(ctx context.Context, txHash *chainhash.Ha // GetBlockHash returns the hash of the mainchain block at the specified height. // Part of the Wallet interface. func (dw *DEXWallet) GetBlockHash(ctx context.Context, blockHeight int64) (*chainhash.Hash, error) { - info, err := dw.Internal().DCR.BlockInfo(ctx, dcrwallet.NewBlockIdentifierFromHeight(int32(blockHeight))) + info, err := dw.w.BlockInfo(ctx, dcrwallet.NewBlockIdentifierFromHeight(int32(blockHeight))) if err != nil { return nil, err } @@ -494,7 +502,7 @@ func (dw *DEXWallet) GetBlockHash(ctx context.Context, blockHeight int64) (*chai // MatchAnyScript looks for any of the provided scripts in the block specified. // Part of the Wallet interface. func (dw *DEXWallet) MatchAnyScript(ctx context.Context, blockHash *chainhash.Hash, scripts [][]byte) (bool, error) { - key, filter, err := dw.Internal().DCR.CFilterV2(ctx, blockHash) + key, filter, err := dw.w.CFilterV2(ctx, blockHash) if err != nil { return false, err } @@ -504,21 +512,21 @@ func (dw *DEXWallet) MatchAnyScript(ctx context.Context, blockHash *chainhash.Ha // AccountUnlocked returns true if the account is unlocked. // Part of the Wallet interface. func (dw *DEXWallet) AccountUnlocked(_ context.Context, _ string) (bool, error) { - return !dw.Wallet.IsLocked(), nil + return !dw.w.Locked(), nil } // LockAccount locks the specified account. // Part of the Wallet interface. func (dw *DEXWallet) LockAccount(_ context.Context, _ string) error { - dw.Wallet.LockWallet() + dw.w.Lock() return nil } // UnlockAccount unlocks the specified account or the wallet if account is not // encrypted. // Part of the Wallet interface. -func (dw *DEXWallet) UnlockAccount(_ context.Context, pass []byte, _ string) error { - return dw.Wallet.UnlockWallet(string(pass)) +func (dw *DEXWallet) UnlockAccount(ctx context.Context, pass []byte, _ string) error { + return dw.w.Unlock(ctx, pass, nil) } // SyncStatus returns the wallet's sync status. @@ -541,10 +549,14 @@ func (dw *DEXWallet) PeerCount(_ context.Context) (uint32, error) { // AddressPrivKey fetches the privkey for the specified address. // Part of the Wallet interface. func (dw *DEXWallet) AddressPrivKey(ctx context.Context, addr stdaddr.Address) (*secp256k1.PrivateKey, error) { - privKey, _, err := dw.Internal().DCR.LoadPrivateKey(ctx, addr) + privKey, _, err := dw.w.LoadPrivateKey(ctx, addr) return privKey, err } +func (dw *DEXWallet) ListSinceBlock(ctx context.Context, start, end, syncHeight int32) ([]walletjson.ListTransactionsResult, error) { + return dw.w.ListSinceBlock(ctx, start, end, syncHeight) +} + // Part of the Wallet interface. func (dw *DEXWallet) Reconfigure(_ context.Context, _ *dexasset.WalletConfig, _ dex.Network, _ string) (restart bool, err error) { return false, errors.New("Reconfigure not implemented by Cryptopower DEX wallet") diff --git a/libwallet/assets/ltc/dex-wallet.go b/libwallet/assets/ltc/dex-wallet.go index 2f4be1328..48d40f50e 100644 --- a/libwallet/assets/ltc/dex-wallet.go +++ b/libwallet/assets/ltc/dex-wallet.go @@ -26,6 +26,7 @@ import ( "github.com/btcsuite/btcwallet/waddrmgr" btcwallet "github.com/btcsuite/btcwallet/wallet" "github.com/btcsuite/btcwallet/wtxmgr" + neutrino "github.com/dcrlabs/neutrino-ltc" "github.com/dcrlabs/neutrino-ltc/chain" btcneutrino "github.com/lightninglabs/neutrino" "github.com/lightninglabs/neutrino/headerfs" @@ -555,12 +556,26 @@ func (s *ltcChainService) BestBlock() (*headerfs.BlockStamp, error) { } func (s *ltcChainService) Peers() []dexbtc.SPVPeer { - rawPeers := s.CS.Peers() - peers := make([]dexbtc.SPVPeer, len(rawPeers)) - for i, p := range rawPeers { - peers[i] = p + // *neutrino.ChainService.Peers() may stall, especially if the wallet hasn't + // started sync yet. Call the method in a goroutine and wait below to see if + // we get a response. Return an empty slice if we don't get a response after + // waiting briefly. + rawPeersChan := make(chan []*neutrino.ServerPeer) + go func() { + rawPeersChan <- s.CS.Peers() + }() + + select { + case rawPeers := <-rawPeersChan: + peers := make([]dexbtc.SPVPeer, 0, len(rawPeers)) + for _, p := range rawPeers { + peers = append(peers, p) + } + return peers + + case <-time.After(2 * time.Second): + return nil // CS.Peers() is taking too long to respond } - return peers } func (s *ltcChainService) AddPeer(addr string) error { diff --git a/libwallet/assets/wallet/types.go b/libwallet/assets/wallet/types.go index d755bc730..e7cb8eee0 100644 --- a/libwallet/assets/wallet/types.go +++ b/libwallet/assets/wallet/types.go @@ -38,11 +38,12 @@ type WConfig struct { // InitParams defines the basic parameters required to instantiate any // wallet interface. type InitParams struct { - RootDir string - NetType utils.NetworkType - DB *storm.DB - DbDriver string - LogDir string + RootDir string + NetType utils.NetworkType + DB *storm.DB + DbDriver string + LogDir string + DEXTestAddr string } // AuthInfo defines the complete information required to either create a diff --git a/libwallet/assets/wallet/wallet_config.go b/libwallet/assets/wallet/wallet_config.go index 883e02109..a76649b38 100644 --- a/libwallet/assets/wallet/wallet_config.go +++ b/libwallet/assets/wallet/wallet_config.go @@ -12,8 +12,7 @@ const ( AccountMixerUnmixedAccount = "account_mixer_unmixed_account" AccountMixerMixTxChange = "account_mixer_mix_tx_change" - userConfigBucketName = "user_config" // Asset level bucket. - walletsMetadataBucketName = "metadata" // Wallet level bucket. + walletsMetadataBucketName = "metadata" // Wallet level bucket. LogLevelConfigKey = "log_level" PrivacyModeConfigKey = "privacy_mode" @@ -62,79 +61,28 @@ const ( PassphraseTypePass int32 = 1 ) -// AssetsManagerDB defines the main generic methods required to access and manage -// the DB at the assets manager level. -type AssetsManagerDB interface { - // DeleteWalletConfigValue deletes a generic value at the assets manager level. - DeleteWalletConfigValue(key string) - // SaveWalletConfigValue stores a generic value at the assets manager level. - SaveWalletConfigValue(key string, value interface{}) - // ReadWalletConfigValue reads a generic value at the assets manager level. - ReadWalletConfigValue(key string, valueOut interface{}) error -} - // walletConfigSave method manages all the write operations. -func (wallet *Wallet) walletConfigSave(isAssetsManager bool, key string, value interface{}) error { - bucket := walletsMetadataBucketName - if !isAssetsManager { - bucket = userConfigBucketName - key = fmt.Sprintf("%d%s", wallet.ID, key) - } - return wallet.db.Set(bucket, key, value) +func (wallet *Wallet) walletConfigSave(key string, value interface{}) error { + key = fmt.Sprintf("%d%s", wallet.ID, key) + return wallet.db.Set(walletsMetadataBucketName, key, value) } // walletConfigRead manages all the read operations. -func (wallet *Wallet) walletConfigRead(isAssetsManager bool, key string, valueOut interface{}) error { - bucket := walletsMetadataBucketName - if !isAssetsManager { - bucket = userConfigBucketName - key = fmt.Sprintf("%d%s", wallet.ID, key) - } - return wallet.db.Get(bucket, key, valueOut) +func (wallet *Wallet) walletConfigRead(key string, valueOut interface{}) error { + key = fmt.Sprintf("%d%s", wallet.ID, key) + return wallet.db.Get(walletsMetadataBucketName, key, valueOut) } // walletConfigDelete manages all delete operations. -func (wallet *Wallet) walletConfigDelete(isAssetsManager bool, key string) error { - bucket := walletsMetadataBucketName - if !isAssetsManager { - bucket = userConfigBucketName - key = fmt.Sprintf("%d%s", wallet.ID, key) - } - return wallet.db.Delete(bucket, key) -} - -// SaveWalletConfigValue stores a generic value against the provided key -// at the assets manager level. -func (wallet *Wallet) SaveWalletConfigValue(key string, value interface{}) { - err := wallet.walletConfigSave(true, key, value) - if err != nil { - log.Errorf("error setting wallet config value for key: %s, error: %v", key, err) - } -} - -// ReadWalletConfigValue reads a generic value stored against the provided key -// at the assets manager level. -func (wallet *Wallet) ReadWalletConfigValue(key string, valueOut interface{}) error { - err := wallet.walletConfigRead(true, key, valueOut) - if err != nil && err != storm.ErrNotFound { - log.Errorf("error reading wallet config value for key: %s, error: %v", key, err) - } - return err -} - -// DeleteWalletConfigValue deletes the value associated with the provided key -// at the assets manager level. -func (wallet *Wallet) DeleteWalletConfigValue(key string) { - err := wallet.walletConfigDelete(true, key) - if err != nil { - log.Errorf("error deleting wallet config value for key: %s, error: %v", key, err) - } +func (wallet *Wallet) walletConfigDelete(key string) error { + key = fmt.Sprintf("%d%s", wallet.ID, key) + return wallet.db.Delete(walletsMetadataBucketName, key) } // SaveUserConfigValue stores the generic value against the provided key // at the asset level. func (wallet *Wallet) SaveUserConfigValue(key string, value interface{}) { - err := wallet.walletConfigSave(false, key, value) + err := wallet.walletConfigSave(key, value) if err != nil { log.Errorf("error setting user config value for key: %s, error: %v", key, err) } @@ -143,7 +91,7 @@ func (wallet *Wallet) SaveUserConfigValue(key string, value interface{}) { // ReadUserConfigValue reads the generic value stored against the provided // key at the asset level. func (wallet *Wallet) ReadUserConfigValue(key string, valueOut interface{}) error { - err := wallet.walletConfigRead(false, key, valueOut) + err := wallet.walletConfigRead(key, valueOut) if err != nil && err != storm.ErrNotFound { log.Errorf("error reading user config value for key: %s, error: %v", key, err) } @@ -153,7 +101,7 @@ func (wallet *Wallet) ReadUserConfigValue(key string, valueOut interface{}) erro // DeleteUserConfigValueForKey method deletes the value stored against the provided // key at the asset level. func (wallet *Wallet) DeleteUserConfigValueForKey(key string) { - err := wallet.walletConfigDelete(false, key) + err := wallet.walletConfigDelete(key) if err != nil { log.Errorf("error deleting user config value for key: %s, error: %v", key, err) } diff --git a/libwallet/assets/wallet/wallet_shared.go b/libwallet/assets/wallet/wallet_shared.go index 42691fbb3..ac1ce9a9f 100644 --- a/libwallet/assets/wallet/wallet_shared.go +++ b/libwallet/assets/wallet/wallet_shared.go @@ -342,13 +342,23 @@ func CreateNewWallet(pass *AuthInfo, loader loader.AssetLoader, netType: params.NetType, } - return wallet.saveNewWallet(func() error { + if err := wallet.saveNewWallet(func() error { err := wallet.prepare() if err != nil { return err } return wallet.createWallet(pass.PrivatePass, seed) - }) + }); err != nil { + return nil, err + } + if params.NetType == utils.DEXTest { + addr := "127.0.0.1" + if params.DEXTestAddr != "" { + addr = params.DEXTestAddr + } + wallet.SetStringConfigValueForKey(SpvPersistentPeerAddressesConfigKey, addr) + } + return wallet, nil } func (wallet *Wallet) createWallet(privatePassphrase, seedMnemonic string) error { @@ -400,13 +410,23 @@ func CreateWatchOnlyWallet(walletName, extendedPublicKey string, loader loader.A netType: params.NetType, } - return wallet.saveNewWallet(func() error { + if err := wallet.saveNewWallet(func() error { err := wallet.prepare() if err != nil { return err } return wallet.createWatchingOnlyWallet(extendedPublicKey) - }) + }); err != nil { + return nil, err + } + if params.NetType == utils.DEXTest { + addr := "127.0.0.1" + if params.DEXTestAddr != "" { + addr = params.DEXTestAddr + } + wallet.SetStringConfigValueForKey(SpvPersistentPeerAddressesConfigKey, addr) + } + return wallet, nil } func (wallet *Wallet) createWatchingOnlyWallet(extendedPublicKey string) error { @@ -445,13 +465,23 @@ func RestoreWallet(seedMnemonic string, pass *AuthInfo, loader loader.AssetLoade netType: params.NetType, } - return wallet.saveNewWallet(func() error { + if err := wallet.saveNewWallet(func() error { err := wallet.prepare() if err != nil { return err } return wallet.createWallet(pass.PrivatePass, seedMnemonic) - }) + }); err != nil { + return nil, err + } + if params.NetType == utils.DEXTest { + addr := "127.0.0.1" + if params.DEXTestAddr != "" { + addr = params.DEXTestAddr + } + wallet.SetStringConfigValueForKey(SpvPersistentPeerAddressesConfigKey, addr) + } + return wallet, nil } func (wallet *Wallet) WalletNameExists(walletName string) (bool, error) { @@ -491,12 +521,12 @@ func (wallet *Wallet) DeleteWallet(privPass string) error { // saveNewWallet completes setting up the wallet. Since sync can only be // initiated after wallet setup is complete, no sync cancel is necessary here. -func (wallet *Wallet) saveNewWallet(setupWallet func() error) (*Wallet, error) { +func (wallet *Wallet) saveNewWallet(setupWallet func() error) error { exists, err := wallet.WalletNameExists(wallet.Name) if err != nil { - return nil, utils.TranslateError(err) + return utils.TranslateError(err) } else if exists { - return nil, errors.New(utils.ErrExist) + return errors.New(utils.ErrExist) } // Perform database save operations in batch transaction @@ -537,10 +567,10 @@ func (wallet *Wallet) saveNewWallet(setupWallet func() error) (*Wallet, error) { }) if err != nil { - return nil, utils.TranslateError(err) + return utils.TranslateError(err) } - return wallet, nil + return nil } func (wallet *Wallet) IsWatchingOnlyWallet() bool { @@ -560,7 +590,6 @@ func (wallet *Wallet) IsWatchingOnlyWallet() bool { func (wallet *Wallet) OpenWallet() error { pubPass := []byte(w.InsecurePubPassphrase) - ctx, _ := wallet.ShutdownContextWithCancel() _, err := wallet.loader.OpenExistingWallet(ctx, strconv.Itoa(wallet.ID), pubPass) if err != nil { diff --git a/libwallet/assets/wallet/wallet_utils.go b/libwallet/assets/wallet/wallet_utils.go index e0ebf139e..7a9848f56 100644 --- a/libwallet/assets/wallet/wallet_utils.go +++ b/libwallet/assets/wallet/wallet_utils.go @@ -3,6 +3,7 @@ package wallet import ( "context" "fmt" + "net" "os" "sort" "strconv" @@ -291,7 +292,13 @@ func ParseWalletPeers(peerAddresses string, port string) ([]string, []error) { if peerAddresses != "" { addresses := strings.Split(peerAddresses, ";") for _, address := range addresses { - peerAddress, err := utils.NormalizeAddress(address, port) + host, p, err := net.SplitHostPort(address) + // If err assume because port was not supplied. + if err != nil { + host = address + p = port + } + peerAddress, err := utils.NormalizeAddress(host, p) if err != nil { errs = append(errs, fmt.Errorf("SPV peer address(%s) is invalid: %v", peerAddress, err)) } else { diff --git a/libwallet/assets_config.go b/libwallet/assets_config.go index a00939b5d..766f50e21 100644 --- a/libwallet/assets_config.go +++ b/libwallet/assets_config.go @@ -24,25 +24,48 @@ const ( walletsMetadataBucketName = "metadata" walletstartupPassphraseField = "startup-passphrase" + appConfigBucketName = "app_config" // App level bucket. ) -// setDBInterface extract the assets manager db interface that is available -// in each wallet by default from one of the validly created wallets. -func (mgr *AssetsManager) setDBInterface(db sharedW.AssetsManagerDB) { - if db != nil { - mgr.db = db +// saveWalletConfigValue method manages all the write operations on the app's config. +func (mgr *AssetsManager) saveAppConfigValue(key string, value interface{}) { + err := mgr.params.DB.Set(appConfigBucketName, key, value) + if err != nil { + log.Errorf("error setting app config value for key: %s, error: %v", key, err) + } +} + +// ReadWalletConfigValue reads a generic value stored against the provided key +// at the assets manager level. +func (mgr *AssetsManager) readAppConfigValue(key string, valueOut interface{}) error { + err := mgr.params.DB.Get(appConfigBucketName, key, valueOut) + if err != nil && err != storm.ErrNotFound { + log.Errorf("error reading app config value for key: %s, error: %v", key, err) + } + + return err +} + +// appConfigDelete manages all delete operations on the app's config. +func (mgr *AssetsManager) appConfigDelete(key string) { + err := mgr.params.DB.Delete(appConfigBucketName, key) + if err != nil { + log.Errorf("error deleting app config value for key: %s, error: %v", key, err) } } -// ReadBoolValue reads a bool value from the wallet's config. +// ReadBoolValue reads a bool value from the app's config. func (mgr *AssetsManager) ReadBoolValue(key string, value bool) bool { - mgr.db.ReadWalletConfigValue(key, &value) + err := mgr.params.DB.Get(appConfigBucketName, key, value) + if err != nil && err != storm.ErrNotFound { + log.Errorf("error reading app config value for key: %s, error: %v", key, err) + } return value } // SaveBoolValue saves a bool value to the wallet's config. func (mgr *AssetsManager) SetBoolValue(key string, value bool) { - mgr.db.SaveWalletConfigValue(key, value) + mgr.saveAppConfigValue(key, value) } // SetStartupPassphrase sets the startup passphrase for the wallet. @@ -53,7 +76,7 @@ func (mgr *AssetsManager) SetStartupPassphrase(passphrase string, passphraseType // VerifyStartupPassphrase verifies the startup passphrase for the wallet. func (mgr *AssetsManager) VerifyStartupPassphrase(startupPassphrase string) error { var startupPassphraseHash []byte - err := mgr.db.ReadWalletConfigValue(walletstartupPassphraseField, &startupPassphraseHash) + err := mgr.params.DB.Get(appConfigBucketName, walletstartupPassphraseField, &startupPassphraseHash) if err != nil && err != storm.ErrNotFound { return err } @@ -91,10 +114,9 @@ func (mgr *AssetsManager) ChangeStartupPassphrase(oldPassphrase, newPassphrase s return err } - mgr.db.SaveWalletConfigValue(walletstartupPassphraseField, startupPassphraseHash) - mgr.db.SaveWalletConfigValue(sharedW.IsStartupSecuritySetConfigKey, true) - mgr.db.SaveWalletConfigValue(sharedW.StartupSecurityTypeConfigKey, passphraseType) - + mgr.saveAppConfigValue(walletstartupPassphraseField, startupPassphraseHash) + mgr.saveAppConfigValue(sharedW.IsStartupSecuritySetConfigKey, true) + mgr.saveAppConfigValue(sharedW.StartupSecurityTypeConfigKey, passphraseType) return nil } @@ -105,9 +127,9 @@ func (mgr *AssetsManager) RemoveStartupPassphrase(oldPassphrase string) error { return err } - mgr.db.DeleteWalletConfigValue(walletstartupPassphraseField) - mgr.db.SaveWalletConfigValue(sharedW.IsStartupSecuritySetConfigKey, false) - mgr.db.DeleteWalletConfigValue(sharedW.StartupSecurityTypeConfigKey) + mgr.appConfigDelete(walletstartupPassphraseField) + mgr.saveAppConfigValue(sharedW.IsStartupSecuritySetConfigKey, false) + mgr.appConfigDelete(sharedW.StartupSecurityTypeConfigKey) return nil } @@ -115,41 +137,38 @@ func (mgr *AssetsManager) RemoveStartupPassphrase(oldPassphrase string) error { // IsStartupSecuritySet checks if the startup security is set. func (mgr *AssetsManager) IsStartupSecuritySet() bool { var data bool - mgr.db.ReadWalletConfigValue(sharedW.IsStartupSecuritySetConfigKey, &data) + mgr.readAppConfigValue(sharedW.IsStartupSecuritySetConfigKey, &data) return data } // IsDarkModeOn checks if the dark mode is set. func (mgr *AssetsManager) IsDarkModeOn() bool { var data bool - if !mgr.IsAssetManagerDB() { - return data - } - mgr.db.ReadWalletConfigValue(sharedW.DarkModeConfigKey, &data) + mgr.readAppConfigValue(sharedW.DarkModeConfigKey, &data) return data } -// SetDarkMode sets the dark mode for the wallet. +// SetDarkMode sets the dark mode for the app. func (mgr *AssetsManager) SetDarkMode(data bool) { - mgr.db.SaveWalletConfigValue(sharedW.DarkModeConfigKey, data) + mgr.saveAppConfigValue(sharedW.DarkModeConfigKey, data) } // GetDexServers returns the dex servers. func (mgr *AssetsManager) GetDexServers() (map[string][]byte, error) { servers := make(map[string][]byte, 0) - err := mgr.db.ReadWalletConfigValue(sharedW.KnownDexServersConfigKey, &servers) + err := mgr.readAppConfigValue(sharedW.KnownDexServersConfigKey, &servers) return servers, err } // SaveDexServers saves the dex servers. func (mgr *AssetsManager) SaveDexServers(servers map[string][]byte) { - mgr.db.SaveWalletConfigValue(sharedW.KnownDexServersConfigKey, servers) + mgr.saveAppConfigValue(sharedW.KnownDexServersConfigKey, servers) } // GetCurrencyConversionExchange returns the currency conversion exchange. func (mgr *AssetsManager) GetCurrencyConversionExchange() string { var key string - mgr.db.ReadWalletConfigValue(sharedW.CurrencyConversionConfigKey, &key) + mgr.readAppConfigValue(sharedW.CurrencyConversionConfigKey, &key) if key == "" { return values.DefaultExchangeValue // default exchange value } @@ -158,7 +177,7 @@ func (mgr *AssetsManager) GetCurrencyConversionExchange() string { // SetCurrencyConversionExchange sets the currency conversion exchange. func (mgr *AssetsManager) SetCurrencyConversionExchange(xc string) { - mgr.db.SaveWalletConfigValue(sharedW.CurrencyConversionConfigKey, xc) + mgr.saveAppConfigValue(sharedW.CurrencyConversionConfigKey, xc) go func() { err := mgr.RateSource.ToggleSource(xc) if err != nil { @@ -180,42 +199,42 @@ func (mgr *AssetsManager) ExchangeRateFetchingEnabled() bool { // GetLanguagePreference returns the language preference. func (mgr *AssetsManager) GetLanguagePreference() string { var lang string - mgr.db.ReadWalletConfigValue(sharedW.LanguagePreferenceKey, &lang) + mgr.readAppConfigValue(sharedW.LanguagePreferenceKey, &lang) return lang } // SetLanguagePreference sets the language preference. func (mgr *AssetsManager) SetLanguagePreference(lang string) { - mgr.db.SaveWalletConfigValue(sharedW.LanguagePreferenceKey, lang) + mgr.saveAppConfigValue(sharedW.LanguagePreferenceKey, lang) } // GetUserAgent returns the user agent. func (mgr *AssetsManager) GetUserAgent() string { var data string - mgr.db.ReadWalletConfigValue(sharedW.UserAgentConfigKey, data) + mgr.readAppConfigValue(sharedW.UserAgentConfigKey, data) return data } // SetUserAgent sets the user agent. func (mgr *AssetsManager) SetUserAgent(data string) { - mgr.db.SaveWalletConfigValue(sharedW.UserAgentConfigKey, data) + mgr.saveAppConfigValue(sharedW.UserAgentConfigKey, data) } // IsTransactionNotificationsOn checks if the transaction notifications is set. func (mgr *AssetsManager) IsTransactionNotificationsOn() bool { var data bool - mgr.db.ReadWalletConfigValue(sharedW.TransactionNotificationConfigKey, &data) + mgr.readAppConfigValue(sharedW.TransactionNotificationConfigKey, &data) return data && mgr.IsPrivacyModeOn() } -// SetTransactionsNotifications sets the transaction notifications for the wallet. +// SetTransactionsNotifications sets the transaction notifications for the app. func (mgr *AssetsManager) SetTransactionsNotifications(data bool) { - mgr.db.SaveWalletConfigValue(sharedW.TransactionNotificationConfigKey, data) + mgr.saveAppConfigValue(sharedW.TransactionNotificationConfigKey, data) } -// SetPrivacyMode sets the privacy mode for the wallet. +// SetPrivacyMode sets the privacy mode for the app. func (mgr *AssetsManager) SetPrivacyMode(isActive bool) { - mgr.db.SaveWalletConfigValue(sharedW.PrivacyModeConfigKey, isActive) + mgr.saveAppConfigValue(sharedW.PrivacyModeConfigKey, isActive) mgr.RateSource.ToggleStatus(isActive) if !isActive && mgr.GetCurrencyConversionExchange() != values.DefaultExchangeValue { go mgr.RateSource.Refresh(true) @@ -226,14 +245,14 @@ func (mgr *AssetsManager) SetPrivacyMode(isActive bool) { // If Privacy mode is on, no API calls that can be made. func (mgr *AssetsManager) IsPrivacyModeOn() bool { var data bool - mgr.db.ReadWalletConfigValue(sharedW.PrivacyModeConfigKey, &data) + mgr.readAppConfigValue(sharedW.PrivacyModeConfigKey, &data) return data } -// SetHTTPAPIPrivacyMode sets Http API the privacy mode for the wallet. +// SetHTTPAPIPrivacyMode sets Http API the privacy mode for the app. func (mgr *AssetsManager) SetHTTPAPIPrivacyMode(apiType utils.HTTPAPIType, isActive bool) { dataKey := genKey(sharedW.PrivacyModeConfigKey, apiType) - mgr.db.SaveWalletConfigValue(dataKey, isActive) + mgr.saveAppConfigValue(dataKey, isActive) } // IsHTTPAPIPrivacyModeOff returns true if the given API type is enabled and false @@ -241,14 +260,14 @@ func (mgr *AssetsManager) SetHTTPAPIPrivacyMode(apiType utils.HTTPAPIType, isAct func (mgr *AssetsManager) IsHTTPAPIPrivacyModeOff(apiType utils.HTTPAPIType) bool { var data bool dataKey := genKey(sharedW.PrivacyModeConfigKey, apiType) - mgr.db.ReadWalletConfigValue(dataKey, &data) + mgr.readAppConfigValue(dataKey, &data) return data && !mgr.IsPrivacyModeOn() } // GetLogLevels returns the log levels. func (mgr *AssetsManager) GetLogLevels() string { var logLevel string - mgr.db.ReadWalletConfigValue(sharedW.LogLevelConfigKey, &logLevel) + mgr.readAppConfigValue(sharedW.LogLevelConfigKey, &logLevel) if logLevel == "" { // return default debug level if no option is stored. return utils.DefaultLogLevel @@ -258,19 +277,19 @@ func (mgr *AssetsManager) GetLogLevels() string { // SetLogLevels sets the log levels. func (mgr *AssetsManager) SetLogLevels(logLevel string) { - mgr.db.SaveWalletConfigValue(sharedW.LogLevelConfigKey, logLevel) + mgr.saveAppConfigValue(sharedW.LogLevelConfigKey, logLevel) SetLogLevels(logLevel) } // SetExchangeConfig sets the exchange config for the asset. func (mgr *AssetsManager) SetExchangeConfig(data sharedW.ExchangeConfig) { - mgr.db.SaveWalletConfigValue(sharedW.ExchangeSourceDstnTypeConfigKey, data) + mgr.saveAppConfigValue(sharedW.ExchangeSourceDstnTypeConfigKey, data) } // GetExchangeConfig returns the previously set exchange config for the asset. func (mgr *AssetsManager) GetExchangeConfig() *sharedW.ExchangeConfig { data := &sharedW.ExchangeConfig{} - mgr.db.ReadWalletConfigValue(sharedW.ExchangeSourceDstnTypeConfigKey, data) + mgr.readAppConfigValue(sharedW.ExchangeSourceDstnTypeConfigKey, data) return data } @@ -281,19 +300,19 @@ func (mgr *AssetsManager) IsExchangeConfigSet() bool { // ClearExchangeConfig clears the wallet's exchange config. func (mgr *AssetsManager) ClearExchangeConfig() { - mgr.db.DeleteWalletConfigValue(sharedW.ExchangeSourceDstnTypeConfigKey) + mgr.appConfigDelete(sharedW.ExchangeSourceDstnTypeConfigKey) } // IsTotalBalanceVisible checks if the total balance visibility is set. func (mgr *AssetsManager) IsTotalBalanceVisible() bool { var data bool - mgr.db.ReadWalletConfigValue(sharedW.HideTotalBalanceConfigKey, &data) + mgr.readAppConfigValue(sharedW.HideTotalBalanceConfigKey, &data) return data } -// SetTotalBalanceVisibility sets the transaction notifications for the wallet. +// SetTotalBalanceVisibility sets the transaction notifications for the app. func (mgr *AssetsManager) SetTotalBalanceVisibility(data bool) { - mgr.db.SaveWalletConfigValue(sharedW.HideTotalBalanceConfigKey, data) + mgr.saveAppConfigValue(sharedW.HideTotalBalanceConfigKey, data) } func genKey(prefix, identifier interface{}) string { diff --git a/libwallet/assets_manager.go b/libwallet/assets_manager.go index 210e04d7f..b53f023c9 100644 --- a/libwallet/assets_manager.go +++ b/libwallet/assets_manager.go @@ -55,8 +55,6 @@ type AssetsManager struct { params *sharedW.InitParams Assets *Assets - db sharedW.AssetsManagerDB // Interface to manage db access at the ASM. - shuttingDown chan bool cancelFuncs []context.CancelFunc chainsParams utils.ChainsParams @@ -74,7 +72,7 @@ type AssetsManager struct { // initializeAssetsFields validate the network provided is valid for all assets before proceeding // to initialize the rest of the other fields. -func initializeAssetsFields(rootDir, dbDriver, logDir string, netType utils.NetworkType) (*AssetsManager, error) { +func initializeAssetsFields(rootDir, dbDriver, logDir string, netType utils.NetworkType, dexTestAddr string) (*AssetsManager, error) { dcrChainParams, err := initializeDCRWalletParameters(netType) if err != nil { log.Errorf("error initializing DCR parameters: %s", err.Error()) @@ -94,10 +92,11 @@ func initializeAssetsFields(rootDir, dbDriver, logDir string, netType utils.Netw } params := &sharedW.InitParams{ - DbDriver: dbDriver, - RootDir: rootDir, - NetType: netType, - LogDir: logDir, + DbDriver: dbDriver, + RootDir: rootDir, + NetType: netType, + LogDir: logDir, + DEXTestAddr: dexTestAddr, } mgr := &AssetsManager{ @@ -120,7 +119,7 @@ func initializeAssetsFields(rootDir, dbDriver, logDir string, netType utils.Netw } // NewAssetsManager creates a new AssetsManager instance. -func NewAssetsManager(rootDir, logDir string, netType utils.NetworkType) (*AssetsManager, error) { +func NewAssetsManager(rootDir, logDir string, netType utils.NetworkType, dexTestAddr string) (*AssetsManager, error) { errors.Separator = ":: " // Create a root dir that has the path up the network folder. @@ -131,7 +130,7 @@ func NewAssetsManager(rootDir, logDir string, netType utils.NetworkType) (*Asset // validate the network type before proceeding to initialize the othe fields. dbDriver := "bdb" // TODO: Should be a constant. - mgr, err := initializeAssetsFields(rootDir, dbDriver, logDir, netType) + mgr, err := initializeAssetsFields(rootDir, dbDriver, logDir, netType, dexTestAddr) if err != nil { return nil, err } @@ -194,11 +193,6 @@ func NewAssetsManager(rootDir, logDir string, netType utils.NetworkType) (*Asset return nil, err } - // Attempt to set the log levels if a valid db interface was found. - if mgr.IsAssetManagerDB() { - mgr.GetLogLevels() - } - mgr.listenForShutdown() return mgr, nil @@ -214,14 +208,8 @@ func (mgr *AssetsManager) initRateSource() (err error) { ctx, cancel := context.WithCancel(context.Background()) mgr.cancelFuncs = append(mgr.cancelFuncs, cancel) - rateSource := values.DefaultExchangeValue - disabled := true - // Check if database has been initialized. ATM, new setups need a wallet - // before mgr.db is initialized. - if mgr.db != nil { - rateSource = mgr.GetCurrencyConversionExchange() - disabled = mgr.IsPrivacyModeOn() - } + rateSource := mgr.GetCurrencyConversionExchange() + disabled := mgr.IsPrivacyModeOn() mgr.RateSource, err = ext.NewCommonRateSource(ctx, rateSource) if err != nil { @@ -254,9 +242,7 @@ func (mgr *AssetsManager) initRateSource() (err error) { return nil } -// prepareExistingWallets loads all the valid and bad wallets. It also attempts -// to extract the assets manager db access interface from one of the validly -// created wallets. +// prepareExistingWallets loads all the valid and bad wallets. func (mgr *AssetsManager) prepareExistingWallets() error { // read all stored wallets info from the db and initialize wallets interfaces. query := mgr.params.DB.Select(q.True()).OrderBy("ID") @@ -266,21 +252,6 @@ func (mgr *AssetsManager) prepareExistingWallets() error { return err } - isOK := func(val interface{}) bool { - var ok bool - if val != nil { - // Extracts the walletExists method and checks if the current wallet - // walletDataDb file exists. Returns true if affirmative. - ok, _ = val.(interface{ WalletExists() (bool, error) }).WalletExists() - // Extracts the asset manager db interface from one of the wallets. - // Assets Manager Db interface that exists in all wallets by default. - if mgr.db == nil { - mgr.setDBInterface(val.(sharedW.AssetsManagerDB)) - } - } - return ok - } - // prepare the wallets loaded from db for use for _, wallet := range wallets { // preset the network type so as to generate correct folder path @@ -292,10 +263,6 @@ func (mgr *AssetsManager) prepareExistingWallets() error { switch wallet.Type { case utils.BTCWalletAsset: w, err := btc.LoadExisting(wallet, mgr.params) - if err == nil && !isOK(w) { - err = fmt.Errorf("missing wallet database file: %v", path) - log.Warn(err) - } if err != nil { mgr.Assets.BTC.BadWallets[wallet.ID] = wallet log.Warnf("Ignored btc wallet load error for wallet %d (%s)", wallet.ID, wallet.Name) @@ -305,10 +272,6 @@ func (mgr *AssetsManager) prepareExistingWallets() error { case utils.DCRWalletAsset: w, err := dcr.LoadExisting(wallet, mgr.params) - if err == nil && !isOK(w) { - err = fmt.Errorf("missing wallet database file: %v", path) - log.Debug(err) - } if err != nil { mgr.Assets.DCR.BadWallets[wallet.ID] = wallet log.Warnf("Ignored dcr wallet load error for wallet %d (%s)", wallet.ID, wallet.Name) @@ -318,10 +281,6 @@ func (mgr *AssetsManager) prepareExistingWallets() error { case utils.LTCWalletAsset: w, err := ltc.LoadExisting(wallet, mgr.params) - if err == nil && !isOK(w) { - err = fmt.Errorf("missing wallet database file: %v", path) - log.Debug(err) - } if err != nil { mgr.Assets.LTC.BadWallets[wallet.ID] = wallet log.Warnf("Ignored ltc wallet load error for wallet %d (%s)", wallet.ID, wallet.Name) @@ -408,14 +367,6 @@ func (mgr *AssetsManager) LogDir() string { return filepath.Join(mgr.params.RootDir, logFileName) } -// IsAssetManagerDB returns true if the asset manager db interface was extracted -// from one of the loaded valid wallets. Assets Manager Db interface exists in -// all wallets by default. If no valid asset manager db interface exists, -// there is no valid wallet loaded yet; - they maybe no wallets at all to load. -func (mgr *AssetsManager) IsAssetManagerDB() bool { - return mgr.db != nil -} - // OpenWallets opens all wallets in the assets manager. func (mgr *AssetsManager) OpenWallets(startupPassphrase string) error { for _, wallet := range mgr.AllWallets() { diff --git a/libwallet/btc.go b/libwallet/btc.go index 567cce705..fea4fa684 100644 --- a/libwallet/btc.go +++ b/libwallet/btc.go @@ -8,7 +8,6 @@ import ( "github.com/btcsuite/btcwallet/waddrmgr" "github.com/crypto-power/cryptopower/libwallet/assets/btc" - "github.com/crypto-power/cryptopower/libwallet/assets/ltc" sharedW "github.com/crypto-power/cryptopower/libwallet/assets/wallet" "github.com/crypto-power/cryptopower/libwallet/utils" ) @@ -36,11 +35,6 @@ func (mgr *AssetsManager) CreateNewBTCWallet(walletName, privatePassphrase strin mgr.Assets.BTC.Wallets[wallet.GetWalletID()] = wallet - // extract the db interface if it hasn't been set already. - if mgr.db == nil && wallet != nil { - mgr.setDBInterface(wallet.(sharedW.AssetsManagerDB)) - } - return wallet, nil } @@ -53,11 +47,6 @@ func (mgr *AssetsManager) CreateNewBTCWatchOnlyWallet(walletName, extendedPublic mgr.Assets.BTC.Wallets[wallet.GetWalletID()] = wallet - // extract the db interface if it hasn't been set already. - if mgr.db == nil && wallet != nil { - mgr.setDBInterface(wallet.(sharedW.AssetsManagerDB)) - } - return wallet, nil } @@ -75,11 +64,6 @@ func (mgr *AssetsManager) RestoreBTCWallet(walletName, seedMnemonic, privatePass mgr.Assets.BTC.Wallets[wallet.GetWalletID()] = wallet - // extract the db interface if it hasn't been set already. - if mgr.db == nil && wallet != nil { - mgr.setDBInterface(wallet.(sharedW.AssetsManagerDB)) - } - return wallet, nil } @@ -157,25 +141,3 @@ func (mgr *AssetsManager) BTCWalletWithSeed(seedMnemonic string) (int, error) { } return -1, nil } - -// RestoreLTCWallet restores a LTC wallet from a seed and returns it. -func (mgr *AssetsManager) RestoreLTCWallet(walletName, seedMnemonic, privatePassphrase string, privatePassphraseType int32) (sharedW.Asset, error) { - pass := &sharedW.AuthInfo{ - Name: walletName, - PrivatePass: privatePassphrase, - PrivatePassType: privatePassphraseType, - } - wallet, err := ltc.RestoreWallet(seedMnemonic, pass, mgr.params) - if err != nil { - return nil, err - } - - mgr.Assets.LTC.Wallets[wallet.GetWalletID()] = wallet - - // extract the db interface if it hasn't been set already. - if mgr.db == nil && wallet != nil { - mgr.setDBInterface(wallet.(sharedW.AssetsManagerDB)) - } - - return wallet, nil -} diff --git a/libwallet/dcr.go b/libwallet/dcr.go index e7d5412ac..f44ce9047 100644 --- a/libwallet/dcr.go +++ b/libwallet/dcr.go @@ -38,11 +38,6 @@ func (mgr *AssetsManager) CreateNewDCRWallet(walletName, privatePassphrase strin mgr.Assets.DCR.Wallets[wallet.GetWalletID()] = wallet - // extract the db interface if it hasn't been set already. - if mgr.db == nil && wallet != nil { - mgr.setDBInterface(wallet.(sharedW.AssetsManagerDB)) - } - // Allow spending from the default account by default. wallet.SetBoolConfigValueForKey(sharedW.SpendUnmixedFundsKey, true) @@ -58,11 +53,6 @@ func (mgr *AssetsManager) CreateNewDCRWatchOnlyWallet(walletName, extendedPublic mgr.Assets.DCR.Wallets[wallet.GetWalletID()] = wallet - // extract the db interface if it hasn't been set already. - if mgr.db == nil && wallet != nil { - mgr.setDBInterface(wallet.(sharedW.AssetsManagerDB)) - } - // Allow spending from the default account by default. wallet.SetBoolConfigValueForKey(sharedW.SpendUnmixedFundsKey, true) @@ -83,11 +73,6 @@ func (mgr *AssetsManager) RestoreDCRWallet(walletName, seedMnemonic, privatePass mgr.Assets.DCR.Wallets[wallet.GetWalletID()] = wallet - // extract the db interface if it hasn't been set already. - if mgr.db == nil && wallet != nil { - mgr.setDBInterface(wallet.(sharedW.AssetsManagerDB)) - } - // Allow spending from the default account by default. wallet.SetBoolConfigValueForKey(sharedW.SpendUnmixedFundsKey, true) diff --git a/libwallet/dex_wallets_loader.go b/libwallet/dex_wallets_loader.go index 4d0c28980..6836b6cfe 100644 --- a/libwallet/dex_wallets_loader.go +++ b/libwallet/dex_wallets_loader.go @@ -85,7 +85,8 @@ func prepareDexSupportForDCRWallet() { return nil, fmt.Errorf("no wallet exists with ID %q", walletIDStr) } - walletParams := wallet.Internal().DCR.ChainParams() + dcrWallet := wallet.Internal().DCR + walletParams := dcrWallet.ChainParams() if walletParams.Net != chainParams.Net { return nil, fmt.Errorf("selected wallet is for %s network, expected %s", walletParams.Name, chainParams.Name) } @@ -111,7 +112,7 @@ func prepareDexSupportForDCRWallet() { return nil, fmt.Errorf("DEX wallet not supported for %s", walletParams.Name) } - return dcr.NewDEXWallet(dcrAsset, accountNumber, dcrAsset.SyncData()), nil + return dcr.NewDEXWallet(dcrWallet, dcrAsset, accountNumber, dcrAsset.SyncData()), nil } dexDcr.RegisterCustomWallet(walletMaker, def) diff --git a/libwallet/ltc.go b/libwallet/ltc.go index 3e1abe71c..c658255c8 100644 --- a/libwallet/ltc.go +++ b/libwallet/ltc.go @@ -35,11 +35,6 @@ func (mgr *AssetsManager) CreateNewLTCWallet(walletName, privatePassphrase strin mgr.Assets.LTC.Wallets[wallet.GetWalletID()] = wallet - // extract the db interface if it hasn't been set already. - if mgr.db == nil && wallet != nil { - mgr.setDBInterface(wallet.(sharedW.AssetsManagerDB)) - } - return wallet, nil } @@ -52,10 +47,22 @@ func (mgr *AssetsManager) CreateNewLTCWatchOnlyWallet(walletName, extendedPublic mgr.Assets.LTC.Wallets[wallet.GetWalletID()] = wallet - // extract the db interface if it hasn't been set already. - if mgr.db == nil && wallet != nil { - mgr.setDBInterface(wallet.(sharedW.AssetsManagerDB)) + return wallet, nil +} + +// RestoreLTCWallet restores a LTC wallet from a seed and returns it. +func (mgr *AssetsManager) RestoreLTCWallet(walletName, seedMnemonic, privatePassphrase string, privatePassphraseType int32) (sharedW.Asset, error) { + pass := &sharedW.AuthInfo{ + Name: walletName, + PrivatePass: privatePassphrase, + PrivatePassType: privatePassphraseType, } + wallet, err := ltc.RestoreWallet(seedMnemonic, pass, mgr.params) + if err != nil { + return nil, err + } + + mgr.Assets.LTC.Wallets[wallet.GetWalletID()] = wallet return wallet, nil } diff --git a/libwallet/utils/httpclient.go b/libwallet/utils/httpclient.go index 88d3a981a..567365a8b 100644 --- a/libwallet/utils/httpclient.go +++ b/libwallet/utils/httpclient.go @@ -29,6 +29,7 @@ const ( FeeRateHTTPAPI ExchangeHTTPAPI VspAPI + UpdateAPI ) type ( diff --git a/libwallet/utils/netparams.go b/libwallet/utils/netparams.go index 93a3b18f3..90feffa5b 100644 --- a/libwallet/utils/netparams.go +++ b/libwallet/utils/netparams.go @@ -18,6 +18,7 @@ const ( Testnet NetworkType = "testnet" Regression NetworkType = "regression" Simulation NetworkType = "simulation" + DEXTest NetworkType = "dextest" Unknown NetworkType = "unknown" ) @@ -39,6 +40,8 @@ func ToNetworkType(str string) NetworkType { return Regression case "simulation", "sim", "simnet": return Simulation + case "dextest": + return DEXTest default: return Unknown } @@ -52,20 +55,29 @@ type ChainsParams struct { } var ( - DCRmainnetParams = dcrcfg.MainNetParams() - DCRtestnetParams = dcrcfg.TestNet3Params() - DCRSimnetParams = dcrcfg.SimNetParams() - DCRRegnetParams = dcrcfg.RegNetParams() - BTCmainnetParams = &btccfg.MainNetParams - BTCtestnetParams = &btccfg.TestNet3Params - BTCSimnetParams = &btccfg.SimNetParams - BTCRegnetParams = &btccfg.RegressionNetParams - LTCmainnetParams = <ccfg.MainNetParams - LTCtestnetParams = <ccfg.TestNet4Params - LTCSimnetParams = <ccfg.SimNetParams - LTCRegnetParams = <ccfg.RegressionNetParams + DCRmainnetParams = dcrcfg.MainNetParams() + DCRtestnetParams = dcrcfg.TestNet3Params() + DCRSimnetParams = dcrcfg.SimNetParams() + DCRRegnetParams = dcrcfg.RegNetParams() + BTCmainnetParams = &btccfg.MainNetParams + BTCtestnetParams = &btccfg.TestNet3Params + BTCSimnetParams = &btccfg.SimNetParams + BTCRegnetParamsVal = btccfg.RegressionNetParams + LTCmainnetParams = <ccfg.MainNetParams + LTCtestnetParams = <ccfg.TestNet4Params + LTCSimnetParams = <ccfg.SimNetParams + LTCRegnetParamsVal = ltccfg.RegressionNetParams + DCRDEXSimnetParams = dcrcfg.SimNetParams() + BTCDEXRegnetParamsVal = btccfg.RegressionNetParams + LTCDEXRegnetParamsVal = ltccfg.RegressionNetParams ) +func init() { + DCRDEXSimnetParams.DefaultPort = "19560" + BTCDEXRegnetParamsVal.DefaultPort = "20575" + LTCDEXRegnetParamsVal.DefaultPort = "20585" +} + // NetDir returns data directory name for a given asset's type and network connected. // If "unknown" is returned, unsupported asset type or network was detected. func NetDir(assetType AssetType, netType NetworkType) string { @@ -99,6 +111,8 @@ func DCRChainParams(netType NetworkType) (*dcrcfg.Params, error) { return DCRSimnetParams, nil case Regression: return DCRRegnetParams, nil + case DEXTest: + return DCRDEXSimnetParams, nil default: return nil, fmt.Errorf("%v: (%v)", ErrInvalidNet, netType) } @@ -115,7 +129,9 @@ func BTCChainParams(netType NetworkType) (*btccfg.Params, error) { case Simulation: return BTCSimnetParams, nil case Regression: - return BTCRegnetParams, nil + return &BTCRegnetParamsVal, nil + case DEXTest: + return &BTCDEXRegnetParamsVal, nil default: return nil, fmt.Errorf("%v: (%v)", ErrInvalidNet, netType) } @@ -132,7 +148,9 @@ func LTCChainParams(netType NetworkType) (*ltccfg.Params, error) { case Simulation: return LTCSimnetParams, nil case Regression: - return LTCRegnetParams, nil + return <CRegnetParamsVal, nil + case DEXTest: + return <CDEXRegnetParamsVal, nil default: return nil, fmt.Errorf("%v: (%v)", ErrInvalidNet, netType) } diff --git a/main.go b/main.go index 773251c62..d61704923 100644 --- a/main.go +++ b/main.go @@ -69,18 +69,15 @@ func main() { logger.SetLogLevels(cfg.DebugLevel) } - assetsManager, err := libwallet.NewAssetsManager(cfg.HomeDir, logDir, netType) + assetsManager, err := libwallet.NewAssetsManager(cfg.HomeDir, logDir, netType, cfg.DEXTestAddr) if err != nil { return nil, err } // if debuglevel is passed at commandLine persist the option. - if cfg.DebugLevel != "" && assetsManager.IsAssetManagerDB() { + if cfg.DebugLevel != "" { assetsManager.SetLogLevels(cfg.DebugLevel) - } - - if assetsManager.IsAssetManagerDB() { - // now that assets manager is up, set stored debuglevel + } else { logger.SetLogLevels(assetsManager.GetLogLevels()) } diff --git a/ui/modal/info_modal.go b/ui/modal/info_modal.go index fc922cae6..a5cfc264c 100644 --- a/ui/modal/info_modal.go +++ b/ui/modal/info_modal.go @@ -364,7 +364,7 @@ func (in *InfoModal) Layout(gtx layout.Context) D { text := in.Theme.Body1(in.subtitle) text.Alignment = in.subTileTextAlignment text.Color = in.Theme.Color.GrayText2 - return text.Layout(gtx) + return layout.Inset{Bottom: values.MarginPadding8}.Layout(gtx, text.Layout) } var w []layout.Widget diff --git a/ui/page/components/components.go b/ui/page/components/components.go index 9d81eda1d..4e375e468 100644 --- a/ui/page/components/components.go +++ b/ui/page/components/components.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "image/color" + "net/http" "os/exec" "runtime" "strconv" @@ -25,6 +26,7 @@ import ( sharedW "github.com/crypto-power/cryptopower/libwallet/assets/wallet" "github.com/crypto-power/cryptopower/libwallet/instantswap" "github.com/crypto-power/cryptopower/libwallet/txhelper" + "github.com/crypto-power/cryptopower/libwallet/utils" libutils "github.com/crypto-power/cryptopower/libwallet/utils" "github.com/crypto-power/cryptopower/ui/cryptomaterial" "github.com/crypto-power/cryptopower/ui/load" @@ -35,6 +37,7 @@ const ( Uint32Size = 32 // 32 or 64 ? shifting 32-bit value by 32 bits will always clear it MaxInt32 = 1<<(Uint32Size-1) - 1 WalletsPageID = "Wallets" + releaseURL = "https://api.github.com/repos/crypto-power/cryptopower/releases/latest" ) type ( @@ -74,6 +77,11 @@ type ( Alignment layout.Alignment WeightSum float32 } + + ReleaseResponse struct { + TagName string `json:"tag_name"` + URL string `json:"html_url"` + } ) // Container is simply a wrapper for the Inset type. Its purpose is to differentiate the use of an inset as a padding or @@ -984,3 +992,67 @@ func GetServerIcon(theme *cryptomaterial.Theme, serverName string) *cryptomateri return theme.Icons.AddExchange } } + +// CheckForUpdate checks if a new version of the app is available +// by comparing the current version with the latest release version +// available on GitHub. +func CheckForUpdate(l *load.Load) *ReleaseResponse { + req := &utils.ReqConfig{ + Method: http.MethodGet, + HTTPURL: releaseURL, + } + + releaseResponse := new(ReleaseResponse) + if _, err := utils.HTTPRequest(req, &releaseResponse); err != nil { + log.Error("checking for update failed:", err) + return nil + } + + isUpdateAvaialble := compareVersions(releaseResponse.TagName, l.Version()) + if !isUpdateAvaialble { + return nil + } + + return releaseResponse +} + +// compareVersions compares two semantic version strings and returns +// true if version1 is greater than version2, otherwise returns false. +func compareVersions(version1, version2 string) bool { + // Remove the 'v' prefix, if it exists + v1 := strings.TrimPrefix(version1, "v") + v2 := strings.TrimPrefix(version2, "v") + + // Split the version strings into their components + parts1 := strings.Split(v1, ".") + parts2 := strings.Split(v2, ".") + + // Iterate through the version parts and compare + for i := 0; i < len(parts1) && i < len(parts2); i++ { + // Convert string parts to integers for comparison + num1, err1 := strconv.Atoi(parts1[i]) + num2, err2 := strconv.Atoi(parts2[i]) + + // Handle potential errors in conversion + if err1 != nil || err2 != nil { + log.Error("Error converting version numbers:", err1, err2) + return false + } + + // Compare the version numbers + if num1 > num2 { + return true // version1 is greater + } else if num1 < num2 { + return false // version2 is greater + } + } + + // If one version has more numbers than the other, compare those + if len(parts1) > len(parts2) { + return true + } else if len(parts1) < len(parts2) { + return false + } + + return false // versions are equal +} diff --git a/ui/page/components/wallet_sync_info.go b/ui/page/components/wallet_sync_info.go index eeca3f621..8ed0d4bae 100644 --- a/ui/page/components/wallet_sync_info.go +++ b/ui/page/components/wallet_sync_info.go @@ -462,6 +462,7 @@ func (wsi *WalletSyncInfo) progressStatusDetails() (int, string) { func (wsi *WalletSyncInfo) layoutAutoSyncSection(gtx C) D { return layout.Flex{}.Layout(gtx, layout.Rigid(func(gtx C) D { + wsi.syncSwitch.SetChecked(wsi.wallet.IsSyncing() || wsi.wallet.IsSynced()) return layout.Inset{Right: values.MarginPadding10}.Layout(gtx, wsi.syncSwitch.Layout) }), layout.Rigid(wsi.Theme.Body2(values.String(values.StrSync)).Layout), diff --git a/ui/page/dcrdex/certs.go b/ui/page/dcrdex/certs.go index 4ce8e6f22..57b79216a 100644 --- a/ui/page/dcrdex/certs.go +++ b/ui/page/dcrdex/certs.go @@ -10,6 +10,7 @@ package dcrdex const ( decredDEXServerMainnet = "dex.decred.org:7232" decredDEXServerTestnet = "bison.exchange:27232" + decredDEXServerSimnet = "127.0.0.1:17273" ) var dexDotDecredCert = []byte(`-----BEGIN CERTIFICATE----- @@ -49,7 +50,27 @@ hW4bgtaK -----END CERTIFICATE----- `) +var dexSimSSGenCert = []byte(`-----BEGIN CERTIFICATE----- +MIICpTCCAgagAwIBAgIQZMfxMkSi24xMr4CClCODrzAKBggqhkjOPQQDBDBJMSIw +IAYDVQQKExlkY3JkZXggYXV0b2dlbmVyYXRlZCBjZXJ0MSMwIQYDVQQDExp1YnVu +dHUtcy0xdmNwdS0yZ2ItbG9uMS0wMTAeFw0yMDA2MDgxMjM4MjNaFw0zMDA2MDcx +MjM4MjNaMEkxIjAgBgNVBAoTGWRjcmRleCBhdXRvZ2VuZXJhdGVkIGNlcnQxIzAh +BgNVBAMTGnVidW50dS1zLTF2Y3B1LTJnYi1sb24xLTAxMIGbMBAGByqGSM49AgEG +BSuBBAAjA4GGAAQApXJpVD7si8yxoITESq+xaXWtEpsCWU7X+8isRDj1cFfH53K6 +/XNvn3G+Yq0L22Q8pMozGukA7KuCQAAL0xnuo10AecWBN0Zo2BLHvpwKkmAs71C+ +5BITJksqFxvjwyMKbo3L/5x8S/JmAWrZoepBLfQ7HcoPqLAcg0XoIgJjOyFZgc+j +gYwwgYkwDgYDVR0PAQH/BAQDAgKkMA8GA1UdEwEB/wQFMAMBAf8wZgYDVR0RBF8w +XYIadWJ1bnR1LXMtMXZjcHUtMmdiLWxvbjEtMDGCCWxvY2FsaG9zdIcEfwAAAYcQ +AAAAAAAAAAAAAAAAAAAAAYcEsj5QQYcEChAABYcQ/oAAAAAAAAAYPqf//vUPXDAK +BggqhkjOPQQDBAOBjAAwgYgCQgFMEhyTXnT8phDJAnzLbYRktg7rTAbTuQRDp1PE +jf6b2Df4DkSX7JPXvVi3NeBru+mnrOkHBUMqZd0m036aC4q/ZAJCASa+olu4Isx7 +8JE3XB6kGr+s48eIFPtmq1D0gOvRr3yMHrhJe3XDNqvppcHihG0qNb0gyaiX18Cv +vF8Ti1x2vTkD +-----END CERTIFICATE----- +`) + var CertStore = map[string][]byte{ decredDEXServerMainnet: dexDotDecredCert, decredDEXServerTestnet: dexTestSSGenCert, + decredDEXServerSimnet: dexSimSSGenCert, } diff --git a/ui/page/dcrdex/dcrdex_page.go b/ui/page/dcrdex/dcrdex_page.go index 165c96cca..0249b3e66 100644 --- a/ui/page/dcrdex/dcrdex_page.go +++ b/ui/page/dcrdex/dcrdex_page.go @@ -33,7 +33,7 @@ type DEXPage struct { splashPageInfoButton cryptomaterial.IconButton splashPageContainer *widget.List startTradingBtn cryptomaterial.Button - isDexFirstVisit bool + showSplashPage bool dexIsLoading bool materialLoader material.LoaderStyle } @@ -48,13 +48,13 @@ func NewDEXPage(l *load.Load) *DEXPage { Alignment: layout.Middle, Axis: layout.Vertical, }}, - isDexFirstVisit: true, + showSplashPage: true, switchToTestnetBtn: l.Theme.Button(values.String(values.StrSwitchToTestnet)), materialLoader: material.Loader(l.Theme.Base), } if dp.AssetsManager.DEXCInitialized() && dp.AssetsManager.DexClient().InitializedWithPassword() { - dp.isDexFirstVisit = false + dp.showSplashPage = false } // Init splash page more info widget. @@ -80,16 +80,25 @@ func (pg *DEXPage) OnNavigatedTo() { if pg.CurrentPage() != nil { pg.CurrentPage().OnNavigatedTo() + } else { + pg.prepareInitialPage() + } +} + +// prepareInitialPage starts a goroutine that waits for dexc to get ready before +// displaying an appropriate page. +func (pg *DEXPage) prepareInitialPage() { + dexClient := pg.AssetsManager.DexClient() + if dexClient == nil { return } pg.dexIsLoading = true go func() { - dexc := pg.AssetsManager.DexClient() - <-dexc.Ready() + <-dexClient.Ready() showOnBoardingPage := true - if len(dexc.Exchanges()) != 0 { // has at least one exchange + if len(dexClient.Exchanges()) != 0 { // has at least one exchange _, _, pendingBond := pendingBondConfirmation(pg.AssetsManager, "") showOnBoardingPage = pendingBond != nil } @@ -101,6 +110,7 @@ func (pg *DEXPage) OnNavigatedTo() { } pg.dexIsLoading = false + pg.showSplashPage = false }() } @@ -108,14 +118,21 @@ func (pg *DEXPage) OnNavigatedTo() { // eventually drawn on screen. // Part of the load.Page interface. func (pg *DEXPage) Layout(gtx C) D { - if pg.isDexFirstVisit || pg.dexIsLoading { + isMainnet := pg.AssetsManager.NetType() == utils.Mainnet + if !isMainnet && (!pg.AssetsManager.DEXCInitialized() || pg.CurrentPage() == nil) { // dexc must have been reset. + pg.showSplashPage = true + if !pg.dexIsLoading { + pg.ParentNavigator().CloseAllPages() + pg.prepareInitialPage() + } + } + + if pg.showSplashPage || pg.dexIsLoading { return pg.Theme.List(pg.splashPageContainer).Layout(gtx, 1, func(gtx C, i int) D { return pg.splashPage(gtx) }) } - hasMultipleWallets := pg.isMultipleAssetTypeWalletAvailable() - isMainnet := pg.AssetsManager.NetType() == utils.Mainnet var msg string var actionBtn *cryptomaterial.Button if isMainnet { @@ -123,10 +140,8 @@ func (pg *DEXPage) Layout(gtx C) D { actionBtn = &pg.switchToTestnetBtn } msg = values.String(values.StrDexMainnetNotReady) - } else if !hasMultipleWallets { + } else if hasMultipleWallets := pg.isMultipleAssetTypeWalletAvailable(); !hasMultipleWallets { msg = values.String(values.StrMultipleAssetRequiredMsg) - } else if !pg.AssetsManager.DEXCInitialized() || pg.CurrentPage() == nil { - msg = values.String(values.StrDEXInitErrorMsg) } if msg != "" { @@ -167,7 +182,7 @@ func (pg *DEXPage) HandleUserInteractions() { pg.showInfoModal() } if pg.startTradingBtn.Button.Clicked() { - pg.isDexFirstVisit = false + pg.showSplashPage = false } } diff --git a/ui/page/dcrdex/dex_onboarding_page.go b/ui/page/dcrdex/dex_onboarding_page.go index ce559054f..5ba34feaf 100644 --- a/ui/page/dcrdex/dex_onboarding_page.go +++ b/ui/page/dcrdex/dex_onboarding_page.go @@ -45,6 +45,9 @@ var ( libutils.Testnet: {{ Text: decredDEXServerTestnet, }}, + libutils.DEXTest: {{ + Text: decredDEXServerSimnet, + }}, } // formWidth is the width for form elements on the onboarding DEX page. @@ -54,6 +57,7 @@ var ( dp16 = values.MarginPadding16 dp2 = values.MarginPadding2 dp10 = values.MarginPadding10 + dp12 = values.MarginPadding12 ) // onboardingStep is each step of the flow required for a user to create a DEX @@ -500,10 +504,14 @@ func (pg *DEXOnboarding) formFooterButtons(gtx C) D { nextBtnEnabled = pg.validateBondStrength() && pg.bondAccountHasEnough() && !pg.isLoading case onBoardingStepWaitForConfirmation: xc, err := dexc.Exchange(pg.bondServer.url) - nextBtnEnabled = err != nil && xc.Auth.EffectiveTier > 0 + nextBtnEnabled = err == nil && xc.Auth.EffectiveTier > 0 hideFooter = !nextBtnEnabled addBackBtn = false - if nextBtnEnabled { + if !dexc.IsLoggedIn() { + hideFooter = false + nextBtnEnabled = true + nextBtnText = values.String(values.StrLogin) + } else if nextBtnEnabled { nextBtnText = values.String(values.StrSkip) } } @@ -705,7 +713,6 @@ func (pg *DEXOnboarding) viewOnlyCard(bg *color.NRGBA, info func(gtx C) D) func( cardBg = *bg } return func(gtx C) D { - dp12 := values.MarginPadding12 dp15 := values.MarginPadding15 return cryptomaterial.LinearLayout{ Width: cryptomaterial.MatchParent, @@ -731,9 +738,28 @@ func (pg *DEXOnboarding) viewOnlyCard(bg *color.NRGBA, info func(gtx C) D) func( } } +func (pg *DEXOnboarding) loginDEXForPendingBondsLayout(gtx C) D { + return layout.Flex{Axis: layout.Vertical, Alignment: layout.Middle}.Layout(gtx, + layout.Rigid(func(gtx C) D { + return centerLayout(gtx, dp20, dp12, pg.Theme.H6(values.String(values.StrLogin)).Layout) + }), + layout.Rigid(func(gtx C) D { + return centerLayout(gtx, 0, 0, pg.Theme.Body1(values.String(values.StrLoginDEXForPendingBonds)).Layout) + }), + layout.Rigid(func(gtx C) D { + pg.passwordEditor.Hint = values.String(values.StrDexPassword) + return layout.Inset{Top: dp16}.Layout(gtx, pg.passwordEditor.Layout) + }), + layout.Rigid(pg.formFooterButtons), + ) +} + func (pg *DEXOnboarding) stepWaitForBondConfirmation(gtx C) D { - dp12 := values.MarginPadding12 - layoutFlex := layout.Flex{Axis: layout.Vertical, Alignment: layout.Middle}.Layout(gtx, + if !pg.AssetsManager.DexClient().IsLoggedIn() { + return pg.loginDEXForPendingBondsLayout(gtx) + } + + return layout.Flex{Axis: layout.Vertical, Alignment: layout.Middle}.Layout(gtx, layout.Rigid(func(gtx C) D { return centerLayout(gtx, dp20, dp12, pg.Theme.H6(values.String(values.StrPostBond)).Layout) }), @@ -811,8 +837,6 @@ func (pg *DEXOnboarding) stepWaitForBondConfirmation(gtx C) D { }), layout.Rigid(pg.formFooterButtons), ) - - return layoutFlex } func (pg *DEXOnboarding) bondAmountInfoDisplay(gtx C) D { @@ -963,10 +987,35 @@ func (pg *DEXOnboarding) HandleUserInteractions() { pg.bondServer = serverInfo pg.isLoading = true - go func() { + if !dexc.InitializedWithPassword() || len(pg.dexPass) > 0 { + go func() { + pg.connectServerAndPrepareForBonding() + pg.isLoading = false + }() + break + } + + // If the user has already initialized using their dex seed and we + // don't have a cache because they navigated away from this page, + // prompt the user to provide their password now so we can use + // dexClient.DiscoverAccount in + // pg.connectServerAndPrepareForBonding. + // Note: User is already logged in from before, but we don't have a + // temporary cache to work with, so just ask for the password. + // Re-login is a no-op. + callbackFn := func(password string) { + pg.dexPass = []byte(password) pg.connectServerAndPrepareForBonding() pg.isLoading = false - }() + } + + dexPasswordModal := dexLoginModal(pg.Load, pg.AssetsManager.DexClient(), callbackFn). + Title(values.String(values.StrDexPassword)). + SetDescription(""). + SetNegativeButtonCallback(func() { + pg.isLoading = false + }) + pg.ParentWindow().ShowModal(dexPasswordModal) case onboardingPostBond: // Validate all input fields. @@ -981,54 +1030,65 @@ func (pg *DEXOnboarding) HandleUserInteractions() { return } - // Initialize with password now, if dex password has not been - // initialized. pg.isLoading = true - if !dexc.InitializedWithPassword() { - go func() { - // Set password. - pg.dexPass = []byte(pg.passwordEditor.Editor.Text()) - if err := dexc.InitWithPassword(pg.dexPass, nil); err != nil { - pg.isLoading = false - pg.notifyError(err.Error()) - return - } + go func() { + if dexc.InitializedWithPassword() { + pg.postBond() + pg.isLoading = false + return + } - // Login. - err := dexc.Login(pg.dexPass) - if err != nil { - pg.isLoading = false - pg.notifyError(err.Error()) - return - } + // DEX has not been initialized with a password, do it now. + pg.dexPass = []byte(pg.passwordEditor.Editor.Text()) + if err := dexc.InitWithPassword(pg.dexPass, nil); err != nil { + pg.isLoading = false + pg.notifyError(err.Error()) + return + } - pg.postBond() - }() + // Login. + err := dexc.Login(pg.dexPass) + if err != nil { + pg.isLoading = false + pg.notifyError(err.Error()) + return + } + + pg.postBond() + }() + + case onBoardingStepWaitForConfirmation: + if dexc.IsLoggedIn() { + break // nothing to do + } + pass := pg.passwordEditor.Editor.Text() + if pass == "" { + pg.passwordEditor.SetError(values.String(values.StrErrPassEmpty)) return } - // dexc password is already set. - dexPasswordModal := modal.NewCreatePasswordModal(pg.Load). - EnableName(false). - EnableConfirmPassword(false). - Title(values.String(values.StrDexPassword)). - PasswordHint(values.String(values.StrDexPassword)). - SetPositiveButtonCallback(func(_, password string, pm *modal.CreatePasswordModal) bool { - pg.dexPass = []byte(password) - err := dexc.Login(pg.dexPass) - if err == nil { - pg.postBond() - return true + pg.isLoading = true + go func() { + pg.dexPass = []byte(pass) + err := dexc.Login(pg.dexPass) + if err != nil { + pg.notifyError(err.Error()) + } else { + // Check now if we should still wait. + xc, err := dexc.Exchange(pg.bondServer.url) + if err != nil { + pg.notifyError(err.Error()) + } else if len(xc.Auth.PendingBonds) == 0 { + // No pending bonds to wait for, show market page with + // the server selected. + pg.ParentNavigator().ClearStackAndDisplay(NewDEXMarketPage(pg.Load, xc.Host)) } + pg.ParentWindow().Reload() + } - pg.isLoading = false - pm.SetError(err.Error()) - pm.SetLoading(false) - return false - }) - dexPasswordModal.SetPasswordTitleVisibility(false) - pg.ParentWindow().ShowModal(dexPasswordModal) + pg.isLoading = false + }() } } } @@ -1268,6 +1328,10 @@ func (pg *DEXOnboarding) waitForConfirmationAndListenForBlockNotifications() { asset.RemoveTxAndBlockNotificationListener(DEXOnboardingPageID) asset.AddTxAndBlockNotificationListener(&sharedW.TxAndBlockNotificationListener{ OnBlockAttached: func(_ int, _ int32) { + if pg.AssetsManager.DEXCInitialized() && !pg.AssetsManager.DexClient().IsLoggedIn() { + // Don't update conf if we are not yet logged in. + return + } pg.bondConfirmationInfo.currentBondConf++ pg.ParentWindow().Reload() }, @@ -1282,29 +1346,45 @@ func (pg *DEXOnboarding) checkForPendingBondPayment(host string) { return } - pg.newTier = 1 - pg.currentStep = onBoardingStepWaitForConfirmation - pg.bondConfirmationInfo = &bondConfirmationInfo{ - bondCoinID: bond.CoinID, - requiredBondConf: uint16(bondAsset.Confs), - currentBondConf: int32(bond.Confs), - } + // waitForBondTx prepares the required information to display the + // onBoardingStepWaitForConfirmation page and sets up a listener for pending + // bond(s). + waitForBondTx := func(_ string) { + pg.newTier = 1 + pg.currentStep = onBoardingStepWaitForConfirmation + pg.bondConfirmationInfo = &bondConfirmationInfo{ + bondCoinID: bond.CoinID, + requiredBondConf: uint16(bondAsset.Confs), + currentBondConf: int32(bond.Confs), + } - // Set fields required by pg.stepWaitForBondConfirmation page. Also See: - // pg.bondAmountInfoDisplay. - bondAssetType := convertAssetIDToAssetType(bondAsset.ID) - pg.bondServer.bondAssets = map[libutils.AssetType]*core.BondAsset{ - bondAssetType: bondAsset, + // Set fields required by pg.stepWaitForBondConfirmation page. Also See: + // pg.bondAmountInfoDisplay. + bondAssetType := convertAssetIDToAssetType(bondAsset.ID) + pg.bondServer.bondAssets = map[libutils.AssetType]*core.BondAsset{ + bondAssetType: bondAsset, + } + pg.bondServer.url = xcHost + pg.bondSourceAccountSelector = components.NewWalletAndAccountSelector(pg.Load, bondAssetType) + ok := pg.bondSourceAccountSelector.SetSelectedAsset(bondAssetType) + if !ok { // impossible but can happen if user deletes wallet shortly after posting bonds. + pg.notifyError(values.String(values.StrNoWalletLoaded)) + return + } + + pg.waitForConfirmationAndListenForBlockNotifications() } - pg.bondServer.url = xcHost - pg.bondSourceAccountSelector = components.NewWalletAndAccountSelector(pg.Load, bondAssetType) - ok := pg.bondSourceAccountSelector.SetSelectedAsset(bondAssetType) - if !ok { // impossible but can happen if user deletes wallet shortly after posting bonds. - pg.notifyError(values.String(values.StrNoWalletLoaded)) + + dexClient := pg.AssetsManager.DexClient() + if dexClient.IsLoggedIn() { + waitForBondTx("") return } - pg.waitForConfirmationAndListenForBlockNotifications() + dexPasswordModal := dexLoginModal(pg.Load, dexClient, waitForBondTx) + dexPasswordModal.SetDescription(values.String(values.StrLoginDEXForPendingBonds)) + dexPasswordModal.SetNegativeButtonCallback(func() { waitForBondTx("") }) // We'll display a form for them to login. + pg.ParentWindow().ShowModal(dexPasswordModal) } func (pg *DEXOnboarding) notifyError(errMsg string) { diff --git a/ui/page/dcrdex/market.go b/ui/page/dcrdex/market.go index 19f307237..f2cc397fa 100644 --- a/ui/page/dcrdex/market.go +++ b/ui/page/dcrdex/market.go @@ -8,7 +8,9 @@ import ( "sort" "strconv" "strings" + "sync/atomic" + "decred.org/dcrdex/client/comms" "decred.org/dcrdex/client/core" "decred.org/dcrdex/client/orderbook" "decred.org/dcrdex/dex" @@ -21,6 +23,7 @@ import ( "github.com/crypto-power/cryptopower/app" "github.com/crypto-power/cryptopower/dexc" + "github.com/crypto-power/cryptopower/libwallet" "github.com/crypto-power/cryptopower/libwallet/assets/wallet" sharedW "github.com/crypto-power/cryptopower/libwallet/assets/wallet" "github.com/crypto-power/cryptopower/libwallet/ext" @@ -88,7 +91,8 @@ type DEXMarketPage struct { lastSelectedDEXServer string addServerBtn *cryptomaterial.Clickable - marketSelector *cryptomaterial.DropDown + marketSelector *cryptomaterial.DropDown + noMarketOrServerDisconnected atomic.Bool toggleBuyAndSellBtn *cryptomaterial.SegmentedControl orderTypesDropdown *cryptomaterial.DropDown @@ -101,6 +105,7 @@ type DEXMarketPage struct { maxBuyOrSellStr string orderFeeEstimateStr string + loginBtn cryptomaterial.Button postBondBtn cryptomaterial.Button createOrderBtn cryptomaterial.Button immediateOrderCheckbox cryptomaterial.CheckBoxStyle @@ -154,6 +159,7 @@ func NewDEXMarketPage(l *load.Load, selectServer string) *DEXMarketPage { totalEditor: newTextEditor(th, values.String(values.StrTotal), "", false), maxBuyOrSellStr: "---", orderFeeEstimateStr: "------", + loginBtn: th.Button(values.String(values.StrLogin)), postBondBtn: th.Button(values.String(values.StrPostBond)), addWalletToDEX: th.Button(values.String(values.StrAddWallet)), createOrderBtn: th.Button(values.String(values.StrBuy)), @@ -236,17 +242,24 @@ func (pg *DEXMarketPage) OnNavigatedTo() { case core.NoteTypeConnEvent: switch n.Topic() { case core.TopicDEXConnected: + pg.noMarketOrServerDisconnected.Store(true) pg.setServerMarkets() if pg.ParentNavigator().CurrentPage().ID() == DEXMarketPageID { modal := modal.NewSuccessModal(pg.Load, n.Details(), modal.DefaultClickFunc()) pg.ParentWindow().ShowModal(modal) - pg.ParentWindow().Reload() } case core.TopicDEXDisconnected, core.TopicDexConnectivity: + if n.Topic() == core.TopicDEXDisconnected { + pg.noMarketOrServerDisconnected.Store(false) + } + if pg.ParentNavigator().CurrentPage().ID() == DEXMarketPageID { pg.notifyError(n.Details()) } } + + pg.ParentWindow().Reload() + case core.NoteTypeOrder, core.NoteTypeMatch: if n.Topic() == core.TopicAsyncOrderFailure { pg.notifyError(n.Details()) @@ -274,24 +287,32 @@ func (pg *DEXMarketPage) OnNavigatedTo() { } // Prompt user to login now. - dexPasswordModal := modal.NewCreatePasswordModal(pg.Load). + pg.ParentWindow().ShowModal(dexLoginModal(pg.Load, dexc, nil)) +} + +func dexLoginModal(load *load.Load, dexClient libwallet.DEXClient, positiveBtnCallback func(password string)) *modal.CreatePasswordModal { + dexPasswordModal := modal.NewCreatePasswordModal(load). EnableName(false). EnableConfirmPassword(false). Title(values.String(values.StrLogin)). SetDescription(values.String(values.StrLoginWithDEXPassword)). PasswordHint(values.String(values.StrDexPassword)). SetPositiveButtonCallback(func(_, password string, pm *modal.CreatePasswordModal) bool { - err := dexc.Login([]byte(password)) - if err == nil { - return true + err := dexClient.Login([]byte(password)) + if err != nil { + pm.SetError(err.Error()) + pm.SetLoading(false) + return false } - pm.SetError(err.Error()) - pm.SetLoading(false) - return false + if positiveBtnCallback != nil { + positiveBtnCallback(password) + } + return true }).SetCancelable(false) + dexPasswordModal.SetPasswordTitleVisibility(false) - pg.ParentWindow().ShowModal(dexPasswordModal) + return dexPasswordModal } func (pg *DEXMarketPage) isDEXReset() bool { @@ -338,12 +359,14 @@ func (pg *DEXMarketPage) setServerMarkets() { dexc := pg.AssetsManager.DexClient() var markets []cryptomaterial.DropDownItem var lastSelectedItem *cryptomaterial.DropDownItem + var serverIsDisconnected bool if pg.serverSelector.Selected() != values.String(values.StrAddServer) { host := pg.serverSelector.Selected() xc, err := dexc.Exchange(host) if err != nil { pg.notifyError(err.Error()) } else { + serverIsDisconnected = xc.ConnectionStatus != comms.Connected for _, m := range xc.Markets { base, quote := convertAssetIDToAssetType(m.BaseID), convertAssetIDToAssetType(m.QuoteID) if base == "" || quote == "" { @@ -364,11 +387,18 @@ func (pg *DEXMarketPage) setServerMarkets() { } } - if len(markets) == 0 { - markets = append(markets, cryptomaterial.DropDownItem{ - Text: values.String(values.StrNoSupportedMarket), + noMarketOrServerDisconnected := len(markets) == 0 || serverIsDisconnected + pg.noMarketOrServerDisconnected.Store(noMarketOrServerDisconnected) + + if noMarketOrServerDisconnected { + msg := values.String(values.StrNoSupportedMarket) + if serverIsDisconnected { + msg = values.String(values.StrDEXServerDisconnected) + } + markets = []cryptomaterial.DropDownItem{{ + Text: msg, PreventSelection: true, - }) + }} } pg.marketSelector = pg.Theme.DropDown(markets, lastSelectedItem, values.DEXCurrencyPairGroup, false) @@ -388,7 +418,7 @@ func (pg *DEXMarketPage) fetchOrderBook() { } pg.closeAndResetOrderbookListener() - if pg.noSupportedMarket() { + if pg.noMarketOrServerDisconnected.Load() { return // nothing to do. } @@ -744,9 +774,17 @@ func (pg *DEXMarketPage) orderForm(gtx C) D { xc := pg.exchange() dexClient := pg.AssetsManager.DexClient() hasZeroEffectiveTier := dexClient.IsLoggedIn() && xc != nil && xc.Auth.EffectiveTier == 0 && xc.Auth.PendingStrength == 0 - if dexClient.IsLoggedIn() && pg.noSupportedMarket() { + if !dexClient.IsLoggedIn() { + overlaySet = true + overlayMsg = values.String(values.StrLoginWithDEXPassword) + actionBtn = &pg.loginBtn + } else if pg.noMarketOrServerDisconnected.Load() { overlaySet = true - overlayMsg = values.String(values.StrNoSupportedMarketMsg) + if xc != nil && xc.ConnectionStatus != comms.Connected { + overlayMsg = values.String(values.StrDEXServerDisconnected) + } else { + overlayMsg = values.String(values.StrNoSupportedMarketMsg) + } } else if hasZeroEffectiveTier { // Need to post bond to trade. overlaySet = true overlayMsg = values.String(values.StrPostBondMsg) @@ -763,7 +801,7 @@ func (pg *DEXMarketPage) orderForm(gtx C) D { asset := pg.AssetsManager.WalletWithID(walletID) accountName, _ := asset.AccountName(int32(accountNumber)) bondAmtString := calculateBondAmount(asset, xc.BondAssets[asset.GetAssetType().ToStringLower()], int(targetTier), dexClient.BondsFeeBuffer(bondAssetID)) - overlayMsg = values.StringF(values.StrBondPostingInProgressMsg, asset.GetAssetType(), asset.GetWalletName(), accountName, bondAmtString) + overlayMsg = values.StringF(values.StrBondPostingInProgressMsg, bondAmtString, accountName, asset.GetAssetType(), asset.GetWalletName()) } } else { actionBtn = &pg.postBondBtn @@ -796,130 +834,136 @@ func (pg *DEXMarketPage) orderForm(gtx C) D { }, Orientation: vertical, }.Layout2(gtx, func(gtx C) D { - return layout.Stack{Alignment: layout.NW}.Layout(gtx, - layout.Expanded(func(gtx C) D { - return cryptomaterial.LinearLayout{ - Width: cryptomaterial.MatchParent, - Height: cryptomaterial.WrapContent, - Margin: layout.Inset{Top: values.MarginPadding70}, - Orientation: vertical, - }.Layout(gtx, - layout.Rigid(func(gtx C) D { - return orderFormRow(gtx, vertical, []layout.FlexChild{ - layout.Rigid(pg.semiBoldLabelText(values.String(values.StrPrice)).Layout), - layout.Rigid(pg.priceEditor.Layout), - }) - }), - layout.Rigid(func(gtx C) D { - return orderFormRow(gtx, vertical, []layout.FlexChild{ - layout.Rigid(func(gtx C) D { - return layout.Inset{Bottom: dp5}.Layout(gtx, func(gtx C) D { - var labelText string - if pg.orderWithLots() { - labelText = fmt.Sprintf("%s (%s)", values.String(values.StrLots), lotsOrAmountSubtext) - } else { - labelText = fmt.Sprintf("%s (%s)", values.String(values.StrAmount), lotsOrAmountSubtext) + formLayout := func(gtx C) D { + return layout.Stack{Alignment: layout.NW}.Layout(gtx, + layout.Expanded(func(gtx C) D { + return cryptomaterial.LinearLayout{ + Width: cryptomaterial.MatchParent, + Height: cryptomaterial.WrapContent, + Margin: layout.Inset{Top: values.MarginPadding70}, + Orientation: vertical, + }.Layout(gtx, + layout.Rigid(func(gtx C) D { + return orderFormRow(gtx, vertical, []layout.FlexChild{ + layout.Rigid(pg.semiBoldLabelText(values.String(values.StrPrice)).Layout), + layout.Rigid(pg.priceEditor.Layout), + }) + }), + layout.Rigid(func(gtx C) D { + return orderFormRow(gtx, vertical, []layout.FlexChild{ + layout.Rigid(func(gtx C) D { + return layout.Inset{Bottom: dp5}.Layout(gtx, func(gtx C) D { + var labelText string + if pg.orderWithLots() { + labelText = fmt.Sprintf("%s (%s)", values.String(values.StrLots), lotsOrAmountSubtext) + } else { + labelText = fmt.Sprintf("%s (%s)", values.String(values.StrAmount), lotsOrAmountSubtext) + } + return layout.Flex{Axis: horizontal}.Layout(gtx, + layout.Rigid(pg.semiBoldLabelText(labelText).Layout), + layout.Flexed(1, func(gtx C) D { + return layout.E.Layout(gtx, pg.switchLotsOrAmount.Layout) + }), + ) + }) + }), + layout.Rigid(pg.lotsOrAmountEditor.Layout), + layout.Rigid(func(gtx C) D { + return layout.Flex{Axis: horizontal}.Layout(gtx, + layout.Rigid(func(gtx C) D { + if !sell { + return D{} + } + return layout.W.Layout(gtx, pg.Theme.Label(values.TextSize12, values.StringF(values.StrAvailableBalance, balStr)).Layout) + }), + layout.Flexed(1, func(gtx layout.Context) layout.Dimensions { + return layout.E.Layout(gtx, pg.Theme.Label(values.TextSize12, values.StringF(values.StrMaxDEX, tradeDirection, pg.maxBuyOrSellStr)).Layout) + }), + ) + }), + }) + }), + layout.Rigid(func(gtx C) D { + return orderFormRow(gtx, vertical, []layout.FlexChild{ + layout.Rigid(func(gtx C) D { + totalLabelTxt := fmt.Sprintf("%s (%s)", values.String(values.StrTotal), totalSubText) + return layout.Inset{Bottom: dp5}.Layout(gtx, pg.semiBoldLabelText(totalLabelTxt).Layout) + }), + layout.Rigid(pg.totalEditor.Layout), + layout.Rigid(func(gtx C) D { + if sell { + return D{} // Base asset available balance is shown on the sell form view } + + // Show quote asset balance return layout.Flex{Axis: horizontal}.Layout(gtx, - layout.Rigid(pg.semiBoldLabelText(labelText).Layout), - layout.Flexed(1, func(gtx C) D { - return layout.E.Layout(gtx, pg.switchLotsOrAmount.Layout) + layout.Flexed(1, func(gtx layout.Context) layout.Dimensions { + return layout.E.Layout(gtx, pg.Theme.Label(values.TextSize12, values.StringF(values.StrAvailableBalance, balStr)).Layout) }), ) - }) - }), - layout.Rigid(pg.lotsOrAmountEditor.Layout), - layout.Rigid(func(gtx C) D { - return layout.Flex{Axis: horizontal}.Layout(gtx, - layout.Rigid(func(gtx C) D { - if !sell { - return D{} - } - return layout.W.Layout(gtx, pg.Theme.Label(values.TextSize12, values.StringF(values.StrAvailableBalance, balStr)).Layout) - }), - layout.Flexed(1, func(gtx layout.Context) layout.Dimensions { - return layout.E.Layout(gtx, pg.Theme.Label(values.TextSize12, values.StringF(values.StrMaxDEX, tradeDirection, pg.maxBuyOrSellStr)).Layout) - }), - ) - }), - }) - }), - layout.Rigid(func(gtx C) D { - return orderFormRow(gtx, vertical, []layout.FlexChild{ - layout.Rigid(func(gtx C) D { - totalLabelTxt := fmt.Sprintf("%s (%s)", values.String(values.StrTotal), totalSubText) - return layout.Inset{Bottom: dp5}.Layout(gtx, pg.semiBoldLabelText(totalLabelTxt).Layout) - }), - layout.Rigid(pg.totalEditor.Layout), - layout.Rigid(func(gtx C) D { - if sell { - return D{} // Base asset available balance is shown on the sell form view - } + }), + }) + }), + layout.Rigid(func(gtx C) D { + return layout.Flex{Axis: horizontal, Alignment: layout.Middle}.Layout(gtx, + layout.Rigid(semiBoldLabelGrey3(pg.Theme, values.String(values.StrEstimatedFee)).Layout), + layout.Rigid(func(gtx C) D { + feeEstimatedLabel := pg.Theme.Label(values.TextSize12, pg.orderFeeEstimateStr) + feeEstimatedLabel.Alignment = text.Middle + return feeEstimatedLabel.Layout(gtx) + }), + ) + }), + layout.Rigid(func(gtx C) D { + pg.immediateOrderCheckbox.Color = pg.Theme.Color.Text + return orderFormRow(gtx, horizontal, []layout.FlexChild{ + layout.Rigid(pg.immediateOrderCheckbox.Layout), + layout.Rigid(func(gtx C) D { + return layout.Inset{Top: dp10, Left: dp2}.Layout(gtx, func(gtx C) D { + return pg.immediateOrderInfoBtn.Layout(gtx, pg.Theme.Icons.InfoAction.Layout16dp) + }) + })}, + ) + }), + layout.Rigid(func(gtx C) D { + pg.createOrderBtn.SetEnabled(pg.hasValidOrderInfo()) + return layout.Flex{Axis: horizontal, Alignment: layout.Middle}.Layout(gtx, + layout.Flexed(1, pg.createOrderBtn.Layout), + ) + }), + ) + }), + layout.Stacked(func(gtx C) D { + return layout.Flex{Axis: horizontal}.Layout(gtx, + layout.Rigid(func(gtx C) D { + return pg.toggleBuyAndSellBtn.GroupTileLayout(gtx) + }), + layout.Flexed(1, func(gtx C) D { + pg.orderTypesDropdown.Background = &pg.Theme.Color.Surface + return layout.Inset{Bottom: dp5, Top: dp5}.Layout(gtx, pg.orderTypesDropdown.Layout) + }), + ) + }), + ) + } - // Show quote asset balance - return layout.Flex{Axis: horizontal}.Layout(gtx, - layout.Flexed(1, func(gtx layout.Context) layout.Dimensions { - return layout.E.Layout(gtx, pg.Theme.Label(values.TextSize12, values.StringF(values.StrAvailableBalance, balStr)).Layout) - }), - ) - }), - }) - }), - layout.Rigid(func(gtx C) D { - return layout.Flex{Axis: horizontal, Alignment: layout.Middle}.Layout(gtx, - layout.Rigid(semiBoldLabelGrey3(pg.Theme, values.String(values.StrEstimatedFee)).Layout), - layout.Rigid(func(gtx C) D { - feeEstimatedLabel := pg.Theme.Label(values.TextSize12, pg.orderFeeEstimateStr) - feeEstimatedLabel.Alignment = text.Middle - return feeEstimatedLabel.Layout(gtx) - }), - ) - }), - layout.Rigid(func(gtx C) D { - pg.immediateOrderCheckbox.Color = pg.Theme.Color.Text - return orderFormRow(gtx, horizontal, []layout.FlexChild{ - layout.Rigid(pg.immediateOrderCheckbox.Layout), - layout.Rigid(func(gtx C) D { - return layout.Inset{Top: dp10, Left: dp2}.Layout(gtx, func(gtx C) D { - return pg.immediateOrderInfoBtn.Layout(gtx, pg.Theme.Icons.InfoAction.Layout16dp) - }) - })}, - ) - }), - layout.Rigid(func(gtx C) D { - pg.createOrderBtn.SetEnabled(pg.hasValidOrderInfo()) - return layout.Flex{Axis: horizontal, Alignment: layout.Middle}.Layout(gtx, - layout.Flexed(1, pg.createOrderBtn.Layout), - ) - }), - ) - }), - layout.Stacked(func(gtx C) D { - return layout.Flex{Axis: horizontal}.Layout(gtx, - layout.Rigid(func(gtx C) D { - return pg.toggleBuyAndSellBtn.GroupTileLayout(gtx) - }), - layout.Flexed(1, func(gtx C) D { - pg.orderTypesDropdown.Background = &pg.Theme.Color.Surface - return layout.Inset{Bottom: dp5, Top: dp5}.Layout(gtx, pg.orderTypesDropdown.Layout) - }), - ) - }), - layout.Stacked(func(gtx C) D { - if !overlaySet { - return D{} - } + if !overlaySet { + return formLayout(gtx) + } - gtxCopy := gtx - label := pg.Theme.Body1(overlayMsg) - label.Alignment = text.Middle - return cryptomaterial.DisableLayout(nil, gtxCopy, - func(gtx C) D { - return layout.Inset{Bottom: values.MarginPadding20}.Layout(gtx, label.Layout) - }, - nil, 180, pg.Theme.Color.Gray3, actionBtn) - }), - ) + gtxCopy := gtx + overlay := func(gtx C) D { + label := pg.Theme.Body1(overlayMsg) + label.Alignment = text.Middle + return cryptomaterial.DisableLayout(nil, gtxCopy, + func(gtx C) D { + return layout.Inset{Bottom: values.MarginPadding20}.Layout(gtx, label.Layout) + }, + nil, 180, pg.Theme.Color.Gray3, actionBtn) + } + + gtx = gtx.Disabled() + return layout.Stack{}.Layout(gtx, layout.Expanded(formLayout), layout.Stacked(overlay)) }) } @@ -991,7 +1035,7 @@ func trimZeros(s string) string { // account for the quote or base asset of the selected market. Returns the // wallet's spendable balance as string. func (pg *DEXMarketPage) availableWalletAccountBalanceString(forQuoteAsset bool) (bal float64, assetSym string) { - if pg.noSupportedMarket() { + if pg.noMarketOrServerDisconnected.Load() { return 0, "" } @@ -1560,18 +1604,24 @@ func (pg *DEXMarketPage) HandleUserInteractions() { pg.ParentNavigator().ClearStackAndDisplay(NewDEXOnboarding(pg.Load, pg.serverSelector.Selected())) } + if pg.loginBtn.Clicked() { + pg.ParentWindow().ShowModal(dexLoginModal(pg.Load, dexc, nil)) + } + for pg.addWalletToDEX.Clicked() { pg.handleMissingMarketWallet() } for _, ord := range pg.orders { if ord.cancelBtn != nil && ord.cancelBtn.Clicked() { - err := dexc.Cancel(ord.ID) - if err != nil { - pg.notifyError(fmt.Errorf("Error canceling order: %v", err).Error()) - } else { - pg.ParentWindow().Reload() - } + go func(ordID dex.Bytes) { + err := dexc.Cancel(ordID) + if err != nil { + pg.notifyError(fmt.Errorf("Error canceling order: %v", err).Error()) + } else { + pg.ParentWindow().Reload() + } + }(ord.ID) } } @@ -1928,10 +1978,6 @@ func (pg *DEXMarketPage) orderWithLots() bool { return !pg.switchLotsOrAmount.IsChecked() } -func (pg *DEXMarketPage) noSupportedMarket() bool { - return pg.marketSelector.Selected() == values.String(values.StrNoSupportedMarket) -} - func (pg *DEXMarketPage) notifyError(errMsg string) { errModal := modal.NewErrorModal(pg.Load, errMsg, modal.DefaultClickFunc()) pg.ParentWindow().ShowModal(errModal) diff --git a/ui/page/exchange/create_order_page.go b/ui/page/exchange/create_order_page.go index 5f9e182d1..95ecc041d 100644 --- a/ui/page/exchange/create_order_page.go +++ b/ui/page/exchange/create_order_page.go @@ -356,7 +356,8 @@ func (pg *CreateOrderPage) HandleUserInteractions() { } if pg.viewAllButton.Clicked() { - pg.ParentWindow().Display(NewOrderHistoryPage(pg.Load)) + tab.SetSelectedSegment(tabTitles[2]) + pg.ParentNavigator().Display(NewOrderHistoryPage(pg.Load)) } if pg.infoButton.Button.Clicked() { diff --git a/ui/page/exchange/trade_page.go b/ui/page/exchange/trade_page.go index acf275d90..86f74ece7 100644 --- a/ui/page/exchange/trade_page.go +++ b/ui/page/exchange/trade_page.go @@ -17,6 +17,8 @@ const ( TradePageID = "Trade" ) +var tab *cryptomaterial.SegmentedControl + var tabTitles = []string{ values.String(values.StrDcrDex), values.String(values.StrCentralizedExchange), @@ -32,12 +34,9 @@ type TradePage struct { dexc *dexc.DEXClient scrollContainer *widget.List - - tab *cryptomaterial.SegmentedControl - - shadowBox *cryptomaterial.Shadow - exchangeBtn *cryptomaterial.Clickable - dcrdexBtn *cryptomaterial.Clickable + shadowBox *cryptomaterial.Shadow + exchangeBtn *cryptomaterial.Clickable + dcrdexBtn *cryptomaterial.Clickable } func NewTradePage(l *load.Load) *TradePage { @@ -58,8 +57,7 @@ func NewTradePage(l *load.Load) *TradePage { filteredTabTitles = filteredTabTitles[1:] } - pg.tab = l.Theme.SegmentedControl(filteredTabTitles, cryptomaterial.SegmentTypeGroup) - + tab = l.Theme.SegmentedControl(filteredTabTitles, cryptomaterial.SegmentTypeGroup) rad := cryptomaterial.Radius(14) pg.exchangeBtn = l.Theme.NewClickable(false) pg.exchangeBtn.Radius = rad @@ -98,12 +96,12 @@ func (pg *TradePage) OnNavigatedTo() { // displayed. // Part of the load.Page interface. func (pg *TradePage) HandleUserInteractions() { - selectedIndex := pg.tab.SelectedIndex() + selectedIndex := tab.SelectedIndex() if pg.IsMobileView() { selectedIndex++ // Adjust index for mobile view } - if pg.CurrentPage() == nil || pg.tab.Changed() { + if pg.CurrentPage() == nil || tab.Changed() { switch selectedIndex { case 0: // DCRDEX if pg.CurrentPageID() != dcrdex.DCRDEXPageID { @@ -140,5 +138,5 @@ func (pg *TradePage) OnNavigatedFrom() { // to be eventually drawn on screen. // Part of the load.Page interface. func (pg *TradePage) Layout(gtx C) D { - return pg.tab.Layout(gtx, pg.CurrentPage().Layout, pg.IsMobileView()) + return tab.Layout(gtx, pg.CurrentPage().Layout, pg.IsMobileView()) } diff --git a/ui/page/root/home_page.go b/ui/page/root/home_page.go index 4a97f95ab..e3e9ea0b7 100644 --- a/ui/page/root/home_page.go +++ b/ui/page/root/home_page.go @@ -2,16 +2,20 @@ package root import ( "context" + "fmt" "strings" "sync/atomic" "time" dexdb "decred.org/dcrdex/client/db" + "gioui.org/font" + "gioui.org/io/clipboard" "gioui.org/io/key" "gioui.org/layout" "gioui.org/widget" "github.com/crypto-power/cryptopower/app" + "github.com/crypto-power/cryptopower/appos" sharedW "github.com/crypto-power/cryptopower/libwallet/assets/wallet" "github.com/crypto-power/cryptopower/libwallet/ext" libutils "github.com/crypto-power/cryptopower/libwallet/utils" @@ -65,14 +69,19 @@ type HomePage struct { isConnected *atomic.Bool showNavigationFunc showNavigationFunc startSpvSync uint32 + + updateAvailableBtn *cryptomaterial.Clickable + copyRedirectURL *cryptomaterial.Clickable + releaseResponse *components.ReleaseResponse } func NewHomePage(dexCtx context.Context, l *load.Load) *HomePage { hp := &HomePage{ - Load: l, - MasterPage: app.NewMasterPage(HomePageID), - isConnected: new(atomic.Bool), - dexCtx: dexCtx, + Load: l, + MasterPage: app.NewMasterPage(HomePageID), + isConnected: new(atomic.Bool), + dexCtx: dexCtx, + copyRedirectURL: l.Theme.NewClickable(false), } hp.hideBalanceButton = hp.Theme.NewClickable(false) @@ -80,6 +89,7 @@ func NewHomePage(dexCtx context.Context, l *load.Load) *HomePage { hp.appNotificationButton = hp.Theme.NewClickable(false) _, hp.infoButton = components.SubpageHeaderButtons(l) hp.infoButton.Size = values.MarginPadding15 + hp.updateAvailableBtn = l.Theme.NewClickable(false) go func() { hp.isConnected.Store(libutils.IsOnline()) @@ -177,6 +187,10 @@ func (hp *HomePage) OnNavigatedTo() { go hp.CalculateAssetsUSDBalance() hp.isBalanceHidden = hp.AssetsManager.IsTotalBalanceVisible() + + if hp.isUpdateAPIAllowed() { + go hp.checkForUpdates() + } } // initDEX initializes a new dex client if dex is not ready. @@ -216,98 +230,105 @@ func (hp *HomePage) initDEX() { return // nothing to do } - dexPassEditor := hp.Theme.EditorPassword(new(widget.Editor), values.String(values.StrDexPassword)) - dexPassEditor.Editor.SingleLine, dexPassEditor.IsRequired = true, true + showDEXLoginModal := func() { + dexPassEditor := hp.Theme.EditorPassword(new(widget.Editor), values.String(values.StrDexPassword)) + dexPassEditor.Editor.SingleLine, dexPassEditor.IsRequired = true, true - loginModal := modal.NewCustomModal(hp.Load). - UseCustomWidget(func(gtx C) D { - return layout.Flex{Axis: layout.Vertical}.Layout(gtx, - layout.Rigid(func(gtx C) D { - return layout.Inset{Bottom: values.MarginPadding10}.Layout(gtx, hp.Theme.Body1(values.String(values.StrLoginDEXForActiveOrdersOrExpiredBonds)).Layout) - }), - layout.Rigid(func(gtx C) D { - return dexPassEditor.Layout(gtx) - }), - ) - }). - SetPositiveButtonText(values.String(values.StrLogin)). - SetPositiveButtonCallback(func(isChecked bool, im *modal.InfoModal) bool { - dexPassEditor.SetError("") - err := dexClient.Login([]byte(dexPassEditor.Editor.Text())) - if err != nil { - dexPassEditor.SetError(err.Error()) - return false - } - - // DEX client has active orders or expired bonds, retrieve the - // wallets involved and ensure they are synced or syncing. - walletsToSyncMap := make(map[uint32]*struct{}) - for _, orders := range activeOrders { - for _, ord := range orders { - walletsToSyncMap[ord.BaseID] = &struct{}{} - walletsToSyncMap[ord.QuoteID] = &struct{}{} + loginModal := modal.NewCustomModal(hp.Load). + UseCustomWidget(func(gtx C) D { + return layout.Flex{Axis: layout.Vertical}.Layout(gtx, + layout.Rigid(func(gtx C) D { + return layout.Inset{Bottom: values.MarginPadding10}.Layout(gtx, hp.Theme.Body1(values.String(values.StrLoginDEXForActiveOrdersOrExpiredBonds)).Layout) + }), + layout.Rigid(func(gtx C) D { + return dexPassEditor.Layout(gtx) + }), + ) + }). + SetNegativeButtonText(values.String(values.StrIWillLoginLater)). + SetPositiveButtonText(values.String(values.StrLogin)). + SetPositiveButtonCallback(func(isChecked bool, im *modal.InfoModal) bool { + dexPassEditor.SetError("") + err := dexClient.Login([]byte(dexPassEditor.Editor.Text())) + if err != nil { + dexPassEditor.SetError(err.Error()) + return false } - } + return true + }). + SetCancelable(false) + hp.ParentWindow().ShowModal(loginModal) + } - for _, bond := range expiredBonds { - walletsToSyncMap[bond.AssetID] = &struct{}{} - } + // DEX client has active orders or expired bonds, retrieve the + // wallets involved and ensure they are synced or syncing. + walletsToSyncMap := make(map[uint32]*struct{}) + for _, orders := range activeOrders { + for _, ord := range orders { + walletsToSyncMap[ord.BaseID] = &struct{}{} + walletsToSyncMap[ord.QuoteID] = &struct{}{} + } + } - var walletsToSync []sharedW.Asset - for assetID := range walletsToSyncMap { - walletID, err := dexClient.WalletIDForAsset(assetID) - if err != nil { - log.Errorf("dexClient.WalletIDForAsset(%d) error: %w", assetID, err) - continue - } + for _, bond := range expiredBonds { + walletsToSyncMap[bond.AssetID] = &struct{}{} + } - if walletID == nil { - continue // impossible but better safe than sorry - } + var namesOfWalletsToSync []string + var walletsToSync []sharedW.Asset + for assetID := range walletsToSyncMap { + walletID, err := dexClient.WalletIDForAsset(assetID) + if err != nil { + log.Errorf("dexClient.WalletIDForAsset(%d) error: %w", assetID, err) + continue + } - wallet := hp.AssetsManager.WalletWithID(*walletID) - if wallet == nil { // impossible but better safe than sorry - log.Error("dex wallet with ID %d is missing", walletID) - continue - } + if walletID == nil { + continue // impossible but better safe than sorry + } - if wallet.IsSynced() || wallet.IsSyncing() { - continue // ok - } + wallet := hp.AssetsManager.WalletWithID(*walletID) + if wallet == nil { // impossible but better safe than sorry + log.Error("dex wallet with ID %d is missing", walletID) + continue + } - walletsToSync = append(walletsToSync, wallet) - } + if wallet.IsSynced() || wallet.IsSyncing() { + continue // ok + } - if len(walletsToSync) == 0 { - return true - } + walletsToSync = append(walletsToSync, wallet) + namesOfWalletsToSync = append(namesOfWalletsToSync, fmt.Sprintf("%s (%s)", wallet.GetWalletName(), wallet.GetAssetType())) + } - walletSyncRequestModal := modal.NewCustomModal(hp.Load). - Title(values.String(values.StrWalletsNeedToSync)). - Body(values.String(values.StrWalletsNeedToSyncMsg)). - SetNegativeButtonText(values.String(values.StrIWillSyncLater)). - SetPositiveButtonText(values.String(values.StrOkaySync)). - SetPositiveButtonCallback(func(isChecked bool, im *modal.InfoModal) bool { - if !hp.isConnected.Load() { - // Notify user and return. - hp.Toast.NotifyError(values.String(values.StrNotConnected)) - return false - } + if len(walletsToSync) == 0 { + showDEXLoginModal() // Request dex login now. + return + } - for _, w := range walletsToSync { - err := w.SpvSync() - if err != nil { - log.Error(err) - } + walletSyncRequestModal := modal.NewCustomModal(hp.Load). + Title(values.String(values.StrWalletsNeedToSync)). + Body(values.StringF(values.StrWalletsNeedToSyncMsg, strings.Join(namesOfWalletsToSync, ", "))). + SetNegativeButtonText(values.String(values.StrIWillSyncLater)). + SetNegativeButtonCallback(showDEXLoginModal). + SetPositiveButtonText(values.String(values.StrOkaySync)). + SetPositiveButtonCallback(func(isChecked bool, im *modal.InfoModal) bool { + if !hp.isConnected.Load() { + hp.Toast.NotifyError(values.String(values.StrNotConnected)) + } else { + for _, w := range walletsToSync { + err := w.SpvSync() + if err != nil { + log.Error(err) } + } + } - return true - }) - hp.ParentWindow().ShowModal(walletSyncRequestModal) + showDEXLoginModal() return true - }). - SetCancelable(false) - hp.ParentWindow().ShowModal(loginModal) + }).SetCancelable(false) + hp.ParentWindow().ShowModal(walletSyncRequestModal) + }() } @@ -414,6 +435,52 @@ func (hp *HomePage) HandleUserInteractions() { } } } + + if hp.updateAvailableBtn.Clicked() { + info := modal.NewCustomModal(hp.Load). + Title(fmt.Sprintf(values.String(values.StrNewUpdateText), hp.releaseResponse.TagName)). + Body(values.String(values.StrCopyLink)). + SetCancelable(true). + UseCustomWidget(func(gtx C) D { + return layout.Stack{}.Layout(gtx, + layout.Stacked(func(gtx C) D { + border := widget.Border{Color: hp.Theme.Color.Gray4, CornerRadius: values.MarginPadding10, Width: values.MarginPadding2} + wrapper := hp.Theme.Card() + wrapper.Color = hp.Theme.Color.Gray4 + return border.Layout(gtx, func(gtx C) D { + return wrapper.Layout(gtx, func(gtx C) D { + return layout.UniformInset(values.MarginPadding10).Layout(gtx, func(gtx C) D { + return layout.Flex{}.Layout(gtx, + layout.Flexed(0.9, hp.Theme.Body1(hp.releaseResponse.URL).Layout), + layout.Flexed(0.1, func(gtx C) D { + return layout.E.Layout(gtx, func(gtx C) D { + if hp.copyRedirectURL.Clicked() { + clipboard.WriteOp{Text: hp.releaseResponse.URL}.Add(gtx.Ops) + hp.Toast.Notify(values.String(values.StrCopied)) + } + return hp.copyRedirectURL.Layout(gtx, hp.Theme.Icons.CopyIcon.Layout24dp) + }) + }), + ) + }) + }) + }) + }), + layout.Stacked(func(gtx C) D { + return layout.Inset{ + Top: values.MarginPaddingMinus10, + Left: values.MarginPadding10, + }.Layout(gtx, func(gtx C) D { + label := hp.Theme.Body2(values.String(values.StrWebURL)) + label.Color = hp.Theme.Color.GrayText2 + return label.Layout(gtx) + }) + }), + ) + }). + SetPositiveButtonText(values.String(values.StrGotIt)) + hp.ParentWindow().ShowModal(info) + } } func (hp *HomePage) displaySelectedPage(title string) { @@ -530,6 +597,13 @@ func (hp *HomePage) layoutDesktop(gtx C) D { ) }), layout.Flexed(1, hp.CurrentPage().Layout), + layout.Rigid(func(gtx C) D { + if hp.isUpdateAPIAllowed() && hp.releaseResponse != nil { + return hp.layoutUpdateAvailable(gtx) + } + + return D{} + }), ) }), ) @@ -597,46 +671,59 @@ func (hp *HomePage) layoutTopBar(gtx C) D { } func (hp *HomePage) initBottomNavItems() { - hp.bottomNavigationBar = components.BottomNavigationBar{ - Load: hp.Load, - CurrentPage: hp.CurrentPageID(), - BottomNavigationItems: []components.BottomNavigationBarHandler{ - { - Clickable: hp.Theme.NewClickable(true), - Image: hp.Theme.Icons.OverviewIcon, - ImageInactive: hp.Theme.Icons.OverviewIconInactive, - Title: values.String(values.StrOverview), - PageID: OverviewPageID, - }, - { - Clickable: hp.Theme.NewClickable(true), - Image: hp.Theme.Icons.TransactionsIcon, - ImageInactive: hp.Theme.Icons.TransactionsIconInactive, - Title: values.String(values.StrTransactions), - PageID: transaction.TransactionsPageID, - }, - { - Clickable: hp.Theme.NewClickable(true), - Image: hp.Theme.Icons.WalletIcon, - ImageInactive: hp.Theme.Icons.WalletIconInactive, - Title: values.String(values.StrWallets), - PageID: WalletSelectorPageID, - }, - { - Clickable: hp.Theme.NewClickable(true), - Image: hp.Theme.Icons.TradeIconActive, - ImageInactive: hp.Theme.Icons.TradeIconInactive, - Title: values.String(values.StrTrade), - PageID: exchange.TradePageID, - }, - { - Clickable: hp.Theme.NewClickable(true), - Image: hp.Theme.Icons.GovernanceActiveIcon, - ImageInactive: hp.Theme.Icons.GovernanceInactiveIcon, - Title: values.String(values.StrGovernance), - PageID: governance.GovernancePageID, - }, + items := []components.BottomNavigationBarHandler{ + { + Clickable: hp.Theme.NewClickable(true), + Image: hp.Theme.Icons.OverviewIcon, + ImageInactive: hp.Theme.Icons.OverviewIconInactive, + Title: values.String(values.StrOverview), + PageID: OverviewPageID, + }, + { + Clickable: hp.Theme.NewClickable(true), + Image: hp.Theme.Icons.TransactionsIcon, + ImageInactive: hp.Theme.Icons.TransactionsIconInactive, + Title: values.String(values.StrTransactions), + PageID: transaction.TransactionsPageID, + }, + { + Clickable: hp.Theme.NewClickable(true), + Image: hp.Theme.Icons.WalletIcon, + ImageInactive: hp.Theme.Icons.WalletIconInactive, + Title: values.String(values.StrWallets), + PageID: WalletSelectorPageID, }, + { + Clickable: hp.Theme.NewClickable(true), + Image: hp.Theme.Icons.GovernanceActiveIcon, + ImageInactive: hp.Theme.Icons.GovernanceInactiveIcon, + Title: values.String(values.StrGovernance), + PageID: governance.GovernancePageID, + }, + } + + // Add the trade tab only if not on mobile + if !appos.Current().IsIOS() { + tradeTab := components.BottomNavigationBarHandler{ + Clickable: hp.Theme.NewClickable(true), + Image: hp.Theme.Icons.TradeIconActive, + ImageInactive: hp.Theme.Icons.TradeIconInactive, + Title: values.String(values.StrTrade), + PageID: exchange.TradePageID, + } + // Determine the insertion point, which is second to last position + insertionPoint := len(items) - 1 + if insertionPoint < 0 { + insertionPoint = 0 + } + // Append at the second to last position + items = append(items[:insertionPoint], append([]components.BottomNavigationBarHandler{tradeTab}, items[insertionPoint:]...)...) + } + + hp.bottomNavigationBar = components.BottomNavigationBar{ + Load: hp.Load, + CurrentPage: hp.CurrentPageID(), + BottomNavigationItems: items, } hp.floatingActionButton = components.BottomNavigationBar{ @@ -978,3 +1065,44 @@ func (hp *HomePage) CalculateAssetsUSDBalance() { hp.ParentWindow().Reload() } } + +func (hp *HomePage) layoutUpdateAvailable(gtx C) D { + return cryptomaterial.LinearLayout{ + Orientation: layout.Horizontal, + Width: cryptomaterial.MatchParent, + Height: cryptomaterial.WrapContent, + Background: hp.Theme.Color.DefaultThemeColors().SurfaceHighlight, + Clickable: hp.updateAvailableBtn, + Margin: layout.Inset{ + Top: values.MarginPaddingMinus10, + Bottom: values.MarginPadding10, + Right: values.MarginPadding40, + }, + Border: cryptomaterial.Border{Radius: hp.updateAvailableBtn.Radius}, + Direction: layout.E, + }.Layout(gtx, + layout.Rigid(func(gtx C) D { + txt := hp.Theme.Label(values.TextSize14, values.String(values.StrUpdateAvailable)) + txt.Color = hp.Theme.Color.DefaultThemeColors().Primary + txt.Font.Weight = font.SemiBold + return layout.Inset{ + Left: values.MarginPadding4, + }.Layout(gtx, txt.Layout) + }), + layout.Rigid(func(gtx C) D { + txt := hp.Theme.Label(values.TextSize14, hp.releaseResponse.TagName) + txt.Font.Weight = font.SemiBold + return layout.Inset{ + Left: values.MarginPadding4, + }.Layout(gtx, txt.Layout) + }), + ) +} + +func (hp *HomePage) checkForUpdates() { + hp.releaseResponse = components.CheckForUpdate(hp.Load) +} + +func (hp *HomePage) isUpdateAPIAllowed() bool { + return hp.AssetsManager.IsHTTPAPIPrivacyModeOff(libutils.UpdateAPI) +} diff --git a/ui/page/settings/app_settings_page.go b/ui/page/settings/app_settings_page.go index 69d84e1d9..63ffdf53b 100644 --- a/ui/page/settings/app_settings_page.go +++ b/ui/page/settings/app_settings_page.go @@ -72,6 +72,7 @@ type AppSettingsPage struct { exchangeAPI *cryptomaterial.Switch feeRateAPI *cryptomaterial.Switch vspAPI *cryptomaterial.Switch + updateAPI *cryptomaterial.Switch privacyActive *cryptomaterial.Switch isDarkModeOn bool @@ -92,6 +93,7 @@ func NewAppSettingsPage(l *load.Load) *AppSettingsPage { exchangeAPI: l.Theme.Switch(), feeRateAPI: l.Theme.Switch(), vspAPI: l.Theme.Switch(), + updateAPI: l.Theme.Switch(), privacyActive: l.Theme.Switch(), changeStartupPass: l.Theme.NewClickable(false), @@ -336,6 +338,9 @@ func (pg *AppSettingsPage) networkSettings() layout.Widget { layout.Rigid(func(gtx C) D { return pg.subSectionSwitch(gtx, values.String(values.StrVSPAPI), pg.vspAPI) }), + layout.Rigid(func(gtx C) D { + return pg.subSectionSwitch(gtx, values.String(values.StrUpdateAPI), pg.updateAPI) + }), ) }) } @@ -544,6 +549,9 @@ func (pg *AppSettingsPage) HandleUserInteractions() { if pg.vspAPI.Changed() { pg.AssetsManager.SetHTTPAPIPrivacyMode(libutils.VspAPI, pg.vspAPI.IsChecked()) } + if pg.updateAPI.Changed() { + pg.AssetsManager.SetHTTPAPIPrivacyMode(libutils.UpdateAPI, pg.updateAPI.IsChecked()) + } if pg.privacyActive.Changed() { pg.AssetsManager.SetPrivacyMode(pg.privacyActive.IsChecked()) @@ -835,6 +843,7 @@ func (pg *AppSettingsPage) updatePrivacySettings() { pg.setInitialSwitchStatus(pg.exchangeAPI, pg.AssetsManager.IsHTTPAPIPrivacyModeOff(libutils.ExchangeHTTPAPI)) pg.setInitialSwitchStatus(pg.feeRateAPI, pg.AssetsManager.IsHTTPAPIPrivacyModeOff(libutils.FeeRateHTTPAPI)) pg.setInitialSwitchStatus(pg.vspAPI, pg.AssetsManager.IsHTTPAPIPrivacyModeOff(libutils.VspAPI)) + pg.setInitialSwitchStatus(pg.updateAPI, pg.AssetsManager.IsHTTPAPIPrivacyModeOff(libutils.UpdateAPI)) } } diff --git a/ui/page/start_page.go b/ui/page/start_page.go index 5b444386e..d159dc248 100644 --- a/ui/page/start_page.go +++ b/ui/page/start_page.go @@ -250,16 +250,15 @@ func (sp *startPage) HandleUserInteractions() { if sp.addWalletButton.Clicked() { createWalletPage := components.NewCreateWallet(sp.Load, func() { sp.setLanguagePref(false) - sp.updateSettings() sp.ParentNavigator().Display(root.NewHomePage(sp.ctx, sp.Load)) }) sp.ParentNavigator().Display(createWalletPage) } for sp.nextButton.Clicked() { - // TODO: Handle Selected settings option (language and advanced or - // recommended settings). Might requires refactor of settings page. if sp.currentPageIndex == len(sp.onBoardingScreens)-1 { // index starts at 0 + // save user setting when reached the last sceen + sp.updateSettings() sp.currentPageIndex = -1 // we have reached the last screen. } else { sp.currentPageIndex++ @@ -267,8 +266,11 @@ func (sp *startPage) HandleUserInteractions() { } for i, item := range sp.settingsOptions { - for item.clickable.Clicked() { // TODO: Show settings page and allow user pick settings for advanced setup. + for item.clickable.Clicked() { sp.selectedSettingsOptionIndex = i + if item.title == values.String(values.StrAdvanced) { + sp.ParentWindow().Display(settings.NewAppSettingsPage(sp.Load)) + } } for item.infoButton.Button.Clicked() { @@ -701,7 +703,7 @@ func (sp *startPage) selectedLanguageKey() string { func (sp *startPage) updateSettings() { wantAdvanced := sp.selectedSettingsOptionIndex == advancedSettingsOptionIndex if wantAdvanced { - return // nothing to do? + return } sp.AssetsManager.SetTransactionsNotifications(true) @@ -710,4 +712,5 @@ func (sp *startPage) updateSettings() { sp.AssetsManager.SetHTTPAPIPrivacyMode(libutils.ExchangeHTTPAPI, true) sp.AssetsManager.SetHTTPAPIPrivacyMode(libutils.FeeRateHTTPAPI, true) sp.AssetsManager.SetHTTPAPIPrivacyMode(libutils.VspAPI, true) + sp.AssetsManager.SetHTTPAPIPrivacyMode(libutils.UpdateAPI, true) } diff --git a/ui/page/wallet/wallet_settings_page.go b/ui/page/wallet/wallet_settings_page.go index 6da99a131..86a04f562 100644 --- a/ui/page/wallet/wallet_settings_page.go +++ b/ui/page/wallet/wallet_settings_page.go @@ -588,14 +588,21 @@ func validatePeerAddressStr(addrs string) (string, bool) { continue } - if net.ParseIP(addr) != nil { + host, _, err := net.SplitHostPort(addr) + // If err assume because port was not supplied. + if err == nil { + host = addr + } + + if net.ParseIP(host) != nil { addrMap[addr] = &struct{}{} continue // ok } - if _, err := url.ParseRequestURI(addr); err != nil { + if _, err := url.ParseRequestURI(host); err != nil { return addr, false } + addrMap[addr] = &struct{}{} } diff --git a/ui/values/localizable/en.go b/ui/values/localizable/en.go index 3afe7ea8b..8ceb67975 100644 --- a/ui/values/localizable/en.go +++ b/ui/values/localizable/en.go @@ -875,7 +875,6 @@ const EN = ` "noSupportedBondAsset" = "DEX server (%s) does not support available asset wallets. Please choose another server." "dexPassword" = "DCRDEX Password" "insufficientBondAmount" = "Insufficient funds in this account, you need a minimum of %v to post bond." -"DEXInitErrorMsg" = "Something unexpected happened, please restart the Cryptopower Wallet." "txRegular" = "Regular Transactions" "stakingTx" = "Staking Transactions" "filter" = "Filter" @@ -909,12 +908,13 @@ const EN = ` "iWillGive" = "I'll give" "noSupportedMarketMsg" = "Oops, there are no supported market at the moment. Please choose another server." "postBondMsg" = "You have zero effective tiers. Please post bonds to trade." -"bondPostingInProgressMsg" = "You have zero effective tiers but bond posting is in progress. Please ensure your %s(%s) wallet account %s is funded with at least %s and is syncing." +"bondPostingInProgressMsg" = "You have zero effective tiers but bond posting is in progress. Please ensure you have more than %s in the %q account of your %s (%s) wallet." "maxDEX" = "Max %s: %s" "availableBalance" = "Available Balance: %s" "missingDEXWalletMsg" = "No %s trade wallet found. Please add %s wallet to the DEX client to trade." "swapAndRedeemFee" = "swap - %s, redeem - %s" -"loginWithDEXPassword" = "Login with your DEX password." +"loginWithDEXPassword" = "Login with your DEX password to trade on DCRDEX." +"loginDEXForPendingBonds" = "Login with your DEX password to monitor pending bond(s) until they are confirmed." "login" = "Login" "destinationWallet" = "Destination Wallet" "switchToMainnet" = "Switch to Mainnet" @@ -923,11 +923,16 @@ const EN = ` "updateDEXWalletPasswordReason" = "Your %s wallet (%s) is connected to your DEX account. Please provide your DEX password to update DEX with the new wallet password." "loginDEXForActiveOrdersOrExpiredBonds" = "You have one or more active orders and/or expired bonds waiting to be refunded, login to DEX now!" "walletsNeedToSync" = "Wallet(s) need to sync" -"walletsNeedToSyncMsg" = "Wallet(s) used by the DEX client needs to sync to settle active trade(s) or refund expired bonds." +"walletsNeedToSyncMsg" = "The following wallet(s) used by the DEX client needs to sync to settle active trade(s) or refund expired bonds: %s" "iWillSyncLater" = "I'll sync later" +"iWillLoginLater" = "I'll login later" "okaySync" = "Okay, sync" "paste" = "Paste" "backupDEXSeed" = "Backup DEX Seed" "dexSeed" = "DEX Seed" "optionalRestorationSeed" = "Restoration Seed (optional)" +"dexServerDisconnected" = "DEX server is disconnected" +"updateAPI" = "Check for Update API" +"newUpdateText" = "A new update, %s, is available" +"updateAvailable" = "Update Available" ` diff --git a/ui/values/strings.go b/ui/values/strings.go index 9b90c53ac..21f47ac51 100644 --- a/ui/values/strings.go +++ b/ui/values/strings.go @@ -116,928 +116,932 @@ func StringF(key string, a ...interface{}) string { } const ( - StrAbandoned = "abandoned" - StrAbout = "about" - StrAbstain = "abstain" - StrAccount = "account" - StrAccountList = "accountList" - StrAccountMixer = "accountMixer" - StrAcctCreated = "acctCreated" - StrAcctDetailsKey = "acctDetailsKey" - StrAcctName = "acctName" - StrAcctNum = "acctNum" - StrAcctRenamed = "accRenamed" - StrAddAcctWarn = "addAcctWarn" - StrAddDexServer = "addDexServer" - StrAddNewAccount = "addNewAccount" - StrAddRecipient = "addRecipient" - StrAddress = "address" - StrAddressCopied = "addressCopied" - StrAddressDiscoveryInProgress = "addressDiscoveryInProgress" - StrAddressDiscoveryStarted = "addressDiscoveryStarted" - StrAddressDiscoveryStartedBody = "addressDiscoveryStartedBody" - StrAddrNotOwned = "addrNotOwned" - StrAddVSP = "addVSP" - StrAddWallet = "addWallet" - StrAdminToTriggerVoting = "adminToTriggerVoting" - StrAdvanced = "advanced" - StrAdvancedOptions = "advancedOptions" - StrAdvancedSettingsMsg = "advancedSettingsMsg" - StrAgendas = "agendas" - StrAgo = "ago" - StrAll = "all" - StrAllowSpendingFromUnmixedAccount = "allowSpendingFromUnmixedAccount" - StrAllowUnspendUnmixedAcct = "allowUnspendUnmixedAcct" - StrAllTickets = "allTickets" - StrAmount = "amount" - StrAppLog = "appLog" - StrAppName = "appName" - StrApproved = "approved" - StrAppTitle = "appTitle" - StrAppWallet = "appWallet" - StrAskedEnterSeedWords = "askedEnterSeedWords" - StrAuthorToAuthorizeVoting = "authorToAuthorizeVoting" - StrAutomatic = "automatic" - StrAutoSetUp = "autoSetUp" - StrAutoSync = "autoSync" - StrAutoTicketInfo = "autoTicketInfo" - StrAutoTicketPurchase = "autoTicketPurchase" - StrAutoTicketWarn = "autoTicketWarn" - StrAwareOfRisk = "imawareOfRisk" - StrBackAndRename = "backAndRename" - StrBackStaking = "backStaking" - StrBackToWallets = "backToWallets" - StrBackupInfo = "backupInfo" - StrBackupLater = "backupLater" - StrBackupNow = "backupNow" - StrBackupSeedPhrase = "backupSeedPhrase" - StrBackupWarning = "backupWarning" - StrBalance = "balance" - StrBalanceAfter = "balanceAfter" - StrBalanceToMaintain = "balanceToMaintain" - StrBalToMaintain = "balToMaintain" - StrBalToMaintainValue = "balToMaintainValue" - StrBeepForNewBlocks = "beepForNewBlocks" - StrBestBlockAge = "bestBlockAge" - StrBestBlocks = "bestBlocks" - StrBestBlockTimestamp = "bestBlockTimestamp" - StrCurrencyConverterRate = "currencyConverterRate" - StrBlockHeaderFetched = "blockHeaderFetched" - StrBlockHeaderFetchedCount = "blockHeaderFetchedCount" - StrBlocksLeft = "blocksLeft" - StrBlocksScanned = "blocksScanned" - StrBuild = "build" - StrBuildDate = "buildDate" - StrCanBuy = "canBuy" - StrCancel = "cancel" - StrCanceling = "canceling" - StrCancelMixer = "cancelMixer" - StrCentralizedExchange = "centralizedExchange" - StrCentralizedExchangeCex = "centralizedExchangeCex" - StrCexContent = "cexContent" - StrChangeAccount = "changeAccount" - StrChangeSpecificPeer = "changeSpecificPeer" - StrChangeSpendingPass = "changeSpendingPass" - StrChangeStartupPassword = "changeStartupPassword" - StrChangeUserAgent = "changeUserAgent" - StrChangeWalletName = "changeWalletName" - StrCheckGovernace = "checkGovernace" - StrCheckMixerStatus = "checkMixerStatus" - StrCheckStatistics = "checkStatistics" - StrCheckWalletLog = "checkWalletLog" - StrChooseSetupType = "chooseSetupType" - StrClear = "clear" - StrClearAll = "clearAll" - StrClearSelection = "clearSelection" - StrClosingWallet = "closingWallet" - StrCoinSelection = "coinSelection" - StrColon = "colon" - StrComplete = "complete" - StrConfirm = "confirm" - StrConfirmations = "confirmations" - StrConfirmed = "confirmed" - StrConfirmNewSpendingPassword = "confirmNewSpendingPassword" - StrConfirmNewStartupPass = "confirmNewStartupPass" - StrConfirmOrder = "confirmOrder" - StrConfirmPending = "confirmPending" - StrConfirmPurchase = "confirmPurchase" - StrConfirmRemoveStartupPass = "confirmRemoveStartupPass" - StrConfirmSend = "confirmSend" - StrConfirmShowSeed = "confirmToShowSeed" - StrConfirmSpendingPassword = "confirmSpendingPassword" - StrConfirmStartupPass = "confirmStartupPass" - StrConfirmToCreateAccs = "confirmtoCreateAccs" - StrConfirmToMixAccount = "confirmToMixAcc" - StrConfirmToRemove = "confirmToRemove" - StrConfirmToSetMixer = "confirmToSetMixer" - StrConfirmToSign = "confirmToSign" - StrConfirmToVerifySeed = "confirmToVerifySeed" - StrConfirmUmixedSpending = "confirmUmixedSpending" - StrConfirmVote = "confirmVote" - StrConfirmYourOrder = "confirmYourOrder" - StrConfStatus = "confStatus" - StrConnectedPeersCount = "connectedPeersCount" - StrConnectedTo = "connectedTo" - StrConnecting = "connecting" - StrConnection = "connection" - StrConnectToSpecificPeer = "connectToSpecificPeer" - StrConsensusChange = "consensusChange" - StrConsensusDashboard = "consensusDashboard" - StrContinue = "continue" - StrCoordinationServer = "coordinationServer" - StrCopied = "copied" - StrCopy = "copy" - StrCopyBlockLink = "copyBlockLink" - StrCopyLink = "copyLink" - StrCopySeed = "copyseed" - StrCost = "cost" - StrCreate = "create" - StrCreateANewWallet = "createANewWallet" - StrCreateNewAccount = "createNewAccount" - StrCreateNewOrder = "createNewOrder" - StrCreateNSetUpAccs = "createNSetUpAccs" - StrCreateOrder = "createOrder" - StrCreateOrderPageInfo = "createOrderPageInfo" - StrCreateStartupPassword = "createStartupPassword" - StrCreateWallet = "createWallet" - StrCrossPlatform = "crossPlatform" - StrCrossPlatformSubtext = "crossPlatformSubtext" - StrCurrentSpendingPassword = "currentSpendingPassword" - StrCurrentStartupPass = "currentStartupPass" - StrCurrentTotalBalance = "currentTotalBalance" - StrCustomUserAgent = "CustomUserAgent" - StrDangerZone = "dangerZone" - StrDarkMode = "darkMode" - StrDateCreated = "dateCreated" - StrDateSize = "dateSize" - StrDayAgo = "dayAgo" - StrDays = "days" - StrDaysAgo = "daysAgo" - StrDaysToMiss = "daysToMiss" - StrDaysToVote = "daysToVote" - StrDCRCaps = "dcrCaps" - StrDcrDex = "dcrDex" - StrDcrReceived = "dcrReceived" - StrDebug = "debug" - StrDecentralized = "decentralized" - StrDefault = "default" - StrDelete = "delete" - StrDescriptionNote = "descriptionNote" - StrDestAddr = "destAddr" - StrDestination = "destination" - StrDestinationMissing = "destinationMissing" - StrDestinationModalInfo = "destinationModalInfo" - StrDestinationWalletNotSynced = "destinationWalletNotSynced" - StrDEX = "dex" - StrDexContent = "dexContent" - StrDexDataReset = "dexDataReset" - StrDexDataResetFalse = "dexDataResetFalse" - StrTradeSettingsMsg = "tradeSettingsMsg" - StrDexResetInfo = "dexResetInfo" - StrDexStartupErr = "dexStartupErr" - StrDisable = "disable" - StrDisabled = "disabled" - StrDisconnect = "disconnect" - StrDiscoverAddressUsage = "discoverAddressUsage" - StrDiscoveringWalletAddress = "discoveringWalletAddress" - StrDiscussions = "discussions" - StrDocumentation = "documentation" - StrDone = "done" - StrDuration = "duration" - StrEdit = "edit" - StrEmptyMsg = "emptyMsg" - StrEmptySign = "emptySign" - StrEnableAPI = "enableAPI" - StrEnabled = "enabled" - StrEnglish = "english" - StrEnterAddressToSign = "enterAddressToSign" - StrEnterExtendedPubKey = "enterXpubKey" - StrEnterHex = "enterHex" - StrEnterSeedPhrase = "enterSeedPhrase" - StrEnterSpendingPassword = "enterSpendingPassword" - StrEnterValidAddress = "enterValidAddress" - StrEnterValidMsg = "enterValidMsg" - StrEnterWalDetails = "enterWalletDetails" - StrEnterWalletHex = "enterWalletHex" - StrEnterWalletName = "enterWalletName" - StrEnterWalletSeed = "enterWalletSeed" - StrErrorMovingFunds = "errorMovingFunds" - StrErrPassEmpty = "errPassEmpty" - StrEstimatedSize = "estimatedSize" - StrEstimatedTime = "estimatedTime" - StrExchange = "exchange" - StrExchangeAPI = "exchangeAPI" - StrExchangeIntro = "exchangeIntro" - StrExchangeRate = "exchangeRate" - StrExistingWalletName = "existingWalletName" - StrExit = "exit" - StrExpired = "expired" - StrExpiredInfo = "expiredInfo" - StrExpiredInfoDisc = "expiredInfoDisc" - StrExpiredInfoDiscSub = "expiredInfoDiscSub" - StrExpiredOn = "expiredOn" - StrExpiresIn = "expiresIn" - StrExplorerURL = "explorerURL" - StrExtendedCopied = "extendedKeyCopied" - StrExtendedInfo = "extendedInfo" - StrExtendedKey = "extendedKey" - StrExtendedPubKey = "extendedPubKey" - StrExternal = "external" - StrFailed = "failed" - StrFee = "fee" - StrFeeRateAPI = "feeRateAPI" - StrFeeRates = "feerates" - StrFetchingAgenda = "fetchingAgenda" - StrFetchingBlockHeaders = "fetchingBlockHeaders" - StrFetchingOrders = "fetchingOrders" - StrFetchingPolicies = "fetchingPolicies" - StrFetchingProposals = "fetchingProposals" - StrFetchProposals = "fetchProposals" - StrFetchRateError = "fetchRateError" - StrFetchRates = "fetchRates" - StrFetched = "fetched" - StrFinished = "finished" - StrFrench = "french" - StrFrequency = "frequency" - StrFrom = "from" - StrFunctionUnavailable = "functionUnavailable" - StrGapLimit = "gapLimit" - StrGapLimitInputErr = "gapLimitInputErr" - StrGeneral = "general" - StrGenerateAddress = "generateAddress" - StrGotIt = "gotIt" - StrGovernance = "governance" - StrGovernanceAPI = "governanceAPI" - StrGovernanceInfo = "governanceInfo" - StrGovernanceSettingsInfo = "governanceSettingsInfo" - StrHash = "hash" - StrHDPath = "hdPath" - StrHelp = "help" - StrHelpInfo = "helpInfo" - StrHex = "hex" - StrHideDetails = "hideDetails" - StrHideSeedPhrase = "hideSeedPhrase" - StrHint = "hint" - StrHistory = "history" - StrHourAgo = "hourAgo" - StrHours = "hours" - StrHoursAgo = "hoursAgo" - StrHowGovernanceWork = "howGovernanceWork" - StrHowNotToStoreSeedPhrase = "howNotToStoreSeedPhrase" - StrHowToCopy = "howToCopy" - StrHowToStoreSeedPhrase = "howToStoreSeedPhrase" - StrHTTPRequest = "httpReq" - StrImmature = "immature" - StrImmatureInfo = "immatureInfo" - StrImmatureRewards = "immatureRewards" - StrImmatureStakeGen = "immatureStakeGen" - StrImport = "import" - StrImportantSeedPhrase = "importantSeedPhrase" - StrImported = "imported" - StrImportExistingWallet = "importExistingWallet" - StrImportWatchingOnlyWallet = "importWatchingOnlyWallet" - StrIncludedInBlock = "includedInBlock" - StrInDiscussion = "inDiscussion" - StrInfo = "info" - StrInitiateSetup = "initiateSetup" - StrInProgress = "inprogress" - StrInsufficientFund = "insufficientFund" - StrInvalidAddress = "invalidAddress" - StrInvalidAmount = "invalidAmount" - StrInvalidHex = "invalidHex" - StrInvalidPassphrase = "invalidPassphrase" - StrInvalidSeedPhrase = "invalidSeedPhrase" - StrInvalidSignature = "invalidSignature" - StrIntegratedExchangeFunctionality = "integratedExchangeFunctionality" - StrIntegratedExchangeSubtext = "integratedExchangeSubtext" - StrIPAddress = "ipAddress" - StrJustNow = "justNow" - StrKeepAppOpen = "keepAppOpen" - StrKeepInMind = "keepInMind" - StrKey = "key" - StrLabelSpendable = "labelSpendable" - StrLanguage = "language" - StrLastBlockHeight = "lastBlockHeight" - StrLatestBlock = "latestBlock" - StrLicense = "license" - StrLifeSpan = "lifeSpan" - StrLive = "live" - StrLiveIn = "liveIn" - StrLiveInfo = "liveInfo" - StrLiveInfoDisc = "liveInfoDisc" - StrLiveInfoDiscSub = "liveInfoDiscSub" - StrLiveTickets = "liveTickets" - StrLoading = "loading" - StrLoadingPrice = "loadingPrice" - StrLoadingVSP = "loadingVSP" - StrLocked = "locked" - StrLockedByTickets = "lockedByTickets" - StrLockedIn = "lockedin" - StrLogLevel = "logLevel" - StrLogLevelCritical = "logLevelCritical" - StrLogLevelDebug = "logLevelDebug" - StrLogLevelError = "logLevelError" - StrLogLevelInfo = "logLevelInfo" - StrLogLevelOff = "logLevelOff" - StrLogLevelTrace = "logLevelTrace" - StrLogLevelWarn = "logLevelWarn" - StrManual = "manual" - StrManualSetUp = "manualSetUp" - StrMaturity = "maturity" - StrMax = "max" - StrMessage = "message" - StrMinimumAssetType = "minimumAssetType" - StrMinMax = "minMax" - StrMinuteAgo = "minuteAgo" - StrMinutes = "mins" - StrMinutesAgo = "minutesAgo" - StrMissedOn = "missedOn" - StrMissedTickets = "missedTickets" - StrMix = "mix" - StrMixed = "mixed" - StrMixedAccDisabled = "mixedAccDisabled" - StrMixedAccHidden = "mixedAccHidden" - StrMixedAccount = "mixedAccount" - StrMixer = "mixer" - StrMixerAccErrorMsg = "mixerAccErrorMsg" - StrMixerRunning = "mixerRunning" - StrMixerShutdown = "mixerShutdown" - StrMixerStart = "mixerStart" - StrMixingActivity = "mixingActivity" - StrMonthAgo = "monthAgo" - StrMonthsAgo = "monthsAgo" - StrMore = "more" - StrMoveFundsFrmDefaultToUnmixed = "moveFundsFrmDefaultToUnmixed" - StrMoveToUnmixed = "moveToUnmixed" - StrMultipleMixerAccNeeded = "multipleMixerAccNeeded" - StrMultiWalletSupport = "multiWalletSupport" - StrMultiWalletSupportSubtext = "multiWalletSubtext" - StrMyAcct = "myAcct" - StrNConfirmations = "nConfirmations" - StrNetwork = "network" - StrNeverSynced = "neverSynced" - StrNewest = "newest" - StrNewProposalUpdate = "newProposalUpdate" - StrNewSpendingPassword = "newSpendingPassword" - StrNewStartupPass = "newStartupPass" - StrNewWallet = "newWallet" - StrNext = "next" - StrNo = "no" - StrNoActiveTickets = "noActiveTickets" - StrNoAgendaYet = "noAgendaYet" - StrNoConnectedPeer = "noConnectedPeer" - StrNoExchangeOnTestnet = "noExchangeOnTestnet" - StrNoInternet = "noInternet" - StrNoMixable = "errNoMixable" - StrNonAccSelector = "nonAccSelector" - StrNone = "none" - StrNoOrders = "noOrders" - StrNoPoliciesYet = "noPoliciesYet" - StrNoProposals = "noProposal" - StrNoReward = "noReward" - StrNoStaking = "noStaking" - StrNotAllowed = "notAllowed" - StrNotApplicable = "notApplicable" - StrNotAvailable = "notAvailable" - StrNotBackedUp = "notBackedUp" - StrNotConnected = "notConnected" - StrNote = "note" - StrNotEnoughVotes = "notEnoughVotes" - StrNoTickets = "noTickets" - StrNotifications = "notifications" - StrNotOwned = "notOwned" - StrNoRecentTrades = "noRecentTrades" - StrNoTransactions = "noTransactions" - StrNotSameAccoutMixUnmix = "notSameAccoutMixUnmix" - StrNotSupported = "notSupported" - StrNoUTXOs = "noUTXOs" - StrNoValidAccountFound = "noValidAccountFound" - StrnoValidWalletFound = "noValidWalletFound" - StrNoVSPLoaded = "noVSPLoaded" - StrNoWalletLoaded = "noWalletLoaded" - StrNumberOfVotes = "numberOfVotes" - StrOffChainVote = "offChainVote" - StrOffline = "offline" - StrOk = "ok" - StrOK = "ok" - StrOldest = "oldest" - StrOnChainVote = "onChainVote" - StrOnline = "online" - StrOpeningWallet = "openingWallet" - StrOrderCeated = "orderCreated" - StrOrderDetails = "orderDetails" - StrOrderHistory = "orderHistory" - StrOrderReceivingTo = "orderReceivingTo" - StrOrderScheduler = "orderScheduler" - StrOrderSchedulerInfo = "orderSchedulerInfo" - StrOrderSendingFrom = "orderSendingFrom" - StrOrderSettingsSaved = "orderSettingsSaved" - StrOrderSubmitted = "orderSubmitted" - StrOverview = "overview" - StrOwned = "owned" - StrPageWarningNotSync = "pageWarningNotSync" - StrPageWarningSync = "pageWarningSync" - StrPasswordNotMatch = "passwordNotMatch" - StrPasteSeedWords = "pasteSeedWords" - StrPeer = "peer" - StrPeers = "peers" - StrPeersConnected = "peersConnected" - StrPending = "pending" - StrPercentageMixed = "percentageMixed" - StrPiKey = "piKey" - StrPolicySetSuccessful = "policySetSuccessfully" - StrPriority = "priority" - StrPrivacyInfo = "privacyInfo" - StrPrivacyModeActive = "privacyModeActive" - StrPrivacyModeInfo = "privacyModeInfo" - StrPrivacyModeInfoDesc = "privacyModeInfoDesc" - StrPrivacySettings = "privacySettings" - StrPropFetching = "propFetching" - StrPropNotif = "propNotif" - StrPropNotification = "propNotification" - StrProposal = "proposals" - StrProposalAddedNotif = "proposalAddedNotif" - StrProposalInfo = "proposalInfo" - StrProposalVoteDetails = "proposalVoteDetails" - StrPublished = "published" - StrPublished2 = "published2" - StrPurchased = "purchased" - StrPurchasedOn = "purchasedOn" - StrPurchasingAcct = "purchasingAcct" - StrQuorumRequirement = "quorumRequirement" - StrRate = "rate" - StrReadyToMix = "readyToMix" - StrRebroadcast = "rebroadcast" - StrReceive = "receive" - StrReceived = "received" - StrReceiveInfo = "receiveInfo" - StrReceiving = "receiving" - StrRecipient = "recipient" - StrReceivingAddress = "receivingAddress" - StrRecentOrders = "recentOrders" - StrRecentProposals = "recentProposals" - StrRecentTransactions = "recentTransactions" - StrRecommended = "recommended" - StrRecommendedSettingsMsg = "recommendedSettingsMsg" - StrRecentTrades = "recentTrades" - StrReconnect = "reconnect" - StrRefresh = "refresh" - StrRejected = "rejected" - StrRemove = "remove" - StrRemovePeer = "removePeer" - StrRemovePeerWarn = "removePeerWarn" - StrRemoveUserAgent = "removeUserAgent" - StrRemoveUserAgentWarn = "removeUserAgentWarn" - StrRemoveWallet = "removeWallet" - StrRemoveWalletInfo = "removeWalletInfo" - StrRename = "rename" - StrRenameAcct = "renameAcct" - StrRenameWalletSheetTitle = "renameWalletSheetTitle" - StrRepublished = "republished" - StrRescan = "rescan" - StrRescanBlockchain = "rescanBlockchain" - StrRescanInfo = "rescanInfo" - StrRescanningBlocks = "rescanningBlocks" - StrRescanningHeaders = "rescanningHeaders" - StrRescanProgressNotification = "rescanProgressNotification" - StrRestore = "restore" - StrRestoreExistingWallet = "restoreExistingWallet" - StrRestoreWallet = "restoreWallet" - StrRestoreWithHex = "restoreWithHex" - StrResumeAccountDiscoveryTitle = "resumeAccountDiscoveryTitle" - StrRetry = "retry" - StrRevocation = "revocation" - StrRevoke = "revoke" - StrRevokeCause = "revokeCause" - StrRevoked = "revoked" - StrRevokeInfo = "revokeInfo" - StrRevokeInfoDisc = "revokeInfoDisc" - StrReward = "reward" - StrRewardsEarned = "rewardsEarned" - StrSave = "save" - StrScheduler = "scheduler" - StrSchedulerRunning = "schedulerRunning" - StrSearch = "search" - StrSeconds = "secs" - StrSecurity = "security" - StrSecurityTools = "securityTools" - StrSecurityToolsInfo = "securityToolsInfo" - StrSeeAll = "seeAll" - StrSeedAlreadyExist = "seedAlreadyExist" - StrSeedHex = "seedHex" - StrSeedPhraseToRestore = "seedPhraseToRestore" - StrSeedPhraseVerified = "seedPhraseVerified" - StrSeedValidationFailed = "seedValidationFailed" - StrSelectAcc = "selectAcc" - StrSelectAServer = "selectAServer" - StrSelectAssetType = "selectAssetType" - StrSelectChangeAcc = "selectChangeAcc" - StrSelectDexServerToOpen = "selectDexServerToOpen" - StrSelectedAccount = "selectedAcct" - StrSelectedUTXO = "selectedUTXO" - StrSelectFrequency = "selectFrequency" - StrSelectMixedAcc = "selectMixedAcc" - StrSelectOption = "selectOption" - StrSelectPhrasesToVerify = "selectPhrasesToVerify" - StrSelectServerTitle = "selectServerTitle" - StrSelectTicket = "selectTicket" - StrSelectUTXO = "selectUTXO" - StrSelectVSP = "selectVSP" - StrSelectWallet = "selectWallet" - StrSelectWalletToOpen = "selectWalletToOpen" - StrSelectWalletType = "selectWalletType" - StrSend = "send" - StrSendConfModalTitle = "sendConfModalTitle" - StrSendInfo = "sendInfo" - StrSending = "sending" - StrSendingAcct = "sendingAcct" - StrSendingFrom = "sendingFrom" - StrSendWarning = "sendWarning" - StrSent = "sent" - StrServer = "server" - StrServerRate = "serverRate" - StrSetChoice = "setchoice" - StrSetGapLimit = "setGapLimit" - StrSetGapLimitInfo = "setGapLimitInfo" - StrSettings = "settings" - StrSetTreasuryPolicy = "setTreasuryPolicy" - StrSetUp = "setUp" - StrSetupMixerInfo = "setupMixerInfo" - StrMixingNotSetUp = "mixingNotSetUp" - StrSetUpNeededAccs = "setUpNeededAccs" - StrSetupStartupPassword = "setupStartupPassword" - StrSignature = "signature" - StrSignCopied = "signCopied" - StrSignMessage = "signMessage" - StrSignMessageInfo = "signMessageInfo" - StrSkip = "skip" - StrSkipWalletCreation = "skipWalletCreation" - StrSource = "source" - StrSourceAccount = "sourceAccount" - StrSourceModalInfo = "sourceModalInfo" - StrSourceWallet = "sourceWallet" - StrSourceWalletNotSynced = "sourceWalletNotSynced" - StrSpanish = "spanish" - StrSpendableIn = "spendableIn" - StrSpendingPassword = "spendingPassword" - StrSpendingPasswordInfo = "spendingPasswordInfo" - StrSpendingPasswordInfo2 = "spendingPasswordInfo2" - StrSpendingPasswordUpdated = "spendingPasswordUpdated" - StrStake = "stake" - StrStakeAge = "stakeAge" - StrStaked = "staked" - StrStakeShuffle = "stakeShuffle" - StrSetUpStakeShuffleIntro = "setUpStakeShuffleIntro" - StrSetUpStakeShuffleIntroDesc = "setUpStakeShuffleIntroDesc" - StrSetUpStakeShuffleIntroSubDesc = "setUpStakeShuffleIntroSubDesc" - StrSetUpStakeShuffleIntroButton = "setUpStakeShuffleIntroButton" - StrSetUpStakeShuffleAutoOrManualA = "setUpStakeShuffleAutoOrManualA" - StrSetUpStakeShuffleAutoOrManualB = "setUpStakeShuffleAutoOrManualB" - StrSetUpStakeShuffleAutoOrManualC = "setUpStakeShuffleAutoOrManualC" - StrSetUpStakeShuffleAutoOrManualD = "setUpStakeShuffleAutoOrManualD" - StrSetUpStakeShuffleAutoTitle = "setUpStakeShuffleAutoTitle" - StrSetUpStakeShuffleAutoDesc = "setUpStakeShuffleAutoDesc" - StrSetUpStakeShuffleManualTitle = "setUpStakeShuffleManualTitle" - StrSetUpStakeShuffleManualDesc = "setUpStakeShuffleManualDesc" - StrSetUpStakeShuffleWarningTitle = "setUpStakeShuffleWarningTitle" - StrSetUpStakeShuffleWarningDesc = "setUpStakeShuffleWarningDesc" - StrNotEnoughAccounts = "notEnoughAccounts" - StrStaking = "staking" - StrStakingActivity = "stakingActivity" - StrStart = "start" - StrStartTrading = "startTrading" - StrStartupPassConfirm = "startupPassConfirm" - StrStartupPassword = "startupPassword" - StrStartupPasswordEnabled = "startupPasswordEnabled" - StrStartupPasswordInfo = "startupPasswordInfo" - StrStatistics = "statistics" - StrStatus = "status" - StrStep1 = "step1" - StrStep2of2 = "step2of2" - StrSubmit = "submit" - StrSummary = "summary" - StrSureToCancelMixer = "sureToCancelMixer" - StrSureToExitBackup = "sureToExitBackup" - StrSureToSafeStoreSeed = "sureToSafeStoreSeed" - StrSync = "sync" - StrSyncCompTime = "syncCompTime" - StrSynced = "synced" - StrSyncingProgress = "syncingProgress" - StrSyncingProgressStat = "syncingProgressStat" - StrSyncingState = "syncingState" - StrSyncSteps = "syncSteps" - StrTakenAccount = "takenAccount" - StrTapToCopy = "tapToCopy" - StrTicketVoted = "ticketVoted" - StrTicket = "ticket" - StrTicketConfirmed = "ticketConfirmed" - StrTicketDetails = "ticketDetails" - StrTicketError = "ticketError" - StrTicketPrice = "ticketPrice" - StrTicketRecord = "ticketRecord" - StrTicketRevoked = "ticketRevoked" - StrTicketRevokedTitle = "ticketRevokedTitle" - StrTickets = "tickets" - StrTicketSettingSaved = "ticketSettingSaved" - StrTicketVotedTitle = "ticketVotedTitle" - StrTimeLeftFmt = "timeLeftFmt" - StrTo = "to" - StrToken = "token" - StrTotal = "total" - StrTotalAmount = "totalAmount" - StrTotalBalance = "totalBalance" - StrTotalCost = "totalCost" - StrTotalValue = "totalValue" - StrTotalVotes = "totalVotes" - StrTotalValueMsg = "totalValueMsg" - StrTotalVotesReverse = "totalVotesReverse" - StrTransactionDetails = "transactionDetails" - StrTransactionID = "transactionId" - StrTransactions = "transactions" - StrTransferred = "transferred" - StrTreasury = "treasury" - StrTreasurySpending = "treasurySpending" - StrTreasurySpendingInfo = "treasurySpendingInfo" - StrTxConfModalInfoTxt = "txConfModalInfoTxt" - StrTxdetailsInfo = "txDetailsInfo" - StrTxEstimateErr = "txEstimateErr" - StrTxFee = "txFee" - StrTxHashCopied = "txHashCopied" - StrTxNotification = "txNotification" - StrTxOverview = "txOverview" - StrTxSent = "txSent" - StrTxSize = "txSize" - StrTxStatusPending = "txStatusPending" - StrType = "type" - StrUmined = "unmined" - StrUnconfirmedFunds = "unconfirmedFunds" - StrUnconfirmedTx = "unconfirmedTx" - StrUnderReview = "underReview" - StrUnknown = "unknown" - StrUnlock = "unlock" - StrUnlockWithPassword = "unlockWithPassword" - StrUnminedInfo = "unminedInfo" - StrUnmixed = "unmixed" - StrUnmixedAccount = "unmixedAccount" - StrUnmixedBalance = "unmixedBalance" - StrUpcoming = "upcoming" - StrUpdated = "updated" - StrUpdatePreference = "updatePreference" - StrUpdatevotePref = "updateVotePref" - StrUptime = "uptime" - StrUsdBinance = "usdBinance" - StrUsdBittrex = "usdBittrex" - StrUseMixer = "useMixer" - StrUserAgent = "userAgent" - StrUserAgentDialogTitle = "userAgentDialogTitle" - StrUserAgentSummary = "userAgentSummary" - StrValidAddress = "validAddress" - StrValidate = "validate" - StrValidateAddr = "validateAddr" - StrValidateHostErr = "validateHostErr" - StrValidateMsg = "validateMsg" - StrValidateNote = "validateNote" - StrValidateWalHex = "validateWalHex" - StrValidateWalSeed = "validateWalSeed" - StrValidSignature = "validSignature" - StrVerify = "verify" - StrVerifyGovernanceKeys = "verifyGovernanceKeys" - StrVerifyMessage = "verifyMessage" - StrVerifyMessageInfo = "verifyMessageInfo" - StrVerifyMsgError = "verifyMsgError" - StrVerifyMsgNote = "verifyMsgNote" - StrVerifySeed = "verifySeed" - StrVerifySeedInfo = "verifySeedInfo" - StrVersion = "version" - StrViewAll = "viewAll" - StrViewAllOrders = "viewAllOrders" - StrViewAppLog = "viewAppLog" - StrViewDetails = "viewDetails" - StrViewOnExplorer = "viewOnExplorer" - StrViewOnPoliteia = "viewOnPoliteia" - StrViewProperty = "viewProperty" - StrViewSeedPhrase = "viewSeedPhrase" - StrViewTicket = "viewTicket" - StrVote = "vote" - StrVotes = "votes" - StrVoteChoice = "votechoice" - StrVoteConfirm = "voteConfirm" - StrVoted = "voted" - StrVotedInfo = "votedInfo" - StrVotedInfoDisc = "votedInfoDisc" - StrVotedOn = "votedOn" - StrVoteEndedNotif = "voteEndedNotif" - StrVoteSent = "voteSent" - StrVoteStartedNotif = "voteStartedNotif" - StrVoteTooltip = "voteTooltip" - StrVoteUpdated = "voteUpdated" - StrVoting = "voting" - StrVotingAuthority = "votingAuthority" - StrVotingDashboard = "votingDashboard" - StrVotingInProgress = "votingInProgress" - StrVotingPreference = "votingPreference" - StrVotingServiceProvider = "votingServiceProvider" - StrVotingWallet = "votingWallet" - StrVsp = "vsp" - StrVSPAPI = "vspAPI" - StrVspFee = "vspFee" - StrWaitingAuthor = "waitingForAuthor" - StrWaitingForAdmin = "waitingForAdmin" - StrWaitingState = "waitingState" - StrWalletCreated = "walletCreated" - StrWalletDirectory = "walletDirectory" - StrWalletExist = "walletExist" - StrWalletLog = "walletLog" - StrWalletName = "walletName" - StrWalletNameLengthError = "walletLengthError" - StrWalletNameMismatch = "walletNameMismatch" - StrWalletNotExist = "walletNotExist" - StrWalletNotSynced = "walletNotSynced" - StrWalletRemoved = "walletRemoved" - StrWalletRemoveInfo = "walletRemoveInfo" - StrWalletRenamed = "walletRenamed" - StrWalletRestored = "walletRestored" - StrWalletRestoreMsg = "walletRestoreMsg" - StrWallet = "wallet" - StrWallets = "wallets" - StrWalletsEnabledPrivacy = "walletsEnabledPrivacy" - StrWalletSettings = "walletSettings" - StrWalletStatus = "walletStatus" - StrWalletSyncing = "walletSyncing" - StrWalletToPurchaseFrom = "walletToPurchaseFrom" - StrWarningWatchWallet = "warningWatchWallet" - StrWatchOnly = "watchOnly" - StrWatchOnlyWalletImported = "watchOnlyWalletImported" - StrWatchOnlyWalletRemoveInfo = "watchOnlyWalletRemoveInfo" - StrWatchOnlyWallets = "watchOnlyWallets" - StrWebURL = "webURL" - StrWeekAgo = "weekAgo" - StrWeeksAgo = "weeksAgo" - StrWelcomeNote = "welcomeNote" - StrWhatIsDex = "whatIsDex" - StrWhatIsCex = "whatIsCex" - StrWhatToCallWallet = "whatToCallWallet" - StrWord = "word" - StrWouldTradeCex = "wouldTradeCex" - StrWriteDownAll33Words = "writeDownAll33Words" - StrWriteDownSeed = "writeDownSeed" - StrWroteAllWords = "wroteAllWords" - StrXInputsConsumed = "xInputsConsumed" - StrXOutputCreated = "xOutputCreated" - StrXpubKeyErr = "xpubKeyErr" - StrXpubWalletExist = "xpubWalletExist" - StrYearAgo = "yearAgo" - StrYearsAgo = "yearsAgo" - StrYes = "yes" - StrYesterday = "yesterday" - StrYourAddress = "yourAddress" - StrYourSeedWords = "yourSeedWord" - StrYourself = "yourself" - StrTrade = "trade" - StrSetTradePassword = "setTradePassword" - StrSetTradePasswordDesc = "setTradePasswordDesc" - StrNewPassword = "newPassword" - StrConfirmPassword = "confirmPassword" - StrSelectServer = "selectServer" - StrSelectDEXServerDesc = "selectDEXServerDesc" - StrSetPassword = "setPassword" - StrPostBond = "postBond" - StrPostBondDesc = "postBondDesc" - StrDCRDEXWelcomeMessage = "dcrdexWelcomeMessage" - StrAddServer = "addServer" - StrAddServerDesc = "addServerDesc" - StrServerURL = "serverURL" - StrInputURL = "inputURL" - StrCertificateOPtional = "certificateOPtional" - StrInputCertificate = "inputCertificate" - StrDEXServerAddrWarning = "DEXServerAddrWarning" - StrAdd = "add" - StrMarketOverview = "marketOverview" - StrName = "name" - StrPrice = "price" - StrRefreshState = "refreshState" - Str24HChange = "24hChange" - StrBack = "back" - StrSelectBondWalletMsg = "selectBondWalletMsg" - StrSupportedWallets = "supportedWallets" - StrSelectBondStrengthMsg = "selectBondStrengthMsg" - StrNewTier = "newTier" - StrBondStrength = "bondStrength" - StrBondStrengthMsg = "bondStrengthMsg" - StrCurrentTier = "currentTier" - StrCurrency = "currency" - StrWaitingForConfirmation = "waitingForConfirmation" - StrDEXBondConfirmationMsg = "dexBondConfirmationMsg" - StrConfirmationStatus = "confirmationStatus" - StrPaymentDetails = "paymentDetails" - StrConfirmationProgressMsg = "confirmationProgressMsg" - StrInsufficientFundsInAccount = "insufficientFundsInAccount" - StrBondStrengthErrMsg = "bondStrengthErrMsg" - StrMinimumBondStrength = "minimumBondStrength" - StrAssets = "assets" - StrTotalVotesTit = "totalVotesTit" - StrQuorumRequite = "quorumRequite" - StrDiscussionsTit = "discussionsTit" - StrTokenTit = "tokenTit" - StrNoWalletsAvailable = "noWalletsAvailable" - StrCreateAssetWalletToSwapMsg = "createAssetWalletToSwapMsg" - StrFound = "found" - StrRFP = "rfp" - StrProposedFor = "proposedFor" - StrAccounts = "accounts" - StrStakingInfo = "stakingInfo" - StrTimeLeft = "timeLeft" - StrTotalReward = "totalReward" - StrMarket = "market" - Str24hLow = "24hLow" - Str24hVolume = "24hVolume" - Str24hHigh = "24hHigh" - StrLots = "lots" - StrInvalidLot = "invalidLot" - StrInvalidPrice = "invalidPrice" - StrBuy = "buy" - StrSell = "sell" - StrImmediate = "immediate" - StrImmediateOrder = "immediateOrder" - StrImmediateExplanation = "immediateExplanation" - StrLimit = "limit" - StrEstimatedFee = "estimatedFee" - StrEpoch = "epoch" - StrAge = "age" - StrFilled = "filled" - StrSettled = "settled" - StrOpenOrders = "openOrders" - StrTradeHistory = "tradeHistory" - StrNoOpenOrdersMsg = "noOpenOrdersMsg" - StrNoTradeHistoryMsg = "noTradeHistoryMsg" - StrCurrencyPair = "currencyPair" - StrPair = "pair" - StrOrderBooks = "orderBooks" - StrSeeMore = "seeMore" - StrAssetPrice = "assetPrice" - StrAssetAmount = "assetAmount" - StrBooked = "booked" - StrExecuted = "executed" - StrCannotSpendWatchOnlyWallet = "cannotSpendWatchOnlyWallet" - StrAllWallets = "allWallets" - StrMultipleAssetRequiredMsg = "multipleAssetTypeRequiredToTradeDEX" - StrNoSupportedBondAsset = "noSupportedBondAsset" - StrDexPassword = "dexPassword" - StrInsufficientBondAmount = "insufficientBondAmount" - StrDEXInitErrorMsg = "DEXInitErrorMsg" - StrTxRegular = "txRegular" - StrStakingTx = "stakingTx" - StrFilter = "filter" - StrExport = "export" - StrMyAddress = "myAddress" - StrRegenerate = "regenerate" - StrIntegratedExchange = "integratedExchange" - StrSeedWords = "seedWords" - StrAllservers = "allservers" - StrReset = "reset" - StrResetDEXData = "resetDEXData" - StrResetDEXDataWarning = "resetDEXDataWarning" - StrDEXResetSuccessful = "dexResetSuccessful" - StrNoRecentProposals = "noRecentProposals" - StrRecommendedModalBody = "recommendedModalBody" - StrAdvancedModalBody = "advancedModalBody" - StrStartSync = "startSync" - StrTelegram = "telegram" - StrTwitter = "twitter" - StrMatrix = "matrix" - StrWebsite = "website" - StrCreateAssetWalletToVoteMsg = "createAssetWalletToVoteMsg" - StrGetTicketsNow = "getTicketsNow" - StrExportTransaction = "exportTransaction" - StrExportTransactionsMsg = "exportTransactionsMsg" - StrTime = "time" - StrDirection = "direction" - StrExportTransactionSuccessMsg = "exportTransactionSuccessMsg" - StrNoSupportedMarket = "noSupportedMarket" - StrNoSupportedMarketMsg = "noSupportedMarketMsg" - StrIWillGet = "iWillGet" - StrIWillGive = "iWillGive" - StrPostBondMsg = "postBondMsg" - StrBondPostingInProgressMsg = "bondPostingInProgressMsg" - StrMaxDEX = "maxDEX" - StrAvailableBalance = "availableBalance" - StrMissingDEXWalletMsg = "missingDEXWalletMsg" - StrSwapAndRedeemFee = "swapAndRedeemFee" - StrLoginWithDEXPassword = "loginWithDEXPassword" - StrLogin = "login" - StrDestinationWallet = "destinationWallet" - StrSwitchToMainnet = "switchToMainnet" - StrSwitchToTestnet = "switchToTestnet" - StrDexMainnetNotReady = "dexMainnetNotReady" - StrUpdateDEXWalletPasswordReason = "updateDEXWalletPasswordReason" - StrWalletsNeedToSync = "walletsNeedToSync" - StrWalletsNeedToSyncMsg = "walletsNeedToSyncMsg" - StrIWillSyncLater = "iWillSyncLater" - StrOkaySync = "okaySync" - StrPaste = "paste" - StrBackupDEXSeed = "backupDEXSeed" - StrDEXSeed = "dexSeed" - StrOptionalRestorationSeed = "optionalRestorationSeed" - + StrAbandoned = "abandoned" + StrAbout = "about" + StrAbstain = "abstain" + StrAccount = "account" + StrAccountList = "accountList" + StrAccountMixer = "accountMixer" + StrAcctCreated = "acctCreated" + StrAcctDetailsKey = "acctDetailsKey" + StrAcctName = "acctName" + StrAcctNum = "acctNum" + StrAcctRenamed = "accRenamed" + StrAddAcctWarn = "addAcctWarn" + StrAddDexServer = "addDexServer" + StrAddNewAccount = "addNewAccount" + StrAddRecipient = "addRecipient" + StrAddress = "address" + StrAddressCopied = "addressCopied" + StrAddressDiscoveryInProgress = "addressDiscoveryInProgress" + StrAddressDiscoveryStarted = "addressDiscoveryStarted" + StrAddressDiscoveryStartedBody = "addressDiscoveryStartedBody" + StrAddrNotOwned = "addrNotOwned" + StrAddVSP = "addVSP" + StrAddWallet = "addWallet" + StrAdminToTriggerVoting = "adminToTriggerVoting" + StrAdvanced = "advanced" + StrAdvancedOptions = "advancedOptions" + StrAdvancedSettingsMsg = "advancedSettingsMsg" + StrAgendas = "agendas" + StrAgo = "ago" + StrAll = "all" + StrAllowSpendingFromUnmixedAccount = "allowSpendingFromUnmixedAccount" + StrAllowUnspendUnmixedAcct = "allowUnspendUnmixedAcct" + StrAllTickets = "allTickets" + StrAmount = "amount" + StrAppLog = "appLog" + StrAppName = "appName" + StrApproved = "approved" + StrAppTitle = "appTitle" + StrAppWallet = "appWallet" + StrAskedEnterSeedWords = "askedEnterSeedWords" + StrAuthorToAuthorizeVoting = "authorToAuthorizeVoting" + StrAutomatic = "automatic" + StrAutoSetUp = "autoSetUp" + StrAutoSync = "autoSync" + StrAutoTicketInfo = "autoTicketInfo" + StrAutoTicketPurchase = "autoTicketPurchase" + StrAutoTicketWarn = "autoTicketWarn" + StrAwareOfRisk = "imawareOfRisk" + StrBackAndRename = "backAndRename" + StrBackStaking = "backStaking" + StrBackToWallets = "backToWallets" + StrBackupInfo = "backupInfo" + StrBackupLater = "backupLater" + StrBackupNow = "backupNow" + StrBackupSeedPhrase = "backupSeedPhrase" + StrBackupWarning = "backupWarning" + StrBalance = "balance" + StrBalanceAfter = "balanceAfter" + StrBalanceToMaintain = "balanceToMaintain" + StrBalToMaintain = "balToMaintain" + StrBalToMaintainValue = "balToMaintainValue" + StrBeepForNewBlocks = "beepForNewBlocks" + StrBestBlockAge = "bestBlockAge" + StrBestBlocks = "bestBlocks" + StrBestBlockTimestamp = "bestBlockTimestamp" + StrCurrencyConverterRate = "currencyConverterRate" + StrBlockHeaderFetched = "blockHeaderFetched" + StrBlockHeaderFetchedCount = "blockHeaderFetchedCount" + StrBlocksLeft = "blocksLeft" + StrBlocksScanned = "blocksScanned" + StrBuild = "build" + StrBuildDate = "buildDate" + StrCanBuy = "canBuy" + StrCancel = "cancel" + StrCanceling = "canceling" + StrCancelMixer = "cancelMixer" + StrCentralizedExchange = "centralizedExchange" + StrCentralizedExchangeCex = "centralizedExchangeCex" + StrCexContent = "cexContent" + StrChangeAccount = "changeAccount" + StrChangeSpecificPeer = "changeSpecificPeer" + StrChangeSpendingPass = "changeSpendingPass" + StrChangeStartupPassword = "changeStartupPassword" + StrChangeUserAgent = "changeUserAgent" + StrChangeWalletName = "changeWalletName" + StrCheckGovernace = "checkGovernace" + StrCheckMixerStatus = "checkMixerStatus" + StrCheckStatistics = "checkStatistics" + StrCheckWalletLog = "checkWalletLog" + StrChooseSetupType = "chooseSetupType" + StrClear = "clear" + StrClearAll = "clearAll" + StrClearSelection = "clearSelection" + StrClosingWallet = "closingWallet" + StrCoinSelection = "coinSelection" + StrColon = "colon" + StrComplete = "complete" + StrConfirm = "confirm" + StrConfirmations = "confirmations" + StrConfirmed = "confirmed" + StrConfirmNewSpendingPassword = "confirmNewSpendingPassword" + StrConfirmNewStartupPass = "confirmNewStartupPass" + StrConfirmOrder = "confirmOrder" + StrConfirmPending = "confirmPending" + StrConfirmPurchase = "confirmPurchase" + StrConfirmRemoveStartupPass = "confirmRemoveStartupPass" + StrConfirmSend = "confirmSend" + StrConfirmShowSeed = "confirmToShowSeed" + StrConfirmSpendingPassword = "confirmSpendingPassword" + StrConfirmStartupPass = "confirmStartupPass" + StrConfirmToCreateAccs = "confirmtoCreateAccs" + StrConfirmToMixAccount = "confirmToMixAcc" + StrConfirmToRemove = "confirmToRemove" + StrConfirmToSetMixer = "confirmToSetMixer" + StrConfirmToSign = "confirmToSign" + StrConfirmToVerifySeed = "confirmToVerifySeed" + StrConfirmUmixedSpending = "confirmUmixedSpending" + StrConfirmVote = "confirmVote" + StrConfirmYourOrder = "confirmYourOrder" + StrConfStatus = "confStatus" + StrConnectedPeersCount = "connectedPeersCount" + StrConnectedTo = "connectedTo" + StrConnecting = "connecting" + StrConnection = "connection" + StrConnectToSpecificPeer = "connectToSpecificPeer" + StrConsensusChange = "consensusChange" + StrConsensusDashboard = "consensusDashboard" + StrContinue = "continue" + StrCoordinationServer = "coordinationServer" + StrCopied = "copied" + StrCopy = "copy" + StrCopyBlockLink = "copyBlockLink" + StrCopyLink = "copyLink" + StrCopySeed = "copyseed" + StrCost = "cost" + StrCreate = "create" + StrCreateANewWallet = "createANewWallet" + StrCreateNewAccount = "createNewAccount" + StrCreateNewOrder = "createNewOrder" + StrCreateNSetUpAccs = "createNSetUpAccs" + StrCreateOrder = "createOrder" + StrCreateOrderPageInfo = "createOrderPageInfo" + StrCreateStartupPassword = "createStartupPassword" + StrCreateWallet = "createWallet" + StrCrossPlatform = "crossPlatform" + StrCrossPlatformSubtext = "crossPlatformSubtext" + StrCurrentSpendingPassword = "currentSpendingPassword" + StrCurrentStartupPass = "currentStartupPass" + StrCurrentTotalBalance = "currentTotalBalance" + StrCustomUserAgent = "CustomUserAgent" + StrDangerZone = "dangerZone" + StrDarkMode = "darkMode" + StrDateCreated = "dateCreated" + StrDateSize = "dateSize" + StrDayAgo = "dayAgo" + StrDays = "days" + StrDaysAgo = "daysAgo" + StrDaysToMiss = "daysToMiss" + StrDaysToVote = "daysToVote" + StrDCRCaps = "dcrCaps" + StrDcrDex = "dcrDex" + StrDcrReceived = "dcrReceived" + StrDebug = "debug" + StrDecentralized = "decentralized" + StrDefault = "default" + StrDelete = "delete" + StrDescriptionNote = "descriptionNote" + StrDestAddr = "destAddr" + StrDestination = "destination" + StrDestinationMissing = "destinationMissing" + StrDestinationModalInfo = "destinationModalInfo" + StrDestinationWalletNotSynced = "destinationWalletNotSynced" + StrDEX = "dex" + StrDexContent = "dexContent" + StrDexDataReset = "dexDataReset" + StrDexDataResetFalse = "dexDataResetFalse" + StrTradeSettingsMsg = "tradeSettingsMsg" + StrDexResetInfo = "dexResetInfo" + StrDexStartupErr = "dexStartupErr" + StrDisable = "disable" + StrDisabled = "disabled" + StrDisconnect = "disconnect" + StrDiscoverAddressUsage = "discoverAddressUsage" + StrDiscoveringWalletAddress = "discoveringWalletAddress" + StrDiscussions = "discussions" + StrDocumentation = "documentation" + StrDone = "done" + StrDuration = "duration" + StrEdit = "edit" + StrEmptyMsg = "emptyMsg" + StrEmptySign = "emptySign" + StrEnableAPI = "enableAPI" + StrEnabled = "enabled" + StrEnglish = "english" + StrEnterAddressToSign = "enterAddressToSign" + StrEnterExtendedPubKey = "enterXpubKey" + StrEnterHex = "enterHex" + StrEnterSeedPhrase = "enterSeedPhrase" + StrEnterSpendingPassword = "enterSpendingPassword" + StrEnterValidAddress = "enterValidAddress" + StrEnterValidMsg = "enterValidMsg" + StrEnterWalDetails = "enterWalletDetails" + StrEnterWalletHex = "enterWalletHex" + StrEnterWalletName = "enterWalletName" + StrEnterWalletSeed = "enterWalletSeed" + StrErrorMovingFunds = "errorMovingFunds" + StrErrPassEmpty = "errPassEmpty" + StrEstimatedSize = "estimatedSize" + StrEstimatedTime = "estimatedTime" + StrExchange = "exchange" + StrExchangeAPI = "exchangeAPI" + StrExchangeIntro = "exchangeIntro" + StrExchangeRate = "exchangeRate" + StrExistingWalletName = "existingWalletName" + StrExit = "exit" + StrExpired = "expired" + StrExpiredInfo = "expiredInfo" + StrExpiredInfoDisc = "expiredInfoDisc" + StrExpiredInfoDiscSub = "expiredInfoDiscSub" + StrExpiredOn = "expiredOn" + StrExpiresIn = "expiresIn" + StrExplorerURL = "explorerURL" + StrExtendedCopied = "extendedKeyCopied" + StrExtendedInfo = "extendedInfo" + StrExtendedKey = "extendedKey" + StrExtendedPubKey = "extendedPubKey" + StrExternal = "external" + StrFailed = "failed" + StrFee = "fee" + StrFeeRateAPI = "feeRateAPI" + StrFeeRates = "feerates" + StrFetchingAgenda = "fetchingAgenda" + StrFetchingBlockHeaders = "fetchingBlockHeaders" + StrFetchingOrders = "fetchingOrders" + StrFetchingPolicies = "fetchingPolicies" + StrFetchingProposals = "fetchingProposals" + StrFetchProposals = "fetchProposals" + StrFetchRateError = "fetchRateError" + StrFetchRates = "fetchRates" + StrFetched = "fetched" + StrFinished = "finished" + StrFrench = "french" + StrFrequency = "frequency" + StrFrom = "from" + StrFunctionUnavailable = "functionUnavailable" + StrGapLimit = "gapLimit" + StrGapLimitInputErr = "gapLimitInputErr" + StrGeneral = "general" + StrGenerateAddress = "generateAddress" + StrGotIt = "gotIt" + StrGovernance = "governance" + StrGovernanceAPI = "governanceAPI" + StrGovernanceInfo = "governanceInfo" + StrGovernanceSettingsInfo = "governanceSettingsInfo" + StrHash = "hash" + StrHDPath = "hdPath" + StrHelp = "help" + StrHelpInfo = "helpInfo" + StrHex = "hex" + StrHideDetails = "hideDetails" + StrHideSeedPhrase = "hideSeedPhrase" + StrHint = "hint" + StrHistory = "history" + StrHourAgo = "hourAgo" + StrHours = "hours" + StrHoursAgo = "hoursAgo" + StrHowGovernanceWork = "howGovernanceWork" + StrHowNotToStoreSeedPhrase = "howNotToStoreSeedPhrase" + StrHowToCopy = "howToCopy" + StrHowToStoreSeedPhrase = "howToStoreSeedPhrase" + StrHTTPRequest = "httpReq" + StrImmature = "immature" + StrImmatureInfo = "immatureInfo" + StrImmatureRewards = "immatureRewards" + StrImmatureStakeGen = "immatureStakeGen" + StrImport = "import" + StrImportantSeedPhrase = "importantSeedPhrase" + StrImported = "imported" + StrImportExistingWallet = "importExistingWallet" + StrImportWatchingOnlyWallet = "importWatchingOnlyWallet" + StrIncludedInBlock = "includedInBlock" + StrInDiscussion = "inDiscussion" + StrInfo = "info" + StrInitiateSetup = "initiateSetup" + StrInProgress = "inprogress" + StrInsufficientFund = "insufficientFund" + StrInvalidAddress = "invalidAddress" + StrInvalidAmount = "invalidAmount" + StrInvalidHex = "invalidHex" + StrInvalidPassphrase = "invalidPassphrase" + StrInvalidSeedPhrase = "invalidSeedPhrase" + StrInvalidSignature = "invalidSignature" + StrIntegratedExchangeFunctionality = "integratedExchangeFunctionality" + StrIntegratedExchangeSubtext = "integratedExchangeSubtext" + StrIPAddress = "ipAddress" + StrJustNow = "justNow" + StrKeepAppOpen = "keepAppOpen" + StrKeepInMind = "keepInMind" + StrKey = "key" + StrLabelSpendable = "labelSpendable" + StrLanguage = "language" + StrLastBlockHeight = "lastBlockHeight" + StrLatestBlock = "latestBlock" + StrLicense = "license" + StrLifeSpan = "lifeSpan" + StrLive = "live" + StrLiveIn = "liveIn" + StrLiveInfo = "liveInfo" + StrLiveInfoDisc = "liveInfoDisc" + StrLiveInfoDiscSub = "liveInfoDiscSub" + StrLiveTickets = "liveTickets" + StrLoading = "loading" + StrLoadingPrice = "loadingPrice" + StrLoadingVSP = "loadingVSP" + StrLocked = "locked" + StrLockedByTickets = "lockedByTickets" + StrLockedIn = "lockedin" + StrLogLevel = "logLevel" + StrLogLevelCritical = "logLevelCritical" + StrLogLevelDebug = "logLevelDebug" + StrLogLevelError = "logLevelError" + StrLogLevelInfo = "logLevelInfo" + StrLogLevelOff = "logLevelOff" + StrLogLevelTrace = "logLevelTrace" + StrLogLevelWarn = "logLevelWarn" + StrManual = "manual" + StrManualSetUp = "manualSetUp" + StrMaturity = "maturity" + StrMax = "max" + StrMessage = "message" + StrMinimumAssetType = "minimumAssetType" + StrMinMax = "minMax" + StrMinuteAgo = "minuteAgo" + StrMinutes = "mins" + StrMinutesAgo = "minutesAgo" + StrMissedOn = "missedOn" + StrMissedTickets = "missedTickets" + StrMix = "mix" + StrMixed = "mixed" + StrMixedAccDisabled = "mixedAccDisabled" + StrMixedAccHidden = "mixedAccHidden" + StrMixedAccount = "mixedAccount" + StrMixer = "mixer" + StrMixerAccErrorMsg = "mixerAccErrorMsg" + StrMixerRunning = "mixerRunning" + StrMixerShutdown = "mixerShutdown" + StrMixerStart = "mixerStart" + StrMixingActivity = "mixingActivity" + StrMonthAgo = "monthAgo" + StrMonthsAgo = "monthsAgo" + StrMore = "more" + StrMoveFundsFrmDefaultToUnmixed = "moveFundsFrmDefaultToUnmixed" + StrMoveToUnmixed = "moveToUnmixed" + StrMultipleMixerAccNeeded = "multipleMixerAccNeeded" + StrMultiWalletSupport = "multiWalletSupport" + StrMultiWalletSupportSubtext = "multiWalletSubtext" + StrMyAcct = "myAcct" + StrNConfirmations = "nConfirmations" + StrNetwork = "network" + StrNeverSynced = "neverSynced" + StrNewest = "newest" + StrNewProposalUpdate = "newProposalUpdate" + StrNewSpendingPassword = "newSpendingPassword" + StrNewStartupPass = "newStartupPass" + StrNewWallet = "newWallet" + StrNext = "next" + StrNo = "no" + StrNoActiveTickets = "noActiveTickets" + StrNoAgendaYet = "noAgendaYet" + StrNoConnectedPeer = "noConnectedPeer" + StrNoExchangeOnTestnet = "noExchangeOnTestnet" + StrNoInternet = "noInternet" + StrNoMixable = "errNoMixable" + StrNonAccSelector = "nonAccSelector" + StrNone = "none" + StrNoOrders = "noOrders" + StrNoPoliciesYet = "noPoliciesYet" + StrNoProposals = "noProposal" + StrNoReward = "noReward" + StrNoStaking = "noStaking" + StrNotAllowed = "notAllowed" + StrNotApplicable = "notApplicable" + StrNotAvailable = "notAvailable" + StrNotBackedUp = "notBackedUp" + StrNotConnected = "notConnected" + StrNote = "note" + StrNotEnoughVotes = "notEnoughVotes" + StrNoTickets = "noTickets" + StrNotifications = "notifications" + StrNotOwned = "notOwned" + StrNoRecentTrades = "noRecentTrades" + StrNoTransactions = "noTransactions" + StrNotSameAccoutMixUnmix = "notSameAccoutMixUnmix" + StrNotSupported = "notSupported" + StrNoUTXOs = "noUTXOs" + StrNoValidAccountFound = "noValidAccountFound" + StrnoValidWalletFound = "noValidWalletFound" + StrNoVSPLoaded = "noVSPLoaded" + StrNoWalletLoaded = "noWalletLoaded" + StrNumberOfVotes = "numberOfVotes" + StrOffChainVote = "offChainVote" + StrOffline = "offline" + StrOk = "ok" + StrOK = "ok" + StrOldest = "oldest" + StrOnChainVote = "onChainVote" + StrOnline = "online" + StrOpeningWallet = "openingWallet" + StrOrderCeated = "orderCreated" + StrOrderDetails = "orderDetails" + StrOrderHistory = "orderHistory" + StrOrderReceivingTo = "orderReceivingTo" + StrOrderScheduler = "orderScheduler" + StrOrderSchedulerInfo = "orderSchedulerInfo" + StrOrderSendingFrom = "orderSendingFrom" + StrOrderSettingsSaved = "orderSettingsSaved" + StrOrderSubmitted = "orderSubmitted" + StrOverview = "overview" + StrOwned = "owned" + StrPageWarningNotSync = "pageWarningNotSync" + StrPageWarningSync = "pageWarningSync" + StrPasswordNotMatch = "passwordNotMatch" + StrPasteSeedWords = "pasteSeedWords" + StrPeer = "peer" + StrPeers = "peers" + StrPeersConnected = "peersConnected" + StrPending = "pending" + StrPercentageMixed = "percentageMixed" + StrPiKey = "piKey" + StrPolicySetSuccessful = "policySetSuccessfully" + StrPriority = "priority" + StrPrivacyInfo = "privacyInfo" + StrPrivacyModeActive = "privacyModeActive" + StrPrivacyModeInfo = "privacyModeInfo" + StrPrivacyModeInfoDesc = "privacyModeInfoDesc" + StrPrivacySettings = "privacySettings" + StrPropFetching = "propFetching" + StrPropNotif = "propNotif" + StrPropNotification = "propNotification" + StrProposal = "proposals" + StrProposalAddedNotif = "proposalAddedNotif" + StrProposalInfo = "proposalInfo" + StrProposalVoteDetails = "proposalVoteDetails" + StrPublished = "published" + StrPublished2 = "published2" + StrPurchased = "purchased" + StrPurchasedOn = "purchasedOn" + StrPurchasingAcct = "purchasingAcct" + StrQuorumRequirement = "quorumRequirement" + StrRate = "rate" + StrReadyToMix = "readyToMix" + StrRebroadcast = "rebroadcast" + StrReceive = "receive" + StrReceived = "received" + StrReceiveInfo = "receiveInfo" + StrReceiving = "receiving" + StrRecipient = "recipient" + StrReceivingAddress = "receivingAddress" + StrRecentOrders = "recentOrders" + StrRecentProposals = "recentProposals" + StrRecentTransactions = "recentTransactions" + StrRecommended = "recommended" + StrRecommendedSettingsMsg = "recommendedSettingsMsg" + StrRecentTrades = "recentTrades" + StrReconnect = "reconnect" + StrRefresh = "refresh" + StrRejected = "rejected" + StrRemove = "remove" + StrRemovePeer = "removePeer" + StrRemovePeerWarn = "removePeerWarn" + StrRemoveUserAgent = "removeUserAgent" + StrRemoveUserAgentWarn = "removeUserAgentWarn" + StrRemoveWallet = "removeWallet" + StrRemoveWalletInfo = "removeWalletInfo" + StrRename = "rename" + StrRenameAcct = "renameAcct" + StrRenameWalletSheetTitle = "renameWalletSheetTitle" + StrRepublished = "republished" + StrRescan = "rescan" + StrRescanBlockchain = "rescanBlockchain" + StrRescanInfo = "rescanInfo" + StrRescanningBlocks = "rescanningBlocks" + StrRescanningHeaders = "rescanningHeaders" + StrRescanProgressNotification = "rescanProgressNotification" + StrRestore = "restore" + StrRestoreExistingWallet = "restoreExistingWallet" + StrRestoreWallet = "restoreWallet" + StrRestoreWithHex = "restoreWithHex" + StrResumeAccountDiscoveryTitle = "resumeAccountDiscoveryTitle" + StrRetry = "retry" + StrRevocation = "revocation" + StrRevoke = "revoke" + StrRevokeCause = "revokeCause" + StrRevoked = "revoked" + StrRevokeInfo = "revokeInfo" + StrRevokeInfoDisc = "revokeInfoDisc" + StrReward = "reward" + StrRewardsEarned = "rewardsEarned" + StrSave = "save" + StrScheduler = "scheduler" + StrSchedulerRunning = "schedulerRunning" + StrSearch = "search" + StrSeconds = "secs" + StrSecurity = "security" + StrSecurityTools = "securityTools" + StrSecurityToolsInfo = "securityToolsInfo" + StrSeeAll = "seeAll" + StrSeedAlreadyExist = "seedAlreadyExist" + StrSeedHex = "seedHex" + StrSeedPhraseToRestore = "seedPhraseToRestore" + StrSeedPhraseVerified = "seedPhraseVerified" + StrSeedValidationFailed = "seedValidationFailed" + StrSelectAcc = "selectAcc" + StrSelectAServer = "selectAServer" + StrSelectAssetType = "selectAssetType" + StrSelectChangeAcc = "selectChangeAcc" + StrSelectDexServerToOpen = "selectDexServerToOpen" + StrSelectedAccount = "selectedAcct" + StrSelectedUTXO = "selectedUTXO" + StrSelectFrequency = "selectFrequency" + StrSelectMixedAcc = "selectMixedAcc" + StrSelectOption = "selectOption" + StrSelectPhrasesToVerify = "selectPhrasesToVerify" + StrSelectServerTitle = "selectServerTitle" + StrSelectTicket = "selectTicket" + StrSelectUTXO = "selectUTXO" + StrSelectVSP = "selectVSP" + StrSelectWallet = "selectWallet" + StrSelectWalletToOpen = "selectWalletToOpen" + StrSelectWalletType = "selectWalletType" + StrSend = "send" + StrSendConfModalTitle = "sendConfModalTitle" + StrSendInfo = "sendInfo" + StrSending = "sending" + StrSendingAcct = "sendingAcct" + StrSendingFrom = "sendingFrom" + StrSendWarning = "sendWarning" + StrSent = "sent" + StrServer = "server" + StrServerRate = "serverRate" + StrSetChoice = "setchoice" + StrSetGapLimit = "setGapLimit" + StrSetGapLimitInfo = "setGapLimitInfo" + StrSettings = "settings" + StrSetTreasuryPolicy = "setTreasuryPolicy" + StrSetUp = "setUp" + StrSetupMixerInfo = "setupMixerInfo" + StrMixingNotSetUp = "mixingNotSetUp" + StrSetUpNeededAccs = "setUpNeededAccs" + StrSetupStartupPassword = "setupStartupPassword" + StrSignature = "signature" + StrSignCopied = "signCopied" + StrSignMessage = "signMessage" + StrSignMessageInfo = "signMessageInfo" + StrSkip = "skip" + StrSkipWalletCreation = "skipWalletCreation" + StrSource = "source" + StrSourceAccount = "sourceAccount" + StrSourceModalInfo = "sourceModalInfo" + StrSourceWallet = "sourceWallet" + StrSourceWalletNotSynced = "sourceWalletNotSynced" + StrSpanish = "spanish" + StrSpendableIn = "spendableIn" + StrSpendingPassword = "spendingPassword" + StrSpendingPasswordInfo = "spendingPasswordInfo" + StrSpendingPasswordInfo2 = "spendingPasswordInfo2" + StrSpendingPasswordUpdated = "spendingPasswordUpdated" + StrStake = "stake" + StrStakeAge = "stakeAge" + StrStaked = "staked" + StrStakeShuffle = "stakeShuffle" + StrSetUpStakeShuffleIntro = "setUpStakeShuffleIntro" + StrSetUpStakeShuffleIntroDesc = "setUpStakeShuffleIntroDesc" + StrSetUpStakeShuffleIntroSubDesc = "setUpStakeShuffleIntroSubDesc" + StrSetUpStakeShuffleIntroButton = "setUpStakeShuffleIntroButton" + StrSetUpStakeShuffleAutoOrManualA = "setUpStakeShuffleAutoOrManualA" + StrSetUpStakeShuffleAutoOrManualB = "setUpStakeShuffleAutoOrManualB" + StrSetUpStakeShuffleAutoOrManualC = "setUpStakeShuffleAutoOrManualC" + StrSetUpStakeShuffleAutoOrManualD = "setUpStakeShuffleAutoOrManualD" + StrSetUpStakeShuffleAutoTitle = "setUpStakeShuffleAutoTitle" + StrSetUpStakeShuffleAutoDesc = "setUpStakeShuffleAutoDesc" + StrSetUpStakeShuffleManualTitle = "setUpStakeShuffleManualTitle" + StrSetUpStakeShuffleManualDesc = "setUpStakeShuffleManualDesc" + StrSetUpStakeShuffleWarningTitle = "setUpStakeShuffleWarningTitle" + StrSetUpStakeShuffleWarningDesc = "setUpStakeShuffleWarningDesc" + StrNotEnoughAccounts = "notEnoughAccounts" + StrStaking = "staking" + StrStakingActivity = "stakingActivity" + StrStart = "start" + StrStartTrading = "startTrading" + StrStartupPassConfirm = "startupPassConfirm" + StrStartupPassword = "startupPassword" + StrStartupPasswordEnabled = "startupPasswordEnabled" + StrStartupPasswordInfo = "startupPasswordInfo" + StrStatistics = "statistics" + StrStatus = "status" + StrStep1 = "step1" + StrStep2of2 = "step2of2" + StrSubmit = "submit" + StrSummary = "summary" + StrSureToCancelMixer = "sureToCancelMixer" + StrSureToExitBackup = "sureToExitBackup" + StrSureToSafeStoreSeed = "sureToSafeStoreSeed" + StrSync = "sync" + StrSyncCompTime = "syncCompTime" + StrSynced = "synced" + StrSyncingProgress = "syncingProgress" + StrSyncingProgressStat = "syncingProgressStat" + StrSyncingState = "syncingState" + StrSyncSteps = "syncSteps" + StrTakenAccount = "takenAccount" + StrTapToCopy = "tapToCopy" + StrTicketVoted = "ticketVoted" + StrTicket = "ticket" + StrTicketConfirmed = "ticketConfirmed" + StrTicketDetails = "ticketDetails" + StrTicketError = "ticketError" + StrTicketPrice = "ticketPrice" + StrTicketRecord = "ticketRecord" + StrTicketRevoked = "ticketRevoked" + StrTicketRevokedTitle = "ticketRevokedTitle" + StrTickets = "tickets" + StrTicketSettingSaved = "ticketSettingSaved" + StrTicketVotedTitle = "ticketVotedTitle" + StrTimeLeftFmt = "timeLeftFmt" + StrTo = "to" + StrToken = "token" + StrTotal = "total" + StrTotalAmount = "totalAmount" + StrTotalBalance = "totalBalance" + StrTotalCost = "totalCost" + StrTotalValue = "totalValue" + StrTotalVotes = "totalVotes" + StrTotalValueMsg = "totalValueMsg" + StrTotalVotesReverse = "totalVotesReverse" + StrTransactionDetails = "transactionDetails" + StrTransactionID = "transactionId" + StrTransactions = "transactions" + StrTransferred = "transferred" + StrTreasury = "treasury" + StrTreasurySpending = "treasurySpending" + StrTreasurySpendingInfo = "treasurySpendingInfo" + StrTxConfModalInfoTxt = "txConfModalInfoTxt" + StrTxdetailsInfo = "txDetailsInfo" + StrTxEstimateErr = "txEstimateErr" + StrTxFee = "txFee" + StrTxHashCopied = "txHashCopied" + StrTxNotification = "txNotification" + StrTxOverview = "txOverview" + StrTxSent = "txSent" + StrTxSize = "txSize" + StrTxStatusPending = "txStatusPending" + StrType = "type" + StrUmined = "unmined" + StrUnconfirmedFunds = "unconfirmedFunds" + StrUnconfirmedTx = "unconfirmedTx" + StrUnderReview = "underReview" + StrUnknown = "unknown" + StrUnlock = "unlock" + StrUnlockWithPassword = "unlockWithPassword" + StrUnminedInfo = "unminedInfo" + StrUnmixed = "unmixed" + StrUnmixedAccount = "unmixedAccount" + StrUnmixedBalance = "unmixedBalance" + StrUpcoming = "upcoming" + StrUpdated = "updated" + StrUpdatePreference = "updatePreference" + StrUpdatevotePref = "updateVotePref" + StrUptime = "uptime" + StrUsdBinance = "usdBinance" + StrUsdBittrex = "usdBittrex" + StrUseMixer = "useMixer" + StrUserAgent = "userAgent" + StrUserAgentDialogTitle = "userAgentDialogTitle" + StrUserAgentSummary = "userAgentSummary" + StrValidAddress = "validAddress" + StrValidate = "validate" + StrValidateAddr = "validateAddr" + StrValidateHostErr = "validateHostErr" + StrValidateMsg = "validateMsg" + StrValidateNote = "validateNote" + StrValidateWalHex = "validateWalHex" + StrValidateWalSeed = "validateWalSeed" + StrValidSignature = "validSignature" + StrVerify = "verify" + StrVerifyGovernanceKeys = "verifyGovernanceKeys" + StrVerifyMessage = "verifyMessage" + StrVerifyMessageInfo = "verifyMessageInfo" + StrVerifyMsgError = "verifyMsgError" + StrVerifyMsgNote = "verifyMsgNote" + StrVerifySeed = "verifySeed" + StrVerifySeedInfo = "verifySeedInfo" + StrVersion = "version" + StrViewAll = "viewAll" + StrViewAllOrders = "viewAllOrders" + StrViewAppLog = "viewAppLog" + StrViewDetails = "viewDetails" + StrViewOnExplorer = "viewOnExplorer" + StrViewOnPoliteia = "viewOnPoliteia" + StrViewProperty = "viewProperty" + StrViewSeedPhrase = "viewSeedPhrase" + StrViewTicket = "viewTicket" + StrVote = "vote" + StrVotes = "votes" + StrVoteChoice = "votechoice" + StrVoteConfirm = "voteConfirm" + StrVoted = "voted" + StrVotedInfo = "votedInfo" + StrVotedInfoDisc = "votedInfoDisc" + StrVotedOn = "votedOn" + StrVoteEndedNotif = "voteEndedNotif" + StrVoteSent = "voteSent" + StrVoteStartedNotif = "voteStartedNotif" + StrVoteTooltip = "voteTooltip" + StrVoteUpdated = "voteUpdated" + StrVoting = "voting" + StrVotingAuthority = "votingAuthority" + StrVotingDashboard = "votingDashboard" + StrVotingInProgress = "votingInProgress" + StrVotingPreference = "votingPreference" + StrVotingServiceProvider = "votingServiceProvider" + StrVotingWallet = "votingWallet" + StrVsp = "vsp" + StrVSPAPI = "vspAPI" + StrVspFee = "vspFee" + StrWaitingAuthor = "waitingForAuthor" + StrWaitingForAdmin = "waitingForAdmin" + StrWaitingState = "waitingState" + StrWalletCreated = "walletCreated" + StrWalletDirectory = "walletDirectory" + StrWalletExist = "walletExist" + StrWalletLog = "walletLog" + StrWalletName = "walletName" + StrWalletNameLengthError = "walletLengthError" + StrWalletNameMismatch = "walletNameMismatch" + StrWalletNotExist = "walletNotExist" + StrWalletNotSynced = "walletNotSynced" + StrWalletRemoved = "walletRemoved" + StrWalletRemoveInfo = "walletRemoveInfo" + StrWalletRenamed = "walletRenamed" + StrWalletRestored = "walletRestored" + StrWalletRestoreMsg = "walletRestoreMsg" + StrWallet = "wallet" + StrWallets = "wallets" + StrWalletsEnabledPrivacy = "walletsEnabledPrivacy" + StrWalletSettings = "walletSettings" + StrWalletStatus = "walletStatus" + StrWalletSyncing = "walletSyncing" + StrWalletToPurchaseFrom = "walletToPurchaseFrom" + StrWarningWatchWallet = "warningWatchWallet" + StrWatchOnly = "watchOnly" + StrWatchOnlyWalletImported = "watchOnlyWalletImported" + StrWatchOnlyWalletRemoveInfo = "watchOnlyWalletRemoveInfo" + StrWatchOnlyWallets = "watchOnlyWallets" + StrWebURL = "webURL" + StrWeekAgo = "weekAgo" + StrWeeksAgo = "weeksAgo" + StrWelcomeNote = "welcomeNote" + StrWhatIsDex = "whatIsDex" + StrWhatIsCex = "whatIsCex" + StrWhatToCallWallet = "whatToCallWallet" + StrWord = "word" + StrWouldTradeCex = "wouldTradeCex" + StrWriteDownAll33Words = "writeDownAll33Words" + StrWriteDownSeed = "writeDownSeed" + StrWroteAllWords = "wroteAllWords" + StrXInputsConsumed = "xInputsConsumed" + StrXOutputCreated = "xOutputCreated" + StrXpubKeyErr = "xpubKeyErr" + StrXpubWalletExist = "xpubWalletExist" + StrYearAgo = "yearAgo" + StrYearsAgo = "yearsAgo" + StrYes = "yes" + StrYesterday = "yesterday" + StrYourAddress = "yourAddress" + StrYourSeedWords = "yourSeedWord" + StrYourself = "yourself" + StrTrade = "trade" + StrSetTradePassword = "setTradePassword" + StrSetTradePasswordDesc = "setTradePasswordDesc" + StrNewPassword = "newPassword" + StrConfirmPassword = "confirmPassword" + StrSelectServer = "selectServer" + StrSelectDEXServerDesc = "selectDEXServerDesc" + StrSetPassword = "setPassword" + StrPostBond = "postBond" + StrPostBondDesc = "postBondDesc" + StrDCRDEXWelcomeMessage = "dcrdexWelcomeMessage" + StrAddServer = "addServer" + StrAddServerDesc = "addServerDesc" + StrServerURL = "serverURL" + StrInputURL = "inputURL" + StrCertificateOPtional = "certificateOPtional" + StrInputCertificate = "inputCertificate" + StrDEXServerAddrWarning = "DEXServerAddrWarning" + StrAdd = "add" + StrMarketOverview = "marketOverview" + StrName = "name" + StrPrice = "price" + StrRefreshState = "refreshState" + Str24HChange = "24hChange" + StrBack = "back" + StrSelectBondWalletMsg = "selectBondWalletMsg" + StrSupportedWallets = "supportedWallets" + StrSelectBondStrengthMsg = "selectBondStrengthMsg" + StrNewTier = "newTier" + StrBondStrength = "bondStrength" + StrBondStrengthMsg = "bondStrengthMsg" + StrCurrentTier = "currentTier" + StrCurrency = "currency" + StrWaitingForConfirmation = "waitingForConfirmation" + StrDEXBondConfirmationMsg = "dexBondConfirmationMsg" + StrConfirmationStatus = "confirmationStatus" + StrPaymentDetails = "paymentDetails" + StrConfirmationProgressMsg = "confirmationProgressMsg" + StrInsufficientFundsInAccount = "insufficientFundsInAccount" + StrBondStrengthErrMsg = "bondStrengthErrMsg" + StrMinimumBondStrength = "minimumBondStrength" + StrAssets = "assets" + StrTotalVotesTit = "totalVotesTit" + StrQuorumRequite = "quorumRequite" + StrDiscussionsTit = "discussionsTit" + StrTokenTit = "tokenTit" + StrNoWalletsAvailable = "noWalletsAvailable" + StrCreateAssetWalletToSwapMsg = "createAssetWalletToSwapMsg" + StrFound = "found" + StrRFP = "rfp" + StrProposedFor = "proposedFor" + StrAccounts = "accounts" + StrStakingInfo = "stakingInfo" + StrTimeLeft = "timeLeft" + StrTotalReward = "totalReward" + StrMarket = "market" + Str24hLow = "24hLow" + Str24hVolume = "24hVolume" + Str24hHigh = "24hHigh" + StrLots = "lots" + StrInvalidLot = "invalidLot" + StrInvalidPrice = "invalidPrice" + StrBuy = "buy" + StrSell = "sell" + StrImmediate = "immediate" + StrImmediateOrder = "immediateOrder" + StrImmediateExplanation = "immediateExplanation" + StrLimit = "limit" + StrEstimatedFee = "estimatedFee" + StrEpoch = "epoch" + StrAge = "age" + StrFilled = "filled" + StrSettled = "settled" + StrOpenOrders = "openOrders" + StrTradeHistory = "tradeHistory" + StrNoOpenOrdersMsg = "noOpenOrdersMsg" + StrNoTradeHistoryMsg = "noTradeHistoryMsg" + StrCurrencyPair = "currencyPair" + StrPair = "pair" + StrOrderBooks = "orderBooks" + StrSeeMore = "seeMore" + StrAssetPrice = "assetPrice" + StrAssetAmount = "assetAmount" + StrBooked = "booked" + StrExecuted = "executed" + StrCannotSpendWatchOnlyWallet = "cannotSpendWatchOnlyWallet" + StrAllWallets = "allWallets" + StrMultipleAssetRequiredMsg = "multipleAssetTypeRequiredToTradeDEX" + StrNoSupportedBondAsset = "noSupportedBondAsset" + StrDexPassword = "dexPassword" + StrInsufficientBondAmount = "insufficientBondAmount" + StrTxRegular = "txRegular" + StrStakingTx = "stakingTx" + StrFilter = "filter" + StrExport = "export" + StrMyAddress = "myAddress" + StrRegenerate = "regenerate" + StrIntegratedExchange = "integratedExchange" + StrSeedWords = "seedWords" + StrAllservers = "allservers" + StrReset = "reset" + StrResetDEXData = "resetDEXData" + StrResetDEXDataWarning = "resetDEXDataWarning" + StrDEXResetSuccessful = "dexResetSuccessful" + StrNoRecentProposals = "noRecentProposals" + StrRecommendedModalBody = "recommendedModalBody" + StrAdvancedModalBody = "advancedModalBody" + StrStartSync = "startSync" + StrTelegram = "telegram" + StrTwitter = "twitter" + StrMatrix = "matrix" + StrWebsite = "website" + StrCreateAssetWalletToVoteMsg = "createAssetWalletToVoteMsg" + StrGetTicketsNow = "getTicketsNow" + StrExportTransaction = "exportTransaction" + StrExportTransactionsMsg = "exportTransactionsMsg" + StrTime = "time" + StrDirection = "direction" + StrExportTransactionSuccessMsg = "exportTransactionSuccessMsg" + StrNoSupportedMarket = "noSupportedMarket" + StrNoSupportedMarketMsg = "noSupportedMarketMsg" + StrIWillGet = "iWillGet" + StrIWillGive = "iWillGive" + StrPostBondMsg = "postBondMsg" + StrBondPostingInProgressMsg = "bondPostingInProgressMsg" + StrMaxDEX = "maxDEX" + StrAvailableBalance = "availableBalance" + StrMissingDEXWalletMsg = "missingDEXWalletMsg" + StrSwapAndRedeemFee = "swapAndRedeemFee" + StrLoginWithDEXPassword = "loginWithDEXPassword" + StrLoginDEXForPendingBonds = "loginDEXForPendingBonds" + StrLogin = "login" + StrDestinationWallet = "destinationWallet" + StrSwitchToMainnet = "switchToMainnet" + StrSwitchToTestnet = "switchToTestnet" + StrDexMainnetNotReady = "dexMainnetNotReady" + StrUpdateDEXWalletPasswordReason = "updateDEXWalletPasswordReason" StrLoginDEXForActiveOrdersOrExpiredBonds = "loginDEXForActiveOrdersOrExpiredBonds" + StrWalletsNeedToSync = "walletsNeedToSync" + StrWalletsNeedToSyncMsg = "walletsNeedToSyncMsg" + StrIWillSyncLater = "iWillSyncLater" + StrIWillLoginLater = "iWillLoginLater" + StrOkaySync = "okaySync" + StrPaste = "paste" + StrBackupDEXSeed = "backupDEXSeed" + StrDEXSeed = "dexSeed" + StrOptionalRestorationSeed = "optionalRestorationSeed" + StrDEXServerDisconnected = "dexServerDisconnected" + StrUpdateAPI = "updateAPI" + StrNewUpdateText = "newUpdateText" + StrUpdateAvailable = "updateAvailable" )