Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63226f7f77 |
@@ -1,5 +1,6 @@
|
||||
CACHED_WEBVIEWS_ENABLED=1
|
||||
DEBUG_WEBVIEW=1
|
||||
DEFAULT_NETWORK=mainnet_rpc
|
||||
DEV_BUILD=1
|
||||
ETHEREUM_DEV_CLUSTER=1
|
||||
EXTENSIONS=0
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,4 +1,5 @@
|
||||
DEBUG_WEBVIEW=1
|
||||
DEFAULT_NETWORK=mainnet_rpc
|
||||
ETHEREUM_DEV_CLUSTER=1
|
||||
EXTENSIONS=0
|
||||
GROUP_CHATS_ENABLED=1
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name: Bug Report
|
||||
about: Bug Report
|
||||
title: ''
|
||||
type: 'Bug'
|
||||
labels: ['bug', ':1234: low prio']
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
@@ -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. That’s 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. That’s 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]) {
|
||||
|
||||
|
Before Width: | Height: | Size: 810 B After Width: | Height: | Size: 673 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1001 B |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 69 KiB |
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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})
|
||||
|
||||
|
||||
@@ -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)}
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)))
|
||||
|
||||
@@ -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)}]]]))
|
||||
|
||||
@@ -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)]])
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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}}
|
||||
|
||||
@@ -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 can’t 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 can’t use it right now"}
|
||||
:keycard/error.keycard-locked {:title "Keycard is locked"
|
||||
:description "You can’t 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."]]]))
|
||||
|
||||
@@ -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 "You’ll 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)}))
|
||||
|
||||
@@ -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])}}]]))
|
||||
@@ -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
|
||||
%])}]]}]]]})))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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}]]]]))))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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
|
||||
[]
|
||||
|
||||
@@ -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]))
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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)))))
|
||||
|
||||
@@ -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))))
|
||||
|
||||
@@ -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])))))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||