diff --git a/.gitignore b/.gitignore index 1c44ee5..841dafe 100644 --- a/.gitignore +++ b/.gitignore @@ -54,5 +54,5 @@ jniLibs/ ic_launcher.png Icon-App-*.png *.apk -assets/icon.png -lib/coin/coindef.dart + +Cargo.lock diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index 3e30aba..0000000 --- a/Cargo.lock +++ /dev/null @@ -1,3409 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "aes" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" -dependencies = [ - "aes-soft", - "aesni", - "cipher", -] - -[[package]] -name = "aes-soft" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" -dependencies = [ - "cipher", - "opaque-debug 0.3.0", -] - -[[package]] -name = "aesni" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" -dependencies = [ - "cipher", - "opaque-debug 0.3.0", -] - -[[package]] -name = "ahash" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e" - -[[package]] -name = "aho-corasick" -version = "0.7.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" -dependencies = [ - "memchr", -] - -[[package]] -name = "allo-isolate" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13e2dba4602fed6c46f7d5cae67fe0fb9a6af1ce01848228f7533a8450c73be" -dependencies = [ - "atomic", -] - -[[package]] -name = "android_log-sys" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85965b6739a430150bdd138e2374a98af0c3ee0d030b3bb7fc3bddff58d0102e" - -[[package]] -name = "android_logger" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9ed09b18365ed295d722d0b5ed59c01b79a826ff2d2a8f73d5ecca8e6fb2f66" -dependencies = [ - "android_log-sys", - "env_logger", - "lazy_static", - "log", -] - -[[package]] -name = "ansi_term" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -dependencies = [ - "winapi", -] - -[[package]] -name = "anyhow" -version = "1.0.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee10e43ae4a853c0a3591d4e2ada1719e553be18199d9da9d4a83f5927c2f5c7" - -[[package]] -name = "arrayref" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" - -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - -[[package]] -name = "async-stream" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "171374e7e3b2504e0e5236e3b59260560f9fe94bfe9ac39ba5e4e929c5590625" -dependencies = [ - "async-stream-impl", - "futures-core", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "648ed8c8d2ce5409ccd57453d9d1b214b342a0d69376a6feda1fd6cae3299308" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "async-trait" -version = "0.1.51" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44318e776df68115a881de9a8fd1b9e53368d7a4a5ce4cc48517da3393233a5e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "atomic" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b88d82667eca772c4aa12f0f1348b3ae643424c8876448f3f7bd5787032e234c" -dependencies = [ - "autocfg", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" - -[[package]] -name = "base-x" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" - -[[package]] -name = "base58" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5024ee8015f02155eee35c711107ddd9a9bf3cb689cf2a9089c97e79b6e1ae83" - -[[package]] -name = "base64" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" - -[[package]] -name = "bech32" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9ff0bbfd639f15c74af777d81383cf53efb7c93613f6cab67c6c11e05bbf8b" - -[[package]] -name = "bellman" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193b2e515c2f2b4547c724dd4d06ee046c95b78a6b2b60e16dd735e4d3b14002" -dependencies = [ - "bitvec 0.20.4", - "blake2s_simd", - "byteorder", - "crossbeam", - "ff", - "futures 0.1.31", - "futures-cpupool", - "group", - "num_cpus", - "pairing", - "rand_core 0.6.3", - "subtle 2.4.1", -] - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bitflags" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" - -[[package]] -name = "bitvec" -version = "0.19.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8942c8d352ae1838c9dda0b0ca2ab657696ef2232a20147cf1b30ae1a9cb4321" -dependencies = [ - "funty", - "radium 0.5.3", - "tap", - "wyz", -] - -[[package]] -name = "bitvec" -version = "0.20.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7774144344a4faa177370406a7ff5f1da24303817368584c6206c8303eb07848" -dependencies = [ - "funty", - "radium 0.6.2", - "tap", - "wyz", -] - -[[package]] -name = "blake2b_simd" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" -dependencies = [ - "arrayref", - "arrayvec", - "constant_time_eq", -] - -[[package]] -name = "blake2s_simd" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e461a7034e85b211a4acb57ee2e6730b32912b06c08cc242243c39fc21ae6a2" -dependencies = [ - "arrayref", - "arrayvec", - "constant_time_eq", -] - -[[package]] -name = "block-buffer" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" -dependencies = [ - "block-padding 0.1.5", - "byte-tools", - "byteorder", - "generic-array 0.12.4", -] - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array 0.14.4", -] - -[[package]] -name = "block-modes" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a0e8073e8baa88212fb5823574c02ebccb395136ba9a164ab89379ec6072f0" -dependencies = [ - "block-padding 0.2.1", - "cipher", -] - -[[package]] -name = "block-padding" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" -dependencies = [ - "byte-tools", -] - -[[package]] -name = "block-padding" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" - -[[package]] -name = "bls12_381" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c56609cc42c628848e7b18e0baf42a4ef626b8c50442dc08b8094bd21d8ad32" -dependencies = [ - "ff", - "group", - "pairing", - "rand_core 0.6.3", - "subtle 2.4.1", -] - -[[package]] -name = "bs58" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" -dependencies = [ - "sha2 0.9.8", -] - -[[package]] -name = "bstr" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a40b47ad93e1a5404e6c18dec46b628214fee441c70f4ab5d6942142cc268a3d" -dependencies = [ - "lazy_static", - "memchr", - "regex-automata", - "serde", -] - -[[package]] -name = "bumpalo" -version = "3.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" - -[[package]] -name = "byte-tools" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" - -[[package]] -name = "cast" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c24dab4283a142afa2fdca129b80ad2c6284e073930f964c3a1293c225ee39a" -dependencies = [ - "rustc_version 0.4.0", -] - -[[package]] -name = "cbindgen" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38728c31b994e4b849cf59feefb4a8bf26acd299ee0b92c9fb35bd14ad4b8dfa" -dependencies = [ - "clap", - "heck", - "indexmap", - "log", - "proc-macro2", - "quote", - "serde", - "serde_json", - "syn", - "tempfile", - "toml", -] - -[[package]] -name = "cc" -version = "1.0.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chrono" -version = "0.4.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" -dependencies = [ - "libc", - "num-integer", - "num-traits", - "time 0.1.44", - "winapi", -] - -[[package]] -name = "cipher" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" -dependencies = [ - "generic-array 0.14.4", -] - -[[package]] -name = "clap" -version = "2.33.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" -dependencies = [ - "ansi_term", - "atty", - "bitflags", - "strsim", - "textwrap", - "unicode-width", - "vec_map", -] - -[[package]] -name = "clipboard-win" -version = "4.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db8340083d28acb43451166543b98c838299b7e0863621be53a338adceea0ed" -dependencies = [ - "error-code", - "str-buf", - "winapi", -] - -[[package]] -name = "const_fn" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f92cfa0fd5690b3cf8c1ef2cabbd9b7ef22fa53cf5e1f92b05103f6d5d1cf6e7" - -[[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - -[[package]] -name = "core-foundation" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - -[[package]] -name = "cpufeatures" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "criterion" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1604dafd25fba2fe2d5895a9da139f8dc9b319a5fe5354ca137cbbce4e178d10" -dependencies = [ - "atty", - "cast", - "clap", - "criterion-plot", - "csv", - "itertools 0.10.1", - "lazy_static", - "num-traits", - "oorandom", - "plotters", - "rayon", - "regex", - "serde", - "serde_cbor", - "serde_derive", - "serde_json", - "tinytemplate", - "walkdir", -] - -[[package]] -name = "criterion-plot" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d00996de9f2f7559f7f4dc286073197f83e92256a59ed395f9aac01fe717da57" -dependencies = [ - "cast", - "itertools 0.10.1", -] - -[[package]] -name = "crossbeam" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69323bff1fb41c635347b8ead484a5ca6c3f11914d784170b158d8449ab07f8e" -dependencies = [ - "cfg-if 0.1.10", - "crossbeam-channel 0.4.4", - "crossbeam-deque 0.7.4", - "crossbeam-epoch 0.8.2", - "crossbeam-queue", - "crossbeam-utils 0.7.2", -] - -[[package]] -name = "crossbeam-channel" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87" -dependencies = [ - "crossbeam-utils 0.7.2", - "maybe-uninit", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-utils 0.8.5", -] - -[[package]] -name = "crossbeam-deque" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20ff29ded3204c5106278a81a38f4b482636ed4fa1e6cfbeef193291beb29ed" -dependencies = [ - "crossbeam-epoch 0.8.2", - "crossbeam-utils 0.7.2", - "maybe-uninit", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-epoch 0.9.5", - "crossbeam-utils 0.8.5", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" -dependencies = [ - "autocfg", - "cfg-if 0.1.10", - "crossbeam-utils 0.7.2", - "lazy_static", - "maybe-uninit", - "memoffset 0.5.6", - "scopeguard", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-utils 0.8.5", - "lazy_static", - "memoffset 0.6.4", - "scopeguard", -] - -[[package]] -name = "crossbeam-queue" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" -dependencies = [ - "cfg-if 0.1.10", - "crossbeam-utils 0.7.2", - "maybe-uninit", -] - -[[package]] -name = "crossbeam-utils" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" -dependencies = [ - "autocfg", - "cfg-if 0.1.10", - "lazy_static", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" -dependencies = [ - "cfg-if 1.0.0", - "lazy_static", -] - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-mac" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" -dependencies = [ - "generic-array 0.12.4", - "subtle 1.0.0", -] - -[[package]] -name = "crypto-mac" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" -dependencies = [ - "generic-array 0.14.4", - "subtle 2.4.1", -] - -[[package]] -name = "crypto_api" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f855e87e75a4799e18b8529178adcde6fd4f97c1449ff4821e747ff728bb102" - -[[package]] -name = "crypto_api_chachapoly" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d930b6a026ce9d358a17f9c9046c55d90b14bb847f36b6ebb6b19365d4feffb8" -dependencies = [ - "crypto_api", -] - -[[package]] -name = "csv" -version = "1.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" -dependencies = [ - "bstr", - "csv-core", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "csv-core" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" -dependencies = [ - "memchr", -] - -[[package]] -name = "digest" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -dependencies = [ - "generic-array 0.12.4", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array 0.14.4", -] - -[[package]] -name = "directories" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e69600ff1703123957937708eb27f7a564e48885c537782722ed0ba3189ce1d7" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if 1.0.0", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "discard" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" - -[[package]] -name = "dotenv" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" - -[[package]] -name = "either" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" - -[[package]] -name = "encoding_rs" -version = "0.8.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a74ea89a0a1b98f6332de42c95baff457ada66d1cb4030f9ff151b2041a1c746" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "endian-type" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" - -[[package]] -name = "env_logger" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "equihash" -version = "0.1.0" -source = "git+https://github.com/hhanh00/librustzcash.git?rev=71b6227a4d96faab2c838159f54b69e6ab034147#71b6227a4d96faab2c838159f54b69e6ab034147" -dependencies = [ - "blake2b_simd", - "byteorder", -] - -[[package]] -name = "error-code" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5115567ac25674e0043e472be13d14e537f37ea8aa4bdc4aef0c89add1db1ff" -dependencies = [ - "libc", - "str-buf", -] - -[[package]] -name = "fake-simd" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" - -[[package]] -name = "fallible-iterator" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" - -[[package]] -name = "fallible-streaming-iterator" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" - -[[package]] -name = "fd-lock" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0010f02effd88c702318c5dde0463206be67495d0b4d906ba7c0a8f166cc7f06" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "ff" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72a4d941a5b7c2a75222e2d44fcdf634a67133d9db31e177ae5ff6ecda852bfe" -dependencies = [ - "bitvec 0.20.4", - "rand_core 0.6.3", - "subtle 2.4.1", -] - -[[package]] -name = "fixedbitset" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" - -[[package]] -name = "flate2" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" -dependencies = [ - "cfg-if 1.0.0", - "crc32fast", - "libc", - "miniz_oxide", -] - -[[package]] -name = "flexi_logger" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab94b6ac8eb69f1496a6993f26f785b5fd6d99b7416023eb2a6175c0b242b1" -dependencies = [ - "atty", - "chrono", - "flate2", - "glob", - "lazy_static", - "log", - "regex", - "thiserror", - "yansi", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "form_urlencoded" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" -dependencies = [ - "matches", - "percent-encoding", -] - -[[package]] -name = "fpe" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a25080721bbcd2cd4d765b7d607ea350425fa087ce53cd3e31afcacdab850352" -dependencies = [ - "aes", - "block-modes", - "num-bigint", - "num-integer", - "num-traits", -] - -[[package]] -name = "funty" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" - -[[package]] -name = "futures" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" - -[[package]] -name = "futures" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12aa0eb539080d55c3f2d45a67c3b58b6b0773c1a3ca2dfec66d58c97fd66ca" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d" - -[[package]] -name = "futures-cpupool" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" -dependencies = [ - "futures 0.1.31", - "num_cpus", -] - -[[package]] -name = "futures-executor" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45025be030969d763025784f7f355043dc6bc74093e4ecc5000ca4dc50d8745c" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "522de2a0fe3e380f1bc577ba0474108faf3f6b18321dbf60b3b9c39a75073377" - -[[package]] -name = "futures-macro" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18e4a4b95cea4b4ccbcf1c5675ca7c4ee4e9e75eb79944d07defde18068f79bb" -dependencies = [ - "autocfg", - "proc-macro-hack", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "futures-sink" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36ea153c13024fe480590b3e3d4cad89a0cfacecc24577b68f86c6ced9c2bc11" - -[[package]] -name = "futures-task" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99" - -[[package]] -name = "futures-util" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481" -dependencies = [ - "autocfg", - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "proc-macro-hack", - "proc-macro-nested", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" -dependencies = [ - "typenum", -] - -[[package]] -name = "generic-array" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if 1.0.0", - "js-sys", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", -] - -[[package]] -name = "glob" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" - -[[package]] -name = "group" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61b3c1e8b4f1ca07e6605ea1be903a5f6956aec5c8a67fd44d56076631675ed8" -dependencies = [ - "byteorder", - "ff", - "rand_core 0.6.3", - "subtle 2.4.1", -] - -[[package]] -name = "h2" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fd819562fcebdac5afc5c113c3ec36f902840b70fd4fc458799c8ce4607ae55" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "half" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" - -[[package]] -name = "hashbrown" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" -dependencies = [ - "ahash", -] - -[[package]] -name = "hashbrown" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" - -[[package]] -name = "hashlink" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d99cf782f0dc4372d26846bec3de7804ceb5df083c2d4462c0b8d2330e894fa8" -dependencies = [ - "hashbrown 0.9.1", -] - -[[package]] -name = "heck" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hmac" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" -dependencies = [ - "crypto-mac 0.7.0", - "digest 0.8.1", -] - -[[package]] -name = "hmac" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" -dependencies = [ - "crypto-mac 0.8.0", - "digest 0.9.0", -] - -[[package]] -name = "hmac-drbg" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6e570451493f10f6581b48cdd530413b63ea9e780f544bfd3bdcaa0d89d1a7b" -dependencies = [ - "digest 0.8.1", - "generic-array 0.12.4", - "hmac 0.7.1", -] - -[[package]] -name = "http" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1323096b05d41827dadeaee54c9981958c0f94e670bc94ed80037d1a7b8b186b" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" - -[[package]] -name = "httpdate" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6456b8a6c8f33fee7d958fcd1b60d55b11940a79e63ae87013e6d22e26034440" - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "hyper" -version = "0.14.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436ec0091e4f20e655156a30a0df3770fe2900aa301e548e08446ec794b6953c" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f7a97316d44c0af9b0301e65010573a853a9fc97046d7331d7f6bc0fd5a64" -dependencies = [ - "futures-util", - "hyper", - "log", - "rustls", - "tokio", - "tokio-rustls", - "webpki", -] - -[[package]] -name = "idna" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "indexmap" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" -dependencies = [ - "autocfg", - "hashbrown 0.11.2", -] - -[[package]] -name = "ipnet" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" - -[[package]] -name = "itertools" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - -[[package]] -name = "js-sys" -version = "0.3.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "jubjub" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d7e7fef85ae7b26dd89f34175b7f3c5ace64067a110c2ac86cf92407a6666ca" -dependencies = [ - "bitvec 0.20.4", - "bls12_381", - "ff", - "group", - "rand_core 0.6.3", - "subtle 2.4.1", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - -[[package]] -name = "lexical-core" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" -dependencies = [ - "arrayvec", - "bitflags", - "cfg-if 1.0.0", - "ryu", - "static_assertions", -] - -[[package]] -name = "libc" -version = "0.2.107" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219" - -[[package]] -name = "libsecp256k1" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc1e2c808481a63dc6da2074752fdd4336a3c8fcc68b83db6f1fd5224ae7962" -dependencies = [ - "arrayref", - "crunchy", - "digest 0.8.1", - "hmac-drbg", - "rand 0.7.3", - "sha2 0.8.2", - "subtle 2.4.1", - "typenum", -] - -[[package]] -name = "libsqlite3-sys" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64d31059f22935e6c31830db5249ba2b7ecd54fd73a9909286f0a67aa55c2fbd" -dependencies = [ - "cc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "log" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "matches" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" - -[[package]] -name = "maybe-uninit" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" - -[[package]] -name = "memchr" -version = "2.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" - -[[package]] -name = "memoffset" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" -dependencies = [ - "autocfg", -] - -[[package]] -name = "memoffset" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" -dependencies = [ - "autocfg", -] - -[[package]] -name = "memzero" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93c0d11ac30a033511ae414355d80f70d9f29a44a49140face477117a1ee90db" - -[[package]] -name = "mime" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" - -[[package]] -name = "miniz_oxide" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" -dependencies = [ - "adler", - "autocfg", -] - -[[package]] -name = "mio" -version = "0.7.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" -dependencies = [ - "libc", - "log", - "miow", - "ntapi", - "winapi", -] - -[[package]] -name = "miow" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" -dependencies = [ - "winapi", -] - -[[package]] -name = "multimap" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" - -[[package]] -name = "nibble_vec" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" -dependencies = [ - "smallvec", -] - -[[package]] -name = "nix" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5e06129fb611568ef4e868c14b326274959aa70ff7776e9d55323531c374945" -dependencies = [ - "bitflags", - "cc", - "cfg-if 1.0.0", - "libc", - "memoffset 0.6.4", -] - -[[package]] -name = "nom" -version = "6.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c5c51b9083a3c620fa67a2a635d1ce7d95b897e957d6b28ff9a5da960a103a6" -dependencies = [ - "bitvec 0.19.5", - "funty", - "lexical-core", - "memchr", - "version_check", -] - -[[package]] -name = "ntapi" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" -dependencies = [ - "winapi", -] - -[[package]] -name = "num-bigint" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-integer" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "once_cell" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" - -[[package]] -name = "oorandom" -version = "11.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" - -[[package]] -name = "opaque-debug" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "openssl-probe" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" - -[[package]] -name = "pairing" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9be899ebf10363f018353dba1baabb7e83145f3683c7b83b73b93b563e3167cc" -dependencies = [ - "ff", - "group", -] - -[[package]] -name = "pbkdf2" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216eaa586a190f0a738f2f918511eecfa90f13295abec0e457cdebcceda80cbd" -dependencies = [ - "crypto-mac 0.8.0", -] - -[[package]] -name = "percent-encoding" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" - -[[package]] -name = "petgraph" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" -dependencies = [ - "fixedbitset", - "indexmap", -] - -[[package]] -name = "pin-project" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "576bc800220cc65dac09e99e97b08b358cfab6e17078de8dc5fee223bd2d0c08" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e8fe8163d14ce7f0cdac2e040116f22eac817edabff0be91e8aff7e9accf389" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f" - -[[package]] -name = "plotters" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a3fd9ec30b9749ce28cd91f255d569591cdf937fe280c312143e3c4bad6f2a" -dependencies = [ - "num-traits", - "plotters-backend", - "plotters-svg", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "plotters-backend" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d88417318da0eaf0fdcdb51a0ee6c3bed624333bff8f946733049380be67ac1c" - -[[package]] -name = "plotters-svg" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521fa9638fa597e1dc53e9412a4f9cefb01187ee1f7413076f9e6749e2885ba9" -dependencies = [ - "plotters-backend", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" - -[[package]] -name = "proc-macro-hack" -version = "0.5.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" - -[[package]] -name = "proc-macro-nested" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" - -[[package]] -name = "proc-macro2" -version = "1.0.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "prost" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e6984d2f1a23009bd270b8bb56d0926810a3d483f59c987d77969e9d8e840b2" -dependencies = [ - "bytes", - "prost-derive", -] - -[[package]] -name = "prost-build" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32d3ebd75ac2679c2af3a92246639f9fcc8a442ee420719cc4fe195b98dd5fa3" -dependencies = [ - "bytes", - "heck", - "itertools 0.9.0", - "log", - "multimap", - "petgraph", - "prost", - "prost-types", - "tempfile", - "which", -] - -[[package]] -name = "prost-derive" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "169a15f3008ecb5160cba7d37bcd690a7601b6d30cfb87a117d45e59d52af5d4" -dependencies = [ - "anyhow", - "itertools 0.9.0", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "prost-types" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b518d7cdd93dab1d1122cf07fa9a60771836c668dde9d9e2a139f957f0d9f1bb" -dependencies = [ - "bytes", - "prost", -] - -[[package]] -name = "protobuf" -version = "2.25.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47c327e191621a2158159df97cdbc2e7074bb4e940275e35abf38eb3d2595754" - -[[package]] -name = "protobuf-codegen" -version = "2.25.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3df8c98c08bd4d6653c2dbae00bd68c1d1d82a360265a5b0bbc73d48c63cb853" -dependencies = [ - "protobuf", -] - -[[package]] -name = "protobuf-codegen-pure" -version = "2.25.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "394a73e2a819405364df8d30042c0f1174737a763e0170497ec9d36f8a2ea8f7" -dependencies = [ - "protobuf", - "protobuf-codegen", -] - -[[package]] -name = "quote" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "radium" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8" - -[[package]] -name = "radium" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb" - -[[package]] -name = "radix_trie" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" -dependencies = [ - "endian-type", - "nibble_vec", -] - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc 0.2.0", -] - -[[package]] -name = "rand" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.3", - "rand_hc 0.3.1", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.3", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] - -[[package]] -name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom 0.2.3", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rand_hc" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" -dependencies = [ - "rand_core 0.6.3", -] - -[[package]] -name = "rayon" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" -dependencies = [ - "autocfg", - "crossbeam-deque 0.8.1", - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" -dependencies = [ - "crossbeam-channel 0.5.1", - "crossbeam-deque 0.8.1", - "crossbeam-utils 0.8.5", - "lazy_static", - "num_cpus", -] - -[[package]] -name = "redox_syscall" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" -dependencies = [ - "bitflags", -] - -[[package]] -name = "redox_users" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" -dependencies = [ - "getrandom 0.2.3", - "redox_syscall", -] - -[[package]] -name = "regex" -version = "1.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a26af418b574bd56588335b3a3659a65725d4e636eb1016c2f9e3b38c7cc759" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" - -[[package]] -name = "regex-syntax" -version = "0.6.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" - -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] - -[[package]] -name = "reqwest" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d2927ca2f685faf0fc620ac4834690d29e7abb153add10f5812eef20b5e280" -dependencies = [ - "base64", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "http", - "http-body", - "hyper", - "hyper-rustls", - "ipnet", - "js-sys", - "lazy_static", - "log", - "mime", - "percent-encoding", - "pin-project-lite", - "rustls", - "serde", - "serde_json", - "serde_urlencoded", - "tokio", - "tokio-rustls", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots", - "winreg", -] - -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin", - "untrusted", - "web-sys", - "winapi", -] - -[[package]] -name = "ripemd160" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eca4ecc81b7f313189bf73ce724400a07da2a6dac19588b03c8bd76a2dcc251" -dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", - "opaque-debug 0.3.0", -] - -[[package]] -name = "rusqlite" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38ee71cbab2c827ec0ac24e76f82eca723cee92c509a65f67dee393c25112" -dependencies = [ - "bitflags", - "fallible-iterator", - "fallible-streaming-iterator", - "hashlink", - "libsqlite3-sys", - "memchr", - "smallvec", -] - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver 0.9.0", -] - -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver 1.0.4", -] - -[[package]] -name = "rustls" -version = "0.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" -dependencies = [ - "base64", - "log", - "ring", - "sct", - "webpki", -] - -[[package]] -name = "rustls-native-certs" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a07b7c1885bd8ed3831c289b7870b13ef46fe0e856d288c30d9cc17d75a2092" -dependencies = [ - "openssl-probe", - "rustls", - "schannel", - "security-framework", -] - -[[package]] -name = "rustyline" -version = "8.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbd4eaf7a7738f76c98e4f0395253ae853be3eb018f7b0bb57fe1b6c17e31874" -dependencies = [ - "bitflags", - "cfg-if 1.0.0", - "clipboard-win", - "dirs-next", - "fd-lock", - "libc", - "log", - "memchr", - "nix", - "radix_trie", - "scopeguard", - "smallvec", - "unicode-segmentation", - "unicode-width", - "utf8parse", - "winapi", -] - -[[package]] -name = "ryu" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "schannel" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" -dependencies = [ - "lazy_static", - "winapi", -] - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "sct" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "secp256k1" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d03ceae636d0fed5bae6a7f4f664354c5f4fcedf6eef053fef17e49f837d0a" -dependencies = [ - "secp256k1-sys", -] - -[[package]] -name = "secp256k1-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "827cb7cce42533829c792fc51b82fbf18b125b45a702ef2c8be77fce65463a7b" -dependencies = [ - "cc", -] - -[[package]] -name = "security-framework" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23a2ac85147a3a11d77ecf1bc7166ec0b92febfa4461c37944e180f319ece467" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - -[[package]] -name = "serde" -version = "1.0.130" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_cbor" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" -dependencies = [ - "half", - "serde", -] - -[[package]] -name = "serde_derive" -version = "1.0.130" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e277c495ac6cd1a01a58d0a0c574568b4d1ddf14f59965c6a58b8d96400b54f3" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha1" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" - -[[package]] -name = "sha2" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" -dependencies = [ - "block-buffer 0.7.3", - "digest 0.8.1", - "fake-simd", - "opaque-debug 0.2.3", -] - -[[package]] -name = "sha2" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if 1.0.0", - "cpufeatures", - "digest 0.9.0", - "opaque-debug 0.3.0", -] - -[[package]] -name = "slab" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" - -[[package]] -name = "smallvec" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" - -[[package]] -name = "socket2" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "standback" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" -dependencies = [ - "version_check", -] - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "stdweb" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" -dependencies = [ - "discard", - "rustc_version 0.2.3", - "stdweb-derive", - "stdweb-internal-macros", - "stdweb-internal-runtime", - "wasm-bindgen", -] - -[[package]] -name = "stdweb-derive" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "serde_derive", - "syn", -] - -[[package]] -name = "stdweb-internal-macros" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" -dependencies = [ - "base-x", - "proc-macro2", - "quote", - "serde", - "serde_derive", - "serde_json", - "sha1", - "syn", -] - -[[package]] -name = "stdweb-internal-runtime" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" - -[[package]] -name = "str-buf" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d44a3643b4ff9caf57abcee9c2c621d6c03d9135e0d8b589bd9afb5992cb176a" - -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - -[[package]] -name = "subtle" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "syn" -version = "1.0.81" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "sync" -version = "0.1.0" -dependencies = [ - "anyhow", - "bech32", - "bincode", - "blake2b_simd", - "bls12_381", - "byteorder", - "chrono", - "clap", - "criterion", - "dotenv", - "env_logger", - "ff", - "flexi_logger", - "futures 0.3.17", - "group", - "hex", - "jubjub", - "lazy_static", - "lazycell", - "log", - "prost", - "protobuf", - "rand 0.8.4", - "rand_chacha 0.3.1", - "rayon", - "reqwest", - "ripemd160", - "rusqlite", - "rustyline", - "secp256k1", - "serde", - "serde_json", - "sha2 0.9.8", - "thiserror", - "tiny-bip39", - "tiny-hderive", - "tokio", - "tokio-stream", - "tonic", - "tonic-build", - "zcash_address", - "zcash_client_backend", - "zcash_params", - "zcash_primitives", - "zcash_proofs", -] - -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "unicode-xid", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "tempfile" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "rand 0.8.4", - "redox_syscall", - "remove_dir_all", - "winapi", -] - -[[package]] -name = "termcolor" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "thiserror" -version = "1.0.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "time" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", -] - -[[package]] -name = "time" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242" -dependencies = [ - "const_fn", - "libc", - "standback", - "stdweb", - "time-macros", - "version_check", - "winapi", -] - -[[package]] -name = "time-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" -dependencies = [ - "proc-macro-hack", - "time-macros-impl", -] - -[[package]] -name = "time-macros-impl" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" -dependencies = [ - "proc-macro-hack", - "proc-macro2", - "quote", - "standback", - "syn", -] - -[[package]] -name = "tiny-bip39" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc59cb9dfc85bb312c3a78fd6aa8a8582e310b0fa885d5bb877f6dcc601839d" -dependencies = [ - "anyhow", - "hmac 0.8.1", - "once_cell", - "pbkdf2", - "rand 0.7.3", - "rustc-hash", - "sha2 0.9.8", - "thiserror", - "unicode-normalization", - "wasm-bindgen", - "zeroize", -] - -[[package]] -name = "tiny-hderive" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01b874a4992538d4b2f4fbbac11b9419d685f4b39bdc3fed95b04e07bfd76040" -dependencies = [ - "base58", - "hmac 0.7.1", - "libsecp256k1", - "memzero", - "sha2 0.8.2", -] - -[[package]] -name = "tinytemplate" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" -dependencies = [ - "serde", - "serde_json", -] - -[[package]] -name = "tinyvec" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" - -[[package]] -name = "tokio" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e992e41e0d2fb9f755b37446f20900f64446ef54874f40a60c78f021ac6144" -dependencies = [ - "autocfg", - "bytes", - "libc", - "memchr", - "mio", - "num_cpus", - "pin-project-lite", - "tokio-macros", - "winapi", -] - -[[package]] -name = "tokio-macros" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9efc1aba077437943f7515666aa2b882dfabfbfdf89c819ea75a8d6e9eaba5e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tokio-rustls" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" -dependencies = [ - "rustls", - "tokio", - "webpki", -] - -[[package]] -name = "tokio-stream" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "log", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "toml" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" -dependencies = [ - "serde", -] - -[[package]] -name = "tonic" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ac42cd97ac6bd2339af5bcabf105540e21e45636ec6fa6aae5e85d44db31be0" -dependencies = [ - "async-stream", - "async-trait", - "base64", - "bytes", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "percent-encoding", - "pin-project", - "prost", - "prost-derive", - "rustls-native-certs", - "tokio", - "tokio-rustls", - "tokio-stream", - "tokio-util", - "tower", - "tower-service", - "tracing", - "tracing-futures", -] - -[[package]] -name = "tonic-build" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c695de27302f4697191dda1c7178131a8cb805463dda02864acb80fe1322fdcf" -dependencies = [ - "proc-macro2", - "prost-build", - "quote", - "syn", -] - -[[package]] -name = "tower" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00e500fff5fa1131c866b246041a6bf96da9c965f8fe4128cb1421f23e93c00" -dependencies = [ - "futures-core", - "futures-util", - "indexmap", - "pin-project", - "pin-project-lite", - "rand 0.8.4", - "slab", - "tokio", - "tokio-stream", - "tokio-util", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-layer" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "343bc9466d3fe6b0f960ef45960509f84480bf4fd96f92901afe7ff3df9d3a62" - -[[package]] -name = "tower-service" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" - -[[package]] -name = "tracing" -version = "0.1.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" -dependencies = [ - "cfg-if 1.0.0", - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tracing-core" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "pin-project", - "tracing", -] - -[[package]] -name = "try-lock" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" - -[[package]] -name = "typenum" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" - -[[package]] -name = "unicode-bidi" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" - -[[package]] -name = "unicode-normalization" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-segmentation" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" - -[[package]] -name = "unicode-width" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" - -[[package]] -name = "unicode-xid" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" - -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - -[[package]] -name = "url" -version = "2.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" -dependencies = [ - "form_urlencoded", - "idna", - "matches", - "percent-encoding", -] - -[[package]] -name = "utf8parse" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372" - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - -[[package]] -name = "version_check" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" - -[[package]] -name = "walkdir" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" -dependencies = [ - "same-file", - "winapi", - "winapi-util", -] - -[[package]] -name = "want" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" -dependencies = [ - "log", - "try-lock", -] - -[[package]] -name = "warp_api_ffi" -version = "0.1.0" -dependencies = [ - "allo-isolate", - "android_logger", - "anyhow", - "cbindgen", - "hex", - "log", - "once_cell", - "sync", - "tokio", - "zcash_multisig", - "zcash_primitives", -] - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - -[[package]] -name = "wasm-bindgen" -version = "0.2.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" -dependencies = [ - "cfg-if 1.0.0", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" -dependencies = [ - "bumpalo", - "lazy_static", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39" -dependencies = [ - "cfg-if 1.0.0", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" - -[[package]] -name = "web-sys" -version = "0.3.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki" -version = "0.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "webpki-roots" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940" -dependencies = [ - "webpki", -] - -[[package]] -name = "which" -version = "4.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea187a8ef279bc014ec368c27a920da2024d2a711109bfbe3440585d5cf27ad9" -dependencies = [ - "either", - "lazy_static", - "libc", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "winreg" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" -dependencies = [ - "winapi", -] - -[[package]] -name = "wyz" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" - -[[package]] -name = "yansi" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71" - -[[package]] -name = "zcash_address" -version = "0.0.0" -source = "git+https://github.com/hhanh00/librustzcash.git?rev=19a97f16945c68c33aedcc89f2a4f4d398658b05#19a97f16945c68c33aedcc89f2a4f4d398658b05" -dependencies = [ - "bech32", - "blake2b_simd", - "bs58", -] - -[[package]] -name = "zcash_client_backend" -version = "0.5.0" -source = "git+https://github.com/hhanh00/librustzcash.git?rev=71b6227a4d96faab2c838159f54b69e6ab034147#71b6227a4d96faab2c838159f54b69e6ab034147" -dependencies = [ - "base64", - "bech32", - "bls12_381", - "bs58", - "ff", - "group", - "hex", - "jubjub", - "nom", - "percent-encoding", - "protobuf", - "protobuf-codegen-pure", - "rand_core 0.6.3", - "subtle 2.4.1", - "time 0.2.27", - "zcash_note_encryption", - "zcash_primitives", -] - -[[package]] -name = "zcash_multisig" -version = "0.1.0" -dependencies = [ - "anyhow", - "bech32", - "blake2b_simd", - "bls12_381", - "byteorder", - "chrono", - "clap", - "dotenv", - "env_logger", - "ff", - "futures 0.3.17", - "group", - "hex", - "jubjub", - "lazy_static", - "lazycell", - "log", - "prost", - "rand 0.8.4", - "rand_chacha 0.3.1", - "rayon", - "reqwest", - "ripemd160", - "rusqlite", - "rustyline", - "secp256k1", - "serde", - "serde_json", - "sha2 0.9.8", - "thiserror", - "tiny-bip39", - "tiny-hderive", - "tokio", - "tokio-stream", - "tonic", - "tonic-build", - "zcash_client_backend", - "zcash_params", - "zcash_primitives", - "zcash_proofs", -] - -[[package]] -name = "zcash_note_encryption" -version = "0.0.0" -source = "git+https://github.com/hhanh00/librustzcash.git?rev=71b6227a4d96faab2c838159f54b69e6ab034147#71b6227a4d96faab2c838159f54b69e6ab034147" -dependencies = [ - "blake2b_simd", - "byteorder", - "crypto_api_chachapoly", - "ff", - "group", - "rand_core 0.6.3", - "subtle 2.4.1", -] - -[[package]] -name = "zcash_params" -version = "0.1.0" -dependencies = [ - "bls12_381", - "byteorder", - "ff", - "funty", - "group", - "hex", - "jubjub", - "lazy_static", - "rand 0.8.4", - "serde", - "zcash_primitives", -] - -[[package]] -name = "zcash_primitives" -version = "0.5.0" -source = "git+https://github.com/hhanh00/librustzcash.git?rev=71b6227a4d96faab2c838159f54b69e6ab034147#71b6227a4d96faab2c838159f54b69e6ab034147" -dependencies = [ - "aes", - "bitvec 0.20.4", - "blake2b_simd", - "blake2s_simd", - "bls12_381", - "byteorder", - "crypto_api_chachapoly", - "equihash", - "ff", - "fpe", - "funty", - "group", - "hex", - "jubjub", - "lazy_static", - "log", - "rand 0.8.4", - "rand_core 0.6.3", - "ripemd160", - "secp256k1", - "sha2 0.9.8", - "subtle 2.4.1", - "zcash_note_encryption", -] - -[[package]] -name = "zcash_proofs" -version = "0.5.0" -source = "git+https://github.com/hhanh00/librustzcash.git?rev=71b6227a4d96faab2c838159f54b69e6ab034147#71b6227a4d96faab2c838159f54b69e6ab034147" -dependencies = [ - "bellman", - "blake2b_simd", - "bls12_381", - "byteorder", - "directories", - "ff", - "group", - "jubjub", - "lazy_static", - "rand_core 0.6.3", - "zcash_primitives", -] - -[[package]] -name = "zeroize" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d68d9dcec5f9b43a30d38c49f91dfedfaac384cb8f085faca366c26207dd1619" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65f1a51723ec88c66d5d1fe80c841f17f63587d6691901d66be9bec6c3b51f73" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] diff --git a/android/app/build.gradle b/android/app/build.gradle index 7a3e0f7..37cfc44 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -41,7 +41,7 @@ android { } defaultConfig { - applicationId "me.hanh.zwallet" + applicationId "me.hanh.zywallet" minSdkVersion 24 targetSdkVersion 30 versionCode flutterVersionCode.toInteger() diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml index 98dd853..159e01a 100644 --- a/android/app/src/debug/AndroidManifest.xml +++ b/android/app/src/debug/AndroidManifest.xml @@ -1,5 +1,5 @@ + package="me.hanh.zywallet"> diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 9a2f10f..de59357 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,7 +1,7 @@ + package="me.hanh.zywallet"> + + + + + + diff --git a/android/app/src/main/kotlin/me/hanh/zwallet/MainActivity.kt b/android/app/src/main/kotlin/me/hanh/zywallet/MainActivity.kt similarity index 93% rename from android/app/src/main/kotlin/me/hanh/zwallet/MainActivity.kt rename to android/app/src/main/kotlin/me/hanh/zywallet/MainActivity.kt index d144736..26f74bb 100644 --- a/android/app/src/main/kotlin/me/hanh/zwallet/MainActivity.kt +++ b/android/app/src/main/kotlin/me/hanh/zywallet/MainActivity.kt @@ -1,4 +1,4 @@ -package me.hanh.zwallet +package me.hanh.zywallet import io.flutter.embedding.android.FlutterFragmentActivity import io.flutter.embedding.engine.FlutterEngine diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml index 98dd853..159e01a 100644 --- a/android/app/src/profile/AndroidManifest.xml +++ b/android/app/src/profile/AndroidManifest.xml @@ -1,5 +1,5 @@ + package="me.hanh.zywallet"> diff --git a/assets/icon.png b/assets/icon.png new file mode 100644 index 0000000..98690c0 Binary files /dev/null and b/assets/icon.png differ diff --git a/configure.sh b/configure.sh index f7d0cad..bbc6aee 100755 --- a/configure.sh +++ b/configure.sh @@ -1,34 +1,4 @@ -export COIN=$1 - -case $COIN in -ycash) - export APP_TITLE=YWallet - export APP_NAME=ywallet - ;; -ycashtest) - export APP_TITLE=YWalletTest - export APP_NAME=ywallettest - ;; -zcashtest) - export APP_TITLE=ZWalletTest - export APP_NAME=zwallettest - ;; -zcash) - export APP_TITLE=ZWallet - export APP_NAME=zwallet - ;; -esac - -cp assets/$COIN.png assets/icon.png -cp lib/coin/$COIN.dart lib/coin/coindef.dart -cp native/zcash-params/src/coindef/$COIN.rs native/zcash-params/src/coin.rs - -mo pubspec.yaml.tpl > pubspec.yaml -mo ios/Runner/Info.plist.tpl > ios/Runner/Info.plist - flutter pub get -flutter pub run change_app_package_name:main me.hanh.$APP_NAME -mo android/app/src/main/AndroidManifest.xml.tpl > android/app/src/main/AndroidManifest.xml flutter pub run flutter_launcher_icons:main flutter pub run flutter_app_name flutter pub run build_runner build diff --git a/install-aab.sh b/install-aab.sh new file mode 100755 index 0000000..1cc0339 --- /dev/null +++ b/install-aab.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +adb uninstall me.hanh.zywallet +bundletool build-apks --overwrite --bundle=build/app/outputs/bundle/release/app-release.aab --output=/tmp/app.apks --ks=docker/zwallet.jks --ks-key-alias=hanh --ks-pass=pass:$JKS_PASSWORD +bundletool install-apks --adb=/usr/bin/adb --apks=/tmp/app.apks diff --git a/lib/about.dart b/lib/about.dart index 22d96c0..686a55a 100644 --- a/lib/about.dart +++ b/lib/about.dart @@ -11,7 +11,7 @@ import 'generated/l10n.dart'; Future showAbout(BuildContext context) async { final contentTemplate = await rootBundle.loadString('assets/about.md'); final template = Template(contentTemplate); - var content = template.renderString({'APP': coin.app}); + var content = template.renderString({'APP': APP_NAME}); PackageInfo packageInfo = await PackageInfo.fromPlatform(); String version = packageInfo.version; String code = packageInfo.buildNumber; @@ -20,7 +20,7 @@ Future showAbout(BuildContext context) async { context: context, barrierDismissible: false, builder: (context) => AlertDialog( - title: Text('${S.of(context).about} ${coin.app}'), + title: Text('${S.of(context).about} $APP_NAME'), contentPadding: EdgeInsets.zero, content: Container( width: double.maxFinite, diff --git a/lib/account.dart b/lib/account.dart index 01c37c4..599a351 100644 --- a/lib/account.dart +++ b/lib/account.dart @@ -1,376 +1,144 @@ import 'dart:async'; -import 'dart:io'; -import 'dart:ui'; -import 'dart:math'; -import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; -import 'package:intl/intl.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; import 'package:qr_flutter/qr_flutter.dart'; -import 'package:sensors_plus/sensors_plus.dart'; -import 'package:url_launcher/url_launcher.dart'; -import 'package:warp/store.dart'; -import 'package:warp_api/warp_api.dart'; -import 'about.dart'; -import 'budget.dart'; -import 'chart.dart'; -import 'contact.dart'; -import 'history.dart'; -import 'main.dart'; import 'generated/l10n.dart'; -import 'note.dart'; +import 'main.dart'; +import 'store.dart'; -class AccountPage extends StatefulWidget { +class AccountPage2 extends StatefulWidget { @override - State createState() => _AccountPageState(); + _AccountState createState() => _AccountState(); } -class _AccountPageState extends State - with - WidgetsBindingObserver, - AutomaticKeepAliveClientMixin, - SingleTickerProviderStateMixin { - Timer? _timerSync; - int _progress = 0; - bool _useSnapAddress = false; - String _snapAddress = ""; - late TabController _tabController; - bool _accountTab = true; - bool _contactsTab = false; - StreamSubscription? _progressDispose; - StreamSubscription? _syncDispose; - StreamSubscription? _accDispose; - final contactKey = GlobalKey(); - bool _flat = false; - +class _AccountState extends State with AutomaticKeepAliveClientMixin { @override - bool get wantKeepAlive => true; - - @override - initState() { - super.initState(); - _tabController = TabController(length: settings.simpleMode ? 3 : 6, vsync: this); - _tabController.addListener(() { - setState(() { - _accountTab = _tabController.index == 0; - _contactsTab = _tabController.index == (settings.simpleMode ? 2: 5); - }); - }); - Future.microtask(() async { - await accountManager.updateUnconfirmedBalance(); - await accountManager.fetchAccountData(false); - await contacts.fetchContacts(); - await _setupTimer(); - }); - WidgetsBinding.instance?.addObserver(this); - _progressDispose = progressStream.listen((percent) { - setState(() { - _progress = percent; - }); - }); - _syncDispose = syncStream.listen((height) { - setState(() { - if (height >= 0) { - syncStatus.setSyncHeight(height); - eta.checkpoint(height, DateTime.now()); - } else { - WarpApi.mempoolReset(syncStatus.latestHeight); - _trySync(); - } - }); - }); - _accDispose = accelerometerEvents.listen(_handleAccel); - } - - @override - void dispose() { - _timerSync?.cancel(); - WidgetsBinding.instance?.removeObserver(this); - _progressDispose?.cancel(); - _syncDispose?.cancel(); - _accDispose?.cancel(); - super.dispose(); - } - - @override - void didChangeAppLifecycleState(AppLifecycleState state) { - switch (state) { - case AppLifecycleState.detached: - case AppLifecycleState.inactive: - case AppLifecycleState.paused: - _timerSync?.cancel(); - _timerSync = null; - _accDispose?.cancel(); - _accDispose = null; - break; - case AppLifecycleState.resumed: - if (_timerSync == null) _setupTimer(); - if (_accDispose == null) - _accDispose = accelerometerEvents.listen(_handleAccel); - break; - } - } + bool get wantKeepAlive => true; //Set to true @override Widget build(BuildContext context) { super.build(context); - var s = S.of(context); - if (!syncStatus.isSynced() && !syncStatus.syncing) _trySync(); - final theme = Theme.of(this.context); - final hasTAddr = accountManager.taddress.isNotEmpty; - final qrSize = getScreenSize(context) / 2.5; - final hasMultisign = accountManager.canPay || accountManager.active.share != null; + return SingleChildScrollView( + padding: EdgeInsets.all(20), + child: Observer(builder: (context) { + final _1 = active.dataEpoch; + return Column(children: [ + SyncStatusWidget(), + Padding(padding: EdgeInsets.symmetric(vertical: 8)), + QRAddressWidget(), + Padding(padding: EdgeInsets.symmetric(vertical: 8)), + BalanceWidget(), + Padding(padding: EdgeInsets.symmetric(vertical: 8)), + MemPoolWidget(), + ProgressWidget(), + ]); + })); + } +} + +class SyncStatusWidget extends StatelessWidget { + @override + Widget build(BuildContext context) { + final s = S.of(context); + final theme = Theme.of(context); final simpleMode = settings.simpleMode; - return Scaffold( - appBar: AppBar( - centerTitle: true, - title: Observer( - builder: (context) => Text("${accountManager.active.name}")), - bottom: TabBar(controller: _tabController, isScrollable: true, tabs: [ - Tab(text: s.account), - if (!simpleMode) Tab(text: s.notes), - Tab(text: s.history), - if (!simpleMode) Tab(text: s.budget), - if (!simpleMode) Tab(text: s.tradingPl), - Tab(text: s.contacts), - ]), - actions: [ - Observer(builder: (context) { - accountManager.canPay; - return PopupMenuButton( - itemBuilder: (context) { - return [ - PopupMenuItem( - child: Text(s.accounts), value: "Accounts"), - PopupMenuItem( - child: Text(s.backup), value: "Backup"), - PopupMenuItem( - child: Text(s.rescan), value: "Rescan"), - if (!simpleMode && accountManager.canPay) - PopupMenuItem( - child: Text(s.coldStorage), value: "Cold"), - if (!simpleMode) - PopupMenuItem( - child: Text(s.multipay), value: "MultiPay"), - if (!simpleMode) - PopupMenuItem( - child: Text(s.broadcast), value: "Broadcast"), - if (!simpleMode && coin.supportsMultisig && hasMultisign) PopupMenuItem( - child: Text(s.multisig), value: "Multisig"), - PopupMenuItem( - child: Text(s.settings), value: "Settings"), - PopupMenuItem(child: Text(s.help), value: "Help"), - PopupMenuItem(child: Text(s.about), value: "About"), - // PopupMenuItem(child: Text(s.reset), value: "Reset"), - // PopupMenuItem(child: Text("Reorg"), value: "Reorg"), - ]; - }, - onSelected: _onMenu, - ); - }) - ], - ), - body: TabBarView(controller: _tabController, children: [ - SingleChildScrollView( - padding: EdgeInsets.all(20), - child: Center( - child: Column(children: [ - if (simpleMode) Text("Simple Mode"), - Observer(builder: (context) { - final share = accountManager.active.share; - return share != null ? Text("MULTISIG ${share.index}/${share.participants}") : SizedBox(); - }), - Observer(builder: (context) { - final _1 = eta.eta; - final _2 = syncStatus.syncedHeight; - final _3 = syncStatus.latestHeight; - return syncStatus.syncedHeight < 0 - ? Text(s.rescanNeeded) - : syncStatus.isSynced() - ? Text('${syncStatus.syncedHeight}', - style: theme.textTheme.caption) - : Text( - '${syncStatus.syncedHeight} / ${syncStatus.latestHeight} ${eta.eta}', - style: theme.textTheme.caption! - .apply(color: theme.primaryColor)); - }), - Padding(padding: EdgeInsets.symmetric(vertical: 8)), - Observer(builder: (context) { - final _ = accountManager.active.address; - final address = _address(); - final shortAddress = addressLeftTrim(address); - final showTAddr = accountManager.showTAddr; - final hide = settings.autoHide && _flat; - return Column(children: [ - if (hasTAddr) - Text(showTAddr - ? s.tapQrCodeForShieldedAddress - : s.tapQrCodeForTransparentAddress), - Padding(padding: EdgeInsets.symmetric(vertical: 4)), - GestureDetector( - onTap: hasTAddr ? _onQRTap : null, - child: RotatedBox( - quarterTurns: hide ? 2 : 0, - child: QrImage( - data: address, - size: qrSize, - embeddedImage: AssetImage('assets/icon.png'), - backgroundColor: Colors.white))), - Padding(padding: EdgeInsets.symmetric(vertical: 8)), - RichText( - text: TextSpan(children: [ - TextSpan( - text: '$shortAddress ', - style: theme.textTheme.bodyText2), - WidgetSpan( - child: GestureDetector( - child: Icon(Icons.content_copy), - onTap: _onAddressCopy)), - WidgetSpan( - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 4))), - WidgetSpan( - child: GestureDetector( - child: Icon(MdiIcons.qrcodeScan), - onTap: _onReceive)), - ])), - Padding(padding: EdgeInsets.symmetric(vertical: 4)), - if (!simpleMode && !showTAddr) - OutlinedButton( - child: Text(s.newSnapAddress), - style: OutlinedButton.styleFrom( - side: BorderSide( - width: 1, color: theme.primaryColor)), - onPressed: _onSnapAddress), - if (!simpleMode && showTAddr) - OutlinedButton( - child: Text(s.shieldTranspBalance), - style: OutlinedButton.styleFrom( - side: - BorderSide(width: 1, color: theme.primaryColor)), - onPressed: () { shieldTAddr(context); }, - ) - ]); - }), - Observer(builder: (context) { - final showTAddr = accountManager.showTAddr; - final balance = showTAddr - ? accountManager.tbalance - : accountManager.balance; - final hide = settings.autoHide && _flat; - final balanceColor = !showTAddr - ? theme.colorScheme.primaryVariant - : theme.colorScheme.secondaryVariant; - final balanceHi = hide ? '-------' : _getBalance_hi(balance); - final deviceWidth = getWidth(context); - final digits = deviceWidth.index < DeviceWidth.sm.index ? 7 : 9; - final balanceStyle = (balanceHi.length > digits - ? theme.textTheme.headline4 - : theme.textTheme.headline2)! - .copyWith(color: balanceColor); - return Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.baseline, - textBaseline: TextBaseline.ideographic, - children: [ - if (!hide) - Text('${coin.symbol}', - style: theme.textTheme.headline5), - Text(' $balanceHi', style: balanceStyle), - if (!hide) Text('${_getBalance_lo(balance)}'), - ]); - }), - Observer(builder: (context) { - final hide = settings.autoHide && _flat; - final balance = accountManager.showTAddr - ? accountManager.tbalance - : accountManager.balance; - final fx = _fx(); - final balanceFX = balance * fx / ZECUNIT; - return hide - ? Text(s.tiltYourDeviceUpToRevealYourBalance) - : Column(children: [ - if (fx != 0.0) - Text( - "${decimalFormat(balanceFX, 2, symbol: settings.currency)}", - style: theme.textTheme.headline6), - if (fx != 0.0) - Text( - "1 ${coin.ticker} = ${decimalFormat(fx, 2, symbol: settings.currency)}"), - ]); - }), - Padding(padding: EdgeInsets.symmetric(vertical: 8)), - Observer( - builder: (context) => (accountManager.unconfirmedBalance != 0) - ? Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.baseline, - textBaseline: TextBaseline.ideographic, - children: [ - Text( - '${_sign(accountManager.unconfirmedBalance)} ${_getBalance_hi(accountManager.unconfirmedBalance)}', - style: theme.textTheme.headline4 - ?.merge(_unconfirmedStyle())), - Text( - '${_getBalance_lo(accountManager.unconfirmedBalance)}', - style: _unconfirmedStyle()), - ]) - : Container()), - if (_progress > 0) - LinearProgressIndicator(value: _progress / 100.0), - ]))), - if (!simpleMode) NoteWidget(tabTo), - HistoryWidget(tabTo), - if (!simpleMode) BudgetWidget(), - if (!simpleMode) PnLWidget(), - ContactsTab(key: contactKey), - ]), - floatingActionButton: _accountTab - ? FloatingActionButton( - onPressed: _onSend, - backgroundColor: Theme.of(context).colorScheme.secondary, - child: Icon(Icons.send), - ) - : _contactsTab - ? FloatingActionButton( - onPressed: _onAddContact, - backgroundColor: theme.colorScheme.secondary, - child: Icon(Icons.add), - ) - : Container(), // This trailing comma makes auto-formatting nicer for build methods. - ); + return Column(children: [ + if (simpleMode) Text(s.simpleMode), + Observer(builder: (context) { + final time = eta.eta; + final syncedHeight = syncStatus.syncedHeight; + final latestHeight = syncStatus.latestHeight; + return syncedHeight == null + ? Text(s.rescanNeeded) + : syncStatus.isSynced() + ? Text('$syncedHeight', style: theme.textTheme.caption) + : Text('$syncedHeight / $latestHeight $time', + style: theme.textTheme.caption! + .apply(color: theme.primaryColor)); + }) + ]); } +} - void tabTo(int index) { - if (index != _tabController.index) _tabController.animateTo(index); - } +class QRAddressWidget extends StatefulWidget { + @override + QRAddressState createState() => QRAddressState(); +} - String _address() { - final address = accountManager.showTAddr - ? accountManager.taddress - : (_useSnapAddress - ? _uaAddress(_snapAddress, accountManager.taddress, settings.useUA) - : _uaAddress(accountManager.active.address, accountManager.taddress, - settings.useUA)); - return address; - } +class QRAddressState extends State { + bool _useSnapAddress = false; + String _snapAddress = ""; - String _uaAddress(String zaddress, String taddress, bool useUA) => - useUA ? WarpApi.getUA(zaddress, taddress) : zaddress; + @override + Widget build(BuildContext context) { + return Observer(builder: (context) { + final s = S.of(context); + final theme = Theme.of(context); + final simpleMode = settings.simpleMode; + final address = _address(); + final shortAddress = addressLeftTrim(address); + final showTAddr = active.showTAddr; + final hasTAddr = active.taddress.isNotEmpty; + final flat = settings.flat; + final qrSize = getScreenSize(context) / 2.5; + final hide = settings.autoHide && flat; + final coinDef = active.coinDef; - _sign(int b) { - return b < 0 ? '-' : '+'; + return Column(children: [ + if (hasTAddr) + Text(showTAddr + ? s.tapQrCodeForShieldedAddress + : s.tapQrCodeForTransparentAddress), + Padding(padding: EdgeInsets.symmetric(vertical: 4)), + GestureDetector( + onTap: hasTAddr ? _onQRTap : null, + child: RotatedBox( + quarterTurns: hide ? 2 : 0, + child: QrImage( + data: address, + size: qrSize, + embeddedImage: coinDef.image, + backgroundColor: Colors.white))), + Padding(padding: EdgeInsets.symmetric(vertical: 8)), + RichText( + text: TextSpan(children: [ + TextSpan(text: '$shortAddress ', style: theme.textTheme.bodyText2), + WidgetSpan( + child: GestureDetector( + child: Icon(Icons.content_copy), onTap: _onAddressCopy)), + WidgetSpan( + child: Padding(padding: EdgeInsets.symmetric(horizontal: 4))), + WidgetSpan( + child: GestureDetector( + child: Icon(MdiIcons.qrcodeScan), onTap: _onReceive)), + ])), + Padding(padding: EdgeInsets.symmetric(vertical: 4)), + if (!simpleMode && !showTAddr) + OutlinedButton( + child: Text(s.newSnapAddress), + style: OutlinedButton.styleFrom( + side: BorderSide(width: 1, color: theme.primaryColor)), + onPressed: _onSnapAddress), + if (!simpleMode && showTAddr) + OutlinedButton( + child: Text(s.shieldTranspBalance), + style: OutlinedButton.styleFrom( + side: BorderSide(width: 1, color: theme.primaryColor)), + onPressed: () { + shieldTAddr(context); + }, + ) + ]); + }); } _onQRTap() { - accountManager.toggleShowTAddr(); + active.toggleShowTAddr(); } _onAddressCopy() { @@ -384,62 +152,8 @@ class _AccountPageState extends State Navigator.of(context).pushNamed('/receive', arguments: _address()); } - _unconfirmedStyle() { - return TextStyle( - color: amountColor(context, accountManager.unconfirmedBalance)); - } - - _getBalance_hi(int b) { - return decimalFormat((b.abs() ~/ 100000) / 1000.0, 3); - } - - _getBalance_lo(b) { - return (b.abs() % 100000).toString().padLeft(5, '0'); - } - - _setupTimer() async { - await Future.delayed(Duration(seconds: 3)); - await _trySync(); - _timerSync = Timer.periodic(Duration(seconds: 15), (Timer t) { - _trySync(); - }); - } - - double _fx() { - return priceStore.zecPrice; - } - - _reorg() async { - final targetHeight = syncStatus.syncedHeight - 10; - WarpApi.rewindToHeight(targetHeight); - syncStatus.setSyncHeight(targetHeight); - await _trySync(); - } - - _trySync() async { - priceStore.fetchZecPrice(); - if (syncStatus.syncedHeight < 0) return; - await syncStatus.update(); - await accountManager.updateUnconfirmedBalance(); - if (!syncStatus.isSynced()) { - final res = - await WarpApi.tryWarpSync(settings.getTx, settings.anchorOffset); - if (res == 1) { - await _reorg(); - } else if (res == 0) { - syncStatus.update(); - } - } - await accountManager.fetchAccountData(false); - await accountManager.updateBalance(); - await accountManager.updateTBalance(); - await accountManager.updateUnconfirmedBalance(); - await contacts.fetchContacts(); - accountManager.autoshield(); - } - _onSnapAddress() { - final address = accountManager.newAddress(); + final address = active.newAddress(); setState(() { _useSnapAddress = true; _snapAddress = address; @@ -451,326 +165,123 @@ class _AccountPageState extends State }); } - _onSend() { - Navigator.of(this.context).pushNamed('/send'); + String _address() { + final address = active.showTAddr + ? active.taddress + : _useSnapAddress + ? _snapAddress + : active.account.address; + return address; } +} - _onMenu(String choice) { - switch (choice) { - case "Accounts": - Navigator.of(this.context).pushNamed('/accounts'); - break; - case "Backup": - _backup(); - break; - case "Rescan": - _rescan(); - break; - case "Cold": - _cold(); - break; - case "MultiPay": - _multiPay(); - break; - case "Broadcast": - _broadcast(); - break; - case "Multisig": - _multisig(); - break; - case "Settings": - _settings(); - break; - case "Help": - launch(DOC_URL); - break; - case "About": - showAbout(this.context); - break; - case "Reorg": - _reorg(); - break; - case "Reset": - _reset(); - break; - } - } +class BalanceWidget extends StatelessWidget { + @override + Widget build(BuildContext context) => Observer(builder: (context) { + final s = S.of(context); + final theme = Theme.of(context); + final flat = settings.flat; + final hide = settings.autoHide && flat; + final showTAddr = active.showTAddr; + final balance = showTAddr ? active.tbalance : active.balances.balance; + final balanceColor = !showTAddr + ? theme.colorScheme.primaryVariant + : theme.colorScheme.secondaryVariant; + final balanceHi = hide ? '-------' : _getBalanceHi(balance); + final deviceWidth = getWidth(context); + final digits = deviceWidth.index < DeviceWidth.sm.index ? 7 : 9; + final balanceStyle = (balanceHi.length > digits + ? theme.textTheme.headline4 + : theme.textTheme.headline2)! + .copyWith(color: balanceColor); - _backup() async { - final didAuthenticate = await authenticate(context, S.of(context).pleaseAuthenticateToShowAccountSeed); - if (didAuthenticate) { - Navigator.of(context).pushNamed('/backup'); - } - } + final fx = priceStore.coinPrice; + final balanceFX = balance * fx / ZECUNIT; + final coinDef = active.coinDef; - _rescan() async { - final approved = await rescanDialog(context); - if (approved) { - syncStatus.sync(context); - } - } + return Column(children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.baseline, + textBaseline: TextBaseline.ideographic, + children: [ + if (!hide) + Text('${coinDef.symbol}', style: theme.textTheme.headline5), + Text(' $balanceHi', style: balanceStyle), + if (!hide) Text('${_getBalanceLo(balance)}'), + ]), + if (hide) Text(s.tiltYourDeviceUpToRevealYourBalance), + if (!hide && fx != 0.0) + Text("${decimalFormat(balanceFX, 2, symbol: settings.currency)}", + style: theme.textTheme.headline6), + if (!hide && fx != 0.0) + Text( + "1 ${coinDef.ticker} = ${decimalFormat( + fx, 2, symbol: settings.currency)}"), + ]); + }); +} - _cold() { - showDialog( - context: context, - barrierDismissible: false, - builder: (context) => AlertDialog( - title: Text(S.of(context).coldStorage), - content: - Text(S.of(context).doYouWantToDeleteTheSecretKeyAndConvert), - actions: confirmButtons(context, _convertToWatchOnly, - okLabel: S.of(context).delete))); - } +class MemPoolWidget extends StatelessWidget { + @override + Widget build(BuildContext context) => Observer(builder: (context) { + final b = active.balances; + final theme = Theme.of(context); + final unconfirmedBalance = b.unconfirmedBalance; + if (unconfirmedBalance == 0) return Container(); + final unconfirmedStyle = TextStyle( + color: amountColor(context, unconfirmedBalance)); - _multiPay() { - Navigator.of(context).pushNamed('/multipay'); - } + return Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.baseline, + textBaseline: TextBaseline.ideographic, + children: [ + Text( + '${_sign(unconfirmedBalance)} ${_getBalanceHi(unconfirmedBalance)}', + style: theme.textTheme.headline4 + ?.merge(unconfirmedStyle)), + Text( + '${_getBalanceLo(unconfirmedBalance)}', + style: unconfirmedStyle), + ]); + }); +} - _broadcast() async { - final result = await FilePicker.platform.pickFiles(); +class ProgressWidget extends StatefulWidget { + @override + ProgressState createState() => ProgressState(); +} - if (result != null) { - final res = WarpApi.broadcast(result.files.single.path!); - final snackBar = SnackBar(content: Text(res)); - rootScaffoldMessengerKey.currentState?.showSnackBar(snackBar); - } - } +class ProgressState extends State { + int _progress = 0; + StreamSubscription? _progressDispose; - _multisig() { - Navigator.of(context).pushNamed('/multisig'); - } - - _convertToWatchOnly() { - accountManager.convertToWatchOnly(); - Navigator.of(context).pop(); - } - - _settings() { - Navigator.of(context).pushNamed('/settings'); - } - - _reset() { - Navigator.of(context).pushNamed('/reset'); - } - - _onAddContact() async { - final contact = await contactKey.currentState - ?.showContactForm(context, Contact.empty()); - if (contact != null) { - contacts.add(contact); - } - } - - _handleAccel(AccelerometerEvent event) { - final n = sqrt(event.x * event.x + event.y * event.y + event.z * event.z); - final inclination = acos(event.z / n) / pi * 180 * event.y.sign; - final flat = inclination < 20; - if (flat != _flat) + @override + void initState() { + super.initState(); + _progressDispose = progressStream.listen((percent) { setState(() { - _flat = flat; + _progress = percent; }); + }); } -} -class BudgetWidget extends StatefulWidget { @override - State createState() => BudgetState(); -} - -class BudgetState extends State - with AutomaticKeepAliveClientMixin { - @override - bool get wantKeepAlive => true; //Set to true + void dispose() { + _progressDispose?.cancel(); + super.dispose(); + } @override Widget build(BuildContext context) { - super.build(context); - return Padding( - padding: EdgeInsets.all(4), - child: Observer(builder: (context) { - final _ = accountManager.dataEpoch; - return Column( - children: [ - Card( - child: Column(children: [ - Text(S.of(context).largestSpendingsByAddress, - style: Theme.of(context).textTheme.headline6), - Padding(padding: EdgeInsets.symmetric(vertical: 4)), - BudgetChart(), - ])), - Expanded( - child: Card( - child: Column(children: [ - Text(S.of(context).accountBalanceHistory, - style: Theme.of(context).textTheme.headline6), - Padding(padding: EdgeInsets.symmetric(vertical: 4)), - Expanded(child: Padding(padding: EdgeInsets.only(right: 20), - child: LineChartTimeSeries.fromTimeSeries(accountManager.accountBalances))) - ]))), - ], - ); - })); + if (_progress == 0) return Container(); + return LinearProgressIndicator(value: _progress / 100.0); } } -class PnLWidget extends StatefulWidget { - @override - State createState() => PnLState(); -} +_getBalanceHi(int b) => decimalFormat((b.abs() ~/ 100000) / 1000.0, 3); -class PnLState extends State with AutomaticKeepAliveClientMixin { - @override - bool get wantKeepAlive => true; //Set to true +_getBalanceLo(int b) => (b.abs() % 100000).toString().padLeft(5, '0'); - @override - Widget build(BuildContext context) { - return IconTheme.merge( - data: IconThemeData(opacity: 0.54), - child: - Column(children: [ - Row(children: [ - Expanded(child: - FormBuilderRadioGroup( - orientation: OptionsOrientation.horizontal, - name: S.of(context).pnl, - initialValue: accountManager.pnlSeriesIndex, - onChanged: (int? v) { - setState(() { - accountManager.setPnlSeriesIndex(v!); - }); - }, - options: [ - FormBuilderFieldOption(child: Text(S.of(context).price), value: 0), - FormBuilderFieldOption( - child: Text(S.of(context).realized), value: 1), - FormBuilderFieldOption(child: Text(S.of(context).mm), value: 2), - FormBuilderFieldOption(child: Text(S.of(context).total), value: 3), - FormBuilderFieldOption(child: Text(S.of(context).qty), value: 4), - FormBuilderFieldOption(child: Text(S.of(context).table), value: 5), - ])), - IconButton(onPressed: _onExport, icon: Icon(Icons.save)), - ]), - Observer(builder: (context) { - final _ = accountManager.pnlSorted; - return Expanded( - child: Padding( - padding: EdgeInsets.only(right: 20), - child: accountManager.pnlSeriesIndex != 5 - ? PnLChart( - accountManager.pnls, accountManager.pnlSeriesIndex) - : PnLTable())); - }) - ])); - } - - _onExport() async { - final csvData = accountManager.pnlSorted.map((pnl) => [ - pnl.timestamp, - pnl.amount, - pnl.price, - pnl.realized, - pnl.unrealized, - pnl.realized + pnl.unrealized]).toList(); - await shareCsv(csvData, 'pnl_history.csv', S.of(context).pnlHistory); - } -} - -class PnLChart extends StatelessWidget { - final List pnls; - final int seriesIndex; - - PnLChart(this.pnls, this.seriesIndex); - - @override - Widget build(BuildContext context) { - final series = _createSeries(pnls, seriesIndex, context); - return LineChartTimeSeries.fromTimeSeries(series); - } - - static double _seriesData(PnL pnl, int index) { - switch (index) { - case 0: - return pnl.price; - case 1: - return pnl.realized; - case 2: - return pnl.unrealized; - case 3: - return pnl.realized + pnl.unrealized; - case 4: - return pnl.amount; - } - return 0.0; - } - - static List> _createSeries( - List data, int index, BuildContext context) { - return data - .map((pnl) => TimeSeriesPoint( - pnl.timestamp.millisecondsSinceEpoch ~/ DAY_MS, - _seriesData(pnl, index))) - .toList(); - } -} - -class PnLTable extends StatelessWidget { - @override - Widget build(BuildContext context) { - final sortSymbol = accountManager.pnlDesc ? ' \u2193' : ' \u2191'; - return SingleChildScrollView( - child: Observer( - builder: (context) => PaginatedDataTable( - columns: [ - DataColumn( - label: Text(S.of(context).date + sortSymbol), - onSort: (_, __) { - accountManager.togglePnlDesc(); - }), - DataColumn(label: Text(S.of(context).qty), numeric: true), - DataColumn(label: Text(S.of(context).price), numeric: true), - DataColumn( - label: Text(S.of(context).realized), numeric: true), - DataColumn(label: Text(S.of(context).mm), numeric: true), - DataColumn(label: Text(S.of(context).total), numeric: true), - ], - columnSpacing: 16, - showCheckboxColumn: false, - availableRowsPerPage: [5, 10, 25, 100], - onRowsPerPageChanged: (int? value) { - settings.setRowsPerPage(value ?? 25); - }, - showFirstLastButtons: true, - rowsPerPage: settings.rowsPerPage, - source: PnLDataSource(context)))); - } -} - -class PnLDataSource extends DataTableSource { - BuildContext context; - final dateFormat = DateFormat("MM-dd"); - - PnLDataSource(this.context); - - @override - DataRow getRow(int index) { - final pnl = accountManager.pnlSorted[index]; - final ts = dateFormat.format(pnl.timestamp); - return DataRow(cells: [ - DataCell(Text("$ts")), - DataCell(Text(decimalFormat(pnl.amount, 2))), - DataCell(Text(decimalFormat(pnl.price, 3))), - DataCell(Text(decimalFormat(pnl.realized, 3))), - DataCell(Text(decimalFormat(pnl.unrealized, 3))), - DataCell(Text(decimalFormat(pnl.realized + pnl.unrealized, 3))), - ]); - } - - @override - bool get isRowCountApproximate => false; - - @override - int get rowCount => accountManager.pnls.length; - - @override - int get selectedRowCount => 0; -} +_sign(int b) => b < 0 ? '-' : '+'; diff --git a/lib/account_manager.dart b/lib/account_manager.dart index 676e229..92c41f4 100644 --- a/lib/account_manager.dart +++ b/lib/account_manager.dart @@ -1,10 +1,13 @@ +import 'package:ZYWallet/accounts.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:warp/main.dart'; -import 'package:warp/store.dart'; +import 'package:flutter_speed_dial/flutter_speed_dial.dart'; +import 'package:warp_api/warp_api.dart'; +import 'backup.dart'; +import 'main.dart'; +import 'store.dart'; import 'generated/l10n.dart'; - import 'about.dart'; class AccountManagerPage extends StatefulWidget { @@ -14,23 +17,20 @@ class AccountManagerPage extends StatefulWidget { class AccountManagerState extends State { var _accountNameController = TextEditingController(); - var _tbalances = Map(); @override initState() { super.initState(); Future.microtask(() async { - await accountManager.refresh(); - final tbalances = await accountManager.getAllTBalances(); - setState(() { - _tbalances = tbalances; - }); + await accounts.refresh(); + await accounts.updateTBalance(); }); showAboutOnce(this.context); } @override Widget build(BuildContext context) { + final theme = Theme.of(context); return Scaffold( appBar: AppBar(title: Text(S.of(context).selectAccount), actions: [ PopupMenuButton( @@ -43,43 +43,73 @@ class AccountManagerState extends State { onSelected: _onMenu) ]), body: Padding(padding: EdgeInsets.all(8), child: Observer( - builder: (context) => - accountManager.accounts.isEmpty - ? Center(child: NoAccount()) - : ListView.builder( - itemCount: accountManager.accounts.length, - itemBuilder: (BuildContext context, int index) { - final a = accountManager.accounts[index]; - final zbal = a.balance / ZECUNIT; - final tbal = (_tbalances[a.id] ?? 0) / ZECUNIT; - final balance = zbal + tbal; - return Card( - child: Dismissible( - key: Key(a.name), - child: ListTile( - title: Text(a.name, - style: Theme.of(context).textTheme.headline5), - subtitle: Text("${decimalFormat(zbal, 3)} + ${_tbalances[a.id] != null ? decimalFormat(tbal, 3) : '?'}"), - trailing: Text(decimalFormat(balance, 3)), - onTap: () { - _selectAccount(a); - }, - onLongPress: () { - _editAccount(a); - }, - ), - confirmDismiss: (d) => _onAccountDelete(a), - onDismissed: (d) => - _onDismissed(index, a), - )); - }), + builder: (context) { + final _1 = accounts.epoch; + return accounts.list.isEmpty + ? Center(child: NoAccount()) + : ListView.builder( + itemCount: accounts.list.length, + itemBuilder: (BuildContext context, int index) { + final a = accounts.list[index]; + final weight = settings.coins[a.coin].active == a.id ? FontWeight.bold : FontWeight.normal; + final zbal = a.balance / ZECUNIT; + final tbal = a.tbalance / ZECUNIT; + final balance = zbal + tbal; + return Card( + child: Dismissible( + key: Key(a.name), + child: ListTile( + leading: CircleAvatar(backgroundImage: settings.coins[a.coin].def.image), + title: Text(a.name, + style: theme.textTheme.headline5 + ?.merge(TextStyle(fontWeight: weight)) + .apply(color: a.coin == 0 ? theme.colorScheme.primary : theme.colorScheme.secondary, + )), + subtitle: Text("${decimalFormat(zbal, 3)} + ${decimalFormat(tbal, 3)}"), + trailing: Text(decimalFormat(balance, 3)), + onTap: () { + _selectAccount(a); + }, + onLongPress: () { + _editAccount(a); + }, + ), + confirmDismiss: (d) => _onAccountDelete(a), + onDismissed: (d) => + _onDismissed(index, a), + )); + });}, )), - floatingActionButton: GestureDetector(onLongPress: _onFullRestore, child: FloatingActionButton( - onPressed: _onRestore, child: Icon(Icons.add)))); + floatingActionButton: SpeedDial( + icon: Icons.add, + onPress: _onRestore, + children: [ + SpeedDialChild( + child: Icon(Icons.download), + backgroundColor: Colors.red, + foregroundColor: Colors.white, + label: 'Restore Batch', + onTap: _onFullRestore, + ), + SpeedDialChild( + child: Icon(Icons.upload), + backgroundColor: Colors.blue, + foregroundColor: Colors.white, + label: 'Save Batch', + onTap: _onFullBackup, + ), + SpeedDialChild( + child: Icon(Icons.subdirectory_arrow_right), + backgroundColor: Colors.green, + foregroundColor: Colors.white, + label: 'New Sub-account', + onTap: _onNewSubaccount, + ), + ] + )); } Future _onAccountDelete(Account account) async { - if (accountManager.accounts.length == 1) return false; final confirm1 = await showDialog( context: context, barrierDismissible: false, @@ -94,7 +124,7 @@ class AccountManagerState extends State { if (!confirm2) return false; final zbal = account.balance; - final tbal = _tbalances[account.id] ?? 0; + final tbal = account.tbalance; if (zbal + tbal > 0) { final confirm3 = await showDialog( context: context, @@ -112,27 +142,21 @@ class AccountManagerState extends State { } void _onDismissed(int index, Account account) async { - await accountManager.delete(account.id); - accountManager.refresh(); + await accounts.delete(account.coin, account.id); + accounts.refresh(); } _selectAccount(Account account) async { - await accountManager.setActiveAccount(account); + await active.setActiveAccount(account.coin, account.id); if (syncStatus.accountRestored) { syncStatus.setAccountRestored(false); final approved = await rescanDialog(context); if (approved) - syncStatus.sync(context); - } - else if (syncStatus.syncedHeight < 0) { - syncStatus.setSyncedToLatestHeight(); + syncStatus.rescan(context); } final navigator = Navigator.of(context); - if (navigator.canPop()) - navigator.pop(); - else - navigator.pushReplacementNamed('/account'); + navigator.pushNamedAndRemoveUntil('/account', (route) => false); } _editAccount(Account account) async { @@ -146,7 +170,7 @@ class AccountManagerState extends State { } _changeAccountName(Account account) { - accountManager.changeAccountName(account, _accountNameController.text); + accounts.changeAccountName(account.coin, account.id, _accountNameController.text); Navigator.of(context).pop(); } @@ -169,6 +193,32 @@ class AccountManagerState extends State { Navigator.of(this.context).pushNamed('/settings'); } + _onNewSubaccount() async { + final s = S.of(context); + if (active.id == 0) { + showSnackBar(s.noActiveAccount); + return; + } + final newName = s.subAccountOf(active.account.name); + _accountNameController.text = newName; + final confirmed = await showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text(s.newSubAccount), + content: TextField(controller: _accountNameController), + actions: confirmButtons(context, () { + Navigator.of(context).pop(true); + }))); + if (confirmed == true) { + WarpApi.newSubAccount(active.coin, active.id, _accountNameController.text); + await accounts.refresh(); + } + } + + _onFullBackup() { + Navigator.of(context).pushNamed('/fullBackup'); + } + _onFullRestore() { Navigator.of(this.context).pushNamed('/fullRestore'); } diff --git a/lib/accounts.dart b/lib/accounts.dart new file mode 100644 index 0000000..045b766 --- /dev/null +++ b/lib/accounts.dart @@ -0,0 +1,345 @@ +import 'package:shared_preferences/shared_preferences.dart'; +import 'coin/coins.dart'; +import 'package:mobx/mobx.dart'; +import 'db.dart'; +import 'package:warp_api/warp_api.dart'; + +import 'backup.dart'; +import 'coin/coin.dart'; +import 'coin/zcash.dart'; +import 'main.dart'; +import 'store.dart'; + +part 'accounts.g.dart'; + +class Account { + final int coin; + final int id; + final String name; + final String address; + final int balance; + int tbalance = 0; + final ShareInfo? share; + + Account(this.coin, this.id, this.name, this.address, this.balance, this.tbalance, this.share); +} + +final Account emptyAccount = Account(0, 0, "", "", 0, 0, null); + +class AccountManager2 = _AccountManager2 with _$AccountManager2; + +abstract class _AccountManager2 with Store { + @observable int epoch = 0; + List list = []; + + @action + Future refresh() async { + List _list = []; + _list.addAll(await _getList(0)); + _list.addAll(await _getList(1)); + list = _list; + epoch += 1; + } + + @action + Future updateTBalance() async { + for (var a in list) { + final tbalance = await WarpApi.getTBalanceAsync(a.coin, a.id); + a.tbalance = tbalance; + } + epoch += 1; + } + + @action + Future delete(int coin, int id) async { + WarpApi.deleteAccount(coin, id); + if (active.coin == coin && active.id == id) + active.reset(); + } + + @action + Future changeAccountName(int coin, int id, String name) async { + final c = settings.coins[coin].def; // TODO: Do in backend would be cleaner + final db = c.db; + await db.execute("UPDATE accounts SET name = ?2 WHERE id_account = ?1", + [id, name]); + await refresh(); + } + + void saveActive(int coin, int id) { + settings.coins[coin].active = id; + Future.microtask(() async { + final prefs = await SharedPreferences.getInstance(); + final def = settings.coins[coin].def; + prefs.setInt("${def.ticker}.active", id); + }); + } + + Account get(int coin, int id) => list.firstWhere((e) => e.coin == coin && e.id == id, orElse: () => emptyAccount); + + static Future> _getList(int coin) async { + final c = settings.coins[coin].def; + final db = c.db; + List accounts = []; + + final List res = await db.rawQuery( + "WITH notes AS (SELECT a.id_account, a.name, a.address, CASE WHEN r.spent IS NULL THEN r.value ELSE 0 END AS nv FROM accounts a LEFT JOIN received_notes r ON a.id_account = r.account)," + "accounts2 AS (SELECT id_account, name, address, COALESCE(sum(nv), 0) AS balance FROM notes GROUP by id_account) " + "SELECT a.id_account, a.name, a.address, a.balance FROM accounts2 a", + []); + for (var r in res) { + final int id = r['id_account']; + // final shareInfo = r['secret'] != null + // ? ShareInfo( + // r['idx'], r['threshold'], r['participants'], r['secret']) + // : null; // TODO: Multisig + final account = Account(coin, + r['id_account'], r['name'], r['address'], r['balance'], 0, null); + accounts.add(account); + } + return accounts; + } +} + +class ActiveAccount = _ActiveAccount with _$ActiveAccount; + +abstract class _ActiveAccount with Store { + @observable + int dataEpoch = 0; + + int coin = 0; + int id = 0; + + Account account = emptyAccount; + CoinBase coinDef = ZcashCoin(); + bool canPay = false; + @observable Balances balances = Balances.zero; + String taddress = ""; + int tbalance = 0; + + @observable List notes = []; + @observable List txs = []; + @observable List spendings = []; + @observable List> accountBalances = []; + @observable List pnls = []; + + @observable + bool showTAddr = false; + + @observable + SortConfig noteSortConfig = SortConfig("", SortOrder.Unsorted); + + @observable + SortConfig txSortConfig = SortConfig("", SortOrder.Unsorted); + + @observable + int pnlSeriesIndex = 0; + + @observable + bool pnlDesc = false; + + @action + Future restore() async { + final prefs = await SharedPreferences.getInstance(); + coin = prefs.getInt('coin') ?? 0; + id = prefs.getInt('account') ?? 0; + setActiveAccount(coin, id); + } + + void reset() { + setActiveAccount(0, 0); + } + + @action + Future setActiveAccount(int _coin, int _id) async { + coin = _coin; + id = _id; + accounts.saveActive(coin, id); + + final prefs = await SharedPreferences.getInstance(); + prefs.setInt('coin', coin); + prefs.setInt('account', id); + + coinDef = settings.coins[coin].def; + final db = coinDef.db; + + account = accounts.get(coin, id); + + if (id > 0) { + final List res1 = await db.rawQuery( + "SELECT address FROM taddrs WHERE account = ?1", [id]); + taddress = res1.isNotEmpty ? res1[0]['address'] : ""; + + final List res2 = await db.rawQuery( + "SELECT sk FROM accounts WHERE id_account = ?1", [id]); + canPay = res2.isNotEmpty && res2[0]['sk'] != null; + } + + showTAddr = false; + balances = Balances.zero; + WarpApi.setMempoolAccount(coin, id); + + await update(); + await priceStore.updateChart(); + } + + @action + void toggleShowTAddr() { + showTAddr = !showTAddr; + } + + @action + void updateTBalance() { + tbalance = WarpApi.getTBalance(coin, id); + } + + @action + Future updateBalances() async { + final dbr = DbReader(coin, id); + balances = await dbr.getBalance(syncStatus.confirmHeight); + } + + @action + Future update() async { + await updateBalances(); + updateTBalance(); + final dbr = DbReader(coin, id); + notes = await dbr.getNotes(); + txs = await dbr.getTxs(); + dataEpoch += 1; + } + + String newAddress() { + return WarpApi.newAddress(coin, id); + } + + @computed + List get sortedNotes { + var notes2 = [...notes]; + switch (noteSortConfig.field) { + case "time": + return _sort(notes2, (Note note) => note.height, noteSortConfig.order); + case "amount": + return _sort(notes2, (Note note) => note.value, noteSortConfig.order); + } + return notes2; + } + + @computed + List get sortedTxs { + var txs2 = [...txs]; + switch (txSortConfig.field) { + case "time": + return _sort(txs2, (Tx tx) => tx.height, txSortConfig.order); + case "amount": + return _sort(txs2, (Tx tx) => tx.value, txSortConfig.order); + case "txid": + return _sort(txs2, (Tx tx) => tx.txid, txSortConfig.order); + case "address": + return _sort( + txs2, (Tx tx) => tx.contact ?? tx.address, txSortConfig.order); + case "memo": + return _sort(txs2, (Tx tx) => tx.memo, txSortConfig.order); + } + return txs2; + } + + @action + void sortNotes(String field) { + noteSortConfig = noteSortConfig.sortOn(field); + } + + @action + void sortTx(String field) { + txSortConfig = txSortConfig.sortOn(field); + } + + List _sort( + List items, T Function(C) project, SortOrder order) { + switch (order) { + case SortOrder.Ascending: + items.sort((a, b) => project(a).compareTo(project(b))); + break; + case SortOrder.Descending: + items.sort((a, b) => -project(a).compareTo(project(b))); + break; + case SortOrder.Unsorted: + items.sort((a, b) => -a.height.compareTo(b.height)); + break; + } + return items; + } + + @action + void setPnlSeriesIndex(int index) { + pnlSeriesIndex = index; + } + + @computed + List get pnlSorted { + if (pnlDesc) { + var _pnls = [...pnls.reversed]; + return _pnls; + } + return pnls; + } + + @action + void togglePnlDesc() { + pnlDesc = !pnlDesc; + } + + @action + Future excludeNote(Note note) async { + await coinDef.db.execute( + "UPDATE received_notes SET excluded = ?2 WHERE id_note = ?1", + [note.id, note.excluded]); + } + + @action + Future invertExcludedNotes() async { + await coinDef.db.execute( + "UPDATE received_notes SET excluded = NOT(COALESCE(excluded, 0)) WHERE account = ?1", + [active.id]); + notes = notes.map((n) => n.invertExcluded).toList(); + } + + @action + Future fetchChartData() async { + final dbr = DbReader(active.coin, active.id); + pnls = await dbr.getPNL(active.id); + spendings = await dbr.getSpending(active.id); + accountBalances = await dbr.getAccountBalanceTimeSeries(active.id, active.balances.balance); + } + + @action + Future convertToWatchOnly() async { + await coinDef.db.rawUpdate( + "UPDATE accounts SET seed = NULL, sk = NULL WHERE id_account = ?1", + [active.id]); + canPay = false; + } +} + +Future getBackup(AccountId account) async { + final c = settings.coins[account.coin].def; + final db = c.db; + final List res = await db.rawQuery( + "SELECT name, seed, aindex, sk, ivk FROM accounts WHERE id_account = ?1", + [account.id]); + if (res.isEmpty) throw Exception("Account N/A"); + // final share = await getShareInfo(account); // Multisig + final row = res[0]; + final name = row['name']; + final seed = row['seed']; + final index = row['aindex']; + final sk = row['sk']; + final ivk = row['ivk']; + int type = 0; + if (seed != null) + type = 0; + else if (sk != null) + type = 1; + else if (ivk != null) type = 2; + return Backup(type, name, seed, index, sk, ivk, null); +} diff --git a/lib/backup.dart b/lib/backup.dart index afec958..c44bb79 100644 --- a/lib/backup.dart +++ b/lib/backup.dart @@ -3,9 +3,16 @@ import 'package:flutter/material.dart'; import 'main.dart'; import 'store.dart'; import 'generated/l10n.dart'; +import 'accounts.dart' show getBackup; + +class AccountId { + final int coin; + final int id; + AccountId(this.coin, this.id); +} class BackupPage extends StatefulWidget { - final int? accountId; + final AccountId? accountId; BackupPage(this.accountId); @@ -21,8 +28,8 @@ class BackupState extends State { final _shareController = TextEditingController(); Future _init() async { - backup = await accountManager.getBackup(widget.accountId ?? accountManager.active.id); - _backupController.text = backup.value(); + backup = await getBackup(widget.accountId ?? AccountId(active.coin, active.id)); + _backupController.text = backupData; _skController.text = backup.sk ?? ""; _ivkController.text = backup.ivk; final share = backup.share; @@ -30,6 +37,8 @@ class BackupState extends State { return true; } + String get backupData => backup.value() + (backup.index != 0 ? " (Index: ${backup.index})" : ""); + @override Widget build(BuildContext context) { return Scaffold(appBar: AppBar(title: Text(S.of(context).backup)), body: @@ -51,8 +60,8 @@ class BackupState extends State { children: [ TextField( decoration: InputDecoration( - labelText: S.of(context).backupDataRequiredForRestore, prefixIcon: IconButton(icon: Icon(Icons.save), - onPressed: () => _showQR(backup.value(), "$name - backup"))), + labelText: s.backupDataRequiredForRestore(name), prefixIcon: IconButton(icon: Icon(Icons.save), + onPressed: () => _showQR(backupData, "$name - backup"))), controller: _backupController, minLines: 3, maxLines: 10, @@ -88,17 +97,12 @@ class BackupState extends State { ), Padding(padding: EdgeInsets.symmetric(vertical: 4)), Text(s.tapAnIconToShowTheQrCode), - Container(margin: EdgeInsets.all(8), padding: EdgeInsets.all(8), decoration: BoxDecoration(border: Border.all(width: 2, color: theme.primaryColor), borderRadius: BorderRadius.circular(4)),child: - GestureDetector(onLongPress: _onFullBackup, child: Text(s.backupWarning, - style: theme.textTheme.subtitle1!.copyWith(color: theme.primaryColor)))), + Container(margin: EdgeInsets.all(8), padding: EdgeInsets.all(8), decoration: BoxDecoration(border: Border.all(width: 2, color: theme.primaryColor), borderRadius: BorderRadius.circular(4)), + child: Text(s.backupWarning, style: theme.textTheme.subtitle1!.copyWith(color: theme.primaryColor))), ] ), )); } _showQR(String text, String title) => showQR(context, text, title); - - _onFullBackup() { - Navigator.of(context).pushNamed('/fullBackup'); - } } diff --git a/lib/budget.dart b/lib/budget.dart index 3f5f59c..4c124e4 100644 --- a/lib/budget.dart +++ b/lib/budget.dart @@ -3,27 +3,234 @@ import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_palette/flutter_palette.dart'; +import 'package:intl/intl.dart'; +import 'package:warp_api/warp_api.dart'; +import 'chart.dart'; import 'store.dart'; import 'main.dart'; import 'generated/l10n.dart'; -class BudgetChart extends StatefulWidget { +class BudgetWidget extends StatefulWidget { @override - BudgetState createState() => BudgetState(); + State createState() => BudgetState(); } -class BudgetState extends State { +class BudgetState extends State + with AutomaticKeepAliveClientMixin { + @override + bool get wantKeepAlive => true; //Set to true + + @override + Widget build(BuildContext context) { + super.build(context); + return Padding( + padding: EdgeInsets.all(4), + child: Observer(builder: (context) { + final _ = active.dataEpoch; + return Column( + children: [ + Card( + child: Column(children: [ + Text(S.of(context).largestSpendingsByAddress, + style: Theme.of(context).textTheme.headline6), + Padding(padding: EdgeInsets.symmetric(vertical: 4)), + BudgetChart(), + ])), + Expanded( + child: Card( + child: Column(children: [ + Text(S.of(context).accountBalanceHistory, + style: Theme.of(context).textTheme.headline6), + Padding(padding: EdgeInsets.symmetric(vertical: 4)), + Expanded(child: Padding(padding: EdgeInsets.only(right: 20), + child: LineChartTimeSeries.fromTimeSeries(active.accountBalances))) + ]))), + ], + ); + })); + } +} + +class PnLWidget extends StatefulWidget { + @override + State createState() => PnLState(); +} + +class PnLState extends State with AutomaticKeepAliveClientMixin { + @override + bool get wantKeepAlive => true; //Set to true + + @override + Widget build(BuildContext context) { + return IconTheme.merge( + data: IconThemeData(opacity: 0.54), + child: + Column(children: [ + Row(children: [ + Expanded(child: + FormBuilderRadioGroup( + orientation: OptionsOrientation.horizontal, + name: S.of(context).pnl, + initialValue: active.pnlSeriesIndex, + onChanged: (int? v) { + setState(() { + active.setPnlSeriesIndex(v!); + }); + }, + options: [ + FormBuilderFieldOption(child: Text(S.of(context).price), value: 0), + FormBuilderFieldOption( + child: Text(S.of(context).realized), value: 1), + FormBuilderFieldOption(child: Text(S.of(context).mm), value: 2), + FormBuilderFieldOption(child: Text(S.of(context).total), value: 3), + FormBuilderFieldOption(child: Text(S.of(context).qty), value: 4), + FormBuilderFieldOption(child: Text(S.of(context).table), value: 5), + ])), + IconButton(onPressed: _onExport, icon: Icon(Icons.save)), + ]), + Observer(builder: (context) { + final _ = active.pnlSorted; + return Expanded( + child: Padding( + padding: EdgeInsets.only(right: 20), + child: active.pnlSeriesIndex != 5 + ? PnLChart( + active.pnls, active.pnlSeriesIndex) + : PnLTable())); + }) + ])); + } + + _onExport() async { + final csvData = active.pnlSorted.map((pnl) => [ + pnl.timestamp, + pnl.amount, + pnl.price, + pnl.realized, + pnl.unrealized, + pnl.realized + pnl.unrealized]).toList(); + await shareCsv(csvData, 'pnl_history.csv', S.of(context).pnlHistory); + } +} + +class PnLChart extends StatelessWidget { + final List pnls; + final int seriesIndex; + + PnLChart(this.pnls, this.seriesIndex); + + @override + Widget build(BuildContext context) { + final series = _createSeries(pnls, seriesIndex, context); + return LineChartTimeSeries.fromTimeSeries(series); + } + + static double _seriesData(PnL pnl, int index) { + switch (index) { + case 0: + return pnl.price; + case 1: + return pnl.realized; + case 2: + return pnl.unrealized; + case 3: + return pnl.realized + pnl.unrealized; + case 4: + return pnl.amount; + } + return 0.0; + } + + static List> _createSeries( + List data, int index, BuildContext context) { + return data + .map((pnl) => TimeSeriesPoint( + pnl.timestamp.millisecondsSinceEpoch ~/ DAY_MS, + _seriesData(pnl, index))) + .toList(); + } +} + +class PnLTable extends StatelessWidget { + @override + Widget build(BuildContext context) { + final sortSymbol = active.pnlDesc ? ' \u2193' : ' \u2191'; + return SingleChildScrollView( + child: Observer( + builder: (context) => PaginatedDataTable( + columns: [ + DataColumn( + label: Text(S.of(context).date + sortSymbol), + onSort: (_, __) { + active.togglePnlDesc(); + }), + DataColumn(label: Text(S.of(context).qty), numeric: true), + DataColumn(label: Text(S.of(context).price), numeric: true), + DataColumn( + label: Text(S.of(context).realized), numeric: true), + DataColumn(label: Text(S.of(context).mm), numeric: true), + DataColumn(label: Text(S.of(context).total), numeric: true), + ], + columnSpacing: 16, + showCheckboxColumn: false, + availableRowsPerPage: [5, 10, 25, 100], + onRowsPerPageChanged: (int? value) { + settings.setRowsPerPage(value ?? 25); + }, + showFirstLastButtons: true, + rowsPerPage: settings.rowsPerPage, + source: PnLDataSource(context)))); + } +} + +class PnLDataSource extends DataTableSource { + BuildContext context; + final dateFormat = DateFormat("MM-dd"); + + PnLDataSource(this.context); + + @override + DataRow getRow(int index) { + final pnl = active.pnlSorted[index]; + final ts = dateFormat.format(pnl.timestamp); + return DataRow(cells: [ + DataCell(Text("$ts")), + DataCell(Text(decimalFormat(pnl.amount, 2))), + DataCell(Text(decimalFormat(pnl.price, 3))), + DataCell(Text(decimalFormat(pnl.realized, 3))), + DataCell(Text(decimalFormat(pnl.unrealized, 3))), + DataCell(Text(decimalFormat(pnl.realized + pnl.unrealized, 3))), + ]); + } + + @override + bool get isRowCountApproximate => false; + + @override + int get rowCount => active.pnls.length; + + @override + int get selectedRowCount => 0; +} + +class BudgetChart extends StatefulWidget { + @override + BudgetChartState createState() => BudgetChartState(); +} + +class BudgetChartState extends State { @override Widget build(BuildContext context) { return Observer( builder: (context) => Padding( padding: EdgeInsets.symmetric(horizontal: 8), child: Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - BudgetBar(accountManager.spendings), - BudgetTable(accountManager.spendings) + BudgetBar(active.spendings), + BudgetTable(active.spendings) ]))); } } diff --git a/lib/coin/coin.dart b/lib/coin/coin.dart index 9491549..428fd34 100644 --- a/lib/coin/coin.dart +++ b/lib/coin/coin.dart @@ -1,6 +1,30 @@ +import 'package:flutter/material.dart'; +import 'package:path/path.dart'; +import 'package:sqflite/sqflite.dart'; + class LWInstance { String name; String url; LWInstance(this.name, this.url); } + +abstract class CoinBase { + String get app; + String get symbol; + String get currency; + String get ticker; + String get explorerUrl; + AssetImage get image; + String get dbName; + late Database db; + List get lwd; + bool get supportsUA; + bool get supportsMultisig; + List get weights; + + Future open(String dbPath) async { + final path = join(dbPath, dbName); + db = await openDatabase(path); + } +} diff --git a/lib/coin/coins.dart b/lib/coin/coins.dart new file mode 100644 index 0000000..cff3659 --- /dev/null +++ b/lib/coin/coins.dart @@ -0,0 +1,7 @@ +import 'coin.dart'; +import 'ycash.dart'; +import 'zcash.dart'; + +CoinBase ycash = YcashCoin(); +CoinBase zcash = ZcashCoin(); + diff --git a/lib/coin/ycash.dart b/lib/coin/ycash.dart index 75e5f04..88519ef 100644 --- a/lib/coin/ycash.dart +++ b/lib/coin/ycash.dart @@ -1,15 +1,19 @@ +import 'package:flutter/material.dart'; + import "coin.dart"; -class Coin { +class YcashCoin extends CoinBase { String app = "YWallet"; String symbol = "\u24E8"; String currency = "ycash"; String ticker = "YEC"; + String dbName = "yec.db"; String explorerUrl = "https://yecblockexplorer.com/tx/"; + AssetImage image = AssetImage('assets/ycash.png'); List lwd = [ LWInstance("Lightwalletd", "https://lite.ycash.xyz:9067"), ]; bool supportsUA = false; bool supportsMultisig = true; - List weights = [5, 25, 250]; + List weights = [5, 25, 250]; } diff --git a/lib/coin/zcash.dart b/lib/coin/zcash.dart index b5fd5f6..430c34a 100644 --- a/lib/coin/zcash.dart +++ b/lib/coin/zcash.dart @@ -1,11 +1,15 @@ +import 'package:flutter/material.dart'; + import 'coin.dart'; -class Coin { +class ZcashCoin extends CoinBase { String app = "ZWallet"; String symbol = "\u24E9"; String currency = "zcash"; String ticker = "ZEC"; + String dbName = "zec.db"; String explorerUrl = "https://explorer.zcha.in/transactions/"; + AssetImage image = AssetImage('assets/zcash.png'); List lwd = [ LWInstance("Lightwalletd", "https://mainnet.lightwalletd.com:9067"), LWInstance("Zecwallet", "https://lwdv3.zecwallet.co"), diff --git a/lib/contact.dart b/lib/contact.dart index 9050026..8c6a04b 100644 --- a/lib/contact.dart +++ b/lib/contact.dart @@ -6,11 +6,11 @@ import 'package:warp_api/warp_api.dart'; import 'main.dart'; import 'generated/l10n.dart'; +import 'settings.dart'; import 'store.dart'; class ContactsTab extends StatefulWidget { - ContactsTab({Key? key}) : super(key: key); - + ContactsTab({key: Key}): super(key: key); @override State createState() => ContactsState(); } @@ -22,32 +22,32 @@ class ContactsState extends State { Padding(padding: EdgeInsets.all(8), child: contacts.contacts.isEmpty ? NoContact() : Column(children: [ - if (contacts.dirty) OutlinedButton(onPressed: accountManager.canPay ? _onCommit : null, child: Text(S.of(context).saveToBlockchain), style: OutlinedButton.styleFrom( - side: BorderSide( - width: 1, color: Theme.of(context).primaryColor))), - Expanded(child: ListView.builder( - itemCount: contacts.contacts.length, - itemBuilder: (BuildContext context, int index) { - final c = contacts.contacts[index]; - return Card( - child: Dismissible( - key: Key("${c.id}"), - child: ListTile( - title: Text(c.name, - style: Theme.of(context).textTheme.headline5), - subtitle: Text(c.address), - trailing: Icon(Icons.chevron_right), - onTap: () { _onContact(c); }, - onLongPress: () { _editContact(c); }, - ), - confirmDismiss: (_) async { - return await _onConfirmDelContact(c); - }, - onDismissed: (_) { - _delContact(c); - })); - }) - )])) + if (!settings.coins[active.coin].contactsSaved) OutlinedButton(onPressed: active.canPay ? _onCommit : null, child: Text(S.of(context).saveToBlockchain), style: OutlinedButton.styleFrom( + side: BorderSide( + width: 1, color: Theme.of(context).primaryColor))), + Expanded(child: ListView.builder( + itemCount: contacts.contacts.length, + itemBuilder: (BuildContext context, int index) { + final c = contacts.contacts[index]; + return Card( + child: Dismissible( + key: Key("${c.id}"), + child: ListTile( + title: Text(c.name, + style: Theme.of(context).textTheme.headline5), + subtitle: Text(c.address), + trailing: Icon(Icons.chevron_right), + onTap: () { _onContact(c); }, + onLongPress: () { _editContact(c); }, + ), + confirmDismiss: (_) async { + return await _onConfirmDelContact(c); + }, + onDismissed: (_) { + _delContact(c); + })); + }) + )])) ); } @@ -57,7 +57,7 @@ class ContactsState extends State { _editContact(Contact c) async { final contact = await showContactForm(context, c); - if (contact != null) contacts.add(contact); + contacts.add(contact); } Future _onConfirmDelContact(Contact c) async { @@ -89,14 +89,14 @@ class ContactsState extends State { _onCommit() async { final approve = await showMessageBox(context, S.of(context).saveToBlockchain, - S.of(context).areYouSureYouWantToSaveYourContactsIt(coin.ticker), + S.of(context).areYouSureYouWantToSaveYourContactsIt(activeCoin().ticker), S.of(context).ok); if (approve) { - contacts.markContactsDirty(false); final tx = await WarpApi.commitUnsavedContacts( - accountManager.active.id, settings.anchorOffset); + active.coin, active.id, settings.anchorOffset); final snackBar = SnackBar(content: Text("${S.of(context).txId}: $tx")); rootScaffoldMessengerKey.currentState?.showSnackBar(snackBar); + contacts.markContactsSaved(active.coin, true); } } } @@ -162,7 +162,7 @@ class ContactState extends State { state.save(); final contact = Contact(widget.contact.id, nameController.text, address); Navigator.of(context).pop(contact); - accountManager.fetchAccountData(true); + active.update(); } } @@ -214,7 +214,7 @@ class AddressState extends State { if (v == null || v.isEmpty) return S.of(context).addressIsEmpty; final zaddr = WarpApi.getSaplingFromUA(v); if (zaddr.isNotEmpty) return null; - if (!WarpApi.validAddress(v)) return S.of(context).invalidAddress; + if (!WarpApi.validAddress(active.coin, v)) return S.of(context).invalidAddress; if (contacts.contacts.where((c) => c.address == v).isNotEmpty) return S.of(context).duplicateContact; return null; } diff --git a/lib/db.dart b/lib/db.dart new file mode 100644 index 0000000..78c29ee --- /dev/null +++ b/lib/db.dart @@ -0,0 +1,228 @@ +import 'dart:math'; +import 'dart:typed_data'; + +import 'package:intl/intl.dart'; +import 'package:sqflite/sqflite.dart'; +import 'store.dart'; +import 'package:warp_api/warp_api.dart'; +import 'package:convert/convert.dart'; +import 'coin/coins.dart'; +import 'main.dart'; + +final DateFormat noteDateFormat = DateFormat("yy-MM-dd HH:mm"); +final DateFormat txDateFormat = DateFormat("MM-dd HH:mm"); + +class DbReader { + int coin; + int id; + Database db; + + DbReader(int coin, int id): this.init(coin, id, settings.coins[coin].def.db); + DbReader.init(this.coin, this.id, this.db); + + Future getBalance(int confirmHeight) async { + final balance = Sqflite.firstIntValue(await db.rawQuery( + "SELECT SUM(value) AS value FROM received_notes WHERE account = ?1 AND (spent IS NULL OR spent = 0)", + [id])) ?? 0; + final shieldedBalance = Sqflite.firstIntValue(await db.rawQuery( + "SELECT SUM(value) AS value FROM received_notes WHERE account = ?1 AND spent IS NULL", + [id])) ?? 0; + final unconfirmedSpentBalance = Sqflite.firstIntValue(await db.rawQuery( + "SELECT SUM(value) AS value FROM received_notes WHERE account = ?1 AND spent = 0", + [id])) ?? 0; + final underConfirmedBalance = Sqflite.firstIntValue(await db.rawQuery( + "SELECT SUM(value) AS value FROM received_notes WHERE account = ?1 AND height > ?2", + [id, confirmHeight])) ?? 0; + final excludedBalance = Sqflite.firstIntValue(await db.rawQuery( + "SELECT SUM(value) FROM received_notes WHERE account = ?1 AND spent IS NULL " + "AND height <= ?2 AND excluded", + [id, confirmHeight])) ?? 0; + final unconfirmedBalance = await WarpApi.mempoolSync(coin); + + return Balances(balance, shieldedBalance, unconfirmedSpentBalance, underConfirmedBalance, excludedBalance, unconfirmedBalance); + } + + Future> getNotes() async { + final List res = await db.rawQuery( + "SELECT n.id_note, n.height, n.value, t.timestamp, n.excluded, n.spent FROM received_notes n, transactions t " + "WHERE n.account = ?1 AND (n.spent IS NULL OR n.spent = 0) " + "AND n.tx = t.id_tx ORDER BY n.height DESC", + [id]); + final notes = res.map((row) { + final id = row['id_note']; + final height = row['height']; + final timestamp = noteDateFormat + .format(DateTime.fromMillisecondsSinceEpoch(row['timestamp'] * 1000)); + final excluded = (row['excluded'] ?? 0) != 0; + final spent = row['spent'] == 0; + return Note( + id, height, timestamp, row['value'] / ZECUNIT, excluded, spent); + }).toList(); + print("NOTES ${notes.length}"); + return notes; + } + + Future> getTxs() async { + final List res2 = await db.rawQuery( + "SELECT id_tx, txid, height, timestamp, t.address, c.name, value, memo FROM transactions t " + "LEFT JOIN contacts c ON t.address = c.address WHERE account = ?1 ORDER BY height DESC", + [id]); + final txs = res2.map((row) { + Uint8List txid = row['txid']; + final fullTxId = hex.encode(txid.reversed.toList()); + final shortTxid = fullTxId.substring(0, 8); + final timestamp = txDateFormat + .format(DateTime.fromMillisecondsSinceEpoch(row['timestamp'] * 1000)); + return Tx( + row['id_tx'], + row['height'], + timestamp, + shortTxid, + fullTxId, + row['value'] / ZECUNIT, + row['address'] ?? "", + row['name'], + row['memo'] ?? ""); + }).toList(); + print("TXS ${txs.length}"); + return txs; + } + + Future> getPNL(int accountId) async { + final range = _getChartRange(); + + final List res1 = await db.rawQuery( + "SELECT timestamp, value FROM transactions WHERE timestamp >= ?2 AND account = ?1", + [accountId, range.start ~/ 1000]); + final List trades = []; + for (var row in res1) { + final dt = DateTime.fromMillisecondsSinceEpoch(row['timestamp'] * 1000); + final qty = row['value'] / ZECUNIT; + trades.add(Trade(dt, qty)); + } + + final portfolioTimeSeries = sampleDaily( + trades, + range.start, + range.end, + (t) => t.dt.millisecondsSinceEpoch ~/ DAY_MS, + (t) => t, + (acc, t) => acc + t.qty, + 0.0); + + final List res2 = await db.rawQuery( + "SELECT timestamp, price FROM historical_prices WHERE timestamp >= ?2 AND currency = ?1", + [settings.currency, range.start ~/ 1000]); + final List quotes = []; + for (var row in res2) { + final dt = DateTime.fromMillisecondsSinceEpoch(row['timestamp'] * 1000); + final price = row['price']; + quotes.add(Quote(dt, price)); + } + + var prevBalance = 0.0; + var cash = 0.0; + var realized = 0.0; + final List pnls = []; + final len = min(quotes.length, portfolioTimeSeries.length); + for (var i = 0; i < len; i++) { + final dt = quotes[i].dt; + final price = quotes[i].price; + final balance = portfolioTimeSeries[i].value; + final qty = balance - prevBalance; + + final closeQty = qty * balance < 0 + ? min(qty.abs(), prevBalance.abs()) * qty.sign + : 0.0; + final openQty = qty - closeQty; + final avgPrice = prevBalance != 0 ? cash / prevBalance : 0.0; + + cash += openQty * price + closeQty * avgPrice; + realized += closeQty * (avgPrice - price); + final unrealized = price * balance - cash; + + final pnl = PnL(dt, price, balance, realized, unrealized); + pnls.add(pnl); + + prevBalance = balance; + } + return pnls; + } + + Future> getSpending(int accountId) async { + final range = _getChartRange(); + final List res = await db.rawQuery( + "SELECT SUM(value) as v, t.address, c.name FROM transactions t LEFT JOIN contacts c ON t.address = c.address " + "WHERE account = ?1 AND timestamp >= ?2 AND value < 0 GROUP BY t.address ORDER BY v ASC LIMIT 5", + [accountId, range.start ~/ 1000]); + final spendings = res.map((row) { + final address = row['address'] ?? ""; + final value = -row['v'] / ZECUNIT; + final contact = row['name']; + return Spending(address, value, contact); + }).toList(); + return spendings; + } + + Future>> getAccountBalanceTimeSeries(int accountId, int balance) async { + final range = _getChartRange(); + final List res = await db.rawQuery( + "SELECT timestamp, value FROM transactions WHERE account = ?1 AND timestamp >= ?2 ORDER BY timestamp DESC", + [accountId, range.start ~/ 1000]); + List _accountBalances = []; + var b = balance; + _accountBalances.add(AccountBalance(DateTime.now(), b / ZECUNIT)); + for (var row in res) { + final timestamp = + DateTime.fromMillisecondsSinceEpoch(row['timestamp'] * 1000); + final value = row['value'] as int; + final ab = AccountBalance(timestamp, b / ZECUNIT); + _accountBalances.add(ab); + b -= value; + } + _accountBalances.add(AccountBalance( + DateTime.fromMillisecondsSinceEpoch(range.start), b / ZECUNIT)); + _accountBalances = _accountBalances.reversed.toList(); + final accountBalances = sampleDaily( + _accountBalances, + range.start, + range.end, + (AccountBalance ab) => ab.time.millisecondsSinceEpoch ~/ DAY_MS, + (AccountBalance ab) => ab.balance, + (acc, v) => v, + 0.0); + return accountBalances; + } + + TimeRange _getChartRange() { + final now = DateTime.now().toUtc(); + final today = DateTime.utc(now.year, now.month, now.day); + final start = today.add(Duration(days: -_chartRangeDays())); + final cutoff = start.millisecondsSinceEpoch; + return TimeRange(cutoff, today.millisecondsSinceEpoch); + } + + int _chartRangeDays() { + switch (settings.chartRange) { + case '1M': + return 30; + case '3M': + return 90; + case '6M': + return 180; + } + return 365; + } +} + +class Balances { + final int balance; + final int shieldedBalance; + final int unconfirmedSpentBalance; + final int underConfirmedBalance; + final int excludedBalance; + final int unconfirmedBalance; + + Balances(this.balance, this.shieldedBalance, this.unconfirmedSpentBalance, this.underConfirmedBalance, this.excludedBalance, this.unconfirmedBalance); + static Balances zero = Balances(0, 0, 0, 0, 0, 0); +} \ No newline at end of file diff --git a/lib/dualmoneyinput.dart b/lib/dualmoneyinput.dart index a59629e..6fd0199 100644 --- a/lib/dualmoneyinput.dart +++ b/lib/dualmoneyinput.dart @@ -17,8 +17,8 @@ class DualMoneyInputWidget extends StatefulWidget { class DualMoneyInputState extends State { static final zero = decimalFormat(0, 3); var useMillis = true; - var inputInZEC = true; - var zecAmountController = TextEditingController(text: zero); + var inputInCoin = true; + var coinAmountController = TextEditingController(text: zero); var fiatAmountController = TextEditingController(text: zero); var amount = 0; @@ -46,17 +46,17 @@ class DualMoneyInputState extends State { Row(children: [ Expanded( child: TextFormField( - style: !inputInZEC + style: !inputInCoin ? TextStyle(fontWeight: FontWeight.w200) : TextStyle(), decoration: InputDecoration( labelText: - s.amountInSettingscurrency(coin.ticker)), - controller: zecAmountController, + s.amountInSettingscurrency(active.coinDef.ticker)), + controller: coinAmountController, keyboardType: TextInputType.number, inputFormatters: [makeInputFormatter(useMillis)], onTap: () => setState(() { - inputInZEC = true; + inputInCoin = true; }), validator: _checkAmount, onChanged: (_) => _updateFiatAmount(), @@ -67,7 +67,7 @@ class DualMoneyInputState extends State { Row(children: [ Expanded( child: TextFormField( - style: inputInZEC + style: inputInCoin ? TextStyle(fontWeight: FontWeight.w200) : TextStyle(), decoration: InputDecoration( @@ -78,7 +78,7 @@ class DualMoneyInputState extends State { inputFormatters: [makeInputFormatter(useMillis)], validator: (v) => _checkAmount(v, isFiat: true), onTap: () => setState(() { - inputInZEC = false; + inputInCoin = false; }), onChanged: (_) => _updateAmount())) ]), @@ -88,7 +88,7 @@ class DualMoneyInputState extends State { void clear() { setState(() { useMillis = true; - zecAmountController.text = zero; + coinAmountController.text = zero; fiatAmountController.text = zero; }); } @@ -102,7 +102,7 @@ class DualMoneyInputState extends State { void setAmount(int amount) { setState(() { useMillis = false; - zecAmountController.text = amountToString(amount); + coinAmountController.text = amountToString(amount); _updateFiatAmount(); }); } @@ -123,17 +123,17 @@ class DualMoneyInputState extends State { } void _updateAmount() { - final rate = 1.0 / priceStore.zecPrice; + final rate = 1.0 / priceStore.coinPrice; final amount = parseNumber(fiatAmountController.text); final otherAmount = decimalFormat(amount * rate, precision(useMillis)); setState(() { - zecAmountController.text = otherAmount; + coinAmountController.text = otherAmount; }); } void _updateFiatAmount() { - final rate = priceStore.zecPrice; - final amount = parseNumber(zecAmountController.text); + final rate = priceStore.coinPrice; + final amount = parseNumber(coinAmountController.text); final otherAmount = decimalFormat(amount * rate, precision(useMillis)); setState(() { fiatAmountController.text = otherAmount; diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index 8114ac8..b231636 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -25,33 +25,37 @@ class MessageLookup extends MessageLookupByLibrary { static String m1(ticker) => "Are you sure you want to save your contacts? It will cost 0.01 m${ticker}"; - static String m2(address, amount) => + static String m2(name) => "Backup Data - ${name} - Required for Restore"; + + static String m3(address, amount) => "Do you want to sign a transaction to ${address} for ${amount}"; - static String m3(ticker) => + static String m4(ticker) => "Do you want to transfer your entire transparent balance to your shielded address? A Network fee of 0.01 m${ticker} will be deducted."; - static String m4(app) => "${app} Encrypted Backup"; + static String m5(app) => "${app} Encrypted Backup"; - static String m5(num) => "${num} more signers needed"; + static String m6(num) => "${num} more signers needed"; - static String m6(ticker) => "Receive ${ticker}"; + static String m7(ticker) => "Receive ${ticker}"; - static String m7(ticker) => "Send ${ticker}"; + static String m8(ticker) => "Send ${ticker}"; - static String m8(ticker) => "Send ${ticker} to..."; + static String m9(ticker) => "Send ${ticker} to..."; - static String m9(app) => "Sent from ${app}"; + static String m10(app) => "Sent from ${app}"; - static String m10(amount, ticker, count) => + static String m11(amount, ticker, count) => "Sending a total of ${amount} ${ticker} to ${count} recipients"; - static String m11(aZEC, ticker, address) => + static String m12(aZEC, ticker, address) => "Sending ${aZEC} ${ticker} to ${address}"; - static String m12(text) => "${text} copied to clipboard"; + static String m13(name) => "Sub Account of ${name}"; - static String m13(currency) => "Use ${currency}"; + static String m14(text) => "${text} copied to clipboard"; + + static String m15(currency) => "Use ${currency}"; final messages = _notInlinedMessages(_notInlinedMessages); static Map _notInlinedMessages(_) => { @@ -67,6 +71,7 @@ class MessageLookup extends MessageLookupByLibrary { "accountHasSomeBalanceAreYouSureYouWantTo": MessageLookupByLibrary.simpleMessage( "Account has some BALANCE. Are you sure you want to delete it?"), + "accountIndex": MessageLookupByLibrary.simpleMessage("Account Index"), "accountName": MessageLookupByLibrary.simpleMessage("Account Name"), "accountNameIsRequired": MessageLookupByLibrary.simpleMessage("Account name is required"), @@ -104,8 +109,7 @@ class MessageLookup extends MessageLookupByLibrary { "backup": MessageLookupByLibrary.simpleMessage("Backup"), "backupAllAccounts": MessageLookupByLibrary.simpleMessage("Backup All Accounts"), - "backupDataRequiredForRestore": MessageLookupByLibrary.simpleMessage( - "Backup Data - Required for Restore"), + "backupDataRequiredForRestore": m2, "backupEncryptionKey": MessageLookupByLibrary.simpleMessage("Backup Encryption Key"), "backupWarning": MessageLookupByLibrary.simpleMessage( @@ -127,7 +131,7 @@ class MessageLookup extends MessageLookupByLibrary { "Are you SURE you want to DELETE this account? You MUST have a BACKUP to recover it. This operation is NOT reversible."), "confirmResetApp": MessageLookupByLibrary.simpleMessage( "Are you sure you want to reset the app? Your accounts will NOT be deleted"), - "confirmSignATransactionToAddressFor": m2, + "confirmSignATransactionToAddressFor": m3, "confirmSigning": MessageLookupByLibrary.simpleMessage("Confirm Signing"), "confs": MessageLookupByLibrary.simpleMessage("Confs"), @@ -150,12 +154,12 @@ class MessageLookup extends MessageLookupByLibrary { "doYouWantToDeleteTheSecretKeyAndConvert": MessageLookupByLibrary.simpleMessage( "Do you want to DELETE the secret key and convert this account to a watch-only account? You will not be able to spend from this device anymore. This operation is NOT reversible."), - "doYouWantToTransferYourEntireTransparentBalanceTo": m3, + "doYouWantToTransferYourEntireTransparentBalanceTo": m4, "duplicateAccount": MessageLookupByLibrary.simpleMessage("Duplicate Account"), "duplicateContact": MessageLookupByLibrary.simpleMessage( "Another contact has this address"), - "encryptedBackup": m4, + "encryptedBackup": m5, "enterSecretShareIfAccountIsMultisignature": MessageLookupByLibrary.simpleMessage( "Enter secret share if account is multi-signature"), @@ -199,7 +203,11 @@ class MessageLookup extends MessageLookupByLibrary { "nameIsEmpty": MessageLookupByLibrary.simpleMessage("Name is empty"), "newSnapAddress": MessageLookupByLibrary.simpleMessage("New Snap Address"), + "newSubAccount": + MessageLookupByLibrary.simpleMessage("New Sub Account"), "noAccount": MessageLookupByLibrary.simpleMessage("No account"), + "noActiveAccount": + MessageLookupByLibrary.simpleMessage("No active account"), "noAuthenticationMethod": MessageLookupByLibrary.simpleMessage("No Authentication Method"), "noContacts": MessageLookupByLibrary.simpleMessage("No Contacts"), @@ -209,7 +217,7 @@ class MessageLookup extends MessageLookupByLibrary { "notEnoughBalance": MessageLookupByLibrary.simpleMessage("Not enough balance"), "notes": MessageLookupByLibrary.simpleMessage("Notes"), - "numMoreSignersNeeded": m5, + "numMoreSignersNeeded": m6, "numberOfConfirmationsNeededBeforeSpending": MessageLookupByLibrary.simpleMessage( "Number of Confirmations Needed before Spending"), @@ -238,7 +246,7 @@ class MessageLookup extends MessageLookupByLibrary { "purple": MessageLookupByLibrary.simpleMessage("Purple"), "qty": MessageLookupByLibrary.simpleMessage("Qty"), "realized": MessageLookupByLibrary.simpleMessage("Realized"), - "receive": m6, + "receive": m7, "receivePayment": MessageLookupByLibrary.simpleMessage("Receive Payment"), "rescan": MessageLookupByLibrary.simpleMessage("Rescan"), @@ -267,11 +275,11 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage( "Select notes to EXCLUDE from payments"), "send": MessageLookupByLibrary.simpleMessage("Send"), - "sendCointicker": m7, - "sendCointickerTo": m8, - "sendFrom": m9, - "sendingATotalOfAmountCointickerToCountRecipients": m10, - "sendingAzecCointickerToAddress": m11, + "sendCointicker": m8, + "sendCointickerTo": m9, + "sendFrom": m10, + "sendingATotalOfAmountCointickerToCountRecipients": m11, + "sendingAzecCointickerToAddress": m12, "server": MessageLookupByLibrary.simpleMessage("Server"), "settings": MessageLookupByLibrary.simpleMessage("Settings"), "shieldTranspBalance": @@ -285,10 +293,12 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Shielding in progress..."), "sign": MessageLookupByLibrary.simpleMessage("Sign Transaction"), "simple": MessageLookupByLibrary.simpleMessage("Simple"), + "simpleMode": MessageLookupByLibrary.simpleMessage("Simple Mode"), "spendable": MessageLookupByLibrary.simpleMessage("Spendable"), "spendableBalance": MessageLookupByLibrary.simpleMessage("Spendable Balance"), "splitAccount": MessageLookupByLibrary.simpleMessage("Split Account"), + "subAccountOf": m13, "synching": MessageLookupByLibrary.simpleMessage("Synching"), "table": MessageLookupByLibrary.simpleMessage("Table"), "tapAnIconToShowTheQrCode": MessageLookupByLibrary.simpleMessage( @@ -302,7 +312,7 @@ class MessageLookup extends MessageLookupByLibrary { "Tap QR Code for Transparent Address"), "tapTransactionForDetails": MessageLookupByLibrary.simpleMessage("Tap Transaction for Details"), - "textCopiedToClipboard": m12, + "textCopiedToClipboard": m14, "theme": MessageLookupByLibrary.simpleMessage("Theme"), "themeEditor": MessageLookupByLibrary.simpleMessage("Theme Editor"), "thisAccountAlreadyExists": MessageLookupByLibrary.simpleMessage( @@ -331,7 +341,7 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Unshielded Balance"), "unsignedTransactionFile": MessageLookupByLibrary.simpleMessage("Unsigned Transaction File"), - "useSettingscurrency": m13, + "useSettingscurrency": m15, "useTransparentBalance": MessageLookupByLibrary.simpleMessage("Use Transparent Balance"), "useUa": MessageLookupByLibrary.simpleMessage("Use UA"), diff --git a/lib/generated/intl/messages_es.dart b/lib/generated/intl/messages_es.dart index c5ee19f..b1c04a2 100644 --- a/lib/generated/intl/messages_es.dart +++ b/lib/generated/intl/messages_es.dart @@ -25,31 +25,36 @@ class MessageLookup extends MessageLookupByLibrary { static String m1(ticker) => "Estás seguro de que quieres guardar tus contactos? Costará 0,01 mZEC "; - static String m2(address, amount) => + static String m2(name) => + "Copia De Seguridad - ${name} - Requerido Para Restaurar"; + + static String m3(address, amount) => "Do you want to sign a transaction to ${address} for ${amount}"; - static String m3(ticker) => + static String m4(ticker) => "¿Quiere transferir su saldo transparente a su dirección blindada? "; - static String m4(app) => "${app} Encrypted Backup"; + static String m5(app) => "${app} Encrypted Backup"; - static String m6(ticker) => "Recibir ${ticker}"; + static String m7(ticker) => "Recibir ${ticker}"; - static String m7(ticker) => "Enviar ${ticker}"; + static String m8(ticker) => "Enviar ${ticker}"; - static String m8(ticker) => "Enviar ${ticker} a…"; + static String m9(ticker) => "Enviar ${ticker} a…"; - static String m9(app) => "Enviado desde ${app}"; + static String m10(app) => "Enviado desde ${app}"; - static String m10(amount, ticker, count) => + static String m11(amount, ticker, count) => "Enviando un total de ${amount} ${ticker} a ${count} direcciones"; - static String m11(aZEC, ticker, address) => + static String m12(aZEC, ticker, address) => "Enviado ${aZEC} ${ticker} a ${address}"; - static String m12(text) => "${text} copied to clipboard"; + static String m13(name) => "Sub Account of ${name}"; - static String m13(currency) => "Utilizar ${currency}"; + static String m14(text) => "${text} copied to clipboard"; + + static String m15(currency) => "Utilizar ${currency}"; final messages = _notInlinedMessages(_notInlinedMessages); static Map _notInlinedMessages(_) => { @@ -65,6 +70,7 @@ class MessageLookup extends MessageLookupByLibrary { "accountHasSomeBalanceAreYouSureYouWantTo": MessageLookupByLibrary.simpleMessage( "La cuenta tiene un saldo. ¿Estás seguro de que quieres eliminarlo?"), + "accountIndex": MessageLookupByLibrary.simpleMessage("Account Index"), "accountName": MessageLookupByLibrary.simpleMessage("Nombre de Cuenta"), "accountNameIsRequired": MessageLookupByLibrary.simpleMessage( "Se requiere el nombre de cuenta"), @@ -102,8 +108,7 @@ class MessageLookup extends MessageLookupByLibrary { "backup": MessageLookupByLibrary.simpleMessage("Copia De Seguridad"), "backupAllAccounts": MessageLookupByLibrary.simpleMessage("Backup All Accounts"), - "backupDataRequiredForRestore": MessageLookupByLibrary.simpleMessage( - "Copia De Seguridad - Requerido Para Restaurar"), + "backupDataRequiredForRestore": m2, "backupEncryptionKey": MessageLookupByLibrary.simpleMessage("Backup Encryption Key"), "backupWarning": MessageLookupByLibrary.simpleMessage( @@ -125,7 +130,7 @@ class MessageLookup extends MessageLookupByLibrary { "¿Está SEGURO de que desea BORRAR esta cuenta? DEBE tener una COPIA DE SEGURIDAD para recuperarla. Esta operación NO es reversible."), "confirmResetApp": MessageLookupByLibrary.simpleMessage( "¿Seguro que quieres restablecer la aplicación? Sus cuentas NO serán eliminadas"), - "confirmSignATransactionToAddressFor": m2, + "confirmSignATransactionToAddressFor": m3, "confirmSigning": MessageLookupByLibrary.simpleMessage("Confirm Signing"), "confs": MessageLookupByLibrary.simpleMessage("Confs"), @@ -151,10 +156,10 @@ class MessageLookup extends MessageLookupByLibrary { "doYouWantToDeleteTheSecretKeyAndConvert": MessageLookupByLibrary.simpleMessage( "¿Quiere BORRAR la clave secreta y convertir esta cuenta a solo lectura? Ya no podrá gastar desde este dispositivo. Esta operación NO es reversible."), - "doYouWantToTransferYourEntireTransparentBalanceTo": m3, + "doYouWantToTransferYourEntireTransparentBalanceTo": m4, "duplicateAccount": MessageLookupByLibrary.simpleMessage("Cuenta duplicada"), - "encryptedBackup": m4, + "encryptedBackup": m5, "enterSecretShareIfAccountIsMultisignature": MessageLookupByLibrary.simpleMessage( "Enter secret share if account is multi-signature"), @@ -198,7 +203,11 @@ class MessageLookup extends MessageLookupByLibrary { "nameIsEmpty": MessageLookupByLibrary.simpleMessage("Nombre vacio"), "newSnapAddress": MessageLookupByLibrary.simpleMessage("Nueva Dirección Instantánea"), + "newSubAccount": + MessageLookupByLibrary.simpleMessage("New Sub Account"), "noAccount": MessageLookupByLibrary.simpleMessage("Sin Cuenta"), + "noActiveAccount": + MessageLookupByLibrary.simpleMessage("No active account"), "noAuthenticationMethod": MessageLookupByLibrary.simpleMessage("Sin método de autenticación"), "noContacts": MessageLookupByLibrary.simpleMessage("Sin Contactos"), @@ -235,7 +244,7 @@ class MessageLookup extends MessageLookupByLibrary { "purple": MessageLookupByLibrary.simpleMessage("Morada"), "qty": MessageLookupByLibrary.simpleMessage("Cantidad"), "realized": MessageLookupByLibrary.simpleMessage("Dio Cuenta"), - "receive": m6, + "receive": m7, "receivePayment": MessageLookupByLibrary.simpleMessage("Recibir un pago"), "rescan": MessageLookupByLibrary.simpleMessage("Escanear"), @@ -266,11 +275,11 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage( "Seleccionar Notas a EXCLUIR de los pagos"), "send": MessageLookupByLibrary.simpleMessage("Enviar"), - "sendCointicker": m7, - "sendCointickerTo": m8, - "sendFrom": m9, - "sendingATotalOfAmountCointickerToCountRecipients": m10, - "sendingAzecCointickerToAddress": m11, + "sendCointicker": m8, + "sendCointickerTo": m9, + "sendFrom": m10, + "sendingATotalOfAmountCointickerToCountRecipients": m11, + "sendingAzecCointickerToAddress": m12, "server": MessageLookupByLibrary.simpleMessage("Servidor"), "settings": MessageLookupByLibrary.simpleMessage("Ajustes"), "shieldTranspBalance": @@ -281,10 +290,12 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Blindaje en progreso…"), "sign": MessageLookupByLibrary.simpleMessage("Sign"), "simple": MessageLookupByLibrary.simpleMessage("Sencillo"), + "simpleMode": MessageLookupByLibrary.simpleMessage("Simple Mode"), "spendable": MessageLookupByLibrary.simpleMessage("Gastable"), "spendableBalance": MessageLookupByLibrary.simpleMessage("Saldo Gastable"), "splitAccount": MessageLookupByLibrary.simpleMessage("Split Account"), + "subAccountOf": m13, "synching": MessageLookupByLibrary.simpleMessage("Sincronizando"), "table": MessageLookupByLibrary.simpleMessage("Lista"), "tapAnIconToShowTheQrCode": MessageLookupByLibrary.simpleMessage( @@ -298,7 +309,7 @@ class MessageLookup extends MessageLookupByLibrary { "Pinchar QR para Dirección Transparente"), "tapTransactionForDetails": MessageLookupByLibrary.simpleMessage( "Toque Transacción para detalles"), - "textCopiedToClipboard": m12, + "textCopiedToClipboard": m14, "theme": MessageLookupByLibrary.simpleMessage("Tema"), "themeEditor": MessageLookupByLibrary.simpleMessage("Editora de temas"), "thisAccountAlreadyExists": @@ -327,7 +338,7 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Saldo sin blindaje"), "unsignedTransactionFile": MessageLookupByLibrary.simpleMessage( "Archivo de transaccion sin firmar"), - "useSettingscurrency": m13, + "useSettingscurrency": m15, "useTransparentBalance": MessageLookupByLibrary.simpleMessage("Usar Saldo Transp"), "useUa": MessageLookupByLibrary.simpleMessage("Usar UA"), diff --git a/lib/generated/intl/messages_fr.dart b/lib/generated/intl/messages_fr.dart index 27e7ae1..1f7a623 100644 --- a/lib/generated/intl/messages_fr.dart +++ b/lib/generated/intl/messages_fr.dart @@ -25,31 +25,36 @@ class MessageLookup extends MessageLookupByLibrary { static String m1(ticker) => "Voulez vous sauver vos contacts? Ceci coute 0.01 m${ticker}"; - static String m2(address, amount) => + static String m2(name) => + "Données de sauvegarde - ${name} - requises pour la restauration"; + + static String m3(address, amount) => "Do you want to sign a transaction to ${address} for ${amount}"; - static String m3(ticker) => + static String m4(ticker) => "Voulez-vous transférer l\'intégralité de votre solde transparent à votre adresse protégée?"; - static String m4(app) => "Sauvegarde de ${app} "; + static String m5(app) => "Sauvegarde de ${app} "; - static String m6(ticker) => "Recevoir ${ticker}"; + static String m7(ticker) => "Recevoir ${ticker}"; - static String m7(ticker) => "Envoyer ${ticker}"; + static String m8(ticker) => "Envoyer ${ticker}"; - static String m8(ticker) => "Envoyer ${ticker} à..."; + static String m9(ticker) => "Envoyer ${ticker} à..."; - static String m9(app) => "Envoyé via ${app}"; + static String m10(app) => "Envoyé via ${app}"; - static String m10(amount, ticker, count) => + static String m11(amount, ticker, count) => "Envoi d\'un total de ${amount} ${ticker} à ${count} destinataires"; - static String m11(aZEC, ticker, address) => + static String m12(aZEC, ticker, address) => "Envoi de ${aZEC} ${ticker} à ${address}"; - static String m12(text) => "${text} copied to clipboard"; + static String m13(name) => "Sous Compte de ${name}"; - static String m13(currency) => "Utiliser ${currency}"; + static String m14(text) => "${text} copied to clipboard"; + + static String m15(currency) => "Utiliser ${currency}"; final messages = _notInlinedMessages(_notInlinedMessages); static Map _notInlinedMessages(_) => { @@ -66,6 +71,7 @@ class MessageLookup extends MessageLookupByLibrary { "accountHasSomeBalanceAreYouSureYouWantTo": MessageLookupByLibrary.simpleMessage( "Ce compte a un solde. Voulez vous l\'effacer?"), + "accountIndex": MessageLookupByLibrary.simpleMessage("Index du Compte"), "accountName": MessageLookupByLibrary.simpleMessage("Nom du compte"), "accountNameIsRequired": MessageLookupByLibrary.simpleMessage("Le nom du compte est requis"), @@ -104,8 +110,7 @@ class MessageLookup extends MessageLookupByLibrary { "backup": MessageLookupByLibrary.simpleMessage("Sauvegarde"), "backupAllAccounts": MessageLookupByLibrary.simpleMessage("Sauver tous les comptes"), - "backupDataRequiredForRestore": MessageLookupByLibrary.simpleMessage( - "Données de sauvegarde - requises pour la restauration"), + "backupDataRequiredForRestore": m2, "backupEncryptionKey": MessageLookupByLibrary.simpleMessage("Clé de chiffrage"), "backupWarning": MessageLookupByLibrary.simpleMessage( @@ -127,7 +132,7 @@ class MessageLookup extends MessageLookupByLibrary { "Êtes-vous SUR de vouloir SUPPRIMER ce compte ? Vous DEVEZ avoir une SAUVEGARDE pour le récupérer. Cette opération n\'est PAS réversible."), "confirmResetApp": MessageLookupByLibrary.simpleMessage( "Etes vous sur de vouloir réinitialiser. Vos comptes ne seront PAS perdus "), - "confirmSignATransactionToAddressFor": m2, + "confirmSignATransactionToAddressFor": m3, "confirmSigning": MessageLookupByLibrary.simpleMessage("Confirm Signing"), "confs": MessageLookupByLibrary.simpleMessage("Confs"), @@ -153,10 +158,10 @@ class MessageLookup extends MessageLookupByLibrary { "doYouWantToDeleteTheSecretKeyAndConvert": MessageLookupByLibrary.simpleMessage( "Voulez-vous SUPPRIMER la clé secrète et convertir ce compte en un compte d\'observation ? Vous ne pourrez plus dépenser depuis cet appareil. Cette opération n\'est PAS réversible."), - "doYouWantToTransferYourEntireTransparentBalanceTo": m3, + "doYouWantToTransferYourEntireTransparentBalanceTo": m4, "duplicateAccount": MessageLookupByLibrary.simpleMessage("Compte en double"), - "encryptedBackup": m4, + "encryptedBackup": m5, "enterSecretShareIfAccountIsMultisignature": MessageLookupByLibrary.simpleMessage( "Enter secret share if account is multi-signature"), @@ -202,7 +207,11 @@ class MessageLookup extends MessageLookupByLibrary { "nameIsEmpty": MessageLookupByLibrary.simpleMessage("Le nom est vide"), "newSnapAddress": MessageLookupByLibrary.simpleMessage( "Nouvelle adresse instantanée"), + "newSubAccount": + MessageLookupByLibrary.simpleMessage("Nouveau Sous Compte"), "noAccount": MessageLookupByLibrary.simpleMessage("Pas de compte"), + "noActiveAccount": + MessageLookupByLibrary.simpleMessage("Aucun Compte sélectionné"), "noAuthenticationMethod": MessageLookupByLibrary.simpleMessage( "Pas de méthode d\'authentification"), "noContacts": MessageLookupByLibrary.simpleMessage("Pas de Contacts"), @@ -240,7 +249,7 @@ class MessageLookup extends MessageLookupByLibrary { "purple": MessageLookupByLibrary.simpleMessage("Violet"), "qty": MessageLookupByLibrary.simpleMessage("Quantité"), "realized": MessageLookupByLibrary.simpleMessage("Réalisé"), - "receive": m6, + "receive": m7, "receivePayment": MessageLookupByLibrary.simpleMessage("Recevoir un payment"), "rescan": MessageLookupByLibrary.simpleMessage("Parcourir à nouveau"), @@ -271,11 +280,11 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage( "Sélectionnez les billets à EXCLURE des paiements"), "send": MessageLookupByLibrary.simpleMessage("Envoyer"), - "sendCointicker": m7, - "sendCointickerTo": m8, - "sendFrom": m9, - "sendingATotalOfAmountCointickerToCountRecipients": m10, - "sendingAzecCointickerToAddress": m11, + "sendCointicker": m8, + "sendCointickerTo": m9, + "sendFrom": m10, + "sendingATotalOfAmountCointickerToCountRecipients": m11, + "sendingAzecCointickerToAddress": m12, "server": MessageLookupByLibrary.simpleMessage("Serveur"), "settings": MessageLookupByLibrary.simpleMessage("Paramètres"), "shieldTranspBalance": MessageLookupByLibrary.simpleMessage( @@ -286,10 +295,12 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Masquage en cours..."), "sign": MessageLookupByLibrary.simpleMessage("Sign"), "simple": MessageLookupByLibrary.simpleMessage("Simple"), + "simpleMode": MessageLookupByLibrary.simpleMessage("Mode Simple"), "spendable": MessageLookupByLibrary.simpleMessage("Dépensable"), "spendableBalance": MessageLookupByLibrary.simpleMessage("Montant dépensable"), "splitAccount": MessageLookupByLibrary.simpleMessage("Split Account"), + "subAccountOf": m13, "synching": MessageLookupByLibrary.simpleMessage("Synchronisation en cours"), "table": MessageLookupByLibrary.simpleMessage("Tableau"), @@ -304,7 +315,7 @@ class MessageLookup extends MessageLookupByLibrary { "Appuyez sur le code QR pour l\'adresse transparente"), "tapTransactionForDetails": MessageLookupByLibrary.simpleMessage( "Presser sur une Transaction pour plus de details"), - "textCopiedToClipboard": m12, + "textCopiedToClipboard": m14, "theme": MessageLookupByLibrary.simpleMessage("Thème"), "themeEditor": MessageLookupByLibrary.simpleMessage("Editeur de Thème"), "thisAccountAlreadyExists": @@ -333,7 +344,7 @@ class MessageLookup extends MessageLookupByLibrary { MessageLookupByLibrary.simpleMessage("Solde Transparent"), "unsignedTransactionFile": MessageLookupByLibrary.simpleMessage( "Fichier de transaction non signée"), - "useSettingscurrency": m13, + "useSettingscurrency": m15, "useTransparentBalance": MessageLookupByLibrary.simpleMessage( "Utiliser le Solde Transparent"), "useUa": MessageLookupByLibrary.simpleMessage("Utiliser UA"), diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index 8cfb129..5a328f5 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -630,13 +630,13 @@ class S { ); } - /// `Backup Data - Required for Restore` - String get backupDataRequiredForRestore { + /// `Backup Data - {name} - Required for Restore` + String backupDataRequiredForRestore(Object name) { return Intl.message( - 'Backup Data - Required for Restore', + 'Backup Data - $name - Required for Restore', name: 'backupDataRequiredForRestore', desc: '', - args: [], + args: [name], ); } @@ -1991,6 +1991,56 @@ class S { args: [], ); } + + /// `Simple Mode` + String get simpleMode { + return Intl.message( + 'Simple Mode', + name: 'simpleMode', + desc: '', + args: [], + ); + } + + /// `Account Index` + String get accountIndex { + return Intl.message( + 'Account Index', + name: 'accountIndex', + desc: '', + args: [], + ); + } + + /// `Sub Account of {name}` + String subAccountOf(Object name) { + return Intl.message( + 'Sub Account of $name', + name: 'subAccountOf', + desc: '', + args: [name], + ); + } + + /// `New Sub Account` + String get newSubAccount { + return Intl.message( + 'New Sub Account', + name: 'newSubAccount', + desc: '', + args: [], + ); + } + + /// `No active account` + String get noActiveAccount { + return Intl.message( + 'No active account', + name: 'noActiveAccount', + desc: '', + args: [], + ); + } } class AppLocalizationDelegate extends LocalizationsDelegate { diff --git a/lib/history.dart b/lib/history.dart index 7a227a6..e179c77 100644 --- a/lib/history.dart +++ b/lib/history.dart @@ -6,9 +6,7 @@ import 'main.dart'; import 'generated/l10n.dart'; class HistoryWidget extends StatefulWidget { - final void Function(int index) tabTo; - - HistoryWidget(this.tabTo); + HistoryWidget(); @override State createState() => HistoryState(); @@ -43,47 +41,47 @@ class HistoryState extends State }), DataColumn( label: Text(S.of(context).datetime + - accountManager.txSortConfig.getIndicator("time")), + active.txSortConfig.getIndicator("time")), onSort: (_, __) { setState(() { - accountManager.sortTx("time"); + active.sortTx("time"); }); }, ), DataColumn( label: Text(S.of(context).amount + - accountManager.txSortConfig.getIndicator("amount")), + active.txSortConfig.getIndicator("amount")), numeric: true, onSort: (_, __) { setState(() { - accountManager.sortTx("amount"); + active.sortTx("amount"); }); }, ), DataColumn( label: Text(S.of(context).txId + - accountManager.txSortConfig.getIndicator("txid")), + active.txSortConfig.getIndicator("txid")), onSort: (_, __) { setState(() { - accountManager.sortTx("txid"); + active.sortTx("txid"); }); }, ), DataColumn( label: Text(S.of(context).address + - accountManager.txSortConfig.getIndicator("address")), + active.txSortConfig.getIndicator("address")), onSort: (_, __) { setState(() { - accountManager.sortTx("address"); + active.sortTx("address"); }); }, ), DataColumn( label: Text(S.of(context).memo + - accountManager.txSortConfig.getIndicator("memo")), + active.txSortConfig.getIndicator("memo")), onSort: (_, __) { setState(() { - accountManager.sortTx("memo"); + active.sortTx("memo"); }); }, ), @@ -101,7 +99,7 @@ class HistoryState extends State } _onExport() async { - final csvData = accountManager.sortedTxs.map((tx) => [ + final csvData = active.sortedTxs.map((tx) => [ tx.fullTxId, tx.height, tx.timestamp, tx.address, tx.contact ?? '', tx.value, tx.memo]).toList(); await shareCsv(csvData, 'tx_history.csv', S.of(context).transactionHistory); @@ -115,7 +113,7 @@ class HistoryDataSource extends DataTableSource { @override DataRow getRow(int index) { - final tx = accountManager.sortedTxs[index]; + final tx = active.sortedTxs[index]; final confsOrHeight = settings.showConfirmations ? syncStatus.latestHeight - tx.height + 1 : tx.height; @@ -144,7 +142,7 @@ class HistoryDataSource extends DataTableSource { bool get isRowCountApproximate => false; @override - int get rowCount => accountManager.txs.length; + int get rowCount => active.txs.length; @override int get selectedRowCount => 0; diff --git a/lib/home.dart b/lib/home.dart new file mode 100644 index 0000000..3f9714a --- /dev/null +++ b/lib/home.dart @@ -0,0 +1,263 @@ +import 'dart:async'; + +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:url_launcher/url_launcher.dart'; +import 'package:warp_api/warp_api.dart'; + +import 'about.dart'; +import 'account.dart'; +import 'account_manager.dart'; +import 'budget.dart'; +import 'contact.dart'; +import 'history.dart'; +import 'generated/l10n.dart'; +import 'main.dart'; +import 'note.dart'; +import 'store.dart'; + +class HomePage extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Observer(builder: (context) { + final simpleMode = settings.simpleMode; + return HomePageInner(simpleMode); + }); + } +} + +class HomePageInner extends StatefulWidget { + final bool simpleMode; + HomePageInner(this.simpleMode); + @override + HomeState createState() => HomeState(); +} + +class HomeState extends State with TickerProviderStateMixin { + TabController? _tabController; + int _tabIndex = 0; + StreamSubscription? _syncDispose; + final contactKey = GlobalKey(); + + @override + void initState() { + super.initState(); + Future.microtask(() async { + await syncStatus.update(); + await active.updateBalances(); + await priceStore.updateChart(); + + await Future.delayed(Duration(seconds: 3)); + await syncStatus.sync(); + await contacts.fetchContacts(); + + Timer.periodic(Duration(seconds: 15), (Timer t) async { + syncStatus.sync(); + await active.updateBalances(); + }); + Timer.periodic(Duration(minutes: 5), (Timer t) async { + await priceStore.updateChart(); + }); + }); + _syncDispose = syncStream.listen((height) { + final h = height as int?; + if (h != null) { + syncStatus.setSyncHeight(h); + eta.checkpoint(h, DateTime.now()); + } else { + WarpApi.mempoolReset(active.coin, syncStatus.latestHeight); + } + }); + } + + @override + void didChangeDependencies() { + _tabController?.dispose(); + final tabController = TabController(length: settings.simpleMode ? 3 : 6, vsync: this); + tabController.addListener(() { + setState(() { + _tabIndex = tabController.index; + }); + }); + _tabController = tabController; + super.didChangeDependencies(); + } + + @override + void dispose() { + _syncDispose?.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) => Observer(builder: (context) { + final s = S.of(context); + final theme = Theme.of(context); + final simpleMode = settings.simpleMode; + + if (active.id == 0) { + return AccountManagerPage(); + } + + final contactTabIndex = simpleMode ? 2 : 5; + Widget button = Container(); + if (_tabIndex == 0) + button = FloatingActionButton( + onPressed: _onSend, + backgroundColor: theme.colorScheme.secondary, + child: Icon(Icons.send), + ); + else if (_tabIndex == contactTabIndex) + button = FloatingActionButton( + onPressed: _onAddContact, + backgroundColor: theme.colorScheme.secondary, + child: Icon(Icons.add), + ); + + final menu = PopupMenuButton( + itemBuilder: (context) { + return [ + PopupMenuItem(child: Text(s.accounts), value: "Accounts"), + PopupMenuItem(child: Text(s.backup), value: "Backup"), + PopupMenuItem(child: Text(s.rescan), value: "Rescan"), + if (!simpleMode && active.canPay) + PopupMenuItem(child: Text(s.coldStorage), value: "Cold"), + if (!simpleMode) + PopupMenuItem(child: Text(s.multipay), value: "MultiPay"), + if (!simpleMode) + PopupMenuItem(child: Text(s.broadcast), value: "Broadcast"), + PopupMenuItem(child: Text(s.settings), value: "Settings"), + PopupMenuItem(child: Text(s.help), value: "Help"), + PopupMenuItem(child: Text(s.about), value: "About"), + ]; + }, + onSelected: _onMenu, + ); + + return Scaffold( + appBar: AppBar( + centerTitle: true, + title: Text("${active.account.name}"), + bottom: TabBar( + controller: _tabController, + isScrollable: true, + tabs: [ + Tab(text: s.account), + if (!simpleMode) Tab(text: s.notes), + Tab(text: s.history), + if (!simpleMode) Tab(text: s.budget), + if (!simpleMode) Tab(text: s.tradingPl), + Tab(text: s.contacts), + ], + ), + actions: [menu], + ), + body: TabBarView( + controller: _tabController, + children: [ + AccountPage2(), + if (!simpleMode) NoteWidget(), + HistoryWidget(), + if (!simpleMode) BudgetWidget(), + if (!simpleMode) PnLWidget(), + ContactsTab(key: contactKey), + ], + ), + floatingActionButton: button, + ); + }); + + _onSend() { + Navigator.of(this.context).pushNamed('/send'); + } + + _onMenu(String choice) { + switch (choice) { + case "Accounts": + Navigator.of(this.context).pushNamed('/accounts'); + break; + case "Backup": + _backup(); + break; + case "Rescan": + _rescan(); + break; + case "Cold": + _cold(); + break; + case "MultiPay": + _multiPay(); + break; + case "Broadcast": + _broadcast(); + break; + case "Settings": + _settings(); + break; + case "Help": + launch(DOC_URL); + break; + case "About": + showAbout(this.context); + break; + } + } + + _backup() async { + final didAuthenticate = await authenticate(context, S.of(context).pleaseAuthenticateToShowAccountSeed); + if (didAuthenticate) { + Navigator.of(context).pushNamed('/backup'); + } + } + + _rescan() async { + final approved = await rescanDialog(context); + if (approved) { + syncStatus.rescan(context); + } + } + + _cold() { + showDialog( + context: context, + barrierDismissible: false, + builder: (context) => AlertDialog( + title: Text(S.of(context).coldStorage), + content: + Text(S.of(context).doYouWantToDeleteTheSecretKeyAndConvert), + actions: confirmButtons(context, _convertToWatchOnly, + okLabel: S.of(context).delete))); + } + + _multiPay() { + Navigator.of(context).pushNamed('/multipay'); + } + + _broadcast() async { + final result = await FilePicker.platform.pickFiles(); + + if (result != null) { + final res = WarpApi.broadcast(active.coin, result.files.single.path!); + final snackBar = SnackBar(content: Text(res)); + rootScaffoldMessengerKey.currentState?.showSnackBar(snackBar); + } + } + + _convertToWatchOnly() async { + await active.convertToWatchOnly(); + Navigator.of(context).pop(); + } + + _settings() { + Navigator.of(context).pushNamed('/settings'); + } + + _onAddContact() async { + final contact = await contactKey.currentState + ?.showContactForm(context, Contact.empty()); + if (contact != null) { + contacts.add(contact); + } + } +} diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 57b576d..5c6470d 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -57,7 +57,7 @@ "seed": "Seed", "confirmDeleteAccount": "Are you SURE you want to DELETE this account? You MUST have a BACKUP to recover it. This operation is NOT reversible.", "changeAccountName": "Change Account Name", - "backupDataRequiredForRestore": "Backup Data - Required for Restore", + "backupDataRequiredForRestore": "Backup Data - {name} - Required for Restore", "secretKey": "Secret Key", "viewingKey": "Viewing Key", "tapAnIconToShowTheQrCode": "Tap an icon to show the QR code", @@ -192,5 +192,10 @@ "encryptedBackup": "{app} Encrypted Backup", "fullRestore": "Full Restore", "loadBackup": "Load Backup", - "backupAllAccounts": "Backup All Accounts" + "backupAllAccounts": "Backup All Accounts", + "simpleMode": "Simple Mode", + "accountIndex": "Account Index", + "subAccountOf": "Sub Account of {name}", + "newSubAccount": "New Sub Account", + "noActiveAccount": "No active account" } diff --git a/lib/l10n/intl_es.arb b/lib/l10n/intl_es.arb index c370106..74c6678 100644 --- a/lib/l10n/intl_es.arb +++ b/lib/l10n/intl_es.arb @@ -59,7 +59,7 @@ "seed":"Semilla", "confirmDeleteAccount":"¿Está SEGURO de que desea BORRAR esta cuenta? DEBE tener una COPIA DE SEGURIDAD para recuperarla. Esta operación NO es reversible.", "changeAccountName":"Cambiar nombre de la cuenta", - "backupDataRequiredForRestore":"Copia De Seguridad - Requerido Para Restaurar", + "backupDataRequiredForRestore":"Copia De Seguridad - {name} - Requerido Para Restaurar", "secretKey":"Llave Secreta", "viewingKey":"Llave Lectura", "tapAnIconToShowTheQrCode":"Pinchar icono para mostrar código QR", @@ -190,5 +190,10 @@ "encryptedBackup": "{app} Encrypted Backup", "fullRestore": "Full Restore", "loadBackup": "Load Backup", - "backupAllAccounts": "Backup All Accounts" + "backupAllAccounts": "Backup All Accounts", + "simpleMode": "Simple Mode", + "accountIndex": "Account Index", + "subAccountOf": "Sub Account of {name}", + "newSubAccount": "New Sub Account", + "noActiveAccount": "No active account" } diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 84bc934..55fa066 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -59,7 +59,7 @@ "seed": "Graine", "confirmDeleteAccount": "Êtes-vous SUR de vouloir SUPPRIMER ce compte ? Vous DEVEZ avoir une SAUVEGARDE pour le récupérer. Cette opération n'est PAS réversible.", "changeAccountName": "Modifier le nom du compte", - "backupDataRequiredForRestore": "Données de sauvegarde - requises pour la restauration", + "backupDataRequiredForRestore": "Données de sauvegarde - {name} - requises pour la restauration", "secretKey": "Clé secrète", "viewingKey": "Affichage de la clé", "tapAnIconToShowTheQrCode": "Appuyez sur une icône pour afficher le code QR", @@ -190,5 +190,10 @@ "encryptedBackup": "Sauvegarde de {app} ", "fullRestore": "Restauration complète", "loadBackup": "Recharger une sauvegarde", - "backupAllAccounts": "Sauver tous les comptes" + "backupAllAccounts": "Sauver tous les comptes", + "simpleMode": "Mode Simple", + "accountIndex": "Index du Compte", + "subAccountOf": "Sous Compte de {name}", + "newSubAccount": "Nouveau Sous Compte", + "noActiveAccount": "Aucun Compte sélectionné" } diff --git a/lib/main.dart b/lib/main.dart index f7f92c4..cea8cf6 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -12,7 +12,6 @@ import 'package:flutter_barcode_scanner/flutter_barcode_scanner.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:intl/intl.dart'; import 'package:local_auth/local_auth.dart'; -import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart'; import 'package:qr_flutter/qr_flutter.dart'; import 'package:rate_my_app/rate_my_app.dart'; @@ -23,15 +22,15 @@ import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:uni_links/uni_links.dart'; import 'package:quick_actions/quick_actions.dart'; -import 'package:flutter/services.dart' show PlatformException; +import 'accounts.dart'; +import 'coin/coins.dart'; import 'generated/l10n.dart'; -import 'account.dart'; import 'account_manager.dart'; import 'backup.dart'; -import 'coin/coindef.dart'; +import 'home.dart'; import 'multisend.dart'; -import 'multisign.dart'; +// import 'multisign.dart'; import 'payment_uri.dart'; import 'reset.dart'; import 'settings.dart'; @@ -41,58 +40,75 @@ import 'store.dart'; import 'theme_editor.dart'; import 'transaction.dart'; -var coin = Coin(); - const ZECUNIT = 100000000.0; var ZECUNIT_DECIMAL = Decimal.parse('100000000'); const mZECUNIT = 100000; const DEFAULT_FEE = 1000; const MAXMONEY = 21000000; const DOC_URL = "https://hhanh00.github.io/zwallet/"; +const APP_NAME = "ZYWallet"; -var accountManager = AccountManager(); +// var accountManager = AccountManager(); var priceStore = PriceStore(); var syncStatus = SyncStatus(); var settings = Settings(); var multipayData = MultiPayStore(); var eta = ETAStore(); var contacts = ContactStore(); - -Future getDatabase() async { - var databasesPath = await getDatabasesPath(); - final path = join(databasesPath, 'zec.db'); - var db = await openDatabase(path); - return db; -} +var accounts = AccountManager2(); +var active = ActiveAccount(); StreamSubscription? subUniLinks; +void handleUri(BuildContext context, Uri uri) { + final scheme = uri.scheme; + final coinDef = settings.coins.firstWhere((c) => c.def.currency == scheme); + final id = settings.coins[coinDef.coin].active; + if (id != 0) { + active.setActiveAccount(coinDef.coin, id); + Navigator.of(context).pushNamed( + '/send', arguments: SendPageArgs(uri: uri.toString())); + } +} + Future initUniLinks(BuildContext context) async { try { - final initialLink = await getInitialLink(); - if (initialLink != null) - Navigator.of(context).pushNamed( - '/send', arguments: SendPageArgs(uri: initialLink)); + final uri = await getInitialUri(); + if (uri != null) { + handleUri(context, uri); + } } on PlatformException {} - subUniLinks = linkStream.listen((String? uri) { - Navigator.of(context).pushNamed('/send', arguments: SendPageArgs(uri: uri)); + subUniLinks = linkStream.listen((String? uriString) { + if (uriString == null) return; + final uri = Uri.parse(uriString); + handleUri(context, uri); }); } -void handleQuickAction(BuildContext context, String shortcut) { - switch (shortcut) { - case 'receive': - Navigator.of(context).pushNamed('/receive'); - break; - case 'send': - Navigator.of(context).pushNamed('/send'); - break; +void handleQuickAction(BuildContext context, String type) { + final t = type.split("."); + final coin = int.parse(t[0]); + final shortcut = t[1]; + final a = settings.coins[coin].active; + + if (a != 0) { + Future.microtask(() async { + await active.setActiveAccount(coin, a); + switch (shortcut) { + case 'receive': + Navigator.of(context).pushNamed('/receive'); + break; + case 'send': + Navigator.of(context).pushNamed('/send'); + break; + } + }); } } class LoadProgress extends StatelessWidget { - double value; + final double value; LoadProgress(this.value); @@ -134,7 +150,8 @@ void main() { headingRowColor: MaterialStateColor.resolveWith( (_) => settings.themeData.highlightColor))); return MaterialApp( - title: coin.app, + title: APP_NAME, + debugShowCheckedModeBanner: false, theme: theme, home: home, scaffoldMessengerKey: rootScaffoldMessengerKey, @@ -147,7 +164,7 @@ void main() { supportedLocales: S.delegate.supportedLocales, onGenerateRoute: (RouteSettings routeSettings) { var routes = { - '/account': (context) => AccountPage(), + '/account': (context) => HomePage(), '/restore': (context) => RestorePage(), '/send': (context) => SendPage(routeSettings.arguments as SendPageArgs?), @@ -158,13 +175,13 @@ void main() { '/tx': (context) => TransactionPage(routeSettings.arguments as Tx), '/backup': (context) => - BackupPage(routeSettings.arguments as int?), + BackupPage(routeSettings.arguments as AccountId?), '/multipay': (context) => MultiPayPage(), - '/multisig': (context) => MultisigPage(), - '/multisign': (context) => MultisigAggregatorPage( - routeSettings.arguments as TxSummary), - '/multisig_shares': (context) => - MultisigSharesPage(routeSettings.arguments as String), + // '/multisig': (context) => MultisigPage(), + // '/multisign': (context) => MultisigAggregatorPage( + // routeSettings.arguments as TxSummary), + // '/multisig_shares': (context) => + // MultisigSharesPage(routeSettings.arguments as String), '/edit_theme': (context) => ThemeEditorPage(onSaved: settings.updateCustomThemeColors), '/reset': (context) => ResetPage(), @@ -185,6 +202,7 @@ class ZWalletApp extends StatefulWidget { class ZWalletAppState extends State { bool initialized = false; + late Future init; RateMyApp rateMyApp = RateMyApp( preferencesPrefix: 'rateMyApp_', @@ -201,32 +219,49 @@ class ZWalletAppState extends State { rateMyApp.showRateDialog(this.context); } }); + init = _init(); } - Future _init(BuildContext context) async { - final s = S.of(this.context); + Future _init() async { if (!initialized) { initialized = true; final dbPath = await getDatabasesPath(); - WarpApi.initWallet(dbPath + "/zec.db", settings.getLWD()); - final db = await getDatabase(); - await accountManager.init(db); - await contacts.init(db); - await syncStatus.init(); - await initUniLinks(context); + await ycash.open(dbPath); + await zcash.open(dbPath); + WarpApi.initWallet(dbPath); + for (var s in settings.servers) { + WarpApi.updateLWD(s.coin, s.getLWDUrl()); + } + await accounts.refresh(); + await active.restore(); + await syncStatus.update(); + if (accounts.list.isEmpty) { + for (var c in settings.coins) { + syncStatus.markAsSynced(c.coin); + } + } + + await initUniLinks(this.context); final quickActions = QuickActions(); quickActions.initialize((type) { handleQuickAction(this.context, type); }); if (!settings.linkHooksInitialized) { - quickActions.setShortcutItems([ - ShortcutItem(type: 'receive', - localizedTitle: s.receive(coin.ticker), - icon: 'receive'), - ShortcutItem(type: 'send', - localizedTitle: s.sendCointicker(coin.ticker), - icon: 'send'), - ]); + Future.microtask(() { + final s = S.of(this.context); + List shortcuts = []; + for (var c in settings.coins) { + final coin = c.coin; + final ticker = c.def.ticker; + shortcuts.add(ShortcutItem(type: '$coin.receive', + localizedTitle: s.receive(ticker), + icon: 'receive')); + shortcuts.add(ShortcutItem(type: '$coin.send', + localizedTitle: s.sendCointicker(ticker), + icon: 'send')); + } + quickActions.setShortcutItems(shortcuts); + }); await settings.setLinkHooksInitialized(); } } @@ -236,12 +271,10 @@ class ZWalletAppState extends State { @override Widget build(BuildContext context) { return FutureBuilder( - future: _init(context), + future: init, builder: (context, snapshot) { if (!snapshot.hasData) return LoadProgress(0.7); - return accountManager.accounts.isNotEmpty - ? AccountPage() : - AccountManagerPage(); + return HomePage(); }); } } @@ -414,6 +447,7 @@ Color amountColor(BuildContext context, num a) { TextStyle fontWeight(TextStyle style, num v) { final value = v.abs(); + final coin = activeCoin(); final style2 = style.copyWith(fontFeatures: [FontFeature.tabularFigures()]); if (value >= coin.weights[2]) return style2.copyWith(fontWeight: FontWeight.w800); @@ -526,13 +560,13 @@ Future shieldTAddr(BuildContext context) async { content: Text(S .of(context) .doYouWantToTransferYourEntireTransparentBalanceTo( - coin.ticker)), + activeCoin().ticker)), actions: confirmButtons(context, () async { final s = S.of(context); Navigator.of(context).pop(); final snackBar1 = SnackBar(content: Text(s.shieldingInProgress)); rootScaffoldMessengerKey.currentState?.showSnackBar(snackBar1); - final txid = await WarpApi.shieldTAddr(accountManager.active.id); + final txid = await WarpApi.shieldTAddr(active.coin, active.id); final snackBar2 = SnackBar(content: Text("${s.txId}: $txid")); rootScaffoldMessengerKey.currentState?.showSnackBar(snackBar2); })), diff --git a/lib/multisend.dart b/lib/multisend.dart index 2b26de7..074a4b0 100644 --- a/lib/multisend.dart +++ b/lib/multisend.dart @@ -6,7 +6,9 @@ import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_typeahead/flutter_typeahead.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; -import 'package:warp/store.dart'; +import 'coin/coins.dart'; +import 'settings.dart'; +import 'store.dart'; import 'package:warp_api/warp_api.dart'; import 'package:warp_api/types.dart'; @@ -78,7 +80,7 @@ class MultiPayState extends State { child: Text(S .of(context) .sendingATotalOfAmountCointickerToCountRecipients( - amount, coin.ticker, count))), + amount, activeCoin().ticker, count))), actions: confirmButtons( context, () => Navigator.of(context).pop(true), cancelValue: false))); @@ -87,7 +89,7 @@ class MultiPayState extends State { await send(context, multipayData.recipients, false); multipayData.clear(); - await accountManager.fetchAccountData(true); + await active.update(); } } } diff --git a/lib/multisign.dart b/lib/multisign.dart.bak similarity index 97% rename from lib/multisign.dart rename to lib/multisign.dart.bak index c434d55..3405207 100644 --- a/lib/multisign.dart +++ b/lib/multisign.dart.bak @@ -92,7 +92,7 @@ class MultisigAggregatorState extends State { final tx = await WarpApi.submitTx(widget.txSummary.txJson, PORT); SnackBar snackBar; if (tx.startsWith("00")) { // first byte is success/error - final txId = WarpApi.broadcastHex(tx.substring(2)); + final txId = WarpApi.broadcastHex(accountManager.coin, tx.substring(2)); snackBar = SnackBar(content: Text("${s.txId}: $txId")); } else { @@ -248,7 +248,7 @@ class MultisigState extends State { _split() async { final s = S.of(context); - final shareString = WarpApi.splitAccount(_threshold, _participants, accountManager.active.id); + final shareString = WarpApi.splitAccount(accountManager.coin, _threshold, _participants, accountManager.active.id); Navigator.of(context).pushNamed('/multisig_shares', arguments: shareString); } diff --git a/lib/note.dart b/lib/note.dart index 66f6262..2e4013f 100644 --- a/lib/note.dart +++ b/lib/note.dart @@ -7,9 +7,7 @@ import 'store.dart'; import 'generated/l10n.dart'; class NoteWidget extends StatefulWidget { - final void Function(int index) tabTo; - - NoteWidget(this.tabTo); + NoteWidget(); @override State createState() => _NoteState(); @@ -21,11 +19,12 @@ class _NoteState extends State with AutomaticKeepAliveClientMixin { @override Widget build(BuildContext context) { + super.build(context); return SingleChildScrollView( padding: EdgeInsets.all(8), scrollDirection: Axis.vertical, child: Observer(builder: (context) { - accountManager.sortedNotes; + active.sortedNotes; return PaginatedDataTable( columns: [ DataColumn( @@ -39,20 +38,20 @@ class _NoteState extends State with AutomaticKeepAliveClientMixin { }), DataColumn( label: Text(S.of(context).datetime + - accountManager.noteSortConfig.getIndicator("time")), + active.noteSortConfig.getIndicator("time")), onSort: (_, __) { setState(() { - accountManager.sortNotes("time"); + active.sortNotes("time"); }); }, ), DataColumn( numeric: true, label: Text(S.of(context).amount + - accountManager.noteSortConfig.getIndicator("amount")), + active.noteSortConfig.getIndicator("amount")), onSort: (_, __) { setState(() { - accountManager.sortNotes("amount"); + active.sortNotes("amount"); }); }, ), @@ -76,11 +75,11 @@ class _NoteState extends State with AutomaticKeepAliveClientMixin { } _onRowSelected(Note note) { - accountManager.excludeNote(note); + active.excludeNote(note); } _selectInverse() { - accountManager.invertExcludedNotes(); + active.invertExcludedNotes(); } } @@ -92,7 +91,7 @@ class NotesDataSource extends DataTableSource { @override DataRow getRow(int index) { - final note = accountManager.sortedNotes[index]; + final note = active.sortedNotes[index]; final theme = Theme.of(context); final confsOrHeight = settings.showConfirmations ? syncStatus.latestHeight - note.height + 1 @@ -127,7 +126,7 @@ class NotesDataSource extends DataTableSource { bool get isRowCountApproximate => false; @override - int get rowCount => accountManager.notes.length; + int get rowCount => active.notes.length; @override int get selectedRowCount => 0; diff --git a/lib/payment_uri.dart b/lib/payment_uri.dart index a185b33..8c8ff35 100644 --- a/lib/payment_uri.dart +++ b/lib/payment_uri.dart @@ -1,16 +1,17 @@ import 'package:decimal/decimal.dart'; import 'package:flutter/material.dart'; import 'package:qr_flutter/qr_flutter.dart'; -import 'package:warp/dualmoneyinput.dart'; +import 'dualmoneyinput.dart'; import 'package:warp_api/warp_api.dart'; import 'main.dart'; import 'generated/l10n.dart'; +import 'settings.dart'; class PaymentURIPage extends StatefulWidget { final String address; - PaymentURIPage(String? _address): address = _address ?? accountManager.active.address; + PaymentURIPage(String? _address): address = _address ?? active.account.address; @override PaymentURIState createState() => PaymentURIState(); @@ -27,7 +28,7 @@ class PaymentURIState extends State { super.initState(); qrText = widget.address; Future.microtask(() { - priceStore.fetchZecPrice(); + priceStore.fetchCoinPrice(active.coin); }); } @@ -38,7 +39,7 @@ class PaymentURIState extends State { return Form( key: _formKey, child: Scaffold( - appBar: AppBar(title: Text(S.of(context).receive(coin.ticker))), + appBar: AppBar(title: Text(S.of(context).receive(activeCoin().ticker))), body: SingleChildScrollView( child: GestureDetector( onTap: () { FocusScope.of(context).unfocus(); }, @@ -83,7 +84,7 @@ class PaymentURIState extends State { final String _qrText; if (amount > 0) { - _qrText = WarpApi.makePaymentURI(widget.address, amount, memo); + _qrText = WarpApi.makePaymentURI(active.coin, widget.address, amount, memo); } else _qrText = widget.address; diff --git a/lib/reset.dart b/lib/reset.dart index 4ddb643..daecc2f 100644 --- a/lib/reset.dart +++ b/lib/reset.dart @@ -101,10 +101,10 @@ class FullBackupPage extends StatelessWidget { _onSave(BuildContext context) async { Directory tempDir = await getTemporaryDirectory(); - String filename = "${tempDir.path}/${coin.app}.bak"; + String filename = "${tempDir.path}/$APP_NAME.bak"; final file = File(filename); await file.writeAsString(backup); - Share.shareFiles([filename], subject: S.of(context).encryptedBackup(coin.app)); + Share.shareFiles([filename], subject: S.of(context).encryptedBackup(APP_NAME)); } } @@ -148,7 +148,7 @@ class _FullRestoreState extends State { rootScaffoldMessengerKey.currentState?.showSnackBar(snackBar); } else { - await accountManager.refresh(); + await accounts.refresh(); syncStatus.setAccountRestored(true); Navigator.of(context).pop(); } diff --git a/lib/restore.dart b/lib/restore.dart index 44e26d7..e7bf06b 100644 --- a/lib/restore.dart +++ b/lib/restore.dart @@ -1,9 +1,12 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_barcode_scanner/flutter_barcode_scanner.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:warp_api/warp_api.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; +import 'backup.dart'; import 'main.dart'; import 'generated/l10n.dart'; @@ -16,16 +19,18 @@ class _RestorePageState extends State { final _formKey = GlobalKey(); final _keyController = TextEditingController(); final _nameController = TextEditingController(); + final _accountIndexController = TextEditingController(text: "0"); final _shareController = TextEditingController(); var _validKey = true; - var _isVK = false; + var _coin = 0; + var _showIndex = false; @override Widget build(BuildContext context) { final s = S.of(context); return Scaffold( appBar: AppBar( - title: Text("${coin.symbol} Wallet"), + title: Text(APP_NAME), ), body: GestureDetector( onTap: () { FocusScope.of(context).unfocus(); }, @@ -34,15 +39,26 @@ class _RestorePageState extends State { child: SingleChildScrollView(child: Padding( padding: EdgeInsets.all(16), child: Column(children: [ + FormBuilderRadioGroup( + orientation: OptionsOrientation.horizontal, + name: 'coin', + initialValue: _coin, + onChanged: (int? v) { _coin = v!; }, + options: [ + FormBuilderFieldOption( + child: Text('Zcash'), value: 0), + FormBuilderFieldOption( + child: Text('Ycash'), value: 1), + ]), TextFormField( - decoration: InputDecoration(labelText: s.accountName), - controller: _nameController, - validator: (String? name) { - if (name == null || name.isEmpty) - return s.accountNameIsRequired; - return null; - }, - ), + decoration: InputDecoration(labelText: s.accountName), + controller: _nameController, + validator: (String? name) { + if (name == null || name.isEmpty) + return s.accountNameIsRequired; + return null; + }, + ), Row( children: [ Expanded( @@ -56,26 +72,39 @@ class _RestorePageState extends State { controller: _keyController, onChanged: _checkKey, )), - IconButton( - icon: new Icon(MdiIcons.qrcodeScan), onPressed: _onScan) + GestureDetector(onLongPress: _toggleShowAccountIndex, + child: IconButton( + icon: new Icon(MdiIcons.qrcodeScan), onPressed: _onScan)) ], ), - if (_isVK && coin.supportsMultisig) Row( - children: [ - Expanded( - child: TextFormField( - decoration: InputDecoration( - labelText: s.secretShare, - hintText: s.enterSecretShareIfAccountIsMultisignature), - minLines: 4, - maxLines: 4, - controller: _shareController, - // TODO: Check share - )), - IconButton( - icon: new Icon(MdiIcons.qrcodeScan), onPressed: _onScanShare) - ], + // if (_isVK && coin.supportsMultisig) Row( + // children: [ + // Expanded( + // child: TextFormField( + // decoration: InputDecoration( + // labelText: s.secretShare, + // hintText: s.enterSecretShareIfAccountIsMultisignature), + // minLines: 4, + // maxLines: 4, + // controller: _shareController, + // // TODO: Check share + // )), + // IconButton( + // icon: new Icon(MdiIcons.qrcodeScan), onPressed: _onScanShare) + // ], + // ), + if (_showIndex) TextFormField( + decoration: InputDecoration(labelText: s.accountIndex), + keyboardType: TextInputType.number, + inputFormatters: [FilteringTextInputFormatter.digitsOnly], + controller: _accountIndexController, + validator: (String? name) { + if (name == null || name.isEmpty) + return s.accountNameIsRequired; + return null; + }, ), + ButtonBar(children: confirmButtons(context, _validKey ? _onOK : null, okLabel: s.addnew, okIcon: Icon(Icons.add))) ])))))); @@ -83,9 +112,11 @@ class _RestorePageState extends State { _onOK() async { final s = S.of(context); - if (_formKey.currentState!.validate()) { + final form = _formKey.currentState!; + final accountIndex = int.parse(_accountIndexController.text); + if (form.validate()) { final account = - WarpApi.newAccount(_nameController.text, _keyController.text); + WarpApi.newAccount(_coin, _nameController.text, _keyController.text, accountIndex); if (account < 0) { showDialog( context: context, @@ -103,27 +134,34 @@ class _RestorePageState extends State { ])); } else { - if (_shareController.text.isNotEmpty) - accountManager.storeShareSecret(account, _shareController.text); - await accountManager.refresh(); + // if (_shareController.text.isNotEmpty) + // accountManager.storeShareSecret(account, _shareController.text); + // await accountManager.refresh(); + await accounts.refresh(); if (_keyController.text != "") { syncStatus.setAccountRestored(true); Navigator.of(context).pop(); } else { - if (accountManager.accounts.length == 1) - WarpApi.skipToLastHeight(); // single new account -> quick sync - Navigator.of(context).pushReplacementNamed('/backup', arguments: account); + if (accounts.list.where((e) => e.coin == active.coin).length == 1) + WarpApi.skipToLastHeight(0); // single new account -> quick sync + Navigator.of(context).pushReplacementNamed('/backup', arguments: AccountId(_coin, account)); // Need coin type } } } } + _toggleShowAccountIndex() { + setState(() { + _showIndex = !_showIndex; + }); + } + _checkKey(key) { setState(() { - final keyType = WarpApi.validKey(key); + final keyType = WarpApi.validKey(_coin, key); _validKey = key == "" || keyType >= 0; - _isVK = keyType == 2; + // _isVK = keyType == 2; }); } diff --git a/lib/send.dart b/lib/send.dart index 1f1b560..f9dac8d 100644 --- a/lib/send.dart +++ b/lib/send.dart @@ -6,7 +6,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:mobx/mobx.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; -import 'package:warp/dualmoneyinput.dart'; +import 'dualmoneyinput.dart'; import 'package:warp_api/types.dart'; import 'package:warp_api/warp_api.dart'; import 'package:decimal/decimal.dart'; @@ -68,15 +68,13 @@ class SendState extends State { super.initState(); _newBlockAutorunDispose = autorun((_) async { - final _ = syncStatus.latestHeight; - final sBalance = await accountManager.getShieldedBalance(); - final tBalance = accountManager.tbalance; - final excludedBalance = await accountManager.getExcludedBalance(); - final underConfirmedBalance = - await accountManager.getUnderConfirmedBalance(); - final unconfirmedSpentBalance = - await accountManager.getUnconfirmedSpentBalance(); - final unconfirmedBalance = accountManager.unconfirmedBalance; + final _ = active.dataEpoch; + final sBalance = active.balances.shieldedBalance; + final tBalance = active.tbalance; + final excludedBalance = active.balances.excludedBalance; + final underConfirmedBalance = active.balances.underConfirmedBalance; + final unconfirmedSpentBalance = active.balances.unconfirmedBalance; + final unconfirmedBalance = active.balances.unconfirmedBalance; setState(() { _sBalance = sBalance; _tBalance = tBalance; @@ -98,10 +96,10 @@ class SendState extends State { Widget build(BuildContext context) { final s = S.of(context); final simpleMode = settings.simpleMode; - _memoController.text = settings.memoSignature ?? s.sendFrom(coin.app); + _memoController.text = settings.memoSignature ?? s.sendFrom(APP_NAME); return Scaffold( - appBar: AppBar(title: Text(s.sendCointicker(coin.ticker))), + appBar: AppBar(title: Text(s.sendCointicker(active.coinDef.ticker))), body: GestureDetector( onTap: () { FocusScope.of(context).unfocus(); @@ -117,7 +115,7 @@ class SendState extends State { textFieldConfiguration: TextFieldConfiguration( controller: _addressController, decoration: InputDecoration( - labelText: s.sendCointickerTo(coin.ticker)), + labelText: s.sendCointickerTo(active.coinDef.ticker)), minLines: 4, maxLines: 10, keyboardType: TextInputType.multiline, @@ -172,7 +170,7 @@ class SendState extends State { title: Text(s.roundToMillis), value: _useMillis, onChanged: _setUseMillis), - if (accountManager.canPay && !widget.isMulti) + if (active.canPay && !widget.isMulti) CheckboxListTile( title: Text(s.useTransparentBalance), value: _useTransparent, @@ -209,7 +207,7 @@ class SendState extends State { if (c.isNotEmpty) return null; final zaddr = WarpApi.getSaplingFromUA(v); if (zaddr.isNotEmpty) return null; - if (!WarpApi.validAddress(v)) return s.invalidAddress; + if (!WarpApi.validAddress(active.coin, v)) return s.invalidAddress; return null; } @@ -259,7 +257,7 @@ class SendState extends State { } void _setPaymentURI(String uri) { - final json = WarpApi.parsePaymentURI(uri); + final json = WarpApi.parsePaymentURI(active.coin, uri); try { final payment = DecodedPaymentURI.fromJson(jsonDecode(json)); setState(() { @@ -302,7 +300,7 @@ class SendState extends State { title: Text(s.pleaseConfirm), content: SingleChildScrollView( child: Text(s.sendingAzecCointickerToAddress( - aZEC, coin.ticker, _address))), + aZEC, active.coinDef.ticker, _address))), actions: confirmButtons( context, () => Navigator.of(context).pop(true), okLabel: s.approve, cancelValue: false))); @@ -424,9 +422,7 @@ Future send(BuildContext context, List recipients, bool useTran final s = S.of(context); String address = ""; - int amount = 0; for (var r in recipients) { - amount += r.amount; if (address.isEmpty) address = r.address; else @@ -439,9 +435,9 @@ Future send(BuildContext context, List recipients, bool useTran if (settings.protectSend && !await authenticate(context, s.pleaseAuthenticateToSend)) return; - if (accountManager.canPay) { + if (active.canPay) { Navigator.of(context).pop(); - final tx = await WarpApi.sendPayment(accountManager.active.id, recipients, + final tx = await WarpApi.sendPayment(active.coin, active.id, recipients, useTransparent, settings.anchorOffset, (progress) { progressPort.sendPort.send(progress); }); @@ -449,25 +445,20 @@ Future send(BuildContext context, List recipients, bool useTran final snackBar2 = SnackBar(content: Text("${s.txId}: $tx")); rootScaffoldMessengerKey.currentState?.showSnackBar(snackBar2); - await accountManager.fetchAccountData(true); + await active.update(); } else { Directory tempDir = await getTemporaryDirectory(); String filename = "${tempDir.path}/tx.json"; - final txjson = WarpApi.prepareTx(accountManager.active.id, recipients, + final txjson = WarpApi.prepareTx(active.coin, active.id, recipients, useTransparent, settings.anchorOffset, filename); - if (coin.supportsMultisig && accountManager.active.share != null) { - final txSummary = TxSummary(address, amount, txjson); - Navigator.of(context).pushReplacementNamed('/multisign', arguments: txSummary); - } else { - final file = File(filename); - await file.writeAsString(txjson); - Share.shareFiles([filename], subject: s.unsignedTransactionFile); + final file = File(filename); + await file.writeAsString(txjson); + Share.shareFiles([filename], subject: s.unsignedTransactionFile); - final snackBar2 = SnackBar(content: Text(s.fileSaved)); - rootScaffoldMessengerKey.currentState?.showSnackBar(snackBar2); - Navigator.of(context).pop(); - } + final snackBar2 = SnackBar(content: Text(s.fileSaved)); + rootScaffoldMessengerKey.currentState?.showSnackBar(snackBar2); + Navigator.of(context).pop(); } } diff --git a/lib/settings.dart b/lib/settings.dart index 0705c99..6bfdf48 100644 --- a/lib/settings.dart +++ b/lib/settings.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_form_builder/flutter_form_builder.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; +import 'coin/coins.dart'; +import 'coin/coin.dart'; import 'main.dart'; import 'generated/l10n.dart'; @@ -12,7 +14,7 @@ class SettingsPage extends StatefulWidget { final _settingsFormKey = GlobalKey(); -class SettingsState extends State { +class SettingsState extends State with SingleTickerProviderStateMixin { var _anchorController = TextEditingController(text: "${settings.anchorOffset}"); var _thresholdController = TextEditingController( @@ -20,27 +22,19 @@ class SettingsState extends State { var _memoController = TextEditingController(); var _currency = settings.currency; var _needAuth = false; + late TabController _tabController; + + @override + void initState() { + super.initState(); + _tabController = TabController(length: 2, vsync: this); + } @override Widget build(BuildContext context) { final s = S.of(context); final simpleMode = settings.simpleMode; - _memoController.text = settings.memoSignature ?? s.sendFrom(coin.app); - - List options = coin.lwd - .map((lwd) => FormBuilderFieldOption( - child: Text(lwd.name), value: lwd.name)) - .toList(); - options.add( - FormBuilderFieldOption( - value: 'custom', - child: FormBuilderTextField( - name: 'lwd_url', - decoration: InputDecoration(labelText: s.custom), - initialValue: settings.ldUrl, - onSaved: _onURL, - )), - ); + _memoController.text = settings.memoSignature ?? s.sendFrom(APP_NAME); return Scaffold( appBar: AppBar(title: Text(s.settings)), @@ -64,14 +58,16 @@ class SettingsState extends State { FormBuilderFieldOption( child: Text(s.advanced), value: 'advanced'), ]), - if (!simpleMode) FormBuilderRadioGroup( - orientation: OptionsOrientation.vertical, - name: 'servers', - decoration: InputDecoration( - labelText: s.server), - initialValue: settings.ldUrlChoice, - onSaved: _onChoice, - options: options), + if (!simpleMode) TabBar(controller: _tabController, tabs: [Tab(text: "Zcash"), Tab(text: "Ycash")]), + if (!simpleMode) SizedBox(height: 200, child: TabBarView(controller: _tabController, children: [ServerSelect(0), ServerSelect(1)])), + // if (!simpleMode) FormBuilderRadioGroup( + // orientation: OptionsOrientation.vertical, + // name: 'servers', + // decoration: InputDecoration( + // labelText: s.server), + // initialValue: settings.ldUrlChoice, + // onSaved: _onChoice, + // options: options), FormBuilderRadioGroup( orientation: OptionsOrientation.horizontal, name: 'themes', @@ -138,13 +134,13 @@ class SettingsState extends State { _needAuth = true; }, onSaved: _onProtectSend)), - if (coin.supportsUA) - Expanded( - child: FormBuilderCheckbox( - name: 'use_ua', - title: Text(s.useUa), - initialValue: settings.useUA, - onSaved: _onUseUA)), + // if (coin.supportsUA) + // Expanded( + // child: FormBuilderCheckbox( + // name: 'use_ua', + // title: Text(s.useUa), + // initialValue: settings.useUA, + // onSaved: _onUseUA)), ]), if (!simpleMode) FormBuilderTextField( decoration: InputDecoration( @@ -216,20 +212,9 @@ class SettingsState extends State { } _onMode(v) { - final simpleMode = v == 'simple'; - if (settings.simpleMode != simpleMode) - showSnackBar(S.of(context).changingTheModeWillTakeEffectAtNextRestart); settings.setMode(v == 'simple'); } - _onChoice(v) { - settings.setURLChoice(v); - } - - _onURL(v) { - settings.setURL(v); - } - _onTheme(v) { settings.setTheme(v); } @@ -268,6 +253,7 @@ class SettingsState extends State { if (form.validate()) { if (_needAuth && !await authenticate(context, S.of(context).protectSendSettingChanged)) return; form.save(); + settings.updateLWD(); Navigator.of(context).pop(); } } @@ -288,3 +274,75 @@ class SettingsState extends State { Navigator.of(context).pushNamed('/edit_theme'); } } + +class ServerSelect extends StatefulWidget { + final int coin; + + ServerSelect(this.coin); + _ServerSelectState createState() => _ServerSelectState(coin); +} + +class _ServerSelectState extends State with + AutomaticKeepAliveClientMixin { + final int coin; + late String choice; + late String customUrl; + bool _saved = true; + + _ServerSelectState(this.coin) { + choice = settings.servers[coin].choice; + customUrl = settings.servers[coin].customUrl; + } + + CoinBase get coinDef => settings.coins[widget.coin].def; + + @override + Widget build(BuildContext context) { + super.build(context); + final s = S.of(context); + List> options = coinDef.lwd + .map((lwd) => FormBuilderFieldOption( + child: Text(lwd.name), value: lwd.name)) + .toList(); + options.add( + FormBuilderFieldOption( + value: 'custom', + child: FormBuilderTextField( + name: 'lwd_url ${coinDef.ticker}', + decoration: InputDecoration(labelText: s.custom), + initialValue: customUrl, + onSaved: _save, + onChanged: (v) { + if (v == null) return; + customUrl = v; + _saved = false; + }, + )), + ); + + return FormBuilderRadioGroup( + orientation: OptionsOrientation.vertical, + name: 'lwd ${coinDef.ticker}', + decoration: InputDecoration( + labelText: s.server), + initialValue: choice, + onSaved: _save, + onChanged: (v) { + if (v == null) return; + choice = v; + _saved = false; + }, + options: options); + } + + void _save(_) async { + if (_saved) return; + await settings.servers[coin].savePrefs(choice, customUrl); + _saved = true; + } + + @override + bool get wantKeepAlive => true; +} + +CoinBase activeCoin() => settings.coins[active.coin].def; diff --git a/lib/store.dart b/lib/store.dart index 7f3b5e3..06de726 100644 --- a/lib/store.dart +++ b/lib/store.dart @@ -1,4 +1,5 @@ import 'dart:isolate'; +import 'dart:math'; import 'dart:typed_data'; import 'dart:math' as math; import 'package:flutter/foundation.dart'; @@ -7,8 +8,8 @@ import 'package:json_annotation/json_annotation.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:mobx/mobx.dart'; -import 'package:path/path.dart'; import 'package:sqflite/sqflite.dart'; +import 'coin/coins.dart'; import 'package:warp_api/warp_api.dart'; import 'package:warp_api/types.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -16,12 +17,74 @@ import 'package:http/http.dart' as http; import 'dart:convert' as convert; import 'package:convert/convert.dart'; import 'package:flex_color_scheme/flex_color_scheme.dart'; +import 'package:sensors_plus/sensors_plus.dart'; +import 'coin/coin.dart'; import 'generated/l10n.dart'; import 'main.dart'; part 'store.g.dart'; +class LWDServer { + final int coin; + final CoinBase coinDef; + late String choice; + late String customUrl; + + LWDServer(this.coin, this.coinDef) { + choice = coinDef.lwd.first.name; + customUrl = coinDef.lwd.first.url; + } + + Future loadPrefs() async { + final prefs = await SharedPreferences.getInstance(); + final ticker = coinDef.ticker; + final _choice = prefs.getString('$ticker.lwd_choice'); + final _customUrl = prefs.getString('$ticker.lwd_custom'); + if (_choice != null && _customUrl != null) { + choice = _choice; + customUrl = _customUrl; + } + else { + await savePrefs(choice, customUrl); + } + return getLWDUrl(); + } + + Future savePrefs(String _choice, String _customUrl) async { + choice = _choice; + customUrl = _customUrl; + final ticker = coinDef.ticker; + final prefs = await SharedPreferences.getInstance(); + prefs.setString('$ticker.lwd_choice', _choice); + prefs.setString('$ticker.lwd_custom', _customUrl); + } + + String getLWDUrl() { + final url; + if (choice == "custom") + url = customUrl; + else { + final lwd = coinDef.lwd.firstWhere((lwd) => lwd.name == choice, + orElse: () => coinDef.lwd.first); + url = lwd.url; + } + return url; + } +} + +class CoinData = _CoinData with _$CoinData; + +abstract class _CoinData with Store { + final int coin; + int active = 0; + int syncedHeight = 0; + final CoinBase def; + @observable bool contactsSaved = true; + + _CoinData(this.coin, this.def); +} + class Settings = _Settings with _$Settings; abstract class _Settings with Store { @@ -31,11 +94,8 @@ abstract class _Settings with Store { @observable bool simpleMode = true; - @observable - String ldUrl = ""; - - @observable - String ldUrlChoice = ""; + List servers = [LWDServer(0, zcash), LWDServer(1, ycash)]; + List coins = [CoinData(0, zcash), CoinData(1, ycash)]; @observable int anchorOffset = 10; @@ -95,15 +155,14 @@ abstract class _Settings with Store { @observable String? memoSignature; + @observable + bool flat = false; + @action Future restore() async { final prefs = await SharedPreferences.getInstance(); linkHooksInitialized = prefs.getBool('link_hooks') ?? false; simpleMode = prefs.getBool('simple_mode') ?? true; - ldUrlChoice = prefs.getString('lightwalletd_choice') ?? "Lightwalletd"; - ldUrl = prefs.getString('lightwalletd_custom') ?? ""; - prefs.setString('lightwalletd_choice', ldUrlChoice); - prefs.setString('lightwalletd_custom', ldUrl); anchorOffset = prefs.getInt('anchor_offset') ?? 3; getTx = prefs.getBool('get_txinfo') ?? true; rowsPerPage = prefs.getInt('rows_per_age') ?? 10; @@ -127,11 +186,31 @@ abstract class _Settings with Store { memoSignature = prefs.getString('memo_signature'); + for (var s in servers) { + await s.loadPrefs(); + } + + for (var c in coins) { + final ticker = c.def.ticker; + c.active = prefs.getInt("$ticker.active") ?? 0; + c.contactsSaved = prefs.getBool("$ticker.contacts_saved") ?? true; + } + _updateThemeData(); Future.microtask(_loadCurrencies); // lazily + accelerometerEvents.listen(_handleAccel); return true; } + @action + void _handleAccel(event) { + final n = sqrt(event.x * event.x + event.y * event.y + event.z * event.z); + final inclination = acos(event.z / n) / pi * 180 * event.y.sign; + final _flat = inclination < 20; + if (flat != _flat) + flat = _flat; + } + @action Future setLinkHooksInitialized() async { final prefs = await SharedPreferences.getInstance(); @@ -143,22 +222,14 @@ abstract class _Settings with Store { Future setMode(bool simple) async { final prefs = await SharedPreferences.getInstance(); prefs.setBool('simple_mode', simple); + simpleMode = simple; } - @action - Future setURLChoice(String choice) async { - ldUrlChoice = choice; - final prefs = await SharedPreferences.getInstance(); - prefs.setString('lightwalletd_choice', ldUrlChoice); - updateLWD(); - } - - @action - Future setURL(String url) async { - ldUrl = url; - final prefs = await SharedPreferences.getInstance(); - prefs.setString('lightwalletd_custom', ldUrl); - updateLWD(); + void updateLWD() async { + for (var s in servers) { + final url = await s.loadPrefs(); + WarpApi.updateLWD(s.coin, url); + } } @action @@ -255,23 +326,7 @@ abstract class _Settings with Store { final prefs = await SharedPreferences.getInstance(); chartRange = v; prefs.setString('chart_range', chartRange); - accountManager.fetchChartData(); - } - - String getLWD() { - switch (ldUrlChoice) { - case "custom": - return ldUrl; - default: - return coin.lwd - .firstWhere((lwd) => lwd.name == ldUrlChoice, - orElse: () => coin.lwd.first) - .url; - } - } - - void updateLWD() { - WarpApi.updateLWD(getLWD()); + active.fetchChartData(); } @action @@ -300,8 +355,8 @@ abstract class _Settings with Store { final prefs = await SharedPreferences.getInstance(); currency = newCurrency; prefs.setString('currency', currency); - await priceStore.fetchZecPrice(); - await accountManager.fetchChartData(); + await priceStore.fetchCoinPrice(active.coin); + await active.fetchChartData(); } @action @@ -360,671 +415,133 @@ abstract class _Settings with Store { } } -class AccountManager = _AccountManager with _$AccountManager; - -abstract class _AccountManager with Store { - late Database db; - - @observable - Account active = Account(0, "", "", 0, null); - - @observable - bool canPay = false; - - @observable - int balance = 0; - - @observable - int unconfirmedBalance = 0; - - @observable - String taddress = ""; - - @observable - bool showTAddr = false; - - @observable - int tbalance = 0; - - @observable - List notes = []; - - @observable - List txs = []; - - @observable - int lastTxHeight = 0; - - @observable - int dataEpoch = 0; - - @observable - List spendings = []; - - @observable - List> accountBalances = []; - - @observable - List pnls = []; - - @observable - List accounts = []; - - @observable - SortConfig noteSortConfig = SortConfig("", SortOrder.Unsorted); - - @observable - SortConfig txSortConfig = SortConfig("", SortOrder.Unsorted); - - @observable - int pnlSeriesIndex = 0; - - @observable - bool pnlDesc = false; - - Future init(Database db) async { - this.db = db; - await resetToDefaultAccount(); - } - - Future resetToDefaultAccount() async { - await refresh(); - if (accounts.isNotEmpty) { - final prefs = await SharedPreferences.getInstance(); - final account = prefs.getInt('account') ?? accounts[0].id; - setActiveAccountId(account); - } - } - - refresh() async { - accounts = await _list(); - } - - @action - Future setActiveAccount(Account account) async { - final prefs = await SharedPreferences.getInstance(); - prefs.setInt('account', account.id); - final List res1 = await db.rawQuery( - "SELECT address FROM taddrs WHERE account = ?1", [account.id]); - taddress = res1.isNotEmpty ? res1[0]['address'] : ""; - showTAddr = false; - - WarpApi.setMempoolAccount(account.id); - final List res2 = await db.rawQuery( - "SELECT sk FROM accounts WHERE id_account = ?1", [account.id]); - canPay = res2.isNotEmpty && res2[0]['sk'] != null; - active = account; - - balance = 0; - tbalance = 0; - await _fetchData(account.id, true); - } - - @action - Future setActiveAccountId(int idAccount) async { - final account = accounts.firstWhere((account) => account.id == idAccount, - orElse: () => accounts[0]); - await setActiveAccount(account); - } - - String newAddress() { - return WarpApi.newAddress(active.id); - } - - Future getBackup(int account) async { - final List res = await db.rawQuery( - "SELECT name, seed, sk, ivk FROM accounts WHERE id_account = ?1", - [account]); - if (res.isEmpty) throw Exception("Account N/A"); - final share = await getShareInfo(account); - final row = res[0]; - final name = row['name']; - final seed = row['seed']; - final sk = row['sk']; - final ivk = row['ivk']; - int type = 0; - if (seed != null) - type = 0; - else if (sk != null) - type = 1; - else if (ivk != null) type = 2; - return Backup(type, name, seed, sk, ivk, share); - } - - Future _getBalance(int accountId) async { - final List res = await db.rawQuery( - "SELECT SUM(value) AS value FROM received_notes WHERE account = ?1 AND (spent IS NULL OR spent = 0)", - [accountId]); - if (res.isEmpty) return 0; - return res[0]['value'] ?? 0; - } - - Future getShieldedBalance() async { - return Sqflite.firstIntValue(await db.rawQuery( - "SELECT SUM(value) AS value FROM received_notes WHERE account = ?1 AND spent IS NULL", - [active.id])) ?? - 0; - } - - Future getUnconfirmedSpentBalance() async { - return Sqflite.firstIntValue(await db.rawQuery( - "SELECT SUM(value) AS value FROM received_notes WHERE account = ?1 AND spent = 0", - [active.id])) ?? - 0; - } - - Future getUnderConfirmedBalance() async { - final height = syncStatus.latestHeight - settings.anchorOffset; - return Sqflite.firstIntValue(await db.rawQuery( - "SELECT SUM(value) AS value FROM received_notes WHERE account = ?1 AND height > ?2", - [active.id, height])) ?? - 0; - } - - Future getExcludedBalance() async { - final height = syncStatus.latestHeight - settings.anchorOffset; - final amount = Sqflite.firstIntValue(await db.rawQuery( - "SELECT SUM(value) FROM received_notes WHERE account = ?1 AND spent IS NULL " - "AND height <= ?2 AND excluded", - [active.id, height])) ?? - 0; - return amount; - } - - @action - Future updateUnconfirmedBalance() async { - unconfirmedBalance = await WarpApi.mempoolSync(); - } - - isEmpty() async { - final List res = await db.rawQuery("SELECT name FROM accounts", []); - return res.isEmpty; - } - - Future> _list() async { - final List res = await db.rawQuery( - "WITH notes AS (SELECT a.id_account, a.name, a.address, CASE WHEN r.spent IS NULL THEN r.value ELSE 0 END AS nv FROM accounts a LEFT JOIN received_notes r ON a.id_account = r.account)," - "accounts2 AS (SELECT id_account, name, address, COALESCE(sum(nv), 0) AS balance FROM notes GROUP by id_account) " - "SELECT a.id_account, a.name, a.address, a.balance, ss.idx, ss.secret, ss.participants, ss.threshold FROM accounts2 a LEFT JOIN secret_shares ss ON a.id_account = ss.account", - []); - return res.map((r) { - final shareInfo = r['secret'] != null - ? ShareInfo(r['idx'], r['threshold'], r['participants'], r['secret']) - : null; - return Account( - r['id_account'], r['name'], r['address'], r['balance'], shareInfo); - }).toList(); - } - - @action - Future delete(int account) async { - WarpApi.deleteAccount(account); - if (account == active.id) resetToDefaultAccount(); - } - - @action - Future changeAccountName(Account account, String name) async { - await db.execute("UPDATE accounts SET name = ?2 WHERE id_account = ?1", - [account.id, name]); - await refresh(); - } - - @action - void storeShareSecret(int account, String secretKey) { - WarpApi.storeShareSecret(account, secretKey); - } - - Future getShareInfo(int accountId) async { - final List res = await db.rawQuery( - "SELECT idx, threshold, participants, secret FROM secret_shares WHERE account = ?1", - [accountId]); - if (res.isEmpty) return null; - final row = res[0]; - return ShareInfo( - row['idx'], row['threshold'], row['participants'], row['secret']); - } - - @action - Future updateBalance() async { - balance = await _getBalance(active.id); - } - - @action - Future fetchAccountData(bool force) async { - await _fetchData(active.id, force); - } - - @action - void toggleShowTAddr() { - showTAddr = !showTAddr; - } - - Future _fetchData(int accountId, bool force) async { - await _updateBalance(accountId); - await _updateTBalance(accountId); - - final hasNewTx = await _fetchNotesAndHistory(accountId, force); - int countNewPrices = await WarpApi.syncHistoricalPrices(settings.currency); - if (hasNewTx) { - await _fetchSpending(accountId); - await _fetchAccountBalanceTimeSeries(accountId); - } - if (countNewPrices > 0 || pnls.isEmpty || hasNewTx) - await _fetchPNL(accountId); - } - - final DateFormat noteDateFormat = DateFormat("yy-MM-dd HH:mm"); - final DateFormat txDateFormat = DateFormat("MM-dd HH:mm"); - - Future _updateBalance(int accountId) async { - final _balance = await _getBalance(accountId); - if (_balance == balance) return; - balance = _balance; - dataEpoch = DateTime.now().millisecondsSinceEpoch; - } - - Future _fetchNotesAndHistory(int accountId, bool force) async { - final List res0 = await db.rawQuery( - "SELECT MAX(height) as height FROM transactions WHERE account = ?1", - [accountId]); - if (res0.isEmpty) return false; - - final _lastTxHeight = res0[0]['height'] ?? 0; - if (!force && lastTxHeight == _lastTxHeight) return false; - lastTxHeight = _lastTxHeight; - - final List res = await db.rawQuery( - "SELECT n.id_note, n.height, n.value, t.timestamp, n.excluded, n.spent FROM received_notes n, transactions t " - "WHERE n.account = ?1 AND (n.spent IS NULL OR n.spent = 0) " - "AND n.tx = t.id_tx ORDER BY n.height DESC", - [accountId]); - notes = res.map((row) { - final id = row['id_note']; - final height = row['height']; - final timestamp = noteDateFormat - .format(DateTime.fromMillisecondsSinceEpoch(row['timestamp'] * 1000)); - final excluded = (row['excluded'] ?? 0) != 0; - final spent = row['spent'] == 0; - return Note( - id, height, timestamp, row['value'] / ZECUNIT, excluded, spent); - }).toList(); - - final List res2 = await db.rawQuery( - "SELECT id_tx, txid, height, timestamp, t.address, c.name, value, memo FROM transactions t " - "LEFT JOIN contacts c ON t.address = c.address WHERE account = ?1 ORDER BY height DESC", - [accountId]); - txs = res2.map((row) { - Uint8List txid = row['txid']; - final fullTxId = hex.encode(txid.reversed.toList()); - final shortTxid = fullTxId.substring(0, 8); - final timestamp = txDateFormat - .format(DateTime.fromMillisecondsSinceEpoch(row['timestamp'] * 1000)); - return Tx( - row['id_tx'], - row['height'], - timestamp, - shortTxid, - fullTxId, - row['value'] / ZECUNIT, - row['address'] ?? "", - row['name'], - row['memo'] ?? ""); - }).toList(); - - dataEpoch = DateTime.now().millisecondsSinceEpoch; - return true; - } - - @computed - List get sortedNotes { - var notes2 = [...notes]; - switch (noteSortConfig.field) { - case "time": - return _sort(notes2, (Note note) => note.height, noteSortConfig.order); - case "amount": - return _sort(notes2, (Note note) => note.value, noteSortConfig.order); - } - return notes2; - } - - @action - void sortNotes(String field) { - noteSortConfig = noteSortConfig.sortOn(field); - } - - @computed - List get sortedTxs { - var txs2 = [...txs]; - switch (txSortConfig.field) { - case "time": - return _sort(txs2, (Tx tx) => tx.height, txSortConfig.order); - case "amount": - return _sort(txs2, (Tx tx) => tx.value, txSortConfig.order); - case "txid": - return _sort(txs2, (Tx tx) => tx.txid, txSortConfig.order); - case "address": - return _sort( - txs2, (Tx tx) => tx.contact ?? tx.address, txSortConfig.order); - case "memo": - return _sort(txs2, (Tx tx) => tx.memo, txSortConfig.order); - } - return txs2; - } - - @action - void sortTx(String field) { - txSortConfig = txSortConfig.sortOn(field); - } - - List _sort( - List txs, T Function(C) project, SortOrder order) { - switch (order) { - case SortOrder.Ascending: - txs.sort((a, b) => project(a).compareTo(project(b))); - break; - case SortOrder.Descending: - txs.sort((a, b) => -project(a).compareTo(project(b))); - break; - case SortOrder.Unsorted: - txs.sort((a, b) => -a.height.compareTo(b.height)); - break; - } - return txs; - } - - TimeRange getChartRange() { - final now = DateTime.now().toUtc(); - final today = DateTime.utc(now.year, now.month, now.day); - final start = today.add(Duration(days: -chartRangeInt())); - final cutoff = start.millisecondsSinceEpoch; - return TimeRange(cutoff, today.millisecondsSinceEpoch); - } - - Future _fetchSpending(int accountId) async { - final range = getChartRange(); - final List res = await db.rawQuery( - "SELECT SUM(value) as v, t.address, c.name FROM transactions t LEFT JOIN contacts c ON t.address = c.address " - "WHERE account = ?1 AND timestamp >= ?2 AND value < 0 GROUP BY t.address ORDER BY v ASC LIMIT 5", - [accountId, range.start ~/ 1000]); - spendings = res.map((row) { - final address = row['address'] ?? ""; - final value = -row['v'] / ZECUNIT; - final contact = row['name']; - return Spending(address, value, contact); - }).toList(); - } - - Future _fetchAccountBalanceTimeSeries(int accountId) async { - final range = getChartRange(); - final List res = await db.rawQuery( - "SELECT timestamp, value FROM transactions WHERE account = ?1 AND timestamp >= ?2 ORDER BY timestamp DESC", - [accountId, range.start ~/ 1000]); - List _accountBalances = []; - var b = balance; - _accountBalances.add(AccountBalance(DateTime.now(), b / ZECUNIT)); - for (var row in res) { - final timestamp = - DateTime.fromMillisecondsSinceEpoch(row['timestamp'] * 1000); - final value = row['value'] as int; - final ab = AccountBalance(timestamp, b / ZECUNIT); - _accountBalances.add(ab); - b -= value; - } - _accountBalances.add(AccountBalance( - DateTime.fromMillisecondsSinceEpoch(range.start), b / ZECUNIT)); - _accountBalances = _accountBalances.reversed.toList(); - accountBalances = sampleDaily( - _accountBalances, - range.start, - range.end, - (AccountBalance ab) => ab.time.millisecondsSinceEpoch ~/ DAY_MS, - (AccountBalance ab) => ab.balance, - (acc, v) => v, - 0.0); - } - - @action - Future fetchChartData() async { - await _fetchPNL(active.id); - await _fetchSpending(active.id); - await _fetchAccountBalanceTimeSeries(active.id); - } - - int chartRangeInt() { - switch (settings.chartRange) { - case '1M': - return 30; - case '3M': - return 90; - case '6M': - return 180; - } - return 365; - } - - Future _fetchPNL(int accountId) async { - final range = getChartRange(); - - final List res1 = await db.rawQuery( - "SELECT timestamp, value FROM transactions WHERE timestamp >= ?2 AND account = ?1", - [accountId, range.start ~/ 1000]); - final List trades = []; - for (var row in res1) { - final dt = DateTime.fromMillisecondsSinceEpoch(row['timestamp'] * 1000); - final qty = row['value'] / ZECUNIT; - trades.add(Trade(dt, qty)); - } - - final portfolioTimeSeries = sampleDaily( - trades, - range.start, - range.end, - (t) => t.dt.millisecondsSinceEpoch ~/ DAY_MS, - (t) => t, - (acc, t) => acc + t.qty, - 0.0); - - final List res2 = await db.rawQuery( - "SELECT timestamp, price FROM historical_prices WHERE timestamp >= ?2 AND currency = ?1", - [settings.currency, range.start ~/ 1000]); - final List quotes = []; - for (var row in res2) { - final dt = DateTime.fromMillisecondsSinceEpoch(row['timestamp'] * 1000); - final price = row['price']; - quotes.add(Quote(dt, price)); - } - - var prevBalance = 0.0; - var cash = 0.0; - var realized = 0.0; - final List _pnls = []; - final len = math.min(quotes.length, portfolioTimeSeries.length); - for (var i = 0; i < len; i++) { - final dt = quotes[i].dt; - final price = quotes[i].price; - final balance = portfolioTimeSeries[i].value; - final qty = balance - prevBalance; - - final closeQty = qty * balance < 0 - ? math.min(qty.abs(), prevBalance.abs()) * qty.sign - : 0.0; - final openQty = qty - closeQty; - final avgPrice = prevBalance != 0 ? cash / prevBalance : 0.0; - - cash += openQty * price + closeQty * avgPrice; - realized += closeQty * (avgPrice - price); - final unrealized = price * balance - cash; - - final pnl = PnL(dt, price, balance, realized, unrealized); - _pnls.add(pnl); - - prevBalance = balance; - } - pnls = _pnls; - } - - @action - void togglePnlDesc() { - pnlDesc = !pnlDesc; - } - - @computed - List get pnlSorted { - if (pnlDesc) { - var _pnls = [...pnls.reversed]; - return _pnls; - } - return pnls; - } - - @action - Future convertToWatchOnly() async { - await db.rawUpdate( - "UPDATE accounts SET seed = NULL, sk = NULL WHERE id_account = ?1", - [active.id]); - canPay = false; - } - - @action - Future excludeNote(Note note) async { - await db.execute( - "UPDATE received_notes SET excluded = ?2 WHERE id_note = ?1", - [note.id, note.excluded]); - } - - @action - Future invertExcludedNotes() async { - await db.execute( - "UPDATE received_notes SET excluded = NOT(COALESCE(excluded, 0)) WHERE account = ?1", - [active.id]); - notes = notes.map((n) => n.invertExcluded).toList(); - } - - @action - Future updateTBalance() async { - _updateTBalance(active.id); - } - - _updateTBalance(int accountId) { - int balance = WarpApi.getTBalance(accountId); - if (balance != tbalance) tbalance = balance; - } - - void autoshield() { - if (settings.autoShieldThreshold != 0.0 && - tbalance / ZECUNIT >= settings.autoShieldThreshold) { - WarpApi.shieldTAddr(active.id); - } - } - - @action - void setPnlSeriesIndex(int index) { - pnlSeriesIndex = index; - } - - Future> getAllTBalances() async { - final Map balances = {}; - for (var a in accounts) { - final b = await WarpApi.getTBalanceAsync(a.id); - balances[a.id] = b; - } - - return balances; - } -} - -class Account { - final int id; - final String name; - final String address; - final int balance; - final ShareInfo? share; - - Account(this.id, this.name, this.address, this.balance, this.share); -} - class PriceStore = _PriceStore with _$PriceStore; abstract class _PriceStore with Store { @observable - double zecPrice = 0.0; + double coinPrice = 0.0; + + int? lastChartUpdateTime; @action - Future fetchZecPrice() async { + Future fetchCoinPrice(int coin) async { + final c = settings.coins[coin].def; final base = "api.coingecko.com"; final uri = Uri.https(base, '/api/v3/simple/price', - {'ids': coin.currency, 'vs_currencies': settings.currency}); + {'ids': c.currency, 'vs_currencies': settings.currency}); final rep = await http.get(uri); if (rep.statusCode == 200) { final json = convert.jsonDecode(rep.body) as Map; - final p = json[coin.currency][settings.currency.toLowerCase()]; - zecPrice = (p is double) ? p : (p as int).toDouble(); + final p = json[c.currency][settings.currency.toLowerCase()]; + coinPrice = (p is double) ? p : (p as int).toDouble(); } else - zecPrice = 0.0; + coinPrice = 0.0; + } + + Future updateChart() async { + final _lastChartUpdateTime = lastChartUpdateTime; + final now = DateTime.now().millisecondsSinceEpoch ~/ 1000; + if (_lastChartUpdateTime == null || now > _lastChartUpdateTime + 5 * 60) { + await fetchCoinPrice(active.coin); + await WarpApi.syncHistoricalPrices(active.coin, settings.currency); + await active.fetchChartData(); + lastChartUpdateTime = _lastChartUpdateTime; + } } } class SyncStatus = _SyncStatus with _$SyncStatus; abstract class _SyncStatus with Store { - late Database _db; - - init() async { - var databasesPath = await getDatabasesPath(); - final path = join(databasesPath, 'zec.db'); - _db = await openDatabase(path); - await update(); - } - @observable - bool accountRestored = false; - - @observable - bool syncing = false; - - @observable - int syncedHeight = -1; + int? syncedHeight; @observable int latestHeight = 0; + bool accountRestored = false; + bool syncing = false; + bool isSynced() { - return syncedHeight < 0 || syncedHeight == latestHeight; + final sh = syncedHeight; + return sh != null && sh >= latestHeight; + } + + int get confirmHeight { + final ch = latestHeight - settings.anchorOffset; + return max(ch, 0); } @action - setSyncHeight(int height) { + setSyncHeight(int? height) { syncedHeight = height; } @action - Future update() async { - latestHeight = await WarpApi.getLatestHeight(); - final _syncedHeight = Sqflite.firstIntValue( - await _db.rawQuery("SELECT MAX(height) FROM blocks")) ?? - 0; - if (_syncedHeight > 0) setSyncHeight(_syncedHeight); - return syncedHeight == latestHeight; + void markAsSynced(int coin) { + WarpApi.skipToLastHeight(coin); + } + + Future getDbSyncedHeight() async { + final db = active.coinDef.db; + final syncedHeight = Sqflite.firstIntValue( + await db.rawQuery("SELECT MAX(height) FROM blocks")); + return syncedHeight; } @action - Future sync(BuildContext context) async { - eta.reset(); - syncing = true; - final snackBar = SnackBar(content: Text(S.of(context).rescanRequested)); - rootScaffoldMessengerKey.currentState?.showSnackBar(snackBar); - setSyncHeight(0); - WarpApi.rewindToHeight(0); - WarpApi.truncateData(); - contacts.markContactsDirty(false); + Future update() async { + latestHeight = await WarpApi.getLatestHeight(active.coin); + final _syncedHeight = await getDbSyncedHeight(); + // if syncedHeight = 0, we just started sync therefore don't set it back to null + if (syncedHeight != 0 || _syncedHeight != null) setSyncHeight( + _syncedHeight); + return latestHeight > 0 && syncedHeight == latestHeight; + } + + @action + Future sync() async { + if (syncing) return; await syncStatus.update(); - final params = - SyncParams(settings.getTx, settings.anchorOffset, syncPort.sendPort); - await compute(WarpApi.warpSync, params); + if (syncedHeight == null) return; + syncing = true; + final currentSyncedHeight = syncedHeight; + if (!isSynced()) { + final params = SyncParams( + active.coin, settings.getTx, settings.anchorOffset, + syncPort.sendPort); + final res = await compute(WarpApi.warpSync, params); + if (res == 0) { + if (currentSyncedHeight != syncedHeight) { + await active.update(); + await priceStore.updateChart(); + await contacts.fetchContacts(); + } + } + else if (res == 1) { // Reorg + final _syncedHeight = await getDbSyncedHeight(); + if (_syncedHeight != null) { + final rewindHeight = max(_syncedHeight - 20, 0); + print("Block reorg detected. Rewind to $rewindHeight"); + WarpApi.rewindToHeight(active.coin, rewindHeight); + } + } + } syncing = false; eta.reset(); } + @action + Future rescan(BuildContext context) async { + eta.reset(); + final snackBar = SnackBar(content: Text(S.of(context).rescanRequested)); + rootScaffoldMessengerKey.currentState?.showSnackBar(snackBar); + syncedHeight = 0; + WarpApi.rewindToHeight(active.coin, 0); + WarpApi.truncateData(active.coin); + await sync(); + } + @action void setAccountRestored(bool v) { accountRestored = v; @@ -1033,7 +550,7 @@ abstract class _SyncStatus with Store { @action void setSyncedToLatestHeight() { setSyncHeight(latestHeight); - WarpApi.skipToLastHeight(); + WarpApi.skipToLastHeight(active.coin); } } @@ -1102,26 +619,12 @@ abstract class _ETAStore with Store { class ContactStore = _ContactStore with _$ContactStore; abstract class _ContactStore with Store { - late Database db; - - @observable - bool dirty = false; - @observable ObservableList contacts = ObservableList.of([]); - Future init(Database db) async { - this.db = db; - final prefs = await SharedPreferences.getInstance(); - dirty = prefs.getBool('contacts_dirty') ?? false; - } - @action Future fetchContacts() async { - await _fetchContacts(); - } - - Future _fetchContacts() async { + final db = active.coinDef.db; List res = await db.rawQuery( "SELECT id, name, address FROM contacts WHERE address <> '' ORDER BY name"); contacts.clear(); @@ -1131,26 +634,28 @@ abstract class _ContactStore with Store { } } - @action - Future markContactsDirty(bool v) async { - final prefs = await SharedPreferences.getInstance(); - dirty = v; - prefs.setBool('contacts_dirty', dirty); - } - @action Future add(Contact c) async { - WarpApi.storeContact(c.id, c.name, c.address, true); - await markContactsDirty(true); - await _fetchContacts(); + WarpApi.storeContact(active.coin, c.id, c.name, c.address, true); + markContactsSaved(active.coin, false); + await fetchContacts(); } @action Future remove(Contact c) async { contacts.removeWhere((contact) => contact.id == c.id); - WarpApi.storeContact(c.id, c.name, "", true); - await markContactsDirty(true); - await _fetchContacts(); + WarpApi.storeContact(active.coin, c.id, c.name, "", true); + markContactsSaved(active.coin, false); + await fetchContacts(); + } + + markContactsSaved(int coin, bool v) { + settings.coins[coin].contactsSaved = v; + Future.microtask(() async { + final prefs = await SharedPreferences.getInstance(); + final c = settings.coins[coin].def; + prefs.setBool("${c.ticker}.contacts_saved", v); + }); } } @@ -1220,11 +725,12 @@ class Backup { final int type; final String name; final String? seed; + final int index; final String? sk; final String ivk; final ShareInfo? share; - Backup(this.type, this.name, this.seed, this.sk, this.ivk, this.share); + Backup(this.type, this.name, this.seed, this.index, this.sk, this.ivk, this.share); String value() { switch (type) { diff --git a/lib/theme_editor.dart b/lib/theme_editor.dart index a00de71..d40bde8 100644 --- a/lib/theme_editor.dart +++ b/lib/theme_editor.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_colorpicker/flutter_colorpicker.dart'; -import 'package:warp/main.dart'; +import 'main.dart'; import 'generated/l10n.dart'; diff --git a/lib/transaction.dart b/lib/transaction.dart index cdef2b1..d015959 100644 --- a/lib/transaction.dart +++ b/lib/transaction.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher.dart'; import 'main.dart'; +import 'settings.dart'; import 'store.dart'; import 'generated/l10n.dart'; @@ -41,6 +42,6 @@ class TransactionState extends State { } _onOpen() { - launch("${coin.explorerUrl}${widget.tx.fullTxId}"); + launch("${activeCoin().explorerUrl}${widget.tx.fullTxId}"); } } diff --git a/native/warp_api_ffi/binding.h b/native/warp_api_ffi/binding.h index 8796715..57dcb49 100644 --- a/native/warp_api_ffi/binding.h +++ b/native/warp_api_ffi/binding.h @@ -1,5 +1,6 @@ #ifndef __APPLE__ typedef char int8_t; +typedef unsigned char uint8_t; typedef short int uint16_t; typedef long long int int64_t; typedef long long int uint64_t; @@ -10,76 +11,78 @@ typedef char bool; #endif typedef void *DartPostCObjectFnType; -void init_wallet(char *db_path, char *ld_url); +void init_wallet(char *db_path); void reset_app(void); -void warp_sync(bool get_tx, uint32_t anchor_offset, int64_t port); +int8_t warp_sync(uint8_t coin, bool get_tx, uint32_t anchor_offset, int64_t port); void dart_post_cobject(DartPostCObjectFnType ptr); -uint32_t get_latest_height(void); +uint32_t get_latest_height(uint8_t coin); -int8_t is_valid_key(char *seed); +int8_t is_valid_key(uint8_t coin, char *seed); -bool valid_address(char *address); +bool valid_address(uint8_t coin, char *address); -char *new_address(uint32_t account); +char *new_address(uint8_t coin, uint32_t account); -void set_mempool_account(uint32_t account); +void set_mempool_account(uint8_t coin, uint32_t account); -int32_t new_account(char *name, char *data); +int32_t new_account(uint8_t coin, char *name, char *data, uint32_t index); -int64_t get_mempool_balance(void); +int32_t new_sub_account(uint8_t coin, uint32_t id, char *name); -const char *send_multi_payment(uint32_t account, +int64_t get_mempool_balance(uint8_t coin); + +const char *send_multi_payment(uint8_t coin, + uint32_t account, char *recipients_json, uint32_t anchor_offset, bool use_transparent, int64_t port); -int8_t try_warp_sync(bool get_tx, uint32_t anchor_offset); +void skip_to_last_height(uint8_t coin); -void skip_to_last_height(void); +void rewind_to_height(uint8_t coin, uint32_t height); -void rewind_to_height(uint32_t height); +int64_t mempool_sync(uint8_t coin); -int64_t mempool_sync(void); +void mempool_reset(uint8_t coin, uint32_t height); -void mempool_reset(uint32_t height); +uint64_t get_taddr_balance(uint8_t coin, uint32_t account); -uint64_t get_taddr_balance(uint32_t account); +char *shield_taddr(uint8_t coin, uint32_t account); -char *shield_taddr(uint32_t account); +void set_lwd_url(uint8_t coin, char *url); -void set_lwd_url(char *url); - -char *prepare_multi_payment(uint32_t account, +char *prepare_multi_payment(uint8_t coin, + uint32_t account, char *recipients_json, bool use_transparent, uint32_t anchor_offset); -char *broadcast(char *tx_filename); +char *broadcast(uint8_t coin, char *tx_filename); -char *broadcast_txhex(char *txhex); +char *broadcast_txhex(uint8_t coin, char *txhex); -uint32_t sync_historical_prices(int64_t now, uint32_t days, char *currency); +uint32_t sync_historical_prices(uint8_t coin, int64_t now, uint32_t days, char *currency); char *get_ua(char *sapling_addr, char *transparent_addr); char *get_sapling(char *ua_addr); -void store_contact(uint32_t id, char *name, char *address, bool dirty); +void store_contact(uint8_t coin, uint32_t id, char *name, char *address, bool dirty); -char *commit_unsaved_contacts(uint32_t account, uint32_t anchor_offset); +char *commit_unsaved_contacts(uint8_t coin, uint32_t account, uint32_t anchor_offset); -void delete_account(uint32_t account); +void delete_account(uint8_t coin, uint32_t account); -void truncate_data(void); +void truncate_data(uint8_t coin); -char *make_payment_uri(char *address, uint64_t amount, char *memo); +char *make_payment_uri(uint8_t coin, char *address, uint64_t amount, char *memo); -char *parse_payment_uri(char *uri); +char *parse_payment_uri(uint8_t coin, char *uri); char *generate_random_enc_key(void); @@ -87,9 +90,9 @@ char *get_full_backup(char *key); char *restore_full_backup(char *key, char *backup); -void store_share_secret(uint32_t account, char *secret); +void store_share_secret(uint8_t coin, uint32_t account, char *secret); -char *get_share_secret(uint32_t account); +char *get_share_secret(uint8_t coin, uint32_t account); void run_aggregator(char *secret_share, uint16_t port, int64_t send_port); @@ -104,6 +107,6 @@ uint32_t run_multi_signer(char *address, char *my_url, uint16_t port); -char *split_account(uint32_t threshold, uint32_t participants, uint32_t account); +char *split_account(uint8_t coin, uint32_t threshold, uint32_t participants, uint32_t account); void dummy_export(void); diff --git a/native/warp_api_ffi/binding2.h b/native/warp_api_ffi/binding2.h index 68459ea..51d8733 100644 --- a/native/warp_api_ffi/binding2.h +++ b/native/warp_api_ffi/binding2.h @@ -1,5 +1,6 @@ #ifndef __APPLE__ typedef char int8_t; +typedef unsigned char uint8_t; typedef short int uint16_t; typedef long long int int64_t; typedef long long int uint64_t; diff --git a/native/warp_api_ffi/src/api.rs b/native/warp_api_ffi/src/api.rs index 7709e53..d78a645 100644 --- a/native/warp_api_ffi/src/api.rs +++ b/native/warp_api_ffi/src/api.rs @@ -6,7 +6,10 @@ use once_cell::sync::OnceCell; use std::fs::File; use std::io::Read; use std::sync::{Mutex, MutexGuard}; -use sync::{broadcast_tx, ChainError, MemPool, Wallet}; +use sync::{ + broadcast_tx, decrypt_backup, encrypt_backup, get_coin_type, ChainError, CoinType, KeyHelpers, + MemPool, Wallet, +}; use tokio::runtime::Runtime; use tokio::time::Duration; use zcash_multisig::{ @@ -16,8 +19,10 @@ use zcash_multisig::{ use zcash_primitives::transaction::builder::Progress; static RUNTIME: OnceCell> = OnceCell::new(); -static WALLET: OnceCell> = OnceCell::new(); -static MEMPOOL: OnceCell> = OnceCell::new(); +static YWALLET: OnceCell> = OnceCell::new(); +static ZWALLET: OnceCell> = OnceCell::new(); +static YMEMPOOL: OnceCell> = OnceCell::new(); +static ZMEMPOOL: OnceCell> = OnceCell::new(); static SYNCLOCK: OnceCell> = OnceCell::new(); static MULTISIG_AGG_LOCK: OnceCell> = OnceCell::new(); static MULTISIG_SIGN_LOCK: OnceCell> = OnceCell::new(); @@ -53,18 +58,28 @@ fn log_result_string(result: anyhow::Result) -> String { } } -pub fn init_wallet(db_path: &str, ld_url: &str) { +pub fn init_wallet(db_path: &str) { android_logger::init_once(Config::default().with_min_level(Level::Info)); info!("Init"); RUNTIME.get_or_init(|| Mutex::new(Runtime::new().unwrap())); - WALLET.get_or_init(|| { - info!("Wallet Init"); - let wallet = Wallet::new(db_path, ld_url); + YWALLET.get_or_init(|| { + info!("YWallet Init"); + let wallet = Wallet::new(CoinType::Ycash, &format!("{}/yec.db", db_path)); Mutex::new(wallet) }); - MEMPOOL.get_or_init(|| { - info!("Mempool Init"); - let mempool = MemPool::new(db_path, ld_url); + ZWALLET.get_or_init(|| { + info!("ZWallet Init"); + let wallet = Wallet::new(CoinType::Zcash, &format!("{}/zec.db", db_path)); + Mutex::new(wallet) + }); + YMEMPOOL.get_or_init(|| { + info!("YMempool Init"); + let mempool = MemPool::new(CoinType::Ycash, &format!("{}/yec.db", db_path)); + Mutex::new(mempool) + }); + ZMEMPOOL.get_or_init(|| { + info!("ZMempool Init"); + let mempool = MemPool::new(CoinType::Zcash, &format!("{}/zec.db", db_path)); Mutex::new(mempool) }); SYNCLOCK.get_or_init(|| Mutex::new(())); @@ -74,21 +89,48 @@ pub fn init_wallet(db_path: &str, ld_url: &str) { pub fn reset_app() { let res = || { - let wallet = get_lock(&WALLET)?; - wallet.reset_db() + let wallet = get_lock(&YWALLET)?; + wallet.reset_db()?; + let wallet = get_lock(&ZWALLET)?; + wallet.reset_db()?; + Ok(()) }; log_result(res()) } -pub fn new_account(name: &str, data: &str) -> i32 { +pub fn new_account(coin: u8, name: &str, data: &str, index: u32) -> i32 { let res = || { - let wallet = get_lock(&WALLET)?; - wallet.new_account(name, data) + let wallet = get_wallet_lock(coin)?; + wallet.new_account(name, data, index) }; log_result(res()) } +pub fn new_sub_account(coin: u8, id: u32, name: &str) -> i32 { + let res = || { + let wallet = get_wallet_lock(coin)?; + let id = wallet.new_sub_account(id, name)?; + Ok(id) + }; + log_result(res()) +} + +fn get_wallet_lock(coin: u8) -> anyhow::Result> { + match coin { + 1 => get_lock(&YWALLET), + _ => get_lock(&ZWALLET), + } +} + +fn get_mempool_lock(coin: u8) -> anyhow::Result> { + match coin { + 1 => get_lock(&YMEMPOOL), + _ => get_lock(&ZMEMPOOL), + } +} + async fn warp( + coin: u8, get_tx: bool, anchor_offset: u32, db_path: &str, @@ -96,7 +138,9 @@ async fn warp( port: i64, ) -> anyhow::Result<()> { info!("Sync started"); + let coin_type = get_coin_type(coin); Wallet::sync_ex( + coin_type, get_tx, anchor_offset, &db_path, @@ -114,7 +158,7 @@ async fn warp( ) .await?; info!("Sync finished"); - let mut mempool = get_lock(&MEMPOOL)?; + let mut mempool = get_mempool_lock(coin)?; mempool.scan().await?; Ok(()) } @@ -136,60 +180,68 @@ fn convert_sync_result(result: anyhow::Result<()>) -> i8 { } } -pub fn warp_sync(get_tx: bool, anchor_offset: u32, port: i64) -> i8 { +pub fn warp_sync(coin: u8, get_tx: bool, anchor_offset: u32, port: i64) -> i8 { let r = get_runtime(); let res = r.block_on(async { android_logger::init_once(Config::default().with_min_level(Level::Info)); let _sync_lock = get_lock(&SYNCLOCK)?; - let wallet = get_lock(&WALLET)?; + let wallet = get_wallet_lock(coin)?; let db_path = wallet.db_path.clone(); let ld_url = wallet.ld_url.clone(); drop(wallet); - warp(get_tx, anchor_offset, &db_path, &ld_url, port).await?; + warp(coin, get_tx, anchor_offset, &db_path, &ld_url, port).await?; Ok::<_, anyhow::Error>(()) }); convert_sync_result(res) } -pub fn try_warp_sync(get_tx: bool, anchor_offset: u32) -> i8 { - let r = get_runtime(); - let res = r.block_on(async { - android_logger::init_once(Config::default().with_min_level(Level::Info)); - let _sync_lock = SYNCLOCK - .get() - .ok_or_else(|| anyhow::anyhow!("Lock not initialized"))? - .try_lock(); - if _sync_lock.is_ok() { - let wallet = get_lock(&WALLET)?; - let db_path = wallet.db_path.clone(); - let ld_url = wallet.ld_url.clone(); - drop(wallet); - warp(get_tx, anchor_offset, &db_path, &ld_url, 0).await?; - Ok::<_, anyhow::Error>(()) - } else { - Err(anyhow::anyhow!(ChainError::Busy)) - } - }); - convert_sync_result(res) +// pub fn try_warp_sync(coin: u8, get_tx: bool, anchor_offset: u32) -> i8 { +// let r = get_runtime(); +// let res = r.block_on(async { +// android_logger::init_once(Config::default().with_min_level(Level::Info)); +// let _sync_lock = SYNCLOCK +// .get() +// .ok_or_else(|| anyhow::anyhow!("Lock not initialized"))? +// .try_lock(); +// if _sync_lock.is_ok() { +// let wallet = get_wallet_lock(coin)?; +// let db_path = wallet.db_path.clone(); +// let ld_url = wallet.ld_url.clone(); +// drop(wallet); +// warp(coin, get_tx, anchor_offset, &db_path, &ld_url, 0).await?; +// Ok::<_, anyhow::Error>(()) +// } else { +// Err(anyhow::anyhow!(ChainError::Busy)) +// } +// }); +// convert_sync_result(res) +// } + +pub fn is_valid_key(coin: u8, seed: &str) -> i8 { + let coin_type = get_coin_type(coin); + let kh = KeyHelpers::new(coin_type); + kh.is_valid_key(seed) } -pub fn valid_address(address: &str) -> bool { - Wallet::valid_address(address) +pub fn valid_address(coin: u8, address: &str) -> bool { + let coin_type = get_coin_type(coin); + let kh = KeyHelpers::new(coin_type); + kh.valid_address(address) } -pub fn new_address(account: u32) -> String { +pub fn new_address(coin: u8, account: u32) -> String { let res = || { - let wallet = get_lock(&WALLET)?; + let wallet = get_wallet_lock(coin)?; wallet.new_diversified_address(account) }; log_result(res()) } -pub fn get_latest_height() -> u32 { +pub fn get_latest_height(coin: u8) -> u32 { let r = get_runtime(); let res = r.block_on(async { android_logger::init_once(Config::default().with_min_level(Level::Info)); - let wallet = get_lock(&WALLET)?; + let wallet = get_wallet_lock(coin)?; let height = sync::latest_height(&wallet.ld_url).await?; Ok::<_, anyhow::Error>(height) }); @@ -212,6 +264,7 @@ fn report_progress(progress: Progress, port: i64) { } pub fn send_multi_payment( + coin: u8, account: u32, recipients_json: &str, use_transparent: bool, @@ -220,7 +273,7 @@ pub fn send_multi_payment( ) -> String { let r = get_runtime(); let res = r.block_on(async { - let mut wallet = get_lock(&WALLET)?; + let mut wallet = get_wallet_lock(coin)?; let height = sync::latest_height(&wallet.ld_url).await?; let recipients = Wallet::parse_recipients(recipients_json)?; let res = wallet @@ -240,84 +293,88 @@ pub fn send_multi_payment( log_result_string(res) } -pub fn skip_to_last_height() { +pub fn skip_to_last_height(coin: u8) { let r = get_runtime(); let res = r.block_on(async { - let wallet = get_lock(&WALLET)?; + let wallet = get_wallet_lock(coin)?; wallet.skip_to_last_height().await }); log_result(res) } -pub fn rewind_to_height(height: u32) { +pub fn rewind_to_height(coin: u8, height: u32) { let res = || { - let mut wallet = get_lock(&WALLET)?; + let mut wallet = get_wallet_lock(coin)?; wallet.rewind_to_height(height) }; log_result(res()) } -pub fn mempool_sync() -> i64 { +pub fn mempool_sync(coin: u8) -> i64 { let r = get_runtime(); let res = r.block_on(async { - let mut mempool = get_lock(&MEMPOOL)?; + let mut mempool = get_mempool_lock(coin)?; mempool.scan().await }); log_result(res) } -pub fn set_mempool_account(account: u32) { +pub fn set_mempool_account(coin: u8, account: u32) { let res = || { - let mut mempool = get_lock(&MEMPOOL)?; + let mut mempool = get_mempool_lock(coin)?; mempool.set_account(account) }; log_result(res()); } -pub fn mempool_reset(height: u32) { +pub fn mempool_reset(coin: u8, height: u32) { let res = || { - let mut mempool = get_lock(&MEMPOOL)?; + let mut mempool = get_mempool_lock(coin)?; mempool.clear(height) }; log_result(res()); } -pub fn get_mempool_balance() -> i64 { +pub fn get_mempool_balance(coin: u8) -> i64 { let res = || { - let mempool = get_lock(&MEMPOOL)?; + let mempool = get_mempool_lock(coin)?; Ok(mempool.get_unconfirmed_balance()) }; log_result(res()) } -pub fn get_taddr_balance(account: u32) -> u64 { +pub fn get_taddr_balance(coin: u8, account: u32) -> u64 { let r = get_runtime(); let res = r.block_on(async { - let wallet = get_lock(&WALLET)?; + let wallet = get_wallet_lock(coin)?; wallet.get_taddr_balance(account).await }); log_result(res) } -pub fn shield_taddr(account: u32) -> String { +pub fn shield_taddr(coin: u8, account: u32) -> String { let r = get_runtime(); let res = r.block_on(async { - let mut wallet = get_lock(&WALLET)?; + let mut wallet = get_wallet_lock(coin)?; let height = sync::latest_height(&wallet.ld_url).await?; wallet.shield_taddr(account, height).await }); log_result(res) } -pub fn set_lwd_url(url: &str) { +pub fn set_lwd_url(coin: u8, url: &str) { let res = || { - let mut wallet = get_lock(&WALLET)?; - wallet.set_lwd_url(url) + let mut wallet = get_wallet_lock(coin)?; + wallet.set_lwd_url(url)?; + let mut mempool = get_mempool_lock(coin)?; + mempool.set_lwd_url(url)?; + Ok(()) }; log_result(res()) } pub fn prepare_multi_payment( + coin: u8, account: u32, recipients_json: &str, use_transparent: bool, @@ -325,7 +382,7 @@ pub fn prepare_multi_payment( ) -> String { let r = get_runtime(); let res = r.block_on(async { - let mut wallet = get_lock(&WALLET)?; + let mut wallet = get_wallet_lock(coin)?; let last_height = sync::latest_height(&wallet.ld_url).await?; let recipients = Wallet::parse_recipients(recipients_json)?; let tx = wallet @@ -342,10 +399,10 @@ pub fn prepare_multi_payment( log_result_string(res) } -pub fn broadcast(tx_filename: &str) -> String { +pub fn broadcast(coin: u8, tx_filename: &str) -> String { let r = get_runtime(); let res = r.block_on(async { - let wallet = get_lock(&WALLET)?; + let wallet = get_wallet_lock(coin)?; let mut file = File::open(&tx_filename)?; let mut s = String::new(); file.read_to_string(&mut s)?; @@ -355,20 +412,20 @@ pub fn broadcast(tx_filename: &str) -> String { log_result_string(res) } -pub fn broadcast_txhex(txhex: &str) -> String { +pub fn broadcast_txhex(coin: u8, txhex: &str) -> String { let r = get_runtime(); let res = r.block_on(async { - let wallet = get_lock(&WALLET)?; + let wallet = get_wallet_lock(coin)?; let tx = hex::decode(txhex)?; broadcast_tx(&tx, &wallet.ld_url).await }); log_result_string(res) } -pub fn sync_historical_prices(now: i64, days: u32, currency: &str) -> u32 { +pub fn sync_historical_prices(coin: u8, now: i64, days: u32, currency: &str) -> u32 { let r = get_runtime(); let res = r.block_on(async { - let mut wallet = get_lock(&WALLET)?; + let mut wallet = get_wallet_lock(coin)?; wallet.sync_historical_prices(now, days, currency).await }); log_result(res) @@ -390,53 +447,55 @@ pub fn get_sapling(ua_addr: &str) -> String { } } -pub fn store_contact(id: u32, name: &str, address: &str, dirty: bool) { +pub fn store_contact(coin: u8, id: u32, name: &str, address: &str, dirty: bool) { let res = || { - let wallet = get_lock(&WALLET)?; + let wallet = get_wallet_lock(coin)?; wallet.store_contact(id, name, address, dirty)?; Ok(()) }; log_result(res()) } -pub fn commit_unsaved_contacts(account: u32, anchor_offset: u32) -> String { +pub fn commit_unsaved_contacts(coin: u8, account: u32, anchor_offset: u32) -> String { let r = get_runtime(); let res = r.block_on(async { - let mut wallet = get_lock(&WALLET)?; + let mut wallet = get_wallet_lock(coin)?; wallet.commit_unsaved_contacts(account, anchor_offset).await }); log_result_string(res) } -pub fn truncate_data() { +pub fn truncate_data(coin: u8) { let res = || { - let wallet = get_lock(&WALLET)?; + let wallet = get_wallet_lock(coin)?; wallet.truncate_data()?; Ok(()) }; log_result(res()) } -pub fn delete_account(account: u32) { +pub fn delete_account(coin: u8, account: u32) { let res = || { - let wallet = get_lock(&WALLET)?; + let wallet = get_wallet_lock(coin)?; wallet.delete_account(account)?; Ok(()) }; log_result(res()) } -pub fn make_payment_uri(address: &str, amount: u64, memo: &str) -> String { +pub fn make_payment_uri(coin: u8, address: &str, amount: u64, memo: &str) -> String { let res = || { - let uri = Wallet::make_payment_uri(address, amount, memo)?; + let wallet = get_wallet_lock(coin)?; + let uri = wallet.make_payment_uri(address, amount, memo)?; Ok(uri) }; log_result(res()) } -pub fn parse_payment_uri(uri: &str) -> String { +pub fn parse_payment_uri(coin: u8, uri: &str) -> String { let res = || { - let payment_json = Wallet::parse_payment_uri(uri)?; + let wallet = get_wallet_lock(coin)?; + let payment_json = wallet.parse_payment_uri(uri)?; Ok(payment_json) }; log_result(res()) @@ -448,8 +507,13 @@ pub fn generate_random_enc_key() -> String { pub fn get_full_backup(key: &str) -> String { let res = || { - let wallet = get_lock(&WALLET)?; - let backup = wallet.get_full_backup(key)?; + let mut accounts = vec![]; + for coin in [0, 1] { + let wallet = get_wallet_lock(coin)?; + accounts.extend(wallet.get_full_backup()?); + } + + let backup = encrypt_backup(&accounts, key)?; Ok(backup) }; log_result(res()) @@ -457,16 +521,19 @@ pub fn get_full_backup(key: &str) -> String { pub fn restore_full_backup(key: &str, backup: &str) -> String { let res = || { - let wallet = get_lock(&WALLET)?; - wallet.restore_full_backup(key, backup)?; + let accounts = decrypt_backup(key, backup)?; + for coin in [0, 1] { + let wallet = get_wallet_lock(coin)?; + wallet.restore_full_backup(&accounts)?; + } Ok(String::new()) }; log_result_string(res()) } -pub fn store_share_secret(account: u32, secret: &str) { +pub fn store_share_secret(coin: u8, account: u32, secret: &str) { let res = || { - let wallet = get_lock(&WALLET)?; + let wallet = get_wallet_lock(coin)?; let share = SecretShare::decode(secret)?; wallet.store_share_secret( account, @@ -480,9 +547,9 @@ pub fn store_share_secret(account: u32, secret: &str) { log_result(res()) } -pub fn get_share_secret(account: u32) -> String { +pub fn get_share_secret(coin: u8, account: u32) -> String { let res = || { - let wallet = get_lock(&WALLET)?; + let wallet = get_wallet_lock(coin)?; let secret = wallet.get_share_secret(account)?; Ok(secret) }; @@ -583,9 +650,9 @@ pub fn run_multi_signer( log_result(res) } -pub fn split_account(threshold: u32, participants: u32, account: u32) -> String { +pub fn split_account(coin: u8, threshold: u32, participants: u32, account: u32) -> String { let res = || { - let wallet = get_lock(&WALLET)?; + let wallet = get_wallet_lock(coin)?; let sk = wallet.get_sk(account)?; let shares = zcash_multisig::split_account(threshold as usize, participants as usize, &sk)?; Ok(shares) diff --git a/native/warp_api_ffi/src/lib.rs b/native/warp_api_ffi/src/lib.rs index 6d7d7cc..47fef41 100644 --- a/native/warp_api_ffi/src/lib.rs +++ b/native/warp_api_ffi/src/lib.rs @@ -7,10 +7,9 @@ mod api; static mut POST_COBJ: Option = None; #[no_mangle] -pub unsafe extern "C" fn init_wallet(db_path: *mut c_char, ld_url: *mut c_char) { +pub unsafe extern "C" fn init_wallet(db_path: *mut c_char) { let db_path = CStr::from_ptr(db_path).to_string_lossy(); - let ld_url = CStr::from_ptr(ld_url).to_string_lossy(); - api::init_wallet(&db_path, &ld_url); + api::init_wallet(&db_path); } #[no_mangle] @@ -19,8 +18,8 @@ pub unsafe extern "C" fn reset_app() { } #[no_mangle] -pub unsafe extern "C" fn warp_sync(get_tx: bool, anchor_offset: u32, port: i64) { - api::warp_sync(get_tx, anchor_offset, port); +pub unsafe extern "C" fn warp_sync(coin: u8, get_tx: bool, anchor_offset: u32, port: i64) -> i8 { + api::warp_sync(coin, get_tx, anchor_offset, port) } #[no_mangle] @@ -29,47 +28,59 @@ pub unsafe extern "C" fn dart_post_cobject(ptr: ffi::DartPostCObjectFnType) { } #[no_mangle] -pub unsafe extern "C" fn get_latest_height() -> u32 { - api::get_latest_height() +pub unsafe extern "C" fn get_latest_height(coin: u8) -> u32 { + api::get_latest_height(coin) } #[no_mangle] -pub unsafe extern "C" fn is_valid_key(seed: *mut c_char) -> i8 { +pub unsafe extern "C" fn is_valid_key(coin: u8, seed: *mut c_char) -> i8 { let seed = CStr::from_ptr(seed).to_string_lossy(); - sync::is_valid_key(&seed) + api::is_valid_key(coin, &seed) } #[no_mangle] -pub unsafe extern "C" fn valid_address(address: *mut c_char) -> bool { +pub unsafe extern "C" fn valid_address(coin: u8, address: *mut c_char) -> bool { let address = CStr::from_ptr(address).to_string_lossy(); - api::valid_address(&address) + api::valid_address(coin, &address) } #[no_mangle] -pub unsafe extern "C" fn new_address(account: u32) -> *mut c_char { - let address = api::new_address(account); +pub unsafe extern "C" fn new_address(coin: u8, account: u32) -> *mut c_char { + let address = api::new_address(coin, account); CString::new(address).unwrap().into_raw() } #[no_mangle] -pub unsafe extern "C" fn set_mempool_account(account: u32) { - api::set_mempool_account(account); +pub unsafe extern "C" fn set_mempool_account(coin: u8, account: u32) { + api::set_mempool_account(coin, account); } #[no_mangle] -pub unsafe extern "C" fn new_account(name: *mut c_char, data: *mut c_char) -> i32 { +pub unsafe extern "C" fn new_account( + coin: u8, + name: *mut c_char, + data: *mut c_char, + index: u32, +) -> i32 { let name = CStr::from_ptr(name).to_string_lossy(); let data = CStr::from_ptr(data).to_string_lossy(); - api::new_account(&name, &data) + api::new_account(coin, &name, &data, index) } #[no_mangle] -pub unsafe extern "C" fn get_mempool_balance() -> i64 { - api::get_mempool_balance() +pub unsafe extern "C" fn new_sub_account(coin: u8, id: u32, name: *mut c_char) -> i32 { + let name = CStr::from_ptr(name).to_string_lossy(); + api::new_sub_account(coin, id, &name) +} + +#[no_mangle] +pub unsafe extern "C" fn get_mempool_balance(coin: u8) -> i64 { + api::get_mempool_balance(coin) } #[no_mangle] pub unsafe extern "C" fn send_multi_payment( + coin: u8, account: u32, recipients_json: *mut c_char, anchor_offset: u32, @@ -78,6 +89,7 @@ pub unsafe extern "C" fn send_multi_payment( ) -> *const c_char { let recipients_json = CStr::from_ptr(recipients_json).to_string_lossy(); let tx_id = api::send_multi_payment( + coin, account, &recipients_json, use_transparent, @@ -87,78 +99,90 @@ pub unsafe extern "C" fn send_multi_payment( CString::new(tx_id).unwrap().into_raw() } +// #[no_mangle] +// pub unsafe extern "C" fn try_warp_sync(coin: u8, get_tx: bool, anchor_offset: u32) -> i8 { +// api::try_warp_sync(coin, get_tx, anchor_offset) +// } + #[no_mangle] -pub unsafe extern "C" fn try_warp_sync(get_tx: bool, anchor_offset: u32) -> i8 { - api::try_warp_sync(get_tx, anchor_offset) +pub unsafe extern "C" fn skip_to_last_height(coin: u8) { + api::skip_to_last_height(coin) } #[no_mangle] -pub unsafe extern "C" fn skip_to_last_height() { - api::skip_to_last_height() +pub unsafe extern "C" fn rewind_to_height(coin: u8, height: u32) { + api::rewind_to_height(coin, height) } #[no_mangle] -pub unsafe extern "C" fn rewind_to_height(height: u32) { - api::rewind_to_height(height) +pub unsafe extern "C" fn mempool_sync(coin: u8) -> i64 { + api::mempool_sync(coin) } #[no_mangle] -pub unsafe extern "C" fn mempool_sync() -> i64 { - api::mempool_sync() +pub unsafe extern "C" fn mempool_reset(coin: u8, height: u32) { + api::mempool_reset(coin, height) } #[no_mangle] -pub unsafe extern "C" fn mempool_reset(height: u32) { - api::mempool_reset(height) +pub unsafe extern "C" fn get_taddr_balance(coin: u8, account: u32) -> u64 { + api::get_taddr_balance(coin, account) } #[no_mangle] -pub unsafe extern "C" fn get_taddr_balance(account: u32) -> u64 { - api::get_taddr_balance(account) -} - -#[no_mangle] -pub unsafe extern "C" fn shield_taddr(account: u32) -> *mut c_char { - let tx_id = api::shield_taddr(account); +pub unsafe extern "C" fn shield_taddr(coin: u8, account: u32) -> *mut c_char { + let tx_id = api::shield_taddr(coin, account); CString::new(tx_id).unwrap().into_raw() } #[no_mangle] -pub unsafe extern "C" fn set_lwd_url(url: *mut c_char) { +pub unsafe extern "C" fn set_lwd_url(coin: u8, url: *mut c_char) { let url = CStr::from_ptr(url).to_string_lossy(); - api::set_lwd_url(&url); + api::set_lwd_url(coin, &url); } #[no_mangle] pub unsafe extern "C" fn prepare_multi_payment( + coin: u8, account: u32, recipients_json: *mut c_char, use_transparent: bool, anchor_offset: u32, ) -> *mut c_char { let recipients_json = CStr::from_ptr(recipients_json).to_string_lossy(); - let tx = api::prepare_multi_payment(account, &recipients_json, use_transparent, anchor_offset); + let tx = api::prepare_multi_payment( + coin, + account, + &recipients_json, + use_transparent, + anchor_offset, + ); CString::new(tx).unwrap().into_raw() } #[no_mangle] -pub unsafe extern "C" fn broadcast(tx_filename: *mut c_char) -> *mut c_char { +pub unsafe extern "C" fn broadcast(coin: u8, tx_filename: *mut c_char) -> *mut c_char { let tx_filename = CStr::from_ptr(tx_filename).to_string_lossy(); - let res = api::broadcast(&tx_filename); + let res = api::broadcast(coin, &tx_filename); CString::new(res).unwrap().into_raw() } #[no_mangle] -pub unsafe extern "C" fn broadcast_txhex(txhex: *mut c_char) -> *mut c_char { +pub unsafe extern "C" fn broadcast_txhex(coin: u8, txhex: *mut c_char) -> *mut c_char { let txhex = CStr::from_ptr(txhex).to_string_lossy(); - let res = api::broadcast_txhex(&txhex); + let res = api::broadcast_txhex(coin, &txhex); CString::new(res).unwrap().into_raw() } #[no_mangle] -pub unsafe extern "C" fn sync_historical_prices(now: i64, days: u32, currency: *mut c_char) -> u32 { +pub unsafe extern "C" fn sync_historical_prices( + coin: u8, + now: i64, + days: u32, + currency: *mut c_char, +) -> u32 { let currency = CStr::from_ptr(currency).to_string_lossy(); - api::sync_historical_prices(now, days, ¤cy) + api::sync_historical_prices(coin, now, days, ¤cy) } #[no_mangle] @@ -181,6 +205,7 @@ pub unsafe extern "C" fn get_sapling(ua_addr: *mut c_char) -> *mut c_char { #[no_mangle] pub unsafe extern "C" fn store_contact( + coin: u8, id: u32, name: *mut c_char, address: *mut c_char, @@ -188,41 +213,46 @@ pub unsafe extern "C" fn store_contact( ) { let name = CStr::from_ptr(name).to_string_lossy(); let address = CStr::from_ptr(address).to_string_lossy(); - api::store_contact(id, &name, &address, dirty); + api::store_contact(coin, id, &name, &address, dirty); } #[no_mangle] -pub unsafe extern "C" fn commit_unsaved_contacts(account: u32, anchor_offset: u32) -> *mut c_char { - let tx_id = api::commit_unsaved_contacts(account, anchor_offset); +pub unsafe extern "C" fn commit_unsaved_contacts( + coin: u8, + account: u32, + anchor_offset: u32, +) -> *mut c_char { + let tx_id = api::commit_unsaved_contacts(coin, account, anchor_offset); CString::new(tx_id).unwrap().into_raw() } #[no_mangle] -pub unsafe extern "C" fn delete_account(account: u32) { - api::delete_account(account); +pub unsafe extern "C" fn delete_account(coin: u8, account: u32) { + api::delete_account(coin, account); } #[no_mangle] -pub unsafe extern "C" fn truncate_data() { - api::truncate_data(); +pub unsafe extern "C" fn truncate_data(coin: u8) { + api::truncate_data(coin); } #[no_mangle] pub unsafe extern "C" fn make_payment_uri( + coin: u8, address: *mut c_char, amount: u64, memo: *mut c_char, ) -> *mut c_char { let address = CStr::from_ptr(address).to_string_lossy(); let memo = CStr::from_ptr(memo).to_string_lossy(); - let uri = api::make_payment_uri(&address, amount, &memo); + let uri = api::make_payment_uri(coin, &address, amount, &memo); CString::new(uri).unwrap().into_raw() } #[no_mangle] -pub unsafe extern "C" fn parse_payment_uri(uri: *mut c_char) -> *mut c_char { +pub unsafe extern "C" fn parse_payment_uri(coin: u8, uri: *mut c_char) -> *mut c_char { let uri = CStr::from_ptr(uri).to_string_lossy(); - let payment_json = api::parse_payment_uri(&uri); + let payment_json = api::parse_payment_uri(coin, &uri); CString::new(payment_json).unwrap().into_raw() } @@ -248,14 +278,14 @@ pub unsafe extern "C" fn restore_full_backup(key: *mut c_char, backup: *mut c_ch } #[no_mangle] -pub unsafe extern "C" fn store_share_secret(account: u32, secret: *mut c_char) { +pub unsafe extern "C" fn store_share_secret(coin: u8, account: u32, secret: *mut c_char) { let secret = CStr::from_ptr(secret).to_string_lossy(); - api::store_share_secret(account, &secret); + api::store_share_secret(coin, account, &secret); } #[no_mangle] -pub unsafe extern "C" fn get_share_secret(account: u32) -> *mut c_char { - let secret = api::get_share_secret(account); +pub unsafe extern "C" fn get_share_secret(coin: u8, account: u32) -> *mut c_char { + let secret = api::get_share_secret(coin, account); CString::new(secret).unwrap().into_raw() } @@ -303,11 +333,12 @@ pub unsafe extern "C" fn run_multi_signer( #[no_mangle] pub unsafe extern "C" fn split_account( + coin: u8, threshold: u32, participants: u32, account: u32, ) -> *mut c_char { - let r = api::split_account(threshold, participants, account); + let r = api::split_account(coin, threshold, participants, account); CString::new(r).unwrap().into_raw() } diff --git a/native/zcash-params b/native/zcash-params index 2bccb52..c26e152 160000 --- a/native/zcash-params +++ b/native/zcash-params @@ -1 +1 @@ -Subproject commit 2bccb5267a7715d974f51f3e70f6866495f65e5e +Subproject commit c26e1525c88522eb69896b79f6553d2cb46eb800 diff --git a/native/zcash-sync b/native/zcash-sync index 766748c..056ffa4 160000 --- a/native/zcash-sync +++ b/native/zcash-sync @@ -1 +1 @@ -Subproject commit 766748ccfb9c69f8bccaeb0b7c4b02b5a9777e71 +Subproject commit 056ffa454aeb8dd6dc8a60ac02379e34557c7740 diff --git a/packages/warp_api_ffi/lib/warp_api.dart b/packages/warp_api_ffi/lib/warp_api.dart index f1c9402..40f1bef 100644 --- a/packages/warp_api_ffi/lib/warp_api.dart +++ b/packages/warp_api_ffi/lib/warp_api.dart @@ -15,28 +15,49 @@ typedef report_callback = Void Function(Int32); const DAY_MS = 24 * 3600 * 1000; class SyncParams { + int coin; bool getTx; int anchorOffset; SendPort? port; - SyncParams(this.getTx, this.anchorOffset, this.port); + SyncParams(this.coin, this.getTx, this.anchorOffset, this.port); } class PaymentParams { + int coin; int account; String recipientsJson; bool useTransparent; int anchorOffset; SendPort port; - PaymentParams(this.account, this.recipientsJson, this.useTransparent, this.anchorOffset, this.port); + PaymentParams(this.coin, this.account, this.recipientsJson, this.useTransparent, this.anchorOffset, this.port); +} + +class ShieldTAddrParams { + int coin; + int account; + ShieldTAddrParams(this.coin, this.account); } class CommitContactsParams { + int coin; int account; int anchorOffset; - CommitContactsParams(this.account, this.anchorOffset); + CommitContactsParams(this.coin, this.account, this.anchorOffset); +} + +class SyncHistoricalPricesParams { + int coin; + String currency; + SyncHistoricalPricesParams(this.coin, this.currency); +} + +class GetTBalanceParams { + int coin; + int account; + GetTBalanceParams(this.coin, this.account); } const DEFAULT_ACCOUNT = 1; @@ -63,60 +84,61 @@ class WarpApi { throw UnsupportedError('This platform is not supported.'); } - static initWallet(String dbPath, String ldUrl) { + static initWallet(String dbPath) { warp_api_lib.init_wallet( - dbPath.toNativeUtf8().cast(), ldUrl.toNativeUtf8().cast()); + dbPath.toNativeUtf8().cast()); } static void resetApp() { warp_api_lib.reset_app(); } - static int newAccount(String name, String key) { - return warp_api_lib.new_account( - name.toNativeUtf8().cast(), key.toNativeUtf8().cast()); + static int newAccount(int coin, String name, String key, int index) { + return warp_api_lib.new_account(coin, + name.toNativeUtf8().cast(), key.toNativeUtf8().cast(), index); } - static void skipToLastHeight() { - warp_api_lib.skip_to_last_height(); + static int newSubAccount(int coin, int accountId, String name) { + return warp_api_lib.new_sub_account(coin, accountId, + name.toNativeUtf8().cast()); } - static void rewindToHeight(int height) { - warp_api_lib.rewind_to_height(height); + static void skipToLastHeight(int coin) { + warp_api_lib.skip_to_last_height(coin); } - static void warpSync(SyncParams params) { - warp_api_lib.warp_sync(params.getTx ? 1 : 0, params.anchorOffset, params.port!.nativePort); - params.port!.send(-1); + static void rewindToHeight(int coin, int height) { + warp_api_lib.rewind_to_height(coin, height); } - static Future tryWarpSync(bool getTx, int anchorOffset) async { - final res = await compute(tryWarpSyncIsolateFn, SyncParams(getTx, anchorOffset, null)); + static int warpSync(SyncParams params) { + final res = warp_api_lib.warp_sync(params.coin, params.getTx ? 1 : 0, params.anchorOffset, params.port!.nativePort); + params.port!.send(null); return res; } - static void mempoolReset(int height) { - warp_api_lib.mempool_reset(height); + static void mempoolReset(int coin, int height) { + warp_api_lib.mempool_reset(coin, height); } - static Future mempoolSync() async { - return compute(mempoolSyncIsolateFn, null); + static Future mempoolSync(int coin) async { + return compute(mempoolSyncIsolateFn, coin); } - static Future getLatestHeight() async { - return await compute(getLatestHeightIsolateFn, null); + static Future getLatestHeight(int coin) async { + return await compute(getLatestHeightIsolateFn, coin); } - static int validKey(String key) { - return warp_api_lib.is_valid_key(key.toNativeUtf8().cast()); + static int validKey(int coin, String key) { + return warp_api_lib.is_valid_key(coin, key.toNativeUtf8().cast()); } - static bool validAddress(String address) { - return warp_api_lib.valid_address(address.toNativeUtf8().cast()) != 0; + static bool validAddress(int coin, String address) { + return warp_api_lib.valid_address(coin, address.toNativeUtf8().cast()) != 0; } - static String newAddress(int account) { - final address = warp_api_lib.new_address(account); + static String newAddress(int coin, int account) { + final address = warp_api_lib.new_address(coin, account); return address.cast().toDartString(); } @@ -130,15 +152,15 @@ class WarpApi { return zaddr.cast().toDartString(); } - static void setMempoolAccount(int account) { - return warp_api_lib.set_mempool_account(account); + static void setMempoolAccount(int coin, int account) { + return warp_api_lib.set_mempool_account(coin, account); } - static int getUnconfirmedBalance() { - return warp_api_lib.get_mempool_balance(); + static int getUnconfirmedBalance(int coin) { + return warp_api_lib.get_mempool_balance(coin); } - static Future sendPayment(int account, List recipients, bool useTransparent, int anchorOffset, void Function(int) f) async { + static Future sendPayment(int coin, int account, List recipients, bool useTransparent, int anchorOffset, void Function(int) f) async { var receivePort = ReceivePort(); receivePort.listen((progress) { f(progress); @@ -149,81 +171,84 @@ class WarpApi { return await compute( sendPaymentIsolateFn, PaymentParams( - account, recipientJson, useTransparent, anchorOffset, receivePort.sendPort)); + coin, account, recipientJson, useTransparent, anchorOffset, receivePort.sendPort)); } - static int getTBalance(int account) { - final balance = warp_api_lib.get_taddr_balance(account); + static int getTBalance(int coin, int account) { + final balance = warp_api_lib.get_taddr_balance(coin, account); return balance; } - static Future getTBalanceAsync(int account) async { - final balance = await compute(getTBalanceIsolateFn, account); + static Future getTBalanceAsync(int coin, int account) async { + final balance = await compute(getTBalanceIsolateFn, GetTBalanceParams(coin, account)); return balance; } - static Future shieldTAddr(int account) async { - final txId = compute(shieldTAddrIsolateFn, account); + static Future shieldTAddr(int coin, int account) async { + final txId = compute(shieldTAddrIsolateFn, ShieldTAddrParams(coin, account)); return txId; } static String prepareTx( + int coin, int account, List recipients, bool useTransparent, int anchorOffset, String txFilename) { final recipientsJson = jsonEncode(recipients); - final res = warp_api_lib.prepare_multi_payment(account, + final res = warp_api_lib.prepare_multi_payment(coin, account, recipientsJson.toNativeUtf8().cast(), useTransparent ? 1 : 0, anchorOffset); return res.cast().toDartString(); } - static String broadcast(String txFilename) { - final res = warp_api_lib.broadcast(txFilename.toNativeUtf8().cast()); + static String broadcast(int coin, String txFilename) { + final res = warp_api_lib.broadcast(coin, txFilename.toNativeUtf8().cast()); return res.cast().toDartString(); } - static String broadcastHex(String tx) { - final res = warp_api_lib.broadcast_txhex(tx.toNativeUtf8().cast()); + static String broadcastHex(int coin, String tx) { + final res = warp_api_lib.broadcast_txhex(coin, tx.toNativeUtf8().cast()); return res.cast().toDartString(); } - static Future syncHistoricalPrices(String currency) async { - return await compute(syncHistoricalPricesIsolateFn, currency); + static Future syncHistoricalPrices(int coin, String currency) async { + return await compute(syncHistoricalPricesIsolateFn, SyncHistoricalPricesParams(coin, currency)); } - static updateLWD(String url) { - warp_api_lib.set_lwd_url(url.toNativeUtf8().cast()); + static updateLWD(int coin, String url) { + warp_api_lib.set_lwd_url(coin, url.toNativeUtf8().cast()); } - static void storeContact(int id, String name, String address, bool dirty) { - warp_api_lib.store_contact(id, name.toNativeUtf8().cast(), address.toNativeUtf8().cast(), dirty ? 1 : 0); + static void storeContact(int coin, int id, String name, String address, bool dirty) { + warp_api_lib.store_contact(coin, id, name.toNativeUtf8().cast(), address.toNativeUtf8().cast(), dirty ? 1 : 0); } - static Future commitUnsavedContacts(int account, int anchorOffset) async { - return compute(commitUnsavedContactsIsolateFn, CommitContactsParams(account, anchorOffset)); + static Future commitUnsavedContacts(int coin, int account, int anchorOffset) async { + return compute(commitUnsavedContactsIsolateFn, CommitContactsParams(coin, account, anchorOffset)); } - static void truncateData() { - warp_api_lib.truncate_data(); + static void truncateData(int coin) { + warp_api_lib.truncate_data(coin); } - static void deleteAccount(int account) { - warp_api_lib.delete_account(account); + static void deleteAccount(int coin, int account) { + warp_api_lib.delete_account(coin, account); } - static String makePaymentURI(String address, int amount, String memo) { + static String makePaymentURI(int coin, String address, int amount, String memo) { final uri = warp_api_lib.make_payment_uri( + coin, address.toNativeUtf8().cast(), amount, memo.toNativeUtf8().cast()); return uri.cast().toDartString(); } - static String parsePaymentURI(String uri) { + static String parsePaymentURI(int coin, String uri) { final json = warp_api_lib.parse_payment_uri( + coin, uri.toNativeUtf8().cast()); return json.cast().toDartString(); } @@ -242,8 +267,8 @@ class WarpApi { return res.cast().toDartString(); } - static void storeShareSecret(int account, String secret) { - warp_api_lib.store_share_secret(account, secret.toNativeUtf8().cast()); + static void storeShareSecret(int coin, int account, String secret) { + warp_api_lib.store_share_secret(coin, account, secret.toNativeUtf8().cast()); } static Future runAggregator(String secretShare, int port, SendPort sendPort) async { @@ -262,8 +287,8 @@ class WarpApi { warp_api_lib.shutdown_aggregator(); } - static String splitAccount(int threshold, int participants, int account) { - return warp_api_lib.split_account(threshold, participants, account).cast().toDartString(); + static String splitAccount(int coin, int threshold, int participants, int account) { + return warp_api_lib.split_account(coin, threshold, participants, account).cast().toDartString(); } } @@ -308,6 +333,7 @@ int runMultiSignerIsolateFn(RunMultiSignerParams params) { String sendPaymentIsolateFn(PaymentParams params) { final txId = warp_api_lib.send_multi_payment( + params.coin, params.account, params.recipientsJson.toNativeUtf8().cast(), params.anchorOffset, @@ -316,35 +342,31 @@ String sendPaymentIsolateFn(PaymentParams params) { return txId.cast().toDartString(); } -int tryWarpSyncIsolateFn(SyncParams params) { - return warp_api_lib.try_warp_sync(params.getTx ? 1 : 0, params.anchorOffset); +int getLatestHeightIsolateFn(int coin) { + return warp_api_lib.get_latest_height(coin); } -int getLatestHeightIsolateFn(Null _dummy) { - return warp_api_lib.get_latest_height(); +int mempoolSyncIsolateFn(int coin) { + return warp_api_lib.mempool_sync(coin); } -int mempoolSyncIsolateFn(Null _dummy) { - return warp_api_lib.mempool_sync(); -} - -String shieldTAddrIsolateFn(int account) { - final txId = warp_api_lib.shield_taddr(account); +String shieldTAddrIsolateFn(ShieldTAddrParams params) { + final txId = warp_api_lib.shield_taddr(params.coin, params.account); return txId.cast().toDartString(); } -int syncHistoricalPricesIsolateFn(String currency) { +int syncHistoricalPricesIsolateFn(SyncHistoricalPricesParams params) { final now = DateTime.now(); final today = DateTime.utc(now.year, now.month, now.day); - return warp_api_lib.sync_historical_prices(today.millisecondsSinceEpoch ~/ 1000, 365, currency.toNativeUtf8().cast()); + return warp_api_lib.sync_historical_prices(params.coin, today.millisecondsSinceEpoch ~/ 1000, 365, params.currency.toNativeUtf8().cast()); } String commitUnsavedContactsIsolateFn(CommitContactsParams params) { - final txId = warp_api_lib.commit_unsaved_contacts(params.account, params.anchorOffset); + final txId = warp_api_lib.commit_unsaved_contacts(params.coin, params.account, params.anchorOffset); return txId.cast().toDartString(); } -int getTBalanceIsolateFn(int account) { - return warp_api_lib.get_taddr_balance(account); +int getTBalanceIsolateFn(GetTBalanceParams params) { + return warp_api_lib.get_taddr_balance(params.coin, params.account); } diff --git a/packages/warp_api_ffi/lib/warp_api_generated.dart b/packages/warp_api_ffi/lib/warp_api_generated.dart index 5782681..4753868 100644 --- a/packages/warp_api_ffi/lib/warp_api_generated.dart +++ b/packages/warp_api_ffi/lib/warp_api_generated.dart @@ -20,11 +20,9 @@ class NativeLibrary { void init_wallet( ffi.Pointer db_path, - ffi.Pointer ld_url, ) { return _init_wallet( db_path, - ld_url, ); } @@ -42,12 +40,14 @@ class NativeLibrary { late final _dart_reset_app _reset_app = _reset_app_ptr.asFunction<_dart_reset_app>(); - void warp_sync( + int warp_sync( + int coin, int get_tx, int anchor_offset, int port, ) { return _warp_sync( + coin, get_tx, anchor_offset, port, @@ -72,8 +72,12 @@ class NativeLibrary { late final _dart_dart_post_cobject _dart_post_cobject = _dart_post_cobject_ptr.asFunction<_dart_dart_post_cobject>(); - int get_latest_height() { - return _get_latest_height(); + int get_latest_height( + int coin, + ) { + return _get_latest_height( + coin, + ); } late final _get_latest_height_ptr = @@ -82,9 +86,11 @@ class NativeLibrary { _get_latest_height_ptr.asFunction<_dart_get_latest_height>(); int is_valid_key( + int coin, ffi.Pointer seed, ) { return _is_valid_key( + coin, seed, ); } @@ -95,9 +101,11 @@ class NativeLibrary { _is_valid_key_ptr.asFunction<_dart_is_valid_key>(); int valid_address( + int coin, ffi.Pointer address, ) { return _valid_address( + coin, address, ); } @@ -108,9 +116,11 @@ class NativeLibrary { _valid_address_ptr.asFunction<_dart_valid_address>(); ffi.Pointer new_address( + int coin, int account, ) { return _new_address( + coin, account, ); } @@ -121,9 +131,11 @@ class NativeLibrary { _new_address_ptr.asFunction<_dart_new_address>(); void set_mempool_account( + int coin, int account, ) { return _set_mempool_account( + coin, account, ); } @@ -135,12 +147,16 @@ class NativeLibrary { _set_mempool_account_ptr.asFunction<_dart_set_mempool_account>(); int new_account( + int coin, ffi.Pointer name, ffi.Pointer data, + int index, ) { return _new_account( + coin, name, data, + index, ); } @@ -149,8 +165,29 @@ class NativeLibrary { late final _dart_new_account _new_account = _new_account_ptr.asFunction<_dart_new_account>(); - int get_mempool_balance() { - return _get_mempool_balance(); + int new_sub_account( + int coin, + int id, + ffi.Pointer name, + ) { + return _new_sub_account( + coin, + id, + name, + ); + } + + late final _new_sub_account_ptr = + _lookup>('new_sub_account'); + late final _dart_new_sub_account _new_sub_account = + _new_sub_account_ptr.asFunction<_dart_new_sub_account>(); + + int get_mempool_balance( + int coin, + ) { + return _get_mempool_balance( + coin, + ); } late final _get_mempool_balance_ptr = @@ -160,6 +197,7 @@ class NativeLibrary { _get_mempool_balance_ptr.asFunction<_dart_get_mempool_balance>(); ffi.Pointer send_multi_payment( + int coin, int account, ffi.Pointer recipients_json, int anchor_offset, @@ -167,6 +205,7 @@ class NativeLibrary { int port, ) { return _send_multi_payment( + coin, account, recipients_json, anchor_offset, @@ -180,25 +219,14 @@ class NativeLibrary { late final _dart_send_multi_payment _send_multi_payment = _send_multi_payment_ptr.asFunction<_dart_send_multi_payment>(); - int try_warp_sync( - int get_tx, - int anchor_offset, + void skip_to_last_height( + int coin, ) { - return _try_warp_sync( - get_tx, - anchor_offset, + return _skip_to_last_height( + coin, ); } - late final _try_warp_sync_ptr = - _lookup>('try_warp_sync'); - late final _dart_try_warp_sync _try_warp_sync = - _try_warp_sync_ptr.asFunction<_dart_try_warp_sync>(); - - void skip_to_last_height() { - return _skip_to_last_height(); - } - late final _skip_to_last_height_ptr = _lookup>( 'skip_to_last_height'); @@ -206,9 +234,11 @@ class NativeLibrary { _skip_to_last_height_ptr.asFunction<_dart_skip_to_last_height>(); void rewind_to_height( + int coin, int height, ) { return _rewind_to_height( + coin, height, ); } @@ -218,8 +248,12 @@ class NativeLibrary { late final _dart_rewind_to_height _rewind_to_height = _rewind_to_height_ptr.asFunction<_dart_rewind_to_height>(); - int mempool_sync() { - return _mempool_sync(); + int mempool_sync( + int coin, + ) { + return _mempool_sync( + coin, + ); } late final _mempool_sync_ptr = @@ -228,9 +262,11 @@ class NativeLibrary { _mempool_sync_ptr.asFunction<_dart_mempool_sync>(); void mempool_reset( + int coin, int height, ) { return _mempool_reset( + coin, height, ); } @@ -241,9 +277,11 @@ class NativeLibrary { _mempool_reset_ptr.asFunction<_dart_mempool_reset>(); int get_taddr_balance( + int coin, int account, ) { return _get_taddr_balance( + coin, account, ); } @@ -254,9 +292,11 @@ class NativeLibrary { _get_taddr_balance_ptr.asFunction<_dart_get_taddr_balance>(); ffi.Pointer shield_taddr( + int coin, int account, ) { return _shield_taddr( + coin, account, ); } @@ -267,9 +307,11 @@ class NativeLibrary { _shield_taddr_ptr.asFunction<_dart_shield_taddr>(); void set_lwd_url( + int coin, ffi.Pointer url, ) { return _set_lwd_url( + coin, url, ); } @@ -280,12 +322,14 @@ class NativeLibrary { _set_lwd_url_ptr.asFunction<_dart_set_lwd_url>(); ffi.Pointer prepare_multi_payment( + int coin, int account, ffi.Pointer recipients_json, int use_transparent, int anchor_offset, ) { return _prepare_multi_payment( + coin, account, recipients_json, use_transparent, @@ -300,9 +344,11 @@ class NativeLibrary { _prepare_multi_payment_ptr.asFunction<_dart_prepare_multi_payment>(); ffi.Pointer broadcast( + int coin, ffi.Pointer tx_filename, ) { return _broadcast( + coin, tx_filename, ); } @@ -313,9 +359,11 @@ class NativeLibrary { _broadcast_ptr.asFunction<_dart_broadcast>(); ffi.Pointer broadcast_txhex( + int coin, ffi.Pointer txhex, ) { return _broadcast_txhex( + coin, txhex, ); } @@ -326,11 +374,13 @@ class NativeLibrary { _broadcast_txhex_ptr.asFunction<_dart_broadcast_txhex>(); int sync_historical_prices( + int coin, int now, int days, ffi.Pointer currency, ) { return _sync_historical_prices( + coin, now, days, currency, @@ -370,12 +420,14 @@ class NativeLibrary { _get_sapling_ptr.asFunction<_dart_get_sapling>(); void store_contact( + int coin, int id, ffi.Pointer name, ffi.Pointer address, int dirty, ) { return _store_contact( + coin, id, name, address, @@ -389,10 +441,12 @@ class NativeLibrary { _store_contact_ptr.asFunction<_dart_store_contact>(); ffi.Pointer commit_unsaved_contacts( + int coin, int account, int anchor_offset, ) { return _commit_unsaved_contacts( + coin, account, anchor_offset, ); @@ -405,9 +459,11 @@ class NativeLibrary { _commit_unsaved_contacts_ptr.asFunction<_dart_commit_unsaved_contacts>(); void delete_account( + int coin, int account, ) { return _delete_account( + coin, account, ); } @@ -417,8 +473,12 @@ class NativeLibrary { late final _dart_delete_account _delete_account = _delete_account_ptr.asFunction<_dart_delete_account>(); - void truncate_data() { - return _truncate_data(); + void truncate_data( + int coin, + ) { + return _truncate_data( + coin, + ); } late final _truncate_data_ptr = @@ -427,11 +487,13 @@ class NativeLibrary { _truncate_data_ptr.asFunction<_dart_truncate_data>(); ffi.Pointer make_payment_uri( + int coin, ffi.Pointer address, int amount, ffi.Pointer memo, ) { return _make_payment_uri( + coin, address, amount, memo, @@ -444,9 +506,11 @@ class NativeLibrary { _make_payment_uri_ptr.asFunction<_dart_make_payment_uri>(); ffi.Pointer parse_payment_uri( + int coin, ffi.Pointer uri, ) { return _parse_payment_uri( + coin, uri, ); } @@ -496,10 +560,12 @@ class NativeLibrary { _restore_full_backup_ptr.asFunction<_dart_restore_full_backup>(); void store_share_secret( + int coin, int account, ffi.Pointer secret, ) { return _store_share_secret( + coin, account, secret, ); @@ -511,9 +577,11 @@ class NativeLibrary { _store_share_secret_ptr.asFunction<_dart_store_share_secret>(); ffi.Pointer get_share_secret( + int coin, int account, ) { return _get_share_secret( + coin, account, ); } @@ -589,11 +657,13 @@ class NativeLibrary { _run_multi_signer_ptr.asFunction<_dart_run_multi_signer>(); ffi.Pointer split_account( + int coin, int threshold, int participants, int account, ) { return _split_account( + coin, threshold, participants, account, @@ -617,25 +687,25 @@ class NativeLibrary { typedef _c_init_wallet = ffi.Void Function( ffi.Pointer db_path, - ffi.Pointer ld_url, ); typedef _dart_init_wallet = void Function( ffi.Pointer db_path, - ffi.Pointer ld_url, ); typedef _c_reset_app = ffi.Void Function(); typedef _dart_reset_app = void Function(); -typedef _c_warp_sync = ffi.Void Function( +typedef _c_warp_sync = ffi.Int8 Function( + ffi.Uint8 coin, ffi.Int8 get_tx, ffi.Uint32 anchor_offset, ffi.Int64 port, ); -typedef _dart_warp_sync = void Function( +typedef _dart_warp_sync = int Function( + int coin, int get_tx, int anchor_offset, int port, @@ -649,57 +719,90 @@ typedef _dart_dart_post_cobject = void Function( ffi.Pointer ptr, ); -typedef _c_get_latest_height = ffi.Uint32 Function(); +typedef _c_get_latest_height = ffi.Uint32 Function( + ffi.Uint8 coin, +); -typedef _dart_get_latest_height = int Function(); +typedef _dart_get_latest_height = int Function( + int coin, +); typedef _c_is_valid_key = ffi.Int8 Function( + ffi.Uint8 coin, ffi.Pointer seed, ); typedef _dart_is_valid_key = int Function( + int coin, ffi.Pointer seed, ); typedef _c_valid_address = ffi.Int8 Function( + ffi.Uint8 coin, ffi.Pointer address, ); typedef _dart_valid_address = int Function( + int coin, ffi.Pointer address, ); typedef _c_new_address = ffi.Pointer Function( + ffi.Uint8 coin, ffi.Uint32 account, ); typedef _dart_new_address = ffi.Pointer Function( + int coin, int account, ); typedef _c_set_mempool_account = ffi.Void Function( + ffi.Uint8 coin, ffi.Uint32 account, ); typedef _dart_set_mempool_account = void Function( + int coin, int account, ); typedef _c_new_account = ffi.Int32 Function( + ffi.Uint8 coin, ffi.Pointer name, ffi.Pointer data, + ffi.Uint32 index, ); typedef _dart_new_account = int Function( + int coin, ffi.Pointer name, ffi.Pointer data, + int index, ); -typedef _c_get_mempool_balance = ffi.Int64 Function(); +typedef _c_new_sub_account = ffi.Int32 Function( + ffi.Uint8 coin, + ffi.Uint32 id, + ffi.Pointer name, +); -typedef _dart_get_mempool_balance = int Function(); +typedef _dart_new_sub_account = int Function( + int coin, + int id, + ffi.Pointer name, +); + +typedef _c_get_mempool_balance = ffi.Int64 Function( + ffi.Uint8 coin, +); + +typedef _dart_get_mempool_balance = int Function( + int coin, +); typedef _c_send_multi_payment = ffi.Pointer Function( + ffi.Uint8 coin, ffi.Uint32 account, ffi.Pointer recipients_json, ffi.Uint32 anchor_offset, @@ -708,6 +811,7 @@ typedef _c_send_multi_payment = ffi.Pointer Function( ); typedef _dart_send_multi_payment = ffi.Pointer Function( + int coin, int account, ffi.Pointer recipients_json, int anchor_offset, @@ -715,65 +819,74 @@ typedef _dart_send_multi_payment = ffi.Pointer Function( int port, ); -typedef _c_try_warp_sync = ffi.Int8 Function( - ffi.Int8 get_tx, - ffi.Uint32 anchor_offset, +typedef _c_skip_to_last_height = ffi.Void Function( + ffi.Uint8 coin, ); -typedef _dart_try_warp_sync = int Function( - int get_tx, - int anchor_offset, +typedef _dart_skip_to_last_height = void Function( + int coin, ); -typedef _c_skip_to_last_height = ffi.Void Function(); - -typedef _dart_skip_to_last_height = void Function(); - typedef _c_rewind_to_height = ffi.Void Function( + ffi.Uint8 coin, ffi.Uint32 height, ); typedef _dart_rewind_to_height = void Function( + int coin, int height, ); -typedef _c_mempool_sync = ffi.Int64 Function(); +typedef _c_mempool_sync = ffi.Int64 Function( + ffi.Uint8 coin, +); -typedef _dart_mempool_sync = int Function(); +typedef _dart_mempool_sync = int Function( + int coin, +); typedef _c_mempool_reset = ffi.Void Function( + ffi.Uint8 coin, ffi.Uint32 height, ); typedef _dart_mempool_reset = void Function( + int coin, int height, ); typedef _c_get_taddr_balance = ffi.Uint64 Function( + ffi.Uint8 coin, ffi.Uint32 account, ); typedef _dart_get_taddr_balance = int Function( + int coin, int account, ); typedef _c_shield_taddr = ffi.Pointer Function( + ffi.Uint8 coin, ffi.Uint32 account, ); typedef _dart_shield_taddr = ffi.Pointer Function( + int coin, int account, ); typedef _c_set_lwd_url = ffi.Void Function( + ffi.Uint8 coin, ffi.Pointer url, ); typedef _dart_set_lwd_url = void Function( + int coin, ffi.Pointer url, ); typedef _c_prepare_multi_payment = ffi.Pointer Function( + ffi.Uint8 coin, ffi.Uint32 account, ffi.Pointer recipients_json, ffi.Int8 use_transparent, @@ -781,6 +894,7 @@ typedef _c_prepare_multi_payment = ffi.Pointer Function( ); typedef _dart_prepare_multi_payment = ffi.Pointer Function( + int coin, int account, ffi.Pointer recipients_json, int use_transparent, @@ -788,28 +902,34 @@ typedef _dart_prepare_multi_payment = ffi.Pointer Function( ); typedef _c_broadcast = ffi.Pointer Function( + ffi.Uint8 coin, ffi.Pointer tx_filename, ); typedef _dart_broadcast = ffi.Pointer Function( + int coin, ffi.Pointer tx_filename, ); typedef _c_broadcast_txhex = ffi.Pointer Function( + ffi.Uint8 coin, ffi.Pointer txhex, ); typedef _dart_broadcast_txhex = ffi.Pointer Function( + int coin, ffi.Pointer txhex, ); typedef _c_sync_historical_prices = ffi.Uint32 Function( + ffi.Uint8 coin, ffi.Int64 now, ffi.Uint32 days, ffi.Pointer currency, ); typedef _dart_sync_historical_prices = int Function( + int coin, int now, int days, ffi.Pointer currency, @@ -834,6 +954,7 @@ typedef _dart_get_sapling = ffi.Pointer Function( ); typedef _c_store_contact = ffi.Void Function( + ffi.Uint8 coin, ffi.Uint32 id, ffi.Pointer name, ffi.Pointer address, @@ -841,6 +962,7 @@ typedef _c_store_contact = ffi.Void Function( ); typedef _dart_store_contact = void Function( + int coin, int id, ffi.Pointer name, ffi.Pointer address, @@ -848,44 +970,56 @@ typedef _dart_store_contact = void Function( ); typedef _c_commit_unsaved_contacts = ffi.Pointer Function( + ffi.Uint8 coin, ffi.Uint32 account, ffi.Uint32 anchor_offset, ); typedef _dart_commit_unsaved_contacts = ffi.Pointer Function( + int coin, int account, int anchor_offset, ); typedef _c_delete_account = ffi.Void Function( + ffi.Uint8 coin, ffi.Uint32 account, ); typedef _dart_delete_account = void Function( + int coin, int account, ); -typedef _c_truncate_data = ffi.Void Function(); +typedef _c_truncate_data = ffi.Void Function( + ffi.Uint8 coin, +); -typedef _dart_truncate_data = void Function(); +typedef _dart_truncate_data = void Function( + int coin, +); typedef _c_make_payment_uri = ffi.Pointer Function( + ffi.Uint8 coin, ffi.Pointer address, ffi.Uint64 amount, ffi.Pointer memo, ); typedef _dart_make_payment_uri = ffi.Pointer Function( + int coin, ffi.Pointer address, int amount, ffi.Pointer memo, ); typedef _c_parse_payment_uri = ffi.Pointer Function( + ffi.Uint8 coin, ffi.Pointer uri, ); typedef _dart_parse_payment_uri = ffi.Pointer Function( + int coin, ffi.Pointer uri, ); @@ -912,20 +1046,24 @@ typedef _dart_restore_full_backup = ffi.Pointer Function( ); typedef _c_store_share_secret = ffi.Void Function( + ffi.Uint8 coin, ffi.Uint32 account, ffi.Pointer secret, ); typedef _dart_store_share_secret = void Function( + int coin, int account, ffi.Pointer secret, ); typedef _c_get_share_secret = ffi.Pointer Function( + ffi.Uint8 coin, ffi.Uint32 account, ); typedef _dart_get_share_secret = ffi.Pointer Function( + int coin, int account, ); @@ -974,12 +1112,14 @@ typedef _dart_run_multi_signer = int Function( ); typedef _c_split_account = ffi.Pointer Function( + ffi.Uint8 coin, ffi.Uint32 threshold, ffi.Uint32 participants, ffi.Uint32 account, ); typedef _dart_split_account = ffi.Pointer Function( + int coin, int threshold, int participants, int account, diff --git a/pubspec.lock b/pubspec.lock index 0bd172c..f48f263 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -301,7 +301,7 @@ packages: name: file_picker url: "https://pub.dartlang.org" source: hosted - version: "4.4.0" + version: "4.5.0" fixnum: dependency: transitive description: @@ -418,7 +418,7 @@ packages: name: flutter_native_splash url: "https://pub.dartlang.org" source: hosted - version: "1.3.3" + version: "1.3.2" flutter_palette: dependency: "direct main" description: @@ -440,6 +440,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "4.0.0" + flutter_speed_dial: + dependency: "direct main" + description: + name: flutter_speed_dial + url: "https://pub.dartlang.org" + source: hosted + version: "5.0.0+1" flutter_svg: dependency: "direct main" description: @@ -599,13 +606,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.12.11" - material_color_utilities: - dependency: transitive - description: - name: material_color_utilities - url: "https://pub.dartlang.org" - source: hosted - version: "0.1.3" material_design_icons_flutter: dependency: "direct main" description: @@ -794,14 +794,14 @@ packages: name: path_provider_android url: "https://pub.dartlang.org" source: hosted - version: "2.0.11" + version: "2.0.12" path_provider_ios: dependency: transitive description: name: path_provider_ios url: "https://pub.dartlang.org" source: hosted - version: "2.0.7" + version: "2.0.8" path_provider_linux: dependency: transitive description: @@ -913,6 +913,20 @@ packages: name: quick_actions url: "https://pub.dartlang.org" source: hosted + version: "0.6.0+10" + quick_actions_android: + dependency: transitive + description: + name: quick_actions_android + url: "https://pub.dartlang.org" + source: hosted + version: "0.6.0+9" + quick_actions_ios: + dependency: transitive + description: + name: quick_actions_ios + url: "https://pub.dartlang.org" + source: hosted version: "0.6.0+9" quick_actions_platform_interface: dependency: transitive @@ -1170,7 +1184,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.8" + version: "0.4.3" timing: dependency: transitive description: @@ -1268,7 +1282,7 @@ packages: name: url_launcher_web url: "https://pub.dartlang.org" source: hosted - version: "2.0.8" + version: "2.0.6" url_launcher_windows: dependency: transitive description: @@ -1347,5 +1361,5 @@ packages: source: hosted version: "3.1.0" sdks: - dart: ">=2.15.1 <3.0.0" - flutter: ">=2.10.0" + dart: ">=2.15.0 <3.0.0" + flutter: ">=2.8.0" diff --git a/pubspec.yaml b/pubspec.yaml index 091cfdb..69ccbf6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ -name: warp -description: A new Flutter project. +name: ZYWallet +description: Z/Ycash Wallet # The following line prevents the package from being accidentally published to # pub.dev using `pub publish`. This is preferred for private packages. @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.7+199 +version: 0.0.1+1 environment: sdk: ">=2.12.0 <3.0.0" @@ -63,6 +63,7 @@ dependencies: git: url: https://github.com/hhanh00/flutter_barcode_scanner.git ref: d338bdc5c0b797d81694f04bd5f52e8171939af5 + flutter_speed_dial: ^5.0.0 currency_text_input_formatter: ^2.1.2 sensors_plus: ^1.1.0 connectivity_plus: ^1.1.0 @@ -89,7 +90,7 @@ dev_dependencies: flutter_native_splash: ^1.2.3 flutter_app_name: - name: "YWallet" + name: "ZYWallet" flutter_icons: android: true @@ -120,6 +121,8 @@ flutter: - assets/wallet.svg - assets/contacts.svg - assets/multipay.svg + - assets/ycash.png + - assets/zcash.png # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware. diff --git a/pubspec.yaml.tpl b/pubspec.yaml.tpl deleted file mode 100644 index 9e43d40..0000000 --- a/pubspec.yaml.tpl +++ /dev/null @@ -1,150 +0,0 @@ -name: warp -description: A new Flutter project. - -# The following line prevents the package from being accidentally published to -# pub.dev using `pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev - -# The following defines the version and build number for your application. -# A version number is three numbers separated by dots, like 1.2.43 -# followed by an optional build number separated by a +. -# Both the version and the builder number may be overridden in flutter -# build by specifying --build-name and --build-number, respectively. -# In Android, build-name is used as versionName while build-number used as versionCode. -# Read more about Android versioning at https://developer.android.com/studio/publish/versioning -# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. -# Read more about iOS versioning at -# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.8+202 - -environment: - sdk: ">=2.12.0 <3.0.0" - -dependencies: - flutter: - sdk: flutter - warp_api: - path: packages/warp_api_ffi - sqflite: ^2.0.0+4 - flutter_mobx: ^2.0.2 - qr_flutter: ^4.0.0 - http: ^0.13.3 - intl: ^0.17.0 - path: ^1.8.0 - material_design_icons_flutter: ^5.0.5955-rc.1 - rflutter_alert: ^2.0.4 - sprintf: ^6.0.0 - local_auth: ^1.1.7 - shared_preferences: ^2.0.7 - flutter_markdown: ^0.6.6 - package_info_plus: ^1.0.6 - velocity_x: ^3.3.0 - decimal: ^1.3.0 - flutter_form_builder: ^6.1.0+1 - url_launcher: ^6.0.10 - flex_color_scheme: ^3.0.1 - flutter_colorpicker: ^0.6.0 - fl_chart: ^0.40.0 - k_chart: - git: - url: https://github.com/hhanh00/k_chart.git - ref: 821f81681f8ee819cd721498f7b28d290f4ebe38 - grouped_list: ^4.1.0 - json_annotation: ^4.1.0 - share_plus: ^2.1.4 - path_provider: ^2.0.3 - file_picker: ^4.0.2 - mustache_template: ^2.0.0 - rate_my_app: ^1.1.1 - flutter_palette: ^1.1.0+1 - flutter_svg: ^0.22.0 - flutter_typeahead: ^3.2.0 - flutter_barcode_scanner: - git: - url: https://github.com/hhanh00/flutter_barcode_scanner.git - ref: d338bdc5c0b797d81694f04bd5f52e8171939af5 - currency_text_input_formatter: ^2.1.2 - sensors_plus: ^1.1.0 - connectivity_plus: ^1.1.0 - uni_links: ^0.5.1 - quick_actions: ^0.6.0 - csv: ^5.0.0 - network_info_plus : ^2.0.2 - flutter_localizations: - sdk: flutter - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.3 - -dev_dependencies: - flutter_test: - sdk: flutter - build_runner: ^2.1.2 - mobx_codegen: ^2.0.3 - json_serializable: ^5.0.0 - flutter_launcher_icons: any - flutter_app_name: any - change_app_package_name: any - flutter_native_splash: ^1.2.3 - -flutter_app_name: - name: "{{APP_TITLE}}" - -flutter_icons: - android: true - ios: true - remove_alpha_ios: true - image_path: "assets/icon.png" - -flutter_native_splash: - color_dark: "#FFFFFF" - color: "#000000" - image: "assets/icon.png" - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter. -flutter: - - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. - uses-material-design: true - - # To add assets to your application, add an assets section, like this: - assets: - - assets/icon.png - - assets/about.md - - assets/wallet.svg - - assets/contacts.svg - - assets/multipay.svg - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware. - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages -flutter_intl: - enabled: true