wallet refactoring
Signed-off-by: Goran Jovic <goranjovic@gmail.com>
This commit is contained in:
parent
2b6d6722b7
commit
3e7c059b59
|
@ -265,7 +265,7 @@
|
|||
view)
|
||||
|
||||
(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)]
|
||||
[main-screen-view styles/flex
|
||||
[keyboard-avoiding-view {:flex 1 :flex-direction :column}
|
||||
|
|
|
@ -16,7 +16,7 @@ dependencies {
|
|||
implementation 'com.instabug.library:instabug:3+'
|
||||
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'
|
||||
|
||||
// Check if the local status-go jar exists, and compile against that if it does
|
||||
|
|
|
@ -504,8 +504,8 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
|
|||
}
|
||||
|
||||
@ReactMethod
|
||||
public void approveSignRequest(final String id, final String password, final Callback callback) {
|
||||
Log.d(TAG, "approveSignRequest");
|
||||
public void sendTransaction(final String txArgsJSON, final String password, final Callback callback) {
|
||||
Log.d(TAG, "sendTransaction");
|
||||
if (!checkAvailability()) {
|
||||
callback.invoke(false);
|
||||
return;
|
||||
|
@ -514,7 +514,7 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
|
|||
Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
String res = Statusgo.ApproveSignRequest(id, password);
|
||||
String res = Statusgo.SendTransaction(txArgsJSON, password);
|
||||
callback.invoke(res);
|
||||
}
|
||||
};
|
||||
|
@ -523,8 +523,8 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
|
|||
}
|
||||
|
||||
@ReactMethod
|
||||
public void approveSignRequestWithArgs(final String id, final String password, final String gas, final String gasPrice, final Callback callback) {
|
||||
Log.d(TAG, "approveSignRequestWithArgs");
|
||||
public void signMessage(final String rpcParams, final Callback callback) {
|
||||
Log.d(TAG, "signMessage");
|
||||
if (!checkAvailability()) {
|
||||
callback.invoke(false);
|
||||
return;
|
||||
|
@ -533,7 +533,7 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
|
|||
Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
String res = Statusgo.ApproveSignRequestWithArgs(id, password, Long.parseLong(gas), Long.parseLong(gasPrice));
|
||||
String res = Statusgo.SignMessage(rpcParams);
|
||||
callback.invoke(res);
|
||||
}
|
||||
};
|
||||
|
@ -541,24 +541,6 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
|
|||
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
|
||||
public void setAdjustResize() {
|
||||
Log.d(TAG, "setAdjustResize");
|
||||
|
@ -667,7 +649,7 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
|
|||
}
|
||||
|
||||
@ReactMethod
|
||||
public void sendWeb3Request(final String payload, final Callback callback) {
|
||||
public void callRPC(final String payload, final Callback callback) {
|
||||
Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
@ -680,7 +662,7 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
|
|||
}
|
||||
|
||||
@ReactMethod
|
||||
public void sendWeb3PrivateRequest(final String payload, final Callback callback) {
|
||||
public void callPrivateRPC(final String payload, final Callback callback) {
|
||||
Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
|
|
@ -251,43 +251,30 @@ RCT_EXPORT_METHOD(login:(NSString *)address
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
#pragma mark - Approve Sign Request
|
||||
//////////////////////////////////////////////////////////////////// approveSignRequests
|
||||
RCT_EXPORT_METHOD(approveSignRequest:(NSString *)id
|
||||
#pragma mark - SendTransaction
|
||||
//////////////////////////////////////////////////////////////////// sendTransaction
|
||||
RCT_EXPORT_METHOD(sendTransaction:(NSString *)txArgsJSON
|
||||
password:(NSString *)password
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"ApproveSignRequest() method called");
|
||||
NSLog(@"SendTransaction() method called");
|
||||
#endif
|
||||
char * result = ApproveSignRequest((char *) [id UTF8String], (char *) [password UTF8String]);
|
||||
char * result = SendTransaction((char *) [txArgsJSON UTF8String], (char *) [password UTF8String]);
|
||||
callback(@[[NSString stringWithUTF8String: result]]);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
#pragma mark - Approve Sign Request With Args
|
||||
//////////////////////////////////////////////////////////////////// approveSignRequestWithArgs
|
||||
RCT_EXPORT_METHOD(approveSignRequestWithArgs:(NSString *)id
|
||||
password:(NSString *)password
|
||||
gas:(NSString *)gas
|
||||
gasPrice:(NSString *)gasPrice
|
||||
#pragma mark - SignMessage
|
||||
//////////////////////////////////////////////////////////////////// signMessage
|
||||
RCT_EXPORT_METHOD(signMessage:(NSString *)message
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
#if DEBUG
|
||||
NSLog(@"ApproveSignRequestWithArgs() method called");
|
||||
NSLog(@"SignMessage() method called");
|
||||
#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]]);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
#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
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
@ -329,7 +316,7 @@ RCT_EXPORT_METHOD(clearStorageAPIs) {
|
|||
}
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(sendWeb3Request:(NSString *)payload
|
||||
RCT_EXPORT_METHOD(callRPC:(NSString *)payload
|
||||
callback:(RCTResponseSenderBlock)callback) {
|
||||
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
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) {
|
||||
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
char * result = CallPrivateRPC((char *) [payload UTF8String]);
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
<artifactItem>
|
||||
<groupId>status-im</groupId>
|
||||
<artifactId>status-go-ios-simulator</artifactId>
|
||||
<version>develop-ga6d69eba</version>
|
||||
<version>tags-v0.11.0^0-g4afd9e6c-302</version>
|
||||
<type>zip</type>
|
||||
<overWrite>true</overWrite>
|
||||
<outputDirectory>./</outputDirectory>
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -2,27 +2,31 @@ if(typeof StatusHttpProvider === "undefined"){
|
|||
var callbackId = 0;
|
||||
var callbacks = {};
|
||||
|
||||
function httpCallback(id, data) {
|
||||
var result = data;
|
||||
var error = null;
|
||||
WebViewBridge.onMessage = function (message) {
|
||||
data = JSON.parse(message);
|
||||
|
||||
try {
|
||||
result = JSON.parse(data);
|
||||
} catch (e) {
|
||||
error = {message: "InvalidResponse"};
|
||||
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 }, "*");
|
||||
}
|
||||
|
||||
if (callbacks[id]) {
|
||||
callbacks[id](error, result);
|
||||
else if (data.type === "web3-send-async-callback")
|
||||
{
|
||||
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.isConnected = function () { return true; };
|
||||
|
||||
function web3Response (payload, result){
|
||||
return {id: payload.id,
|
||||
|
@ -66,57 +70,19 @@ StatusHttpProvider.prototype.sendAsync = function (payload, callback) {
|
|||
else {
|
||||
var messageId = callbackId++;
|
||||
callbacks[messageId] = callback;
|
||||
if (typeof StatusBridge == "undefined") {
|
||||
var data = {
|
||||
payload: JSON.stringify(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;
|
||||
WebViewBridge.send(JSON.stringify({type: 'web3-send-async',
|
||||
messageId: messageId,
|
||||
payload: payload}));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var protocol = window.location.protocol
|
||||
var address = providerAddress || "http://localhost:8545";
|
||||
console.log(protocol);
|
||||
if (typeof web3 === "undefined") {
|
||||
//why do we need this condition?
|
||||
if (protocol == "https:" || protocol == "http:") {
|
||||
console.log("StatusHttpProvider");
|
||||
web3 = new Web3(new StatusHttpProvider(address));
|
||||
web3.eth.defaultAccount = currentAccountAddress;
|
||||
web3 = new Web3(new StatusHttpProvider());
|
||||
web3.eth.defaultAccount = currentAccountAddress; // currentAccountAddress - injected from status-react
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 }, "*");
|
||||
};
|
||||
}());
|
||||
|
|
|
@ -37,9 +37,7 @@
|
|||
{:type [{:id :inbound :label (i18n/label :t/incoming) :checked? true}
|
||||
{:id :outbound :label (i18n/label :t/outgoing) :checked? true}
|
||||
{:id :pending :label (i18n/label :t/pending) :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}]}})
|
||||
{:id :failed :label (i18n/label :t/failed) :checked? true}]}})
|
||||
|
||||
(def mainnet-networks
|
||||
{"mainnet" {:id "mainnet",
|
||||
|
@ -250,11 +248,11 @@
|
|||
;; Used to generate topic for contact discoveries
|
||||
(def contact-discovery "contact-discovery")
|
||||
|
||||
(def ^:const send-transaction-no-error-code "0")
|
||||
(def ^:const send-transaction-default-error-code "1")
|
||||
(def ^:const send-transaction-password-error-code "2")
|
||||
(def ^:const send-transaction-timeout-error-code "3")
|
||||
(def ^:const send-transaction-discarded-error-code "4")
|
||||
(def ^:const send-transaction-failed-parse-response 1)
|
||||
(def ^:const send-transaction-failed-parse-params 2)
|
||||
(def ^:const send-transaction-no-account-selected 3)
|
||||
(def ^:const send-transaction-invalid-tx-sender 4)
|
||||
(def ^:const send-transaction-err-decrypt 5)
|
||||
|
||||
(def ^:const web3-send-transaction "eth_sendTransaction")
|
||||
(def ^:const web3-personal-sign "personal_sign")
|
||||
|
@ -268,4 +266,6 @@
|
|||
(def ^:const dapp-permission-contact-code "CONTACT_CODE")
|
||||
(def ^:const status-api-success "status-api-success")
|
||||
(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")
|
|
@ -4,7 +4,8 @@
|
|||
[status-im.i18n :as i18n]
|
||||
[status-im.constants :as constants]
|
||||
[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]}]
|
||||
(when (and history-index history)
|
||||
|
@ -17,8 +18,8 @@
|
|||
(< history-index (dec (count history))))
|
||||
|
||||
(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 _)))
|
||||
dapp (first (filter #(= history-host (http/url-host (:dapp-url %))) (apply concat (mapv :data default-dapps/all))))]
|
||||
(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))))]
|
||||
(if dapp
|
||||
(assoc browser :dapp? true :name (:name dapp))
|
||||
(assoc browser :dapp? false :name (i18n/label :t/browser)))))
|
||||
|
@ -72,7 +73,10 @@
|
|||
(assoc (update-dapp-permissions-fx cofx {:dapp dapp-name
|
||||
:permissions (vec (set (concat (keys permissions-allowed)
|
||||
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]]
|
||||
(request-permission
|
||||
|
@ -82,4 +86,16 @@
|
|||
(update :index inc)
|
||||
|
||||
(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}])]}))
|
|
@ -1,5 +1,12 @@
|
|||
(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))
|
||||
|
||||
|
@ -22,7 +29,7 @@
|
|||
"0"))
|
||||
|
||||
(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])]
|
||||
(assoc edit :max-fee (calculate-max-fee gas gas-price))))
|
||||
|
||||
|
@ -51,3 +58,114 @@
|
|||
(defn edit-value
|
||||
[key value {:keys [db]}]
|
||||
{: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)))
|
||||
|
|
|
@ -3,9 +3,6 @@
|
|||
|
||||
(def adjust-resize 16)
|
||||
|
||||
(defn move-to-internal-storage [callback]
|
||||
(native-module/move-to-internal-storage callback))
|
||||
|
||||
(defn start-node [config]
|
||||
(native-module/start-node config))
|
||||
|
||||
|
@ -21,33 +18,27 @@
|
|||
(defn 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]
|
||||
(native-module/set-soft-input-mode mode))
|
||||
|
||||
(defn clear-web-data []
|
||||
(native-module/clear-web-data))
|
||||
|
||||
(defn call-web3 [payload callback]
|
||||
(native-module/call-web3 payload callback))
|
||||
(defn call-rpc [payload callback]
|
||||
(native-module/call-rpc payload callback))
|
||||
|
||||
(defn call-web3-private [payload callback]
|
||||
(native-module/call-web3-private payload callback))
|
||||
(defn call-private-rpc [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! []
|
||||
(native-module/module-initialized!))
|
||||
|
||||
(defn should-move-to-internal-storage? [callback]
|
||||
(native-module/should-move-to-internal-storage? callback))
|
||||
|
||||
(defn notify-users [m callback]
|
||||
(native-module/notify-users m callback))
|
||||
|
||||
|
|
|
@ -54,14 +54,6 @@
|
|||
(.addListener r/device-event-emitter "gethEvent"
|
||||
#(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 []
|
||||
(when status
|
||||
(call-module #(.stopNode status))))
|
||||
|
@ -101,27 +93,6 @@
|
|||
(when status
|
||||
(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]
|
||||
(when status
|
||||
(call-module #(.setSoftInputMode status mode))))
|
||||
|
@ -131,13 +102,21 @@
|
|||
(call-module #(.clearCookies status))
|
||||
(call-module #(.clearStorageAPIs status))))
|
||||
|
||||
(defn call-web3 [payload callback]
|
||||
(defn call-rpc [payload callback]
|
||||
(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
|
||||
(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 []
|
||||
(.closeApplication status))
|
||||
|
|
|
@ -11,7 +11,10 @@
|
|||
[status-im.models.browser :as model]
|
||||
[status-im.utils.platform :as platform]
|
||||
[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
|
||||
:browse
|
||||
|
@ -20,12 +23,22 @@
|
|||
(utils.universal-links/open! 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
|
||||
:send-to-bridge-fx
|
||||
(fn [[permissions-allowed webview]]
|
||||
(.sendToBridge @webview (.stringify js/JSON (clj->js {:type constants/status-api-success
|
||||
:data permissions-allowed
|
||||
:keys (keys permissions-allowed)})))))
|
||||
(fn [[message webview]]
|
||||
(.sendToBridge webview (types/clj->json message))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:show-dapp-permission-confirmation-fx
|
||||
|
@ -67,6 +80,12 @@
|
|||
:history-index 0
|
||||
: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
|
||||
:open-browser
|
||||
[re-frame/trim-v]
|
||||
|
@ -112,24 +131,32 @@
|
|||
(handlers/register-handler-fx
|
||||
:on-bridge-message
|
||||
[re-frame/trim-v]
|
||||
(fn [{:keys [db] :as cofx} [{{:keys [url]} :navState :keys [type host permissions]} browser webview]]
|
||||
(cond
|
||||
(fn [{:keys [db] :as cofx} [message]]
|
||||
(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))
|
||||
(model/update-browser-history-fx cofx browser url false)
|
||||
(and (= type constants/history-state-changed) platform/ios? (not= "about:blank" url))
|
||||
(model/update-browser-history-fx cofx browser url false)
|
||||
|
||||
(= type constants/status-api-request)
|
||||
(let [{:account/keys [account]} db
|
||||
{:keys [dapp? name]} browser
|
||||
dapp-name (if dapp? name host)]
|
||||
(model/request-permission
|
||||
cofx
|
||||
{:dapp-name dapp-name
|
||||
:webview webview
|
||||
: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)}})))))
|
||||
(= type constants/web3-send-async)
|
||||
(model/web3-send-async cofx payload messageId)
|
||||
|
||||
(= type constants/status-api-request)
|
||||
(let [{:account/keys [account]} db
|
||||
{:keys [dapp? name]} browser
|
||||
dapp-name (if dapp? name host)]
|
||||
(model/request-permission
|
||||
cofx
|
||||
{:dapp-name dapp-name
|
||||
:webview webview-bridge
|
||||
: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
|
||||
:next-dapp-permission
|
||||
|
|
|
@ -103,18 +103,16 @@
|
|||
[components.webview-bridge/webview-bridge
|
||||
{:dapp? dapp?
|
||||
:dapp-name name
|
||||
:ref #(reset! webview %)
|
||||
:ref #(do
|
||||
(reset! webview %)
|
||||
(re-frame/dispatch [:set :webview-bridge %]))
|
||||
:source {:uri url}
|
||||
:java-script-enabled true
|
||||
:bounces false
|
||||
:local-storage-enabled true
|
||||
:render-error web-view-error
|
||||
:on-navigation-state-change #(on-navigation-change % browser)
|
||||
:on-bridge-message #(re-frame/dispatch [:on-bridge-message
|
||||
(js->clj (.parse js/JSON %)
|
||||
:keywordize-keys true)
|
||||
browser
|
||||
webview])
|
||||
:on-bridge-message #(re-frame/dispatch [:on-bridge-message %])
|
||||
:on-load #(re-frame/dispatch [:update-browser-options {:error? false}])
|
||||
:on-error #(re-frame/dispatch [:update-browser-options {:error? true
|
||||
:loading? false}])
|
||||
|
|
|
@ -364,11 +364,6 @@
|
|||
(re-frame/dispatch [:fetch-web3-node-version-callback resp])))))
|
||||
nil))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
:webview-geo-permissions-granted
|
||||
(fn [{{:keys [webview-bridge]} :db} _]
|
||||
(.geoPermissionsGranted webview-bridge)))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
:get-fcm-token
|
||||
(fn [_ _]
|
||||
|
@ -393,8 +388,6 @@
|
|||
(instabug/log (str "Signal event: " event-str))
|
||||
(let [{:keys [type event]} (types/json->clj event-str)
|
||||
to-dispatch (case type
|
||||
"sign-request.queued" [:sign-request-queued event]
|
||||
"sign-request.failed" [:sign-request-failed event]
|
||||
"node.started" [:status-node-started]
|
||||
"node.stopped" [:status-node-stopped]
|
||||
"module.initialized" [:status-module-initialized]
|
||||
|
|
|
@ -28,11 +28,6 @@
|
|||
(fn [db [_ path]]
|
||||
(get-in db path)))
|
||||
|
||||
(reg-sub :signed-up?
|
||||
:<- [:get-current-account]
|
||||
(fn [current-account]
|
||||
(:signed-up? current-account)))
|
||||
|
||||
(reg-sub :network
|
||||
:<- [:get-current-account]
|
||||
(fn [current-account]
|
||||
|
|
|
@ -32,10 +32,10 @@
|
|||
[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.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.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.network-settings.views :refer [network-settings]]
|
||||
[status-im.ui.screens.network-settings.network-details.views :refer [network-details]]
|
||||
|
@ -110,7 +110,7 @@
|
|||
:wallet-send-transaction-modal send-transaction-modal
|
||||
:wallet-transaction-sent-modal transaction-sent-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
|
||||
[react/view [react/text (str "Unknown modal view: " modal-view)]]))
|
||||
|
||||
|
@ -135,8 +135,7 @@
|
|||
[component]])]])))
|
||||
|
||||
(defview main []
|
||||
(letsubs [signed-up? [:signed-up?]
|
||||
view-id [:get :view-id]]
|
||||
(letsubs [view-id [:get :view-id]]
|
||||
{:component-did-mount utils.universal-links/initialize
|
||||
:component-will-unmount utils.universal-links/finalize
|
||||
:component-will-update (fn [] (react/dismiss-keyboard!))}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
(ns status-im.ui.screens.wallet.db
|
||||
(:require-macros [status-im.utils.db :refer [allowed-keys]])
|
||||
(:require [cljs.spec.alpha :as spec]
|
||||
[status-im.i18n :as i18n]
|
||||
status-im.ui.screens.wallet.request.db
|
||||
|
@ -7,14 +8,36 @@
|
|||
|
||||
(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
|
||||
:wallet/transactions-queue]))
|
||||
|
||||
;; Placeholder namespace for wallet specs, which are a WIP depending on data
|
||||
;; model we decide on for balances, prices, etc.
|
||||
(spec/def :wallet/balance-loading? (spec/nilable boolean?))
|
||||
(spec/def :wallet/transactions-loading? (spec/nilable boolean?))
|
||||
(spec/def :wallet/transactions-sync-started? (spec/nilable boolean?))
|
||||
(spec/def :wallet/errors (spec/nilable any?))
|
||||
(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?
|
||||
"Checks if number has any extra digit beyond the allowed number of decimals.
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
(ns status-im.ui.screens.wallet.navigation
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[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
|
||||
[db _]
|
||||
|
@ -22,6 +23,7 @@
|
|||
(def transaction-send-default
|
||||
(let [symbol :ETH]
|
||||
{:gas (ethereum/estimate-gas symbol)
|
||||
:method constants/web3-send-transaction
|
||||
:symbol symbol}))
|
||||
|
||||
(def transaction-request-default
|
||||
|
|
|
@ -4,8 +4,16 @@
|
|||
[status-im.utils.money :as money]
|
||||
[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 ::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 ::amount-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 ::wrong-password? (spec/nilable boolean?))
|
||||
(spec/def ::id (spec/nilable string?))
|
||||
(spec/def ::waiting-signal? (spec/nilable boolean?))
|
||||
(spec/def ::signing? (spec/nilable boolean?))
|
||||
(spec/def ::later? (spec/nilable boolean?))
|
||||
(spec/def ::show-password-input? (spec/nilable boolean?))
|
||||
(spec/def ::height double?)
|
||||
(spec/def ::width double?)
|
||||
(spec/def ::camera-flashlight #{:on :off})
|
||||
(spec/def ::in-progress? boolean?)
|
||||
(spec/def ::from-chat? (spec/nilable boolean?))
|
||||
(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 ::whisper-identity (spec/nilable string?))
|
||||
(spec/def ::method (spec/nilable string?))
|
||||
(spec/def ::tx-hash (spec/nilable string?))
|
||||
(spec/def ::dapp-transaction (spec/nilable any?))
|
||||
|
||||
(spec/def :wallet/send-transaction (allowed-keys
|
||||
:opt-un [::amount ::to ::to-name ::amount-error ::asset-error ::amount-text ::password
|
||||
::waiting-signal? ::signing? ::id ::later?
|
||||
::camera-flashlight ::in-progress?
|
||||
:opt-un [::amount ::to ::to-name ::amount-error ::asset-error ::amount-text
|
||||
::password ::show-password-input? ::id ::from ::data
|
||||
::camera-flashlight ::in-progress? ::dapp-transaction
|
||||
::wrong-password? ::from-chat? ::symbol ::advanced?
|
||||
::gas ::gas-price ::whisper-identity ::method ::tx-hash]))
|
||||
|
|
|
@ -1,16 +1,13 @@
|
|||
(ns status-im.ui.screens.wallet.send.events
|
||||
(:require [clojure.string :as string]
|
||||
[re-frame.core :as re-frame]
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.i18n :as i18n]
|
||||
[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.utils.ethereum.core :as ethereum]
|
||||
[status-im.utils.ethereum.erc20 :as erc20]
|
||||
[status-im.utils.ethereum.tokens :as tokens]
|
||||
[status-im.utils.handlers :as handlers]
|
||||
[status-im.utils.handlers-macro :as handlers-macro]
|
||||
[status-im.utils.hex :as utils.hex]
|
||||
[status-im.utils.money :as money]
|
||||
[status-im.utils.security :as security]
|
||||
[status-im.utils.types :as types]
|
||||
|
@ -19,71 +16,154 @@
|
|||
[status-im.chat.models.message :as models.message]
|
||||
[status-im.chat.commands.sending :as commands-sending]
|
||||
[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.wallet.transactions :as wallet.transactions]))
|
||||
|
||||
;;;; FX
|
||||
|
||||
(re-frame/reg-fx
|
||||
::accept-transaction
|
||||
(fn [{:keys [masked-password id on-completed]}]
|
||||
;; unmasking the password as late as possible to avoid being exposed from app-db
|
||||
(status/approve-sign-request id
|
||||
(security/unmask masked-password)
|
||||
on-completed)))
|
||||
(defn- send-ethers [params on-completed masked-password]
|
||||
(status/send-transaction (types/clj->json params)
|
||||
(security/unmask masked-password)
|
||||
on-completed))
|
||||
|
||||
(re-frame/reg-fx
|
||||
::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]}]
|
||||
(defn- send-tokens [symbol chain {:keys [from to value gas gas-price]} on-completed masked-password]
|
||||
(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
|
||||
::send-transaction
|
||||
(fn [{:keys [symbol] :as params}]
|
||||
(fn [[params symbol chain on-completed masked-password]]
|
||||
(case symbol
|
||||
:ETH (send-ethers params)
|
||||
(send-tokens params))))
|
||||
:ETH (send-ethers params on-completed masked-password)
|
||||
(send-tokens symbol chain params on-completed masked-password))))
|
||||
|
||||
(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]
|
||||
;; (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)))
|
||||
|
||||
(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
|
||||
|
||||
;; 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]
|
||||
(let [{:keys [value error]} (wallet.db/parse-amount amount decimals)]
|
||||
(-> db
|
||||
|
@ -96,6 +176,13 @@
|
|||
(fn [{:keys [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
|
||||
([db edit? success-event]
|
||||
{:update-gas-price {:web3 (:web3 db)
|
||||
|
@ -130,305 +217,11 @@
|
|||
(fn [{:keys [db]} [_ 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
|
||||
:sign-request-queued
|
||||
(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
|
||||
:wallet/cancel-entering-password
|
||||
(fn [{:keys [db]} _]
|
||||
{:db (update-in db [:wallet :send-transaction] assoc
|
||||
:signing? false
|
||||
:show-password-input? false
|
||||
:wrong-password? false
|
||||
:password nil)}))
|
||||
|
||||
|
@ -437,11 +230,6 @@
|
|||
(fn [{:keys [db]} [_ 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
|
||||
:wallet.send/edit-value
|
||||
(fn [cofx [_ key value]]
|
||||
|
@ -475,7 +263,7 @@
|
|||
:close-transaction-sent-screen
|
||||
(fn [{:keys [db]} [_ chat-id]]
|
||||
{:dispatch [:navigate-back]
|
||||
:dispatch-later [{:ms 400 :dispatch [:check-transactions-queue]}]}))
|
||||
:dispatch-later [{:ms 400 :dispatch [:check-dapps-transactions-queue]}]}))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
:sync-wallet-transactions
|
||||
|
|
|
@ -1,54 +1,43 @@
|
|||
(ns status-im.ui.screens.wallet.send.subs
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.utils.money :as money]
|
||||
[status-im.models.wallet :as models.wallet]
|
||||
[status-im.utils.hex :as utils.hex]))
|
||||
[status-im.models.wallet :as models.wallet]))
|
||||
|
||||
(re-frame/reg-sub ::send-transaction
|
||||
:<- [:wallet]
|
||||
(fn [wallet]
|
||||
(:send-transaction wallet)))
|
||||
(re-frame/reg-sub
|
||||
::send-transaction
|
||||
:<- [:wallet]
|
||||
(fn [wallet]
|
||||
(:send-transaction wallet)))
|
||||
|
||||
(re-frame/reg-sub :wallet.send/symbol
|
||||
:<- [::send-transaction]
|
||||
(fn [send-transaction]
|
||||
(:symbol send-transaction)))
|
||||
(re-frame/reg-sub
|
||||
:wallet.send/symbol
|
||||
:<- [::send-transaction]
|
||||
(fn [send-transaction]
|
||||
(:symbol send-transaction)))
|
||||
|
||||
(re-frame/reg-sub :wallet.send/advanced?
|
||||
:<- [::send-transaction]
|
||||
(fn [send-transaction]
|
||||
(:advanced? send-transaction)))
|
||||
(re-frame/reg-sub
|
||||
:wallet.send/advanced?
|
||||
:<- [::send-transaction]
|
||||
(fn [send-transaction]
|
||||
(:advanced? send-transaction)))
|
||||
|
||||
(re-frame/reg-sub :wallet.send/camera-flashlight
|
||||
:<- [::send-transaction]
|
||||
(fn [send-transaction]
|
||||
(:camera-flashlight send-transaction)))
|
||||
(re-frame/reg-sub
|
||||
:wallet.send/camera-flashlight
|
||||
:<- [::send-transaction]
|
||||
(fn [send-transaction]
|
||||
(:camera-flashlight send-transaction)))
|
||||
|
||||
(re-frame/reg-sub :wallet.send/wrong-password?
|
||||
:<- [::send-transaction]
|
||||
(fn [send-transaction]
|
||||
(:wrong-password? send-transaction)))
|
||||
(re-frame/reg-sub
|
||||
:wallet.send/wrong-password?
|
||||
:<- [::send-transaction]
|
||||
(fn [send-transaction]
|
||||
(:wrong-password? send-transaction)))
|
||||
|
||||
(re-frame/reg-sub :wallet.send/sign-password-enabled?
|
||||
:<- [::send-transaction]
|
||||
(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))}))))
|
||||
(re-frame/reg-sub
|
||||
:wallet.send/sign-password-enabled?
|
||||
:<- [::send-transaction]
|
||||
(fn [{:keys [password]}]
|
||||
(and (not (nil? password)) (not= password ""))))
|
||||
|
||||
(defn edit-or-transaction-data
|
||||
"Set up edit data structure, defaulting to transaction when not available"
|
||||
|
@ -64,16 +53,14 @@
|
|||
:gas
|
||||
(money/to-fixed (:gas transaction)))))
|
||||
|
||||
(re-frame/reg-sub :wallet/edit
|
||||
:<- [::send-transaction]
|
||||
:<- [::unsigned-transaction]
|
||||
:<- [:wallet]
|
||||
(fn [[send-transaction unsigned-transaction {:keys [edit]}]]
|
||||
(edit-or-transaction-data
|
||||
(if (:id send-transaction)
|
||||
unsigned-transaction
|
||||
send-transaction)
|
||||
edit)))
|
||||
(re-frame/reg-sub
|
||||
:wallet/edit
|
||||
:<- [::send-transaction]
|
||||
:<- [:wallet]
|
||||
(fn [[send-transaction {:keys [edit]}]]
|
||||
(edit-or-transaction-data
|
||||
send-transaction
|
||||
edit)))
|
||||
|
||||
(defn check-sufficient-funds [transaction balance symbol amount]
|
||||
(assoc transaction :sufficient-funds?
|
||||
|
@ -93,25 +80,13 @@
|
|||
(money/formatted->internal :ETH 18))
|
||||
(money/bignumber available-for-gas))))))
|
||||
|
||||
(re-frame/reg-sub :wallet.send/transaction
|
||||
:<- [::send-transaction]
|
||||
:<- [:balance]
|
||||
(fn [[{:keys [amount symbol] :as transaction} balance]]
|
||||
(-> transaction
|
||||
(models.wallet/add-max-fee)
|
||||
(check-sufficient-funds balance symbol amount)
|
||||
(check-sufficient-gas balance symbol amount))))
|
||||
|
||||
(re-frame/reg-sub :wallet.send/unsigned-transaction
|
||||
:<- [::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))))))
|
||||
(re-frame/reg-sub
|
||||
:wallet.send/transaction
|
||||
:<- [::send-transaction]
|
||||
:<- [:balance]
|
||||
(fn [[{:keys [amount symbol] :as transaction} balance]]
|
||||
(-> transaction
|
||||
(models.wallet/transform-data-for-message)
|
||||
(models.wallet/add-max-fee)
|
||||
(check-sufficient-funds balance symbol amount)
|
||||
(check-sufficient-gas balance symbol amount))))
|
||||
|
|
|
@ -23,9 +23,45 @@
|
|||
[status-im.utils.security :as security]
|
||||
[status-im.utils.utils :as utils]
|
||||
[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]
|
||||
wrong-password? [:wallet.send/wrong-password?]
|
||||
signing-phrase (:signing-phrase @account)
|
||||
|
@ -37,8 +73,6 @@
|
|||
[tooltip/tooltip (i18n/label :t/wrong-password) styles/password-error-tooltip])
|
||||
[react/animated-view {:style (styles/sign-panel opacity-value)}
|
||||
[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?
|
||||
[react/activity-indicator {:animating true
|
||||
:size :large}])]
|
||||
|
@ -58,8 +92,8 @@
|
|||
:accessibility-label :enter-password-input
|
||||
:auto-capitalize :none}]]]]))
|
||||
|
||||
;; "Cancel" and "Sign Transaction >" buttons, signing with password
|
||||
(defview signing-buttons [spinning? cancel-handler sign-handler sign-label]
|
||||
;; "Cancel" and "Sign Transaction >" or "Sign >" buttons, signing with password
|
||||
(defview enter-password-buttons [spinning? cancel-handler sign-handler sign-label]
|
||||
(letsubs [sign-enabled? [:wallet.send/sign-password-enabled?]]
|
||||
[bottom-buttons/bottom-buttons
|
||||
styles/sign-buttons
|
||||
|
@ -74,156 +108,34 @@
|
|||
(i18n/label sign-label)
|
||||
[vector-icons/icon :icons/forward {:color :white}]]]))
|
||||
|
||||
(defn- sign-enabled? [amount-error to amount modal?]
|
||||
(and
|
||||
(nil? amount-error)
|
||||
(or modal? (not (empty? to))) ;;NOTE(goranjovic) - contract creation will have empty `to`
|
||||
(not (nil? amount))))
|
||||
|
||||
;; "Sign Later" and "Sign Transaction >" buttons
|
||||
(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?)]
|
||||
;; "Sign Transaction >" button
|
||||
(defn- sign-transaction-button [amount-error to amount sufficient-funds? sufficient-gas? modal?]
|
||||
(let [sign-enabled? (and (nil? amount-error)
|
||||
(or modal? (not (empty? to))) ;;NOTE(goranjovic) - contract creation will have empty `to`
|
||||
(not (nil? amount))
|
||||
sufficient-funds?
|
||||
sufficient-gas?)]
|
||||
[bottom-buttons/bottom-buttons
|
||||
styles/sign-buttons
|
||||
[react/view]
|
||||
[button/button {:style components.styles/flex
|
||||
:disabled? (not immediate-sign-enabled?)
|
||||
:on-press #(re-frame/dispatch [:wallet.send/set-signing? true])
|
||||
:disabled? (not sign-enabled?)
|
||||
:on-press #(re-frame/dispatch [:set-in
|
||||
[:wallet :send-transaction :show-password-input?]
|
||||
true])
|
||||
:text-style {:color :white}
|
||||
:accessibility-label :sign-transaction-button}
|
||||
(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?]
|
||||
(if dapp-transaction?
|
||||
(re-frame/dispatch [:navigate-to-modal :wallet-send-transaction-modal])
|
||||
(act/default-handler)))
|
||||
|
||||
(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)]
|
||||
;; MAIN SEND TRANSACTION VIEW
|
||||
(defn- send-transaction-view [{:keys [modal? transaction scroll advanced? network]}]
|
||||
(let [{:keys [amount amount-text amount-error asset-error show-password-input? 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)]
|
||||
[wallet.components/simple-screen {:avoid-keyboard? (not modal?)
|
||||
:status-bar-type (if modal? :modal-wallet :wallet)}
|
||||
[toolbar from-chat? false (if modal? act/close-white act/back-white)
|
||||
(i18n/label :t/send-transaction)]
|
||||
[toolbar modal? (i18n/label :t/send-transaction)]
|
||||
[react/view components.styles/flex
|
||||
[common/network-info {:text-color :white}]
|
||||
[react/scroll-view {:keyboard-should-persist-taps :always
|
||||
|
@ -247,46 +159,47 @@
|
|||
:amount-text amount-text
|
||||
: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]
|
||||
[advanced-options advanced? transaction modal? scroll]]]
|
||||
(if signing?
|
||||
[signing-buttons in-progress?
|
||||
#(re-frame/dispatch (if modal? [:wallet/cancel-signing-modal] [:wallet/discard-transaction]))
|
||||
#(re-frame/dispatch (if modal? [:wallet/sign-transaction-modal] [:wallet/sign-transaction]))
|
||||
[advanced-options advanced? transaction scroll]]]
|
||||
(if show-password-input?
|
||||
[enter-password-buttons in-progress?
|
||||
#(re-frame/dispatch [:wallet/cancel-entering-password])
|
||||
#(re-frame/dispatch [:wallet/send-transaction])
|
||||
:t/transactions-sign-transaction]
|
||||
[sign-button amount-error to amount sufficient-funds? sufficient-gas? modal?])
|
||||
(when signing?
|
||||
[sign-panel :t/signing-phrase-description in-progress?])
|
||||
[sign-transaction-button amount-error to amount sufficient-funds? sufficient-gas? modal?])
|
||||
(when show-password-input?
|
||||
[password-input-panel :t/signing-phrase-description in-progress?])
|
||||
(when in-progress? [react/view styles/processing-view])]]))
|
||||
|
||||
;; SEND TRANSACTION FROM WALLET (CHAT)
|
||||
(defview send-transaction []
|
||||
(letsubs [transaction [:wallet.send/transaction]
|
||||
advanced? [:wallet.send/advanced?]
|
||||
network [:get-current-account-network]
|
||||
scroll (atom nil)]
|
||||
[send-transaction-panel {:modal? false :transaction transaction :scroll scroll :advanced? advanced?
|
||||
:network network}]))
|
||||
[send-transaction-view {:modal? false :transaction transaction :scroll scroll :advanced? advanced?
|
||||
:network network}]))
|
||||
|
||||
;; SEND TRANSACTION FROM DAPP
|
||||
(defview send-transaction-modal []
|
||||
(letsubs [transaction [:wallet.send/unsigned-transaction]
|
||||
(letsubs [transaction [:wallet.send/transaction]
|
||||
advanced? [:wallet.send/advanced?]
|
||||
network [:get-current-account-network]
|
||||
scroll (atom nil)]
|
||||
(if transaction
|
||||
[send-transaction-panel {:modal? true :transaction transaction :scroll scroll :advanced? advanced?
|
||||
:network network}]
|
||||
[send-transaction-view {:modal? true :transaction transaction :scroll scroll :advanced? advanced?
|
||||
:network network}]
|
||||
[react/view wallet.styles/wallet-modal-container
|
||||
[react/view components.styles/flex
|
||||
[status-bar/status-bar {:type :modal-wallet}]
|
||||
[toolbar false false act/close-white
|
||||
(i18n/label :t/send-transaction)]
|
||||
[toolbar true (i18n/label :t/send-transaction)]
|
||||
[react/i18n-text {:style styles/empty-text
|
||||
:key :unsigned-transaction-expired}]]])))
|
||||
|
||||
;; SIGN MESSAGE FROM DAPP
|
||||
(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}
|
||||
[toolbar true false act/close-white
|
||||
(i18n/label :t/sign-message)]
|
||||
[toolbar true (i18n/label :t/sign-message)]
|
||||
[react/view components.styles/flex
|
||||
[react/scroll-view
|
||||
[react/view styles/send-transaction-form
|
||||
|
@ -297,10 +210,10 @@
|
|||
:input-options {:multiline true}
|
||||
:amount-text data}
|
||||
nil]]]]
|
||||
[signing-buttons false
|
||||
[enter-password-buttons false
|
||||
#(re-frame/dispatch [:wallet/discard-transaction-navigate-back])
|
||||
#(re-frame/dispatch [:wallet/sign-message-modal])
|
||||
#(re-frame/dispatch [:wallet/sign-message])
|
||||
:t/transactions-sign]
|
||||
[sign-panel :t/signing-message-phrase-description false]
|
||||
[password-input-panel :t/signing-message-phrase-description false]
|
||||
(when in-progress?
|
||||
[react/view styles/processing-view])]]))
|
||||
|
|
|
@ -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)]]]])))
|
|
@ -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 [status-im.ui.components.colors :as colors]))
|
||||
|
|
@ -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 [status-im.ui.components.react :as react]
|
||||
[status-im.ui.components.status-bar.view :as status-bar]
|
||||
[status-im.ui.components.icons.vector-icons :as vi]
|
||||
[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]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.ui.screens.wallet.components.views :as components]
|
|
@ -42,45 +42,6 @@
|
|||
(fn [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
|
||||
:<- [:wallet.transactions/grouped-transactions]
|
||||
(fn [{:keys [pending]}]
|
||||
|
@ -113,12 +74,10 @@
|
|||
(group-transactions-by-date (concat inbound outbound failed))))
|
||||
|
||||
(reg-sub :wallet.transactions/transactions-history-list
|
||||
:<- [:wallet.transactions/postponed-transactions-list]
|
||||
:<- [:wallet.transactions/pending-transactions-list]
|
||||
:<- [:wallet.transactions/completed-transactions-list]
|
||||
(fn [[postponed pending completed]]
|
||||
(fn [[pending completed]]
|
||||
(cond-> []
|
||||
postponed (into postponed)
|
||||
pending (into pending)
|
||||
completed (into completed))))
|
||||
|
||||
|
@ -128,13 +87,11 @@
|
|||
(:current-transaction wallet)))
|
||||
|
||||
(reg-sub :wallet.transactions/transaction-details
|
||||
:<- [:wallet.transactions/unsigned-transactions]
|
||||
:<- [:wallet.transactions/transactions]
|
||||
:<- [:wallet.transactions/current-transaction]
|
||||
:<- [:network]
|
||||
(fn [[unsigned-transactions transactions current-transaction network]]
|
||||
(let [transactions (merge transactions unsigned-transactions)
|
||||
{:keys [gas-used gas-price hash timestamp type] :as transaction} (get transactions current-transaction)
|
||||
(fn [[transactions current-transaction network]]
|
||||
(let [{:keys [gas-used gas-price hash timestamp type] :as transaction} (get transactions current-transaction)
|
||||
chain (ethereum/network->chain-keyword network)]
|
||||
(when transaction
|
||||
(merge transaction
|
||||
|
|
|
@ -20,7 +20,9 @@
|
|||
[status-im.utils.ethereum.tokens :as tokens]
|
||||
[status-im.constants :as constants]
|
||||
[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]))
|
||||
|
||||
(defn name [web3 contract cb]
|
||||
|
@ -42,12 +44,14 @@
|
|||
(ethereum/call-params contract "balanceOf(address)" (ethereum/normalized-address address))
|
||||
#(cb %1 (ethereum/hex->bignumber %2))))
|
||||
|
||||
(defn transfer [web3 contract from address value params cb]
|
||||
(ethereum/send-transaction web3
|
||||
(merge (ethereum/call-params contract "transfer(address,uint256)" (ethereum/normalized-address address) (ethereum/int->hex value))
|
||||
{:from from}
|
||||
params)
|
||||
#(cb %1 (ethereum/hex->boolean %2))))
|
||||
(defn transfer [contract from to value gas gas-price masked-password on-completed]
|
||||
(status/send-transaction (types/clj->json
|
||||
(merge (ethereum/call-params contract "transfer(address,uint256)" to value)
|
||||
{:from from
|
||||
:gas gas
|
||||
:gasPrice gas-price}))
|
||||
(security/unmask masked-password)
|
||||
on-completed))
|
||||
|
||||
(defn transfer-from [web3 contract from-address to-address value cb]
|
||||
(ethereum/call web3
|
||||
|
@ -168,8 +172,8 @@
|
|||
(add-padding from)
|
||||
(add-padding to)]}]}
|
||||
payload (.stringify js/JSON (clj->js args))]
|
||||
(status/call-web3-private payload
|
||||
(response-handler web3 current-block-number chain direction ethereum/handle-error cb))))
|
||||
(status/call-private-rpc payload
|
||||
(response-handler web3 current-block-number chain direction ethereum/handle-error cb))))
|
||||
|
||||
(defn get-token-transactions
|
||||
[web3 chain contracts direction address cb]
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
(defn make-internal-web3 []
|
||||
(dependencies/Web3.
|
||||
#js {:sendAsync (fn [payload callback]
|
||||
(status/call-web3-private
|
||||
(status/call-private-rpc
|
||||
(.stringify js/JSON payload)
|
||||
(fn [response]
|
||||
(if (= "" response)
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
status-im.ui.screens.db
|
||||
status-im.ui.screens.subs
|
||||
[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 []
|
||||
|
||||
|
@ -129,18 +130,18 @@
|
|||
|
||||
(is (zero? (count @dapps-permissions)))
|
||||
|
||||
(re-frame/dispatch [:on-bridge-message {:type "status-api-request"
|
||||
:host dapp-name
|
||||
:permissions ["FAKE_PERMISSION"]}
|
||||
(re-frame/dispatch [:on-bridge-message (types/clj->json {:type "status-api-request"
|
||||
:host dapp-name
|
||||
:permissions ["FAKE_PERMISSION"]})
|
||||
nil nil])
|
||||
|
||||
(is (= {:dapp dapp-name
|
||||
:permissions []}
|
||||
(get @dapps-permissions dapp-name)))
|
||||
|
||||
(re-frame/dispatch [:on-bridge-message {:type "status-api-request"
|
||||
:host dapp-name
|
||||
:permissions ["CONTACT_CODE"]}
|
||||
(re-frame/dispatch [:on-bridge-message (types/clj->json {:type "status-api-request"
|
||||
:host dapp-name
|
||||
:permissions ["CONTACT_CODE"]})
|
||||
nil nil])
|
||||
|
||||
(is (= 1 (count @dapps-permissions)))
|
||||
|
@ -149,9 +150,9 @@
|
|||
:permissions ["CONTACT_CODE"]}
|
||||
(get @dapps-permissions dapp-name)))
|
||||
|
||||
(re-frame/dispatch [:on-bridge-message {:type "status-api-request"
|
||||
:host dapp-name
|
||||
:permissions ["CONTACT_CODE" "FAKE_PERMISSION"]}
|
||||
(re-frame/dispatch [:on-bridge-message (types/clj->json {:type "status-api-request"
|
||||
:host dapp-name
|
||||
:permissions ["CONTACT_CODE" "FAKE_PERMISSION"]})
|
||||
nil nil])
|
||||
|
||||
(is (= 1 (count @dapps-permissions)))
|
||||
|
@ -160,9 +161,9 @@
|
|||
:permissions ["CONTACT_CODE"]}
|
||||
(get @dapps-permissions dapp-name)))
|
||||
|
||||
(re-frame/dispatch [:on-bridge-message {:type "status-api-request"
|
||||
:host dapp-name
|
||||
:permissions ["FAKE_PERMISSION"]}
|
||||
(re-frame/dispatch [:on-bridge-message (types/clj->json {:type "status-api-request"
|
||||
:host dapp-name
|
||||
:permissions ["FAKE_PERMISSION"]})
|
||||
nil nil])
|
||||
|
||||
(is (= 1 (count @dapps-permissions)))
|
||||
|
@ -171,9 +172,9 @@
|
|||
:permissions ["CONTACT_CODE"]}
|
||||
(get @dapps-permissions dapp-name)))
|
||||
|
||||
(re-frame/dispatch [:on-bridge-message {:type "status-api-request"
|
||||
:host dapp-name2
|
||||
:permissions ["CONTACT_CODE"]}
|
||||
(re-frame/dispatch [:on-bridge-message (types/clj->json {:type "status-api-request"
|
||||
:host dapp-name2
|
||||
:permissions ["CONTACT_CODE"]})
|
||||
nil nil])
|
||||
|
||||
(is (= 2 (count @dapps-permissions)))
|
||||
|
|
Loading…
Reference in New Issue