Compare commits

...
Author SHA1 Message Date
Richard Ramos b9af66f8c8 fix_: use 24h batches to retrieve message history 2024-12-02 09:10:17 -04:00
Mohamed Javid 52a8f8fc22 feat(wallet)_: Show buy and receive cta on zero balance (#21690)
This commit:

- adds wallet-card component
- adds Buy and Receive CTAs above the assets list in the wallet home if the balance is zero

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2024-11-30 00:03:29 +05:30
Brian Sztamfater 6859fb8f8e fix(swap): optimize endpoints calls (#21549)
Signed-off-by: Brian Sztamfater <brian@status.im>
2024-11-29 13:21:44 -03:00
frank f45f96975e refactor_: rename LOG_REQUEST_GO to API_LOGGING_ENABLED (#21677)
https://github.com/status-im/status-go/compare/032eb5b6...c014fbfc

- Rename LOG_REQUEST_GO env variable to API_LOGGING_ENABLED across all env files
- Rename requests.log to api.log for consistency
- Remove standalone initLogging functionality from native modules as it's now
  handled within InitializeApplication
- Add logging configuration (logEnabled, logLevel, apiLoggingEnabled) to
  InitializeApplication params
- Add logDir parameter for Android to ensure proper log file location

This change consolidates logging initialization into the InitializeApplication
flow and makes the naming more consistent with its actual functionality.
2024-11-29 22:01:27 +08:00
Lungu Cristian 53d5ac4ea2 Using Keycard with the new Endpoints for Send/Bridge (#21705)
* fix: routes component annoying warning

* feat: replaced keycard sign with sign-hashes

* feat: using the keycard to sign transactions

* fix: removed unused and weird imports

* chore: removed debug logs

* chore: linting

* fix: removed clearing data on send error
2024-11-29 09:18:12 +02:00
Mohsen 874f07bd77 [#21644] fix: display zero for empty balances asset (#21697) 2024-11-29 08:42:37 +03:00
Mohamed Javid 4efb3c111a fix(wallet)_: error on tapping from/to section in tx confirmation screen (#21695)
This commit fixes the error thrown when tapping the From or To sections in the transaction confirmation screen.

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2024-11-28 22:59:19 +05:30
Alexander de2b5fb935 Update sending transaction end point (#21480) (#21541)
* Update sending transaction end point

* Transaction approval

* Fixes

* Fixes

* Fixes

* Fix for issue 3

* Fixes for 2, 4, 7

* Update

* Fixes

* Fixes
2024-11-28 14:08:35 +01:00
Icaro Motta 5f8d5fd57e perf: Optimize messenger initialization (#21642)
Integrates the optimizations coming from the status-go PR
https://github.com/status-im/status-go/pull/6106. Now messenger filters are
set-up concurrently and we can call the endpoint wakuext_chatsPreview twice:
once for non-community chats and once for community chats. This way, the user
should be able to see some data render before the slow part ends
(community-related data).

Fixes https://github.com/status-im/status-mobile/issues/21456

Areas that may be impacted

- Login
- Loading of data after login (contacts, chats, etc)
2024-11-28 08:46:32 -03:00
Ulises Manuel 763b291629 fix(messenger): List of users overlap composer input (#21641)
* Fix mentions overlapped

* Fix rendering blink
2024-11-27 16:05:56 -06:00
Sean Hagstrom 9f50686042 fix: ensure keycard pin error message is visible (#21691)
Primarily, this change fixes an issue related to the Keycard PIN error message not being visible displayed on some devices. Additionally, this change also clears the PIN entry state after failing to enter the correct Keycard PIN. And this change also allows for the PIN entry component to have two separate error modes: one for the entire component and another for just the info text.
2024-11-27 08:47:56 -08:00
Yevheniia Berdnyk 1652601a82 e2e: fix for fallback tests 2024-11-27 15:47:12 +02:00
Yevheniia Berdnyk 7a44db36fe e2e: updated onboarding flow in tests 2024-11-27 03:30:00 +05:30
Parvesh Monu 338b241c8e Implement onboarding share usage data screen 2024-11-27 03:30:00 +05:30
Volodymyr Kozieiev 91761a92c7 Replace GetWalletToken endpoint with FetchOrGetCachedWalletBalances (#21625) 2024-11-26 13:27:58 +00:00
Sean Hagstrom 4b2a1db12f fix: add spacing between wrong-password error and forgot-password link (#21594) 2024-11-25 10:29:00 -08:00
Mohsen 05d4b4b068 [#21615] fix: hide mercuryo provider (#21626) 2024-11-25 20:58:58 +03:00
flexsurfer 9c9b55fe3e [#21573] Keycard - Sign up with a new Keycard (#21623) 2024-11-25 15:22:21 +01:00
Yevheniia Berdnyk 6ec307a8bb e2e: updated buttons ids 2024-11-25 15:07:37 +05:30
Parvesh Monu 86553eff14 Update create profile and sign in screen 2024-11-25 15:06:18 +05:30
Yevheniia Berdnyk 60911a1dd1 e2e: false failures fixes 2024-11-22 17:54:38 +02:00
119 changed files with 1991 additions and 1604 deletions
+1 -1
View File
@@ -35,4 +35,4 @@ TEST_NETWORKS_ENABLED=1
SHOW_NOT_IMPLEMENTED_FEATURES=0
ENABLE_ALERT_BANNER=0
FLAG_WALLET_CONNECT_ENABLED=1
LOG_REQUEST_GO=1
API_LOGGING_ENABLED=1
+1 -1
View File
@@ -39,4 +39,4 @@ DELETE_MESSAGE_UNDO_TIME_LIMIT=10000
ENABLE_ALERT_BANNER=0
FLAG_WALLET_CONNECT_ENABLED=1
MOBILE_DATA_SYNCING_TOGGLE_ENABLE=0
LOG_REQUEST_GO=1
API_LOGGING_ENABLED=1
+1 -1
View File
@@ -36,4 +36,4 @@ FAST_CREATE_COMMUNITY_ENABLED=1
TEST_NETWORKS_ENABLED=1
ENABLE_ALERT_BANNER=1
FLAG_WALLET_CONNECT_ENABLED=1
LOG_REQUEST_GO=1
API_LOGGING_ENABLED=1
+1 -1
View File
@@ -23,4 +23,4 @@ FAST_CREATE_COMMUNITY_ENABLED=0
TEST_NETWORKS_ENABLED=0
ENABLE_ALERT_BANNER=1
FLAG_WALLET_CONNECT_ENABLED=1
LOG_REQUEST_GO=0
API_LOGGING_ENABLED=0
+1 -1
View File
@@ -20,4 +20,4 @@ FAST_CREATE_COMMUNITY_ENABLED=0
TEST_NETWORKS_ENABLED=0
STATUS_PROXY_STAGE_NAME=prod
FLAG_WALLET_CONNECT_ENABLED=1
LOG_REQUEST_GO=0
API_LOGGING_ENABLED=0
+1 -1
View File
@@ -26,7 +26,7 @@ export STATUS_BACKEND_SERVER_HOST="127.0.0.1:60000"
export STATUS_BACKEND_SERVER_ROOT_DATA_DIR="/path/to/your/root/data/dir"
```
You need to change `STATUS_BACKEND_SERVER_ROOT_DATA_DIR` to your preferred directory and ensure it exists, it should be in absolute path.
All the db files and log files(requests.log/geth.log) and keystore files etc will be stored in this directory.
All the db files and log files(api.log/geth.log) and keystore files etc will be stored in this directory.
### Start the status backend server:
```shell
@@ -248,10 +248,14 @@ class AccountManager(private val reactContext: ReactApplicationContext) : ReactC
@ReactMethod
private fun initializeApplication(request: String, callback: Callback) {
Log.d(TAG, "initializeApplication")
val jsonParams = JSONObject(request)
// for ios, the log dir will be the same as the root data dir, status-go will default to root data dir if logDir is not provided
jsonParams.put("logDir", utils.getPublicStorageDirectory()?.absolutePath)
val jsonString = jsonParams.toString()
StatusBackendClient.executeStatusGoRequestWithCallback(
"InitializeApplication",
request,
{ Statusgo.initializeApplication(request) },
jsonString,
{ Statusgo.initializeApplication(jsonString) },
callback
)
}
@@ -195,25 +195,6 @@ class LogManager(private val reactContext: ReactApplicationContext) : ReactConte
}
}
@ReactMethod
fun initLogging(enabled: Boolean, mobileSystem: Boolean, logLevel: String, logRequestGo: Boolean, callback: Callback) {
val jsonConfig = JSONObject().apply {
put("Enabled", enabled)
put("MobileSystem", mobileSystem)
put("Level", logLevel)
put("File", getGethLogFile().absolutePath)
put("LogRequestGo", logRequestGo)
put("LogRequestFile", getRequestLogFile().absolutePath)
}
val config = jsonConfig.toString()
StatusBackendClient.executeStatusGoRequestWithCallback(
endpoint = "InitLogging",
requestBody = config,
statusgoFunction = { Statusgo.initLogging(config) },
callback = callback
)
}
@ReactMethod(isBlockingSynchronousMethod = true)
fun logFileDirectory(): String? {
return utils.getPublicStorageDirectory()?.absolutePath
@@ -223,7 +204,7 @@ class LogManager(private val reactContext: ReactApplicationContext) : ReactConte
private const val TAG = "LogManager"
private const val gethLogFileName = "geth.log"
private const val statusLogFileName = "Status.log"
private const val requestsLogFileName = "requests.log"
private const val requestsLogFileName = "api.log"
private const val logsZipFileName = "Status-debug-logs.zip"
}
}
@@ -36,7 +36,7 @@ RCT_EXPORT_METHOD(sendLogs:(NSString *)dbJson
NSURL *mainGethLogsFile = [rootUrl URLByAppendingPathComponent:@"geth.log"];
NSURL *mainLogsFile = [logsFolderName URLByAppendingPathComponent:@"geth.log"];
NSURL *requestsLogFile = [rootUrl URLByAppendingPathComponent:@"requests.log"];
NSURL *requestsLogFile = [rootUrl URLByAppendingPathComponent:@"api.log"];
[dbJson writeToFile:dbFile.path atomically:YES encoding:NSUTF8StringEncoding error:nil];
[jsLogs writeToFile:jsLogsFile.path atomically:YES encoding:NSUTF8StringEncoding error:nil];
@@ -44,7 +44,7 @@ RCT_EXPORT_METHOD(sendLogs:(NSString *)dbJson
[fileManager copyItemAtPath:mainGethLogsFile.path toPath:mainLogsFile.path error:nil];
if ([fileManager fileExistsAtPath:requestsLogFile.path]) {
[fileManager copyItemAtPath:requestsLogFile.path toPath:[logsFolderName URLByAppendingPathComponent:@"requests.log"].path error:nil];
[fileManager copyItemAtPath:requestsLogFile.path toPath:[logsFolderName URLByAppendingPathComponent:@"api.log"].path error:nil];
}
[SSZipArchive createZipFileAtPath:zipFile.path withContentsOfDirectory:logsFolderName.path];
@@ -53,43 +53,6 @@ RCT_EXPORT_METHOD(sendLogs:(NSString *)dbJson
callback(@[zipFile.absoluteString]);
}
RCT_EXPORT_METHOD(initLogging:(BOOL)enabled
mobileSystem:(BOOL)mobileSystem
logLevel:(NSString *)logLevel
logRequestGo:(BOOL)logRequestGo
callback:(RCTResponseSenderBlock)callback)
{
NSString *logDirectory = [self logFileDirectory];
NSString *logFilePath = [logDirectory stringByAppendingPathComponent:@"geth.log"];
NSString *logRequestFilePath = [logDirectory stringByAppendingPathComponent:@"requests.log"];
NSDictionary *config = @{
@"Enabled": @(enabled),
@"MobileSystem": @(mobileSystem),
@"Level": logLevel,
@"File": logFilePath,
@"LogRequestGo": @(logRequestGo),
@"LogRequestFile": logRequestFilePath
};
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:config options:0 error:&error];
if (error) {
NSLog(@"Error creating JSON: %@", [error localizedDescription]);
return;
}
NSString *configJson = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
[StatusBackendClient executeStatusGoRequestWithCallback:@"InitLogging"
body:configJson
statusgoFunction:^NSString *{
return StatusgoInitLogging(configJson);
}
callback:callback];
}
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(logFileDirectory) {
NSURL *rootUrl = [Utils getRootUrl];
return rootUrl.path;
@@ -1946,35 +1946,6 @@ void _ConnectionChange(const FunctionCallbackInfo<Value>& args) {
ConnectionChange(arg0, arg1);
}
void _InitLogging(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
if (args.Length() != 1) {
// Throw an Error that is passed back to JavaScript
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8Literal(isolate, "Wrong number of arguments for InitLogging")));
return;
}
// Check the argument types
if (!args[0]->IsString()) {
isolate->ThrowException(Exception::TypeError(
String::NewFromUtf8Literal(isolate, "Wrong argument type for 'logSettingsJSON'")));
return;
}
String::Utf8Value arg0Obj(isolate, args[0]->ToString(context).ToLocalChecked());
char *arg0 = *arg0Obj;
// Call exported Go function, which returns a C string
char *c = InitLogging(arg0);
Local<String> ret = String::NewFromUtf8(isolate, c).ToLocalChecked();
args.GetReturnValue().Set(ret);
delete c;
}
void _RestoreAccountAndLogin(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
@@ -2064,7 +2035,6 @@ void init(Local<Object> exports) {
NODE_SET_METHOD(exports, "hashTransaction", _HashTransaction);
NODE_SET_METHOD(exports, "connectionChange", _ConnectionChange);
NODE_SET_METHOD(exports, "pollSignal", _PollSignal);
NODE_SET_METHOD(exports, "initLogging", _InitLogging);
NODE_SET_METHOD(exports, "restoreAccountAndLogin", _RestoreAccountAndLogin);
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

+15 -11
View File
@@ -2,7 +2,9 @@
(:require
["react-native" :as rn]
["react-native-status-keycard" :default status-keycard]
[oops.core :as oops]
[react-native.platform :as platform]
[schema.core :as schema]
[taoensso.timbre :as log]))
(defonce event-emitter
@@ -254,17 +256,19 @@
(catch on-failure))))
(defn sign
[{pin :pin path :path card-hash :hash on-success :on-success on-failure :on-failure}]
(when (and pin card-hash)
(if path
(.. status-keycard
(signWithPath pin path card-hash)
(then on-success)
(catch on-failure))
(.. status-keycard
(sign pin card-hash)
(then on-success)
(catch on-failure)))))
[{:keys [pin path hash-data]}]
(if path
(oops/ocall status-keycard "signWithPath" pin path hash-data)
(oops/ocall status-keycard "sign" pin hash-data)))
(schema/=> sign
[:=>
[:cat
[:map
[:pin :string]
[:hash-data :string]
[:path {:optional true} [:maybe :string]]]]
:any])
(defn sign-typed-data
[{card-hash :hash on-success :on-success on-failure :on-failure}]
+17 -19
View File
@@ -18,25 +18,23 @@
clock-value
"0x0000000000000000000000000000000000000000000000000000000000000000"))
(rf/defn update-chats-in-app-db
{:events [:chats-list/load-success]}
[{:keys [db]} ^js new-chats-js]
(let [{:keys [all-chats chats-home-list]}
(reduce (fn [acc ^js chat-js]
(let [{:keys [chat-id profile-public-key timeline? community-id active] :as chat}
(data-store.chats/<-rpc-js chat-js)]
(cond-> acc
(and (not profile-public-key) (not timeline?) (not community-id) active)
(update :chats-home-list conj chat-id)
:always
(assoc-in [:all-chats chat-id] chat))))
{:all-chats {}
:chats-home-list #{}}
new-chats-js)]
{:db (assoc db
:chats all-chats
:chats-home-list chats-home-list
:chats/loading? false)}))
(rf/reg-event-fx :chats-list/load-success
(fn [{:keys [db]} [^js new-chats-js]]
(let [{:keys [all-chats chats-home-list]}
(reduce (fn [acc ^js chat-js]
(let [{:keys [chat-id profile-public-key timeline? community-id active] :as chat}
(data-store.chats/<-rpc-js chat-js)]
(cond-> acc
(and (not profile-public-key) (not timeline?) (not community-id) active)
(update :chats-home-list conj chat-id)
:always
(assoc-in [:all-chats chat-id] chat))))
{:all-chats {}
:chats-home-list #{}}
new-chats-js)]
{:db (-> db
(update :chats merge all-chats)
(update :chats-home-list into chats-home-list))})))
(rf/defn load-chat-success
{:events [:chats-list/load-chat-success]}
+2 -2
View File
@@ -134,9 +134,9 @@
unmarshal-members))
(re-frame/reg-fx :fetch-chats-preview
(fn [{:keys [on-success]}]
(fn [{:keys [on-success chat-preview-type]}]
(json-rpc/call {:method "wakuext_chatsPreview"
:params []
:params [chat-preview-type]
:js-response true
:on-success #(on-success ^js %)
:on-error #(log/error "failed to fetch chats" 0 -1 %)})))
@@ -135,7 +135,6 @@
:chat/cooldowns
:chat/last-outgoing-message-sent-at
:chat/spam-messages-frequency
:chats/loading?
:dimensions/window]))]
{:logs/archive-logs [db-json
(if (= transport :email)
-18
View File
@@ -1,18 +0,0 @@
(ns legacy.status-im.utils.prices
(:require
[status-im.common.json-rpc.events :as json-rpc]))
(defn- format-price-resp
[resp to mainnet?]
(if mainnet?
resp
{:ETH (into {}
(for [[_ price] resp]
{(keyword to) price}))}))
(defn get-prices
[from to mainnet? on-success on-error]
(json-rpc/call {:method "wallet_fetchPrices"
:params [from to]
:on-success (fn [resp] (on-success (format-price-resp resp to mainnet?)))
:on-error on-error}))
-4
View File
@@ -535,10 +535,6 @@
[]
(.logFileDirectory ^js (log-manager)))
(defn init-status-go-logging
[{:keys [enable? mobile-system? log-level log-request-go? callback]}]
(.initLogging ^js (log-manager) enable? mobile-system? log-level log-request-go? callback))
(defn get-random-mnemonic
[callback]
(.getRandomMnemonic ^js (account-manager) #(callback (types/json->clj %))))
@@ -0,0 +1,34 @@
(ns quo.components.cards.wallet-card.component-spec
(:require
[quo.components.cards.wallet-card.view :as wallet-card]
[quo.foundations.resources :as resources]
[test-helpers.component :as h]))
(def ^:private base-props
{:image (resources/get-image :keycard-logo)
:title "Buy"
:subtitle "Start investing now"})
(h/describe "cards: wallet card"
(h/test "Test default render"
(let [event (h/mock-fn)]
(h/render-with-theme-provider [wallet-card/view
(assoc base-props
:on-press
event)])
(h/is-truthy (h/get-by-label-text :wallet-card))
(h/is-truthy (h/get-by-text "Buy"))
(h/is-truthy (h/get-by-text "Start investing now"))
(h/is-truthy (h/get-by-label-text :image))
(h/fire-event :press (h/get-by-label-text :wallet-card))
(h/was-called event)))
(h/test "Test render with dismissible prop"
(let [event (h/mock-fn)]
(h/render-with-theme-provider [wallet-card/view
(assoc base-props
:dismissible? true
:on-press-close event)])
(h/is-truthy (h/get-by-label-text :close-icon))
(h/fire-event :press (h/get-by-label-text :icon-container))
(h/was-called event))))
@@ -0,0 +1,14 @@
(ns quo.components.cards.wallet-card.schema)
(def ?schema
[:=>
[:catn
[:props
[:map {:closed true}
[:image :schema.common/image-source]
[:title :string]
[:subtitle :string]
[:dismissible? {:optional true} :boolean]
[:on-press {:optional true} fn?]
[:on-press-close {:optional true} fn?]]]]
:any])
@@ -0,0 +1,30 @@
(ns quo.components.cards.wallet-card.style
(:require [quo.foundations.colors :as colors]
[quo.foundations.shadows :as shadows]))
(defn root-container
[theme]
(assoc (shadows/get 2 theme)
:border-radius 16
:padding-vertical 10
:padding-horizontal 12
:width 161
:background-color (colors/theme-colors colors/white colors/neutral-90 theme)))
(def top-container
{:flex-direction :row
:height 32
:justify-content :space-between
:margin-bottom 8})
(def image
{:height 32
:width 32})
(defn title
[theme]
{:color (colors/theme-colors colors/neutral-100 colors/white theme)})
(defn subtitle
[theme]
{:color (colors/theme-colors colors/neutral-50 colors/neutral-40 theme)})
@@ -0,0 +1,44 @@
(ns quo.components.cards.wallet-card.view
(:require [quo.components.cards.wallet-card.schema :as component-schema]
[quo.components.cards.wallet-card.style :as style]
[quo.components.icon :as icon]
[quo.components.markdown.text :as text]
[quo.theme :as quo.theme]
[react-native.core :as rn]
[react-native.fast-image :as fast-image]
[schema.core :as schema]))
(defn- view-internal
[{:keys [image title subtitle dismissible? on-press on-press-close]}]
(let [theme (quo.theme/use-theme)]
[rn/pressable
{:on-press on-press
:accessibility-label :wallet-card}
[rn/view {:style (style/root-container theme)}
[rn/view {:style style/top-container}
[fast-image/fast-image
{:style style/image
:source image
:accessibility-label :image}]
(when dismissible?
[rn/pressable
{:on-press on-press-close
:accessibility-label :icon-container
:hit-slop {:top 5 :bottom 5 :left 5 :right 5}}
[icon/icon :i/close
{:size 12
:accessibility-label :close-icon}]])]
[text/text
{:style (style/title theme)
:size :paragraph-1
:weight :semi-bold
:number-of-lines 1}
title]
[text/text
{:style (style/subtitle theme)
:size :paragraph-2
:weight :regular
:number-of-lines 1}
subtitle]]]))
(def view (schema/instrument #'view-internal component-schema/?schema))
+6 -5
View File
@@ -2,11 +2,12 @@
(defn container
[container-style]
(merge container-style
{:padding-vertical 7
:padding-horizontal 20
:flex-direction :row
:align-items :flex-start}))
(merge
{:padding-vertical 7
:padding-horizontal 20
:flex-direction :row
:align-items :flex-start}
container-style))
(def index
{:margin-left 5})
+3 -1
View File
@@ -41,7 +41,7 @@
(defn view
[{:keys [title description tag-picture tag-name description-after-tag step-number
customization-color type blur? container-style]
customization-color type blur? container-style icon icon-props]
:or {type :bullet}}]
(let [theme (quo.theme/use-theme)]
[rn/view {:style (style/container container-style)}
@@ -54,6 +54,8 @@
:type (if customization-color :complete :neutral)} step-number]
:lock
[icon/icon :i/locked {:color (get-colors theme blur?)}]
:custom-icon
[icon/icon icon icon-props]
[icon/icon :i/bullet {:color (get-colors theme blur?)}])]
[rn/view {:style style/text-container}
(when title
+2 -2
View File
@@ -6,7 +6,7 @@
[react-native.core :as rn]))
(defn view
[{:keys [number-of-pins number-of-filled-pins error? info]
[{:keys [number-of-pins number-of-filled-pins error? info info-error?]
:or {number-of-pins 6 number-of-filled-pins 0}}]
(let [theme (quo.theme/use-theme)]
[rn/view {:style {:align-items :center}}
@@ -20,7 +20,7 @@
(= i (inc number-of-filled-pins)) :active)}])]
(when info
[text/text
{:style {:color (if error?
{:style {:color (if (or error? info-error?)
(colors/theme-colors colors/danger-50 colors/danger-60 theme)
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme))}
:size :paragraph-2}
+2
View File
@@ -28,6 +28,7 @@
quo.components.calendar.calendar-day.view
quo.components.calendar.calendar-year.view
quo.components.calendar.calendar.view
quo.components.cards.wallet-card.view
quo.components.code.snippet-preview.view
quo.components.code.snippet.view
quo.components.colors.color-picker.view
@@ -241,6 +242,7 @@
;;;; Cards
(def small-option-card quo.components.onboarding.small-option-card.view/small-option-card)
(def keycard quo.components.keycard.view/keycard)
(def wallet-card quo.components.cards.wallet-card.view/view)
;;;; Colors
(def color-picker quo.components.colors.color-picker.view/view)
+17 -32
View File
@@ -1,12 +1,9 @@
(ns status-im.common.log
(:require
[clojure.pprint :as promesa]
[clojure.pprint :as pprint]
[clojure.string :as string]
[native-module.core :as native-module]
[re-frame.core :as re-frame]
[status-im.config :as config]
[taoensso.timbre :as log]
[utils.transforms :as transforms]))
[taoensso.timbre :as log]))
(def logs-queue (atom #queue []))
(def max-log-entries 1000)
@@ -21,33 +18,21 @@
(defn setup
[level]
(let [handle-error (fn [res]
(let [{:keys [error]} (transforms/json->clj res)]
(when-not (string/blank? error)
(log/error "init statusgo logging failed" error))))
logging-params {:enable? true
:mobile-system? false
:log-level level
:log-request-go? config/log-request-go
:callback handle-error}]
(log/merge-config! {:ns-filter {:allow #{"*"} :deny #{"taoensso.sente"}}})
(if (string/blank? level)
(native-module/init-status-go-logging (merge logging-params {:log-level "WARN"}))
(do
(log/set-min-level! (-> level
string/lower-case
keyword))
(log/merge-config!
{:output-fn (fn [& data]
(let [res (apply log/default-output-fn data)]
(add-log-entry res)
res))
:middleware [(fn [data]
(update data
:vargs
(partial mapv
#(if (string? %) % (with-out-str (promesa/pprint %))))))]})
(native-module/init-status-go-logging logging-params)))))
(log/merge-config! {:ns-filter {:allow #{"*"} :deny #{"taoensso.sente"}}})
(when-not (string/blank? level)
(log/set-min-level! (-> level
string/lower-case
keyword))
(log/merge-config!
{:output-fn (fn [& data]
(let [res (apply log/default-output-fn data)]
(add-log-entry res)
res))
:middleware [(fn [data]
(update data
:vargs
(partial mapv
#(if (string? %) % (with-out-str (pprint/pprint %))))))]})))
(re-frame/reg-fx
:logs/set-level
@@ -1,19 +0,0 @@
(ns status-im.common.metrics-confirmation-modal.style)
(def points-wrapper
{:margin-top 11
:margin-horizontal 20
:gap 21
:margin-bottom 8})
(def item-text
{:margin-left -24})
(def info-text
{:margin-top -5})
(def privacy-policy
{:margin-top 4
:margin-horizontal 20
:margin-bottom 20
:align-items :center})
@@ -1,119 +0,0 @@
(ns status-im.common.metrics-confirmation-modal.view
(:require
[quo.core :as quo]
[quo.foundations.colors :as colors]
[quo.theme]
[react-native.core :as rn]
[status-im.common.metrics-confirmation-modal.style :as style]
[status-im.common.privacy.view :as privacy]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- dismiss-keyboard
[]
(rf/dispatch [:dismiss-keyboard]))
(defn- hide-bottom-sheet
[]
(rf/dispatch [:hide-bottom-sheet]))
(def ^:private will-receive-for-current-points
[:t/number-of-messages-sent
:t/connected-peers
:t/successful-messages-rate
:t/connection-type
:t/os-app-version-bandwidth])
(def ^:private will-receive-for-all-points
[:t/action-logs
:t/ip-addresses-uuid])
(def ^:private not-receive-points
[:t/your-profile-information
:t/your-addresses
:t/information-you-input-and-send])
(defn- bullet-points
[{:keys [title points lock?]}]
[rn/view
[quo/text {:weight :semi-bold}
title]
(for [label points]
^{:key label}
[quo/markdown-list
{:description (i18n/label label)
:blur? true
:type (when lock? :lock)
:container-style style/item-text}])])
(defn- on-share-usage
[]
(rf/dispatch [:centralized-metrics/toggle-centralized-metrics true true])
(hide-bottom-sheet))
(defn- on-do-not-share
[]
(rf/dispatch [:centralized-metrics/toggle-centralized-metrics false true])
(hide-bottom-sheet))
(declare view)
(defn- on-privacy-policy-press
[]
(rf/dispatch
[:show-bottom-sheet
{:content privacy/privacy-statement
:on-close (fn []
(rf/dispatch [:show-bottom-sheet
{:content view
:theme :dark
:shell? true}]))
:theme :dark
:shell? true}]))
(defn- privacy-policy-text
[]
[rn/view {:style style/privacy-policy}
[quo/text
[quo/text
{:style {:color colors/white-opa-50}
:size :paragraph-2}
(i18n/label :t/more-details-in-privacy-policy-1)]
[quo/text
{:size :paragraph-2
:weight :bold
:on-press on-privacy-policy-press}
(i18n/label :t/more-details-in-privacy-policy-2)]]])
(defn view
[]
(rn/use-mount #(dismiss-keyboard))
[:<>
[quo/drawer-top
{:title (i18n/label :t/help-us-improve-status)
:description (i18n/label :t/collecting-usage-data)}]
[rn/scroll-view
[rn/view {:style style/points-wrapper}
[bullet-points
{:title (i18n/label :t/we-will-receive-from-all-profiles)
:points will-receive-for-all-points}]
[bullet-points
{:title (i18n/label :t/we-will-receive-from-the-current-profile)
:points will-receive-for-current-points}]
[bullet-points
{:title (i18n/label :t/what-we-wont-receive)
:points not-receive-points
:lock? true}]
[quo/text
{:size :paragraph-2
:style style/info-text}
(i18n/label :t/sharing-usage-data-can-be-turned-off)]]]
[quo/bottom-actions
{:actions :two-actions
:blur? true
:button-one-label (i18n/label :t/help-us-improve-status)
:button-one-props {:on-press on-share-usage}
:button-two-label (i18n/label :t/not-now)
:button-two-props {:type :grey
:on-press on-do-not-share}}]
[privacy-policy-text]])
+4 -1
View File
@@ -17,6 +17,7 @@
:generate-keys1 (js/require "../resources/images/ui2/generating-keys-1.png")
:ethereum-address (js/require "../resources/images/ui2/ethereum-address.png")
:use-keycard (js/require "../resources/images/ui2/keycard.png")
:use-recovery-phrase (js/require "../resources/images/ui2/recovery-phrase.png")
:check-your-keycard (js/require "../resources/images/ui2/check-your-keycard.png")
:onboarding-illustration (js/require "../resources/images/ui2/onboarding_illustration.png")
:qr-code (js/require "../resources/images/ui2/qr-code.png")
@@ -36,7 +37,9 @@
:nfc-success (js/require "../resources/images/ui2/nfc-success.png")
:preparing-status (js/require "../resources/images/ui2/preparing-status.png")
:syncing-devices (js/require "../resources/images/ui2/syncing_devices.png")
:syncing-wrong (js/require "../resources/images/ui2/syncing_wrong.png")})
:syncing-wrong (js/require "../resources/images/ui2/syncing_wrong.png")
:buy (js/require "../resources/images/ui2/buy.png")
:receive (js/require "../resources/images/ui2/receive.png")})
(def ui-themed
{:angry-man
+6 -6
View File
@@ -36,15 +36,15 @@
"wallet"
{:fx [[:dispatch [:wallet/signal-received event-js]]]}
"wallet.sign.transactions"
{:fx [[:dispatch
[:standard-auth/authorize-with-keycard
{:on-complete #(rf/dispatch [:keycard/sign-hash %
(first (transforms/js->clj event-js))])}]]]}
"wallet.suggested.routes"
{:fx [[:dispatch [:wallet/handle-suggested-routes (transforms/js->clj event-js)]]]}
"wallet.router.sign-transactions"
{:fx [[:dispatch [:wallet/sign-transactions-signal-received (transforms/js->clj event-js)]]]}
"wallet.router.transactions-sent"
{:fx [[:dispatch [:wallet/transactions-sent-signal-received (transforms/js->clj event-js)]]]}
"envelope.sent"
(messages.transport/update-envelopes-status
cofx
@@ -116,6 +116,7 @@
:on-close #(rf/dispatch [:standard-auth/reset-login-password])
:content (fn []
[keycard.pin/auth {:on-complete on-complete}])}]]]})
(rf/reg-event-fx :standard-auth/authorize-with-keycard authorize-with-keycard)
(defn authorize-with-password
@@ -3,6 +3,7 @@
(def error-message
{:margin-top 8
:flex-direction :row
:gap 4
:align-items :center})
(def auth-button
+1 -1
View File
@@ -71,7 +71,7 @@
;; CONFIG VALUES
(def log-level (string/upper-case (get-config :LOG_LEVEL "")))
(def log-request-go (enabled? (get-config :LOG_REQUEST_GO "0")))
(def api-logging-enabled? (enabled? (get-config :API_LOGGING_ENABLED "0")))
(def fleet (get-config :FLEET ""))
(def apn-topic (get-config :APN_TOPIC "im.status.ethereum"))
(def max-installations 2)
+3
View File
@@ -76,6 +76,9 @@
(def ^:const timeline-chat-type 5)
(def ^:const community-chat-type 6)
(def ^:const chat-preview-type-community 0)
(def ^:const chat-preview-type-non-community 1)
(def ^:const contact-request-message-state-none 0)
(def ^:const contact-request-message-state-pending 1)
(def ^:const contact-request-message-state-accepted 2)
@@ -36,18 +36,10 @@
(assoc :centralized-metrics/enabled? enabled?)
(assoc :centralized-metrics/onboarding-enabled? (and onboarding? enabled?)))}))
(rf/reg-event-fx :centralized-metrics/check-modal
(fn [{:keys [db]} [modal-view]]
(rf/reg-event-fx :centralized-metrics/check-user-confirmation
(fn [{:keys [db]}]
(when-not (:centralized-metrics/user-confirmed? db)
{:fx [[:dispatch
[:show-bottom-sheet
{:content (fn [] [modal-view])
;; When in the profiles screen do biometric auth after the metrics sheet is dismissed
;; https://github.com/status-im/status-mobile/issues/20932
:on-close (when (= (:view-id db) :screen/profile.profiles)
#(rf/dispatch [:profile.login/login-with-biometric-if-available
(get-in db [:profile/login :key-uid])]))
:shell? true}]]]})))
{:fx [[:dispatch [:navigate-to :screen/onboarding.share-usage]]]})))
(rf/reg-fx :effects.centralized-metrics/track
(fn [event]
@@ -2,29 +2,39 @@
(:require
[quo.foundations.colors :as colors]
[react-native.platform :as platform]
[react-native.reanimated :as reanimated]
[status-im.contexts.chat.messenger.composer.constants :as constants]))
(defn shadow
[theme]
(defn- add-shadow
[theme styles]
(if platform/ios?
{:shadow-radius (colors/theme-colors 30 50 theme)
:shadow-opacity (colors/theme-colors 0.1 0.7 theme)
:shadow-color colors/neutral-100
:shadow-offset {:width 0 :height (colors/theme-colors 8 12 theme)}}
{:elevation 10}))
(assoc styles
:shadow-radius (colors/theme-colors 30 50 theme)
:shadow-opacity (colors/theme-colors 0.1 0.7 theme)
:shadow-color colors/neutral-100
:shadow-offset {:width 0 :height (colors/theme-colors 8 12 theme)})
(assoc styles :elevation 10)))
(defn inner
[top theme]
(add-shadow theme
{:position :absolute
:top (- (+ 8 top))
:left 8
:right 8
:border-radius 16
:z-index 4
:max-height constants/mentions-max-height
:background-color (colors/theme-colors colors/white colors/neutral-95 theme)}))
(defn container
[opacity top theme]
(reanimated/apply-animations-to-style
{:opacity opacity}
(merge
{:position :absolute
:top (- (+ 8 top))
:left 8
:right 8
:border-radius 16
:z-index 4
:max-height constants/mentions-max-height
:background-color (colors/theme-colors colors/white colors/neutral-95 theme)}
(shadow theme))))
[{:opacity opacity}
(add-shadow theme
{:position :absolute
:top (- (+ 8 top))
:left 8
:right 8
:border-radius 16
:z-index 4
:max-height constants/mentions-max-height
:background-color (colors/theme-colors colors/white colors/neutral-95 theme)})])
@@ -10,37 +10,36 @@
[status-im.contexts.chat.messenger.messages.constants :as messages.constants]
[utils.re-frame :as rf]))
(defn mention-item
[user]
[contact-list-item/contact-list-item
{:on-press #(rf/dispatch [:chat.ui/select-mention user])}
user])
(defn- mention-item
[[user-key user]]
(with-meta
[contact-list-item/contact-list-item {:on-press #(rf/dispatch [:chat.ui/select-mention user])}
user]
{:key user-key}))
(defn view
[layout-height]
(let [suggestions (rf/sub [:chat/mention-suggestions])
suggestions? (seq suggestions)
theme (quo.theme/use-theme)
opacity (reanimated/use-shared-value (if suggestions? 1 0))
[suggestions-state set-suggestions-state] (rn/use-state suggestions)
top (min constants/mentions-max-height
(* (count suggestions-state) 56)
(- @layout-height
(+ (safe-area/get-top)
messages.constants/top-bar-height
5)))]
(let [suggestions (rf/sub [:chat/mention-suggestions])
suggestions? (seq suggestions)
theme (quo.theme/use-theme)
opacity (reanimated/use-shared-value (if suggestions? 1 0))
[suggestions-state
set-suggestions-state] (rn/use-state suggestions)
top (min constants/mentions-max-height
(* (count suggestions-state) 56)
(- @layout-height
(+ (safe-area/get-top)
messages.constants/top-bar-height
5)))]
(rn/use-effect
(fn []
(if suggestions?
(set-suggestions-state suggestions)
(js/setTimeout #(set-suggestions-state suggestions) 300))
(reanimated/animate opacity (if suggestions? 1 0)))
[suggestions?])
[reanimated/view
{:style (style/container opacity top theme)}
[rn/flat-list
{:keyboard-should-persist-taps :always
:data (vals suggestions-state)
:key-fn :key
:render-fn mention-item
:accessibility-label :mentions-list}]]))
[suggestions])
[reanimated/view {:style (style/container opacity top theme)}
[rn/scroll-view
{:accessibility-label :mentions-list
:keyboard-should-persist-taps :always}
(map mention-item suggestions-state)]]))
@@ -225,7 +225,8 @@
distance-from-list-top
chat-list-scroll-y
(on-scroll-fn distance-atom layout-height)))
:style {:background-color (colors/theme-colors colors/white
:style {:height "100%"
:background-color (colors/theme-colors colors/white
colors/neutral-95
theme)}
:content-container-style (style/list-paddings add-padding-bottom?)
@@ -0,0 +1,101 @@
(ns status-im.contexts.keycard.create.events
(:require [clojure.string :as string]
[utils.re-frame :as rf]
[utils.security.core :as security]))
(rf/reg-event-fx :keycard/create.check-empty-card
(fn [_]
{:fx [[:dispatch
[:keycard/connect
{:on-error
(fn [error]
(if (= error :keycard/error.keycard-blank)
(do
(rf/dispatch [:keycard/disconnect])
(rf/dispatch [:open-modal :screen/keycard.empty-create]))
(rf/dispatch [:keycard/on-application-info-error error])))}]]]}))
(rf/reg-event-fx :keycard/create.get-phrase
(fn [{:keys [db]}]
{:db (assoc-in db [:keycard :create] nil)
:fx [[:dispatch [:navigate-back]]
[:dispatch
[:open-modal :screen/backup-recovery-phrase-dark
{:on-success #(rf/dispatch [:keycard/create.phrase-backed-up %])}]]]}))
(rf/reg-event-fx :keycard/create.phrase-backed-up
(fn [{:keys [db]} [masked-phrase-vector]]
{:db (assoc-in db
[:keycard :create :masked-phrase]
(->> masked-phrase-vector
security/safe-unmask-data
(string/join " ")
security/mask-data))
:fx [[:dispatch [:keycard/create.create-or-enter-pin]]]}))
(rf/reg-event-fx :keycard/create.create-or-enter-pin
(fn [{:keys [db]}]
(let [{:keys [initialized?]} (get-in db [:keycard :application-info])]
{:fx [[:dispatch [:navigate-back]]
(if initialized?
[:dispatch
[:open-modal :screen/keycard.pin.enter
{:on-complete (fn [new-pin]
(rf/dispatch [:keycard/create.save-pin new-pin])
(rf/dispatch [:keycard/create.start]))}]]
[:dispatch
[:open-modal :screen/keycard.pin.create
{:on-complete (fn [new-pin]
(rf/dispatch [:navigate-back])
(rf/dispatch [:keycard/create.save-pin new-pin])
(rf/dispatch [:open-modal :screen/keycard.create.ready-to-add]))}]])]})))
(rf/reg-event-fx :keycard/create.save-pin
(fn [{:keys [db]} [pin]]
{:db (assoc-in db [:keycard :create :pin] pin)}))
(rf/reg-event-fx :keycard/create.start
(fn [_]
{:fx [[:dispatch
[:keycard/connect
{:on-error
(fn [error]
(if (= error :keycard/error.keycard-blank)
(rf/dispatch [:keycard/create.continue])
(rf/dispatch [:keycard/on-application-info-error error])))}]]]}))
(defn get-application-info-and-continue
[init?]
(rf/dispatch [:keycard/get-application-info
{:on-success #(rf/dispatch [:keycard/create.continue])
:on-error
(fn [error]
(if (or (= error :keycard/error.keycard-blank)
(and (not init?) (= error :keycard/error.keycard-wrong-profile)))
(rf/dispatch [:keycard/create.continue])
(rf/dispatch [:keycard/on-application-info-error error])))}]))
(rf/reg-event-fx :keycard/create.continue
(fn [{:keys [db]}]
(let [{:keys [initialized? has-master-key?]} (get-in db [:keycard :application-info])
{:keys [masked-phrase pin]} (get-in db [:keycard :create])]
(cond
(not initialized?)
{:fx [[:keycard/init-card
{:pin pin
:on-success #(get-application-info-and-continue true)}]]}
(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 false)
:on-failure #(rf/dispatch [:keycard/on-action-with-pin-error %])}]]}
:else
{:fx [[:effects.keycard/get-more-keys
{:pin pin
:on-success #(rf/dispatch [:keycard.login/recover-profile-and-login %])
:on-failure #(rf/dispatch [:keycard/on-action-with-pin-error %])}]]}))))
@@ -0,0 +1,62 @@
(ns status-im.contexts.keycard.create.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]
[status-im.constants :as constants]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn view
[]
[:<>
[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/create-profile-keycard)}]
[rn/view {:style {:padding-horizontal 20 :padding-top 20}}
[quo/small-option-card
{:variant :main
:title (i18n/label :t/check-keycard)
:subtitle (i18n/label :t/see-keycard-ready)
:button-label (i18n/label :t/scan-keycard)
:accessibility-label :get-keycard
:image (resources/get-image :check-your-keycard)
:on-press #(rf/dispatch [:keycard/create.check-empty-card])}]
[rn/view {:style {:height 12}}]
[quo/small-option-card
{:variant :icon
:title (i18n/label :t/learn-more-keycard)
:subtitle (i18n/label :t/secure-wallet-card)
:accessibility-label :setup-keycard
:image (resources/get-image :use-keycard)
:on-press #(rf/dispatch [:browser.ui/open-url constants/get-keycard-url])}]]
[rn/view {:style {:flex 1}}]
[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)}]])
(defn ready-to-add
[]
[:<>
[quo/page-nav
{:icon-name :i/arrow-left
:on-press events-helper/navigate-back}]
[quo/page-top
{:title (i18n/label :t/ready-add-keypair-keycard)
:description :text
:description-text ""}]
[rn/view {:style {:flex 1 :align-items :center :justify-content :center}}
[rn/image
{:resize-mode :contain
:source (resources/get-image :generate-keys1)}]]
[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/create.start])}}]])
+18 -1
View File
@@ -1,11 +1,13 @@
(ns status-im.contexts.keycard.effects
(:require [keycard.keycard :as keycard]
[native-module.core :as native-module]
[promesa.core :as promesa]
[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.hex :as hex]
[utils.re-frame :as rf]))
(defonce ^:private active-listeners (atom []))
@@ -50,7 +52,22 @@
(rf/reg-fx :effects.keycard/sign
(fn [args]
(keycard/sign (keycard.utils/wrap-handlers args))))
(-> (keycard/sign args)
(promesa/then (keycard.utils/get-on-success args))
(promesa/catch (keycard.utils/get-on-failure args)))))
(rf/reg-fx :effects.keycard/sign-hashes
(fn [{:keys [hashes pin path on-success] :as args}]
(-> (promesa/all
(for [hash-data hashes]
(-> (keycard/sign {:pin pin
:path path
:hash-data (hex/normalize-hex hash-data)})
(promesa/then (fn [signature]
{:signature signature
:message hash-data})))))
(promesa/then on-success)
(promesa/catch (keycard.utils/get-on-failure args)))))
(rf/reg-fx :keycard/init-card
(fn [args]
+25 -1
View File
@@ -7,6 +7,30 @@
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn create
[]
[:<>
[quo/page-nav
{:icon-name :i/close
:on-press events-helper/navigate-back}]
[quo/page-top
{:title (i18n/label :t/keycard-empty)
:description :text
:description-text (i18n/label :t/what-to-do)}]
[rn/view {:style {:padding-horizontal 28 :padding-top 20}}
[quo/small-option-card
{:variant :main
:title (i18n/label :t/create-new-profile)
:subtitle (i18n/label :t/new-key-pair-keycard)
:button-label (i18n/label :t/lets-go)
:accessibility-label :create-new-profile-keycard
:image (resources/get-image :keycard-buy)
:on-press #(rf/dispatch [:keycard/create.get-phrase])}]]
[quo/information-box
{:type :default
:style {:margin-top 32 :margin-horizontal 28}}
(i18n/label :t/add-key-pair-desktop)]])
(defn view
[]
[:<>
@@ -23,7 +47,7 @@
:title (i18n/label :t/import-key-pair-keycard)
:subtitle (i18n/label :t/use-keycard-login-sign)
:button-label (i18n/label :t/import-profile-key-pair)
:accessibility-label :get-keycard
:accessibility-label :import-key-pair-keycard
:image (resources/get-image :keycard-buy)
:on-press (fn []
(rf/dispatch
@@ -1,5 +1,6 @@
(ns status-im.contexts.keycard.events
(:require [re-frame.core :as rf]
status-im.contexts.keycard.create.events
status-im.contexts.keycard.login.events
status-im.contexts.keycard.migrate.events
status-im.contexts.keycard.migrate.re-encrypting.events
@@ -44,6 +45,7 @@
(when-not (or tag-was-lost? (nil? pin-retries-count))
{:db (-> db
(assoc-in [:keycard :application-info :pin-retry-counter] pin-retries-count)
(assoc-in [:keycard :pin :text] "")
(assoc-in [:keycard :pin :status] :error))
:fx [[:dispatch [:keycard/disconnect]]
(when (zero? pin-retries-count)
@@ -14,5 +14,4 @@
{:icon-name :i/close
:on-press events-helper/navigate-back}]
[quo/page-top {:title (i18n/label :t/enter-keycard-pin)}]
[rn/view {:style {:flex 1}}]
[keycard.pin/auth {:on-complete on-complete}]]))
+7 -4
View File
@@ -13,17 +13,20 @@
pin-retry-counter (rf/sub [:keycard/pin-retry-counter])
error? (or error? (= status :error))]
(rn/use-unmount #(rf/dispatch [:keycard.pin/clear]))
[rn/view {:padding-bottom 12 :flex 1}
[rn/view {:flex 1 :justify-content :center :align-items :center :padding 34}
[rn/view
{:style {:flex 1
:gap 34
:padding-bottom 12}}
[rn/view {:style {:flex 1 :justify-content :center :align-items :center}}
[quo/pin-input
{:blur? false
:number-of-pins constants/pincode-length
:number-of-filled-pins (count text)
:error? error?
:info-error? error?
:info (when error?
(if (not (string/blank? error-message))
error-message
(i18n/label :t/pin-retries-left {:number pin-retry-counter})))}]]
(i18n/label-pluralize pin-retry-counter :t/pin-retries-left)))}]]
[quo/numbered-keyboard
{:delete-key? true
:on-delete #(rf/dispatch [:keycard.pin/delete-pressed])
+11 -20
View File
@@ -2,33 +2,24 @@
(:require [utils.address]
[utils.re-frame :as rf]))
(defn get-signature-map
[tx-hash signature]
{tx-hash {:r (subs signature 0 64)
:s (subs signature 64 128)
:v (subs signature 128 130)}})
(rf/reg-event-fx :keycard/sign
(rf/reg-event-fx :keycard/sign-hashes
(fn [_ [data]]
{:effects.keycard/sign data}))
{:effects.keycard/sign-hashes 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])
path (get-in db [:wallet :accounts current-address :path])
key-uid (get-in db [:profile/profile :key-uid])]
(rf/reg-event-fx :keycard/connect-and-sign-hashes
(fn [{:keys [db]} [{:keys [keycard-pin address hashes on-success on-failure]}]]
(let [{:keys [path key-uid]} (get-in db [:wallet :accounts address])]
{:fx [[:dispatch
[:keycard/connect
{:key-uid key-uid
:on-success
(fn []
(rf/dispatch
[:keycard/sign
{:pin pin-text
[:keycard/sign-hashes
{:pin keycard-pin
:path path
:hash (utils.address/naked-address tx-hash)
:on-success (fn [signature]
:hashes hashes
:on-success (fn [signatures]
(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 %])}]))}]]]})))
(when on-success (on-success signatures)))
:on-failure on-failure}]))}]]]})))
+5 -1
View File
@@ -57,9 +57,13 @@
data
(update data :key-uid address/normalized-hex)))
(defn format-success-data
[data]
(normalize-key-uid (transforms/js->clj data)))
(defn get-on-success
[{:keys [on-success]}]
#(when on-success (on-success (normalize-key-uid (transforms/js->clj %)))))
#(when on-success (on-success (format-success-data %))))
(defn get-on-failure
[{:keys [on-failure]}]
@@ -3,11 +3,7 @@
[quo.foundations.colors :as colors]))
(def content-container
{:position :absolute
:top 0
:bottom 0
:left 0
:right 0})
{:flex 1})
(def options-container
{:padding-top 12
@@ -5,7 +5,7 @@
[react-native.core :as rn]
[react-native.safe-area :as safe-area]
[status-im.common.check-before-syncing.view :as check-before-syncing]
[status-im.common.metrics-confirmation-modal.view :as metrics-modal]
[status-im.common.events-helper :as events-helper]
[status-im.common.resources :as resources]
[status-im.config :as config]
[status-im.contexts.onboarding.create-or-sync-profile.style :as style]
@@ -35,7 +35,7 @@
{:on-submit navigate-to-sign-in-by-syncing}])
:shell? true}]))
(defn- navigate-to-sign-in-by-seed-phrase
(defn- navigate-to-sign-in-by-recovery-phrase
[create-profile?]
(rf/dispatch [:syncing/clear-syncing-fallback-flow])
(rf/dispatch [:onboarding/navigate-to-sign-in-by-seed-phrase
@@ -50,80 +50,97 @@
(* 2 16) ;; spacing between items
220)) ;; extra spacing (top bar)
(defn- create-profile-option-card
(defn- start-fresh-main-card
[window-height]
[quo/small-option-card
{:variant :main
:title (i18n/label :t/generate-keys)
:subtitle (i18n/label :t/generate-keys-subtitle)
:title (i18n/label :t/start-fresh)
:subtitle (i18n/label :t/start-fresh-subtitle)
:button-label (i18n/label :t/lets-go)
:accessibility-label :generate-key-option-card
:accessibility-label :start-fresh-main-card
:image (resources/get-image :generate-keys)
:max-height (option-card-max-height window-height)
:on-press navigate-to-create-profile}])
(defn- sync-profile-option-card
(defn- log-in-with-recovery-phrase-main-card
[window-height]
[quo/small-option-card
{:variant :main
:title (i18n/label :t/sign-in-by-syncing)
:subtitle (i18n/label :t/if-you-have-status-on-another-device)
:button-label (i18n/label :t/scan-sync-code)
:accessibility-label :scan-sync-code-option-card
:image (resources/get-image :generate-keys)
:title (i18n/label :t/log-in-with-recovery-phrase)
:subtitle (i18n/label :t/log-in-with-recovery-phrase-subtitle)
:button-label (i18n/label :t/use-recovery-phrase)
:accessibility-label :log-in-with-recovery-phrase-main-card
:image (resources/get-image :use-recovery-phrase)
:max-height (option-card-max-height window-height)
:on-press #(navigate-to-sign-in-by-recovery-phrase false)}])
(defn- use-recovery-phrase-icon-card
[]
[quo/small-option-card
{:variant :icon
:title (i18n/label :t/use-a-recovery-phrase)
:subtitle (i18n/label :t/use-a-recovery-phrase-subtitle)
:accessibility-label :use-a-recovery-phrase-icon-card
:image (resources/get-image :ethereum-address)
:on-press #(navigate-to-sign-in-by-recovery-phrase true)}])
(defn- log-in-by-syncing-icon-card
[]
[quo/small-option-card
{:variant :icon
:title (i18n/label :t/log-in-by-syncing)
:subtitle (i18n/label :t/log-in-by-syncing-subtitle)
:accessibility-label :log-in-by-syncing-icon-card
:image (resources/get-image :ethereum-address)
:on-press show-check-before-syncing}])
(defn- use-empty-keycard-icon-card
[]
[quo/small-option-card
{:variant :icon
:title (i18n/label :t/use-an-empty-keycard)
:subtitle (i18n/label :t/use-an-empty-keycard-subtitle)
:accessibility-label :use-an-empty-keycard-icon-card
:image (resources/get-image :use-keycard)
:on-press #(rf/dispatch [:open-modal :screen/keycard.create-profile])}])
(defn- log-in-with-keycard-icon-card
[]
[quo/small-option-card
{:variant :icon
:title (i18n/label :t/log-in-with-keycard)
:subtitle (i18n/label :t/log-in-with-keycard-subtitle)
:accessibility-label :log-in-with-keycard
:image (resources/get-image :use-keycard)
:on-press (fn []
(rf/dispatch [:open-modal :screen/keycard.check
{:on-press #(rf/dispatch [:keycard.login/check-card])}]))}])
(defn sign-in-options
[sign-in-type]
(let [window-height (rf/sub [:dimensions/window-height])
create-profile? (= sign-in-type :create-profile)
nav-to-seed-phrase-with-cur-screen (rn/use-callback
#(navigate-to-sign-in-by-seed-phrase
create-profile?)
[create-profile?])
main-option-card (if create-profile?
create-profile-option-card
sync-profile-option-card)]
[create-profile?]
(let [window-height (rf/sub [:dimensions/window-height])]
[rn/view {:style style/options-container}
[quo/text
{:style style/title
:size :heading-1
:weight :semi-bold}
(i18n/label (if create-profile? :t/create-profile :t/sync-or-recover-profile))]
[main-option-card window-height]
(i18n/label (if create-profile? :t/create-profile :t/log-in))]
(if create-profile?
[start-fresh-main-card window-height]
[log-in-with-recovery-phrase-main-card window-height])
[rn/view {:style style/subtitle-container}
[quo/text
{:style style/subtitle
:size :paragraph-2
:weight :medium}
(i18n/label (if create-profile? :t/experienced-web3 :t/dont-have-statatus-on-another-device))]]
[rn/view
[quo/small-option-card
{:variant :icon
:title (i18n/label :t/use-recovery-phrase)
:subtitle (i18n/label :t/use-recovery-phrase-subtitle)
:accessibility-label :use-recovery-phrase-option-card
:image (resources/get-image :ethereum-address)
:on-press nav-to-seed-phrase-with-cur-screen}]
[rn/view {:style style/space-between-suboptions}]
[quo/small-option-card
{:variant :icon
:title (i18n/label :t/use-keycard)
:subtitle (i18n/label :t/profile-keys-on-keycard)
:accessibility-label :use-keycard-option-card
:image (resources/get-image :use-keycard)
:on-press (fn []
(rf/dispatch [:open-modal :screen/keycard.check
{:on-press
#(rf/dispatch
[:keycard.login/check-card])}]))}]]]))
(defn- navigate-back
[]
(rf/dispatch [:onboarding/overlay-dismiss])
(rf/dispatch [:navigate-back]))
(i18n/label :t/other-options)]]
(if create-profile?
[use-recovery-phrase-icon-card]
[log-in-by-syncing-icon-card])
[rn/view {:style style/space-between-suboptions}]
(if create-profile?
[use-empty-keycard-icon-card]
[log-in-with-keycard-icon-card])]))
(defn- navigate-to-quo-preview
[]
@@ -131,7 +148,6 @@
(defn- internal-view
[sign-in-type]
(rn/use-mount #(rf/dispatch [:centralized-metrics/check-modal metrics-modal/view]))
(let [{:keys [top]} (safe-area/get-insets)]
[rn/view {:style style/content-container}
[quo/page-nav
@@ -139,13 +155,13 @@
:type :no-title
:background :blur
:icon-name :i/arrow-left
:on-press navigate-back
:on-press events-helper/navigate-back
:right-side [{:icon-name :i/info
:on-press getting-started-doc/show-as-bottom-sheet}
(when config/quo-preview-enabled?
{:icon-name :i/reveal-whitelist
:on-press navigate-to-quo-preview})]}]
[sign-in-options sign-in-type]]))
[sign-in-options (= sign-in-type :create-profile)]]))
(defn create-profile
[]
@@ -52,13 +52,16 @@
[]
(when-let [blur-show-fn @overlay/blur-show-fn-atom]
(blur-show-fn))
(rf/dispatch [:open-modal :screen/onboarding.sync-or-recover-profile]))
(rf/dispatch [:open-modal
:screen/onboarding.share-usage
{:next-screen :screen/onboarding.sync-or-recover-profile}]))
(defn- create-profile
[]
(when-let [blur-show-fn @overlay/blur-show-fn-atom]
(blur-show-fn))
(rf/dispatch [:open-modal :screen/onboarding.new-to-status]))
(rf/dispatch [:open-modal :screen/onboarding.share-usage
{:next-screen :screen/onboarding.new-to-status}]))
(defn view
[]
@@ -0,0 +1,58 @@
(ns status-im.contexts.onboarding.share-usage.learn-more-sheet
(:require
[quo.core :as quo]
[quo.foundations.colors :as colors]
[quo.theme]
[react-native.core :as rn]
[status-im.common.terms.view :as terms]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(def ^:private bullet-points-data
[[:t/help-us-improve-status-bullet-point-1 true]
[:t/help-us-improve-status-bullet-point-2 true]
[:t/help-us-improve-status-bullet-point-3 false]
[:t/help-us-improve-status-bullet-point-4 false]
[:t/help-us-improve-status-bullet-point-5 false]])
(defn- privacy-policy-text
[]
[rn/view {:style {:margin-horizontal 20 :margin-vertical 8}}
[quo/text
[quo/text
{:style {:color colors/white-opa-50}
:size :paragraph-2}
(i18n/label :t/more-details-in-privacy-policy-1-onboarding)]
[quo/text
{:size :paragraph-2
:weight :bold
:on-press #(rf/dispatch [:show-bottom-sheet {:content terms/terms-of-use :shell? true}])}
(i18n/label :t/more-details-in-privacy-policy-2)]]])
(defn- bullet-points
[]
[:<>
(for [[label collected?] bullet-points-data]
^{:key label}
[quo/markdown-list
{:description (i18n/label label)
:blur? true
:type :custom-icon
:container-style {:padding-vertical 5}
:icon (if collected? :i/check-circle :i/clear)
:icon-props (if collected?
{:no-color true}
{:size 20 :color colors/danger-60 :color-2 colors/white})}])])
(defn view
[]
[:<>
[quo/drawer-top
{:title (i18n/label :t/help-us-improve-status)}]
[quo/text
{:size :paragraph-1
:style {:padding-horizontal 20}}
(i18n/label :t/help-us-improve-status-subtitle)]
[rn/view {:style {:padding-vertical 8}}
[bullet-points]]
[privacy-policy-text]])
@@ -0,0 +1,18 @@
(ns status-im.contexts.onboarding.share-usage.style)
(def title-container
{:margin-horizontal 20
:padding-bottom 20
:padding-top 12})
(defn page-illustration
[width]
{:flex 1
:margin-top 12
:margin-bottom 10
:width width})
(defn buttons
[insets]
{:margin 20
:margin-bottom (+ 12 (:bottom insets))})
@@ -0,0 +1,62 @@
(ns status-im.contexts.onboarding.share-usage.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]
[status-im.contexts.onboarding.share-usage.learn-more-sheet :as learn-more-sheet]
[status-im.contexts.onboarding.share-usage.style :as style]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn- share-usage-data-fn
[enabled? next-screen]
(rf/dispatch [:centralized-metrics/toggle-centralized-metrics enabled? true])
(if next-screen
(rf/dispatch [:navigate-to-within-stack [next-screen :screen/onboarding.share-usage]]) ;; Onboarding
(rf/dispatch [:navigate-back]))) ;; Login Screen
(defn view
[]
(let [insets (safe-area/get-insets)
next-screen (:next-screen (rf/sub [:get-screen-params :screen/onboarding.share-usage]))
share-usage-data (rn/use-callback #(share-usage-data-fn true next-screen))
maybe-later (rn/use-callback #(share-usage-data-fn false next-screen))
learn-more (rn/use-callback #(rf/dispatch [:show-bottom-sheet
{:content learn-more-sheet/view
:shell? true}]))]
[:<>
[quo/page-nav
{:margin-top (:top insets)
:background :blur
:icon-name :i/arrow-left
:on-press events-helper/navigate-back
:right-side [{:icon-left :i/info
:accessibility-label :learn-more
:label (i18n/label :t/learn-more)
:on-press learn-more}]}]
[quo/text-combinations
{:container-style style/title-container
:title (i18n/label :t/help-us-improve-status)
:title-accessibility-label :share-usage-title
:description (i18n/label :t/collecting-usage-data)
:description-accessibility-label :share-usage-subtitle}]
[rn/image
{:resize-mode :contain
:style (style/page-illustration (:width (rn/get-window)))
:source (resources/get-image :biometrics)}]
[rn/view {:style (style/buttons insets)}
[quo/button
{:size 40
:accessibility-label :share-usage-data
:on-press share-usage-data}
(i18n/label :t/agree)]
[quo/button
{:size 40
:accessibility-label :maybe-later-button
:background :blur
:type :grey
:on-press maybe-later
:container-style {:margin-top 12}}
(i18n/label :t/maybe-later)]]]))
@@ -0,0 +1,29 @@
(ns status-im.contexts.preview.quo.cards.wallet-card
(:require
[quo.core :as quo]
[reagent.core :as reagent]
[status-im.common.resources :as resources]
[status-im.contexts.preview.quo.preview :as preview]))
(def descriptor
[{:key :title
:type :text}
{:key :subtitle
:type :text}
{:key :dismissible?
:type :boolean}])
(defn view
[]
(let [state (reagent/atom {:image (resources/get-image :buy)
:title "Buy"
:subtitle "Start investing now"
:on-press #(js/alert "Item pressed")
:on-press-close #(js/alert "Close pressed")
:dismissible? false})]
(fn []
[preview/preview-container
{:state state
:descriptor descriptor
:component-container-style {:align-items :center}}
[quo/wallet-card @state]])))
@@ -34,6 +34,7 @@
[status-im.contexts.preview.quo.calendar.calendar :as calendar]
[status-im.contexts.preview.quo.calendar.calendar-day :as calendar-day]
[status-im.contexts.preview.quo.calendar.calendar-year :as calendar-year]
[status-im.contexts.preview.quo.cards.wallet-card :as wallet-card]
[status-im.contexts.preview.quo.code.snippet :as code-snippet]
[status-im.contexts.preview.quo.code.snippet-preview :as code-snippet-preview]
[status-im.contexts.preview.quo.colors.color :as color]
@@ -274,6 +275,8 @@
:component calendar-day/view}
{:name :calendar-year
:component calendar-year/view}]
:cards [{:name :wallet-card
:component wallet-card/view}]
:code [{:name :snippet
:component code-snippet/view}
{:name :snippet-preview
@@ -11,6 +11,8 @@
:key :number-of-filled-pins}
{:type :boolean
:key :error?}
{:type :boolean
:key :info-error?}
{:type :text
:key :info}])
+5 -1
View File
@@ -1,5 +1,6 @@
(ns status-im.contexts.profile.events
(:require
[clojure.string :as string]
[legacy.status-im.data-store.settings :as data-store.settings]
[legacy.status-im.multiaccounts.update.core :as multiaccounts.update]
[native-module.core :as native-module]
@@ -36,7 +37,10 @@
{:dataDir (native-module/backup-disabled-data-dir)
:mixpanelAppId config/mixpanel-app-id
:mixpanelToken config/mixpanel-token
:mediaServerEnableTLS (config/enabled? config/STATUS_BACKEND_SERVER_MEDIA_SERVER_ENABLE_TLS)}
:mediaServerEnableTLS (config/enabled? config/STATUS_BACKEND_SERVER_MEDIA_SERVER_ENABLE_TLS)
:logEnabled (not (string/blank? config/log-level))
:logLevel config/log-level
:apiLoggingEnabled config/api-logging-enabled?}
callback)))
(rf/reg-event-fx
@@ -4,6 +4,7 @@
[native-module.core :as native-module]
[status-im.common.keychain.events :as keychain]
[status-im.config :as config]
[status-im.constants :as constants]
status-im.contexts.profile.login.effects
[status-im.contexts.profile.rpc :as profile.rpc]
[taoensso.timbre :as log]
@@ -41,10 +42,10 @@
log-level (or (:log-level settings) config/log-level)
pairing-completed? (= (get-in db [:syncing :pairing-status]) :completed)
new-db (-> db
(assoc :chats/loading? true
:profile/profile (merge profile-overview
settings
{:log-level log-level}))
(assoc :profile/profile
(merge profile-overview
settings
{:log-level log-level}))
(assoc-in [:activity-center :loading?] true)
(dissoc :centralized-metrics/onboarding-enabled?))]
{:db (cond-> new-db
@@ -131,10 +132,15 @@
(let [new-account? (get db :onboarding/new-account?)]
{:db (assoc db :messenger/started? true)
:fx [[:fetch-chats-preview
{:on-success (fn [result]
(rf/dispatch [:chats-list/load-success result])
(rf/dispatch [:communities/get-user-requests-to-join])
(rf/dispatch [:profile.login/get-chats-callback]))}]
{:chat-preview-type constants/chat-preview-type-non-community
:on-success (fn [result]
(rf/dispatch [:chats-list/load-success result])
(rf/dispatch [:profile.login/get-chats-callback]))}]
[:fetch-chats-preview
{:chat-preview-type constants/chat-preview-type-community
:on-success (fn [result]
(rf/dispatch [:chats-list/load-success result])
(rf/dispatch [:communities/get-user-requests-to-join]))}]
(when (and (:syncing/fallback-flow? db) (:syncing/installation-id db))
[:dispatch [:pairing/finish-seed-phrase-fallback-syncing]])
(when-not new-account?
@@ -7,7 +7,6 @@
[react-native.reanimated :as reanimated]
[react-native.safe-area :as safe-area]
[status-im.common.confirmation-drawer.view :as confirmation-drawer]
[status-im.common.metrics-confirmation-modal.view :as metrics-modal]
[status-im.common.standard-authentication.core :as standard-authentication]
[status-im.config :as config]
[status-im.constants :as constants]
@@ -277,10 +276,10 @@
(defn view
[]
(rn/use-mount #(rf/dispatch [:centralized-metrics/check-modal metrics-modal/view]))
(let [[show-profiles? set-show-profiles] (rn/use-state true)
show-profiles (rn/use-callback #(set-show-profiles true))
hide-profiles (rn/use-callback #(set-show-profiles false))]
(rn/use-mount #(rf/dispatch [:centralized-metrics/check-user-confirmation]))
[:<>
[background/view true]
(if show-profiles?
@@ -139,10 +139,28 @@
:on-success #(log/debug "mnemonic was marked as shown")
:on-error #(log/error "mnemonic was not marked as shown" %)}]]]}))
(defn token-symbols-from-accounts
[accounts]
(reduce-kv (fn [acc _ account]
(into acc (map :symbol (:tokens account))))
#{}
accounts))
(rf/reg-event-fx :profile.settings/update-currency
(fn [_ [currency]]
{:fx [[:dispatch [:profile.settings/profile-update :currency currency]]
[:dispatch [:wallet/get-wallet-token-for-all-accounts]]]}))
(fn [{:keys [db]} [currency]]
(let [accounts (get-in db [:wallet :accounts])
symbols (token-symbols-from-accounts accounts)]
{:fx [[:dispatch [:profile.settings/profile-update :currency currency]]
[:effects.wallet.tokens/fetch-market-values
{:symbols symbols
:currency currency
:on-success [:wallet.tokens/store-market-values]
:on-error [:wallet.tokens/fetch-market-values-failed]}]
[:effects.wallet.tokens/fetch-prices
{:symbols symbols
:currencies [constants/profile-default-currency currency]
:on-success [:wallet.tokens/store-prices]
:on-error [:wallet.tokens/fetch-prices-failed]}]]})))
;; Logout process
(rf/reg-event-fx
@@ -19,12 +19,14 @@
(let [network (rf/sub [:wallet/network-details-by-chain-id chain-id])
currency (rf/sub [:profile/currency])
currency-symbol (rf/sub [:profile/currency-symbol])
prices-per-token (rf/sub [:wallet/prices-per-token])
balance (utils/calculate-total-token-balance token [chain-id])
crypto-value (utils/get-standard-crypto-format token balance)
crypto-value (utils/get-standard-crypto-format token balance prices-per-token)
fiat-value (utils/calculate-token-fiat-value
{:currency currency
:balance balance
:token token})
{:currency currency
:balance balance
:token token
:prices-per-token prices-per-token})
fiat-formatted (utils/fiat-formatted-for-ui currency-symbol
fiat-value)
token-available-on-network? (network-utils/token-available-on-network? supported-networks
@@ -12,12 +12,13 @@
disabled? :bridge-disabled?
:as token}
_ _
{:keys [currency currency-symbol on-token-press preselected-token-symbol]}]
{:keys [currency currency-symbol on-token-press preselected-token-symbol prices-per-token]}]
(let [fiat-value (utils/calculate-token-fiat-value
{:currency currency
:balance total-balance
:token token})
crypto-formatted (utils/get-standard-crypto-format token total-balance)
{:currency currency
:balance total-balance
:token token
:prices-per-token prices-per-token})
crypto-formatted (utils/get-standard-crypto-format token total-balance prices-per-token)
fiat-formatted (utils/fiat-formatted-for-ui currency-symbol fiat-value)]
[quo/token-network
{:token token-symbol
@@ -34,17 +35,19 @@
(defn view
[{:keys [content-container-style search-text on-token-press preselected-token-symbol chain-ids]
:or {content-container-style {:padding-horizontal 8}}}]
(let [filtered-tokens (rf/sub [:wallet/current-viewing-account-tokens-filtered
{:query search-text
:chain-ids chain-ids}])
currency (rf/sub [:profile/currency])
currency-symbol (rf/sub [:profile/currency-symbol])]
(let [filtered-tokens (rf/sub [:wallet/current-viewing-account-tokens-filtered
{:query search-text
:chain-ids chain-ids}])
currency (rf/sub [:profile/currency])
currency-symbol (rf/sub [:profile/currency-symbol])
prices-per-token (rf/sub [:wallet/prices-per-token])]
[gesture/flat-list
{:data filtered-tokens
:render-data {:currency currency
:currency-symbol currency-symbol
:on-token-press on-token-press
:preselected-token-symbol preselected-token-symbol}
:preselected-token-symbol preselected-token-symbol
:prices-per-token prices-per-token}
:style {:flex 1}
:content-container-style content-container-style
:keyboard-should-persist-taps :handled
+81 -52
View File
@@ -27,12 +27,18 @@
[balance]
(cut-fiat-balance balance 2))
(defn prettify-balance
[currency-symbol balance]
(defn prepend-curency-symbol-to-fiat-balance
[fiat-balance currency-symbol]
(let [formatted-symbol (if (> (count currency-symbol) 1)
(str currency-symbol " ")
currency-symbol)]
(str formatted-symbol (cut-fiat-balance-to-two-decimals balance))))
(str formatted-symbol fiat-balance)))
(defn prettify-balance
[currency-symbol fiat-balance]
(-> fiat-balance
cut-fiat-balance-to-two-decimals
(prepend-curency-symbol-to-fiat-balance currency-symbol)))
(defn get-derivation-path
[number-of-accounts]
@@ -71,9 +77,14 @@
(inc max-decimals)
max-decimals)))
(defn token-price-by-symbol
"Get token price for specific currency from prices-per-token structure"
[prices-per-token token-symbol currency]
(get-in prices-per-token [(keyword token-symbol) currency]))
(defn token-usd-price
[token]
(get-in token [:market-values-per-currency :usd :price]))
[token prices-per-token]
(token-price-by-symbol prices-per-token (:symbol token) :usd))
(defn one-cent-value
[token-price-in-usd]
@@ -104,16 +115,20 @@
:else (number/remove-trailing-zeroes
(.toFixed token-units standardized-decimals-count)))))
(defn add-token-symbol-to-crypto-balance
[crypto-balance token-symbol]
(str crypto-balance " " (string/upper-case token-symbol)))
(defn prettify-crypto-balance
[token-symbol crypto-balance conversion-rate]
(str (cut-crypto-decimals-to-fit-usd-cents crypto-balance conversion-rate)
" "
(string/upper-case token-symbol)))
(-> crypto-balance
(cut-crypto-decimals-to-fit-usd-cents conversion-rate)
(add-token-symbol-to-crypto-balance token-symbol)))
(defn get-standard-crypto-format
"For full details: https://github.com/status-im/status-mobile/issues/18225"
[token token-units]
(let [token-price-in-usd (token-usd-price token)
[token token-units prices-per-token]
(let [token-price-in-usd (token-usd-price token prices-per-token)
{:keys [zero-value? usd-cent-value standardized-decimals-count]}
(analyze-token-amount-for-price token-price-in-usd token-units)]
(cond
@@ -127,11 +142,9 @@
(number/remove-trailing-zeroes (.toFixed token-units standardized-decimals-count)))))
(defn get-market-value
[currency {:keys [market-values-per-currency]}]
(or (get-in market-values-per-currency
[currency :price])
(get-in market-values-per-currency
[constants/profile-default-currency :price])
[currency {token-symbol :symbol} prices-per-token]
(or (token-price-by-symbol prices-per-token token-symbol currency)
(token-price-by-symbol prices-per-token token-symbol constants/profile-default-currency)
;; NOTE: adding fallback value (zero) in case prices are
;; unavailable and to prevent crash on calculating fiat value
0))
@@ -161,15 +174,15 @@
(defn calculate-token-fiat-value
"Returns the token fiat value for provided raw balance"
[{:keys [currency balance token]}]
(let [price (get-market-value currency token)]
[{:keys [currency balance token prices-per-token]}]
(let [price (get-market-value currency token prices-per-token)]
(money/crypto->fiat balance price)))
(defn formatted-token-fiat-value
"Converts a token balance into its equivalent fiat value, formatted with a currency symbol.
If the fiat value is below $0.01, it returns a <$0.01 string"
[{:keys [currency currency-symbol balance token]}]
(let [price (or (get-market-value currency token) 0)
[{:keys [currency currency-symbol balance token prices-per-token]}]
(let [price (or (get-market-value currency token prices-per-token) 0)
price-zero? (zero? price)
balance (or balance 0)
balance-positive? (pos? balance)
@@ -204,12 +217,13 @@
(sanitized-token-amount-to-display display-decimals))))
(defn calculate-balance-from-tokens
[{:keys [currency tokens chain-ids]}]
[{:keys [currency tokens chain-ids prices-per-token]}]
(->> tokens
(map #(calculate-token-fiat-value
{:currency currency
:balance (calculate-total-token-balance % chain-ids)
:token %}))
{:currency currency
:balance (calculate-total-token-balance % chain-ids)
:token %
:prices-per-token prices-per-token}))
(reduce money/add)))
(defn- add-balances-per-chain
@@ -252,9 +266,10 @@
(defn fiat-formatted-for-ui
[currency-symbol fiat-value]
(if (money/less-than fiat-value 0.01)
(str "<" currency-symbol "0.01")
(prettify-balance currency-symbol fiat-value)))
(cond
(money/equal-to fiat-value 0) (str currency-symbol "0.00")
(money/less-than fiat-value 0.01) (str "<" currency-symbol "0.01")
:else (prettify-balance currency-symbol fiat-value)))
(defn prettify-percentage-change
"Returns unsigned precentage"
@@ -266,30 +281,30 @@
(defn calculate-token-value
"This function returns token values in the props of token-value (quo) component"
[{:keys [token color currency currency-symbol]}]
(let [balance (calculate-total-token-balance token)
fiat-unformatted-value (calculate-token-fiat-value
{:currency currency
:balance balance
:token token})
market-values (or (get-in token [:market-values-per-currency currency])
(get-in token
[:market-values-per-currency
constants/profile-default-currency]))
{:keys [price change-pct-24hour]} market-values
formatted-token-price (prettify-balance currency-symbol price)
percentage-change (prettify-percentage-change change-pct-24hour)
crypto-value (get-standard-crypto-format token balance)
fiat-value (fiat-formatted-for-ui currency-symbol
fiat-unformatted-value)]
[{:keys [token color currency currency-symbol prices-per-token market-values-per-token]}]
(let [balance (calculate-total-token-balance token)
fiat-unformatted-value (calculate-token-fiat-value
{:currency currency
:balance balance
:token token
:prices-per-token prices-per-token})
currency (or currency constants/profile-default-currency)
market-values (get market-values-per-token (keyword (:symbol token)))
{:keys [change-pct-24h]} market-values
price (token-price-by-symbol prices-per-token (:symbol token) currency)
formatted-token-price (prettify-balance currency-symbol price)
percentage-change (prettify-percentage-change change-pct-24h)
crypto-value (get-standard-crypto-format token balance prices-per-token)
fiat-value (fiat-formatted-for-ui currency-symbol
fiat-unformatted-value)]
{:token (:symbol token)
:token-name (:name token)
:state :default
:metrics? (money/above-zero? balance)
:status (cond
(pos? change-pct-24hour) :positive
(neg? change-pct-24hour) :negative
:else :empty)
(pos? change-pct-24h) :positive
(neg? change-pct-24h) :negative
:else :empty)
:customization-color color
:values {:crypto-value crypto-value
:fiat-value fiat-value
@@ -335,10 +350,10 @@
(map #(update % :balances-per-chain select-keys chain-ids) tokens))
(defn calculate-balances-per-chain
[{:keys [tokens currency currency-symbol]}]
[{:keys [tokens currency currency-symbol prices-per-token]}]
(->
(reduce (fn [acc {:keys [balances-per-chain decimals] :as token}]
(let [currency-value (get-market-value currency token)
(let [currency-value (get-market-value currency token prices-per-token)
fiat-balance-per-chain (update-vals balances-per-chain
#(-> (money/token->unit (:raw-balance %)
decimals)
@@ -379,12 +394,14 @@
(utils.string/contains-special-character? s) :special-character))
(defn calculate-and-sort-tokens
[{:keys [tokens color currency currency-symbol]}]
[{:keys [tokens color currency currency-symbol prices-per-token market-values-per-token]}]
(let [calculate-token (fn [token]
(calculate-token-value {:token token
:color color
:currency currency
:currency-symbol currency-symbol}))
(calculate-token-value {:token token
:color color
:currency currency
:currency-symbol currency-symbol
:prices-per-token prices-per-token
:market-values-per-token market-values-per-token}))
calculated-tokens (map calculate-token tokens)]
(sort-by (fn [token]
(let [fiat-value (get-in token [:values :fiat-unformatted-value])
@@ -549,6 +566,18 @@
(:less-than-five-minutes constants/wallet-transaction-estimation) "3-5"
">5"))
(defn transactions->hash-to-transaction-map
[transactions]
(reduce
(fn [acc {to-chain :toChain tx-hash :hash}]
(assoc acc
tx-hash
{:status :pending
:id tx-hash
:chain-id to-chain}))
{}
transactions))
(defn get-accounts-with-token-balance
[accounts token]
(let [operable-account (filter :operable? (vals accounts))
@@ -59,25 +59,33 @@
(deftest get-standard-crypto-format-test
(testing "get-standard-crypto-format function"
(let [market-values-per-currency {:usd {:price 100}}
token-units (money/bignumber 0.005)]
(is (= (utils/get-standard-crypto-format {:market-values-per-currency market-values-per-currency}
token-units)
(let [token {:symbol "ETH"}
prices-per-token {:ETH {:usd 100}}
token-units (money/bignumber 0.005)]
(is (= (utils/get-standard-crypto-format token
token-units
prices-per-token)
"0.005")))
(let [market-values-per-currency {:usd {:price nil}}
token-units (money/bignumber 0.0123456)]
(is (= (utils/get-standard-crypto-format {:market-values-per-currency market-values-per-currency}
token-units)
(let [token {:symbol "ETH"}
prices-per-token {:ETH {:usd nil}}
token-units (money/bignumber 0.0123456)]
(is (= (utils/get-standard-crypto-format token
token-units
prices-per-token)
"0.012346")))
(let [market-values-per-currency {:usd {:price 0.005}}
token-units (money/bignumber 0.01)]
(is (= (utils/get-standard-crypto-format {:market-values-per-currency market-values-per-currency}
token-units)
(let [token {:symbol "ETH"}
prices-per-token {:ETH {:usd 0.005}}
token-units (money/bignumber 0.01)]
(is (= (utils/get-standard-crypto-format token
token-units
prices-per-token)
"<2")))
(let [market-values-per-currency {:usd {:price 0.005}}
token-units "0.01"]
(is (= (utils/get-standard-crypto-format {:market-values-per-currency market-values-per-currency}
token-units)
(let [token {:symbol "ETH"}
prices-per-token {:ETH {:usd 0.005}}
token-units "0.01"]
(is (= (utils/get-standard-crypto-format token
token-units
prices-per-token)
"0")))))
(deftest calculate-total-token-balance-test
@@ -151,10 +159,12 @@
:name "Status Network Token"
:balances-per-chain {:mock-chain 1}
:decimals 18}]
mock-input {:tokens mock-tokens
:color mock-color
:currency mock-currency
:currency-symbol mock-currency-symbol}
mock-input {:tokens mock-tokens
:color mock-color
:currency mock-currency
:currency-symbol mock-currency-symbol
:prices-per-token {}
:market-values-per-token {}}
sorted-tokens (map :token (utils/calculate-and-sort-tokens mock-input))
expected-order ["DAI" "ETH" "SNT"]]
(is (= expected-order sorted-tokens))))))))
@@ -170,19 +180,20 @@
(deftest formatted-token-fiat-value-test
(testing "formatted-token-fiat-value function"
(let [default-params {:currency "USD"
:currency-symbol "$"
:balance 0.5
:token {:market-values-per-currency {:usd {:price 2000}}}}]
(let [default-params {:currency "USD"
:currency-symbol "$"
:balance 0.5
:token {:symbol "ETH"}
:prices-per-token {:ETH {:usd 2000}}}]
(is (= (utils/formatted-token-fiat-value default-params) "$1000"))
(is (= (utils/formatted-token-fiat-value (assoc default-params :balance 0)) "$0"))
(is (= (utils/formatted-token-fiat-value (assoc default-params :balance 0.000001)) "<$0.01"))
(is (= (utils/formatted-token-fiat-value (assoc default-params :balance nil)) "$0"))
(is (= (utils/formatted-token-fiat-value
(assoc default-params :balance 1 :token {:market-values-per-currency {:usd {:price nil}}}))
(assoc default-params :balance 1 :prices-per-token {:ETH {:usd nil}}))
"$0"))
(is (= (utils/formatted-token-fiat-value
(assoc default-params :balance 1 :token {:market-values-per-currency {:usd {:price 0}}}))
(assoc default-params :balance 1 :prices-per-token {:ETH {:usd 0}}))
"$0")))))
(deftest sanitized-token-amount-to-display-test
@@ -5,6 +5,7 @@
[promesa.core :as promesa]
[status-im.common.json-rpc.events :as json-rpc]
[status-im.contexts.profile.recover.effects :as profile.recover.effects]
[status-im.contexts.wallet.rpc :as wallet-rpc]
[taoensso.timbre :as log]
[utils.re-frame :as rf]
[utils.security.core :as security]
@@ -103,3 +104,22 @@
(-> (verify-private-key-for-keypair keypair-key-uid private-key)
(promesa/then (partial rf/call-continuation on-success))
(promesa/catch (partial rf/call-continuation on-error)))))
(defn sign-transaction-hashes
[hashes address password]
(-> (promesa/all
(for [h hashes]
(promesa/let [signature (wallet-rpc/sign-message h address password)]
{:message h
:signature signature})))
(promesa/catch (fn [err]
(throw (ex-info "Failed to sign transaction hashes"
{:error err
:code :error/sign-transaction-hashes}))))))
(rf/reg-fx
:effects.wallet/sign-transaction-hashes
(fn [{:keys [hashes address password on-success on-error]}]
(-> (sign-transaction-hashes hashes address password)
(promesa/then on-success)
(promesa/catch on-error))))
+53 -5
View File
@@ -213,7 +213,7 @@
(fn [{:keys [db]} [address]]
{:db (assoc-in db [:wallet :ui :tokens-loading address] true)
:fx [[:json-rpc/call
[{:method "wallet_getWalletToken"
[{:method "wallet_fetchOrGetCachedWalletBalances"
:params [[address]]
:on-success [:wallet/store-wallet-token address]
:on-error [:wallet/get-wallet-token-for-account-failed address]}]]]}))
@@ -232,6 +232,7 @@
:wallet/store-wallet-token
(fn [{:keys [db]} [address raw-tokens-data]]
(let [supported-chains-by-token-symbol (get-in db [:wallet :tokens :supported-chains-by-symbol])
profile-currency (get-in db [:profile/profile :currency])
tokens (data-store/rpc->tokens raw-tokens-data
supported-chains-by-token-symbol)
add-tokens (fn [stored-accounts tokens-per-account]
@@ -241,11 +242,25 @@
(update accounts address assoc :tokens tokens-data)
accounts))
stored-accounts
tokens-per-account))]
{:fx [[:dispatch [:wallet/get-last-wallet-token-update-if-needed]]]
:db (-> db
tokens-per-account))
symbols (reduce-kv (fn [acc _ v]
(into acc (map :symbol v)))
#{}
tokens)]
{:db (-> db
(update-in [:wallet :accounts] add-tokens tokens)
(assoc-in [:wallet :ui :tokens-loading address] false))})))
(assoc-in [:wallet :ui :tokens-loading address] false))
:fx [[:dispatch [:wallet/get-last-wallet-token-update-if-needed]]
[:effects.wallet.tokens/fetch-market-values
{:symbols symbols
:currency profile-currency
:on-success [:wallet.tokens/store-market-values]
:on-error [:wallet.tokens/fetch-market-values-failed]}]
[:effects.wallet.tokens/fetch-prices
{:symbols symbols
:currencies [constants/profile-default-currency profile-currency]
:on-success [:wallet.tokens/store-prices]
:on-error [:wallet.tokens/fetch-prices-failed]}]]})))
(rf/reg-event-fx
:wallet/get-last-wallet-token-update-if-needed
@@ -686,3 +701,36 @@
(let [full-status (cske/transform-keys message transforms/->kebab-case-keyword)]
{:db (assoc-in db [:wallet :blockchain] full-status)})))
(rf/reg-event-fx
:wallet/sign-transactions-signal-received
(fn [{:keys [db]} [{send-details :sendDetails :as data}]]
(let [type (if (or (= (:fromToken send-details)
(:toToken send-details))
(string/blank? (:toToken send-details)))
:send
:swap)
callback-fx (get-in db [:wallet :ui type :sign-transactions-callback-fx])
error-response (:errorResponse send-details)]
{:fx [(when (and callback-fx (not error-response))
callback-fx)]
:db (-> db
(assoc-in [:wallet :ui type :sign-transactions-callback-fx] nil)
(assoc-in [:wallet :ui type :error-response] error-response)
(assoc-in [:wallet :ui type :transaction-for-signing] data))})))
(rf/reg-event-fx
:wallet/transactions-sent-signal-received
(fn [{:keys [db]}
[{sent-transactions :sentTransactions
send-details :sendDetails}]]
(let [swap? (get-in db [:wallet :ui :swap])]
{:fx [[:dispatch
(if-let [error-response (:errorResponse send-details)]
[(if swap?
:wallet.swap/transaction-failure
:wallet/transaction-failure)
error-response]
[(if swap?
:wallet.swap/transaction-success
:wallet/transaction-success)
sent-transactions])]]})))
@@ -124,7 +124,7 @@
[event-id dispatch]
(let [expected-effects {:db {:wallet {:ui {:tokens-loading {address true}}}}
:fx [[:json-rpc/call
[{:method "wallet_getWalletToken"
[{:method "wallet_fetchOrGetCachedWalletBalances"
:params [[address]]
:on-success [:wallet/store-wallet-token address]
:on-error [:wallet/get-wallet-token-for-account-failed
@@ -4,3 +4,9 @@
(def list-container
{:padding-horizontal 8
:padding-bottom constants/floating-shell-button-height})
(def buy-and-receive-cta-container
{:flex-direction :row
:justify-content :space-between
:padding-horizontal 20
:padding-vertical 8})
@@ -2,21 +2,46 @@
(:require
[quo.core :as quo]
[react-native.core :as rn]
[status-im.common.resources :as resources]
[status-im.contexts.wallet.common.token-value.view :as token-value]
[status-im.contexts.wallet.home.tabs.assets.style :as style]
[status-im.contexts.wallet.sheets.buy-token.view :as buy-token]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn view
[]
(let [tokens-loading? (rf/sub [:wallet/home-tokens-loading?])
{:keys [tokens]} (rf/sub [:wallet/aggregated-token-values-and-balance])]
(if tokens-loading?
[quo/skeleton-list
{:content :assets
:parent-height 560
:animated? false}]
[rn/flat-list
{:render-fn token-value/view
:data tokens
:render-data {:entry-point :wallet-stack}
:content-container-style style/list-container}])))
{:keys [tokens]} (rf/sub [:wallet/aggregated-token-values-and-balance])
zero-balance? (rf/sub [:wallet/zero-balance-in-all-non-watched-accounts?])
buy-assets (rn/use-callback
(fn []
(rf/dispatch [:show-bottom-sheet
{:content buy-token/view}])))
receive-assets (rn/use-callback
(fn []
(rf/dispatch [:open-modal :screen/share-shell {:initial-tab :wallet}])))]
[:<>
(when (and (some? tokens-loading?) (not tokens-loading?) zero-balance?)
[rn/view
{:style style/buy-and-receive-cta-container}
[quo/wallet-card
{:image (resources/get-image :buy)
:title (i18n/label :t/ways-to-buy)
:subtitle (i18n/label :t/via-card-or-bank)
:on-press buy-assets}]
[quo/wallet-card
{:image (resources/get-image :receive)
:title (i18n/label :t/receive)
:subtitle (i18n/label :t/deposit-to-your-wallet)
:on-press receive-assets}]])
(if tokens-loading?
[quo/skeleton-list
{:content :assets
:parent-height 560
:animated? false}]
[rn/flat-list
{:render-fn token-value/view
:data tokens
:render-data {:entry-point :wallet-stack}
:content-container-style style/list-container}])]))
+59
View File
@@ -0,0 +1,59 @@
(ns status-im.contexts.wallet.rpc
(:require [oops.core :as oops]
[promesa.core :as promesa]
[status-im.common.json-rpc.events :as rpc-events]
[status-im.constants :as constants]
[utils.hex]
[utils.transforms :as transforms]))
(defn build-transaction
[chain-id tx]
(promesa/let [res (rpc-events/call-async "wallet_buildTransaction" true chain-id tx)]
{:message-to-sign (oops/oget res :messageToSign)
:tx-args (oops/oget res :txArgs)}))
(defn build-raw-transaction
[chain-id tx-args signature]
(-> (rpc-events/call-async "wallet_buildRawTransaction"
true
chain-id
(transforms/js-stringify tx-args 0)
signature)
(promesa/then #(oops/oget % "rawTx"))))
(defn hash-message-eip-191
[message]
(rpc-events/call-async "wallet_hashMessageEIP191" true message))
(defn safe-sign-typed-data
[data address password chain-id legacy?]
(rpc-events/call-async "wallet_safeSignTypedDataForDApps"
true
data
address
password
chain-id
legacy?))
(defn get-suggested-fees
[chain-id]
(-> (rpc-events/call-async "wallet_getSuggestedFees" true chain-id)
(promesa/then transforms/js->clj)))
(defn send-transaction-with-signature
[chain-id tx-args signature]
(rpc-events/call-async "wallet_sendTransactionWithSignature"
true
chain-id
constants/transaction-pending-type-wallet-connect-transfer
(transforms/js-stringify tx-args 0)
signature))
(defn sign-message
[message address password]
(-> (rpc-events/call-async "wallet_signMessage"
true
message
address
password)
(promesa/then utils.hex/normalize-hex)))
+124 -122
View File
@@ -1,7 +1,6 @@
(ns status-im.contexts.wallet.send.events
(:require
[clojure.string :as string]
[native-module.core :as native-module]
[status-im.constants :as constants]
[status-im.contexts.wallet.collectible.utils :as collectible.utils]
[status-im.contexts.wallet.common.utils :as utils]
@@ -9,11 +8,10 @@
[status-im.contexts.wallet.data-store :as data-store]
[status-im.contexts.wallet.send.utils :as send-utils]
[taoensso.timbre :as log]
[utils.address :as address]
[utils.hex :as utils.hex]
[utils.money :as utils.money]
[utils.number]
[utils.re-frame :as rf]))
[utils.re-frame :as rf]
[utils.security.core :as security]))
(rf/reg-event-fx :wallet/clean-send-data
(fn [{:keys [db]}]
@@ -327,22 +325,30 @@
(rf/reg-event-fx
:wallet/set-token-amount-to-send
(fn [{:keys [db]} [{:keys [amount stack-id start-flow?]}]]
{:db (assoc-in db [:wallet :ui :send :amount] amount)
:fx [[:dispatch
[:wallet/wizard-navigate-forward
{:current-screen stack-id
:start-flow? start-flow?
:flow-id :wallet-send-flow}]]]}))
(let [last-request-uuid (get-in db [:wallet :ui :send :last-request-uuid])]
{:db (-> db
(assoc-in [:wallet :ui :send :amount] amount)
(update-in [:wallet :ui :send] dissoc :transaction-for-signing))
:fx [[:dispatch [:wallet/build-transactions-from-route {:request-uuid last-request-uuid}]]
[:dispatch
[:wallet/wizard-navigate-forward
{:current-screen stack-id
:start-flow? start-flow?
:flow-id :wallet-send-flow}]]]})))
(rf/reg-event-fx
:wallet/set-token-amount-to-bridge
(fn [{:keys [db]} [{:keys [amount stack-id start-flow?]}]]
{:db (assoc-in db [:wallet :ui :send :amount] amount)
:fx [[:dispatch
[:wallet/wizard-navigate-forward
{:current-screen stack-id
:start-flow? start-flow?
:flow-id :wallet-bridge-flow}]]]}))
(let [last-request-uuid (get-in db [:wallet :ui :send :last-request-uuid])]
{:db (-> db
(assoc-in [:wallet :ui :send :amount] amount)
(update-in [:wallet :ui :send] dissoc :transaction-for-signing))
:fx [[:dispatch [:wallet/build-transactions-from-route {:request-uuid last-request-uuid}]]
[:dispatch
[:wallet/wizard-navigate-forward
{:current-screen stack-id
:start-flow? start-flow?
:flow-id :wallet-bridge-flow}]]]})))
(rf/reg-event-fx
:wallet/clean-bridge-to-selection
@@ -458,7 +464,8 @@
:token-networks-ids token-networks-ids
:tx-type tx-type
:receiver? true}))
params [{:uuid (str (random-uuid))
request-uuid (str (random-uuid))
params [{:uuid request-uuid
:sendType send-type
:addrFrom from-address
:addrTo to-address
@@ -477,7 +484,8 @@
{:db (update-in db
[:wallet :ui :send]
#(-> %
(assoc :amount amount
(assoc :last-request-uuid request-uuid
:amount amount
:loading-suggested-routes? true
:sender-network-values sender-network-values
:receiver-network-values receiver-network-values)
@@ -565,27 +573,39 @@
:else [:wallet/suggested-routes-success (fix-routes data)
enough-assets?])]]})))))
(rf/reg-event-fx :wallet/add-authorized-transaction
(fn [{:keys [db]} [transaction]]
(let [transaction-batch-id (:id transaction)
transaction-hashes (:hashes transaction)
transaction-ids (flatten (vals transaction-hashes))
transaction-details (send-utils/map-multitransaction-by-ids transaction-batch-id
transaction-hashes)]
(rf/reg-event-fx
:wallet/transaction-success
(fn [{:keys [db]} [sent-transactions]]
(let [wallet-transactions (get-in db [:wallet :transactions] {})
transactions (utils/transactions->hash-to-transaction-map sent-transactions)
transaction-ids (->> transactions
vals
(map :hash))]
{:db (-> db
(assoc-in [:wallet :ui :send :just-completed-transaction?] true)
(assoc-in [:wallet :transactions] transaction-details)
(assoc-in [:wallet :ui :send :transaction-ids] transaction-ids))
:fx [;; Remove wallet/stop-and-clean-suggested-routes event when we move to new transaction
;; submission process as the routes as stopped by status-go
[:dispatch
[:wallet/stop-and-clean-suggested-routes]]
[:dispatch
[:wallet/end-transaction-flow]]
(assoc-in [:wallet :ui :send :transaction-ids] transaction-ids)
(assoc-in [:wallet :transactions] (merge wallet-transactions transactions)))
:fx [[:dispatch [:wallet/end-transaction-flow]]
[:dispatch-later
[{:ms 2000
:dispatch [:wallet/stop-and-clean-suggested-routes]}]]
[:dispatch-later
[{:ms 2000
:dispatch [:wallet/clean-just-completed-transaction]}]]]})))
(rf/reg-event-fx
:wallet/transaction-failure
(fn [_ [{:keys [details]}]]
{:fx [[:dispatch [:wallet/end-transaction-flow]]
[:dispatch-later
[{:ms 2000
:dispatch [:wallet/stop-and-clean-suggested-routes]}]]
[:dispatch
[:toasts/upsert
{:id :send-transaction-failure
:type :negative
:text (or details "An error occured")}]]]}))
(rf/reg-event-fx :wallet/clean-just-completed-transaction
(fn [{:keys [db]}]
{:db (update-in db [:wallet :ui :send] dissoc :just-completed-transaction?)}))
@@ -609,100 +629,82 @@
[{:ms 20
:dispatch [:wallet/clean-up-transaction-flow]}]]]})))
(rf/reg-event-fx :wallet/send-transaction
(fn [{:keys [db]} [sha3-pwd]]
(let [routes (get-in db [:wallet :ui :send :route])
first-route (first routes)
from-address (get-in db [:wallet :current-viewing-account-address])
transaction-type (get-in db [:wallet :ui :send :tx-type])
multi-transaction-type (if (= :tx/bridge transaction-type)
constants/multi-transaction-type-bridge
constants/multi-transaction-type-send)
token (get-in db [:wallet :ui :send :token])
collectible (get-in db [:wallet :ui :send :collectible])
first-route-from-chain-id (get-in first-route [:from :chain-id])
token-id (if token
(:symbol token)
(get-in collectible [:id :token-id]))
erc20-transfer? (and token (not= token-id "ETH"))
eth-transfer? (and token (not erc20-transfer?))
token-address (cond collectible
(get-in collectible
[:id :contract-id :address])
erc20-transfer?
(get-in token [:balances-per-chain first-route-from-chain-id :address]))
to-address (get-in db [:wallet :ui :send :to-address])
transaction-paths (into []
(mapcat
(fn [route]
(let [approval-required? (:approval-required route)
data (when erc20-transfer?
(native-module/encode-transfer
(address/normalized-hex to-address)
(:amount-in route)))
base-path (utils/transaction-path
{:to-address to-address
:from-address from-address
:route route
:token-address token-address
:token-id-from (if collectible
(utils.hex/number-to-hex
token-id)
token-id)
:data data
:eth-transfer? eth-transfer?})]
(if approval-required?
[(utils/approval-path {:route route
:token-address token-address
:from-address from-address
:to-address to-address})
base-path]
[base-path]))))
routes)
request-params
[(utils/multi-transaction-command
{:from-address from-address
:to-address to-address
:from-asset token-id
:to-asset token-id
:amount-out (if eth-transfer? (:amount-out first-route) "0x0")
:multi-transaction-type multi-transaction-type})
transaction-paths
sha3-pwd]]
(log/info "multi transaction called")
{:json-rpc/call [{:method "wallet_createMultiTransaction"
:params request-params
:on-success (fn [result]
(when result
(rf/dispatch [:wallet/add-authorized-transaction result])
(rf/dispatch [:hide-bottom-sheet])))
(rf/reg-event-fx
:wallet/build-transactions-from-route
(fn [_ [{:keys [request-uuid slippage] :or {slippage constants/default-slippage}}]]
{:json-rpc/call [{:method "wallet_buildTransactionsFromRoute"
:params [{:uuid request-uuid
:slippagePercentage slippage}]
:on-error (fn [error]
(log/error "failed to build transactions from route"
{:event :wallet/build-transactions-from-route
:error error})
(rf/dispatch [:toasts/upsert
{:id :build-transactions-from-route-error
:type :negative
:text (:message error)}]))}]}))
(rf/reg-event-fx
:wallet/prepare-signatures-for-transactions
(fn [{:keys [db]} [type sha3-pwd]]
(let [{:keys [hashes address signOnKeycard]} (get-in db
[:wallet :ui type :transaction-for-signing
:signingDetails])
on-success (fn [signatures]
(rf/dispatch
[:wallet/send-router-transactions-with-signatures type
signatures]))
on-error (fn [error]
(log/error
"failed to prepare signatures for transactions"
{:event :wallet/prepare-signatures-for-transactions
:error error})
(rf/dispatch
[:toasts/upsert
{:id :prepare-signatures-for-transactions-error
:type :negative
:text (:message error)}]))]
(if signOnKeycard
{:fx [[:dispatch
[:standard-auth/authorize-with-keycard
{:on-complete #(rf/dispatch [:keycard/connect-and-sign-hashes
{:keycard-pin %
:address address
:hashes hashes
:on-success on-success
:on-failure on-error}])}]]]}
{:fx [[:effects.wallet/sign-transaction-hashes
{:hashes hashes
:address address
:password (security/safe-unmask-data sha3-pwd)
:on-success on-success
:on-error on-error}]]}))))
(rf/reg-event-fx
:wallet/send-router-transactions-with-signatures
(fn [{:keys [db]} [type signatures]]
(let [transaction-for-signing (get-in db [:wallet :ui type :transaction-for-signing])
signatures-map (reduce (fn [acc {:keys [message signature]}]
(assoc acc
message
(send-utils/signature-rsv signature)))
{}
signatures)]
{:json-rpc/call [{:method "wallet_sendRouterTransactionsWithSignatures"
:params [{:uuid (get-in transaction-for-signing [:sendDetails :uuid])
:signatures signatures-map}]
:on-success (fn []
(rf/dispatch [:wallet/clean-send-data])
(rf/dispatch [:hide-bottom-sheet]))
:on-error (fn [error]
(log/error "failed to send transaction"
{:event :wallet/send-transaction
:error error
:params request-params})
(log/error "failed to send router transactions with signatures"
{:event :wallet/send-router-transactions-with-signatures
:error error})
(rf/dispatch [:toasts/upsert
{:id :send-transaction-error
{:id :send-router-transactions-with-signatures-error
:type :negative
:text (:message error)}]))}]})))
(rf/reg-event-fx :wallet/proceed-with-transactions-signatures
(fn [_ [signatures]]
{:json-rpc/call [{:method "wallet_proceedWithTransactionsSignatures"
:params [signatures]
:on-success (fn [result]
(when result
(rf/dispatch [:wallet/add-authorized-transaction result])
(rf/dispatch [:hide-bottom-sheet])))
:on-error (fn [error]
(log/error "failed to proceed-with-transactions-signatures"
{:event :wallet/proceed-with-transactions-signatures
:error error})
(rf/dispatch [:toasts/upsert
{:id :send-transaction-error
:type :negative
:text (:message error)}]))}]}))
(rf/reg-event-fx
:wallet/select-from-account
(fn [{db :db} [{:keys [address stack-id network-details start-flow?]}]]
@@ -575,29 +575,6 @@
:from-locked-amounts {}}}}})
(is (match? expected-db (:db (dispatch [event-id suggested-routes timestamp]))))))
(h/deftest-event :wallet/add-authorized-transaction
[event-id dispatch]
(let [hashes {:chain-1 ["tx-1" "tx-2" "tx-3"]
:chain-2 ["tx-4" "tx-5"]
:chain-3 ["tx-6" "tx-7" "tx-8" "tx-9"]}
transaction-id "txid-1"
expected-result {:db {:wallet {:ui {:send {:transaction-ids ["tx-1" "tx-2" "tx-3"
"tx-4" "tx-5" "tx-6"
"tx-7" "tx-8" "tx-9"]}}
:transactions (send-utils/map-multitransaction-by-ids
transaction-id
hashes)}}
:fx [[:dispatch
[:wallet/stop-and-clean-suggested-routes]]
[:dispatch [:wallet/end-transaction-flow]]
[:dispatch-later
[{:ms 2000
:dispatch [:wallet/clean-just-completed-transaction]}]]]}]
(is (match? expected-result
(dispatch [event-id
{:id transaction-id
:hashes hashes}])))))
(h/deftest-event :wallet/select-from-account
[event-id dispatch]
(let [stack-id :screen/stack
@@ -43,23 +43,22 @@
:removed false}
:wallet/current-viewing-account-color :purple
:wallet/wallet-send-enough-assets? true
:wallet/wallet-send-token {:symbol :eth
:networks [{:source 879
:short-name "eth"
:network-name :mainnet
:abbreviated-name
"Eth."
:full-name "Mainnet"
:chain-id 1
:related-chain-id 1
:layer 1}]
:balances-per-chain {1 {:raw-balance
(money/bignumber
"2500")
:has-error false}}
:total-balance 100
:available-balance 100
:market-values-per-currency {:usd {:price 10}}}
:wallet/wallet-send-token {:symbol "ETH"
:networks [{:source 879
:short-name "eth"
:network-name :mainnet
:abbreviated-name
"Eth."
:full-name "Mainnet"
:chain-id 1
:related-chain-id 1
:layer 1}]
:balances-per-chain {1 {:raw-balance
(money/bignumber
"2500")
:has-error false}}
:total-balance 100
:available-balance 100}
:wallet/wallet-send-loading-suggested-routes? false
:wallet/wallet-send-route [{:from {:chainid 1
:native-currency-symbol "ETH"}
@@ -75,14 +74,13 @@
:wallet/wallet-send-to-address "0x04371e2d9d66b82f056bc128064"
:profile/currency-symbol "$"
:profile/currency :usd
:wallet/token-by-symbol {:symbol :eth
:total-balance 100
:available-balance 100
:balances-per-chain {1 {:raw-balance
(money/bignumber
"2500")
:has-error false}}
:market-values-per-currency {:usd {:price 10}}}
:wallet/token-by-symbol {:symbol "ETH"
:total-balance 100
:available-balance 100
:balances-per-chain {1 {:raw-balance
(money/bignumber
"2500")
:has-error false}}}
:wallet/wallet-send-disabled-from-chain-ids []
:wallet/wallet-send-from-locked-amounts {}
:wallet/wallet-send-from-values-by-chain {1 (money/bignumber "250")}
@@ -106,6 +104,7 @@
:wallet/sending-collectible? false
:wallet/send-total-amount-formatted "250 ETH"
:wallet/total-amount (money/bignumber "250")
:wallet/prices-per-token {:ETH {:usd 10}}
:wallet/bridge-to-network-details nil
:wallet/send-amount-fixed ""
:wallet/send-display-token-decimals 5})
@@ -164,13 +164,14 @@
(str token-symbol)
enabled-from-chain-ids])
token-balance (or default-limit-crypto total-balance)
usd-conversion-rate (utils/token-usd-price token)
prices-per-token (rf/sub [:wallet/prices-per-token])
usd-conversion-rate (utils/token-usd-price token prices-per-token)
currency (rf/sub [:profile/currency])
conversion-rate (-> token
:market-values-per-currency
currency
:price)
token-decimals (rf/sub [:wallet/send-display-token-decimals])
conversion-rate (utils/token-price-by-symbol prices-per-token
token-symbol
currency)
[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])
@@ -80,202 +80,44 @@
(rf/dispatch [:wallet/update-receiver-networks
chain-ids]))}]))}]))
#_(defn- edit-amount
[{:keys [chain-id token-symbol send-amount-in-crypto init-amount]}]
(rf/dispatch
[:show-bottom-sheet
{:content
(fn []
(let [{:keys [network-name] :as network-details} (rf/sub [:wallet/network-details-by-chain-id
chain-id])
{fiat-currency :currency} (rf/sub [:profile/profile])
{token-decimals :decimals
:as
token} (rf/sub [:wallet/wallet-send-token])
currency (rf/sub [:profile/currency])
currency-symbol (rf/sub [:profile/currency-symbol])
send-from-locked-amounts (rf/sub
[:wallet/wallet-send-from-locked-amounts])
{account-color :color} (rf/sub [:wallet/current-viewing-account])
locked-amount (get send-from-locked-amounts chain-id)
network-name-str (string/capitalize (name network-name))
[input-state set-input-state] (rn/use-state
(cond-> controlled-input/init-state
init-amount
(controlled-input/set-input-value
(money/to-string init-amount))
locked-amount
(controlled-input/set-input-value
locked-amount)))
[crypto-currency? set-crypto-currency] (rn/use-state true)
conversion-rate (-> token
:market-values-per-currency
currency
:price)
{token-balance :total-balance} (rf/sub [:wallet/token-by-symbol
(str token-symbol)
[chain-id]])
current-crypto-limit (utils/get-standard-crypto-format
token
token-balance)
current-fiat-limit (.toFixed (* token-balance conversion-rate) 2)
current-limit (if crypto-currency?
current-crypto-limit
current-fiat-limit)
crypto-decimals token-decimals
input-amount (controlled-input/input-value input-state)
[is-amount-locked? set-is-amount-locked] (rn/use-state (some? locked-amount))
bottom (safe-area/get-bottom)
amount-in-crypto (if crypto-currency?
input-amount
(number/remove-trailing-zeroes
(.toFixed (/ input-amount
conversion-rate)
crypto-decimals)))
locked-greater-then-send-amount? (let [amount (money/bignumber
amount-in-crypto)
send-amount (money/bignumber
send-amount-in-crypto)]
(and (money/bignumber? amount)
(money/bignumber? send-amount)
(money/greater-than amount
send-amount)))
swap-between-fiat-and-crypto (fn [swap-to-crypto-currency?]
(set-crypto-currency
swap-to-crypto-currency?)
(set-input-state
(fn [input-state]
(controlled-input/set-input-value
input-state
(let [value (controlled-input/input-value
input-state)
new-value
(if
swap-to-crypto-currency?
(.toFixed
(/ value
conversion-rate)
crypto-decimals)
(.toFixed
(* value
conversion-rate)
12))]
(number/remove-trailing-zeroes
new-value))))))
lock-or-unlock-amount (fn []
(if is-amount-locked?
(rf/dispatch [:wallet/lock-from-amount
chain-id
amount-in-crypto])
(rf/dispatch [:wallet/unlock-from-amount
chain-id]))
(rf/dispatch [:hide-bottom-sheet]))]
(rn/use-effect
(fn []
(set-input-state #(controlled-input/set-upper-limit % current-limit)))
[current-limit])
[:<>
[quo/drawer-top
{:title (i18n/label :t/send-from-network {:network network-name-str})
:description (i18n/label :t/define-amount-sent-from-network {:network network-name-str})}]
[quo/token-input
{:container-style style/input-container
:token token-symbol
:currency fiat-currency
:currency-symbol currency-symbol
:crypto-decimals (min token-decimals 6)
:error? (controlled-input/input-error input-state)
:networks [network-details]
:title (i18n/label
:t/send-limit
{:limit (if crypto-currency?
(utils/make-limit-label-crypto current-limit token-symbol)
(utils/make-limit-label-fiat current-limit currency-symbol))})
:conversion conversion-rate
:show-keyboard? false
:value (controlled-input/input-value input-state)
:on-swap swap-between-fiat-and-crypto
:allow-selection? false}]
(when locked-greater-then-send-amount?
[quo/information-box
{:type :error
:icon :i/info
:style style/error-box}
(i18n/label :t/value-higher-than-send-amount)])
[quo/disclaimer
{:on-change (fn [checked?]
(set-is-amount-locked checked?))
:checked? is-amount-locked?
:container-style style/disclaimer
:icon (if is-amount-locked?
:i/locked
:i/unlocked)
:customization-color account-color}
(i18n/label :t/dont-auto-recalculate-network {:network network-name-str})]
[quo/bottom-actions
{:actions :one-action
:button-one-label (i18n/label :t/update)
:button-one-props {:on-press lock-or-unlock-amount
:customization-color account-color
:disabled? (or (controlled-input/empty-value? input-state)
(controlled-input/input-error input-state)
locked-greater-then-send-amount?)}}]
[quo/numbered-keyboard
{:container-style (style/keyboard-container bottom)
:left-action :dot
:delete-key? true
:on-press (fn [c]
(let [new-text (str input-amount c)
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)
(set-is-amount-locked true)
(set-input-state #(controlled-input/add-character % c)))))
:on-delete (fn []
(set-is-amount-locked true)
(set-input-state controlled-input/delete-last))
:on-long-press-delete (fn []
(set-is-amount-locked true)
(set-input-state controlled-input/delete-all))}]]))}]))
(defn render-network-values
[{:keys [network-values token-symbol on-press on-long-press receiver? loading-routes?
token-not-supported-in-receiver-networks?]}]
[rn/view
(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))]
[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)
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)])
(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))]
[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)
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))])
(defn render-network-links
[{:keys [network-links sender-network-values]}]
@@ -13,8 +13,7 @@
[status-im.contexts.wallet.send.utils :as send-utils]
[status-im.feature-flags :as ff]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.security.core :as security]))
[utils.re-frame :as rf]))
(defn- transaction-title
[{:keys [token-display-name amount account route to-network image-url transaction-type
@@ -238,6 +237,7 @@
bridge-to-chain-id]))
loading-suggested-routes? (rf/sub
[:wallet/wallet-send-loading-suggested-routes?])
transaction-for-signing (rf/sub [:wallet/wallet-send-transaction-for-signing])
from-account-props {:customization-color account-color
:size 32
:emoji (:emoji account)
@@ -268,51 +268,50 @@
: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}
{: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}
:disabled? (not transaction-for-signing)
:customization-color account-color
:on-auth-success #(rf/dispatch
[:wallet/send-transaction
(security/safe-unmask-data %)])
:auth-button-label (i18n/label :t/confirm)}])]
:on-auth-success
(fn [data]
(rf/dispatch
[:wallet/prepare-signatures-for-transactions
:send data]))
:auth-button-label (i18n/label :t/confirm)}])]
:gradient-cover? true
:customization-color (:color account)}
[rn/view
[rn/pressable
{:on-press #(rf/dispatch [:navigate-to-within-stack
[:screen/wallet.transaction-details
:screen/wallet.transaction-confirmation]])}
[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}]]]]]))))
[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}]]]]))))
@@ -303,3 +303,9 @@
(defn bridge-disabled?
[token-symbol]
(not (constants/bridge-assets token-symbol)))
(defn signature-rsv
[signature]
{:r (subs signature 0 64)
:s (subs signature 64 128)
:v (subs signature 128 130)})
@@ -49,6 +49,7 @@
(rf/dispatch [:wallet/get-crypto-on-ramps])))
(let [crypto-on-ramps (rf/sub [:wallet/crypto-on-ramps])
account (rf/sub [:wallet/current-viewing-account-or-default])
has-recurrent? (seq (:recurrent crypto-on-ramps))
[selected-tab set-selected-tab] (rn/use-state initial-tab)
[min-height set-min-height] (rn/use-state 0)
on-layout (rn/use-callback
@@ -56,18 +57,27 @@
(oops/oget % :nativeEvent :layout :height)))]
[:<>
[quo/drawer-top {:title (or title (i18n/label :t/ways-to-buy-assets))}]
[quo/segmented-control
{:size 32
:container-style style/tabs
:default-active initial-tab
:on-change set-selected-tab
:data tabs}]
[rn/flat-list
{:data (if (= selected-tab :recurrent)
(:recurrent crypto-on-ramps)
(:one-time crypto-on-ramps))
:on-layout on-layout
:style (style/list-container min-height)
:render-data {:tab selected-tab
:account account}
:render-fn crypto-on-ramp-item}]]))
(when has-recurrent?
[:<>
[quo/segmented-control
{:size 32
:container-style style/tabs
:default-active initial-tab
:on-change set-selected-tab
:data tabs}]
[rn/flat-list
{:data (if (= selected-tab :recurrent)
(:recurrent crypto-on-ramps)
(:one-time crypto-on-ramps))
:on-layout on-layout
:style (style/list-container min-height)
:render-data {:tab selected-tab
:account account}
:render-fn crypto-on-ramp-item}]])
(when-not has-recurrent?
[rn/flat-list
{:data (:one-time crypto-on-ramps)
:on-layout on-layout
:style (style/list-container min-height)
:render-data {:account account}
:render-fn crypto-on-ramp-item}])]))
@@ -15,11 +15,13 @@
bridge-disabled? :bridge-disabled?
:as token}
{:keys [currency currency-symbol on-token-press disable-token-fn preselected-token-symbol]}]
(let [fiat-value (utils/calculate-token-fiat-value
{:currency currency
:balance total-balance
:token token})
crypto-formatted (utils/get-standard-crypto-format token total-balance)
(let [prices-per-token (rf/sub [:wallet/prices-per-token])
fiat-value (utils/calculate-token-fiat-value
{:currency currency
:balance total-balance
:token token
:prices-per-token prices-per-token})
crypto-formatted (utils/get-standard-crypto-format token total-balance prices-per-token)
fiat-formatted (utils/fiat-formatted-for-ui currency-symbol fiat-value)]
[rn/view {:style {:padding-horizontal 8}}
[quo/token-network
+195 -165
View File
@@ -1,14 +1,11 @@
(ns status-im.contexts.wallet.swap.events
(:require [native-module.core :as native-module]
[re-frame.core :as rf]
(:require [re-frame.core :as rf]
[status-im.constants :as constants]
[status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.send.utils :as send-utils]
[status-im.contexts.wallet.sheets.network-selection.view :as network-selection]
[status-im.contexts.wallet.swap.utils :as swap-utils]
[taoensso.timbre :as log]
[utils.address :as address]
[utils.debounce :as debounce]
[utils.i18n :as i18n]
[utils.money :as money]
[utils.number :as number]))
@@ -114,6 +111,10 @@
(fn [{:keys [db]} [max-slippage]]
{:db (assoc-in db [:wallet :ui :swap :max-slippage] (number/parse-float max-slippage))}))
(rf/reg-event-fx :wallet.swap/set-loading-swap-proposal
(fn [{:keys [db]}]
{:db (assoc-in db [:wallet :ui :swap :loading-swap-proposal?] true)}))
(rf/reg-event-fx :wallet/start-get-swap-proposal
(fn [{:keys [db]} [{:keys [amount-in amount-out clean-approval-transaction?]}]]
(let [wallet-address (get-in db [:wallet :current-viewing-account-address])
@@ -151,21 +152,19 @@
:disabledFromChainIDs disabled-from-chain-ids
:disabledToChainIDs disabled-to-chain-ids
:gasFeeMode gas-rates
:fromLockedAmount from-locked-amount}
amount-in (assoc :amountIn amount-in-hex)
amount-out (assoc :amountOut amount-out-hex))]]
:fromLockedAmount from-locked-amount
:amountOut (or amount-out-hex "0x0")}
amount-in (assoc :amountIn amount-in-hex))]]
(when-let [amount (or amount-in amount-out)]
{:db (update-in db
[:wallet :ui :swap]
#(cond-> %
:always
(assoc
:last-request-uuid request-uuid
:amount amount
:amount-hex amount-in-hex
:loading-swap-proposal? true
:initial-response? true)
:last-request-uuid request-uuid
:amount amount
:amount-hex amount-in-hex
:initial-response? true)
clean-approval-transaction?
(dissoc :approval-transaction-id :approved-amount :swap-proposal)))
:fx [[:dispatch
@@ -246,13 +245,16 @@
[:centralized-metrics/track :metric/swap-proposal-failed {:error (:code error-response)}]]]}))
(rf/reg-event-fx :wallet/stop-get-swap-proposal
(fn []
{:json-rpc/call [{:method "wallet_stopSuggestedRoutesAsyncCalculation"
:params []
:on-error (fn [error]
(log/error "failed to stop fetching swap proposals"
{:event :wallet/stop-get-swap-proposal
:error error}))}]}))
(fn [{:keys [db]}]
(let [route-request-ongoing? (some? (get-in db [:wallet :ui :swap :last-request-uuid]))]
(when route-request-ongoing?
{:db (update-in db [:wallet :ui :swap] dissoc :last-request-uuid)
:json-rpc/call [{:method "wallet_stopSuggestedRoutesAsyncCalculation"
:params []
:on-error (fn [error]
(log/error "failed to stop fetching swap proposals"
{:event :wallet/stop-get-swap-proposal
:error error}))}]}))))
(rf/reg-event-fx
:wallet/clean-swap-proposal
@@ -270,150 +272,21 @@
(fn [{:keys [db]}]
{:db (update-in db [:wallet :ui] dissoc :swap)}))
(rf/reg-event-fx :wallet/swap-transaction
(fn [{:keys [db]} [sha3-pwd]]
(let [wallet-address (get-in db
[:wallet
:current-viewing-account-address])
{:keys [asset-to-pay asset-to-receive
swap-proposal network amount
approval-transaction-id
max-slippage]} (get-in db [:wallet :ui :swap])
transactions (get-in db [:wallet :transactions])
approval-transaction (when approval-transaction-id
(get transactions approval-transaction-id))
already-approved? (and approval-transaction
(= (:status approval-transaction)
:confirmed))
approval-required? (and (:approval-required swap-proposal)
(not already-approved?))
multi-transaction-type constants/multi-transaction-type-swap
swap-chain-id (:chain-id network)
token-id-from (:symbol asset-to-pay)
token-id-to (:symbol asset-to-receive)
erc20-transfer? (and asset-to-pay (not= token-id-from "ETH"))
eth-transfer? (and asset-to-pay (not erc20-transfer?))
token-address (when erc20-transfer?
(get-in asset-to-pay
[:balances-per-chain swap-chain-id
:address]))
data (when erc20-transfer?
(native-module/encode-transfer
(address/normalized-hex wallet-address)
(:amount-in swap-proposal)))
transaction-paths (if approval-required?
[(utils/approval-path
{:route swap-proposal
:token-address token-address
:from-address wallet-address
:to-address wallet-address})]
[(utils/transaction-path
{:to-address wallet-address
:from-address wallet-address
:route swap-proposal
:token-address token-address
:token-id-from token-id-from
:token-id-to token-id-to
:data data
:slippage-percentage max-slippage
:eth-transfer? eth-transfer?})])
request-params [(utils/multi-transaction-command
{:from-address wallet-address
:to-address wallet-address
:from-asset token-id-from
:to-asset (if approval-required?
token-id-from
token-id-to)
:amount-out (if eth-transfer?
(:amount-out swap-proposal)
"0x0")
:multi-transaction-type
multi-transaction-type})
transaction-paths
sha3-pwd]]
(log/info "multi transaction called")
{:json-rpc/call [{:method "wallet_createMultiTransaction"
:params request-params
:on-success (fn [result]
(when result
(let [receive-token-decimals (:decimals asset-to-receive)
amount-out (:amount-out swap-proposal)
receive-amount
(when amount-out
(-> amount-out
(number/hex->whole receive-token-decimals)
(money/to-fixed receive-token-decimals)))]
(rf/dispatch [:centralized-metrics/track
(if approval-required?
:metric/swap-approval-execution-start
:metric/swap-transaction-execution-start)
(cond-> {:network swap-chain-id
:pay_token token-id-from}
(not approval-required?)
(assoc :receive_token token-id-to))])
(rf/dispatch [:wallet.swap/add-authorized-transaction
(cond-> {:transaction result
:approval-transaction?
approval-required?}
(not approval-required?)
(assoc :swap-data
{:pay-token-symbol token-id-from
:pay-amount amount
:receive-token-symbol token-id-to
:receive-amount receive-amount
:swap-chain-id swap-chain-id}))])
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch [:dismiss-modal
(if approval-required?
:screen/wallet.swap-set-spending-cap
:screen/wallet.swap-confirmation)])
(when-not approval-required?
(rf/dispatch [:wallet/end-swap-flow])
(debounce/debounce-and-dispatch
[:toasts/upsert
{:id :swap-transaction-pending
:icon :i/info
:type :neutral
:text (i18n/label :t/swapping-to
{:pay-amount amount
:pay-token-symbol token-id-from
:receive-token-symbol token-id-to
:receive-amount receive-amount})}]
500)))))
:on-error (fn [error]
(rf/dispatch [:centralized-metrics/track
(if approval-required?
:metric/swap-approval-execution-failed
:metric/swap-transaction-execution-failed)
(cond-> {:network swap-chain-id
:error error
:pay_token token-id-from}
(not approval-required?)
(assoc :receive_token token-id-to))])
(log/error "failed swap transaction"
{:event :wallet/swap-transaction
:error error
:params request-params})
(rf/dispatch [:toasts/upsert
{:id :swap-transaction-error
:type :negative
:text (:message error)}]))}]})))
(rf/reg-event-fx :wallet.swap/add-authorized-transaction
(fn [{:keys [db]} [{:keys [transaction swap-data approval-transaction?]}]]
(let [transactions (get-in db [:wallet :transactions] {})
transaction-batch-id (:id transaction)
transaction-hashes (:hashes transaction)
transaction-ids (flatten (vals transaction-hashes))
(fn [{:keys [db]} [{:keys [sent-transactions swap-data approval-transaction?]}]]
(let [wallet-transactions (get-in db [:wallet :transactions] {})
transactions (utils/transactions->hash-to-transaction-map sent-transactions)
transaction-ids (->> transactions
vals
(map :hash))
transaction-id (first transaction-ids)
transaction-details (cond-> (send-utils/map-multitransaction-by-ids transaction-batch-id
transaction-hashes)
transaction-details (cond-> transactions
:always (assoc-in [transaction-id :tx-type] :swap)
swap-data (assoc-in [transaction-id :swap-data] swap-data))
swap-transaction-ids (get-in db [:wallet :swap-transaction-ids])]
{:db (cond-> db
:always (assoc-in [:wallet :transactions]
(merge transactions transaction-details))
(merge wallet-transactions transaction-details))
:always (assoc-in [:wallet :ui :swap :transaction-ids] transaction-ids)
approval-transaction? (assoc-in [:wallet :ui :swap :approval-transaction-id]
transaction-id)
@@ -463,7 +336,8 @@
(not transaction-confirmed?)
(assoc :db (update-in db [:wallet :ui :swap] dissoc :approval-transaction-id)))))))
(rf/reg-event-fx :wallet.swap/swap-transaction-update
(rf/reg-event-fx
:wallet.swap/swap-transaction-update
(fn [{:keys [db]} [{:keys [tx-hash status]}]]
(let [{:keys [pay-amount pay-token-symbol
receive-amount receive-token-symbol
@@ -494,7 +368,8 @@
:receive-amount receive-amount})
(i18n/label :t/swap-failed))}]]]}))))
(rf/reg-event-fx :wallet.swap/flip-assets
(rf/reg-event-fx
:wallet.swap/flip-assets
(fn [{:keys [db]}]
(let [{:keys [asset-to-pay asset-to-receive
swap-proposal amount network]} (get-in db [:wallet :ui :swap])
@@ -528,14 +403,169 @@
:previous_token (:symbol asset-to-receive)
:new_token (:symbol asset-to-pay)}]]]})))
(rf/reg-event-fx :wallet/end-swap-flow
(rf/reg-event-fx
:wallet.swap/set-sign-transactions-callback-fx
(fn [{:keys [db]} [callback-fx]]
{:db (assoc-in db [:wallet :ui :swap :sign-transactions-callback-fx] callback-fx)}))
(rf/reg-event-fx
:wallet.swap/approve
(fn [{:keys [db]}]
(let [launch-screen (get-in db [:wallet :ui :swap :launch-screen])
address (get-in db [:wallet :current-viewing-account-address])]
{:fx [(when (= launch-screen :wallet-stack)
[:dispatch [:wallet/navigate-to-account-within-stack address]])
(let [last-request-uuid (get-in db [:wallet :ui :swap :last-request-uuid])
max-slippage (get-in db [:wallet :ui :swap :max-slippage])]
{:fx [[:dispatch
[:wallet/build-transactions-from-route
{:request-uuid last-request-uuid
:slippage max-slippage}]]
[:dispatch
[:wallet.swap/set-sign-transactions-callback-fx
[:dispatch [:open-modal :screen/wallet.swap-set-spending-cap]]]]]})))
(rf/reg-event-fx
:wallet.swap/review-swap
(fn [{:keys [db]}]
(let [last-request-uuid (get-in db [:wallet :ui :swap :last-request-uuid])
max-slippage (get-in db [:wallet :ui :swap :max-slippage])]
{:db (-> db
(update-in [:wallet :ui :swap] dissoc :transaction-for-signing))
:fx [[:dispatch
[:wallet/build-transactions-from-route
{:request-uuid last-request-uuid
:slippage max-slippage}]]
[:dispatch
[:navigate-to-within-stack
[:screen/wallet.swap-confirmation
:screen/wallet.setup-swap]]]]})))
(defn transaction-approval-required?
[transactions {:keys [swap-proposal approval-transaction-id]}]
(let [approval-transaction (when approval-transaction-id
(get transactions approval-transaction-id))
already-approved? (and approval-transaction
(= (:status approval-transaction)
:confirmed))]
(and (:approval-required swap-proposal)
(not already-approved?))))
(rf/reg-event-fx
:wallet.swap/mark-as-pending
(fn [{:keys [db]} [transaction-id]]
{:db (-> db
(assoc-in [:wallet :transactions transaction-id :status] :pending)
(assoc-in [:wallet :ui :swap :approval-transaction-id] transaction-id))}))
(rf/reg-event-fx
:wallet.swap/transaction-success
(fn [{:keys [db]} [sent-transactions]]
(let [transactions (get-in db [:wallet :transactions])
{:keys [swap-proposal
asset-to-pay
asset-to-receive
network
amount]
:as swap} (get-in db [:wallet :ui :swap])
swap-chain-id (:chain-id network)
token-id-from (:symbol asset-to-pay)
token-id-to (:symbol asset-to-receive)
receive-token-decimals (:decimals asset-to-receive)
amount-out (:amount-out swap-proposal)
receive-amount (when amount-out
(-> amount-out
(number/hex->whole receive-token-decimals)
(money/to-fixed receive-token-decimals)))
approval-required? (transaction-approval-required? transactions swap)]
{:fx [[:dispatch
[:centralized-metrics/track
(if approval-required?
:metric/swap-approval-execution-start
:metric/swap-transaction-execution-start)
(cond-> {:network swap-chain-id
:pay_token token-id-from}
(not approval-required?)
(assoc :receive_token token-id-to))]]
[:dispatch
[:wallet.swap/add-authorized-transaction
(cond-> {:sent-transactions sent-transactions
:approval-transaction? approval-required?}
(not approval-required?)
(assoc :swap-data
{:pay-token-symbol token-id-from
:pay-amount amount
:receive-token-symbol token-id-to
:receive-amount receive-amount
:swap-chain-id swap-chain-id}))]]
(when approval-required?
;; dismiss the spending cap dialog if the transaction needs to be approved
[:dispatch [:dismiss-modal :screen/wallet.swap-set-spending-cap]])
(when approval-required?
[:dispatch [:wallet.swap/mark-as-pending (-> sent-transactions first :hash)]])
(when-not approval-required?
;; just end the whole transaction flow if no approval needed
[:dispatch [:wallet.swap/end-transaction-flow]])
(when-not approval-required?
[:dispatch-later
{:ms 500
:dispatch [:toasts/upsert
{:id :swap-transaction-pending
:icon :i/info
:type :neutral
:text (i18n/label :t/swapping-to
{:pay-amount amount
:pay-token-symbol token-id-from
:receive-token-symbol token-id-to
:receive-amount receive-amount})}]}])]})))
(rf/reg-event-fx
:wallet.swap/transaction-failure
(fn [{:keys [db]} [{:keys [details] :as error}]]
(let [transactions (get-in db [:wallet :transactions])
{:keys [asset-to-pay
asset-to-receive
network]
:as swap} (get-in db [:wallet :ui :swap])
swap-chain-id (:chain-id network)
token-id-from (:symbol asset-to-pay)
token-id-to (:symbol asset-to-receive)
approval-required? (transaction-approval-required? transactions swap)]
{:fx [[:centralized-metrics/track
(if approval-required?
:metric/swap-approval-execution-failed
:metric/swap-transaction-execution-failed)
(cond-> {:network swap-chain-id
:error error
:pay_token token-id-from}
(not approval-required?)
(assoc :receive_token token-id-to))]
[:dispatch [:wallet.swap/end-transaction-flow]]
[:dispatch
[:toasts/upsert
{:id :send-transaction-error
:type :negative
:text (or details "An error occured")}]]]})))
(rf/reg-event-fx
:wallet.swap/clean-up-transaction-flow
(fn [{:keys [db]}]
(let [transactions (get-in db [:wallet :transactions])
swap (get-in db [:wallet :ui :swap])
approval-required? (transaction-approval-required? transactions swap)]
{:db (update-in db [:wallet :ui] dissoc :swap)
:fx [[:dispatch
[:dismiss-modal
(if approval-required?
:screen/wallet.swap-set-spending-cap
:screen/wallet.swap-confirmation)]]]})))
(rf/reg-event-fx
:wallet.swap/end-transaction-flow
(fn [{:keys [db]}]
(let [address (get-in db [:wallet :current-viewing-account-address])]
{:fx [[:dispatch [:wallet/navigate-to-account-within-stack address]]
[:dispatch [:wallet/fetch-activities-for-current-account]]
[:dispatch [:wallet/select-account-tab :activity]]]})))
[:dispatch [:wallet/select-account-tab :activity]]
[:dispatch-later
[{:ms 20
:dispatch [:wallet.swap/clean-up-transaction-flow]}]]]})))
(rf/reg-event-fx :wallet.swap/start-from-account
(fn [{:keys [db]} [account]]
@@ -12,8 +12,7 @@
[status-im.contexts.wallet.swap.set-spending-cap.style :as style]
[utils.address :as address-utils]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.security.core :as security]))
[utils.re-frame :as rf]))
(defn- swap-title
[]
@@ -221,9 +220,8 @@
(let [loading-swap-proposal? (rf/sub [:wallet/swap-loading-swap-proposal?])
swap-proposal (rf/sub [:wallet/swap-proposal-without-fees])
account (rf/sub [:wallet/current-viewing-account])
on-auth-success (rn/use-callback #(rf/dispatch
[:wallet/swap-transaction
(security/safe-unmask-data %)]))]
on-auth-success (rn/use-callback
#(rf/dispatch [:wallet/prepare-signatures-for-transactions :swap %]))]
[standard-auth/slide-button
{:size :size-48
:track-text (i18n/label :t/slide-to-sign)
@@ -26,6 +26,7 @@
[utils.string :as utils.string]))
(def ^:private default-text-for-unfocused-input "0.00")
(def ^:private swap-proposal-debounce-time-ms 1000)
(defn- on-close
[start-point]
@@ -36,13 +37,20 @@
:clean-approval-transaction? true}])
(events-helper/navigate-back))
(defn- start-get-swap-proposal
[amount clean-approval-transaction?]
(rf/dispatch [:wallet/stop-get-swap-proposal])
(rf/dispatch [:wallet.swap/set-loading-swap-proposal])
(debounce/debounce-and-dispatch [:wallet/start-get-swap-proposal
{:amount-in amount
:clean-approval-transaction? clean-approval-transaction?}]
swap-proposal-debounce-time-ms))
(defn- fetch-swap-proposal
[{:keys [amount valid-input? clean-approval-transaction?]}]
(debounce/clear-all)
(if valid-input?
(debounce/debounce-and-dispatch [:wallet/start-get-swap-proposal
{:amount-in amount
:clean-approval-transaction? clean-approval-transaction?}]
100)
(start-get-swap-proposal amount clean-approval-transaction?)
(rf/dispatch [:wallet/clean-swap-proposal
{:clean-amounts? true
:clean-approval-transaction? clean-approval-transaction?}])))
@@ -95,8 +103,10 @@
:subtitle (str max-slippage "%")
:subtitle-icon :i/edit
:size :small
:on-press #(rf/dispatch [:show-bottom-sheet
{:content slippage-settings/view}])}]]))
:on-press (fn []
(when-not loading-swap-proposal?
(rf/dispatch [:show-bottom-sheet
{:content slippage-settings/view}])))}]]))
(defn- pay-token-input
[{:keys [input-state on-max-press on-input-focus on-token-press on-approve-press input-focused?]}]
@@ -195,7 +205,8 @@
:approve)
:token-value approval-label-token-value
:button-props (merge {:on-press on-approve-press}
(when error-response
(when (or loading-swap-proposal?
error-response)
{:disabled? true}))
:customization-color account-color
:token-symbol pay-token-symbol}}]))
@@ -216,6 +227,7 @@
currency-symbol (rf/sub [:profile/currency-symbol])
amount-out (rf/sub [:wallet/swap-proposal-amount-out])
approval-required? (rf/sub [:wallet/swap-proposal-approval-required])
prices-per-token (rf/sub [:wallet/prices-per-token])
receive-token-symbol (:symbol asset-to-receive)
receive-token-decimals (:decimals asset-to-receive)
amount-out-whole-number (when amount-out
@@ -224,10 +236,11 @@
(number/to-fixed amount-out-whole-number receive-token-decimals)
default-text-for-unfocused-input)
receive-token-fiat-value (utils/formatted-token-fiat-value
{:currency currency
:currency-symbol currency-symbol
:balance (or amount-out-whole-number 0)
:token asset-to-receive})]
{:currency currency
:currency-symbol currency-symbol
:balance (or amount-out-whole-number 0)
:token asset-to-receive
:prices-per-token prices-per-token})]
[quo/swap-input
{:type :receive
:error? false
@@ -359,9 +372,7 @@
(not pay-input-error?))
on-review-swap-press (rn/use-callback
(fn []
(rf/dispatch [:navigate-to-within-stack
[:screen/wallet.swap-confirmation
:screen/wallet.setup-swap]])))
(rf/dispatch [:wallet.swap/review-swap])))
on-press (rn/use-callback
(fn [c]
(let
@@ -490,7 +501,7 @@
:on-max-press on-max-press
:input-focused? pay-input-focused?
:on-token-press #(rf/dispatch [:show-bottom-sheet {:content pay-token-bottom-sheet}])
:on-approve-press #(rf/dispatch [:open-modal :screen/wallet.swap-set-spending-cap])
:on-approve-press #(rf/dispatch [:wallet.swap/approve])
:on-input-focus (fn []
(when platform/android? (rf/dispatch [:dismiss-keyboard]))
(set-pay-input-focused? true))}]
@@ -12,8 +12,7 @@
[status-im.contexts.wallet.swap.swap-confirmation.style :as style]
[utils.address :as address-utils]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.security.core :as security]))
[utils.re-frame :as rf]))
(defn- on-close-action
[]
@@ -165,22 +164,23 @@
(defn- slide-button
[]
(let [loading-swap-proposal? (rf/sub [:wallet/swap-loading-swap-proposal?])
swap-proposal (rf/sub [:wallet/swap-proposal-without-fees])
account (rf/sub [:wallet/current-viewing-account])
account-color (:color account)
on-auth-success (rn/use-callback (fn [data]
(rf/dispatch [:wallet/stop-get-swap-proposal])
(rf/dispatch [:wallet/swap-transaction
(security/safe-unmask-data data)])))]
(let [loading-swap-proposal? (rf/sub [:wallet/swap-loading-swap-proposal?])
transaction-for-signing (rf/sub [:wallet/swap-transaction-for-signing])
swap-proposal (rf/sub [:wallet/swap-proposal-without-fees])
account (rf/sub [:wallet/current-viewing-account])
account-color (:color account)]
[standard-auth/slide-button
{:size :size-48
:track-text (i18n/label :t/slide-to-swap)
:container-style {:z-index 2}
:customization-color account-color
:disabled? (or loading-swap-proposal? (not swap-proposal))
:disabled? (or loading-swap-proposal?
(not swap-proposal)
(not transaction-for-signing))
:auth-button-label (i18n/label :t/confirm)
:on-auth-success on-auth-success}]))
:on-auth-success (fn [data]
(rf/dispatch [:wallet/stop-get-swap-proposal])
(rf/dispatch [:wallet/prepare-signatures-for-transactions :swap data]))}]))
(defn footer
[]
@@ -1,4 +0,0 @@
(ns status-im.contexts.wallet.swap.swap-proposal.style)
(def container
{:flex 1})
@@ -1,27 +0,0 @@
(ns status-im.contexts.wallet.swap.swap-proposal.view
(:require
[quo.core :as quo]
[react-native.core :as rn]
[status-im.contexts.wallet.sheets.slippage-settings.view :as slippage-settings]
[status-im.contexts.wallet.swap.swap-proposal.style :as style]
[utils.re-frame :as rf]))
(defn view
[]
(let [max-slippage (rf/sub [:wallet/swap-max-slippage])]
[rn/view {:style style/container}
[quo/button
{:on-press #(rf/dispatch [:show-bottom-sheet
{:content slippage-settings/view}])}
(str "Edit Slippage: " max-slippage "%")]
[quo/button
{:on-press #(rf/dispatch [:navigate-to-within-stack
[:screen/wallet.swap-confirmation :screen/wallet.swap-propasal]])}
"Swap confirmation"]
[quo/button
{:on-press #(rf/dispatch [:open-modal :screen/wallet.swap-set-spending-cap])}
"Set spending cap"]
[quo/button
{:on-press #(rf/dispatch [:navigate-to-within-stack
[:screen/wallet.setup-swap :screen/wallet.swap-propasal]])}
"Setup swap"]]))
@@ -21,6 +21,7 @@
(defn store-token-list
[{:keys [db]} [{:keys [data]}]]
(let [chain-ids (chain/chain-ids db)
profile-currency (get-in db [:profile/profile :currency])
tokens (reduce (fn [{:keys [by-address by-symbol] :as data}
{:keys [name source version tokens]}]
(-> data
@@ -48,13 +49,11 @@
:by-address {}
:by-symbol {}}
data)
symbols (->> tokens
:by-symbol
vals
(map :symbol)
set
vec)
by-symbol-vals (-> tokens :by-symbol vals)
symbols (reduce (fn [acc token]
(conj acc (:symbol token)))
#{}
by-symbol-vals)
supported-chains-by-symbol (reduce
(fn [result
{token-symbol :symbol
@@ -64,9 +63,10 @@
(assoc result token-symbol #{chain-id})))
{}
by-symbol-vals)]
{:fx [[:effects.wallet.tokens/fetch-market-values
{:symbols symbols
:currency constants/profile-default-currency
:currency profile-currency
:on-success [:wallet.tokens/store-market-values]
:on-error [:wallet.tokens/fetch-market-values-failed]}]
[:effects.wallet.tokens/fetch-details
@@ -75,7 +75,7 @@
:on-error [:wallet.tokens/fetch-details-failed]}]
[:effects.wallet.tokens/fetch-prices
{:symbols symbols
:currencies [constants/profile-default-currency]
:currencies [constants/profile-default-currency profile-currency]
:on-success [:wallet.tokens/store-prices]
:on-error [:wallet.tokens/fetch-prices-failed]}]]
:db (-> db
@@ -116,7 +116,7 @@
{}
raw-data)]
{:db (-> db
(assoc-in [:wallet :tokens :market-values-per-token] market-values)
(update-in [:wallet :tokens :market-values-per-token] merge market-values)
(assoc-in [:wallet :ui :loading :market-values] false))})))
(rf/reg-event-fx
@@ -146,7 +146,7 @@
:wallet.tokens/store-prices
(fn [{:keys [db]} [prices]]
{:db (-> db
(assoc-in [:wallet :tokens :prices-per-token] prices)
(update-in [:wallet :tokens :prices-per-token] merge prices)
(assoc-in [:wallet :ui :loading :prices] false))}))
(rf/reg-event-fx
@@ -1,63 +1,8 @@
(ns status-im.contexts.wallet.wallet-connect.utils.rpc
(:require [oops.core :as oops]
[promesa.core :as promesa]
(:require [promesa.core :as promesa]
[status-im.common.json-rpc.events :as rpc-events]
[status-im.constants :as constants]
[utils.hex :as hex]
[utils.transforms :as transforms]))
(defn wallet-build-transaction
[chain-id tx]
(promesa/let [res (rpc-events/call-async :wallet_buildTransaction true chain-id tx)]
{:message-to-sign (oops/oget res :messageToSign)
:tx-args (oops/oget res :txArgs)}))
(defn wallet-build-raw-transaction
[chain-id tx-args signature]
(-> (rpc-events/call-async "wallet_buildRawTransaction"
true
chain-id
(transforms/js-stringify tx-args 0)
signature)
(promesa/then #(oops/oget % "rawTx"))))
(defn wallet-send-transaction-with-signature
[chain-id tx-args signature]
(rpc-events/call-async "wallet_sendTransactionWithSignature"
true
chain-id
constants/transaction-pending-type-wallet-connect-transfer
(transforms/js-stringify tx-args 0)
signature))
(defn wallet-sign-message
[message address password]
(-> (rpc-events/call-async "wallet_signMessage"
true
message
address
password)
(promesa/then hex/normalize-hex)))
(defn wallet-hash-message-eip-191
[message]
(rpc-events/call-async "wallet_hashMessageEIP191" true message))
(defn wallet-safe-sign-typed-data
[data address password chain-id legacy?]
(rpc-events/call-async "wallet_safeSignTypedDataForDApps"
true
data
address
password
chain-id
legacy?))
(defn wallet-get-suggested-fees
[chain-id]
(-> (rpc-events/call-async "wallet_getSuggestedFees" true chain-id)
(promesa/then transforms/js->clj)))
(defn wallet-disconnect-persisted-session
[topic]
(rpc-events/call-async "wallet_disconnectWalletConnectSession" true topic))
@@ -2,9 +2,9 @@
(:require
[native-module.core :as native-module]
[promesa.core :as promesa]
[status-im.contexts.wallet.rpc :as wallet-rpc]
[status-im.contexts.wallet.wallet-connect.utils.data-store :as
data-store]
[status-im.contexts.wallet.wallet-connect.utils.rpc :as rpc]
[utils.hex :as hex]
[utils.transforms :as transforms]))
@@ -19,6 +19,6 @@
(defn personal-sign
[password address data]
(-> (rpc/wallet-hash-message-eip-191 data)
(promesa/then #(rpc/wallet-sign-message % address password))
(-> (wallet-rpc/hash-message-eip-191 data)
(promesa/then #(wallet-rpc/sign-message % address password))
(promesa/then hex/prefix-hex)))
@@ -4,9 +4,10 @@
[native-module.core :as native-module]
[promesa.core :as promesa]
[status-im.constants :as constants]
[status-im.contexts.wallet.rpc :as wallet-rpc]
[status-im.contexts.wallet.wallet-connect.utils.data-store :as
data-store]
[status-im.contexts.wallet.wallet-connect.utils.rpc :as rpc]
[status-im.contexts.wallet.wallet-connect.utils.rpc :as wallet-connect-rpc]
[utils.money :as money]
[utils.transforms :as transforms]))
@@ -119,16 +120,17 @@
"Formats and builds the incoming transaction, adding the missing properties and returning the final
transaction, along with the transaction hash and the suggested fees"
[tx chain-id tx-priority]
(promesa/let [suggested-fees (rpc/wallet-get-suggested-fees chain-id)
(promesa/let [suggested-fees (wallet-rpc/get-suggested-fees chain-id)
{:keys [tx-args message-to-sign]} (->>
(prepare-transaction-fees tx
tx-priority
suggested-fees)
prepare-transaction-for-rpc
(rpc/wallet-build-transaction chain-id))
estimated-time (rpc/wallet-get-transaction-estimated-time
chain-id
(:maxPriorityFeePerGas suggested-fees))]
(wallet-rpc/build-transaction
chain-id))
estimated-time (wallet-connect-rpc/wallet-get-transaction-estimated-time
chain-id
(:maxPriorityFeePerGas suggested-fees))]
{:tx-args tx-args
:tx-hash message-to-sign
:suggested-fees suggested-fees
@@ -137,15 +139,15 @@
(defn sign-transaction
[password address tx-hash tx-args chain-id]
(promesa/let
[signature (rpc/wallet-sign-message tx-hash address password)
raw-tx (rpc/wallet-build-raw-transaction chain-id tx-args signature)]
[signature (wallet-rpc/sign-message tx-hash address password)
raw-tx (wallet-rpc/build-raw-transaction chain-id tx-args signature)]
raw-tx))
(defn send-transaction
[password address tx-hash tx-args chain-id]
(promesa/let
[signature (rpc/wallet-sign-message tx-hash address password)
tx (rpc/wallet-send-transaction-with-signature chain-id
[signature (wallet-rpc/sign-message tx-hash address password)
tx (wallet-rpc/send-transaction-with-signature chain-id
tx-args
signature)]
tx))
@@ -1,8 +1,8 @@
(ns status-im.contexts.wallet.wallet-connect.utils.typed-data
(:require [clojure.string :as string]
[status-im.constants :as constants]
[status-im.contexts.wallet.rpc :as wallet-rpc]
[status-im.contexts.wallet.wallet-connect.utils.networks :as networks]
[status-im.contexts.wallet.wallet-connect.utils.rpc :as rpc]
[utils.number :as number]))
(declare flatten-data)
@@ -88,7 +88,7 @@
[password address data chain-id-eip155 version]
(let [legacy? (= version :v1)
chain-id (networks/eip155->chain-id chain-id-eip155)]
(rpc/wallet-safe-sign-typed-data data
(wallet-rpc/safe-sign-typed-data data
address
password
chain-id
+1 -1
View File
@@ -43,4 +43,4 @@
:stickers/packs-pending #{}
:settings/change-password {}
:keycard {}
:theme :light})
:theme :dark})

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