Compare commits

..
Author SHA1 Message Date
frank 63226f7f77 fix_: default log level to error for release build 2024-10-31 15:07:35 +08:00
109 changed files with 1085 additions and 1767 deletions
+1
View File
@@ -1,5 +1,6 @@
CACHED_WEBVIEWS_ENABLED=1
DEBUG_WEBVIEW=1
DEFAULT_NETWORK=mainnet_rpc
DEV_BUILD=1
ETHEREUM_DEV_CLUSTER=1
EXTENSIONS=0
+3 -2
View File
@@ -1,4 +1,5 @@
DEBUG_WEBVIEW=1
DEFAULT_NETWORK=goerli_rpc
ETHEREUM_DEV_CLUSTER=1
EXTENSIONS=0
FLEET=status.staging
@@ -21,8 +22,8 @@ KEYCARD_TEST_MENU=1
QR_READ_TEST_MENU=1
ENABLE_ROOT_ALERT=0
APN_TOPIC=im.status.ethereum.pr
VERIFY_TRANSACTION_CHAIN_ID=11155111
VERIFY_ENS_CHAIN_ID=11155111
VERIFY_TRANSACTION_CHAIN_ID=5
VERIFY_ENS_CHAIN_ID=5
TEST_STATEOFUS=1
DATABASE_MANAGEMENT_ENABLED=1
COMMUNITIES_ENABLED=1
+3 -2
View File
@@ -1,5 +1,6 @@
CACHED_WEBVIEWS_ENABLED=1
DEBUG_WEBVIEW=1
DEFAULT_NETWORK=goerli_rpc
ETHEREUM_DEV_CLUSTER=1
EXTENSIONS=0
GROUP_CHATS_ENABLED=1
@@ -20,8 +21,8 @@ KEYCARD_TEST_MENU=0
ENABLE_ROOT_ALERT=1
DISABLE_WALLET_ON_MOBILE_NETWORK=1
APN_TOPIC=im.status.ethereum.pr
VERIFY_TRANSACTION_CHAIN_ID=11155111
VERIFY_ENS_CHAIN_ID=11155111
VERIFY_TRANSACTION_CHAIN_ID=5
VERIFY_ENS_CHAIN_ID=5
TEST_STATEOFUS=1
BLANK_PREVIEW=0
DATABASE_MANAGEMENT_ENABLED=1
+1
View File
@@ -1,4 +1,5 @@
DEBUG_WEBVIEW=1
DEFAULT_NETWORK=mainnet_rpc
ETHEREUM_DEV_CLUSTER=1
EXTENSIONS=0
GROUP_CHATS_ENABLED=1
+2 -1
View File
@@ -1,8 +1,9 @@
DEBUG_WEBVIEW=0
DEFAULT_NETWORK=mainnet_rpc
ETHEREUM_DEV_CLUSTER=0
EXTENSIONS=0
GROUP_CHATS_ENABLED=1
LOG_LEVEL=
LOG_LEVEL=error
MAILSERVER_CONFIRMATIONS_ENABLED=1
MAINNET_WARNING_ENABLED=1
PFS_ENCRYPTION_ENABLED=1
+1 -1
View File
@@ -2,7 +2,7 @@
name: Bug Report
about: Bug Report
title: ''
type: 'Bug'
labels: ['bug', ':1234: low prio']
assignees: ''
---
+1 -1
View File
@@ -92,7 +92,7 @@ For particular example it was failed on `Recover access(password:qwerty, keycard
Not all features of the app could be covered by e2e at the moment:
- Colours or place of an element on UI.
- Real ETH/token transactions. Thats the main reason we have separate .apk build for automation needs - it defaults to Sepolia network. Also it has enabled keycard test menu, ENS names and chat commands are also on Sepolia network (the same in PR builds, but not in nightlies / release)
- Real ETH/token transactions. Thats the main reason we have separate .apk build for automation needs - it defaults to Goerli network. Also it has enabled keycard test menu, ENS names and chat commands are also on Goerli network (the same in PR builds, but not in nightlies / release)
- Autologin/Biometric related actions (autologin available when device meets certain conditions like the it has set unlock password and device is not rooted: all emulators are rooted in SauceLabs)
## Brief flow for test to be automated
@@ -14,8 +14,9 @@ RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(sendLogs:(NSString *)dbJson
jsLogs:(NSString *)jsLogs
callback:(RCTResponseSenderBlock)callback) {
// TODO: Implement SendLogs for iOS
#if DEBUG
NSLog(@"SendLogs() method called");
NSLog(@"SendLogs() method called, not implemented");
#endif
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error = nil;
@@ -33,6 +34,25 @@ RCT_EXPORT_METHOD(sendLogs:(NSString *)dbJson
NSURL *dbFile = [logsFolderName URLByAppendingPathComponent:@"db.json"];
NSURL *jsLogsFile = [logsFolderName URLByAppendingPathComponent:@"Status.log"];
#if DEBUG
NSString *networkDirPath = @"ethereum/mainnet_rpc_dev";
#else
NSString *networkDirPath = @"ethereum/mainnet_rpc";
#endif
#if DEBUG
NSString *goerliNetworkDirPath = @"ethereum/goerli_rpc_dev";
#else
NSString *goerliNetworkDirPath = @"ethereum/goerli_rpc";
#endif
NSURL *networkDir = [rootUrl URLByAppendingPathComponent:networkDirPath];
NSURL *originalGethLogsFile = [networkDir URLByAppendingPathComponent:@"geth.log"];
NSURL *gethLogsFile = [logsFolderName URLByAppendingPathComponent:@"mainnet_geth.log"];
NSURL *goerliNetworkDir = [rootUrl URLByAppendingPathComponent:goerliNetworkDirPath];
NSURL *goerliGethLogsFile = [goerliNetworkDir URLByAppendingPathComponent:@"geth.log"];
NSURL *goerliLogsFile = [logsFolderName URLByAppendingPathComponent:@"goerli_geth.log"];
NSURL *mainGethLogsFile = [rootUrl URLByAppendingPathComponent:@"geth.log"];
NSURL *mainLogsFile = [logsFolderName URLByAppendingPathComponent:@"geth.log"];
@@ -42,6 +62,10 @@ RCT_EXPORT_METHOD(sendLogs:(NSString *)dbJson
[dbJson writeToFile:dbFile.path atomically:YES encoding:NSUTF8StringEncoding error:nil];
[jsLogs writeToFile:jsLogsFile.path atomically:YES encoding:NSUTF8StringEncoding error:nil];
//NSString* gethLogs = StatusgoExportNodeLogs();
//[gethLogs writeToFile:gethLogsFile.path atomically:YES encoding:NSUTF8StringEncoding error:nil];
[fileManager copyItemAtPath:originalGethLogsFile.path toPath:gethLogsFile.path error:nil];
[fileManager copyItemAtPath:goerliGethLogsFile.path toPath:goerliLogsFile.path error:nil];
[fileManager copyItemAtPath:mainGethLogsFile.path toPath:mainLogsFile.path error:nil];
if ([fileManager fileExistsAtPath:requestsLogFile.path]) {
Binary file not shown.

Before

Width:  |  Height:  |  Size: 810 B

After

Width:  |  Height:  |  Size: 673 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1001 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

+3
View File
@@ -32,9 +32,12 @@ trap "rm -vf ${SECRETS_FILE_PATH}" EXIT ERR INT QUIT
# Names of variables containing secrets to save in env file.
SECRETS_ENV_VARS=(
'ALCHEMY_ETHEREUM_MAINNET_TOKEN'
'ALCHEMY_ETHEREUM_GOERLI_TOKEN'
'ALCHEMY_ETHEREUM_SEPOLIA_TOKEN'
'ALCHEMY_ARBITRUM_GOERLI_TOKEN'
'ALCHEMY_ARBITRUM_MAINNET_TOKEN'
'ALCHEMY_ARBITRUM_SEPOLIA_TOKEN'
'ALCHEMY_OPTIMISM_GOERLI_TOKEN'
'ALCHEMY_OPTIMISM_MAINNET_TOKEN'
'ALCHEMY_OPTIMISM_SEPOLIA_TOKEN'
'RARIBLE_MAINNET_API_KEY'
+9
View File
@@ -75,10 +75,13 @@
status-im.config/RARIBLE_MAINNET_API_KEY #shadow/env "RARIBLE_MAINNET_API_KEY"
status-im.config/RARIBLE_TESTNET_API_KEY #shadow/env "RARIBLE_TESTNET_API_KEY"
status-im.config/ALCHEMY_ETHEREUM_MAINNET_TOKEN #shadow/env "ALCHEMY_ETHEREUM_MAINNET_TOKEN"
status-im.config/ALCHEMY_ETHEREUM_GOERLI_TOKEN #shadow/env "ALCHEMY_ETHEREUM_GOERLI_TOKEN"
status-im.config/ALCHEMY_ETHEREUM_SEPOLIA_TOKEN #shadow/env "ALCHEMY_ETHEREUM_SEPOLIA_TOKEN"
status-im.config/ALCHEMY_ARBITRUM_MAINNET_TOKEN #shadow/env "ALCHEMY_ARBITRUM_MAINNET_TOKEN"
status-im.config/ALCHEMY_ARBITRUM_GOERLI_TOKEN #shadow/env "ALCHEMY_ARBITRUM_GOERLI_TOKEN"
status-im.config/ALCHEMY_ARBITRUM_SEPOLIA_TOKEN #shadow/env "ALCHEMY_ARBITRUM_SEPOLIA_TOKEN"
status-im.config/ALCHEMY_OPTIMISM_MAINNET_TOKEN #shadow/env "ALCHEMY_OPTIMISM_MAINNET_TOKEN"
status-im.config/ALCHEMY_OPTIMISM_GOERLI_TOKEN #shadow/env "ALCHEMY_OPTIMISM_GOERLI_TOKEN"
status-im.config/ALCHEMY_OPTIMISM_SEPOLIA_TOKEN #shadow/env "ALCHEMY_OPTIMISM_SEPOLIA_TOKEN"}
:compiler-options {:output-feature-set :es5
;; We disable `:fn-deprecated` warnings because we
@@ -111,10 +114,13 @@
status-im.config/RARIBLE_MAINNET_API_KEY #shadow/env "RARIBLE_MAINNET_API_KEY"
status-im.config/RARIBLE_TESTNET_API_KEY #shadow/env "RARIBLE_TESTNET_API_KEY"
status-im.config/ALCHEMY_ETHEREUM_MAINNET_TOKEN #shadow/env "ALCHEMY_ETHEREUM_MAINNET_TOKEN"
status-im.config/ALCHEMY_ETHEREUM_GOERLI_TOKEN #shadow/env "ALCHEMY_ETHEREUM_GOERLI_TOKEN"
status-im.config/ALCHEMY_ETHEREUM_SEPOLIA_TOKEN #shadow/env "ALCHEMY_ETHEREUM_SEPOLIA_TOKEN"
status-im.config/ALCHEMY_ARBITRUM_MAINNET_TOKEN #shadow/env "ALCHEMY_ARBITRUM_MAINNET_TOKEN"
status-im.config/ALCHEMY_ARBITRUM_GOERLI_TOKEN #shadow/env "ALCHEMY_ARBITRUM_GOERLI_TOKEN"
status-im.config/ALCHEMY_ARBITRUM_SEPOLIA_TOKEN #shadow/env "ALCHEMY_ARBITRUM_SEPOLIA_TOKEN"
status-im.config/ALCHEMY_OPTIMISM_MAINNET_TOKEN #shadow/env "ALCHEMY_OPTIMISM_MAINNET_TOKEN"
status-im.config/ALCHEMY_OPTIMISM_GOERLI_TOKEN #shadow/env "ALCHEMY_OPTIMISM_GOERLI_TOKEN"
status-im.config/ALCHEMY_OPTIMISM_SEPOLIA_TOKEN #shadow/env "ALCHEMY_OPTIMISM_SEPOLIA_TOKEN"}
:compiler-options {:output-feature-set :es6
;;disable for android build as there
@@ -152,10 +158,13 @@
status-im.config/RARIBLE_MAINNET_API_KEY #shadow/env "RARIBLE_MAINNET_API_KEY"
status-im.config/RARIBLE_TESTNET_API_KEY #shadow/env "RARIBLE_TESTNET_API_KEY"
status-im.config/ALCHEMY_ETHEREUM_MAINNET_TOKEN #shadow/env "ALCHEMY_ETHEREUM_MAINNET_TOKEN"
status-im.config/ALCHEMY_ETHEREUM_GOERLI_TOKEN #shadow/env "ALCHEMY_ETHEREUM_GOERLI_TOKEN"
status-im.config/ALCHEMY_ETHEREUM_SEPOLIA_TOKEN #shadow/env "ALCHEMY_ETHEREUM_SEPOLIA_TOKEN"
status-im.config/ALCHEMY_ARBITRUM_MAINNET_TOKEN #shadow/env "ALCHEMY_ARBITRUM_MAINNET_TOKEN"
status-im.config/ALCHEMY_ARBITRUM_GOERLI_TOKEN #shadow/env "ALCHEMY_ARBITRUM_GOERLI_TOKEN"
status-im.config/ALCHEMY_ARBITRUM_SEPOLIA_TOKEN #shadow/env "ALCHEMY_ARBITRUM_SEPOLIA_TOKEN"
status-im.config/ALCHEMY_OPTIMISM_MAINNET_TOKEN #shadow/env "ALCHEMY_OPTIMISM_MAINNET_TOKEN"
status-im.config/ALCHEMY_OPTIMISM_GOERLI_TOKEN #shadow/env "ALCHEMY_OPTIMISM_GOERLI_TOKEN"
status-im.config/ALCHEMY_OPTIMISM_SEPOLIA_TOKEN #shadow/env "ALCHEMY_OPTIMISM_SEPOLIA_TOKEN"
status-im.config/WALLET_CONNECT_PROJECT_ID #shadow/env "WALLET_CONNECT_PROJECT_ID"}
:compiler-options
+12 -10
View File
@@ -3,7 +3,8 @@
["react-native" :as rn]
["react-native-status-keycard" :default status-keycard]
[react-native.platform :as platform]
[taoensso.timbre :as log]))
[taoensso.timbre :as log]
[utils.address :as address]))
(defonce event-emitter
(if platform/ios?
@@ -93,14 +94,22 @@
[{:keys [on-success on-failure]}]
(.. status-keycard
(getApplicationInfo)
(then on-success)
(then (fn [response]
(let [info (-> response
(js->clj :keywordize-keys true)
(update :key-uid address/normalized-hex))]
(on-success info))))
(catch on-failure)))
(defn factory-reset
[{:keys [on-success on-failure]}]
(.. status-keycard
(factoryReset)
(then on-success)
(then (fn [response]
(let [info (-> response
(js->clj :keywordize-keys true)
(update :key-uid address/normalized-hex))]
(on-success info))))
(catch on-failure)))
(defn install-applet
@@ -146,13 +155,6 @@
(then on-success)
(catch on-failure)))
(defn save-mnemonic
[{:keys [mnemonic pin on-success on-failure]}]
(.. status-keycard
(saveMnemonic mnemonic pin)
(then on-success)
(catch on-failure)))
(defn unblock-pin
[{:keys [puk new-pin on-success on-failure]}]
(when (and new-pin puk)
+2 -2
View File
@@ -20,9 +20,9 @@
{:mainnet {:name "Ether"
:symbol :ETH
:decimals 18}
:sepolia {:name "Sepolia Ether"
:goerli {:name "Goerli Ether"
:symbol :ETH
:symbol-display :ETH
:symbol-display :ETHgo
:decimals 18}
:xdai {:name "xDAI"
:symbol :ETH
@@ -15,12 +15,12 @@
(def etherscan-supported?
#{(chain/chain-keyword->chain-id :mainnet)
(chain/chain-keyword->chain-id :sepolia)})
(chain/chain-keyword->chain-id :goerli)})
(def binance-mainnet-chain-id (chain/chain-keyword->chain-id :bsc))
(def binance-testnet-chain-id (chain/chain-keyword->chain-id :bsc-testnet))
(def network->subdomain {11155111 "sepolia"})
(def network->subdomain {5 "goerli"})
(defn get-transaction-details-url
[chain-id tx-hash]
+7 -6
View File
@@ -508,16 +508,17 @@
new-password-hashed
callback))))
(defn convert-to-keycard-profile
[{:keys [key-uid] :as profile} settings password new-password callback]
(defn convert-to-keycard-account
[{:keys [key-uid] :as multiaccount-data} settings current-password# new-password callback]
(log/debug "[native-module] convert-to-keycard-account")
(.convertToKeycardAccount ^js (encryption)
key-uid
(types/clj->json profile)
(types/clj->json multiaccount-data)
(types/clj->json settings)
(:keycard-instance-uid settings)
password
""
current-password#
new-password
#(when callback (callback (types/json->clj %)))))
callback))
(defn backup-disabled-data-dir
[]
@@ -3,12 +3,12 @@
(def inner-container
{:flex-direction :row
:justify-content :space-between
:justify-content :center
:flex 1})
(def button-container
{:padding-vertical 8
:flex 1
:width 77.75
:justify-content :center
:align-items :center})
+1 -1
View File
@@ -16,7 +16,7 @@
"
[{:keys [holder-name locked?]}]
(let [theme (quo.theme/use-theme)
label (if holder-name
label (if (boolean holder-name)
(i18n/label :t/user-keycard {:name holder-name})
(i18n/label :t/empty-keycard))]
[rn/view {:style (style/card-container locked? theme)}
+2 -4
View File
@@ -3,10 +3,8 @@
(defn container
[container-style]
(merge container-style
{:padding-vertical 7
:padding-horizontal 20
:flex-direction :row
:align-items :flex-start}))
{:flex-direction :row
:align-items :flex-start}))
(def index
{:margin-left 5})
+1 -1
View File
@@ -45,7 +45,7 @@
:or {type :bullet}}]
(let [theme (quo.theme/use-theme)]
[rn/view {:style (style/container container-style)}
[rn/view
[rn/view {:style style/index}
(case type
:step
[step/view
@@ -55,36 +55,26 @@
theme))}])]))
(defn- left-title
[{:keys [title blur? title-icon]}]
[{:keys [title blur?]}]
(let [theme (quo.theme/use-theme)]
[rn/view {:style style/title-container}
[text/text
{:weight :regular
:size :paragraph-2
:style (style/title blur? theme)}
title]
(when title-icon
[icons/icon title-icon
{:accessibility-label :title-icon
:size 12
:color (if blur?
colors/neutral-40
(colors/theme-colors colors/neutral-50
colors/neutral-40
theme))}])]))
title]]))
(defn- left-side
"The description can either be given as a string `subtitle-type` or a component `custom-subtitle`"
[{:keys [title status size blur? custom-subtitle icon subtitle subtitle-type subtitle-color icon-color
customization-color network-image emoji title-icon]
customization-color network-image emoji]
:as props}]
(let [theme (quo.theme/use-theme)]
[rn/view {:style style/left-side}
[rn/view
[left-title
{:title title
:title-icon title-icon
:blur? blur?}]]
[left-title
{:title title
:blur? blur?
:theme theme}]
(if (= status :loading)
[left-loading
{:size size
@@ -142,7 +132,6 @@
[:subtitle {:optional true} [:maybe [:or :string :double]]]
[:custom-subtitle {:optional true} [:maybe fn?]]
[:icon {:optional true} [:maybe :keyword]]
[:title-icon {:optional true} [:maybe :keyword]]
[:emoji {:optional true} [:maybe :string]]
[:customization-color {:optional true} [:maybe :schema.common/customization-color]]
[:network-image {:optional true} [:maybe :schema.common/image-source]]
@@ -59,15 +59,12 @@
- :image-source - resource - image to display on :network, :collectible and :user
- :theme - :light / :dark"
[{:keys [label customization-color type container-style]
:as props}]
(let [theme (quo.theme/use-theme)
default-color (if (= theme :light)
colors/neutral-80-opa-5
colors/white-opa-5)
color (or customization-color default-color)]
:as props
:or {customization-color colors/neutral-80-opa-5}}]
(let [theme (quo.theme/use-theme)]
[rn/view
{:accessibility-label :container
:style (merge (style/main (assoc props :customization-color color)
:style (merge (style/main (assoc props :customization-color customization-color)
theme)
container-style)}
[left-view props]
@@ -8,7 +8,8 @@
(def header
{:flex-direction :row
:justify-content :space-between})
:justify-content :space-between
:height 32})
(def header-title
{:flex 1
@@ -59,6 +59,7 @@
[rn/view {:style (merge style/container container-style)}
[text/text
{:size :heading-1
:number-of-lines 1
:weight :semi-bold
:style style/text
:accessibility-label accessibility-label}
@@ -7,7 +7,8 @@
:margin-bottom 8})
(def item-text
{:margin-left -24})
{:margin-top 10
:margin-left -4})
(def info-text
{:margin-top -5})
-5
View File
@@ -22,11 +22,6 @@
:qr-code (js/require "../resources/images/ui2/qr-code.png")
:keycard-logo (js/require "../resources/images/ui2/keycard-logo.png")
:keycard-watermark (js/require "../resources/images/ui2/keycard-watermark.png")
:keycard-buy (js/require "../resources/images/ui2/keycard-buy.png")
:keycard-migration (js/require "../resources/images/ui2/keycard-migration.png")
:keycard-migration-failed (js/require "../resources/images/ui2/keycard-migration-failed.png")
:keycard-migration-succeeded (js/require "../resources/images/ui2/keycard-migration-succeeded.png")
:not-keycard (js/require "../resources/images/ui2/not-keycard.png")
:discover (js/require "../resources/images/ui2/discover.png")
:invite-friends (js/require "../resources/images/ui2/invite-friends.png")
:transaction-progress (js/require "../resources/images/ui2/transaction-progress.png")
@@ -1,10 +1,9 @@
(ns status-im.common.standard-authentication.enter-password.view
(:require
[clojure.string :as string]
[quo.core :as quo]
[react-native.core :as rn]
[status-im.common.standard-authentication.core :as standard-authentication]
[status-im.common.standard-authentication.enter-password.style :as style]
[status-im.common.standard-authentication.password-input.view :as password-input]
[status-im.contexts.profile.utils :as profile.utils]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
@@ -32,12 +31,11 @@
:full-name (profile.utils/displayed-name profile)
:customization-color customization-color
:size 24}]]
[standard-authentication/password-input
[password-input/view
{:on-press-biometrics on-press-biometrics
:blur? true
:processing processing
:error {:error? (not (string/blank? error))
:error-message error}
:error error
:default-password password
:sign-in-enabled? sign-in-enabled?}]
[quo/button
@@ -1,5 +1,6 @@
(ns status-im.common.standard-authentication.password-input.view
(:require
[clojure.string :as string]
[quo.core :as quo]
[quo.foundations.colors :as colors]
[quo.theme :as quo.theme]
@@ -11,6 +12,14 @@
[utils.re-frame :as rf]
[utils.security.core :as security]))
(defn- get-error-message
[error]
(if (and (some? error)
(or (= error "file is not a database")
(string/starts-with? (string/lower-case error) "failed")))
(i18n/label :t/oops-wrong-password)
error))
(defn- error-info
[error-message processing shell?]
(let [theme (quo.theme/use-theme)
@@ -40,21 +49,21 @@
(i18n/label :t/forgot-password)]]]))
(defn view
[{:keys [shell? on-press-biometrics blur? processing error]}]
(let [{:keys [error?
error-message]} error
default-value (rn/use-ref-atom "") ;;bug on Android
;;https://github.com/status-im/status-mobile/issues/19004
theme (quo.theme/use-theme)
on-change-password (rn/use-callback
(fn [entered-password]
(reset! default-value entered-password)
(debounce/debounce-and-dispatch
[:profile/on-password-input-changed
{:password (security/mask-data
entered-password)
:error ""}]
100)))]
[{:keys [shell? on-press-biometrics blur?]}]
(let [{:keys [error processing]} (rf/sub [:profile/login])
error-message (rn/use-memo #(get-error-message error) [error])
error? (boolean (seq error-message))
default-value (rn/use-ref-atom "") ;;bug on Android
;;https://github.com/status-im/status-mobile/issues/19004
theme (quo.theme/use-theme)
on-change-password (rn/use-callback
(fn [entered-password]
(reset! default-value entered-password)
(debounce/debounce-and-dispatch [:profile/on-password-input-changed
{:password (security/mask-data
entered-password)
:error ""}]
100)))]
[:<>
[rn/view {:style {:flex-direction :row}}
[quo/input
+9 -3
View File
@@ -18,23 +18,28 @@
(goog-define RARIBLE_MAINNET_API_KEY "")
(goog-define RARIBLE_TESTNET_API_KEY "")
(goog-define ALCHEMY_ETHEREUM_MAINNET_TOKEN "")
(goog-define ALCHEMY_ETHEREUM_GOERLI_TOKEN "")
(goog-define ALCHEMY_ETHEREUM_SEPOLIA_TOKEN "")
(goog-define ALCHEMY_ARBITRUM_MAINNET_TOKEN "")
(goog-define ALCHEMY_ARBITRUM_GOERLI_TOKEN "")
(goog-define ALCHEMY_ARBITRUM_SEPOLIA_TOKEN "")
(goog-define ALCHEMY_OPTIMISM_MAINNET_TOKEN "")
(goog-define ALCHEMY_OPTIMISM_GOERLI_TOKEN "")
(goog-define ALCHEMY_OPTIMISM_SEPOLIA_TOKEN "")
(goog-define WALLET_CONNECT_PROJECT_ID "87815d72a81d739d2a7ce15c2cfdefb3")
(goog-define MIXPANEL_APP_ID "3350627")
(goog-define MIXPANEL_TOKEN "5c73bda2d36a9f688a5ee45641fb6775")
(def mainnet-rpc-url (str "https://eth-archival.rpc.grove.city/v1/" POKT_TOKEN))
(def sepolia-rpc-url (str "https://sepolia-archival.rpc.grove.city/v1/" POKT_TOKEN))
(def goerli-rpc-url (str "https://goerli-archival.gateway.pokt.network/v1/lb/" POKT_TOKEN))
(def mainnet-chain-explorer-link "https://etherscan.io/address/")
(def optimism-mainnet-chain-explorer-link "https://optimistic.etherscan.io/address/")
(def arbitrum-mainnet-chain-explorer-link "https://arbiscan.io/address/")
(def sepolia-chain-explorer-link "https://sepolia.etherscan.io/address/")
(def optimism-sepolia-chain-explorer-link "https://sepolia-optimistic.etherscan.io/address/")
(def arbitrum-sepolia-chain-explorer-link "https://sepolia.arbiscan.io/address/")
(def goerli-chain-explorer-link "https://goerli.etherscan.io/address/")
(def optimism-goerli-chain-explorer-link "https://goerli-optimistic.etherscan.io/address/")
(def opensea-link "https://opensea.io")
(def opensea-tesnet-link "https://testnets.opensea.io")
@@ -74,6 +79,7 @@
(def log-request-go (enabled? (get-config :LOG_REQUEST_GO "0")))
(def fleet (get-config :FLEET ""))
(def apn-topic (get-config :APN_TOPIC "im.status.ethereum"))
(def default-network (get-config :DEFAULT_NETWORK "goerli_rpc"))
(def max-installations 2)
; currently not supported in status-go
(def enable-remove-profile-picture? false)
@@ -94,13 +100,13 @@
(def verify-transaction-url
(if (= :mainnet (chain/chain-id->chain-keyword verify-transaction-chain-id))
mainnet-rpc-url
sepolia-rpc-url))
goerli-rpc-url))
(def verify-ens-chain-id (js/parseInt (get-config :VERIFY_ENS_CHAIN_ID "1")))
(def verify-ens-url
(if (= :mainnet (chain/chain-id->chain-keyword verify-ens-chain-id))
mainnet-rpc-url
sepolia-rpc-url))
goerli-rpc-url))
(def verify-ens-contract-address
(get-config :VERIFY_ENS_CONTRACT_ADDRESS
((chain/chain-id->chain-keyword verify-ens-chain-id) utils.ens/ens-registries)))
+11 -5
View File
@@ -295,8 +295,6 @@
(def ^:const token-sort-priority {"SNT" 1 "STT" 1 "ETH" 2 "DAI" 3})
(def ^:const token-display-precision 6)
(def ^:const dapp-permission-contact-code "contact-code")
(def ^:const dapp-permission-web3 "web3")
(def ^:const dapp-permission-qr-code "qr-code")
@@ -475,22 +473,30 @@
;; wallet
(def ^:const ethereum-mainnet-chain-id 1)
(def ^:const ethereum-goerli-chain-id 5)
(def ^:const ethereum-sepolia-chain-id 11155111)
(def ^:const arbitrum-mainnet-chain-id 42161)
(def ^:const arbitrum-goerli-chain-id 421613)
(def ^:const arbitrum-sepolia-chain-id 421614)
(def ^:const optimism-mainnet-chain-id 10)
(def ^:const optimism-goerli-chain-id 420)
(def ^:const optimism-sepolia-chain-id 11155420)
(def opensea-url-names
{:ethereum "ethereum"
:sepolia "sepolia"})
:sepolia "sepolia"
:goerli "goerli"})
(def ^:const ethereum "ethereum")
(def ^:const sepolia "sepolia")
(def ^:const goerli "goerli")
(def ^:const mainnet-chain-ids
#{ethereum-mainnet-chain-id arbitrum-mainnet-chain-id optimism-mainnet-chain-id})
(def ^:const goerli-chain-ids
#{ethereum-goerli-chain-id arbitrum-goerli-chain-id optimism-goerli-chain-id})
(def ^:const sepolia-chain-ids
#{ethereum-sepolia-chain-id arbitrum-sepolia-chain-id optimism-sepolia-chain-id})
@@ -510,6 +516,7 @@
(def ^:const arbitrum-full-name "Arbitrum")
(def ^:const sepolia-full-name "Sepolia")
(def ^:const goerli-full-name "Goerli")
(def ^:const mainnet-network-name :mainnet)
(def ^:const ethereum-network-name :ethereum)
@@ -573,14 +580,13 @@
(def ^:const sheet-screen-handle-height 20)
(def ^:const status-hostname "status.app")
(def ^:const get-keycard-url "https://get.keycard.tech/")
(def ^:const community-joined-notification-type "communityJoined")
(def ^:const default-telemetry-server-url "https://telemetry.status.im")
(def ^:const contact-item-height 56)
(def ^:const page-nav-height 56)
(def ^:const currency-item-height 64)
(def ^:const slippages [0.1 0.5 1])
@@ -4,18 +4,20 @@
[status-im.common.events-helper :as events-helper]
[status-im.common.standard-authentication.core :as standard-auth]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
[utils.re-frame :as rf]
[utils.security.core :as security]))
(defn view
[]
(let [profile-name (rf/sub [:profile/name])
profile-picture (rf/sub [:profile/image])
customization-color (rf/sub [:profile/customization-color])
{:keys [on-success]} (rf/sub [:get-screen-params])]
(let [profile-name (rf/sub [:profile/name])
profile-picture (rf/sub [:profile/image])
customization-color (rf/sub [:profile/customization-color])]
[:<>
[quo/page-nav
{:icon-name :i/close
:on-press events-helper/navigate-back}]
{:key :header
:background :blur
:icon-name :i/close
:on-press events-helper/navigate-back}]
[quo/page-top
{:title (i18n/label :t/authorise-with-password)
:description :context-tag
@@ -29,5 +31,5 @@
:container-style {}
:customization-color customization-color
:track-text (i18n/label :t/slide-to-authorise)
:on-auth-success #(when on-success (on-success %))
:on-auth-success #(println "TBD" (security/safe-unmask-data %))
:auth-button-label (i18n/label :t/confirm)}]]]))
+15 -8
View File
@@ -10,8 +10,10 @@
[]
[:<>
[quo/page-nav
{:icon-name :i/arrow-left
:on-press events-helper/navigate-back}]
{:key :header
:background :blur
:icon-name :i/arrow-left
:on-press events-helper/navigate-back}]
[quo/page-top
{:title (i18n/label :t/check-keycard)
:description :text
@@ -21,9 +23,14 @@
{:resize-mode :contain
:source (resources/get-image :check-your-keycard)}]]
[quo/divider-label (i18n/label :t/tips-scan-keycard)]
[quo/markdown-list {:description (i18n/label :t/remove-phone-case)}]
[quo/markdown-list {:description (i18n/label :t/keep-card-steady)}]
[quo/bottom-actions
{:actions :one-action
:button-one-label (i18n/label :t/ready-to-scan)
:button-one-props {:on-press #(rf/dispatch [:keycard/migration.check-empty-card])}}]])
[rn/view {:style {:padding-horizontal 10}}
[quo/markdown-list
{:container-style {:padding-vertical 10}
:description (i18n/label :t/remove-phone-case)}]
[quo/markdown-list
{:container-style {:padding-bottom 25}
:description (i18n/label :t/keep-card-steady)}]]
[quo/button
{:on-press #(rf/dispatch [:keycard/connect])
:container-style {:margin-horizontal 20}}
(i18n/label :t/ready-to-scan)]])
+103 -42
View File
@@ -3,10 +3,10 @@
[native-module.core :as native-module]
[react-native.async-storage :as async-storage]
[react-native.platform :as platform]
status-im.contexts.keycard.nfc.effects
[status-im.contexts.keycard.utils :as keycard.utils]
[status-im.contexts.profile.config :as profile.config]
[utils.re-frame :as rf]))
[taoensso.timbre :as log]
[utils.re-frame :as rf]
[utils.transforms :as transforms]))
(defonce ^:private active-listeners (atom []))
@@ -32,45 +32,106 @@
(rf/reg-fx :effects.keycard/register-card-events register-card-events)
(rf/reg-fx :effects.keycard/unregister-card-events unregister-card-events)
(rf/reg-fx :effects.keycard/get-application-info
(fn [args]
(keycard/get-application-info (keycard.utils/wrap-handlers args))))
(rf/reg-fx :effects.keycard/get-keys
(fn [args]
(keycard/get-keys (keycard.utils/wrap-handlers args))))
(rf/reg-fx :effects.keycard/sign
(fn [args]
(keycard/sign (keycard.utils/wrap-handlers args))))
(rf/reg-fx :keycard/init-card
(fn [args]
(keycard/init-card (keycard.utils/wrap-handlers args))))
(rf/reg-fx :effects.keycard/generate-and-load-key
(fn [args]
(keycard/generate-and-load-key (keycard.utils/wrap-handlers args))))
(rf/reg-fx :effects.keycard/login-with-keycard
(fn [{:keys [key-uid password whisper-private-key]}]
(native-module/login-account
(assoc (profile.config/login)
:keyUid key-uid
:password password
:keycardWhisperPrivateKey whisper-private-key))))
(rf/reg-fx :effects.keycard/set-pairing-to-keycard
(fn [pairings]
(keycard/set-pairings pairings)))
(defn check-nfc-enabled
[]
(log/debug "[keycard] check-nfc-enabled")
(keycard/check-nfc-enabled
{:on-success
(fn [response]
(log/debug "[keycard response] check-nfc-enabled")
(rf/dispatch [:keycard/on-check-nfc-enabled-success response]))}))
(rf/reg-fx :effects.keycard/check-nfc-enabled check-nfc-enabled)
(rf/reg-fx
:keycard/persist-pairings
(fn [pairings]
(async-storage/set-item! "status-keycard-pairings" pairings)))
:effects.keycard.ios/start-nfc
(fn [args]
(log/debug "fx start-nfc")
(keycard/start-nfc args)))
(rf/reg-fx :effects.keycard/retrieve-pairings
(fn []
(async-storage/get-item
"status-keycard-pairings"
#(rf/dispatch [:keycard/on-retrieve-pairings-success %]))))
(rf/reg-fx
:effects.keycard.ios/stop-nfc
(fn [args]
(log/debug "fx stop-nfc")
(keycard/stop-nfc args)))
(defn- error-object->map
[^js object]
{:code (.-code object)
:error (.-message object)})
(defn get-application-info
[{:keys [on-success on-failure] :as args}]
(log/debug "[keycard] get-application-info")
(keycard/get-application-info
(assoc
args
:on-success
(fn [response]
(log/debug "[keycard response succ] get-application-info")
(when on-success
(on-success response)))
:on-failure
(fn [response]
(log/error "[keycard response fail] get-application-info")
(when on-failure
(on-failure (error-object->map response)))))))
(rf/reg-fx :effects.keycard/get-application-info get-application-info)
(defn get-keys
[{:keys [on-success on-failure] :as args}]
(log/debug "[keycard] get-keys")
(keycard/get-keys
(assoc
args
:on-success
(fn [response]
(log/debug "[keycard response succ] get-keys")
(when on-success
(on-success (transforms/js->clj response))))
:on-failure
(fn [response]
(log/warn "[keycard response fail] get-keys"
(error-object->map response))
(when on-failure
(on-failure (error-object->map response)))))))
(rf/reg-fx :effects.keycard/get-keys get-keys)
(defn login
[{:keys [key-uid password whisper-private-key]}]
(native-module/login-account
(assoc (profile.config/login)
:keyUid key-uid
:password password
:keycardWhisperPrivateKey whisper-private-key)))
(rf/reg-fx :effects.keycard/login-with-keycard login)
(defn retrieve-pairings
[]
(async-storage/get-item
"status-keycard-pairings"
#(rf/dispatch [:keycard/on-retrieve-pairings-success %])))
(rf/reg-fx :effects.keycard/retrieve-pairings retrieve-pairings)
(defn set-pairing-to-keycard
[pairings]
(keycard/set-pairings pairings))
(rf/reg-fx :effects.keycard/set-pairing-to-keycard set-pairing-to-keycard)
(defn sign
[{:keys [on-success on-failure] :as args}]
(log/debug "[keycard] sign")
(keycard/sign
(assoc
args
:on-success
(fn [response]
(log/debug "[keycard response succ] sign")
(when on-success
(on-success (transforms/js->clj response))))
:on-failure
(fn [response]
(log/warn "[keycard response fail] sign"
(error-object->map response))
(when on-failure
(on-failure (error-object->map response)))))))
(rf/reg-fx :effects.keycard/sign sign)
+9 -12
View File
@@ -3,7 +3,7 @@
[react-native.core :as rn]
[status-im.common.events-helper :as events-helper]
[status-im.common.resources :as resources]
[status-im.contexts.keycard.migrate.sheets.view :as sheets.migrate]
[status-im.contexts.keycard.sheets.migrate.view :as sheets.migrate]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
@@ -11,8 +11,10 @@
[]
[:<>
[quo/page-nav
{:icon-name :i/close
:on-press events-helper/navigate-back}]
{:key :header
:background :blur
:icon-name :i/arrow-left
:on-press events-helper/navigate-back}]
[quo/page-top
{:title (i18n/label :t/keycard-empty)
:description :text
@@ -24,15 +26,10 @@
:subtitle (i18n/label :t/use-keycard-login-sign)
:button-label (i18n/label :t/import-profile-key-pair)
:accessibility-label :get-keycard
:image (resources/get-image :keycard-buy)
:on-press (fn []
(rf/dispatch
[:show-bottom-sheet
{:theme :dark
:content (fn []
[sheets.migrate/view
{:on-continue #(rf/dispatch
[:keycard/migration.get-phrase])}])}]))}]]
:image (resources/get-image :generate-keys)
:on-press #(rf/dispatch [:show-bottom-sheet
{:theme :dark
:content (fn [] [sheets.migrate/view])}])}]]
[quo/information-box
{:type :default
:style {:margin-top 32 :margin-horizontal 28}}
+26 -21
View File
@@ -1,37 +1,42 @@
(ns status-im.contexts.keycard.error.view
(:require [quo.core :as quo]
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[status-im.common.events-helper :as events-helper]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(def titles
{:keycard/error.keycard-blank {:title (i18n/label :t/keycard-empty)
:description (i18n/label :t/no-key-pair-keycard)}
:keycard/error.keycard-wrong-profile {:title (i18n/label :t/keycard-not-empty)
:description (i18n/label :t/cant-store-new-keys)}
:keycard/error.keycard-unpaired {:title (i18n/label :t/keycard-full)
:description (i18n/label :t/pairing-slots-occupied)}
:keycard/error.keycard-frozen {:title (i18n/label :t/keycard-locked)
:description (i18n/label :t/cant-use-right-now)}
:keycard/error.keycard-locked {:title (i18n/label :t/keycard-locked)
:description (i18n/label :t/cant-use-right-now)}})
{:keycard/error.keycard-wrong {:title "Keycard is not empty"
:description "You cant use it to store new keys right now"}
:keycard/error.keycard-unpaired {:title "Keycard is full"
:description "All pairing slots are occupied"}
:keycard/error.keycard-frozen {:title "Keycard is locked"
:description "You cant use it right now"}
:keycard/error.keycard-locked {:title "Keycard is locked"
:description "You cant use it right now"}})
(defn view
[]
(let [error (rf/sub [:keycard/application-info-error])
(let [{:keys [top bottom]} (safe-area/get-insets)
error (rf/sub [:keycard/application-info-error])
{:keys [title description]} (get titles error)]
[:<>
[quo/overlay
{:type :shell
:container-style {:padding-top top
:padding-bottom bottom}}
[quo/page-nav
{:icon-name :i/close
:on-press events-helper/navigate-back}]
{:key :header
:background :blur
:icon-name :i/arrow-left
:on-press events-helper/navigate-back}]
[quo/page-top
{:title title
:description :text
:description-text description}]
[rn/view {:style {:margin-horizontal 20}}
[quo/keycard {:holder-name ""}]
[quo/information-box
{:type :default
:style {:margin-top 20}}
(i18n/label :t/unlock-reset-instructions)]]]))
[rn/view {:height 226}]
[rn/view {:padding-horizontal 20}
[quo/info-message
{:container-style {:padding-top 15}
:icon :i/info
:size :default}
"To unlock or factory reset the Keycard, please use the Status desktop app. If you'd like this features on mobile, feel free to upvote them and discuss in the Status community."]]]))
+70 -71
View File
@@ -1,117 +1,116 @@
(ns status-im.contexts.keycard.events
(:require [re-frame.core :as rf]
status-im.contexts.keycard.login.events
status-im.contexts.keycard.migrate.events
status-im.contexts.keycard.migrate.re-encrypting.events
status-im.contexts.keycard.nfc.events
status-im.contexts.keycard.nfc.sheets.events
status-im.contexts.keycard.nfc-sheet.events
status-im.contexts.keycard.pin.events
status-im.contexts.keycard.sign.events
[status-im.contexts.keycard.utils :as keycard.utils]
utils.datetime))
[taoensso.timbre :as log]))
(rf/reg-event-fx :keycard/on-check-nfc-enabled-success
(fn [{:keys [db]} [nfc-enabled?]]
{:db (assoc-in db [:keycard :nfc-enabled?] nfc-enabled?)}))
(rf/reg-event-fx :keycard.ios/on-nfc-user-cancelled
(fn [{:keys [db]}]
(log/debug "[keycard] nfc user cancelled")
{:db (-> db
(assoc-in [:keycard :pin :status] nil)
(assoc-in [:keycard :on-nfc-cancelled-event-vector] nil))
:fx [(when-let [on-nfc-cancelled-event-vector (get-in db [:keycard :on-nfc-cancelled-event-vector])]
[:dispatch on-nfc-cancelled-event-vector])]}))
(rf/reg-event-fx :keycard/on-card-connected
(fn [{:keys [db]} _]
(log/debug "[keycard] card globally connected")
{:db (assoc-in db [:keycard :card-connected?] true)
:fx [(when-let [event (get-in db [:keycard :on-card-connected-event-vector])]
[:dispatch event])]}))
(rf/reg-event-fx :keycard/on-card-disconnected
(fn [{:keys [db]} _]
(log/debug "[keycard] card disconnected")
{:db (assoc-in db [:keycard :card-connected?] false)
:fx [(when-let [event (get-in db [:keycard :on-card-disconnected-event-vector])]
[:dispatch event])]}))
(rf/reg-event-fx :keycard.ios/start-nfc
(fn [_]
{:effects.keycard.ios/start-nfc nil}))
(rf/reg-event-fx :keycard.ios/on-nfc-timeout
(fn [{:keys [db]} _]
(log/debug "[keycard] nfc timeout")
{:db (assoc-in db [:keycard :card-connected?] false)
:fx [[:dispatch-later [{:ms 500 :dispatch [:keycard.ios/start-nfc]}]]]}))
(rf/reg-event-fx :keycard/on-retrieve-pairings-success
(fn [{:keys [db]} [pairings]]
{:db (assoc-in db [:keycard :pairings] pairings)
:fx [[:effects.keycard/set-pairing-to-keycard pairings]]}))
(rf/reg-event-fx :keycard/update-pairings
(fn [{:keys [db]} [instance-uid pairing]]
(let [pairings (get-in db [:keycard :pairings])
new-pairings (assoc pairings
instance-uid
{:pairing pairing
:paired-on (utils.datetime/timestamp)})]
{:db (assoc-in db [:keycard :pairings] new-pairings)
:keycard/persist-pairings new-pairings})))
(rf/reg-event-fx :keycard.ios/on-start-nfc-success
(fn [{:keys [db]} [{:keys [on-cancel-event-vector]}]]
(log/debug "[keycard] nfc started success")
{:db (assoc-in db [:keycard :on-nfc-cancelled-event-vector] on-cancel-event-vector)}))
(rf/reg-event-fx :keycard/on-action-with-pin-error
(fn [{:keys [db]} [error]]
(log/debug "[keycard] on-action-with-pin-error: " error)
(let [tag-was-lost? (keycard.utils/tag-lost? (:error error))
pin-retries-count (keycard.utils/pin-retries (:error error))]
(if (or tag-was-lost? (nil? pin-retries-count))
(if tag-was-lost?
{:db (assoc-in db [:keycard :pin :status] nil)}
{:db (-> db
(assoc-in [:keycard :application-info :pin-retry-counter] pin-retries-count)
(assoc-in [:keycard :pin :status] :error))
:fx [[:dispatch [:keycard/disconnect]]
(when (zero? pin-retries-count)
[:dispatch
[:keycard/on-application-info-error
:keycard/error.keycard-locked]])]}))))
(if (nil? pin-retries-count)
{:effects.utils/show-popup {:title "wrong-keycard"}}
{:db (-> db
(assoc-in [:keycard :application-info :pin-retry-counter] pin-retries-count)
(assoc-in [:keycard :pin :status] :error))
:fx [[:dispatch [:keycard/disconnect]]
(when (zero? pin-retries-count)
[:effects.utils/show-popup {:title "frozen-keycard"}])]})))))
(rf/reg-event-fx :keycard/get-keys
(fn [_ [data]]
{:effects.keycard/get-keys data}))
(rf/reg-event-fx :keycard/on-get-application-info-success
(fn [{:keys [db]} [application-info {:keys [key-uid on-success-fx]}]]
(if-let [error (keycard.utils/validate-application-info key-uid application-info)]
(case error
:keycard/error.not-keycard
{:fx [[:dispatch [:keycard/disconnect]]
[:dispatch [:open-modal :screen/keycard.not-keycard]]]}
:keycard/error.keycard-blank
{:fx [[:dispatch [:keycard/disconnect]]
[:dispatch [:open-modal :screen/keycard.empty]]]}
{:db (assoc-in db [:keycard :application-info-error] error)
:fx [[:dispatch [:keycard/disconnect]]
[:dispatch [:open-modal :screen/keycard.error]]]})
{:db (-> db
(assoc-in [:keycard :application-info] application-info)
(assoc-in [:keycard :pin :status] :verifying))
:fx on-success-fx})))
(rf/reg-event-fx :keycard/get-application-info
(fn [_ [{:keys [on-success on-failure]}]]
{:effects.keycard/get-application-info {:on-success on-success :on-failure on-failure}}))
(rf/reg-event-fx :keycard/cancel-connection
(fn [{:keys [db]}]
{:db (update db :keycard dissoc :on-card-connected-event-vector :on-nfc-cancelled-event-vector)}))
{:db (-> db
(assoc-in [:keycard :on-card-connected-event-vector] nil)
(assoc-in [:keycard :on-nfc-cancelled-event-vector] nil))}))
(rf/reg-event-fx :keycard/disconnect
(fn [_ _]
{:fx [[:dispatch [:keycard/cancel-connection]]
[:dispatch [:keycard/hide-connection-sheet]]]}))
(rf/reg-event-fx :keycard/on-application-info-error
(fn [{:keys [db]} [error]]
{:db (assoc-in db [:keycard :application-info-error] error)
:fx [[:dispatch [:keycard/disconnect]]
[:dispatch
[:open-modal
(if (= :keycard/error.not-keycard error)
:screen/keycard.not-keycard
:screen/keycard.error)]]]}))
(rf/reg-event-fx :keycard/update-application-info
(fn [{:keys [db]} [app-info]]
{:db (update db
:keycard
#(-> %
(assoc :application-info app-info)
(dissoc :application-info-error)))}))
(rf/reg-event-fx :keycard/get-application-info
(fn [_ [{:keys [key-uid on-success on-error]}]]
{:effects.keycard/get-application-info
{:on-success (fn [{:keys [instance-uid new-pairing] :as app-info}]
(rf/dispatch [:keycard/update-application-info app-info])
(when (and instance-uid new-pairing)
(rf/dispatch [:keycard/update-pairings instance-uid new-pairing]))
(if-let [error (keycard.utils/validate-application-info key-uid app-info)]
(if on-error
(on-error error)
(rf/dispatch [:keycard/on-application-info-error error]))
(when on-success (on-success app-info))))
:on-error (fn []
(if on-error
(on-error :keycard/error.not-keycard)
(rf/dispatch [:keycard/on-application-info-error
:keycard/error.not-keycard])))}}))
(rf/reg-event-fx :keycard/connect
(fn [{:keys [db]} [{:keys [key-uid on-success on-error on-connect-event-vector]}]]
(let [event-vector
(or on-connect-event-vector
[:keycard/get-application-info
{:key-uid key-uid
:on-success on-success
:on-error on-error}])]
(fn [{:keys [db]} [args]]
(let [connected? (get-in db [:keycard :card-connected?])
event-vector [:keycard/get-application-info
{:on-success #(rf/dispatch [:keycard/on-get-application-info-success % args])}]]
{:db (assoc-in db [:keycard :on-card-connected-event-vector] event-vector)
:fx [[:dispatch
[:keycard/show-connection-sheet
{:on-cancel-event-vector [:keycard/cancel-connection]}]]
(when (get-in db [:keycard :card-connected?])
(when connected?
[:dispatch event-vector])]})))
@@ -5,6 +5,7 @@
(fn [{:keys [db]} [data]]
(let [{:keys [key-uid encryption-public-key
whisper-private-key]} data
key-uid (str "0x" key-uid)
profile (get-in db [:profile/profiles-overview key-uid])]
{:db
(-> db
@@ -1,132 +0,0 @@
(ns status-im.contexts.keycard.migrate.events
(:require [clojure.string :as string]
[status-im.contexts.keycard.pin.view :as keycard.pin]
[utils.re-frame :as rf]
[utils.security.core :as security]))
(rf/reg-event-fx :keycard/migration.check-empty-card
(fn [{:keys [db]}]
{:fx [[:dispatch
[:keycard/connect
{:key-uid (get-in db [:profile/profile :key-uid])
:on-success
(fn []
;;TODO keys already on the keycard, new flow needs to be implemented
;; https://github.com/status-im/status-mobile/issues/21446
(rf/dispatch [:keycard/disconnect])
(rf/dispatch [:open-modal :screen/keycard.authorise
{:on-success
#(rf/dispatch [:keycard/migration.authorisation-success %])}]))
:on-error
(fn [error]
(if (= error :keycard/error.keycard-blank)
(do
(rf/dispatch [:keycard/disconnect])
(rf/dispatch [:open-modal :screen/keycard.empty]))
(rf/dispatch [:keycard/on-application-info-error error])))}]]]}))
(defn get-application-info-and-continue
[key-uid]
(rf/dispatch [:keycard/get-application-info
{:key-uid key-uid
:on-success #(rf/dispatch [:keycard/migration.continue])
:on-error
(fn [error]
(if (= error :keycard/error.keycard-blank)
(rf/dispatch [:keycard/migration.continue])
(rf/dispatch [:keycard/on-application-info-error error])))}]))
(rf/reg-event-fx :keycard/migration.continue
(fn [{:keys [db]}]
(let [key-uid (get-in db [:profile/profile :key-uid])
{:keys [initialized? has-master-key?]} (get-in db [:keycard :application-info])
{:keys [masked-phrase pin]} (get-in db [:keycard :migration])
on-failure (fn []
(rf/dispatch [:keycard/disconnect])
(rf/dispatch [:navigate-to
:screen/keycard.migrate.fail]))]
(cond
(not initialized?)
{:fx [[:keycard/init-card
{:pin pin
:on-success #(get-application-info-and-continue key-uid)
:on-failure on-failure}]]}
(not has-master-key?)
{:fx [[:effects.keycard/generate-and-load-key
{:mnemonic (security/safe-unmask-data masked-phrase)
:pin pin
:on-success #(get-application-info-and-continue key-uid)
:on-failure on-failure}]]}
:else
{:fx [[:effects.keycard/get-keys
{:pin pin
:on-success #(rf/dispatch [:keycard/migration.convert-to-keycard-profile %])
:on-failure on-failure}]]}))))
(rf/reg-event-fx :keycard/migration.start
(fn [{:keys [db]}]
{:fx [[:dispatch
[:keycard/connect
{:key-uid (get-in db [:profile/profile :key-uid])
:on-success #(rf/dispatch [:keycard/migration.continue])
:on-error
(fn [error]
(if (= error :keycard/error.keycard-blank)
(rf/dispatch [:keycard/migration.continue])
(rf/dispatch [:keycard/on-application-info-error error])))}]]]}))
(rf/reg-event-fx :keycard/migration.get-phrase
(fn [{:keys [db]}]
{:db (assoc-in db [:keycard :migration] nil)
:fx [[:dispatch [:navigate-back]]
(if (string/blank? (get-in db [:profile/profile :mnemonic]))
[:dispatch
[:open-modal :screen/use-recovery-phrase
{:on-success #(rf/dispatch [:keycard/migration.phrase-entered %])}]]
[:dispatch
[:open-modal :screen/backup-recovery-phrase
{:on-success #(rf/dispatch [:keycard/migration.phrase-backed-up %])}]])]}))
(rf/reg-event-fx :keycard/migration.phrase-entered
(fn [{:keys [db]} [{:keys [phrase]}]]
{:db (assoc-in db [:keycard :migration :masked-phrase] (security/mask-data phrase))
:fx [[:dispatch [:navigate-back]]
[:dispatch
[:open-modal :screen/keycard.authorise
{:on-success #(rf/dispatch [:keycard/migration.authorisation-success %])}]]]}))
(rf/reg-event-fx :keycard/migration.phrase-backed-up
(fn [{:keys [db]}]
{:db (assoc-in db
[:keycard :migration :masked-phrase]
(security/mask-data (get-in db [:profile/profile :mnemonic])))
:fx [[:dispatch [:profile.settings/profile-update :mnemonic nil]]
[:dispatch [:navigate-back]]
[:dispatch
[:open-modal :screen/keycard.authorise
{:on-success #(rf/dispatch [:keycard/migration.authorisation-success %])}]]]}))
(rf/reg-event-fx :keycard/migration.authorisation-success
(fn [{:keys [db]} [masked-password]]
(let [{:keys [initialized?]} (get-in db [:keycard :application-info])]
{:db (assoc-in db [:keycard :migration :masked-password] masked-password)
:fx [[:dispatch [:navigate-back]]
(if initialized?
[:dispatch
[:show-bottom-sheet
{:content (fn []
[keycard.pin/auth
{:on-complete #(rf/dispatch [:keycard/migration.pin-created %])}])}]]
[:dispatch
[:open-modal :screen/keycard.pin.create
{:on-complete (fn [new-pin]
(rf/dispatch [:navigate-back])
(rf/dispatch [:keycard/migration.pin-created new-pin]))}]])]})))
(rf/reg-event-fx :keycard/migration.pin-created
(fn [{:keys [db]} [pin]]
{:db (assoc-in db [:keycard :migration :pin] pin)
:fx [[:dispatch [:open-modal :screen/keycard.migrate]]]}))
@@ -1,32 +0,0 @@
(ns status-im.contexts.keycard.migrate.fail.view
(:require [quo.core :as quo]
[react-native.core :as rn]
[status-im.common.resources :as resources]
[status-im.constants :as constants]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn view
[]
(let [profile-name (rf/sub [:profile/name])
profile-picture (rf/sub [:profile/image])
customization-color (rf/sub [:profile/customization-color])]
[:<>
[quo/page-top
{:title (i18n/label :t/failed-to-migrate-key-pair)
:description :context-tag
:context-tag {:full-name profile-name
:profile-picture profile-picture
:customization-color customization-color}
:container-style {:margin-top constants/page-nav-height}}]
[rn/view {:style {:flex 1 :align-items :center :justify-content :center}}
[rn/image
{:resize-mode :contain
:source (resources/get-image :keycard-migration-failed)}]]
[quo/divider-label (i18n/label :t/what-you-can-do)]
[quo/markdown-list {:description (i18n/label :t/log-out-remove-profile)}]
[quo/markdown-list {:description (i18n/label :t/recover-status-profile)}]
[quo/bottom-actions
{:actions :one-action
:button-one-label (i18n/label :t/log-out-remove)
:button-one-props {:on-press #(rf/dispatch [:logout])}}]]))
@@ -1,31 +0,0 @@
(ns status-im.contexts.keycard.migrate.re-encrypting.events
(:require [clojure.string :as string]
[utils.re-frame :as rf]
[utils.security.core :as security]))
(rf/reg-event-fx :keycard/migration.convert-to-keycard-profile
(fn [{:keys [db]} [{:keys [key-uid instance-uid encryption-public-key]}]]
(let [{:keys [masked-password]} (get-in db [:keycard :migration])
{:keys [pairing paired-on]} (get-in db [:keycard :pairings instance-uid])
{:keys [kdfIterations]} (:profile/profile db)]
{:fx [[:dispatch [:keycard/disconnect]]
[:dispatch [:navigate-back]]
[:dispatch [:open-modal :screen/keycard.re-encrypting]]
[:effects.profile/convert-to-keycard-profile
{:profile
{:key-uid key-uid
:keycard-pairing pairing
:kdfIterations kdfIterations}
:settings
{:keycard-instance-uid instance-uid
:keycard-paired-on paired-on
:keycard-pairing pairing}
:password
(security/safe-unmask-data masked-password)
:new-password
encryption-public-key
:callback
(fn [{:keys [error]}]
(if (string/blank? error)
(rf/dispatch [:navigate-to :screen/keycard.migrate.success])
(rf/dispatch [:navigate-to :screen/keycard.migrate.fail])))}]]})))
@@ -1,27 +0,0 @@
(ns status-im.contexts.keycard.migrate.re-encrypting.view
(:require [quo.core :as quo]
[react-native.core :as rn]
[status-im.common.resources :as resources]
[status-im.constants :as constants]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn view
[]
(let [profile-name (rf/sub [:profile/name])
profile-picture (rf/sub [:profile/image])
customization-color (rf/sub [:profile/customization-color])]
[:<>
[quo/page-top
{:title (i18n/label :t/re-encrypting-data)
:description :context-tag
:context-tag {:full-name profile-name
:profile-picture profile-picture
:customization-color customization-color}
:container-style {:margin-top constants/page-nav-height}}]
[quo/text {:style {:padding-horizontal 20}}
(i18n/label :t/do-not-quit)]
[rn/view {:style {:flex 1 :align-items :center :justify-content :center}}
[rn/image
{:resize-mode :contain
:source (resources/get-image :keycard-migration)}]]]))
@@ -1,31 +0,0 @@
(ns status-im.contexts.keycard.migrate.success.view
(:require [quo.core :as quo]
[react-native.core :as rn]
[status-im.common.resources :as resources]
[status-im.constants :as constants]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn view
[]
(let [profile-name (rf/sub [:profile/name])
profile-picture (rf/sub [:profile/image])
customization-color (rf/sub [:profile/customization-color])]
[:<>
[quo/page-top
{:title (i18n/label :t/key-pair-migrated-successfully)
:description :context-tag
:context-tag {:full-name profile-name
:profile-picture profile-picture
:customization-color customization-color}
:container-style {:margin-top constants/page-nav-height}}]
[quo/text {:style {:padding-horizontal 20}}
(i18n/label :t/use-keycard-for-status)]
[rn/view {:style {:flex 1 :align-items :center :justify-content :center}}
[rn/image
{:resize-mode :contain
:source (resources/get-image :keycard-migration-succeeded)}]]
[quo/button
{:on-press #(rf/dispatch [:logout])
:container-style {:margin-horizontal 20}}
(i18n/label :t/logout)]]))
@@ -1,34 +0,0 @@
(ns status-im.contexts.keycard.migrate.view
(:require [quo.core :as quo]
[react-native.core :as rn]
[status-im.common.events-helper :as events-helper]
[status-im.common.resources :as resources]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn view
[]
(let [profile-name (rf/sub [:profile/name])
profile-picture (rf/sub [:profile/image])
customization-color (rf/sub [:profile/customization-color])]
[:<>
[quo/page-nav
{:icon-name :i/close
:on-press events-helper/navigate-back}]
[quo/page-top
{:title (i18n/label :t/ready-to-migrate-key-pair)
:description :context-tag
:context-tag {:full-name profile-name
:profile-picture profile-picture
:customization-color customization-color}}]
[rn/view {:style {:flex 1 :align-items :center :justify-content :center}}
[rn/image
{:resize-mode :contain
:source (resources/get-image :keycard-migration)}]]
[quo/divider-label (i18n/label :t/tips-scan-keycard)]
[quo/markdown-list {:description (i18n/label :t/remove-phone-case)}]
[quo/markdown-list {:description (i18n/label :t/keep-card-steady)}]
[quo/bottom-actions
{:actions :one-action
:button-one-label (i18n/label :t/scan-keycard)
:button-one-props {:on-press #(rf/dispatch [:keycard/migration.start])}}]]))
@@ -1,11 +0,0 @@
(ns status-im.contexts.keycard.nfc.effects
(:require [keycard.keycard :as keycard]
[utils.re-frame :as rf]))
(rf/reg-fx :effects.keycard/check-nfc-enabled
(fn []
(keycard/check-nfc-enabled
{:on-success #(rf/dispatch [:keycard/on-check-nfc-enabled-success %])})))
(rf/reg-fx :effects.keycard.ios/start-nfc keycard/start-nfc)
(rf/reg-fx :effects.keycard.ios/stop-nfc keycard/stop-nfc)
@@ -1,27 +0,0 @@
(ns status-im.contexts.keycard.nfc.events
(:require [utils.re-frame :as rf]))
(rf/reg-event-fx :keycard.ios/start-nfc
(fn [_]
{:fx [[:effects.keycard.ios/start-nfc]]}))
(rf/reg-event-fx :keycard.ios/on-start-nfc-success
(fn [{:keys [db]} [{:keys [on-cancel-event-vector]}]]
{:db (assoc-in db [:keycard :on-nfc-cancelled-event-vector] on-cancel-event-vector)}))
(rf/reg-event-fx :keycard.ios/on-nfc-timeout
(fn [{:keys [db]} _]
{:db (assoc-in db [:keycard :card-connected?] false)
:fx [[:dispatch-later [{:ms 500 :dispatch [:keycard.ios/start-nfc]}]]]}))
(rf/reg-event-fx :keycard/on-check-nfc-enabled-success
(fn [{:keys [db]} [nfc-enabled?]]
{:db (assoc-in db [:keycard :nfc-enabled?] nfc-enabled?)}))
(rf/reg-event-fx :keycard.ios/on-nfc-user-cancelled
(fn [{:keys [db]}]
{:db (-> db
(assoc-in [:keycard :pin :status] nil)
(assoc-in [:keycard :on-nfc-cancelled-event-vector] nil))
:fx [(when-let [on-nfc-cancelled-event-vector (get-in db [:keycard :on-nfc-cancelled-event-vector])]
[:dispatch on-nfc-cancelled-event-vector])]}))
@@ -1,4 +1,4 @@
(ns status-im.contexts.keycard.nfc.sheets.events
(ns status-im.contexts.keycard.nfc-sheet.events
(:require [re-frame.core :as rf]
[react-native.platform :as platform]
[taoensso.timbre :as log]))
@@ -1,4 +1,4 @@
(ns status-im.contexts.keycard.nfc.sheets.view
(ns status-im.contexts.keycard.nfc-sheet.view
(:require [quo.foundations.colors :as colors]
quo.theme
[react-native.core :as rn]
@@ -1,24 +1,28 @@
(ns status-im.contexts.keycard.not-keycard.view
(:require [quo.core :as quo]
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[status-im.common.events-helper :as events-helper]
[status-im.common.resources :as resources]
[utils.i18n :as i18n]))
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn view
[]
[:<>
[quo/page-nav
{:icon-name :i/close
:on-press events-helper/navigate-back}]
[quo/page-top
{:title (i18n/label :t/oops-not-keycard)
:description :text
:description-text (i18n/label :t/make-sure-keycard)}]
[rn/view {:style {:flex 1 :align-items :center :justify-content :center}}
[rn/image
{:resize-mode :contain
:source (resources/get-image :not-keycard)}]]
[rn/view {:padding-horizontal 20}
[quo/button {:on-press events-helper/navigate-back}
(i18n/label :t/try-again)]]])
(let [{:keys [top bottom]} (safe-area/get-insets)]
[quo/overlay
{:type :shell
:container-style {:padding-top top
:padding-bottom bottom}}
[quo/page-nav
{:key :header
:background :blur
:icon-name :i/arrow-left
:on-press events-helper/navigate-back}]
[quo/page-top
{:title (i18n/label :t/oops-not-keycard)
:description :text
:description-text (i18n/label :t/make-sure-keycard)}]
[rn/view {:flex 1}]
[rn/view {:padding-horizontal 20}
[quo/button {:on-press #(rf/dispatch [:keycard/connect])}
(i18n/label :t/try-again)]]]))
@@ -2,15 +2,12 @@
(:require [clojure.string :as string]
[quo.core :as quo]
[react-native.core :as rn]
[status-im.common.events-helper :as events-helper]
[status-im.constants :as constants]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
[utils.i18n :as i18n]))
(defn view
[]
(let [{:keys [on-complete]} (rf/sub [:get-screen-params])
[pin set-pin] (rn/use-state "")
[{:keys [on-complete]}]
(let [[pin set-pin] (rn/use-state "")
[first-pin set-first-pin] (rn/use-state "")
[error? set-error] (rn/use-state false)
[stage set-stage] (rn/use-state :create)
@@ -41,15 +38,10 @@
(set-stage :repeat)))))))
[pin stage first-pin])]
[rn/view {:style {:padding-bottom 12 :flex 1}}
[quo/page-nav
{:icon-name :i/close
:on-press events-helper/navigate-back}]
[quo/page-top
{:title (if (= :create stage)
(i18n/label :t/create-keycard-pin)
(i18n/label :t/repeat-keycard-pin))
:description :text
:description-text "Youll need this PIN to login and sign transactions"}]
[quo/drawer-top
{:title (if (= :create stage)
(i18n/label :t/create-keycard-pin)
(i18n/label :t/repeat-keycard-pin))}]
[rn/view {:style {:flex 1 :justify-content :center :align-items :center :padding-vertical 34}}
[quo/pin-input
{:blur? false
@@ -23,7 +23,3 @@
(assoc-in [:keycard :pin :status] nil))
:fx [(when (and on-complete (= (dec max-numbers) (count pin)))
[:effects.keycard.pin/dispatch-on-complete [on-complete new-pin]])]}))))
(rf/reg-event-fx :keycard.pin/clear
(fn [{:keys [db]}]
{:db (assoc-in db [:keycard :pin] nil)}))
+5 -9
View File
@@ -6,12 +6,10 @@
[utils.re-frame :as rf]))
(defn auth
[{:keys [on-complete error]}]
(let [{:keys [error? error-message]} error
{:keys [text status]} (rf/sub [:keycard/pin])
pin-retry-counter (rf/sub [:keycard/pin-retry-counter])
error? (or error? (= status :error))]
(rn/use-unmount #(rf/dispatch [:keycard.pin/clear]))
[{:keys [on-complete]}]
(let [{:keys [text status]} (rf/sub [:keycard/pin])
pin-retry-counter (rf/sub [:keycard/pin-retry-counter])
error? (= status :error)]
[rn/view {:padding-bottom 12 :flex 1}
[rn/view {:flex 1 :justify-content :center :align-items :center :padding 34}
[quo/pin-input
@@ -20,9 +18,7 @@
:number-of-filled-pins (count text)
:error? error?
:info (when error?
(if error-message
error-message
(i18n/label :t/pin-retries-left {:number pin-retry-counter})))}]]
(i18n/label :t/pin-retries-left {:number pin-retry-counter}))}]]
[quo/numbered-keyboard
{:delete-key? true
:on-delete #(rf/dispatch [:keycard.pin/delete-pressed])
@@ -1,14 +1,15 @@
(ns status-im.contexts.keycard.migrate.sheets.view
(ns status-im.contexts.keycard.sheets.migrate.view
(:require [quo.core :as quo]
[react-native.core :as rn]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn view
[{:keys [on-continue]}]
(let [profile-name (rf/sub [:profile/name])
profile-picture (rf/sub [:profile/image])
customization-color (rf/sub [:profile/customization-color])]
[]
(let [profile-name (rf/sub [:profile/name])
profile-picture (rf/sub [:profile/image])
customization-color (rf/sub [:profile/customization-color])
recovery-phrase-backed-up? (rf/sub [:profile/recovery-phrase-backed-up?])]
[:<>
[quo/drawer-top
{:type :context-tag
@@ -27,7 +28,11 @@
[quo/bottom-actions
{:actions :two-actions
:button-one-label (i18n/label :t/continue)
:button-one-props {:on-press on-continue}
:button-one-props {:on-press #(if recovery-phrase-backed-up?
(rf/dispatch [:open-modal :screen/use-recovery-phrase
{:on-success (fn [])}])
(rf/dispatch [:open-modal :screen/backup-recovery-phrase
{:on-success (fn [])}]))}
:button-two-label (i18n/label :t/cancel)
:button-two-props {:type :grey
:on-press #(rf/dispatch [:hide-bottom-sheet])}}]]))
+13 -17
View File
@@ -8,10 +8,6 @@
:s (subs signature 64 128)
:v (str (js/parseInt (subs signature 128 130) 16))}})
(rf/reg-event-fx :keycard/sign
(fn [_ [data]]
{:effects.keycard/sign data}))
(rf/reg-event-fx :keycard/sign-hash
(fn [{:keys [db]} [pin-text tx-hash]]
(let [current-address (get-in db [:wallet :current-viewing-account-address])
@@ -19,16 +15,16 @@
key-uid (get-in db [:profile/profile :key-uid])]
{:fx [[:dispatch
[:keycard/connect
{:key-uid key-uid
:on-success
(fn []
(rf/dispatch
[:keycard/sign
{:pin pin-text
:path path
:hash (utils.address/naked-address tx-hash)
:on-success (fn [signature]
(rf/dispatch [:keycard/disconnect])
(rf/dispatch [:wallet/proceed-with-transactions-signatures
(get-signature-map tx-hash signature)]))
:on-failure #(rf/dispatch [:keycard/on-action-with-pin-error %])}]))}]]]})))
{:key-uid key-uid
:on-success-fx [[:effects.keycard/sign
{:pin pin-text
:path path
:hash (utils.address/naked-address tx-hash)
:on-success
#(do
(rf/dispatch [:keycard/disconnect])
(rf/dispatch
[:wallet/proceed-with-transactions-signatures
(get-signature-map tx-hash %)]))
:on-failure #(rf/dispatch [:keycard/on-action-with-pin-error
%])}]]}]]]})))
+25 -49
View File
@@ -1,7 +1,5 @@
(ns status-im.contexts.keycard.utils
(:require [clojure.string :as string]
[utils.address :as address]
[utils.transforms :as transforms]))
(:require [taoensso.timbre :as log]))
(def pin-mismatch-error #"Unexpected error SW, 0x63C(\d+)|wrongPIN\(retryCounter: (\d+)\)")
@@ -18,55 +16,33 @@
(re-matches #".*NFCError:100.*" error)))
(defn validate-application-info
[profile-key-uid
{:keys [key-uid has-master-key? paired? pin-retry-counter puk-retry-counter] :as application-info}]
[profile-key-uid {:keys [key-uid paired? pin-retry-counter puk-retry-counter] :as application-info}]
(let [profile-mismatch? (or (nil? profile-key-uid) (not= profile-key-uid key-uid))]
(log/debug "[keycard]" "login-with-keycard"
"empty application info" (empty? application-info)
"no key-uid" (empty? key-uid)
"profile-mismatch?" profile-mismatch?
"no pairing" paired?)
(cond
(empty? application-info)
:keycard/error.not-keycard
(cond
(empty? application-info)
:keycard/error.not-keycard
(empty? (:key-uid application-info))
:keycard/error.keycard-blank
(not has-master-key?)
:keycard/error.keycard-blank
profile-mismatch?
:keycard/error.keycard-wrong
(not= profile-key-uid key-uid)
:keycard/error.keycard-wrong-profile
(not paired?)
:keycard/error.keycard-unpaired
(not paired?)
:keycard/error.keycard-unpaired
(and (zero? pin-retry-counter)
(or (nil? puk-retry-counter)
(pos? puk-retry-counter)))
:keycard/error.keycard-frozen
(and (zero? pin-retry-counter)
(or (nil? puk-retry-counter)
(pos? puk-retry-counter)))
:keycard/error.keycard-frozen
(zero? puk-retry-counter)
:keycard/error.keycard-locked
(zero? puk-retry-counter)
:keycard/error.keycard-locked
:else
nil))
(defn- error-object->map
[^js object]
{:code (.-code object)
:error (.-message object)})
(defn normalize-key-uid
[{:keys [key-uid] :as data}]
(if (string/blank? key-uid)
data
(update data :key-uid address/normalized-hex)))
(defn get-on-success
[{:keys [on-success]}]
#(when on-success (on-success (normalize-key-uid (transforms/js->clj %)))))
(defn get-on-failure
[{:keys [on-failure]}]
#(when on-failure (on-failure (error-object->map %))))
(defn wrap-handlers
[args]
(assoc
args
:on-success (get-on-success args)
:on-failure (get-on-failure args)))
:else
nil)))
@@ -25,12 +25,6 @@
{:key :i/copy}
{:key nil
:value "None"}]}
{:type :select
:key :title-icon
:options [{:key :i/chevron-right}
{:key :i/copy}
{:key nil
:value "None"}]}
{:type :select
:key :right-content
:options [{:key nil
@@ -22,10 +22,13 @@
:raribleMainnetAPIKey config/RARIBLE_MAINNET_API_KEY
:raribleTestnetAPIKey config/RARIBLE_TESTNET_API_KEY
:alchemyEthereumMainnetToken config/ALCHEMY_ETHEREUM_MAINNET_TOKEN
:alchemyEthereumGoerliToken config/ALCHEMY_ETHEREUM_GOERLI_TOKEN
:alchemyEthereumSepoliaToken config/ALCHEMY_ETHEREUM_SEPOLIA_TOKEN
:alchemyOptimismMainnetToken config/ALCHEMY_OPTIMISM_MAINNET_TOKEN
:alchemyOptimismGoerliToken config/ALCHEMY_OPTIMISM_GOERLI_TOKEN
:alchemyOptimismSepoliaToken config/ALCHEMY_OPTIMISM_SEPOLIA_TOKEN
:alchemyArbitrumMainnetToken config/ALCHEMY_ARBITRUM_MAINNET_TOKEN
:alchemyArbitrumGoerliToken config/ALCHEMY_ARBITRUM_GOERLI_TOKEN
:alchemyArbitrumSepoliaToken config/ALCHEMY_ARBITRUM_SEPOLIA_TOKEN})
(defn create
@@ -13,7 +13,3 @@
(rf/reg-fx :effects.profile/enable-local-notifications
(fn []
(native-module/start-local-notifications)))
(rf/reg-fx :effects.profile/convert-to-keycard-profile
(fn [{:keys [profile settings password new-password callback]}]
(native-module/convert-to-keycard-profile profile settings password new-password callback)))
@@ -1,6 +1,5 @@
(ns status-im.contexts.profile.profiles.view
(:require
[clojure.string :as string]
[native-module.core :as native-module]
[quo.core :as quo]
[react-native.core :as rn]
@@ -183,7 +182,7 @@
:render-fn profile-card}]]))
(defn password-input
[processing error]
[]
(let [auth-method (rf/sub [:auth-method])
on-press-biometrics (fn []
(rf/dispatch [:biometric/authenticate
@@ -194,31 +193,18 @@
%])}]))]
[standard-authentication/password-input
{:processing processing
:error error
:shell? true
{:shell? true
:blur? true
:on-press-biometrics (when (= auth-method constants/auth-method-biometric) on-press-biometrics)}]))
(defn- get-error-message
[error]
(if (and (some? error)
(or (= error "file is not a database")
(string/starts-with? (string/lower-case error) "failed")))
(i18n/label :t/oops-wrong-password)
error))
(defn login-section
[{:keys [show-profiles]}]
(let [{:keys [key-uid name keycard-pairing
(let [processing (rf/sub [:profile/login-processing])
{:keys [key-uid name keycard-pairing
customization-color]} (rf/sub [:profile/login-profile])
sign-in-enabled? (rf/sub [:sign-in-enabled?])
profile-picture (rf/sub [:profile/login-profiles-picture key-uid])
{:keys [error processing]} (rf/sub [:profile/login])
error-message (rn/use-memo #(get-error-message error) [error])
error {:error? (boolean (seq error-message))
:error-message error-message}
login-profile (rn/use-callback #(rf/dispatch [:profile.login/login]))]
login-multiaccount (rn/use-callback #(rf/dispatch [:profile.login/login]))]
[rn/keyboard-avoiding-view
{:style style/login-container
:keyboard-vertical-offset (- (safe-area/get-bottom))}
@@ -253,20 +239,17 @@
:card-style style/login-profile-card}]
(if keycard-pairing
[keycard.pin/auth
{:error error
:on-complete
(fn [pin]
{:on-complete
(fn [pin-text]
(rf/dispatch
[:keycard/connect
{:key-uid key-uid
:on-success
(fn []
(rf/dispatch
[:keycard/get-keys
{:pin pin
:on-success #(rf/dispatch [:keycard.login/on-get-keys-success %])
:on-failure #(rf/dispatch [:keycard/on-action-with-pin-error %])}]))}]))}]
[password-input processing error])]
{:key-uid key-uid
:on-success-fx [[:effects.keycard/get-keys
{:pin pin-text
:on-success #(rf/dispatch [:keycard.login/on-get-keys-success %])
:on-failure #(rf/dispatch [:keycard/on-action-with-pin-error
%])}]]}]))}]
[password-input])]
(when-not keycard-pairing
[quo/button
{:size 40
@@ -275,7 +258,7 @@
:accessibility-label :login-button
:icon-left :i/unlocked
:disabled? (or (not sign-in-enabled?) processing)
:on-press login-profile
:on-press login-multiaccount
:container-style {:margin-bottom (+ (safe-area/get-bottom) 12)}}
(i18n/label :t/log-in)])]))
@@ -50,6 +50,17 @@
{:on-success on-success}])
:on-cancel nil}]]})))
(rf/reg-event-fx :profile.settings/toggle-goerli-test-network
(fn [{:keys [db]}]
(let [value (get-in db [:profile/profile :is-goerli-enabled?])
on-success #(rf/dispatch [:logout])]
{:fx [[:ui/show-confirmation
{:content (i18n/label :t/goerli-testnet-toggle-confirmation)
:on-accept #(rf/dispatch [:profile.settings/profile-update :is-goerli-enabled?
(not value)
{:on-success on-success}])
:on-cancel nil}]]})))
(rf/reg-event-fx :profile.settings/change-preview-privacy
(fn [_ [private?]]
(let [private?' (boolean private?)]
@@ -4,7 +4,6 @@
[react-native.core :as rn]
[status-im.common.events-helper :as events-helper]
[status-im.common.resources :as resources]
[status-im.constants :as constants]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
@@ -28,11 +27,11 @@
:subtitle (i18n/label :t/secure-wallet-card)
:button-label (i18n/label :t/buy-keycard)
:accessibility-label :get-keycard
:image (resources/get-image :keycard-buy)
:on-press #(rf/dispatch [:browser.ui/open-url constants/get-keycard-url])}]
:image (resources/get-image :generate-keys)
:on-press #()}]
[rn/view {:style {:margin-top 24}}
[quo/text
{:style {:margin-bottom 1
{:style {:margin-bottom 12
:color colors/white-opa-70}
:size :paragraph-2
:weight :medium}
@@ -41,9 +41,10 @@
(for [label points]
^{:key label}
[quo/markdown-list
{:description (i18n/label label)
:blur? true
:type (when lock? :lock)}])]})
{:description (i18n/label label)
:blur? true
:type (when lock? :lock)
:container-style {:padding-top 8}}])]})
(defn- on-privacy-policy-press
[]
@@ -357,7 +357,8 @@
{:chain-id chain-id
:token-id token-id
:contract-address contract-address
:test-networks-enabled? (get-in db [:profile/profile :test-networks-enabled?])})]
:test-networks-enabled? (get-in db [:profile/profile :test-networks-enabled?])
:is-goerli-enabled? (get-in db [:profile/profile :is-goerli-enabled?])})]
{:fx [[:dispatch
[:hide-bottom-sheet]]
[:dispatch-later
@@ -376,4 +377,5 @@
{:chain-id chain-id
:token-id token-id
:contract-address contract-address
:test-networks-enabled? (get-in db [:profile/profile :test-networks-enabled?])})]]]}))
:test-networks-enabled? (get-in db [:profile/profile :test-networks-enabled?])
:is-goerli-enabled? (get-in db [:profile/profile :is-goerli-enabled?])})]]]}))
@@ -33,11 +33,17 @@
(when (> total 1) (str "x" total)))
(defn- get-opensea-network-name
[chain-id test-networks-enabled?]
[chain-id test-networks-enabled? is-goerli-enabled?]
(let [network-kw (network-utils/id->network chain-id)
network-name (name network-kw)
mainnet? (= :mainnet network-kw)]
(cond (and test-networks-enabled? mainnet?)
(cond (and test-networks-enabled? is-goerli-enabled? mainnet?)
(:goerli constants/opensea-url-names)
(and test-networks-enabled? is-goerli-enabled?)
(str network-name "-" (:goerli constants/opensea-url-names))
(and test-networks-enabled? mainnet?)
(:sepolia constants/opensea-url-names)
test-networks-enabled?
@@ -57,10 +63,11 @@
(defn get-opensea-collectible-url
[{:keys [chain-id token-id contract-address
test-networks-enabled?]}]
test-networks-enabled? is-goerli-enabled?]}]
(let [base-link (get-opensea-base-url test-networks-enabled?)
opensea-network-name (get-opensea-network-name chain-id
test-networks-enabled?)]
test-networks-enabled?
is-goerli-enabled?)]
(str base-link "/assets/" opensea-network-name "/" contract-address "/" token-id)))
(defn get-collectible-unique-id
@@ -43,4 +43,26 @@
:contract-address contract-address
:token-id token-id
:test-networks-enabled? true})
"https://testnets.opensea.io/assets/optimism-sepolia/0xC/0xT"))))
"https://testnets.opensea.io/assets/optimism-sepolia/0xC/0xT")))
(testing "get-opensea-collectible-url goerli"
(is (= (utils/get-opensea-collectible-url {:chain-id constants/ethereum-goerli-chain-id
:contract-address contract-address
:token-id token-id
:test-networks-enabled? true
:is-goerli-enabled? true})
"https://testnets.opensea.io/assets/goerli/0xC/0xT")))
(testing "get-opensea-collectible-url goerli arbitrum"
(is (= (utils/get-opensea-collectible-url {:chain-id constants/arbitrum-goerli-chain-id
:contract-address contract-address
:token-id token-id
:test-networks-enabled? true
:is-goerli-enabled? true})
"https://testnets.opensea.io/assets/arbitrum-goerli/0xC/0xT")))
(testing "get-opensea-collectible-url goerli optimism"
(is (= (utils/get-opensea-collectible-url {:chain-id constants/optimism-goerli-chain-id
:contract-address contract-address
:token-id token-id
:test-networks-enabled? true
:is-goerli-enabled? true})
"https://testnets.opensea.io/assets/optimism-goerli/0xC/0xT"))))
@@ -445,7 +445,7 @@
(defn transaction-path
[{:keys [from-address to-address token-id-from token-address token-id-to route data
slippage-percentage eth-transfer?]}]
(let [{:keys [bridge-name amount-in from
(let [{:keys [bridge-name amount-in bonder-fees from
to]} route
tx-data (transaction-data {:from-address from-address
:to-address to-address
@@ -453,7 +453,6 @@
:route route
:data data
:eth-transfer? eth-transfer?})
bonder-fees (-> route :bounder-fees money/to-string)
to-chain-id (:chain-id to)
from-chain-id (:chain-id from)]
(cond-> {:BridgeName bridge-name
@@ -14,6 +14,12 @@
(= chain-id constants/optimism-mainnet-chain-id)
config/optimism-mainnet-chain-explorer-link
(= chain-id constants/ethereum-goerli-chain-id)
config/goerli-chain-explorer-link
(= chain-id constants/optimism-goerli-chain-id)
config/optimism-goerli-chain-explorer-link
(= chain-id constants/ethereum-sepolia-chain-id)
config/sepolia-chain-explorer-link
@@ -10,42 +10,58 @@
(def id->network
{constants/ethereum-mainnet-chain-id constants/mainnet-network-name
constants/ethereum-goerli-chain-id constants/mainnet-network-name
constants/ethereum-sepolia-chain-id constants/mainnet-network-name
constants/optimism-mainnet-chain-id constants/optimism-network-name
constants/optimism-goerli-chain-id constants/optimism-network-name
constants/optimism-sepolia-chain-id constants/optimism-network-name
constants/arbitrum-mainnet-chain-id constants/arbitrum-network-name
constants/arbitrum-goerli-chain-id constants/arbitrum-network-name
constants/arbitrum-sepolia-chain-id constants/arbitrum-network-name})
(defn- get-chain-id
[{:keys [mainnet-chain-id sepolia-chain-id testnet-enabled?]}]
(if testnet-enabled?
[{:keys [mainnet-chain-id sepolia-chain-id goerli-chain-id testnet-enabled? goerli-enabled?]}]
(cond
(and testnet-enabled? goerli-enabled?)
goerli-chain-id
testnet-enabled?
sepolia-chain-id
:else
mainnet-chain-id))
(defn network->chain-id
([db network]
(let [{:keys [test-networks-enabled?]} (:profile/profile db)]
(let [{:keys [test-networks-enabled? is-goerli-enabled?]} (:profile/profile db)]
(network->chain-id {:network network
:testnet-enabled? test-networks-enabled?})))
([{:keys [network testnet-enabled?]}]
:testnet-enabled? test-networks-enabled?
:goerli-enabled? is-goerli-enabled?})))
([{:keys [network testnet-enabled? goerli-enabled?]}]
(condp contains? (keyword network)
#{constants/mainnet-network-name (keyword constants/mainnet-short-name)}
(get-chain-id
{:mainnet-chain-id constants/ethereum-mainnet-chain-id
:sepolia-chain-id constants/ethereum-sepolia-chain-id
:testnet-enabled? testnet-enabled?})
:goerli-chain-id constants/ethereum-goerli-chain-id
:testnet-enabled? testnet-enabled?
:goerli-enabled? goerli-enabled?})
#{constants/optimism-network-name (keyword constants/optimism-short-name)}
(get-chain-id
{:mainnet-chain-id constants/optimism-mainnet-chain-id
:sepolia-chain-id constants/optimism-sepolia-chain-id
:testnet-enabled? testnet-enabled?})
:goerli-chain-id constants/optimism-goerli-chain-id
:testnet-enabled? testnet-enabled?
:goerli-enabled? goerli-enabled?})
#{constants/arbitrum-network-name (keyword constants/arbitrum-short-name)}
(get-chain-id
{:mainnet-chain-id constants/arbitrum-mainnet-chain-id
:sepolia-chain-id constants/arbitrum-sepolia-chain-id
:testnet-enabled? testnet-enabled?}))))
:goerli-chain-id constants/arbitrum-goerli-chain-id
:testnet-enabled? testnet-enabled?
:goerli-enabled? goerli-enabled?}))))
(defn network-list
[{:keys [balances-per-chain]} networks]
@@ -66,13 +82,19 @@
(network-list $ networks)))
(defn get-default-chain-ids-by-mode
[{:keys [test-networks-enabled?]}]
(if test-networks-enabled?
[{:keys [test-networks-enabled? is-goerli-enabled?]}]
(cond
(and test-networks-enabled? is-goerli-enabled?)
constants/goerli-chain-ids
test-networks-enabled?
constants/sepolia-chain-ids
:else
constants/mainnet-chain-ids))
(defn resolve-receiver-networks
[{:keys [prefix testnet-enabled?]}]
[{:keys [prefix testnet-enabled? goerli-enabled?]}]
(let [prefix (if (string/blank? prefix)
constants/default-multichain-address-prefix
prefix)
@@ -82,7 +104,8 @@
(mapv
#(network->chain-id
{:network %
:testnet-enabled? testnet-enabled?})))))
:testnet-enabled? testnet-enabled?
:goerli-enabled? goerli-enabled?})))))
(def network->short-name
{constants/mainnet-network-name constants/mainnet-short-name
@@ -162,13 +185,16 @@
[chain-id]
(as-> chain-id $
(condp contains? $
#{constants/ethereum-mainnet-chain-id constants/ethereum-sepolia-chain-id}
#{constants/ethereum-mainnet-chain-id constants/ethereum-goerli-chain-id
constants/ethereum-sepolia-chain-id}
mainnet-network-details
#{constants/arbitrum-mainnet-chain-id constants/arbitrum-sepolia-chain-id}
#{constants/arbitrum-mainnet-chain-id constants/arbitrum-goerli-chain-id
constants/arbitrum-sepolia-chain-id}
arbitrum-network-details
#{constants/optimism-mainnet-chain-id constants/optimism-sepolia-chain-id}
#{constants/optimism-mainnet-chain-id constants/optimism-goerli-chain-id
constants/optimism-sepolia-chain-id}
optimism-network-details
nil)
@@ -6,17 +6,19 @@
(deftest network->chain-id-test
(testing "network->chain-id function"
(is (= (utils/network->chain-id {:network :mainnet :testnet-enabled? false})
(is (= (utils/network->chain-id {:network :mainnet :testnet-enabled? false :goerli-enabled? false})
constants/ethereum-mainnet-chain-id))
(is (= (utils/network->chain-id {:network :eth :testnet-enabled? true})
(is (= (utils/network->chain-id {:network :eth :testnet-enabled? true :goerli-enabled? false})
constants/ethereum-sepolia-chain-id))
(is (= (utils/network->chain-id {:network "optimism" :testnet-enabled? true})
(is (= (utils/network->chain-id {:network "optimism" :testnet-enabled? true :goerli-enabled? false})
constants/optimism-sepolia-chain-id))
(is (= (utils/network->chain-id {:network "oeth" :testnet-enabled? false})
(is (= (utils/network->chain-id {:network "oeth" :testnet-enabled? false :goerli-enabled? true})
constants/optimism-mainnet-chain-id))
(is (= (utils/network->chain-id {:network :arb1 :testnet-enabled? false})
(is (= (utils/network->chain-id {:network :oeth :testnet-enabled? true :goerli-enabled? true})
constants/optimism-goerli-chain-id))
(is (= (utils/network->chain-id {:network :arb1 :testnet-enabled? false :goerli-enabled? false})
constants/arbitrum-mainnet-chain-id))
(is (= (utils/network->chain-id {:network :arbitrum :testnet-enabled? true})
(is (= (utils/network->chain-id {:network :arbitrum :testnet-enabled? true :goerli-enabled? false})
constants/arbitrum-sepolia-chain-id))))
(deftest short-names->network-preference-prefix-test
@@ -233,12 +233,13 @@
(defn new->old-route-path
[new-path]
(let [bonder-fees (-> new-path :tx-bonder-fees money/bignumber)
token-fees (-> new-path :tx-token-fees money/bignumber)]
(let [bonder-fees (:tx-bonder-fees new-path)
token-fees (+ (money/wei->ether bonder-fees)
(money/wei->ether (:tx-token-fees new-path)))]
{:from (:from-chain new-path)
:amount-in-locked (:amount-in-locked new-path)
:amount-in (:amount-in new-path)
:max-amount-in (:max-amount-in new-path)
:max-amount-in "0x0"
:gas-fees {:gas-price "0"
:base-fee (send-utils/convert-to-gwei (:tx-base-fee
new-path)
+16 -4
View File
@@ -387,14 +387,23 @@
(rf/dispatch [:wallet/set-ens-address nil ens])
(on-error-fn))))}]]]})))
(defn- resolved-address->prefixed-address
[address networks]
(let [prefixes (->> networks
(map network-utils/network->short-name)
network-utils/short-names->network-preference-prefix)]
(str prefixes (eip55/address->checksum address))))
(rf/reg-event-fx
:wallet/set-ens-address
(fn [{:keys [db]} [result ens]]
(let [suggestion (if result
(let [networks [constants/ethereum-network-name constants/optimism-network-name]
suggestion (if result
[{:type item-types/address
:ens ens
:address (eip55/address->checksum result)
:full-address (eip55/address->checksum result)}]
:networks networks
:full-address (resolved-address->prefixed-address result networks)}]
[])]
{:db (-> db
(assoc-in [:wallet :ui :search-address :local-suggestions] suggestion)
@@ -502,8 +511,10 @@
(for [[k v] chains :when (= v "down")] k))
keys)
test-networks-enabled? (get-in db [:profile/profile :test-networks-enabled?])
is-goerli-enabled? (get-in db [:profile/profile :is-goerli-enabled?])
chain-ids-by-mode (network-utils/get-default-chain-ids-by-mode
{:test-networks-enabled? test-networks-enabled?})
{:test-networks-enabled? test-networks-enabled?
:is-goerli-enabled? is-goerli-enabled?})
chains-filtered-by-mode (remove #(not (contains? chain-ids-by-mode %)) down-chain-ids)
chains-down? (and (network.data-store/online? db) (seq chains-filtered-by-mode))
chain-names (when chains-down?
@@ -516,7 +527,8 @@
(when (seq down-chain-ids)
(log/info "[wallet] Chain(s) down: " down-chain-ids)
(log/info "[wallet] Chain name(s) down: " chain-names)
(log/info "[wallet] Test network enabled: " (boolean test-networks-enabled?)))
(log/info "[wallet] Test network enabled: " (boolean test-networks-enabled?))
(log/info "[wallet] Goerli network enabled: " (boolean is-goerli-enabled?)))
;; NOTE <shivekkhurana>: We used to show an error toast, but disabled it because the down
;; signal is sent randomly. Needs to be investigated and enabled again !
@@ -152,9 +152,11 @@
(fn [{:keys [db]} [{:keys [address recipient stack-id start-flow?]}]]
(let [[prefix to-address] (utils/split-prefix-and-address address)
testnet-enabled? (get-in db [:profile/profile :test-networks-enabled?])
goerli-enabled? (get-in db [:profile/profile :is-goerli-enabled?])
receiver-networks (network-utils/resolve-receiver-networks
{:prefix prefix
:testnet-enabled? testnet-enabled?})
:testnet-enabled? testnet-enabled?
:goerli-enabled? goerli-enabled?})
collectible-tx? (send-utils/tx-type-collectible?
(-> db :wallet :ui :send :tx-type))
collectible (when collectible-tx?
@@ -357,9 +357,11 @@
(testing "testing when collectible balance is more than 1"
(let [collectible (collectible-with-balance 2)
testnet-enabled? false
goerli-enabled? false
receiver-networks (network-utils/resolve-receiver-networks
{:prefix prefix
:testnet-enabled? testnet-enabled?})
:testnet-enabled? testnet-enabled?
:goerli-enabled? goerli-enabled?})
expected-result {:db {:wallet {:ui {:send {:other-props :value
:recipient recipient
:to-address to-address
@@ -369,7 +371,8 @@
:receiver-networks receiver-networks
:tx-type tx-type
:collectible collectible}}}
:profile/profile {:test-networks-enabled? false}}
:profile/profile {:test-networks-enabled? false
:is-goerli-enabled? false}}
:fx [nil
[:dispatch
[:wallet/wizard-navigate-forward
@@ -380,7 +383,8 @@
{:wallet {:ui {:send {:other-props :value
:tx-type tx-type
:collectible collectible}}}
:profile/profile {:test-networks-enabled? testnet-enabled?}})
:profile/profile {:test-networks-enabled? testnet-enabled?
:is-goerli-enabled? goerli-enabled?}})
(is (match? expected-result
(dispatch [event-id
{:address address
@@ -390,9 +394,11 @@
(testing "testing when collectible balance is 1"
(let [collectible (collectible-with-balance 1)
testnet-enabled? false
goerli-enabled? false
receiver-networks (network-utils/resolve-receiver-networks
{:prefix prefix
:testnet-enabled? testnet-enabled?})
:testnet-enabled? testnet-enabled?
:goerli-enabled? goerli-enabled?})
expected-result {:db {:wallet {:ui {:send {:other-props :value
:recipient recipient
:to-address to-address
@@ -402,7 +408,8 @@
:receiver-networks receiver-networks
:tx-type tx-type
:collectible collectible}}}
:profile/profile {:test-networks-enabled? false}}
:profile/profile {:test-networks-enabled? false
:is-goerli-enabled? false}}
:fx [[:dispatch [:wallet/start-get-suggested-routes {:amount 1}]]
[:dispatch
[:wallet/wizard-navigate-forward
@@ -413,7 +420,8 @@
{:wallet {:ui {:send {:other-props :value
:tx-type tx-type
:collectible collectible}}}
:profile/profile {:test-networks-enabled? testnet-enabled?}})
:profile/profile {:test-networks-enabled? testnet-enabled?
:is-goerli-enabled? goerli-enabled?}})
(is (match? expected-result
(dispatch [event-id
{:address address
@@ -104,19 +104,16 @@
:wallet/wallet-send-tx-type :tx/send
:wallet/wallet-send-fee-fiat-formatted "$5,00"
:wallet/sending-collectible? false
:wallet/send-total-amount-formatted "250 ETH"
:wallet/total-amount (money/bignumber "250")
:wallet/bridge-to-network-details nil
:wallet/send-amount-fixed ""
:wallet/send-display-token-decimals 5})
:wallet/total-amount (money/bignumber "250")})
(h/describe "Send > input amount screen"
(h/setup-restorable-re-frame)
(h/test "Default render"
(h/setup-subs (assoc sub-mocks :wallet/send-display-token-decimals 2))
(h/setup-subs sub-mocks)
(h/render-with-theme-provider [input-amount/view
{:limit-crypto (money/bignumber 250)
{:crypto-decimals 2
:limit-crypto (money/bignumber 250)
:initial-crypto-currency? false}])
(h/is-truthy (h/get-by-text "0"))
(h/is-truthy (h/get-by-text "USD"))
@@ -125,11 +122,12 @@
(h/is-disabled (h/get-by-label-text :button-one)))
(h/test "Fill token input and confirm"
(h/setup-subs (assoc sub-mocks :wallet/send-display-token-decimals 10))
(h/setup-subs sub-mocks)
(let [on-confirm (h/mock-fn)]
(h/render-with-theme-provider [input-amount/view
{:limit-crypto (money/bignumber 1000)
{:crypto-decimals 10
:limit-crypto (money/bignumber 1000)
:on-confirm on-confirm
:initial-crypto-currency? true}])
@@ -149,9 +147,10 @@
(h/was-called on-confirm)))))))
(h/test "Try to fill more than limit"
(h/setup-subs (assoc sub-mocks :wallet/send-display-token-decimals 1))
(h/setup-subs sub-mocks)
(h/render-with-theme-provider [input-amount/view
{:limit-crypto (money/bignumber 1)}])
{:crypto-decimals 1
:limit-crypto (money/bignumber 1)}])
(h/fire-event :press (h/query-by-label-text :keyboard-key-2))
(h/fire-event :press (h/query-by-label-text :keyboard-key-9))
@@ -160,10 +159,11 @@
(h/is-truthy (h/get-by-label-text :container-error)))
(h/test "Switch from crypto to fiat and check limit"
(h/setup-subs (assoc sub-mocks :wallet/send-display-token-decimals 1))
(h/setup-subs sub-mocks)
(h/render-with-theme-provider [input-amount/view
{:limit-crypto (money/bignumber 10)
:on-confirm #()}])
{:crypto-decimals 1
:limit-crypto (money/bignumber 10)
:on-confirm #()}])
(h/fire-event :press (h/query-by-label-text :keyboard-key-9))
(h/fire-event :press (h/query-by-label-text :keyboard-key-9))
@@ -1,67 +0,0 @@
(ns status-im.contexts.wallet.send.input-amount.estimated-fees
(:require
[quo.core :as quo]
[quo.theme]
[react-native.core :as rn]
[status-im.common.not-implemented :as not-implemented]
[status-im.contexts.wallet.send.input-amount.style :as style]
[status-im.feature-flags :as ff]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- bonder-fee-info-sheet
[]
[:<>
[quo/drawer-top
{:title (i18n/label :t/understanding-bonder-fees-title)}]
[rn/view {:style {:padding-horizontal 20}}
[quo/text
{:weight :regular
:size :paragraph-2}
(i18n/label :t/understanding-bonder-fees-description)]]])
(defn- show-bonder-fee-info
[]
(rf/dispatch
[:show-bottom-sheet {:content bonder-fee-info-sheet}]))
(defn- received-amount
[{:keys [loading-routes?]}]
(let [amount (rf/sub [:wallet/send-total-amount-formatted])
tx-type (rf/sub [:wallet/wallet-send-tx-type])
{:keys [full-name]} (rf/sub [:wallet/bridge-to-network-details])]
[quo/data-item
(cond-> {:container-style style/amount-data-item
:status (if loading-routes? :loading :default)
:size :small
:title (i18n/label :t/recipient-gets)
:subtitle amount}
(= tx-type :tx/bridge)
(assoc
:title-icon :i/info
:title (i18n/label :t/bridged-to
{:network full-name})
:on-press show-bonder-fee-info))]))
(defn view
[{:keys [loading-routes? fees]}]
[rn/view {:style style/estimated-fees-container}
(when (ff/enabled? ::ff/wallet.advanced-sending)
[rn/view {:style style/estimated-fees-content-container}
[quo/button
{:icon-only? true
:type :outline
:size 32
:inner-style {:opacity 1}
:accessibility-label :advanced-button
:disabled? loading-routes?
:on-press not-implemented/alert}
:i/advanced]])
[quo/data-item
{:container-style style/fees-data-item
:status (if loading-routes? :loading :default)
:size :small
:title (i18n/label :t/fees)
:subtitle fees}]
[received-amount {:loading-routes? loading-routes?}]])
@@ -11,17 +11,45 @@
[status-im.contexts.wallet.common.account-switcher.view :as account-switcher]
[status-im.contexts.wallet.common.asset-list.view :as asset-list]
[status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.send.input-amount.estimated-fees :as estimated-fees]
[status-im.contexts.wallet.send.input-amount.style :as style]
[status-im.contexts.wallet.send.routes.view :as routes]
[status-im.contexts.wallet.sheets.buy-token.view :as buy-token]
[status-im.contexts.wallet.sheets.unpreferred-networks-alert.view :as unpreferred-networks-alert]
[status-im.feature-flags :as ff]
[status-im.setup.hot-reload :as hot-reload]
[utils.debounce :as debounce]
[utils.i18n :as i18n]
[utils.money :as money]
[utils.number :as number]
[utils.re-frame :as rf]))
(defn- estimated-fees
[{:keys [loading-routes? fees amount]}]
[rn/view {:style style/estimated-fees-container}
(when (ff/enabled? ::ff/wallet.advanced-sending)
[rn/view {:style style/estimated-fees-content-container}
[quo/button
{:icon-only? true
:type :outline
:size 32
:inner-style {:opacity 1}
:accessibility-label :advanced-button
:disabled? loading-routes?
:on-press #(js/alert "Not implemented yet")}
:i/advanced]])
[quo/data-item
{:container-style style/fees-data-item
:status (if loading-routes? :loading :default)
:size :small
:title (i18n/label :t/fees)
:subtitle fees}]
[quo/data-item
{:container-style style/amount-data-item
:status (if loading-routes? :loading :default)
:size :small
:title (i18n/label :t/recipient-gets)
:subtitle amount}]])
(defn- every-network-value-is-zero?
[sender-network-values]
(every? (fn [{:keys [total-amount]}]
@@ -137,6 +165,7 @@
;; for component tests only
[{default-on-confirm :on-confirm
default-limit-crypto :limit-crypto
default-crypto-decimals :crypto-decimals
on-navigate-back :on-navigate-back
button-one-label :button-one-label
button-one-props :button-one-props
@@ -170,13 +199,17 @@
:market-values-per-currency
currency
:price)
token-decimals (rf/sub [:wallet/send-display-token-decimals])
token-decimals (-> token
utils/token-usd-price
utils/one-cent-value
utils/calc-max-crypto-decimals)
[input-state set-input-state] (rn/use-state controlled-input/init-state)
clear-input! #(set-input-state controlled-input/delete-all)
currency-symbol (rf/sub [:profile/currency-symbol])
loading-routes? (rf/sub [:wallet/wallet-send-loading-suggested-routes?])
route (rf/sub [:wallet/wallet-send-route])
on-confirm (or default-on-confirm handle-on-confirm)
crypto-decimals (or token-decimals default-crypto-decimals)
max-limit (if crypto-currency?
(utils/cut-crypto-decimals-to-fit-usd-cents
token-balance
@@ -188,8 +221,15 @@
(controlled-input/input-error input-state)))
amount-in-crypto (if crypto-currency?
input-value
(rf/sub [:wallet/send-amount-fixed
(/ input-value conversion-rate)]))
(number/remove-trailing-zeroes
(.toFixed (/ input-value conversion-rate)
crypto-decimals)))
total-amount-receiver (rf/sub [:wallet/total-amount true])
amount-text (str (number/remove-trailing-zeroes
(.toFixed total-amount-receiver
(min token-decimals 6)))
" "
token-symbol)
show-select-asset-sheet #(rf/dispatch
[:show-bottom-sheet
{:content (fn []
@@ -342,9 +382,10 @@
[not-enough-asset])
(when (or (and (not no-routes-found?) (or loading-routes? route))
not-enough-asset?)
[estimated-fees/view
[estimated-fees
{:loading-routes? loading-routes?
:fees fee-formatted}])
:fees fee-formatted
:amount amount-text}])
(when show-no-routes?
[no-routes-found])
[quo/bottom-actions
@@ -376,7 +417,7 @@
:delete-key? true
:on-press (fn [c]
(let [new-text (str input-value c)
max-decimals (if crypto-currency? token-decimals 2)
max-decimals (if crypto-currency? crypto-decimals 2)
regex-pattern (str "^\\d*\\.?\\d{0," max-decimals "}$")
regex (re-pattern regex-pattern)]
(when (re-matches regex new-text)
@@ -243,40 +243,37 @@
[{:keys [network-values token-symbol on-press on-long-press receiver? loading-routes?
token-not-supported-in-receiver-networks?]}]
[rn/view
(doall
(map-indexed (fn [index
{chain-id :chain-id
network-value-type :type
total-amount :total-amount}]
(let [status (cond (and (= network-value-type :not-available)
loading-routes?
token-not-supported-in-receiver-networks?)
:loading
(= network-value-type :not-available)
:disabled
:else network-value-type)
amount-formatted (-> (rf/sub [:wallet/send-amount-fixed total-amount])
(str " " token-symbol))]
^{:key (str (if receiver? "to" "from") "-" chain-id)}
[rn/view
{:style {:margin-top (if (pos? index) 11 7.5)}}
[quo/network-bridge
{:amount (if (= network-value-type :not-available)
(i18n/label :t/not-available)
amount-formatted)
:network (network-utils/id->network chain-id)
:status status
:on-press #(when (not loading-routes?)
(cond
(= network-value-type :edit)
(open-preferences)
on-press (on-press chain-id total-amount)))
:on-long-press #(when (and (not loading-routes?) (not= status :disabled))
(cond
(= network-value-type :add)
(open-preferences)
on-long-press (on-long-press chain-id total-amount)))}]]))
network-values))])
(map-indexed (fn [index
{chain-id :chain-id
network-value-type :type
total-amount :total-amount}]
(let [status (cond (and (= network-value-type :not-available)
loading-routes?
token-not-supported-in-receiver-networks?)
:loading
(= network-value-type :not-available)
:disabled
:else network-value-type)]
[rn/view
{:key (str (if receiver? "to" "from") "-" chain-id)
:style {:margin-top (if (pos? index) 11 7.5)}}
[quo/network-bridge
{:amount (if (= network-value-type :not-available)
(i18n/label :t/not-available)
(str total-amount " " token-symbol))
:network (network-utils/id->network chain-id)
:status status
:on-press #(when (not loading-routes?)
(cond
(= network-value-type :edit)
(open-preferences)
on-press (on-press chain-id total-amount)))
:on-long-press #(when (and (not loading-routes?) (not= status :disabled))
(cond
(= network-value-type :add)
(open-preferences)
on-long-press (on-long-press chain-id total-amount)))}]]))
network-values)])
(defn render-network-links
[{:keys [network-links sender-network-values]}]
@@ -1,81 +0,0 @@
(ns status-im.contexts.wallet.send.transaction-confirmation.account-summary
(:require
[quo.core :as quo]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.send.transaction-confirmation.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- account-summary-header
[{:keys [label accessibility-label]}]
(let [theme (quo.theme/use-theme)]
[quo/text
{:size :paragraph-2
:weight :medium
:style (style/section-label theme)
:accessibility-label accessibility-label}
label]))
(defn- current-user-account-summary
[{:keys [network-values]}]
(let [{:keys [color emoji name address]} (rf/sub [:wallet/current-viewing-account])]
[quo/summary-info
{:type :status-account
:networks? true
:values network-values
:account-props {:customization-color color
:size 32
:emoji emoji
:type :default
:name name
:address (utils/get-shortened-address address)}}]))
(defn- recipient-account-summary
[{:keys [network-values]}]
(let [{:keys [label emoji customization-color recipient-type]} (rf/sub [:wallet/send-recipient])
address (rf/sub [:wallet/send-to-address])
shortened-address (utils/get-shortened-address address)
summary-type (condp = recipient-type
:saved-address :saved-account
:account :status-account
:address :account
:account)]
[quo/summary-info
{:type summary-type
:networks? true
:values network-values
:account-props (cond-> {:customization-color customization-color
:size 32
:emoji emoji
:type :default
:name (or label address)
:full-name label
:address shortened-address}
(= recipient-type :address)
(assoc :full-name "0 x"
:monospace? true
:lowercase? true))}]))
(defn from-summary
[]
(let [network-values (rf/sub [:wallet/network-values false])]
[rn/view {:style style/summary-container}
[account-summary-header
{:label (i18n/label :t/from-capitalized)
:accessibility-label :summary-from-label}]
[current-user-account-summary {:network-values network-values}]]))
(defn to-summary
[]
(let [network-values (rf/sub [:wallet/network-values true])
tx-type (rf/sub [:wallet/send-tx-type])]
[rn/view {:style style/summary-container}
[account-summary-header
{:label (i18n/label :t/to-capitalized)
:accessibility-label :summary-to-label}]
(if (= tx-type :tx/bridge)
[current-user-account-summary {:network-values network-values}]
[recipient-account-summary {:network-values network-values}])]))
@@ -1,17 +1,13 @@
(ns status-im.contexts.wallet.send.transaction-confirmation.style
(:require [quo.foundations.colors :as colors]))
(def summary-container
{:padding-horizontal 20
:padding-bottom 16})
(def detail-item
{:flex 1
:height 36
:background-color :transparent})
(def content-container
{:padding-top 8
{:padding-top 12
:padding-horizontal 20
:padding-bottom 32})
@@ -1,131 +0,0 @@
(ns status-im.contexts.wallet.send.transaction-confirmation.transaction-title
(:require [quo.core :as quo]
[react-native.core :as rn]
[react-native.utils :as rn.utils]
[status-im.contexts.wallet.send.transaction-confirmation.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- title-container
[& argv]
(let [[_ children] (rn.utils/get-props-and-children argv)]
(into [rn/view {:style style/content-container}]
children)))
(defn- title-row
[& argv]
(let [[_ children] (rn.utils/get-props-and-children argv)]
(into [rn/view
{:style {:flex-direction :row
:margin-top 4}}]
children)))
(defn- title-text
[text]
[quo/text
{:size :heading-1
:weight :semi-bold
:style style/title-container
:accessibility-label :send-label}
text])
(defn- title-token
[]
(let [amount (rf/sub [:wallet/send-amount])
token-display-name (rf/sub [:wallet/wallet-send-token-symbol])]
[quo/summary-tag
{:token token-display-name
:label (str amount " " token-display-name)
:type :token}]))
(defn- title-collectible
[]
(let [{:keys [collectible-data preview-url]} (rf/sub [:wallet/send-collectible])]
[quo/summary-tag
{:label (:name collectible-data)
:image-source (:uri preview-url)
:type :collectible}]))
(defn- title-network
[{:keys [chain-id]}]
(let [{:keys [full-name source network-name]} (rf/sub [:wallet/network-details-by-chain-id chain-id])]
[quo/summary-tag
{:label full-name
:type :network
:image-source source
:customization-color network-name}]))
(defn- from-bridge-networks
[]
(let [chain-ids (rf/sub [:wallet/send-from-chain-ids])]
[:<>
(doall (map-indexed (fn [idx chain-id]
^{:key (str "transaction-title" idx)}
[title-row
[title-text
(if (zero? idx)
(i18n/label :t/from)
(str (i18n/label :t/and) " "))]
[title-network {:chain-id chain-id}]])
chain-ids))]))
(defn- to-bridge-network
[]
(let [chain-id (rf/sub [:wallet/send-bridge-to-chain-id])]
[title-network {:chain-id chain-id}]))
(defn- title-account
[]
(let [{:keys [name emoji color]} (rf/sub [:wallet/current-viewing-account])]
[quo/summary-tag
{:label name
:type :account
:emoji emoji
:customization-color color}]))
(defn- title-recipient
[]
(let [{:keys [recipient-type emoji label customization-color]} (rf/sub [:wallet/send-recipient])]
[quo/summary-tag
{:type recipient-type
:emoji emoji
:label label
:customization-color customization-color}]))
(defn- bridge-title
[]
[title-container
[title-row
[title-text (i18n/label :t/bridge)]
[title-token]]
[from-bridge-networks]
[title-row
[title-text (i18n/label :t/to)]
[to-bridge-network]]
[title-row
[title-text (i18n/label :t/in)]
[title-account]]])
(defn- send-title
[]
(let [sending-collectible? (rf/sub [:wallet/sending-collectible?])]
[title-container
[title-row
[title-text (i18n/label :t/send)]
(if sending-collectible?
[title-collectible]
[title-token])]
[title-row
[title-text (i18n/label :t/from)]
[title-account]]
[title-row
[title-text (i18n/label :t/to)]
[title-recipient]]]))
(defn view
[]
(let [tx-type (rf/sub [:wallet/send-tx-type])]
(condp = tx-type
:tx/bridge [bridge-title]
:tx/send [send-title]
[send-title])))
@@ -1,17 +1,158 @@
(ns status-im.contexts.wallet.send.transaction-confirmation.view
(:require
[clojure.string :as string]
[quo.core :as quo]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[status-im.common.floating-button-page.view :as floating-button-page]
[status-im.common.standard-authentication.core :as standard-auth]
[status-im.contexts.wallet.send.transaction-confirmation.account-summary :as account-summary]
[status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.send.transaction-confirmation.style :as style]
[status-im.contexts.wallet.send.transaction-confirmation.transaction-title :as transaction-title]
[status-im.contexts.wallet.send.utils :as send-utils]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.security.core :as security]))
(defn- transaction-title
[{:keys [token-display-name amount account route to-network image-url transaction-type
collectible? recipient]}]
(let [to-network-name (:network-name to-network)
to-network-color (if (= to-network-name :mainnet) :ethereum to-network-name)]
[rn/view {:style style/content-container}
[rn/view {:style {:flex-direction :row}}
[quo/text
{:size :heading-1
:weight :semi-bold
:style style/title-container
:accessibility-label :send-label}
(if (= transaction-type :tx/bridge)
(i18n/label :t/bridge)
(i18n/label :t/send))]
[quo/summary-tag
(cond-> {:token (if collectible? "" token-display-name)
:label (str amount " " token-display-name)
:type (if collectible? :collectible :token)}
collectible? (assoc :image-source image-url))]]
(if (= transaction-type :tx/bridge)
(doall
(map-indexed
(fn [idx path]
(let [from-network (:from path)
chain-id (:chain-id from-network)
network (rf/sub [:wallet/network-details-by-chain-id
chain-id])
network-name (:network-name network)
network-name-text (name network-name)
network-name-capitalized (when (seq network-name-text)
(string/capitalize network-name-text))
network-color (if (= network-name :mainnet) :ethereum network-name)]
(with-meta
[rn/view
{:style {:flex-direction :row
:margin-top 4}}
(if (zero? idx)
[:<>
[quo/text
{:size :heading-1
:weight :semi-bold
:style style/title-container
:accessibility-label :send-label}
(i18n/label :t/from)]
[quo/summary-tag
{:label network-name-capitalized
:type :network
:image-source (:source network)
:customization-color network-color}]]
[:<>
[quo/text
{:size :heading-1
:weight :semi-bold
:style style/title-container
:accessibility-label :send-label}
(str (i18n/label :t/and) " ")]
[quo/summary-tag
{:label network-name-capitalized
:type :network
:image-source (:source network)
:customization-color network-color}]])]
{:key (str "transaction-title" idx)})))
route))
[rn/view
{:style {:flex-direction :row
:margin-top 4}}
[quo/text
{:size :heading-1
:weight :semi-bold
:style style/title-container
:accessibility-label :send-label}
(i18n/label :t/from)]
[quo/summary-tag
{:label (:name account)
:type :account
:emoji (:emoji account)
:customization-color (:color account)}]])
[rn/view
{:style {:flex-direction :row
:margin-top 4}}
[quo/text
{:size :heading-1
:weight :semi-bold
:style style/title-container
:accessibility-label :send-label}
(i18n/label :t/to)]
(if (= transaction-type :tx/bridge)
[quo/summary-tag
{:type :network
:image-source (:source to-network)
:label (string/capitalize (name (:network-name to-network)))
:customization-color to-network-color}]
[quo/summary-tag (assoc recipient :type (:recipient-type recipient))])]
(when (= transaction-type :tx/bridge)
[rn/view
{:style {:flex-direction :row
:margin-top 4}}
[quo/text
{:size :heading-1
:weight :semi-bold
:style style/title-container
:accessibility-label :send-label}
(i18n/label :t/in)]
[quo/summary-tag
{:label (:name account)
:type :account
:emoji (:emoji account)
:customization-color (:color account)}]])]))
(defn- user-summary
[{:keys [account-props theme label accessibility-label summary-type recipient bridge-tx? account-to?]}]
(let [network-values (rf/sub [:wallet/network-values account-to?])
summary-info-type (case (:recipient-type recipient)
:saved-address :saved-account
:account :status-account
summary-type)]
[rn/view
{:style {:padding-horizontal 20
:padding-bottom 16}}
[quo/text
{:size :paragraph-2
:weight :medium
:style (style/section-label theme)
:accessibility-label accessibility-label}
label]
[quo/summary-info
{:type summary-info-type
:networks? true
:values (send-utils/network-values-for-ui network-values)
:account-props (cond-> account-props
(and account-to? (not bridge-tx?))
(assoc
:size 32
:name (:label recipient)
:full-name (:label recipient)
:emoji (:emoji recipient)
:customization-color (:customization-color recipient)))}]]))
(defn- data-item
[{:keys [title subtitle]}]
[quo/data-item
@@ -23,53 +164,11 @@
:title title
:subtitle subtitle}])
(defn- estimated-time-view
[]
(let [estimated-time (rf/sub [:wallet/send-estimated-time])]
[data-item
{:title (i18n/label :t/est-time)
:subtitle (i18n/label :t/time-in-mins
{:minutes (str estimated-time)})}]))
(defn- max-fees-view
[]
(let [native-currency-symbol (-> (rf/sub [:wallet/send-route])
first
(get-in [:from :native-currency-symbol]))
max-fees (rf/sub [:wallet/wallet-send-fee-fiat-formatted
native-currency-symbol])]
[data-item
{:title (i18n/label :t/max-fees)
:subtitle max-fees}]))
(defn- bridge-received-amount
[]
(let [to-network (rf/sub [:wallet/bridge-to-network-details])
amount (rf/sub [:wallet/send-total-amount-formatted])]
[data-item
{:title (i18n/label :t/bridged-to
{:network (:abbreviated-name to-network)})
:subtitle amount}]))
(defn- send-received-amount
[]
(let [amount (rf/sub [:wallet/send-total-amount-formatted])]
[data-item
{:title (i18n/label :t/recipient-gets)
:subtitle amount}]))
(defn- received-amount-view
[]
(let [tx-type (rf/sub [:wallet/send-tx-type])]
(if (= tx-type :tx/bridge)
[bridge-received-amount]
[send-received-amount])))
(defn- transaction-details
[]
(let [route-loaded? (seq (rf/sub [:wallet/send-route]))
loading-suggested-routes? (rf/sub [:wallet/wallet-send-loading-suggested-routes?])
sending-collectible? (rf/sub [:wallet/sending-collectible?])]
[{:keys [estimated-time-min max-fees token-display-name amount to-network route
transaction-type]}]
(let [route-loaded? (and route (seq route))
loading-suggested-routes? (rf/sub [:wallet/wallet-send-loading-suggested-routes?])]
[rn/view
{:style (style/details-container
{:loading-suggested-routes? loading-suggested-routes?
@@ -77,60 +176,127 @@
(cond
loading-suggested-routes?
[rn/activity-indicator {:style {:flex 1}}]
route-loaded?
[:<>
[estimated-time-view]
[max-fees-view]
(when-not sending-collectible?
[received-amount-view])]
[data-item
{:title (i18n/label :t/est-time)
:subtitle (i18n/label :t/time-in-mins {:minutes (str estimated-time-min)})}]
[data-item
{:title (i18n/label :t/max-fees)
:subtitle max-fees}]
[data-item
{:title (if (= transaction-type :tx/bridge)
(i18n/label :t/bridged-to
{:network (:abbreviated-name to-network)})
(i18n/label :t/recipient-gets))
:subtitle (str amount " " token-display-name)}]]
:else
[quo/text {:style {:align-self :center}}
(i18n/label :t/no-routes-found-confirmation)])]))
(defn- slide-to-confirm
[]
(let [route-loaded? (seq (rf/sub [:wallet/send-route]))
loading-suggested-routes? (rf/sub [:wallet/wallet-send-loading-suggested-routes?])
tx-type (rf/sub [:wallet/send-tx-type])
customization-color (rf/sub [:wallet/current-viewing-account-color])]
(when (and (not loading-suggested-routes?) route-loaded?)
[standard-auth/slide-button
{:keycard-supported? true
:size :size-48
:track-text (if (= tx-type :tx/bridge)
(i18n/label :t/slide-to-bridge)
(i18n/label :t/slide-to-send))
:container-style {:z-index 2}
:customization-color customization-color
:on-auth-success #(rf/dispatch
[:wallet/send-transaction
(security/safe-unmask-data %)])
:auth-button-label (i18n/label :t/confirm)}])))
(defn- on-close
[]
(rf/dispatch [:wallet/transaction-confirmation-navigate-back]))
(defn view
[_]
(let [customization-color (rf/sub [:wallet/current-viewing-account-color])]
[rn/view {:style {:flex 1}}
[floating-button-page/view
{:footer-container-padding 0
:header [quo/page-nav
{:icon-name :i/arrow-left
:on-press on-close
:margin-top (safe-area/get-top)
:background :blur
:accessibility-label :top-bar}]
:footer [:<>
[transaction-details]
[slide-to-confirm]]
:gradient-cover? true
:customization-color customization-color}
[rn/view
[transaction-title/view]
[account-summary/from-summary]
[account-summary/to-summary]]]]))
(let [on-close #(rf/dispatch [:wallet/transaction-confirmation-navigate-back])]
(fn []
(let [theme (quo.theme/use-theme)
send-transaction-data (rf/sub [:wallet/wallet-send])
{:keys [token-display-name collectible amount
route
to-address bridge-to-chain-id type
recipient]} send-transaction-data
collectible? (some? collectible)
image-url (when collectible
(get-in collectible [:preview-url :uri]))
transaction-type (:tx-type send-transaction-data)
estimated-time-min (reduce + (map :estimated-time route))
token-symbol (or token-display-name
(-> send-transaction-data :token :symbol))
first-route (first route)
native-currency-symbol (get-in first-route
[:from :native-currency-symbol])
fee-formatted (rf/sub [:wallet/wallet-send-fee-fiat-formatted
native-currency-symbol])
account (rf/sub [:wallet/current-viewing-account])
account-color (:color account)
bridge-to-network (when bridge-to-chain-id
(rf/sub [:wallet/network-details-by-chain-id
bridge-to-chain-id]))
loading-suggested-routes? (rf/sub
[:wallet/wallet-send-loading-suggested-routes?])
total-amount-receiver (rf/sub [:wallet/total-amount true])
from-account-props {:customization-color account-color
:size 32
:emoji (:emoji account)
:type :default
:name (:name account)
:address (utils/get-shortened-address
(:address
account))}
user-props {:full-name to-address
:address (utils/get-shortened-address
to-address)}]
[rn/view {:style {:flex 1}}
[floating-button-page/view
{:footer-container-padding 0
:header [quo/page-nav
{:icon-name :i/arrow-left
:on-press on-close
:margin-top (safe-area/get-top)
:background :blur
:accessibility-label :top-bar}]
:footer [:<>
[transaction-details
{:estimated-time-min estimated-time-min
:max-fees fee-formatted
:token-display-name token-symbol
:amount total-amount-receiver
:to-network bridge-to-network
:theme theme
:route route
:transaction-type transaction-type}]
(when (and (not loading-suggested-routes?) route (seq route))
[standard-auth/slide-button
{:keycard-supported? true
:size :size-48
:track-text (if (= transaction-type :tx/bridge)
(i18n/label :t/slide-to-bridge)
(i18n/label :t/slide-to-send))
:container-style {:z-index 2}
:customization-color account-color
:on-auth-success #(rf/dispatch
[:wallet/send-transaction
(security/safe-unmask-data %)])
:auth-button-label (i18n/label :t/confirm)}])]
:gradient-cover? true
:customization-color (:color account)}
[rn/view
[transaction-title
{:token-display-name token-symbol
:amount amount
:account account
:type type
:recipient recipient
:route route
:to-network bridge-to-network
:image-url image-url
:transaction-type transaction-type
:collectible? collectible?}]
[user-summary
{:summary-type :status-account
:accessibility-label :summary-from-label
:label (i18n/label :t/from-capitalized)
:account-props from-account-props
:theme theme}]
[user-summary
{:summary-type (if (= transaction-type :tx/bridge)
:status-account
:account)
:accessibility-label :summary-to-label
:label (i18n/label :t/to-capitalized)
:account-props (if (= transaction-type :tx/bridge)
from-account-props
user-props)
:recipient recipient
:bridge-tx? (= transaction-type :tx/bridge)
:account-to? true
:theme theme}]]]]))))
+13 -57
View File
@@ -1,7 +1,9 @@
(ns status-im.contexts.wallet.send.utils
(:require
[native-module.core :as native-module]
[status-im.constants :as constants]
[status-im.contexts.wallet.common.utils.networks :as network-utils]
[utils.hex :as utils.hex]
[utils.money :as money]))
(defn amount-in-hex
@@ -41,67 +43,21 @@
[route]
(money/wei->ether (reduce money/add (map calculate-gas-fee route))))
(defn- path-amount-in
[path]
(-> path :amount-in money/from-hex))
(defn- path-amount-out
[path]
(if (= (:bridge-name path) constants/bridge-name-hop)
(let [{:keys [token-fees bonder-fees amount-in]} path]
(-> amount-in
money/from-hex
(money/sub token-fees)
(money/add bonder-fees)))
(-> path :amount-out money/from-hex)))
(defn- convert-wei-to-eth
[amount native-token? token-decimals]
(money/with-precision
(if native-token?
(money/wei->ether amount)
(money/token->unit amount token-decimals))
constants/token-display-precision))
(defn network-amounts-by-chain
[{:keys [route token-decimals native-token? receiver?]}]
(reduce
(fn [acc path]
(let [amount (if receiver?
(path-amount-out path)
(path-amount-in path))
chain-id (if receiver?
(get-in path [:to :chain-id])
(get-in path [:from :chain-id]))]
(as-> amount $
(convert-wei-to-eth $ native-token? token-decimals)
(update acc chain-id money/add $))))
{}
route))
(defn path-estimated-received
"Calculates the (`bignumber`) estimated received token amount. For
bridge transactions, the amount is the difference between the
`amount-in` and the `token-fees`."
[path]
(if (-> path :bridge-name (= constants/bridge-name-hop))
(-> path
:amount-in
money/from-hex
(money/sub (:token-fees path)))
(-> path
:amount-out
money/from-hex)))
(defn estimated-received-by-chain
[route token-decimals native-token?]
(reduce
(fn [acc path]
(let [chain-id (get-in path [:to :chain-id])
estimated-received (-> path
path-estimated-received
(convert-wei-to-eth native-token? token-decimals))]
(update acc chain-id money/add estimated-received)))
(let [amount-hex (if receiver? (:amount-out path) (:amount-in path))
amount-units (native-module/hex-to-number
(utils.hex/normalize-hex amount-hex))
amount (money/with-precision
(if native-token?
(money/wei->ether amount-units)
(money/token->unit amount-units
token-decimals))
6)
chain-id (if receiver? (get-in path [:to :chain-id]) (get-in path [:from :chain-id]))]
(update acc chain-id money/add amount)))
{}
route))
@@ -79,91 +79,9 @@
:receiver? receiver?})
expected {1 (money/bignumber "2")
10 (money/bignumber "2")}]
(doseq [[chain-id exp-value] expected]
(is (money/equal-to (get result chain-id) exp-value)))))
(testing "Correctly calculates network (out) amounts for bridge transaction"
(let [route [{:bridge-name "Hop"
:amount-in "0xde0b6b3a7640000"
:bonder-fees (money/bignumber "200000000000000")
:token-fees (money/bignumber "230000000000000")
:to {:chain-id 1}}
{:bridge-name "Hop"
:bonder-fees (money/bignumber "300000000000000")
:token-fees (money/bignumber "410000000000000")
:amount-in "0xde0b6b3a7640000"
:to {:chain-id 10}}]
token-decimals 6
native-token? true
receiver? true
result (utils/network-amounts-by-chain {:route route
:token-decimals token-decimals
:native-token? native-token?
:receiver? receiver?})
expected {1 (money/bignumber "0.99997")
10 (money/bignumber "0.99989")}]
(doseq [[chain-id exp-value] expected]
(is (money/equal-to (get result chain-id) exp-value))))))
(deftest estimated-received-by-chain-test
(testing "Correctly calculates the bridge estimated received amount"
(let [chain-id 1
route [{:bridge-name "Hop"
:amount-in "0xde0b6b3a7640000"
:token-fees (money/bignumber "230000000000000")
:to {:chain-id 1}}]
token-decimals 18
native-token? true
result (utils/estimated-received-by-chain route token-decimals native-token?)
expected (money/bignumber "0.99977")]
(is (money/equal-to (get result chain-id) expected))))
(testing "Correctly calculates the estimated received amount with native token"
(let [chain-id 1
route [{:amount-out "0x1bc16d674ec80000"
:to {:chain-id chain-id}}]
token-decimals 18
native-token? true
result (utils/estimated-received-by-chain route token-decimals native-token?)
expected (money/bignumber "2")]
(is (money/equal-to (get result chain-id) expected))))
(testing "Correctly calculates the estimated received amount with non-native token"
(let [chain-id 10
route [{:amount-out "0x1bc16d674ec80000"
:to {:chain-id chain-id}}]
token-decimals 18
native-token? false
result (utils/estimated-received-by-chain route token-decimals native-token?)
expected (money/bignumber "2")]
(is (money/equal-to (get result chain-id) expected))))
(testing
"Correctly calculates the estimated received amount with multiple routes on different networks"
(let [route [{:amount-out "0x1bc16d674ec80000"
:to {:chain-id 1}}
{:amount-out "0xde0b6b3a7640000"
:to {:chain-id 10}}]
token-decimals 18
native-token? false
result (utils/estimated-received-by-chain route token-decimals native-token?)
expected {10 (money/bignumber "1")
1 (money/bignumber "2")}]
(doseq [[chain-id exp-value] expected]
(is (money/equal-to (get result chain-id) exp-value)))))
(testing "Correctly calculates the estimated received amount with multiple routes on the same network"
(let [chain-id 10
route [{:amount-out "0x1bc16d674ec80000"
:to {:chain-id chain-id}}
{:amount-out "0x1bc16d674ec80000"
:to {:chain-id chain-id}}]
token-decimals 18
native-token? false
result (utils/estimated-received-by-chain route token-decimals native-token?)
expected (money/bignumber "4")]
(is (money/equal-to (get result chain-id) expected)))))
(deftest network-values-for-ui-test
(testing "Sanitizes values correctly for display"
(let [amounts {1 (money/bignumber "0")
@@ -29,6 +29,7 @@
(update network :full-name #(str % " " testnet-name)))]
(condp #(contains? %1 %2) (:chain-id network)
constants/sepolia-chain-ids (add-testnet-name constants/sepolia-full-name)
constants/goerli-chain-ids (add-testnet-name constants/goerli-full-name)
network)))
(defn chain-id->network-details
@@ -46,7 +47,7 @@
js/parseInt))
chains))]
(if testnet-mode?
(set/subset? chain-ids constants/sepolia-chain-ids)
(set/subset? chain-ids (set/union constants/sepolia-chain-ids constants/goerli-chain-ids))
(set/subset? chain-ids constants/mainnet-chain-ids))))
(defn get-proposal-networks
+6
View File
@@ -69,6 +69,12 @@
transitions/new-to-status-modal-animations
transitions/push-animations-for-transparent-background)}))
(def keycard-modal-screen-options
(assoc transparent-modal-screen-options
:layout nil
:theme :dark
:insets {:top? true :bottom? true}))
(def sheet-options
{:layout {:componentBackgroundColor :transparent
:orientation ["portrait"]
+13 -63
View File
@@ -32,12 +32,7 @@
[status-im.contexts.keycard.check.view :as keycard.check]
[status-im.contexts.keycard.empty.view :as keycard.empty]
[status-im.contexts.keycard.error.view :as keycard.error]
[status-im.contexts.keycard.migrate.fail.view :as keycard.migrate.fail]
[status-im.contexts.keycard.migrate.re-encrypting.view :as keycard.re-encrypting]
[status-im.contexts.keycard.migrate.success.view :as keycard.migrate.success]
[status-im.contexts.keycard.migrate.view :as keycard.migrate]
[status-im.contexts.keycard.not-keycard.view :as keycard.not-keycard]
[status-im.contexts.keycard.pin.create.view :as pin.create]
[status-im.contexts.onboarding.create-or-sync-profile.view :as create-or-sync-profile]
[status-im.contexts.onboarding.create-password.view :as create-password]
[status-im.contexts.onboarding.create-profile.view :as create-profile]
@@ -315,9 +310,8 @@
:component settings/view}
{:name :screen/settings.keycard
:metrics {:track? :true
:alias-id :settings.keycard}
:options {:insets {:top? true :bottom? true}}
:metrics {:track? :true}
:options options/keycard-modal-screen-options
:component settings.keycard/view}
{:name :edit-profile
@@ -883,73 +877,29 @@
(def keycard-screens
[{:name :screen/keycard.check
:metrics {:track? :true
:alias-id :keycard.check}
:options {:insets {:top? true :bottom? true}}
:metrics {:track? true}
:options options/keycard-modal-screen-options
:component keycard.check/view}
{:name :screen/keycard.empty
:metrics {:track? :true
:alias-id :keycard.empty}
:options {:insets {:top? true :bottom? true}}
:metrics {:track? true}
:options options/keycard-modal-screen-options
:component keycard.empty/view}
{:name :screen/keycard.error
:metrics {:track? :true
:alias-id :keycard.error}
:options {:insets {:top? true :bottom? true}}
:metrics {:track? true}
:options options/keycard-modal-screen-options
:component keycard.error/view}
{:name :screen/keycard.not-keycard
:metrics {:track? :true
:alias-id :keycard.not-keycard}
:options {:insets {:top? true :bottom? true}}
:metrics {:track? true}
:options options/keycard-modal-screen-options
:component keycard.not-keycard/view}
{:name :screen/keycard.authorise
:metrics {:track? :true
:alias-id :keycard.authorise}
:options {:insets {:top? true :bottom? true}}
:component keycard.authorise/view}
{:name :screen/keycard.migrate
:metrics {:track? :true
:alias-id :keycard.migrate}
:options {:insets {:top? true :bottom? true}}
:component keycard.migrate/view}
{:name :screen/keycard.re-encrypting
:metrics {:track? :true
:alias-id :keycard.re-encrypting}
:options {:insets {:top? true :bottom? true}
:popGesture false
:hardwareBackButton {:dismissModalOnPress false
:popStackOnPress false}}
:component keycard.re-encrypting/view}
{:name :screen/keycard.migrate.success
:metrics {:track? :true
:alias-id :keycard.migrate.success}
:options {:insets {:top? true :bottom? true}
:popGesture false
:hardwareBackButton {:dismissModalOnPress false
:popStackOnPress false}}
:component keycard.migrate.success/view}
{:name :screen/keycard.migrate.fail
:metrics {:track? :true
:alias-id :keycard.migrate.fail}
:options {:insets {:top? true :bottom? true}
:popGesture false
:hardwareBackButton {:dismissModalOnPress false
:popStackOnPress false}}
:component keycard.migrate.fail/view}
{:name :screen/keycard.pin.create
:metrics {:track? :true
:alias-id :keycard.pin.create}
:options {:insets {:top? true :bottom? true}}
:component pin.create/view}])
:metrics {:track? true}
:options options/keycard-modal-screen-options
:component keycard.authorise/view}])
(defn screens
[]
+1 -1
View File
@@ -11,7 +11,7 @@
[status-im.common.bottom-sheet-screen.view :as bottom-sheet-screen]
[status-im.common.bottom-sheet.view :as bottom-sheet]
[status-im.common.toasts.view :as toasts]
[status-im.contexts.keycard.nfc.sheets.view :as keycard.sheet]
[status-im.contexts.keycard.nfc-sheet.view :as keycard.sheet]
[status-im.navigation.screens :as screens]
[status-im.setup.hot-reload :as reloader]
[utils.re-frame :as rf]))
+6
View File
@@ -136,6 +136,12 @@
(fn [profile]
(:universal-profile-url profile)))
(re-frame/reg-sub
:profile/is-goerli-enabled?
:<- [:profile/profile]
(fn [profile]
(:is-goerli-enabled? profile)))
(re-frame/reg-sub
:profile/peer-syncing-enabled?
:<- [:profile/profile]
+17 -8
View File
@@ -1,8 +1,7 @@
(ns status-im.subs.wallet.networks
(:require [re-frame.core :as re-frame]
[status-im.contexts.wallet.common.utils.networks :as network-utils]
[utils.money :as money]
[utils.number :as number]))
[utils.money :as money]))
(def max-network-prefixes 2)
@@ -63,18 +62,28 @@
(re-frame/reg-sub
:wallet/network-values
:<- [:wallet/wallet-send]
:<- [:wallet/send-display-token-decimals]
(fn [[{:keys [from-values-by-chain to-values-by-chain token-display-name] :as send-data} token-decimals]
[_ to-values?]]
(fn [{:keys [from-values-by-chain to-values-by-chain token-display-name] :as send-data} [_ to-values?]]
(let [network-values (if to-values? to-values-by-chain from-values-by-chain)
token-symbol (or token-display-name
(-> send-data :token :symbol))]
(reduce-kv
(fn [acc chain-id amount]
(let [network-name (network-utils/id->network chain-id)
amount-fixed (number/to-fixed (money/->bignumber amount) token-decimals)]
(let [network-name (network-utils/id->network chain-id)]
(assoc acc
(if (= network-name :mainnet) :ethereum network-name)
{:amount amount-fixed :token-symbol token-symbol})))
{:amount amount :token-symbol token-symbol})))
{}
network-values))))
(re-frame/reg-sub
:wallet/total-amount
:<- [:wallet/wallet-send]
(fn [{:keys [from-values-by-chain to-values-by-chain]} [_ to-values?]]
(let [network-values (if to-values? to-values-by-chain from-values-by-chain)]
(reduce
(fn [acc amount]
(if (money/bignumber? amount)
(money/add acc amount)
acc))
(money/bignumber 0)
(vals network-values)))))
+3 -124
View File
@@ -1,12 +1,9 @@
(ns status-im.subs.wallet.send
(:require
[re-frame.core :as rf]
[status-im.constants :as constants]
[status-im.contexts.wallet.common.activity-tab.constants :as activity-constants]
[status-im.contexts.wallet.common.utils :as common-utils]
[status-im.contexts.wallet.common.activity-tab.constants :as constants]
[status-im.contexts.wallet.send.utils :as send-utils]
[utils.money :as money]
[utils.number :as number]))
[utils.number]))
(rf/reg-sub
:wallet/send-tab
@@ -24,21 +21,6 @@
:<- [:wallet/wallet-send]
:-> :recipient)
(rf/reg-sub
:wallet/send-to-address
:<- [:wallet/wallet-send]
:-> :to-address)
(rf/reg-sub
:wallet/send-route
:<- [:wallet/wallet-send]
:-> :route)
(rf/reg-sub
:wallet/send-token
:<- [:wallet/wallet-send]
:-> :token)
(rf/reg-sub
:wallet/send-transaction-ids
:<- [:wallet/wallet-send]
@@ -59,28 +41,11 @@
:<- [:wallet/wallet-send]
:-> :tx-type)
(rf/reg-sub
:wallet/send-collectible
:<- [:wallet/wallet-send]
#(:collectible %))
(rf/reg-sub
:wallet/sending-collectible?
:<- [:wallet/send-tx-type]
#(send-utils/tx-type-collectible? %))
(rf/reg-sub
:wallet/send-collectible-preview
:<- [:wallet/send-collectible]
:-> :preview-url
:-> :uri)
(rf/reg-sub
:wallet/send-collectible-name
:<- [:wallet/send-collectible]
:-> :collectible-data
:-> :name)
(rf/reg-sub
:wallet/send-transaction-progress
:<- [:wallet/send-transaction-ids]
@@ -99,7 +64,7 @@
(->> address-activity
(sort :timestamp)
(keep (fn [{:keys [activity-type recipient]}]
(when (= activity-constants/wallet-activity-type-send activity-type)
(when (= constants/wallet-activity-type-send activity-type)
recipient)))
(distinct)))))
@@ -108,12 +73,6 @@
:<- [:wallet/wallet-send]
:-> :token-not-supported-in-receiver-networks?)
(rf/reg-sub
:wallet/send-bridge-to-chain-id
:<- [:wallet/wallet-send]
(fn [send-data]
(:bridge-to-chain-id send-data)))
(rf/reg-sub
:wallet/bridge-from-networks
:<- [:wallet/wallet-send]
@@ -132,83 +91,3 @@
(when (not= (:chain-id network) bridge-to-chain-id)
(:chain-id network)))
networks)))
(rf/reg-sub
:wallet/send-token-decimals
:<- [:wallet/wallet-send]
(fn [{:keys [token collectible]}]
(if collectible 0 (:decimals token))))
(rf/reg-sub
:wallet/send-display-token-decimals
:<- [:wallet/wallet-send]
(fn [{:keys [token collectible]}]
(if collectible
0
(-> token
common-utils/token-usd-price
common-utils/one-cent-value
common-utils/calc-max-crypto-decimals
(min constants/min-token-decimals-to-display)))))
(rf/reg-sub
:wallet/send-native-token?
:<- [:wallet/wallet-send]
(fn [{:keys [token token-display-name]}]
(and token (= token-display-name "ETH"))))
(rf/reg-sub
:wallet/total-amount
:<- [:wallet/send-route]
:<- [:wallet/send-token-decimals]
:<- [:wallet/send-native-token?]
(fn [[route token-decimals native-token?]]
(let [default-amount (money/bignumber 0)]
(if route
(->> (send-utils/estimated-received-by-chain
route
token-decimals
native-token?)
vals
(reduce money/add default-amount))
default-amount))))
(rf/reg-sub
:wallet/send-total-amount-formatted
:<- [:wallet/total-amount]
:<- [:wallet/send-display-token-decimals]
:<- [:wallet/wallet-send-token-symbol]
(fn [[amount token-decimals token-symbol]]
(-> amount
(number/to-fixed token-decimals)
(str " " token-symbol))))
(rf/reg-sub
:wallet/send-amount-fixed
:<- [:wallet/send-display-token-decimals]
(fn [token-decimals [_ amount]]
(number/to-fixed (money/->bignumber amount) token-decimals)))
(rf/reg-sub
:wallet/bridge-to-network-details
:<- [:wallet/wallet-send]
:<- [:wallet/network-details]
(fn [[{:keys [bridge-to-chain-id]} networks]]
(when bridge-to-chain-id
(some (fn [network]
(when
(= (:chain-id network) bridge-to-chain-id)
network))
networks))))
(rf/reg-sub
:wallet/send-from-chain-ids
:<- [:wallet/send-route]
(fn [route]
(map #(-> % :from :chain-id) route)))
(rf/reg-sub
:wallet/send-estimated-time
:<- [:wallet/send-route]
(fn [route]
(reduce + (map :estimated-time route))))
-148
View File
@@ -6,15 +6,8 @@
[status-im.subs.root]
[status-im.subs.wallet.send]
[test-helpers.unit :as h]
[utils.money :as money]
[utils.re-frame :as rf]))
(def ^:private token-mock
{:decimals 8
:symbol "ETH"
:balances-per-chain {1 {:raw-balance "100"}}
:market-values-per-currency {:usd {:price 10000}}})
(h/deftest-sub :wallet/send-tab
[sub-name]
(testing "returns active tab for selecting address"
@@ -91,144 +84,3 @@
:timestamp 1588464000}}})
(assoc-in [:wallet :current-viewing-account-address] "acc1"))))
(is (match? ["acc2" "acc4"] (rf/sub [sub-name])))))
(h/deftest-sub :wallet/send-token-decimals
[sub-name]
(testing "returns the decimals for the chosen token"
(swap! rf-db/app-db assoc-in [:wallet :ui :send :token] token-mock)
(is (= 8 (rf/sub [sub-name]))))
(testing "returns 0 if sending collectibles"
(swap! rf-db/app-db assoc-in [:wallet :ui :send :collectible] {})
(is (match? 0 (rf/sub [sub-name])))))
(h/deftest-sub :wallet/send-display-token-decimals
[sub-name]
(testing "returns the decimals based on the token price for the chosen token"
(swap! rf-db/app-db assoc-in
[:wallet :ui :send :token]
{:symbol "ETH"
:balances-per-chain {1 {:raw-balance "100"}}
:market-values-per-currency {:usd {:price 10000}}})
(is (match? 6 (rf/sub [sub-name]))))
(testing "returns 0 if sending collectibles"
(swap! rf-db/app-db assoc-in [:wallet :ui :send :collectible] {})
(is (match? 0 (rf/sub [sub-name])))))
(h/deftest-sub :wallet/send-native-token?
[sub-name]
(testing "returns true if the chosen token is native (ETH)"
(swap! rf-db/app-db
(fn [db]
(-> db
(assoc-in [:wallet :ui :send :token] {})
(assoc-in [:wallet :ui :send :token-display-name] "ETH"))))
(is (rf/sub [sub-name])))
(testing "returns false if the chosen token is not native"
(swap! rf-db/app-db
(swap! rf-db/app-db
(fn [db]
(-> db
(assoc-in [:wallet :ui :send :token] {})
(assoc-in [:wallet :ui :send :token-display-name] "SNT")))))
(is (not (rf/sub [sub-name])))))
(h/deftest-sub :wallet/total-amount
[sub-name]
(testing "returns the total SEND amount when the route is present"
(swap! rf-db/app-db
(fn [db]
(-> db
(assoc-in [:wallet :ui :send :token] token-mock)
(assoc-in [:wallet :ui :send :token-display-name] "ETH")
(assoc-in [:wallet :ui :send :route]
[{:amount-out "0x1bc16d674ec80000"
:to {:chain-id 1}}]))))
(is (match? (money/bignumber 2) (rf/sub [sub-name]))))
(testing "returns the total BRIDGE amount when the route is present"
(swap! rf-db/app-db
(fn [db]
(-> db
(assoc-in [:wallet :ui :send :token] token-mock)
(assoc-in [:wallet :ui :send :token-display-name] "ETH")
(assoc-in [:wallet :ui :send :route]
[{:bridge-name "Hop"
:amount-in "0xde0b6b3a7640000"
:token-fees (money/bignumber "230000000000000")
:to {:chain-id 1}}]))))
(is (match? (money/bignumber 0.99977) (rf/sub [sub-name]))))
(testing "returns the default total amount (0) when the route is not present"
(swap! rf-db/app-db
(fn [db]
(-> db
(assoc-in [:wallet :ui :send :token] token-mock)
(assoc-in [:wallet :ui :send :token-display-name] "ETH"))))
(is (match? (money/bignumber 0) (rf/sub [sub-name])))))
(h/deftest-sub :wallet/send-total-amount-formatted
[sub-name]
(testing "returns the formatted total amount"
(swap! rf-db/app-db
(fn [db]
(-> db
(assoc-in [:wallet :ui :send :token] token-mock)
(assoc-in [:wallet :ui :send :token-display-name] "ETH")
(assoc-in [:wallet :ui :send :route]
[{:amount-out "0x2b139f68a611c00" ;; 193999990000000000
:to {:chain-id 1}}]))))
(is (match? "0.194 ETH" (rf/sub [sub-name])))))
(h/deftest-sub :wallet/send-amount-fixed
[sub-name]
(testing "returns the fixed value when the amount is a string"
(swap! rf-db/app-db
(fn [db]
(-> db
(assoc-in [:wallet :ui :send :token] token-mock))))
(is (match? "2" (rf/sub [sub-name "1.9999999"]))))
(testing "returns the fixed value when the amount is a number"
(swap! rf-db/app-db
(fn [db]
(-> db
(assoc-in [:wallet :ui :send :token] token-mock))))
(is (match? "2" (rf/sub [sub-name 1.9999999]))))
(testing "returns the fixed value when the amount is a bignumber"
(swap! rf-db/app-db
(fn [db]
(-> db
(assoc-in [:wallet :ui :send :token] token-mock))))
(is (match? "2" (rf/sub [sub-name (money/bignumber "1.9999999")])))))
(def ^:private mainnet-network
{:short-name "eth"
:network-name :mainnet
:abbreviated-name "Eth."
:full-name "Mainnet"
:chain-id 1
:related-chain-id 1
:layer 1})
(h/deftest-sub :wallet/bridge-to-network-details
[sub-name]
(testing "returns the network details for bridge transactions"
(swap! rf-db/app-db
(fn [db]
(-> db
(assoc-in [:wallet :networks] {:prod [mainnet-network]})
(assoc-in [:profile/profile :test-networks-enabled?] false)
(assoc-in [:wallet :ui :send] {:bridge-to-chain-id 1}))))
(is (match? mainnet-network (rf/sub [sub-name]))))
(testing "returns nil if not on the bridge flow"
(swap! rf-db/app-db
(fn [db]
(-> db
(assoc-in [:wallet :networks] {:prod [mainnet-network]})
(assoc-in [:profile/profile :test-networks-enabled?] false))))
(is (match? nil (rf/sub [sub-name])))))
+5 -4
View File
@@ -154,8 +154,7 @@
(rf/reg-sub
:wallet/wallet-send-token-symbol
:<- [:wallet/wallet-send]
(fn [{:keys [token-symbol token]}]
(or token-symbol (:symbol token))))
:-> :token-symbol)
(rf/reg-sub
:wallet/wallet-send-disabled-from-chain-ids
@@ -254,10 +253,12 @@
:wallet/selected-networks->chain-ids
:<- [:wallet/selected-networks]
:<- [:profile/test-networks-enabled?]
(fn [[selected-networks testnet-enabled?]]
:<- [:profile/is-goerli-enabled?]
(fn [[selected-networks testnet-enabled? goerli-enabled?]]
(set (map #(network-utils/network->chain-id
{:network %
:testnet-enabled? testnet-enabled?})
:testnet-enabled? testnet-enabled?
:goerli-enabled? goerli-enabled?})
selected-networks))))
(defn- format-settings-keypair-accounts
+4
View File
@@ -122,6 +122,10 @@
:ens-name "test.eth"
:chain-id constants/ethereum-mainnet-chain-id
:expected-result "0xeefb13c7d42efcc655e528da6d6f7bbcf9a2251d"}
{:description "Test on Goerli testnet"
:ens-name "qoqobolo.stateofus.eth"
:chain-id constants/ethereum-goerli-chain-id
:expected-result "0xf8f4f6b5c73f4fb561eb2676326d81b7aba180b0"}
{:description "Test on Sepolia testnet"
:ens-name "code.eth"
:chain-id constants/ethereum-sepolia-chain-id
+1 -1
View File
@@ -4,7 +4,7 @@
;; this is the addresses of ens registries for the different networks
(def ens-registries
{:mainnet "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e"
:sepolia "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e"})
:goerli "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e"})
(def default-address "0x0000000000000000000000000000000000000000")
(def default-key
+9
View File
@@ -26,6 +26,15 @@
(when (and name (string/ends-with? name domain))
(first (string/split name "."))))
(def old-registrars
(merge
{:mainnet "0xDB5ac1a559b02E12F29fC0eC0e37Be8E046DEF49"
:goerli "0xD1f7416F91E7Eb93dD96A61F12FC092aD6B67B11"}))
(defn get-cached-registrar
[chain]
(get old-registrars chain))
(defn lower-case?
[s]
(when s
+2 -2
View File
@@ -7,7 +7,7 @@
(def chains
{:mainnet {:id 1 :name "Mainnet"}
:xdai {:id 100 :name "xDai"}
:sepolia {:id 11155111 :name "Sepolia"}
:goerli {:id 5 :name "Goerli"}
:bsc {:id BSC-mainnet-chain-id
:name "BSC"}
:bsc-testnet {:id BSC-testnet-chain-id
@@ -37,7 +37,7 @@
(defn testnet?
[id]
(contains? #{(chain-keyword->chain-id :sepolia)
(contains? #{(chain-keyword->chain-id :goerli)
(chain-keyword->chain-id :bsc-testnet)}
id))

Some files were not shown because too many files have changed in this diff Show More