Compare commits

..
Author SHA1 Message Date
Cristian Lungu 9983dd5f7d ref: wc sessions refactor 2025-01-08 16:05:42 +02:00
Cristian Lungu 1a22a0ac10 ref: moved request functionality w/ schemas 2025-01-04 03:07:38 +02:00
Cristian Lungu 616b5224c7 ref: finished wc proposals 2025-01-04 02:26:12 +02:00
Cristian Lungu 0d8ac7de81 ref: wc part-1 2025-01-04 00:08:47 +02:00
Cristian Lungu e9f97f7b3b ref: consistend wallet-connect uri validation 2025-01-03 20:57:30 +02: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
129 changed files with 2278 additions and 1892 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.

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: 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 %))))
+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}
+3 -2
View File
@@ -4,7 +4,8 @@
["@walletconnect/utils" :as wc-utils]
["@walletconnect/web3wallet$default" :as Web3Wallet]
[cljs-bean.core :as bean]
[oops.core :as oops]))
[oops.core :as oops]
[utils.transforms :as transforms]))
(defn- wallet-connect-core
[project-id]
@@ -34,7 +35,7 @@
(defn parse-uri
[uri]
(-> (oops/ocall wc-utils "parseUri" uri)
(bean/->clj)))
transforms/js->clj))
(defn respond-session-request
[{:keys [web3-wallet topic id result error]}]
+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]])
+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")
+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)]]]))
@@ -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
@@ -7,7 +7,6 @@
[status-im.common.validation.general :as validators]
[status-im.contexts.communities.events]
[status-im.contexts.wallet.wallet-connect.utils.uri :as wc-uri]
[status-im.feature-flags :as ff]
[utils.address :as utils-address]
[utils.debounce :as debounce]
[utils.ethereum.eip.eip681 :as eip681]
@@ -99,9 +98,7 @@
;; TODO: https://github.com/status-im/status-mobile/issues/18744
nil
(and
(wc-uri/valid-uri? scanned-text)
(ff/enabled? ::ff/wallet.wallet-connect))
(-> scanned-text wc-uri/parse wc-uri/valid?)
(handle-wallet-connect scanned-text)
(url? scanned-text)
@@ -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))
@@ -0,0 +1,19 @@
(ns status-im.contexts.wallet.common.utils.account
(:require [clojure.string :as string]))
(defn filter-operable
[accounts]
(filter #(and (:operable? %)
(not (:watch-only? %)))
accounts))
(defn to-eip155
[address chain-id]
(str chain-id ":" address))
(defn from-eip155
[address]
(-> address
(string/split #":")
last))
@@ -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
@@ -9,7 +9,7 @@
[status-im.common.resources :as resources]
[status-im.contexts.wallet.connected-dapps.disconnect-dapp.view :as disconnect-dapp]
[status-im.contexts.wallet.connected-dapps.style :as style]
[status-im.contexts.wallet.wallet-connect.utils.data-store :as data-store]
[status-im.contexts.wallet.wallet-connect.utils.dapp :as dapp]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.string]))
@@ -107,17 +107,15 @@
:content-container-style (style/dapps-list theme)
:render-fn (fn [{:keys [topic pairingTopic name url iconUrl]}]
[quo/dapp
{:dapp {:avatar
(data-store/compute-dapp-icon-path
iconUrl
url)
:name
(data-store/compute-dapp-name
name
url)
:value url
:topic topic
:pairing-topic pairingTopic
{:dapp {:avatar (dapp/compute-icon
iconUrl
url)
:name (dapp/compute-name
name
url)
:value url
:topic topic
:pairing-topic pairingTopic
:customization-color customization-color}
:accessibility-label (str "dapp-" topic)
:state :default
@@ -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
+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
+177 -153
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]))
@@ -151,10 +148,9 @@
: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]
@@ -270,150 +266,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 +330,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 +362,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 +397,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)
@@ -95,8 +95,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 +197,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 +219,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 +228,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 +364,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 +493,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
@@ -4,7 +4,7 @@
[react-native.wallet-connect :as wallet-connect]
[status-im.config :as config]
[status-im.constants :as constants]
[status-im.contexts.wallet.wallet-connect.utils.sessions :as sessions]
[status-im.contexts.wallet.wallet-connect.utils.session :as session]
[status-im.contexts.wallet.wallet-connect.utils.signing :as signing]
[status-im.contexts.wallet.wallet-connect.utils.transactions :as transactions]
[status-im.contexts.wallet.wallet-connect.utils.typed-data :as typed-data]
@@ -44,14 +44,14 @@
(rf/reg-fx
:effects.wallet-connect/disconnect
(fn [{:keys [web3-wallet topic on-success on-fail]}]
(-> (sessions/disconnect web3-wallet topic)
(-> (session/disconnect web3-wallet topic)
(promesa/then on-success)
(promesa/catch on-fail))))
(rf/reg-fx
:effects.wallet-connect/approve-session
(fn [{:keys [web3-wallet proposal-request session-networks address on-success on-fail]}]
(-> (sessions/approve
(-> (session/approve
{:web3-wallet web3-wallet
:proposal-request proposal-request
:address address
@@ -145,6 +145,6 @@
(rf/reg-fx
:effects.wallet-connect/get-sessions
(fn [{:keys [web3-wallet addresses online? on-success on-error]}]
(-> (sessions/get-sessions web3-wallet addresses online?)
(-> (session/get-sessions web3-wallet addresses online?)
(promesa/then on-success)
(promesa/catch on-error))))
@@ -1,11 +1,12 @@
(ns status-im.contexts.wallet.wallet-connect.events.session-proposals
(:require [clojure.string :as string]
[re-frame.core :as rf]
[react-native.wallet-connect :as wallet-connect]
[status-im.contexts.wallet.wallet-connect.utils.data-store :as
data-store]
[status-im.contexts.wallet.common.utils.account :as account-utils]
[status-im.contexts.wallet.wallet-connect.utils.networks :as networks]
[status-im.contexts.wallet.wallet-connect.utils.sessions :as sessions]
[status-im.contexts.wallet.wallet-connect.utils.proposal :as session-proposal]
[status-im.contexts.wallet.wallet-connect.utils.session :as sessions]
[status-im.contexts.wallet.wallet-connect.utils.uri :as uri]
[taoensso.timbre :as log]
[utils.i18n :as i18n]))
@@ -23,37 +24,27 @@
(rf/reg-event-fx
:wallet-connect/on-scan-connection
(fn [{:keys [db]} [scanned-text]]
(let [network-status (:network/status db)
parsed-uri (wallet-connect/parse-uri scanned-text)
version (:version parsed-uri)
valid-wc-uri? (uri/valid-wc-uri? parsed-uri)
expired? (-> parsed-uri
:expiryTimestamp
uri/timestamp-expired?)
version-supported? (uri/version-supported? version)]
(if (or (not valid-wc-uri?)
(not version-supported?)
(= network-status :offline)
expired?)
(let [network-status (:network/status db)
parsed-uri (uri/parse scanned-text)
error-toast (cond
(= network-status :offline)
(i18n/label :t/wallet-connect-no-internet-warning)
(not (uri/valid? parsed-uri))
(i18n/label :t/wallet-connect-wrong-qr)
(uri/expired? parsed-uri)
(i18n/label :t/wallet-connect-qr-expired)
(not (uri/version-supported? parsed-uri))
(i18n/label :t/wallet-connect-version-not-supported
{:version (:version parsed-uri)}))]
(if error-toast
{:fx [[:dispatch
[:toasts/upsert
{:type :negative
:theme :dark
:text (cond (= network-status :offline)
(i18n/label :t/wallet-connect-no-internet-warning)
(not valid-wc-uri?)
(i18n/label :t/wallet-connect-wrong-qr)
expired?
(i18n/label :t/wallet-connect-qr-expired)
(not version-supported?)
(i18n/label :t/wallet-connect-version-not-supported
{:version version})
:else
(i18n/label :t/something-went-wrong))}]]]}
:text error-toast}]]]}
{:fx [[:dispatch [:wallet-connect/pair scanned-text]]]}))))
(rf/reg-event-fx
@@ -63,11 +54,12 @@
(let [accounts (get-in db [:wallet :accounts])
current-viewing-address (get-in db [:wallet :current-viewing-account-address])
sessions (get db :wallet-connect/sessions)
available-accounts (sessions/filter-operable-accounts (vals accounts))
available-accounts (-> accounts vals account-utils/filter-operable)
latest-connected-account-address (sessions/latest-connected-account-address sessions)
networks (networks/get-networks-by-mode db)
session-networks (networks/proposal-networks-intersection proposal networks)
required-networks-supported? (networks/required-networks-supported? proposal networks)]
session-networks (session-proposal/networks-intersection proposal networks)
required-networks-supported? (session-proposal/required-networks-supported? proposal
networks)]
(if (and (not-empty session-networks) required-networks-supported?)
{:db (update db
:wallet-connect/current-proposal assoc
@@ -91,13 +83,12 @@
(rf/reg-event-fx
:wallet-connect/show-session-networks-unsupported-toast
(fn [{:keys [db]} [proposal]]
(let [{:keys [name url]} (data-store/get-session-dapp-metadata proposal)]
{:fx [[:dispatch
[:toasts/upsert
{:type :negative
:theme (:theme db)
:text (i18n/label :t/wallet-connect-networks-not-supported
{:dapp (data-store/compute-dapp-name name url)})}]]]})))
{:fx [[:dispatch
[:toasts/upsert
{:type :negative
:theme (:theme db)
:text (i18n/label :t/wallet-connect-networks-not-supported
{:dapp (session-proposal/dapp-name proposal)})}]]]}))
(rf/reg-event-fx
:wallet-connect/reset-current-session-proposal
@@ -118,13 +109,10 @@
(map networks/chain-id->eip155)
vec)
current-address (get-in db [:wallet-connect/current-proposal :address])
network-status (:network/status db)
expired? (-> current-proposal
(get-in [:params :expiryTimestamp])
uri/timestamp-expired?)]
network-status (:network/status db)]
(if (= network-status :online)
{:db (assoc-in db [:wallet-connect/current-proposal :response-sent?] true)
:fx [(if expired?
:fx [(if (session-proposal/expired? current-proposal)
[:dispatch
[:toasts/upsert
{:id :wallet-connect-proposal-expired
@@ -173,11 +161,9 @@
(fn [{:keys [db]} [proposal]]
(let [web3-wallet (get db :wallet-connect/web3-wallet)
{:keys [request response-sent?]} (:wallet-connect/current-proposal db)
networks (networks/get-proposal-networks (or proposal request))
rejected? (nil? proposal)
dapp-name (-> (data-store/get-session-dapp-metadata (or proposal
request))
:name)]
networks (session-proposal/networks (or proposal request))
dapp-name (session-proposal/dapp-name (or proposal request))
rejected? (nil? proposal)]
{:fx (concat
(when-not response-sent?
[[:effects.wallet-connect/reject-session-proposal
@@ -6,29 +6,32 @@
[status-im.contexts.wallet.wallet-connect.utils.data-store :as
data-store]
[status-im.contexts.wallet.wallet-connect.utils.networks :as networks]
[status-im.contexts.wallet.wallet-connect.utils.request :as session-request]
[status-im.contexts.wallet.wallet-connect.utils.transactions :as transactions]
[status-im.contexts.wallet.wallet-connect.utils.typed-data :as typed-data]
[taoensso.timbre :as log]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.transforms :as transforms]))
[utils.transforms :as transforms]
[cljs.pprint :as pprint]))
(rf/reg-event-fx
:wallet-connect/show-request-modal
(fn [{:keys [db]}]
(let [event (get-in db [:wallet-connect/current-request :event])
method (data-store/get-request-method event)
screen (data-store/method-to-screen method)]
(let [screen (-> db
data-store/get-db-current-request-event
session-request/screen)]
(if screen
{:fx [[:dispatch [:open-modal screen]]]}
(log/error "Didn't find screen for Wallet Connect method"
{:method method
:event :wallet-connect/process-session-request})))))
{:event :wallet-connect/process-session-request})))))
(rf/reg-event-fx
:wallet-connect/process-session-request
(fn [{:keys [db]} [event]]
(let [method (data-store/get-request-method event)
existing-event (get-in db [:wallet-connect/current-request :event])]
(pprint/pprint event)
(let [method (session-request/method event)
existing-event (data-store/get-db-current-request-event db)]
(log/info "Processing Wallet Connect session request" method)
;; NOTE: make sure we don't show two requests at the same time
(when-not existing-event
@@ -95,17 +98,14 @@
:address address
:raw-data prepared-tx
:transaction tx
:chain-id chain-id
:display-data display-data)})))
(rf/reg-event-fx
:wallet-connect/process-eth-send-transaction
(fn [{:keys [db]} [{:keys [on-success]}]]
(let [event (data-store/get-db-current-request-event db)
tx (-> event data-store/get-request-params first)
chain-id (-> event
(get-in [:params :chainId])
networks/eip155->chain-id)]
tx (-> event session-request/params first)
chain-id (session-request/chain event)]
(when tx
{:fx [[:effects.wallet-connect/prepare-transaction
{:tx tx
@@ -120,10 +120,8 @@
:wallet-connect/process-eth-sign-transaction
(fn [{:keys [db]}]
(let [event (data-store/get-db-current-request-event db)
tx (-> event data-store/get-request-params first)
chain-id (-> event
(get-in [:params :chainId])
networks/eip155->chain-id)]
tx (-> event session-request/params first)
chain-id (session-request/chain event)]
{:fx [[:effects.wallet-connect/prepare-transaction
{:tx tx
:chain-id chain-id
@@ -135,9 +133,9 @@
(fn [{:keys [db]}]
(try
(let [[address raw-data] (data-store/get-db-current-request-params db)
session-chain-id (-> (data-store/get-db-current-request-event db)
(get-in [:params :chainId])
networks/eip155->chain-id)
session-chain-id (-> db
data-store/get-db-current-request-event
session-request/chain)
typed-data (-> raw-data
transforms/js-parse
transforms/js->clj)
@@ -189,11 +187,10 @@
(rf/reg-event-fx
:wallet-connect/on-processing-error
(fn [{:keys [db]} [error]]
(let [{:keys [address event]} (get db :wallet-connect/current-request)
method (data-store/get-request-method event)]
(let [event (data-store/get-db-current-request-event db)
method (session-request/method event)]
(log/error "Failed to process Wallet Connect request"
{:error error
:address address
:method method
:wallet-connect-event event
:event :wallet-connect/on-processing-error})
@@ -2,9 +2,8 @@
(:require [re-frame.core :as rf]
[react-native.wallet-connect :as wallet-connect]
[status-im.constants :as constants]
[status-im.contexts.wallet.wallet-connect.utils.data-store :as
data-store]
[status-im.contexts.wallet.wallet-connect.utils.uri :as uri]
[status-im.contexts.wallet.wallet-connect.utils.data-store :as data-store]
[status-im.contexts.wallet.wallet-connect.utils.request :as session-request]
[taoensso.timbre :as log]
[utils.i18n :as i18n]
[utils.transforms :as transforms]))
@@ -12,18 +11,15 @@
(rf/reg-event-fx
:wallet-connect/respond-current-session
(fn [{:keys [db]} [password]]
(let [event (get-in db [:wallet-connect/current-request :event])
method (data-store/get-request-method event)
screen (data-store/method-to-screen method)
expiry (get-in event [:params :request :expiryTimestamp])]
(if (uri/timestamp-expired? expiry)
(let [event (data-store/get-db-current-request-event db)]
(if (session-request/expired? event)
{:fx [[:dispatch
[:toasts/upsert
{:id :new-wallet-account-created
:type :negative
:text (i18n/label :t/wallet-connect-request-expired)}]]
[:dispatch [:dismiss-modal screen]]]}
{:fx [(condp = method
[:dispatch [:dismiss-modal (session-request/screen event)]]]}
{:fx [(condp = (session-request/method event)
constants/wallet-connect-personal-sign-method
[:dispatch [:wallet-connect/respond-sign-message password :personal-sign]]
@@ -58,7 +54,7 @@
:wallet-connect/respond-sign-typed-data
(fn [{:keys [db]} [password typed-data-version]]
(let [{:keys [address raw-data event]} (get db :wallet-connect/current-request)
chain-id (get-in event [:params :chainId])]
chain-id (session-request/chain event)]
{:fx [[:effects.wallet-connect/sign-typed-data
{:password password
:address address
@@ -71,8 +67,9 @@
(rf/reg-event-fx
:wallet-connect/respond-send-transaction-data
(fn [{:keys [db]} [password]]
(let [{:keys [chain-id raw-data address]} (get db :wallet-connect/current-request)
{:keys [tx-hash tx-args]} raw-data]
(let [{:keys [raw-data address event]} (get db :wallet-connect/current-request)
{:keys [tx-hash tx-args]} raw-data
chain-id (session-request/chain event)]
{:fx [[:effects.wallet-connect/send-transaction
{:password password
:address address
@@ -100,7 +97,7 @@
:wallet-connect/on-sign-error
(fn [{:keys [db]} [error]]
(let [{:keys [raw-data address event]} (get db :wallet-connect/current-request)
method (data-store/get-request-method event)]
method (session-request/method event)]
(log/error "Failed to sign Wallet Connect request"
{:error error
:address address
@@ -118,8 +115,8 @@
:wallet-connect/send-response
(fn [{:keys [db]} [{:keys [request result error]}]]
(when-let [{:keys [id topic] :as event} (or request
(get-in db [:wallet-connect/current-request :event]))]
(let [method (data-store/get-request-method event)
(data-store/get-db-current-request-event db))]
(let [method (session-request/method event)
web3-wallet (get db :wallet-connect/web3-wallet)]
{:db (assoc-in db [:wallet-connect/current-request :response-sent?] true)
:fx [[:effects.wallet-connect/respond-session-request
@@ -141,13 +138,14 @@
(rf/reg-event-fx
:wallet-connect/redirect-to-dapp
(fn [{:keys [db]} [url]]
(let [redirect-url (or url
(->> (get db :wallet-connect/current-request)
(data-store/get-current-request-dapp
(get db :wallet-connect/sessions))
:sessionJson
transforms/json->clj
data-store/get-dapp-redirect-url))]
(let [sessions (get db :wallet-connect/sessions)
redirect-url (or url
(-> db
data-store/get-db-current-request-event
(data-store/get-current-request-dapp sessions)
:sessionJson
transforms/json->clj
data-store/get-dapp-redirect-url))]
{:fx [[:effects/open-url redirect-url]]})))
(rf/reg-event-fx
@@ -155,15 +153,14 @@
(fn [{:keys [db]} _]
(let [screen (-> db
(get-in [:wallet-connect/current-request :event])
data-store/get-request-method
data-store/method-to-screen)]
session-request/screen)]
{:fx [[:dispatch [:dismiss-modal screen]]]})))
(rf/reg-event-fx
:wallet-connect/finish-session-request
(fn [{:keys [db]} [result]]
(let [event (get-in db [:wallet-connect/current-request :event])
method (data-store/get-request-method event)
method (session-request/method event)
toast-text (condp contains? method
constants/wallet-connect-message-signing-methods
(i18n/label :t/wallet-connect-message-request-success-toast)
@@ -192,11 +189,11 @@
(rf/reg-event-fx
:wallet-connect/on-request-modal-dismissed
(fn [{:keys [db]}]
(let [{:keys [response-sent? event] :as request} (get db :wallet-connect/current-request)
method (data-store/get-request-method event)
dapp (->> (get db :wallet-connect/sessions)
(data-store/get-current-request-dapp request)
:name)]
(let [{:keys [response-sent? event]} (get db :wallet-connect/current-request)
method (session-request/method event)
dapp (->> (get db :wallet-connect/sessions)
(data-store/get-current-request-dapp event)
:name)]
{:fx (concat
(when-not response-sent?
[[:dispatch
@@ -3,9 +3,10 @@
[status-im.contexts.wallet.wallet-connect.utils.data-store :as
data-store]
[status-im.contexts.wallet.wallet-connect.utils.networks :as networks]
[status-im.contexts.wallet.wallet-connect.utils.sessions :as sessions]
[status-im.contexts.wallet.wallet-connect.utils.session :as session-utils]
[taoensso.timbre :as log]
[utils.i18n :as i18n]))
[utils.i18n :as i18n]
[status-im.contexts.wallet.common.utils.account :as account-utils]))
(rf/reg-event-fx
:wallet-connect/on-session-delete
@@ -58,7 +59,7 @@
(fn [{:keys [db]}]
(let [addresses (->> (get-in db [:wallet :accounts])
vals
sessions/filter-operable-accounts
account-utils/filter-operable
(map :address))]
(if (not (seq addresses))
;; NOTE: Re-trying to get active sessions if accounts weren't loaded yet during
@@ -94,7 +95,7 @@
:wallet-connect/sessions
(fn [sessions]
(->> new-session
sessions/sdk-session->db-session
session-utils/session->db
(conj sessions))))}))
(rf/reg-event-fx
@@ -3,14 +3,13 @@
[quo.core :as quo]
[react-native.core :as rn]
[status-im.contexts.wallet.wallet-connect.modals.common.header.style :as style]
[status-im.contexts.wallet.wallet-connect.utils.data-store :as
data-store]))
[status-im.contexts.wallet.wallet-connect.utils.dapp :as dapp-utils]))
(defn view
[{:keys [label dapp account]}]
[rn/view {:style style/header-container}
(let [{:keys [name iconUrl url]} dapp
image-source (data-store/compute-dapp-icon-path iconUrl url)]
image-source (dapp-utils/compute-icon iconUrl url)]
[rn/view {:style style/dapp-container}
[quo/summary-tag
{:type :dapp
@@ -7,22 +7,19 @@
[react-native.core :as rn]
[status-im.common.floating-button-page.view :as floating-button-page]
[status-im.contexts.wallet.wallet-connect.modals.session-proposal.style :as style]
[status-im.contexts.wallet.wallet-connect.utils.data-store :as data-store]
[utils.i18n :as i18n]
[utils.re-frame :as rf]
[utils.string]))
(defn- dapp-metadata
[]
(let [proposer (rf/sub [:wallet-connect/session-proposer])
{:keys [icons name url]} (:metadata proposer)
first-icon (first icons)
dapp-name (data-store/compute-dapp-name name url)
profile-picture (data-store/compute-dapp-icon-path first-icon url)]
(let [dapp-name (rf/sub [:wallet-connect/session-proposer-name])
dapp-icon (rf/sub [:wallet-connect/session-proposer-icon])
dapp-url (rf/sub [:wallet-connect/session-proposer-url])]
[:<>
[rn/view {:style style/dapp-avatar}
[quo/user-avatar
{:profile-picture profile-picture
{:profile-picture dapp-icon
:size :big
:full-name dapp-name}]]
[quo/page-top
@@ -31,7 +28,7 @@
:context-tag {:type :icon
:size 32
:icon :i/link
:context url}}]]))
:context dapp-url}}]]))
(defn- approval-note
[]
@@ -0,0 +1,33 @@
(ns status-im.contexts.wallet.wallet-connect.utils.dapp
(:require
[clojure.string :as string]
utils.string))
(defn compute-name
"Sometimes dapps have no name or an empty name. Return url as name in that case"
[name url]
(if (seq name)
name
(when (seq url)
(-> url
utils.string/remove-trailing-slash
utils.string/remove-http-prefix
string/capitalize))))
(defn compute-icon
"Some dapps have icons with relative paths, make paths absolute in those cases, send nil if icon is missing"
[icon-path url]
(when (and (seq icon-path)
(seq url))
(if (string/starts-with? icon-path "http")
icon-path
(str (utils.string/remove-trailing-slash url) icon-path))))
(defn best-icon
"Find the icon format from the provided list that can be rendered (prefer images)"
[icons]
(let [image-extensions [".png" ".jpg" ".jpeg"]
image? (fn [icon]
(some #(string/ends-with? icon %) image-extensions))]
(or (first (filter image? icons))
(first icons))))
@@ -1,65 +1,22 @@
(ns status-im.contexts.wallet.wallet-connect.utils.data-store
(:require
[clojure.string :as string]
[status-im.constants :as constants]
[status-im.contexts.wallet.common.utils :as wallet-utils]
[status-im.contexts.wallet.common.utils.networks :as network-utils]
[status-im.contexts.wallet.wallet-connect.utils.request :as session-request]
utils.string
[utils.transforms :as transforms]))
(defn compute-dapp-name
"Sometimes dapps have no name or an empty name. Return url as name in that case"
[name url]
(if (seq name)
name
(when (seq url)
(-> url
utils.string/remove-trailing-slash
utils.string/remove-http-prefix
string/capitalize))))
(defn compute-dapp-icon-path
"Some dapps have icons with relative paths, make paths absolute in those cases, send nil if icon is missing"
[icon-path url]
(when (and (seq icon-path)
(seq url))
(if (string/starts-with? icon-path "http")
icon-path
(str (utils.string/remove-trailing-slash url) icon-path))))
(def method-to-screen
{constants/wallet-connect-personal-sign-method :screen/wallet-connect.sign-message
constants/wallet-connect-eth-sign-typed-method :screen/wallet-connect.sign-message
constants/wallet-connect-eth-sign-method :screen/wallet-connect.sign-message
constants/wallet-connect-eth-sign-typed-v4-method :screen/wallet-connect.sign-message
constants/wallet-connect-eth-send-transaction-method :screen/wallet-connect.send-transaction
constants/wallet-connect-eth-sign-transaction-method :screen/wallet-connect.sign-transaction})
(defn extract-native-call-signature
[data]
(-> data transforms/json->clj :result))
(defn get-request-method
[event]
(get-in event [:params :request :method]))
(defn get-request-params
[event]
(get-in event [:params :request :params]))
(defn get-db-current-request-event
[db]
(get-in db [:wallet-connect/current-request :event]))
(defn get-session-dapp-metadata
[proposal]
(let [metadata (get-in proposal [:params :proposer :metadata])
origin (get-in proposal [:verifyContext :verified :origin])]
(or metadata {:url origin})))
(defn get-current-request-dapp
[request sessions]
(let [dapp-url (get-in request [:event :verifyContext :verified :origin])]
[event sessions]
(let [dapp-url (session-request/url event)]
(->> sessions
(filter (fn [session]
(= (utils.string/remove-trailing-slash dapp-url)
@@ -78,7 +35,7 @@
[db]
(-> db
get-db-current-request-event
get-request-params))
session-request/params))
(defn get-total-connected-dapps
[db]
@@ -8,14 +8,14 @@
(deftest get-current-request-dapp-test
(testing "returns the correct dapp based on the request's origin"
(let [request {:event {:verifyContext {:verified {:origin "https://dapp.com"}}}}
(let [request {:verifyContext {:verified {:origin "https://dapp.com"}}}
sessions [{:url "https://dapp.com"}
{:url "https://anotherdapp.com"}]]
(is (= {:url "https://dapp.com"}
(sut/get-current-request-dapp request sessions)))))
(testing "returns nil if no matching dapp is found"
(let [request {:event {:verifyContext {:verified {:origin "https://dapp.com"}}}}
(let [request {:verifyContext {:verified {:origin "https://dapp.com"}}}
sessions [{:url "https://anotherdapp.com"}]]
(is (nil? (sut/get-current-request-dapp request sessions))))))
@@ -17,10 +17,6 @@
last
edn/read-string))
(defn format-eip155-address
[address chain-id]
(str chain-id ":" address))
(defn- add-full-testnet-name
"Updates the `:full-name` key with the full testnet name if using testnet `:chain-id`.\n
e.g. `{:full-name \"Mainnet\"}` -> `{:full-name \"Mainnet Sepolia\"`}`"
@@ -49,33 +45,19 @@
(set/subset? chain-ids constants/sepolia-chain-ids)
(set/subset? chain-ids constants/mainnet-chain-ids))))
(defn get-proposal-networks
[proposal]
(let [required-namespaces (get-in proposal [:params :requiredNamespaces])
optional-namespaces (get-in proposal [:params :optionalNamespaces])]
(->> [required-namespaces optional-namespaces]
(map #(get-in % [:eip155 :chains]))
(apply concat)
(into #{}))))
(defn proposal-networks-intersection
[proposal supported-networks]
(let [proposed-networks (get-proposal-networks proposal)]
(->> supported-networks
(filter #(->> %
chain-id->eip155
(contains? proposed-networks))))))
(defn networks-intersection
[proposed-networks supported-networks]
(->> supported-networks
(filter #(->> %
chain-id->eip155
(contains? proposed-networks)))))
(defn required-networks-supported?
[proposal supported-networks]
(let [supported-namespaces #{:eip155}
required-namespaces (get-in proposal [:params :requiredNamespaces])]
(when (every? #(contains? supported-namespaces %)
(keys required-namespaces))
(let [required-networks (get-in required-namespaces [:eip155 :chains])
supported-eip155 (set (map chain-id->eip155 supported-networks))]
(every? #(contains? supported-eip155 %)
required-networks)))))
[required-networks supported-networks]
(every? #(-> (map chain-id->eip155 supported-networks)
set
(contains? %))
required-networks))
(defn get-networks-by-mode
[db]
@@ -0,0 +1,135 @@
(ns status-im.contexts.wallet.wallet-connect.utils.proposal
(:require [clojure.string :as string]
[schema.core :as schema]
[status-im.contexts.wallet.wallet-connect.utils.dapp :as dapp-utils]
[status-im.contexts.wallet.wallet-connect.utils.networks :as networks-utils]
[utils.datetime :as datetime]))
(def ^:private ?proposer-metadata
[:map
[:name {:optional true} :string]
[:url {:optional true} :string]
[:description {:optional true} :string]
[:icons {:optional true} [:sequential :string]]])
(def ^:private ?namespace
[:map
[:methods [:sequential :string]]
[:chains [:sequential :string]]
[:events [:sequential :string]]])
(def ^:private ?proposal
[:map
[:id :int]
[:verifyContext
[:map
[:verified
[:map
[:validation :string]
[:origin :string]
[:verifyUrl :string]
[:isScam {:optional true} :boolean]]]]]
[:params
[:map
[:id :int]
[:expiryTimestamp :int]
[:pairingTopic :string]
[:requiredNamespaces [:map-of :keyword ?namespace]]
[:optionalNamespaces [:map-of :keyword ?namespace]]
[:relays [:sequential :any]]
[:proposer
[:map
[:publicKey :string]
[:metadata ?proposer-metadata]]]]]])
(defn expired?
[proposal]
(some-> proposal
:params
:expiryTimestamp
datetime/timestamp-expired?))
(schema/=> expired?
[:=>
[:cat ?proposal]
:boolean])
(defn dapp-metadata
[proposal]
(let [metadata (get-in proposal [:params :proposer :metadata])
origin (get-in proposal [:verifyContext :verified :origin])]
(or metadata {:url origin})))
(schema/=> dapp-metadata
[:=>
[:cat ?proposal]
?proposer-metadata])
(defn dapp-name
[proposal]
(let [{:keys [name url]} (-> proposal dapp-metadata)]
(dapp-utils/compute-name name url)))
(schema/=> dapp-name
[:=>
[:cat ?proposal]
[:maybe :string]])
(defn dapp-icon
[proposal]
(let [{:keys [icons url]} (-> proposal dapp-metadata)
icon (dapp-utils/best-icon icons)]
(dapp-utils/compute-icon icon url)))
(schema/=> dapp-icon
[:=>
[:cat ?proposal]
[:maybe :string]])
(defn dapp-url
[proposal]
(-> proposal dapp-metadata :url))
(schema/=> dapp-url
[:=>
[:cat ?proposal]
[:maybe :string]])
(defn networks
[proposal]
(let [required-namespaces (get-in proposal [:params :requiredNamespaces])
optional-namespaces (get-in proposal [:params :optionalNamespaces])]
(->> [required-namespaces optional-namespaces]
(map #(get-in % [:eip155 :chains]))
(apply concat)
(into #{}))))
(schema/=> networks
[:=>
[:cat ?proposal]
[:set :string]])
(defn networks-intersection
[proposal supported-networks]
(let [proposed-networks (networks proposal)]
(networks-utils/networks-intersection proposed-networks
supported-networks)))
(schema/=> networks-intersection
[:=>
[:cat ?proposal [:sequential :int]]
[:sequential :int]])
(defn required-networks-supported?
[proposal supported-networks]
(let [supported-namespaces #{:eip155}
required-namespaces (get-in proposal [:params :requiredNamespaces])
required-networks (get-in required-namespaces [:eip155 :chains])]
(when (every? #(contains? supported-namespaces %)
(keys required-namespaces))
(networks-utils/required-networks-supported? required-networks supported-networks))))
(schema/=> required-networks-supported?
[:=>
[:cat ?proposal [:sequential :int]]
:boolean])
@@ -0,0 +1,87 @@
(ns status-im.contexts.wallet.wallet-connect.utils.request
(:require
[schema.core :as schema]
[status-im.constants :as constants]
[status-im.contexts.wallet.wallet-connect.utils.networks :as networks]
[utils.datetime :as datetime]))
(def ^:private ?event
[:map
[:id :string]
[:params
[:map
[:chainId :string]
[:request
[:map
[:method :string]
[:params :any]
[:expiryTimestamp :int]]]]]
[:verifyContext
[:map
[:verified
[:map
[:validation :string]
[:origin :string]
[:verifyUrl :string]
[:isScam {:optional true} :boolean]]]]]])
(defn expired?
[event]
(some-> event
:params
:request
:expiryTimestamp
datetime/timestamp-expired?))
(schema/=> expired?
[:=> [:cat ?event]
[:maybe :boolean]])
(defn method
[event]
(get-in event [:params :request :method]))
(schema/=> method
[:=> [:cat ?event]
:string])
(defn chain
[event]
(-> event
(get-in [:params :chainId])
networks/eip155->chain-id))
(schema/=> chain
[:=> [:cat ?event]
:int])
(defn screen
[event]
(let [method-to-screen
{constants/wallet-connect-personal-sign-method :screen/wallet-connect.sign-message
constants/wallet-connect-eth-sign-typed-method :screen/wallet-connect.sign-message
constants/wallet-connect-eth-sign-method :screen/wallet-connect.sign-message
constants/wallet-connect-eth-sign-typed-v4-method :screen/wallet-connect.sign-message
constants/wallet-connect-eth-send-transaction-method :screen/wallet-connect.send-transaction
constants/wallet-connect-eth-sign-transaction-method :screen/wallet-connect.sign-transaction}]
(-> event method method-to-screen)))
(schema/=> screen
[:=> [:cat ?event]
[:maybe :keyword]])
(defn params
[event]
(get-in event [:params :request :params]))
(schema/=> screen
[:=> [:cat ?event]
:any])
(defn url
[event]
(get-in event [:verifyContext :verified :origin]))
(schema/=> screen
[:=> [:cat ?event]
:string])
@@ -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))

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