22
.gitignore
vendored
|
@ -96,3 +96,25 @@ pubspec.yaml
|
|||
/windows/runner/main.cpp
|
||||
/windows/CMakeLists.txt
|
||||
/windows/runner/resources/app_icon.ico
|
||||
|
||||
# FVM Version Cache
|
||||
.fvm/
|
||||
|
||||
android/app/src/main/jniLibs/arm64-v8a/libwownero_wallet2_api_c.so
|
||||
android/app/src/main/jniLibs/arm64-v8a/libmonero_wallet2_api_c.so
|
||||
android/app/src/main/jniLibs/armeabi-v7a/libmonero_wallet2_api_c.so
|
||||
android/app/src/main/jniLibs/armeabi-v7a/libwownero_wallet2_api_c.so
|
||||
android/app/src/main/jniLibs/x86_64/libmonero_wallet2_api_c.so
|
||||
android/app/src/main/jniLibs/x86_64/libwownero_wallet2_api_c.so
|
||||
macos/monero_wallet2_api_c.dylib
|
||||
macos/wownero_wallet2_api_c.dylib
|
||||
/macos/monero_libwallet2_api_c.dylib
|
||||
/macos/wownero_libwallet2_api_c.dylib
|
||||
/ios/monero_libwallet2_api_c.dylib
|
||||
/ios/wownero_libwallet2_api_c.dylib
|
||||
/android/app/src/main/jniLibs/arm64-v8a/libmonero_libwallet2_api_c.so
|
||||
/android/app/src/main/jniLibs/armeabi-v7a/libmonero_libwallet2_api_c.so
|
||||
/android/app/src/main/jniLibs/x86_64/libmonero_libwallet2_api_c.so
|
||||
/android/app/src/main/jniLibs/arm64-v8a/libwownero_libwallet2_api_c.so
|
||||
/android/app/src/main/jniLibs/armeabi-v7a/libwownero_libwallet2_api_c.so
|
||||
/android/app/src/main/jniLibs/x86_64/libwownero_libwallet2_api_c.so
|
||||
|
|
0
android/app/src/main/jniLibs/arm64-v8a/.gitkeep
Normal file
0
android/app/src/main/jniLibs/armeabi-v7a/.gitkeep
Normal file
0
android/app/src/main/jniLibs/x86_64/.gitkeep
Normal file
BIN
asset_sources/default_themes/stack_wallet/light.zip
Normal file
BIN
asset_sources/icon/campfire/app_icon_alpha.png
Normal file
After Width: | Height: | Size: 58 KiB |
BIN
asset_sources/icon/campfire/icon.png
Normal file
After Width: | Height: | Size: 52 KiB |
BIN
asset_sources/icon/campfire/macos-icon.png
Normal file
After Width: | Height: | Size: 58 KiB |
BIN
asset_sources/icon/campfire/splash.png
Normal file
After Width: | Height: | Size: 33 KiB |
|
@ -0,0 +1,9 @@
|
|||
<svg width="72" height="84" viewBox="0 0 72 84" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1.12967 68.765C0.433381 71.2717 0.414629 73.9187 1.07535 76.435C1.73607 78.9513 3.05254 81.2466 4.89 83.086L15.2595 72.7011L4.89 62.3129C3.10079 64.102 1.80521 66.325 1.12967 68.765ZM56.7407 72.6913L67.1134 83.0762C68.4759 81.7125 69.5568 80.0934 70.2942 78.3112C71.0316 76.529 71.4112 74.6188 71.4112 72.6896C71.4112 70.7605 71.0316 68.8503 70.2942 67.0681C69.5568 65.2859 68.4759 63.6667 67.1134 62.3031L56.7407 72.6913ZM36.0017 62.3129L25.6322 51.928L4.89 31.1581C2.14007 33.9125 0.595189 37.648 0.595189 41.543C0.595189 45.438 2.14007 49.1736 4.89 51.928L25.6322 72.7011C28.3829 75.4543 32.1127 77.0009 36.0017 77.0009C39.8907 77.0009 43.6205 75.4543 46.3712 72.7011L67.1134 51.928C69.8625 49.1731 71.4069 45.4378 71.4069 41.543C71.4069 37.6482 69.8625 33.9129 67.1134 31.1581L36.0017 62.3129ZM36.0017 31.1581C34.6393 32.5215 33.5585 34.1404 32.8212 35.9223C32.0838 37.7042 31.7042 39.6141 31.7042 41.543C31.7042 43.4719 32.0838 45.3818 32.8212 47.1637C33.5585 48.9456 34.6393 50.5646 36.0017 51.928L51.556 36.3505C52.9185 34.9869 53.9993 33.3677 54.7368 31.5855C55.4742 29.8033 55.8537 27.8931 55.8537 25.964C55.8537 24.0348 55.4742 22.1246 54.7368 20.3424C53.9993 18.5603 52.9185 16.9411 51.556 15.5774L36.0017 31.1581ZM20.4474 15.5774C19.0853 16.9413 18.0049 18.5605 17.2677 20.3427C16.5305 22.1248 16.151 24.035 16.151 25.964C16.151 27.893 16.5305 29.8031 17.2677 31.5853C18.0049 33.3674 19.0853 34.9866 20.4474 36.3505L36.0017 20.7699C37.3641 19.4065 38.4449 17.7876 39.1823 16.0057C39.9196 14.2238 40.2992 12.3138 40.2992 10.3849C40.2992 8.45606 39.9196 6.5461 39.1823 4.7642C38.4449 2.9823 37.3641 1.36339 36.0017 0L20.4474 15.5774Z" fill="url(#paint0_linear_243_16074)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_243_16074" x1="36.0017" y1="0" x2="36.0017" y2="83.086" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#F5595C"/>
|
||||
<stop offset="1" stop-color="#F94167"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 2 KiB |
|
@ -0,0 +1,9 @@
|
|||
<svg width="72" height="84" viewBox="0 0 72 84" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1.12967 68.765C0.433381 71.2717 0.414629 73.9187 1.07535 76.435C1.73607 78.9513 3.05254 81.2466 4.89 83.086L15.2595 72.7011L4.89 62.3129C3.10079 64.102 1.80521 66.325 1.12967 68.765ZM56.7407 72.6913L67.1134 83.0762C68.4759 81.7125 69.5568 80.0934 70.2942 78.3112C71.0316 76.529 71.4112 74.6188 71.4112 72.6896C71.4112 70.7605 71.0316 68.8503 70.2942 67.0681C69.5568 65.2859 68.4759 63.6667 67.1134 62.3031L56.7407 72.6913ZM36.0017 62.3129L25.6322 51.928L4.89 31.1581C2.14007 33.9125 0.595189 37.648 0.595189 41.543C0.595189 45.438 2.14007 49.1736 4.89 51.928L25.6322 72.7011C28.3829 75.4543 32.1127 77.0009 36.0017 77.0009C39.8907 77.0009 43.6205 75.4543 46.3712 72.7011L67.1134 51.928C69.8625 49.1731 71.4069 45.4378 71.4069 41.543C71.4069 37.6482 69.8625 33.9129 67.1134 31.1581L36.0017 62.3129ZM36.0017 31.1581C34.6393 32.5215 33.5585 34.1404 32.8212 35.9223C32.0838 37.7042 31.7042 39.6141 31.7042 41.543C31.7042 43.4719 32.0838 45.3818 32.8212 47.1637C33.5585 48.9456 34.6393 50.5646 36.0017 51.928L51.556 36.3505C52.9185 34.9869 53.9993 33.3677 54.7368 31.5855C55.4742 29.8033 55.8537 27.8931 55.8537 25.964C55.8537 24.0348 55.4742 22.1246 54.7368 20.3424C53.9993 18.5603 52.9185 16.9411 51.556 15.5774L36.0017 31.1581ZM20.4474 15.5774C19.0853 16.9413 18.0049 18.5605 17.2677 20.3427C16.5305 22.1248 16.151 24.035 16.151 25.964C16.151 27.893 16.5305 29.8031 17.2677 31.5853C18.0049 33.3674 19.0853 34.9866 20.4474 36.3505L36.0017 20.7699C37.3641 19.4065 38.4449 17.7876 39.1823 16.0057C39.9196 14.2238 40.2992 12.3138 40.2992 10.3849C40.2992 8.45606 39.9196 6.5461 39.1823 4.7642C38.4449 2.9823 37.3641 1.36339 36.0017 0L20.4474 15.5774Z" fill="url(#paint0_linear_243_16074)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_243_16074" x1="36.0017" y1="0" x2="36.0017" y2="83.086" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#F5595C"/>
|
||||
<stop offset="1" stop-color="#F94167"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 2 KiB |
1
asset_sources/lottie/campfire/arrow_rotate.json
Normal file
|
@ -0,0 +1 @@
|
|||
{"v":"5.10.2","fr":30,"ip":0,"op":60,"w":30,"h":30,"nm":"arrow-rotate","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"arrow-rotate","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":44,"s":[200]},{"t":60,"s":[360]}],"ix":10},"p":{"a":0,"k":[15,15,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-0.828],[0,0],[3.389,0],[1.441,-4.074],[-0.781,-0.277],[-0.276,0.778],[-3.22,0],[-1.369,-1.823],[0,0],[0,-0.83],[-0.83,0],[0,0],[-0.023,0],[0,0],[0,0.83],[0,0],[0.83,0]],"o":[[0,0],[-1.964,-2.437],[-4.533,0],[-0.276,0.741],[0.781,0.277],[1.031,-2.916],[2.494,0],[0,0],[-0.83,0],[0,0.83],[0,0],[0.023,0],[0,0],[0.83,0],[0,0],[0,-0.828],[-0.83,0]],"v":[[8.25,-8.25],[8.25,-6.497],[-0.042,-10.5],[-9.902,-3.502],[-8.988,-1.584],[-7.073,-2.498],[-0.042,-7.5],[6,-4.5],[4.5,-4.5],[3,-3],[4.5,-1.5],[8.452,-1.5],[8.522,-1.5],[9.75,-1.5],[11.25,-3],[11.25,-8.25],[9.75,-9.75]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,0],[-0.828,0],[0,0.83],[0,0],[-3.347,0],[-1.439,4.073],[0.783,0.277],[0.277,-0.778],[3.262,0],[1.411,1.823],[0,0],[0,0.83],[0.83,0],[0,0],[0,-0.83]],"o":[[0,0.83],[0.828,0],[0,0],[1.922,2.438],[4.575,0],[0.277,-0.783],[-0.778,-0.277],[-1.031,2.916],[-2.452,0],[0,0],[0.83,0],[0,-0.83],[0,0],[-0.828,0],[0,0]],"v":[[-11.25,8.25],[-9.75,9.75],[-8.25,8.25],[-8.25,6.497],[0,10.5],[9.9,3.502],[8.986,1.584],[7.073,2.498],[0,7.5],[-6.042,4.5],[-4.5,4.5],[-3,3],[-4.5,1.5],[-9.75,1.5],[-11.25,3]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.137254908681,0.137254908681,0.137254908681,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"ct":1,"bm":0}],"markers":[]}
|
1
asset_sources/lottie/campfire/icon_send.json
Normal file
|
@ -0,0 +1 @@
|
|||
{"v":"5.10.2","fr":30,"ip":0,"op":100,"w":24,"h":24,"nm":"icon-send","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"MASK","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[12,12,0],"ix":2,"l":2},"a":{"a":0,"k":[0.125,0.125,0],"ix":1,"l":2},"s":{"a":0,"k":[87.368,87.368,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[23.75,23.75],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0.125,0.125],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":100,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Arrow","tt":1,"tp":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[-1.009,25.009,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":33,"s":[11.695,12.306,0],"to":[0,0,0],"ti":[0,0,0]},{"t":53,"s":[24.398,-0.397,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,-0.69],[-0.69,0],[0,0],[0,0],[-0.488,-0.488],[-0.488,0.488],[0,0],[0,0],[-0.69,0],[0,0.69],[0,0],[0.234,0.234],[0.332,0]],"o":[[-0.69,0],[0,0.69],[0,0],[0,0],[-0.488,0.488],[0.488,0.488],[0,0],[0,0],[0,0.69],[0.69,0],[0,0],[0,-0.332],[-0.234,-0.234],[0,0]],"v":[[-2,-5.25],[-3.25,-4],[-2,-2.75],[0.982,-2.75],[-4.884,3.116],[-4.884,4.884],[-3.116,4.884],[2.75,-0.982],[2.75,2],[4,3.25],[5.25,2],[5.25,-4],[4.884,-4.884],[4,-5.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.137254908681,0.137254908681,0.137254908681,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Vector (Stroke)","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":100,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Outline","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[12,12,0],"ix":2,"l":2},"a":{"a":0,"k":[12,0.063,0],"ix":1,"l":2},"s":{"a":0,"k":[90.104,90.104,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[24,24],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.137254901961,0.137254901961,0.137254901961,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2.5,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[12,0.063],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":58,"s":[0]},{"t":97,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":58,"s":[100]},{"t":97,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":100,"st":0,"ct":1,"bm":0}],"markers":[{"tm":0,"cm":"{\r\n\"name\":\"SEGMENT 1\"\r\n}","dr":0},{"tm":53,"cm":"{\r\n\"name\":\"SEGMENT 2\"\r\n}","dr":0},{"tm":97,"cm":"{\r\n\"name\":\"SEGMENT 3\"\r\n}","dr":0}]}
|
1
asset_sources/lottie/campfire/loader.json
Normal file
1
asset_sources/lottie/campfire/loader_and_checkmark.json
Normal file
1
asset_sources/lottie/campfire/onion_animation.json
Normal file
After Width: | Height: | Size: 52 KiB |
|
@ -1 +1 @@
|
|||
Subproject commit 6e71b956c3801f65a662c7f140e871c246166db3
|
||||
Subproject commit 4b87151d4914606b911f738a8236a6e54a6d8ecb
|
|
@ -53,7 +53,7 @@ sudo apt-get install libc6:i386 libncurses5:i386 libstdc++6:i386 lib32z1 libbz2-
|
|||
### Build dependencies
|
||||
Install basic dependencies
|
||||
```
|
||||
sudo apt-get install libssl-dev curl unzip automake build-essential file pkg-config git python libtool libtinfo5 cmake libgit2-dev clang libncurses5-dev libncursesw5-dev zlib1g-dev llvm python3-distutils
|
||||
sudo apt-get install libssl-dev curl unzip automake build-essential file pkg-config git python libtool libtinfo5 cmake libgit2-dev clang libncurses5-dev libncursesw5-dev zlib1g-dev llvm python3-distutils g++ gcc gperf
|
||||
```
|
||||
|
||||
Install [Rust](https://www.rust-lang.org/tools/install) with command:
|
||||
|
@ -180,7 +180,7 @@ Download and install [Homebrew](https://brew.sh/). The following command can in
|
|||
|
||||
After installing Homebrew, install the following packages:
|
||||
```
|
||||
brew install autoconf automake boost berkeley-db ca-certificates cbindgen cmake cmake cocoapods curl git libssh2 make openssl@1.1 openssl@3 perl pkg-config rustup-init sodium unbound unzip xz zmq
|
||||
brew install autoconf automake boost berkeley-db ca-certificates cbindgen cmake cocoapods curl git libssh2 libsodium make openssl@1.1 openssl@3 perl pkg-config rustup-init unbound unzip xz zmq
|
||||
```
|
||||
|
||||
The following brew formula *may* be needed:
|
||||
|
|
|
@ -9,63 +9,6 @@ PODS:
|
|||
- connectivity_plus (0.0.1):
|
||||
- Flutter
|
||||
- ReachabilitySwift
|
||||
- cw_monero (0.0.2):
|
||||
- cw_monero/Boost (= 0.0.2)
|
||||
- cw_monero/Monero (= 0.0.2)
|
||||
- cw_monero/OpenSSL (= 0.0.2)
|
||||
- cw_monero/Sodium (= 0.0.2)
|
||||
- cw_monero/Unbound (= 0.0.2)
|
||||
- cw_shared_external
|
||||
- Flutter
|
||||
- cw_monero/Boost (0.0.2):
|
||||
- cw_shared_external
|
||||
- Flutter
|
||||
- cw_monero/Monero (0.0.2):
|
||||
- cw_shared_external
|
||||
- Flutter
|
||||
- cw_monero/OpenSSL (0.0.2):
|
||||
- cw_shared_external
|
||||
- Flutter
|
||||
- cw_monero/Sodium (0.0.2):
|
||||
- cw_shared_external
|
||||
- Flutter
|
||||
- cw_monero/Unbound (0.0.2):
|
||||
- cw_shared_external
|
||||
- Flutter
|
||||
- cw_shared_external (0.0.1):
|
||||
- cw_shared_external/Boost (= 0.0.1)
|
||||
- cw_shared_external/OpenSSL (= 0.0.1)
|
||||
- cw_shared_external/Sodium (= 0.0.1)
|
||||
- Flutter
|
||||
- cw_shared_external/Boost (0.0.1):
|
||||
- Flutter
|
||||
- cw_shared_external/OpenSSL (0.0.1):
|
||||
- Flutter
|
||||
- cw_shared_external/Sodium (0.0.1):
|
||||
- Flutter
|
||||
- cw_wownero (0.0.2):
|
||||
- cw_shared_external
|
||||
- cw_wownero/Boost (= 0.0.2)
|
||||
- cw_wownero/OpenSSL (= 0.0.2)
|
||||
- cw_wownero/Sodium (= 0.0.2)
|
||||
- cw_wownero/Unbound (= 0.0.2)
|
||||
- cw_wownero/Wownero (= 0.0.2)
|
||||
- Flutter
|
||||
- cw_wownero/Boost (0.0.2):
|
||||
- cw_shared_external
|
||||
- Flutter
|
||||
- cw_wownero/OpenSSL (0.0.2):
|
||||
- cw_shared_external
|
||||
- Flutter
|
||||
- cw_wownero/Sodium (0.0.2):
|
||||
- cw_shared_external
|
||||
- Flutter
|
||||
- cw_wownero/Unbound (0.0.2):
|
||||
- cw_shared_external
|
||||
- Flutter
|
||||
- cw_wownero/Wownero (0.0.2):
|
||||
- cw_shared_external
|
||||
- Flutter
|
||||
- device_info_plus (0.0.1):
|
||||
- Flutter
|
||||
- devicelocale (0.0.1):
|
||||
|
@ -140,6 +83,21 @@ PODS:
|
|||
- SDWebImage/Core (5.13.2)
|
||||
- share_plus (0.0.1):
|
||||
- Flutter
|
||||
- sqlite3 (3.46.0):
|
||||
- sqlite3/common (= 3.46.0)
|
||||
- sqlite3/common (3.46.0)
|
||||
- sqlite3/fts5 (3.46.0):
|
||||
- sqlite3/common
|
||||
- sqlite3/perf-threadsafe (3.46.0):
|
||||
- sqlite3/common
|
||||
- sqlite3/rtree (3.46.0):
|
||||
- sqlite3/common
|
||||
- sqlite3_flutter_libs (0.0.1):
|
||||
- Flutter
|
||||
- sqlite3 (~> 3.46.0)
|
||||
- sqlite3/fts5
|
||||
- sqlite3/perf-threadsafe
|
||||
- sqlite3/rtree
|
||||
- stack_wallet_backup (0.0.1):
|
||||
- Flutter
|
||||
- SwiftProtobuf (1.19.0)
|
||||
|
@ -155,9 +113,6 @@ DEPENDENCIES:
|
|||
- barcode_scan2 (from `.symlinks/plugins/barcode_scan2/ios`)
|
||||
- coinlib_flutter (from `.symlinks/plugins/coinlib_flutter/darwin`)
|
||||
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
||||
- cw_monero (from `.symlinks/plugins/cw_monero/ios`)
|
||||
- cw_shared_external (from `.symlinks/plugins/cw_shared_external/ios`)
|
||||
- cw_wownero (from `.symlinks/plugins/cw_wownero/ios`)
|
||||
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||
- devicelocale (from `.symlinks/plugins/devicelocale/ios`)
|
||||
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
||||
|
@ -177,6 +132,7 @@ DEPENDENCIES:
|
|||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||
- sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/ios`)
|
||||
- stack_wallet_backup (from `.symlinks/plugins/stack_wallet_backup/ios`)
|
||||
- tor_ffi_plugin (from `.symlinks/plugins/tor_ffi_plugin/ios`)
|
||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||
|
@ -189,6 +145,7 @@ SPEC REPOS:
|
|||
- MTBBarcodeScanner
|
||||
- ReachabilitySwift
|
||||
- SDWebImage
|
||||
- sqlite3
|
||||
- SwiftProtobuf
|
||||
- SwiftyGif
|
||||
|
||||
|
@ -199,12 +156,6 @@ EXTERNAL SOURCES:
|
|||
:path: ".symlinks/plugins/coinlib_flutter/darwin"
|
||||
connectivity_plus:
|
||||
:path: ".symlinks/plugins/connectivity_plus/ios"
|
||||
cw_monero:
|
||||
:path: ".symlinks/plugins/cw_monero/ios"
|
||||
cw_shared_external:
|
||||
:path: ".symlinks/plugins/cw_shared_external/ios"
|
||||
cw_wownero:
|
||||
:path: ".symlinks/plugins/cw_wownero/ios"
|
||||
device_info_plus:
|
||||
:path: ".symlinks/plugins/device_info_plus/ios"
|
||||
devicelocale:
|
||||
|
@ -243,6 +194,8 @@ EXTERNAL SOURCES:
|
|||
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||
share_plus:
|
||||
:path: ".symlinks/plugins/share_plus/ios"
|
||||
sqlite3_flutter_libs:
|
||||
:path: ".symlinks/plugins/sqlite3_flutter_libs/ios"
|
||||
stack_wallet_backup:
|
||||
:path: ".symlinks/plugins/stack_wallet_backup/ios"
|
||||
tor_ffi_plugin:
|
||||
|
@ -256,9 +209,6 @@ SPEC CHECKSUMS:
|
|||
barcode_scan2: 0af2bb63c81b4565aab6cd78278e4c0fa136dbb0
|
||||
coinlib_flutter: 6abec900d67762a6e7ccfd567a3cd3ae00bbee35
|
||||
connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a
|
||||
cw_monero: 9816991daff0e3ad0a8be140e31933b5526babd4
|
||||
cw_shared_external: 2972d872b8917603478117c9957dfca611845a92
|
||||
cw_wownero: ac53899fa5c6ff46b3fb490aa3b7ca36301fa832
|
||||
device_info_plus: 7545d84d8d1b896cb16a4ff98c19f07ec4b298ea
|
||||
devicelocale: b22617f40038496deffba44747101255cee005b0
|
||||
DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac
|
||||
|
@ -283,6 +233,8 @@ SPEC CHECKSUMS:
|
|||
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
|
||||
SDWebImage: 72f86271a6f3139cc7e4a89220946489d4b9a866
|
||||
share_plus: 599aa54e4ea31d4b4c0e9c911bcc26c55e791028
|
||||
sqlite3: 154b084339ede06960a5b3c8160066adc9176b7d
|
||||
sqlite3_flutter_libs: 0d611efdf6d1c9297d5ab03dab21b75aeebdae31
|
||||
stack_wallet_backup: 5b8563aba5d8ffbf2ce1944331ff7294a0ec7c03
|
||||
SwiftProtobuf: 6ef3f0e422ef90d6605ca20b21a94f6c1324d6b3
|
||||
SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780
|
||||
|
|
|
@ -3,6 +3,12 @@ import 'wallets/crypto_currency/intermediate/frost_currency.dart';
|
|||
|
||||
part 'app_config.g.dart';
|
||||
|
||||
enum AppFeature {
|
||||
themeSelection,
|
||||
buy,
|
||||
swap;
|
||||
}
|
||||
|
||||
abstract class AppConfig {
|
||||
static const appName = _prefix + _separator + suffix;
|
||||
|
||||
|
@ -12,6 +18,8 @@ abstract class AppConfig {
|
|||
static String get appDefaultDataDirName => _appDataDirName;
|
||||
static String get commitHash => _commitHash;
|
||||
|
||||
static bool hasFeature(AppFeature feature) => _features.contains(feature);
|
||||
|
||||
static ({String light, String dark})? get appIconAsset => _appIconAsset;
|
||||
|
||||
static List<CryptoCurrency> get coins => _supportedCoins;
|
||||
|
|
|
@ -422,6 +422,20 @@ class DbVersionMigrator with WalletDB {
|
|||
// try to continue migrating
|
||||
return await migrate(12, secureStore: secureStore);
|
||||
|
||||
case 12:
|
||||
// migrate
|
||||
await _v12(secureStore);
|
||||
|
||||
// update version
|
||||
await DB.instance.put<dynamic>(
|
||||
boxName: DB.boxNameDBInfo,
|
||||
key: "hive_data_version",
|
||||
value: 13,
|
||||
);
|
||||
|
||||
// try to continue migrating
|
||||
return await migrate(13, secureStore: secureStore);
|
||||
|
||||
default:
|
||||
// finally return
|
||||
return;
|
||||
|
@ -701,4 +715,15 @@ class DbVersionMigrator with WalletDB {
|
|||
Future<void> _v11(SecureStorageInterface secureStore) async {
|
||||
await migrateWalletsToIsar(secureStore: secureStore);
|
||||
}
|
||||
|
||||
Future<void> _v12(SecureStorageInterface secureStore) async {
|
||||
for (final identifier in ["firo", "firoTestNet"]) {
|
||||
await DB.instance.deleteBoxFromDisk(
|
||||
boxName: "${identifier}_anonymitySetSparkCache",
|
||||
);
|
||||
await DB.instance.deleteBoxFromDisk(
|
||||
boxName: "${identifier}_sparkUsedCoinsTagsCache",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import 'dart:isolate';
|
|||
import 'package:cw_core/wallet_info.dart' as xmr;
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mutex/mutex.dart';
|
||||
|
||||
import '../../app_config.dart';
|
||||
import '../../models/exchange/response_objects/trade.dart';
|
||||
import '../../models/node_model.dart';
|
||||
|
@ -55,12 +56,8 @@ class DB {
|
|||
// firo only
|
||||
String _boxNameSetCache({required CryptoCurrency currency}) =>
|
||||
"${currency.identifier}_anonymitySetCache";
|
||||
String _boxNameSetSparkCache({required CryptoCurrency currency}) =>
|
||||
"${currency.identifier}_anonymitySetSparkCache";
|
||||
String _boxNameUsedSerialsCache({required CryptoCurrency currency}) =>
|
||||
"${currency.identifier}_usedSerialsCache";
|
||||
String _boxNameSparkUsedCoinsTagsCache({required CryptoCurrency currency}) =>
|
||||
"${currency.identifier}_sparkUsedCoinsTagsCache";
|
||||
|
||||
Box<NodeModel>? _boxNodeModels;
|
||||
Box<NodeModel>? _boxPrimaryNodes;
|
||||
|
@ -81,7 +78,6 @@ class DB {
|
|||
|
||||
final Map<String, Box<dynamic>> _txCacheBoxes = {};
|
||||
final Map<String, Box<dynamic>> _setCacheBoxes = {};
|
||||
final Map<String, Box<dynamic>> _setSparkCacheBoxes = {};
|
||||
final Map<String, Box<dynamic>> _usedSerialsCacheBoxes = {};
|
||||
final Map<String, Box<dynamic>> _getSparkUsedCoinsTagsCacheBoxes = {};
|
||||
|
||||
|
@ -213,16 +209,6 @@ class DB {
|
|||
await Hive.openBox<dynamic>(_boxNameSetCache(currency: currency));
|
||||
}
|
||||
|
||||
Future<Box<dynamic>> getSparkAnonymitySetCacheBox({
|
||||
required CryptoCurrency currency,
|
||||
}) async {
|
||||
if (_setSparkCacheBoxes[currency.identifier]?.isOpen != true) {
|
||||
_setSparkCacheBoxes.remove(currency.identifier);
|
||||
}
|
||||
return _setSparkCacheBoxes[currency.identifier] ??=
|
||||
await Hive.openBox<dynamic>(_boxNameSetSparkCache(currency: currency));
|
||||
}
|
||||
|
||||
Future<void> closeAnonymitySetCacheBox({
|
||||
required CryptoCurrency currency,
|
||||
}) async {
|
||||
|
@ -241,18 +227,6 @@ class DB {
|
|||
);
|
||||
}
|
||||
|
||||
Future<Box<dynamic>> getSparkUsedCoinsTagsCacheBox({
|
||||
required CryptoCurrency currency,
|
||||
}) async {
|
||||
if (_getSparkUsedCoinsTagsCacheBoxes[currency.identifier]?.isOpen != true) {
|
||||
_getSparkUsedCoinsTagsCacheBoxes.remove(currency.identifier);
|
||||
}
|
||||
return _getSparkUsedCoinsTagsCacheBoxes[currency.identifier] ??=
|
||||
await Hive.openBox<dynamic>(
|
||||
_boxNameSparkUsedCoinsTagsCache(currency: currency),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> closeUsedSerialsCacheBox({
|
||||
required CryptoCurrency currency,
|
||||
}) async {
|
||||
|
@ -266,15 +240,9 @@ class DB {
|
|||
await deleteAll<dynamic>(boxName: _boxNameTxCache(currency: currency));
|
||||
if (currency is Firo) {
|
||||
await deleteAll<dynamic>(boxName: _boxNameSetCache(currency: currency));
|
||||
await deleteAll<dynamic>(
|
||||
boxName: _boxNameSetSparkCache(currency: currency),
|
||||
);
|
||||
await deleteAll<dynamic>(
|
||||
boxName: _boxNameUsedSerialsCache(currency: currency),
|
||||
);
|
||||
await deleteAll<dynamic>(
|
||||
boxName: _boxNameSparkUsedCoinsTagsCache(currency: currency),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
import 'package:decimal/decimal.dart';
|
||||
import 'package:flutter_native_splash/cli_commands.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
import '../../exceptions/main_db/main_db_exception.dart';
|
||||
import '../../models/isar/models/block_explorer.dart';
|
||||
import '../../models/isar/models/blockchain_data/v2/transaction_v2.dart';
|
||||
|
@ -26,7 +28,6 @@ import '../../wallets/isar/models/spark_coin.dart';
|
|||
import '../../wallets/isar/models/token_wallet_info.dart';
|
||||
import '../../wallets/isar/models/wallet_info.dart';
|
||||
import '../../wallets/isar/models/wallet_info_meta.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
part '../queries/queries.dart';
|
||||
|
||||
|
@ -149,8 +150,9 @@ class MainDB {
|
|||
}
|
||||
|
||||
// tx block explorers
|
||||
TransactionBlockExplorer? getTransactionBlockExplorer(
|
||||
{required CryptoCurrency cryptoCurrency}) {
|
||||
TransactionBlockExplorer? getTransactionBlockExplorer({
|
||||
required CryptoCurrency cryptoCurrency,
|
||||
}) {
|
||||
return isar.transactionBlockExplorers
|
||||
.where()
|
||||
.tickerEqualTo(cryptoCurrency.ticker)
|
||||
|
|
|
@ -207,7 +207,8 @@ Future<void> migrateWalletsToIsar({
|
|||
}
|
||||
|
||||
await _cleanupOnSuccess(
|
||||
walletIds: newInfo.map((e) => e.$1.walletId).toList());
|
||||
walletIds: newInfo.map((e) => e.$1.walletId).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _cleanupOnSuccess({required List<String> walletIds}) async {
|
||||
|
|
164
lib/db/sqlite/firo_cache.dart
Normal file
|
@ -0,0 +1,164 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_libsparkmobile/flutter_libsparkmobile.dart';
|
||||
import 'package:mutex/mutex.dart';
|
||||
import 'package:sqlite3/sqlite3.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
import '../../electrumx_rpc/electrumx_client.dart';
|
||||
import '../../utilities/extensions/extensions.dart';
|
||||
import '../../utilities/logger.dart';
|
||||
import '../../utilities/stack_file_system.dart';
|
||||
|
||||
part 'firo_cache_coordinator.dart';
|
||||
part 'firo_cache_reader.dart';
|
||||
part 'firo_cache_worker.dart';
|
||||
part 'firo_cache_writer.dart';
|
||||
|
||||
/// Temporary debugging log function for this file
|
||||
void _debugLog(Object? object) {
|
||||
if (kDebugMode) {
|
||||
Logging.instance.log(
|
||||
object,
|
||||
level: LogLevel.Debug,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class _FiroCache {
|
||||
static const int _setCacheVersion = 1;
|
||||
static const int _tagsCacheVersion = 1;
|
||||
static const String sparkSetCacheFileName =
|
||||
"spark_set_v$_setCacheVersion.sqlite3";
|
||||
static const String sparkUsedTagsCacheFileName =
|
||||
"spark_tags_v$_tagsCacheVersion.sqlite3";
|
||||
|
||||
static Database? _setCacheDB;
|
||||
static Database? _usedTagsCacheDB;
|
||||
static Database get setCacheDB {
|
||||
if (_setCacheDB == null) {
|
||||
throw Exception(
|
||||
"FiroCache.init() must be called before accessing FiroCache.db!",
|
||||
);
|
||||
}
|
||||
return _setCacheDB!;
|
||||
}
|
||||
|
||||
static Database get usedTagsCacheDB {
|
||||
if (_usedTagsCacheDB == null) {
|
||||
throw Exception(
|
||||
"FiroCache.init() must be called before accessing FiroCache.db!",
|
||||
);
|
||||
}
|
||||
return _usedTagsCacheDB!;
|
||||
}
|
||||
|
||||
static Future<void>? _initFuture;
|
||||
static Future<void> init() => _initFuture ??= _init();
|
||||
|
||||
static Future<void> _init() async {
|
||||
final sqliteDir =
|
||||
await StackFileSystem.applicationFiroCacheSQLiteDirectory();
|
||||
|
||||
final sparkSetCacheFile = File("${sqliteDir.path}/$sparkSetCacheFileName");
|
||||
final sparkUsedTagsCacheFile =
|
||||
File("${sqliteDir.path}/$sparkUsedTagsCacheFileName");
|
||||
|
||||
if (!(await sparkSetCacheFile.exists())) {
|
||||
await _createSparkSetCacheDb(sparkSetCacheFile.path);
|
||||
}
|
||||
if (!(await sparkUsedTagsCacheFile.exists())) {
|
||||
await _createSparkUsedTagsCacheDb(sparkUsedTagsCacheFile.path);
|
||||
}
|
||||
|
||||
_setCacheDB = sqlite3.open(
|
||||
sparkSetCacheFile.path,
|
||||
mode: OpenMode.readWrite,
|
||||
);
|
||||
_usedTagsCacheDB = sqlite3.open(
|
||||
sparkUsedTagsCacheFile.path,
|
||||
mode: OpenMode.readWrite,
|
||||
);
|
||||
}
|
||||
|
||||
static Future<void> _deleteAllCache() async {
|
||||
final start = DateTime.now();
|
||||
setCacheDB.execute(
|
||||
"""
|
||||
DELETE FROM SparkSet;
|
||||
DELETE FROM SparkCoin;
|
||||
DELETE FROM SparkSetCoins;
|
||||
VACUUM;
|
||||
""",
|
||||
);
|
||||
usedTagsCacheDB.execute(
|
||||
"""
|
||||
DELETE FROM SparkUsedCoinTags;
|
||||
VACUUM;
|
||||
""",
|
||||
);
|
||||
_debugLog(
|
||||
"_deleteAllCache() "
|
||||
"duration = ${DateTime.now().difference(start)}",
|
||||
);
|
||||
}
|
||||
|
||||
static Future<void> _createSparkSetCacheDb(String file) async {
|
||||
final db = sqlite3.open(
|
||||
file,
|
||||
mode: OpenMode.readWriteCreate,
|
||||
);
|
||||
|
||||
db.execute(
|
||||
"""
|
||||
CREATE TABLE SparkSet (
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
|
||||
blockHash TEXT NOT NULL,
|
||||
setHash TEXT NOT NULL,
|
||||
groupId INTEGER NOT NULL,
|
||||
timestampUTC INTEGER NOT NULL,
|
||||
UNIQUE (blockHash, setHash, groupId)
|
||||
);
|
||||
|
||||
CREATE TABLE SparkCoin (
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
|
||||
serialized TEXT NOT NULL,
|
||||
txHash TEXT NOT NULL,
|
||||
context TEXT NOT NULL,
|
||||
UNIQUE(serialized, txHash, context)
|
||||
);
|
||||
|
||||
CREATE TABLE SparkSetCoins (
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
|
||||
setId INTEGER NOT NULL,
|
||||
coinId INTEGER NOT NULL,
|
||||
FOREIGN KEY (setId) REFERENCES SparkSet(id),
|
||||
FOREIGN KEY (coinId) REFERENCES SparkCoin(id)
|
||||
);
|
||||
""",
|
||||
);
|
||||
|
||||
db.dispose();
|
||||
}
|
||||
|
||||
static Future<void> _createSparkUsedTagsCacheDb(String file) async {
|
||||
final db = sqlite3.open(
|
||||
file,
|
||||
mode: OpenMode.readWriteCreate,
|
||||
);
|
||||
|
||||
db.execute(
|
||||
"""
|
||||
CREATE TABLE SparkUsedCoinTags (
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
|
||||
tag TEXT NOT NULL UNIQUE
|
||||
);
|
||||
""",
|
||||
);
|
||||
|
||||
db.dispose();
|
||||
}
|
||||
}
|
166
lib/db/sqlite/firo_cache_coordinator.dart
Normal file
|
@ -0,0 +1,166 @@
|
|||
part of 'firo_cache.dart';
|
||||
|
||||
/// Wrapper class for [_FiroCache] as [_FiroCache] should eventually be handled in a
|
||||
/// background isolate and [FiroCacheCoordinator] should manage that isolate
|
||||
abstract class FiroCacheCoordinator {
|
||||
static _FiroCacheWorker? _worker;
|
||||
|
||||
static bool _init = false;
|
||||
static Future<void> init() async {
|
||||
if (_init) {
|
||||
return;
|
||||
}
|
||||
_init = true;
|
||||
await _FiroCache.init();
|
||||
_worker = await _FiroCacheWorker.spawn();
|
||||
}
|
||||
|
||||
static Future<void> clearSharedCache() async {
|
||||
return await _FiroCache._deleteAllCache();
|
||||
}
|
||||
|
||||
static Future<String> getSparkCacheSize() async {
|
||||
final dir = await StackFileSystem.applicationFiroCacheSQLiteDirectory();
|
||||
final setCacheFile = File(
|
||||
"${dir.path}/${_FiroCache.sparkSetCacheFileName}",
|
||||
);
|
||||
final usedTagsCacheFile = File(
|
||||
"${dir.path}/${_FiroCache.sparkUsedTagsCacheFileName}",
|
||||
);
|
||||
final int bytes =
|
||||
((await setCacheFile.exists()) ? await setCacheFile.length() : 0) +
|
||||
((await usedTagsCacheFile.exists())
|
||||
? await usedTagsCacheFile.length()
|
||||
: 0);
|
||||
|
||||
if (bytes < 1024) {
|
||||
return '$bytes B';
|
||||
} else if (bytes < 1048576) {
|
||||
final double kbSize = bytes / 1024;
|
||||
return '${kbSize.toStringAsFixed(2)} KB';
|
||||
} else if (bytes < 1073741824) {
|
||||
final double mbSize = bytes / 1048576;
|
||||
return '${mbSize.toStringAsFixed(2)} MB';
|
||||
} else {
|
||||
final double gbSize = bytes / 1073741824;
|
||||
return '${gbSize.toStringAsFixed(2)} GB';
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> runFetchAndUpdateSparkUsedCoinTags(
|
||||
ElectrumXClient client,
|
||||
) async {
|
||||
final count = await FiroCacheCoordinator.getUsedCoinTagsLastAddedRowId();
|
||||
final unhashedTags = await client.getSparkUnhashedUsedCoinsTags(
|
||||
startNumber: count,
|
||||
);
|
||||
if (unhashedTags.isNotEmpty) {
|
||||
await _worker!.runTask(
|
||||
FCTask(
|
||||
func: FCFuncName._updateSparkUsedTagsWith,
|
||||
data: unhashedTags,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> runFetchAndUpdateSparkAnonSetCacheForGroupId(
|
||||
int groupId,
|
||||
ElectrumXClient client,
|
||||
) async {
|
||||
final blockhashResult =
|
||||
await FiroCacheCoordinator.getLatestSetInfoForGroupId(
|
||||
groupId,
|
||||
);
|
||||
final blockHash = blockhashResult?.blockHash ?? "";
|
||||
|
||||
final json = await client.getSparkAnonymitySet(
|
||||
coinGroupId: groupId.toString(),
|
||||
startBlockHash: blockHash.toHexReversedFromBase64,
|
||||
);
|
||||
|
||||
await _worker!.runTask(
|
||||
FCTask(
|
||||
func: FCFuncName._updateSparkAnonSetCoinsWith,
|
||||
data: (groupId, json),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
|
||||
static Future<Set<String>> getUsedCoinTags(int startNumber) async {
|
||||
final result = await _Reader._getSparkUsedCoinTags(
|
||||
startNumber,
|
||||
db: _FiroCache.usedTagsCacheDB,
|
||||
);
|
||||
return result.map((e) => e["tag"] as String).toSet();
|
||||
}
|
||||
|
||||
/// This should be the equivalent of counting the number of tags in the db.
|
||||
/// Assuming the integrity of the data. Faster than actually calling count on
|
||||
/// a table where no records have been deleted. None should be deleted from
|
||||
/// this table in practice.
|
||||
static Future<int> getUsedCoinTagsLastAddedRowId() async {
|
||||
final result = await _Reader._getUsedCoinTagsLastAddedRowId(
|
||||
db: _FiroCache.usedTagsCacheDB,
|
||||
);
|
||||
if (result.isEmpty) {
|
||||
return 0;
|
||||
}
|
||||
return result.first["highestId"] as int? ?? 0;
|
||||
}
|
||||
|
||||
static Future<bool> checkTagIsUsed(
|
||||
String tag,
|
||||
) async {
|
||||
return await _Reader._checkTagIsUsed(
|
||||
tag,
|
||||
db: _FiroCache.usedTagsCacheDB,
|
||||
);
|
||||
}
|
||||
|
||||
static Future<ResultSet> getSetCoinsForGroupId(
|
||||
int groupId, {
|
||||
int? newerThanTimeStamp,
|
||||
}) async {
|
||||
return await _Reader._getSetCoinsForGroupId(
|
||||
groupId,
|
||||
db: _FiroCache.setCacheDB,
|
||||
newerThanTimeStamp: newerThanTimeStamp,
|
||||
);
|
||||
}
|
||||
|
||||
static Future<
|
||||
({
|
||||
String blockHash,
|
||||
String setHash,
|
||||
int timestampUTC,
|
||||
})?> getLatestSetInfoForGroupId(
|
||||
int groupId,
|
||||
) async {
|
||||
final result = await _Reader._getLatestSetInfoForGroupId(
|
||||
groupId,
|
||||
db: _FiroCache.setCacheDB,
|
||||
);
|
||||
|
||||
if (result.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
blockHash: result.first["blockHash"] as String,
|
||||
setHash: result.first["setHash"] as String,
|
||||
timestampUTC: result.first["timestampUTC"] as int,
|
||||
);
|
||||
}
|
||||
|
||||
static Future<bool> checkSetInfoForGroupIdExists(
|
||||
int groupId,
|
||||
) async {
|
||||
return await _Reader._checkSetInfoForGroupIdExists(
|
||||
groupId,
|
||||
db: _FiroCache.setCacheDB,
|
||||
);
|
||||
}
|
||||
}
|
103
lib/db/sqlite/firo_cache_reader.dart
Normal file
|
@ -0,0 +1,103 @@
|
|||
part of 'firo_cache.dart';
|
||||
|
||||
/// Keep all fetch queries in this separate file
|
||||
abstract class _Reader {
|
||||
// ===========================================================================
|
||||
// =============== Spark anonymity set queries ===============================
|
||||
|
||||
static Future<ResultSet> _getSetCoinsForGroupId(
|
||||
int groupId, {
|
||||
required Database db,
|
||||
int? newerThanTimeStamp,
|
||||
}) async {
|
||||
String query = """
|
||||
SELECT sc.serialized, sc.txHash, sc.context
|
||||
FROM SparkSet AS ss
|
||||
JOIN SparkSetCoins AS ssc ON ss.id = ssc.setId
|
||||
JOIN SparkCoin AS sc ON ssc.coinId = sc.id
|
||||
WHERE ss.groupId = $groupId
|
||||
""";
|
||||
|
||||
if (newerThanTimeStamp != null) {
|
||||
query += " AND ss.timestampUTC"
|
||||
" > $newerThanTimeStamp";
|
||||
}
|
||||
|
||||
return db.select("$query;");
|
||||
}
|
||||
|
||||
static Future<ResultSet> _getLatestSetInfoForGroupId(
|
||||
int groupId, {
|
||||
required Database db,
|
||||
}) async {
|
||||
final query = """
|
||||
SELECT ss.blockHash, ss.setHash, ss.timestampUTC
|
||||
FROM SparkSet ss
|
||||
WHERE ss.groupId = $groupId
|
||||
ORDER BY ss.timestampUTC DESC
|
||||
LIMIT 1;
|
||||
""";
|
||||
|
||||
return db.select("$query;");
|
||||
}
|
||||
|
||||
static Future<bool> _checkSetInfoForGroupIdExists(
|
||||
int groupId, {
|
||||
required Database db,
|
||||
}) async {
|
||||
final query = """
|
||||
SELECT EXISTS (
|
||||
SELECT 1
|
||||
FROM SparkSet
|
||||
WHERE groupId = $groupId
|
||||
) AS setExists;
|
||||
""";
|
||||
|
||||
return db.select("$query;").first["setExists"] == 1;
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// =============== Spark used coin tags queries ==============================
|
||||
|
||||
static Future<ResultSet> _getSparkUsedCoinTags(
|
||||
int startNumber, {
|
||||
required Database db,
|
||||
}) async {
|
||||
String query = """
|
||||
SELECT tag
|
||||
FROM SparkUsedCoinTags
|
||||
""";
|
||||
|
||||
if (startNumber > 0) {
|
||||
query += " WHERE id >= $startNumber";
|
||||
}
|
||||
|
||||
return db.select("$query;");
|
||||
}
|
||||
|
||||
static Future<ResultSet> _getUsedCoinTagsLastAddedRowId({
|
||||
required Database db,
|
||||
}) async {
|
||||
const query = """
|
||||
SELECT MAX(id) AS highestId
|
||||
FROM SparkUsedCoinTags;
|
||||
""";
|
||||
|
||||
return db.select("$query;");
|
||||
}
|
||||
|
||||
static Future<bool> _checkTagIsUsed(
|
||||
String tag, {
|
||||
required Database db,
|
||||
}) async {
|
||||
final query = """
|
||||
SELECT EXISTS (
|
||||
SELECT 1
|
||||
FROM SparkUsedCoinTags
|
||||
WHERE tag = '$tag'
|
||||
) AS tagExists;
|
||||
""";
|
||||
|
||||
return db.select("$query;").first["tagExists"] == 1;
|
||||
}
|
||||
}
|
140
lib/db/sqlite/firo_cache_worker.dart
Normal file
|
@ -0,0 +1,140 @@
|
|||
part of 'firo_cache.dart';
|
||||
|
||||
enum FCFuncName {
|
||||
_updateSparkAnonSetCoinsWith,
|
||||
_updateSparkUsedTagsWith,
|
||||
}
|
||||
|
||||
class FCTask {
|
||||
final id = const Uuid().v4();
|
||||
final FCFuncName func;
|
||||
final dynamic data;
|
||||
|
||||
FCTask({required this.func, required this.data});
|
||||
}
|
||||
|
||||
class _FiroCacheWorker {
|
||||
final SendPort _commands;
|
||||
final ReceivePort _responses;
|
||||
final Map<String, Completer<Object?>> _activeRequests = {};
|
||||
|
||||
Future<Object?> runTask(FCTask task) async {
|
||||
final completer = Completer<Object?>.sync();
|
||||
_activeRequests[task.id] = completer;
|
||||
_commands.send(task);
|
||||
return await completer.future;
|
||||
}
|
||||
|
||||
static Future<_FiroCacheWorker> spawn() async {
|
||||
final dir = await StackFileSystem.applicationFiroCacheSQLiteDirectory();
|
||||
final setCacheFilePath = "${dir.path}/${_FiroCache.sparkSetCacheFileName}";
|
||||
final usedTagsCacheFilePath =
|
||||
"${dir.path}/${_FiroCache.sparkUsedTagsCacheFileName}";
|
||||
|
||||
final initPort = RawReceivePort();
|
||||
final connection = Completer<(ReceivePort, SendPort)>.sync();
|
||||
|
||||
initPort.handler = (dynamic initialMessage) {
|
||||
final commandPort = initialMessage as SendPort;
|
||||
connection.complete(
|
||||
(
|
||||
ReceivePort.fromRawReceivePort(initPort),
|
||||
commandPort,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
try {
|
||||
await Isolate.spawn(
|
||||
_startWorkerIsolate,
|
||||
(initPort.sendPort, setCacheFilePath, usedTagsCacheFilePath),
|
||||
);
|
||||
} catch (_) {
|
||||
initPort.close();
|
||||
rethrow;
|
||||
}
|
||||
|
||||
final (receivePort, sendPort) = await connection.future;
|
||||
|
||||
return _FiroCacheWorker._(receivePort, sendPort);
|
||||
}
|
||||
|
||||
_FiroCacheWorker._(this._responses, this._commands) {
|
||||
_responses.listen(_handleResponsesFromIsolate);
|
||||
}
|
||||
|
||||
void _handleResponsesFromIsolate(dynamic message) {
|
||||
final (id, error) = message as (String, Object?);
|
||||
final completer = _activeRequests.remove(id)!;
|
||||
|
||||
if (error != null) {
|
||||
completer.completeError(error);
|
||||
} else {
|
||||
completer.complete(id);
|
||||
}
|
||||
}
|
||||
|
||||
static void _handleCommandsToIsolate(
|
||||
ReceivePort receivePort,
|
||||
SendPort sendPort,
|
||||
Database setCacheDb,
|
||||
Database usedTagsCacheDb,
|
||||
Mutex mutex,
|
||||
) {
|
||||
receivePort.listen((message) {
|
||||
final task = message as FCTask;
|
||||
|
||||
mutex.protect(() async {
|
||||
try {
|
||||
final FCResult result;
|
||||
switch (task.func) {
|
||||
case FCFuncName._updateSparkAnonSetCoinsWith:
|
||||
final data = task.data as (int, Map<String, dynamic>);
|
||||
result = _updateSparkAnonSetCoinsWith(
|
||||
setCacheDb,
|
||||
data.$2,
|
||||
data.$1,
|
||||
);
|
||||
break;
|
||||
|
||||
case FCFuncName._updateSparkUsedTagsWith:
|
||||
result = _updateSparkUsedTagsWith(
|
||||
usedTagsCacheDb,
|
||||
task.data as List<String>,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
if (result.success) {
|
||||
sendPort.send((task.id, null));
|
||||
} else {
|
||||
sendPort.send((task.id, result.error!));
|
||||
}
|
||||
} catch (e) {
|
||||
sendPort.send((task.id, e));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
static void _startWorkerIsolate((SendPort, String, String) args) {
|
||||
final receivePort = ReceivePort();
|
||||
args.$1.send(receivePort.sendPort);
|
||||
final mutex = Mutex();
|
||||
final setCacheDb = sqlite3.open(
|
||||
args.$2,
|
||||
mode: OpenMode.readWrite,
|
||||
);
|
||||
final usedTagsCacheDb = sqlite3.open(
|
||||
args.$3,
|
||||
mode: OpenMode.readWrite,
|
||||
);
|
||||
_handleCommandsToIsolate(
|
||||
receivePort,
|
||||
args.$1,
|
||||
setCacheDb,
|
||||
usedTagsCacheDb,
|
||||
mutex,
|
||||
);
|
||||
}
|
||||
}
|
169
lib/db/sqlite/firo_cache_writer.dart
Normal file
|
@ -0,0 +1,169 @@
|
|||
part of 'firo_cache.dart';
|
||||
|
||||
class FCResult {
|
||||
final bool success;
|
||||
final Object? error;
|
||||
|
||||
FCResult({required this.success, this.error});
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// ================== write to spark used tags cache =========================
|
||||
|
||||
/// update the sqlite cache
|
||||
/// Expected json format:
|
||||
/// returns true if successful, otherwise some exception
|
||||
FCResult _updateSparkUsedTagsWith(
|
||||
Database db,
|
||||
List<String> tags,
|
||||
) {
|
||||
// hash the tags here since this function is called in a background isolate
|
||||
final hashedTags = LibSpark.hashTags(base64Tags: tags);
|
||||
|
||||
if (hashedTags.isEmpty) {
|
||||
// nothing to add, return early
|
||||
return FCResult(success: true);
|
||||
}
|
||||
|
||||
db.execute("BEGIN;");
|
||||
try {
|
||||
for (final tag in hashedTags) {
|
||||
db.execute(
|
||||
"""
|
||||
INSERT OR IGNORE INTO SparkUsedCoinTags (tag)
|
||||
VALUES (?);
|
||||
""",
|
||||
[tag],
|
||||
);
|
||||
}
|
||||
|
||||
db.execute("COMMIT;");
|
||||
|
||||
return FCResult(success: true);
|
||||
} catch (e) {
|
||||
db.execute("ROLLBACK;");
|
||||
return FCResult(success: false, error: e);
|
||||
}
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// ================== write to spark anon set cache ==========================
|
||||
|
||||
/// update the sqlite cache
|
||||
/// Expected json format:
|
||||
/// {
|
||||
/// "blockHash": "someBlockHash",
|
||||
/// "setHash": "someSetHash",
|
||||
/// "coins": [
|
||||
/// ["serliazed1", "hash1", "context1"],
|
||||
/// ["serliazed2", "hash2", "context2"],
|
||||
/// ...
|
||||
/// ["serliazed3", "hash3", "context3"],
|
||||
/// ["serliazed4", "hash4", "context4"],
|
||||
/// ],
|
||||
/// }
|
||||
///
|
||||
/// returns true if successful, otherwise false
|
||||
FCResult _updateSparkAnonSetCoinsWith(
|
||||
Database db,
|
||||
Map<String, dynamic> json,
|
||||
int groupId,
|
||||
) {
|
||||
final blockHash = json["blockHash"] as String;
|
||||
final setHash = json["setHash"] as String;
|
||||
final coinsRaw = json["coins"] as List;
|
||||
|
||||
if (coinsRaw.isEmpty) {
|
||||
// no coins to actually insert
|
||||
return FCResult(success: true);
|
||||
}
|
||||
|
||||
final checkResult = db.select(
|
||||
"""
|
||||
SELECT *
|
||||
FROM SparkSet
|
||||
WHERE blockHash = ? AND setHash = ? AND groupId = ?;
|
||||
""",
|
||||
[
|
||||
blockHash,
|
||||
setHash,
|
||||
groupId,
|
||||
],
|
||||
);
|
||||
|
||||
if (checkResult.isNotEmpty) {
|
||||
// already up to date
|
||||
return FCResult(success: true);
|
||||
}
|
||||
|
||||
final coins = coinsRaw
|
||||
.map(
|
||||
(e) => [
|
||||
e[0] as String,
|
||||
e[1] as String,
|
||||
e[2] as String,
|
||||
],
|
||||
)
|
||||
.toList();
|
||||
|
||||
final timestamp = DateTime.now().toUtc().millisecondsSinceEpoch ~/ 1000;
|
||||
|
||||
db.execute("BEGIN;");
|
||||
try {
|
||||
db.execute(
|
||||
"""
|
||||
INSERT INTO SparkSet (blockHash, setHash, groupId, timestampUTC)
|
||||
VALUES (?, ?, ?, ?);
|
||||
""",
|
||||
[blockHash, setHash, groupId, timestamp],
|
||||
);
|
||||
final setId = db.lastInsertRowId;
|
||||
|
||||
for (final coin in coins) {
|
||||
int coinId;
|
||||
try {
|
||||
// try to insert and get row id
|
||||
db.execute(
|
||||
"""
|
||||
INSERT INTO SparkCoin (serialized, txHash, context)
|
||||
VALUES (?, ?, ?);
|
||||
""",
|
||||
coin,
|
||||
);
|
||||
coinId = db.lastInsertRowId;
|
||||
} on SqliteException catch (e) {
|
||||
// if there already is a matching coin in the db
|
||||
// just grab its row id
|
||||
if (e.extendedResultCode == 2067) {
|
||||
final result = db.select(
|
||||
"""
|
||||
SELECT id
|
||||
FROM SparkCoin
|
||||
WHERE serialized = ? AND txHash = ? AND context = ?;
|
||||
""",
|
||||
coin,
|
||||
);
|
||||
coinId = result.first["id"] as int;
|
||||
} else {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
// finally add the row id to the newly added set
|
||||
db.execute(
|
||||
"""
|
||||
INSERT INTO SparkSetCoins (setId, coinId)
|
||||
VALUES (?, ?);
|
||||
""",
|
||||
[setId, coinId],
|
||||
);
|
||||
}
|
||||
|
||||
db.execute("COMMIT;");
|
||||
|
||||
return FCResult(success: true);
|
||||
} catch (e) {
|
||||
db.execute("ROLLBACK;");
|
||||
return FCResult(success: false, error: e);
|
||||
}
|
||||
}
|
|
@ -11,7 +11,6 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import '../../utilities/amount/amount.dart';
|
||||
import '../../wallets/crypto_currency/coins/ethereum.dart';
|
||||
import '../../wallets/crypto_currency/crypto_currency.dart';
|
||||
|
||||
class EthTokenTxExtraDTO {
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import '../../utilities/amount/amount.dart';
|
||||
import '../../wallets/crypto_currency/coins/ethereum.dart';
|
||||
import '../../wallets/crypto_currency/crypto_currency.dart';
|
||||
|
||||
class EthTxDTO {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import 'litescribe_response.dart';
|
||||
import 'inscription_data.dart';
|
||||
import 'litescribe_response.dart';
|
||||
|
||||
class AddressInscriptionResponse extends LitescribeResponse<AddressInscriptionResponse> {
|
||||
class AddressInscriptionResponse
|
||||
extends LitescribeResponse<AddressInscriptionResponse> {
|
||||
final int status;
|
||||
final String message;
|
||||
final AddressInscriptionResult result;
|
||||
|
@ -16,7 +17,8 @@ class AddressInscriptionResponse extends LitescribeResponse<AddressInscriptionRe
|
|||
return AddressInscriptionResponse(
|
||||
status: json['status'] as int,
|
||||
message: json['message'] as String,
|
||||
result: AddressInscriptionResult.fromJson(json['result'] as Map<String, dynamic>),
|
||||
result: AddressInscriptionResult.fromJson(
|
||||
json['result'] as Map<String, dynamic>),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +34,9 @@ class AddressInscriptionResult {
|
|||
|
||||
factory AddressInscriptionResult.fromJson(Map<String, dynamic> json) {
|
||||
return AddressInscriptionResult(
|
||||
list: (json['list'] as List).map((item) => InscriptionData.fromJson(item as Map<String, dynamic>)).toList(),
|
||||
list: (json['list'] as List)
|
||||
.map((item) => InscriptionData.fromJson(item as Map<String, dynamic>))
|
||||
.toList(),
|
||||
total: json['total'] as int,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -11,11 +11,12 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:string_validator/string_validator.dart';
|
||||
|
||||
import '../db/hive/db.dart';
|
||||
import 'electrumx_client.dart';
|
||||
import '../utilities/logger.dart';
|
||||
import '../wallets/crypto_currency/crypto_currency.dart';
|
||||
import 'package:string_validator/string_validator.dart';
|
||||
import 'electrumx_client.dart';
|
||||
|
||||
class CachedElectrumXClient {
|
||||
final ElectrumXClient electrumXClient;
|
||||
|
@ -115,70 +116,6 @@ class CachedElectrumXClient {
|
|||
}
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> getSparkAnonymitySet({
|
||||
required String groupId,
|
||||
String blockhash = "",
|
||||
required CryptoCurrency cryptoCurrency,
|
||||
required bool useOnlyCacheIfNotEmpty,
|
||||
}) async {
|
||||
try {
|
||||
final box = await DB.instance.getSparkAnonymitySetCacheBox(
|
||||
currency: cryptoCurrency,
|
||||
);
|
||||
final cachedSet = box.get(groupId) as Map?;
|
||||
|
||||
Map<String, dynamic> set;
|
||||
|
||||
// null check to see if there is a cached set
|
||||
if (cachedSet == null) {
|
||||
set = {
|
||||
"coinGroupID": int.parse(groupId),
|
||||
"blockHash": blockhash,
|
||||
"setHash": "",
|
||||
"coins": <dynamic>[],
|
||||
};
|
||||
} else {
|
||||
set = Map<String, dynamic>.from(cachedSet);
|
||||
if (useOnlyCacheIfNotEmpty) {
|
||||
return set;
|
||||
}
|
||||
}
|
||||
|
||||
final newSet = await electrumXClient.getSparkAnonymitySet(
|
||||
coinGroupId: groupId,
|
||||
startBlockHash: set["blockHash"] as String,
|
||||
);
|
||||
|
||||
// update set with new data
|
||||
if (newSet["setHash"] != "" && set["setHash"] != newSet["setHash"]) {
|
||||
set["setHash"] = newSet["setHash"];
|
||||
set["blockHash"] = newSet["blockHash"];
|
||||
for (int i = (newSet["coins"] as List).length - 1; i >= 0; i--) {
|
||||
// TODO verify this is correct (or append?)
|
||||
if ((set["coins"] as List)
|
||||
.where((e) => e[0] == newSet["coins"][i][0])
|
||||
.isEmpty) {
|
||||
set["coins"].insert(0, newSet["coins"][i]);
|
||||
}
|
||||
}
|
||||
// save set to db
|
||||
await box.put(groupId, set);
|
||||
Logging.instance.log(
|
||||
"Updated current anonymity set for ${cryptoCurrency.identifier} with group ID $groupId",
|
||||
level: LogLevel.Info,
|
||||
);
|
||||
}
|
||||
|
||||
return set;
|
||||
} catch (e, s) {
|
||||
Logging.instance.log(
|
||||
"Failed to process CachedElectrumX.getSparkAnonymitySet(): $e\n$s",
|
||||
level: LogLevel.Error,
|
||||
);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
String base64ToHex(String source) =>
|
||||
base64Decode(LineSplitter.split(source).join())
|
||||
.map((e) => e.toRadixString(16).padLeft(2, '0'))
|
||||
|
@ -283,56 +220,10 @@ class CachedElectrumXClient {
|
|||
}
|
||||
}
|
||||
|
||||
Future<Set<String>> getSparkUsedCoinsTags({
|
||||
/// Clear all cached transactions for the specified coin
|
||||
Future<void> clearSharedTransactionCache({
|
||||
required CryptoCurrency cryptoCurrency,
|
||||
}) async {
|
||||
try {
|
||||
final box = await DB.instance.getSparkUsedCoinsTagsCacheBox(
|
||||
currency: cryptoCurrency,
|
||||
);
|
||||
|
||||
final _list = box.get("tags") as List?;
|
||||
|
||||
final Set<String> cachedTags =
|
||||
_list == null ? {} : List<String>.from(_list).toSet();
|
||||
|
||||
final startNumber = max(
|
||||
0,
|
||||
cachedTags.length - 100, // 100 being some arbitrary buffer
|
||||
);
|
||||
|
||||
final newTags = await electrumXClient.getSparkUsedCoinsTags(
|
||||
startNumber: startNumber,
|
||||
);
|
||||
|
||||
// ensure we are getting some overlap so we know we are not missing any
|
||||
if (cachedTags.isNotEmpty && newTags.isNotEmpty) {
|
||||
assert(cachedTags.intersection(newTags).isNotEmpty);
|
||||
}
|
||||
|
||||
// Make newTags an Iterable<String>.
|
||||
final Iterable<String> iterableTags = newTags.map((e) => e.toString());
|
||||
|
||||
cachedTags.addAll(iterableTags);
|
||||
|
||||
await box.put(
|
||||
"tags",
|
||||
cachedTags.toList(),
|
||||
);
|
||||
|
||||
return cachedTags;
|
||||
} catch (e, s) {
|
||||
Logging.instance.log(
|
||||
"Failed to process CachedElectrumX.getSparkUsedCoinsTags(): $e\n$s",
|
||||
level: LogLevel.Error,
|
||||
);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear all cached transactions for the specified coin
|
||||
Future<void> clearSharedTransactionCache(
|
||||
{required CryptoCurrency cryptoCurrency}) async {
|
||||
await DB.instance.clearSharedTransactionCache(currency: cryptoCurrency);
|
||||
await DB.instance.closeAnonymitySetCacheBox(currency: cryptoCurrency);
|
||||
}
|
||||
|
|
|
@ -17,10 +17,9 @@ import 'package:electrum_adapter/electrum_adapter.dart' as electrum_adapter;
|
|||
import 'package:electrum_adapter/electrum_adapter.dart';
|
||||
import 'package:electrum_adapter/methods/specific/firo.dart';
|
||||
import 'package:event_bus/event_bus.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_libsparkmobile/flutter_libsparkmobile.dart';
|
||||
import 'package:mutex/mutex.dart';
|
||||
import 'client_manager.dart';
|
||||
import 'package:stream_channel/stream_channel.dart';
|
||||
|
||||
import '../exceptions/electrumx/no_such_transaction.dart';
|
||||
import '../services/event_bus/events/global/tor_connection_status_changed_event.dart';
|
||||
import '../services/event_bus/events/global/tor_status_changed_event.dart';
|
||||
|
@ -28,10 +27,8 @@ import '../services/event_bus/global_event_bus.dart';
|
|||
import '../services/tor_service.dart';
|
||||
import '../utilities/logger.dart';
|
||||
import '../utilities/prefs.dart';
|
||||
import '../wallets/crypto_currency/coins/dogecoin.dart';
|
||||
import '../wallets/crypto_currency/coins/firo.dart';
|
||||
import '../wallets/crypto_currency/crypto_currency.dart';
|
||||
import 'package:stream_channel/stream_channel.dart';
|
||||
import 'client_manager.dart';
|
||||
|
||||
class WifiOnlyException implements Exception {}
|
||||
|
||||
|
@ -912,10 +909,7 @@ class ElectrumXClient {
|
|||
String? requestID,
|
||||
}) async {
|
||||
try {
|
||||
Logging.instance.log(
|
||||
"attempting to fetch spark.getsparkanonymityset...",
|
||||
level: LogLevel.Info,
|
||||
);
|
||||
final start = DateTime.now();
|
||||
await _checkElectrumAdapter();
|
||||
final Map<String, dynamic> response =
|
||||
await (getElectrumAdapter() as FiroElectrumClient)
|
||||
|
@ -924,7 +918,10 @@ class ElectrumXClient {
|
|||
startBlockHash: startBlockHash,
|
||||
);
|
||||
Logging.instance.log(
|
||||
"Fetching spark.getsparkanonymityset finished",
|
||||
"Finished ElectrumXClient.getSparkAnonymitySet(coinGroupId"
|
||||
"=$coinGroupId, startBlockHash=$startBlockHash). "
|
||||
"coins.length: ${(response["coins"] as List?)?.length}"
|
||||
"Duration=${DateTime.now().difference(start)}",
|
||||
level: LogLevel.Info,
|
||||
);
|
||||
return response;
|
||||
|
@ -933,18 +930,15 @@ class ElectrumXClient {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: update when we get new call to include tx hashes in response
|
||||
/// Takes [startNumber], if it is 0, we get the full set,
|
||||
/// otherwise the used tags after that number
|
||||
Future<Set<String>> getSparkUsedCoinsTags({
|
||||
Future<List<String>> getSparkUnhashedUsedCoinsTags({
|
||||
String? requestID,
|
||||
required int startNumber,
|
||||
}) async {
|
||||
try {
|
||||
// Use electrum_adapter package's getSparkUsedCoinsTags method.
|
||||
Logging.instance.log(
|
||||
"attempting to fetch spark.getusedcoinstags...",
|
||||
level: LogLevel.Info,
|
||||
);
|
||||
final start = DateTime.now();
|
||||
await _checkElectrumAdapter();
|
||||
final Map<String, dynamic> response =
|
||||
await (getElectrumAdapter() as FiroElectrumClient)
|
||||
|
@ -956,8 +950,16 @@ class ElectrumXClient {
|
|||
level: LogLevel.Info,
|
||||
);
|
||||
final map = Map<String, dynamic>.from(response);
|
||||
final set = Set<String>.from(map["tags"] as List);
|
||||
return await compute(_ffiHashTagsComputeWrapper, set);
|
||||
final tags = List<String>.from(map["tags"] as List);
|
||||
|
||||
Logging.instance.log(
|
||||
"Finished ElectrumXClient.getSparkUnhashedUsedCoinsTags(startNumber"
|
||||
"=$startNumber). "
|
||||
"Duration=${DateTime.now().difference(start)}",
|
||||
level: LogLevel.Info,
|
||||
);
|
||||
|
||||
return tags;
|
||||
} catch (e) {
|
||||
Logging.instance.log(e, level: LogLevel.Error);
|
||||
rethrow;
|
||||
|
@ -1023,6 +1025,64 @@ class ElectrumXClient {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the txids of the current transactions found in the mempool
|
||||
Future<Set<String>> getMempoolTxids({
|
||||
String? requestID,
|
||||
}) async {
|
||||
try {
|
||||
final start = DateTime.now();
|
||||
final response = await request(
|
||||
requestID: requestID,
|
||||
command: "spark.getmempooltxids",
|
||||
);
|
||||
|
||||
// TODO verify once server is live
|
||||
final txids = List<String>.from(response as List).toSet();
|
||||
// final map = Map<String, dynamic>.from(response as Map);
|
||||
// final txids = List<String>.from(map["tags"] as List).toSet();
|
||||
|
||||
Logging.instance.log(
|
||||
"Finished ElectrumXClient.getMempoolTxids(). "
|
||||
"Duration=${DateTime.now().difference(start)}",
|
||||
level: LogLevel.Info,
|
||||
);
|
||||
|
||||
return txids;
|
||||
} catch (e) {
|
||||
Logging.instance.log(e, level: LogLevel.Error);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the txids of the current transactions found in the mempool
|
||||
Future<Map<String, dynamic>> getMempoolSparkData({
|
||||
String? requestID,
|
||||
required List<String> txids,
|
||||
}) async {
|
||||
try {
|
||||
final start = DateTime.now();
|
||||
final response = await request(
|
||||
requestID: requestID,
|
||||
command: "spark.getmempooltxs",
|
||||
args: txids,
|
||||
);
|
||||
|
||||
// TODO verify once server is live
|
||||
final map = Map<String, dynamic>.from(response as Map);
|
||||
|
||||
Logging.instance.log(
|
||||
"Finished ElectrumXClient.getMempoolSparkData(txids: $txids). "
|
||||
"Duration=${DateTime.now().difference(start)}",
|
||||
level: LogLevel.Info,
|
||||
);
|
||||
|
||||
return map;
|
||||
} catch (e) {
|
||||
Logging.instance.log(e, level: LogLevel.Error);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
|
||||
/// Get the current fee rate.
|
||||
|
@ -1094,7 +1154,3 @@ class ElectrumXClient {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
Set<String> _ffiHashTagsComputeWrapper(Set<String> base64Tags) {
|
||||
return LibSpark.hashTags(base64Tags: base64Tags);
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ import 'app_config.dart';
|
|||
import 'db/db_version_migration.dart';
|
||||
import 'db/hive/db.dart';
|
||||
import 'db/isar/main_db.dart';
|
||||
import 'db/sqlite/firo_cache.dart';
|
||||
import 'models/exchange/change_now/exchange_transaction.dart';
|
||||
import 'models/exchange/change_now/exchange_transaction_status.dart';
|
||||
import 'models/exchange/response_objects/trade.dart';
|
||||
|
@ -180,7 +181,8 @@ void main(List<String> args) async {
|
|||
|
||||
Hive.registerAdapter(UnspentCoinsInfoAdapter());
|
||||
await Hive.initFlutter(
|
||||
(await StackFileSystem.applicationHiveDirectory()).path);
|
||||
(await StackFileSystem.applicationHiveDirectory()).path,
|
||||
);
|
||||
|
||||
await Hive.openBox<dynamic>(DB.boxNameDBInfo);
|
||||
await Hive.openBox<dynamic>(DB.boxNamePrefs);
|
||||
|
@ -199,11 +201,14 @@ void main(List<String> args) async {
|
|||
}
|
||||
|
||||
await StackFileSystem.initThemesDir();
|
||||
await FiroCacheCoordinator.init();
|
||||
|
||||
// Desktop migrate handled elsewhere (currently desktop_login_view.dart)
|
||||
if (!Util.isDesktop) {
|
||||
int dbVersion = DB.instance.get<dynamic>(
|
||||
boxName: DB.boxNameDBInfo, key: "hive_data_version") as int? ??
|
||||
final int dbVersion = DB.instance.get<dynamic>(
|
||||
boxName: DB.boxNameDBInfo,
|
||||
key: "hive_data_version",
|
||||
) as int? ??
|
||||
0;
|
||||
if (dbVersion < Constants.currentDataVersion) {
|
||||
try {
|
||||
|
@ -215,18 +220,17 @@ void main(List<String> args) async {
|
|||
),
|
||||
);
|
||||
} catch (e, s) {
|
||||
Logging.instance.log("Cannot migrate mobile database\n$e $s",
|
||||
level: LogLevel.Error, printFullLength: true);
|
||||
Logging.instance.log(
|
||||
"Cannot migrate mobile database\n$e $s",
|
||||
level: LogLevel.Error,
|
||||
printFullLength: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!Platform.isWindows) {
|
||||
monero.onStartup();
|
||||
}
|
||||
if (!Platform.isLinux && !Platform.isWindows) {
|
||||
wownero.onStartup();
|
||||
}
|
||||
monero.onStartup();
|
||||
wownero.onStartup();
|
||||
|
||||
// SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
|
||||
// overlays: [SystemUiOverlay.bottom]);
|
||||
|
@ -266,7 +270,7 @@ void main(List<String> args) async {
|
|||
|
||||
/// MyApp initialises relevant services with a MultiProvider
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({Key? key}) : super(key: key);
|
||||
const MyApp({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -283,8 +287,8 @@ class MyApp extends StatelessWidget {
|
|||
|
||||
class MaterialAppWithTheme extends ConsumerStatefulWidget {
|
||||
const MaterialAppWithTheme({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
ConsumerState<MaterialAppWithTheme> createState() =>
|
||||
|
@ -386,7 +390,8 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
|||
switch (ref.read(prefsChangeNotifierProvider).backupFrequencyType) {
|
||||
case BackupFrequencyType.everyTenMinutes:
|
||||
ref.read(autoSWBServiceProvider).startPeriodicBackupTimer(
|
||||
duration: const Duration(minutes: 10));
|
||||
duration: const Duration(minutes: 10),
|
||||
);
|
||||
break;
|
||||
case BackupFrequencyType.everyAppStart:
|
||||
unawaited(ref.read(autoSWBServiceProvider).doBackup());
|
||||
|
@ -452,7 +457,8 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
|||
await loadingCompleter.future;
|
||||
|
||||
await goToRestoreSWB(
|
||||
ref.read(openedFromSWBFileStringStateProvider.state).state!);
|
||||
ref.read(openedFromSWBFileStringStateProvider.state).state!,
|
||||
);
|
||||
ref.read(openedFromSWBFileStringStateProvider.state).state = null;
|
||||
}
|
||||
// ref.read(shouldShowLockscreenOnResumeStateProvider.state).state = false;
|
||||
|
@ -515,7 +521,8 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
|||
if (ref.read(openedFromSWBFileStringStateProvider.state).state !=
|
||||
null) {
|
||||
await goToRestoreSWB(
|
||||
ref.read(openedFromSWBFileStringStateProvider.state).state!);
|
||||
ref.read(openedFromSWBFileStringStateProvider.state).state!,
|
||||
);
|
||||
ref.read(openedFromSWBFileStringStateProvider.state).state = null;
|
||||
}
|
||||
}
|
||||
|
@ -563,8 +570,9 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
|||
await resetOpenPath();
|
||||
|
||||
Logging.instance.log(
|
||||
"This is the .swb content from intent: ${ref.read(openedFromSWBFileStringStateProvider.state).state}",
|
||||
level: LogLevel.Info);
|
||||
"This is the .swb content from intent: ${ref.read(openedFromSWBFileStringStateProvider.state).state}",
|
||||
level: LogLevel.Info,
|
||||
);
|
||||
}
|
||||
|
||||
/// should only be called on android currently
|
||||
|
@ -579,27 +587,31 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
|||
.then((value) {
|
||||
if (value is! bool || value == false) {
|
||||
Navigator.of(navigatorKey.currentContext!).pushNamed(
|
||||
RestoreFromEncryptedStringView.routeName,
|
||||
arguments: encrypted);
|
||||
RestoreFromEncryptedStringView.routeName,
|
||||
arguments: encrypted,
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
unawaited(Navigator.push(
|
||||
navigatorKey.currentContext!,
|
||||
RouteGenerator.getRoute(
|
||||
shouldUseMaterialRoute: RouteGenerator.useMaterialPageRoute,
|
||||
builder: (_) => LockscreenView(
|
||||
showBackButton: true,
|
||||
routeOnSuccess: RestoreFromEncryptedStringView.routeName,
|
||||
routeOnSuccessArguments: encrypted,
|
||||
biometricsCancelButtonString: "CANCEL",
|
||||
biometricsLocalizedReason:
|
||||
"Authenticate to restore ${AppConfig.appName} backup",
|
||||
biometricsAuthenticationTitle: "Restore ${AppConfig.prefix} backup",
|
||||
unawaited(
|
||||
Navigator.push(
|
||||
navigatorKey.currentContext!,
|
||||
RouteGenerator.getRoute(
|
||||
shouldUseMaterialRoute: RouteGenerator.useMaterialPageRoute,
|
||||
builder: (_) => LockscreenView(
|
||||
showBackButton: true,
|
||||
routeOnSuccess: RestoreFromEncryptedStringView.routeName,
|
||||
routeOnSuccessArguments: encrypted,
|
||||
biometricsCancelButtonString: "CANCEL",
|
||||
biometricsLocalizedReason:
|
||||
"Authenticate to restore ${AppConfig.appName} backup",
|
||||
biometricsAuthenticationTitle:
|
||||
"Restore ${AppConfig.prefix} backup",
|
||||
),
|
||||
settings: const RouteSettings(name: "/swbrestorelockscreen"),
|
||||
),
|
||||
settings: const RouteSettings(name: "/swbrestorelockscreen"),
|
||||
),
|
||||
));
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -659,7 +671,8 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
|||
foregroundColor:
|
||||
MaterialStateProperty.all(colorScheme.buttonTextSecondary),
|
||||
backgroundColor: MaterialStateProperty.all<Color>(
|
||||
colorScheme.buttonBackSecondary),
|
||||
colorScheme.buttonBackSecondary,
|
||||
),
|
||||
shape: MaterialStateProperty.all<OutlinedBorder>(
|
||||
RoundedRectangleBorder(
|
||||
// 1000 to be relatively sure it keeps its pill shape
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
|
||||
import '../add_wallet_list_entity.dart';
|
||||
import '../../isar/models/ethereum/eth_contract.dart';
|
||||
import '../../../wallets/crypto_currency/coins/ethereum.dart';
|
||||
import '../../../wallets/crypto_currency/crypto_currency.dart';
|
||||
|
||||
class EthTokenEntity extends AddWalletListEntity {
|
||||
|
|
|
@ -72,7 +72,8 @@ class Balance {
|
|||
),
|
||||
pendingSpendable: decoded["pendingSpendable"] is String
|
||||
? Amount.fromSerializedJsonString(
|
||||
decoded["pendingSpendable"] as String)
|
||||
decoded["pendingSpendable"] as String,
|
||||
)
|
||||
: Amount(
|
||||
rawValue: BigInt.from(decoded["pendingSpendable"] as int),
|
||||
fractionDigits: deprecatedValue,
|
||||
|
|
|
@ -23,11 +23,12 @@ class Fiat {
|
|||
/// Fiat name
|
||||
final Decimal maxAmount;
|
||||
|
||||
Fiat(
|
||||
{required this.ticker,
|
||||
required this.name,
|
||||
required this.minAmount,
|
||||
required this.maxAmount});
|
||||
Fiat({
|
||||
required this.ticker,
|
||||
required this.name,
|
||||
required this.minAmount,
|
||||
required this.maxAmount,
|
||||
});
|
||||
|
||||
factory Fiat.fromJson(Map<String, dynamic> json) {
|
||||
try {
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
*/
|
||||
|
||||
import 'package:decimal/decimal.dart';
|
||||
|
||||
import '../response_objects/crypto.dart';
|
||||
import '../response_objects/fiat.dart';
|
||||
import '../response_objects/order.dart';
|
||||
|
@ -20,7 +21,8 @@ class Simplex {
|
|||
SimplexQuote quote = SimplexQuote(
|
||||
crypto: Crypto.fromJson({'ticker': 'BTC', 'name': 'Bitcoin', 'image': ''}),
|
||||
fiat: Fiat.fromJson(
|
||||
{'ticker': 'USD', 'name': 'United States Dollar', 'image': ''}),
|
||||
{'ticker': 'USD', 'name': 'United States Dollar', 'image': ''},
|
||||
),
|
||||
youPayFiatPrice: Decimal.parse("100"),
|
||||
youReceiveCryptoAmount: Decimal.parse("1.0238917"),
|
||||
id: "someID",
|
||||
|
@ -28,20 +30,22 @@ class Simplex {
|
|||
buyWithFiat: true,
|
||||
);
|
||||
SimplexOrder order = SimplexOrder(
|
||||
quote: SimplexQuote(
|
||||
crypto:
|
||||
Crypto.fromJson({'ticker': 'BTC', 'name': 'Bitcoin', 'image': ''}),
|
||||
fiat: Fiat.fromJson(
|
||||
{'ticker': 'USD', 'name': 'United States Dollar', 'image': ''}),
|
||||
youPayFiatPrice: Decimal.parse("100"),
|
||||
youReceiveCryptoAmount: Decimal.parse("1.0238917"),
|
||||
id: "someID",
|
||||
receivingAddress: '',
|
||||
buyWithFiat: true,
|
||||
quote: SimplexQuote(
|
||||
crypto:
|
||||
Crypto.fromJson({'ticker': 'BTC', 'name': 'Bitcoin', 'image': ''}),
|
||||
fiat: Fiat.fromJson(
|
||||
{'ticker': 'USD', 'name': 'United States Dollar', 'image': ''},
|
||||
),
|
||||
orderId: 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee',
|
||||
paymentId: 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee',
|
||||
userId: 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee');
|
||||
youPayFiatPrice: Decimal.parse("100"),
|
||||
youReceiveCryptoAmount: Decimal.parse("1.0238917"),
|
||||
id: "someID",
|
||||
receivingAddress: '',
|
||||
buyWithFiat: true,
|
||||
),
|
||||
orderId: 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee',
|
||||
paymentId: 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee',
|
||||
userId: 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee',
|
||||
);
|
||||
|
||||
void updateSupportedCryptos(List<Crypto> newCryptos) {
|
||||
supportedCryptos = newCryptos;
|
||||
|
|
|
@ -44,13 +44,13 @@ class Contact {
|
|||
List<ContactAddressEntry>? addresses,
|
||||
bool? isFavorite,
|
||||
}) {
|
||||
List<ContactAddressEntry> _addresses = [];
|
||||
final List<ContactAddressEntry> _addresses = [];
|
||||
if (addresses == null) {
|
||||
for (var e in this.addresses) {
|
||||
for (final e in this.addresses) {
|
||||
_addresses.add(e.copyWith());
|
||||
}
|
||||
} else {
|
||||
for (var e in addresses) {
|
||||
for (final e in addresses) {
|
||||
_addresses.add(e.copyWith());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
import 'epicbox_server_model.dart';
|
||||
|
||||
part 'type_adaptors/epicbox_config_model.g.dart';
|
||||
|
@ -57,7 +58,7 @@ class EpicBoxConfigModel {
|
|||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
Map<String, dynamic> map = {};
|
||||
final Map<String, dynamic> map = {};
|
||||
map['epicbox_domain'] = host;
|
||||
map['epicbox_port'] = port;
|
||||
map['epicbox_protocol_insecure'] = protocolInsecure;
|
||||
|
@ -84,7 +85,7 @@ class EpicBoxConfigModel {
|
|||
}
|
||||
|
||||
static EpicBoxConfigModel fromString(String epicBoxConfigString) {
|
||||
dynamic _epicBox = json.decode(epicBoxConfigString);
|
||||
final dynamic _epicBox = json.decode(epicBoxConfigString);
|
||||
|
||||
// handle old epicbox config formats
|
||||
final oldDomain = _epicBox["domain"] ?? "empty";
|
||||
|
@ -117,8 +118,11 @@ class EpicBoxConfigModel {
|
|||
);
|
||||
}
|
||||
|
||||
static EpicBoxConfigModel fromServer(EpicBoxServerModel server,
|
||||
{bool? protocolInsecure, int? addressIndex}) {
|
||||
static EpicBoxConfigModel fromServer(
|
||||
EpicBoxServerModel server, {
|
||||
bool? protocolInsecure,
|
||||
int? addressIndex,
|
||||
}) {
|
||||
return EpicBoxConfigModel(
|
||||
host: server.host,
|
||||
port: server.port ?? 443,
|
||||
|
|
|
@ -64,7 +64,7 @@ class EpicBoxServerModel {
|
|||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
Map<String, dynamic> map = {};
|
||||
final Map<String, dynamic> map = {};
|
||||
map['id'] = id;
|
||||
map['host'] = host;
|
||||
map['port'] = port;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
*/
|
||||
|
||||
import 'package:decimal/decimal.dart';
|
||||
|
||||
import '../../../utilities/logger.dart';
|
||||
|
||||
class EstimatedExchangeAmount {
|
||||
|
@ -45,8 +46,10 @@ class EstimatedExchangeAmount {
|
|||
factory EstimatedExchangeAmount.fromJson(Map<String, dynamic> json) {
|
||||
try {
|
||||
return EstimatedExchangeAmount(
|
||||
estimatedAmount: Decimal.parse(json["estimatedAmount"]?.toString() ??
|
||||
json["estimatedDeposit"].toString()),
|
||||
estimatedAmount: Decimal.parse(
|
||||
json["estimatedAmount"]?.toString() ??
|
||||
json["estimatedDeposit"].toString(),
|
||||
),
|
||||
transactionSpeedForecast:
|
||||
json["transactionSpeedForecast"] as String? ?? "",
|
||||
warningMessage: json["warningMessage"] as String?,
|
||||
|
|
|
@ -10,13 +10,15 @@
|
|||
|
||||
import 'package:decimal/decimal.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'exchange_transaction_status.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
import 'exchange_transaction_status.dart';
|
||||
|
||||
part '../../type_adaptors/exchange_transaction.g.dart';
|
||||
|
||||
@Deprecated(
|
||||
"Do not use. Migrated to Trade in db_version_migration to hive_data_version 2")
|
||||
"Do not use. Migrated to Trade in db_version_migration to hive_data_version 2",
|
||||
)
|
||||
// @HiveType(typeId: 13)
|
||||
class ExchangeTransaction {
|
||||
/// You can use it to get transaction status at the Transaction status API endpoint
|
||||
|
@ -114,7 +116,8 @@ class ExchangeTransaction {
|
|||
statusString: json["statusString"] as String? ?? "",
|
||||
statusObject: json["statusObject"] is Map<String, dynamic>
|
||||
? ExchangeTransactionStatus.fromJson(
|
||||
json["statusObject"] as Map<String, dynamic>)
|
||||
json["statusObject"] as Map<String, dynamic>,
|
||||
)
|
||||
: null,
|
||||
);
|
||||
} catch (e) {
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
*/
|
||||
|
||||
import 'package:hive/hive.dart';
|
||||
|
||||
import '../../../utilities/logger.dart';
|
||||
|
||||
part '../../type_adaptors/exchange_transaction_status.g.dart';
|
||||
|
@ -38,7 +39,8 @@ ChangeNowTransactionStatus changeNowTransactionStatusFromStringIgnoreCase(
|
|||
}
|
||||
}
|
||||
throw ArgumentError(
|
||||
"String value does not match any known ChangeNowTransactionStatus");
|
||||
"String value does not match any known ChangeNowTransactionStatus",
|
||||
);
|
||||
}
|
||||
|
||||
@HiveType(typeId: 16)
|
||||
|
@ -189,7 +191,8 @@ class ExchangeTransactionStatus {
|
|||
try {
|
||||
return ExchangeTransactionStatus(
|
||||
status: changeNowTransactionStatusFromStringIgnoreCase(
|
||||
json["status"] as String),
|
||||
json["status"] as String,
|
||||
),
|
||||
payinAddress: json["payinAddress"] as String? ?? "",
|
||||
payoutAddress: json["payoutAddress"] as String? ?? "",
|
||||
fromCurrency: json["fromCurrency"] as String? ?? "",
|
||||
|
|
|
@ -9,10 +9,15 @@
|
|||
*/
|
||||
|
||||
import 'package:decimal/decimal.dart';
|
||||
|
||||
import 'mb_object.dart';
|
||||
|
||||
class MBRate extends MBObject {
|
||||
MBRate({required this.fromCurrency, required this.toCurrency, required this.rate,});
|
||||
MBRate({
|
||||
required this.fromCurrency,
|
||||
required this.toCurrency,
|
||||
required this.rate,
|
||||
});
|
||||
|
||||
final String fromCurrency;
|
||||
final String toCurrency;
|
||||
|
|
|
@ -9,8 +9,9 @@
|
|||
*/
|
||||
|
||||
import 'package:hive/hive.dart';
|
||||
import '../change_now/exchange_transaction.dart';
|
||||
|
||||
import '../../../services/exchange/change_now/change_now_exchange.dart';
|
||||
import '../change_now/exchange_transaction.dart';
|
||||
|
||||
part 'trade.g.dart';
|
||||
|
||||
|
@ -213,7 +214,9 @@ class Trade {
|
|||
}
|
||||
|
||||
factory Trade.fromExchangeTransaction(
|
||||
ExchangeTransaction exTx, bool reversed) {
|
||||
ExchangeTransaction exTx,
|
||||
bool reversed,
|
||||
) {
|
||||
return Trade(
|
||||
uuid: exTx.uuid,
|
||||
tradeId: exTx.id,
|
||||
|
|
|
@ -59,8 +59,10 @@ class SPCurrency {
|
|||
warningsTo: json["warnings_to"] as List<dynamic>,
|
||||
);
|
||||
} catch (e, s) {
|
||||
Logging.instance.log("SPCurrency.fromJson failed to parse: $e\n$s",
|
||||
level: LogLevel.Error);
|
||||
Logging.instance.log(
|
||||
"SPCurrency.fromJson failed to parse: $e\n$s",
|
||||
level: LogLevel.Error,
|
||||
);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,10 +23,12 @@ class Currency {
|
|||
final String exchangeName;
|
||||
|
||||
/// Currency ticker
|
||||
@Index(composite: [
|
||||
CompositeIndex("exchangeName"),
|
||||
CompositeIndex("name"),
|
||||
])
|
||||
@Index(
|
||||
composite: [
|
||||
CompositeIndex("exchangeName"),
|
||||
CompositeIndex("name"),
|
||||
],
|
||||
)
|
||||
final String ticker;
|
||||
|
||||
/// Currency name
|
||||
|
|
|
@ -29,10 +29,12 @@ class Pair {
|
|||
@Index()
|
||||
final String exchangeName;
|
||||
|
||||
@Index(composite: [
|
||||
CompositeIndex("exchangeName"),
|
||||
CompositeIndex("to"),
|
||||
])
|
||||
@Index(
|
||||
composite: [
|
||||
CompositeIndex("exchangeName"),
|
||||
CompositeIndex("to"),
|
||||
],
|
||||
)
|
||||
final String from;
|
||||
|
||||
final String to;
|
||||
|
|
|
@ -12,11 +12,12 @@ import 'dart:convert';
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
import '../../../../utilities/amount/amount.dart';
|
||||
import 'address.dart';
|
||||
import 'input.dart';
|
||||
import 'output.dart';
|
||||
import '../../../../utilities/amount/amount.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
part 'transaction.g.dart';
|
||||
|
||||
|
@ -65,24 +66,24 @@ class Transaction {
|
|||
}) {
|
||||
return Tuple2(
|
||||
Transaction(
|
||||
walletId: walletId ?? this.walletId,
|
||||
txid: txid ?? this.txid,
|
||||
timestamp: timestamp ?? this.timestamp,
|
||||
type: type ?? this.type,
|
||||
subType: subType ?? this.subType,
|
||||
amount: amount ?? this.amount,
|
||||
amountString: amountString ?? this.amountString,
|
||||
fee: fee ?? this.fee,
|
||||
height: height ?? this.height,
|
||||
isCancelled: isCancelled ?? this.isCancelled,
|
||||
isLelantus: isLelantus ?? this.isLelantus,
|
||||
slateId: slateId ?? this.slateId,
|
||||
otherData: otherData ?? this.otherData,
|
||||
nonce: nonce ?? this.nonce,
|
||||
inputs: inputs ?? this.inputs,
|
||||
outputs: outputs ?? this.outputs,
|
||||
numberOfMessages: numberOfMessages ?? this.numberOfMessages)
|
||||
..id = id ?? this.id,
|
||||
walletId: walletId ?? this.walletId,
|
||||
txid: txid ?? this.txid,
|
||||
timestamp: timestamp ?? this.timestamp,
|
||||
type: type ?? this.type,
|
||||
subType: subType ?? this.subType,
|
||||
amount: amount ?? this.amount,
|
||||
amountString: amountString ?? this.amountString,
|
||||
fee: fee ?? this.fee,
|
||||
height: height ?? this.height,
|
||||
isCancelled: isCancelled ?? this.isCancelled,
|
||||
isLelantus: isLelantus ?? this.isLelantus,
|
||||
slateId: slateId ?? this.slateId,
|
||||
otherData: otherData ?? this.otherData,
|
||||
nonce: nonce ?? this.nonce,
|
||||
inputs: inputs ?? this.inputs,
|
||||
outputs: outputs ?? this.outputs,
|
||||
numberOfMessages: numberOfMessages ?? this.numberOfMessages,
|
||||
)..id = id ?? this.id,
|
||||
address ?? this.address.value,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -38,10 +38,14 @@ class UTXO {
|
|||
@Index()
|
||||
late final String walletId;
|
||||
|
||||
@Index(unique: true, replace: true, composite: [
|
||||
CompositeIndex("walletId"),
|
||||
CompositeIndex("vout"),
|
||||
])
|
||||
@Index(
|
||||
unique: true,
|
||||
replace: true,
|
||||
composite: [
|
||||
CompositeIndex("walletId"),
|
||||
CompositeIndex("vout"),
|
||||
],
|
||||
)
|
||||
late final String txid;
|
||||
|
||||
late final int vout;
|
||||
|
|
|
@ -54,7 +54,7 @@ class OutputV2 {
|
|||
bool isFullAmountNotSats = false,
|
||||
}) {
|
||||
try {
|
||||
List<String> addresses = [];
|
||||
final List<String> addresses = [];
|
||||
|
||||
if (json["scriptPubKey"]?["addresses"] is List) {
|
||||
for (final e in json["scriptPubKey"]["addresses"] as List) {
|
||||
|
@ -68,7 +68,7 @@ class OutputV2 {
|
|||
scriptPubKeyHex: json["scriptPubKey"]["hex"] as String,
|
||||
scriptPubKeyAsm: json["scriptPubKey"]["asm"] as String?,
|
||||
valueStringSats: parseOutputAmountString(
|
||||
json["value"] != null ? json["value"].toString(): "0",
|
||||
json["value"] != null ? json["value"].toString() : "0",
|
||||
decimalPlaces: decimalPlaces,
|
||||
isFullAmountNotSats: isFullAmountNotSats,
|
||||
),
|
||||
|
|
|
@ -124,12 +124,14 @@ const _LoglogLevelEnumValueMap = {
|
|||
r'Warning': r'Warning',
|
||||
r'Error': r'Error',
|
||||
r'Fatal': r'Fatal',
|
||||
r'Debug': r'Debug',
|
||||
};
|
||||
const _LoglogLevelValueEnumMap = {
|
||||
r'Info': LogLevel.Info,
|
||||
r'Warning': LogLevel.Warning,
|
||||
r'Error': LogLevel.Error,
|
||||
r'Fatal': LogLevel.Fatal,
|
||||
r'Debug': LogLevel.Debug,
|
||||
};
|
||||
|
||||
Id _logGetId(Log object) {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:isar/isar.dart';
|
||||
|
||||
import '../../db/isar/main_db.dart';
|
||||
import '../../dto/ordinals/inscription_data.dart';
|
||||
import 'models/isar_models.dart';
|
||||
|
@ -11,10 +12,14 @@ class Ordinal {
|
|||
|
||||
final String walletId;
|
||||
|
||||
@Index(unique: true, replace: true, composite: [
|
||||
CompositeIndex("utxoTXID"),
|
||||
CompositeIndex("utxoVOUT"),
|
||||
])
|
||||
@Index(
|
||||
unique: true,
|
||||
replace: true,
|
||||
composite: [
|
||||
CompositeIndex("utxoTXID"),
|
||||
CompositeIndex("utxoVOUT"),
|
||||
],
|
||||
)
|
||||
final String inscriptionId;
|
||||
|
||||
final int inscriptionNumber;
|
||||
|
|
|
@ -40,7 +40,7 @@ class LelantusCoin {
|
|||
|
||||
@override
|
||||
String toString() {
|
||||
String coin =
|
||||
final String coin =
|
||||
"{index: $index, value: $value, publicCoin: $publicCoin, txId: $txId, anonymitySetId: $anonymitySetId, isUsed: $isUsed}";
|
||||
return coin;
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ class NodeModel {
|
|||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
Map<String, dynamic> map = {};
|
||||
final Map<String, dynamic> map = {};
|
||||
map['host'] = host;
|
||||
map['port'] = port;
|
||||
map['name'] = name;
|
||||
|
|
|
@ -34,29 +34,32 @@ class TransactionData {
|
|||
TransactionData({this.txChunks = const []});
|
||||
|
||||
factory TransactionData.fromJson(Map<String, dynamic> json) {
|
||||
var dateTimeChunks = json['dateTimeChunks'] as List;
|
||||
List<TransactionChunk> chunksList = dateTimeChunks
|
||||
.map((txChunk) =>
|
||||
TransactionChunk.fromJson(txChunk as Map<String, dynamic>))
|
||||
final dateTimeChunks = json['dateTimeChunks'] as List;
|
||||
final List<TransactionChunk> chunksList = dateTimeChunks
|
||||
.map(
|
||||
(txChunk) =>
|
||||
TransactionChunk.fromJson(txChunk as Map<String, dynamic>),
|
||||
)
|
||||
.toList();
|
||||
|
||||
return TransactionData(txChunks: chunksList);
|
||||
}
|
||||
|
||||
factory TransactionData.fromMap(Map<String, Transaction> transactions) {
|
||||
Map<String, List<Transaction>> chunks = {};
|
||||
final Map<String, List<Transaction>> chunks = {};
|
||||
transactions.forEach((key, value) {
|
||||
String date = extractDateFromTimestamp(value.timestamp);
|
||||
final String date = extractDateFromTimestamp(value.timestamp);
|
||||
if (!chunks.containsKey(date)) {
|
||||
chunks[date] = [];
|
||||
}
|
||||
chunks[date]!.add(value);
|
||||
});
|
||||
List<TransactionChunk> chunksList = [];
|
||||
final List<TransactionChunk> chunksList = [];
|
||||
chunks.forEach((key, value) {
|
||||
value.sort((a, b) => b.timestamp.compareTo(a.timestamp));
|
||||
chunksList.add(
|
||||
TransactionChunk(timestamp: value[0].timestamp, transactions: value));
|
||||
TransactionChunk(timestamp: value[0].timestamp, transactions: value),
|
||||
);
|
||||
});
|
||||
chunksList.sort((a, b) => b.timestamp.compareTo(a.timestamp));
|
||||
return TransactionData(txChunks: chunksList);
|
||||
|
@ -64,9 +67,9 @@ class TransactionData {
|
|||
|
||||
Transaction? findTransaction(String txid) {
|
||||
for (var i = 0; i < txChunks.length; i++) {
|
||||
var txChunk = txChunks[i].transactions;
|
||||
final txChunk = txChunks[i].transactions;
|
||||
for (var j = 0; j < txChunk.length; j++) {
|
||||
var tx = txChunk[j];
|
||||
final tx = txChunk[j];
|
||||
if (tx.txid == txid) {
|
||||
return tx;
|
||||
}
|
||||
|
@ -76,11 +79,11 @@ class TransactionData {
|
|||
}
|
||||
|
||||
Map<String, Transaction> getAllTransactions() {
|
||||
Map<String, Transaction> transactions = {};
|
||||
final Map<String, Transaction> transactions = {};
|
||||
for (var i = 0; i < txChunks.length; i++) {
|
||||
var txChunk = txChunks[i].transactions;
|
||||
final txChunk = txChunks[i].transactions;
|
||||
for (var j = 0; j < txChunk.length; j++) {
|
||||
var tx = txChunk[j];
|
||||
final tx = txChunk[j];
|
||||
transactions[tx.txid] = tx;
|
||||
}
|
||||
}
|
||||
|
@ -98,14 +101,15 @@ class TransactionChunk {
|
|||
TransactionChunk({required this.timestamp, required this.transactions});
|
||||
|
||||
factory TransactionChunk.fromJson(Map<String, dynamic> json) {
|
||||
var txArray = json['transactions'] as List;
|
||||
List<Transaction> txList = txArray
|
||||
final txArray = json['transactions'] as List;
|
||||
final List<Transaction> txList = txArray
|
||||
.map((tx) => Transaction.fromJson(tx as Map<String, dynamic>))
|
||||
.toList();
|
||||
|
||||
return TransactionChunk(
|
||||
timestamp: int.parse(json['timestamp'].toString()),
|
||||
transactions: txList);
|
||||
timestamp: int.parse(json['timestamp'].toString()),
|
||||
transactions: txList,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -191,15 +195,16 @@ class Transaction {
|
|||
});
|
||||
|
||||
factory Transaction.fromJson(Map<String, dynamic> json) {
|
||||
var inputArray = json['inputs'] as List;
|
||||
var outputArray = json['outputs'] as List;
|
||||
final inputArray = json['inputs'] as List;
|
||||
final outputArray = json['outputs'] as List;
|
||||
|
||||
List<Input> inputList = inputArray
|
||||
final List<Input> inputList = inputArray
|
||||
.map((input) => Input.fromJson(Map<String, dynamic>.from(input as Map)))
|
||||
.toList();
|
||||
List<Output> outputList = outputArray
|
||||
.map((output) =>
|
||||
Output.fromJson(Map<String, dynamic>.from(output as Map)))
|
||||
final List<Output> outputList = outputArray
|
||||
.map(
|
||||
(output) => Output.fromJson(Map<String, dynamic>.from(output as Map)),
|
||||
)
|
||||
.toList();
|
||||
|
||||
return Transaction(
|
||||
|
@ -304,7 +309,7 @@ class Transaction {
|
|||
|
||||
@override
|
||||
String toString() {
|
||||
String transaction =
|
||||
final String transaction =
|
||||
"{txid: $txid, type: $txType, subType: $subType, value: $amount, fee: $fees, height: $height, confirm: $confirmedStatus, confirmations: $confirmations, address: $address, timestamp: $timestamp, worthNow: $worthNow, inputs: $inputs, slateid: $slateId, numberOfMessages: $numberOfMessages }";
|
||||
return transaction;
|
||||
}
|
||||
|
@ -344,7 +349,7 @@ class Input {
|
|||
});
|
||||
|
||||
factory Input.fromJson(Map<String, dynamic> json) {
|
||||
bool iscoinBase = json['coinbase'] != null;
|
||||
final bool iscoinBase = json['coinbase'] != null;
|
||||
return Input(
|
||||
txid: json['txid'] as String? ?? "",
|
||||
vout: json['vout'] as int? ?? -1,
|
||||
|
@ -361,7 +366,7 @@ class Input {
|
|||
|
||||
@override
|
||||
String toString() {
|
||||
String transaction = "{txid: $txid}";
|
||||
final String transaction = "{txid: $txid}";
|
||||
return transaction;
|
||||
}
|
||||
}
|
||||
|
@ -383,12 +388,13 @@ class Output {
|
|||
// @HiveField(4)
|
||||
final int value;
|
||||
|
||||
Output(
|
||||
{this.scriptpubkey,
|
||||
this.scriptpubkeyAsm,
|
||||
this.scriptpubkeyType,
|
||||
required this.scriptpubkeyAddress,
|
||||
required this.value});
|
||||
Output({
|
||||
this.scriptpubkey,
|
||||
this.scriptpubkeyAsm,
|
||||
this.scriptpubkeyType,
|
||||
required this.scriptpubkeyAddress,
|
||||
required this.value,
|
||||
});
|
||||
|
||||
factory Output.fromJson(Map<String, dynamic> json) {
|
||||
// TODO determine if any of this code is needed.
|
||||
|
@ -405,12 +411,13 @@ class Output {
|
|||
);
|
||||
} catch (s, e) {
|
||||
return Output(
|
||||
// Return output object with null values; allows wallet history to be built
|
||||
scriptpubkey: "",
|
||||
scriptpubkeyAsm: "",
|
||||
scriptpubkeyType: "",
|
||||
scriptpubkeyAddress: "",
|
||||
value: _parse(0.toString()));
|
||||
// Return output object with null values; allows wallet history to be built
|
||||
scriptpubkey: "",
|
||||
scriptpubkeyAsm: "",
|
||||
scriptpubkeyType: "",
|
||||
scriptpubkeyAddress: "",
|
||||
value: _parse(0.toString()),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,8 +35,8 @@ class UtxoData {
|
|||
});
|
||||
|
||||
factory UtxoData.fromJson(Map<String, dynamic> json) {
|
||||
var outputList = json['outputArray'] as List;
|
||||
List<UtxoObject> utxoList = outputList
|
||||
final outputList = json['outputArray'] as List;
|
||||
final List<UtxoObject> utxoList = outputList
|
||||
.map((output) => UtxoObject.fromJson(output as Map<String, dynamic>))
|
||||
.toList();
|
||||
final String totalUserCurr = json['total_user_currency'] as String? ?? "";
|
||||
|
@ -104,7 +104,7 @@ class UtxoObject {
|
|||
|
||||
@override
|
||||
String toString() {
|
||||
String utxo =
|
||||
final String utxo =
|
||||
"{txid: $txid, vout: $vout, value: $value, fiat: $fiatWorth, blocked: $blocked, status: $status, is_coinbase: $isCoinbase}";
|
||||
|
||||
return utxo;
|
||||
|
|
|
@ -44,12 +44,16 @@ class PaynymAccount {
|
|||
.map((e) => PaynymCode.fromMap(Map<String, dynamic>.from(e as Map)))
|
||||
.toList(),
|
||||
followers = (map["followers"] as List<dynamic>)
|
||||
.map((e) =>
|
||||
PaynymAccountLite.fromMap(Map<String, dynamic>.from(e as Map)))
|
||||
.map(
|
||||
(e) => PaynymAccountLite.fromMap(
|
||||
Map<String, dynamic>.from(e as Map)),
|
||||
)
|
||||
.toList(),
|
||||
following = (map["following"] as List<dynamic>)
|
||||
.map((e) =>
|
||||
PaynymAccountLite.fromMap(Map<String, dynamic>.from(e as Map)))
|
||||
.map(
|
||||
(e) => PaynymAccountLite.fromMap(
|
||||
Map<String, dynamic>.from(e as Map)),
|
||||
)
|
||||
.toList();
|
||||
|
||||
PaynymAccount copyWith({
|
||||
|
|
|
@ -21,13 +21,13 @@ class PaynymFollow {
|
|||
token = map["token"] as String;
|
||||
|
||||
Map<String, dynamic> toMap() => {
|
||||
"follower": follower,
|
||||
"following": following,
|
||||
"token": token,
|
||||
};
|
||||
"follower": follower,
|
||||
"following": following,
|
||||
"token": token,
|
||||
};
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return toMap().toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,9 +10,10 @@
|
|||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'wallet_restore_state.dart';
|
||||
|
||||
import '../utilities/enums/stack_restoring_status.dart';
|
||||
import '../wallets/wallet/wallet.dart';
|
||||
import 'wallet_restore_state.dart';
|
||||
|
||||
class StackRestoringUIState extends ChangeNotifier {
|
||||
bool _walletsWasSet = false;
|
||||
|
@ -94,7 +95,7 @@ class StackRestoringUIState extends ChangeNotifier {
|
|||
}
|
||||
|
||||
List<Wallet> get wallets {
|
||||
List<Wallet> _wallets = [];
|
||||
final List<Wallet> _wallets = [];
|
||||
for (final item in _walletStates.values) {
|
||||
if (item.wallet != null) {
|
||||
_wallets.add(item.wallet!);
|
||||
|
@ -125,7 +126,8 @@ class StackRestoringUIState extends ChangeNotifier {
|
|||
}
|
||||
|
||||
ChangeNotifierProvider<WalletRestoreState> getWalletRestoreStateProvider(
|
||||
String walletId) {
|
||||
String walletId,
|
||||
) {
|
||||
return _walletStateProviders[walletId]!;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import 'dart:io';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
|
||||
import '../app_config.dart';
|
||||
import '../models/isar/stack_theme.dart';
|
||||
import '../models/notification_model.dart';
|
||||
|
@ -28,9 +29,9 @@ import '../widgets/rounded_white_container.dart';
|
|||
|
||||
class NotificationCard extends ConsumerWidget {
|
||||
const NotificationCard({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.notification,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final NotificationModel notification;
|
||||
|
||||
|
@ -71,10 +72,11 @@ class NotificationCard extends ConsumerWidget {
|
|||
? SvgPicture.file(
|
||||
File(
|
||||
coinIconPath(
|
||||
ref.watch(
|
||||
themeAssetsProvider,
|
||||
),
|
||||
ref),
|
||||
ref.watch(
|
||||
themeAssetsProvider,
|
||||
),
|
||||
ref,
|
||||
),
|
||||
),
|
||||
width: isDesktop ? desktopIconSize : mobileIconSize,
|
||||
height: isDesktop ? desktopIconSize : mobileIconSize,
|
||||
|
@ -89,10 +91,11 @@ class NotificationCard extends ConsumerWidget {
|
|||
child: SvgPicture.file(
|
||||
File(
|
||||
coinIconPath(
|
||||
ref.watch(
|
||||
themeAssetsProvider,
|
||||
),
|
||||
ref),
|
||||
ref.watch(
|
||||
themeAssetsProvider,
|
||||
),
|
||||
ref,
|
||||
),
|
||||
),
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
|
@ -123,7 +126,7 @@ class NotificationCard extends ConsumerWidget {
|
|||
.extension<StackColors>()!
|
||||
.accentColorGreen,
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Text(
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class TermsOfServiceView extends StatelessWidget {
|
||||
const TermsOfServiceView({Key? key}) : super(key: key);
|
||||
const TermsOfServiceView({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
|
@ -13,6 +13,7 @@ import 'dart:async';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../../models/isar/models/ethereum/eth_contract.dart';
|
||||
import '../../../services/ethereum/ethereum_api.dart';
|
||||
import '../../../themes/stack_colors.dart';
|
||||
|
@ -29,8 +30,8 @@ import '../../../widgets/stack_dialog.dart';
|
|||
|
||||
class AddCustomTokenView extends ConsumerStatefulWidget {
|
||||
const AddCustomTokenView({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
super.key,
|
||||
});
|
||||
|
||||
static const routeName = "/addCustomToken";
|
||||
|
||||
|
@ -138,7 +139,8 @@ class _AddCustomTokenViewState extends ConsumerState<AddCustomTokenView> {
|
|||
onPressed: () async {
|
||||
final response = await showLoading(
|
||||
whileFuture: EthereumAPI.getTokenContractInfoByAddress(
|
||||
contractController.text),
|
||||
contractController.text,
|
||||
),
|
||||
context: context,
|
||||
message: "Looking up contract",
|
||||
);
|
||||
|
@ -212,10 +214,12 @@ class _AddCustomTokenViewState extends ConsumerState<AddCustomTokenView> {
|
|||
controller: decimalsController,
|
||||
style: STextStyles.field(context),
|
||||
inputFormatters: [
|
||||
TextInputFormatter.withFunction((oldValue, newValue) =>
|
||||
RegExp(r'^([0-9]*)$').hasMatch(newValue.text)
|
||||
? newValue
|
||||
: oldValue),
|
||||
TextInputFormatter.withFunction(
|
||||
(oldValue, newValue) =>
|
||||
RegExp(r'^([0-9]*)$').hasMatch(newValue.text)
|
||||
? newValue
|
||||
: oldValue,
|
||||
),
|
||||
],
|
||||
keyboardType: const TextInputType.numberWithOptions(
|
||||
signed: false,
|
||||
|
@ -253,10 +257,12 @@ class _AddCustomTokenViewState extends ConsumerState<AddCustomTokenView> {
|
|||
controller: decimalsController,
|
||||
style: STextStyles.field(context),
|
||||
inputFormatters: [
|
||||
TextInputFormatter.withFunction((oldValue, newValue) =>
|
||||
RegExp(r'^([0-9]*)$').hasMatch(newValue.text)
|
||||
? newValue
|
||||
: oldValue),
|
||||
TextInputFormatter.withFunction(
|
||||
(oldValue, newValue) =>
|
||||
RegExp(r'^([0-9]*)$').hasMatch(newValue.text)
|
||||
? newValue
|
||||
: oldValue,
|
||||
),
|
||||
],
|
||||
keyboardType: const TextInputType.numberWithOptions(
|
||||
signed: false,
|
||||
|
|
|
@ -14,14 +14,10 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
import '../../../db/isar/main_db.dart';
|
||||
import '../../../models/isar/models/ethereum/eth_contract.dart';
|
||||
import '../../../notifications/show_flush_bar.dart';
|
||||
import 'add_custom_token_view.dart';
|
||||
import 'sub_widgets/add_token_list.dart';
|
||||
import 'sub_widgets/add_token_list_element.dart';
|
||||
import 'sub_widgets/add_token_text.dart';
|
||||
import '../../home_view/home_view.dart';
|
||||
import '../../../pages_desktop_specific/desktop_home_view.dart';
|
||||
import '../../../providers/global/price_provider.dart';
|
||||
import '../../../providers/global/wallets_provider.dart';
|
||||
|
@ -46,14 +42,19 @@ import '../../../widgets/icon_widgets/x_icon.dart';
|
|||
import '../../../widgets/rounded_white_container.dart';
|
||||
import '../../../widgets/stack_text_field.dart';
|
||||
import '../../../widgets/textfield_icon_button.dart';
|
||||
import '../../home_view/home_view.dart';
|
||||
import 'add_custom_token_view.dart';
|
||||
import 'sub_widgets/add_token_list.dart';
|
||||
import 'sub_widgets/add_token_list_element.dart';
|
||||
import 'sub_widgets/add_token_text.dart';
|
||||
|
||||
class EditWalletTokensView extends ConsumerStatefulWidget {
|
||||
const EditWalletTokensView({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.walletId,
|
||||
this.contractsToMarkSelected,
|
||||
this.isDesktopPopup = false,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final String walletId;
|
||||
final List<String>? contractsToMarkSelected;
|
||||
|
@ -178,7 +179,8 @@ class _EditWalletTokensViewState extends ConsumerState<EditWalletTokensView> {
|
|||
if (contracts.isEmpty) {
|
||||
contracts.addAll(DefaultTokens.list);
|
||||
MainDB.instance.putEthContracts(contracts).then(
|
||||
(_) => ref.read(priceAnd24hChangeNotifierProvider).updatePrice());
|
||||
(_) => ref.read(priceAnd24hChangeNotifierProvider).updatePrice(),
|
||||
);
|
||||
}
|
||||
|
||||
tokenEntities.addAll(contracts.map((e) => AddTokenListElementData(e)));
|
||||
|
@ -241,7 +243,8 @@ class _EditWalletTokensViewState extends ConsumerState<EditWalletTokensView> {
|
|||
"Add custom token",
|
||||
style:
|
||||
STextStyles.desktopButtonSmallSecondaryEnabled(
|
||||
context),
|
||||
context,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -18,9 +18,9 @@ import '../../../../utilities/util.dart';
|
|||
|
||||
class AddCustomTokenSelector extends StatelessWidget {
|
||||
const AddCustomTokenSelector({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.addFunction,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final VoidCallback addFunction;
|
||||
|
||||
|
|
|
@ -15,11 +15,11 @@ import '../../../../widgets/conditional_parent.dart';
|
|||
|
||||
class AddTokenList extends StatelessWidget {
|
||||
const AddTokenList({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.walletId,
|
||||
required this.items,
|
||||
required this.addFunction,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final String walletId;
|
||||
final List<AddTokenListElementData> items;
|
||||
|
|
|
@ -13,10 +13,10 @@ import '../../../../utilities/text_styles.dart';
|
|||
|
||||
class AddTokenText extends StatelessWidget {
|
||||
const AddTokenText({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.isDesktop,
|
||||
this.walletName,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final String? walletName;
|
||||
final bool isDesktop;
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
*/
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -132,11 +131,6 @@ class _AddWalletViewState extends ConsumerState<AddWalletView> {
|
|||
_searchFieldController = TextEditingController();
|
||||
_searchFocusNode = FocusNode();
|
||||
// _coinsTestnet.remove(Coin.firoTestNet);
|
||||
if (Platform.isWindows) {
|
||||
_coins.removeWhere((e) => e is Monero || e is Wownero);
|
||||
} else if (Platform.isLinux) {
|
||||
_coins.removeWhere((e) => e is Wownero);
|
||||
}
|
||||
|
||||
if (Util.isDesktop && !kDebugMode) {
|
||||
_coins.removeWhere((e) => e is BitcoinFrost);
|
||||
|
@ -154,8 +148,10 @@ class _AddWalletViewState extends ConsumerState<AddWalletView> {
|
|||
|
||||
if (contracts.isEmpty) {
|
||||
contracts.addAll(DefaultTokens.list);
|
||||
MainDB.instance.putEthContracts(contracts).then((value) =>
|
||||
ref.read(priceAnd24hChangeNotifierProvider).updatePrice());
|
||||
MainDB.instance.putEthContracts(contracts).then(
|
||||
(value) =>
|
||||
ref.read(priceAnd24hChangeNotifierProvider).updatePrice(),
|
||||
);
|
||||
}
|
||||
|
||||
tokenEntities.addAll(contracts.map((e) => EthTokenEntity(e)));
|
||||
|
@ -354,63 +350,63 @@ class _AddWalletViewState extends ConsumerState<AddWalletView> {
|
|||
height: 16,
|
||||
),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
child: Semantics(
|
||||
label:
|
||||
"Search Text Field. Inputs Text To Search In Wallets.",
|
||||
excludeSemantics: true,
|
||||
child: TextField(
|
||||
autofocus: isDesktop,
|
||||
autocorrect: !isDesktop,
|
||||
enableSuggestions: !isDesktop,
|
||||
controller: _searchFieldController,
|
||||
focusNode: _searchFocusNode,
|
||||
onChanged: (value) =>
|
||||
setState(() => _searchTerm = value),
|
||||
style: STextStyles.field(context),
|
||||
decoration: standardInputDecoration(
|
||||
"Search",
|
||||
_searchFocusNode,
|
||||
context,
|
||||
desktopMed: isDesktop,
|
||||
).copyWith(
|
||||
prefixIcon: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 16,
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.search,
|
||||
width: 16,
|
||||
height: 16,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
child: Semantics(
|
||||
label:
|
||||
"Search Text Field. Inputs Text To Search In Wallets.",
|
||||
excludeSemantics: true,
|
||||
child: TextField(
|
||||
autofocus: isDesktop,
|
||||
autocorrect: !isDesktop,
|
||||
enableSuggestions: !isDesktop,
|
||||
controller: _searchFieldController,
|
||||
focusNode: _searchFocusNode,
|
||||
onChanged: (value) =>
|
||||
setState(() => _searchTerm = value),
|
||||
style: STextStyles.field(context),
|
||||
decoration: standardInputDecoration(
|
||||
"Search",
|
||||
_searchFocusNode,
|
||||
context,
|
||||
desktopMed: isDesktop,
|
||||
).copyWith(
|
||||
prefixIcon: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 16,
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.search,
|
||||
width: 16,
|
||||
height: 16,
|
||||
),
|
||||
suffixIcon: _searchFieldController.text.isNotEmpty
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(right: 0),
|
||||
child: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
TextFieldIconButton(
|
||||
child: const XIcon(),
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
_searchFieldController.text =
|
||||
"";
|
||||
_searchTerm = "";
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
suffixIcon: _searchFieldController.text.isNotEmpty
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(right: 0),
|
||||
child: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
TextFieldIconButton(
|
||||
child: const XIcon(),
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
_searchFieldController.text = "";
|
||||
_searchTerm = "";
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
)),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
|
|
|
@ -14,10 +14,10 @@ import 'coin_select_item.dart';
|
|||
|
||||
class AddWalletEntityList extends StatelessWidget {
|
||||
const AddWalletEntityList({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.entities,
|
||||
this.trailing,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final List<AddWalletListEntity> entities;
|
||||
final Widget? trailing;
|
||||
|
|
|
@ -10,27 +10,27 @@
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
|
||||
import '../../../../models/add_wallet_list_entity/add_wallet_list_entity.dart';
|
||||
import 'add_wallet_entity_list.dart';
|
||||
import '../../../../themes/stack_colors.dart';
|
||||
import '../../../../utilities/assets.dart';
|
||||
import '../../../../utilities/text_styles.dart';
|
||||
import '../../../../utilities/util.dart';
|
||||
import '../../../../widgets/animated_widgets/rotate_icon.dart';
|
||||
import '../../../../widgets/expandable.dart';
|
||||
import 'add_wallet_entity_list.dart';
|
||||
|
||||
class ExpandingSubListItem extends StatefulWidget {
|
||||
const ExpandingSubListItem({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.entities,
|
||||
this.trailing,
|
||||
required this.initialState,
|
||||
double? animationDurationMultiplier,
|
||||
this.curve = Curves.easeInOutCubicEmphasized,
|
||||
}) : animationDurationMultiplier =
|
||||
animationDurationMultiplier ?? entities.length * 0.11,
|
||||
super(key: key);
|
||||
}) : animationDurationMultiplier =
|
||||
animationDurationMultiplier ?? entities.length * 0.11;
|
||||
|
||||
final String title;
|
||||
final List<AddWalletListEntity> entities;
|
||||
|
|
|
@ -19,9 +19,9 @@ import '../../../../utilities/text_styles.dart';
|
|||
|
||||
class AddWalletNextButton extends ConsumerWidget {
|
||||
const AddWalletNextButton({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.isDesktop,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final bool isDesktop;
|
||||
|
||||
|
|
|
@ -13,9 +13,9 @@ import '../../../../utilities/text_styles.dart';
|
|||
|
||||
class CreateRestoreWalletSubTitle extends StatelessWidget {
|
||||
const CreateRestoreWalletSubTitle({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.isDesktop,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final bool isDesktop;
|
||||
|
||||
|
|
|
@ -8,16 +8,14 @@
|
|||
*
|
||||
*/
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../name_your_wallet_view/name_your_wallet_view.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
import '../../../../themes/stack_colors.dart';
|
||||
import '../../../../utilities/enums/add_wallet_type_enum.dart';
|
||||
import '../../../../utilities/text_styles.dart';
|
||||
import '../../../../wallets/crypto_currency/coins/wownero.dart';
|
||||
import '../../../../wallets/crypto_currency/crypto_currency.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
import '../../name_your_wallet_view/name_your_wallet_view.dart';
|
||||
|
||||
class CreateWalletButtonGroup extends StatelessWidget {
|
||||
const CreateWalletButtonGroup({
|
||||
|
@ -35,37 +33,35 @@ class CreateWalletButtonGroup extends StatelessWidget {
|
|||
crossAxisAlignment:
|
||||
isDesktop ? CrossAxisAlignment.center : CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
if (Platform.isAndroid || coin is! Wownero)
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: isDesktop ? 70 : 0,
|
||||
minWidth: isDesktop ? 480 : 0,
|
||||
),
|
||||
child: TextButton(
|
||||
style: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getPrimaryEnabledButtonStyle(context),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
NameYourWalletView.routeName,
|
||||
arguments: Tuple2(
|
||||
AddWalletType.New,
|
||||
coin,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Text(
|
||||
"Create new wallet",
|
||||
style: isDesktop
|
||||
? STextStyles.desktopButtonEnabled(context)
|
||||
: STextStyles.button(context),
|
||||
),
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: isDesktop ? 70 : 0,
|
||||
minWidth: isDesktop ? 480 : 0,
|
||||
),
|
||||
child: TextButton(
|
||||
style: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.getPrimaryEnabledButtonStyle(context),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
NameYourWalletView.routeName,
|
||||
arguments: Tuple2(
|
||||
AddWalletType.New,
|
||||
coin,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Text(
|
||||
"Create new wallet",
|
||||
style: isDesktop
|
||||
? STextStyles.desktopButtonEnabled(context)
|
||||
: STextStyles.button(context),
|
||||
),
|
||||
),
|
||||
if (Platform.isAndroid || coin is! Wownero)
|
||||
SizedBox(
|
||||
height: isDesktop ? 16 : 12,
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: isDesktop ? 16 : 12,
|
||||
),
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: isDesktop ? 70 : 0,
|
||||
|
@ -91,7 +87,8 @@ class CreateWalletButtonGroup extends StatelessWidget {
|
|||
: STextStyles.button(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark),
|
||||
.accentColorDark,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -167,7 +167,7 @@ class _SelectNewFrostImportTypeViewState
|
|||
FrostStepScaffold.routeName,
|
||||
);
|
||||
},
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
@ -176,7 +176,7 @@ class _FrostCreateStep1bState extends ConsumerState<FrostCreateStep1b> {
|
|||
.routeName,
|
||||
);
|
||||
},
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
@ -70,7 +70,7 @@ class _FrostCreateStep4State extends ConsumerState<FrostCreateStep4> {
|
|||
.routeName,
|
||||
);
|
||||
},
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
@ -222,7 +222,7 @@ class _FrostReshareStep1cState extends ConsumerState<FrostReshareStep1c> {
|
|||
_buttonLock = false;
|
||||
}
|
||||
},
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
@ -341,7 +341,7 @@ class _NameYourWalletViewState extends ConsumerState<NameYourWalletView> {
|
|||
});
|
||||
}
|
||||
},
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
|
@ -40,11 +40,11 @@ import 'sub_widgets/mnemonic_table.dart';
|
|||
|
||||
class NewWalletRecoveryPhraseView extends ConsumerStatefulWidget {
|
||||
const NewWalletRecoveryPhraseView({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.wallet,
|
||||
required this.mnemonic,
|
||||
this.clipboardInterface = const ClipboardWrapper(),
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
static const routeName = "/newWalletRecoveryPhrase";
|
||||
|
||||
|
@ -91,12 +91,14 @@ class _NewWalletRecoveryPhraseViewState
|
|||
Future<void> _copy() async {
|
||||
final words = _mnemonic;
|
||||
await _clipboardInterface.setData(ClipboardData(text: words.join(" ")));
|
||||
unawaited(showFloatingFlushBar(
|
||||
type: FlushBarType.info,
|
||||
message: "Copied to clipboard",
|
||||
iconAsset: Assets.svg.copy,
|
||||
context: context,
|
||||
));
|
||||
unawaited(
|
||||
showFloatingFlushBar(
|
||||
type: FlushBarType.info,
|
||||
message: "Copied to clipboard",
|
||||
iconAsset: Assets.svg.copy,
|
||||
context: context,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -238,7 +240,8 @@ class _NewWalletRecoveryPhraseViewState
|
|||
.background
|
||||
: Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius),
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: isDesktop
|
||||
|
@ -252,7 +255,8 @@ class _NewWalletRecoveryPhraseViewState
|
|||
: STextStyles.label(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.accentColorDark),
|
||||
.accentColorDark,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -300,8 +304,9 @@ class _NewWalletRecoveryPhraseViewState
|
|||
Text(
|
||||
"Copy to clipboard",
|
||||
style: STextStyles.desktopButtonSecondaryEnabled(
|
||||
context),
|
||||
)
|
||||
context,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -325,10 +330,12 @@ class _NewWalletRecoveryPhraseViewState
|
|||
.read(verifyMnemonicCorrectWordStateProvider.state)
|
||||
.update((state) => _mnemonic[next]);
|
||||
|
||||
unawaited(Navigator.of(context).pushNamed(
|
||||
VerifyRecoveryPhraseView.routeName,
|
||||
arguments: Tuple2(_wallet, _mnemonic),
|
||||
));
|
||||
unawaited(
|
||||
Navigator.of(context).pushNamed(
|
||||
VerifyRecoveryPhraseView.routeName,
|
||||
arguments: Tuple2(_wallet, _mnemonic),
|
||||
),
|
||||
);
|
||||
},
|
||||
style: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
|
|
|
@ -13,11 +13,11 @@ import 'mnemonic_table_item.dart';
|
|||
|
||||
class MnemonicTable extends StatelessWidget {
|
||||
const MnemonicTable({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.words,
|
||||
required this.isDesktop,
|
||||
this.itemBorderColor,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final List<String> words;
|
||||
final bool isDesktop;
|
||||
|
|
|
@ -15,12 +15,12 @@ import '../../../../widgets/rounded_white_container.dart';
|
|||
|
||||
class MnemonicTableItem extends StatelessWidget {
|
||||
const MnemonicTableItem({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.number,
|
||||
required this.word,
|
||||
required this.isDesktop,
|
||||
this.borderColor,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final int number;
|
||||
final String word;
|
||||
|
|
|
@ -14,7 +14,7 @@ import '../../../widgets/desktop/secondary_button.dart';
|
|||
import '../../../widgets/stack_dialog.dart';
|
||||
|
||||
class RecoveryPhraseExplanationDialog extends StatelessWidget {
|
||||
const RecoveryPhraseExplanationDialog({Key? key}) : super(key: key);
|
||||
const RecoveryPhraseExplanationDialog({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
|
@ -21,8 +21,7 @@ import '../../../widgets/desktop/secondary_button.dart';
|
|||
import '../../../widgets/stack_dialog.dart';
|
||||
|
||||
class ConfirmRecoveryDialog extends StatelessWidget {
|
||||
const ConfirmRecoveryDialog({Key? key, required this.onConfirm})
|
||||
: super(key: key);
|
||||
const ConfirmRecoveryDialog({super.key, required this.onConfirm});
|
||||
|
||||
final VoidCallback onConfirm;
|
||||
|
||||
|
@ -85,10 +84,10 @@ class ConfirmRecoveryDialog extends StatelessWidget {
|
|||
onConfirm.call();
|
||||
},
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
@ -12,13 +12,8 @@ import 'package:dropdown_button2/dropdown_button2.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import '../../create_or_restore_wallet_view/sub_widgets/coin_image.dart';
|
||||
import 'sub_widgets/mobile_mnemonic_length_selector.dart';
|
||||
import 'sub_widgets/restore_from_date_picker.dart';
|
||||
import 'sub_widgets/restore_options_next_button.dart';
|
||||
import 'sub_widgets/restore_options_platform_layout.dart';
|
||||
import '../restore_wallet_view.dart';
|
||||
import '../sub_widgets/mnemonic_word_count_select_sheet.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
import '../../../../pages_desktop_specific/my_stack_view/exit_to_my_stack_button.dart';
|
||||
import '../../../../providers/ui/verify_recovery_phrase/mnemonic_word_count_state_provider.dart';
|
||||
import '../../../../themes/stack_colors.dart';
|
||||
|
@ -27,19 +22,23 @@ import '../../../../utilities/constants.dart';
|
|||
import '../../../../utilities/format.dart';
|
||||
import '../../../../utilities/text_styles.dart';
|
||||
import '../../../../utilities/util.dart';
|
||||
import '../../../../wallets/crypto_currency/coins/epiccash.dart';
|
||||
import '../../../../wallets/crypto_currency/coins/monero.dart';
|
||||
import '../../../../wallets/crypto_currency/coins/wownero.dart';
|
||||
import '../../../../wallets/crypto_currency/crypto_currency.dart';
|
||||
import '../../../../widgets/conditional_parent.dart';
|
||||
import '../../../../widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import '../../../../widgets/custom_buttons/checkbox_text_button.dart';
|
||||
import '../../../../widgets/date_picker/date_picker.dart';
|
||||
import '../../../../widgets/desktop/desktop_app_bar.dart';
|
||||
import '../../../../widgets/desktop/desktop_scaffold.dart';
|
||||
import '../../../../widgets/expandable.dart';
|
||||
import '../../../../widgets/rounded_white_container.dart';
|
||||
import '../../../../widgets/stack_text_field.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
import '../../create_or_restore_wallet_view/sub_widgets/coin_image.dart';
|
||||
import '../restore_wallet_view.dart';
|
||||
import '../sub_widgets/mnemonic_word_count_select_sheet.dart';
|
||||
import 'sub_widgets/mobile_mnemonic_length_selector.dart';
|
||||
import 'sub_widgets/restore_from_date_picker.dart';
|
||||
import 'sub_widgets/restore_options_next_button.dart';
|
||||
import 'sub_widgets/restore_options_platform_layout.dart';
|
||||
|
||||
class RestoreOptionsView extends ConsumerStatefulWidget {
|
||||
const RestoreOptionsView({
|
||||
|
@ -74,6 +73,9 @@ class _RestoreOptionsViewState extends ConsumerState<RestoreOptionsView> {
|
|||
|
||||
bool get supportsMnemonicPassphrase => coin.hasMnemonicPassphraseSupport;
|
||||
|
||||
bool enableLelantusScanning = false;
|
||||
bool get supportsLelantus => coin is Firo;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
walletName = widget.walletName;
|
||||
|
@ -109,12 +111,13 @@ class _RestoreOptionsViewState extends ConsumerState<RestoreOptionsView> {
|
|||
if (mounted) {
|
||||
await Navigator.of(context).pushNamed(
|
||||
RestoreWalletView.routeName,
|
||||
arguments: Tuple5(
|
||||
arguments: Tuple6(
|
||||
walletName,
|
||||
coin,
|
||||
ref.read(mnemonicWordCountStateProvider.state).state,
|
||||
_restoreFromDate,
|
||||
passwordController.text,
|
||||
enableLelantusScanning,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -219,7 +222,9 @@ class _RestoreOptionsViewState extends ConsumerState<RestoreOptionsView> {
|
|||
SizedBox(
|
||||
height: isDesktop ? 40 : 24,
|
||||
),
|
||||
if (coin is Monero ||
|
||||
if ((coin is Monero &&
|
||||
ref.watch(mnemonicWordCountStateProvider.state).state ==
|
||||
25) ||
|
||||
coin is Epiccash ||
|
||||
(coin is Wownero &&
|
||||
ref.watch(mnemonicWordCountStateProvider.state).state ==
|
||||
|
@ -235,7 +240,9 @@ class _RestoreOptionsViewState extends ConsumerState<RestoreOptionsView> {
|
|||
: STextStyles.smallMed12(context),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
if (coin is Monero ||
|
||||
if ((coin is Monero &&
|
||||
ref.watch(mnemonicWordCountStateProvider.state).state ==
|
||||
25) ||
|
||||
coin is Epiccash ||
|
||||
(coin is Wownero &&
|
||||
ref.watch(mnemonicWordCountStateProvider.state).state ==
|
||||
|
@ -243,7 +250,9 @@ class _RestoreOptionsViewState extends ConsumerState<RestoreOptionsView> {
|
|||
SizedBox(
|
||||
height: isDesktop ? 16 : 8,
|
||||
),
|
||||
if (coin is Monero ||
|
||||
if ((coin is Monero &&
|
||||
ref.watch(mnemonicWordCountStateProvider.state).state ==
|
||||
25) ||
|
||||
coin is Epiccash ||
|
||||
(coin is Wownero &&
|
||||
ref.watch(mnemonicWordCountStateProvider.state).state ==
|
||||
|
@ -253,7 +262,9 @@ class _RestoreOptionsViewState extends ConsumerState<RestoreOptionsView> {
|
|||
onTap: chooseDate,
|
||||
controller: _dateController,
|
||||
),
|
||||
if (coin is Monero ||
|
||||
if ((coin is Monero &&
|
||||
ref.watch(mnemonicWordCountStateProvider.state).state ==
|
||||
25) ||
|
||||
coin is Epiccash ||
|
||||
(coin is Wownero &&
|
||||
ref.watch(mnemonicWordCountStateProvider.state).state ==
|
||||
|
@ -264,7 +275,9 @@ class _RestoreOptionsViewState extends ConsumerState<RestoreOptionsView> {
|
|||
onTap: chooseDesktopDate,
|
||||
controller: _dateController,
|
||||
),
|
||||
if (coin is Monero ||
|
||||
if ((coin is Monero &&
|
||||
ref.watch(mnemonicWordCountStateProvider.state).state ==
|
||||
25) ||
|
||||
coin is Epiccash ||
|
||||
(coin is Wownero &&
|
||||
ref.watch(mnemonicWordCountStateProvider.state).state ==
|
||||
|
@ -272,7 +285,9 @@ class _RestoreOptionsViewState extends ConsumerState<RestoreOptionsView> {
|
|||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
if (coin is Monero ||
|
||||
if ((coin is Monero &&
|
||||
ref.watch(mnemonicWordCountStateProvider.state).state ==
|
||||
25) ||
|
||||
coin is Epiccash ||
|
||||
(coin is Wownero &&
|
||||
ref.watch(mnemonicWordCountStateProvider.state).state ==
|
||||
|
@ -293,7 +308,9 @@ class _RestoreOptionsViewState extends ConsumerState<RestoreOptionsView> {
|
|||
),
|
||||
),
|
||||
),
|
||||
if (coin is Monero ||
|
||||
if ((coin is Monero &&
|
||||
ref.watch(mnemonicWordCountStateProvider.state).state ==
|
||||
25) ||
|
||||
coin is Epiccash ||
|
||||
(coin is Wownero &&
|
||||
ref.watch(mnemonicWordCountStateProvider.state).state ==
|
||||
|
@ -398,8 +415,8 @@ class _RestoreOptionsViewState extends ConsumerState<RestoreOptionsView> {
|
|||
"Advanced",
|
||||
style: isDesktop
|
||||
? STextStyles.desktopTextExtraExtraSmall(
|
||||
context)
|
||||
.copyWith(
|
||||
context,
|
||||
).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark3,
|
||||
|
@ -425,6 +442,17 @@ class _RestoreOptionsViewState extends ConsumerState<RestoreOptionsView> {
|
|||
color: Colors.transparent,
|
||||
child: Column(
|
||||
children: [
|
||||
CheckboxTextButton(
|
||||
label: "Scan for Lelantus transactions",
|
||||
onChanged: (newValue) {
|
||||
setState(() {
|
||||
enableLelantusScanning = newValue ?? true;
|
||||
});
|
||||
},
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
|
@ -461,7 +489,8 @@ class _RestoreOptionsViewState extends ConsumerState<RestoreOptionsView> {
|
|||
),
|
||||
GestureDetector(
|
||||
key: const Key(
|
||||
"mnemonicPassphraseFieldShowPasswordButtonKey"),
|
||||
"mnemonicPassphraseFieldShowPasswordButtonKey",
|
||||
),
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
hidePassword = !hidePassword;
|
||||
|
|
|
@ -20,9 +20,9 @@ import '../../../../../utilities/util.dart';
|
|||
|
||||
class MobileMnemonicLengthSelector extends ConsumerWidget {
|
||||
const MobileMnemonicLengthSelector({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.chooseMnemonicLength,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final VoidCallback chooseMnemonicLength;
|
||||
|
||||
|
@ -66,7 +66,7 @@ class MobileMnemonicLengthSelector extends ConsumerWidget {
|
|||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -17,10 +17,10 @@ import '../../../../../utilities/util.dart';
|
|||
|
||||
class RestoreFromDatePicker extends StatefulWidget {
|
||||
const RestoreFromDatePicker({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.onTap,
|
||||
required this.controller,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final VoidCallback onTap;
|
||||
final TextEditingController controller;
|
||||
|
|
|
@ -14,10 +14,10 @@ import '../../../../../utilities/text_styles.dart';
|
|||
|
||||
class RestoreOptionsNextButton extends StatelessWidget {
|
||||
const RestoreOptionsNextButton({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.isDesktop,
|
||||
this.onPressed,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final bool isDesktop;
|
||||
final VoidCallback? onPressed;
|
||||
|
|
|
@ -13,10 +13,10 @@ import '../../../../../themes/stack_colors.dart';
|
|||
|
||||
class RestoreOptionsPlatformLayout extends StatelessWidget {
|
||||
const RestoreOptionsPlatformLayout({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.isDesktop,
|
||||
required this.child,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final bool isDesktop;
|
||||
final Widget child;
|
||||
|
|
|
@ -22,15 +22,9 @@ import 'package:flutter_libmonero/monero/monero.dart' as libxmr;
|
|||
import 'package:flutter_libmonero/wownero/wownero.dart' as libwow;
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:wakelock/wakelock.dart';
|
||||
|
||||
import '../../../notifications/show_flush_bar.dart';
|
||||
import '../add_token_view/edit_wallet_tokens_view.dart';
|
||||
import 'confirm_recovery_dialog.dart';
|
||||
import 'sub_widgets/restore_failed_dialog.dart';
|
||||
import 'sub_widgets/restore_succeeded_dialog.dart';
|
||||
import 'sub_widgets/restoring_dialog.dart';
|
||||
import '../select_wallet_for_token_view.dart';
|
||||
import '../verify_recovery_phrase_view/verify_recovery_phrase_view.dart';
|
||||
import '../../home_view/home_view.dart';
|
||||
import '../../../pages_desktop_specific/desktop_home_view.dart';
|
||||
import '../../../pages_desktop_specific/my_stack_view/exit_to_my_stack_button.dart';
|
||||
import '../../../providers/db/main_db_provider.dart';
|
||||
|
@ -48,11 +42,6 @@ import '../../../utilities/enums/form_input_status_enum.dart';
|
|||
import '../../../utilities/logger.dart';
|
||||
import '../../../utilities/text_styles.dart';
|
||||
import '../../../utilities/util.dart';
|
||||
import '../../../wallets/crypto_currency/coins/epiccash.dart';
|
||||
import '../../../wallets/crypto_currency/coins/ethereum.dart';
|
||||
import '../../../wallets/crypto_currency/coins/firo.dart';
|
||||
import '../../../wallets/crypto_currency/coins/monero.dart';
|
||||
import '../../../wallets/crypto_currency/coins/wownero.dart';
|
||||
import '../../../wallets/crypto_currency/crypto_currency.dart';
|
||||
import '../../../wallets/isar/models/wallet_info.dart';
|
||||
import '../../../wallets/wallet/impl/epiccash_wallet.dart';
|
||||
|
@ -69,7 +58,14 @@ import '../../../widgets/icon_widgets/qrcode_icon.dart';
|
|||
import '../../../widgets/table_view/table_view.dart';
|
||||
import '../../../widgets/table_view/table_view_cell.dart';
|
||||
import '../../../widgets/table_view/table_view_row.dart';
|
||||
import 'package:wakelock/wakelock.dart';
|
||||
import '../../home_view/home_view.dart';
|
||||
import '../add_token_view/edit_wallet_tokens_view.dart';
|
||||
import '../select_wallet_for_token_view.dart';
|
||||
import '../verify_recovery_phrase_view/verify_recovery_phrase_view.dart';
|
||||
import 'confirm_recovery_dialog.dart';
|
||||
import 'sub_widgets/restore_failed_dialog.dart';
|
||||
import 'sub_widgets/restore_succeeded_dialog.dart';
|
||||
import 'sub_widgets/restoring_dialog.dart';
|
||||
|
||||
class RestoreWalletView extends ConsumerStatefulWidget {
|
||||
const RestoreWalletView({
|
||||
|
@ -79,6 +75,7 @@ class RestoreWalletView extends ConsumerStatefulWidget {
|
|||
required this.seedWordsLength,
|
||||
required this.mnemonicPassphrase,
|
||||
required this.restoreFromDate,
|
||||
this.enableLelantusScanning = false,
|
||||
this.barcodeScanner = const BarcodeScannerWrapper(),
|
||||
this.clipboard = const ClipboardWrapper(),
|
||||
});
|
||||
|
@ -90,6 +87,7 @@ class RestoreWalletView extends ConsumerStatefulWidget {
|
|||
final String mnemonicPassphrase;
|
||||
final int seedWordsLength;
|
||||
final DateTime restoreFromDate;
|
||||
final bool enableLelantusScanning;
|
||||
|
||||
final BarcodeScannerInterface barcodeScanner;
|
||||
final ClipboardInterface clipboard;
|
||||
|
@ -261,6 +259,8 @@ class _RestoreWalletViewState extends ConsumerState<RestoreWalletView> {
|
|||
otherDataJsonString = jsonEncode(
|
||||
{
|
||||
WalletInfoKeys.lelantusCoinIsarRescanRequired: false,
|
||||
WalletInfoKeys.enableLelantusScanning:
|
||||
widget.enableLelantusScanning,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import '../../../../providers/ui/verify_recovery_phrase/mnemonic_word_count_state_provider.dart';
|
||||
import '../../../../themes/stack_colors.dart';
|
||||
import '../../../../utilities/constants.dart';
|
||||
|
@ -17,9 +18,9 @@ import '../../../../utilities/text_styles.dart';
|
|||
|
||||
class MnemonicWordCountSelectSheet extends ConsumerWidget {
|
||||
const MnemonicWordCountSelectSheet({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.lengthOptions,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final List<int> lengthOptions;
|
||||
|
||||
|
@ -113,13 +114,16 @@ class MnemonicWordCountSelectSheet extends ConsumerWidget {
|
|||
.radioButtonIconEnabled,
|
||||
value: lengthOptions[i],
|
||||
groupValue: ref
|
||||
.watch(mnemonicWordCountStateProvider
|
||||
.state)
|
||||
.watch(
|
||||
mnemonicWordCountStateProvider.state,
|
||||
)
|
||||
.state,
|
||||
onChanged: (x) {
|
||||
ref
|
||||
.read(mnemonicWordCountStateProvider
|
||||
.state)
|
||||
.read(
|
||||
mnemonicWordCountStateProvider
|
||||
.state,
|
||||
)
|
||||
.state = lengthOptions[i];
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
|
|
|
@ -20,11 +20,11 @@ import '../../../../widgets/stack_dialog.dart';
|
|||
|
||||
class RestoreFailedDialog extends ConsumerStatefulWidget {
|
||||
const RestoreFailedDialog({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.errorMessage,
|
||||
required this.walletName,
|
||||
required this.walletId,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final String errorMessage;
|
||||
final String walletName;
|
||||
|
|
|
@ -20,7 +20,7 @@ import '../../../../widgets/desktop/primary_button.dart';
|
|||
import '../../../../widgets/stack_dialog.dart';
|
||||
|
||||
class RestoreSucceededDialog extends StatelessWidget {
|
||||
const RestoreSucceededDialog({Key? key}) : super(key: key);
|
||||
const RestoreSucceededDialog({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
|
@ -20,9 +20,9 @@ import '../../../../widgets/stack_dialog.dart';
|
|||
|
||||
class RestoringDialog extends StatefulWidget {
|
||||
const RestoringDialog({
|
||||
Key? key,
|
||||
super.key,
|
||||
required this.onCancel,
|
||||
}) : super(key: key);
|
||||
});
|
||||
|
||||
final Future<void> Function() onCancel;
|
||||
|
||||
|
|