wallet refactoring

Signed-off-by: Goran Jovic <goranjovic@gmail.com>
This commit is contained in:
Andrey Shovkoplyas 2018-08-10 20:15:16 +03:00 committed by Goran Jovic
parent 2b6d6722b7
commit 3e7c059b59
No known key found for this signature in database
GPG Key ID: D429D1A9B2EB8A8E
31 changed files with 731 additions and 925 deletions

View File

@ -265,7 +265,7 @@
view) view)
(views/defview main-screen-modal-view [current-view & components] (views/defview main-screen-modal-view [current-view & components]
(views/letsubs [signing? [:get-in [:wallet :send-transaction :signing?]]] (views/letsubs [signing? [:get-in [:wallet :send-transaction :show-password-input?]]]
(let [main-screen-view (create-main-screen-view current-view)] (let [main-screen-view (create-main-screen-view current-view)]
[main-screen-view styles/flex [main-screen-view styles/flex
[keyboard-avoiding-view {:flex 1 :flex-direction :column} [keyboard-avoiding-view {:flex 1 :flex-direction :column}

View File

@ -16,7 +16,7 @@ dependencies {
implementation 'com.instabug.library:instabug:3+' implementation 'com.instabug.library:instabug:3+'
implementation 'status-im:function:0.0.1' implementation 'status-im:function:0.0.1'
String statusGoVersion = 'develop-ga6d69eba' String statusGoVersion = 'tags-v0.11.0^0-g4afd9e6c-302'
final String statusGoGroup = 'status-im', statusGoName = 'status-go' final String statusGoGroup = 'status-im', statusGoName = 'status-go'
// Check if the local status-go jar exists, and compile against that if it does // Check if the local status-go jar exists, and compile against that if it does

View File

@ -504,8 +504,8 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
} }
@ReactMethod @ReactMethod
public void approveSignRequest(final String id, final String password, final Callback callback) { public void sendTransaction(final String txArgsJSON, final String password, final Callback callback) {
Log.d(TAG, "approveSignRequest"); Log.d(TAG, "sendTransaction");
if (!checkAvailability()) { if (!checkAvailability()) {
callback.invoke(false); callback.invoke(false);
return; return;
@ -514,7 +514,7 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
Runnable r = new Runnable() { Runnable r = new Runnable() {
@Override @Override
public void run() { public void run() {
String res = Statusgo.ApproveSignRequest(id, password); String res = Statusgo.SendTransaction(txArgsJSON, password);
callback.invoke(res); callback.invoke(res);
} }
}; };
@ -523,8 +523,8 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
} }
@ReactMethod @ReactMethod
public void approveSignRequestWithArgs(final String id, final String password, final String gas, final String gasPrice, final Callback callback) { public void signMessage(final String rpcParams, final Callback callback) {
Log.d(TAG, "approveSignRequestWithArgs"); Log.d(TAG, "signMessage");
if (!checkAvailability()) { if (!checkAvailability()) {
callback.invoke(false); callback.invoke(false);
return; return;
@ -533,7 +533,7 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
Runnable r = new Runnable() { Runnable r = new Runnable() {
@Override @Override
public void run() { public void run() {
String res = Statusgo.ApproveSignRequestWithArgs(id, password, Long.parseLong(gas), Long.parseLong(gasPrice)); String res = Statusgo.SignMessage(rpcParams);
callback.invoke(res); callback.invoke(res);
} }
}; };
@ -541,24 +541,6 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
StatusThreadPoolExecutor.getInstance().execute(r); StatusThreadPoolExecutor.getInstance().execute(r);
} }
@ReactMethod
public void discardSignRequest(final String id) {
Log.d(TAG, "discardSignRequest");
if (!checkAvailability()) {
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
Statusgo.DiscardSignRequest(id);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod @ReactMethod
public void setAdjustResize() { public void setAdjustResize() {
Log.d(TAG, "setAdjustResize"); Log.d(TAG, "setAdjustResize");
@ -667,7 +649,7 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
} }
@ReactMethod @ReactMethod
public void sendWeb3Request(final String payload, final Callback callback) { public void callRPC(final String payload, final Callback callback) {
Runnable r = new Runnable() { Runnable r = new Runnable() {
@Override @Override
public void run() { public void run() {
@ -680,7 +662,7 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
} }
@ReactMethod @ReactMethod
public void sendWeb3PrivateRequest(final String payload, final Callback callback) { public void callPrivateRPC(final String payload, final Callback callback) {
Runnable r = new Runnable() { Runnable r = new Runnable() {
@Override @Override
public void run() { public void run() {

View File

@ -251,43 +251,30 @@ RCT_EXPORT_METHOD(login:(NSString *)address
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
#pragma mark - Approve Sign Request #pragma mark - SendTransaction
//////////////////////////////////////////////////////////////////// approveSignRequests //////////////////////////////////////////////////////////////////// sendTransaction
RCT_EXPORT_METHOD(approveSignRequest:(NSString *)id RCT_EXPORT_METHOD(sendTransaction:(NSString *)txArgsJSON
password:(NSString *)password password:(NSString *)password
callback:(RCTResponseSenderBlock)callback) { callback:(RCTResponseSenderBlock)callback) {
#if DEBUG #if DEBUG
NSLog(@"ApproveSignRequest() method called"); NSLog(@"SendTransaction() method called");
#endif #endif
char * result = ApproveSignRequest((char *) [id UTF8String], (char *) [password UTF8String]); char * result = SendTransaction((char *) [txArgsJSON UTF8String], (char *) [password UTF8String]);
callback(@[[NSString stringWithUTF8String: result]]); callback(@[[NSString stringWithUTF8String: result]]);
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
#pragma mark - Approve Sign Request With Args #pragma mark - SignMessage
//////////////////////////////////////////////////////////////////// approveSignRequestWithArgs //////////////////////////////////////////////////////////////////// signMessage
RCT_EXPORT_METHOD(approveSignRequestWithArgs:(NSString *)id RCT_EXPORT_METHOD(signMessage:(NSString *)message
password:(NSString *)password
gas:(NSString *)gas
gasPrice:(NSString *)gasPrice
callback:(RCTResponseSenderBlock)callback) { callback:(RCTResponseSenderBlock)callback) {
#if DEBUG #if DEBUG
NSLog(@"ApproveSignRequestWithArgs() method called"); NSLog(@"SignMessage() method called");
#endif #endif
char * result = ApproveSignRequestWithArgs((char *) [id UTF8String], (char *) [password UTF8String], (long long) [gas longLongValue], (long long) [gasPrice longLongValue]); char * result = SignMessage((char *) [message UTF8String]);
callback(@[[NSString stringWithUTF8String: result]]); callback(@[[NSString stringWithUTF8String: result]]);
} }
////////////////////////////////////////////////////////////////////
#pragma mark - Discard Sign Request
//////////////////////////////////////////////////////////////////// discardSignRequest
RCT_EXPORT_METHOD(discardSignRequest:(NSString *)id) {
#if DEBUG
NSLog(@"DiscardSignRequest() method called");
#endif
DiscardSignRequest((char *) [id UTF8String]);
}
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
#pragma mark - only android methods #pragma mark - only android methods
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -329,7 +316,7 @@ RCT_EXPORT_METHOD(clearStorageAPIs) {
} }
} }
RCT_EXPORT_METHOD(sendWeb3Request:(NSString *)payload RCT_EXPORT_METHOD(callRPC:(NSString *)payload
callback:(RCTResponseSenderBlock)callback) { callback:(RCTResponseSenderBlock)callback) {
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
char * result = CallRPC((char *) [payload UTF8String]); char * result = CallRPC((char *) [payload UTF8String]);
@ -339,7 +326,7 @@ RCT_EXPORT_METHOD(sendWeb3Request:(NSString *)payload
}); });
} }
RCT_EXPORT_METHOD(sendWeb3PrivateRequest:(NSString *)payload RCT_EXPORT_METHOD(callPrivateRPC:(NSString *)payload
callback:(RCTResponseSenderBlock)callback) { callback:(RCTResponseSenderBlock)callback) {
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
char * result = CallPrivateRPC((char *) [payload UTF8String]); char * result = CallPrivateRPC((char *) [payload UTF8String]);

View File

@ -25,7 +25,7 @@
<artifactItem> <artifactItem>
<groupId>status-im</groupId> <groupId>status-im</groupId>
<artifactId>status-go-ios-simulator</artifactId> <artifactId>status-go-ios-simulator</artifactId>
<version>develop-ga6d69eba</version> <version>tags-v0.11.0^0-g4afd9e6c-302</version>
<type>zip</type> <type>zip</type>
<overWrite>true</overWrite> <overWrite>true</overWrite>
<outputDirectory>./</outputDirectory> <outputDirectory>./</outputDirectory>

File diff suppressed because one or more lines are too long

View File

@ -2,27 +2,31 @@ if(typeof StatusHttpProvider === "undefined"){
var callbackId = 0; var callbackId = 0;
var callbacks = {}; var callbacks = {};
function httpCallback(id, data) { WebViewBridge.onMessage = function (message) {
var result = data; data = JSON.parse(message);
var error = null;
try { if (data.type === "navigate-to-blank")
result = JSON.parse(data); window.location.href = "about:blank";
} catch (e) {
error = {message: "InvalidResponse"}; else if (data.type === "status-api-success")
{
window.STATUS_API = data.data;
window.postMessage({ type: 'STATUS_API_SUCCESS', permissions: data.keys }, "*");
} }
if (callbacks[id]) { else if (data.type === "web3-send-async-callback")
callbacks[id](error, result); {
var id = data.messageId;
if (callbacks[id]) {
callbacks[id](data.error, data.result);
}
} }
}
var StatusHttpProvider = function (host, timeout) {
this.host = host || 'http://localhost:8545';
this.timeout = timeout || 0;
}; };
var StatusHttpProvider = function () {};
StatusHttpProvider.prototype.isStatus = true; StatusHttpProvider.prototype.isStatus = true;
StatusHttpProvider.prototype.isConnected = function () { return true; };
function web3Response (payload, result){ function web3Response (payload, result){
return {id: payload.id, return {id: payload.id,
@ -66,57 +70,19 @@ StatusHttpProvider.prototype.sendAsync = function (payload, callback) {
else { else {
var messageId = callbackId++; var messageId = callbackId++;
callbacks[messageId] = callback; callbacks[messageId] = callback;
if (typeof StatusBridge == "undefined") { WebViewBridge.send(JSON.stringify({type: 'web3-send-async',
var data = { messageId: messageId,
payload: JSON.stringify(payload), payload: payload}));
callbackId: JSON.stringify(messageId),
host: this.host
};
webkit.messageHandlers.sendRequest.postMessage(JSON.stringify(data));
} else {
StatusBridge.sendRequest(this.host, JSON.stringify(messageId), JSON.stringify(payload));
}
}
};
StatusHttpProvider.prototype.prepareRequest = function () {
var request = new XMLHttpRequest();
request.open('POST', this.host, false);
request.setRequestHeader('Content-Type', 'application/json');
return request;
};
/**
* Synchronously tries to make Http request
*
* @method isConnected
* @return {Boolean} returns true if request haven't failed. Otherwise false
*/
StatusHttpProvider.prototype.isConnected = function () {
try {
this.sendAsync({
id: 9999999999,
jsonrpc: '2.0',
method: 'net_listening',
params: []
}, function () {
});
return true;
} catch (e) {
return false;
} }
}; };
} }
var protocol = window.location.protocol var protocol = window.location.protocol
var address = providerAddress || "http://localhost:8545";
console.log(protocol);
if (typeof web3 === "undefined") { if (typeof web3 === "undefined") {
//why do we need this condition?
if (protocol == "https:" || protocol == "http:") { if (protocol == "https:" || protocol == "http:") {
console.log("StatusHttpProvider"); console.log("StatusHttpProvider");
web3 = new Web3(new StatusHttpProvider(address)); web3 = new Web3(new StatusHttpProvider());
web3.eth.defaultAccount = currentAccountAddress; web3.eth.defaultAccount = currentAccountAddress; // currentAccountAddress - injected from status-react
} }
} }

View File

@ -25,16 +25,4 @@
}); });
} }
}); });
WebViewBridge.onMessage = function (message) {
data = JSON.parse(message);
if (data.type === "navigate-to-blank")
window.location.href = "about:blank";
else if (data.type === "status-api-success")
window.STATUS_API = data.data;
window.postMessage({ type: 'STATUS_API_SUCCESS', permissions: data.keys }, "*");
};
}()); }());

View File

@ -37,9 +37,7 @@
{:type [{:id :inbound :label (i18n/label :t/incoming) :checked? true} {:type [{:id :inbound :label (i18n/label :t/incoming) :checked? true}
{:id :outbound :label (i18n/label :t/outgoing) :checked? true} {:id :outbound :label (i18n/label :t/outgoing) :checked? true}
{:id :pending :label (i18n/label :t/pending) :checked? true} {:id :pending :label (i18n/label :t/pending) :checked? true}
{:id :failed :label (i18n/label :t/failed) :checked? true} {:id :failed :label (i18n/label :t/failed) :checked? true}]}})
;; TODO(jeluard) Restore once we support postponing transaction
#_{:id :postponed :label (i18n/label :t/postponed) :checked? true}]}})
(def mainnet-networks (def mainnet-networks
{"mainnet" {:id "mainnet", {"mainnet" {:id "mainnet",
@ -250,11 +248,11 @@
;; Used to generate topic for contact discoveries ;; Used to generate topic for contact discoveries
(def contact-discovery "contact-discovery") (def contact-discovery "contact-discovery")
(def ^:const send-transaction-no-error-code "0") (def ^:const send-transaction-failed-parse-response 1)
(def ^:const send-transaction-default-error-code "1") (def ^:const send-transaction-failed-parse-params 2)
(def ^:const send-transaction-password-error-code "2") (def ^:const send-transaction-no-account-selected 3)
(def ^:const send-transaction-timeout-error-code "3") (def ^:const send-transaction-invalid-tx-sender 4)
(def ^:const send-transaction-discarded-error-code "4") (def ^:const send-transaction-err-decrypt 5)
(def ^:const web3-send-transaction "eth_sendTransaction") (def ^:const web3-send-transaction "eth_sendTransaction")
(def ^:const web3-personal-sign "personal_sign") (def ^:const web3-personal-sign "personal_sign")
@ -268,4 +266,6 @@
(def ^:const dapp-permission-contact-code "CONTACT_CODE") (def ^:const dapp-permission-contact-code "CONTACT_CODE")
(def ^:const status-api-success "status-api-success") (def ^:const status-api-success "status-api-success")
(def ^:const status-api-request "status-api-request") (def ^:const status-api-request "status-api-request")
(def ^:const history-state-changed "history-state-changed") (def ^:const history-state-changed "history-state-changed")
(def ^:const web3-send-async "web3-send-async")
(def ^:const web3-send-async-callback "web3-send-async-callback")

View File

@ -4,7 +4,8 @@
[status-im.i18n :as i18n] [status-im.i18n :as i18n]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.ui.screens.browser.default-dapps :as default-dapps] [status-im.ui.screens.browser.default-dapps :as default-dapps]
[status-im.utils.http :as http])) [status-im.utils.http :as http]
[re-frame.core :as re-frame]))
(defn get-current-url [{:keys [history history-index]}] (defn get-current-url [{:keys [history history-index]}]
(when (and history-index history) (when (and history-index history)
@ -17,8 +18,8 @@
(< history-index (dec (count history)))) (< history-index (dec (count history))))
(defn check-if-dapp-in-list [{:keys [history history-index] :as browser}] (defn check-if-dapp-in-list [{:keys [history history-index] :as browser}]
(let [history-host (http/url-host (try (nth history history-index) (catch js/Error _))) (let [history-host (http/url-host (try (nth history history-index) (catch js/Error _)))
dapp (first (filter #(= history-host (http/url-host (:dapp-url %))) (apply concat (mapv :data default-dapps/all))))] dapp (first (filter #(= history-host (http/url-host (:dapp-url %))) (apply concat (mapv :data default-dapps/all))))]
(if dapp (if dapp
(assoc browser :dapp? true :name (:name dapp)) (assoc browser :dapp? true :name (:name dapp))
(assoc browser :dapp? false :name (i18n/label :t/browser))))) (assoc browser :dapp? false :name (i18n/label :t/browser)))))
@ -72,7 +73,10 @@
(assoc (update-dapp-permissions-fx cofx {:dapp dapp-name (assoc (update-dapp-permissions-fx cofx {:dapp dapp-name
:permissions (vec (set (concat (keys permissions-allowed) :permissions (vec (set (concat (keys permissions-allowed)
user-permissions)))}) user-permissions)))})
:send-to-bridge-fx [permissions-allowed webview]))) :send-to-bridge-fx [{:type constants/status-api-success
:data permissions-allowed
:keys (keys permissions-allowed)}
webview])))
(defn next-permission [cofx params & [permission permissions-data]] (defn next-permission [cofx params & [permission permissions-data]]
(request-permission (request-permission
@ -82,4 +86,16 @@
(update :index inc) (update :index inc)
(and permission permissions-data) (and permission permissions-data)
(assoc-in [:permissions-allowed permission] (get permissions-data permission))))) (assoc-in [:permissions-allowed permission] (get permissions-data permission)))))
(defn web3-send-async [{:keys [db]} {:keys [method] :as payload} message-id]
(if (or (= method constants/web3-send-transaction)
(= method constants/web3-personal-sign))
{:db (update-in db [:wallet :transactions-queue] conj {:message-id message-id :payload payload})
:dispatch [:check-dapps-transactions-queue]}
{:call-rpc [payload
#(re-frame/dispatch [:send-to-bridge
{:type constants/web3-send-async-callback
:messageId message-id
:error %1
:result %2}])]}))

View File

@ -1,5 +1,12 @@
(ns status-im.models.wallet (ns status-im.models.wallet
(:require [status-im.utils.money :as money])) (:require [status-im.utils.money :as money]
[status-im.i18n :as i18n]
[status-im.utils.ethereum.core :as ethereum]
[status-im.constants :as constants]
[status-im.utils.ethereum.tokens :as tokens]
[status-im.ui.screens.navigation :as navigation]
[status-im.utils.hex :as utils.hex]
[status-im.transport.utils :as transport.utils]))
(def min-gas-price-wei (money/bignumber 1)) (def min-gas-price-wei (money/bignumber 1))
@ -22,7 +29,7 @@
"0")) "0"))
(defn- edit-max-fee [edit] (defn- edit-max-fee [edit]
(let [gas (get-in edit [:gas-price :value-number]) (let [gas (get-in edit [:gas-price :value-number])
gas-price (get-in edit [:gas :value-number])] gas-price (get-in edit [:gas :value-number])]
(assoc edit :max-fee (calculate-max-fee gas gas-price)))) (assoc edit :max-fee (calculate-max-fee gas gas-price))))
@ -51,3 +58,114 @@
(defn edit-value (defn edit-value
[key value {:keys [db]}] [key value {:keys [db]}]
{:db (update-in db [:wallet :edit] build-edit key value)}) {:db (update-in db [:wallet :edit] build-edit key value)})
;; DAPP TRANSACTION -> SEND TRANSACTION
(defn prepare-dapp-transaction [{{:keys [id method params]} :payload :as queued-transaction} contacts]
(let [{:keys [to value data gas gasPrice nonce]} (first params)
contact (get contacts (utils.hex/normalize-hex to))]
(cond-> {:id (str id)
:to-name (or (when (nil? to)
(i18n/label :t/new-contract))
contact)
:symbol :ETH
:method method
:dapp-transaction queued-transaction
:to to
:amount (money/bignumber (or value 0))
:gas (cond
gas
(money/bignumber gas)
value
(money/bignumber 21000))
:gas-price (when gasPrice
(money/bignumber gasPrice))
:data data}
nonce
(assoc :nonce nonce))))
;; SEND TRANSACTION -> RPC TRANSACTION
(defn prepare-send-transaction [from {:keys [amount to gas gas-price data nonce]}]
(cond-> {:from (ethereum/normalized-address from)
:to (ethereum/normalized-address to)
:value (ethereum/int->hex amount)
:gas (ethereum/int->hex gas)
:gas-price (ethereum/int->hex gas-price)}
data
(assoc :data data)
nonce
(assoc :nonce nonce)))
;; NOTE (andrey) we need this function, because params may be mixed up, so we need to figure out which one is address
;; and which message
(defn normalize-sign-message-params [params]
(let [first_param (first params)
second_param (second params)
first-param-address? (ethereum/address? first_param)
second-param-address? (ethereum/address? second_param)]
(when (or first-param-address? second-param-address?)
(if first-param-address?
[first_param second_param]
[second_param first_param]))))
(defn web3-error-callback [fx {:keys [webview-bridge]} {:keys [message-id]} message]
(assoc fx :send-to-bridge-fx [{:type constants/web3-send-async-callback
:messageId message-id
:error message}
webview-bridge]))
(defn dapp-complete-transaction [id result method message-id webview]
(cond-> {:send-to-bridge-fx [{:type constants/web3-send-async-callback
:messageId message-id
:result {:jsonrpc "2.0"
:id (int id)
:result result}}
webview]
:dispatch [:navigate-back]}
(= method constants/web3-send-transaction)
(assoc :dispatch-later [{:ms 400 :dispatch [:navigate-to-modal :wallet-transaction-sent-modal]}])))
(defn discard-transaction
[{:keys [db]}]
(let [{:keys [dapp-transaction]} (get-in db [:wallet :send-transaction])]
(cond-> {:db (assoc-in db [:wallet :send-transaction] {})}
dapp-transaction
(web3-error-callback db dapp-transaction "discarded"))))
(defn prepare-unconfirmed-transaction [db now hash]
(let [transaction (get-in db [:wallet :send-transaction])]
(let [chain (:chain db)
token (tokens/symbol->token (keyword chain) (:symbol transaction))]
(-> transaction
(assoc :confirmations "0"
:timestamp (str now)
:type :outbound
:hash hash)
(update :gas-price str)
(assoc :value (:amount transaction))
(assoc :token token)
(update :gas str)
(dissoc :message-id :id)))))
(defn handle-transaction-error [db {:keys [code message]}]
(let [{:keys [dapp-transaction]} (get-in db [:wallet :send-transaction])]
(case code
;;WRONG PASSWORD
constants/send-transaction-err-decrypt
{:db (-> db
(assoc-in [:wallet :send-transaction :wrong-password?] true))}
(cond-> {:db (-> db
navigation/navigate-back
(assoc-in [:wallet :transactions-queue] nil)
(assoc-in [:wallet :send-transaction] {}))
:wallet/show-transaction-error message}
dapp-transaction
(web3-error-callback db dapp-transaction message)))))
(defn transform-data-for-message [{:keys [method] :as transaction}]
(cond-> transaction
(= method constants/web3-personal-sign)
(update :data transport.utils/to-utf8)))

View File

@ -3,9 +3,6 @@
(def adjust-resize 16) (def adjust-resize 16)
(defn move-to-internal-storage [callback]
(native-module/move-to-internal-storage callback))
(defn start-node [config] (defn start-node [config]
(native-module/start-node config)) (native-module/start-node config))
@ -21,33 +18,27 @@
(defn login [address password callback] (defn login [address password callback]
(native-module/login address password callback)) (native-module/login address password callback))
(defn approve-sign-request [id password callback]
(native-module/approve-sign-request id password callback))
(defn approve-sign-request-with-args [id password gas gas-price callback]
(native-module/approve-sign-request-with-args id password gas gas-price callback))
(defn discard-sign-request [id]
(native-module/discard-sign-request id))
(defn set-soft-input-mode [mode] (defn set-soft-input-mode [mode]
(native-module/set-soft-input-mode mode)) (native-module/set-soft-input-mode mode))
(defn clear-web-data [] (defn clear-web-data []
(native-module/clear-web-data)) (native-module/clear-web-data))
(defn call-web3 [payload callback] (defn call-rpc [payload callback]
(native-module/call-web3 payload callback)) (native-module/call-rpc payload callback))
(defn call-web3-private [payload callback] (defn call-private-rpc [payload callback]
(native-module/call-web3-private payload callback)) (native-module/call-private-rpc payload callback))
(defn sign-message [rpcParams callback]
(native-module/sign-message rpcParams callback))
(defn send-transaction [rpcParams password callback]
(native-module/send-transaction rpcParams password callback))
(defn module-initialized! [] (defn module-initialized! []
(native-module/module-initialized!)) (native-module/module-initialized!))
(defn should-move-to-internal-storage? [callback]
(native-module/should-move-to-internal-storage? callback))
(defn notify-users [m callback] (defn notify-users [m callback]
(native-module/notify-users m callback)) (native-module/notify-users m callback))

View File

@ -54,14 +54,6 @@
(.addListener r/device-event-emitter "gethEvent" (.addListener r/device-event-emitter "gethEvent"
#(re-frame/dispatch [:signal-event (.-jsonEvent %)]))) #(re-frame/dispatch [:signal-event (.-jsonEvent %)])))
(defn should-move-to-internal-storage? [on-result]
(when status
(call-module #(.shouldMoveToInternalStorage status on-result))))
(defn move-to-internal-storage [on-result]
(when status
(call-module #(.moveToInternalStorage status on-result))))
(defn stop-node [] (defn stop-node []
(when status (when status
(call-module #(.stopNode status)))) (call-module #(.stopNode status))))
@ -101,27 +93,6 @@
(when status (when status
(call-module #(.login status address password on-result)))) (call-module #(.login status address password on-result))))
(defn approve-sign-request
[id password callback]
(log/debug :approve-sign-request (boolean status) id)
(when status
(call-module #(.approveSignRequest status id password callback))))
(defn approve-sign-request-with-args
[id password gas gas-price callback]
(log/debug :approve-sign-request-with-args (boolean status) id gas gas-price)
(when status
(call-module #(.approveSignRequestWithArgs status id password gas gas-price callback))))
(defn discard-sign-request
[id]
(log/debug :discard-sign-request id)
(when status
(call-module #(.discardSignRequest status id))))
(defn- append-catalog-init [js]
(str js "\n" "var catalog = JSON.stringify(_status_catalog); catalog;"))
(defn set-soft-input-mode [mode] (defn set-soft-input-mode [mode]
(when status (when status
(call-module #(.setSoftInputMode status mode)))) (call-module #(.setSoftInputMode status mode))))
@ -131,13 +102,21 @@
(call-module #(.clearCookies status)) (call-module #(.clearCookies status))
(call-module #(.clearStorageAPIs status)))) (call-module #(.clearStorageAPIs status))))
(defn call-web3 [payload callback] (defn call-rpc [payload callback]
(when status (when status
(call-module #(.sendWeb3Request status payload callback)))) (call-module #(.callRPC status payload callback))))
(defn call-web3-private [payload callback] (defn call-private-rpc [payload callback]
(when status (when status
(call-module #(.sendWeb3PrivateRequest status payload callback)))) (call-module #(.callPrivateRPC status payload callback))))
(defn sign-message [rpcParams callback]
(when status
(call-module #(.signMessage status rpcParams callback))))
(defn send-transaction [rpcParams password callback]
(when status
(call-module #(.sendTransaction status rpcParams password callback))))
(defn close-application [] (defn close-application []
(.closeApplication status)) (.closeApplication status))

View File

@ -11,7 +11,10 @@
[status-im.models.browser :as model] [status-im.models.browser :as model]
[status-im.utils.platform :as platform] [status-im.utils.platform :as platform]
[status-im.utils.utils :as utils] [status-im.utils.utils :as utils]
[status-im.constants :as constants])) [status-im.constants :as constants]
[status-im.native-module.core :as status]
[taoensso.timbre :as log]
[status-im.utils.types :as types]))
(re-frame/reg-fx (re-frame/reg-fx
:browse :browse
@ -20,12 +23,22 @@
(utils.universal-links/open! link) (utils.universal-links/open! link)
(list-selection/browse link)))) (list-selection/browse link))))
(re-frame/reg-fx
:call-rpc
(fn [[payload callback]]
(status/call-rpc
(types/clj->json payload)
(fn [response]
(if (= "" response)
(do
(log/warn :web3-response-error)
(callback "web3-response-error" nil))
(callback nil (.parse js/JSON response)))))))
(re-frame/reg-fx (re-frame/reg-fx
:send-to-bridge-fx :send-to-bridge-fx
(fn [[permissions-allowed webview]] (fn [[message webview]]
(.sendToBridge @webview (.stringify js/JSON (clj->js {:type constants/status-api-success (.sendToBridge webview (types/clj->json message))))
:data permissions-allowed
:keys (keys permissions-allowed)})))))
(re-frame/reg-fx (re-frame/reg-fx
:show-dapp-permission-confirmation-fx :show-dapp-permission-confirmation-fx
@ -67,6 +80,12 @@
:history-index 0 :history-index 0
:history [normalized-url]})))) :history [normalized-url]}))))
(handlers/register-handler-fx
:send-to-bridge
[re-frame/trim-v]
(fn [cofx [message]]
{:send-to-bridge-fx [message (get-in cofx [:db :webview-bridge])]}))
(handlers/register-handler-fx (handlers/register-handler-fx
:open-browser :open-browser
[re-frame/trim-v] [re-frame/trim-v]
@ -112,24 +131,32 @@
(handlers/register-handler-fx (handlers/register-handler-fx
:on-bridge-message :on-bridge-message
[re-frame/trim-v] [re-frame/trim-v]
(fn [{:keys [db] :as cofx} [{{:keys [url]} :navState :keys [type host permissions]} browser webview]] (fn [{:keys [db] :as cofx} [message]]
(cond (let [{:browser/keys [options browsers] :keys [webview-bridge]} db
{:keys [browser-id]} options
browser (get browsers browser-id)
data (types/json->clj message)
{{:keys [url]} :navState :keys [type host permissions payload messageId]} data]
(cond
(and (= type constants/history-state-changed) platform/ios? (not= "about:blank" url)) (and (= type constants/history-state-changed) platform/ios? (not= "about:blank" url))
(model/update-browser-history-fx cofx browser url false) (model/update-browser-history-fx cofx browser url false)
(= type constants/status-api-request) (= type constants/web3-send-async)
(let [{:account/keys [account]} db (model/web3-send-async cofx payload messageId)
{:keys [dapp? name]} browser
dapp-name (if dapp? name host)] (= type constants/status-api-request)
(model/request-permission (let [{:account/keys [account]} db
cofx {:keys [dapp? name]} browser
{:dapp-name dapp-name dapp-name (if dapp? name host)]
:webview webview (model/request-permission
:index 0 cofx
:user-permissions (get-in db [:dapps/permissions dapp-name :permissions]) {:dapp-name dapp-name
:requested-permissions permissions :webview webview-bridge
:permissions-data {constants/dapp-permission-contact-code (:public-key account)}}))))) :index 0
:user-permissions (get-in db [:dapps/permissions dapp-name :permissions])
:requested-permissions permissions
:permissions-data {constants/dapp-permission-contact-code (:public-key account)}}))))))
(handlers/register-handler-fx (handlers/register-handler-fx
:next-dapp-permission :next-dapp-permission

View File

@ -103,18 +103,16 @@
[components.webview-bridge/webview-bridge [components.webview-bridge/webview-bridge
{:dapp? dapp? {:dapp? dapp?
:dapp-name name :dapp-name name
:ref #(reset! webview %) :ref #(do
(reset! webview %)
(re-frame/dispatch [:set :webview-bridge %]))
:source {:uri url} :source {:uri url}
:java-script-enabled true :java-script-enabled true
:bounces false :bounces false
:local-storage-enabled true :local-storage-enabled true
:render-error web-view-error :render-error web-view-error
:on-navigation-state-change #(on-navigation-change % browser) :on-navigation-state-change #(on-navigation-change % browser)
:on-bridge-message #(re-frame/dispatch [:on-bridge-message :on-bridge-message #(re-frame/dispatch [:on-bridge-message %])
(js->clj (.parse js/JSON %)
:keywordize-keys true)
browser
webview])
:on-load #(re-frame/dispatch [:update-browser-options {:error? false}]) :on-load #(re-frame/dispatch [:update-browser-options {:error? false}])
:on-error #(re-frame/dispatch [:update-browser-options {:error? true :on-error #(re-frame/dispatch [:update-browser-options {:error? true
:loading? false}]) :loading? false}])

View File

@ -364,11 +364,6 @@
(re-frame/dispatch [:fetch-web3-node-version-callback resp]))))) (re-frame/dispatch [:fetch-web3-node-version-callback resp])))))
nil)) nil))
(handlers/register-handler-fx
:webview-geo-permissions-granted
(fn [{{:keys [webview-bridge]} :db} _]
(.geoPermissionsGranted webview-bridge)))
(handlers/register-handler-fx (handlers/register-handler-fx
:get-fcm-token :get-fcm-token
(fn [_ _] (fn [_ _]
@ -393,8 +388,6 @@
(instabug/log (str "Signal event: " event-str)) (instabug/log (str "Signal event: " event-str))
(let [{:keys [type event]} (types/json->clj event-str) (let [{:keys [type event]} (types/json->clj event-str)
to-dispatch (case type to-dispatch (case type
"sign-request.queued" [:sign-request-queued event]
"sign-request.failed" [:sign-request-failed event]
"node.started" [:status-node-started] "node.started" [:status-node-started]
"node.stopped" [:status-node-stopped] "node.stopped" [:status-node-stopped]
"module.initialized" [:status-module-initialized] "module.initialized" [:status-module-initialized]

View File

@ -28,11 +28,6 @@
(fn [db [_ path]] (fn [db [_ path]]
(get-in db path))) (get-in db path)))
(reg-sub :signed-up?
:<- [:get-current-account]
(fn [current-account]
(:signed-up? current-account)))
(reg-sub :network (reg-sub :network
:<- [:get-current-account] :<- [:get-current-account]
(fn [current-account] (fn [current-account]

View File

@ -32,10 +32,10 @@
[status-im.ui.screens.wallet.request.views :refer [request-transaction send-transaction-request]] [status-im.ui.screens.wallet.request.views :refer [request-transaction send-transaction-request]]
[status-im.ui.screens.wallet.components.views :as wallet.components] [status-im.ui.screens.wallet.components.views :as wallet.components]
[status-im.ui.screens.wallet.onboarding.setup.views :as wallet.onboarding.setup] [status-im.ui.screens.wallet.onboarding.setup.views :as wallet.onboarding.setup]
[status-im.ui.screens.wallet.send.views :as wallet.send] [status-im.ui.screens.wallet.transaction-fee.views :as wallet.transaction-fee]
[status-im.ui.screens.wallet.settings.views :as wallet-settings] [status-im.ui.screens.wallet.settings.views :as wallet-settings]
[status-im.ui.screens.wallet.transactions.views :as wallet-transactions] [status-im.ui.screens.wallet.transactions.views :as wallet-transactions]
[status-im.ui.screens.wallet.send.transaction-sent.views :refer [transaction-sent transaction-sent-modal]] [status-im.ui.screens.wallet.transaction-sent.views :refer [transaction-sent transaction-sent-modal]]
[status-im.ui.screens.wallet.components.views :refer [contact-code recent-recipients recipient-qr-code]] [status-im.ui.screens.wallet.components.views :refer [contact-code recent-recipients recipient-qr-code]]
[status-im.ui.screens.network-settings.views :refer [network-settings]] [status-im.ui.screens.network-settings.views :refer [network-settings]]
[status-im.ui.screens.network-settings.network-details.views :refer [network-details]] [status-im.ui.screens.network-settings.network-details.views :refer [network-details]]
@ -110,7 +110,7 @@
:wallet-send-transaction-modal send-transaction-modal :wallet-send-transaction-modal send-transaction-modal
:wallet-transaction-sent-modal transaction-sent-modal :wallet-transaction-sent-modal transaction-sent-modal
:wallet-sign-message-modal sign-message-modal :wallet-sign-message-modal sign-message-modal
:wallet-transaction-fee wallet.send/transaction-fee :wallet-transaction-fee wallet.transaction-fee/transaction-fee
:wallet-onboarding-setup-modal wallet.onboarding.setup/modal :wallet-onboarding-setup-modal wallet.onboarding.setup/modal
[react/view [react/text (str "Unknown modal view: " modal-view)]])) [react/view [react/text (str "Unknown modal view: " modal-view)]]))
@ -135,8 +135,7 @@
[component]])]]))) [component]])]])))
(defview main [] (defview main []
(letsubs [signed-up? [:signed-up?] (letsubs [view-id [:get :view-id]]
view-id [:get :view-id]]
{:component-did-mount utils.universal-links/initialize {:component-did-mount utils.universal-links/initialize
:component-will-unmount utils.universal-links/finalize :component-will-unmount utils.universal-links/finalize
:component-will-update (fn [] (react/dismiss-keyboard!))} :component-will-update (fn [] (react/dismiss-keyboard!))}

View File

@ -1,4 +1,5 @@
(ns status-im.ui.screens.wallet.db (ns status-im.ui.screens.wallet.db
(:require-macros [status-im.utils.db :refer [allowed-keys]])
(:require [cljs.spec.alpha :as spec] (:require [cljs.spec.alpha :as spec]
[status-im.i18n :as i18n] [status-im.i18n :as i18n]
status-im.ui.screens.wallet.request.db status-im.ui.screens.wallet.request.db
@ -7,14 +8,36 @@
(spec/def :wallet.send/recipient string?) (spec/def :wallet.send/recipient string?)
(spec/def :wallet/send (spec/keys :req-un [:wallet.send/recipient])) (spec/def :wallet/send (allowed-keys :req-un [:wallet.send/recipient]))
(spec/def :wallet/wallet (spec/keys :opt-un [:wallet/send-transaction :wallet/request-transaction (spec/def :wallet/balance-loading? (spec/nilable boolean?))
:wallet/transactions-queue])) (spec/def :wallet/transactions-loading? (spec/nilable boolean?))
(spec/def :wallet/transactions-sync-started? (spec/nilable boolean?))
;; Placeholder namespace for wallet specs, which are a WIP depending on data (spec/def :wallet/errors (spec/nilable any?))
;; model we decide on for balances, prices, etc. (spec/def :wallet/transactions-last-updated-at (spec/nilable any?))
(spec/def :wallet/chat-transactions (spec/nilable any?))
(spec/def :wallet/transactions (spec/nilable any?))
(spec/def :wallet/transactions-queue (spec/nilable any?))
(spec/def :wallet/edit (spec/nilable any?))
(spec/def :wallet/current-tab (spec/nilable any?))
(spec/def :wallet/current-transaction (spec/nilable any?))
(spec/def :wallet/modal-history? (spec/nilable any?))
(spec/def :wallet/visible-tokens (spec/nilable any?))
(spec/def :wallet/currency (spec/nilable any?))
(spec/def :wallet/balance (spec/nilable any?))
(spec/def :wallet/wallet (allowed-keys :opt-un [:wallet/send-transaction :wallet/request-transaction
:wallet/transactions-queue
:wallet/balance-loading? :wallet/errors :wallet/transactions-loading?
:wallet/transactions-last-updated-at :wallet/chat-transactions
:wallet/transactions-sync-started? :wallet/transactions
:wallet/edit
:wallet/current-tab
:wallet/current-transaction
:wallet/modal-history?
:wallet/visible-tokens
:wallet/currency
:wallet/balance]))
(defn- too-precise-amount? (defn- too-precise-amount?
"Checks if number has any extra digit beyond the allowed number of decimals. "Checks if number has any extra digit beyond the allowed number of decimals.

View File

@ -1,7 +1,8 @@
(ns status-im.ui.screens.wallet.navigation (ns status-im.ui.screens.wallet.navigation
(:require [re-frame.core :as re-frame] (:require [re-frame.core :as re-frame]
[status-im.ui.screens.navigation :as navigation] [status-im.ui.screens.navigation :as navigation]
[status-im.utils.ethereum.core :as ethereum])) [status-im.utils.ethereum.core :as ethereum]
[status-im.constants :as constants]))
(defmethod navigation/preload-data! :wallet (defmethod navigation/preload-data! :wallet
[db _] [db _]
@ -22,6 +23,7 @@
(def transaction-send-default (def transaction-send-default
(let [symbol :ETH] (let [symbol :ETH]
{:gas (ethereum/estimate-gas symbol) {:gas (ethereum/estimate-gas symbol)
:method constants/web3-send-transaction
:symbol symbol})) :symbol symbol}))
(def transaction-request-default (def transaction-request-default

View File

@ -4,8 +4,16 @@
[status-im.utils.money :as money] [status-im.utils.money :as money]
[status-im.utils.security :as security])) [status-im.utils.security :as security]))
(spec/def ::amount (spec/nilable money/valid?)) ; transaction
(spec/def ::from (spec/nilable string?))
(spec/def ::to (spec/nilable string?)) (spec/def ::to (spec/nilable string?))
(spec/def ::amount (spec/nilable money/valid?))
(spec/def ::gas (spec/nilable money/valid?))
(spec/def ::gas-price (spec/nilable money/valid?))
; dapp transaction
(spec/def ::data (spec/nilable string?))
(spec/def ::nonce (spec/nilable money/valid?))
(spec/def ::to-name (spec/nilable string?)) (spec/def ::to-name (spec/nilable string?))
(spec/def ::amount-error (spec/nilable string?)) (spec/def ::amount-error (spec/nilable string?))
(spec/def ::asset-error (spec/nilable string?)) (spec/def ::asset-error (spec/nilable string?))
@ -13,25 +21,22 @@
(spec/def ::password (spec/nilable #(instance? security/MaskedData %))) (spec/def ::password (spec/nilable #(instance? security/MaskedData %)))
(spec/def ::wrong-password? (spec/nilable boolean?)) (spec/def ::wrong-password? (spec/nilable boolean?))
(spec/def ::id (spec/nilable string?)) (spec/def ::id (spec/nilable string?))
(spec/def ::waiting-signal? (spec/nilable boolean?)) (spec/def ::show-password-input? (spec/nilable boolean?))
(spec/def ::signing? (spec/nilable boolean?))
(spec/def ::later? (spec/nilable boolean?))
(spec/def ::height double?) (spec/def ::height double?)
(spec/def ::width double?) (spec/def ::width double?)
(spec/def ::camera-flashlight #{:on :off}) (spec/def ::camera-flashlight #{:on :off})
(spec/def ::in-progress? boolean?) (spec/def ::in-progress? boolean?)
(spec/def ::from-chat? (spec/nilable boolean?)) (spec/def ::from-chat? (spec/nilable boolean?))
(spec/def ::symbol (spec/nilable keyword?)) (spec/def ::symbol (spec/nilable keyword?))
(spec/def ::gas (spec/nilable money/valid?))
(spec/def ::gas-price (spec/nilable money/valid?))
(spec/def ::advanced? boolean?) (spec/def ::advanced? boolean?)
(spec/def ::whisper-identity (spec/nilable string?)) (spec/def ::whisper-identity (spec/nilable string?))
(spec/def ::method (spec/nilable string?)) (spec/def ::method (spec/nilable string?))
(spec/def ::tx-hash (spec/nilable string?)) (spec/def ::tx-hash (spec/nilable string?))
(spec/def ::dapp-transaction (spec/nilable any?))
(spec/def :wallet/send-transaction (allowed-keys (spec/def :wallet/send-transaction (allowed-keys
:opt-un [::amount ::to ::to-name ::amount-error ::asset-error ::amount-text ::password :opt-un [::amount ::to ::to-name ::amount-error ::asset-error ::amount-text
::waiting-signal? ::signing? ::id ::later? ::password ::show-password-input? ::id ::from ::data
::camera-flashlight ::in-progress? ::camera-flashlight ::in-progress? ::dapp-transaction
::wrong-password? ::from-chat? ::symbol ::advanced? ::wrong-password? ::from-chat? ::symbol ::advanced?
::gas ::gas-price ::whisper-identity ::method ::tx-hash])) ::gas ::gas-price ::whisper-identity ::method ::tx-hash]))

View File

@ -1,16 +1,13 @@
(ns status-im.ui.screens.wallet.send.events (ns status-im.ui.screens.wallet.send.events
(:require [clojure.string :as string] (:require [re-frame.core :as re-frame]
[re-frame.core :as re-frame]
[status-im.i18n :as i18n] [status-im.i18n :as i18n]
[status-im.native-module.core :as status] [status-im.native-module.core :as status]
[status-im.ui.screens.db :as db]
[status-im.ui.screens.wallet.db :as wallet.db] [status-im.ui.screens.wallet.db :as wallet.db]
[status-im.utils.ethereum.core :as ethereum] [status-im.utils.ethereum.core :as ethereum]
[status-im.utils.ethereum.erc20 :as erc20] [status-im.utils.ethereum.erc20 :as erc20]
[status-im.utils.ethereum.tokens :as tokens] [status-im.utils.ethereum.tokens :as tokens]
[status-im.utils.handlers :as handlers] [status-im.utils.handlers :as handlers]
[status-im.utils.handlers-macro :as handlers-macro] [status-im.utils.handlers-macro :as handlers-macro]
[status-im.utils.hex :as utils.hex]
[status-im.utils.money :as money] [status-im.utils.money :as money]
[status-im.utils.security :as security] [status-im.utils.security :as security]
[status-im.utils.types :as types] [status-im.utils.types :as types]
@ -19,71 +16,154 @@
[status-im.chat.models.message :as models.message] [status-im.chat.models.message :as models.message]
[status-im.chat.commands.sending :as commands-sending] [status-im.chat.commands.sending :as commands-sending]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.transport.utils :as transport.utils]
[taoensso.timbre :as log]
[status-im.ui.screens.navigation :as navigation] [status-im.ui.screens.navigation :as navigation]
[status-im.wallet.transactions :as wallet.transactions])) [status-im.wallet.transactions :as wallet.transactions]))
;;;; FX ;;;; FX
(re-frame/reg-fx (defn- send-ethers [params on-completed masked-password]
::accept-transaction (status/send-transaction (types/clj->json params)
(fn [{:keys [masked-password id on-completed]}] (security/unmask masked-password)
;; unmasking the password as late as possible to avoid being exposed from app-db on-completed))
(status/approve-sign-request id
(security/unmask masked-password)
on-completed)))
(re-frame/reg-fx (defn- send-tokens [symbol chain {:keys [from to value gas gas-price]} on-completed masked-password]
::accept-transaction-with-changed-gas
(fn [{:keys [masked-password id on-completed gas gas-price default-gas-price]}]
;; unmasking the password as late as possible to avoid being exposed from app-db
(if gas
(status/approve-sign-request-with-args id
(security/unmask masked-password)
(money/to-fixed gas)
(money/to-fixed (or gas-price default-gas-price))
on-completed)
(status/approve-sign-request id
(security/unmask masked-password)
on-completed))))
(defn- send-ethers [{:keys [web3 from to value gas gas-price]}]
(.sendTransaction (.-eth web3)
(clj->js {:from from :to to :value value :gas gas :gasPrice gas-price})
#()))
(defn- send-tokens [{:keys [web3 from to value gas gas-price symbol chain]}]
(let [contract (:address (tokens/symbol->token (keyword chain) symbol))] (let [contract (:address (tokens/symbol->token (keyword chain) symbol))]
(erc20/transfer web3 contract from to value {:gas gas :gasPrice gas-price} #()))) (erc20/transfer contract from to value gas gas-price masked-password on-completed)))
(re-frame/reg-fx (re-frame/reg-fx
::send-transaction ::send-transaction
(fn [{:keys [symbol] :as params}] (fn [[params symbol chain on-completed masked-password]]
(case symbol (case symbol
:ETH (send-ethers params) :ETH (send-ethers params on-completed masked-password)
(send-tokens params)))) (send-tokens symbol chain params on-completed masked-password))))
(re-frame/reg-fx (re-frame/reg-fx
::show-transaction-error ::sign-message
(fn [{:keys [params on-completed]}]
(status/sign-message (types/clj->json params)
on-completed)))
(re-frame/reg-fx
:wallet/show-transaction-error
(fn [message] (fn [message]
;; (andrey) we need this timeout because modal window conflicts with alert ;; (andrey) we need this timeout because modal window conflicts with alert
(utils/set-timeout #(utils/show-popup (i18n/label :t/transaction-failed) message) 1000))) (utils/set-timeout #(utils/show-popup (i18n/label :t/transaction-failed) message) 1000)))
(re-frame/reg-fx
:discard-transaction
(fn [id]
(status/discard-sign-request id)))
;;Helper functions
(defn transaction-valid? [{{:keys [to data]} :args}]
(or (and to (utils.hex/valid-hex? to)) (and data (not= data "0x"))))
(defn dispatch-transaction-completed [result & [modal?]]
(re-frame/dispatch [::transaction-completed {:id (:id result) :response result} modal?]))
;;;; Handlers ;;;; Handlers
;; SEND TRANSACTION
(handlers/register-handler-fx
:wallet/send-transaction
(fn [{{:keys [chain] :as db} :db} _]
(let [{:keys [password symbol in-progress?] :as transaction} (get-in db [:wallet :send-transaction])
from (get-in db [:account/account :address])]
(when-not in-progress?
{:db (-> db
(assoc-in [:wallet :send-transaction :wrong-password?] false)
(assoc-in [:wallet :send-transaction :in-progress?] true))
::send-transaction [(models.wallet/prepare-send-transaction from transaction)
symbol
chain
#(re-frame/dispatch [::transaction-completed (types/json->clj %)])
password]}))))
;; SIGN MESSAGE
(handlers/register-handler-fx
:wallet/sign-message
(fn [{db :db} _]
(let [{:keys [data from password]} (get-in db [:wallet :send-transaction])]
{:db (assoc-in db [:wallet :send-transaction :in-progress?] true)
::sign-message {:params {:data data
:password (security/unmask password)
:account from}
:on-completed #(re-frame/dispatch [::transaction-completed (types/json->clj %)])}})))
;; SEND TRANSACTION (SIGN MESSAGE) CALLBACK
(handlers/register-handler-fx
::transaction-completed
(fn [{:keys [db now]} [_ {:keys [result error]}]]
(let [{:keys [id method whisper-identity to symbol amount-text dapp-transaction]} (get-in db [:wallet :send-transaction])
db' (assoc-in db [:wallet :send-transaction :in-progress?] false)]
(if error
;; ERROR
(models.wallet/handle-transaction-error db' error)
;; RESULT
(merge
{:db (cond-> (assoc-in db' [:wallet :send-transaction] {})
(not= method constants/web3-personal-sign)
(assoc-in [:wallet :transactions result]
(models.wallet/prepare-unconfirmed-transaction db now result)))}
(if dapp-transaction
(let [{:keys [message-id]} dapp-transaction
webview (:webview-bridge db)]
(models.wallet/dapp-complete-transaction (int id) result method message-id webview))
{:dispatch [:send-transaction-message whisper-identity {:address to
:asset (name symbol)
:amount amount-text
:tx-hash result}]}))))))
;; DISCARD TRANSACTION
(handlers/register-handler-fx
:wallet/discard-transaction
(fn [cofx _]
(models.wallet/discard-transaction cofx)))
;; DAPP TRANSACTIONS QUEUE
;; NOTE(andrey) We need this queue because dapp can send several transactions in a row, this is bad behaviour
;; but we need to support it
(handlers/register-handler-fx
:check-dapps-transactions-queue
(fn [{:keys [db]} _]
(let [{:keys [send-transaction transactions-queue]} (:wallet db)
{:keys [payload message-id] :as queued-transaction} (last transactions-queue)
{:keys [method params]} payload
db' (update-in db [:wallet :transactions-queue] drop-last)]
(when (and (not (:id send-transaction)) queued-transaction)
(cond
;;SEND TRANSACTION
(= method constants/web3-send-transaction)
(let [{:keys [gas gasPrice] :as transaction} (models.wallet/prepare-dapp-transaction
queued-transaction (:contacts/contacts db))
{:keys [wallet-set-up-passed?]} (:account/account db)]
{:db (assoc-in db' [:wallet :send-transaction] transaction)
:dispatch-n [[:update-wallet]
(when-not gas
[:wallet/update-estimated-gas (first params)])
(when-not gasPrice
[:wallet/update-gas-price])
[:navigate-to-modal (if wallet-set-up-passed?
:wallet-send-transaction-modal
:wallet-onboarding-setup-modal)]]})
;;SIGN MESSAGE
(= method constants/web3-personal-sign)
(let [[address data] (models.wallet/normalize-sign-message-params params)]
(if (and address data)
{:db (assoc-in db' [:wallet :send-transaction] {:id (str message-id)
:from address
:data data
:dapp-transaction queued-transaction
:method method})
:dispatch [:navigate-to-modal :wallet-sign-message-modal]}
{:db db'})))))))
(handlers/register-handler-fx
:send-transaction-message
(concat models.message/send-interceptors
navigation/navigation-interceptors)
(fn [{:keys [db] :as cofx} [chat-id params]]
;;NOTE(goranjovic): we want to send the payment message only when we have a whisper id
;; for the recipient, we always redirect to `:wallet-transaction-sent` even when we don't
(if-let [send-command (and chat-id (get-in db [:id->command ["send" #{:personal-chats}]]))]
(handlers-macro/merge-fx cofx
(commands-sending/send chat-id send-command params)
(navigation/replace-view :wallet-transaction-sent))
(handlers-macro/merge-fx cofx
(navigation/replace-view :wallet-transaction-sent)))))
(defn set-and-validate-amount-db [db amount symbol decimals] (defn set-and-validate-amount-db [db amount symbol decimals]
(let [{:keys [value error]} (wallet.db/parse-amount amount decimals)] (let [{:keys [value error]} (wallet.db/parse-amount amount decimals)]
(-> db (-> db
@ -96,6 +176,13 @@
(fn [{:keys [db]} [_ amount symbol decimals]] (fn [{:keys [db]} [_ amount symbol decimals]]
{:db (set-and-validate-amount-db db amount symbol decimals)})) {:db (set-and-validate-amount-db db amount symbol decimals)}))
(handlers/register-handler-fx
:wallet/discard-transaction-navigate-back
(fn [cofx _]
(-> cofx
models.wallet/discard-transaction
(assoc :dispatch [:navigate-back]))))
(defn update-gas-price (defn update-gas-price
([db edit? success-event] ([db edit? success-event]
{:update-gas-price {:web3 (:web3 db) {:update-gas-price {:web3 (:web3 db)
@ -130,305 +217,11 @@
(fn [{:keys [db]} [_ advanced?]] (fn [{:keys [db]} [_ advanced?]]
{:db (assoc-in db [:wallet :send-transaction :advanced?] advanced?)})) {:db (assoc-in db [:wallet :send-transaction :advanced?] advanced?)}))
(def ^:private clear-send-properties {:id nil
:signing? false
:wrong-password? false
:waiting-signal? false
:from-chat? false
:in-progress? false
:password nil})
;; TODO(goranjovic) - this is a temporary workaround because in regular `clear-send-properties` we need `:from-chat?`
;; to be false to avoid the wallet onboarding crash if the user accessed transactions from dapps first.
;; On the other hand, if we reset the same flag to false every time we clear the current transaction that would also
;; happen when user clicks Cancel in chat initiated transaction and then all fields would become editable, which is
;; another bug.
;; This temporary workaround resets all fields except the flag if we are in a chat /send transaction.
;; A permanent solution would be to add onboarding check to dapp transaction and/or review the workflow.
(def ^:private partial-clear-send-properties {:id nil
:signing? false
:wrong-password? false
:waiting-signal? false
:in-progress? false
:password nil})
(defn on-transactions-completed [raw-results]
(let [result (types/json->clj raw-results)]
(dispatch-transaction-completed result)))
(defn prepare-transaction [{:keys [id message_id method args]} now]
;;NOTE(goranjovic): the transactions started from chat using /send command
;; are only in ether, so this parameter defaults to ETH
(let [{:keys [from to value symbol data gas gasPrice] :or {symbol :ETH}} args]
{:id id
:from from
:to to
:to-name (when (nil? to)
(i18n/label :t/new-contract))
:symbol symbol
:method method
:value (money/bignumber (or value 0))
:data data
:gas (when (seq gas)
(money/bignumber (money/to-decimal gas)))
:gas-price (when (seq gasPrice)
(money/bignumber (money/to-decimal gasPrice)))
:timestamp now
:message-id message_id}))
;;TRANSACTION QUEUED signal from status-go
(handlers/register-handler-fx (handlers/register-handler-fx
:sign-request-queued :wallet/cancel-entering-password
(fn [{:keys [db]} [_ transaction]]
{:db (update-in db [:wallet :transactions-queue] conj transaction)
:dispatch [:check-transactions-queue]}))
(handlers/register-handler-fx
:check-transactions-queue
(fn [{:keys [db now]} _]
(let [{:keys [send-transaction transactions-queue]} (:wallet db)
{:keys [id method args] :as queued-transaction} (last transactions-queue)
db' (update-in db [:wallet :transactions-queue] drop-last)]
(when (and (not (:id send-transaction)) queued-transaction)
(cond
;;SEND TRANSACTION
(= method constants/web3-send-transaction)
(let [{:keys [gas gasPrice]} args
transaction (prepare-transaction queued-transaction now)
sending-from-dapp? (not (get-in db [:wallet :send-transaction :waiting-signal?]))
new-db (assoc-in db' [:wallet :transactions-unsigned id] transaction)
sender-account (:account/account db)
sending-db {:id id
:method method
:from-chat? (or sending-from-dapp? ;;TODO(goranjovic): figure out why we need to
;; have from-chat? flag for dapp txs and get rid of this
(get-in db [:wallet :send-transaction :from-chat?]))}]
(if sending-from-dapp?
;;SENDING FROM DAPP
{:db (assoc-in new-db [:wallet :send-transaction] sending-db) ; we need to completely reset sending state here
:dispatch-n [[:update-wallet]
[:navigate-to-modal (if (:wallet-set-up-passed? sender-account)
:wallet-send-transaction-modal
:wallet-onboarding-setup-modal)]
(when-not (seq gas)
[:wallet/update-estimated-gas transaction])
(when-not (seq gasPrice)
[:wallet/update-gas-price])]}
;;WALLET SEND SCREEN WAITING SIGNAL
(let [{:keys [password]} (get-in db [:wallet :send-transaction])
new-db' (update-in new-db [:wallet :send-transaction] merge sending-db)] ; just update sending state as we are in wallet flow
{:db new-db'
::accept-transaction {:id id
:masked-password password
:on-completed on-transactions-completed}})))
;;SIGN MESSAGE
(= method constants/web3-personal-sign)
(let [{:keys [data]} args
data' (transport.utils/to-utf8 data)]
(if data'
{:db (-> db'
(assoc-in [:wallet :transactions-unsigned id] {:data data' :id id})
(assoc-in [:wallet :send-transaction] {:id id :method method}))
:dispatch [:navigate-to-modal :wallet-sign-message-modal]}
{:db db'})))))))
(defn this-transaction-signing? [id signing-id view-id modal]
(and (= signing-id id)
(or (= view-id :wallet-send-transaction)
(= view-id :wallet-send-transaction-chat)
(= modal :wallet-send-transaction-modal)
(= modal :wallet-sign-message-modal))))
(defn handle-failed-tx [cofx error_message]
(-> cofx
(assoc ::show-transaction-error error_message)
(update-in [:db :wallet] dissoc :send-transaction)))
;;TRANSACTION FAILED signal from status-go
(handlers/register-handler-fx
:sign-request-failed
(fn [{{:keys [view-id modal] :as db} :db} [_ {:keys [id method error_code error_message]}]]
(let [send-transaction (get-in db [:wallet :send-transaction])]
(case error_code
;;WRONG PASSWORD
constants/send-transaction-password-error-code
{:db (-> db
(assoc-in [:wallet :send-transaction :wrong-password?] true)
(assoc-in [:wallet :send-transaction :waiting-signal?] false))}
;;NO ERROR, DISCARDED, TIMEOUT or DEFAULT ERROR
(if (this-transaction-signing? id (:id send-transaction) view-id modal)
(cond-> {:db (-> db
navigation/navigate-back
(assoc-in [:wallet :transactions-queue] nil)
(update-in [:wallet :transactions-unsigned] dissoc id)
(update-in [:wallet :send-transaction] merge clear-send-properties))}
(= method constants/web3-send-transaction)
(handle-failed-tx error_message))
{:db (update-in db [:wallet :transactions-unsigned] dissoc id)})))))
(defn prepare-unconfirmed-dapp-transaction [now hash transaction]
(-> transaction
(assoc :confirmations "0"
:timestamp (str now)
:type :outbound
:hash hash)
(update :gas-price str)
(update :value str)
(update :gas str)
(dissoc :message-id :id)))
(defn prepare-unconfirmed-status-transaction [db now hash transaction]
(let [chain (:chain db)
token (tokens/symbol->token (keyword chain) (:symbol transaction))]
(-> transaction
(assoc :confirmations "0"
:timestamp (str now)
:type :outbound
:hash hash)
(update :gas-price str)
(assoc :value (:amount transaction))
(assoc :token token)
(update :gas str)
(dissoc :message-id :id))))
(defn prepare-unconfirmed-transaction [db now hash id]
(let [unsigned-transaction (get-in db [:wallet :transactions-unsigned id])
send-transaction (get-in db [:wallet :send-transaction])]
;;TODO(goranjovic) - unify `send-transaction` with transactions-unsigned`
;; currently the latter is only used for transactions initiated from dapps
(if-not (:symbol send-transaction)
(prepare-unconfirmed-dapp-transaction now hash unsigned-transaction)
(prepare-unconfirmed-status-transaction db now hash send-transaction))))
(handlers/register-handler-fx
:send-transaction-message
(concat models.message/send-interceptors
navigation/navigation-interceptors)
(fn [{:keys [db] :as cofx} [chat-id params]]
;;NOTE(goranjovic): we want to send the payment message only when we have a whisper id
;; for the recipient, we always redirect to `:wallet-transaction-sent` even when we don't
(if-let [send-command (and chat-id (get-in db [:id->command ["send" #{:personal-chats}]]))]
(handlers-macro/merge-fx cofx
(commands-sending/send chat-id send-command params)
(navigation/replace-view :wallet-transaction-sent))
(handlers-macro/merge-fx cofx
(navigation/replace-view :wallet-transaction-sent)))))
(handlers/register-handler-fx
::transaction-completed
(fn [{db :db now :now} [_ {:keys [id response] :as params} modal?]]
(let [{:keys [hash error]} response
{:keys [method whisper-identity to symbol amount-text]} (get-in db [:wallet :send-transaction])
db' (assoc-in db [:wallet :send-transaction :in-progress?] false)]
(if (and error (string? error) (not (string/blank? error))) ;; ignore error here, error will be handled in :transaction-failed
{:db db'}
(merge
{:db (cond-> db'
(= method constants/web3-send-transaction)
(assoc-in [:wallet :transactions hash] (prepare-unconfirmed-transaction db now hash id))
true
(update-in [:wallet :transactions-unsigned] dissoc id)
true
(update-in [:wallet :send-transaction] merge clear-send-properties {:tx-hash hash}))}
(if modal?
(cond-> {:dispatch [:navigate-back]}
(= method constants/web3-send-transaction)
(assoc :dispatch-later [{:ms 400 :dispatch [:navigate-to-modal :wallet-transaction-sent-modal]}]))
{:dispatch [:send-transaction-message whisper-identity {:address to
:asset (name symbol)
:amount amount-text
:tx-hash hash}]}))))))
(defn on-transactions-modal-completed [raw-results]
(let [result (types/json->clj raw-results)]
(dispatch-transaction-completed result true)))
(handlers/register-handler-fx
:wallet/sign-transaction
(fn [{{:keys [web3 chain] :as db} :db} [_ later?]]
(let [db' (assoc-in db [:wallet :send-transaction :wrong-password?] false)
{:keys [amount id password to symbol method gas gas-price]} (get-in db [:wallet :send-transaction])]
(if id
{::accept-transaction {:id id
:masked-password password
:on-completed on-transactions-completed}
:db (assoc-in db' [:wallet :send-transaction :in-progress?] true)}
{:db (update-in db' [:wallet :send-transaction] assoc
:waiting-signal? true
:later? later?
:in-progress? true)
::send-transaction {:web3 web3
:from (get-in db [:account/account :address])
:to to
:value amount
:gas gas
:gas-price gas-price
:symbol symbol
:method method
:chain chain}}))))
(handlers/register-handler-fx
:wallet/sign-message-modal
(fn [{db :db} _]
(let [{:keys [id password]} (get-in db [:wallet :send-transaction])]
{:db (assoc-in db [:wallet :send-transaction :in-progress?] true)
::accept-transaction {:id id
:masked-password password
:on-completed on-transactions-modal-completed}})))
(defn sign-transaction-modal [{:keys [db]} default-gas-price]
;;TODO(goranjovic) - unify send-transaction and unsigned-transaction
(let [{:keys [id password] :as send-transaction} (get-in db [:wallet :send-transaction])
{:keys [gas gas-price]} [:wallet.send/unsigned-transaction]]
{:db (assoc-in db [:wallet :send-transaction :in-progress?] true)
::accept-transaction-with-changed-gas {:id id
:masked-password password
:gas (or gas (:gas send-transaction))
:gas-price (or gas-price (:gas-price send-transaction))
:default-gas-price default-gas-price
:on-completed on-transactions-modal-completed}}))
(handlers/register-handler-fx
:wallet/sign-transaction-modal-update-gas-success
(fn [cofx [_ default-gas-price]]
(sign-transaction-modal cofx default-gas-price)))
(handlers/register-handler-fx
:wallet/sign-transaction-modal
(fn [{:keys [db]} _]
(update-gas-price db false :wallet/sign-transaction-modal-update-gas-success)))
(defn discard-transaction
[{:keys [db]}]
(let [{:keys [id from-chat?]} (get-in db [:wallet :send-transaction])
clear-fields (if from-chat? partial-clear-send-properties clear-send-properties)]
(merge {:db (update-in db [:wallet :send-transaction] merge clear-fields)}
(when id
{:discard-transaction id}))))
(handlers/register-handler-fx
:wallet/discard-transaction
(fn [cofx _]
(discard-transaction cofx)))
(handlers/register-handler-fx
:wallet/discard-transaction-navigate-back
(fn [cofx _]
(-> cofx
discard-transaction
(assoc :dispatch [:navigate-back]))))
(handlers/register-handler-fx
:wallet/cancel-signing-modal
(fn [{:keys [db]} _] (fn [{:keys [db]} _]
{:db (update-in db [:wallet :send-transaction] assoc {:db (update-in db [:wallet :send-transaction] assoc
:signing? false :show-password-input? false
:wrong-password? false :wrong-password? false
:password nil)})) :password nil)}))
@ -437,11 +230,6 @@
(fn [{:keys [db]} [_ masked-password]] (fn [{:keys [db]} [_ masked-password]]
{:db (assoc-in db [:wallet :send-transaction :password] masked-password)})) {:db (assoc-in db [:wallet :send-transaction :password] masked-password)}))
(handlers/register-handler-fx
:wallet.send/set-signing?
(fn [{:keys [db]} [_ signing?]]
{:db (assoc-in db [:wallet :send-transaction :signing?] signing?)}))
(handlers/register-handler-fx (handlers/register-handler-fx
:wallet.send/edit-value :wallet.send/edit-value
(fn [cofx [_ key value]] (fn [cofx [_ key value]]
@ -475,7 +263,7 @@
:close-transaction-sent-screen :close-transaction-sent-screen
(fn [{:keys [db]} [_ chat-id]] (fn [{:keys [db]} [_ chat-id]]
{:dispatch [:navigate-back] {:dispatch [:navigate-back]
:dispatch-later [{:ms 400 :dispatch [:check-transactions-queue]}]})) :dispatch-later [{:ms 400 :dispatch [:check-dapps-transactions-queue]}]}))
(handlers/register-handler-fx (handlers/register-handler-fx
:sync-wallet-transactions :sync-wallet-transactions

View File

@ -1,54 +1,43 @@
(ns status-im.ui.screens.wallet.send.subs (ns status-im.ui.screens.wallet.send.subs
(:require [re-frame.core :as re-frame] (:require [re-frame.core :as re-frame]
[status-im.utils.money :as money] [status-im.utils.money :as money]
[status-im.models.wallet :as models.wallet] [status-im.models.wallet :as models.wallet]))
[status-im.utils.hex :as utils.hex]))
(re-frame/reg-sub ::send-transaction (re-frame/reg-sub
:<- [:wallet] ::send-transaction
(fn [wallet] :<- [:wallet]
(:send-transaction wallet))) (fn [wallet]
(:send-transaction wallet)))
(re-frame/reg-sub :wallet.send/symbol (re-frame/reg-sub
:<- [::send-transaction] :wallet.send/symbol
(fn [send-transaction] :<- [::send-transaction]
(:symbol send-transaction))) (fn [send-transaction]
(:symbol send-transaction)))
(re-frame/reg-sub :wallet.send/advanced? (re-frame/reg-sub
:<- [::send-transaction] :wallet.send/advanced?
(fn [send-transaction] :<- [::send-transaction]
(:advanced? send-transaction))) (fn [send-transaction]
(:advanced? send-transaction)))
(re-frame/reg-sub :wallet.send/camera-flashlight (re-frame/reg-sub
:<- [::send-transaction] :wallet.send/camera-flashlight
(fn [send-transaction] :<- [::send-transaction]
(:camera-flashlight send-transaction))) (fn [send-transaction]
(:camera-flashlight send-transaction)))
(re-frame/reg-sub :wallet.send/wrong-password? (re-frame/reg-sub
:<- [::send-transaction] :wallet.send/wrong-password?
(fn [send-transaction] :<- [::send-transaction]
(:wrong-password? send-transaction))) (fn [send-transaction]
(:wrong-password? send-transaction)))
(re-frame/reg-sub :wallet.send/sign-password-enabled? (re-frame/reg-sub
:<- [::send-transaction] :wallet.send/sign-password-enabled?
(fn [{:keys [password]}] :<- [::send-transaction]
(and (not (nil? password)) (not= password "")))) (fn [{:keys [password]}]
(and (not (nil? password)) (not= password ""))))
(re-frame/reg-sub ::unsigned-transactions
:<- [:wallet]
(fn [wallet]
(:transactions-unsigned wallet)))
(re-frame/reg-sub ::unsigned-transaction
:<- [::send-transaction]
:<- [::unsigned-transactions]
(fn [[send-transaction unsigned-transactions]]
(when-let [unsigned-transaction (get unsigned-transactions
(:id send-transaction))]
(merge send-transaction
unsigned-transaction
{:gas (or (:gas send-transaction) (:gas unsigned-transaction))
:gas-price (or (:gas-price send-transaction) (:gas-price unsigned-transaction))}))))
(defn edit-or-transaction-data (defn edit-or-transaction-data
"Set up edit data structure, defaulting to transaction when not available" "Set up edit data structure, defaulting to transaction when not available"
@ -64,16 +53,14 @@
:gas :gas
(money/to-fixed (:gas transaction))))) (money/to-fixed (:gas transaction)))))
(re-frame/reg-sub :wallet/edit (re-frame/reg-sub
:<- [::send-transaction] :wallet/edit
:<- [::unsigned-transaction] :<- [::send-transaction]
:<- [:wallet] :<- [:wallet]
(fn [[send-transaction unsigned-transaction {:keys [edit]}]] (fn [[send-transaction {:keys [edit]}]]
(edit-or-transaction-data (edit-or-transaction-data
(if (:id send-transaction) send-transaction
unsigned-transaction edit)))
send-transaction)
edit)))
(defn check-sufficient-funds [transaction balance symbol amount] (defn check-sufficient-funds [transaction balance symbol amount]
(assoc transaction :sufficient-funds? (assoc transaction :sufficient-funds?
@ -93,25 +80,13 @@
(money/formatted->internal :ETH 18)) (money/formatted->internal :ETH 18))
(money/bignumber available-for-gas)))))) (money/bignumber available-for-gas))))))
(re-frame/reg-sub :wallet.send/transaction (re-frame/reg-sub
:<- [::send-transaction] :wallet.send/transaction
:<- [:balance] :<- [::send-transaction]
(fn [[{:keys [amount symbol] :as transaction} balance]] :<- [:balance]
(-> transaction (fn [[{:keys [amount symbol] :as transaction} balance]]
(models.wallet/add-max-fee) (-> transaction
(check-sufficient-funds balance symbol amount) (models.wallet/transform-data-for-message)
(check-sufficient-gas balance symbol amount)))) (models.wallet/add-max-fee)
(check-sufficient-funds balance symbol amount)
(re-frame/reg-sub :wallet.send/unsigned-transaction (check-sufficient-gas balance symbol amount))))
:<- [::unsigned-transaction]
:<- [:get-contacts-by-address]
:<- [:balance]
(fn [[{:keys [value to symbol] :as transaction} contacts balance]]
(when transaction
(let [contact (contacts (utils.hex/normalize-hex to))]
(-> transaction
(assoc :amount value
:to-name (:name contact))
(models.wallet/add-max-fee)
(check-sufficient-funds balance symbol value)
(check-sufficient-gas balance symbol value))))))

View File

@ -23,9 +23,45 @@
[status-im.utils.security :as security] [status-im.utils.security :as security]
[status-im.utils.utils :as utils] [status-im.utils.utils :as utils]
[status-im.utils.ethereum.tokens :as tokens] [status-im.utils.ethereum.tokens :as tokens]
[status-im.utils.ethereum.core :as ethereum])) [status-im.utils.ethereum.core :as ethereum]
[status-im.transport.utils :as transport.utils]))
(defview sign-panel [message-label spinning?] (defn- toolbar [modal? title]
(let [action (if modal? act/close-white act/back-white)]
[toolbar/toolbar {:style wallet.styles/toolbar}
[toolbar/nav-button (action (if modal?
#(re-frame/dispatch [:wallet/discard-transaction-navigate-back])
#(act/default-handler)))]
[toolbar/content-title {:color :white} title]]))
(defn- advanced-cartouche [{:keys [max-fee gas gas-price]}]
[react/view
[wallet.components/cartouche {:on-press #(do (re-frame/dispatch [:wallet.send/clear-gas])
(re-frame/dispatch [:navigate-to-modal :wallet-transaction-fee]))}
(i18n/label :t/wallet-transaction-fee)
[react/view {:style styles/advanced-options-text-wrapper
:accessibility-label :transaction-fee-button}
[react/text {:style styles/advanced-fees-text}
(str max-fee " " (i18n/label :t/eth))]
[react/text {:style styles/advanced-fees-details-text}
(str (money/to-fixed gas) " * " (money/to-fixed (money/wei-> :gwei gas-price)) (i18n/label :t/gwei))]]]])
(defn- advanced-options [advanced? transaction scroll]
[react/view {:style styles/advanced-wrapper}
[react/touchable-highlight {:on-press (fn []
(re-frame/dispatch [:wallet.send/toggle-advanced (not advanced?)])
(when (and scroll @scroll) (utils/set-timeout #(.scrollToEnd @scroll) 350)))}
[react/view {:style styles/advanced-button-wrapper}
[react/view {:style styles/advanced-button
:accessibility-label :advanced-button}
[react/i18n-text {:style (merge wallet.components.styles/label
styles/advanced-label)
:key :wallet-advanced}]
[vector-icons/icon (if advanced? :icons/up :icons/down) {:color :white}]]]]
(when advanced?
[advanced-cartouche transaction])])
(defview password-input-panel [message-label spinning?]
(letsubs [account [:get-current-account] (letsubs [account [:get-current-account]
wrong-password? [:wallet.send/wrong-password?] wrong-password? [:wallet.send/wrong-password?]
signing-phrase (:signing-phrase @account) signing-phrase (:signing-phrase @account)
@ -37,8 +73,6 @@
[tooltip/tooltip (i18n/label :t/wrong-password) styles/password-error-tooltip]) [tooltip/tooltip (i18n/label :t/wrong-password) styles/password-error-tooltip])
[react/animated-view {:style (styles/sign-panel opacity-value)} [react/animated-view {:style (styles/sign-panel opacity-value)}
[react/view styles/spinner-container [react/view styles/spinner-container
;;NOTE(goranjovic) - android build doesn't seem to react on change in `:animating` property, so
;;we have this workaround of just using `when` around the whole element.
(when spinning? (when spinning?
[react/activity-indicator {:animating true [react/activity-indicator {:animating true
:size :large}])] :size :large}])]
@ -58,8 +92,8 @@
:accessibility-label :enter-password-input :accessibility-label :enter-password-input
:auto-capitalize :none}]]]])) :auto-capitalize :none}]]]]))
;; "Cancel" and "Sign Transaction >" buttons, signing with password ;; "Cancel" and "Sign Transaction >" or "Sign >" buttons, signing with password
(defview signing-buttons [spinning? cancel-handler sign-handler sign-label] (defview enter-password-buttons [spinning? cancel-handler sign-handler sign-label]
(letsubs [sign-enabled? [:wallet.send/sign-password-enabled?]] (letsubs [sign-enabled? [:wallet.send/sign-password-enabled?]]
[bottom-buttons/bottom-buttons [bottom-buttons/bottom-buttons
styles/sign-buttons styles/sign-buttons
@ -74,156 +108,34 @@
(i18n/label sign-label) (i18n/label sign-label)
[vector-icons/icon :icons/forward {:color :white}]]])) [vector-icons/icon :icons/forward {:color :white}]]]))
(defn- sign-enabled? [amount-error to amount modal?] ;; "Sign Transaction >" button
(and (defn- sign-transaction-button [amount-error to amount sufficient-funds? sufficient-gas? modal?]
(nil? amount-error) (let [sign-enabled? (and (nil? amount-error)
(or modal? (not (empty? to))) ;;NOTE(goranjovic) - contract creation will have empty `to` (or modal? (not (empty? to))) ;;NOTE(goranjovic) - contract creation will have empty `to`
(not (nil? amount)))) (not (nil? amount))
sufficient-funds?
;; "Sign Later" and "Sign Transaction >" buttons sufficient-gas?)]
(defn- sign-button [amount-error to amount sufficient-funds? sufficient-gas? modal?]
(let [sign-enabled? (sign-enabled? amount-error to amount modal?)
immediate-sign-enabled? (and sign-enabled? sufficient-funds? sufficient-gas?)]
[bottom-buttons/bottom-buttons [bottom-buttons/bottom-buttons
styles/sign-buttons styles/sign-buttons
[react/view] [react/view]
[button/button {:style components.styles/flex [button/button {:style components.styles/flex
:disabled? (not immediate-sign-enabled?) :disabled? (not sign-enabled?)
:on-press #(re-frame/dispatch [:wallet.send/set-signing? true]) :on-press #(re-frame/dispatch [:set-in
[:wallet :send-transaction :show-password-input?]
true])
:text-style {:color :white} :text-style {:color :white}
:accessibility-label :sign-transaction-button} :accessibility-label :sign-transaction-button}
(i18n/label :t/transactions-sign-transaction) (i18n/label :t/transactions-sign-transaction)
[vector-icons/icon :icons/forward {:color (if immediate-sign-enabled? :white :gray)}]]])) [vector-icons/icon :icons/forward {:color (if sign-enabled? :white :gray)}]]]))
(defn return-to-transaction [dapp-transaction?] ;; MAIN SEND TRANSACTION VIEW
(if dapp-transaction? (defn- send-transaction-view [{:keys [modal? transaction scroll advanced? network]}]
(re-frame/dispatch [:navigate-to-modal :wallet-send-transaction-modal]) (let [{:keys [amount amount-text amount-error asset-error show-password-input? to to-name sufficient-funds?
(act/default-handler))) sufficient-gas? in-progress? from-chat? symbol]} transaction
{:keys [decimals] :as token} (tokens/asset-for (ethereum/network->chain-keyword network) symbol)]
(defn handler [discard? dapp-transaction?]
(if discard?
#(re-frame/dispatch [:wallet/discard-transaction-navigate-back])
#(return-to-transaction dapp-transaction?)))
(defn- toolbar [discard? dapp-transaction? action title]
[toolbar/toolbar {:style wallet.styles/toolbar}
[toolbar/nav-button (action (handler discard? dapp-transaction?))]
[toolbar/content-title {:color :white} title]])
(defview transaction-fee []
(letsubs [send-transaction [:wallet.send/transaction]
unsigned-transaction [:wallet.send/unsigned-transaction]
network [:get-current-account-network]
{gas-edit :gas
max-fee :max-fee
gas-price-edit :gas-price} [:wallet/edit]]
(let [modal? (:id send-transaction)
;;TODO(goranjovic) - unify unsigned and regular transaction subs
{:keys [amount symbol] :as transaction} (if modal? unsigned-transaction send-transaction)
gas (:value gas-edit)
gas-price (:value gas-price-edit)
{:keys [decimals]} (tokens/asset-for (ethereum/network->chain-keyword network) symbol)]
[wallet.components/simple-screen {:status-bar-type :modal-wallet}
[toolbar false modal? act/close-white
(i18n/label :t/wallet-transaction-fee)]
[react/view components.styles/flex
[react/view {:flex-direction :row}
[react/view styles/gas-container-wrapper
[wallet.components/cartouche {}
(i18n/label :t/gas-limit)
[react/view styles/gas-input-wrapper
[react/text-input (merge styles/transaction-fee-input
{:on-change-text #(re-frame/dispatch [:wallet.send/edit-value :gas %])
:default-value gas
:accessibility-label :gas-limit-input})]]]
(when (:invalid? gas-edit)
[tooltip/tooltip (i18n/label :t/invalid-number)])]
[react/view styles/gas-container-wrapper
[wallet.components/cartouche {}
(i18n/label :t/gas-price)
[react/view styles/gas-input-wrapper
[react/text-input (merge styles/transaction-fee-input
{:on-change-text #(re-frame/dispatch [:wallet.send/edit-value :gas-price %])
:default-value gas-price
:accessibility-label :gas-price-input})]
[wallet.components/cartouche-secondary-text
(i18n/label :t/gwei)]]]
(when (:invalid? gas-price-edit)
[tooltip/tooltip (i18n/label (if (= :invalid-number (:invalid? gas-price-edit))
:t/invalid-number
:t/wallet-send-min-wei))])]]
[react/view styles/transaction-fee-info
[react/view styles/transaction-fee-info-icon
[react/text {:style styles/transaction-fee-info-icon-text} "?"]]
[react/view styles/transaction-fee-info-text-wrapper
[react/i18n-text {:style styles/advanced-fees-text
:key :wallet-transaction-fee-details}]]]
[components/separator]
[react/view styles/transaction-fee-block-wrapper
[wallet.components/cartouche {:disabled? true}
(i18n/label :t/amount)
[react/view {:accessibility-label :amount-input}
[wallet.components/cartouche-text-content
(str (money/to-fixed (money/internal->formatted amount symbol decimals)))
(name symbol)]]]
[wallet.components/cartouche {:disabled? true}
(i18n/label :t/wallet-transaction-total-fee)
[react/view {:accessibility-label :total-fee-input}
[wallet.components/cartouche-text-content
(str max-fee " " (i18n/label :t/eth))]]]]
[bottom-buttons/bottom-buttons styles/fee-buttons
[button/button {:on-press #(re-frame/dispatch [:wallet.send/reset-gas-default])
:accessibility-label :reset-to-default-button}
(i18n/label :t/reset-default)]
[button/button {:on-press #(do (re-frame/dispatch [:wallet.send/set-gas-details
(:value-number gas-edit)
(:value-number gas-price-edit)])
(return-to-transaction modal?))
:accessibility-label :done-button
:disabled? (or (:invalid? gas-edit)
(:invalid? gas-price-edit))}
(i18n/label :t/done)]]]])))
(defn- advanced-cartouche [{:keys [max-fee gas gas-price]}]
[react/view
[wallet.components/cartouche {:on-press #(do (re-frame/dispatch [:wallet.send/clear-gas])
(re-frame/dispatch [:navigate-to-modal :wallet-transaction-fee]))}
(i18n/label :t/wallet-transaction-fee)
[react/view {:style styles/advanced-options-text-wrapper
:accessibility-label :transaction-fee-button}
[react/text {:style styles/advanced-fees-text}
(str max-fee " " (i18n/label :t/eth))]
[react/text {:style styles/advanced-fees-details-text}
(str (money/to-fixed gas) " * " (money/to-fixed (money/wei-> :gwei gas-price)) (i18n/label :t/gwei))]]]])
(defn- advanced-options [advanced? transaction modal? scroll]
[react/view {:style styles/advanced-wrapper}
[react/touchable-highlight {:on-press (fn []
(re-frame/dispatch [:wallet.send/toggle-advanced (not advanced?)])
(when (and scroll @scroll) (utils/set-timeout #(.scrollToEnd @scroll) 350)))}
[react/view {:style styles/advanced-button-wrapper}
[react/view {:style styles/advanced-button
:accessibility-label :advanced-button}
[react/i18n-text {:style (merge wallet.components.styles/label
styles/advanced-label)
:key :wallet-advanced}]
[vector-icons/icon (if advanced? :icons/up :icons/down) {:color :white}]]]]
(when advanced?
[advanced-cartouche transaction])])
(defn- send-transaction-panel [{:keys [modal? transaction scroll advanced? network]}]
(let [{:keys [amount amount-text amount-error asset-error signing? to to-name sufficient-funds? sufficient-gas?
in-progress? from-chat? symbol]} transaction
{:keys [decimals] :as token} (tokens/asset-for (ethereum/network->chain-keyword network) symbol)
timeout (atom nil)]
[wallet.components/simple-screen {:avoid-keyboard? (not modal?) [wallet.components/simple-screen {:avoid-keyboard? (not modal?)
:status-bar-type (if modal? :modal-wallet :wallet)} :status-bar-type (if modal? :modal-wallet :wallet)}
[toolbar from-chat? false (if modal? act/close-white act/back-white) [toolbar modal? (i18n/label :t/send-transaction)]
(i18n/label :t/send-transaction)]
[react/view components.styles/flex [react/view components.styles/flex
[common/network-info {:text-color :white}] [common/network-info {:text-color :white}]
[react/scroll-view {:keyboard-should-persist-taps :always [react/scroll-view {:keyboard-should-persist-taps :always
@ -247,46 +159,47 @@
:amount-text amount-text :amount-text amount-text
:input-options {:on-focus (fn [] (when (and scroll @scroll) (utils/set-timeout #(.scrollToEnd @scroll) 100))) :input-options {:on-focus (fn [] (when (and scroll @scroll) (utils/set-timeout #(.scrollToEnd @scroll) 100)))
:on-change-text #(re-frame/dispatch [:wallet.send/set-and-validate-amount % symbol decimals])}} token] :on-change-text #(re-frame/dispatch [:wallet.send/set-and-validate-amount % symbol decimals])}} token]
[advanced-options advanced? transaction modal? scroll]]] [advanced-options advanced? transaction scroll]]]
(if signing? (if show-password-input?
[signing-buttons in-progress? [enter-password-buttons in-progress?
#(re-frame/dispatch (if modal? [:wallet/cancel-signing-modal] [:wallet/discard-transaction])) #(re-frame/dispatch [:wallet/cancel-entering-password])
#(re-frame/dispatch (if modal? [:wallet/sign-transaction-modal] [:wallet/sign-transaction])) #(re-frame/dispatch [:wallet/send-transaction])
:t/transactions-sign-transaction] :t/transactions-sign-transaction]
[sign-button amount-error to amount sufficient-funds? sufficient-gas? modal?]) [sign-transaction-button amount-error to amount sufficient-funds? sufficient-gas? modal?])
(when signing? (when show-password-input?
[sign-panel :t/signing-phrase-description in-progress?]) [password-input-panel :t/signing-phrase-description in-progress?])
(when in-progress? [react/view styles/processing-view])]])) (when in-progress? [react/view styles/processing-view])]]))
;; SEND TRANSACTION FROM WALLET (CHAT)
(defview send-transaction [] (defview send-transaction []
(letsubs [transaction [:wallet.send/transaction] (letsubs [transaction [:wallet.send/transaction]
advanced? [:wallet.send/advanced?] advanced? [:wallet.send/advanced?]
network [:get-current-account-network] network [:get-current-account-network]
scroll (atom nil)] scroll (atom nil)]
[send-transaction-panel {:modal? false :transaction transaction :scroll scroll :advanced? advanced? [send-transaction-view {:modal? false :transaction transaction :scroll scroll :advanced? advanced?
:network network}])) :network network}]))
;; SEND TRANSACTION FROM DAPP
(defview send-transaction-modal [] (defview send-transaction-modal []
(letsubs [transaction [:wallet.send/unsigned-transaction] (letsubs [transaction [:wallet.send/transaction]
advanced? [:wallet.send/advanced?] advanced? [:wallet.send/advanced?]
network [:get-current-account-network] network [:get-current-account-network]
scroll (atom nil)] scroll (atom nil)]
(if transaction (if transaction
[send-transaction-panel {:modal? true :transaction transaction :scroll scroll :advanced? advanced? [send-transaction-view {:modal? true :transaction transaction :scroll scroll :advanced? advanced?
:network network}] :network network}]
[react/view wallet.styles/wallet-modal-container [react/view wallet.styles/wallet-modal-container
[react/view components.styles/flex [react/view components.styles/flex
[status-bar/status-bar {:type :modal-wallet}] [status-bar/status-bar {:type :modal-wallet}]
[toolbar false false act/close-white [toolbar true (i18n/label :t/send-transaction)]
(i18n/label :t/send-transaction)]
[react/i18n-text {:style styles/empty-text [react/i18n-text {:style styles/empty-text
:key :unsigned-transaction-expired}]]]))) :key :unsigned-transaction-expired}]]])))
;; SIGN MESSAGE FROM DAPP
(defview sign-message-modal [] (defview sign-message-modal []
(letsubs [{:keys [data in-progress?]} [:wallet.send/unsigned-transaction]] (letsubs [{:keys [data in-progress?]} [:wallet.send/transaction]]
[wallet.components/simple-screen {:status-bar-type :modal-wallet} [wallet.components/simple-screen {:status-bar-type :modal-wallet}
[toolbar true false act/close-white [toolbar true (i18n/label :t/sign-message)]
(i18n/label :t/sign-message)]
[react/view components.styles/flex [react/view components.styles/flex
[react/scroll-view [react/scroll-view
[react/view styles/send-transaction-form [react/view styles/send-transaction-form
@ -297,10 +210,10 @@
:input-options {:multiline true} :input-options {:multiline true}
:amount-text data} :amount-text data}
nil]]]] nil]]]]
[signing-buttons false [enter-password-buttons false
#(re-frame/dispatch [:wallet/discard-transaction-navigate-back]) #(re-frame/dispatch [:wallet/discard-transaction-navigate-back])
#(re-frame/dispatch [:wallet/sign-message-modal]) #(re-frame/dispatch [:wallet/sign-message])
:t/transactions-sign] :t/transactions-sign]
[sign-panel :t/signing-message-phrase-description false] [password-input-panel :t/signing-message-phrase-description false]
(when in-progress? (when in-progress?
[react/view styles/processing-view])]])) [react/view styles/processing-view])]]))

View File

@ -0,0 +1,103 @@
(ns status-im.ui.screens.wallet.transaction-fee.views
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [re-frame.core :as re-frame]
[status-im.i18n :as i18n]
[status-im.ui.components.bottom-buttons.view :as bottom-buttons]
[status-im.ui.components.button.view :as button]
[status-im.ui.components.react :as react]
[status-im.ui.components.styles :as components.styles]
[status-im.ui.components.toolbar.actions :as act]
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.ui.components.tooltip.views :as tooltip]
[status-im.ui.screens.wallet.components.views :as components]
[status-im.ui.screens.wallet.send.styles :as styles]
[status-im.ui.screens.wallet.styles :as wallet.styles]
[status-im.utils.money :as money]
[status-im.utils.ethereum.tokens :as tokens]
[status-im.utils.ethereum.core :as ethereum]))
(defn return-to-transaction [modal?]
(if modal?
;;TODO(andrey) artificial navigation stack for modals (should be reworked)
(re-frame/dispatch [:navigate-to-modal :wallet-send-transaction-modal])
(act/default-handler)))
(defn- toolbar [modal? title]
[toolbar/toolbar {:style wallet.styles/toolbar}
[toolbar/nav-button (act/close-white #(return-to-transaction modal?))]
[toolbar/content-title {:color :white} title]])
(defview transaction-fee []
(letsubs [send-transaction [:wallet.send/transaction]
network [:get-current-account-network]
{gas-edit :gas
max-fee :max-fee
gas-price-edit :gas-price} [:wallet/edit]]
(let [modal? (:id send-transaction)
{:keys [amount symbol]} send-transaction
gas (:value gas-edit)
gas-price (:value gas-price-edit)
{:keys [decimals]} (tokens/asset-for (ethereum/network->chain-keyword network) symbol)]
[components/simple-screen {:status-bar-type :modal-wallet}
[toolbar modal? (i18n/label :t/wallet-transaction-fee)]
[react/view components.styles/flex
[react/view {:flex-direction :row}
[react/view styles/gas-container-wrapper
[components/cartouche {}
(i18n/label :t/gas-limit)
[react/view styles/gas-input-wrapper
[react/text-input (merge styles/transaction-fee-input
{:on-change-text #(re-frame/dispatch [:wallet.send/edit-value :gas %])
:default-value gas
:accessibility-label :gas-limit-input})]]]
(when (:invalid? gas-edit)
[tooltip/tooltip (i18n/label :t/invalid-number)])]
[react/view styles/gas-container-wrapper
[components/cartouche {}
(i18n/label :t/gas-price)
[react/view styles/gas-input-wrapper
[react/text-input (merge styles/transaction-fee-input
{:on-change-text #(re-frame/dispatch [:wallet.send/edit-value :gas-price %])
:default-value gas-price
:accessibility-label :gas-price-input})]
[components/cartouche-secondary-text
(i18n/label :t/gwei)]]]
(when (:invalid? gas-price-edit)
[tooltip/tooltip (i18n/label (if (= :invalid-number (:invalid? gas-price-edit))
:t/invalid-number
:t/wallet-send-min-wei))])]]
[react/view styles/transaction-fee-info
[react/view styles/transaction-fee-info-icon
[react/text {:style styles/transaction-fee-info-icon-text} "?"]]
[react/view styles/transaction-fee-info-text-wrapper
[react/i18n-text {:style styles/advanced-fees-text
:key :wallet-transaction-fee-details}]]]
[components/separator]
[react/view styles/transaction-fee-block-wrapper
[components/cartouche {:disabled? true}
(i18n/label :t/amount)
[react/view {:accessibility-label :amount-input}
[components/cartouche-text-content
(str (money/to-fixed (money/internal->formatted amount symbol decimals)))
(name symbol)]]]
[components/cartouche {:disabled? true}
(i18n/label :t/wallet-transaction-total-fee)
[react/view {:accessibility-label :total-fee-input}
[components/cartouche-text-content
(str max-fee " " (i18n/label :t/eth))]]]]
[bottom-buttons/bottom-buttons styles/fee-buttons
[button/button {:on-press #(re-frame/dispatch [:wallet.send/reset-gas-default])
:accessibility-label :reset-to-default-button}
(i18n/label :t/reset-default)]
[button/button {:on-press #(do (re-frame/dispatch [:wallet.send/set-gas-details
(:value-number gas-edit)
(:value-number gas-price-edit)])
(return-to-transaction modal?))
:accessibility-label :done-button
:disabled? (or (:invalid? gas-edit)
(:invalid? gas-price-edit))}
(i18n/label :t/done)]]]])))

View File

@ -1,4 +1,4 @@
(ns status-im.ui.screens.wallet.send.transaction-sent.styles (ns status-im.ui.screens.wallet.transaction-sent.styles
(:require-macros [status-im.utils.styles :refer [defnstyle defstyle]]) (:require-macros [status-im.utils.styles :refer [defnstyle defstyle]])
(:require [status-im.ui.components.colors :as colors])) (:require [status-im.ui.components.colors :as colors]))

View File

@ -1,10 +1,10 @@
(ns status-im.ui.screens.wallet.send.transaction-sent.views (ns status-im.ui.screens.wallet.transaction-sent.views
(:require-macros [status-im.utils.views :refer [defview letsubs]]) (:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [status-im.ui.components.react :as react] (:require [status-im.ui.components.react :as react]
[status-im.ui.components.status-bar.view :as status-bar] [status-im.ui.components.status-bar.view :as status-bar]
[status-im.ui.components.icons.vector-icons :as vi] [status-im.ui.components.icons.vector-icons :as vi]
[status-im.ui.screens.wallet.styles :as wallet.styles] [status-im.ui.screens.wallet.styles :as wallet.styles]
[status-im.ui.screens.wallet.send.transaction-sent.styles :as styles] [status-im.ui.screens.wallet.transaction-sent.styles :as styles]
[status-im.ui.components.styles :as components.styles] [status-im.ui.components.styles :as components.styles]
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.ui.screens.wallet.components.views :as components] [status-im.ui.screens.wallet.components.views :as components]

View File

@ -42,45 +42,6 @@
(fn [transactions] (fn [transactions]
(group-by :type (vals transactions)))) (group-by :type (vals transactions))))
(defn- format-unsigned-transaction [{:keys [id] :as transaction}]
(assoc transaction
:type :unsigned
:confirmations 0
;; TODO (andrey) revisit this, we shouldn't set not hash value to the hash field
:hash id))
(reg-sub :wallet/unsigned-transactions
:<- [:wallet]
:<- [:get-contacts-by-address]
(fn [[wallet contacts]]
(map #(enrich-transaction % contacts) (vals (:transactions-unsigned wallet)))))
(reg-sub :wallet.transactions/unsigned-transactions
:<- [:wallet/unsigned-transactions]
(fn [transactions]
(reduce (fn [acc {:keys [id] :as transaction}]
(assoc acc id (format-unsigned-transaction transaction)))
{}
transactions)))
(reg-sub :wallet.transactions/unsigned-transactions-count
:<- [:wallet.transactions/unsigned-transactions]
(fn [unsigned-transactions]
(count unsigned-transactions)))
(reg-sub :wallet.transactions/unsigned-transactions-list
:<- [:wallet.transactions/unsigned-transactions]
(fn [unsigned-transactions]
(vals unsigned-transactions)))
(reg-sub :wallet.transactions/postponed-transactions-list
:<- [:wallet.transactions/grouped-transactions]
(fn [{:keys [postponed]}]
(when postponed
{:title "Postponed"
:key :postponed
:data postponed})))
(reg-sub :wallet.transactions/pending-transactions-list (reg-sub :wallet.transactions/pending-transactions-list
:<- [:wallet.transactions/grouped-transactions] :<- [:wallet.transactions/grouped-transactions]
(fn [{:keys [pending]}] (fn [{:keys [pending]}]
@ -113,12 +74,10 @@
(group-transactions-by-date (concat inbound outbound failed)))) (group-transactions-by-date (concat inbound outbound failed))))
(reg-sub :wallet.transactions/transactions-history-list (reg-sub :wallet.transactions/transactions-history-list
:<- [:wallet.transactions/postponed-transactions-list]
:<- [:wallet.transactions/pending-transactions-list] :<- [:wallet.transactions/pending-transactions-list]
:<- [:wallet.transactions/completed-transactions-list] :<- [:wallet.transactions/completed-transactions-list]
(fn [[postponed pending completed]] (fn [[pending completed]]
(cond-> [] (cond-> []
postponed (into postponed)
pending (into pending) pending (into pending)
completed (into completed)))) completed (into completed))))
@ -128,13 +87,11 @@
(:current-transaction wallet))) (:current-transaction wallet)))
(reg-sub :wallet.transactions/transaction-details (reg-sub :wallet.transactions/transaction-details
:<- [:wallet.transactions/unsigned-transactions]
:<- [:wallet.transactions/transactions] :<- [:wallet.transactions/transactions]
:<- [:wallet.transactions/current-transaction] :<- [:wallet.transactions/current-transaction]
:<- [:network] :<- [:network]
(fn [[unsigned-transactions transactions current-transaction network]] (fn [[transactions current-transaction network]]
(let [transactions (merge transactions unsigned-transactions) (let [{:keys [gas-used gas-price hash timestamp type] :as transaction} (get transactions current-transaction)
{:keys [gas-used gas-price hash timestamp type] :as transaction} (get transactions current-transaction)
chain (ethereum/network->chain-keyword network)] chain (ethereum/network->chain-keyword network)]
(when transaction (when transaction
(merge transaction (merge transaction

View File

@ -20,7 +20,9 @@
[status-im.utils.ethereum.tokens :as tokens] [status-im.utils.ethereum.tokens :as tokens]
[status-im.constants :as constants] [status-im.constants :as constants]
[status-im.utils.datetime :as datetime] [status-im.utils.datetime :as datetime]
[clojure.string :as string]) [clojure.string :as string]
[status-im.utils.security :as security]
[status-im.utils.types :as types])
(:refer-clojure :exclude [name symbol])) (:refer-clojure :exclude [name symbol]))
(defn name [web3 contract cb] (defn name [web3 contract cb]
@ -42,12 +44,14 @@
(ethereum/call-params contract "balanceOf(address)" (ethereum/normalized-address address)) (ethereum/call-params contract "balanceOf(address)" (ethereum/normalized-address address))
#(cb %1 (ethereum/hex->bignumber %2)))) #(cb %1 (ethereum/hex->bignumber %2))))
(defn transfer [web3 contract from address value params cb] (defn transfer [contract from to value gas gas-price masked-password on-completed]
(ethereum/send-transaction web3 (status/send-transaction (types/clj->json
(merge (ethereum/call-params contract "transfer(address,uint256)" (ethereum/normalized-address address) (ethereum/int->hex value)) (merge (ethereum/call-params contract "transfer(address,uint256)" to value)
{:from from} {:from from
params) :gas gas
#(cb %1 (ethereum/hex->boolean %2)))) :gasPrice gas-price}))
(security/unmask masked-password)
on-completed))
(defn transfer-from [web3 contract from-address to-address value cb] (defn transfer-from [web3 contract from-address to-address value cb]
(ethereum/call web3 (ethereum/call web3
@ -168,8 +172,8 @@
(add-padding from) (add-padding from)
(add-padding to)]}]} (add-padding to)]}]}
payload (.stringify js/JSON (clj->js args))] payload (.stringify js/JSON (clj->js args))]
(status/call-web3-private payload (status/call-private-rpc payload
(response-handler web3 current-block-number chain direction ethereum/handle-error cb)))) (response-handler web3 current-block-number chain direction ethereum/handle-error cb))))
(defn get-token-transactions (defn get-token-transactions
[web3 chain contracts direction address cb] [web3 chain contracts direction address cb]

View File

@ -9,7 +9,7 @@
(defn make-internal-web3 [] (defn make-internal-web3 []
(dependencies/Web3. (dependencies/Web3.
#js {:sendAsync (fn [payload callback] #js {:sendAsync (fn [payload callback]
(status/call-web3-private (status/call-private-rpc
(.stringify js/JSON payload) (.stringify js/JSON payload)
(fn [response] (fn [response]
(if (= "" response) (if (= "" response)

View File

@ -5,7 +5,8 @@
status-im.ui.screens.db status-im.ui.screens.db
status-im.ui.screens.subs status-im.ui.screens.subs
[re-frame.core :as re-frame] [re-frame.core :as re-frame]
[status-im.models.browser :as model])) [status-im.models.browser :as model]
[status-im.utils.types :as types]))
(defn test-fixtures [] (defn test-fixtures []
@ -129,18 +130,18 @@
(is (zero? (count @dapps-permissions))) (is (zero? (count @dapps-permissions)))
(re-frame/dispatch [:on-bridge-message {:type "status-api-request" (re-frame/dispatch [:on-bridge-message (types/clj->json {:type "status-api-request"
:host dapp-name :host dapp-name
:permissions ["FAKE_PERMISSION"]} :permissions ["FAKE_PERMISSION"]})
nil nil]) nil nil])
(is (= {:dapp dapp-name (is (= {:dapp dapp-name
:permissions []} :permissions []}
(get @dapps-permissions dapp-name))) (get @dapps-permissions dapp-name)))
(re-frame/dispatch [:on-bridge-message {:type "status-api-request" (re-frame/dispatch [:on-bridge-message (types/clj->json {:type "status-api-request"
:host dapp-name :host dapp-name
:permissions ["CONTACT_CODE"]} :permissions ["CONTACT_CODE"]})
nil nil]) nil nil])
(is (= 1 (count @dapps-permissions))) (is (= 1 (count @dapps-permissions)))
@ -149,9 +150,9 @@
:permissions ["CONTACT_CODE"]} :permissions ["CONTACT_CODE"]}
(get @dapps-permissions dapp-name))) (get @dapps-permissions dapp-name)))
(re-frame/dispatch [:on-bridge-message {:type "status-api-request" (re-frame/dispatch [:on-bridge-message (types/clj->json {:type "status-api-request"
:host dapp-name :host dapp-name
:permissions ["CONTACT_CODE" "FAKE_PERMISSION"]} :permissions ["CONTACT_CODE" "FAKE_PERMISSION"]})
nil nil]) nil nil])
(is (= 1 (count @dapps-permissions))) (is (= 1 (count @dapps-permissions)))
@ -160,9 +161,9 @@
:permissions ["CONTACT_CODE"]} :permissions ["CONTACT_CODE"]}
(get @dapps-permissions dapp-name))) (get @dapps-permissions dapp-name)))
(re-frame/dispatch [:on-bridge-message {:type "status-api-request" (re-frame/dispatch [:on-bridge-message (types/clj->json {:type "status-api-request"
:host dapp-name :host dapp-name
:permissions ["FAKE_PERMISSION"]} :permissions ["FAKE_PERMISSION"]})
nil nil]) nil nil])
(is (= 1 (count @dapps-permissions))) (is (= 1 (count @dapps-permissions)))
@ -171,9 +172,9 @@
:permissions ["CONTACT_CODE"]} :permissions ["CONTACT_CODE"]}
(get @dapps-permissions dapp-name))) (get @dapps-permissions dapp-name)))
(re-frame/dispatch [:on-bridge-message {:type "status-api-request" (re-frame/dispatch [:on-bridge-message (types/clj->json {:type "status-api-request"
:host dapp-name2 :host dapp-name2
:permissions ["CONTACT_CODE"]} :permissions ["CONTACT_CODE"]})
nil nil]) nil nil])
(is (= 2 (count @dapps-permissions))) (is (= 2 (count @dapps-permissions)))